ms-ai-architect/tests/kb-eval/test-gold-consistency.test.mjs

100 lines
4.9 KiB
JavaScript

// 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('; '),
);
});