gsd-pi 2.44.0-dev.d25d507 → 2.45.0-dev.1afbdaa

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 (407) hide show
  1. package/dist/help-text.js +1 -1
  2. package/dist/loader.js +34 -0
  3. package/dist/resources/extensions/gsd/activity-log.js +7 -0
  4. package/dist/resources/extensions/gsd/auto/infra-errors.js +3 -0
  5. package/dist/resources/extensions/gsd/auto/phases.js +63 -77
  6. package/dist/resources/extensions/gsd/auto/run-unit.js +6 -3
  7. package/dist/resources/extensions/gsd/auto/session.js +0 -11
  8. package/dist/resources/extensions/gsd/auto-artifact-paths.js +112 -0
  9. package/dist/resources/extensions/gsd/auto-post-unit.js +25 -96
  10. package/dist/resources/extensions/gsd/auto-prompts.js +24 -1
  11. package/dist/resources/extensions/gsd/auto-start.js +23 -5
  12. package/dist/resources/extensions/gsd/auto-timers.js +57 -3
  13. package/dist/resources/extensions/gsd/auto-worktree-sync.js +4 -0
  14. package/dist/resources/extensions/gsd/auto-worktree.js +14 -10
  15. package/dist/resources/extensions/gsd/auto.js +42 -60
  16. package/dist/resources/extensions/gsd/bootstrap/db-tools.js +170 -11
  17. package/dist/resources/extensions/gsd/bootstrap/register-hooks.js +18 -0
  18. package/dist/resources/extensions/gsd/commands/catalog.js +7 -1
  19. package/dist/resources/extensions/gsd/commands/context.js +0 -4
  20. package/dist/resources/extensions/gsd/commands/handlers/core.js +2 -0
  21. package/dist/resources/extensions/gsd/commands/handlers/ops.js +10 -0
  22. package/dist/resources/extensions/gsd/commands/handlers/parallel.js +1 -1
  23. package/dist/resources/extensions/gsd/commands-mcp-status.js +187 -0
  24. package/dist/resources/extensions/gsd/crash-recovery.js +2 -4
  25. package/dist/resources/extensions/gsd/dashboard-overlay.js +0 -44
  26. package/dist/resources/extensions/gsd/db-writer.js +40 -22
  27. package/dist/resources/extensions/gsd/doctor-checks.js +167 -2
  28. package/dist/resources/extensions/gsd/doctor.js +13 -3
  29. package/dist/resources/extensions/gsd/git-service.js +8 -3
  30. package/dist/resources/extensions/gsd/gsd-db.js +28 -4
  31. package/dist/resources/extensions/gsd/guided-flow.js +1 -2
  32. package/dist/resources/extensions/gsd/markdown-renderer.js +1 -1
  33. package/dist/resources/extensions/gsd/parallel-merge.js +1 -1
  34. package/dist/resources/extensions/gsd/parallel-orchestrator.js +5 -18
  35. package/dist/resources/extensions/gsd/preferences-types.js +2 -2
  36. package/dist/resources/extensions/gsd/preferences.js +8 -4
  37. package/dist/resources/extensions/gsd/prompts/complete-milestone.md +21 -10
  38. package/dist/resources/extensions/gsd/prompts/complete-slice.md +10 -23
  39. package/dist/resources/extensions/gsd/prompts/discuss.md +2 -2
  40. package/dist/resources/extensions/gsd/prompts/execute-task.md +5 -15
  41. package/dist/resources/extensions/gsd/prompts/guided-discuss-milestone.md +1 -1
  42. package/dist/resources/extensions/gsd/prompts/guided-discuss-slice.md +1 -1
  43. package/dist/resources/extensions/gsd/prompts/guided-plan-slice.md +1 -1
  44. package/dist/resources/extensions/gsd/prompts/guided-research-slice.md +1 -1
  45. package/dist/resources/extensions/gsd/prompts/plan-milestone.md +1 -1
  46. package/dist/resources/extensions/gsd/prompts/plan-slice.md +5 -3
  47. package/dist/resources/extensions/gsd/prompts/queue.md +2 -2
  48. package/dist/resources/extensions/gsd/prompts/quick-task.md +2 -0
  49. package/dist/resources/extensions/gsd/prompts/reactive-execute.md +1 -1
  50. package/dist/resources/extensions/gsd/prompts/reassess-roadmap.md +6 -6
  51. package/dist/resources/extensions/gsd/prompts/replan-slice.md +3 -14
  52. package/dist/resources/extensions/gsd/prompts/research-slice.md +3 -3
  53. package/dist/resources/extensions/gsd/prompts/rethink.md +83 -0
  54. package/dist/resources/extensions/gsd/prompts/system.md +1 -1
  55. package/dist/resources/extensions/gsd/prompts/validate-milestone.md +7 -37
  56. package/dist/resources/extensions/gsd/provider-error-pause.js +7 -0
  57. package/dist/resources/extensions/gsd/repo-identity.js +45 -7
  58. package/dist/resources/extensions/gsd/rethink.js +115 -0
  59. package/dist/resources/extensions/gsd/session-lock.js +1 -3
  60. package/dist/resources/extensions/gsd/state.js +48 -3
  61. package/dist/resources/extensions/gsd/sync-lock.js +89 -0
  62. package/dist/resources/extensions/gsd/tools/complete-milestone.js +61 -11
  63. package/dist/resources/extensions/gsd/tools/complete-slice.js +56 -11
  64. package/dist/resources/extensions/gsd/tools/complete-task.js +50 -2
  65. package/dist/resources/extensions/gsd/tools/plan-milestone.js +37 -1
  66. package/dist/resources/extensions/gsd/tools/plan-slice.js +31 -1
  67. package/dist/resources/extensions/gsd/tools/plan-task.js +28 -1
  68. package/dist/resources/extensions/gsd/tools/reassess-roadmap.js +32 -2
  69. package/dist/resources/extensions/gsd/tools/reopen-slice.js +86 -0
  70. package/dist/resources/extensions/gsd/tools/reopen-task.js +90 -0
  71. package/dist/resources/extensions/gsd/tools/replan-slice.js +34 -2
  72. package/dist/resources/extensions/gsd/tools/validate-milestone.js +88 -0
  73. package/dist/resources/extensions/gsd/unit-ownership.js +85 -0
  74. package/dist/resources/extensions/gsd/workflow-events.js +102 -0
  75. package/dist/resources/extensions/gsd/workflow-logger.js +193 -0
  76. package/dist/resources/extensions/gsd/workflow-manifest.js +244 -0
  77. package/dist/resources/extensions/gsd/workflow-migration.js +280 -0
  78. package/dist/resources/extensions/gsd/workflow-projections.js +373 -0
  79. package/dist/resources/extensions/gsd/workflow-reconcile.js +411 -0
  80. package/dist/resources/extensions/gsd/worktree-manager.js +34 -3
  81. package/dist/resources/extensions/gsd/worktree-resolver.js +43 -0
  82. package/dist/resources/extensions/gsd/write-intercept.js +84 -0
  83. package/dist/resources/extensions/mcp-client/index.js +14 -0
  84. package/dist/resources/extensions/voice/index.js +11 -16
  85. package/dist/resources/extensions/voice/linux-ready.js +67 -0
  86. package/dist/web/standalone/.next/BUILD_ID +1 -1
  87. package/dist/web/standalone/.next/app-path-routes-manifest.json +14 -14
  88. package/dist/web/standalone/.next/build-manifest.json +3 -3
  89. package/dist/web/standalone/.next/prerender-manifest.json +3 -3
  90. package/dist/web/standalone/.next/react-loadable-manifest.json +2 -2
  91. package/dist/web/standalone/.next/server/app/_global-error/page_client-reference-manifest.js +1 -1
  92. package/dist/web/standalone/.next/server/app/_global-error.html +2 -2
  93. package/dist/web/standalone/.next/server/app/_global-error.rsc +1 -1
  94. package/dist/web/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  95. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
  96. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
  97. package/dist/web/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  98. package/dist/web/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  99. package/dist/web/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  100. package/dist/web/standalone/.next/server/app/_not-found/page.js +1 -1
  101. package/dist/web/standalone/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  102. package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
  103. package/dist/web/standalone/.next/server/app/_not-found.rsc +3 -3
  104. package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +3 -3
  105. package/dist/web/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  106. package/dist/web/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +2 -2
  107. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  108. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  109. package/dist/web/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +2 -2
  110. package/dist/web/standalone/.next/server/app/api/boot/route_client-reference-manifest.js +1 -1
  111. package/dist/web/standalone/.next/server/app/api/bridge-terminal/input/route_client-reference-manifest.js +1 -1
  112. package/dist/web/standalone/.next/server/app/api/bridge-terminal/resize/route_client-reference-manifest.js +1 -1
  113. package/dist/web/standalone/.next/server/app/api/bridge-terminal/stream/route_client-reference-manifest.js +1 -1
  114. package/dist/web/standalone/.next/server/app/api/browse-directories/route_client-reference-manifest.js +1 -1
  115. package/dist/web/standalone/.next/server/app/api/captures/route_client-reference-manifest.js +1 -1
  116. package/dist/web/standalone/.next/server/app/api/cleanup/route_client-reference-manifest.js +1 -1
  117. package/dist/web/standalone/.next/server/app/api/dev-mode/route_client-reference-manifest.js +1 -1
  118. package/dist/web/standalone/.next/server/app/api/doctor/route_client-reference-manifest.js +1 -1
  119. package/dist/web/standalone/.next/server/app/api/export-data/route_client-reference-manifest.js +1 -1
  120. package/dist/web/standalone/.next/server/app/api/files/route_client-reference-manifest.js +1 -1
  121. package/dist/web/standalone/.next/server/app/api/forensics/route_client-reference-manifest.js +1 -1
  122. package/dist/web/standalone/.next/server/app/api/git/route_client-reference-manifest.js +1 -1
  123. package/dist/web/standalone/.next/server/app/api/history/route_client-reference-manifest.js +1 -1
  124. package/dist/web/standalone/.next/server/app/api/hooks/route_client-reference-manifest.js +1 -1
  125. package/dist/web/standalone/.next/server/app/api/inspect/route_client-reference-manifest.js +1 -1
  126. package/dist/web/standalone/.next/server/app/api/knowledge/route_client-reference-manifest.js +1 -1
  127. package/dist/web/standalone/.next/server/app/api/live-state/route_client-reference-manifest.js +1 -1
  128. package/dist/web/standalone/.next/server/app/api/onboarding/route_client-reference-manifest.js +1 -1
  129. package/dist/web/standalone/.next/server/app/api/preferences/route_client-reference-manifest.js +1 -1
  130. package/dist/web/standalone/.next/server/app/api/projects/route_client-reference-manifest.js +1 -1
  131. package/dist/web/standalone/.next/server/app/api/recovery/route_client-reference-manifest.js +1 -1
  132. package/dist/web/standalone/.next/server/app/api/remote-questions/route_client-reference-manifest.js +1 -1
  133. package/dist/web/standalone/.next/server/app/api/session/browser/route_client-reference-manifest.js +1 -1
  134. package/dist/web/standalone/.next/server/app/api/session/command/route_client-reference-manifest.js +1 -1
  135. package/dist/web/standalone/.next/server/app/api/session/events/route_client-reference-manifest.js +1 -1
  136. package/dist/web/standalone/.next/server/app/api/session/manage/route_client-reference-manifest.js +1 -1
  137. package/dist/web/standalone/.next/server/app/api/settings-data/route_client-reference-manifest.js +1 -1
  138. package/dist/web/standalone/.next/server/app/api/shutdown/route_client-reference-manifest.js +1 -1
  139. package/dist/web/standalone/.next/server/app/api/skill-health/route_client-reference-manifest.js +1 -1
  140. package/dist/web/standalone/.next/server/app/api/steer/route_client-reference-manifest.js +1 -1
  141. package/dist/web/standalone/.next/server/app/api/switch-root/route_client-reference-manifest.js +1 -1
  142. package/dist/web/standalone/.next/server/app/api/terminal/input/route_client-reference-manifest.js +1 -1
  143. package/dist/web/standalone/.next/server/app/api/terminal/resize/route_client-reference-manifest.js +1 -1
  144. package/dist/web/standalone/.next/server/app/api/terminal/sessions/route_client-reference-manifest.js +1 -1
  145. package/dist/web/standalone/.next/server/app/api/terminal/stream/route_client-reference-manifest.js +1 -1
  146. package/dist/web/standalone/.next/server/app/api/terminal/upload/route_client-reference-manifest.js +1 -1
  147. package/dist/web/standalone/.next/server/app/api/undo/route_client-reference-manifest.js +1 -1
  148. package/dist/web/standalone/.next/server/app/api/update/route_client-reference-manifest.js +1 -1
  149. package/dist/web/standalone/.next/server/app/api/visualizer/route_client-reference-manifest.js +1 -1
  150. package/dist/web/standalone/.next/server/app/index.html +1 -1
  151. package/dist/web/standalone/.next/server/app/index.rsc +4 -4
  152. package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +2 -2
  153. package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +4 -4
  154. package/dist/web/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
  155. package/dist/web/standalone/.next/server/app/index.segments/_index.segment.rsc +2 -2
  156. package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +2 -2
  157. package/dist/web/standalone/.next/server/app/page.js +1 -1
  158. package/dist/web/standalone/.next/server/app/page_client-reference-manifest.js +1 -1
  159. package/dist/web/standalone/.next/server/app-paths-manifest.json +14 -14
  160. package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
  161. package/dist/web/standalone/.next/server/middleware-react-loadable-manifest.js +1 -1
  162. package/dist/web/standalone/.next/server/pages/404.html +1 -1
  163. package/dist/web/standalone/.next/server/pages/500.html +2 -2
  164. package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
  165. package/dist/web/standalone/.next/static/chunks/4024.11ca5c01938e5948.js +9 -0
  166. package/dist/web/standalone/.next/static/chunks/{3721.bf31263de6d5fa46.js → 485.243af25f0cdf50d6.js} +2 -2
  167. package/dist/web/standalone/.next/static/chunks/app/{page-b9367c5ae13b99c6.js → page-6654a8cca61a3d1c.js} +1 -1
  168. package/dist/web/standalone/.next/static/chunks/webpack-0a4cd455ec4197d2.js +1 -0
  169. package/dist/web/standalone/.next/static/css/dd4ae3f58ac9b600.css +1 -0
  170. package/package.json +2 -1
  171. package/packages/native/dist/stream-process/index.js +2 -2
  172. package/packages/native/src/__tests__/stream-process.test.mjs +34 -0
  173. package/packages/native/src/stream-process/index.ts +2 -2
  174. package/packages/pi-coding-agent/dist/core/auth-storage.d.ts +3 -1
  175. package/packages/pi-coding-agent/dist/core/auth-storage.d.ts.map +1 -1
  176. package/packages/pi-coding-agent/dist/core/auth-storage.js +15 -1
  177. package/packages/pi-coding-agent/dist/core/auth-storage.js.map +1 -1
  178. package/packages/pi-coding-agent/dist/core/compaction-orchestrator.d.ts.map +1 -1
  179. package/packages/pi-coding-agent/dist/core/compaction-orchestrator.js +2 -0
  180. package/packages/pi-coding-agent/dist/core/compaction-orchestrator.js.map +1 -1
  181. package/packages/pi-coding-agent/dist/core/extensions/types.d.ts +2 -1
  182. package/packages/pi-coding-agent/dist/core/extensions/types.d.ts.map +1 -1
  183. package/packages/pi-coding-agent/dist/core/extensions/types.js.map +1 -1
  184. package/packages/pi-coding-agent/dist/core/lifecycle-hooks.d.ts +4 -0
  185. package/packages/pi-coding-agent/dist/core/lifecycle-hooks.d.ts.map +1 -1
  186. package/packages/pi-coding-agent/dist/core/lifecycle-hooks.js +10 -5
  187. package/packages/pi-coding-agent/dist/core/lifecycle-hooks.js.map +1 -1
  188. package/packages/pi-coding-agent/dist/core/lifecycle-hooks.test.d.ts +2 -0
  189. package/packages/pi-coding-agent/dist/core/lifecycle-hooks.test.d.ts.map +1 -0
  190. package/packages/pi-coding-agent/dist/core/lifecycle-hooks.test.js +185 -0
  191. package/packages/pi-coding-agent/dist/core/lifecycle-hooks.test.js.map +1 -0
  192. package/packages/pi-coding-agent/dist/core/local-model-check.d.ts +15 -0
  193. package/packages/pi-coding-agent/dist/core/local-model-check.d.ts.map +1 -0
  194. package/packages/pi-coding-agent/dist/core/local-model-check.js +41 -0
  195. package/packages/pi-coding-agent/dist/core/local-model-check.js.map +1 -0
  196. package/packages/pi-coding-agent/dist/core/model-registry-auth-mode.test.js +239 -10
  197. package/packages/pi-coding-agent/dist/core/model-registry-auth-mode.test.js.map +1 -1
  198. package/packages/pi-coding-agent/dist/core/model-registry.d.ts +13 -1
  199. package/packages/pi-coding-agent/dist/core/model-registry.d.ts.map +1 -1
  200. package/packages/pi-coding-agent/dist/core/model-registry.js +40 -3
  201. package/packages/pi-coding-agent/dist/core/model-registry.js.map +1 -1
  202. package/packages/pi-coding-agent/dist/core/package-commands.test.js +206 -195
  203. package/packages/pi-coding-agent/dist/core/package-commands.test.js.map +1 -1
  204. package/packages/pi-coding-agent/dist/core/settings-manager.d.ts +3 -0
  205. package/packages/pi-coding-agent/dist/core/settings-manager.d.ts.map +1 -1
  206. package/packages/pi-coding-agent/dist/core/settings-manager.js +6 -0
  207. package/packages/pi-coding-agent/dist/core/settings-manager.js.map +1 -1
  208. package/packages/pi-coding-agent/dist/main.d.ts.map +1 -1
  209. package/packages/pi-coding-agent/dist/main.js +17 -0
  210. package/packages/pi-coding-agent/dist/main.js.map +1 -1
  211. package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/timestamp.test.d.ts +2 -0
  212. package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/timestamp.test.d.ts.map +1 -0
  213. package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/timestamp.test.js +32 -0
  214. package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/timestamp.test.js.map +1 -0
  215. package/packages/pi-coding-agent/dist/modes/interactive/components/assistant-message.d.ts +3 -1
  216. package/packages/pi-coding-agent/dist/modes/interactive/components/assistant-message.d.ts.map +1 -1
  217. package/packages/pi-coding-agent/dist/modes/interactive/components/assistant-message.js +8 -1
  218. package/packages/pi-coding-agent/dist/modes/interactive/components/assistant-message.js.map +1 -1
  219. package/packages/pi-coding-agent/dist/modes/interactive/components/settings-selector.d.ts +2 -0
  220. package/packages/pi-coding-agent/dist/modes/interactive/components/settings-selector.d.ts.map +1 -1
  221. package/packages/pi-coding-agent/dist/modes/interactive/components/settings-selector.js +12 -0
  222. package/packages/pi-coding-agent/dist/modes/interactive/components/settings-selector.js.map +1 -1
  223. package/packages/pi-coding-agent/dist/modes/interactive/components/timestamp.d.ts +15 -0
  224. package/packages/pi-coding-agent/dist/modes/interactive/components/timestamp.d.ts.map +1 -0
  225. package/packages/pi-coding-agent/dist/modes/interactive/components/timestamp.js +40 -0
  226. package/packages/pi-coding-agent/dist/modes/interactive/components/timestamp.js.map +1 -0
  227. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
  228. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.js +4 -1
  229. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.js.map +1 -1
  230. package/packages/pi-coding-agent/dist/modes/interactive/components/user-message.d.ts +5 -2
  231. package/packages/pi-coding-agent/dist/modes/interactive/components/user-message.d.ts.map +1 -1
  232. package/packages/pi-coding-agent/dist/modes/interactive/components/user-message.js +13 -2
  233. package/packages/pi-coding-agent/dist/modes/interactive/components/user-message.js.map +1 -1
  234. package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.d.ts.map +1 -1
  235. package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.js +17 -8
  236. package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.js.map +1 -1
  237. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  238. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js +7 -3
  239. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js.map +1 -1
  240. package/packages/pi-coding-agent/package.json +1 -1
  241. package/packages/pi-coding-agent/src/core/auth-storage.ts +15 -1
  242. package/packages/pi-coding-agent/src/core/compaction-orchestrator.ts +2 -0
  243. package/packages/pi-coding-agent/src/core/extensions/types.ts +2 -1
  244. package/packages/pi-coding-agent/src/core/lifecycle-hooks.test.ts +227 -0
  245. package/packages/pi-coding-agent/src/core/lifecycle-hooks.ts +11 -5
  246. package/packages/pi-coding-agent/src/core/local-model-check.ts +45 -0
  247. package/packages/pi-coding-agent/src/core/model-registry-auth-mode.test.ts +297 -11
  248. package/packages/pi-coding-agent/src/core/model-registry.ts +51 -4
  249. package/packages/pi-coding-agent/src/core/package-commands.test.ts +227 -205
  250. package/packages/pi-coding-agent/src/core/settings-manager.ts +9 -0
  251. package/packages/pi-coding-agent/src/main.ts +19 -0
  252. package/packages/pi-coding-agent/src/modes/interactive/components/__tests__/timestamp.test.ts +38 -0
  253. package/packages/pi-coding-agent/src/modes/interactive/components/assistant-message.ts +10 -0
  254. package/packages/pi-coding-agent/src/modes/interactive/components/settings-selector.ts +15 -0
  255. package/packages/pi-coding-agent/src/modes/interactive/components/timestamp.ts +48 -0
  256. package/packages/pi-coding-agent/src/modes/interactive/components/tool-execution.ts +3 -1
  257. package/packages/pi-coding-agent/src/modes/interactive/components/user-message.ts +18 -3
  258. package/packages/pi-coding-agent/src/modes/interactive/controllers/chat-controller.ts +16 -7
  259. package/packages/pi-coding-agent/src/modes/interactive/interactive-mode.ts +8 -1
  260. package/pkg/package.json +1 -1
  261. package/src/resources/extensions/gsd/activity-log.ts +1 -0
  262. package/src/resources/extensions/gsd/auto/infra-errors.ts +3 -0
  263. package/src/resources/extensions/gsd/auto/loop-deps.ts +0 -19
  264. package/src/resources/extensions/gsd/auto/phases.ts +69 -91
  265. package/src/resources/extensions/gsd/auto/run-unit.ts +6 -3
  266. package/src/resources/extensions/gsd/auto/session.ts +0 -18
  267. package/src/resources/extensions/gsd/auto-artifact-paths.ts +131 -0
  268. package/src/resources/extensions/gsd/auto-dashboard.ts +0 -1
  269. package/src/resources/extensions/gsd/auto-post-unit.ts +25 -106
  270. package/src/resources/extensions/gsd/auto-prompts.ts +24 -1
  271. package/src/resources/extensions/gsd/auto-start.ts +26 -5
  272. package/src/resources/extensions/gsd/auto-timers.ts +64 -3
  273. package/src/resources/extensions/gsd/auto-worktree-sync.ts +5 -0
  274. package/src/resources/extensions/gsd/auto-worktree.ts +17 -11
  275. package/src/resources/extensions/gsd/auto.ts +44 -86
  276. package/src/resources/extensions/gsd/bootstrap/db-tools.ts +162 -11
  277. package/src/resources/extensions/gsd/bootstrap/register-hooks.ts +22 -0
  278. package/src/resources/extensions/gsd/commands/catalog.ts +7 -1
  279. package/src/resources/extensions/gsd/commands/context.ts +0 -5
  280. package/src/resources/extensions/gsd/commands/handlers/core.ts +2 -0
  281. package/src/resources/extensions/gsd/commands/handlers/ops.ts +10 -0
  282. package/src/resources/extensions/gsd/commands/handlers/parallel.ts +1 -1
  283. package/src/resources/extensions/gsd/commands-mcp-status.ts +247 -0
  284. package/src/resources/extensions/gsd/crash-recovery.ts +1 -5
  285. package/src/resources/extensions/gsd/dashboard-overlay.ts +0 -50
  286. package/src/resources/extensions/gsd/db-writer.ts +41 -27
  287. package/src/resources/extensions/gsd/doctor-checks.ts +180 -2
  288. package/src/resources/extensions/gsd/doctor-types.ts +7 -1
  289. package/src/resources/extensions/gsd/doctor.ts +13 -4
  290. package/src/resources/extensions/gsd/git-service.ts +6 -2
  291. package/src/resources/extensions/gsd/gsd-db.ts +32 -4
  292. package/src/resources/extensions/gsd/guided-flow.ts +1 -2
  293. package/src/resources/extensions/gsd/journal.ts +6 -1
  294. package/src/resources/extensions/gsd/markdown-renderer.ts +1 -1
  295. package/src/resources/extensions/gsd/parallel-merge.ts +1 -1
  296. package/src/resources/extensions/gsd/parallel-orchestrator.ts +5 -21
  297. package/src/resources/extensions/gsd/preferences-types.ts +2 -2
  298. package/src/resources/extensions/gsd/preferences.ts +7 -3
  299. package/src/resources/extensions/gsd/prompts/complete-milestone.md +21 -10
  300. package/src/resources/extensions/gsd/prompts/complete-slice.md +10 -23
  301. package/src/resources/extensions/gsd/prompts/discuss.md +2 -2
  302. package/src/resources/extensions/gsd/prompts/execute-task.md +5 -15
  303. package/src/resources/extensions/gsd/prompts/guided-discuss-milestone.md +1 -1
  304. package/src/resources/extensions/gsd/prompts/guided-discuss-slice.md +1 -1
  305. package/src/resources/extensions/gsd/prompts/guided-plan-slice.md +1 -1
  306. package/src/resources/extensions/gsd/prompts/guided-research-slice.md +1 -1
  307. package/src/resources/extensions/gsd/prompts/plan-milestone.md +1 -1
  308. package/src/resources/extensions/gsd/prompts/plan-slice.md +5 -3
  309. package/src/resources/extensions/gsd/prompts/queue.md +2 -2
  310. package/src/resources/extensions/gsd/prompts/quick-task.md +2 -0
  311. package/src/resources/extensions/gsd/prompts/reactive-execute.md +1 -1
  312. package/src/resources/extensions/gsd/prompts/reassess-roadmap.md +6 -6
  313. package/src/resources/extensions/gsd/prompts/replan-slice.md +3 -14
  314. package/src/resources/extensions/gsd/prompts/research-slice.md +3 -3
  315. package/src/resources/extensions/gsd/prompts/rethink.md +83 -0
  316. package/src/resources/extensions/gsd/prompts/system.md +1 -1
  317. package/src/resources/extensions/gsd/prompts/validate-milestone.md +7 -37
  318. package/src/resources/extensions/gsd/provider-error-pause.ts +9 -0
  319. package/src/resources/extensions/gsd/repo-identity.ts +46 -7
  320. package/src/resources/extensions/gsd/rethink.ts +154 -0
  321. package/src/resources/extensions/gsd/session-lock.ts +0 -4
  322. package/src/resources/extensions/gsd/state.ts +49 -1
  323. package/src/resources/extensions/gsd/sync-lock.ts +94 -0
  324. package/src/resources/extensions/gsd/tests/auto-lock-creation.test.ts +5 -13
  325. package/src/resources/extensions/gsd/tests/auto-loop.test.ts +6 -10
  326. package/src/resources/extensions/gsd/tests/auto-pr-bugs.test.ts +88 -0
  327. package/src/resources/extensions/gsd/tests/complete-milestone.test.ts +96 -0
  328. package/src/resources/extensions/gsd/tests/complete-slice.test.ts +264 -228
  329. package/src/resources/extensions/gsd/tests/complete-task.test.ts +317 -250
  330. package/src/resources/extensions/gsd/tests/completed-units-metrics-sync.test.ts +114 -0
  331. package/src/resources/extensions/gsd/tests/crash-recovery.test.ts +2 -8
  332. package/src/resources/extensions/gsd/tests/custom-engine-loop-integration.test.ts +0 -3
  333. package/src/resources/extensions/gsd/tests/db-writer.test.ts +79 -0
  334. package/src/resources/extensions/gsd/tests/derive-state-db-disk-reconcile.test.ts +121 -0
  335. package/src/resources/extensions/gsd/tests/derive-state-db.test.ts +60 -0
  336. package/src/resources/extensions/gsd/tests/est-annotation-timeout.test.ts +120 -0
  337. package/src/resources/extensions/gsd/tests/gsd-db.test.ts +1 -1
  338. package/src/resources/extensions/gsd/tests/idle-recovery.test.ts +1 -1
  339. package/src/resources/extensions/gsd/tests/infra-error.test.ts +20 -2
  340. package/src/resources/extensions/gsd/tests/inherited-repo-home-dir.test.ts +121 -0
  341. package/src/resources/extensions/gsd/tests/integration-proof.test.ts +15 -24
  342. package/src/resources/extensions/gsd/tests/journal-integration.test.ts +0 -3
  343. package/src/resources/extensions/gsd/tests/mcp-status.test.ts +103 -0
  344. package/src/resources/extensions/gsd/tests/md-importer.test.ts +1 -1
  345. package/src/resources/extensions/gsd/tests/memory-store.test.ts +2 -2
  346. package/src/resources/extensions/gsd/tests/merge-conflict-stops-loop.test.ts +66 -0
  347. package/src/resources/extensions/gsd/tests/milestone-transition-state-rebuild.test.ts +8 -9
  348. package/src/resources/extensions/gsd/tests/none-mode-gates.test.ts +42 -3
  349. package/src/resources/extensions/gsd/tests/parallel-budget-atomicity.test.ts +0 -1
  350. package/src/resources/extensions/gsd/tests/parallel-crash-recovery.test.ts +0 -7
  351. package/src/resources/extensions/gsd/tests/parallel-merge.test.ts +7 -8
  352. package/src/resources/extensions/gsd/tests/parallel-orchestration.test.ts +20 -24
  353. package/src/resources/extensions/gsd/tests/parallel-worker-monitoring.test.ts +0 -2
  354. package/src/resources/extensions/gsd/tests/plan-milestone.test.ts +9 -6
  355. package/src/resources/extensions/gsd/tests/post-mutation-hook.test.ts +171 -0
  356. package/src/resources/extensions/gsd/tests/preferences.test.ts +7 -9
  357. package/src/resources/extensions/gsd/tests/projection-regression.test.ts +174 -0
  358. package/src/resources/extensions/gsd/tests/prompt-contracts.test.ts +26 -21
  359. package/src/resources/extensions/gsd/tests/recovery-attempts-reset.test.ts +176 -0
  360. package/src/resources/extensions/gsd/tests/reopen-slice.test.ts +155 -0
  361. package/src/resources/extensions/gsd/tests/reopen-task.test.ts +165 -0
  362. package/src/resources/extensions/gsd/tests/session-lock-regression.test.ts +1 -4
  363. package/src/resources/extensions/gsd/tests/stop-auto-merge-back.test.ts +67 -0
  364. package/src/resources/extensions/gsd/tests/stop-auto-remote.test.ts +2 -3
  365. package/src/resources/extensions/gsd/tests/survivor-branch-complete.test.ts +108 -0
  366. package/src/resources/extensions/gsd/tests/sync-lock.test.ts +122 -0
  367. package/src/resources/extensions/gsd/tests/terminated-transient.test.ts +49 -0
  368. package/src/resources/extensions/gsd/tests/tool-naming.test.ts +2 -1
  369. package/src/resources/extensions/gsd/tests/unit-ownership.test.ts +175 -0
  370. package/src/resources/extensions/gsd/tests/workflow-events.test.ts +205 -0
  371. package/src/resources/extensions/gsd/tests/workflow-logger.test.ts +275 -0
  372. package/src/resources/extensions/gsd/tests/workflow-manifest.test.ts +186 -0
  373. package/src/resources/extensions/gsd/tests/workflow-projections.test.ts +171 -0
  374. package/src/resources/extensions/gsd/tests/worktree-journal-events.test.ts +220 -0
  375. package/src/resources/extensions/gsd/tests/worktree-submodule-safety.test.ts +65 -0
  376. package/src/resources/extensions/gsd/tests/write-intercept.test.ts +76 -0
  377. package/src/resources/extensions/gsd/tools/complete-milestone.ts +74 -11
  378. package/src/resources/extensions/gsd/tools/complete-slice.ts +68 -11
  379. package/src/resources/extensions/gsd/tools/complete-task.ts +63 -1
  380. package/src/resources/extensions/gsd/tools/plan-milestone.ts +45 -0
  381. package/src/resources/extensions/gsd/tools/plan-slice.ts +40 -0
  382. package/src/resources/extensions/gsd/tools/plan-task.ts +37 -1
  383. package/src/resources/extensions/gsd/tools/reassess-roadmap.ts +39 -1
  384. package/src/resources/extensions/gsd/tools/reopen-slice.ts +125 -0
  385. package/src/resources/extensions/gsd/tools/reopen-task.ts +129 -0
  386. package/src/resources/extensions/gsd/tools/replan-slice.ts +41 -1
  387. package/src/resources/extensions/gsd/tools/validate-milestone.ts +127 -0
  388. package/src/resources/extensions/gsd/types.ts +8 -0
  389. package/src/resources/extensions/gsd/unit-ownership.ts +104 -0
  390. package/src/resources/extensions/gsd/workflow-events.ts +154 -0
  391. package/src/resources/extensions/gsd/workflow-logger.ts +243 -0
  392. package/src/resources/extensions/gsd/workflow-manifest.ts +334 -0
  393. package/src/resources/extensions/gsd/workflow-migration.ts +345 -0
  394. package/src/resources/extensions/gsd/workflow-projections.ts +425 -0
  395. package/src/resources/extensions/gsd/workflow-reconcile.ts +503 -0
  396. package/src/resources/extensions/gsd/worktree-manager.ts +41 -5
  397. package/src/resources/extensions/gsd/worktree-resolver.ts +44 -0
  398. package/src/resources/extensions/gsd/write-intercept.ts +90 -0
  399. package/src/resources/extensions/mcp-client/index.ts +20 -0
  400. package/src/resources/extensions/voice/index.ts +11 -21
  401. package/src/resources/extensions/voice/linux-ready.ts +87 -0
  402. package/src/resources/extensions/voice/tests/linux-ready.test.ts +124 -0
  403. package/dist/web/standalone/.next/static/chunks/4024.0de81b543b28b9fe.js +0 -9
  404. package/dist/web/standalone/.next/static/chunks/webpack-9014b5adb127a98a.js +0 -1
  405. package/dist/web/standalone/.next/static/css/8a727f372cf53002.css +0 -1
  406. /package/dist/web/standalone/.next/static/{tokoGmfkYfWf1_Yl_Gz7i → j-BskPs0nxxPeYY-bSrab}/_buildManifest.js +0 -0
  407. /package/dist/web/standalone/.next/static/{tokoGmfkYfWf1_Yl_Gz7i → j-BskPs0nxxPeYY-bSrab}/_ssgManifest.js +0 -0
