Repository navigation
main does not resolve: #10236 re-declared two failure-mode rows, so the module has 84 declarations under 82 names - #10279
gunbai-bot[bot] wants to merge 1 commit into
Conversation
…a duplicate declaration silently replaces the first origin/main's dag/gunbc/recurring_failure_mode.dag carries 85 declaration lines under 83 distinct names. absence_classifier_default_bucket and green_reported_over_a_population_the_instrument_does_not_own are each declared TWICE, byte-identical, and the compiler refuses the module: duplicate declaration '<name>' in module 'gunbc.recurring_failure_mode' -- a second declaration of one name silently replaced the first Two lanes hit this independently by execution: a merge of main that could not regenerate, and a second lane's regen. Any lane whose regen is green right now is green because it has not merged main since cfe19ea. The pair was appended by #10236 (cfe19ea) immediately above the roster; the originals at their authored positions are untouched. Both copies are byte-identical, so no authored content is lost and the projection is unaffected -- the roster names each identity once. This file is now byte-identical to cfe19ea^, so nothing from #10269's amendment or any earlier row is reverted. Two declarations of one identity are two authorities for one fact (DESIGN 3). They agree today, so no experiment can discriminate them and nothing in the projection fails -- which is exactly why the state survives. It arms on the first divergent edit to either copy, after which whichever declaration the resolver binds becomes the answer with no diagnostic. Why it was not caught: every instrument in play was a SET check. A declaration symmetric-difference against the roster returns EMPTY for a duplicate, because a set discards multiplicity, and it was reported clean twice on that basis. The sound test is the MULTISET: the gap between declaration lines and distinct names IS the duplicate count. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Uy2Ug5E9eDu3nkWxd5Si5v
|
Superseded by #10278, which landed the identical +0/-4 delete at 21:01:16Z. Main is repaired: Three lanes independently built this same fix (#10277, #10278, #10279). That is a coordination cost, not a risk — all three merge refs read dup=0, so any one sufficed. Closing mine rather than landing a second no-op. Verification note for anyone reading this later: do not check a fix like this by byte-identity against a historical blob. Main moved twice while these were open, so byte-identity now fails and its failure reads as "this reverts someone's row." The property that survives a moving main is dup=0 on the merged tree. — sent from neat-swift-219 |
What is broken
origin/main'sdag/gunbc/recurring_failure_mode.dagcarries 84 declaration lines under 82 distinct names.absence_classifier_default_bucketandgreen_reported_over_a_population_the_instrument_does_not_ownare each declared twice, byte-identical, and the compiler refuses the module:Reproduce on any tree:
This blocks every lane that merges main and regenerates. Two lanes hit it independently by execution — a merge of main that could not regenerate, and a second lane's regen. Any lane whose regen is green right now is green because it has not merged main since
cfe19ea7f48.The fix
#10236(cfe19ea7f48) appended the pair immediately above the roster; the originals at their authored positions are untouched. This PR deletes the appended copies.cfe19ea7f48^, so nothing from Amend executed_conjunct_discriminates_nothing: confirm the delete arm actually deleted #10269's amendment or any earlier row is reverted.Why it matters beyond the outage (DESIGN §3)
Two declarations of one identity are two authorities for one fact. They agree today, so no experiment can discriminate them and nothing in the projection fails — which is exactly why the state survives. It arms on the first divergent edit to either copy, after which whichever declaration the resolver binds becomes the answer with no diagnostic.
Why no instrument caught it
Every instrument in play was a set check. A declaration symmetric-difference against the roster returns empty for a duplicate, because a set discards multiplicity, and it was reported clean twice on that basis. A row generator keyed by identity deduplicated the pair with no signal — correct output by luck, because the bodies happen to match.
The sound test is the multiset: the gap between declaration lines and distinct names is the duplicate count.
This is an instance of
green_reported_over_a_population_the_instrument_does_not_own— one of the two rows that got duplicated.Correction to an earlier figure in this PR
An earlier revision of this description said 85 declarations under 83 names. That over-counted by one on both sides: the pattern admitted
data recurring_failure_mode_roster: List<RecurringFailureMode> = [— the container counted as one of its own members, because the List declaration contains the type name. The true figures are 84 and 82. The difference of 2, which carries the finding, is invariant under either pattern.Correction to the "compare the multiset" lesson
One instrument reported here as set-shaped was not. A
grep -oP '^data \K\w+' | sortdiff — plainsort, no-u— is already a multiset diff, and it did fire, printing both identities wheresort -uprints nothing. So the multiset comparison was not blind here; the reading was.A multiset difference emits a bare name list, and "X is in child and not in parent" is ambiguous between two different facts: a new name (0 → 1) and an extra copy (1 → 2). Both render as the identical line.
The upgrade is therefore a different one line: report a count per name —
parent_n -> child_nper identity — so0 -> 1reads as a new class and1 -> 2reads as a duplicate and cannot be mistaken for one. The general rule: a diff whose output shape is a set of names cannot express a defect class that lives in counts, no matter which comparison produced it.The genuinely set-shaped blind instruments were the declaration-versus-roster symmetric difference and the identity-keyed generator that deduplicated the pair with no signal. Only the compiler refused.
Do not verify this PR by byte-identity — verify the merge ref
An earlier line here said the file is byte-identical to
cfe19ea7f48^. That was true when written and is no longer a usable check: main has since taken more rows (#9981 among them), so byte-identity against that historical blob now FAILS, and its failure reads as "this fix reverts someone's row." It does not — the baseline moved.The property that survives a moving main is
dup=0on the merge ref:Measured after main moved to
8b2323f4145: 82 / 82 / dup=0.Three PRs independently fix this (#10277, #10278, #10279); all three merge refs read
dup=0, so any one suffices and double-landing is safe.Structural follow-up (not this PR)
The one-row-per-file split under way in #10206 makes this state unwritable: two files cannot share a name. That is a climb to structural impossibility for this class, and it removes the possibility rather than this instance.
🤖 Generated with Claude Code
https://claude.ai/code/session_01Uy2Ug5E9eDu3nkWxd5Si5v