cleargate 0.14.0 → 0.15.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (149) hide show
  1. package/CHANGELOG.md +16 -0
  2. package/dist/MANIFEST.json +71 -15
  3. package/dist/admin-api/index.cjs +0 -1
  4. package/dist/admin-api/index.js +1 -2
  5. package/dist/auth/factory.cjs +0 -1
  6. package/dist/auth/factory.js +2 -3
  7. package/dist/auth/require-token.cjs +0 -1
  8. package/dist/auth/require-token.js +1 -2
  9. package/dist/auth/token-store.cjs +0 -1
  10. package/dist/auth/token-store.js +1 -2
  11. package/dist/{bootstrap-root-QKSA5V75.js → bootstrap-root-2H5HVTCC.js} +1 -2
  12. package/dist/{chunk-PDE37WFQ.js → chunk-A7MSQUU7.js} +2 -3
  13. package/dist/{chunk-BTSZOEWC.js → chunk-P6KEDAK2.js} +0 -1
  14. package/dist/{chunk-E3X7IE5E.js → chunk-PY6FHGV5.js} +1 -2
  15. package/dist/{chunk-5DI2Z3C2.js → chunk-Y53ZZYYU.js} +1 -2
  16. package/dist/cli.cjs +1564 -1414
  17. package/dist/cli.js +1514 -1364
  18. package/dist/lib/ledger.cjs +0 -1
  19. package/dist/lib/ledger.js +1 -2
  20. package/dist/lib/lifecycle-reconcile.cjs +0 -1
  21. package/dist/lib/lifecycle-reconcile.js +2 -3
  22. package/dist/{whoami-EANGN46Z.js → whoami-JKQQPABQ.js} +3 -4
  23. package/package.json +4 -3
  24. package/templates/cleargate-planning/.claude/agents/architect.md +4 -2
  25. package/templates/cleargate-planning/.claude/agents/developer.md +4 -11
  26. package/templates/cleargate-planning/.claude/agents/qa.md +14 -6
  27. package/templates/cleargate-planning/.claude/hooks/pending-task-sentinel.sh +2 -2
  28. package/templates/cleargate-planning/.claude/skills/sprint-execution/SKILL.md +19 -1
  29. package/templates/cleargate-planning/.cleargate/config.example.yml +16 -0
  30. package/templates/cleargate-planning/.cleargate/scripts/close_sprint.deferred-verify.red.node.test.ts +245 -0
  31. package/templates/cleargate-planning/.cleargate/scripts/close_sprint.mjs +227 -0
  32. package/templates/cleargate-planning/.cleargate/scripts/gate-checks.json +5 -4
  33. package/templates/cleargate-planning/.cleargate/scripts/init_sprint.mjs +75 -2
  34. package/templates/cleargate-planning/.cleargate/scripts/pre_gate_common.sh +48 -0
  35. package/templates/cleargate-planning/.cleargate/scripts/pre_gate_runner.sh +57 -1
  36. package/templates/cleargate-planning/.cleargate/scripts/provision_worktree_config.sh +155 -0
  37. package/templates/cleargate-planning/.cleargate/scripts/qa_red_lint.mjs +380 -0
  38. package/templates/cleargate-planning/.cleargate/scripts/run_script.sh +34 -1
  39. package/templates/cleargate-planning/.cleargate/scripts/test/cr077_eviction.red.sh +113 -0
  40. package/templates/cleargate-planning/.cleargate/scripts/test/cr078_init.test.sh +309 -0
  41. package/templates/cleargate-planning/.cleargate/scripts/test/cr079_provision.red.sh +262 -0
  42. package/templates/cleargate-planning/.cleargate/scripts/test/cr080_wrapper.test.sh +177 -0
  43. package/templates/cleargate-planning/.cleargate/scripts/test/cr081_qa_red_lint.red.sh +348 -0
  44. package/templates/cleargate-planning/.cleargate/sprint-runs/_off-sprint/.session-totals.json +1 -0
  45. package/templates/cleargate-planning/.cleargate/sprint-runs/_off-sprint/token-ledger.jsonl +27 -0
  46. package/templates/cleargate-planning/.cleargate/templates/sprint_context.md +17 -0
  47. package/templates/cleargate-planning/.cleargate/templates/story.md +1 -0
  48. package/templates/cleargate-planning/MANIFEST.json +71 -15
  49. package/dist/admin-api/index.cjs.map +0 -1
  50. package/dist/admin-api/index.js.map +0 -1
  51. package/dist/auth/factory.cjs.map +0 -1
  52. package/dist/auth/factory.js.map +0 -1
  53. package/dist/auth/require-token.cjs.map +0 -1
  54. package/dist/auth/require-token.js.map +0 -1
  55. package/dist/auth/token-store.cjs.map +0 -1
  56. package/dist/auth/token-store.js.map +0 -1
  57. package/dist/bootstrap-root-QKSA5V75.js.map +0 -1
  58. package/dist/chunk-5DI2Z3C2.js.map +0 -1
  59. package/dist/chunk-BTSZOEWC.js.map +0 -1
  60. package/dist/chunk-E3X7IE5E.js.map +0 -1
  61. package/dist/chunk-PDE37WFQ.js.map +0 -1
  62. package/dist/cli.cjs.map +0 -1
  63. package/dist/cli.js.map +0 -1
  64. package/dist/lib/ledger.cjs.map +0 -1
  65. package/dist/lib/ledger.js.map +0 -1
  66. package/dist/lib/lifecycle-reconcile.cjs.map +0 -1
  67. package/dist/lib/lifecycle-reconcile.js.map +0 -1
  68. package/dist/templates/cleargate-planning/.claude/agents/architect-reader.md +0 -61
  69. package/dist/templates/cleargate-planning/.claude/agents/architect-synth.md +0 -124
  70. package/dist/templates/cleargate-planning/.claude/agents/architect.md +0 -230
  71. package/dist/templates/cleargate-planning/.claude/agents/cleargate-wiki-contradict.md +0 -108
  72. package/dist/templates/cleargate-planning/.claude/agents/cleargate-wiki-ingest.md +0 -194
  73. package/dist/templates/cleargate-planning/.claude/agents/cleargate-wiki-lint.md +0 -261
  74. package/dist/templates/cleargate-planning/.claude/agents/cleargate-wiki-query.md +0 -143
  75. package/dist/templates/cleargate-planning/.claude/agents/developer.md +0 -185
  76. package/dist/templates/cleargate-planning/.claude/agents/devops.md +0 -257
  77. package/dist/templates/cleargate-planning/.claude/agents/qa.md +0 -171
  78. package/dist/templates/cleargate-planning/.claude/agents/reporter.md +0 -274
  79. package/dist/templates/cleargate-planning/.claude/hooks/pending-task-sentinel.sh +0 -209
  80. package/dist/templates/cleargate-planning/.claude/hooks/pre-commit-surface-gate.sh +0 -33
  81. package/dist/templates/cleargate-planning/.claude/hooks/pre-commit-test-ratchet.sh +0 -58
  82. package/dist/templates/cleargate-planning/.claude/hooks/pre-commit.sh +0 -19
  83. package/dist/templates/cleargate-planning/.claude/hooks/pre-edit-gate.sh +0 -162
  84. package/dist/templates/cleargate-planning/.claude/hooks/pre-tool-use-autonomy.sh +0 -58
  85. package/dist/templates/cleargate-planning/.claude/hooks/pre-tool-use-task.sh +0 -148
  86. package/dist/templates/cleargate-planning/.claude/hooks/session-start.sh +0 -75
  87. package/dist/templates/cleargate-planning/.claude/hooks/stamp-and-gate.sh +0 -43
  88. package/dist/templates/cleargate-planning/.claude/hooks/token-ledger.sh +0 -590
  89. package/dist/templates/cleargate-planning/.claude/settings.json +0 -68
  90. package/dist/templates/cleargate-planning/.claude/skills/flashcard/SKILL.md +0 -102
  91. package/dist/templates/cleargate-planning/.claude/skills/sprint-execution/SKILL.md +0 -742
  92. package/dist/templates/cleargate-planning/.cleargate/FLASHCARD.md +0 -7
  93. package/dist/templates/cleargate-planning/.cleargate/config.example.yml +0 -67
  94. package/dist/templates/cleargate-planning/.cleargate/config.yml +0 -18
  95. package/dist/templates/cleargate-planning/.cleargate/delivery/archive/.gitkeep +0 -0
  96. package/dist/templates/cleargate-planning/.cleargate/delivery/pending-sync/.gitkeep +0 -0
  97. package/dist/templates/cleargate-planning/.cleargate/knowledge/cleargate-enforcement.md +0 -551
  98. package/dist/templates/cleargate-planning/.cleargate/knowledge/cleargate-protocol.md +0 -878
  99. package/dist/templates/cleargate-planning/.cleargate/knowledge/mid-sprint-triage-rubric.md +0 -160
  100. package/dist/templates/cleargate-planning/.cleargate/knowledge/readiness-gates.md +0 -213
  101. package/dist/templates/cleargate-planning/.cleargate/knowledge/sprint-closeout-checklist.md +0 -71
  102. package/dist/templates/cleargate-planning/.cleargate/scripts/_migrate-schema-v3.mjs +0 -120
  103. package/dist/templates/cleargate-planning/.cleargate/scripts/assert_story_files.mjs +0 -265
  104. package/dist/templates/cleargate-planning/.cleargate/scripts/close_sprint.mjs +0 -1012
  105. package/dist/templates/cleargate-planning/.cleargate/scripts/collision_surface.sh +0 -114
  106. package/dist/templates/cleargate-planning/.cleargate/scripts/constants.mjs +0 -62
  107. package/dist/templates/cleargate-planning/.cleargate/scripts/dedupe_frontmatter.mjs +0 -219
  108. package/dist/templates/cleargate-planning/.cleargate/scripts/file_surface_diff.sh +0 -320
  109. package/dist/templates/cleargate-planning/.cleargate/scripts/gate-checks.json +0 -15
  110. package/dist/templates/cleargate-planning/.cleargate/scripts/init_gate_config.sh +0 -38
  111. package/dist/templates/cleargate-planning/.cleargate/scripts/init_sprint.mjs +0 -240
  112. package/dist/templates/cleargate-planning/.cleargate/scripts/launch_wave.mjs +0 -341
  113. package/dist/templates/cleargate-planning/.cleargate/scripts/lib/report-filename.mjs +0 -54
  114. package/dist/templates/cleargate-planning/.cleargate/scripts/pre_gate_common.sh +0 -206
  115. package/dist/templates/cleargate-planning/.cleargate/scripts/pre_gate_runner.sh +0 -371
  116. package/dist/templates/cleargate-planning/.cleargate/scripts/prefill_report.mjs +0 -280
  117. package/dist/templates/cleargate-planning/.cleargate/scripts/prep_doc_refresh.mjs +0 -378
  118. package/dist/templates/cleargate-planning/.cleargate/scripts/prep_qa_context.mjs +0 -888
  119. package/dist/templates/cleargate-planning/.cleargate/scripts/run_script.sh +0 -209
  120. package/dist/templates/cleargate-planning/.cleargate/scripts/sprint_trends.mjs +0 -71
  121. package/dist/templates/cleargate-planning/.cleargate/scripts/state.schema.json +0 -127
  122. package/dist/templates/cleargate-planning/.cleargate/scripts/suggest_improvements.mjs +0 -717
  123. package/dist/templates/cleargate-planning/.cleargate/scripts/surface-whitelist.txt +0 -27
  124. package/dist/templates/cleargate-planning/.cleargate/scripts/test/test_assert_story_files.sh +0 -261
  125. package/dist/templates/cleargate-planning/.cleargate/scripts/test/test_file_surface.sh +0 -210
  126. package/dist/templates/cleargate-planning/.cleargate/scripts/test/test_flashcard_gate.sh +0 -190
  127. package/dist/templates/cleargate-planning/.cleargate/scripts/test/test_prep_qa_context.sh +0 -482
  128. package/dist/templates/cleargate-planning/.cleargate/scripts/test/test_test_ratchet.sh +0 -327
  129. package/dist/templates/cleargate-planning/.cleargate/scripts/test_ratchet.mjs +0 -261
  130. package/dist/templates/cleargate-planning/.cleargate/scripts/update_state.mjs +0 -246
  131. package/dist/templates/cleargate-planning/.cleargate/scripts/validate_bounce_readiness.mjs +0 -111
  132. package/dist/templates/cleargate-planning/.cleargate/scripts/validate_state.mjs +0 -184
  133. package/dist/templates/cleargate-planning/.cleargate/scripts/write_dispatch.sh +0 -172
  134. package/dist/templates/cleargate-planning/.cleargate/templates/Bug.md +0 -126
  135. package/dist/templates/cleargate-planning/.cleargate/templates/CR.md +0 -130
  136. package/dist/templates/cleargate-planning/.cleargate/templates/Sprint Plan Template.md +0 -137
  137. package/dist/templates/cleargate-planning/.cleargate/templates/epic.md +0 -166
  138. package/dist/templates/cleargate-planning/.cleargate/templates/hotfix.md +0 -111
  139. package/dist/templates/cleargate-planning/.cleargate/templates/initiative.md +0 -122
  140. package/dist/templates/cleargate-planning/.cleargate/templates/sprint_context.md +0 -50
  141. package/dist/templates/cleargate-planning/.cleargate/templates/sprint_report.md +0 -224
  142. package/dist/templates/cleargate-planning/.cleargate/templates/story.md +0 -213
  143. package/dist/templates/cleargate-planning/CLAUDE.md +0 -66
  144. package/dist/templates/cleargate-planning/MANIFEST.json +0 -503
  145. package/dist/templates/synthesis/active-sprint.md +0 -30
  146. package/dist/templates/synthesis/open-gates.md +0 -38
  147. package/dist/templates/synthesis/product-state.md +0 -31
  148. package/dist/templates/synthesis/roadmap.md +0 -63
  149. package/dist/whoami-EANGN46Z.js.map +0 -1
@@ -117,4 +117,3 @@ function sumDeltas(rows) {
117
117
  0 && (module.exports = {
118
118
  sumDeltas
119
119
  });
120
- //# sourceMappingURL=ledger.cjs.map
@@ -3,7 +3,7 @@ import { createRequire as __cgCreateRequire } from "module";
3
3
  const require = __cgCreateRequire(import.meta.url);
4
4
  import {
5
5
  init_esm_shims
6
- } from "../chunk-BTSZOEWC.js";
6
+ } from "../chunk-P6KEDAK2.js";
7
7
 
8
8
  // src/lib/ledger.ts
9
9
  init_esm_shims();
@@ -99,4 +99,3 @@ function sumDeltas(rows) {
99
99
  export {
100
100
  sumDeltas
101
101
  };
102
- //# sourceMappingURL=ledger.js.map
@@ -888,4 +888,3 @@ function reconcileCurrentSprintStories(opts) {
888
888
  rollUpParentStatus,
889
889
  walkActiveParents
890
890
  });
891
- //# sourceMappingURL=lifecycle-reconcile.cjs.map
@@ -12,8 +12,8 @@ import {
12
12
  reconcileLifecycle,
13
13
  rollUpParentStatus,
14
14
  walkActiveParents
15
- } from "../chunk-5DI2Z3C2.js";
16
- import "../chunk-BTSZOEWC.js";
15
+ } from "../chunk-Y53ZZYYU.js";
16
+ import "../chunk-P6KEDAK2.js";
17
17
  export {
18
18
  ARTIFACT_TERMINAL_STATUSES,
19
19
  VERB_STATUS_MAP,
@@ -26,4 +26,3 @@ export {
26
26
  rollUpParentStatus,
27
27
  walkActiveParents
28
28
  };
29
- //# sourceMappingURL=lifecycle-reconcile.js.map
@@ -8,11 +8,11 @@ import {
8
8
  getMembershipState,
9
9
  loadConfig,
10
10
  requireMcpUrl
11
- } from "./chunk-PDE37WFQ.js";
12
- import "./chunk-E3X7IE5E.js";
11
+ } from "./chunk-A7MSQUU7.js";
12
+ import "./chunk-PY6FHGV5.js";
13
13
  import {
14
14
  init_esm_shims
15
- } from "./chunk-BTSZOEWC.js";
15
+ } from "./chunk-P6KEDAK2.js";
16
16
 
