gsd-pi 2.80.0-dev.c5f2443b3 → 2.80.0-dev.fbe7c8c6f

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 (306) hide show
  1. package/dist/resources/.managed-resources-content-hash +1 -1
  2. package/dist/resources/GSD-WORKFLOW.md +2 -2
  3. package/dist/resources/extensions/github-sync/templates.js +39 -8
  4. package/dist/resources/extensions/gsd/auto/loop.js +16 -9
  5. package/dist/resources/extensions/gsd/auto/phases.js +37 -30
  6. package/dist/resources/extensions/gsd/auto/run-unit.js +19 -15
  7. package/dist/resources/extensions/gsd/auto-dashboard.js +51 -15
  8. package/dist/resources/extensions/gsd/auto-dispatch.js +10 -0
  9. package/dist/resources/extensions/gsd/auto-post-unit.js +10 -10
  10. package/dist/resources/extensions/gsd/auto-prompts.js +111 -1
  11. package/dist/resources/extensions/gsd/auto-recovery.js +154 -8
  12. package/dist/resources/extensions/gsd/auto-start.js +2 -3
  13. package/dist/resources/extensions/gsd/auto.js +9 -1
  14. package/dist/resources/extensions/gsd/bootstrap/agent-end-recovery.js +15 -1
  15. package/dist/resources/extensions/gsd/bootstrap/register-hooks.js +11 -1
  16. package/dist/resources/extensions/gsd/bootstrap/write-gate.js +129 -1
  17. package/dist/resources/extensions/gsd/clean-root-preflight.js +42 -4
  18. package/dist/resources/extensions/gsd/commands/dispatcher.js +5 -0
  19. package/dist/resources/extensions/gsd/commands-extract-learnings.js +17 -12
  20. package/dist/resources/extensions/gsd/custom-workflow-engine.js +22 -2
  21. package/dist/resources/extensions/gsd/db-base-schema.js +14 -0
  22. package/dist/resources/extensions/gsd/db-migration-steps.js +16 -0
  23. package/dist/resources/extensions/gsd/detection.js +106 -0
  24. package/dist/resources/extensions/gsd/graph.js +9 -3
  25. package/dist/resources/extensions/gsd/gsd-db.js +102 -2
  26. package/dist/resources/extensions/gsd/guided-flow.js +33 -9
  27. package/dist/resources/extensions/gsd/pr-evidence.js +57 -16
  28. package/dist/resources/extensions/gsd/prompts/complete-milestone.md +7 -8
  29. package/dist/resources/extensions/gsd/prompts/plan-milestone.md +3 -1
  30. package/dist/resources/extensions/gsd/safety/evidence-collector.js +10 -2
  31. package/dist/resources/extensions/gsd/working-output-messages.js +64 -0
  32. package/dist/resources/extensions/gsd/worktree-manager.js +16 -14
  33. package/dist/tsconfig.extensions.tsbuildinfo +1 -1
  34. package/dist/web/standalone/.next/BUILD_ID +1 -1
  35. package/dist/web/standalone/.next/app-path-routes-manifest.json +13 -13
  36. package/dist/web/standalone/.next/build-manifest.json +3 -3
  37. package/dist/web/standalone/.next/prerender-manifest.json +3 -3
  38. package/dist/web/standalone/.next/react-loadable-manifest.json +1 -1
  39. package/dist/web/standalone/.next/server/app/_global-error.html +1 -1
  40. package/dist/web/standalone/.next/server/app/_global-error.rsc +1 -1
  41. package/dist/web/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  42. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
  43. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
  44. package/dist/web/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  45. package/dist/web/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  46. package/dist/web/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  47. package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
  48. package/dist/web/standalone/.next/server/app/_not-found.rsc +1 -1
  49. package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +1 -1
  50. package/dist/web/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  51. package/dist/web/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +1 -1
  52. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  53. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  54. package/dist/web/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
  55. package/dist/web/standalone/.next/server/app/api/onboarding/route.js +1 -1
  56. package/dist/web/standalone/.next/server/app/index.html +1 -1
  57. package/dist/web/standalone/.next/server/app/index.rsc +1 -1
  58. package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +1 -1
  59. package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +1 -1
  60. package/dist/web/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
  61. package/dist/web/standalone/.next/server/app/index.segments/_index.segment.rsc +1 -1
  62. package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
  63. package/dist/web/standalone/.next/server/app-paths-manifest.json +13 -13
  64. package/dist/web/standalone/.next/server/chunks/6897.js +3 -3
  65. package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
  66. package/dist/web/standalone/.next/server/middleware-react-loadable-manifest.js +1 -1
  67. package/dist/web/standalone/.next/server/pages/404.html +1 -1
  68. package/dist/web/standalone/.next/server/pages/500.html +1 -1
  69. package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
  70. package/dist/web/standalone/.next/static/chunks/{8336.6f6f30e410419aff.js → 8336.631939fb583761fa.js} +1 -1
  71. package/dist/web/standalone/.next/static/chunks/{webpack-d82dbee6356c1733.js → webpack-0481f1221120a7c6.js} +1 -1
  72. package/package.json +10 -6
  73. package/packages/contracts/package.json +1 -1
  74. package/packages/pi-ai/dist/models/fake-model.d.ts +12 -0
  75. package/packages/pi-ai/dist/models/fake-model.d.ts.map +1 -0
  76. package/packages/pi-ai/dist/models/fake-model.js +27 -0
  77. package/packages/pi-ai/dist/models/fake-model.js.map +1 -0
  78. package/packages/pi-ai/dist/models/index.d.ts.map +1 -1
  79. package/packages/pi-ai/dist/models/index.js +8 -0
  80. package/packages/pi-ai/dist/models/index.js.map +1 -1
  81. package/packages/pi-ai/dist/providers/fake.d.ts +42 -0
  82. package/packages/pi-ai/dist/providers/fake.d.ts.map +1 -0
  83. package/packages/pi-ai/dist/providers/fake.js +319 -0
  84. package/packages/pi-ai/dist/providers/fake.js.map +1 -0
  85. package/packages/pi-ai/dist/providers/register-builtins.d.ts.map +1 -1
  86. package/packages/pi-ai/dist/providers/register-builtins.js +24 -0
  87. package/packages/pi-ai/dist/providers/register-builtins.js.map +1 -1
  88. package/packages/pi-ai/src/models/fake-model.ts +30 -0
  89. package/packages/pi-ai/src/models/index.ts +9 -0
  90. package/packages/pi-ai/src/providers/fake.ts +376 -0
  91. package/packages/pi-ai/src/providers/register-builtins.ts +23 -0
  92. package/packages/pi-ai/tsconfig.tsbuildinfo +1 -1
  93. package/packages/pi-coding-agent/dist/core/chat-controller-ordering.test.js +74 -0
  94. package/packages/pi-coding-agent/dist/core/chat-controller-ordering.test.js.map +1 -1
  95. package/packages/pi-coding-agent/dist/core/extensions/runner.d.ts +2 -0
  96. package/packages/pi-coding-agent/dist/core/extensions/runner.d.ts.map +1 -1
  97. package/packages/pi-coding-agent/dist/core/extensions/runner.js +14 -1
  98. package/packages/pi-coding-agent/dist/core/extensions/runner.js.map +1 -1
  99. package/packages/pi-coding-agent/dist/core/extensions/runner.test.js +97 -0
  100. package/packages/pi-coding-agent/dist/core/extensions/runner.test.js.map +1 -1
  101. package/packages/pi-coding-agent/dist/core/model-registry.d.ts.map +1 -1
  102. package/packages/pi-coding-agent/dist/core/model-registry.js +5 -0
  103. package/packages/pi-coding-agent/dist/core/model-registry.js.map +1 -1
  104. package/packages/pi-coding-agent/dist/core/settings-manager.d.ts +4 -0
  105. package/packages/pi-coding-agent/dist/core/settings-manager.d.ts.map +1 -1
  106. package/packages/pi-coding-agent/dist/core/settings-manager.js +8 -0
  107. package/packages/pi-coding-agent/dist/core/settings-manager.js.map +1 -1
  108. package/packages/pi-coding-agent/dist/core/slash-commands.d.ts.map +1 -1
  109. package/packages/pi-coding-agent/dist/core/slash-commands.js +1 -0
  110. package/packages/pi-coding-agent/dist/core/slash-commands.js.map +1 -1
  111. package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/chat-frame-compaction-tone.test.js +6 -4
  112. package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/chat-frame-compaction-tone.test.js.map +1 -1
  113. package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/tool-execution.test.js +54 -15
  114. package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/tool-execution.test.js.map +1 -1
  115. package/packages/pi-coding-agent/dist/modes/interactive/components/adaptive-layout.d.ts +26 -0
  116. package/packages/pi-coding-agent/dist/modes/interactive/components/adaptive-layout.d.ts.map +1 -0
  117. package/packages/pi-coding-agent/dist/modes/interactive/components/adaptive-layout.js +112 -0
  118. package/packages/pi-coding-agent/dist/modes/interactive/components/adaptive-layout.js.map +1 -0
  119. package/packages/pi-coding-agent/dist/modes/interactive/components/adaptive-layout.test.d.ts +2 -0
  120. package/packages/pi-coding-agent/dist/modes/interactive/components/adaptive-layout.test.d.ts.map +1 -0
  121. package/packages/pi-coding-agent/dist/modes/interactive/components/adaptive-layout.test.js +51 -0
  122. package/packages/pi-coding-agent/dist/modes/interactive/components/adaptive-layout.test.js.map +1 -0
  123. package/packages/pi-coding-agent/dist/modes/interactive/components/assistant-message.d.ts.map +1 -1
  124. package/packages/pi-coding-agent/dist/modes/interactive/components/assistant-message.js.map +1 -1
  125. package/packages/pi-coding-agent/dist/modes/interactive/components/chat-frame.d.ts.map +1 -1
  126. package/packages/pi-coding-agent/dist/modes/interactive/components/chat-frame.js +10 -9
  127. package/packages/pi-coding-agent/dist/modes/interactive/components/chat-frame.js.map +1 -1
  128. package/packages/pi-coding-agent/dist/modes/interactive/components/settings-selector.d.ts +3 -0
  129. package/packages/pi-coding-agent/dist/modes/interactive/components/settings-selector.d.ts.map +1 -1
  130. package/packages/pi-coding-agent/dist/modes/interactive/components/settings-selector.js +11 -0
  131. package/packages/pi-coding-agent/dist/modes/interactive/components/settings-selector.js.map +1 -1
  132. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-card-cleanup-and-success-runtime.test.js +7 -6
  133. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-card-cleanup-and-success-runtime.test.js.map +1 -1
  134. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.d.ts +16 -0
  135. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
  136. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.js +106 -17
  137. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.js.map +1 -1
  138. package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.d.ts.map +1 -1
  139. package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.js +60 -1
  140. package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.js.map +1 -1
  141. package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.test.js +40 -1
  142. package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.test.js.map +1 -1
  143. package/packages/pi-coding-agent/dist/modes/interactive/controllers/input-controller.test.js +1 -0
  144. package/packages/pi-coding-agent/dist/modes/interactive/controllers/input-controller.test.js.map +1 -1
  145. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode-state.d.ts +1 -0
  146. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode-state.d.ts.map +1 -1
  147. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode-state.js.map +1 -1
  148. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.d.ts +3 -0
  149. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  150. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js +23 -0
  151. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js.map +1 -1
  152. package/packages/pi-coding-agent/dist/modes/interactive/slash-command-handlers.d.ts.map +1 -1
  153. package/packages/pi-coding-agent/dist/modes/interactive/slash-command-handlers.js +20 -0
  154. package/packages/pi-coding-agent/dist/modes/interactive/slash-command-handlers.js.map +1 -1
  155. package/packages/pi-coding-agent/dist/modes/interactive/slash-command-handlers.test.d.ts +2 -0
  156. package/packages/pi-coding-agent/dist/modes/interactive/slash-command-handlers.test.d.ts.map +1 -0
  157. package/packages/pi-coding-agent/dist/modes/interactive/slash-command-handlers.test.js +79 -0
  158. package/packages/pi-coding-agent/dist/modes/interactive/slash-command-handlers.test.js.map +1 -0
  159. package/packages/pi-coding-agent/dist/modes/interactive/theme/theme-schema.d.ts +12 -0
  160. package/packages/pi-coding-agent/dist/modes/interactive/theme/theme-schema.d.ts.map +1 -1
  161. package/packages/pi-coding-agent/dist/modes/interactive/theme/theme-schema.js +13 -0
  162. package/packages/pi-coding-agent/dist/modes/interactive/theme/theme-schema.js.map +1 -1
  163. package/packages/pi-coding-agent/dist/modes/interactive/theme/theme.d.ts +1 -1
  164. package/packages/pi-coding-agent/dist/modes/interactive/theme/theme.d.ts.map +1 -1
  165. package/packages/pi-coding-agent/dist/modes/interactive/theme/theme.js +18 -1
  166. package/packages/pi-coding-agent/dist/modes/interactive/theme/theme.js.map +1 -1
  167. package/packages/pi-coding-agent/dist/modes/interactive/theme/themes.d.ts.map +1 -1
  168. package/packages/pi-coding-agent/dist/modes/interactive/theme/themes.js +36 -27
  169. package/packages/pi-coding-agent/dist/modes/interactive/theme/themes.js.map +1 -1
  170. package/packages/pi-coding-agent/dist/modes/interactive/tui-mode.d.ts +11 -0
  171. package/packages/pi-coding-agent/dist/modes/interactive/tui-mode.d.ts.map +1 -0
  172. package/packages/pi-coding-agent/dist/modes/interactive/tui-mode.js +18 -0
  173. package/packages/pi-coding-agent/dist/modes/interactive/tui-mode.js.map +1 -0
  174. package/packages/pi-coding-agent/dist/modes/interactive/tui-mode.test.d.ts +2 -0
  175. package/packages/pi-coding-agent/dist/modes/interactive/tui-mode.test.d.ts.map +1 -0
  176. package/packages/pi-coding-agent/dist/modes/interactive/tui-mode.test.js +48 -0
  177. package/packages/pi-coding-agent/dist/modes/interactive/tui-mode.test.js.map +1 -0
  178. package/packages/pi-coding-agent/src/core/chat-controller-ordering.test.ts +87 -0
  179. package/packages/pi-coding-agent/src/core/extensions/runner.test.ts +108 -0
  180. package/packages/pi-coding-agent/src/core/extensions/runner.ts +16 -1
  181. package/packages/pi-coding-agent/src/core/model-registry.ts +4 -0
  182. package/packages/pi-coding-agent/src/core/settings-manager.ts +12 -0
  183. package/packages/pi-coding-agent/src/core/slash-commands.ts +1 -0
  184. package/packages/pi-coding-agent/src/modes/interactive/components/__tests__/chat-frame-compaction-tone.test.ts +7 -5
  185. package/packages/pi-coding-agent/src/modes/interactive/components/__tests__/tool-execution.test.ts +78 -15
  186. package/packages/pi-coding-agent/src/modes/interactive/components/adaptive-layout.test.ts +59 -0
  187. package/packages/pi-coding-agent/src/modes/interactive/components/adaptive-layout.ts +160 -0
  188. package/packages/pi-coding-agent/src/modes/interactive/components/assistant-message.ts +1 -0
  189. package/packages/pi-coding-agent/src/modes/interactive/components/chat-frame.ts +10 -9
  190. package/packages/pi-coding-agent/src/modes/interactive/components/settings-selector.ts +15 -0
  191. package/packages/pi-coding-agent/src/modes/interactive/components/tool-card-cleanup-and-success-runtime.test.ts +10 -9
  192. package/packages/pi-coding-agent/src/modes/interactive/components/tool-execution.ts +118 -17
  193. package/packages/pi-coding-agent/src/modes/interactive/controllers/chat-controller.test.ts +43 -1
  194. package/packages/pi-coding-agent/src/modes/interactive/controllers/chat-controller.ts +75 -1
  195. package/packages/pi-coding-agent/src/modes/interactive/controllers/input-controller.test.ts +1 -0
  196. package/packages/pi-coding-agent/src/modes/interactive/interactive-mode-state.ts +1 -1
  197. package/packages/pi-coding-agent/src/modes/interactive/interactive-mode.ts +25 -0
  198. package/packages/pi-coding-agent/src/modes/interactive/slash-command-handlers.test.ts +95 -0
  199. package/packages/pi-coding-agent/src/modes/interactive/slash-command-handlers.ts +24 -1
  200. package/packages/pi-coding-agent/src/modes/interactive/theme/theme-schema.ts +13 -0
  201. package/packages/pi-coding-agent/src/modes/interactive/theme/theme.ts +32 -2
  202. package/packages/pi-coding-agent/src/modes/interactive/theme/themes.ts +36 -27
  203. package/packages/pi-coding-agent/src/modes/interactive/tui-mode.test.ts +65 -0
  204. package/packages/pi-coding-agent/src/modes/interactive/tui-mode.ts +29 -0
  205. package/packages/pi-coding-agent/tsconfig.tsbuildinfo +1 -1
  206. package/packages/pi-tui/dist/__tests__/style.test.d.ts +2 -0
  207. package/packages/pi-tui/dist/__tests__/style.test.d.ts.map +1 -0
  208. package/packages/pi-tui/dist/__tests__/style.test.js +63 -0
  209. package/packages/pi-tui/dist/__tests__/style.test.js.map +1 -0
  210. package/packages/pi-tui/dist/__tests__/tui.test.js +24 -3
  211. package/packages/pi-tui/dist/__tests__/tui.test.js.map +1 -1
  212. package/packages/pi-tui/dist/index.d.ts +1 -0
  213. package/packages/pi-tui/dist/index.d.ts.map +1 -1
  214. package/packages/pi-tui/dist/index.js +2 -0
  215. package/packages/pi-tui/dist/index.js.map +1 -1
  216. package/packages/pi-tui/dist/style.d.ts +41 -0
  217. package/packages/pi-tui/dist/style.d.ts.map +1 -0
  218. package/packages/pi-tui/dist/style.js +158 -0
  219. package/packages/pi-tui/dist/style.js.map +1 -0
  220. package/packages/pi-tui/dist/tui.d.ts +0 -1
  221. package/packages/pi-tui/dist/tui.d.ts.map +1 -1
  222. package/packages/pi-tui/dist/tui.js +3 -8
  223. package/packages/pi-tui/dist/tui.js.map +1 -1
  224. package/packages/pi-tui/src/__tests__/style.test.ts +76 -0
  225. package/packages/pi-tui/src/__tests__/tui.test.ts +29 -3
  226. package/packages/pi-tui/src/index.ts +9 -0
  227. package/packages/pi-tui/src/style.ts +225 -0
  228. package/packages/pi-tui/src/tui.ts +3 -8
  229. package/packages/pi-tui/tsconfig.tsbuildinfo +1 -1
  230. package/pkg/dist/modes/interactive/theme/theme-schema.d.ts +12 -0
  231. package/pkg/dist/modes/interactive/theme/theme-schema.d.ts.map +1 -1
  232. package/pkg/dist/modes/interactive/theme/theme-schema.js +13 -0
  233. package/pkg/dist/modes/interactive/theme/theme-schema.js.map +1 -1
  234. package/pkg/dist/modes/interactive/theme/theme.d.ts +1 -1
  235. package/pkg/dist/modes/interactive/theme/theme.d.ts.map +1 -1
  236. package/pkg/dist/modes/interactive/theme/theme.js +18 -1
  237. package/pkg/dist/modes/interactive/theme/theme.js.map +1 -1
  238. package/pkg/dist/modes/interactive/theme/themes.d.ts.map +1 -1
  239. package/pkg/dist/modes/interactive/theme/themes.js +36 -27
  240. package/pkg/dist/modes/interactive/theme/themes.js.map +1 -1
  241. package/src/resources/GSD-WORKFLOW.md +2 -2
  242. package/src/resources/extensions/github-sync/templates.ts +38 -8
  243. package/src/resources/extensions/github-sync/tests/inline-code.test.ts +66 -0
  244. package/src/resources/extensions/gsd/auto/loop-deps.ts +1 -0
  245. package/src/resources/extensions/gsd/auto/loop.ts +17 -10
  246. package/src/resources/extensions/gsd/auto/phases.ts +42 -28
  247. package/src/resources/extensions/gsd/auto/run-unit.ts +24 -14
  248. package/src/resources/extensions/gsd/auto-dashboard.ts +57 -8
  249. package/src/resources/extensions/gsd/auto-dispatch.ts +17 -0
  250. package/src/resources/extensions/gsd/auto-post-unit.ts +10 -10
  251. package/src/resources/extensions/gsd/auto-prompts.ts +116 -1
  252. package/src/resources/extensions/gsd/auto-recovery.ts +153 -7
  253. package/src/resources/extensions/gsd/auto-start.ts +7 -6
  254. package/src/resources/extensions/gsd/auto.ts +12 -1
  255. package/src/resources/extensions/gsd/bootstrap/agent-end-recovery.ts +16 -1
  256. package/src/resources/extensions/gsd/bootstrap/register-hooks.ts +17 -1
  257. package/src/resources/extensions/gsd/bootstrap/write-gate.ts +135 -1
  258. package/src/resources/extensions/gsd/clean-root-preflight.ts +41 -3
  259. package/src/resources/extensions/gsd/commands/dispatcher.ts +6 -0
  260. package/src/resources/extensions/gsd/commands-extract-learnings.ts +17 -12
  261. package/src/resources/extensions/gsd/custom-workflow-engine.ts +24 -1
  262. package/src/resources/extensions/gsd/db-base-schema.ts +15 -0
  263. package/src/resources/extensions/gsd/db-migration-steps.ts +17 -0
  264. package/src/resources/extensions/gsd/detection.ts +128 -0
  265. package/src/resources/extensions/gsd/graph.ts +12 -5
  266. package/src/resources/extensions/gsd/gsd-db.ts +119 -1
  267. package/src/resources/extensions/gsd/guided-flow.ts +30 -9
  268. package/src/resources/extensions/gsd/pr-evidence.ts +63 -5
  269. package/src/resources/extensions/gsd/prompts/complete-milestone.md +7 -8
  270. package/src/resources/extensions/gsd/prompts/plan-milestone.md +3 -1
  271. package/src/resources/extensions/gsd/safety/evidence-collector.ts +11 -2
  272. package/src/resources/extensions/gsd/tests/auto-abort-pause-regression.test.ts +7 -1
  273. package/src/resources/extensions/gsd/tests/auto-dashboard.test.ts +33 -0
  274. package/src/resources/extensions/gsd/tests/auto-loop.test.ts +84 -5
  275. package/src/resources/extensions/gsd/tests/auto-recovery.test.ts +170 -1
  276. package/src/resources/extensions/gsd/tests/clean-root-preflight.test.ts +88 -2
  277. package/src/resources/extensions/gsd/tests/commands-extract-learnings.test.ts +9 -0
  278. package/src/resources/extensions/gsd/tests/custom-engine-loop-integration.test.ts +112 -6
  279. package/src/resources/extensions/gsd/tests/custom-workflow-engine.test.ts +40 -2
  280. package/src/resources/extensions/gsd/tests/db-migration-steps.integration.test.ts +428 -0
  281. package/src/resources/extensions/gsd/tests/db-schema-metadata.test.ts +2 -2
  282. package/src/resources/extensions/gsd/tests/detection.test.ts +140 -0
  283. package/src/resources/extensions/gsd/tests/fixtures/pr-body/commands-ship-basic.md +52 -0
  284. package/src/resources/extensions/gsd/tests/fixtures/pr-body/commands-ship-empty-optionals.md +42 -0
  285. package/src/resources/extensions/gsd/tests/fixtures/pr-body/swarm-lane-no-blockers.md +55 -0
  286. package/src/resources/extensions/gsd/tests/fixtures/pr-body/swarm-lane-with-blockers.md +60 -0
  287. package/src/resources/extensions/gsd/tests/graph-operations.test.ts +10 -0
  288. package/src/resources/extensions/gsd/tests/gsd-db.test.ts +44 -0
  289. package/src/resources/extensions/gsd/tests/has-pending-deep-stage.test.ts +33 -1
  290. package/src/resources/extensions/gsd/tests/pr-evidence-equivalence.test.ts +102 -0
  291. package/src/resources/extensions/gsd/tests/pr-evidence-hardening.test.ts +165 -0
  292. package/src/resources/extensions/gsd/tests/right-sized-workflow-prompts.test.ts +192 -0
  293. package/src/resources/extensions/gsd/tests/safety-harness-false-positives.test.ts +29 -0
  294. package/src/resources/extensions/gsd/tests/smart-entry-complete.test.ts +18 -0
  295. package/src/resources/extensions/gsd/tests/start-auto-detached.test.ts +46 -2
  296. package/src/resources/extensions/gsd/tests/uok-plan-v2-wiring.test.ts +1 -1
  297. package/src/resources/extensions/gsd/tests/working-output-messages.test.ts +93 -0
  298. package/src/resources/extensions/gsd/tests/worktree-health-dispatch.test.ts +37 -6
  299. package/src/resources/extensions/gsd/tests/worktree-manager.test.ts +7 -0
  300. package/src/resources/extensions/gsd/tests/worktree-nested-git-safety.test.ts +9 -2
  301. package/src/resources/extensions/gsd/tests/worktree-write-gate.test.ts +179 -0
  302. package/src/resources/extensions/gsd/working-output-messages.ts +120 -0
  303. package/src/resources/extensions/gsd/worktree-manager.ts +15 -4
  304. package/packages/contracts/tsconfig.tsbuildinfo +0 -1
  305. /package/dist/web/standalone/.next/static/{bQDK5_LtkGVS64AirQgQG → yTuahMMuJzVnsov5PreWl}/_buildManifest.js +0 -0
  306. /package/dist/web/standalone/.next/static/{bQDK5_LtkGVS64AirQgQG → yTuahMMuJzVnsov5PreWl}/_ssgManifest.js +0 -0
