DFlash: let the gates-only dry run actually reach the DFlash gate - #820
Merged
Conversation
#815 split the enablement interlock so the DFlash pipeline could be validated WITHOUT flipping the go-live flags: run_benchmark=false publishes no score, so it is admitted against an inert track. The point of that split is to exercise "DFlash correctness and parity gate (untimed)" -- the one gate this track owns. It could not. Two steps above that gate were not gated on inputs.run_benchmark: * "Select hidden DFlash timed target from the pool", which fails closed on an empty timed_prompt_pool; and * the timed-golden half of "Prepare hidden DFlash goldens", which is addressed BY that selection. The pool is deliberately empty on main (pinned by theTrackFixtureRemainsInertOnMain), so before go-live EVERY dispatch died red at pool selection -- one step short of the gate the dry run exists to run. The dry run validated nothing the serial pipeline had not, and operators were being taught to read red as success. A dry run also downloaded a hidden timed golden it would never time against. Scope both to the ranked path. The DFlash CORRECTNESS golden is pinned independently of the pool, so the untimed leg -- including the parity gate -- now runs and a clean dry run ends GREEN. The ranked path is unchanged and is not relaxed: * a ranked dispatch still runs the selection step and still fails closed on an empty, absent, null or malformed pool. Losing per-run sampling would lose contract layer L6 (anti-lottery), under which a failed ranked run becomes a free retry and every output-side gate degrades into submit-until-green; * the timed golden's pin check against the SAMPLED entry is unchanged; * the row-count preflight is scoped, not relaxed -- it stays unconditionally binding whenever the run times; * a new, independent refusal fires if a ranked run ever reaches the download with an empty selection. Honest scope: this is defence in depth and a named diagnostic, not the only barrier -- download-r2-object.sh already refuses an empty key -- but it names the cause instead of dying on a generic exit 2 in a helper. Tests: Tests/MLXFastTests/DFlashDryRunReachesItsGateTests.swift, 8 tests. Six of them EXECUTE the real step bodies (pool selection, golden preparation) against sandboxes; the step-set tests evaluate the real `if:` expressions with an evaluator that hard-fails on any construct it does not recognise rather than guessing. Verified by attack: reverting the workflow to origin/main fires 3 tests (8 issues), and five separately-injected defects -- a ranked run allowed past an empty pool, the empty-selection refusal removed, the whole goldens step gated ranked-only, the parity gate itself gated ranked-only, and the timed pin check dropped -- each fire the test that names them. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
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 join this conversation on GitHub.
Already have an account?
Sign in to comment
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.
The problem
#815 split the DFlash enablement interlock in two so the pipeline could be
validated without flipping the go-live flags:
run_benchmark=falsepublishes no ranked score, so a gates-only dispatch is admitted against an
inert track. The stated purpose of that split is to exercise
"DFlash correctness and parity gate (untimed)" — the one gate this track
owns and the serial pipeline does not.
It never could. Two steps sat above that gate and were not gated on
inputs.run_benchmark:Select hidden DFlash timed target from the pooltimed_prompt_poolPrepare hidden DFlash goldensThe pool is deliberately empty on
main— pinned byDFlashEnablementInterlockTests.theTrackFixtureRemainsInertOnMain. So beforego-live every dispatch, ranked or dry, died red at pool selection: one
step short of the only DFlash-specific gate the dry run exists to run.
Two consequences, and the second is the worse one:
pipeline had not already validated; and
A dry run also downloaded a hidden timed golden it would never time
against.
The fix
Scope both to the ranked path. The DFlash correctness golden is pinned
independently of the pool (a fixed R2 key, not a pool entry), which is exactly
what lets the untimed leg — including the parity gate — run while the pool is
still empty. A clean dry run now ends green.
The ranked path is not relaxed
This is the mirror hazard, and it is the one worth reviewing hardest: a guard
that also let a ranked run past an empty pool would destroy contract layer
L6 (anti-lottery). Under L6 a frozen timed prompt makes a failed ranked run a
free retry, and every output-side gate degrades from "must be correct" into
"must be correct often enough", with a drafter-confidence threshold tunable by
submit-until-green.
closed on a pool that is empty, absent,
null, or has an incomplete entry;sha256/bytesis unchanged;binding whenever the run times;
Prepare hidden DFlash goldensindependently refuses a ranked runwhose selection outputs are empty. Honest scope: this is defence in depth
plus a named diagnostic, not the only barrier —
download-r2-object.shalready rejects an empty object key (exit 2), and anempty pin would mismatch a real digest. What it buys is that the job says
"the selection step did not run" instead of dying on a generic
argument-validation exit 2 inside a helper, ~25 minutes into a job, and that
the refusal does not depend on either of those downstream behaviours staying
as they are.
Tests
Tests/MLXFastTests/DFlashDryRunReachesItsGateTests.swift— 8 tests.Six execute the real thing, not its text: the actual
run:block scalarsof
Select hidden DFlash timed target from the poolandPrepare hidden DFlash goldensare extracted and run under/bin/bashagainstsandboxed contracts and a stub R2 downloader that records every object key it
is asked for.
The two step-set tests evaluate the real
if:expressions — parsing everystep of the job (including unnamed
uses:-only steps; the parser wascross-checked against a real YAML parse) and computing which steps run, with
GitHub's implicit-
success()rule andsteps.<id>.outcomeresolved in order.The evaluator hard-fails on any construct it does not recognise rather than
silently returning
false: a test that quietly mis-parses a condition is worsethan no test.
aCleanDryRunReachesTheParityGateAndSkipsPoolSelectiontimed_prompt_poolaRankedRunStillSelectsATimedTargetAndTimestheCorrectnessGoldenIsPinnedIndependentlyOfThePoolaRankedSelectionStillFailsClosedOnAnUnusablePool::error::and emit no step outputsaRankedSelectionSamplesFromAPopulatedPoolaGatesOnlyGoldenPreparationSkipsTheTimedGoldenaRankedGoldenPreparationStillPinsTheTimedGoldenaRankedGoldenPreparationRefusesAnEmptySelectionAttack evidence
Per the method requirement, each fix was attacked by reintroducing the defect.
Attack A — literal revert of
.github/workflows/dflash-benchmark.ymltoorigin/main(the exact pre-fix file). Fires 3 tests / 8 issues:Attack B — the mirror hazard: keep the
if:but let a ranked run past anempty pool (
exit 1→::notice::+exit 0):Attack C — remove the independent empty-selection refusal:
Attack D — the tempting over-broad "fix": gate the whole
Prepare hidden DFlash goldensstep ranked-only, so the dry run has nocorrectness golden either:
Attack E — "fix" the red dry run by skipping the gate: gate
DFlash correctness and parity gate (untimed)oninputs.run_benchmark. Thismakes a dry run green while deleting its entire purpose:
Attack F — drop the timed pin check while keeping the ranked download:
Suite state
swift test: 543 tests / 25 suites pass (baseline onorigin/mainmeasuredin this workspace is 535/24; this adds 8 tests and 1 suite).
Pre-existing flakiness, unrelated to this change and reproduced with these
changes stashed on clean
origin/main: theBenchmarkSupportTestsruntime-worker-protocol tests (
... timed out waiting for protocol hello) failintermittently under machine load — measured 5 pass / 1 fail over 6 runs of
dflashReferenceRowsRequestCarriesTheVerifyBlockOnTheWireon unmodifiedorigin/main. This PR touches only workflow YAML and adds a test file thatspawns no runtime worker.
Notes
origin/main(3e9f072), which already contains R2: sign the canonical request SigV4 actually specifies #818 and DFlash: stop a failed gates-only run uploading the raw sealed score #819 —both merged since the task was written. Touches no
.github/scripts/*r2*.dflash-benchmark.ymlrefuses to run from abranch ("Enforce trusted workflow context").
not touched: the job-level
MLXFAST_DFLASH_BENCH_GOLDEN_SHA256/_BYTESpins are now dead (the timed golden is verified against the sampledpool entry instead) but are still required non-empty by
Enforce DFlash track enablement.🤖 Generated with Claude Code
Need help on this PR? Tag
@codesmithwith what you need. Autofix is disabled.