gsd-pi 2.26.0 → 2.27.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 (171) hide show
  1. package/README.md +43 -6
  2. package/dist/cli.js +4 -2
  3. package/dist/headless.d.ts +3 -0
  4. package/dist/headless.js +136 -8
  5. package/dist/help-text.js +3 -0
  6. package/dist/loader.js +33 -4
  7. package/dist/resources/extensions/bg-shell/index.ts +19 -2
  8. package/dist/resources/extensions/bg-shell/process-manager.ts +45 -0
  9. package/dist/resources/extensions/bg-shell/types.ts +21 -1
  10. package/dist/resources/extensions/gsd/auto/session.ts +224 -0
  11. package/dist/resources/extensions/gsd/auto-budget.ts +32 -0
  12. package/dist/resources/extensions/gsd/auto-dashboard.ts +63 -10
  13. package/dist/resources/extensions/gsd/auto-direct-dispatch.ts +229 -0
  14. package/dist/resources/extensions/gsd/auto-dispatch.ts +23 -10
  15. package/dist/resources/extensions/gsd/auto-model-selection.ts +179 -0
  16. package/dist/resources/extensions/gsd/auto-observability.ts +74 -0
  17. package/dist/resources/extensions/gsd/auto-prompts.ts +0 -1
  18. package/dist/resources/extensions/gsd/auto-timeout-recovery.ts +262 -0
  19. package/dist/resources/extensions/gsd/auto-tool-tracking.ts +54 -0
  20. package/dist/resources/extensions/gsd/auto-unit-closeout.ts +46 -0
  21. package/dist/resources/extensions/gsd/auto-worktree-sync.ts +207 -0
  22. package/dist/resources/extensions/gsd/auto.ts +977 -1551
  23. package/dist/resources/extensions/gsd/commands.ts +3 -3
  24. package/dist/resources/extensions/gsd/dashboard-overlay.ts +47 -72
  25. package/dist/resources/extensions/gsd/doctor-proactive.ts +9 -4
  26. package/dist/resources/extensions/gsd/export-html.ts +1001 -0
  27. package/dist/resources/extensions/gsd/export.ts +49 -1
  28. package/dist/resources/extensions/gsd/git-service.ts +6 -0
  29. package/dist/resources/extensions/gsd/gitignore.ts +4 -1
  30. package/dist/resources/extensions/gsd/guided-flow.ts +24 -5
  31. package/dist/resources/extensions/gsd/index.ts +54 -1
  32. package/dist/resources/extensions/gsd/native-git-bridge.ts +30 -2
  33. package/dist/resources/extensions/gsd/observability-validator.ts +21 -0
  34. package/dist/resources/extensions/gsd/parallel-orchestrator.ts +231 -20
  35. package/dist/resources/extensions/gsd/preferences.ts +62 -1
  36. package/dist/resources/extensions/gsd/prompts/execute-task.md +4 -3
  37. package/dist/resources/extensions/gsd/prompts/system.md +1 -1
  38. package/dist/resources/extensions/gsd/reports.ts +510 -0
  39. package/dist/resources/extensions/gsd/roadmap-slices.ts +1 -1
  40. package/dist/resources/extensions/gsd/skills/gsd-headless/SKILL.md +178 -0
  41. package/dist/resources/extensions/gsd/skills/gsd-headless/references/answer-injection.md +54 -0
  42. package/dist/resources/extensions/gsd/skills/gsd-headless/references/commands.md +59 -0
  43. package/dist/resources/extensions/gsd/skills/gsd-headless/references/multi-session.md +185 -0
  44. package/dist/resources/extensions/gsd/state.ts +30 -0
  45. package/dist/resources/extensions/gsd/templates/task-summary.md +9 -0
  46. package/dist/resources/extensions/gsd/tests/auto-dashboard.test.ts +13 -0
  47. package/dist/resources/extensions/gsd/tests/continue-here.test.ts +81 -0
  48. package/dist/resources/extensions/gsd/tests/derive-state-db.test.ts +5 -0
  49. package/dist/resources/extensions/gsd/tests/derive-state-deps.test.ts +1 -0
  50. package/dist/resources/extensions/gsd/tests/derive-state-draft.test.ts +1 -0
  51. package/dist/resources/extensions/gsd/tests/derive-state.test.ts +10 -1
  52. package/dist/resources/extensions/gsd/tests/dispatch-missing-task-plans.test.ts +132 -0
  53. package/dist/resources/extensions/gsd/tests/doctor-proactive.test.ts +14 -0
  54. package/dist/resources/extensions/gsd/tests/integration-mixed-milestones.test.ts +1 -0
  55. package/dist/resources/extensions/gsd/tests/milestone-transition-worktree.test.ts +1 -1
  56. package/dist/resources/extensions/gsd/tests/native-has-changes-cache.test.ts +61 -0
  57. package/dist/resources/extensions/gsd/tests/network-error-fallback.test.ts +51 -1
  58. package/dist/resources/extensions/gsd/tests/parallel-budget-atomicity.test.ts +331 -0
  59. package/dist/resources/extensions/gsd/tests/parallel-crash-recovery.test.ts +298 -0
  60. package/dist/resources/extensions/gsd/tests/parallel-merge.test.ts +465 -0
  61. package/dist/resources/extensions/gsd/tests/parallel-orchestration.test.ts +39 -10
  62. package/dist/resources/extensions/gsd/tests/plan-slice-prompt.test.ts +71 -0
  63. package/dist/resources/extensions/gsd/tests/replan-slice.test.ts +42 -0
  64. package/dist/resources/extensions/gsd/tests/triage-dispatch.test.ts +9 -9
  65. package/dist/resources/extensions/gsd/tests/verification-evidence.test.ts +743 -0
  66. package/dist/resources/extensions/gsd/tests/verification-gate.test.ts +965 -0
  67. package/dist/resources/extensions/gsd/tests/visualizer-data.test.ts +1 -1
  68. package/dist/resources/extensions/gsd/tests/visualizer-overlay.test.ts +44 -10
  69. package/dist/resources/extensions/gsd/tests/worktree.test.ts +3 -1
  70. package/dist/resources/extensions/gsd/types.ts +38 -0
  71. package/dist/resources/extensions/gsd/verification-evidence.ts +183 -0
  72. package/dist/resources/extensions/gsd/verification-gate.ts +567 -0
  73. package/dist/resources/extensions/gsd/visualizer-data.ts +25 -3
  74. package/dist/resources/extensions/gsd/visualizer-overlay.ts +31 -21
  75. package/dist/resources/extensions/gsd/visualizer-views.ts +15 -66
  76. package/dist/resources/extensions/search-the-web/tool-search.ts +26 -0
  77. package/dist/resources/extensions/shared/format-utils.ts +85 -0
  78. package/dist/resources/extensions/shared/tests/format-utils.test.ts +153 -0
  79. package/dist/resources/extensions/subagent/index.ts +46 -1
  80. package/dist/resources/extensions/subagent/isolation.ts +9 -6
  81. package/package.json +1 -1
  82. package/packages/pi-ai/dist/providers/openai-completions.js +7 -4
  83. package/packages/pi-ai/dist/providers/openai-completions.js.map +1 -1
  84. package/packages/pi-ai/src/providers/openai-completions.ts +7 -4
  85. package/packages/pi-coding-agent/dist/core/lsp/client.d.ts.map +1 -1
  86. package/packages/pi-coding-agent/dist/core/lsp/client.js +7 -0
  87. package/packages/pi-coding-agent/dist/core/lsp/client.js.map +1 -1
  88. package/packages/pi-coding-agent/dist/core/lsp/config.d.ts.map +1 -1
  89. package/packages/pi-coding-agent/dist/core/lsp/config.js +9 -2
  90. package/packages/pi-coding-agent/dist/core/lsp/config.js.map +1 -1
  91. package/packages/pi-coding-agent/src/core/lsp/client.ts +8 -0
  92. package/packages/pi-coding-agent/src/core/lsp/config.ts +9 -2
  93. package/packages/pi-tui/dist/components/editor.d.ts.map +1 -1
  94. package/packages/pi-tui/dist/components/editor.js +1 -1
  95. package/packages/pi-tui/dist/components/editor.js.map +1 -1
  96. package/packages/pi-tui/src/components/editor.ts +3 -1
  97. package/scripts/link-workspace-packages.cjs +22 -6
  98. package/src/resources/extensions/bg-shell/index.ts +19 -2
  99. package/src/resources/extensions/bg-shell/process-manager.ts +45 -0
  100. package/src/resources/extensions/bg-shell/types.ts +21 -1
  101. package/src/resources/extensions/gsd/auto/session.ts +224 -0
  102. package/src/resources/extensions/gsd/auto-budget.ts +32 -0
  103. package/src/resources/extensions/gsd/auto-dashboard.ts +63 -10
  104. package/src/resources/extensions/gsd/auto-direct-dispatch.ts +229 -0
  105. package/src/resources/extensions/gsd/auto-dispatch.ts +23 -10
  106. package/src/resources/extensions/gsd/auto-model-selection.ts +179 -0
  107. package/src/resources/extensions/gsd/auto-observability.ts +74 -0
  108. package/src/resources/extensions/gsd/auto-prompts.ts +0 -1
  109. package/src/resources/extensions/gsd/auto-timeout-recovery.ts +262 -0
  110. package/src/resources/extensions/gsd/auto-tool-tracking.ts +54 -0
  111. package/src/resources/extensions/gsd/auto-unit-closeout.ts +46 -0
  112. package/src/resources/extensions/gsd/auto-worktree-sync.ts +207 -0
  113. package/src/resources/extensions/gsd/auto.ts +977 -1551
  114. package/src/resources/extensions/gsd/commands.ts +3 -3
  115. package/src/resources/extensions/gsd/dashboard-overlay.ts +47 -72
  116. package/src/resources/extensions/gsd/doctor-proactive.ts +9 -4
  117. package/src/resources/extensions/gsd/export-html.ts +1001 -0
  118. package/src/resources/extensions/gsd/export.ts +49 -1
  119. package/src/resources/extensions/gsd/git-service.ts +6 -0
  120. package/src/resources/extensions/gsd/gitignore.ts +4 -1
  121. package/src/resources/extensions/gsd/guided-flow.ts +24 -5
  122. package/src/resources/extensions/gsd/index.ts +54 -1
  123. package/src/resources/extensions/gsd/native-git-bridge.ts +30 -2
  124. package/src/resources/extensions/gsd/observability-validator.ts +21 -0
  125. package/src/resources/extensions/gsd/parallel-orchestrator.ts +231 -20
  126. package/src/resources/extensions/gsd/preferences.ts +62 -1
  127. package/src/resources/extensions/gsd/prompts/execute-task.md +4 -3
  128. package/src/resources/extensions/gsd/prompts/system.md +1 -1
  129. package/src/resources/extensions/gsd/reports.ts +510 -0
  130. package/src/resources/extensions/gsd/roadmap-slices.ts +1 -1
  131. package/src/resources/extensions/gsd/skills/gsd-headless/SKILL.md +178 -0
  132. package/src/resources/extensions/gsd/skills/gsd-headless/references/answer-injection.md +54 -0
  133. package/src/resources/extensions/gsd/skills/gsd-headless/references/commands.md +59 -0
  134. package/src/resources/extensions/gsd/skills/gsd-headless/references/multi-session.md +185 -0
  135. package/src/resources/extensions/gsd/state.ts +30 -0
  136. package/src/resources/extensions/gsd/templates/task-summary.md +9 -0
  137. package/src/resources/extensions/gsd/tests/auto-dashboard.test.ts +13 -0
  138. package/src/resources/extensions/gsd/tests/continue-here.test.ts +81 -0
  139. package/src/resources/extensions/gsd/tests/derive-state-db.test.ts +5 -0
  140. package/src/resources/extensions/gsd/tests/derive-state-deps.test.ts +1 -0
  141. package/src/resources/extensions/gsd/tests/derive-state-draft.test.ts +1 -0
  142. package/src/resources/extensions/gsd/tests/derive-state.test.ts +10 -1
  143. package/src/resources/extensions/gsd/tests/dispatch-missing-task-plans.test.ts +132 -0
  144. package/src/resources/extensions/gsd/tests/doctor-proactive.test.ts +14 -0
  145. package/src/resources/extensions/gsd/tests/integration-mixed-milestones.test.ts +1 -0
  146. package/src/resources/extensions/gsd/tests/milestone-transition-worktree.test.ts +1 -1
  147. package/src/resources/extensions/gsd/tests/native-has-changes-cache.test.ts +61 -0
  148. package/src/resources/extensions/gsd/tests/network-error-fallback.test.ts +51 -1
  149. package/src/resources/extensions/gsd/tests/parallel-budget-atomicity.test.ts +331 -0
  150. package/src/resources/extensions/gsd/tests/parallel-crash-recovery.test.ts +298 -0
  151. package/src/resources/extensions/gsd/tests/parallel-merge.test.ts +465 -0
  152. package/src/resources/extensions/gsd/tests/parallel-orchestration.test.ts +39 -10
  153. package/src/resources/extensions/gsd/tests/plan-slice-prompt.test.ts +71 -0
  154. package/src/resources/extensions/gsd/tests/replan-slice.test.ts +42 -0
  155. package/src/resources/extensions/gsd/tests/triage-dispatch.test.ts +9 -9
  156. package/src/resources/extensions/gsd/tests/verification-evidence.test.ts +743 -0
  157. package/src/resources/extensions/gsd/tests/verification-gate.test.ts +965 -0
  158. package/src/resources/extensions/gsd/tests/visualizer-data.test.ts +1 -1
  159. package/src/resources/extensions/gsd/tests/visualizer-overlay.test.ts +44 -10
  160. package/src/resources/extensions/gsd/tests/worktree.test.ts +3 -1
  161. package/src/resources/extensions/gsd/types.ts +38 -0
  162. package/src/resources/extensions/gsd/verification-evidence.ts +183 -0
  163. package/src/resources/extensions/gsd/verification-gate.ts +567 -0
  164. package/src/resources/extensions/gsd/visualizer-data.ts +25 -3
  165. package/src/resources/extensions/gsd/visualizer-overlay.ts +31 -21
  166. package/src/resources/extensions/gsd/visualizer-views.ts +15 -66
  167. package/src/resources/extensions/search-the-web/tool-search.ts +26 -0
  168. package/src/resources/extensions/shared/format-utils.ts +85 -0
  169. package/src/resources/extensions/shared/tests/format-utils.test.ts +153 -0
  170. package/src/resources/extensions/subagent/index.ts +46 -1
  171. package/src/resources/extensions/subagent/isolation.ts +9 -6
