feat(engine): TAG-SIGNED — a tag that names a signer, not just write access
The check sat parked as "blocked on an operator decision" on the strength of 0 of 18 repos signing. That zero was never evidence against it: it is what a practice nobody has adopted yet looks like, and reasoning from it makes the gate ratify the status quo it exists to move. The rule that settled it asks something else — would a public AAA+ repository do this? Yes, and unlike the two conventions that killed VERSION-DRIFT, there is no second convention under which release tags are better left unattributable. The measurement then decided the SCOPE, not the yes. Across 20 clones: 158 v* tags, 144 annotated, 14 lightweight, 0 signed, and not one dated after 2026-08-12. A boolean "this org signs" would have failed 20 correct repositories on day one with force-moving 144 published refs as its only remedy — the mechanism that gets gates switched off. So the policy is a DATE in the register (never in the engine, the rule that keeps the org-profile exemption a flag): nothing fires today, teeth at each repo's next release. Verified both directions, because a sweep returning zero proves nothing on its own — a dead check returns zero too. 21 repos: 18 PREPOLICY + 3 NONE, 0 ERROR, 0 WARN; then against real git objects an unsigned post-policy newest tag does fire ERROR. Two exclusions keep one defect from being counted twice: a lightweight tag has no object to carry a signature (byDesign skip naming TAG-ANNOTATED as owner), and a pre-policy tag was correct when cut (OK, said out loud, never silence). It answers "is it signed", never "does the forge vouch for it". The README row ships in this commit, not after it, and the stale test count in CLAUDE.md (230 → 241) is corrected here rather than left for the next reader. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XsPsVsvhrSaejK3cLPmnN2
This commit is contained in:
parent
9f08ebc5ed
commit
e6cd8983ca
9 changed files with 415 additions and 17 deletions
52
CHANGELOG.md
52
CHANGELOG.md
|
|
@ -6,6 +6,58 @@ versioning is [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|||
|
||||
## [Unreleased]
|
||||
|
||||
## [0.11.0] — 2026-08-13
|
||||
|
||||
### Added
|
||||
|
||||
- **`TAG-SIGNED` — a release tag that names a signer, not just whoever could
|
||||
write to the forge.** The catalog pins plugins by tag, so this sits on the
|
||||
same supply-chain axis as `TAG-ANNOTATED`: annotation makes a tag
|
||||
immovable-without-a-trace, a signature makes it attributable.
|
||||
|
||||
The check had been parked for a release as "blocked on an operator decision",
|
||||
on the strength of 0 of 18 repos signing. That zero was never evidence against
|
||||
it — it is what a practice nobody has adopted yet looks like, and reasoning
|
||||
from it makes the gate ratify the status quo it exists to move. The rule that
|
||||
settled it (operator, 2026-08-13) asks something else entirely: *would a public
|
||||
AAA+ repository do this?* Yes. Unlike the two legitimate conventions that
|
||||
killed `VERSION-DRIFT`, there is no second convention under which release tags
|
||||
are better left unattributable.
|
||||
|
||||
**The policy is a DATE, and the date is what made the check shippable.**
|
||||
Measured across 20 clones before the rule was locked: 158 `v*` tags, 144
|
||||
annotated, 14 lightweight, 0 signed, and not one tag dated after 2026-08-12. A
|
||||
boolean "this org signs" would have failed 20 correct repositories the day it
|
||||
landed, with force-moving 144 published refs as its only remedy — the mechanism
|
||||
that gets gates switched off. `tags_signed_from` lives in the register, never
|
||||
in the engine (the rule that keeps the org-profile exemption a flag rather than
|
||||
a class name in a classifier); absent it, the check is a `notRun` SKIP, because
|
||||
the gate never assumes a policy nobody recorded.
|
||||
|
||||
Verified both directions: a sweep of 21 repos yields 18 `TAG-SIGNED-PREPOLICY`
|
||||
+ 3 `TAG-SIGNED-NONE`, 0 ERROR, 0 WARN — then, against real git objects, an
|
||||
unsigned post-policy newest tag does fire `ERROR`. A sweep returning zero
|
||||
proves nothing alone; a dead check returns zero too.
|
||||
|
||||
Two exclusions keep one defect from being counted twice under two codes: a
|
||||
**lightweight** tag has no tag object to carry a signature at all
|
||||
(`TAG-SIGNED-LIGHTWEIGHT`, a `byDesign` skip naming `TAG-ANNOTATED` as the
|
||||
owner), and a **pre-policy** tag was correct when it was cut
|
||||
(`TAG-SIGNED-PREPOLICY`, an `OK` — said out loud, never as silence).
|
||||
|
||||
It answers *is this tag signed* and never *does the forge vouch for it*: the
|
||||
signature is read from the clone, while the forge's "Verified" badge needs a
|
||||
key registered there.
|
||||
|
||||
### Changed
|
||||
|
||||
- The engine reads tag signature and creation date alongside the object type, in
|
||||
the same single `for-each-ref`. The signature is read with
|
||||
`%(if)%(contents:signature)%(then)…` rather than by printing the block, which
|
||||
is multi-line and would break the line-oriented parser on exactly the tags the
|
||||
check cares about. No new API call — this stays at three.
|
||||
- 241 tests, from 230.
|
||||
|
||||
## [0.10.1] — 2026-08-12
|
||||
|
||||
### Fixed
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue