#!/usr/bin/env node // audit-corpus-headers.mjs — Spor 1 Step 1: read-only audit of the corpus header state. // // Measures, per non-advisor reference file and in aggregate, the empirical ground // truth the migration plan rests on: header-region **Verified:** values (clean date // vs stale "MCP "), body-duplicates and pipe-delimited meta rows inside the // 500-byte header window, base-field presence (English **Last updated:**, **Status:**, // title) and the header dialect (**Category:** / **Kategori:** / pipe). Mutates nothing. // // Usage: // node scripts/kb-update/audit-corpus-headers.mjs # human summary // node scripts/kb-update/audit-corpus-headers.mjs --json # full report to stdout // node scripts/kb-update/audit-corpus-headers.mjs --write # persist data/corpus-header-audit.json import { readdirSync, readFileSync, existsSync, realpathSync } from 'node:fs'; import { join, relative, dirname } from 'node:path'; import { fileURLToPath } from 'node:url'; import { atomicWriteJson } from './lib/atomic-write.mjs'; import { insertVerifiedFields } from './lib/transform.mjs'; const __dirname = dirname(fileURLToPath(import.meta.url)); const PLUGIN_ROOT = join(__dirname, '..', '..'); const SKILLS_DIR = join(PLUGIN_ROOT, 'skills'); const OUT_PATH = join(__dirname, 'data', 'corpus-header-audit.json'); // The four non-advisor skills — advisor is out of audit scope (S-Cosmo: never mutated, // and its header state is irrelevant to the Step-5/9 blast radius this audit pins). const NON_ADVISOR_SKILLS = [ 'ms-ai-engineering', 'ms-ai-governance', 'ms-ai-infrastructure', 'ms-ai-security', ]; // Mirrors kb-headers.mjs — the Port-1 contract reads only the top 500 bytes. const HEADER_REGION_BYTES = 500; // Base-field regexes mirror transform.mjs validateKbFile (RE_LAST_UPDATED:261 is not // exported; replicated verbatim so the audit measures exactly what the guard reads). const RE_TITLE = /^#\s+\S/m; const RE_LAST_UPDATED = /\*\*Last updated:\*\*\s*[\d-]+/i; const RE_STATUS = /\*\*Status:\*\*\s*\S/i; // Verified value = everything after the label up to a pipe separator or line end, // so "MCP 2026-06" survives intact while a pipe row stops at its sibling token. const RE_VERIFIED = /\*\*Verified:\*\*\s*([^|\n]*)/; const RE_CLEAN_DATE = /^\d{4}-\d{2}(-\d{2})?$/; // PLAIN (non-bold) `Verified:` in the header window — the RX-KB1 poison class: a `| Verified: // ` tail on the **Last updated:** line, claiming a verification the judge never made and // INVISIBLE to the bold-only RE_VERIFIED above (this blindness was M4). The `[^*]` guard keeps // a bold `**Verified:**` from matching (its 'V' is preceded by '*'). Surfaced as a separate // signal so a stale plain tail can never silently re-enter the corpus (strip-stale-verified- // pipe.mjs removes the current 87; this detects any reappearance within the non-advisor scope). const RE_PLAIN_VERIFIED = /(?:^|[^*])Verified:/m; // ── RX-KB1b: footer date-conflict + label-whitelist ──────────────────────── // A file that carries a header **Last updated:** date AND a norsk footer date under a // last-updated-EQUIVALENT label ("Sist oppdatert") with a different value contradicts // itself — it asserts two distinct last-updated dates. That is the only norsk footer // label that can conflict with the header. LABEL WHITELIST: "Verifisert"/"Sist // verifisert" record an INDEPENDENT axis (when content was last checked against source, // not when the file was edited), so a differing date there is expected and is never a // conflict. Detection is read-only and flag-to-human: the audit reports, the operator // resolves per file against git/source (aldri-auto-fiks — a footer date can be right and // the header stale, or vice versa; only a human can adjudicate which). // Header **Last updated:** DATE — captured value (RE_LAST_UPDATED above only tests presence). const RE_HEADER_LAST_UPDATED_DATE = /\*\*Last updated:\*\*\s*(\d{4}-\d{2}(?:-\d{2})?)/i; // Any norsk footer meta-line carrying a date: "**Label:** DATE", "*Label: DATE", "Label: DATE". // A valid month (01-12) is required, which also excludes template placeholders ([Dato], // YYYY-MM-DD — they never match \d{4}-(0[1-9]|1[0-2])). Whole-file scan: footers live well // outside the 500-byte header window. Group 1 = label, group 2 = date. const RE_FOOTER_META = /(?:^|\n)[ \t>*_-]*\*{0,2}\s*(Sist oppdatert|Sist verifisert|Verifisert)\s*:?\**\s*(\d{4}-(?:0[1-9]|1[0-2])(?:-\d{2})?)/gi; // Only this label is last-updated-equivalent (conflict-bearing). Everything else the // RE_FOOTER_META captures ("sist verifisert", "verifisert") is on the whitelist. const FOOTER_LABEL_LAST_UPDATED = 'sist oppdatert'; // ── RX-HDR (dissolved → permanent audit signal): R7-unstampability ────────── // RX-HDR was replanned as a 10-file header-slanking unit; premiss-verification collapsed // the population to 1 — the only real class is a header so byte-dense that BOTH R7 stamps // (**Verified:** + **Verified by:**) cannot fit inside the 500-byte window. insertVerifiedFields // throws exactly there ("header-slanking required"), so the ground-truth test is to run the // real stamper and catch the throw. Field-reorder cannot fix such a file (its meta lines carry // fat prose tails needing a VALUE-slim — out of scope), so it folds into R7's existing // "header-slanking kreves" flag path. This signal keeps the population from silently growing. // A fixed representative stamp (deterministic; matches R7's byte-length: YYYY-MM-DD + judge-v3.1). const STAMP_PROBE = { verified: '2026-01-01', verified_by: 'judge-v3.1' }; /** True iff R7's insertVerifiedFields can stamp this content within the 500-byte window. Pure. */ function isStampable(content) { try { insertVerifiedFields(content, STAMP_PROBE); return true; } catch { return false; } } /** * Footer date-conflict for one file's full content. Returns null when there is no header * Last-updated date, no last-updated-equivalent footer, or all such footers agree on the * YYYY-MM prefix. Otherwise returns the header date and the conflicting footer date(s). Pure. */ function detectFooterConflict(content, headerLastUpdated) { if (!headerLastUpdated) return null; const footerDates = []; for (const m of content.matchAll(RE_FOOTER_META)) { if (m[1].toLowerCase() === FOOTER_LABEL_LAST_UPDATED) footerDates.push(m[2]); } if (footerDates.length === 0) return null; const headerPrefix = headerLastUpdated.slice(0, 7); const conflicting = footerDates.filter((d) => d.slice(0, 7) !== headerPrefix); if (conflicting.length === 0) return null; return { headerLastUpdated, footerDates: conflicting }; } /** Audit a single file's content. Pure. */ function auditContent(content) { const head = content.slice(0, HEADER_REGION_BYTES); const verifiedMatches = [...head.matchAll(new RegExp(RE_VERIFIED.source, 'g'))]; let verified = null; if (verifiedMatches.length > 0) { const value = verifiedMatches[0][1].trim(); const line = head.slice(0, verifiedMatches[0].index).split('\n').length - 1; const lineText = head.split('\n')[line] ?? ''; verified = { value, isDate: RE_CLEAN_DATE.test(value), pipe: lineText.includes(' | '), duplicateWithinHeaderRegion: verifiedMatches.length > 1, }; } let dialect = 'none'; if (/\*\*[^*\n]+:\*\*[^\n]* \| \*\*/.test(head)) dialect = 'pipe'; else if (/\*\*Category:\*\*/i.test(head)) dialect = 'category'; else if (/\*\*Kategori:\*\*/i.test(head)) dialect = 'kategori'; const headerLuMatch = head.match(RE_HEADER_LAST_UPDATED_DATE); const footerConflict = detectFooterConflict(content, headerLuMatch ? headerLuMatch[1] : null); return { title: RE_TITLE.test(content), status: RE_STATUS.test(content), lastUpdatedEnglish: RE_LAST_UPDATED.test(content), dialect, verified, plainVerified: RE_PLAIN_VERIFIED.test(head), footerConflict, stampable: isStampable(content), }; } /** * Audit a batch of files. Pure + dependency-injected reader (validate-kb-file.mjs * pattern) so tests never touch disk. An unreadable file is reported, not thrown. * * @param {string[]} paths * @param {(p: string) => string} [readFile] * @returns {{files: Record, aggregate: object}} */ export function auditHeaders(paths, readFile = (p) => readFileSync(p, 'utf8')) { const files = {}; const aggregate = { files: 0, readErrors: 0, verifiedPresent: 0, verifiedDate: 0, verifiedNonDate: 0, verifiedPipe: 0, verifiedDuplicate: 0, plainVerifiedPipe: 0, footerConflicts: 0, unstampable: 0, missingTitle: 0, missingStatus: 0, missingEnglishLastUpdated: 0, dialects: { category: 0, kategori: 0, pipe: 0, none: 0 }, }; for (const path of paths ?? []) { aggregate.files++; let content; try { content = readFile(path); } catch (err) { files[path] = { error: err.message }; aggregate.readErrors++; continue; } const r = auditContent(content); files[path] = r; if (r.verified) { aggregate.verifiedPresent++; if (r.verified.isDate) aggregate.verifiedDate++; else aggregate.verifiedNonDate++; if (r.verified.pipe) aggregate.verifiedPipe++; if (r.verified.duplicateWithinHeaderRegion) aggregate.verifiedDuplicate++; } if (r.plainVerified) aggregate.plainVerifiedPipe++; if (r.footerConflict) aggregate.footerConflicts++; if (!r.stampable) aggregate.unstampable++; if (!r.title) aggregate.missingTitle++; if (!r.status) aggregate.missingStatus++; if (!r.lastUpdatedEnglish) aggregate.missingEnglishLastUpdated++; aggregate.dialects[r.dialect]++; } return { files, aggregate }; } // Walk directory recursively for .md files (build-registry.mjs shape). function walkMd(dir) { const results = []; if (!existsSync(dir)) return results; for (const entry of readdirSync(dir, { withFileTypes: true })) { const full = join(dir, entry.name); if (entry.isDirectory()) { results.push(...walkMd(full)); } else if (entry.name.endsWith('.md') && entry.name !== 'SKILL.md') { results.push(full); } } return results; } function main(argv) { const json = argv.includes('--json'); const write = argv.includes('--write'); const paths = NON_ADVISOR_SKILLS .flatMap((skill) => walkMd(join(SKILLS_DIR, skill, 'references'))) .sort(); const report = auditHeaders(paths); // Re-key on plugin-relative paths for a stable, machine-diffable artifact. const relFiles = {}; for (const [abs, r] of Object.entries(report.files)) { relFiles[relative(PLUGIN_ROOT, abs)] = r; } const out = { generated_by: 'audit-corpus-headers.mjs', files: relFiles, aggregate: report.aggregate }; if (write) { atomicWriteJson(OUT_PATH, out); console.log(`Wrote ${relative(PLUGIN_ROOT, OUT_PATH)}`); } if (json) { process.stdout.write(JSON.stringify(out, null, 2) + '\n'); } if (!json) { const a = report.aggregate; console.log(`Corpus header audit — ${a.files} non-advisor reference files`); console.log(` Verified header present: ${a.verifiedPresent} (date: ${a.verifiedDate}, stale non-date: ${a.verifiedNonDate})`); console.log(` Verified pipe rows: ${a.verifiedPipe}, duplicates within 500B: ${a.verifiedDuplicate}`); console.log(` Plain (non-bold) Verified tails (RX-KB1 poison class): ${a.plainVerifiedPipe}`); console.log(` Footer "Sist oppdatert" ≠ header Last updated (RX-KB1b, flag-to-human): ${a.footerConflicts}`); for (const [abs, r] of Object.entries(report.files)) { if (!r.footerConflict) continue; console.log(` FLAG ${relative(PLUGIN_ROOT, abs)} — header ${r.footerConflict.headerLastUpdated} vs footer ${r.footerConflict.footerDates.join(', ')}`); } console.log(` R7-unstampable (both stamps land past 500B → header-slanking kreves, R7 flag path): ${a.unstampable}`); for (const [abs, r] of Object.entries(report.files)) { if (r.stampable !== false) continue; console.log(` FLAG ${relative(PLUGIN_ROOT, abs)} — insertVerifiedFields would throw (value-slim required)`); } console.log(` Missing English Last updated: ${a.missingEnglishLastUpdated}`); console.log(` Missing Status: ${a.missingStatus}`); console.log(` Missing title: ${a.missingTitle}`); console.log(` Dialects: category=${a.dialects.category} kategori=${a.dialects.kategori} pipe=${a.dialects.pipe} none=${a.dialects.none}`); } } const isMain = (() => { try { return realpathSync(process.argv[1]) === realpathSync(fileURLToPath(import.meta.url)); } catch { return false; } })(); if (isMain) main(process.argv.slice(2));