Expand description
File-watch + auto-rebuild loop for mesofact-static workloads.
Watches <workload>/src/ via notify, debounces edits, rebuilds
(BuildDriver — in-process by default, sh -c when workload.toml
declares a [build] command), then snapshots
<workload>/dist/ into <workload>/.mesofact-dev/gen-<N>/ and flips a
DistPointer so the running server starts serving
the new artifact on the next request. Build stdout/stderr inherits the
parent’s, so output shows up in the operator’s terminal or the Run-tab
log surface.
Atomicity story: each generation is a separate directory. dist/ is
copied into .mesofact-dev/gen-<N>-staging/ and then atomic-renamed to
.mesofact-dev/gen-<N>/ on the same filesystem; the pointer flip is a
single RwLock write; in-flight reads keep using the PathBuf they
already cloned, so a request that started reading
gen-<N-1>/html/index.html doesn’t observe a torn write. dist/ itself
is left intact so concurrent publishers (mesofact-publisher to R2,
qed-run to pond/MinIO) can read it without racing the watcher. GC keeps
the last two generations.
@yah:ticket(R255-S5, “Decide tier-1 object store: s3s-fs vs serve-off-disk vs self-mock”) @yah:assignee(agent:claude) @yah:at(2026-05-25T20:08:09Z) @yah:kind(spike) @yah:status(review) @yah:parent(R255) @yah:next(“decide on the single fact: does the almanac/runtime fetch via S3 API calls or plain CDN GET? if S3, tier-1 needs s3s-fs regardless”) @yah:next(“if s3s-fs: run the existing publish_to_local_sim unchanged against it so tier-1 becomes a strict subset of tier-2 (only diff: in-process s3s-fs vs containerized MinIO+Caddy+yubaba)”) @yah:next(“reject self-mock: reimplementing SigV4 + bucket-policy + error shapes drifts from MinIO and defeats the point”) @yah:gotcha(“license-check s3s before adopting — must be MIT/BSD/Apache-2.0/ISC (believed Apache-2.0)”) @yah:assumes(“tier-1 read path is plain HTTP-GET (Caddy proxies the public bucket), so serve-off-disk is faithful for reads; only the build->PUT->read publish contract is unexercised at tier 1”) @yah:handoff(“Spike closed: serve-off-disk wins for tier-1 (dev). Read path is plain HTTP GET via axum ServeDir — browsers never call S3 APIs. The build→PUT→serve publish contract is exercised at tier-2 (sim+MinIO, R256 T1–T5 in review). No s3s-fs needed at tier-1; self-mock rejected as before. The @yah:assumes fact was correct. Only a tier-2 concern: R256-F8 tracks the publish-to-MinIO watcher sink for hot-ish sim reload, which uses the real MinIO container (not s3s-fs).”) @yah:verify(“No code change needed — tier-1 is already serve-off-disk. Verify the assumes by checking mesofact-dev routes: nothing in app/yah/web/src/ or mesofact-runtime makes S3 API calls client-side.”)
@yah:ticket(R256-F8, “Parameterize watcher sink: DistPointer (serve off disk) vs publish-to-MinIO”)
@yah:assignee(agent:claude)
@yah:at(2026-05-25T20:30:17Z)
@yah:status(review)
@yah:parent(R256)
@yah:next(“introduce a sink abstraction so the same watch->rebuild loop can target either the in-process DistPointer (dev tier) or publish_to_local_sim (sim tier -> MinIO container)”)
@yah:next(“this is what gives the containerized sim a hot-ish reload (edit -> rebuild -> republish -> Caddy serves new artifact) WITHOUT putting mesofact-dev inside a container”)
@yah:next(“keep the host-side watcher as the only dev-mode component; the sim containers stay pure mesofact-core + Caddy + MinIO”)
@yah:assumes(“today the watcher’s only sink is the in-process DistPointer (build_and_swap -> pointer.set at watcher.rs:253); there is no publish sink”)
@arch:see(.yah/docs/working/mesofact-dev-camp-embedding.md)
@yah:handoff(“PostBuildFn + PostBuildFuture type aliases added to watcher.rs. Watcher gains post_build: Option
@arch:see(app/yah/cli/src/camp.rs)
Structs§
- Watch
Options - Knobs for the watch loop.
- Watcher
- File-watch + rebuild loop. Construct with
Watcher::newand drive withWatcher::run; pair with acrate::Serversharing the sameDistPointer. - Watcher
Handle - Handle that keeps a spawned watcher alive. Dropping it does not cancel
the task (tokio detaches on drop); use
WatcherHandle::is_runningto observe lifecycle.
Enums§
- Build
Driver - How a rebuild is produced.
Constants§
- LEGACY_
SHELL_ BUILD - The pre-R759-T4 default. Retained as the
--no-default-featuresfallback and named so the one place it still applies is greppable.
Functions§
- spawn
- Spawn a watch loop on a background tokio task. Returns a handle that stops the loop when dropped (the watcher’s internal channel closes when the task exits, which is fine for a process-lifetime dev server).
Type Aliases§
- Post
Build Fn - Optional post-build hook. Called with the snapshot directory (the
gen-N/directory, whosehtml/subdirectory is what the DistPointer points at) after every successful build, immediately after the pointer flip. - Post
Build Future - Boxed future returned by a
PostBuildFn.