Repository navigation
BL-2: split the subject roster from the execution batch, delete the aggregate helpers - #8208
Merged
Merged
Conversation
OtherAssembleRefusal is the catch-all arm of AssembleRejectionCause, and it carried only a reason symbol. A reason symbol does not say WHICH file refused, so recovering that meant re-lexing an entire import closure by hand — measured on use_site_verdict, whose 15-file closure had to be folded through tokenize one source at a time to learn that dag/std/algebra.dag was the refusing file. The location is carried as a probe-owned projection rather than a bare Locus. Locus admits NodeLocus and PortLocus, which have no manifest serialization; a host emitter that errored on them would abort a 27-module survey over one unrepresentable position. FrontierProbeCauseLocation is serializable by construction and total, and its unavailable arm carries a typed reason so the deficit is counted rather than fabricated or fatal. Location is kept structurally separate from the cause key: two modules refusing for the same reason at different positions are the same detailed cause, so a clustering key reads the cause and not this field. The survey binary is the third consumer and is updated with it — the DAG carrier change alone would have emitted a manifest that no longer typechecks against the new field, which is worse than dropping it. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…e its variants
The location was a field of OtherAssembleRefusal, which made a position
structurally part of what the refusal IS: two modules refusing for the same
reason at different positions read as different detailed causes at a
clustering key. It now sits beside the cause on AssembleRejectionDetail.
CauseLocationUnavailable { reason: Symbol } is replaced by the closed pair
CauseLocationPort { port } and CauseLocationNodeNotSerializable. A PortLocus
carries real serializable data and no longer degrades to a reason string;
only a Node, which has no manifest rendering, reaches an unavailable arm.
The survey emitter follows the field and the arms, and stays total.
Controls (all executed): each of the three Locus arms reaches its own arm;
the byte range is asserted exactly, so substituting WholeFile reds; and a
two-diagnostic control pins reason and location to the SAME diagnostic
(RED verified by asserting the tail's offset).
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The fixture was hand-edited to the reshaped carrier, which is exactly what its own note forbids: a hand-written approximation tests the author's idea of the format instead of the emitter's. These bytes are copied verbatim out of a survey run at commit 2880549, tree 932acf8, executable sha256 677c1bf0673c70ef… over src/v2/compiler/use_site_verdict.dag, and the reload half passes against them. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…eipts frontier_gap_clustering_from_receipts counted blocker CLASSES and then wrote a hardcoded located_reason literal into whichever branch fired, so the verdict reported a reason no receipt had to carry: the one real survey measures ^tokenize_lex_e1_unrecognized_char, which no branch could name. Its HeterogeneousGaps arm reported module_count as the distinct-reason count, which is a count of modules wearing the name of a count of reasons. And count_receipts_with_located_reason, the function that reads the measured axis, had zero callers. Both numbers are now read off the receipts. CommonRoot requires unanimity rather than a plurality, because any plurality bar is a threshold and a threshold is the smuggled heuristic a closed system never needs. An empty survey reports a distinct count of zero rather than becoming a common root. Controls (executed, both REDs verified): a unanimous survey reports the measured reason and counts the receipts carrying it; a mixed survey reports 2 distinct reasons over 3 receipts, where the old arm reported 3. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
These eight files are the one-off measurement instruments behind the A/B finding — the real-closure door probe, the cause and reason readouts, the lex localizer and three tokenize fixtures. They were never meant to land; the PR description says so explicitly. A blanket 'git add -A src/v2' put them in the tree anyway, which is experimental residue and a description that no longer described the diff. They stay local, excluded via .git/info/exclude so the next add cannot repeat this. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The tally ran unconditionally and its result was discarded on the heterogeneous arm, which needs the distinct count alone. Cheap at survey scale, but a copied fold whose output is thrown away is a cost-shape defect regardless of the realized n, and 'n is small here' is not a time-stable fact. Found by review 51421. All four clustering claims still pass by execution. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
MEASURED, not hypothesised. src/v2/compiler/03_resolve.dag needs 71 source reads against the manifest transport's 64-row inline cap, so the manifest emits SourceRootManifestElided with produced_row_count 0 and an Empty ingest. Handed that, frontier_probe_emit_from_ingest answered ^resolve_module_not_found with cause MissingModuleIdentityUnavailable — a survey row asserting the module is missing when the truth is that its sources were never supplied. Two states, different remedies, and the one reported was the plausible one. frontier_probe_coverage_gate already classifies an elided manifest correctly, but it guards only the discovery path; this entry receives the ingest directly and never consults coverage, so the conflation was reachable from every from_ingest caller. An empty ingest is now a typed, located, counted population refusal naming ^frontier_probe_empty_ingest. Residue, declared rather than closed: emptiness is decidable here, coverage is not, because the entry is not given the SourceRootCoverage — a PARTIAL closure still answers. Dissolve-on is on the carrier: route the coverage receipt into this entry so the refusal becomes an arm of the existing gate. Four claims green by execution; RED verified by disabling the wall, which reds three of them and restores the module-not-found answer. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…tern Every other match site was updated when the field landed; this one kept the two-field pattern. Review 51431 flagged it. The predicted typecheck failure does not occur — the file's five claims were green by execution before this change and are green after — so the language admits the partial pattern. That is the reason to fix it rather than to leave it: a pattern that silently keeps matching when the carrier grows a field is how a match stops covering what its author thought it covered. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Review 51432 is right that this claim's behavior against the checked-in stub changed. What it was before is the part worth naming: with receipts Empty, clustering asked self_emit_ready_count == module_count, both were 0, and it answered CommonRoot with dominant_located_reason ^probe_stage_emit_ok — a survey with no receipts reporting that every module reached emit. The one claim whose subject IS the clustering verdict was passing vacuously on a fabricated verdict. Receipt-derived clustering answers HeterogeneousGaps for an empty survey, so the claim now reds on the stub. That is the same state its siblings were already in and is the file's declared offline condition, not a CI failure shape: compiler_frontier_per_module_probe_survey_holds compares the receipt count against the 27-row roster, and 0 is not 27. The length check makes THIS claim's red say 'no receipts' explicitly rather than leaving it to read as a clustering disagreement. Verified by execution that the conjunction is false on an empty survey rather than erroring. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The observer carried two arms over three reachable states, and the missing arm failed OPEN: every Rejected outcome fell to the else branch and reported BodyLoweringLowered. The instrument used to measure the body-lowering gap was counting failures as successes, so any population read through it undercounted by exactly the refusals. That arm is reachable by construction, not in theory: the rejection- propagation repair deliberately made body_lower_finish_for_normalize propagate genuine rejection through rejected_with_pending, and this classifier then discarded the distinction it created. Recognition had the same fail-open by a second route. It read the diagnostic HEAD alone, while retention travels through diagnostics_merge and rejected_with_pending — both of which place OUTER diagnostics first — so a retained body behind any pending diagnostic also read as lowered. It now scans the whole list. Six claims green by execution. RED verified by restoring the previous classifier: the rejection claim, the rejection-diagnostics claim and the behind-a-pending-diagnostic claim all red, while the three negative controls stay green — so the claims pin these two defects rather than failing broadly. The controls are the load-bearing half: a classifier that answered retained more often would satisfy the positive claims and destroy the count. Two existing consumers gain the third arm. Residue declared on the carrier: recognition still reads an encoding rather than a producer-returned variant, correct for every encoding the single retention producer can emit, with dissolve-on naming the producer change that makes this function the identity. First step of the sequence gunbc.roadmap_authority already declares: honest frontier, then the normalized-tree construction boundary, then the retained population to zero. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…ation probe The stage classifier and reconciliation machinery already existed in gunbc.tools.frontier_ingestion_probe, hardcoded to the 15-member std ingestion roster. Measuring a second population needed the roster to be an argument rather than a second copy of the mechanism — two classifiers are two authorities that can disagree. take_census_over(rows) is the general form; take_frontend_census() is now that call with the module roster. Roster identity derives from the rows PASSED, never the module-level roster, so a receipt cannot claim a denominator it was not measured over — the exact misreading census_receipt_provenance_note exists to prevent, which a parameterised census stamping the default identity would have reintroduced. All 14 existing claims over the instrument still pass. The new probe measures the body-lowering population: the two SH-D shards with measured door receipts, the std members the pre-repair observation recorded as retained plus the one it never re-ran, and the seam's own modules. It is deliberately not the whole corpus, and it carries no aggregate pass/fail — the population IS the finding, and a Boolean over it would be a verdict on subjects not separately established. WHY BEFORE THE WALL: normalize returns Accepted for a tree whose diagnostics carry wrapper-retention, and resolve consumes it. Making retention unconstructible changes which modules normalize accepts, so the newly refusing population must be known before the wall lands rather than discovered from a red tree afterwards. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Executed, three-member roster: use_site_verdict NORM_ACCEPTED, materialization_carriers NORM_OTHER, optional NORM_RETAINED. That corrects the shard rows rather than the finding. Neither shard retains as a file — use_site_verdict normalizes clean — so the wrapper-retained residue measured in their door chains comes from OTHER modules in their import closures, of which optional is one confirmed instance. 'The shard is body-lowering blocked' was too coarse: the shard is blocked because its closure contains retaining modules, and the repair subjects are those. The roster is three because ten was executed and ABANDONED at 2h51m and 8.3 GiB resident with no verdict, on a host whose shared 20 GiB slice is reaped largest-task-first — a hazard to other sessions before it was a slow measurement. The instrument's own note already owed a realized execution for exactly this reason. The seven dropped members are unmeasured, not excluded, and the carrier says so. The 10-to-3 shrink is nonlinear (2h51m to ~2min), so a dropped member is pathological rather than merely larger. Which one is NOT established, and guessing the largest file would be a guess dressed as a finding. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
added 2 commits
August 13, 2026 16:48
…ed text Two defects, both reachable only because an aggregate was authored over a roster this module's own note says carries no aggregate verdict. A refusal became an integer. count_stage_matching answered 0 - 1 for CensusRefusedAtWorld and 0 - 2 for CensusRefusedAtReconciliation, so a typed refusal and a genuine population count inhabited one type: a caller could add, compare or display a failure as a measurement, and -1 reads as a number rather than as a stop. The census already carries typed refusal arms, so this discarded exactly the distinction they exist to preserve. Presentation became semantic authority. The count compared stage_label_of(s) against a target String, but FrontendStage is a closed coproduct — a label rename would change the count with no stage changing, and a label collision would merge distinct stages. Counting belongs to the structural variant or to frontend_stage_eq, never to rendered text. Deleted rather than repaired: the module already states the per-member observation IS the finding and that no aggregate verdict is warranted over subjects that have not each been separately established. The helpers contradicted the stated scope, so there is nothing for a corrected aggregate to mean here. Int and Bool were left import-only and are dropped with them.
The previous revision declared three rows named body_lowering_population_roster while the module's prose said the subjects were ten. That made the planning error this instrument exists to detect representable in its own carrier: seven unmeasured subjects erased, one retained member observed, and a bare count licensing "the retained population is one". Two authorities now. body_lowering_subject_roster is the denominator, carrying exact identities cited rather than asserted — the two SH-D shards, and five std members from the executed table in docs/plans/v2-frontend-std-ingestion-frontier-exact-head.md (logic, optional, diagnostic, occurrence_identity as its NORM_RETAINED rows; node as its NOT RE-OBSERVED row). body_lowering_execution_batch is what one run selected, and it is DERIVED from the roster by label selection rather than re-authored beside it, so a batch row cannot name a path the roster does not. Standing is three states, because observed-or-absent cannot carry this population: a subject dropped for cost, a subject whose run a budget interrupted, and a subject genuinely measured are three epistemic positions with three remedies, and collapsing the first two into absence renders "we did not look" as "there is nothing there". SubjectInterrupted stays separate from SubjectUnmeasured because node's 900s overrun is a real lower bound on cost that an unmeasured subject does not carry; body_lowering_prior_standing gives that arm its producer from the cited historical receipt, declared apart from this run so the two can never be read as one measurement. The population is EIGHT, derived rather than chosen (operator ruling 2026-08-13). Seven exact identities plus one subject for the vacuity bare-expression specimen CLASS, which grounds as a class and not as members: the vacuity note describes "many isolated configurations" and enumerates none, and its optional_absent copy is the same subject as the rostered optional member. Minting three rows would have fabricated two identities to satisfy a prose sentence. The prose "ten" is recorded as retired rather than silently corrected, and the note states that eight is not an authority either — completeness is an identity join, not a count equality.
added 3 commits
August 13, 2026 16:58
Four blocking diagnostics, found by compiling the entry rather than by
inspection, and one of them is worth recording because it is a language-layer
trap rather than a typo.
A BRACE INSIDE A PROSE NOTE IS INTERPOLATION SYNTAX. The roster note described a
standing as SubjectUnmeasured{no_committed_specimen_identity}, and the compiler
read the braces as an interpolation and refused an undefined variable — inside a
String literal, at a position no reader would look for code. Escaped as \{, which
is the existing modeled form. This is the class DESIGN records from the
${...}-in-strings incident: the tempting move is to respell the prose around the
obstacle, and the modeled escape already exists.
The other three are empty list literals in fold seeds, which carry no element
type on their own. Written as [] as List<T>, the idiom already used by
frontier_ingestion_probe and normalize_retention_contract_probe_test.
Result: 0 blocking errors on dag/tools/body_lowering_population_probe.dag. The
248 remaining advisories are pre-existing and corpus-wide.
body_lowering_program_receipt was run against the live tree (73s) and returned eight standings. Recorded because compiling is not running, and the join is the part worth proving: eight subjects in and eight standings out establishes that no subject is dropped between roster and receipt, the three observed rows reproduce the earlier batch reading so deriving the batch from the roster did not change what executes, and the specimen class routes to no_committed_specimen_identity rather than to not_selected_into_execution_batch — the distinction the realization coproduct exists to make. The note also states what the run does not establish: the four unmeasured subjects are unobserved, not staged, and node's prior interruption is neither confirmed nor refuted here.
Merges main through 3c533ba rather than through its tip. #8228 landed at 9c5d007, so this drops the BL-0 files this branch was duplicating and reduces the diff to the BL-2 work alone. It deliberately stops one commit short. #8214 (9156b78) introduced in-body source annotations across 13 files, confirmed by compiling dag/std/affine_space.dag against the live tree, so merging main's tip today would import a known compile-clean red into a branch that is currently green — turning an unrelated lane's defect into this PR's failure. 3c533ba is the newest main commit whose tree carries neither the in-body nor the unattached annotation form. The remainder of main merges once that class is repaired.
Review on #8208 noted that stage_label_of paralleled frontier_ingestion_probe's frontend_stage_label and disagreed with it on one variant — NORM_ACCEPTED against ACCEPTED — and judged it non-blocking because it is presentation-only. Taking it anyway: it is a second renderer for one closed coproduct, and it had already drifted, which is the §3 fork rather than a style preference. This module's own note claims there is one stage classifier in the repository and not two; forking the label while importing the classifier honoured that in the half that was harder to get wrong. frontend_stage_label is imported and the fork deleted. Confirmed by execution that the divergence was real and is now gone: census_report prints "use_site_verdict ACCEPTED | materialization_carriers NORM_OTHER | optional NORM_RETAINED", where the forked renderer printed NORM_ACCEPTED for the same stage. The recorded first reading named stages by their rendered label, which would now be stale. It names them by VARIANT instead, and says why: the spelling belongs to the renderer, so a note pinning it goes stale the moment that renderer is edited — the same reason counting may not read labels. Five variant imports were left import-only by the deletion and are dropped; NormalizeRetained stays, since the prior-standing rows construct it.
added 3 commits
August 13, 2026 20:25
Review 51759 (REQUEST_CHANGES) found that body_lowering_program_receipt routed EVERY live-file subject through the census-refusal cause. Verified and correct: on CensusRefusedAtWorld, logic, diagnostic, occurrence_identity and node — four subjects this run was never going to execute — were rewritten from "not selected" into "the census refused". That is state-space conflation, and it undid the roster/batch split on the one path where the distinction is least visible. The two states have different remedies: a not-selected subject needs a bigger batch or a cheaper instrument, while a refused census needs the infrastructure repaired, so a reader deciding what to do next was misdirected. More generally it is a failure at one scope attributed to subjects outside that scope — the absorbing fallback wearing an error's name, manufacturing information about subjects nobody looked at. The refusal cause now reaches only selected subjects. A subject outside the batch keeps not_selected_into_execution_batch under refusal exactly as under success, because nothing about it was attempted and its standing does not depend on the census. The specimen class keeps its own structural cause under both. Proven by execution with a discriminating control, not by inspection. census_refusal_does_not_reach_a_non_batch_subject_RED returns false against the previous arm and true against this one; the other three assert that a batch member does receive the cause, that the cause is carried rather than fixed, and that the specimen class is unaffected. The arm takes a cause symbol as input, so it is reachable by ordinary call and needs no broken tree to observe.
…identity row Per-subject censuses (one subject per run) measured six of the seven runnable subjects. Two consequences, plus the reading that keeps them from being misread. THE STALE RECEIPT ROW. occurrence_identity measures NormalizeOther, not the NormalizeRetained the exact-head receipt records. That receipt is explicit that the member was never re-run after the repair, so the cell was unknown rather than confirmed — and it is now known stale. Corrected where the receipt lives rather than only in a message, because a stale cell in a cited authority is the failure that survives on the assumption that someone else checked it. logic independently reproduced what the receipt predicted for it, which is evidence the receipt was right about the mechanism and wrong only about the row it could not re-run. WHAT A STAGE LICENSES IS NOW DERIVED, not left to prose. Leaving NormalizeRetained is not evidence of repair: NormalizeOther is by construction neither retention nor a graft refusal, so it names where a module refuses and never why. ObservedReading projects that — ObservedClean for the one admitting stage, ObservedRefusedCauseNamed where the classifier identified the kind, ObservedRefusedCauseUnclassified for NormalizeOther alone. Derived from the stage rather than stored beside it, so it cannot disagree with the coproduct it reads. A READING EXISTS ONLY WHERE SOMETHING WAS OBSERVED. A first draft of reading_of_standing mapped SubjectUnmeasured and SubjectInterrupted onto the unclassified-refusal arm on the reasoning that all three leave the cause unknown. That is the census-refusal conflation again, in the module that just repaired it: unknown-because-unclassified and unknown-because-unattempted are different unknowns with different remedies. StandingReading keeps them apart. Nine witnesses, all executed. Two are discriminating controls for the classes above: NormalizeOther must not read clean, and an unmeasured subject must have no reading at all. node is not included in any of this. It was SIGKILLed at 346s while every other subject finished between 138s and 292s; the signal is observed and the cause is not established, so it stands interrupted with a fresh lower bound and carries no stage. The six completions sit within a factor of 2.1, so the earlier 2h51m ten-member run is unexplained by them, and node dying rather than finishing left node-is-the-outlier versus superlinear-in-roster-size open.
… can complete The earlier merge deliberately stopped at 3c533ba, one commit short of #8214, because that commit put in-body source annotations across 13 files and merging main's tip would have imported a known compile-clean red. #8235 moved all 87 body-grain annotations to module-item grain and main now carries none, so the reason for stopping short is gone and this takes the remainder. Recorded because the earlier stop was itself declared: a branch that resumes a partial merge should say what condition cleared, not silently catch up.
briansrls
pushed a commit
that referenced
this pull request
Aug 14, 2026
…t reach resolve (#8256) * Carry the refusal location on the catch-all assemble cause OtherAssembleRefusal is the catch-all arm of AssembleRejectionCause, and it carried only a reason symbol. A reason symbol does not say WHICH file refused, so recovering that meant re-lexing an entire import closure by hand — measured on use_site_verdict, whose 15-file closure had to be folded through tokenize one source at a time to learn that dag/std/algebra.dag was the refusing file. The location is carried as a probe-owned projection rather than a bare Locus. Locus admits NodeLocus and PortLocus, which have no manifest serialization; a host emitter that errored on them would abort a 27-module survey over one unrepresentable position. FrontierProbeCauseLocation is serializable by construction and total, and its unavailable arm carries a typed reason so the deficit is counted rather than fabricated or fatal. Location is kept structurally separate from the cause key: two modules refusing for the same reason at different positions are the same detailed cause, so a clustering key reads the cause and not this field. The survey binary is the third consumer and is updated with it — the DAG carrier change alone would have emitted a manifest that no longer typechecks against the new field, which is worse than dropping it. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * SH-D D0: move the refusal location onto AssembleRejectionDetail, close its variants The location was a field of OtherAssembleRefusal, which made a position structurally part of what the refusal IS: two modules refusing for the same reason at different positions read as different detailed causes at a clustering key. It now sits beside the cause on AssembleRejectionDetail. CauseLocationUnavailable { reason: Symbol } is replaced by the closed pair CauseLocationPort { port } and CauseLocationNodeNotSerializable. A PortLocus carries real serializable data and no longer degrades to a reason string; only a Node, which has no manifest rendering, reaches an unavailable arm. The survey emitter follows the field and the arms, and stays total. Controls (all executed): each of the three Locus arms reaches its own arm; the byte range is asserted exactly, so substituting WholeFile reds; and a two-diagnostic control pins reason and location to the SAME diagnostic (RED verified by asserting the tail's offset). Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * SH-D D0: regenerate the round-trip fixture from real emitted bytes The fixture was hand-edited to the reshaped carrier, which is exactly what its own note forbids: a hand-written approximation tests the author's idea of the format instead of the emitter's. These bytes are copied verbatim out of a survey run at commit 2880549, tree 932acf8, executable sha256 677c1bf0673c70ef… over src/v2/compiler/use_site_verdict.dag, and the reload half passes against them. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * SH-D D0: derive the clustering verdict's dominant reason from the receipts frontier_gap_clustering_from_receipts counted blocker CLASSES and then wrote a hardcoded located_reason literal into whichever branch fired, so the verdict reported a reason no receipt had to carry: the one real survey measures ^tokenize_lex_e1_unrecognized_char, which no branch could name. Its HeterogeneousGaps arm reported module_count as the distinct-reason count, which is a count of modules wearing the name of a count of reasons. And count_receipts_with_located_reason, the function that reads the measured axis, had zero callers. Both numbers are now read off the receipts. CommonRoot requires unanimity rather than a plurality, because any plurality bar is a threshold and a threshold is the smuggled heuristic a closed system never needs. An empty survey reports a distinct count of zero rather than becoming a common root. Controls (executed, both REDs verified): a unanimous survey reports the measured reason and counts the receipts carrying it; a mixed survey reports 2 distinct reasons over 3 receipts, where the old arm reported 3. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * SH-D: unland the local probe instruments swept in by git add -A These eight files are the one-off measurement instruments behind the A/B finding — the real-closure door probe, the cause and reason readouts, the lex localizer and three tokenize fixtures. They were never meant to land; the PR description says so explicitly. A blanket 'git add -A src/v2' put them in the tree anyway, which is experimental residue and a description that no longer described the diff. They stay local, excluded via .git/info/exclude so the next add cannot repeat this. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * Compute the reason tally only on the arm that consumes it The tally ran unconditionally and its result was discarded on the heterogeneous arm, which needs the distinct count alone. Cheap at survey scale, but a copied fold whose output is thrown away is a cost-shape defect regardless of the realized n, and 'n is small here' is not a time-stable fact. Found by review 51421. All four clustering claims still pass by execution. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * Refuse an empty ingest instead of reporting the module missing MEASURED, not hypothesised. src/v2/compiler/03_resolve.dag needs 71 source reads against the manifest transport's 64-row inline cap, so the manifest emits SourceRootManifestElided with produced_row_count 0 and an Empty ingest. Handed that, frontier_probe_emit_from_ingest answered ^resolve_module_not_found with cause MissingModuleIdentityUnavailable — a survey row asserting the module is missing when the truth is that its sources were never supplied. Two states, different remedies, and the one reported was the plausible one. frontier_probe_coverage_gate already classifies an elided manifest correctly, but it guards only the discovery path; this entry receives the ingest directly and never consults coverage, so the conflation was reachable from every from_ingest caller. An empty ingest is now a typed, located, counted population refusal naming ^frontier_probe_empty_ingest. Residue, declared rather than closed: emptiness is decidable here, coverage is not, because the entry is not given the SourceRootCoverage — a PARTIAL closure still answers. Dissolve-on is on the carrier: route the coverage receipt into this entry so the refusal becomes an arm of the existing gate. Four claims green by execution; RED verified by disabling the wall, which reds three of them and restores the module-not-found answer. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * Bind the location field in the last stale AssembleRejectionDetail pattern Every other match site was updated when the field landed; this one kept the two-field pattern. Review 51431 flagged it. The predicted typecheck failure does not occur — the file's five claims were green by execution before this change and are green after — so the language admits the partial pattern. That is the reason to fix it rather than to leave it: a pattern that silently keeps matching when the carrier grows a field is how a match stops covering what its author thought it covered. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * Make the clustering witness require a nonempty survey Review 51432 is right that this claim's behavior against the checked-in stub changed. What it was before is the part worth naming: with receipts Empty, clustering asked self_emit_ready_count == module_count, both were 0, and it answered CommonRoot with dominant_located_reason ^probe_stage_emit_ok — a survey with no receipts reporting that every module reached emit. The one claim whose subject IS the clustering verdict was passing vacuously on a fabricated verdict. Receipt-derived clustering answers HeterogeneousGaps for an empty survey, so the claim now reds on the stub. That is the same state its siblings were already in and is the file's declared offline condition, not a CI failure shape: compiler_frontier_per_module_probe_survey_holds compares the receipt count against the 27-row roster, and 0 is not 27. The length check makes THIS claim's red say 'no receipts' explicitly rather than leaving it to read as a clustering disagreement. Verified by execution that the conjunction is false on an empty survey rather than erroring. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * BL-0: give the body-lowering frontier its third state The observer carried two arms over three reachable states, and the missing arm failed OPEN: every Rejected outcome fell to the else branch and reported BodyLoweringLowered. The instrument used to measure the body-lowering gap was counting failures as successes, so any population read through it undercounted by exactly the refusals. That arm is reachable by construction, not in theory: the rejection- propagation repair deliberately made body_lower_finish_for_normalize propagate genuine rejection through rejected_with_pending, and this classifier then discarded the distinction it created. Recognition had the same fail-open by a second route. It read the diagnostic HEAD alone, while retention travels through diagnostics_merge and rejected_with_pending — both of which place OUTER diagnostics first — so a retained body behind any pending diagnostic also read as lowered. It now scans the whole list. Six claims green by execution. RED verified by restoring the previous classifier: the rejection claim, the rejection-diagnostics claim and the behind-a-pending-diagnostic claim all red, while the three negative controls stay green — so the claims pin these two defects rather than failing broadly. The controls are the load-bearing half: a classifier that answered retained more often would satisfy the positive claims and destroy the count. Two existing consumers gain the third arm. Residue declared on the carrier: recognition still reads an encoding rather than a producer-returned variant, correct for every encoding the single retention producer can emit, with dissolve-on naming the producer change that makes this function the identity. First step of the sequence gunbc.roadmap_authority already declares: honest frontier, then the normalized-tree construction boundary, then the retained population to zero. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * BL-2: parameterise the census roster, and add the body-lowering population probe The stage classifier and reconciliation machinery already existed in gunbc.tools.frontier_ingestion_probe, hardcoded to the 15-member std ingestion roster. Measuring a second population needed the roster to be an argument rather than a second copy of the mechanism — two classifiers are two authorities that can disagree. take_census_over(rows) is the general form; take_frontend_census() is now that call with the module roster. Roster identity derives from the rows PASSED, never the module-level roster, so a receipt cannot claim a denominator it was not measured over — the exact misreading census_receipt_provenance_note exists to prevent, which a parameterised census stamping the default identity would have reintroduced. All 14 existing claims over the instrument still pass. The new probe measures the body-lowering population: the two SH-D shards with measured door receipts, the std members the pre-repair observation recorded as retained plus the one it never re-ran, and the seam's own modules. It is deliberately not the whole corpus, and it carries no aggregate pass/fail — the population IS the finding, and a Boolean over it would be a verdict on subjects not separately established. WHY BEFORE THE WALL: normalize returns Accepted for a tree whose diagnostics carry wrapper-retention, and resolve consumes it. Making retention unconstructible changes which modules normalize accepts, so the newly refusing population must be known before the wall lands rather than discovered from a red tree afterwards. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * BL-2 first reading: neither SH-D shard retains as a file Executed, three-member roster: use_site_verdict NORM_ACCEPTED, materialization_carriers NORM_OTHER, optional NORM_RETAINED. That corrects the shard rows rather than the finding. Neither shard retains as a file — use_site_verdict normalizes clean — so the wrapper-retained residue measured in their door chains comes from OTHER modules in their import closures, of which optional is one confirmed instance. 'The shard is body-lowering blocked' was too coarse: the shard is blocked because its closure contains retaining modules, and the repair subjects are those. The roster is three because ten was executed and ABANDONED at 2h51m and 8.3 GiB resident with no verdict, on a host whose shared 20 GiB slice is reaped largest-task-first — a hazard to other sessions before it was a slow measurement. The instrument's own note already owed a realized execution for exactly this reason. The seven dropped members are unmeasured, not excluded, and the carrier says so. The 10-to-3 shrink is nonlinear (2h51m to ~2min), so a dropped member is pathological rather than merely larger. Which one is NOT established, and guessing the largest file would be a guess dressed as a finding. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * BL-2: delete the aggregate helpers, which counted refusals and rendered text Two defects, both reachable only because an aggregate was authored over a roster this module's own note says carries no aggregate verdict. A refusal became an integer. count_stage_matching answered 0 - 1 for CensusRefusedAtWorld and 0 - 2 for CensusRefusedAtReconciliation, so a typed refusal and a genuine population count inhabited one type: a caller could add, compare or display a failure as a measurement, and -1 reads as a number rather than as a stop. The census already carries typed refusal arms, so this discarded exactly the distinction they exist to preserve. Presentation became semantic authority. The count compared stage_label_of(s) against a target String, but FrontendStage is a closed coproduct — a label rename would change the count with no stage changing, and a label collision would merge distinct stages. Counting belongs to the structural variant or to frontend_stage_eq, never to rendered text. Deleted rather than repaired: the module already states the per-member observation IS the finding and that no aggregate verdict is warranted over subjects that have not each been separately established. The helpers contradicted the stated scope, so there is nothing for a corrected aggregate to mean here. Int and Bool were left import-only and are dropped with them. * BL-2: separate the subject roster from the execution batch The previous revision declared three rows named body_lowering_population_roster while the module's prose said the subjects were ten. That made the planning error this instrument exists to detect representable in its own carrier: seven unmeasured subjects erased, one retained member observed, and a bare count licensing "the retained population is one". Two authorities now. body_lowering_subject_roster is the denominator, carrying exact identities cited rather than asserted — the two SH-D shards, and five std members from the executed table in docs/plans/v2-frontend-std-ingestion-frontier-exact-head.md (logic, optional, diagnostic, occurrence_identity as its NORM_RETAINED rows; node as its NOT RE-OBSERVED row). body_lowering_execution_batch is what one run selected, and it is DERIVED from the roster by label selection rather than re-authored beside it, so a batch row cannot name a path the roster does not. Standing is three states, because observed-or-absent cannot carry this population: a subject dropped for cost, a subject whose run a budget interrupted, and a subject genuinely measured are three epistemic positions with three remedies, and collapsing the first two into absence renders "we did not look" as "there is nothing there". SubjectInterrupted stays separate from SubjectUnmeasured because node's 900s overrun is a real lower bound on cost that an unmeasured subject does not carry; body_lowering_prior_standing gives that arm its producer from the cited historical receipt, declared apart from this run so the two can never be read as one measurement. The population is EIGHT, derived rather than chosen (operator ruling 2026-08-13). Seven exact identities plus one subject for the vacuity bare-expression specimen CLASS, which grounds as a class and not as members: the vacuity note describes "many isolated configurations" and enumerates none, and its optional_absent copy is the same subject as the rostered optional member. Minting three rows would have fabricated two identities to satisfy a prose sentence. The prose "ten" is recorded as retired rather than silently corrected, and the note states that eight is not an authority either — completeness is an identity join, not a count equality. * BL-2: make the roster module compile Four blocking diagnostics, found by compiling the entry rather than by inspection, and one of them is worth recording because it is a language-layer trap rather than a typo. A BRACE INSIDE A PROSE NOTE IS INTERPOLATION SYNTAX. The roster note described a standing as SubjectUnmeasured{no_committed_specimen_identity}, and the compiler read the braces as an interpolation and refused an undefined variable — inside a String literal, at a position no reader would look for code. Escaped as \{, which is the existing modeled form. This is the class DESIGN records from the ${...}-in-strings incident: the tempting move is to respell the prose around the obstacle, and the modeled escape already exists. The other three are empty list literals in fold seeds, which carry no element type on their own. Written as [] as List<T>, the idiom already used by frontier_ingestion_probe and normalize_retention_contract_probe_test. Result: 0 blocking errors on dag/tools/body_lowering_population_probe.dag. The 248 remaining advisories are pre-existing and corpus-wide. * BL-2: record the executed program receipt body_lowering_program_receipt was run against the live tree (73s) and returned eight standings. Recorded because compiling is not running, and the join is the part worth proving: eight subjects in and eight standings out establishes that no subject is dropped between roster and receipt, the three observed rows reproduce the earlier batch reading so deriving the batch from the roster did not change what executes, and the specimen class routes to no_committed_specimen_identity rather than to not_selected_into_execution_batch — the distinction the realization coproduct exists to make. The note also states what the run does not establish: the four unmeasured subjects are unobserved, not staged, and node's prior interruption is neither confirmed nor refuted here. * BL-2: render stages through the one label authority Review on #8208 noted that stage_label_of paralleled frontier_ingestion_probe's frontend_stage_label and disagreed with it on one variant — NORM_ACCEPTED against ACCEPTED — and judged it non-blocking because it is presentation-only. Taking it anyway: it is a second renderer for one closed coproduct, and it had already drifted, which is the §3 fork rather than a style preference. This module's own note claims there is one stage classifier in the repository and not two; forking the label while importing the classifier honoured that in the half that was harder to get wrong. frontend_stage_label is imported and the fork deleted. Confirmed by execution that the divergence was real and is now gone: census_report prints "use_site_verdict ACCEPTED | materialization_carriers NORM_OTHER | optional NORM_RETAINED", where the forked renderer printed NORM_ACCEPTED for the same stage. The recorded first reading named stages by their rendered label, which would now be stale. It names them by VARIANT instead, and says why: the spelling belongs to the renderer, so a note pinning it goes stale the moment that renderer is edited — the same reason counting may not read labels. Five variant imports were left import-only by the deletion and are dropped; NormalizeRetained stays, since the prior-standing rows construct it. * BL-2: scope a census refusal to the batch, not to the roster Review 51759 (REQUEST_CHANGES) found that body_lowering_program_receipt routed EVERY live-file subject through the census-refusal cause. Verified and correct: on CensusRefusedAtWorld, logic, diagnostic, occurrence_identity and node — four subjects this run was never going to execute — were rewritten from "not selected" into "the census refused". That is state-space conflation, and it undid the roster/batch split on the one path where the distinction is least visible. The two states have different remedies: a not-selected subject needs a bigger batch or a cheaper instrument, while a refused census needs the infrastructure repaired, so a reader deciding what to do next was misdirected. More generally it is a failure at one scope attributed to subjects outside that scope — the absorbing fallback wearing an error's name, manufacturing information about subjects nobody looked at. The refusal cause now reaches only selected subjects. A subject outside the batch keeps not_selected_into_execution_batch under refusal exactly as under success, because nothing about it was attempted and its standing does not depend on the census. The specimen class keeps its own structural cause under both. Proven by execution with a discriminating control, not by inspection. census_refusal_does_not_reach_a_non_batch_subject_RED returns false against the previous arm and true against this one; the other three assert that a batch member does receive the cause, that the cause is carried rather than fixed, and that the specimen class is unaffected. The arm takes a cause symbol as input, so it is reachable by ordinary call and needs no broken tree to observe. * BL-2: derive what a stage licenses, and correct the stale occurrence_identity row Per-subject censuses (one subject per run) measured six of the seven runnable subjects. Two consequences, plus the reading that keeps them from being misread. THE STALE RECEIPT ROW. occurrence_identity measures NormalizeOther, not the NormalizeRetained the exact-head receipt records. That receipt is explicit that the member was never re-run after the repair, so the cell was unknown rather than confirmed — and it is now known stale. Corrected where the receipt lives rather than only in a message, because a stale cell in a cited authority is the failure that survives on the assumption that someone else checked it. logic independently reproduced what the receipt predicted for it, which is evidence the receipt was right about the mechanism and wrong only about the row it could not re-run. WHAT A STAGE LICENSES IS NOW DERIVED, not left to prose. Leaving NormalizeRetained is not evidence of repair: NormalizeOther is by construction neither retention nor a graft refusal, so it names where a module refuses and never why. ObservedReading projects that — ObservedClean for the one admitting stage, ObservedRefusedCauseNamed where the classifier identified the kind, ObservedRefusedCauseUnclassified for NormalizeOther alone. Derived from the stage rather than stored beside it, so it cannot disagree with the coproduct it reads. A READING EXISTS ONLY WHERE SOMETHING WAS OBSERVED. A first draft of reading_of_standing mapped SubjectUnmeasured and SubjectInterrupted onto the unclassified-refusal arm on the reasoning that all three leave the cause unknown. That is the census-refusal conflation again, in the module that just repaired it: unknown-because-unclassified and unknown-because-unattempted are different unknowns with different remedies. StandingReading keeps them apart. Nine witnesses, all executed. Two are discriminating controls for the classes above: NormalizeOther must not read clean, and an unmeasured subject must have no reading at all. node is not included in any of this. It was SIGKILLed at 346s while every other subject finished between 138s and 292s; the signal is observed and the cause is not established, so it stands interrupted with a fresh lower bound and carries no stage. The six completions sit within a factor of 2.1, so the earlier 2h51m ten-member run is unexplained by them, and node dying rather than finishing left node-is-the-outlier versus superlinear-in-roster-size open. * BL-1: seal NormalizedTree behind an admission door so retention cannot reach resolve normalize computed 'this root's body lowering left no wrapper behind' and had nowhere to put it: NormalizedTree was an alias for Node, so the fact travelled as diagnostics beside a bare Node and was dropped at the first FreeMonoid<Node> carrier before resolve. Restore the carrier through the root-carrying signatures and give it a door. NormalizedTree becomes a sole_constructor record; admit_normalized_tree refuses a root carrying wrapper-retention evidence. The retention recognizer lives once, beside its single producer in v2.std.compilers.body_lowering, and scans the whole diagnostic list -- rejected_with_pending prepends outer diagnostics, so a head-only read is systematically wrong. Rung: accepted refinement with executing refusal, not structural impossibility. Also records the typecheck-blindness receipt on the hollow-alias lane: the conversion reported 0 blocking errors with a record in a Node position AND a raw Node in the NormalizedTree field, and surfaced only under execution. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018vBm5HzpzKuQ5jJ6bjgKsQ * BL-1: correct the typecheck-blindness receipt to wrapper-payload conflation The ContentHash specimen was characterised as a family member in union position. Read on main, Fnv1a64Structural is the coproduct's PAYLOAD, not a member -- and that is the stronger fact: no alias participates in it, so the class is not a quirk of alias declarations. Both specimens are one shape. The typechecker does not distinguish a wrapper from the thing it wraps. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018vBm5HzpzKuQ5jJ6bjgKsQ * BL-1: delete the retention-recognizer fork, consume the producer's own Review 51940 is right and this was my error. body_lowering_fold already carried body_lowering_diagnostic_is_wrapper_retained and the whole-list body_lowering_any_diagnostic_is_wrapper_retained -- landed by BL-0 -- and I minted a second copy in v2.std.compilers.body_lowering under the SAME names, then pointed the admission door at the fork. Two modules could answer 'does this carry retention?' and drift when the reason symbol moved: the exact parallel authority the change was supposed to close, authored by the commit message that claimed to close it. The std/ copy is deleted whole (that file is now byte-identical to main). body_lowering_fold gains one Diagnostics-grain reading beside its existing pair, delegating to the same whole-list scan, and normalized_tree imports it from there. No cycle: body_lowering_fold does not reach normalized_tree. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018vBm5HzpzKuQ5jJ6bjgKsQ * BL-1: convert the name-resolve fixtures, and close three seams they exposed Review 51952 is right that the name-resolve fixtures still passed raw FreeMonoid<Node>. Converting them and RUNNING them exposed three genuine production seams in this diff, none reported by the typechecker: admit_import_root_find declared Outcome<Node> -> no field 'children' symbol_index_fill_module_roots declared FreeMonoid<Node> -> no field 'kind' ModuleRootFound / PassingCandidateFold declared Node -> no field 'kind' The third reached a shared std carrier, so PassingCandidateFold is parameterised over its candidate type: the cardinality of a search result is not a fact about what was searched, and it was declared over Node only because every consumer happened to fold over Node. A local fold type would have been a second name for one concept; projecting and re-wrapping would have re-minted the carrier outside the door. Fixtures admit through the real door, and the refusal arm fails the witness rather than fabricating a value. Records the denominator in section 8.2: 39 declarations across 7 modules, every Node-meeting point resolved as one of 16 .root projections, 1 list projection, or 3 converted callees. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018vBm5HzpzKuQ5jJ6bjgKsQ * BL-1: convert the witness callers the floor found, and re-cut the denominator at its true boundary The floor reported nine runtime 'no field root on type Node' failures across three witness modules. All nine are mine: the first denominator was drawn at production modules reachable from the converted chain, and witness modules meet a converted value exactly as production modules do. Adds admit_normalized_roots -- the plural door, refusing as a whole so a partially admitted list has no representation -- and routes every hand-built fixture through it. Negative witnesses keep a distinct arm for fixture-admission failure so a broken fixture cannot masquerade as the rejection under test. Denominator re-cut as a caller census with its boundary named: 28 modules reference the converted APIs, 13 feed from normalize and are unaffected, 15 hand-build roots and 12 needed conversion. Residue stated rather than implied -- the instrument is execution, so a witness that meets a converted value and does not execute is broken silently; intersecting the 190-file no-executing-consumer roster against the callers yields 6 files, 4 hand-built, all executed green. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018vBm5HzpzKuQ5jJ6bjgKsQ * BL-1: stop the plural door duplicating its shared diagnostics N+1 times Review 52023 flagged admit_normalized_roots re-passing the outer diagnostics into each per-element admit. It is a real defect, not just a smell: bind_outcome MERGES accumulated with incoming, the seed carried the shared set, and every step carried it again -- so an N-element list accumulated N+1 copies of one diagnostic set. Reachable only from tests today because every caller passes None, where merging is harmless. The obvious fix -- admit each element under None -- would have made the plural door total, i.e. a cast around the door rather than the door. So the decision stays per element (same verdict each time, since it reads only the shared diagnostics), the threading becomes explicit instead of bind_outcome, the refusal keeps carrying its pending set exactly once via admit_normalized_tree, and the accepted diagnostics are attached once at the end. plural_admission_does_not_duplicate_shared_diagnostics_RED asserts the result's diagnostics equal the input for a two-element list; restoring the bind_outcome threading flips it to false. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018vBm5HzpzKuQ5jJ6bjgKsQ * BL-1: convert the self-host intermediate the caller census missed Review 52034 found frontier_probe_first_missing_import_lookup declaring roots: FreeMonoid<Node> while receiving validated_roots from validate_module_roots and forwarding it into module_root_lookup -- the exact seam this PR closes, left open on a self-host consumer. The census had classified that module safe by checking WHERE its roots came from (an already-admitted ingest fold) rather than WHAT each intermediate declared. Provenance is not a substitute for declaration, and that is the third time this population was drawn at the wrong boundary. Replaces the judgement with a mechanical check: for every function, does it pass one of its own Node / FreeMonoid<Node> parameters into a converted API. Corpus wide it reports zero -- and the instrument is validated against the defect it must catch, reporting exactly the frontier_probe site when that parameter is reintroduced and zero when the fix is restored. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018vBm5HzpzKuQ5jJ6bjgKsQ * BL-1: close the conversion population by fixpoint over three syntactic shapes Review 52042 found a witness helper passing normalize's result into qualified_name_from_module_node without projecting .root -- a shape neither earlier check covered, on a path backing an enrolled long-lane witness. The class has three shapes and they compose into a fixpoint: A own Node parameter passed into a converted API B a NormalizedTree-producer binding passed into a Node-declared parameter C a function declaring -> Outcome<Node> while returning a producer Fixing a shape-C return type makes its consumers visible to shape B, and fixing those exposes more shape C. Iterated to convergence: A and B report zero; the four remaining C hits are confirmed false positives, feeding resolve, which takes the admitted carrier, with ResolvedTree = Node so their return is correct. Closed here: 6 witness/lens helpers, 6 return declarations, 8 fixpoint-surfaced consumers. Each shape was discovered only after a review or CI surfaced an instance -- with no checker signal there is no way to derive the shapes, only to enumerate them, and that is the finding worth keeping. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018vBm5HzpzKuQ5jJ6bjgKsQ * BL-1: revert two over-applied .root projections, and record the direction asymmetry The 58e7410 red was NOT another instance of the invisible conflation. It was the inverse: .root applied to a genuine Node, which the checker DOES catch statically at resolve. Two sites, both introduced by my own bulk string replaces hitting call sites whose value came from resolve (ResolvedTree = Node) rather than from normalize -- parse_binding_fidelity_support pbf_resolved_add_arrow and stage_bridge pipeline_match_corpus_resolve_relationship_holds. Records the asymmetry in the receipt, because citing both directions as typecheck blindness would inflate the class: payload-supplied-where-wrapper- declared is invisible; wrapper-projection-on-payload is caught. Also records that what kept being wrong was the ENUMERATION, not the fix. The population was cut three times -- production, then _test modules, then support modules -- each drawn where the last failure landed rather than from a rule. All three were proxies keyed on path or role. The sweeps key on the only thing that matters, whether a declaration meets a converted value, and glob the whole tree with no path filter. Verified: all 18 files carrying an introduced projection resolve clean, and the three tree-wide sweeps report A=0, B=0, C=4-known-false-positives. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018vBm5HzpzKuQ5jJ6bjgKsQ * BL-1: correct 8.2's direction rule -- one symptom, two defects, truthfulness of the declaration is the discriminator The asymmetry this receipt claimed (payload-into-wrapper invisible, wrapper-onto-payload caught) is refuted by its own two runs: the SAME projection on the SAME two types produced 81 runtime failures on one floor run and 1 resolve failure on the next. Verified against both. The 81 came from validate_module_roots, whose fold binder is DECLARED NormalizedTree, so root.root is statically correct and the real defect sits upstream at the call boundary -- the invisible class, whose symptom is a projection failing at runtime wherever the untruthful value is finally read. The 1 came from pbf_resolved_add_arrow, whose binder is declared Node, so the declaration itself is the mismatch and resolve refuses it. The discriminator is whether the declaration at the site is truthful: a mismatch is caught exactly where it is expressible against a declaration, and invisible where the declaration is right and only the value flowing in is wrong. Also notes that 81 occurrences are not 81 instances -- they are one symptom of fewer boundary defects, read at many sites. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018vBm5HzpzKuQ5jJ6bjgKsQ --------- 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>
briansrls
pushed a commit
that referenced
this pull request
Aug 14, 2026
… of casting past it (#8269) * Carry the refusal location on the catch-all assemble cause OtherAssembleRefusal is the catch-all arm of AssembleRejectionCause, and it carried only a reason symbol. A reason symbol does not say WHICH file refused, so recovering that meant re-lexing an entire import closure by hand — measured on use_site_verdict, whose 15-file closure had to be folded through tokenize one source at a time to learn that dag/std/algebra.dag was the refusing file. The location is carried as a probe-owned projection rather than a bare Locus. Locus admits NodeLocus and PortLocus, which have no manifest serialization; a host emitter that errored on them would abort a 27-module survey over one unrepresentable position. FrontierProbeCauseLocation is serializable by construction and total, and its unavailable arm carries a typed reason so the deficit is counted rather than fabricated or fatal. Location is kept structurally separate from the cause key: two modules refusing for the same reason at different positions are the same detailed cause, so a clustering key reads the cause and not this field. The survey binary is the third consumer and is updated with it — the DAG carrier change alone would have emitted a manifest that no longer typechecks against the new field, which is worse than dropping it. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * SH-D D0: move the refusal location onto AssembleRejectionDetail, close its variants The location was a field of OtherAssembleRefusal, which made a position structurally part of what the refusal IS: two modules refusing for the same reason at different positions read as different detailed causes at a clustering key. It now sits beside the cause on AssembleRejectionDetail. CauseLocationUnavailable { reason: Symbol } is replaced by the closed pair CauseLocationPort { port } and CauseLocationNodeNotSerializable. A PortLocus carries real serializable data and no longer degrades to a reason string; only a Node, which has no manifest rendering, reaches an unavailable arm. The survey emitter follows the field and the arms, and stays total. Controls (all executed): each of the three Locus arms reaches its own arm; the byte range is asserted exactly, so substituting WholeFile reds; and a two-diagnostic control pins reason and location to the SAME diagnostic (RED verified by asserting the tail's offset). Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * SH-D D0: regenerate the round-trip fixture from real emitted bytes The fixture was hand-edited to the reshaped carrier, which is exactly what its own note forbids: a hand-written approximation tests the author's idea of the format instead of the emitter's. These bytes are copied verbatim out of a survey run at commit 2880549, tree 932acf8, executable sha256 677c1bf0673c70ef… over src/v2/compiler/use_site_verdict.dag, and the reload half passes against them. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * SH-D D0: derive the clustering verdict's dominant reason from the receipts frontier_gap_clustering_from_receipts counted blocker CLASSES and then wrote a hardcoded located_reason literal into whichever branch fired, so the verdict reported a reason no receipt had to carry: the one real survey measures ^tokenize_lex_e1_unrecognized_char, which no branch could name. Its HeterogeneousGaps arm reported module_count as the distinct-reason count, which is a count of modules wearing the name of a count of reasons. And count_receipts_with_located_reason, the function that reads the measured axis, had zero callers. Both numbers are now read off the receipts. CommonRoot requires unanimity rather than a plurality, because any plurality bar is a threshold and a threshold is the smuggled heuristic a closed system never needs. An empty survey reports a distinct count of zero rather than becoming a common root. Controls (executed, both REDs verified): a unanimous survey reports the measured reason and counts the receipts carrying it; a mixed survey reports 2 distinct reasons over 3 receipts, where the old arm reported 3. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * SH-D: unland the local probe instruments swept in by git add -A These eight files are the one-off measurement instruments behind the A/B finding — the real-closure door probe, the cause and reason readouts, the lex localizer and three tokenize fixtures. They were never meant to land; the PR description says so explicitly. A blanket 'git add -A src/v2' put them in the tree anyway, which is experimental residue and a description that no longer described the diff. They stay local, excluded via .git/info/exclude so the next add cannot repeat this. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * Compute the reason tally only on the arm that consumes it The tally ran unconditionally and its result was discarded on the heterogeneous arm, which needs the distinct count alone. Cheap at survey scale, but a copied fold whose output is thrown away is a cost-shape defect regardless of the realized n, and 'n is small here' is not a time-stable fact. Found by review 51421. All four clustering claims still pass by execution. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * Refuse an empty ingest instead of reporting the module missing MEASURED, not hypothesised. src/v2/compiler/03_resolve.dag needs 71 source reads against the manifest transport's 64-row inline cap, so the manifest emits SourceRootManifestElided with produced_row_count 0 and an Empty ingest. Handed that, frontier_probe_emit_from_ingest answered ^resolve_module_not_found with cause MissingModuleIdentityUnavailable — a survey row asserting the module is missing when the truth is that its sources were never supplied. Two states, different remedies, and the one reported was the plausible one. frontier_probe_coverage_gate already classifies an elided manifest correctly, but it guards only the discovery path; this entry receives the ingest directly and never consults coverage, so the conflation was reachable from every from_ingest caller. An empty ingest is now a typed, located, counted population refusal naming ^frontier_probe_empty_ingest. Residue, declared rather than closed: emptiness is decidable here, coverage is not, because the entry is not given the SourceRootCoverage — a PARTIAL closure still answers. Dissolve-on is on the carrier: route the coverage receipt into this entry so the refusal becomes an arm of the existing gate. Four claims green by execution; RED verified by disabling the wall, which reds three of them and restores the module-not-found answer. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * Bind the location field in the last stale AssembleRejectionDetail pattern Every other match site was updated when the field landed; this one kept the two-field pattern. Review 51431 flagged it. The predicted typecheck failure does not occur — the file's five claims were green by execution before this change and are green after — so the language admits the partial pattern. That is the reason to fix it rather than to leave it: a pattern that silently keeps matching when the carrier grows a field is how a match stops covering what its author thought it covered. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * Make the clustering witness require a nonempty survey Review 51432 is right that this claim's behavior against the checked-in stub changed. What it was before is the part worth naming: with receipts Empty, clustering asked self_emit_ready_count == module_count, both were 0, and it answered CommonRoot with dominant_located_reason ^probe_stage_emit_ok — a survey with no receipts reporting that every module reached emit. The one claim whose subject IS the clustering verdict was passing vacuously on a fabricated verdict. Receipt-derived clustering answers HeterogeneousGaps for an empty survey, so the claim now reds on the stub. That is the same state its siblings were already in and is the file's declared offline condition, not a CI failure shape: compiler_frontier_per_module_probe_survey_holds compares the receipt count against the 27-row roster, and 0 is not 27. The length check makes THIS claim's red say 'no receipts' explicitly rather than leaving it to read as a clustering disagreement. Verified by execution that the conjunction is false on an empty survey rather than erroring. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * BL-0: give the body-lowering frontier its third state The observer carried two arms over three reachable states, and the missing arm failed OPEN: every Rejected outcome fell to the else branch and reported BodyLoweringLowered. The instrument used to measure the body-lowering gap was counting failures as successes, so any population read through it undercounted by exactly the refusals. That arm is reachable by construction, not in theory: the rejection- propagation repair deliberately made body_lower_finish_for_normalize propagate genuine rejection through rejected_with_pending, and this classifier then discarded the distinction it created. Recognition had the same fail-open by a second route. It read the diagnostic HEAD alone, while retention travels through diagnostics_merge and rejected_with_pending — both of which place OUTER diagnostics first — so a retained body behind any pending diagnostic also read as lowered. It now scans the whole list. Six claims green by execution. RED verified by restoring the previous classifier: the rejection claim, the rejection-diagnostics claim and the behind-a-pending-diagnostic claim all red, while the three negative controls stay green — so the claims pin these two defects rather than failing broadly. The controls are the load-bearing half: a classifier that answered retained more often would satisfy the positive claims and destroy the count. Two existing consumers gain the third arm. Residue declared on the carrier: recognition still reads an encoding rather than a producer-returned variant, correct for every encoding the single retention producer can emit, with dissolve-on naming the producer change that makes this function the identity. First step of the sequence gunbc.roadmap_authority already declares: honest frontier, then the normalized-tree construction boundary, then the retained population to zero. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * BL-2: parameterise the census roster, and add the body-lowering population probe The stage classifier and reconciliation machinery already existed in gunbc.tools.frontier_ingestion_probe, hardcoded to the 15-member std ingestion roster. Measuring a second population needed the roster to be an argument rather than a second copy of the mechanism — two classifiers are two authorities that can disagree. take_census_over(rows) is the general form; take_frontend_census() is now that call with the module roster. Roster identity derives from the rows PASSED, never the module-level roster, so a receipt cannot claim a denominator it was not measured over — the exact misreading census_receipt_provenance_note exists to prevent, which a parameterised census stamping the default identity would have reintroduced. All 14 existing claims over the instrument still pass. The new probe measures the body-lowering population: the two SH-D shards with measured door receipts, the std members the pre-repair observation recorded as retained plus the one it never re-ran, and the seam's own modules. It is deliberately not the whole corpus, and it carries no aggregate pass/fail — the population IS the finding, and a Boolean over it would be a verdict on subjects not separately established. WHY BEFORE THE WALL: normalize returns Accepted for a tree whose diagnostics carry wrapper-retention, and resolve consumes it. Making retention unconstructible changes which modules normalize accepts, so the newly refusing population must be known before the wall lands rather than discovered from a red tree afterwards. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * BL-2 first reading: neither SH-D shard retains as a file Executed, three-member roster: use_site_verdict NORM_ACCEPTED, materialization_carriers NORM_OTHER, optional NORM_RETAINED. That corrects the shard rows rather than the finding. Neither shard retains as a file — use_site_verdict normalizes clean — so the wrapper-retained residue measured in their door chains comes from OTHER modules in their import closures, of which optional is one confirmed instance. 'The shard is body-lowering blocked' was too coarse: the shard is blocked because its closure contains retaining modules, and the repair subjects are those. The roster is three because ten was executed and ABANDONED at 2h51m and 8.3 GiB resident with no verdict, on a host whose shared 20 GiB slice is reaped largest-task-first — a hazard to other sessions before it was a slow measurement. The instrument's own note already owed a realized execution for exactly this reason. The seven dropped members are unmeasured, not excluded, and the carrier says so. The 10-to-3 shrink is nonlinear (2h51m to ~2min), so a dropped member is pathological rather than merely larger. Which one is NOT established, and guessing the largest file would be a guess dressed as a finding. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * BL-2: delete the aggregate helpers, which counted refusals and rendered text Two defects, both reachable only because an aggregate was authored over a roster this module's own note says carries no aggregate verdict. A refusal became an integer. count_stage_matching answered 0 - 1 for CensusRefusedAtWorld and 0 - 2 for CensusRefusedAtReconciliation, so a typed refusal and a genuine population count inhabited one type: a caller could add, compare or display a failure as a measurement, and -1 reads as a number rather than as a stop. The census already carries typed refusal arms, so this discarded exactly the distinction they exist to preserve. Presentation became semantic authority. The count compared stage_label_of(s) against a target String, but FrontendStage is a closed coproduct — a label rename would change the count with no stage changing, and a label collision would merge distinct stages. Counting belongs to the structural variant or to frontend_stage_eq, never to rendered text. Deleted rather than repaired: the module already states the per-member observation IS the finding and that no aggregate verdict is warranted over subjects that have not each been separately established. The helpers contradicted the stated scope, so there is nothing for a corrected aggregate to mean here. Int and Bool were left import-only and are dropped with them. * BL-2: separate the subject roster from the execution batch The previous revision declared three rows named body_lowering_population_roster while the module's prose said the subjects were ten. That made the planning error this instrument exists to detect representable in its own carrier: seven unmeasured subjects erased, one retained member observed, and a bare count licensing "the retained population is one". Two authorities now. body_lowering_subject_roster is the denominator, carrying exact identities cited rather than asserted — the two SH-D shards, and five std members from the executed table in docs/plans/v2-frontend-std-ingestion-frontier-exact-head.md (logic, optional, diagnostic, occurrence_identity as its NORM_RETAINED rows; node as its NOT RE-OBSERVED row). body_lowering_execution_batch is what one run selected, and it is DERIVED from the roster by label selection rather than re-authored beside it, so a batch row cannot name a path the roster does not. Standing is three states, because observed-or-absent cannot carry this population: a subject dropped for cost, a subject whose run a budget interrupted, and a subject genuinely measured are three epistemic positions with three remedies, and collapsing the first two into absence renders "we did not look" as "there is nothing there". SubjectInterrupted stays separate from SubjectUnmeasured because node's 900s overrun is a real lower bound on cost that an unmeasured subject does not carry; body_lowering_prior_standing gives that arm its producer from the cited historical receipt, declared apart from this run so the two can never be read as one measurement. The population is EIGHT, derived rather than chosen (operator ruling 2026-08-13). Seven exact identities plus one subject for the vacuity bare-expression specimen CLASS, which grounds as a class and not as members: the vacuity note describes "many isolated configurations" and enumerates none, and its optional_absent copy is the same subject as the rostered optional member. Minting three rows would have fabricated two identities to satisfy a prose sentence. The prose "ten" is recorded as retired rather than silently corrected, and the note states that eight is not an authority either — completeness is an identity join, not a count equality. * BL-2: make the roster module compile Four blocking diagnostics, found by compiling the entry rather than by inspection, and one of them is worth recording because it is a language-layer trap rather than a typo. A BRACE INSIDE A PROSE NOTE IS INTERPOLATION SYNTAX. The roster note described a standing as SubjectUnmeasured{no_committed_specimen_identity}, and the compiler read the braces as an interpolation and refused an undefined variable — inside a String literal, at a position no reader would look for code. Escaped as \{, which is the existing modeled form. This is the class DESIGN records from the ${...}-in-strings incident: the tempting move is to respell the prose around the obstacle, and the modeled escape already exists. The other three are empty list literals in fold seeds, which carry no element type on their own. Written as [] as List<T>, the idiom already used by frontier_ingestion_probe and normalize_retention_contract_probe_test. Result: 0 blocking errors on dag/tools/body_lowering_population_probe.dag. The 248 remaining advisories are pre-existing and corpus-wide. * BL-2: record the executed program receipt body_lowering_program_receipt was run against the live tree (73s) and returned eight standings. Recorded because compiling is not running, and the join is the part worth proving: eight subjects in and eight standings out establishes that no subject is dropped between roster and receipt, the three observed rows reproduce the earlier batch reading so deriving the batch from the roster did not change what executes, and the specimen class routes to no_committed_specimen_identity rather than to not_selected_into_execution_batch — the distinction the realization coproduct exists to make. The note also states what the run does not establish: the four unmeasured subjects are unobserved, not staged, and node's prior interruption is neither confirmed nor refuted here. * BL-2: render stages through the one label authority Review on #8208 noted that stage_label_of paralleled frontier_ingestion_probe's frontend_stage_label and disagreed with it on one variant — NORM_ACCEPTED against ACCEPTED — and judged it non-blocking because it is presentation-only. Taking it anyway: it is a second renderer for one closed coproduct, and it had already drifted, which is the §3 fork rather than a style preference. This module's own note claims there is one stage classifier in the repository and not two; forking the label while importing the classifier honoured that in the half that was harder to get wrong. frontend_stage_label is imported and the fork deleted. Confirmed by execution that the divergence was real and is now gone: census_report prints "use_site_verdict ACCEPTED | materialization_carriers NORM_OTHER | optional NORM_RETAINED", where the forked renderer printed NORM_ACCEPTED for the same stage. The recorded first reading named stages by their rendered label, which would now be stale. It names them by VARIANT instead, and says why: the spelling belongs to the renderer, so a note pinning it goes stale the moment that renderer is edited — the same reason counting may not read labels. Five variant imports were left import-only by the deletion and are dropped; NormalizeRetained stays, since the prior-standing rows construct it. * BL-2: scope a census refusal to the batch, not to the roster Review 51759 (REQUEST_CHANGES) found that body_lowering_program_receipt routed EVERY live-file subject through the census-refusal cause. Verified and correct: on CensusRefusedAtWorld, logic, diagnostic, occurrence_identity and node — four subjects this run was never going to execute — were rewritten from "not selected" into "the census refused". That is state-space conflation, and it undid the roster/batch split on the one path where the distinction is least visible. The two states have different remedies: a not-selected subject needs a bigger batch or a cheaper instrument, while a refused census needs the infrastructure repaired, so a reader deciding what to do next was misdirected. More generally it is a failure at one scope attributed to subjects outside that scope — the absorbing fallback wearing an error's name, manufacturing information about subjects nobody looked at. The refusal cause now reaches only selected subjects. A subject outside the batch keeps not_selected_into_execution_batch under refusal exactly as under success, because nothing about it was attempted and its standing does not depend on the census. The specimen class keeps its own structural cause under both. Proven by execution with a discriminating control, not by inspection. census_refusal_does_not_reach_a_non_batch_subject_RED returns false against the previous arm and true against this one; the other three assert that a batch member does receive the cause, that the cause is carried rather than fixed, and that the specimen class is unaffected. The arm takes a cause symbol as input, so it is reachable by ordinary call and needs no broken tree to observe. * BL-2: derive what a stage licenses, and correct the stale occurrence_identity row Per-subject censuses (one subject per run) measured six of the seven runnable subjects. Two consequences, plus the reading that keeps them from being misread. THE STALE RECEIPT ROW. occurrence_identity measures NormalizeOther, not the NormalizeRetained the exact-head receipt records. That receipt is explicit that the member was never re-run after the repair, so the cell was unknown rather than confirmed — and it is now known stale. Corrected where the receipt lives rather than only in a message, because a stale cell in a cited authority is the failure that survives on the assumption that someone else checked it. logic independently reproduced what the receipt predicted for it, which is evidence the receipt was right about the mechanism and wrong only about the row it could not re-run. WHAT A STAGE LICENSES IS NOW DERIVED, not left to prose. Leaving NormalizeRetained is not evidence of repair: NormalizeOther is by construction neither retention nor a graft refusal, so it names where a module refuses and never why. ObservedReading projects that — ObservedClean for the one admitting stage, ObservedRefusedCauseNamed where the classifier identified the kind, ObservedRefusedCauseUnclassified for NormalizeOther alone. Derived from the stage rather than stored beside it, so it cannot disagree with the coproduct it reads. A READING EXISTS ONLY WHERE SOMETHING WAS OBSERVED. A first draft of reading_of_standing mapped SubjectUnmeasured and SubjectInterrupted onto the unclassified-refusal arm on the reasoning that all three leave the cause unknown. That is the census-refusal conflation again, in the module that just repaired it: unknown-because-unclassified and unknown-because-unattempted are different unknowns with different remedies. StandingReading keeps them apart. Nine witnesses, all executed. Two are discriminating controls for the classes above: NormalizeOther must not read clean, and an unmeasured subject must have no reading at all. node is not included in any of this. It was SIGKILLed at 346s while every other subject finished between 138s and 292s; the signal is observed and the cause is not established, so it stands interrupted with a fresh lower bound and carries no stage. The six completions sit within a factor of 2.1, so the earlier 2h51m ten-member run is unexplained by them, and node dying rather than finishing left node-is-the-outlier versus superlinear-in-roster-size open. * BL-1: seal NormalizedTree behind an admission door so retention cannot reach resolve normalize computed 'this root's body lowering left no wrapper behind' and had nowhere to put it: NormalizedTree was an alias for Node, so the fact travelled as diagnostics beside a bare Node and was dropped at the first FreeMonoid<Node> carrier before resolve. Restore the carrier through the root-carrying signatures and give it a door. NormalizedTree becomes a sole_constructor record; admit_normalized_tree refuses a root carrying wrapper-retention evidence. The retention recognizer lives once, beside its single producer in v2.std.compilers.body_lowering, and scans the whole diagnostic list -- rejected_with_pending prepends outer diagnostics, so a head-only read is systematically wrong. Rung: accepted refinement with executing refusal, not structural impossibility. Also records the typecheck-blindness receipt on the hollow-alias lane: the conversion reported 0 blocking errors with a record in a Node position AND a raw Node in the NormalizedTree field, and surfaced only under execution. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018vBm5HzpzKuQ5jJ6bjgKsQ * BL-1: correct the typecheck-blindness receipt to wrapper-payload conflation The ContentHash specimen was characterised as a family member in union position. Read on main, Fnv1a64Structural is the coproduct's PAYLOAD, not a member -- and that is the stronger fact: no alias participates in it, so the class is not a quirk of alias declarations. Both specimens are one shape. The typechecker does not distinguish a wrapper from the thing it wraps. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018vBm5HzpzKuQ5jJ6bjgKsQ * BL-1: delete the retention-recognizer fork, consume the producer's own Review 51940 is right and this was my error. body_lowering_fold already carried body_lowering_diagnostic_is_wrapper_retained and the whole-list body_lowering_any_diagnostic_is_wrapper_retained -- landed by BL-0 -- and I minted a second copy in v2.std.compilers.body_lowering under the SAME names, then pointed the admission door at the fork. Two modules could answer 'does this carry retention?' and drift when the reason symbol moved: the exact parallel authority the change was supposed to close, authored by the commit message that claimed to close it. The std/ copy is deleted whole (that file is now byte-identical to main). body_lowering_fold gains one Diagnostics-grain reading beside its existing pair, delegating to the same whole-list scan, and normalized_tree imports it from there. No cycle: body_lowering_fold does not reach normalized_tree. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018vBm5HzpzKuQ5jJ6bjgKsQ * BL-1: convert the name-resolve fixtures, and close three seams they exposed Review 51952 is right that the name-resolve fixtures still passed raw FreeMonoid<Node>. Converting them and RUNNING them exposed three genuine production seams in this diff, none reported by the typechecker: admit_import_root_find declared Outcome<Node> -> no field 'children' symbol_index_fill_module_roots declared FreeMonoid<Node> -> no field 'kind' ModuleRootFound / PassingCandidateFold declared Node -> no field 'kind' The third reached a shared std carrier, so PassingCandidateFold is parameterised over its candidate type: the cardinality of a search result is not a fact about what was searched, and it was declared over Node only because every consumer happened to fold over Node. A local fold type would have been a second name for one concept; projecting and re-wrapping would have re-minted the carrier outside the door. Fixtures admit through the real door, and the refusal arm fails the witness rather than fabricating a value. Records the denominator in section 8.2: 39 declarations across 7 modules, every Node-meeting point resolved as one of 16 .root projections, 1 list projection, or 3 converted callees. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018vBm5HzpzKuQ5jJ6bjgKsQ * BL-1: convert the witness callers the floor found, and re-cut the denominator at its true boundary The floor reported nine runtime 'no field root on type Node' failures across three witness modules. All nine are mine: the first denominator was drawn at production modules reachable from the converted chain, and witness modules meet a converted value exactly as production modules do. Adds admit_normalized_roots -- the plural door, refusing as a whole so a partially admitted list has no representation -- and routes every hand-built fixture through it. Negative witnesses keep a distinct arm for fixture-admission failure so a broken fixture cannot masquerade as the rejection under test. Denominator re-cut as a caller census with its boundary named: 28 modules reference the converted APIs, 13 feed from normalize and are unaffected, 15 hand-build roots and 12 needed conversion. Residue stated rather than implied -- the instrument is execution, so a witness that meets a converted value and does not execute is broken silently; intersecting the 190-file no-executing-consumer roster against the callers yields 6 files, 4 hand-built, all executed green. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018vBm5HzpzKuQ5jJ6bjgKsQ * BL-1: stop the plural door duplicating its shared diagnostics N+1 times Review 52023 flagged admit_normalized_roots re-passing the outer diagnostics into each per-element admit. It is a real defect, not just a smell: bind_outcome MERGES accumulated with incoming, the seed carried the shared set, and every step carried it again -- so an N-element list accumulated N+1 copies of one diagnostic set. Reachable only from tests today because every caller passes None, where merging is harmless. The obvious fix -- admit each element under None -- would have made the plural door total, i.e. a cast around the door rather than the door. So the decision stays per element (same verdict each time, since it reads only the shared diagnostics), the threading becomes explicit instead of bind_outcome, the refusal keeps carrying its pending set exactly once via admit_normalized_tree, and the accepted diagnostics are attached once at the end. plural_admission_does_not_duplicate_shared_diagnostics_RED asserts the result's diagnostics equal the input for a two-element list; restoring the bind_outcome threading flips it to false. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018vBm5HzpzKuQ5jJ6bjgKsQ * BL-1: convert the self-host intermediate the caller census missed Review 52034 found frontier_probe_first_missing_import_lookup declaring roots: FreeMonoid<Node> while receiving validated_roots from validate_module_roots and forwarding it into module_root_lookup -- the exact seam this PR closes, left open on a self-host consumer. The census had classified that module safe by checking WHERE its roots came from (an already-admitted ingest fold) rather than WHAT each intermediate declared. Provenance is not a substitute for declaration, and that is the third time this population was drawn at the wrong boundary. Replaces the judgement with a mechanical check: for every function, does it pass one of its own Node / FreeMonoid<Node> parameters into a converted API. Corpus wide it reports zero -- and the instrument is validated against the defect it must catch, reporting exactly the frontier_probe site when that parameter is reintroduced and zero when the fix is restored. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018vBm5HzpzKuQ5jJ6bjgKsQ * BL-1: close the conversion population by fixpoint over three syntactic shapes Review 52042 found a witness helper passing normalize's result into qualified_name_from_module_node without projecting .root -- a shape neither earlier check covered, on a path backing an enrolled long-lane witness. The class has three shapes and they compose into a fixpoint: A own Node parameter passed into a converted API B a NormalizedTree-producer binding passed into a Node-declared parameter C a function declaring -> Outcome<Node> while returning a producer Fixing a shape-C return type makes its consumers visible to shape B, and fixing those exposes more shape C. Iterated to convergence: A and B report zero; the four remaining C hits are confirmed false positives, feeding resolve, which takes the admitted carrier, with ResolvedTree = Node so their return is correct. Closed here: 6 witness/lens helpers, 6 return declarations, 8 fixpoint-surfaced consumers. Each shape was discovered only after a review or CI surfaced an instance -- with no checker signal there is no way to derive the shapes, only to enumerate them, and that is the finding worth keeping. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018vBm5HzpzKuQ5jJ6bjgKsQ * BL-1: revert two over-applied .root projections, and record the direction asymmetry The 58e7410 red was NOT another instance of the invisible conflation. It was the inverse: .root applied to a genuine Node, which the checker DOES catch statically at resolve. Two sites, both introduced by my own bulk string replaces hitting call sites whose value came from resolve (ResolvedTree = Node) rather than from normalize -- parse_binding_fidelity_support pbf_resolved_add_arrow and stage_bridge pipeline_match_corpus_resolve_relationship_holds. Records the asymmetry in the receipt, because citing both directions as typecheck blindness would inflate the class: payload-supplied-where-wrapper- declared is invisible; wrapper-projection-on-payload is caught. Also records that what kept being wrong was the ENUMERATION, not the fix. The population was cut three times -- production, then _test modules, then support modules -- each drawn where the last failure landed rather than from a rule. All three were proxies keyed on path or role. The sweeps key on the only thing that matters, whether a declaration meets a converted value, and glob the whole tree with no path filter. Verified: all 18 files carrying an introduced projection resolve clean, and the three tree-wide sweeps report A=0, B=0, C=4-known-false-positives. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018vBm5HzpzKuQ5jJ6bjgKsQ * BL-1: correct 8.2's direction rule -- one symptom, two defects, truthfulness of the declaration is the discriminator The asymmetry this receipt claimed (payload-into-wrapper invisible, wrapper-onto-payload caught) is refuted by its own two runs: the SAME projection on the SAME two types produced 81 runtime failures on one floor run and 1 resolve failure on the next. Verified against both. The 81 came from validate_module_roots, whose fold binder is DECLARED NormalizedTree, so root.root is statically correct and the real defect sits upstream at the call boundary -- the invisible class, whose symptom is a projection failing at runtime wherever the untruthful value is finally read. The 1 came from pbf_resolved_add_arrow, whose binder is declared Node, so the declaration itself is the mismatch and resolve refuses it. The discriminator is whether the declaration at the site is truthful: a mismatch is caught exactly where it is expressible against a declaration, and invisible where the declaration is right and only the value flowing in is wrong. Also notes that 81 occurrences are not 81 instances -- they are one symptom of fewer boundary defects, read at many sites. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018vBm5HzpzKuQ5jJ6bjgKsQ * Admit the add-arrow fixture through the door instead of casting past it Main has been red since BL-1 on one witness, five merges deep: arrow_body_form_eval_value_vertical_holds fails with `no field 'root' on type 'Node'`. `resolve` declares `tree: NormalizedTree` and dereferences `tree.root`. The witness passed `dag_add_arrow_with_body_node()`, which returns a raw `Node`. ABF-1 was authored against the old bare alias (`type NormalizedTree = Node`) and merged after BL-1 sealed the carrier — both sides individually correct, the hazard is the merge order. Nothing refused it at compile time because `module_skips_direct_call_arg_check` exempts every `v2.*` module from the direct-call argument TYPE judgment, and this witness is `v2.test.claim.body_lowering`. So it surfaced only where `.root` was actually dereferenced, at runtime. The repair is the smaller one: the fixture reaches `resolve` through `admit_normalized_tree`, the `Outcome` is threaded, and the `Rejected` arm FAILS the witness. No `.root` accessor at the call site, no widening of `resolve` to take a `Node`, no weakening of the carrier — reaching past the door is exactly what BL-1 made unwritable, and a fixture that cast past it would plant the wrapper-payload conflation in the witness population. Pattern follows `admission_fail_closed.dag` (`fc_admitted` / `fc_with_roots`). Green by execution: the witness returns `true`. The discriminating RED needs no construction — it is main's current CI failure on this exact function. NOT fixed here, reported rather than silently left: three sibling sites in `manual/body_lowering_normalize_add.dag` pass raw `Node`s into the same parameter (its `body_lowering_normalized_module` is declared `-> Optional<Node>`). They are latent, not absent — `manual/` is outside per-PR discovery, so nothing executes them today. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018vBm5HzpzKuQ5jJ6bjgKsQ --------- 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>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Draft until #8228 merges. This branch still carries BL-0's four files, so its diff currently duplicates #8228. Once that lands, merging main drops them and this PR reduces to the BL-2 census work alone. Holding it in draft rather than opening two PRs that both claim the same BL-0 change.
Reworked per the operator ruling of 2026-08-13, which requested changes on the combined PR and judged BL-0 good on its own. BL-0 was split out to #8228; the three BL-2 defects are repaired here.
1 & 2 — the aggregate helpers, deleted rather than fixed
count_stage_matchingandretained_member_countare gone.0 - 1forCensusRefusedAtWorldand0 - 2forCensusRefusedAtReconciliation, so a typed refusal and a population count shared one type — a caller could add, compare or display a failure as a measurement, and-1reads as a number rather than as a stop.stage_label_of(s)against aString, butFrontendStageis a closed coproduct: a label rename would change the count with no stage changing, and a label collision would merge distinct stages.Deleted rather than corrected because the module already states that the per-member observation is the finding and that no aggregate verdict is warranted over subjects not each separately established — the helpers contradicted the stated scope, so there was nothing for a fixed aggregate to mean. Both failures are recorded on the module so they are not reintroduced.
3 — the roster was wearing the population's name
Three rows were declared as
body_lowering_population_rosterwhile the prose said ten subjects. That made the exact planning error this instrument exists to detect representable in its own carrier.Now two authorities:
body_lowering_subject_roster— the denominator, with identities cited rather than asserted. Two SH-D shards; five std members from the executed table indocs/plans/v2-frontend-std-ingestion-frontier-exact-head.md(logic,optional,diagnostic,occurrence_identityas itsNORM_RETAINEDrows,nodeas itsNOT RE-OBSERVEDrow).body_lowering_execution_batch— what one run selected, derived from the roster by label selection rather than re-authored beside it, so a batch row cannot name a path the roster does not, and a subject with no committed realization cannot enter a batch at all.BodyLoweringObservationStandingis three states —SubjectObserved/SubjectUnmeasured{cause}/SubjectInterrupted{receipt}— because observed-or-absent cannot carry this population: dropped-for-cost, budget-interrupted and genuinely-measured are three positions with three remedies, and collapsing the first two into absence renders "we did not look" as "there is nothing there".SubjectInterruptedstays distinct becausenode's 900s overrun is a real lower bound on cost;body_lowering_prior_standinggives that arm its producer from the cited historical receipt, declared apart from this run so the two can never be read as one measurement.The population is eight, derived rather than chosen
Seven exact identities plus one subject for the vacuity bare-expression specimen class. The class grounds; its members do not —
vacuity_bool_witness_body_lowering_ceiling_notedescribes "many isolated configurations" and enumerates none, no fixture carries them, and itsoptional_absentcopy is the same subject as the rosteredoptionalmember. Rostering three would have minted two identities to satisfy a prose sentence, which is what a roster exists to prevent.The prose "ten" is recorded as retired, not silently corrected, so the next reader does not reconcile eight against it and reopen the question. The note also states that eight is not an authority either: completeness is an identity join, not a count equality (DESIGN §5), so a ninth grounded identity joins and the specimen placeholder dissolves into real members when committed.
Not done here
No new census run. The 2h51m ten-member failure bounds the next one, which should be one independent job per subject — each emitting its own identity, stage receipt, runtime and terminal standing — so the pathological subject is identified rather than attributed by file size.