Repository navigation
compute fabric design - #9467
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>
…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>
… "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>
… 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>
…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>
`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>
…ide 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>
… 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>
…cope # Conflicts: # dag/extdeps/tools/stat.dag
…#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>
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>
…nch 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>
…nstead 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>
…ource 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>
…olved 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>
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>
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>
…act create WIP for piece 3. Full message on the eventual PR. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
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>
…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>
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>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: ac0862b18f
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| CellPlanRealizable { effects: es } => | ||
| map(es, e => match e { | ||
| FabricCellDirectoryCreate { path: pth, command: _ } => | ||
| concat("REALIZE fabric-cell create-directory ", pth as String) |
There was a problem hiding this comment.
Execute directory effects before labeling them realized
When an admitted readback has the resource boundary converged but either directory absent, this branch emits REALIZE fabric-cell create-directory even though the fleet-converge apply path never executes the stored ArgvCommand: fabric_cell_apply_lines turns every returned line into a shell comment and explicitly states that no fabric-cell host effect is emitted. Applying such a plan therefore leaves the directory absent despite the plan presenting it as realizable; either wire these effects into the apply/host-effect path or continue rendering them as unavailable.
Useful? React with 👍 / 👎.
Auto-opened by session-dashboard for session
silent-bear-842.Pushing to
fabric/cell-realizationadvances this PR.Worker attestation
Before flipping this PR to ready for review, confirm each item:
npm test,cargo test) and the result.Closes #Ndirective.Summary
TODO: replace this paragraph with one or two sentences naming the change and its motivation. Reviewers read this first.
Test plan