@opengsd/gsd-pi 1.4.0-dev.c18009cd → 1.5.0-dev.4fb2d460

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 (334) hide show
  1. package/README.md +10 -10
  2. package/dist/resources/.managed-resources-content-hash +1 -1
  3. package/dist/resources/extensions/ask-user-questions.js +1 -1
  4. package/dist/resources/extensions/gsd/auto/closeout.js +97 -5
  5. package/dist/resources/extensions/gsd/auto/orchestrator.js +4 -3
  6. package/dist/resources/extensions/gsd/auto/pre-dispatch.js +6 -3
  7. package/dist/resources/extensions/gsd/auto/unit-phase.js +5 -9
  8. package/dist/resources/extensions/gsd/auto-artifact-paths.js +12 -3
  9. package/dist/resources/extensions/gsd/auto-direct-dispatch.js +11 -7
  10. package/dist/resources/extensions/gsd/auto-dispatch.js +16 -2
  11. package/dist/resources/extensions/gsd/auto-recovery.js +50 -6
  12. package/dist/resources/extensions/gsd/auto-start.js +4 -3
  13. package/dist/resources/extensions/gsd/auto-worktree-branch-lifecycle.js +189 -0
  14. package/dist/resources/extensions/gsd/auto-worktree-cleanup.js +133 -0
  15. package/dist/resources/extensions/gsd/auto-worktree-conflict-auto-resolve.js +35 -0
  16. package/dist/resources/extensions/gsd/auto-worktree-creation.js +130 -0
  17. package/dist/resources/extensions/gsd/auto-worktree-entry.js +118 -0
  18. package/dist/resources/extensions/gsd/auto-worktree-merge-already-merged.js +98 -0
  19. package/dist/resources/extensions/gsd/auto-worktree-merge-branch-head.js +52 -0
  20. package/dist/resources/extensions/gsd/auto-worktree-merge-cleanup.js +87 -0
  21. package/dist/resources/extensions/gsd/auto-worktree-merge-code-changes.js +46 -0
  22. package/dist/resources/extensions/gsd/auto-worktree-merge-db-ready.js +68 -0
  23. package/dist/resources/extensions/gsd/auto-worktree-merge-integration-branch.js +51 -0
  24. package/dist/resources/extensions/gsd/auto-worktree-merge-message.js +106 -0
  25. package/dist/resources/extensions/gsd/auto-worktree-merge-pre-teardown.js +64 -0
  26. package/dist/resources/extensions/gsd/auto-worktree-merge-stash.js +168 -0
  27. package/dist/resources/extensions/gsd/auto-worktree-merge.js +302 -0
  28. package/dist/resources/extensions/gsd/auto-worktree-milestone-shelter.js +119 -0
  29. package/dist/resources/extensions/gsd/auto-worktree-path-resolution.js +5 -0
  30. package/dist/resources/extensions/gsd/auto-worktree-path-utils.js +5 -0
  31. package/dist/resources/extensions/gsd/auto-worktree-resource-version.js +41 -0
  32. package/dist/resources/extensions/gsd/auto-worktree-runtime-cleanup.js +99 -0
  33. package/dist/resources/extensions/gsd/auto-worktree-session-registry.js +57 -0
  34. package/dist/resources/extensions/gsd/auto-worktree-sync.js +249 -0
  35. package/dist/resources/extensions/gsd/auto-worktree-teardown.js +120 -0
  36. package/dist/resources/extensions/gsd/auto-worktree.js +20 -2073
  37. package/dist/resources/extensions/gsd/auto.js +9 -3
  38. package/dist/resources/extensions/gsd/bootstrap/dynamic-tools.js +1 -1
  39. package/dist/resources/extensions/gsd/bootstrap/register-hooks.js +20 -2
  40. package/dist/resources/extensions/gsd/bootstrap/system-context.js +1 -1
  41. package/dist/resources/extensions/gsd/bootstrap/tool-call-loop-guard.js +185 -27
  42. package/dist/resources/extensions/gsd/codebase-generator.js +7 -1
  43. package/dist/resources/extensions/gsd/commands-handlers.js +1 -1
  44. package/dist/resources/extensions/gsd/commands-workflow-templates.js +3 -3
  45. package/dist/resources/extensions/gsd/db/queries.js +9 -9
  46. package/dist/resources/extensions/gsd/db/writers/cascades.js +20 -2
  47. package/dist/resources/extensions/gsd/doctor-engine-checks.js +93 -11
  48. package/dist/resources/extensions/gsd/flat-phase-migration.js +32 -3
  49. package/dist/resources/extensions/gsd/forensics.js +1 -1
  50. package/dist/resources/extensions/gsd/git-conflict-resolve.js +2 -2
  51. package/dist/resources/extensions/gsd/git-service.js +45 -0
  52. package/dist/resources/extensions/gsd/gsd-db.js +23 -11
  53. package/dist/resources/extensions/gsd/guided-flow.js +22 -17
  54. package/dist/resources/extensions/gsd/markdown-renderer.js +15 -3
  55. package/dist/resources/extensions/gsd/md-importer.js +89 -23
  56. package/dist/resources/extensions/gsd/migration-auto-check.js +30 -3
  57. package/dist/resources/extensions/gsd/milestone-merge-transaction.js +11 -0
  58. package/dist/resources/extensions/gsd/milestone-settlement.js +1 -1
  59. package/dist/resources/extensions/gsd/parallel-merge.js +1 -1
  60. package/dist/resources/extensions/gsd/parallel-orchestrator.js +3 -1
  61. package/dist/resources/extensions/gsd/paths.js +17 -3
  62. package/dist/resources/extensions/gsd/preferences-skills.js +10 -16
  63. package/dist/resources/extensions/gsd/preferences-types.js +1 -0
  64. package/dist/resources/extensions/gsd/preferences-validation.js +82 -0
  65. package/dist/resources/extensions/gsd/preferences.js +20 -0
  66. package/dist/resources/extensions/gsd/prompts/system.md +6 -6
  67. package/dist/resources/extensions/gsd/quick.js +12 -3
  68. package/dist/resources/extensions/gsd/skill-discovery.js +36 -23
  69. package/dist/resources/extensions/gsd/slice-parallel-orchestrator.js +2 -1
  70. package/dist/resources/extensions/gsd/state/derive/from-db.js +88 -153
  71. package/dist/resources/extensions/gsd/tool-contract.js +14 -0
  72. package/dist/resources/extensions/gsd/tools/reopen-slice.js +5 -0
  73. package/dist/resources/extensions/gsd/tools/reopen-task.js +13 -5
  74. package/dist/resources/extensions/gsd/unit-registry.js +12 -2
  75. package/dist/resources/extensions/gsd/unmerged-milestone-guard.js +1 -1
  76. package/dist/resources/extensions/gsd/validation-block-guard.js +2 -1
  77. package/dist/resources/extensions/gsd/workspace-git-preflight.js +2 -2
  78. package/dist/resources/extensions/gsd/worktree-command.js +1 -1
  79. package/dist/resources/extensions/gsd/worktree-lifecycle.js +14 -9
  80. package/dist/resources/extensions/gsd/worktree-reentry.js +2 -1
  81. package/dist/resources/extensions/shared/interview-ui.js +62 -9
  82. package/dist/tsconfig.extensions.tsbuildinfo +1 -1
  83. package/dist/web/standalone/.next/BUILD_ID +1 -1
  84. package/dist/web/standalone/.next/app-path-routes-manifest.json +8 -8
  85. package/dist/web/standalone/.next/build-manifest.json +2 -2
  86. package/dist/web/standalone/.next/prerender-manifest.json +3 -3
  87. package/dist/web/standalone/.next/server/app/_global-error.html +1 -1
  88. package/dist/web/standalone/.next/server/app/_global-error.rsc +1 -1
  89. package/dist/web/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  90. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
  91. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
  92. package/dist/web/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  93. package/dist/web/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  94. package/dist/web/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  95. package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
  96. package/dist/web/standalone/.next/server/app/_not-found.rsc +1 -1
  97. package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +1 -1
  98. package/dist/web/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  99. package/dist/web/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +1 -1
  100. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  101. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  102. package/dist/web/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
  103. package/dist/web/standalone/.next/server/app/index.html +1 -1
  104. package/dist/web/standalone/.next/server/app/index.rsc +1 -1
  105. package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +1 -1
  106. package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +1 -1
  107. package/dist/web/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
  108. package/dist/web/standalone/.next/server/app/index.segments/_index.segment.rsc +1 -1
  109. package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
  110. package/dist/web/standalone/.next/server/app-paths-manifest.json +8 -8
  111. package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
  112. package/dist/web/standalone/.next/server/pages/404.html +1 -1
  113. package/dist/web/standalone/.next/server/pages/500.html +1 -1
  114. package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
  115. package/dist/web/standalone/node_modules/@gsd/native/package.json +1 -1
  116. package/dist/web/standalone/node_modules/node-pty/build/Makefile +1 -1
  117. package/integrations/hermes/open_gsd_hermes/gsd_client.py +24 -35
  118. package/integrations/hermes/tests/test_gsd_client_timeout.py +4 -4
  119. package/package.json +6 -6
  120. package/packages/cloud-mcp-gateway/package.json +2 -2
  121. package/packages/contracts/package.json +1 -1
  122. package/packages/daemon/package.json +4 -4
  123. package/packages/gsd-agent-core/dist/export-html/template.js +53 -4
  124. package/packages/gsd-agent-core/package.json +5 -5
  125. package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-controller.d.ts.map +1 -1
  126. package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-controller.js +3 -2
  127. package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-controller.js.map +1 -1
  128. package/packages/gsd-agent-modes/package.json +7 -7
  129. package/packages/mcp-server/package.json +4 -4
  130. package/packages/native/package.json +1 -1
  131. package/packages/pi-agent-core/dist/agent-loop.d.ts.map +1 -1
  132. package/packages/pi-agent-core/dist/agent-loop.js +14 -9
  133. package/packages/pi-agent-core/dist/agent-loop.js.map +1 -1
  134. package/packages/pi-agent-core/package.json +1 -1
  135. package/packages/pi-ai/dist/index.d.ts +1 -0
  136. package/packages/pi-ai/dist/index.d.ts.map +1 -1
  137. package/packages/pi-ai/dist/index.js +1 -0
  138. package/packages/pi-ai/dist/index.js.map +1 -1
  139. package/packages/pi-ai/dist/providers/transform-messages.d.ts.map +1 -1
  140. package/packages/pi-ai/dist/providers/transform-messages.js +7 -3
  141. package/packages/pi-ai/dist/providers/transform-messages.js.map +1 -1
  142. package/packages/pi-ai/dist/tool-result-content.d.ts +3 -0
  143. package/packages/pi-ai/dist/tool-result-content.d.ts.map +1 -0
  144. package/packages/pi-ai/dist/tool-result-content.js +62 -0
  145. package/packages/pi-ai/dist/tool-result-content.js.map +1 -0
  146. package/packages/pi-ai/dist/types.d.ts +8 -20
  147. package/packages/pi-ai/dist/types.d.ts.map +1 -1
  148. package/packages/pi-ai/dist/types.js.map +1 -1
  149. package/packages/pi-ai/package.json +1 -1
  150. package/packages/pi-coding-agent/dist/core/package-manager.d.ts +9 -0
  151. package/packages/pi-coding-agent/dist/core/package-manager.d.ts.map +1 -1
  152. package/packages/pi-coding-agent/dist/core/package-manager.js +74 -53
  153. package/packages/pi-coding-agent/dist/core/package-manager.js.map +1 -1
  154. package/packages/pi-coding-agent/dist/core/resource-loader.d.ts +6 -0
  155. package/packages/pi-coding-agent/dist/core/resource-loader.d.ts.map +1 -1
  156. package/packages/pi-coding-agent/dist/core/resource-loader.js +30 -2
  157. package/packages/pi-coding-agent/dist/core/resource-loader.js.map +1 -1
  158. package/packages/pi-coding-agent/dist/core/skill-directories.d.ts +66 -0
  159. package/packages/pi-coding-agent/dist/core/skill-directories.d.ts.map +1 -0
  160. package/packages/pi-coding-agent/dist/core/skill-directories.js +138 -0
  161. package/packages/pi-coding-agent/dist/core/skill-directories.js.map +1 -0
  162. package/packages/pi-coding-agent/dist/core/skills.d.ts +4 -1
  163. package/packages/pi-coding-agent/dist/core/skills.d.ts.map +1 -1
  164. package/packages/pi-coding-agent/dist/core/skills.js +4 -1
  165. package/packages/pi-coding-agent/dist/core/skills.js.map +1 -1
  166. package/packages/pi-coding-agent/dist/index.d.ts +1 -0
  167. package/packages/pi-coding-agent/dist/index.d.ts.map +1 -1
  168. package/packages/pi-coding-agent/dist/index.js +1 -0
  169. package/packages/pi-coding-agent/dist/index.js.map +1 -1
  170. package/packages/pi-coding-agent/package.json +7 -7
  171. package/packages/pi-tui/package.json +2 -2
  172. package/packages/rpc-client/package.json +2 -2
  173. package/pkg/dist/core/export-html/template.js +53 -4
  174. package/pkg/package.json +1 -1
  175. package/src/resources/extensions/ask-user-questions.ts +1 -1
  176. package/src/resources/extensions/gsd/auto/closeout.ts +114 -7
  177. package/src/resources/extensions/gsd/auto/orchestrator.ts +4 -3
  178. package/src/resources/extensions/gsd/auto/pre-dispatch.ts +6 -3
  179. package/src/resources/extensions/gsd/auto/unit-phase.ts +5 -9
  180. package/src/resources/extensions/gsd/auto-artifact-paths.ts +12 -3
  181. package/src/resources/extensions/gsd/auto-direct-dispatch.ts +13 -7
  182. package/src/resources/extensions/gsd/auto-dispatch.ts +18 -2
  183. package/src/resources/extensions/gsd/auto-recovery.ts +50 -6
  184. package/src/resources/extensions/gsd/auto-start.ts +5 -3
  185. package/src/resources/extensions/gsd/auto-worktree-branch-lifecycle.ts +237 -0
  186. package/src/resources/extensions/gsd/auto-worktree-cleanup.ts +166 -0
  187. package/src/resources/extensions/gsd/auto-worktree-conflict-auto-resolve.ts +37 -0
  188. package/src/resources/extensions/gsd/auto-worktree-creation.ts +171 -0
  189. package/src/resources/extensions/gsd/auto-worktree-entry.ts +154 -0
  190. package/src/resources/extensions/gsd/auto-worktree-merge-already-merged.ts +161 -0
  191. package/src/resources/extensions/gsd/auto-worktree-merge-branch-head.ts +83 -0
  192. package/src/resources/extensions/gsd/auto-worktree-merge-cleanup.ts +132 -0
  193. package/src/resources/extensions/gsd/auto-worktree-merge-code-changes.ts +71 -0
  194. package/src/resources/extensions/gsd/auto-worktree-merge-db-ready.ts +112 -0
  195. package/src/resources/extensions/gsd/auto-worktree-merge-integration-branch.ts +99 -0
  196. package/src/resources/extensions/gsd/auto-worktree-merge-message.ts +166 -0
  197. package/src/resources/extensions/gsd/auto-worktree-merge-pre-teardown.ts +93 -0
  198. package/src/resources/extensions/gsd/auto-worktree-merge-stash.ts +213 -0
  199. package/src/resources/extensions/gsd/auto-worktree-merge.ts +391 -0
  200. package/src/resources/extensions/gsd/auto-worktree-milestone-shelter.ts +159 -0
  201. package/src/resources/extensions/gsd/auto-worktree-path-resolution.ts +6 -0
  202. package/src/resources/extensions/gsd/auto-worktree-path-utils.ts +10 -0
  203. package/src/resources/extensions/gsd/auto-worktree-resource-version.ts +44 -0
  204. package/src/resources/extensions/gsd/auto-worktree-runtime-cleanup.ts +119 -0
  205. package/src/resources/extensions/gsd/auto-worktree-session-registry.ts +71 -0
  206. package/src/resources/extensions/gsd/auto-worktree-sync.ts +370 -0
  207. package/src/resources/extensions/gsd/auto-worktree-teardown.ts +161 -0
  208. package/src/resources/extensions/gsd/auto-worktree.ts +69 -2429
  209. package/src/resources/extensions/gsd/auto.ts +11 -17
  210. package/src/resources/extensions/gsd/bootstrap/dynamic-tools.ts +1 -1
  211. package/src/resources/extensions/gsd/bootstrap/register-hooks.ts +20 -2
  212. package/src/resources/extensions/gsd/bootstrap/system-context.ts +1 -1
  213. package/src/resources/extensions/gsd/bootstrap/tool-call-loop-guard.ts +218 -27
  214. package/src/resources/extensions/gsd/codebase-generator.ts +7 -1
  215. package/src/resources/extensions/gsd/commands-handlers.ts +1 -1
  216. package/src/resources/extensions/gsd/commands-workflow-templates.ts +3 -3
  217. package/src/resources/extensions/gsd/db/queries.ts +9 -9
  218. package/src/resources/extensions/gsd/db/writers/cascades.ts +23 -1
  219. package/src/resources/extensions/gsd/doctor-engine-checks.ts +109 -10
  220. package/src/resources/extensions/gsd/flat-phase-migration.ts +34 -3
  221. package/src/resources/extensions/gsd/forensics.ts +1 -1
  222. package/src/resources/extensions/gsd/git-conflict-resolve.ts +2 -2
  223. package/src/resources/extensions/gsd/git-service.ts +53 -0
  224. package/src/resources/extensions/gsd/gsd-db.ts +33 -11
  225. package/src/resources/extensions/gsd/guided-flow.ts +26 -18
  226. package/src/resources/extensions/gsd/markdown-renderer.ts +15 -3
  227. package/src/resources/extensions/gsd/md-importer.ts +93 -22
  228. package/src/resources/extensions/gsd/migration-auto-check.ts +32 -3
  229. package/src/resources/extensions/gsd/milestone-merge-transaction.ts +15 -0
  230. package/src/resources/extensions/gsd/milestone-settlement.ts +1 -1
  231. package/src/resources/extensions/gsd/parallel-merge.ts +1 -1
  232. package/src/resources/extensions/gsd/parallel-orchestrator.ts +3 -1
  233. package/src/resources/extensions/gsd/paths.ts +21 -3
  234. package/src/resources/extensions/gsd/preferences-skills.ts +10 -16
  235. package/src/resources/extensions/gsd/preferences-types.ts +30 -0
  236. package/src/resources/extensions/gsd/preferences-validation.ts +69 -0
  237. package/src/resources/extensions/gsd/preferences.ts +20 -0
  238. package/src/resources/extensions/gsd/prompts/system.md +6 -6
  239. package/src/resources/extensions/gsd/quick.ts +11 -3
  240. package/src/resources/extensions/gsd/skill-discovery.ts +47 -23
  241. package/src/resources/extensions/gsd/slice-parallel-orchestrator.ts +3 -1
  242. package/src/resources/extensions/gsd/state/derive/from-db.ts +201 -157
  243. package/src/resources/extensions/gsd/tests/auto-artifact-paths.test.ts +169 -1
  244. package/src/resources/extensions/gsd/tests/auto-loop-no-copy-artifacts.test.ts +2 -1
  245. package/src/resources/extensions/gsd/tests/auto-recovery.test.ts +57 -0
  246. package/src/resources/extensions/gsd/tests/auto-start-clean-runtime-db-gated.test.ts +1 -1
  247. package/src/resources/extensions/gsd/tests/auto-warning-noise-regression.test.ts +2 -1
  248. package/src/resources/extensions/gsd/tests/auto-worktree-auto-resolve.test.ts +1 -1
  249. package/src/resources/extensions/gsd/tests/auto-worktree-merge-already-merged.test.ts +177 -0
  250. package/src/resources/extensions/gsd/tests/auto-worktree-merge-branch-head.test.ts +118 -0
  251. package/src/resources/extensions/gsd/tests/auto-worktree-merge-cleanup.test.ts +115 -0
  252. package/src/resources/extensions/gsd/tests/auto-worktree-merge-code-changes.test.ts +76 -0
  253. package/src/resources/extensions/gsd/tests/auto-worktree-merge-db-ready.test.ts +140 -0
  254. package/src/resources/extensions/gsd/tests/auto-worktree-merge-integration-branch.test.ts +123 -0
  255. package/src/resources/extensions/gsd/tests/auto-worktree-merge-message.test.ts +114 -0
  256. package/src/resources/extensions/gsd/tests/auto-worktree-merge-pre-teardown.test.ts +84 -0
  257. package/src/resources/extensions/gsd/tests/auto-worktree-merge-stash.test.ts +70 -0
  258. package/src/resources/extensions/gsd/tests/auto-worktree-milestone-shelter.test.ts +52 -0
  259. package/src/resources/extensions/gsd/tests/auto-worktree-registry.test.ts +6 -6
  260. package/src/resources/extensions/gsd/tests/auto-worktree-resource-version.test.ts +59 -0
  261. package/src/resources/extensions/gsd/tests/auto-worktree-untracked-content.test.ts +3 -1
  262. package/src/resources/extensions/gsd/tests/codebase-generator.test.ts +22 -0
  263. package/src/resources/extensions/gsd/tests/completed-units-metrics-sync.test.ts +1 -1
  264. package/src/resources/extensions/gsd/tests/derive-state-db.test.ts +4 -0
  265. package/src/resources/extensions/gsd/tests/doctor-scope-db-unavailable.test.ts +174 -0
  266. package/src/resources/extensions/gsd/tests/fast-forward-reused-milestone-branch.test.ts +1 -1
  267. package/src/resources/extensions/gsd/tests/flat-phase-migration.test.ts +71 -1
  268. package/src/resources/extensions/gsd/tests/flat-phase-renderer.test.ts +25 -1
  269. package/src/resources/extensions/gsd/tests/gsd-db.test.ts +40 -0
  270. package/src/resources/extensions/gsd/tests/import-completion-lost-update.test.ts +196 -0
  271. package/src/resources/extensions/gsd/tests/integration/all-milestones-complete-merge.test.ts +4 -6
  272. package/src/resources/extensions/gsd/tests/integration/auto-stash-merge.test.ts +2 -1
  273. package/src/resources/extensions/gsd/tests/integration/auto-worktree-milestone-merge.test.ts +3 -3
  274. package/src/resources/extensions/gsd/tests/integration/auto-worktree.test.ts +7 -8
  275. package/src/resources/extensions/gsd/tests/integration/feature-branch-lifecycle-integration.test.ts +2 -5
  276. package/src/resources/extensions/gsd/tests/integration/git-service.test.ts +76 -0
  277. package/src/resources/extensions/gsd/tests/integration/merge-cwd-restore.test.ts +1 -1
  278. package/src/resources/extensions/gsd/tests/integration/merge-preserve-worktree-on-preteardown-error.test.ts +1 -1
  279. package/src/resources/extensions/gsd/tests/integration/merge-strategy-regular.test.ts +2 -1
  280. package/src/resources/extensions/gsd/tests/integration/milestone-transition-worktree.test.ts +5 -7
  281. package/src/resources/extensions/gsd/tests/integration/workspace-collapse-integration.test.ts +2 -4
  282. package/src/resources/extensions/gsd/tests/journal-integration.test.ts +16 -15
  283. package/src/resources/extensions/gsd/tests/merge-closeout-consistency-gate.test.ts +1 -1
  284. package/src/resources/extensions/gsd/tests/merge-db-cycle.test.ts +1 -1
  285. package/src/resources/extensions/gsd/tests/merge-self-branch-guard.test.ts +1 -1
  286. package/src/resources/extensions/gsd/tests/migration-auto-check.test.ts +91 -0
  287. package/src/resources/extensions/gsd/tests/milestone-merge-stash-restore.test.ts +439 -0
  288. package/src/resources/extensions/gsd/tests/milestone-merge-transaction.test.ts +24 -0
  289. package/src/resources/extensions/gsd/tests/none-mode-gates.test.ts +1 -1
  290. package/src/resources/extensions/gsd/tests/originalbase-path-comparison.test.ts +3 -3
  291. package/src/resources/extensions/gsd/tests/parallel-worker-lock-contention.test.ts +1 -1
  292. package/src/resources/extensions/gsd/tests/preferences-worktree-sync.test.ts +1 -4
  293. package/src/resources/extensions/gsd/tests/preferences.test.ts +19 -0
  294. package/src/resources/extensions/gsd/tests/prefs-wizard-coverage.test.ts +5 -0
  295. package/src/resources/extensions/gsd/tests/reopen-slice.test.ts +52 -0
  296. package/src/resources/extensions/gsd/tests/runtime-invariant-modules.test.ts +20 -0
  297. package/src/resources/extensions/gsd/tests/skill-discovery.test.ts +46 -0
  298. package/src/resources/extensions/gsd/tests/stale-worktree-cwd.test.ts +3 -5
  299. package/src/resources/extensions/gsd/tests/stash-pop-gsd-conflict.test.ts +2 -1
  300. package/src/resources/extensions/gsd/tests/stash-queued-context-files.test.ts +2 -1
  301. package/src/resources/extensions/gsd/tests/steer-worktree-path.test.ts +1 -1
  302. package/src/resources/extensions/gsd/tests/sync-worktree-skip-current.test.ts +1 -1
  303. package/src/resources/extensions/gsd/tests/system-flat-phase-layout.test.ts +33 -0
  304. package/src/resources/extensions/gsd/tests/teardown-chdir-failure-clears-registry.test.ts +4 -4
  305. package/src/resources/extensions/gsd/tests/teardown-cleanup-parity.test.ts +2 -1
  306. package/src/resources/extensions/gsd/tests/teardown-failure-clears-registry.test.ts +4 -4
  307. package/src/resources/extensions/gsd/tests/tool-call-loop-guard.test.ts +196 -0
  308. package/src/resources/extensions/gsd/tests/uat-stuck-loop-orphaned-worktree.test.ts +1 -1
  309. package/src/resources/extensions/gsd/tests/unit-registry.test.ts +26 -1
  310. package/src/resources/extensions/gsd/tests/worktree-db-integration.test.ts +1 -1
  311. package/src/resources/extensions/gsd/tests/worktree-db-respawn-truncation.test.ts +1 -1
  312. package/src/resources/extensions/gsd/tests/worktree-db-same-file.test.ts +1 -3
  313. package/src/resources/extensions/gsd/tests/worktree-expected-warnings.test.ts +1 -1
  314. package/src/resources/extensions/gsd/tests/worktree-git-pathspec.test.ts +1 -1
  315. package/src/resources/extensions/gsd/tests/worktree-main-branch.test.ts +1 -1
  316. package/src/resources/extensions/gsd/tests/worktree-preferences-sync.test.ts +1 -1
  317. package/src/resources/extensions/gsd/tests/worktree-reentry.test.ts +2 -1
  318. package/src/resources/extensions/gsd/tests/worktree-sync-milestones.test.ts +5 -2
  319. package/src/resources/extensions/gsd/tests/worktree-sync-overwrite-loop.test.ts +1 -1
  320. package/src/resources/extensions/gsd/tests/worktree-sync-tasks.test.ts +1 -1
  321. package/src/resources/extensions/gsd/tool-contract.ts +29 -0
  322. package/src/resources/extensions/gsd/tools/reopen-slice.ts +5 -0
  323. package/src/resources/extensions/gsd/tools/reopen-task.ts +13 -5
  324. package/src/resources/extensions/gsd/unit-registry.ts +16 -2
  325. package/src/resources/extensions/gsd/unmerged-milestone-guard.ts +1 -1
  326. package/src/resources/extensions/gsd/validation-block-guard.ts +2 -1
  327. package/src/resources/extensions/gsd/workspace-git-preflight.ts +2 -2
  328. package/src/resources/extensions/gsd/worktree-command.ts +1 -1
  329. package/src/resources/extensions/gsd/worktree-lifecycle.ts +19 -15
  330. package/src/resources/extensions/gsd/worktree-reentry.ts +2 -1
  331. package/src/resources/extensions/shared/interview-ui.ts +58 -11
  332. package/src/resources/extensions/shared/tests/interview-ui-border.test.ts +32 -0
  333. /package/dist/web/standalone/.next/static/{L2UMqXELiDH3oz-7yguXp → gXoWec4UNqcR0tkuHwYCr}/_buildManifest.js +0 -0
  334. /package/dist/web/standalone/.next/static/{L2UMqXELiDH3oz-7yguXp → gXoWec4UNqcR0tkuHwYCr}/_ssgManifest.js +0 -0
