igneum/tools/ci/test-record.mjs

221 lines
30 KiB
JavaScript

#!/usr/bin/env node
// The acceptance layer's recorder (the founder's Test and Acceptance Standard, 8 October 2026; main through the coordinator, 18:2x UK).
// The registry docs/plans/igneum-2.0-test-registry.json holds the cases; tools/ci/test-map.json maps every case whose method includes
// "Automated" to the harness or suite that answers it (the command, the box class, the fixture ids it needs). A run batch writes
// status, run id, evidence path and the pinned manifest sha back into the registry for the cases it covered; a case's accept text is
// never edited (the self-test diffs every accept field before and after); a case with no harness reads NOT RUN with the reason,
// never PASS by inference.
//
// node tools/ci/test-record.mjs --check every Automated case has a map entry or a NOT RUN reason; the map's ids exist
// node tools/ci/test-record.mjs --record <batch.json> batch: {run_id, manifest_sha, evidence_dir, cells:[{cell, status, evidence}]}
// each cell's case ids come from the map; the registry gains status/run/evidence/manifest
// node tools/ci/test-record.mjs --cases <cell> the case ids a matrix cell answers (for the matrix scripts' column)
// node tools/ci/test-record.mjs --note <suite code> "<text>" append a dated note to a suite's notes (a ruling, a review finding's
// disposition); never a case's accept text
// node tools/ci/test-record.mjs --self-test
import fs from 'node:fs'; import path from 'node:path'; import child_process from 'node:child_process';
const args = process.argv.slice(2); const arg = (n) => { const i = args.indexOf(n); return i >= 0 ? args[i + 1] : undefined; };
const ROOT = process.env.TEST_RECORD_ROOT || path.resolve(path.dirname(new URL(import.meta.url).pathname), '..', '..');
const REG = process.env.TEST_REGISTRY || path.join(ROOT, 'docs/plans/igneum-2.0-test-registry.json');
const MAP = process.env.TEST_MAP || path.join(ROOT, 'tools/ci/test-map.json');
const ACCEPT_KEYS = ['accept', 'acceptance', 'accept_text', 'criteria', 'expected']; // whichever the registry uses; all are frozen
const DECISIONS = new Set(['NOT RUN', 'BLOCKED', 'FAIL', 'PASS']);
const METHODS = new Set(['native', 'GPU', 'SP1', 'model', 'static', 'team-reported']);
export function normalize(reg) { // the one-time pass over rows written before the vocabulary: RUNNING -> NOT RUN in progress; DEFERRED -> NOT RUN with the note
let n = 0; const now = new Date().toISOString();
for (const c of casesOf(reg)) {
if (c.run_status === 'RUNNING') { c.run_status = 'NOT RUN'; c.in_progress_since = c.in_progress_since || c.updated || now; n++; }
else if (c.run_status === 'DEFERRED' || /^DEFERRED/.test(String(c.run_status || ''))) { c.deferral_note = c.deferral_note || 'P13 deferred by the founder (8 October 2026, 18:2x UK: "forget P13 for now")'; c.run_status = 'NOT RUN'; n++; }
else if (c.run_status && !DECISIONS.has(c.run_status)) { c.status_note = c.run_status; c.run_status = 'NOT RUN'; n++; }
if (c.evidence_record && !c.evidence_record.method) { const cell = String(c.evidence_record.cell || ''); c.evidence_record.method = /^(suite|check|gate|crosscheck)/.test(cell) ? 'native' : /pods|fleet|bench:|p01-vectors/.test(cell) ? 'GPU' : /proving|zkp/.test(cell) ? 'SP1' : /model|economics|eco|sim/.test(cell) ? 'model' : /study/.test(cell) ? 'team-reported' : 'static'; c.evidence_record.requirement_id = c.evidence_record.requirement_id || c.id; c.evidence_record.decision = c.run_status; c.evidence_record.reviewer = c.evidence_record.reviewer ?? ''; c.evidence_record.claim_impact = c.evidence_record.claim_impact ?? ''; c.evidence_record.release_identity = c.evidence_record.release_identity || { commit: c.evidence_record.manifest_sha || '', lockfile: '', binary: '', network_object: '', activation: '', profile_hashes: '' }; n++; }
c.approvals = c.approvals || { scope_approved: null, implementation_complete: null, evidence_reproduced: null, claim_authorised: null };
if (c.evidence_record && !c.evidence_records) { c.evidence_records = { [c.evidence_record.cell || 'record']: { ...c.evidence_record, run_id: c.evidence_record.run_id || c.run_id, evidence: c.evidence_record.evidence || c.evidence_path, in_progress: !!c.in_progress_since } }; n++; }
}
if (reg.live_fields) reg.live_fields.run_status = 'NOT RUN | BLOCKED | FAIL | PASS (the master, p. 139; in_progress_since marks work under way; deferral_note a founder deferral)';
return n;
}
const load = (p) => JSON.parse(fs.readFileSync(p, 'utf8'));
const casesOf = (reg) => Array.isArray(reg) ? reg : (reg.cases || reg.tests || (reg.suites || []).flatMap((s) => s.tests || s.cases || []));
const isAutomated = (c) => /automated/i.test(String(c.method ?? c.methods ?? ''));
const idOf = (c) => String(c.id ?? c.case ?? c.case_id);
function check(reg, map) {
const cases = casesOf(reg); const ids = new Set(cases.map(idOf)); const out = []; let bad = 0;
for (const [cell, m] of Object.entries(map.cells || {})) for (const id of m.cases || []) if (!ids.has(id)) { out.push(`map: cell ${cell} names an unknown case ${id}`); bad++; }
const mapped = new Set(Object.values(map.cells || {}).flatMap((m) => m.cases || []));
const notRun = map.not_run || {};
for (const c of cases) {
if (!isAutomated(c)) continue; const id = idOf(c);
if (!mapped.has(id) && !notRun[id]) { out.push(`case ${id} (Automated) has no harness in the map and no NOT RUN reason`); bad++; }
}
for (const c of cases) { const st = c.run_status; if (st && !DECISIONS.has(st)) { out.push(`case ${idOf(c)} reads ${st}: the decision vocabulary is NOT RUN, BLOCKED, FAIL, PASS (run --normalize)`); bad++; } }
for (const [cell, m] of Object.entries(map.cells || {})) { if (!m.command) { out.push(`map: cell ${cell} has no command`); bad++; } if (!m.box_class) { out.push(`map: cell ${cell} has no box class`); bad++; } if (!Array.isArray(m.fixtures)) { out.push(`map: cell ${cell} has no fixtures list (F0 onward, or empty)`); bad++; } }
return { bad, lines: out, automated: cases.filter(isAutomated).length, mapped: [...mapped].length, notRun: Object.keys(notRun).length };
}
function record(reg, map, batch) {
// the founder's word (8 October 2026, 20:21 UK): a sha is "cut" only when its binary exists on build-1 with its commit string read
// back, and a fleet binary only from the cross-build kit scanned clean of AVX-512; a batch that declares a cut carries all three
if (batch.cut) {
const c = batch.cut; const errs = [];
if (!/^build-\d+:\/srv\//.test(String(c.binary || ''))) errs.push('cut.binary must be a build-N:/srv/... path on a build box');
if (!c.commit_string || !(String(c.commit_string).startsWith(String(batch.manifest_sha)) || String(batch.manifest_sha).startsWith(String(c.commit_string)))) errs.push(`cut.commit_string (${c.commit_string}) must be the read-back of the batch's manifest sha (${batch.manifest_sha})`);
if (!/every binary is clean of AVX-512/.test(String(c.kit_isa || ''))) errs.push('cut.kit_isa must carry tools/ci/kit-isa-check.sh\'s clean line over the kit');
if (errs.length) throw new Error(`a cut batch is refused: ${errs.join('; ')}`);
}
const cases = casesOf(reg); const byId = new Map(cases.map((c) => [idOf(c), c])); const touched = [];
const now = new Date().toISOString();
for (const cell of batch.cells || []) {
const m = (map.cells || {})[cell.cell]; if (!m) throw new Error(`batch names a cell not in the map: ${cell.cell}`);
// INT-17 (the master edition, 8 October 2026 20:1x UK): a cell that declares its prerequisites (prereqs: {oracle, pinned_keys,
// real_proof_fixture}, each "present" or "missing") with any one missing is BLOCKED, never PASS, with the missing ones named
// the decision vocabulary is exactly NOT RUN, BLOCKED, FAIL, PASS (the master, p. 139; main through the coordinator, 8 October 2026,
// 20:3x UK): RUNNING is not a state and becomes NOT RUN with in_progress_since; DEFERRED becomes NOT RUN with the deferral note
let status = String(cell.status || '').toUpperCase(); let inProgress = false, deferral = null;
if (status === 'RUNNING') { status = 'NOT RUN'; inProgress = true; }
if (status === 'DEFERRED') { status = 'NOT RUN'; deferral = cell.deferral || 'deferred by the founder'; }
if (!DECISIONS.has(status)) throw new Error(`cell ${cell.cell}: status ${cell.status} is not one of NOT RUN, BLOCKED, FAIL, PASS (RUNNING reads as NOT RUN in progress; DEFERRED as NOT RUN with a deferral note)`);
const method = cell.method || batch.method; if (!METHODS.has(method)) throw new Error(`cell ${cell.cell}: method must be one of ${[...METHODS].join(', ')} (never conflated); got ${method}`);
// evidence paths (the coordinator's and lane D's classes, 8 October 2026 21:3x UK): a tree path must exist at record time and name a file,
// never a directory (a directory citation makes every file beneath it the row's evidence and trips the move-together rule on every
// other lane's landing); an absolute path or a box path (host:/path) is outside the tree and is not checked here (rule 1 reads it at the merge)
for (const one of String(cell.evidence || '').split(';').map((x) => x.trim()).filter(Boolean)) {
if (one.startsWith('/') || one.includes(':/')) continue;
if (process.env.TEST_RECORD_NO_TREE === '1') continue;
const abs = path.resolve(ROOT, one);
if (!fs.existsSync(abs)) throw new Error(`cell ${cell.cell}: evidence ${one} is not in the tree at record time (cite only files that exist here; another lane's unlanded file is cited after it lands)`);
if (fs.statSync(abs).isDirectory()) throw new Error(`cell ${cell.cell}: evidence ${one} is a directory; name the file (a directory citation binds every file beneath it to this row)`);
}
const missing = Object.entries(cell.prereqs || {}).filter(([, v]) => v !== 'present').map(([k]) => k);
if (missing.length) status = 'BLOCKED';
// a cell may narrow its write to named cases of the cell ("cases": [...]) so an incident on one case never writes a cell's other cases
// (the shipper, 8 October 2026, 20:2x UK: UX-06's overflow FAIL must not touch UX-04 or POW-01 under suite:miner)
const only = Array.isArray(cell.cases) ? new Set(cell.cases) : null;
if (only) for (const id of only) if (!(m.cases || []).includes(id)) throw new Error(`cell ${cell.cell} does not map case ${id}`);
for (const id of m.cases || []) {
if (only && !only.has(id)) continue;
const c = byId.get(id); if (!c) throw new Error(`map names an unknown case ${id}`);
// several cells may answer one case (the fleet's 4090 cell and the AMD/Intel lane's bench on GPU-03): every cell's record is kept
// in evidence_records keyed by cell, the case's decision is combined (FAIL if any cell failed, else BLOCKED if any is blocked, else
// PASS only when every cell passed, else NOT RUN), evidence_path lists every cell's path, run_id the latest
c.evidence_records = c.evidence_records || {};
if (deferral) c.deferral_note = deferral;
// the record schema (the master, p. 139): release identity, requirement id, method, decision, claim impact, reviewer (empty allowed,
// never inferred); the four approval layers (p. 18) are separate fields a batch sets explicitly, none inferred from another
const ri = batch.release_identity || {};
c.evidence_record = { requirement_id: id, decision: status, method, cell: cell.cell, manifest_sha: batch.manifest_sha, run_id: batch.run_id, evidence: cell.evidence || batch.evidence_dir, in_progress: inProgress, coverage: (m.coverage || {})[id] || m.coverage || {},
release_identity: { commit: ri.commit || batch.manifest_sha || '', lockfile: ri.lockfile || '', binary: ri.binary || '', network_object: ri.network_object || '', activation: ri.activation || '', profile_hashes: ri.profile_hashes || '' },
claim_impact: cell.claim_impact || batch.claim_impact || '', reviewer: cell.reviewer || batch.reviewer || '', at: now,
...(missing.length ? { blocked_on: missing, reason: `INT-17: missing ${missing.join(', ')}; never PASS without them` } : {}) };
c.evidence_records[cell.cell] = c.evidence_record;
// a lifted legacy 'record' entry that carries only a no-harness reason (no run) is not a run: it never counts in the combined
// decision and is dropped once a real cell lands (8 October 2026, 21:5x UK: R2-F03-R01 read NOT RUN on master beside its PASS cell)
for (const [k, r] of Object.entries(c.evidence_records)) if (k !== cell.cell && !r.run_id && !r.evidence && r.reason) delete c.evidence_records[k];
const decs = Object.values(c.evidence_records).filter((r) => r.run_id || r.evidence || r.decision === 'BLOCKED').map((r) => r.decision);
const combined = decs.includes('FAIL') ? 'FAIL' : decs.includes('BLOCKED') ? 'BLOCKED' : decs.every((d) => d === 'PASS') ? 'PASS' : 'NOT RUN';
c.run_status = combined; c.run_id = batch.run_id; c.updated = now;
c.evidence_path = [...new Set(Object.values(c.evidence_records).map((r) => r.evidence).filter(Boolean))].join('; ');
if (combined !== 'NOT RUN') delete c.in_progress_since; else if (decs.some(() => true) && Object.values(c.evidence_records).some((r) => r.in_progress)) c.in_progress_since = c.in_progress_since || now;
const ap = cell.approvals || batch.approvals || {}; c.approvals = { scope_approved: ap.scope_approved ?? c.approvals?.scope_approved ?? null, implementation_complete: ap.implementation_complete ?? c.approvals?.implementation_complete ?? null, evidence_reproduced: ap.evidence_reproduced ?? c.approvals?.evidence_reproduced ?? null, claim_authorised: ap.claim_authorised ?? c.approvals?.claim_authorised ?? null };
touched.push(id);
}
}
// a NOT RUN row is stamped only when its status or reason changes (8 October 2026, 20:1x UK: re-stamping every NOT RUN row on every
// run made each lane's landing collide on 39 rows it never touched)
for (const [id, reason] of Object.entries(map.not_run || {})) { const c = byId.get(id); if (c && (!c.run_status || c.run_status === 'NOT RUN') && !(c.run_status === 'NOT RUN' && c.evidence_record?.reason === reason)) { c.run_status = 'NOT RUN'; c.evidence_record = { reason, at: now }; c.updated = now; } }
return touched;
}
const acceptSnapshot = (reg) => JSON.stringify(casesOf(reg).map((c) => { const o = { id: idOf(c) }; for (const k of ACCEPT_KEYS) if (k in c) o[k] = c[k]; return o; }));
if (args.includes('--self-test')) {
const d = fs.mkdtempSync('/tmp/test-record-'); let fails = 0; process.env.TEST_RECORD_NO_TREE = '1'; // the fixture cells cite fake paths; the tree rule is exercised by its own cases below
const reg = { cases: [
{ id: 'C1', method: 'Automated', accept: 'one million vectors agree' }, { id: 'C2', method: 'Automated', accept: 'parser refuses ten thousand malformed' },
{ id: 'C3', method: 'Manual review', accept: 'the reviewer signs' }, { id: 'C4', method: 'Automated; manual', accept: 'frozen verify time holds' } ] };
const map = { cells: { 'pow': { command: 'cargo test --release', box_class: 'suite', fixtures: ['F0'], cases: ['C1'] } }, not_run: { C2: 'no malformed-case corpus yet', C4: 'the hostile-load rig is not built' } };
fs.writeFileSync(`${d}/reg.json`, JSON.stringify(reg)); fs.writeFileSync(`${d}/map.json`, JSON.stringify(map));
let r = check(reg, map); if (r.bad !== 0) { console.log(`self-test failed: a complete map was refused: ${r.lines.join('; ')}`); fails = 1; }
const regV = { cases: [{ id: 'V1', method: 'Automated', accept: 'a', run_status: 'RUNNING' }] }; const rv = check(regV, { cells: { c: { command: 'x', box_class: 'b', fixtures: [], cases: ['V1'] } }, not_run: {} });
if (!(rv.bad === 1 && /vocabulary/.test(rv.lines[0]))) { console.log(`self-test failed: a RUNNING row passed --check: ${rv.lines}`); fails = 1; }
const map2 = { cells: { pow: { ...map.cells.pow, cases: ['C1', 'C9'] } }, not_run: {} };
r = check(reg, map2); if (!(r.bad >= 2 && r.lines.some((l) => l.includes('unknown case C9')) && r.lines.some((l) => l.includes('C2') && l.includes('no harness')))) { console.log(`self-test failed: an unknown id or an unmapped Automated case was not refused: ${r.lines.join('; ')}`); fails = 1; }
const before = acceptSnapshot(reg); const touched = record(reg, map, { run_id: 'r1', manifest_sha: 'abc', evidence_dir: '/e', method: 'native', cells: [{ cell: 'pow', status: 'PASS', evidence: '/e/pow.log' }] });
if (!(reg.cases[0].evidence_record.method === 'native' && reg.cases[0].evidence_record.decision === 'PASS' && reg.cases[0].evidence_record.requirement_id === 'C1' && reg.cases[0].evidence_record.reviewer === '' && reg.cases[0].approvals.claim_authorised === null)) { console.log(`self-test failed: the p.139 record fields or the four approvals (never inferred) are wrong: ${JSON.stringify(reg.cases[0].evidence_record)} ${JSON.stringify(reg.cases[0].approvals)}`); fails = 1; }
record(reg, map, { run_id: 'r1r', manifest_sha: 'abc', method: 'native', cells: [{ cell: 'pow', status: 'RUNNING', evidence: '/e/pow.log' }] });
if (!(reg.cases[0].run_status === 'NOT RUN' && reg.cases[0].in_progress_since)) { console.log('self-test failed: RUNNING did not become NOT RUN with in_progress_since'); fails = 1; }
let badStatus = false; try { record(reg, map, { run_id: 'x', manifest_sha: 'abc', method: 'native', cells: [{ cell: 'pow', status: 'GREEN' }] }); } catch { badStatus = true; }
delete process.env.TEST_RECORD_NO_TREE; let badPath = ''; try { record(reg, map, { run_id: 'x', manifest_sha: 'abc', method: 'native', cells: [{ cell: 'pow', status: 'FAIL', evidence: `${d}/missing.log`.replace(/^\//, 'rel/') }] }); } catch (e) { badPath = String(e.message); }
if (!/not in the tree at record time/.test(badPath)) { console.log(`self-test failed: an evidence path not in the tree was accepted at record time: ${badPath}`); fails = 1; }
fs.mkdirSync(`${d}/dir`); const relDir = path.relative(ROOT, `${d}/dir`); let badDir = '';
try { record(reg, map, { run_id: 'x', manifest_sha: 'abc', method: 'native', cells: [{ cell: 'pow', status: 'FAIL', evidence: relDir }] }); } catch (e) { badDir = String(e.message); }
if (!/is a directory/.test(badDir)) { console.log(`self-test failed: a directory cited as evidence was accepted: ${badDir}`); fails = 1; }
fs.writeFileSync(`${d}/dir/f.log`, 'x'); let okFile = true; try { record(reg, map, { run_id: 'x', manifest_sha: 'abc', method: 'native', cells: [{ cell: 'pow', status: 'FAIL', evidence: `${relDir}/f.log` }] }); } catch { okFile = false; }
if (!okFile) { console.log('self-test failed: a tree file that exists was refused as evidence'); fails = 1; }
process.env.TEST_RECORD_NO_TREE = '1';
if (!badStatus) { console.log('self-test failed: a status outside the vocabulary was accepted'); fails = 1; }
let badMethod = false; try { record(reg, map, { run_id: 'x', manifest_sha: 'abc', cells: [{ cell: 'pow', status: 'PASS' }] }); } catch { badMethod = true; }
if (!badMethod) { console.log('self-test failed: a batch with no method was accepted (methods are never conflated)'); fails = 1; }
const regO = { cases: [{ id: 'O1', method: 'Automated', accept: 'a', run_status: 'RUNNING', updated: 't', evidence_record: { cell: 'suite:x' } }, { id: 'O2', method: 'Automated', accept: 'b', run_status: 'DEFERRED' }] }; normalize(regO);
if (!(regO.cases[0].run_status === 'NOT RUN' && regO.cases[0].in_progress_since === 't' && regO.cases[0].evidence_record.method === 'native' && regO.cases[1].run_status === 'NOT RUN' && /deferred/.test(regO.cases[1].deferral_note))) { console.log(`self-test failed: normalize: ${JSON.stringify(regO)}`); fails = 1; }
// a lifted reason-only record beside a real cell: the row reads the cell's decision and the placeholder is gone
const regL = { cases: [{ id: 'L1', method: 'Automated', accept: 'a', run_status: 'NOT RUN', evidence_records: { record: { decision: 'NOT RUN', reason: 'no harness yet', at: 't' } } }] };
record(regL, { cells: { lc: { command: 'x', box_class: 'b', fixtures: [], cases: ['L1'] } }, not_run: {} }, { run_id: 'rl', manifest_sha: 'abc', method: 'native', cells: [{ cell: 'lc', status: 'PASS', evidence: '/e/l.log' }] });
if (!(regL.cases[0].run_status === 'PASS' && !('record' in regL.cases[0].evidence_records))) { console.log(`self-test failed: a lifted reason-only record kept a PASS cell at NOT RUN or survived beside it: ${JSON.stringify(regL.cases[0].evidence_records)}`); fails = 1; }
record(reg, map, { run_id: 'r1p', manifest_sha: 'abc', method: 'native', cells: [{ cell: 'pow', status: 'PASS', evidence: '/e/pow.log' }] });
if (acceptSnapshot(reg) !== before) { console.log('self-test failed: a record changed an accept text'); fails = 1; }
if (!(touched.length === 1 && reg.cases[0].run_status === 'PASS' && reg.cases[0].run_id === 'r1p' && reg.cases[0].evidence_record.manifest_sha === 'abc' && reg.cases[0].evidence_path === '/e/pow.log' && reg.cases[0].updated)) { console.log(`self-test failed: the run was not written to the mapped case's live fields: ${JSON.stringify(reg.cases[0])}`); fails = 1; }
if (!(reg.cases[1].run_status === 'NOT RUN' && /corpus/.test(reg.cases[1].evidence_record.reason))) { console.log('self-test failed: an unmapped Automated case did not read NOT RUN with its reason'); fails = 1; }
if (reg.cases[0].run_status !== 'PASS') { console.log('self-test failed: the PASS after the RUNNING pass did not land'); fails = 1; }
const stamp1 = reg.cases[1].updated; record(reg, map, { run_id: 'r1b', manifest_sha: 'abc', evidence_dir: '/e', method: 'native', cells: [{ cell: 'pow', status: 'PASS', evidence: '/e/pow.log' }] });
if (reg.cases[1].updated !== stamp1) { console.log('self-test failed: an unchanged NOT RUN row was re-stamped on a later run'); fails = 1; }
if (reg.cases[2].run_status) { console.log('self-test failed: a manual case was given a run status'); fails = 1; }
const regS = { suites: [{ code: 'X', tests: [{ id: 'X-1', method: 'Automated', accept: 'a' }] }] }; if (casesOf(regS).length !== 1) { console.log('self-test failed: the suites/tests registry shape was not read'); fails = 1; }
const regN = { suites: [{ code: 'FIN', tests: [{ id: 'F-1', method: 'Automated', accept: 'keep' }] }] }; fs.writeFileSync(`${d}/regn.json`, JSON.stringify(regN));
const nr = child_process.spawnSync(process.execPath, [new URL(import.meta.url).pathname, '--note', 'FIN', 'the ruling'], { env: { ...process.env, TEST_REGISTRY: `${d}/regn.json`, TEST_MAP: `${d}/map.json` }, encoding: 'utf8' });
const regN2 = JSON.parse(fs.readFileSync(`${d}/regn.json`, 'utf8'));
if (!(nr.status === 0 && regN2.suites[0].notes?.length === 1 && regN2.suites[0].notes[0].text === 'the ruling' && regN2.suites[0].tests[0].accept === 'keep')) { console.log(`self-test failed: --note did not append a dated note to the suite and keep the accept text: ${nr.stdout} ${nr.stderr}`); fails = 1; }
record(reg, map, { run_id: 'r3', manifest_sha: 'abc', method: 'native', cells: [{ cell: 'pow', status: 'PASS', evidence: '/e/pow.log', prereqs: { oracle: 'present', pinned_keys: 'missing', real_proof_fixture: 'present' } }] });
if (!(reg.cases[0].run_status === 'BLOCKED' && reg.cases[0].evidence_record.blocked_on?.[0] === 'pinned_keys')) { console.log(`self-test failed: a PASS with a missing prerequisite was not BLOCKED (INT-17): ${JSON.stringify(reg.cases[0].run_status)}`); fails = 1; }
record(reg, map, { run_id: 'r4', manifest_sha: 'abc', method: 'native', cells: [{ cell: 'pow', status: 'PASS', evidence: '/e/pow.log', prereqs: { oracle: 'present', pinned_keys: 'present', real_proof_fixture: 'present' } }] });
if (reg.cases[0].run_status !== 'PASS') { console.log('self-test failed: a PASS with every prerequisite present was not written'); fails = 1; }
const reg2 = { cases: [{ id: 'G3', method: 'Automated', accept: 'a' }] }; const mapG = { cells: { fleet: { command: 'x', box_class: 'b', fixtures: [], cases: ['G3'] }, amd: { command: 'y', box_class: 'b', fixtures: [], cases: ['G3'] } }, not_run: {} };
record(reg2, mapG, { run_id: 'f1', manifest_sha: 'abc', method: 'GPU', cells: [{ cell: 'fleet', status: 'RUNNING', evidence: '/e/fleet' }] });
record(reg2, mapG, { run_id: 'a1', manifest_sha: 'abc', method: 'GPU', cells: [{ cell: 'amd', status: 'PASS', evidence: '/e/amd' }] });
const g = reg2.cases[0];
if (!(Object.keys(g.evidence_records).length === 2 && g.evidence_records.fleet.run_id === 'f1' && g.run_status === 'NOT RUN' && g.in_progress_since && /\/e\/fleet; \/e\/amd|\/e\/amd; \/e\/fleet/.test(g.evidence_path))) { console.log(`self-test failed: two cells on one case did not keep both records with a combined decision: ${JSON.stringify(g)}`); fails = 1; }
record(reg2, mapG, { run_id: 'f2', manifest_sha: 'abc', method: 'GPU', cells: [{ cell: 'fleet', status: 'PASS', evidence: '/e/fleet' }] });
if (g.run_status !== 'PASS') { console.log('self-test failed: PASS on every cell did not combine to PASS'); fails = 1; }
record(reg2, mapG, { run_id: 'a2', manifest_sha: 'abc', method: 'GPU', cells: [{ cell: 'amd', status: 'FAIL', evidence: '/e/amd' }] });
if (g.run_status !== 'FAIL') { console.log('self-test failed: a FAIL on one cell did not combine to FAIL'); fails = 1; }
const regF = { cases: [{ id: 'M1', method: 'Automated', accept: 'a' }, { id: 'M2', method: 'Automated', accept: 'b' }] }; const mapF = { cells: { miner: { command: 'x', box_class: 'b', fixtures: [], cases: ['M1', 'M2'] } }, not_run: {} };
record(regF, mapF, { run_id: 'inc', manifest_sha: 'abc', method: 'native', cells: [{ cell: 'miner', status: 'FAIL', evidence: 'e', cases: ['M2'] }] });
if (!(regF.cases[1].run_status === 'FAIL' && !regF.cases[0].run_status)) { console.log('self-test failed: a cell with a cases filter wrote beyond the named cases'); fails = 1; }
let filtThrew = false; try { record(regF, mapF, { run_id: 'inc2', manifest_sha: 'abc', method: 'native', cells: [{ cell: 'miner', status: 'FAIL', cases: ['Z9'] }] }); } catch { filtThrew = true; }
if (!filtThrew) { console.log('self-test failed: a cases filter naming a case the cell does not map was accepted'); fails = 1; }
let cutThrew = false; try { record(reg, map, { run_id: 'c1', manifest_sha: 'abc', method: 'native', cut: { binary: '/root/x', commit_string: 'abc', kit_isa: 'clean' }, cells: [] }); } catch (e) { cutThrew = /cut batch is refused/.test(e.message); }
if (!cutThrew) { console.log('self-test failed: a cut batch without a box path and the ISA clean line was accepted'); fails = 1; }
let cutOk = true; try { record(reg, map, { run_id: 'c2', manifest_sha: 'abc1234', method: 'native', cut: { binary: 'build-1:/srv/artefacts/x/igneumd', commit_string: 'abc1234', kit_isa: 'kit-isa: every binary is clean of AVX-512 (the x86-64-v3 baseline)' }, cells: [] }); } catch { cutOk = false; }
if (!cutOk) { console.log('self-test failed: a cut batch with the read-back and the ISA line was refused'); fails = 1; }
let threw = false; try { record(reg, map, { run_id: 'r2', manifest_sha: 'x', method: 'native', cells: [{ cell: 'ghost', status: 'PASS' }] }); } catch { threw = true; }
if (!threw) { console.log('self-test failed: a batch naming a cell not in the map was accepted'); fails = 1; }
fs.rmSync(d, { recursive: true, force: true });
if (!fails) console.log('self-test passed: a complete map checks; an unknown case id and an unmapped Automated case are refused; a run batch writes run_status, run_id, evidence_path, updated and the evidence record to the mapped cases only, leaves every accept text byte-identical, gives an unmapped Automated case NOT RUN with its reason and a manual case nothing; a batch naming an unknown cell is refused; --note appends a dated note to a suite and never touches an accept text; an unchanged NOT RUN row is not re-stamped; a cell with a missing prerequisite is BLOCKED, never PASS (INT-17); a cut batch needs the binary on a box, the commit string read back and the kit ISA clean line; a cell may narrow its write to named cases; the decision vocabulary is exactly NOT RUN, BLOCKED, FAIL, PASS with RUNNING read as in progress; every record carries the p.139 fields and the four approvals, never inferred; several cells on one case keep every record and combine the decision');
process.exit(fails);
}
const reg = load(REG); const map = load(MAP);
if (args.includes('--check')) { const r = check(reg, map); for (const l of r.lines) console.error(`test-record: ${l}`); console.log(`test-record: ${r.automated} Automated cases, ${r.mapped} mapped to cells, ${r.notRun} NOT RUN with a reason${r.bad ? `, ${r.bad} problems` : ''}`); process.exit(r.bad ? 1 : 0); }
if (args.includes('--normalize')) { const n = normalize(reg); fs.writeFileSync(REG, JSON.stringify(reg, null, 2) + '\n'); console.log(`test-record: normalized ${n} rows to the decision vocabulary and the record schema`); process.exit(0); }
if (arg('--cases')) { console.log(((map.cells || {})[arg('--cases')]?.cases || []).join(',')); process.exit(0); }
if (arg('--note-file')) { const n = load(arg('--note-file')); args.push('--note', n.suite, n.text); }
if (arg('--note')) {
const code = arg('--note'); const text = args[args.indexOf('--note') + 2];
if (code === 'registry' && text) { const before = acceptSnapshot(reg); reg.notes = reg.notes || []; reg.notes.push({ at: new Date().toISOString(), text }); if (acceptSnapshot(reg) !== before) { console.error('test-record: REFUSED'); process.exit(1); } fs.writeFileSync(REG, JSON.stringify(reg, null, 2) + '\n'); console.log(`test-record: registry note ${reg.notes.length}: ${text.slice(0, 80)}`); process.exit(0); }
const suite = (reg.suites || []).find((s) => s.code === code);
if (!suite || !text) { console.error(`test-record: --note needs a suite code in the registry and a text (got ${code}, ${text ? 'text' : 'no text'})`); process.exit(2); }
const before = acceptSnapshot(reg); suite.notes = suite.notes || []; suite.notes.push({ at: new Date().toISOString(), text });
if (acceptSnapshot(reg) !== before) { console.error('test-record: REFUSED: the note would change an accept text'); process.exit(1); }
fs.writeFileSync(REG, JSON.stringify(reg, null, 2) + '\n'); console.log(`test-record: note ${suite.notes.length} on ${code}: ${text.slice(0, 80)}`); process.exit(0);
}
if (arg('--record')) {
const batch = load(arg('--record')); const before = acceptSnapshot(reg); const touched = record(reg, map, batch);
if (acceptSnapshot(reg) !== before) { console.error('test-record: REFUSED: the record would change an accept text'); process.exit(1); }
fs.writeFileSync(REG, JSON.stringify(reg, null, 2) + '\n'); console.log(`test-record: run ${batch.run_id} (manifest ${String(batch.manifest_sha).slice(0, 8)}) written to ${touched.length} cases: ${touched.join(', ')}`); process.exit(0);
}
console.error('usage: test-record.mjs --check | --record <batch.json> | --cases <cell> | --self-test'); process.exit(2);