Files
stack/scripts/gate-remediation.test.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

284 lines
11 KiB
JavaScript

import assert from 'node:assert/strict';
import { copyFile, mkdir, mkdtemp, readFile, rm, writeFile } from 'node:fs/promises';
import os from 'node:os';
import path from 'node:path';
import { spawnSync } from 'node:child_process';
import test from 'node:test';
const root = process.cwd();
const verifierPath = path.join(root, 'scripts', 'gate-verify.mjs');
const manifestPath = path.join(root, 'gates', 'gates.manifest.json');
const requiredGateId = 'hook-pre-push';
function output(result) {
return `${result.stdout ?? ''}\n${result.stderr ?? ''}`;
}
function shrinkManifest(manifest, removedGateId) {
const removedGate = manifest.gates.find((gate) => gate.id === removedGateId);
assert.ok(removedGate, `fixture gate ${removedGateId} must exist`);
const removedCaseRefs = new Set(
removedGate.cases.map((gateCase) => `${removedGateId}/${gateCase.id}`),
);
const removedCriterionIds = new Set(
manifest.criteria
.filter(
(criterion) =>
criterion.caseRefs.length > 0 &&
criterion.caseRefs.every((caseRef) => removedCaseRefs.has(caseRef)),
)
.map((criterion) => criterion.id),
);
manifest.gates = manifest.gates.filter((gate) => gate.id !== removedGateId);
manifest.criteria = manifest.criteria
.filter((criterion) => !removedCriterionIds.has(criterion.id))
.map((criterion) => ({
...criterion,
caseRefs: criterion.caseRefs.filter((caseRef) => !removedCaseRefs.has(caseRef)),
...(criterion.gateRefs
? { gateRefs: criterion.gateRefs.filter((gateId) => gateId !== removedGateId) }
: {}),
}));
manifest.proseClaims = manifest.proseClaims.filter(
(claim) => !removedCriterionIds.has(claim.criterionId) && !removedCaseRefs.has(claim.caseRef),
);
manifest.compatibilityScenarios = manifest.compatibilityScenarios
.map((scenario) => ({
...scenario,
caseRefs: scenario.caseRefs.filter((caseRef) => !removedCaseRefs.has(caseRef)),
}))
.filter((scenario) => scenario.caseRefs.length > 0);
for (const gate of manifest.gates) {
for (const gateCase of gate.cases) {
gateCase.criterionIds = gateCase.criterionIds.filter(
(criterionId) => !removedCriterionIds.has(criterionId),
);
}
}
}
test('shrinking the verifier inventory and manifest together is rejected by a same-checkout baseline', async () => {
const fixture = await mkdtemp(path.join(os.tmpdir(), 'rm02-shrink-both-'));
try {
await mkdir(path.join(fixture, 'scripts'), { recursive: true });
await mkdir(path.join(fixture, 'gates'), { recursive: true });
const verifier = await readFile(verifierPath, 'utf8');
const inventoryEntry = " ['hook-pre-push', '.husky/pre-push'],\n";
assert.equal(verifier.split(inventoryEntry).length - 1, 1, 'source inventory fixture drifted');
await writeFile(
path.join(fixture, 'scripts', 'gate-verify.mjs'),
verifier.replace(inventoryEntry, ''),
);
await copyFile(
path.join(root, 'gates', 'required-gates.baseline.json'),
path.join(fixture, 'gates', 'required-gates.baseline.json'),
);
const manifest = JSON.parse(await readFile(manifestPath, 'utf8'));
shrinkManifest(manifest, requiredGateId);
await writeFile(
path.join(fixture, 'gates', 'gates.manifest.json'),
`${JSON.stringify(manifest)}\n`,
);
const result = spawnSync(
process.execPath,
[
path.join(fixture, 'scripts', 'gate-verify.mjs'),
'--root',
fixture,
'--manifest',
'gates/gates.manifest.json',
'--structure-only',
],
{ cwd: fixture, encoding: 'utf8' },
);
assert.notEqual(result.status, 0, 'shrinking source anchor and manifest together must go red');
assert.match(output(result), /same-checkout required-gate baseline.*hook-pre-push/i);
} finally {
await rm(fixture, { recursive: true, force: true });
}
});
test('shrinking the same-checkout baseline and manifest together is rejected by verifier inventory', async () => {
const fixture = await mkdtemp(path.join(os.tmpdir(), 'rm02-shrink-baseline-manifest-'));
try {
await mkdir(path.join(fixture, 'scripts'), { recursive: true });
await mkdir(path.join(fixture, 'gates'), { recursive: true });
await copyFile(verifierPath, path.join(fixture, 'scripts', 'gate-verify.mjs'));
const baseline = JSON.parse(
await readFile(path.join(root, 'gates', 'required-gates.baseline.json'), 'utf8'),
);
baseline.gates = baseline.gates.filter((gate) => gate.id !== requiredGateId);
await writeFile(
path.join(fixture, 'gates', 'required-gates.baseline.json'),
`${JSON.stringify(baseline)}\n`,
);
const manifest = JSON.parse(await readFile(manifestPath, 'utf8'));
shrinkManifest(manifest, requiredGateId);
await writeFile(
path.join(fixture, 'gates', 'gates.manifest.json'),
`${JSON.stringify(manifest)}\n`,
);
const result = spawnSync(
process.execPath,
[
path.join(fixture, 'scripts', 'gate-verify.mjs'),
'--root',
fixture,
'--manifest',
'gates/gates.manifest.json',
'--structure-only',
],
{ cwd: fixture, encoding: 'utf8' },
);
assert.notEqual(result.status, 0, 'shrinking baseline and manifest together must go red');
assert.match(output(result), /verifier inventory.*hook-pre-push.*baseline/i);
} finally {
await rm(fixture, { recursive: true, force: true });
}
});
test('every gate carries an evidence-side subject distinct from its definition', async () => {
const manifest = JSON.parse(await readFile(manifestPath, 'utf8'));
for (const gate of manifest.gates) {
assert.ok(gate.cases.length > 0, `${gate.id} must have consumable evidence`);
for (const gateCase of gate.cases) {
assert.equal(
gateCase.evidence?.subject,
gate.id,
`${gate.id}/${gateCase.id} must source its subject from the evidence record`,
);
}
}
});
test('inventory claim control rejects qualified independence wording', async () => {
const fixture = await mkdtemp(path.join(os.tmpdir(), 'rm02-inventory-overclaim-'));
const artifacts = [
'gates/required-gates.baseline.json',
'gates/gates.manifest.json',
'docs/PRD.md',
'docs/ADMIN-GUIDE/quality-gate-registry.md',
'docs/DEVELOPER-GUIDE/quality-gate-registry.md',
'docs/remediation/GATE-CLAIMS.md',
'docs/plans/2026-08-01-rm-02-gate-registry.md',
'docs/SITEMAP.md',
];
try {
await mkdir(path.join(fixture, 'scripts'), { recursive: true });
await copyFile(
path.join(root, 'scripts', 'gate-inventory-claim-control.mjs'),
path.join(fixture, 'scripts', 'gate-inventory-claim-control.mjs'),
);
for (const relativePath of artifacts) {
const target = path.join(fixture, relativePath);
await mkdir(path.dirname(target), { recursive: true });
await copyFile(path.join(root, relativePath), target);
}
const prd = path.join(fixture, 'docs', 'PRD.md');
await writeFile(
prd,
`${await readFile(prd, 'utf8')}\nThis provides an independent seven-gate baseline comparison.\n`,
);
const result = spawnSync(
process.execPath,
[path.join(fixture, 'scripts', 'gate-inventory-claim-control.mjs')],
{ cwd: fixture, encoding: 'utf8' },
);
assert.equal(result.status, 84, output(result));
assert.match(output(result), /INVENTORY_CLAIM_OVERSTATED.*docs\/PRD\.md/i);
} finally {
await rm(fixture, { recursive: true, force: true });
}
});
test('evidence population control depends on production result consumption wiring', async () => {
const fixture = await mkdtemp(path.join(os.tmpdir(), 'rm02-evidence-consumer-inert-'));
try {
await mkdir(path.join(fixture, 'scripts'), { recursive: true });
await mkdir(path.join(fixture, 'gates'), { recursive: true });
const verifier = await readFile(verifierPath, 'utf8');
const consumer = ` const subjectFailure = consumeEvidenceSubject(gate, result.evidence);\n if (subjectFailure) failures.push(subjectFailure);\n`;
assert.equal(verifier.split(consumer).length - 1, 1, 'consumer fixture drifted');
await writeFile(
path.join(fixture, 'scripts', 'gate-verify.mjs'),
verifier.replace(consumer, ''),
);
await copyFile(
path.join(root, 'scripts', 'gate-population-control.mjs'),
path.join(fixture, 'scripts', 'gate-population-control.mjs'),
);
await copyFile(manifestPath, path.join(fixture, 'gates', 'gates.manifest.json'));
const result = spawnSync(
process.execPath,
[path.join(fixture, 'scripts', 'gate-population-control.mjs'), 'evidence-subject'],
{ cwd: fixture, encoding: 'utf8' },
);
assert.equal(result.status, 0, output(result));
assert.match(output(result), /did not reject every registered gate/i);
} finally {
await rm(fixture, { recursive: true, force: true });
}
});
test('production verification has a closed current-tree observation renderer', async () => {
const verifier = await readFile(verifierPath, 'utf8');
assert.doesNotMatch(verifier, /from ['"]\.\/gate-history\.mjs['"]/);
assert.doesNotMatch(verifier, /\bverifyHistory\s*\(/);
assert.doesNotMatch(verifier, /history[-_ ]?provenance/i);
const attacks = [
[
'history wording',
' /^META-NEGATIVE-CONTROL /,',
' /^HISTORY PROVENANCE VERIFIED /,\n /^META-NEGATIVE-CONTROL /,',
],
[
'renamed ancestry wording',
' /^META-NEGATIVE-CONTROL /,',
' /^COMMIT ANCESTRY VERIFIED /,\n /^META-NEGATIVE-CONTROL /,',
],
[
'provider lineage wording',
' /^META-NEGATIVE-CONTROL /,',
' /^PROVIDER LINEAGE SUCCESS /,\n /^META-NEGATIVE-CONTROL /,',
],
['writer assertion bypass', ' assertCurrentTreeObservation(output);\n', ''],
[
'final success stdout bypass',
' writeCurrentTreeOutput(\n `REGISTRY SUMMARY open behavior deltas: ${defects}; required-behavior conformance is not asserted while deltas remain`,\n );',
" process.stdout.write('COMMIT ANCESTRY VERIFIED\\n');",
],
[
'direct success stderr bypass',
" const defects = observations.filter((line) => line.startsWith('DEFECT ')).length;",
" process.stderr.write('PROVIDER LINEAGE SUCCESS\\n');\n const defects = observations.filter((line) => line.startsWith('DEFECT ')).length;",
],
];
for (const [name, find, replace] of attacks) {
const fixture = await mkdtemp(path.join(os.tmpdir(), 'rm02-history-renderer-'));
try {
await mkdir(path.join(fixture, 'scripts'), { recursive: true });
assert.equal(verifier.split(find).length - 1, 1, `${name}: fixture drifted`);
await writeFile(
path.join(fixture, 'scripts', 'gate-verify.mjs'),
verifier.replace(find, replace),
);
await copyFile(
path.join(root, 'scripts', 'gate-history-exclusion-control.mjs'),
path.join(fixture, 'scripts', 'gate-history-exclusion-control.mjs'),
);
const result = spawnSync(
process.execPath,
[path.join(fixture, 'scripts', 'gate-history-exclusion-control.mjs')],
{ cwd: fixture, encoding: 'utf8' },
);
assert.equal(result.status, 79, `${name}: ${output(result)}`);
assert.match(output(result), /HISTORY_PROVENANCE_FORBIDDEN/);
} finally {
await rm(fixture, { recursive: true, force: true });
}
}
});