Files
stack/scripts/prepare-concept-tests.mjs
T
jason.woltje 193479b52d docs: concept annexation, provider/reference docs, ACT-1 groundwork
Mosaic concepts pages now own the adapted content; source/license
metadata under docs/reference/concepts. Adds ACT-1 agent-context
planning capture, pinned concept test package + preparation utility,
foundation observation notes (durability, evidence, federation,
onboarding, workflow), and the #1495 consolidation assessment.
TOOLS.md updated for the host-dev launcher.
2026-09-07 14:07:05 -05:00

89 lines
5.3 KiB
JavaScript

#!/usr/bin/env node
// Validate or stage synthetic ACT-1 review inputs. Never launches an agent.
import { readFileSync, readdirSync, lstatSync, mkdtempSync, mkdirSync, cpSync, writeFileSync, chmodSync } from 'node:fs';
import { createHash } from 'node:crypto';
import { tmpdir } from 'node:os';
import { resolve, join } from 'node:path';
const root = resolve(import.meta.dirname, '..');
const pack = join(root, 'docs/plans/act-1-tests');
const refs = join(root, 'docs/reference/concepts');
const concepts = join(root, 'docs/concepts');
const hash = bytes => createHash('sha256').update(bytes).digest('hex');
const readJson = file => JSON.parse(readFileSync(file, 'utf8'));
function requireTrue(value, message) { if (!value) throw new Error(message); }
function regularTree(dir) {
for (const entry of readdirSync(dir)) {
const file = join(dir, entry), stat = lstatSync(file);
requireTrue(!stat.isSymbolicLink(), `symlink not allowed in test inputs: ${file}`);
if (stat.isDirectory()) regularTree(file);
else requireTrue(stat.isFile(), `not a regular input: ${file}`);
}
}
try {
requireTrue(process.argv.length <= 3 && (!process.argv[2] || process.argv[2] === '--check'),
'usage: node scripts/prepare-concept-tests.mjs [--check]');
regularTree(pack);
regularTree(refs);
regularTree(concepts);
const source = readJson(join(refs, 'SOURCE.json'));
requireTrue(source.schemaVersion === 2, 'unsupported concept manifest');
for (const entry of source.files) {
requireTrue(/^[a-z0-9-]+\.md$/.test(entry.file) && entry.path === `docs/concepts/${entry.file}`, 'invalid concept path');
const bytes = readFileSync(join(concepts, entry.file));
requireTrue(bytes.length === entry.bytes && hash(bytes) === entry.sha256,
`concept drift: ${entry.file}; update current content identity after reviewing the change`);
}
requireTrue(source.license.path === 'docs/reference/concepts/LICENSE', 'invalid license path');
const license = readFileSync(join(refs, 'LICENSE'));
requireTrue(license.length === source.license.bytes && hash(license) === source.license.sha256, 'license drift');
const cases = readJson(join(pack, 'cases.json'));
requireTrue(cases.schemaVersion === 1 && cases.automatedModelCalls === 0, 'invalid pack metadata');
requireTrue(cases.cases.length > 0, 'empty scenario catalog');
const ids = new Set();
for (const item of cases.cases) {
requireTrue(/^ACT-C\d{2}$/.test(item.id) && !ids.has(item.id), `invalid/duplicate case: ${item.id}`);
ids.add(item.id);
requireTrue(item.kind === 'manual-agent' && typeof item.prompt === 'string' && item.prompt.trim(), `invalid case: ${item.id}`);
requireTrue(Array.isArray(item.expected) && item.expected.length > 0 && item.expected.every(x => typeof x === 'string' && x.trim()), `missing rubric: ${item.id}`);
requireTrue(source.files.some(entry => entry.file === item.concept), `missing reference: ${item.id}`);
if (item.fixture !== null) {
requireTrue(/^[a-z0-9-]+\.json$/.test(item.fixture), `invalid fixture: ${item.id}`);
requireTrue(readJson(join(pack, 'fixtures', item.fixture)).synthetic === true, `non-synthetic fixture: ${item.id}`);
}
}
const baseline = readFileSync(join(root, 'agents/darkwing/SOUL.md'));
const candidate = readFileSync(join(pack, 'candidate/SOUL.md'));
requireTrue(baseline.length > 0 && candidate.length > 0, 'empty personality input');
console.log(`Verified ${source.files.length} Mosaic concepts, license attribution and ${ids.size} synthetic scenarios; no model calls.`);
if (process.argv[2] !== '--check') {
const out = mkdtempSync(join(tmpdir(), 'mosaic-act1-'));
chmodSync(out, 0o700);
for (const dir of ['baseline', 'candidate']) mkdirSync(join(out, dir));
writeFileSync(join(out, 'baseline/SOUL.md'), baseline);
writeFileSync(join(out, 'candidate/SOUL.md'), candidate);
cpSync(join(pack, 'fixtures'), join(out, 'fixtures'), { recursive: true, errorOnExist: true, force: false });
cpSync(concepts, join(out, 'concepts'), { recursive: true, errorOnExist: true, force: false });
cpSync(refs, join(out, 'provenance'), { recursive: true, errorOnExist: true, force: false });
for (const file of ['cases.json', 'README.md']) cpSync(join(pack, file), join(out, file));
const results = {
packId: cases.packId, preparedAt: new Date().toISOString(), upstreamRevision: source.sourceRevision,
concepts: source.files.map(({ file, sha256 }) => ({ file, sha256 })),
sourceManifestSha256: hash(readFileSync(join(refs, 'SOURCE.json'))),
casesSha256: hash(readFileSync(join(pack, 'cases.json'))),
variants: { baseline: hash(baseline), candidate: hash(candidate) },
operator: null, reviewer: null, modelBudget: null, environment: null,
cases: cases.cases.flatMap(item => ['baseline', 'candidate'].map(variant => ({
id: item.id, variant, status: 'NOT_RUN', actualOutputFile: null, evidence: null,
runtimeGate: item.runtimeGate ? 'DEFERRED' : 'NOT_APPLICABLE'
})))
};
writeFileSync(join(out, 'RESULTS.json'), JSON.stringify(results, null, 2) + '\n', { mode: 0o600 });
console.log(`Prepared review workspace: ${out}\nResults: ${out}/RESULTS.json\nNo TUI, model, deployment, or session mutation performed.`);
}
} catch (error) {
console.error(`concept-test preparation: ${error.message}`);
process.exitCode = 1;
}