Skip to content

witnesses.yml: the bundled receipt upload's if-no-files-found evaluates once, so one step per file instead - #8877

Merged
briansrls merged 6 commits into
mainfrom
session/fierce-lynx-647-floor-roster-artifact
Aug 23, 2026
Merged

briansrls merged 6 commits into
mainfrom
session/fierce-lynx-647-floor-roster-artifact

Conversation

@gunbai-bot

@gunbai-bot gunbai-bot Bot commented Aug 22, 2026 •

Copy link
Copy Markdown
Contributor

What this is now

#8875 landed the upload of the three floor disposition receipts while this PR was open. This is not a competing implementation of that — it is the remainder plus one shape change, and the shape change is the substance.

Reduced from what it was: this branch was rebuilt from origin/main rather than rebased, so the diff is only what main lacks.

1. if-no-files-found: error cannot fire in the bundled shape — measured, not preferred

Main's step names three paths under one artifact with if-no-files-found: warn. The obvious repair is to flip that flag. It does not do what it looks like it does.

actions/upload-artifact resolves every path pattern through one globber into one combined file list — findFilesToUpload takes a single searchPath, and where multiple search paths exist it computes their least common ancestor and returns a unified array — then evaluates the flag exactly once:

if (searchResult.filesToUpload.length === 0)

…and only inside that branch switches on warn / error / ignore.

So with three paths and one file missing, the combined list is non-empty, the condition is false, and error never fires. Two files upload, one is absent, and nothing says so. Flipping warn → error changes behaviour only when all three are missing.

A missing long_home_storage_agreement.tsv — the row that records a PATH/MODULE long-home disagreement, and the first hypothesis any recurrence of the originating incident has to eliminate — is silent under both flag values.

One step per file is what makes each absence reportable, because each step's search list is the one file it names. That moves single-file absence from silent to refusing by construction (§5), rather than by a flag that cannot reach the case.

Source read rather than recalled: actions/upload-artifact src/shared/search.ts findFilesToUpload, src/upload/upload-artifact.ts run.

2. The filenames were spelled twice each and are now spelled once

Six literals: the job env that tells the writer where to write, and the uploader that names what to collect. Each pair was free to drift silently in the worst direction — the uploader looks for a file the writer never wrote, and the failure reads "the floor produced no roster" when the truth is "the writer and the uploader disagree about a filename". They are now six data rows, each read by both sides. That disagreement is unrepresentable rather than caught.

3. The toolchain-environment probe

Unchanged from the previous revision, and still absent from main. spawn rustfmt: No such file or directory (os error 2) reports the syscall and nothing about the environment that produced it, so the failure destroys its own evidence and every occurrence is exactly as unreadable as the last — two sessions produced three mutually incompatible readings from it in one night, all three refuted by data none of them had.

It records and it never refuses: every probe falls back to a printed marker rather than a nonzero exit, so the step cannot fail and needs no continue-on-error (which would be a step that may report a verdict and has been told to be ignored — the escape-hatch shape §5 forbids). It runs on green jobs too, because a zero is readable only beside a nonzero and today there is neither.

What this replaces, and what it carries forward

Main's witness_floor_disposition_upload_step and its bound-step entry are deleted in this diff — there is never an interval with two upload authorities (§3 replacement migration, not a second path beside the first).

