@opengsd/gsd-pi 1.4.0 → 1.5.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 (329) hide show
  1. package/README.md +11 -11
  2. package/dist/onboarding.js +11 -0
  3. package/dist/resources/.managed-resources-content-hash +1 -1
  4. package/dist/resources/GSD-WORKFLOW.md +20 -25
  5. package/dist/resources/extensions/claude-code-cli/stream-adapter.js +118 -53
  6. package/dist/resources/extensions/cursor-cli/index.js +16 -0
  7. package/dist/resources/extensions/cursor-cli/models.js +63 -0
  8. package/dist/resources/extensions/cursor-cli/package.json +11 -0
  9. package/dist/resources/extensions/cursor-cli/readiness.js +128 -0
  10. package/dist/resources/extensions/cursor-cli/stream-adapter.js +353 -0
  11. package/dist/resources/extensions/gsd/activity-log.js +4 -1
  12. package/dist/resources/extensions/gsd/artifact-verification.js +25 -9
  13. package/dist/resources/extensions/gsd/auto/closeout.js +45 -42
  14. package/dist/resources/extensions/gsd/auto/finalize.js +9 -0
  15. package/dist/resources/extensions/gsd/auto/orchestrator.js +1 -1
  16. package/dist/resources/extensions/gsd/auto/run-unit.js +5 -2
  17. package/dist/resources/extensions/gsd/auto/schedule-wakeup.js +3 -0
  18. package/dist/resources/extensions/gsd/auto-dispatch.js +15 -5
  19. package/dist/resources/extensions/gsd/auto-model-selection.js +36 -18
  20. package/dist/resources/extensions/gsd/auto-prompts.js +76 -2
  21. package/dist/resources/extensions/gsd/auto-start.js +4 -3
  22. package/dist/resources/extensions/gsd/auto-timeout-recovery.js +4 -0
  23. package/dist/resources/extensions/gsd/auto-worktree.js +38 -11
  24. package/dist/resources/extensions/gsd/auto.js +23 -3
  25. package/dist/resources/extensions/gsd/bootstrap/db-tools.js +3 -0
  26. package/dist/resources/extensions/gsd/bootstrap/register-hooks.js +114 -5
  27. package/dist/resources/extensions/gsd/bootstrap/schedule-wakeup-tool.js +58 -9
  28. package/dist/resources/extensions/gsd/bootstrap/tool-call-loop-guard.js +10 -2
  29. package/dist/resources/extensions/gsd/bootstrap/write-gate.js +14 -1
  30. package/dist/resources/extensions/gsd/codebase-generator.js +157 -19
  31. package/dist/resources/extensions/gsd/commands/handlers/core.js +40 -4
  32. package/dist/resources/extensions/gsd/commands-prefs-wizard.js +153 -1
  33. package/dist/resources/extensions/gsd/commands-worktree.js +11 -2
  34. package/dist/resources/extensions/gsd/db/queries.js +39 -0
  35. package/dist/resources/extensions/gsd/discussion-handoff.js +8 -3
  36. package/dist/resources/extensions/gsd/docs/preferences-reference.md +8 -2
  37. package/dist/resources/extensions/gsd/doctor-engine-checks.js +206 -14
  38. package/dist/resources/extensions/gsd/doctor-providers.js +38 -10
  39. package/dist/resources/extensions/gsd/doctor.js +93 -3
  40. package/dist/resources/extensions/gsd/exec-sandbox.js +6 -2
  41. package/dist/resources/extensions/gsd/flat-phase-migration.js +74 -23
  42. package/dist/resources/extensions/gsd/git-service.js +14 -18
  43. package/dist/resources/extensions/gsd/gsd-db.js +52 -0
  44. package/dist/resources/extensions/gsd/guidance.js +1 -0
  45. package/dist/resources/extensions/gsd/guided-flow.js +40 -2
  46. package/dist/resources/extensions/gsd/key-manager.js +1 -0
  47. package/dist/resources/extensions/gsd/markdown-renderer.js +25 -8
  48. package/dist/resources/extensions/gsd/migrate-external.js +24 -7
  49. package/dist/resources/extensions/gsd/milestone-merge-transaction.js +3 -3
  50. package/dist/resources/extensions/gsd/preferences-diagnostics.js +24 -1
  51. package/dist/resources/extensions/gsd/preferences-models.js +6 -4
  52. package/dist/resources/extensions/gsd/preferences-validation.js +25 -2
  53. package/dist/resources/extensions/gsd/preferences.js +29 -2
  54. package/dist/resources/extensions/gsd/prompts/plan-slice.md +4 -2
  55. package/dist/resources/extensions/gsd/redact-secrets.js +37 -0
  56. package/dist/resources/extensions/gsd/repo-identity.js +16 -0
  57. package/dist/resources/extensions/gsd/repository-registry.js +6 -1
  58. package/dist/resources/extensions/gsd/root-write-leak-guard.js +4 -2
  59. package/dist/resources/extensions/gsd/safe-fs.js +23 -2
  60. package/dist/resources/extensions/gsd/skills/gsd-headless/SKILL.md +7 -9
  61. package/dist/resources/extensions/gsd/skills/gsd-headless/references/commands.md +1 -0
  62. package/dist/resources/extensions/gsd/tools/plan-task.js +76 -8
  63. package/dist/resources/extensions/gsd/tools/workflow-tool-executors.js +40 -0
  64. package/dist/resources/extensions/gsd/unit-context-manifest.js +5 -4
  65. package/dist/resources/extensions/gsd/unit-registry.js +27 -2
  66. package/dist/resources/extensions/gsd/unit-runtime.js +21 -0
  67. package/dist/resources/extensions/gsd/workspace.js +26 -8
  68. package/dist/resources/extensions/gsd/worktree-lifecycle.js +67 -5
  69. package/dist/resources/extensions/gsd/worktree-manager.js +97 -4
  70. package/dist/tsconfig.extensions.tsbuildinfo +1 -1
  71. package/dist/web/standalone/.next/BUILD_ID +1 -1
  72. package/dist/web/standalone/.next/app-path-routes-manifest.json +7 -7
  73. package/dist/web/standalone/.next/build-manifest.json +2 -2
  74. package/dist/web/standalone/.next/server/app/_global-error.html +1 -1
  75. package/dist/web/standalone/.next/server/app/_global-error.rsc +1 -1
  76. package/dist/web/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  77. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
  78. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
  79. package/dist/web/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  80. package/dist/web/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  81. package/dist/web/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  82. package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
  83. package/dist/web/standalone/.next/server/app/_not-found.rsc +1 -1
  84. package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +1 -1
  85. package/dist/web/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  86. package/dist/web/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +1 -1
  87. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  88. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  89. package/dist/web/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
  90. package/dist/web/standalone/.next/server/app/api/update/route.js +1 -1
  91. package/dist/web/standalone/.next/server/app/index.html +1 -1
  92. package/dist/web/standalone/.next/server/app/index.rsc +1 -1
  93. package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +1 -1
  94. package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +1 -1
  95. package/dist/web/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
  96. package/dist/web/standalone/.next/server/app/index.segments/_index.segment.rsc +1 -1
  97. package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
  98. package/dist/web/standalone/.next/server/app-paths-manifest.json +7 -7
  99. package/dist/web/standalone/.next/server/chunks/8357.js +1 -1
  100. package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
  101. package/dist/web/standalone/.next/server/pages/404.html +1 -1
  102. package/dist/web/standalone/.next/server/pages/500.html +1 -1
  103. package/dist/web/standalone/node_modules/@gsd/native/package.json +1 -1
  104. package/dist/web/standalone/node_modules/node-pty/build/Makefile +2 -2
  105. package/dist/web/standalone/node_modules/node-pty/build/pty.target.mk +14 -14
  106. package/dist/wizard.d.ts +8 -0
  107. package/dist/wizard.js +8 -1
  108. package/integrations/hermes/CONTEXT.md +47 -0
  109. package/integrations/hermes/README.md +21 -0
  110. package/integrations/hermes/__init__.py +31 -0
  111. package/integrations/hermes/docs/issue-1162-grilling.md +197 -0
  112. package/integrations/hermes/docs/setup.md +4 -0
  113. package/integrations/hermes/open_gsd_hermes/commands.py +141 -0
  114. package/integrations/hermes/open_gsd_hermes/config.py +2 -0
  115. package/integrations/hermes/open_gsd_hermes/gsd_client.py +594 -31
  116. package/integrations/hermes/open_gsd_hermes/notifications.py +10 -1
  117. package/integrations/hermes/plugin.yaml +4 -0
  118. package/integrations/hermes/tests/test_config.py +6 -0
  119. package/integrations/hermes/tests/test_directory_loader.py +37 -0
  120. package/integrations/hermes/tests/test_gsd_client_cache.py +3 -0
  121. package/integrations/hermes/tests/test_gsd_client_execute.py +35 -0
  122. package/integrations/hermes/tests/test_gsd_client_process_cwd.py +22 -0
  123. package/integrations/hermes/tests/test_gsd_client_timeout.py +98 -0
  124. package/integrations/hermes/tests/test_milestone_client.py +447 -0
  125. package/integrations/hermes/tests/test_new_milestone_command.py +313 -0
  126. package/integrations/hermes/tests/test_supervisor_fsm.py +40 -0
  127. package/package.json +8 -8
  128. package/packages/cloud-mcp-gateway/dist/auth-store.d.ts +1 -0
  129. package/packages/cloud-mcp-gateway/dist/auth-store.d.ts.map +1 -1
  130. package/packages/cloud-mcp-gateway/dist/auth-store.js +21 -1
  131. package/packages/cloud-mcp-gateway/dist/auth-store.js.map +1 -1
  132. package/packages/cloud-mcp-gateway/dist/auth-store.test.js +34 -0
  133. package/packages/cloud-mcp-gateway/dist/auth-store.test.js.map +1 -1
  134. package/packages/cloud-mcp-gateway/package.json +2 -2
  135. package/packages/contracts/package.json +1 -1
  136. package/packages/daemon/dist/cloud-runtime.d.ts +2 -0
  137. package/packages/daemon/dist/cloud-runtime.d.ts.map +1 -1
  138. package/packages/daemon/dist/cloud-runtime.js +25 -1
  139. package/packages/daemon/dist/cloud-runtime.js.map +1 -1
  140. package/packages/daemon/dist/cloud-runtime.test.js +51 -0
  141. package/packages/daemon/dist/cloud-runtime.test.js.map +1 -1
  142. package/packages/daemon/dist/message-batcher.d.ts +7 -0
  143. package/packages/daemon/dist/message-batcher.d.ts.map +1 -1
  144. package/packages/daemon/dist/message-batcher.js +13 -0
  145. package/packages/daemon/dist/message-batcher.js.map +1 -1
  146. package/packages/daemon/dist/message-batcher.test.js +48 -0
  147. package/packages/daemon/dist/message-batcher.test.js.map +1 -1
  148. package/packages/daemon/dist/session-manager.js +1 -1
  149. package/packages/daemon/dist/session-manager.js.map +1 -1
  150. package/packages/daemon/package.json +5 -5
  151. package/packages/gsd-agent-core/dist/compaction/compaction.d.ts +13 -1
  152. package/packages/gsd-agent-core/dist/compaction/compaction.d.ts.map +1 -1
  153. package/packages/gsd-agent-core/dist/compaction/compaction.js +15 -1
  154. package/packages/gsd-agent-core/dist/compaction/compaction.js.map +1 -1
  155. package/packages/gsd-agent-core/dist/session/agent-session-navigation.d.ts.map +1 -1
  156. package/packages/gsd-agent-core/dist/session/agent-session-navigation.js +4 -1
  157. package/packages/gsd-agent-core/dist/session/agent-session-navigation.js.map +1 -1
  158. package/packages/gsd-agent-core/package.json +5 -5
  159. package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-segment-walker.d.ts.map +1 -1
  160. package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-segment-walker.js +1 -0
  161. package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-segment-walker.js.map +1 -1
  162. package/packages/gsd-agent-modes/dist/modes/rpc/rpc-client.js +1 -1
  163. package/packages/gsd-agent-modes/dist/modes/rpc/rpc-client.js.map +1 -1
  164. package/packages/gsd-agent-modes/package.json +7 -7
  165. package/packages/mcp-server/dist/server.d.ts +18 -5
  166. package/packages/mcp-server/dist/server.d.ts.map +1 -1
  167. package/packages/mcp-server/dist/server.js +46 -5
  168. package/packages/mcp-server/dist/server.js.map +1 -1
  169. package/packages/mcp-server/dist/session-manager.js +1 -1
  170. package/packages/mcp-server/dist/session-manager.js.map +1 -1
  171. package/packages/mcp-server/package.json +4 -4
  172. package/packages/native/package.json +1 -1
  173. package/packages/pi-agent-core/package.json +1 -1
  174. package/packages/pi-ai/dist/env-api-keys.d.ts.map +1 -1
  175. package/packages/pi-ai/dist/env-api-keys.js +3 -0
  176. package/packages/pi-ai/dist/env-api-keys.js.map +1 -1
  177. package/packages/pi-ai/dist/types.d.ts +2 -2
  178. package/packages/pi-ai/dist/types.d.ts.map +1 -1
  179. package/packages/pi-ai/dist/types.js.map +1 -1
  180. package/packages/pi-ai/package.json +1 -1
  181. package/packages/pi-coding-agent/dist/core/model-resolver.d.ts.map +1 -1
  182. package/packages/pi-coding-agent/dist/core/model-resolver.js +1 -0
  183. package/packages/pi-coding-agent/dist/core/model-resolver.js.map +1 -1
  184. package/packages/pi-coding-agent/dist/core/session-manager-types.d.ts +1 -0
  185. package/packages/pi-coding-agent/dist/core/session-manager-types.d.ts.map +1 -1
  186. package/packages/pi-coding-agent/dist/core/session-manager-types.js.map +1 -1
  187. package/packages/pi-coding-agent/dist/core/session-manager.d.ts.map +1 -1
  188. package/packages/pi-coding-agent/dist/core/session-manager.js +3 -0
  189. package/packages/pi-coding-agent/dist/core/session-manager.js.map +1 -1
  190. package/packages/pi-coding-agent/package.json +7 -7
  191. package/packages/pi-tui/package.json +2 -2
  192. package/packages/rpc-client/dist/rpc-client.js +1 -1
  193. package/packages/rpc-client/dist/rpc-client.js.map +1 -1
  194. package/packages/rpc-client/package.json +2 -2
  195. package/pkg/package.json +1 -1
  196. package/scripts/link-workspace-packages.cjs +14 -4
  197. package/src/resources/GSD-WORKFLOW.md +20 -25
  198. package/src/resources/extensions/claude-code-cli/stream-adapter.ts +130 -51
  199. package/src/resources/extensions/claude-code-cli/tests/stream-adapter.test.ts +371 -39
  200. package/src/resources/extensions/cursor-cli/index.ts +18 -0
  201. package/src/resources/extensions/cursor-cli/models.ts +65 -0
  202. package/src/resources/extensions/cursor-cli/package.json +11 -0
  203. package/src/resources/extensions/cursor-cli/readiness.ts +143 -0
  204. package/src/resources/extensions/cursor-cli/stream-adapter.ts +440 -0
  205. package/src/resources/extensions/cursor-cli/tests/index.test.ts +54 -0
  206. package/src/resources/extensions/cursor-cli/tests/readiness.test.ts +29 -0
  207. package/src/resources/extensions/cursor-cli/tests/stream-adapter.test.ts +166 -0
  208. package/src/resources/extensions/gsd/activity-log.ts +4 -1
  209. package/src/resources/extensions/gsd/artifact-verification.ts +25 -9
  210. package/src/resources/extensions/gsd/auto/closeout.ts +54 -56
  211. package/src/resources/extensions/gsd/auto/finalize.ts +8 -0
  212. package/src/resources/extensions/gsd/auto/loop-deps.ts +1 -0
  213. package/src/resources/extensions/gsd/auto/orchestrator.ts +1 -1
  214. package/src/resources/extensions/gsd/auto/run-unit.ts +5 -2
  215. package/src/resources/extensions/gsd/auto/schedule-wakeup.ts +8 -0
  216. package/src/resources/extensions/gsd/auto-dispatch.ts +17 -5
  217. package/src/resources/extensions/gsd/auto-model-selection.ts +49 -18
  218. package/src/resources/extensions/gsd/auto-prompts.ts +73 -2
  219. package/src/resources/extensions/gsd/auto-start.ts +4 -3
  220. package/src/resources/extensions/gsd/auto-timeout-recovery.ts +4 -0
  221. package/src/resources/extensions/gsd/auto-worktree.ts +50 -11
  222. package/src/resources/extensions/gsd/auto.ts +22 -3
  223. package/src/resources/extensions/gsd/bootstrap/db-tools.ts +3 -0
  224. package/src/resources/extensions/gsd/bootstrap/register-hooks.ts +128 -5
  225. package/src/resources/extensions/gsd/bootstrap/schedule-wakeup-tool.ts +80 -12
  226. package/src/resources/extensions/gsd/bootstrap/tool-call-loop-guard.ts +12 -2
  227. package/src/resources/extensions/gsd/bootstrap/write-gate.ts +23 -1
  228. package/src/resources/extensions/gsd/codebase-generator.ts +179 -18
  229. package/src/resources/extensions/gsd/commands/handlers/core.ts +39 -3
  230. package/src/resources/extensions/gsd/commands-prefs-wizard.ts +135 -1
  231. package/src/resources/extensions/gsd/commands-worktree.ts +13 -2
  232. package/src/resources/extensions/gsd/db/queries.ts +40 -0
  233. package/src/resources/extensions/gsd/discussion-handoff.ts +10 -2
  234. package/src/resources/extensions/gsd/docs/preferences-reference.md +8 -2
  235. package/src/resources/extensions/gsd/doctor-engine-checks.ts +269 -18
  236. package/src/resources/extensions/gsd/doctor-providers.ts +39 -10
  237. package/src/resources/extensions/gsd/doctor-types.ts +5 -1
  238. package/src/resources/extensions/gsd/doctor.ts +95 -3
  239. package/src/resources/extensions/gsd/exec-sandbox.ts +6 -2
  240. package/src/resources/extensions/gsd/flat-phase-migration.ts +98 -22
  241. package/src/resources/extensions/gsd/git-service.ts +14 -17
  242. package/src/resources/extensions/gsd/gsd-db.ts +72 -0
  243. package/src/resources/extensions/gsd/guidance.ts +2 -0
  244. package/src/resources/extensions/gsd/guided-flow.ts +45 -2
  245. package/src/resources/extensions/gsd/key-manager.ts +1 -0
  246. package/src/resources/extensions/gsd/markdown-renderer.ts +25 -7
  247. package/src/resources/extensions/gsd/migrate-external.ts +23 -6
  248. package/src/resources/extensions/gsd/milestone-merge-transaction.ts +4 -4
  249. package/src/resources/extensions/gsd/model-router.ts +4 -5
  250. package/src/resources/extensions/gsd/preferences-diagnostics.ts +24 -0
  251. package/src/resources/extensions/gsd/preferences-models.ts +6 -3
  252. package/src/resources/extensions/gsd/preferences-validation.ts +28 -2
  253. package/src/resources/extensions/gsd/preferences.ts +38 -4
  254. package/src/resources/extensions/gsd/prompts/plan-slice.md +4 -2
  255. package/src/resources/extensions/gsd/redact-secrets.ts +40 -0
  256. package/src/resources/extensions/gsd/repo-identity.ts +17 -0
  257. package/src/resources/extensions/gsd/repository-registry.ts +7 -1
  258. package/src/resources/extensions/gsd/root-write-leak-guard.ts +3 -2
  259. package/src/resources/extensions/gsd/safe-fs.ts +22 -2
  260. package/src/resources/extensions/gsd/skills/gsd-headless/SKILL.md +7 -9
  261. package/src/resources/extensions/gsd/skills/gsd-headless/references/commands.md +1 -0
  262. package/src/resources/extensions/gsd/tests/auto-loop.test.ts +131 -5
  263. package/src/resources/extensions/gsd/tests/auto-migrating-recovery.test.ts +18 -0
  264. package/src/resources/extensions/gsd/tests/auto-model-selection.test.ts +178 -0
  265. package/src/resources/extensions/gsd/tests/auto-phases-lifecycle.test.ts +43 -1
  266. package/src/resources/extensions/gsd/tests/auto-recovery.test.ts +54 -0
  267. package/src/resources/extensions/gsd/tests/auto-remote-session-lock-cleanup.test.ts +41 -0
  268. package/src/resources/extensions/gsd/tests/auto-runtime-state.test.ts +41 -0
  269. package/src/resources/extensions/gsd/tests/auto-session-scope.test.ts +4 -4
  270. package/src/resources/extensions/gsd/tests/auto-start-bootstrap-await-3420.test.ts +3 -0
  271. package/src/resources/extensions/gsd/tests/auto-timeout-recovery-harness-abort.test.ts +55 -0
  272. package/src/resources/extensions/gsd/tests/auto-warning-noise-regression.test.ts +8 -0
  273. package/src/resources/extensions/gsd/tests/batch-task-query.test.ts +85 -0
  274. package/src/resources/extensions/gsd/tests/check-auto-start-ready-guard.test.ts +29 -0
  275. package/src/resources/extensions/gsd/tests/codebase-generator.test.ts +365 -0
  276. package/src/resources/extensions/gsd/tests/complete-milestone-excerpt.test.ts +62 -1
  277. package/src/resources/extensions/gsd/tests/doctor-providers.test.ts +138 -1
  278. package/src/resources/extensions/gsd/tests/doctor-scope-db-unavailable.test.ts +397 -5
  279. package/src/resources/extensions/gsd/tests/doctor-workspace.test.ts +207 -0
  280. package/src/resources/extensions/gsd/tests/flat-phase-migration.test.ts +38 -1
  281. package/src/resources/extensions/gsd/tests/flat-rate-routing-guard.test.ts +87 -19
  282. package/src/resources/extensions/gsd/tests/gate-1b-orphan-discrimination.test.ts +89 -0
  283. package/src/resources/extensions/gsd/tests/guided-flow-session-isolation.test.ts +9 -0
  284. package/src/resources/extensions/gsd/tests/integration/state-machine-edge-cases.test.ts +4 -2
  285. package/src/resources/extensions/gsd/tests/markdown-renderer.test.ts +122 -0
  286. package/src/resources/extensions/gsd/tests/merge-conflict-stops-loop.test.ts +4 -4
  287. package/src/resources/extensions/gsd/tests/migrate-external-worktree.test.ts +116 -0
  288. package/src/resources/extensions/gsd/tests/milestone-merge-stash-restore.test.ts +152 -22
  289. package/src/resources/extensions/gsd/tests/milestone-merge-transaction.test.ts +1 -1
  290. package/src/resources/extensions/gsd/tests/pending-autostart-scope.test.ts +2 -2
  291. package/src/resources/extensions/gsd/tests/phases-merge-error-stops-auto.test.ts +50 -6
  292. package/src/resources/extensions/gsd/tests/plan-slice-prompt.test.ts +17 -0
  293. package/src/resources/extensions/gsd/tests/plan-task.test.ts +264 -1
  294. package/src/resources/extensions/gsd/tests/preferences.test.ts +150 -0
  295. package/src/resources/extensions/gsd/tests/prefs-wizard-coverage.test.ts +142 -0
  296. package/src/resources/extensions/gsd/tests/recovery-verify-logs.test.ts +4 -4
  297. package/src/resources/extensions/gsd/tests/redact-secrets.test.ts +56 -0
  298. package/src/resources/extensions/gsd/tests/register-hooks-flat-phase-migration.test.ts +73 -0
  299. package/src/resources/extensions/gsd/tests/register-hooks-loop-guard-auto.test.ts +275 -0
  300. package/src/resources/extensions/gsd/tests/repository-registry.test.ts +41 -2
  301. package/src/resources/extensions/gsd/tests/require-slice-discussion-dispatch.test.ts +55 -0
  302. package/src/resources/extensions/gsd/tests/root-write-leak-guard.test.ts +31 -9
  303. package/src/resources/extensions/gsd/tests/schedule-wakeup-tool.test.ts +167 -0
  304. package/src/resources/extensions/gsd/tests/stalled-tool-recovery.test.ts +49 -0
  305. package/src/resources/extensions/gsd/tests/state-corruption-2945.test.ts +1 -1
  306. package/src/resources/extensions/gsd/tests/status-per-repo.test.ts +94 -0
  307. package/src/resources/extensions/gsd/tests/token-tool-gating.test.ts +35 -0
  308. package/src/resources/extensions/gsd/tests/tool-call-loop-guard.test.ts +63 -0
  309. package/src/resources/extensions/gsd/tests/unit-context-manifest.test.ts +66 -18
  310. package/src/resources/extensions/gsd/tests/unit-registry.test.ts +56 -0
  311. package/src/resources/extensions/gsd/tests/unit-runtime.test.ts +30 -0
  312. package/src/resources/extensions/gsd/tests/uok-gitops-turn-action.test.ts +52 -0
  313. package/src/resources/extensions/gsd/tests/validator-scope-parity.test.ts +53 -1
  314. package/src/resources/extensions/gsd/tests/workflow-tool-executors.test.ts +208 -1
  315. package/src/resources/extensions/gsd/tests/workspace.test.ts +18 -4
  316. package/src/resources/extensions/gsd/tests/worktree-journal-events.test.ts +4 -4
  317. package/src/resources/extensions/gsd/tests/worktree-lifecycle.test.ts +2 -2
  318. package/src/resources/extensions/gsd/tests/worktree-sync-milestones.test.ts +31 -1
  319. package/src/resources/extensions/gsd/tests/worktree-teardown-safety.test.ts +32 -0
  320. package/src/resources/extensions/gsd/tools/plan-task.ts +107 -9
  321. package/src/resources/extensions/gsd/tools/workflow-tool-executors.ts +58 -0
  322. package/src/resources/extensions/gsd/unit-context-manifest.ts +11 -5
  323. package/src/resources/extensions/gsd/unit-registry.ts +34 -2
  324. package/src/resources/extensions/gsd/unit-runtime.ts +42 -0
  325. package/src/resources/extensions/gsd/workspace.ts +42 -8
  326. package/src/resources/extensions/gsd/worktree-lifecycle.ts +135 -11
  327. package/src/resources/extensions/gsd/worktree-manager.ts +143 -5
  328. /package/dist/web/standalone/.next/static/{zQLcXm0Za8lexoXtgDpMG → bCg3aYrFN1DKZr2dqgE7L}/_buildManifest.js +0 -0
  329. /package/dist/web/standalone/.next/static/{zQLcXm0Za8lexoXtgDpMG → bCg3aYrFN1DKZr2dqgE7L}/_ssgManifest.js +0 -0