@@ -0,0 +1,178 @@
1
+ ---
2
+ name: gsd-headless
3
+ description: Orchestrate GSD (Get Shit Done) projects programmatically via headless CLI. Use when an agent needs to create milestones from specs, execute software development workflows, monitor task progress, check project status, or control GSD execution (pause/stop/skip/steer). Triggers on requests to "run gsd", "create milestone", "execute project", "check gsd status", "orchestrate development", "run headless workflow", or any programmatic interaction with the GSD project management system. Essential for building orchestrators that coordinate multiple GSD workers.
4
+ ---
5
+
6
+ # GSD Headless Orchestration
7
+
8
+ Run GSD commands without TUI via `gsd headless`. Spawns an RPC child process, auto-responds to UI prompts, streams progress.
9
+
10
+ ## Command Syntax
11
+
12
+ ```bash
13
+ gsd headless [flags] [command] [args...]
14
+ ```
15
+
16
+ **Flags:** `--timeout N` (ms, default 300000), `--json` (JSONL to stdout), `--model ID`, `--verbose`
17
+ **Exit codes:** 0=complete, 1=error/timeout, 2=blocked
18
+
19
+ ## Core Workflows
20
+
21
+ ### 1. Create + Execute a Milestone (end-to-end)
22
+
23
+ ```bash
24
+ gsd headless new-milestone --context spec.md --auto
25
+ ```
26
+
27
+ Reads spec, bootstraps `.gsd/`, creates milestone, then chains into auto-mode executing all phases (discuss → research → plan → execute → summarize → complete).
28
+
29
+ Extra flags for `new-milestone`: `--context <path>` (use `-` for stdin), `--context-text <text>`, `--auto`.
30
+
31
+ ### 2. Run All Queued Work
32
+
33
+ ```bash
34
+ gsd headless auto
35
+ ```
36
+
37
+ Default command. Loops through all pending units until milestone complete or blocked.
38
+
39
+ ### 3. Run One Unit
40
+
41
+ ```bash
42
+ gsd headless next
43
+ ```
44
+
45
+ Execute exactly one unit (task/slice/milestone step), then exit. Ideal for step-by-step orchestration with external decision logic between steps.
46
+
47
+ ### 4. Check Status
48
+
49
+ ```bash
50
+ gsd headless --json status
51
+ ```
52
+
53
+ Returns project state: active milestone/slice/task, phase, progress counts, blockers. Parse the JSONL output for machine-readable state.
54
+
55
+ ### 5. Dispatch Specific Phase
56
+
57
+ ```bash
58
+ gsd headless dispatch research|plan|execute|complete|reassess|uat|replan
59
+ ```
60
+
61
+ Force-route to a specific phase, bypassing normal state-machine routing.
62
+
63
+ ## Orchestrator Patterns
64
+
65
+ ### Poll-and-React Loop
66
+
67
+ ```bash
68
+ # Check status, decide what to do
69
+ STATUS=$(gsd headless --json status 2>/dev/null)
70
+ EXIT=$?
71
+
72
+ case $EXIT in
73
+ 0) echo "Complete" ;;
74
+ 2) echo "Blocked — needs intervention" ;;
75
+ *) echo "Error" ;;
76
+ esac
77
+ ```
78
+
79
+ ### Step-by-Step with Monitoring
80
+
81
+ ```bash
82
+ while true; do
83
+ gsd headless next
84
+ EXIT=$?
85
+ [ $EXIT -ne 0 ] && break
86
+ # Check progress, log, decide whether to continue
87
+ gsd headless --json status
88
+ done
89
+ ```
90
+
91
+ ### Multi-Session Orchestration
92
+
93
+ GSD tracks concurrent workers via file-based IPC in `.gsd/parallel/`. See [references/multi-session.md](references/multi-session.md) for the full architecture.
94
+
95
+ **Quick overview:**
96
+
97
+ Each worker spawns with `GSD_MILESTONE_LOCK=M00X` + its own git worktree. Workers write heartbeats to `.gsd/parallel/<milestoneId>.status.json`. The orchestrator enumerates all status files to get a dashboard of all workers, and sends commands via signal files.
98
+
99
+ ```bash
100
+ # Spawn a worker for milestone M001 in its worktree
101
+ GSD_MILESTONE_LOCK=M001 GSD_PARALLEL_WORKER=1 \
102
+ gsd headless --json auto \
103
+ --cwd .gsd/worktrees/M001 2>worker-M001.log &
104
+
105
+ # Monitor all workers: read .gsd/parallel/*.status.json
106
+ for f in .gsd/parallel/*.status.json; do
107
+ jq '{mid: .milestoneId, state: .state, unit: .currentUnit.id, cost: .cost}' "$f"
108
+ done
109
+
110
+ # Send pause signal to M001
111
+ echo '{"signal":"pause","sentAt":'$(date +%s000)',"from":"coordinator"}' \
112
+ > .gsd/parallel/M001.signal.json
113
+ ```
114
+
115
+ **Status file fields:** `milestoneId`, `pid`, `state` (running/paused/stopped/error), `currentUnit`, `completedUnits`, `cost`, `lastHeartbeat`, `startedAt`, `worktreePath`.
116
+
117
+ **Signal commands:** `pause`, `resume`, `stop`, `rebase`.
118
+
119
+ **Liveness detection:** PID alive check (`kill -0 $pid`) + heartbeat freshness (30s timeout). Stale sessions are auto-cleaned.
120
+
121
+ **For multiple projects:** each project has its own `.gsd/` directory. The orchestrator must track `(projectPath, milestoneId)` tuples externally.
122
+
123
+ ### JSONL Event Stream
124
+
125
+ Use `--json` to get real-time events on stdout for downstream processing:
126
+
127
+ ```bash
128
+ gsd headless --json auto 2>/dev/null | while read -r line; do
129
+ TYPE=$(echo "$line" | jq -r '.type')
130
+ case "$TYPE" in
131
+ tool_execution_start) echo "Tool: $(echo "$line" | jq -r '.toolName')" ;;
132
+ extension_ui_request) echo "GSD: $(echo "$line" | jq -r '.message // .title // empty')" ;;
133
+ agent_end) echo "Session ended" ;;
134
+ esac
135
+ done
136
+ ```
137
+
138
+ Event types: `agent_start`, `agent_end`, `tool_execution_start`, `tool_execution_end`, `extension_ui_request`, `message_update`, `error`.
139
+
140
+ ## Answer Injection
141
+
142
+ Pre-supply answers for non-interactive runs. See [references/answer-injection.md](references/answer-injection.md) for schema and usage.
143
+
144
+ ## GSD Project Structure
145
+
146
+ All state lives in `.gsd/` as markdown files (version-controllable):
147
+
148
+ ```
149
+ .gsd/
150
+ milestones/M001/
151
+ M001-CONTEXT.md # Requirements, scope, decisions
152
+ M001-ROADMAP.md # Slices with tasks, dependencies, checkboxes
153
+ M001-SUMMARY.md # Completion summary
154
+ slices/S01/
155
+ S01-PLAN.md # Task list
156
+ S01-SUMMARY.md # Slice summary with frontmatter
157
+ tasks/T01-PLAN.md # Individual task spec
158
+ ```
159
+
160
+ State is derived from files on disk — checkboxes in ROADMAP.md are the source of truth for completion.
161
+
162
+ ## All Headless Commands
163
+
164
+ Quick reference — see [references/commands.md](references/commands.md) for the complete list.
165
+
166
+ | Command | Purpose |
167
+ |---------|---------|
168
+ | `auto` | Run all queued units (default) |
169
+ | `next` | Run one unit |
170
+ | `status` | Progress dashboard |
171
+ | `new-milestone` | Create milestone from spec |
172
+ | `queue` | Queue/reorder milestones |
173
+ | `history` | View execution history |
174
+ | `stop` / `pause` | Control auto-mode |
175
+ | `dispatch <phase>` | Force specific phase |
176
+ | `skip` / `undo` | Unit control |
177
+ | `doctor` | Health check + auto-fix |
178
+ | `steer <desc>` | Hard-steer plan mid-execution |
@@ -0,0 +1,54 @@
1
+ # Answer Injection
2
+
3
+ Pre-supply answers to eliminate interactive prompts during headless execution.
4
+
5
+ ## Answer File Schema
6
+
7
+ ```json
8
+ {
9
+ "questions": {
10
+ "question_id": "selected_option_label",
11
+ "multi_select_question": ["option_a", "option_b"]
12
+ },
13
+ "secrets": {
14
+ "API_KEY": "sk-...",
15
+ "DATABASE_URL": "postgres://..."
16
+ },
17
+ "defaults": {
18
+ "strategy": "first_option"
19
+ }
20
+ }
21
+ ```
22
+
23
+ ### Fields
24
+
25
+ - **questions**: Map question ID → answer. String for single-select, string[] for multi-select.
26
+ - **secrets**: Map env var name → value. Used for `secure_env_collect` tool calls. Values are never logged.
27
+ - **defaults.strategy**: Fallback for unmatched questions.
28
+ - `"first_option"` — auto-select first available option
29
+ - `"cancel"` — cancel the request
30
+
31
+ ## How It Works
32
+
33
+ Two-phase correlation:
34
+ 1. **Observe** `tool_execution_start` events for `ask_user_questions` — extracts question metadata (ID, options, allowMultiple)
35
+ 2. **Match** subsequent `extension_ui_request` events to metadata, respond with pre-supplied answer
36
+
37
+ Handles out-of-order events (extension_ui_request can arrive before tool_execution_start in RPC mode) via deferred processing queue.
38
+
39
+ ## Without Answer Injection
40
+
41
+ Headless mode has built-in auto-responders:
42
+ - **select** → picks first option
43
+ - **confirm** → auto-confirms
44
+ - **input** → empty string
45
+ - **editor** → returns prefill or empty
46
+
47
+ Answer injection overrides these defaults with specific answers when precision matters.
48
+
49
+ ## Diagnostics
50
+
51
+ The injector tracks stats:
52
+ - `questionsAnswered` / `questionsDefaulted`
53
+ - `secretsProvided` / `secretsMissing`
54
+ - `fireAndForgetConsumed` / `confirmationsHandled`
@@ -0,0 +1,59 @@
1
+ # GSD Commands Reference
2
+
3
+ All commands can be run via `gsd headless [command]`.
4
+
5
+ ## Workflow Commands
6
+
7
+ | Command | Description |
8
+ |---------|-------------|
9
+ | `auto` | Autonomous mode — loop until milestone complete (default) |
10
+ | `next` | Step mode — execute one unit, then exit |
11
+ | `stop` | Stop auto-mode gracefully |
12
+ | `pause` | Pause auto-mode (preserves state, resumable) |
13
+ | `new-milestone` | Create milestone from specification (requires `--context`) |
14
+ | `dispatch <phase>` | Force-dispatch: research, plan, execute, complete, reassess, uat, replan |
15
+
16
+ ## Status & Monitoring
17
+
18
+ | Command | Description |
19
+ |---------|-------------|
20
+ | `status` | Progress dashboard (active unit, phase, blockers) |
21
+ | `visualize` | Workflow visualizer (deps, metrics, timeline) |
22
+ | `history` | Execution history (supports --cost, --phase, --model, limit) |
23
+
24
+ ## Unit Control
25
+
26
+ | Command | Description |
27
+ |---------|-------------|
28
+ | `skip` | Prevent a unit from auto-mode dispatch |
29
+ | `undo` | Revert last completed unit (--force flag) |
30
+ | `steer <desc>` | Hard-steer plan documents during execution |
31
+ | `queue` | Queue and reorder future milestones |
32
+ | `capture` | Fire-and-forget thought capture |
33
+ | `triage` | Manually trigger triage of pending captures |
34
+
35
+ ## Configuration & Health
36
+
37
+ | Command | Description |
38
+ |---------|-------------|
39
+ | `prefs` | Manage preferences (global/project/status/wizard/setup) |
40
+ | `config` | Set API keys for external tools |
41
+ | `doctor` | Runtime health checks with auto-fix |
42
+ | `hooks` | Show configured post-unit and pre-dispatch hooks |
43
+ | `knowledge <rule\|pattern\|lesson>` | Add persistent project knowledge |
44
+ | `cleanup` | Remove merged branches or snapshots |
45
+ | `export` | Export results (--json, --markdown) |
46
+ | `migrate` | Migrate v1 .planning directory to .gsd format |
47
+
48
+ ## Phases
49
+
50
+ GSD workflows progress through these phases:
51
+ `pre-planning` → `needs-discussion` → `discussing` → `researching` → `planning` → `executing` → `verifying` → `summarizing` → `advancing` → `validating-milestone` → `completing-milestone` → `complete`
52
+
53
+ Special phases: `paused`, `blocked`, `replanning-slice`
54
+
55
+ ## Hierarchy
56
+
57
+ - **Milestone**: Shippable version (4-10 slices, 1-4 weeks)
58
+ - **Slice**: One demoable vertical capability (1-7 tasks, 1-3 days)
59
+ - **Task**: One context-window-sized unit of work (one session)
@@ -0,0 +1,185 @@
1
+ # Multi-Session Orchestration
2
+
3
+ How to run and monitor multiple concurrent GSD sessions.
4
+
5
+ ## Architecture
6
+
7
+ GSD uses **file-based IPC** — no sockets or ports. All coordination happens through JSON files in `.gsd/parallel/`.
8
+
9
+ ```
10
+ .gsd/parallel/
11
+ ├── M001.status.json # Worker heartbeat + state
12
+ ├── M001.signal.json # Coordinator → worker commands (ephemeral)
13
+ ├── M002.status.json
14
+ ├── M003.status.json
15
+ └── ...
16
+ ```
17
+
18
+ ## Worker Isolation
19
+
20
+ Each worker gets:
21
+ 1. **`GSD_MILESTONE_LOCK=M00X`** — state derivation only sees this milestone
22
+ 2. **`GSD_PARALLEL_WORKER=1`** — prevents nested parallel spawns
23
+ 3. **Own git worktree** at `.gsd/worktrees/M00X/` — branch `milestone/M00X`
24
+
25
+ Workers cannot interfere with each other. Each has its own filesystem and git branch.
26
+
27
+ ## Status File Schema
28
+
29
+ Written atomically (`.tmp` + rename) by each worker at `.gsd/parallel/<milestoneId>.status.json`:
30
+
31
+ ```json
32
+ {
33
+ "milestoneId": "M001",
34
+ "pid": 12345,
35
+ "state": "running",
36
+ "currentUnit": {
37
+ "type": "task",
38
+ "id": "T03",
39
+ "startedAt": 1710000000000
40
+ },
41
+ "completedUnits": 7,
42
+ "cost": 1.23,
43
+ "lastHeartbeat": 1710000015000,
44
+ "startedAt": 1710000000000,
45
+ "worktreePath": ".gsd/worktrees/M001"
46
+ }
47
+ ```
48
+
49
+ **States:** `running`, `paused`, `stopped`, `error`
50
+
51
+ ## Signal Files
52
+
53
+ Coordinator writes to `.gsd/parallel/<milestoneId>.signal.json`. Worker consumes and deletes on next dispatch cycle.
54
+
55
+ ```json
56
+ {
57
+ "signal": "pause",
58
+ "sentAt": 1710000020000,
59
+ "from": "coordinator"
60
+ }
61
+ ```
62
+
63
+ **Signals:** `pause`, `resume`, `stop`, `rebase`
64
+
65
+ ## Spawning Workers
66
+
67
+ ```bash
68
+ # Spawn worker in its worktree
69
+ GSD_MILESTONE_LOCK=M001 \
70
+ GSD_PARALLEL_WORKER=1 \
71
+ GSD_BIN_PATH=$(which gsd) \
72
+ gsd --mode json --print "/gsd auto" \
73
+ 2>logs/M001.log &
74
+ WORKER_PID=$!
75
+ ```
76
+
77
+ Workers emit NDJSON on stdout. Parse `message_end` events for cost tracking:
78
+ ```bash
79
+ # Extract cost from worker output
80
+ gsd --mode json --print "/gsd auto" | while read -r line; do
81
+ COST=$(echo "$line" | jq -r 'select(.type=="message_end") | .message.usage.cost.total // empty')
82
+ [ -n "$COST" ] && echo "Cost update: $COST"
83
+ done
84
+ ```
85
+
86
+ ## Monitoring All Workers
87
+
88
+ ```bash
89
+ # Dashboard: enumerate all status files
90
+ for f in .gsd/parallel/*.status.json; do
91
+ [ -f "$f" ] || continue
92
+ jq -r '[.milestoneId, .state, (.currentUnit.id // "idle"), "\(.cost | tostring)$"] | join("\t")' "$f"
93
+ done
94
+
95
+ # Liveness check
96
+ for f in .gsd/parallel/*.status.json; do
97
+ PID=$(jq -r '.pid' "$f")
98
+ MID=$(jq -r '.milestoneId' "$f")
99
+ if kill -0 "$PID" 2>/dev/null; then
100
+ echo "$MID: alive (pid=$PID)"
101
+ else
102
+ echo "$MID: DEAD (pid=$PID) — cleanup needed"
103
+ rm "$f"
104
+ fi
105
+ done
106
+ ```
107
+
108
+ ## Sending Commands
109
+
110
+ ```bash
111
+ # Pause a worker
112
+ send_signal() {
113
+ local MID=$1 SIGNAL=$2
114
+ echo "{\"signal\":\"$SIGNAL\",\"sentAt\":$(date +%s000),\"from\":\"coordinator\"}" \
115
+ > ".gsd/parallel/${MID}.signal.json"
116
+ }
117
+
118
+ send_signal M001 pause
119
+ send_signal M002 stop
120
+ send_signal M003 resume
121
+ ```
122
+
123
+ ## Budget Enforcement
124
+
125
+ Track aggregate cost across all workers:
126
+ ```bash
127
+ TOTAL=$(jq -s 'map(.cost) | add // 0' .gsd/parallel/*.status.json)
128
+ CEILING=50.00
129
+ if (( $(echo "$TOTAL > $CEILING" | bc -l) )); then
130
+ echo "Budget exceeded ($TOTAL > $CEILING) — stopping all"
131
+ for f in .gsd/parallel/*.status.json; do
132
+ MID=$(jq -r '.milestoneId' "$f")
133
+ send_signal "$MID" stop
134
+ done
135
+ fi
136
+ ```
137
+
138
+ ## Stale Session Cleanup
139
+
140
+ A session is stale when:
141
+ - PID is dead (`kill -0 $pid` fails), OR
142
+ - `lastHeartbeat` is older than 30 seconds
143
+
144
+ ```bash
145
+ NOW=$(date +%s000)
146
+ STALE_THRESHOLD=30000
147
+ for f in .gsd/parallel/*.status.json; do
148
+ PID=$(jq -r '.pid' "$f")
149
+ HB=$(jq -r '.lastHeartbeat' "$f")
150
+ AGE=$((NOW - HB))
151
+ if ! kill -0 "$PID" 2>/dev/null || [ "$AGE" -gt "$STALE_THRESHOLD" ]; then
152
+ echo "Stale: $(jq -r '.milestoneId' "$f") — removing"
153
+ rm "$f"
154
+ fi
155
+ done
156
+ ```
157
+
158
+ ## Multi-Project Orchestration
159
+
160
+ Within one project, milestones are tracked automatically in `.gsd/parallel/`. For orchestrating across **multiple projects**, maintain an external registry:
161
+
162
+ ```json
163
+ {
164
+ "sessions": [
165
+ { "project": "/path/to/project-a", "milestoneId": "M001" },
166
+ { "project": "/path/to/project-b", "milestoneId": "M001" },
167
+ { "project": "/path/to/project-b", "milestoneId": "M002" }
168
+ ]
169
+ }
170
+ ```
171
+
172
+ Then poll each project's `.gsd/parallel/` directory. GSD has no cross-project awareness — the orchestrator must bridge this gap.
173
+
174
+ ## Built-in Parallel Commands
175
+
176
+ Inside an interactive GSD session, these commands manage the parallel orchestrator:
177
+
178
+ | Command | Description |
179
+ |---------|-------------|
180
+ | `/gsd parallel start` | Analyze eligibility, spawn workers |
181
+ | `/gsd parallel status` | Show all workers, costs, progress |
182
+ | `/gsd parallel stop [MID]` | Stop one or all workers |
183
+ | `/gsd parallel pause [MID]` | Pause without killing |
184
+ | `/gsd parallel resume [MID]` | Resume paused worker |
185
+ | `/gsd parallel merge [MID]` | Merge completed milestone branch |
@@ -26,6 +26,7 @@ import {
26
26
  resolveSlicePath,
27
27
  resolveSliceFile,
28
28
  resolveTaskFile,
29
+ resolveTasksDir,
29
30
  resolveGsdRootFile,
30
31
  gsdRoot,
31
32
  } from './paths.js';
