fix(rm-02): narrow inventory drift guarantee
ci/woodpecker/pr/ci Pipeline was successful

This commit is contained in:
2026-08-01 15:09:57 -05:00
parent c5b0d510d7
commit e910a45ab3
13 changed files with 203 additions and 38 deletions
+1 -1
View File
@@ -28,7 +28,7 @@ Do not add an ownerless exception or describe an open delta as pass/green/OK.
Woodpecker runs `gate-verify` on every pull request and protected-main push without path filtering. This is deliberate: changes outside gate files can make a gate inert. The step needs no local history preparation because RM-02 asserts no history-provenance property.
**DOES:** PR CI executes current-tree verification unprivileged and fail-closed. It compares the manifest and verifier inventory separately with `gates/required-gates.baseline.json`, and consumed case evidence carries a subject checked against its gate definition. The registered shrink-both and per-gate evidence-subject controls must remain red for their stated reasons.
**DOES:** PR CI executes current-tree verification unprivileged and fail-closed. It compares the manifest and verifier inventory separately with `gates/required-gates.baseline.json`, and consumed case evidence carries a subject checked against its gate definition. **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.** The registered shrink-both, overclaim, and per-gate evidence-subject controls must remain red for their stated reasons.
**DOES NOT:** No local git state in the PR checkout is trustworthy as a history anchor because PR-controlled lifecycle code executes before the gate. The verifier has no history-verification path, and its closed current-tree observation renderer has no history/ancestry/lineage success class. Do not add a local ref, config, remote URL, source constant, or author-positioned path as a replacement anchor.
@@ -34,12 +34,10 @@ A gate with an external installed counterpart declares it explicitly. When the i
## Current-tree and history-provenance boundary
**DOES:** Every PR evaluates the current checkout's registered gates and declared inerting mutations directly, unprivileged and fail-closed. The seven-gate population is compared independently against `gates/required-gates.baseline.json`; a registered control shrinks the verifier inventory and manifest together and proves that the unchanged baseline rejects the attack. Evidence-side subjects are consumed and compared with gate definitions for every gate.
**DOES:** Every PR evaluates the current checkout's registered gates and declared inerting mutations directly, unprivileged and fail-closed. The seven-gate population, verifier inventory, and `gates/required-gates.baseline.json` are compared inside the checkout. Evidence-side subjects are consumed and compared with gate definitions for every gate. **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.**
**DOES NOT:** This repository layer establishes history provenance at all. `pnpm install` executes PR-controlled lifecycle code before `gate:verify`, so no local ref, git config, remote URL, constant, or author-positioned path in the checkout can anchor a history claim. An observation saying “unverifiable” while returning zero would be a green wearing a disclaimer, so the claim and history verifier path are removed.
The production output path uses a closed current-tree observation renderer; history, ancestry, and provider-lineage success are not representable observation classes. `scripts/gate-history-exclusion-control.mjs` exercises alternate success wording and production renderer wiring. Its registered must-fail case turns red if a prohibited success class is added or renderer consumption is bypassed. RM-60 owns the provider-controlled/protected pre-execution boundary needed before history provenance can be asserted. No bootstrap override exists in RM-02.
Universal checks first prove populations non-empty. The production profile reads the independent baseline before comparing the verifier inventory and manifest separately, while `gateRefs` on the D-38/D-40 criteria must exactly span every registered gate. Population controls mutate evidence-side subjects and type-strict comparison inputs one gate at a time and require rejection across the complete inventory.
The independent baseline blocks the registered source-plus-manifest shrink attack. It does not claim same-authority authenticity against an actor who consistently rewrites every repository artifact; RM-60/RM-59 own external execution/artifact integrity.
Universal checks first prove populations non-empty. The production profile reads the same-checkout baseline before comparing the verifier inventory and manifest separately, while `gateRefs` on the D-38/D-40 criteria must exactly span every registered gate. Population controls mutate evidence-side subjects and type-strict comparison inputs one gate at a time and require rejection across the complete inventory. A registered prose-claim control fails when the same-checkout mechanism is described as an adversarial protection.
+8 -8
View File
@@ -38,10 +38,10 @@ Existing deterministic gates can return success without enforcing their stated p
8. `RM02-REQ-08`: Every gate with a deployed counterpart SHALL register source/deployed byte identity and a must-fail drift control. Gates without a deployed counterpart SHALL say so explicitly.
9. `RM02-REQ-09`: CI SHALL run `pnpm gate:verify` on every pull request without path filtering and on protected-main pushes.
10. `RM02-REQ-10` (restated): PR CI SHALL perform unprivileged, fail-closed current-tree verification only. The production observation renderer SHALL be a closed current-tree-only output type with no representable history-provenance success state. A registered must-fail control SHALL turn red if history/ancestry/lineage success is added to that renderer or if production output bypasses the renderer. RM-60 owns the provider-controlled/protected pre-execution boundary required to establish history provenance.
11. `RM02-REQ-11` (`D-46`): The required seven-gate inventory SHALL be read from an independent baseline artifact and compared separately with both the verifier inventory and manifest. Shrinking the verifier inventory and manifest together while leaving the baseline intact SHALL fail for the removed gate.
11. `RM02-REQ-11` (`D-46`): The required seven-gate inventory SHALL be read from a same-checkout baseline and compared separately with both the verifier inventory and manifest. Shrinking the verifier inventory and manifest together while leaving the baseline intact SHALL fail for the removed gate. **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.**
12. `RM02-REQ-12` (`D-38`): Every consumed case result SHALL carry an evidence-side subject independently declared from the gate definition. The verifier SHALL compare that evidence subject with the gate definition when consuming the result. A population control SHALL mutate the evidence-side subject independently for every required gate and observe rejection for every gate.
13. `RM02-REQ-13` (`D-40`): For every gate in the independently baselined required inventory, each discriminator and comparison input SHALL have a recursively closed, type-strict schema. Unknown, misspelled, wrong-type, or present-but-empty nested assertion fields SHALL fail rather than disabling an assertion.
14. `RM02-REQ-14` (`D-46`): No universally quantified registry check SHALL run until its population is proven non-empty and independently baselined. The required seven-gate inventory, criteria, prose claims, and compatibility scenarios SHALL reject empty populations before reporting that all registered cases ran.
13. `RM02-REQ-13` (`D-40`): For every gate listed in the required inventory baseline, each discriminator and comparison input SHALL have a recursively closed, type-strict schema. Unknown, misspelled, wrong-type, or present-but-empty nested assertion fields SHALL fail rather than disabling an assertion.
14. `RM02-REQ-14` (`D-46`): No universally quantified registry check SHALL run until its population is proven non-empty and compared with the same-checkout baseline. The required seven-gate inventory, criteria, prose claims, and compatibility scenarios SHALL reject empty populations before reporting that all registered cases ran.
#### RM02-REQ-10 meaning-change provenance
@@ -57,15 +57,15 @@ Existing deterministic gates can return success without enforcing their stated p
4. `RM02-AC-04`: A gate with zero must-fail cases returns nonzero and includes `no negative control`.
5. `RM02-AC-05`: Unbound or semantically misbound criteria, prose claims bound to unrelated cases, unbound governing prose markers, ownerless behavior deltas, stale mutations, source/deployed drift, and modeled compatibility conflicts each return nonzero with the responsible stable ID. A simultaneous stale fixture or independent phase error SHALL NOT mask responsible stable-ID diagnostics. Registered meta-negative controls move a criterion binding, remove meaning provenance, and redirect a prose claim to an unrelated case; each is observed red for its stated reason.
6. `RM02-AC-06`: CI configuration invokes the verifier unconditionally on every pull request.
7. `RM02-AC-07`: PR output states adjacent `DOES`/`DOES NOT` boundaries: the repository layer verifies current-tree registered cases, independent inventory shape, and evidence-side subject consumption; it does not establish history provenance at all. RM-60 is the tracked owner of the provider-controlled/protected pre-execution boundary.
8. `RM02-AC-08`: Registered must-fail controls reject an emptied registry; shrinking the verifier inventory and manifest together; adding history/ancestry/lineage success to the closed observation renderer or bypassing renderer consumption; a misspelled nested outcome field; a wrong outcome field type; and a present-but-empty outcome pattern.
9. `RM02-AC-09`: Population controls iterate every gate in the independently baselined inventory and prove consumed evidence-subject mismatch and wrong-type comparison input are rejected for each gate. `RM02-EVIDENCE-SUBJECT-BINDING`, `RM02-TYPE-STRICT-SCHEMA`, `RM02-HISTORY-PROVENANCE-EXCLUDED`, and `RM02-NONEMPTY-ANCHORED-QUANTIFICATION` are bidirectionally bound to their must-fail controls.
7. `RM02-AC-07`: PR output states adjacent `DOES`/`DOES NOT` boundaries: the repository layer verifies current-tree registered cases, same-checkout inventory drift, and evidence-side subject consumption; it does not establish history provenance at all. **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.**
8. `RM02-AC-08`: Registered must-fail controls reject an emptied registry; shrinking the verifier inventory and manifest together; overstating the same-checkout inventory boundary; adding history/ancestry/lineage success to the closed observation renderer or bypassing renderer consumption; a misspelled nested outcome field; a wrong outcome field type; and a present-but-empty outcome pattern.
9. `RM02-AC-09`: Population controls iterate every gate listed in the baseline and prove consumed evidence-subject mismatch and wrong-type comparison input are rejected for each gate. `RM02-EVIDENCE-SUBJECT-BINDING`, `RM02-TYPE-STRICT-SCHEMA`, `RM02-HISTORY-PROVENANCE-EXCLUDED`, and `RM02-NONEMPTY-ANCHORED-QUANTIFICATION` are bidirectionally bound to their must-fail controls.
### Risks, dependencies, and verification boundary
- **DOES:** The repository verifier proves declared current-tree controls, modeled scenarios, bidirectional criterion/case relationships, source/deployed equality at execution time, an independent seven-gate baseline comparison, and evidence-side subject consumption.
- **DOES:** The repository verifier proves declared current-tree controls, modeled scenarios, bidirectional criterion/case relationships, source/deployed equality at execution time, a same-checkout seven-gate baseline comparison, and evidence-side subject consumption.
- **DOES NOT:** This layer establishes no history provenance. PR-controlled lifecycle code executes before the gate, so no local git state in the checkout is trustworthy as a history anchor. RM-60 owns the provider-controlled/protected pre-execution boundary. The production verifier has no history verifier path, and its closed current-tree observation renderer cannot represent a history-provenance success state.
- The independent inventory baseline prevents the registered shrink-both attack proved by RM-02's control; it does not claim same-authority authenticity against an actor who consistently rewrites every repository artifact. RM-60/RM-59 own external execution/artifact integrity.
- **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.** A registered claim control fails if the checked artifacts overstate this boundary.
- The verifier does **not** infer arbitrary-English semantics or defend against an actor able to rewrite the gate, registry, verifier, controls, and baseline consistently.
- `ASSUMPTION:` RM-54 is the owner for expanding registration and prose-marker coverage beyond this approved seven-gate slice; rationale: the remediation task graph already assigns the fleet-wide inert-gate audit there.
+1 -1
View File
@@ -2,7 +2,7 @@
## Gate verification
- [Developer gate registry guide](DEVELOPER-GUIDE/quality-gate-registry.md) — manifest schema, negative controls, evidence-side subjects, independent inventory baseline, and the enforced RM-60 history-provenance exclusion.
- [Developer gate registry guide](DEVELOPER-GUIDE/quality-gate-registry.md) — manifest schema, negative controls, evidence-side subjects, and the enforced RM-60 history-provenance exclusion. 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.
- [Gate registry operations](ADMIN-GUIDE/quality-gate-registry.md) — routine verification, failure interpretation, registry updates, and unconditional CI behavior.
- [RM-02 governing claim index](remediation/GATE-CLAIMS.md) — marker bindings for orchestrator-owned remediation claims without modifying task tracking.
+2 -2
View File
@@ -4,7 +4,7 @@
**Goal:** Build a machine-readable seven-gate registry and an unconditional CI verifier that detects inert gates, binds criteria to observed negative controls, records defects honestly, and verifies the current PR tree unprivileged and fail-closed.
**Architecture:** A dependency-free Node CLI reads `gates/gates.manifest.json` and the independent `gates/required-gates.baseline.json`, validates closed schemas and references, then runs typed cases in isolated main-disk fixtures. Gate-specific fixture setup remains declarative; exact invocations, evidence-side subjects, and exact observed/required exits stay in JSON. The production verifier is structurally incapable of asserting history provenance; RM-60 owns the provider-controlled/protected pre-execution boundary.
**Architecture:** A dependency-free Node CLI reads `gates/gates.manifest.json` and the same-checkout `gates/required-gates.baseline.json`, validates closed schemas and references, then runs typed cases in isolated main-disk fixtures. 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. Gate-specific fixture setup remains declarative; exact invocations, evidence-side subjects, and exact observed/required exits stay in JSON. The production verifier is structurally incapable of asserting history provenance; RM-60 owns the provider-controlled/protected pre-execution boundary.
**Tech Stack:** Node.js ESM, `node:test`, JSON, shell gates, pnpm, Woodpecker CI.
@@ -76,7 +76,7 @@ Enumerate current security/integrity claims, bind each marker/id to a negative c
- Modify: `scripts/gate-verify.mjs`
- Modify: `gates/gates.manifest.json`
Verify current-tree behavior only. Remove the anchor-dependent history verifier and provider-history consumption because PR-controlled lifecycle code executes before the gate and makes every local git anchor untrustworthy. Use a closed current-tree observation renderer and register a must-fail control that turns red if history/ancestry/lineage success becomes representable or production output bypasses renderer consumption. Compare the verifier inventory and manifest separately against the independent baseline, and register a must-fail attack that shrinks source inventory plus manifest together. State both directions: current-tree controls, inventory shape, and evidence subjects are enforced here; history provenance is not established until RM-60 provides a provider-controlled/protected pre-execution boundary.
Verify current-tree behavior only. Remove the anchor-dependent history verifier and provider-history consumption because PR-controlled lifecycle code executes before the gate and makes every local git anchor untrustworthy. Use a closed current-tree observation renderer and register a must-fail control that turns red if history/ancestry/lineage success becomes representable or production output bypasses renderer consumption. Compare the verifier inventory and manifest separately against the same-checkout baseline, register must-fail attacks that shrink either paired representation, and register an overclaim control. 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. State both directions: current-tree controls, inventory shape, and evidence subjects are enforced here; history provenance is not established until RM-60 provides a provider-controlled/protected pre-execution boundary.
### Task 7: CI and documentation
+8
View File
@@ -24,6 +24,14 @@ This index binds remediation claims that live in orchestrator-owned `TASKS.md` w
- Anchored text: “SELF-VERIFICATION BY THE AUDITED PARTY IS NOT VERIFICATION.”
- Dependency: RM-60/#1031, cross-referenced with RM-59.
## Same-checkout inventory drift boundary
<!-- GATE-CLAIM:INVENTORY-DRIFT-BOUNDARY -->
- Source: RM-02 ruling after D-48/CWE-353 reproduced against the round-4 baseline.
- 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.
- Negative control: `checkout-preflight/inventory-claim-overstatement` rewrites the boundary as protection and must go red.
## Criterion restatement provenance
<!-- GATE-CLAIM:CRITERION-RESTATEMENT -->
@@ -102,3 +102,6 @@ The queue guard's `get_state_from_status_json` runs `python3 - <<'PY'` while pro
- Independent Codex code review requested changes on two valid blockers. First, the evidence population control called the subject helper directly rather than traversing production result consumption. It now creates one lightweight executed fixture per required gate, invokes the real `verifyRegistry` path, and fails to observe rejection if the production consumer is removed; a regression mutation proves that coupling. Second, a lexical history blacklist overclaimed structural incapacity. Production output now passes through a closed current-tree observation renderer with no history/ancestry/lineage success class; the exclusion control tests three alternate success wordings plus exact production renderer wiring, and the registered must-fail fixture adds a prohibited class.
- Codex review test attempts were unrunnable in its read-only sandbox (`EROFS`/`EPERM`); the reviewer disclosed this rather than substituting a passing variant. Local writable-worktree tests remain the runnable evidence.
- Renderer remediation: every non-error production observation and final summary now routes through the closed current-tree output renderer. The exclusion control additionally requires one stdout sink, exactly two `GATE VERIFY FAILED` stderr sinks, no console sinks, and renderer use for both per-observation and final-summary paths. Regression attacks cover allowlisted history/ancestry/lineage wording, writer assertion removal, direct final-success stdout, and direct success stderr; focused suite 41/41, `pnpm gate:verify` green with six declared RM-03 deltas, and format check green.
- Coordinator ruling after CWE-353: retain the same-checkout inventory comparison but narrow its claim. Canonical current 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.” This supersedes the earlier “independent/anchored” wording in the historical entries above; those entries remain as append-only evidence, not current claims.
- Genuine RED-first overclaim control: before claim remediation, `node scripts/gate-inventory-claim-control.mjs` exited 84 and named every artifact missing the narrowed boundary plus each current overclaim. The registered `inventory-claim-overstatement` case now rewrites the baseline purpose to a protection claim and must exit 84 with `INVENTORY_CLAIM_OVERSTATED`. Existing shrink-pair controls remain regression guards for the same-checkout drift property, not adversarial-integrity claims.
- Follow-up code review found a genuine propagation/control gap: `docs/PRD.md` still said “independent seven-gate baseline,” and the first forbidden regex did not match the intervening qualifier. The reviewer observed the control green against that overclaim. The PRD now says same-checkout, the detector rejects `independent … baseline` across bounded same-line qualifiers, the registered mutation uses the exact qualified wording, and a focused behavioral test requires exit 84 for it. Follow-up security review passed with risk `none` and explicitly accepted the narrowed RM-60 boundary.
+58 -4
View File
@@ -44,7 +44,8 @@
"hook-pre-commit/lint-staged-failure",
"hook-pre-push/typecheck-failure",
"checkout-preflight/history-provenance-exclusion",
"checkout-preflight/inventory-source-and-manifest-shrink"
"checkout-preflight/inventory-source-and-manifest-shrink",
"checkout-preflight/inventory-claim-overstatement"
]
},
{
@@ -303,13 +304,23 @@
{
"id": "RM02-NONEMPTY-ANCHORED-QUANTIFICATION",
"originalText": "No universally quantified registry check runs until its population is proven non-empty and anchored.",
"currentText": "No universally quantified registry check runs until its population is proven non-empty and anchored.",
"currentText": "No universally quantified registry check runs over an empty population. 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.",
"claimType": "integrity",
"source": "docs/remediation/TASKS.md#d-46",
"meaningChanges": [],
"meaningChanges": [
{
"originalText": "No universally quantified registry check runs until its population is proven non-empty and anchored.",
"restatement": "No universally quantified registry check runs over an empty population. 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.",
"reason": "D-48/CWE-353 established that a same-checkout baseline detects drift but is not an adversarial trust anchor.",
"finding": "D-48",
"task": "RM-60",
"date": "2026-08-01"
}
],
"caseRefs": [
"checkout-preflight/empty-registry-populations",
"checkout-preflight/inventory-source-and-manifest-shrink"
"checkout-preflight/inventory-source-and-manifest-shrink",
"checkout-preflight/inventory-claim-overstatement"
],
"gateRefs": [
"quality-typecheck",
@@ -362,6 +373,11 @@
"id": "CRITERION-RESTATEMENT",
"criterionId": "RM02-MEANING-PROVENANCE",
"caseRef": "checkout-preflight/missing-meaning-provenance"
},
{
"id": "INVENTORY-DRIFT-BOUNDARY",
"criterionId": "RM02-NONEMPTY-ANCHORED-QUANTIFICATION",
"caseRef": "checkout-preflight/inventory-claim-overstatement"
}
],
"compatibilityScenarios": [
@@ -1004,6 +1020,44 @@
"evidence": {
"subject": "checkout-preflight"
}
},
{
"id": "inventory-claim-overstatement",
"criterionIds": ["RM02-CHECK-RIGHT", "RM02-NONEMPTY-ANCHORED-QUANTIFICATION"],
"mustFail": true,
"invocation": ["node", "scripts/gate-inventory-claim-control.mjs"],
"required": {
"exitCode": 84,
"outputPattern": "INVENTORY_CLAIM_OVERSTATED"
},
"actual": {
"exitCode": 84,
"outputPattern": "INVENTORY_CLAIM_OVERSTATED"
},
"reasonPattern": "INVENTORY_CLAIM_OVERSTATED",
"fixture": {
"copyPaths": [
"scripts/gate-inventory-claim-control.mjs",
"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"
],
"replaceFiles": [
{
"path": "gates/required-gates.baseline.json",
"find": "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.",
"replace": "Provides an independent seven-gate baseline comparison."
}
]
},
"evidence": {
"subject": "checkout-preflight"
}
}
]
},
+25 -7
View File
@@ -1,16 +1,34 @@
{
"schemaVersion": 1,
"purpose": "Independent required-gate population baseline; manifest and verifier inventory must both match.",
"purpose": "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.",
"gates": [
{ "id": "quality-typecheck", "source": "package.json" },
{ "id": "quality-lint", "source": "package.json" },
{ "id": "quality-format", "source": "package.json" },
{ "id": "checkout-preflight", "source": "scripts/preflight.mjs" },
{
"id": "quality-typecheck",
"source": "package.json"
},
{
"id": "quality-lint",
"source": "package.json"
},
{
"id": "quality-format",
"source": "package.json"
},
{
"id": "checkout-preflight",
"source": "scripts/preflight.mjs"
},
{
"id": "ci-queue-wait",
"source": "packages/mosaic/framework/tools/git/ci-queue-wait.sh"
},
{ "id": "hook-pre-commit", "source": ".husky/pre-commit" },
{ "id": "hook-pre-push", "source": ".husky/pre-push" }
{
"id": "hook-pre-commit",
"source": ".husky/pre-commit"
},
{
"id": "hook-pre-push",
"source": ".husky/pre-push"
}
]
}
+44
View File
@@ -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');
+43 -3
View File
@@ -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 {
+7 -7
View File
@@ -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}`);
}
}
+1 -1
View File
@@ -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,
);
});