cleargate 0.14.0 → 0.15.1
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.
- package/CHANGELOG.md +21 -0
- package/dist/MANIFEST.json +72 -16
- package/dist/admin-api/index.cjs +0 -1
- package/dist/admin-api/index.js +1 -2
- package/dist/auth/factory.cjs +0 -1
- package/dist/auth/factory.js +2 -3
- package/dist/auth/require-token.cjs +0 -1
- package/dist/auth/require-token.js +1 -2
- package/dist/auth/token-store.cjs +0 -1
- package/dist/auth/token-store.js +1 -2
- package/dist/{bootstrap-root-QKSA5V75.js → bootstrap-root-2H5HVTCC.js} +1 -2
- package/dist/{chunk-PDE37WFQ.js → chunk-A7MSQUU7.js} +2 -3
- package/dist/{chunk-BTSZOEWC.js → chunk-P6KEDAK2.js} +0 -1
- package/dist/{chunk-E3X7IE5E.js → chunk-PY6FHGV5.js} +1 -2
- package/dist/{chunk-5DI2Z3C2.js → chunk-Y53ZZYYU.js} +1 -2
- package/dist/cli.cjs +1564 -1414
- package/dist/cli.js +1514 -1364
- package/dist/lib/ledger.cjs +0 -1
- package/dist/lib/ledger.js +1 -2
- package/dist/lib/lifecycle-reconcile.cjs +0 -1
- package/dist/lib/lifecycle-reconcile.js +2 -3
- package/dist/{whoami-EANGN46Z.js → whoami-JKQQPABQ.js} +3 -4
- package/package.json +4 -3
- package/templates/cleargate-planning/.claude/agents/architect-synth.md +2 -0
- package/templates/cleargate-planning/.claude/agents/architect.md +4 -2
- package/templates/cleargate-planning/.claude/agents/developer.md +4 -11
- package/templates/cleargate-planning/.claude/agents/qa.md +14 -6
- package/templates/cleargate-planning/.claude/hooks/pending-task-sentinel.sh +2 -2
- package/templates/cleargate-planning/.claude/skills/sprint-execution/SKILL.md +19 -1
- package/templates/cleargate-planning/.cleargate/config.example.yml +16 -0
- package/templates/cleargate-planning/.cleargate/scripts/close_sprint.deferred-verify.red.node.test.ts +245 -0
- package/templates/cleargate-planning/.cleargate/scripts/close_sprint.mjs +227 -0
- package/templates/cleargate-planning/.cleargate/scripts/gate-checks.json +5 -4
- package/templates/cleargate-planning/.cleargate/scripts/init_sprint.mjs +75 -2
- package/templates/cleargate-planning/.cleargate/scripts/pre_gate_common.sh +48 -0
- package/templates/cleargate-planning/.cleargate/scripts/pre_gate_runner.sh +57 -1
- package/templates/cleargate-planning/.cleargate/scripts/provision_worktree_config.sh +155 -0
- package/templates/cleargate-planning/.cleargate/scripts/qa_red_lint.mjs +380 -0
- package/templates/cleargate-planning/.cleargate/scripts/run_script.sh +34 -1
- package/templates/cleargate-planning/.cleargate/scripts/test/cr077_eviction.red.sh +113 -0
- package/templates/cleargate-planning/.cleargate/scripts/test/cr078_init.test.sh +309 -0
- package/templates/cleargate-planning/.cleargate/scripts/test/cr079_provision.red.sh +262 -0
- package/templates/cleargate-planning/.cleargate/scripts/test/cr080_wrapper.test.sh +177 -0
- package/templates/cleargate-planning/.cleargate/scripts/test/cr081_qa_red_lint.red.sh +348 -0
- package/templates/cleargate-planning/.cleargate/sprint-runs/_off-sprint/.session-totals.json +1 -0
- package/templates/cleargate-planning/.cleargate/sprint-runs/_off-sprint/token-ledger.jsonl +222 -0
- package/templates/cleargate-planning/.cleargate/templates/sprint_context.md +17 -0
- package/templates/cleargate-planning/.cleargate/templates/story.md +1 -0
- package/templates/cleargate-planning/MANIFEST.json +72 -16
- package/dist/admin-api/index.cjs.map +0 -1
- package/dist/admin-api/index.js.map +0 -1
- package/dist/auth/factory.cjs.map +0 -1
- package/dist/auth/factory.js.map +0 -1
- package/dist/auth/require-token.cjs.map +0 -1
- package/dist/auth/require-token.js.map +0 -1
- package/dist/auth/token-store.cjs.map +0 -1
- package/dist/auth/token-store.js.map +0 -1
- package/dist/bootstrap-root-QKSA5V75.js.map +0 -1
- package/dist/chunk-5DI2Z3C2.js.map +0 -1
- package/dist/chunk-BTSZOEWC.js.map +0 -1
- package/dist/chunk-E3X7IE5E.js.map +0 -1
- package/dist/chunk-PDE37WFQ.js.map +0 -1
- package/dist/cli.cjs.map +0 -1
- package/dist/cli.js.map +0 -1
- package/dist/lib/ledger.cjs.map +0 -1
- package/dist/lib/ledger.js.map +0 -1
- package/dist/lib/lifecycle-reconcile.cjs.map +0 -1
- package/dist/lib/lifecycle-reconcile.js.map +0 -1
- package/dist/templates/cleargate-planning/.claude/agents/architect-reader.md +0 -61
- package/dist/templates/cleargate-planning/.claude/agents/architect-synth.md +0 -124
- package/dist/templates/cleargate-planning/.claude/agents/architect.md +0 -230
- package/dist/templates/cleargate-planning/.claude/agents/cleargate-wiki-contradict.md +0 -108
- package/dist/templates/cleargate-planning/.claude/agents/cleargate-wiki-ingest.md +0 -194
- package/dist/templates/cleargate-planning/.claude/agents/cleargate-wiki-lint.md +0 -261
- package/dist/templates/cleargate-planning/.claude/agents/cleargate-wiki-query.md +0 -143
- package/dist/templates/cleargate-planning/.claude/agents/developer.md +0 -185
- package/dist/templates/cleargate-planning/.claude/agents/devops.md +0 -257
- package/dist/templates/cleargate-planning/.claude/agents/qa.md +0 -171
- package/dist/templates/cleargate-planning/.claude/agents/reporter.md +0 -274
- package/dist/templates/cleargate-planning/.claude/hooks/pending-task-sentinel.sh +0 -209
- package/dist/templates/cleargate-planning/.claude/hooks/pre-commit-surface-gate.sh +0 -33
- package/dist/templates/cleargate-planning/.claude/hooks/pre-commit-test-ratchet.sh +0 -58
- package/dist/templates/cleargate-planning/.claude/hooks/pre-commit.sh +0 -19
- package/dist/templates/cleargate-planning/.claude/hooks/pre-edit-gate.sh +0 -162
- package/dist/templates/cleargate-planning/.claude/hooks/pre-tool-use-autonomy.sh +0 -58
- package/dist/templates/cleargate-planning/.claude/hooks/pre-tool-use-task.sh +0 -148
- package/dist/templates/cleargate-planning/.claude/hooks/session-start.sh +0 -75
- package/dist/templates/cleargate-planning/.claude/hooks/stamp-and-gate.sh +0 -43
- package/dist/templates/cleargate-planning/.claude/hooks/token-ledger.sh +0 -590
- package/dist/templates/cleargate-planning/.claude/settings.json +0 -68
- package/dist/templates/cleargate-planning/.claude/skills/flashcard/SKILL.md +0 -102
- package/dist/templates/cleargate-planning/.claude/skills/sprint-execution/SKILL.md +0 -742
- package/dist/templates/cleargate-planning/.cleargate/FLASHCARD.md +0 -7
- package/dist/templates/cleargate-planning/.cleargate/config.example.yml +0 -67
- package/dist/templates/cleargate-planning/.cleargate/config.yml +0 -18
- package/dist/templates/cleargate-planning/.cleargate/delivery/archive/.gitkeep +0 -0
- package/dist/templates/cleargate-planning/.cleargate/delivery/pending-sync/.gitkeep +0 -0
- package/dist/templates/cleargate-planning/.cleargate/knowledge/cleargate-enforcement.md +0 -551
- package/dist/templates/cleargate-planning/.cleargate/knowledge/cleargate-protocol.md +0 -878
- package/dist/templates/cleargate-planning/.cleargate/knowledge/mid-sprint-triage-rubric.md +0 -160
- package/dist/templates/cleargate-planning/.cleargate/knowledge/readiness-gates.md +0 -213
- package/dist/templates/cleargate-planning/.cleargate/knowledge/sprint-closeout-checklist.md +0 -71
- package/dist/templates/cleargate-planning/.cleargate/scripts/_migrate-schema-v3.mjs +0 -120
- package/dist/templates/cleargate-planning/.cleargate/scripts/assert_story_files.mjs +0 -265
- package/dist/templates/cleargate-planning/.cleargate/scripts/close_sprint.mjs +0 -1012
- package/dist/templates/cleargate-planning/.cleargate/scripts/collision_surface.sh +0 -114
- package/dist/templates/cleargate-planning/.cleargate/scripts/constants.mjs +0 -62
- package/dist/templates/cleargate-planning/.cleargate/scripts/dedupe_frontmatter.mjs +0 -219
- package/dist/templates/cleargate-planning/.cleargate/scripts/file_surface_diff.sh +0 -320
- package/dist/templates/cleargate-planning/.cleargate/scripts/gate-checks.json +0 -15
- package/dist/templates/cleargate-planning/.cleargate/scripts/init_gate_config.sh +0 -38
- package/dist/templates/cleargate-planning/.cleargate/scripts/init_sprint.mjs +0 -240
- package/dist/templates/cleargate-planning/.cleargate/scripts/launch_wave.mjs +0 -341
- package/dist/templates/cleargate-planning/.cleargate/scripts/lib/report-filename.mjs +0 -54
- package/dist/templates/cleargate-planning/.cleargate/scripts/pre_gate_common.sh +0 -206
- package/dist/templates/cleargate-planning/.cleargate/scripts/pre_gate_runner.sh +0 -371
- package/dist/templates/cleargate-planning/.cleargate/scripts/prefill_report.mjs +0 -280
- package/dist/templates/cleargate-planning/.cleargate/scripts/prep_doc_refresh.mjs +0 -378
- package/dist/templates/cleargate-planning/.cleargate/scripts/prep_qa_context.mjs +0 -888
- package/dist/templates/cleargate-planning/.cleargate/scripts/run_script.sh +0 -209
- package/dist/templates/cleargate-planning/.cleargate/scripts/sprint_trends.mjs +0 -71
- package/dist/templates/cleargate-planning/.cleargate/scripts/state.schema.json +0 -127
- package/dist/templates/cleargate-planning/.cleargate/scripts/suggest_improvements.mjs +0 -717
- package/dist/templates/cleargate-planning/.cleargate/scripts/surface-whitelist.txt +0 -27
- package/dist/templates/cleargate-planning/.cleargate/scripts/test/test_assert_story_files.sh +0 -261
- package/dist/templates/cleargate-planning/.cleargate/scripts/test/test_file_surface.sh +0 -210
- package/dist/templates/cleargate-planning/.cleargate/scripts/test/test_flashcard_gate.sh +0 -190
- package/dist/templates/cleargate-planning/.cleargate/scripts/test/test_prep_qa_context.sh +0 -482
- package/dist/templates/cleargate-planning/.cleargate/scripts/test/test_test_ratchet.sh +0 -327
- package/dist/templates/cleargate-planning/.cleargate/scripts/test_ratchet.mjs +0 -261
- package/dist/templates/cleargate-planning/.cleargate/scripts/update_state.mjs +0 -246
- package/dist/templates/cleargate-planning/.cleargate/scripts/validate_bounce_readiness.mjs +0 -111
- package/dist/templates/cleargate-planning/.cleargate/scripts/validate_state.mjs +0 -184
- package/dist/templates/cleargate-planning/.cleargate/scripts/write_dispatch.sh +0 -172
- package/dist/templates/cleargate-planning/.cleargate/templates/Bug.md +0 -126
- package/dist/templates/cleargate-planning/.cleargate/templates/CR.md +0 -130
- package/dist/templates/cleargate-planning/.cleargate/templates/Sprint Plan Template.md +0 -137
- package/dist/templates/cleargate-planning/.cleargate/templates/epic.md +0 -166
- package/dist/templates/cleargate-planning/.cleargate/templates/hotfix.md +0 -111
- package/dist/templates/cleargate-planning/.cleargate/templates/initiative.md +0 -122
- package/dist/templates/cleargate-planning/.cleargate/templates/sprint_context.md +0 -50
- package/dist/templates/cleargate-planning/.cleargate/templates/sprint_report.md +0 -224
- package/dist/templates/cleargate-planning/.cleargate/templates/story.md +0 -213
- package/dist/templates/cleargate-planning/CLAUDE.md +0 -66
- package/dist/templates/cleargate-planning/MANIFEST.json +0 -503
- package/dist/templates/synthesis/active-sprint.md +0 -30
- package/dist/templates/synthesis/open-gates.md +0 -38
- package/dist/templates/synthesis/product-state.md +0 -31
- package/dist/templates/synthesis/roadmap.md +0 -63
- package/dist/whoami-EANGN46Z.js.map +0 -1
package/dist/lib/ledger.cjs
CHANGED
package/dist/lib/ledger.js
CHANGED
|
@@ -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-
|
|
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
|
|
@@ -12,8 +12,8 @@ import {
|
|
|
12
12
|
reconcileLifecycle,
|
|
13
13
|
rollUpParentStatus,
|
|
14
14
|
walkActiveParents
|
|
15
|
-
} from "../chunk-
|
|
16
|
-
import "../chunk-
|
|
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-
|
|
12
|
-
import "./chunk-
|
|
11
|
+
} from "./chunk-A7MSQUU7.js";
|
|
12
|
+
import "./chunk-PY6FHGV5.js";
|
|
13
13
|
import {
|
|
14
14
|
init_esm_shims
|
|
15
|
-
} from "./chunk-
|
|
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.
|
|
3
|
+
"version": "0.15.1",
|
|
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": "
|
|
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",
|
|
@@ -43,6 +43,8 @@ Two stories A and B may share a wave IFF ALL five clauses hold:
|
|
|
43
43
|
|
|
44
44
|
Any failed clause → serialize A and B into different waves. Cite the failing clause in the wave rationale.
|
|
45
45
|
|
|
46
|
+
**The DB axis is intentionally coarse.** The fail-safe-serialize rule below puts *every* DB-writing story in its own wave, so clause 4 is satisfied by construction for any co-waved pair. This is deliberate: a shared development database couples stories through fixtures, sequences, triggers, and foreign keys that a per-table write list cannot see, so the safe default is to run DB-writing stories serially rather than infer parallelism from disjoint table names.
|
|
47
|
+
|
|
46
48
|
**Empty-surface guard (BUG-033 — do NOT fail open).** Clause 2 is only meaningful when BOTH stories have a NON-empty surface. If either story's `(file_surface ∪ file_creates)` is empty — `collision_surface.sh` emitted nothing because the story has no §3.1 table, a prose-only table, or only slash-free / extension-less tokens (`architect-reader` reports `[]`) — you must NOT read `∅ ∩ ∅ = ∅` as "disjoint". An empty surface is **unproven**, not **proven-disjoint**. Fail-safe-serialize the empty-surface story (see below) BEFORE running any pairwise clause-2 check; never co-wave it.
|
|
47
49
|
|
|
48
50
|
## Tiny-sprint floor (N ≤ 2)
|
|
@@ -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
|
|
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
|
|
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
|
-
|
|
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
|
-
**
|
|
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
|
-
**
|
|
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
|
-
-
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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}"/
|
|
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
|
|
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
|
+
});
|