@@ -0,0 +1,114 @@
1
+ /**
2
+ * completed-units-metrics-sync.test.ts — Regression tests for #2313.
3
+ *
4
+ * 1. completed-units.json should be archived (not wiped) on milestone transition
5
+ * 2. metrics.json should be in the worktree → project root sync file list
6
+ */
7
+
8
+ import test from "node:test";
9
+ import assert from "node:assert/strict";
10
+ import { readFileSync, mkdtempSync, mkdirSync, writeFileSync, existsSync, cpSync } from "node:fs";
11
+ import { join } from "node:path";
12
+ import { tmpdir } from "node:os";
13
+
14
+ // ─── Bug 1: completed-units.json should be archived, not wiped ─────────────
15
+
16
+ const phasesSrcPath = join(import.meta.dirname, "..", "auto", "phases.ts");
17
+ const phasesSrc = readFileSync(phasesSrcPath, "utf-8");
18
+
19
+ test("#2313: completed-units.json should not be blindly wiped to [] on milestone transition", () => {
20
+ // The milestone transition block should NOT write an empty array to completed-units.json
21
+ // without first archiving the existing data. Look for the archive/rename pattern.
22
+ const transitionIdx = phasesSrc.indexOf("Milestone transition");
23
+ assert.ok(transitionIdx !== -1, "Milestone transition section exists");
24
+
25
+ // Find the completed-units handling block
26
+ const completedUnitsIdx = phasesSrc.indexOf("completed-units", transitionIdx);
27
+ assert.ok(completedUnitsIdx !== -1, "completed-units handling exists in transition");
28
+
29
+ // Get a window around the completed-units handling (1200 chars to
30
+ // accommodate CRLF line endings on Windows which inflate byte offsets).
31
+ const windowStart = Math.max(0, completedUnitsIdx - 300);
32
+ const windowEnd = Math.min(phasesSrc.length, completedUnitsIdx + 900);
33
+ const window = phasesSrc.slice(windowStart, windowEnd).toLowerCase();
34
+
35
+ // Should archive/rename the old file before resetting
36
+ const hasArchive = window.includes("archive") ||
37
+ window.includes("rename") ||
38
+ window.includes("cpsync") ||
39
+ window.includes("safecopy") ||
40
+ window.includes("completed-units-");
41
+
42
+ assert.ok(
43
+ hasArchive,
44
+ "completed-units.json should be archived before reset during milestone transition",
45
+ );
46
+ });
47
+
48
+ // ─── Bug 2: metrics.json should be in the sync file lists ──────────────────
49
+
50
+ test("#2313: syncStateToProjectRoot should sync metrics.json", () => {
51
+ const syncSrcPath = join(import.meta.dirname, "..", "auto-worktree-sync.ts");
52
+ const syncSrc = readFileSync(syncSrcPath, "utf-8");
53
+
54
+ // syncStateToProjectRoot should copy metrics.json from worktree to project root
55
+ assert.ok(
56
+ syncSrc.includes("metrics.json"),
57
+ "auto-worktree-sync.ts should reference metrics.json for sync",
58
+ );
59
+ });
60
+
61
+ test("#2313: syncWorktreeStateBack should include metrics.json in root files list", () => {
62
+ const autoWorktreeSrcPath = join(import.meta.dirname, "..", "auto-worktree.ts");
63
+ const autoWorktreeSrc = readFileSync(autoWorktreeSrcPath, "utf-8");
64
+
65
+ // Find the rootFiles array in syncWorktreeStateBack
66
+ const syncBackIdx = autoWorktreeSrc.indexOf("syncWorktreeStateBack");
67
+ assert.ok(syncBackIdx !== -1, "syncWorktreeStateBack exists");
68
+
69
+ const rootFilesIdx = autoWorktreeSrc.indexOf("rootFiles", syncBackIdx);
70
+ assert.ok(rootFilesIdx !== -1, "rootFiles list exists in syncWorktreeStateBack");
71
+
72
+ // Get the rootFiles array content
73
+ const arrayStart = autoWorktreeSrc.indexOf("[", rootFilesIdx);
74
+ const arrayEnd = autoWorktreeSrc.indexOf("]", arrayStart);
75
+ const rootFilesBlock = autoWorktreeSrc.slice(arrayStart, arrayEnd);
76
+
77
+ assert.ok(
78
+ rootFilesBlock.includes("metrics.json"),
79
+ "metrics.json should be in syncWorktreeStateBack rootFiles list",
80
+ );
81
+ });
82
+
83
+ // ─── Functional test: completed-units archive ────────────────────────────────
84
+
85
+ test("#2313: functional — completed-units archive creates milestone-specific file", () => {
86
+ const tmpBase = mkdtempSync(join(tmpdir(), "gsd-completed-units-"));
87
+ const gsdDir = join(tmpBase, ".gsd");
88
+ mkdirSync(gsdDir, { recursive: true });
89
+
90
+ // Simulate existing completed-units.json with data
91
+ const existing = [
92
+ { type: "task", id: "T01" },
93
+ { type: "slice", id: "S01" },
94
+ ];
95
+ const completedKeysPath = join(gsdDir, "completed-units.json");
96
+ writeFileSync(completedKeysPath, JSON.stringify(existing, null, 2));
97
+
98
+ // Simulate the archive behavior: copy to milestone-specific file
99
+ const milestoneId = "M001";
100
+ const archivePath = join(gsdDir, `completed-units-${milestoneId}.json`);
101
+ cpSync(completedKeysPath, archivePath);
102
+
103
+ // Reset the main file
104
+ writeFileSync(completedKeysPath, JSON.stringify([], null, 2));
105
+
106
+ // Verify archive exists with original data
107
+ assert.ok(existsSync(archivePath), "archive file should exist");
108
+ const archived = JSON.parse(readFileSync(archivePath, "utf-8"));
109
+ assert.deepEqual(archived, existing, "archived data should match original");
110
+
111
+ // Verify main file is reset
112
+ const current = JSON.parse(readFileSync(completedKeysPath, "utf-8"));
113
+ assert.deepEqual(current, [], "current completed-units should be empty after transition");
114
+ });
@@ -30,12 +30,11 @@ test("writeLock creates lock file and readCrashLock reads it", (t) => {
30
30
  const base = makeTmpBase();
31
31
  t.after(() => cleanup(base));
32
32
 
33
- writeLock(base, "execute-task", "M001/S01/T01", 3, "/tmp/session.jsonl");
33
+ writeLock(base, "execute-task", "M001/S01/T01", "/tmp/session.jsonl");
34
34
  const lock = readCrashLock(base);
35
35
  assert.ok(lock, "lock should exist");
36
36
  assert.equal(lock!.unitType, "execute-task");
37
37
  assert.equal(lock!.unitId, "M001/S01/T01");
38
- assert.equal(lock!.completedUnits, 3);
39
38
  assert.equal(lock!.sessionFile, "/tmp/session.jsonl");
40
39
  assert.equal(lock!.pid, process.pid);
41
40
  });
