100 lines
4.9 KiB
JavaScript
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('; '),
|
|
);
|
|
});
|