Repository navigation
Deploy replaces the record: the lifetime in the store, reconcile's six words, an occupant is what the record wrote, and adopt leaves a record - #940
Merged
Conversation
added 5 commits
September 23, 2026 16:35
…fold reads the current one A lifetime is one writ deploy invocation across every scope it runs, with the upgrades, reconciliations and adoptions written into it, until a later deploy replaces it or a decommission ends it. It is lifetimes/<id>.yaml under the store, a UUIDv7 minted after pre-flight and reaching the store with the first trace; lifetimes/current names the current one. upgrade, adopt and decommission require a current lifetime and exit 66 without one. readback.Fold reads the current lifetime's runs and nothing else: the run index stays a hint, the trace glob goes, and a trace no lifetime names is not folded. A file the previous lifetime deployed and this one does not is not in the record; on the Linux VM the 52 rows a layer reorganization had left as missing are gone after one deploy. A store written before this has no lifetime, so the first plain deploy refuses every occupant; one --conflict=replace deploy opens the first lifetime. Part of #922; lane 1 of #916, PR A. Claude-Session: https://claude.ai/code/session_01SqQVFZz7AdiLZkGoAcUqRW
…sent, changed, dangling, stale The record is the desired state; the layer checkout is only deploy's input. reconcile compares the system to the record and says one of six words per entry. absent: the record says a target is there and it is not. changed: it is there but is not what the record says. dangling: the source the record names does not resolve, for a link and for a copy alike, a reference that outlives its referent, reported and left because only deploy makes a new record. stale: the source resolves and its content moved, a derivative behind its origin, upgrade's to refresh. orphan, missing, conflict, modified and modified-or-stale are gone. The classifiers decide from the link and the two recorded digests; the fresh-render comparison goes. The ledger records a source under the path the run read it from, the pinned snapshot, so the graph's files annotation now carries read_from and readback joins the source digest through it; without that, stale never fired on a real deploy. Part of #923; lane 2 of #916, PR A. Claude-Session: https://claude.ai/code/session_01SqQVFZz7AdiLZkGoAcUqRW
…ngling link is re-pointed under stop readback.Entry.AsRecorded is the one predicate. A link is as recorded when its literal endpoint, absolutized against the link's own directory, is the recorded source, resolved or dangling; a copy when its content digest is the recorded as-deployed digest. The source is never resolved. Deploy's pre-flight under stop and reconcile's changed decide by it, so a link left dangling by a file that moved between layers is writ's own and a plain deploy re-points it; occupantIsOurs, which resolved both sides and read a dangling link as foreign, is gone. The layer-journey scenario's step 3.3 redeploys the moved links under the default policy. The guide and the reconciliation page say what an occupant of writ's own is. Part of #883; lane 3 of #916, PR A. Claude-Session: https://claude.ai/code/session_01SqQVFZz7AdiLZkGoAcUqRW
--platform names the suffix the adopted files carry, in the segment vocabulary the layer tree matches (Darwin, Unix, Windows, Linux, Linux.Debian, Darwin.arm64); a word the matcher never reads is a usage error, and the file lands under <project>.<suffix>. An adoption is a write into the current deployment. Its graph now carries the origin and the files annotation a deploy's carries, one file.move and one file.link unit per item so every link has its own unit id, and RunBatches writes the graph before the run, which it never had. The fold reads an adoption as it reads a deploy, reconcile says linked, and the next deploy recognizes the link as writ's own. Two findings on the way. A graph holding a flow.choose does not load back from the document (#939), so the existing-destination guard runs before the graph and the chain has no subgraph. A registered layer is a symlink into its repository and the confined run root refuses to write through it, so adopt had never worked on a real install; the layer path now resolves through its symlink, which is also what the record must name. Part of #931; lane 14 of #916, PR A. Claude-Session: https://claude.ai/code/session_01SqQVFZz7AdiLZkGoAcUqRW
This was referenced Sep 24, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
PR A of #916, the writ lifecycle schedule: lanes 1, 2, 3
and 14, one commit each, on one branch, merged with develop before opening.
one
writ deployinvocation across every scope it runs, a store documentlifetimes/<id>.yamlwith a UUIDv7 idminted after pre-flight and written with the first trace;
lifetimes/currentnames the current one.upgrade,adoptanddecommissionrequire a current lifetime and exit 66 without one.readback.Foldreads the currentlifetime's runs and nothing else: the index read, the trace glob and two helpers are gone. A store written
before this has no lifetime, so the first plain deploy refuses every occupant; one
--conflict=replacedeployopens the first lifetime.
desired state.
linked,copied,absent,changed,dangling(the source the record names does not resolve,for a link or a copy; repair
deploy),stale(the source resolves and its content moved; repairupgrade).orphan,missing,conflict,modified,modified-or-staleare gone. The classifiers decide from the link andthe two recorded digests. The ledger records a source under the path the run read it from, the pinned snapshot,
so the
filesannotation carriesread_fromand readback joins the source digest through it;stalehad neverfired on a real deploy.
readback.Entry.AsRecordedis the one predicate: a link whose literal endpoint is the recorded source, resolvedor dangling, or a copy whose digest is the recorded one; the source is never resolved. Pre-flight and reconcile
share it, so a link left dangling by a file that moved between layers is re-pointed under
stop.—
--platform <suffix>in the segment vocabulary; onefile.moveand onefile.linkper item; the graph carriesthe origin and
filesannotation a deploy's carries, and is written before the run. Two findings: a graph holdinga
flow.choosedoes not load back (#939), so the guardruns before the graph; and a registered layer is a symlink the confined root refuses to write through, so adopt
had never worked on a real install — the layer path now resolves through its symlink.
Evidence
Each lane ran on both VMs, snapshotted first; raw logs under
build/e2e/9{22,23,83,31}-e2e-*.login the worktree.danoble-ud24-1.locallinux/arm64danoble-wd11-3.localwindows/arm64replaceopened lifetime 1; a plain deploy opened lifetime 2, the firstreplaced;reconcile166 linked + 1 copied, the 52 stale rows of 2026-09-22 gone; 0.46 / 0.53 / 0.56 sstopaccepted the dangling link and ran; restored--platform Ubunturefused (64); adopted withLinuxundernoblefactor-ops.Linux; the lifetime's last run adopt;reconcilelinked; a deploy understopaccepted it; undoneWindowsLocally:
make checkand the eight scenarios green on every lane; the layer-journey's moved links now redeploy underthe default policy. Coverage total 63.1%;
readback82.4%,reconcile86.1%,adopt78.9%,cmd/internal/cli69.2%. Nothing new over complexity 8.
Test plan
make check,make test-scenario, per lanewrit deploy common --conflict=replaceTime
Computed by the algorithm of noblefactor-ops#188 at merge.
https://claude.ai/code/session_01SqQVFZz7AdiLZkGoAcUqRW