Watcher-driven build and publication chain
While you work, every package recompiles and republishes itself in the background, and a consumer never sees a half-written build.
- What it is
- Tool — something you run.
- What it does
- In the framework repository, compiling and PUBLISHING are separate steps: each package's compiler emits into a private staging area, and a finished generation is published in one journaled move that tells supervisors the package is ready.
- Where it stops
- There is no retained previous generation.
Working inside the framework repository, you start one process and every package compiles, finalizes and republishes itself as you edit. The chain has five stages with distinct owners, and its load-bearing property is not speed: a package’s published output directory only ever holds a complete generation, so a consumer importing it mid-edit cannot read a file that is half written.
The problem it solves
A monorepo with dozens of interdependent packages has a build problem that is easy to underestimate. The obvious half is ordering: package B cannot compile until package A has emitted. The expensive half is that the emitted directory is a shared, mutable surface, read by other compilers, by running processes, and by an application whose dependencies are linked into it. Every write to that surface is visible to a reader the instant it happens.
Two failures follow, and both were paid for here. Two processes watching the same output directory interleave their writes and produce truncated files, so a running service crashes on a syntax error in a file nobody edited. And a compiler that kills its own post-compile step when the next compilation starts can interrupt an in-place write between truncating a file and refilling it, leaving a zero-byte module and every link to it empty. Both present as a defect somewhere else entirely: a missing export, a module that cannot be found, a service that will not start. The chain’s design is a response to those, not to build times.
What it does
In the framework repository, compiling and PUBLISHING are separate steps: each package’s compiler emits into a private staging area, and a finished generation is published in one journaled move that tells supervisors the package is ready.
The watcher fleet is derived, not listed. The workspace graph is read and the packages that declare a watch step are selected — so a new package is watched the day it is created. One status document is written for supervisors to read, and it is enforced that there is only ever one writer, because two writers produce oscillating timestamps and drive every supervisor into a restart loop.
Each watcher runs behind an exclusive lock on its own output directory, and the lock records both the compiler process and the orchestrator that started it — so a holder can be JUDGED rather than trusted. A live holder under a live orchestrator is respected and the newcomer stands down with a distinct exit code; a live holder whose orchestrator is gone is an orphan, and is terminated and replaced. Reclaiming is atomic, so two processes racing the same reclaimable lock cannot both win.
The finalizer runs on the compiler’s success signal. It resolves each package’s internal path aliases and rewrites module extensions in a single in-memory pass, so each emitted file is written exactly once rather than twice with an observable intermediate state. It installs a cooperative interrupt guard before anything else it imports, because installing any signal listener moves the signal onto the event loop and a synchronous write can no longer be torn apart part-way.
Before anything is published, one shared rule refuses a generation containing a zero-byte module — applied at every write path rather than at one of them.
Publication is journaled: the generation is recorded, written into the public directory in place so that linked files survive, and the signal raised that tells supervisors the package is ready. Compiler state files and the writer lock are excluded from what is published, distinguished from what is merely present on disk so that a stray file can still be reclaimed rather than becoming permanent.
Limits
There is no retained previous generation. Publication commits the new one and removes the retired one, so rolling a package back means recompiling and republishing it.
A failed or interrupted compile leaves the previous generation in place and publishes nothing — the intended behaviour, and it means a package can be silently one edit behind its source if its watcher died. That is what the currency sweep exists to report, and reading it is a separate act.
The chain is bound to package-local compilation. A mode where one compiler emits into several packages’ directories is rejected at runtime, because it writes past the finalizers that own those directories.