gsd-pi 2.70.1 → 2.71.0-dev.4c35d99

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 (533) hide show
  1. package/README.md +57 -17
  2. package/dist/cli.js +29 -3
  3. package/dist/headless-events.d.ts +2 -0
  4. package/dist/headless-events.js +7 -0
  5. package/dist/headless.js +16 -3
  6. package/dist/mcp-server.js +40 -17
  7. package/dist/provider-migrations.d.ts +10 -0
  8. package/dist/provider-migrations.js +12 -0
  9. package/dist/resource-loader.js +139 -13
  10. package/dist/resources/GSD-WORKFLOW.md +1 -1
  11. package/dist/resources/agents/debugger.md +58 -0
  12. package/dist/resources/agents/doc-writer.md +43 -0
  13. package/dist/resources/agents/git-ops.md +56 -0
  14. package/dist/resources/agents/javascript-pro.md +46 -271
  15. package/dist/resources/agents/planner.md +55 -0
  16. package/dist/resources/agents/refactorer.md +47 -0
  17. package/dist/resources/agents/reviewer.md +48 -0
  18. package/dist/resources/agents/security.md +59 -0
  19. package/dist/resources/agents/tester.md +50 -0
  20. package/dist/resources/agents/typescript-pro.md +41 -235
  21. package/dist/resources/extensions/claude-code-cli/stream-adapter.js +242 -40
  22. package/dist/resources/extensions/get-secrets-from-user.js +17 -1
  23. package/dist/resources/extensions/gsd/auto/infra-errors.js +34 -0
  24. package/dist/resources/extensions/gsd/auto/loop.js +32 -1
  25. package/dist/resources/extensions/gsd/auto/phases.js +5 -1
  26. package/dist/resources/extensions/gsd/auto/session.js +11 -0
  27. package/dist/resources/extensions/gsd/auto-dashboard.js +22 -16
  28. package/dist/resources/extensions/gsd/auto-model-selection.js +10 -2
  29. package/dist/resources/extensions/gsd/auto-prompts.js +88 -33
  30. package/dist/resources/extensions/gsd/auto-start.js +37 -18
  31. package/dist/resources/extensions/gsd/auto-tool-tracking.js +1 -1
  32. package/dist/resources/extensions/gsd/auto-worktree.js +1 -1
  33. package/dist/resources/extensions/gsd/auto.js +56 -0
  34. package/dist/resources/extensions/gsd/bootstrap/db-tools.js +3 -3
  35. package/dist/resources/extensions/gsd/bootstrap/register-hooks.js +6 -0
  36. package/dist/resources/extensions/gsd/bootstrap/register-shortcuts.js +63 -51
  37. package/dist/resources/extensions/gsd/bootstrap/system-context.js +6 -0
  38. package/dist/resources/extensions/gsd/commands/context.js +15 -6
  39. package/dist/resources/extensions/gsd/commands/dispatcher.js +12 -2
  40. package/dist/resources/extensions/gsd/commands/handlers/auto.js +10 -33
  41. package/dist/resources/extensions/gsd/commands/handlers/core.js +56 -11
  42. package/dist/resources/extensions/gsd/commands/handlers/notifications-handler.js +15 -6
  43. package/dist/resources/extensions/gsd/commands/handlers/workflow.js +4 -10
  44. package/dist/resources/extensions/gsd/custom-workflow-engine.js +16 -12
  45. package/dist/resources/extensions/gsd/dashboard-overlay.js +8 -3
  46. package/dist/resources/extensions/gsd/dispatch-guard.js +18 -1
  47. package/dist/resources/extensions/gsd/doctor-providers.js +23 -0
  48. package/dist/resources/extensions/gsd/error-classifier.js +1 -1
  49. package/dist/resources/extensions/gsd/file-lock.js +60 -0
  50. package/dist/resources/extensions/gsd/forensics.js +19 -6
  51. package/dist/resources/extensions/gsd/gate-registry.js +208 -0
  52. package/dist/resources/extensions/gsd/gsd-db.js +41 -0
  53. package/dist/resources/extensions/gsd/guided-flow.js +17 -20
  54. package/dist/resources/extensions/gsd/init-wizard.js +3 -11
  55. package/dist/resources/extensions/gsd/metrics.js +1 -0
  56. package/dist/resources/extensions/gsd/milestone-actions.js +10 -4
  57. package/dist/resources/extensions/gsd/milestone-validation-gates.js +11 -12
  58. package/dist/resources/extensions/gsd/notification-overlay.js +42 -13
  59. package/dist/resources/extensions/gsd/notification-store.js +56 -5
  60. package/dist/resources/extensions/gsd/notification-widget.js +5 -13
  61. package/dist/resources/extensions/gsd/parallel-monitor-overlay.js +8 -3
  62. package/dist/resources/extensions/gsd/pre-execution-checks.js +35 -2
  63. package/dist/resources/extensions/gsd/prompt-validation.js +126 -0
  64. package/dist/resources/extensions/gsd/prompts/complete-slice.md +5 -3
  65. package/dist/resources/extensions/gsd/prompts/discuss.md +33 -13
  66. package/dist/resources/extensions/gsd/prompts/execute-task.md +22 -19
  67. package/dist/resources/extensions/gsd/prompts/guided-discuss-milestone.md +2 -0
  68. package/dist/resources/extensions/gsd/prompts/guided-discuss-slice.md +2 -0
  69. package/dist/resources/extensions/gsd/prompts/guided-resume-task.md +1 -1
  70. package/dist/resources/extensions/gsd/prompts/queue.md +3 -2
  71. package/dist/resources/extensions/gsd/prompts/system.md +1 -0
  72. package/dist/resources/extensions/gsd/prompts/validate-milestone.md +4 -1
  73. package/dist/resources/extensions/gsd/session-model-override.js +25 -0
  74. package/dist/resources/extensions/gsd/shortcut-defs.js +40 -0
  75. package/dist/resources/extensions/gsd/state.js +241 -332
  76. package/dist/resources/extensions/gsd/tools/complete-slice.js +52 -1
  77. package/dist/resources/extensions/gsd/tools/complete-task.js +51 -1
  78. package/dist/resources/extensions/gsd/tools/workflow-tool-executors.js +38 -1
  79. package/dist/resources/extensions/gsd/workflow-events.js +25 -13
  80. package/dist/resources/extensions/gsd/workflow-mcp-auto-prep.js +56 -0
  81. package/dist/resources/extensions/gsd/workflow-mcp.js +1 -1
  82. package/dist/resources/extensions/ollama/index.js +13 -5
  83. package/dist/resources/extensions/shared/gsd-phase-state.js +35 -0
  84. package/dist/resources/extensions/subagent/agents.js +8 -0
  85. package/dist/resources/extensions/subagent/index.js +17 -0
  86. package/dist/resources/skills/create-skill/SKILL.md +2 -0
  87. package/dist/startup-model-validation.d.ts +0 -1
  88. package/dist/startup-model-validation.js +6 -2
  89. package/dist/web/standalone/.next/BUILD_ID +1 -1
  90. package/dist/web/standalone/.next/app-path-routes-manifest.json +17 -17
  91. package/dist/web/standalone/.next/build-manifest.json +4 -4
  92. package/dist/web/standalone/.next/prerender-manifest.json +3 -3
  93. package/dist/web/standalone/.next/react-loadable-manifest.json +1 -1
  94. package/dist/web/standalone/.next/required-server-files.json +3 -3
  95. package/dist/web/standalone/.next/server/app/_global-error/page.js +3 -3
  96. package/dist/web/standalone/.next/server/app/_global-error/page_client-reference-manifest.js +1 -1
  97. package/dist/web/standalone/.next/server/app/_global-error.html +1 -1
  98. package/dist/web/standalone/.next/server/app/_global-error.rsc +1 -1
  99. package/dist/web/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  100. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
  101. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
  102. package/dist/web/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  103. package/dist/web/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  104. package/dist/web/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  105. package/dist/web/standalone/.next/server/app/_not-found/page.js +2 -2
  106. package/dist/web/standalone/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  107. package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
  108. package/dist/web/standalone/.next/server/app/_not-found.rsc +3 -3
  109. package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +3 -3
  110. package/dist/web/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  111. package/dist/web/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +3 -3
  112. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  113. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  114. package/dist/web/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
  115. package/dist/web/standalone/.next/server/app/api/boot/route.js +1 -1
  116. package/dist/web/standalone/.next/server/app/api/boot/route_client-reference-manifest.js +1 -1
  117. package/dist/web/standalone/.next/server/app/api/bridge-terminal/input/route.js +1 -1
  118. package/dist/web/standalone/.next/server/app/api/bridge-terminal/input/route_client-reference-manifest.js +1 -1
  119. package/dist/web/standalone/.next/server/app/api/bridge-terminal/resize/route.js +1 -1
  120. package/dist/web/standalone/.next/server/app/api/bridge-terminal/resize/route_client-reference-manifest.js +1 -1
  121. package/dist/web/standalone/.next/server/app/api/bridge-terminal/stream/route.js +2 -2
  122. package/dist/web/standalone/.next/server/app/api/bridge-terminal/stream/route_client-reference-manifest.js +1 -1
  123. package/dist/web/standalone/.next/server/app/api/browse-directories/route.js +1 -1
  124. package/dist/web/standalone/.next/server/app/api/browse-directories/route_client-reference-manifest.js +1 -1
  125. package/dist/web/standalone/.next/server/app/api/captures/route.js +1 -1
  126. package/dist/web/standalone/.next/server/app/api/captures/route_client-reference-manifest.js +1 -1
  127. package/dist/web/standalone/.next/server/app/api/cleanup/route.js +1 -1
  128. package/dist/web/standalone/.next/server/app/api/cleanup/route_client-reference-manifest.js +1 -1
  129. package/dist/web/standalone/.next/server/app/api/dev-mode/route.js +1 -1
  130. package/dist/web/standalone/.next/server/app/api/dev-mode/route_client-reference-manifest.js +1 -1
  131. package/dist/web/standalone/.next/server/app/api/doctor/route.js +1 -1
  132. package/dist/web/standalone/.next/server/app/api/doctor/route_client-reference-manifest.js +1 -1
  133. package/dist/web/standalone/.next/server/app/api/experimental/route.js +2 -2
  134. package/dist/web/standalone/.next/server/app/api/experimental/route_client-reference-manifest.js +1 -1
  135. package/dist/web/standalone/.next/server/app/api/export-data/route.js +1 -1
  136. package/dist/web/standalone/.next/server/app/api/export-data/route_client-reference-manifest.js +1 -1
  137. package/dist/web/standalone/.next/server/app/api/files/route.js +1 -1
  138. package/dist/web/standalone/.next/server/app/api/files/route_client-reference-manifest.js +1 -1
  139. package/dist/web/standalone/.next/server/app/api/forensics/route.js +1 -1
  140. package/dist/web/standalone/.next/server/app/api/forensics/route_client-reference-manifest.js +1 -1
  141. package/dist/web/standalone/.next/server/app/api/git/route.js +1 -1
  142. package/dist/web/standalone/.next/server/app/api/git/route_client-reference-manifest.js +1 -1
  143. package/dist/web/standalone/.next/server/app/api/history/route.js +1 -1
  144. package/dist/web/standalone/.next/server/app/api/history/route_client-reference-manifest.js +1 -1
  145. package/dist/web/standalone/.next/server/app/api/hooks/route.js +1 -1
  146. package/dist/web/standalone/.next/server/app/api/hooks/route_client-reference-manifest.js +1 -1
  147. package/dist/web/standalone/.next/server/app/api/inspect/route.js +1 -1
  148. package/dist/web/standalone/.next/server/app/api/inspect/route_client-reference-manifest.js +1 -1
  149. package/dist/web/standalone/.next/server/app/api/knowledge/route.js +1 -1
  150. package/dist/web/standalone/.next/server/app/api/knowledge/route_client-reference-manifest.js +1 -1
  151. package/dist/web/standalone/.next/server/app/api/live-state/route.js +1 -1
  152. package/dist/web/standalone/.next/server/app/api/live-state/route_client-reference-manifest.js +1 -1
  153. package/dist/web/standalone/.next/server/app/api/notifications/route.js +2 -2
  154. package/dist/web/standalone/.next/server/app/api/notifications/route_client-reference-manifest.js +1 -1
  155. package/dist/web/standalone/.next/server/app/api/onboarding/route.js +1 -1
  156. package/dist/web/standalone/.next/server/app/api/onboarding/route_client-reference-manifest.js +1 -1
  157. package/dist/web/standalone/.next/server/app/api/preferences/route.js +1 -1
  158. package/dist/web/standalone/.next/server/app/api/preferences/route_client-reference-manifest.js +1 -1
  159. package/dist/web/standalone/.next/server/app/api/projects/route.js +1 -1
  160. package/dist/web/standalone/.next/server/app/api/projects/route_client-reference-manifest.js +1 -1
  161. package/dist/web/standalone/.next/server/app/api/recovery/route.js +1 -1
  162. package/dist/web/standalone/.next/server/app/api/recovery/route_client-reference-manifest.js +1 -1
  163. package/dist/web/standalone/.next/server/app/api/remote-questions/route.js +2 -2
  164. package/dist/web/standalone/.next/server/app/api/remote-questions/route_client-reference-manifest.js +1 -1
  165. package/dist/web/standalone/.next/server/app/api/session/browser/route.js +1 -1
  166. package/dist/web/standalone/.next/server/app/api/session/browser/route_client-reference-manifest.js +1 -1
  167. package/dist/web/standalone/.next/server/app/api/session/command/route.js +1 -1
  168. package/dist/web/standalone/.next/server/app/api/session/command/route_client-reference-manifest.js +1 -1
  169. package/dist/web/standalone/.next/server/app/api/session/events/route.js +2 -2
  170. package/dist/web/standalone/.next/server/app/api/session/events/route_client-reference-manifest.js +1 -1
  171. package/dist/web/standalone/.next/server/app/api/session/manage/route.js +1 -1
  172. package/dist/web/standalone/.next/server/app/api/session/manage/route_client-reference-manifest.js +1 -1
  173. package/dist/web/standalone/.next/server/app/api/settings-data/route.js +1 -1
  174. package/dist/web/standalone/.next/server/app/api/settings-data/route_client-reference-manifest.js +1 -1
  175. package/dist/web/standalone/.next/server/app/api/shutdown/route.js +1 -1
  176. package/dist/web/standalone/.next/server/app/api/shutdown/route_client-reference-manifest.js +1 -1
  177. package/dist/web/standalone/.next/server/app/api/skill-health/route.js +1 -1
  178. package/dist/web/standalone/.next/server/app/api/skill-health/route_client-reference-manifest.js +1 -1
  179. package/dist/web/standalone/.next/server/app/api/steer/route.js +1 -1
  180. package/dist/web/standalone/.next/server/app/api/steer/route_client-reference-manifest.js +1 -1
  181. package/dist/web/standalone/.next/server/app/api/switch-root/route.js +1 -1
  182. package/dist/web/standalone/.next/server/app/api/switch-root/route_client-reference-manifest.js +1 -1
  183. package/dist/web/standalone/.next/server/app/api/terminal/input/route.js +1 -1
  184. package/dist/web/standalone/.next/server/app/api/terminal/input/route_client-reference-manifest.js +1 -1
  185. package/dist/web/standalone/.next/server/app/api/terminal/resize/route.js +2 -2
  186. package/dist/web/standalone/.next/server/app/api/terminal/resize/route_client-reference-manifest.js +1 -1
  187. package/dist/web/standalone/.next/server/app/api/terminal/sessions/route.js +1 -1
  188. package/dist/web/standalone/.next/server/app/api/terminal/sessions/route_client-reference-manifest.js +1 -1
  189. package/dist/web/standalone/.next/server/app/api/terminal/stream/route.js +2 -2
  190. package/dist/web/standalone/.next/server/app/api/terminal/stream/route_client-reference-manifest.js +1 -1
  191. package/dist/web/standalone/.next/server/app/api/terminal/upload/route.js +1 -1
  192. package/dist/web/standalone/.next/server/app/api/terminal/upload/route_client-reference-manifest.js +1 -1
  193. package/dist/web/standalone/.next/server/app/api/undo/route.js +1 -1
  194. package/dist/web/standalone/.next/server/app/api/undo/route_client-reference-manifest.js +1 -1
  195. package/dist/web/standalone/.next/server/app/api/update/route.js +1 -1
  196. package/dist/web/standalone/.next/server/app/api/update/route_client-reference-manifest.js +1 -1
  197. package/dist/web/standalone/.next/server/app/api/visualizer/route.js +1 -1
  198. package/dist/web/standalone/.next/server/app/api/visualizer/route_client-reference-manifest.js +1 -1
  199. package/dist/web/standalone/.next/server/app/index.html +1 -1
  200. package/dist/web/standalone/.next/server/app/index.rsc +4 -4
  201. package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +2 -2
  202. package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +4 -4
  203. package/dist/web/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
  204. package/dist/web/standalone/.next/server/app/index.segments/_index.segment.rsc +3 -3
  205. package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
  206. package/dist/web/standalone/.next/server/app/page.js +2 -2
  207. package/dist/web/standalone/.next/server/app/page_client-reference-manifest.js +1 -1
  208. package/dist/web/standalone/.next/server/app-paths-manifest.json +17 -17
  209. package/dist/web/standalone/.next/server/chunks/63.js +3 -3
  210. package/dist/web/standalone/.next/server/chunks/6897.js +1 -1
  211. package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
  212. package/dist/web/standalone/.next/server/middleware-react-loadable-manifest.js +1 -1
  213. package/dist/web/standalone/.next/server/middleware.js +2 -2
  214. package/dist/web/standalone/.next/server/next-font-manifest.js +1 -1
  215. package/dist/web/standalone/.next/server/next-font-manifest.json +1 -1
  216. package/dist/web/standalone/.next/server/pages/404.html +1 -1
  217. package/dist/web/standalone/.next/server/pages/500.html +1 -1
  218. package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
  219. package/dist/web/standalone/.next/static/chunks/2826.dd3dc8bbd3025fa5.js +9 -0
  220. package/dist/web/standalone/.next/static/chunks/app/_not-found/{page-2f24283c162b6ab3.js → page-f2a7482d42a5614b.js} +1 -1
  221. package/dist/web/standalone/.next/static/chunks/app/{layout-9ecfd95f343793f0.js → layout-a16c7a7ecdf0c2cf.js} +1 -1
  222. package/dist/web/standalone/.next/static/chunks/app/page-f1e30ab6bb269149.js +1 -0
  223. package/dist/web/standalone/.next/static/chunks/main-app-fdab67f7802d7832.js +1 -0
  224. package/dist/web/standalone/.next/static/chunks/next/dist/client/components/builtin/global-error-459824ffb8c323dd.js +1 -0
  225. package/dist/web/standalone/.next/static/chunks/{webpack-6e4d7e9a4f57bed4.js → webpack-b868033a5834586d.js} +1 -1
  226. package/dist/web/standalone/node_modules/node-pty/build/Makefile +2 -2
  227. package/dist/web/standalone/node_modules/node-pty/build/Release/pty.node +0 -0
  228. package/dist/web/standalone/node_modules/node-pty/build/pty.target.mk +14 -14
  229. package/dist/web/standalone/node_modules/node-pty/node-addon-api/node_addon_api.target.mk +14 -14
  230. package/dist/web/standalone/node_modules/node-pty/node-addon-api/node_addon_api_except.target.mk +14 -14
  231. package/dist/web/standalone/node_modules/node-pty/node-addon-api/node_addon_api_maybe.target.mk +14 -14
  232. package/dist/web/standalone/server.js +1 -1
  233. package/package.json +1 -1
  234. package/packages/mcp-server/dist/env-writer.d.ts +39 -0
  235. package/packages/mcp-server/dist/env-writer.d.ts.map +1 -0
  236. package/packages/mcp-server/dist/env-writer.js +158 -0
  237. package/packages/mcp-server/dist/env-writer.js.map +1 -0
  238. package/packages/mcp-server/dist/server.d.ts +23 -3
  239. package/packages/mcp-server/dist/server.d.ts.map +1 -1
  240. package/packages/mcp-server/dist/server.js +192 -44
  241. package/packages/mcp-server/dist/server.js.map +1 -1
  242. package/packages/mcp-server/dist/workflow-tools.d.ts.map +1 -1
  243. package/packages/mcp-server/dist/workflow-tools.js +22 -12
  244. package/packages/mcp-server/dist/workflow-tools.js.map +1 -1
  245. package/packages/mcp-server/src/env-writer.test.ts +280 -0
  246. package/packages/mcp-server/src/env-writer.ts +183 -0
  247. package/packages/mcp-server/src/secure-env-collect.test.ts +265 -0
  248. package/packages/mcp-server/src/server.ts +247 -41
  249. package/packages/mcp-server/src/workflow-tools.test.ts +110 -0
  250. package/packages/mcp-server/src/workflow-tools.ts +32 -12
  251. package/packages/pi-ai/dist/providers/amazon-bedrock.js +11 -2
  252. package/packages/pi-ai/dist/providers/amazon-bedrock.js.map +1 -1
  253. package/packages/pi-ai/dist/providers/anthropic-auth.test.d.ts +2 -0
  254. package/packages/pi-ai/dist/providers/anthropic-auth.test.d.ts.map +1 -0
  255. package/packages/pi-ai/dist/providers/anthropic-auth.test.js +20 -0
  256. package/packages/pi-ai/dist/providers/anthropic-auth.test.js.map +1 -0
  257. package/packages/pi-ai/dist/providers/anthropic-shared.d.ts +4 -1
  258. package/packages/pi-ai/dist/providers/anthropic-shared.d.ts.map +1 -1
  259. package/packages/pi-ai/dist/providers/anthropic-shared.js +8 -3
  260. package/packages/pi-ai/dist/providers/anthropic-shared.js.map +1 -1
  261. package/packages/pi-ai/dist/providers/anthropic-shared.test.js +44 -1
  262. package/packages/pi-ai/dist/providers/anthropic-shared.test.js.map +1 -1
  263. package/packages/pi-ai/dist/providers/anthropic.d.ts +2 -1
  264. package/packages/pi-ai/dist/providers/anthropic.d.ts.map +1 -1
  265. package/packages/pi-ai/dist/providers/anthropic.js +7 -4
  266. package/packages/pi-ai/dist/providers/anthropic.js.map +1 -1
  267. package/packages/pi-ai/dist/providers/openai-completions.d.ts.map +1 -1
  268. package/packages/pi-ai/dist/providers/openai-completions.js +11 -0
  269. package/packages/pi-ai/dist/providers/openai-completions.js.map +1 -1
  270. package/packages/pi-ai/src/providers/amazon-bedrock.ts +13 -1
  271. package/packages/pi-ai/src/providers/anthropic-auth.test.ts +32 -0
  272. package/packages/pi-ai/src/providers/anthropic-shared.test.ts +55 -1
  273. package/packages/pi-ai/src/providers/anthropic-shared.ts +14 -3
  274. package/packages/pi-ai/src/providers/anthropic.ts +8 -4
  275. package/packages/pi-ai/src/providers/openai-completions.ts +14 -0
  276. package/packages/pi-coding-agent/dist/core/agent-session-renderable-tools.test.d.ts +2 -0
  277. package/packages/pi-coding-agent/dist/core/agent-session-renderable-tools.test.d.ts.map +1 -0
  278. package/packages/pi-coding-agent/dist/core/agent-session-renderable-tools.test.js +61 -0
  279. package/packages/pi-coding-agent/dist/core/agent-session-renderable-tools.test.js.map +1 -0
  280. package/packages/pi-coding-agent/dist/core/agent-session.d.ts.map +1 -1
  281. package/packages/pi-coding-agent/dist/core/agent-session.js +2 -1
  282. package/packages/pi-coding-agent/dist/core/agent-session.js.map +1 -1
  283. package/packages/pi-coding-agent/dist/core/auth-storage.d.ts +10 -0
  284. package/packages/pi-coding-agent/dist/core/auth-storage.d.ts.map +1 -1
  285. package/packages/pi-coding-agent/dist/core/auth-storage.js +27 -0
  286. package/packages/pi-coding-agent/dist/core/auth-storage.js.map +1 -1
  287. package/packages/pi-coding-agent/dist/core/auth-storage.test.js +85 -0
  288. package/packages/pi-coding-agent/dist/core/auth-storage.test.js.map +1 -1
  289. package/packages/pi-coding-agent/dist/core/chat-controller-ordering.test.d.ts +2 -0
  290. package/packages/pi-coding-agent/dist/core/chat-controller-ordering.test.d.ts.map +1 -0
  291. package/packages/pi-coding-agent/dist/core/chat-controller-ordering.test.js +388 -0
  292. package/packages/pi-coding-agent/dist/core/chat-controller-ordering.test.js.map +1 -0
  293. package/packages/pi-coding-agent/dist/core/extensions/types.d.ts +2 -0
  294. package/packages/pi-coding-agent/dist/core/extensions/types.d.ts.map +1 -1
  295. package/packages/pi-coding-agent/dist/core/extensions/types.js.map +1 -1
  296. package/packages/pi-coding-agent/dist/core/model-resolver-initial-model-auth.test.d.ts +2 -0
  297. package/packages/pi-coding-agent/dist/core/model-resolver-initial-model-auth.test.d.ts.map +1 -0
  298. package/packages/pi-coding-agent/dist/core/model-resolver-initial-model-auth.test.js +64 -0
  299. package/packages/pi-coding-agent/dist/core/model-resolver-initial-model-auth.test.js.map +1 -0
  300. package/packages/pi-coding-agent/dist/core/model-resolver.d.ts.map +1 -1
  301. package/packages/pi-coding-agent/dist/core/model-resolver.js +22 -18
  302. package/packages/pi-coding-agent/dist/core/model-resolver.js.map +1 -1
  303. package/packages/pi-coding-agent/dist/core/model-resolver.test.d.ts +8 -0
  304. package/packages/pi-coding-agent/dist/core/model-resolver.test.d.ts.map +1 -0
  305. package/packages/pi-coding-agent/dist/core/model-resolver.test.js +75 -0
  306. package/packages/pi-coding-agent/dist/core/model-resolver.test.js.map +1 -0
  307. package/packages/pi-coding-agent/dist/core/sdk.d.ts +11 -0
  308. package/packages/pi-coding-agent/dist/core/sdk.d.ts.map +1 -1
  309. package/packages/pi-coding-agent/dist/core/sdk.js +38 -5
  310. package/packages/pi-coding-agent/dist/core/sdk.js.map +1 -1
  311. package/packages/pi-coding-agent/dist/core/sdk.test.d.ts +2 -0
  312. package/packages/pi-coding-agent/dist/core/sdk.test.d.ts.map +1 -0
  313. package/packages/pi-coding-agent/dist/core/sdk.test.js +71 -0
  314. package/packages/pi-coding-agent/dist/core/sdk.test.js.map +1 -0
  315. package/packages/pi-coding-agent/dist/index.d.ts +1 -1
  316. package/packages/pi-coding-agent/dist/index.d.ts.map +1 -1
  317. package/packages/pi-coding-agent/dist/index.js +1 -1
  318. package/packages/pi-coding-agent/dist/index.js.map +1 -1
  319. package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/login-dialog.test.d.ts +2 -0
  320. package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/login-dialog.test.d.ts.map +1 -0
  321. package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/login-dialog.test.js +13 -0
  322. package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/login-dialog.test.js.map +1 -0
  323. package/packages/pi-coding-agent/dist/modes/interactive/components/dynamic-border.d.ts +19 -2
  324. package/packages/pi-coding-agent/dist/modes/interactive/components/dynamic-border.d.ts.map +1 -1
  325. package/packages/pi-coding-agent/dist/modes/interactive/components/dynamic-border.js +50 -1
  326. package/packages/pi-coding-agent/dist/modes/interactive/components/dynamic-border.js.map +1 -1
  327. package/packages/pi-coding-agent/dist/modes/interactive/components/extension-input.d.ts +1 -0
  328. package/packages/pi-coding-agent/dist/modes/interactive/components/extension-input.d.ts.map +1 -1
  329. package/packages/pi-coding-agent/dist/modes/interactive/components/extension-input.js +1 -0
  330. package/packages/pi-coding-agent/dist/modes/interactive/components/extension-input.js.map +1 -1
  331. package/packages/pi-coding-agent/dist/modes/interactive/components/login-dialog.d.ts +4 -0
  332. package/packages/pi-coding-agent/dist/modes/interactive/components/login-dialog.d.ts.map +1 -1
  333. package/packages/pi-coding-agent/dist/modes/interactive/components/login-dialog.js +24 -2
  334. package/packages/pi-coding-agent/dist/modes/interactive/components/login-dialog.js.map +1 -1
  335. package/packages/pi-coding-agent/dist/modes/interactive/components/model-selector.d.ts.map +1 -1
  336. package/packages/pi-coding-agent/dist/modes/interactive/components/model-selector.js +9 -2
  337. package/packages/pi-coding-agent/dist/modes/interactive/components/model-selector.js.map +1 -1
  338. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.d.ts +4 -0
  339. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
  340. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.js +43 -0
  341. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.js.map +1 -1
  342. package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.d.ts.map +1 -1
  343. package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.js +175 -25
  344. package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.js.map +1 -1
  345. package/packages/pi-coding-agent/dist/modes/interactive/controllers/model-controller.d.ts.map +1 -1
  346. package/packages/pi-coding-agent/dist/modes/interactive/controllers/model-controller.js +6 -1
  347. package/packages/pi-coding-agent/dist/modes/interactive/controllers/model-controller.js.map +1 -1
  348. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode-state.d.ts +1 -0
  349. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode-state.d.ts.map +1 -1
  350. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode-state.js.map +1 -1
  351. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.d.ts +6 -0
  352. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  353. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js +62 -5
  354. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js.map +1 -1
  355. package/packages/pi-coding-agent/dist/modes/interactive/slash-command-handlers.js +4 -2
  356. package/packages/pi-coding-agent/dist/modes/interactive/slash-command-handlers.js.map +1 -1
  357. package/packages/pi-coding-agent/dist/modes/rpc/rpc-mode.js +1 -1
  358. package/packages/pi-coding-agent/dist/modes/rpc/rpc-mode.js.map +1 -1
  359. package/packages/pi-coding-agent/dist/modes/rpc/rpc-types.d.ts +1 -0
  360. package/packages/pi-coding-agent/dist/modes/rpc/rpc-types.d.ts.map +1 -1
  361. package/packages/pi-coding-agent/dist/modes/rpc/rpc-types.js.map +1 -1
  362. package/packages/pi-coding-agent/package.json +1 -1
  363. package/packages/pi-coding-agent/src/core/agent-session-renderable-tools.test.ts +70 -0
  364. package/packages/pi-coding-agent/src/core/agent-session.ts +2 -1
  365. package/packages/pi-coding-agent/src/core/auth-storage.test.ts +108 -0
  366. package/packages/pi-coding-agent/src/core/auth-storage.ts +30 -0
  367. package/packages/pi-coding-agent/src/core/chat-controller-ordering.test.ts +468 -0
  368. package/packages/pi-coding-agent/src/core/extensions/types.ts +2 -0
  369. package/packages/pi-coding-agent/src/core/model-resolver-initial-model-auth.test.ts +78 -0
  370. package/packages/pi-coding-agent/src/core/model-resolver.test.ts +85 -0
  371. package/packages/pi-coding-agent/src/core/model-resolver.ts +22 -18
  372. package/packages/pi-coding-agent/src/core/sdk.test.ts +89 -0
  373. package/packages/pi-coding-agent/src/core/sdk.ts +45 -9
  374. package/packages/pi-coding-agent/src/index.ts +1 -0
  375. package/packages/pi-coding-agent/src/modes/interactive/components/__tests__/login-dialog.test.ts +24 -0
  376. package/packages/pi-coding-agent/src/modes/interactive/components/dynamic-border.ts +58 -2
  377. package/packages/pi-coding-agent/src/modes/interactive/components/extension-input.ts +2 -0
  378. package/packages/pi-coding-agent/src/modes/interactive/components/login-dialog.ts +30 -2
  379. package/packages/pi-coding-agent/src/modes/interactive/components/model-selector.ts +15 -6
  380. package/packages/pi-coding-agent/src/modes/interactive/components/tool-execution.ts +47 -0
  381. package/packages/pi-coding-agent/src/modes/interactive/controllers/chat-controller.ts +205 -31
  382. package/packages/pi-coding-agent/src/modes/interactive/controllers/model-controller.ts +6 -1
  383. package/packages/pi-coding-agent/src/modes/interactive/interactive-mode-state.ts +1 -0
  384. package/packages/pi-coding-agent/src/modes/interactive/interactive-mode.ts +70 -5
  385. package/packages/pi-coding-agent/src/modes/interactive/slash-command-handlers.ts +4 -2
  386. package/packages/pi-coding-agent/src/modes/rpc/rpc-mode.ts +1 -1
  387. package/packages/pi-coding-agent/src/modes/rpc/rpc-types.ts +1 -0
  388. package/packages/pi-tui/dist/components/__tests__/input.test.js +9 -0
  389. package/packages/pi-tui/dist/components/__tests__/input.test.js.map +1 -1
  390. package/packages/pi-tui/dist/components/__tests__/markdown-maxlines.test.d.ts +2 -0
  391. package/packages/pi-tui/dist/components/__tests__/markdown-maxlines.test.d.ts.map +1 -0
  392. package/packages/pi-tui/dist/components/__tests__/markdown-maxlines.test.js +66 -0
  393. package/packages/pi-tui/dist/components/__tests__/markdown-maxlines.test.js.map +1 -0
  394. package/packages/pi-tui/dist/components/input.d.ts +2 -0
  395. package/packages/pi-tui/dist/components/input.d.ts.map +1 -1
  396. package/packages/pi-tui/dist/components/input.js +7 -4
  397. package/packages/pi-tui/dist/components/input.js.map +1 -1
  398. package/packages/pi-tui/dist/components/markdown.d.ts +3 -0
  399. package/packages/pi-tui/dist/components/markdown.d.ts.map +1 -1
  400. package/packages/pi-tui/dist/components/markdown.js +17 -1
  401. package/packages/pi-tui/dist/components/markdown.js.map +1 -1
  402. package/packages/pi-tui/src/components/__tests__/input.test.ts +11 -0
  403. package/packages/pi-tui/src/components/__tests__/markdown-maxlines.test.ts +75 -0
  404. package/packages/pi-tui/src/components/input.ts +7 -4
  405. package/packages/pi-tui/src/components/markdown.ts +22 -1
  406. package/pkg/package.json +1 -1
  407. package/src/resources/GSD-WORKFLOW.md +1 -1
  408. package/src/resources/agents/debugger.md +58 -0
  409. package/src/resources/agents/doc-writer.md +43 -0
  410. package/src/resources/agents/git-ops.md +56 -0
  411. package/src/resources/agents/javascript-pro.md +46 -271
  412. package/src/resources/agents/planner.md +55 -0
  413. package/src/resources/agents/refactorer.md +47 -0
  414. package/src/resources/agents/reviewer.md +48 -0
  415. package/src/resources/agents/security.md +59 -0
  416. package/src/resources/agents/tester.md +50 -0
  417. package/src/resources/agents/typescript-pro.md +41 -235
  418. package/src/resources/extensions/claude-code-cli/stream-adapter.ts +288 -39
  419. package/src/resources/extensions/claude-code-cli/tests/stream-adapter.test.ts +330 -2
  420. package/src/resources/extensions/get-secrets-from-user.ts +24 -1
  421. package/src/resources/extensions/gsd/auto/infra-errors.ts +38 -0
  422. package/src/resources/extensions/gsd/auto/loop-deps.ts +2 -0
  423. package/src/resources/extensions/gsd/auto/loop.ts +45 -1
  424. package/src/resources/extensions/gsd/auto/phases.ts +6 -0
  425. package/src/resources/extensions/gsd/auto/session.ts +11 -0
  426. package/src/resources/extensions/gsd/auto-dashboard.ts +29 -18
  427. package/src/resources/extensions/gsd/auto-model-selection.ts +9 -1
  428. package/src/resources/extensions/gsd/auto-prompts.ts +111 -33
  429. package/src/resources/extensions/gsd/auto-start.ts +44 -20
  430. package/src/resources/extensions/gsd/auto-tool-tracking.ts +1 -1
  431. package/src/resources/extensions/gsd/auto-worktree.ts +1 -1
  432. package/src/resources/extensions/gsd/auto.ts +72 -0
  433. package/src/resources/extensions/gsd/bootstrap/db-tools.ts +3 -3
  434. package/src/resources/extensions/gsd/bootstrap/register-hooks.ts +6 -0
  435. package/src/resources/extensions/gsd/bootstrap/register-shortcuts.ts +79 -60
  436. package/src/resources/extensions/gsd/bootstrap/system-context.ts +7 -0
  437. package/src/resources/extensions/gsd/commands/context.ts +16 -5
  438. package/src/resources/extensions/gsd/commands/dispatcher.ts +14 -2
  439. package/src/resources/extensions/gsd/commands/handlers/auto.ts +10 -36
  440. package/src/resources/extensions/gsd/commands/handlers/core.ts +58 -11
  441. package/src/resources/extensions/gsd/commands/handlers/notifications-handler.ts +17 -7
  442. package/src/resources/extensions/gsd/commands/handlers/workflow.ts +4 -10
  443. package/src/resources/extensions/gsd/custom-workflow-engine.ts +19 -14
  444. package/src/resources/extensions/gsd/dashboard-overlay.ts +10 -3
  445. package/src/resources/extensions/gsd/dispatch-guard.ts +18 -1
  446. package/src/resources/extensions/gsd/doctor-providers.ts +24 -0
  447. package/src/resources/extensions/gsd/error-classifier.ts +1 -1
  448. package/src/resources/extensions/gsd/file-lock.ts +59 -0
  449. package/src/resources/extensions/gsd/forensics.ts +23 -7
  450. package/src/resources/extensions/gsd/gate-registry.ts +251 -0
  451. package/src/resources/extensions/gsd/gsd-db.ts +51 -0
  452. package/src/resources/extensions/gsd/guided-flow.ts +17 -19
  453. package/src/resources/extensions/gsd/init-wizard.ts +3 -13
  454. package/src/resources/extensions/gsd/interrupted-session.ts +1 -0
  455. package/src/resources/extensions/gsd/metrics.ts +12 -1
  456. package/src/resources/extensions/gsd/milestone-actions.ts +10 -3
  457. package/src/resources/extensions/gsd/milestone-validation-gates.ts +11 -13
  458. package/src/resources/extensions/gsd/notification-overlay.ts +47 -14
  459. package/src/resources/extensions/gsd/notification-store.ts +54 -5
  460. package/src/resources/extensions/gsd/notification-widget.ts +5 -14
  461. package/src/resources/extensions/gsd/parallel-monitor-overlay.ts +10 -3
  462. package/src/resources/extensions/gsd/pre-execution-checks.ts +39 -2
  463. package/src/resources/extensions/gsd/prompt-validation.ts +157 -0
  464. package/src/resources/extensions/gsd/prompts/complete-slice.md +5 -3
  465. package/src/resources/extensions/gsd/prompts/discuss.md +33 -13
  466. package/src/resources/extensions/gsd/prompts/execute-task.md +22 -19
  467. package/src/resources/extensions/gsd/prompts/guided-discuss-milestone.md +2 -0
  468. package/src/resources/extensions/gsd/prompts/guided-discuss-slice.md +2 -0
  469. package/src/resources/extensions/gsd/prompts/guided-resume-task.md +1 -1
  470. package/src/resources/extensions/gsd/prompts/queue.md +3 -2
  471. package/src/resources/extensions/gsd/prompts/system.md +1 -0
  472. package/src/resources/extensions/gsd/prompts/validate-milestone.md +4 -1
  473. package/src/resources/extensions/gsd/session-model-override.ts +36 -0
  474. package/src/resources/extensions/gsd/shortcut-defs.ts +56 -0
  475. package/src/resources/extensions/gsd/state.ts +285 -344
  476. package/src/resources/extensions/gsd/tests/auto-start-model-capture.test.ts +25 -9
  477. package/src/resources/extensions/gsd/tests/auto-start-worktree-db-path.test.ts +28 -0
  478. package/src/resources/extensions/gsd/tests/bootstrap-derive-state-db-open.test.ts +39 -0
  479. package/src/resources/extensions/gsd/tests/complete-slice-gate-closure.test.ts +167 -0
  480. package/src/resources/extensions/gsd/tests/complete-slice-prompt-task-summary-layout.test.ts +18 -0
  481. package/src/resources/extensions/gsd/tests/derive-state-helpers.test.ts +436 -0
  482. package/src/resources/extensions/gsd/tests/discuss-incremental-persistence.test.ts +9 -0
  483. package/src/resources/extensions/gsd/tests/dispatch-guard.test.ts +27 -0
  484. package/src/resources/extensions/gsd/tests/doctor-providers.test.ts +36 -0
  485. package/src/resources/extensions/gsd/tests/execute-task-prompt-existing-artifact-guard.test.ts +33 -0
  486. package/src/resources/extensions/gsd/tests/file-lock.test.ts +103 -0
  487. package/src/resources/extensions/gsd/tests/forensics-stuck-loops.test.ts +62 -0
  488. package/src/resources/extensions/gsd/tests/format-shortcut.test.ts +31 -0
  489. package/src/resources/extensions/gsd/tests/gate-dispatch.test.ts +27 -0
  490. package/src/resources/extensions/gsd/tests/gate-registry.test.ts +140 -0
  491. package/src/resources/extensions/gsd/tests/gsd-no-project-error.test.ts +73 -0
  492. package/src/resources/extensions/gsd/tests/infra-errors-cooldown.test.ts +180 -0
  493. package/src/resources/extensions/gsd/tests/integration/auto-worktree-milestone-merge.test.ts +66 -1
  494. package/src/resources/extensions/gsd/tests/model-isolation.test.ts +36 -51
  495. package/src/resources/extensions/gsd/tests/notification-store.test.ts +35 -0
  496. package/src/resources/extensions/gsd/tests/notification-widget.test.ts +26 -0
  497. package/src/resources/extensions/gsd/tests/notifications-handler.test.ts +90 -0
  498. package/src/resources/extensions/gsd/tests/parallel-monitor-overlay.test.ts +1 -0
  499. package/src/resources/extensions/gsd/tests/park-db-sync.test.ts +18 -0
  500. package/src/resources/extensions/gsd/tests/pre-execution-checks.test.ts +49 -0
  501. package/src/resources/extensions/gsd/tests/prompt-contracts.test.ts +19 -0
  502. package/src/resources/extensions/gsd/tests/prompt-system-gate-coverage.test.ts +208 -0
  503. package/src/resources/extensions/gsd/tests/provider-errors.test.ts +7 -0
  504. package/src/resources/extensions/gsd/tests/register-shortcuts.test.ts +63 -5
  505. package/src/resources/extensions/gsd/tests/secure-env-collect.test.ts +45 -0
  506. package/src/resources/extensions/gsd/tests/session-model-override.test.ts +35 -0
  507. package/src/resources/extensions/gsd/tests/start-auto-detached.test.ts +90 -0
  508. package/src/resources/extensions/gsd/tests/tool-invocation-error-loop-break.test.ts +7 -0
  509. package/src/resources/extensions/gsd/tests/validate-milestone-prompt-verification-classes.test.ts +18 -0
  510. package/src/resources/extensions/gsd/tests/workflow-mcp-auto-prep.test.ts +76 -0
  511. package/src/resources/extensions/gsd/tests/workflow-mcp.test.ts +155 -1
  512. package/src/resources/extensions/gsd/tests/workflow-tool-executors.test.ts +22 -0
  513. package/src/resources/extensions/gsd/tools/complete-slice.ts +63 -0
  514. package/src/resources/extensions/gsd/tools/complete-task.ts +63 -0
  515. package/src/resources/extensions/gsd/tools/workflow-tool-executors.ts +64 -26
  516. package/src/resources/extensions/gsd/types.ts +26 -0
  517. package/src/resources/extensions/gsd/workflow-events.ts +34 -25
  518. package/src/resources/extensions/gsd/workflow-mcp-auto-prep.ts +76 -0
  519. package/src/resources/extensions/gsd/workflow-mcp.ts +1 -1
  520. package/src/resources/extensions/ollama/index.ts +13 -3
  521. package/src/resources/extensions/ollama/ollama-status-indicator.test.ts +28 -0
  522. package/src/resources/extensions/shared/gsd-phase-state.ts +42 -0
  523. package/src/resources/extensions/shared/tests/gsd-phase-state.test.ts +48 -0
  524. package/src/resources/extensions/subagent/agents.ts +10 -0
  525. package/src/resources/extensions/subagent/index.ts +18 -0
  526. package/src/resources/extensions/subagent/tests/agents-conflicts.test.ts +33 -0
  527. package/src/resources/skills/create-skill/SKILL.md +2 -0
  528. package/dist/web/standalone/.next/static/chunks/2826.821e01b07d92e948.js +0 -9
  529. package/dist/web/standalone/.next/static/chunks/app/page-7115e62689b5fd84.js +0 -1
  530. package/dist/web/standalone/.next/static/chunks/main-app-d3d4c336195465f9.js +0 -1
  531. package/dist/web/standalone/.next/static/chunks/next/dist/client/components/builtin/global-error-ab5a8926e07ec673.js +0 -1
  532. /package/dist/web/standalone/.next/static/{9pw9EXtXjdM7EFrCXUEPf → OI4n_CKC-lM8IQbvGJ_tK}/_buildManifest.js +0 -0
  533. /package/dist/web/standalone/.next/static/{9pw9EXtXjdM7EFrCXUEPf → OI4n_CKC-lM8IQbvGJ_tK}/_ssgManifest.js +0 -0
