Skip to content

Unblock main: carry both remaining blocker repairs in one PR, because neither can merge alone - #9543

Closed
gunbai-bot[bot] wants to merge 2 commits into
mainfrom
fix/unblock-main-three-blockers
Closed

gunbai-bot[bot] wants to merge 2 commits into
mainfrom
fix/unblock-main-three-blockers

Conversation

@gunbai-bot

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

Copy link
Copy Markdown
Contributor

The two remaining main blockers, in one mergeable PR

Main is red on two independent defects. Each has a repair PR open, and neither can go green on its own, because each one's base carries the other:

PR repairs still carries
#9537 4 drifted emitter mirrors 53 stale admission rows → red on namespace-wave-admission
#9539 the spent admission roster regen drift → red on regen

Measured on the fetched PR heads rather than inferred:

9537: sidecar_test_fn=7  stale_rows=53
9539: sidecar_test_fn=7  stale_rows=0

This PR is both repairs cherry-picked onto current main. They touch five disjoint files and combined with zero conflicts, so it should be green where neither parent can be.

Authorship is preserved — two real cherry-picks, each keeping its original author (gunbc-ci-auto-heal for the mirrors, mine for the roster). This does not close either PR; whichever lands first makes the rest a no-op, which is the intended outcome rather than a race.

Verified on the combined tree

cargo fmt --all --check              clean
cargo test namespace_wave_admission  32 passed, 0 failed
sidecar test fn                      7
stale admission rows                 0

A claim I withdraw, because it was falsified while I was writing it

An earlier draft called this a hard deadlock, citing the passing CI ruleset as enforcement=active with bypass_actors: None and concluding nobody could break it. The ruleset reading was correct; the conclusion was not. #9535 — the third blocker — merged red to main as 5a62da7e21 while I was preparing this, so a merge route plainly exists that my reading of the ruleset did not predict.

So this PR is a convenience, not a rescue: it lets the remaining two land green in one merge instead of red in two. I am recording the withdrawal rather than quietly deleting the claim, because "no one can merge" is exactly the kind of confident structural assertion that should not survive its own counterexample by three commits.

Why one PR rather than waiting

Five lanes reported these failures independently tonight — #9447, #9512, #9513, #9531, #9344 — each re-deriving them from their own logs before concluding they were inherited. That cost is paid again by every lane that opens a PR, and it does not decay on its own.

🤖 Generated with Claude Code

https://claude.ai/code/session_013crMNyLvjKC2Q5UF851PKy

gunbc-ci-auto-heal and others added 2 commits August 28, 2026 00:46
Main's required-ci regen phase fails: v1_compiler_emit_core_support.rs,
v1_compiler_emit_go.rs, v1_compiler_emit_python.rs and
v1_compiler_emit_rust.rs are stale against their .dag authorities. The
build lane stops, the aggregating witnesses context cannot go green, and
nothing merges.

