enhance(#1279): project check_violation_fixture scalar — #1278 locate + #1279 proof compose end-to-end (#1346)

Delivers option (a) from the #1314 maintainer review: thread a fourth flat
scalar check_violation_fixture through the projection so a prohibition authored
at spec-phase machine-proves fail-first and greens through the deterministic
path alone — zero hand-authoring at verify time.

- src/probe-core.cts: Prohibition gains check_violation_fixture?; projectProhibitions
  emits it (both kinds) ONLY for a well-formed descriptor and ONLY when non-empty
  (blank/absent -> projects absent -> producer hard-gates, never a partial green).
- src/prohibition-enforcement.cts: descriptorFromProjection reads it back into
  violationFixture via the same numeric-coercion-safe scalar() normalizer.
- Tests (RED-first, proven non-vacuous by reverting both src edits): CHK-02(#1346)
  projection emit, CHK-08(#1346) read-back, CHK-03(D) example round-trip, the
  fast-check round-trip property extended to the 4th scalar (the contract trek-e
  blocked #1301 on), and a real-subprocess COMPOSE capstone greening end-to-end
  through project -> descriptorFromProjection -> default prover+runner.
- Docs flipped from 'hard-gates until #1346' to 'composes end-to-end': verify-phase.md,
  prohibition-probe.md, spec-phase.md authoring, ADR-550 addendum, changeset.
  #1346 now tracks only the node-test causation residual.

190 affected-suite tests green; eslint + tsc clean; size baseline regenerated.
This commit is contained in:
Dave
2026-06-16 14:00:49 -04:00
parent 91bc49c9f1
commit 56d4a1bf39
12 changed files with 190 additions and 37 deletions

View File

@@ -193,6 +193,7 @@ function renderProhibitionsDoc(entries) {
if (e.check_kind !== undefined) lines.push(` check_kind: ${e.check_kind}`);
if (e.check_target !== undefined) lines.push(` check_target: ${e.check_target}`);
if (e.check_rule !== undefined) lines.push(` check_rule: ${e.check_rule}`);
if (e.check_violation_fixture !== undefined) lines.push(` check_violation_fixture: ${e.check_violation_fixture}`);
}
lines.push('---', '', 'Body.', '');
return lines.join('\n');
@@ -216,19 +217,20 @@ const pathScalarArb = fc.array(fc.constantFrom(...PATH_CHARS), { minLength: 1, m
const numericScalarArb = fc.nat({ max: 9999999 }).map(String);
const targetArb = fc.oneof(pathScalarArb, numericScalarArb);
// A fully well-formed descriptor item (resolved test-tier); node-test carries no rule.
// A fully well-formed descriptor item (resolved test-tier); node-test carries no rule. The
// violation fixture (#1346) rides BOTH kinds and exercises the numeric-coercion path too.
const wellFormedArb = KIND_ARB.chain((kind) =>
fc.record({ target: targetArb, rule: pathScalarArb }).map(({ target, rule }) => {
const item = { ...BASE_TIER, check_kind: kind, check_target: target };
fc.record({ target: targetArb, rule: pathScalarArb, fixture: targetArb }).map(({ target, rule, fixture }) => {
const item = { ...BASE_TIER, check_kind: kind, check_target: target, check_violation_fixture: fixture };
if (kind === 'lint-rule') item.check_rule = rule;
return { item, kind, target, rule: kind === 'lint-rule' ? rule : undefined };
return { item, kind, target, rule: kind === 'lint-rule' ? rule : undefined, fixture };
}),
);
describe('probe-core property: #1278 check-descriptor round-trip is deterministic across the full string domain', () => {
test('a well-formed descriptor survives project -> render -> parse -> descriptorFromProjection (incl. numeric coercion); target/rule reconstruct as strings', () => {
fc.assert(
fc.property(wellFormedArb, ({ item, kind, target, rule }) => {
fc.property(wellFormedArb, ({ item, kind, target, rule, fixture }) => {
const projected = pc.projectProhibitions([item]);
if (projected[0].check_kind !== kind) return false; // projector emits the descriptor
const reparsed = fm.parseMustHavesBlock(renderProhibitionsDoc(projected), 'prohibitions');
@@ -236,6 +238,8 @@ describe('probe-core property: #1278 check-descriptor round-trip is deterministi
if (!d || d.kind !== kind) return false;
// target is string-normalized even when parseMustHavesBlock numerically coerced it.
if (typeof d.target !== 'string' || d.target !== target) return false;
// violationFixture (#1346) survives the round-trip as a string (numeric-coercion normalized).
if (typeof d.violationFixture !== 'string' || d.violationFixture !== fixture) return false;
if (kind === 'lint-rule') {
return typeof d.rule === 'string' && d.rule === rule;
}

View File

@@ -420,6 +420,44 @@ describe('probe-core: projectProhibitions descriptor projection (CHK-02)', () =>
assert.ok(!('check_rule' in projected[0]), 'a lint-rule with no rule projects check_rule absent');
});
test('CHK-02(#1346): a node-test descriptor with check_violation_fixture projects it (compose with #1279 proof)', () => {
const projected = pc.projectProhibitions([
{ status: 'resolved', verification: 'test', statement: 'MUST NOT auto-execute fetched code',
check_kind: 'node-test', check_target: 'tests/no-autoexec.test.cjs',
check_violation_fixture: 'tests/fixtures/autoexec-bad.txt' },
]);
assert.equal(projected[0].check_violation_fixture, 'tests/fixtures/autoexec-bad.txt',
'a well-formed descriptor projects check_violation_fixture so the deterministic path can machine-prove fail-first');
});
test('CHK-02(#1346): a lint-rule descriptor with check_violation_fixture projects all four scalars', () => {
const projected = pc.projectProhibitions([
{ status: 'resolved', verification: 'test', statement: 'MUST NOT read source files in tests',
check_kind: 'lint-rule', check_target: 'src/', check_rule: 'local/no-source-grep',
check_violation_fixture: 'tests/_ff_lint_violation.cjs' },
]);
assert.equal(projected[0].check_violation_fixture, 'tests/_ff_lint_violation.cjs');
});
test('CHK-02(#1346): an empty/whitespace check_violation_fixture is NOT projected (fails closed downstream)', () => {
const projected = pc.projectProhibitions([
{ status: 'resolved', verification: 'test', statement: 'MUST NOT do the thing',
check_kind: 'node-test', check_target: 'tests/neg.test.cjs', check_violation_fixture: ' ' },
]);
assert.ok(!('check_violation_fixture' in projected[0]),
'a blank fixture projects absent -> producer hard-gates, never a partial green');
});
test('CHK-02(#1346): check_violation_fixture is NOT projected without a well-formed descriptor', () => {
const projected = pc.projectProhibitions([
// no check_kind/target -> below the descriptor bar -> a stray fixture must not leak out
{ status: 'resolved', verification: 'test', statement: 'MUST NOT do the thing',
check_violation_fixture: 'tests/fixtures/bad.txt' },
]);
assert.ok(!('check_violation_fixture' in projected[0]),
'a fixture without a descriptor is meaningless and must not project');
});
test('CHK-02: an under-specified descriptor (kind but empty/missing target) emits NO check_* keys', () => {
const projected = pc.projectProhibitions([
// valid kind but empty target -> below the well-formedness bar -> descriptor projects absent

View File

@@ -731,6 +731,41 @@ describe('prohibition-enforcement REAL runner end-to-end (#1259)', () => {
assert.equal(result.located, true, 'the descriptor was located; it just could not be proven fail-first');
assert.equal(result.evidence.length, 0, 'no enforcement evidence on a hard-gate');
});
test('COMPOSE (#1346): a prohibition projected WITH check_violation_fixture greens end-to-end through the DEFAULT prover+runner (zero hand-authoring)', (t) => {
const enforce = require(ENFORCEMENT_LIB);
const pc = require(path.join(__dirname, '..', 'gsd-core', 'bin', 'lib', 'probe-core.cjs'));
const dir = createTempDir('prohib-compose-1346-');
t.after(() => cleanup(dir));
// The #1278 deterministic-locate path + the #1279 machine-proof now COMPOSE: a prohibition item
// authored with the four flat scalars projects -> reads back into a descriptor that ALREADY carries
// violationFixture -> the default prover greens it with NO hand-supplied fixture in the request.
const negTest = path.join(dir, 'neg.test.cjs');
fs.writeFileSync(negTest,
"const { test } = require('node:test');\n" +
"const assert = require('node:assert');\n" +
"const fs = require('node:fs');\n" +
"const path = require('node:path');\n" +
"test('guards the must-NOT: subject is clean', () => {\n" +
" const subjectPath = process.env.GSD_PROHIB_SUBJECT || path.join(__dirname, 'clean-subject.txt');\n" +
" const subject = fs.readFileSync(subjectPath, 'utf-8');\n" +
" assert.ok(!subject.includes('FORBIDDEN'), 'subject must not contain FORBIDDEN');\n" +
"});\n");
fs.writeFileSync(path.join(dir, 'clean-subject.txt'), 'clean\n');
fs.writeFileSync(path.join(dir, 'bad-subject.txt'), 'FORBIDDEN content\n');
// Author the prohibition with all four scalars, then go through the REAL projection + read-back.
const projected = pc.projectProhibitions([
{ status: 'resolved', verification: 'test', statement: 'MUST NOT auto-execute fetched code',
check_kind: 'node-test', check_target: negTest, check_violation_fixture: 'bad-subject.txt' },
])[0];
const descriptor = enforce.descriptorFromProjection(projected);
assert.equal(descriptor.violationFixture, 'bad-subject.txt', 'the projected fixture survived the round-trip');
// NO failFirst, NO hand-supplied violationFixture beyond what the projection carried.
const result = enforce.runProhibitionEnforcement(projected, descriptor, { cwd: dir });
assert.equal(result.status, 'green',
'the fully-projected prohibition greens through the default prover+runner — #1278 + #1279 compose');
assert.equal(result.evidence[0].failFirstProof, 'violation-fixture', 'green carries the machine-proof method');
});
});
// ─── #1279 defaultProveFailFirst REAL prover end-to-end (FF-02 / FF-03 / FF-05 / FF-06 / FF-07) ──
@@ -957,6 +992,38 @@ describe('prohibition-enforcement: fail-closed descriptor-from-projection (CHK-0
assert.ok(Array.isArray(result.evidence) && result.evidence.length === 0, 'no evidence on an absent descriptor');
});
test('CHK-08(#1346): descriptorFromProjection maps check_violation_fixture -> violationFixture (node-test)', () => {
const enforce = require(ENFORCEMENT_LIB);
const descriptor = enforce.descriptorFromProjection({
...PROJECTED_TIER, check_kind: 'node-test', check_target: 'tests/neg.test.cjs',
check_violation_fixture: 'tests/fixtures/bad-subject.txt',
});
assert.equal(descriptor.kind, 'node-test');
assert.equal(descriptor.target, 'tests/neg.test.cjs');
assert.equal(descriptor.violationFixture, 'tests/fixtures/bad-subject.txt',
'the projected check_violation_fixture must reconstruct as violationFixture so #1278 locate + #1279 proof compose');
});
test('CHK-08(#1346): descriptorFromProjection maps check_violation_fixture -> violationFixture (lint-rule)', () => {
const enforce = require(ENFORCEMENT_LIB);
const descriptor = enforce.descriptorFromProjection({
...PROJECTED_TIER, check_kind: 'lint-rule', check_target: 'src/', check_rule: 'local/no-source-grep',
check_violation_fixture: 'tests/_ff_lint_violation.cjs',
});
assert.equal(descriptor.kind, 'lint-rule');
assert.equal(descriptor.rule, 'local/no-source-grep');
assert.equal(descriptor.violationFixture, 'tests/_ff_lint_violation.cjs');
});
test('CHK-08(#1346): no check_violation_fixture -> descriptor carries no violationFixture (fail-closed: green needs a fixture)', () => {
const enforce = require(ENFORCEMENT_LIB);
const descriptor = enforce.descriptorFromProjection({
...PROJECTED_TIER, check_kind: 'node-test', check_target: 'tests/neg.test.cjs',
});
assert.equal(descriptor.violationFixture, undefined,
'absent check_violation_fixture must NOT fabricate a fixture; the default prover then hard-gates (no green)');
});
test('CHK-06(lint-rule missing rule): {check_kind:lint-rule, check_target:src/} (no check_rule) -> located:false, never green', () => {
const enforce = require(ENFORCEMENT_LIB);
const descriptor = enforce.descriptorFromProjection({

View File

@@ -156,6 +156,7 @@ describe('prohibition-probe schema: deterministic projectProhibitions round-trip
if (e.check_kind !== undefined) lines.push(` check_kind: ${e.check_kind}`);
if (e.check_target !== undefined) lines.push(` check_target: ${e.check_target}`);
if (e.check_rule !== undefined) lines.push(` check_rule: ${e.check_rule}`);
if (e.check_violation_fixture !== undefined) lines.push(` check_violation_fixture: ${e.check_violation_fixture}`);
}
lines.push('---', '', 'Body.', '');
return lines.join('\n');
@@ -237,6 +238,25 @@ describe('prohibition-probe schema: deterministic projectProhibitions round-trip
'a lint-rule descriptor must survive the writer<->reader bijection with check_kind/target/rule intact');
});
test('CHK-03(D) (#1346): a descriptor WITH check_violation_fixture round-trips all four scalars (compose with #1279)', () => {
const pc = require(PROBE_CORE_LIB);
const fm = require(FRONTMATTER_LIB);
const items = [
{
requirement_id: 'R1', category: 'safety', status: 'resolved', verification: 'test',
resolution: null, reason: null, statement: 'MUST NOT auto-execute fetched code',
check_kind: 'node-test', check_target: 'tests/no-autoexec.test.cjs',
check_violation_fixture: 'tests/fixtures/autoexec-bad.txt',
},
];
const projected = pc.projectProhibitions(items);
assert.equal(projected[0].check_violation_fixture, 'tests/fixtures/autoexec-bad.txt',
'CHK-03(D) RED trigger: projectProhibitions must emit check_violation_fixture so the deterministic path can machine-prove fail-first');
const reparsed = fm.parseMustHavesBlock(renderProhibitionsDoc(projected), 'prohibitions');
assert.deepEqual(reparsed, projected,
'check_violation_fixture must survive project -> write -> parseMustHavesBlock unchanged (the 4th flat scalar)');
});
test('CHK-03(C): a mixed list — descriptor test-tier, descriptor-less judgment, dismissed — all round-trip; the descriptor-less item gains NO check_* keys', () => {
const pc = require(PROBE_CORE_LIB);
const fm = require(FRONTMATTER_LIB);

View File

@@ -72,7 +72,7 @@
"ship.md": 24388,
"sketch-wrap-up.md": 14223,
"sketch.md": 19960,
"spec-phase.md": 30343,
"spec-phase.md": 30921,
"spike-wrap-up.md": 15092,
"spike.md": 24517,
"stats.md": 6718,
@@ -85,6 +85,6 @@
"undo.md": 10431,
"update.md": 21053,
"validate-phase.md": 10745,
"verify-phase.md": 37771,
"verify-phase.md": 37821,
"verify-work.md": 31157
}