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>
This commit is contained in:
sim
2026-08-05 17:20:56 -04:00
parent 9723d2b7e0
commit 7dd9e59f6b
69 changed files with 365 additions and 256 deletions

View File

@@ -1,5 +1,5 @@
[
"tests/agent-classification-parity.test.cjs :: runtime-contract-is-the-product — docs/AGENTS.md section layout + docs/INVENTORY.md table ARE the classification surface being validated",
"tests/agent-classification-parity.test.cjs :: source-text-is-the-product — docs/AGENTS.md section layout + docs/INVENTORY.md table ARE the classification surface being validated",
"tests/agent-frontmatter.test.cjs :: source-text-is-the-product",
"tests/agent-required-reading-consistency.test.cjs :: source-text-is-the-product",
"tests/agent-size-budget.test.cjs :: source-text-is-the-product",
@@ -15,16 +15,13 @@
"tests/autonomous-interactive.test.cjs :: source-text-is-the-product",
"tests/autonomous-to-flag.test.cjs :: source-text-is-the-product",
"tests/chain-flag-plan-phase.test.cjs :: source-text-is-the-product",
"tests/changeset-cli.test.cjs :: reads a product workflow .md file (not CJS source) to verify",
"tests/changeset-cli.test.cjs :: source-text-is-the-product",
"tests/check-update-config-dir.test.cjs :: structural-regression-guard",
"tests/ci-test-scope.test.cjs :: CLI usage banner presence is a user-facing contract.",
"tests/ci-test-scope.test.cjs :: CLI usage failure text is user-facing contract for this parser guard.",
"tests/claude-md.test.cjs :: source-text-is-the-product",
"tests/claude-skills-migration.test.cjs :: source-text-is-the-product",
"tests/cleanup-branch-pruning.test.cjs :: source-text-is-the-product",
"tests/cline-install.test.cjs :: source-text-is-the-product",
"tests/cline-support.test.cjs :: source-text-is-the-product",
"tests/clock-seam.test.cjs :: line 159 reads the STATE.md temp file written by readModifyWriteStateMd — this is a runtime output file assertion, not a source-grep; the API returns void so a file read-back is the only way to verify the transform was applied",
"tests/code-review-agent-skills.test.cjs :: source-text-is-the-product",
"tests/code-review-command.test.cjs :: source-text-is-the-product",
"tests/code-review-pipeline-regression.test.cjs :: source-text-is-the-product",
@@ -47,18 +44,15 @@
"tests/discuss-phase-power.test.cjs :: source-text-is-the-product",
"tests/docs-parity-live-registry.test.cjs :: source-text-is-the-product",
"tests/drift-detection.test.cjs :: source-text-is-the-product",
"tests/edge-probe-docs-fixtures.test.cjs :: runtime-contract-is-the-product — the rendered reference/SPEC/ADR vocab surfaces are the runtime contract; this pins their bijection to the code (docs-parity)",
"tests/edge-probe-planner-contract.test.cjs :: runtime-contract-is-the-product — plan-phase.md's planner prompt is the deployed runtime contract under assertion",
"tests/edge-probe-spec-phase-contract.test.cjs :: runtime-contract-is-the-product — spec-phase.md Step 5.5 is the deployed workflow runtime contract under assertion",
"tests/edge-probe-docs-fixtures.test.cjs :: source-text-is-the-product — the rendered reference/SPEC/ADR vocab surfaces are the runtime contract; this pins their bijection to the code (docs-parity)",
"tests/edge-probe-planner-contract.test.cjs :: source-text-is-the-product — plan-phase.md's planner prompt is the deployed runtime contract under assertion",
"tests/edge-probe-spec-phase-contract.test.cjs :: source-text-is-the-product — spec-phase.md Step 5.5 is the deployed workflow runtime contract under assertion",
"tests/edit-phase.test.cjs :: source-text-is-the-product",
"tests/eslint-rules.test.cjs :: <source-grep reason> must still error",
"tests/eslint-rules.test.cjs :: pending migration",
"tests/eslint-rules.test.cjs :: source-text-is-the-product",
"tests/execute-phase-active-flags.test.cjs :: source-text-is-the-product",
"tests/execute-phase-step-5-5-deviation-doc.test.cjs :: source-text-is-the-product",
"tests/execute-phase-wave.test.cjs :: behavioral — calls gsd-tools and asserts structured output",
"tests/execute-phase-wave.test.cjs :: behavioral — exercises config-set validation, not source text",
"tests/execute-phase-wave.test.cjs :: behavioral — exercises gsd-tools wave-defaulting logic",
"tests/execute-phase-wave.test.cjs :: source-text-is-the-product",
"tests/execute-phase-worktree-artifacts.test.cjs :: source-text-is-the-product",
"tests/explore-command.test.cjs :: source-text-is-the-product",
@@ -66,9 +60,9 @@
"tests/forensics.test.cjs :: source-text-is-the-product",
"tests/frontmatter-cli.test.cjs :: source-text-is-the-product",
"tests/gates-taxonomy.test.cjs :: source-text-is-the-product",
"tests/git-base-branch.test.cjs :: runtime-contract-is-the-product",
"tests/git-base-branch.test.cjs :: runtime-contract-is-the-product — the workflow .md content IS",
"tests/gsd-check-update-worker-platform-gate.test.cjs :: structural assertion on spawn-options shape; the behavior",
"tests/git-base-branch.test.cjs :: source-text-is-the-product",
"tests/git-base-branch.test.cjs :: source-text-is-the-product — the workflow .md content IS",
"tests/gsd-check-update-worker-platform-gate.test.cjs :: structural-regression-guard",
"tests/gsd-researcher-app-aware.test.cjs :: source-text-is-the-product",
"tests/gsd-researcher-flow-diagram.test.cjs :: source-text-is-the-product",
"tests/gsd-settings-advanced.test.cjs :: source-text-is-the-product",
@@ -80,21 +74,19 @@
"tests/install-minimal-hooks.test.cjs :: source-text-is-the-product",
"tests/install-nested-layout.test.cjs :: source-text-is-the-product",
"tests/install-runtime-artifacts.test.cjs :: source-text-is-the-product",
"tests/install.test.cjs :: runtime-contract-is-the-product",
"tests/install.test.cjs :: source-text-is-the-product",
"tests/intel.test.cjs :: source-text-is-the-product — agents/gsd-intel-updater.md IS the",
"tests/intel.test.cjs :: source-text-is-the-product — readFileSync assertions target API-SURFACE.md, which is the generated product of intelApiSurface; asserting on its text content is the only way to verify correct generation.",
"tests/inventory-headings-countfree.test.cjs :: runtime-contract-is-the-product — INVENTORY.md heading format is the shipped doc surface being locked",
"tests/inventory-headings-countfree.test.cjs :: source-text-is-the-product — INVENTORY.md heading format is the shipped doc surface being locked",
"tests/ios-scaffold-safety.test.cjs :: source-text-is-the-product",
"tests/issue-2639-codex-toml-neutralization.test.cjs :: source-text-is-the-product",
"tests/issue-429-comment-text-gate.test.cjs :: source-text-is-the-product",
"tests/issue-498-update-backup-runtime-dir.test.cjs :: structural assertion on the deployed update.md backup bash;",
"tests/issue-57-runtime-install-no-drift.test.cjs :: delegation-presence guard. Catches wholesale removal of the registry",
"tests/issue-57-runtime-install-no-drift.test.cjs :: structural guard over bin/install.js source. Behavioral assertions",
"tests/issue-498-update-backup-runtime-dir.test.cjs :: source-text-is-the-product",
"tests/issue-57-runtime-install-no-drift.test.cjs :: structural-regression-guard",
"tests/issue-607-installer-dry-run.install.test.cjs :: integration-test-input",
"tests/issue-607-legacy-cleanup.test.cjs :: integration-test-input",
"tests/issue-787-cline-hooks-agents.test.cjs :: source-text-is-the-product",
"tests/issue-815-update-next-channel.test.cjs :: reads product workflow/command markdown to verify the --next RC channel contract — not a source-grep test",
"tests/issue-815-update-next-channel.test.cjs :: source-text-is-the-product",
"tests/locking-bugs-1909-1916-1925-1927.test.cjs :: architectural-invariant",
"tests/mcp-tool-inheritance.test.cjs :: source-text-is-the-product",
"tests/milestone-summary.test.cjs :: source-text-is-the-product",
@@ -111,7 +103,6 @@
"tests/path-replacement.test.cjs :: source-text-is-the-product",
"tests/phase-dependency-levels.test.cjs :: source-text-is-the-product",
"tests/phase.test.cjs :: source-text-is-the-product",
"tests/phase.test.cjs :: state-md-is-the-runtime-contract — regression tests for",
"tests/phase6-capability-docs.test.cjs :: source-text-is-the-product",
"tests/phase6-capstone-conformance.test.cjs :: source-text-is-the-product",
"tests/phase6-planning-capabilities.test.cjs :: source-text-is-the-product",
@@ -127,7 +118,6 @@
"tests/product-name-purity.test.cjs :: source-text-is-the-product",
"tests/profile-output.test.cjs :: source-text-is-the-product",
"tests/progress-forensic.test.cjs :: source-text-is-the-product",
"tests/prompt-budget-cli.test.cjs :: prompt-content-is-the-product",
"tests/prompt-thinning.test.cjs :: source-text-is-the-product",
"tests/quick-session-management.test.cjs :: source-text-is-the-product",
"tests/qwen-skills-migration.test.cjs :: source-text-is-the-product",
@@ -137,12 +127,11 @@
"tests/release-coverage-scope.test.cjs :: source-text-is-the-product",
"tests/release-tarball-smoke-workflow.test.cjs :: source-text-is-the-product",
"tests/release-tarball-smoke.install.test.cjs :: integration-test-input",
"tests/research-agent-profiles.test.cjs :: <runtime-contract-is-the-product> research agent .md content is the governed surface",
"tests/research-agent-profiles.test.cjs :: source-text-is-the-product research agent .md content is the governed surface",
"tests/review-default-reviewers-workflow.test.cjs :: source-text-is-the-product",
"tests/roadmap.test.cjs :: source-text-is-the-product",
"tests/run-tests-harness.test.cjs :: run-tests.cjs is a CLI test harness whose only IR is its",
"tests/runtime-launcher-parity.test.cjs :: structural parity/drift guard — asserts literal presence/absence of the canonical gsd_run launcher and the retired $GSD_SDK / `/gsd-tools` tokens across workflow markdown; there is no typed IR for \"this source file does not contain substring X\".",
"tests/runtime-name-policy.test.cjs :: runtime-contract-is-the-product — FALLBACK_ALIASES source text IS the",
"tests/runtime-launcher-parity.test.cjs :: structural-regression-guard",
"tests/runtime-name-policy.test.cjs :: source-text-is-the-product — FALLBACK_ALIASES source text IS the",
"tests/scan-command.test.cjs :: source-text-is-the-product",
"tests/secret-scan-lint.security.test.cjs :: source-text-is-the-product",
"tests/secure-phase.test.cjs :: source-text-is-the-product",
@@ -166,7 +155,7 @@
"tests/workflow-compat.test.cjs :: source-text-is-the-product",
"tests/workflow-guard-registration.test.cjs :: structural-regression-guard",
"tests/workflow-maintainer-skip.test.cjs :: source-text-is-the-product",
"tests/workflow-shell-pinning.test.cjs :: file-scope prefilter, not a test assertion — we need to",
"tests/workflow-shell-pinning.test.cjs :: source-text-is-the-product",
"tests/workflow-size-budget.test.cjs :: source-text-is-the-product",
"tests/workspace.test.cjs :: source-text-is-the-product",
"tests/worktree-cleanup.test.cjs :: source-text-is-the-product",

View File

@@ -1,6 +1,8 @@
'use strict';
// allow-test-rule: reads workflow YAML source as the security artifact under test #1190
// allow-test-rule: source-text-is-the-product (#1190)
// Reads the shipped .github/workflows YAML — the deployed text GitHub
// Actions executes — as the security artifact under test.
/**
* ADR-230 regression guard: PR target-branch policy.

View File

@@ -1,4 +1,4 @@
// allow-test-rule: runtime-contract-is-the-product — docs/AGENTS.md section layout + docs/INVENTORY.md table ARE the classification surface being validated
// allow-test-rule: source-text-is-the-product — docs/AGENTS.md section layout + docs/INVENTORY.md table ARE the classification surface being validated
'use strict';
/**

View File

@@ -1,3 +1,9 @@
// allow-test-rule: structural-regression-guard (#1972)
// Reads milestone.cjs/phase.cjs/frontmatter.cjs source and parses for bare
// fs.writeFileSync call sites — a specific code pattern that must not exist
// to prevent partial-write corruption. Behavioral tests cannot distinguish
// platformWriteSync from a bare fs.writeFileSync; only source inspection can.
/**
* Structural regression guard for atomic write usage (#1972).
*

View File

@@ -327,7 +327,8 @@ describe('changeset cli extract: version-range changelog extraction (#3496)', ()
});
// F1: workflows/update.md must reference the extract subcommand invocation.
// allow-test-rule: reads a product workflow .md file (not CJS source) to verify
// allow-test-rule: source-text-is-the-product
// Reads a product workflow .md file (not CJS source) to verify
// the user-facing instruction was wired; there is no behavioural runtime to invoke.
test('F1: workflows/update.md contains concrete extract subcommand invocation', (_t) => {
const workflowPath = path.join(ROOT, 'gsd-core', 'workflows', 'update.md');
@@ -359,7 +360,8 @@ describe('changeset cli extract: version-range changelog extraction (#3496)', ()
// NOT the old broken path ($GSD_DIR/gsd-core/scripts/changeset/cli.cjs).
// The installer copies scripts/changeset/ into <configDir>/scripts/changeset/,
// so the runtime path is $GSD_DIR/scripts/changeset/cli.cjs (#935).
// allow-test-rule: reads a product workflow .md file (not CJS source) to verify
// allow-test-rule: source-text-is-the-product
// Reads a product workflow .md file (not CJS source) to verify
// the runtime install path contract; there is no behavioural runtime to invoke.
test('F2: update.md CLI path is $GSD_DIR/scripts/changeset/cli.cjs (not gsd-core/scripts/…) (#935)', (_t) => {
const workflowPath = path.join(ROOT, 'gsd-core', 'workflows', 'update.md');
@@ -377,7 +379,8 @@ describe('changeset cli extract: version-range changelog extraction (#3496)', ()
});
// F3: update.md must guard against the CLI being missing (not pure silent-swallow)
// allow-test-rule: reads a product workflow .md file (not CJS source) to verify
// allow-test-rule: source-text-is-the-product
// Reads a product workflow .md file (not CJS source) to verify
// the guard is present; there is no behavioural runtime to invoke.
test('F3: update.md has an explicit guard when changeset CLI is missing (#935)', (_t) => {
const workflowPath = path.join(ROOT, 'gsd-core', 'workflows', 'update.md');

View File

@@ -176,9 +176,12 @@ describe('ci-test-scope.cjs', () => {
encoding: 'utf8',
});
assert.notStrictEqual(r.status, 0);
// allow-test-rule: CLI usage failure text is user-facing contract for this parser guard.
// allow-test-rule: pending-migration-to-typed-ir [#3090]
// Regex-matches the CLI's human-readable stderr formatter (usage banner +
// arg-parser Error#message) — CONTRIBUTING's own BAD example verbatim.
// scripts/ci-test-scope.cjs has no --json / frozen-reason-enum error mode
// yet; adding one is a production change out of scope here. Tracked under #3090.
assert.match(r.stderr, /--files requires a value/);
// allow-test-rule: CLI usage banner presence is a user-facing contract.
assert.match(r.stderr, /Usage:/);
});
@@ -211,7 +214,6 @@ describe('ci-test-scope.cjs', () => {
// A plain source file that matches no RULES entry but is under gsd-core/ (code path)
const result = scopeFor(['gsd-core/src/some-util.js']);
assert.strictEqual(result.code_changed, true);
// allow-test-rule: the unit-fallback contract is the exact subject of bug #408.
assert.deepStrictEqual(result.targeted_tests, ['unit'],
'targeted_tests must be [\'unit\'] when code changed but no rule matched');
});

View File

@@ -1,4 +1,4 @@
// allow-test-rule: AC2 requires asserting no `runtime === 'claude'` string-equality branch remains in bin/install.js — the descriptor-migration contract is a property of the source text, so a source-grep is the only faithful check (#2086)
// allow-test-rule: structural-regression-guard — AC2 requires asserting no `runtime === 'claude'` string-equality branch remains in bin/install.js — the descriptor-migration contract is a property of the source text, so a source-grep is the only faithful check (#2086)
'use strict';
/**

View File

@@ -1,4 +1,4 @@
// allow-test-rule: AC2 requires asserting no `runtime === 'cline'` string-equality branch remains in bin/install.js/src — the descriptor-migration contract is a property of the source text, so a source-grep is the only faithful check (#2090)
// allow-test-rule: structural-regression-guard — AC2 requires asserting no `runtime === 'cline'` string-equality branch remains in bin/install.js/src — the descriptor-migration contract is a property of the source text, so a source-grep is the only faithful check (#2090)
'use strict';
/**

View File

@@ -1,5 +1,12 @@
'use strict';
// allow-test-rule: line 159 reads the STATE.md temp file written by readModifyWriteStateMd — this is a runtime output file assertion, not a source-grep; the API returns void so a file read-back is the only way to verify the transform was applied
// allow-test-rule: pending-migration-to-typed-ir [#3090]
// This file's assert.throws(fn, /regex/) sites (acquireStateLock /
// readModifyWriteStateMd / withPlanningLock / acquireInstallMigrationLock
// timeout and error-propagation checks) regex-match the human-readable
// thrown Error#message — CONTRIBUTING's "Error / status / reason" BAD
// pattern; the fix is a frozen-enum REASON code on the thrown error, which
// requires a production change to those locking functions that is out of
// scope for this test-only change. Tracked for correction under #3090.
/**
* Deterministic clock-seam tests for acquireStateLock / withPlanningLock (issue #453).
@@ -40,6 +47,7 @@ const stateMod = require('../gsd-core/bin/lib/state.cjs');
const { acquireStateLock, releaseStateLock, readModifyWriteStateMd } = stateMod;
const { withPlanningLock } = require('../gsd-core/bin/lib/planning-workspace.cjs');
const { createTempProject, cleanup, runGsdTools } = require('./helpers.cjs');
const { stateExtractField } = require('../gsd-core/bin/lib/state-document.cjs');
// ─────────────────────────────────────────────────────────────────────────────
// 1. Fake-clock proof: acquireStateLock accepts and uses the clock seam
@@ -822,7 +830,7 @@ describe('readModifyWriteStateMd lock cleanup on error', () => {
const clock = makeFakeClock(0);
readModifyWriteStateMd(statePath, (c) => c + '\n**Patched:** yes\n', tmpDir, undefined, clock);
const content = fs.readFileSync(statePath, 'utf-8');
assert.ok(content.includes('**Patched:** yes'), 'transform must be applied');
assert.strictEqual(stateExtractField(content, 'Patched'), 'yes', 'transform must be applied');
assert.strictEqual(clock.sleepCalls.length, 0, 'no sleep when lock is immediately available');
});
});

View File

@@ -1,4 +1,4 @@
// allow-test-rule: AC2 requires asserting no `runtime === 'codex'` string-equality and no positive `isCodex` branch remain in bin/install.js/src — the descriptor-migration contract is a property of the source text, so a source-grep is the only faithful check (#2088)
// allow-test-rule: structural-regression-guard — AC2 requires asserting no `runtime === 'codex'` string-equality and no positive `isCodex` branch remain in bin/install.js/src — the descriptor-migration contract is a property of the source text, so a source-grep is the only faithful check (#2088)
'use strict';
/**

View File

@@ -707,8 +707,6 @@ describe('bug #2638 — sub_repos canonical location', () => {
__foldDescribe("folded:bug-3523-cjs-loadconfig-branching-strategy-warning (consolidation epic #1969 B6 #1975)", () => {
'use strict';
// allow-test-rule: validates runtime CLI stdout/stderr warning behavior, not source grep (see #3523)
/**
* Regression tests for #3523 — CJS loadConfig must not emit a false
* "unknown config key(s)" warning for `branching_strategy` when that key
@@ -833,7 +831,7 @@ describe('bug-3523 — no warning for legacy top-level branching_strategy', () =
);
// After migration write-back, config-get should find git.branching_strategy.
const result = runWithStderr(['config-get', 'git.branching_strategy'], tmpDir);
const result = runWithStderr(['config-get', 'git.branching_strategy', '--raw'], tmpDir);
assert.equal(
result.status,
@@ -845,8 +843,9 @@ describe('bug-3523 — no warning for legacy top-level branching_strategy', () =
'',
`No error should fire when reading migrated branching_strategy (#3523) — got: ${result.stderr}`
);
assert.ok(
result.stdout.includes('milestone'),
assert.equal(
result.stdout.trim(),
'milestone',
`Expected git.branching_strategy to be 'milestone' but got: ${result.stdout}`
);
});
@@ -880,6 +879,11 @@ describe('bug-3523 — double-emission reduced to single-emission', () => {
const result = runWithStderr(['resolve-model', 'planner'], tmpDir);
// allow-test-rule: pending-migration-to-typed-ir [#3090]
// Counts occurrences of a sentinel substring in the CLI's human-readable
// stderr warning text — no structured "warning count"/warning-list API is
// exposed yet; adding one is a production change out of scope here.
// Tracked under #3090.
// Count how many times the sentinel key appears in warnings
const warningLines = result.stderr
.split('\n')

View File

@@ -1808,7 +1808,7 @@ describe('#3197 — gsd-tools.cjs config-set workflow._auto_chain_active', () =>
{
const { describe: __foldDescribe } = require('node:test');
__foldDescribe("folded:bug-3086-git-create-tag-config-gate (consolidation epic #1969 B2 #1971)", () => {
// allow-test-rule: workflow-markdown-is-the-runtime-contract (see #3086)
// allow-test-rule: source-text-is-the-product (see #3086)
// Justification: complete-milestone.md IS the runtime — the agent reads and
// follows it directly. Asserting the <config-check> block is present in the
// markdown is the only way to verify the gate is wired. Per CONTEXT.md L611.
@@ -2285,7 +2285,7 @@ describe('feat-3210 / H5: enum validation for code_quality.fallow.scope and .pro
__foldDescribe("folded:bug-3212-execute-phase-stall-safe-resume (consolidation epic #1969 B3 #1972)", () => {
'use strict';
// allow-test-rule: source-text-is-product [#3212]
// allow-test-rule: source-text-is-the-product [#3212]
// The bug is in workflow/config contracts consumed by agents at runtime.
const { describe, test } = require('node:test');

View File

@@ -1,4 +1,4 @@
// allow-test-rule: AC2 requires asserting no `runtime === 'cursor'` string-equality branch remains in bin/install.js/src — the descriptor-migration contract is a property of the source text, so a source-grep is the only faithful check (#2089)
// allow-test-rule: structural-regression-guard — AC2 requires asserting no `runtime === 'cursor'` string-equality branch remains in bin/install.js/src — the descriptor-migration contract is a property of the source text, so a source-grep is the only faithful check (#2089)
'use strict';
/**

View File

@@ -1,4 +1,4 @@
// allow-test-rule: runtime-contract-is-the-product see #2568
// allow-test-rule: source-text-is-the-product see #2568
// agents/gsd-debug-session-manager.md is executed instruction text: the orchestrator
// follows it verbatim, so WHERE the commit step sits relative to the terminal vs
// non-terminal summary shapes IS the contract. The commit_docs gate it relies on is

View File

@@ -914,11 +914,11 @@ describe('bug #2950: stale deleted-command references removed from workflow file
* integration. (Test-enforced via concept-mapping audit.)
*/
// allow-test-rule: structural-IR parser for a docs guide. The .includes() (see #2840)
// calls below build a typed record (commandsPresent flags, conceptPairs
// flags, nonGoalFlags, safetyFlags); assertions run on those booleans, not
// on raw text. This is the documented escape hatch in
// scripts/lint-no-source-grep.cjs for doc-shape tests.
// allow-test-rule: source-text-is-the-product (see #2840)
// docs/issue-driven-orchestration.md's deployed prose IS the product being
// validated for required-concept coverage. The .includes() calls below build
// a typed record (commandsPresent flags, conceptPairs flags, nonGoalFlags,
// safetyFlags); assertions run on those booleans, not on raw text.
'use strict';

View File

@@ -1,4 +1,4 @@
// allow-test-rule: runtime-contract-is-the-product — the rendered reference/SPEC/ADR vocab surfaces are the runtime contract; this pins their bijection to the code (docs-parity)
// allow-test-rule: source-text-is-the-product — the rendered reference/SPEC/ADR vocab surfaces are the runtime contract; this pins their bijection to the code (docs-parity)
// Asserts the portable reference doc (gsd-core/references/edge-probe.md) keeps its
// worked-example JSON blocks in sync with the source-of-truth fixture files under
// gsd-core/references/edge-probe-fixtures/. The fixtures are the canonical data; the

View File

@@ -1,4 +1,4 @@
// allow-test-rule: runtime-contract-is-the-product — plan-phase.md's planner prompt is the deployed runtime contract under assertion
// allow-test-rule: source-text-is-the-product — plan-phase.md's planner prompt is the deployed runtime contract under assertion
// plan-phase.md is the deployed planning workflow contract; these checks lock
// the SPEC path wiring and quality-gate that the edge-probe review (RR-01/02/03)
// requires — assertions scope to extracted sub-blocks to avoid false positives.

View File

@@ -1,4 +1,4 @@
// allow-test-rule: runtime-contract-is-the-product — spec-phase.md Step 5.5 is the deployed workflow runtime contract under assertion
// allow-test-rule: source-text-is-the-product — spec-phase.md Step 5.5 is the deployed workflow runtime contract under assertion
// spec-phase.md is the deployed spec workflow contract; these checks lock
// the Step 5.5 wiring so the edge-probe.cjs runtime invocation cannot
// silently rot the way the original plan-phase no-op did (reviewer finding RR-11).

View File

@@ -268,7 +268,6 @@ describe('execute-phase docs: user-facing wave flag', () => {
describe('phase-plan-index: wave grouping behavior', () => {
test('phase-plan-index groups plans by wave (DAG-bucketing: P002 depends on P001)', () => {
// allow-test-rule: behavioral — calls gsd-tools and asserts structured output
const fs = require('fs');
const path = require('path');
const tmpDir = createTempProject();
@@ -334,7 +333,6 @@ describe('phase-plan-index: wave grouping behavior', () => {
});
test('phase-plan-index defaults missing wave frontmatter to wave 1', () => {
// allow-test-rule: behavioral — exercises gsd-tools wave-defaulting logic
const fs = require('fs');
const path = require('path');
const tmpDir = createTempProject();
@@ -419,7 +417,6 @@ describe('use_worktrees config: cross-workflow structural coverage', () => {
});
test('config-set accepts workflow.use_worktrees', () => {
// allow-test-rule: behavioral — exercises config-set validation, not source text
const tmpDir = createTempProject();
try {
const result = runGsdTools('config-set workflow.use_worktrees true', tmpDir);
@@ -802,7 +799,9 @@ describe('execute-phase: between-wave manifest reset (#1369, #3384)', () => {
const { describe: __foldDescribe } = require('node:test');
__foldDescribe("folded:bug-3096-ai-integration-phase-parallel-race (consolidation epic #1969 B4 #1973)", () => {
'use strict';
// allow-test-rule: reads product workflow markdown (ai-integration-phase.md) to verify structural ordering contract — not a source-grep test (see #3096)
// allow-test-rule: source-text-is-the-product (see #3096)
// Reads product workflow markdown (ai-integration-phase.md) to verify
// structural ordering contract.
// Regression guard for bug #3096.
//

View File

@@ -1,4 +1,4 @@
// allow-test-rule: runtime-contract-is-the-product see #1856
// allow-test-rule: source-text-is-the-product see #1856
// The orchestrator cwd-drift guard (#48) is shell EMBEDDED in execute-phase.md.
// The shipped text IS the runtime contract, so these tests extract the block and
// EXECUTE it against real git fixtures rather than asserting on its characters —

View File

@@ -105,7 +105,9 @@ describe('gsd-executor — state.* calls use the named-only router form (#1863 r
const { describe: __foldDescribe } = require('node:test');
__foldDescribe("folded:bug-3097-3099-executor-worktree-path-safety (consolidation epic #1969 B7 #1976)", () => {
'use strict';
// allow-test-rule: reads markdown product files (gsd-executor.md, worktree-path-safety.md) to verify structural protocol — not source-grep (see #3097)
// allow-test-rule: source-text-is-the-product (see #3097)
// Reads markdown product files (gsd-executor.md, worktree-path-safety.md) to
// verify structural protocol.
// Regression guards for bug #3097 and #3099.
//

View File

@@ -46,9 +46,11 @@ const DEBUG_MD = path.join(__dirname, '..', 'gsd-core', 'workflows', 'debug.md')
const SESSION_MANAGER_MD = path.join(__dirname, '..', 'agents', 'gsd-debug-session-manager.md');
describe('#2257 debug non-terminal session-manager return contract', () => {
// allow-test-rule: workflow/agent prose IS the runtime contract under test #2257
// allow-test-rule: source-text-is-the-product (#2257)
// workflow/agent prose IS the runtime contract under test
const debugContent = fs.readFileSync(DEBUG_MD, 'utf-8');
// allow-test-rule: workflow/agent prose IS the runtime contract under test #2257
// allow-test-rule: source-text-is-the-product (#2257)
// workflow/agent prose IS the runtime contract under test
const managerContent = fs.readFileSync(SESSION_MANAGER_MD, 'utf-8');
const section4Start = debugContent.indexOf('## 4. Session Management');

View File

@@ -24,7 +24,7 @@
* silently re-assert an unsupported capability.
*/
// allow-test-rule: runtime-contract-is-the-product #2598 — the descriptor JSON and the
// allow-test-rule: source-text-is-the-product #2598 — the descriptor JSON and the
// host-integration matrix ARE the negotiated contract; asserting their values is behavioral.
'use strict';

View File

@@ -29,7 +29,7 @@
* disagreement is how the gap survived.
*/
// allow-test-rule: runtime-contract-is-the-product #2603 — the descriptor JSON and the
// allow-test-rule: source-text-is-the-product #2603 — the descriptor JSON and the
// host-integration matrix ARE the negotiated contract; asserting their values is behavioral.
'use strict';

View File

@@ -1,4 +1,4 @@
// allow-test-rule: runtime-contract-is-the-product #2615 — the host-integration matrix
// allow-test-rule: source-text-is-the-product #2615 — the host-integration matrix
// IS the cited source of truth for every descriptor axis (ADR-1239); asserting that a
// shipped axis value appears there, and matches, is a contract assertion.

View File

@@ -13,11 +13,11 @@
* G. Anti-regression guard: five affected workflows must NOT contain the
* duplicated bare `:-main` / `:-master` fallback pattern that was the root cause.
* They must call `gsd_run query git.base-branch` instead.
* (allow-test-rule: runtime-contract-is-the-product — the workflow .md content IS
* (allow-test-rule: source-text-is-the-product — the workflow .md content IS
* the runtime surface; the absence of the bad pattern is what ships to agents.)
*/
// allow-test-rule: runtime-contract-is-the-product
// allow-test-rule: source-text-is-the-product
// Justification: the workflow .md files ARE the product surface — agents read and
// execute them directly. Guard G asserts that the resolved command appears in all five
// workflows, which requires reading those workflow files. Per TESTING-STANDARDS.md §6.

View File

@@ -92,7 +92,8 @@ const CONSUMERS = [
for (const consumer of CONSUMERS) {
test(`${consumer.name} does not re-declare an inline buildParityManifest or any exclusion constant (#2266)`, () => {
// allow-test-rule: source text is the product for this anti-divergence check, see #2266
// allow-test-rule: source-text-is-the-product (see #2266)
// Source text is the product for this anti-divergence check.
const content = fs.readFileSync(path.join(ROOT, ...consumer.rel), 'utf8');
for (const { label, re } of FORBIDDEN_INLINE) {
assert.ok(

View File

@@ -22,7 +22,8 @@
* is the minimum-cost contract.
*/
// allow-test-rule: structural assertion on spawn-options shape; the behavior
// allow-test-rule: structural-regression-guard
// structural assertion on spawn-options shape; the behavior
// (Windows-only shell resolution) is platform-gated at runtime and cannot be
// reached on POSIX CI without a Windows lane.
@@ -295,7 +296,8 @@ describe('Issue #815: --next dist-tag support', () => {
* contract for the worker, the same rationale #378 carried.
*/
// allow-test-rule: structural assertion on hook delegation; the behavior being (see #378)
// allow-test-rule: structural-regression-guard (see #378)
// structural assertion on hook delegation; the behavior being
// tested (correct package name → no E404) only manifests at runtime against the
// live npm registry, which CI does not call.

View File

@@ -1,4 +1,4 @@
// allow-test-rule: AC2 requires asserting no `runtime === 'hermes'` string-equality branch remains in bin/install.js — the descriptor-migration contract is a property of the source text, so a source-grep is the only faithful check (#2091)
// allow-test-rule: structural-regression-guard — AC2 requires asserting no `runtime === 'hermes'` string-equality branch remains in bin/install.js — the descriptor-migration contract is a property of the source text, so a source-grep is the only faithful check (#2091)
'use strict';
/**

View File

@@ -2652,7 +2652,7 @@ describe('enh-770: gsd-config-reload.js hook script', () => {
});
test('gsd-config-reload.js contains the gsd-hook-version stamp', () => {
// allow-test-rule: runtime-contract-is-the-product — the stamp template token (see #770)
// allow-test-rule: source-text-is-the-product — the stamp template token (see #770)
// IS the product surface that the installer must find and replace with the
// real version at copy time; asserting its presence is required.
const content = fs.readFileSync(reloadScript, 'utf8');
@@ -2663,7 +2663,7 @@ describe('enh-770: gsd-config-reload.js hook script', () => {
});
test('gsd-config-reload.js reads from stdin and emits JSON output', () => {
// allow-test-rule: runtime-contract-is-the-product — the stdin-read and (see #770)
// allow-test-rule: source-text-is-the-product — the stdin-read and (see #770)
// JSON-emit pattern IS the hook contract; asserting its presence is required.
const content = fs.readFileSync(reloadScript, 'utf8');
assert.ok(
@@ -2673,7 +2673,7 @@ describe('enh-770: gsd-config-reload.js hook script', () => {
});
test('gsd-config-reload.js targets the FileChanged hook event', () => {
// allow-test-rule: runtime-contract-is-the-product — the hookEventName is (see #770)
// allow-test-rule: source-text-is-the-product — the hookEventName is (see #770)
// the protocol surface; asserting its presence verifies the contract.
const content = fs.readFileSync(reloadScript, 'utf8');
assert.ok(
@@ -2693,7 +2693,7 @@ describe('enh-770: hooks/hooks.json plugin manifest includes new hook events', (
});
test('hooks.json contains SubagentStop event', () => {
// allow-test-rule: runtime-contract-is-the-product — hooks.json IS the (see #770)
// allow-test-rule: source-text-is-the-product — hooks.json IS the (see #770)
// plugin manifest surface that Claude Code reads at plugin load time.
const content = JSON.parse(fs.readFileSync(hooksJsonPath, 'utf8'));
assert.ok(
@@ -2703,7 +2703,7 @@ describe('enh-770: hooks/hooks.json plugin manifest includes new hook events', (
});
test('hooks.json contains Stop event', () => {
// allow-test-rule: runtime-contract-is-the-product — hooks.json IS the (see #770)
// allow-test-rule: source-text-is-the-product — hooks.json IS the (see #770)
// plugin manifest surface that Claude Code reads at plugin load time.
const content = JSON.parse(fs.readFileSync(hooksJsonPath, 'utf8'));
assert.ok(
@@ -2713,7 +2713,7 @@ describe('enh-770: hooks/hooks.json plugin manifest includes new hook events', (
});
test('hooks.json contains PreCompact event', () => {
// allow-test-rule: runtime-contract-is-the-product — hooks.json IS the (see #770)
// allow-test-rule: source-text-is-the-product — hooks.json IS the (see #770)
// plugin manifest surface that Claude Code reads at plugin load time.
const content = JSON.parse(fs.readFileSync(hooksJsonPath, 'utf8'));
assert.ok(
@@ -2723,7 +2723,7 @@ describe('enh-770: hooks/hooks.json plugin manifest includes new hook events', (
});
test('hooks.json contains FileChanged event', () => {
// allow-test-rule: runtime-contract-is-the-product — hooks.json IS the (see #770)
// allow-test-rule: source-text-is-the-product — hooks.json IS the (see #770)
// plugin manifest surface that Claude Code reads at plugin load time.
const content = JSON.parse(fs.readFileSync(hooksJsonPath, 'utf8'));
assert.ok(

View File

@@ -1153,7 +1153,7 @@ describe('readCmdNames() — tolerates missing commands/gsd directory (#1223)',
});
// ─── Section N: Antigravity .agents canonical workspace dir (#791) ─────────────
// allow-test-rule: runtime-contract-is-the-product
// allow-test-rule: source-text-is-the-product
// Reads deployed agent .md files whose text IS the product surface the
// Antigravity runtime loads at startup (path references, command names).
@@ -1289,7 +1289,7 @@ describe('install — --devin-desktop CLI flag routes to windsurf runtime (#792)
});
});
// ─── Section N: Windsurf workflow slash-command install (#1615) ─────────────
// allow-test-rule: runtime-contract-is-the-product
// allow-test-rule: source-text-is-the-product
// Reads deployed workflow .md files whose text IS the product surface the
// Windsurf runtime loads at startup (path references, command names).

View File

@@ -1,4 +1,4 @@
// allow-test-rule: runtime-contract-is-the-product — INVENTORY.md heading format is the shipped doc surface being locked
// allow-test-rule: source-text-is-the-product — INVENTORY.md heading format is the shipped doc surface being locked
'use strict';
/**

View File

@@ -1,4 +1,3 @@
// allow-test-rule: behavioral-fs-fixture (#2828)
'use strict';
// Regression guard for #2828: on a flat unmilestoned roadmap (no versioned milestone

View File

@@ -76,7 +76,9 @@ describe('Issue #498: the live repo passes the drift lint', () => {
const { describe: __foldDescribe } = require('node:test');
__foldDescribe("folded:bug-170-workflow-fallback-install-hint (consolidation epic #1969 B4 #1973)", () => {
'use strict';
// allow-test-rule: workflow markdown is shipped product text; this test validates fallback hint literals across all workflow files (see #170)
// allow-test-rule: source-text-is-the-product (see #170)
// Workflow markdown is shipped product text; this test validates fallback
// hint literals across all workflow files.
const { test } = require('node:test');
const assert = require('node:assert/strict');

View File

@@ -18,9 +18,11 @@
* program; asserting their shape is asserting on the deployed contract.
*/
// allow-test-rule: structural assertion on the deployed update.md backup bash;
// the data-loss behavior only manifests against a real install during a clean
// reinstall, which CI does not perform.
// allow-test-rule: source-text-is-the-product
// update.md's bash blocks ARE the deployed /gsd:update program; asserting
// their shape is asserting on the deployed contract. The data-loss behavior
// only manifests against a real install during a clean reinstall, which CI
// does not perform.
'use strict';

View File

@@ -159,7 +159,8 @@ describe('issue-57 AC2 — config-mutation dispatch is closed over the explicit
}
});
// allow-test-rule: structural guard over bin/install.js source. Behavioral assertions
// allow-test-rule: structural-regression-guard
// structural guard over bin/install.js source. Behavioral assertions
// cannot observe inline `runtime === '...'` config branching, so this enforces that
// every inline per-runtime branch references a runtime the adapter registry knows
// about — a NEW branch against an unregistered runtime name fails here. It matches
@@ -184,7 +185,8 @@ describe('issue-57 AC2 — config-mutation dispatch is closed over the explicit
);
});
// allow-test-rule: structural guard over bin/install.js source (#2103). VS Code
// allow-test-rule: structural-regression-guard (#2103)
// structural guard over bin/install.js source. VS Code
// (capabilities/vscode/capability.json) is a registry runtime (role:runtime, for
// validator/host-integration coverage) but is NEVER CLI-installed — it is a
// Marketplace/VSIX extension with no --vscode flag and no allRuntimes membership
@@ -214,7 +216,8 @@ describe('issue-57 AC2 — config-mutation dispatch is closed over the explicit
);
});
// allow-test-rule: delegation-presence guard. Catches wholesale removal of the registry
// allow-test-rule: structural-regression-guard
// delegation-presence guard. Catches wholesale removal of the registry
// dispatch (a regression to scattered per-runtime config branching). Presence-style, not
// absence-grep, so it does not bite on incidental non-config `runtime === '...'` checks.
test('bin/install.js requires the config adapter registry and dispatches through it', () => {

View File

@@ -1,5 +1,7 @@
'use strict';
// allow-test-rule: reads product workflow/command markdown to verify the --next RC channel contract — not a source-grep test
// allow-test-rule: source-text-is-the-product
// Reads product workflow/command markdown to verify the --next RC channel
// contract.
// Issue #815: `/gsd-update --next` (alias `--rc`) must thread the @next dist-tag
// through the whole update flow (version check + install) while leaving the
@@ -102,7 +104,9 @@ describe('update.md — no bare ~.claude path references (#2470)', () => {
const { describe: __foldDescribe } = require('node:test');
__foldDescribe("folded:bug-3130-update-npx-robust-invocation (consolidation epic #1969 B4 #1973)", () => {
'use strict';
// allow-test-rule: reads product workflow markdown (update.md) to verify structural invocation contract — not a source-grep test (see #3130)
// allow-test-rule: source-text-is-the-product (see #3130)
// Reads product workflow markdown (update.md) to verify structural
// invocation contract.
// Regression guard for bug #3130.
//

View File

@@ -1,8 +1,9 @@
// allow-test-rule: behavioral-subprocess-test — see #2505 — Phase 5 kimi-variant
// disambiguation is verified via install.js subprocess output capture, since
// the disambiguateKimiVariant function is inline in bin/install.js (not
// exported). The test sets a disposable HOME, creates the probe config files,
// and asserts on the printed notices.
// allow-test-rule: pending-migration-to-typed-ir [#3090]
// Phase 5 kimi-variant disambiguation is verified via install.js subprocess
// output capture (regex on printed notices), since disambiguateKimiVariant
// is inline in bin/install.js (not exported) and emits no structured/JSON
// output. Exporting the function and/or adding a --json notice mode is a
// production change out of scope here. Tracked under #3090.
process.env.GSD_TEST_MODE = '1';
const { test, describe, before, after } = require('node:test');

View File

@@ -930,7 +930,7 @@ describe('TABLE_SCHEMAS parity: registry headers must appear verbatim in their s
*/
function assertHeaderInFile(relPath, variant) {
const fullPath = path.join(ROOT, relPath);
const content = fs.readFileSync(fullPath, 'utf8'); // allow-test-rule: runtime-contract-is-the-product — template/registry parity (#2242)
const content = fs.readFileSync(fullPath, 'utf8'); // allow-test-rule: source-text-is-the-product — template/registry parity (#2242)
const expectedHeader = buildHeader(variant);
const normalizedExpected = normalize(expectedHeader);
const found = content

View File

@@ -1,5 +1,5 @@
// allow-test-rule: structural-regression-guard see #2517
// allow-test-rule: runtime-contract-is-the-product see #2684
// allow-test-rule: source-text-is-the-product see #2684
// Guards the omit-when-inherit fix: workflow orchestrators must instruct the agent to
// OMIT the model= param from Agent() calls when the *_model var is "inherit" or empty.
// Without it, model="" is passed verbatim and 404s on non-Claude runtimes

View File

@@ -1,4 +1,4 @@
// allow-test-rule: runtime-contract-is-the-product (see #1073) — this guard asserts the
// allow-test-rule: source-text-is-the-product (see #1073) — this guard asserts the
// ABSENCE of phantom pre-migration issue references in repo text (docs, tests,
// workflows). The file *content* is the product surface here (#1073): dangling
// refs that don't exist in open-gsd/gsd-core mislead triage and manufacture

View File

@@ -1,4 +1,4 @@
// allow-test-rule: AC2 requires asserting no `runtime === 'opencode'` string-equality branch remains in bin/install.js/src — the descriptor-migration contract is a property of the source text, so a source-grep is the only faithful check (#2087)
// allow-test-rule: structural-regression-guard — AC2 requires asserting no `runtime === 'opencode'` string-equality branch remains in bin/install.js/src — the descriptor-migration contract is a property of the source text, so a source-grep is the only faithful check (#2087)
'use strict';
/**

View File

@@ -1,10 +1,6 @@
// allow-test-rule: source-text-is-the-product
// Reads .md/.json/.yml product files whose deployed text IS what the
// runtime loads — testing text content tests the deployed contract.
// allow-test-rule: state-md-is-the-runtime-contract — regression tests for
// bug #3517 assert the exact STATE.md fields written by phase.complete;
// STATE.md IS the product surface being verified, not source code.
// Migration to typed-IR parser tracked in #2974.
/**
* GSD Tools Tests - Phase
@@ -5757,6 +5753,13 @@ describe('bug-3287 — init plan-phase exposes expected_phase_dir with project_c
// ─────────────────────────────────────────────────────────────────────────────
{
// Typed STATE.md surfaces (#3090) — replaces raw regex/substring matching
// on STATE.md content written by phase.complete in this bug-#3517 block.
const { stateExtractField } = require('../gsd-core/bin/lib/state-document.cjs');
const { extractFrontmatter } = require('../gsd-core/bin/lib/frontmatter.cjs');
const { parseMarkdownTable } = require('../gsd-core/bin/lib/markdown-table.cjs');
const { parsePhaseFromProse } = require('../gsd-core/bin/lib/phase-id.cjs');
function runSdkQuery(args, cwd) {
if (Array.isArray(args) && args[0] === 'phase.complete') {
writePassedVerificationForPhase(cwd, args[1]);
@@ -5986,24 +5989,24 @@ describe('bug-3287 — init plan-phase exposes expected_phase_dir with project_c
assert.ok(r1.success, `first call failed: ${r1.error}`);
const stateAfter1 = fs.readFileSync(statePath, 'utf8');
const match1 = stateAfter1.match(/completed_phases:\s*(\d+)/);
assert.ok(match1, 'completed_phases not found in frontmatter after first call');
const progress1 = extractFrontmatter(stateAfter1).progress;
assert.ok(progress1, 'progress not found in frontmatter after first call');
assert.equal(
Number(match1[1]),
Number(progress1.completed_phases),
2,
`After first call: completed_phases should be 2 (derived from ROADMAP: phases 4 and 5 complete), got ${match1[1]}`,
`After first call: completed_phases should be 2 (derived from ROADMAP: phases 4 and 5 complete), got ${progress1.completed_phases}`,
);
const r2 = runSdkQuery(['phase.complete', '5'], tmpDir);
assert.ok(r2.success, `second call failed: ${r2.error}`);
const stateAfter2 = fs.readFileSync(statePath, 'utf8');
const match2 = stateAfter2.match(/completed_phases:\s*(\d+)/);
assert.ok(match2, 'completed_phases not found in frontmatter after second call');
const progress2 = extractFrontmatter(stateAfter2).progress;
assert.ok(progress2, 'progress not found in frontmatter after second call');
assert.equal(
Number(match2[1]),
Number(progress2.completed_phases),
2,
`After second call (same phase): completed_phases must remain 2 (idempotent), got ${match2[1]}`,
`After second call (same phase): completed_phases must remain 2 (idempotent), got ${progress2.completed_phases}`,
);
});
@@ -6015,16 +6018,16 @@ describe('bug-3287 — init plan-phase exposes expected_phase_dir with project_c
assert.ok(r.success, `call failed: ${r.error}`);
const state = fs.readFileSync(statePath, 'utf8');
const stoppedMatch = state.match(/stopped_at:\s*(.+)/);
assert.ok(stoppedMatch, 'stopped_at not found in frontmatter');
const stoppedAt = extractFrontmatter(state).stopped_at;
assert.ok(stoppedAt, 'stopped_at not found in frontmatter');
assert.ok(
!stoppedMatch[1].includes('05-03-PLAN.md'),
`stopped_at should not still say "Completed 05-03-PLAN.md" — got: ${stoppedMatch[1]}`,
!stoppedAt.includes('05-03-PLAN.md'),
`stopped_at should not still say "Completed 05-03-PLAN.md" — got: ${stoppedAt}`,
);
assert.ok(
stoppedMatch[1].toLowerCase().includes('phase 5') ||
stoppedMatch[1].toLowerCase().includes('complete'),
`stopped_at should reference phase 5 completion, got: ${stoppedMatch[1]}`,
stoppedAt.toLowerCase().includes('phase 5') ||
stoppedAt.toLowerCase().includes('complete'),
`stopped_at should reference phase 5 completion, got: ${stoppedAt}`,
);
});
@@ -6036,10 +6039,8 @@ describe('bug-3287 — init plan-phase exposes expected_phase_dir with project_c
assert.ok(r.success, `call failed: ${r.error}`);
const state = fs.readFileSync(statePath, 'utf8');
const lastUpdatedMatch = state.match(/last_updated:\s*(.+)/);
assert.ok(lastUpdatedMatch, 'last_updated not found in frontmatter');
const raw = lastUpdatedMatch[1].trim().replace(/^"(.*)"$/, '$1');
const raw = extractFrontmatter(state).last_updated;
assert.ok(raw, 'last_updated not found in frontmatter');
// Must have been refreshed — not the stale seed value from setupPhase3517Project
assert.notEqual(
@@ -6073,10 +6074,10 @@ describe('bug-3287 — init plan-phase exposes expected_phase_dir with project_c
assert.ok(r.success, `call failed: ${r.error}`);
const state = fs.readFileSync(statePath, 'utf8');
const match = state.match(/total_plans:\s*(\d+)/);
assert.ok(match, 'total_plans not found in frontmatter');
const totalPlans = Number(match[1]);
assert.ok(Number.isFinite(totalPlans) && totalPlans > 0, `total_plans must be a positive number, got: ${match[1]}`);
const progress = extractFrontmatter(state).progress;
assert.ok(progress && progress.total_plans !== undefined, 'total_plans not found in frontmatter');
const totalPlans = Number(progress.total_plans);
assert.ok(Number.isFinite(totalPlans) && totalPlans > 0, `total_plans must be a positive number, got: ${progress.total_plans}`);
});
test('frontmatter completed_plans is updated from SUMMARY file count after phase.complete', () => {
@@ -6087,9 +6088,9 @@ describe('bug-3287 — init plan-phase exposes expected_phase_dir with project_c
assert.ok(r.success, `call failed: ${r.error}`);
const state = fs.readFileSync(statePath, 'utf8');
const match = state.match(/completed_plans:\s*(\d+)/);
assert.ok(match, 'completed_plans not found in frontmatter');
const completedPlans = Number(match[1]);
const progress = extractFrontmatter(state).progress;
assert.ok(progress && progress.completed_plans !== undefined, 'completed_plans not found in frontmatter');
const completedPlans = Number(progress.completed_plans);
assert.equal(
completedPlans,
10,
@@ -6105,9 +6106,9 @@ describe('bug-3287 — init plan-phase exposes expected_phase_dir with project_c
assert.ok(r.success, `call failed: ${r.error}`);
const state = fs.readFileSync(statePath, 'utf8');
const match = state.match(/percent:\s*(\d+)/);
assert.ok(match, 'percent not found in frontmatter');
assert.equal(Number(match[1]), 67, `percent should be 67 (2/3 phases), got: ${match[1]}`);
const progress = extractFrontmatter(state).progress;
assert.ok(progress && progress.percent !== undefined, 'percent not found in frontmatter');
assert.equal(Number(progress.percent), 67, `percent should be 67 (2/3 phases), got: ${progress.percent}`);
});
test('state frontmatter and numeric phase line reflect next phase after phase.complete', () => {
@@ -6118,8 +6119,19 @@ describe('bug-3287 — init plan-phase exposes expected_phase_dir with project_c
assert.ok(r.success, `call failed: ${r.error}`);
const state = fs.readFileSync(statePath, 'utf8');
assert.match(state, /completed_phases:\s*2/, 'completed_phases must be updated in frontmatter');
assert.match(state, /Phase:\s*0?6\b/, 'numeric Phase line should advance to phase 6');
const progress = extractFrontmatter(state).progress;
assert.equal(
Number(progress && progress.completed_phases),
2,
`completed_phases must be updated in frontmatter, got: ${progress && progress.completed_phases}`,
);
const phaseLine = stateExtractField(state, 'Phase');
const { phase: nextPhase } = parsePhaseFromProse(phaseLine);
assert.equal(
Number(nextPhase),
6,
`numeric Phase line should advance to phase 6, got Phase line: ${phaseLine}`,
);
});
test('prose-block STATE keeps next phase name without field-miss warnings (#1316)', () => {
@@ -6143,16 +6155,27 @@ describe('bug-3287 — init plan-phase exposes expected_phase_dir with project_c
);
const state = fs.readFileSync(path.join(planningDir, 'STATE.md'), 'utf8');
assert.match(state, /current_phase:\s*"?33"?/, 'current_phase frontmatter must advance to 33');
assert.match(
state,
/^Phase:\s*33\s+—\s+Follow Up Implementation\b/m,
`Current Position Phase line must keep the next phase name; state:\n${state}`,
const currentPhase = extractFrontmatter(state).current_phase;
assert.equal(
String(currentPhase),
'33',
`current_phase frontmatter must advance to 33, got: ${currentPhase}`,
);
const phaseLine = stateExtractField(state, 'Phase');
const { phase: nextPhase, name: nextPhaseName } = parsePhaseFromProse(phaseLine);
assert.equal(Number(nextPhase), 33, `Current Position Phase line must advance to 33; got Phase line: ${phaseLine}`);
assert.equal(
nextPhaseName,
'Follow Up Implementation',
`Current Position Phase line must keep the next phase name; got Phase line: ${phaseLine}`,
);
const lastActivity = stateExtractField(state, 'Last activity');
assert.match(
state,
/^Last activity:\s*\d{4}-\d{2}-\d{2}\s+—\s+Phase 32 complete/m,
`Last activity line must use the template em-dash delimiter with narrative; state:\n${state}`,
lastActivity || '',
/^\d{4}-\d{2}-\d{2}\s+—\s+Phase 32 complete/,
`Last activity line must use the template em-dash delimiter with narrative; got: ${lastActivity}`,
);
});
@@ -6164,10 +6187,14 @@ describe('bug-3287 — init plan-phase exposes expected_phase_dir with project_c
assert.ok(r.success, `call failed: ${r.error}`);
const state = fs.readFileSync(statePath, 'utf8');
assert.match(
state,
/\|\s*5\s*\|\s*7\s*\|/,
`By Phase table should have a row for phase 5 with 7 summaries.\nState:\n${state}`,
const table = parseMarkdownTable(state);
assert.ok(table.ok, `By Phase table must parse; reason: ${table.ok ? '' : table.reason}`);
const row = table.value.rows.find((r) => r.Phase.trim() === '5');
assert.ok(row, `By Phase table should have a row for phase 5.\nState:\n${state}`);
assert.equal(
row.Plans.trim(),
'7',
`By Phase table row for phase 5 should show 7 summaries, got row: ${JSON.stringify(row)}`,
);
});
@@ -6181,10 +6208,24 @@ describe('bug-3287 — init plan-phase exposes expected_phase_dir with project_c
const state = fs.readFileSync(statePath, 'utf8');
assert.match(state, /completed_phases:\s*2/, 'completed_phases must be 2 (4 and 5 complete)');
assert.match(state, /percent:\s*67/, 'percent must be 67%');
const hasPhase6 = /Phase:\s*0?6/.test(state) || /current_phase:\s*0?6/.test(state);
assert.ok(hasPhase6, `STATE.md must reference Phase 6 as current after completing Phase 5.\nState:\n${state}`);
const fm = extractFrontmatter(state);
assert.equal(
Number(fm.progress && fm.progress.completed_phases),
2,
`completed_phases must be 2 (4 and 5 complete), got: ${fm.progress && fm.progress.completed_phases}`,
);
assert.equal(
Number(fm.progress && fm.progress.percent),
67,
`percent must be 67%, got: ${fm.progress && fm.progress.percent}`,
);
const phaseLine = stateExtractField(state, 'Phase');
const { phase: bodyPhase } = parsePhaseFromProse(phaseLine);
const hasPhase6 = Number(bodyPhase) === 6 || Number(fm.current_phase) === 6;
assert.ok(
hasPhase6,
`STATE.md must reference Phase 6 as current after completing Phase 5. body Phase line: ${phaseLine}, frontmatter current_phase: ${fm.current_phase}`,
);
});
});
}

View File

@@ -148,7 +148,7 @@ describe('reference files contain key content from original mode sections', () =
__foldDescribe("folded:bug-3320-planner-deep-work-rules (consolidation epic #1969 B4 #1973)", () => {
'use strict';
// allow-test-rule: source-text-is-product [#3320]
// allow-test-rule: source-text-is-the-product [#3320]
// The bug is a contradiction in prompt/workflow source text. These assertions
// intentionally pin the contract words that planner agents consume.

View File

@@ -1,4 +1,4 @@
// allow-test-rule: runtime-contract-is-the-product — asserts GSD workflow/template markdown prose, the executable contract (#138, #2117)
// allow-test-rule: source-text-is-the-product — asserts GSD workflow/template markdown prose, the executable contract (#138, #2117)
'use strict';
// Policy regression test for issue #138:

View File

@@ -1,4 +1,4 @@
// allow-test-rule: runtime-contract-is-the-product (see #644) — the rendered reference doc's worked-example
// allow-test-rule: source-text-is-the-product (see #644) — the rendered reference doc's worked-example
// vocab surface is the runtime contract; this pins its bijection to the source-of-truth fixtures (docs-parity).
//
// RED-first parity contract: the portable reference doc (gsd-core/references/prohibition-probe.md) keeps

View File

@@ -1,4 +1,4 @@
// allow-test-rule: runtime-contract-is-the-product (see #644) — plan-phase.md's planner prompt is the deployed
// 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

View File

@@ -1,4 +1,4 @@
// allow-test-rule: runtime-contract-is-the-product (see #644) — the must_haves.prohibitions: block is the
// allow-test-rule: source-text-is-the-product (see #644) — the must_haves.prohibitions: block is the
// runtime plan-contract surface; this pins its parse/round-trip/projection bijection to the code.
//
// RED-first schema contract for the `must_haves.prohibitions:` SIBLING block (ADR-550 Decision 3 —

View File

@@ -1,4 +1,4 @@
// allow-test-rule: runtime-contract-is-the-product (see #644) — spec-phase.md Step 5.6 is the deployed workflow
// allow-test-rule: source-text-is-the-product (see #644) — spec-phase.md Step 5.6 is the deployed workflow
// runtime contract under assertion (the workflow PROSE is the product; ADR-550 D5 forbids a JS engine here).
//
// RED-first PROSE-PRESENCE contract for the prohibition probe's Step 5.6 (ADR-550 D1 DIVERGENCE,

View File

@@ -1,4 +1,4 @@
// allow-test-rule: runtime-contract-is-the-product (see #644) — the prohibition validators and the verify-time
// allow-test-rule: source-text-is-the-product (see #644) — the prohibition validators and the verify-time
// disposition are the deployed safety contract; this pins them against the CANONICAL fixture corpus
// and the ADR-550 D4 "judgment is never silently green" invariant so the code can never drift from
// its own documented intent again.

View File

@@ -1,4 +1,4 @@
// allow-test-rule: runtime-contract-is-the-product (see #644) — the verify-time disposition of a test-tier
// allow-test-rule: source-text-is-the-product (see #644) — the verify-time disposition of a test-tier
// prohibition is the deployed safety contract; this pins its fail-closed default to the code.
//
// RED-first SAFETY HALF of ADR-550 Decision 5(d) [maintainer decision 2026-06-12 "B-with-guard"].

View File

@@ -89,7 +89,8 @@ describe('bug #1529: getProjectInstructionFile ↔ gsd-tools query parity', () =
});
describe('bug #1529: new-project.md workflow uses the shared policy query', () => {
// allow-test-rule: structural drift guard for #1529 — the workflow's bash block MUST invoke the
// allow-test-rule: structural-regression-guard (#1529)
// the workflow's bash block MUST invoke the
// shared `gsd_run query project-instruction-file` query rather than a hardcoded
// codex-only `if/else` branch; there is no typed IR for "this bash block calls a
// specific gsd-tools query instead of a hardcoded mapping".

View File

@@ -1,12 +1,14 @@
'use strict';
// allow-test-rule: prompt-content-is-the-product
// The prompt-budget CLI writes an assembled, trimmed prompt string to disk.
// Testing that the prompt omits a dropped section (research) requires a
// content assertion on the output file — the file content IS the product.
// Structured metadata (omitted[], hardFailed, etc.) is always the primary
// assertion; text content checks are secondary and only used to verify the
// trim policy was applied correctly to the assembled output.
// allow-test-rule: pending-migration-to-typed-ir [#3090]
// The prompt-budget CLI writes an assembled, trimmed prompt string to disk —
// a computed/assembled output, not shipped source text, so this is not
// source-text-is-the-product (same mistake as the STATE.md-as-deployed-
// artifact miscategorization). Structured metadata (omitted[], hardFailed,
// etc.) is the primary assertion; the secondary raw substring check that the
// dropped research content is actually absent from the assembled prompt has
// no typed IR to convert to — prompt text is unstructured by nature.
// Tracked under #3090.
/**
* prompt-budget-cli.test.cjs

View File

@@ -1,5 +1,8 @@
'use strict';
// allow-test-rule: structural guard-placement verification in bin/install.js requires source-text analysis; install.js is a non-exportable CLI script and the guard must be in a specific lexical scope which require()+behavior cannot verify #1188
// allow-test-rule: structural-regression-guard (#1188)
// Guard-placement verification in bin/install.js requires source-text
// analysis; install.js is a non-exportable CLI script and the guard must be
// in a specific lexical scope which require()+behavior cannot verify.
/**
* Governance tests for the gsd-core repository root layout.

View File

@@ -1,4 +1,4 @@
// allow-test-rule: <runtime-contract-is-the-product> research agent .md content is the governed surface
// allow-test-rule: source-text-is-the-product research agent .md content is the governed surface
// The 7 researcher agent .md files are the deployed AI agent definitions — their
// frontmatter and @-includes ARE what the runtime loads. Asserting on their content
// is asserting on the deployed contract, not the test author's source code.

View File

@@ -1,8 +1,3 @@
// allow-test-rule: behavioral-query-coverage — see #2505 — this test exercises the
// resolve-dispatch-type query end-to-end (subprocess) AND the pure
// resolveDispatchType function (require), covering the runtime-aware dispatch
// contract from epic #2505 Phase 4 (#2508). The query is the workflow-facing
// surface; the function is the pure projection.
process.env.GSD_TEST_MODE = '1';
const { test, describe } = require('node:test');

View File

@@ -1529,7 +1529,9 @@ describe('bug #730 — milestone (Phase Details) section scope resolution', () =
const { describe: __foldDescribe } = require('node:test');
__foldDescribe("folded:bug-3128-roadmap-plan-count-slug-layout (consolidation epic #1969 B3 #1972)", () => {
'use strict';
// allow-test-rule: reads roadmap.cjs source to verify isPlanFile pattern was adopted — structural contract prevents silent regression to old filter (see #3128)
// allow-test-rule: structural-regression-guard (see #3128)
// Reads roadmap.cjs source to verify isPlanFile pattern was adopted —
// structural contract prevents silent regression to the old filter.
// Regression guard for bug #3128.
//

View File

@@ -1,4 +1,4 @@
// allow-test-rule: runtime-contract-is-the-product agent .md instruction surface see #1205
// allow-test-rule: source-text-is-the-product agent .md instruction surface see #1205
// agents/gsd-roadmapper.md is the deployed agent — the Granularity Calibration table
// AND the phase_id_convention instructions ARE the deployed behavior. Asserting on
// their prose asserts what runs in production (#163, #1205).

View File

@@ -1,7 +1,10 @@
// allow-test-rule: run-tests.cjs is a CLI test harness whose only IR is its
// stable stderr line `run-tests: suite="X" files=N: name1 name2 ...` plus its
// exit code. No typed IR is exposable from a shell script; the printed line
// IS the contract this test pins. See docs/TESTING-SUITES.md and issue #3597.
// allow-test-rule: pending-migration-to-typed-ir [#3090]
// run-tests.cjs is a CLI test harness with no --json/structured output mode;
// these tests regex/substring-match its human-readable stderr (usage errors,
// the `run-tests: suite="X" files=N: name1 name2 ...` selection line) instead
// of a frozen typed IR. Adding a structured output mode is a production
// change out of scope here. See docs/TESTING-SUITES.md and issue #3597.
// Tracked under #3090.
//
// Tests for scripts/run-tests.cjs --suite filtering (issue #3597).
//

View File

@@ -973,7 +973,11 @@ test('claude runtime does NOT rewrite the runtime default — stamping is non-cl
// ---------------------------------------------------------------------------
test('regression: every edited workflow gets codex-stamped (source↔engine parity, all surfaces) (#1515)', () => {
// allow-test-rule: emitted workflow runtime-resolution shell block is the runtime contract surface (#1515) — asserts on engine-transformed output of the real source
// allow-test-rule: pending-migration-to-typed-ir [#3090]
// `out` is _applyRuntimeRewrites's engine-transformed shell text, not shipped
// source — substring-matching it is the "Rendered file" row CONTRIBUTING
// requires a typed-IR builder for. No such IR exists yet for the shell
// rewrite output; production change out of scope here. Tracked under #3090.
const WORKFLOWS = ['execute-phase.md', 'autonomous.md', 'manager.md', 'diagnose-issues.md', 'quick.md'];
const CLAUDE_RUNTIME = 'config-get runtime --default claude --raw 2>/dev/null || echo "claude"';
const CODEX_RUNTIME = 'config-get runtime --default codex --raw 2>/dev/null || echo "codex"';
@@ -1065,7 +1069,11 @@ const FALSE_WT_LINE = 'config-get workflow.use_worktrees --default false --raw 2
// ---------------------------------------------------------------------------
test('parity: every non-Claude runtime stamps its own runtime default and use_worktrees=false on all workflows (#1521)', () => {
// allow-test-rule: emitted workflow runtime-resolution shell block is the runtime contract surface (#1521)
// allow-test-rule: pending-migration-to-typed-ir [#3090]
// `out` is _applyRuntimeRewrites's engine-transformed shell text, not shipped
// source — substring-matching it is the "Rendered file" row CONTRIBUTING
// requires a typed-IR builder for. No such IR exists yet for the shell
// rewrite output; production change out of scope here. Tracked under #3090.
for (const rt of NON_CLAUDE) {
for (const wf of WORKFLOWS) {
const src = fs.readFileSync(
@@ -1181,7 +1189,10 @@ test('property: _stampNonClaudeRuntimeDefaults is idempotent (#1521)', () => {
// ---------------------------------------------------------------------------
test('execute-phase.md, quick.md, and diagnose-issues.md guards are generalized to != "claude" (not Codex-specific) (#1521)', () => {
// allow-test-rule: emitted workflow runtime-resolution shell block is the runtime contract surface (#1521)
// allow-test-rule: source-text-is-the-product (#1521)
// Reads the raw shipped workflow .md source directly (not engine-transformed
// output) and asserts on its literal guard-clause text — the deployed prose
// IS the runtime contract here.
// #2584 Phase 3 (#2627): execute-phase.md graduated PAST the `!= "claude"`
// guard — worktree isolation there is now keyed on the negotiated
// `dispatch.isolation` capability, so no runtime name appears in its guard at
@@ -1226,7 +1237,9 @@ test('execute-phase.md, quick.md, and diagnose-issues.md guards are generalized
// ---------------------------------------------------------------------------
test('manager.md and autonomous.md gate run_in_background on FLATTEN=false, not a runtime name (#1521, graduated by #1708)', () => {
// allow-test-rule: orchestration dispatch gating in manager/autonomous .md is the runtime contract surface (#1521/#1708)
// allow-test-rule: source-text-is-the-product (#1521/#1708)
// Reads the raw shipped manager.md/autonomous.md workflow prose directly —
// the deployed orchestration dispatch gating text IS the runtime contract.
const manager = fs.readFileSync(
path.join(__dirname, '..', 'gsd-core', 'workflows', 'manager.md'),
'utf8',
@@ -1263,7 +1276,9 @@ test('manager.md and autonomous.md gate run_in_background on FLATTEN=false, not
});
test('manager.md and autonomous.md no longer contain old "not claude" background-dispatch gating (#1521)', () => {
// allow-test-rule: orchestration dispatch gating in manager/autonomous .md is the runtime contract surface (#1521)
// allow-test-rule: source-text-is-the-product (#1521)
// Reads the raw shipped manager.md/autonomous.md workflow prose directly —
// the deployed orchestration dispatch gating text IS the runtime contract.
const manager = fs.readFileSync(
path.join(__dirname, '..', 'gsd-core', 'workflows', 'manager.md'),
'utf8',

View File

@@ -881,7 +881,9 @@ describe('descriptor-driven parity: 13 non-probe registry runtimes × no-env-var
const { describe: __foldDescribe } = require('node:test');
__foldDescribe("folded:bug-3126-global-skills-base-runtime-path (consolidation epic #1969 B3 #1972)", () => {
'use strict';
// allow-test-rule: last three tests read init.cjs source to verify delegation contract to runtime-homes.cjs — structural guard, no behavioral IR exposed (see #3126)
// allow-test-rule: structural-implementation-guard (see #3126)
// Last three tests read init.cjs source to verify delegation contract to
// runtime-homes.cjs — no behavioral IR exposed yet for this wiring point.
// Regression guard for bug #3126.
//

View File

@@ -21,7 +21,8 @@
* can satisfy gsd_run for Codex shim-only installs.
*/
// allow-test-rule: structural parity/drift guard — asserts literal presence/absence of the canonical gsd_run launcher and the retired $GSD_SDK / `/gsd-tools` tokens across workflow markdown; there is no typed IR for "this source file does not contain substring X".
// allow-test-rule: structural-regression-guard
// structural parity/drift guard — asserts literal presence/absence of the canonical gsd_run launcher and the retired $GSD_SDK / `/gsd-tools` tokens across workflow markdown; there is no typed IR for "this source file does not contain substring X".
const { describe, test } = require('node:test');
const assert = require('node:assert/strict');
@@ -790,7 +791,8 @@ describe('runtime-launcher-parity — agents (#1041)', () => {
* When all three miss, exit non-zero.
*/
// allow-test-rule: structural/behavioral regression for the ~/.claude fallback arm in (see #211)
// allow-test-rule: structural-regression-guard (see #211)
// structural/behavioral regression for the ~/.claude fallback arm in
// the gsd_run launcher snippet -- asserts literal substring presence and exercises the
// bash resolution path via execFileSync; there is no typed IR for "snippet contains arm X".
@@ -998,7 +1000,8 @@ describe('bug-211: launcher ~/.claude home fallback', () => {
* (sync-runtime-launcher.cjs was re-run after editing the snippet).
*/
// allow-test-rule: structural/behavioral regression for non-Claude runtime-home (see #891)
// allow-test-rule: structural-regression-guard (see #891)
// structural/behavioral regression for non-Claude runtime-home
// fallback arms in the gsd_run launcher snippet -- asserts literal substring
// presence for each runtime-home probe and exercises the bash resolution paths
// via execFileSync; there is no typed IR for "snippet contains arm X".
@@ -1606,7 +1609,8 @@ describe('bug-3668: workflow SDK resolver supports installed user projects', { s
* (C) Precedence: repo-local .claude/ wins over $HOME/.claude/ when both exist.
*/
// allow-test-rule: structural/behavioral regression for the repo-local .claude/ install (see #444)
// allow-test-rule: structural-regression-guard (see #444)
// structural/behavioral regression for the repo-local .claude/ install
// arm in the gsd_run launcher snippet -- asserts literal substring presence and exercises
// the bash resolution path via execFileSync; there is no typed IR for "snippet contains arm X".

View File

@@ -54,7 +54,7 @@ describe('runtime-name-policy windsurf alias parity — manifest vs FALLBACK_ALI
test('manifest and FALLBACK_ALIASES windsurf alias sets are identical', () => {
// Read FALLBACK_ALIASES from source to detect manual drift before a build.
const srcPath = path.join(ROOT, 'src', 'runtime-name-policy.cts');
// allow-test-rule: runtime-contract-is-the-product — FALLBACK_ALIASES source text IS the
// allow-test-rule: source-text-is-the-product — FALLBACK_ALIASES source text IS the
// product contract for runtimes that can't load the manifest at runtime; verifying
// both surfaces contain the same windsurf aliases catches manual-mirror drift.
const src = fs.readFileSync(srcPath, 'utf8');

View File

@@ -634,7 +634,7 @@ describe('SECURE: threat-model-anchored behaviour', () => {
});
// ─── 8. Regression: security config variables resolved before use (#1625) ────
// allow-test-rule: runtime-contract-is-the-product — secure-phase.md prose is the executed contract (#1625)
// allow-test-rule: source-text-is-the-product — secure-phase.md prose is the executed contract (#1625)
describe('SECURE: security config variables resolved before use (#1625)', () => {
const wfPath = path.join(WORKFLOWS_DIR, 'secure-phase.md');
@@ -718,7 +718,9 @@ describe('SECURE: security config variables resolved before use (#1625)', () =>
const { describe: __foldDescribe } = require('node:test');
__foldDescribe("folded:bug-3120-secure-phase-empty-register (consolidation epic #1969 B4 #1973)", () => {
'use strict';
// allow-test-rule: reads product workflow markdown (secure-phase.md) to verify structural guard contract — not a source-grep test (see #3120)
// allow-test-rule: source-text-is-the-product (see #3120)
// Reads product workflow markdown (secure-phase.md) to verify structural
// guard contract.
// Regression guard for bug #3120.
//

View File

@@ -6918,10 +6918,6 @@ describe('bug #3454: state mutation must preserve literal $N amounts', () => {
__foldDescribe("folded:bug-3489-complete-phase-idempotent (consolidation epic #1969 B2 #1971)", () => {
'use strict';
// allow-test-rule: source-text-is-the-product (see #3489)
// State.md is the deployed artifact; asserting on its literal text content
// tests the deployed contract.
/**
* Regression test for #3489
*
@@ -6941,6 +6937,7 @@ const assert = require('node:assert/strict');
const fs = require('node:fs');
const path = require('node:path');
const { createTempProject, cleanup, runGsdTools } = require('./helpers.cjs');
const { stateExtractField } = require('../gsd-core/bin/lib/state-document.cjs');
describe('bug #3489: state complete-phase must be idempotent', () => {
let tmpDir;
@@ -7030,8 +7027,9 @@ describe('bug #3489: state complete-phase must be idempotent', () => {
assert.ok(result.success, `command failed: ${result.error || result.output}`);
const after = fs.readFileSync(statePath, 'utf8');
assert.ok(
after.includes('**Status:** Phase 03 complete'),
assert.equal(
stateExtractField(after, 'Status'),
'Phase 03 complete',
`expected Status updated to "Phase 03 complete", got:\n${after}`,
);
@@ -7051,7 +7049,6 @@ describe('bug #3489: state complete-phase must be idempotent', () => {
const { describe: __foldDescribe } = require('node:test');
__foldDescribe("folded:bug-397-state-preserve-executor-authored (consolidation epic #1969 B2 #1971)", () => {
'use strict';
// allow-test-rule: reads runtime STATE.md written to temp dir — behavioral output test, not source-grep (see #397)
// Regression tests for bug #397.
//
@@ -7086,6 +7083,8 @@ const fs = require('node:fs');
const os = require('node:os');
const path = require('node:path');
const { cleanup } = require('./helpers.cjs');
const { stateExtractField } = require('../gsd-core/bin/lib/state-document.cjs');
const { collectSection } = require('../gsd-core/bin/lib/markdown-sectionizer.cjs');
const ROOT = path.join(__dirname, '..');
const TOOLS_PATH = path.join(ROOT, 'gsd-core', 'bin', 'gsd-tools.cjs');
@@ -7279,12 +7278,12 @@ describe('bug #397: executor-authored STATE.md fields must be preserved', () =>
const r = runGsdState(['record-session', '--stopped-at', 'Plan 1 complete'], dir);
assert.ok(r.success, `record-session failed: ${r.error}`);
const after = readState(dir);
const rfMatch = after.match(/Resume File:\s*(.+)/i);
assert.ok(rfMatch, 'Resume File field not found in STATE.md after record-session');
const resumeFile = stateExtractField(after, 'Resume File');
assert.ok(resumeFile, 'Resume File field not found in STATE.md after record-session');
assert.strictEqual(
rfMatch[1].trim(),
resumeFile,
'/home/user/my-custom-context.md',
`record-session overwrote executor-authored Resume File with '${rfMatch[1].trim()}'`,
`record-session overwrote executor-authored Resume File with '${resumeFile}'`,
);
} finally {
cleanup(dir);
@@ -7298,12 +7297,12 @@ describe('bug #397: executor-authored STATE.md fields must be preserved', () =>
const r = runGsdState(['record-session', '--stopped-at', 'Plan complete'], dir);
assert.ok(r.success, `record-session failed: ${r.error}`);
const after = readState(dir);
const rfMatch = after.match(/Resume File:\s*(.+)/i);
assert.ok(rfMatch, 'Resume File field not found in STATE.md after record-session');
const resumeFile = stateExtractField(after, 'Resume File');
assert.ok(resumeFile, 'Resume File field not found in STATE.md after record-session');
assert.strictEqual(
rfMatch[1].trim(),
resumeFile,
'None',
`Expected 'None' to remain when it was already 'None', got: ${rfMatch[1].trim()}`,
`Expected 'None' to remain when it was already 'None', got: ${resumeFile}`,
);
} finally {
cleanup(dir);
@@ -7317,12 +7316,12 @@ describe('bug #397: executor-authored STATE.md fields must be preserved', () =>
const r = runGsdState(['record-session', '--resume-file', '/tmp/new-resume.md'], dir);
assert.ok(r.success, `record-session failed: ${r.error}`);
const after = readState(dir);
const rfMatch = after.match(/Resume File:\s*(.+)/i);
assert.ok(rfMatch, 'Resume File field not found in STATE.md after record-session');
const resumeFile = stateExtractField(after, 'Resume File');
assert.ok(resumeFile, 'Resume File field not found in STATE.md after record-session');
assert.strictEqual(
rfMatch[1].trim(),
resumeFile,
'/tmp/new-resume.md',
`Expected explicit --resume-file value to be written, got: ${rfMatch[1].trim()}`,
`Expected explicit --resume-file value to be written, got: ${resumeFile}`,
);
} finally {
cleanup(dir);
@@ -7338,12 +7337,12 @@ describe('bug #397: executor-authored STATE.md fields must be preserved', () =>
assert.ok(r.success, `advance-plan failed: ${r.error}`);
const after = readState(dir);
// The Configuration-level Status must not be clobbered
const statusMatch = after.match(/^Status:\s*(.+)/m);
assert.ok(statusMatch, 'Status field not found after advance-plan');
const status = stateExtractField(after, 'Status');
assert.ok(status, 'Status field not found after advance-plan');
assert.strictEqual(
statusMatch[1].trim(),
status,
'Awaiting QA sign-off before proceeding',
`advance-plan overwrote executor-authored Status: got '${statusMatch[1].trim()}'`,
`advance-plan overwrote executor-authored Status: got '${status}'`,
);
} finally {
cleanup(dir);
@@ -7358,17 +7357,17 @@ describe('bug #397: executor-authored STATE.md fields must be preserved', () =>
const r = runGsdState(['advance-plan'], dir);
assert.ok(r.success, `advance-plan failed: ${r.error}`);
const after = readState(dir);
const statusMatch = after.match(/^Status:\s*(.+)/m);
assert.ok(statusMatch, 'Status field not found after advance-plan');
const status = stateExtractField(after, 'Status');
assert.ok(status, 'Status field not found after advance-plan');
// 'Ready to execute' is a known default and should be replaced
assert.notStrictEqual(
statusMatch[1].trim(),
status,
'Ready to execute',
`Status should have been updated from 'Ready to execute' after phase-complete, but was not`,
);
assert.ok(
statusMatch[1].includes('Phase complete') || statusMatch[1].includes('ready for verification'),
`Expected phase-complete Status text, got: '${statusMatch[1].trim()}'`,
status.includes('Phase complete') || status.includes('ready for verification'),
`Expected phase-complete Status text, got: '${status}'`,
);
} finally {
cleanup(dir);
@@ -7384,12 +7383,12 @@ describe('bug #397: executor-authored STATE.md fields must be preserved', () =>
assert.ok(r.success, `advance-plan failed: ${r.error}`);
const after = readState(dir);
// The top-level Last Activity (in Configuration section) must be preserved
const laMatch = after.match(/^Last Activity:\s*(.+)/im);
assert.ok(laMatch, 'Last Activity field not found after advance-plan');
const lastActivity = stateExtractField(after, 'Last Activity');
assert.ok(lastActivity, 'Last Activity field not found after advance-plan');
assert.strictEqual(
laMatch[1].trim(),
lastActivity,
'Unblocked after infra fix — merged PR #88 manually',
`advance-plan overwrote executor-authored Last Activity: got '${laMatch[1].trim()}'`,
`advance-plan overwrote executor-authored Last Activity: got '${lastActivity}'`,
);
} finally {
cleanup(dir);
@@ -7404,21 +7403,21 @@ describe('bug #397: executor-authored STATE.md fields must be preserved', () =>
const r = runGsdState(['advance-plan'], dir);
assert.ok(r.success, `advance-plan failed: ${r.error}`);
const after = readState(dir);
const posMatch = after.match(/##\s*Current Position\s*\n([\s\S]*?)(?=\n##|$)/i);
assert.ok(posMatch, 'Current Position section not found after advance-plan');
const posBody = posMatch[1];
const posStatusMatch = posBody.match(/^Status:\s*(.+)/m);
assert.ok(posStatusMatch, 'Status field not found in Current Position section');
const section = collectSection(after, (h) => h.text.trim() === 'Current Position');
assert.ok(section, 'Current Position section not found after advance-plan');
const posBody = section.body;
const posStatus = stateExtractField(posBody, 'Status');
assert.ok(posStatus, 'Status field not found in Current Position section');
assert.strictEqual(
posStatusMatch[1].trim(),
posStatus,
'On hold — waiting for upstream dependency merge',
`advance-plan overwrote executor-authored Current Position Status: got '${posStatusMatch[1].trim()}'`,
`advance-plan overwrote executor-authored Current Position Status: got '${posStatus}'`,
);
const posActivityMatch = posBody.match(/^Last activity:\s*(.+)/im);
assert.ok(posActivityMatch, 'Last activity field not found in Current Position section');
const posActivity = stateExtractField(posBody, 'Last activity');
assert.ok(posActivity, 'Last activity field not found in Current Position section');
assert.ok(
posActivityMatch[1].includes('blocked by infra'),
`advance-plan overwrote executor-authored Current Position Last activity: got '${posActivityMatch[1].trim()}'`,
posActivity.includes('blocked by infra'),
`advance-plan overwrote executor-authored Current Position Last activity: got '${posActivity}'`,
);
} finally {
cleanup(dir);
@@ -9978,7 +9977,6 @@ describe('buildStateFrontmatter cache invalidation (#1967)', () => {
const { describe: __foldDescribe } = require('node:test');
__foldDescribe("folded:bug-3127-state-begin-phase-idempotent (consolidation epic #1969 B3 #1972)", () => {
'use strict';
// allow-test-rule: reads runtime STATE.md written to temp dir — behavioral output test, not source-grep (see #3127)
// Regression tests for bug #3127.
//
@@ -10003,6 +10001,7 @@ const fs = require('node:fs');
const os = require('node:os');
const path = require('node:path');
const { cleanup } = require('./helpers.cjs');
const { stateExtractField } = require('../gsd-core/bin/lib/state-document.cjs');
const ROOT = path.join(__dirname, '..');
@@ -10079,9 +10078,9 @@ describe('bug #3127: state.begin-phase idempotency guard', () => {
cmdStateBeginPhase(dir, '5', 'test-phase', 8, false);
const after = fs.readFileSync(path.join(dir, '.planning', 'STATE.md'), 'utf8');
// Current Plan must not have been reset to 1
const planMatch = after.match(/^Current Plan:\s*(\S+)/m);
if (planMatch) {
assert.notStrictEqual(planMatch[1], '1',
const currentPlan = stateExtractField(after, 'Current Plan');
if (currentPlan !== null) {
assert.notStrictEqual(currentPlan, '1',
'begin-phase reset Current Plan to 1 on a mid-flight phase — idempotency guard not applied');
}
} finally {
@@ -10098,9 +10097,10 @@ describe('bug #3127: state.begin-phase idempotency guard', () => {
cmdStateBeginPhase(dir, '5', 'test-phase', 8, false);
const after = fs.readFileSync(path.join(dir, '.planning', 'STATE.md'), 'utf8');
// The rich stopped_at narrative must be preserved
const stoppedAt = stateExtractField(after, 'stopped_at');
assert.ok(
after.includes('Plan 02 SHIPPED') || after.includes('Wave 2 GREEN'),
'begin-phase overwrote stopped_at narrative on a mid-flight phase',
stoppedAt && (stoppedAt.includes('Plan 02 SHIPPED') || stoppedAt.includes('Wave 2 GREEN')),
`begin-phase overwrote stopped_at narrative on a mid-flight phase; got: ${stoppedAt}`,
);
} finally {
cleanup(dir);
@@ -10116,9 +10116,9 @@ describe('bug #3127: state.begin-phase idempotency guard', () => {
cmdStateBeginPhase(dir, '5', 'test-phase', 8, false);
const after = fs.readFileSync(path.join(dir, '.planning', 'STATE.md'), 'utf8');
// Normal path: Current Plan should become 1 (or stay 1)
const planMatch = after.match(/^Current Plan:\s*(\S+)/m);
if (planMatch) {
assert.strictEqual(planMatch[1], '1',
const currentPlan = stateExtractField(after, 'Current Plan');
if (currentPlan !== null) {
assert.strictEqual(currentPlan, '1',
'begin-phase should set Current Plan to 1 on a fresh phase');
}
} finally {
@@ -10142,9 +10142,10 @@ describe('bug #3127: state.begin-phase idempotency guard', () => {
try {
cmdStateBeginPhase(dir, '5', 'test-phase', 8, false);
const after = fs.readFileSync(path.join(dir, '.planning', 'STATE.md'), 'utf8');
const lastActivity = stateExtractField(after, 'Last activity');
assert.ok(
after.includes(PINNED_DATE),
`begin-phase must update Last Activity date to the pinned date ${PINNED_DATE} even on resume (safe field)`,
lastActivity && lastActivity.includes(PINNED_DATE),
`begin-phase must update Last Activity date to the pinned date ${PINNED_DATE} even on resume (safe field); got: ${lastActivity}`,
);
} finally {
// Restore env vars before cleanup to avoid leaking state to other tests.

View File

@@ -1,4 +1,4 @@
// allow-test-rule: runtime-contract-is-the-product (see #1867) — the rendered reference doc's taxonomy table IS the runtime contract; this pins its bijection to the code (docs-parity, ADR-456 exception matrix)
// allow-test-rule: source-text-is-the-product (see #1867) — the rendered reference doc's taxonomy table IS the runtime contract; this pins its bijection to the code (docs-parity, ADR-456 exception matrix)
// Asserts gsd-core/references/ui-consideration-probe.md keeps its taxonomy id column in
// sync with the source-of-truth UI_TAXONOMY (built .cjs), and that the closed compiled
// taxonomy stays DISJOINT from the open-prose domain-probes.md bank (the mixed-axis boundary,

View File

@@ -1742,14 +1742,14 @@ describe('bug-967 verify key-links strict file-path contract', () => {
// This test reads the canonical docs file and asserts the example is consistent
// with the strict-path contract.
//
// allow-test-rule: <runtime-contract-is-the-product> the plan-md.md reference (see #967)
// allow-test-rule: source-text-is-the-product the plan-md.md reference (see #967)
// example IS the documented authoring surface for key_links; asserting it uses
// a file path (not an endpoint) directly tests the documented contract.
test('docs/reference/plan-md.md key_links example uses a relative file path for to:, not an HTTP endpoint', () => {
// Locate plan-md.md relative to this test file's repo root
const docPath = path.join(__dirname, '..', 'docs', 'reference', 'plan-md.md');
assert.ok(fs.existsSync(docPath), `plan-md.md not found at ${docPath}`);
const content = fs.readFileSync(docPath, 'utf-8'); // allow-test-rule: <runtime-contract-is-the-product> the plan-md.md reference example IS the documented authoring surface for key_links; asserting it uses a file path (not an endpoint) directly tests the documented contract. (see #967)
const content = fs.readFileSync(docPath, 'utf-8'); // allow-test-rule: source-text-is-the-product the plan-md.md reference example IS the documented authoring surface for key_links; asserting it uses a file path (not an endpoint) directly tests the documented contract. (see #967)
// Find the key_links block in the annotated example (the first YAML frontmatter fence)
// The bad old value was: to: "/api/feed"

View File

@@ -56,8 +56,10 @@ function listWorkflowFiles() {
.map((e) => path.join(WORKFLOWS_DIR, e));
// Filter to files that have at least one Windows hosted-runner reference.
// allow-test-rule: file-scope prefilter, not a test assertion — we need to
// detect whether a workflow file targets Windows runners at all. The pwsh
// allow-test-rule: source-text-is-the-product
// Reads the shipped .github/workflows/*.yml — the deployed text GitHub
// Actions executes — to detect whether a workflow targets Windows runners
// at all. The pwsh
// stderr-swallow class is windows-only, so files that never mention
// windows-hosted labels are out of scope. Exposing a typed IR from production
// code is not appropriate here because the source-of-truth is the YAML

View File

@@ -4299,7 +4299,12 @@ describe('bug-3707: reapOrphanWorktrees — adversarial edge cases', () => {
const { describe: __foldDescribe } = require('node:test');
__foldDescribe("folded:bug-3129-validate-commit-git-bypass (consolidation epic #1969 B5 #1974)", () => {
'use strict';
// allow-test-rule: reads hook shell script to verify delegation pattern — structural contract test, not source-grep (see #3129)
// allow-test-rule: structural-regression-guard (see #3129)
// Reads the gsd-validate-commit.sh hook source to verify it delegates to
// git-cmd.js isGitSubcommand() rather than the old regex — a specific code
// pattern that must (and must not) exist; behavioral tests of tokenize()/
// isGitSubcommand() cannot observe which detection strategy the hook itself
// calls.
// Regression tests for bug #3129.
//