@@ -54,7 +53,7 @@ test("clearLock removes existing lock file", (t) => {
54
53
  const base = makeTmpBase();
55
54
  t.after(() => cleanup(base));
56
55
 
57
- writeLock(base, "plan-slice", "M001/S01", 0);
56
+ writeLock(base, "plan-slice", "M001/S01");
58
57
  assert.ok(readCrashLock(base), "lock should exist before clear");
59
58
  clearLock(base);
60
59
  assert.equal(readCrashLock(base), null, "lock should be gone after clear");
@@ -77,7 +76,6 @@ test("isLockProcessAlive returns true for current process (different pid)", () =
77
76
  unitType: "execute-task",
78
77
  unitId: "M001/S01/T01",
79
78
  unitStartedAt: new Date().toISOString(),
80
- completedUnits: 0,
81
79
  };
82
80
  assert.equal(isLockProcessAlive(lock), false, "own PID should return false");
83
81
  });
@@ -89,7 +87,6 @@ test("isLockProcessAlive returns false for dead PID", () => {
89
87
  unitType: "execute-task",
90
88
  unitId: "M001/S01/T01",
91
89
  unitStartedAt: new Date().toISOString(),
92
- completedUnits: 0,
93
90
  };
94
91
  assert.equal(isLockProcessAlive(lock), false);
95
92
  });