@@ -1 +1 @@
1
- 4cdd0ed39f5663d1
1
+ 4b5e4f1bfb55a0a1
@@ -28,7 +28,7 @@ Then do the thing `STATE.md` says to do next.
28
28
  ## The Hierarchy
29
29
 
30
30
  ```
31
- Milestone → a shippable version (4-10 slices)
31
+ Milestone → a shippable version (1-10 slices, sized to the work)
32
32
  Slice → one demoable vertical capability (1-7 tasks)
33
33
  Task → one context-window-sized unit of work (fits in one session)
34
34
  ```
@@ -331,7 +331,7 @@ The **Don't Hand-Roll** and **Common Pitfalls** sections prevent the most expens
331
331
 
332
332
  **For a milestone (roadmap):**
333
333
  1. Read `M###-CONTEXT.md`, `M###-RESEARCH.md`, and `.gsd/DECISIONS.md` if they exist.
334
- 2. Decompose the vision into 4-10 demoable vertical slices.
334
+ 2. Decompose the vision into 1-10 demoable vertical slices. Prefer one slice for tiny, single-file, or static work unless the request clearly spans independent capabilities.
335
335
  3. Order by risk (high-risk first to validate feasibility early).
336
336
  4. Write `M###-ROADMAP.md` with checkboxes, risk levels, dependencies, demo sentences.
