get-shit-done-cc 1.22.2 → 1.22.3

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/README.md CHANGED
@@ -532,7 +532,7 @@ GSD stores project settings in `.planning/config.json`. Configure during `/gsd:n
532
532
  | Setting | Options | Default | What it controls |
533
533
  |---------|---------|---------|------------------|
534
534
  | `mode` | `yolo`, `interactive` | `interactive` | Auto-approve vs confirm at each step |
535
- | `depth` | `quick`, `standard`, `comprehensive` | `standard` | Planning thoroughness (phases × plans) |
535
+ | `granularity` | `coarse`, `standard`, `fine` | `standard` | Phase granularity — how finely scope is sliced (phases × plans) |
536
536
 
537
537
  ### Model Profiles
538
538
 
@@ -0,0 +1,176 @@
1
+ ---
2
+ name: gsd-nyquist-auditor
3
+ description: Fills Nyquist validation gaps by generating tests and verifying coverage for phase requirements
4
+ tools:
5
+ - Read
6
+ - Write
7
+ - Edit
8
+ - Bash
9
+ - Glob
10
+ - Grep
11
+ color: "#8B5CF6"
12
+ ---
13
+
14
+ <role>
15
+ GSD Nyquist auditor. Spawned by /gsd:validate-phase to fill validation gaps in completed phases.
16
+
17
+ For each gap in `<gaps>`: generate minimal behavioral test, run it, debug if failing (max 3 iterations), report results.
18
+
19
+ **Mandatory Initial Read:** If prompt contains `<files_to_read>`, load ALL listed files before any action.
20
+
21
+ **Implementation files are READ-ONLY.** Only create/modify: test files, fixtures, VALIDATION.md. Implementation bugs → ESCALATE. Never fix implementation.
22
+ </role>
23
+
24
+ <execution_flow>
25
+
26
+ <step name="load_context">
27
+ Read ALL files from `<files_to_read>`. Extract:
28
+ - Implementation: exports, public API, input/output contracts
29
+ - PLANs: requirement IDs, task structure, verify blocks
30
+ - SUMMARYs: what was implemented, files changed, deviations
31
+ - Test infrastructure: framework, config, runner commands, conventions
32
+ - Existing VALIDATION.md: current map, compliance status
33
+ </step>
34
+
35
+ <step name="analyze_gaps">
36
+ For each gap in `<gaps>`:
37
+
38
+ 1. Read related implementation files
39
+ 2. Identify observable behavior the requirement demands
40
+ 3. Classify test type:
41
+
42
+ | Behavior | Test Type |
43
+ |----------|-----------|
44
+ | Pure function I/O | Unit |
45
+ | API endpoint | Integration |
46
+ | CLI command | Smoke |
47
+ | DB/filesystem operation | Integration |
48
+
49
+ 4. Map to test file path per project conventions
50
+
51
+ Action by gap type:
52
+ - `no_test_file` → Create test file
53
+ - `test_fails` → Diagnose and fix the test (not impl)
54
+ - `no_automated_command` → Determine command, update map
55
+ </step>
56
+
57
+ <step name="generate_tests">
58
+ Convention discovery: existing tests → framework defaults → fallback.
59
+
60
+ | Framework | File Pattern | Runner | Assert Style |
61
+ |-----------|-------------|--------|--------------|
62
+ | pytest | `test_{name}.py` | `pytest {file} -v` | `assert result == expected` |
63
+ | jest | `{name}.test.ts` | `npx jest {file}` | `expect(result).toBe(expected)` |
64
+ | vitest | `{name}.test.ts` | `npx vitest run {file}` | `expect(result).toBe(expected)` |
65
+ | go test | `{name}_test.go` | `go test -v -run {Name}` | `if got != want { t.Errorf(...) }` |
66
+
67
+ Per gap: Write test file. One focused test per requirement behavior. Arrange/Act/Assert. Behavioral test names (`test_user_can_reset_password`), not structural (`test_reset_function`).
68
+ </step>
69
+
70
+ <step name="run_and_verify">
71
+ Execute each test. If passes: record success, next gap. If fails: enter debug loop.
72
+
73
+ Run every test. Never mark untested tests as passing.
74
+ </step>
75
+
76
+ <step name="debug_loop">
77
+ Max 3 iterations per failing test.
78
+
79
+ | Failure Type | Action |
80
+ |--------------|--------|
81
+ | Import/syntax/fixture error | Fix test, re-run |
82
+ | Assertion: actual matches impl but violates requirement | IMPLEMENTATION BUG → ESCALATE |
83
+ | Assertion: test expectation wrong | Fix assertion, re-run |
84
+ | Environment/runtime error | ESCALATE |
85
+
86
+ Track: `{ gap_id, iteration, error_type, action, result }`
87
+
88
+ After 3 failed iterations: ESCALATE with requirement, expected vs actual behavior, impl file reference.
89
+ </step>
90
+
91
+ <step name="report">
92
+ Resolved gaps: `{ task_id, requirement, test_type, automated_command, file_path, status: "green" }`
93
+ Escalated gaps: `{ task_id, requirement, reason, debug_iterations, last_error }`
94
+
95
+ Return one of three formats below.
96
+ </step>
97
+
98
+ </execution_flow>
99
+
100
+ <structured_returns>
101
+
102
+ ## GAPS FILLED
103
+
104
+ ```markdown
105
+ ## GAPS FILLED
106
+
107
+ **Phase:** {N} — {name}
108
+ **Resolved:** {count}/{count}
109
+
110
+ ### Tests Created
111
+ | # | File | Type | Command |
112
+ |---|------|------|---------|
113
+ | 1 | {path} | {unit/integration/smoke} | `{cmd}` |
114
+
115
+ ### Verification Map Updates
116
+ | Task ID | Requirement | Command | Status |
117
+ |---------|-------------|---------|--------|
118
+ | {id} | {req} | `{cmd}` | green |
119
+
120
+ ### Files for Commit
121
+ {test file paths}
122
+ ```
123
+
124
+ ## PARTIAL
125
+
126
+ ```markdown
127
+ ## PARTIAL
128
+
129
+ **Phase:** {N} — {name}
130
+ **Resolved:** {M}/{total} | **Escalated:** {K}/{total}
131
+
132
+ ### Resolved
133
+ | Task ID | Requirement | File | Command | Status |
134
+ |---------|-------------|------|---------|--------|
135
+ | {id} | {req} | {file} | `{cmd}` | green |
136
+
137
+ ### Escalated
138
+ | Task ID | Requirement | Reason | Iterations |
139
+ |---------|-------------|--------|------------|
140
+ | {id} | {req} | {reason} | {N}/3 |
141
+
142
+ ### Files for Commit
143
+ {test file paths for resolved gaps}
144
+ ```
145
+
146
+ ## ESCALATE
147
+
148
+ ```markdown
149
+ ## ESCALATE
150
+
151
+ **Phase:** {N} — {name}
152
+ **Resolved:** 0/{total}
153
+
154
+ ### Details
155
+ | Task ID | Requirement | Reason | Iterations |
156
+ |---------|-------------|--------|------------|
157
+ | {id} | {req} | {reason} | {N}/3 |
158
+
159
+ ### Recommendations
160
+ - **{req}:** {manual test instructions or implementation fix needed}
161
+ ```
162
+
163
+ </structured_returns>
164
+
165
+ <success_criteria>
166
+ - [ ] All `<files_to_read>` loaded before any action
167
+ - [ ] Each gap analyzed with correct test type
168
+ - [ ] Tests follow project conventions
169
+ - [ ] Tests verify behavior, not structure
170
+ - [ ] Every test executed — none marked passing without running
171
+ - [ ] Implementation files never modified
172
+ - [ ] Max 3 debug iterations per gap
173
+ - [ ] Implementation bugs escalated, not fixed
174
+ - [ ] Structured return provided (GAPS FILLED / PARTIAL / ESCALATE)
175
+ - [ ] Test files listed for commit
176
+ </success_criteria>
@@ -306,7 +306,7 @@ Verified patterns from official sources:
306
306
 
307
307
  ## Validation Architecture
308
308
 
309
- > Skip this section entirely if workflow.nyquist_validation is false in .planning/config.json
309
+ > Skip this section entirely if workflow.nyquist_validation is explicitly set to false in .planning/config.json. If the key is absent, treat as enabled.
310
310
 
311
311
  ### Test Framework
312
312
  | Property | Value |
@@ -372,7 +372,7 @@ INIT=$(node "$HOME/.claude/get-shit-done/bin/gsd-tools.cjs" init phase-op "${PHA
372
372
 
373
373
  Extract from init JSON: `phase_dir`, `padded_phase`, `phase_number`, `commit_docs`.
374
374
 
375
- Also read `.planning/config.json` — if `workflow.nyquist_validation` is `true`, include Validation Architecture section in RESEARCH.md. If `false`, skip it.
375
+ Also read `.planning/config.json` — include Validation Architecture section in RESEARCH.md unless `workflow.nyquist_validation` is explicitly `false`. If the key is absent or `true`, include the section.
376
376
 
377
377
  Then read CONTEXT.md if exists:
378
378
  ```bash
