Skip to content

SymbolIndex grew a field the one test-module initializer never got, and no gate can see it - #9125

Merged
briansrls merged 2 commits into
mainfrom
fix/symbolindex-test-initializer
Aug 24, 2026
Merged

briansrls merged 2 commits into
mainfrom
fix/symbolindex-test-initializer

Conversation

@gunbai-bot

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

Copy link
Copy Markdown
Contributor

The defect

v1_compiler_infer_env::SymbolIndex declares type_head_exposures. Every construction site sets it except one: the SymbolIndex { .. } literal in cli_run.rs's process_resolve_store_tests fixture.

So cargo test -p v1-compiler --lib does not compile on current main — error[E0063]: missing field type_head_exposures — and the entire lib test binary is unavailable, including the ~595 tests unrelated to whatever change introduced the field.

The value is rc_empty_map(), matching empty_symbol_index(), which is what this empty fixture means. One line.

Why nothing caught it — the finding, which is worth more than the fix

The Rust suite was removed from CI on 2026-07-11 by operator ruling, on the grounds that a crate-wide #![allow(clippy::all)]-style zero signal was not worth ~44 minutes per run. The side effect nobody priced is that the test build stopped being compiled at all.

Every required phase — floor, regen, parse — builds the binary, and the binary compiles clean. The break lives entirely inside #[cfg(test)], so it is invisible to every required gate by construction, not by accident.

That makes the window silent and unbounded: nothing measures it, so it persists across arbitrarily many merges, and the cost lands on whichever lane next runs a local test and inherits a break it did not cause. I hit it merging main into an unrelated PR and first read it as my own merge reverting someone else's work; establishing it was main's took joining four receipts (byte-identical cli_run.rs against origin/main, the mismatch present at two separate commits by git show, my own diff confined to one unrelated file, and the production build passing while the test build failed).

What this PR deliberately does not do

It does not repair the gate. cargo check --tests would catch this whole class in a fraction of the time the 2026-07-11 ruling objected to, so that is a genuine proposal rather than a re-litigation of the ruling — but re-adding anything to the required floor is an operator decision, and the floor is mid-rung-drop with an open re-add queue. Recorded here as a named finding; the gate question is being carried to the operator separately. Not built speculatively.

Evidence — both directions, one remote dispatch, clean checkout of 450b3d3e2e

Arm Result
Treatment — with the field cargo check -p v1-compiler --lib --tests → Finished; process_resolve_store_dedupes_repeat_resolve → ok
Control — same line removed again, same dispatch error[E0063]: missing field `type_head_exposures`

The control is what makes this more than "it builds on my machine": the RED is reproduced on demand from the fixed tree.

v1 freeze admission

Argued, not assumed. This is a defect repair in the seed under the 2026-08-20 purpose test ("anything in support of v2 self host is safe"): it restores the ability to run v1's own test suite locally, which the self-host program depends on for every seed-touching change. cli_run.rs is on HAND_MAINTAINED_STAGE0_FILES, so editing the file directly is the sanctioned route rather than an emitter bypass. It adds no growth surface — one field on one existing initializer, no new row, no new capability, no semantics moved.

…nd no gate can see it

`v1_compiler_infer_env::SymbolIndex` declares `type_head_exposures`.
Every construction site sets it except one: the `SymbolIndex { .. }`
literal in `cli_run.rs`'s `process_resolve_store_tests` fixture. So
`cargo test -p v1-compiler --lib` does not compile -- E0063, missing
field -- and the whole lib test binary is unavailable, including the
hundreds of tests unrelated to the change that broke it.

The value is `rc_empty_map()`, matching `empty_symbol_index()`, which is
what this empty fixture means.

WHY NOTHING CAUGHT IT is the finding worth more than the fix. The Rust
suite was removed from CI 2026-07-11 by operator ruling, for its zero
signal at ~44 minutes. The side effect nobody priced is that the test
build stopped being COMPILED at all. Every required phase -- floor,
regen, parse -- builds the BINARY, and the binary compiles clean; the
break lives entirely inside `#[cfg(test)]`. So the window is silent and
unbounded: nothing measures it, it survives arbitrarily many merges, and
the cost lands on whichever lane next runs a local test and inherits a
break it did not cause. Attributing it took joining four receipts.

I am NOT proposing the gate repair here. `cargo check --tests` would
catch this class in a fraction of the time the 2026-07-11 ruling
objected to, so it is a real proposal rather than a re-litigation -- but
re-adding anything to the required floor is an operator decision and the
floor already has an open re-add queue. Stated as a named finding; the
gate question is being carried separately.

EVIDENCE, both directions, one remote dispatch on a clean checkout of
450b3d3: with the field, `cargo check -p v1-compiler --lib --tests`
finishes and `process_resolve_store_dedupes_repeat_resolve` passes;
removing the same line in the same dispatch returns
`error[E0063]: missing field `type_head_exposures``.

v1 freeze admission: this is a defect repair in the seed under the
2026-08-20 purpose test -- it restores the ability to run v1's own test
suite locally, which the v2 self-host program depends on for every
seed-touching change. It adds no growth surface: one field on one
existing initializer, no new row, no new capability, no semantics moved.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@gunbai-bot

gunbai-bot Bot commented Aug 24, 2026

Copy link
Copy Markdown
Contributor Author

Taking the approval; one correction to the framing, because acting on it as written would build something we already have.

The review reads the meta-concern as "no gate catches missing initializer fields in v1 hand-Rust." That is not the gap. rustc catches missing initializer fields perfectly — E0063 is exactly that check, it is total over struct literals, and it is the reason this defect is a hard compile error rather than a silent wrong value. Nothing needs to be built to detect missing fields.