@@ -34,6 +35,7 @@ import { milestoneIdSort, findMilestoneIds } from './guided-flow.js';
34
35
  import { nativeBatchParseGsdFiles, type BatchParsedFile } from './native-parser-bridge.js';
35
36
 
36
37
  import { join, resolve } from 'path';
38
+ import { existsSync, readdirSync } from 'node:fs';
37
39
  import { debugCount, debugTime } from './debug-logger.js';
38
40
 
39
41
  // ─── Query Functions ───────────────────────────────────────────────────────
@@ -573,6 +575,34 @@ async function _deriveStateImpl(basePath: string): Promise<GSDState> {
573
575
  title: activeTaskEntry.title,
574
576
  };
575
577
 
578
+ // ── Task plan file check (#909) ──────────────────────────────────────
579
+ // The slice plan may reference tasks but per-task plan files may be
580
+ // missing — e.g. when the slice plan was pre-created during roadmapping.
581
+ // If the tasks dir exists but has literally zero files (empty dir from
582
+ // mkdir), fall back to planning so plan-slice generates task plans.
583
+ const tasksDir = resolveTasksDir(basePath, activeMilestone.id, activeSlice.id);
584
+ if (tasksDir && existsSync(tasksDir) && slicePlan.tasks.length > 0) {
585
+ const allFiles = readdirSync(tasksDir).filter(f => f.endsWith(".md"));
586
+ if (allFiles.length === 0) {
587
+ return {
588
+ activeMilestone,
589
+ activeSlice,
590
+ activeTask: null,
591
+ phase: 'planning',
592
+ recentDecisions: [],
593
+ blockers: [],
594
+ nextAction: `Task plan files missing for ${activeSlice.id}. Run plan-slice to generate task plans.`,
595
+ registry,
596
+ requirements,
597
+ progress: {
598
+ milestones: milestoneProgress,
599
+ slices: sliceProgress,
600
+ tasks: taskProgress,
601
+ },
602
+ };
603
+ }
604
+ }
605
+
576
606
  // ── Blocker detection: scan completed task summaries ──────────────────
