From 6838baa07155fcb9edf80e5a18da79165a4c2e4f Mon Sep 17 00:00:00 2001 From: Mathias Date: Fri, 11 Sep 2026 11:11:16 +0200 Subject: [PATCH] =?UTF-8?q?fix(ci):=20gate=20the=20release-bump=20commit?= =?UTF-8?q?=20=E2=80=94=20nothing=20has=20ever=20tested=20it?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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. --- .gitea/workflows/ci.yml | 27 +++++++++++++++++++++++++++ tests/ci_gate_test.sh | 26 ++++++++++++++++++++++++++ 2 files changed, 53 insertions(+) diff --git a/.gitea/workflows/ci.yml b/.gitea/workflows/ci.yml index 8f54585..02129b6 100644 --- a/.gitea/workflows/ci.yml +++ b/.gitea/workflows/ci.yml @@ -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 diff --git a/tests/ci_gate_test.sh b/tests/ci_gate_test.sh index c410955..be8edad 100644 --- a/tests/ci_gate_test.sh +++ b/tests/ci_gate_test.sh @@ -53,5 +53,31 @@ printf '%s' "$tag_job" | grep -qE '^ +needs: *gate' \ && ok 'ungated release.yml removed' \ || no 'ungated release.yml removed' +# skills#46. The manifest-bump commit is created and pushed by the `tag` job +# using secrets.GITEA_TOKEN, and a push authenticated with the Actions token +# does not trigger workflows -- deliberate platform behaviour to stop CI +# recursing. So the ONE commit in this repo that no CI run has ever seen is the +# one the release job writes, every release: +# +# 6cc224f chore(release): plugin.json version 0.19.4 -> 0 runs +# f031c2b chore(release): plugin.json version 0.19.5 -> 0 runs +# +# The koala cluster's CIHeadUncovered{repo=skills} alert had been firing on it +# for 85 hours when this was found, and it cannot clear on its own -- only an +# unrelated human push moves main's HEAD to a tested commit. +# +# The commit is not nothing: 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 with the manifest reading 0.9.0). +# +# Anchored past comments: the tag job's prose already SAYS "`task check` +# asserts the manifest is not behind the latest tag", and a bare grep matches +# that and passes. A grep for a concept matches the rationale, and the more +# carefully a thing is documented the more likely that is. +printf '%s' "$tag_job" | grep -qE '^[^#]*task check' \ + && ok 'the release-bump commit is gated before it is pushed' \ + || no 'the release-bump commit is pushed with no gate -- nothing ever tests it (#46)' + printf '\nci_gate: %d passed, %d failed\n' "$pass" "$fail" [ "$fail" -eq 0 ]