Skip to content

fix(cache): make the contract true, and derive the surface it must cover - #181

Merged
rldyourmnd merged 2 commits into
mainfrom
fixtures/cache-refusals
Aug 15, 2026
Merged

fix(cache): make the contract true, and derive the surface it must cover#181
rldyourmnd merged 2 commits into
mainfrom
fixtures/cache-refusals

Conversation

@rldyourmnd

Copy link
Copy Markdown
Contributor

The declaration was the opposite of the behaviour

catalog/cache-contract.yml recorded:

  - action: astral-sh/setup-uv
    default_caches: false

The pinned action declares enable-cache: auto, and its getEnableCache() returns
RUNNER_ENVIRONMENT === "github-hosted"true on every hosted runner. Two gate
jobs carried a comment saying their explicit false "changes nothing today". It
changed something.

default_caches was also read by no line of code. It is now backed by
upstream_default — the literal default the pinned action.yml declares — and
check_cache_upstream_defaults.py resolves it from that exact commit each sweep.

The refusal list was hand-written, and the contract knew better than it could

The old closing paragraph said the undeclared remainder were "advisory lanes where a
poisoned entry cannot reach a release or a required check". check_cache_contract.py
now derives the required surface from ci-gate's own needs graph, following a
uses: job into the workflow it calls.

It immediately found two leaks, not the one that was known:

ci.yml: job 'shell-gates' is required by ci-gate and runs actions/setup-go,
  which caches with no input, but no refusal is declared
zizmor-sarif.yml: job 'zizmor' is required by ci-gate and runs astral-sh/setup-uv,
  which caches with no input, but no refusal is declared

actions/setup-go in shell-gates was in nobody's list — not the review's, not mine.
Deriving the surface found it; listing it never would have.

A second finding, adjacent

Two call sites — qt-ci.yml and runtime-fixtures-languages.yml — had no with:
block at all
, so they were also not pinning uv itself. setup-uv's version
input defaults to '', documented as "the version in pyproject.toml or latest",
and there is no pyproject.toml here.

A SHA-pinned action was installing whatever uv was latest at run time — including
in qt-ci.yml, a product reusable consumers call. Both now pin 0.11.30.

What changed

  • 12 call sites state their refusal, each with the reason it applies there.
    python-ci.yml keeps enable-cache: true — deliberate, it caches the consumer's
    own dependencies.
  • The falsified rationale is gone; the miscounted "three true entries" (there were
    four; there are now five) is corrected.
  • The two false ci.yml comments are corrected.

Evidence, before the ledger was updated — not after

Touching six product reusables invalidated their proven_digest, exactly as designed.
Rather than downgrade them, the branch was pushed as fixtures/** and the estate
re-proved every one:

reusable job result
private-static.yml fixture / private-static success
zizmor-no-sarif.yml fixture / zizmor (no SARIF) success
zizmor-sarif.yml fixture / zizmor SARIF success
semgrep-ci.yml fixture / semgrep success
sql-ci.yml fixture / sql-ci (linux, macOS, Windows) success
qt-ci.yml fixture / qt-ci (6.8.3) + observe success

Runs 31853478055
and 31853477977.
proven_digest and last_run were updated only after those succeeded, and the six
files are byte-identical to the commit they proved.

Mutations

Mutation Result
required gate stops refusing the uv cache RED (caught)
required gate stops refusing the Go cache RED (caught)
catalog lies about setup-uv's declared default RED (caught)
catalog lies about setup-go's declared default RED (caught)

Verification

  • validate_all — all tiers OK with GH_TOKEN
  • actionlint — clean · zizmor@1.26.1 --persona pedantic --min-severity low — no findings

The catalog recorded astral-sh/setup-uv as not caching by default. The pinned
action declares `enable-cache: auto`, and its getEnableCache() returns true
when RUNNER_ENVIRONMENT is github-hosted, so the entry asserted the opposite
of the behaviour and nothing executed the assertion. Two gate jobs carried a
comment saying their explicit `false` changed nothing today. It changed
something.

upstream_default now records the literal default the pinned action.yml
declares, and check_cache_upstream_defaults.py resolves it from that commit
each sweep, so default_caches stops being prose about somebody else's code.

The refusal list was hand-written, and the contract's closing paragraph
claimed the undeclared remainder could not reach a release or a required
check. It could. check_cache_contract.py now derives the required surface from
ci-gate's own `needs` graph and follows a `uses:` job into the workflow it
calls. That found two leaks rather than the one that was known: zizmor-sarif,
which backs the required zizmor job, and actions/setup-go in shell-gates,
which caches by default and had never been named.

Twelve call sites now state their refusal. python-ci.yml keeps enable-cache:
true deliberately -- it caches the consumer's own dependencies.

Two of those sites, qt-ci.yml and runtime-fixtures-languages.yml, had no
`with:` block at all and so were not pinning uv itself: setup-uv's `version`
defaults to the version in pyproject.toml or, absent one, latest, and there is
none here. A SHA-pinned action was installing an unpinned tool in a product
reusable. Both now pin 0.11.30.

All six affected reusables were re-proven by real fixture runs before their
proven_digest was updated.
@github-actions github-actions Bot added ci Continuous integration and removed ci Continuous integration labels Aug 15, 2026
Comment thread scripts/check_cache_upstream_defaults.py Fixed
Comment thread scripts/check_cache_upstream_defaults.py Fixed
@github-actions github-actions Bot added ci Continuous integration and removed ci Continuous integration labels Aug 15, 2026
@rldyourmnd
rldyourmnd merged commit 8a2c662 into main Aug 15, 2026
115 checks passed
@rldyourmnd
rldyourmnd deleted the fixtures/cache-refusals branch August 15, 2026 00:45
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants