Files
msd-core/tests/prohibition-probe.planner-contract.test.cjs
sim 7dd9e59f6b test(#3090): stop exempting violations under categories that do not fit
An allow-test-rule annotation citing a category that does not apply is worse
than no annotation, because it reads as reviewed. Eight were confirmed by
reading the assertions each one covered, and auditing the rest found five more
plus one refutation — a converter test whose wording described the wrong
mechanism while the covered assertion genuinely was deployed-text.

The instructive one used the CANONICAL string for the same mistake: STATE.md
command output labelled as a deployed artifact. A canonical string is not
evidence the category fits, which is why normalising strings alone would have
laundered the problem rather than fixed it. Every mapping the audit had inferred
rather than code-verified was spot-checked before rewriting, and the ones that
turned out not to fit were re-annotated rather than relabelled.

Fourteen STATE.md assertions had a typed extractor available all along and now
use it; their annotations came out because nothing needs exempting. Eight
assertions genuinely need a production change first — CLI stdout and stderr with
no structured mode — and are tagged pending-migration-to-typed-ir citing #3090,
which is what that category is for. It had zero real uses before this, while one
file carried a real citation to migration issue #2974 under a non-canonical tag.

Six annotations covered assertions that do no text matching at all. An exemption
for a violation that does not exist is noise that makes the real ones harder to
audit; those are removed.

atomic-write-coverage gains the annotation it always warranted — its own
docstring describes a structural-regression-guard while the file carried none.

Fifty-nine non-canonical strings across roughly thirty files are normalised, and
the allow-test-rule allowlist is regenerated to match. 472 annotations became
463: every one now uses a canonical category, and the two remaining
non-canonical strings are ESLint RuleTester fixtures, not annotations.

Refs #3057

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-05 17:20:56 -04:00

97 lines
4.4 KiB
JavaScript

// allow-test-rule: source-text-is-the-product (see #644) — plan-phase.md's planner prompt is the deployed
// runtime contract under assertion (the workflow PROSE is the product).
//
// RED-first PROSE-PRESENCE contract for the plan-phase lift of confirmed prohibitions. plan-phase.md
// spawns the planner from its own inline <downstream_consumer> block; the load-bearing lift instruction
// must live THERE (not in templates/planner-subagent-prompt.md, which nothing loads at runtime — the
// edge-probe orphan-prompt regression, edge planner test 145-153). Assertions scope to extracted
// sub-blocks to avoid false positives.
//
// ADR-550 Decision 2: resolved prohibitions lift into must_haves.PROHIBITIONS — NOT must_haves.truths.
// EXPECTED RED until Wave 3 adds the plan-phase lift + quality_gate item.
'use strict';
process.env.GSD_TEST_MODE = '1';
const { test } = require('node:test');
const assert = require('node:assert/strict');
const fs = require('node:fs');
const path = require('node:path');
const PLAN_PHASE_PATH = path.join(__dirname, '..', 'gsd-core', 'workflows', 'plan-phase.md');
const PROMPT_PATH = path.join(__dirname, '..', 'gsd-core', 'templates', 'planner-subagent-prompt.md');
function readPlanPhase() {
return fs.readFileSync(PLAN_PHASE_PATH, 'utf8');
}
// Extract the planner <downstream_consumer> block from plan-phase.md (the runtime planner surface).
function extractDownstreamConsumerBlock(content) {
const start = content.indexOf('<downstream_consumer>');
if (start === -1) return '';
const end = content.indexOf('</downstream_consumer>', start);
if (end === -1) return '';
return content.slice(start, end + '</downstream_consumer>'.length);
}
// Extract the planner <quality_gate> block (the last one in plan-phase.md, inside the planner prompt).
function extractQualityGateBlock(content) {
const start = content.lastIndexOf('<quality_gate>');
if (start === -1) return '';
const end = content.indexOf('</quality_gate>', start);
if (end === -1) return '';
return content.slice(start, end + '</quality_gate>'.length);
}
// PROB-04 consumer: the lift instruction lives in the RUNTIME planner surface and lifts resolved
// prohibitions into must_haves.prohibitions (ADR-550 D2 — NOT truths).
test('PROB-04 consumer: downstream_consumer lifts SPEC Prohibitions into must_haves.prohibitions', () => {
const block = extractDownstreamConsumerBlock(readPlanPhase());
assert.ok(block.length > 0, 'sanity: plan-phase.md must contain a <downstream_consumer> block to scope this test');
assert.match(
block,
/Prohibitions/,
'plan-phase.md <downstream_consumer> must reference the SPEC Prohibitions section'
);
assert.match(
block,
/must_haves\.prohibitions/,
'plan-phase.md <downstream_consumer> must lift resolved prohibitions into must_haves.prohibitions (ADR-550 D2 — NOT truths)'
);
// ADR-550 D2 GUARD: prohibitions must NOT be lifted into truths.
assert.doesNotMatch(
block,
/prohibition[^.\n]*must_haves\.truths|must_haves\.truths[^.\n]*prohibition/i,
'prohibitions must NOT be lifted into must_haves.truths — truths keeps its positive-observable semantics (ADR-550 D2)'
);
});
// Orphan-prompt regression guard (mirror edge planner test 145-153): the lift instruction must NOT
// be relocated into templates/planner-subagent-prompt.md (which nothing loads at runtime).
test('PROB-04 regression: the prohibitions lift does not live in the orphaned planner-subagent-prompt.md', () => {
const prompt = fs.readFileSync(PROMPT_PATH, 'utf8');
assert.doesNotMatch(
prompt,
/must_haves\.prohibitions/,
'planner-subagent-prompt.md is not loaded at runtime — the prohibitions lift instruction must not live there'
);
});
// PROB-04: quality_gate covers every resolved SPEC Prohibition being represented in must_haves.prohibitions.
test('PROB-04: planner quality_gate requires every resolved SPEC Prohibition represented in must_haves.prohibitions', () => {
const qgBlock = extractQualityGateBlock(readPlanPhase());
assert.ok(qgBlock.length > 0, 'sanity: plan-phase.md must contain a <quality_gate> block to scope this test');
assert.match(
qgBlock,
/prohibition/i,
'planner quality_gate must contain a checklist item covering SPEC prohibitions (PROB-04)'
);
assert.match(
qgBlock,
/must_haves\.prohibitions/,
'planner quality_gate must tie SPEC prohibitions to must_haves.prohibitions with no silent drops (PROB-04)'
);
});