337
337
  5. **Write the boundary map** — for each slice, specify what it produces (functions, types, interfaces, endpoints) and what it consumes from upstream slices. This forces interface thinking before implementation and enables deterministic verification that slices actually connect.
@@ -8,6 +8,37 @@
8
8
  * for the `gh` CLI body parameters.
9
9
  */
10
10
  import { buildPrEvidence } from "../gsd/pr-evidence.js";
11
+ // ─── Helpers ────────────────────────────────────────────────────────────────
12
+ /**
13
+ * Wrap a string in a CommonMark inline-code span, escaping any embedded
14
+ * backticks by selecting a fence longer than the longest backtick run inside
15
+ * the input. If the input begins or ends with a backtick, pad with a single
16
+ * space inside the fence (CommonMark requirement).
17
+ *
18
+ * Empty input returns an empty string (no fence) — there is nothing to render
19
+ * as code, and emitting an empty pair of backticks would produce literal
20
+ * backticks in GitHub-flavored markdown.
21
+ */
22
+ export function inlineCode(s) {
23
+ if (s.length === 0)
24
+ return "";
25
+ let longestRun = 0;
26
+ let currentRun = 0;
27
+ for (const ch of s) {
28
+ if (ch === "`") {
29
+ currentRun++;
30
+ if (currentRun > longestRun)
31
+ longestRun = currentRun;
32
+ }
33
+ else {
34
+ currentRun = 0;
35
+ }
36
+ }
37
+ const fence = "`".repeat(longestRun + 1);
38
+ const needsPad = s.startsWith("`") || s.endsWith("`");
39
+ const pad = needsPad ? " " : "";
40
+ return `${fence}${pad}${s}${pad}${fence}`;
41
+ }
11
42
  export function formatMilestoneIssueBody(data) {
12
43
  const lines = [];
13
44
  lines.push(`# ${data.id}: ${data.title}`);
@@ -78,7 +109,7 @@ export function formatTaskIssueBody(data) {
78
109
  if (data.files?.length) {
79
110
  lines.push("### Files");
80
111
  for (const file of data.files) {
81
- lines.push(`- \`${file}\``);
112
+ lines.push(`- ${inlineCode(file)}`);
82
113
  }
83
114
  lines.push("");
84
115
  }
@@ -132,15 +163,14 @@ export function formatSwarmLanePRBody(data) {
132
163
  const summaries = [
133
164
  [
134
165
  "### Swarm lane",
135
- `**Lane:** \`${laneLabel}\``,
136
- `**Branch:** \`${data.lane.branch}\``,
166
+ `**Lane:** ${inlineCode(laneLabel)}`,
167
+ `**Branch:** ${inlineCode(data.lane.branch)}`,
137
168
  data.lane.owner ? `**Owner:** ${data.lane.owner}` : "",
138
- data.lane.latestCommit ? `**Latest commit:** \`${data.lane.latestCommit}\`` : "",
169
+ data.lane.latestCommit ? `**Latest commit:** ${inlineCode(data.lane.latestCommit)}` : "",
139
170
  ].filter(Boolean).join("\n"),
140
171
  `### Impact area\n${data.impactArea}`,
141
172
  `### Changed contracts\n${checkedList(data.lane.changedContracts, "No shared contracts changed").join("\n")}`,
142
173
  `### Transition risks\n${checkedList(data.transitionRisks, "No transition risks identified").join("\n")}`,
143
- data.lane.blockers?.length ? `### Blockers\n${data.lane.blockers.map((blocker) => `- ${blocker}`).join("\n")}` : "",
144
174
  ].filter(Boolean);
145
175
  return buildPrEvidence({
146
176
  milestoneId: laneLabel,
@@ -150,6 +180,7 @@ export function formatSwarmLanePRBody(data) {
150
180
  changeType: "refactor",
151
181
  linkedIssue: data.linkedIssue ? `Closes #${data.linkedIssue}` : undefined,
152
182
  summaries,
183
+ blockers: data.lane.blockers ?? [],
153
184
  testsRun: data.lane.testEvidence,
154
185
  rollbackNotes: data.rollbackPlan,
155
186
  how: "Generated by GSD GitHub Sync swarm routines from lane evidence.",
@@ -159,7 +190,7 @@ export function formatSwarmReleaseChecklistBody(data) {
159
190
  const lines = [];
160
191
  lines.push(`# UOK Swarm Release Checklist`);
161
192
  lines.push("");
162
- lines.push(`**Integration branch:** \`${data.integrationBranch}\``);
193
+ lines.push(`**Integration branch:** ${inlineCode(data.integrationBranch)}`);
163
194
  lines.push("");
164
195
  lines.push("## Lane summary");
165
196
  lines.push("");
@@ -167,9 +198,9 @@ export function formatSwarmReleaseChecklistBody(data) {
167
198
  lines.push("|------|--------|-------|--------|--------|");
168
199
  for (const lane of data.lanes) {
169
200
  const owner = lane.owner ?? "";
170
- const commit = lane.latestCommit ? `\`${lane.latestCommit}\`` : "";
201
+ const commit = lane.latestCommit ? inlineCode(lane.latestCommit) : "";
171
202
  const status = lane.blockers?.length ? "blocked" : "ready";
172
- lines.push(`| \`${SWARM_LANE_LABELS[lane.id]}\` | \`${lane.branch}\` | ${owner} | ${commit} | ${status} |`);
203
+ lines.push(`| ${inlineCode(SWARM_LANE_LABELS[lane.id])} | ${inlineCode(lane.branch)} | ${owner} | ${commit} | ${status} |`);
173
204
  }
174
205
  lines.push("");
175
206
  lines.push("## Required evidence");
@@ -268,6 +268,19 @@ export async function autoLoop(ctx, pi, s, deps, options) {
268
268
  // ── Blanket try/catch: one bad iteration must not kill the session
269
269
  const prefs = deps.loadEffectiveGSDPreferences()?.preferences;
270
270
  const uokFlags = resolveUokFlags(prefs);
271
+ // ── Check sidecar queue before deriveState ──
272
+ // NOTE: Sidecar dequeue MUST run before validateWorkflowSessionLock so a
273
+ // queued item is popped (and the `sidecar-dequeue` journal event emitted)
274
+ // even when the session lock invalidates this iteration. Inverting this
275
+ // order silently drops queued items on lock-loss. Refs #5308.
276
+ const sidecarItem = await dequeueSidecarItem({
277
+ queue: s.sidecarQueue,
278
+ executionGraphEnabled: uokFlags.executionGraph,
279
+ scheduleQueue: scheduleSidecarQueue,
280
+ warnSchedulingFailure: message => logWarning("dispatch", `sidecar queue scheduling failed: ${message}`),
281
+ logDequeue: payload => debugLog("autoLoop", { phase: "sidecar-dequeue", ...payload }),
282
+ emitDequeue: payload => journalReporter.emit("sidecar-dequeue", payload),
283
+ });
271
284
  const sessionLockOutcome = validateWorkflowSessionLock({
272
285
  active: s.active,
273
286
  iteration,
@@ -287,17 +300,9 @@ export async function autoLoop(ctx, pi, s, deps, options) {
287
300
  },
288
301
  });
289
302
  if (sessionLockOutcome.action === "stop" && sessionLockOutcome.reason === "session-lock-lost") {
303
+ finishTurn("stopped", "manual-attention", sessionLockOutcome.reason);
290
304
  break;
291
305
  }
292
- // ── Check sidecar queue before deriveState ──
293
- const sidecarItem = await dequeueSidecarItem({
294
- queue: s.sidecarQueue,
295
- executionGraphEnabled: uokFlags.executionGraph,
296
- scheduleQueue: scheduleSidecarQueue,
297
- warnSchedulingFailure: message => logWarning("dispatch", `sidecar queue scheduling failed: ${message}`),
298
- logDequeue: payload => debugLog("autoLoop", { phase: "sidecar-dequeue", ...payload }),
299
- emitDequeue: payload => journalReporter.emit("sidecar-dequeue", payload),
300
- });
301
306
  const ic = { ctx, pi, s, deps, prefs, iteration, flowId, nextSeq };
302
307
  journalReporter.emit("iteration-start", { iteration });
303
308
  let iterData;
@@ -331,6 +336,7 @@ export async function autoLoop(ctx, pi, s, deps, options) {
331
336
  isComplete: engineState.isComplete,
332
337
  });
333
338
  if (engineState.isComplete) {
339
+ finishTurn("completed");
334
340
  await deps.stopAuto(ctx, pi, "Workflow complete");
335
341
  break;
336
342
  }
@@ -734,6 +740,7 @@ export async function autoLoop(ctx, pi, s, deps, options) {
734
740
  });
735
741
  if (cooldownDecision.action === "stop") {
736
742
  ctx.ui.notify(cooldownDecision.notifyMessage, "error");
743
+ finishTurn("stopped", "timeout", msg);
737
744
  await deps.stopAuto(ctx, pi, cooldownDecision.stopMessage);
738
745
  break;
739
746
  }
@@ -13,13 +13,13 @@ import { detectStuck } from "./detect-stuck.js";
13
13
  import { runUnit } from "./run-unit.js";
14
14
  import { debugLog } from "../debug-logger.js";
15
15
  import { resolveWorktreeProjectRoot, normalizeWorktreePathForCompare } from "../worktree-root.js";
16
- import { PROJECT_FILES, hasProjectFileInAncestor } from "../detection.js";
16
+ import { classifyProject } from "../detection.js";
17
17
  import { MergeConflictError } from "../git-service.js";
18
18
  import { setCurrentPhase, clearCurrentPhase } from "../../shared/gsd-phase-state.js";
19
19
  import { pauseAutoForProviderError } from "../provider-error-pause.js";
20
20
  import { resumeAutoAfterProviderDelay } from "../bootstrap/provider-error-resume.js";
21
21
  import { join, basename } from "node:path";
22
- import { existsSync, cpSync, readdirSync } from "node:fs";
22
+ import { existsSync, cpSync } from "node:fs";
23
23
  import { logWarning, logError, _resetLogs, drainLogs, drainAndSummarize, formatForNotification, hasAnyIssues, } from "../workflow-logger.js";
24
24
  import { gsdRoot } from "../paths.js";
25
25
  import { atomicWriteSync } from "../atomic-write.js";
@@ -498,7 +498,7 @@ export async function runPreDispatch(ic, loopState) {
498
498
  }
499
499
  // #2909: postflight — restore stashed changes after successful merge
500
500
  if (preflightTransition.stashPushed) {
501
- deps.postflightPopStash(s.originalBasePath || s.basePath, s.currentMilestoneId, ctx.ui.notify.bind(ctx.ui));
501
+ deps.postflightPopStash(s.originalBasePath || s.basePath, s.currentMilestoneId, preflightTransition.stashMarker, ctx.ui.notify.bind(ctx.ui));
502
502
  }
503
503
  // PR creation (auto_pr) is handled inside mergeMilestoneToMain (#2302)
504
504
  deps.invalidateAllCaches();
@@ -574,7 +574,7 @@ export async function runPreDispatch(ic, loopState) {
574
574
  }
575
575
  // #2909: postflight — restore stashed changes after successful merge
576
576
  if (preflightAllComplete.stashPushed) {
577
- deps.postflightPopStash(s.originalBasePath || s.basePath, s.currentMilestoneId, ctx.ui.notify.bind(ctx.ui));
577
+ deps.postflightPopStash(s.originalBasePath || s.basePath, s.currentMilestoneId, preflightAllComplete.stashMarker, ctx.ui.notify.bind(ctx.ui));
578
578
  }
579
579
  // PR creation (auto_pr) is handled inside mergeMilestoneToMain (#2302)
580
580
  }
@@ -660,7 +660,7 @@ export async function runPreDispatch(ic, loopState) {
660
660
  }
661
661
  // #2909: postflight — restore stashed changes after successful merge
662
662
  if (preflightComplete.stashPushed) {
663
- deps.postflightPopStash(s.originalBasePath || s.basePath, s.currentMilestoneId, ctx.ui.notify.bind(ctx.ui));
663
+ deps.postflightPopStash(s.originalBasePath || s.basePath, s.currentMilestoneId, preflightComplete.stashMarker, ctx.ui.notify.bind(ctx.ui));
664
664
  }
665
665
  // PR creation (auto_pr) is handled inside mergeMilestoneToMain (#2302)
666
666
  }
@@ -1084,8 +1084,9 @@ export async function runUnitPhase(ic, iterData, loopState, sidecarItem) {
1084
1084
  // Verify the working directory is a valid git checkout with project
1085
1085
  // files before dispatching work. A broken worktree causes agents to
1086
1086
  // hallucinate summaries since they cannot read or write any files.
1087
- // Uses the shared PROJECT_FILES list from detection.ts to support all
1088
- // ecosystems (Rust, Go, Python, Java, etc.), not just JS.
1087
+ // Uses project classification so project presence is not conflated with
1088
+ // ecosystem marker detection. Static/minimal repos become untyped-existing.
1089
+ let projectClassification = null;
1089
1090
  if (s.basePath && unitType === "execute-task") {
1090
1091
  const gitMarker = join(s.basePath, ".git");
1091
1092
  const hasGit = deps.existsSync(gitMarker);
@@ -1096,30 +1097,26 @@ export async function runUnitPhase(ic, iterData, loopState, sidecarItem) {
1096
1097
  await deps.stopAuto(ctx, pi, msg);
1097
1098
  return { action: "break", reason: "worktree-invalid" };
1098
1099
  }
1099
- const hasProjectFile = PROJECT_FILES.some((f) => deps.existsSync(join(s.basePath, f)));
1100
- const hasSrcDir = deps.existsSync(join(s.basePath, "src"));
1101
- // Xcode bundles have project-specific names (*.xcodeproj, *.xcworkspace)
1102
- // that cannot be matched by exact filename — scan the directory by suffix.
1103
- let hasXcodeBundle = false;
1104
- try {
1105
- const entries = deps.existsSync(s.basePath) ? readdirSync(s.basePath) : [];
1106
- hasXcodeBundle = entries.some((e) => e.endsWith(".xcodeproj") || e.endsWith(".xcworkspace"));
1100
+ projectClassification = classifyProject(s.basePath);
1101
+ if (projectClassification.kind === "invalid-repo") {
1102
+ const msg = `Worktree health check failed: ${s.basePath} classified as invalid-repo (${projectClassification.reason}) — refusing to dispatch ${unitType} ${unitId}`;
1103
+ debugLog("runUnitPhase", { phase: "worktree-health-invalid-repo", basePath: s.basePath, classification: projectClassification });
1104
+ if (projectClassification.reason === "missing .git" && hasGit) {
1105
+ ctx.ui.notify(`Warning: ${s.basePath} project classification could not confirm .git; assuming it has no project content yet — proceeding as greenfield project because worktree health reported .git present`, "warning");
1106
+ }
1107
+ else {
1108
+ ctx.ui.notify(msg, "error");
1109
+ await deps.stopAuto(ctx, pi, msg);
1110
+ return { action: "break", reason: "worktree-invalid" };
1111
+ }
1107
1112
  }
1108
- catch (err) {
1109
- debugLog("runUnitPhase", { phase: "xcode-bundle-scan-failed", basePath: s.basePath, error: String(err) });
1110
- }
1111
- // Monorepo support (#2347): if no project files in the worktree directory,
1112
- // walk parent directories up to the filesystem root. In monorepos,
1113
- // package.json / Cargo.toml etc. live in a parent directory.
1114
- const hasProjectFileInParent = !hasProjectFile && !hasSrcDir && !hasXcodeBundle
1115
- ? hasProjectFileInAncestor(s.basePath, deps.existsSync)
1116
- : false;
1117
- if (!hasProjectFile && !hasSrcDir && !hasXcodeBundle && !hasProjectFileInParent) {
1118
- // Greenfield projects won't have project files yet — the first task creates them.
1119
- // Log a warning but allow execution to proceed. The .git check above is sufficient
1120
- // to ensure we're in a valid working directory.
1121
- debugLog("runUnitPhase", { phase: "worktree-health-warn-greenfield", basePath: s.basePath, hasProjectFile, hasSrcDir, hasXcodeBundle });
1122
- ctx.ui.notify(`Warning: ${s.basePath} has no recognized project files — proceeding as greenfield project`, "warning");
1113
+ else if (projectClassification.kind === "greenfield") {
1114
+ debugLog("runUnitPhase", { phase: "worktree-health-greenfield", basePath: s.basePath, classification: projectClassification });
1115
+ ctx.ui.notify(`Warning: ${s.basePath} has no project content yet — proceeding as greenfield project`, "warning");
1116
+ }
1117
+ else if (projectClassification.kind === "untyped-existing") {
1118
+ debugLog("runUnitPhase", { phase: "worktree-health-untyped-existing", basePath: s.basePath, classification: projectClassification });
1119
+ ctx.ui.notify(`Notice: ${s.basePath} has existing project content but no recognized tooling markers — using generic file-level workflow guidance`, "info");
1123
1120
  }
1124
1121
  }
1125
1122
  // Detect retry and capture previous tier for escalation
@@ -1182,6 +1179,16 @@ export async function runUnitPhase(ic, iterData, loopState, sidecarItem) {
1182
1179
  }
1183
1180
  // Prompt injection
1184
1181
  let finalPrompt = prompt;
1182
+ if (unitType === "execute-task") {
1183
+ projectClassification ??= classifyProject(s.basePath);
1184
+ if (projectClassification.kind === "untyped-existing") {
1185
+ const samples = projectClassification.contentFiles.slice(0, 8).join(", ") || "project files";
1186
+ finalPrompt +=
1187
+ "\n\n**Project classification:** Existing untyped project. No recognized build/tooling markers were detected, " +
1188
+ "so use generic file-level workflow guidance. Task plans and completion summaries must list every concrete " +
1189
+ `project file changed in \`files\` or \`expected_output\`. Detected content sample: ${samples}.`;
1190
+ }
1191
+ }
1185
1192
  if (s.pendingVerificationRetry) {
1186
1193
  const retryCtx = s.pendingVerificationRetry;
1187
1194
  s.pendingVerificationRetry = null;
@@ -1,14 +1,11 @@
1
- /**
2
- * auto/run-unit.ts — Single unit execution: session create → prompt → await agent_end.
3
- *
4
- * Imports from: auto/types, auto/resolve
5
- */
1
+ // GSD-2 + src/resources/extensions/gsd/auto/run-unit.ts - Runs one GSD auto-mode unit from session creation through agent completion.
6
2
  import { NEW_SESSION_TIMEOUT_MS } from "./session.js";
7
3
  import { _clearCurrentResolve, _setCurrentResolve, _setSessionSwitchInFlight } from "./resolve.js";
8
4
  import { getCurrentTurnGeneration, runWithTurnGeneration, } from "./turn-epoch.js";
9
5
  import { debugLog } from "../debug-logger.js";
10
6
  import { logWarning } from "../workflow-logger.js";
11
7
  import { resolveAutoSupervisorConfig } from "../preferences.js";
8
+ import { formatAutoUnitWorkingMessage } from "../working-output-messages.js";
12
9
  // Tracks the latest session-switch attempt so a late timeout settlement from an
13
10
  // older runUnit() call cannot clear the guard for a newer one.
14
11
  let sessionSwitchGeneration = 0;
@@ -151,22 +148,29 @@ export async function runUnit(ctx, pi, s, unitType, unitId, prompt) {
151
148
  // ── Send the prompt ──
152
149
  debugLog("runUnit", { phase: "send-message", unitType, unitId });
153
150
  const requestDispatchedAt = Date.now();
154
- pi.sendMessage({ customType: "gsd-auto", content: prompt, display: s.verbose }, { triggerTurn: true });
151
+ ctx.ui.setWorkingMessage?.(formatAutoUnitWorkingMessage(unitType, unitId));
155
152
  // ── Await agent_end with absolute timeout (H4 fix) ──
156
153
  // If supervision fails to resolve unitPromise within 30s, treat as cancelled.
157
154
  // Without this, a crashed agent that never emits agent_end hangs the loop (#3161).
158
- debugLog("runUnit", { phase: "awaiting-agent-end", unitType, unitId });
159
155
  const supervisor = resolveAutoSupervisorConfig();
160
156
  const UNIT_HARD_TIMEOUT_MS = Math.max(30_000, ((supervisor.hard_timeout_minutes ?? 30) * 60 * 1000) + 30_000);
161
157
  let unitTimeoutHandle;
162
- const timeoutResult = new Promise((resolve) => {
163
- unitTimeoutHandle = setTimeout(() => {
164
- resolve({ status: "cancelled", errorContext: { message: "Unit hard timeout — supervision may have failed", category: "timeout", isTransient: true } });
165
- }, UNIT_HARD_TIMEOUT_MS);
166
- });
167
- const result = await runWithTurnGeneration(capturedTurnGen, () => Promise.race([unitPromise, timeoutResult]));
168
- if (unitTimeoutHandle)
169
- clearTimeout(unitTimeoutHandle);
158
+ let result;
159
+ try {
160
+ pi.sendMessage({ customType: "gsd-auto", content: prompt, display: s.verbose }, { triggerTurn: true });
161
+ debugLog("runUnit", { phase: "awaiting-agent-end", unitType, unitId });
162
+ const timeoutResult = new Promise((resolve) => {
163
+ unitTimeoutHandle = setTimeout(() => {
164
+ resolve({ status: "cancelled", errorContext: { message: "Unit hard timeout — supervision may have failed", category: "timeout", isTransient: true } });
165
+ }, UNIT_HARD_TIMEOUT_MS);
166
+ });
167
+ result = await runWithTurnGeneration(capturedTurnGen, () => Promise.race([unitPromise, timeoutResult]));
168
+ }
169
+ finally {
170
+ if (unitTimeoutHandle)
171
+ clearTimeout(unitTimeoutHandle);
172
+ ctx.ui.setWorkingMessage?.(undefined);
173
+ }
170
174
  debugLog("runUnit", {
171
175
  phase: "agent-end-received",
172
176
  unitType,
@@ -1,10 +1,4 @@
1
- /**
2
- * Auto-mode Dashboard — progress widget rendering, elapsed time formatting,
3
- * unit description helpers, and slice progress caching.
4
- *
5
- * Pure functions that accept specific parameters — no module-level globals
6
- * or AutoContext dependency. State accessors are passed as callbacks.
7
- */
1
+ // GSD-2 + src/resources/extensions/gsd/auto-dashboard.ts - Auto-mode progress widget rendering and dashboard helpers.
8
2
  import { getCurrentBranch } from "./worktree.js";
9
3
  import { getActiveHook } from "./post-unit-hooks.js";
10
4
  import { getLedger, getProjectTotals } from "./metrics.js";
@@ -25,6 +19,7 @@ import { formatRtkSavingsLabel, getRtkSessionSavings, } from "../shared/rtk-sess
25
19
  import { logWarning } from "./workflow-logger.js";
26
20
  import { formattedShortcutPair } from "./shortcut-defs.js";
27
21
  import { homedir } from "node:os";
22
+ import { readUnitRuntimeRecord } from "./unit-runtime.js";
28
23
  // ─── UAT Slice Extraction ─────────────────────────────────────────────────────
29
24
  /**
30
25
  * Extract the target slice ID from a run-uat unit ID (e.g. "M001/S01" → "S01").
@@ -165,6 +160,30 @@ export function formatWidgetTokens(count) {
165
160
  return `${(count / 1000000).toFixed(1)}M`;
166
161
  return `${Math.round(count / 1000000)}M`;
167
162
  }
163
+ export function formatRuntimeHealthSignal(record, now = Date.now()) {
164
+ if (!record)
165
+ return null;
166
+ const idleMs = Math.max(0, now - record.lastProgressAt);
167
+ const idleMinutes = Math.floor(idleMs / 60_000);
168
+ if ((record.recoveryAttempts ?? 0) > 0 || record.phase === "recovered" || record.lastProgressKind.includes("recovery")) {
169
+ return {
170
+ level: "yellow",
171
+ summary: "Recovering",
172
+ detail: `retry ${record.recoveryAttempts ?? 1} after ${record.lastRecoveryReason ?? "idle"} stall`,
173
+ };
174
+ }
175
+ if (record.progressCount === 0 && idleMs >= 60_000) {
176
+ return {
177
+ level: "yellow",
178
+ summary: "Waiting on provider",
179
+ detail: `no output for ${idleMinutes}m`,
180
+ };
181
+ }
182
+ return null;
183
+ }
184
+ export function shouldRenderRoadmapProgress(progress) {
185
+ return !!progress && progress.total > 0;
186
+ }
168
187
  // ─── ETA Estimation ──────────────────────────────────────────────────────────
169
188
  /**
170
189
  * Estimate remaining time based on average unit duration from the metrics ledger.
@@ -499,6 +518,7 @@ export function updateProgressWidget(ctx, unitType, unitId, state, accessors, ti
499
518
  let cachedLines;
500
519
  let cachedWidth;
501
520
  let cachedRtkLabel;
521
+ let cachedRuntimeRecord = null;
502
522
  const refreshRtkLabel = () => {
503
523
  try {
504
524
  const sessionId = ctx.sessionManager.getSessionId();
@@ -510,7 +530,16 @@ export function updateProgressWidget(ctx, unitType, unitId, state, accessors, ti
510
530
  cachedRtkLabel = null;
511
531
  }
512
532
  };
533
+ const refreshRuntimeRecord = () => {
534
+ try {
535
+ cachedRuntimeRecord = readUnitRuntimeRecord(accessors.getBasePath(), unitType, unitId);
536
+ }
537
+ catch {
538
+ cachedRuntimeRecord = null;
539
+ }
540
+ };
513
541
  refreshRtkLabel();
542
+ refreshRuntimeRecord();
514
543
  const pulseTimer = setInterval(() => {
515
544
  pulseBright = !pulseBright;
516
545
  cachedLines = undefined;
@@ -526,6 +555,7 @@ export function updateProgressWidget(ctx, unitType, unitId, state, accessors, ti
526
555
  updateSliceProgressCache(accessors.getBasePath(), mid.id, slice?.id);
527
556
  }
528
557
  refreshRtkLabel();
558
+ refreshRuntimeRecord();
529
559
  cachedLines = undefined;
530
560
  }
531
561
  catch (err) { /* non-fatal */
@@ -555,13 +585,16 @@ export function updateProgressWidget(ctx, unitType, unitId, state, accessors, ti
555
585
  const modeTag = accessors.isStepMode() ? "NEXT" : "AUTO";
556
586
  // Health indicator in header
557
587
  const score = computeProgressScore();
558
- const healthColor = score.level === "green" ? "success"
559
- : score.level === "yellow" ? "warning"
588
+ const runtimeSignal = formatRuntimeHealthSignal(cachedRuntimeRecord);
589
+ const healthLevel = runtimeSignal?.level ?? score.level;
590
+ const healthSummary = runtimeSignal?.summary ?? score.summary;
591
+ const healthColor = healthLevel === "green" ? "success"
592
+ : healthLevel === "yellow" ? "warning"
560
593
  : "error";
561
- const healthIcon = score.level === "green" ? GLYPH.statusActive
562
- : score.level === "yellow" ? "!"
594
+ const healthIcon = healthLevel === "green" ? GLYPH.statusActive
595
+ : healthLevel === "yellow" ? "!"
563
596
  : "x";
564
- const healthStr = ` ${theme.fg(healthColor, healthIcon)} ${theme.fg(healthColor, score.summary)}`;
597
+ const healthStr = ` ${theme.fg(healthColor, healthIcon)} ${theme.fg(healthColor, healthSummary)}`;
565
598
  const headerLeft = `${pad}${dot} ${theme.fg("accent", theme.bold("GSD"))} ${theme.fg("success", modeTag)}${healthStr}`;
566
599
  // ETA in header right, after elapsed
567
600
  const eta = estimateTimeRemaining();
@@ -573,7 +606,10 @@ export function updateProgressWidget(ctx, unitType, unitId, state, accessors, ti
573
606
  : "";
574
607
  lines.push(rightAlign(headerLeft, headerRight, width));
575
608
  // Show health signal details when degraded (yellow/red)
576
- if (score.level !== "green" && score.signals.length > 0 && widgetMode !== "min") {
609
+ if (runtimeSignal?.detail && widgetMode !== "min") {
610
+ lines.push(`${pad} ${theme.fg("dim", runtimeSignal.detail)}`);
611
+ }
612
+ else if (score.level !== "green" && score.signals.length > 0 && widgetMode !== "min") {
577
613
  // Show up to 3 most relevant signals in compact form
578
614
  const topSignals = score.signals
579
615
  .filter(s => s.kind === "negative")
@@ -645,7 +681,7 @@ export function updateProgressWidget(ctx, unitType, unitId, state, accessors, ti
645
681
  lines.push(rightAlign(actionLeft, theme.fg("dim", phaseLabel), width));
646
682
  // Progress bar
647
683
  const roadmapSlices = mid ? getRoadmapSlicesSync() : null;
648
- if (roadmapSlices) {
684
+ if (shouldRenderRoadmapProgress(roadmapSlices)) {
649
685
  const { done, total, activeSliceTasks } = roadmapSlices;
650
686
  const barWidth = Math.max(6, Math.min(18, Math.floor(width * 0.25)));
651
687
  const pct = total > 0 ? done / total : 0;
@@ -706,7 +742,7 @@ export function updateProgressWidget(ctx, unitType, unitId, state, accessors, ti
706
742
  ? Math.floor(width * (width >= 100 ? 0.45 : 0.50))
707
743
  : width;
708
744
  const leftLines = [];
709
- if (roadmapSlices) {
745
+ if (shouldRenderRoadmapProgress(roadmapSlices)) {
710
746
  const { done, total, activeSliceTasks } = roadmapSlices;
711
747
  const barWidth = Math.max(6, Math.min(18, Math.floor(leftColWidth * 0.4)));
712
748
  const pct = total > 0 ? done / total : 0;
@@ -93,6 +93,16 @@ export function hasPendingDeepStage(prefs, basePath) {
93
93
  const gate = getDeepStageGate(prefs, basePath);
94
94
  return gate.status === "pending" || gate.status === "blocked";
95
95
  }
96
+ export function shouldRunDeepProjectSetup(state, prefs, basePath, options = {}) {
97
+ if (options.hasSurvivorBranch === true)
98
+ return false;
99
+ if (state.phase !== "pre-planning" &&
100
+ state.phase !== "needs-discussion" &&
101
+ state.phase !== "planning") {
102
+ return false;
103
+ }
104
+ return hasPendingDeepStage(prefs, basePath);
105
+ }
96
106
  function missingSliceStop(mid, phase) {
97
107
  return {
98
108
  action: "stop",
@@ -25,7 +25,7 @@ import { verifyExpectedArtifact, resolveExpectedArtifactPath, writeBlockerPlaceh
25
25
  import { regenerateIfMissing } from "./workflow-projections.js";
26
26
  import { syncStateToProjectRoot } from "./auto-worktree.js";
27
27
  import { normalizeWorktreePathForCompare } from "./worktree-root.js";
28
- import { isDbAvailable, getTask, getSlice, getMilestone, updateTaskStatus, _getAdapter } from "./gsd-db.js";
28
+ import { isDbAvailable, getTask, getSlice, getMilestone, updateTaskStatus, _getAdapter, getVerificationEvidence } from "./gsd-db.js";
29
29
  import { renderPlanCheckboxes } from "./markdown-renderer.js";
30
30
  import { consumeSignal } from "./session-status-io.js";
31
31
  import { checkPostUnitHooks, isRetryPending, consumeRetryTrigger, persistHookState, resolveHookArtifactPath, } from "./post-unit-hooks.js";
@@ -719,21 +719,21 @@ export async function postUnitPreVerification(pctx, opts) {
719
719
  }
720
720
  }
721
721
  // Evidence cross-reference (execute-task only)
722
- // Verification evidence is passed via the complete-task tool call and
723
- // stored in the SUMMARY.md on disk not available as structured data
724
- // in the DB. The evidence collector tracks actual bash tool calls, so
725
- // we can still detect units that claimed success but ran no commands.
722
+ // Only compare against concrete command evidence persisted by the task
723
+ // completion tool. A prose Verify field can be satisfied later by the
724
+ // host verification gate, so it is not enough to accuse the unit.
726
725
  if (safetyConfig.evidence_cross_reference && s.currentUnit.type === "execute-task") {
727
726
  try {
728
727
  const actual = getEvidence();
729
728
  const bashCalls = actual.filter(e => e.kind === "bash");
730
- // If the task is marked complete but zero bash commands were run,
731
- // it's suspicious — the LLM may have fabricated results.
732
729
  if (sMid && sSid && sTid && isDbAvailable()) {
733
730
  const taskRow = getTask(sMid, sSid, sTid);
734
- if (taskRow?.status === "complete" && taskRow.verify && bashCalls.length === 0) {
735
- logWarning("safety", "task marked complete with verification commands but no bash calls were executed");
736
- ctx.ui.notify(`Safety: task ${sTid} has verification commands but no bash calls were recorded`, "warning");
731
+ const claimedCommands = getVerificationEvidence(sMid, sSid, sTid)
732
+ .map((row) => row.command)
733
+ .filter((command) => typeof command === "string" && command.trim().length > 0);
734
+ if (taskRow?.status === "complete" && claimedCommands.length > 0 && bashCalls.length === 0) {
735
+ logWarning("safety", "task claimed verification command evidence but no execution tool calls were recorded");
736
+ ctx.ui.notify(`Safety: task ${sTid} claimed command evidence but no execution tool calls were recorded`, "warning");
737
737
  }
738
738
  }
739
739
  }