Files
stack/scripts/gate-verify.mjs
coder-mos2 e910a45ab3
ci/woodpecker/pr/ci Pipeline was successful
fix(rm-02): narrow inventory drift guarantee
2026-08-01 15:09:57 -05:00

1230 lines
45 KiB
JavaScript

#!/usr/bin/env node
import { constants } from 'node:fs';
import { createHash } from 'node:crypto';
import {
access,
chmod,
copyFile,
lstat,
mkdir,
mkdtemp,
open,
readFile,
readdir,
readlink,
rm,
symlink,
writeFile,
} from 'node:fs/promises';
import path from 'node:path';
import { spawnSync } from 'node:child_process';
const COPY_SKIP = new Set(['.git', '.mosaic-test-work', '.next', '.turbo', 'coverage', 'dist']);
const POPULATION_CRITERION_IDS = new Set([
'RM02-EVIDENCE-SUBJECT-BINDING',
'RM02-TYPE-STRICT-SCHEMA',
'RM02-NONEMPTY-ANCHORED-QUANTIFICATION',
]);
const REQUIRED_GATE_INVENTORY = new Map([
['quality-typecheck', 'package.json'],
['quality-lint', 'package.json'],
['quality-format', 'package.json'],
['checkout-preflight', 'scripts/preflight.mjs'],
['ci-queue-wait', 'packages/mosaic/framework/tools/git/ci-queue-wait.sh'],
['hook-pre-commit', '.husky/pre-commit'],
['hook-pre-push', '.husky/pre-push'],
]);
function parseArgs(argv) {
const options = {
root: process.cwd(),
manifest: 'gates/gates.manifest.json',
structureOnly: false,
};
for (let index = 0; index < argv.length; index += 1) {
const value = argv[index];
if (value === '--root') options.root = path.resolve(argv[++index]);
else if (value === '--manifest') options.manifest = argv[++index];
else if (value === '--structure-only') options.structureOnly = true;
else throw new Error(`unknown option: ${value}`);
}
return options;
}
function runInvocation(root, invocation, extraEnvironment = {}) {
if (!Array.isArray(invocation) || invocation.length === 0) {
return { status: null, stdout: '', stderr: 'missing invocation' };
}
return spawnSync(invocation[0], invocation.slice(1), {
cwd: root,
encoding: 'utf8',
env: { ...process.env, GATE_VERIFY: '1', ...extraEnvironment },
timeout: 300_000,
});
}
async function sandboxPath(root, relativePath, label) {
if (typeof relativePath !== 'string' || path.isAbsolute(relativePath)) {
throw new Error(`${label}: path escapes sandbox (${String(relativePath)})`);
}
const resolvedRoot = path.resolve(root);
const target = path.resolve(resolvedRoot, relativePath);
if (target !== resolvedRoot && !target.startsWith(`${resolvedRoot}${path.sep}`)) {
throw new Error(`${label}: path escapes sandbox (${relativePath})`);
}
const parts = path.relative(resolvedRoot, path.dirname(target)).split(path.sep).filter(Boolean);
let current = resolvedRoot;
for (const part of parts) {
current = path.join(current, part);
try {
if ((await lstat(current)).isSymbolicLink()) {
throw new Error(`${label}: path crosses symbolic link (${relativePath})`);
}
} catch (error) {
if (error.code === 'ENOENT') break;
throw error;
}
}
return target;
}
function expand(value, root) {
return String(value)
.replaceAll('${ROOT}', root)
.replaceAll('${HOME}', process.env.HOME ?? '')
.replaceAll('${PATH}', process.env.PATH ?? '');
}
function normalizedJson(value) {
if (Array.isArray(value)) return value.map(normalizedJson);
if (value && typeof value === 'object') {
return Object.fromEntries(
Object.keys(value)
.sort()
.map((key) => [key, normalizedJson(value[key])]),
);
}
return value;
}
function structuredValuesEqual(left, right) {
return JSON.stringify(normalizedJson(left)) === JSON.stringify(normalizedJson(right));
}
function outcomeMatches(outcome, result) {
const combined = `${result.stdout ?? ''}\n${result.stderr ?? ''}`;
return (
result.status === outcome?.exitCode &&
(!outcome?.outputPattern || new RegExp(outcome.outputPattern, 'm').test(combined)) &&
(!outcome?.notOutputPattern || !new RegExp(outcome.notOutputPattern, 'm').test(combined))
);
}
function resultMatches(gateCase, result) {
const combined = `${result.stdout ?? ''}\n${result.stderr ?? ''}`;
return (
outcomeMatches(gateCase.actual, result) &&
(!gateCase.reasonPattern || new RegExp(gateCase.reasonPattern, 'm').test(combined))
);
}
async function safeSandboxWrite(root, relativePath, contents, label) {
const target = await sandboxPath(root, relativePath, label);
await mkdir(path.dirname(target), { recursive: true });
try {
if ((await lstat(target)).isSymbolicLink()) {
throw new Error(`${label}: target is a symbolic link (${relativePath})`);
}
} catch (error) {
if (error.code !== 'ENOENT') throw error;
}
let handle;
try {
handle = await open(
target,
constants.O_WRONLY | constants.O_CREAT | constants.O_TRUNC | constants.O_NOFOLLOW,
0o666,
);
await handle.writeFile(contents);
} catch (error) {
if (error.code === 'ELOOP') {
throw new Error(`${label}: target is a symbolic link (${relativePath})`);
}
throw error;
} finally {
await handle?.close();
}
return target;
}
async function applyFixture(root, fixture = {}) {
for (const entry of fixture.writeFiles ?? []) {
const target = await safeSandboxWrite(root, entry.path, entry.content, 'fixture write');
if (entry.mode !== undefined) await chmod(target, entry.mode);
}
for (const entry of fixture.replaceFiles ?? []) {
const target = await sandboxPath(root, entry.path, 'fixture replace');
if ((await lstat(target)).isSymbolicLink()) {
throw new Error(`fixture replace: target is a symbolic link (${entry.path})`);
}
const source = await readFile(target, 'utf8');
const occurrences = source.split(entry.find).length - 1;
if (occurrences !== 1) {
throw new Error(
`fixture replace: stale or ambiguous match for ${entry.path} (${occurrences} matches)`,
);
}
await safeSandboxWrite(
root,
entry.path,
source.replace(entry.find, entry.replace),
'fixture replace',
);
}
for (const relativePath of fixture.removePaths ?? []) {
await rm(await sandboxPath(root, relativePath, 'fixture remove'), {
recursive: true,
force: true,
});
}
}
async function copySandbox(root, destination, selectedPaths) {
if (!selectedPaths?.length) {
await copyTree(root, destination);
return;
}
await mkdir(destination, { recursive: true });
for (const relativePath of selectedPaths) {
await copyTree(
await sandboxPath(root, relativePath, 'sandbox copy source'),
await sandboxPath(destination, relativePath, 'sandbox copy destination'),
);
}
}
async function runCase(root, gate, gateCase) {
let caseRoot = root;
if (gateCase.fixture) {
caseRoot = await mkdtemp(path.join(path.dirname(root), `.gate-case-${gate.id}-`));
await copySandbox(root, caseRoot, gateCase.fixture.copyPaths);
await applyFixture(caseRoot, gateCase.fixture);
}
try {
const environment = Object.fromEntries(
Object.entries(gateCase.environment ?? {}).map(([key, value]) => [
key,
expand(value, caseRoot),
]),
);
const result = runInvocation(caseRoot, gateCase.invocation ?? gate.invocation, environment);
return { ...result, evidence: structuredClone(gateCase.evidence) };
} finally {
if (caseRoot !== root) await rm(caseRoot, { recursive: true, force: true });
}
}
async function copyTree(source, destination) {
const stats = await lstat(source);
if (stats.isSymbolicLink()) {
await symlink(await readlink(source), destination);
return;
}
if (stats.isFile()) {
await mkdir(path.dirname(destination), { recursive: true });
await copyFile(source, destination);
await chmod(destination, stats.mode);
return;
}
if (!stats.isDirectory()) return;
await mkdir(destination, { recursive: true });
for (const child of await readdir(source, { withFileTypes: true })) {
if (COPY_SKIP.has(child.name)) continue;
const childSource = path.join(source, child.name);
const childDestination = path.join(destination, child.name);
if (child.name === 'node_modules') {
await symlink(childSource, childDestination, 'dir');
continue;
}
await copyTree(childSource, childDestination);
}
}
async function executableFiles(root, relativeRoot) {
const base = await sandboxPath(root, relativeRoot, 'gate root');
const found = [];
async function walk(current) {
for (const child of await readdir(current, { withFileTypes: true })) {
const target = path.join(current, child.name);
if (child.isDirectory()) await walk(target);
else if (child.isFile()) {
try {
await access(target, constants.X_OK);
found.push(path.relative(root, target).split(path.sep).join('/'));
} catch {
// Non-executable files are not gates for discovery purposes.
}
}
}
}
try {
await walk(base);
} catch (error) {
if (error.code !== 'ENOENT') throw error;
}
return found;
}
function rejectUnknownKeys(value, allowed, label, failures) {
if (!value || typeof value !== 'object' || Array.isArray(value)) {
failures.push(`${label}: expected an object`);
return;
}
for (const key of Object.keys(value)) {
if (!allowed.has(key)) failures.push(`${label}: unknown field ${key}`);
}
}
function rejectDuplicateIds(values, label, failures) {
const seen = new Set();
for (const value of values ?? []) {
if (typeof value?.id !== 'string' || value.id.length === 0) {
failures.push(`${label}: missing stable id`);
} else if (seen.has(value.id)) {
failures.push(`duplicate ${label} id ${value.id}`);
} else {
seen.add(value.id);
}
}
}
function requireString(value, label, failures, { allowEmpty = false } = {}) {
if (typeof value !== 'string' || (!allowEmpty && value.length === 0)) {
failures.push(`${label}: expected ${allowEmpty ? 'a string' : 'a non-empty string'}`);
}
}
function validateStringArray(value, label, failures, { allowEmpty = false } = {}) {
if (!Array.isArray(value) || (!allowEmpty && value.length === 0)) {
failures.push(`${label}: expected ${allowEmpty ? 'an array' : 'a non-empty array'}`);
return;
}
if (value.some((entry) => typeof entry !== 'string' || entry.length === 0)) {
failures.push(`${label}: entries must be non-empty strings`);
}
}
function validateOutcome(value, label, failures) {
rejectUnknownKeys(
value,
new Set(['exitCode', 'outputPattern', 'notOutputPattern']),
label,
failures,
);
if (typeof value?.exitCode !== 'number' || !Number.isInteger(value.exitCode)) {
failures.push(`${label}.exitCode: expected an integer`);
}
for (const key of ['outputPattern', 'notOutputPattern']) {
if (value?.[key] !== undefined && typeof value[key] !== 'string') {
failures.push(`${label}.${key}: expected a string`);
} else if (typeof value?.[key] === 'string' && value[key].trim().length === 0) {
failures.push(`${label}.${key}: expected a non-empty pattern`);
}
}
}
function validateFixture(fixture, label, failures) {
if (fixture === undefined) return;
rejectUnknownKeys(
fixture,
new Set(['writeFiles', 'replaceFiles', 'removePaths', 'copyPaths']),
label,
failures,
);
const writeFiles = Array.isArray(fixture?.writeFiles) ? fixture.writeFiles : [];
const replaceFiles = Array.isArray(fixture?.replaceFiles) ? fixture.replaceFiles : [];
for (const [index, entry] of writeFiles.entries()) {
rejectUnknownKeys(
entry,
new Set(['path', 'content', 'mode']),
`${label}.writeFiles[${index}]`,
failures,
);
requireString(entry?.path, `${label}.writeFiles[${index}].path`, failures);
if (typeof entry?.content !== 'string')
failures.push(`${label}.writeFiles[${index}].content: expected a string`);
if (entry?.mode !== undefined && (!Number.isInteger(entry.mode) || entry.mode < 0)) {
failures.push(`${label}.writeFiles[${index}].mode: expected a non-negative integer`);
}
}
for (const [index, entry] of replaceFiles.entries()) {
rejectUnknownKeys(
entry,
new Set(['path', 'find', 'replace']),
`${label}.replaceFiles[${index}]`,
failures,
);
requireString(entry?.path, `${label}.replaceFiles[${index}].path`, failures);
if (typeof entry?.find !== 'string')
failures.push(`${label}.replaceFiles[${index}].find: expected a string`);
if (typeof entry?.replace !== 'string')
failures.push(`${label}.replaceFiles[${index}].replace: expected a string`);
}
for (const key of ['removePaths', 'copyPaths']) {
if (fixture?.[key] !== undefined)
validateStringArray(fixture[key], `${label}.${key}`, failures, { allowEmpty: true });
}
for (const key of ['writeFiles', 'replaceFiles']) {
if (fixture?.[key] !== undefined && !Array.isArray(fixture[key])) {
failures.push(`${label}.${key}: expected an array`);
}
}
}
function validateEnvironment(environment, label, failures) {
if (environment === undefined) return;
if (!environment || typeof environment !== 'object' || Array.isArray(environment)) {
failures.push(`${label}: expected an object`);
return;
}
for (const [key, value] of Object.entries(environment)) {
if (!key || typeof value !== 'string') failures.push(`${label}.${key}: expected a string`);
}
}
function inventoriesEqual(left, right) {
return structuredValuesEqual([...left.entries()], [...right.entries()]);
}
export function consumeEvidenceSubject(gate, evidence) {
if (evidence?.subject !== gate.id) {
return `gate ${gate.id}: consumed evidence subject ${String(evidence?.subject)} does not match gate definition`;
}
return undefined;
}
const CURRENT_TREE_OBSERVATION_PATTERNS = [
/^META-NEGATIVE-CONTROL /,
/^DEPLOYMENT-NEGATIVE-CONTROL /,
/^DEPLOYED IDENTITY UNAVAILABLE /,
/^DEFECT /,
/^COMPATIBILITY /,
/^REGISTRY SUMMARY open behavior deltas: [0-9]+; required-behavior conformance is not asserted while deltas remain$/,
];
export function assertCurrentTreeObservation(observation) {
if (!CURRENT_TREE_OBSERVATION_PATTERNS.some((pattern) => pattern.test(observation))) {
throw new Error(`unsupported observation class: ${observation}`);
}
}
function writeCurrentTreeOutput(output) {
assertCurrentTreeObservation(output);
process.stdout.write(`${output}\n`);
}
function validateClosedSchema(
manifest,
failures,
{ fixtureProfile = false, requiredGateInventory } = {},
) {
if (!fixtureProfile) {
for (const population of ['criteria', 'gates', 'proseClaims', 'compatibilityScenarios']) {
if (!Array.isArray(manifest[population]) || manifest[population].length === 0) {
failures.push(`${population} population must be non-empty and anchored before evaluation`);
}
}
}
if (manifest.schemaVersion !== 1)
failures.push(`unsupported schemaVersion ${String(manifest.schemaVersion)}`);
rejectUnknownKeys(
manifest,
new Set([
'schemaVersion',
'gateRoots',
'governingClaimFiles',
'coverageBoundary',
'criteria',
'proseClaims',
'compatibilityScenarios',
'mergeAssertions',
'gates',
]),
'manifest',
failures,
);
validateStringArray(manifest.gateRoots, 'manifest.gateRoots', failures);
validateStringArray(manifest.governingClaimFiles, 'manifest.governingClaimFiles', failures, {
allowEmpty: true,
});
rejectUnknownKeys(
manifest.coverageBoundary,
new Set(['included', 'excluded', 'trackedBy']),
'coverageBoundary',
failures,
);
validateStringArray(manifest.coverageBoundary?.included, 'coverageBoundary.included', failures);
validateStringArray(manifest.coverageBoundary?.excluded, 'coverageBoundary.excluded', failures, {
allowEmpty: true,
});
requireString(manifest.coverageBoundary?.trackedBy, 'coverageBoundary.trackedBy', failures);
if (manifest.mergeAssertions !== undefined) {
rejectUnknownKeys(
manifest.mergeAssertions,
new Set(['mode', 'deferredReplayOwner', 'trustDependencies', 'postMergeResponse']),
'mergeAssertions',
failures,
);
for (const key of ['mode', 'deferredReplayOwner', 'postMergeResponse']) {
requireString(manifest.mergeAssertions?.[key], `mergeAssertions.${key}`, failures);
}
validateStringArray(
manifest.mergeAssertions?.trustDependencies,
'mergeAssertions.trustDependencies',
failures,
);
}
rejectDuplicateIds(manifest.criteria, 'criterion', failures);
for (const criterion of manifest.criteria ?? []) {
rejectUnknownKeys(
criterion,
new Set([
'id',
'originalText',
'currentText',
'claimType',
'source',
'meaningChanges',
'caseRefs',
'gateRefs',
]),
`criterion ${criterion.id}`,
failures,
);
for (const key of ['id', 'originalText', 'currentText', 'claimType', 'source']) {
requireString(criterion[key], `criterion ${criterion.id}.${key}`, failures);
}
if (!Array.isArray(criterion.meaningChanges)) {
failures.push(`criterion ${criterion.id}.meaningChanges: expected an array`);
}
for (const [index, change] of (criterion.meaningChanges ?? []).entries()) {
rejectUnknownKeys(
change,
new Set(['originalText', 'restatement', 'reason', 'finding', 'task', 'date']),
`criterion ${criterion.id}.meaningChanges[${index}]`,
failures,
);
for (const key of ['originalText', 'restatement', 'reason', 'finding', 'task', 'date']) {
requireString(
change?.[key],
`criterion ${criterion.id}.meaningChanges[${index}].${key}`,
failures,
);
}
}
if (POPULATION_CRITERION_IDS.has(criterion.id) && criterion.gateRefs === undefined) {
failures.push(`${criterion.id}: gateRefs population binding is required`);
}
if (criterion.gateRefs !== undefined) {
validateStringArray(criterion.gateRefs, `criterion ${criterion.id}.gateRefs`, failures);
}
if (!Array.isArray(criterion.caseRefs) || criterion.caseRefs.length === 0) {
failures.push(`${criterion.id}: no declared exercising cases`);
} else {
if (criterion.caseRefs.some((caseRef) => typeof caseRef !== 'string' || !caseRef)) {
failures.push(`${criterion.id}: declared exercising cases must be non-empty strings`);
}
if (new Set(criterion.caseRefs).size !== criterion.caseRefs.length) {
failures.push(`${criterion.id}: duplicate declared exercising case`);
}
}
}
rejectDuplicateIds(manifest.proseClaims, 'prose claim', failures);
for (const claim of manifest.proseClaims ?? []) {
rejectUnknownKeys(
claim,
new Set(['id', 'criterionId', 'caseRef']),
`prose claim ${claim.id}`,
failures,
);
for (const key of ['id', 'criterionId', 'caseRef']) {
requireString(claim[key], `GATE-CLAIM:${claim.id}.${key}`, failures);
}
if (typeof claim.caseRef !== 'string' || claim.caseRef.length === 0) {
failures.push(`GATE-CLAIM:${claim.id} has no declared exercising case`);
}
}
rejectDuplicateIds(manifest.compatibilityScenarios, 'compatibility scenario', failures);
for (const scenario of manifest.compatibilityScenarios ?? []) {
rejectUnknownKeys(
scenario,
new Set([
'id',
'construction',
'caseRefs',
'invocation',
'expected',
'environment',
'fixture',
]),
`compatibility scenario ${scenario.id}`,
failures,
);
for (const key of ['id', 'construction']) {
requireString(scenario[key], `compatibility scenario ${scenario.id}.${key}`, failures);
}
if (!Array.isArray(scenario.caseRefs) || scenario.caseRefs.length === 0) {
failures.push(`${scenario.id}: compatibility construction has no referenced conditions`);
} else {
validateStringArray(scenario.caseRefs, `${scenario.id}.caseRefs`, failures);
}
if (!Array.isArray(scenario.invocation) || scenario.invocation.length === 0) {
failures.push(`${scenario.id}: compatibility construction invocation is missing`);
} else {
validateStringArray(scenario.invocation, `${scenario.id}.invocation`, failures);
}
validateOutcome(scenario.expected, `${scenario.id}.expected`, failures);
validateEnvironment(scenario.environment, `${scenario.id}.environment`, failures);
validateFixture(scenario.fixture, `${scenario.id}.fixture`, failures);
}
rejectDuplicateIds(manifest.gates, 'gate', failures);
if (!fixtureProfile) {
if (!(requiredGateInventory instanceof Map) || requiredGateInventory.size === 0) {
failures.push('same-checkout required-gate baseline is absent or empty');
} else {
if (!inventoriesEqual(REQUIRED_GATE_INVENTORY, requiredGateInventory)) {
for (const [requiredId, requiredSource] of requiredGateInventory) {
if (REQUIRED_GATE_INVENTORY.get(requiredId) !== requiredSource) {
failures.push(
`same-checkout required-gate baseline rejects verifier inventory drift at ${requiredId}`,
);
}
}
for (const [requiredId, requiredSource] of REQUIRED_GATE_INVENTORY) {
if (requiredGateInventory.get(requiredId) !== requiredSource) {
failures.push(
`verifier inventory ${requiredId} is absent or changed in same-checkout required-gate baseline`,
);
}
}
}
for (const [requiredId, requiredSource] of requiredGateInventory) {
const registered = (manifest.gates ?? []).find((gate) => gate?.id === requiredId);
if (!registered || registered.source !== requiredSource) {
failures.push(
`same-checkout required-gate baseline rejects manifest drift at ${requiredId}: required source ${requiredSource}`,
);
}
}
}
}
for (const gate of manifest.gates ?? []) {
rejectUnknownKeys(
gate,
new Set([
'id',
'source',
'invocation',
'deployment',
'inertMutation',
'cases',
'discoveryAliases',
]),
`gate ${gate.id}`,
failures,
);
requireString(gate.id, `gate ${gate.id}.id`, failures);
requireString(gate.source, `gate ${gate.id}.source`, failures);
rejectDuplicateIds(gate.cases, `case in gate ${gate.id}`, failures);
if (!Array.isArray(gate.invocation) || gate.invocation.length === 0) {
failures.push(`${gate.id}: exact invocation is missing`);
} else {
validateStringArray(gate.invocation, `${gate.id}.invocation`, failures);
}
if (gate.discoveryAliases !== undefined) {
validateStringArray(gate.discoveryAliases, `${gate.id}.discoveryAliases`, failures, {
allowEmpty: true,
});
}
rejectUnknownKeys(
gate.deployment,
new Set(['kind', 'reason', 'source', 'path', 'unavailableOwner', 'observedSha256']),
`${gate.id}.deployment`,
failures,
);
if (!['none', 'file'].includes(gate.deployment?.kind)) {
failures.push(`${gate.id}: unsupported deployment kind ${String(gate.deployment?.kind)}`);
} else if (gate.deployment.kind === 'none') {
requireString(gate.deployment.reason, `${gate.id}.deployment.reason`, failures);
} else {
for (const key of ['source', 'path', 'unavailableOwner', 'observedSha256']) {
requireString(gate.deployment[key], `${gate.id}.deployment.${key}`, failures);
}
}
rejectUnknownKeys(
gate.inertMutation,
new Set(['file', 'find', 'replace', 'caseId', 'expected', 'sandboxFiles']),
`${gate.id}.inertMutation`,
failures,
);
for (const key of ['file', 'find', 'replace']) {
if (typeof gate.inertMutation?.[key] !== 'string') {
failures.push(`${gate.id}.inertMutation.${key}: expected a string`);
}
}
if (gate.inertMutation?.caseId !== undefined) {
requireString(gate.inertMutation.caseId, `${gate.id}.inertMutation.caseId`, failures);
}
if (gate.inertMutation?.sandboxFiles !== undefined) {
validateStringArray(
gate.inertMutation.sandboxFiles,
`${gate.id}.inertMutation.sandboxFiles`,
failures,
{ allowEmpty: true },
);
}
validateOutcome(gate.inertMutation?.expected, `${gate.id}.inertMutation.expected`, failures);
for (const gateCase of gate.cases ?? []) {
rejectUnknownKeys(
gateCase,
new Set([
'id',
'criterionIds',
'mustFail',
'invocation',
'required',
'actual',
'evidence',
'reasonPattern',
'environment',
'fixture',
'defect',
]),
`${gate.id}/${gateCase.id}`,
failures,
);
requireString(gateCase.id, `${gate.id}/${gateCase.id}.id`, failures);
validateStringArray(
gateCase.criterionIds,
`${gate.id}/${gateCase.id}.criterionIds`,
failures,
);
if (typeof gateCase.mustFail !== 'boolean') {
failures.push(`${gate.id}/${gateCase.id}.mustFail: expected a boolean`);
}
validateOutcome(gateCase.required, `${gate.id}/${gateCase.id}.required`, failures);
validateOutcome(gateCase.actual, `${gate.id}/${gateCase.id}.actual`, failures);
rejectUnknownKeys(
gateCase.evidence,
new Set(['subject']),
`${gate.id}/${gateCase.id}.evidence`,
failures,
);
requireString(
gateCase.evidence?.subject,
`${gate.id}/${gateCase.id}.evidence.subject`,
failures,
);
if (gateCase.invocation !== undefined) {
validateStringArray(gateCase.invocation, `${gate.id}/${gateCase.id}.invocation`, failures);
}
if (typeof gateCase.reasonPattern !== 'string') {
failures.push(`${gate.id}/${gateCase.id}.reasonPattern: expected a string`);
}
if (gateCase.mustFail === true && !gateCase.reasonPattern?.trim()) {
failures.push(
`${gate.id}/${gateCase.id}: must-fail case requires a non-empty reasonPattern`,
);
}
validateEnvironment(gateCase.environment, `${gate.id}/${gateCase.id}.environment`, failures);
validateFixture(gateCase.fixture, `${gate.id}/${gateCase.id}.fixture`, failures);
if (gateCase.defect !== undefined) {
rejectUnknownKeys(
gateCase.defect,
new Set(['owner', 'reason']),
`${gate.id}/${gateCase.id}.defect`,
failures,
);
requireString(gateCase.defect.owner, `${gate.id}/${gateCase.id}.defect.owner`, failures);
requireString(gateCase.defect.reason, `${gate.id}/${gateCase.id}.defect.reason`, failures);
}
}
}
}
function validateStructure(manifest, failures, options = {}) {
validateClosedSchema(manifest, failures, options);
const criteria = new Map((manifest.criteria ?? []).map((criterion) => [criterion.id, criterion]));
const boundCriteria = new Set();
const negativeBoundCriteria = new Set();
const observedCaseRefs = new Map();
for (const gate of manifest.gates ?? []) {
const negativeCases = (gate.cases ?? []).filter((gateCase) => gateCase.mustFail === true);
if (negativeCases.length === 0) failures.push(`${gate.id}: no negative control`);
if (!gate.deployment?.kind) failures.push(`${gate.id}: deployment identity is not declared`);
for (const gateCase of gate.cases ?? []) {
for (const criterionId of gateCase.criterionIds ?? []) {
if (!criteria.has(criterionId)) {
failures.push(`${gate.id}/${gateCase.id}: unknown criterion ${criterionId}`);
}
boundCriteria.add(criterionId);
const caseRef = `${gate.id}/${gateCase.id}`;
if (!observedCaseRefs.has(criterionId)) observedCaseRefs.set(criterionId, new Set());
observedCaseRefs.get(criterionId).add(caseRef);
if (gateCase.mustFail === true) negativeBoundCriteria.add(criterionId);
}
if (!structuredValuesEqual(gateCase.required, gateCase.actual) && !gateCase.defect?.owner) {
failures.push(`${gate.id}/${gateCase.id}: behavior delta requires a tracked owner`);
}
}
}
const registeredGateIds = new Set((manifest.gates ?? []).map((gate) => gate.id));
for (const criterion of criteria.values()) {
if (criterion.gateRefs === undefined) continue;
const referenced = new Set(criterion.gateRefs);
for (const gateId of registeredGateIds) {
if (!referenced.has(gateId)) {
failures.push(`${criterion.id}: gate population binding is missing ${gateId}`);
}
}
for (const gateId of referenced) {
if (!registeredGateIds.has(gateId)) {
failures.push(`${criterion.id}: gate population binding references unknown gate ${gateId}`);
}
}
}
for (const claim of manifest.proseClaims ?? []) {
if (!criteria.has(claim.criterionId)) {
failures.push(`GATE-CLAIM:${claim.id} references unknown criterion ${claim.criterionId}`);
continue;
}
const found = findGateCase(manifest, claim.caseRef ?? '');
if (!found) {
failures.push(`GATE-CLAIM:${claim.id} references missing exercising case ${claim.caseRef}`);
} else if (
found.gateCase.mustFail !== true ||
!found.gateCase.criterionIds?.includes(claim.criterionId)
) {
failures.push(
`GATE-CLAIM:${claim.id} exercising case ${claim.caseRef} does not exercise criterion ${claim.criterionId}`,
);
}
}
for (const criterion of criteria.values()) {
if (!boundCriteria.has(criterion.id)) failures.push(`${criterion.id}: no bound case`);
else if (!negativeBoundCriteria.has(criterion.id)) {
failures.push(`${criterion.id}: no must-fail case exercises this criterion`);
}
const declaredRefs = new Set(criterion.caseRefs ?? []);
const actualRefs = observedCaseRefs.get(criterion.id) ?? new Set();
for (const caseRef of declaredRefs) {
const found = findGateCase(manifest, caseRef);
if (!found) failures.push(`${criterion.id}: declared exercising case ${caseRef} is missing`);
else if (!actualRefs.has(caseRef)) {
failures.push(`${criterion.id}: declared exercising case ${caseRef} is not bound`);
}
}
for (const caseRef of actualRefs) {
if (!declaredRefs.has(caseRef)) {
failures.push(`${criterion.id}: bound to undeclared exercising case ${caseRef}`);
}
}
if (
criterion.originalText !== criterion.currentText &&
(!Array.isArray(criterion.meaningChanges) || criterion.meaningChanges.length === 0)
) {
failures.push(`${criterion.id}: missing meaning-change provenance`);
}
}
const byConstruction = new Map();
for (const scenario of manifest.compatibilityScenarios ?? []) {
const previous = byConstruction.get(scenario.construction);
if (previous && !structuredValuesEqual(previous.expected, scenario.expected)) {
failures.push(`${previous.id} and ${scenario.id}: modeled compatibility conflict`);
} else {
byConstruction.set(scenario.construction, scenario);
}
}
}
async function validateClaims(root, manifest, failures) {
const registered = new Set((manifest.proseClaims ?? []).map((claim) => claim.id));
const observed = new Set();
for (const relativeFile of manifest.governingClaimFiles ?? []) {
const contents = await readFile(
await sandboxPath(root, relativeFile, 'governing claim file'),
'utf8',
);
for (const match of contents.matchAll(/GATE-CLAIM:([A-Z0-9][A-Z0-9-]*)/g)) {
observed.add(match[1]);
if (!registered.has(match[1])) {
failures.push(`GATE-CLAIM:${match[1]} is unbound in ${relativeFile}`);
}
}
}
for (const claimId of registered) {
if (!observed.has(claimId)) failures.push(`GATE-CLAIM:${claimId} marker is missing`);
}
}
async function validateDiscovery(root, manifest, failures) {
const registered = new Set(
(manifest.gates ?? []).flatMap((gate) => [gate.source, ...(gate.discoveryAliases ?? [])]),
);
for (const gateRoot of manifest.gateRoots ?? []) {
for (const executable of await executableFiles(root, gateRoot)) {
if (!registered.has(executable)) failures.push(`unregistered gate: ${executable}`);
}
}
}
async function deploymentFilesEqual(sourcePath, deployedPath) {
const [source, deployed] = await Promise.all([readFile(sourcePath), readFile(deployedPath)]);
return source.equals(deployed);
}
async function validateDeployment(root, gate, failures, observations) {
if (gate.deployment?.kind !== 'file') return;
const sourcePath = await sandboxPath(
root,
gate.deployment.source ?? gate.source,
`${gate.id} deployment source`,
);
const deployedPath = path.isAbsolute(expand(gate.deployment.path, root))
? expand(gate.deployment.path, root)
: path.resolve(root, expand(gate.deployment.path, root));
const source = await readFile(sourcePath);
let deployed;
try {
deployed = await readFile(deployedPath);
} catch (error) {
if (error.code !== 'ENOENT') throw error;
const observedHash = createHash('sha256').update(source).digest('hex');
if (
!gate.deployment.unavailableOwner ||
!gate.deployment.observedSha256 ||
gate.deployment.observedSha256 !== observedHash
) {
failures.push(
`${gate.id}: deployed counterpart is unavailable and no current tracked observation matches source`,
);
return;
}
observations.push(
`DEPLOYED IDENTITY UNAVAILABLE (owner: ${gate.deployment.unavailableOwner}) ${gate.id}: ${deployedPath} is outside this runner; source matches pinned observation ${observedHash}, live equality is not inferred`,
);
return;
}
if (!(await deploymentFilesEqual(sourcePath, deployedPath))) {
failures.push(`${gate.id}: source versus deployed copy drift`);
return;
}
const controlRoot = await mkdtemp(
path.join(path.dirname(root), `.gate-deployment-control-${gate.id}-`),
);
try {
const alteredPath = path.join(controlRoot, 'deployed-copy');
await writeFile(
alteredPath,
Buffer.concat([deployed, Buffer.from('\n# gate deployment drift control\n')]),
);
if (await deploymentFilesEqual(sourcePath, alteredPath)) {
failures.push(`${gate.id}: deployed-copy drift negative control was ineffective`);
} else {
observations.push(`DEPLOYMENT-NEGATIVE-CONTROL ${gate.id}: observed red`);
}
} finally {
await rm(controlRoot, { recursive: true, force: true });
}
}
async function validateMutation(root, gate, failures, observations) {
const mutation = gate.inertMutation;
if (
!mutation?.file ||
typeof mutation.find !== 'string' ||
typeof mutation.replace !== 'string' ||
typeof mutation.expected?.exitCode !== 'number'
) {
failures.push(`${gate.id}: declared inert mutation is incomplete`);
return;
}
const mutationPath = await sandboxPath(root, mutation.file, `${gate.id} mutation source`);
let source;
try {
source = await readFile(mutationPath, 'utf8');
} catch (error) {
failures.push(`${gate.id}: mutation source unreadable: ${error.message}`);
return;
}
const occurrences = source.split(mutation.find).length - 1;
if (occurrences !== 1) {
failures.push(
`${gate.id}: declared inert mutation is stale or ambiguous (${occurrences} matches)`,
);
return;
}
const targetCase = mutation.caseId
? (gate.cases ?? []).find((gateCase) => gateCase.id === mutation.caseId)
: (gate.cases ?? []).find((gateCase) => gateCase.mustFail === true);
if (!targetCase) {
failures.push(
mutation.caseId
? `${gate.id}: declared mutation case ${mutation.caseId} was not found`
: `${gate.id}: declared mutation has no must-fail case`,
);
return;
}
const sandbox = await mkdtemp(path.join(path.dirname(root), `.gate-sandbox-${gate.id}-`));
try {
await copySandbox(root, sandbox, mutation.sandboxFiles);
await safeSandboxWrite(
sandbox,
mutation.file,
source.replace(mutation.find, mutation.replace),
`${gate.id} sandbox mutation write`,
);
await applyFixture(sandbox, targetCase.fixture);
const environment = Object.fromEntries(
Object.entries(targetCase.environment ?? {}).map(([key, value]) => [
key,
expand(value, sandbox),
]),
);
const result = runInvocation(sandbox, targetCase.invocation ?? gate.invocation, environment);
if (result.error || result.signal) {
failures.push(
`${gate.id}: mutation execution crashed${result.signal ? ` with ${result.signal}` : ''}${result.error ? `: ${result.error.message}` : ''}`,
);
} else if (!outcomeMatches(mutation.expected, result)) {
failures.push(
`${gate.id}: mutation produced unexpected outcome ${String(result.status)}; expected ${JSON.stringify(mutation.expected)}`,
);
} else if (resultMatches(targetCase, result)) {
failures.push(`${gate.id}: declared inert mutation was ineffective`);
} else {
observations.push(`META-NEGATIVE-CONTROL ${gate.id}: observed red`);
}
} finally {
await rm(sandbox, { recursive: true, force: true });
}
}
function findGateCase(manifest, caseRef) {
if (typeof caseRef !== 'string') return undefined;
const separator = caseRef.indexOf('/');
if (separator < 1) return undefined;
const gateId = caseRef.slice(0, separator);
const caseId = caseRef.slice(separator + 1);
const gate = (manifest.gates ?? []).find((candidate) => candidate.id === gateId);
const gateCase = (gate?.cases ?? []).find((candidate) => candidate.id === caseId);
return gate && gateCase ? { gate, gateCase } : undefined;
}
async function runCompatibilityScenario(root, manifest, scenario, failures, observations) {
const referenced = [];
for (const caseRef of scenario.caseRefs ?? []) {
const found = findGateCase(manifest, caseRef);
if (!found) {
failures.push(
`${scenario.id}: compatibility construction references missing case ${caseRef}`,
);
} else {
referenced.push({ caseRef, ...found });
}
}
if (referenced.length !== (scenario.caseRefs ?? []).length) return;
const sandbox = await mkdtemp(path.join(path.dirname(root), `.gate-compat-${scenario.id}-`));
try {
await copySandbox(root, sandbox);
const environment = {};
const writeSignatures = new Map();
const removedPaths = new Set();
const fixtures = [...referenced.map(({ gateCase }) => gateCase.fixture), scenario.fixture];
for (const fixture of fixtures.filter(Boolean)) {
for (const entry of fixture.writeFiles ?? []) {
const signature = JSON.stringify({ content: entry.content, mode: entry.mode });
const previous = writeSignatures.get(entry.path);
if (previous !== undefined && previous !== signature) {
failures.push(`${scenario.id}: incompatible fixture writes for ${entry.path}`);
return;
}
if (removedPaths.has(entry.path)) {
failures.push(`${scenario.id}: fixture both writes and removes ${entry.path}`);
return;
}
writeSignatures.set(entry.path, signature);
}
for (const removed of fixture.removePaths ?? []) {
if (writeSignatures.has(removed)) {
failures.push(`${scenario.id}: fixture both writes and removes ${removed}`);
return;
}
removedPaths.add(removed);
}
await applyFixture(sandbox, fixture);
}
for (const source of [
...referenced.map(({ gateCase }) => gateCase.environment),
scenario.environment,
]) {
for (const [key, value] of Object.entries(source ?? {})) {
if (environment[key] !== undefined && environment[key] !== value) {
failures.push(`${scenario.id}: incompatible environment values for ${key}`);
return;
}
environment[key] = value;
}
}
const expandedEnvironment = Object.fromEntries(
Object.entries(environment).map(([key, value]) => [key, expand(value, sandbox)]),
);
const result = runInvocation(sandbox, scenario.invocation, expandedEnvironment);
if (result.error || result.signal || !outcomeMatches(scenario.expected, result)) {
failures.push(
`${scenario.id}: combined compatibility construction ${scenario.construction} observed ${String(result.status)}${result.signal ? ` signal ${result.signal}` : ''}${result.error ? ` error ${result.error.message}` : ''}; expected ${JSON.stringify(scenario.expected)}`,
);
} else {
observations.push(`COMPATIBILITY ${scenario.id}: observed expected outcome`);
}
} finally {
await rm(sandbox, { recursive: true, force: true });
}
}
export async function verifyRegistry(options) {
const failures = [];
const observations = [];
const manifestPath = path.resolve(options.root, options.manifest);
const manifest = JSON.parse(await readFile(manifestPath, 'utf8'));
let requiredGateInventory;
if (!options.fixtureProfile) {
const baselinePath = path.resolve(options.root, 'gates/required-gates.baseline.json');
try {
const baseline = JSON.parse(await readFile(baselinePath, 'utf8'));
if (
baseline.schemaVersion !== 1 ||
!Array.isArray(baseline.gates) ||
Object.keys(baseline).some((key) => !['schemaVersion', 'purpose', 'gates'].includes(key)) ||
baseline.gates.some(
(gate) =>
!gate ||
typeof gate !== 'object' ||
Array.isArray(gate) ||
Object.keys(gate).some((key) => !['id', 'source'].includes(key)) ||
typeof gate.id !== 'string' ||
gate.id.length === 0 ||
typeof gate.source !== 'string' ||
gate.source.length === 0,
)
) {
failures.push('same-checkout required-gate baseline has unsupported structure');
} else {
requiredGateInventory = new Map(baseline.gates.map((gate) => [gate.id, gate.source]));
if (requiredGateInventory.size !== baseline.gates.length) {
failures.push('same-checkout required-gate baseline has duplicate gate ids');
}
}
} catch (error) {
failures.push(`same-checkout required-gate baseline cannot be read: ${error.message}`);
}
}
validateStructure(manifest, failures, { ...options, requiredGateInventory });
if (options.structureOnly) return { failures, manifest, observations };
async function collectPhaseFailure(label, action) {
try {
return await action();
} catch (error) {
failures.push(`${label}: ${error.message}`);
return undefined;
}
}
await collectPhaseFailure('governing claim validation failed', () =>
validateClaims(options.root, manifest, failures),
);
await collectPhaseFailure('gate discovery failed', () =>
validateDiscovery(options.root, manifest, failures),
);
for (const gate of manifest.gates ?? []) {
await collectPhaseFailure(`${gate.id}: deployment validation failed`, () =>
validateDeployment(options.root, gate, failures, observations),
);
for (const gateCase of gate.cases ?? []) {
const result = await collectPhaseFailure(
`${gate.id}/${gateCase.id}: case execution failed`,
() => runCase(options.root, gate, gateCase),
);
if (!result) continue;
const combined = `${result.stdout ?? ''}\n${result.stderr ?? ''}`;
try {
const subjectFailure = consumeEvidenceSubject(gate, result.evidence);
if (subjectFailure) failures.push(subjectFailure);
if (!outcomeMatches(gateCase.actual, result)) {
failures.push(
`${gate.id}/${gateCase.id}: observed exit ${String(result.status)}${result.signal ? ` signal ${result.signal}` : ''}${result.error ? ` error ${result.error.message}` : ''} or output disagrees with registry actual ${JSON.stringify(gateCase.actual)}`,
);
}
if (gateCase.reasonPattern && !new RegExp(gateCase.reasonPattern, 'm').test(combined)) {
failures.push(`${gate.id}/${gateCase.id}: did not fail for its stated reason`);
}
} catch (error) {
failures.push(`${gate.id}/${gateCase.id}: outcome validation failed: ${error.message}`);
}
if (!structuredValuesEqual(gateCase.required, gateCase.actual) && gateCase.defect?.owner) {
observations.push(
`DEFECT (owner: ${gateCase.defect.owner}) ${gate.id}/${gateCase.id}: required ${JSON.stringify(gateCase.required)}, actual ${JSON.stringify(gateCase.actual)}`,
);
}
}
await collectPhaseFailure(`${gate.id}: mutation validation failed`, () =>
validateMutation(options.root, gate, failures, observations),
);
}
for (const scenario of manifest.compatibilityScenarios ?? []) {
await collectPhaseFailure(`${scenario.id}: compatibility validation failed`, () =>
runCompatibilityScenario(options.root, manifest, scenario, failures, observations),
);
}
return { failures, manifest, observations };
}
async function main() {
try {
const options = parseArgs(process.argv.slice(2));
const { failures, observations } = await verifyRegistry(options);
for (const observation of observations) {
writeCurrentTreeOutput(observation);
}
if (failures.length > 0) {
for (const failure of failures) process.stderr.write(`GATE VERIFY FAILED: ${failure}\n`);
process.exitCode = 1;
return;
}
const defects = observations.filter((line) => line.startsWith('DEFECT ')).length;
writeCurrentTreeOutput(
`REGISTRY SUMMARY open behavior deltas: ${defects}; required-behavior conformance is not asserted while deltas remain`,
);
} catch (error) {
process.stderr.write(`GATE VERIFY FAILED: ${error.message}\n`);
process.exitCode = 1;
}
}
if (import.meta.url === `file://${process.argv[1]}`) await main();