Expand description
L2 GC-survival: decide which store paths the warm cache must capture.
── WHAT THIS CLOSES ──────────────────────────────────────────────────────
theory/ATATAME.md calls L2 “the single highest-value gap in this
doctrine”. attic watch-store used to provide it; attic was retired
2026-07-31, and until something captures a newly-realized store path
before nix-collect-garbage reaches it, the warm store is a
within-session memo rather than a durable win.
── A RECONCILER, NOT AN EVENT STREAM ───────────────────────────────────── attic watched inotify events. This diffs state instead, and that is a deliberate upgrade rather than an implementation shortcut:
- it converges after a restart, a missed event, or a crash — an event stream loses whatever happened while it was down;
- it captures paths that arrived by SUBSTITUTION, which no build hook ever sees. rio’s post-build hook only pushes what rio BUILDS, so a substituted path has never been covered by anything;
- “what is missing” is computed from the cache itself, so a push that failed is simply still missing next pass — retry needs no bookkeeping.
── WHY A BASELINE, AND WHY IT IS THE DEFAULT ───────────────────────────── A pure reconciler would mirror the ENTIRE store on first run. Measured on rio 2026-08-08: 60,025 store paths against 6,929 cached, so the first pass would try to capture ~53,000 paths and grow a 12 GiB cache toward the size of the whole store. That is not what the doctrine asks for — L2 is survival of newly-realized paths, not a full mirror.
So the watcher records a BASELINE at startup and captures only what
appears after it. --initial-reconcile starts from an empty baseline for
operators who do want the backfill, and max_per_pass bounds either mode
so a large build cannot turn one tick into an unbounded upload.
Structs§
- Capture
Plan - What a single capture pass should do.
- Watch
Report - Outcome of a capture pass, for the operator-facing line.
Functions§
- plan_
capture - Decide what to capture, from state alone.