Repository navigation
Pin the sort that main's deduplicate_identities repair depends on for correctness - #10012
Conversation
…er the corpus census DESIGN §6's bare-minimum-cost standing rule: a proven cost-shape defect — a copied accumulator, a quadratic fold — is always fixed, regardless of the realized n. This is both, in one expression, over a population that is not small and does not stay fixed. WHAT IT WAS. `deduplicate_identities` decided membership by rescanning the survivors it had already kept (`any(unique, prior => prior == identity)`) and extended them by copying them (`concat(unique, [identity])`). Both halves are O(n) per element, so the fold is quadratic in comparisons AND quadratic in list construction. WHY n IS NOT SMALL HERE. The argument is a whole-corpus rustc diagnostic census: the two persisted receipts carry `genesis_5006ddc6_raw_error_diagnostic_identities` and `receipt_1_eced9d24_raw_error_diagnostic_identities`, and the population is the emitted compiler's error identities — it grows as the self-host corpus grows. It is reached from `canonical_identity_set` (every phase row, every unplaced/codeless partition) and from `census_population_is_duplicate_free` (twice per receipt in `receipt_population_coherent`), so the whole series ratchet pays it repeatedly. WHAT IT IS NOW. One traversal carrying the two questions it was conflating in one list: `seen` (a `Set<String>`) answers "has this identity been kept", `unique` answers "in what order". The `std.graph` DFS accumulators are the same shape for the same reason. BEHAVIOUR IS UNCHANGED, deliberately and at both grains that are observable. Order: an identity is appended exactly at its first occurrence, as before — and every caller except `census_population_is_duplicate_free` sorts afterwards through `canonical_identity_set`, which only counts. Multiplicity: the function still collapses repeats rather than refusing them, so the duplicate WALL stays exactly where it was — upstream, in `census_population_is_duplicate_free` and `receipt_population_coherent`, which is the distinction `a_duplicated_census_population_is_not_silently_collapsed_to_a_set` exists to hold. EXECUTED EVIDENCE, not a typecheck. Eight enrolled witnesses over the real persisted series and the duplicate walls pass on this tree, run individually through `gunbc run --claim-run --source-root dag --source-root src/v2 --entry dag/test/claim/self_host_compile_phase_frontier_witness_test.dag --function <claim>`: `a_repeated_population_entry_is_refused_rather_than_collapsed`, `a_duplicated_census_population_is_not_silently_collapsed_to_a_set`, `a_receipt_whose_refused_population_repeats_a_path_is_refused_by_coherence`, `the_same_receipt_without_the_repeat_is_coherent`, `current_persisted_compile_phase_frontier_holds`, `the_census_digest_is_invariant_under_reordering_of_either_population`, `the_persisted_genesis_census_is_fully_attributed_to_its_phases`, `phase_boards_are_invariant_under_population_reordering`. The first four are the discriminating REDs for this change: collapsing a duplicated population, or refusing one that is clean, reddens them. THIS IS A COST-SHAPE REPAIR, NOT A FLOOR UNBLOCK. It is landed on its own terms because §6 requires it, not to buy headroom for any claim. The separate question — that ten required-floor claims each recompute the whole series ratchet, which is authored duplication under §2 and wants one provider at their least common ancestor — is a restructure and lands separately, so its design review does not hold this defect hostage. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01W377Pq4Tp5eQjGBXtPQEoM
Set equality is not the acceptance test for a set-backed deduplicate. A dedup that returns the SET's ordering produces the same MEMBERS in a different ORDER: identical for a caller that counts, byte-drifting for a caller that renders. The previous commit asserted order was preserved; this one proves it, and proves the assertion can fail. THE FIXTURE IS CHOSEN SO THE TWO ANSWERS CANNOT COINCIDE. First-occurrence order over ["b", "a", "b", "c", "a"] is ["b", "a", "c"]; every sorted or set-ordered spelling answers ["a", "b", "c"]. A population that happened to arrive already sorted would let an ordered-equality assertion pass while discriminating nothing, which is why the subject is authored rather than read off the live series. THE MUTANT IS THE WHOLE CHECK, and it was run rather than described. Planting `|> sort_by(identity => identity)` on the accumulator's result — the set-ordered variant — turns this arm RED, and turns RED a probe comparing the new function against the PRE-CHANGE algorithm over both live populations (`genesis_5006ddc6_raw_error_diagnostic_identities`, `receipt_1_eced9d24_raw_error_diagnostic_identities`). Unmutated, both pass: the new function answers the old function's exact ordered sequence on the real corpus census, not merely its member set. Four arms, in one run: A pass, B pass, C fail, C2 fail. WHERE THE ORDER CAN AND CANNOT ESCAPE. `deduplicate_identities` has exactly two consumers in the corpus: `canonical_identity_set`, which sorts the result and therefore erases order, and `census_population_is_duplicate_free`, which counts it. The projection consumer — `gunbc.design_ledgers compile_phase_frontier_blocks`, which renders committed bytes into docs/design-ledgers.md — reaches it only through the sorting one, and that order-independence is separately executed by `the_census_digest_is_invariant_under_reordering_of_either_population` and `phase_boards_are_invariant_under_population_reordering`, both passing here. So the projected bytes cannot move under this change by construction, which is a stronger statement than one byte comparison over one population would have been. The arm is therefore not a belt on either current caller. It holds the contract for the NEXT consumer — the one that would not survive a silent reordering, and that would discover it as generated-artifact drift at a distance from the diff that caused it. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01W377Pq4Tp5eQjGBXtPQEoM
… two compiler_tests failures on this PR's run are main's, inherited through a merge ref pinned at 06:47Z against bb96afa The merge ref CI built for f3839b2 was pinned at push time against main as it then stood (bb96afa), which carried #9886's stale mirror. That is the exact subject #10017 repaired at 06:56Z, nine minutes after this PR's run started. Evidence, checked rather than assumed: - main at bb96afa, run 33596615712: 'test result: FAILED. 642 passed; 2 failed' with render_rust_applied_type_routes_qualified_base_through_leaf_name and shell_service_unmodeled_output_key_refuses -- byte-identical counts and identities to this PR's rust-unit-tests failure. - #10017's own body names 'regen FAIL generated surface drift: compiler_tests.rs, std_realization_schedule.rs' reproduced on 4059156 and bb96afa -- the same two files this PR's build job reported. - This branch changes two .dag files and no Rust: git diff origin/main...HEAD over compiler_tests.rs and std_realization_schedule.rs is empty. Merging rather than rebasing per the squash-merge policy, and pushing after main's last move so the merge ref is recomputed against the repaired base. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01W377Pq4Tp5eQjGBXtPQEoM
Floor: FloorClean — and the attempt count, because the green is a mitigation and not a verdict
This was the third floor attempt on this head, and I am reporting that rather than presenting the green on its own. The two prior attempts on the identical commit were
Attempt 1's victim passed in attempt 2 at 479ms. Four unrelated families. None of those rows is reachable from this diff. The measurement this PR is actually about
cpu_ms against the 500ms ceiling, three independent clean runs. The quadratic repair moves both claims down and the direction is consistent. It does not clear the tip, and this PR does not claim to. The population over the 100ms line on the green run is 267 further rows beyond the 25 printed; the band sits well inside contention range of the ceiling, and the victim rotates with host load rather than with the tree. That is the standing drop's subject, not this PR's. Remaining red
— sent from stern-lark-508 |
…control it is missing main landed a competing repair for the same defect while this PR waited for a floor slot (`|> sort_by` then compare against the last kept element). Mine used a Set for membership and `list_push` for the accumulator. Both fix the quadratic scan; I argued mine additionally fixed a copied accumulator, and I measured that argument rather than asserting it. THE MEASUREMENT REFUTES MY PATCH. Three variants over an amplified probe — ~720 identities with every one duplicated, ten dedup passes per run, two rounds, same host: mine 112.1s / 110.3s, main's 110.6s / 127.3s, main's-with-`list_push` 110.9s / 137.6s. Round one spans 1.5% and round two is dominated by host noise in the wrong direction. `concat(unique, [identity])` is not a measurable O(n) copy in this interpreter, so §6's bare-minimum-cost argument does not distinguish the two implementations. Forking a landed fix on an unmeasured preference is the thing this project keeps catching, so main's implementation is taken whole and `dag/gunbc/self_host_compile_phase_frontier.dag` is byte-identical to `origin/main` in this commit. WHAT SURVIVES IS A GAP IN THAT FIX, NOT A COMPETING ONE. main's fold compares only against the LAST KEPT element, which is sound exactly because the input was sorted first — equal identities are adjacent. The sort is therefore a correctness precondition, not a presentation choice, and nothing in the corpus pins it: delete `|> sort_by(identity => identity)` and the fold silently stops collapsing duplicates separated by any other identity while still collapsing adjacent ones. MEASURED, NOT ASSERTED, INCLUDING THE PART THAT DECIDES WHETHER IT IS WORTH LANDING: A new witness against main's implementation PASS B same witness, `sort_by` deleted FAIL C the four pre-existing duplicate-wall witnesses vs that mutant ALL PASS C is the finding. `a_repeated_population_entry_is_refused_rather_than_collapsed`, `a_duplicated_census_population_is_not_silently_collapsed_to_a_set`, `a_receipt_whose_refused_population_repeats_a_path_is_refused_by_coherence` and `current_persisted_compile_phase_frontier_holds` all stay green with the sort removed, so this arm is the only thing in the corpus that reddens on it. THE SUBJECT IS CHOSEN SO THE MUTANT CANNOT PASS: `["b", "a", "b"]` holds a duplicate that is not adjacent in encounter order — 2 under the sorted fold, 3 without the sort — and the all-adjacent cases sit beside it as the positive control the mutant still passes, which is what makes the first arm discriminating rather than the set being merely two examples. IT ASSERTS DUPLICATE SURVIVAL RATHER THAN OUTPUT ORDER, deliberately. Encounter order is unobservable through both consumers (`canonical_identity_set` sorts its result, `census_population_is_duplicate_free` reads only the count), so an order assertion would pin a property no caller can distinguish — and the previous revision of this branch did exactly that, which main's change would have reddened. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01W377Pq4Tp5eQjGBXtPQEoM
…he sort control No conflicts — the collapse touches compile_phase_frontier_standing and its consumers, while the dedup repair and its new control sit in a different region of the same module. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01W377Pq4Tp5eQjGBXtPQEoM
New head
|
| claim | cpu_ms |
|---|---|
current_persisted_compile_phase_frontier_holds |
433 |
the_published_frontier_standing_… |
424 |
These are not evidence for this PR and are recorded only so the series stays continuous. This head carries main's deduplicate_identities, not mine, so both figures are properties of main's repair plus this run's host load — they sit between the 404/395 and 446/437 already observed on other clean runs of the same implementation family, which is the band moving, not a change in the code.
On the earlier rust-unit-tests red
shell_service_unmodeled_output_key_refuses was repaired on main by another lane (green across five SHAs from 10:54Z), and this head already carries that base — baf6a7ce is an ancestor of 48bf811a. So the red that stood on the previous head was an artifact of a merge ref pinned before the fix, and no re-run was needed to clear it: the rewrite that resolved the merge conflict picked up the repair as a side effect.
— sent from stern-lark-508
What happened, and why my patch is not here
While this PR waited for a floor slot, main landed its own repair of the same quadratic dedup — sort first, then compare against the last kept element. Mine used a
Setfor membership andlist_pushfor the accumulator. Both fix the quadratic scan; I argued mine additionally fixed a copied accumulator (concat(unique, [identity])in a fold), which is the §6 bare-minimum-cost shape.I measured that argument instead of asserting it, and it did not survive. Three variants over an amplified probe — ~720 identities with every one duplicated, ten dedup passes per run, two rounds, same host:
Set+list_push)last+concat)list_pushRound 1 spans 1.5%; round 2 is dominated by host noise in the wrong direction.
concat(unique, [identity])is not a measurable O(n) copy in this interpreter, so §6 does not distinguish the two implementations — and forking a landed fix on an unmeasured preference is exactly what this project keeps catching. main's implementation is taken whole:dag/gunbc/self_host_compile_phase_frontier.dagis byte-identical toorigin/mainon this branch.What survives is a gap in that fix, not a competing one
main's fold compares only against the last kept element. That is sound exactly because the input was sorted first, so equal identities are adjacent. The sort is a correctness precondition, not a presentation choice — and nothing in the corpus pins it. Delete
|> sort_by(identity => identity)and the fold silently stops collapsing duplicates separated by any other identity, while still collapsing adjacent ones: a dedup that works on the easy inputs and quietly fails on the real ones.Measured, including the part that decides whether it is worth landing
C is the finding.
a_repeated_population_entry_is_refused_rather_than_collapsed,a_duplicated_census_population_is_not_silently_collapsed_to_a_set,a_receipt_whose_refused_population_repeats_a_path_is_refused_by_coherenceandcurrent_persisted_compile_phase_frontier_holdsall stay green with the sort removed. This arm is the only thing in the corpus that reddens on it.The subject is chosen so the mutant cannot pass:
["b", "a", "b"]holds a duplicate that is not adjacent in encounter order — 2 elements under the sorted fold, 3 without the sort — with the all-adjacent cases beside it as the positive control the mutant still passes. That pairing is what makes the first arm discriminating rather than the set being merely two examples.It asserts duplicate survival rather than output order, deliberately. Encounter order is unobservable through both consumers —
canonical_identity_setsorts its result andcensus_population_is_duplicate_freereads only the count — so an order assertion would pin a property no caller can distinguish. The previous revision of this branch asserted exactly that, and main's change would have reddened it.Floor
The prior head's floor reading is recorded in the comment above, including that it was the third attempt and therefore a counted retry-until-green mitigation under
gunbc.rung_dropfloor_cost_contention_verdict, not a verdict about the tree. This head needs its own run.🤖 Generated with Claude Code
https://claude.ai/code/session_01W377Pq4Tp5eQjGBXtPQEoM