@@ -58,7 +58,7 @@ import {
58
58
  insertSlice,
59
59
  insertTask,
60
60
  updateTaskStatus,
61
- getPendingSliceGateCount,
61
+ getPendingGateCountForTurn,
62
62
  type MilestoneRow,
63
63
  type SliceRow,
64
64
  type TaskRow,
@@ -322,17 +322,8 @@ const isStatusDone = isClosedStatus;
322
322
  *
323
323
  * Must produce field-identical GSDState to _deriveStateImpl() for the same project.
324
324
  */
325
- export async function deriveStateFromDb(basePath: string): Promise<GSDState> {
326
- const requirements = parseRequirementCounts(await loadFile(resolveGsdRootFile(basePath, "REQUIREMENTS")));
327
-
325
+ function reconcileDiskToDb(basePath: string): MilestoneRow[] {
328
326
  let allMilestones = getAllMilestones();
329
-
330
- // Incremental disk→DB sync: milestone directories created outside the DB
331
- // write path (via /gsd queue, manual mkdir, or complete-milestone writing the
332
- // next CONTEXT.md) are never inserted by the initial migration guard in
333
- // auto-start.ts because that guard only runs when gsd.db doesn't exist yet.
334
- // Reconcile here so deriveStateFromDb never silently misses queued milestones.
335
- // insertMilestone uses INSERT OR IGNORE, so this is safe to call every time.
336
327
  const dbIdSet = new Set(allMilestones.map(m => m.id));
337
328
  const diskIds = findMilestoneIds(basePath);
338
329
  let synced = false;
@@ -344,11 +335,6 @@ export async function deriveStateFromDb(basePath: string): Promise<GSDState> {
344
335
  }
345
336
  if (synced) allMilestones = getAllMilestones();
346
337
 
347
- // Disk→DB slice reconciliation (#2533): slices defined in ROADMAP.md but
348
- // missing from the DB cause permanent "No slice eligible" blocks because
349
- // the dependency resolver only sees DB rows. Parse each milestone's roadmap
350
- // and insert any missing slices, checking SUMMARY files to set correct status.
351
- // insertSlice uses INSERT OR IGNORE, so existing rows are never overwritten.
352
338
  for (const mid of diskIds) {
353
339
  if (isGhostMilestone(basePath, mid)) continue;
354
340
  const roadmapPath = resolveMilestoneFile(basePath, mid, "ROADMAP");
@@ -373,93 +359,43 @@ export async function deriveStateFromDb(basePath: string): Promise<GSDState> {
373
359
  });
374
360
  }
375
361
  }
362
+ return allMilestones;
363
+ }
376
364
 