@@ -408,7 +408,7 @@ For each domain: Context7 first → Official docs → WebSearch → Cross-verify
408
408
 
409
409
  ## Step 4: Validation Architecture Research (if nyquist_validation enabled)
410
410
 
411
- **Skip if** workflow.nyquist_validation is false.
411
+ **Skip if** workflow.nyquist_validation is explicitly set to false. If absent, treat as enabled.
412
412
 
413
413
  ### Detect Test Infrastructure
414
414
  Scan for: test config files (pytest.ini, jest.config.*, vitest.config.*), test directories (test/, tests/, __tests__/), test files (*.test.*, *.spec.*), package.json test scripts.
@@ -316,7 +316,20 @@ issue:
316
316
 
317
317
  ## Dimension 8: Nyquist Compliance
318
318
 
319
- Skip if: `workflow.nyquist_validation` is false, phase has no RESEARCH.md, or RESEARCH.md has no "Validation Architecture" section. Output: "Dimension 8: SKIPPED (nyquist_validation disabled or not applicable)"
319
+ Skip if: `workflow.nyquist_validation` is explicitly set to `false` in config.json (absent key = enabled), phase has no RESEARCH.md, or RESEARCH.md has no "Validation Architecture" section. Output: "Dimension 8: SKIPPED (nyquist_validation disabled or not applicable)"
320
+
321
+ ### Check 8e — VALIDATION.md Existence (Gate)
322
+
323
+ Before running checks 8a-8d, verify VALIDATION.md exists:
324
+
325
+ ```bash
326
+ ls "${PHASE_DIR}"/*-VALIDATION.md 2>/dev/null
327
+ ```
328
+
329
+ **If missing:** **BLOCKING FAIL** — "VALIDATION.md not found for phase {N}. Re-run `/gsd:plan-phase {N} --research` to regenerate."
330
+ Skip checks 8a-8d entirely. Report Dimension 8 as FAIL with this single issue.
331
+
332
+ **If exists:** Proceed to checks 8a-8d.
320
333
 
321
334
  ### Check 8a — Automated Verify Presence
322
335
 
@@ -371,15 +371,15 @@ Plans should complete within ~50% context (not 80%). No context anxiety, quality
371
371
 
372
372
  **CONSIDER splitting:** >5 files total, complex domains, uncertainty about approach, natural semantic boundaries.
373
373
 
374
- ## Depth Calibration
374
+ ## Granularity Calibration
375
375
 
376
- | Depth | Typical Plans/Phase | Tasks/Plan |
377
- |-------|---------------------|------------|
378
- | Quick | 1-3 | 2-3 |
376
+ | Granularity | Typical Plans/Phase | Tasks/Plan |
377
+ |-------------|---------------------|------------|
378
+ | Coarse | 1-3 | 2-3 |
379
379
  | Standard | 3-5 | 2-3 |
380
- | Comprehensive | 5-10 | 2-3 |
380
+ | Fine | 5-10 | 2-3 |
381
381
 
382
- Derive plans from actual work. Depth determines compression tolerance, not a target. Don't pad small work to hit a number. Don't compress complex work to look efficient.
382
+ Derive plans from actual work. Granularity determines compression tolerance, not a target. Don't pad small work to hit a number. Don't compress complex work to look efficient.
383
383
 
384
384
  ## Context Per Task Estimates
385
385
 
@@ -1135,7 +1135,7 @@ Apply goal-backward methodology (see goal_backward section):
1135
1135
  </step>
1136
1136
 
1137
1137
  <step name="estimate_scope">
1138
- Verify each plan fits context budget: 2-3 tasks, ~50% target. Split if necessary. Check depth setting.
1138
+ Verify each plan fits context budget: 2-3 tasks, ~50% target. Split if necessary. Check granularity setting.
1139
1139
  </step>
1140
1140
 
1141
1141
  <step name="confirm_breakdown">
@@ -200,17 +200,17 @@ Track coverage as you go.
200
200
  - New milestone: Start at 1
201
201
  - Continuing milestone: Check existing phases, start at last + 1
202
202
 
203
- ## Depth Calibration
203
+ ## Granularity Calibration
204
204
 
205
- Read depth from config.json. Depth controls compression tolerance.
205
+ Read granularity from config.json. Granularity controls compression tolerance.
206
206
 
207
- | Depth | Typical Phases | What It Means |
208
- |-------|----------------|---------------|
209
- | Quick | 3-5 | Combine aggressively, critical path only |
207
+ | Granularity | Typical Phases | What It Means |
208
+ |-------------|----------------|---------------|
209
+ | Coarse | 3-5 | Combine aggressively, critical path only |
210
210
  | Standard | 5-8 | Balanced grouping |
211
- | Comprehensive | 8-12 | Let natural boundaries stand |
211
+ | Fine | 8-12 | Let natural boundaries stand |
212
212
 
213
- **Key:** Derive phases from work, then apply depth as compression guidance. Don't pad small projects or compress complex ones.
213
+ **Key:** Derive phases from work, then apply granularity as compression guidance. Don't pad small projects or compress complex ones.
214
214
 
215
215
  ## Good Phase Patterns
216
216
 
@@ -357,7 +357,7 @@ When presenting to user for approval:
357
357
  ## ROADMAP DRAFT
358
358
 
359
359
  **Phases:** [N]
360
- **Depth:** [from config]
360
+ **Granularity:** [from config]
361
361
  **Coverage:** [X]/[Y] requirements mapped
362
362
 
363
363
  ### Phase Structure
@@ -401,7 +401,7 @@ Orchestrator provides:
401
401
  - PROJECT.md content (core value, constraints)
402
402
  - REQUIREMENTS.md content (v1 requirements with REQ-IDs)
403
403
  - research/SUMMARY.md content (if exists - phase suggestions)
404
- - config.json (depth setting)
404
+ - config.json (granularity setting)
405
405
 
406
406
  Parse and confirm understanding before proceeding.
407
407
 
@@ -437,7 +437,7 @@ Apply phase identification methodology:
437
437
  1. Group requirements by natural delivery boundaries
438
438
  2. Identify dependencies between groups
439
439
  3. Create phases that complete coherent capabilities
440
- 4. Check depth setting for compression guidance
440
+ 4. Check granularity setting for compression guidance
441
441
 
442
442
  ## Step 5: Derive Success Criteria
443
443
 
@@ -502,7 +502,7 @@ When files are written and returning to orchestrator:
502
502
  ### Summary
503
503
 
504
504
  **Phases:** {N}
505
- **Depth:** {from config}
505
+ **Granularity:** {from config}
506
506
  **Coverage:** {X}/{X} requirements mapped ✓
507
507
 
508
508
  | Phase | Goal | Requirements |
@@ -628,7 +628,7 @@ Roadmap is complete when:
628
628
  - [ ] All v1 requirements extracted with IDs
629
629
  - [ ] Research context loaded (if exists)
630
630
  - [ ] Phases derived from requirements (not imposed)
631
- - [ ] Depth calibration applied
631
+ - [ ] Granularity calibration applied
632
632
  - [ ] Dependencies between phases identified
633
633
  - [ ] Success criteria derived for each phase (2-5 observable behaviors)
634
634
  - [ ] Success criteria cross-checked against requirements (gaps resolved)
package/bin/install.js CHANGED
@@ -58,6 +58,21 @@ if (hasAll) {
58
58
  if (hasCodex) selectedRuntimes.push('codex');
59
59
  }
60
60
 
61
+ /**
62
+ * Convert a pathPrefix (which uses absolute paths for global installs) to a
63
+ * $HOME-relative form for replacing $HOME/.claude/ references in bash code blocks.
64
+ * Preserves $HOME as a shell variable so paths remain portable across machines.
65
+ */
66
+ function toHomePrefix(pathPrefix) {
67
+ const home = os.homedir().replace(/\\/g, '/');
68
+ const normalized = pathPrefix.replace(/\\/g, '/');
69
+ if (normalized.startsWith(home)) {
70
+ return '$HOME' + normalized.slice(home.length);
71
+ }
72
+ // For relative paths or paths not under $HOME, return as-is
73
+ return normalized;
74
+ }
75
+
61
76
  // Helper to get directory name for a runtime (used for local/project installs)
