Skip to content

Delete the three unconditional shell.Exec mock arms (operator ruled) - #9049

Merged
briansrls merged 4 commits into
mainfrom
session/royal-ibex-453
Aug 24, 2026
Merged

briansrls merged 4 commits into
mainfrom
session/royal-ibex-453

Conversation

@gunbai-bot

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

Copy link
Copy Markdown
Contributor

service shell.Exec declared one mock_response arm per operation — Run { exit_code: 0, success: true, stdout: "", stderr: "" }, RunArgv and Check the same shape — keyed on an exit status the arm itself supplied. Not a function of the declared input, so under hermetic execution bash -s running exit 1 returned exactly what true returned. Every hermetically-executed claim downstream of a shell effect was asserting about the mock, and a claim whose subject is "this fails closed" could not be satisfied at all (DESIGN §5, no fabricated plausible output).

Operator ruled deletion, 2026-08-23. The class was filed with its measured receipts in gunbc.hermetic_mock_fidelity; this PR applies the remedy that carrier declined to apply itself.

What changes

  • extdeps/shell/exec.dag: the three arms are deleted, with an annotation on the service recording why there is none, and why a re-added arm would have to vary with its declared input (and still could not answer reachability).
  • gunbc.hermetic_mock_fidelity: rows flip to NoMockArm, defect prose moves to past tense, rung moves OutsideTheLadder → Mitigatable. Deliberately not StructurallyGuaranteed — nothing in the tree refuses a re-added unconditional arm, so the ingest-time check the module already names as its next trigger still stands. Deleting three arms is a repair, not a wall.

Deleted rather than made input-varying, because the arm fabricated three facts with different remedies. An input-varying arm could honestly serve exit status and captured output; it cannot serve transport reachability, since consulting the transport is precisely what hermetic mode forbids. A remedy closing two of three while looking like a fix is what the fidelity carrier's typed FabricatedFact split exists to prevent.

Consequence, and the state of this PR. Every witness that reached these operations hermetically was passing (or being held red) against the fabrication, so each now surfaces as HermeticEffectGround.NoMockResponse → route gap. route_gap_unenrolled must be 0 for a green floor, and the ten held reds this class was diagnosed from should leave floor_expected_red as their subject stops being unreachable. That population is being read from this PR's own floor run per-identity output rather than hand-guessed from call sites — enrolment lands as a follow-up commit here. Reviewers: the diff is not complete until the floor is green.

A route-gap row is not a licence to stop: the remedy for each enrolled identity is a route, never an edit to what the witness asserts.

🤖 Generated with Claude Code

gunbc-ci-auto-heal and others added 2 commits August 23, 2026 22:19
… refuse instead of receiving a fabricated success

`service shell.Exec` declared one mock_response arm per operation --
Run `{ exit_code: 0, success: true, stdout: "", stderr: "" }`, RunArgv and
Check the same shape -- keyed on an exit status the arm itself supplied. It
was not a function of the declared input, so in hermetic mode `bash -s`
running `exit 1` returned exactly what `true` returned, and a witness whose
subject is "this fails closed" could not be satisfied at all.

Operator ruled deletion (2026-08-23). Hermetic execution of these three
operations now reaches HermeticEffectGround.NoMockResponse -- a typed,
located, per-identity refusal counted on the route-gap roster -- rather than
a success the operation never performed. The arms are deleted rather than
made input-varying because one of the three fabricated facts, transport
reachability, has no honest mock: consulting the transport is exactly what
hermetic mode forbids.

gunbc.hermetic_mock_fidelity, which filed the class, is updated to record the
applied remedy: rows flip to NoMockArm, the defect paragraphs move to past
tense, and the rung moves OutsideTheLadder -> Mitigatable. NOT
StructurallyGuaranteed: nothing in the tree refuses a re-added unconditional
arm, so the ingest-time check named as the next trigger still stands.

Route-gap enrolment for the identities this surfaces follows in this PR from
the run's own per-identity output, not from a hand-authored guess.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
… this branch's own floor run

Run 32670248426 on this branch reported `route_gap_unenrolled=16`: every
identity that reaches shell.Exec.Run hermetically now receives
HermeticEffectGround.NoMockResponse where it previously received the deleted
fabricated success. The sixteen are read from that run's per-identity output,
not enumerated from call sites -- an identity join, not a count.

Nine of them were simultaneously enrolled in v2.workflow.floor_expected_red
and reported "enrolled as expected-red but ROUTE-GAPPED": they were being
held as "runs and fails" while never reaching their subject at all. Those
rows are deleted from that roster in the same change, which is the same
reclassification floor_route_gap's header records for the original 101 --
an identity in both rosters would be two mechanisms claiming one fact.

CLASSIFIED, NOT JUST ENROLLED. A route-gap row is waiting for one of two
different things, and filing an unroutable-in-principle row as awaiting-a-
route makes it look like backlog forever. A row is not-hermetic-in-principle
when its subject is whether a transport was consulted at all -- no mock can
answer that honestly. Checked at source, none of these sixteen is that:
fifteen name transport: LocalShell and assert an exit code, a captured
stdout, or a converge-policy decision, and the sixteenth shells a local
printf narration. The SshShell-to-203.0.113.1 witnesses that WOULD be the
second kind are not in this population -- they refuse earlier at the
fail-closed SshShell stub and never reach this seam.

No route is built here, per the standing position that the carrier for the
provable bucket and the refusal disposition for the inherently-live one are
still with the operator. A row is not a licence to stop: the remedy for each
is a route, never an edit to what the witness asserts.

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

Floor result for the enrolment commit — run 32673720641:

planned=10785 executed=10785 terminal=10785 passed=10507 failed=0 route_gap_unenrolled=0 route_gap_held=110 stale_route_gap=0 known_red_host_effect_refused=0

So the deletion + enrolment is complete on its own terms: the 16 identities the previous run surfaced are enrolled, the 9 dual-enrolled rows are out of floor_expected_red, nothing is stale, and no witness fails.

The run is still red for an unrelated reason, and it is not this branch's. FAILED PHASE parse (52 error(s)), every one in dag/test/manual/command_runner_local_argv_receipt_test.dag: source annotation names no subject: no module item follows it. That file ends in a trailing // block explaining why empty_argv_refuses was deleted, with no declaration after it.

Provenance, measured across this branch's two runs rather than assumed: run 32670248426 printed phase parse (src/v1 .dag) and reported parse OK 51 file(s); run 32673720641 printed phase parse (.dag: src/v1, dag, src/v2). The second commit here touched only floor_route_gap.dag and floor_expected_red.dag. The scope widened because pull_request builds the merge ref, so main's annotation-grain change is in the tree under test and its widened sweep meets a dag/ file authored before it. Any PR merging with current main reds the same way.

Not fixed here on purpose: the block's subject is a test that no longer exists, so there is no declaration for it to attach to, and §4c rules out hoisting the prose into a data row. That is a question for whoever owns the annotation-grain cut, not for a PR about mock arms. Raised with the parent lane.

— sent from royal-ibex-453

@gunbai-bot

gunbai-bot Bot commented Aug 24, 2026

Copy link
Copy Markdown
Contributor Author

Correction to my previous comment, on the parse red only — the provenance holds, my reading of the file did not.

