On a working checkout, immediately after git pull --rebase brings in the tagger's chore(release): plugin.json version X commit:
$ task check
ERROR: plugin.json version (0.9.0) != latest tag (0.8.5)
exit 201
Nothing is wrong. v0.9.0 exists on the remote; this checkout has not fetched it. git fetch --tags && task check → exit 0.
Hit three times in one session (2026-08-29) while working the backlog, because the loop is: push → CI tags and pushes the bump commit → pull → check is red until you also fetch tags.
Cause
Taskfile.yml check step 4 asserts equality:
if[ -n "$tag_v"]&&["$manifest_v" !="$tag_v"];then
git describe --tags is local-only, so tag_v is whatever this checkout last fetched.
What the assertion is actually for
Claude Code gates plugin updates on plugin.json's version. The failure it must catch is the manifest not moving — a release tag with a manifest still on the old number, so no installed host ever sees the update. That is manifest < tag.
manifest > tag is the normal state on any checkout between the bump commit and a tag fetch, and it is also the normal state inside CI for the tagger's own commit. It is not a fault.
Fix
Compare order, not equality: fail when the manifest is behind the latest known tag, and stay quiet when it is ahead. bin/doctor already grew a newer_version helper (major.minor.patch via awk) in #30 that can be reused rather than reimplemented — which argues for extracting it to bin/ where both can call it, with the tests coming along.
Definition of done
task check on a checkout with stale tags exits 0; task check with plugin.json behind the latest tag still exits non-zero and names both versions.
## Symptom
On a working checkout, immediately after `git pull --rebase` brings in the tagger's `chore(release): plugin.json version X` commit:
```
$ task check
ERROR: plugin.json version (0.9.0) != latest tag (0.8.5)
exit 201
```
Nothing is wrong. `v0.9.0` exists on the remote; this checkout has not fetched it. `git fetch --tags && task check` → exit 0.
Hit three times in one session (2026-08-29) while working the backlog, because the loop is: push → CI tags and pushes the bump commit → pull → check is red until you also fetch tags.
## Cause
`Taskfile.yml` check step 4 asserts **equality**:
```sh
if [ -n "$tag_v" ] && [ "$manifest_v" != "$tag_v" ]; then
```
`git describe --tags` is local-only, so `tag_v` is whatever this checkout last fetched.
## What the assertion is actually for
Claude Code gates plugin updates on `plugin.json`'s `version`. The failure it must catch is the manifest **not moving** — a release tag with a manifest still on the old number, so no installed host ever sees the update. That is `manifest < tag`.
`manifest > tag` is the normal state on any checkout between the bump commit and a tag fetch, and it is also the normal state inside CI for the tagger's own commit. It is not a fault.
## Fix
Compare order, not equality: fail when the manifest is **behind** the latest known tag, and stay quiet when it is ahead. `bin/doctor` already grew a `newer_version` helper (major.minor.patch via awk) in #30 that can be reused rather than reimplemented — which argues for extracting it to `bin/` where both can call it, with the tests coming along.
## Definition of done
`task check` on a checkout with stale tags exits 0; `task check` with `plugin.json` behind the latest tag still exits non-zero and names both versions.
Fixed in 1573732. Gate green on run #36 (the first run after the tagger fix; #33's own run died in the tag job on the queue race described in #22, not in the gate).
task check now fails only when the manifest is behind the newest tag it knows about, and stays quiet when it is ahead — the normal state between the tagger's bump commit and a tag fetch.
The comparison moved to bin/newer-version, shared with bin/doctor rather than existing twice, with seven cases including the lexical trap (0.8.10 beats 0.8.9) and a missing component counting as zero.
Mutation-verified:
manifest 0.1.0 vs tag v0.9.0 → exit 201, "plugin.json version (0.1.0) is behind the latest tag (0.9.0)"
manifest 9.9.9 vs tag v0.9.0 → exit 0
It immediately earned itself
An hour after landing, three queued runs failed to push their manifest bump and v0.10.0 got tagged with plugin.json still reading 0.9.0 — a release no installed host would ever see, which is exactly the fault the equality check was nominally guarding and which the old equality check would have reported identically to a harmless stale-tag checkout. Ordering made the difference between noise and a signal: main was red for the right reason, and stayed red until d7775aa fixed the tagger.
Fixed in `1573732`. Gate green on run [#36](https://git.d-ma.be/mathias/skills/actions/runs/4853) (the first run after the tagger fix; #33's own run died in the `tag` job on the queue race described in #22, not in the gate).
`task check` now fails only when the manifest is **behind** the newest tag it knows about, and stays quiet when it is ahead — the normal state between the tagger's bump commit and a tag fetch.
The comparison moved to `bin/newer-version`, shared with `bin/doctor` rather than existing twice, with seven cases including the lexical trap (`0.8.10` beats `0.8.9`) and a missing component counting as zero.
Mutation-verified:
```
manifest 0.1.0 vs tag v0.9.0 → exit 201, "plugin.json version (0.1.0) is behind the latest tag (0.9.0)"
manifest 9.9.9 vs tag v0.9.0 → exit 0
```
## It immediately earned itself
An hour after landing, three queued runs failed to push their manifest bump and `v0.10.0` got tagged with `plugin.json` still reading `0.9.0` — a release no installed host would ever see, which is exactly the fault the equality check was nominally guarding and which the *old* equality check would have reported identically to a harmless stale-tag checkout. Ordering made the difference between noise and a signal: main was red for the right reason, and stayed red until `d7775aa` fixed the tagger.
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Symptom
On a working checkout, immediately after
git pull --rebasebrings in the tagger'schore(release): plugin.json version Xcommit:Nothing is wrong.
v0.9.0exists on the remote; this checkout has not fetched it.git fetch --tags && task check→ exit 0.Hit three times in one session (2026-08-29) while working the backlog, because the loop is: push → CI tags and pushes the bump commit → pull → check is red until you also fetch tags.
Cause
Taskfile.ymlcheck step 4 asserts equality:git describe --tagsis local-only, sotag_vis whatever this checkout last fetched.What the assertion is actually for
Claude Code gates plugin updates on
plugin.json'sversion. The failure it must catch is the manifest not moving — a release tag with a manifest still on the old number, so no installed host ever sees the update. That ismanifest < tag.manifest > tagis the normal state on any checkout between the bump commit and a tag fetch, and it is also the normal state inside CI for the tagger's own commit. It is not a fault.Fix
Compare order, not equality: fail when the manifest is behind the latest known tag, and stay quiet when it is ahead.
bin/doctoralready grew anewer_versionhelper (major.minor.patch via awk) in #30 that can be reused rather than reimplemented — which argues for extracting it tobin/where both can call it, with the tests coming along.Definition of done
task checkon a checkout with stale tags exits 0;task checkwithplugin.jsonbehind the latest tag still exits non-zero and names both versions.Fixed in
1573732. Gate green on run #36 (the first run after the tagger fix; #33's own run died in thetagjob on the queue race described in #22, not in the gate).task checknow fails only when the manifest is behind the newest tag it knows about, and stays quiet when it is ahead — the normal state between the tagger's bump commit and a tag fetch.The comparison moved to
bin/newer-version, shared withbin/doctorrather than existing twice, with seven cases including the lexical trap (0.8.10beats0.8.9) and a missing component counting as zero.Mutation-verified:
It immediately earned itself
An hour after landing, three queued runs failed to push their manifest bump and
v0.10.0got tagged withplugin.jsonstill reading0.9.0— a release no installed host would ever see, which is exactly the fault the equality check was nominally guarding and which the old equality check would have reported identically to a harmless stale-tag checkout. Ordering made the difference between noise and a signal: main was red for the right reason, and stayed red untild7775aafixed the tagger.