The actual gap is one level up: the test build is never compiled by any required gate. Floor, regen and parse all build the binary, which compiles clean; this break lives entirely inside #[cfg(test)], where no required phase ever invokes rustc at all. So the wall exists and fires — nobody ever runs it.

The distinction decides what the repair is. Under the review's framing you would go looking for a field-completeness lens, which would be a second authority for a judgment the compiler already owns (§3). Under the actual mechanism the repair is cargo check --tests in the required run — no new check, just arranging for the existing one to execute. That is the difference between inventing a wall and enrolling one.

Not this PR's job to close either way, and I have deliberately not built it: re-adding to the required floor is an operator decision and the floor has an open re-add queue. Flagging the framing only so the finding does not propagate in the shape that sends someone to write a redundant lens.

— sent from sleek-badger-108

@gunbai-bot

gunbai-bot Bot commented Aug 24, 2026

Copy link
Copy Markdown
Contributor Author

CI is red here and this PR cannot fix it. Recording the mechanism so the red is not mistaken for this diff.

The failure

required-ci: FAILED PHASE floor refused: REQUIRED-FLOOR REFUSAL cause=RouteGapFreezeIntersection count=4

Four witness identities are simultaneously enrolled in v2.workflow.floor_route_gap's floor_route_gap_roster and path-deferred in dag/gunbc/witness_deferral_freeze.dag's frozen_path_deferrals. Both claims cannot hold of one identity, and the floor refuses the contradiction.

Why it is not this diff

This PR's entire change is one line adding a struct field to a #[cfg(test)] initializer in cli_run.rs. Nothing in it can enroll a witness, route one, or freeze a path.

Established rather than asserted: both identities I checked are already present in both rosters at this branch's base commit 450b3d3e2e — one hit each, by git show <base>:<file>. The contradiction predates the branch, so it fails on anything cut from that base.

I checked two of the four identities and one roster file per side. That is enough to establish inheritance; it is not a full derivation of the join, and should not be cited as one. The full derived route-gap roster is 110 identities across five chunk functions and spans a v2.test.* family as well as test.claim.* — two lanes computed clean joins over partial denominators today and were refused by the wall anyway.

Root cause, and it is structural

#9049 (which deleted the shell.Exec mock arms those witnesses rode on, adding the route-gap rows) and #9114 (which landed the wall refusing the intersection) merged 103 seconds apart. Each measured a clean join against a main that did not contain the other; both were correct in isolation. With no merge queue, nothing evaluates the union until CI runs on the merged result. Every PR cut afterwards is collateral.

What I am deliberately not doing

Not editing either roster to green this PR. The two dispositions — retire the freeze rows, or drop the identities from the route-gap roster — are not interchangeable: one retires a deferral, the other declares a witness unconsumed, and each records a permanent fact about whether the floor executes those witnesses. Picking one to unblock a one-line change would be resolving someone else's correctness question for my own convenience, and the innocence of this diff is exactly what makes that tempting rather than what makes it acceptable.

Status

The fix is #9133 ("Retire four stale frozen_path_deferrals rows"), which takes the disposition the floor's own message argues for. It is still open, and main at 8ab8a8e75a still carries the contradiction — so re-running CI here now would fail identically. This PR needs no change; it needs #9133 to land, then a re-run.

— sent from sleek-badger-108

@gunbai-bot

gunbai-bot Bot commented Aug 24, 2026

Copy link
Copy Markdown
Contributor Author

Correction to this PR's framing, and it strengthens the case rather than weakening it.

My body presents "no gate compiles #[cfg(test)]" as a finding. It is not new: #8941 established it two days ago, in more depth than I did, and I should have found that before writing it up. That PR named the same two composing rulings (clippy leaving CI 2026-07-08, which was the only job running --all-targets — the only flag that compiles test targets — and the suite leaving CI 2026-07-11 while remaining the first line of DESIGN's "Building & checks"), and it fixed the same class at three sites.

Credit where it belongs. What follows is the part I can actually add.

This is the same struct, breaking again

#8941's cli_run.rs fix was: the stale SymbolIndex initializer gains transparent_alias_rep. Today's fix is: that same initializer gains type_head_exposures.

Same file, same literal, different field, two days apart. #8941 measured two breaks 92 minutes apart and drew the right conclusion — "evidence of rate, not of rarity". This is the next point on that curve, and it is the first one that lands after a repair, which the earlier evidence could not show: fixing the sites did not lower the recurrence rate, because the fix was never the mechanism. The feedback loop is still missing.

What that changes about the disposition

#8941 deliberately did not re-add a gate, on the grounds that it is the operator's call and that a gate re-added over a red tree lands red. Both still hold, and this PR takes the same position for the same reasons — it is the prerequisite, not the repair.

But the recurrence retires one argument that could have been made in good faith after #8941: that the incident was a same-day cluster from one unlucky window. It was not. The interval has now stretched across two days, two separate authors, and one intervening fix.

I still make no proposal here. cargo check --tests remains the cheap candidate — it catches the class in a fraction of the time the 2026-07-11 ruling objected to, and it is not a re-litigation of that ruling since it does not run the suite. That decision belongs to the operator, with the floor's re-add queue already open, and it is being carried separately.

Unchanged

The fix itself stands: one line, rc_empty_map(), matching empty_symbol_index(). Evidence is still two-directional on a clean checkout — with the field, cargo check -p v1-compiler --lib --tests finishes and the fixture test passes; removing the same line in the same dispatch returns E0063.

Main has been merged in (360f9f0a3b) now that #9133 has landed and cleared the fleet-wide RouteGapFreezeIntersection; a bare re-run would have replayed the pinned pre-fix merge ref. I verified all four identities cleared on main — freeze rows retired, route-gap receipts kept — rather than sampling two as I did when first diagnosing it.

— sent from sleek-badger-108

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