This repository was archived by the owner on Aug 11, 2026. It is now read-only.
retire benchmark v2-v6 behind a data-layer version floor - #560
Merged
Conversation
Peyton-Spencer
marked this pull request as ready for review
July 28, 2026 20:01
Peyton-Spencer
force-pushed
the
sec/close-bench-v6
branch
from
July 28, 2026 21:50
c85f386 to
7684235
Compare
Peyton-Spencer
changed the base branch from
main
to
task/ditto-platform-561-20260728-163127
July 28, 2026 21:50
This comment has been minimized.
This comment has been minimized.
v2-v5 were already unreachable through three forward-only application guards. v6 was unreachable only by configuration, and two paths could still reach it: * `_issue_source_backfill_ticket` looked up an existing unexpired `from_version` lease and re-issued it BEFORE reaching its own retired-era gate, and `request_job` deliberately resurrects the activated v7 rollout to feed that lane -- whose `from_version` is 6. So a v6 lease renewed itself with no rollout open and no flag set, and the score at the end was validated against `ticket.bench_version` rather than the active version. * `allow_retired_era_backfill` was an MCP-exposed runtime setting whose own docstring advertised that flipping it restored retired-era admission "without a deploy". One Backroom write re-opened v6. Put the bound in the schema instead, where no code path can be on the wrong side of it: * `scores`, `confirmation_scores` and `benchmark_rollouts` get `>= 7` CHECK constraints, added NOT VALID so the historical v2-v6 rows stay present, readable and byte-identical while every new INSERT and UPDATE is refused. This closes the era; it is not a purge. * `validator_tickets` gets a trigger rather than a CHECK, because a CHECK cannot see a transition. It refuses an insert and refuses flipping a sub-v7 ticket back to `issued`, while still permitting the `issued -> expired/scored` drain an in-flight lease needs. The re-lease arm is load-bearing: re-issuing is an UPDATE, so an INSERT-only guard would have left `replace_validator_score_after_infrastructure_failure` (which had no era check at all) and the score-retest queue able to hand a retired era another 90-minute lease from Backroom. Remove `allow_retired_era_backfill` entirely; `extra="forbid"` means a stale writer that still sends it is rejected rather than ignored. Gate the source-backfill resume on the floor and on the active version. Refuse a retired-era score with 410 + code 4002 -- terminal, and deliberately not `infrastructure`, which is no-fault and would re-lease forever. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
`prev_gen_carryover` is stored WHOLE and its model is `extra="forbid"`, so removing `allow_retired_era_backfill` is not free: any revision already in the table still carries it. `settings_from_row` fails open onto the shipped defaults, so this would not have crashed. It would have done something quieter and worse -- silently reverting the operator's ENTIRE policy (cohort sizes, lane cycle, owner limits) behind a single log line, because one dead key failed validation for the whole document. Drop retired keys before validating. Read-side only: the write path stays strict, so an operator or a stale client that still SENDS the key is rejected rather than quietly ignored. Forgiving a stored document is not the same as accepting a new instruction. Production is not exposed today -- `queue_policy_settings_revisions` is empty, so there is no stored revision to misread -- but staging may have one, and the next field removal would hit this again. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
`fetch_dataset` defaulted `bench_version` to 2 and the dataset-reveal
endpoint omitted it, so `GET /agent/{id}/dataset` served the v2 dataset
for every finalized agent regardless of the benchmark it was scored
under. Nothing failed: a v2 dataset is a well-formed artifact with a
matching hash, it just is not that agent's, so anyone re-grading a
post-v2 submission from the reveal was grading against the wrong answer
key.
The agent's own scores are the authority on which era finalized it, so
the endpoint takes the newest of them and passes it explicitly. A
submission that reaches this endpoint is finalized and therefore has
scores; the active era is the fallback rather than an assumption.
`bench_version` loses its default on `generate` and `fetch_dataset` in
the process. A default here is what let the omission go unnoticed, and
every other caller already passes one.
Independent of the benchmark-version floor that surfaced it, and
cherry-pickable on its own.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The floor makes v2-v6 unwritable, and the test corpus used those versions as generic fixture values -- overwhelmingly because `Score.bench_version` defaulted to 2, not because the tests were about retired eras. 563 tests broke on data, not on behaviour. Most are renumbered to the active era. Where a test's SUBJECT is the retired era -- rollout history, the legacy report path, retired-era public pages -- it seeds through `ditto/tests/legacy_era.py`, which lifts the floor, writes the pre-floor rows and restores it, reproducing production's grandfathered state rather than pretending the rows are new. Two structural facts forced most of the helper use, and neither is a shortcut: shipped contracts stop at v7 while the floor is 7, so v7 is the only era that is both leasable and writable; and every rollout must move FORWARD into a shipped contract, which puts its source era below the floor by construction. A handful of tests asserted behaviour the floor makes impossible and were inverted, not deleted, with docstrings recording what changed. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Idempotent era seeding (the transition index is UNIQUE and several fixtures legitimately want the same era), a capable-heartbeat pool and slot_id on the consensus job claims, and the retired-era rows seeded beneath a lifted floor. test_activated_v7_retires_v6 loses its second half: it turned the removed setting on and asserted the whole v6 contract came back. What survives is the half that is still true, and now permanently. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The [3, 4] axis over active versions no longer exists: shipped contracts stop at v7 and the floor is 7, so exactly one era is both leasable and writable. Moving the parameter to the RETIRED side keeps two cases exercising both halves of the boundary, which is what the test was about. Also makes the era and dataset seeding idempotent -- (from_version, desired_version) and (agent_id, bench_version) are both UNIQUE, and more than one fixture legitimately wants the same active era. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Unlike the retired-ticket test, this one cannot move its parameter to
the retired side: the accepting case has to WRITE a score, and only v7
is writable.
The version-less case also changed answer, and the test now says so. It
used to 409 ("no open scoring ticket") because it looked for a v2 lease
and found none; it now 410s before the lookup, because pinning to
LEGACY_BENCH_VERSION means pinning to a retired era, and that is
terminal rather than a conflict.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…ive era Both activated v3 and then leased it, which the ticket floor refuses. Neither test is about v3; the version was an arbitrary post-legacy value. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Three tests were still describing a world with more than one writable benchmark. ``_seed_top5_emission_set`` wrote its own activation row. ``(from_version, desired_version)`` is UNIQUE and the classes that lease work already install that transition from an autouse fixture, so any test that wanted both got a duplicate-key error instead of an era. It now records the activation through ``_seed_activated_era``, the one idempotent writer, so the two compose. ``test_after_activation_new_submission_finalizes_on_three_scores`` seeded protocol-8 heartbeats advertising v2 and v3. Both halves are dead under the floor -- a validator offering only retired eras is offered nothing, and protocol 8 cannot take a v7 lease regardless -- so it leases through the same capable pool as the rest of the class. The behaviour under test, finalize on the third score, is untouched. ``_v8_capabilities`` had no other caller and goes with it. ``test_after_activation_v2_only_expires_v2_and_stays_idle`` is renamed to ``test_retired_era_only_validator_is_permanently_unserviceable``. It was written as a statement about a moment: v2 had just been superseded and a v2-only validator was idle until it upgraded. The floor makes it a statement about the system -- nothing can ever hand this validator work again -- so the test now asserts the whole terminal arc: offered nothing, leases nothing, drains the grandfathered lease it holds, stays idle with nothing left to drain. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Six mypy errors and one formatting deviation, none of them caught before the rebase onto current main. `create_rollout_snapshot` lost its `from_version` default on this branch, and two callers still relied on it. Both seed the inherited pre-floor era, which is what the old default of 2 meant, so both now say so explicitly. `_stored` in the retired-keys test built a `SimpleNamespace` where `settings_from_row` wants a `QueuePolicySettingsRevision`; it builds the real row type now, so the signature cannot drift unnoticed. `_seed_ticket`'s context variable needed an annotation to hold either the floor-lifting manager or a `nullcontext`. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…king A test that fails while holding an open transaction can leave its connection CHECKED OUT. `engine.dispose()` returns pooled connections but cannot force-close one the application never handed back, so the locks that connection holds outlive the test that took them. The next test's TRUNCATE then blocks on those locks forever. The failure mode is the worst shape available: the run hangs with no failing test to point at, no traceback, and nothing in the output but a stalled progress bar. It is invisible while the suite is green, because it takes a failing test to arm it -- which is why it has sat here undetected. The reset now evicts any backend still attached to the database before it truncates. Each xdist worker owns its database exclusively, so anything else connected at reset time is by definition a straggler from a previous test. A ten-second `lock_timeout` backs that up: if something still cannot be evicted, the reset raises and names itself instead of waiting forever. Measured on a file with 17 failing tests: previously hung indefinitely, now completes in 6 seconds and reports all 17. Independent of the benchmark-version floor and cherry-pickable on its own -- it makes the suite fail-safe rather than fail-deadlock whatever else lands. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…e era Six tests arrived on main (heartbeat v17, the widened failure_detail, the never-disclose release policy) after this branch's fixture migration was written. They merged textually clean and were semantically stale: each was authored against the old `bench_version` default of 2. Five looked up a `ValidatorTicket` by primary key with a hardcoded 2 while the fixture now seats the lease on the live era, or asserted a lease roster of `[2, 3]` for it. The era is incidental to what all five are about -- detail length, roster membership -- so they move to the live era. The roster keeps its second lease on v3 deliberately: one live lease and one grandfathered lease still draining is the shape that proves the roster reports a version per lease rather than one for the fleet. The sixth was not an era problem at all. `_seed_agent` now supplies a verified screened image by default, which silently moved the never-disclose test off the source tarball whose key it asserts and onto the image path. It asks for no screened image explicitly now, so it keeps testing the artifact its docstring names. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The floor changed `active_bench_version`'s no-activation-on-record fallback from `DEFAULT_BENCH_VERSION` (2) to the floor itself, because a ledger with no durable authority decision that answers 2 makes `request_job` cut a v2 lease the ticket trigger then refuses -- a 500 on the job-claim path where a 204 belongs. Every remaining failure was that fallback, not a retired-era rejection. Two shapes, one rule: where the era is incidental, move to the floor; where the era is the subject, keep it retired and say so. Floor-era reads (4). Empty-database status and discovery reads whose subject was never the era -- a status read starts nothing, discovery is authenticated and read-only, config shape and defaults. They now assert the floor. `available_target_versions` changes shape rather than value: a target must be both above the active version and at or above the floor, and with v7 the newest shipped contract those meet at the one era already in force, so the console offers nothing. It becomes [8] when the v8 contract lands. An era genuinely in force (22). These exercise a TRANSITION, and a transition needs a source era below its target. A rollout must move forward, the newest shipped contract is v7 and the floor is 7, so the source era of any rollout that can still be built lies beneath the floor -- which is why these fixtures already seeded their v2/v6 ledgers through `retired_era_writes_allowed`. What was missing was the ACTIVATION that made that era authoritative. Without it source and target collapse onto one era and the assertion quietly voids itself: `test_open_v7_rollout_keeps_active_ v6_harness_authoritative` was asserting 7 == 7, and `test_control_degrades_the_slow_section_instead_of_hanging` had no candidate target left for its deliberately-hanging stub to be called about, so it passed without ever running the code it names. `grandfather_active_era` writes that activation as the grandfathered pre-floor rollout row production still holds -- the floor is NOT VALID precisely so activation history survives it. Re-point every caller at a 7 -> 8 transition once the v8 contract ships (ditto-platform#513); only then does a legal forward target exist above the floor.
Peyton-Spencer
force-pushed
the
sec/close-bench-v6
branch
from
July 29, 2026 01:21
7684235 to
2562c0b
Compare
Peyton-Spencer
force-pushed
the
sec/close-bench-v6
branch
from
July 29, 2026 02:15
2562c0b to
8e886ad
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to subscribe to this conversation on GitHub.
Already have an account?
Sign in.
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Benchmark versions below 7 are retired. This makes them unscoreable by construction rather than by configuration: the bound moves into the schema, where no application path can be on the wrong side of it.
v2-v5 were already unreachable through three forward-only guards (
benchmark_rollout.py,admin_benchmark_rollout.py, and thebenchmark_rollout_forwardCHECK). v6 was unreachable only by configuration, and two paths could still reach it:_issue_source_backfill_ticketlooked up an existing unexpiredfrom_versionlease and re-issued it above its own retired-era gate — andrequest_jobdeliberately resurrects the activated v7 rollout to feed that lane, whosefrom_versionis6. So a v6 lease renewed itself with no rollout open and no flag set, and the score at the end was validated againstticket.bench_versionrather than the active version.allow_retired_era_backfill. An MCP-exposed runtime setting whose own docstring advertised that flipping it restored retired-era admission "without a deploy". One Backroom write re-opened v6.Both were verified present on
mainbefore this change. Production at the time of writing: active v7, no rollout open,queue_policy_settings_revisionsempty (the setting had never been written), and zeroissuedsub-v7 tickets — the newest sub-v7 deadline passed 2026-07-26.Merge ordering — Backroom has already landed; this closes the pair
ditto-assistant/backroom#103 merged as
632f8ccon 2026-07-28 19:26 UTC, ahead of this PR. That was the safe half of the pair, and the ordering constraint is now discharged rather than pending. Recorded here because both bodies were written while it was still an open draft and said "must land simultaneously".The constraint was real but asymmetric, and it landed on the harmless side:
extra="forbid"and Backroom sends the carryover block whole on every write, so a Backroom still carryingallow_retired_era_backfillwould have madeset_queue_policy_settingsreturn 422 for every field, not just this one. Operator queue-policy settings would have broken fleet-wide.main'sallow_retired_era_backfill: bool = Falsecarries a default, so a payload omitting it validates and the platform fills it in —extra="forbid"rejects unknown keys, not absent defaulted ones. On the read path Backroom'sprevGenCarryoverSchemais a plainz.object, not.strict(), so Zod strips the field the platform still returns. No 422, no read failure, nothing operator-visible in the gap.So the live state right now is a Backroom that has stopped sending a switch the platform still has and still ships
False. Merging this PR removes the switch itself and closes the pair.Remaining order:
dittobench-apiandditto-subnet(v2-v6-only infrastructure, safe to delete only once nothing can score a sub-v7 benchmark)Step 2 is already in flight and blocked on this PR — those removals are being prepared separately and must not merge before the floor lands, since until it does a sub-v7 benchmark is still theoretically scoreable and the infrastructure they delete is what would score it.
The migration
alembic/versions/2026_07_28_enforce_bench_version_floor.py(d4b8e6c1a205)scores,confirmation_scores→bench_version >= 7;benchmark_rollouts→desired_version >= 7. AllNOT VALID: Postgres skips the existing-row scan but enforces on every INSERT and UPDATE, so the 1,685 historical sub-v7scoresrows and 106confirmation_scoresrows stay present, readable and byte-identical. This closes the era to new writes; it is not a purge. Skipping the scan is also what keeps the migration metadata-only.validator_tickets→ a trigger, not a CHECK, because a CHECK sees only the resulting row and never the transition. It would have refused theissued -> expireddrain and stranded every in-flight v6 lease inissuedwith no way to close it.The trigger predicate — please do not "simplify" this
The obvious predicate is wrong, and it was my first attempt:
issue_ticket's reuse branch (tickets.py:672-687) writesstatus = ISSUEDover a row that is alreadyISSUEDand simply pushesdeadlineout. That is exactly how the v6 lease renewed itself in production — the status never changed, so there was no transition for a status-watching guard to see. That predicate would have reproduced the original hole inside the schema, while looking like a fix.The shipped predicate keys on the lease moving, not on the status changing:
Still permitted, so an in-flight lease can drain:
issued -> expired/scored, the overdue sweep'sretry_after,force_expire_lease(moves the deadline backwards), andfailure_reason/first_reported_attouches. Regression-tested both ways inditto/tests/db/test_bench_version_floor.py.Application
allow_retired_era_backfillremoved entirely.extra="forbid"means a stale writer that still sends the key is rejected, not silently ignored._issue_source_backfill_ticketgates on the floor and the active version, above the resume lookup. The lane's legitimate use — keeping a source-version validator busy while a rollout is open, wherefrom_versionis the active version — is preserved.MIN_SCOREABLE_BENCH_VERSION = 7.upsert_scorenow requires an explicitbench_version(the= 2default is gone);Score.bench_versionmodel default 2 → 7; the simulator no longer seeds v2.active_bench_version's no-activation-on-record fallback is now the floor, notDEFAULT_BENCH_VERSION. Worth calling out because it is the one behavioural change here that a reader would not predict from "close v2–v6", and it accounts for every remaining test failure this branch had. A ledger with no durable authority decision — a fresh deployment, or one restored without its rollout history — used to answer 2.request_jobtakes that at face value, cuts a v2 lease, and the ticket trigger refuses the insert: an unhandledIntegrityErrorsurfacing as a 500 on the job-claim path where a 204 belongs.DEFAULT_BENCH_VERSIONstays 2 — it is a statement about where this subnet began, frozen for the same reasonLEGACY_BENCH_VERSIONis — it simply stopped being a usable answer to "what era are we scoring".🐛 Live user-facing bug: the public dataset reveal served the wrong dataset
Independently shippable — if this PR stalls in review, cherry-pick
fix(public): reveal the dataset the agent actually ranfirst. Four files:endpoints/public.py,datapipeline/client.py, and the two test modules that stub or exercise the generator (datapipeline/test_client.py,endpoints/test_public.py). Both stubs had afetch_datasetthat predates the requiredbench_version, so the commit does not stand up without them.Verified by applying the commit to a clean
mainworktree and running the affected tests — 178 passed. An earlier draft of this section claimed independence on the strength ofgit apply --checkalone; applying cleanly and passing are different claims, and only the second one is worth making.GET /api/v1/public/agent/{id}/datasetreveals the benchmark dataset a finalized submission actually ran.public.py:4299calledgenerator.fetch_dataset(seed, run_size)and omittedbench_version, which defaulted to 2.Symptom, in plain terms: every miner who opened the dataset reveal for a finalized agent was shown the v2 dataset, no matter which benchmark their agent actually ran. It never errored and never looked wrong, because a v2 dataset is a perfectly well-formed artifact — it just is not that agent's. Anyone who used the reveal to check their scoring inputs was reading the wrong file.
Pre-existing and unrelated to the floor; the floor is only what made it visible, by turning a silently-wrong default into an unreachable one. Fixed by deriving the era from the agent's own scores (the authority on which benchmark finalized it), falling back to the active era rather than assuming.
Worth a deliberate yes/no before merge rather than discovering it later.
Every rollout targeting v7 has
from_version = 6. Because the previous-generation carryover lane operates on the source era, and 6 is now beneath the floor, that lane is inert for the v6→v7 transition specifically. It reopens normally at 7→8, when the source era will be 7.This reads as correct — carryover exists to hand the new era a populated leaderboard, and it cannot do that with scores the ledger will not accept. But it was not the goal of this change, so it should be an explicit decision. If anyone disagrees, now is the time.
Related and worth stating plainly:
_CONTRACTScovers v2–v7 and the floor is 7, so exactly one era is currently both leasable and writable. Shipping the v8 contract (ditto-platform#513) is a hard prerequisite for the next rollout —start_rollouthas no legal target until it exists.Additional admission paths found and closed
Beyond the two above, an audit turned up three more:
replace_validator_score_after_infrastructure_failurehad no era check of any kind. Independent of both known paths, and reachable straight from Backroom: it re-leases a ticket by UPDATE, so it never touched the insert path, and unlikereinstatement_gatenext door it never asked what era it was operating in. It would hand a v6 agent another 90-minute lease on request. Now refused by the trigger, plus a 409 so an operator gets a reason rather than a 500.activate_next_score_retestgated only on what the validator advertises it can run. That is a capability check, not a policy floor — it kept retired eras out only incidentally, because no modern validator advertises v6. Now closes the request as unserviceable.start_rollouthad no floor guard — an operator naming a retired target got a rawCheckViolationErroras a 500, with a constraint name as the only explanation, where the forward-only guard beside it answers 409. Now a 409 naming both versions; andavailable_target_versionsno longer offers retired targets, so the console cannot suggest what the start path will refuse.upsert_score(bench_version=2),Score.bench_versiondefault 2, simulatorDEFAULT_BENCH_VERSION = 2) — each now a guaranteed constraint violation rather than a silent wrong write.In-flight leases: terminal, and structurally unable to loop
Left alone rather than force-closed. A live lease reaches a terminal state either by the validator reporting the 410 through
fail_job(scoring_error— consumes the attempt, mints no grant) or by the overdue sweep expiring it at its own deadline, ≤90 minutes out.410 rather than 409 is deliberate: a conflict invites a retry, and this must never be handed back as
fail_job(reason="infrastructure"). Infrastructure is the no-fault class — it mints a compensating grant, raises the attempt cap and re-leases — so a condition that never clears would re-lease forever. That is the ditto-subnet#279 shape, where twelve deadmnemo*leases burned ~4.5 validator-hours apiece because a terminal fault was reported as retryable infrastructure.The belt-and-braces is what makes this safe by construction rather than by policy: even a misclassified
infrastructurereport cannot loop, because the reissue it asks for must put a sub-v7 ticket back intoissued, and the trigger refuses it. The lease dies either way. Correct classification is the fast path; the schema is the guarantee.Silent-reset hazard on stored revisions
prev_gen_carryoveris stored whole and the model isextra="forbid", so removing a field is not free — revisions already in the table still carry it.settings_from_rowfails open, so this would not have crashed; it would have done something quieter and worse: silently reverting the operator's entire policy (cohort sizes, lane cycle, owner limits) to defaults behind one log line, because one dead key failed validation for the whole document.Retired keys are now stripped before validation. Read-side only — the write path stays strict, so a stale client that still sends the key is rejected rather than quietly ignored. Forgiving a stored document is not the same as accepting a new instruction.
Reversibility
Fully reversible.
downgrade()drops four constraints, the trigger and its function; nothing is dropped, rewritten or backfilled on the way up, so nothing needs restoring. Verified against a local database: downgrade leaves all rows intact and v6 writes work again afterwards; re-upgrade is clean.Test plan
Verified against a real Postgres, seeded with sub-v7 rows at the previous head so the migration meets production's grandfathered state:
pg_constraint.convalidated = falseproves it never scanned.issued -> expired) and can still recordfailure_reason; renewal (refreshedissued_at+ deadline pushed) rejected; re-lease of a drained ticket rejected; new v6 ticket rejected.PrevGenCarryoverSettings(allow_retired_era_backfill=True)raisesValidationError.ruff check,ruff format --check,mypy ditto/clean.pytest— 2541 passed, 0 failed, 95s, rebased ontomain@db7cb53.Suite status, stated plainly
The bar is that
mainis green, and both sides were measured on the same tree, not inferred:main@db7cb53The twenty-test difference is this branch's own — the migration and floor regression suite in
ditto/tests/db/test_bench_version_floor.py, plus the cases added while migrating the fixture corpus. The same delta held at the previous base (eaf697b: 2489 green onmain, 2509 on the branch), so nothing here is carried bymainmoving.A note so nobody reconstructs a number from the review thread: an earlier count of "~38 failures" on this branch came from a run that hung and never printed a summary, so it was a partial tally of a run that never finished — not a measurement. The suite did not regress and recover; it was never measured until the hang was fixed.
The hang is fixed, in its own commit (
test(harness): make the pre-test reset fail loudly instead of deadlocking). It is not a v6 problem and is cherry-pickable on its own: a test that fails while holding an open transaction leaves its connection checked out,engine.dispose()cannot force-close it, and the next test'sTRUNCATEblocks on its locks forever. Onmainit never fires because nothing fails. Measured on a file with 17 failing tests: previously hung indefinitely, now completes in 6 seconds and reports all 17.The last 26, and what they actually were
They were expected to be "the retired era is now the subject" — tests asserting a v2–v6 rollout could be started, activated or selected, which the floor turns into a 409. Read one by one, none of them was that. Every one of the 26 failed on the
active_bench_versionfallback described under Application above: a fixture that seeds an inherited v2 world and never records an activation used to get v2 for free fromDEFAULT_BENCH_VERSION, and now gets the floor. Not one 409 among them. Recorded because a reviewer skimming the diff would otherwise assume the first reading was right.db/queries/test_benchmark_rollout.pyendpoints/test_admin_benchmark_rollout.pyendpoints/test_public.py(TestBenchConfig, leaderboard cache)db/queries/test_prev_gen_carryover.pyThese counts correct an earlier draft of this section, which had
test_benchmark_rollout.pyat 16 and so totalled 25 against a measured 26. They are read off the run's ownshort test summary, not recounted by hand.Two shapes, both following the same rule — where the era is incidental, move to the floor; where the era is the subject, keep it retired and state the real behaviour:
Floor-era reads (4 tests). Empty-database status and discovery reads. The era was never their subject — "a status read starts nothing", "discovery is authenticated and read-only", "config shape and defaults". They now assert the floor. One of them changes shape rather than value and is worth a look:
available_target_versionson a fresh ledger is now[], because a target must be both above the active version and at or above the floor, and with v7 the newest shipped contract those two conditions meet at exactly one era — the one already in force. That is the_CONTRACTS/floor coincidence noted above, made visible. It becomes[8]when ditto-assistant/ditto-subnet#463 lands.A genuinely-active inherited era (22 tests). These exercise a transition, and a transition needs a source era below its target. A rollout must move forward, the newest shipped contract is v7, the floor is 7 — so the source era of any rollout that can still be built is beneath the floor, necessarily. There is no renumbering that keeps the transition and clears the floor; the fixtures already seeded their v2/v6 ledgers through
retired_era_writes_allowedfor exactly this reason. What was missing was the activation that made that era authoritative, soactive_bench_versionhad no durable decision to read and answered the floor — collapsing source and target onto one era and quietly voiding the assertion.supersede_open_rolloutis the clearest case: it refuses a rollout that already owns active authority, so the supersede under test 409'd before it ran.The fix is one shared helper,
ditto/tests/legacy_era.py::grandfather_active_era— a grandfathered pre-flooractivatedrollout row. That is a state the database legitimately holds: the floor isNOT VALIDprecisely so production keeps its activation history, and it is the shape production was in before the v7 transition. Not a fiction, and not a weakening — it restores bite that had been lost, e.g.test_open_v7_rollout_keeps_active_v6_harness_authoritativewas asserting7 == 7and is once again asserting what its name says.Its docstring, and every call site, says to re-point at a 7 → 8 transition once ditto-assistant/ditto-subnet#463 ships — the point at which a forward target above the floor exists and these fixtures stop needing to reach back into retired history.
Two tests changed shape enough to name explicitly:
test_control_degrades_the_slow_section_instead_of_hanginghad become vacuous: with no candidate target the qualification loop never runs, so the deliberately-hanging stub was never called and the test passed while proving nothing. It now seeds the v6 authority that leaves exactly one candidate (v7) to hang on.test_config_shape_and_defaults' harness assertions were v2-era values. The harness block is derived from the era being reported, so on the floor era they are the proxy-routed model and a pinned reasoning effort.Known fixture hazard:
_seed_agent(screened_image=True)A default this branch introduced, and the class of bug worth watching for in the rest of the diff: it changed what several tests exercise without failing any of them. Every contract from v3 up sets
requires_screened_image, so once the corpus moved off v2 an agent seeded without one is silently dropped from the candidate set. Defaulting it on is right. But it silently moved a never-disclose test off the source-tarball path whose storage key it asserts and onto the image path — still green, still passing, no longer testing its docstring.Four call sites now pass
screened_image=Falseexplicitly, each with a comment saying why the absence is the point (TestArtifact×2, the v7 screened-only lease tests ×2). Flagged rather than swept because the general hazard — a fixture default that changes a test's subject without breaking it — is not something a green suite can catch.Pre-existing flake, not from this branch
TestUnmatchableWorkClaimIsLoud's twocaplogtests: whichever runs first against a freshly created test database fails its "…produced no warning" assertion. Reproduced deterministically, both trees, bymake test-db-cleanfollowed by the class alone:main@eaf697b→test_retest_reported_on_the_wrong_slot_warns_and_is_acceptedfailstest_running_benchmark_with_no_active_slot_warns_and_is_acceptedfailsSame failure, different member of the pair, depending only on which one the scheduler hands out first. It is a logging-configuration ordering artifact, not a benchmark-version one, and it never fires in a full-suite run on either tree — something earlier configures logging first. Not among the 26 and not introduced here; recorded so it is not attributed to this PR if someone runs that file on its own.
Migration head, re-verified at submission time
This repo uses Alembic revision hashes, not numeric prefixes, so the Goose-style version-prefix collision does not apply. Checked the analogous failure instead —
alembic headsreports exactly one head (d4b8e6c1a205),down_revision(45ef71514f21) ismain's head, no duplicate revision ids, no branch point.That check was re-run after rebasing, and it mattered once already: the branch originally chained from
c7a4f1e2b903, which #555 has since merged withf4b7d2c91ae5into45ef71514f21. Left alone, this PR's green migration check would have been stale — GitHub does not re-run checks when the base moves — and merging it would have produced exactly the two-head outage #555 repaired.mainthen moved again, todb7cb53(#556, #557, #558). This branch is rebased onto it and re-verified: those three commits add no Alembic revision, sodown_revisionstill namesmain's head and needed no re-pointing.scripts/check_migration_order.py— the guard #556 added for precisely this failure mode — reportsok (1 new migration(s); origin/main head 45ef71514f21; merged head d4b8e6c1a205). The full suite above was run after that rebase, not before it.If
mainmoves again before this merges, re-verify rather than trusting the check on the page. #556 also shippedscripts/recheck_open_pr_migrations.shfor exactly that.🤖 Generated with Claude Code