// tests/kb-eval/test-gold-consistency.test.mjs // G3 (programdok §8 mekanisme-gap-register) — deterministisk gull-intern-konsistens-vakt. // // Feilklassen (FP1, Spor 2b): et gull-claim bærer verdict=correct mens dens egen // `notes` innrømmer at den påståtte verdien IKKE er bekreftet mot kilden // («uverifisert / illustrativ / ikke i kilden»). Det er en selvmotsigende // annotasjon — verdikten sier «matcher kilden i dag», noten sier «gjorde det ikke». // Reconciliation fant FP1 dyrt (live re-fetch). En deterministisk lint fanger // klassen gratis ved hvert gull-bygg. // // Designet er en TRIPWIRE, ikke en auto-klassifiserer: brede markører (recall), // presisjon via en eksplisitt `consistency_waiver`-escape-hatch (menneske-i-loop // §4c — relabel ELLER waiver-med-begrunnelse, aldri auto-fiks). Et waiver er den // varige menneske-bekreftelses-loggen. 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'; import { lintGoldConsistency, findContradictionMarkers, } from '../../scripts/kb-eval/lib/gold-consistency.mjs'; const __dirname = dirname(fileURLToPath(import.meta.url)); const PLUGIN_ROOT = join(__dirname, '..', '..'); // --- (1) marker-deteksjon ------------------------------------------------------ test('findContradictionMarkers detects unverified/illustrative admissions', () => { assert.ok(findContradictionMarkers('RTO 15 min illustrativ.').length > 0); assert.ok(findContradictionMarkers("'mai 2025' ikke i kilden.").length > 0); assert.ok(findContradictionMarkers('Tallet er uverifisert mot kilden.').length > 0); assert.ok(findContradictionMarkers('kan ikke verifisere mot kilden').length > 0); // clean note → no markers assert.equal(findContradictionMarkers('GA-dato og kontekstvindu matcher modell-siden eksakt.').length, 0); assert.equal(findContradictionMarkers('').length, 0); assert.equal(findContradictionMarkers(undefined).length, 0); }); // --- (2) lint over en konstruert gull-fixture ---------------------------------- const fixture = { claims: [ // FP1-klasse: correct, men noten innrømmer verdien ikke er i kilden, ingen waiver → FLAGGET { id: 'a#1', verdict: 'correct', notes: "'11000+ modeller' ikke i kilden; '40+ regioner' finnes ikke." }, // correct + illustrativ-innrømmelse, ingen waiver → FLAGGET { id: 'a#2', verdict: 'correct', notes: 'RTO 15 min illustrativ.' }, // correct + selvmotsigende note MEN waiver til stede → IKKE flagget { id: 'a#3', verdict: 'correct', notes: '~0-tall illustrative men kapabiliteten korrekt.', consistency_waiver: 'FP5: kapabilitet grunnet, tall illustrativt.' }, // correct + ren note → IKKE flagget { id: 'a#4', verdict: 'correct', notes: 'Matcher modell-siden eksakt.' }, // verdict=wrong med samme markør → IKKE flagget (verdikten er allerede uenig med kilden) { id: 'a#5', verdict: 'wrong', notes: 'tallet er ikke i kilden.' }, // verdict=outdated → IKKE flagget (anerkjenner allerede ikke-match) { id: 'a#6', verdict: 'outdated', notes: 'verdien er ikke i kilden lenger.' }, // verdict=unsourced → IKKE flagget (ærlig merket) { id: 'a#7', verdict: 'unsourced', notes: 'kan ikke verifisere; pris JS-rendret.' }, // tom waiver (whitespace) teller ikke som waiver → FLAGGET { id: 'a#8', verdict: 'correct', notes: 'uverifisert.', consistency_waiver: ' ' }, ], }; test('lintGoldConsistency flags only un-waived contradictory correct-claims', () => { const r = lintGoldConsistency(fixture); const flaggedIds = r.flagged.map((f) => f.id).sort(); assert.deepEqual(flaggedIds, ['a#1', 'a#2', 'a#8']); assert.equal(r.ok, false); }); test('lintGoldConsistency returns ok:true on a fully-waived/clean set', () => { const clean = { claims: [ { id: 'b#1', verdict: 'correct', notes: 'matcher kilden eksakt.' }, { id: 'b#2', verdict: 'correct', notes: 'illustrativt tall.', consistency_waiver: 'kapabilitet grunnet.' }, { id: 'b#3', verdict: 'wrong', notes: 'ikke i kilden.' }, ], }; const r = lintGoldConsistency(clean); assert.equal(r.ok, true); assert.equal(r.flagged.length, 0); }); // --- (3) live-gull-vakt: den shippede fasiten har INGEN uwaived motsigelse ------ // Dette kobler suiten til gold-correctness-set.json — invarianten «kjøres på // fremtidige gull-bygg». RØD til de 3 nåværende treffene er waivet/adjudisert. test('shipped gold-correctness-set.json is internally consistent (no un-waived contradictions)', () => { const gold = JSON.parse( readFileSync(join(PLUGIN_ROOT, 'scripts/kb-eval/data/gold-correctness-set.json'), 'utf8'), ); const r = lintGoldConsistency(gold); assert.equal( r.ok, true, `gull har ${r.flagged.length} uwaived selvmotsigende correct-claim(s): ` + r.flagged.map((f) => `${f.id} [${f.markers.join(',')}]`).join('; '), ); });