577
607
  // If any completed task has blocker_discovered: true and no REPLAN.md
578
608
  // exists yet, transition to replanning-slice instead of executing.
@@ -37,6 +37,15 @@ blocker_discovered: false
37
37
 
38
38
  {{whatWasVerifiedAndHow — commands run, tests passed, behavior confirmed}}
39
39
 
40
+ ## Verification Evidence
41
+
42
+ <!-- Populated from verification gate output. If the gate ran, fill in the table below.
43
+ If no gate ran (e.g., no verification commands discovered), note that. -->
44
+
45
+ | # | Command | Exit Code | Verdict | Duration |
46
+ |---|---------|-----------|---------|----------|
47
+ | {{row}} | {{command}} | {{exitCode}} | {{verdict}} | {{duration}} |
48
+
40
49
  ## Diagnostics
41
50
 
42
51
  {{howToInspectWhatThisTaskBuiltLater — status surfaces, logs, error shapes, failure artifacts, or none}}
@@ -7,6 +7,7 @@ import {
7
7
  describeNextUnit,
8
8
  formatAutoElapsed,
9
9
  formatWidgetTokens,
10
+ estimateTimeRemaining,
10
11
  } from "../auto-dashboard.ts";
11
12
 
12
13
  // ─── unitVerb ─────────────────────────────────────────────────────────────