377
- // Reconcile: discover milestones that exist on disk but are missing from
378
- // the DB. This happens when milestones were created before the DB migration
379
- // or were manually added to the filesystem. Without this, disk-only
380
- // milestones are invisible after migration (#2416).
381
- const dbMilestoneIds = new Set(allMilestones.map(m => m.id));
382
- const diskMilestoneIds = findMilestoneIds(basePath);
383
- for (const diskId of diskMilestoneIds) {
384
- if (!dbMilestoneIds.has(diskId)) {
385
- // Synthesize a minimal MilestoneRow for the disk-only milestone.
386
- // Title and status will be resolved from disk files in the loop below.
387
- allMilestones.push({
388
- id: diskId,
389
- title: diskId,
390
- status: 'active',
391
- depends_on: [] as string[],
392
- created_at: new Date().toISOString(),
393
- } as MilestoneRow);
394
- }
395
- }
396
- // Re-sort so milestones follow queue order (same as dispatch guard) (#2556)
397
- const customOrder = loadQueueOrder(basePath);
398
- const sortedIds = sortByQueueOrder(allMilestones.map(m => m.id), customOrder);
399
- const byId = new Map(allMilestones.map(m => [m.id, m]));
400
- allMilestones.length = 0;
401
- for (const id of sortedIds) allMilestones.push(byId.get(id)!);
402
-
403
- // Parallel worker isolation: when locked, filter to just the locked milestone
404
- const milestoneLock = process.env.GSD_MILESTONE_LOCK;
405
- const milestones = milestoneLock
406
- ? allMilestones.filter(m => m.id === milestoneLock)
407
- : allMilestones;
408
-
409
- if (milestones.length === 0) {
410
- return {
411
- activeMilestone: null,
412
- activeSlice: null,
413
- activeTask: null,
414
- phase: 'pre-planning',
415
- recentDecisions: [],
416
- blockers: [],
417
- nextAction: 'No milestones found. Run /gsd to create one.',
418
- registry: [],
419
- requirements,
420
- progress: { milestones: { done: 0, total: 0 } },
421
- };
422
- }
423
-
424
- // Phase 1: Build completeness set (which milestones count as "done" for dep resolution)
365
+ function buildCompletenessSet(basePath: string, milestones: MilestoneRow[]) {
425
366
  const completeMilestoneIds = new Set<string>();
426
367
  const parkedMilestoneIds = new Set<string>();
427
368
 
428
369
  for (const m of milestones) {
429
- // Check disk for PARKED flag (not stored in DB status reliably — disk is truth for flag files)
430
370
  const parkedFile = resolveMilestoneFile(basePath, m.id, "PARKED");
431
371
  if (parkedFile || m.status === 'parked') {
432
372
  parkedMilestoneIds.add(m.id);
433
373
  continue;
434
374
  }
435
-
436
375
  if (isStatusDone(m.status)) {
437
376
  completeMilestoneIds.add(m.id);
438
377
  continue;
439
378
  }
440
-
441
- // Check if milestone has a summary on disk (terminal artifact per #864)
442
379
  const summaryFile = resolveMilestoneFile(basePath, m.id, "SUMMARY");
443
380
  if (summaryFile) {
444
381
  completeMilestoneIds.add(m.id);
445
382
  continue;
446
383
  }
447
-
448
- // Milestones with all slices done but no SUMMARY file are in
449
- // validating/completing state — intentionally NOT added to
450
- // completeMilestoneIds. The SUMMARY file (checked above) is the
451
- // terminal artifact that proves completion per #864.
452
384
  }
385
+ return { completeMilestoneIds, parkedMilestoneIds };
386
+ }
453
387
 
454
- // Phase 2: Build registry and find active milestone
388
+ async function buildRegistryAndFindActive(
389
+ basePath: string,
390
+ milestones: MilestoneRow[],
391
+ completeMilestoneIds: Set<string>,
392
+ parkedMilestoneIds: Set<string>
393
+ ) {
455
394
  const registry: MilestoneRegistryEntry[] = [];
456
395
  let activeMilestone: ActiveRef | null = null;
457
396
  let activeMilestoneSlices: SliceRow[] = [];
458
397
  let activeMilestoneFound = false;
459
398
  let activeMilestoneHasDraft = false;
460
- // Queued shells (DB row, no slices, no content files) are deferred during
461
- // the main loop so they don't eclipse real active milestones (#3470).
462
- // If no real active milestone is found, the first deferred shell is promoted.
463
399
  let firstDeferredQueuedShell: { id: string; title: string; deps: string[] } | null = null;
464
400
 
465
401
  for (const m of milestones) {
@@ -468,19 +404,14 @@ export async function deriveStateFromDb(basePath: string): Promise<GSDState> {
468
404
  continue;
469
405
  }
470
406
 
471
- // Ghost milestone check: no slices in DB AND no substantive files on disk.
472
- // Skip queued milestones — they are handled by the deferred-shell logic below (#3470).
473
407
  const slices = getMilestoneSlices(m.id);
474
408
  if (slices.length === 0 && !isStatusDone(m.status) && m.status !== 'queued') {
475
- // Check disk for ghost detection
476
409
  if (isGhostMilestone(basePath, m.id)) continue;
477
410
  }
478
411
 
479
412
  const summaryFile = resolveMilestoneFile(basePath, m.id, "SUMMARY");
480
413
 
481
- // Determine if this milestone is complete
482
414
  if (completeMilestoneIds.has(m.id) || (summaryFile !== null)) {
483
- // Get title from DB or summary
484
415
  let title = stripMilestonePrefix(m.title) || m.id;
485
416
  if (summaryFile && !m.title) {
486
417
  const summaryContent = await loadFile(summaryFile);
@@ -489,14 +420,12 @@ export async function deriveStateFromDb(basePath: string): Promise<GSDState> {
489
420
  }
490
421
  }
491
422
  registry.push({ id: m.id, title, status: 'complete' });
492
- completeMilestoneIds.add(m.id); // ensure it's in the set
423
+ completeMilestoneIds.add(m.id);
493
424
  continue;
494
425
  }
495
426
 
496
- // Not complete — determine if it should be active
497
427
  const allSlicesDone = slices.length > 0 && slices.every(s => isStatusDone(s.status));
498
428
 
499
- // Get title — prefer DB, fall back to context file extraction
500
429
  let title = stripMilestonePrefix(m.title) || m.id;
501
430
  if (title === m.id) {
502
431
  const contextFile = resolveMilestoneFile(basePath, m.id, "CONTEXT");
@@ -507,7 +436,6 @@ export async function deriveStateFromDb(basePath: string): Promise<GSDState> {
507
436
  }
508
437
 
509
438
  if (!activeMilestoneFound) {
510
- // Check milestone-level dependencies
511
439
  const deps = m.depends_on;
512
440
  const depsUnmet = deps.some(dep => !completeMilestoneIds.has(dep));
513
441
 
@@ -516,11 +444,6 @@ export async function deriveStateFromDb(basePath: string): Promise<GSDState> {
516
444
  continue;
517
445
  }
518
446
 
519
- // Defer queued shell milestones with no substantive content (#3470).
520
- // A queued milestone with no slices and no context/draft file is a
521
- // placeholder that should not block later real active milestones.
522
- // If no real active milestone is found after the loop, the first
523
- // deferred shell is promoted to active (#2921).
524
447
  if (m.status === 'queued' && slices.length === 0) {
525
448
  const contextFile = resolveMilestoneFile(basePath, m.id, "CONTEXT");
526
449
  const draftFile = resolveMilestoneFile(basePath, m.id, "CONTEXT-DRAFT");
@@ -533,14 +456,12 @@ export async function deriveStateFromDb(basePath: string): Promise<GSDState> {
533
456
  }
534
457
  }
535
458
 
536
- // Handle all-slices-done case (validating/completing)
537
459
  if (allSlicesDone) {
538
460
  const validationFile = resolveMilestoneFile(basePath, m.id, "VALIDATION");
539
461
  const validationContent = validationFile ? await loadFile(validationFile) : null;
540
462
  const validationTerminal = validationContent ? isValidationTerminal(validationContent) : false;
541
463
 
542
464
  if (!validationTerminal || (validationTerminal && !summaryFile)) {
543
- // Validating or completing — still active
544
465
  activeMilestone = { id: m.id, title };
545
466
  activeMilestoneSlices = slices;
546
467
  activeMilestoneFound = true;
@@ -549,7 +470,6 @@ export async function deriveStateFromDb(basePath: string): Promise<GSDState> {
549
470
  }
550
471
  }
551
472
 
552
- // Check for context draft (needs-discussion phase)
553
473
  const contextFile = resolveMilestoneFile(basePath, m.id, "CONTEXT");
554
474
  const draftFile = resolveMilestoneFile(basePath, m.id, "CONTEXT-DRAFT");
555
475
  if (!contextFile && draftFile) activeMilestoneHasDraft = true;
@@ -559,13 +479,11 @@ export async function deriveStateFromDb(basePath: string): Promise<GSDState> {
559
479
  activeMilestoneFound = true;
560
480
  registry.push({ id: m.id, title, status: 'active', ...(deps.length > 0 ? { dependsOn: deps } : {}) });
561
481
  } else {
562
- // After active milestone found — rest are pending
563
482
  const deps = m.depends_on;
564
483
  registry.push({ id: m.id, title, status: 'pending', ...(deps.length > 0 ? { dependsOn: deps } : {}) });
565
484
  }
566
485
  }
567
486
 
568
- // Promote deferred queued shell if no real active milestone was found (#3470/#2921).
569
487
  if (!activeMilestoneFound && firstDeferredQueuedShell) {
570
488
  const shell = firstDeferredQueuedShell;
571
489
  activeMilestone = { id: shell.id, title: shell.title };
@@ -575,221 +493,141 @@ export async function deriveStateFromDb(basePath: string): Promise<GSDState> {
575
493
  if (entry) entry.status = 'active';
576
494
  }
577
495
 
578
- const milestoneProgress = {
579
- done: registry.filter(e => e.status === 'complete').length,
580
- total: registry.length,
581
- };
582
-
583
- // ── No active milestone ──────────────────────────────────────────────
584
- if (!activeMilestone) {
585
- const pendingEntries = registry.filter(e => e.status === 'pending');
586
- const parkedEntries = registry.filter(e => e.status === 'parked');
587
-
588
- if (pendingEntries.length > 0) {
589
- const blockerDetails = pendingEntries
590
- .filter(e => e.dependsOn && e.dependsOn.length > 0)
591
- .map(e => `${e.id} is waiting on unmet deps: ${e.dependsOn!.join(', ')}`);
592
- return {
593
- activeMilestone: null, activeSlice: null, activeTask: null,
594
- phase: 'blocked',
595
- recentDecisions: [], blockers: blockerDetails.length > 0
596
- ? blockerDetails
597
- : ['All remaining milestones are dep-blocked but no deps listed — check CONTEXT.md files'],
598
- nextAction: 'Resolve milestone dependencies before proceeding.',
599
- registry, requirements,
600
- progress: { milestones: milestoneProgress },
601
- };
602
- }
603
-
604
- if (parkedEntries.length > 0) {
605
- const parkedIds = parkedEntries.map(e => e.id).join(', ');
606
- return {
607
- activeMilestone: null, activeSlice: null, activeTask: null,
608
- phase: 'pre-planning',
609
- recentDecisions: [], blockers: [],
610
- nextAction: `All remaining milestones are parked (${parkedIds}). Run /gsd unpark <id> or create a new milestone.`,
611
- registry, requirements,
612
- progress: { milestones: milestoneProgress },
613
- };
614
- }
615
-
616
- if (registry.length === 0) {
617
- return {
618
- activeMilestone: null, activeSlice: null, activeTask: null,
619
- phase: 'pre-planning',
620
- recentDecisions: [], blockers: [],
621
- nextAction: 'No milestones found. Run /gsd to create one.',
622
- registry: [], requirements,
623
- progress: { milestones: { done: 0, total: 0 } },
624
- };
625
- }
496
+ return { registry, activeMilestone, activeMilestoneSlices, activeMilestoneHasDraft };
497
+ }
626
498
 
627
- // All milestones complete
628
- const lastEntry = registry[registry.length - 1];
629
- const activeReqs = requirements.active ?? 0;
630
- const completionNote = activeReqs > 0
631
- ? `All milestones complete. ${activeReqs} active requirement${activeReqs === 1 ? '' : 's'} in REQUIREMENTS.md ${activeReqs === 1 ? 'has' : 'have'} not been mapped to a milestone.`
632
- : 'All milestones complete.';
499
+ function handleNoActiveMilestone(
500
+ registry: MilestoneRegistryEntry[],
501
+ requirements: any,
502
+ milestoneProgress: { done: number, total: number }
503
+ ): GSDState {
504
+ const pendingEntries = registry.filter(e => e.status === 'pending');
505
+ const parkedEntries = registry.filter(e => e.status === 'parked');
506
+
507
+ if (pendingEntries.length > 0) {
508
+ const blockerDetails = pendingEntries
509
+ .filter(e => e.dependsOn && e.dependsOn.length > 0)
510
+ .map(e => `${e.id} is waiting on unmet deps: ${e.dependsOn!.join(', ')}`);
633
511
  return {
634
- activeMilestone: null,
635
- lastCompletedMilestone: lastEntry ? { id: lastEntry.id, title: lastEntry.title } : null,
636
- activeSlice: null, activeTask: null,
637
- phase: 'complete',
638
- recentDecisions: [], blockers: [],
639
- nextAction: completionNote,
512
+ activeMilestone: null, activeSlice: null, activeTask: null,
513
+ phase: 'blocked',
514
+ recentDecisions: [], blockers: blockerDetails.length > 0
515
+ ? blockerDetails
516
+ : ['All remaining milestones are dep-blocked but no deps listed — check CONTEXT.md files'],
517
+ nextAction: 'Resolve milestone dependencies before proceeding.',
640
518
  registry, requirements,
641
519
  progress: { milestones: milestoneProgress },
642
520
  };
643
521
  }
644
522
 
645
- // ── Active milestone has no slices or no roadmap ────────────────────
646
- const hasRoadmap = resolveMilestoneFile(basePath, activeMilestone.id, "ROADMAP") !== null;
647
-
648
- if (activeMilestoneSlices.length === 0) {
649
- if (!hasRoadmap) {
650
- const phase = activeMilestoneHasDraft ? 'needs-discussion' as const : 'pre-planning' as const;
651
- const nextAction = activeMilestoneHasDraft
652
- ? `Discuss draft context for milestone ${activeMilestone.id}.`
653
- : `Plan milestone ${activeMilestone.id}.`;
654
- return {
655
- activeMilestone, activeSlice: null, activeTask: null,
656
- phase, recentDecisions: [], blockers: [],
657
- nextAction, registry, requirements,
658
- progress: { milestones: milestoneProgress },
659
- };
660
- }
661
-
662
- // Has roadmap file but zero slices in DB — pre-planning (zero-slice roadmap guard)
523
+ if (parkedEntries.length > 0) {
524
+ const parkedIds = parkedEntries.map(e => e.id).join(', ');
663
525
  return {
664
- activeMilestone, activeSlice: null, activeTask: null,
526
+ activeMilestone: null, activeSlice: null, activeTask: null,
665
527
  phase: 'pre-planning',
666
528
  recentDecisions: [], blockers: [],
667
- nextAction: `Milestone ${activeMilestone.id} has a roadmap but no slices defined. Add slices to the roadmap.`,
529
+ nextAction: `All remaining milestones are parked (${parkedIds}). Run /gsd unpark <id> or create a new milestone.`,
668
530
  registry, requirements,
669
- progress: {
670
- milestones: milestoneProgress,
671
- slices: { done: 0, total: 0 },
672
- },
531
+ progress: { milestones: milestoneProgress },
673
532
  };
674
533
  }
675
534
 
676
- // ── All slices done → validating/completing ─────────────────────────
677
- const allSlicesDone = activeMilestoneSlices.every(s => isStatusDone(s.status));
678
- if (allSlicesDone) {
679
- const validationFile = resolveMilestoneFile(basePath, activeMilestone.id, "VALIDATION");
680
- const validationContent = validationFile ? await loadFile(validationFile) : null;
681
- const validationTerminal = validationContent ? isValidationTerminal(validationContent) : false;
682
- const verdict = validationContent ? extractVerdict(validationContent) : undefined;
683
- const sliceProgress = {
684
- done: activeMilestoneSlices.length,
685
- total: activeMilestoneSlices.length,
535
+ if (registry.length === 0) {
536
+ return {
537
+ activeMilestone: null, activeSlice: null, activeTask: null,
538
+ phase: 'pre-planning',
539
+ recentDecisions: [], blockers: [],
540
+ nextAction: 'No milestones found. Run /gsd to create one.',
541
+ registry: [], requirements,
542
+ progress: { milestones: { done: 0, total: 0 } },
686
543
  };
544
+ }
687
545
 
688
- // Force re-validation when verdict is needs-remediation — remediation slices
689
- // may have completed since the stale validation was written (#3596).
690
- if (!validationTerminal || verdict === 'needs-remediation') {
691
- return {
692
- activeMilestone, activeSlice: null, activeTask: null,
693
- phase: 'validating-milestone',
694
- recentDecisions: [], blockers: [],
695
- nextAction: `Validate milestone ${activeMilestone.id} before completion.`,
696
- registry, requirements,
697
- progress: { milestones: milestoneProgress, slices: sliceProgress },
698
- };
699
- }
546
+ const lastEntry = registry[registry.length - 1];
547
+ const activeReqs = requirements.active ?? 0;
548
+ const completionNote = activeReqs > 0
549
+ ? `All milestones complete. ${activeReqs} active requirement${activeReqs === 1 ? '' : 's'} in REQUIREMENTS.md ${activeReqs === 1 ? 'has' : 'have'} not been mapped to a milestone.`
550
+ : 'All milestones complete.';
551
+ return {
552
+ activeMilestone: null,
553
+ lastCompletedMilestone: lastEntry ? { id: lastEntry.id, title: lastEntry.title } : null,
554
+ activeSlice: null, activeTask: null,
555
+ phase: 'complete',
556
+ recentDecisions: [], blockers: [],
557
+ nextAction: completionNote,
558
+ registry, requirements,
559
+ progress: { milestones: milestoneProgress },
560
+ };
561
+ }
700
562
 
563
+ async function handleAllSlicesDone(
564
+ basePath: string,
565
+ activeMilestone: ActiveRef,
566
+ registry: MilestoneRegistryEntry[],
567
+ requirements: any,
568
+ milestoneProgress: { done: number, total: number },
569
+ sliceProgress: { done: number, total: number }
570
+ ): Promise<GSDState> {
571
+ const validationFile = resolveMilestoneFile(basePath, activeMilestone.id, "VALIDATION");
572
+ const validationContent = validationFile ? await loadFile(validationFile) : null;
573
+ const validationTerminal = validationContent ? isValidationTerminal(validationContent) : false;
574
+ const verdict = validationContent ? extractVerdict(validationContent) : undefined;
575
+
576
+ if (!validationTerminal || verdict === 'needs-remediation') {
701
577
  return {
702
578
  activeMilestone, activeSlice: null, activeTask: null,
703
- phase: 'completing-milestone',
579
+ phase: 'validating-milestone',
704
580
  recentDecisions: [], blockers: [],
705
- nextAction: `All slices complete in ${activeMilestone.id}. Write milestone summary.`,
581
+ nextAction: `Validate milestone ${activeMilestone.id} before completion.`,
706
582
  registry, requirements,
707
583
  progress: { milestones: milestoneProgress, slices: sliceProgress },
708
584
  };
709
585
  }
710
586
 
711
- // ── Find active slice (first incomplete with deps satisfied) ─────────
712
- const sliceProgress = {
713
- done: activeMilestoneSlices.filter(s => isStatusDone(s.status)).length,
714
- total: activeMilestoneSlices.length,
587
+ return {
588
+ activeMilestone, activeSlice: null, activeTask: null,
589
+ phase: 'completing-milestone',
590
+ recentDecisions: [], blockers: [],
591
+ nextAction: `All slices complete in ${activeMilestone.id}. Write milestone summary.`,
592
+ registry, requirements,
593
+ progress: { milestones: milestoneProgress, slices: sliceProgress },
715
594
  };
595
+ }
716
596
 
597
+ function resolveSliceDependencies(activeMilestoneSlices: SliceRow[]): { activeSlice: ActiveRef | null, activeSliceRow: SliceRow | null } {
717
598
  const doneSliceIds = new Set(
718
599
  activeMilestoneSlices.filter(s => isStatusDone(s.status)).map(s => s.id)
719
600
  );
720
601
 
721
- let activeSlice: ActiveRef | null = null;
722
- let activeSliceRow: SliceRow | null = null;
723
-
724
- // ── Slice-level parallel worker isolation ─────────────────────────────
725
- // When GSD_SLICE_LOCK is set, this process is a parallel worker scoped
726
- // to a single slice. Override activeSlice to only the locked slice ID.
727
602
  const sliceLock = process.env.GSD_SLICE_LOCK;
728
603
  if (sliceLock) {
729
604
  const lockedSlice = activeMilestoneSlices.find(s => s.id === sliceLock);
730
605
  if (lockedSlice) {
731
- activeSlice = { id: lockedSlice.id, title: lockedSlice.title };
732
- activeSliceRow = lockedSlice;
606
+ return { activeSlice: { id: lockedSlice.id, title: lockedSlice.title }, activeSliceRow: lockedSlice };
733
607
  } else {
734
608
  logWarning("state", `GSD_SLICE_LOCK=${sliceLock} not found in active slices — worker has no assigned work`);
735
- // Don't silently continue this is a dispatch error
736
- return {
737
- activeMilestone, activeSlice: null, activeTask: null,
738
- phase: 'blocked',
739
- recentDecisions: [], blockers: [`GSD_SLICE_LOCK=${sliceLock} not found in active milestone slices`],
740
- nextAction: 'Slice lock references a non-existent slice — check orchestrator dispatch.',
741
- registry, requirements,
742
- progress: { milestones: milestoneProgress, slices: sliceProgress },
743
- };
609
+ return { activeSlice: null, activeSliceRow: null };
744
610
  }
745
- } else {
746
- for (const s of activeMilestoneSlices) {
747
- if (isStatusDone(s.status)) continue;
748
- // #2661: Skip deferred slices — a decision explicitly deferred this work.
749
- // Without this guard the dispatcher would keep dispatching deferred slices
750
- // because DECISIONS.md is only contextual, not authoritative for dispatch.
751
- if (isDeferredStatus(s.status)) continue;
752
- if (s.depends.every(dep => doneSliceIds.has(dep))) {
753
- activeSlice = { id: s.id, title: s.title };
754
- activeSliceRow = s;
755
- break;
756
- }
757
- }
758
- }
759
-
760
- if (!activeSlice) {
761
- return {
762
- activeMilestone, activeSlice: null, activeTask: null,
763
- phase: 'blocked',
764
- recentDecisions: [], blockers: ['No slice eligible — check dependency ordering'],
765
- nextAction: 'Resolve dependency blockers or plan next slice.',
766
- registry, requirements,
767
- progress: { milestones: milestoneProgress, slices: sliceProgress },
768
- };
769
611
  }
770
612
 
771
- // ── Check for slice plan file on disk ────────────────────────────────
772
- const planFile = resolveSliceFile(basePath, activeMilestone.id, activeSlice.id, "PLAN");
773
- if (!planFile) {
774
- return {
775
- activeMilestone, activeSlice, activeTask: null,
776
- phase: 'planning',
777
- recentDecisions: [], blockers: [],
778
- nextAction: `Plan slice ${activeSlice.id} (${activeSlice.title}).`,
779
- registry, requirements,
780
- progress: { milestones: milestoneProgress, slices: sliceProgress },
781
- };
613
+ for (const s of activeMilestoneSlices) {
614
+ if (isStatusDone(s.status)) continue;
615
+ if (isDeferredStatus(s.status)) continue;
616
+ if (s.depends.every(dep => doneSliceIds.has(dep))) {
617
+ return { activeSlice: { id: s.id, title: s.title }, activeSliceRow: s };
618
+ }
782
619
  }
620
+ return { activeSlice: null, activeSliceRow: null };
621
+ }
783
622
 
784
- // ── Get tasks from DB ────────────────────────────────────────────────
785
- let tasks = getSliceTasks(activeMilestone.id, activeSlice.id);
623
+ async function reconcileSliceTasks(
624
+ basePath: string,
625
+ milestoneId: string,
626
+ sliceId: string,
627
+ planFile: string
628
+ ): Promise<TaskRow[]> {
629
+ let tasks = getSliceTasks(milestoneId, sliceId);
786
630
 
787
- // ── Reconcile missing tasks: plan file has tasks but DB is empty (#3600) ──
788
- // When the planning agent writes S##-PLAN.md with task entries but never
789
- // calls the gsd_plan_slice persistence tool, the DB has zero task rows
790
- // even though the plan file contains valid tasks. Without this reconciliation,
791
- // deriveState returns phase='planning' forever — the dispatcher re-dispatches
792
- // plan-slice in an infinite loop.
793
631
  if (tasks.length === 0 && planFile) {
794
632
  try {
795
633
  const planContent = await loadFile(planFile);
@@ -801,53 +639,188 @@ export async function deriveStateFromDb(basePath: string): Promise<GSDState> {
801
639
  try {
802
640
  insertTask({
803
641
  id: t.id,
804
- sliceId: activeSlice.id,
805
- milestoneId: activeMilestone.id,
642
+ sliceId,
643
+ milestoneId,
806
644
  title: t.title,
807
645
  status: t.done ? 'complete' : 'pending',
808
646
  sequence: i + 1,
809
647
  });
810
648
  } catch (insertErr) {
811
- // Task may already exist from a partial previous import — skip
812
649
  logWarning("reconcile", `failed to insert task ${t.id} from plan file: ${insertErr instanceof Error ? insertErr.message : String(insertErr)}`);
813
650
  }
814
651
  }
815
- tasks = getSliceTasks(activeMilestone.id, activeSlice.id);
816
- logWarning("reconcile", `imported ${tasks.length} tasks from plan file for ${activeMilestone.id}/${activeSlice.id} — DB was empty (#3600)`, { mid: activeMilestone.id, sid: activeSlice.id });
652
+ tasks = getSliceTasks(milestoneId, sliceId);
653
+ logWarning("reconcile", `imported ${tasks.length} tasks from plan file for ${milestoneId}/${sliceId} — DB was empty (#3600)`, { mid: milestoneId, sid: sliceId });
817
654
  }
818
655
  }
819
656
  } catch (err) {
820
- // Non-fatal fall through to the existing "empty plan" logic
821
- logError("reconcile", `plan-file task import failed for ${activeMilestone.id}/${activeSlice.id}: ${err instanceof Error ? err.message : String(err)}`);
657
+ logError("reconcile", `plan-file task import failed for ${milestoneId}/${sliceId}: ${err instanceof Error ? err.message : String(err)}`);
822
658
  }
823
659
  }
824
660
 
825
- // ── Reconcile stale task status (#2514) ──────────────────────────────
826
- // When a session disconnects after the agent writes SUMMARY + VERIFY
827
- // artifacts but before postUnitPostVerification updates the DB, tasks
828
- // remain "pending" in the DB despite being complete on disk. Without
829
- // reconciliation, deriveState keeps returning the stale task as active,
830
- // causing the dispatcher to re-dispatch the same completed task forever.
831
661
  let reconciled = false;
832
662
  for (const t of tasks) {
833
663
  if (isStatusDone(t.status)) continue;
834
- const summaryPath = resolveTaskFile(basePath, activeMilestone.id, activeSlice.id, t.id, "SUMMARY");
664
+ const summaryPath = resolveTaskFile(basePath, milestoneId, sliceId, t.id, "SUMMARY");
835
665
  if (summaryPath && existsSync(summaryPath)) {
836
666
  try {
837
- updateTaskStatus(activeMilestone.id, activeSlice.id, t.id, "complete");
838
- logWarning("reconcile", `task ${activeMilestone.id}/${activeSlice.id}/${t.id} status reconciled from "${t.status}" to "complete" (#2514)`, { mid: activeMilestone.id, sid: activeSlice.id, tid: t.id });
667
+ updateTaskStatus(milestoneId, sliceId, t.id, "complete");
668
+ logWarning("reconcile", `task ${milestoneId}/${sliceId}/${t.id} status reconciled from "${t.status}" to "complete" (#2514)`, { mid: milestoneId, sid: sliceId, tid: t.id });
839
669
  reconciled = true;
840
670
  } catch (e) {
841
- // DB write failed — continue with stale status rather than crash
842
671
  logError("reconcile", `failed to update task ${t.id}`, { tid: t.id, error: (e as Error).message });
843
672
  }
844
673
  }
845
674
  }
846
- // Re-fetch tasks if any were reconciled so downstream logic sees fresh status
847
675
  if (reconciled) {
848
- tasks = getSliceTasks(activeMilestone.id, activeSlice.id);
676
+ tasks = getSliceTasks(milestoneId, sliceId);
677
+ }
678
+ return tasks;
679
+ }
680
+
681
+ async function detectBlockers(basePath: string, milestoneId: string, sliceId: string, tasks: TaskRow[]): Promise<string | null> {
682
+ const completedTasks = tasks.filter(t => isStatusDone(t.status));
683
+ for (const ct of completedTasks) {
684
+ if (ct.blocker_discovered) {
685
+ return ct.id;
686
+ }
687
+ const summaryFile = resolveTaskFile(basePath, milestoneId, sliceId, ct.id, "SUMMARY");
688
+ if (!summaryFile) continue;
689
+ const summaryContent = await loadFile(summaryFile);
690
+ if (!summaryContent) continue;
691
+ const summary = parseSummary(summaryContent);
692
+ if (summary.frontmatter.blocker_discovered) {
693
+ return ct.id;
694
+ }
695
+ }
696
+ return null;
697
+ }
698
+
699
+ function checkReplanTrigger(basePath: string, milestoneId: string, sliceId: string): boolean {
700
+ const sliceRow = getSlice(milestoneId, sliceId);
701
+ const dbTriggered = !!sliceRow?.replan_triggered_at;
702
+ const diskTriggered = !dbTriggered &&
703
+ !!resolveSliceFile(basePath, milestoneId, sliceId, "REPLAN-TRIGGER");
704
+ return dbTriggered || diskTriggered;
705
+ }
706
+
707
+ async function checkInterruptedWork(basePath: string, milestoneId: string, sliceId: string): Promise<boolean> {
708
+ const sDir = resolveSlicePath(basePath, milestoneId, sliceId);
709
+ const continueFile = sDir ? resolveSliceFile(basePath, milestoneId, sliceId, "CONTINUE") : null;
710
+ return !!(continueFile && await loadFile(continueFile)) ||
711
+ !!(sDir && await loadFile(join(sDir, "continue.md")));
712
+ }
713
+
714
+ export async function deriveStateFromDb(basePath: string): Promise<GSDState> {
715
+ const requirements = parseRequirementCounts(await loadFile(resolveGsdRootFile(basePath, "REQUIREMENTS")));
716
+
717
+ let allMilestones = reconcileDiskToDb(basePath);
718
+
719
+ const customOrder = loadQueueOrder(basePath);
720
+ const sortedIds = sortByQueueOrder(allMilestones.map(m => m.id), customOrder);
721
+ const byId = new Map(allMilestones.map(m => [m.id, m]));
722
+ allMilestones.length = 0;
723
+ for (const id of sortedIds) allMilestones.push(byId.get(id)!);
724
+
725
+ const milestoneLock = process.env.GSD_MILESTONE_LOCK;
726
+ const milestones = milestoneLock
727
+ ? allMilestones.filter(m => m.id === milestoneLock)
728
+ : allMilestones;
729
+
730
+ if (milestones.length === 0) {
731
+ return {
732
+ activeMilestone: null, activeSlice: null, activeTask: null,
733
+ phase: 'pre-planning', recentDecisions: [], blockers: [],
734
+ nextAction: 'No milestones found. Run /gsd to create one.',
735
+ registry: [], requirements,
736
+ progress: { milestones: { done: 0, total: 0 } },
737
+ };
738
+ }
739
+
740
+ const { completeMilestoneIds, parkedMilestoneIds } = buildCompletenessSet(basePath, milestones);
741
+
742
+ const registryContext = await buildRegistryAndFindActive(basePath, milestones, completeMilestoneIds, parkedMilestoneIds);
743
+ const { registry, activeMilestone, activeMilestoneSlices, activeMilestoneHasDraft } = registryContext;
744
+
745
+ const milestoneProgress = {
746
+ done: registry.filter(e => e.status === 'complete').length,
747
+ total: registry.length,
748
+ };
749
+
750
+ if (!activeMilestone) {
751
+ return handleNoActiveMilestone(registry, requirements, milestoneProgress);
849
752
  }
850
753
 
754
+ const hasRoadmap = resolveMilestoneFile(basePath, activeMilestone.id, "ROADMAP") !== null;
755
+
756
+ if (activeMilestoneSlices.length === 0) {
757
+ if (!hasRoadmap) {
758
+ const phase = activeMilestoneHasDraft ? 'needs-discussion' as const : 'pre-planning' as const;
759
+ const nextAction = activeMilestoneHasDraft
760
+ ? `Discuss draft context for milestone ${activeMilestone.id}.`
761
+ : `Plan milestone ${activeMilestone.id}.`;
762
+ return {
763
+ activeMilestone, activeSlice: null, activeTask: null,
764
+ phase, recentDecisions: [], blockers: [],
765
+ nextAction, registry, requirements,
766
+ progress: { milestones: milestoneProgress },
767
+ };
768
+ }
769
+
770
+ return {
771
+ activeMilestone, activeSlice: null, activeTask: null,
772
+ phase: 'pre-planning', recentDecisions: [], blockers: [],
773
+ nextAction: `Milestone ${activeMilestone.id} has a roadmap but no slices defined. Add slices to the roadmap.`,
774
+ registry, requirements,
775
+ progress: { milestones: milestoneProgress, slices: { done: 0, total: 0 } },
776
+ };
777
+ }
778
+
779
+ const allSlicesDone = activeMilestoneSlices.every(s => isStatusDone(s.status));
780
+ const sliceProgress = {
781
+ done: activeMilestoneSlices.filter(s => isStatusDone(s.status)).length,
782
+ total: activeMilestoneSlices.length,
783
+ };
784
+
785
+ if (allSlicesDone) {
786
+ return handleAllSlicesDone(basePath, activeMilestone, registry, requirements, milestoneProgress, sliceProgress);
787
+ }
788
+
789
+ const activeSliceContext = resolveSliceDependencies(activeMilestoneSlices);
790
+ if (!activeSliceContext.activeSlice) {
791
+ // If locked slice wasn't found, it returns null but logs warning, we need to return 'blocked'
792
+ if (process.env.GSD_SLICE_LOCK) {
793
+ return {
794
+ activeMilestone, activeSlice: null, activeTask: null,
795
+ phase: 'blocked', recentDecisions: [], blockers: [`GSD_SLICE_LOCK=${process.env.GSD_SLICE_LOCK} not found in active milestone slices`],
796
+ nextAction: 'Slice lock references a non-existent slice — check orchestrator dispatch.',
797
+ registry, requirements,
798
+ progress: { milestones: milestoneProgress, slices: sliceProgress },
799
+ };
800
+ }
801
+ return {
802
+ activeMilestone, activeSlice: null, activeTask: null,
803
+ phase: 'blocked', recentDecisions: [], blockers: ['No slice eligible — check dependency ordering'],
804
+ nextAction: 'Resolve dependency blockers or plan next slice.',
805
+ registry, requirements,
806
+ progress: { milestones: milestoneProgress, slices: sliceProgress },
807
+ };
808
+ }
809
+ const { activeSlice } = activeSliceContext;
810
+
811
+ const planFile = resolveSliceFile(basePath, activeMilestone.id, activeSlice.id, "PLAN");
812
+ if (!planFile) {
813
+ return {
814
+ activeMilestone, activeSlice, activeTask: null,
815
+ phase: 'planning', recentDecisions: [], blockers: [],
816
+ nextAction: `Plan slice ${activeSlice.id} (${activeSlice.title}).`,
817
+ registry, requirements,
818
+ progress: { milestones: milestoneProgress, slices: sliceProgress },
819
+ };
820
+ }
821
+
822
+ const tasks = await reconcileSliceTasks(basePath, activeMilestone.id, activeSlice.id, planFile);
823
+
851
824
  const taskProgress = {
852
825
  done: tasks.filter(t => isStatusDone(t.status)).length,
853
826
  total: tasks.length,
@@ -856,23 +829,19 @@ export async function deriveStateFromDb(basePath: string): Promise<GSDState> {
856
829
  const activeTaskRow = tasks.find(t => !isStatusDone(t.status));
857
830
 
858
831
  if (!activeTaskRow && tasks.length > 0) {
859
- // All tasks done but slice not marked complete → summarizing
860
832
  return {
861
833
  activeMilestone, activeSlice, activeTask: null,
862
- phase: 'summarizing',
863
- recentDecisions: [], blockers: [],
834
+ phase: 'summarizing', recentDecisions: [], blockers: [],
864
835
  nextAction: `All tasks done in ${activeSlice.id}. Write slice summary and complete slice.`,
865
836
  registry, requirements,
866
837
  progress: { milestones: milestoneProgress, slices: sliceProgress, tasks: taskProgress },
867
838
  };
868
839
  }
869
840
 
870
- // Empty plan — no tasks defined yet
871
841
  if (!activeTaskRow) {
872
842
  return {
873
843
  activeMilestone, activeSlice, activeTask: null,
874
- phase: 'planning',
875
- recentDecisions: [], blockers: [],
844
+ phase: 'planning', recentDecisions: [], blockers: [],
876
845
  nextAction: `Slice ${activeSlice.id} has a plan file but no tasks. Add tasks to the plan.`,
877
846
  registry, requirements,
878
847
  progress: { milestones: milestoneProgress, slices: sliceProgress, tasks: taskProgress },
@@ -881,15 +850,13 @@ export async function deriveStateFromDb(basePath: string): Promise<GSDState> {
881
850
 
882
851
  const activeTask: ActiveRef = { id: activeTaskRow.id, title: activeTaskRow.title };
883
852
 
884
- // ── Task plan file check (#909) ─────────────────────────────────────
885
853
  const tasksDir = resolveTasksDir(basePath, activeMilestone.id, activeSlice.id);
886
854
  if (tasksDir && existsSync(tasksDir) && tasks.length > 0) {
887
855
  const allFiles = readdirSync(tasksDir).filter(f => f.endsWith(".md"));
888
856
  if (allFiles.length === 0) {
889
857
  return {
890
858
  activeMilestone, activeSlice, activeTask: null,
891
- phase: 'planning',
892
- recentDecisions: [], blockers: [],
859
+ phase: 'planning', recentDecisions: [], blockers: [],
893
860
  nextAction: `Task plan files missing for ${activeSlice.id}. Run plan-slice to generate task plans.`,
894
861
  registry, requirements,
895
862
  progress: { milestones: milestoneProgress, slices: sliceProgress, tasks: taskProgress },
@@ -898,50 +865,34 @@ export async function deriveStateFromDb(basePath: string): Promise<GSDState> {
898
865
  }
899
866
 
900
867
  // ── Quality gate evaluation check ──────────────────────────────────
901
- // If slice-scoped gates (Q3/Q4) are still pending, pause before execution
902
- // so the gate-evaluate dispatch rule can run parallel sub-agents.
868
+ // Pause before execution only when gates owned by the `gate-evaluate`
869
+ // turn (Q3/Q4) are still pending. Q8 is also `scope:"slice"` but is
870
+ // owned by `complete-slice`, so it must NOT block the evaluating-gates
871
+ // phase — otherwise auto-loop stalls forever waiting for a gate that
872
+ // this turn never evaluates. See gate-registry.ts for the ownership map.
903
873
  // Slices with zero gate rows (pre-feature or simple) skip straight through.
904
- const pendingGateCount = getPendingSliceGateCount(activeMilestone.id, activeSlice.id);
874
+ const pendingGateCount = getPendingGateCountForTurn(
875
+ activeMilestone.id,
876
+ activeSlice.id,
877
+ "gate-evaluate",
878
+ );
905
879
  if (pendingGateCount > 0) {
906
880
  return {
907
881
  activeMilestone, activeSlice, activeTask: null,
908
- phase: 'evaluating-gates',
909
- recentDecisions: [], blockers: [],
882
+ phase: 'evaluating-gates', recentDecisions: [], blockers: [],
910
883
  nextAction: `Evaluate ${pendingGateCount} quality gate(s) for ${activeSlice.id} before execution.`,
911
884
  registry, requirements,
912
885
  progress: { milestones: milestoneProgress, slices: sliceProgress, tasks: taskProgress },
913
886
  };
914
887
  }
915
888
 
916
- // ── Blocker detection: check completed tasks for blocker_discovered ──
917
- const completedTasks = tasks.filter(t => isStatusDone(t.status));
918
- let blockerTaskId: string | null = null;
919
- for (const ct of completedTasks) {
920
- if (ct.blocker_discovered) {
921
- blockerTaskId = ct.id;
922
- break;
923
- }
924
- // Also check disk summary in case DB doesn't have the flag
925
- const summaryFile = resolveTaskFile(basePath, activeMilestone.id, activeSlice.id, ct.id, "SUMMARY");
926
- if (!summaryFile) continue;
927
- const summaryContent = await loadFile(summaryFile);
928
- if (!summaryContent) continue;
929
- const summary = parseSummary(summaryContent);
930
- if (summary.frontmatter.blocker_discovered) {
931
- blockerTaskId = ct.id;
932
- break;
933
- }
934
- }
935
-
889
+ const blockerTaskId = await detectBlockers(basePath, activeMilestone.id, activeSlice.id, tasks);
936
890
  if (blockerTaskId) {
937
- // Loop protection: if replan_history has entries for this slice, a replan
938
- // was already performed — don't re-enter replanning phase.
939
891
  const replanHistory = getReplanHistory(activeMilestone.id, activeSlice.id);
940
892
  if (replanHistory.length === 0) {
941
893
  return {
942
894
  activeMilestone, activeSlice, activeTask,
943
- phase: 'replanning-slice',
944
- recentDecisions: [],
895
+ phase: 'replanning-slice', recentDecisions: [],
945
896
  blockers: [`Task ${blockerTaskId} discovered a blocker requiring slice replan`],
946
897
  nextAction: `Task ${blockerTaskId} reported blocker_discovered. Replan slice ${activeSlice.id} before continuing.`,
947
898
  activeWorkspace: undefined,
@@ -951,22 +902,14 @@ export async function deriveStateFromDb(basePath: string): Promise<GSDState> {
951
902
  }
952
903
  }
953
904
 
954
- // ── REPLAN-TRIGGER detection ─────────────────────────────────────────
955
905
  if (!blockerTaskId) {
956
- const sliceRow = getSlice(activeMilestone.id, activeSlice.id);
957
- // Check DB column first, fall back to disk trigger file when DB write
958
- // was best-effort and failed (triage-resolution.ts dual-write gap).
959
- const dbTriggered = !!sliceRow?.replan_triggered_at;
960
- const diskTriggered = !dbTriggered &&
961
- !!resolveSliceFile(basePath, activeMilestone.id, activeSlice.id, "REPLAN-TRIGGER");
962
- if (dbTriggered || diskTriggered) {
963
- // Loop protection: if replan_history has entries, replan was already done
906
+ const isTriggered = checkReplanTrigger(basePath, activeMilestone.id, activeSlice.id);
907
+ if (isTriggered) {
964
908
  const replanHistory = getReplanHistory(activeMilestone.id, activeSlice.id);
965
909
  if (replanHistory.length === 0) {
966
910
  return {
967
911
  activeMilestone, activeSlice, activeTask,
968
- phase: 'replanning-slice',
969
- recentDecisions: [],
912
+ phase: 'replanning-slice', recentDecisions: [],
970
913
  blockers: ['Triage replan trigger detected — slice replan required'],
971
914
  nextAction: `Triage replan triggered for slice ${activeSlice.id}. Replan before continuing.`,
972
915
  activeWorkspace: undefined,
@@ -977,16 +920,11 @@ export async function deriveStateFromDb(basePath: string): Promise<GSDState> {
977
920
  }
978
921
  }
979
922
 
980
- // ── Check for interrupted work ───────────────────────────────────────
981
- const sDir = resolveSlicePath(basePath, activeMilestone.id, activeSlice.id);
982
- const continueFile = sDir ? resolveSliceFile(basePath, activeMilestone.id, activeSlice.id, "CONTINUE") : null;
983
- const hasInterrupted = !!(continueFile && await loadFile(continueFile)) ||
984
- !!(sDir && await loadFile(join(sDir, "continue.md")));
923
+ const hasInterrupted = await checkInterruptedWork(basePath, activeMilestone.id, activeSlice.id);
985
924
 
986
925
  return {
987
926
  activeMilestone, activeSlice, activeTask,
988
- phase: 'executing',
989
- recentDecisions: [], blockers: [],
927
+ phase: 'executing', recentDecisions: [], blockers: [],
990
928
  nextAction: hasInterrupted
991
929
  ? `Resume interrupted work on ${activeTask.id}: ${activeTask.title} in slice ${activeSlice.id}. Read continue.md first.`
992
930
  : `Execute ${activeTask.id}: ${activeTask.title} in slice ${activeSlice.id}.`,
@@ -995,11 +933,14 @@ export async function deriveStateFromDb(basePath: string): Promise<GSDState> {
995
933
  };
996
934
  }
997
935
 
936
+
998
937
  // LEGACY: Filesystem-based state derivation for unmigrated projects.
999
938
  // DB-backed projects use deriveStateFromDb() above. Target: extract to
1000
939
  // state-legacy.ts when all projects are DB-backed.
1001
940
  export async function _deriveStateImpl(basePath: string): Promise<GSDState> {
1002
- const milestoneIds = findMilestoneIds(basePath);
941
+ const diskIds = findMilestoneIds(basePath);
942
+ const customOrder = loadQueueOrder(basePath);
943
+ const milestoneIds = sortByQueueOrder(diskIds, customOrder);
1003
944
 
1004
945
  // ── Parallel worker isolation ──────────────────────────────────────────
1005
946
  // When GSD_MILESTONE_LOCK is set, this process is a parallel worker