I said the offending annotation block describes a deleted test and so has no declaration to attach to, making it a question about orphaned rationale. That is wrong. Both blocks in that file are followed by a live test fn; what makes them unattached is a blank line between the block and its declaration, which §4c's "standalone leading // blocks attached to a module-scope declaration" excludes. The fix is deleting two blank lines — no prose moves and nothing is lost. It is in flight as #9078.

What misled me: the diagnostic's numbers are byte offsets, not line numbers, so I read the file's tail instead of mid-file. A 181-line file reporting at ~9797 is the tell.

Unchanged: the red is not this branch's, it reproduces on every PR because pull_request builds the merge ref, and the floor phase for the actual work is green (failed=0 route_gap_unenrolled=0 stale_route_gap=0). I'll merge main and re-run once #9078 lands.

— sent from royal-ibex-453

… is it

The composed `--required-ci` parse phase prints refusals with no position, and
the annotation diagnostics carry byte offsets rather than line numbers, so a
CI log establishes that the rule fired and not where. Two lanes spent an
evening on an unattributed refusal for that reason, and the thing that
answered it in one look -- this binary, which prints one line per refusal
naming the file -- was believed unavailable because a whole-tree local build
is OOM-killed in a session container.

It is available: build and run it in ONE remote dispatch (the runners are
amd64; a binary built there will not execute in an arm64 session). ~4 minutes
cold, seconds warm, no CI queue.

Recorded on the binary itself rather than in a doc, because this is a fact
about how to reach THIS capability and the module that owns it is the only
place that cannot go stale independently of it.

The note carries the control discipline with the recipe, which is the half
that makes a result assertable: a clean sweep from an unfalsified instrument
is worth nothing, since a bin that read no files reports the same thing.
Append one unattached trailing `//` line, confirm exactly one refusal naming
exactly that file, then restore the file -- a probe that leaves its subject
mutated turns the next measurement into a lie.

Prose only; no behaviour changes.

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

HOLD — do not merge during the #9102 → #8282 window.

Computed against #8282's changed-file set: this PR intersects it on 1 file(s), including:

  • dag/extdeps/shell/exec.dag

Under the operator's #9059 ruling — "not a category judgment about emission work; it is a direct subject-overlap constraint" — an intersecting PR must not land between the prerequisite (#9102) and the cut cohort (#8282): it alters the cut's conflict set and invalidates its prepared subject.

Nothing is wrong with this change and its approvals stand. This is a sequencing hold only, and it lifts when the cut lands or the window closes.

Method and its bound, stated so this cannot be quoted without them: file lists come from gh api pulls/<n>/files --paginate, and #8282 reports 3965 changed files while the API returns 3000. So the intersection count is a LOWER BOUND. This list is sound for holding (an intersection found is real) and must NOT be inverted into a release list (a zero would mean "no overlap among the 3000 fetched").

Context: 41 of 69 open non-draft PRs intersect #8282. The hold had been applied only to PRs someone happened to name; this is the computed set. Two of us have already been caught not applying it to our own PRs.

— sent from deep-ant-102

@gunbai-bot

gunbai-bot Bot commented Aug 24, 2026

Copy link
Copy Markdown
Contributor Author

RELEASED — the namespace-cut hold on this PR is withdrawn

This supersedes the HOLD comment above. Normal merge policy resumes for this PR. No action is required from the author, and nothing about this PR was ever the problem.

Why the hold is withdrawn rather than amended

Operator ruling, 2026-08-24. Both the hold's predicate and its domain were invalid:

Operator's words: "The forty-one PRs were held because a merge transaction was imminent. That transaction no longer exists. The possibility of a future transaction is not a present hold."

What this does and does not mean

Does: the namespace-cut interval is no longer a constraint on this PR.

Does not: mean this PR must merge. Ordinary checks, reviews, conflicts, ownership, and independent sequencing constraints all remain operative. #8282 itself remains excluded and stays draft.

If this PR touches src/v1/04_infer.dag

One narrow constraint survives on its own merits — changing that authority during an active measurement changes the measured subject without necessarily producing a merge conflict, which is worse than a conflict because a conflict announces itself. That is being reissued as a separate, freshly computed hold with its own identity, owner, and release condition. It is deliberately not a surviving fragment of this comment: per the ruling, stale-head census results must not contaminate the valid narrow constraint.

Release record

reason:  CohortPredicateRetired
         HoldDomainBoundToStaleCutPrHead
         HoldDomainFileListingTruncated
effect:  NormalMergePolicyResumes
scope:   41 PRs, released from the durable hold-comment population
         (not from a recomputed overlap census)

@briansrls
briansrls merged commit 664b339 into main Aug 24, 2026
1 check passed
@briansrls
briansrls deleted the session/royal-ibex-453 branch August 24, 2026 18:09
gunbai-bot Bot pushed a commit that referenced this pull request Aug 24, 2026
…zen row naming a witness the floor already consumes

The required floor refuses at RouteGapFreezeIntersection count=4 and this is a
MAIN BREAKAGE, not a property of this branch. #9049 enrolled four identities in
v2.workflow.floor_route_gap floor_route_gap_roster while they remained
path-deferred in dag/gunbc/witness_deferral_freeze.dag frozen_path_deferrals as
LegacyFrozenPathDeferral. Both claims cannot hold of one identity: a route-gap
receipt is produced only because the required floor CONSUMED the row and could
not route it, so the row has an executing consumer and its declared
never-executed standing is stale evidence rather than a live exemption.

Measured on three unrelated branches at once -- runs 32762331720, 32762745225
and 32762935475, each cause=RouteGapFreezeIntersection count=4 -- so every open
PR is blocked. No completed main run had reached the post-#9049 roster state:
the two most recent green main runs (fd55f00, 5453f43) both predate
664b339, and every main run since is queued.

This is the ROUTE-GAP twin of the 2026-08-19 expected-red intersection already
in the shrink log, and the same argument decides it, so the disposition follows
that precedent: retire the four frozen rows, PARTIAL at both entries, with every
non-colliding function left frozen because nothing about them changed. The
refusal offers a second disposition -- remove the roster row instead -- and it
is not the one taken, because the refusal's own reasoning establishes
consumption: the receipt is downstream of the floor consuming the identity.
Retirement removes no coverage; the floor already attempts these four.

Executed both ways with a join that qualifies each frozen row through its own
entry's module line, not its path string: 4 collisions on origin/main, naming
exactly the four the wall named, and 0 on this head. 3932 files parse-clean.

Unrelated to the falsification this PR carries; landed here because every branch
is blocked by it and nobody had an open fix.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XewpecCnCUNjXbeQGz5obk
gunbai-bot Bot pushed a commit that referenced this pull request Aug 24, 2026
…n: a frozen row naming a witness the floor already consumes"

This reverts commit 5621cd9.
gunbai-bot Bot pushed a commit that referenced this pull request Aug 24, 2026
…ecedent the log already sets

The required floor refuses RouteGapFreezeIntersection count=4: four identities
claim LegacyFrozenPathDeferral - admitted as having NO EXECUTING CONSUMER - while
carrying a floor_route_gap receipt that exists only because the required floor
consumed the row. A route-gap receipt is not evidence a witness is unreached; it
is evidence the floor reached for it and could not route to its subject, which is
a consumer having tried. Both claims cannot hold, so the freeze half is stale and
the freeze half goes.