@@ -151,3 +152,15 @@ test("formatWidgetTokens formats millions with M", () => {
151
152
  assert.equal(formatWidgetTokens(10_000_000), "10M");
152
153
  assert.equal(formatWidgetTokens(25_000_000), "25M");
153
154
  });
155
+
156
+ // ─── estimateTimeRemaining ──────────────────────────────────────────────
157
+
158
+ test("estimateTimeRemaining returns null when no ledger data", () => {
159
+ // With no active auto-mode session, ledger is empty
160
+ const result = estimateTimeRemaining();
161
+ assert.equal(result, null);
162
+ });
163
+
164
+ test("estimateTimeRemaining is exported and callable", () => {
165
+ assert.equal(typeof estimateTimeRemaining, "function");
166
+ });
@@ -201,4 +201,85 @@ describe("continue-here", () => {
201
201
  }
202
202
  });
203
203
  });
204
+
205
+ describe("context-pressure monitor integration", () => {
206
+ it("should fire wrap-up when context >= threshold and mark continueHereFired", async () => {
207
+ const { writeUnitRuntimeRecord, readUnitRuntimeRecord, clearUnitRuntimeRecord } = await import("../unit-runtime.js");
208
+ const fs = await import("node:fs");
209
+ const path = await import("node:path");
210
+ const os = await import("node:os");
211
+
212
+ const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "continue-here-monitor-"));
213
+ try {
214
+ // Simulate the monitor's one-shot logic:
215
+ // 1. Write initial runtime record (continueHereFired=false)
216
+ const startedAt = Date.now();
217
+ writeUnitRuntimeRecord(tmpDir, "execute-task", "M001/S01/T01", startedAt, {
218
+ phase: "dispatched",
219
+ wrapupWarningSent: false,
220
+ });
221
+
222
+ const budget = computeBudgets(128_000);
223
+ const threshold = budget.continueThresholdPercent;
224
+
225
+ // Simulate the monitor poll: context at 75% (above threshold)
226
+ const contextPercent = 75;
227
+ const runtime = readUnitRuntimeRecord(tmpDir, "execute-task", "M001/S01/T01");
228
+ assert.ok(runtime, "runtime record should exist");
229
+ assert.equal(runtime!.continueHereFired, false, "initially false");
230
+
231
+ // Check: should fire
232
+ const shouldFire = !runtime!.continueHereFired
233
+ && contextPercent >= threshold;
234
+ assert.ok(shouldFire, "should fire when context >= threshold and not yet fired");
235
+
236
+ // Mark as fired (what the monitor does)
237
+ writeUnitRuntimeRecord(tmpDir, "execute-task", "M001/S01/T01", startedAt, {
238
+ continueHereFired: true,
239
+ });
240
+
241
+ // Verify one-shot: second poll should NOT fire
242
+ const runtime2 = readUnitRuntimeRecord(tmpDir, "execute-task", "M001/S01/T01");
243
+ assert.ok(runtime2, "runtime record should still exist");
244
+ assert.equal(runtime2!.continueHereFired, true, "should be marked as fired");
245
+
246
+ const shouldFireAgain = !runtime2!.continueHereFired
247
+ && contextPercent >= threshold;
248
+ assert.equal(shouldFireAgain, false, "must not fire again — one-shot guard");
249
+
250
+ // Clean up
251
+ clearUnitRuntimeRecord(tmpDir, "execute-task", "M001/S01/T01");
252
+ } finally {
253
+ fs.rmSync(tmpDir, { recursive: true, force: true });
254
+ }
255
+ });
256
+
257
+ it("should not fire when context is below threshold", () => {
258
+ const budget = computeBudgets(200_000);
259
+ const threshold = budget.continueThresholdPercent;
260
+
261
+ // Simulate monitor poll with context at 50%
262
+ const contextPercent = 50;
263
+ const continueHereFired = false;
264
+ const shouldFire = !continueHereFired && contextPercent >= threshold;
265
+ assert.equal(shouldFire, false, "50% should not trigger continue-here");
266
+ });
267
+
268
+ it("should not fire when contextUsage is null/undefined", () => {
269
+ const budget = computeBudgets(128_000);
270
+ const threshold = budget.continueThresholdPercent;
271
+
272
+ // Simulate the full guard chain from the monitor
273
+ const usageUndefined = undefined as { percent: number | null } | undefined;
274
+ const shouldFire1 = usageUndefined != null
275
+ && usageUndefined.percent != null
276
+ && usageUndefined.percent >= threshold;
277
+ assert.equal(shouldFire1, false, "undefined usage must not fire");
278
+
279
+ const usageNullPercent: { percent: number | null } = { percent: null };
280
+ const shouldFire2 = usageNullPercent.percent != null
281
+ && usageNullPercent.percent >= threshold;
282
+ assert.equal(shouldFire2, false, "null percent must not fire");
283
+ });
284
+ });
204
285
  });