@@ -100,7 +97,6 @@ test("isLockProcessAlive returns false for invalid PIDs", () => {
100
97
  unitType: "x",
101
98
  unitId: "x",
102
99
  unitStartedAt: new Date().toISOString(),
103
- completedUnits: 0,
104
100
  };
105
101
  assert.equal(isLockProcessAlive({ ...base, pid: 0 } as LockData), false);
106
102
  assert.equal(isLockProcessAlive({ ...base, pid: -1 } as LockData), false);
@@ -116,11 +112,9 @@ test("formatCrashInfo includes unit type, id, and PID", () => {
116
112
  unitType: "complete-slice",
117
113
  unitId: "M002/S03",
118
114
  unitStartedAt: "2025-01-01T00:01:00.000Z",
119
- completedUnits: 7,
120
115
  };
121
116
  const info = formatCrashInfo(lock);
122
117
  assert.ok(info.includes("complete-slice"));
123
118
  assert.ok(info.includes("M002/S03"));
124
119
  assert.ok(info.includes("12345"));
125
- assert.ok(info.includes("7"));
126
120
  });
@@ -195,9 +195,6 @@ function makeMockDeps(overrides?: Partial<LoopDeps>): LoopDeps & { callLog: stri
195
195
  getPriorSliceCompletionBlocker: () => null,
196
196
  getMainBranch: () => "main",
197
197
  closeoutUnit: async () => {},
198
- verifyExpectedArtifact: () => true,
199
- clearUnitRuntimeRecord: () => {},
200
- writeUnitRuntimeRecord: () => {},
201
198
  recordOutcome: () => {},
202
199
  writeLock: () => {},
203
200
  captureAvailableSkills: () => {},
@@ -483,6 +483,85 @@ describe('db-writer', () => {
483
483
  }
484
484
  });
