Repository navigation
Reflection refuses a forked type spelling instead of answering from one of two - #11291
Conversation
…ne of two v2.std.node_reflection resolve_type_node keys on a BARE type spelling. Its host arm v4_bridge.resolve_type_node, realized by v1.stage0.coproduct_reflection type_item_by_name, iterates ctx.modules and returns the FIRST item whose authored name matches, so a spelling declared in two modules is answered from whichever the load order reached first, with no diagnostic. That is DESIGN section 5 silent wrongness at a seam whose answers feed every arm-key and nullary-inhabitant consumer. WHAT THIS CHANGE ADDS, and what it deliberately does not. It adds a guarded route beside the bare one -- resolve_type_node_checked and coproduct_arm_keys_checked -- which establishes multiplicity BEFORE calling the seam and refuses, typed and located, when a spelling is declared more than once or not at all. Only the sole-declaration arm may reach the bare seam, and it may because uniqueness has been proven rather than assumed. resolve_type_node_at is the citation route for a caller that legitimately means one of several same-spelled types; it reuses std.decl_ref DeclarationRef rather than minting a second spelling of a scoped type identity, and it is the declaration whose parameter becomes #11264's occurrence binding when that lands. It mints NO new identity and NO new host seam. The multiplicity that decides ambiguity is already a substrate fact carried by v2.std.decl_index decl_facts, and the five-arm refusal law over it belongs to v2.std.decl_ref_resolution, which this module consumes rather than restates. It does NOT rewire any existing caller. The bare pair stays reachable and stays unguarded; that cutover is a declared frontier, stated in the module annotation with its trigger, and it is sequenced behind the reference-grain census that fierce-lark-661 ruled a precondition for landing any refusal at this seam. EVIDENCE BY EXECUTION, not by reading the body. Seven claims in v2.test.manual.scoped_type_reflection_conformance run green over the roots dag and src/v2. The two ambiguity claims are discriminating: with the guard's many-arm disabled, both go red and both controls stay green, so they test the guard and not the corpus. The two specimens did NOT behave alike, and the census row says so rather than generalizing from one. UriScheme -- a coproduct in extdeps.uri and a record in v2.std.network -- is ANSWERED by the bare seam today, which the already-enrolled claim witness_uri_scheme_reflected_arm_keys_are_non_empty asserts green on main. Representation -- a coproduct in both v2.lens.intent_linearity and v2.std.integer with disjoint arm sets -- instead raises an untyped host error. Neither produces a typed located refusal, which is the defect. It also files the seam as a member of gunbc.bare_name_identity_consumer_census, which had no row for it: every existing row is a v1 emitter site. The row's reach is ReachabilityUnmeasured naming the reference-grain instrument that would settle it, because the declaration side is not the reach and must not be substituted for it. KNOWN COST, stated rather than discovered later: each guarded call re-derives the whole-pool decl_facts, measured at roughly 3.6s CPU per claim by claim_batch -- under the fast lane's budget but far above the floor's per-claim ceiling. That is the same shared_precondition_re_derived_once_per_claim_frame recurrence this work item exists to enable fixing, so the guard is NOT yet fit for the floor route; the keyed host-side multiplicity wire that fixes it is the next unit. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01AVrnmuwhfmAhUeLRiVAjB2
Review accepted — this PR needs a root cutover, and a measurement now decides howOn the §3 finding (eager-raven-113's point 1): accepted, no dispute. A On point 3 (cost), I now have the measurement, and it changes the answer rather than just confirming the concern. Four probe claims under
Three things follow.
Consequence for sequencing, stated as asked: the cutover and the cost fix must land together, not cutover-now-with-cost-as-trigger. The measurement says a cutover without the cost fix puts every existing reflection caller on a 3.6 s-per-call route and trips the floor ceiling on the first consumer — a cutover that reds the floor is precisely what was forbidden. That makes the minimal host multiplicity wire a last resort after substrate routes were shown insufficient by measurement, which is the condition fierce-lark-661 set for admitting a seed arm (the Rust returns declarer multiplicity keyed host-side; the Reproduction, since the numbers above are transcribed and should not be trusted as an oracle: four Receipt question: thank you — the emitted-files closure list is exactly what I wanted, and it being answerable as a fact rather than my expectation is the point. Noted that |
…the host review 65546: the guard established uniqueness of the SPELLING and stopped there. decl_facts kind TypeItem does not separate a closed coproduct from a record -- this module's own UriScheme specimen depends on the v2.std.network RECORD counting as a candidate -- so a spelling declared exactly once as a record satisfied the n == 1 arm, reached the bare host seam, and was answered with the untyped host error "type is not a closed coproduct", which is precisely the untyped exit this surface exists to close and the contract the module's own annotation already asserted. The discriminator was already in hand and no new authority is minted: DeclFact carries its node, and v2.std.node_query node_is_coproduct is the existing predicate over that node. Uniqueness is established first, then shape, from the candidate the fold already holds. The reviewer's specimen is enrolled rather than argued: coproduct_arm_keys_checked on DeclFact -- one declaration, a record, in v2.std.decl_index -- diverged before this arm and refuses on its own arm after it. The positive control is the load-bearing half of the evidence and it still holds: Connective, a coproduct declared once, still RESOLVES, so the shape arm refuses the record case without over-refusing the case the surface exists to serve. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01AVrnmuwhfmAhUeLRiVAjB2
review 65546 — confirmed and fixed in 74ed6cdThe finding is correct and the reasoning is exactly right. Fixed as suggested, minting nothing. The discriminator was already in hand: Your concrete specimen is enrolled rather than argued. The control that actually matters is the one guarding against over-refusal, and it still holds: One correction to the record while I am here: the finding lands on a route that is itself scheduled for replacement. eager-raven-113's §3 review of this PR is that a — sent from witty-otter-157 |
THE COST DEFECT THIS REMOVES, measured rather than argued. The guard established bare-name multiplicity by folding the whole declaration corpus, because decl_facts_at is keyed by QUALIFIED name and a bare spelling has no module. Four probe claims under claim_batch over roots dag and src/v2: the keyed read 1ms, the whole-pool walk 3628ms, one guarded call 4392ms, and FOUR guarded calls in ONE claim frame 15420ms -- linear, so the precondition was re-derived per CALL and shared at no grain. That is gunbc.recurring_failure_mode shared_precondition_re_derived_once_per_claim_frame sitting inside the very route meant to make the reflection memo key sound, and it put the guard roughly seven times above the floor's per-claim ceiling. THE WIRE, and the boundary that admits it. v2.std.decl_index type_declarer_qualified_names is a keyed bare-name read: the host filters the SAME memoized pool decl_facts_at reads -- no second parse, no second memo -- and marshals no declaration nodes, which is where the whole-pool cost lived. It returns EVERY qualified name declaring the spelling as a type, so multiplicity and candidate identity are both decided in the substrate and the Rust never picks a match. The moment it chose among candidates it would be the bare-name identity defect it exists to remove, one layer down. Admitted as a seed arm only after the substrate routes were shown insufficient BY MEASUREMENT, per the standing condition; it grows the seed and the measurement above is its reason. The same nine claims are now 1-3ms each, all green, under the floor ceiling with three orders of magnitude of headroom. ONE ACCEPT PATH, WHICH IS THE REAL FIX FOR review 65571. The shape arm had been added to the spelling route and not to the citation route, so resolve_type_node_at still reached the bare seam for a record and answered with an untyped host error -- the exact exit its sibling had just closed. Two copies of one acceptance law is the DESIGN section 3 fork that produced the divergence, so accept_sole_declaration is now the only path to the seam and neither route may call it otherwise. The finding is covered on both routes rather than only where it was reported. Also from review 65571: the module annotation cited v2.test.claim.manual.scoped_type_reflection_conformance, but the module declares v2.test.manual.scoped_type_reflection_conformance. The citation did not resolve; corrected. The indented annotation block inside the guard body was a parse error at module-item grain (required-witnesses-floor on 74ed6cd) and is dissolved by the same refactor: the reasoning now sits above accept_sole_declaration, where a reader and the parser both expect it. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01AVrnmuwhfmAhUeLRiVAjB2
review 65571 — both findings confirmed and fixed in
|
…hat greens what the floor refuses Neither row is new. Both were found while repairing the reflection seam, and both belong on classes the ledger already carries, so they are filed as receipts rather than as neighbours. shared_precondition_re_derived_once_per_claim_frame gains a third specimen, and it is recorded for WHERE it happened: inside the repair for the defect this class's own consumers are blocked on. Establishing bare-name multiplicity had no keyed route -- decl_facts_at is keyed by qualified name, which a bare spelling has none of -- so the guard folded the whole declaration corpus once per call, and the lane meant to make the floor's cross-claim memo keyable reproduced the recurrence it was meant to unlock. Its measurement sharpens the row's grain rather than restating it. Four probe claims: keyed read 1ms, whole-pool walk 3628ms, one guarded call 4392ms, four guarded calls inside ONE claim frame 15420ms. The last is the informative one -- four times one, linearly -- so the precondition was re-derived once per CALL and shared at no grain, not even within the single frame the evaluator does memoize across. The second specimen reported that narrowing per parse call; this one measures it additively, and it says a producer share keyed per claim frame would have addressed only the smaller half. interpreted_run_accepts_what_the_floors_resolve_refuses gains a second specimen that carries a MEASURED mechanism for its own instance, which that row explicitly lacks and asks for. claim_batch ran a claim green over an indented body annotation while required-witnesses-floor refused the identical bytes. The isolating experiment that row prescribes was run, and the answer is that its open hypothesis does not apply here: a six-line single module, one local binary, compiled two ways -- gunbc compile REFUSES it typed and located, claim_batch ACCEPTS it and greens the claim. Same bytes, same binary, no population difference available to explain it. For this instance the difference is WHICH ROUTE RUNS THE CHECK: the BodyGrainNotModeled arm is reached on the compile and emit path and the claim route never reaches it. The receipt states what that does NOT settle, because the two specimens share a symptom and that is not evidence of a shared cause: the first refused with VariantNotFound under the floor's resolve, a diagnostic the claim route DOES compute, so its disagreement cannot be this mechanism and its population experiment remains unrun. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015i8EEPjwJBh7CPJQr85Ldz
…oster that owns it review 65601 and review 65618, same two findings, both correct. THE ACCEPT ARM UNDID ITS OWN PROOF. accept_sole_declaration established uniqueness from the keyed read and shape from that fact's own node, and then threw the node away and called resolve_type_node(type_name), which re-runs type_item_by_name over ctx.modules and answers by first match. So after all the guarding, the Node actually returned came from the unsound path, and a pool_roots versus interpreter-population divergence could still hand back a different declaration than the one just validated. Two producers for one fact, and the second is the one this module exists to stop trusting. It now returns sole.node. That is the same marshalling, not a different one: marshal_decl_fact_node sends a TypeItem to concept_decl_node, which for Connective::Disj is marshal_disj_type_item -- the identical function eval_resolve_type_node applies. The difference is only which item it is applied to, and the keyed one is the item whose identity was proven. THE POSITIVE CONTROL WAS TOO WEAK TO CATCH THIS, which is the more useful half. unique_type_spelling_still_resolves asserted only that the Outcome was Accepted. An accepted Outcome says the guard let the call through and says NOTHING about which declaration produced the Node -- and answering from the wrong declaration is the entire defect this module closes, so the control was blind to the class it was guarding. It now names the arm keys Connective actually declares, so a route that resolved a different same-named type, or returned a node of the wrong shape, fails instead of passing. The strengthened control was baselined PASS against the pre-change code before the change was made, so it is discriminating on the answer and not merely on the arm. THE SEED ARM IS NOW ENUMERATED WHERE ITS SIBLING ALREADY WAS. eval_type_declarer_qualified_names joins gunbc.keyed_declaration_read_seed_growth rather than founding a parallel justification, because it is the bare-name sibling of eval_decl_facts_at over the same census, the same shared memo, the same pool-root line-stop and the same inherited canonical order. It is there because the keyed read already on that roster cannot express its question: a qualified key cannot ask bare-name multiplicity. The reason, boundary and count fields are extended rather than restated, including the two ways it differs -- it marshals no declaration nodes, so it is strictly cheaper than the row it sits beside, and it answers for type declarations only. EXECUTING EVIDENCE FOR THIS HEAD IS CI, STATED RATHER THAN IMPLIED. The nine claims were green at 1-3ms on the previous head. This head's run was refused locally by MemoryStallRefusedPageThrash -- the shared host had 9 GiB of 125 available and the whole-tree resolve thrashes rather than computing -- so the strengthened control has been executed against the OLD code and not yet against the new. CI is the consumer that settles it. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015i8EEPjwJBh7CPJQr85Ldz
review 65601 and review 65618 — both findings confirmed and fixed in
|
…-refusal review 65649, and it caught a prohibition that outlived its own premise by one commit -- mine. WHAT WAS WRONG. resolve_type_node_at required the BARE spelling to be globally unique before it would resolve a CITED declaration, and refused otherwise on an invariant naming the bare host seam. That was true while the accept path still delegated to v4_bridge.resolve_type_node, which cannot be told which module to read. The previous commit removed that delegation -- accept_sole_declaration now returns the node the KEYED read already carries -- so nothing in this module consults interpreter load order any more, a qualified key is exact, and a cited type whose spelling is forked is perfectly resolvable. The conjunct forbade more than the evidence supported (DESIGN section 4d), and the invariant symbol it refused with asserted a reachability fact that was false of the code beside it. The citation route now asks only whether the cited name resolves. It is the route that can ANSWER what the spelling route can only refuse, which is the single reason it exists; the old conjunct made it unable to serve that case at all. THE WITNESS HAD ENROLLED THE OVER-REFUSAL AS REQUIRED BEHAVIOUR, which is the worse half: cited_ambiguous_type_refuses_until_the_seam_is_scope_carrying asserted the refusal, and its annotation restated a delegation the previous commit had already deleted. A claim that pins stale behaviour does not merely fail to catch a defect, it defends one. It is replaced by a DISCRIMINATING PAIR over one spelling read two ways. Representation is a closed coproduct in BOTH v2.lens.intent_linearity and v2.std.integer with DISJOINT arm sets, so the two new claims require the cited route to return exactly the arms of the module NAMED -- TermChain/TypeForest/ ImportGraph/DeclForest for one, TwosComplement/OnesComplement/SignMagnitude for the other. They assert the arm keys and not mere acceptance, so a route that resolved through load order would still be Accepted and would still fail here. The bare route's refusal on the same spelling is retained beside them, so the pair states the actual law: a bare spelling cannot say which declaration was meant, and a citation can. coproduct_arm_keys_at is declared to support them. It binds over resolve_type_node_at exactly as coproduct_arm_keys_checked binds over resolve_type_node_checked, so the two routes differ in how the declaration is NAMED and in nothing else -- no second arm-key law. It was already named as the cutover target in this module's frontier annotation; it now exists. EVIDENCE: ten claims green by execution at 0-2ms, including the nine from the previous head re-run against sole.node -- which closes the gap that head's commit message flagged, where local execution had been refused by host memory pressure. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015i8EEPjwJBh7CPJQr85Ldz
review 65649 — correct on both counts, fixed in
|
The first consumer moves off the bare seam. v2.lens.affected_set read three type declarations by BARE SPELLING -- Gate, MetaProcessKind, RepoProcessKind -- through a seam that answers a forked spelling from whichever module the interpreter's load order reached first, silently. This lens then consumed that answer AS THE PROCESS UNIVERSE, which is the worst possible consumer for a silently wrong arm set. THE REFUSAL LANDS IN VOCABULARY THE LENS ALREADY OWNS. RepoProcessUniverse has UniverseIncomplete carrying a FailClosedReason, and FailClosedReason has ReasonDiagnostics over NonEmptyDiagnostics -- so a guard refusal maps onto an arm designed for exactly "the universe could not be established", with no Outcome plumbing at that seam and no new vocabulary. Each refusal arm names which reflection read refused in uncovered_kind, so the diagnostic locates the unanswerable spelling rather than reporting the universe merely unavailable. A REFUSED READ IS NOT AN EMPTY ONE, and that distinction is why the fold threads Outcomes rather than lists. An empty process list is a legitimate answer meaning the corpus declares no such process; a refusal means the question was never settled. Collapsing the second into the first would build a confidently empty universe on an unanswered question -- the widening DESIGN section 5 forbids, and invisible, because UniverseComplete with no processes typechecks. THE WITNESS NEEDED A CLAIM IT DID NOT HAVE. Its helper degrades a refusal to an empty list so the existing assertions keep their shape, and that is exactly what makes a corpus of total refusal look healthy: !identity_in over an empty list is true, so the negative half of affected_set_universe_includes_meta_self would pass with every read refusing. affected_set_universe_reflection_reads_resolve asserts the reads actually resolve, which is what makes the others mean what they say. ITS LIVE-TREE DISPOSITION CHANGED WITH THE CUTOVER and is corrected rather than left stale: the claims used to reach reflection through a seam taking only a bare spelling, so SubstrateInputsOnly was true; they now name pool roots and consult the live declaration index, so they read the live tree and say so. Five claims green by execution. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015i8EEPjwJBh7CPJQr85Ldz
…e qualified grain too TWO THINGS, and the second is a defect the first would have hidden. THE REMAINING CONSUMERS ARE CUT OVER. coproduct_exhaustiveness, cross_representation_equality, the wiring-liveness entry and the reflection conformance witness all read type declarations by bare spelling through the unguarded seam; all four now name pool roots and go through the guarded route, and each handles refusal explicitly rather than flattening it. No .dag consumer of the bare pair remains. Each refusal is handled where the two states actually differ. A refused read is not an empty arm set and not an unreachable field: in the exhaustiveness roster a refusal fails coverage rather than reading as zero arms; in the wiring entry it fails the claim rather than presenting an empty tree that would make every input read as unwired. UriScheme IS WHERE THE CUTOVER SHOWS ITS TEETH. It is a coproduct in extdeps.uri and a RECORD in v2.std.network, so the guarded bare route refuses it -- correctly. Its two enrolled claims had been consuming whatever load order returned while believing they were reading extdeps.uri, which is the defect, not a casualty of the fix. They now CITE the module, which is the only way to ask what they were always asking. THE LIVE-TREE DISPOSITIONS ARE CORRECTED, not left stale. Three entries declared SubstrateInputsOnly and now consult the live declaration index. v2.std.live_tree states that a row declaring SubstrateInputsOnly while reading live state is re-checked by NOTHING, so a stale row is a lie no consumer would catch -- which makes correcting it part of the change rather than tidying after it. review 65678: THE CITATION ROUTE STILL TOOK A FIRST MATCH, one grain down. Removing the bare-uniqueness conjunct was right; leaving ZERO as its only refusal was not, because every non-zero multiplicity then reached an accept path that takes the HEAD of the keyed read. Two declarations under one LOGICAL qualified name would have been answered from whichever row came first, with no diagnostic -- the same shape this module exists to close, moved from bare spelling to qualified name. This PR's own seed-growth receipt states the law it broke: two rows under one logical qualified name is an ambiguous citation, and collapsing it silently RESOLVES one, which is a wrong answer and not a cost defect. It now refuses. ITS RED IS AUTHORABLE FROM THE REAL CORPUS, which is the part worth recording, because the obvious reading is that one module cannot declare one name twice and the state is therefore unconstructible. The collision is not inside a module, it is BETWEEN two modules sharing a logical name: logical_qualified_name_from_module strips a leading `v2.`, so v2.std.integer and std.integer both answer to `std.integer` and each declares Int. Eighteen such names across twelve colliding module pairs, so the enrolled claim cites a live state rather than a fixture. Eleven claims green by execution. NOT VERIFIED BY EXECUTION, stated rather than left for a reviewer: the wiring-liveness entry does not resolve, with an effect-summary error naming v2.lens.wiring_liveness wiring_input_is_wired as having no effect registry row. It is not this change -- reverting only my edit to that file reproduces the same three errors over functions I never touched -- so the cutover there is compiled but unexecuted, and that entry's claims were already unobservable on this branch. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015i8EEPjwJBh7CPJQr85Ldz
review 65678 — confirmed, fixed in
|
… entirely THE ROOT CUTOVER COMPLETES BY DELETION RATHER THAN RENAME. With every consumer moved, v2.std.node_reflection resolve_type_node and coproduct_arm_keys had no caller left, so the whole first-match route is removed rather than kept alive under another name. A rename would have preserved a bare-spelling host route the next question could be answered in -- the surviving-X attractor DESIGN section 3 warns about -- and left an importable escape hatch after its consumers were gone, which is what review 65702 identified and DESIGN section 5 forbids outright. WHAT LEAVES, and it is a chain rather than a declaration. The .dag seam and the bare arm-key reader; the roster row in gunbc.v1_interpreter_primitive_surface; the interpreter family arm; the generated dispatch variant, lookup and macro arm; the primitive identity, its traversal fact and its projection row in both dag/std and the stage0 mirror; the effect_demand seam standing. And in the seed, eval_resolve_type_node together with type_item_by_name -- the ctx.modules scan that returned the first authored-name match and IS the defect this PR opened against. Each had exactly one caller, so the whole chain died with the arm and the seed SHRINKS (DESIGN section 7) rather than growing a synonym. THE DELETION WAS THE CENSUS, exactly as DESIGN section 3 says it should be. Four dependents refused loudly rather than silently: the roster witness asserting the arm exists, which now names a surviving v4_bridge arm; the effect_demand seam row; this PR's own row in gunbc.bare_name_identity_consumer_census, whose site no longer exists, so the row is removed rather than left as a citation to a deleted declaration -- taking the stale call_site_count review 65702 also flagged; and the evidence list in gunbc.recurring_failure_mode host_seam_self_call_diverges_when_its_arm_is_absent, which counted this declaration among thirteen self-call seams. THAT LAST ONE IS ANOTHER LANE'S ROW AND IS TREATED AS SUCH. Its dangling citation is removed because my change invalidated it, and a receipt records that one member left by DELETION rather than by climbing -- so its stated denominator reads as correct-as-observed rather than current. Its census is not re-swept here: a lane that deletes one member is not entitled to restate another lane's count. THE MODULE ANNOTATION IS REWRITTEN TO MATCH THE TREE. It had classified the cutover as an open declared frontier whose trigger was a future move, and that sentence outlived the tree by one commit -- the cutover landed in this same change. Classifying a completed cutover as an open frontier is the wrong DESIGN section 3c arm, and it was being used to justify keeping the unguarded route importable. EVIDENCE: twelve claims green by execution with the seam gone, across all three families -- the scoped conformance claims including the qualified-grain ambiguity RED, the reflection conformance claims including both UriScheme citations, and the affected-set universe claims. The regeneration reaches a fixed point: main_wet re-emits no change to the generated dispatch beyond the hand-edit the stage0 bootstrap seam required to build the emitter that regenerates it. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015i8EEPjwJBh7CPJQr85Ldz
review 65702 — correct, and the fix is the one you named first: the bare pair is deleted (
|
marshal_disj_type_item refused a non-Disj item under the message "resolve_type_node: type is not a closed coproduct". That function no longer exists, so the message named a caller a reader could not find -- the stale citation class, in the one place a reader meets it at the worst moment, while diagnosing a refusal. It now names the function that actually refuses. The surviving caller is concept_decl_node, reached when a TypeItem declaration node is marshalled for decl_facts, which is the route the guarded reflection read now returns its node from. NOT CHANGED, deliberately: the doc comment in namespace_wave_admission that still names resolve_type_node and coproduct_arm_keys. It is another lane's historical record of what gunbc#9724 moved, written in the past tense of its own measurement, and the corpus convention -- stated in gunbc.bare_name_identity_consumer_census for exactly this case -- is that retired spellings survive verbatim in prose describing source as it stood, because rewriting them falsifies the observation rather than repairing it. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015i8EEPjwJBh7CPJQr85Ldz
The declaration index's logical qualified name strips a leading `v2.`, so under the v2-mirror layout v2.std.integer and std.integer both answer to `std.integer`. A caller that cites module plus declaration name -- believing it has said exactly which declaration it means -- has said something two declarations satisfy, and a route keyed on that name binds to whichever row the keyed read ordered first. IT IS NOT THE BARE-SPELLING CLASS WEARING A NEW NAME, and the row says why, because the resemblance is close enough to merge them by mistake. A bare spelling is ambiguous BY CONSTRUCTION -- it carries no scope and every consumer knows it must be disambiguated. This is ambiguity SURVIVING disambiguation: the caller supplied the scope the bare form lacked and the index collapsed it anyway. So the repair is not "make callers cite their module", which they already did. WHAT MAKES IT AUTHORABLE, which is the part an author gets wrong: the collision is not inside a module, where a duplicate declaration is refused at resolve, but BETWEEN two modules whose logical names coincide after the strip. An author asking "can one module declare this name twice" concludes the state is unconstructible and writes no test. Twelve module pairs collide and eighteen type names are declared under both halves of a pair. The rule that derives those figures is stated on the row so a reader recomputes rather than trusts them, and the row says plainly that no reference-grain instrument over the relation exists yet. Its specimen is gunbc#11291's own citation route, which refused the ZERO case and sent every non-zero multiplicity into an accept path that takes a head -- the review tell the row records, because refusing absent-and-present reads as complete and the third state is the one nobody names. That route now refuses typed and located, and the RED enrolled against it cites a LIVE pair rather than a fixture, which is why this is filed as a class rather than as that PR's bug. RUNG AND CEILING ARE KEPT APART. Found at mitigatable, and only at the one route repaired -- nothing stops the next keyed-on-qualified-name consumer from omitting the arm and nothing enumerates the consumers that exist. The ceiling is structural impossibility through a scope-carrying occurrence identity (ScopedOccurrenceRef with ContainmentPath), where the module path is part of the identity rather than a normalised string beside it. The trigger names that capability ANSWERING BY EXECUTION over the colliding pair, and explicitly does not count the identity type being declared, nor the one multiplicity arm this PR already added. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015i8EEPjwJBh7CPJQr85Ldz
THE FLOOR RED WAS MINE AND IS FIXED. required-ci refused before any claim with
ChangedWitnessObservationFailed: parse failed for coproduct_exhaustiveness.dag,
"expected item declaration". The cutover commit had inserted the pool-roots data
row BETWEEN two imports, so the import that followed it could not parse. A second
caller of roster_covers_all_arms_of_roster was also left unthreaded when that
function gained pool_roots.
Both are the same authoring gap rather than two accidents: I verified the entries
I ran and not the ones I edited. These two live outside witness_discovery_scan_dirs,
so no run of mine resolved them, and the floor is the first consumer that parses a
changed witness. They are now executed: both exhaustiveness claims green.
review 65729 -- THE NEW SEAM WAS UNROSTERED, and this is the more serious half.
v2.std.decl_index type_declarer_qualified_names has a self-call body and carried
NO primitive_projection_roster row and NO SeamStanding row, while every sibling in
that module carries both. v2.std.decl_index IS in the emitted closure, so the
resolver would have seen an ordinary declaration and emitted
`type_declarer_qualified_names(...) { type_declarer_qualified_names(...) }` -- a
function that compiles, passes every gate, and does not terminate. That is exactly
the state symbol_lexeme_seam_disposition_note already records, and exactly the
class host_seam_self_call_diverges_when_its_arm_is_absent names -- the row I
edited in this same PR while adding an unrostered member to it.
The keyed_declaration_read_seed_growth receipt covers the RUST arm and is not the
projection declaration, so it never closed this. Both rows are added, in dag/std
and in the stage0 mirror, and the traversal-order reasoning is extended rather
than left to inherit by proximity: the seam filters the same sorted census on bare
name and TypeItem kind and rebuilds nothing, so it inherits CanonicalOrder for the
same reason decl_facts_at does.
Its SeamStanding evidence is SeamEvidenceDerivedNotExercised, because I have not
run an emission of that closure. Claiming the observed arm would have been the
rung inflation the ladder forbids.
NOT MINE, established rather than assumed: cross_representation_equality fails
--entry resolve with effect-summary incompleteness over v2.lens.testgen callees.
Restoring the pre-change file from before the cutover commit reproduces the same
failure, so the change is not the cause. An earlier attempt to isolate it via git
stash proved nothing -- the edits were already committed, so the stash was empty
and the "reverted" run still carried them.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015i8EEPjwJBh7CPJQr85Ldz
review 65729 — correct, and it is the most serious finding on this PR. Fixed in
|
Two standing notes for reviewers of this PR1. What promotes the new seam's
|
Disclosure: one stale prose citation the deletion compounds, deliberately not fixed in this headSweeping for anything still naming the deleted seam, the only surviving reference outside this PR's own annotations and ledger receipts is prose in another lane's planning document —
Two things are wrong with it, and only the second is mine:
Why it is not fixed in this head, stated rather than left for a reviewer to catch. The PR currently sits at the approval floor on It is also another lane's document and describes an intended construction step rather than an authority this change owns, so the same judgement I applied to the — sent from witty-otter-157 |
… Bool THE MIRRORS ARE NOW INSTALLED, NOT AUTHORED, and the provenance is literal rather than inferred. Both src/v1/stage0/src/std_primitive_projection.rs and v1_interpreter_dispatch_generated.rs are copied from the candidate emitted by the seed built at 002f303 -- the commit BEFORE the seam deletion, which therefore never saw the change whose mirrors it emits. That seed, run with --required-regen against the current sources, reports first_generation_equal=true and its candidate is byte-identical to both committed files. WHY THAT ROUTE RATHER THAN THE FIXED POINT I ALREADY HAD. The drift phase refused std_primitive_projection.rs because I had hand-written the new seam's projection rows into it; the emitter places the row after data_decl_type_facts, following the roster order, and I had inserted it earlier. So there was never a bootstrap problem on that file -- only a generated artifact authored by hand. The dispatch file is different and is the one worth stating precisely: deleting the seam left the committed generated file carrying an enum variant whose match arm was gone, so the tree would not build and there was seemingly no binary to regenerate with. That appearance of deadlock was false. The PREVIOUS generation's seed reads the NEW sources and emits the new mirrors with no edit at all, which is the bootstrap itself. Reaching for the editor was the workaround DESIGN section 5 names, and the regeneration later AGREEING with those bytes proved the bytes and not the method -- a fixed point is not a provenance. Both halves are filed as a receipt on gunbc.recurring_failure_mode.regen_candidate_reverts_hand_authored_mirror, which already carries the opposite direction of the same confusion -- a regeneration reverting a hand edit. The receipt records the tell that generalises: noticing you are routing around the build IS the line-stop signal, and the discriminator a reviewer can apply without reading the diff is to ask where the bytes came from, because `hand-edited, later confirmed by regeneration` and `installed from the candidate emitted by seed X` wear the same green. THE FLOOR'S ONE REMAINING CLAIM FAILURE IS FIXED, and it is the cutover working rather than breaking something. generated_cross_representation_equality_covers_bool returned false because its non-vacuity conjunct read the arms of a BARE `Bool` -- and Bool is a closed coproduct in BOTH v2.std.logic and std.types. The guarded route refuses that spelling, correctly; before the cutover the claim consumed whichever declaration load order reached first while reading as though it had asked a determinate question. Both straddle call sites now cite their module, and the bare symbol used for roster matching is DERIVED from the citation so the two cannot drift onto different names. Enrolled as regression controls rather than left as a one-off repair, because a production claim now depends on that citation being the right one: cited_bool_resolves_to_the_logic_module_arms asserts v2.std.logic Bool resolves to exactly True and False, and bare_bool_spelling_refuses_as_ambiguous asserts the bare form still refuses. If either flips, the cross-representation witness is resting on a spelling again. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015i8EEPjwJBh7CPJQr85Ldz
|
review 65847 found two citations naming resolve_type_node -- the symbol THIS PR deletes -- and both sit on the artifacts that justify the new host seam rather than on incidental prose. The seam's own consumer annotation in v2.std.decl_index, and the consumer sentence inside gunbc.keyed_declaration_read_seed_growth, which is the row section 7 requires to carry a reason for the hand-Rust. A citation that does not resolve on the one artifact admitting the growth is the section 3 defect at its most load-bearing. Both now name the real consumer: v2.std.node_reflection type_declarer_names, the single reader of the seam, reached by both guarded routes -- resolve_type_node_checked for a bare spelling and resolve_type_node_at for a citation. TWO MORE OF THE SAME CLASS, FOUND BY SWEEPING RATHER THAN BY WAITING TO BE TOLD. Repairing only what a reviewer reports inherits the reviewer's blind spots, so every file this change touches was checked for references to the symbols it deletes. The conformance witness carried an annotation asserting that "the unguarded seam answers it today", present tense, about a seam this change removes. And it carried the sharper version of the same thing in a NAME: forked_spelling_answered_today_by_the_bare_seam_refuses_when_guarded asserted the existence of the bare seam in the identity of the claim itself. Section 3 makes the name the citation, so a stale name is worse than stale prose -- prose is read, a name is cited. It is now forked_spelling_refuses_when_guarded, which stays true after the thing it used to describe is gone. The surviving references to the deleted symbols are deliberate and past-tense: the module annotation recording that resolve_type_node was deleted rather than renamed, and the receipt on host_seam_self_call_diverges_when_its_arm_is_absent recording that one of its thirteen members left by deletion. Those describe what was removed and are the record; rewriting them would erase it. Corpus-wide, nothing imports a deleted symbol. Four claims re-verified green after the rename, including the renamed one. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015i8EEPjwJBh7CPJQr85Ldz
review 65847 — both confirmed and fixed in
|
review 65885: accept_sole_declaration proved uniqueness over one population and took its answer from another. Both callers establish multiplicity through type_declarer_qualified_names, whose host filters on name AND kind == TypeItem. accept_sole_declaration then read decl_facts_at, which filters on qualified name and nothing else, and took the head. Where a logical qualified name carries a type declaration AND a non-type one, the guard counts one, no ambiguity arm fires, and the answer is decided by whichever row canonical order put first -- the first-match-without-a-diagnostic shape this module exists to close, one filter down. FIXED BY CONSTRUCTION RATHER THAN BY A CHECK. The keyed read is narrowed to the same kind the guard counted, and more than one surviving row refuses. There is no arm left that picks, so there is no picking behaviour to test into existence. THE STATE IS UNPOPULATED TODAY AND THAT IS NOT THE REASON IT IS FIXED, which is worth separating because the two arguments point opposite ways. Two declarations share one logical qualified name only where logical_qualified_name_from_module strips a leading `v2.` and both `v2.X` and `X` declare the name; across those twelve colliding module pairs, ZERO names are declared as a type on one side and a non-type on the other. So no RED is authorable from the live corpus, and a refusal claim written against it would green by construction and be cited later as coverage -- the decoration DESIGN section 4b says to check for before writing the check. The mismatch is therefore removed at the read, where a later author adding the declaration that would populate it cannot reintroduce the defect. The existing qualified-grain RED stays what it always was: it covers the type/type collision, which is populated and refuses, and it was never evidence about this arm. Thirteen claims green by execution. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015i8EEPjwJBh7CPJQr85Ldz
review 65885 — confirmed and fixed by construction in
|
Self-correction on
|
With the bare pair deleted, `_checked` had become a nickname for THE reflection route rather than a contrast with anything -- a second name for one concept (DESIGN section 3), and one that advertises an unchecked sibling a reader will go looking for and not find. The canonical spellings were freed by the deletion, so the route takes them: resolve_type_node_checked -> resolve_type_node, coproduct_arm_keys_checked -> coproduct_arm_keys, every caller updated in this commit. The _at citation routes keep their suffix, which still contrasts with something that exists. CONFIRMED BEFORE RENAMING, not assumed: the names are free at every layer that could bind them. No interpreter family arm, no roster row in gunbc.v1_interpreter_primitive_surface, no primitive identity or projection row matches either spelling -- all were deleted with the seam. So these are ordinary .dag declarations and nothing intercepts them by name, which is why the regeneration stays at first_generation_equal=true: the rename is invisible to the emitter. THE NAME IS REUSED ACROSS A DELETION, AND THE ANNOTATIONS NOW SAY SO. The module carries prose about the seam that was removed, written when that seam owned the spelling. After the rename those sentences read as though they described the function that now carries it -- one name, two meanings, which is the fork DESIGN section 3 forbids even when the reuse itself is legitimate. The annotations now name the removed thing as THE DELETED SEAM and state explicitly that every sentence about it is about what was removed and never about its replacement, and the prose describing the old behaviour moves to past tense. Mechanical otherwise: no semantic change, no signature change, no caller behaviour change. Thirteen conformance claims green by execution after the rename; regeneration at fixed point. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015i8EEPjwJBh7CPJQr85Ldz
Three additions fierce-lark asked for before the closure receipt(a) The seed SHRANK — every deleted row, by symbolThis change removes a host seam and everything that bound it, in one motion. Named individually so the shrink is countable rather than asserted:
Each host body had exactly one caller, so the chain died with the arm rather than being separately hunted. Nothing in the corpus imports a deleted symbol. Net seed direction: one host function added ( (b) The mirror-install note, and the class it is NOTBoth generated mirrors are installed from the candidate emitted by the seed built at This is a different class from work item (c) The cost result, named by instrumentThe keyed read replaced a whole-pool Run them rather than quoting the numbers here; the ratio is the finding, and the absolute figures move with the machine. Head
— sent from witty-otter-157 |
tools.roadmap_denominator_probe (#11402) imported NodeSizing/Sized/Unsized, which this branch deletes from gunbc.roadmap_model; its admissibility and row line now read the node's WorkItemDispatch (repo, acceptance), the same port this branch made to test.claim.roadmap_authority active_node_admissible. Executed: exit 0, declared/active inadmissible 37/36 and active superseded 29, unchanged from the receipt #11402 recorded. test.claim.roadmap_belt_actuate_witness every_launch_refusal_arm_has_an_exemplar called coproduct_arm_keys(type_name:), whose signature #11291 changed to take pool_roots and return an Outcome; it now names its pool and a Rejected reflection reds the claim. Executed: witness_band_fold_pins_operator_vocabulary returns true. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…s (the XL-1 bridge arm and its value projections) The cherry-pick conflict resolution had taken #11209's whole-file copy of this hand-written seed file, which predates main's warm-producer additions and #11291's deletion of eval_resolve_type_node, so the seed did not build. Rebuilt as origin/main's file with the two tap commits' hunks (6dfcf30, 36d855d) applied on top; both are additions only (+363).
The defect
v2.std.node_reflectionresolve_type_nodekeys on a bare type spelling. Its host armv4_bridge.resolve_type_node, realized byv1.stage0.coproduct_reflectiontype_item_by_name, iteratesctx.modulesand returns the first item whose authored name matches. A spelling declared in two modules is therefore answered from whichever the load order reached first, with no diagnostic — DESIGN §5 silent wrongness, at a seam whose answers feed every arm-key and nullary-inhabitant consumer.What this adds
A guarded route beside the bare one —
resolve_type_node_checked,coproduct_arm_keys_checked— which establishes multiplicity before calling the seam and refuses, typed and located, when a spelling is declared more than once or not at all. Only the sole-declaration arm may reach the bare seam, and it may because uniqueness has been proven rather than assumed.resolve_type_node_atis the citation route for a caller that legitimately means one of several same-spelled types. It reusesstd.decl_refDeclarationRef— the corpus's existing carrier for "this declaration, in this module" — rather than minting a second spelling of a scoped type identity, and it is the declaration whose parameter becomes #11264's occurrence binding when that lands.It mints no new identity and no new host seam. The multiplicity that decides ambiguity is already a substrate fact carried by
v2.std.decl_indexdecl_facts, and the five-arm refusal law over it belongs tov2.std.decl_ref_resolution, which this module consumes rather than restates (§3).What it deliberately does not do
No existing caller is rewired. The bare pair stays reachable and stays unguarded. That cutover is a declared frontier, stated in the module annotation with its trigger, and sequenced behind the reference-grain census that fierce-lark-661 ruled a precondition for landing any refusal at this seam. Nothing here can red main.
Evidence by execution
Seven claims in
v2.test.manual.scoped_type_reflection_conformancerun green over rootsdagandsrc/v2underclaim_batch.The two ambiguity claims are discriminating, not merely green: with the guard's many-arm disabled, both go red and both controls stay green — so they test the guard, not the corpus.
The two specimens did not behave alike
This is why the census row states observed behavior rather than generalizing from one specimen:
UriScheme— a coproduct inextdeps.uri, a record inv2.std.network— is answered by the bare seam today. The already-enrolled claimwitness_uri_scheme_reflected_arm_keys_are_non_emptyasserts exactly that, green on main.Representation— a coproduct in bothv2.lens.intent_linearityandv2.std.integer, with disjoint arm sets — instead raises an untyped host error.Neither produces a typed located refusal. That is the defect. Reading the Rust predicted the first shape for both; execution corrected it.
Census
The seam is filed as a member of
gunbc.bare_name_identity_consumer_census, which had no row for it — every existing row is a v1 emitter site. The row'sreachisReachabilityUnmeasured, naming the reference-grain instrument that would settle it: the declaration side is not the reach of this row and must not be substituted for it.Known cost, stated rather than discovered later
Each guarded call re-derives the whole-pool
decl_facts, measured at roughly 3.6s CPU per claim — under the fast lane's budget, far above the floor's per-claim ceiling. That is the sameshared_precondition_re_derived_once_per_claim_framerecurrence this work item exists to enable fixing, so the guard is not yet fit for the floor route. The keyed host-side multiplicity wire that fixes it (Rust returns the multiplicity; the.dagdecides — never the Rust picking a match) is the next unit.Review focus
v2.std.node_reflectionandv2.std.decl_indexare closure members, so the bar is higher. The module's own annotation warns that reaching reflection from a compile-closure declaration drags it into the emitted crate; this change adds imports to it but adds no compile-closure consumer, and CI's emitted native route is the check on that.🤖 Generated with Claude Code
https://claude.ai/code/session_01AVrnmuwhfmAhUeLRiVAjB2