@ahmed118glitch/get-shit-done-codex 1.18.3 → 1.19.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (32) hide show
  1. package/CHANGELOG.md +18 -1
  2. package/README.md +101 -85
  3. package/agents/gsd-phase-researcher.md +1 -1
  4. package/agents/gsd-roadmapper.md +39 -5
  5. package/agents/gsd-verifier.md +2 -2
  6. package/bin/install-codex.js +100 -100
  7. package/bin/install.js +65 -8
  8. package/commands/gsd/discuss-phase.md +1 -1
  9. package/commands/gsd/reapply-patches.md +3 -3
  10. package/commands/gsd/verify-work.md +1 -1
  11. package/get-shit-done/bin/gsd-tools.js +106 -19
  12. package/get-shit-done/bin/gsd-tools.test.js +44 -0
  13. package/get-shit-done/references/model-profile-resolution.md +4 -2
  14. package/get-shit-done/references/model-profiles.md +3 -0
  15. package/get-shit-done/references/questioning.md +1 -0
  16. package/get-shit-done/templates/UAT.md +1 -1
  17. package/get-shit-done/templates/context.md +2 -2
  18. package/get-shit-done/templates/planner-subagent-prompt.md +4 -4
  19. package/get-shit-done/templates/research.md +2 -2
  20. package/get-shit-done/templates/verification-report.md +1 -1
  21. package/get-shit-done/workflows/diagnose-issues.md +1 -1
  22. package/get-shit-done/workflows/discovery-phase.md +1 -1
  23. package/get-shit-done/workflows/discuss-phase.md +3 -3
  24. package/get-shit-done/workflows/execute-phase.md +51 -2
  25. package/get-shit-done/workflows/help.md +4 -4
  26. package/get-shit-done/workflows/new-milestone.md +1 -1
  27. package/get-shit-done/workflows/new-project.md +3 -3
  28. package/get-shit-done/workflows/plan-phase.md +1 -1
  29. package/get-shit-done/workflows/progress.md +2 -2
  30. package/get-shit-done/workflows/update.md +14 -12
  31. package/get-shit-done/workflows/verify-work.md +3 -3
  32. package/package.json +1 -1