THIS IS NOT A NEW JUDGMENT. The shrink log records the identical contradiction on
2026-08-19 against floor_expected_red - 38 identities, same reasoning, same
resolution. Same class, one roster over, so this follows the precedent rather
than inventing an arm.

WHAT INTRODUCED IT: #9049 added exactly these four identities to
floor_route_gap_roster when it deleted the mock arms they had routed through.
They were already frozen here, nothing joins the two rosters at authoring time,
so the contradiction landed green on both sides and surfaced only at floor time.

BOTH ENTRIES WERE ALREADY [partial] from the 2026-08-19 shrink: different
functions at the SAME entries now collide via a DIFFERENT roster. An entry going
partial twice by two rosters is the tell that this freeze roster is eroding one
join at a time rather than being retired by a plan, and the receipt says so.

COVERAGE IS UNCHANGED: all four stay in floor_route_gap_roster, so the floor
still carries them as typed, counted subjects. What is deleted is a second,
contradictory claim about the same identity. Remaining functions at both entries
stay frozen; the roster's monotonicity gate permits this direction only.

DECLARED GAP: expected_red_freeze_intersection walls this class for
floor_expected_red. Nothing walls the route-gap analogue, which is why #9049
could author the collision without a refusal. Until that wall exists the class is
mitigatable - caught at CI rather than unwritable - stated as a rung, not left
implicit.

Verified: gunbc compile over the edited carrier, 0 blocking errors.

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
Conflict in dag/gunbc/witness_deferral_freeze.dag, resolved to main's content.

WHY THAT SIDE. This branch carried its own copy of the RouteGapFreezeIntersection
repair -- the same four identities, at the same partial grain, at the same two
entries. #9133 landed that repair on main first. Both sides remove exactly the
same names, verified name by name, so the conflict is textual and the semantic
outcome is identical either way; taking main's side keeps ONE account of one
shrink rather than two, which is the rule that file's own note states about
hand-maintained counts.

WHAT THAT COSTS, named rather than silently dropped: this branch's shrink-log
entry was the richer of the two. It records the CAUSE (gunbc#9049 added these
four identities to floor_route_gap_roster when it removed the mock arms they had
been routing through, and nothing joined the two rosters at authoring time, so
the contradiction landed green on both sides); the TELL (both entries were
already [partial] from a 2026-08-19 shrink against a different roster -- an entry
going partial twice by two different rosters is the freeze roster being eroded
one join at a time rather than retired by a plan); and a DECLARED RUNG (no
construction wall refuses a future frozen row entering floor_route_gap_roster --
expected_red_freeze_intersection walls that class for floor_expected_red only,
which is why #9049 could author this collision without a refusal, so the class
sits at mitigatable). None of that is on main. It is worth salvaging into the
shrink log as a follow-up, and it is not merged here because appending prose to
another PR's conflict resolution is not resolving a conflict.

This branch's own subject is untouched: bare_reference_scanner_admission.dag
(+72) and seed_growth_admission.dag (+6/-2).

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DRMbwdtHZxTiMNZD5WLS3P
briansrls pushed a commit that referenced this pull request Aug 24, 2026
…s, resolved by rule

The branch was 95 commits behind and is now current. Its own lane has done this
117 times before - this is that workflow, not a new approach.

