This commit is contained in:
@@ -0,0 +1,44 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
import { readFile } from 'node:fs/promises';
|
||||
import path from 'node:path';
|
||||
|
||||
const boundary =
|
||||
"Detects accidental and incompetent inventory drift within a checkout; does NOT survive an adversary who rewrites baseline, manifest, and verifier consistently — that guarantee requires RM-60's external boundary.";
|
||||
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',
|
||||
];
|
||||
const forbidden = [
|
||||
/\bindependent\b[^\n.]{0,80}\bbaseline\b/i,
|
||||
/independently baselined/i,
|
||||
/independently anchored inventory/i,
|
||||
/protected (?:inventory )?(?:anchor|baseline)/i,
|
||||
/inventory (?:anchor|anchored)/i,
|
||||
];
|
||||
const failures = [];
|
||||
for (const relativePath of artifacts) {
|
||||
const contents = await readFile(path.join(process.cwd(), relativePath), 'utf8');
|
||||
const claims =
|
||||
relativePath === 'gates/gates.manifest.json'
|
||||
? JSON.parse(contents)
|
||||
.criteria.map((criterion) => criterion.currentText)
|
||||
.join('\n')
|
||||
: contents;
|
||||
if (!claims.includes(boundary)) failures.push(`${relativePath}: narrowed boundary is missing`);
|
||||
const overclaim = forbidden.find((pattern) => pattern.test(claims));
|
||||
if (overclaim) failures.push(`${relativePath}: inventory protection is overstated`);
|
||||
}
|
||||
if (failures.length > 0) {
|
||||
for (const failure of failures) {
|
||||
process.stderr.write(`INVENTORY_CLAIM_OVERSTATED: ${failure}\n`);
|
||||
}
|
||||
process.exit(84);
|
||||
}
|
||||
process.stdout.write('inventory drift boundary is stated in both directions; owner RM-60\n');
|
||||
@@ -58,7 +58,7 @@ function shrinkManifest(manifest, removedGateId) {
|
||||
}
|
||||
}
|
||||
|
||||
test('shrinking the verifier inventory and manifest together is rejected by an independent baseline', async () => {
|
||||
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 });
|
||||
@@ -94,13 +94,13 @@ test('shrinking the verifier inventory and manifest together is rejected by an i
|
||||
{ cwd: fixture, encoding: 'utf8' },
|
||||
);
|
||||
assert.notEqual(result.status, 0, 'shrinking source anchor and manifest together must go red');
|
||||
assert.match(output(result), /independent required-gate baseline.*hook-pre-push/i);
|
||||
assert.match(output(result), /same-checkout required-gate baseline.*hook-pre-push/i);
|
||||
} finally {
|
||||
await rm(fixture, { recursive: true, force: true });
|
||||
}
|
||||
});
|
||||
|
||||
test('shrinking the independent baseline and manifest together is rejected by verifier inventory', async () => {
|
||||
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 });
|
||||
@@ -154,6 +154,46 @@ test('every gate carries an evidence-side subject distinct from its definition',
|
||||
}
|
||||
});
|
||||
|
||||
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 {
|
||||
|
||||
@@ -591,20 +591,20 @@ function validateClosedSchema(
|
||||
rejectDuplicateIds(manifest.gates, 'gate', failures);
|
||||
if (!fixtureProfile) {
|
||||
if (!(requiredGateInventory instanceof Map) || requiredGateInventory.size === 0) {
|
||||
failures.push('independent required-gate baseline is absent or empty');
|
||||
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(
|
||||
`independent required-gate baseline rejects verifier inventory drift at ${requiredId}`,
|
||||
`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 independent required-gate baseline`,
|
||||
`verifier inventory ${requiredId} is absent or changed in same-checkout required-gate baseline`,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -613,7 +613,7 @@ function validateClosedSchema(
|
||||
const registered = (manifest.gates ?? []).find((gate) => gate?.id === requiredId);
|
||||
if (!registered || registered.source !== requiredSource) {
|
||||
failures.push(
|
||||
`independent required-gate baseline rejects manifest drift at ${requiredId}: required source ${requiredSource}`,
|
||||
`same-checkout required-gate baseline rejects manifest drift at ${requiredId}: required source ${requiredSource}`,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1128,15 +1128,15 @@ export async function verifyRegistry(options) {
|
||||
gate.source.length === 0,
|
||||
)
|
||||
) {
|
||||
failures.push('independent required-gate baseline has unsupported structure');
|
||||
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('independent required-gate baseline has duplicate gate ids');
|
||||
failures.push('same-checkout required-gate baseline has duplicate gate ids');
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
failures.push(`independent required-gate baseline cannot be read: ${error.message}`);
|
||||
failures.push(`same-checkout required-gate baseline cannot be read: ${error.message}`);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -161,7 +161,7 @@ test('anchored gate inventory and population criteria cannot shrink together', a
|
||||
assert.notEqual(result.status, 0);
|
||||
assert.match(
|
||||
output(result),
|
||||
/independent required-gate baseline rejects manifest drift at hook-pre-push/i,
|
||||
/same-checkout required-gate baseline rejects manifest drift at hook-pre-push/i,
|
||||
);
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user