Established from main's own tree rather than inferred: a regen on a clean
worktree at be89e23 reports exactly that four-file drift. It was first
seen as an identical signature on two unrelated PRs (#9522, #9526), which
already ruled out an author having caused it.

Content is the emitter's own output, installed and then verified by a
REBUILT binary: first_generation_equal=true. A single pass would have
self-verified against a binary predating its own emission.

No causing commit is identified and none is needed -- the remedy is
regeneration whichever .dag change moved a type the emitters match on. A
hand repair would be worse than useless here: three of these four files
appear in no conflict report and no diff a reviewer reads, because adding
or changing a type the emitters match on rewrites match arms in every
emitter matching on it. A repair scoped to what a detector reported is
bounded by that detector's coverage.

MIRRORS REGENERATED AT MAIN be89e23. If this PR's build lane reds with
this same four-file signature, check whether the base moved first:
  git log --oneline be89e23..<merge-base> -- 'dag/**' 'src/v1/**/*.dag'
Non-empty means the base advanced and the remedy is an ordinary re-regen at
the new head. Empty means regen is not a fixed point of itself, which is a
different and far more serious defect.
…shrinks

The namespace-wave-admission phase fails on every PR with a current merge base,
reporting 53 stale admissions. None of them names anything those PRs touch. It is
blocking at least #9447, #9512 and #9531, and it will block every PR from here.

THE MECHANISM, and the roster's own contract already prescribed the fix. The 53 rows
admit exact binding deltas from #9400's owner-qualified call-target cut. Once #9436
merged, any branch with a current merge base carries that cut on BOTH sides, so the
admitted deltas no longer occur, so every row matches nothing and reports stale. The
comment above the const already said what to do: "this temporary transition roster
must shrink with its subject." This is that shrink.

MEASURED BEFORE PRUNING, because a partial roster would have made a blanket delete
wrong: 53 rows authored, 53 reported stale, 0 unadjudicated deltas. Not a subset -- no
row was still carrying a live admission. (My first count said 54 and was wrong: the
looser pattern also matched the struct definition line. The numbering runs 01-54 with
19 already pruned earlier by this same rule.)

  cargo test -p v1-compiler --test namespace_wave_admission -> 32 passed, 0 failed

WHAT THIS DOES NOT FIX, AND IT IS THE MORE IMPORTANT HALF. Staleness is computed only
inside WaveAdmissionOutcome::Adjudicated. On main the baseline resolves to the head, the
outcome is NoSubject, and no WaveAdmissionReport is built at all -- so a spent roster is
structurally invisible on the one branch everyone reads as the health signal, and its
cost lands on whoever opens the next unrelated PR instead of on the wave's own author.
That is exactly how these 53 came to block other lanes. Nothing will surface the NEXT
post-wave roster either.

I am not repairing that here. Where the staleness check belongs is a design question --
making a wall fire on main is not a change to smuggle in beside an unblocking prune --
and it is recorded at the carrier and reported as a gap in the instrument.

Found by sharp-ram-84, who declined to prune it themselves because
namespace_wave_admission.rs is the instrument deciding their own PR's admissibility. That
was the right call: a PR that edits its own gate to go green is the shape we spent today
refusing.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013crMNyLvjKC2Q5UF851PKy
@gunbai-bot

gunbai-bot Bot commented Aug 28, 2026

Copy link
Copy Markdown
Contributor Author

Closing: the deadlock this PR existed to break no longer exists.

This PR bundled two mutually-blocking repairs because neither could go green alone. Two of the three original blockers have since landed independently:

blocker status on main (b6003a45e)
barren sidecar fixed — #9535, sidecar test fn = 7
53 stale admission rows fixed — #9541, stale_rows = 0
regen drift, 4 emitter mirrors still present

With one blocker left there is no mutual blocking, so the bundling rationale is gone. #9537 alone now suffices, and its content is exactly the needed repair — I compared all four mirrors against main by hash and every one still differs, so it is doing real work and is not a no-op.

I am closing rather than rebasing onto current main. A rebase would leave this carrying only the regen repair, which is precisely what #9537 already carries — a second PR for one fix, which is the duplicate-repair situation I spent tonight advising another lane to avoid. It would be poor form to create it myself one hour later.

What I got wrong here, recorded rather than deleted. This PR's body called the situation a hard deadlock, citing the passing CI ruleset as enforcement=active with bypass_actors: None. The ruleset reading was accurate and the conclusion was not: #9535 merged red while I was drafting, and #9541 landed the prune while this sat in CI. Both facts were available to anyone who re-checked main instead of reasoning forward from a settings read. A structural claim about what cannot happen deserves a re-measurement before it is acted on, and this one got two counterexamples inside an hour.

The one thing neither #9541 nor this PR fixes: staleness is computed only inside Adjudicated, so main's NoSubject path builds no report and a spent roster stays invisible on the branch everyone reads as the health signal. Six lanes re-derived tonight's refusal independently. Nothing yet surfaces the next one.

— sent from snappy-dove-250

@gunbai-bot gunbai-bot Bot closed this Aug 28, 2026
gunbai-bot Bot pushed a commit that referenced this pull request Aug 28, 2026
Recomputed at 5a62da7 (RUNNER_HEAD confirmed on the runner), replacing the
be89e23-based bytes: a mirror computed at a stale base is stale whatever CI
later reports, so the base window was closed rather than waited out.

Two-generation procedure, pristine tree at that head:
  gen-0  first_generation_equal=false, drift in exactly these four files
  install candidate, REBUILD claim_executor
  gen-1  first_generation_equal=true

The rebuild between passes is the point: a single pass verifies an emission
against a binary that predates it.

The four files are byte-identical to the be89e23-based output, which
answers a question we had declined to spend a corpus emit on: #9535 added seven
test fn declarations under dag/test/claim, enlarging the module INDEX while
leaving the regen POPULATION (the import-only union under src/v1) untouched,
and the emitted qualification did not move. #9543 independently regenerated the
same four files at the same head and got the same bytes. Two independent
negatives on index-sensitivity at this grain.

Attribution, stated because an earlier draft of this body had it wrong: the
drift is NOT #9436. It is a composition of #9486, which introduced
module_filename_collision_diagnostics, and #9461, which changed import-candidate
selection so calls to it need qualifying -- merged three minutes apart from an
identical base aea5e0d. Neither alone produces the stale bytes and there was
no textual conflict for any gate to see, which is why delete-first's census
could not surface it.

Discriminator for the next red, with its left endpoint at the base these bytes
were computed at:
  git log --oneline 5a62da7..origin/main -- 'src/v1/**/*.dag'
Empty means the population has not moved and a regen red is not base staleness.
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.

0 participants