485
485
 
486
+ test('saveArtifactToDb — shrinkage guard preserves larger existing file', async () => {
487
+ const tmpDir = makeTmpDir();
488
+ const dbPath = path.join(tmpDir, '.gsd', 'gsd.db');
489
+ openDatabase(dbPath);
490
+
491
+ try {
492
+ const fullContent = '# Full Research\n\n' + 'x'.repeat(20000) + '\n';
493
+ const abbreviatedContent = '# Summary\n\nShort version.\n';
494
+
495
+ // Pre-create the file with full content (simulating a prior `write` tool call)
496
+ const relPath = 'milestones/M001/M001-RESEARCH.md';
497
+ const filePath = path.join(tmpDir, '.gsd', relPath);
498
+ fs.mkdirSync(path.dirname(filePath), { recursive: true });
499
+ fs.writeFileSync(filePath, fullContent);
500
+
501
+ // Call saveArtifactToDb with abbreviated content — should trigger shrinkage guard
502
+ await saveArtifactToDb({
503
+ path: relPath,
504
+ artifact_type: 'RESEARCH',
505
+ content: abbreviatedContent,
506
+ milestone_id: 'M001',
507
+ }, tmpDir);
508
+
509
+ // Disk file should be preserved (not overwritten)
510
+ assert.deepStrictEqual(
511
+ fs.readFileSync(filePath, 'utf-8'),
512
+ fullContent,
513
+ 'disk file preserved — shrinkage guard prevented overwrite',
514
+ );
515
+
516
+ // DB should contain the full disk content, not the abbreviated content
517
+ const adapter = _getAdapter();
518
+ const row = adapter!
519
+ .prepare('SELECT full_content FROM artifacts WHERE path = ?')
520
+ .get(relPath);
521
+ assert.deepStrictEqual(
522
+ row!['full_content'],
523
+ fullContent,
524
+ 'DB stores the richer disk content instead of abbreviated content',
525
+ );
526
+ } finally {
527
+ closeDatabase();
528
+ cleanupDir(tmpDir);
529
+ }
530
+ });
531
+
532
+ test('saveArtifactToDb — allows overwrite when new content is similar size', async () => {
533
+ const tmpDir = makeTmpDir();
534
+ const dbPath = path.join(tmpDir, '.gsd', 'gsd.db');
535
+ openDatabase(dbPath);
536
+
537
+ try {
538
+ const oldContent = '# Summary v1\n\nOriginal content here.\n';
539
+ const newContent = '# Summary v2\n\nUpdated content here with more details.\n';
540
+
541
+ const relPath = 'milestones/M001/M001-SUMMARY.md';
542
+ const filePath = path.join(tmpDir, '.gsd', relPath);
543
+ fs.mkdirSync(path.dirname(filePath), { recursive: true });
544
+ fs.writeFileSync(filePath, oldContent);
545
+
546
+ await saveArtifactToDb({
547
+ path: relPath,
548
+ artifact_type: 'SUMMARY',
549
+ content: newContent,
550
+ milestone_id: 'M001',
551
+ }, tmpDir);
552
+
553
+ // Disk file should be updated (new content is >=50% of old size)
554
+ assert.deepStrictEqual(
555
+ fs.readFileSync(filePath, 'utf-8'),
556
+ newContent,
557
+ 'disk file updated when new content is similar size',
558
+ );
559
+ } finally {
560
+ closeDatabase();
561
+ cleanupDir(tmpDir);
562
+ }
563
+ });
564
+
486
565
  // ═══════════════════════════════════════════════════════════════════════════
