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:
Kjell Tore Guttormsen 2026-08-13 10:51:36 +02:00
commit e6cd8983ca
9 changed files with 415 additions and 17 deletions

View file

@ -22,6 +22,7 @@ import {
checkHeadings,
checkBadges,
checkTagIntegrity,
checkTagSigned,
checkInstallPins,
extractInstallPins,
checkReadmeLanguage,
@ -934,6 +935,148 @@ test('newest is the highest version, not the last element of an unsorted list',
assert.equal(f.some((x) => x.code === 'TAG-ANNOTATED-HISTORY'), true);
});
// ------------------------------------------------------------- tag signing
//
// Built on an operator decision (2026-08-13): the org signs tags FORWARD. The
// decision rule was not the adoption rate — it was "would a public AAA+ repo do
// this", which is a yes. 0 of 158 signed is what you expect of a practice not
// yet adopted, and letting that zero veto the check would make the gate ratify
// the status quo it exists to move.
//
// The measurement decides the SCOPE, not the yes. Across 20 clones: 158 `v*`
// tags, 144 annotated, 14 lightweight, 0 signed, and NOT ONE tag dated after the
// decision. So a policy DATE fires zero findings on day one and acquires teeth
// at each repo's next release — which is how signing is actually adopted, never
// by rewriting published history.
//
// Two exclusions keep this from double-counting a defect that already has an
// owner: a lightweight tag has no tag object to carry a signature at all
// (TAG-ANNOTATED owns it), and a tag predating the policy was correct when it
// was cut.
const SIGN_REG = { tags_signed_from: '2026-08-13' };
test('an unsigned newest tag cut under the policy is an ERROR', () => {
const f = checkTagSigned({ tagObjects: [
{ name: 'v1.0.0', annotated: true, signed: true, date: '2026-08-14' },
{ name: 'v1.1.0', annotated: true, signed: false, date: '2026-08-15' },
] }, SIGN_REG);
const hit = f.find((x) => x.code === 'TAG-SIGNED');
assert.equal(hit.level, 'ERROR');
assert.equal(hit.bucket, 'weakening');
assert.match(hit.msg, /v1\.1\.0/);
});
test('all in-scope tags signed is an OK', () => {
const f = checkTagSigned({ tagObjects: [
{ name: 'v1.0.0', annotated: true, signed: true, date: '2026-08-14' },
{ name: 'v1.1.0', annotated: true, signed: true, date: '2026-08-15' },
] }, SIGN_REG);
assert.equal(f.some((x) => x.level === 'ERROR' || x.level === 'WARN'), false);
assert.equal(f.some((x) => x.code === 'TAGS-SIGNED'), true);
});
// The same split TAG-ANNOTATED makes, for the same reason: the newest can be
// re-cut, the older ones can only be "fixed" by force-moving a published ref.
test('older unsigned in-scope tags are ONE aggregated WARN, never one per tag', () => {
const f = checkTagSigned({ tagObjects: [
{ name: 'v1.0.0', annotated: true, signed: false, date: '2026-08-14' },
{ name: 'v1.1.0', annotated: true, signed: false, date: '2026-08-15' },
{ name: 'v1.2.0', annotated: true, signed: true, date: '2026-08-16' },
] }, SIGN_REG);
const warns = f.filter((x) => x.code === 'TAG-SIGNED-HISTORY');
assert.equal(warns.length, 1);
assert.equal(warns[0].level, 'WARN');
assert.match(warns[0].msg, /2/);
assert.equal(f.some((x) => x.level === 'ERROR'), false);
});
// This is the test that keeps the check from failing 20 correct repositories on
// the day the policy lands. Every tag in the corpus predates it.
test('tags predating the policy are never judged — and say so as an OK, not silence', () => {
const f = checkTagSigned({ tagObjects: [
{ name: 'v0.9.0', annotated: true, signed: false, date: '2026-08-09' },
{ name: 'v1.0.0', annotated: true, signed: false, date: '2026-08-12' },
] }, SIGN_REG);
assert.equal(f.some((x) => x.level === 'ERROR' || x.level === 'WARN'), false);
const ok = f.find((x) => x.code === 'TAG-SIGNED-PREPOLICY');
assert.equal(ok.level, 'OK');
assert.match(ok.msg, /2/);
assert.match(ok.msg, /2026-08-13/);
});
test('a tag cut exactly ON the policy date is in scope — the policy starts that day', () => {
const f = checkTagSigned({ tagObjects: [
{ name: 'v1.0.0', annotated: true, signed: false, date: '2026-08-13' },
] }, SIGN_REG);
assert.equal(f.find((x) => x.code === 'TAG-SIGNED').level, 'ERROR');
});
// A lightweight tag has no tag object, so it cannot carry a signature — there
// is no version of it that passes this check while staying lightweight. Firing
// here would report ONE defect twice under two codes; TAG-ANNOTATED already
// owns it, and the skip names that owner rather than going quiet.
test('a lightweight tag is a byDesign SKIP that names TAG-ANNOTATED as the owner', () => {
const f = checkTagSigned({ tagObjects: [
{ name: 'v1.0.0', annotated: false, signed: false, date: '2026-08-14' },
] }, SIGN_REG);
assert.equal(f.some((x) => x.level === 'ERROR' || x.level === 'WARN'), false);
const skip = f.find((x) => x.code === 'TAG-SIGNED-LIGHTWEIGHT');
assert.equal(skip.level, 'SKIP');
assert.equal(skip.skip, 'byDesign');
assert.match(skip.msg, /TAG-ANNOTATED/);
});
// The engine must hold no org decision of its own — the same rule that keeps
// the org-profile exemption a register flag rather than `if (klass === …)`.
// notRun, not byDesign: an operator adding the key turns this into a verdict.
test('no policy in the register is a notRun SKIP — the engine never assumes the org signs', () => {
const f = checkTagSigned({ tagObjects: [
{ name: 'v1.0.0', annotated: true, signed: false, date: '2026-08-14' },
] }, {});
assert.equal(f.length, 1);
assert.equal(f[0].level, 'SKIP');
assert.equal(f[0].skip, 'notRun');
assert.equal(f[0].code, 'TAG-SIGNED-NOPOLICY');
});
test('a repo with no tags has nothing to judge — OK, not SKIP', () => {
const f = checkTagSigned({ tagObjects: [] }, SIGN_REG);
assert.equal(f.length, 1);
assert.equal(f[0].level, 'OK');
assert.equal(f[0].code, 'TAG-SIGNED-NONE');
});
// An undatable tag cannot be PROVEN to fall under the policy, and a gate that
// fails a correct repository on a missing field is the mechanism that gets
// gates switched off. Absence falls to the safe side here — unlike an untagged
// SKIP, where the loud side is the safe one.
test('a tag with no readable date is out of scope, never assumed post-policy', () => {
const f = checkTagSigned({ tagObjects: [
{ name: 'v1.0.0', annotated: true, signed: false },
] }, SIGN_REG);
assert.equal(f.some((x) => x.level === 'ERROR' || x.level === 'WARN'), false);
});
// CLAUDE.md fixes this scope in advance: `git cat-file tag` reads the signature
// block from the clone, so the check answers *is it signed* and must never
// claim the forge vouches for it — that needs a key registered there.
test('no finding claims the forge verified anything', () => {
const f = checkTagSigned({ tagObjects: [
{ name: 'v1.0.0', annotated: true, signed: false, date: '2026-08-14' },
{ name: 'v1.1.0', annotated: true, signed: false, date: '2026-08-15' },
] }, SIGN_REG);
for (const x of f) assert.equal(/verified|vouch|trusted|valid signature/i.test(x.msg), false);
});
test('newest in scope is the highest version, not the last element of an unsorted list', () => {
const f = checkTagSigned({ tagObjects: [
{ name: 'v10.0.0', annotated: true, signed: true, date: '2026-08-14' },
{ name: 'v9.0.0', annotated: true, signed: false, date: '2026-08-15' },
] }, SIGN_REG);
assert.equal(f.some((x) => x.code === 'TAG-SIGNED'), false);
assert.equal(f.some((x) => x.code === 'TAG-SIGNED-HISTORY'), true);
});
// ---------------------------------------------------------------- dead pins
//
// Reported by org-ops (census 08) and re-measured here against the FORGE, not