Severity: HIGH — every reusable workflow in this repository is delivered by the moving v1
ref, and v1 only advances as a side effect of publishing the @theokit/dep-check npm package.
A change that touches only .github/workflows/** therefore sits on main, correct and merged,
reaching zero of the ten consumers until somebody happens to release the package.
Evidence
release.yml triggers on push: tags: ['dep-check-v*'], and advance-v1 is gated further:
if: needs.publish.result == 'success' && startsWith(github.ref, 'refs/tags/dep-check-v')
Both conditions are right for what they guard. Neither has anything to do with a workflow change.
Measured on this repository's own history:
| commit |
landed on main |
v1 next moved |
stale for |
863a274 feat(checks-pass) |
2026-09-01 13:41 |
2026-09-04 20:42 (dep-check-v0.10.0) |
3d 7h |
e1c663a fix(preview) |
2026-09-02 16:08 |
same |
~2d 4h |
a4c8128 fix(preview) |
2026-09-02 18:40 |
same |
~2d 2h |
8137d9b fix(preview) |
2026-09-02 18:47 |
same |
~2d 2h |
2b90a81 fix(preview) |
2026-09-02 21:09 |
same |
~2d |
Four of those five are fixes to preview.yml, one of them specifically so that a preview which
falls back to publishing everything announces that it is not installable. For two days every
consumer kept producing the silent version, and nothing said so.
Why this is not the same as the pin discipline
dep-check.yml deliberately splits the two: the workflow moves by ref so a policy change
reaches every caller, while the tool is a semver artifact that moves by a bump somebody
reviewed. That reasoning is written at the top of the file and it is correct.
The defect is that the mechanism does not implement it. Today the policy cannot move at all
without the tool moving, which is the exact coupling the split exists to avoid.
Repro
- Merge any change that touches only
.github/workflows/** into main.
git fetch --tags --force && git rev-parse v1 — still the previous commit.
- Any consumer resolving
@v1 runs the old workflow, indefinitely.
Suggested fix
Advance v1 on a push to main that changed workflows or composite actions, with the guard that
already exists for the release path: v1 must never point at a commit whose declared
@theokit/dep-check version is not the published one, or consumers get
npx @theokit/dep-check@<missing> failing at the point of use. That invariant is the real reason
advance-v1 is gated on a successful publish; a workflow-only path needs the same invariant
checked directly rather than inherited from the publish.
Doing it by hand instead is the option this repository already rejected in advance-v1's own
docblock: "Moving it by hand was the last manual step in a release, and it has been missed on two
of six."
Related
How this was found: standardising the promotion gate across the ten consumers, the follow-up
step "convert the consumers to thin callers" required knowing when @v1 would serve the new
workflow. The answer was "at the next unrelated package release".
No secrets, tokens or credentials appear in this report.
Severity: HIGH — every reusable workflow in this repository is delivered by the moving
v1ref, and
v1only advances as a side effect of publishing the@theokit/dep-checknpm package.A change that touches only
.github/workflows/**therefore sits onmain, correct and merged,reaching zero of the ten consumers until somebody happens to release the package.
Evidence
release.ymltriggers onpush: tags: ['dep-check-v*'], andadvance-v1is gated further:Both conditions are right for what they guard. Neither has anything to do with a workflow change.
Measured on this repository's own history:
mainv1next moved863a274feat(checks-pass)dep-check-v0.10.0)e1c663afix(preview)a4c8128fix(preview)8137d9bfix(preview)2b90a81fix(preview)Four of those five are fixes to
preview.yml, one of them specifically so that a preview whichfalls back to publishing everything announces that it is not installable. For two days every
consumer kept producing the silent version, and nothing said so.
Why this is not the same as the pin discipline
dep-check.ymldeliberately splits the two: the workflow moves by ref so a policy changereaches every caller, while the tool is a semver artifact that moves by a bump somebody
reviewed. That reasoning is written at the top of the file and it is correct.
The defect is that the mechanism does not implement it. Today the policy cannot move at all
without the tool moving, which is the exact coupling the split exists to avoid.
Repro
.github/workflows/**intomain.git fetch --tags --force && git rev-parse v1— still the previous commit.@v1runs the old workflow, indefinitely.Suggested fix
Advance
v1on a push tomainthat changed workflows or composite actions, with the guard thatalready exists for the release path:
v1must never point at a commit whose declared@theokit/dep-checkversion is not the published one, or consumers getnpx @theokit/dep-check@<missing>failing at the point of use. That invariant is the real reasonadvance-v1is gated on a successful publish; a workflow-only path needs the same invariantchecked directly rather than inherited from the publish.
Doing it by hand instead is the option this repository already rejected in
advance-v1's owndocblock: "Moving it by hand was the last manual step in a release, and it has been missed on two
of six."
Related
promotion-gate.yml, which is blocked by exactly this: merged, it would reachno consumer.
How this was found: standardising the promotion gate across the ten consumers, the follow-up
step "convert the consumers to thin callers" required knowing when
@v1would serve the newworkflow. The answer was "at the next unrelated package release".
No secrets, tokens or credentials appear in this report.