487
566
  // Full Round-Trip: DB → Markdown → Parse → Compare
488
567
  // ═══════════════════════════════════════════════════════════════════════════
@@ -0,0 +1,121 @@
1
+ /**
2
+ * derive-state-db-disk-reconcile.test.ts — #2416
3
+ *
4
+ * After migration to DB-backed state, milestones that exist on disk
5
+ * (in .gsd/milestones/) but were never imported into the DB become
6
+ * invisible to deriveStateFromDb(). This test verifies that
7
+ * deriveStateFromDb reconciles disk milestones with DB milestones.
8
+ */
9
+
10
+ import { mkdtempSync, mkdirSync, rmSync, writeFileSync } from "node:fs";
11
+ import { join } from "node:path";
12
+ import { tmpdir } from "node:os";
13
+
14
+ import { deriveStateFromDb, invalidateStateCache } from "../state.ts";
15
+ import {
16
+ openDatabase,
17
+ closeDatabase,
18
+ insertMilestone,
19
+ insertSlice,
20
+ insertTask,
21
+ } from "../gsd-db.ts";
22
+ import { createTestContext } from "./test-helpers.ts";
23
+
24
+ const { assertEq, assertTrue, report } = createTestContext();
25
+
26
+ function createFixtureBase(): string {
27
+ const base = mkdtempSync(join(tmpdir(), "gsd-disk-reconcile-"));
28
+ mkdirSync(join(base, ".gsd", "milestones"), { recursive: true });
29
+ return base;
30
+ }
31
+
32
+ function writeFile(base: string, relativePath: string, content: string): void {
33
+ const full = join(base, ".gsd", relativePath);
34
+ mkdirSync(join(full, ".."), { recursive: true });
35
+ writeFileSync(full, content);
36
+ }
37
+
38
+ function cleanup(base: string): void {
39
+ rmSync(base, { recursive: true, force: true });
40
+ }
41
+
42
+ const CONTEXT_CONTENT = `# M002: Disk-Only Milestone
43
+
44
+ This milestone exists on disk but not in the DB.
45
+
46
+ ## Must-Haves
47
+ - Something important
48
+ `;
49
+
50
+ const ROADMAP_CONTENT = `# M002: Disk-Only Milestone
51
+
52
+ **Vision:** Test disk reconciliation.
53
+
54
+ ## Slices
55
+
56
+ - [ ] **S01: First Slice** \`risk:low\` \`depends:[]\`
57
+ > Do something.
58
+ `;
59
+
60
+ async function main(): Promise<void> {
61
+ console.log("\n=== #2416: deriveStateFromDb reconciles disk milestones ===");
62
+
63
+ // Set up: M001 in DB, M002 on disk only
64
+ const base = createFixtureBase();
65
+ const dbPath = join(base, ".gsd", "gsd.db");
66
+
67
+ try {
68
+ openDatabase(dbPath);
69
+
70
+ // M001 is in the DB with a complete status
71
+ insertMilestone({ id: "M001", title: "M001: DB Milestone", status: "complete", depends_on: [] });
72
+ insertSlice({ id: "S01", milestoneId: "M001", title: "S01: Done Slice", status: "complete", depends: [] });
73
+
74
+ // Write M001 summary on disk (marks it complete on filesystem too)
75
+ writeFile(base, "milestones/M001/SUMMARY.md", "# M001: DB Milestone\n\nDone.");
76
+
77
+ // M002 exists ONLY on disk, not in DB
78
+ writeFile(base, "milestones/M002/CONTEXT.md", CONTEXT_CONTENT);
79
+ writeFile(base, "milestones/M002/ROADMAP.md", ROADMAP_CONTENT);
80
+
81
+ invalidateStateCache();
82
+ const state = await deriveStateFromDb(base);
83
+
84
+ // M002 should be visible in the registry
85
+ const m002Entry = state.registry.find((m) => m.id === "M002");
86
+ assertTrue(
87
+ m002Entry !== undefined,
88
+ "M002 (disk-only milestone) should appear in state.registry (#2416)",
89
+ );
90
+
91
+ // M001 should still be in the registry
92
+ const m001Entry = state.registry.find((m) => m.id === "M001");
93
+ assertTrue(
94
+ m001Entry !== undefined,
95
+ "M001 (DB milestone) should still appear in state.registry",
96
+ );
97
+
98
+ // The active milestone should be M002 (since M001 is complete)
99
+ assertTrue(
100
+ state.activeMilestone !== null,
101
+ "There should be an active milestone",
102
+ );
103
+ if (state.activeMilestone) {
104
+ assertEq(
105
+ state.activeMilestone.id,
106
+ "M002",
107
+ "Active milestone should be M002 (disk-only, not complete) (#2416)",
108
+ );
109
+ }
110
+ } finally {
111
+ closeDatabase();
112
+ cleanup(base);
113
+ }
114
+
115
+ report();
116
+ }
117
+
118
+ main().catch((err) => {
119
+ console.error(err);
120
+ process.exit(1);
121
+ });
@@ -11,6 +11,7 @@ import {
11
11
  insertArtifact,
12
12
  isDbAvailable,
13
13
  insertMilestone,
14
+ getAllMilestones,
14
15
  insertSlice,
15
16
  insertTask,
16
17
  } from '../gsd-db.ts';