@@ -835,14 +835,33 @@ function cmdRoadmapGetPhase(cwd, phaseNum, raw) {
835
835
  // Escape special regex chars in phase number, handle decimal
836
836
  const escapedPhase = phaseNum.replace(/\./g, '\\.');
837
837
 
838
- // Match "### Phase X:" or "### Phase X.Y:" with optional name
838
+ // Match "## Phase X:" or "### Phase X:" with optional name
839
839
  const phasePattern = new RegExp(
840
- `###\\s*Phase\\s+${escapedPhase}:\\s*([^\\n]+)`,
840
+ `#{2,3}\\s*Phase\\s+${escapedPhase}:\\s*([^\\n]+)`,
841
841
  'i'
842
842
  );
843
843
  const headerMatch = content.match(phasePattern);
844
844
 
845
845
  if (!headerMatch) {
846
+ // Fallback: check if phase exists in summary list but missing detail section
847
+ const checklistPattern = new RegExp(
848
+ `-\\s*\\[[ x]\\]\\s*\\*\\*Phase\\s+${escapedPhase}:\\s*([^*]+)\\*\\*`,
849
+ 'i'
850
+ );
851
+ const checklistMatch = content.match(checklistPattern);
852
+
853
+ if (checklistMatch) {
854
+ // Phase exists in summary but missing detail section - malformed ROADMAP
855
+ output({
856
+ found: false,
857
+ phase_number: phaseNum,
858
+ phase_name: checklistMatch[1].trim(),
859
+ error: 'malformed_roadmap',
860
+ message: `Phase ${phaseNum} exists in summary list but missing "### Phase ${phaseNum}:" detail section. ROADMAP.md needs both formats.`
861
+ }, raw, '');
862
+ return;
863
+ }
864
+
846
865
  output({ found: false, phase_number: phaseNum }, raw, '');
847
866
  return;
848
867
  }
@@ -850,9 +869,9 @@ function cmdRoadmapGetPhase(cwd, phaseNum, raw) {
850
869
  const phaseName = headerMatch[1].trim();
851
870
  const headerIndex = headerMatch.index;
852
871
 
853
- // Find the end of this section (next ### or end of file)
872
+ // Find the end of this section (next ## or ### phase header, or end of file)
854
873
  const restOfContent = content.slice(headerIndex);
855
- const nextHeaderMatch = restOfContent.match(/\n###\s+Phase\s+\d/i);
874
+ const nextHeaderMatch = restOfContent.match(/\n#{2,3}\s+Phase\s+\d/i);
856
875
  const sectionEnd = nextHeaderMatch
857
876
  ? headerIndex + nextHeaderMatch.index
858
877
  : content.length;
@@ -1328,7 +1347,8 @@ function cmdResolveModel(cwd, agentType, raw) {
1328
1347
  return;
1329
1348
  }
1330
1349
 
1331
- const model = agentModels[profile] || agentModels['balanced'] || 'sonnet';
1350
+ const resolved = agentModels[profile] || agentModels['balanced'] || 'sonnet';
1351
+ const model = resolved === 'opus' ? 'inherit' : resolved;
1332
1352
  const result = { model, profile };
1333
1353
  output(result, raw, model);
1334
1354
  }
@@ -2430,8 +2450,8 @@ function cmdRoadmapAnalyze(cwd, raw) {
2430
2450
  const content = fs.readFileSync(roadmapPath, 'utf-8');
2431
2451
  const phasesDir = path.join(cwd, '.planning', 'phases');
2432
2452
 
2433
- // Extract all phase headings: ### Phase N: Name
2434
- const phasePattern = /###\s*Phase\s+(\d+(?:\.\d+)?)\s*:\s*([^\n]+)/gi;
2453
+ // Extract all phase headings: ## Phase N: Name or ### Phase N: Name
2454
+ const phasePattern = /#{2,3}\s*Phase\s+(\d+(?:\.\d+)?)\s*:\s*([^\n]+)/gi;
2435
2455
  const phases = [];
2436
2456
  let match;
2437
2457
 
@@ -2442,7 +2462,7 @@ function cmdRoadmapAnalyze(cwd, raw) {
2442
2462
  // Extract goal from the section
2443
2463
  const sectionStart = match.index;
2444
2464
  const restOfContent = content.slice(sectionStart);
2445
- const nextHeader = restOfContent.match(/\n###\s+Phase\s+\d/i);
2465
+ const nextHeader = restOfContent.match(/\n#{2,3}\s+Phase\s+\d/i);
2446
2466
  const sectionEnd = nextHeader ? sectionStart + nextHeader.index : content.length;
2447
2467
  const section = content.slice(sectionStart, sectionEnd);
2448
2468
 
@@ -2520,6 +2540,16 @@ function cmdRoadmapAnalyze(cwd, raw) {
2520
2540
  const totalSummaries = phases.reduce((sum, p) => sum + p.summary_count, 0);
2521
2541
  const completedPhases = phases.filter(p => p.disk_status === 'complete').length;
2522
2542
 
2543
+ // Detect phases in summary list without detail sections (malformed ROADMAP)
2544
+ const checklistPattern = /-\s*\[[ x]\]\s*\*\*Phase\s+(\d+(?:\.\d+)?)/gi;
2545
+ const checklistPhases = new Set();
2546
+ let checklistMatch;
2547
+ while ((checklistMatch = checklistPattern.exec(content)) !== null) {
2548
+ checklistPhases.add(checklistMatch[1]);
2549
+ }
2550
+ const detailPhases = new Set(phases.map(p => p.number));
2551
+ const missingDetails = [...checklistPhases].filter(p => !detailPhases.has(p));
2552
+
2523
2553
  const result = {
2524
2554
  milestones,
2525
2555
  phases,
@@ -2530,6 +2560,7 @@ function cmdRoadmapAnalyze(cwd, raw) {
2530
2560
  progress_percent: totalPlans > 0 ? Math.round((totalSummaries / totalPlans) * 100) : 0,
2531
2561
  current_phase: currentPhase ? currentPhase.number : null,
2532
2562
  next_phase: nextPhase ? nextPhase.number : null,
2563
+ missing_phase_details: missingDetails.length > 0 ? missingDetails : null,
2533
2564
  };
2534
2565
 
2535
2566
  output(result, raw);
@@ -2551,7 +2582,7 @@ function cmdPhaseAdd(cwd, description, raw) {
2551
2582
  const slug = generateSlugInternal(description);
2552
2583
 
2553
2584
  // Find highest integer phase number
2554
- const phasePattern = /###\s*Phase\s+(\d+)(?:\.\d+)?:/gi;
2585
+ const phasePattern = /#{2,3}\s*Phase\s+(\d+)(?:\.\d+)?:/gi;
2555
2586
  let maxPhase = 0;
2556
2587
  let m;
2557
2588
  while ((m = phasePattern.exec(content)) !== null) {
@@ -2609,7 +2640,7 @@ function cmdPhaseInsert(cwd, afterPhase, description, raw) {
2609
2640
 
2610
2641
  // Verify target phase exists
2611
2642
  const afterPhaseEscaped = afterPhase.replace(/\./g, '\\.');
2612
- const targetPattern = new RegExp(`###\\s*Phase\\s+${afterPhaseEscaped}:`, 'i');
2643
+ const targetPattern = new RegExp(`#{2,3}\\s*Phase\\s+${afterPhaseEscaped}:`, 'i');
2613
2644
  if (!targetPattern.test(content)) {
2614
2645
  error(`Phase ${afterPhase} not found in ROADMAP.md`);
2615
2646
  }
@@ -2641,7 +2672,7 @@ function cmdPhaseInsert(cwd, afterPhase, description, raw) {
2641
2672
  const phaseEntry = `\n### Phase ${decimalPhase}: ${description} (INSERTED)\n\n**Goal:** [Urgent work - to be planned]\n**Depends on:** Phase ${afterPhase}\n**Plans:** 0 plans\n\nPlans:\n- [ ] TBD (run /gsd:plan-phase ${decimalPhase} to break down)\n`;
2642
2673
 
2643
2674
  // Insert after the target phase section
2644
- const headerPattern = new RegExp(`(###\\s*Phase\\s+${afterPhaseEscaped}:[^\\n]*\\n)`, 'i');
2675
+ const headerPattern = new RegExp(`(#{2,3}\\s*Phase\\s+${afterPhaseEscaped}:[^\\n]*\\n)`, 'i');
2645
2676
  const headerMatch = content.match(headerPattern);
2646
2677
  if (!headerMatch) {
2647
2678
  error(`Could not find Phase ${afterPhase} header`);
@@ -2649,7 +2680,7 @@ function cmdPhaseInsert(cwd, afterPhase, description, raw) {
2649
2680
 
2650
2681
  const headerIdx = content.indexOf(headerMatch[0]);
2651
2682
  const afterHeader = content.slice(headerIdx + headerMatch[0].length);
2652
- const nextPhaseMatch = afterHeader.match(/\n###\s+Phase\s+\d/i);
2683
+ const nextPhaseMatch = afterHeader.match(/\n#{2,3}\s+Phase\s+\d/i);
2653
2684
 
2654
2685
  let insertIdx;
2655
2686
  if (nextPhaseMatch) {
@@ -2832,7 +2863,7 @@ function cmdPhaseRemove(cwd, targetPhase, options, raw) {
2832
2863
  // Remove the target phase section
2833
2864
  const targetEscaped = targetPhase.replace(/\./g, '\\.');
2834
2865
  const sectionPattern = new RegExp(
2835
- `\\n?###\\s*Phase\\s+${targetEscaped}\\s*:[\\s\\S]*?(?=\\n###\\s+Phase\\s+\\d|$)`,
2866
+ `\\n?#{2,3}\\s*Phase\\s+${targetEscaped}\\s*:[\\s\\S]*?(?=\\n#{2,3}\\s+Phase\\s+\\d|$)`,
2836
2867
  'i'
2837
2868
  );
2838
2869
  roadmapContent = roadmapContent.replace(sectionPattern, '');
@@ -2858,9 +2889,9 @@ function cmdPhaseRemove(cwd, targetPhase, options, raw) {
2858
2889
  const oldPad = oldStr.padStart(2, '0');
2859
2890
  const newPad = newStr.padStart(2, '0');
2860
2891
 
2861
- // Phase headings: ### Phase 18: → ### Phase 17:
2892
+ // Phase headings: ## Phase 18: or ### Phase 18: → ## Phase 17: or ### Phase 17:
2862
2893
  roadmapContent = roadmapContent.replace(
2863
- new RegExp(`(###\\s*Phase\\s+)${oldStr}(\\s*:)`, 'gi'),
2894
+ new RegExp(`(#{2,3}\\s*Phase\\s+)${oldStr}(\\s*:)`, 'gi'),
2864
2895
  `$1${newStr}$2`
2865
2896
  );
2866
2897
 
@@ -2971,7 +3002,7 @@ function cmdPhaseComplete(cwd, phaseNum, raw) {
2971
3002
 
2972
3003
  // Update plan count in phase section
2973
3004
  const planCountPattern = new RegExp(
2974
- `(###\\s*Phase\\s+${phaseEscaped}[\\s\\S]*?\\*\\*Plans:\\*\\*\\s*)[^\\n]+`,
3005
+ `(#{2,3}\\s*Phase\\s+${phaseEscaped}[\\s\\S]*?\\*\\*Plans:\\*\\*\\s*)[^\\n]+`,
2975
3006
  'i'
2976
3007
  );
2977
3008
  roadmapContent = roadmapContent.replace(
@@ -3206,7 +3237,7 @@ function cmdValidateConsistency(cwd, raw) {
3206
3237
 
3207
3238
  // Extract phases from ROADMAP
3208
3239
  const roadmapPhases = new Set();
3209
- const phasePattern = /###\s*Phase\s+(\d+(?:\.\d+)?)\s*:/gi;
3240
+ const phasePattern = /#{2,3}\s*Phase\s+(\d+(?:\.\d+)?)\s*:/gi;
3210
3241
  let m;
3211
3242
  while ((m = phasePattern.exec(roadmapContent)) !== null) {
3212
3243
  roadmapPhases.add(m[1]);
@@ -3479,7 +3510,8 @@ function resolveModelInternal(cwd, agentType) {
3479
3510
  const profile = config.model_profile || 'balanced';
3480
3511
  const agentModels = MODEL_PROFILES[agentType];
3481
3512
  if (!agentModels) return 'sonnet';
3482
- return agentModels[profile] || agentModels['balanced'] || 'sonnet';
3513
+ const resolved = agentModels[profile] || agentModels['balanced'] || 'sonnet';
3514
+ return resolved === 'opus' ? 'inherit' : resolved;
3483
3515
  }
3484
3516
 
3485
3517
  function findPhaseInternal(cwd, phase) {
@@ -3533,6 +3565,40 @@ function findPhaseInternal(cwd, phase) {
3533
3565
  }
3534
3566
  }
3535
3567
 
3568
+ function getRoadmapPhaseInternal(cwd, phaseNum) {
3569
+ if (!phaseNum) return null;
3570
+ const roadmapPath = path.join(cwd, '.planning', 'ROADMAP.md');
3571
+ if (!fs.existsSync(roadmapPath)) return null;
3572
+
3573
+ try {
3574
+ const content = fs.readFileSync(roadmapPath, 'utf-8');
3575
+ const escapedPhase = phaseNum.toString().replace(/\./g, '\\.');
3576
+ const phasePattern = new RegExp(`#{2,3}\\s*Phase\\s+${escapedPhase}:\\s*([^\\n]+)`, 'i');
3577
+ const headerMatch = content.match(phasePattern);
3578
+ if (!headerMatch) return null;
3579
+
3580
+ const phaseName = headerMatch[1].trim();
3581
+ const headerIndex = headerMatch.index;
3582
+ const restOfContent = content.slice(headerIndex);
3583
+ const nextHeaderMatch = restOfContent.match(/\n#{2,3}\s+Phase\s+\d/i);
3584
+ const sectionEnd = nextHeaderMatch ? headerIndex + nextHeaderMatch.index : content.length;
3585
+ const section = content.slice(headerIndex, sectionEnd).trim();
3586
+
3587
+ const goalMatch = section.match(/\*\*Goal:\*\*\s*([^\n]+)/i);
3588
+ const goal = goalMatch ? goalMatch[1].trim() : null;
3589
+
3590
+ return {
3591
+ found: true,
3592
+ phase_number: phaseNum.toString(),
3593
+ phase_name: phaseName,
3594
+ goal,
3595
+ section,
3596
+ };
3597
+ } catch {
3598
+ return null;
3599
+ }
3600
+ }
3601
+
3536
3602
  function pathExistsInternal(cwd, targetPath) {
3537
3603
  const fullPath = path.isAbsolute(targetPath) ? targetPath : path.join(cwd, targetPath);
3538
3604
  try {
@@ -3918,7 +3984,28 @@ function cmdInitVerifyWork(cwd, phase, raw) {
3918
3984
 
3919
3985
  function cmdInitPhaseOp(cwd, phase, raw) {
3920
3986
  const config = loadConfig(cwd);
3921
- const phaseInfo = findPhaseInternal(cwd, phase);
3987
+ let phaseInfo = findPhaseInternal(cwd, phase);
3988
+
3989
+ // Fallback to ROADMAP.md if no directory exists (e.g., Plans: TBD)
3990
+ if (!phaseInfo) {
3991
+ const roadmapPhase = getRoadmapPhaseInternal(cwd, phase);
3992
+ if (roadmapPhase?.found) {
3993
+ const phaseName = roadmapPhase.phase_name;
3994
+ phaseInfo = {
3995
+ found: true,
3996
+ directory: null,
3997
+ phase_number: roadmapPhase.phase_number,
3998
+ phase_name: phaseName,
3999
+ phase_slug: phaseName ? phaseName.toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/^-+|-+$/g, '') : null,
4000
+ plans: [],
4001
+ summaries: [],
4002
+ incomplete_plans: [],
4003
+ has_research: false,
4004
+ has_context: false,
4005
+ has_verification: false,
4006
+ };
4007
+ }
4008
+ }
3922
4009
 
3923
4010
  const result = {
3924
4011
  // Config
@@ -520,6 +520,50 @@ This phase covers:
520
520
  assert.strictEqual(output.found, false, 'should return not found');
521
521
  assert.strictEqual(output.error, 'ROADMAP.md not found', 'should explain why');
522
522
  });
523
+
524
+ test('accepts ## phase headers (two hashes)', () => {
525
+ fs.writeFileSync(
526
+ path.join(tmpDir, '.planning', 'ROADMAP.md'),
527
+ `# Roadmap v1.0
528
+
529
+ ## Phase 1: Foundation
530
+ **Goal:** Set up project infrastructure
531
+ **Plans:** 2 plans
532
+
533
+ ## Phase 2: API
534
+ **Goal:** Build REST API
535
+ `
536
+ );
537
+
538
+ const result = runGsdTools('roadmap get-phase 1', tmpDir);
539
+ assert.ok(result.success, `Command failed: ${result.error}`);
540
+
541
+ const output = JSON.parse(result.output);
542
+ assert.strictEqual(output.found, true, 'phase with ## header should be found');
543
+ assert.strictEqual(output.phase_name, 'Foundation', 'phase name extracted');
544
+ assert.strictEqual(output.goal, 'Set up project infrastructure', 'goal extracted');
545
+ });
546
+
547
+ test('detects malformed ROADMAP with summary list but no detail sections', () => {
548
+ fs.writeFileSync(
549
+ path.join(tmpDir, '.planning', 'ROADMAP.md'),
550
+ `# Roadmap v1.0
551
+
552
+ ## Phases
553
+
554
+ - [ ] **Phase 1: Foundation** - Set up project
555
+ - [ ] **Phase 2: API** - Build REST API
556
+ `
557
+ );
558
+
559
+ const result = runGsdTools('roadmap get-phase 1', tmpDir);
560
+ assert.ok(result.success, `Command failed: ${result.error}`);
561
+
562
+ const output = JSON.parse(result.output);
563
+ assert.strictEqual(output.found, false, 'phase should not be found');
564
+ assert.strictEqual(output.error, 'malformed_roadmap', 'should identify malformed roadmap');
565
+ assert.ok(output.message.includes('missing'), 'should explain the issue');
566
+ });
523
567
  });
524
568
 
525
569
  // ─────────────────────────────────────────────────────────────────────────────
@@ -20,13 +20,15 @@ Look up the agent in the table for the resolved profile. Pass the model paramete
20
20
  Task(
21
21
  prompt="...",
22
22
  subagent_type="gsd-planner",
23
- model="{resolved_model}" # e.g., "opus" for quality profile
23
+ model="{resolved_model}" # "inherit", "sonnet", or "haiku"
24
24
  )
25
25
  ```
26
26
 
27
+ **Note:** Opus-tier agents resolve to `"inherit"` (not `"opus"`). This causes the agent to use the parent session's model, avoiding conflicts with organization policies that may block specific opus versions.
28
+
27
29
  ## Usage
28
30
 
29
31
  1. Resolve once at orchestration start
30
32
  2. Store the profile value
31
33
  3. Look up each agent's model from the table when spawning
32
- 4. Pass model parameter to each Task call
34
+ 4. Pass model parameter to each Task call (values: `"inherit"`, `"sonnet"`, `"haiku"`)
@@ -71,3 +71,6 @@ Verification requires goal-backward reasoning - checking if code *delivers* what
71
71
 
72
72
  **Why Haiku for gsd-codebase-mapper?**
73
73
  Read-only exploration and pattern extraction. No reasoning required, just structured output from file contents.
74
+
75
+ **Why `inherit` instead of passing `opus` directly?**
76
+ Claude Code's `"opus"` alias maps to a specific model version. Organizations may block older opus versions while allowing newer ones. GSD returns `"inherit"` for opus-tier agents, causing them to use whatever opus version the user has configured in their session. This avoids version conflicts and silent fallbacks to Sonnet.
@@ -79,6 +79,7 @@ Use AskUserQuestion to help users think by presenting concrete options to react
79
79
  - Generic categories ("Technical", "Business", "Other")
80
80
  - Leading options that presume an answer
81
81
  - Too many options (2-4 is ideal)
82
+ - Headers longer than 12 characters (hard limit — validation will reject them)
82
83
 
83
84
  **Example — vague answer:**
84
85
  User says "it should be fast"
@@ -1,6 +1,6 @@
1
1
  # UAT Template
2
2
 
3
- Template for `.planning/phases/XX-name/{phase}-UAT.md` — persistent UAT session tracking.
3
+ Template for `.planning/phases/XX-name/{phase_num}-UAT.md` — persistent UAT session tracking.
4
4
 
5
5
  ---
6
6
 
@@ -1,6 +1,6 @@
1
1
  # Phase Context Template
2
2
 
3
- Template for `.planning/phases/XX-name/{phase}-CONTEXT.md` - captures implementation decisions for a phase.
3
+ Template for `.planning/phases/XX-name/{phase_num}-CONTEXT.md` - captures implementation decisions for a phase.
4
4
 
5
5
  **Purpose:** Document decisions that downstream agents need. Researcher uses this to know WHAT to investigate. Planner uses this to know WHAT choices are locked vs flexible.
6
6
 
@@ -276,7 +276,7 @@ The output should answer: "What does the researcher need to investigate? What ch
276
276
  - "Easy to use"
277
277
 
278
278
  **After creation:**
279
- - File lives in phase directory: `.planning/phases/XX-name/{phase}-CONTEXT.md`
279
+ - File lives in phase directory: `.planning/phases/XX-name/{phase_num}-CONTEXT.md`
280
280
  - `gsd-phase-researcher` uses decisions to focus investigation
281
281
  - `gsd-planner` uses decisions + research to create executable tasks
282
282
  - Downstream agents should NOT need to ask the user again about captured decisions
@@ -22,14 +22,14 @@ Template for spawning gsd-planner agent. The agent contains all planning experti
22
22
  @.planning/REQUIREMENTS.md
23
23
 
24
24
  **Phase Context (if exists):**
25
- @.planning/phases/{phase_dir}/{phase}-CONTEXT.md
25
+ @.planning/phases/{phase_dir}/{phase_num}-CONTEXT.md
26
26
 
27
27
  **Research (if exists):**
28
- @.planning/phases/{phase_dir}/{phase}-RESEARCH.md
28
+ @.planning/phases/{phase_dir}/{phase_num}-RESEARCH.md
29
29
 
30
30
  **Gap Closure (if --gaps mode):**
31
- @.planning/phases/{phase_dir}/{phase}-VERIFICATION.md
32
- @.planning/phases/{phase_dir}/{phase}-UAT.md
31
+ @.planning/phases/{phase_dir}/{phase_num}-VERIFICATION.md
32
+ @.planning/phases/{phase_dir}/{phase_num}-UAT.md
33
33
 
34
34
  </planning_context>
35
35
 
@@ -1,6 +1,6 @@
1
1
  # Research Template
2
2
 
3
- Template for `.planning/phases/XX-name/{phase}-RESEARCH.md` - comprehensive ecosystem research before planning.
3
+ Template for `.planning/phases/XX-name/{phase_num}-RESEARCH.md` - comprehensive ecosystem research before planning.
4
4
 
5
5
  **Purpose:** Document what Claude needs to know to implement a phase well - not just "which library" but "how do experts build this."
6
6
 
@@ -547,6 +547,6 @@ function useVehicleControls(rigidBodyRef) {
547
547
  - Code examples can be referenced in task actions
548
548
 
549
549
  **After creation:**
550
- - File lives in phase directory: `.planning/phases/XX-name/{phase}-RESEARCH.md`
550
+ - File lives in phase directory: `.planning/phases/XX-name/{phase_num}-RESEARCH.md`
551
551
  - Referenced during planning workflow
552
552
  - plan-phase loads it automatically when present
@@ -1,6 +1,6 @@
1
1
  # Verification Report Template
2
2
 
3
- Template for `.planning/phases/XX-name/{phase}-VERIFICATION.md` — phase goal verification results.
3
+ Template for `.planning/phases/XX-name/{phase_num}-VERIFICATION.md` — phase goal verification results.
4
4
 
5
5
  ---
6
6
 
@@ -158,7 +158,7 @@ Update status in frontmatter to "diagnosed".
158
158
 
159
159
  Commit the updated UAT.md:
160
160
  ```bash
161
- node ~/.claude/get-shit-done/bin/gsd-tools.js commit "docs({phase}): add root causes from diagnosis" --files ".planning/phases/XX-name/{phase}-UAT.md"
161
+ node ~/.claude/get-shit-done/bin/gsd-tools.js commit "docs({phase_num}): add root causes from diagnosis" --files ".planning/phases/XX-name/{phase_num}-UAT.md"
162
162
  ```
163
163
  </step>
164
164
 
@@ -216,7 +216,7 @@ After creating DISCOVERY.md, check confidence level.
216
216
  If confidence is LOW:
217
217
  Use AskUserQuestion:
218
218
 
219
- - header: "Low Confidence"
219
+ - header: "Low Conf."
220
220
  - question: "Discovery confidence is LOW: [reason]. How would you like to proceed?"
221
221
  - options:
222
222
  - "Dig deeper" - Do more research before planning
@@ -136,7 +136,7 @@ ls ${phase_dir}/*-CONTEXT.md 2>/dev/null
136
136
 
137
137
  **If exists:**
138
138
  Use AskUserQuestion:
139
- - header: "Existing context"
139
+ - header: "Context"
140
140
  - question: "Phase [X] already has context. What do you want to do?"
141
141
  - options:
142
142
  - "Update it" — Review and revise existing context
@@ -240,13 +240,13 @@ Ask 4 questions per area before offering to continue or move on. Each answer oft
240
240
  ```
241
241
 
242
242
  2. **Ask 4 questions using AskUserQuestion:**
243
- - header: "[Area]"
243
+ - header: "[Area]" (max 12 chars — abbreviate if needed)
244
244
  - question: Specific decision for this area
245
245
  - options: 2-3 concrete choices (AskUserQuestion adds "Other" automatically)
246
246
  - Include "You decide" as an option when reasonable — captures Claude discretion
247
247
 
248
248
  3. **After 4 questions, check:**
249
- - header: "[Area]"
249
+ - header: "[Area]" (max 12 chars)
250
250
  - question: "More questions about [area], or move to next?"
251
251
  - options: "More questions" / "Next area"
252
252
 
@@ -226,6 +226,55 @@ After all waves:
226
226
  ```
227
227
  </step>
228
228
 
229
+ <step name="close_parent_artifacts">
230
+ **For decimal/polish phases only (X.Y pattern):** Close the feedback loop by resolving parent UAT and debug artifacts.
231
+
232
+ **Skip if** phase number has no decimal (e.g., `3`, `04`) — only applies to gap-closure phases like `4.1`, `03.1`.
233
+
234
+ **1. Detect decimal phase and derive parent:**
235
+ ```bash
236
+ # Check if phase_number contains a decimal
237
+ if [[ "$PHASE_NUMBER" == *.* ]]; then
238
+ PARENT_PHASE="${PHASE_NUMBER%%.*}"
239
+ fi
240
+ ```
241
+
242
+ **2. Find parent UAT file:**
243
+ ```bash
244
+ find .planning/phases -path "*${PARENT_PHASE}*/*-UAT.md" -type f 2>/dev/null
245
+ ```
246
+
247
+ **If no parent UAT found:** Skip this step (gap-closure may have been triggered by VERIFICATION.md instead).
248
+
249
+ **3. Update UAT gap statuses:**
250
+
251
+ Read the parent UAT file's `## Gaps` section. For each gap entry with `status: failed`:
252
+ - Update to `status: resolved`
253
+
254
+ **4. Update UAT frontmatter:**
255
+
256
+ If all gaps now have `status: resolved`:
257
+ - Update frontmatter `status: diagnosed` → `status: resolved`
258
+ - Update frontmatter `updated:` timestamp
259
+
260
+ **5. Resolve referenced debug sessions:**
261
+
262
+ For each gap that has a `debug_session:` field:
263
+ - Read the debug session file
264
+ - Update frontmatter `status:` → `resolved`
265
+ - Update frontmatter `updated:` timestamp
266
+ - Move to resolved directory:
267
+ ```bash
268
+ mkdir -p .planning/debug/resolved
269
+ mv .planning/debug/{slug}.md .planning/debug/resolved/
270
+ ```
271
+
272
+ **6. Commit updated artifacts:**
273
+ ```bash
274
+ node ~/.claude/get-shit-done/bin/gsd-tools.js commit "docs(phase-${PARENT_PHASE}): resolve UAT gaps and debug sessions after ${PHASE_NUMBER} gap closure" --files .planning/phases/*${PARENT_PHASE}*/*-UAT.md .planning/debug/resolved/*.md
275
+ ```
276
+ </step>
277
+
229
278
  <step name="verify_phase_goal">
230
279
  Verify phase achieved its GOAL, not just completed tasks.
231
280
 
@@ -267,7 +316,7 @@ All automated checks passed. {N} items need human testing:
267
316
  ## ⚠ Phase {X}: {Name} — Gaps Found
268
317
 
269
318
  **Score:** {N}/{M} must-haves verified
270
- **Report:** {phase_dir}/{phase}-VERIFICATION.md
319
+ **Report:** {phase_dir}/{phase_num}-VERIFICATION.md
271
320
 
272
321
  ### What's Missing
273
322
  {Gap summaries from VERIFICATION.md}
@@ -279,7 +328,7 @@ All automated checks passed. {N} items need human testing:
279
328
 
280
329
  <sub>`/clear` first → fresh context window</sub>
281
330
 
282
- Also: `cat {phase_dir}/{phase}-VERIFICATION.md` — full report
331
+ Also: `cat {phase_dir}/{phase_num}-VERIFICATION.md` — full report
283
332
  Also: `/gsd:verify-work {X}` — manual testing first
284
333
  ```
285
334
 
@@ -17,9 +17,9 @@ Display the complete GSD command reference. Output ONLY the reference content. D
17
17
 
18
18
  GSD evolves fast. Update periodically:
19
19
 
20
- ```bash
21
- npx --yes --package=@ahmed118glitch/get-shit-done-codex@latest get-shit-done-cc
22
- ```
20
+ ```bash
21
+ npx --yes --package=@ahmed118glitch/get-shit-done-codex@latest get-shit-done-cc
22
+ ```
23
23
 
24
24
  ## Core Workflow
25
25
 
@@ -322,7 +322,7 @@ Update GSD to latest version with changelog preview.
322
322
  - Displays changelog entries for versions you've missed
323
323
  - Highlights breaking changes
324
324
  - Confirms before running install
325
- - Better than raw `npx --yes --package=@ahmed118glitch/get-shit-done-codex@latest get-shit-done-cc`
325
+ - Better than raw `npx --yes --package=@ahmed118glitch/get-shit-done-codex@latest get-shit-done-cc`
326
326
 
327
327
  Usage: `/gsd:update`
328
328
 
@@ -200,7 +200,7 @@ Present features by category:
200
200
 
201
201
  **If no research:** Gather requirements through conversation. Ask: "What are the main things users need to do with [new features]?" Clarify, probe for related capabilities, group into categories.
202
202
 
203
- **Scope each category** via AskUserQuestion (multiSelect: true):
203
+ **Scope each category** via AskUserQuestion (multiSelect: true, header max 12 chars):
204
204
  - "[Feature 1]" — [brief description]
205
205
  - "[Feature 2]" — [brief description]
206
206
  - "None for this milestone" — Defer entire category
@@ -59,7 +59,7 @@ git init
59
59
  **If `needs_codebase_map` is true** (from init — existing code detected but no codebase map):
60
60
 
61
61
  Use AskUserQuestion:
62
- - header: "Existing Code"
62
+ - header: "Codebase"
63
63
  - question: "I detected existing code in this directory. Would you like to map the codebase first?"
64
64
  - options:
65
65
  - "Map codebase first" — Run /gsd:map-codebase to understand existing architecture (Recommended)
@@ -303,7 +303,7 @@ questions: [
303
303
  ]
304
304
  },
305
305
  {
306
- header: "Model Profile",
306
+ header: "AI Models",
307
307
  question: "Which AI models for planning agents?",
308
308
  multiSelect: false,
309
309
  options: [
@@ -663,7 +663,7 @@ For each capability mentioned:
663
663
 
664
664
  For each category, use AskUserQuestion:
665
665
 
666
- - header: "[Category name]"
666
+ - header: "[Category]" (max 12 chars)
667
667
  - question: "Which [category] features are in v1?"
668
668
  - multiSelect: true
669
669
  - options:
@@ -104,7 +104,7 @@ IMPORTANT: If CONTEXT.md exists below, it contains user decisions from /gsd:disc
104
104
  </additional_context>
105
105
 
106
106
  <output>
107
- Write to: {phase_dir}/{phase}-RESEARCH.md
107
+ Write to: {phase_dir}/{phase_num}-RESEARCH.md
108
108
  </output>
109
109
  ```
110
110
 
@@ -194,7 +194,7 @@ Read its `<objective>` section.
194
194
 
195
195
  **Route B: Phase needs planning**
196
196
 
197
- Check if `{phase}-CONTEXT.md` exists in phase directory.
197
+ Check if `{phase_num}-CONTEXT.md` exists in phase directory.
198
198
 
199
199
  **If CONTEXT.md exists:**
200
200
 
@@ -246,7 +246,7 @@ UAT.md exists with gaps (diagnosed issues). User needs to plan fixes.
246
246
 
247
247
  ## ⚠ UAT Gaps Found
248
248
 
249
- **{phase}-UAT.md** has {N} gaps requiring fixes.
249
+ **{phase_num}-UAT.md** has {N} gaps requiring fixes.
250
250
 
251
251
  `/gsd:plan-phase {phase} --gaps`
252
252