docs(catalog): record annotated-tag policy at the hand-tagging drift site

repo-standard's org-wide run flagged TAG-ANNOTATED (ERROR) on this repo: the
newest tag (v7.7.2) is lightweight, and the catalog pins plugins by tag, so a
movable tag is a movable pin.

Measured before writing, and the order's premise did not survive it:
release-plugin.mjs is NOT the reproduction site. It has used `git tag -a`
since 9b1838f, and the published surface agrees -- `git ls-remote --tags` on
repo-mailbox returns refs/tags/v0.25.0 (tag object a9b5eb8) AND the peeled
v0.25.0^{} (95ac710), which a lightweight tag does not have. The script tags
plugin repos; it never tags the catalog. The catalog's own tags are cut by
hand, and that is where nothing enforces anything.

Forward-only: the published lightweight tags stay. The only remedy for a
published tag is `git tag -a -f`, which force-moves a ref others may have
fetched -- the same traceless move the finding warns about. Consequence
recorded rather than hidden: TAG-ANNOTATED stays ERROR until a new annotated
tag sorts above v7.7.2 under compareTags, because tags_lightweight_accepted
reaches history only and cannot excuse the newest tag.

Probe pair run locally and deleted: `git tag -a` -> objecttype `tag`,
`git update-ref refs/tags/x` -> `commit`. Control that the measurement
discriminates on real tags: pre-polyrepo-archive is `tag`, v7.7.2 is `commit`.

Also noted: ~/.gitconfig has tag.gpgsign=true, which makes a bare `git tag`
fail rather than cut a lightweight one -- but the probe's -a tag came out
unsigned, so that is an accident, not the enforcement.

check-versions before and after: 12 plugins, 11 OK, 1 WARN, 0 ERROR
(unchanged -- the WARN is repo-mailbox 0.26.0, a separate order).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FLwHvGkH2kV4XLjUHorhLu
This commit is contained in:
Kjell Tore Guttormsen 2026-08-17 13:35:01 +02:00
commit acdd6060bd

View file

@ -18,6 +18,33 @@ their own Forgejo repositories under `https://git.fromaitochitta.com/open/`.
- Adding/updating a plugin entry: edit `.claude-plugin/marketplace.json` (external `source: "url"` with - Adding/updating a plugin entry: edit `.claude-plugin/marketplace.json` (external `source: "url"` with
a pinned `ref`) and re-state the plugin in README.md with its verified version. a pinned `ref`) and re-state the plugin in README.md with its verified version.
- Plugin source, issues, and releases live in each plugin's own repository — not here. - Plugin source, issues, and releases live in each plugin's own repository — not here.
- **Every tag this repo cuts is ANNOTATED — `git tag -a`, never a bare `git tag`.** A lightweight tag
is a branch-like ref: it can be moved to another commit with nothing recorded that it ever pointed
elsewhere. The catalog pins every plugin to `ref: vX.Y.Z`, so a movable tag is a movable pin — a
supply chain property, not a tidiness one (repo-standard `TAG-ANNOTATED`, ERROR).
**Two tagging sites, and only one of them was ever enforced:**
- *Plugin tags* are cut by `scripts/release-plugin.mjs` (`--create-tag --write`), which has used
`git tag -a` since its first commit (`9b1838f`) and has never had the defect. Verified 2026-08-17
against the published surface, not the source: `git ls-remote --tags` on repo-mailbox returns both
`refs/tags/v0.25.0` (tag object `a9b5eb8`) and the peeled `refs/tags/v0.25.0^{}` (commit `95ac710`)
— a lightweight tag has no peeled ref. **Do not "fix" `release-plugin.mjs`; it is not the drift site.**
- *The catalog's own tags* are cut BY HAND, and that is the site with no enforcement. All four
newest (`v7.7.2`, `v7.7.1`, `v7.7.0`, `v5.0.3`) are lightweight, from the monorepo era. Control
proving the measurement discriminates: `pre-polyrepo-archive` reports `tag`, and a local probe pair
(`git tag -a``tag`, `git update-ref refs/tags/x``commit`) reproduces both outcomes.
**The published lightweight tags STAY** (decided 2026-08-17, forward-only). The only remedy for an
already-published tag is `git tag -a -f <name> <name>^{}`, which force-moves a ref others may have
fetched — precisely the traceless move the finding warns about. Applying it as the fix would
demonstrate the defect. **Consequence to state plainly, not to hide:** `TAG-ANNOTATED` stays ERROR
until a NEW annotated tag becomes the newest, because `checkTagIntegrity`'s
`tags_lightweight_accepted` register key reaches tag HISTORY only and cannot excuse the newest tag.
And "newest" is `compareTags` order (semver triple), so a fresh-start `v1.0.0` would NOT clear it —
a clearing tag must sort above `v7.7.2`. Non-semver tags (`config-audit/v5.0.0`,
`pre-polyrepo-archive`) parse to `[0,0,0,…]` and never rank.
Note: `~/.gitconfig` sets `tag.gpgsign true`, which makes a bare `git tag <name>` **fail**
("no tag message?") rather than silently cut a lightweight one. Measured 2026-08-17, the probe's
`-a` tag came out **unsigned** all the same (0 PGP blocks) — so this config is an accident that
happens to block one path, not a signing guarantee. Do not treat it as the enforcement.
- **Releasing a plugin (canonical path — `scripts/release-plugin.mjs`):** since the polyrepo split, - **Releasing a plugin (canonical path — `scripts/release-plugin.mjs`):** since the polyrepo split,
a release is a TWO-repo act — tag the plugin repo AND bump the catalog `ref`. Forgetting the second a release is a TWO-repo act — tag the plugin repo AND bump the catalog `ref`. Forgetting the second
step strands users on the old version (the exact drift this helper exists to prevent). Run step strands users on the old version (the exact drift this helper exists to prevent). Run