From 9b147b470fc9750fb78f2e95ea9c4eda1a1e0eda Mon Sep 17 00:00:00 2001 From: Kjell Tore Guttormsen Date: Tue, 30 Jun 2026 09:55:15 +0200 Subject: [PATCH] =?UTF-8?q?feat(ms-ai-architect):=20G3=20gull-intern-konsi?= =?UTF-8?q?stens-lint=20+=20G4=20build-instruks-policy=20=E2=80=94=20?= =?UTF-8?q?=C2=A78=20gap-lukk=20[skip-docs]?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/ref-kb-correctness-program-2026-06.md | 9 +- scripts/kb-eval/build-gold-set.mjs | 34 ++++++ .../kb-eval/data/gold-correctness-set.json | 9 +- scripts/kb-eval/lib/gold-consistency.mjs | 62 +++++++++++ scripts/kb-eval/lint-gold-consistency.mjs | 47 ++++++++ tests/kb-eval/test-gold-consistency.test.mjs | 100 ++++++++++++++++++ 6 files changed, 256 insertions(+), 5 deletions(-) create mode 100644 scripts/kb-eval/lib/gold-consistency.mjs create mode 100644 scripts/kb-eval/lint-gold-consistency.mjs create mode 100644 tests/kb-eval/test-gold-consistency.test.mjs diff --git a/docs/ref-kb-correctness-program-2026-06.md b/docs/ref-kb-correctness-program-2026-06.md index c21889c..e8e2091 100644 --- a/docs/ref-kb-correctness-program-2026-06.md +++ b/docs/ref-kb-correctness-program-2026-06.md @@ -98,7 +98,12 @@ Status-nøkkel: 🔴 ikke startet · 🟡 pågår · 🟢 lukket. |---|---|---|---|---|---| | **G1** | Judgen er ikke herdet mot de 8 dokumenterte feilmodusene (`source_silent`-maskerer-fravær, legacy-rad-match, ramme-skifte-tall-overlever, nedre-grense-understatement, eksakt-streng-pedanteri, taksonomi-nyanse, kapabilitet-bom) | Judge-FN/FP påvist i Spor 2b (8 mål, se `ref-kb-gold-reconciliation-2026-06.md`) | **Spor 2a** — judge-prompt-v3, MÅLT single vs v3/ensemble på herdet gull; adopter kun ved målt forbedring (ad-hoc-patch overfitter + bytter P/R) | 🔴 | Spor 1 korpus-pass (judgen brukes i ~2700 fetches) | | **G2** | Herdet judge er ikke wired inn i Port 2 (born-verified create-guard) + Port 3 (kadens) — uten innplugging binder ikke hardningen mekanisk | Re-introdusert drift ved nye/regenererte filer + kadens-bom | Del av Spor 2a→3: bytt ut v2 med v3 i `transform.mjs`-judge-passet + kadens-runneren | 🔴 (avh. G1) | Spor 1 | -| **G3** | Ingen deterministisk gull-intern-konsistens-vakt (`verdict=correct` mens egen `notes` sier «uverifisert/illustrativ») | Gull-labeling-feil av FP1-klassen (selvmotsigende annotasjon) | Liten TDD-lint over `gold-correctness-set.json` (+ kjøres på fremtidige gull-bygg) | 🔴 | Spor 1 (nytt gull bygges) / §7 friskt utvalg | -| **G4** | Nedre-grense-policyen lever kun i prosa (denne dok + reconciliation-logg) — ikke kodet i judge-prompt ELLER `build-gold-set`-instruks | Re-introdusert nedre-grense-ambivalens i fremtidige gull-bygg + judge-kjøringer | Kod policyen inn i judge-prompt-v3 (G1) + build-gold-set-instruks | 🔴 | Spor 1 / §7 friskt utvalg | +| **G3** | Ingen deterministisk gull-intern-konsistens-vakt (`verdict=correct` mens egen `notes` sier «uverifisert/illustrativ») | Gull-labeling-feil av FP1-klassen (selvmotsigende annotasjon) | Liten TDD-lint over `gold-correctness-set.json` (+ kjøres på fremtidige gull-bygg) | 🟢 **lukket 2026-06-30** | Spor 1 (nytt gull bygges) / §7 friskt utvalg | +| **G4** | Nedre-grense-policyen lever kun i prosa (denne dok + reconciliation-logg) — ikke kodet i judge-prompt ELLER `build-gold-set`-instruks | Re-introdusert nedre-grense-ambivalens i fremtidige gull-bygg + judge-kjøringer | Kod policyen inn i judge-prompt-v3 (G1) + build-gold-set-instruks | 🟡 **build-instruks ✅ (2026-06-30); judge-prompt-v3-del i G1** | Spor 1 / §7 friskt utvalg | **Ikke mekanisme-gap, men sporet backlog (innhold, ikke loop):** reference-`.md`-fil-fiksene fra Spor 2b (FP1 11000+/40+, FP2 «kun», FP6 Preview/Norway-East, FN2–FN6 utdaterte tall) er **Spor 0/1**-innholdsarbeid — pekt per-claim i `notes`, ikke gjentakelses-mekanisme. Føres i Spor 0-manifest / Spor 1-korpus-pass, ikke her. + +### Lukke-logg + +- **G3 🟢 lukket (2026-06-30).** Deterministisk lint `lib/gold-consistency.mjs` + CLI `lint-gold-consistency.mjs` + TDD `tests/kb-eval/test-gold-consistency.test.mjs` (4 tester); wiret som hard gate på `build-gold-set.mjs --write` (dry-run forhåndsviser ikke-fatalt). De 3 nåværende shippet-gull-treffene resolvert med `consistency_waiver` (én — `reserved-capacity-planning#5` — live kilde-bekreftet FØR waiver, verifiseringsplikt). **Bevist mot FP1:** rebuild fra batch-returns flagger `azure-ai-foundry.md#2` (verdict=correct, note «11000+/40+ uverifisert») — nøyaktig claimen Spor 2b fant via dyr live re-fetch. Linten fanger klassen gratis ved bygg. +- **G4 🟡 build-instruks ✅ (2026-06-30).** Nedre-grense-policyen + konsistens-policyen kodet inn i `build-gold-set.mjs` `_meta.lower_bound_policy` / `_meta.consistency_policy` (instruks subagenter følger ved fremtidig gull-bygg). Resterende halvdel — policyen inn i `judge-claim-prompt-v3.md` — lukkes i G1 når v3 forfattes. diff --git a/scripts/kb-eval/build-gold-set.mjs b/scripts/kb-eval/build-gold-set.mjs index 23723d8..e9c422f 100644 --- a/scripts/kb-eval/build-gold-set.mjs +++ b/scripts/kb-eval/build-gold-set.mjs @@ -20,6 +20,7 @@ import fs from 'node:fs'; import path from 'node:path'; import { fileURLToPath } from 'node:url'; import { parseLastUpdated, fileLastmodChanged } from './lib/base-rate.mjs'; +import { lintGoldConsistency } from './lib/gold-consistency.mjs'; const __dirname = path.dirname(fileURLToPath(import.meta.url)); const ROOT = path.resolve(__dirname, '..', '..'); @@ -86,12 +87,39 @@ const goldSet = { raw_returns: 'fase0-returns/batch-*.json', note_evidence_quote: 'Per-claim evidence_quote omitted here to bound size; full verbatim quotes live in the session transcript. notes + evidence_url capture each verdict basis.', + // G4 (programdok §8 / §3-kjennelse 2026-06-30): nedre-grense-policy for + // subagenter som bygger fremtidig gull. En nedre-grense-påstand («100+», + // «200k+») teller som FEIL (outdated/wrong) hvis den GROVT understater + // (>~2x, beslutnings-endrende) den sanne verdien; en STRAM nedre grense (sann + // verdi i samme størrelsesorden) blir stående correct. Anvendt på FN2 (Spor 2b). + lower_bound_policy: + 'Lower-bound claims (e.g. "100+", "200k+"): verdict=outdated/wrong if the bound grossly understates (>~2x, decision-changing) the true value; a tight bound (true value within the same order of magnitude) stays correct.', + // G3 (programdok §8): gull-intern-konsistens. Et verdict=correct-claim hvis + // egen `notes` innrømmer at verdien ikke er bekreftet mot kilden («uverifisert + // / illustrativ / ikke i kilden») MÅ enten relabel-es ELLER bære et + // `consistency_waiver` med begrunnelse. Håndhevet av lint-gold-consistency.mjs. + consistency_policy: + 'verdict=correct claims whose notes admit the value is unverified/illustrative/not-in-source must be relabeled OR carry a consistency_waiver. Enforced by lint-gold-consistency.mjs.', claim_count: claims.length, }, claims, }; +// G3 (programdok §8): gull fødes konsistent. Hard gate kun på --write — et +// nybygget gull med uwaived selvmotsigende correct-claims (verdict=correct mens +// noten innrømmer ikke-bekreftet: «uverifisert/illustrativ/ikke i kilden») nektes +// SKREVET; relabel eller waiver først. Dry-run forhåndsviser (advarer, blokkerer +// ikke). Escape-hatch --allow-inconsistent (samme idiom som +// run-judge-bakeoff --allow-incomplete) for bevisst override. +const consistency = lintGoldConsistency(goldSet); + if (process.argv.includes('--write')) { + if (!consistency.ok && !process.argv.includes('--allow-inconsistent')) { + console.error(`error: ${consistency.flagged.length} uwaived selvmotsigende correct-claim(s) — gull nektes skrevet (G3):`); + for (const f of consistency.flagged) console.error(` - ${f.id} [${f.markers.join(', ')}]: ${f.notes}`); + console.error('Resolver hver (relabel ELLER consistency_waiver), eller --allow-inconsistent for å overstyre.'); + process.exit(2); + } const out = path.join(DATA, 'gold-correctness-set.json'); fs.writeFileSync(out, JSON.stringify(goldSet, null, 2) + '\n'); console.log(`wrote ${out} (${claims.length} claims)`); @@ -101,5 +129,11 @@ if (process.argv.includes('--write')) { const filesUnknown = Object.entries(fileMeta).filter(([, m]) => m.lastmod_changed === null).length; console.log(`claims: ${claims.length} | files: ${Object.keys(fileMeta).length}`); console.log(`file lastmod_changed: true=${filesWithChange} false=${filesNoChange} unknown(null date)=${filesUnknown}`); + if (consistency.ok) { + console.log('G3 gull-konsistens: OK (0 uwaived selvmotsigende correct-claims)'); + } else { + console.log(`G3 gull-konsistens: ⚠ ${consistency.flagged.length} uwaived selvmotsigende correct-claim(s) — --write vil nektes:`); + for (const f of consistency.flagged) console.log(` - ${f.id} [${f.markers.join(', ')}]`); + } console.log('(dry run — pass --write to persist gold-correctness-set.json)'); } diff --git a/scripts/kb-eval/data/gold-correctness-set.json b/scripts/kb-eval/data/gold-correctness-set.json index 1a5e86f..32664ae 100644 --- a/scripts/kb-eval/data/gold-correctness-set.json +++ b/scripts/kb-eval/data/gold-correctness-set.json @@ -3451,7 +3451,8 @@ "evidence_url": "https://learn.microsoft.com/azure/reliability/reliability-ai-search", "lastmod_changed": false, "file_last_updated": "2026-02-01", - "notes": "AI Search single-region; manuell multi-region matcher MS-veiledning. RTO 15 min illustrativ." + "notes": "AI Search single-region; manuell multi-region matcher MS-veiledning. RTO 15 min illustrativ.", + "consistency_waiver": "G3: 'RTO 15 min illustrativ' er en note-aside om et tidstall utenfor den vurderte claim-en (claim = 'ingen built-in cross-region', grunnet mot reliability-ai-search). Kapabiliteten korrekt; ikke FP1." }, { "id": "ms-ai-infrastructure/bcdr/service-level-documentation-dr.md#5", @@ -3477,7 +3478,8 @@ "evidence_url": "https://learn.microsoft.com/azure/cosmos-db/continuous-backup-restore-introduction", "lastmod_changed": false, "file_last_updated": "2026-02-01", - "notes": "Multi-write reell active-active; ~0-tall illustrative men kapabiliteten korrekt." + "notes": "Multi-write reell active-active; ~0-tall illustrative men kapabiliteten korrekt.", + "consistency_waiver": "G3/FP5 (Spor 2b reconciled): multi-region-write active-active grunnet (cosmos-db/distribute-data-globally); '~0'-RTO/RPO-tall illustrative, ikke kilde-siterte. Kapabilitet korrekt — behold correct." }, { "id": "ms-ai-infrastructure/bcdr/service-level-documentation-dr.md#7", @@ -4296,7 +4298,8 @@ "evidence_url": "https://learn.microsoft.com/azure/cost-management-billing/reservations/azure-openai", "lastmod_changed": false, "file_last_updated": "2026-06-01", - "notes": "Cross-model-sharing + per-time matching bekreftet. 'mai 2025' ikke i kilden." + "notes": "Cross-model-sharing + per-time matching bekreftet. 'mai 2025' ikke i kilden.", + "consistency_waiver": "G3: kjerne-kapabilitet (cross-model PTU-deling + per-minutt pro-rata matching) bekreftet live mot Foundry PTU-reservasjons-siden 2026-06-30; 'mai 2025'-ikrafttredelsesdato IKKE i kilden (ikke-materiell, ikke beslutnings-endrende). Behold correct; fjern/merk datoen i .md = Spor 0/1-fil-fiks." }, { "id": "ms-ai-security/cost-optimization/reserved-capacity-planning.md#6", diff --git a/scripts/kb-eval/lib/gold-consistency.mjs b/scripts/kb-eval/lib/gold-consistency.mjs new file mode 100644 index 0000000..9aabdbd --- /dev/null +++ b/scripts/kb-eval/lib/gold-consistency.mjs @@ -0,0 +1,62 @@ +// 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 }; +} diff --git a/scripts/kb-eval/lint-gold-consistency.mjs b/scripts/kb-eval/lint-gold-consistency.mjs new file mode 100644 index 0000000..340bd68 --- /dev/null +++ b/scripts/kb-eval/lint-gold-consistency.mjs @@ -0,0 +1,47 @@ +#!/usr/bin/env node +// lint-gold-consistency.mjs — G3 (programdok §8) CLI: kjør gull-intern- +// konsistens-vakten over gold-correctness-set.json. Tynn wiring; all logikk i +// testet lib/gold-consistency.mjs (samme form som run-judge-bakeoff.mjs). +// +// Flagger verdict=correct-claims hvis egen `notes` innrømmer at verdien ikke er +// bekreftet mot kilden («uverifisert / illustrativ / ikke i kilden») og mangler +// et `consistency_waiver`. Exit 1 hvis noen uwaived → hard gate for gull-bygg. +// +// Usage: +// node scripts/kb-eval/lint-gold-consistency.mjs [--gold ] [--json] + +import fs from 'node:fs'; +import path from 'node:path'; +import { fileURLToPath } from 'node:url'; +import { lintGoldConsistency } from './lib/gold-consistency.mjs'; + +const __dirname = path.dirname(fileURLToPath(import.meta.url)); +const DATA = path.join(__dirname, 'data'); + +function flag(name) { + const i = process.argv.indexOf(name); + return i >= 0 ? process.argv[i + 1] : undefined; +} + +const goldPath = flag('--gold') || path.join(DATA, 'gold-correctness-set.json'); +if (!fs.existsSync(goldPath)) { + console.error(`error: ${goldPath} not found`); + process.exit(2); +} +const gold = JSON.parse(fs.readFileSync(goldPath, 'utf8')); +const r = lintGoldConsistency(gold); + +if (process.argv.includes('--json')) { + console.log(JSON.stringify(r, null, 2)); +} else if (r.ok) { + console.log(`gull-konsistens OK — 0 uwaived selvmotsigende correct-claims (${(gold.claims || []).length} claims)`); +} else { + console.error(`gull-konsistens FEIL — ${r.flagged.length} uwaived selvmotsigende correct-claim(s):`); + for (const f of r.flagged) { + console.error(` - ${f.id} [${f.markers.join(', ')}]`); + console.error(` notes: ${f.notes}`); + } + console.error('\nResolver hver: relabel verdikten ELLER legg til et consistency_waiver med begrunnelse (menneske-i-loop §4c).'); +} + +process.exit(r.ok ? 0 : 1); diff --git a/tests/kb-eval/test-gold-consistency.test.mjs b/tests/kb-eval/test-gold-consistency.test.mjs new file mode 100644 index 0000000..6756164 --- /dev/null +++ b/tests/kb-eval/test-gold-consistency.test.mjs @@ -0,0 +1,100 @@ +// 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('; '), + ); +});