Skip to content

setup-pnpm caches COREPACK_HOME with actions/cache@v6, so a job can save back an unpinned package manager — the channel that reddened main and four PRs #16012

Description

@claude

Filed by the PM dispatch loop as option B of an open question the #15990 round left for decision (its report, and PR #16009). ⛔ Unassigned and ungraded — domain:*, type and priority are triage's.

⚠️ Framed honestly: this is defence in depth against a channel whose only known trigger has just been removed. The round that found it recommended A — leave it, and its reasoning is sound and measured. This card exists because the realised cost of the channel firing was high, not because the round was wrong. A maintainer who agrees with A should close this cheaply.

The channel

.github/actions/setup-pnpm caches COREPACK_HOME with actions/cache@v6 — the full cache action, not cache/restore. So the post-job save writes back whatever any step materialised into that store, including a package manager nobody pinned.

That is what fired today. Measured by the #15990 round:

  • setup-pnpm materialises only the pinned pnpm 10.31.0 and writes no lastKnownGood.json.
  • check-regen-pending.mjs --self-test ran a stub gate in a tmpdir() that pins nothing, so Corepack resolved pnpm's latest from the registry — measured, it downloaded 12.3.4.
  • pnpm 12's Rust CLI rejects -s with exit 2, so every stub read as "gate exited non-zero" ⇒ stale, reddening exactly the cases expecting a non-stale verdict.
  • Corepack wrote lastKnownGood.json, which is sticky, and cache@v6 saved that store back. Subsequent jobs then restored a store holding either a -s-accepting pnpm (green) or a pnpm 12 (red).

That is why the failure was flaky ACROSS jobs but byte-identical WITHIN one — a fact a plain "the launcher sometimes fails to resolve" story cannot account for.

Realised cost, so the priority is judged on evidence

On 2026-09-05 this reddened origin/main's own push build (run 33981169123, event=push, concluded failure at 17:51Z) and four unrelated PRs — #15980, #15996, #15978, #15998 — three of which were otherwise complete and correct. It cost a PM cycle per PR and pointed each seat at its own innocent diff.

Why the round recommended A, stated fully rather than summarised

After #16009, nothing in scripts/ spawns a package manager outside the repo any more, so the channel has no trigger. The round measured this with a positively-controlled grep (the pattern fires on exactly the pre-fix line the defect was about), leaving three sites, none exposed: scripts/pm/dispatch-gates.mjs:18541 is a string literal inside a self-test corpus and never executed; both scripts/qa/log-volume-census.mjs sites run with cwd: repoRoot where the pin applies, in a tool no workflow invokes.

What B would be

Make the store unable to hold an unpinned package manager, so the channel cannot be re-armed by the next fixture that shells out:

  • switch to actions/cache/restore so a job cannot write the store back, or
  • assert after setup that the store holds only the pinned version, failing loudly if not.

⚠️ Verify the mechanism before implementing — it is one round's measurement. In particular re-measure that setup-pnpm writes no lastKnownGood.json and that cache@v6 (not cache/restore) is what the action uses today; ⛔ a hardening that misreads the current action would be worse than none.

Refs

#15990 / PR #16009 (the fix that removes today's trigger) · #9258 (prior art in the same fixture: an environment-dependent flake fixed by removing the dependence rather than retrying) · #15980 · #15996 · #15978 · #15998 (the four PRs it reddened)


Generated by Claude Code

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions