gsd-pi 2.37.1 → 2.38.0-dev.4d4d14a

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 (222) hide show
  1. package/README.md +1 -1
  2. package/dist/app-paths.js +1 -1
  3. package/dist/cli.js +9 -0
  4. package/dist/extension-discovery.d.ts +5 -3
  5. package/dist/extension-discovery.js +14 -9
  6. package/dist/extension-registry.js +2 -2
  7. package/dist/onboarding.js +1 -0
  8. package/dist/remote-questions-config.js +2 -2
  9. package/dist/resources/extensions/browser-tools/package.json +3 -1
  10. package/dist/resources/extensions/cmux/index.js +55 -1
  11. package/dist/resources/extensions/context7/package.json +1 -1
  12. package/dist/resources/extensions/env-utils.js +29 -0
  13. package/dist/resources/extensions/get-secrets-from-user.js +5 -24
  14. package/dist/resources/extensions/github-sync/cli.js +284 -0
  15. package/dist/resources/extensions/github-sync/index.js +73 -0
  16. package/dist/resources/extensions/github-sync/mapping.js +67 -0
  17. package/dist/resources/extensions/github-sync/sync.js +424 -0
  18. package/dist/resources/extensions/github-sync/templates.js +118 -0
  19. package/dist/resources/extensions/github-sync/types.js +7 -0
  20. package/dist/resources/extensions/google-search/package.json +3 -1
  21. package/dist/resources/extensions/gsd/auto/session.js +6 -23
  22. package/dist/resources/extensions/gsd/auto-dispatch.js +74 -9
  23. package/dist/resources/extensions/gsd/auto-loop.js +149 -170
  24. package/dist/resources/extensions/gsd/auto-post-unit.js +105 -68
  25. package/dist/resources/extensions/gsd/auto-prompts.js +98 -33
  26. package/dist/resources/extensions/gsd/auto-recovery.js +37 -1
  27. package/dist/resources/extensions/gsd/auto-start.js +13 -2
  28. package/dist/resources/extensions/gsd/auto-worktree-sync.js +13 -5
  29. package/dist/resources/extensions/gsd/auto.js +143 -96
  30. package/dist/resources/extensions/gsd/captures.js +9 -1
  31. package/dist/resources/extensions/gsd/commands-extensions.js +3 -2
  32. package/dist/resources/extensions/gsd/commands-handlers.js +16 -3
  33. package/dist/resources/extensions/gsd/commands-prefs-wizard.js +1 -1
  34. package/dist/resources/extensions/gsd/commands.js +22 -2
  35. package/dist/resources/extensions/gsd/context-budget.js +2 -10
  36. package/dist/resources/extensions/gsd/detection.js +1 -2
  37. package/dist/resources/extensions/gsd/docs/preferences-reference.md +0 -2
  38. package/dist/resources/extensions/gsd/doctor-checks.js +82 -0
  39. package/dist/resources/extensions/gsd/doctor-environment.js +78 -0
  40. package/dist/resources/extensions/gsd/doctor-format.js +15 -0
  41. package/dist/resources/extensions/gsd/doctor-providers.js +62 -12
  42. package/dist/resources/extensions/gsd/doctor.js +184 -11
  43. package/dist/resources/extensions/gsd/export.js +1 -1
  44. package/dist/resources/extensions/gsd/files.js +43 -2
  45. package/dist/resources/extensions/gsd/forensics.js +1 -1
  46. package/dist/resources/extensions/gsd/git-service.js +8 -1
  47. package/dist/resources/extensions/gsd/index.js +24 -20
  48. package/dist/resources/extensions/gsd/migrate/parsers.js +1 -1
  49. package/dist/resources/extensions/gsd/observability-validator.js +24 -0
  50. package/dist/resources/extensions/gsd/package.json +1 -1
  51. package/dist/resources/extensions/gsd/preferences-models.js +0 -12
  52. package/dist/resources/extensions/gsd/preferences-types.js +3 -2
  53. package/dist/resources/extensions/gsd/preferences-validation.js +101 -11
  54. package/dist/resources/extensions/gsd/preferences.js +8 -5
  55. package/dist/resources/extensions/gsd/prompts/discuss.md +11 -14
  56. package/dist/resources/extensions/gsd/prompts/execute-task.md +2 -2
  57. package/dist/resources/extensions/gsd/prompts/guided-discuss-milestone.md +11 -12
  58. package/dist/resources/extensions/gsd/prompts/guided-discuss-slice.md +8 -10
  59. package/dist/resources/extensions/gsd/prompts/guided-resume-task.md +1 -1
  60. package/dist/resources/extensions/gsd/prompts/plan-slice.md +2 -1
  61. package/dist/resources/extensions/gsd/prompts/queue.md +4 -8
  62. package/dist/resources/extensions/gsd/prompts/reactive-execute.md +44 -0
  63. package/dist/resources/extensions/gsd/prompts/run-uat.md +25 -10
  64. package/dist/resources/extensions/gsd/prompts/workflow-start.md +2 -2
  65. package/dist/resources/extensions/gsd/reactive-graph.js +227 -0
  66. package/dist/resources/extensions/gsd/repo-identity.js +21 -4
  67. package/dist/resources/extensions/gsd/resource-version.js +2 -1
  68. package/dist/resources/extensions/gsd/state.js +1 -1
  69. package/dist/resources/extensions/gsd/templates/task-plan.md +11 -3
  70. package/dist/resources/extensions/gsd/visualizer-data.js +1 -1
  71. package/dist/resources/extensions/gsd/worktree.js +35 -16
  72. package/dist/resources/extensions/remote-questions/status.js +2 -1
  73. package/dist/resources/extensions/remote-questions/store.js +2 -1
  74. package/dist/resources/extensions/search-the-web/provider.js +2 -1
  75. package/dist/resources/extensions/subagent/index.js +12 -3
  76. package/dist/resources/extensions/subagent/isolation.js +2 -1
  77. package/dist/resources/extensions/ttsr/rule-loader.js +2 -1
  78. package/dist/resources/extensions/universal-config/package.json +1 -1
  79. package/dist/welcome-screen.d.ts +12 -0
  80. package/dist/welcome-screen.js +53 -0
  81. package/package.json +2 -1
  82. package/packages/pi-ai/dist/env-api-keys.js +13 -0
  83. package/packages/pi-ai/dist/env-api-keys.js.map +1 -1
  84. package/packages/pi-ai/dist/models.generated.d.ts +172 -0
  85. package/packages/pi-ai/dist/models.generated.d.ts.map +1 -1
  86. package/packages/pi-ai/dist/models.generated.js +172 -0
  87. package/packages/pi-ai/dist/models.generated.js.map +1 -1
  88. package/packages/pi-ai/dist/providers/anthropic-shared.d.ts +64 -0
  89. package/packages/pi-ai/dist/providers/anthropic-shared.d.ts.map +1 -0
  90. package/packages/pi-ai/dist/providers/anthropic-shared.js +668 -0
  91. package/packages/pi-ai/dist/providers/anthropic-shared.js.map +1 -0
  92. package/packages/pi-ai/dist/providers/anthropic-vertex.d.ts +5 -0
  93. package/packages/pi-ai/dist/providers/anthropic-vertex.d.ts.map +1 -0
  94. package/packages/pi-ai/dist/providers/anthropic-vertex.js +85 -0
  95. package/packages/pi-ai/dist/providers/anthropic-vertex.js.map +1 -0
  96. package/packages/pi-ai/dist/providers/anthropic.d.ts +4 -30
  97. package/packages/pi-ai/dist/providers/anthropic.d.ts.map +1 -1
  98. package/packages/pi-ai/dist/providers/anthropic.js +47 -764
  99. package/packages/pi-ai/dist/providers/anthropic.js.map +1 -1
  100. package/packages/pi-ai/dist/providers/register-builtins.d.ts.map +1 -1
  101. package/packages/pi-ai/dist/providers/register-builtins.js +6 -0
  102. package/packages/pi-ai/dist/providers/register-builtins.js.map +1 -1
  103. package/packages/pi-ai/dist/types.d.ts +2 -2
  104. package/packages/pi-ai/dist/types.d.ts.map +1 -1
  105. package/packages/pi-ai/dist/types.js.map +1 -1
  106. package/packages/pi-ai/dist/utils/oauth/anthropic.js +2 -2
  107. package/packages/pi-ai/dist/utils/oauth/anthropic.js.map +1 -1
  108. package/packages/pi-ai/package.json +1 -0
  109. package/packages/pi-ai/src/env-api-keys.ts +14 -0
  110. package/packages/pi-ai/src/models.generated.ts +172 -0
  111. package/packages/pi-ai/src/providers/anthropic-shared.ts +761 -0
  112. package/packages/pi-ai/src/providers/anthropic-vertex.ts +130 -0
  113. package/packages/pi-ai/src/providers/anthropic.ts +76 -868
  114. package/packages/pi-ai/src/providers/register-builtins.ts +7 -0
  115. package/packages/pi-ai/src/types.ts +2 -0
  116. package/packages/pi-ai/src/utils/oauth/anthropic.ts +2 -2
  117. package/packages/pi-coding-agent/dist/core/model-resolver.d.ts.map +1 -1
  118. package/packages/pi-coding-agent/dist/core/model-resolver.js +1 -0
  119. package/packages/pi-coding-agent/dist/core/model-resolver.js.map +1 -1
  120. package/packages/pi-coding-agent/dist/core/package-manager.d.ts.map +1 -1
  121. package/packages/pi-coding-agent/dist/core/package-manager.js +8 -4
  122. package/packages/pi-coding-agent/dist/core/package-manager.js.map +1 -1
  123. package/packages/pi-coding-agent/package.json +1 -1
  124. package/packages/pi-coding-agent/src/core/model-resolver.ts +1 -0
  125. package/packages/pi-coding-agent/src/core/package-manager.ts +8 -4
  126. package/pkg/package.json +1 -1
  127. package/src/resources/extensions/cmux/index.ts +57 -1
  128. package/src/resources/extensions/env-utils.ts +31 -0
  129. package/src/resources/extensions/get-secrets-from-user.ts +5 -24
  130. package/src/resources/extensions/github-sync/cli.ts +364 -0
  131. package/src/resources/extensions/github-sync/index.ts +93 -0
  132. package/src/resources/extensions/github-sync/mapping.ts +81 -0
  133. package/src/resources/extensions/github-sync/sync.ts +556 -0
  134. package/src/resources/extensions/github-sync/templates.ts +183 -0
  135. package/src/resources/extensions/github-sync/tests/cli.test.ts +20 -0
  136. package/src/resources/extensions/github-sync/tests/commit-linking.test.ts +39 -0
  137. package/src/resources/extensions/github-sync/tests/mapping.test.ts +104 -0
  138. package/src/resources/extensions/github-sync/tests/templates.test.ts +110 -0
  139. package/src/resources/extensions/github-sync/types.ts +47 -0
  140. package/src/resources/extensions/gsd/auto/session.ts +7 -25
  141. package/src/resources/extensions/gsd/auto-dispatch.ts +99 -8
  142. package/src/resources/extensions/gsd/auto-loop.ts +207 -252
  143. package/src/resources/extensions/gsd/auto-post-unit.ts +82 -39
  144. package/src/resources/extensions/gsd/auto-prompts.ts +132 -36
  145. package/src/resources/extensions/gsd/auto-recovery.ts +42 -0
  146. package/src/resources/extensions/gsd/auto-start.ts +18 -2
  147. package/src/resources/extensions/gsd/auto-worktree-sync.ts +15 -4
  148. package/src/resources/extensions/gsd/auto.ts +139 -101
  149. package/src/resources/extensions/gsd/captures.ts +10 -1
  150. package/src/resources/extensions/gsd/commands-extensions.ts +4 -2
  151. package/src/resources/extensions/gsd/commands-handlers.ts +17 -2
  152. package/src/resources/extensions/gsd/commands-prefs-wizard.ts +1 -1
  153. package/src/resources/extensions/gsd/commands.ts +24 -2
  154. package/src/resources/extensions/gsd/context-budget.ts +2 -12
  155. package/src/resources/extensions/gsd/detection.ts +2 -2
  156. package/src/resources/extensions/gsd/docs/preferences-reference.md +0 -2
  157. package/src/resources/extensions/gsd/doctor-checks.ts +75 -0
  158. package/src/resources/extensions/gsd/doctor-environment.ts +82 -1
  159. package/src/resources/extensions/gsd/doctor-format.ts +20 -0
  160. package/src/resources/extensions/gsd/doctor-providers.ts +64 -10
  161. package/src/resources/extensions/gsd/doctor-types.ts +16 -1
  162. package/src/resources/extensions/gsd/doctor.ts +177 -13
  163. package/src/resources/extensions/gsd/export.ts +1 -1
  164. package/src/resources/extensions/gsd/files.ts +47 -2
  165. package/src/resources/extensions/gsd/forensics.ts +1 -1
  166. package/src/resources/extensions/gsd/git-service.ts +13 -1
  167. package/src/resources/extensions/gsd/index.ts +24 -17
  168. package/src/resources/extensions/gsd/migrate/parsers.ts +1 -1
  169. package/src/resources/extensions/gsd/observability-validator.ts +27 -0
  170. package/src/resources/extensions/gsd/preferences-models.ts +0 -12
  171. package/src/resources/extensions/gsd/preferences-types.ts +9 -5
  172. package/src/resources/extensions/gsd/preferences-validation.ts +92 -11
  173. package/src/resources/extensions/gsd/preferences.ts +8 -5
  174. package/src/resources/extensions/gsd/prompts/discuss.md +11 -14
  175. package/src/resources/extensions/gsd/prompts/execute-task.md +2 -2
  176. package/src/resources/extensions/gsd/prompts/guided-discuss-milestone.md +11 -12
  177. package/src/resources/extensions/gsd/prompts/guided-discuss-slice.md +8 -10
  178. package/src/resources/extensions/gsd/prompts/guided-resume-task.md +1 -1
  179. package/src/resources/extensions/gsd/prompts/plan-slice.md +2 -1
  180. package/src/resources/extensions/gsd/prompts/queue.md +4 -8
  181. package/src/resources/extensions/gsd/prompts/reactive-execute.md +44 -0
  182. package/src/resources/extensions/gsd/prompts/run-uat.md +25 -10
  183. package/src/resources/extensions/gsd/prompts/workflow-start.md +2 -2
  184. package/src/resources/extensions/gsd/reactive-graph.ts +289 -0
  185. package/src/resources/extensions/gsd/repo-identity.ts +23 -4
  186. package/src/resources/extensions/gsd/resource-version.ts +3 -1
  187. package/src/resources/extensions/gsd/state.ts +1 -1
  188. package/src/resources/extensions/gsd/templates/task-plan.md +11 -3
  189. package/src/resources/extensions/gsd/tests/agent-end-retry.test.ts +21 -18
  190. package/src/resources/extensions/gsd/tests/auto-loop.test.ts +16 -37
  191. package/src/resources/extensions/gsd/tests/cmux.test.ts +93 -0
  192. package/src/resources/extensions/gsd/tests/doctor-enhancements.test.ts +266 -0
  193. package/src/resources/extensions/gsd/tests/doctor-providers.test.ts +191 -3
  194. package/src/resources/extensions/gsd/tests/plan-quality-validator.test.ts +111 -0
  195. package/src/resources/extensions/gsd/tests/preferences.test.ts +2 -7
  196. package/src/resources/extensions/gsd/tests/prompt-contracts.test.ts +59 -0
  197. package/src/resources/extensions/gsd/tests/reactive-executor.test.ts +511 -0
  198. package/src/resources/extensions/gsd/tests/reactive-graph.test.ts +299 -0
  199. package/src/resources/extensions/gsd/tests/repo-identity-worktree.test.ts +21 -1
  200. package/src/resources/extensions/gsd/tests/run-uat.test.ts +11 -3
  201. package/src/resources/extensions/gsd/tests/worktree.test.ts +47 -0
  202. package/src/resources/extensions/gsd/types.ts +43 -1
  203. package/src/resources/extensions/gsd/visualizer-data.ts +1 -1
  204. package/src/resources/extensions/gsd/worktree.ts +35 -15
  205. package/src/resources/extensions/remote-questions/status.ts +3 -1
  206. package/src/resources/extensions/remote-questions/store.ts +3 -1
  207. package/src/resources/extensions/search-the-web/provider.ts +2 -1
  208. package/src/resources/extensions/subagent/index.ts +12 -3
  209. package/src/resources/extensions/subagent/isolation.ts +3 -1
  210. package/src/resources/extensions/ttsr/rule-loader.ts +3 -1
  211. package/dist/resources/extensions/gsd/prompt-compressor.js +0 -393
  212. package/dist/resources/extensions/gsd/semantic-chunker.js +0 -254
  213. package/dist/resources/extensions/gsd/summary-distiller.js +0 -212
  214. package/src/resources/extensions/gsd/prompt-compressor.ts +0 -508
  215. package/src/resources/extensions/gsd/semantic-chunker.ts +0 -336
  216. package/src/resources/extensions/gsd/summary-distiller.ts +0 -258
  217. package/src/resources/extensions/gsd/tests/context-compression.test.ts +0 -193
  218. package/src/resources/extensions/gsd/tests/prompt-compressor.test.ts +0 -529
  219. package/src/resources/extensions/gsd/tests/semantic-chunker.test.ts +0 -426
  220. package/src/resources/extensions/gsd/tests/summary-distiller.test.ts +0 -323
  221. package/src/resources/extensions/gsd/tests/token-optimization-benchmark.test.ts +0 -1272
  222. package/src/resources/extensions/gsd/tests/token-optimization-prefs.test.ts +0 -164