@@ -12,6 +12,7 @@ import { appendEvent } from "../workflow-events.js";
12
12
  import { logWarning } from "../workflow-logger.js";
13
13
  import { loadEffectiveGSDPreferences } from "../preferences.js";
14
14
  import { validatePathOnlyPlanningFields, validatePlanningPathScope } from "../planning-path-scope.js";
15
+ import { createRepositoryRegistryFromPreferences, defaultRepositoryTargets, type RepositoryRegistry } from "../repository-registry.js";
15
16
  import type { GateId } from "../types.js";
16
17
 
17
18
  export interface PlanTaskParams {
@@ -26,6 +27,8 @@ export interface PlanTaskParams {
26
27
  inputs: string[];
27
28
  expectedOutput: string[];
28
29
  observabilityImpact?: string;
30
+ /** Repository id(s) this task touches (parent workspace); omitted for single-repo projects. */
31
+ targetRepositories?: string[];
29
32
  fullPlanMd?: string;
30
33
  /** Optional caller-provided identity for audit trail */
31
34
  actorName?: string;
@@ -40,6 +43,63 @@ export interface PlanTaskResult {
40
43
  taskPlanPath: string;
41
44
  }
42
45
 
46
+ function validateRepositoryTargetIds(field: string, value: unknown): string[] {
47
+ const ids = validateStringArray(value, field);
48
+ if (ids.length === 0) throw new Error(`${field} must include at least one repository id when provided`);
49
+ const deduped = Array.from(new Set(ids.map((id) => id.trim()).filter(Boolean)));
50
+ if (deduped.length === 0) throw new Error(`${field} must include at least one repository id when provided`);
51
+ return deduped;
52
+ }
53
+
54
+ function validateReferencedRepositories(
55
+ targetRepositories: string[] | undefined,
56
+ registry: RepositoryRegistry,
57
+ ): string | null {
58
+ if (!targetRepositories) return null;
59
+ const known = new Set(registry.repositories.map((repo) => repo.id));
60
+ const missing = targetRepositories.filter((id) => !known.has(id));
61
+ if (missing.length === 0) return null;
62
+ return `unknown targetRepositories: ${missing.join(", ")}. Declared repositories: ${Array.from(known).join(", ")}`;
63
+ }
64
+
65
+ function resolveAllowedRootsForPathScope(
66
+ targetRepositories: string[],
67
+ registry: RepositoryRegistry,
68
+ ): string[] {
69
+ if (targetRepositories.length === 0) return [registry.projectRoot];
70
+ const roots = targetRepositories
71
+ .map((id) => registry.byId.get(id)?.root)
72
+ .filter((root): root is string => typeof root === "string");
73
+ return roots.length > 0 ? roots : [registry.projectRoot];
74
+ }
75
+
76
+ function validatePathScopeForTargetRepositories(
77
+ params: PlanTaskParams,
78
+ basePath: string,
79
+ registry: RepositoryRegistry,
80
+ targetRepositories: string[],
81
+ ): string | null {
82
+ return validatePlanningPathScope(
83
+ basePath,
84
+ [
85
+ { field: "files", values: params.files },
86
+ { field: "inputs", values: params.inputs },
87
+ { field: "expectedOutput", values: params.expectedOutput },
88
+ ],
89
+ resolveAllowedRootsForPathScope(targetRepositories, registry),
90
+ );
91
+ }
92
+
93
+ function resolveEffectiveTargetRepositories(
94
+ taskTargetRepositories: string[] | undefined,
95
+ sliceTargetRepositories: string[] | undefined,
96
+ defaultTargets: string[],
97
+ ): string[] {
98
+ if (taskTargetRepositories) return taskTargetRepositories;
99
+ if (sliceTargetRepositories?.length) return sliceTargetRepositories;
100
+ return defaultTargets;
101
+ }
102
+
43
103
  function validateParams(params: PlanTaskParams): PlanTaskParams {
44
104
  if (!isNonEmptyString(params?.milestoneId)) throw new Error("milestoneId is required");
45
105
  if (!isNonEmptyString(params?.sliceId)) throw new Error("sliceId is required");
@@ -57,6 +117,9 @@ function validateParams(params: PlanTaskParams): PlanTaskParams {
57
117
  files: validateStringArray(params.files, "files"),
58
118
  inputs: validateStringArray(params.inputs, "inputs"),
59
119
  expectedOutput: validateStringArray(params.expectedOutput, "expectedOutput"),
120
+ ...(params.targetRepositories !== undefined
121
+ ? { targetRepositories: validateRepositoryTargetIds("targetRepositories", params.targetRepositories) }
122
+ : {}),
60
123
  };
61
124
  }
62
125
 
@@ -84,23 +147,19 @@ export async function handlePlanTask(
84
147
  return { error: `validation failed: ${pathOnlyError}` };
85
148
  }
86
149
 
87
- const pathScopeError = validatePlanningPathScope(basePath, [
88
- { field: "files", values: params.files },
89
- { field: "inputs", values: params.inputs },
90
- { field: "expectedOutput", values: params.expectedOutput },
91
- ]);
92
- if (pathScopeError) {
93
- return { error: `validation failed: ${pathScopeError}` };
94
- }
95
-
96
150
  let taskGates: GateId[];
151
+ let repositoryRegistry: RepositoryRegistry;
97
152
  try {
98
153
  taskGates = resolveTaskGates(basePath);
154
+ const loaded = loadEffectiveGSDPreferences(basePath);
155
+ repositoryRegistry = createRepositoryRegistryFromPreferences(basePath, loaded?.preferences);
99
156
  } catch (err) {
100
157
  const message = err instanceof Error ? err.message : String(err);
101
158
  return { error: `validation failed: ${message}` };
102
159
  }
103
160
 
161
+ const defaultTargets = defaultRepositoryTargets(repositoryRegistry);
162
+
104
163
  // ── Guards + DB writes inside a single transaction (prevents TOCTOU) ───
105
164
  // Guards must be inside the transaction so the state they check cannot
106
165
  // change between the read and the write (#2723).
@@ -124,6 +183,44 @@ export async function handlePlanTask(
124
183
  return;
125
184
  }
126
185
 
186
+ let effectiveTargetRepositories = resolveEffectiveTargetRepositories(
187
+ params.targetRepositories,
188
+ parentSlice.target_repositories,
189
+ defaultTargets,
190
+ );
191
+ const repoValidationError = validateReferencedRepositories(effectiveTargetRepositories, repositoryRegistry);
192
+ if (repoValidationError) {
193
+ guardError = `validation failed: ${repoValidationError}`;
194
+ return;
195
+ }
196
+
197
+ let pathScopeError = validatePathScopeForTargetRepositories(
198
+ params,
199
+ basePath,
200
+ repositoryRegistry,
201
+ effectiveTargetRepositories,
202
+ );
203
+ const storedTaskTargets = existingTask?.target_repositories?.length
204
+ ? existingTask.target_repositories
205
+ : undefined;
206
+ // Omitted targetRepositories inherit the current slice default first.
207
+ // Fall back to the stored task target only when that inherited scope
208
+ // rejects the replan paths, preserving explicit per-task parent-root work.
209
+ if (pathScopeError && params.targetRepositories === undefined && storedTaskTargets) {
210
+ const storedRepoValidationError = validateReferencedRepositories(storedTaskTargets, repositoryRegistry);
211
+ const storedPathScopeError = storedRepoValidationError
212
+ ? storedRepoValidationError
213
+ : validatePathScopeForTargetRepositories(params, basePath, repositoryRegistry, storedTaskTargets);
214
+ if (!storedPathScopeError) {
215
+ effectiveTargetRepositories = storedTaskTargets;
216
+ pathScopeError = null;
217
+ }
218
+ }
219
+ if (pathScopeError) {
220
+ guardError = `validation failed: ${pathScopeError}`;
221
+ return;
222
+ }
223
+
127
224
  if (!existingTask) {
128
225
  insertTask({
129
226
  id: params.taskId,
@@ -143,6 +240,7 @@ export async function handlePlanTask(
143
240
  expectedOutput: params.expectedOutput,
144
241
  observabilityImpact: params.observabilityImpact ?? "",
145
242
  fullPlanMd: params.fullPlanMd,
243
+ targetRepositories: effectiveTargetRepositories,
146
244
  });
147
245
  for (const gid of taskGates) {
148
246
  insertGateRow({ milestoneId: params.milestoneId, sliceId: params.sliceId, gateId: gid, scope: "task", taskId: params.taskId });
@@ -53,11 +53,13 @@ import { loadEffectiveGSDPreferences } from "../preferences.js";
53
53
  import { parseProject } from "../schemas/parsers.js";
54
54
  import { autoSession, getAutoRuntimeSnapshot, isAutoActive } from "../auto-runtime-state.js";
55
55
  import { renderPlanFromDb } from "../markdown-renderer.js";
56
+ import { readUnitHarnessAbort, type UnitHarnessAbortRecord } from "../unit-runtime.js";
56
57
  import {
57
58
  prepareUatRun,
58
59
  saveUatAttemptArtifact,
59
60
  type UatResultSaveParams,
60
61
  } from "../uat-run.js";
62
+ import { appendNotification } from "../notification-store.js";
61
63
  import { registerAutoWorker, markWorkerStopping, getAutoWorker } from "../db/auto-workers.js";
62
64
  import {
63
65
  claimMilestoneLease,
@@ -116,6 +118,48 @@ function blockIfWrongAutoUnit(requiredUnitType: string, operation: string): Tool
116
118
  };
117
119
  }
118
120
 
121
+ function blockIfHarnessAbortedUnit(operation: string, basePath: string): ToolExecutionResult | null {
122
+ const snapshot = getAutoRuntimeSnapshot();
123
+ if (!snapshot.active || !snapshot.currentUnit) return null;
124
+
125
+ const abort = readUnitHarnessAbort(
126
+ snapshot.basePath || basePath,
127
+ snapshot.currentUnit.type,
128
+ snapshot.currentUnit.id,
129
+ snapshot.currentUnit.startedAt,
130
+ );
131
+ if (!abort) return null;
132
+
133
+ return harnessAbortResult(operation, snapshot.currentUnit.type, snapshot.currentUnit.id, abort);
134
+ }
135
+
136
+ function harnessAbortResult(
137
+ operation: string,
138
+ unitType: string,
139
+ unitId: string,
140
+ abort: UnitHarnessAbortRecord,
141
+ ): ToolExecutionResult {
142
+ const toolText = abort.toolName ? ` while calling ${abort.toolName}` : "";
143
+ const message =
144
+ `Retryable harness abort: ${unitType} ${unitId} was truncated by ${abort.kind}${toolText}. ` +
145
+ "This gate result was not saved; leave the gate pending so auto-mode can rerun it.";
146
+ return {
147
+ content: [{ type: "text", text: message }],
148
+ details: {
149
+ operation,
150
+ error: "harness_aborted_needs_retry",
151
+ retryable: true,
152
+ unitType,
153
+ unitId,
154
+ harnessAbortKind: abort.kind,
155
+ harnessAbortReason: abort.reason,
156
+ harnessAbortToolName: abort.toolName,
157
+ harnessAbortCount: abort.count,
158
+ },
159
+ isError: true,
160
+ };
161
+ }
162
+
119
163
  function milestoneLeaseConflictResult(
120
164
  milestoneId: string,
121
165
  byWorker: string,
@@ -1135,6 +1179,9 @@ export async function executeSaveGateResult(
1135
1179
  params: SaveGateResultParams,
1136
1180
  basePath: string = process.cwd(),
1137
1181
  ): Promise<ToolExecutionResult> {
1182
+ const harnessAbort = blockIfHarnessAbortedUnit("save_gate_result", basePath);
1183
+ if (harnessAbort) return harnessAbort;
1184
+
1138
1185
  const dbAvailable = await ensureDbOpen(basePath);
1139
1186
  if (!dbAvailable) {
1140
1187
  return {
@@ -1206,6 +1253,9 @@ export async function executeUatResultSave(
1206
1253
  const unitGuard = blockIfWrongAutoUnit("run-uat", "save_uat_result");
1207
1254
  if (unitGuard) return unitGuard;
1208
1255
 
1256
+ const harnessAbort = blockIfHarnessAbortedUnit("save_uat_result", basePath);
1257
+ if (harnessAbort) return harnessAbort;
1258
+
1209
1259
  const dbAvailable = await ensureDbOpen(basePath);
1210
1260
  if (!dbAvailable) return errorResult("save_uat_result", "GSD database is not available.", "db_unavailable");
1211
1261
 
@@ -1268,6 +1318,14 @@ export async function executeUatResultSave(
1268
1318
  evaluatedAt: run.evaluatedAt,
1269
1319
  });
1270
1320
  invalidateStateCache();
1321
+ if (run.hasHuman) {
1322
+ appendNotification(
1323
+ `UAT for ${run.params.milestoneId}/${run.params.sliceId} has NEEDS-HUMAN checks awaiting human validation`,
1324
+ "warning",
1325
+ "notify",
1326
+ { kind: "uat-needs-human", scope: `${run.params.milestoneId}/${run.params.sliceId}` },
1327
+ );
1328
+ }
1271
1329
  const savedText = `UAT result saved for ${run.params.milestoneId}/${run.params.sliceId}: ${run.params.verdict}`;
1272
1330
  return {
1273
1331
  content: [{
@@ -140,6 +140,10 @@ export type ContextModePolicy =
140
140
  * restricted to .gsd/**. Subagent dispatch is denied unless
141
141
  * `allowedSubagents` opts a unit into controlled read-only
142
142
  * specialist delegation.
143
+ * - "workflow-only"
144
+ * — Read tools + GSD workflow tools only. No generic writes
145
+ * and no generic shell; used by lanes whose durable output
146
+ * must be produced by a dedicated DB-backed workflow tool.
143
147
  *
144
148
  * The allowlist for "docs" is declared per-manifest rather than hardcoded so
145
149
  * projects with non-standard doc layouts can extend it without forking the
@@ -152,7 +156,8 @@ export type ToolsPolicy =
152
156
  | { readonly mode: "planning" }
153
157
  | { readonly mode: "planning-dispatch"; readonly allowedSubagents: readonly string[] }
154
158
  | { readonly mode: "docs"; readonly allowedPathGlobs: readonly string[] }
155
- | { readonly mode: "verification"; readonly allowedSubagents?: readonly string[] };
159
+ | { readonly mode: "verification"; readonly allowedSubagents?: readonly string[] }
160
+ | { readonly mode: "workflow-only"; readonly allowedSubagents?: readonly string[] };
156
161
 
157
162
  // ─── Computed-artifact registry (#4924 v2 contract) ───────────────────────
158
163
 
@@ -405,9 +410,10 @@ export const UNIT_MANIFESTS: Record<UnitType, UnitContextManifest> = {
405
410
  codebaseMap: false,
406
411
  preferences: "active-only",
407
412
  contextMode: "verification",
408
- // Validation may need to run shell verification commands and apply source
409
- // fixes before milestone closeout can proceed.
410
- tools: TOOLS_ALL,
413
+ // Validation must persist its durable output through gsd_validate_milestone;
414
+ // generic write/bash tools can create filesystem artifacts that bypass DB
415
+ // state and trigger destructive recovery loops.
416
+ tools: { mode: "workflow-only", allowedSubagents: ["reviewer", "security", "tester"] },
411
417
  artifacts: {
412
418
  inline: ["roadmap", "requirements", "templates"],
413
419
  excerpt: ["slice-summary", "slice-assessment"],
@@ -774,7 +780,7 @@ export function compileSubagentPermissionContract(
774
780
  return { allowed: true, allowedSubagents: ["*"], toolsMode: policy.mode };
775
781
  }
776
782
  if (
777
- (policy.mode === "planning-dispatch" || policy.mode === "verification") &&
783
+ (policy.mode === "planning-dispatch" || policy.mode === "verification" || policy.mode === "workflow-only") &&
778
784
  Array.isArray(policy.allowedSubagents)
779
785
  ) {
780
786
  return {
@@ -20,8 +20,9 @@
20
20
  //
21
21
  // Not yet declared here (remaining ADR-033 steps): the manifest data
22
22
  // (`UNIT_MANIFESTS` stays in unit-context-manifest.ts, already type-enforced
23
- // against the registry's `UnitType`) and prompt-template association (still
24
- // implicit in auto-prompts.ts builders).
23
+ // against the registry's `UnitType`). Prompt-template association is declared
24
+ // for direct one-template units; conditional/composite prompt selection stays
25
+ // with the prompt builders until it has a richer registry shape.
25
26
 
26
27
  import type { CanonicalWorkflowToolName } from "@opengsd/contracts";
27
28
  import { BROWSER_CONTRACT_TOOL_NAMES } from "../shared/browser-contract.js";
@@ -61,6 +62,12 @@ export interface UnitDescriptor {
61
62
  * resolves against session defaults).
62
63
  */
63
64
  readonly phaseChain: readonly GSDModelPhaseKey[] | null;
65
+ /**
66
+ * Prompt template id passed to loadPrompt for units with a direct
67
+ * one-template builder. Omitted for conditional, composite, or not-yet
68
+ * verified prompt associations.
69
+ */
70
+ readonly promptTemplate?: string;
64
71
  /** `null` = the unit has no scoped gsd-tool contract. */
65
72
  readonly toolContract: UnitToolSurfaceContract | null;
66
73
  }
@@ -97,6 +104,7 @@ export const UNIT_REGISTRY = {
97
104
  kind: "primary",
98
105
  scopeClass: "standard",
99
106
  phaseChain: ["research"],
107
+ promptTemplate: "research-milestone",
100
108
  toolContract: {
101
109
  allowedGsdTools: [
102
110
  "gsd_summary_save",
@@ -112,6 +120,7 @@ export const UNIT_REGISTRY = {
112
120
  kind: "primary",
113
121
  scopeClass: "standard",
114
122
  phaseChain: ["planning"],
123
+ promptTemplate: "plan-milestone",
115
124
  toolContract: {
116
125
  allowedGsdTools: [
117
126
  "gsd_milestone_status",
@@ -160,6 +169,7 @@ export const UNIT_REGISTRY = {
160
169
  kind: "primary",
161
170
  scopeClass: "section-close",
162
171
  phaseChain: ["validation", "planning"],
172
+ promptTemplate: "validate-milestone",
163
173
  toolContract: {
164
174
  allowedGsdTools: [
165
175
  "gsd_milestone_status",
@@ -177,6 +187,7 @@ export const UNIT_REGISTRY = {
177
187
  kind: "primary",
178
188
  scopeClass: "standard",
179
189
  phaseChain: ["completion"],
190
+ promptTemplate: "complete-milestone",
180
191
  toolContract: {
181
192
  allowedGsdTools: [
182
193
  "gsd_milestone_status",
@@ -200,6 +211,7 @@ export const UNIT_REGISTRY = {
200
211
  kind: "primary",
201
212
  scopeClass: "standard",
202
213
  phaseChain: ["research"],
214
+ promptTemplate: "research-slice",
203
215
  toolContract: {
204
216
  allowedGsdTools: [
205
217
  "gsd_milestone_status",
@@ -216,6 +228,7 @@ export const UNIT_REGISTRY = {
216
228
  kind: "primary",
217
229
  scopeClass: "standard",
218
230
  phaseChain: ["planning"],
231
+ promptTemplate: "plan-slice",
219
232
  toolContract: {
220
233
  allowedGsdTools: [
221
234
  "gsd_milestone_status",
@@ -234,6 +247,7 @@ export const UNIT_REGISTRY = {
234
247
  kind: "primary",
235
248
  scopeClass: "standard",
236
249
  phaseChain: ["planning"],
250
+ promptTemplate: "refine-slice",
237
251
  toolContract: {
238
252
  allowedGsdTools: [
239
253
  "gsd_milestone_status",
@@ -250,6 +264,7 @@ export const UNIT_REGISTRY = {
250
264
  kind: "primary",
251
265
  scopeClass: "standard",
252
266
  phaseChain: ["planning"],
267
+ promptTemplate: "replan-slice",
253
268
  toolContract: {
254
269
  allowedGsdTools: ["gsd_replan_slice", "gsd_decision_save"],
255
270
  requiredWorkflowTools: ["gsd_replan_slice"],
@@ -259,6 +274,7 @@ export const UNIT_REGISTRY = {
259
274
  kind: "primary",
260
275
  scopeClass: "section-close",
261
276
  phaseChain: ["completion"],
277
+ promptTemplate: "complete-slice",
262
278
  toolContract: {
263
279
  allowedGsdTools: [
264
280
  "gsd_milestone_status",
@@ -293,6 +309,7 @@ export const UNIT_REGISTRY = {
293
309
  kind: "primary",
294
310
  scopeClass: "standard",
295
311
  phaseChain: ["validation", "planning"],
312
+ promptTemplate: "reassess-roadmap",
296
313
  toolContract: {
297
314
  allowedGsdTools: ["gsd_milestone_status", "gsd_reassess_roadmap"],
298
315
  requiredWorkflowTools: ["gsd_milestone_status", "gsd_reassess_roadmap"],
@@ -302,6 +319,7 @@ export const UNIT_REGISTRY = {
302
319
  kind: "primary",
303
320
  scopeClass: "execute-task",
304
321
  phaseChain: ["execution"],
322
+ promptTemplate: "execute-task",
305
323
  toolContract: {
306
324
  allowedGsdTools: [
307
325
  "gsd_task_complete",
@@ -346,6 +364,7 @@ export const UNIT_REGISTRY = {
346
364
  kind: "primary",
347
365
  scopeClass: "execute-task",
348
366
  phaseChain: ["execution"],
367
+ promptTemplate: "reactive-execute",
349
368
  toolContract: {
350
369
  allowedGsdTools: [
351
370
  "gsd_milestone_status",
@@ -361,6 +380,7 @@ export const UNIT_REGISTRY = {
361
380
  kind: "primary",
362
381
  scopeClass: "standard",
363
382
  phaseChain: ["uat", "completion"],
383
+ promptTemplate: "run-uat",
364
384
  toolContract: {
365
385
  allowedGsdTools: [...RUN_UAT_WORKFLOW_TOOL_NAMES, "subagent"],
366
386
  requiredWorkflowTools: [...RUN_UAT_WORKFLOW_TOOL_NAMES],
@@ -375,6 +395,7 @@ export const UNIT_REGISTRY = {
375
395
  kind: "primary",
376
396
  scopeClass: "standard",
377
397
  phaseChain: ["validation", "planning"],
398
+ promptTemplate: "gate-evaluate",
378
399
  toolContract: {
379
400
  allowedGsdTools: ["gsd_save_gate_result"],
380
401
  requiredWorkflowTools: ["gsd_save_gate_result"],
@@ -384,6 +405,7 @@ export const UNIT_REGISTRY = {
384
405
  kind: "primary",
385
406
  scopeClass: "standard",
386
407
  phaseChain: ["validation", "planning"],
408
+ promptTemplate: "rewrite-docs",
387
409
  toolContract: {
388
410
  allowedGsdTools: ["gsd_summary_save", "gsd_decision_save"],
389
411
  requiredWorkflowTools: [],
@@ -408,6 +430,7 @@ export const UNIT_REGISTRY = {
408
430
  kind: "primary",
409
431
  scopeClass: "standard",
410
432
  phaseChain: ["discuss", "planning"],
433
+ promptTemplate: "guided-workflow-preferences",
411
434
  toolContract: {
412
435
  allowedGsdTools: ["gsd_summary_save"],
413
436
  requiredWorkflowTools: [],
@@ -417,6 +440,7 @@ export const UNIT_REGISTRY = {
417
440
  kind: "primary",
418
441
  scopeClass: "standard",
419
442
  phaseChain: ["discuss", "planning"],
443
+ promptTemplate: "guided-discuss-project",
420
444
  toolContract: {
421
445
  allowedGsdTools: ["gsd_summary_save", "gsd_decision_save", "gsd_requirement_save"],
422
446
  requiredWorkflowTools: ["ask_user_questions", "gsd_summary_save"],
@@ -426,6 +450,7 @@ export const UNIT_REGISTRY = {
426
450
  kind: "primary",
427
451
  scopeClass: "standard",
428
452
  phaseChain: ["discuss", "planning"],
453
+ promptTemplate: "guided-discuss-requirements",
429
454
  toolContract: {
430
455
  allowedGsdTools: ["gsd_requirement_save", "gsd_summary_save"],
431
456
  requiredWorkflowTools: ["ask_user_questions", "gsd_requirement_save", "gsd_summary_save"],
@@ -435,6 +460,7 @@ export const UNIT_REGISTRY = {
435
460
  kind: "primary",
436
461
  scopeClass: "standard",
437
462
  phaseChain: ["discuss", "planning"],
463
+ promptTemplate: "guided-research-decision",
438
464
  toolContract: {
439
465
  allowedGsdTools: ["gsd_summary_save"],
440
466
  requiredWorkflowTools: ["ask_user_questions"],
@@ -447,6 +473,7 @@ export const UNIT_REGISTRY = {
447
473
  kind: "primary",
448
474
  scopeClass: "standard",
449
475
  phaseChain: ["research"],
476
+ promptTemplate: "guided-research-project",
450
477
  toolContract: {
451
478
  allowedGsdTools: [],
452
479
  requiredWorkflowTools: [],
@@ -494,3 +521,8 @@ export function getUnitDescriptor(unitType: string): UnitDescriptor | undefined
494
521
  export function getUnitPhaseChain(unitType: string): readonly GSDModelPhaseKey[] | null {
495
522
  return getUnitDescriptor(unitType)?.phaseChain ?? null;
496
523
  }
524
+
525
+ /** Prompt template id for units with a direct one-template builder. */
526
+ export function getUnitPromptTemplate(unitType: string): string | undefined {
527
+ return getUnitDescriptor(unitType)?.promptTemplate;
528
+ }
@@ -100,6 +100,14 @@ export interface ExecuteTaskRecoveryStatus {
100
100
  mustHavesMentionedInSummary: number;
101
101
  }
102
102
 
103
+ export interface UnitHarnessAbortRecord {
104
+ kind: "tool-loop-guard" | "tool-error" | "turn-abort";
105
+ reason: string;
106
+ toolName?: string;
107
+ count?: number;
108
+ recordedAt: number;
109
+ }
110
+
103
111
  export interface AutoUnitRuntimeRecord {
104
112
  version: 1;
105
113
  unitType: string;
@@ -116,6 +124,7 @@ export interface AutoUnitRuntimeRecord {
116
124
  recovery?: ExecuteTaskRecoveryStatus;
117
125
  recoveryAttempts?: number;
118
126
  lastRecoveryReason?: "idle" | "hard";
127
+ harnessAbort?: UnitHarnessAbortRecord;
119
128
  }
120
129
 
121
130
  function runtimeDir(basePath: string): string {
@@ -138,6 +147,8 @@ export function writeUnitRuntimeRecord(
138
147
  const path = runtimePath(basePath, unitType, unitId);
139
148
  return withRecordLock(path, () => {
140
149
  const prev = readUnitRuntimeRecord(basePath, unitType, unitId);
150
+ const sameRun = prev?.startedAt === startedAt;
151
+ const updatesHarnessAbort = Object.prototype.hasOwnProperty.call(updates, "harnessAbort");
141
152
  const next: AutoUnitRuntimeRecord = {
142
153
  version: 1,
143
154
  unitType,
@@ -154,12 +165,32 @@ export function writeUnitRuntimeRecord(
154
165
  recovery: updates.recovery ?? prev?.recovery,
155
166
  recoveryAttempts: updates.recoveryAttempts ?? prev?.recoveryAttempts ?? 0,
156
167
  lastRecoveryReason: updates.lastRecoveryReason ?? prev?.lastRecoveryReason,
168
+ harnessAbort: updatesHarnessAbort
169
+ ? updates.harnessAbort
170
+ : (sameRun ? prev?.harnessAbort : undefined),
157
171
  };
158
172
  atomicWriteSync(path, JSON.stringify(next, null, 2) + "\n", "utf-8");
159
173
  return next;
160
174
  });
161
175
  }
162
176
 
177
+ export function recordUnitHarnessAbort(
178
+ basePath: string,
179
+ unitType: string,
180
+ unitId: string,
181
+ startedAt: number,
182
+ abort: Omit<UnitHarnessAbortRecord, "recordedAt"> & { recordedAt?: number },
183
+ ): AutoUnitRuntimeRecord {
184
+ return writeUnitRuntimeRecord(basePath, unitType, unitId, startedAt, {
185
+ harnessAbort: {
186
+ ...abort,
187
+ recordedAt: abort.recordedAt ?? Date.now(),
188
+ },
189
+ lastProgressAt: Date.now(),
190
+ lastProgressKind: `harness-abort:${abort.kind}`,
191
+ });
192
+ }
193
+
163
194
  export function readUnitRuntimeRecord(basePath: string, unitType: string, unitId: string): AutoUnitRuntimeRecord | null {
164
195
  const path = runtimePath(basePath, unitType, unitId);
165
196
  if (!existsSync(path)) return null;
@@ -170,6 +201,17 @@ export function readUnitRuntimeRecord(basePath: string, unitType: string, unitId
170
201
  }
171
202
  }
172
203
 
204
+ export function readUnitHarnessAbort(
205
+ basePath: string,
206
+ unitType: string,
207
+ unitId: string,
208
+ startedAt: number,
209
+ ): UnitHarnessAbortRecord | null {
210
+ const record = readUnitRuntimeRecord(basePath, unitType, unitId);
211
+ if (!record || record.startedAt !== startedAt) return null;
212
+ return record.harnessAbort ?? null;
213
+ }
214
+
173
215
  export function clearUnitRuntimeRecord(basePath: string, unitType: string, unitId: string): void {
174
216
  const path = runtimePath(basePath, unitType, unitId);
175
217
  if (existsSync(path)) unlinkSync(path);
@@ -1,7 +1,17 @@
1
1
  // gsd-pi + Workspace handle: single source of truth for path resolution per milestone
2
2
 
3
- import { join, resolve } from "node:path";
4
- import { type GsdPathContract, resolveGsdPathContract, normalizeRealPath } from "./paths.js";
3
+ import { dirname, join, resolve } from "node:path";
4
+ import {
5
+ buildMilestoneFileName,
6
+ canonicalPhaseDirName,
7
+ isLegacyMilestonesLayout,
8
+ milestonesDir,
9
+ type GsdPathContract,
10
+ resolveGsdPathContract,
11
+ resolveMilestoneFile,
12
+ resolveMilestonePath,
13
+ normalizeRealPath,
14
+ } from "./paths.js";
5
15
  import { isGsdWorktreePath, resolveWorktreeProjectRoot } from "./worktree-root.js";
6
16
 
7
17
  export type GsdWorkspaceMode = "project" | "worktree";
@@ -31,6 +41,29 @@ function tryRealpath(p: string): string {
31
41
  return normalizeRealPath(p);
32
42
  }
33
43
 
44
+ function isLegacyMilestoneDirectory(dir: string): boolean {
45
+ const parent = dirname(dir);
46
+ return parent.endsWith("/milestones") || parent.endsWith("\\milestones");
47
+ }
48
+
49
+ function scopeMilestoneDir(basePath: string, milestoneId: string): string {
50
+ return resolveMilestonePath(basePath, milestoneId) ?? join(
51
+ milestonesDir(basePath),
52
+ isLegacyMilestonesLayout(basePath) ? milestoneId : canonicalPhaseDirName(milestoneId),
53
+ );
54
+ }
55
+
56
+ function scopeMilestoneFile(basePath: string, milestoneId: string, suffix: string): string {
57
+ const existing = resolveMilestoneFile(basePath, milestoneId, suffix);
58
+ if (existing) return existing;
59
+
60
+ const dir = scopeMilestoneDir(basePath, milestoneId);
61
+ const filename = isLegacyMilestoneDirectory(dir)
62
+ ? `${milestoneId}-${suffix}.md`
63
+ : buildMilestoneFileName(milestoneId, suffix);
64
+ return join(dir, filename);
65
+ }
66
+
34
67
  /**
35
68
  * Create an immutable GsdWorkspace handle from a raw base path.
36
69
  * Resolves both the project root and (when applicable) the worktree root,
@@ -73,22 +106,23 @@ export function createWorkspace(rawBasePath: string): GsdWorkspace {
73
106
  * Bind a milestoneId to a workspace, producing an immutable MilestoneScope
74
107
  * with path-returning closures for DB-authoritative project state.
75
108
  *
76
- * These scope paths intentionally route to contract.projectGsd. In-flight
77
- * markdown artifact readers outside this scope use the projection-aware
78
- * resolvers in paths.ts so worktree units can see worktree-local projections.
109
+ * DB paths route to contract.projectGsd. Milestone artifact paths resolve from
110
+ * the pinned project root with the layout-aware resolvers, so flat-phase and
111
+ * legacy projects use the same scope surface.
79
112
  */
80
113
  export function scopeMilestone(workspace: GsdWorkspace, milestoneId: string): MilestoneScope {
81
114
  const { contract } = workspace;
82
115
  const gsd = contract.projectGsd;
116
+ const basePath = workspace.projectRoot;
83
117
 
84
118
  const scope: MilestoneScope = Object.freeze({
85
119
  workspace,
86
120
  milestoneId,
87
- contextFile: () => join(gsd, "milestones", milestoneId, `${milestoneId}-CONTEXT.md`),
88
- roadmapFile: () => join(gsd, "milestones", milestoneId, `${milestoneId}-ROADMAP.md`),
121
+ contextFile: () => scopeMilestoneFile(basePath, milestoneId, "CONTEXT"),
122
+ roadmapFile: () => scopeMilestoneFile(basePath, milestoneId, "ROADMAP"),
89
123
  stateFile: () => join(gsd, "STATE.md"),
90
124
  dbPath: () => contract.projectDb,
91
- milestoneDir: () => join(gsd, "milestones", milestoneId),
125
+ milestoneDir: () => scopeMilestoneDir(basePath, milestoneId),
92
126
  metaJson: () => join(gsd, `${milestoneId}-META.json`),
93
127
  });
94
128