fix(engine): TAG-ANNOTATED never consulted the register for the newest tag

Reported by catalog (coord, 2026-08-17): register/repos.json has listed
ktg-plugin-marketplace v7.7.2 under tags_lightweight_accepted since the
0.11.2 release (2026-08-14), but `accepted` was only ever applied to
tags.slice(0, -1) — the newest tag is excluded from that slice by
construction, so the entry was dead weight from the day it was written.

checkTagIntegrity now checks the newest tag against `accepted` too, emitting
a distinct TAG-ANNOTATED-ACCEPTED-NEWEST OK instead of silently doing
nothing when the register names it exactly. Acceptance is per-tag-name, not
a standing exemption: a new lightweight tag that becomes newest afterwards
is still judged (tested).

TDD: two new tests written failing first (an existing test's title claimed
"cannot be accepted away" but never actually passed a matching `name`, so it
was accidentally still green either way — reworded to test what it actually
covers). Verified against a fresh clone of ktg-plugin-marketplace: the full
finding set now reads OK TAG-ANNOTATED-ACCEPTED-NEWEST + OK
TAG-ANNOTATED-ACCEPTED(7 older) instead of ERROR, with no knock-on effect on
TAG-SIGNED (all 9 tags predate the signing policy). Register comment updated
to record the three-day dead-weight window rather than silently correcting
it — a decision that turns out wrong is worse than no record.

247 tests pass (245 + 2 new).
This commit is contained in:
Kjell Tore Guttormsen 2026-08-18 16:59:00 +02:00
commit aee0f28539
3 changed files with 72 additions and 12 deletions

View file

@ -223,6 +223,15 @@
"itself enforces that, since a new lightweight newest tag is not on this",
"list and fires ERROR.",
"",
"This entry was DEAD WEIGHT from 2026-08-14 to 2026-08-18: `accepted` was",
"only ever consulted for tags OLDER than newest, so v7.7.2 kept firing",
"TAG-ANNOTATED ERROR the whole time — the exact 'we decided this' vs.",
"'nobody looked' collapse this axis exists to prevent, one level down.",
"Caught and reported by the catalog itself (coord, 2026-08-17), fixed in",
"`checkTagIntegrity` (repo-standard, 2026-08-18): the accepted set is now",
"checked against the newest tag too, emitting a distinct",
"`TAG-ANNOTATED-ACCEPTED-NEWEST` OK rather than silently doing nothing.",
"",
"The two slash-named `config-audit/v*` tags on that forge are OUT OF SCOPE",
"by construction, not omitted by a fetch gap: the engine reads",
"`refs/tags/v*` — the repo's own version line — and a namespaced",

View file

@ -708,15 +708,27 @@ export function checkTagIntegrity({ tagObjects, name }, register) {
const findings = [];
const newest = tags[tags.length - 1];
if (!newest.annotated) {
findings.push({
level: 'ERROR',
code: 'TAG-ANNOTATED',
bucket: 'broken',
msg: `newest tag \`${newest.name}\` is lightweight — it can be moved to another commit with no record that it ever pointed elsewhere, and the catalog pins releases by tag. Re-cut it annotated: \`git tag -a -f ${newest.name} ${newest.name}^{}\`.`,
});
}
const accepted = new Set(register?.tags_lightweight_accepted?.[name] ?? []);
if (!newest.annotated) {
if (accepted.has(newest.name)) {
// Named acceptance, not a standing exemption: only THIS exact tag is
// excused, so a later real release still fires ERROR the moment it
// becomes newest and isn't itself on the list (proven by the sibling
// test below).
findings.push({
level: 'OK',
code: 'TAG-ANNOTATED-ACCEPTED-NEWEST',
msg: `newest tag \`${newest.name}\` is lightweight, but the register accepts it by name as an exception to the newest-tag rule — a safe remedy exists (\`git tag -a -f\`) but costs more than the finding for this specific tag. Any OTHER tag that becomes newest is still judged.`,
});
} else {
findings.push({
level: 'ERROR',
code: 'TAG-ANNOTATED',
bucket: 'broken',
msg: `newest tag \`${newest.name}\` is lightweight — it can be moved to another commit with no record that it ever pointed elsewhere, and the catalog pins releases by tag. Re-cut it annotated: \`git tag -a -f ${newest.name} ${newest.name}^{}\`.`,
});
}
}
const olderLightweight = tags.slice(0, -1).filter((t) => !t.annotated);
const older = olderLightweight.filter((t) => !accepted.has(t.name));
const excused = olderLightweight.filter((t) => accepted.has(t.name));
@ -842,7 +854,7 @@ export function checkTagSigned({ tagObjects }, register) {
level: 'SKIP',
skip: 'byDesign',
code: 'TAG-SIGNED-LIGHTWEIGHT',
msg: `${lightweight.length} tag(s) cut under the policy are lightweight (${lightweight.map((t) => t.name).join(', ')}) — a lightweight tag has no tag object to carry a signature, so signing is not a remedy it has. TAG-ANNOTATED owns that finding; this check declines rather than report one defect twice.`,
msg: `${lightweight.length} tag(s) cut under the policy are lightweight (${lightweight.map((t) => t.name).join(', ')}) — a lightweight tag has no tag object to carry a signature, so signing is not a remedy it has. TAG-ANNOTATED owns the verdict on these — an ERROR, or an OK if the register names one as an accepted exception; this check declines rather than report one defect twice.`,
});
}

