// tests/kb-update/test-discover-invariant.test.mjs // Architecture-invariant guard (STATE.md §83 / roadmap §159): the detection // script discover-new-urls.mjs must NEVER import write-utils. It only READS the // decision ledger to filter; all writes go through the operator gate. saveReport // (its own discovery-report.json) is allowed — that is detection's own output, // not a KB/registry mutation. import { test } from 'node:test'; import assert from 'node:assert/strict'; import { readFileSync } from 'node:fs'; import { dirname, join } from 'node:path'; import { fileURLToPath } from 'node:url'; const __dirname = dirname(fileURLToPath(import.meta.url)); const DISCOVER = join(__dirname, '..', '..', 'scripts', 'kb-update', 'discover-new-urls.mjs'); const src = readFileSync(DISCOVER, 'utf8'); // Sjekkene er IMPORT-spesifikke med vilje: en invariant-guard som brytes av en // kommentar som *beskriver* invarianten er verdiløs. Vi gransker import-linjer. const importLines = src.split('\n').filter((l) => /^\s*import\b/.test(l)).join('\n'); test('discover does NOT import the ledger write path (saveDecisions)', () => { assert.doesNotMatch(importLines, /\bsaveDecisions\b/); }); test('discover does NOT import the registry write path (saveRegistry)', () => { assert.doesNotMatch(importLines, /\bsaveRegistry\b/); }); test('discover does NOT import atomic-write', () => { assert.doesNotMatch(importLines, /from\s+'[^']*atomic-write[^']*'/); }); test('discover does NOT import backup', () => { assert.doesNotMatch(importLines, /from\s+'[^']*\/backup\.mjs'/); }); test('discover DOES read the ledger (loadDecisions from decisions-io)', () => { assert.match(src, /import \{[^}]*\bloadDecisions\b[^}]*\} from '\.\/lib\/decisions-io\.mjs'/); }); test('discover actually applies the dedup filter (isDecided)', () => { assert.match(src, /isDecided\(/); });