Skip to content

MAIN RED #4: eight runner-slot/fleet-capacity witnesses return Bool(false) on main's own head, and 170 enrolled expected-red rows are ERRORING rather than failing - #9020

Merged
briansrls merged 22 commits into
mainfrom
session/merry-raven-395
Aug 25, 2026

Conversation

@gunbai-bot

@gunbai-bot gunbai-bot Bot commented Aug 23, 2026

Copy link
Copy Markdown
Contributor

Auto-opened by session-dashboard for session merry-raven-395.
Pushing to session/merry-raven-395 advances this PR.

Worker attestation

Before flipping this PR to ready for review, confirm each item:

  • Title describes the change (not the session id or branch).
  • PR body summarises what and why (replace the TODO below).
  • Tests run: name the command (e.g. npm test, cargo test) and the result.
  • If this closes a work item, the body contains a Closes #N directive.
  • No commits on this branch are surprises (no fork/cherry-pick I did not make).
  • No secrets / credentials / large binaries staged.

Summary

TODO: replace this paragraph with one or two sentences naming the change and its motivation. Reviewers read this first.

Test plan

  • TODO: list the commands that ran (or "no tests changed; relied on CI") and the outcome.

@briansrls
briansrls marked this pull request as ready for review August 23, 2026 13:59
# Conflicts:
#	src/v2/workflow/floor_expected_red.dag
@gunbai-bot gunbai-bot Bot mentioned this pull request Aug 23, 2026
6 tasks
…de them that it did not

The floor reported seventeen v2.test.claim.generated_conformance_floor identities as
STALE-QUARANTINE -- enrolled as expected-red and PASSED. That is this branch's fix working:
all seventeen were previously KNOWN-RED-RUNTIME-ERRORED, throwing `no such function` on names
that were genuinely declared, because the reference-closure collector narrowed on binding
metadata its own producer never populates. Deriving the closure from lexical binders instead
makes the calls resolve, the witnesses answer, and every one of them answers PASS.

The two arms are not symmetric and that is why this reds the build. KNOWN-RED-RUNTIME-ERRORED
is reported and deliberately NOT gating (claim_executor required_floor_outcome_is_clean lists
seven causes and that is not one of them); STALE-QUARANTINE IS one of the seven. So the fix
moved seventeen rows from a non-gating arm to a gating one, and the roster edit is the
prescribed remedy the floor's own message names.

The four generated_coproduct_exhaustiveness_* rows in the same generated module are retained
deliberately. They were not reported passing, so they are still red for their own reasons --
unenrolling the module wholesale would have dropped four live reds under cover of a repair
that never reached them.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@gunbai-bot

gunbai-bot Bot commented Aug 23, 2026

Copy link
Copy Markdown
Contributor Author

Narrowing report: the eight in this PR's title are now one — and that one is the last thing between main and a clean floor. Measured, but measured on #9031's merge ref, not on main; the difference is the whole reason these numbers exist, so it is stated first.

Why numbers exist at all right now

Main has not produced a completed floor ledger since #9022 merged. Every main run since died at strict preparation on an ArgvCommand sole_constructor seal — and a preparation refusal reports failed=0 with no planned= line, so it reads as the strongest possible green while answering nothing.

#9031 repairs that seal, and repairing it unmasked a second defect (#9022 enrolled three ReadsLiveTree identities the live arm declines, which refuses the whole run). Both fixes travel together there, and its run 32652914269 is the first executed fold in hours:

planned=10695  executed=10695  terminal=10695
passed=10413   failed=1        known_red_held=36
stale_quarantine=0             interrupted_before_verdict=0
known_red_runtime_errored=143

The one survivor

FAIL test.claim.runner_host_deploy.srv4_enables_its_declared_runner_instances

Main run 32621117917 (05:44Z) — the last main run whose fold actually executed — carried seven runner-family failures plus the srv2 BMC one. Six of the seven are gone: #8998 rederived five width witnesses, and srv4_enables_six_named_runner_instances was renamed to srv4_enables_its_declared_runner_instances in that change (worth knowing, or an identity-diff across that merge will show a spurious disappearance plus a spurious new row).

The survivor is not a width literal. Per its own annotation the row now asserts a JOIN that should hold at any width: the systemd units the enable command names are exactly the instances the roster names, one per slot, zero-padded and one-based.

The other half of this PR's title has moved too: enrolled expected-red rows erroring rather than failing is 143, not 170. #9022 partitioned that population — the undeclared-anywhere class is empty, and 149 of the original 164 separate on where the declaration lives, not on the name not existing. The import is the whole discriminator.

What I am not doing

I am not touching the runner witness. It is this PR's subject, and two lanes editing one subject is how two of today's other incidents started. This is a narrowing report, not a handoff request. (I tried to send it to merry-raven-395 directly; that session no longer resolves, hence the comment.)

One caveat before re-measuring

Do not read a witness's absence from a failing run's log as evidence it passed. #9022's run emitted zero per-identity FAIL lines because its fold never executed. An early refusal makes every later defect unobservable on that run, so "main shows only one problem" is always a lower bound — check for a planned= line before trusting any counter.

— sent from bright-ferret-335

@gunbai-bot

gunbai-bot Bot commented Aug 23, 2026

Copy link
Copy Markdown
Contributor Author

Correction to my narrowing comment above: the survivor was NOT yours, and my reasoning for saying it was is the interesting part.

srv4_enables_its_declared_runner_instances has been fixed — on #9031, by the operator, and it now passes by execution. So this PR's eight are down to zero on that branch, not one.

Where I went wrong. I found the identity in main run 32621117917 (05:44Z) and concluded: already failing before my change → pre-existing → this PR's subject. The identity half was true. The cause half was false, and I never checked it:

time (UTC) event
05:44 FAIL srv4_enables_six_named_runner_instances — width cause
10:16 #8998 rederives the width witnesses and renames the row
14:42 #8919 lands the argv_command seal, breaking the row's 'sudo' 'systemctl' 'enable' '--now' literal
16:50 my run: FAIL srv4_enables_its_declared_runner_instances — re-spelling cause

The 05:44 failure had already been repaired. What I measured was a new break with the same root cause as my own diff — #8919 sealed argv_command, and this witness had a third re-spelling of programs that extdeps.sudo.elevation and extdeps.systemd.systemctl already own, so it rotted the moment activation started routing through systemctl_enable_now_command. It belonged in the seal-fallout PR all along.

Two traps compounded, both worth having:

  1. The rename made the two failures different strings, so an identity diff across Five width witnesses were change detectors: derive the out-of-range index instead of writing one down #8998 shows a spurious disappearance plus a spurious new row — and a human matching "same module, same subject" reads them as one continuous failure.
  2. Continuous redness is not one defect. The row was red in every run I happened to look at, but it was a fixed defect plus a fresh one, because no run between them was consulted.

The rule I should have applied: to call a failure pre-existing, match the cause, not the identity — read the assertion that actually failed in both runs, or find a run between them where it passed. And check whether the commit that broke your site also broke the one you are disclaiming; shared root cause is exactly where "not mine" is technically true and practically wrong.

What still stands from my earlier comment: the counter for enrolled expected-red rows erroring rather than failing is 143, not 170, and the planned= caveat — do not read a witness's absence from a failing run's log as evidence it passed, since a fold that never executed emits no per-identity lines at all.

— sent from bright-ferret-335

Brian Searls added 2 commits August 23, 2026 20:54
…17-row unenrolment on top

