139 lines
4.6 KiB
JavaScript
139 lines
4.6 KiB
JavaScript
#!/usr/bin/env node
|
|
|
|
import { chmod, mkdir, mkdtemp, readFile, rm, writeFile } from 'node:fs/promises';
|
|
import os from 'node:os';
|
|
import path from 'node:path';
|
|
|
|
import { verifyRegistry } from './gate-verify.mjs';
|
|
|
|
const mode = process.argv[2];
|
|
const root = process.cwd();
|
|
const source = JSON.parse(await readFile(path.join(root, 'gates/gates.manifest.json'), 'utf8'));
|
|
const expectedGateIds = source.gates.map((gate) => gate.id);
|
|
if (expectedGateIds.length === 0) {
|
|
process.stderr.write('gate population control requires a non-empty anchored inventory\n');
|
|
process.exit(2);
|
|
}
|
|
|
|
async function rejectedForEveryGate(mutate, diagnostic) {
|
|
for (const gateId of expectedGateIds) {
|
|
const manifest = structuredClone(source);
|
|
const gate = manifest.gates.find((candidate) => candidate.id === gateId);
|
|
mutate(gate);
|
|
const directory = await mkdtemp(path.join(os.tmpdir(), 'gate-population-control-'));
|
|
const manifestPath = path.join(directory, 'manifest.json');
|
|
try {
|
|
await writeFile(manifestPath, `${JSON.stringify(manifest)}\n`);
|
|
const result = await verifyRegistry({
|
|
root,
|
|
manifest: manifestPath,
|
|
structureOnly: true,
|
|
fixtureProfile: false,
|
|
});
|
|
if (!result.failures.some((failure) => diagnostic(failure, gateId))) return false;
|
|
} finally {
|
|
await rm(directory, { recursive: true, force: true });
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
let rejected;
|
|
if (mode === 'evidence-subject') {
|
|
rejected = true;
|
|
for (const gateId of expectedGateIds) {
|
|
const directory = await mkdtemp(path.join(os.tmpdir(), 'gate-evidence-consumption-'));
|
|
try {
|
|
await mkdir(path.join(directory, 'gates'), { recursive: true });
|
|
const probe = path.join(directory, 'gates', 'probe.sh');
|
|
await writeFile(probe, '#!/bin/sh\necho EVIDENCE_PROBE >&2\nexit 7\n');
|
|
await chmod(probe, 0o755);
|
|
const manifest = {
|
|
schemaVersion: 1,
|
|
gateRoots: ['gates'],
|
|
governingClaimFiles: [],
|
|
coverageBoundary: { included: ['evidence fixture'], excluded: [], trackedBy: 'RM-02' },
|
|
criteria: [
|
|
{
|
|
id: 'EVIDENCE-CONSUMPTION',
|
|
originalText: 'Consumed evidence stays bound to its gate.',
|
|
currentText: 'Consumed evidence stays bound to its gate.',
|
|
claimType: 'integrity',
|
|
source: 'gate-population-control',
|
|
meaningChanges: [],
|
|
caseRefs: [`${gateId}/probe`],
|
|
},
|
|
],
|
|
proseClaims: [],
|
|
compatibilityScenarios: [],
|
|
gates: [
|
|
{
|
|
id: gateId,
|
|
source: 'gates/probe.sh',
|
|
invocation: ['gates/probe.sh'],
|
|
deployment: { kind: 'none', reason: 'population fixture' },
|
|
inertMutation: {
|
|
file: 'gates/probe.sh',
|
|
find: 'exit 7',
|
|
replace: 'exit 0',
|
|
caseId: 'probe',
|
|
expected: { exitCode: 0 },
|
|
},
|
|
cases: [
|
|
{
|
|
id: 'probe',
|
|
criterionIds: ['EVIDENCE-CONSUMPTION'],
|
|
mustFail: true,
|
|
required: { exitCode: 7 },
|
|
actual: { exitCode: 7 },
|
|
evidence: { subject: 'different-gate-subject' },
|
|
reasonPattern: 'EVIDENCE_PROBE',
|
|
},
|
|
],
|
|
},
|
|
],
|
|
};
|
|
const manifestPath = path.join(directory, 'gates', 'gates.manifest.json');
|
|
await writeFile(manifestPath, `${JSON.stringify(manifest)}\n`);
|
|
const result = await verifyRegistry({
|
|
root: directory,
|
|
manifest: manifestPath,
|
|
structureOnly: false,
|
|
fixtureProfile: true,
|
|
});
|
|
if (
|
|
!result.failures.some(
|
|
(failure) =>
|
|
failure.includes(`gate ${gateId}: consumed evidence subject`) &&
|
|
failure.includes('does not match gate definition'),
|
|
)
|
|
) {
|
|
rejected = false;
|
|
break;
|
|
}
|
|
} finally {
|
|
await rm(directory, { recursive: true, force: true });
|
|
}
|
|
}
|
|
} else if (mode === 'type-strict') {
|
|
rejected = await rejectedForEveryGate(
|
|
(gate) => {
|
|
gate.cases[0].actual.exitCode = '0';
|
|
},
|
|
(failure, gateId) =>
|
|
failure.includes(
|
|
`${gateId}/${source.gates.find((gate) => gate.id === gateId).cases[0].id}.actual.exitCode`,
|
|
) && failure.includes('expected an integer'),
|
|
);
|
|
} else {
|
|
process.stderr.write(`unknown gate population control ${String(mode)}\n`);
|
|
process.exit(2);
|
|
}
|
|
|
|
if (!rejected) {
|
|
process.stdout.write(`${mode} population control did not reject every registered gate\n`);
|
|
process.exit(0);
|
|
}
|
|
process.stderr.write(`${mode} population control rejected every registered gate\n`);
|
|
process.exit(1);
|