feat(ms-ai-architect): Fase 0 steg 2 — verifiserings-fan-out (373 påstander) + gull-sett m/ deterministisk lastmod-join + TDD [skip-docs]
15 batcher (Opus 4.8 xhigh, read-only) verifiserte 55 sample-frame-filer mot live MS Learn (strikt v2 bevis-krav: correct/outdated/wrong krever hentet learn-sitat, ellers unsourced). 373 påstander: 259 correct, 29 outdated, 11 wrong, 74 unsourced. 40 reelle feil. lastmod_changed joinet i kode (registry sitemap_lastmod vs filens dato): 0 filer ville staleness-loopen flagget -> alle 40 feil er judge-unike (rå funn, krever tolkning i base-rate-rapporten). lib/base-rate.mjs (Wilson, lastmod, aggregering) TDD 15 tester. Suite 538/538. Steg 3 (compute-base-rate + gate) gjenstår.
This commit is contained in:
parent
f7fba5e4e7
commit
6985ed1f5d
19 changed files with 5239 additions and 0 deletions
105
scripts/kb-eval/build-gold-set.mjs
Normal file
105
scripts/kb-eval/build-gold-set.mjs
Normal file
|
|
@ -0,0 +1,105 @@
|
|||
#!/usr/bin/env node
|
||||
// build-gold-set.mjs — Fase 0, steg 2/3 glue: assemble the gold correctness set.
|
||||
//
|
||||
// Consolidates the per-batch verification returns (scripts/kb-eval/data/
|
||||
// fase0-returns/batch-*.json — produced by Opus 4.8 subagents that checked each
|
||||
// volatile claim against live MS Learn) into a single, reusable gold set, and
|
||||
// joins the DETERMINISTIC lastmod_changed signal in code (never an LLM call):
|
||||
// for each file, did any cited source change on the MS Learn sitemap after the
|
||||
// file's own "last updated" date? That is exactly what the existing KB staleness
|
||||
// loop sees, so it tells us which genuine errors a correctness judge would catch
|
||||
// that the staleness loop would miss.
|
||||
//
|
||||
// The non-trivial logic (date parse, lastmod comparison) lives in tested
|
||||
// lib/base-rate.mjs; this CLI is thin wiring over it.
|
||||
//
|
||||
// Usage: node scripts/kb-eval/build-gold-set.mjs [--write]
|
||||
// (default: print summary; --write: persist gold-correctness-set.json)
|
||||
|
||||
import fs from 'node:fs';
|
||||
import path from 'node:path';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
import { parseLastUpdated, fileLastmodChanged } from './lib/base-rate.mjs';
|
||||
|
||||
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
||||
const ROOT = path.resolve(__dirname, '..', '..');
|
||||
const DATA = path.join(__dirname, 'data');
|
||||
const RETURNS = path.join(DATA, 'fase0-returns');
|
||||
|
||||
const frame = JSON.parse(fs.readFileSync(path.join(DATA, 'fase0-sample-frame.json'), 'utf8'));
|
||||
const registry = JSON.parse(
|
||||
fs.readFileSync(path.join(ROOT, 'scripts/kb-update/data/url-registry.json'), 'utf8'),
|
||||
);
|
||||
|
||||
// file -> citedUrls (from the deterministic sample frame)
|
||||
const citedByFile = {};
|
||||
for (const e of [...frame.volatile, ...frame.control]) citedByFile[e.file] = e.citedUrls;
|
||||
|
||||
// file -> { date, lastmod_changed } (read each file once)
|
||||
const fileMeta = {};
|
||||
function metaFor(file) {
|
||||
if (fileMeta[file]) return fileMeta[file];
|
||||
let date = null;
|
||||
try {
|
||||
date = parseLastUpdated(fs.readFileSync(path.join(ROOT, file), 'utf8'));
|
||||
} catch {
|
||||
date = null;
|
||||
}
|
||||
const changed = fileLastmodChanged(citedByFile[file] || [], registry, date);
|
||||
return (fileMeta[file] = { date, lastmod_changed: changed });
|
||||
}
|
||||
|
||||
// relpath after skills/<skill>/references/ for a compact, stable claim id
|
||||
function relOf(file, skill) {
|
||||
const prefix = `skills/${skill}/references/`;
|
||||
return file.startsWith(prefix) ? file.slice(prefix.length) : file;
|
||||
}
|
||||
|
||||
const claims = [];
|
||||
const batchFiles = fs.readdirSync(RETURNS).filter((f) => /^batch-\d+\.json$/.test(f)).sort();
|
||||
for (const bf of batchFiles) {
|
||||
const batch = JSON.parse(fs.readFileSync(path.join(RETURNS, bf), 'utf8'));
|
||||
for (const c of batch.claims) {
|
||||
const meta = metaFor(c.file);
|
||||
claims.push({
|
||||
id: `${c.skill}/${relOf(c.file, c.skill)}#${c.n}`,
|
||||
file: c.file,
|
||||
skill: c.skill,
|
||||
stratum: c.stratum,
|
||||
claim: c.claim,
|
||||
claim_type: c.claim_type,
|
||||
verdict: c.verdict,
|
||||
evidence_url: c.evidence_url ?? null,
|
||||
lastmod_changed: meta.lastmod_changed,
|
||||
file_last_updated: meta.date,
|
||||
notes: c.notes ?? '',
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
const goldSet = {
|
||||
_meta: {
|
||||
created: '2026-06-26',
|
||||
method:
|
||||
'Per-file volatile-claim verification against live MS Learn by Opus 4.8 subagents (strict v2 evidence rule: correct/outdated/wrong require a fetched learn.microsoft.com quote; otherwise unsourced). lastmod_changed joined deterministically in code from url-registry sitemap_lastmod vs the file last-updated date.',
|
||||
sample_frame: 'fase0-sample-frame.json',
|
||||
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.',
|
||||
claim_count: claims.length,
|
||||
},
|
||||
claims,
|
||||
};
|
||||
|
||||
if (process.argv.includes('--write')) {
|
||||
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)`);
|
||||
} else {
|
||||
const filesWithChange = Object.entries(fileMeta).filter(([, m]) => m.lastmod_changed === true).length;
|
||||
const filesNoChange = Object.entries(fileMeta).filter(([, m]) => m.lastmod_changed === false).length;
|
||||
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}`);
|
||||
console.log('(dry run — pass --write to persist gold-correctness-set.json)');
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue