Repository navigation
The cost-debt roster's outside-gate arm was a name test, so an enrolled identity that does not exist refused nothing - #9695
Merged
Conversation
…ty that does not exist refused nothing The roster's reverse join already had an outside-the-gate arm, added with the 2026-08-29 gate cut, and it was already counted rather than silent. What it could not do is tell the two populations apart. Its filter, `identity_inside_required_gate`, takes an identity's module path and prefix-matches it against `required_gate_prefixes` -- a NAME test consulting nothing else -- so for any enrolled row that did not execute it asked only whether the string starts with a gate prefix. A module name cannot distinguish "declared, but this run's gate never loaded it" from "no such declaration anywhere". So an enrolled identity that DOES NOT EXIST -- a typo, a renamed or deleted witness, a fabricated line -- prefix-missed the gate, was counted as outside-the-gate, and refused nothing. That is exactly the hole the reverse join exists to close, in its own words: "the cheapest way to fake a green run: enrolling an identity that does not exist would otherwise cost nothing." The gate cut reopened it across the whole non-gate namespace, which is now the majority of the corpus -- 122 enrolled rows sat in that arm on the first gate-bounded fold, and nothing could tell a real one from a fabricated one. gunbc#9684 makes the repair possible: every DECLARED identity now carries exactly one disposition, so preparation's own account of what it loaded is available as a population instead of a name test. `partition_cost_debt_roster` puts every enrolled identity in exactly one arm -- Withheld (the operative debt), OutsideThisRunsUniverse (declared, never offered: record, NOT debt), Undeclared (REFUSES), DeclaredButNotWithheld (the pre-existing stale arm, REFUSES). It is a free function beside `reconcile_identity_population` for that function's own stated reason: the test that proves it must call the production code, not a paraphrase. The discriminating RED is a PAIR that differs only in declaration: two enrolled identities in the same non-gate module, neither withheld, one declared and one not. The undeclared one must refuse; its declared sibling must not. A fix that refused both would close the hole by breaking the 122 legitimate rows, and nothing but the declared join separates them. Verified by mutation: restoring the old semantics reds three of the four new tests and leaves the untouched withheld-precedence arm green. Reporting is now identity grain. The old line was a bare count of an arm that silently mixed legitimate rows with unrefusable fabrications; the roster-standing line names all four arms and the outside-universe identities are listed. Both refusing arms stay guarded by `reverse_joins_answerable` exactly as before: a halted run's population is truncated, and answering "delete the row" over it is the empty-observation narrow this file refuses to commit for the sibling rosters. Roster edits: NONE. No row released, no row added. Also records the held live_deploy population's trigger on the carrier. It is a capability, not a PR -- the restoration of the 4b(3) rung drop "Required gate reduced to the compiler floor" by a change-derived required population -- because those identities are outside the gate closure AND no full-corpus floor remains in CI (witnesses.yml is --required-ci twice), so their cost is unobservable by any instrument this repository runs. The roster is a bare List<String> with no per-row field, which is why the trigger is a paragraph beside it; that limitation is stated rather than worked around. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BotWMhkrZSo3Hcy1K68Mt9
… gate) gunbc.seed_growth_admission seed_growth_forward_freeze_policy_note makes an unenumerated hand-src/v1-Rust addition a stop-line, and this change adds two hand declarations without one. Adds the row and its roster entry. A SECOND ROW BESIDE gunbc.floor_cost_debt_seed_growth RATHER THAN AN EDIT TO IT. That row justifies the FORWARD direction — the build-loop branch declining a rostered site at build rather than skipping it at execution — and retires when the self-emitted claim executor consumes required_floor_site_disposition directly. This one justifies the REVERSE direction, which asks a different question of different inputs (the declared identity population and preparation's disposition rows, neither of which the forward branch reads) and retires on a different condition. Folding them would give one trigger two capabilities, so whichever was satisfied first would retire an obligation it does not cover — the grain mismatch DESIGN §4b(3) names as the review tell for a trigger naming less than it restores. One row per capability is already the pattern at this boundary (floor_non_verdict, floor_route_gap, floor_cost_debt, floor_population_projection), not a fork. The two items are enumerated exactly: partition_cost_debt_roster and CostDebtRosterStanding, both in v1_compiler.cli_run. The row records why Rust is still needed (the declared population and disposition rows are produced inside run_required_floor and never leave it), what is NOT grown (no roster, no policy, no status vocabulary, no roster line), why the repair was impossible before gunbc#9684 (it needs a declared population to join against), and why the RED is a pair rather than a single probe. NO LOC OR ITEM DELTAS ARE TYPED. The policy note rules them derivable from the diff and drops them from the enumeration — an authored copy is a second representation of a fact the diff already owns, and it measured one wrong (gunbc#9095 claimed +195/+65 against +203/-0, +60/-5, +102/-0). Two existing rows still carry them from before that ruling; this one does not. Trigger is the roster's reverse join folding in .dag with the v1 floor bridge hollowed — explicitly NOT retired by the roster reaching zero, and NOT by the undeclared arm being empty on any number of runs: an empty arm still needs the wall that keeps it empty. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BotWMhkrZSo3Hcy1K68Mt9
…3 defect it was repairing Blocker from neat-swift-219, and it is correct. `partition_cost_debt_roster` consulted the fold's withheld set BEFORE the disposition and fell back to it, so two structures answered one question — the same §3 defect this change exists to close, rebuilt inside the repair. It was not hypothetical: the test `a_withheld_identity_is_debt_regardless_of_its_disposition_row` asserted that a withheld identity with NO disposition row is `Withheld`, blessing exactly the divergence the join should refuse. The four arms are now driven SOLELY by the disposition index: no disposition -> Undeclared; DeclinedCostDebt -> Withheld; DeclinedOutsideGateClosure | DeclinedDiscoveryExcluded -> OutsideThisRunsUniverse; every other declared disposition -> DeclaredButNotWithheld. The declared-set parameter is REMOVED as well, which the blocker did not ask for and which follows from it: since gunbc#9684 every declared identity carries exactly one disposition, so "no disposition" IS "not declared", and passing a separate declared set would have been a third representation of one fact. The withheld set is still computed for execution accounting and is RECONCILED against the classification rather than consulted by it. `reconcile_withheld_against_dispositions` reports both directions of the difference — a withhold with no DeclinedCostDebt disposition, and a DeclinedCostDebt disposition with no withhold — and the runner REFUSES on either with CostDebtWithholdDispositionDisagreement. Two observations of one act may disagree loudly; they may not be silently resolved in favour of one. The offending test is REVERSED rather than deleted: a_missing_disposition_refuses_and_is_never_overridden_by_another_structure asserts that a missing disposition refuses whatever any other structure believes. Mutation-verified twice: absorbing the undeclared arm back into outside-universe reds 3 of 6; dropping one direction of the reconciliation reds the disagreement test while the agreement test stays green. The seed-growth receipt gains the third declaration and records why the draft was wrong, so the next reader sees the failure mode rather than only the repair. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BotWMhkrZSo3Hcy1K68Mt9
briansrls
pushed a commit
that referenced
this pull request
Aug 29, 2026
…ion/deep-heron-231 # Conflicts: # dag/gunbc/seed_growth_admission.dag
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.
The defect
The cost-debt roster's reverse (staleness) join already had an outside-the-gate arm — added with the 2026-08-29 gate cut, already counted rather than silent. What it could not do is tell two populations apart.
Its filter,
identity_inside_required_gate, takes an identity's module path and prefix-matches it againstrequired_gate_prefixes. It is a name test consulting nothing else. So for any enrolled row that did not execute, it asked only whether the string starts with a gate prefix.A module name cannot distinguish "declared, but this run's gate never loaded it" from "no such declaration anywhere." So an enrolled identity that does not exist — a typo, a renamed or deleted witness, a fabricated line — prefix-missed the gate, was counted as outside-the-gate, and refused nothing.
That is precisely the hole the reverse join exists to close, in its own words:
The gate cut reopened it across the whole non-gate namespace, which is now the majority of the corpus. 122 enrolled rows sat in that arm on the first gate-bounded fold, and nothing could tell a real one from a fabricated one.
This is a fail-open in the roster's anti-fabrication wall, not only in its debt accounting.
Why it is fixable now
gunbc#9684 makes every declared identity carry exactly one disposition, so preparation's own account of what it loaded is available here as a population rather than as a name test.
The change
partition_cost_debt_rosterputs every enrolled identity in exactly one arm:WithheldOutsideThisRunsUniverseUndeclaredDeclaredButNotWithheldThe discriminator is the disposition, and only the disposition — derived from what preparation actually loaded, never the spelling of a name and never a second structure's opinion.
It is a free function beside
reconcile_identity_population, for that function's own stated reason: "the test that proves it must call THE PRODUCTION CODE" — an earlier draft of that test carried its own copy of the loop and would have passed while production drifted.The discriminating RED
A pair that differs only in declaration: two enrolled identities in the same non-gate module, neither withheld, one declared and one not. The undeclared one must refuse; its declared sibling must not.
That pairing is the point. A fix that refused both would close the hole by breaking the 122 legitimate rows, and nothing but the declared join separates them.
Verified by mutation, not by assertion, twice:
One test is deliberately the reverse of one the first draft carried:
a_missing_disposition_refuses_and_is_never_overridden_by_another_structure.Reporting
Identity grain, replacing a bare count of an arm that silently mixed legitimate rows with unrefusable fabrications:
Preserved deliberately
Both refusing arms stay guarded by
reverse_joins_answerable, exactly as before. A halted run's population is truncated, and answering "delete the row" over it is the empty-observation narrow this file already refuses to commit for the sibling rosters. The partition's inputs are planning-time facts and survive an execution halt — but a halt during preparation truncates them too, and this arm must not be the one place that assumes otherwise.No roster edits. No row released, no row added.
Seed-growth receipt
gunbc.floor_cost_debt_standing_seed_growthenrols all three new hand declarations (partition_cost_debt_roster,CostDebtRosterStanding,reconcile_withheld_against_dispositions), with one entry inseed_growth_justification_roster().It is a sibling of
floor_cost_debt_seed_growthrather than an edit to it: that row justifies the forward direction (declining a rostered site at build) and retires when the self-emitted claim executor consumesrequired_floor_site_dispositiondirectly. This one justifies the reverse direction, which reads different inputs and retires on a different condition. Folding them would give one trigger two capabilities, so whichever was satisfied first would retire an obligation it does not cover — the grain mismatch §4b(3) names. One row per capability is already the pattern here.No LOC or item deltas are typed: the policy note rules them derivable from the diff and drops them from the enumeration.
Also: the held
live_deploypopulation's triggerRecorded on the carrier, and it is a capability, not a PR: restoration of the §4b(3) rung drop "Required gate reduced to the compiler floor" by a change-derived required population.
Those 45 identities cannot take the roster's ordinary exit ("delete the line, let the floor run it"), because the floor does not run them — they are outside the gate closure, and no full-corpus floor remains in CI (
witnesses.ymlinvokesclaim_executor --required-citwice, once per lane, and nothing else runs a floor). Their marginal cost is unobservable by any instrument this repository currently runs, so releasing them would be bookkeeping rather than a measured shrink.floor_cost_debt's roster is a bareList<String>with no per-row field, so the trigger is a paragraph beside it rather than on the rows — a real limitation of the carrier, stated rather than worked around.Verification
cargo test --release -p v1-compiler --lib(what CI'srust-unit-testsruns): 541 passed, 0 failed; all four new tests confirmed present in that release profile, not just locally in debug.cargo clippy -p v1-compiler --lib -- -D warnings: clean. (--all-targetsfails on main already, intests//examples//bins/I did not touch.)cargo fmt --all --check: clean.gunbc compileon the changed.dag: 0 blocking errors.