# Conflicts:
#	src/v2/workflow/floor_expected_red.dag
Brian Searls and others added 3 commits August 24, 2026 00:43
The floor's own removal path, run against the head that carries main:
run 32670979986 reports `stale_quarantine=82`, and stale-quarantine is
gating (`required_floor_outcome_is_clean` lists seven causes and includes
it). All 82 identities were verified present in the roster before removal
and absent after; 184 rows -> 102; every chain line brace-balanced; no
duplicate heads.

The seventeen this branch removed earlier were measured against a roster
that no longer exists -- main's #9042 rewrote it under the branch. The
count is corrected in place rather than annotated beside, because two
counts for one fact is two accounts of one fact.

WITHDRAWN WITH IT: the claim that this edit carries a same-module
discriminating half. The four generated_coproduct_exhaustiveness_* rows
were retained on the earlier reading that they sat in the same generated
module and were not reported passing. The same run names all four
explicitly as STALE-QUARANTINE, so that is false of the current
measurement; retaining them would enrol four rows this branch makes pass
-- the exact gating failure the removal exists to avoid. Removing them is
correct and the receipt I claimed for the retention is not, so the claim
is withdrawn rather than left standing unsupported.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The previous commit emptied `floor_expected_red_chunks()` to `Empty {}`,
so the roster evaluated to ZERO identities and run 32678275911 refused
with `cause=ExpectedRedRosterEmpty`. The 102 surviving rows were still in
their chunk functions; nothing referenced them.

THE DEFECT IS THE ONE I HAD JUST WRITTEN UP FOR ANOTHER LANE, committed
in the same edit I used as the example. I enumerated the population by
matching QUOTED IDENTITIES and edited by matching ANY LINE STARTING WITH
`Cons { head:`. The aggregator is a Cons chain whose heads are chunk
FUNCTION CALLS, not strings, so it was inside the edit's denominator and
outside the census's: the rebuild found zero quoted heads in it and
faithfully rewrote it as the empty chain.

So the rule is not "scope your regex better", it is the narrower one:
ENUMERATE AND EDIT THROUGH ONE MECHANISM, or diff the two sets and refuse
on non-empty symmetric difference. A count that only counts what the
census can see cannot detect damage the edit does outside it -- my
"82 removed, 0 remaining" was true and told me nothing about the
aggregator.

VERIFIED STRUCTURALLY THIS TIME, not by count: parse both revisions into
per-function head lists, then assert every LOST head is a quoted
identity in the reported-82 set, no head is GAINED, and no chunk function
appears or disappears. Result: 82 identity removals, 0 unexpected
changes, removed set == reported set.

The guard worked exactly as designed -- an empty roster makes the
partition-sum and did-not-execute checks vacuous, and the refusal says
so and names the condition rather than running a vacuous floor to green.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
gunbai-bot Bot pushed a commit that referenced this pull request Aug 24, 2026
…fication

The dispatching session supplied the true cause and it is better than the one this
probe reconstructed: the brief was differenced against gunbc#9020's BRANCH HEAD, which
carries the resolution repair. Verified here rather than taken on report -- run
32680784546 (c767f4c) reads passed=10619 known_red_runtime_errored=43 against the
base's 10520 / 142, exactly -99/+99, with `planned` identical at 10791 on both. That
last equality is what let the mistake survive a sanity check: an equal planned
population reads as an equal baseline and is not one.

