Repository navigation
ci(cache): let each PR write its own cache instead of never writing one - #84
Conversation
CLOUD-176 rust-cache never writes, so every CI run is cold
Why All three Those two facts are incompatible. Evidence Measured on the CI run for
For comparison, Options (a decision is needed, hence Todo not Backlog)
Definition of done
Acceptance
Ready — refinement specialization
Found while measuring whether |
|
Measured, rather than argued from the docs. Two runs on this branch — run 1 wrote the branch cache for the first time, run 2 restored it:
The mechanism is visible in the step list too: Two honest caveats on the numbers:
The empty Generated by Claude Code |
|
/fast-forward |
|
Triggered from #84 (comment) by @wenzowski. Trying to fast forward Target branch ( commit e600c71250f80c18724869b62904d631fd4a2231 (HEAD -> main, origin/main)
Author: Claude <noreply@anthropic.com>
Date: Fri Aug 7 04:56:47 2026 +0000
test(ready-guard): build a scratch repo instead of reading the working clone
The suite read the surrounding clone's HEAD and origin/main. That passed here
and failed in CI, where a fresh checkout has no origin/main ref and setup died
with status 128 before a single assertion ran — an environment-dependent test
proves nothing about the guard, and this one hid behind a local pass.
Each case now builds its own repository with a known HEAD and a synthetic
origin/main, so the amended-commit and moved-main cases are constructed rather
than borrowed. Adds the case CI actually exercised: with no origin/main ref at
all the guard fails open, because a local pre-flight must not deny in an
environment it cannot evaluate.Pull request ( commit e70c38f1dee40a252085fa1fd422d2742c81c036 (pull_request/claude/cloud-176-cache)
Author: Claude <noreply@anthropic.com>
Date: Fri Aug 7 04:59:57 2026 +0000
ci(cache): measure a warm run
An empty commit to produce the second CI run on this branch. Run 1
wrote the branch's cache for the first time; this run is the one that
can restore it, and the durations either show the drop the change
claims or they do not.
Refs CLOUD-176.Can't fast forward * e70c38f1dee40a252085fa1fd422d2742c81c036 ci(cache): measure a warm run
* a073799a667c2bdbe7b2e70d7a4e5b0a2a94badb ci(cache): let each PR write its own cache instead of never writing one
| * e600c71250f80c18724869b62904d631fd4a2231 test(ready-guard): build a scratch repo instead of reading the working clone
| * 17b8436a8ced5d06d059fb5db44700e948bcab41 chore(lock): record the cargo-zigbuild platform checksum
| * 1535406b5ed54285d4972558a2981709c60ab612 fix(ready-guard): a missing trailing newline allowed every command
| * abd1e3aabb4c8b2a04e86ae54cbbefdcbeab5e9a feat(gate): deny gh pr ready without verify and linear-check receipts
|/
* 6730729af469460c0f83497b3d10a7e25b92b972 ci(darwin): link the macOS targets in a parallel job, off the critical path
commit 6730729af469460c0f83497b3d10a7e25b92b972
Author: Claude <noreply@anthropic.com>
Date: Fri Aug 7 04:16:02 2026 +0000
ci(darwin): link the macOS targets in a parallel job, off the critical path
macos-link-check reads a list of framework-linking crates, so it is
incomplete by construction. Close that gap the only way with no false
negatives: link the target. It either links or it does not.
Structured to cost no wall-clock. It is its own CI job with one target
per matrix leg, so the legs run concurrently with each other and with
ci/cross, and nothing was added to mise run ci or the pre-commit hook.
Measured 7s per leg against a critical path of 46-63s.
7s, not the 35s an optimized build takes: linking is what is under test,
so the dev profile proves the same thing without LTO. A test asserts the
task never grows a dist profile, so that cannot regress quietly.
Net CI work goes down. Both Darwin triples leave cross-check, where they
were cargo check'd — which never links and so never caught this class of
break anyway. cross-check now covers windows-gnu alone.
A file task, not an inline one: mise appends CLI args to inline task
bodies, which passed a stray positional to cargo.
Refs CLOUD-173.Rebase locally, and then force push to |
save-if was gated on github.ref == refs/heads/main, but this workflow runs on pull_request only — main advances by fast-forward to already-tested commits and is deliberately not a trigger. The condition could therefore never be true: the cache was never written, every restore missed, and rust-cache was pure overhead while reading as though caching were handled. Measured cold on 6730729: ci 74s, cross 53s, darwin-link 66-75s, against ~7s for a warm local darwin link. The guard existed to stop one PR poisoning another's cache. GitHub already prevents that: a pull_request run writes under refs/pull/N/merge, restorable only by re-runs of that same PR, never by a sibling PR and never by main. So dropping the guard costs no isolation and makes every run after the first on a given PR warm — which is the common case here, where a branch is pushed several times before landing. Not fixed: the first run of a new PR stays cold, since only a base-branch cache could warm it. That needs a push-to-main trigger, which AGENTS.md forbids — the trade is CLOUD-176's open decision. Refs CLOUD-176.
An empty commit to produce the second CI run on this branch. Run 1 wrote the branch's cache for the first time; this run is the one that can restore it, and the durations either show the drop the change claims or they do not. Refs CLOUD-176.
e70c38f to
6faa49e
Compare
|
/fast-forward |
Fixes the dead cache found in CLOUD-176.
The bug
All three
rust-cachesteps were gatedsave-if: ${{ github.ref == 'refs/heads/main' }}. This workflow triggers onpull_requestonly — deliberately, sincemainadvances by fast-forward to commits that already passed, so re-running there would be waste.github.refis neverrefs/heads/mainin apull_requestrun. The condition could never be true, so the cache was never written, every restore was a miss, andrust-cacheran on every job doing nothing — while its comment read as though caching were handled.Measured cold on
6730729:ci74s,cross53s,darwin-link66–75s. A warm localdarwin-linkis ~7s.Why dropping the guard is safe
The guard's stated purpose was "a PR cannot poison the cache other PRs restore." GitHub already guarantees that: a
pull_requestrun writes its cache underrefs/pull/N/merge, and that cache "can only be restored by re-runs of the pull request. It cannot be restored by the base branch or other pull requests."So cross-PR isolation is a platform property, not something this workflow has to enforce. Removing
save-ifcosts no isolation and makes every run after the first on a given PR warm — the common case here, where a branch is typically pushed several times before landing (#77 took four).What this does not fix
The first run of a new PR is still cold. Only a cache on the base branch could warm it, and nothing writes there. Doing so needs a push-to-main trigger, which AGENTS.md explicitly forbids ("Never re-run CI on an already-tested SHA … Don't add push-to-main CI triggers"). Whether a cache-warming job — which runs no gates and asserts nothing — counts against that rule is a judgement for the maintainers, and it costs one job per merge. Left as CLOUD-176's open decision rather than taken unilaterally, particularly given runner spend is a live concern.
Verification
The honest test is the CI run on this PR, and it is necessarily two-part:
rust-cachestep's log.I'll push a trivial follow-up commit to produce that second data point before marking this ready, so the claim is backed by a measurement rather than by reasoning about the docs.
actionlintandzizmorclean.Refs CLOUD-176.
Generated by Claude Code