HOW THE 224 HUNKS RESOLVED, each arm an explicit rule rather than a judgment call:
   47  take the cut side  - main re-adding imports the cut deletes on purpose, or
                            adding prose above an otherwise-unchanged line (main's
                            comment kept, cut's qualification kept)
  162  requalify main     - main changed meaning; its line is taken and qualified
                            from a declaration index derived from main itself
   15  hand-resolved      - genuine refactors, chiefly #9049's replacement of
                            per-transport dispatch with one host_operation_exec

WHERE BOTH SIDES ADDED DIFFERENT CONTENT, BOTH ARE KEPT. Taking one side whole is
a silent deletion of the other's authority. Verified: no duplicated declarations,
and cut-side rows main lacks (gunbc_deploy_privileged_helper_path) survive beside
main's new ones.

GENERATED MIRRORS ARE NOT MERGED. Five .rs files conflicted and are restored from
main rather than resolved - a mirror is regenerated from its .dag, and a
hand-merged mirror is a hybrid no authority produced. REGEN IS OWED before this
lands; the branch's own history does this as a separate "install the emitted seed"
step. One dangling `mod class_b_trim_specimen_test;` is dropped from the restored
lib.rs because the cut deleted that test deliberately ("Delete the tests that
assert import semantics").

FIVE DEFECTS IN MY OWN TOOLING, each caught by reading output rather than trusting
the run, and each silent damage at scale:
  - it ran on GENERATED .rs FILES and wrote `use crate::std_algebra::std.algebra.
    AlgebraProfile::{` - a .dag qualification inside a Rust use statement. Caught
    by pre-commit, not by me; the .rs files are now excluded and restored.
  - rewriting inside STRING LITERALS: the corpus stores .dag source as fixture
    text, and this branch already had to "revert 302 in-string qualifications".
  - qualifying DECLARATION HEADS: `fn v2.compiler.infer.facts_map_from_entries`
    renames a declaration rather than referring to it.
  - SELF-QUALIFICATION: a name declared in the same module stays bare; the branch
    previously repaired 116 of these.
  - an index blind spot: the FIRST variant after `type X =` carries no leading `=`
    or `|`, so LocalShell and ~50 others were invisible.

WHAT THE INDEX REFUSES TO DO: 1169 names are declared in more than one module. A
wrong qualification there binds the wrong authority AND COMPILES CLEAN, so
ambiguous names are never auto-qualified - they fall to the file's own bare-in-cut
evidence, or to hand resolution.

NOT YET COMPILED against a guarded binary: the local build SIGKILLs and the
scanner guards now live on main. CI is the oracle for this merge.

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

* A parenthesis-free lambda parameter was read as a reference, and pulled the module that happened to declare its letter

`bare_identifier_candidates` records which identifiers a file BINDS so they are
not mistaken for references to other modules' declarations. It recognises the
parenthesised lambda form `(a, b) => ...` and the pattern form `{ .. } => ...`,
both through `destructuring_bound_spans`. It does not recognise the
single-parameter form with no parentheses:

    algebra_templates_for_profile(profile: profile) |> any(t => t.name == name)

`t` is a binder. It was scanned as a reference, resolved against the WHOLE POOL
by `bare_reference_pull_paths_for_source`, and matched `fn t(component, terminal)`
in `dag/test/claim/pcb_footprint_witness_test.dag` — so `dag/std/algebra.dag`
acquired a closure edge to a PCB witness test, and through it to the PCB product
corpus, spatial frames, orthogonal topology, observation and attribution.

The same shape ran twice more in the same closure: `ok` in `error_primitives.dag`
matched `fn ok(out: String)` in a spark witness test, and `w` matched another.

WHY THIS SURFACED NOW rather than years ago. The bare census is gated on
`source_declares_import_lines` — a file that declares any import is skipped by it
entirely. On main almost every file declares imports, so the defect is dormant.
The namespace cut deletes every import line in the corpus, which switches the
bare census on for every file at once; the defect is not new, its population is.

THE GUARD IS THE PRECEDING TOKEN, NOT THE ARROW. A match arm head is also
`ident =>`, and binding it would suppress a real edge to the module declaring the
variant. An arm head is preceded by `{`, `}` or a newline; a lambda parameter
sits in argument position, after `(`, `,` or a named-argument `:`. Measured over
the corpus, all 4397 sites of the form `[(,:] ident =>` are lambdas and no match
arm head is preceded by any of the three.

MEASURED, one command, three arms, `gunbc compile --source-root dag
--source-root src/v1 --entry src/v1/compile.dag`:

  main,   unpatched binary:   99 sources,   0 blocking, exit 0
  main,   patched binary:     99 sources,   0 blocking, exit 0   <- no regression
  cut branch, unpatched:     172 sources,  65 blocking
  cut branch, patched:       155 sources,  34 blocking

Main is byte-for-byte unchanged in both closure size and diagnostics, so the
narrowing removes only edges that were never real. On the branch where the
defect is live it removes 17 modules from the seed's compile subject and half
its blocking diagnostics.

The two arms on main are the discriminating control in the honest direction: a
change that narrows a closure can always be made to look good by narrowing it too
far, and the main arm is what rules that out.

* A module's own coproduct variant was read as a reference out, and pulled whatever module declared that name at top level

Second instance of the same class as the previous commit, found by re-tracing
after it landed. `bare_identifier_candidates` records binders; the global bare
census indexes top-level declaration HEADS. A coproduct VARIANT is neither, so a
module that declares a variant and then writes it was scanned as referencing
someone else — and the whole pool was consulted.

Four in one closure, every one absurd in the same way:

  std.cache_interface   `type VisibilityScope = Repo | Org | Network | World`
                        used `World` -> pulled dag/product/spatial_world.dag
                        (`type World sole_constructor`) and the spatial corpus
  std.measure           `| Volume` (its own Dimension) used in `Measure<Volume,
                        Nano, Nat>` -> pulled gunbc.roadmap_model (`type Volume`)
  std.realization_sched `| Measured` used as `basis: Measured`
                        -> pulled std.observation (`type Measured<T>`)
  std.spatial_frame     `= ExtentInFrame { length: FrameLength }`
                        -> pulled std.attribution (`type ExtentInFrame`)

This is not a tiebreak between candidates and not a policy about which module is
likelier. A name the module itself declares is BOUND BY THAT DECLARATION, so it
cannot be a reference out; skipping it is the language's own scoping rule. That
matters for how the change is read: the nearest-ancestor picker below is still a
heuristic, and this commit does not improve it — it removes a population that
should never have reached it.

MEASURED, same command and arms as the previous commit:

  main,   unpatched:      99 sources,   0 blocking, exit 0
  main,   both guards:    99 sources,   0 blocking, exit 0
  cut branch, unpatched: 172 sources,  65 blocking
  cut branch, lambda:    155 sources,  34 blocking
  cut branch, both:      104 sources,   4 blocking

Main is unchanged across every arm. The cut branch's seed closure converges to
104 against main's 99 — the remaining five are real edges the cut's own
qualifications introduced, not over-pull — and the residue is 4 diagnostics.

* A declaration's own type parameters were read as references — and the trace now says which arm resolved a name

THIRD instance of the class, and it closes the third diagnostic.

`dag/std/error_primitives.dag` declares `type Result<ok, err> = Ok { value: ok }
| Err { value: err }`. `ok` and `err` are TYPE PARAMETERS. Scanned as references
they reached the whole-pool arm and matched `fn ok(out: String) ->
SshSessionExecResult` in `dag/test/claim/spark_serving_durability_witness_test.dag`,
so `std.error_primitives` acquired closure edges to that witness and to
`extdeps.ssh.session` — and `extdeps.dns.domain_name`'s
`Ok { value: labels |> list_push(label) }` then typed `labels` as
`SshSessionExecResult` and reported `list_push` unresolvable on it.

Arms, one command throughout:

  main,   unpatched:      99 sources,  0 blocking, exit 0
  main,   three guards:   99 sources,  0 blocking, exit 0
  cut branch, unpatched: 172 sources, 65 blocking
  cut branch, two:       104 sources,  4 blocking
  cut branch, three:      94 sources,  2 blocking

Main is unchanged across every arm.

SECOND CHANGE, and it is the reason the third instance was findable: the
resolution arm is now carried instead of discarded.

    let target_module = match resolve_in(&census) {
        Some(m) => Some(m),                            // scoped census hit
        None => resolve_in(&pool_bare_census(index)?), // WHOLE-POOL fallback
    };

collapsed both arms into one `Option<String>` one line after computing the
distinction, so `GUNBC_BARE_PULL_TRACE` could report WHAT a name resolved to and
never HOW. Those are different facts: a pool-fallback hit means the closure is
depending on ambient pool membership rather than on anything the file's own tree
provides, which is precisely the state a zero-import corpus can sit in while
looking green. The arms already know which fired; carrying it costs nothing.

First measurement it makes possible, on the cut branch: 37389 scoped against 733
whole-pool fallbacks, 508 of them (distinct file+name) from `src/v1`. Every one
of the 23 distinct names whose fallback answer differs from the module main's
import list named is a RE-EXPORT — `v1.std.core` imported and re-exported them
from `std.syntax` / `std.types`, and `src/v1/00_core.dag` declares none of the
23 — so the fallback names the declarer where the import named the re-exporter.
No wrong-provider selection was found. That is a measurement the previous trace
could not express.

* Collect the multiline coproduct form review 55386 caught being missed, and carry the census's own verdict

REVIEW FINDING, verified and correct. `module_self_declared_names` opened a
coproduct block only when the `type X =` head line carried its own `=`. The
corpus also writes

    type FrameExtent
      = ExtentInFrame { length: FrameLength }
      | ExtentFrameUnregistered { .. }

so `ExtentInFrame` and `Predicted` — two of the four variants the guard exists
for — were never collected. The closure improvement I measured for
`std.spatial_frame` and `std.realization_schedule` therefore came from those
modules leaving the closure for an unrelated upstream reason, not from this
guard. Right conclusion, wrong evidence, and the review caught it.

The fix is one predicate: a bare `type X` head opens a pending coproduct too.
The same-line extraction is re-gated on the LINE's own `=` rather than on the
flag, so widening the flag cannot silently disable it — which it did in the
first attempt at this repair.

WHAT THAT FIRST ATTEMPT COST, recorded because the number is the whole argument:
rewriting the scanner to gather each declaration as a BLOCK (with an alias guard,
so `type List<element> = FreeMonoid<element>` would stop contributing
`FreeMonoid`) is the obviously nicer design, and it measured WORSE — the branch
arm went 94 sources -> 126 and 2 blocking -> 3, while main stayed at 99/0. Two
distinct causes were found and fixed inside it (a blank line inside a coproduct
ended the block, silently reopening the `Volume` -> `gunbc.roadmap_model` edge
guard 2 closes) and it was STILL worse, so the alias distinction interacts with
something not yet understood. The line-wise scanner is kept and the alias
over-collection is left in place, marked, as a known under-pull needing its own
arms rather than a rider on this one.

REGRESSION TESTS, as the review asked — five, over the scanner paths this PR
adds: every coproduct form including the multiline one and blank lines inside a
block; record field labels NOT collected; a parenthesis-free arrow lambda
parameter bound while a match arm head is NOT (the direction that matters, since
binding an arm head is a silent under-pull); and the named-argument and
comma-positioned lambda forms.

SECOND CHANGE: the trace now carries the census's own verdict beside the arm.
`resolve_in` returned `Option<String>` and discarded whether the lookup was
`GlobalBareUniqueBinding` or `GlobalBareAmbiguousBinding` — the one authority on
whether a bare name has competing declarations. Reconstructing that from source
does not work: a line-leading `=`/`|` declaration scanner undercounts (it misses
`type Connective = Conj | Disj | NoConnective | Arrow` entirely) and a permissive
one overcounts (it reads alias targets and `data` initializer heads as variants),
and the two answers differed by 30x on the same trace. The census already knows.

First measurement it makes possible, cut branch under regen's roots:

  35017 scoped unique
    534 scoped service
      9 scoped AMBIGUOUS
    737 pool-fallback, every one unique

So the nearest-ancestor picker is consulted nine times in this closure and never
on the whole-pool arm. Those nine are `Json`, `owner`, `row` and
`rm_force_command` — real competing declarations, decided by module-path
proximity.

Arms unchanged by this commit: main 99 sources / 0 blocking / exit 0; cut branch
94 sources / 2 blocking.

* An alias target is a reference out, not a variant this module declares

review 55399: `module_self_declared_names` collected the first identifier
after a type `=` as a self-declared variant, alias targets included, so
`type List<element> = FreeMonoid<element>` suppressed the closure edge to
FreeMonoid's declarer. A fail-open under-pull, the dangerous direction.

An earlier revision carried it as a marked KNOWN GAP with no owner and no
trigger. That was the error under review, not just the scanner: a marked
gap records how debt ENDS, it does not authorize creating it.

The discriminator is the right-hand side's own shape — a coproduct
alternates or carries a record payload, an alias does neither.

The arm shape cannot decide (`type X = Foo`, bare capitalized RHS) is
censused rather than assumed: 60 occurrences across dag + src/v2 + src/v1,
every one an alias. 47 name a type in another module — the population this
changes behaviour on. 13 name a same-file type and are inert either way,
since a same-file `type` head is already self-declared via the
declaration-head arm. Zero are single-alternative coproducts. Not claimed:
that the ambiguity is impossible; the residual failure would be an
over-pull.

The closure is INVARIANT under this fix on this corpus — 94/2 before and
after, because the 47 restored edges reach targets already pulled by
another route — so the closure measurement cannot be evidence the fix
works, and the two new tests carry the correctness claim alone. Executed,
not argued: with the predicate forced to `false` the alias test FAILS and
the payload test stays green. 7 pass in the lib target.

Main unchanged at 99 sources / 0 blocking / exit 0.

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

* Pay the seed-growth receipt #9102 owes, enumerated from the diff rather than from prose

The bare-reference scanner adds nine hand-authored Rust declarations to the
seed and shipped without the SeedGrowthJustification that DESIGN's
forward-freeze policy requires. This is that receipt: one owning module plus
one row in the closed roster, modelled on gunbc.whole_corpus_compile_admission.

Every figure is measured against origin/main, not recalled. Nine added
declarations - one production (module_self_declared_names, 115 lines, not the
~603 that circulated) and eight test scaffolding. Hand-LOC delta +413/-17, all
in cli_run.rs. Three existing items are MODIFIED and are named in a separate
not-growth note rather than netted into the addition census, which is the
direction that makes a census look conservative while being wrong.

Two corrections to what I had written from memory. "No other .rs changed" was
false: five generated mirrors moved, and the row now names them and cites the
generated-population exemption instead of asserting a clean slate. And a naive
grep for added Rust declarations returns eleven extra hits that are fixture
.dag source inside Rust string literals - data, not declarations - which is
recorded so the next person re-running this census does not count a script's
own echo as its output.

What this does NOT do is admit the scanner. The scaffold presumption stands and
is stated rather than argued away: the scanner re-derives parser-owned facts
from raw text, and 115 lines rather than 603 changes how much debt is on the
table, never whether the presumption applies. The realization disposition is an
operator judgment and fails closed in its absence. A receipt that also granted
its own admission would be the self-authorized dissolution failure DESIGN
names - an author who can write a scaffold can equally write a row claiming it
was approved.

Verified by execution: gunbc compile over the roster entry, 0 blocking errors.

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

* Reconcile the three counts the receipt would otherwise leave a reader to add up wrong

My "eleven extra grep hits" was a LINE count reported where a reader would read
an ITEM count, and it was measured with an ad-hoc filter. Re-measured against
the authority instead of by grep: scripts/rust_item_census.py --diff origin/main
reports EIGHTEEN added hand items, NINE real and NINE phantom, and the phantoms
are now named individually rather than counted.

Three denominators circulate and the receipt now says which is which. Eighteen
is the census item total. Nine is the deduplicated phantom item count. Twelve is
the fixture LINE count, larger because CostAccount is authored twice (bare and
parameterized) and fn f twice (over List<Int> and List<Row>) - the census enrols
an item once, the line scan sees each spelling. Nine plus eleven reconciled with
nothing, and a reader doing that arithmetic would have concluded the receipt was
wrong when it was the caveat that was.

Stated as a CENSUS DEFECT rather than only a grep caveat, because the next
author will run the census, get eighteen, and otherwise have no way to know why
it disagrees with the enrolled nine. The census predicts this itself:
seed_growth_g0_census_host_scaffold_note records that its extraction is regex,
parallel to but outside the rust grammar authority. This is that prediction
firing - and it is the exact class of the artifact this receipt admits, a
raw-text recognizer miscounting language structure because it reads source as
text instead of consulting the authority that owns it. That is the strongest
available argument that the terminal construction the presumption note names is
the right one for both.

Also records that the census reads module_self_declared_names as 116 lines to
the fn-body count's 115, since it includes the signature line.

Reconciliation raised by deep-ant-102, who authored a competing receipt and
discarded it unpushed rather than land a second authority for one fact.

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

* Measure the branch delta from the merge base — my correction was the error, not the claim it corrected

The receipt said five generated mirrors moved on this branch. They did not. The
branch touches exactly THREE files, measured from the merge base bd84f66:
this module, the roster row, and cli_run.rs at +413/-17.

The false population came from diffing against origin/main, which puts main on
the LEFT — so main's own commits since the branch point rendered as deletions
authored here. My ORIGINAL sentence, no other .rs changed, was correct, and I
replaced it with a falsehood while believing I was repairing an overclaim.

The consequence is the part worth recording. deep-ant-102 then independently
verified the generated-file exemption for those five files, by reading their
// Generated by v1 compiler banners with cli_run.rs as a discriminating control.
That verification was sound and its subject was invented: five files this branch
never touched. Two sessions agreed, both were wrong, and nothing in either
method could have caught it because neither of us re-derived the population -
we both took the diff direction on trust.

So the row now carries the rule rather than only the corrected number: a branch
delta is measured from the MERGE BASE, never from the moving tip, and a hand-LOC
census that names a file must be reproducible by a command that states its base.

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

* State the seed-growth delta against main, not only against the merge base

A byte-identical copy of module_self_declared_names landed on main via #9090
while this PR was open. Verified: absent at merge base bd84f66, present on
main, md5-identical to the copy here. Git collapses identical additions at one
location, so the merge yields exactly one definition and zero conflict markers -
nothing breaks, which is why no reviewer, no gate and neither author noticed.

The receipt said +9. That is correct for AUTHORSHIP, measured from the merge
base, and it is the wrong question. A seed-growth receipt asks what MERGING adds
to the seed, and against current main that is +8 - all test scaffolding, zero
production items. Both numbers are now stated with their base and their question,
because neither base is simply correct and using one where the other was meant is
what produced this.

This is the mirror of the correction I made earlier today. That one moved from
origin/main to the merge base after main's commits rendered as deletions I had
authored. This one adds main back for the question the merge base cannot answer.
The lesson is not "use the merge base" - it is that a delta is meaningless until
its base is named, and the two bases answer different questions.

The consequence is recorded because it changes what the pending operator
disposition can do: the main copy carries NO receipt - nothing in any .dag on
main names the function - so a hand-written source scanner is already in the
frozen seed unadmitted, and the only receipt that would admit it is behind this
PR. RejectedForFinalConstruction no longer removes it from the seed; it removes
the receipt and leaves the unadmitted copy standing. Refusal does not restore the
prior state, and the operator must not decide believing it does.

Not an accusation of anyone. Two sessions independently needed the same fact and
independently wrote the same scanner. That is the terminal-construction argument
made by the corpus rather than by an author: when parse does not publish a fact,
the number of hand re-derivations is bounded only by the number of consumers who
need it.

Found by deep-ant-102, routed from an orphaned #9090 finding by smart-ram-730.

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

* The scanner already shipped via #9090 — this receipt is its accounting, not a proposal

My previous revision said the delta was +8 against main, all test scaffolding.
That assumed an independent duplicate. It is not one. #9090 merged THIS branch
into its own (Merge commit eb2ad48... into session/witty-lark-109, carrying
the five scanner commits by name) and squash-merged to main at 18:01:36Z as
fd55f00. All NINE declarations are on main - the production item and all eight
test items - so the figure against current main is ZERO, not +8.

Squash carries CONTENT, NOT ANCESTRY. merge-base --is-ancestor reports none of
those commits on main while git grep finds the function and every test fn there
byte-identical. Both answers are true of different questions, and reading the
ancestry answer as an authorship answer is what made the byte-identity look like
a second author independently writing the same 115 lines. One author, one lineage.

The receipt keeps +9 from the merge base as the AUTHORSHIP figure, because those
declarations are what was written and naming them is what the receipt is for. It
now states separately that the population already landed by another route.

WHAT THIS INVERTS: refusing no longer removes the scanner from the seed. Nothing
in any .dag on main names module_self_declared_names, so the seed today carries a
hand-written source scanner with no receipt anywhere. Refusal deletes the
accounting and leaves the code standing - the outcome that keeps the seed
silently grown. Admission is what makes the seed honest about what already
landed. The disposition is a decision about accounting, not about admitting code.

This PR now contains two .dag files. The 413 lines of scanner are on main.

Found and corrected by deep-ant-102, who also retracted their own independent-
authorship reading rather than let it stand.

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

* Two defects, one rule, TWO retirement triggers — and a name that implied the judgment it refuses

Both corrections raised by the side-chat reviewer, and both are real.

1. "one terminal construction retires both" was WRONG. The scanner and the
   rust_item_census share a CLASS - a raw-text recognizer misreading language
   structure - but not a terminal construction. The scanner retires when the .dag
   front end publishes binder and declaration facts. The census retires when a
   Rust grammar authority publishes real Rust item identities. A .dag binder
   surface does not retire a regex Rust-item census. Writing them as one trigger
   discharges the second defect by implication instead of giving it an owner,
   which is how a known defect goes quiet. What they actually share is the
   architectural rule: language structure comes from the authority for that
   language, never from a parallel raw-text recognizer - two consumers, two
   triggers.

2. bare_reference_scanner_admission_authority_disposition was named for the exact
   judgment its own reason refuses to make. Its value is Terminal and it
   dispositions the RECEIPT, while the open question is whether the SCANNER gets a
   realization disposition. A declaration named admission_authority_disposition
   sitting beside that open question invites a reviewer to conclude the admission
   already exists. Renamed to bare_reference_scanner_receipt_disposition. Not
   cosmetic: the live blocker is precisely that admission.

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

* Record the operator disposition in the receipt: ScaffoldAdmitted, with its boundary

The realization disposition this receipt deliberately refused to grant itself has
been given: ScaffoldAdmitted, bound to 7bc6606, relayed by deep-ant-102. The
scanner is a bounded bootstrap realization serving the v2 self-host namespace
cut, NOT a retained kernel, and it deletes rather than surviving as a fallback
when parse publishes the binder and declaration facts it re-derives.

The receipt now carries the admitted scope and - more importantly - the SIX
things that are NOT admitted and each need a new disposition: additional
source-text recognizers, new callers, public-surface growth, the whole-pool
fallback, nearest-ancestor ambiguity selection, and a general hand-written
namespace parser. A receipt that recorded only the permission and not its edges
would be read later as broader than it is.

RECONCILED ONE DISCREPANCY rather than assuming it away: the verdict counts
THREE separately recorded modifications and its boundary block names TWO. The
third is project_roadmap_acceptance_event_history_from_authority_text_builtin.
Count agrees, enumeration does not, which reads as an omission in the boundary
rather than a narrowing - the verdict's own count includes it. Stated explicitly
because a future scope question judged against a two-item boundary reaches a
different answer than one judged against the three the verdict counted.

Also records why the head moving from 7bc6606 to the merge does not require
renewed admission: measured against main the delta is two .dag files, zero Rust,
one definition of module_self_declared_names. No scope growth, so the
next-reviewed-head clause applies rather than the renewed-admission clause.

The relay is recorded AS a relay. This session cannot read the operator thread,
so the carrier is named rather than the reading claimed first-hand.

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

* Retire the four route-gap/freeze collisions #9049 authored, by the precedent the log already sets

The required floor refuses RouteGapFreezeIntersection count=4: four identities
claim LegacyFrozenPathDeferral - admitted as having NO EXECUTING CONSUMER - while
carrying a floor_route_gap receipt that exists only because the required floor
consumed the row. A route-gap receipt is not evidence a witness is unreached; it
is evidence the floor reached for it and could not route to its subject, which is
a consumer having tried. Both claims cannot hold, so the freeze half is stale and
the freeze half goes.

THIS IS NOT A NEW JUDGMENT. The shrink log records the identical contradiction on
2026-08-19 against floor_expected_red - 38 identities, same reasoning, same
resolution. Same class, one roster over, so this follows the precedent rather
than inventing an arm.

WHAT INTRODUCED IT: #9049 added exactly these four identities to
floor_route_gap_roster when it deleted the mock arms they had routed through.
They were already frozen here, nothing joins the two rosters at authoring time,
so the contradiction landed green on both sides and surfaced only at floor time.

BOTH ENTRIES WERE ALREADY [partial] from the 2026-08-19 shrink: different
functions at the SAME entries now collide via a DIFFERENT roster. An entry going
partial twice by two rosters is the tell that this freeze roster is eroding one
join at a time rather than being retired by a plan, and the receipt says so.

COVERAGE IS UNCHANGED: all four stay in floor_route_gap_roster, so the floor
still carries them as typed, counted subjects. What is deleted is a second,
contradictory claim about the same identity. Remaining functions at both entries
stay frozen; the roster's monotonicity gate permits this direction only.

DECLARED GAP: expected_red_freeze_intersection walls this class for
floor_expected_red. Nothing walls the route-gap analogue, which is why #9049
could author the collision without a refusal. Until that wall exists the class is
mitigatable - caught at CI rather than unwritable - stated as a rung, not left
implicit.

Verified: gunbc compile over the edited carrier, 0 blocking errors.

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

---------

Co-authored-by: gunbc-ci-auto-heal <gunbc-ci-auto-heal@users.noreply.github.com>
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
Co-authored-by: Brian Searls <briansearls1@gmail.com>
briansrls pushed a commit that referenced this pull request Aug 25, 2026
…oster both walls read (#9145)

Two walls read frozen_path_deferrals -- expected_red_freeze_intersection and
route_gap_freeze_intersection, both in v1_compiler.cli_run -- each refusing an
identity simultaneously frozen here and enrolled in a roster asserting the
required floor executes it. They landed five days apart and only one landed
against an empty population.

NOT A NEW PRINCIPLE. It is DESIGN's own admission test seen from the arming side
rather than the phase-enrolment side it is stated on: "the admission test is
whether every red is closable by the author who caused it at the moment they
caused it". A wall armed over an already-non-empty population fails it by
construction -- the author who causes the red is whoever pushes next, and the row
that makes it red was authored elsewhere.

SATISFIED  #8494 521d4cf 2026-08-19: 38 colliding rows deleted and the wall
           wired in ONE diff.
VIOLATED   #9114 96cd1bf 2026-08-24 14:10: armed over a population of 4.
           Retired by #9133.

WHY THE VIOLATION IS NOT CARELESSNESS, which is the reason the row is worth
reading rather than a caution to recall. #9049 (664b339) enrolled the four
identities at 14:09 -- SIXTY SECONDS earlier -- and is an ancestor of #9114, so
the arming commit's base already carried them. Sixty seconds is far less than a
witnesses run, so #9114's green was established against a base WITHOUT the
collision and it landed on a base WITH it. Neither author could see the
contradiction from inside their own change: #9049 added rows to a roster no wall
yet guarded, #9114 armed a wall it had correctly measured as empty. The union was
red and neither half was.

So the test is NOT "count the population before arming" -- that would have changed
nothing here. It is that a wall's green must be established on the base the wall
LANDS on. The satisfied instance got that for free by clearing and arming in one
diff, leaving no interval in which another change could enter the population.

RUNG AND TRIGGER (DESIGN 4b(2)), written as a trigger rather than a ceiling
because an unnamed stall and a real ceiling read identically. The class sits at
mitigatable, held by a one-diff discipline nothing enforces. No roster-shaped
check can climb it: at the moment #9114 was authored and reviewed its governed
population was genuinely empty, so no state in either roster could have been
refused, and a check over authored rows would be a ratchet. The next-rung trigger
is named at the layer the defect lives on -- up-to-date-with-base before merge,
strict required checks or a merge queue, which makes "green on the base it lands
on" true by construction. That is an operator decision and the row does NOT
assert how it is configured today; the setting was not readable when it was
written.

HOME. Proposed for v2.workflow.required_floor; the walls are not there and it
names neither, so a row there asserting how they landed would be authority
substitution. This file is the one roster both walls read, already names one of
them, and already carries both polarities as shrink-log entries -- so the row
joins facts in the file rather than importing them, and avoids hand-LOC growth in
the frozen seed.

Re-derivable: join floor_route_gap_roster (110 entries) against
frozen_path_deferrals (128 rows, 614 qualified identities), qualifying every
frozen row through its own entry's module line and not its path string -- 4 of
614 at 8ab8a8e, matching the wall's count=4. Corroborated on this branch:
frozen_path_deferral_identity_count returns 610, which is 614 less the 4 #9133
retired.

Verified: the module parses and evaluates after the annotation.

Co-authored-by: gunbc-ci-auto-heal <gunbc-ci-auto-heal@users.noreply.github.com>
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
briansrls added a commit that referenced this pull request Aug 25, 2026
…l-open: the premise falsified, and the join enrolled (#9123)

* rust_runtime_bridge_name's Absent arm is the identity case, not a fail-open: the premise falsified, and the join enrolled

The brief held that an unknown bridge name is emitted unchanged, so every
mismatch becomes silently invalid Rust. Measured, that is false in two
independent ways, and the arm is correct as written.

rt_bridge_function_names is DERIVED, not authored: filter(f => f.name !=
f.bridge_name) over rt_function_registry, 9 rows of 57. A miss therefore means
"this bridge's v1_rt symbol is spelled like its .dag name", which holds for the
other 48. Returning the input unchanged is the total answer, not a fabrication.

An unknown name also cannot reach either use of the result. emit_typed_call
consumes runtime_name only inside `if is_rt`, and is_rt requires
map_contains_key(rt_functions(), func); emit_rust_generic_method_call computes
bridge_name only in the else of a guard that already refuses with the Rust
error_type_template when rt_functions() misses.

What lands is the evidence, not a repair. The witness joins registry to
derivation by IDENTITY over every row -- rt_bridge_name(entry.name) ==
entry.bridge_name, no count and no literal, per DESIGN section 5's oracle rule --
so a future row whose override is dropped from the map goes red here instead of
emitting a call to a v1_rt symbol that does not exist. Executed both ways:
green on the tree as it stands; false when the derivation's filter is replaced
by filter(f => f.name == "concat"), the control reverted with no diff. The
section 4c annotation records the two guarded call sites, which a reader of the
function alone cannot see.

One further falsification, recorded here because it cost a build and would
otherwise be re-derived: the same shape one arm over -- emit_typed_method_call's
PlainMethodSemantics arm rendering recv.method(args) for a method with no
rust_method_templates row -- is NOT a live fail-open either. 04_infer's
method_existence_decision refuses first, typed and located, on every shape
probed: a kernel-profiled receiver (method 'nope_not_real' not found on receiver
type 'Container(List,Primitive(String))'), a propagated lambda parameter, and a
bare type variable (ReceiverTypeUnestablished). Contrary readings came from the
baked /usr/local/bin/gunbc, which resolves the entry file alone -- its own
documented tell, and it emitted x.nope_not_real() with 0 diagnostics where the
tree binary blocks.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XewpecCnCUNjXbeQGz5obk

* Retire the four route-gap/freeze collisions #9049 left on main: a frozen row naming a witness the floor already consumes

The required floor refuses at RouteGapFreezeIntersection count=4 and this is a
MAIN BREAKAGE, not a property of this branch. #9049 enrolled four identities in
v2.workflow.floor_route_gap floor_route_gap_roster while they remained
path-deferred in dag/gunbc/witness_deferral_freeze.dag frozen_path_deferrals as
LegacyFrozenPathDeferral. Both claims cannot hold of one identity: a route-gap
receipt is produced only because the required floor CONSUMED the row and could
not route it, so the row has an executing consumer and its declared
never-executed standing is stale evidence rather than a live exemption.

Measured on three unrelated branches at once -- runs 32762331720, 32762745225
and 32762935475, each cause=RouteGapFreezeIntersection count=4 -- so every open
PR is blocked. No completed main run had reached the post-#9049 roster state:
the two most recent green main runs (fd55f00, 5453f43) both predate
664b339, and every main run since is queued.

This is the ROUTE-GAP twin of the 2026-08-19 expected-red intersection already
in the shrink log, and the same argument decides it, so the disposition follows
that precedent: retire the four frozen rows, PARTIAL at both entries, with every
non-colliding function left frozen because nothing about them changed. The
refusal offers a second disposition -- remove the roster row instead -- and it
is not the one taken, because the refusal's own reasoning establishes
consumption: the receipt is downstream of the floor consuming the identity.
Retirement removes no coverage; the floor already attempts these four.

Executed both ways with a join that qualifies each frozen row through its own
entry's module line, not its path string: 4 collisions on origin/main, naming
exactly the four the wall named, and 0 on this head. 3932 files parse-clean.

Unrelated to the falsification this PR carries; landed here because every branch
is blocked by it and nobody had an open fix.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XewpecCnCUNjXbeQGz5obk

* Revert "Retire the four route-gap/freeze collisions #9049 left on main: a frozen row naming a witness the floor already consumes"

This reverts commit 5621cd9.

* The witness was enrolled where the floor declines the whole arm: move it to a module that actually executes

Measured on this PR's own green run (32778297671), the witness this PR adds is
NOT executed by the required floor:

  test.claim.v1_source_audit_witness_test.rt_bridge_name_agrees_with_registry_on_every_row
      declined_live_tree

It is one of declined_live_tree=902 of offered=12303 -- discovered, counted,
folded never. Every identity in that module shares the disposition, because the
module declares live_tree_disposition = ReadsLiveTree for its src_has file
readers. So the green run said nothing about this witness, and a passing CI
would have been cited as coverage for evidence that cannot run. That is the
specification-without-execution trap (DESIGN section 5) and the decoration
failure (section 4b): a check whose red is unreachable is worse than absent.

Nothing about the FOLD needed the live tree. rt_function_registry is an
in-corpus declaration and the join reads no file, so the decline was inherited
from the module's blanket disposition rather than earned by the witness. The fix
is therefore a home, not a rewrite: test.claim.rt_bridge_registry_witness_test,
which declares no live-tree disposition and is planned. The identity join, its
denominator and its argument are unchanged.

v1_source_audit_witness_test.dag returns byte-identical to main -- this PR no
longer touches it at all.

Executed in the NEW home, both ways: returns true as the tree stands, and false
when the derivation's filter is replaced by filter(f => f.name == "concat"),
control reverted with no diff. 3939 files parse-clean.

Found by reading the floor's own required_floor_disposition.tsv artifact rather
than the check mark. A green witnesses run and a witness that never ran render
identically at the check level, which is exactly why the disposition roster
exists.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XewpecCnCUNjXbeQGz5obk

* The annotation cited the witness's old module: the move broke the citation in the same PR that wrote it

review 55574 (REQUEST_CHANGES) is correct. The previous commit moved the witness
out of test.claim.v1_source_audit_witness_test -- whose whole arm the floor
declines -- into test.claim.rt_bridge_registry_witness_test, and did not update
the annotation on rust_runtime_bridge_name that names it. The citation was false
on the day it was written, in the same diff that authored both ends.

This is the DESIGN section 3 cite-the-symbol class, and worth recording that it
is now caught by review rather than by a gate: the cited-symbol census was
dropped from CI 2026-08-23 by operator directive, so the rung is review
diligence. This is what that costs.

Fixed the module name. Re-resolved every symbol the annotation names, rather
than only the one reported: emit_typed_call, emit_rust_generic_method_call,
rt_functions, rt_bridge_function_names in the emitters; rt_function_registry,
rt_bridge_name in extdeps.languages.rust.emit; and the cited test fn in the new
module. All six resolve.

The surviving reference to v1_source_audit_witness_test is in the new module's
own header, explaining why the witness is NOT there. That module exists and the
sentence is about it, so it stays.

3939 files parse-clean.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XewpecCnCUNjXbeQGz5obk

* The totality argument rests on the map being derived: say so where it can be invalidated

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XewpecCnCUNjXbeQGz5obk

---------

Co-authored-by: Brian Searls <briansearls1@gmail.com>
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
briansrls pushed a commit that referenced this pull request Aug 25, 2026
#9163)

Two main reds in one evening, and a third pair before them, all one class:
two PRs independently green, jointly broken. #9049 with #9114, #9057 with
#9062, #8919 with #8992. No textual conflict in any of them -- the first
change altered a semantic API and the second was checked against a base
where the old API still existed, so both greens were true of trees that
never existed together. The #9057/#9062 pair took the whole fleet red for
about an hour and produced four uncoordinated repair PRs plus a fifth
branch, two of which proposed deleting live code.

No wall inside this repository can close that. The invariant needed is that
the commit admitted to main is the exact composed commit that passed the
required checks, and nothing in the substrate can constrain what GitHub
admits.

WHAT THIS CHANGE IS: the in-repository half, and it is INERT ON ITS OWN.
witnesses.yml listens only to workflow_dispatch, push and pull_request, so
enabling a merge queue today would create a merge-group commit for which the
required check never schedules -- pending forever. This adds the trigger so
the prerequisite exists BEFORE the setting is changed, with no window in
between. With no queue configured the event never fires, so CI behaviour is
unchanged by this diff.

WHAT IT IS NOT: the repository setting. The ruleset currently carries
strict_required_status_checks_policy false, no merge-queue rule, and an
always-bypass role. That is an operator decision and this PR does not
presume it.

MergeGroup already exists in extdeps.github.actions WorkflowTrigger and the
YAML emitter already renders it, so this is one row, not new modelling.

EVIDENCE, including what I could NOT establish. Evaluating the workflow
authority on this branch and on clean origin/main gives byte-identical
diagnostic sets (2232 lines, zero diff), so nothing is introduced. I could
NOT run the emitter to regenerate witnesses.yml: the local gunbc shim is a
Jun 26 build that cannot resolve this corpus. The one yml line was derived by
reading the emitter -- `MergeGroup => kv(key: "merge_group", value: YamlNull)`
renders exactly as `workflow_dispatch:` does, in the position its entry
occupies in the `on:` list. DESIGN records the generated-artifact drift gates
as currently unguarded, so nothing will catch that line if I have it wrong,
which is why it is stated rather than assumed. Regenerating in CI and
diffing is the check I want on this PR.

Co-authored-by: Brian Searls <briansearls1@gmail.com>
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