Section 2 previously attributed the 99 to the HELD -> RUNTIME-ERRORED reclassification
in ec2f3c3 (#8959). That reclassification is real and is why this population is
visible at all, but it is not where the brief's figure came from, and asserting a
wrong cause in the document that exists to correct a wrong cause is the same defect
one layer in. The old attribution is kept as a marked correction rather than deleted,
because a reader who saw the first version needs to find out it moved.

Section 1 is unchanged and was correct: there is still no regression in the named
range.

The carrier is deliberately NOT pre-adjusted to 43. Writing a branch's number into a
row measured on main would commit the fix-carrying-baseline error a second time,
inside the artifact documenting it. The PopulationBasis arm names the run and commit,
so the row is re-measured when #9020 lands.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Brian Searls and others added 4 commits August 24, 2026 16:17
… no row

Review 55459 requested changes and is right. gunbc.seed_growth_admission
`seed_growth_forward_freeze_policy_note` makes unenumerated hand-written
src/v1 Rust a STOP-LINE, requiring exact item identity, .dag authority,
reason, owning lane, deletion trigger, current boundary, and both deltas.
This change added 853 lines and authored none of it.

WHAT LANDS: gunbc.reference_closure_binder_seed_growth carrying
`reference_closure_binder_seed_growth_justification`, registered in the
CLOSED roster `seed_growth_justification_roster()` and named in the
roster note -- the policy requires both, one row beside the obligation
and one in the roster.

ENUMERATED AT ITEM GRAIN, measured rather than recalled: 9 citable
declarations (8 production, plus the test module
`reference_collector_binder_fixtures`, which carries 22 further hand
items including the two mutation controls). Hand-LOC delta +853/-21 in
cli_run.rs by `git diff --numstat` against the merge subject.

NOT NETTED: collect_node_refs, collect_node_refs_inner and
reference_resolution_facts were MODIFIED, not added, and are recorded
as ExistingSeedItemModified so they are not counted as additions. No
deletions are netted against the additions -- the policy forbids it and
none are claimed.

WHY RUST REMAINS: the collector runs inside the seed's own resolve path,
building the index a .dag authority would itself need in order to
evaluate, so a .dag realization would require the closure it computes.
The trigger names the ordering break rather than a date, and refuses a
partial migration explicitly: a split between a .dag classifier and a
seed walk would be two authorities for one decision.

Same defect crisp-boar-716 self-reported on #9095; same remedy, same
roster.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…t either side

#9028 touched cli_run.rs and the expected-red roster, so both sides had
edits. cli_run.rs auto-merged; the roster conflicted.

RESOLVED BY IDENTITY SETS, not by taking a side. Measured at the three
merge stages: base=201, ours=102, theirs=192. Neither side ADDED a row
(ours-minus-base = 0, theirs-minus-base = 0), main removed 9, this branch
removed 99, and the two removal sets are DISJOINT. The correct result is
therefore the intersection, 93 rows -- taking ours would have resurrected
main's 9, taking theirs would have resurrected this branch's 99, and both
would be a stale exemption readmitted in silence.

Built by starting from THEIRS and applying this branch's 99 removals, so
main's own edits survive by construction rather than by re-application.
All 99 applied, 0 unapplied.

THE REBUILD IS GUARDED THIS TIME. It rewrites a Cons chain only when
every head in it is a quoted string literal; the chunk AGGREGATOR, whose
heads are function calls, is passed through untouched. That guard is the
repair for the defect this branch already committed once -- an unscoped
rebuild blanked the aggregator to `Empty {}` and the roster evaluated to
zero identities.

RECOVERED A DROP THE REBUILD CAUSED: starting from theirs silently lost
this branch's 29-line annotation recording the 82-row correction, because
main never carried it. Re-applied from stage :2. That is the same
take-one-side-wholesale failure as the roster itself, in prose, and it
was found by checking rather than by review.

Verified: 0 conflict markers, 93 rows (= |ours n theirs|), 22 chunk
functions, no duplicate heads, every chain line brace-balanced,
annotation present, both sides' cli_run.rs changes intact.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…the note main left stale

#9089 added its own SeedGrowthJustification to the same closed roster this
branch adds to, so the import line and the roster note conflicted. The
roster FUNCTION auto-merged correctly and carries both.

RESOLVED ADDITIVELY, because this is not a choice between sides: both
imports kept, both rows in the roster, five entries total. Taking either
side would have silently unregistered the other's receipt -- and an
unregistered receipt is exactly the stop-line state the policy exists to
refuse, so a wrong resolution here fails open rather than loudly.

REPAIRED A STALENESS THAT WAS ALREADY ON MAIN: #9089 added its import and
its roster entry without naming its row in seed_growth_justification_roster_note,
so main's note listed three justifications while its roster carried four.
The note is a SECOND REPRESENTATION of the roster function (DESIGN §2/§3),
which is why it decayed silently and why no gate caught it. Repaired here
to name all five, with the standing fix recorded in the note itself:
derive the listing from seed_growth_justification_roster() rather than
re-author it.

Verified: 0 conflict markers, 0 unmerged paths, 5 roster entries (checked
with a pattern that matches `stage0_...` -- my first check used [a-z_]+
and silently dropped it, which is the same wrong-instrument error this
branch has already made twice), 5 imports, both receipt modules present,
and the expected-red roster unchanged at 93 rows with its aggregator and
annotation intact.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@gunbai-bot

gunbai-bot Bot commented Aug 24, 2026

Copy link
Copy Markdown
Contributor Author

Heads-up: a REQUEST_CHANGES on this PR went stale rather than being answered

review 55459 (codex, REQUEST_CHANGES) was filed against 254700925 on 2026-08-24T16:25. Your head is now 260973809, so that verdict no longer counts toward readiness — but nobody withdrew it and nobody re-ran codex against the new head. It stopped counting because pushes aged it out.

This PR currently has an approval on head and no current-head request-changes, so the only unmet merge requirement is checks. Those are pending/failing on the main-state RouteGapFreezeIntersection breakage rather than on anything branch-local. When #9133 lands and clears it, this PR flips straight to ready with that blocker still unaddressed.

I swept all 40 open PRs and this is one of five in that position, so this is a property of how readiness is computed, not a claim about your work — and it may well be that your pushes already fixed what codex raised. On #9062 I checked the two stale findings by hand and one of them genuinely was fixed.

What would settle it: either confirm on this PR which commit addressed each point of review 55459, or get a fresh codex verdict on 260973809. Either makes "answered" mean answered.

Not blocking, not merging, and no action needed from me — flagging it before the green wave arrives so it isn't merged on a readiness flag that a stale blocker fell out of.

— sent from smart-ram-730

Brian Searls added 2 commits August 24, 2026 21:28
…five

The closed roster conflicted three ways -- import, roster list, and the
prose note -- because main added bare_reference_scanner (#9102) while this
branch added reference_closure_binder. Both sides carried five entries
sharing four. Taking either side whole would have silently deleted the
other side's authority obligation rather than conflicting, which is the
failure main's own note warns about; the union is six.

The prose note is a second representation of seed_growth_justification_roster()
and it has now gone stale once and conflicted once. Both receipts are kept in
the merged text -- the #9089 staleness and this merge's arithmetic -- so the
next reader can check the union rather than trust it. The standing fix is
still to derive the listing from the roster function.

Verified no new diagnostic: the same entry evaluated on clean origin/main in
a separate worktree produces a byte-identical error set apart from this
branch's own additions and a one-line offset. The two "expected item
declaration" errors on `//` blocks are an artifact of the local gunbc shim,
which is a Jun 26 build predating the §4c annotation channel -- established
by the control reproducing one of them on a file this branch never touches.
gunbai-bot Bot pushed a commit that referenced this pull request Aug 24, 2026
… the hand-Rust receipt (review 55577)

Two findings, both verified against the code before fixing.

ONE: THE MODEL DISAGREED WITH ITS ONLY CONSUMER, AND I CAUSED IT.
`non_verdict_admits` -- modeled in v2.workflow.floor_non_verdict_admission and
mirrored in v1_compiler.cli_run -- answered `added.is_empty()`. The executing
gate `required_floor_outcome_is_clean` has nine conjuncts, two of which are
`non_verdict_unenrolled.is_empty()` (the added arm) AND
`stale_non_verdict.is_empty()` (the repaid arm). So the canonical model said
ADMIT exactly where its consumer said REFUSE.

The provenance matters because it is not an oversight in the original design: it
is the divergence review 55361 opened. That review correctly argued stale rows
must gate, I made the GATE gate, and I did not carry the change into the model or
its tests. DESIGN section 3 is the point -- a modeled authority whose consumer
disagrees is worse than no model, because it is cited as the rule while something
else enforces a different one.

WHAT WAS RIGHT ABOUT THE OLD RULE AND WHAT WAS WRONG. The rationale for admitting
repayment was that refusing it would red the merge that repairs the population
(#9020 repays 99 in one landing). Right about the goal, wrong about the subject:
what refuses is not the repayment, it is the roster row LEFT BEHIND asserting a
debt that no longer exists. Repaying and deleting the row are one act, which is
already how the #9020 merge-order deletion was planned.

So both paths now admit iff added and repaid are BOTH empty, and both suites gain
a control -- repayment WITH the row deleted is admitted. Without that pair the
tests cannot discriminate "stale rows refuse" from "repayment refuses", which are
opposite rules; the pair fails in opposite directions if either is wrong.

TWO: THE HAND-RUST RECEIPT WAS STALE.
It read +195 in cli_run.rs and +65 in claim_executor.rs and omitted
claim_executor's deletions entirely. Measured by git diff --numstat against
origin/main at this head: +203/-0 cli_run.rs, +60/-5 claim_executor.rs, +102/-0
the test file. The forward-freeze policy exists to make hand growth checkable, and
a receipt measured once and not re-measured as the change grew is a figure rather
than a receipt -- the row now says so and says it must be re-measured on every
push touching those files.

Also removed a blank line between one annotation block and the declaration it
attaches to, matching the convention every other annotation in that file follows
(DESIGN section 4c attaches annotations to a declaration). This is NOT claimed as
a fix for anything: a discriminating control through the actual parse sweep, with
and without the blank line, produced identical output, so the form was not what
any gate was refusing.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
briansrls pushed a commit that referenced this pull request Aug 24, 2026
…is paragraph never noticed (#9085)

DESIGN's CI bullet states its phase count in the present tense, on the
explicit ground that a knowingly-false recital in the canonical authority
is premise contamination. That count has been false since #9035.

MEASURED, not recalled. Run 32678275911 prints the roster from the
required mode itself:

  required-ci: phase parse (.dag: src/v1, dag, src/v2)
  required-ci: phase regen (first generation vs committed)
  required-ci: phase v2-emission (one entry, the board's producer)
  required-ci: phase floor (one prepared subject, one fold)
  required-ci: phases_run=4

`--required-v2-emission` is real and enrolled: the flag is parsed in
claim_executor, `gunbc.ci_layer_roots` carries its entry roster and its
dissolution condition, and fixtures/v2_emission_gate/ carries its green
subject. It landed in #9035 -- "today an emission break reached main and
no gate could see it" -- AFTER the 2026-08-21 ruling that cut the roster
to three.

THIS IS AN ADDITION, NOT A REVERSAL. The five phases that ruling deleted
stay deleted and stay enumerated below; #9035 restored none of them. The
scope narrowing that ruling declared is unaffected, and merge-admission
stamping remains on the unguarded list.

The sentence now records that the count has been wrong in BOTH
directions -- four, corrected to three, stale again at four -- because
that is the argument for stating it as a measurement rather than as a
recollection, and a reader who sees only the latest correction will
assume the previous one was the careless kind.

Found while diagnosing an unrelated floor red on #9020: the run's own
`phases_run=4` contradicted the document, and a lane had already quoted
the same figure back to me as if it were unremarkable.

Co-authored-by: Brian Searls <briansearls1@gmail.com>
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
Brian Searls added 2 commits August 25, 2026 00:25
Main gained witness_runtime_cause (#9137) while this branch carries
reference_closure_binder. Union is seven; both sides kept, per the rule
main's own note now states.

This file has conflicted on a roster union three times this evening, each
time because the prose listing is a second representation of
seed_growth_justification_roster(). Recorded that count in the note, since
the argument for deriving the listing is now a measurement rather than a
prediction.
…ch no longer removes anything

Resolved the floor_expected_red conflict by set algebra rather than by
choosing a side. Measured on the three merge stages:

  base   192 identities
  ours    93  (99 removed)
  theirs  65  (127 removed)
  additions on either side: 0
  comm -13 ours theirs: EMPTY

That last line is the whole resolution: main's roster is a strict SUBSET of
this branch's, so every identity this branch removed is already gone from
main. The merged roster is main's 65 and taking it loses nothing. An
intersection would have produced the same 65; taking a side by preference
would have been luck.

The branch's own annotation block is KEPT rather than swept away with the
rows it described -- it records run 32670979986 / stale_quarantine=82, which
nothing else carries, and a prose row deleted for one reason takes everything
in it unless its contents are enumerated first. It is marked superseded, with
the arithmetic above, so no reader takes it as describing a removal this
branch still performs.

ONE QUESTION LEFT OPEN rather than answered, because either answer would be
invention: those 82 left because THIS branch's fix made them pass, and main
removed them without that fix. Either they pass for an independent reason or
main's larger removal has a different warrant. Nothing in this merge
distinguishes the two. The floor run on the merged head decides it, and the
note says where to look if any of the 82 returns as a fresh red.
briansrls pushed a commit that referenced this pull request Aug 25, 2026
… and confined at identity grain (#9095)

* The floor's non-verdict expected-red population, sized and given a next-rung trigger

The brief this lands under reported 99 witnesses going PASSED to RUNTIME-ERRORED
across 330f63c..5482863 with the floor still green. Measured against the runs
those two commits produced, nothing changed in that range: both are green, both
report known_red_runtime_errored=142 over the SAME 142 identities (extracted and
diffed), both roster joins read roster=177 still_red=177, and the range's diff does
not touch floor_expected_red.dag. The described transition could not have occurred
either -- an unenrolled runtime error is already a gating conjunct via
outcome.failures, and an enrolled identity that passes reports known_red_now_passing,
also gating.

The real event is a HELD to RUNTIME-ERRORED reclassification, on 2026-08-23 in
ec2f3c3 (#8959): known_red_held fell 208 to 36 while runtime_errored appeared at
164. That commit revealed rot rather than causing it.

What survives from the brief is its second clause, and it is real: 142 enrolled
identities produce no verdict on every run and the floor reports failed=0.
required_floor_outcome_is_clean deliberately omits known_red_runtime_errored and
known_red_observation_unreadable, and that call is not disputed here -- the arm's own
comment reserves the change for an approved design. What was missing beside it is the
DESIGN 4b(2) obligation: a class parked below its ceiling must name its next-rung
trigger and size its population, and neither existed.

gunbc.floor_non_verdict_enrollment carries both. The population is measured (119 name
not in the loaded index, 13 type error, 9 undefined variable, 1 call contract
mismatch) and declared through a PopulationBasis arm as a one-run snapshot rather than
a bound, because nothing counts a witness that starts throwing tomorrow. The dominant
cause was checked by hand rather than assumed: four unresolved names all ARE declared
in the corpus, and the witnesses referencing them declare no import -- the refusal is
correct and the witness is unimportable, which is why the trigger makes population
repayment a precondition of the gating conjunct rather than a follow-up.

The witness asserts the census sums to the measured total, that no declared cause sits
at zero, and that the basis is still the snapshot arm -- so the next-rung transition
cannot be asserted by editing one row.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* The carrier's declaration RHS lives on the declaration's own line

`data <name>: <Type> =` followed by a newline before the expression is a parse error
in .dag -- "expected expression, found Newline" -- and three rows in the new carrier
were wrapped that way. Measured, not guessed: the module index refused the file with
that diagnostic on a remote dispatch, and with the rows unwrapped all three witness
functions evaluate and return true.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* The 99 came from a fix-carrying baseline, not from the #8959 reclassification

The dispatching session supplied the true cause and it is better than the one this
probe reconstructed: the brief was differenced against gunbc#9020's BRANCH HEAD, which
carries the resolution repair. Verified here rather than taken on report -- run
32680784546 (c767f4c) reads passed=10619 known_red_runtime_errored=43 against the
base's 10520 / 142, exactly -99/+99, with `planned` identical at 10791 on both. That
last equality is what let the mistake survive a sanity check: an equal planned
population reads as an equal baseline and is not one.

Section 2 previously attributed the 99 to the HELD -> RUNTIME-ERRORED reclassification
in ec2f3c3 (#8959). That reclassification is real and is why this population is
visible at all, but it is not where the brief's figure came from, and asserting a
wrong cause in the document that exists to correct a wrong cause is the same defect
one layer in. The old attribution is kept as a marked correction rather than deleted,
because a reader who saw the first version needs to find out it moved.

Section 1 is unchanged and was correct: there is still no regression in the named
range.

The carrier is deliberately NOT pre-adjusted to 43. Writing a branch's number into a
row measured on main would commit the fix-carrying-baseline error a second time,
inside the artifact documenting it. The PopulationBasis arm names the run and commit,
so the row is re-measured when #9020 lands.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* Freeze the non-verdict population's growth at identity grain: the composition was below floor

RULING (operator, relayed via the dispatching session): the observation arm is not
the defect. known_red_runtime_errored and known_red_observation_unreadable say
something TRUE -- this enrolled claim produced no Boolean verdict. What was wrong is
the COMPOSITION: required_floor_outcome_is_clean consulted neither, so the floor
returned CLEAN while an enrolled expected-red assertion had ceased to assert anything.
A diagnostic can describe the evidence truthfully while the gate draws a false
conclusion from it. Per seam: routing is mitigatable, printing the count is
mitigatable, printing failed=0 without distinguishing verdict incompleteness is a
misleading projection, and returning clean over an enrolled claim that produced no
verdict is BELOW FLOOR. Below floor is not a rung, so this could not wait on the
population being repaid, and the previous revision's 4b(2) row understated it.

WHAT ENROLMENT ADMITS: a known SEMANTIC VERDICT, not permission for the subject to
stop evaluating. Without a wall the expected-red arm absorbs an arbitrary regression
strictly INSIDE the enrolled population -- yesterday the witness answered false, today
it throws before reaching its assertion, no verdict exists, floor stays clean. An
UNENROLLED witness that throws already gates through the ordinary failure path, so the
absorption was never repo-wide and this commit does not claim it was.

THE WALL IS A GROWTH FREEZE, NOT A GATE ON THE POPULATION. v2.workflow.floor_non_verdict
carries the 142 identities; admission is that ADDED is empty. 142 -> 43 -> 0 is
admitted in any order; 142 -> 143 refuses; and a swap that repairs one identity while
a different one begins throwing refuses even though the count never moves. That last
case is why both sides are compared as IDENTITY SETS -- a count cannot see a swap, and
a repaired witness must not buy permission for an unrelated witness to lose its
verdict. It has its own witness, asserting the length equality beside the refusal so
the discrimination cannot be read as an artifact of differing sizes.

TWO DESIGN DECISIONS THAT ARE NOT ARBITRARY. The polarity is unenrolled-blocks, so an
EMPTY roster is the strictest state and a roster read failure cannot flatter a run --
the opposite polarity rebuilds the absorbing-fallback shape inside the mechanism
written to close one. And repaid does NOT gate, deliberately breaking symmetry with
floor_route_gap's stale-row refusal: copying it would red the floor on the merge that
repairs 99 of these (gunbc#9020), which is how a repository teaches people not to
repair debt. Added is walled; repaid is announced per identity with its remedy.

A non-verdict row not also in floor_expected_red is REFUSED, not warned. Only enrolled
identities reach these arms, so such a row can never fire -- unreachable, not empty --
and DESIGN is explicit that a check whose red cannot be authored is a decoration,
worse than absent because it is cited as coverage.

FloorAdmittedWithNonVerdictDebt separates unexpected_failures from verdict_incomplete
on the top line. `failed=0` is a sentence a reader can finish alone and finishes
wrongly; that misreading is what dispatched this whole lane against a regression that
did not exist.

Evidence: cargo check clean; four admission witnesses return true by execution
(unchanged admitted, repayment admitted with repaid=2/added=0, growth refused with
added=1, and the equal-count swap refused with added=1 and repaid=1); the roster
evaluates.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* The admission is one pure function of two identity sets, with its red on the seed path

The decision was taken INLINE IN TWO ARMS, which is two places one rule could drift
from the modeled admission it mirrors. It is now taken once, after the fold, over two
identity sets: the arms only RECORD what they observed. That was prompted as a way to
make the seed testable and turned out to remove a real second-representation seam.

THE SEED PATH NOW HAS A DISCRIMINATING RED, which the previous commit did not have --
its four witnesses exercised the MODELED admission, and a mirror can be wrong in ways
its carrier cannot see. Five unit tests drive growth, repayment, the equal-count swap,
and the empty roster through the Rust that actually gates.

MUTATION-CONTROLLED, because five green tests establish nothing on their own:

  stuck-admit (always true)                     -> 3 of 5 FAIL
  count-based (added.len() <= repaid.len())     -> 4 PASS, 1 FAILS
  restored                                      -> 5 pass

The second mutant is the load-bearing measurement. A count-based admission passes
every test except the swap, which is exactly the claim the design rests on: a count
cannot see one identity repaired while a different one begins producing no verdict,
and only the identity-grain comparison refuses that trade. Without that single case
the mutant ships green.

THE MIRROR'S RUNG IS NAMED rather than left implicit. There are now two
implementations of one rule -- v2.workflow.floor_non_verdict_admission and
v1_compiler.cli_run non_verdict_admission -- so the carrier records the seed half as
MITIGATABLE with its next-rung trigger being derivation from the carrier, the same
framing v1_compiler.required_regen_host already carries for its hand-mirrored
ordering. The class rung is the minimum across paths, so it is mitigatable.

RESIDUAL GAP, NAMED: none of this reaches the wiring from a real thrown witness into
the observed set. That path is still unexercised. It is narrower than "the seed wall
is untested" and it is not zero.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* Repayment and roster deletion are one act; and the hand-Rust growth gets its receipt

Both findings in review 55361 are correct. Neither is argued with.

FINDING 1 -- THE STALE ROW WAS A LIVE EXEMPTION. stale_non_verdict shipped
report-only under my argument that refusing it "punishes the fix", and the reviewer
found the hole: the identity is repaired, the row stays, and if that witness later
stops producing a verdict again it is ALREADY ROSTERED, so the exact regression this
wall exists to refuse is admitted in silence. Repayment without deletion turns a
bounded debt row into a permanent licence -- the absorbing fallback wearing the word
"diagnostic". Refusing does not punish a repair; it requires the repair to be
COMPLETE, the run names every row to delete, and this is the discipline
stale_route_gap and the expected-red staleness join already enforce. The asymmetry was
the error and consistency with them is the repair. Worth recording that the asymmetry
had an argument behind it and was endorsed before a reviewer found the hole -- an
endorsement is not a proof.

FINDING 2 -- THE FORWARD-FREEZE ROW WAS MISSING. Verified against the tree rather
than taken on report: gunbc.seed_growth_admission seed_growth_forward_freeze_policy_note
requires every PR adding hand-written src/v1 Rust to enumerate exact item identity,
.dag authority, why Rust is still needed, owning lane, deletion trigger, current
boundary, hand-item delta and hand-LOC delta, and rules unenumerated hand growth a
stop-line with no netting of deletions against additions. This change added hand Rust
and authored no row.

floor_non_verdict_seed_growth_justification now carries it: +8 citable items (three
production, five discriminating unit tests), measured deltas of +195 cli_run.rs, +65
claim_executor.rs, +85 in the new test file, lane v1-hand-queue-drain, trigger the
self-emitted claim executor, boundary named end to end. The two new
RequiredFloorOutcome fields, the added conjuncts, the roster read and the report lines
are deliberately NOT listed: none adds a declaration, so their disposition is
ExistingSeedItemModified and listing them would net modifications into an addition
census. The row is registered in the closed roster in seed_growth_admission.

Evidence: build clean; 5/5 seed admission tests; the seed-growth roster
well-formedness gate returns true over the extended roster (closed, well-formed, no
duplicate DeclarationRef keys); the carrier witness returns true.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* The roster needs a reachability edge, and the note that should have prevented this did not

CI refused during floor preparation: `floor_non_verdict_roster: no declaration named
'v2.workflow.floor_non_verdict.floor_non_verdict_roster' in this execution's loaded
index`. The host evaluates roster functions in v2.workflow.required_floor's frame, so
a roster module reaches the loaded index only via an import edge there. Mine had none.

THE FIX IS ONE LINE. What is worth recording is that required_floor.dag ALREADY
CARRIED TWO PARAGRAPHS documenting this exact failure happening twice before, saying
precisely what would go wrong and why -- and I read that file, added a roster, and
reproduced it a third time. A prose note that has now failed to prevent the thing it
describes on three consecutive occasions is not a wall; it is a record of a mechanism
defect. The list is a hand-maintained reachability closure that nothing derives and
nothing checks, and its violation surfaces only from a full CI run.

So the third paragraph says that, rather than adding a fourth warning: the structural
repair is for the host's roster evaluations to derive reachability from the qualified
names they call, at which point the import list and all three paragraphs delete
together.

THE POLARITY IS WHY THIS WAS LOUD. The roster read fails closed, so a missing edge
stops the floor instead of yielding an empty roster. Under the opposite polarity this
same omission would have produced a silently permissive wall -- the exact failure mode
this PR exists to close, reintroduced by an oversight nobody would have seen.

AND THE FAILURE IS THE DOCUMENTED CLASS, on its own author: "not in this execution's
loaded index" is the same diagnostic behind 119 of the 142 identities this roster
carries. The witnesses in that population reference declarations that exist while
declaring no import; so did I.

Evidence: required_floor's closure compiles with the edge -- exit 0, 0 blocking
errors, 126 advisory. The host's own roster read is exercised by CI, which is the test
that failed and is the test that has to pass.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* Name the axis the wall does not confine: the roster itself is not compared against the base

The dispatching session, relaying its second reader, asked whether R (subset of) B is
enforced. Checked against the implementation rather than my description of it: it is
not. non_verdict_admission takes observed and roster-at-head, and no base reference
exists anywhere in that path. So a change that makes a NEW identity stop producing a
verdict may add its row IN THE SAME CHANGE, satisfy both executing arms, and be
admitted — the roster legalising the regression it exists to bound. That is review
55361's finding (the row becomes the permission) reached through authorship instead of
through survival.

NO MECHANISM CHANGES HERE. The wall does exactly what it did when CI went green. What
was defective was the CLAIM: the module was named a growth freeze and the title said
"frozen against growth at identity grain", from which a reader concludes growth is
refused. That is rung inflation in DESIGN 4b(1)'s exact sense — worse than sitting
low, because an inflated class never ranks for climbing — and it was false in the
artifact independent of whether the conjunct ever lands. So the header now opens with
what is confined and what is not, BEFORE it describes the admission, and the title
says confines rather than freezes.

TWO AXES, ONE EXECUTES. Observed-vs-roster is walled by execution every run: growth
refuses, stale exemptions refuse, both at identity grain. Roster-vs-base-roster is not
walled at all — mitigatable, confined by review diligence, which is strictly weaker.
The class rung stays the minimum across axes and paths.

NEXT-RUNG TRIGGER, and it is specified at identity grain for the same reason the
executing arm is: the floor reads the roster at the merge base and refuses when the
head roster is not a subset of it, established against a discriminating red. Not
satisfied by counting rows — an addition and a deletion in one change leave every
count unmoved.

WHY THE CONJUNCT IS NOT IN THIS PR: reading the base roster means evaluating a .dag
module out of a git blob in a separate resolve context. That is real machinery
deserving its own discriminating red, not a paragraph bolted onto a change already at
full approval with green CI.

AND THE HONEST ACCOUNT OF WHY THE HOLE EXISTS, which is the reader's framing and
better than mine: the admission was SPECIFIED against a base OBSERVATION, and
substituting the roster for it is what makes the wall runnable in a single pass. The
substitution is sound exactly to the degree the roster cannot be grown freely, so
R (subset of) B is the condition that makes the cheap design safe rather than a nicety
on top of it.

Evidence: required_floor's closure compiles at 0 blocking errors (126 advisory); the
carrier's closure at 0 blocking errors (276 advisory).

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* The growth trigger names the target-base tip, not the merge base

A precisely-wrong trigger is worse than a vague one, because someone implements
exactly what it says. The previous text named the MERGE BASE as the future authority
for the roster-growth law. It is the wrong baseline, and the failure is an admission
rather than a spelling.

EXEMPTION RESURRECTION. M = {a} at the merge base. Main later repairs `a` and deletes
its row, so the current base tip carries B = {}. A stale branch reintroduces `a` and it
throws: H = {a}, O = {a}. Then O = H passes and H subset-of M PASSES — readmitting an
exemption main has already paid off. H subset-of B fails, which is the law actually
wanted. The mirror is a false red: a row main legitimately adds after the fork sits in
B and in the composed result but not in M, so a subset-of-M rule refuses a branch that
did nothing wrong.

So the law is H subset-of B — the roster in the COMPOSED RESULT is a subset of the
roster at that result's CURRENT TARGET-BASE PARENT — and the trigger now says so, in
both the carrier and the roster header, with the counterexample beside it so the next
author cannot re-derive the weaker rule from the stronger sentence.

CONSEQUENCE FOR THE CUT'S SHAPE, recorded because it changes how the follow-up is
scoped: a BRANCH-HEAD-ONLY RUN CANNOT ESTABLISH THIS LAW. It needs the composed result
AND that result's base parent, which is strictly stronger than "read the roster at
another commit". The trigger also pins the receipt the cut must carry —
target_base_sha, composed_result_sha, base_roster_identity_set,
result_roster_identity_set, added_exemptions = result - base — and requires the same
read-failure polarity the executing arms already have.

NO MECHANISM CHANGES. The two executing axes are untouched and the "confined at
identity grain" claim is unaffected; only the future authority named in the trigger was
wrong. Amended in this PR rather than deferred to the follow-up because the head had
already moved for the main integration, so the approval cycle was already reset and the
marginal cost was zero.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* The no-import story does not cover the whole bucket: six of the eight are inside it

An independent census (valiant-lynx-227, run 32743601436) partitions the erroring
modules 53 declaring NO import against 8 that declare imports and error anyway.
Cross-joining that against this carrier's own cause census — which neither lane had
done — puts SIX of those eight inside the `no declaration named` bucket. The bucket is
not homogeneous, and §5's four hand-checked names, all lacking imports, leave the
impression that it is.

ONE OF THE SIX READ IN FULL, so the second shape is evidence and not inference:
v2.lens.vacuity_test fails on `nat_add_left_identity_input` while declaring ten
imports, among them v2.test.nat_semiring.rung_5 — a module that REFERENCES that name
but is not the module that DECLARES it (v2.std.algebra_laws.nat_semiring). Importing a
module does not transitively supply the names its own declarations reach for. So the
second shape is an INCOMPLETE import set, not an absent one.

THE CONCLUSION IS UNCHANGED AND SLIGHTLY STRENGTHENED: both shapes are authored
defects, both repaired by adding the import that declares the referenced name, neither
is a floor scope defect. "Widen the scope" is still the wrong trigger. What moves is
the repairer's expectation, which matters to the lane inheriting the backlog. The
remaining five are established only as "declares imports AND lands in this bucket";
their specific missing imports are unread, and the row says so.

ALSO RECORDED: the census reached me first as "142 rows but 133 distinct, 9 appearing
twice" — which would have meant this roster carried nine stale exemptions. False, and
instructively so: the floor COLUMN-PADS the identity, so short names are followed by
spaces before `ERROR in`, and an extraction using `[^ ]*` cannot cross that padding.
The pattern silently selected for long identifiers and dropped exactly nine rows. The
nine were MISSED, not duplicated — sign inverted, the same shape as the brief this
document exists to correct. Two independent diagnoses converged on the padding cause.
The durable rule: a distinct-vs-total discrepancy is a tell about the READER before it
is a tell about the population, and the control is to count with a different pattern
than the one that extracts.

AND THE CAUSE CENSUS IS DECLARED A CLAIM, NOT A RECEIPT. The floor log carries no cause
text beside the ERROR row, so the 119/13/9/1 partition cannot be checked by anyone
unwilling to re-run a floor that OOMs in a session container — unverifiable by
construction. Printing the cause beside the identity is a smaller change than this
wall and would have made the false alarm impossible; it is a separate subject, and
until it lands the partition is a claim.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* The modeled authority still said repaid never gates: one rule, two answers

Review 55515, correct. When stale_non_verdict was flipped to gating after review
55361, the reversal reached the roster header, the seed's field doc and the gate
conjunct — and missed the docstring on v2.workflow.floor_non_verdict_admission
non_verdict_repaid, which went on saying "Reported, never gating: a repaid row must not
red the run that repaid it".

That left two authorities stating OPPOSITE RULES about one admission, inside the module
whose whole subject is single authority, in a PR whose body cites review 55361 for
exactly this class. The mechanism was never wrong — the executing code has refused
stale rows since that fix — which is precisely why the sentence survived: nothing that
runs reads it.

FIXED IN BOTH PLACES, because the reviewer found one instance and there were two. The
docstring now states the rule and records that it was reversed and why. The PR body's
"repaid does not gate" paragraph carried the same stale claim and is corrected as a
struck-through amendment rather than a silent rewrite — a reader of the earlier
revision saw the reversed claim, and quietly replacing it would hide the drift instead
of recording it.

SWEPT FOR OTHERS: grepped every carrier, the seed, the probe document and the PR body
for surviving assertions that repaid or stale does not gate. The only remaining hit is
the verbatim quote of the original brief in the probe document, which is a quotation
and stays.

WHAT THIS IS AN INSTANCE OF, recorded because it is the day's recurring shape: prose
asserting more or other than the mechanism does. Every defect found in this PR has been
that — the failed=0 line, the diagnostic stale row, the growth-freeze title, the
merge-base trigger, and now a docstring left behind by its own reversal. None was a
coding error. All were caught by someone checking a claim against the tree.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* Drop the "65 distinct signatures" figure: it counted names, not causes

valiant-lynx-227 found that the floor's [floor-known-red-causes] census keys on the
FIRST TWELVE WORDS OF THE MESSAGE PROSE, and that prose embeds the missing NAME. So
`no declaration named 'srv3_install_hang_no_router_lease_ms'` and `no declaration named
'extdeps_cargo_build_module'` are two distinct signatures. It reports ~65 for a
population with roughly four actual causes — an artifact of the key, reading as "many
roots" when the truth is "one root, many names". A plan sized off it is sized wrong,
toward heterogeneity, when this population is mostly one repair.

The same line is SILENTLY TRUNCATED AT 20: the printed rows sum to 93 of 142
identities, with 49 identities and 45 signatures dropped and nothing saying so.

WHAT THIS DOES AND DOES NOT REACH, and the boundary is the derivation rather than luck.
The 119/13/9/1 partition was folded over the 142 PER-IDENTITY
KNOWN-RED-RUNTIME-ERRORED lines and sums exactly to 142 — neither prose-keyed nor
truncated — so it stands. The population count and the identity set stand too; the
executed set match settles both. What does not survive is any DISTINCTNESS or
HETEROGENEITY claim, which is precisely the one figure this document took from that
line. It is deleted rather than annotated, because an artifact carried with a caveat is
still cited as a count.

ALSO CORRECTED, IN MY OWN FAVOUR, WHICH IS WHY IT IS WORTH WRITING DOWN: an earlier
revision of this document called the partition "a claim, not a receipt" and
"unverifiable by construction", on the assumption I had hand-derived it. That was too
harsh and is now scoped to what is actually unverifiable — there is no per-identity
cause field, so the partition cannot be JOINED BACK to identities by a reader, which is
a narrower and truer statement than "unverifiable".

The root — ClaimOutcome::RuntimeError carrying message: String built by format! over an
already-closed 27-arm InterpError, so a typed cause is destroyed at the witness
boundary and guessed back by word-slicing at the reporting site — is being repaired
under its own item and is not held by this PR.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* Delete the probe board, keep the finding on the carrier: the measurement corpus was bankrupted under this PR

Main landed #9132 (bankrupt the measurement corpus) while this branch was open,
under an operator ruling this change is directly subject to: name the
instrument, never transcribe its output. 164 files and 44,736 lines went out of
docs/probes/, every .sh/.py instrument kept.

docs/probes/expected_red_non_verdict_population_2026-08-24.md is a transcription
board of exactly the class that ruling deletes. After merging main it was the
ONLY file left in that directory -- one document re-establishing a corpus main
had just emptied, on the same day, which is the attractor DESIGN section 3 names
rather than a document that happened to survive. It is deleted here.

WHAT IS NOT LOST, and why the deletion is not a coverage cut. The measurement
never depended on the board: floor_non_verdict_cause_census and
floor_non_verdict_measured_total are typed rows, and
floor_non_verdict_population_basis carries MeasuredOnOneRun { run, head_commit },
so every figure names the run and head it was taken on. That is the receipt-vs-
figure distinction a markdown board structurally cannot carry, which is the
ruling's own argument for the cut.

WHAT MOVED. One irreducible fact in the board was rationale rather than
measurement and had no other home: the dispatched brief -- 99 witnesses PASSED ->
RUNTIME-ERRORED across 330f63c..5482863 with the floor reporting failed=0
-- is false in its first half, shown by an identity-set diff, because the 99 were
baselined against a branch head already carrying the fix and so were compared to
a tree they never ran in. The carrier's existing prose REFERRED to that false
regression without ever stating it; it now states it once, as a section 4c
annotation on the declaration it explains. A first draft of that annotation also
restated the fix-carrying baseline the preceding block already covers, and was
trimmed to the half that was actually missing.

RESIDUE, checked against the three classes #9132 declared for its own cut and
found empty here: no .gitignore un-ignore row, no prose note in any module, and
no witness asserting the filename. Nothing in the tree references the deleted
path, so this leaves none of the debt that commit declared for itself.

Verified: the module parses and evaluates after the annotation --
floor_non_verdict_measured_total returns 142, the host refusing only on its
exit-code contract (an Int is not a ProcessExit), which it can raise only after
computing the value.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* Make the modeled admission say what its consumer does, and re-measure the hand-Rust receipt (review 55577)

Two findings, both verified against the code before fixing.

ONE: THE MODEL DISAGREED WITH ITS ONLY CONSUMER, AND I CAUSED IT.
`non_verdict_admits` -- modeled in v2.workflow.floor_non_verdict_admission and
mirrored in v1_compiler.cli_run -- answered `added.is_empty()`. The executing
gate `required_floor_outcome_is_clean` has nine conjuncts, two of which are
`non_verdict_unenrolled.is_empty()` (the added arm) AND
`stale_non_verdict.is_empty()` (the repaid arm). So the canonical model said
ADMIT exactly where its consumer said REFUSE.

The provenance matters because it is not an oversight in the original design: it
is the divergence review 55361 opened. That review correctly argued stale rows
must gate, I made the GATE gate, and I did not carry the change into the model or
its tests. DESIGN section 3 is the point -- a modeled authority whose consumer
disagrees is worse than no model, because it is cited as the rule while something
else enforces a different one.

WHAT WAS RIGHT ABOUT THE OLD RULE AND WHAT WAS WRONG. The rationale for admitting
repayment was that refusing it would red the merge that repairs the population
(#9020 repays 99 in one landing). Right about the goal, wrong about the subject:
what refuses is not the repayment, it is the roster row LEFT BEHIND asserting a
debt that no longer exists. Repaying and deleting the row are one act, which is
already how the #9020 merge-order deletion was planned.

So both paths now admit iff added and repaid are BOTH empty, and both suites gain
a control -- repayment WITH the row deleted is admitted. Without that pair the
tests cannot discriminate "stale rows refuse" from "repayment refuses", which are
opposite rules; the pair fails in opposite directions if either is wrong.

TWO: THE HAND-RUST RECEIPT WAS STALE.
It read +195 in cli_run.rs and +65 in claim_executor.rs and omitted
claim_executor's deletions entirely. Measured by git diff --numstat against
origin/main at this head: +203/-0 cli_run.rs, +60/-5 claim_executor.rs, +102/-0
the test file. The forward-freeze policy exists to make hand growth checkable, and
a receipt measured once and not re-measured as the change grew is a figure rather
than a receipt -- the row now says so and says it must be re-measured on every
push touching those files.

Also removed a blank line between one annotation block and the declaration it
attaches to, matching the convention every other annotation in that file follows
(DESIGN section 4c attaches annotations to a declaration). This is NOT claimed as
a fix for anything: a discriminating control through the actual parse sweep, with
and without the blank line, produced identical output, so the form was not what
any gate was refusing.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* Retire the prose that still states the rule the reversal replaced, and the citation naming a test that no longer exists (review 55591)

The reported finding, plus two of the same class the sweep for it found.

REPORTED: the `observed_repaid` fixture comment still read "Must be admitted --
refusing it would red the merge that repairs the population", directly above the
test that now asserts the opposite. Fixed, and it now says WHY the refusal is not
a penalty on repayment: what refuses is the roster row left standing, not the
repayment, and the deleted-row form is admitted by the fixture below it.

FOUND BY SWEEPING FOR THE CLASS RATHER THAN THE INSTANCE:

(1) The same defect in the Rust mirror. `non_verdict_admits`'s docstring still
described the retired asymmetry -- "so a caller cannot accidentally admit on
`repaid` -- the asymmetry between the two is the content of this wall" -- sitting
directly above a body that now refuses on both arms. Rewritten to state both
refusal reasons, with the correction dated.

(2) A STALE CITATION, and this one would have shipped silently.
`floor_non_verdict_seed_growth_justification` cited
`repayment_is_admitted_and_reported_per_identity`, which the previous commit
RENAMED. That is a DESIGN section 3 fabricated symbol -- and the mechanism that
used to catch it, the cited-symbol census, was removed from CI on 2026-08-23, so
nothing in the repository would have refused it. Verified the whole file by hand
instead: all 12 DeclarationRefs resolve to a definition in the tree. The new
control test is added as a cited row, since a hand-authored seed item that is not
enumerated is exactly what the forward-freeze policy refuses.

Receipt re-measured, as the row itself now requires on every push touching these
files: +210/-1 cli_run.rs, +60/-5 claim_executor.rs, +102/-0 the test file.

THE PATTERN, recorded because it is now four for four in this PR: every defect
found here has been PROSE CLAIMING SOMETHING OTHER THAN WHAT THE MECHANISM DOES
-- the failed=0 headline, the "diagnostic" stale row, the growth-freeze title, the
merge-base trigger, this docstring, this fixture comment, this citation. Not one
was a coding error. That is the cost DESIGN section 4c prices when it calls prose
commentary modeling debt: it cannot be mechanically joined to the code it
describes, so a reversal updates the assertion and leaves every sentence about it
standing.

Verified by execution: both witnesses return true.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: gunbc-ci-auto-heal <gunbc-ci-auto-heal@users.noreply.github.com>
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
…sentence

Main added floor_non_verdict (#9095's lane) while this branch carries
reference_closure_binder. Union is eight.

THE RESOLUTION IS ALSO THE DIAGNOSIS. seed_growth_justification_roster()
auto-merged cleanly to all eight rows -- git handled it, no human involved.
The PROSE SENTENCE listing the same eight was the only conflict in the file.
That is the cleanest statement of the problem available: the authority merges,
its restatement does not, and the restatement is what costs a resolution every
single time.

Count recorded in the row: five roster unions this evening, every one a
conflict in this sentence and none in the function. The argument for deriving
the listing from seed_growth_justification_roster() stopped being a
prediction four unions ago; it is now a measurement with a denominator.

Rows unchanged in substance -- both sides kept, per the rule the note already
states, because taking either side whole silently deletes the other branch's
authority obligation rather than conflicting.
Brian Searls added 2 commits August 25, 2026 02:30
… six

Main added parse_refusal_location while this branch carries
reference_closure_binder. Union is nine, and the prose note names all nine.

SIX FOR SIX. On every one of the six roster unions this branch has resolved
tonight, seed_growth_justification_roster() auto-merged with no human
involved, and the prose sentence listing the same rows was the ONLY conflict
in the file. Not four of six, not usually -- every time.

The two carry identical information. Only one generates work, because a merge
tool can reconcile a list of rows and cannot reconcile a paragraph. That is
the DESIGN section 2 second-representation argument stated as a ratio instead
of a preference, and it is recorded in the row so the next person to propose
deriving the listing does not have to re-derive the evidence for it.

Rows unchanged in substance: both sides kept, per the rule the note states,
because taking either side whole deletes the other branch's authority
obligation without conflicting.
@gunbai-bot gunbai-bot Bot mentioned this pull request Aug 25, 2026
6 tasks
Brian Searls added 3 commits August 25, 2026 03:30
review 55667 is correct. `reference_resolution_facts` builds an
`ExprVarClassification` and never reads `classify.refusals` or
`classify.reconciles()` before publishing edges. An unsupported binder form
means the binder set is incomplete, so a name that IS bound can be recorded
as a free reference or the reverse; the graph is then published under-bound
or widened while the refusal sits unread in a field. The floor's index build
checks both correctly -- this is the same judgment with one consumer
enforcing it and one ignoring it, which is the fail-open the classification
exists to remove.

TWO CAUSES, NOT ONE. A binder refusal names a syntax the collector must
learn; a reconciliation miss names an accounting defect in the collector
itself. Different remedies, so one reason symbol each -- collapsing them
would route both to whichever remedy the shared symbol suggested.

SKIP-AND-RECORD, matching this producer's three existing refusal arms
(`unreadable`, `no-module-line`, `parse-failed`). The file is excluded from
the published edges and its exclusion is typed, located and countable through
`reference_accounting_refusals`. That is what separates it from the
empty-observation narrow: a skipped file is visible in the refusal channel
rather than silently absent from the graph.

RECEIPT UPDATED, and the near-miss is worth recording. The hand-LOC figure
moved +853 -> +887. My first edit added a SECOND figure in the header while
the original stood at line 49 -- two counts for one fact, in the receipt
whose entire purpose is to enumerate growth, three hours after gunbc#9151
was sent back for exactly that shape in a neighbouring file. Caught before
commit by grepping the file for the thing I had just written rather than for
the thing I had in mind. One figure now, updated in place, with its basis
stated: measured against the merge base, not against main's tip, because a
diff against a moving tip counts main's own edits as this change's growth.

EVIDENCE, and its limit. `cargo check -p v1-compiler` passes. That is a
positive control only: the two new arms have NO executing discriminating
test, because reddening them needs a .dag on disk with unsupported binder
syntax plus a call over a temp root, and this producer's fixture harness
takes source strings rather than roots. The classifier's own refusal path IS
covered (the fixture asserts `refusals.is_empty()`); what is uncovered is
this consumer's response to it. Naming that gap is the honest report -- the
missing root-based fixture is the next-rung trigger, not a claim of coverage.
…resent

review 55673 is right, and the overcount was not a miscount. classify,
refuse_binder and reconciles are methods on `impl ExprVarClassification<'_>`
(cli_run.rs, one impl block, three methods -- verified by reading it), and
std.decl_ref has no ImplMethod field. A DeclarationRef naming an impl method
is not a row that happens to be wrong; it is a row whose referent the carrier
cannot express, in a census whose entire value is exactness.

gunbc.seed_growth_admission already says impl methods are uncitable and
already carries the precedent: stage0_rust_observation_seed_growth_justification
documents `impl std::fmt::Display for Stage0CargoBinManifestParseRefusal` as
prose for this reason. So the correction follows that precedent rather than
inventing a spelling -- the three are enumerated in prose, by name, with what
each does. They are still hand Rust and still enumerated; what they are not
is citable, and stating the distinction is the point.

collect_every_var_name came out for a different reason, and it was this
receipt contradicting itself: it lives inside mod
reference_collector_binder_fixtures, which the same paragraph already names
as the enumeration unit because the module is what would be deleted. Counting
the module and one of its members double-counts.

HAND-ITEM DELTA: +9 -> +5 citable (4 production, 1 test module), plus 3
uncitable impl methods in prose. The four that survive are genuinely citable
and I checked each: ExprVarClass (enum), ExprVarClassification (struct),
binder_names_of and pattern_binder_names (free functions, not methods).

This is the second correction to this receipt in one PR -- the first was a
stale LOC figure. Both are the same class the receipt exists to prevent, in
the receipt itself, which is worth saying plainly rather than fixing quietly:
an item-grain census is only worth its exactness, and mine was wrong twice
before a reviewer had to point at it.

EVIDENCE: prose and row-list only, no behaviour. Evaluating this module's
justification on the pre-edit and post-edit trees gives byte-identical
diagnostic sets (203 lines, zero diff); the single diagnostic naming this
file is the stale local shim's inability to parse a section 4c annotation
block, present identically before and after.
@briansrls
briansrls merged commit 80ba875 into main Aug 25, 2026
1 check passed
@briansrls
briansrls deleted the session/merry-raven-395 branch August 25, 2026 14:49
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant