// lib/gold-consistency.mjs — G3 (programdok §8): deterministisk gull-intern- // konsistens-vakt. Fanger FP1-klassen (Spor 2b): et claim med verdict=correct // hvis egen `notes` innrømmer at den påståtte verdien ikke er bekreftet mot // kilden («uverifisert / illustrativ / ikke i kilden»). Verdikten sier «matcher // kilden»; noten sier det motsatte — en selvmotsigende annotasjon. // // Tripwire-design: brede markører for recall, presisjon via en eksplisitt // `consistency_waiver`-escape-hatch (menneske-i-loop §4c). Linten klassifiserer // ikke — den surfacer kandidater et menneske enten relabel-er eller waiver-er // med begrunnelse. Ren funksjon, ingen I/O; testet i tests/kb-eval/. // Markører som — i en note festet til et verdict=correct-claim — innrømmer at // den påståtte verdien IKKE er bekreftet mot den siterte kilden. Holdt brede med // vilje (recall); falske positive absorberes av waiver-hatchen, ikke av en // perfekt regex. Hver er en delstreng matchet case-insensitivt. export const CONTRADICTION_MARKERS = [ 'uverifisert', 'ikke verifiser', // ikke verifisert / ikke verifiserbar 'kan ikke verifiser', 'illustrativ', // illustrativt / illustrative 'ikke i kilden', 'ikke nevnt i kild', 'kilden oppgir ikke', 'ikke bekreftet', 'ubekreftet', 'source_silent', 'plassholder', 'placeholder', 'eksempel-tall', 'eksempeltall', ]; // Verdikter som hevder samsvar med dagens kilde. Kun disse kan motsi en // «ikke bekreftet»-note: outdated/wrong/unsourced anerkjenner allerede ikke-match. const ASSERTS_SOURCE_MATCH = new Set(['correct']); export function findContradictionMarkers(notes) { if (!notes || typeof notes !== 'string') return []; const lc = notes.toLowerCase(); return CONTRADICTION_MARKERS.filter((m) => lc.includes(m)); } function hasWaiver(claim) { return typeof claim.consistency_waiver === 'string' && claim.consistency_waiver.trim().length > 0; } // Returnerer { ok, flagged: [{ id, verdict, notes, markers }] }. // Et claim flagges når: verdikten hevder kilde-samsvar, noten bærer en // motsigelses-markør, og det ikke finnes et ikke-tomt consistency_waiver. export function lintGoldConsistency(goldSet) { const claims = (goldSet && goldSet.claims) || []; const flagged = []; for (const c of claims) { if (!ASSERTS_SOURCE_MATCH.has(c.verdict)) continue; if (hasWaiver(c)) continue; const markers = findContradictionMarkers(c.notes); if (markers.length) { flagged.push({ id: c.id, verdict: c.verdict, notes: c.notes, markers }); } } return { ok: flagged.length === 0, flagged }; }