@@ -962,4 +963,63 @@ describe('derive-state-db', async () => {
962
963
  cleanup(base);
963
964
  }
964
965
  });
966
+
967
+ // ─── Regression: disk-only milestones synced into DB (#2416) ─────────
968
+ test('derive-state-db: disk-only milestone auto-synced into DB (#2416)', async () => {
969
+ const base = createFixtureBase();
970
+ try {
971
+ // M001 is complete and exists in DB. M002 was queued on disk only — no DB row.
972
+ writeFile(base, 'milestones/M001/M001-SUMMARY.md', '# M001 Summary\n\nDone.');
973
+ writeFile(base, 'milestones/M002/M002-CONTEXT.md', '# M002: Queued\n\nQueued milestone.');
974
+
975
+ openDatabase(':memory:');
976
+ // Only insert M001 — simulates the state after migration guard ran then /gsd queue added M002
977
+ insertMilestone({ id: 'M001', title: 'First', status: 'complete' });
978
+
979
+ invalidateStateCache();
980
+ const state = await deriveStateFromDb(base);
981
+
982
+ // Before the fix, M002 was invisible: getAllMilestones() returned only M001
983
+ // (complete) → phase='complete' → auto-mode stopped.
984
+ // After the fix, deriveStateFromDb reconciles disk dirs and inserts M002.
985
+ assert.deepStrictEqual(state.phase, 'pre-planning', 'disk-sync-2416: phase is pre-planning, not complete');
986
+ assert.deepStrictEqual(state.registry.length, 2, 'disk-sync-2416: both milestones visible in registry');
987
+ assert.deepStrictEqual(state.registry[0]?.id, 'M001', 'disk-sync-2416: registry[0] is M001');
988
+ assert.deepStrictEqual(state.registry[0]?.status, 'complete', 'disk-sync-2416: M001 is complete');
989
+ assert.deepStrictEqual(state.registry[1]?.id, 'M002', 'disk-sync-2416: registry[1] is M002');
990
+ assert.deepStrictEqual(state.registry[1]?.status, 'active', 'disk-sync-2416: M002 is active');
991
+ assert.deepStrictEqual(state.activeMilestone?.id, 'M002', 'disk-sync-2416: activeMilestone is M002');
992
+
993
+ closeDatabase();
994
+ } finally {
995
+ closeDatabase();
996
+ cleanup(base);
997
+ }
998
+ });
999
+
1000
+ // ─── Queued milestone row not clobbered by later plan (#2416 root cause) ──
1001
+ test('derive-state-db: queued milestone row survives gsd_plan_milestone INSERT OR IGNORE', async () => {
1002
+ try {
1003
+ openDatabase(':memory:');
1004
+
1005
+ // Simulates gsd_milestone_generate_id inserting a minimal queued row
1006
+ insertMilestone({ id: 'M001', status: 'queued' });
1007
+
1008
+ const before = getAllMilestones();
1009
+ assert.equal(before.length, 1, 'queued-row: one row after generate_id');
1010
+ assert.equal(before[0]!.status, 'queued', 'queued-row: status is queued');
1011
+
1012
+ // Simulates gsd_plan_milestone calling insertMilestone (INSERT OR IGNORE)
1013
+ insertMilestone({ id: 'M001', title: 'Planned Title', status: 'active' });
1014
+
1015
+ const after = getAllMilestones();
1016
+ assert.equal(after.length, 1, 'queued-row: still one row after plan');
1017
+ // INSERT OR IGNORE keeps the original row — status stays 'queued'
1018
+ assert.equal(after[0]!.status, 'queued', 'queued-row: INSERT OR IGNORE preserves original status');
1019
+
1020
+ closeDatabase();
1021
+ } finally {
1022
+ closeDatabase();
1023
+ }
1024
+ });
965
1025
  });
