Repository navigation
PRINT-6: register printer-01 as a fleet intent row, and give the AMS Lite its own feed-path authority - #10344
Conversation
…own feed-path authority The first printer is physically received, labelled printer-01 by the operator, and on the operator LAN. This lands the three facts that were previously missing, each in its own authority, and closes one roster that had been standing empty. THE PRINTER IS AN APPLIANCE, MODELLED LIKE THE BMC AND NOT LIKE A HOST. fleet_reach's probe targets and fleet_probe_ready_ssh_target are SSH-shaped, and a printer has no SSH, so routing it through them would have required a HostIdentity that names no host. The existing precedent is the BMC: a network-reachable device with its own endpoint authority sitting beside the host endpoints in fleet_intent_network. printer_01_lan_endpoint follows it, with host absent and a new FabricationAppliance attachment role. The address was discovered rather than declared: a two-port sweep of the operator segment from srv2 found one responder on 990 whose FTPS banner is `220 BBL-P003 FTP Server`. 8883 is closed, which is the signature of a printer that is on the network but not in LAN Mode, so no route from this repo can command it yet. That is also why the endpoint carries no transport rows: a transport is a realization bound to a shape, and the shape is not yet observed. network_reachability already says ContainerRunner cannot reach HostLan, and the discovery confirmed it -- the sweep had to run from srv2 because the session container cannot see the segment. Every future route to this address is brokered through a host on the LAN by the model, not by a workaround. THE ASSET ROW IS DECLARED ONCE, IN fleet_physical_inventory, beside switch_01 and pdu_01, and imported by manufacturing_manifest. A printer is a physical asset on the operator floor before it is anything the manifest cares about, so re-authoring its identity or catalog there would be a second authority for one machine. physical_serial stays absent: a serial corroborates an individuation rather than establishing one, which is the rule that module already states. owned_printer_inventory was empty pending physical receipt. It now carries printer_01_asset, which makes w_every_live_inventory_row_names_the_qualifiable_product discriminating for the first time -- it was folding over an empty list, so it could not fail. THE AMS LITE IS A SEPARATE SUBJECT AND THE EXCLUSION IS A PROPERTY OF THE FEED PATH. The A1 mini's own vendor table grades Tpu and Pva SuitabilityIdeal; the AMS Lite manual excludes both. Those are not in conflict -- the machine prints them from the external spool holder, and the AMS Lite cannot convey them -- so authoring the exclusions into the machine module would have forced one of two true facts to be deleted. The accessory gets its own module and citation. FeedPathSuitability is deliberately a second type rather than a reuse of MaterialSuitability, because the two grade different subjects and only one carries a cause. The manual gives two distinct reasons, and the fiber-reinforced clause ends in "etc": keying that row to FiberReinforcedPolymer rather than to the six spellings it lists covers the members it declined to enumerate, where a blocklist would have admitted the seventh. Pla and Petg are NOT graded admitted. A first draft graded them so on the reasoning that the AMS Lite plainly feeds them; no quoted sentence said so, and "plainly" is not a citation. Both rows were withdrawn and the lookup answers FeedPathUnstated for them, which is the state the manual is actually in. They return when the supported-material statement is quoted. Evidence, by execution: the three AMS Lite witnesses and the manifest live-row witness each return true under `gunbc run` on this tree. The discriminating one is w_tpu_is_machine_ideal_and_feed_path_excluded, which a model collapsing the two grades into one cannot make green; w_an_unmentioned_material_is_unstated_not_admitted goes red the moment a row is authored without a citation behind it. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01TuCmz2HjuYzP6GWLgw11E4
cc1554d to
af24107
Compare
…was first repaired at review 59754 found that feed_path_suitability folded to the LAST matching row, so a table naming one material twice resolved silently to one of them. That is byte-for-byte the defect the sibling material_suitability in the same file had already been rewritten to refuse via SuitabilityRowsConflict -- whose own annotation says "a defect class is not closed at one site." The finding is correct: the new lookup landed the closed class again, sixty lines from the repair, in the same file and the same change. THE FIX IS ONE AUTHORITY, NOT TWO MATCHING SHAPES. Mirroring Resolved/RowsConflict into a second hand-rolled scan would have satisfied the review while leaving the same decision written twice, and DESIGN section 2 prices that as re-invention -- the next repair would again have to find both sites. material_key_match_count now owns the question "how many rows name this material", both lookups ask it, and neither counts for itself. The value selection stays local to each lookup because it is only ever reached when the count is exactly one. SuitabilityScan is deleted rather than left beside its replacement. It existed only to carry the count alongside the value through the old fold, and nothing outside this module referenced it. feed_path_suitability now returns FeedPathSuitabilityLookup, and ams_lite_feeds relays the whole lookup rather than unwrapping to a bare suitability: a duplicate row is a defect in the AMS Lite module's own rows, and a caller handed FeedPathUnstated for one could not distinguish a manual that is silent from a table that is broken. EVIDENCE, AND THE CONTROL IS PROVEN TO FLIP. w_a_material_named_twice_refuses_rather_than_taking_ the_last_row is enrolled as the executing evidence that the class stays closed at THIS site. It was run against the defect it describes -- the count arm forced to the pre-review behaviour -- and returned false, then true against the repair. A conflict witness that cannot go red is decoration, so the pair was measured rather than assumed. The three existing AMS Lite witnesses and the two coupon witnesses that consume material_suitability (w_pla_is_ideal_and_abs_is_not_recommended, w_a_material_the_vendor_did_not_grade_reads_unstated) all return true on this tree, so the rewire of the sibling changed no answer it already gave. Also prunes the unused material_suitability / MaterialSuitabilityLookup / a1_mini_material_suitability imports from the witness module, the review's second finding. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01TuCmz2HjuYzP6GWLgw11E4
|
Both findings fixed in 28a3c50. Finding 1 ( I did not take the mirror-the-shape option. Hand-rolling a second
The control was proven to flip, not assumed to. Also re-ran the two coupon witnesses that consume the rewired sibling — Finding 2 (unused imports) — pruned. The witness module no longer imports Compile gate on this tree: 25 blocking errors, none in any file this PR touches — 25 is the current — sent from crisp-ibex-710 |
…icing an exception for it review 59779 approved the previous shape and recorded the quadratic fold as a minor observation, excused on the grounds that "at table sizes of a dozen rows this is well below the section 6 bare-minimum-cost floor". That is the argument the rule refuses in its own words: a proven cost-shape defect is always fixed regardless of the realized n, because "n is small here" is not a time-stable fact -- reuse changes n -- and pricing a per-site exception is itself redundant work. So the observation is taken as a finding. feed_path_row_keys and suitability_row_keys each folded with concat(acc, [r.material]), which is quadratic, and existed only to feed a counter that then walked the rows a second time. Both are deleted. material_keyed_scan is generic over the row and the value, takes the key and value projections as functions, and returns the count and the last matching value from ONE pass -- so the duplicate-row refusal keeps the single authority the previous commit established while the cost shape stops being wrong. The generic-with-function-parameter form was PROBED before being relied on rather than assumed available: a throwaway two-record fixture confirmed a fn parameter and two type parameters resolve and execute, and it was deleted rather than committed. All six affected witnesses return true on this tree: the four AMS Lite rows including the enrolled duplicate-row control, and the two coupon witnesses that consume the rewired sibling lookup. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01TuCmz2HjuYzP6GWLgw11E4
…dentity beneath both users review 59815 found printer_01_lan_endpoint joined to printer_01 only through the label string "printer-01-lan", so the endpoint could drift from the asset it addresses with nothing failing, and the inventory row's claim that the individuating observation "lives with the endpoint row" was not represented anywhere the compiler could see it. The finding is correct. THE REPAIR IS A CLOSED SUBJECT, NOT A SECOND OPTIONAL. Adding asset: PhysicalAssetIdentity? beside host: HostIdentity? would have satisfied the finding while making two meaningless states writable -- both present, and both absent -- so a validator would then be needed to forbid what construction can simply decline to offer. NetworkEndpointSubject is EndpointHost | EndpointAsset | EndpointSubjectUnregistered, and the endpoint names exactly one subject. DESIGN section 4b prefers construction over the check. The third arm is not a null. The operator's LAN gateway is a real device at a real address for which this repository holds no inventory row, which is a different fact from an endpoint addressing nothing. Naming it Unregistered keeps the two distinguishable and leaves it fillable: the day the router gets an asset row, that arm changes and nothing else does. THE CYCLE WAS THE SIGNAL, NOT THE OBSTACLE. Binding the endpoint to printer_01 made gunbc.fleet_intent_network import gunbc.fleet_physical_inventory, which already imports it back -- and DESIGN section 3 states the import graph's one structural law is acyclicity. The right reading is that an identity is not a fact ABOUT an inventory row or ABOUT an endpoint; it is the name both of them index, so it belongs beneath both rather than inside either. All 13 PhysicalAssetIdentity constants move to gunbc.fleet_asset_identity, and the endpoint and the inventory row now refer to one declaration instead of agreeing by string. EVIDENCE. w_the_printer_endpoint_names_the_registered_printer_asset asserts the binding structurally and could not have been written against the old field at all -- there was nowhere on the record to put a PhysicalAssetIdentity, which is precisely what the review was reporting. w_the_lan_gateway_subject_is_unregistered_rather_than_a_null pins the third arm. The file's existing endpoint roster caught the addition on its own: membership is an identity join read in both directions, so adding the printer to the topology without adding it to the authored roster went red as foreign. A count oracle would have stayed green under that substitution, which is the defect that witness was written against. All 8 witnesses in the module return true on this tree. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01TuCmz2HjuYzP6GWLgw11E4
…relative path out of the string
Both findings from review 59854 are correct.
THE IMPORT WAS THE ODD ONE OUT. fleet_printer_access still reached printer_01 through
gunbc.fleet_physical_inventory while the two modules this PR edited had already been repointed at
gunbc.fleet_asset_identity -- the module that exists precisely so both cite one upstream
declaration. Routing an identity through a module that merely also imports it is the transitive
citation DESIGN section 3 refuses, and it would have been the first crack in a split made one commit
earlier to close exactly that.
THE TILDE WAS A SHELL CONVENTION SMUGGLED INTO A NonEmptyStr. path: "~/.printer-01-access-code"
requires a shell in the loop to mean anything: a process-launch API that does not run one opens a
literal directory named "~". That is a String leaf hiding named parts -- which base, and what under
it -- so CredentialFilePath splits UnderHomeOf { account, relative_path } from AbsolutePath, and a
consumer resolves the arm rather than pattern-matching a leading character. The review filed this as
advisory because the locus is not actuated in this PR; it is fixed here anyway, because the actuator
is the next cut and a string that needs re-parsing is cheaper to remove before it has a caller than
after.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TuCmz2HjuYzP6GWLgw11E4
…own merge consumed CI refused the witnesses lane with FAILED PHASE namespace-wave-admission (19 unadjudicated deltas). The floor itself was clean -- verdict=FloorClean unexpected_failures=0 -- so this is the namespace wall doing exactly its job on a change that moved declarations between modules. EVERY ONE OF THE 19 IS THE SAME MOVE. gunbc.fleet_asset_identity was extracted so a printer's network endpoint and its inventory row could cite ONE declaration of printer_01 instead of agreeing by label string; binding them otherwise made gunbc.fleet_intent_network and gunbc.fleet_physical_inventory import each other, which DESIGN section 3 forbids. So a spelling that resolved to fleet_physical_inventory now resolves to fleet_asset_identity: no name changed, no value changed, and no consumer reads a different constant. The declarations moved BENEATH their readers rather than away from them. The four test.claim.cooling_qualification_witness rows are the same move seen from a witness reading cooler_srv3 through that namespace. THE gunbc#10218 ROW IS DELETED RATHER THAN CARRIED. CI reported it consumed -- its relocation is already satisfied at the base, because that PR merged earlier today -- and this roster's own rule is that a consumed row's deletion is owed on the roster's next touch. This is that touch, so the obligation is paid here rather than deferred to whoever edits next. Carrying consumed rows forward is how this roster accumulated seventeen of them once before. cargo check -p v1-compiler --lib is clean. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01TuCmz2HjuYzP6GWLgw11E4
…nsumed
The roster conflicted because both sides appended to NAMESPACE_TRANSITION_ADMISSIONS. Resolved by
CHECKING consumption rather than by picking a side: main's rows all carry
RUNG_DROP_PER_ROW_SPLIT_LABEL and relocate rung_drop_roster to gunbc.rung_drop.roster, and
gunbc.design_ledgers on main ALREADY imports gunbc.rung_drop.roster { rung_drop_roster }. So that
relocation is satisfied at the base, the rows are consumed, and this roster's rule is that a
consumed row's deletion is owed on its next touch.
This is the failure mode that cost two full CI rounds earlier in this session: carrying rows forward
because they appeared in the incoming side, without asking whether the base already binds the
spelling to the target. The check is one git show against origin/main and it is the whole
resolution.
The 19 surviving rows are this PR's own asset-identity extraction, unchanged.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TuCmz2HjuYzP6GWLgw11E4
…d with the file it lived in Merging main again (#10344 landed 19 asset-identity rows). Two things worth recording. I GUESSED A RESOLUTION STEP AGAIN, ONE LAYER DOWN. Verifying whether main's 19 incoming rows are consumed, I asked whether the row's module imports the spelling DIRECTLY FROM the target. That reported four unbound -- the cooler_srv3 rows in test.claim.cooling_qualification_witness, which imports that spelling from gunbc.fleet_physical_inventory. But fleet_physical_inventory imports it from gunbc.fleet_asset_identity, where `data cooler_srv3` is declared, so the witness's binding resolves through the re-export to a declaration the target owns. A direct-import test answers a question about the IMPORT PATH; the wall's `target` asks about the OWNING MODULE, and the two differ exactly when a name is re-exported. Re-run as "is the spelling DECLARED in the target", 19/19 bind. This one fails toward KEEPING a stale row rather than deleting a live one, which is the quieter direction: a kept stale row refuses the next unrelated PR rather than this one. TAKING MAIN'S FILE WHOLE DROPPED MY OWN TRANSITION ENTRY. Resolving the conflict by checking out main's version left my 30 rows standing with no entry saying why they exist, what encloses them, or when they dissolve -- rows with no authority, which is worse than either side's version. Restored. Main's file also no longer carries ordinals 24 through 27; that gap is stated rather than reconstructed, since those entries narrated deletions whose rows are already gone. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VQ4iThiZ1B9LPB9ePr8qa9
…rrate the #10197 deletion main moved again while this branch was open: gunbc#10344 landed (61274d0) and did BOTH halves of the roster treadmill in one change -- it deleted the sixteen gunbc#10197 rows that its own roster touch had made due, and added nineteen of its own for the asset-identity extraction. TWO CONSEQUENCES, AND NEITHER IS A PLAIN MERGE. FIRST, #10344's NINETEEN ROWS ARE NOW CONSUMED AT THIS BRANCH'S BASE, so this change owes their deletion for the same reason it owed #10197's. gunbc.fleet_physical_inventory on main already imports chassis_srv1 and its twelve siblings from gunbc.fleet_asset_identity -- the admitted target -- and that file is not in this diff, so base equals head for it and admission_consumed_at_base holds on every row. consumed_due is roster_touched && !consumed.is_empty(), this change touches the roster, so leaving them refuses the run. Deleted. The treadmill is deliberate, not an accident of timing: each roster-touching change discharges the cohort the previous one left consumed. #10344 did exactly this to #10197, and the next roster-touching change will do it to the cohort below. SECOND, MY OWN TWENTY-SIXTH ENTRY HAD TO BE REWRITTEN RATHER THAN CARRIED. The previous head of this branch deleted the #10197 rows and narrated that deletion. main deleted the same sixteen independently. Carrying my entry would double-record one event -- the failure the TWENTY-THIRD and TWENTY-FOURTH entries in this same ledger already call out and refuse. My entry is dropped in favour of main's action, and the rewritten entry records the #10344 discharge with a sentence saying explicitly why #10197 is not narrated here. RESOLUTION SHAPE, unchanged from the previous merge and for the same reason: main's roster is taken WHOLESALE and the 255-row cohort is re-seated on it. Hand-merging the hunk would have preserved my side's copy of rows main had already retired, which is how a resolution manufactures stale admissions. The 255 identities are untouched and none were added to #10370. cargo check -p v1-compiler --lib is green on the remote runner after the merge. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01U397y4s3dSBof7vGPAX87G
…rdinal was wrong twice over Takes side chat's exact-head REQUEST_CHANGES review 5111687963. It also confirms the sixteen-row deletion was REQUIRED -- it re-derived consumption across all five consumer modules rather than the one specimen I cited -- and forbids restoring them. Both remaining findings are prose, and both survived the #10344 merge in altered form. ONE: THE ORDINAL. Side chat found my entry duplicating TWENTY-SIXTH. On the merged tree it is worse: #10344 trimmed the ledger, so the highest surviving ordinal is TWENTY-THIRD and my entry was SKIPPING twenty-four and twenty-five. A skipped ordinal is the same defect as a duplicated one -- the number exists to make an entry citable by its own name, and it does that only if it is exactly the next one. Now TWENTY-FOURTH. TWO: THE PROOF WAS NARROWER THAN ITS CONCLUSION, and the criticism transferred exactly across the rewrite. Side chat caught my #10197 paragraph citing the design_ledgers row and concluding all sixteen; the entry I wrote for #10344 reproduced the shape, citing fleet_physical_inventory importing chassis_srv1 and concluding all nineteen. Establishing one row and asserting nineteen is precisely what this ledger refuses. The actual partition, computed rather than assumed: 15 gunbc.fleet_physical_inventory -> gunbc.fleet_asset_identity 4 test.claim.cooling_qualification_witness -> gunbc.fleet_asset_identity The fifteen resolve directly. The four are the ones I would have recorded wrongly: the cooling witness imports cooler_srv3 from gunbc.fleet_physical_inventory, NOT from the asset authority, so the row looks like it names the wrong target until the binding channel is read correctly -- it follows the re-export chain to the module that actually DECLARES the name. Same admitted target, one hop further out. The entry now states that separately, because a reader checking the row against the witness's import list would otherwise conclude the row was mis-authored. Neither consumer file is in this diff, so base equals head for both and admission_consumed_at_base holds on all nineteen. The 255 identities are untouched and none were added to #10370. cargo check -p v1-compiler --lib is green on the remote runner. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01U397y4s3dSBof7vGPAX87G
…t_identity admissions Required run 33854290231 refused this branch on namespace-wave-admission -- 0 unadjudicated deltas, 0 stale admissions, and consumed rows due for deletion on a roster-touching change. This branch touched the roster to admit its own two rows and thereby inherited the deletion of nineteen it did not cause, which is the rule the TWENTIETH DISSOLUTION recorded as recurring. Fifth firing: 57, 3, 47, now 19. THE FLOOR RED WAS NEVER COST. The failing step is D0-ADJUDICATE, which consumes the measurement receipt rather than running claims, its standing is measurement_completed, and the job reports completed_over_cost_requirement=0. The aggregate said as much on its own -- mechanism=unestablished attribution=unestablished, with an explicit instruction to read the log before assuming a defect in the diff. An "unestablished" is a refusal to attribute, and it was read as an attribution already held. DECIDED PER ROW AGAINST THE CURRENT BASE, not from the reported count. All ten distinct spellings -- six chassis, two coolers, pdu_01, switch_01 -- are declared in fleet_asset_identity.dag on main and imported from there by fleet_physical_inventory, so all 19 deltas are unproducible. The reported count was 16 against a head predating this branch's merge of main; the merge then took main's roster wholesale and carried #10344's rows in. The consumed set is a property of the base one sits on, not the base one measured under. THE TWO SURVIVING ROWS ARE THE POSITIVE CONTROL, without which this is a sweep rather than a paid trigger: host_converge_for_identity is NOT in gunbc.host_converge on main and IS still in gunbc.fleet_converge_cli, so its delta is still producible and its rows stay. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015EdHQ244XXdBKRKGX6jrV4
…t never served (#10358) * FIT STEP 1: the selector stops advertising a runtime-neutral domain it never served Structural reclassification only, authorized by side-chat ruling as step 1 of the realization-axis cut. No behaviour changes and no vLLM path appears. WHY IT WAS A LIE BY NAME. gunbc.model.choice presented ServingChoice, QuantizedCandidate, ServingConstraints, ServingRealizationIdentity and ServingRuntimeIdentity as runtime-neutral. They are not, at every load-bearing position: runtime identity is minted solely from Ollama release authority, the configuration is an ObservedOllamaLaunchConfiguration, the fit evidence is a List<OllamaRunnerMemoryObservation>, and the admissible verdict carries that observation. There is no vLLM reference anywhere in the module. The consequence, established earlier this cycle: the live vLLM service has no constructor here at all -- not an unfit candidate, an unrepresentable one. gunbc.model.choice -> gunbc.model.ollama_choice choose_serving_candidate -> choose_ollama_serving_candidate ServingChoice -> OllamaServingChoice QuantizedCandidate -> OllamaQuantizedCandidate ServingConstraints -> OllamaServingConstraints ServingRealizationIdentity-> OllamaServingRealizationIdentity ServingRuntimeIdentity -> OllamaServingRuntimeIdentity CandidateVerdict -> OllamaCandidateVerdict evaluate_candidate -> qualify_ollama_candidate THE ENTRY POINT STAYS A CHOOSER, and that is the correction I needed. I was going to call the module a QUALIFIER. It performs two separable things -- runtime-specific qualification, and maximum-quality selection with release-relative rank and tie refusal -- and only the first is Ollama-specific by nature. Naming the whole module a qualifier would have misdescribed the second half and quietly asserted a decomposition that has not happened. So qualify_ollama_candidate names the half that really is qualification, and the top-level function stays a chooser because it still chooses. WHAT IS DELIBERATELY ABSENT. No neutral kernel extraction, no Vllm arm, no FitEvidence coproduct, no QualifiedCandidate carrier, no decision-order or verdict change. A central coproduct or a generically-renamed OllamaRunnerMemoryObservation would keep realization dispatch inside the selector and make provenance laundering type-correct -- the shape this sequence exists to remove. The first common type belongs AFTER qualification. NO COMPATIBILITY RE-EXPORT. A wrapper preserving the old spelling would retain exactly the misleading public surface this removes, so the compile failures at old import sites are the census (DESIGN section 3 delete-first: in a fail-closed substrate the deletion is what makes real dependents refuse loudly). Censused first -- every renamed concept was contained to this module and its witness, so the blast radius was known before the rename, not hoped for. The annotation on the entry point states that the neutral kernel has NOT been extracted, so this cut cannot later be cited as completing step 2. EVIDENCE: all 42 witnesses in the choice entry pass, changed only in module and symbol references. Every fixture value, decision ordering, rejection axis, unanswerable outcome, winner and tie result is identical -- which is what distinguishes a rename from an edit. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01U397y4s3dSBof7vGPAX87G * Cut the four prose citations over too: a rename is not done while a dead name is cited Review 59813. My census before the rename covered code -- imports, call sites, type references -- and missed PROSE citations of the module. Four sites still named the dead authority: dag/std/measure.dag dag/gunbc/model/population.dag dag/gunbc/spark/serving_convergence_withholding.dag docs/plans/spark-fleet-hand-edit-gap-analysis.md These are exactly what DESIGN section 3's standing rule protects. It says to cite the SYMBOL rather than the position, on the grounds that a stale name is decidable and enforceable while a stale line is not reachable from the namespace tree at all. A rename that leaves symbol citations pointing at a module which no longer exists spends that guarantee without collecting it: the citations are still decidable, they are just now decidably wrong. Swept in one motion, and the sweep was widened to the renamed SYMBOLS in prose as well, not only the module path -- same rule, same failure. Nothing under the old spellings survives anywhere in the tree. ONE OF THEM IS NOT A MECHANICAL RELABEL. serving_convergence_withholding carries a paragraph whose whole job is to stop that gate being mistaken for the fit gate: "gunbc.model.choice decides whether a candidate fits a host's memory. This decides whether a host may receive serving convergence AT ALL." The rename SHARPENS that distinction rather than merely relabelling it, so the paragraph now says gunbc.model.ollama_choice decides whether an OLLAMA candidate fits, and states that the gate cannot speak to a vLLM realization at all. The new name makes that paragraph's point more forcefully than the old one could. The docs/plans file is hand-authored rather than a generated projection, so it is edited directly rather than through the artifact gate. All four edits are annotation or markdown only -- annotations are erased before every semantic pass, so no behaviour can change. Executed rather than asserted: 42 of 42 witnesses pass, no failures. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01U397y4s3dSBof7vGPAX87G * Close the two sections the rename attached a live authority name to a dead account of review 59866 is correct and the defect is mine. The rename swept docs/plans/spark-fleet-hand-edit-gap-analysis.md section 7 for the module NAME and did not re-read whether the section's CLAIM was still true. It was not. Section 7 said gunbc.model.ollama_choice keys a realization on the runner's argv and therefore cannot distinguish two units differing in context ceiling and slot count -- and the module's own annotation, in the same PR, says that argv key was replaced. Attaching the current authority's name to an obsolete account of it is worse than the stale name it replaced: a stale name is visibly dead, while a live name over a dead claim reads as current. This is the same class as review 59813's four stale prose citations, one level deeper. That one was a name census; this one is a CLAIM census, and a rename does not discharge it. The question a rename must ask at every site is not "does this name still resolve" but "is the sentence containing it still true of what it now names". Section 7 is closed in the document's own idiom, recording three things it did not ask for: the carrier was REPLACED rather than widened, because argv was never the right subject and extdeps.ollama.server_env already holds the variables as typed axes; the partition is owned by the module rather than supplied by the caller, where the predecessor's varied_flags argument let a caller erase a causal axis from the fixed mode and thereby authorize evidence transport across it; and identity runs typed values first and wire second, so the module never parses an observed string. Ordered item 4 is closed with it. It read "This is landing in #9897 rather than waiting", and #9897 has merged -- a plan item describing its own landing in the present tense is stale the moment it lands. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01U397y4s3dSBof7vGPAX87G * A rename is a namespace wave: admit the 255 relocated bindings by exact identity CI caught what four dashboard reviews, one exact-head review and I all missed. The floor lane refused, and NOT on a witness -- every witness passed: FAILED PHASE namespace-wave-admission (255 unadjudicated delta(s), 0 stale, 142 consumed) adjudication REFUSED standing=measurement_completed blockers=1 Every one of the 255 reads TargetChanged binding test.claim.model.serving_choice_witness_test::<decl> `<spelling>` base {gunbc.model.choice} -> head {gunbc.model.ollama_choice} which is the wall working exactly as gunbc.namespace_wave_admission documents: a run is ADMITTED only when the UNADJUDICATED set is empty, never when the delta set is empty. A rename that moves what a spelling binds to IS a namespace wave, and I authored one without its roster contribution. WHY EVERY ROW IS ENUMERATED RATHER THAN PATTERNED. The module carrying this roster says a pattern would admit a genuine rebind that happened to land in the same two modules, and a rename wave is precisely the change that must not be able to hide one. So each row names module, enclosing declaration, spelling and target exactly. Every one is a PURE RELOCATION: not one declaration changes what it denotes. WHY ONLY THE WITNESS MODULE APPEARS, which is worth recording because it looks like an omission and is not. The wall's binding channel is authored NAME OCCURRENCES resolved per module, and test.claim.model.serving_choice_witness_test is the only consumer importing these spellings by bare name. The other three touched files -- gunbc.model.population, gunbc.spark.serving_convergence_withholding and std.measure -- carry the old module name only in PROSE. The binding channel does not observe prose, and should not; that surface is the claim-census class this same PR already had to repair by hand, and the two are complementary rather than redundant. DISSOLUTION IS MANDATORY AND DATED. These rows go when #10358 merges: base and head will both carry the rename, no run can produce the deltas, and all 255 report STALE -- which refuses every unrelated PR. The shrink is the fix, not housekeeping. cargo check -p v1-compiler --lib is green on the remote runner. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01U397y4s3dSBof7vGPAX87G * Consumed is not stale, and a roster touch owes the deletions it makes due Takes side chat's exact-head REQUEST_CHANGES review 5111479181 on #10358. Two findings, and only the first is prose. ONE: THE LIFECYCLE CLAIM WAS WRONG IN BOTH HALVES. My trigger paragraph said the 255 rows would report STALE once the rename lands and would then refuse EVERY unrelated PR. Neither is what the mechanism implements. A STALE row matches no delta in the run. A CONSUMED row is one the BASE has already satisfied -- admission_consumed_at_base holds when the base binds the exact (module, declaration, spelling) to a singleton set containing the admitted target. After #10358 lands, every one of these spellings binds to gunbc.model.ollama_choice at the base, so they are consumed. Different states, different reporters. Consumed rows are NOT globally blocking. The predicate is consumed_due = roster_touched && !consumed_admissions.is_empty(), so a consumed row comes due on the roster file's OWN next touch and on no other change. An enrolled unit test names exactly that behaviour. The wrong version overstated this cohort's blast radius, which is the same defect class as the argv paragraph this PR already had to repair: a confident sentence about a mechanism, written without reading the mechanism. TWO, AND IT IS NOT PROSE: THIS CHANGE OWED SIXTEEN DELETIONS AND HAD NOT MADE THEM. Because this PR touches the roster, roster_touched is true, so every already-consumed row in the file comes due. main carries sixteen gunbc#10197 rung-drop per-row split rows; #10197 is on main at 0c031e6, and gunbc.design_ledgers there already imports rung_drop_roster from gunbc.rung_drop.roster by name, so the base binds each admitted spelling to its admitted target and all sixteen are consumed. Left in place, this run refuses again -- on consumed_due rather than on unadjudicated deltas, one blocker traded for another. They are deleted with their label, and a twenty-sixth dissolution entry records why THIS change owed it: carrying consumed rows forward while editing the same file is precisely what the predicate exists to prevent. Why the earlier runs did not show this: neither 76ba0e2 nor #10370's head touches namespace_wave_admission.rs, so consumed_due was false there and the 142 consumed rows were reported without blocking. Touching the roster is what makes them due. The 255 identities are unchanged, and none were added to #10370. cargo check -p v1-compiler --lib is green on the remote runner. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01U397y4s3dSBof7vGPAX87G * A proof that establishes one row may not conclude nineteen, and the ordinal was wrong twice over Takes side chat's exact-head REQUEST_CHANGES review 5111687963. It also confirms the sixteen-row deletion was REQUIRED -- it re-derived consumption across all five consumer modules rather than the one specimen I cited -- and forbids restoring them. Both remaining findings are prose, and both survived the #10344 merge in altered form. ONE: THE ORDINAL. Side chat found my entry duplicating TWENTY-SIXTH. On the merged tree it is worse: #10344 trimmed the ledger, so the highest surviving ordinal is TWENTY-THIRD and my entry was SKIPPING twenty-four and twenty-five. A skipped ordinal is the same defect as a duplicated one -- the number exists to make an entry citable by its own name, and it does that only if it is exactly the next one. Now TWENTY-FOURTH. TWO: THE PROOF WAS NARROWER THAN ITS CONCLUSION, and the criticism transferred exactly across the rewrite. Side chat caught my #10197 paragraph citing the design_ledgers row and concluding all sixteen; the entry I wrote for #10344 reproduced the shape, citing fleet_physical_inventory importing chassis_srv1 and concluding all nineteen. Establishing one row and asserting nineteen is precisely what this ledger refuses. The actual partition, computed rather than assumed: 15 gunbc.fleet_physical_inventory -> gunbc.fleet_asset_identity 4 test.claim.cooling_qualification_witness -> gunbc.fleet_asset_identity The fifteen resolve directly. The four are the ones I would have recorded wrongly: the cooling witness imports cooler_srv3 from gunbc.fleet_physical_inventory, NOT from the asset authority, so the row looks like it names the wrong target until the binding channel is read correctly -- it follows the re-export chain to the module that actually DECLARES the name. Same admitted target, one hop further out. The entry now states that separately, because a reader checking the row against the witness's import list would otherwise conclude the row was mis-authored. Neither consumer file is in this diff, so base equals head for both and admission_consumed_at_base holds on all nineteen. The 255 identities are untouched and none were added to #10370. cargo check -p v1-compiler --lib is green on the remote runner. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01U397y4s3dSBof7vGPAX87G * 13, 10 and 15 count three different things The grouped 15/4 partition is correct -- verified against the base roster, which holds exactly 19 rows: 15 in gunbc.fleet_physical_inventory and 4 in test.claim.cooling_qualification_witness, both targeting gunbc.fleet_asset_identity. The prose around it was not. It said the fifteen were `chassis_srv1` "and its twelve siblings". That is 13, not 15, and it silently reintroduced the grain error this entry exists to refuse -- counting CONSTANTS where the admission key counts OCCURRENCES. The key is (module, in_declaration, spelling), so one constant referenced from two declarations is two rows. Measured: the fifteen are 10 distinct spellings across 8 declarations. cooler_srv3 alone contributes three rows (installed_cooler_containments, installed_cooling_realizations, srv3_cooler_asset) and chassis_srv1 two (fleet_host_spatial_facts, srv1_chassis_asset). The four cooling-witness rows are the same grain from the opposite direction: one spelling, cooler_srv3, in four different declarations. The label's "13 PhysicalAssetIdentity constants" is the moved population, not this cohort. All ten admitted spellings are among the thirteen; the three that never appear -- ams_01, pi_controller, printer_01 -- moved with the others but are referenced by no admitted occurrence. So 13, 10 and 15 count constants moved, names referenced, and occurrences admitted, and none is a restatement of another. No admission row changes; the 255-row Ollama cohort is untouched. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01U397y4s3dSBof7vGPAX87G --------- Co-authored-by: gunbc-ci-auto-heal <gunbc-ci-auto-heal@users.noreply.github.com> Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
…up's Optional, and delete the bare ruleset PUT (#10324) * Generalize fleet converge into world convergence * Name the ruleset actuation hold honestly * Declare the world-convergence witness fixture types so the witness executes FixtureDifference and FixtureReceipt were single-name `type X = Y` forms, which this grammar reads as ALIASES, so both aliased a type that was never declared: the module never resolved and the hub's only claim had never run. Its green was the absence of a run, not a passing one. Declares FixtureDrift and FixtureApplied as records used directly at each position -- the aliases are gone rather than repaired, since a second name for one fixture type is the nickname §3 forbids. Adds the positive control the ladder requires beside the refusal claim: without it, a root that refuses every subject satisfies the RED. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015EdHQ244XXdBKRKGX6jrV4 * Delete apply_desired_ruleset: the bare whole-ruleset PUT is gone, not guarded World convergence took the actuation root, leaving the unguarded github.Rulesets.Update with zero callers. Zero callers make the deletion the census: nothing refuses, so nothing was load-bearing. Keeping it would leave a live destructive write reachable by the next author who greps for "how do we apply a ruleset" and never passes repo_ruleset_actuation_admission -- the §3 attractor. Actuation returns through the admission or not at all. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015EdHQ244XXdBKRKGX6jrV4 * Restore the Optional at the host lookup, and declare the difference type the handler names Two defects at the fleet_converge_apply match site, both reported far from their cause. The generic find_by_identity returns T?, and the Optional does not survive inference: matched directly, its result reads as bare HostConverge, so Present is reported as a missing variant of HostConverge at that type's declaration -- an innocent line in another file. Two callers already worked around it with a private monomorphic wrapper each; this promotes ONE wrapper beside the type, names the compiler deficit and its dissolution trigger there, and routes all three call sites through it. `type HostConvergenceDifference = HostConvergenceRequired` is an alias, not a one-variant coproduct, so the handler's difference parameter named a type that was never declared -- the same defect as the hub witness fixtures. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015EdHQ244XXdBKRKGX6jrV4 * Lift the two body-position annotations to their declaration §4c admits only standalone leading // blocks attached to module-scope declarations; a comment inside a match arm is not capturable and refuses. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015EdHQ244XXdBKRKGX6jrV4 * Adjudicate the namespace wave the Optional repair moves, and pay the roster's consumed-row debt The required floor refused with three unadjudicated namespace deltas, and the first of them was a real defect, not paperwork: fleet_converge_apply still CALLS find_by_identity in fleet_compute_host_for_identity, and the import-list edit dropped it, so that binding went from {gunbc.host_converge} to {} — NewUnresolvedness. The witnesses stayed green over it because the bare name still resolved through the pool; the wall is what saw it. The import is restored. The other two are the move itself: host_converge_for_identity's two callers in fleet_converge_cli now resolve to gunbc.host_converge. That is TargetChanged and is not auto-admitted, so it gets two exact admission rows naming module, declaration, spelling and target — enumerated by identity, never a wildcard over "anything that moved". Touching the roster makes its consumed rows due. The required run reported six — two from gunbc#10206 and four from gunbc#10028 — as satisfied at this base, and this module's own convention charges their deletion to its next toucher. They are deleted here rather than left for a later change to trip over. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015EdHQ244XXdBKRKGX6jrV4 * Split the held-actuation cause: an unsigned desire is not an unmodeled live rule Review finding from witty-otter-195 on #10324, and it is correct. merge_queue_projection_refusal returns one Present for two different facts — a live rule this projection cannot express, and a signed-off merge queue the desired projection does not carry — and the admission mapped every Present to UnmodeledLiveRuleActuationHeld. Only the first path is reachable today because desire is unsigned, so the collapse would become a wrong diagnostic on the first sign-off, which is exactly when nobody is re-reading this arm. It also left RepositoryRulesetUnsignedDesireActuationHeld as a declared variant that nothing on the observed path constructs. The admission now reads the two halves separately and names the cause that actually holds. The combined helper keeps its located refusal prose and its two enrolled claims, with its standing — no production caller — recorded on the declaration rather than left for a reader to mistake for a live gate. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015EdHQ244XXdBKRKGX6jrV4 * Delete required_context_reconcile: the membership plan this module no longer computes The reconcile step's only consumer was the whole-ruleset actuate root world convergence replaced, so it was left with exactly ONE occurrence in the tree — its own declaration. No caller, no test, no import. It is an orphan this PR created, not one it inherited, and delete-first says the deletion is the census. It is disposed of by deletion rather than kept with a standing note, and the distinction from merge_queue_projection_refusal is the reason: that helper is retained because deleting it would destroy authored located refusal prose and two claims that execute over it. This one has neither. Nothing is lost by the cut, and silence plus one declaration is how a reader concludes a reconcile step runs. The cut is the census: it takes its four private helpers, its member-at type, the gunbc.membership_reconcile and gunbc.ownership imports that only it used, and the carrier note describing the Owned-versus-Ensured choice it no longer makes. desired_required_status_checks survives — the desired projection still reads it. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015EdHQ244XXdBKRKGX6jrV4 * Re-enroll the transport RED the cut retired, and stop the admitted arm asserting the negation of its own precondition Two review findings on #10324, both verified against the tree before acting. THE DROPPED WITNESS IS REAL. witness_emit_artifact_transport_rejects_shell_command exists on main and not on this branch; its replacement covers unknown identity, a different class, so the bash-emit-versus-local-shell refusal lost its enrolled RED. It could not survive verbatim — it called converge_apply, which world convergence replaced — but §4b(4) retires the obsolete PRODUCTION machinery and never the class's evidence. It is re-enrolled against the new root with the same subject: an EmitArtifactThenThinRun transport must not silently converge. Identity, policy and handler match the sibling claims, so the transport is the only varied input and this green cannot be borrowed from another arm. THE ADMITTED ARM ASSERTED THE NEGATION OF WHAT ADMISSION ESTABLISHED. Admission is reached only when the bypass roster projects, no live rule is unexpected, and the desire IS signed — so refusing an admitted subject with UnsignedDesireActuationHeld says the opposite of the fact just decided. The arm is unreachable today, so it misdiagnoses nothing in production; a diagnostic that only becomes wrong once a writer lands is wrong at the one moment anyone reads it. The cause is now ActuationUnbound, which is the state that actually holds: no admitted write realization exists on this tree. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015EdHQ244XXdBKRKGX6jrV4 * The held-actuation causes were INVERTED: say what is true of the values that construct them Review 5109503332, verified against the tree. Two production causes asserted the negation of what their own inputs establish, and one of them is the live path. IGNORANCE PROMOTED TO AN OBSERVATION. bypass_roster_projection_refusal answers Present for an UNREADABLE roster and for an OBSERVED non-empty one alike, and the admission folded both into one held cause rendering "live bypass actor cannot be projected". Under the credential this repository has, a live ruleset GET omits bypass_actors on every read — GitHub omits that field both when the roster is empty and when the reader may not see it — so on the path we actually execute, converge reported THAT A LIVE ACTOR EXISTS when the truth is that this reader CANNOT SEE whether any do. That is top-as-answer standing in for top-as-ignorance, asserted rather than merely widened. SIGNED-BUT-UNPROJECTABLE REPORTED AS UNSIGNED. signed_desire_projection_refusal is Present only when a policy IS signed and the projected rule drifts from it, and that was mapped to a cause rendering "desired ruleset is not signed" — which sends an operator to sign something already signed. The admission now reads the observed values directly and preserves five distinct standings, each carrying enough payload to render the actual fact: BypassRosterUnobservable (with the reader, because the refusal is a property of the credential and not the moment), ObservedLiveBypassActors (with the actors), UnexpectedLiveRule (with the divergences), SignedPolicyProjectionUnrepresentable (saying signing again fixes nothing), and the terminal ActuationRealizationUnbound. verify's exit arms render each. THE NEW CLAIMS CROSS THE REPLACEMENT JOIN, which the retained ones do not: they call repo_ruleset_actuation_admission itself, not the helpers whose text no production path emits any more. The unobservable-versus-observed pair is the discriminating RED for the inversion — the same fixture with only the roster varied — and the empty-roster terminal cause is the positive control without which an admission that refuses everything identically would satisfy them. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015EdHQ244XXdBKRKGX6jrV4 * The standing note's own trigger had fired and retired nothing: name the third outcome The note on merge_queue_projection_refusal read "dissolve-on: the admission constructing its own located causes, at which point this prose has a consumer again or has nothing left to say". The admission now constructs them, and NEITHER arm happened: the helper did not get a consumer back — the admission deliberately does not route through it, since folding two facts into one Present is what inverted the causes — and it does not have nothing left to say, because it answers the coarser may-converge-actuate-at-all question in prose no cause carries, that applying desired would DELETE what the projection omits. The fired trigger is recorded rather than quietly swapped, because a trigger nobody re-reads after the event that fires it is the failure this repository keeps paying for: usually as a trigger naming less than the capability it governs, satisfied while that capability stays dead, and here as its mirror. The replacement names a capability and not an event: an executing claim asserting the DELETE-hazard prose through the admission's own located causes. Nothing smaller retires it — in particular the admission merely HAVING located causes does not, which is the mistake the fired trigger made. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015EdHQ244XXdBKRKGX6jrV4 * A variant that erased its own distinction: delete WorldAbsent rather than explain it WorldAbsent carried the same Observed payload as WorldObserved and nothing else, and its arm in world_converge was byte-identical to WorldObserved's. The carrier note said the collapse was deliberate because absence "proceeds through the same subject-owned difference operation" -- but the difference operation receives only the payload, so it could not tell the two apart. The distinction was not delegated to the subject, it was DESTROYED before the subject could see it, while a note stood there reading as coverage. Latent, not live: zero producers corpus-wide, so no path reached the arm. But world_converge.dag does not exist on origin/main -- this PR INTRODUCES the file and the variant, so merging is the clock and the residual is this PR's to close, not a follow-up's. Of the two shapes, deletion over a discriminating payload: a discriminator cannot be got wrong if the variant does not exist, and re-adding it when a real producer appears is a smaller diff than a note explaining why the payload is identical. This is a climb to structurally impossible -- the misleading state now has no constructor -- so it dissolves the note rather than repairing it. The fold's existing discriminating RED and positive control stay enrolled unchanged. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015EdHQ244XXdBKRKGX6jrV4 * Two of five causes had no executing discriminator, and the unreachable one was the inverted one The replacement-join battery varied the bypass roster, which discriminates the roster arms and leaves the other two untouched: the fixture pins rule_types to the declared list, and the signed-policy arm is decided by signed_desire_projection_refusal, which is NULLARY and folds two module-scope globals. While this repository's merge queue is unsigned that arm is unreachable through ANY observation, so no fixture could reach it. Counting claims that cross the join answered a cardinality question; this answers the coverage one. AN UNREACHABLE ARM IS EXACTLY THE ARM THAT NEEDS ONE. This branch was WRONG while it was unreachable -- it reported a signed-but-unprojectable policy as "not signed", sending an operator to sign what was already signed -- and unreachability is what let the inversion sit. "The source reads correctly today" is not a wall against the same inversion returning at the first sign-off, which is the moment the arm goes live and the moment it is first read. repo_ruleset_observed_actuation_admission is the ordered cause mapping, split out so every arm is reachable by an executing claim. It is NOT a fixture double: repo_ruleset_actuation_admission holds no copy, decides only readable-versus-unreadable, and delegates every observed case here, so production and the claims execute one function. Only the signed refusal arrives as a parameter -- production passes signed_desire_projection_refusal() and no fact about which policy is signed leaves the module. The two new claims prove the MAPPING, not the predicate: the fixture supplies the Present, so they do not establish that production constructs it correctly. That is the right cut rather than a gap, because the defect WAS in the mapping, but it is stated here and in the PR body so "the signed-policy arm is covered" is not later read as covering the predicate too. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015EdHQ244XXdBKRKGX6jrV4 * The transport witness did not discriminate transport, and its own note said it did The re-enrolled claim accepted any refusal constructor through a wildcard, while its note read "the transport is the only varied input and a green here cannot be borrowed from another arm". That sentence was false when it was written: the sibling knob-frontier claim uses this same fixture under LocalShell and already refuses, so substituting LocalShell left the witness green. A claim named for a boundary, passing for a reason unrelated to that boundary, is worse than no claim -- it is cited as coverage for a wall it never touched. That is the same shape as the WorldAbsent note deleted earlier in this branch: prose asserting a discrimination the code does not make. DESIGN 4c says an annotation is never evidence that a machine claim holds, and the structural version of that rule is to put the control INSIDE the claim rather than beside it. Both halves are now asserted together: the emit transport must refuse with the reason naming the fresh-standup bootstrap boundary, AND the identical fixture under LocalShell must NOT satisfy that same assertion. A realization that ignored transport and answered both alike fails the second half, so the claim cannot be satisfied without the distinction existing. Routing EmitArtifactThenThinRun through the in-process arm turns the first half red and leaves the LocalShell half untouched. operator_host_srv3 maps to FreshStandup, so the transport-specific arm genuinely fires rather than falling through to realize_converge_in_process. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015EdHQ244XXdBKRKGX6jrV4 * verify's note named a variant that does not exist The header said drift "reaches RepositoryRulesetActuationHeld". That variant has no declaration anywhere in the corpus -- the name was introduced by this branch's own cause rewrite and never corresponded to an arm of RepositoryRulesetConvergenceRefusal. This is the third instance in this PR of the same defect: prose asserting something the code does not carry. The other two were a note claiming a discrimination the signature could not provide and a note claiming a variation the assertion did not make; this one names a symbol that was never declared at all. DESIGN 3 makes exactly this decidable -- a name is reachable from the containment tree, so a stale one is greppable and enforceable where a stale line number is not -- and it went unnoticed because nothing joins a comment to the type it describes. Named to the real shape rather than to a single arm, because drift does not reach one cause: it reaches whichever of the five located causes holds, and only terminates at ActuationRealizationUnbound when nothing more specific does. Substituting the nearest live variant would have restated the same over-narrow claim with a name that happens to compile. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015EdHQ244XXdBKRKGX6jrV4 * The apply note argued about a cause inversion in the vocabulary of the inversion world_repo_ruleset_apply's annotation named two variants with zero declarations anywhere in the tree. Both were deleted by this branch's own cause rewrite, so the note describing the fix was written in the vocabulary of the world it replaced. UnsignedDesire no successor -- no live cause carries an unsigned standing ActuationUnbound RepositoryRulesetActuationRealizationUnbound THE TWO ARE NOT THE SAME EDIT. ActuationUnbound is a clean rename: the sentence stays true with the live name in it. UnsignedDesire has NO successor, so renaming it is unavailable, and substituting the nearest live cause would be worse than leaving it -- SignedPolicyProjectionUnrepresentable means "signed but unrepresentable", which is not the negation admission establishes, so the illustration would be false in a way that parses. The sentence is reworded to state the principle over the admission's actual preconditions, and says what is now true instead: no such cause remains CONSTRUCTIBLE, because the inversion was fixed in the type rather than in the arm. That is a stronger claim than the original made. Also expands ActuationRealizationUnbound at the admission's own note to the declared name. DESIGN 3 says cite the symbol; an abbreviation is not the symbol, and prose dropping a common prefix is exactly what a substring matcher cannot distinguish from a live reference. Swept the whole file rather than the two names reported: a token matcher over every comment against every non-comment line tree-wide now returns no unresolvable name in this file. Two dead names remain in other files this PR touches -- ConvergeSparkPreEnrollment in fleet_converge_cli and PerSlotUnitProperty in host_converge -- and both are left alone deliberately: they pre-date this branch on origin/main and this diff does not touch their lines, so they are a pre-existing corpus defect and not this PR's residual. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015EdHQ244XXdBKRKGX6jrV4 * Pay the roster-touching obligation: delete the 19 consumed fleet_asset_identity admissions Required run 33854290231 refused this branch on namespace-wave-admission -- 0 unadjudicated deltas, 0 stale admissions, and consumed rows due for deletion on a roster-touching change. This branch touched the roster to admit its own two rows and thereby inherited the deletion of nineteen it did not cause, which is the rule the TWENTIETH DISSOLUTION recorded as recurring. Fifth firing: 57, 3, 47, now 19. THE FLOOR RED WAS NEVER COST. The failing step is D0-ADJUDICATE, which consumes the measurement receipt rather than running claims, its standing is measurement_completed, and the job reports completed_over_cost_requirement=0. The aggregate said as much on its own -- mechanism=unestablished attribution=unestablished, with an explicit instruction to read the log before assuming a defect in the diff. An "unestablished" is a refusal to attribute, and it was read as an attribution already held. DECIDED PER ROW AGAINST THE CURRENT BASE, not from the reported count. All ten distinct spellings -- six chassis, two coolers, pdu_01, switch_01 -- are declared in fleet_asset_identity.dag on main and imported from there by fleet_physical_inventory, so all 19 deltas are unproducible. The reported count was 16 against a head predating this branch's merge of main; the merge then took main's roster wholesale and carried #10344's rows in. The consumed set is a property of the base one sits on, not the base one measured under. THE TWO SURVIVING ROWS ARE THE POSITIVE CONTROL, without which this is a sweep rather than a paid trigger: host_converge_for_identity is NOT in gunbc.host_converge on main and IS still in gunbc.fleet_converge_cli, so its delta is still producible and its rows stay. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015EdHQ244XXdBKRKGX6jrV4 * A dissolution trigger at the wrong grain retires the row while the deficit still bites `host_converge_for_identity`'s trigger read "Dissolves when generic Optional returns infer" -- five words that name a capability but never say what it must be SUFFICIENT FOR. DESIGN 4b(3) makes that the review tell: a corpus-wide loss under a trigger stated at no particular grain is satisfiable by something far smaller than the capability it claims to restore, so the row would retire while three call sites still need the wrapper. 4b(2) calls the same thing an untracked stall. This restates it at the grain of the loss: inference carrying the Optional through a generic return position, sufficient for every consumer to match on such a call with no monomorphic wrapper in front of it -- and names what does NOT retire it, since the failure mode is a near-miss satisfying the sentence while the deficit stands. Prose only; no behaviour, no declaration, and no change to the annotation's shape -- it was a leading block attached to a module-scope declaration before and after. The wording follows the trigger already carried by `merge_queue_projection_refusal` in `gunbc.repo_ruleset`, which was written to this grain in this same change. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015EdHQ244XXdBKRKGX6jrV4 * Delete the host world-convergence binding: its differences() asserted what its type says it computes Review 60232, upheld. `host_convergence_differences` ignored all three parameters and returned `WorldDiverged` unconditionally, so `WorldInAgreement` was unreachable for hosts, an already-converged host was unrepresentable, actuation was always entered, and `unchanged_host_convergence_receipt` was dead by construction. §5's fabricated plausible output, at the function whose whole job is deciding. NO BETTER HANDLER WAS AVAILABLE, which is why this is a deletion and not a repair. `WorldDifference` is exactly `WorldInAgreement | WorldDiverged` with no cannot-decide arm, and the host realization FUSES OBSERVATION INTO ACTUATION -- convergence state is discovered by converge_apply_for_host in the act of applying. There is no honest differences() to write for this subject, so the handler had to lie in one direction or the other. Trigger, as a capability: a host observation that reads convergence state WITHOUT actuating. Recorded in the PR body, not in docs/design-failure-modes.md, which is under an active landing lease. THE WITNESSES ARE RESTORED, NOT DELETED. Five of the six claims in fleet_converge_apply_witness_test.dag PRE-EXIST this change; this branch had migrated them onto the new binding, so reverting the migration restores their original subject rather than dropping coverage -- §4b(4) retires obsolete production machinery, never the class's evidence. Only the sixth, which existed solely to exercise the deleted binding, goes with it. fleet_converge_apply.dag is byte-identical to its base, which also restores converge_apply / FleetConvergeApplyResult: this branch had deleted the old path in favour of the binding, and with the binding gone the frozen path is what the witnesses call. Zero dangling references corpus-wide to host_world_convergence_handler, HostConvergenceSubject, HostConvergenceRefusal or observe_host_convergence. WHAT IS UNAFFECTED: the repo_ruleset binding, which is honest and is the sole production consumer of world_converge -- observe_repo_ruleset reads live state and observation_divergences computes a real divergence, so its InAgreement arm is reachable. The host_converge_for_identity promotion also stands on its own: three consumers remain, and the Optional-through-inference defect is a property of the lookup rather than of the binding that was removed. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015EdHQ244XXdBKRKGX6jrV4 * Pay the consumed-row obligation: gunbc#10355's 30 rows are satisfied at the base The namespace-wave-admission phase refused run 33896895062 with `30 consumed admission(s) due for deletion on this roster-touching change`. Deletion of consumed rows is charged to whoever next touches the roster, and that is this change. IDENTITY JOIN, NOT A COUNT. The run emitted exactly 30 `CONSUMED ADMISSION` lines, every one naming gunbc#10355 and its own binding -- `gunbc.scm.authoring:: author_requirement `Proposal` -> gunbc.scm.proposal ... already satisfied at the base`. The roster carried exactly 30 rows labelled SCM_PROPOSAL_VOCABULARY_LABEL. Thirty reported, thirty labelled, thirty deleted; the two gunbc#10324 rows are the whole remainder, and their own trigger is this PR merging, so they are not yet consumed. The label constant goes with its rows, per the TWENTY-NINTH's precedent for gunbc#10358's cohort. THE DELETION PASS ASSERTED `examined == kept + deleted` AND THAT ASSERTION EARNED ITS KEEP TWICE. The first parse anchored on `&[` and matched the TYPE ANNOTATION `&[TransitionAdmission]` rather than the array literal, so it examined a 19-character body and found zero rows -- which without the assertion would have been a silent no-op reported as success. The second failed a census check because grep counts `pub struct TransitionAdmission {` alongside the rows: the roster held 32, not the 33 an earlier commit message on this branch stated, and main contributed 30 rather than 31. Both are recorded in the THIRTIETH DISSOLUTION entry so the off-by-one is not inherited by whoever counts next. This pays the ONE blocker of the three on this head that is mine. The other two -- undeclared regen drift on v1_compiler_infer.rs, and a stale-quarantine row that #10402 greened without unenrolling -- are main's, present on main's own push run, and fixed by #10450. This commit is deliberately not pushed on its own: the head would still fail on those two, and cancel-in-progress is false, so it would cost two runs rather than one. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015EdHQ244XXdBKRKGX6jrV4 * A refusal that dropped the drift it was handed: name what diverged, not just the missing writer Review 60298, upheld. `world_repo_ruleset_apply` took `_: List<RulesetDivergence>` and discarded it, so a name, enforcement, target or required-context drift reached `verify()` as `RepositoryRulesetActuationRealizationUnbound` -- a true sentence about the tree that says nothing about what drifted. The failure STATUS survived and the LOCATED CAUSE did not. §5 requires typed AND located; §3 requires the replacement to preserve every refusal the thing it replaced produced, and the base verifier ended `NotConverged { reason: render_divergences(ds: divergences) }`. It bit hardest on the read-only path, whose entire question is WHAT drifted. THE OBVIOUS FIX IS A NO-OP AND ONLY THE WITNESS SHOWED IT. Both the review and an independent reviewer prescribed carrying the divergences into the `RepositoryRulesetActuationAdmitted` arm. That typechecks, resolves clean, reads correctly, and changes nothing observable: the sibling claim `an_empty_roster_reaches_the_unbound_realization_cause` asserts the admission returns `Refused(RealizationUnbound)`, NOT `Admitted`, so RealizationUnbound is produced by the ADMISSION KERNEL and passed straight through. The arm named by the remedy is unreachable. The witness went FAIL on that version and PASS on this one, which is the discriminating RED and the positive control on one class. WHAT ACTUALLY ROUTES: when the only objection is the unbound writer, drift is the answer; a genuine hold still wins. `repo_ruleset_hold_is_only_the_unbound_writer` enumerates all seven causes rather than using a wildcard, so a hold added later must be classified by hand instead of silently joining the drift branch. HOLDS DELIBERATELY STILL WIN, which is where this departs from the review. A hold means the admission question could not be answered safely at all -- an unobservable bypass roster, an unexpected live rule, a signed policy this projection cannot carry. Those are typed and located about their own subject, and answering with drift instead would suppress a safety refusal to answer a question nobody asked. §5 forbids losing the located cause, not ordering two located causes. Recorded in the annotation, not left as a silent deviation. `repo_ruleset_drift_actuation` is split out because the handler adapters take the context as `_: Bool`, which no witness can supply by name -- the same split `world_repo_ruleset_observation` already carries behind its adapter. The decision was reachable only through a live read, which is how the discard survived four reviews. Seven witnesses PASS locally, six of them pre-existing and unchanged. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015EdHQ244XXdBKRKGX6jrV4 * Pay the roster-touch obligation #10350's merge created, and regrain the read-only trigger FLOOR BLOCKER, from required run 33908433503: `2 consumed admission(s) due for deletion on this roster-touching change`. Both `kernel-identity predicate relocation gunbc#10350` rows were consumed by their own merge into this branch's base. Their entry named this roster's next touch as when the deletion comes due; this change is that touch. Deleted, with KERNEL_IDENTITY_RELOCATION_LABEL. Adjudicated by the declaring-module join rather than the trigger sentence, on that entry's own rule: `resolved_node_is_kernel_identity_for_name` is declared exactly once in main's tree, at module v1.std.core, and both named callers still reference it -- so base and head bind the same declaring module and the delta is unproducible. The join was calibrated: a fabricated name answers NOT DECLARED against that module while `kernel_span` answers DECLARED, so both verdicts were reachable. Deletion pass asserted examined == kept + deleted (4 = 2 + 2) against a brace-depth parse, not a grep. The claim in the TWENTY-FOURTH TRANSITION that the merged array is four rows is corrected in place rather than left to rot, since it is now false. ALSO, from review 60315's non-blocking remark. It observed that WorldApplied is never constructed. The type stays: it is a variant of the subject-agnostic WorldActuation shape, and that no currently-bound subject constructs it is a fact about repo_ruleset's binding, not about the shape -- deleting it would leave the generic fold unable to express a successful actuation at all. But the remark points at a real §4b(3) defect it did not name: the read-only standing named #10204, an ARTIFACT, as its trigger. Both sites now name the CAPABILITY and what that change must be SUFFICIENT FOR -- a write preserving the live bypass-actor roster AND a post-apply read-back observing it survived -- and say explicitly that #10204 merging is not itself the trigger. This is the grain whose absence this same module already records paying for once. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015EdHQ244XXdBKRKGX6jrV4 * File the failure mode the &[ near-miss exposed: an edit pass that matched nothing reports success An edit pass whose locator binds to the WRONG CONSTRUCT finds zero subjects, changes zero bytes and exits zero -- and a pass that acted on nothing is indistinguishable from one that correctly had nothing to act on. SPECIMEN, from this PR. Paying the consumed-admission obligation, a pass anchored the roster array on the literal `&[`. That substring occurs FIRST in the declaration's TYPE ANNOTATION, `&[TransitionAdmission]`, so it bound to a 19-character body, found zero rows, and would have reported the obligation paid with an EMPTY DIFF -- after which the wall refuses the next push for the same two rows, against a branch whose own entry claims to have deleted them. Filed rather than folded into empty_capture_read_as_clean_result, which is the nearest sibling: that row covers an empty CAPTURE, and this is the same class moved from reading to WRITING. The move is what makes it worse. An empty capture gives a false all-clear about someone else's artifact; an empty edit gives a false all-clear about an obligation THE AUTHOR OWES, so the debt stays on the roster while the commit message says it was paid. The discriminator is a denominator assertion, not a louder locator: assert examined == kept + deleted against an INDEPENDENT count. Independence is load bearing here -- a grep of `TransitionAdmission {` over the same file answers one HIGHER than the roster because it counts the struct definition, so the cross-check must be a different instrument rather than the same one re-run. CEILING mechanically preventable, with the trigger at capability grain: a shared edit-pass harness that takes the subject population as a REQUIRED input and refuses on a zero or mismatched match count. An author remembering to write the assertion does NOT retire the row -- that is the state this specimen was already in, and it held only because this branch had been burned once already. docs/design-failure-modes.md regenerated by the sanctioned producer named in the workflow's own refusal (generated_artifact_gate main_wet): 2 insertions, 0 removals, no unrelated artifact drift. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015EdHQ244XXdBKRKGX6jrV4 * chore: regenerate drifted generated artifacts (ci auto-heal) * Pay the roster-touch obligation a third time: delete gunbc#10439's six consumed serving-engine rows FLOOR BLOCKER on the merge head: `6 consumed admission(s) due for deletion on this roster-touching change`. gunbc#10439 merged, so all six `serving engine launch vocabulary move` rows were consumed by their own merge, and the deletion falls to this roster's next touch. That is this change, which inherited the rows by taking main's file whole in the preceding merge. ADJUDICATED BY THE DECLARING-MODULE JOIN, not by the trigger sentence. All three spellings the rows name -- spark_serving_bind_listen_wire, SparkServingBindListen, OllamaServingLaunchProfile -- are declared in gunbc.spark.serving_engine on main, so base and head bind the same module and no run can produce these TargetChanged deltas. Calibrated: a fabricated name answers NOT DECLARED against that same module, so a uniform "found" was not available to the join. Deletion pass asserted examined == kept + deleted against a brace-depth parse, 8 = 2 + 6. The two gunbc#10324 rows are the whole remainder. THIRD TIME ON THIS BRANCH IN ONE EVENING, and it authored none of them: 30 gunbc#10355 rows, both gunbc#10350 rows, now these six. Each arrived the same way -- another PR merged, its rows became consumed, the debt attached to whoever next touched the file. Recorded in the entry because the roster is where the cost lands, and no author action avoids it. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015EdHQ244XXdBKRKGX6jrV4 * chore: regenerate drifted generated artifacts (ci auto-heal) Ledger-Repair-Judged: docs/design-failure-modes.md Ledger-Rows-Repaired: docs/design-failure-modes.md edit_pass_that_matched_nothing_reports_success Ledger-Repair-Judged: docs/design-rung-drops.md * chore: regenerate drifted generated artifacts (ci auto-heal) Ledger-Repair-Judged: docs/design-failure-modes.md Ledger-Rows-Repaired: docs/design-failure-modes.md edit_pass_that_matched_nothing_reports_success Ledger-Repair-Judged: docs/design-rung-drops.md * Pay the fourth consumed-admission obligation: 22 gunbc#10445 rows go dark THE WALL NAMED THEM, AND IT NAMED ALL OF THEM: required-ci: FAILED PHASE namespace-wave-admission (0 unadjudicated delta(s), 0 stale admission(s), 22 consumed admission(s) due for deletion on this roster-touching change) gunbc#10445 merged between this branch's last two heads, so its 22 rows -- 5 node_target_of constructor rows and 17 object-table codec rows -- were consumed by their own merge, and the deletion falls to the next change that touches this roster. That is this one. Both label constants go with the rows they labelled. ADJUDICATED BY THE DECLARING-MODULE JOIN, NOT BY THE TRIGGER SENTENCE. Every spelling the rows name is declared in the module the row gives as its target: node_target_of in gunbc.scm.object_store, and object_id_key / encode_target / EncodeAcc / DecodeAcc / DecodePositions / decode_object_table / encode_object_table / node_position_of / resolve_reference in gunbc.scm.object_table_json. Base and head bind them identically, so no run can produce these deltas. CALIBRATED, NOT TRUSTED: a fabricated spelling through the same join answers NOT DECLARED against the same module, so a uniform "found" was never available to it -- without that control the join could have been reporting its own success. THE PASS ASSERTED examined == kept + deleted AGAINST A BRACE-DEPTH PARSE: 24 = 2 + 22. Not against a grep, because `TransitionAdmission {` also matches the struct definition, and an earlier pass on this branch anchored on `&[`, matched the type annotation `&[TransitionAdmission]`, examined a 19-character body, found zero rows, and would have reported the obligation paid with an empty diff. That class is filed as edit_pass_that_matched_nothing_reports_success -- whose roster row this same branch adds. FOURTH TIME, 60 ROWS, NONE OF THEM AUTHORED HERE: 30 from gunbc#10355, 2 from gunbc#10350, 6 from gunbc#10439, 22 from gunbc#10445. No author action avoids it; it is a queue artifact, and the roster is simply where it lands. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015EdHQ244XXdBKRKGX6jrV4 * chore: regenerate drifted generated artifacts (ci auto-heal) Ledger-Repair-Judged: docs/design-failure-modes.md Ledger-Rows-Repaired: docs/design-failure-modes.md edit_pass_that_matched_nothing_reports_success Ledger-Repair-Judged: docs/design-rung-drops.md * chore: regenerate drifted generated artifacts (ci auto-heal) Ledger-Repair-Judged: docs/design-failure-modes.md Ledger-Rows-Repaired: docs/design-failure-modes.md edit_pass_that_matched_nothing_reports_success Ledger-Repair-Judged: docs/design-rung-drops.md --------- Co-authored-by: gunbc-ci-auto-heal <gunbc-ci-auto-heal@users.noreply.github.com> Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
Register printer-01 as a fleet intent row, and give the AMS Lite its own feed-path authority
The first printer is physically received, labelled printer-01 by the operator, and on the
operator LAN. This lands the three facts that were previously missing, each in its own
authority, and closes one roster that had been standing empty.
THE PRINTER IS AN APPLIANCE, MODELLED LIKE THE BMC AND NOT LIKE A HOST. fleet_reach's probe
targets and fleet_probe_ready_ssh_target are SSH-shaped, and a printer has no SSH, so routing
it through them would have required a HostIdentity that names no host. The existing precedent
is the BMC: a network-reachable device with its own endpoint authority sitting beside the host
endpoints in fleet_intent_network. printer_01_lan_endpoint follows it, with host absent and a
new FabricationAppliance attachment role.
The address was discovered rather than declared: a two-port sweep of the operator segment from
srv2 found one responder on 990 whose FTPS banner is
220 BBL-P003 FTP Server. 8883 is closed,which is the signature of a printer that is on the network but not in LAN Mode, so no route
from this repo can command it yet. That is also why the endpoint carries no transport rows: a
transport is a realization bound to a shape, and the shape is not yet observed.
network_reachability already says ContainerRunner cannot reach HostLan, and the discovery
confirmed it -- the sweep had to run from srv2 because the session container cannot see the
segment. Every future route to this address is brokered through a host on the LAN by the model,
not by a workaround.
THE ASSET ROW IS DECLARED ONCE, IN fleet_physical_inventory, beside switch_01 and pdu_01, and
imported by manufacturing_manifest. A printer is a physical asset on the operator floor before
it is anything the manifest cares about, so re-authoring its identity or catalog there would be
a second authority for one machine. physical_serial stays absent: a serial corroborates an
individuation rather than establishing one, which is the rule that module already states.
owned_printer_inventory was empty pending physical receipt. It now carries printer_01_asset,
which makes w_every_live_inventory_row_names_the_qualifiable_product discriminating for the
first time -- it was folding over an empty list, so it could not fail.
THE AMS LITE IS A SEPARATE SUBJECT AND THE EXCLUSION IS A PROPERTY OF THE FEED PATH. The A1
mini's own vendor table grades Tpu and Pva SuitabilityIdeal; the AMS Lite manual excludes both.
Those are not in conflict -- the machine prints them from the external spool holder, and the
AMS Lite cannot convey them -- so authoring the exclusions into the machine module would have
forced one of two true facts to be deleted. The accessory gets its own module and citation.
FeedPathSuitability is deliberately a second type rather than a reuse of MaterialSuitability,
because the two grade different subjects and only one carries a cause. The manual gives two
distinct reasons, and the fiber-reinforced clause ends in "etc": keying that row to
FiberReinforcedPolymer rather than to the six spellings it lists covers the members it declined
to enumerate, where a blocklist would have admitted the seventh.
Pla and Petg are NOT graded admitted. A first draft graded them so on the reasoning that the
AMS Lite plainly feeds them; no quoted sentence said so, and "plainly" is not a citation. Both
rows were withdrawn and the lookup answers FeedPathUnstated for them, which is the state the
manual is actually in. They return when the supported-material statement is quoted.
Evidence, by execution: the three AMS Lite witnesses and the manifest live-row witness each
return true under
gunbc runon this tree. The discriminating one isw_tpu_is_machine_ideal_and_feed_path_excluded, which a model collapsing the two grades into one
cannot make green; w_an_unmentioned_material_is_unstated_not_admitted goes red the moment a row
is authored without a citation behind it.
Co-Authored-By: Claude Opus 5 noreply@anthropic.com
Claude-Session: https://claude.ai/code/session_01TuCmz2HjuYzP6GWLgw11E4
🤖 Generated with Claude Code
https://claude.ai/code/session_01TuCmz2HjuYzP6GWLgw11E4