ci: let domain-unit restore the rust-cache it was forbidden from matching - #857
Conversation
…hing "Domain crates — unit tests" is the critical path of CI at 12m, one step worth 714s. It was never a slow test suite. On merge_group run 32434156418 that step is 684.8s of rustc -- 1434 `Compiling` lines, 0 `Fresh` -- and about 29s of actual test execution: the 1156-test lib sweep in 1.1s, the 18 doctests in 14.3s, one 8.7s gate suite, everything else sub-second. It was a cold build on every run, masquerading as a slow job. rust-cache logged "No cache found." on all 14 recent main runs (577-749s each). The key it asked for was ...-b587c171-; the writer and all five PostgreSQL shards restore ...-f3304cdf- with an identical lockfile half. The env half differed by exactly two variables -- CARGO_PROFILE_DEV_DEBUG=0 and CARGO_PROFILE_TEST_DEBUG=0 -- which rust-cache folds into its hash, which 03e7292 added to the shards on 2026-08-18 to fix THIS SAME MISS there, and which scripts/check-ci-preflight.mjs FORBADE on this job: if (/^ (?:env|defaults):/m.test(domainUnit) ...) failures.push("domain-unit must use the default shell with no job or step env/defaults overrides"); A blanket "no env" ban is what kept the cache miss in place. The contract was protecting the shell, and the side effect was a 685s build per run. Even on a key hit, a target dir built WITH debuginfo cannot be reused by a build that wants it off, so matching the writer's profile is also what makes a restored cache usable at all. THE FIX IS TWO LINES OF YAML AND A NARROWER BAN. The job gains the same env block the shards carry, with the same MUST-match-the-writer rationale. The contract no longer forbids job env; it REQUIRES job env to be exactly those two variables, and still forbids step-level env and any `defaults:`. The failure text is unchanged, so the existing tests keep matching. I went in believing the lever was nextest. Measured locally on a warm build, the lib sweep is 57.4s under cargo and 2.08s under nextest -- a 27x ratio that turned out to be a macOS process-spawn artifact applied to what is, on the runner, about one second of execution. The honest nextest gain here is 10-20s, and it would have opened .config/nextest.toml as a second, unpinned selection surface for 141 binaries (a profile default-filter narrows what runs while the verbatim-pinned ci.yml text is unchanged and check-executed-tests keeps reporting everything executed). An adversarial review found both. No nextest, no new run step, no change to the pinned cargo invocations, ratchets unchanged at 130/390. Proven: preflight exit 0; contract suite 61/61; gate sweep 13/13; check-executed-tests 0. The env requirement can FAIL -- dropping CARGO_PROFILE_DEV_DEBUG, appending RUSTC_WRAPPER to the block, or re-adding a `defaults:` each turns the preflight red with the pinned text; restored, exit 0. The envelope digest was recomputed with the preflight's own serializer, not copied. PREDICTED, NOT MEASURED: a warm restore takes this step from ~714s to roughly the shards' warm numbers -- the ~1434 crates become `Fresh` and the step should land near the 30s of real execution plus restore time. That is the first run's job to confirm. If it holds, the 12m critical path drops out entirely and CI wall clock is set by buck-app (11m) and the platform shard (9m). Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Anvil Canonical Code Review & Adversarial Quality ReportPR: #857 — Executive SummaryPR #857 identifies and resolves the root cause of CI critical-path latency in
Canonical 16-Lens Adversarial Review Matrix
Critical Risks & Findings
Decision: APPROVE without reservations. Findings not addressable in the diff
🤖 [Reviewed] by Oyatie Anvil |
|
❌ Blocked — 13 finding(s) across 72 gates; 10 gate(s) produced no measurement.
🤖 [Blocked] by Oyatie Anvil |
|
Measured outcome — closing the "predicted, not measured" section above. Prediction was ~714s → "near the 30s of real execution plus restore time". Measured on this PR's run (32437098619, job 96640754902):
Why: the restored cache is built by the writer's That gives a further lever, if this job ever matters again: a writer whose cache also carries built test artifacts would cut most of the remaining 356s. Wall clock is 14m (from ~19m). This job is off the critical path; |
"Domain crates — unit tests" is CI's critical path at 12m — one step worth 714s. It was never a slow test suite. On merge_group run 32434156418 that step is 684.8s of rustc (1434
Compiling, 0Fresh) and ~29s of actual test execution: the 1156-test lib sweep in 1.1s, 18 doctests in 14.3s, everything else sub-second.It was a cold build on every run, masquerading as a slow job.
The cause
rust-cache logged
No cache found.on all 14 recent main runs. The key it asked for was…-b587c171-; the writer and all five PostgreSQL shards restore…-f3304cdf-with an identical lockfile half. The env half differed by exactly two variables —CARGO_PROFILE_DEV_DEBUG=0andCARGO_PROFILE_TEST_DEBUG=0— which rust-cache folds into its hash, which 03e7292 added to the shards on 2026-08-18 to fix this same miss there, and whichcheck-ci-preflight.mjsforbade on this job:A blanket "no env" ban is what kept the miss in place. The contract was protecting the shell; the side effect was a 685s build per run.
The fix
Two lines of YAML and a narrower ban. The job gains the same env block the shards carry, with the same rationale. The contract no longer forbids job env — it requires it to be exactly those two variables, and still forbids step-level env and any
defaults:. Failure text unchanged, so existing tests keep matching.I went in believing the lever was nextest
Measured locally on a warm build, the lib sweep is 57.4s under cargo vs 2.08s under nextest — a 27× ratio that turned out to be a macOS process-spawn artifact applied to what is, on the runner, about one second of execution. Honest nextest gain here: 10–20s. It would also have opened
.config/nextest.tomlas a second, unpinned selection surface for 141 binaries — a profiledefault-filternarrows what runs while the verbatim-pinned ci.yml text is unchanged andcheck-executed-testskeeps reporting everything executed. An adversarial review found both. No nextest, no new run step, no change to the pinned cargo invocations, ratchets unchanged at 130/390.Proven
Preflight exit 0 · contract suite 61/61 · gate sweep 13/13 · executed-tests 0.
The env requirement can fail: dropping
CARGO_PROFILE_DEV_DEBUG, appendingRUSTC_WRAPPER, or re-addingdefaults:each turns preflight red with the pinned text; restored, exit 0. The envelope digest was recomputed with the preflight's own serializer, not copied.Predicted, not measured
A warm restore should take this step from ~714s to near the ~30s of real execution plus restore time. That is the first run's job to confirm. If it holds, the 12m critical path drops out entirely and wall clock is set by buck-app (11m) and the platform shard (9m).
🤖 Generated with Claude Code