fix(ci): gate the release-bump commit — nothing has ever tested it
CI / Check / Test (push) Successful in 3s
CI / Tag release (push) Successful in 1s

Found from the other end: the koala cluster's CIHeadUncovered{repo=skills}
alert had been firing for 85 hours, and it is a true positive.

    mathias/skills main HEAD   f031c2b
    workflow runs for that sha  0

Not a one-off. The previous release-bump commit has the same property:

    6cc224f  chore(release): plugin.json version 0.19.4  ->  0 runs
    f031c2b  chore(release): plugin.json version 0.19.5  ->  0 runs

The `tag` job pushes the manifest bump itself with secrets.GITEA_TOKEN, and a
push authenticated with the Actions token does not trigger workflows -- that is
deliberate platform behaviour to stop CI recursing. The `Skip-Release: true`
trailer stops the TAGGER recursing and works; nothing stopped the whole run,
`gate` included, from never firing on that commit.

So after every release, main's HEAD is a commit no CI has seen, until an
unrelated human push moves it. CIHeadUncovered therefore cannot clear on its
own -- and an alert that can only be cleared by doing something unrelated is
worse than a noisy one, because it teaches the reader that red is the normal
state of that rule.

The commit is not cosmetic either. plugin.json's version is what Claude Code
gates plugin updates on, `task check` asserts it is not behind the latest tag,
and the sed-and-retry logic that writes it already shipped a mismatched
manifest once -- v0.10.0 tagged with the manifest still reading 0.9.0, per the
comment above that very loop.

Fix: run `task check` on the bump commit before pushing it. The commit is
produced in that step, so it can be validated in that step -- one check instead
of a second full CI run, and no recursion risk to reintroduce.

Considered and rejected: re-triggering CI with a different credential after the
push (costs a full run per release and brings back what the token behaviour
exists to prevent), and teaching CIHeadUncovered to ignore manifest-only
commits (narrowing the measure to fit the system, and it would hide a real gap
too).

The assertion in tests/ci_gate_test.sh had to be anchored with ^[^#]*: the tag
job's own prose says "`task check` asserts the manifest is not behind the
latest tag", so a bare grep matched the comment explaining the problem and
reported the problem fixed.

Closes #46.
This commit is contained in:
2026-09-11 11:11:16 +02:00
parent f031c2b77d
commit 6838baa071
2 changed files with 53 additions and 0 deletions
+27
View File
@@ -133,6 +133,33 @@ jobs:
git add "$manifest"
# The trailer below stops this push from re-triggering the tagger.
git commit -m "chore(release): plugin.json version $version" -m "Skip-Release: true"
# skills#46. This commit is pushed with secrets.GITEA_TOKEN, and a
# push authenticated with the Actions token does NOT trigger
# workflows -- deliberate platform behaviour to stop CI recursing.
# `Skip-Release: true` stops the tagger recursing; nothing stopped
# the whole run, `gate` included, from never firing on this commit.
#
# So every release left main's HEAD with zero CI runs. Measured:
# 6cc224f (0.19.4) and f031c2b (0.19.5) each have none, and the
# koala cluster's CIHeadUncovered{repo=skills} had been firing on it
# for 85 hours -- an alert that can only be cleared by an unrelated
# human push.
#
# The commit is not cosmetic: plugin.json's version is what Claude
# Code gates plugin updates on, `task check` asserts it is not
# behind the latest tag, and the sed-and-retry logic above already
# shipped a mismatched manifest once (v0.10.0 reading 0.9.0).
#
# Gating it HERE rather than re-triggering CI afterwards: the commit
# is produced here, so it can be validated here, at the cost of one
# `task check` instead of a second full run -- and with no recursion
# risk to reintroduce.
if ! task check; then
echo "::error::the release-bump commit fails task check -- not pushing it" >&2
exit 1
fi
if git push origin HEAD:main; then
echo "bumped $manifest to $version"
exit 0