@@ -9,8 +9,8 @@
9
9
  */
10
10
 
11
11
  import { existsSync, readdirSync, readFileSync, type Dirent } from "node:fs";
12
- import { homedir } from "node:os";
13
12
  import { join } from "node:path";
13
+ import { getSkillDirectories } from "@gsd/pi-coding-agent";
14
14
  import { gsdHome } from "./gsd-home.js";
15
15
  import { getInstalledSkills, normalizeSkillName } from "./skills.js";
16
16
 
@@ -20,28 +20,47 @@ export interface DiscoveredSkill {
20
20
  location: string;
21
21
  }
22
22
 
23
- /** Snapshot of normalized skill names at auto-mode start */
24
- let baselineSkills: Set<string> | null = null;
23
+ /**
24
+ * Snapshot state. `searchDirs` is captured at snapshot time so every detection
25
+ * pass during the session scans the same project + user dirs (the bug fix:
26
+ * previously only user dirs were scanned, so skills installed into the project
27
+ * mid-session were silently missed).
28
+ */
29
+ interface SkillSnapshotState {
30
+ baselineNames: Set<string>;
31
+ searchDirs: string[];
32
+ }
33
+
34
+ let snapshot: SkillSnapshotState | null = null;
25
35
 
26
36
  /**
27
37
  * Snapshot the current installed skill catalog. Call at auto-mode start.
38
+ * `cwd` is the project base path; skills dropped into `<cwd>/.agents/skills/`,
39
+ * `<cwd>/.claude/skills/`, or `<cwd>/.gsd/skills/` mid-session are detected.
28
40
  */
