Repository navigation
The observer stops being handed a placeholder: the wet acquisition half, and the one operation the boolean vocabulary could not express - #9304
Conversation
…ecome a real probe (#fabric) gunbc.fabric_cell_observe has been the pure half of an observer whose other half did not exist -- every production call site passed fabric_cell_observation_unobserved, which says "nobody looked" and is honest precisely because nobody did. This is the half that looks. IT ADDS NO HOST VOCABULARY, AND THAT IS THE LOAD-BEARING FACT ABOUT IT. Filesystem.List, shell.Test.IsDirectory/IsFile and Systemctl.ShowProperty/ ListUnits are already declared with cited upstream authorities, so the acquisition composes five declared readonly reads. An acquisition layer that needed a NEW operation would have been telling us the observation model wants a fact the substrate cannot establish -- a modeling question wearing transport clothes -- and it did not. A DIRECTORY HAS FOUR HONEST STATES, NOT TWO, AND THE PAIR A Bool MERGES IS THE PAIR WITH OPPOSITE CONSEQUENCES. Filesystem.List answers success: Bool, so a listing that FAILED and a directory that IS NOT THERE arrive as the same false -- and not-there licenses a provisioning ADD while could-not-read must stop the line. They are separated by an independent declared read rather than by matching the error text for "No such file", because parsing a message is a heuristic standing where an operation was available. The fourth state is the one no Bool can hold: a regular FILE where a cell root belongs is not an absence and not a listing, and calling it absence would license an ADD whose failure surfaces at apply time as a mkdir refusal instead of here as an observation. THE CONTRADICTION LAW IS OBEYED AT ACQUISITION TIME RATHER THAN REPAIRED AFTERWARDS. A state read is performed only where the parent listing held the entry, so this module cannot hand the observer an address that is absent from its namespace and simultaneously read -- the impossible host the observer refuses, and the exact shape three fixtures on the last PR had to be corrected for. An absent directory contributes NO address probe at all rather than one carrying a refusal, because absence is the namespace's answer and overruling it would make a fresh host refuse instead of plan. ONE SPELLING AUTHORITY, BECAUSE THE OBSERVED SIDE NOW HAS TO PRODUCE THE SAME STRING. The boundary digest was spelled inline in the desired function; an observer composing its own would fork, and a reordered key would make every cell on every host read as drifted with the drift in the RENDERER rather than on the host. fabric_cell_boundary_state_digest_of is now that one function with two callers. It takes rendered strings and not typed magnitudes on purpose: the observed side holds whatever systemctl printed, which is a decimal or the word "infinity", and parsing it back to re-render it would invent a round trip the comparison does not need. NINE LAWS, ALL PURE, AND ONE MUTATION RECEIPT THAT CORRECTED ITS OWN ROW. The digest-agreement law looked like it should red when the shared spelling changes. Measured: it does NOT -- renaming a key in the shared function leaves it green, because both sides route through it, which is what one authority MEANS. That is not a hole, but the comment claiming otherwise would have been. The RED it actually carries is on the acquisition side and is executed: transposing memory_high and memory_swap_max where this module composes the reading fails it, restored green after. The row now states the boundary it measured rather than the one I assumed. The host effects are unreachable from a hermetic fixture by construction, and that is the boundary rather than a gap: a mocked listing would assert these laws against authored data instead of against the projection. WIRED AT THE PLAN AND APPLY CALL SITES, ONE OBSERVATION EACH. The apply path binds the probe ONCE and compares it against both the member-set fingerprint and the observed baseline -- two probes there could disagree with each other, and a plan refused because the host changed between two reads of the same run would name drift that never happened. Spark's planner stays unobserved, and correctly: it is a pure fn that does not read a host and must not claim to. 4052 file(s) parse-clean; nine rows green by execution; the CLI closure resolves through a witness that imports it. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
Review 56129 is an APPROVE with no actionable items — nothing to push in response. One receipt worth adding, because it is the gap the previous PR's floor run exposed. On #9185 the floor found three reds in fixtures I had not touched and therefore had not run; an entry-scoped local run only reaches what its entry names, so "I ran the rows I changed" was structurally unable to see them. This time I ran the full roster of every file this change can reach, before the floor did:
116/116 green by execution, plus the 9 new rows in — sent from silent-bear-842 |
…dary was asking about nothing (#9304) Two findings from the side chat, both real, and the first is a live contradiction in the module whose header claims the opposite. THE BOUNDARY WAS ASKED UNCONDITIONALLY. The header says a state read is performed only where the parent said the entry is there. That was true of the two directory addresses and FALSE of the slice: every expected slot issued `systemctl show` regardless of what the slice enumeration said. And the failure mode is worse than an inconsistency, because `systemctl show` on a unit that does not exist EXITS ZERO AND PRINTS DEFAULTS -- so an unprovisioned slice came back as a SUCCESSFUL read of values no host is carrying. The observer then saw an address absent from its namespace with a state supplied, refused it as a contradiction, and the cell could never be planned. That is the exact "first real observation of an unprovisioned host blocked instead of planning" failure gunbc.fabric_cell_observe records in its own header, reintroduced by the producer. A success exit is not evidence that a unit exists. The enumeration now decides, and the three answers are not two: HOLDS asks; RAN-AND-DOES-NOT-HOLD establishes absence and asks nothing, because absence is the namespace's answer; REFUSED establishes nothing and the address refuses with it rather than inheriting an absence nobody observed. "NOT A DIRECTORY" IS NOT "IS A REGULAR FILE". The first cut asked `test -f` after `test -d` and called everything else absent -- so a socket, a FIFO, a device node or a symlink reported a path that CANNOT BE CREATED as one that is free to create. `shell.Test.Exists` and `shell.Test.IsSymlink` are added to extdeps and cited: `-e` answers the question the two proxies were approximating and closes every case but one, because it follows symlinks, so a link whose target is gone answers false while the link still holds the name and still defeats mkdir. `-L` sees the link rather than through it. This module claimed as a virtue that it added no host vocabulary. It does now, and the reason is the signal I said to watch for: the observation model wanted a fact the declared vocabulary could not establish. That is a modeling question, and it is answered in extdeps where it belongs rather than by composing two tests that do not add up to the third. AND A THIRD FINDING THE FIRST TWO TURNED UP, WHICH IS ABOUT MY EVIDENCE RATHER THAN MY CODE. The first cut of the ask/do-not-ask law had to REACH the arm that asks in order to establish the contrast -- and that arm performs a host effect. It passed. It passed because claim_batch runs hermetically and systemd's operation carries a declared mock_response, so the row was asserting a law about acquisition against AUTHORED DATA. The same row under `gunbc run` errored: "failed to execute 'systemctl'". Two runners disagreeing about one row is what exposed it. The decision is now its own named fact -- FabricCellBoundaryStanding -- and the effect happens strictly downstream of it. Both rows are pure, no mock participates, and the receipt is that they now PASS under the runner that errored on them before. Mutation receipt: making the does-not-hold branch ask anyway reds the law. 11/11 rows green after restoring; 4052 file(s) parse-clean. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
Both side-chat findings were real and are fixed in 1. The enumeration now decides, and the three answers are not two: holds → ask; ran and does not hold → absence is the namespace's answer, ask nothing; refused → establishes nothing, so the address refuses with it rather than inheriting an absence nobody observed. 2. "not a directory" is not "is a regular file". The first cut asked This PR claimed as a virtue that it added no host vocabulary. It does now, and the reason is the signal I said to watch for: the observation model wanted a fact the declared vocabulary could not establish. That is a modeling question, answered in extdeps rather than by composing two tests that don't add up to the third. 3. A third finding the first two turned up, about my evidence rather than my code. The first version of the ask/don't-ask law had to reach the arm that asks in order to establish the contrast — and that arm performs a host effect. It passed. It passed because The decision is now its own named fact — Mutation receipt: making the does-not-hold branch ask anyway reds the law. 11/11 green after restoring; — sent from silent-bear-842 |
… "not there" from "could not look" (#9304) The last commit replaced `test -d` plus `test -f` with `test -e` plus `test -L` and called the occupancy question closed. It is not, and the second spelling fails for the same reason as the first: EVERY BOOLEAN TEST REPORTS ITS OWN FAILURE TO OBSERVE AS A NEGATIVE ANSWER. `test -e` returns false for a path that is not there AND for a path whose parent directory cannot be searched, so absence and "I could not look" -- the pair whose remedies are furthest apart -- still arrive identically. A third boolean would not have helped either; the shape of the answer is the problem. THE KIND IS NOW OBSERVED AS A KIND. `stat.File.FileType` is declared in extdeps.tools.stat beside the argv builder that already spelled those words, following the pair shape extdeps.shell already carries for IsExecutable. It reports the GNU type token on success and reports only that the read FAILED otherwise. It never reports absence, because a metadata read that failed establishes nothing. GNU stat uses lstat by default, so a symbolic link reads as a link rather than as its target -- which is what an observer deciding whether a name is OCCUPIED needs, since a link defeats mkdir whether or not its target exists. AND ABSENCE COMES FROM WHERE THIS FAMILY ALWAYS SAID IT COMES FROM: THE PARENT ENUMERATION. That is the observe module's own parent-observation law, applied here instead of approximated by a probe of the path itself. A failed metadata read is routed by what the containing directory already established -- absent if the parent listed and did not hold the entry, unreadable otherwise, INCLUDING when the parent itself was never observed. Nothing turns a failed read into an absence on its own authority any more. THE CELLS ROOT WAS THE PATH WITH NO PARENT TO ASK, AND THAT IS WHY ITS SPELLING SPLIT. Every other path in the family has a modeled containing namespace; the top of the family had none, so its absence was exactly the case the old code could not get right. `/opt` is now listed and asked whether it holds `fabric-cells` -- the same law one level up rather than an exemption from it -- and `fabric_cell_base_dir` is DERIVED from the parent and the entry name so the three do not fork. The recursion stops there deliberately: `/opt` is filesystem hierarchy standard, not something this family provisions, so an unobservable `/opt` is a refusal rather than another level. THE TYPE WORDS DECODE INTO A CLOSED VOCABULARY with an UnknownKind arm carrying the word verbatim. Folding an unseen type into a known arm would report a device node as a regular file, and the refusal messages now name what is actually sitting there. Three new laws, all pure: a failed metadata read is an absence only when the parent said so; the parent standing is derived from a successful enumeration and from nothing else -- an unlistable parent must not read as "lacks the entry", which is the arm that licenses an ADD; and the GNU words decode with no silent default. 14/14 green, 4052 file(s) parse-clean. Found by the side chat, which rejected the `test -e` fix I had just pushed. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…er was dropping them (#9304) Two things, and the first is the one the acquisition layer was CHANGED to make possible before anything downstream could use it. THE ENUMERATION PRODUCED THE EVIDENCE AND THE OBSERVER THREW IT AWAY. This lane changed the boundary read from probing one expected unit by name to enumerating `fabric-cell-*.slice`, for the stated reason that probing desired names can never discover an undesired one. But gunbc.fabric_cell_observe read the slice namespace ONLY to answer whether each EXPECTED boundary was present -- discovery was driven by the filesystem cells-root alone -- so `fabric-cell-srv3-07.slice` on a host that does not declare srv3-07 was enumerated, matched against nothing, and dropped. No output position, no refusal, no baseline row. The old blindness survived one layer downstream of the fix for it. Discovery now spans every family-owned namespace through ONE classifier, distinguished only by how a slot spells itself there -- `srv3-06` in the cells root, `fabric-cell-srv3-06.slice` in the slice namespace. Every committed identity is rendered FORWARD into the namespace's own spelling and compared; no identity is parsed back out of an observed name, which would be a second naming scheme that disagrees with the first the moment either spelling changes -- and which would have reported a committed slot's own slice as foreign. EITHER ENUMERATION FAILING REFUSES THE WHOLE DISCOVERY, because a population that was only half enumerated is not a population: an out-of-band slice would be invisible while the cells root read clean, and reporting the clean half as the answer is the empty-observation narrow. Seven existing rows went red on exactly this, all of them probes that supplied a cells-root listing and no slice enumeration -- fixtures written when the slice namespace answered a narrower question. They now supply both, which is what a fully-read host means. AND TWO ORPHAN OPERATIONS DELETED, FOUND BY REVIEW 56184. `shell.Test.Exists` and `shell.Test.IsSymlink` were added one commit and made unreachable the next, when the boolean approach was replaced by `stat -c %F`. That leaves declared surface with no consumer, standing beside a module header that argues why the primitive does not work -- the unmarked-scaffold shape exactly. Deleted. The header claiming this module "adds no new host vocabulary" is corrected rather than quietly dropped: it reaches four declared readonly operations and added ONE, `stat.File.FileType`, and the reason it added one is the finding -- no composition of boolean tests can express the fact the observer needs, so the gap was answered where modeling lives instead of approximated in the producer. 60 rows green by execution across the two witness files; 4052 file(s) parse-clean. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
Review 56184 is correct and the two orphan operations are deleted in On the orphans — the review found a real one and named the shape exactly. Worth noting for calibration: review 56180 approved the very commit this one rejects part of. It praised The third finding, which is the largest of the three. The acquisition layer was changed to enumerate Discovery now spans every family-owned namespace through one classifier, distinguished only by how a slot spells itself there. Every committed identity is rendered forward into the namespace's spelling and compared — no identity is parsed back out of an observed name, which would report a committed slot's own slice as foreign. And either enumeration failing refuses the whole discovery: a half-enumerated population is not a population. Seven existing rows went red on exactly that — all probes supplying a cells-root listing and no slice enumeration, written when the slice namespace answered a narrower question. They now supply both, which is what a fully-read host means. 60 rows green by execution across the two witness files; — sent from silent-bear-842 |
… from the namespace that owns it (#9304) Review 56191 noted that `fabric_cell_slice_namespace_name() = "fabric-slices"` is a wire label rather than a systemd pattern, so it does not fork the `fabric-cell-*.slice` authority. That is correct, and checking it turned up a DIFFERENT fork the note was not about: the label is spelled twice -- once in `fabric_cell_namespace_wire`, which owns how a namespace spells itself, and once in the location helper beside it. `fabric-cells-root` had the same pair, pre-existing. Two authorities for one concept, and a rename moves one of them. A discovered subject's location IS the namespace it was found in, so it is now projected through the function that already owns that spelling; both helpers become one-line derivations and each label appears exactly once in the module. Nothing about the discovery behaviour changes -- the strings are identical, which is the point: this is the class where a fork stays invisible until someone edits one side. 60 rows green, 4052 file(s) parse-clean. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
The entry-closure compile is done, and it is the acceptance-set item I have been reporting as outstanding. I have repeatedly said this PR has "the CLI closure resolves" and not "the CLI closure compiles", and declined to call the first the second. It now has the second: Zero blocking diagnostics over the production closure, fresh binary, emitting Rust. Why the earlier attempts read as "clean" and were not: they were budgeted at 900s and this closure takes 1428s. Exit 124 with no diagnostic line is byte-identical to a clean run through any grep for errors, which is exactly the instrument defect from earlier in this lane. This run captured One correction to my own instrument, worth stating rather than quietly dropping. My wrapper's derived count printed The 827 advisories are pre-existing corpus-wide classes, not this change: 632 This does not change what the PR claims to do, and it does not license running the provisioning apply — that remains gated on its own work. It closes the gap between resolves and compiles that I had left open in the receipts. — sent from silent-bear-842 |
…ed nothing admitted as a value (#9304) Two more producer defects, both found by the side chat, and both are the same shape as ones this lane already repaired -- one level down. THE CELL ROOT WAS ACQUIRED TWICE INSIDE ONE NOMINAL PROBE. `fabric_cell_probe_wet` observed it once while building the child-namespace entry and the address fold observed it AGAIN. That is not a duplicate cost, it is a consistency hole: if the path changes between the two reads, the observer receives a namespace derived from read A and address evidence derived from read B, and refuses a contradiction ITS OWN ACQUISITION LAYER MANUFACTURED. It fails closed, which is why it would have surfaced as host drift that never happened rather than as a false ADD -- and that is the worse diagnostic outcome, because the search starts on the host instead of in the producer. One acquisition per slot now, projected into both views. This is the same law the plan and apply paths already obey one level up, where the probe is bound once and compared twice; it is stated at slot grain because that is where a second read is cheapest to write and hardest to see. A COMPLETED CALL THAT PRINTED NOTHING WAS ADMITTED AS A READING. `systemctl show` can exit zero and print an empty line, and the reader took every success=true as a value -- so an empty string entered the digest, the digest differed from the desired one, the boundary reported MemberChanged, and a repair would have been planned against a property nobody read. It is the exit-zero-with-defaults defect again in its other half: there the unit did not exist, here the question was not answered. The three answers are now a pure projection: unsuccessful refuses, successful and empty refuses, successful and non-empty reads. THE POSITIVE CONTROLS ARE WHAT MAKE IT A LAW rather than a filter -- a reader that refused everything would satisfy the empty-output row and break every real host, so "0" and "infinity" are asserted READ. "0" because it is falsy-looking and a plausible accidental filter; "infinity" because it is what systemd prints for an unset cap and is therefore the most common real value. Mutation receipts, both executed: admitting empty output again reds law 15; projecting the namespace from a different observation than the addresses reds law 16. 62 rows green after restoring, 4052 file(s) parse-clean. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
All three of the side chat's remaining findings are addressed — two code defects in 1. One cell root was acquired twice inside one nominal probe. Confirmed exactly: 2. A completed call that printed nothing was admitted as a reading. Mutation receipts, both executed and restored: admitting empty output again reds law 15; projecting the namespace from a different observation than the addresses reds law 16. 62 rows green after restoring. 3. The body is rewritten, and the reason it mattered is worth stating: the squash record would have preserved the exact premise review disproved — "adds no new host vocabulary". The new body leads with the finding instead, that no composition of boolean tests can separate absence from could-not-look because every boolean reports its own failure to observe as a negative answer, and that needing one operation is the modeling signal rather than a failure. It carries a dated note saying it was rewritten and why, so the correction is visible rather than silent. — sent from silent-bear-842 |
`stat -c %F` can exit zero and print an empty line. The reader took every
success as a reading, so the empty string fell through the token decoder --
which is TOTAL, and that is exactly what made this reachable: an empty `%F`
is simply a word it has never met, so it decoded to `UnknownKind { word: "" }`
and left as `PathOccupiedByNonDirectory`. That is a POSITIVE assertion that
some object nobody can name occupies the path.
It fails closed for provisioning and is still wrong, because the two readings
send an operator to opposite remedies: "clear the obstruction, go look at it"
versus "the metadata read did not answer, the host read is broken". Success is
a fact about the CALL, never about whether the OUTPUT carrying the answer
arrived -- the same distinction the property reader already draws one operation
over for `systemctl show`.
So emptiness is decided BEFORE the decoder rather than inside it:
fabric_cell_path_kind_observation(success, output)
-> PathKindRead { kind } | PathKindReadRefused { cause }
`PathMetadataUnread` now carries that cause, threaded through the parent-routed
refusal, so the operator is told WHICH read failed. Without it the new cause
would have been a dead field, and the parent still decides absence-versus-
unreadable exactly as before.
LAW 17 asserts the whole `success x output` table. "0" and "weird new thing"
are asserted READ, so the rule is a presence test and not a value filter -- a
reader that dropped values it found uninteresting would pass an emptiness
assertion and still lose real answers. Receipt: mutating the wall away gives
FAIL, restoring gives PASS, file byte-identical after restore.
TWO STALE ANNOTATIONS RETIRED. The module header still credited `test -d` as
the mechanism separating absence from unreadability, while the same source four
paragraphs down explains why every boolean construction was retired for typed
`stat` evidence -- so a reader who stopped at the header left with the
predecessor mechanism. Review named one; checking found the claim spelled twice,
the second at the host-effects boundary. The surviving `test -d` mentions are
the historical narrative and are correct in the past tense.
63 rows green (17 acquire + 46 observe), 4052 file(s) parse-clean.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Fourth-round finding closed: successful-but-empty
|
Durable-record corrections applied, and the invocation receipt for this headBoth body corrections are in. The receipt line read Current-head CI has since completed — at the time the review was taken, Invocation receiptAdopting the proposed standard, with the content digest as the load-bearing field: The digest point is right and I can show why from this incident rather than in principle: the two sweep binaries I ran differed in output (the newer one reports the declaration index) while returning the same 4052 count. Size and mtime would have flagged that pair, but a measurement whose value is identical across two vintages is exactly the case where only a digest distinguishes which one answered. A bare — sent from silent-bear-842 |
# Conflicts: # dag/extdeps/tools/stat.dag
…ide the all-families signature (#9369) * The observer stops being handed a placeholder: three declared reads become a real probe (#fabric) gunbc.fabric_cell_observe has been the pure half of an observer whose other half did not exist -- every production call site passed fabric_cell_observation_unobserved, which says "nobody looked" and is honest precisely because nobody did. This is the half that looks. IT ADDS NO HOST VOCABULARY, AND THAT IS THE LOAD-BEARING FACT ABOUT IT. Filesystem.List, shell.Test.IsDirectory/IsFile and Systemctl.ShowProperty/ ListUnits are already declared with cited upstream authorities, so the acquisition composes five declared readonly reads. An acquisition layer that needed a NEW operation would have been telling us the observation model wants a fact the substrate cannot establish -- a modeling question wearing transport clothes -- and it did not. A DIRECTORY HAS FOUR HONEST STATES, NOT TWO, AND THE PAIR A Bool MERGES IS THE PAIR WITH OPPOSITE CONSEQUENCES. Filesystem.List answers success: Bool, so a listing that FAILED and a directory that IS NOT THERE arrive as the same false -- and not-there licenses a provisioning ADD while could-not-read must stop the line. They are separated by an independent declared read rather than by matching the error text for "No such file", because parsing a message is a heuristic standing where an operation was available. The fourth state is the one no Bool can hold: a regular FILE where a cell root belongs is not an absence and not a listing, and calling it absence would license an ADD whose failure surfaces at apply time as a mkdir refusal instead of here as an observation. THE CONTRADICTION LAW IS OBEYED AT ACQUISITION TIME RATHER THAN REPAIRED AFTERWARDS. A state read is performed only where the parent listing held the entry, so this module cannot hand the observer an address that is absent from its namespace and simultaneously read -- the impossible host the observer refuses, and the exact shape three fixtures on the last PR had to be corrected for. An absent directory contributes NO address probe at all rather than one carrying a refusal, because absence is the namespace's answer and overruling it would make a fresh host refuse instead of plan. ONE SPELLING AUTHORITY, BECAUSE THE OBSERVED SIDE NOW HAS TO PRODUCE THE SAME STRING. The boundary digest was spelled inline in the desired function; an observer composing its own would fork, and a reordered key would make every cell on every host read as drifted with the drift in the RENDERER rather than on the host. fabric_cell_boundary_state_digest_of is now that one function with two callers. It takes rendered strings and not typed magnitudes on purpose: the observed side holds whatever systemctl printed, which is a decimal or the word "infinity", and parsing it back to re-render it would invent a round trip the comparison does not need. NINE LAWS, ALL PURE, AND ONE MUTATION RECEIPT THAT CORRECTED ITS OWN ROW. The digest-agreement law looked like it should red when the shared spelling changes. Measured: it does NOT -- renaming a key in the shared function leaves it green, because both sides route through it, which is what one authority MEANS. That is not a hole, but the comment claiming otherwise would have been. The RED it actually carries is on the acquisition side and is executed: transposing memory_high and memory_swap_max where this module composes the reading fails it, restored green after. The row now states the boundary it measured rather than the one I assumed. The host effects are unreachable from a hermetic fixture by construction, and that is the boundary rather than a gap: a mocked listing would assert these laws against authored data instead of against the projection. WIRED AT THE PLAN AND APPLY CALL SITES, ONE OBSERVATION EACH. The apply path binds the probe ONCE and compares it against both the member-set fingerprint and the observed baseline -- two probes there could disagree with each other, and a plan refused because the host changed between two reads of the same run would name drift that never happened. Spark's planner stays unobserved, and correctly: it is a pure fn that does not read a host and must not claim to. 4052 file(s) parse-clean; nine rows green by execution; the CLI closure resolves through a witness that imports it. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * systemctl show exits zero for a unit that does not exist, so the boundary was asking about nothing (#9304) Two findings from the side chat, both real, and the first is a live contradiction in the module whose header claims the opposite. THE BOUNDARY WAS ASKED UNCONDITIONALLY. The header says a state read is performed only where the parent said the entry is there. That was true of the two directory addresses and FALSE of the slice: every expected slot issued `systemctl show` regardless of what the slice enumeration said. And the failure mode is worse than an inconsistency, because `systemctl show` on a unit that does not exist EXITS ZERO AND PRINTS DEFAULTS -- so an unprovisioned slice came back as a SUCCESSFUL read of values no host is carrying. The observer then saw an address absent from its namespace with a state supplied, refused it as a contradiction, and the cell could never be planned. That is the exact "first real observation of an unprovisioned host blocked instead of planning" failure gunbc.fabric_cell_observe records in its own header, reintroduced by the producer. A success exit is not evidence that a unit exists. The enumeration now decides, and the three answers are not two: HOLDS asks; RAN-AND-DOES-NOT-HOLD establishes absence and asks nothing, because absence is the namespace's answer; REFUSED establishes nothing and the address refuses with it rather than inheriting an absence nobody observed. "NOT A DIRECTORY" IS NOT "IS A REGULAR FILE". The first cut asked `test -f` after `test -d` and called everything else absent -- so a socket, a FIFO, a device node or a symlink reported a path that CANNOT BE CREATED as one that is free to create. `shell.Test.Exists` and `shell.Test.IsSymlink` are added to extdeps and cited: `-e` answers the question the two proxies were approximating and closes every case but one, because it follows symlinks, so a link whose target is gone answers false while the link still holds the name and still defeats mkdir. `-L` sees the link rather than through it. This module claimed as a virtue that it added no host vocabulary. It does now, and the reason is the signal I said to watch for: the observation model wanted a fact the declared vocabulary could not establish. That is a modeling question, and it is answered in extdeps where it belongs rather than by composing two tests that do not add up to the third. AND A THIRD FINDING THE FIRST TWO TURNED UP, WHICH IS ABOUT MY EVIDENCE RATHER THAN MY CODE. The first cut of the ask/do-not-ask law had to REACH the arm that asks in order to establish the contrast -- and that arm performs a host effect. It passed. It passed because claim_batch runs hermetically and systemd's operation carries a declared mock_response, so the row was asserting a law about acquisition against AUTHORED DATA. The same row under `gunbc run` errored: "failed to execute 'systemctl'". Two runners disagreeing about one row is what exposed it. The decision is now its own named fact -- FabricCellBoundaryStanding -- and the effect happens strictly downstream of it. Both rows are pure, no mock participates, and the receipt is that they now PASS under the runner that errored on them before. Mutation receipt: making the does-not-hold branch ask anyway reds the law. 11/11 rows green after restoring; 4052 file(s) parse-clean. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * Two spellings of one defect: no composition of boolean tests can tell "not there" from "could not look" (#9304) The last commit replaced `test -d` plus `test -f` with `test -e` plus `test -L` and called the occupancy question closed. It is not, and the second spelling fails for the same reason as the first: EVERY BOOLEAN TEST REPORTS ITS OWN FAILURE TO OBSERVE AS A NEGATIVE ANSWER. `test -e` returns false for a path that is not there AND for a path whose parent directory cannot be searched, so absence and "I could not look" -- the pair whose remedies are furthest apart -- still arrive identically. A third boolean would not have helped either; the shape of the answer is the problem. THE KIND IS NOW OBSERVED AS A KIND. `stat.File.FileType` is declared in extdeps.tools.stat beside the argv builder that already spelled those words, following the pair shape extdeps.shell already carries for IsExecutable. It reports the GNU type token on success and reports only that the read FAILED otherwise. It never reports absence, because a metadata read that failed establishes nothing. GNU stat uses lstat by default, so a symbolic link reads as a link rather than as its target -- which is what an observer deciding whether a name is OCCUPIED needs, since a link defeats mkdir whether or not its target exists. AND ABSENCE COMES FROM WHERE THIS FAMILY ALWAYS SAID IT COMES FROM: THE PARENT ENUMERATION. That is the observe module's own parent-observation law, applied here instead of approximated by a probe of the path itself. A failed metadata read is routed by what the containing directory already established -- absent if the parent listed and did not hold the entry, unreadable otherwise, INCLUDING when the parent itself was never observed. Nothing turns a failed read into an absence on its own authority any more. THE CELLS ROOT WAS THE PATH WITH NO PARENT TO ASK, AND THAT IS WHY ITS SPELLING SPLIT. Every other path in the family has a modeled containing namespace; the top of the family had none, so its absence was exactly the case the old code could not get right. `/opt` is now listed and asked whether it holds `fabric-cells` -- the same law one level up rather than an exemption from it -- and `fabric_cell_base_dir` is DERIVED from the parent and the entry name so the three do not fork. The recursion stops there deliberately: `/opt` is filesystem hierarchy standard, not something this family provisions, so an unobservable `/opt` is a refusal rather than another level. THE TYPE WORDS DECODE INTO A CLOSED VOCABULARY with an UnknownKind arm carrying the word verbatim. Folding an unseen type into a known arm would report a device node as a regular file, and the refusal messages now name what is actually sitting there. Three new laws, all pure: a failed metadata read is an absence only when the parent said so; the parent standing is derived from a successful enumeration and from nothing else -- an unlistable parent must not read as "lacks the entry", which is the arm that licenses an ADD; and the GNU words decode with no silent default. 14/14 green, 4052 file(s) parse-clean. Found by the side chat, which rejected the `test -e` fix I had just pushed. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * The slice enumeration was discovering unexpected units and the observer was dropping them (#9304) Two things, and the first is the one the acquisition layer was CHANGED to make possible before anything downstream could use it. THE ENUMERATION PRODUCED THE EVIDENCE AND THE OBSERVER THREW IT AWAY. This lane changed the boundary read from probing one expected unit by name to enumerating `fabric-cell-*.slice`, for the stated reason that probing desired names can never discover an undesired one. But gunbc.fabric_cell_observe read the slice namespace ONLY to answer whether each EXPECTED boundary was present -- discovery was driven by the filesystem cells-root alone -- so `fabric-cell-srv3-07.slice` on a host that does not declare srv3-07 was enumerated, matched against nothing, and dropped. No output position, no refusal, no baseline row. The old blindness survived one layer downstream of the fix for it. Discovery now spans every family-owned namespace through ONE classifier, distinguished only by how a slot spells itself there -- `srv3-06` in the cells root, `fabric-cell-srv3-06.slice` in the slice namespace. Every committed identity is rendered FORWARD into the namespace's own spelling and compared; no identity is parsed back out of an observed name, which would be a second naming scheme that disagrees with the first the moment either spelling changes -- and which would have reported a committed slot's own slice as foreign. EITHER ENUMERATION FAILING REFUSES THE WHOLE DISCOVERY, because a population that was only half enumerated is not a population: an out-of-band slice would be invisible while the cells root read clean, and reporting the clean half as the answer is the empty-observation narrow. Seven existing rows went red on exactly this, all of them probes that supplied a cells-root listing and no slice enumeration -- fixtures written when the slice namespace answered a narrower question. They now supply both, which is what a fully-read host means. AND TWO ORPHAN OPERATIONS DELETED, FOUND BY REVIEW 56184. `shell.Test.Exists` and `shell.Test.IsSymlink` were added one commit and made unreachable the next, when the boolean approach was replaced by `stat -c %F`. That leaves declared surface with no consumer, standing beside a module header that argues why the primitive does not work -- the unmarked-scaffold shape exactly. Deleted. The header claiming this module "adds no new host vocabulary" is corrected rather than quietly dropped: it reaches four declared readonly operations and added ONE, `stat.File.FileType`, and the reason it added one is the finding -- no composition of boolean tests can express the fact the observer needs, so the gap was answered where modeling lives instead of approximated in the producer. 60 rows green by execution across the two witness files; 4052 file(s) parse-clean. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * Each namespace label had two spellings; the location is now projected from the namespace that owns it (#9304) Review 56191 noted that `fabric_cell_slice_namespace_name() = "fabric-slices"` is a wire label rather than a systemd pattern, so it does not fork the `fabric-cell-*.slice` authority. That is correct, and checking it turned up a DIFFERENT fork the note was not about: the label is spelled twice -- once in `fabric_cell_namespace_wire`, which owns how a namespace spells itself, and once in the location helper beside it. `fabric-cells-root` had the same pair, pre-existing. Two authorities for one concept, and a rename moves one of them. A discovered subject's location IS the namespace it was found in, so it is now projected through the function that already owns that spelling; both helpers become one-line derivations and each label appears exactly once in the module. Nothing about the discovery behaviour changes -- the strings are identical, which is the point: this is the class where a fork stays invisible until someone edits one side. 60 rows green, 4052 file(s) parse-clean. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * One cell root read twice in one tick, and a completed call that printed nothing admitted as a value (#9304) Two more producer defects, both found by the side chat, and both are the same shape as ones this lane already repaired -- one level down. THE CELL ROOT WAS ACQUIRED TWICE INSIDE ONE NOMINAL PROBE. `fabric_cell_probe_wet` observed it once while building the child-namespace entry and the address fold observed it AGAIN. That is not a duplicate cost, it is a consistency hole: if the path changes between the two reads, the observer receives a namespace derived from read A and address evidence derived from read B, and refuses a contradiction ITS OWN ACQUISITION LAYER MANUFACTURED. It fails closed, which is why it would have surfaced as host drift that never happened rather than as a false ADD -- and that is the worse diagnostic outcome, because the search starts on the host instead of in the producer. One acquisition per slot now, projected into both views. This is the same law the plan and apply paths already obey one level up, where the probe is bound once and compared twice; it is stated at slot grain because that is where a second read is cheapest to write and hardest to see. A COMPLETED CALL THAT PRINTED NOTHING WAS ADMITTED AS A READING. `systemctl show` can exit zero and print an empty line, and the reader took every success=true as a value -- so an empty string entered the digest, the digest differed from the desired one, the boundary reported MemberChanged, and a repair would have been planned against a property nobody read. It is the exit-zero-with-defaults defect again in its other half: there the unit did not exist, here the question was not answered. The three answers are now a pure projection: unsuccessful refuses, successful and empty refuses, successful and non-empty reads. THE POSITIVE CONTROLS ARE WHAT MAKE IT A LAW rather than a filter -- a reader that refused everything would satisfy the empty-output row and break every real host, so "0" and "infinity" are asserted READ. "0" because it is falsy-looking and a plausible accidental filter; "infinity" because it is what systemd prints for an unset cap and is therefore the most common real value. Mutation receipts, both executed: admitting empty output again reds law 15; projecting the namespace from a different observation than the addresses reds law 16. 62 rows green after restoring, 4052 file(s) parse-clean. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * A completed stat that printed nothing named a kind nobody read (#9304) `stat -c %F` can exit zero and print an empty line. The reader took every success as a reading, so the empty string fell through the token decoder -- which is TOTAL, and that is exactly what made this reachable: an empty `%F` is simply a word it has never met, so it decoded to `UnknownKind { word: "" }` and left as `PathOccupiedByNonDirectory`. That is a POSITIVE assertion that some object nobody can name occupies the path. It fails closed for provisioning and is still wrong, because the two readings send an operator to opposite remedies: "clear the obstruction, go look at it" versus "the metadata read did not answer, the host read is broken". Success is a fact about the CALL, never about whether the OUTPUT carrying the answer arrived -- the same distinction the property reader already draws one operation over for `systemctl show`. So emptiness is decided BEFORE the decoder rather than inside it: fabric_cell_path_kind_observation(success, output) -> PathKindRead { kind } | PathKindReadRefused { cause } `PathMetadataUnread` now carries that cause, threaded through the parent-routed refusal, so the operator is told WHICH read failed. Without it the new cause would have been a dead field, and the parent still decides absence-versus- unreadable exactly as before. LAW 17 asserts the whole `success x output` table. "0" and "weird new thing" are asserted READ, so the rule is a presence test and not a value filter -- a reader that dropped values it found uninteresting would pass an emptiness assertion and still lose real answers. Receipt: mutating the wall away gives FAIL, restoring gives PASS, file byte-identical after restore. TWO STALE ANNOTATIONS RETIRED. The module header still credited `test -d` as the mechanism separating absence from unreadability, while the same source four paragraphs down explains why every boolean construction was retired for typed `stat` evidence -- so a reader who stopped at the header left with the predecessor mechanism. Review named one; checking found the claim spelled twice, the second at the host-effects boundary. The surviving `test -d` mentions are the historical narrative and are correct in the past tense. 63 rows green (17 acquire + 46 observe), 4052 file(s) parse-clean. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * Fabric-only converge scope: a request coproduct, not a scope flag beside the all-families signature (#9369) A fleet converge answers for a set of resource families, and the fabric execution-cells cut needs to answer for exactly one of them. The tempting spelling is a scope flag alongside the existing signature with the unselected families passed empty -- and the existing signature made that the ONLY spelling available: `fleet_converge_plan_subject` and `observed_baseline_text` both took all five families positionally, so a fabric-only plan could only be expressed by passing four empty lists. That is the empty-observation narrow at the plan layer. An empty list says THIS FAMILY HAS NO MEMBERS -- a positive claim about the host, and the one that licenses removals -- while the truth is that nobody was asked. The two states have opposite consequences and the signature could not tell them apart. OUT OF SCOPE IS NOT A REFUSAL EITHER, and that is what decides the shape. A refusal means a SELECTED obligation could not be satisfied; out of scope means the obligation was never selected. Rendering the unselected families as refusals would make every fabric-only plan operationally red for reasons no operator can act on. So neither state is represented: FleetConvergeRequest = FullHostConverge { host, timers, caps, spark, slots, fabric } | FabricExecutionCellsConverge { host, fabric } The fabric-only arm carries no position for timers, caps, spark or slots. There is no field to pass empty and none to pass refused, so the fabric-only CLI branch cannot invoke those observers and discard them -- it cannot reach them. Subject, member-set fingerprint, baseline text and apply-time admission are all derived FROM the request. SCOPE IS IN THE BASELINE TEXT, AND THE THIRD LAW IS WHY THAT IS NOT DEFENSIVE. Give the full-host request empty families and it renders no timer, cap, spark or slot rows -- exactly what the fabric-only arm renders, because it has no such fields. Without a scope row the two baselines would be BYTE-IDENTICAL, the two subjects would collide, and an apply could admit a fabric-only plan against a full-host re-observation. `witness_scope_row_is_what_separates_the_two_baselines` asserts the two line sets are the same size and differ only by which scope row they carry. APPLY-TIME ADMISSION WAS NOT PLANNED INTO THIS CHANGE; THE COMPILER FOUND IT. Rewriting the fingerprint signature made `fleet_converge_apply_subject_admits` refuse to compile, which surfaced that apply re-observes through the same function. Had it kept taking loose families, an apply could have compared a fabric-only plan against a full-host observation and read the mismatch as ordinary drift rather than as a scope confusion. Three of the four remaining call sites construct a `FullHostConverge` internally rather than taking a request parameter -- they ARE the full-host builders, so that is honest rather than a shim, and it leaves the CLI untouched until its fabric-only branch exists. The one site that needed the parameter is apply-time admission, where scope must survive from plan to apply or the two can be crossed. 4052 file(s) parse-clean. Three new scope laws green; full regression roster across nine witness files in progress. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * Scope becomes a routing fact and one annotation stops describing code that does not exist (#9369) THE ANNOTATION WAS FALSE AND THAT IS THE MOST IMPORTANT LINE OF THIS COMMIT. It said the fabric-only CLI branch "cannot invoke those observers ... it cannot reach them at all". There is no fabric-only CLI branch: the wet plan path observes timers, caps, slots and fabric unconditionally and builds a FullHostConverge, and the wet apply path still routes through the loose-family matchers. The sentence described the construction this type MAKES POSSIBLE in the present tense, which is how a reader concludes coverage exists that does not. Corrected in place, with the overclaim recorded rather than erased -- a type that makes a wall possible is not the wall. SCOPE IS NOW CARRIED ON THE SUBJECT, NOT ONLY INSIDE ITS FINGERPRINT. The baseline text already includes the scope row, so two scopes hash differently and a crossing was refusable. But that only DISTINGUISHES subjects; apply also has to DECIDE which observers to run, and A HASH CANNOT BE INVERTED into FullHost versus FabricExecutionCellsOnly. Distinguishing and routing are different jobs and a digest does only the first. It is a field rather than a parse of plan.txt for the same reason the bundle digest exists: recovering a routing decision by reading back rendered human text would make the reviewed artifact's PROSE load-bearing, and prose is the one part of a plan nobody hashes for meaning. fleet_converge_apply_subject_admits now compares scope explicitly through a total fleet_converge_scope_matches, written as a nested match rather than == so that adding a third scope arm FAILS TO COMPILE instead of quietly answering false and routing nothing. Two laws. witness_subject_carries_scope_as_a_readable_fact asserts the routing fact is readable off the subject rather than implied by a digest. witness_apply_refuses_a_scope_crossing_and_admits_its_own_scope refuses a crossing in BOTH directions and admits the matching scope -- the positive control is what makes it a scope wall rather than a blanket refusal. WHAT IS STILL THE PREDECESSOR SEAM, named rather than left to be discovered: the production wet apply does not call the request-based predicate at all. Its live path runs fleet_converge_member_set_fingerprint_matches and fleet_converge_observed_baseline_matches, both of which reconstruct a FullHostConverge internally, so the canonical decision exists while production consumes the older one. Porting that path, and then deleting or explicitly naming the loose-family matchers, is the next commit in this lane. Five scope laws green. 4052 file(s) parse-clean. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * The law said ONLY and checked something weaker; compare the remainder (#9369) witness_scope_row_is_what_separates_the_two_baselines asserted equal line counts, one expected scope row in each, and the shared host row. None of that establishes what its name claims. A mutation that CHANGED a non-scope row, or reordered the rows, preserves every one of those assertions -- so the law was green for reasons narrower than its name, which is the worst kind of green because it gets cited as coverage. It now compares the two line lists after normalising the scope rows away, so the remainder must be IDENTICAL. "Only" is in the check rather than in the name. RECEIPT, and the mutation is chosen to be one the OLD law passed: reordering the fabric arm's rows preserves line count, preserves both scope-row counts and preserves the host row, so every previous assertion still held. Mutated: FAIL. Restored: PASS. Source byte-identical after restore. Row order is not a cosmetic property here -- the baseline text feeds the fingerprint, so two orderings are two subject identities. line_is_a_scope_row routes through fleet_converge_scope_wire rather than matching two literals at each site, so a third scope's row cannot be left in the remainder and read as an ordinary difference. Found by review, not by me. The defect class is the one this lane has been finding all day in other people's instruments and has now found twice in its own: an assertion that passes for reasons narrower than its name implies. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * Production apply stops consuming the predecessor seam (#9369) Named in an earlier commit on this branch so it could not go quiet, and this is that commit: the canonical request-based decision existed while the LIVE apply path did not call it. fleet_converge_member_set_fingerprint_matches and fleet_converge_observed_baseline_matches each took five loose family parameters and rebuilt a FullHostConverge internally. So the request coproduct was the authority everywhere EXCEPT where it matters -- one authority standing beside the older one rather than replacing it. That is DESIGN section 3's two-authorities case arriving as a leftover instead of as a fork, which is the shape that survives review because nothing about it looks like a second model. Both now take the request. The apply CLI binds observed_request ONCE and passes it to both, rather than spelling the same five observations twice. IT ALSO CARRIES SCOPE INTO APPLY BY CONSTRUCTION, which is the part that is not just tidying. A caller now has to say WHICH REQUEST it observed, so a fabric-only plan re-observed as a full host cannot reach these predicates at all -- where before, the loose parameters would have been compared against whatever fingerprint the plan carried and a scope crossing would have read as ordinary member drift. The CLI constructs FullHostConverge because that is what today's wet apply observes -- there is still no fabric-only branch, and this commit does not pretend otherwise. 4122 file(s) parse-clean, debt=42, zero citation refusals. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * Repair the call sites the port broke, and retract two claims this branch made about its own strength (#9369) THE BREAKAGE: porting the two apply predicates to take a FleetConvergeRequest left four witness call sites passing five loose family parameters. Every fleet_converge_plan row failed on one resolve error. My local roster found it; CI independently reproduced it at the same three source positions, so the attribution was never in doubt. Two things about that are worth keeping. Parse-clean held at 4122 with zero citation refusals throughout, because call shape is RESOLVE and not PARSE -- a sweep count is not a build. And a regex fixed three of the four sites and silently skipped the fourth, because that one orders host: before observed_baseline_hex:. Three of four is the shape that survives a spot check and dies on a roster. RETRACTION ONE -- the scope crossing is REFUSED, not PREVENTED. An annotation said a fabric-only plan re-observed as a full host "cannot reach these at all". False: the predicates take a request of either arm beside a fingerprint STRING and nothing binds the two, so the crossing reaches them and FAILS a value comparison, because scope is inside the baseline the fingerprint hashes. What is actually unspellable is an observed request with no stated scope. Making the crossing unwritable would need the plan artifact to hand over its scope as a typed field rather than a hex string. RETRACTION TWO -- production apply did NOT stop consuming the predecessor seam, which is what the previous commit on this branch claimed. True of the two comparison helpers; false of fleet_converge_apply_subject_admits, the composed predicate that judges host AND scope AND fingerprint together and whose only callers are witnesses. So the canonical decision still sits beside production rather than being what production runs -- the same two-authorities shape the helpers were just ported out of, one level up. WHY IT CANNOT SIMPLY BE SWITCHED OVER, which is the useful residue: that predicate takes a FleetConvergePlanSubject and the subject carries SCOPE. The apply CLI has no subject -- it has an observed host plus a fingerprint string and a baseline hex read back from FILES THE PLAN WROTE, and nothing in that artifact records which scope was planned. So persisting the planned scope in the artifact is a PRECONDITION rather than a follow-up, and it is inside piece 1 of the brief, which requires scope to enter the plan text, the bundle digest and apply-time admission -- not only the subject and the baseline. All three overclaims on this branch have one shape: describing what a construction makes POSSIBLE as though it were already wired. Each was caught by review and none by a check. Recorded in the source rather than quietly edited, because the rate is the finding. 62/62 fleet_converge_plan rows green -- the file the port broke, fully re-measured. 4122 file(s) parse-clean, debt=42. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * Apply admits through one predicate, and an unreadable scope refuses instead of widening Review 56581 found production apply composing separate admission checks while the composed predicate had only witness callers -- two authorities for one decision, read off an annotation that said so in as many words. It was right, and the reason it could not simply be switched over was that the artifact recorded no scope: apply had a host it observed plus a fingerprint string and a baseline hex read back from files, so a subject could not be rebuilt without inventing the one field that decides routing. The subject is still not rebuilt, and that is deliberate. Turning the persisted fingerprint text back into a ContentHash would mean minting a content hash from untrusted file bytes -- the forgeable-mint shape DESIGN 4b records for UriValidatedScalar, where a sole_constructor type's emitted mirror admitted a representative from every refusal partition. So the direction is fixed one way: serialize the subject down to the wire the artifact holds and judge there. Nothing parses back. fleet_converge_apply_artifact_admits is the canonical predicate, fleet_converge_apply_subject_admits is a projection of it, and the wet path calls it. The planned scope is persisted as subject_scope.txt and decoded fail-closed. Defaulting an empty or unrecognised wire to FullHost is the expensive mistake available here: it would widen apply from one resource family to every family on the host, and the widened run is indistinguishable from what the pre-scope signature always did, so nothing would ever report the widening. That is the absorbing fallback with its deficit frequency zeroed by construction. Empty and unrecognised are separate causes because a write that did not complete and an artifact minted by another vintage have different remedies. The admission returns a coproduct rather than a Bool because the three failures have different operator remedies -- wrong machine, would actuate an unselected family, ordinary drift -- and only the third is routine. Executed on this tree, five rows, each returning true: witness_an_undecodable_scope_wire_refuses_and_never_defaults_to_full_host witness_apply_admission_distinguishes_its_three_refusals witness_a_foreign_artifact_reports_its_host_before_its_scope witness_apply_refuses_a_scope_crossing_and_admits_its_own_scope witness_scope_row_is_what_separates_the_two_baselines The fourth is the pre-existing law, passing after the delegation, so the refactor preserved the behaviour rather than merely compiling. gunbc.fleet_converge_plan_cli typechecks whole-module. The annotations disclosing the two-authority gap are deleted with the gap, not left standing over a construction that no longer has it. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * Delete the duplicate apply predicate, which compiled and silently resolved last-wins Review 56598 found two definitions of fleet_converge_apply_subject_admits in one module. My edit replaced the annotation above the original and appended a new definition below it, leaving both. The original is deleted; the artifact-level projection is the sole authority its annotation describes. THE REVIEW'S "WON'T COMPILE" IS REFUTED BY EXECUTION, AND THAT IS THE WORSE HALF. The duplicate compiled, and five witnesses passed over it. Probed deliberately with a two-line fixture -- one module, two definitions of one fn returning 1 and 2, and a caller: probe_dup_caller() -> returned 2 No diagnostic. The compiler admits duplicate function definitions in a single module and resolves the call to the LAST one. That is the same class DESIGN 4b already records for a census-AMBIGUOUS type name resolving by silent last-import-wins instead of refusing, one level over: definitions inside a module rather than imports across them. WHY MY OWN EVIDENCE WAS BLIND TO IT, stated because green-by-execution is the standard I hold others to. The two definitions were semantically equivalent -- both computed host AND scope AND fingerprint -- so no behavioural witness could discriminate them, whichever one won. Execution proved the behaviour and could not see the duplication, and no gate reported it either. I do not claim to know which definition CI was running. Verified after the deletion: witness_apply_refuses_a_scope_crossing_and_admits_its_own_scope returns true. compare_content_hash and ContentHashEqual became unused imports with the deleted body and are removed with it; the two annotation blocks that had been left stacked are merged into one. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> --------- Co-authored-by: Brian Searls <briansearls1@gmail.com> Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
* The observer stops being handed a placeholder: three declared reads become a real probe (#fabric) gunbc.fabric_cell_observe has been the pure half of an observer whose other half did not exist -- every production call site passed fabric_cell_observation_unobserved, which says "nobody looked" and is honest precisely because nobody did. This is the half that looks. IT ADDS NO HOST VOCABULARY, AND THAT IS THE LOAD-BEARING FACT ABOUT IT. Filesystem.List, shell.Test.IsDirectory/IsFile and Systemctl.ShowProperty/ ListUnits are already declared with cited upstream authorities, so the acquisition composes five declared readonly reads. An acquisition layer that needed a NEW operation would have been telling us the observation model wants a fact the substrate cannot establish -- a modeling question wearing transport clothes -- and it did not. A DIRECTORY HAS FOUR HONEST STATES, NOT TWO, AND THE PAIR A Bool MERGES IS THE PAIR WITH OPPOSITE CONSEQUENCES. Filesystem.List answers success: Bool, so a listing that FAILED and a directory that IS NOT THERE arrive as the same false -- and not-there licenses a provisioning ADD while could-not-read must stop the line. They are separated by an independent declared read rather than by matching the error text for "No such file", because parsing a message is a heuristic standing where an operation was available. The fourth state is the one no Bool can hold: a regular FILE where a cell root belongs is not an absence and not a listing, and calling it absence would license an ADD whose failure surfaces at apply time as a mkdir refusal instead of here as an observation. THE CONTRADICTION LAW IS OBEYED AT ACQUISITION TIME RATHER THAN REPAIRED AFTERWARDS. A state read is performed only where the parent listing held the entry, so this module cannot hand the observer an address that is absent from its namespace and simultaneously read -- the impossible host the observer refuses, and the exact shape three fixtures on the last PR had to be corrected for. An absent directory contributes NO address probe at all rather than one carrying a refusal, because absence is the namespace's answer and overruling it would make a fresh host refuse instead of plan. ONE SPELLING AUTHORITY, BECAUSE THE OBSERVED SIDE NOW HAS TO PRODUCE THE SAME STRING. The boundary digest was spelled inline in the desired function; an observer composing its own would fork, and a reordered key would make every cell on every host read as drifted with the drift in the RENDERER rather than on the host. fabric_cell_boundary_state_digest_of is now that one function with two callers. It takes rendered strings and not typed magnitudes on purpose: the observed side holds whatever systemctl printed, which is a decimal or the word "infinity", and parsing it back to re-render it would invent a round trip the comparison does not need. NINE LAWS, ALL PURE, AND ONE MUTATION RECEIPT THAT CORRECTED ITS OWN ROW. The digest-agreement law looked like it should red when the shared spelling changes. Measured: it does NOT -- renaming a key in the shared function leaves it green, because both sides route through it, which is what one authority MEANS. That is not a hole, but the comment claiming otherwise would have been. The RED it actually carries is on the acquisition side and is executed: transposing memory_high and memory_swap_max where this module composes the reading fails it, restored green after. The row now states the boundary it measured rather than the one I assumed. The host effects are unreachable from a hermetic fixture by construction, and that is the boundary rather than a gap: a mocked listing would assert these laws against authored data instead of against the projection. WIRED AT THE PLAN AND APPLY CALL SITES, ONE OBSERVATION EACH. The apply path binds the probe ONCE and compares it against both the member-set fingerprint and the observed baseline -- two probes there could disagree with each other, and a plan refused because the host changed between two reads of the same run would name drift that never happened. Spark's planner stays unobserved, and correctly: it is a pure fn that does not read a host and must not claim to. 4052 file(s) parse-clean; nine rows green by execution; the CLI closure resolves through a witness that imports it. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * systemctl show exits zero for a unit that does not exist, so the boundary was asking about nothing (#9304) Two findings from the side chat, both real, and the first is a live contradiction in the module whose header claims the opposite. THE BOUNDARY WAS ASKED UNCONDITIONALLY. The header says a state read is performed only where the parent said the entry is there. That was true of the two directory addresses and FALSE of the slice: every expected slot issued `systemctl show` regardless of what the slice enumeration said. And the failure mode is worse than an inconsistency, because `systemctl show` on a unit that does not exist EXITS ZERO AND PRINTS DEFAULTS -- so an unprovisioned slice came back as a SUCCESSFUL read of values no host is carrying. The observer then saw an address absent from its namespace with a state supplied, refused it as a contradiction, and the cell could never be planned. That is the exact "first real observation of an unprovisioned host blocked instead of planning" failure gunbc.fabric_cell_observe records in its own header, reintroduced by the producer. A success exit is not evidence that a unit exists. The enumeration now decides, and the three answers are not two: HOLDS asks; RAN-AND-DOES-NOT-HOLD establishes absence and asks nothing, because absence is the namespace's answer; REFUSED establishes nothing and the address refuses with it rather than inheriting an absence nobody observed. "NOT A DIRECTORY" IS NOT "IS A REGULAR FILE". The first cut asked `test -f` after `test -d` and called everything else absent -- so a socket, a FIFO, a device node or a symlink reported a path that CANNOT BE CREATED as one that is free to create. `shell.Test.Exists` and `shell.Test.IsSymlink` are added to extdeps and cited: `-e` answers the question the two proxies were approximating and closes every case but one, because it follows symlinks, so a link whose target is gone answers false while the link still holds the name and still defeats mkdir. `-L` sees the link rather than through it. This module claimed as a virtue that it added no host vocabulary. It does now, and the reason is the signal I said to watch for: the observation model wanted a fact the declared vocabulary could not establish. That is a modeling question, and it is answered in extdeps where it belongs rather than by composing two tests that do not add up to the third. AND A THIRD FINDING THE FIRST TWO TURNED UP, WHICH IS ABOUT MY EVIDENCE RATHER THAN MY CODE. The first cut of the ask/do-not-ask law had to REACH the arm that asks in order to establish the contrast -- and that arm performs a host effect. It passed. It passed because claim_batch runs hermetically and systemd's operation carries a declared mock_response, so the row was asserting a law about acquisition against AUTHORED DATA. The same row under `gunbc run` errored: "failed to execute 'systemctl'". Two runners disagreeing about one row is what exposed it. The decision is now its own named fact -- FabricCellBoundaryStanding -- and the effect happens strictly downstream of it. Both rows are pure, no mock participates, and the receipt is that they now PASS under the runner that errored on them before. Mutation receipt: making the does-not-hold branch ask anyway reds the law. 11/11 rows green after restoring; 4052 file(s) parse-clean. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * Two spellings of one defect: no composition of boolean tests can tell "not there" from "could not look" (#9304) The last commit replaced `test -d` plus `test -f` with `test -e` plus `test -L` and called the occupancy question closed. It is not, and the second spelling fails for the same reason as the first: EVERY BOOLEAN TEST REPORTS ITS OWN FAILURE TO OBSERVE AS A NEGATIVE ANSWER. `test -e` returns false for a path that is not there AND for a path whose parent directory cannot be searched, so absence and "I could not look" -- the pair whose remedies are furthest apart -- still arrive identically. A third boolean would not have helped either; the shape of the answer is the problem. THE KIND IS NOW OBSERVED AS A KIND. `stat.File.FileType` is declared in extdeps.tools.stat beside the argv builder that already spelled those words, following the pair shape extdeps.shell already carries for IsExecutable. It reports the GNU type token on success and reports only that the read FAILED otherwise. It never reports absence, because a metadata read that failed establishes nothing. GNU stat uses lstat by default, so a symbolic link reads as a link rather than as its target -- which is what an observer deciding whether a name is OCCUPIED needs, since a link defeats mkdir whether or not its target exists. AND ABSENCE COMES FROM WHERE THIS FAMILY ALWAYS SAID IT COMES FROM: THE PARENT ENUMERATION. That is the observe module's own parent-observation law, applied here instead of approximated by a probe of the path itself. A failed metadata read is routed by what the containing directory already established -- absent if the parent listed and did not hold the entry, unreadable otherwise, INCLUDING when the parent itself was never observed. Nothing turns a failed read into an absence on its own authority any more. THE CELLS ROOT WAS THE PATH WITH NO PARENT TO ASK, AND THAT IS WHY ITS SPELLING SPLIT. Every other path in the family has a modeled containing namespace; the top of the family had none, so its absence was exactly the case the old code could not get right. `/opt` is now listed and asked whether it holds `fabric-cells` -- the same law one level up rather than an exemption from it -- and `fabric_cell_base_dir` is DERIVED from the parent and the entry name so the three do not fork. The recursion stops there deliberately: `/opt` is filesystem hierarchy standard, not something this family provisions, so an unobservable `/opt` is a refusal rather than another level. THE TYPE WORDS DECODE INTO A CLOSED VOCABULARY with an UnknownKind arm carrying the word verbatim. Folding an unseen type into a known arm would report a device node as a regular file, and the refusal messages now name what is actually sitting there. Three new laws, all pure: a failed metadata read is an absence only when the parent said so; the parent standing is derived from a successful enumeration and from nothing else -- an unlistable parent must not read as "lacks the entry", which is the arm that licenses an ADD; and the GNU words decode with no silent default. 14/14 green, 4052 file(s) parse-clean. Found by the side chat, which rejected the `test -e` fix I had just pushed. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * The slice enumeration was discovering unexpected units and the observer was dropping them (#9304) Two things, and the first is the one the acquisition layer was CHANGED to make possible before anything downstream could use it. THE ENUMERATION PRODUCED THE EVIDENCE AND THE OBSERVER THREW IT AWAY. This lane changed the boundary read from probing one expected unit by name to enumerating `fabric-cell-*.slice`, for the stated reason that probing desired names can never discover an undesired one. But gunbc.fabric_cell_observe read the slice namespace ONLY to answer whether each EXPECTED boundary was present -- discovery was driven by the filesystem cells-root alone -- so `fabric-cell-srv3-07.slice` on a host that does not declare srv3-07 was enumerated, matched against nothing, and dropped. No output position, no refusal, no baseline row. The old blindness survived one layer downstream of the fix for it. Discovery now spans every family-owned namespace through ONE classifier, distinguished only by how a slot spells itself there -- `srv3-06` in the cells root, `fabric-cell-srv3-06.slice` in the slice namespace. Every committed identity is rendered FORWARD into the namespace's own spelling and compared; no identity is parsed back out of an observed name, which would be a second naming scheme that disagrees with the first the moment either spelling changes -- and which would have reported a committed slot's own slice as foreign. EITHER ENUMERATION FAILING REFUSES THE WHOLE DISCOVERY, because a population that was only half enumerated is not a population: an out-of-band slice would be invisible while the cells root read clean, and reporting the clean half as the answer is the empty-observation narrow. Seven existing rows went red on exactly this, all of them probes that supplied a cells-root listing and no slice enumeration -- fixtures written when the slice namespace answered a narrower question. They now supply both, which is what a fully-read host means. AND TWO ORPHAN OPERATIONS DELETED, FOUND BY REVIEW 56184. `shell.Test.Exists` and `shell.Test.IsSymlink` were added one commit and made unreachable the next, when the boolean approach was replaced by `stat -c %F`. That leaves declared surface with no consumer, standing beside a module header that argues why the primitive does not work -- the unmarked-scaffold shape exactly. Deleted. The header claiming this module "adds no new host vocabulary" is corrected rather than quietly dropped: it reaches four declared readonly operations and added ONE, `stat.File.FileType`, and the reason it added one is the finding -- no composition of boolean tests can express the fact the observer needs, so the gap was answered where modeling lives instead of approximated in the producer. 60 rows green by execution across the two witness files; 4052 file(s) parse-clean. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * Each namespace label had two spellings; the location is now projected from the namespace that owns it (#9304) Review 56191 noted that `fabric_cell_slice_namespace_name() = "fabric-slices"` is a wire label rather than a systemd pattern, so it does not fork the `fabric-cell-*.slice` authority. That is correct, and checking it turned up a DIFFERENT fork the note was not about: the label is spelled twice -- once in `fabric_cell_namespace_wire`, which owns how a namespace spells itself, and once in the location helper beside it. `fabric-cells-root` had the same pair, pre-existing. Two authorities for one concept, and a rename moves one of them. A discovered subject's location IS the namespace it was found in, so it is now projected through the function that already owns that spelling; both helpers become one-line derivations and each label appears exactly once in the module. Nothing about the discovery behaviour changes -- the strings are identical, which is the point: this is the class where a fork stays invisible until someone edits one side. 60 rows green, 4052 file(s) parse-clean. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * One cell root read twice in one tick, and a completed call that printed nothing admitted as a value (#9304) Two more producer defects, both found by the side chat, and both are the same shape as ones this lane already repaired -- one level down. THE CELL ROOT WAS ACQUIRED TWICE INSIDE ONE NOMINAL PROBE. `fabric_cell_probe_wet` observed it once while building the child-namespace entry and the address fold observed it AGAIN. That is not a duplicate cost, it is a consistency hole: if the path changes between the two reads, the observer receives a namespace derived from read A and address evidence derived from read B, and refuses a contradiction ITS OWN ACQUISITION LAYER MANUFACTURED. It fails closed, which is why it would have surfaced as host drift that never happened rather than as a false ADD -- and that is the worse diagnostic outcome, because the search starts on the host instead of in the producer. One acquisition per slot now, projected into both views. This is the same law the plan and apply paths already obey one level up, where the probe is bound once and compared twice; it is stated at slot grain because that is where a second read is cheapest to write and hardest to see. A COMPLETED CALL THAT PRINTED NOTHING WAS ADMITTED AS A READING. `systemctl show` can exit zero and print an empty line, and the reader took every success=true as a value -- so an empty string entered the digest, the digest differed from the desired one, the boundary reported MemberChanged, and a repair would have been planned against a property nobody read. It is the exit-zero-with-defaults defect again in its other half: there the unit did not exist, here the question was not answered. The three answers are now a pure projection: unsuccessful refuses, successful and empty refuses, successful and non-empty reads. THE POSITIVE CONTROLS ARE WHAT MAKE IT A LAW rather than a filter -- a reader that refused everything would satisfy the empty-output row and break every real host, so "0" and "infinity" are asserted READ. "0" because it is falsy-looking and a plausible accidental filter; "infinity" because it is what systemd prints for an unset cap and is therefore the most common real value. Mutation receipts, both executed: admitting empty output again reds law 15; projecting the namespace from a different observation than the addresses reds law 16. 62 rows green after restoring, 4052 file(s) parse-clean. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * A completed stat that printed nothing named a kind nobody read (#9304) `stat -c %F` can exit zero and print an empty line. The reader took every success as a reading, so the empty string fell through the token decoder -- which is TOTAL, and that is exactly what made this reachable: an empty `%F` is simply a word it has never met, so it decoded to `UnknownKind { word: "" }` and left as `PathOccupiedByNonDirectory`. That is a POSITIVE assertion that some object nobody can name occupies the path. It fails closed for provisioning and is still wrong, because the two readings send an operator to opposite remedies: "clear the obstruction, go look at it" versus "the metadata read did not answer, the host read is broken". Success is a fact about the CALL, never about whether the OUTPUT carrying the answer arrived -- the same distinction the property reader already draws one operation over for `systemctl show`. So emptiness is decided BEFORE the decoder rather than inside it: fabric_cell_path_kind_observation(success, output) -> PathKindRead { kind } | PathKindReadRefused { cause } `PathMetadataUnread` now carries that cause, threaded through the parent-routed refusal, so the operator is told WHICH read failed. Without it the new cause would have been a dead field, and the parent still decides absence-versus- unreadable exactly as before. LAW 17 asserts the whole `success x output` table. "0" and "weird new thing" are asserted READ, so the rule is a presence test and not a value filter -- a reader that dropped values it found uninteresting would pass an emptiness assertion and still lose real answers. Receipt: mutating the wall away gives FAIL, restoring gives PASS, file byte-identical after restore. TWO STALE ANNOTATIONS RETIRED. The module header still credited `test -d` as the mechanism separating absence from unreadability, while the same source four paragraphs down explains why every boolean construction was retired for typed `stat` evidence -- so a reader who stopped at the header left with the predecessor mechanism. Review named one; checking found the claim spelled twice, the second at the host-effects boundary. The surviving `test -d` mentions are the historical narrative and are correct in the past tense. 63 rows green (17 acquire + 46 observe), 4052 file(s) parse-clean. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * Fabric-only converge scope: a request coproduct, not a scope flag beside the all-families signature (#9369) A fleet converge answers for a set of resource families, and the fabric execution-cells cut needs to answer for exactly one of them. The tempting spelling is a scope flag alongside the existing signature with the unselected families passed empty -- and the existing signature made that the ONLY spelling available: `fleet_converge_plan_subject` and `observed_baseline_text` both took all five families positionally, so a fabric-only plan could only be expressed by passing four empty lists. That is the empty-observation narrow at the plan layer. An empty list says THIS FAMILY HAS NO MEMBERS -- a positive claim about the host, and the one that licenses removals -- while the truth is that nobody was asked. The two states have opposite consequences and the signature could not tell them apart. OUT OF SCOPE IS NOT A REFUSAL EITHER, and that is what decides the shape. A refusal means a SELECTED obligation could not be satisfied; out of scope means the obligation was never selected. Rendering the unselected families as refusals would make every fabric-only plan operationally red for reasons no operator can act on. So neither state is represented: FleetConvergeRequest = FullHostConverge { host, timers, caps, spark, slots, fabric } | FabricExecutionCellsConverge { host, fabric } The fabric-only arm carries no position for timers, caps, spark or slots. There is no field to pass empty and none to pass refused, so the fabric-only CLI branch cannot invoke those observers and discard them -- it cannot reach them. Subject, member-set fingerprint, baseline text and apply-time admission are all derived FROM the request. SCOPE IS IN THE BASELINE TEXT, AND THE THIRD LAW IS WHY THAT IS NOT DEFENSIVE. Give the full-host request empty families and it renders no timer, cap, spark or slot rows -- exactly what the fabric-only arm renders, because it has no such fields. Without a scope row the two baselines would be BYTE-IDENTICAL, the two subjects would collide, and an apply could admit a fabric-only plan against a full-host re-observation. `witness_scope_row_is_what_separates_the_two_baselines` asserts the two line sets are the same size and differ only by which scope row they carry. APPLY-TIME ADMISSION WAS NOT PLANNED INTO THIS CHANGE; THE COMPILER FOUND IT. Rewriting the fingerprint signature made `fleet_converge_apply_subject_admits` refuse to compile, which surfaced that apply re-observes through the same function. Had it kept taking loose families, an apply could have compared a fabric-only plan against a full-host observation and read the mismatch as ordinary drift rather than as a scope confusion. Three of the four remaining call sites construct a `FullHostConverge` internally rather than taking a request parameter -- they ARE the full-host builders, so that is honest rather than a shim, and it leaves the CLI untouched until its fabric-only branch exists. The one site that needed the parameter is apply-time admission, where scope must survive from plan to apply or the two can be crossed. 4052 file(s) parse-clean. Three new scope laws green; full regression roster across nine witness files in progress. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * Scope becomes a routing fact and one annotation stops describing code that does not exist (#9369) THE ANNOTATION WAS FALSE AND THAT IS THE MOST IMPORTANT LINE OF THIS COMMIT. It said the fabric-only CLI branch "cannot invoke those observers ... it cannot reach them at all". There is no fabric-only CLI branch: the wet plan path observes timers, caps, slots and fabric unconditionally and builds a FullHostConverge, and the wet apply path still routes through the loose-family matchers. The sentence described the construction this type MAKES POSSIBLE in the present tense, which is how a reader concludes coverage exists that does not. Corrected in place, with the overclaim recorded rather than erased -- a type that makes a wall possible is not the wall. SCOPE IS NOW CARRIED ON THE SUBJECT, NOT ONLY INSIDE ITS FINGERPRINT. The baseline text already includes the scope row, so two scopes hash differently and a crossing was refusable. But that only DISTINGUISHES subjects; apply also has to DECIDE which observers to run, and A HASH CANNOT BE INVERTED into FullHost versus FabricExecutionCellsOnly. Distinguishing and routing are different jobs and a digest does only the first. It is a field rather than a parse of plan.txt for the same reason the bundle digest exists: recovering a routing decision by reading back rendered human text would make the reviewed artifact's PROSE load-bearing, and prose is the one part of a plan nobody hashes for meaning. fleet_converge_apply_subject_admits now compares scope explicitly through a total fleet_converge_scope_matches, written as a nested match rather than == so that adding a third scope arm FAILS TO COMPILE instead of quietly answering false and routing nothing. Two laws. witness_subject_carries_scope_as_a_readable_fact asserts the routing fact is readable off the subject rather than implied by a digest. witness_apply_refuses_a_scope_crossing_and_admits_its_own_scope refuses a crossing in BOTH directions and admits the matching scope -- the positive control is what makes it a scope wall rather than a blanket refusal. WHAT IS STILL THE PREDECESSOR SEAM, named rather than left to be discovered: the production wet apply does not call the request-based predicate at all. Its live path runs fleet_converge_member_set_fingerprint_matches and fleet_converge_observed_baseline_matches, both of which reconstruct a FullHostConverge internally, so the canonical decision exists while production consumes the older one. Porting that path, and then deleting or explicitly naming the loose-family matchers, is the next commit in this lane. Five scope laws green. 4052 file(s) parse-clean. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * The law said ONLY and checked something weaker; compare the remainder (#9369) witness_scope_row_is_what_separates_the_two_baselines asserted equal line counts, one expected scope row in each, and the shared host row. None of that establishes what its name claims. A mutation that CHANGED a non-scope row, or reordered the rows, preserves every one of those assertions -- so the law was green for reasons narrower than its name, which is the worst kind of green because it gets cited as coverage. It now compares the two line lists after normalising the scope rows away, so the remainder must be IDENTICAL. "Only" is in the check rather than in the name. RECEIPT, and the mutation is chosen to be one the OLD law passed: reordering the fabric arm's rows preserves line count, preserves both scope-row counts and preserves the host row, so every previous assertion still held. Mutated: FAIL. Restored: PASS. Source byte-identical after restore. Row order is not a cosmetic property here -- the baseline text feeds the fingerprint, so two orderings are two subject identities. line_is_a_scope_row routes through fleet_converge_scope_wire rather than matching two literals at each site, so a third scope's row cannot be left in the remainder and read as an ordinary difference. Found by review, not by me. The defect class is the one this lane has been finding all day in other people's instruments and has now found twice in its own: an assertion that passes for reasons narrower than its name implies. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * Production apply stops consuming the predecessor seam (#9369) Named in an earlier commit on this branch so it could not go quiet, and this is that commit: the canonical request-based decision existed while the LIVE apply path did not call it. fleet_converge_member_set_fingerprint_matches and fleet_converge_observed_baseline_matches each took five loose family parameters and rebuilt a FullHostConverge internally. So the request coproduct was the authority everywhere EXCEPT where it matters -- one authority standing beside the older one rather than replacing it. That is DESIGN section 3's two-authorities case arriving as a leftover instead of as a fork, which is the shape that survives review because nothing about it looks like a second model. Both now take the request. The apply CLI binds observed_request ONCE and passes it to both, rather than spelling the same five observations twice. IT ALSO CARRIES SCOPE INTO APPLY BY CONSTRUCTION, which is the part that is not just tidying. A caller now has to say WHICH REQUEST it observed, so a fabric-only plan re-observed as a full host cannot reach these predicates at all -- where before, the loose parameters would have been compared against whatever fingerprint the plan carried and a scope crossing would have read as ordinary member drift. The CLI constructs FullHostConverge because that is what today's wet apply observes -- there is still no fabric-only branch, and this commit does not pretend otherwise. 4122 file(s) parse-clean, debt=42, zero citation refusals. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * Repair the call sites the port broke, and retract two claims this branch made about its own strength (#9369) THE BREAKAGE: porting the two apply predicates to take a FleetConvergeRequest left four witness call sites passing five loose family parameters. Every fleet_converge_plan row failed on one resolve error. My local roster found it; CI independently reproduced it at the same three source positions, so the attribution was never in doubt. Two things about that are worth keeping. Parse-clean held at 4122 with zero citation refusals throughout, because call shape is RESOLVE and not PARSE -- a sweep count is not a build. And a regex fixed three of the four sites and silently skipped the fourth, because that one orders host: before observed_baseline_hex:. Three of four is the shape that survives a spot check and dies on a roster. RETRACTION ONE -- the scope crossing is REFUSED, not PREVENTED. An annotation said a fabric-only plan re-observed as a full host "cannot reach these at all". False: the predicates take a request of either arm beside a fingerprint STRING and nothing binds the two, so the crossing reaches them and FAILS a value comparison, because scope is inside the baseline the fingerprint hashes. What is actually unspellable is an observed request with no stated scope. Making the crossing unwritable would need the plan artifact to hand over its scope as a typed field rather than a hex string. RETRACTION TWO -- production apply did NOT stop consuming the predecessor seam, which is what the previous commit on this branch claimed. True of the two comparison helpers; false of fleet_converge_apply_subject_admits, the composed predicate that judges host AND scope AND fingerprint together and whose only callers are witnesses. So the canonical decision still sits beside production rather than being what production runs -- the same two-authorities shape the helpers were just ported out of, one level up. WHY IT CANNOT SIMPLY BE SWITCHED OVER, which is the useful residue: that predicate takes a FleetConvergePlanSubject and the subject carries SCOPE. The apply CLI has no subject -- it has an observed host plus a fingerprint string and a baseline hex read back from FILES THE PLAN WROTE, and nothing in that artifact records which scope was planned. So persisting the planned scope in the artifact is a PRECONDITION rather than a follow-up, and it is inside piece 1 of the brief, which requires scope to enter the plan text, the bundle digest and apply-time admission -- not only the subject and the baseline. All three overclaims on this branch have one shape: describing what a construction makes POSSIBLE as though it were already wired. Each was caught by review and none by a check. Recorded in the source rather than quietly edited, because the rate is the finding. 62/62 fleet_converge_plan rows green -- the file the port broke, fully re-measured. 4122 file(s) parse-clean, debt=42. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * Apply admits through one predicate, and an unreadable scope refuses instead of widening Review 56581 found production apply composing separate admission checks while the composed predicate had only witness callers -- two authorities for one decision, read off an annotation that said so in as many words. It was right, and the reason it could not simply be switched over was that the artifact recorded no scope: apply had a host it observed plus a fingerprint string and a baseline hex read back from files, so a subject could not be rebuilt without inventing the one field that decides routing. The subject is still not rebuilt, and that is deliberate. Turning the persisted fingerprint text back into a ContentHash would mean minting a content hash from untrusted file bytes -- the forgeable-mint shape DESIGN 4b records for UriValidatedScalar, where a sole_constructor type's emitted mirror admitted a representative from every refusal partition. So the direction is fixed one way: serialize the subject down to the wire the artifact holds and judge there. Nothing parses back. fleet_converge_apply_artifact_admits is the canonical predicate, fleet_converge_apply_subject_admits is a projection of it, and the wet path calls it. The planned scope is persisted as subject_scope.txt and decoded fail-closed. Defaulting an empty or unrecognised wire to FullHost is the expensive mistake available here: it would widen apply from one resource family to every family on the host, and the widened run is indistinguishable from what the pre-scope signature always did, so nothing would ever report the widening. That is the absorbing fallback with its deficit frequency zeroed by construction. Empty and unrecognised are separate causes because a write that did not complete and an artifact minted by another vintage have different remedies. The admission returns a coproduct rather than a Bool because the three failures have different operator remedies -- wrong machine, would actuate an unselected family, ordinary drift -- and only the third is routine. Executed on this tree, five rows, each returning true: witness_an_undecodable_scope_wire_refuses_and_never_defaults_to_full_host witness_apply_admission_distinguishes_its_three_refusals witness_a_foreign_artifact_reports_its_host_before_its_scope witness_apply_refuses_a_scope_crossing_and_admits_its_own_scope witness_scope_row_is_what_separates_the_two_baselines The fourth is the pre-existing law, passing after the delegation, so the refactor preserved the behaviour rather than merely compiling. gunbc.fleet_converge_plan_cli typechecks whole-module. The annotations disclosing the two-authority gap are deleted with the gap, not left standing over a construction that no longer has it. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * Ownership becomes a match over the subject, so a host-scoped cell resource cannot inherit teardown /opt/fabric-cells is one directory shared by every cell on a machine. It is observed today and is not a converged member, and it could not become one: FabricCellMember.slot is a required RunnerSlotIdentity and fabric_cell_member_key joins on slot fields, so a host-scoped resource had no spelling except by naming an arbitrary slot as its owner -- and every available slot is the wrong answer rather than an imprecise one. The prior ownership authority was worse than an over-broad claim: fn fabric_cell_member_ownership(member) -> Optional<Ownership> { Present { value: Owned } } a constant function that never reads its parameter. There was no arm to add, so a host-scoped subject would have inherited Owned with nothing going non-exhaustive and nothing failing to compile. Total over its input by not inspecting it. That matters because Owned is teardown eligibility. A host that stopped serving fabric execution produces an empty desired set; an owned host root would reconcile as an observed extra and select removal of the directory containing every cell on the host. No step in that chain is a mistake, which is why a policy field saying do-not-remove would have been the wrong repair. So ownership is now a total match over a subject coproduct, in gunbc.fabric_cell_subject, and BOTH cell families route through it. No classification moves -- every slot member is still Owned. What moves is where the decision is made: a fourth subject kind now fails to compile at one function instead of being inherited silently by both families. The subject is upstream of both families rather than inside either, and that was forced rather than chosen: either family owning it would make the other import it and the first import back, which the acyclicity law forbids. It is NOT a spelling of FabricCellNamespace, and the difference is decidable. fabric_cell_address_namespace maps FabricCellRootAddress -- per-slot, created by this family, teardown-eligible -- onto the SHARED FabricCellsRootNamespace, because listing the shared parent is how one slot's cell root is proven absent. Deriving ownership from the namespace would make every slot's cell root Ensured and permanently un-tearable. Container and subject coincide for two of three addresses and diverge for the third, and the third is the one this piece is about. Ensured for the host root is measured, not assumed: nothing creates /opt/fabric-cells. gunbc.fabric_cell_converge emits no effects surface at all, corroborated by runner_lifecycle and by fleet_converge_plan's own apply line. No EffectsOrRefusal surface is exposed here for the same reason -- it would be the subsystem's first, with no consumer until the realization pieces land. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * Delete the duplicate apply predicate, which compiled and silently resolved last-wins Review 56598 found two definitions of fleet_converge_apply_subject_admits in one module. My edit replaced the annotation above the original and appended a new definition below it, leaving both. The original is deleted; the artifact-level projection is the sole authority its annotation describes. THE REVIEW'S "WON'T COMPILE" IS REFUTED BY EXECUTION, AND THAT IS THE WORSE HALF. The duplicate compiled, and five witnesses passed over it. Probed deliberately with a two-line fixture -- one module, two definitions of one fn returning 1 and 2, and a caller: probe_dup_caller() -> returned 2 No diagnostic. The compiler admits duplicate function definitions in a single module and resolves the call to the LAST one. That is the same class DESIGN 4b already records for a census-AMBIGUOUS type name resolving by silent last-import-wins instead of refusing, one level over: definitions inside a module rather than imports across them. WHY MY OWN EVIDENCE WAS BLIND TO IT, stated because green-by-execution is the standard I hold others to. The two definitions were semantically equivalent -- both computed host AND scope AND fingerprint -- so no behavioural witness could discriminate them, whichever one won. Execution proved the behaviour and could not see the duplication, and no gate reported it either. I do not claim to know which definition CI was running. Verified after the deletion: witness_apply_refuses_a_scope_crossing_and_admits_its_own_scope returns true. compare_content_hash and ContentHashEqual became unused imports with the deleted body and are removed with it; the two annotation blocks that had been left stacked are merged into one. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * Delete a stale annotation left above the witness it no longer describes Review 56618. The block above witness_teardown_eligibility_is_projected_from_the_subject_both_ways appeared twice: my edit anchored on the `test fn` line rather than on the annotation, so the replacement was inserted and the predecessor was left standing. THE STALE COPY WAS THE WEAKER ONE, which is why this is not purely cosmetic. It read "a host-scoped subject is never teardown-eligible and a slot-scoped one always is", describing the sampled-slot form the row had before it was strengthened; the current row folds over the whole fabric execution slot population and says so. A reader taking the first block at face value would have understood the assertion to be narrower than it is. SAME ROOT CAUSE AS THE DUPLICATE PREDICATE THIS STACK ALREADY CARRIES a fix for (119e00c): an edit anchored on the wrong boundary, leaving the thing it replaced. There the compiler admitted the duplicate silently; here nothing could refuse it at all, since 4c makes annotations invisible to every semantic pass. Swept the four touched files for repeated annotation openers -- clean. witness_teardown_eligibility_is_projected_from_the_subject_both_ways returns true after the deletion. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> --------- Co-authored-by: Brian Searls <briansearls1@gmail.com> Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
* The observer stops being handed a placeholder: three declared reads become a real probe (#fabric) gunbc.fabric_cell_observe has been the pure half of an observer whose other half did not exist -- every production call site passed fabric_cell_observation_unobserved, which says "nobody looked" and is honest precisely because nobody did. This is the half that looks. IT ADDS NO HOST VOCABULARY, AND THAT IS THE LOAD-BEARING FACT ABOUT IT. Filesystem.List, shell.Test.IsDirectory/IsFile and Systemctl.ShowProperty/ ListUnits are already declared with cited upstream authorities, so the acquisition composes five declared readonly reads. An acquisition layer that needed a NEW operation would have been telling us the observation model wants a fact the substrate cannot establish -- a modeling question wearing transport clothes -- and it did not. A DIRECTORY HAS FOUR HONEST STATES, NOT TWO, AND THE PAIR A Bool MERGES IS THE PAIR WITH OPPOSITE CONSEQUENCES. Filesystem.List answers success: Bool, so a listing that FAILED and a directory that IS NOT THERE arrive as the same false -- and not-there licenses a provisioning ADD while could-not-read must stop the line. They are separated by an independent declared read rather than by matching the error text for "No such file", because parsing a message is a heuristic standing where an operation was available. The fourth state is the one no Bool can hold: a regular FILE where a cell root belongs is not an absence and not a listing, and calling it absence would license an ADD whose failure surfaces at apply time as a mkdir refusal instead of here as an observation. THE CONTRADICTION LAW IS OBEYED AT ACQUISITION TIME RATHER THAN REPAIRED AFTERWARDS. A state read is performed only where the parent listing held the entry, so this module cannot hand the observer an address that is absent from its namespace and simultaneously read -- the impossible host the observer refuses, and the exact shape three fixtures on the last PR had to be corrected for. An absent directory contributes NO address probe at all rather than one carrying a refusal, because absence is the namespace's answer and overruling it would make a fresh host refuse instead of plan. ONE SPELLING AUTHORITY, BECAUSE THE OBSERVED SIDE NOW HAS TO PRODUCE THE SAME STRING. The boundary digest was spelled inline in the desired function; an observer composing its own would fork, and a reordered key would make every cell on every host read as drifted with the drift in the RENDERER rather than on the host. fabric_cell_boundary_state_digest_of is now that one function with two callers. It takes rendered strings and not typed magnitudes on purpose: the observed side holds whatever systemctl printed, which is a decimal or the word "infinity", and parsing it back to re-render it would invent a round trip the comparison does not need. NINE LAWS, ALL PURE, AND ONE MUTATION RECEIPT THAT CORRECTED ITS OWN ROW. The digest-agreement law looked like it should red when the shared spelling changes. Measured: it does NOT -- renaming a key in the shared function leaves it green, because both sides route through it, which is what one authority MEANS. That is not a hole, but the comment claiming otherwise would have been. The RED it actually carries is on the acquisition side and is executed: transposing memory_high and memory_swap_max where this module composes the reading fails it, restored green after. The row now states the boundary it measured rather than the one I assumed. The host effects are unreachable from a hermetic fixture by construction, and that is the boundary rather than a gap: a mocked listing would assert these laws against authored data instead of against the projection. WIRED AT THE PLAN AND APPLY CALL SITES, ONE OBSERVATION EACH. The apply path binds the probe ONCE and compares it against both the member-set fingerprint and the observed baseline -- two probes there could disagree with each other, and a plan refused because the host changed between two reads of the same run would name drift that never happened. Spark's planner stays unobserved, and correctly: it is a pure fn that does not read a host and must not claim to. 4052 file(s) parse-clean; nine rows green by execution; the CLI closure resolves through a witness that imports it. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * systemctl show exits zero for a unit that does not exist, so the boundary was asking about nothing (#9304) Two findings from the side chat, both real, and the first is a live contradiction in the module whose header claims the opposite. THE BOUNDARY WAS ASKED UNCONDITIONALLY. The header says a state read is performed only where the parent said the entry is there. That was true of the two directory addresses and FALSE of the slice: every expected slot issued `systemctl show` regardless of what the slice enumeration said. And the failure mode is worse than an inconsistency, because `systemctl show` on a unit that does not exist EXITS ZERO AND PRINTS DEFAULTS -- so an unprovisioned slice came back as a SUCCESSFUL read of values no host is carrying. The observer then saw an address absent from its namespace with a state supplied, refused it as a contradiction, and the cell could never be planned. That is the exact "first real observation of an unprovisioned host blocked instead of planning" failure gunbc.fabric_cell_observe records in its own header, reintroduced by the producer. A success exit is not evidence that a unit exists. The enumeration now decides, and the three answers are not two: HOLDS asks; RAN-AND-DOES-NOT-HOLD establishes absence and asks nothing, because absence is the namespace's answer; REFUSED establishes nothing and the address refuses with it rather than inheriting an absence nobody observed. "NOT A DIRECTORY" IS NOT "IS A REGULAR FILE". The first cut asked `test -f` after `test -d` and called everything else absent -- so a socket, a FIFO, a device node or a symlink reported a path that CANNOT BE CREATED as one that is free to create. `shell.Test.Exists` and `shell.Test.IsSymlink` are added to extdeps and cited: `-e` answers the question the two proxies were approximating and closes every case but one, because it follows symlinks, so a link whose target is gone answers false while the link still holds the name and still defeats mkdir. `-L` sees the link rather than through it. This module claimed as a virtue that it added no host vocabulary. It does now, and the reason is the signal I said to watch for: the observation model wanted a fact the declared vocabulary could not establish. That is a modeling question, and it is answered in extdeps where it belongs rather than by composing two tests that do not add up to the third. AND A THIRD FINDING THE FIRST TWO TURNED UP, WHICH IS ABOUT MY EVIDENCE RATHER THAN MY CODE. The first cut of the ask/do-not-ask law had to REACH the arm that asks in order to establish the contrast -- and that arm performs a host effect. It passed. It passed because claim_batch runs hermetically and systemd's operation carries a declared mock_response, so the row was asserting a law about acquisition against AUTHORED DATA. The same row under `gunbc run` errored: "failed to execute 'systemctl'". Two runners disagreeing about one row is what exposed it. The decision is now its own named fact -- FabricCellBoundaryStanding -- and the effect happens strictly downstream of it. Both rows are pure, no mock participates, and the receipt is that they now PASS under the runner that errored on them before. Mutation receipt: making the does-not-hold branch ask anyway reds the law. 11/11 rows green after restoring; 4052 file(s) parse-clean. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * Two spellings of one defect: no composition of boolean tests can tell "not there" from "could not look" (#9304) The last commit replaced `test -d` plus `test -f` with `test -e` plus `test -L` and called the occupancy question closed. It is not, and the second spelling fails for the same reason as the first: EVERY BOOLEAN TEST REPORTS ITS OWN FAILURE TO OBSERVE AS A NEGATIVE ANSWER. `test -e` returns false for a path that is not there AND for a path whose parent directory cannot be searched, so absence and "I could not look" -- the pair whose remedies are furthest apart -- still arrive identically. A third boolean would not have helped either; the shape of the answer is the problem. THE KIND IS NOW OBSERVED AS A KIND. `stat.File.FileType` is declared in extdeps.tools.stat beside the argv builder that already spelled those words, following the pair shape extdeps.shell already carries for IsExecutable. It reports the GNU type token on success and reports only that the read FAILED otherwise. It never reports absence, because a metadata read that failed establishes nothing. GNU stat uses lstat by default, so a symbolic link reads as a link rather than as its target -- which is what an observer deciding whether a name is OCCUPIED needs, since a link defeats mkdir whether or not its target exists. AND ABSENCE COMES FROM WHERE THIS FAMILY ALWAYS SAID IT COMES FROM: THE PARENT ENUMERATION. That is the observe module's own parent-observation law, applied here instead of approximated by a probe of the path itself. A failed metadata read is routed by what the containing directory already established -- absent if the parent listed and did not hold the entry, unreadable otherwise, INCLUDING when the parent itself was never observed. Nothing turns a failed read into an absence on its own authority any more. THE CELLS ROOT WAS THE PATH WITH NO PARENT TO ASK, AND THAT IS WHY ITS SPELLING SPLIT. Every other path in the family has a modeled containing namespace; the top of the family had none, so its absence was exactly the case the old code could not get right. `/opt` is now listed and asked whether it holds `fabric-cells` -- the same law one level up rather than an exemption from it -- and `fabric_cell_base_dir` is DERIVED from the parent and the entry name so the three do not fork. The recursion stops there deliberately: `/opt` is filesystem hierarchy standard, not something this family provisions, so an unobservable `/opt` is a refusal rather than another level. THE TYPE WORDS DECODE INTO A CLOSED VOCABULARY with an UnknownKind arm carrying the word verbatim. Folding an unseen type into a known arm would report a device node as a regular file, and the refusal messages now name what is actually sitting there. Three new laws, all pure: a failed metadata read is an absence only when the parent said so; the parent standing is derived from a successful enumeration and from nothing else -- an unlistable parent must not read as "lacks the entry", which is the arm that licenses an ADD; and the GNU words decode with no silent default. 14/14 green, 4052 file(s) parse-clean. Found by the side chat, which rejected the `test -e` fix I had just pushed. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * The slice enumeration was discovering unexpected units and the observer was dropping them (#9304) Two things, and the first is the one the acquisition layer was CHANGED to make possible before anything downstream could use it. THE ENUMERATION PRODUCED THE EVIDENCE AND THE OBSERVER THREW IT AWAY. This lane changed the boundary read from probing one expected unit by name to enumerating `fabric-cell-*.slice`, for the stated reason that probing desired names can never discover an undesired one. But gunbc.fabric_cell_observe read the slice namespace ONLY to answer whether each EXPECTED boundary was present -- discovery was driven by the filesystem cells-root alone -- so `fabric-cell-srv3-07.slice` on a host that does not declare srv3-07 was enumerated, matched against nothing, and dropped. No output position, no refusal, no baseline row. The old blindness survived one layer downstream of the fix for it. Discovery now spans every family-owned namespace through ONE classifier, distinguished only by how a slot spells itself there -- `srv3-06` in the cells root, `fabric-cell-srv3-06.slice` in the slice namespace. Every committed identity is rendered FORWARD into the namespace's own spelling and compared; no identity is parsed back out of an observed name, which would be a second naming scheme that disagrees with the first the moment either spelling changes -- and which would have reported a committed slot's own slice as foreign. EITHER ENUMERATION FAILING REFUSES THE WHOLE DISCOVERY, because a population that was only half enumerated is not a population: an out-of-band slice would be invisible while the cells root read clean, and reporting the clean half as the answer is the empty-observation narrow. Seven existing rows went red on exactly this, all of them probes that supplied a cells-root listing and no slice enumeration -- fixtures written when the slice namespace answered a narrower question. They now supply both, which is what a fully-read host means. AND TWO ORPHAN OPERATIONS DELETED, FOUND BY REVIEW 56184. `shell.Test.Exists` and `shell.Test.IsSymlink` were added one commit and made unreachable the next, when the boolean approach was replaced by `stat -c %F`. That leaves declared surface with no consumer, standing beside a module header that argues why the primitive does not work -- the unmarked-scaffold shape exactly. Deleted. The header claiming this module "adds no new host vocabulary" is corrected rather than quietly dropped: it reaches four declared readonly operations and added ONE, `stat.File.FileType`, and the reason it added one is the finding -- no composition of boolean tests can express the fact the observer needs, so the gap was answered where modeling lives instead of approximated in the producer. 60 rows green by execution across the two witness files; 4052 file(s) parse-clean. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * Each namespace label had two spellings; the location is now projected from the namespace that owns it (#9304) Review 56191 noted that `fabric_cell_slice_namespace_name() = "fabric-slices"` is a wire label rather than a systemd pattern, so it does not fork the `fabric-cell-*.slice` authority. That is correct, and checking it turned up a DIFFERENT fork the note was not about: the label is spelled twice -- once in `fabric_cell_namespace_wire`, which owns how a namespace spells itself, and once in the location helper beside it. `fabric-cells-root` had the same pair, pre-existing. Two authorities for one concept, and a rename moves one of them. A discovered subject's location IS the namespace it was found in, so it is now projected through the function that already owns that spelling; both helpers become one-line derivations and each label appears exactly once in the module. Nothing about the discovery behaviour changes -- the strings are identical, which is the point: this is the class where a fork stays invisible until someone edits one side. 60 rows green, 4052 file(s) parse-clean. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * One cell root read twice in one tick, and a completed call that printed nothing admitted as a value (#9304) Two more producer defects, both found by the side chat, and both are the same shape as ones this lane already repaired -- one level down. THE CELL ROOT WAS ACQUIRED TWICE INSIDE ONE NOMINAL PROBE. `fabric_cell_probe_wet` observed it once while building the child-namespace entry and the address fold observed it AGAIN. That is not a duplicate cost, it is a consistency hole: if the path changes between the two reads, the observer receives a namespace derived from read A and address evidence derived from read B, and refuses a contradiction ITS OWN ACQUISITION LAYER MANUFACTURED. It fails closed, which is why it would have surfaced as host drift that never happened rather than as a false ADD -- and that is the worse diagnostic outcome, because the search starts on the host instead of in the producer. One acquisition per slot now, projected into both views. This is the same law the plan and apply paths already obey one level up, where the probe is bound once and compared twice; it is stated at slot grain because that is where a second read is cheapest to write and hardest to see. A COMPLETED CALL THAT PRINTED NOTHING WAS ADMITTED AS A READING. `systemctl show` can exit zero and print an empty line, and the reader took every success=true as a value -- so an empty string entered the digest, the digest differed from the desired one, the boundary reported MemberChanged, and a repair would have been planned against a property nobody read. It is the exit-zero-with-defaults defect again in its other half: there the unit did not exist, here the question was not answered. The three answers are now a pure projection: unsuccessful refuses, successful and empty refuses, successful and non-empty reads. THE POSITIVE CONTROLS ARE WHAT MAKE IT A LAW rather than a filter -- a reader that refused everything would satisfy the empty-output row and break every real host, so "0" and "infinity" are asserted READ. "0" because it is falsy-looking and a plausible accidental filter; "infinity" because it is what systemd prints for an unset cap and is therefore the most common real value. Mutation receipts, both executed: admitting empty output again reds law 15; projecting the namespace from a different observation than the addresses reds law 16. 62 rows green after restoring, 4052 file(s) parse-clean. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * A completed stat that printed nothing named a kind nobody read (#9304) `stat -c %F` can exit zero and print an empty line. The reader took every success as a reading, so the empty string fell through the token decoder -- which is TOTAL, and that is exactly what made this reachable: an empty `%F` is simply a word it has never met, so it decoded to `UnknownKind { word: "" }` and left as `PathOccupiedByNonDirectory`. That is a POSITIVE assertion that some object nobody can name occupies the path. It fails closed for provisioning and is still wrong, because the two readings send an operator to opposite remedies: "clear the obstruction, go look at it" versus "the metadata read did not answer, the host read is broken". Success is a fact about the CALL, never about whether the OUTPUT carrying the answer arrived -- the same distinction the property reader already draws one operation over for `systemctl show`. So emptiness is decided BEFORE the decoder rather than inside it: fabric_cell_path_kind_observation(success, output) -> PathKindRead { kind } | PathKindReadRefused { cause } `PathMetadataUnread` now carries that cause, threaded through the parent-routed refusal, so the operator is told WHICH read failed. Without it the new cause would have been a dead field, and the parent still decides absence-versus- unreadable exactly as before. LAW 17 asserts the whole `success x output` table. "0" and "weird new thing" are asserted READ, so the rule is a presence test and not a value filter -- a reader that dropped values it found uninteresting would pass an emptiness assertion and still lose real answers. Receipt: mutating the wall away gives FAIL, restoring gives PASS, file byte-identical after restore. TWO STALE ANNOTATIONS RETIRED. The module header still credited `test -d` as the mechanism separating absence from unreadability, while the same source four paragraphs down explains why every boolean construction was retired for typed `stat` evidence -- so a reader who stopped at the header left with the predecessor mechanism. Review named one; checking found the claim spelled twice, the second at the host-effects boundary. The surviving `test -d` mentions are the historical narrative and are correct in the past tense. 63 rows green (17 acquire + 46 observe), 4052 file(s) parse-clean. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * Fabric-only converge scope: a request coproduct, not a scope flag beside the all-families signature (#9369) A fleet converge answers for a set of resource families, and the fabric execution-cells cut needs to answer for exactly one of them. The tempting spelling is a scope flag alongside the existing signature with the unselected families passed empty -- and the existing signature made that the ONLY spelling available: `fleet_converge_plan_subject` and `observed_baseline_text` both took all five families positionally, so a fabric-only plan could only be expressed by passing four empty lists. That is the empty-observation narrow at the plan layer. An empty list says THIS FAMILY HAS NO MEMBERS -- a positive claim about the host, and the one that licenses removals -- while the truth is that nobody was asked. The two states have opposite consequences and the signature could not tell them apart. OUT OF SCOPE IS NOT A REFUSAL EITHER, and that is what decides the shape. A refusal means a SELECTED obligation could not be satisfied; out of scope means the obligation was never selected. Rendering the unselected families as refusals would make every fabric-only plan operationally red for reasons no operator can act on. So neither state is represented: FleetConvergeRequest = FullHostConverge { host, timers, caps, spark, slots, fabric } | FabricExecutionCellsConverge { host, fabric } The fabric-only arm carries no position for timers, caps, spark or slots. There is no field to pass empty and none to pass refused, so the fabric-only CLI branch cannot invoke those observers and discard them -- it cannot reach them. Subject, member-set fingerprint, baseline text and apply-time admission are all derived FROM the request. SCOPE IS IN THE BASELINE TEXT, AND THE THIRD LAW IS WHY THAT IS NOT DEFENSIVE. Give the full-host request empty families and it renders no timer, cap, spark or slot rows -- exactly what the fabric-only arm renders, because it has no such fields. Without a scope row the two baselines would be BYTE-IDENTICAL, the two subjects would collide, and an apply could admit a fabric-only plan against a full-host re-observation. `witness_scope_row_is_what_separates_the_two_baselines` asserts the two line sets are the same size and differ only by which scope row they carry. APPLY-TIME ADMISSION WAS NOT PLANNED INTO THIS CHANGE; THE COMPILER FOUND IT. Rewriting the fingerprint signature made `fleet_converge_apply_subject_admits` refuse to compile, which surfaced that apply re-observes through the same function. Had it kept taking loose families, an apply could have compared a fabric-only plan against a full-host observation and read the mismatch as ordinary drift rather than as a scope confusion. Three of the four remaining call sites construct a `FullHostConverge` internally rather than taking a request parameter -- they ARE the full-host builders, so that is honest rather than a shim, and it leaves the CLI untouched until its fabric-only branch exists. The one site that needed the parameter is apply-time admission, where scope must survive from plan to apply or the two can be crossed. 4052 file(s) parse-clean. Three new scope laws green; full regression roster across nine witness files in progress. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * Scope becomes a routing fact and one annotation stops describing code that does not exist (#9369) THE ANNOTATION WAS FALSE AND THAT IS THE MOST IMPORTANT LINE OF THIS COMMIT. It said the fabric-only CLI branch "cannot invoke those observers ... it cannot reach them at all". There is no fabric-only CLI branch: the wet plan path observes timers, caps, slots and fabric unconditionally and builds a FullHostConverge, and the wet apply path still routes through the loose-family matchers. The sentence described the construction this type MAKES POSSIBLE in the present tense, which is how a reader concludes coverage exists that does not. Corrected in place, with the overclaim recorded rather than erased -- a type that makes a wall possible is not the wall. SCOPE IS NOW CARRIED ON THE SUBJECT, NOT ONLY INSIDE ITS FINGERPRINT. The baseline text already includes the scope row, so two scopes hash differently and a crossing was refusable. But that only DISTINGUISHES subjects; apply also has to DECIDE which observers to run, and A HASH CANNOT BE INVERTED into FullHost versus FabricExecutionCellsOnly. Distinguishing and routing are different jobs and a digest does only the first. It is a field rather than a parse of plan.txt for the same reason the bundle digest exists: recovering a routing decision by reading back rendered human text would make the reviewed artifact's PROSE load-bearing, and prose is the one part of a plan nobody hashes for meaning. fleet_converge_apply_subject_admits now compares scope explicitly through a total fleet_converge_scope_matches, written as a nested match rather than == so that adding a third scope arm FAILS TO COMPILE instead of quietly answering false and routing nothing. Two laws. witness_subject_carries_scope_as_a_readable_fact asserts the routing fact is readable off the subject rather than implied by a digest. witness_apply_refuses_a_scope_crossing_and_admits_its_own_scope refuses a crossing in BOTH directions and admits the matching scope -- the positive control is what makes it a scope wall rather than a blanket refusal. WHAT IS STILL THE PREDECESSOR SEAM, named rather than left to be discovered: the production wet apply does not call the request-based predicate at all. Its live path runs fleet_converge_member_set_fingerprint_matches and fleet_converge_observed_baseline_matches, both of which reconstruct a FullHostConverge internally, so the canonical decision exists while production consumes the older one. Porting that path, and then deleting or explicitly naming the loose-family matchers, is the next commit in this lane. Five scope laws green. 4052 file(s) parse-clean. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * The law said ONLY and checked something weaker; compare the remainder (#9369) witness_scope_row_is_what_separates_the_two_baselines asserted equal line counts, one expected scope row in each, and the shared host row. None of that establishes what its name claims. A mutation that CHANGED a non-scope row, or reordered the rows, preserves every one of those assertions -- so the law was green for reasons narrower than its name, which is the worst kind of green because it gets cited as coverage. It now compares the two line lists after normalising the scope rows away, so the remainder must be IDENTICAL. "Only" is in the check rather than in the name. RECEIPT, and the mutation is chosen to be one the OLD law passed: reordering the fabric arm's rows preserves line count, preserves both scope-row counts and preserves the host row, so every previous assertion still held. Mutated: FAIL. Restored: PASS. Source byte-identical after restore. Row order is not a cosmetic property here -- the baseline text feeds the fingerprint, so two orderings are two subject identities. line_is_a_scope_row routes through fleet_converge_scope_wire rather than matching two literals at each site, so a third scope's row cannot be left in the remainder and read as an ordinary difference. Found by review, not by me. The defect class is the one this lane has been finding all day in other people's instruments and has now found twice in its own: an assertion that passes for reasons narrower than its name implies. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * Production apply stops consuming the predecessor seam (#9369) Named in an earlier commit on this branch so it could not go quiet, and this is that commit: the canonical request-based decision existed while the LIVE apply path did not call it. fleet_converge_member_set_fingerprint_matches and fleet_converge_observed_baseline_matches each took five loose family parameters and rebuilt a FullHostConverge internally. So the request coproduct was the authority everywhere EXCEPT where it matters -- one authority standing beside the older one rather than replacing it. That is DESIGN section 3's two-authorities case arriving as a leftover instead of as a fork, which is the shape that survives review because nothing about it looks like a second model. Both now take the request. The apply CLI binds observed_request ONCE and passes it to both, rather than spelling the same five observations twice. IT ALSO CARRIES SCOPE INTO APPLY BY CONSTRUCTION, which is the part that is not just tidying. A caller now has to say WHICH REQUEST it observed, so a fabric-only plan re-observed as a full host cannot reach these predicates at all -- where before, the loose parameters would have been compared against whatever fingerprint the plan carried and a scope crossing would have read as ordinary member drift. The CLI constructs FullHostConverge because that is what today's wet apply observes -- there is still no fabric-only branch, and this commit does not pretend otherwise. 4122 file(s) parse-clean, debt=42, zero citation refusals. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * Repair the call sites the port broke, and retract two claims this branch made about its own strength (#9369) THE BREAKAGE: porting the two apply predicates to take a FleetConvergeRequest left four witness call sites passing five loose family parameters. Every fleet_converge_plan row failed on one resolve error. My local roster found it; CI independently reproduced it at the same three source positions, so the attribution was never in doubt. Two things about that are worth keeping. Parse-clean held at 4122 with zero citation refusals throughout, because call shape is RESOLVE and not PARSE -- a sweep count is not a build. And a regex fixed three of the four sites and silently skipped the fourth, because that one orders host: before observed_baseline_hex:. Three of four is the shape that survives a spot check and dies on a roster. RETRACTION ONE -- the scope crossing is REFUSED, not PREVENTED. An annotation said a fabric-only plan re-observed as a full host "cannot reach these at all". False: the predicates take a request of either arm beside a fingerprint STRING and nothing binds the two, so the crossing reaches them and FAILS a value comparison, because scope is inside the baseline the fingerprint hashes. What is actually unspellable is an observed request with no stated scope. Making the crossing unwritable would need the plan artifact to hand over its scope as a typed field rather than a hex string. RETRACTION TWO -- production apply did NOT stop consuming the predecessor seam, which is what the previous commit on this branch claimed. True of the two comparison helpers; false of fleet_converge_apply_subject_admits, the composed predicate that judges host AND scope AND fingerprint together and whose only callers are witnesses. So the canonical decision still sits beside production rather than being what production runs -- the same two-authorities shape the helpers were just ported out of, one level up. WHY IT CANNOT SIMPLY BE SWITCHED OVER, which is the useful residue: that predicate takes a FleetConvergePlanSubject and the subject carries SCOPE. The apply CLI has no subject -- it has an observed host plus a fingerprint string and a baseline hex read back from FILES THE PLAN WROTE, and nothing in that artifact records which scope was planned. So persisting the planned scope in the artifact is a PRECONDITION rather than a follow-up, and it is inside piece 1 of the brief, which requires scope to enter the plan text, the bundle digest and apply-time admission -- not only the subject and the baseline. All three overclaims on this branch have one shape: describing what a construction makes POSSIBLE as though it were already wired. Each was caught by review and none by a check. Recorded in the source rather than quietly edited, because the rate is the finding. 62/62 fleet_converge_plan rows green -- the file the port broke, fully re-measured. 4122 file(s) parse-clean, debt=42. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * Apply admits through one predicate, and an unreadable scope refuses instead of widening Review 56581 found production apply composing separate admission checks while the composed predicate had only witness callers -- two authorities for one decision, read off an annotation that said so in as many words. It was right, and the reason it could not simply be switched over was that the artifact recorded no scope: apply had a host it observed plus a fingerprint string and a baseline hex read back from files, so a subject could not be rebuilt without inventing the one field that decides routing. The subject is still not rebuilt, and that is deliberate. Turning the persisted fingerprint text back into a ContentHash would mean minting a content hash from untrusted file bytes -- the forgeable-mint shape DESIGN 4b records for UriValidatedScalar, where a sole_constructor type's emitted mirror admitted a representative from every refusal partition. So the direction is fixed one way: serialize the subject down to the wire the artifact holds and judge there. Nothing parses back. fleet_converge_apply_artifact_admits is the canonical predicate, fleet_converge_apply_subject_admits is a projection of it, and the wet path calls it. The planned scope is persisted as subject_scope.txt and decoded fail-closed. Defaulting an empty or unrecognised wire to FullHost is the expensive mistake available here: it would widen apply from one resource family to every family on the host, and the widened run is indistinguishable from what the pre-scope signature always did, so nothing would ever report the widening. That is the absorbing fallback with its deficit frequency zeroed by construction. Empty and unrecognised are separate causes because a write that did not complete and an artifact minted by another vintage have different remedies. The admission returns a coproduct rather than a Bool because the three failures have different operator remedies -- wrong machine, would actuate an unselected family, ordinary drift -- and only the third is routine. Executed on this tree, five rows, each returning true: witness_an_undecodable_scope_wire_refuses_and_never_defaults_to_full_host witness_apply_admission_distinguishes_its_three_refusals witness_a_foreign_artifact_reports_its_host_before_its_scope witness_apply_refuses_a_scope_crossing_and_admits_its_own_scope witness_scope_row_is_what_separates_the_two_baselines The fourth is the pre-existing law, passing after the delegation, so the refactor preserved the behaviour rather than merely compiling. gunbc.fleet_converge_plan_cli typechecks whole-module. The annotations disclosing the two-authority gap are deleted with the gap, not left standing over a construction that no longer has it. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * Ownership becomes a match over the subject, so a host-scoped cell resource cannot inherit teardown /opt/fabric-cells is one directory shared by every cell on a machine. It is observed today and is not a converged member, and it could not become one: FabricCellMember.slot is a required RunnerSlotIdentity and fabric_cell_member_key joins on slot fields, so a host-scoped resource had no spelling except by naming an arbitrary slot as its owner -- and every available slot is the wrong answer rather than an imprecise one. The prior ownership authority was worse than an over-broad claim: fn fabric_cell_member_ownership(member) -> Optional<Ownership> { Present { value: Owned } } a constant function that never reads its parameter. There was no arm to add, so a host-scoped subject would have inherited Owned with nothing going non-exhaustive and nothing failing to compile. Total over its input by not inspecting it. That matters because Owned is teardown eligibility. A host that stopped serving fabric execution produces an empty desired set; an owned host root would reconcile as an observed extra and select removal of the directory containing every cell on the host. No step in that chain is a mistake, which is why a policy field saying do-not-remove would have been the wrong repair. So ownership is now a total match over a subject coproduct, in gunbc.fabric_cell_subject, and BOTH cell families route through it. No classification moves -- every slot member is still Owned. What moves is where the decision is made: a fourth subject kind now fails to compile at one function instead of being inherited silently by both families. The subject is upstream of both families rather than inside either, and that was forced rather than chosen: either family owning it would make the other import it and the first import back, which the acyclicity law forbids. It is NOT a spelling of FabricCellNamespace, and the difference is decidable. fabric_cell_address_namespace maps FabricCellRootAddress -- per-slot, created by this family, teardown-eligible -- onto the SHARED FabricCellsRootNamespace, because listing the shared parent is how one slot's cell root is proven absent. Deriving ownership from the namespace would make every slot's cell root Ensured and permanently un-tearable. Container and subject coincide for two of three addresses and diverge for the third, and the third is the one this piece is about. Ensured for the host root is measured, not assumed: nothing creates /opt/fabric-cells. gunbc.fabric_cell_converge emits no effects surface at all, corroborated by runner_lifecycle and by fleet_converge_plan's own apply line. No EffectsOrRefusal surface is exposed here for the same reason -- it would be the subsystem's first, with no consumer until the realization pieces land. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * Delete the duplicate apply predicate, which compiled and silently resolved last-wins Review 56598 found two definitions of fleet_converge_apply_subject_admits in one module. My edit replaced the annotation above the original and appended a new definition below it, leaving both. The original is deleted; the artifact-level projection is the sole authority its annotation describes. THE REVIEW'S "WON'T COMPILE" IS REFUTED BY EXECUTION, AND THAT IS THE WORSE HALF. The duplicate compiled, and five witnesses passed over it. Probed deliberately with a two-line fixture -- one module, two definitions of one fn returning 1 and 2, and a caller: probe_dup_caller() -> returned 2 No diagnostic. The compiler admits duplicate function definitions in a single module and resolves the call to the LAST one. That is the same class DESIGN 4b already records for a census-AMBIGUOUS type name resolving by silent last-import-wins instead of refusing, one level over: definitions inside a module rather than imports across them. WHY MY OWN EVIDENCE WAS BLIND TO IT, stated because green-by-execution is the standard I hold others to. The two definitions were semantically equivalent -- both computed host AND scope AND fingerprint -- so no behavioural witness could discriminate them, whichever one won. Execution proved the behaviour and could not see the duplication, and no gate reported it either. I do not claim to know which definition CI was running. Verified after the deletion: witness_apply_refuses_a_scope_crossing_and_admits_its_own_scope returns true. compare_content_hash and ContentHashEqual became unused imports with the deleted body and are removed with it; the two annotation blocks that had been left stacked are merged into one. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * Delete a stale annotation left above the witness it no longer describes Review 56618. The block above witness_teardown_eligibility_is_projected_from_the_subject_both_ways appeared twice: my edit anchored on the `test fn` line rather than on the annotation, so the replacement was inserted and the predecessor was left standing. THE STALE COPY WAS THE WEAKER ONE, which is why this is not purely cosmetic. It read "a host-scoped subject is never teardown-eligible and a slot-scoped one always is", describing the sampled-slot form the row had before it was strengthened; the current row folds over the whole fabric execution slot population and says so. A reader taking the first block at face value would have understood the assertion to be narrower than it is. SAME ROOT CAUSE AS THE DUPLICATE PREDICATE THIS STACK ALREADY CARRIES a fix for (119e00c): an edit anchored on the wrong boundary, leaving the thing it replaced. There the compiler admitted the duplicate silently; here nothing could refuse it at all, since 4c makes annotations invisible to every semantic pass. Swept the four touched files for repeated annotation openers -- clean. witness_teardown_eligibility_is_projected_from_the_subject_both_ways returns true after the deletion. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * Model mkdir's exact form, which POSIX has and this module omitted Piece 3 of the provisioning brief needs a create that REFUSES a missing parent. extdeps.tools.mkdir modeled only the -p form, so there was no door -- the module mis-described its cited upstream by omission rather than by error. The authority it already cites (pubs.opengroup.org mkdir) specifies both forms. WHY THE REFUSING FORM IS THE ONE A CELL ROOT WANTS. The module's own annotation makes the argument without knowing it was doing so: "-p is the idempotent form: an existing directory is not an error. That is what makes this an ENSURE rather than a CREATE." A missing parent is a failure state, and -p answers it by FABRICATING the whole chain instead of refusing -- an absorbing fallback in the DESIGN section 5 sense. Where the parent belongs to another authority, that is not merely untidy: gunbc.fabric_cell_subject classifies /opt/fabric-cells as Ensured, meaning required present and never created by the cell family, so -p would have the cell family silently create the exact directory its ownership model says is not its to make, and nothing would report it. THE NON-IDEMPOTENCE IS NOT A DEFECT TO WORK AROUND, and the annotation says so rather than leaving a reader to discover it. This form is reached from an ADDED hunk, where the member was observed absent. A second run erroring means the observation and the host disagree, which is a line-stop, not a nuisance. A caller wanting run-anyway semantics wants -p and should say so by calling it. ONE WALL FIRED AND IS RECORDED BECAUSE IT WORKED. extdeps.exec.command.argv_command carries an admit_callers roster and refused mkdir_exact_command_at at resolve, naming every permitted caller. That is the per-function admission wall DESIGN 4b describes as a live capability, and it is live: the refusal could not be silenced from inside this module. Added to the roster in extdeps/exec/command.dag, which is where the fact belongs. Worth stating beside the duplicate-definition hole this stack already carries a fix for: same substrate, same afternoon, one guarantee real and one absent. Verified: mkdir_exact_args(path: /opt/fabric-cells/srv3-06) returns [/opt/fabric-cells/srv3-06] -- exact args, no -p. No duplicate declarations in the module (8 fns, checked). NOT PUSHED AS ITS OWN PR. Per the grouping ruling, piece 3 lands whole -- this surface, the closed effect sum, and the cell-root create that consumes both -- because a create surface with no consumer is section 6 residue. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * Cell realization spine: closed effect sum, all-or-nothing refusal, exact create WIP for piece 3. Full message on the eventual PR. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * Wire the realization into the family that reported it unavailable The effect sum had only witness callers, which is the same two-authorities shape piece 1 existed to close, one level over: a realization surface exists and the thing that needs it does not consult it. gunbc.fleet_converge_plan's FabricCellFamilyRealizationUnavailable arm rendered every action as an undifferentiated REFUSED-REALIZATION. That was honest when the fabric family had no realization surface at all -- fabric_cell_converge emits no effects and two carriers say so in their own words. It has one now, so the family asks it, and an operator reads WHICH address could not be realized and WHY rather than learning that something somewhere is unrealizable. THE PLAN STILL REFUSES AS A WHOLE and the lines say so rather than implying partial progress. A cell root created without its resource boundary is an UNBOUNDED cgroup slice, which is worse than an absent cell: absent is honest, unbounded looks provisioned, and the fleet's dominant failure is eviction under resource pressure. THE REFUSAL COUNT STAYS TOTAL WHEN THE PLAN REFUSES, and this is the part that would have gone wrong quietly. With a realization surface in place it is tempting to count only the TYPED refusals -- which today reports ONE deficit for a cell whose three addresses all went unapplied, understating the owed work by exactly the amount the all-or-nothing rule causes. The existing annotation ("the deficit is per unit of work owed") survives unaltered, because its argument was never about the arm it sat on. Verified on this tree, six rows returning true: witness_one_unrealizable_address_refuses_the_whole_cell_and_emits_no_effects witness_directory_addresses_alone_realize_both_creates witness_the_directory_create_carries_no_parents_flag witness_refusals_accumulate_across_addresses witness_the_refusal_causes_are_distinguished witness_apply_refuses_a_scope_crossing_and_admits_its_own_scope (pre-existing, after the wiring) No duplicate declarations in any touched file. Recording that my first two checks for that were FALSE POSITIVES from my own regex truncating at digits and then matching a prefix -- the check that decided it uses full identifiers. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * Re-express three cell rows against the refusal variant, now that the verb no longer carries the distinction The realization wiring moved WHICH address could not be realized and WHY off the action verb and onto the refusal variant. These three rows were still asserting the old rendering: one undifferentiated line per action, with ADD and CHANGE spelled into the line text. The distinction they exist to protect survives -- an absent boundary refuses through BoundaryDirectiveVocabularyIncomplete and a drifted one through CellChangeNotYetRealizable, each naming its address -- so the rows are re-expressed against that structure rather than the code reverted. The line count moves from one-per-action to per-address refusals plus exactly one whole-plan line, which is the all-or-nothing rule made visible: an operator reads which addresses are owed and that none was applied. Verified by execution rather than by adjusting to output: refusal_is_ rendered read false before this change and true after, on the same row. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * Delete the action-line renderer the realization wiring orphaned The realization call replaced the only use of fabric_cell_action_line, so it stood defined with zero callers -- an artifact with no consumer, introduced by this branch rather than inherited. Found by merging main and re-reading what the wiring displaced, not by a check. The five-arm match it carried is not lost: the refusal variants in gunbc.fabric_cell_effect now carry the same distinction, and each names its address rather than its action verb. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> --------- Co-authored-by: Brian Searls <briansearls1@gmail.com> Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
gunbc.fabric_cell_observehas been the pure half of an observer whose other half did not exist. Every production call site passedfabric_cell_observation_unobserved— which says nobody looked, and was honest precisely because nobody did. This is the half that looks.The load-bearing finding: no composition of boolean tests can express what the observer needs
The module was built out of boolean path tests twice, and both spellings failed the same way.
test -dplustest -fcalled everything else absent — so a socket, FIFO, device node or symlink reported a path that cannot be created as one free to create. Replacing that withtest -eplustest -Lcloses the occupancy half and still cannot answer the question, becausetest -ereturns false both for a path that is not there and for one whose parent directory cannot be searched. That is general, not a gap in the choice of tests: every boolean test reports its own failure to observe as a negative answer, and absence versus could-not-look is the pair whose remedies are furthest apart.So this PR adds exactly one operation —
extdeps.tools.statstat.File.FileType— which reports the type on success and reports only that the read failed otherwise. It never reports absence. Absence comes from where this family's law always said it comes from: the parent enumeration. That forced the cells root to split into parent plus entry name, since it was the one path with no modeled parent to ask, which is exactly why its absence was the case the old code could not get right.The needing of a new operation is the signal, not a failure: the observation model wanted a fact the declared vocabulary could not express, so it was answered where modeling lives rather than approximated in the producer.
Five producer defects found after the first "done"
systemctl showexits zero and prints defaults for a unit that does not exist, so an unprovisioned slice returned a successful read of values no host carries. The observer then saw an address absent from its namespace with a state supplied, refused it as a contradiction, and the cell could never be planned — the exact "first real observation of an unprovisioned host blocked instead of planning" failure the observe module records in its own header, reintroduced by the producer. The slice enumeration now decides whether the read happens at all, and the three answers are not two: holds asks; ran-and-does-not-hold contributes nothing, because absence is the namespace's answer; refused refuses with the namespace rather than inheriting an absence nobody observed.The slice enumeration discovered unexpected units and the observer dropped them. The acquisition was changed to enumerate
fabric-cell-*.slicerather than probe one unit by name — precisely so an undesired unit could be found — andgunbc.fabric_cell_observeread that namespace only to answer whether each expected boundary was present. The enumeration produced the evidence and the observer threw it away. Discovery now spans both family-owned namespaces through one classifier, matching by rendering every committed identity forward into that namespace's spelling; a reverse parse would have reported a committed slot's own slice as foreign. Either enumeration failing refuses the whole discovery.One cell root was acquired twice inside one nominal probe — once for the child namespace, once in the address fold. Not a duplicate cost but a consistency hole: if the path changes between the reads, the observer refuses a contradiction its own acquisition layer manufactured, surfacing as host drift that never happened. One acquisition per slot now, projected into both views.
A completed call that printed nothing was admitted as a reading.
systemctl showcan exit zero and print an empty line; that entered the digest, differed from the desired one, and would have planned a repair against a property nobody read. Now a pure three-answer projection, with"0"and"infinity"asserted read so the rule is a law rather than a filter that refuses everything.What else the diff does
fnthat does not read a host and must not claim to.readonly.Receipts
4052 file(s) parse-cleantest.claim.fabric_cell_acquire_witness(17) andtest.claim.fabric_cell_observe_witness(46), re-taken at4c8eb4a29cagainst an absolutely-pathed local binary (see the provenance note below)gunbc compile --entry dag/gunbc/fleet_converge_plan_cli.dag --target rust→rc=0, 1428s, 486 files, 0 blocking.DESIGN.mdrecords that no required phase emits over a closure reaching call sites, so a green floor cannot establish thisbuild,floor,witnessesWhat this does not do
It applies nothing. Provisioning is the next cut, and it does not begin here.