Repository navigation
Genericity is declared, not inferred from a failed lookup - #10727
Conversation
At the census-borrowed declaration route, absence from a lookup was standing
as positive evidence of generic ownership -- and the environment that lookup
consulted has no bindings at all, so the evidence was unconditional rather
than occasional.
census_declaration_type_env builds bindings, str_bindings, ancestry_str_bindings
and intern_table as empty maps, seeded only with the declaration's type-parameter
bindings. In that environment lookup_binding_by_name_local can only ever find a
tp_name. Two sites read a miss there as "this leaf is a generic": the roster
producer declaration_unbound_leaf_names, whose results were concatenated into the
generic-name roster, and the stamp producer declaration_substitution_basis, whose
second disjunct repeated the same test. So for every census-borrowed declaration,
every unqualified non-kernel concrete type name in every parameter was enrolled as
a generic parameter and stamped TypeVariable { id: <its own name> } -- always,
not sometimes.
The harm is at the DESIGN 4b floor. direct_call_argument_inhabitance_diags takes
the substitution basis as the declared side whenever it carries an unbound type
variable, and declared_type_inhabitance reads that stamp as a generic formal and
declines with UndecidableGenericFormal. A value of any constructor reaching a
NonEmptyStr or HostIdentity position was therefore admitted in silence -- not
because the pair was undecidable, but because the declared side's identity had
been overwritten before the judgment ran. That is DESIGN section 5's absorbing
fallback: unable to resolve, so widen to generic, so decline.
The authority was already in hand one field over. borrowed_census_callable_candidate
computes borrowed_generic_param_names on its first line and classifies the RETURN
type of the same declaration positively with it, via qualify_borrowed_type_names:
roster members stay variables, every other bare leaf resolves to its owner module
and is qualified. The parameter types, in the next field of the same record literal,
went through the guess. One question, two answers (DESIGN section 3). The parameter
side now consumes the same roster and the same qualifier, and the absence disjunct
is deleted from both sites together, because they were one classification authority
written twice.
The roster reads a declared fact, not a naming convention: parse_optional_type_params
mints one param per name in the angle-bracket clause whose name and whose type-expr
name are both that name, and no authored parameter of that shape exists in the
corpus. fn_type_param_names and param_is_generic_decl already read the same
encoding.
Declared limit: a synthesized or member declaration that borrows an enclosing
owner's generic without restating it is not on its own roster, so that leaf stays
bare and is reported UndecidableFormalUnresolved -- the reason that means exactly
that -- rather than claiming a genericity the declaration does not carry. Its
next-rung trigger is an owner-generic carrier on the borrowed declaration.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MuB8wHjijaFwNoyzm78stF
DESIGN 4c admits only standalone leading blocks attached to module-scope declarations; the block sat inside declaration_substitution_basis's body and the v2 self-compile refused it seven times, once per line. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MuB8wHjijaFwNoyzm78stF
Routing declared_type through qualify_borrowed_type_names was a second, wider claim riding under this one's title. Every consumer of ResolvedFormal.declared_type would have read a qualified name where it read a bare one, and one of them -- direct_call_generic_type_argument_inhabitance_diags -- compares authored_name_at of the declared node against the actual's for EQUALITY. A population delta after that change could not be attributed to the genericity repair. Only the roster moves: declaration_generic_names is now borrowed_generic_param_names (the declaration's own generic clause) rather than a census of lookup failures, and declaration_substitution_basis consults that roster alone. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MuB8wHjijaFwNoyzm78stF
… repair The .dag edit without its emitted seed is drift: heal-generated-artifacts succeeded on the previous head while required-witnesses-build refused on exactly v1_compiler_infer_env.rs and v1_compiler_infer_lookup.rs. Leaning on heal to launder that is relying on one mechanism to answer for a lane that is separately refusing, so the mirrors are regenerated and committed here, verified by content: the env mirror loses the lookup disjunct, and the lookup mirror loses declaration_unbound_leaf_names entirely. Regenerated by target/release/claim_executor --required-ci --required-lane build into target/stage0-regen-candidate and installed from there. Files the class row adds: gunbc.recurring_failure_mode lookup_miss_read_as_positive_classification, enrolled in the roster, with its projection into docs/design-failure-modes.md. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MuB8wHjijaFwNoyzm78stF
…one chain Removing the generic-formal stamp made a question reachable that had never been put: a value whose type sits on the same refinement chain as its declared position. Measured the moment the stamp came out, two corpus sites refused -- HostIdentity flowing into a NonEmptyStr formal, in dag/test/claim/ci/ ci_deploy_target_host_witness_test.dag -- which is widening to its own declared base and is correct code. v1.compiler.infer declared_type_conformance_note had already measured that exact class from the other side and classified it as correct code falsely refused. Two causes, both fixed here. nominal_product_head_name_if_declared_product read a where-refinement as a nominal product: `NonEmptyStr = String where non_empty` carries connective Conj with one child, which is also the shape of a one-field record, so the bare Conj-with-children test admitted refinements to an arm whose contract is "concrete named products and their transparent aliases". is_where_refinement_type already existed to tell them apart and is now consulted. refinement_inhabitance decides the pair from where_refinement_chain -- the peel authority peel_where_refinement_base already consumes, so this adds a relation over the existing single authority and not a second peel. Widening (the produced type carries the declared one on its chain) INHABITS. The other two arms decline. WHY NARROWING DECLINES RATHER THAN REFUSING, and this is a measurement. std's brand prose refuses narrowing, and the first build of this relation did too: 10 corpus refusals became 4928, of which 3870 were a plain String reaching a NonEmptyStr formal. That is not four thousand defects. Refinement values are introduced by literal elaboration and declared casts, and a produced node no longer carries which introduction made it, so the type pair does not settle the question and refusing on it would fabricate a verdict in the direction DESIGN section 5 forbids exactly as firmly as fabricating a success. The pair is reported UndecidableRefinementIntroduction: located, per-reason, countable, so the deficit can rank. Next-rung trigger: the refinement-introduction authority reaching this seam, carried on the produced node. nominal_opaque is not touched: `type Secret nominal_opaque = String` is not a where-refinement, its chain is one link, and the relation returns absent for it -- Secret does not ground to String, which is the wall that form exists to provide. Measured, same tree, locally built binaries frozen before running, partitioned by site identity: the refusal set is IDENTICAL to main's -- none gained, none lost -- while generic-formal declines fall 13643 to 5078 and 4920 pairs reach the new typed refinement decline. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MuB8wHjijaFwNoyzm78stF
…t this seam The first account of why narrowing declines said the produced node no longer carries which introduction made it. An executed check refutes that half. dag/test/fixture/compile_phase_frontier_receipt_fixture calls sha256_digest(hex: "..." as NonEmptyStr) -- a String literal at a refinement formal, carrying an explicit cast -- and this relation produces NO diagnostic for it at all. Its four diagnostics are optional-carrier and nothing else. Meanwhile every site that does decline is a bare literal handed straight to a refinement formal with no cast: "feedFwdOffsetCoeff" at a NonEmptyStr, 168 at a numeric refinement, "declaration-worker" at a NonEmptyStr. So one of the two introduction forms is already read here, and the blanket claim was wrong. The remaining form is literal elaboration at the declared boundary, which std.literal_elaboration owns and this relation does not consult -- the same shape the numeric-realization note in this file already records, where the class was never undecidable, it was UNCONSULTED. Declining is still right, because refusing on a fact one is declining to look up fabricates a verdict; but the trigger is now named at the capability that actually decides it: the elaboration verdict for a literal at its destination refinement reaching this seam on the produced node. Annotation and the reported reason both corrected. No behaviour change. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MuB8wHjijaFwNoyzm78stF
Two measurements, one section 3 answer, no behaviour change (annotations are erased from the semantic projection, so the stage0 mirror is unchanged). FIRST, the refinement-introduction trigger is not speculative. An empty string literal handed to a NonEmptyStr formal compiles with NO diagnostic from anything in the tree. So the 4875 sites this relation newly declines are real unchecked refinement introductions -- the predicate is not verified at introduction today, and this relation is the first thing that makes the population visible, located and countable. SECOND, the horizontal case is already walled and must not be walled twice. where_refinement_brand_nominal_verdict, reached through where_refinement_mismatch_diags on the expected-type path, compares brand DECLARATION SITES rather than spellings and refuses BrandNominalDistinctDeclarations. Refusing sibling pairs in this relation as well would be a second authority for one judgment. It would also be wrong at this grain, measured rather than reasoned: refusing every sibling pair produced FilePath at a NonEmptyStr formal, GcpProjectId at a NonEmptyStr formal and Sha512DigestHex at a NonEmptyStr formal -- peer predicate refinements over the same base carrying no conflicting brand at all, whose chains fail to nest only because each is declared over String directly rather than over NonEmptyStr. A sibling relation is not a brand conflict, and only the brand authority can tell the two apart. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MuB8wHjijaFwNoyzm78stF
# Conflicts: # dag/gunbc/recurring_failure_mode/roster.dag # docs/design-failure-modes.md
Ledger-Repair-Judged: docs/design-failure-modes.md Ledger-Rows-Repaired: docs/design-failure-modes.md lookup_miss_read_as_positive_classification Ledger-Repair-Judged: docs/design-rung-drops.md
CONTROLS, all measured live on the merged tree before enrolment.
Producer grain, the paired mutations that a diagnostic-count drop cannot prove.
Both go through the GLOBAL-BARE CENSUS route -- callee named without an import,
so it resolves through the corpus census and reaches the producer this repair
changed.
positive: optional_present<T> still declines UndecidableGenericFormal (1).
Silencing borrowed_generic_param_names leaves the leaf unstamped, resolving
nowhere, producing NO diagnostic -- the arm reds on zero.
negative: sha256_digest_wire_form(digest: Sha256Digest) declines nothing (0).
Restoring the local-miss classification in either deleted site stamps
TypeVariable { id: "Sha256Digest" } and the arm reds on one.
Together they prove both halves: declared implies generic, failed lookup does not.
Refinement grain.
a branded refinement at its own declared base INHABITS (0 refused, 0 undecided
-- the second count is the discriminating half, since zero refusals alone
would also hold if the arm merely stopped deciding);
two distinct brands over one base are not refused twice: this relation declines
and the existing brand wall refuses, measured AT THE DIRECT-CALL ARGUMENT
SEAM rather than assumed from the cast path;
nominal_opaque does not ground through the chain;
a bare literal declines and a cast one does not, at the same formal.
TWO CLAIMS OF MINE WITHDRAWN, both refuted by fixtures rather than argued down.
The annotation said an explicit cast is not carried at this seam. It is:
takes_non_empty(s: "x" as NonEmptyStr) produces nothing from this relation.
The annotation then said the predicate goes unchecked at introduction, on a probe
that reported no diagnostic for an empty literal. A three-way pair refutes it:
takes_non_empty(s: "") is REFUSED by the where-refinement machinery with
`type mismatch: expected Product(NonEmptyStr), got Primitive(String)`, while
takes_non_empty(s: "x") is admitted by it, and this relation declines both. So
the introduction question is ALREADY DECIDED by an authority this relation does
not consult -- the same "never undecidable, merely unconsulted" shape the numeric
note in this file records. The next-rung trigger is correspondingly cheaper:
consult that verdict, not carry a new elaboration fact to the seam.
The recurring_failure_mode row asserted that the roster residue reports
UndecidableFormalUnresolved. The census moved that population by zero across the
repair, so the row now records it as an ASSERTED AND UNEXERCISED boundary. A row
is the durable artifact and a disposition nothing executed is
specification-without-execution.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MuB8wHjijaFwNoyzm78stF
…ion/smart-ant-528
Ledger-Repair-Judged: docs/design-failure-modes.md Ledger-Rows-Repaired: docs/design-failure-modes.md lookup_miss_read_as_positive_classification Ledger-Repair-Judged: docs/design-rung-drops.md
Three repairs, each from a measurement or a review finding, not from taste. THE ROSTER IS A UNION AGAIN. Deleting the absence disjunct was the repair; replacing fn_type_param_names with borrowed_generic_param_names alongside it was a second, uncontrolled change. The two predicates are not the same: the borrowed roster is wider on one axis and NARROWER on two, so a declared type param whose type expression is not a childless NoConnective leaf would have been reclassified as a concrete unresolved name -- the exact failure the negative control exists to prevent, arriving through the other door. declaration_generic_names is now concat(tp_names, map_keys(borrowed_generic_param_names(...))). Review finding, tidy-lynx-804, 2026-09-07. THE CONTROLS WERE AUTHORED AGAINST THE WRONG ORACLE AND ALL SIX WERE FALSE. violation_count reports every row of the class in the WHOLE census, not the fixture's contribution, and the literals in those arms were copied from a probe DELTA. That is precisely what DESIGN 5 refuses: a merge-blocking test comparing a live population to a numeric literal that is not grounded in a controlled fixture. Each arm is now a PAIR of fixtures differing on exactly one axis, so the shared corpus contribution cancels and only the axis is measured. Measured on this head, with the same instrument on both halves: declared NonEmptyStr <- produced HostIdentity == its exact-match partner the horizontal brand pair > its exact-match partner bare literal at a refinement formal > the cast form concrete leaf via global-bare == a fixture with no call THE POSITIVE CENSUS ARM IS WITHDRAWN BECAUSE ITS RED IS NOT AUTHORABLE. The fixture contributes zero rows of the class -- equal to a fixture with no call at all -- so the seam emits nothing there whether the classification is right or wrong. An arm asserting one is false; an arm asserting zero is green by construction and would later be cited as coverage of something it never measured. The cause is a harness boundary, not a fixture shape: src/v1 is not a source root under the required floor, so no witness there can observe the substitution basis. NEXT-RUNG TRIGGER, at capability grain: a floor-executable surface that can observe the substitution basis's classification -- src/v1 on a floor source root, or a black-box seam that renders the stamp distinguishably. What is measured is the REMOVAL OF A FALSE POSITIVE, which the negative arm measures directly. What stays unmeasured is the survival of the positive authority. The union above makes that safer by construction; construction is not evidence and this does not claim it is. Also regenerates docs/design-failure-modes.md, which drifted because the merge resolution took main's projection without re-projecting the new row. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MuB8wHjijaFwNoyzm78stF
…ion/smart-ant-528
Six items, all from review, all on the relation this PR adds. They are one shape stated once: the new relation collapsed distinctions its neighbours preserve -- two facts under one reason, two identities under one spelling, and a missing identity repaired by lowering the grain until something compared equal. 1. THE PEER ARM CARRIES ITS OWN REASON. Narrowing and siblings both reported UndecidableRefinementIntroduction, whose label describes introduction only. The measured population proves they differ: declared NonEmptyStr <- produced FilePath, GcpProjectId, Sha512DigestHex are peer predicates over one base, neither introductions nor brand conflicts. A mixed bucket is not located, per-reason or countable -- the property that note claims for itself -- and it would mis-aim the next trigger, discharging it while leaving a peer residue under a name asserting it had been handled. (Review 62043, cursor/auto.) 2. THE CONSTRUCTOR IS RENAMED WITH THE REASON. RefinementSiblingBrands -> RefinementPeerChains: the measured population contains no brands, so the old name encoded the same conflation one level above the reason. 3. THE CHAIN IS COMPARED AT DECLARATION SITE, NOT SPELLING. It consumed qualified_last_segment -- a module-stripped last segment -- while where_refinement_brand_declaration_site in the same file builds file#name identity for the same question. Two homonymous refinements would have compared EQUAL and widening would have returned Inhabits for a pair that does not inhabit. This file already suffered that class: a second HostIdentity was minted beside product.placement_supply's and did not surface as a duplicate declaration. Only one survives today because the incident was CLEANED UP, not avoided -- so this is a reintroduction, not a hypothetical. 4. THE LOOKUP-MISS ARM REFUSES INSTEAD OF WIDENING. It manufactured a singleton chain from the name's last segment when no declaration was established, so callers treating both chains as established compared something that was never resolved -- DESIGN §5's absorbing fallback, and under declaration-site keying it would fabricate an identity the subject does not have, at the one grain where unrelated things compare equal. ABSENCE OF IDENTITY CANNOT BE REPAIRED BY LOWERING THE IDENTITY GRAIN UNTIL SOMETHING COMPARES EQUAL. 5. THE FAILURE-MODE ROW'S CEILING STATES BOTH POPULATIONS. It claimed ceiling 3 from a premise about the CLAUSE while the subject is the CLASSIFICATION, which a predicate decides and an authored shape can satisfy: eight structural producers cannot mint `pname == type-expr name`, four authored-dependent sites can. Now 3 for the clause-derived population, 2 for authored parameters, with the authored shape named as what stands between it and 3. The row's repair sentence also records that the roster passed is a UNION. 6. BOTH HALVES OF THE BRAND DELEGATION ARE ASSERTED. The witness asserted only that this relation abstains. If the brand wall regressed or stopped reaching the argument path, that stayed green while the horizontal pair was admitted in silence by both relations -- green for a reason other than the one it claims, which is the defect this whole lane exists to remove. The wall's TypeMismatch firing is now a conjunct of the same witness, against the same exact-match partner so the corpus term cancels. The whole required floor is green locally with all six in. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MuB8wHjijaFwNoyzm78stF
# Conflicts: # dag/gunbc/recurring_failure_mode/roster.dag # docs/design-failure-modes.md
The merge took main's side of docs/design-failure-modes.md, which is the declared repair route for that path's merge driver, but the route leaves the projection carrying main's rows and not this branch's until something re-derives it. So the row was declared and rostered and NOT projected -- the generated artifact drift the build lane refuses. Verified by CONTENT in both directions rather than by exit code: every row main carries is present, and this branch's row is present. That check is the one that matters after a MERGE, not only after a regeneration -- this is the second generated artifact on this branch lost that way (the stage0 mirrors were the first), and in both cases the loss was invisible in the merge itself: no conflict markers, no complaint, a file quietly holding the other side. The stage0 mirrors were re-derived on the merged tree in the same step and agree by content, so they are unchanged here. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MuB8wHjijaFwNoyzm78stF
Ledger-Repair-Judged: docs/design-failure-modes.md Ledger-Rows-Repaired: docs/design-failure-modes.md lookup_miss_read_as_positive_classification Ledger-Repair-Judged: docs/design-rung-drops.md
Two review findings, both correct, both falsifying something this diff asserted about itself. EVERY ARM IS NOW A DIFFERENCE, INCLUDING THE ZEROES AND THE ONES. The nominal_opaque arm asserted `DeclaredTypeNotInhabited == 1` -- a merge-blocking equality against a population the census draws from the fixture's whole import closure, with a bare literal recording today's tree. This file's own standing note says why that cannot be an oracle, seventy lines above the arm that did it. Re-authored against a Secret-free partner differing only on the nominal_opaque axis. Re-checking the others against the same question rather than trusting an earlier pass found the same shape in the `== 0` conjuncts, which look fixture-local and are not: refusal counts happen to be zero across that closure today, which is the coincidence that makes the absolute form look safe and is not a time-stable fact. All five arms are now differences between two fixtures. (Review 62112, claude/opus.) A CHAIN WITH ANY UNIDENTIFIED LINK IS DISCARDED, NOT COMPACTED. The annotation claimed dropping a link "can only turn an admission into a decline -- the conservative direction, never a new admission." THAT CLAIM IS FALSE, and the counterexample is the HEAD: drop the declared side's head and its BASE is promoted to head, so the produced chain contains it, the widening arm fires, and a value is admitted at a strictly narrower declared refinement it was never shown to inhabit. Compaction is not the conservative direction; it is the widening one, one link up -- the same move the sibling paragraph forbids by name. The function now returns the empty chain unless every link is identified, and the annotation states what is true rather than what was convenient. (Review 62130, claude/opus.) The whole required floor is green locally with both in. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MuB8wHjijaFwNoyzm78stF
…ion/smart-ant-528
…degrading what you compare Both arms in this relation reached for a weaker comparison when the precise one was unavailable -- one lowering the GRAIN to a spelling, one shortening the CHAIN past a link it could not identify -- and both were assessed as conservative in general while reversing direction at a particular position. That is one principle with two instances, not two patches, and the file now says it once above the function that carries both rather than twice inside it. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MuB8wHjijaFwNoyzm78stF
briansrls
left a comment
There was a problem hiding this comment.
Exact-head whole-diff review of 774a048: REQUEST_CHANGES.
-
BLOCKER — the v1-freeze admission argument does not discharge PublicSurfaceGrowth, which the standing says DOMINATES purpose admission. The authority explicitly defines PublicSurfaceGrowth as a diff over the emitted seed's exported declarations. This head adds exported declarations in the public
v1_compiler_infermodule:refinement_chain_link_identities,refinement_chain_names, the newRefinementInhabitanceenum,refinement_inhabitance, plus new variants on the publicInhabitanceUndecidableReason. The generated mirror renders these aspub. Refusal-set identity is good evidence against NewLanguageBehavior; it is not evidence that the exported surface did not grow. Either restructure the integration closure so it does not grow the frozen seed's exported declaration surface, or obtain an authority ruling that actually changes/discharges this dominating refusal. The current purpose/self-host measurements cannot override it. -
BLOCKER — the refinement identity repair still loses exact declaration identity before the new site-key comparison, and the 'all links identified' check does not actually prove declaration resolution.
refinement_chain_namesstarts by takingauthored_name_at(n)and callinglookup_type_by_name, even thoughlookup_type_foris the existing node-aware authority and usesnode.identfirst. Thus a node that already carries identity can be rebound through ambient bare-name/homonym resolution before declaration-site IDs are compared. Inside the chain,where_refinement_chainexplicitly falls back to the rawbasenode whenlookup_type_formisses;refinement_chain_link_identitiesthen callswhere_refinement_brand_declaration_siteand treats the presence ofident_spanas proof of identity. Butident_spanis not declaration provenance —default_ident_spangives any named node an ident span. An unresolved reference can therefore be assigned afile#namekey and survive the completeness check. This is the same class the comments say is forbidden: absence of identity is being repaired by degrading what is compared. Resolve the head and every chain link through an exact declaration-aware carrier (or make the whole relation unavailable on any resolution miss); do not turn reference spelling/location into declaration identity.
Gate note: this head is no longer an integration candidate even apart from the findings. Its base is 100b6e7..., while current main is f69e0a3... (9 commits ahead), including a docs/design-failure-modes projection change. Per exact-head doctrine, merge current main, re-project/regenerate, re-derive the matrix/refusal identity set, and rerun CI before requesting another verdict. Any new head is a new review subject.
Blocker 2 of the REQUEST_CHANGES on 774a048, verified in source before accepting and not argued. THE KEY WAS NOT AN IDENTITY. where_refinement_chain falls back to the raw base node on a lookup miss, and where_refinement_brand_declaration_site keys on ident_span -- which default_ident_span sets from the node's OWN name_span at construction. So on a fallback link the key was a REFERENCE-site key, wrong in both directions at once: two distinct declarations referenced from one file key EQUAL, one declaration referenced from two files keys UNEQUAL. A key that both merges distinct things and splits identical ones is not an identity, and there is no tightening that repairs one, so the fallback is gone rather than sharpened. THE ENTRY POINT RESOLVED BY SPELLING. refinement_chain_names took authored_name_at and then looked the type up BY NAME, so a node already carrying identity was rebound by spelling at the head of a relation whose whole subject is spelling collision. Both it and every link now resolve through lookup_type_for, the node-aware authority that matches node.ident first, and the site is taken from the resolved DECLARATION. THE COMPLETENESS CHECK MEASURED THE WRONG THING. The count equality compared ident_span PRESENCE, which every named node has, so it proved the links were NAMED and could not fail for the reason it existed. It now compares RESOLVED links, and any miss discards the whole chain: the pair is not this relation's subject. Refuse, never widen -- no falling through to the raw node. Also drops the where-refinement blinding of the nominal-product arm, which was unnecessary: the refinement arm runs first, so widening is admitted before that arm is consulted, and every control stays green without it. It is NOT claimed to repair the disjoint-pair case from review 62172 -- measured on both sides, main's compiler does not refuse that pair either, so that defect is pre-existing and is being enrolled separately rather than attributed here. The whole required floor is green locally. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MuB8wHjijaFwNoyzm78stF
Four conflicts, ALL generated mirrors: v1_compiler_infer.rs, v1_compiler_infer_lookup.rs, lib.rs, emitted_population.rs. #10743 regenerated the same mirrors this branch regenerates, and #10727/#10737 changed the same resolver. Both .dag AUTHORITIES (04_infer, 04_lookup) auto-merged as real content merges -- that is where the semantics live. The mirrors are NOT hand-resolved: taking ours here is a bootstrap step only, because a mirror tree that cannot compile cannot run the producer that would regenerate it. cargo check green at this point confirms a working producer; the mirrors are re-derived from the merged authorities in the following commit. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012wNf4iRSUKNtgxmE8qxkww
REVIEW FINDING 1, CONFIRMED AND IT IS THIS PR'S OWN CLASS. Roster was 175 on main and 174 here: added production_actuation... and silently dropped decision_surface_truncation (#10712) and lookup_miss_read_as_positive_classification (#10727), both landed after the branch point and both with their module files still present. The triple-dot diff renders it as reorder-plus-two and shows nothing. Found by the count comparison the PR's own row prescribes, named by an identity join, and repaired by merging main so the roster is 176 with an empty join in both directions -- imports and list body checked separately, since either half can drop alone. REVIEW FINDING 2, CONFIRMED. The reduction left the launch graph's imports (32 lines -> 8 in the module, 37 -> 18 in the witness) and an annotation asserting mint-last/LaunchAdmitted ordering that no longer has a mint or a launch to range over -- a 4c annotation no Accepted program reads, so a false one never reds and it drifts to flatter the change. Annotation replaced with what the module now does, including why the ordering argument is NOT restated here. Orphan fixtures deleted (launch_org, launch_net, launch_binding, witness_reserve, both lifecycle intents, launch_test_slot); witness_reserve's own note was stale too, still saying production refuses every launch. Also declares the seal a 3c frontier rather than leaving it implicit: nothing consumes it, and no witness CAN -- its input is a sole_constructor snapshot -- so the trigger is named beside it, with the honest alternative stated. Three claims pass. Compile shows the 3 pre-existing extdeps/gunbc shell transport errors and nothing in these files. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VQ1HbKBP5SH24N2zxExYmN
The row's architecture is unchanged -- placement outside the admitted roster, no fifth arm, evidence-not-permission framing. These are precision fixes to what it asserts as historical record, each verified in source before writing. 1. "THERE IS NO REVIEW" WAS FALSE, AND FALSE WHERE THE ROW COULD LEAST AFFORD IT, since the ruling-versus-objection distinction IS the row's subject. There WAS a review: the exact-head REQUEST_CHANGES on #10727, GitHub review 5135375330, which is what raised the PublicSurfaceGrowth objection. What never existed is a RULING answering it, so the merge is the only disposition -- and a disposition is not an adjudication. 2. "THIS CHANGE SATISFIED NO TEST" SMUGGLED IN A NEGATIVE ADJUDICATION. Asserting the change FAILED the purpose test is itself a ruling, and this row exists to say no ruling was made. Restated: the purpose test was never adjudicated for this change, so it has no place in a population of adjudicated satisfactions. Same placement conclusion. 3. THE MODAL CLAIM IS RESTORED AND NOW STANDS ON TWO INDEPENDENT LEGS, verified rather than relayed: LEG ONE, the source language cannot ASK -- .dag has no module-private declaration concept; authority is v1.compiler.emit_rust's ceiling note above rust_scalar_checkpoint_reference_base, whose next-rung trigger is "a module-private function boundary in the language". LEG TWO, the emission path cannot ANSWER -- v1.compiler.languages declares VisibilitySpec with ONE prefix per target and no representation of a public/private pair; Rust binds KeywordVisibility { prefix: rust_visibility }, rust_visibility is "pub ", and rust_visibility_prefix takes NO ARGUMENT. The counterexample that looks decisive is recorded and refuted in the row so it is not raised again: emit_cli_root_struct's `public: Bool` is a bespoke local conditional for one hand-written CLI struct, not the declaration path. The 46-mirror census is demoted to observed confirmation of the two legs rather than the proof of them. 4. THE EVIDENCE PARAGRAPH UNDERSTATED THE MATRIX. "What moved is which non-blocking reason a decline reports" omits the direction carrying the repair. The matrix moved BOTH ways: 8425 sites cleared a spurious UndecidableGenericFormal decline outright (diagnostic to none), and the new relation fires at 4989 introduction sites plus 5 peer chains. Reason-to-reason is the third kind, not the only kind. The producer is named so the figures are re-derivable rather than quoted. Verified on this head: projection gate regen clean, and claim_executor --required-floor verdict=FloorClean, 3549/3549 terminal, claims_failed=0, unexpected_failures=0. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MuB8wHjijaFwNoyzm78stF
* Make an existing floor defect permanently executable and named A branded where-refinement value (HostIdentity, `NonEmptyStr where brand(...)`) passed at an ordinary product formal reaches NO refusal at the direct call argument seam. A value of an unrelated constructor is accepted at a declared position -- the DESIGN §4b floor sentence, `values inhabit declared types`. THE MONOTONICITY IS INVERTED, which is what makes it worth its own class. The two refinement chains share no link, so this is the STRONGER incompatibility; pairs whose chains merely MEET are at least reported undecidable and are therefore countable. The unrelated pair matches no arm at all, falls through the whole procedure, and is admitted. The further apart two things are, the more likely they are accepted. THIS IS PRE-EXISTING AND THE ATTRIBUTION IS MEASURED, NOT ASSERTED. The control was authored while investigating review 62172, which attributed the hole to a where-refinement exclusion in the nominal-product arm on gunbc#10727. That attribution does not survive measurement: the same control, over the same fixtures and the same corpus, run against two compilers differing only in the three stage0 infer mirrors, returns false under BOTH main's and the candidate's. Removing the exclusion does not change the result, because the product arm was not making the refusal the exclusion was thought to suppress. The finding's CONCLUSION was right and its CAUSE was not. It therefore lands here rather than in #10727: a review-originated finding moves to its own PR when its discriminating RED reproduces on main WITHOUT the reviewed diff. Discovery provenance stays recorded; implementation ownership follows the demonstrated subject. Self-sufficiency is executed rather than argued -- on this branch, which is main plus these three files, the floor plans and executes 3540 claims with verdict_incomplete=0 and the assertion RUNS and returns false, so it is ExpectAssertionFalse and not a pre-verdict refusal. Three pieces, no production repair: the discriminating control, its ExpectAssertionFalse admission, and the failure-mode row. Measured on this head: verdict=FloorClean, known_red_held 19 -> 20, claims_failed=0. The row's next-rung trigger is stated at CAPABILITY grain -- the judgment REFUSES a produced value whose refinement chain is disjoint from the declared position's. Neither a control existing nor a diagnostic variant being declared retires it; both would be satisfied while the capability stayed dead. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MuB8wHjijaFwNoyzm78stF * chore: regenerate drifted generated artifacts (ci auto-heal) Ledger-Repair-Judged: docs/design-failure-modes.md Ledger-Rows-Repaired: docs/design-failure-modes.md disjoint_chains_admitted_while_meeting_chains_decline Ledger-Repair-Judged: docs/design-rung-drops.md * wip: A/B/C fixes * Fold the specimen onto its existing class; one closure for both fixtures Three blockers from GitHub review 5136486384, all verified against the tree before accepting. A - THE ORACLE WAS UNSOUND, AND THE CONSEQUENCE WAS A FALSE DISSOLUTION TRIGGER. The failing fixture imported product.placement_supply and the partner did not, so the two closure-wide counts were taken over DIFFERENT closures and the shared term did not cancel by construction -- the assertion was false only because two unequal closures happened to contribute equally today. That is not flakiness here: this control is enrolled as an expected RED whose greening is a counted un-quarantine that DELETES its admission row, so any DeclaredTypeNotInhabited appearing anywhere in that closure would have greened it for an unrelated reason and retired the admission while the defect stayed live. Both fixtures now carry identical imports and identical declarations and differ on exactly one token, the actual passed. The rule was already stated seventy lines up in the same file. B - IT IS NOT A NEW CLASS. The mechanism is absence_classifier_default_bucket's own -- an accepted bucket defined by the ABSENCE of a matching arm, the residual catching only shapes whose absence pattern differs -- and decisively the SAME REPAIR closes both: carry the discriminating fact, consume it in an exhaustive match with no default accepting bucket. Two things sharing a mechanism AND a repair are one class, and DESIGN section 2 refuses a fresh authority for a concept that already exists. The differing arity (a relation over a pair rather than a classifier over a subject) is immaterial: the residual-accepts shape does not depend on how many subjects the question ranges over. That row already carries a second specimen in a different authority, filed and not repaired, which is exactly this situation. WHAT IS GENUINELY NEW LANDS AS A RECOGNITION RULE ON THAT ROW, because the inversion is NOT derivable from its existing rule: "a new kind can silently resemble the default" predicts that UNMODELED kinds land in acceptance and says nothing about an ORDERING over severity being inverted. Here the escaping population is not a kind nobody enumerated -- it is present, well defined, known at authoring time, and exactly the EXTREME of the incompatibility order. WHERE THE DOMAIN IS ORDERED BY SEVERITY AND THE ARMS COVER ONLY THE NEAR END, THE ACCEPTED BUCKET IS THE FAR END, SO SAFETY DECREASES MONOTONICALLY WITH INCOMPATIBILITY. C - THE DANGLING CITATION IS GONE with the separate module: nothing here cites a neighbour that is unreachable in the tree it lands in. And the quadruplicated prose from review 62297 is cut to citations: the receipt row is the authority, the admission reason carries the admission-local fact, and the two comment blocks name the row instead of restating it. The one comment that stays is a fact about the FIXTURE construction, which no row owns. Measured on this head: verdict=FloorClean, planned=executed=terminal=3550, known_red_held=20, claims_failed=0, unexpected_failures=0. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MuB8wHjijaFwNoyzm78stF * wip-blockers * chore: regenerate drifted generated artifacts (ci auto-heal) Ledger-Repair-Judged: docs/design-failure-modes.md Ledger-Rows-Repaired: docs/design-failure-modes.md absence_classifier_default_bucket Ledger-Repair-Judged: docs/design-rung-drops.md * Address the three blockers: identical fixtures, causal fold, final-default trigger 1. THE PAIR ORACLE'S TWO FIXTURES WERE NOT BYTE-IDENTICAL. They differed in their module declarations as well as in the argument under test, so the difference the oracle measures was not attributable to the argument alone. Both strings now declare `module probe_disjoint_refinement_at_product`; the ONLY remaining difference is `probe_host_d` vs `probe_box_d` at the call. Re-established on the merged head: required-floor verdict=FloorClean, 3550/3550 terminal, claims_failed=0, known_red_held=20 -- the assertion still RUNS and returns false, so ExpectAssertionFalse still holds. 2. THE FOLD ARGUMENT INVOKED A UNIVERSAL LAW IT DOES NOT HAVE. "Two things that share a mechanism and a repair are one class" is not true in general and was doing the work the cause should do. Deleted. The fold is now grounded on the SHARED CAUSAL PREDICATE: an accepting residual defined by "no explicit arm matched", so a member of the declared subject population that no arm dispositions silently lands in acceptance. Arity changes none of that. A shared repair is evidence ABOUT the cause, never the argument. The severity-inversion recognition rule is likewise softened to a CONDITIONAL CONSEQUENCE and explicitly not a monotonicity theorem: when the enumerated arms cover the near end of a severity order, the far end is what falls through -- a consequence of which cases were written, not a law about incompatibility, and a tell to check for rather than a property to rely on. 3. THE DISSOLUTION TRIGGER WAS BROADER THAN THE DEFECT. "Refuses every pair whose refinement chains are disjoint" would license an OVER-REFUSING repair, since disjointness is not itself evidence of incompatibility. It is re-phrased at the FINAL-DEFAULT grain -- where `Absent => Inhabits` fires -- in both carriers: Inhabits for an identity-established pair requires a NAMED POSITIVE COMPATIBILITY AUTHORITY -- there is no implicit accepting residual -- sufficient for the HostIdentity-at-Box specimen to refuse. Each carrier states why the obvious phrasing is a false trigger, so the grain is not re-lost by the next reader. docs/design-failure-modes.md is the deterministic projection of the edited row plus main's roster catch-up; it was regenerated, not hand-merged. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MuB8wHjijaFwNoyzm78stF * Regenerate the failure-mode projection from the merged authorities The merge left main's copy of this generated artifact; the projection is re-derived from the merged .dag rows rather than spliced. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MuB8wHjijaFwNoyzm78stF * Integrate main 5e24cc9: resolve two append-rosters as unions, verified both ways Two of the three overlapping files are APPEND-ROSTERS, where a take-one-side resolution loses the other lane's row without conflicting, without breaking the parse, and without reddening anything either lane runs. So both are resolved as unions and then verified BY CONTENT IN BOTH DIRECTIONS -- the thing most likely to vanish here is somebody else's entry, and its absence is silent. `dag/gunbc/explicit_witness_admission.dag` -- main deleted the `green_control_sanctioned_reader_body_not_flagged` probe (its dissolution fired) in the same region this branch added its own. Resolved by taking main's deletion AND keeping this branch's probe. VERIFIED: the probe roster is exactly main's plus one entry, `w_a_branded_refinement_at_an_ unrelated_product_formal_still_refuses`, with nothing removed. `src/v2/workflow/floor_expected_red.dag` -- main dropped `floor_expected_red_chunk_22` from the aggregator AND deleted its function, while this branch had added a chunk. Taking either side alone would have either resurrected a call to a function that no longer exists or dropped this branch's chunk. The aggregator is rebuilt from MAIN's chunk list with this branch's chunk inserted. VERIFIED: expected-red identities go 97 -> 98, exactly one addition and no removals, and every chunk function referenced by the aggregator is defined. `docs/design-failure-modes.md` is the generated projection and was regenerated from the merged authorities rather than resolved. `dag/gunbc/recurring_failure_mode/absence_classifier_default_bucket.dag` is NOT in the overlap -- main did not touch the fold target, so the specimen and its recognition rule are uncontested. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MuB8wHjijaFwNoyzm78stF * Integrate main b6f7987: union the admission roster, regenerate the projection Two overlapping files, resolved by kind rather than by text. `dag/gunbc/explicit_witness_admission.dag` -- the same append-roster that nearly broke this PR on the previous integration. Main added several probes in the region this branch added one to. Resolved as main's current state PLUS this branch's entry, and VERIFIED BY SET DIFFERENCE IN BOTH DIRECTIONS rather than by count, because a count passes when one entry is swapped for another: the roster is exactly main's plus `w_a_branded_refinement_at_an_unrelated_product_formal_still_refuses`, with nothing removed. `docs/design-failure-modes.md` is a GENERATED projection and was not text-merged. A three-way merge has no useful granularity there -- a failure-mode row projects as one very long line, so any conflict is a whole-row conflict and the merge can emit bytes no generator would produce. Resolved by taking the side that makes the AUTHORITIES correct and then regenerating. Verified on the merged tree: parse sweep -- 5231 files parse-clean; fixed point -- regenerator run leaves no diff; authored-delta arrival -- every sentence the authority gained is present. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MuB8wHjijaFwNoyzm78stF --------- Co-authored-by: Brian Searls <briansearls1@gmail.com> Co-authored-by: Claude Opus 5 <noreply@anthropic.com> Co-authored-by: gunbc-ci-auto-heal <gunbc-ci-auto-heal@users.noreply.github.com>
…#10826) * Record #10727 as an unadjudicated merged instance, not an admission gunbc#10727 merged on 2026-09-08 as 7fb62e6 carrying five new exported declarations in the emitted v1 seed while PublicSurfaceGrowth remained a dominating refusal in v1_seed_standing and the boundary question was open and escalated. The row states, in its own words, that the merge settles the disposition of that exact change and NOT the semantics of the PublicSurfaceGrowth class. Three placement constraints hold deliberately: - it is NOT under "what this standing has admitted so far", because that roster records changes that SATISFIED the purpose test; - it mints NO fifth MaintenanceAdmissionInstance arm, which would be the self-authored boundary change this standing warns against; - it is NOT filed only elsewhere, because the dangerous future inference happens while somebody is reading THIS authority. docs/design-failure-modes.md carries the deterministic projection of main's decision_surface_truncation row; it is catch-up, not new content. Verified on this head: docs projection gate regen clean, and claim_executor --required-floor verdict=FloorClean, 3549/3549 terminal, claims_failed=0. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MuB8wHjijaFwNoyzm78stF * Correct four historical claims in the unadjudicated-merged-instance row The row's architecture is unchanged -- placement outside the admitted roster, no fifth arm, evidence-not-permission framing. These are precision fixes to what it asserts as historical record, each verified in source before writing. 1. "THERE IS NO REVIEW" WAS FALSE, AND FALSE WHERE THE ROW COULD LEAST AFFORD IT, since the ruling-versus-objection distinction IS the row's subject. There WAS a review: the exact-head REQUEST_CHANGES on #10727, GitHub review 5135375330, which is what raised the PublicSurfaceGrowth objection. What never existed is a RULING answering it, so the merge is the only disposition -- and a disposition is not an adjudication. 2. "THIS CHANGE SATISFIED NO TEST" SMUGGLED IN A NEGATIVE ADJUDICATION. Asserting the change FAILED the purpose test is itself a ruling, and this row exists to say no ruling was made. Restated: the purpose test was never adjudicated for this change, so it has no place in a population of adjudicated satisfactions. Same placement conclusion. 3. THE MODAL CLAIM IS RESTORED AND NOW STANDS ON TWO INDEPENDENT LEGS, verified rather than relayed: LEG ONE, the source language cannot ASK -- .dag has no module-private declaration concept; authority is v1.compiler.emit_rust's ceiling note above rust_scalar_checkpoint_reference_base, whose next-rung trigger is "a module-private function boundary in the language". LEG TWO, the emission path cannot ANSWER -- v1.compiler.languages declares VisibilitySpec with ONE prefix per target and no representation of a public/private pair; Rust binds KeywordVisibility { prefix: rust_visibility }, rust_visibility is "pub ", and rust_visibility_prefix takes NO ARGUMENT. The counterexample that looks decisive is recorded and refuted in the row so it is not raised again: emit_cli_root_struct's `public: Bool` is a bespoke local conditional for one hand-written CLI struct, not the declaration path. The 46-mirror census is demoted to observed confirmation of the two legs rather than the proof of them. 4. THE EVIDENCE PARAGRAPH UNDERSTATED THE MATRIX. "What moved is which non-blocking reason a decline reports" omits the direction carrying the repair. The matrix moved BOTH ways: 8425 sites cleared a spurious UndecidableGenericFormal decline outright (diagnostic to none), and the new relation fires at 4989 introduction sites plus 5 peer chains. Reason-to-reason is the third kind, not the only kind. The producer is named so the figures are re-derivable rather than quoted. Verified on this head: projection gate regen clean, and claim_executor --required-floor verdict=FloorClean, 3549/3549 terminal, claims_failed=0, unexpected_failures=0. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MuB8wHjijaFwNoyzm78stF * Move the disposition into a typed carrier and name the instruments Review 62399 raised two findings on the String note row; both are correct against the authority docs and both are fixed rather than argued. FINDING 1 -- DANGLING DECLARATION (§3c) PLUS UNCLASSIFIED PROSE (§4c). Verified: `git grep` returned exactly one hit for the note row, its own definition, and the four sibling `*_note: String` rows in this file are equally unread. §4c is explicit that a ruling, citation, status or count belongs in a typed carrier and that a String declaration whose sole purpose is commentary is misplaced data -- and this row was precisely a ruling-status record plus a citation plus a census. Prevalence is debt, not precedent; a fifth would have been a third one added by me. The row is now `UnadjudicatedDisposition = MergedWhileObjectionStood`, carrying the change, the merge SHA, the objecting review and the refusal class at issue as FIELDS. It is a separate type and NOT an arm of `MaintenanceAdmissionInstance`, for the reason the previous draft already argued and which the reviewer agreed with: that vocabulary records shapes the purpose test has been satisfied BY, and an unadjudicated merge satisfied nothing and failed nothing, so an arm there would mint the ruling that was asked for and not given. §3c is ANSWERED rather than left dangling, and answered honestly: the consuming route is the one that consumes `v1_seed_standing` itself -- DESIGN states this authority's rung plainly, "the vocabulary is consumed by review diligence, not by any gate" -- so a row here reaches its consumer when a reviewer classifies a proposed v1 change against this standing. That is the declared route, not a claim that a fold reads it. The irreducible rationale moves to a module-scope `//` annotation, which is §4c's own quarantine channel for prose and the shape this file already uses for its operator rulings. FINDING 2 -- TRANSCRIBED MEASUREMENT (§6). The census was a copied count. It is now stated as a PREDICATE anyone can re-run -- over the generated mirrors, count `pub fn` against bare `fn` and `pub` types against bare -- with the reading marked as a DATED OBSERVATION on main after 7fb62e6 and explicitly not the proof of the claim. Its scope limit survives re-derivation: the same predicate over `src/v1/stage0/src` as a whole is FALSE. The behavioural matrix likewise now states what it ESTABLISHED -- refusal set identical by identity, everything else non-blocking and moving in both directions -- and names the instrument instead of leaning on its counts. Verified: projection gate regen clean on this head. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MuB8wHjijaFwNoyzm78stF * Consume CommitSha, carry the full 40-hex head, and fix the parse break REVIEW 62407 -- the disposition row declined to consume an authority that already exists. `std.types` declares `CommitSha`, named by every Git-head consumer in the tree, and `merged_as: String` was a second name for that one concept (DESIGN §3) on the one declaration in this file that is structured rather than prose. It is now `merged_as: CommitSha`, with the import. The value is also widened from the abbreviated `7fb62e6e5e7` to the full 40-hex head. `CommitSha` is presently an unvalidated alias, but `std.types` homes a located syntax wall beside it -- lowercase 40-hex for an externally supplied Git head -- so an abbreviated value would have been written to be refused the day that wall becomes a constructor. The two remaining `String` leaves are left as they are, deliberately and for the reason the review itself gives: `change` is a repo-plus-number and `objecting_review` is a review reference, and no carrier for either shape exists in `dag/` yet, so minting one here would be the §2 re-invention the `CommitSha` fix exists to avoid. `CommitSha` is different -- it exists. THE PARSE BREAK, and it is why the required floor refused on 828d3ea. In `.dag` a DATA expression may not begin on the line after `=`; the constructor now sits on the equals line, matching `v1_seed_standing` in this same file. The six `type X =` declarations that break to the next line are NOT the same shape and are correct as written. WHAT THIS COST AND WHAT I TAKE FROM IT: my local `--required-floor` run returned FloorClean on the broken form, so the local instrument did not discriminate on this class while CI's did. "The edit is in the file" and "the file parses" are two different facts, and only the second is established by running the parser that CI runs. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MuB8wHjijaFwNoyzm78stF * One fact, one spelling: make both SHA references in the authority full DESIGN §3 -- the row carried the merge head twice, forty lines apart, once as the full 40-hex `merged_as` value and once abbreviated inside the producers annotation. One fact with two spellings inside a single authority. Both are now the full form. Also carries the merge of origin/main 5e24cc9; the authority subject is byte-preserved across it (identical blob). VERIFIED WITH THE INSTRUMENT THAT ACTUALLY REACHES THIS SUBJECT: `claim_executor --required-ci --source-root dag --source-root src/v2 --required-lane witnesses` -- exit 0, zero adjudication BLOCKING. The `--required-floor` reading this PR previously cited is VACUOUS here for the reason this branch's sibling specimen establishes: nothing imports `gunbc.v1_maintenance_standing`, so the floor never parses it and its green was never about this file. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MuB8wHjijaFwNoyzm78stF --------- Co-authored-by: Brian Searls <briansearls1@gmail.com> Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
…#10674) * Compose the micro-VM launch plan, and mint the credential last Four stages have to hold before a guest may start: the fabric cell must be owned, the VM config must resolve, the admission must pass, and the App credential must carry organization self-hosted-runners write. This composes them in that order and returns one typed plan. The order is load-bearing rather than stylistic. Every stage but the last is pure and free to refuse; a JIT mint is not, because it creates a runner identity at GitHub. A refusal after a mint leaves that identity registered against the organization for a VM that will never start, nothing on the host knows to delete it, and orphaned registrations are reconcilable only by name. So the mint runs last, and its authorized value is reachable only from inside LaunchAdmitted -- a caller cannot hold a mint authorization for a launch that was refused. Every admission arm is matched where the launch rules on it rather than through a helper over RunnerMicroVmAdmission. Such a helper needs an arm for the admitted case that cannot occur there, and the only thing to put in it is a fabricated refusal, in the one place nothing can observe it. plan_microvm_launch_of_binding carries the decisions and takes a binding a witness can build; plan_microvm_launch is the five-line projection over the sealed RequiredBuildCellSnapshot. Putting the ordering behind the seal would have made it unwitnessable, which is how the previous PR ended up with an annotation claiming coverage that did not exist. Refusal arms stay separate rather than collapsing into one cause string: the cell belongs to fabric_required_build_cell, the size to runner_slot_allocation, the topology to runner_unit, the credential to auth.github_credential, and collapsing them sends an operator to the wrong module. Credential fixtures are imported from test.claim.github_app_registry rather than rebuilt, so there is one answer to what an installation holds. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VQ1HbKBP5SH24N2zxExYmN * temp: diagnostic arm probes for the launch fixture Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VQ1HbKBP5SH24N2zxExYmN * Take the VMM reserve as an input, and state that production admits no guest The launch plan read gunbc_runner_microvm_realization_reserve() inside the composition, which made the admitted arm unreachable from any caller. Production's reserve is ReserveUnmodeled -- deliberately, because no VMM resident footprint is a cited quantity in extdeps.virtualization.firecracker and inventing one would be a fabricated plausible output -- so every launch refuses at MemoryFitUnresolved and the mint stage below it was dead code no input could reach. The lane caught this: the positive control asserted LaunchAdmitted, a state no input can produce. That is the same decoration class this branch documented for members_owned, this time written into a test rather than a production path, and it would have shipped as coverage for an ordering nothing could exercise. The reserve is now a parameter. The fit judgment is decided by the caller that owns the quantity, the ordering below becomes reachable and therefore witnessable, and production keeps refusing at the entry point rather than by accident inside a composition. Adds the control that says what is true today: with production's own reserve, every launch refuses, so NO CI JOB RUNS IN A MICRO-VM until that quantity is cited or measured and declared. It goes red the day the reserve becomes declared, which is when the launch path becomes live and the claim has to be re-read. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VQ1HbKBP5SH24N2zxExYmN * Observe whether a runner host can measure a VMM's footprint The micro-VM path admits no guest because the realization reserve is ReserveUnmodeled: no VMM resident footprint is a cited quantity, and inventing one would be a fabricated plausible output. The quantity has to be OBSERVED on a host before it can be declared, so which instrument exists is a host readiness fact in the same way /dev/kvm is -- a host that boots a guest but cannot report what the VMM cost cannot retire the reserve. Reports GNU time, cgroup v2 memory.peak, and the systemd version line, in the receipt the existing microvm_host_converge mode already prints. No new module and no new workflow job: this had to have a consumer the moment it existed. The instruments are named rather than ranked and nothing here picks one. Each reports the same quantity exactly, and which is available is a property of the host's systemd and kernel, so selecting among them belongs with the measurement. The systemd version is read rather than inferred because MemoryPeak is version-gated at v254. Sampling /proc/<pid>/status VmHWM is deliberately not among them: it needs the process alive, so a guest that powers itself off can be missed, and a missed sample under-reports -- the wrong direction for a value that becomes a safety reserve. Readiness deliberately does not move with the instruments, with a control for it: wiring them into firecracker_host_ready would have taken every host not-ready the moment this field landed, to answer a measurement question. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VQ1HbKBP5SH24N2zxExYmN * Import systemctl_version_command, which no local gate had checked The fleet-converge dispatch died on srv2 with NoSuchFunction while the whole witnesses lane had reported RC=0 and FloorClean over the same tree. Two reasons stack and either alone is enough. runner_microvm_host_ready sits declined_outside_gate_closure, so no CI check compiles it. And claims evaluate only the functions they call, so the witnesses added beside this observer cover its pure functions while the func body is never evaluated -- wet bodies run only on a host. A green floor is not evidence that a wet function resolves. gunbc compile --entry DOES catch it, confirmed by running the red arm with the mutation verified applied first. So the gate existed and was not run; it is now run before any dispatch that executes a .dag entry on a host. Worth recording that this is the opposite blindness from the indented-annotation refusal earlier on this branch, where the lane's parse phase caught what compile --entry missed. Neither instrument dominates; a touched module needs both. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VQ1HbKBP5SH24N2zxExYmN * Model the VMM footprint probe, sampled across guest sizes The micro-VM path refuses every launch because the realization reserve is ReserveUnmodeled. This is the observation that could retire it, and it produces a RELATIONSHIP OVER SIZES rather than a constant, because the model's own reasoning is that the overhead is not constant. Sampling several sizes is not thoroughness, it is the whole experiment. Guest RAM is lazily faulted, so a page joins the VMM's resident set only when the guest touches it, and a guest that boots and powers off touches a small and roughly size-independent amount. One reading at one size therefore measures the workload, not the size. A peak that stays flat as declared size rises says the declared size is not committed; a peak that tracks it says it is. Those are different worlds for an admission rule and one sample cannot tell them apart. Stated in the module and not only here: this does NOT establish a worst case. That is what a guest costs when it USES its declared memory, which needs a guest workload that touches all of it. Reporting these numbers as if they bounded that case would be the fabricated plausible output the reserve refusal exists to prevent, arriving by way of a measurement instead of a guess. The instruments were chosen on evidence rather than assumption: the srv2 receipt reports gnu_time present, systemd 255 so MemoryPeak is available, and cgroup memory.peak absent. GNU time's %M is a kernel-maintained high-water mark, which is why it was preferred over sampling VmHWM -- sampling needs the process alive and under-reports a subject that exits on its own, the wrong direction for a reserve. The span check carries its own negative arm as a fixture rather than deferring to a mutation run: the collapses it exists to catch -- one size, two adjacent, three clustered -- are asserted to fail on every run. The third is the one reasoning alone would miss, since count >= 3 passes it and only the span requirement does not. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VQ1HbKBP5SH24N2zxExYmN * Convert mebibytes to bytes explicitly; pin the sizes in absolutes The declared sample sizes were 512..4096 BYTES, not mebibytes. Assigning mebibyte(count: N) into a ByteSize field is accepted and does not convert -- the magnitude rides across unchanged -- and std.measure mebibyte_to_byte_size is the conversion that was available and unused. The type system stopped the READ and permitted the WRITE: mebibyte_count on a widened value is a hard error, so the obvious repair is to switch to byte_size_count, which compiles clean and relabels the same number as bytes. That repair is what I made earlier on this branch and reported as a units win. It was not one. WHY NOTHING ELSE CAUGHT IT, which is the part worth fixing rather than the typo. The span control asserts largest >= smallest * 4 over the same list. A uniform unit error preserves every ratio, so it passed identically on bytes and on mebibytes; only the absolute assertion in the render test went red, and that one covers the witness's own fixtures rather than the list production samples. So this also pins the production sizes in absolute bytes. The literals are the specification -- these are the sizes the probe declares it will sample -- so the control is a controlled fixture rather than a measurement copied out of the tree. General rule, recorded because it generalises past this file: when a suite tests a RELATIONSHIP -- ratio, ordering, spread, delta -- at least one control must pin an ABSOLUTE, or a scale error is invisible to the whole suite. Also enrolls the empty-receipt control permanently rather than as a one-off, per 4b(4): footprint_receipt_is_complete folds with init: true, so an empty outcome list is vacuously complete and only the count guard beside it refuses. That guard is load-bearing and had no control. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VQ1HbKBP5SH24N2zxExYmN * Convert the one mebibyte site a digits-only regex could not match The previous commit claimed to fix the unit bug and did not. It converted with a pattern requiring digits, so every literal call site was repaired while mebibyte(count: mib) in the sampled() helper -- an identifier, not a literal -- was silently skipped. That helper is exactly what the failing render claim drives, so the fix looked complete, compiled clean, and left the same claim red. A mechanical edit that matches most of its population and misses the rest is indistinguishable from a complete one at the diff, and the widening it failed to remove is legal, so nothing local objected. What found it was running the lane before pushing rather than reporting the fix from the edit. So the check is now over the POPULATION rather than the pattern: every mebibyte( in both files is wrapped in mebibyte_to_byte_size, verified by a grep whose expected output is empty, not by trusting the substitution's own reach. The absolute control added in the previous commit is what isolated this. It passed while the render claim failed, which said conversion works and the fault is in the render path -- without it the two hypotheses were indistinguishable. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VQ1HbKBP5SH24N2zxExYmN * Re-read the launch control now that the reserve is declared production_refuses_every_launch_while_the_vmm_reserve_is_unmodeled asserted that every launch refused at MemoryFitUnresolved, and it said in its own text that it would turn RED the day the reserve became declared -- at which point the launch path becomes live and the control has to be re-read rather than silently starting to pass a guest through. #10657 was that day. It set the allowance aside and made the reserve ReserveDeclared, so production now reaches an admitted guest and the assertion is false. The control was not broken; it fired. This is the re-reading it demanded. The red was only visible on the PR merge ref: my branch alone still has the unmodeled reserve, so both local instruments greened while CI -- which builds branch-merged-with-main -- refused. Running an instrument against a tree that lacks the change under test measures the wrong subject no matter how correct the instrument is, and a green from it looks exactly like a green from the right one. What replaces the row is the same question asked of the live path, in three parts. The admission control pins that the guest admitted is the DECLARED size rather than any default. The refusal control keeps that from being satisfied by a fit judgment that answers yes to everything: a reserve larger than the cell must admit no guest of any size. And the original discriminating RED stays enrolled with an explicitly unmodeled reserve -- 4b(4) keeps a class's red executing after its wall lands, and an unresolved fit is exactly what the launch path must never pass a guest through, whoever the caller is. The arithmetic is deliberately NOT re-derived in the witness. guest_memory_fits already carries the comparison, and recomputing it here would be a second representation of one constraint, satisfiable by editing the test while the production path still lied. Both new controls were verified by mutation rather than by reading: shrinking the oversized reserve and expecting a size production does not declare turn each of them red, while the untouched third stays green -- without that last one a run that failed everything would have looked like proof. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VQ1HbKBP5SH24N2zxExYmN * Require the execution grant, so the micro-VM is a realization under the fabric runner_microvm_launch_plan composed a launch straight from a cell binding and started a VMM. gunbc.fabric_ci_program FCI-3 owns that question -- what causes the target process -- and its exit predicate is that a persistent executor re-reads the committed Grant before starting, so an absent, corrupt or expired Grant yields ZERO target processes. FCI-1's non-goals name Work-process-start and ExecutionGrant explicitly; FCI-2's name process-start. The module therefore did FCI-3's job while skipping FCI-2, which is the second execution system ROADMAP forbids when it says the fabric is four consumers of one contract. That is also why nothing consumed the module. The correct consumer is the FCI-3 executor, and it could not consume a launch whose signature omitted the grant -- so writing any caller would have cemented the bypass rather than closing it. The grant is now a REQUIRED PARAMETER rather than something this module looks up. The absent case has no constructor, so it is unwritable rather than validated; the fence check against the issuing account covers corrupt and expired, because a grant whose lease generation no longer admits is a superseded authorization. Authorization is the first stage: nothing starts without it whatever the cell's state, and a control pins that ordering by presenting an unowned cell and requiring the grant refusal to still win. THE CONTROLS CAUGHT A BUG THAT PASSED THEM. issue_grant returns the account carrying the recorded lease, and the first version fenced against the PRE-issuance account -- so every launch refused, and both grant controls passed against a mechanism that was refusing everything. Only the seven positive claims failing revealed it. The grant and its committed account are now one IssuedAuthorization value, because splitting them is what made the error writable. Verified by mutation in PRODUCTION rather than in the test: neutering the fence check turns both grant controls red and leaves the other seven green. No claim asserts the absent-grant case, because its RED is unauthorable once the parameter is required -- that check would be a 4b decoration, permanently green and cited as coverage. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VQ1HbKBP5SH24N2zxExYmN * Move the VMM footprint measurement to its own branch The host-readiness observation and the footprint probe answer a different question than the launch plan does, and they stand alone: no reference to the launch plan, the execution grant or the mint. They are now PR #10806 off main. This branch keeps one subject -- what causes a micro-VM to start, and under whose authorization -- so its title is the one squash-merge writes into main's history. The extdeps rows move with the code that needs them: gnu_time_max_rss_command and systemctl_version_command are admitted for the probe, so they belong to the probe's branch and are reverted here rather than left admitting callers that no longer exist on this branch. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VQ1HbKBP5SH24N2zxExYmN * Join the grant to the reservation it authorizes, and split planning from authorization Requiring an ExecutionGrant parameter closed the absent-grant case and nothing else. The grant and the cell binding arrived as independent arguments and the only thing read off the grant was its money reservation, so a grant issued for execution A composed with the reserved cell for execution B was ADMITTED whenever the supplied account fenced A. That is worse than no grant: its presence reads as authorization while authorizing nothing about the subject. Found in review; the PR text claiming this bound the launch under the fabric contract overclaimed. MicroVmExecutionBinding is sole_constructor and reachable only through seal_execution_binding, which refuses unless Work, Demand and Offer all agree between the grant and the reservation. The mismatched pair now has no constructor rather than being caught by a check inside the planner. THE RULE IS FACTORED OUT OF THE SEAL SO ITS RED IS AUTHORABLE. RequiredBuildCellSnapshot is sole_constructor precisely so a caller cannot manufacture a reservation, which also means no witness outside its module can build a DISAGREEING one -- a control aimed at the seal could never construct the input it exists to catch, so it would be permanently green and cited as coverage. Exposing a snapshot builder for tests would punch the hole the seal exists to close, the tradeoff runner_microvm_attempt's own witness already records. So grant_authorizes_reservation takes the coordinates, where a disagreeing pair IS constructible, and the seal reads them off the snapshot and delegates to it. PLANNING NO LONGER PRETENDS TO BE AUTHORIZATION. plan_microvm_launch_of_binding takes no grant at all, because cell ownership, VM config, admission and the mint are not authorization questions and a signature that accepted a grant while ignoring its subject is what created the opening. Production reaches it only through the sealed value, so the gate cannot be routed around; witnesses drive each level where that level's red is writable. WHAT THIS STILL DOES NOT ESTABLISH, recorded because the type name would read as more. ExecutionGrant is an ordinary constructible record, so this proves AGREEMENT between a presented grant and a presented reservation, not that fabric issuance durably committed either. The unforgeable form is a persistent executor re-reading the committed grant from durable storage after the submitter terminates, which is FCI-3's own exit predicate and the next slice. Ten claims pass. The substitution control was verified by removing ONLY the Work join in production: it reds alone while the fence and positive controls stay green, so it is bound to this defect rather than to a rule that refuses broadly. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VQ1HbKBP5SH24N2zxExYmN * Reduce to the authorization rule, and file what the two homes cost The launch plan half is deleted. Not because it was unconsumed -- because it was REDUNDANT. runner_microvm_witness_test already carries a_reserve_larger_than_the_whole_envelope_admits_no_guest, the_production_reserve_is_the_declared_allowance, the_production_reserve_lets_the_declared_configuration_reach_admission_under_authored_topology, orphanable_unit_refuses_the_microvm_migration, shared_slot_directory_credential_is_refused and the whole guest-memory boundary family; cell ownership is in runner_microvm_attempt_witness_test and the credential join in the jit mint witness. Deleting the seven planning claims removes NO coverage. I re-derived them because this branch predated #10657 and I never read the neighbouring witness, which is the same root as a roster edit on this branch that would have dropped ten rows other lanes had landed. What survives is the part nothing else answers. Grant and cell binding arrived as independent arguments with only the money reservation read, so a grant issued for execution A authorized execution B whenever the account fenced A -- the grant's presence read as authorization while authorizing nothing about the subject. grant_authorizes_reservation now requires Work, Demand and Offer to agree, MicroVmExecutionBinding is sole_constructor so the mismatched pair has no constructor, and three controls carry it. The module is renamed for what it now contains. A module called launch_plan holding no launch plan is a name promising something it does not carry. ON THE SEAL, because the question was whether it is a 4b decoration. SoleConstructorViolation for cross-module construction is already proven by sole_constructor_violation_outside_module over a synthetic two-module pair, so the red IS authorable and the seal is not a decoration. A type-specific fixture would re-prove the general wall in a second home, which is the same redundancy this commit deletes. Stated honestly: that evidence runs under cargo test -p v1-compiler --lib, which no CI step executes -- the declared drop rust_unit_tests_off_the_merge_path -- so the wall is compile-enforced but the evidence one would cite for it is not on the merge path. This refusal and #10733's do not duplicate. jitconfig-bytes=0 is about CREDENTIAL MATERIAL; this is about AUTHORIZATION SUBJECT, and it orders strictly earlier because it refuses before any drive is built. They compose. Verified after the reduction rather than before: three claims pass, and removing ONLY the Work join in the renamed module reds the substitution control alone while the fence and positive controls stay green. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VQ1HbKBP5SH24N2zxExYmN * Record the count-first merge check on the two-homes row The near-misses this branch produced belong with the class they are nearest to: an append-only carrier losing a row renders as reordering, so the diff is blind and only the count against the base raises it. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VQ1HbKBP5SH24N2zxExYmN * Restore two dark roster rows, and cut the launch-plan residue REVIEW FINDING 1, CONFIRMED AND IT IS THIS PR'S OWN CLASS. Roster was 175 on main and 174 here: added production_actuation... and silently dropped decision_surface_truncation (#10712) and lookup_miss_read_as_positive_classification (#10727), both landed after the branch point and both with their module files still present. The triple-dot diff renders it as reorder-plus-two and shows nothing. Found by the count comparison the PR's own row prescribes, named by an identity join, and repaired by merging main so the roster is 176 with an empty join in both directions -- imports and list body checked separately, since either half can drop alone. REVIEW FINDING 2, CONFIRMED. The reduction left the launch graph's imports (32 lines -> 8 in the module, 37 -> 18 in the witness) and an annotation asserting mint-last/LaunchAdmitted ordering that no longer has a mint or a launch to range over -- a 4c annotation no Accepted program reads, so a false one never reds and it drifts to flatter the change. Annotation replaced with what the module now does, including why the ordering argument is NOT restated here. Orphan fixtures deleted (launch_org, launch_net, launch_binding, witness_reserve, both lifecycle intents, launch_test_slot); witness_reserve's own note was stale too, still saying production refuses every launch. Also declares the seal a 3c frontier rather than leaving it implicit: nothing consumes it, and no witness CAN -- its input is a sole_constructor snapshot -- so the trigger is named beside it, with the honest alternative stated. Three claims pass. Compile shows the 3 pre-existing extdeps/gunbc shell transport errors and nothing in these files. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VQ1HbKBP5SH24N2zxExYmN * chore: regenerate drifted generated artifacts (ci auto-heal) Ledger-Repair-Judged: docs/design-failure-modes.md Ledger-Rows-Repaired: docs/design-failure-modes.md production_actuation_unmodeled_beside_a_modeled_probe Ledger-Rows-Repaired: docs/design-failure-modes.md decision_surface_truncation Ledger-Repair-Judged: docs/design-rung-drops.md * Give the Demand and Offer joins their own discriminating REDs Review 62349 is right and it is the sharpest catch on this branch. The rule has FOUR refusal arms -- fence, Work, Demand, Offer -- and the witness enrolled REDs for TWO. A rule that dropped the Demand and Offer checks entirely would have greened every claim in the file, so the join was reported at a rung its executed evidence does not establish. That is rung inflation in the module built to stop a substitution. Two controls added, same shape as the Work case. Arm-to-assertion count is now 4 and 4. MUTATION-PROVEN INDIVIDUALLY, because five passing claims do not establish discrimination -- a control that fails alongside others is not a discriminating RED: remove ONLY the Demand arm -> ONLY a_grant_for_another_demand reds remove ONLY the Offer arm -> ONLY a_grant_for_another_offer reds Both times the other four stay green. Also records in the witness WHY these REDs are authorable when the seal's is not, since the questions sound identical and get opposite answers: the seal's input is a sole_constructor snapshot, so a disagreeing one has no constructor a fixture can reach and a control there would be a 4b decoration; FabricIdentity is an ordinary record, so a disagreeing demand or offer is writable right here. The constructor decides it, not how important the check feels. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VQ1HbKBP5SH24N2zxExYmN * Delete the unconsumed seal, and restore two more dark roster rows Review 62362, both findings correct. THE SEAL IS DELETED, WHICH IS WHAT MY OWN ANNOTATION SAID TO DO. I had declared it a 3c frontier, but 3c admits one only with a NAMED LATER CHANGE, and mine was FCI-3 -- which the displacement analysis shows is blocked behind a prerequisite nobody has started. The review's phrasing is right: a gate id plus a missing plan and an unpriceable prerequisite is not a named later change. And the annotation cited docs/plans/microvm-launch-displacement-analysis.md, WHICH DOES NOT EXIST ON THIS BRANCH -- it is on the unmerged #10823. So the justification for keeping unconsumed code rested on an artifact that does not resolve, which is the citation defect this session has now made three times across unmerged branch boundaries. This also DISCHARGES rather than overrides the earlier ruling to keep the seal: that keep was conditional on a fixture being able to author the forbidden state and be refused. No witness can call it at all -- its input is a sole_constructor snapshot -- so the condition failed. Module is 79 lines: one rule, four refusal arms, five claims. All five still pass with the seal gone, which is the evidence that they were driving the RULE and never the seal. ROSTER WAS BEHIND MAIN AGAIN, 176 vs 177, missing guarded_filter_branch_dropped_by_emitter and match_variant_lookup_does_not_peel_type_alias. Merged: 178 imports and 178 list entries, empty join against main in both directions. Second time on this branch -- the ledger surface moves faster than an edit-review cycle, and only the count comparison catches it. The projection came back UU with NO conflict markers, the merge driver refusing as designed. That artifact is HealRegeneratesAfterProvisionalMerge, so the provisional state is staged for heal rather than hand-written. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VQ1HbKBP5SH24N2zxExYmN * chore: regenerate drifted generated artifacts (ci auto-heal) Ledger-Repair-Judged: docs/design-failure-modes.md Ledger-Rows-Repaired: docs/design-failure-modes.md guarded_filter_branch_dropped_by_emitter Ledger-Rows-Repaired: docs/design-failure-modes.md match_variant_lookup_does_not_peel_type_alias Ledger-Repair-Judged: docs/design-rung-drops.md * Declare the frontier for the rule this module keeps, and record a false annotation as false Review 62386, both findings correct. THE ANNOTATION WAS FALSE, NOT MERELY STALE. It said the rule was "factored out of the seal above it" and that "the seal delegates to it". The seal was deleted last commit, and bind_microvm_to_cell takes only a snapshot and never sees an ExecutionGrant -- so the sentence asserted a relationship between two declarations that does not exist in the corpus. Recorded as false rather than quietly removed: a 4c annotation is read by no Accepted program, so a false one never reds, and the next reader deserves to know the claim was wrong rather than find a tidy comment. THE FRONTIER WAS DECLARED FOR WHAT I DELETED AND NOT FOR WHAT I KEPT, which is the review's actual point and it is exact. 3c's two admissible states differ by whether the consumer and trigger are stated BESIDE the declaration; for grant_authorizes_reservation they were not, so the rule was dangling rather than declared. Now stated: named consumer is the FCI-3 executor whose exit predicate this rule is the agreement half of, trigger is that executor existing, and the route today is that nothing in the runner path calls it. STATED WITH ITS COST RATHER THAN AS A REASSURANCE: that trigger is not near, because the actuation FCI-3 must displace is authored in no repository. A reader weighing whether the module earned its place should have that beside what it does carry -- four typed refusal arms, each mutation-proven to red ALONE. Correctness established by execution; only production consumption deferred. AND A TENSION IN MY OWN REASONING, NAMED RATHER THAN LEFT FOR A REVIEWER: I deleted the seal partly on "the trigger is not near", and I now declare a frontier for the rule on that same trigger. 3c requires a trigger to be STATED, not to be near, so "not near" was over-applied there. The seal still went for the right reason -- no fixture could construct its sole_constructor input, so it had ZERO executed evidence. The seal failed consumption AND evidence; the rule fails only consumption. 5 claims pass. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VQ1HbKBP5SH24N2zxExYmN * Home the grant-authorization join beside the issuer, with typed refusal variants Review 62520 raised two findings and both were real. The refusal type collapsed four named causes into AuthorizationDisagrees { cause: String } while the annotation directly above it asserted "THE REFUSAL ARMS ARE NOT COLLAPSED INTO ONE `cause` STRING". The four arms were four if-branches producing four prose strings. That is DESIGN section 2's anemic String leaf, and because a section 4c annotation is read by no Accepted program, the false claim could never red. The type is now a sum matching the sibling ReceiptAdmission: GrantAuthorizes | GrantFenceRefused | GrantAuthorizesDifferentWork | GrantAnswersDifferentDemand | GrantIssuedAgainstDifferentOffer. The five controls match a named variant instead of string_contains, which strengthens them -- a refusal naming the wrong authority could still contain the right words. The home finding was also right, but its prescribed destination does not compile. product.fabric.budget already imports product.fabric.execution, so homing a rule that needs MoneyAccount and fence_admits into execution.dag creates an import cycle -- the one structural law section 4 states. The rule is homed in product.fabric.grant_issue instead, which already imports execution, budget, identity and work, and which issues the grant: the agreement join is the direct counterpart of issue_grant, at FCI-2's grain. gunbc.runner_microvm_execution_authorization is deleted and the witness moved beside the issuance fixture it already imported. The section 3c frontier is re-declared at the new home: the rule still has no production consumer, its named consumer is the FCI-3 executor, and the trigger is stated. Executed evidence: five claims pass under claim_batch --claim-run against dag/test/claim/fabric/fabric_grant_authorization_witness_test.dag. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VQ1HbKBP5SH24N2zxExYmN * Scope the append-only-carrier corollary to carriers that are still a list #10822 deleted the hand-appended failure-mode roster: membership is the row DIRECTORY now, derived before compile. This row's corollary prescribed a count comparison against the base for append-only carriers and cited the roster at 163 against 173 as a live countable specimen. Half that citation is a superseded claim, and it had not landed yet -- so this corrects the row before it ever becomes an authority rather than editing a published one. I had declined this edit earlier on the grounds that the row was a live authority and that changing prose inside a PR waiting on an unrelated gate is how a superseded claim survives review. The reasoning was sound and its PREMISE WAS FALSE: the row is absent from origin/main and exists only on this branch. There is no landed authority to preserve. The corrected text is taken verbatim from #10853 (deep-crane-107, 9a2358a), which added the identical file and differed only in this one receipt string; that PR closes rather than landing a second copy for this one to later drop. The file is now byte-identical to its version, so one row has one authority. What the text now does: scopes the rule to append-only carriers that are STILL A LIST, keeps extdeps.exec.command admit_callers at 67-vs-68 as the surviving specimen, marks the roster SUPERSEDED FOR ONE CARRIER ONLY citing #10822, and keeps the closing instruction not to invent a replacement count for the dissolved carrier -- directory membership is a different subject. The correction is self-applying: refreshing that citation as though the roster were still countable would itself be a_live_authority_name_carries_a_superseded_claim. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VQ1HbKBP5SH24N2zxExYmN * Fence the money reservation with the money generation, not the lease's Review 62587 found two real defects and the second explains why the first survived. THE RULE JOINED TWO INDEPENDENTLY ADVANCING COUNTERS. fence_admits compares the generation the ACCOUNT holds for a reference, and product.fabric.budget reserve_money records that from the MONEY request (`generation: request.generation`). The rule presented grant_lease_generation, which answers the RESOURCE lease's counter. So a grant authorized whenever the two happened to coincide, and a legitimate grant whose counters had diverged was refused as GrantFenceRefused -- a cause naming the wrong fact. product.fabric.execution already records this class one authority over: comparing generations alone was a real fencing hole, and it made prose describe a wall the code did not build. This was that class re-introduced against a different generation authority. ExecutionGrant carries NO money generation -- money_reservation is a bare ReservationRef -- so there was no correct value to read off the grant. It is now a presented coordinate, which is what the rest of this rule already does and what makes its REDs authorable. The witness presents money_request.generation, what the ISSUER asked for, against what the account RECORDED: two independently authored facts. Reading it back off the account would compare a value to itself and green by construction, which is the 4b decoration this rule exists to avoid. THE POSITIVE CONTROL WAS GREEN BY FIXTURE COINCIDENCE. money_request.generation is 9 and fixture_lease.generation is 9, so the old control passed while proving only that two unrelated counters agreed. The fixtures are left at 9 and the discrimination is carried by a new claim that presents the RIGHT account against a WRONG generation -- the arm the coincidence was hiding, since the existing exhausted_account claim refuses because the account holds no lease for the reference AT ALL and would pass a rule that ignored the generation entirely. MUTATION-PROVEN, and the result is the review's finding as an executed measurement: with the defect restored, EXACTLY ONE claim fails -- a_grant_fenced_at_another_money_generation_authorizes_nothing -- while the other five pass, INCLUDING the old positive control. The pre-existing suite was structurally blind to this defect, which is how it survived. Reverted, all six pass. The now-unused grant_lease_generation import is dropped: leaving it would advertise a dependency the code no longer has. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VQ1HbKBP5SH24N2zxExYmN --------- Co-authored-by: Brian Searls <cutencool.work@gmail.com> Co-authored-by: Claude Opus 5 <noreply@anthropic.com> Co-authored-by: Brian Searls <briansearls1@gmail.com> Co-authored-by: gunbc-ci-auto-heal <gunbc-ci-auto-heal@users.noreply.github.com>
The state this refuses
At the census-borrowed declaration route, absence from a lookup was standing as positive evidence of generic ownership — and the environment that lookup consulted has no bindings at all, so the evidence was unconditional rather than occasional.
v1.compiler.env census_declaration_type_envbuildsbindings,str_bindings,ancestry_str_bindingsandintern_tableas empty maps, seeded only with the declaration's type-parameter bindings. In that environmentlookup_binding_by_name_localcan only ever find atp_name. Two sites read a miss there as "this leaf is a generic":v1.compiler.lookup declaration_unbound_leaf_names— results concatenated into the generic-name roster;v1.compiler.env declaration_substitution_basis— second disjunct, the same test again.So for every census-borrowed declaration, every unqualified non-kernel concrete type name in every parameter was enrolled as a generic parameter of that declaration and stamped
TypeVariable { id: <its own name> }. Always, not sometimes.Why that is a floor defect
v1.compiler.infer direct_call_argument_inhabitance_diagsselects the substitution basis as the declared side whenever it carries an unbound type variable, anddeclared_type_inhabitancereads that stamp as a generic formal and declines withUndecidableGenericFormal. A value of any constructor reaching aNonEmptyStror aHostIdentityposition was therefore admitted in silence — not because the pair was undecidable, but because the declared side's identity had been overwritten before the judgment ran.DESIGN §4b puts "values inhabit declared types" in the ordinary compiler floor, and §5 names this shape exactly: unable to resolve, so widen to "generic", so decline. The absorbing arm destroyed the only signal that would have made the deficit rank.
The authority was already in hand, one field over
v1.compiler.lookup borrowed_census_callable_candidatecomputesborrowed_generic_param_nameson its first line and classifies the return type of that same declaration positively with it, throughqualify_borrowed_type_names: roster members stay variables, every other bare leaf resolves to its owner module and is qualified. The parameter types, in the next field of the same record literal, went through the guess.One question, two answers, inside one record literal — DESIGN §3. The parameter side now consumes the same roster, and the absence disjunct is deleted from both sites together, because they were one classification authority written twice.
Scope, deliberately narrow: only the roster moves.
declared_typestaysparam_node_type_expr. Routing it throughqualify_borrowed_type_namesas well would change the name every consumer ofResolvedFormal.declared_typereads — and one of them,direct_call_generic_type_argument_inhabitance_diags, comparesauthored_name_atof the declared node against the actual's for equality. That is a second, wider claim, and a population delta after it could not be attributed to this one. If the judgment turns out to need qualification to reachInhabitson a bare-declared / qualified-produced pair, it returns as its own PR with its own justification.The roster reads a declared fact, not a naming convention
v1.compiler.parse parse_optional_type_paramsmints, for each name in the angle-bracket clause, one param whose name and whose type-expr name are that name. The<...>clause has no other carrier. A search for an authored parameter of the formName: Nameacrosssrc/v1,src/v2anddagmatches nothing, whilefn f<T>(...)matches throughoutstd— so the spelling this roster recognises is not hand-authorable in the corpus and exists only where the parser minted it from a declared generic clause.fn_type_param_namesandparam_is_generic_declalready read the same encoding; this adds a fourth reader of one fact, not a second fact. (Consolidating those readers is a real §3 item and deliberately not opened here.)Cost
The obvious alternative — keep the absence test but consult the census-backed
lookup_binding_by_name— is refused on shape, and the shape argument needs no timing: it dragslisted_import_required_bare_call_blocked, the global-bare ambiguity list and the nearest-ancestor containment walk onto every leaf of every declaration, where the roster route is a map lookup against a roster already computed for the sibling field. DESIGN §6 bare-minimum-cost refuses the first shape independently of the semantic argument, and the cheaper path is also the correct one — the usual direction when a heuristic is replaced by the fact it was approximating.(An earlier draft of this paragraph cited a wall-clock figure for that alternative. It is withdrawn: the run that produced it compiled stale stage0 mirrors, so it measured a compiler that did not contain the change. No number is cited here in its place.)
Producer census: is the parser the only site that can mint
name == type-expr name?The source-shape reading above says what nobody has written; the claim the repair depends on is what nothing can produce.
make_param_nodehas twelve call sites (thirteen matches, one the definition), and every one is classified:pname == tname?parse.parse_optional_type_paramstypes.algebra_method_fieldand the two callable-type builders (×3)"_"; would need a type named_parseat the twoname: ""sites (×2)pname != ""guardinferfold-signature params (×2)acc/elemagainst computed typesparserecord-field → param (×2)Name: Nameparseauthored value params (×2)Name: NameThe last four are authored-dependent, and both authored shapes were censused across
src/v1,src/v2anddag: zero occurrences of a parameter or a record field whose name equals its type name.So exactly one producer mints the shape structurally; the rest are either name-excluded by construction or need an authored spelling that does not occur. Stated at honest rung: for the eight structural producers this is structurally impossible (§4b(4)) — the shape has no constructor there. For the four authored-dependent ones it is mechanically preventable at best: an author who writes
fn f(Vendor: Vendor)would have that parameter read as a generic. That is a pre-existing property of the parser's encoding, shared by all three readers of it below, and is neither introduced nor climbed here.Recorded §3 fork (not opened here)
Three readers test one fact — "is this param the parser's encoding of a generic clause?" — with three separate predicates:
v1.compiler.resolve fn_type_param_namesv1.compiler.infer param_is_generic_declv1.compiler.env borrowed_generic_param_names(the one this PR routes the parameter side through)They differ in detail (
param_is_generic_decladditionally admits a type-expr already stamped a variable). One fact, three tests, is a §3 fork; it is known and deliberately unwritten here so this cut stays one cut. Recording it so the next person to touch any of the three does not re-derive this analysis.Declared boundary (§4b(2)) — it refuses where it cannot decide
The roster is the declaration's own generic clause. A synthesized or member declaration that borrows an enclosing owner's generic without restating it is not on that roster, so its borrowed leaf stays bare and reaches the judgment with a declared name that resolves to nothing — reported
UndecidableFormalUnresolved.That is the correct disposition, not a gap: the position genuinely is not judgeable from what the declaration carries, and the reason names precisely that. The change refuses where it cannot decide instead of guessing, which is the whole point of it — the state it replaces claimed genericity it could not demonstrate. Next-rung trigger, at capability grain: an owner-generic carrier on the borrowed declaration. Not a census and not a fixture — the carrier.
This PR does not claim the roster is exhaustive over every generic a declaration could mention.
Measured
Re-derived on the current head. Every earlier figure in this body is SUPERSEDED, including the previous "final-head" table: the tree has since taken another
origin/mainmerge and eight commits, and a number that outlives its base is not evidence.Construction. Same corpus on both halves, same instrument, two binaries differing only in the three stage0 mirrors — the before half built from
origin/main'sv1_compiler_infer{,_env,_lookup}.rs, the after half from this branch. Both built locally and frozen outsidetarget/before running. Vary the compiler, hold the corpus.Transition matrix over site identity
(module, span):UndecidableGenericFormalUndecidableGenericFormalUndecidableRefinementIntroductionUndecidableOptionalCarrierUndecidableProducedIdentityErasedUndecidableGenericFormalUndecidableProducedIdentityErasedUndecidableGenericFormalUndecidableRefinementIntroductionDeclaredTypeNotInhabitedDeclaredTypeNotInhabitedUndecidableFormalUnresolvedUndecidableRefinementPeerChainsUndecidableGenericFormalThe refusal set is identical BY IDENTITY: the same 7 sites before and after. No refusal gained, none lost. This diff removes a fail-open without adding a single refusal the corpus did not already carry — and that fact does more work in this PR than any other, because it is what answers the freeze question below.
The reason split is visible in the matrix and it was not cosmetic. 5 sites report
UndecidableRefinementPeerChainsand 4944 reportUndecidableRefinementIntroduction. Before the split those were one bucket of 4949 filed under a label saying they were introductions, 5 of which are not. Small, and exactly the population that would have been silently carried past the next climb.Rung-honest statement. This does not climb the class: no previously-accepted invalid site newly refuses.
The −8425 is not permitted to do rhetorical work the +4949 contradicts.
Every off-diagonal cell, characterised rather than counted:
UndecidableGenericFormaland none from any other disposition. With the declared identity restored the judgment progresses further and finds the produced identity genuinely unavailable — each a bare identifier bound by amatchpattern orlet. The next honest deficit, exposed rather than introduced.v2.lens.unused_parametersat the generic type argument seam, adjacent in the same call expression to a formal whose false classification this repair cleared. A different seam, not a re-appearance.One instrument defect found in my own controls, and it generalises
All six controls enrolled in the previous head were false, and they were false for a reason worth stating rather than quietly fixing.
violation_countreports every row of the class in the whole census, not the fixture's contribution — the counts run to thousands — while the literals I asserted (== 1,== 0) were copied from a probe delta. That is exactly the shape DESIGN §5 refuses: a merge-blocking test comparing a live population to a numeric literal not grounded in a controlled fixture. They did not merely have wrong numbers; they were comparing two different quantities.The repair generalises: each arm is now a PAIR of fixtures differing on exactly one axis, so the shared corpus contribution cancels and only the axis is measured. Anyone authoring a witness against a census will reach for the same wrong literal, and the pair form is the answer.
Enrolled controls
No enforcement RED is owed, because no enforcement climb is claimed. An earlier draft carried one — a previously-accepted unrelated-constructor site must newly refuse — and it is retired, not satisfied. Forcing it would make this PR solve the separate literal-introduction question merely to justify the story about why it existed, and an unmet acceptance control in a merged body is worse than none: the next reader takes it as met.
What is owed is a discriminating RED for the classification repair. All five arms below were measured live on this head, and all six of their predecessors were observed RED under the real floor harness before the oracle repair — so they discriminate in the direction that matters.
w_a_concrete_declared_leaf_is_not_classified_generic_through_the_censussha256_digest_wire_form(digest: Sha256Digest)== a fixture with no callTypeVariable { id: "Sha256Digest" }→ the pair separates by onew_a_branded_refinement_at_its_own_declared_base_inhabitsNonEmptyStr← producedHostIdentity: 0 refused, and == its exact-match partnerwhere-refinement exclusion → refuses, as it did at two live corpus sitesw_two_distinct_brands_over_one_base_are_not_refused_twicew_a_nominal_opaque_alias_does_not_ground_through_the_refinement_chainSecretdoes not ground toString(1 refused)nominal_opaquealias in the chain → sibling pairw_a_bare_literal_at_a_refinement_formal_declines_and_a_cast_one_does_notThe positive census arm is withdrawn because its RED is not authorable
The sixth control was to assert that a declared generic clause is still classified generic through the census route. Measured: that fixture contributes zero rows of the class — equal to a fixture with no call at all — so the seam emits nothing there whether the classification is right or wrong. An arm asserting one is false; an arm asserting zero is green by construction and would later be cited as coverage of something it never measured. DESIGN §4b says to ask whether the RED is authorable before writing the check, so the arm and its now-dangling fixture are removed rather than enrolled as a decoration.
The cause is a harness boundary, not a fixture shape.
src/v1is not a--source-rootin any workflow, so no witness under the required floor can observedeclaration_substitution_basis's output at all;dag/test/claim/checkpoint_identity_keying_witness_test.dagrecords the same boundary and exists to work around it black-box. That makes this a §4b missing-harness case, which is a next-rung trigger and explicitly not a permanent ceiling.The residue, stated precisely. What is measured is the removal of a false positive, which the negative arm measures directly and discriminatingly. What stays unmeasured is the survival of the positive authority — that a genuine declared generic is still classified as one. The roster union below makes that considerably safer by construction; construction is not evidence, and this body does not blur them.
Review finding taken: the roster is a union again
Deleting the absence disjunct was the repair. Replacing
fn_type_param_nameswithborrowed_generic_param_namesalongside it was a second, uncontrolled change, invisible to every control enrolled at the time. The two predicates are not the same — the borrowed roster is wider on one axis and narrower on two, dropping any declared type param whose type expression is not a childlessNoConnectiveleaf — so such a param would have been reclassified as a concrete unresolved name: the exact failure the negative control exists to prevent, arriving through the other door.declaration_generic_namesis nowconcat(tp_names, map_keys(borrowed_generic_param_names(...))), which is what the pre-repair code passed. Only the absence disjunct is deleted. (Review finding,tidy-lynx-804, 2026-09-07.)I took the union rather than measuring the two predicates for agreement: the union is correct whether or not the sets agree today, so measuring to license a narrowing that is not needed is work priced against elegance (§6).
Admission under the v1 freeze (
gunbc.v1_maintenance_standingv1_seed_standing)This PR changes the frozen v1 seed, so it owes an admission argument, and this section is it. §3's frozen-X carve-out reclassifies v1 as semantics-frozen with maintenance active under a PURPOSE test — a change is admitted when it serves the v2 self-host program — with five refused classes that DOMINATE every admission. The whole diff's source change is in
src/v1, so the question is live and was not answered in earlier revisions of this body.The admitted instance is
DefectRepairDiagnosticsMayMove, and the standing draws the line in exactly the terms this change needs: the seed doing something INCORRECT is admitted, repair it, diagnostics may move; the seed FAILING TO DO something it never did isSeedFeatureCompletion, refused. The producer repair is pinned to a specific incorrect behaviour the seed exhibits today, evidenced rather than asserted: a lookup miss against an environment carrying no bindings was read as positive evidence of generic ownership, unconditionally on the census-borrowed route, and 8411 sites are measured moving off that misclassification.The self-host connection is direct, and it is a measurement rather than a claim: v1 COMPILES v2. Partitioning the moved population by source root:
src/v2UndecidableProducedIdentityErasedThe compiler that builds v2 was overwriting the declared identity at 3707 positions in v2's own sources and admitting any constructor there in silence. That is not a downstream benefit of a seed improvement — it is the self-host program's own files being mis-judged today, so repairing it meets the purpose test at its centre rather than at its edge. The 95.1% is the sharper figure: the population where the judgment now gets far enough to find the produced identity missing is almost entirely v2.
The lane is compiler-guarantee — "the DESIGN §4b ladder climbs" — not v1 exit. A reviewer reading only the v1-exit lane sees lines added to the thing being exited, which is why the lane is named here.
And the seam has no v2 counterpart, so "fix it in v2 instead" is not an available option. Derivable predicate rather than an assertion:
lookup_binding_by_name_local,substitution_basisanddeclaration_unbound_leaf_nameshave zero occurrences anywhere undersrc/v2(they occur only insrc/v1). Anyone can re-run that at any later head.The hardest objection, taken at its strongest
A reviewer applying the refusal precedent — the lane refused as
SeedFeatureCompletionfor authoringOperatorSpec/TypeCheckpointrows so the emitter could render constructs it has no row for — could reasonably say that a new inhabitance relation with a new type and two new verdict reasons isNewLanguageBehavior. That is the right objection to raise and it deserves a measurement, not an assurance.It is answered by the refusal set, which is identical by identity: the same 7 sites before and after. The diff adds no refusal and removes none. Nothing the seed accepted is now rejected, and nothing it rejected is now accepted — so there is no new language behaviour to speak of. What changes is why a non-blocking decline is reported: one coarse reason that was frequently FALSE is replaced by reasons that are true, and the widening arm restores to
Inhabitsa pair the pre-repair seed also admitted (silently, for the wrong reason).And the relation is not optional garnish on the repair — it is the repair's integration closure. The producer fix, landed alone, makes the seed newly REFUSE correct code: with the declared identity restored,
declared NonEmptyStr <- produced HostIdentityrefuses at live corpus sites. Shipping that would be a wrong-closed diff, which §5 makes a hard reject. So the relation exists because the admitted repair exposed a false refusal, and it earns admission on the same instance rather than as capability sought for its own sake. The distinguishing test is the refused-class one: this adds no capability the seed did not already claim — it removes a fabricated judgment and refuses to fabricate its replacement, declining where another authority already decides rather than minting a second one.What would have made me stop. If the relation had needed to decide pairs the seed never claimed to decide, or had added a refusal the corpus did not already carry, it would be seed capability and I would have said so rather than argued it. The refusal-set identity is what distinguishes those cases, and it is measured on the final head rather than assumed.
The relation fires — proven, because making it unavailable on a miss is exactly the failure that hides
The chain is now discarded whenever any link fails to resolve. A fail-closed collapse is the safe direction, which is precisely why it can hide: if every chain collapsed, the relation would be permanently unavailable — never wrong, never right, never firing — and that is the inert check DESIGN §4b calls worse than absent, because it gets cited as coverage.
Measured on this head, counting emitted reasons over
dag+src/v2:UndecidableRefinementIntroductionUndecidableRefinementPeerChainsDeclaredTypeNotInhabitedBoth arms reach verdicts, and the peer arm is inhabited rather than being a variant nothing produces.
The widening arm is proven by an enrolled control, and the mechanism matters.
w_a_branded_refinement_at_its_own_declared_base_inhabitsassertsNotInhabited(widening) == NotInhabited(exact-match partner). Ifrefinement_inhabitancereturnedAbsentfor every pair, the widening pair would fall through to the now-unblinded nominal-product arm, which refusesNonEmptyStragainstHostIdentityas distinct constructors, while the exact-match partner would not refuse. The equality breaks and the control reds. It is green, so the relation is reachingRefinementWidensToDeclaredBaseand admitting.That property is new: while the product arm was blinded, a dead relation would have produced
0 == 0and passed vacuously. Removing the blinding is what turned a control that could pass while the relation was dead into one that cannot.A review-originated finding that moved to its own PR
Review 62172 reported that a
where-refinement exclusion added here reopened a refusal for disjoint pairs — a branded refinement at a true product formal. The conclusion was right and the cause was not: measured on both sides, with two compilers differing only in the three stage0 infer mirrors, that pair is unrefused underorigin/mainas well. The defect is pre-existing and is not this diff's.It is therefore enrolled in its own PR — #10817 — as a discriminating control, an
ExpectAssertionFalseadmission and arecurring_failure_moderow, on the rule that a review-originated finding moves to its own PR when its discriminating RED reproduces on main without the reviewed diff. Discovery provenance is recorded here; implementation ownership follows the demonstrated subject. This PR deliberately keeps no second copy of that control — two owners for one property is the fork this project spends its time removing.The
where-refinement blinding is still removed here, on its own merits: it was unnecessary, since the refinement arm runs before the product arm, and removing it is what makes the widening control discriminating above. It is not claimed to repair the disjoint case.Status
The five enrolled controls and the whole required floor are green locally on this head; the same harness reproduced all six predecessors as red before the repair. The regenerated stage0 mirror is committed with the
.dagchange, verified by content.docs/design-failure-modes.mdis re-projected — it drifted because the merge resolution took main's projection without re-projecting the new row.One observed unrelated signal, not chased and not fixed here: the floor emits
STALE-COST-DEBTfortest.claim.live_deploy.emit.apply_script_and_tree_sync_unit_stay_within_ci_runner_grants, enrolled inv2.workflow.floor_cost_debt. It is not counted inunexpected_failuresand is not what refuses the lane. This diff does not touchsrc/v2/workflow/floor_cost_debt.dagat all.Two claims withdrawn, both refuted by fixture rather than argued down
takes_non_empty(s: "x" as NonEmptyStr)produces nothing from this relation.takes_non_empty(s: "")is refused by thewhere-refinement machinery (type mismatch: expected Product(NonEmptyStr), got Primitive(String)),takes_non_empty(s: "x")is admitted by it, and this relation declines both. So the introduction question is already decided by an authority this relation does not consult — the "never undecidable, merely unconsulted" shape the numeric note in the same file records. The next-rung trigger is correspondingly cheaper: consult that verdict, not carry a new elaboration fact to the seam.The
recurring_failure_moderow is corrected the same way: it asserted that the roster residue reportsUndecidableFormalUnresolved, and the census moved that population by zero across the repair, so the row now records that boundary as asserted and unexercised.🤖 Generated with Claude Code
https://claude.ai/code/session_01MuB8wHjijaFwNoyzm78stF