@@ -0,0 +1,120 @@
1
+ /**
2
+ * est-annotation-timeout.test.ts — Regression tests for #2243.
3
+ *
4
+ * Tasks with `est: 30m` or `est: 2h` annotations should get extended
5
+ * supervision timeouts. The parseEstimateMinutes helper should parse
6
+ * estimate strings, and startUnitSupervision should use them.
7
+ */
8
+
9
+ import test from "node:test";
10
+ import assert from "node:assert/strict";
11
+ import { readFileSync } from "node:fs";
12
+ import { join } from "node:path";
13
+
14
+ const timersSrcPath = join(import.meta.dirname, "..", "auto-timers.ts");
15
+ const timersSrc = readFileSync(timersSrcPath, "utf-8");
16
+
17
+ // ─── Source analysis: parseEstimateMinutes exists and is exported ────────────
18
+
19
+ test("#2243: auto-timers.ts should export parseEstimateMinutes", () => {
20
+ assert.ok(
21
+ timersSrc.includes("export function parseEstimateMinutes"),
22
+ "parseEstimateMinutes should be exported from auto-timers.ts",
23
+ );
24
+ });
25
+
26
+ // ─── Inline unit test of parseEstimateMinutes logic ─────────────────────────
27
+ // Since importing the module pulls in heavy deps, test the parsing logic inline.
28
+
29
+ function parseEstimateMinutes(estimate: string): number | null {
30
+ if (!estimate || typeof estimate !== "string") return null;
31
+ const trimmed = estimate.trim();
32
+ if (!trimmed) return null;
33
+
34
+ let totalMinutes = 0;
35
+ let matched = false;
36
+
37
+ const hoursMatch = trimmed.match(/(\d+)\s*h/i);
38
+ if (hoursMatch) {
39
+ totalMinutes += Number(hoursMatch[1]) * 60;
40
+ matched = true;
41
+ }
42
+
43
+ const minutesMatch = trimmed.match(/(\d+)\s*m/i);
44
+ if (minutesMatch) {
45
+ totalMinutes += Number(minutesMatch[1]);
46
+ matched = true;
47
+ }
48
+
49
+ return matched ? totalMinutes : null;
50
+ }
51
+
52
+ test("#2243: parseEstimateMinutes parses '30m' correctly", () => {
53
+ assert.equal(parseEstimateMinutes("30m"), 30);
54
+ });
55
+
56
+ test("#2243: parseEstimateMinutes parses '2h' correctly", () => {
57
+ assert.equal(parseEstimateMinutes("2h"), 120);
58
+ });
59
+
60
+ test("#2243: parseEstimateMinutes parses '1h30m' correctly", () => {
61
+ assert.equal(parseEstimateMinutes("1h30m"), 90);
62
+ });
63
+
64
+ test("#2243: parseEstimateMinutes parses '15m' correctly", () => {
65
+ assert.equal(parseEstimateMinutes("15m"), 15);
66
+ });
67
+
68
+ test("#2243: parseEstimateMinutes returns null for empty string", () => {
69
+ assert.equal(parseEstimateMinutes(""), null);
70
+ });
71
+
72
+ test("#2243: parseEstimateMinutes returns null for invalid string", () => {
73
+ assert.equal(parseEstimateMinutes("not a time"), null);
74
+ });
75
+
76
+ // ─── Source analysis: startUnitSupervision uses task estimates ───────────────
77
+
78
+ test("#2243: startUnitSupervision should reference task estimates for timeout scaling", () => {
79
+ const usesEstimate =
80
+ timersSrc.includes("parseEstimateMinutes") &&
81
+ timersSrc.includes("estimateMinutes") &&
82
+ timersSrc.includes("taskEstimate");
83
+
84
+ assert.ok(
85
+ usesEstimate,
86
+ "startUnitSupervision should use task estimate annotations for timeout scaling",
87
+ );
88
+ });
89
+
90
+ test("#2243: SupervisionContext should accept an optional taskEstimate field", () => {
91
+ const ctxIdx = timersSrc.indexOf("SupervisionContext");
92
+ assert.ok(ctxIdx !== -1, "SupervisionContext interface exists");
93
+
94
+ const ctxEnd = timersSrc.indexOf("}", ctxIdx);
95
+ const ctxBlock = timersSrc.slice(ctxIdx, ctxEnd);
96
+
97
+ assert.ok(
98
+ ctxBlock.includes("taskEstimate"),
99
+ "SupervisionContext should include a taskEstimate field",
100
+ );
101
+ });
102
+
103
+ test("#2243: timeouts should be scaled by estimate (timeoutScale in source)", () => {
104
+ assert.ok(
105
+ timersSrc.includes("timeoutScale"),
106
+ "auto-timers.ts should use a timeoutScale factor derived from est: annotations",
107
+ );
108
+ });
109
+
110
+ test("#2243: idle timeout should NOT be scaled (idle is idle regardless of estimate)", () => {
111
+ // Find the idleTimeoutMs line
112
+ const idleIdx = timersSrc.indexOf("const idleTimeoutMs");
113
+ assert.ok(idleIdx !== -1, "idleTimeoutMs variable exists");
114
+
115
+ const idleLine = timersSrc.slice(idleIdx, timersSrc.indexOf("\n", idleIdx));
116
+ assert.ok(
117
+ !idleLine.includes("timeoutScale"),
118
+ "idleTimeoutMs should NOT be scaled — idle is idle",
119
+ );
120
+ });
@@ -64,7 +64,7 @@ describe('gsd-db', () => {
64
64
  // Check schema_version table
65
65
  const adapter = _getAdapter()!;
66
66
  const version = adapter.prepare('SELECT MAX(version) as version FROM schema_version').get();
67
- assert.deepStrictEqual(version?.['version'], 10, 'schema version should be 10');
67
+ assert.deepStrictEqual(version?.['version'], 11, 'schema version should be 11');
68
68
 
69
69
  // Check tables exist by querying them
70
70
  const dRows = adapter.prepare('SELECT count(*) as cnt FROM decisions').get();
@@ -7,7 +7,7 @@ import {
7
7
  writeBlockerPlaceholder,
8
8
  verifyExpectedArtifact,
9
9
  buildLoopRemediationSteps,
10
- } from "../auto.ts";
10
+ } from "../auto-recovery.ts";
11
11
  import { describe, test, beforeEach, afterEach } from 'node:test';
12
12
  import assert from 'node:assert/strict';
13
13
 
@@ -7,10 +7,13 @@ import { isInfrastructureError, INFRA_ERROR_CODES } from "../auto/infra-errors.j
7
7
  // ── INFRA_ERROR_CODES constant ───────────────────────────────────────────────
8
8
 
9
9
  test("INFRA_ERROR_CODES contains the expected codes", () => {
10
- for (const code of ["ENOSPC", "ENOMEM", "EROFS", "EDQUOT", "EMFILE", "ENFILE"]) {
10
+ for (const code of [
11
+ "ENOSPC", "ENOMEM", "EROFS", "EDQUOT", "EMFILE", "ENFILE",
12
+ "ECONNREFUSED", "ENOTFOUND", "ENETUNREACH",
13
+ ]) {
11
14
  assert.ok(INFRA_ERROR_CODES.has(code), `missing ${code}`);
12
15
  }
13
- assert.equal(INFRA_ERROR_CODES.size, 6, "unexpected extra codes");
16
+ assert.equal(INFRA_ERROR_CODES.size, 9, "unexpected extra codes");
14
17
  });
15
18
 
16
19
  // ── isInfrastructureError: code property detection ───────────────────────────
@@ -45,6 +48,21 @@ test("detects ENFILE via code property", () => {
45
48
  assert.equal(isInfrastructureError(err), "ENFILE");
46
49
  });
47
50
 
51
+ test("detects ECONNREFUSED via code property", () => {
52
+ const err = Object.assign(new Error("connect ECONNREFUSED 127.0.0.1:3000"), { code: "ECONNREFUSED" });
53
+ assert.equal(isInfrastructureError(err), "ECONNREFUSED");
54
+ });
55
+
56
+ test("detects ENOTFOUND via code property", () => {
57
+ const err = Object.assign(new Error("getaddrinfo ENOTFOUND api.example.com"), { code: "ENOTFOUND" });
58
+ assert.equal(isInfrastructureError(err), "ENOTFOUND");
59
+ });
60
+
61
+ test("detects ENETUNREACH via code property", () => {
62
+ const err = Object.assign(new Error("connect ENETUNREACH 2607:f8b0:4004::"), { code: "ENETUNREACH" });
63
+ assert.equal(isInfrastructureError(err), "ENETUNREACH");
64
+ });
65
+
48
66
  // ── isInfrastructureError: message fallback ──────────────────────────────────
49
67
 
50
68
  test("falls back to message scanning when no code property", () => {