Its prose is carried forward into the new step, not dropped with its function: the originating incident (sixteen failures in witness families the diff could not reach, fifteen identities reported moved, the rows naming which fifteen already gone, two independent recomputations disagreeing with the floor's own counts at both ends), the long_home_storage_agreement.tsv rationale, and the cancellation-exclusion rationale.

What this does not answer

A row reading Planned means the identity was discovered and routed. It does not mean the claim executed, and it does not mean it passed — ClaimOutcome::Pass increments a counter and discards the identity, so no execution roster exists anywhere to upload. This narrows the question; it does not close it.

error is safe on all three, and that was checked

cli_run.rs guards the join file on roster_join_active (GUNBC_EXPECTED_RED_ROSTER_JOIN.is_some()), and this job sets it; the other two are guarded only on their own paths being set, likewise set here. All three are written whenever the floor reaches its writes. The one way a file is absent is a refusal that returns before them — a run whose line has already stopped, where a second red is noise rather than a fabricated verdict.

This PR is the instrument; #8874 is the fix. Neither substitutes for the other.

The probe here fixes nothing and refuses nothing — it records. Do not read it as handling the rustfmt question.

#8874 is the fix: it asks for the rustfmt component and makes the run step declare that it consumes it, which is both what makes the component present and what lets the capability closure refuse the shape that produced the failure. It is open, MERGEABLE, and still needed independently of this PR.

The probe exists for the case the fix does not cover — a different cause, or an incomplete one. Its value is that the next occurrence is diagnosable on first sighting instead of third, which is what the last one cost: two sessions, three mutually incompatible readings, all three refuted by data none of them had.

A fix removes one cause; an instrument reads whatever the next one turns out to be. So #8874 landing does not retire this step, and this step landing does not close #8874.

@gunbai-bot

gunbai-bot Bot commented Aug 22, 2026

Copy link
Copy Markdown
Contributor Author

Scope widened, on the parent's ruling: all three discarded TSVs, plus an environment recorder

The original PR uploaded one file. Two changes since, both directed:

1. All three TSVs, not one

The floor writes three files on every run and discards all three. cli_run.rs reads GUNBC_REQUIRED_FLOOR_DISPOSITION, GUNBC_EXPECTED_RED_ROSTER_JOIN and GUNBC_LONG_HOME_STORAGE_AGREEMENT, all three are already set on this job, and witnesses.yml had zero upload-artifact steps. That is one unwired last hop with three instances, not three similar things — so it is fixed at the class. Fixing one site would have left the other two for someone to rediscover, paying the whole investigation again to reach a conclusion already written down.

The single-authority path move is now load-bearing three times over: six literals would otherwise be free to drift into the worst failure shape, where if-no-files-found: error fires and the message reads "the floor produced no roster" when the truth is "the writer and the uploader disagree about a filename".

The strongest case for this PR is the run that is red right now. --required-ci runs three independent phases, and a failure in one does not skip the others: run 32556977241 refused in regen on spawn rustfmt and the floor still ran to completion afterwards (phases_run=3 failed=1, planned=10439 executed=10439 passed=10132). So on a refused run the three files are still written — and with these steps the uploads publish the roster of a refused run, which is the case the artifact is most wanted in. We would have had it for today's failure.

One case does red all three uploads, stated in advance so nobody spends an investigation on it: a floor phase that refuses before its writes — the early return on a required-floor refusal such as ClaimIdentityCountsDisagree — produces no files, and the three uploads then red on top of it. That is a genuinely stopped line and the extra reds mask nothing.

if-no-files-found: error was checked, not assumed, since error on a file a run does not always write would redden green runs. The join file's write is guarded on roster_join_active, which is GUNBC_EXPECTED_RED_ROSTER_JOIN.is_some() — set here. The other two are guarded only on their own paths, likewise set. So all three are written whenever the floor reaches its writes, and the one way a file is absent is a refusal that returns before them: a run whose line has already stopped, where a second red is noise rather than a fabricated verdict.

2. An instrument for a failure that destroys its own evidence

spawn rustfmt: No such file or directory (os error 2) refused a required run today. The message reports the syscall and nothing about the environment that produced it, so two sessions produced three mutually incompatible readings from it in one night — a per-host fact, a runner lottery, a missing component — and every one was refuted by data the reading did not have. The rustup toolchain logged component rustfmt is up to date in the same job that could not spawn it, so a bare PATH lookup found neither a rustup shim nor a toolchain binary, and no run records which of those exists.

The new step records hostname/RUNNER_NAME, PATH, which -a rustfmt, which -a cargo rustc rustup, the cargo bin directory listing, rustup which rustfmt, rustup toolchain list and rustup default.

It runs on green jobs too, deliberately: there is currently no baseline. Nobody knows what a working job's PATH or toolchain list looks like either, so a failed run's environment could not be compared against anything even if we captured it. A zero is readable only beside a nonzero, and today there is neither.

It is guarded on !cancelled() alone — weaker than witness_floor_precondition, which also requires the build to have succeeded. A build failure is precisely a case where the environment is in question, so gating the recorder on the build would blind it exactly when it is wanted.

Every probe is total by construction — each falls back to a printed marker rather than a nonzero exit, so the step cannot fail and needs no continue-on-error. That is not cosmetic: continue-on-error is a step that may report a verdict and has been told to be ignored, which is the escape-hatch shape §5 forbids. A step with no verdict to give is a different object, and writing it that way keeps it unmistakable. It also means a tool's absence is recorded as a line rather than as a dead step.

Unchanged from the approved version

if-no-files-found: error on all three, witness_floor_precondition on all three uploads so a red floor still publishes, and the honest limit, restated verbatim because it is the part a reader skips:

A row reading Planned means the identity was discovered and routed. It does not mean the claim executed, and it certainly does not mean it passed. cli_run.rs:41702 is ClaimOutcome::Pass => outcome.passed += 1 — the identity is discarded at accumulation, so no execution roster exists anywhere to upload. This narrows the #8860 question; it does not close it.

Verified: regen clean, YAML diff contains only the new steps with the env rows byte-unchanged, and workflow_capability_closure_witness the_live_witness_floor_job_closes_its_capabilities returns true with all four new steps enrolled.

— sent from fierce-lynx-647

…e flag it relies on evaluates once

gunbc#8875 landed the upload of the three floor disposition receipts as one
artifact naming three paths, with `if-no-files-found: warn`. Flipping that flag
to `error` does not produce the guarantee it looks like it produces.

actions/upload-artifact resolves every path pattern through ONE globber into ONE
combined file list -- `findFilesToUpload` takes a single searchPath, and where
multiple search paths exist it computes their least common ancestor and returns a
unified array -- then evaluates the flag exactly once, on
`searchResult.filesToUpload.length === 0`, and only inside that branch switches
on warn / error / ignore.

So with three paths and one file missing the combined list is non-empty, the
condition is false, and `error` never fires: two files upload, one is absent, and
nothing says so. The flag reaches only the all-three-missing case. A missing
long_home_storage_agreement.tsv -- the first hypothesis any recurrence of the
originating incident has to eliminate -- is silent at either flag value.

One step per file is what makes each absence reportable, because each step's
search list is the one file it names. That is a property of the shape, not of the
flag, and it moves single-file absence from silent to refusing by construction.

Main's `witness_floor_disposition_upload_step` and its bound-step entry are
deleted in this same diff, so there is never an interval with two upload
authorities. Its prose is carried forward into the new step rather than deleted
with its function: the originating incident, the long_home rationale, and the
cancellation-exclusion rationale.

Also lands the §3 fix main does not have. The three filenames were spelled twice
each -- once in the job env that tells the writer where to write, once in the
uploader that names what to collect -- and each pair was free to drift silently
into a failure that reads "the floor produced no roster" when the truth is that
two literals disagree. They are now six `data` rows read by both sides.

And the toolchain-environment probe, unchanged and still absent from main:
`spawn rustfmt: No such file or directory` reports the syscall and nothing about
the environment that produced it, so every occurrence is exactly as unreadable as
the last. It records and never refuses -- each probe falls back to a printed
marker rather than a nonzero exit, so the step cannot fail and needs no
`continue-on-error`, which would be the escape-hatch shape.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@gunbai-bot
gunbai-bot Bot force-pushed the session/fierce-lynx-647-floor-roster-artifact branch from 18bb939 to 2a86cd8 Compare August 22, 2026 16:42
@gunbai-bot gunbai-bot Bot changed the title witnesses.yml: upload the floor's admission roster, which every run already writes and throws away witnesses.yml: the bundled receipt upload's if-no-files-found evaluates once, so one step per file instead Aug 22, 2026
…47-floor-roster-artifact

# Conflicts:
#	.github/workflows/witnesses.yml
#	dag/gunbc/witness_floor_workflow.dag
gunbai-bot Bot pushed a commit that referenced this pull request Aug 22, 2026
…on is unilateral

The consequence I published from §4m was "a deletion PR that merges main cleanly
is not verified by that merge, so deleters owe a resolve pass". True, and it told
a sibling lane it was clear when it was not. bright-ferret-335 checked its subtree
against that rule, found no deletion lanes (#8877, #8909, #8882 are purely
additive) and concluded it had nothing to check. #8909 adds three files that all
import v2.workflow.floor2_prepared_subject -- the module §4m restores.

THE HAZARD IS SYMMETRIC AND THE ADDER IS THE WORSE SIDE. A deleter that merges
main discovers an added consumer in its own resolve. An adder is already green
when the deletion lands later and breaks it, and an additive PR feels safe by
construction, so its author has no reason to ask whether anything it imports is
scheduled for deletion in someone else's open branch. The party in danger is the
party without a prompt.

The proposed repair was an intersection requiring one side to publish a list,
premised on neither side being able to run it alone. THE DELETER-CANNOT-SEE HALF
IS FALSE, and I falsified it by running it: open PR heads are on the remote, so
the intersection went against all three sibling PRs from this side with no list.
Zero real exposure -- no qualified import of any of the 55, and of the 664 symbols
declared solely by the 55 with no surviving declarer, one token hit: render_footer
in #8909, which declares its own and never depended on the deleted tools.readme.
Deleting it REMOVES a two-declarer whole-pool ambiguity rather than creating a
break.

So the obligation is unilateral, which is the point of restating it: a rule
requiring publication is a rule requiring coordination, and coordination fails
silently when a lane is busy, blocked or archived. A rule one party can execute
alone has no such failure mode. The deleter owes the intersection against every
open PR head at merge time; the adder owes nothing but pushing. An adder who
cannot be expected to look cannot be expected to publish either.

Residue, strictly smaller than the coordinated version: this reaches PUSHED work
only. Never-pushed worktree state is unreachable by any mechanism, and that alone
is what publication would buy.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
gunbc-ci-auto-heal added 2 commits August 22, 2026 21:07
…47-floor-roster-artifact

# Conflicts:
#	.github/workflows/witnesses.yml
#	dag/gunbc/witness_floor_workflow.dag
briansrls pushed a commit that referenced this pull request Aug 23, 2026
…-first, batched, refusals reported as the census (#8862)

* B1 — the RESIDUE-EMPTY control batch: 6 modules with no content to strand

The census's control batch, run first for the reason it was chosen as one: a module that
declares no symbol cannot be bare-referenced, so RESIDUE-EMPTY scored 0-of-8 consumed on
the defect-6 re-score and is the batch that proves the deletion mechanics without risking
working code. Every file here is a module line plus at most an import or a single unread
String; none declares a symbol, none is named by any .dag, .rs, .yml or .toml in the tree.

v2.bin.main's main_rs_trampoline_authority is the one row carrying content, and it is dead
data in DESIGN 4c's sense: the only other occurrence of that text in the tree is a parse
specimen in gap4_parse_tokens_remain_test.dag, which embeds the literal as a fixture rather
than referencing this module.

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

* B2 — 66 modules unreachable from every root and named bare by nothing

The remainder of the residue that carries no obligation to anything outside itself: not
reachable from any discovery path, entry row or v1 seed mirror; no uniquely-owned symbol
named bare by any .dag file in the corpus; and no mention in any .dag, .rs, .yml, .yaml,
.toml or .sh source. Their only occurrences tree-wide are in .md prose, repaired in the
next commit.

None of the 66 declares a test fn, so no assertion stops executing with them, and none has
a src/v1/stage0/src mirror, so the seed population is unchanged.

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

* Restore gunbc.scm.commit_closure_store: the census deferred it by name, and it is not residue

The census's finding on this row is that it is a replacement-migration leftover, not
residue: gunbc.scm.repository_envelope took its grain one day later and left the Filesystem
save/load half unattached. Which way that resolves -- the envelope grows save/load, or this
module is the persistence layer the envelope should consume -- turns on the #8820 author's
intent, and the census explicitly declined to disposition it for that reason. Sweeping it
here on an unreachability score would answer that question by deletion.

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

* Repair the two doc citations the deletion would have stranded

Both rows describe a deleted module as a live subject: model-grounding-lens-extract names
gunbc.tools.grounding_confirm as the CONFIRM judge, and review-surface-subject-map weighs
gunbc.pr_digests.MergeReadinessVerdict as a second required aggregate. The analysis in each
stands; only its premise about what exists in the tree does not, so the row is marked
rather than rewritten.

Three further mentions are deliberately left untouched. floor-cut-replacement-plan names
dag/gunbc/floor_resolve_realization.dag on a 'delete as reachability confirms' list, so this
deletion DISCHARGES that row rather than stranding it. Two receipts embed the literal
git-status line 'A  src/v2/workflow/host_discovered_owned_data_manifest.dag' inside a
transcript; editing a transcript to match a later tree would falsify the receipt.
algebra-grounding-unification-design names std.list as a road not taken, not as an
authority it cites -- and the stub's deletion corroborates it.

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

* Record the disposition: 71 deleted, 68 held with a typed reason each

The census answers what is unconsumed; this records what was done to it. Every held row
carries the reason it survived, because a refusal is the deliverable here as much as a
deletion is.

Three results worth the document rather than the commit message. The instrument was
re-derived rather than inherited, and it reproduces the census within four days of drift
(131 residue -> 139), which is what makes the deletion auditable against the tree it ran on
rather than against a row list. Re-scoring AFTER the cut is the discriminating evidence:
reachable and CONSUMED-DECISIVE are both unchanged, so nothing with a live caller moved,
while population fell by exactly the file count. And seven modules moved from
DEAD-CONSUMER-ONLY to STILL-UNCONSUMED as their island neighbours went -- the census
predicted the mechanic and this measures it, so the residue list is a fixed point reached
by iteration, not a set reached in one pass.

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

* Point the census at its execution record

One line, at the top, because the two documents answer different questions and a reader who
finds the census must not conclude the population is untouched.

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

* Keep the instrument beside the census, drop the working blobs

The script is what makes the numbers checkable; the JSON it emitted is derived data with no
consumer, so it is ignored rather than committed.

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

* Specify the instrument in prose: the repository excludes *.py, so the method ships instead

Committing the script was the first attempt and .gitignore refused it tree-wide. Rather than
route around that, the method is written out to the grain a reader can re-implement, and the
section-2 counters are noted as checkable without it at all.

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

* The deletion's one real strand: two pinned exclude lists named a file that no longer exists

Found by widening the mention scan past the source extensions to .txt/.json, which is where
it was hiding: docs/probes/census_extra_excludes.txt and its seeds file both listed
dag/examples/gunbhub_serve_program/gunbhub_serve_program.dag, and census_exclude_derive.rs
loads both -- the seeds drive the derived closure and the oracle is its drift witness, so a
row naming a deleted path skews the symmetric diff between them in the direction that reads
as drift rather than as staleness.

The row is removed from both rather than the file being restored: the exclusion existed to
keep a module out of a resolve walk, and a module that is gone needs no exclusion. The two
count literals that pin those files (83 -> 82, 27 -> 26) move with them; they are not
oracles in DESIGN 5's sense either before or after, and this change does not make them one.

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

* Record the .txt strand in the disposition, and what it says about the precondition

A source-extension mention scan is not a complete consumption surface. The finding is small
-- one row in two pinned lists -- but the lesson is the reusable part, so it goes in the
document rather than only in the commit that fixed it.

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

* Record what the manifest deletion actually removed, and why its exclusion rows stay

Review cleared two surviving references to a deleted filename as harmless string patterns,
which is right. Checking why turned up two things worth the row.

The deleted module was a committed instance of an artifact the generator that produces it
stamps DO NOT COMMIT -- a second module path holding an all-zero copy -- so the deletion has
a better justification than unreachability. And the exclusion rows are LIVE, not dead:
path_excluded is a substring match over the full path, so the pattern still matches the
ephemeral file the generator writes. Tidying them away, which 'a pattern matching nothing'
invites, would re-admit a generated transport into discovery.

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

* Restore gunbc.pr_digests and the deficit filing: one instrument defect, one KEEP disposition

TWO RESTORES, DIFFERENT CAUSES, AND ONLY THE FIRST IS A DEFECT IN THE MEASUREMENT.

gunbc.pr_digests is instrument defect 7, and the floor found it: "unresolved type
MergeReadinessVerdict" plus eight "undefined variable Ready" in gunbc.code_change_workflow
and its witness. The re-score's declared-symbol extraction read fn/data/type/const
declarations and NOT COPRODUCT VARIANT CONSTRUCTORS, so the variants of
type MergeReadinessVerdict = Ready | NotReady {...} contributed no owned symbols.
code_change_workflow names Ready bare with no import -- exactly the whole-pool resolution
the re-score exists to see -- and the instrument was blind to the constructor half of it.
Counting variants moves CONSUMED-DECISIVE from 89 to 94 at this branch's base and
reclassifies pr_digests as consumed. Same class as the census's own defect 6, one level
down: a surface decoded for declarations and not for their variants.

gunbc.generic_binder_field_projection_deficit is NOT a measurement error -- it scores
STILL-UNCONSUMED correctly. It is dispositioned KEEP-WITH-REASON in #8851 as a DESIGN 4b
deficit filing: declared rung, ceiling, next-rung trigger. An unconsumed deficit row is
4b(2) working as specified, since a class below its ceiling is REQUIRED to name its trigger
and nothing consumes that filing by design. Deleting it would have removed a safety-ledger
row and lowered a rung with none of the declaration 4b(3) demands. Unreachability is simply
the wrong predicate for this class of row, and the census said so before I ran.

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

* Record instrument defect 7 and the deficit-filing restore in the disposition

The floor's refusal is the section that matters: the two counters I offered as
discriminating evidence were computed by the instrument that had the defect, so they could
not see the case they were blind to. A control derived from the measurement it controls does
not discriminate that measurement's blind spot. The floor did.

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

* File defect 7 in the census, and the count-literal finding beside the v1 edit

Defect 7 goes in the census document rather than only here, because it is a defect in that
document's METHOD -- an extractor that reads fn/data/type/const and not variant constructors
cannot answer the bare-symbol question defect 6 poses. Its scope is stated precisely: measured
in this lane's re-implementation, unmeasured in the census's own, so the 91 and 96 are to be
checked rather than assumed either way.

The lesson is carried verbatim, because it generalizes past this row: a control derived from
the measurement it controls does not discriminate that measurement's blind spot. Both counters
this lane offered as safety evidence came from the instrument carrying the defect, and they
read as reassurance precisely because they were consistent.

The count literals moved for the pin are filed as a change detector with a receipt -- one row
moved for an unrelated reason and both literals had to be hand-followed -- and deliberately
not fixed, since the pin has its own lane and its own declared dissolution.

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

* Restore 13 more: the corrected extractor moves them to AMBIGUOUS, and the src/v1 edit dissolves with them

silent-deer-368 sent four discriminating cases for the declared-symbol extractor. Mine passed
three and FAILED the fourth: a same-line coproduct, type X = A | B, where the variants sit on
the type line itself and a start-of-line variant scan never sees them. That is a fourth
under-extraction bug in the same family as defect 7, and under-extraction creates FALSE
UNIQUENESS, which is the delete-a-live-module direction rather than the keep-a-dead-one
direction.

Adopted their region-based extractor, which fixes all four at the root instead of patching a
regex per bug: a type's region runs to the next TOP-LEVEL declaration (so a multi-line variant
record cannot truncate it), a leading generic <...> is stripped before testing for =, and
operation/service/resource are counted as declarations in the flat service namespace. All four
discriminators now pass, and the (c) tell passes too: extdeps.transports.sql is AMBIGUOUS
rather than consumed-by-filesystem_io, which linked only through operation Delete.

THE RE-DERIVATION IS NOT A NULL RESULT. 13 of the 69 leave the residue, and every one lands in
AMBIGUOUS-SHARED-ONLY -- no longer provably unconsumed, and NOT proven consumed. That bucket
needs a per-row read, so none of them may be deleted on this evidence: nine formatters, the
language_model rust root, generic_instantiation, roadmap_dispatch, and gunbhub_serve_program.
Restored. 56 remain, still a strict subset of the corrected 114-row residue, still zero island
violations.

The src/v1 edit dissolves rather than being justified. It existed only because a deleted path
left a dangling row in census_extra_excludes; gunbhub_serve_program is back, so the row is
correct again and the two count literals go back to 83 and 27. This PR no longer touches the
frozen seed at all. The change-detector finding about those literals stands on its own and
stays filed -- it was always a property of the pin, not of this deletion.

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

* Close the island: rust_leaf_model_claim's consumer came back with the 13

Restoring v2.test.language_model.rust re-created an in-population consumer for
v2.std.rust_leaf_model_claim, so deleting the latter would split the island the restore had
just re-formed. This is the island constraint behaving exactly as the census describes it --
eligibility is a property of the SET, so changing the set changes who is eligible, and a
restore can create a violation as readily as a deletion can.

Computed as a fixed point rather than a single pass: repeatedly drop from the deletion set any
module with a surviving in-population consumer, until nothing drops. One iteration, one module.
55 deletions, zero island violations, still a strict subset of the corrected residue.

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

* Delete commit_closure_store on its author's answer, with the cause they gave

gentle-eagle-360 (#8820's author) answered the arm the census deferred, and corrected the
premise of BOTH arms it offered. The correction is the part worth the row: the envelope did
NOT supersede this module -- it holds zero Filesystem operations and not one func, it is a
pure codec -- and it should NOT consume it either, because a commit-closure carrier demands a
root and a repository's init has none, so wiring them would force init to invent a phantom
commit. A census that records a false cause for a true deletion still carries a false row.

Cause: STAGED ORPHAN AT THE WRONG GRAIN. Deleted rather than held for the repository-grain
layer because a surviving X is an attractor -- every nearby persistence question keeps getting
answered in vocabulary already scheduled to die. That is an author volunteering their own
module on the doctrine.

They also volunteered a defect that strengthens it: the header claims persistence is verified
by direct execution in Wet mode, and the probe it names is enrolled nowhere and executes
nothing. Unconsumed AND overclaiming its own evidence is a stronger warrant than unreachable.

Island check re-run with it in the set, since eligibility is a property of the set: 56
deletions, fixed point, zero violations, still a strict subset of the corrected residue.

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

* Bring the record's headline numbers and deleted table in line with the final 56

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

* Rewrite the record's stale sections against the final set

Four sections described a tree this branch no longer produces. Rewritten rather than
annotated, because two accounts of one fact is the defect this document polices elsewhere:
section 2's counters are re-measured on the corrected instrument and DEMOTED to necessary-
but-not-sufficient with the reason; 4b records the .txt strand as a surface finding whose
repair dissolved when its module came back; 4d grows from one defect to four with the
mechanism they share and the 13 rows re-deriving cost; 4g makes the island fixed point its own
section, since it fired in both directions and is the reason this is one PR.

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

* Regenerate the held table against the final set, and name the ambiguous 97 beside it

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

* Section 7: state the limits against the final set, including the ones this lane created

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

* Record the discriminator roster, the count/set rule, and the two-population skew

The reusable result from the cross-lane exchange is that a discriminator travels and a
description does not: three relayed bug descriptions found one bug in this extractor, and the
four test cases that came with them found a second nobody had named. (e) is contributed back
with a specimen whose declared set is EMPTY under an unfixed extractor rather than merely
incomplete, which puts it directly on the delete-a-live-module precondition.

silent-deer-368's count/set rule is recorded verbatim because it resolves what looked like a
discrepancy between two implementations into a property worth stating: a residue COUNT from
the precise implementation, a deletion SET from the conservative one.

And the skew is stated as a finding rather than bookkeeping, because the natural summary of it
is wrong in the dangerous direction.

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

* Record (f) and its second half: a generic parameter is a binder on both sides of the instrument

The sixth case from silent-deer-368 still failed here after implementing (f) exactly as
described, because the construct produces TWO independent false references: the parameter list
in the header, and uses of the bound name in the declaration's body on the next line. Only
region-scoped shadowing moved v2.std.projection to AMBIGUOUS.

Scoping is the safety-critical part and the obvious implementation is wrong in the dangerous
direction: dropping a bound name file-wide REMOVES references, which moves live modules into
the residue. Shadow is scoped to the binding declaration's region.

Also withdrew the service short-name credit -- my pattern never matched dotted forms but did
match undotted ones, which is exactly the class that produced a false consumption claim on the
other lane. Both defects inflate consumption, so neither could have caused a wrong deletion;
the deletion set is unchanged at 56 with all six discriminators passing.

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

* Correct this document's own reason for dropping the service credit

The reason given was a false-consumption claim measured on the other lane's instrument, and it
was never true on this one -- their specimen is a DOTTED service name that this lane's pattern
never matched. Restored and re-run with (f) fixed, the credit moves nothing here, so the honest
reason is 'no measured effect on this population'.

And the evidence points the other way: artifact_store_fs bare-resolves Filesystem.Write with no
import, and Filesystem is declared undotted by two modules. A consumer DOES name a service short
name bare, so a wholesale withdrawal stops filesystem_io owning a symbol it visibly declares --
the own-nothing direction (e) exists to catch. Recorded as the seventh discriminator.

Adopting a peer's conclusion is not the same as reproducing their measurement, and writing
their reason into this document as though it were mine was the same error this lane already
filed once: a claim carried rather than checked.

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

* Restore the service credit on correctness of ownership, the basis that actually settles it

Third and final revision of this reason, and the churn is the point: 'it causes false
consumption' was another lane's measurement I had not reproduced; 'no measured effect' is true
but settles nothing, since nil effect argues for either choice. Correctness of ownership does
settle it -- filesystem_io declares service Filesystem, so an instrument denying it is wrong
whether or not a row moves.

And withholding the credit was worse than a missing fact: Filesystem is declared twice, service
in filesystem_io and resource in std.resources, so dropping the service side handed
std.resources FALSE SOLE OWNERSHIP -- manufactured uniqueness, the mechanism behind every wrong
attribution in this chain, created by the fix meant to avoid it.

Dotted forms now credited by last segment too. Zero exposure in this corpus, same basis.
Deletion set unchanged at 56, subset, zero islands, counts identical.

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

* Roster OwnedDataDiscoveryReceipt: the floor's second refusal, and it is correct

Deleting the committed copy of the DO-NOT-COMMIT manifest (4c) removed the only non-test
reference to this carrier, so it became inert and unrostered and the inert-carrier lens
refused. Verified directly on the symbol's references before and after, not through the
re-implementation I first reached for -- that re-implementation shared ZERO names with the live
roster under two different readings, so its stable delta was suggestive and not evidence.

Rostered rather than restoring the module. The carrier's real consumer is generated at runtime
and stamped DO NOT COMMIT, so in the committed tree it is inert BY CONSTRUCTION and no corpus
scan can see otherwise; re-committing a file its own generator forbids committing, in order to
green a hygiene lens, would be the tail wagging the dog.

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

* Record the OccurrenceId-keyed reproduction hazard as a class finding

Not a claim about this change, which fixed nothing, and not about the original defect, which
was root-caused at the declaration with a caller census and is green on main by that repair.
The claim is about the CLASS: a defect keyed on a specific OccurrenceId value stops reproducing
under any change that perturbs allocation, so a bisect across trees of different module counts
measures luck rather than the defect.

This branch is the specimen precisely because it contains no fix -- 16 identities red at the
base, the same 16 green on a tree differing only by 56 deletions, planned identical at 10425 so
roster change is ruled out and corpus size is the isolated variable.

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

* Record what the static instrument was worth, and the rule for editing the set

Two findings the lane owes its next reader. First, measured rather than asserted: across seven
instrument defects and four extractor corrections, the static census never identified a live
module -- it identified unknowns, correctly, and the one true positive came from one execution.
DESIGN 3 as a measurement against a static alternative that had four rounds of sharpening.

Second, the zero-near-miss reading carried verbatim, because it is the antidote to a later
reader relaxing at a zero: AMBIGUOUS means unresolved, a live module would present exactly as
those 13 do, so the honest statement is thirteen rows of unknown status I would have deleted.
The same standard is turned on the 56 being deleted -- LowerBoundOnly, best-evidenced, not
proof -- which is the argument FOR landing and reading refusals.

And the editing rule: 69-13+1 reconciles by the wrong route, since 13 left evidentially and one
left structurally. Anyone editing this set re-runs the fixed point rather than adjusting the
count; the failure mode is silent because the arithmetic keeps agreeing.

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

* Downgrade the hazard-(b) control: a matching count does not rule out roster change

An earlier revision claimed 'planned is identical at 10425, which rules out roster change'.
It does not -- identical counts are consistent with an unchanged roster and do not establish
one. And the caveat is not theoretical: on the next run offered moved 11798 -> 11812 and routed
10425 -> 10439 across two commits changing only a markdown file and one inert_row data row,
declines unchanged. Fourteen witnesses entered discovery with no corpus change, cause not yet
attributed, so the discovered roster is not a pure function of the committed tree.

The comparison survives as corroboration -- the two runs agree on every reported discovery
figure and differ by exactly the 56 deletions -- but the isolated-variable claim is downgraded.
This is the failure the document records elsewhere, committed in its own write-up: a count
treated as a check.

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

* Remove .census/s.json: session scratch with no consumer, and the .gitignore hand-edit it caused

.census/s.json is the disposition instrument's raw output, swept into
19d6301 by a broad `git add` rather than authored as a repository fact.
Nothing in the tree cites it -- not the census, not the disposition record,
not a lens. That is DESIGN.md's experimental-residue tell exactly: a new
artifact with no final consumer, in a PR whose whole subject is removing
artifacts with no consumers.

The accident had a second half. To stop the directory reappearing I had
hand-added `.census/` to `.gitignore` -- which is a GENERATED artifact,
emitted from gunbc.gitignore_authority. That is manual application committed
as source (DESIGN.md §6): the line survives until the next regen and then
vanishes without anyone editing it, so the rule it states is not a fact the
authority holds. The merge with origin/main surfaced it as a driver refusal
on that path, which is the driver working as specified.

Both halves are dropped rather than legislated. The scratch directory is
session-local and belongs in the session scratchpad, not the repo, so the
authority has nothing to say about it and no row is added to it. .gitignore
is taken at origin/main's regenerated bytes.

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

* Restore v2.workflow.floor2_prepared_subject: the merge was a third census, and it refused

#8896 landed src/v2/workflow/floor_terminal_ledger.dag on main while this branch
was open. It imports WitnessIdentity and witness_identity_qualified_name from
v2.workflow.floor2_prepared_subject by name, and its test does the same. This
branch deletes that module.

THE TWO BRANCHES TOUCH DISJOINT FILES, SO GIT MERGED THEM CLEAN. No textual
conflict, no marker, no driver refusal -- and the merged tree does not compile.
A three-way textual merge answers "did the same bytes change twice"; the question
that decides this case is "does the merged tree still resolve", which is not a
question about bytes. A merge preserves bytes, not measurements: every count in
the disposition record is a statement about a tree, and the merge produced a tree
none of them were measured against.

The measurement was not wrong. The module was STILL-UNCONSUMED at base
90986d1 on every decoded surface and still is at that commit; it acquired a
consumer afterwards. This is the census's second blind spot and it is not
fixable by improving the instrument: a static census reads a tree, and the part
that had not been written yet is as invisible as the part it decoded wrongly.

Found by re-scoring the deleted set against the MERGED tree rather than against
main -- a deliberately over-generous token match that flagged nine, of which
eight were prose, annotation, or substring collisions (`Binding` inside
CapabilityBinding prose, `YamlBool` whose live declarer is a coproduct VARIANT --
defect 7's own shape, in the triage regex this time -- `grade` and `readme` in
citation text) and one was real. §4l's rule was followed rather than the count
adjusted: the restore's imports were checked for in-population consumers, the
fixed point closed at one restore, no cascade followed.

56 deleted becomes 55. The near-miss ledger reads 2, both found by execution and
neither by the census: gunbc.pr_digests and this one. §4m records it.

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

* Correct §4m's rule: the adder is the exposed side, and the intersection is unilateral

The consequence I published from §4m was "a deletion PR that merges main cleanly
is not verified by that merge, so deleters owe a resolve pass". True, and it told
a sibling lane it was clear when it was not. bright-ferret-335 checked its subtree
against that rule, found no deletion lanes (#8877, #8909, #8882 are purely
additive) and concluded it had nothing to check. #8909 adds three files that all
import v2.workflow.floor2_prepared_subject -- the module §4m restores.

THE HAZARD IS SYMMETRIC AND THE ADDER IS THE WORSE SIDE. A deleter that merges
main discovers an added consumer in its own resolve. An adder is already green
when the deletion lands later and breaks it, and an additive PR feels safe by
construction, so its author has no reason to ask whether anything it imports is
scheduled for deletion in someone else's open branch. The party in danger is the
party without a prompt.

The proposed repair was an intersection requiring one side to publish a list,
premised on neither side being able to run it alone. THE DELETER-CANNOT-SEE HALF
IS FALSE, and I falsified it by running it: open PR heads are on the remote, so
the intersection went against all three sibling PRs from this side with no list.
Zero real exposure -- no qualified import of any of the 55, and of the 664 symbols
declared solely by the 55 with no surviving declarer, one token hit: render_footer
in #8909, which declares its own and never depended on the deleted tools.readme.
Deleting it REMOVES a two-declarer whole-pool ambiguity rather than creating a
break.

So the obligation is unilateral, which is the point of restating it: a rule
requiring publication is a rule requiring coordination, and coordination fails
silently when a lane is busy, blocked or archived. A rule one party can execute
alone has no such failure mode. The deleter owes the intersection against every
open PR head at merge time; the adder owes nothing but pushing. An adder who
cannot be expected to look cannot be expected to publish either.

Residue, strictly smaller than the coordinated version: this reaches PUSHED work
only. Never-pushed worktree state is unreachable by any mechanism, and that alone
is what publication would buy.

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

* Record the sign-reversed token hit as its own class: a deletion can improve resolution safety

From bright-ferret-335, reading the intersection result rather than the rule.

The render_footer hit read as a hazard -- a name declared by a module being
deleted and used in an open PR -- and inverted on inspection: #8909's consumer
declares its own, and while tools.readme stands the name has TWO whole-pool
declarers. It is AMBIGUOUS, and the deletion REMOVES the ambiguity. So the same
intersection that finds breaks also finds improvements, and a reviewer who reads
any token hit as danger will misclassify this direction.

It matters beyond one row because it is the ambient-pool defect in miniature: a
name resolving by whole-pool uniqueness is one deletion away from binding
differently, so its meaning is a function of the corpus denominator rather than
of anything written at the declaration or the use. This deletion moves that name
from two declarers to one. NOTHING STRUCTURAL GUARANTEES THE NEXT ONE MOVES THAT
WAY -- the mechanism that disambiguates here can elsewhere silently rebind a use
from one surviving declarer to another. Not created by this change, not closed by
it, and visible from here only because an intersection over deletions is one of
the few operations that looks straight at it.

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>
@gunbai-bot

gunbai-bot Bot commented Aug 23, 2026

Copy link
Copy Markdown
Contributor Author

This red is main's, not this diff's — there is no fix to push here.

The failing phase is the floor, and its entire content is main's current baseline:

9 × required-floor: FAIL
6 × required-floor: STALE-QUARANTINE
required-ci: phases_run=3 failed=1     ← regen PASSED

Identity-level proof, not matching counts. PR #8877 and PR #8882 are unrelated diffs — one is GitHub Actions upload steps plus a diagnostic probe, the other is sudoers grant parsing — and their nine FAIL identities are a byte-identical set:

fleet_intent_memory.srv2_population_matches_bmc_memory_summary
host_allocation_conservation.*                                  (6)
installed_bom_reconcile_witness_test.*                          (2)

Two diffs that share nothing cannot produce the same nine failures unless the failures belong to neither.

Attribution. All nine identities live in the three witness files that 96cb3628566 (#8947, "Model the 64 GiB DIMM upgrade") modified — nine for nine on a 10,569-row fold, with no failing identity outside them. Reported there: #8947 (comment)

The six STALE-QUARANTINE rows are separately fixed by #8980 (open, approved).

What is genuinely this PR's: nothing in this run. Regen passes, and no failure outside the shared baseline appears — which for #8882 includes its own ordering witness against #8860, which passes.

I am deliberately not pushing, rebasing, or re-running: a re-run replays the same merge ref, and a push spends a ~45-minute slot to reproduce main's baseline. This clears when #8947's lane repairs the nine and #8980 lands.

— sent from fierce-lynx-647

@gunbai-bot

gunbai-bot Bot commented Aug 23, 2026

Copy link
Copy Markdown
Contributor Author

Carrier change: fierce-lynx-647 has been closed out; I (bright-ferret-335) am carrying this PR and #8882.

The failing witnesses check is INHERITED, verified rather than assumed. Run 32612472013 reports nine FAILs: the six host_allocation_conservation rows, fleet_intent_memory.srv2_population_matches_bmc_memory_summary, and two installed_bom_reconcile_witness_test rows. All are hardware-modelling witnesses; this PR changes the witnesses.yml receipt upload to emit one step per file. I checked the failing subjects rather than grepping this PR's module name — the two answer different questions, and conflating them is how an unrelated red gets reported as inherited when one row actually is yours.

All nine are fixed on main by #8975, #8980, #8987, #8992 and #8998; the floor went failed=8 → failed=0.

This needs a PUSH, not a re-run. gh run rerun replays the merge ref the run was created with and never recomputes refs/pull/N/merge, so re-running today reproduces the same nine failures regardless of main's state. Only a push creates a fresh merge ref.

Waiting on main, which is one witness from green — a single test.claim.live_deploy.emit row sits INTERRUPTED-BEFORE-VERDICT against the 5000ms CPU cap. #9012 is the repair.

@briansrls
briansrls merged commit 3c82b22 into main Aug 23, 2026
2 checks passed
@briansrls
briansrls deleted the session/fierce-lynx-647-floor-roster-artifact branch August 23, 2026 22:32
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