View file

@ -903,15 +903,54 @@ test('an unaccepted lightweight tag still fires, and the WARN counts only the un
});
// The newest tag is what a consumer resolves today and what an operator can
// re-cut at no cost. It is the one lightweight tag with a safe remedy, so it
// is the one that cannot be accepted away.
test('the NEWEST lightweight tag is still an ERROR even when the register accepts its name', () => {
const reg = { tags_lightweight_accepted: { alpha: ['v2.0.0'] } };
const f = checkTagIntegrity({ tagObjects: [{ name: 'v1.0.0', annotated: true }, { name: 'v2.0.0', annotated: false }] }, reg);
// re-cut at no cost, so acceptance is never inherited from a repo merely
// having OTHER accepted entries — only an exact name match on THIS tag excuses
// it (the exception directly below).
test('an unaccepted newest lightweight tag is still an ERROR, even when the register has other entries for this repo', () => {
const reg = { tags_lightweight_accepted: { alpha: ['v9.9.9'] } };
const f = checkTagIntegrity({ tagObjects: [{ name: 'v1.0.0', annotated: true }, { name: 'v2.0.0', annotated: false }], name: 'alpha' }, reg);
const hit = f.find((x) => x.code === 'TAG-ANNOTATED');
assert.equal(hit.level, 'ERROR');
});
// A tag that sorts "newest" by version string without being the newest
// RELEASE — a monorepo-era tag predating a split, consumed by nothing — has a
// real safe remedy (`git tag -a -f`, same commit) that still costs more than
// the finding when nothing resolves it. The register names the tag by EXACT
// NAME, never "this repo's newest is always excused" — caught the moment
// `ktg-plugin-marketplace v7.7.2` was found dead weight: the register had
// accepted it since 2026-08-14 (repos.json `tags_lightweight_accepted`) but
// `accepted` was only ever consulted for tags OLDER than newest, so the
// recorded decision could never take effect. Reported by the catalog
// (coord, 2026-08-17): measured against the real register and real tag data,
// exactly two findings — ERROR TAG-ANNOTATED on the (then-)inert acceptance,
// OK TAG-ANNOTATED-ACCEPTED for the 7 older accepted tags.
test('the newest lightweight tag IS excused when the register names it exactly, with a distinct OK code', () => {
const reg = { tags_lightweight_accepted: { alpha: ['v2.0.0'] } };
const f = checkTagIntegrity({ tagObjects: [{ name: 'v1.0.0', annotated: true }, { name: 'v2.0.0', annotated: false }], name: 'alpha' }, reg);
assert.equal(f.some((x) => x.code === 'TAG-ANNOTATED'), false);
const ok = f.find((x) => x.code === 'TAG-ANNOTATED-ACCEPTED-NEWEST');
assert.equal(ok.level, 'OK');
assert.match(ok.msg, /v2\.0\.0/);
});
// Acceptance names ONE tag, not a standing exemption for "whatever is newest".
// A real new tag cut after the accepted one is still judged the moment it
// becomes newest and isn't itself on the list — exactly what the register's
// own comment promises: "a new lightweight newest tag is not on this list and
// fires ERROR".
test('a NEW lightweight tag cut after an accepted newest is still judged', () => {
const reg = { tags_lightweight_accepted: { alpha: ['v2.0.0'] } };
const f = checkTagIntegrity({ tagObjects: [
{ name: 'v1.0.0', annotated: true },
{ name: 'v2.0.0', annotated: false },
{ name: 'v3.0.0', annotated: false },
], name: 'alpha' }, reg);
const hit = f.find((x) => x.code === 'TAG-ANNOTATED');
assert.equal(hit.level, 'ERROR');
assert.match(hit.msg, /v3\.0\.0/);
});
test('acceptance is per repo — a name accepted for one repo does not excuse another', () => {
const tagObjects = [{ name: 'v0.1.0', annotated: false }, { name: 'v1.0.0', annotated: true }];
const f = checkTagIntegrity({ tagObjects, name: 'beta' }, ACCEPT_REG);