17
17
  // src/commands/whoami.ts
18
18
  init_esm_shims();
@@ -80,4 +80,3 @@ async function whoamiHandler(opts) {
80
80
  export {
81
81
  whoamiHandler
82
82
  };
83
- //# sourceMappingURL=whoami-EANGN46Z.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cleargate",
3
- "version": "0.14.0",
3
+ "version": "0.15.0",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "description": "Planning framework for Claude Code agents — sprint/epic/story protocol, five-role agent team (architect/developer/qa/devops/reporter), Karpathy-style awareness wiki.",
@@ -44,10 +44,11 @@
44
44
  },
45
45
  "scripts": {
46
46
  "prebuild": "tsx scripts/build-manifest.ts && node scripts/copy-planning-payload.mjs",
47
- "build": "tsup",
47
+ "build": "rm -rf dist && tsup",
48
48
  "dev": "tsup --watch",
49
49
  "typecheck": "tsc --noEmit",
50
- "test": "tsx --test --test-concurrency=1 --experimental-test-module-mocks --test-reporter=spec 'test/**/*.node.test.ts' '!test/fixtures/**'",
50
+ "test": "node scripts/run-default-tests.mjs '!test/**/*.integration.node.test.ts' '!test/fixtures/**'",
51
+ "test:integration": "tsx --test --test-concurrency=1 --experimental-test-module-mocks --test-reporter=spec 'test/**/*.integration.node.test.ts'",
51
52
  "test:file": "tsx --test --test-concurrency=1 --experimental-test-module-mocks --test-reporter=spec",
52
53
  "test:node": "tsx --test --test-concurrency=1 --experimental-test-module-mocks --test-reporter=spec 'test/**/*.node.test.ts' '!test/fixtures/**'",
53
54
  "test:node:file": "tsx --test --test-concurrency=1 --experimental-test-module-mocks --test-reporter=spec",
@@ -107,16 +107,18 @@ These rules apply under `execution_mode: v2`. Under v1 the Design Review is info
107
107
 
108
108
  ## Mode: TPV (Test Pattern Validation)
109
109
 
110
- Dispatched between QA-Red and Developer for standard-lane stories under v2 (fast lane skips). Dispatched only when the `pre_gate_runner.sh` wiring scan flags a problem — a clean scan (exit 0, no flags) skips the live TPV dispatch and proceeds directly to §C.4 Developer. You receive: story file, QA-Red commit SHA, list of `*.red.node.test.ts` files. You verify ONLY:
110
+ Dispatched between QA-Red and Developer for standard-lane stories under v2 (fast lane skips). Dispatched only when the `pre_gate_runner.sh` wiring scan flags a problem — a clean scan (exit 0, no flags) skips the live TPV dispatch and proceeds directly to §C.4 Developer. You receive: story file, QA-Red commit SHA, list of red-test files (named per `sprint_context.md` §Test Stack). You verify ONLY:
111
111
 
112
112
  1. All imports resolve to real modules at the cited paths.
113
113
  2. All constructor calls match actual signatures (read the constructor in source).
114
114
  3. All `t.mock.method()` calls reference methods that exist on the mocked object.
115
115
  4. Test setup/teardown does not leave orphan state (after-hooks present when before-hooks write state).
116
- 5. Test files end in `*.red.node.test.ts` (CR-043 immutability naming).
116
+ 5. Test files follow the red-test naming declared in `sprint_context.md` §Test Stack.
117
117
 
118
118
  You DO NOT verify test logic correctness — that is Dev's TDD challenge.
119
119
 
120
+ > **Note (CR-081):** Semantic fixture correctness (invalid enum literals, unsatisfiable duplicate-text queries) is caught by the separate `qa_red_lint` pre-gate step (CR-081), NOT by TPV. TPV stays wiring-only — never fold semantic-correctness checks here.
121
+
120
122
  Return:
121
123
  - `TPV: APPROVED` — Dev proceeds.
122
124
  - `TPV: BLOCKED-WIRING-GAP — <one-sentence specific issue>` — orchestrator routes back to QA-Red; `arch_bounces` increments via `node update_state.mjs <story-id> --arch-bounce`.
@@ -80,17 +80,11 @@ flashcards_flagged:
80
80
 
81
81
  ## Inner-loop test runner
82
82
 
83
- All tests use **`node:test` + `node:assert/strict`** this is the single, mandatory runner across all ClearGate packages (EPIC-028, 2026-05-18). vitest is fully eliminated; adding it back is forbidden and blocked by the `check:no-vitest` pre-commit guard.
83
+ Use the project's test runner and red-test naming as declared in `sprint_context.md` §Test Stack. Run the backend runner (and frontend runner if present) before committing.
84
84
 
85
- **File naming:** `*.node.test.ts` for all new test files.
85
+ **Mocking pattern:** prefer constructor-injected DI seams over module-level mocks. Inject the dependency via the constructor or function parameter and pass a fake in tests. For function-level mocks, use `mock.fn()` / `mock.method()`. For static-import un-interceptability (e.g. toast, clipboard), use the `__overrides__` pattern: a `__mocks__/` stub with a mutable `__overrides__` object that the test sets before each call.
86
86
 
87
- **Run commands per package:**
88
- - `mcp/` and `cleargate-cli/`: `tsx --test --test-concurrency=1 --experimental-test-module-mocks 'test/**/*.node.test.ts'`
89
- - `admin/`: `node --conditions browser --import tsx tests/run-tests.mjs` — the `--conditions browser` flag is required; it triggers jsdom-bootstrap via `setup-node-test.mjs` for Svelte component tests.
90
-
91
- **Mocking pattern:** prefer constructor-injected DI seams over module-level mocks. Inject the dependency via the constructor or function parameter and pass a fake in tests. For function-level mocks, use `mock.fn()` / `mock.method()` from `node:test`. For static-import un-interceptability in admin/ (e.g. toast, clipboard), use the `__overrides__` pattern: a `__mocks__/` stub with a mutable `__overrides__` object that the test sets before each call — see `admin/TESTING.md` for full pattern description.
92
-
93
- **Full-suite verification at commit-time.** Use the project's standard test command (`npm test`, etc.) before committing.
87
+ **Full-suite verification at commit-time.** Use the project's standard test command before committing.
94
88
 
95
89
  ## Script Invocation
96
90
 
@@ -123,8 +117,7 @@ These rules apply under `execution_mode: v2`. Under v1 they are informational.
123
117
 
124
118
  These files are **immutable** for Developer dispatches. Do not Read, Edit, Write, or stage them:
125
119
 
126
- - `**/*.red.test.ts` — QA-Red-authored test files (vitest naming, legacy)
127
- - `**/*.red.node.test.ts` — QA-Red-authored test files (node:test naming, SPRINT-22+)
120
+ - QA-Red-authored red-test files (naming per `sprint_context.md` §Test Stack)
128
121
 
129
122
  These files are written by the QA-Red dispatch (SKILL.md §C.3) and committed to the story branch before Developer spawns. The pre-commit hook (`pre-commit-surface-gate.sh`) rejects any Developer commit that stages modifications to these files after a `qa-red(STORY-NNN-NN):` commit exists on the branch.
130
123
 
@@ -40,16 +40,16 @@ Dispatch prompt contains: `Mode: RED — write failing tests against §4 accepta
40
40
 
41
41
  In RED mode you:
42
42
  1. Read the story's §4 acceptance Gherkin (and ONLY the story file — no implementation source files).
43
- 2. Write failing test files named `*.red.node.test.ts` covering each acceptance scenario.
43
+ 2. Write failing test files named as declared in `sprint_context.md` §Test Stack covering each acceptance scenario.
44
44
  3. Confirm each test FAILS against the clean baseline (no implementation yet).
45
45
  4. Return the `QA-RED:` output shape (see §C.3 in SKILL.md).
46
46
  5. **Forbidden:** Read, edit, or reference any implementation file (`.ts` source, not tests).
47
- 6. **Wiring soundness:** Tests must be wiring-sound for Architect TPV approval (SKILL.md §C.3.5). TPV checks: imports resolve, constructor signatures match, mocked methods exist, after-hooks present, file naming `*.red.node.test.ts`. Wiring gap → orchestrator routes back to QA-Red (increments `arch_bounces`, NOT `qa_bounces`).
47
+ 6. **Wiring soundness:** Tests must be wiring-sound for Architect TPV approval (SKILL.md §C.3.5). TPV checks: imports resolve, constructor signatures match, mocked methods exist, after-hooks present, file naming as declared in `sprint_context.md` §Test Stack. Wiring gap → orchestrator routes back to QA-Red (increments `arch_bounces`, NOT `qa_bounces`).
48
48
 
49
49
  Output shape for RED mode:
50
50
  ```
51
51
  QA-RED: WRITTEN | BLOCKED
52
- RED_TESTS: <list of *.red.node.test.ts files written>
52
+ RED_TESTS: <list of red-test files written (naming per sprint_context.md §Test Stack)>
53
53
  BASELINE_FAIL: <count of failing scenarios>
54
54
  flashcards_flagged: [ ... ]
55
55
  ```
@@ -95,7 +95,7 @@ Dispatch verification depth by reading `lane.value` from the pack's JSON block (
95
95
  - Grep checklist for required strings (heading anchors, schema field names).
96
96
  - DoD §2.2 audit (cross-check the story's Gherkin → diff one-to-one).
97
97
  - Spec-vs-impl drift table (one row per requirement).
98
- - **Skip** typecheck and targeted vitest UNLESS `pack.adjacent.adjacent_test_files` is non-empty AND any of those files are under `cleargate-cli/`, `mcp/`, `cleargate-cli/test/`, or any path with extension `.ts` / `.test.ts` / `.test.sh`.
98
+ - **Skip** typecheck and targeted test-suite re-run UNLESS `pack.adjacent.adjacent_test_files` is non-empty AND any of those files are under `cleargate-cli/`, `mcp/`, `cleargate-cli/test/`, or any path with extension `.ts` / `.test.ts` / `.test.sh`.
99
99
 
100
100
  - **`standard` lane** (current default — most stories):
101
101
  - Everything in `fast`, PLUS:
@@ -107,7 +107,7 @@ Dispatch verification depth by reading `lane.value` from the pack's JSON block (
107
107
  - **`runtime` lane** (NEW — CLI / integration / runtime-surface stories):
108
108
  - Everything in `standard`, PLUS:
109
109
  - **Full test suite** re-run (not just touched-file scope) — `cleargate gate test` against the full package.
110
- - Coverage check: every Gherkin scenario has a passing test (zero MISSING entries).
110
+ - Coverage check: every Gherkin scenario has a passing test (zero MISSING entries). A red test that now passes against the Developer's commit counts as its own green — no separate green file required (red-now-green, CR-081).
111
111
  - **exit-code matrix:** invoke each new/modified command with `--help`, the happy path, and at least one explicit error path; assert exit codes match documented values.
112
112
  - **Integration smoke:** if the story changes a script under `.cleargate/scripts/`, run the script's bash test harness from a `mktemp -d` fixture (mirrors test_prep_qa_context.sh pattern at `.cleargate/scripts/test/`).
113
113
 
@@ -131,6 +131,7 @@ Verify that a Developer's claim of "done" is real. Approve with `QA: PASS` or re
131
131
  4. **Map commit to acceptance criteria.** For each Gherkin scenario in the Story:
132
132
  - Find the corresponding test in the diff
133
133
  - If no test matches, that's a FAIL with reason `missing test for "<scenario name>"`
134
+ - **Red-now-green clause (CR-081):** A QA-Red test file that was failing at baseline and now PASSES against the Developer's commit fully satisfies the green-test / acceptance-coverage requirement. Do NOT require a separate green-path file distinct from the now-passing red file — a duplicate is redundant under reuse-over-recreate (Rule 18). Map each Gherkin scenario to the now-passing red test; absence of a second file is NOT a MISSING entry and must not produce a FAIL.
134
135
  5. **Check for regressions** — follow the **Lane-Aware Playbook**: on `standard` lane run scoped tests (touched-file neighborhoods); on `runtime` lane run the complete package suite. If anything in scope broke, FAIL.
135
136
  6. **Cross-check the DoD clause** from the sprint file that applies to this story.
136
137
  7. **Record flashcards on recurring QA failure patterns.** `Skill(flashcard, "record: #qa <lesson>")`. Examples:
@@ -140,7 +141,7 @@ Verify that a Developer's claim of "done" is real. Approve with `QA: PASS` or re
140
141
  ## Output shape
141
142
  ```
142
143
  STORY: STORY-NNN-NN
143
- QA: PASS | FAIL
144
+ QA: PASS | PASS-PENDING-SMOKE | FAIL
144
145
  TYPECHECK: pass | fail
145
146
  TESTS: X passed, Y failed, Z skipped (full suite)
146
147
  ACCEPTANCE_COVERAGE: N of M Gherkin scenarios have matching tests
@@ -151,6 +152,13 @@ flashcards_flagged:
151
152
  - "YYYY-MM-DD · #tag1 #tag2 · lesson ≤120 chars"
152
153
  ```
153
154
 
155
+ **Verdict decision order (CR-081 × CR-082 composed):**
156
+ 1. **(a) Deferred-acceptance check (CR-082):** Does the story declare a `deferred_verification` entry whose heavy acceptance scenario was NOT run in this QA pass (out of static scope)? If yes and every OTHER static scenario passes → return `PASS-PENDING-SMOKE`. The story merges to the sprint branch but is NOT fully Done; the close gate (Step 2.9) enforces the deferred check before sprint-close. `PASS-PENDING-SMOKE` shadows `PASS` when a deferred entry is unrun.
157
+ 2. **(b) Full-coverage check (CR-081):** Else, are all Gherkin scenarios covered by passing tests — including red-now-green tests counting as their own green? → return full `PASS`.
158
+ 3. **(c)** Else → `FAIL`.
159
+
160
+ Note: CR-081's red-now-green clause applies to the STATIC scenarios (step b); CR-082's PASS-PENDING-SMOKE is gated on an UNRUN DEFERRED scenario (step a). A story can be red-now-green on its static scenarios AND still be PASS-PENDING-SMOKE because a separate heavy scenario is deferred. The deferred-check (step a) is evaluated BEFORE the full-PASS branch (step b).
161
+
154
162
  `flashcards_flagged` is a YAML list of strings, each matching the `FLASHCARD.md` one-liner format (`YYYY-MM-DD · #tag1 #tag2 · lesson`). Default is `[]` (empty list — omit if no new cards). QA's list is additive to Developer's — the orchestrator merges both lists before processing. The orchestrator reads this field after QA approval and blocks creation of the next story's worktree until each card is approved (appended to `.cleargate/FLASHCARD.md`) or explicitly rejected (reason recorded in sprint §4 Execution Log). See protocol §4.
155
163
 
156
164
  ## Guardrails
@@ -51,7 +51,7 @@ mkdir -p "${SPRINT_DIR}"
51
51
  TOOL_NAME_EARLY="$(printf '%s' "${INPUT}" | jq -r '.tool_name // empty')"
52
52
 
53
53
  if [[ "${TOOL_NAME_EARLY}" == "Task" && "${SKIP_FLASHCARD_GATE:-0}" != "1" && "${SPRINT_ID}" != "_off-sprint" ]]; then
54
- # Collect flagged cards from all STORY-*-dev.md and STORY-*-qa.md in SPRINT_DIR (flat layout).
54
+ # Collect flagged cards from all *-dev.md / *-qa.md reports (any id: STORY/CR/BUG/...), flat + reports/ subdir (CR-082 moved reports to reports/).
55
55
  UNPROCESSED_CARDS=()
56
56
  UNPROCESSED_HASHES=()
57
57
 
@@ -59,7 +59,7 @@ if [[ "${TOOL_NAME_EARLY}" == "Task" && "${SKIP_FLASHCARD_GATE:-0}" != "1" && "$
59
59
  REPORT_FILES=()
60
60
  while IFS= read -r f; do
61
61
  REPORT_FILES+=("$f")
62
- done < <(ls -t "${SPRINT_DIR}"/STORY-*-dev.md "${SPRINT_DIR}"/STORY-*-qa.md 2>/dev/null)
62
+ done < <(ls -t "${SPRINT_DIR}"/*-dev.md "${SPRINT_DIR}"/*-qa.md "${SPRINT_DIR}"/reports/*-dev.md "${SPRINT_DIR}"/reports/*-qa.md 2>/dev/null)
63
63
 
64
64
  for REPORT_FILE in "${REPORT_FILES[@]}"; do
65
65
  [[ ! -f "${REPORT_FILE}" ]] && continue
@@ -149,7 +149,11 @@ Sprint branch is **never committed to directly**. All work lands via story-branc
149
149
  node .cleargate/scripts/init_sprint.mjs SPRINT-NN
150
150
  ```
151
151
 
152
- This writes `.cleargate/sprint-runs/SPRINT-NN/state.json` and flips `.cleargate/sprint-runs/.active` to `SPRINT-NN`. Without `state.json` the lane router, dispatch hook, and close pipeline all fail.
152
+ This writes `.cleargate/sprint-runs/SPRINT-NN/state.json`, flips `.cleargate/sprint-runs/.active` to `SPRINT-NN` (atomic tmp+rename), and ingests SDR lane assignments so fast-lane stories are stamped correctly without manual reclassification. Without `state.json` the lane router, dispatch hook, and close pipeline all fail.
153
+
154
+ **Lane ingest (CR-078):** `init_sprint.mjs` reads lane assignments from `<sprintDir>/plans/waves.json` (`lane_assignments: { "STORY-ID": "fast"|"standard" }` top-level key) when the file is present. When `waves.json` is absent it falls back to parsing the Sprint Plan §2.4 Lane Audit table (`| Story-ID | fast | rationale |`). Stories not declared in either source keep the `standard` default. Fast-lane stories receive `lane_assigned_by: 'sdr-lane-audit'`; no manual `cleargate story lane …` reclassification is needed after init.
155
+
156
+ **`.active` sentinel (CR-078):** `init_sprint.mjs` atomically writes the sprint ID to `.active` as its final step. This is the single place the sentinel is SET at kickoff; `cleargate sprint close` (`sprint.ts`) is the single place it is CLEARED. If a prior `.active` value differs from the sprint being initialised, init emits `WARN: .active was SPRINT-NN, overwriting with SPRINT-MM — prior sprint may not have been closed` to stderr but does not block.
153
157
 
154
158
  `init_sprint.mjs` also writes `<sprintDir>/sprint-context.md` from `.cleargate/templates/sprint_context.md`, populated with `sprint_id` + goal (extracted from sprint plan §0 `- **Sprint Goal:** …` bullet, or placeholder if absent) + active CR list. Every Dev/QA/Architect/DevOps dispatch reads this file as preflight (see §B + §C contracts + agent prompts `## Preflight`).
155
159
 
@@ -255,6 +259,18 @@ git worktree list
255
259
 
256
260
  **Do not run `git worktree add` inside `mcp/`.** It is a nested git repo. If the story touches `mcp/`, the Developer edits `mcp/` from inside `.worktrees/STORY-NNN-NN/mcp/...` — visible as a subdirectory of the outer worktree. (`cleargate-enforcement.md` §1.3.)
257
261
 
262
+ After creating the worktree, provision configured gitignored config into it:
263
+
264
+ ```bash
265
+ bash .cleargate/scripts/provision_worktree_config.sh .worktrees/STORY-NNN-NN
266
+ ```
267
+
268
+ This symlinks (or copies, per `config.yml worktree.provision_mode`) the roots in
269
+ `config.yml worktree.provision_config` (default `[.env]`) so the target's build/tests
270
+ load their config in-worktree without a manual step. The provisioned roots are exempt
271
+ from the `stray_env_files` pre-gate scan — the same configured list serves as both the
272
+ provisioning spec and the scan-exemption list (single source of truth, CR-079).
273
+
258
274
  ### C.3 Spawn QA-Red (standard lane only — fast lane skips this step)
259
275
 
260
276
  ```bash
@@ -295,6 +311,8 @@ After QA-Red commits Red tests, run the wiring/pre-gate scan first:
295
311
  bash .cleargate/scripts/pre_gate_runner.sh arch .worktrees/STORY-NNN-NN/ sprint/S-NN
296
312
  ```
297
313
 
314
+ **Semantic fixture lint (CR-081 — included in the pre-gate scan above):** `pre_gate_runner.sh arch` automatically invokes `qa_red_lint.mjs` (check #5 inside `run_arch()`) against the worktree's QA-Red test files (`*.red.node.test.ts` and other red-test forms). This is a pre-gate step — it runs as part of the scan, before any TPV or Developer dispatch. If `qa_red_lint` flags a semantic fixture error (R-enum: invalid enum literal; R-query: duplicate-text `queryByText`/`getByText`), the scan exits non-zero and routes back to QA-Red via `arch_bounces` (identical to a TPV wiring gap — increment `arch_bounces` via `node .cleargate/scripts/update_state.mjs STORY-NNN-NN --arch-bounce`, then re-dispatch §C.3 QA-Red with the lint flag description). The lint check is gated by `arch.qa_red_lint` in `gate-checks.json` (default `true`).
315
+
298
316
  **Conditional TPV dispatch (scan-flag gated — a clean standard-lane story proceeds to §C.4 with NO Architect TPV dispatch):**
299
317
 
300
318
  - **If the scan returns exit 0 with no flags:** skip the live Architect `Mode: TPV` dispatch and proceed directly to §C.4 Spawn Developer. No Architect agent is spawned for TPV on the clean path.
@@ -65,3 +65,19 @@
65
65
  # test: "cargo test"
66
66
  # typecheck: "cargo check"
67
67
  # lint: "cargo clippy"
68
+
69
+ # worktree:
70
+ # # Gitignored config roots provisioned into each new story worktree (CR-079).
71
+ # # Each root in this list is symlinked (or copied, per provision_mode) from the
72
+ # # repo root into the worktree when `provision_worktree_config.sh` is called.
73
+ # # The same list is exempt from the stray_env_files pre-gate scan so officially-
74
+ # # provisioned roots do not false-flag. Default: [".env"].
75
+ # provision_config:
76
+ # - .env
77
+ # - .env.local # example — add any additional gitignored config roots
78
+ # provision_mode: symlink # symlink (default) | copy
79
+ # # symlink: creates absolute symlink worktree-root/<root> -> repo-root/<root>.
80
+ # # Symlink stays live with the source .env; trivially torn down with
81
+ # # the worktree. Requires target to resolve via absolute path (done
82
+ # # automatically by provision_worktree_config.sh).
83
+ # # copy: copies the file so the worktree can hold a divergent config.
@@ -0,0 +1,245 @@
1
+ /**
2
+ * RED test — CR-082: Deferred-Verification Close Gate
3
+ *
4
+ * Tests fail against the clean baseline because:
5
+ * - close_sprint.mjs has NO Step 2.9 (deferred-verification gate absent)
6
+ * - CLEARGATE_FORCE_DEFERRED_VERIFY env seam does not exist yet
7
+ * - PASS-PENDING-SMOKE is absent from cleargate-planning/.claude/agents/qa.md
8
+ * - deferred_verification field is absent from .cleargate/templates/story.md
9
+ *
10
+ * Run (standalone — NOT the cli npm-test suite):
11
+ * node --test --import tsx .cleargate/scripts/close_sprint.deferred-verify.red.node.test.ts
12
+ *
13
+ * NOTE: On Node ≥25 `--import tsx/esm` triggers ERR_REQUIRE_CYCLE_MODULE (Node v25 stricter
14
+ * ESM/CJS cycle detection). Use `--import tsx` instead — functionally identical; tsx auto-detects
15
+ * ESM. The M3 plan specifies `tsx/esm`; this deviation is Node-version-specific and non-breaking.
16
+ *
17
+ * Node:test + assert only. No Pydantic Literal, no queryByText/getByText.
18
+ * qa_red_lint (CR-081, now live) will exit 0 on this file — no R-enum/R-query match.
19
+ *
20
+ * Self-isolating: every scenario uses a mktemp tmpdir, bypasses real git/Steps 2.7/2.8
21
+ * via CLEARGATE_SKIP_WORKTREE_CHECK=1 + CLEARGATE_SKIP_MERGE_CHECK=1.
22
+ * The real SPRINT-34 .active sentinel + state.json are never touched.
23
+ */
24
+
25
+ import { test } from 'node:test';
26
+ import assert from 'node:assert/strict';
27
+ import { spawnSync } from 'node:child_process';
28
+ import * as fs from 'node:fs';
29
+ import * as path from 'node:path';
30
+ import * as os from 'node:os';
31
+ import { fileURLToPath } from 'node:url';
32
+
33
+ const __dirname = path.dirname(fileURLToPath(import.meta.url));
34
+ const REPO_ROOT = path.resolve(__dirname, '..', '..');
35
+ const CLOSE_SPRINT = path.join(__dirname, 'close_sprint.mjs');
36
+
37
+ // ── Fixture helpers ──────────────────────────────────────────────────────────
38
+
39
+ /**
40
+ * Minimal state.json (schema_version 3) with a single story in Done state.
41
+ * Uses standard lane only — no fast lane — to avoid v2.1 validation check
42
+ * which requires a SPRINT-NN_REPORT.md with metric rows/sections.
43
+ * All required per-story validator fields are present.
44
+ */
45
+ function makeStateJson(sprintId: string): object {
46
+ return {
47
+ schema_version: 3,
48
+ sprint_id: sprintId,
49
+ sprint_status: 'Active',
50
+ stories: {
51
+ 'CR-082': {
52
+ state: 'Done',
53
+ lane: 'standard',
54
+ lane_assigned_by: 'architect',
55
+ lane_demoted_at: null,
56
+ lane_demotion_reason: null,
57
+ qa_bounces: 0,
58
+ arch_bounces: 0,
59
+ updated_at: '2026-06-04T00:00:00Z',
60
+ },
61
+ },
62
+ last_action: 'test setup',
63
+ updated_at: '2026-06-04T00:00:00Z',
64
+ };
65
+ }
66
+
67
+ /** Create a tmpdir with a minimal state.json for the given sprint id. */
68
+ function makeTmpSprintDir(sprintId: string): string {
69
+ const dir = fs.mkdtempSync(path.join(os.tmpdir(), `cg-cr082-red-${sprintId}-`));
70
+ fs.writeFileSync(
71
+ path.join(dir, 'state.json'),
72
+ JSON.stringify(makeStateJson(sprintId), null, 2) + '\n',
73
+ 'utf8'
74
+ );
75
+ return dir;
76
+ }
77
+
78
+ /**
79
+ * Base env seams shared by all scenarios.
80
+ * All non-Step-2.9 gates bypassed so the test isolates Step 2.9 specifically.
81
+ */
82
+ function baseEnv(sprintDir: string): NodeJS.ProcessEnv {
83
+ return {
84
+ ...process.env,
85
+ CLEARGATE_SPRINT_DIR: sprintDir,
86
+ CLEARGATE_REPO_ROOT: REPO_ROOT,
87
+ CLEARGATE_SKIP_LIFECYCLE_CHECK: '1', // skip WS8(e) dist assertion + Step 2.6/2.6b
88
+ CLEARGATE_SKIP_WORKTREE_CHECK: '1', // skip Step 2.7
89
+ CLEARGATE_SKIP_MERGE_CHECK: '1', // skip Step 2.8
90
+ CLEARGATE_SKIP_PARENT_ROLLUP: '1', // skip Step 2.6c
91
+ CLEARGATE_SKIP_SPRINT_TRENDS: '1', // skip Step 6.5
92
+ CLEARGATE_SKIP_SKILL_CANDIDATES: '1', // skip Step 6.6
93
+ CLEARGATE_SKIP_FLASHCARD_CLEANUP: '1',// skip Step 6.7
94
+ CLEARGATE_SKIP_BUNDLE_CHECK: '1', // skip Step 3.5
95
+ };
96
+ }
97
+
98
+ /**
99
+ * Run close_sprint.mjs in a subprocess. Returns { exitCode, stdout, stderr }.
100
+ * Never throws — captures all output even on non-zero exit.
101
+ * Creates and destroys its own tmpdir automatically.
102
+ */
103
+ function runClose(
104
+ sprintId: string,
105
+ extraEnv: NodeJS.ProcessEnv = {}
106
+ ): { exitCode: number; stdout: string; stderr: string } {
107
+ const sprintDir = makeTmpSprintDir(sprintId);
108
+ const env = { ...baseEnv(sprintDir), ...extraEnv };
109
+
110
+ const result = spawnSync('node', [CLOSE_SPRINT, sprintId, '--assume-ack'], {
111
+ env,
112
+ encoding: 'utf8',
113
+ timeout: 30_000,
114
+ });
115
+
116
+ // Clean up tmpdir regardless of outcome
117
+ try {
118
+ fs.rmSync(sprintDir, { recursive: true, force: true });
119
+ } catch {
120
+ // cleanup failure is non-fatal in test context
121
+ }
122
+
123
+ return {
124
+ exitCode: result.status ?? 1,
125
+ stdout: result.stdout ?? '',
126
+ stderr: result.stderr ?? '',
127
+ };
128
+ }
129
+
130
+ // ── Scenario 1: declared + NO result (null) → close BLOCKS ───────────────────
131
+ test('Scenario 1: declared entry with no result → Step 2.9 blocks close (non-zero exit + Step 2.9 message)', () => {
132
+ const forcedState = JSON.stringify({
133
+ 'STORY-TEST-01': {
134
+ declared: [{ command: 'docker build .', blocks: 'close' }],
135
+ result: null,
136
+ },
137
+ });
138
+
139
+ const { exitCode, stdout, stderr } = runClose('SPRINT-TEST', {
140
+ CLEARGATE_FORCE_DEFERRED_VERIFY: forcedState,
141
+ });
142
+
143
+ // RED: Step 2.9 is absent. close_sprint.mjs proceeds to Step 3 (prefill_report.mjs)
144
+ // which fails for unrelated reasons — but the Step 2.9 message is never emitted.
145
+ // After implementation: exit non-zero + "Step 2.9" message in combined output.
146
+ assert.notEqual(exitCode, 0,
147
+ 'Expected non-zero exit when a declared deferred verification has no result');
148
+ const combined = stdout + stderr;
149
+ assert.match(
150
+ combined,
151
+ /Step 2\.9.*fail|deferred.*verif.*fail|STORY-TEST-01.*unrun|unrun.*STORY-TEST-01/i,
152
+ 'Expected a Step 2.9 failure message for STORY-TEST-01 (unrun result); Step 2.9 not yet implemented'
153
+ );
154
+ });
155
+
156
+ // ── Scenario 2: declared + green result → gate PASSES ────────────────────────
157
+ test('Scenario 2: declared entry with green result → Step 2.9 passes; close proceeds past 2.9', () => {
158
+ const forcedState = JSON.stringify({
159
+ 'STORY-TEST-01': {
160
+ declared: [{ command: 'docker build .', blocks: 'close' }],
161
+ result: 'green',
162
+ },
163
+ });
164
+
165
+ const { stdout } = runClose('SPRINT-TEST', {
166
+ CLEARGATE_FORCE_DEFERRED_VERIFY: forcedState,
167
+ });
168
+
169
+ // RED: Step 2.9 is absent — stdout will never contain "Step 2.9 passed".
170
+ assert.match(
171
+ stdout,
172
+ /Step 2\.9 passed/i,
173
+ 'Expected "Step 2.9 passed" in stdout when all declared verifications are green; Step 2.9 not yet implemented'
174
+ );
175
+ });
176
+
177
+ // ── Scenario 3: none declared → silent no-op (THE SPRINT-34-own-close case) ──
178
+ test('Scenario 3: no deferred_verification declarations → Step 2.9 silent no-op, close proceeds', () => {
179
+ // No CLEARGATE_FORCE_DEFERRED_VERIFY injected — Step 2.9 should scan story files,
180
+ // find none with deferred_verification declarations, print the no-op line, and continue.
181
+ // This is the load-bearing regression: if this fails post-implementation,
182
+ // CR-082's own close gate would block SPRINT-34's close (sprint-ending defect).
183
+ const { stdout } = runClose('SPRINT-TEST', {});
184
+
185
+ // RED: Step 2.9 is absent — stdout will never contain the no-op line.
186
+ assert.match(
187
+ stdout,
188
+ /Step 2\.9 passed: no deferred verifications declared/i,
189
+ 'Expected "Step 2.9 passed: no deferred verifications declared" no-op line; Step 2.9 not yet implemented'
190
+ );
191
+ });
192
+
193
+ // ── Scenario 4: declared + red result → close BLOCKS ─────────────────────────
194
+ test('Scenario 4: declared entry with red result → Step 2.9 blocks close', () => {
195
+ const forcedState = JSON.stringify({
196
+ 'STORY-TEST-02': {
197
+ declared: [{ command: 'docker build .', blocks: 'close' }],
198
+ result: 'red',
199
+ },
200
+ });
201
+
202
+ const { exitCode, stdout, stderr } = runClose('SPRINT-TEST', {
203
+ CLEARGATE_FORCE_DEFERRED_VERIFY: forcedState,
204
+ });
205
+
206
+ // RED: Step 2.9 is absent. exit may be non-zero (Step 3 failure) but for wrong reason.
207
+ assert.notEqual(exitCode, 0,
208
+ 'Expected non-zero exit when a declared deferred verification has a red result');
209
+ const combined = stdout + stderr;
210
+ assert.match(
211
+ combined,
212
+ /Step 2\.9.*fail|deferred.*verif.*fail|STORY-TEST-02.*red|red.*STORY-TEST-02/i,
213
+ 'Expected a Step 2.9 failure message for STORY-TEST-02 (red result); Step 2.9 not yet implemented'
214
+ );
215
+ });
216
+
217
+ // ── Scenario 5: grep — PASS-PENDING-SMOKE in qa.md ───────────────────────────
218
+ test('Scenario 5: grep — PASS-PENDING-SMOKE present in cleargate-planning/.claude/agents/qa.md', () => {
219
+ const qaPath = path.join(REPO_ROOT, 'cleargate-planning', '.claude', 'agents', 'qa.md');
220
+
221
+ // RED: PASS-PENDING-SMOKE is not yet in qa.md — will exist after CR-082 Developer merges.
222
+ const grepResult = spawnSync('grep', ['-q', 'PASS-PENDING-SMOKE', qaPath], {
223
+ encoding: 'utf8',
224
+ });
225
+ assert.equal(
226
+ grepResult.status,
227
+ 0,
228
+ `Expected "PASS-PENDING-SMOKE" in ${qaPath} (output-shape verdict line); field not yet added`
229
+ );
230
+ });
231
+
232
+ // ── Scenario 6: grep — deferred_verification field in story.md ───────────────
233
+ test('Scenario 6: grep — deferred_verification field present in .cleargate/templates/story.md', () => {
234
+ const storyMdPath = path.join(REPO_ROOT, '.cleargate', 'templates', 'story.md');
235
+
236
+ // RED: deferred_verification field is not yet in story.md — will exist after CR-082 Developer merges.
237
+ const grepResult = spawnSync('grep', ['-q', 'deferred_verification', storyMdPath], {
238
+ encoding: 'utf8',
239
+ });
240
+ assert.equal(
241
+ grepResult.status,
242
+ 0,
243
+ `Expected "deferred_verification" field in ${storyMdPath}; field not yet added`
244
+ );
245
+ });