29
- export function snapshotSkills(): void {
30
- baselineSkills = snapshotCurrentSkillNames();
41
+ export function snapshotSkills(options?: { cwd?: string }): void {
42
+ const searchDirs = getSkillDirectories({
43
+ cwd: options?.cwd ?? process.cwd(),
44
+ gsdHome: gsdHome(),
45
+ }).map((entry) => entry.path);
46
+ snapshot = {
47
+ baselineNames: snapshotCurrentSkillNames(searchDirs),
48
+ searchDirs,
49
+ };
31
50
  }
32
51
 
33
52
  /**
34
53
  * Clear the snapshot. Call when auto-mode stops.
35
54
  */
36
55
  export function clearSkillSnapshot(): void {
37
- baselineSkills = null;
56
+ snapshot = null;
38
57
  }
39
58
 
40
59
  /**
41
60
  * Check if a snapshot is active (auto-mode is running with discovery).
42
61
  */
43
62
  export function hasSkillSnapshot(): boolean {
44
- return baselineSkills !== null;
63
+ return snapshot !== null;
45
64
  }
46
65
 
47
66
  /**
@@ -49,12 +68,12 @@ export function hasSkillSnapshot(): boolean {
49
68
  * Returns skill metadata for any new skills found in the loader catalog or on disk.
50
69
  */
51
70
  export function detectNewSkills(): DiscoveredSkill[] {
52
- if (!baselineSkills) return [];
71
+ if (!snapshot) return [];
53
72
 
54
73
  const newSkills: DiscoveredSkill[] = [];
55
- for (const skill of getCurrentSkillsForDiscovery()) {
74
+ for (const skill of getCurrentSkillsForDiscovery(snapshot.searchDirs)) {
56
75
  const normalized = normalizeSkillName(skill.name);
57
- if (baselineSkills.has(normalized)) continue;
76
+ if (snapshot.baselineNames.has(normalized)) continue;
58
77
  newSkills.push(skill);
59
78
  }
60
79
 
@@ -84,7 +103,13 @@ export async function refreshCatalogForNewSkills(options?: {
84
103
  }
85
104
  }
86
105
 
87
- snapshotSkills();
106
+ // Re-snapshot, preserving the search dirs captured at auto-mode start.
107
+ if (snapshot) {
108
+ snapshot = {
109
+ baselineNames: snapshotCurrentSkillNames(),
110
+ searchDirs: snapshot.searchDirs,
111
+ };
112
+ }
88
113
  const names = newSkills.map((skill) => skill.name).join(", ");
89
114
  options?.notify?.(`GSD: loaded new skills: ${names}`, "info");
90
115
  return newSkills;
@@ -132,19 +157,18 @@ function escapeXml(value: string): string {
132
157
  });
133
158
  }
134
159
 
135
- function skillSearchDirs(): string[] {
136
- return [
137
- join(gsdHome(), "agent", "skills"),
138
- join(homedir(), ".agents", "skills"),
139
- join(homedir(), ".claude", "skills"),
140
- ];
160
+ function snapshotCurrentSkillNames(searchDirsOverride?: string[]): Set<string> {
161
+ // Uses explicit dirs, the snapshot's search dirs when active, or user-only dirs
162
+ // (no project context) when called before a snapshot is taken.
163
+ const searchDirs = searchDirsOverride ?? snapshot?.searchDirs ?? defaultSearchDirs();
164
+ return new Set(getCurrentSkillsForDiscovery(searchDirs).map(skill => normalizeSkillName(skill.name)));
141
165
  }
142
166
 
143
- function snapshotCurrentSkillNames(): Set<string> {
144
- return new Set(getCurrentSkillsForDiscovery().map(skill => normalizeSkillName(skill.name)));
167
+ function defaultSearchDirs(): string[] {
168
+ return getSkillDirectories({ cwd: process.cwd(), gsdHome: gsdHome() }).map((entry) => entry.path);
145
169
  }
146
170
 
147
- function getCurrentSkillsForDiscovery(): DiscoveredSkill[] {
171
+ function getCurrentSkillsForDiscovery(searchDirs: string[]): DiscoveredSkill[] {
148
172
  const skills = new Map<string, DiscoveredSkill>();
149
173
  for (const skill of getInstalledSkills()) {
150
174
  const normalized = normalizeSkillName(skill.name);
@@ -155,7 +179,7 @@ function getCurrentSkillsForDiscovery(): DiscoveredSkill[] {
155
179
  });
156
180
  }
157
181
 
158
- for (const skill of getDiskSkills()) {
182
+ for (const skill of getDiskSkills(searchDirs)) {
159
183
  const normalized = normalizeSkillName(skill.name);
160
184
  if (!skills.has(normalized)) skills.set(normalized, skill);
161
185
  }
@@ -163,9 +187,9 @@ function getCurrentSkillsForDiscovery(): DiscoveredSkill[] {
163
187
  return [...skills.values()];
164
188
  }
165
189
 
166
- function getDiskSkills(): DiscoveredSkill[] {
190
+ function getDiskSkills(searchDirs: string[]): DiscoveredSkill[] {
167
191
  const skills = new Map<string, DiscoveredSkill>();
168
- for (const dir of skillSearchDirs()) {
192
+ for (const dir of searchDirs) {
169
193
  if (!existsSync(dir)) continue;
170
194
  let entries: Dirent[];
171
195
  try {
@@ -30,7 +30,9 @@ import { isAbsolute, join, dirname, resolve } from "node:path";
30
30
  import { fileURLToPath } from "node:url";
31
31
  import { gsdRoot } from "./paths.js";
32
32
  import { createWorktree, worktreePath, removeWorktree } from "./worktree-manager.js";
33
- import { autoWorktreeBranch, runWorktreePostCreateHook, syncGsdStateToWorktreeByScope } from "./auto-worktree.js";
33
+ import { autoWorktreeBranch } from "./auto-worktree-branch-lifecycle.js";
34
+ import { syncGsdStateToWorktreeByScope } from "./auto-worktree-sync.js";
35
+ import { runWorktreePostCreateHook } from "./worktree-post-create-hook.js";
34
36
  import { createWorkspace, scopeMilestone } from "./workspace.js";
35
37
  import {
36
38
  writeSessionStatus,
@@ -1,7 +1,7 @@
1
1
  // Project/App: gsd-pi
2
2
  // File Purpose: DB-backed GSD state derivation pipeline stage.
3
3
 
4
- import type { ActiveRef, GSDState, MilestoneRegistryEntry } from '../../types.js';
4
+ import type { ActiveRef, GSDState, MilestoneRegistryEntry, Phase } from '../../types.js';
5
5
  import { join } from 'node:path';
6
6
  import { existsSync } from 'node:fs';
7
7
  import { isClosedStatus, isDeferredStatus } from '../../status-guards.js';
@@ -41,6 +41,59 @@ import {
41
41
 
42
42
  const isStatusDone = isClosedStatus;
43
43
 
44
+ type MilestoneProgress = { done: number; total: number };
45
+ type SliceProgress = { done: number; total: number };
46
+ type TaskProgress = { done: number; total: number };
47
+
48
+ interface DerivedStateContext {
49
+ activeMilestone: ActiveRef | null;
50
+ activeSlice?: ActiveRef | null;
51
+ activeTask?: ActiveRef | null;
52
+ registry: MilestoneRegistryEntry[];
53
+ requirements: GSDState["requirements"];
54
+ milestoneProgress: MilestoneProgress;
55
+ sliceProgress?: SliceProgress;
56
+ taskProgress?: TaskProgress;
57
+ }
58
+
59
+ interface DerivedStateOptions {
60
+ blockers?: string[];
61
+ lastCompletedMilestone?: ActiveRef | null;
62
+ includeActiveWorkspace?: boolean;
63
+ }
64
+
65
+ function buildProgress(context: DerivedStateContext): NonNullable<GSDState["progress"]> {
66
+ return {
67
+ milestones: context.milestoneProgress,
68
+ ...(context.sliceProgress ? { slices: context.sliceProgress } : {}),
69
+ ...(context.taskProgress ? { tasks: context.taskProgress } : {}),
70
+ };
71
+ }
72
+
73
+ function buildDerivedState(
74
+ context: DerivedStateContext,
75
+ phase: Phase,
76
+ nextAction: string,
77
+ options: DerivedStateOptions = {},
78
+ ): GSDState {
79
+ return {
80
+ activeMilestone: context.activeMilestone,
81
+ activeSlice: context.activeSlice ?? null,
82
+ activeTask: context.activeTask ?? null,
83
+ phase,
84
+ recentDecisions: [],
85
+ blockers: options.blockers ?? [],
86
+ nextAction,
87
+ ...(options.lastCompletedMilestone !== undefined
88
+ ? { lastCompletedMilestone: options.lastCompletedMilestone }
89
+ : {}),
90
+ ...(options.includeActiveWorkspace ? { activeWorkspace: undefined } : {}),
91
+ registry: context.registry,
92
+ requirements: context.requirements,
93
+ progress: buildProgress(context),
94
+ };
95
+ }
96
+
44
97
  function stripMilestonePrefix(title: string): string {
45
98
  return title.replace(/^M\d+(?:-[a-z0-9]{6})?[^:]*:\s*/, '') || title;
46
99
  }
@@ -182,58 +235,47 @@ function handleNoActiveMilestone(
182
235
  const pendingEntries = registry.filter(e => e.status === 'pending');
183
236
  const parkedEntries = registry.filter(e => e.status === 'parked');
184
237
 
238
+ const context: DerivedStateContext = {
239
+ activeMilestone: null,
240
+ registry,
241
+ requirements,
242
+ milestoneProgress,
243
+ };
244
+
185
245
  if (pendingEntries.length > 0) {
186
246
  const blockerDetails = pendingEntries
187
247
  .filter(e => e.dependsOn && e.dependsOn.length > 0)
188
248
  .map(e => `${e.id} is waiting on unmet deps: ${e.dependsOn!.join(', ')}`);
189
- return {
190
- activeMilestone: null, activeSlice: null, activeTask: null,
191
- phase: 'blocked',
192
- recentDecisions: [], blockers: blockerDetails.length > 0
249
+ return buildDerivedState(context, 'blocked', 'Resolve milestone dependencies before proceeding.', {
250
+ blockers: blockerDetails.length > 0
193
251
  ? blockerDetails
194
252
  : ['All remaining milestones are dep-blocked but no deps listed — check CONTEXT.md files'],
195
- nextAction: 'Resolve milestone dependencies before proceeding.',
196
- registry, requirements,
197
- progress: { milestones: milestoneProgress },
198
- };
253
+ });
199
254
  }
200
255
 
201
256
  if (parkedEntries.length > 0) {
202
257
  const parkedIds = parkedEntries.map(e => e.id).join(', ');
203
- return {
204
- activeMilestone: null, activeSlice: null, activeTask: null,
205
- phase: 'pre-planning',
206
- recentDecisions: [], blockers: [],
207
- nextAction: `All remaining milestones are parked (${parkedIds}). Run /gsd unpark <id> or create a new milestone.`,
208
- registry, requirements,
209
- progress: { milestones: milestoneProgress },
210
- };
258
+ return buildDerivedState(
259
+ context,
260
+ 'pre-planning',
261
+ `All remaining milestones are parked (${parkedIds}). Run /gsd unpark <id> or create a new milestone.`,
262
+ );
211
263
  }
212
264
 
213
265
  if (registry.length === 0) {
214
- return {
215
- activeMilestone: null, activeSlice: null, activeTask: null,
216
- phase: 'pre-planning',
217
- recentDecisions: [], blockers: [],
218
- nextAction: 'No milestones found. Run /gsd to create one.',
219
- registry: [], requirements,
220
- progress: { milestones: { done: 0, total: 0 } },
221
- };
266
+ return buildDerivedState(
267
+ { ...context, registry: [], milestoneProgress: { done: 0, total: 0 } },
268
+ 'pre-planning',
269
+ 'No milestones found. Run /gsd to create one.',
270
+ );
222
271
  }
223
272
 
224
273
  const lastEntry = registry[registry.length - 1];
225
274
  const unmappedActive = countUnmappedActiveRequirements();
226
275
  const completionNote = formatCompletePhaseNextAction(unmappedActive);
227
- return {
228
- activeMilestone: null,
276
+ return buildDerivedState(context, 'complete', completionNote, {
229
277
  lastCompletedMilestone: lastEntry ? { id: lastEntry.id, title: lastEntry.title } : null,
230
- activeSlice: null, activeTask: null,
231
- phase: 'complete',
232
- recentDecisions: [], blockers: [],
233
- nextAction: completionNote,
234
- registry, requirements,
235
- progress: { milestones: milestoneProgress },
236
- };
278
+ });
237
279
  }
238
280
 
239
281
  async function handleAllSlicesDone(
@@ -248,52 +290,48 @@ async function handleAllSlicesDone(
248
290
  const verdict = typeof validation?.status === "string" ? validation.status : undefined;
249
291
  const validationTerminal = verdict != null && verdict !== "";
250
292
 
293
+ const context: DerivedStateContext = {
294
+ activeMilestone,
295
+ registry,
296
+ requirements,
297
+ milestoneProgress,
298
+ sliceProgress,
299
+ };
300
+
251
301
  if (!validationTerminal) {
252
- return {
253
- activeMilestone, activeSlice: null, activeTask: null,
254
- phase: 'validating-milestone',
255
- recentDecisions: [], blockers: [],
256
- nextAction: `Validate milestone ${activeMilestone.id} before completion.`,
257
- registry, requirements,
258
- progress: { milestones: milestoneProgress, slices: sliceProgress },
259
- };
302
+ return buildDerivedState(
303
+ context,
304
+ 'validating-milestone',
305
+ `Validate milestone ${activeMilestone.id} before completion.`,
306
+ );
260
307
  }
261
308
 
262
309
  // All roadmap slices are done (enforced by caller) and verdict is
263
310
  // needs-remediation — remediation cannot progress without new slices.
264
311
  // Return blocked instead of re-dispatching validate-milestone (#4506).
265
312
  if (verdict === 'needs-attention') {
266
- return {
267
- activeMilestone, activeSlice: null, activeTask: null,
268
- phase: 'blocked',
269
- recentDecisions: [],
270
- blockers: [formatNeedsAttentionBlocker(activeMilestone.id)],
271
- nextAction: `Resolve ${activeMilestone.id} validation attention before proceeding.`,
272
- registry, requirements,
273
- progress: { milestones: milestoneProgress, slices: sliceProgress },
274
- };
313
+ return buildDerivedState(
314
+ context,
315
+ 'blocked',
316
+ `Resolve ${activeMilestone.id} validation attention before proceeding.`,
317
+ { blockers: [formatNeedsAttentionBlocker(activeMilestone.id)] },
318
+ );
275
319
  }
276
320
 
277
321
  if (verdict === 'needs-remediation') {
278
- return {
279
- activeMilestone, activeSlice: null, activeTask: null,
280
- phase: 'blocked',
281
- recentDecisions: [],
282
- blockers: [formatNeedsRemediationBlocker(activeMilestone.id)],
283
- nextAction: `Resolve ${activeMilestone.id} remediation before proceeding.`,
284
- registry, requirements,
285
- progress: { milestones: milestoneProgress, slices: sliceProgress },
286
- };
322
+ return buildDerivedState(
323
+ context,
324
+ 'blocked',
325
+ `Resolve ${activeMilestone.id} remediation before proceeding.`,
326
+ { blockers: [formatNeedsRemediationBlocker(activeMilestone.id)] },
327
+ );
287
328
  }
288
329
 
289
- return {
290
- activeMilestone, activeSlice: null, activeTask: null,
291
- phase: 'completing-milestone',
292
- recentDecisions: [], blockers: [],
293
- nextAction: `All slices complete in ${activeMilestone.id}. Write milestone summary.`,
294
- registry, requirements,
295
- progress: { milestones: milestoneProgress, slices: sliceProgress },
296
- };
330
+ return buildDerivedState(
331
+ context,
332
+ 'completing-milestone',
333
+ `All slices complete in ${activeMilestone.id}. Write milestone summary.`,
334
+ );
297
335
  }
298
336
 
299
337
  function resolveSliceDependencies(activeMilestoneSlices: SliceRow[]): { activeSlice: ActiveRef | null, activeSliceRow: SliceRow | null } {
@@ -356,13 +394,16 @@ export async function deriveStateFromDb(
356
394
  : allMilestones;
357
395
 
358
396
  if (milestones.length === 0) {
359
- return {
360
- activeMilestone: null, activeSlice: null, activeTask: null,
361
- phase: 'pre-planning', recentDecisions: [], blockers: [],
362
- nextAction: 'No milestones found. Run /gsd to create one.',
363
- registry: [], requirements,
364
- progress: { milestones: { done: 0, total: 0 } },
365
- };
397
+ return buildDerivedState(
398
+ {
399
+ activeMilestone: null,
400
+ registry: [],
401
+ requirements,
402
+ milestoneProgress: { done: 0, total: 0 },
403
+ },
404
+ 'pre-planning',
405
+ 'No milestones found. Run /gsd to create one.',
406
+ );
366
407
  }
367
408
 
368
409
  const { completeMilestoneIds, parkedMilestoneIds } = buildCompletenessSet(basePath, milestones);
@@ -384,12 +425,11 @@ export async function deriveStateFromDb(
384
425
  const nextAction = activeMilestoneHasDraft
385
426
  ? `Discuss draft context for milestone ${activeMilestone.id}.`
386
427
  : `Plan milestone ${activeMilestone.id}.`;
387
- return {
388
- activeMilestone, activeSlice: null, activeTask: null,
389
- phase, recentDecisions: [], blockers: [],
390
- nextAction, registry, requirements,
391
- progress: { milestones: milestoneProgress },
392
- };
428
+ return buildDerivedState(
429
+ { activeMilestone, registry, requirements, milestoneProgress },
430
+ phase,
431
+ nextAction,
432
+ );
393
433
  }
394
434
 
395
435
  const allSlicesDone = activeMilestoneSlices.every(s => isStatusDone(s.status));
@@ -397,6 +437,13 @@ export async function deriveStateFromDb(
397
437
  done: activeMilestoneSlices.filter(s => isStatusDone(s.status)).length,
398
438
  total: activeMilestoneSlices.length,
399
439
  };
440
+ const sliceStateContext: DerivedStateContext = {
441
+ activeMilestone,
442
+ registry,
443
+ requirements,
444
+ milestoneProgress,
445
+ sliceProgress,
446
+ };
400
447
 
401
448
  if (allSlicesDone) {
402
449
  return handleAllSlicesDone(basePath, activeMilestone, registry, requirements, milestoneProgress, sliceProgress);
@@ -407,21 +454,19 @@ export async function deriveStateFromDb(
407
454
  // If locked slice wasn't found, it returns null but logs warning, we need to return 'blocked'
408
455
  const sliceLock = process.env.GSD_PARALLEL_WORKER ? process.env.GSD_SLICE_LOCK : undefined;
409
456
  if (sliceLock) {
410
- return {
411
- activeMilestone, activeSlice: null, activeTask: null,
412
- phase: 'blocked', recentDecisions: [], blockers: [`GSD_SLICE_LOCK=${sliceLock} not found in active milestone slices`],
413
- nextAction: 'Slice lock references a non-existent slice — check orchestrator dispatch.',
414
- registry, requirements,
415
- progress: { milestones: milestoneProgress, slices: sliceProgress },
416
- };
457
+ return buildDerivedState(
458
+ sliceStateContext,
459
+ 'blocked',
460
+ 'Slice lock references a non-existent slice — check orchestrator dispatch.',
461
+ { blockers: [`GSD_SLICE_LOCK=${sliceLock} not found in active milestone slices`] },
462
+ );
417
463
  }
418
- return {
419
- activeMilestone, activeSlice: null, activeTask: null,
420
- phase: 'blocked', recentDecisions: [], blockers: ['No slice eligible — check dependency ordering'],
421
- nextAction: 'Resolve dependency blockers or plan next slice.',
422
- registry, requirements,
423
- progress: { milestones: milestoneProgress, slices: sliceProgress },
424
- };
464
+ return buildDerivedState(
465
+ sliceStateContext,
466
+ 'blocked',
467
+ 'Resolve dependency blockers or plan next slice.',
468
+ { blockers: ['No slice eligible — check dependency ordering'] },
469
+ );
425
470
  }
426
471
  const { activeSlice } = activeSliceContext;
427
472
  const activeSliceRow = activeSliceContext.activeSliceRow;
@@ -432,13 +477,11 @@ export async function deriveStateFromDb(
432
477
  // PLAN.md and preference flags are projections/configuration and are
433
478
  // deliberately not used to infer whether the slice itself is a sketch.
434
479
  if (activeSliceRow?.is_sketch === 1) {
435
- return {
436
- activeMilestone, activeSlice, activeTask: null,
437
- phase: 'refining', recentDecisions: [], blockers: [],
438
- nextAction: `Refine sketch slice ${activeSlice.id} (${activeSlice.title}) using prior slice context.`,
439
- registry, requirements,
440
- progress: { milestones: milestoneProgress, slices: sliceProgress },
441
- };
480
+ return buildDerivedState(
481
+ { ...sliceStateContext, activeSlice },
482
+ 'refining',
483
+ `Refine sketch slice ${activeSlice.id} (${activeSlice.title}) using prior slice context.`,
484
+ );
442
485
  }
443
486
 
444
487
  const tasks = getSliceTasks(activeMilestone.id, activeSlice.id);
@@ -447,30 +490,35 @@ export async function deriveStateFromDb(
447
490
  done: tasks.filter(t => isStatusDone(t.status)).length,
448
491
  total: tasks.length,
449
492
  };
493
+ const taskStateContext: DerivedStateContext = {
494
+ ...sliceStateContext,
495
+ activeSlice,
496
+ taskProgress,
497
+ };
450
498
 
451
499
  const activeTaskRow = tasks.find(t => !isStatusDone(t.status));
452
500
 
453
501
  if (!activeTaskRow && tasks.length > 0) {
454
- return {
455
- activeMilestone, activeSlice, activeTask: null,
456
- phase: 'summarizing', recentDecisions: [], blockers: [],
457
- nextAction: `All tasks done in ${activeSlice.id}. Write slice summary and complete slice.`,
458
- registry, requirements,
459
- progress: { milestones: milestoneProgress, slices: sliceProgress, tasks: taskProgress },
460
- };
502
+ return buildDerivedState(
503
+ taskStateContext,
504
+ 'summarizing',
505
+ `All tasks done in ${activeSlice.id}. Write slice summary and complete slice.`,
506
+ );
461
507
  }
462
508
 
463
509
  if (!activeTaskRow) {
464
- return {
465
- activeMilestone, activeSlice, activeTask: null,
466
- phase: 'planning', recentDecisions: [], blockers: [],
467
- nextAction: `Slice ${activeSlice.id} has no DB tasks. Plan slice tasks before execution.`,
468
- registry, requirements,
469
- progress: { milestones: milestoneProgress, slices: sliceProgress, tasks: taskProgress },
470
- };
510
+ return buildDerivedState(
511
+ taskStateContext,
512
+ 'planning',
513
+ `Slice ${activeSlice.id} has no DB tasks. Plan slice tasks before execution.`,
514
+ );
471
515
  }
472
516
 
473
517
  const activeTask: ActiveRef = { id: activeTaskRow.id, title: activeTaskRow.title };
518
+ const activeTaskStateContext: DerivedStateContext = {
519
+ ...taskStateContext,
520
+ activeTask,
521
+ };
474
522
 
475
523
  // ── Quality gate evaluation check ──────────────────────────────────
476
524
  // Pause before execution only when gates owned by the `gate-evaluate`
@@ -485,28 +533,26 @@ export async function deriveStateFromDb(
485
533
  "gate-evaluate",
486
534
  );
487
535
  if (pendingGateCount > 0) {
488
- return {
489
- activeMilestone, activeSlice, activeTask: null,
490
- phase: 'evaluating-gates', recentDecisions: [], blockers: [],
491
- nextAction: `Evaluate ${pendingGateCount} quality gate(s) for ${activeSlice.id} before execution.`,
492
- registry, requirements,
493
- progress: { milestones: milestoneProgress, slices: sliceProgress, tasks: taskProgress },
494
- };
536
+ return buildDerivedState(
537
+ taskStateContext,
538
+ 'evaluating-gates',
539
+ `Evaluate ${pendingGateCount} quality gate(s) for ${activeSlice.id} before execution.`,
540
+ );
495
541
  }
496
542
 
497
543
  const blockerTaskId = await detectBlockers(basePath, activeMilestone.id, activeSlice.id, tasks);
498
544
  if (blockerTaskId) {
499
545
  const replanHistory = getReplanHistory(activeMilestone.id, activeSlice.id);
500
546
  if (replanHistory.length === 0) {
501
- return {
502
- activeMilestone, activeSlice, activeTask,
503
- phase: 'replanning-slice', recentDecisions: [],
504
- blockers: [`Task ${blockerTaskId} discovered a blocker requiring slice replan`],
505
- nextAction: `Task ${blockerTaskId} reported blocker_discovered. Replan slice ${activeSlice.id} before continuing.`,
506
- activeWorkspace: undefined,
507
- registry, requirements,
508
- progress: { milestones: milestoneProgress, slices: sliceProgress, tasks: taskProgress },
509
- };
547
+ return buildDerivedState(
548
+ activeTaskStateContext,
549
+ 'replanning-slice',
550
+ `Task ${blockerTaskId} reported blocker_discovered. Replan slice ${activeSlice.id} before continuing.`,
551
+ {
552
+ blockers: [`Task ${blockerTaskId} discovered a blocker requiring slice replan`],
553
+ includeActiveWorkspace: true,
554
+ },
555
+ );
510
556
  }
511
557
  }
512
558
 
@@ -522,15 +568,15 @@ export async function deriveStateFromDb(
522
568
  // and the user's prior resolution never lands.
523
569
  const escalatingTaskId = detectPendingEscalation(tasks, basePath);
524
570
  if (escalatingTaskId) {
525
- return {
526
- activeMilestone, activeSlice, activeTask,
527
- phase: 'escalating-task', recentDecisions: [],
528
- blockers: [`Task ${escalatingTaskId} requires a user decision before the loop can proceed`],
529
- nextAction: `Run /gsd escalate show ${escalatingTaskId} to review, then /gsd escalate resolve ${escalatingTaskId} <choice> to proceed.`,
530
- activeWorkspace: undefined,
531
- registry, requirements,
532
- progress: { milestones: milestoneProgress, slices: sliceProgress, tasks: taskProgress },
533
- };
571
+ return buildDerivedState(
572
+ activeTaskStateContext,
573
+ 'escalating-task',
574
+ `Run /gsd escalate show ${escalatingTaskId} to review, then /gsd escalate resolve ${escalatingTaskId} <choice> to proceed.`,
575
+ {
576
+ blockers: [`Task ${escalatingTaskId} requires a user decision before the loop can proceed`],
577
+ includeActiveWorkspace: true,
578
+ },
579
+ );
534
580
  }
535
581
 
536
582
  if (!blockerTaskId) {
@@ -538,24 +584,22 @@ export async function deriveStateFromDb(
538
584
  if (isTriggered) {
539
585
  const replanHistory = getReplanHistory(activeMilestone.id, activeSlice.id);
540
586
  if (replanHistory.length === 0) {
541
- return {
542
- activeMilestone, activeSlice, activeTask,
543
- phase: 'replanning-slice', recentDecisions: [],
544
- blockers: ['Triage replan trigger detected slice replan required'],
545
- nextAction: `Triage replan triggered for slice ${activeSlice.id}. Replan before continuing.`,
546
- activeWorkspace: undefined,
547
- registry, requirements,
548
- progress: { milestones: milestoneProgress, slices: sliceProgress, tasks: taskProgress },
549
- };
587
+ return buildDerivedState(
588
+ activeTaskStateContext,
589
+ 'replanning-slice',
590
+ `Triage replan triggered for slice ${activeSlice.id}. Replan before continuing.`,
591
+ {
592
+ blockers: ['Triage replan trigger detected — slice replan required'],
593
+ includeActiveWorkspace: true,
594
+ },
595
+ );
550
596
  }
551
597
  }
552
598
  }
553
599
 
554
- return {
555
- activeMilestone, activeSlice, activeTask,
556
- phase: 'executing', recentDecisions: [], blockers: [],
557
- nextAction: `Execute ${activeTask.id}: ${activeTask.title} in slice ${activeSlice.id}.`,
558
- registry, requirements,
559
- progress: { milestones: milestoneProgress, slices: sliceProgress, tasks: taskProgress },
560
- };
600
+ return buildDerivedState(
601
+ activeTaskStateContext,
602
+ 'executing',
603
+ `Execute ${activeTask.id}: ${activeTask.title} in slice ${activeSlice.id}.`,
604
+ );
561
605
  }