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:
@@ -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;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user