Repository navigation
Enroll both instance-gap carriers in cited-symbol resolution, with a counted outside-index disposition - #8775
Conversation
…e, as a carrier Three E0282 sites in the emission boundary share one mechanism: v1's emit_rust emit_typed_call binds a lambda argument's parameter types only when the callee's SPELLED name is one of four literals (map, filter, flat_map, fold). A callee that resolves to a real declaration taking a function parameter, but is spelled anything else, gets its lambda emitted with no parameter annotation and rustc has nothing to infer from. The three sites are the discriminating RED for the root lane: two are near-misses of a roster name (fold_list vs fold) and one is absent from the roster entirely (is_prefix_of), so a fifth literal key closes two of three and the class survives — which is the point. `callee` is already in scope at the gate, resolved, five lines below. Population is carried as an UPPER BOUND (UpperBoundPendingResidueCheck), not a measurement: the residue half of the E0282 partition is weaker than the converted half, and the standing prediction is that the true count is higher. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…counted outside-index disposition Widening, not a new authority: the two instance-gap carriers project their DeclarationRefs into v2.lens.cited_symbol_resolution's production population, resolved by v2.std.decl_ref_resolution unchanged. This is the widening that lens's own law names as its next step. THE ENROLLMENT CAUGHT A DEFECT ON ITS FIRST EXECUTION, in a MERGED carrier. Two of gunbc.list_membership_operation_gap's five rows (#8735) cited the FILENAME — test.claim.attribution_span_witness_test — where the module declares itself without the _test suffix. Four reviews passed over those rows, one of which verified the citations by hand; a name-level grep passes because the declaration names were right and only the address was wrong. THE SPELLING FIX WAS NECESSARY AND INSUFFICIENT. Per-ref probe with positive controls: 6 of 8 refs resolve; the two test.claim rows still refuse because v2.std.decl_index decl_facts SKIPS TEST .dag FILES DELIBERATELY. Their modules are in module_declaration_facts_live; their declarations are in no decl_facts row at all. Those citations are true and unanswerable at once — DESIGN 4b's outside-the-modeled-guarantee column. So std.decl_ref gains CitationIndexCoverage (one axis, two variants; a single-variant coproduct is read as a type alias and does not export its name), both carriers carry it per row, and each projects citable refs, outside-index refs, and a COUNT — never a silent subtraction. Six witnesses, and the honesty guard is mutation-tested rather than asserted: swapping a resolvable row to outside-index and an outside row to citable keeps the count at 2, so the counting witness stays green and both honesty witnesses go red. Marking a resolvable ref outside-index to dodge a red cannot pass. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…-citation-enrollment # Conflicts: # dag/gunbc/lambda_argument_typing_gate.dag
Rung correction on this PR's own evidence, before it mergesThe body says the honesty guard is mutation-tested, and it is. It does not say clearly enough where that guard runs, and the answer changes its rung. All four new witnesses land in
That makes the honest rung mitigatable today, not mechanically preventable. A guard that discriminates but never runs is evidence, not enforcement, and calling it enforcement here would be the rung inflation DESIGN §4b names as worse than sitting low. Next-rung trigger, already in flight: I am deliberately not duplicating their guard here. Two implementations of one check is the fork §3 forbids, and the one in the executing phase is the one that counts. Also settled since this PR opened, by — sent from wise-boar-649 |
… type drifted
dag/std/decl_ref.dag is in the v1 seed closure, so adding the coproduct
drifted its stage0 mirror. CI reported it twice from one cause:
required-ci: regen FAIL generated surface drift: std_decl_ref.rs
behavioral-receipt: std.decl_ref REFUSED — seed: the driver did not compile
against the mirror: could not find `CitationIndexCoverage` in
`std_decl_ref`; cannot find function `citation_is_outside_index`
Emitted by claim_executor --required-regen and installed from the candidate
tree, that ONE file only — the candidate also carries a Cargo.toml and omits
bin/, so a whole-tree copy-back would contaminate hand-maintained files.
Regen's drift set was exactly one file, so no other mirror is implicated.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…-citation-enrollment
…use, not a copied one Adding per-site `cause` and `cause_receipt` to LambdaTypingGateSite made every existing literal incomplete, and the floor refused exactly one: dag/test/claim/long/cited_symbol_resolution_witness_test.dag's planted red control, landed by #8775 after this branch's type change was authored. That refusal is the fail-closed census working -- the widened type surfaced its one real dependent loudly rather than silently defaulting it. The planted site is SYNTHETIC: it names a declaration that does not exist, so no probe can have measured why it fails to type and there is nothing for a receipt to point at. It therefore carries SiteCauseUnmeasured / NotYetProbed, and an annotation says why it must stay that way -- copying a real site's cause here to make the literal look complete would put a measured attribution on a specimen no run produced, which is the fabricated-receipt failure the receipt field exists to prevent. Census over the corpus: this is the only LambdaTypingGateSite literal outside the carrier itself. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…lta, so #8758's carrier gets a corrected cause (two roots, per-site) and the four-name roster goes as cleanup (#8772) * wip: lambda_argument_scope (roster removal + per-arg lambda scope) * Correct #8758's carrier: the mechanism it names is refuted, the sites and the derivation stand The row asserted a cause -- emit_typed_call's four-name collection_scope gate produces the three E0282 sites. Two-arm emission over three closures, from two binaries whose difference was confirmed before either arm ran, is byte-identical: 258 files per arm, 0 differing files, 0 differing lines. A third arm deleting the binding entirely is also identical, which is what separates "refuted" from "my replacement happens to agree with the roster". The mechanism cannot reach emitted bytes: scope.locals has exactly one reader in the Rust emitter (is_already_optional) on the arm taken only when a node carries no resolved type, and the declared parameter types the gate was meant to supply are already bound onto the lambda's param nodes one stage earlier by infer_expr's ExprCall/ExprLambda path. THE DISSOLUTION TRIGGER WAS THE DANGEROUS HALF. It keyed on repairing that gate, so it was exactly satisfiable by a change that fixes nothing -- delete the roster, retire the row, three sites still red. A false cause misleads a reader; a false trigger disposes of the evidence. The replacement keys on the three sites emitting and compiling without an inference failure, and says outright that no repair to the gate satisfies it. Not over-retracted: the three sites, the E0282 x E0061 intersection that derived them, and UpperBoundPendingResidueCheck with its standing prediction all survive untouched -- none depended on the cause. The surface is still a bare-name-keying shape, now at the honest strength: syntactic, with no observable consequence. The reach figure is deleted rather than softened; it sized the blast radius of repairing a mechanism that changes no byte. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * Per-site cause on the lambda-typing-gate row, and finish #8597's map_keys conversion CARRIER: cause becomes a PER-SITE field, because the sites and the causes were established by different instruments with different groupings. The three sites were derived together by one intersection over one subject; the causes were established one site at a time by three cargo probes over three emitted closures. A single row-level CauseAttributed would force one instrument's grouping onto the other's -- the same collapse this row was already corrected for once. parse/parse_lexeme_digest StringDeclarationSiteAlias (help annotates ch) target_model/...apply_callee UntypedFoldInitAccumulator (help annotates acc) tokenize/lex_match_prefix StringDeclarationSiteAlias (help annotates a) Three real reds, TWO causes. Row-level CauseUnlocated is retired rather than answered, replaced by CauseGrouping = CausePerSite. The dissolution trigger now states that no single repair can fire it. The discriminator is free and sits in output everyone reads past: rustc's help names WHICH parameter it wants annotated. Element means the collection could not supply T; accumulator means the fold's init could not supply its own type. Also recorded: a pre-registered hunch that was REFUTED (field access on the lambda parameter was named in advance as the first place to look, and rustc's caret is on acc, never on row), and a warning that four E0282s in the parse crate belong to the held contains class so a count there is not a population. MAP_KEYS, NOT MINE AND SAID SO: the witness floor refuses on trait_derive_supplemental_generic_bound_contract_test.dag with "method 'keys' not found on receiver type 'Map(...)'". Confirmed pre-existing on main at c603c06 -- same file, same two lines, same message, on a commit unrelated to this branch. #8597 converted six sites in that file to map_keys and left two behind. This applies the same conversion to the stragglers; it repairs a main-wide red, not a defect this branch introduced. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * Retract 'closes a real leak' in the emitter comment: the same null refutes it The per-argument narrowing was described as closing a real leak -- one lambda's parameter names visible while emitting sibling arguments and filled defaults. That is a mechanism claim, and the measurement in this PR refutes it exactly as it refutes the roster: scope.locals is read once in the whole Rust emitter, on an arm taken only when a node carries no resolved type, so nothing measured shows the leak producing a wrong byte. It is now stated as construction cleanup with no measured current consequence -- narrowed because the narrow form is the correct construction, not because a defect was observed, and unearned as a repair until a fixture makes it observable. A change whose whole point is that an unmeasured mechanism claim was wrong must not carry a second unmeasured mechanism claim in its own text. Same defect, one level down. Caught by external review, not by me. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * Correct the map_keys attribution: the two .keys() lines are #8749's, not #8597's Commit bbf4350 in this branch blamed #8597 for the two surviving `.keys()` call sites. That is wrong, and a commit repairing a main-wide red while misattributing it puts a false fact in the durable record beside a true fix. Verified per-commit on the file rather than taken on trust: #8726 dd1ac18 +6 map_keys lines, +0 .keys() lines #8749 8077603 +0 map_keys lines, +2 .keys() lines <- introduced both #8597 aa8a65e +0 in either direction, touched neither THE MECHANISM IS ALSO NOT WHAT bbf4350 SAID. It described an incomplete conversion -- six done, two missed. It was not. #8726's sweep was COMPLETE when it ran: it converted every site that existed at that point. #8749 then introduced NEW uses of the retired spelling, from a branch that predated the deletion. No census over #8726's tree could have found them, because they did not exist yet. So the class is REINTRODUCTION AFTER A SWEEP, not a missed census, and it is a merge-time question rather than a branch-time one: neither branch is wrong on its own, and the defect exists only in their union. Nothing guards that today. This two-line repair is therefore the right SCOPE and explicitly NOT the root. It clears a main-wide red; what stops the next reintroduction is unbuilt. Caught by quick-lynx-620 pushing back on the attribution I was given. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * File the second cause as its own row: untyped fold-init accumulator The target_model site in lambda_typing_gate_sites has a distinct cause with a distinct repair, so it gets a row. empty_map() in a fold's `empty:` position emits as v1_rt::rc_empty_map::<_, _>(); the only thing that could close those type parameters is the fold's own closure, which RECEIVES the accumulator -- so the target compiler is asked to infer a parameter's type from a body that uses it, and refuses. What rules out the neighbours, since this site was first filed under one: the collection is a genuine List<TargetEffectCalleeRow>, rustc never asks about `row`, and `acc` traces to the init rather than to the collection or the callee's spelling. The discriminator is rustc's own help text naming which parameter it wants annotated -- |acc: Type, row| here, |acc, ch: Type| for the collection-realization class. POPULATION IS ObservedSitesOnly. One measured site is not a census: this one was found only because it sat in a partition being probed member by member, and no sweep for untyped empty-collection literals in fold-init position has been run. The sweep is named rather than performed so the absence is a stated gap, not an implied zero. The site STAYS LISTED in lambda_typing_gate_sites with a cross-reference, because that row records that the three were derived TOGETHER by one intersection over one subject. Removing a member because its cause turned out to differ would destroy the only artifact of that derivation. Trigger is keyed to the site compiling, not to any repair -- the sibling row learned that the expensive way. It also refuses the cheap escape: annotating the closure at the call site moves the obligation to the author instead of deriving it. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * The census refused a synthetic control literal: give it the honest cause, not a copied one Adding per-site `cause` and `cause_receipt` to LambdaTypingGateSite made every existing literal incomplete, and the floor refused exactly one: dag/test/claim/long/cited_symbol_resolution_witness_test.dag's planted red control, landed by #8775 after this branch's type change was authored. That refusal is the fail-closed census working -- the widened type surfaced its one real dependent loudly rather than silently defaulting it. The planted site is SYNTHETIC: it names a declaration that does not exist, so no probe can have measured why it fails to type and there is nothing for a receipt to point at. It therefore carries SiteCauseUnmeasured / NotYetProbed, and an annotation says why it must stay that way -- copying a real site's cause here to make the literal look complete would put a measured attribution on a specimen no run produced, which is the fabricated-receipt failure the receipt field exists to prevent. Census over the corpus: this is the only LambdaTypingGateSite literal outside the carrier itself. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Brian Searls <briansearls1@gmail.com> Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
…enty-seven witnesses that were never its DESIGN authorised this cut and got its population wrong, which is the finding rather than a footnote. The row read "v2.lens.cited_symbol_resolution and its sixteen witnesses are not deleted -- they are unreachable from any required check and are dead rather than competing". Both halves of that population claim are false, and this change corrects the row in the same diff that falsifies it. SIXTEEN WAS THE COUNT AT #7707, when the lens landed. The file grew twice after (#8673 enrolled roster_registry, #8775 the two instance-gap carriers) and held 27 `test fn` identities. `gunbc.ci_layer_roots` said "eighteen". Three numbers, three snapshots of a growing file, none of them current -- the transcribed-measurement class the standing rule already names. The replacement rows name their subject and no number. AND "DEAD" WAS TRUE OF THE LENS AND FALSE OF A THIRD OF ITS WITNESSES. Measured against the seven symbols that file imported FROM the lens, 6 of the 27 touch one. The other 21 do not. "The lens and its witnesses" was never one population, and the three-way split is: 6 LENS-BOUND -- die with it. The census-green claim, the three planted-control rows, the enrolled-population exemption identity, the ambiguous control. `declaration_index` re-derives this machinery as PLANTED_CONTROL_CITATIONS plus PlantedControlNoLongerRefuses. 6 RESOLVER-BOUND -- MOVED, not deleted, to test.claim.long.decl_ref_resolution_witness_test. They call `resolve_declaration_ref`, which lives in `v2.std.decl_ref_resolution` -- a module that SURVIVES with four other consumers -- and they are the only rows in the tree that execute its five-arm refusal. Deleting them because they sat in a file named for the lens would be the §4b(4) failure exactly: a climb deletes the redundant PRODUCTION machinery, never the discriminating RED and positive control. 15 CARRIER-BOUND -- MOVED to test.claim.long.carrier_reference_integrity_witness_test. Population and projection claims about the four carriers that PROJECT DeclarationRefs. Their resolution half is subsumed; their population half is subsumed by nothing. SUBSUMPTION IS SCOPED, not general: `declaration_index` extracts TYPED-LITERAL citations -- DeclarationRef record literals and the decl_ref/decl_field_ref constructors -- and reports computed reference fields as a coverage boundary. A prose reference inside a String is covered by nothing, before or after, and this change does not claim otherwise. THE PER-PR WITNESS IS RENAMED, NOT DELETED. test.claim.cited_symbol_resolution_witness_test never imported the lens; its one claim is a three-term bucket partition over gunbc.doc_graph_roots. Two readers independently concluded from its NAME that the resolution law was enforced per-PR -- it was not, a bucket identity was -- and after this cut it would have been the only cited-symbol-named thing left in the tree, reading as the law's residue. It is now test.claim.doc_graph_reference_partition_witness_test. Same borrowed-authority shape as #9252's fixture rehome, one layer up: there the home was borrowed, here the name. WHAT THE WALL ITSELF NAMED, because this was cut delete-first and the census is the deletion. The first corpus run after the cut reported six refusals: two IMPORT-MEMBER-ABSENT for a `CitedSymbolResolution` lens-id the registry no longer declares, and four PLANTED-CONTROL-RESOLVES for the exact rows #9211 declared as this cut's residue ("they delete with the lens, not before it"). Every one predicted, none discovered by reading. AND ONE GAP THE ROSTER DELETION WOULD HAVE OPENED, which is why those four rows are not simply removed. Each named one refusal arm of the cited-symbol wall. Three of the four arms already had controlled fixtures in tests/declaration_index_integrity.rs. THE FOURTH DID NOT: measured, the string `CitedFieldAbsent` did not occur in that file at all, so its only evidence anywhere was the planted row I was deleting. Deleting it would have left a refusal arm with nothing executing against it -- §4b(4) again, one level up from where I first hit it. `citation_to_an_absent_field_is_refused_and_a_present_field_is_not` is authored here, with its positive control, and a controlled fixture is the stronger oracle anyway (§5): the planted row only ever asserted that one hand-authored citation still refuses. PLANTED_CONTROL_CITATIONS is left EMPTY rather than deleted. Mechanism reachable, join reachable, occupancy zero -- a healthy guard being quiet, not a dead one. EVIDENCE, by execution (remote, release binaries): before after parse-clean files 4012 4011 the lens module modules 4012 4011 citations 1470 1466 the lens's four planted citations lens_modules 71 70 debt 42 42 UNCHANGED corpus findings 0 0 declaration_index_integrity 21 passed 22 passed the new field-absent pair THE DEBT COUNTS RECONCILE, and the brief's "46" is none of them. 38 = roster rows, counted. 46 = citation SITES at the time the roster's doc comment was written. 42 = citations currently suppressed by a row, the live measurement. debt is 42 before and after, and a row that stopped reproducing refuses as CitationDebtRowStale -- none did, which is the executable form of "the defects are still found". Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…ction, cli_invoke builders, walk_plan_stage fixture family) (#9286) * Delete the .dag residue of the plan/walk CLI surface, and give the scope-disposition witness a fixture home it owns #9228 deleted claim_executor's plan/walk surface and named its .dag residue rather than sweeping it. This is that cut, plus one repair the census surfaced. WHAT WENT, AT THE ROOT: PlanFunction and the plan argv builders. The coproduct modelled a closed roster of --plan-function targets; the flag no longer parses and the entry every variant named (src/v2/workflow/ci_floor_plan.dag) was deleted by the 2026-08-15 floor cut. Gone with it: claim_executor_run_plan_transport_argv, claim_executor_run_plan_shell, the notice-title normalizer and shell suffix that existed only to feed them, claim_executor.Executor.RunPlan, ci_spec's scheduler_invoke/scheduler_invoke_with/ floor_plan_entry/floor_plan_function/plan_artifact_plan_function, and gunbc_ci_floor_only_script. The --verify-build-artifacts half is UNTOUCHED and deliberately so: it is a live mode (fleet-converge.yml), so claim_executor_verify_artifacts_shell, claim_executor_bin_shell, release_bin_shell_path and SourceRootShellStyle all stay. cli_invoke's dissolve trigger is NARROWED to name only what survives rather than deleted, since the shell-vs-argv fork it records is still open for that one spelling. gunbc_ci_run_script emitted the release build AND a claim_executor --plan-entry line. The second half is deleted, not repointed at --required-ci: witnesses.yml already invokes that, and a second route to it here is the parallel authority the floor cut removed. The walk_plan_stage fixture family, whole: 11 fixture modules, the 379-line #[ignore] harness, its scaffold row and witness, and the seed_retention_frontier retained_test_harness row. Their sole driver was the plan.dag recipes #9228 deleted. v2.workflow.required_floor fixture_home_prefixes() and RequiredFloorDisposition:: DeclinedFixtureMember, with the cli_run.rs decode, branch, counter and TSV column. That arm's roster was one prefix and the family above was its entire population; its own header said "DISSOLVES when the fixture stops authoring test fns", and this is that condition. Coordinated with sleek-carp-211, who is modelling the enum in #9246 and asked for both sides deleted here. The pre-push witness-corpus gate. Not on the brief, found by the flag census: pre_push.rs built claim_executor and invoked --plan-entry/--plan-function on the deleted floor plan entry. Its EMISSION died 2026-07-25 when the operator made the hook fmt-only; its INVOCATION died 2026-08-25 with the flags. Two witness rows asserting "a .dag push arms a gate" are deleted rather than weakened -- measured against the roster, the corpus binding was the only thing making them true, so they were green against the plan and false about the hook anyone runs. THE REPAIR THE CENSUS SURFACED (tools.dag_compile_clean_scope): Three walk_plan_stage files were pinned as the roster and pool of the SCOPE DISPOSITION witness, which has nothing to do with plan/walk. Its own note records that these same specimens already moved once for exactly this reason -- from test/fixture/floor_skip, which died with affected-set selection. This would have been the third home. They are rehomed to src/v2/test/fixture/compile_clean_scope/, a home this witness owns: three modules, no test fn, no effects, one consumer. No discovery exclusion is needed because there is nothing to discover, which is a stronger construction than the dir-grain exclusion the old home required. AND THE PROPERTY THE NOTE CLAIMED IS NOW ASSERTED. The expectation was ExpectScopedContaining -- MEMBERSHIP -- so a selector returning the whole roster satisfied every row and the "strict-subset proof" the note describes was checked by nothing. ExpectScopedExactly compares the selected list to the expected list. EVIDENCE, by execution on a release gunbc (remote, one dispatch): control witness_touched_path_dispositions_hold -> true mutation give scope_isolated an import edge to scope_shared -> false The mutation is exactly the strict-subset violation; the old assertion could not see it. Rust: cargo check -p v1-compiler --bins clean, fmt clean. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * Repair the scope-disposition witness's per-PR half, which the floor caught dag/test/claim/dag_compile_clean_scope_witness_test.dag imports ExpectScopedContaining and pins the disposition row count. Both moved with the rehoming and I checked only the long-lane witness, so strict preparation refused with a name-resolution error before any site ran. Fixed at both ends: the import drops the deleted variant (ExpectSkip went with it -- it was imported and never used), and the pin goes 7 -> 8, which is the roster growing by one row because the strict-subset proof needs three specimens where the old home carried two. The pin doing its job here is the argument for keeping it: a count that had to be updated by hand is exactly what stopped this rehoming from silently shipping a roster of a different size than the one the note describes. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * Delete the `gunbc ci` verb rather than leave a command named ci that greens without running CI REQUEST_CHANGES from codex/gpt-5.6-sol (review 56054), and the finding is correct. WHAT I BROKE. This branch narrowed gunbc_ci_run_script to ci_release_build_script() alone, because its other half emitted a `claim_executor --plan-entry` line naming an entry the floor cut deleted. But `gunbc ci` is a real CLI subcommand, so what survived was a callable verb that builds the release binaries, verifies the artifacts, and exits 0 -- under a name that says it ran CI. That is fail-open semantic dilution: the failure mode is not a wrong answer, it is a CORRECT answer to a much smaller question, reported under the name of the larger one. §5's absorbing-fallback rule is about a failure arm that widens; this is its mirror at the success arm, a green that narrowed. WHY DELETED AND NOT REBOUND. Binding the verb to `claim_executor --required-ci` was the reviewer's other option and I am not taking it, on the grounds this branch already argued in the commit that caused the defect: witnesses.yml invokes --required-ci, and a second route to it is the parallel authority the floor cut removed. The verb also has no distinct job left -- "build the release binaries and verify the artifacts" already has a name, ci_release_build_script, and fleet-converge.yml already calls it. So the verb is not an authority that lost its body; it is a name with nothing left to denote. THE CENSUS, cut at the root and followed where it led: dag/tools/gunbc_ci.dag the entry module, deleted main.rs Commands::Ci + its arm the CLI verb gunbc.cli_dispatch_surface "ci" row the modeled CLI surface gunbc_cli_dispatch_surface.rs its generated mirror v2.workflow.ci_release_build_emit gunbc_ci_run_script, the wrapper std.emit_on_demand gunbc_ci_emission_surface the wet-surface row naming tools.gunbc_ci main emit_on_demand_kernel_witness_test its enrollment assertion wall_residue_live_test residue_gunbc_ci_clean a test fn whose subject was the deleted file ci_release_build_script itself is UNTOUCHED and still has three consumers (ci_materialization, fleet_workflow_steps, fleet-converge.yml). Only the wrapper goes. EVIDENCE, build lane on this tree (remote, one dispatch): required-ci: lane=build phases_run=2 failed=0 regen first_generation_equal=true -- the mirror edit is byte-equal to the emitter's own output, established by the gate rather than by my reading of the diff v2-emission blocking=0, census 3792 -> 3791, the one deleted module The emitter-gap witness still holds: gap_is_non_empty_while_the_divergence_row_stands needs at least one AbsentFromEmitMainRs row and 17 remain after this one goes. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * Delete v2.lens.cited_symbol_resolution, and keep the twelve of its twenty-seven witnesses that were never its DESIGN authorised this cut and got its population wrong, which is the finding rather than a footnote. The row read "v2.lens.cited_symbol_resolution and its sixteen witnesses are not deleted -- they are unreachable from any required check and are dead rather than competing". Both halves of that population claim are false, and this change corrects the row in the same diff that falsifies it. SIXTEEN WAS THE COUNT AT #7707, when the lens landed. The file grew twice after (#8673 enrolled roster_registry, #8775 the two instance-gap carriers) and held 27 `test fn` identities. `gunbc.ci_layer_roots` said "eighteen". Three numbers, three snapshots of a growing file, none of them current -- the transcribed-measurement class the standing rule already names. The replacement rows name their subject and no number. AND "DEAD" WAS TRUE OF THE LENS AND FALSE OF A THIRD OF ITS WITNESSES. Measured against the seven symbols that file imported FROM the lens, 6 of the 27 touch one. The other 21 do not. "The lens and its witnesses" was never one population, and the three-way split is: 6 LENS-BOUND -- die with it. The census-green claim, the three planted-control rows, the enrolled-population exemption identity, the ambiguous control. `declaration_index` re-derives this machinery as PLANTED_CONTROL_CITATIONS plus PlantedControlNoLongerRefuses. 6 RESOLVER-BOUND -- MOVED, not deleted, to test.claim.long.decl_ref_resolution_witness_test. They call `resolve_declaration_ref`, which lives in `v2.std.decl_ref_resolution` -- a module that SURVIVES with four other consumers -- and they are the only rows in the tree that execute its five-arm refusal. Deleting them because they sat in a file named for the lens would be the §4b(4) failure exactly: a climb deletes the redundant PRODUCTION machinery, never the discriminating RED and positive control. 15 CARRIER-BOUND -- MOVED to test.claim.long.carrier_reference_integrity_witness_test. Population and projection claims about the four carriers that PROJECT DeclarationRefs. Their resolution half is subsumed; their population half is subsumed by nothing. SUBSUMPTION IS SCOPED, not general: `declaration_index` extracts TYPED-LITERAL citations -- DeclarationRef record literals and the decl_ref/decl_field_ref constructors -- and reports computed reference fields as a coverage boundary. A prose reference inside a String is covered by nothing, before or after, and this change does not claim otherwise. THE PER-PR WITNESS IS RENAMED, NOT DELETED. test.claim.cited_symbol_resolution_witness_test never imported the lens; its one claim is a three-term bucket partition over gunbc.doc_graph_roots. Two readers independently concluded from its NAME that the resolution law was enforced per-PR -- it was not, a bucket identity was -- and after this cut it would have been the only cited-symbol-named thing left in the tree, reading as the law's residue. It is now test.claim.doc_graph_reference_partition_witness_test. Same borrowed-authority shape as #9252's fixture rehome, one layer up: there the home was borrowed, here the name. WHAT THE WALL ITSELF NAMED, because this was cut delete-first and the census is the deletion. The first corpus run after the cut reported six refusals: two IMPORT-MEMBER-ABSENT for a `CitedSymbolResolution` lens-id the registry no longer declares, and four PLANTED-CONTROL-RESOLVES for the exact rows #9211 declared as this cut's residue ("they delete with the lens, not before it"). Every one predicted, none discovered by reading. AND ONE GAP THE ROSTER DELETION WOULD HAVE OPENED, which is why those four rows are not simply removed. Each named one refusal arm of the cited-symbol wall. Three of the four arms already had controlled fixtures in tests/declaration_index_integrity.rs. THE FOURTH DID NOT: measured, the string `CitedFieldAbsent` did not occur in that file at all, so its only evidence anywhere was the planted row I was deleting. Deleting it would have left a refusal arm with nothing executing against it -- §4b(4) again, one level up from where I first hit it. `citation_to_an_absent_field_is_refused_and_a_present_field_is_not` is authored here, with its positive control, and a controlled fixture is the stronger oracle anyway (§5): the planted row only ever asserted that one hand-authored citation still refuses. PLANTED_CONTROL_CITATIONS is left EMPTY rather than deleted. Mechanism reachable, join reachable, occupancy zero -- a healthy guard being quiet, not a dead one. EVIDENCE, by execution (remote, release binaries): before after parse-clean files 4012 4011 the lens module modules 4012 4011 citations 1470 1466 the lens's four planted citations lens_modules 71 70 debt 42 42 UNCHANGED corpus findings 0 0 declaration_index_integrity 21 passed 22 passed the new field-absent pair THE DEBT COUNTS RECONCILE, and the brief's "46" is none of them. 38 = roster rows, counted. 46 = citation SITES at the time the roster's doc comment was written. 42 = citations currently suppressed by a row, the live measurement. debt is 42 before and after, and a row that stopped reproducing refuses as CitationDebtRowStale -- none did, which is the executable form of "the defects are still found". Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> --------- Co-authored-by: gunbc-ci-auto-heal <gunbc-ci-auto-heal@users.noreply.github.com> Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com> Co-authored-by: Brian Searls <11205878+briansrls@users.noreply.github.com>
What this is
The widening
v2.lens.cited_symbol_resolution's own law names as its next step: enrollment widens to every structural DeclarationRef carrier. Both instance-gap carriers project their refs into that lens's production population, resolved byv2.std.decl_ref_resolutionunchanged — no second resolver, no new authority.Landed under an operator ruling ((e), after (a)–(d) were each rejected — see Why not simply exclude below).
The enrollment caught a defect on its first execution, in a merged carrier
Two of
gunbc.list_membership_operation_gap's five rows (merged as #8735) cited the filename —test.claim.attribution_span_witness_test— where the module declares itself without the_testsuffix.That carrier passed four reviews, one of which verified its citations by hand. A name-level grep passes on both rows because the declaration names were correct and only the address was wrong. Hand-verification and grep both certify a citation that resolution rejects.
The spelling fix was necessary and insufficient
Per-ref probe, one process per ref, with positive controls:
v2.compiler.parse.parse_lexeme_digestv2.std.compilers.target_model.target_value_expr_effect_apply_calleev2.compiler.tokenize.lex_match_prefixgunbc.publication_policy.known_publication_rootsgunbc.session_auto_publish.write_set_refusalsextdeps.unicode.blocks.zero_width_codepointstest.claim.attribution_span_witness.unattributed_widthstest.claim.recorded_observation_envelope_witness.active_idsRoot cause, established with controls so a FAIL means more than an unsatisfied predicate: the modules are in
module_declaration_facts_live(control:gunbc.publication_policylikewise), and there is nodecl_factsrow from those files at all (control:dag/gunbc/publication_policy.dagrows are present).v2.std.decl_indexdecl_factsskips test.dagfiles deliberately —gunbc.ci_layer_rootsrecords the exclusion in the row explaining why a second walk must not skip them.Those two citations are true and unanswerable at once: DESIGN §4b's outside-the-modeled-guarantee column.
The shape
std.decl_refgains, besideDeclarationRefbecause it is a fact about references generally:One axis, N variants — not a wrapper around a nested class enum. A single-variant coproduct is read as a type alias and does not export its variant name; measured, not assumed.
Both carriers carry
index_coverageper row and project three things: citable refs (what enrolls), outside-index refs, and a count. Never a silent subtraction.The lambda carrier's rows are all citable today and it carries the filter anyway — a carrier that grows the filter only when it first needs one has a green that silently changes meaning that day.
Why not simply exclude the two rows
Because that green would mean "nothing left to check". This one means "N resolved, M outside the index, here is which and why". The difference is whether the number stays readable, and it is the operator's stated condition: an uncounted disposition degrades into an exemption list.
The guard that enforces it —
instance_gap_outside_index_refs_really_do_refuse— asserts the refs marked outside-index actually refuse, in exactly the claimed count. A partition witness holdscitable + outside == total.Verification
Six witnesses PASS. The guard is mutation-tested, not asserted: swap a resolvable row to outside-index and a genuinely-outside row to citable — chosen because it leaves the count at 2, so the counting witness cannot see it.
So marking a resolvable ref outside-index to dodge a red cannot pass, and the counting witness is measured insufficient alone rather than assumed to be. Mutated file restored byte-identical before commit.
Known-red, pre-existing, not from this change
cited_symbol_resolution_lens_is_green_on_live_corpusfails on unmodified main — established by stashing all four files and re-running clean. It is 27 violations on main, of which 4 are this same outside-index class;quiet-lark-881owns that lane and will consume this shape rather than mint a second one. All 16 witnesses in that file areOfflineLocalRecipe, so nothing runs them in CI today — which is why the lens could sit red unnoticed.— sent from wise-boar-649