/** * Registry invariants for the finding-code scheme (M-BUG-28). * * These are blanket assertions over the whole registry, never a relation between * two chosen entries: a per-entry check goes green on a partial conversion, which * is the failure mode #51/#57/#58 kept reproducing. */ import { describe, it } from 'node:test'; import assert from 'node:assert/strict'; import { FINDING_CODES, RETIRED_CODES, codeNumber, findingId, allFindingIds } from '../../scanners/lib/finding-codes.mjs'; import { GAP_CHECKS, LEVERS } from '../../scanners/feature-gap-scanner.mjs'; describe('finding-code registry', () => { it('gives every check a distinct number within its scanner', () => { const collisions = []; for (const [scanner, table] of Object.entries(FINDING_CODES)) { const seen = new Map(); for (const [key, n] of Object.entries(table)) { if (seen.has(n)) collisions.push(`${scanner}: ${seen.get(n)} and ${key} both claim ${n}`); seen.set(n, key); } } assert.deepEqual(collisions, []); }); it('uses positive integers only', () => { const bad = []; for (const [scanner, table] of Object.entries(FINDING_CODES)) { for (const [key, n] of Object.entries(table)) { if (!Number.isInteger(n) || n < 1) bad.push(`${scanner}.${key} = ${n}`); } } assert.deepEqual(bad, []); }); it('never lets a retired key stay active', () => { const resurrected = []; for (const [scanner, keys] of Object.entries(RETIRED_CODES)) { for (const key of keys) { if (FINDING_CODES[scanner] && key in FINDING_CODES[scanner]) { resurrected.push(`${scanner}.${key}`); } } } assert.deepEqual(resurrected, []); }); it('never reissues a retired number', () => { // The whole point of the tombstone: D1 retired GAP t3_8 and shifted three // live IDs down by one. Reusing a retired number would repeat that silently. assert.ok(RETIRED_CODES.GAP.includes('t3_8'), 'D1 tombstone missing'); }); it('declares exactly the GAP dimensions the scanner ships, plus its levers', () => { const declared = new Set(Object.keys(FINDING_CODES.GAP)); const shipped = GAP_CHECKS.map((g) => g.id); const missing = shipped.filter((id) => !declared.has(id)); assert.deepEqual(missing, [], 'a GAP dimension has no declared code'); // Derived from the scanner, not listed again here: a hand-written copy of // this list is the drift class the registry exists to prevent. const levers = Object.values(LEVERS).map((l) => l.code); const orphans = [...declared].filter((k) => !shipped.includes(k) && !levers.includes(k)); assert.deepEqual(orphans, [], 'a declared GAP code matches no shipped dimension'); }); it('throws on an undeclared code instead of inventing an ID', () => { assert.throws(() => codeNumber('GAP', 'nope'), /undeclared check/); assert.throws(() => codeNumber('NOSUCH', 't1_1'), /unknown scanner/); assert.throws(() => codeNumber('GAP', undefined), /missing "code"/); }); it('names retirement explicitly when a retired key is used', () => { assert.throws(() => codeNumber('GAP', 't3_8'), /RETIRED/); }); it('renders the published ID format', () => { assert.equal(findingId('GAP', 't1_1'), 'CA-GAP-001'); assert.equal(findingId('PLH', 'skills-array-entry'), 'CA-PLH-016'); assert.ok(allFindingIds().has('CA-SKL-003')); }); }); describe('published finding IDs (pinned exhaustively — README is a contract)', () => { // Every number that shipped in README, CLAUDE.md or command copy before the // registry existed. Spot-checking these would let a renumber through. const PUBLISHED = [ ['SKL', 'description-over-cap', 'CA-SKL-001'], ['SKL', 'aggregate-listing-budget', 'CA-SKL-002'], ['SKL', 'oversized-body', 'CA-SKL-003'], ['OST', 'strips-coding-instructions', 'CA-OST-001'], ['OST', 'plugin-forces-style', 'CA-OST-002'], ['OST', 'style-not-found', 'CA-OST-003'], ['TOK', 'volatile-top', 'CA-TOK-001'], ['TOK', 'redundant-permissions', 'CA-TOK-002'], ['TOK', 'deep-import-chain', 'CA-TOK-003'], ['TOK', 'mcp-schema-budget', 'CA-TOK-005'], ['TOK', 'mcp-schema-deferral', 'CA-TOK-006'], ['PLH', 'plugin-json-shadows-default', 'CA-PLH-015'], ['PLH', 'skills-array-entry', 'CA-PLH-016'], ['OPT', 'procedure-should-be-skill', 'CA-OPT-001'], ['AGT', 'description-bloat', 'CA-AGT-001'], ['AGT', 'aggregate-listing-budget', 'CA-AGT-002'], ['CPS', 'volatile-in-prefix', 'CA-CPS-001'], ['COL', 'skill-user-vs-plugin', 'CA-COL-001'], ]; for (const [scanner, key, expected] of PUBLISHED) { it(`${expected} still names ${key}`, () => { assert.equal(findingId(scanner, key), expected); }); } });