@@ -33,19 +33,16 @@ Ask **1–3 questions per round**. Keep each question focused on one of:
33
33
 
34
34
  After the user answers, investigate further if any answer opens a new unknown, then ask the next round.
35
35
 
36
- ### Check-in after each round
36
+ ### Round cadence
37
37
 
38
- After each round of answers, ask:
38
+ After each round of answers, decide whether you already have enough depth to write a strong context file.
39
39
 
40
- > "I think I have a solid picture of this milestone. Ready to wrap up and write the context file, or is there more to cover?"
41
-
42
- **If `{{structuredQuestionsAvailable}}` is `true`:** use `ask_user_questions` with options:
43
- - "Wrap up — write the context file" *(recommended after ~2–3 rounds)*
44
- - "Keep going — more to discuss"
45
-
46
- **If `{{structuredQuestionsAvailable}}` is `false`:** ask in plain text.
47
-
48
- If the user wants to keep going, keep asking. Stop when they say wrap up.
40
+ - If not, investigate any newly-opened unknowns and continue to the next round immediately. Do **not** ask a meta "ready to wrap up?" question after every round.
41
+ - Use a single wrap-up prompt only when you genuinely believe the depth checklist is satisfied or the user signals they want to stop.
42
+ - **If `{{structuredQuestionsAvailable}}` is `true` and you need that wrap-up prompt:** use `ask_user_questions` with options:
43
+ - "Write the context file" *(recommended when depth is satisfied)*
44
+ - "One more pass"
45
+ - **If `{{structuredQuestionsAvailable}}` is `false`:** ask in plain text only once you believe you are ready to write.
49
46
 
50
47
  ---
51
48
 
@@ -55,7 +52,7 @@ If the user wants to keep going, keep asking. Stop when they say wrap up.
55
52
 
56
53
  **Challenge vagueness, make abstract concrete.** When the user says something abstract ("it should be smart" / "good UX"), push for specifics.
57
54
 
58
- **Questions must be about the experience, not the implementation.** Never ask "what auth provider?" ask "when someone logs in, what should that feel like?" Implementation is your job. Understanding what they want to experience is the discussion's job.
55
+ **Lead with experience, but ask implementation when it materially matters.** Default questions should target the experience and outcome. But when implementation choices materially change scope, proof, compliance, integration, deployment, or irreversible architecture, ask them directly instead of forcing a fake UX phrasing.
59
56
 
60
57
  **Position-first framing.** Have opinions. "I'd lean toward X because Y — does that match your thinking?" is better than "what do you think about X vs Y?"
61
58
 
@@ -95,6 +92,8 @@ Before moving to the wrap-up gate, verify you have covered:
95
92
 
96
93
  If they clarify, absorb the correction and re-verify.
97
94
 
95
+ The depth verification is the only required confirmation gate. Do not add a second "ready to proceed?" gate after it.
96
+
98
97
  ---
99
98
 
100
99
  ## Output
@@ -1,6 +1,6 @@
1
1
  You are interviewing the user to surface behavioural, UX, and usage grey areas for slice **{{sliceId}}: {{sliceTitle}}** of milestone **{{milestoneId}}**.
2
2
 
3
- Your goal is **not** to settle tech stack, naming conventions, or architecture — that happens during research and planning. Your goal is to produce a context file that captures the human decisions: what this slice should feel like, how it should behave, what edge cases matter, where scope begins and ends, and what the user cares about that won't be obvious from the roadmap entry alone.
3
+ Your goal is **not** to center the discussion on tech stack trivia, naming conventions, or speculative architecture. Your goal is to produce a context file that captures the human decisions: what this slice should feel like, how it should behave, what edge cases matter, where scope begins and ends, and what the user cares about that won't be obvious from the roadmap entry alone. If a technical choice materially changes scope, proof, or integration behavior, ask it directly and capture it.
4
4
 
5
5
  {{inlinedContext}}
6
6
 
@@ -27,17 +27,15 @@ Ask **1–3 questions per round** using `ask_user_questions`. Keep each question
27
27
 
28
28
  After the user answers, investigate further if any answer opens a new unknown, then ask the next round.
29
29
 
30
- ### Check-in after each round
30
+ ### Round cadence
31
31
 
32
- After each round of answers, use `ask_user_questions` to ask:
32
+ After each round of answers, decide whether you already have enough signal to write the slice context cleanly.
33
33
 
34
- > "I think I have a solid picture of this slice. Ready to wrap up and write the context file, or is there more to cover?"
35
-
36
- Options:
37
- - "Wrap up — write the context file" *(recommended after ~2–3 rounds)*
38
- - "Keep going — more to discuss"
39
-
40
- If the user wants to keep going, keep asking. Stop when they say wrap up.
34
+ - If not, investigate any new unknowns and continue to the next round immediately. Do **not** ask a meta "ready to wrap up?" question after every round.
35
+ - Ask a single wrap-up question only when you genuinely believe the slice is well understood or the user signals they want to stop.
36
+ - When you do ask it, use `ask_user_questions` with:
37
+ - "Write the context file" *(recommended when the slice is well understood)*
38
+ - "One more pass"
41
39
 
42
40
  ---
43
41
 
@@ -1 +1 @@
1
- Resume interrupted work. Find the continue file (`{{sliceId}}-CONTINUE.md` or `continue.md`) in slice {{sliceId}} of milestone {{milestoneId}}, then pick up from where you left off. Delete the continue file after reading it. If a `GSD Skill Preferences` block is present in system context, use it to decide which skills to load and follow during execution, without relaxing required verification or artifact rules.
1
+ Resume interrupted work. Find the continue file (`{{sliceId}}-CONTINUE.md` or `continue.md`) in slice {{sliceId}} of milestone {{milestoneId}}, read it, and use it as the recovery contract for where to pick up. Do **not** delete the continue file immediately. Keep it until the task is successfully completed or you have written a newer summary/continue artifact that clearly supersedes it. If the resumed attempt fails again, update or replace the continue file so no recovery context is lost. If a `GSD Skill Preferences` block is present in system context, use it to decide which skills to load and follow during execution, without relaxing required verification or artifact rules.
@@ -61,13 +61,14 @@ Then:
61
61
  - a concrete, action-oriented title
62
62
  - the inline task entry fields defined in the plan.md template (Why / Files / Do / Verify / Done when)
63
63
  - a matching task plan file with description, steps, must-haves, verification, inputs, and expected output
64
+ - **Inputs and Expected Output must list concrete backtick-wrapped file paths** (e.g. `` `src/types.ts` ``). These are machine-parsed to derive task dependencies — vague prose without paths breaks parallel execution. Every task must have at least one output file path.
64
65
  - Observability Impact section **only if the task touches runtime boundaries, async flows, or error paths** — omit it otherwise
65
66
  6. Write `{{outputPath}}`
66
67
  7. Write individual task plans in `{{slicePath}}/tasks/`: `T01-PLAN.md`, `T02-PLAN.md`, etc.
67
68
  8. **Self-audit the plan.** Walk through each check — if any fail, fix the plan files before moving on:
68
69
  - **Completion semantics:** If every task were completed exactly as written, the slice goal/demo should actually be true.
69
70
  - **Requirement coverage:** Every must-have in the slice maps to at least one task. No must-have is orphaned. If `REQUIREMENTS.md` exists, every Active requirement this slice owns maps to at least one task.
70
- - **Task completeness:** Every task has steps, must-haves, verification, inputs, and expected output — none are blank or vague.
71
+ - **Task completeness:** Every task has steps, must-haves, verification, inputs, and expected output — none are blank or vague. Inputs and Expected Output list backtick-wrapped file paths, not prose descriptions.
71
72
  - **Dependency correctness:** Task ordering is consistent. No task references work from a later task.
72
73
  - **Key links planned:** For every pair of artifacts that must connect, there is an explicit step that wires them.
73
74
  - **Scope sanity:** Target 2–5 steps and 3–8 files per task. 10+ steps or 12+ files — must split. Each task must be completable in a single fresh context window.
@@ -36,15 +36,11 @@ Don't go deep — just enough that your next question reflects what's actually t
36
36
  - How the new work relates to existing milestones — overlap, dependencies, prerequisites
37
37
  - If `.gsd/REQUIREMENTS.md` exists: which unmet Active or Deferred requirements this queued work advances
38
38
 
39
- **Then use ask_user_questions** to dig into gray areas — architecture choices, scope boundaries, tech preferences, what's in vs out. 1-3 questions per round.
39
+ **Then use ask_user_questions** to dig into gray areas — scope boundaries, proof expectations, integration choices, tech preferences when they materially matter, and what's in vs out. 1-3 questions per round.
40
40
 
41
41
  If a `GSD Skill Preferences` block is present in system context, use it to decide which skills to load and follow during discuss/planning work, but do not let it override the required discuss flow or artifact requirements.
42
42
 
43
- **Self-regulate:** After about 10-15 questions total (3-5 rounds), or when you feel you have a solid understanding, include a question like:
44
- "I think I have a good picture. Ready to queue this, or are there more things to discuss?"
45
- with options: "Ready to queue (Recommended)", "I have more to discuss"
46
-
47
- If the user wants to keep going, keep asking. If they're ready, proceed.
43
+ **Self-regulate:** Do **not** ask a meta "ready to queue?" question after every round. Keep going until you have enough depth to write the context well, then use a single wrap-up prompt if needed. If the user clearly keeps adding detail instead of objecting, treat that as permission to continue.
48
44
 
49
45
  ## Existing Milestone Awareness
50
46
 
@@ -88,7 +84,7 @@ For EACH milestone you are about to write context for, investigate the codebase
88
84
  1. **Read the actual code** — for every file or module you reference in "Existing Codebase / Prior Art", read enough to confirm your assumptions about what exists, what it does, and what it doesn't do. Do not guess from memory or training data.
89
85
  2. **Check for stale assumptions** — the codebase may have changed since the user's spec was written. Verify: do the APIs you reference still exist? Have modules been refactored? Has upstream merged features that change the landscape?
90
86
  3. **Identify phantom capabilities** — for every capability you list as "existing," confirm it actually works as described. Look for: functions that exist but are never called, fields that are set but never read, features that are piped but never connected.
91
- 4. **Note what you found** — include verified findings in the context file's "Existing Codebase / Prior Art" section with "verified against v{version}" annotations.
87
+ 4. **Note what you found** — include verified findings in the context file's "Existing Codebase / Prior Art" section with annotations like "verified against current codebase state" or an actual concrete version/commit only if you truly have one.
92
88
 
93
89
  ### Step 2: Per-Milestone Depth Verification
94
90
 
@@ -103,7 +99,7 @@ This triggers the per-milestone write-gate. The question should present:
103
99
  - Key technical assumptions you verified (or couldn't verify)
104
100
  - Any risks or unknowns the investigation surfaced
105
101
 
106
- The user confirms or corrects before you write. One depth verification per milestone — not one for all milestones combined.
102
+ The user confirms or corrects before you write. One depth verification per milestone — not one for all milestones combined. This is the required write-gate; do not add extra "ready to proceed?" prompts around it once you have enough signal.
107
103
 
108
104
  **If you skip this step, the system will block the CONTEXT.md write and return an error telling you to complete verification first.**
109
105
 
@@ -0,0 +1,44 @@
1
+ # Reactive Task Execution — Parallel Dispatch
2
+
3
+ **Working directory:** `{{workingDirectory}}`
4
+ **Milestone:** {{milestoneId}} — {{milestoneTitle}}
5
+ **Slice:** {{sliceId}} — {{sliceTitle}}
6
+
7
+ ## Mission
8
+
9
+ You are executing **multiple tasks in parallel** for this slice. The task graph below shows which tasks are ready for simultaneous execution based on their input/output dependencies.
10
+
11
+ **Critical rule:** Use the `subagent` tool in **parallel mode** to dispatch all ready tasks simultaneously. Each subagent gets a full `execute-task` prompt and is responsible for its own implementation, verification, task summary, and checkbox updates. The parent batch agent orchestrates, verifies, and records failures only when a dispatched task failed before it could leave its own summary behind.
12
+
13
+ ## Task Dependency Graph
14
+
15
+ {{graphContext}}
16
+
17
+ ## Ready Tasks for Parallel Dispatch
18
+
19
+ {{readyTaskCount}} tasks are ready for parallel execution:
20
+
21
+ {{readyTaskList}}
22
+
23
+ ## Execution Protocol
24
+
25
+ 1. **Dispatch all ready tasks** using `subagent` in parallel mode. Each subagent prompt is provided below.
26
+ 2. **Wait for all subagents** to complete.
27
+ 3. **Verify each dispatched task's outputs** — check that expected files were created/modified, that verification commands pass where applicable, and that each task wrote its own `T##-SUMMARY.md`.
28
+ 4. **Do not rewrite successful task summaries or duplicate checkbox edits.** Treat a subagent-written summary as authoritative for that task.
29
+ 5. **If a failed task produced no summary, write a recovery summary for that task** with `blocker_discovered: true`, clear failure details, and leave the task unchecked so replan/retry has an authoritative record.
30
+ 6. **Preserve successful sibling tasks exactly as they landed.** Do not roll back good work because another parallel task failed.
31
+ 7. **Do NOT create a batch commit.** The surrounding unit lifecycle owns commits; this parent batch agent should not invent a second commit layer.
32
+ 8. **Report the batch outcome** — which tasks succeeded, which failed, and any output collisions or dependency surprises.
33
+
34
+ If any subagent fails:
35
+ - Keep successful task summaries and checkbox updates as-is
36
+ - Write a failure summary only when the failed task did not leave one behind
37
+ - Do not silently discard or overwrite another task's outputs
38
+ - The orchestrator will handle re-dispatch or replanning on the next iteration
39
+
40
+ ## Subagent Prompts
41
+
42
+ {{subagentPrompts}}
43
+
44
+ {{inlinedTemplates}}
@@ -18,32 +18,47 @@ If a `GSD Skill Preferences` block is present in system context, use it to decid
18
18
 
19
19
  **UAT file:** `{{uatPath}}`
20
20
  **Result file to write:** `{{uatResultPath}}`
21
+ **Detected UAT mode:** `{{uatType}}`
21
22
 
22
- You are the test runner. Execute every check defined in `{{uatPath}}` directly:
23
+ You are the UAT runner. Execute every check defined in `{{uatPath}}` as deeply as this mode truthfully allows. Do not collapse live or subjective checks into cheap artifact checks just to get a PASS.
24
+
25
+ ### Automation rules by mode
26
+
27
+ - `artifact-driven` — verify with shell commands, scripts, file reads, and artifact structure checks.
28
+ - `live-runtime` — exercise the real runtime path. Start or connect to the app/service if needed, use browser/runtime/network checks, and verify observable behavior.
29
+ - `mixed` — run all automatable artifact-driven and live-runtime checks. Separate any remaining human-only checks explicitly.
30
+ - `human-experience` — automate setup, preconditions, screenshots, logs, and objective checks, but do **not** invent subjective PASS results. Mark taste-based, experiential, or purely human-judgment checks as `NEEDS-HUMAN` and use an overall verdict of `PARTIAL` unless every required check was objective and passed.
31
+
32
+ ### Evidence tools
33
+
34
+ Choose the lightest tool that proves the check honestly:
23
35
 
24
36
  - Run shell commands with `bash`
25
37
  - Run `grep` / `rg` checks against files
26
- - Run `node` / script invocations
38
+ - Run `node` / other script invocations
27
39
  - Read files and verify their contents
28
40
  - Check that expected artifacts exist and have correct structure
41
+ - For live/runtime/UI checks, exercise the real flow in the browser when applicable and inspect runtime/network/console state
42
+ - When a check cannot be honestly automated, gather the best objective evidence you can and mark it `NEEDS-HUMAN`
29
43
 
30
44
  For each check, record:
31
45
  - The check description (from the UAT file)
46
+ - The evidence mode used: `artifact`, `runtime`, or `human-follow-up`
32
47
  - The command or action taken
33
48
  - The actual result observed
34
- - PASS or FAIL verdict
49
+ - `PASS`, `FAIL`, or `NEEDS-HUMAN`
35
50
 
36
51
  After running all checks, compute the **overall verdict**:
37
- - `PASS` — all checks passed
52
+ - `PASS` — all required checks passed and no human-only checks remain
38
53
  - `FAIL` — one or more checks failed
39
- - `PARTIAL` — some checks passed, some failed or were skipped
54
+ - `PARTIAL` — some checks passed, but one or more checks were skipped, inconclusive, or still require human judgment
40
55
 
41
56
  Write `{{uatResultPath}}` with:
42
57
 
43
58
  ```markdown
44
59
  ---
45
60
  sliceId: {{sliceId}}
46
- uatType: artifact-driven
61
+ uatType: {{uatType}}
47
62
  verdict: PASS | FAIL | PARTIAL
48
63
  date: <ISO 8601 timestamp>
49
64
  ---
@@ -52,9 +67,9 @@ date: <ISO 8601 timestamp>
52
67
 
53
68
  ## Checks
54
69
 
55
- | Check | Result | Notes |
56
- |-------|--------|-------|
57
- | <check description> | PASS / FAIL | <observed output or reason> |
70
+ | Check | Mode | Result | Notes |
71
+ |-------|------|--------|-------|
72
+ | <check description> | artifact / runtime / human-follow-up | PASS / FAIL / NEEDS-HUMAN | <observed output, evidence, or reason> |
58
73
 
59
74
  ## Overall Verdict
60
75
 
@@ -62,7 +77,7 @@ date: <ISO 8601 timestamp>
62
77
 
63
78
  ## Notes
64
79
 
65
- <any additional context, errors encountered, or follow-up items>
80
+ <any additional context, errors encountered, screenshots/logs gathered, or manual follow-up still required>
66
81
  ```
67
82
 
68
83
  ---
@@ -14,7 +14,7 @@ You are executing a **{{templateName}}** workflow (template: `{{templateId}}`).
14
14
 
15
15
  ## Workflow Definition
16
16
 
17
- Follow the workflow defined below. Execute each phase in order, completing one before moving to the next. At each phase gate, confirm with the user before proceeding.
17
+ Follow the workflow defined below. Execute each phase in order, completing one before moving to the next. For low and medium complexity workflows, keep moving by default — pause only at true decision gates (user must choose between materially different directions, outward-facing actions need approval, or the workflow explicitly requires a human checkpoint). For high complexity workflows, confirm at phase transitions unless the workflow explicitly marks a gate as skip-safe.
18
18
 
19
19
  {{workflowContent}}
20
20
 
@@ -24,5 +24,5 @@ Follow the workflow defined below. Execute each phase in order, completing one b
24
24
  2. **Artifact discipline.** If an artifact directory is specified, write all planning/summary documents there.
25
25
  3. **Atomic commits.** Commit working code after each meaningful change. Use conventional commit format: `<type>(<scope>): <description>`.
26
26
  4. **Verify before shipping.** Run the project's test suite and build before marking the workflow complete.
27
- 5. **Gate between phases.** After each phase, summarize what was done and ask the user to confirm before moving to the next phase.
27
+ 5. **Decision gates, not ceremony.** After each phase, summarize what changed. For low/medium complexity, ask for confirmation only when the next phase depends on a real user choice or external approval. For high complexity, confirm before proceeding to each new phase.
28
28
  6. **Stay focused.** This is a {{complexity}}-complexity workflow. Match your ceremony level to the task — don't over-engineer or under-deliver.
@@ -0,0 +1,289 @@
1
+ /**
2
+ * Reactive Task Graph — derives dependency edges from task plan IO signatures.
3
+ *
4
+ * Pure functions that build a DAG from task IO intersections and resolve
5
+ * which tasks are currently ready for parallel dispatch. Used by the
6
+ * reactive-execute dispatch path (ADR-004).
7
+ *
8
+ * Graph derivation and resolution functions are pure (no filesystem access).
9
+ * The `loadSliceTaskIO` loader at the bottom is the only async/IO function.
10
+ */
11
+
12
+ import type { TaskIO, DerivedTaskNode, ReactiveExecutionState } from "./types.js";
13
+ import { loadFile, parsePlan, parseTaskPlanIO } from "./files.js";
14
+ import { resolveTasksDir, resolveTaskFiles } from "./paths.js";
15
+ import { join } from "node:path";
16
+ import { loadJsonFileOrNull, saveJsonFile } from "./json-persistence.js";
17
+ import { existsSync, unlinkSync } from "node:fs";
18
+
19
+ // ─── Graph Construction ───────────────────────────────────────────────────
20
+
21
+ /**
22
+ * Build a dependency graph from task IO signatures.
23
+ *
24
+ * A task T_b depends on T_a when any of T_b's inputFiles appear in T_a's
25
+ * outputFiles. Self-references are excluded.
26
+ *
27
+ * Tasks are returned in the same order as the input array.
28
+ */
29
+ export function deriveTaskGraph(tasks: TaskIO[]): DerivedTaskNode[] {
30
+ // Build output → producer lookup
31
+ const outputToProducer = new Map<string, string[]>();
32
+ for (const task of tasks) {
33
+ for (const outFile of task.outputFiles) {
34
+ const existing = outputToProducer.get(outFile);
35
+ if (existing) {
36
+ existing.push(task.id);
37
+ } else {
38
+ outputToProducer.set(outFile, [task.id]);
39
+ }
40
+ }
41
+ }
42
+
43
+ return tasks.map((task) => {
44
+ const deps = new Set<string>();
45
+ for (const inFile of task.inputFiles) {
46
+ const producers = outputToProducer.get(inFile);
47
+ if (producers) {
48
+ for (const pid of producers) {
49
+ if (pid !== task.id) deps.add(pid);
50
+ }
51
+ }
52
+ }
53
+ return {
54
+ ...task,
55
+ dependsOn: [...deps].sort(),
56
+ };
57
+ });
58
+ }
59
+
60
+ // ─── Ready Set Resolution ─────────────────────────────────────────────────
61
+
62
+ /**
63
+ * Return task IDs whose dependencies are all in `completed`.
64
+ * Excludes tasks that are already done or in-flight.
65
+ */
66
+ export function getReadyTasks(
67
+ graph: DerivedTaskNode[],
68
+ completed: Set<string>,
69
+ inFlight: Set<string>,
70
+ ): string[] {
71
+ return graph
72
+ .filter((node) => {
73
+ if (node.done || completed.has(node.id) || inFlight.has(node.id)) return false;
74
+ return node.dependsOn.every((dep) => completed.has(dep));
75
+ })
76
+ .map((node) => node.id);
77
+ }
78
+
79
+ // ─── Conflict-Free Subset Selection ──────────────────────────────────────
80
+
81
+ /**
82
+ * Greedy selection of non-conflicting tasks up to `maxParallel`.
83
+ *
84
+ * Two tasks conflict if they share any outputFile. We also exclude tasks
85
+ * whose outputs overlap with `inFlightOutputs` (files being written by
86
+ * tasks currently in progress).
87
+ */
88
+ export function chooseNonConflictingSubset(
89
+ readyIds: string[],
90
+ graph: DerivedTaskNode[],
91
+ maxParallel: number,
92
+ inFlightOutputs: Set<string>,
93
+ ): string[] {
94
+ const nodeMap = new Map(graph.map((n) => [n.id, n]));
95
+ const claimed = new Set(inFlightOutputs);
96
+ const selected: string[] = [];
97
+
98
+ for (const id of readyIds) {
99
+ if (selected.length >= maxParallel) break;
100
+ const node = nodeMap.get(id);
101
+ if (!node) continue;
102
+
103
+ // Check for output overlap with already-selected or in-flight
104
+ const conflicts = node.outputFiles.some((f) => claimed.has(f));
105
+ if (conflicts) continue;
106
+
107
+ // Claim this task's outputs
108
+ for (const f of node.outputFiles) claimed.add(f);
109
+ selected.push(id);
110
+ }
111
+
112
+ return selected;
113
+ }
114
+
115
+ // ─── Graph Quality Checks ─────────────────────────────────────────────────
116
+
117
+ /**
118
+ * Returns true if any incomplete task has 0 inputFiles AND 0 outputFiles.
119
+ *
120
+ * An ambiguous graph means IO annotations are too sparse to derive reliable
121
+ * edges — the dispatcher should fall back to sequential execution.
122
+ */
123
+ export function isGraphAmbiguous(graph: DerivedTaskNode[]): boolean {
124
+ return graph.some(
125
+ (node) =>
126
+ !node.done &&
127
+ node.inputFiles.length === 0 &&
128
+ node.outputFiles.length === 0,
129
+ );
130
+ }
131
+
132
+ /**
133
+ * Detect deadlock: no tasks are ready and none are in-flight, yet incomplete
134
+ * tasks remain. This indicates a circular dependency or impossible state.
135
+ */
136
+ export function detectDeadlock(
137
+ graph: DerivedTaskNode[],
138
+ completed: Set<string>,
139
+ inFlight: Set<string>,
140
+ ): boolean {
141
+ const incomplete = graph.filter(
142
+ (n) => !n.done && !completed.has(n.id) && !inFlight.has(n.id),
143
+ );
144
+ if (incomplete.length === 0) return false; // all done
145
+ if (inFlight.size > 0) return false; // something is running, wait for it
146
+
147
+ // Nothing in flight, but incomplete tasks remain — check if any are ready
148
+ const ready = getReadyTasks(graph, completed, inFlight);
149
+ return ready.length === 0;
150
+ }
151
+
152
+ // ─── Graph Metrics ────────────────────────────────────────────────────────
153
+
154
+ /** Compute summary metrics for logging. */
155
+ export function graphMetrics(graph: DerivedTaskNode[]): {
156
+ taskCount: number;
157
+ edgeCount: number;
158
+ readySetSize: number;
159
+ ambiguous: boolean;
160
+ } {
161
+ const completed = new Set(graph.filter((n) => n.done).map((n) => n.id));
162
+ const ready = getReadyTasks(graph, completed, new Set());
163
+ const edgeCount = graph.reduce((sum, n) => sum + n.dependsOn.length, 0);
164
+
165
+ return {
166
+ taskCount: graph.length,
167
+ edgeCount,
168
+ readySetSize: ready.length,
169
+ ambiguous: isGraphAmbiguous(graph),
170
+ };
171
+ }
172
+
173
+ // ─── IO Loader (async, filesystem) ────────────────────────────────────────
174
+
175
+ /**
176
+ * Load TaskIO for all tasks in a slice by reading the slice plan (for done
177
+ * status and task IDs) and individual task plan files (for IO sections).
178
+ *
179
+ * Returns [] when the slice plan or tasks directory doesn't exist.
180
+ */
181
+ export async function loadSliceTaskIO(
182
+ basePath: string,
183
+ mid: string,
184
+ sid: string,
185
+ ): Promise<TaskIO[]> {
186
+ const { resolveSliceFile } = await import("./paths.js");
187
+ const slicePlanPath = resolveSliceFile(basePath, mid, sid, "PLAN");
188
+ const planContent = slicePlanPath ? await loadFile(slicePlanPath) : null;
189
+ if (!planContent) return [];
190
+
191
+ const plan = parsePlan(planContent);
192
+ const tDir = resolveTasksDir(basePath, mid, sid);
193
+ if (!tDir) return [];
194
+
195
+ const results: TaskIO[] = [];
196
+
197
+ for (const taskEntry of plan.tasks) {
198
+ const planFiles = resolveTaskFiles(tDir, "PLAN");
199
+ const taskFileName = planFiles.find((f) =>
200
+ f.toUpperCase().startsWith(taskEntry.id.toUpperCase() + "-"),
201
+ );
202
+ if (!taskFileName) {
203
+ // Task plan file missing — include with empty IO (will trigger ambiguous)
204
+ results.push({
205
+ id: taskEntry.id,
206
+ title: taskEntry.title,
207
+ inputFiles: [],
208
+ outputFiles: [],
209
+ done: taskEntry.done,
210
+ });
211
+ continue;
212
+ }
213
+
214
+ const taskContent = await loadFile(join(tDir, taskFileName));
215
+ if (!taskContent) {
216
+ results.push({
217
+ id: taskEntry.id,
218
+ title: taskEntry.title,
219
+ inputFiles: [],
220
+ outputFiles: [],
221
+ done: taskEntry.done,
222
+ });
223
+ continue;
224
+ }
225
+
226
+ const io = parseTaskPlanIO(taskContent);
227
+ results.push({
228
+ id: taskEntry.id,
229
+ title: taskEntry.title,
230
+ inputFiles: io.inputFiles,
231
+ outputFiles: io.outputFiles,
232
+ done: taskEntry.done,
233
+ });
234
+ }
235
+
236
+ return results;
237
+ }
238
+
239
+ // ─── State Persistence ────────────────────────────────────────────────────
240
+
241
+ function reactiveStatePath(basePath: string, mid: string, sid: string): string {
242
+ return join(basePath, ".gsd", "runtime", `${mid}-${sid}-reactive.json`);
243
+ }
244
+
245
+ function isReactiveState(data: unknown): data is ReactiveExecutionState {
246
+ if (!data || typeof data !== "object") return false;
247
+ const d = data as Record<string, unknown>;
248
+ return typeof d.sliceId === "string" && Array.isArray(d.completed) && Array.isArray(d.dispatched);
249
+ }
250
+
251
+ /**
252
+ * Load persisted reactive execution state for a slice.
253
+ * Returns null when no state file exists or the file is invalid.
254
+ */
255
+ export function loadReactiveState(
256
+ basePath: string,
257
+ mid: string,
258
+ sid: string,
259
+ ): ReactiveExecutionState | null {
260
+ return loadJsonFileOrNull(reactiveStatePath(basePath, mid, sid), isReactiveState);
261
+ }
262
+
263
+ /**
264
+ * Save reactive execution state to disk.
265
+ */
266
+ export function saveReactiveState(
267
+ basePath: string,
268
+ mid: string,
269
+ sid: string,
270
+ state: ReactiveExecutionState,
271
+ ): void {
272
+ saveJsonFile(reactiveStatePath(basePath, mid, sid), state);
273
+ }
274
+
275
+ /**
276
+ * Remove the reactive state file when a slice completes.
277
+ */
278
+ export function clearReactiveState(
279
+ basePath: string,
280
+ mid: string,
281
+ sid: string,
282
+ ): void {
283
+ const path = reactiveStatePath(basePath, mid, sid);
284
+ try {
285
+ if (existsSync(path)) unlinkSync(path);
286
+ } catch {
287
+ // Non-fatal
288
+ }
289
+ }
@@ -12,6 +12,8 @@ import { existsSync, lstatSync, mkdirSync, readFileSync, realpathSync, rmSync, s
12
12
  import { homedir } from "node:os";
13
13
  import { join, resolve } from "node:path";
14
14
 
15
+ const gsdHome = process.env.GSD_HOME || join(homedir(), ".gsd");
16
+
15
17
  // ─── Repo Identity ──────────────────────────────────────────────────────────
16
18
 
17
19
  /**
@@ -90,14 +92,31 @@ function resolveGitRoot(basePath: string): string {
90
92
  }
91
93
  }
92
94
 
95
+ /**
96
+ * Validate a GSD_PROJECT_ID value.
97
+ *
98
+ * Must contain only alphanumeric characters, hyphens, and underscores.
99
+ * Call this once at startup so the user gets immediate feedback on bad values.
100
+ */
101
+ export function validateProjectId(id: string): boolean {
102
+ return /^[a-zA-Z0-9_-]+$/.test(id);
103
+ }
104
+
93
105
  /**
94
106
  * Compute a stable identity for a repository.
95
107
  *
96
- * SHA-256 of `${remoteUrl}\n${resolvedRoot}`, truncated to 12 hex chars.
97
- * Deterministic: same repo always produces the same hash regardless of
98
- * which worktree the caller is inside.
108
+ * If `GSD_PROJECT_ID` is set, returns it directly (validation is expected
109
+ * to have already happened at startup via `validateProjectId`).
110
+ *
111
+ * Otherwise returns SHA-256 of `${remoteUrl}\n${resolvedRoot}`, truncated
112
+ * to 12 hex chars. Deterministic: same repo always produces the same hash
113
+ * regardless of which worktree the caller is inside.
99
114
  */
100
115
  export function repoIdentity(basePath: string): string {
116
+ const projectId = process.env.GSD_PROJECT_ID;
117
+ if (projectId) {
118
+ return projectId;
119
+ }
101
120
  const remoteUrl = getRemoteUrl(basePath);
102
121
  const root = resolveGitRoot(basePath);
103
122
  const input = `${remoteUrl}\n${root}`;
@@ -113,7 +132,7 @@ export function repoIdentity(basePath: string): string {
113
132
  * otherwise `~/.gsd/projects/<hash>`.
114
133
  */
115
134
  export function externalGsdRoot(basePath: string): string {
116
- const base = process.env.GSD_STATE_DIR || join(homedir(), ".gsd");
135
+ const base = process.env.GSD_STATE_DIR || gsdHome;
117
136
  return join(base, "projects", repoIdentity(basePath));
118
137
  }
119
138
 
@@ -11,6 +11,8 @@ import { join } from "node:path";
11
11
  import { homedir } from "node:os";
12
12
  import { resolveProjectRoot } from "./worktree.js";
13
13
 
14
+ const gsdHome = process.env.GSD_HOME || join(homedir(), ".gsd");
15
+
14
16
  // ─── Resource Staleness ───────────────────────────────────────────────────
15
17
 
16
18
  /**
@@ -23,7 +25,7 @@ function isManifestWithVersion(data: unknown): data is { gsdVersion: string } {
23
25
  }
24
26
 
25
27
  export function readResourceVersion(): string | null {
26
- const agentDir = process.env.GSD_CODING_AGENT_DIR || join(homedir(), ".gsd", "agent");
28
+ const agentDir = process.env.GSD_CODING_AGENT_DIR || join(gsdHome, "agent");
27
29
  const manifestPath = join(agentDir, "managed-resources.json");
28
30
  const manifest = loadJsonFileOrNull(manifestPath, isManifestWithVersion);
29
31
  return manifest?.gsdVersion ?? null;
@@ -31,7 +31,7 @@ import {
31
31
  gsdRoot,
32
32
  } from './paths.js';
33
33
 
34
- import { milestoneIdSort, findMilestoneIds } from './guided-flow.js';
34
+ import { milestoneIdSort, findMilestoneIds } from './milestone-ids.js';
35
35
  import { nativeBatchParseGsdFiles, type BatchParsedFile } from './native-parser-bridge.js';
36
36
 
37
37
  import { join, resolve } from 'path';