* Add inherit_program / include_file master applies; auto hot-reload demo
New compile-time master applies, consulted for every inherit statement
and #include directive:
* mixed inherit_program(string from, string path, int priv)
Called while compiling `from` for `inherit "path";` (priv nonzero for
private inherits). A string return is an alternate path for the
inherited file; an array-of-strings return is the inherited program's
source itself (compiled via load_object_from_source under the inherit
statement's name, through the existing load_object retry loop); any
other return prevents the inheritance.
* mixed include_file(string compiled, string from, string path)
Called when `from` is about to include `path` while compiling
`compiled`. A string return is the translated path (resolved absolute
from the mudlib root or relative to the includer; returning `path`
unchanged keeps the "..."-vs-<...> search semantics); an
array-of-strings return is the included text itself (pushed as an
in-memory include buffer with the usual file-identity bookkeeping);
any other return prevents the inclusion.
Both follow the valid_override/get_include_path precedent for calling
master LPC mid-compile (skipped without a VM context or master object),
and a missing apply keeps stock behavior.
The applies expose the full compile-time dependency graph, which the
testsuite uses to demonstrate mudlib auto hot-reload on file changes:
/single/hot_reload.lpc registers as the master's compile hooks, records
which source files each program's bytecode was built from (own source,
includes, inherited programs, transitively), and its call_out poller
destructs+reloads watched blueprints whose dependency closure changed
on disk - including reloading a stale parent when only the parent's
include changed.
Testsuite: single/tests/compiler/{inherit_program,include_file}.lpc pin
the apply semantics (redirect, inline source, deny, priv flag, argument
shapes on nested includes) via the scriptable /clone/compile_hook;
single/tests/applies/hot_reload.lpc demonstrates end-to-end hot reload
over a runtime-written inherit+include fixture chain. Docs added for
both applies.
Validated: full LPC suite (532 files) x2 on RelWithDebInfo, x2 on
Debug+ASan/UBSan, plus the 297 GTest unit tests.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018DGhzJfPhDGJA94EPh1gEK
* docs: add hot reload guide for the new compile-time master applies
New concepts page (concepts/general/hot_reload.md) explaining why
"file changed -> reload" needs the compile-time dependency graph, how
the inherit_program / include_file master applies expose it, and a
step-by-step mudlib implementation with examples: master delegation,
dependency recording, closure computation, change detection with
size+mtime snapshots, parent-first reload ordering, and the call_out
poller. Documents blueprint-reload semantics and caveats (clones keep
the old program, no compiles inside the applies, records only complete
for compiles observed by the daemon), pointing at the testsuite
reference implementation. Cross-linked from both apply reference pages
and the concepts indices.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018DGhzJfPhDGJA94EPh1gEK
* docs/testsuite: say "master copy", not "blueprint"
Align the hot-reload guide, apply reference, daemon, and test comments
with the project's terminology for the object loaded from a file (see
clonep(3): "the master copy").
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018DGhzJfPhDGJA94EPh1gEK
* testsuite: edge cases for the compile-time applies; docs review fixes
compile_hooks_edge.lpc pins that unusual inherit_program/include_file
return values produce clean LPC errors or the documented behavior,
never a driver crash: empty array, non-string elements, redirect to
self, empty-string redirect, inline source vs already-loaded object,
multiline inline content, the apply itself throwing mid-compile
(safe_apply falls back to default resolution), extension-spelled
redirects, and denying the auto-included global include file.
Fixtures in /clone/adv_*; /clone/adv_hook wraps the scriptable hook
with a throwing include_file.
Docs fixes from review: the apply-page examples now look the daemon up
with find_object() instead of a path call_other (which would load the
target and trigger a compile mid-compile -- the exact pattern the same
pages forbid), and the hot-reload guide's ancestors() snippet carries
the seen-mapping argument to match the reference daemon.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018DGhzJfPhDGJA94EPh1gEK
* hot_reload: fix multi-watch and failed-reload handling; harden tests
Three defects from the LPC review round, each now pinned by the
demonstration test:
* check_now() collected and reloaded in one pass, so the first reload's
snapshot refresh erased the change evidence for every other watched
program sharing the dependency (a common header, or a watched parent
iterating before its watched child - which then stayed bound to the
destructed old parent forever). The stale set is now collected before
any reload runs.
* A reload whose recompile throws (a syntax error mid-edit - the most
common event in a hot-reload workflow) unwound poll() before the
re-arm, killing the poller forever and leaving the watched master
copy destructed. poll() re-arms first, check_now() catches per
program, reload_count only counts successes, and closure_changed()
treats a watched-but-not-loaded program as stale so the retry
self-heals once the file compiles again.
* The three apply tests registered master compile hooks (and the
hot-reload test armed the poller and wrote /data/hot) with cleanup on
the success path only; a thrown check would leave the hook routing
every remaining compile of the randomized run. Each test now runs its
checks under catch and unhooks/tears down unconditionally, re-raising
the error afterward.
The demo test now also covers the shared-dependency pass (watch parent
and child, change the common include, both reload in one pass) and the
broken-edit recovery cycle. The hot-reload guide's snippets are updated
to match the daemon.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018DGhzJfPhDGJA94EPh1gEK
* simulate: let inline inherit source resolve its own unloaded inherits
From the C++ review round: master::inherit_program returning inline
source whose text itself inherits a not-yet-loaded program failed the
whole load with "#inherit is not supported when compiling from
in-memory source" -- reachable in the feature's primary use case, since
synthesized programs routinely inherit ordinary on-disk library files.
load_object_from_source() now runs the same iterative dance as
load_object(): when the compile aborts on an unloaded parent, load that
parent (from disk, or from further master-supplied inline source, so
synthesized-inheriting-synthesized chains work), then recompile the
same source string -- which is in hand, unlike the historical
no-filename rationale for rejecting #inherit here. Mirrors
load_object()'s guards: illegal-to-inherit-self, the duplicate-name
check after the parent's arbitrary LPC ran, and an inherit-chain bound
on the retry loop as a backstop against a master that redirects to a
fresh unloaded name on every recompile.
The inherit_program test now covers both new shapes (inline inheriting
unloaded on-disk, and two levels of inline source); the apply doc drops
the already-loaded-only caveat.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018DGhzJfPhDGJA94EPh1gEK
---------
Co-authored-by: Claude <noreply@anthropic.com>
* docs: add local full-text search and a contributor README
Add @easyops-cn/docusaurus-search-local to the Docusaurus site so the
docs get an offline search bar (index built at build time, no external
service). English and zh-CN pages are both indexed, and matched terms
are highlighted on the target page.
Add docs/README.md describing the Docusaurus setup, local dev/build
commands, search behavior, directory layout, and gotchas; exclude it
from the published site alongside CLAUDE.md. Point the root README's
docs/ entry at it.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TSzcESzU9947zkGzQ6SMmE
* docs: remove dead framework leftovers, fix index generation, complete the nav
Delete the VitePress (.vitepress/) and Jekyll (_layouts/, css/) leftovers,
the one-shot migration scripts (fix_md_header.py, fix_seealso.py), and the
stale keywords.json snapshot; prune the matching .gitignore entries and
docusaurus exclude patterns.
Rewrite gen_index.py for Docusaurus: it emitted dead .html links and
legacy 'layout: doc' frontmatter, choked on non-markdown entries, and
dropped nested categories — regenerating an index would have broken it.
It now emits the extension-less links the site actually uses, links
nested category indexes (restoring apply/* on the zh-CN index), and
refuses to run on the docs root. Fix update_index.sh's copy-paste titles
(zh-CN efun/build were titled 'APPLY'), stop it clobbering the
hand-written lpc/index.md, and cover cli/. Regenerated indexes pick up
the missing driver/ffi-plan entry. add_missing_efuns.py now takes the
keywords.json path as an argument instead of requiring a stale copy.
Move CNAME and the Google site-verification file into static/ so they
actually reach the published build output.
Complete the sidebar: link the CLI category to cli/index and add the
missing portbind/symbol/generate_keywords pages, and expose the
previously orphaned stdlib section under Reference.
Promote onBrokenLinks to 'throw' now the build is warning-free, and drop
the empty Demo section from the landing page.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TSzcESzU9947zkGzQ6SMmE
* docs: strip legacy 'layout: doc' frontmatter from all pages
Mechanical sweep removing the Jekyll-era 'layout: doc' line from every
doc page's frontmatter (Docusaurus ignores it), and the matching line
from the templates in docs/CLAUDE.md so new pages don't reintroduce it.
No content changes.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TSzcESzU9947zkGzQ6SMmE
---------
Co-authored-by: Claude <noreply@anthropic.com>
New pages: lpc/source-files (extension rules, extension-blind object
identity, registry-before-filesystem, portable-code guidance),
lpc/diagnostics (clang-style output, macro expansion notes, include
chains, fix-its, show_error_context), preprocessor/conditionals
(token-based #if with C precedence, defined()/efun_defined()) and
preprocessor/pragma (real pragma table from the driver).
Rewrote preprocessor/index (full directive table, immutable
predefines), define (function-like macros, stringize/paste, rescan,
redefinition-warning semantics) and include (search order, master
get_include_path, trailing text, macro file names); constructs/include
is now a summary pointing at the reference, and constructs/inherit
documents pathname resolution. Dropped the vestigial
preprocessor/README; sidebar gains the new pages plus the previously
unlisted text_blocks.
AGENTS.md now points agents at docs/lpc/ as the authoritative LPC
reference. Validated with a clean docusaurus production build.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>