62
77
  function getDirName(runtime) {
63
78
  if (runtime === 'opencode') return '.opencode';
@@ -700,8 +715,14 @@ function installCodexConfig(targetDir, agentsSrc) {
700
715
  const agentEntries = fs.readdirSync(agentsSrc).filter(f => f.startsWith('gsd-') && f.endsWith('.md'));
701
716
  const agents = [];
702
717
 
718
+ // Compute the Codex pathPrefix for replacing .claude paths
719
+ const codexPathPrefix = `${targetDir.replace(/\\/g, '/')}/`;
720
+
703
721
  for (const file of agentEntries) {
704
- const content = fs.readFileSync(path.join(agentsSrc, file), 'utf8');
722
+ let content = fs.readFileSync(path.join(agentsSrc, file), 'utf8');
723
+ // Replace .claude paths before generating TOML (source files use ~/.claude and $HOME/.claude)
724
+ content = content.replace(/~\/\.claude\//g, codexPathPrefix);
725
+ content = content.replace(/\$HOME\/\.claude\//g, toHomePrefix(codexPathPrefix));
705
726
  const { frontmatter } = extractFrontmatterAndBody(content);
706
727
  const name = extractFrontmatterField(frontmatter, 'name') || file.replace('.md', '');
707
728
  const description = extractFrontmatterField(frontmatter, 'description') || '';
@@ -823,8 +844,9 @@ function convertClaudeToOpencodeFrontmatter(content) {
823
844
  convertedContent = convertedContent.replace(/\bTodoWrite\b/g, 'todowrite');
824
845
  // Replace /gsd:command with /gsd-command for opencode (flat command structure)
825
846
  convertedContent = convertedContent.replace(/\/gsd:/g, '/gsd-');
826
- // Replace ~/.claude with ~/.config/opencode (OpenCode's correct config location)
847
+ // Replace ~/.claude and $HOME/.claude with OpenCode's config location
827
848
  convertedContent = convertedContent.replace(/~\/\.claude\b/g, '~/.config/opencode');
849
+ convertedContent = convertedContent.replace(/\$HOME\/\.claude\b/g, '$HOME/.config/opencode');
828
850
  // Replace general-purpose subagent type with OpenCode's equivalent "general"
829
851
  convertedContent = convertedContent.replace(/subagent_type="general-purpose"/g, 'subagent_type="general"');
830
852
 
@@ -1006,9 +1028,11 @@ function copyFlattenedCommands(srcDir, destDir, prefix, pathPrefix, runtime) {
1006
1028
 
1007
1029
  let content = fs.readFileSync(srcPath, 'utf8');
1008
1030
  const globalClaudeRegex = /~\/\.claude\//g;
1031
+ const globalClaudeHomeRegex = /\$HOME\/\.claude\//g;
1009
1032
  const localClaudeRegex = /\.\/\.claude\//g;
1010
1033
  const opencodeDirRegex = /~\/\.opencode\//g;
1011
1034
  content = content.replace(globalClaudeRegex, pathPrefix);
1035
+ content = content.replace(globalClaudeHomeRegex, toHomePrefix(pathPrefix));
1012
1036
  content = content.replace(localClaudeRegex, `./${getDirName(runtime)}/`);
1013
1037
  content = content.replace(opencodeDirRegex, pathPrefix);
1014
1038
  content = processAttribution(content, getCommitAttribution(runtime));
@@ -1065,9 +1089,11 @@ function copyCommandsAsCodexSkills(srcDir, skillsDir, prefix, pathPrefix, runtim
1065
1089
 
1066
1090
  let content = fs.readFileSync(srcPath, 'utf8');
1067
1091
  const globalClaudeRegex = /~\/\.claude\//g;
1092
+ const globalClaudeHomeRegex = /\$HOME\/\.claude\//g;
1068
1093
  const localClaudeRegex = /\.\/\.claude\//g;
1069
1094
  const codexDirRegex = /~\/\.codex\//g;
1070
1095
  content = content.replace(globalClaudeRegex, pathPrefix);
1096
+ content = content.replace(globalClaudeHomeRegex, toHomePrefix(pathPrefix));
1071
1097
  content = content.replace(localClaudeRegex, `./${getDirName(runtime)}/`);
1072
1098
  content = content.replace(codexDirRegex, pathPrefix);
1073
1099
  content = processAttribution(content, getCommitAttribution(runtime));
@@ -1108,11 +1134,13 @@ function copyWithPathReplacement(srcDir, destDir, pathPrefix, runtime, isCommand
1108
1134
  if (entry.isDirectory()) {
1109
1135
  copyWithPathReplacement(srcPath, destPath, pathPrefix, runtime, isCommand);
1110
1136
  } else if (entry.name.endsWith('.md')) {
1111
- // Replace ~/.claude/ and ./.claude/ with runtime-appropriate paths
1137
+ // Replace ~/.claude/ and $HOME/.claude/ and ./.claude/ with runtime-appropriate paths
1112
1138
  let content = fs.readFileSync(srcPath, 'utf8');
1113
1139
  const globalClaudeRegex = /~\/\.claude\//g;
1140
+ const globalClaudeHomeRegex = /\$HOME\/\.claude\//g;
1114
1141
  const localClaudeRegex = /\.\/\.claude\//g;
1115
1142
  content = content.replace(globalClaudeRegex, pathPrefix);
1143
+ content = content.replace(globalClaudeHomeRegex, toHomePrefix(pathPrefix));
1116
1144
  content = content.replace(localClaudeRegex, `./${dirName}/`);
1117
1145
  content = processAttribution(content, getCommitAttribution(runtime));
1118
1146
 
@@ -1953,9 +1981,11 @@ function install(isGlobal, runtime = 'claude') {
1953
1981
  for (const entry of agentEntries) {
1954
1982
  if (entry.isFile() && entry.name.endsWith('.md')) {
1955
1983
  let content = fs.readFileSync(path.join(agentsSrc, entry.name), 'utf8');
1956
- // Always replace ~/.claude/ as it is the source of truth in the repo
1984
+ // Replace ~/.claude/ and $HOME/.claude/ as they are the source of truth in the repo
1957
1985
  const dirRegex = /~\/\.claude\//g;
1986
+ const homeDirRegex = /\$HOME\/\.claude\//g;
1958
1987
  content = content.replace(dirRegex, pathPrefix);
1988
+ content = content.replace(homeDirRegex, toHomePrefix(pathPrefix));
1959
1989
  content = processAttribution(content, getCommitAttribution(runtime));
1960
1990
  // Convert frontmatter for runtime compatibility
1961
1991
  if (isOpencode) {
@@ -2046,6 +2076,39 @@ function install(isGlobal, runtime = 'claude') {
2046
2076
  // Report any backed-up local patches
2047
2077
  reportLocalPatches(targetDir, runtime);
2048
2078
 
2079
+ // Verify no leaked .claude paths in non-Claude runtimes
2080
+ if (runtime !== 'claude') {
2081
+ const leakedPaths = [];
2082
+ function scanForLeakedPaths(dir) {
2083
+ if (!fs.existsSync(dir)) return;
2084
+ const entries = fs.readdirSync(dir, { withFileTypes: true });
2085
+ for (const entry of entries) {
2086
+ const fullPath = path.join(dir, entry.name);
2087
+ if (entry.isDirectory()) {
2088
+ scanForLeakedPaths(fullPath);
2089
+ } else if ((entry.name.endsWith('.md') || entry.name.endsWith('.toml')) && entry.name !== 'CHANGELOG.md') {
2090
+ const content = fs.readFileSync(fullPath, 'utf8');
2091
+ const matches = content.match(/(?:~|\$HOME)\/\.claude\b/g);
2092
+ if (matches) {
2093
+ leakedPaths.push({ file: fullPath.replace(targetDir + '/', ''), count: matches.length });
2094
+ }
2095
+ }
2096
+ }
2097
+ }
2098
+ scanForLeakedPaths(targetDir);
2099
+ if (leakedPaths.length > 0) {
2100
+ const totalLeaks = leakedPaths.reduce((sum, l) => sum + l.count, 0);
2101
+ console.warn(`\n ${yellow}⚠${reset} Found ${totalLeaks} unreplaced .claude path reference(s) in ${leakedPaths.length} file(s):`);
2102
+ for (const leak of leakedPaths.slice(0, 5)) {
2103
+ console.warn(` ${dim}${leak.file}${reset} (${leak.count})`);
2104
+ }
2105
+ if (leakedPaths.length > 5) {
2106
+ console.warn(` ${dim}... and ${leakedPaths.length - 5} more file(s)${reset}`);
2107
+ }
2108
+ console.warn(` ${dim}These paths may not resolve correctly for ${runtimeLabel}.${reset}`);
2109
+ }
2110
+ }
2111
+
2049
2112
  if (isCodex) {
2050
2113
  // Generate Codex config.toml and per-agent .toml files
2051
2114
  const agentCount = installCodexConfig(targetDir, agentsSrc);
@@ -0,0 +1,35 @@
1
+ ---
2
+ name: gsd:validate-phase
3
+ description: Retroactively audit and fill Nyquist validation gaps for a completed phase
4
+ argument-hint: "[phase number]"
5
+ allowed-tools:
6
+ - Read
7
+ - Write
8
+ - Edit
9
+ - Bash
10
+ - Glob
11
+ - Grep
12
+ - Task
13
+ - AskUserQuestion
14
+ ---
15
+ <objective>
16
+ Audit Nyquist validation coverage for a completed phase. Three states:
17
+ - (A) VALIDATION.md exists — audit and fill gaps
18
+ - (B) No VALIDATION.md, SUMMARY.md exists — reconstruct from artifacts
19
+ - (C) Phase not executed — exit with guidance
20
+
21
+ Output: updated VALIDATION.md + generated test files.
22
+ </objective>
23
+
24
+ <execution_context>
25
+ @~/.claude/get-shit-done/workflows/validate-phase.md
26
+ </execution_context>
27
+
28
+ <context>
29
+ Phase: $ARGUMENTS — optional, defaults to last completed phase.
30
+ </context>
31
+
32
+ <process>
33
+ Execute @~/.claude/get-shit-done/workflows/validate-phase.md.
34
+ Preserve all workflow gates.
35
+ </process>
@@ -37,6 +37,13 @@ function cmdConfigEnsureSection(cwd, raw) {
37
37
  try {
38
38
  if (fs.existsSync(globalDefaultsPath)) {
39
39
  userDefaults = JSON.parse(fs.readFileSync(globalDefaultsPath, 'utf-8'));
40
+ // Migrate deprecated "depth" key to "granularity"
41
+ if ('depth' in userDefaults && !('granularity' in userDefaults)) {
42
+ const depthToGranularity = { quick: 'coarse', standard: 'standard', comprehensive: 'fine' };
43
+ userDefaults.granularity = depthToGranularity[userDefaults.depth] || userDefaults.depth;
44
+ delete userDefaults.depth;
45
+ try { fs.writeFileSync(globalDefaultsPath, JSON.stringify(userDefaults, null, 2), 'utf-8'); } catch {}
46
+ }
40
47
  }
41
48
  } catch (err) {
42
49
  // Ignore malformed global defaults, fall back to hardcoded
@@ -54,7 +61,7 @@ function cmdConfigEnsureSection(cwd, raw) {
54
61
  research: true,
55
62
  plan_check: true,
56
63
  verifier: true,
57
- nyquist_validation: false,
64
+ nyquist_validation: true,
58
65
  },
59
66
  parallelization: true,
60
67
  brave_search: hasBraveSearch,
@@ -27,6 +27,7 @@ const MODEL_PROFILES = {
27
27
  'gsd-verifier': { quality: 'sonnet', balanced: 'sonnet', budget: 'haiku' },
28
28
  'gsd-plan-checker': { quality: 'sonnet', balanced: 'sonnet', budget: 'haiku' },
29
29
  'gsd-integration-checker': { quality: 'sonnet', balanced: 'sonnet', budget: 'haiku' },
30
+ 'gsd-nyquist-auditor': { quality: 'sonnet', balanced: 'sonnet', budget: 'haiku' },
30
31
  };
31
32
 
32
33
  // ─── Output helpers ───────────────────────────────────────────────────────────
@@ -76,7 +77,7 @@ function loadConfig(cwd) {
76
77
  research: true,
77
78
  plan_checker: true,
78
79
  verifier: true,
79
- nyquist_validation: false,
80
+ nyquist_validation: true,
80
81
  parallelization: true,
81
82
  brave_search: false,
82
83
  };
@@ -85,6 +86,14 @@ function loadConfig(cwd) {
85
86
  const raw = fs.readFileSync(configPath, 'utf-8');
86
87
  const parsed = JSON.parse(raw);
87
88
 
89
+ // Migrate deprecated "depth" key to "granularity" with value mapping
90
+ if ('depth' in parsed && !('granularity' in parsed)) {
91
+ const depthToGranularity = { quick: 'coarse', standard: 'standard', comprehensive: 'fine' };
92
+ parsed.granularity = depthToGranularity[parsed.depth] || parsed.depth;
93
+ delete parsed.depth;
94
+ try { fs.writeFileSync(configPath, JSON.stringify(parsed, null, 2), 'utf-8'); } catch {}
95
+ }
96
+
88
97
  const get = (key, nested) => {
89
98
  if (parsed[key] !== undefined) return parsed[key];
90
99
  if (nested && parsed[nested.section] && parsed[nested.section][nested.field] !== undefined) {
@@ -617,6 +617,18 @@ function cmdValidateHealth(cwd, options, raw) {
617
617
  }
618
618
  }
619
619
 
620
+ // ─── Check 5b: Nyquist validation key presence ──────────────────────────
621
+ if (fs.existsSync(configPath)) {
622
+ try {
623
+ const configRaw = fs.readFileSync(configPath, 'utf-8');
624
+ const configParsed = JSON.parse(configRaw);
625
+ if (configParsed.workflow && configParsed.workflow.nyquist_validation === undefined) {
626
+ addIssue('warning', 'W008', 'config.json: workflow.nyquist_validation absent (defaults to enabled but agents may skip)', 'Run /gsd:health --repair to add key', true);
627
+ if (!repairs.includes('addNyquistKey')) repairs.push('addNyquistKey');
628
+ }
629
+ } catch {}
630
+ }
631
+
620
632
  // ─── Check 6: Phase directory naming (NN-name format) ─────────────────────
621
633
  try {
622
634
  const entries = fs.readdirSync(phasesDir, { withFileTypes: true });
@@ -646,6 +658,24 @@ function cmdValidateHealth(cwd, options, raw) {
646
658
  }
647
659
  } catch {}
648
660
 
661
+ // ─── Check 7b: Nyquist VALIDATION.md consistency ────────────────────────
662
+ try {
663
+ const phaseEntries = fs.readdirSync(phasesDir, { withFileTypes: true });
664
+ for (const e of phaseEntries) {
665
+ if (!e.isDirectory()) continue;
666
+ const phaseFiles = fs.readdirSync(path.join(phasesDir, e.name));
667
+ const hasResearch = phaseFiles.some(f => f.endsWith('-RESEARCH.md'));
668
+ const hasValidation = phaseFiles.some(f => f.endsWith('-VALIDATION.md'));
669
+ if (hasResearch && !hasValidation) {
670
+ const researchFile = phaseFiles.find(f => f.endsWith('-RESEARCH.md'));
671
+ const researchContent = fs.readFileSync(path.join(phasesDir, e.name, researchFile), 'utf-8');
672
+ if (researchContent.includes('## Validation Architecture')) {
673
+ addIssue('warning', 'W009', `Phase ${e.name}: has Validation Architecture in RESEARCH.md but no VALIDATION.md`, 'Re-run /gsd:plan-phase with --research to regenerate');
674
+ }
675
+ }
676
+ }
677
+ } catch {}
678
+
649
679
  // ─── Check 8: Run existing consistency checks ─────────────────────────────
650
680
  // Inline subset of cmdValidateConsistency
651
681
  if (fs.existsSync(roadmapPath)) {
@@ -730,6 +760,23 @@ function cmdValidateHealth(cwd, options, raw) {
730
760
  repairActions.push({ action: repair, success: true, path: 'STATE.md' });
731
761
  break;
732
762
  }
763
+ case 'addNyquistKey': {
764
+ if (fs.existsSync(configPath)) {
765
+ try {
766
+ const configRaw = fs.readFileSync(configPath, 'utf-8');
767
+ const configParsed = JSON.parse(configRaw);
768
+ if (!configParsed.workflow) configParsed.workflow = {};
769
+ if (configParsed.workflow.nyquist_validation === undefined) {
770
+ configParsed.workflow.nyquist_validation = true;
771
+ fs.writeFileSync(configPath, JSON.stringify(configParsed, null, 2), 'utf-8');
772
+ }
773
+ repairActions.push({ action: repair, success: true, path: 'config.json' });
774
+ } catch (err) {
775
+ repairActions.push({ action: repair, success: false, error: err.message });
776
+ }
777
+ }
778
+ break;
779
+ }
733
780
  }
734
781
  } catch (err) {
735
782
  repairActions.push({ action: repair, success: false, error: err.message });
@@ -17,6 +17,7 @@ Model profiles control which Claude model each GSD agent uses. This allows balan
17
17
  | gsd-verifier | sonnet | sonnet | haiku |
18
18
  | gsd-plan-checker | sonnet | sonnet | haiku |
19
19
  | gsd-integration-checker | sonnet | sonnet | haiku |
20
+ | gsd-nyquist-auditor | sonnet | sonnet | haiku |
20
21
 
21
22
  ## Profile Philosophy
22
23
 
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "mode": "interactive",
3
- "depth": "standard",
3
+ "granularity": "standard",
4
4
  "workflow": {
5
5
  "research": true,
6
6
  "plan_check": true,
7
7
  "verifier": true,
8
8
  "auto_advance": false,
9
- "nyquist_validation": false
9
+ "nyquist_validation": true
10
10
  },
11
11
  "planning": {
12
12
  "commit_docs": true,
@@ -105,7 +105,7 @@ Phases execute in numeric order: 2 → 2.1 → 2.2 → 3 → 3.1 → 4
105
105
 
106
106
  <guidelines>
107
107
  **Initial planning (v1.0):**
108
- - Phase count depends on depth setting (quick: 3-5, standard: 5-8, comprehensive: 8-12)
108
+ - Phase count depends on granularity setting (coarse: 3-5, standard: 5-8, fine: 8-12)
109
109
  - Each phase delivers something coherent
110
110
  - Phases can have 1+ plans (split if >3 tasks or multiple subsystems)
111
111
  - Plans use naming: {phase}-{plan}-PLAN.md (e.g., 01-02-PLAN.md)
@@ -127,6 +127,30 @@ For each REQ-ID, determine status using all three sources:
127
127
 
128
128
  **Orphan detection:** Requirements present in REQUIREMENTS.md traceability table but absent from ALL phase VERIFICATION.md files MUST be flagged as orphaned. Orphaned requirements are treated as `unsatisfied` — they were assigned but never verified by any phase.
129
129
 
130
+ ## 5.5. Nyquist Compliance Discovery
131
+
132
+ Skip if `workflow.nyquist_validation` is explicitly `false` (absent = enabled).
133
+
134
+ ```bash
135
+ NYQUIST_CONFIG=$(node "$HOME/.claude/get-shit-done/bin/gsd-tools.cjs" config get workflow.nyquist_validation --raw 2>/dev/null)
136
+ ```
137
+
138
+ If `false`: skip entirely.
139
+
140
+ For each phase directory, check `*-VALIDATION.md`. If exists, parse frontmatter (`nyquist_compliant`, `wave_0_complete`).
141
+
142
+ Classify per phase:
143
+
144
+ | Status | Condition |
145
+ |--------|-----------|
146
+ | COMPLIANT | `nyquist_compliant: true` and all tasks green |
147
+ | PARTIAL | VALIDATION.md exists, `nyquist_compliant: false` or red/pending |
148
+ | MISSING | No VALIDATION.md |
149
+
150
+ Add to audit YAML: `nyquist: { compliant_phases, partial_phases, missing_phases, overall }`
151
+
152
+ Discovery only — never auto-calls `/gsd:validate-phase`.
153
+
130
154
  ## 6. Aggregate into v{version}-MILESTONE-AUDIT.md
131
155
 
132
156
  Create `.planning/v{version}-v{version}-MILESTONE-AUDIT.md` with:
@@ -227,6 +251,14 @@ All requirements covered. Cross-phase integration verified. E2E flows complete.
227
251
  {For each flow gap:}
228
252
  - **{flow name}:** breaks at {step}
229
253
 
254
+ ### Nyquist Coverage
255
+
256
+ | Phase | VALIDATION.md | Compliant | Action |
257
+ |-------|---------------|-----------|--------|
258
+ | {phase} | exists/missing | true/false/partial | `/gsd:validate-phase {N}` |
259
+
260
+ Phases needing validation: run `/gsd:validate-phase {N}` for each flagged phase.
261
+
230
262
  ───────────────────────────────────────────────────────────────
231
263
 
232
264
  ## ▶ Next Up
@@ -293,5 +325,7 @@ All requirements met. No critical blockers. Accumulated tech debt needs review.
293
325
  - [ ] Integration checker spawned with milestone requirement IDs
294
326
  - [ ] v{version}-MILESTONE-AUDIT.md created with structured requirement gap objects
295
327
  - [ ] FAIL gate enforced — any unsatisfied requirement forces gaps_found status
328
+ - [ ] Nyquist compliance scanned for all milestone phases (if enabled)
329
+ - [ ] Missing VALIDATION.md phases flagged with validate-phase suggestion
296
330
  - [ ] Results presented with actionable next steps
297
331
  </success_criteria>
@@ -136,6 +136,8 @@ Report final status.
136
136
  | W005 | warning | Phase directory naming mismatch | No |
137
137
  | W006 | warning | Phase in ROADMAP but no directory | No |
138
138
  | W007 | warning | Phase on disk but not in ROADMAP | No |
139
+ | W008 | warning | config.json: workflow.nyquist_validation absent (defaults to enabled but agents may skip) | Yes |
140
+ | W009 | warning | Phase has Validation Architecture in RESEARCH.md but no VALIDATION.md | No |
139
141
  | I001 | info | Plan without SUMMARY (may be in progress) | No |
140
142
 
141
143
  </error_codes>
@@ -147,6 +149,7 @@ Report final status.
147
149
  | createConfig | Create config.json with defaults | None |
148
150
  | resetConfig | Delete + recreate config.json | Loses custom settings |
149
151
  | regenerateState | Create STATE.md from ROADMAP structure | Loses session history |
152
+ | addNyquistKey | Add workflow.nyquist_validation: true to config.json | None — matches existing default |
150
153
 
151
154
  **Not repairable (too risky):**
152
155
  - PROJECT.md, ROADMAP.md content
@@ -14,7 +14,7 @@ Check if `--auto` flag is present in $ARGUMENTS.
14
14
  **If auto mode:**
15
15
  - Skip brownfield mapping offer (assume greenfield)
16
16
  - Skip deep questioning (extract context from provided document)
17
- - Config: YOLO mode is implicit (skip that question), but ask depth/git/agents FIRST (Step 2a)
17
+ - Config: YOLO mode is implicit (skip that question), but ask granularity/git/agents FIRST (Step 2a)
18
18
  - After config: run Steps 6-9 automatically with smart defaults:
19
19
  - Research: Always yes
20
20
  - Requirements: Include all table stakes + features from provided document
@@ -90,13 +90,13 @@ YOLO mode is implicit (auto = YOLO). Ask remaining config questions:
90
90
  ```
91
91
  AskUserQuestion([
92
92
  {
93
- header: "Depth",
94
- question: "How thorough should planning be?",
93
+ header: "Granularity",
94
+ question: "How finely should scope be sliced into phases?",
95
95
  multiSelect: false,
96
96
  options: [
97
- { label: "Quick (Recommended)", description: "Ship fast (3-5 phases, 1-3 plans each)" },
98
- { label: "Standard", description: "Balanced scope and speed (5-8 phases, 3-5 plans each)" },
99
- { label: "Comprehensive", description: "Thorough coverage (8-12 phases, 5-10 plans each)" }
97
+ { label: "Coarse (Recommended)", description: "Fewer, broader phases (3-5 phases, 1-3 plans each)" },
98
+ { label: "Standard", description: "Balanced phase size (5-8 phases, 3-5 plans each)" },
99
+ { label: "Fine", description: "Many focused phases (8-12 phases, 5-10 plans each)" }
100
100
  ]
101
101
  },
102
102
  {
@@ -169,7 +169,7 @@ Create `.planning/config.json` with mode set to "yolo":
169
169
  ```json
170
170
  {
171
171
  "mode": "yolo",
172
- "depth": "[selected]",
172
+ "granularity": "[selected]",
173
173
  "parallelization": true|false,
174
174
  "commit_docs": true|false,
175
175
  "model_profile": "quality|balanced|budget",
@@ -177,6 +177,7 @@ Create `.planning/config.json` with mode set to "yolo":
177
177
  "research": true|false,
178
178
  "plan_check": true|false,
179
179
  "verifier": true|false,
180
+ "nyquist_validation": depth !== "quick",
180
181
  "auto_advance": true
181
182
  }
182
183
  }
@@ -379,13 +380,13 @@ questions: [
379
380
  ]
380
381
  },
381
382
  {
382
- header: "Depth",
383
- question: "How thorough should planning be?",
383
+ header: "Granularity",
384
+ question: "How finely should scope be sliced into phases?",
384
385
  multiSelect: false,
385
386
  options: [
386
- { label: "Quick", description: "Ship fast (3-5 phases, 1-3 plans each)" },
387
- { label: "Standard", description: "Balanced scope and speed (5-8 phases, 3-5 plans each)" },
388
- { label: "Comprehensive", description: "Thorough coverage (8-12 phases, 5-10 plans each)" }
387
+ { label: "Coarse", description: "Fewer, broader phases (3-5 phases, 1-3 plans each)" },
388
+ { label: "Standard", description: "Balanced phase size (5-8 phases, 3-5 plans each)" },
389
+ { label: "Fine", description: "Many focused phases (8-12 phases, 5-10 plans each)" }
389
390
  ]
390
391
  },
391
392
  {
@@ -468,14 +469,15 @@ Create `.planning/config.json` with all settings:
468
469
  ```json
469
470
  {
470
471
  "mode": "yolo|interactive",
471
- "depth": "quick|standard|comprehensive",
472
+ "granularity": "coarse|standard|fine",
472
473
  "parallelization": true|false,
473
474
  "commit_docs": true|false,
474
475
  "model_profile": "quality|balanced|budget",
475
476
  "workflow": {
476
477
  "research": true|false,
477
478
  "plan_check": true|false,
478
- "verifier": true|false
479
+ "verifier": true|false,
480
+ "nyquist_validation": depth !== "quick"
479
481
  }
480
482
  }
481
483
  ```
@@ -903,7 +905,7 @@ Task(prompt="
903
905
  - .planning/PROJECT.md (Project context)
904
906
  - .planning/REQUIREMENTS.md (v1 Requirements)
905
907
  - .planning/research/SUMMARY.md (Research findings - if exists)
906
- - .planning/config.json (Depth and mode settings)
908
+ - .planning/config.json (Granularity and mode settings)
907
909
  </files_to_read>
908
910
 
909
911
  </planning_context>
@@ -1090,7 +1092,7 @@ Exit skill and invoke SlashCommand("/gsd:discuss-phase 1 --auto")
1090
1092
  - [ ] Brownfield detection completed
1091
1093
  - [ ] Deep questioning completed (threads followed, not rushed)
1092
1094
  - [ ] PROJECT.md captures full context → **committed**
1093
- - [ ] config.json has workflow mode, depth, parallelization → **committed**
1095
+ - [ ] config.json has workflow mode, granularity, parallelization → **committed**
1094
1096
  - [ ] Research completed (if selected) — 4 parallel agents spawned → **committed**
1095
1097
  - [ ] Requirements gathered (from research or conversation)
1096
1098
  - [ ] User scoped each category (v1/v2/out of scope)
@@ -219,30 +219,26 @@ Task(
219
219
  - **`## RESEARCH COMPLETE`:** Display confirmation, continue to step 6
220
220
  - **`## RESEARCH BLOCKED`:** Display blocker, offer: 1) Provide context, 2) Skip research, 3) Abort
221
221
 
222
- ## 5.5. Create Validation Strategy (if Nyquist enabled)
222
+ ## 5.5. Create Validation Strategy
223
223
 
224
- **Skip if:** `nyquist_validation_enabled` is false from INIT JSON.
225
-
226
- After researcher completes, check if RESEARCH.md contains a Validation Architecture section:
224
+ MANDATORY unless `nyquist_validation_enabled` is false.
227
225
 
228
226
  ```bash
229
227
  grep -l "## Validation Architecture" "${PHASE_DIR}"/*-RESEARCH.md 2>/dev/null
230
228
  ```
231
229
 
232
230
  **If found:**
233
- 1. Read validation template from `~/.claude/get-shit-done/templates/VALIDATION.md`
234
- 2. Write to `${PHASE_DIR}/${PADDED_PHASE}-VALIDATION.md`
235
- 3. Fill frontmatter: replace `{N}` with phase number, `{phase-slug}` with phase slug, `{date}` with current date
236
- 4. If `commit_docs` is true:
231
+ 1. Read template: `~/.claude/get-shit-done/templates/VALIDATION.md`
232
+ 2. Write to `${PHASE_DIR}/${PADDED_PHASE}-VALIDATION.md` (use Write tool)
233
+ 3. Fill frontmatter: `{N}` phase number, `{phase-slug}` slug, `{date}` current date
234
+ 4. Verify:
237
235
  ```bash
238
- node "$HOME/.claude/get-shit-done/bin/gsd-tools.cjs" commit-docs "docs(phase-${PHASE}): add validation strategy"
236
+ test -f "${PHASE_DIR}/${PADDED_PHASE}-VALIDATION.md" && echo "VALIDATION_CREATED=true" || echo "VALIDATION_CREATED=false"
239
237
  ```
238
+ 5. If `VALIDATION_CREATED=false`: STOP — do not proceed to Step 6
239
+ 6. If `commit_docs`: `commit-docs "docs(phase-${PHASE}): add validation strategy"`
240
240
 
241
- **If not found (and nyquist enabled):** Display warning:
242
- ```
243
- ⚠ Nyquist validation enabled but researcher did not produce a Validation Architecture section.
244
- Continuing without validation strategy. Plans may fail Dimension 8 check.
245
- ```
241
+ **If not found:** Warn and continue plans may fail Dimension 8.
246
242
 
247
243
  ## 6. Check Existing Plans
248
244
 
@@ -266,6 +262,21 @@ UAT_PATH=$(printf '%s\n' "$INIT" | jq -r '.uat_path // empty')
266
262
  CONTEXT_PATH=$(printf '%s\n' "$INIT" | jq -r '.context_path // empty')
267
263
  ```
268
264
 
265
+ ## 7.5. Verify Nyquist Artifacts
266
+
267
+ Skip if `nyquist_validation_enabled` is false.
268
+
269
+ ```bash
270
+ VALIDATION_EXISTS=$(ls "${PHASE_DIR}"/*-VALIDATION.md 2>/dev/null | head -1)
271
+ ```
272
+
273
+ If missing and Nyquist enabled — ask user:
274
+ 1. Re-run: `/gsd:plan-phase {PHASE} --research`
275
+ 2. Disable Nyquist in config
276
+ 3. Continue anyway (plans fail Dimension 8)
277
+
278
+ Proceed to Step 8 only if user selects 2 or 3.
279
+
269
280
  ## 8. Spawn gsd-planner Agent
270
281
 
271
282
  Display banner:
@@ -28,7 +28,7 @@ Parse current values (default to `true` if not present):
28
28
  - `workflow.research` — spawn researcher during plan-phase
29
29
  - `workflow.plan_check` — spawn plan checker during plan-phase
30
30
  - `workflow.verifier` — spawn verifier during execute-phase
31
- - `workflow.nyquist_validation` — validation architecture research during plan-phase
31
+ - `workflow.nyquist_validation` — validation architecture research during plan-phase (default: true if absent)
32
32
  - `model_profile` — which model each agent uses (default: `balanced`)
33
33
  - `git.branching_strategy` — branching approach (default: `"none"`)
34
34
  </step>
@@ -157,7 +157,7 @@ Write `~/.gsd/defaults.json` with:
157
157
  ```json
158
158
  {
159
159
  "mode": <current>,
160
- "depth": <current>,
160
+ "granularity": <current>,
161
161
  "model_profile": <current>,
162
162
  "commit_docs": <current>,
163
163
  "parallelization": <current>,
@@ -0,0 +1,166 @@
1
+ <purpose>
2
+ Audit Nyquist validation gaps for a completed phase. Generate missing tests. Update VALIDATION.md.
3
+ </purpose>
4
+
5
+ <required_reading>
6
+ @~/.claude/get-shit-done/references/ui-brand.md
7
+ </required_reading>
8
+
9
+ <process>
10
+
11
+ ## 0. Initialize
12
+
13
+ ```bash
14
+ INIT=$(node "$HOME/.claude/get-shit-done/bin/gsd-tools.cjs" init phase-op "${PHASE_ARG}")
15
+ ```
16
+
17
+ Parse: `phase_dir`, `phase_number`, `phase_name`, `phase_slug`, `padded_phase`.
18
+
19
+ ```bash
20
+ AUDITOR_MODEL=$(node "$HOME/.claude/get-shit-done/bin/gsd-tools.cjs" resolve-model gsd-nyquist-auditor --raw)
21
+ NYQUIST_CFG=$(node "$HOME/.claude/get-shit-done/bin/gsd-tools.cjs" config get workflow.nyquist_validation --raw)
22
+ ```
23
+
24
+ If `NYQUIST_CFG` is `false`: exit with "Nyquist validation is disabled. Enable via /gsd:settings."
25
+
26
+ Display banner: `GSD > VALIDATE PHASE {N}: {name}`
27
+
28
+ ## 1. Detect Input State
29
+
30
+ ```bash
31
+ VALIDATION_FILE=$(ls "${PHASE_DIR}"/*-VALIDATION.md 2>/dev/null | head -1)
32
+ SUMMARY_FILES=$(ls "${PHASE_DIR}"/*-SUMMARY.md 2>/dev/null)
33
+ ```
34
+
35
+ - **State A** (`VALIDATION_FILE` non-empty): Audit existing
36
+ - **State B** (`VALIDATION_FILE` empty, `SUMMARY_FILES` non-empty): Reconstruct from artifacts
37
+ - **State C** (`SUMMARY_FILES` empty): Exit — "Phase {N} not executed. Run /gsd:execute-phase {N} first."
38
+
39
+ ## 2. Discovery
40
+
41
+ ### 2a. Read Phase Artifacts
42
+
43
+ Read all PLAN and SUMMARY files. Extract: task lists, requirement IDs, key-files changed, verify blocks.
44
+
45
+ ### 2b. Build Requirement-to-Task Map
46
+
47
+ Per task: `{ task_id, plan_id, wave, requirement_ids, has_automated_command }`
48
+
49
+ ### 2c. Detect Test Infrastructure
50
+
51
+ State A: Parse from existing VALIDATION.md Test Infrastructure table.
52
+ State B: Filesystem scan:
53
+
54
+ ```bash
55
+ find . -name "pytest.ini" -o -name "jest.config.*" -o -name "vitest.config.*" -o -name "pyproject.toml" 2>/dev/null | head -10
56
+ find . \( -name "*.test.*" -o -name "*.spec.*" -o -name "test_*" \) -not -path "*/node_modules/*" 2>/dev/null | head -40
57
+ ```
58
+
59
+ ### 2d. Cross-Reference
60
+
61
+ Match each requirement to existing tests by filename, imports, test descriptions. Record: requirement → test_file → status.
62
+
63
+ ## 3. Gap Analysis
64
+
65
+ Classify each requirement:
66
+
67
+ | Status | Criteria |
68
+ |--------|----------|
69
+ | COVERED | Test exists, targets behavior, runs green |
70
+ | PARTIAL | Test exists, failing or incomplete |
71
+ | MISSING | No test found |
72
+
73
+ Build: `{ task_id, requirement, gap_type, suggested_test_path, suggested_command }`
74
+
75
+ No gaps → skip to Step 6, set `nyquist_compliant: true`.
76
+
77
+ ## 4. Present Gap Plan
78
+
79
+ Call AskUserQuestion with gap table and options:
80
+ 1. "Fix all gaps" → Step 5
81
+ 2. "Skip — mark manual-only" → add to Manual-Only, Step 6
82
+ 3. "Cancel" → exit
83
+
84
+ ## 5. Spawn gsd-nyquist-auditor
85
+
86
+ ```
87
+ Task(
88
+ prompt="Read ~/.claude/agents/gsd-nyquist-auditor.md for instructions.\n\n" +
89
+ "<files_to_read>{PLAN, SUMMARY, impl files, VALIDATION.md}</files_to_read>" +
90
+ "<gaps>{gap list}</gaps>" +
91
+ "<test_infrastructure>{framework, config, commands}</test_infrastructure>" +
92
+ "<constraints>Never modify impl files. Max 3 debug iterations. Escalate impl bugs.</constraints>",
93
+ subagent_type="gsd-nyquist-auditor",
94
+ model="{AUDITOR_MODEL}",
95
+ description="Fill validation gaps for Phase {N}"
96
+ )
97
+ ```
98
+
99
+ Handle return:
100
+ - `## GAPS FILLED` → record tests + map updates, Step 6
101
+ - `## PARTIAL` → record resolved, move escalated to manual-only, Step 6
102
+ - `## ESCALATE` → move all to manual-only, Step 6
103
+
104
+ ## 6. Generate/Update VALIDATION.md
105
+
106
+ **State B (create):**
107
+ 1. Read template from `~/.claude/get-shit-done/templates/VALIDATION.md`
108
+ 2. Fill: frontmatter, Test Infrastructure, Per-Task Map, Manual-Only, Sign-Off
109
+ 3. Write to `${PHASE_DIR}/${PADDED_PHASE}-VALIDATION.md`
110
+
111
+ **State A (update):**
112
+ 1. Update Per-Task Map statuses, add escalated to Manual-Only, update frontmatter
113
+ 2. Append audit trail:
114
+
115
+ ```markdown
116
+ ## Validation Audit {date}
117
+ | Metric | Count |
118
+ |--------|-------|
119
+ | Gaps found | {N} |
120
+ | Resolved | {M} |
121
+ | Escalated | {K} |
122
+ ```
123
+
124
+ ## 7. Commit
125
+
126
+ ```bash
127
+ git add {test_files}
128
+ git commit -m "test(phase-${PHASE}): add Nyquist validation tests"
129
+
130
+ node "$HOME/.claude/get-shit-done/bin/gsd-tools.cjs" commit-docs "docs(phase-${PHASE}): add/update validation strategy"
131
+ ```
132
+
133
+ ## 8. Results + Routing
134
+
135
+ **Compliant:**
136
+ ```
137
+ GSD > PHASE {N} IS NYQUIST-COMPLIANT
138
+ All requirements have automated verification.
139
+ ▶ Next: /gsd:audit-milestone
140
+ ```
141
+
142
+ **Partial:**
143
+ ```
144
+ GSD > PHASE {N} VALIDATED (PARTIAL)
145
+ {M} automated, {K} manual-only.
146
+ ▶ Retry: /gsd:validate-phase {N}
147
+ ```
148
+
149
+ Display `/clear` reminder.
150
+
151
+ </process>
152
+
153
+ <success_criteria>
154
+ - [ ] Nyquist config checked (exit if disabled)
155
+ - [ ] Input state detected (A/B/C)
156
+ - [ ] State C exits cleanly
157
+ - [ ] PLAN/SUMMARY files read, requirement map built
158
+ - [ ] Test infrastructure detected
159
+ - [ ] Gaps classified (COVERED/PARTIAL/MISSING)
160
+ - [ ] User gate with gap table
161
+ - [ ] Auditor spawned with complete context
162
+ - [ ] All three return formats handled
163
+ - [ ] VALIDATION.md created or updated
164
+ - [ ] Test files committed separately
165
+ - [ ] Results with routing presented
166
+ </success_criteria>
@@ -108,6 +108,19 @@ Examples:
108
108
  → Expected: "Clicking Reply opens inline composer below comment. Submitting shows reply nested under parent with visual indentation."
109
109
 
110
110
  Skip internal/non-observable items (refactors, type changes, etc.).
111
+
112
+ **Cold-start smoke test injection:**
113
+
114
+ After extracting tests from SUMMARYs, scan the SUMMARY files for modified/created file paths. If ANY path matches these patterns:
115
+
116
+ `server.ts`, `server.js`, `app.ts`, `app.js`, `index.ts`, `index.js`, `main.ts`, `main.js`, `database/*`, `db/*`, `seed/*`, `seeds/*`, `migrations/*`, `startup*`, `docker-compose*`, `Dockerfile*`
117
+
118
+ Then **prepend** this test to the test list:
119
+
120
+ - name: "Cold Start Smoke Test"
121
+ - expected: "Kill any running server/service. Clear ephemeral state (temp DBs, caches, lock files). Start the application from scratch. Server boots without errors, any seed/migration completes, and a primary query (health check, homepage load, or basic API call) returns live data."
122
+
123
+ This catches bugs that only manifest on fresh start — race conditions in startup sequences, silent seed failures, missing environment setup — which pass against warm state but break in production.
111
124
  </step>
112
125
 
113
126
  <step name="create_uat_file">
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "get-shit-done-cc",
3
- "version": "1.22.2",
3
+ "version": "1.22.3",
4
4
  "description": "A meta-prompting, context engineering and spec-driven development system for Claude Code, OpenCode, Gemini and Codex by TÂCHES.",
5
5
  "bin": {
6
6
  "get-shit-done-cc": "bin/install.js"