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

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 (303) 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-extract-learnings.js +17 -12
  19. package/dist/resources/extensions/gsd/custom-workflow-engine.js +22 -2
  20. package/dist/resources/extensions/gsd/db-base-schema.js +14 -0
  21. package/dist/resources/extensions/gsd/db-migration-steps.js +16 -0
  22. package/dist/resources/extensions/gsd/detection.js +106 -0
  23. package/dist/resources/extensions/gsd/graph.js +9 -3
  24. package/dist/resources/extensions/gsd/gsd-db.js +102 -2
  25. package/dist/resources/extensions/gsd/guided-flow.js +2 -2
  26. package/dist/resources/extensions/gsd/pr-evidence.js +57 -16
  27. package/dist/resources/extensions/gsd/prompts/complete-milestone.md +7 -8
  28. package/dist/resources/extensions/gsd/prompts/plan-milestone.md +3 -1
  29. package/dist/resources/extensions/gsd/safety/evidence-collector.js +10 -2
  30. package/dist/resources/extensions/gsd/working-output-messages.js +64 -0
  31. package/dist/resources/extensions/gsd/worktree-manager.js +16 -14
  32. package/dist/tsconfig.extensions.tsbuildinfo +1 -1
  33. package/dist/web/standalone/.next/BUILD_ID +1 -1
  34. package/dist/web/standalone/.next/app-path-routes-manifest.json +11 -11
  35. package/dist/web/standalone/.next/build-manifest.json +3 -3
  36. package/dist/web/standalone/.next/prerender-manifest.json +3 -3
  37. package/dist/web/standalone/.next/react-loadable-manifest.json +1 -1
  38. package/dist/web/standalone/.next/server/app/_global-error.html +1 -1
  39. package/dist/web/standalone/.next/server/app/_global-error.rsc +1 -1
  40. package/dist/web/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  41. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
  42. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
  43. package/dist/web/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  44. package/dist/web/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  45. package/dist/web/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  46. package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
  47. package/dist/web/standalone/.next/server/app/_not-found.rsc +1 -1
  48. package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +1 -1
  49. package/dist/web/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  50. package/dist/web/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +1 -1
  51. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  52. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  53. package/dist/web/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
  54. package/dist/web/standalone/.next/server/app/api/onboarding/route.js +1 -1
  55. package/dist/web/standalone/.next/server/app/index.html +1 -1
  56. package/dist/web/standalone/.next/server/app/index.rsc +1 -1
  57. package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +1 -1
  58. package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +1 -1
  59. package/dist/web/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
  60. package/dist/web/standalone/.next/server/app/index.segments/_index.segment.rsc +1 -1
  61. package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
  62. package/dist/web/standalone/.next/server/app-paths-manifest.json +11 -11
  63. package/dist/web/standalone/.next/server/chunks/6897.js +3 -3
  64. package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
  65. package/dist/web/standalone/.next/server/middleware-react-loadable-manifest.js +1 -1
  66. package/dist/web/standalone/.next/server/pages/404.html +1 -1
  67. package/dist/web/standalone/.next/server/pages/500.html +1 -1
  68. package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
  69. package/dist/web/standalone/.next/static/chunks/{8336.6f6f30e410419aff.js → 8336.631939fb583761fa.js} +1 -1
  70. package/dist/web/standalone/.next/static/chunks/{webpack-d82dbee6356c1733.js → webpack-0481f1221120a7c6.js} +1 -1
  71. package/package.json +10 -6
  72. package/packages/contracts/package.json +1 -1
  73. package/packages/pi-ai/dist/models/fake-model.d.ts +12 -0
  74. package/packages/pi-ai/dist/models/fake-model.d.ts.map +1 -0
  75. package/packages/pi-ai/dist/models/fake-model.js +27 -0
  76. package/packages/pi-ai/dist/models/fake-model.js.map +1 -0
  77. package/packages/pi-ai/dist/models/index.d.ts.map +1 -1
  78. package/packages/pi-ai/dist/models/index.js +8 -0
  79. package/packages/pi-ai/dist/models/index.js.map +1 -1
  80. package/packages/pi-ai/dist/providers/fake.d.ts +42 -0
  81. package/packages/pi-ai/dist/providers/fake.d.ts.map +1 -0
  82. package/packages/pi-ai/dist/providers/fake.js +319 -0
  83. package/packages/pi-ai/dist/providers/fake.js.map +1 -0
  84. package/packages/pi-ai/dist/providers/register-builtins.d.ts.map +1 -1
  85. package/packages/pi-ai/dist/providers/register-builtins.js +24 -0
  86. package/packages/pi-ai/dist/providers/register-builtins.js.map +1 -1
  87. package/packages/pi-ai/src/models/fake-model.ts +30 -0
  88. package/packages/pi-ai/src/models/index.ts +9 -0
  89. package/packages/pi-ai/src/providers/fake.ts +376 -0
  90. package/packages/pi-ai/src/providers/register-builtins.ts +23 -0
  91. package/packages/pi-ai/tsconfig.tsbuildinfo +1 -1
  92. package/packages/pi-coding-agent/dist/core/chat-controller-ordering.test.js +74 -0
  93. package/packages/pi-coding-agent/dist/core/chat-controller-ordering.test.js.map +1 -1
  94. package/packages/pi-coding-agent/dist/core/extensions/runner.d.ts +2 -0
  95. package/packages/pi-coding-agent/dist/core/extensions/runner.d.ts.map +1 -1
  96. package/packages/pi-coding-agent/dist/core/extensions/runner.js +14 -1
  97. package/packages/pi-coding-agent/dist/core/extensions/runner.js.map +1 -1
  98. package/packages/pi-coding-agent/dist/core/extensions/runner.test.js +97 -0
  99. package/packages/pi-coding-agent/dist/core/extensions/runner.test.js.map +1 -1
  100. package/packages/pi-coding-agent/dist/core/model-registry.d.ts.map +1 -1
  101. package/packages/pi-coding-agent/dist/core/model-registry.js +5 -0
  102. package/packages/pi-coding-agent/dist/core/model-registry.js.map +1 -1
  103. package/packages/pi-coding-agent/dist/core/settings-manager.d.ts +4 -0
  104. package/packages/pi-coding-agent/dist/core/settings-manager.d.ts.map +1 -1
  105. package/packages/pi-coding-agent/dist/core/settings-manager.js +8 -0
  106. package/packages/pi-coding-agent/dist/core/settings-manager.js.map +1 -1
  107. package/packages/pi-coding-agent/dist/core/slash-commands.d.ts.map +1 -1
  108. package/packages/pi-coding-agent/dist/core/slash-commands.js +1 -0
  109. package/packages/pi-coding-agent/dist/core/slash-commands.js.map +1 -1
  110. package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/chat-frame-compaction-tone.test.js +6 -4
  111. package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/chat-frame-compaction-tone.test.js.map +1 -1
  112. package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/tool-execution.test.js +54 -15
  113. package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/tool-execution.test.js.map +1 -1
  114. package/packages/pi-coding-agent/dist/modes/interactive/components/adaptive-layout.d.ts +26 -0
  115. package/packages/pi-coding-agent/dist/modes/interactive/components/adaptive-layout.d.ts.map +1 -0
  116. package/packages/pi-coding-agent/dist/modes/interactive/components/adaptive-layout.js +112 -0
  117. package/packages/pi-coding-agent/dist/modes/interactive/components/adaptive-layout.js.map +1 -0
  118. package/packages/pi-coding-agent/dist/modes/interactive/components/adaptive-layout.test.d.ts +2 -0
  119. package/packages/pi-coding-agent/dist/modes/interactive/components/adaptive-layout.test.d.ts.map +1 -0
  120. package/packages/pi-coding-agent/dist/modes/interactive/components/adaptive-layout.test.js +51 -0
  121. package/packages/pi-coding-agent/dist/modes/interactive/components/adaptive-layout.test.js.map +1 -0
  122. package/packages/pi-coding-agent/dist/modes/interactive/components/assistant-message.d.ts.map +1 -1
  123. package/packages/pi-coding-agent/dist/modes/interactive/components/assistant-message.js.map +1 -1
  124. package/packages/pi-coding-agent/dist/modes/interactive/components/chat-frame.d.ts.map +1 -1
  125. package/packages/pi-coding-agent/dist/modes/interactive/components/chat-frame.js +10 -9
  126. package/packages/pi-coding-agent/dist/modes/interactive/components/chat-frame.js.map +1 -1
  127. package/packages/pi-coding-agent/dist/modes/interactive/components/settings-selector.d.ts +3 -0
  128. package/packages/pi-coding-agent/dist/modes/interactive/components/settings-selector.d.ts.map +1 -1
  129. package/packages/pi-coding-agent/dist/modes/interactive/components/settings-selector.js +11 -0
  130. package/packages/pi-coding-agent/dist/modes/interactive/components/settings-selector.js.map +1 -1
  131. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-card-cleanup-and-success-runtime.test.js +7 -6
  132. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-card-cleanup-and-success-runtime.test.js.map +1 -1
  133. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.d.ts +16 -0
  134. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
  135. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.js +106 -17
  136. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.js.map +1 -1
  137. package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.d.ts.map +1 -1
  138. package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.js +60 -1
  139. package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.js.map +1 -1
  140. package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.test.js +40 -1
  141. package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.test.js.map +1 -1
  142. package/packages/pi-coding-agent/dist/modes/interactive/controllers/input-controller.test.js +1 -0
  143. package/packages/pi-coding-agent/dist/modes/interactive/controllers/input-controller.test.js.map +1 -1
  144. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode-state.d.ts +1 -0
  145. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode-state.d.ts.map +1 -1
  146. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode-state.js.map +1 -1
  147. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.d.ts +3 -0
  148. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  149. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js +23 -0
  150. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js.map +1 -1
  151. package/packages/pi-coding-agent/dist/modes/interactive/slash-command-handlers.d.ts.map +1 -1
  152. package/packages/pi-coding-agent/dist/modes/interactive/slash-command-handlers.js +20 -0
  153. package/packages/pi-coding-agent/dist/modes/interactive/slash-command-handlers.js.map +1 -1
  154. package/packages/pi-coding-agent/dist/modes/interactive/slash-command-handlers.test.d.ts +2 -0
  155. package/packages/pi-coding-agent/dist/modes/interactive/slash-command-handlers.test.d.ts.map +1 -0
  156. package/packages/pi-coding-agent/dist/modes/interactive/slash-command-handlers.test.js +79 -0
  157. package/packages/pi-coding-agent/dist/modes/interactive/slash-command-handlers.test.js.map +1 -0
  158. package/packages/pi-coding-agent/dist/modes/interactive/theme/theme-schema.d.ts +12 -0
  159. package/packages/pi-coding-agent/dist/modes/interactive/theme/theme-schema.d.ts.map +1 -1
  160. package/packages/pi-coding-agent/dist/modes/interactive/theme/theme-schema.js +13 -0
  161. package/packages/pi-coding-agent/dist/modes/interactive/theme/theme-schema.js.map +1 -1
  162. package/packages/pi-coding-agent/dist/modes/interactive/theme/theme.d.ts +1 -1
  163. package/packages/pi-coding-agent/dist/modes/interactive/theme/theme.d.ts.map +1 -1
  164. package/packages/pi-coding-agent/dist/modes/interactive/theme/theme.js +18 -1
  165. package/packages/pi-coding-agent/dist/modes/interactive/theme/theme.js.map +1 -1
  166. package/packages/pi-coding-agent/dist/modes/interactive/theme/themes.d.ts.map +1 -1
  167. package/packages/pi-coding-agent/dist/modes/interactive/theme/themes.js +36 -27
  168. package/packages/pi-coding-agent/dist/modes/interactive/theme/themes.js.map +1 -1
  169. package/packages/pi-coding-agent/dist/modes/interactive/tui-mode.d.ts +11 -0
  170. package/packages/pi-coding-agent/dist/modes/interactive/tui-mode.d.ts.map +1 -0
  171. package/packages/pi-coding-agent/dist/modes/interactive/tui-mode.js +18 -0
  172. package/packages/pi-coding-agent/dist/modes/interactive/tui-mode.js.map +1 -0
  173. package/packages/pi-coding-agent/dist/modes/interactive/tui-mode.test.d.ts +2 -0
  174. package/packages/pi-coding-agent/dist/modes/interactive/tui-mode.test.d.ts.map +1 -0
  175. package/packages/pi-coding-agent/dist/modes/interactive/tui-mode.test.js +48 -0
  176. package/packages/pi-coding-agent/dist/modes/interactive/tui-mode.test.js.map +1 -0
  177. package/packages/pi-coding-agent/src/core/chat-controller-ordering.test.ts +87 -0
  178. package/packages/pi-coding-agent/src/core/extensions/runner.test.ts +108 -0
  179. package/packages/pi-coding-agent/src/core/extensions/runner.ts +16 -1
  180. package/packages/pi-coding-agent/src/core/model-registry.ts +4 -0
  181. package/packages/pi-coding-agent/src/core/settings-manager.ts +12 -0
  182. package/packages/pi-coding-agent/src/core/slash-commands.ts +1 -0
  183. package/packages/pi-coding-agent/src/modes/interactive/components/__tests__/chat-frame-compaction-tone.test.ts +7 -5
  184. package/packages/pi-coding-agent/src/modes/interactive/components/__tests__/tool-execution.test.ts +78 -15
  185. package/packages/pi-coding-agent/src/modes/interactive/components/adaptive-layout.test.ts +59 -0
  186. package/packages/pi-coding-agent/src/modes/interactive/components/adaptive-layout.ts +160 -0
  187. package/packages/pi-coding-agent/src/modes/interactive/components/assistant-message.ts +1 -0
  188. package/packages/pi-coding-agent/src/modes/interactive/components/chat-frame.ts +10 -9
  189. package/packages/pi-coding-agent/src/modes/interactive/components/settings-selector.ts +15 -0
  190. package/packages/pi-coding-agent/src/modes/interactive/components/tool-card-cleanup-and-success-runtime.test.ts +10 -9
  191. package/packages/pi-coding-agent/src/modes/interactive/components/tool-execution.ts +118 -17
  192. package/packages/pi-coding-agent/src/modes/interactive/controllers/chat-controller.test.ts +43 -1
  193. package/packages/pi-coding-agent/src/modes/interactive/controllers/chat-controller.ts +75 -1
  194. package/packages/pi-coding-agent/src/modes/interactive/controllers/input-controller.test.ts +1 -0
  195. package/packages/pi-coding-agent/src/modes/interactive/interactive-mode-state.ts +1 -1
  196. package/packages/pi-coding-agent/src/modes/interactive/interactive-mode.ts +25 -0
  197. package/packages/pi-coding-agent/src/modes/interactive/slash-command-handlers.test.ts +95 -0
  198. package/packages/pi-coding-agent/src/modes/interactive/slash-command-handlers.ts +24 -1
  199. package/packages/pi-coding-agent/src/modes/interactive/theme/theme-schema.ts +13 -0
  200. package/packages/pi-coding-agent/src/modes/interactive/theme/theme.ts +32 -2
  201. package/packages/pi-coding-agent/src/modes/interactive/theme/themes.ts +36 -27
  202. package/packages/pi-coding-agent/src/modes/interactive/tui-mode.test.ts +65 -0
  203. package/packages/pi-coding-agent/src/modes/interactive/tui-mode.ts +29 -0
  204. package/packages/pi-coding-agent/tsconfig.tsbuildinfo +1 -1
  205. package/packages/pi-tui/dist/__tests__/style.test.d.ts +2 -0
  206. package/packages/pi-tui/dist/__tests__/style.test.d.ts.map +1 -0
  207. package/packages/pi-tui/dist/__tests__/style.test.js +63 -0
  208. package/packages/pi-tui/dist/__tests__/style.test.js.map +1 -0
  209. package/packages/pi-tui/dist/__tests__/tui.test.js +24 -3
  210. package/packages/pi-tui/dist/__tests__/tui.test.js.map +1 -1
  211. package/packages/pi-tui/dist/index.d.ts +1 -0
  212. package/packages/pi-tui/dist/index.d.ts.map +1 -1
  213. package/packages/pi-tui/dist/index.js +2 -0
  214. package/packages/pi-tui/dist/index.js.map +1 -1
  215. package/packages/pi-tui/dist/style.d.ts +41 -0
  216. package/packages/pi-tui/dist/style.d.ts.map +1 -0
  217. package/packages/pi-tui/dist/style.js +158 -0
  218. package/packages/pi-tui/dist/style.js.map +1 -0
  219. package/packages/pi-tui/dist/tui.d.ts +0 -1
  220. package/packages/pi-tui/dist/tui.d.ts.map +1 -1
  221. package/packages/pi-tui/dist/tui.js +3 -8
  222. package/packages/pi-tui/dist/tui.js.map +1 -1
  223. package/packages/pi-tui/src/__tests__/style.test.ts +76 -0
  224. package/packages/pi-tui/src/__tests__/tui.test.ts +29 -3
  225. package/packages/pi-tui/src/index.ts +9 -0
  226. package/packages/pi-tui/src/style.ts +225 -0
  227. package/packages/pi-tui/src/tui.ts +3 -8
  228. package/packages/pi-tui/tsconfig.tsbuildinfo +1 -1
  229. package/pkg/dist/modes/interactive/theme/theme-schema.d.ts +12 -0
  230. package/pkg/dist/modes/interactive/theme/theme-schema.d.ts.map +1 -1
  231. package/pkg/dist/modes/interactive/theme/theme-schema.js +13 -0
  232. package/pkg/dist/modes/interactive/theme/theme-schema.js.map +1 -1
  233. package/pkg/dist/modes/interactive/theme/theme.d.ts +1 -1
  234. package/pkg/dist/modes/interactive/theme/theme.d.ts.map +1 -1
  235. package/pkg/dist/modes/interactive/theme/theme.js +18 -1
  236. package/pkg/dist/modes/interactive/theme/theme.js.map +1 -1
  237. package/pkg/dist/modes/interactive/theme/themes.d.ts.map +1 -1
  238. package/pkg/dist/modes/interactive/theme/themes.js +36 -27
  239. package/pkg/dist/modes/interactive/theme/themes.js.map +1 -1
  240. package/src/resources/GSD-WORKFLOW.md +2 -2
  241. package/src/resources/extensions/github-sync/templates.ts +38 -8
  242. package/src/resources/extensions/github-sync/tests/inline-code.test.ts +66 -0
  243. package/src/resources/extensions/gsd/auto/loop-deps.ts +1 -0
  244. package/src/resources/extensions/gsd/auto/loop.ts +17 -10
  245. package/src/resources/extensions/gsd/auto/phases.ts +42 -28
  246. package/src/resources/extensions/gsd/auto/run-unit.ts +24 -14
  247. package/src/resources/extensions/gsd/auto-dashboard.ts +57 -8
  248. package/src/resources/extensions/gsd/auto-dispatch.ts +17 -0
  249. package/src/resources/extensions/gsd/auto-post-unit.ts +10 -10
  250. package/src/resources/extensions/gsd/auto-prompts.ts +116 -1
  251. package/src/resources/extensions/gsd/auto-recovery.ts +153 -7
  252. package/src/resources/extensions/gsd/auto-start.ts +7 -6
  253. package/src/resources/extensions/gsd/auto.ts +12 -1
  254. package/src/resources/extensions/gsd/bootstrap/agent-end-recovery.ts +16 -1
  255. package/src/resources/extensions/gsd/bootstrap/register-hooks.ts +17 -1
  256. package/src/resources/extensions/gsd/bootstrap/write-gate.ts +135 -1
  257. package/src/resources/extensions/gsd/clean-root-preflight.ts +41 -3
  258. package/src/resources/extensions/gsd/commands-extract-learnings.ts +17 -12
  259. package/src/resources/extensions/gsd/custom-workflow-engine.ts +24 -1
  260. package/src/resources/extensions/gsd/db-base-schema.ts +15 -0
  261. package/src/resources/extensions/gsd/db-migration-steps.ts +17 -0
  262. package/src/resources/extensions/gsd/detection.ts +128 -0
  263. package/src/resources/extensions/gsd/graph.ts +12 -5
  264. package/src/resources/extensions/gsd/gsd-db.ts +119 -1
  265. package/src/resources/extensions/gsd/guided-flow.ts +2 -2
  266. package/src/resources/extensions/gsd/pr-evidence.ts +63 -5
  267. package/src/resources/extensions/gsd/prompts/complete-milestone.md +7 -8
  268. package/src/resources/extensions/gsd/prompts/plan-milestone.md +3 -1
  269. package/src/resources/extensions/gsd/safety/evidence-collector.ts +11 -2
  270. package/src/resources/extensions/gsd/tests/auto-abort-pause-regression.test.ts +7 -1
  271. package/src/resources/extensions/gsd/tests/auto-dashboard.test.ts +33 -0
  272. package/src/resources/extensions/gsd/tests/auto-loop.test.ts +84 -5
  273. package/src/resources/extensions/gsd/tests/auto-recovery.test.ts +170 -1
  274. package/src/resources/extensions/gsd/tests/clean-root-preflight.test.ts +88 -2
  275. package/src/resources/extensions/gsd/tests/commands-extract-learnings.test.ts +9 -0
  276. package/src/resources/extensions/gsd/tests/custom-engine-loop-integration.test.ts +112 -6
  277. package/src/resources/extensions/gsd/tests/custom-workflow-engine.test.ts +40 -2
  278. package/src/resources/extensions/gsd/tests/db-migration-steps.integration.test.ts +428 -0
  279. package/src/resources/extensions/gsd/tests/db-schema-metadata.test.ts +2 -2
  280. package/src/resources/extensions/gsd/tests/detection.test.ts +140 -0
  281. package/src/resources/extensions/gsd/tests/fixtures/pr-body/commands-ship-basic.md +52 -0
  282. package/src/resources/extensions/gsd/tests/fixtures/pr-body/commands-ship-empty-optionals.md +42 -0
  283. package/src/resources/extensions/gsd/tests/fixtures/pr-body/swarm-lane-no-blockers.md +55 -0
  284. package/src/resources/extensions/gsd/tests/fixtures/pr-body/swarm-lane-with-blockers.md +60 -0
  285. package/src/resources/extensions/gsd/tests/graph-operations.test.ts +10 -0
  286. package/src/resources/extensions/gsd/tests/gsd-db.test.ts +44 -0
  287. package/src/resources/extensions/gsd/tests/has-pending-deep-stage.test.ts +33 -1
  288. package/src/resources/extensions/gsd/tests/pr-evidence-equivalence.test.ts +102 -0
  289. package/src/resources/extensions/gsd/tests/pr-evidence-hardening.test.ts +165 -0
  290. package/src/resources/extensions/gsd/tests/right-sized-workflow-prompts.test.ts +192 -0
  291. package/src/resources/extensions/gsd/tests/safety-harness-false-positives.test.ts +29 -0
  292. package/src/resources/extensions/gsd/tests/start-auto-detached.test.ts +46 -2
  293. package/src/resources/extensions/gsd/tests/uok-plan-v2-wiring.test.ts +1 -1
  294. package/src/resources/extensions/gsd/tests/working-output-messages.test.ts +93 -0
  295. package/src/resources/extensions/gsd/tests/worktree-health-dispatch.test.ts +37 -6
  296. package/src/resources/extensions/gsd/tests/worktree-manager.test.ts +7 -0
  297. package/src/resources/extensions/gsd/tests/worktree-nested-git-safety.test.ts +9 -2
  298. package/src/resources/extensions/gsd/tests/worktree-write-gate.test.ts +179 -0
  299. package/src/resources/extensions/gsd/working-output-messages.ts +120 -0
  300. package/src/resources/extensions/gsd/worktree-manager.ts +15 -4
  301. package/packages/contracts/tsconfig.tsbuildinfo +0 -1
  302. /package/dist/web/standalone/.next/static/{bQDK5_LtkGVS64AirQgQG → mPZbi5BH9dwokaPZlrYuQ}/_buildManifest.js +0 -0
  303. /package/dist/web/standalone/.next/static/{bQDK5_LtkGVS64AirQgQG → mPZbi5BH9dwokaPZlrYuQ}/_ssgManifest.js +0 -0
@@ -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.
@@ -11,6 +11,36 @@
11
11
 
12
12
  import { buildPrEvidence } from "../gsd/pr-evidence.js";
13
13
 
14
+ // ─── Helpers ────────────────────────────────────────────────────────────────
15
+
16
+ /**
17
+ * Wrap a string in a CommonMark inline-code span, escaping any embedded
18
+ * backticks by selecting a fence longer than the longest backtick run inside
19
+ * the input. If the input begins or ends with a backtick, pad with a single
20
+ * space inside the fence (CommonMark requirement).
21
+ *
22
+ * Empty input returns an empty string (no fence) — there is nothing to render
23
+ * as code, and emitting an empty pair of backticks would produce literal
24
+ * backticks in GitHub-flavored markdown.
25
+ */
26
+ export function inlineCode(s: string): string {
27
+ if (s.length === 0) return "";
28
+ let longestRun = 0;
29
+ let currentRun = 0;
30
+ for (const ch of s) {
31
+ if (ch === "`") {
32
+ currentRun++;
33
+ if (currentRun > longestRun) longestRun = currentRun;
34
+ } else {
35
+ currentRun = 0;
36
+ }
37
+ }
38
+ const fence = "`".repeat(longestRun + 1);
39
+ const needsPad = s.startsWith("`") || s.endsWith("`");
40
+ const pad = needsPad ? " " : "";
41
+ return `${fence}${pad}${s}${pad}${fence}`;
42
+ }
43
+
14
44
  // ─── Milestone Issue Body ───────────────────────────────────────────────────
15
45
 
16
46
  export interface MilestoneData {
@@ -124,7 +154,7 @@ export function formatTaskIssueBody(data: TaskData): string {
124
154
  if (data.files?.length) {
125
155
  lines.push("### Files");
126
156
  for (const file of data.files) {
127
- lines.push(`- \`${file}\``);
157
+ lines.push(`- ${inlineCode(file)}`);
128
158
  }
129
159
  lines.push("");
130
160
  }
@@ -227,15 +257,14 @@ export function formatSwarmLanePRBody(data: SwarmLanePRData): string {
227
257
  const summaries = [
228
258
  [
229
259
  "### Swarm lane",
230
- `**Lane:** \`${laneLabel}\``,
231
- `**Branch:** \`${data.lane.branch}\``,
260
+ `**Lane:** ${inlineCode(laneLabel)}`,
261
+ `**Branch:** ${inlineCode(data.lane.branch)}`,
232
262
  data.lane.owner ? `**Owner:** ${data.lane.owner}` : "",
233
- data.lane.latestCommit ? `**Latest commit:** \`${data.lane.latestCommit}\`` : "",
263
+ data.lane.latestCommit ? `**Latest commit:** ${inlineCode(data.lane.latestCommit)}` : "",
234
264
  ].filter(Boolean).join("\n"),
235
265
  `### Impact area\n${data.impactArea}`,
236
266
  `### Changed contracts\n${checkedList(data.lane.changedContracts, "No shared contracts changed").join("\n")}`,
237
267
  `### Transition risks\n${checkedList(data.transitionRisks, "No transition risks identified").join("\n")}`,
238
- data.lane.blockers?.length ? `### Blockers\n${data.lane.blockers.map((blocker) => `- ${blocker}`).join("\n")}` : "",
239
268
  ].filter(Boolean);
240
269
 
241
270
  return buildPrEvidence({
@@ -246,6 +275,7 @@ export function formatSwarmLanePRBody(data: SwarmLanePRData): string {
246
275
  changeType: "refactor",
247
276
  linkedIssue: data.linkedIssue ? `Closes #${data.linkedIssue}` : undefined,
248
277
  summaries,
278
+ blockers: data.lane.blockers ?? [],
249
279
  testsRun: data.lane.testEvidence,
250
280
  rollbackNotes: data.rollbackPlan,
251
281
  how: "Generated by GSD GitHub Sync swarm routines from lane evidence.",
@@ -257,7 +287,7 @@ export function formatSwarmReleaseChecklistBody(data: SwarmReleaseChecklistData)
257
287
 
258
288
  lines.push(`# UOK Swarm Release Checklist`);
259
289
  lines.push("");
260
- lines.push(`**Integration branch:** \`${data.integrationBranch}\``);
290
+ lines.push(`**Integration branch:** ${inlineCode(data.integrationBranch)}`);
261
291
  lines.push("");
262
292
 
263
293
  lines.push("## Lane summary");
@@ -266,9 +296,9 @@ export function formatSwarmReleaseChecklistBody(data: SwarmReleaseChecklistData)
266
296
  lines.push("|------|--------|-------|--------|--------|");
267
297
  for (const lane of data.lanes) {
268
298
  const owner = lane.owner ?? "";
269
- const commit = lane.latestCommit ? `\`${lane.latestCommit}\`` : "";
299
+ const commit = lane.latestCommit ? inlineCode(lane.latestCommit) : "";
270
300
  const status = lane.blockers?.length ? "blocked" : "ready";
271
- lines.push(`| \`${SWARM_LANE_LABELS[lane.id]}\` | \`${lane.branch}\` | ${owner} | ${commit} | ${status} |`);
301
+ lines.push(`| ${inlineCode(SWARM_LANE_LABELS[lane.id])} | ${inlineCode(lane.branch)} | ${owner} | ${commit} | ${status} |`);
272
302
  }
273
303
  lines.push("");
274
304
 
@@ -0,0 +1,66 @@
1
+ // Project/App: GSD-2
2
+ // File Purpose: Tests for the inlineCode markdown helper and its use in PR body templates.
3
+
4
+ import { describe, it } from "node:test";
5
+ import assert from "node:assert/strict";
6
+ import { inlineCode, formatSwarmLanePRBody } from "../templates.ts";
7
+
8
+ describe("inlineCode", () => {
9
+ it("wraps a plain string in single backticks", () => {
10
+ assert.equal(inlineCode("hello"), "`hello`");
11
+ });
12
+
13
+ it("uses a double-backtick fence when the input contains a single backtick", () => {
14
+ const out = inlineCode("a`b");
15
+ assert.equal(out, "``a`b``");
16
+ });
17
+
18
+ it("uses a 4-backtick fence when the input contains a run of three backticks", () => {
19
+ const out = inlineCode("x```y");
20
+ assert.equal(out, "````x```y````");
21
+ });
22
+
23
+ it("pads with a leading space when the input starts with a backtick", () => {
24
+ const out = inlineCode("`leading");
25
+ // longest run = 1 → fence length 2; leading backtick → pad both sides
26
+ assert.equal(out, "`` `leading ``");
27
+ });
28
+
29
+ it("pads with a trailing space when the input ends with a backtick", () => {
30
+ const out = inlineCode("trailing`");
31
+ assert.equal(out, "`` trailing` ``");
32
+ });
33
+
34
+ it("returns an empty string for empty input", () => {
35
+ // Documented invariant: empty input renders as nothing rather than as
36
+ // a literal pair of backticks (which GFM would render as the characters
37
+ // themselves, not as code).
38
+ assert.equal(inlineCode(""), "");
39
+ });
40
+
41
+ it("escapes a branch with embedded backticks inside formatSwarmLanePRBody", () => {
42
+ const malicious = "feature`evil";
43
+ const body = formatSwarmLanePRBody({
44
+ lane: {
45
+ id: "workflow",
46
+ branch: malicious,
47
+ },
48
+ impactArea: "test",
49
+ transitionRisks: [],
50
+ rollbackPlan: [],
51
+ });
52
+ // The branch must appear inside a properly fenced inline-code span,
53
+ // i.e. wrapped in the helper's chosen fence (here a double backtick).
54
+ assert.ok(
55
+ body.includes("``feature`evil``"),
56
+ `expected double-backtick fenced branch, got body:\n${body}`,
57
+ );
58
+ // And there must be no markdown break-out: the substring "evil" should
59
+ // never appear unfenced as a bare word adjacent to a closing single
60
+ // backtick (the unpatched template produced "`feature`evil`").
61
+ assert.ok(
62
+ !body.includes("`feature`evil`\n") && !body.includes("`feature`evil` "),
63
+ `expected no inline-code break-out, got body:\n${body}`,
64
+ );
65
+ });
66
+ });
@@ -139,6 +139,7 @@ export interface LoopDeps {
139
139
  postflightPopStash: (
140
140
  basePath: string,
141
141
  milestoneId: string,
142
+ stashMarker: string | undefined,
142
143
  notify: (message: string, level: "info" | "warning" | "error") => void,
143
144
  ) => void;
144
145
 
@@ -359,6 +359,20 @@ export async function autoLoop(
359
359
  const prefs = deps.loadEffectiveGSDPreferences()?.preferences;
360
360
  const uokFlags = resolveUokFlags(prefs);
361
361
 
362
+ // ── Check sidecar queue before deriveState ──
363
+ // NOTE: Sidecar dequeue MUST run before validateWorkflowSessionLock so a
364
+ // queued item is popped (and the `sidecar-dequeue` journal event emitted)
365
+ // even when the session lock invalidates this iteration. Inverting this
366
+ // order silently drops queued items on lock-loss. Refs #5308.
367
+ const sidecarItem = await dequeueSidecarItem({
368
+ queue: s.sidecarQueue,
369
+ executionGraphEnabled: uokFlags.executionGraph,
370
+ scheduleQueue: scheduleSidecarQueue,
371
+ warnSchedulingFailure: message => logWarning("dispatch", `sidecar queue scheduling failed: ${message}`),
372
+ logDequeue: payload => debugLog("autoLoop", { phase: "sidecar-dequeue", ...payload }),
373
+ emitDequeue: payload => journalReporter.emit("sidecar-dequeue", payload),
374
+ });
375
+
362
376
  const sessionLockOutcome = validateWorkflowSessionLock({
363
377
  active: s.active,
364
378
  iteration,
@@ -378,19 +392,10 @@ export async function autoLoop(
378
392
  },
379
393
  });
380
394
  if (sessionLockOutcome.action === "stop" && sessionLockOutcome.reason === "session-lock-lost") {
395
+ finishTurn("stopped", "manual-attention", sessionLockOutcome.reason);
381
396
  break;
382
397
  }
383
398
 
384
- // ── Check sidecar queue before deriveState ──
385
- const sidecarItem = await dequeueSidecarItem({
386
- queue: s.sidecarQueue,
387
- executionGraphEnabled: uokFlags.executionGraph,
388
- scheduleQueue: scheduleSidecarQueue,
389
- warnSchedulingFailure: message => logWarning("dispatch", `sidecar queue scheduling failed: ${message}`),
390
- logDequeue: payload => debugLog("autoLoop", { phase: "sidecar-dequeue", ...payload }),
391
- emitDequeue: payload => journalReporter.emit("sidecar-dequeue", payload),
392
- });
393
-
394
399
  const ic: IterationContext = { ctx, pi, s, deps, prefs, iteration, flowId, nextSeq };
395
400
  journalReporter.emit("iteration-start", { iteration });
396
401
  let iterData: IterationData;
@@ -427,6 +432,7 @@ export async function autoLoop(
427
432
  isComplete: engineState.isComplete,
428
433
  });
429
434
  if (engineState.isComplete) {
435
+ finishTurn("completed");
430
436
  await deps.stopAuto(ctx, pi, "Workflow complete");
431
437
  break;
432
438
  }
@@ -873,6 +879,7 @@ export async function autoLoop(
873
879
 
874
880
  if (cooldownDecision.action === "stop") {
875
881
  ctx.ui.notify(cooldownDecision.notifyMessage, "error");
882
+ finishTurn("stopped", "timeout", msg);
876
883
  await deps.stopAuto(ctx, pi, cooldownDecision.stopMessage);
877
884
  break;
878
885
  }
@@ -32,13 +32,13 @@ import { detectStuck } from "./detect-stuck.js";
32
32
  import { runUnit } from "./run-unit.js";
33
33
  import { debugLog } from "../debug-logger.js";
34
34
  import { resolveWorktreeProjectRoot, normalizeWorktreePathForCompare } from "../worktree-root.js";
35
- import { PROJECT_FILES, hasProjectFileInAncestor } from "../detection.js";
35
+ import { classifyProject } from "../detection.js";
36
36
  import { MergeConflictError } from "../git-service.js";
37
37
  import { setCurrentPhase, clearCurrentPhase } from "../../shared/gsd-phase-state.js";
38
38
  import { pauseAutoForProviderError } from "../provider-error-pause.js";
39
39
  import { resumeAutoAfterProviderDelay } from "../bootstrap/provider-error-resume.js";
40
40
  import { join, basename } from "node:path";
41
- import { existsSync, cpSync, readdirSync } from "node:fs";
41
+ import { existsSync, cpSync } from "node:fs";
42
42
  import {
43
43
  logWarning,
44
44
  logError,
@@ -684,6 +684,7 @@ export async function runPreDispatch(
684
684
  deps.postflightPopStash(
685
685
  s.originalBasePath || s.basePath,
686
686
  s.currentMilestoneId!,
687
+ preflightTransition.stashMarker,
687
688
  ctx.ui.notify.bind(ctx.ui),
688
689
  );
689
690
  }
@@ -797,6 +798,7 @@ export async function runPreDispatch(
797
798
  deps.postflightPopStash(
798
799
  s.originalBasePath || s.basePath,
799
800
  s.currentMilestoneId,
801
+ preflightAllComplete.stashMarker,
800
802
  ctx.ui.notify.bind(ctx.ui),
801
803
  );
802
804
  }
@@ -925,6 +927,7 @@ export async function runPreDispatch(
925
927
  deps.postflightPopStash(
926
928
  s.originalBasePath || s.basePath,
927
929
  s.currentMilestoneId,
930
+ preflightComplete.stashMarker,
928
931
  ctx.ui.notify.bind(ctx.ui),
929
932
  );
930
933
  }
@@ -1484,8 +1487,9 @@ export async function runUnitPhase(
1484
1487
  // Verify the working directory is a valid git checkout with project
1485
1488
  // files before dispatching work. A broken worktree causes agents to
1486
1489
  // hallucinate summaries since they cannot read or write any files.
1487
- // Uses the shared PROJECT_FILES list from detection.ts to support all
1488
- // ecosystems (Rust, Go, Python, Java, etc.), not just JS.
1490
+ // Uses project classification so project presence is not conflated with
1491
+ // ecosystem marker detection. Static/minimal repos become untyped-existing.
1492
+ let projectClassification: ReturnType<typeof classifyProject> | null = null;
1489
1493
  if (s.basePath && unitType === "execute-task") {
1490
1494
  const gitMarker = join(s.basePath, ".git");
1491
1495
  const hasGit = deps.existsSync(gitMarker);
@@ -1496,30 +1500,29 @@ export async function runUnitPhase(
1496
1500
  await deps.stopAuto(ctx, pi, msg);
1497
1501
  return { action: "break", reason: "worktree-invalid" };
1498
1502
  }
1499
- const hasProjectFile = PROJECT_FILES.some((f) => deps.existsSync(join(s.basePath, f)));
1500
- const hasSrcDir = deps.existsSync(join(s.basePath, "src"));
1501
- // Xcode bundles have project-specific names (*.xcodeproj, *.xcworkspace)
1502
- // that cannot be matched by exact filename — scan the directory by suffix.
1503
- let hasXcodeBundle = false;
1504
- try {
1505
- const entries = deps.existsSync(s.basePath) ? readdirSync(s.basePath) : [];
1506
- hasXcodeBundle = entries.some((e: string) => e.endsWith(".xcodeproj") || e.endsWith(".xcworkspace"));
1507
- } catch (err) {
1508
- debugLog("runUnitPhase", { phase: "xcode-bundle-scan-failed", basePath: s.basePath, error: String(err) });
1509
- }
1510
- // Monorepo support (#2347): if no project files in the worktree directory,
1511
- // walk parent directories up to the filesystem root. In monorepos,
1512
- // package.json / Cargo.toml etc. live in a parent directory.
1513
- const hasProjectFileInParent =
1514
- !hasProjectFile && !hasSrcDir && !hasXcodeBundle
1515
- ? hasProjectFileInAncestor(s.basePath, deps.existsSync)
1516
- : false;
1517
- if (!hasProjectFile && !hasSrcDir && !hasXcodeBundle && !hasProjectFileInParent) {
1518
- // Greenfield projects won't have project files yet — the first task creates them.
1519
- // Log a warning but allow execution to proceed. The .git check above is sufficient
1520
- // to ensure we're in a valid working directory.
1521
- debugLog("runUnitPhase", { phase: "worktree-health-warn-greenfield", basePath: s.basePath, hasProjectFile, hasSrcDir, hasXcodeBundle });
1522
- ctx.ui.notify(`Warning: ${s.basePath} has no recognized project files — proceeding as greenfield project`, "warning");
1503
+ projectClassification = classifyProject(s.basePath);
1504
+ if (projectClassification.kind === "invalid-repo") {
1505
+ const msg = `Worktree health check failed: ${s.basePath} classified as invalid-repo (${projectClassification.reason}) — refusing to dispatch ${unitType} ${unitId}`;
1506
+ debugLog("runUnitPhase", { phase: "worktree-health-invalid-repo", basePath: s.basePath, classification: projectClassification });
1507
+ if (projectClassification.reason === "missing .git" && hasGit) {
1508
+ ctx.ui.notify(
1509
+ `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`,
1510
+ "warning",
1511
+ );
1512
+ } else {
1513
+ ctx.ui.notify(msg, "error");
1514
+ await deps.stopAuto(ctx, pi, msg);
1515
+ return { action: "break", reason: "worktree-invalid" };
1516
+ }
1517
+ } else if (projectClassification.kind === "greenfield") {
1518
+ debugLog("runUnitPhase", { phase: "worktree-health-greenfield", basePath: s.basePath, classification: projectClassification });
1519
+ ctx.ui.notify(`Warning: ${s.basePath} has no project content yet — proceeding as greenfield project`, "warning");
1520
+ } else if (projectClassification.kind === "untyped-existing") {
1521
+ debugLog("runUnitPhase", { phase: "worktree-health-untyped-existing", basePath: s.basePath, classification: projectClassification });
1522
+ ctx.ui.notify(
1523
+ `Notice: ${s.basePath} has existing project content but no recognized tooling markers using generic file-level workflow guidance`,
1524
+ "info",
1525
+ );
1523
1526
  }
1524
1527
  }
1525
1528
 
@@ -1598,6 +1601,17 @@ export async function runUnitPhase(
1598
1601
  // Prompt injection
1599
1602
  let finalPrompt = prompt;
1600
1603
 
1604
+ if (unitType === "execute-task") {
1605
+ projectClassification ??= classifyProject(s.basePath);
1606
+ if (projectClassification.kind === "untyped-existing") {
1607
+ const samples = projectClassification.contentFiles.slice(0, 8).join(", ") || "project files";
1608
+ finalPrompt +=
1609
+ "\n\n**Project classification:** Existing untyped project. No recognized build/tooling markers were detected, " +
1610
+ "so use generic file-level workflow guidance. Task plans and completion summaries must list every concrete " +
1611
+ `project file changed in \`files\` or \`expected_output\`. Detected content sample: ${samples}.`;
1612
+ }
1613
+ }
1614
+
1601
1615
  if (s.pendingVerificationRetry) {
1602
1616
  const retryCtx = s.pendingVerificationRetry;
1603
1617
  s.pendingVerificationRetry = null;
@@ -1,3 +1,5 @@
1
+ // GSD-2 + src/resources/extensions/gsd/auto/run-unit.ts - Runs one GSD auto-mode unit from session creation through agent completion.
2
+
1
3
  /**
2
4
  * auto/run-unit.ts — Single unit execution: session create → prompt → await agent_end.
3
5
  *
@@ -17,6 +19,7 @@ import {
17
19
  import { debugLog } from "../debug-logger.js";
18
20
  import { logWarning, logError } from "../workflow-logger.js";
19
21
  import { resolveAutoSupervisorConfig } from "../preferences.js";
22
+ import { formatAutoUnitWorkingMessage } from "../working-output-messages.js";
20
23
 
21
24
  // Tracks the latest session-switch attempt so a late timeout settlement from an
22
25
  // older runUnit() call cannot clear the guard for a newer one.
@@ -184,30 +187,37 @@ export async function runUnit(
184
187
  debugLog("runUnit", { phase: "send-message", unitType, unitId });
185
188
 
186
189
  const requestDispatchedAt = Date.now();
187
- pi.sendMessage(
188
- { customType: "gsd-auto", content: prompt, display: s.verbose },
189
- { triggerTurn: true },
190
- );
190
+ ctx.ui.setWorkingMessage?.(formatAutoUnitWorkingMessage(unitType, unitId));
191
191
 
192
192
  // ── Await agent_end with absolute timeout (H4 fix) ──
193
193
  // If supervision fails to resolve unitPromise within 30s, treat as cancelled.
194
194
  // Without this, a crashed agent that never emits agent_end hangs the loop (#3161).
195
- debugLog("runUnit", { phase: "awaiting-agent-end", unitType, unitId });
196
195
  const supervisor = resolveAutoSupervisorConfig();
197
196
  const UNIT_HARD_TIMEOUT_MS = Math.max(
198
197
  30_000,
199
198
  ((supervisor.hard_timeout_minutes ?? 30) * 60 * 1000) + 30_000,
200
199
  );
201
200
  let unitTimeoutHandle: ReturnType<typeof setTimeout> | undefined;
202
- const timeoutResult = new Promise<UnitResult>((resolve) => {
203
- unitTimeoutHandle = setTimeout(() => {
204
- resolve({ status: "cancelled", errorContext: { message: "Unit hard timeout — supervision may have failed", category: "timeout", isTransient: true } });
205
- }, UNIT_HARD_TIMEOUT_MS);
206
- });
207
- const result = await runWithTurnGeneration(capturedTurnGen, () =>
208
- Promise.race([unitPromise, timeoutResult]),
209
- );
210
- if (unitTimeoutHandle) clearTimeout(unitTimeoutHandle);
201
+ let result: UnitResult;
202
+ try {
203
+ pi.sendMessage(
204
+ { customType: "gsd-auto", content: prompt, display: s.verbose },
205
+ { triggerTurn: true },
206
+ );
207
+
208
+ debugLog("runUnit", { phase: "awaiting-agent-end", unitType, unitId });
209
+ const timeoutResult = new Promise<UnitResult>((resolve) => {
210
+ unitTimeoutHandle = setTimeout(() => {
211
+ resolve({ status: "cancelled", errorContext: { message: "Unit hard timeout — supervision may have failed", category: "timeout", isTransient: true } });
212
+ }, UNIT_HARD_TIMEOUT_MS);
213
+ });
214
+ result = await runWithTurnGeneration(capturedTurnGen, () =>
215
+ Promise.race([unitPromise, timeoutResult]),
216
+ );
217
+ } finally {
218
+ if (unitTimeoutHandle) clearTimeout(unitTimeoutHandle);
219
+ ctx.ui.setWorkingMessage?.(undefined);
220
+ }
211
221
  debugLog("runUnit", {
212
222
  phase: "agent-end-received",
213
223
  unitType,
@@ -1,3 +1,5 @@
1
+ // GSD-2 + src/resources/extensions/gsd/auto-dashboard.ts - Auto-mode progress widget rendering and dashboard helpers.
2
+
1
3
  /**
2
4
  * Auto-mode Dashboard — progress widget rendering, elapsed time formatting,
3
5
  * unit description helpers, and slice progress caching.
@@ -46,6 +48,7 @@ import {
46
48
  import { logWarning } from "./workflow-logger.js";
47
49
  import { formattedShortcutPair } from "./shortcut-defs.js";
48
50
  import { homedir } from "node:os";
51
+ import { readUnitRuntimeRecord, type AutoUnitRuntimeRecord } from "./unit-runtime.js";
49
52
 
50
53
  // ─── UAT Slice Extraction ─────────────────────────────────────────────────────
51
54
 
@@ -215,6 +218,36 @@ export function formatWidgetTokens(count: number): string {
215
218
  return `${Math.round(count / 1000000)}M`;
216
219
  }
217
220
 
221
+ export function formatRuntimeHealthSignal(
222
+ record: AutoUnitRuntimeRecord | null,
223
+ now = Date.now(),
224
+ ): { level: "green" | "yellow"; summary: string; detail?: string } | null {
225
+ if (!record) return null;
226
+ const idleMs = Math.max(0, now - record.lastProgressAt);
227
+ const idleMinutes = Math.floor(idleMs / 60_000);
228
+ if ((record.recoveryAttempts ?? 0) > 0 || record.phase === "recovered" || record.lastProgressKind.includes("recovery")) {
229
+ return {
230
+ level: "yellow",
231
+ summary: "Recovering",
232
+ detail: `retry ${record.recoveryAttempts ?? 1} after ${record.lastRecoveryReason ?? "idle"} stall`,
233
+ };
234
+ }
235
+ if (record.progressCount === 0 && idleMs >= 60_000) {
236
+ return {
237
+ level: "yellow",
238
+ summary: "Waiting on provider",
239
+ detail: `no output for ${idleMinutes}m`,
240
+ };
241
+ }
242
+ return null;
243
+ }
244
+
245
+ export function shouldRenderRoadmapProgress(
246
+ progress: { total: number; activeSliceTasks?: { total: number } | null } | null,
247
+ ): progress is { total: number; activeSliceTasks?: { total: number } | null } {
248
+ return !!progress && progress.total > 0;
249
+ }
250
+
218
251
  // ─── ETA Estimation ──────────────────────────────────────────────────────────
219
252
 
220
253
  /**
@@ -622,6 +655,7 @@ export function updateProgressWidget(
622
655
  let cachedLines: string[] | undefined;
623
656
  let cachedWidth: number | undefined;
624
657
  let cachedRtkLabel: string | null | undefined;
658
+ let cachedRuntimeRecord: AutoUnitRuntimeRecord | null = null;
625
659
 
626
660
  const refreshRtkLabel = (): void => {
627
661
  try {
@@ -634,7 +668,16 @@ export function updateProgressWidget(
634
668
  }
635
669
  };
636
670
 
671
+ const refreshRuntimeRecord = (): void => {
672
+ try {
673
+ cachedRuntimeRecord = readUnitRuntimeRecord(accessors.getBasePath(), unitType, unitId);
674
+ } catch {
675
+ cachedRuntimeRecord = null;
676
+ }
677
+ };
678
+
637
679
  refreshRtkLabel();
680
+ refreshRuntimeRecord();
638
681
 
639
682
  const pulseTimer = setInterval(() => {
640
683
  pulseBright = !pulseBright;
@@ -652,6 +695,7 @@ export function updateProgressWidget(
652
695
  updateSliceProgressCache(accessors.getBasePath(), mid.id, slice?.id);
653
696
  }
654
697
  refreshRtkLabel();
698
+ refreshRuntimeRecord();
655
699
  cachedLines = undefined;
656
700
  } catch (err) { /* non-fatal */
657
701
  logWarning("dashboard", `DB status update failed: ${err instanceof Error ? err.message : String(err)}`);
@@ -685,13 +729,16 @@ export function updateProgressWidget(
685
729
 
686
730
  // Health indicator in header
687
731
  const score = computeProgressScore();
688
- const healthColor = score.level === "green" ? "success"
689
- : score.level === "yellow" ? "warning"
732
+ const runtimeSignal = formatRuntimeHealthSignal(cachedRuntimeRecord);
733
+ const healthLevel = runtimeSignal?.level ?? score.level;
734
+ const healthSummary = runtimeSignal?.summary ?? score.summary;
735
+ const healthColor = healthLevel === "green" ? "success"
736
+ : healthLevel === "yellow" ? "warning"
690
737
  : "error";
691
- const healthIcon = score.level === "green" ? GLYPH.statusActive
692
- : score.level === "yellow" ? "!"
738
+ const healthIcon = healthLevel === "green" ? GLYPH.statusActive
739
+ : healthLevel === "yellow" ? "!"
693
740
  : "x";
694
- const healthStr = ` ${theme.fg(healthColor, healthIcon)} ${theme.fg(healthColor, score.summary)}`;
741
+ const healthStr = ` ${theme.fg(healthColor, healthIcon)} ${theme.fg(healthColor, healthSummary)}`;
695
742
 
696
743
  const headerLeft = `${pad}${dot} ${theme.fg("accent", theme.bold("GSD"))} ${theme.fg("success", modeTag)}${healthStr}`;
697
744
 
@@ -706,7 +753,9 @@ export function updateProgressWidget(
706
753
  lines.push(rightAlign(headerLeft, headerRight, width));
707
754
 
708
755
  // Show health signal details when degraded (yellow/red)
709
- if (score.level !== "green" && score.signals.length > 0 && widgetMode !== "min") {
756
+ if (runtimeSignal?.detail && widgetMode !== "min") {
757
+ lines.push(`${pad} ${theme.fg("dim", runtimeSignal.detail)}`);
758
+ } else if (score.level !== "green" && score.signals.length > 0 && widgetMode !== "min") {
710
759
  // Show up to 3 most relevant signals in compact form
711
760
  const topSignals = score.signals
712
761
  .filter(s => s.kind === "negative")
@@ -785,7 +834,7 @@ export function updateProgressWidget(
785
834
 
786
835
  // Progress bar
787
836
  const roadmapSlices = mid ? getRoadmapSlicesSync() : null;
788
- if (roadmapSlices) {
837
+ if (shouldRenderRoadmapProgress(roadmapSlices)) {
789
838
  const { done, total, activeSliceTasks } = roadmapSlices;
790
839
  const barWidth = Math.max(6, Math.min(18, Math.floor(width * 0.25)));
791
840
  const pct = total > 0 ? done / total : 0;
@@ -853,7 +902,7 @@ export function updateProgressWidget(
853
902
 
854
903
  const leftLines: string[] = [];
855
904
 
856
- if (roadmapSlices) {
905
+ if (shouldRenderRoadmapProgress(roadmapSlices)) {
857
906
  const { done, total, activeSliceTasks } = roadmapSlices;
858
907
  const barWidth = Math.max(6, Math.min(18, Math.floor(leftColWidth * 0.4)));
859
908
  const pct = total > 0 ? done / total : 0;
@@ -211,6 +211,23 @@ export function hasPendingDeepStage(prefs: GSDPreferences | undefined, basePath:
211
211
  return gate.status === "pending" || gate.status === "blocked";
212
212
  }
213
213
 
214
+ export function shouldRunDeepProjectSetup(
215
+ state: Pick<GSDState, "phase">,
216
+ prefs: GSDPreferences | undefined,
217
+ basePath: string,
218
+ options: { hasSurvivorBranch?: boolean } = {},
219
+ ): boolean {
220
+ if (options.hasSurvivorBranch === true) return false;
221
+ if (
222
+ state.phase !== "pre-planning" &&
223
+ state.phase !== "needs-discussion" &&
224
+ state.phase !== "planning"
225
+ ) {
226
+ return false;
227
+ }
228
+ return hasPendingDeepStage(prefs, basePath);
229
+ }
230
+
214
231
  function missingSliceStop(mid: string, phase: string): DispatchAction {
215
232
  return {
216
233
  action: "stop",
@@ -43,7 +43,7 @@ import {
43
43
  import { regenerateIfMissing } from "./workflow-projections.js";
44
44
  import { syncStateToProjectRoot } from "./auto-worktree.js";
45
45
  import { normalizeWorktreePathForCompare } from "./worktree-root.js";
46
- import { isDbAvailable, getTask, getSlice, getMilestone, updateTaskStatus, _getAdapter } from "./gsd-db.js";
46
+ import { isDbAvailable, getTask, getSlice, getMilestone, updateTaskStatus, _getAdapter, getVerificationEvidence } from "./gsd-db.js";
47
47
  import { renderPlanCheckboxes } from "./markdown-renderer.js";
48
48
  import { consumeSignal } from "./session-status-io.js";
49
49
  import {
@@ -852,22 +852,22 @@ export async function postUnitPreVerification(pctx: PostUnitContext, opts?: PreV
852
852
  }
853
853
 
854
854
  // Evidence cross-reference (execute-task only)
855
- // Verification evidence is passed via the complete-task tool call and
856
- // stored in the SUMMARY.md on disk not available as structured data
857
- // in the DB. The evidence collector tracks actual bash tool calls, so
858
- // we can still detect units that claimed success but ran no commands.
855
+ // Only compare against concrete command evidence persisted by the task
856
+ // completion tool. A prose Verify field can be satisfied later by the
857
+ // host verification gate, so it is not enough to accuse the unit.
859
858
  if (safetyConfig.evidence_cross_reference && s.currentUnit.type === "execute-task") {
860
859
  try {
861
860
  const actual = getEvidence();
862
861
  const bashCalls = actual.filter(e => e.kind === "bash");
863
- // If the task is marked complete but zero bash commands were run,
864
- // it's suspicious — the LLM may have fabricated results.
865
862
  if (sMid && sSid && sTid && isDbAvailable()) {
866
863
  const taskRow = getTask(sMid, sSid, sTid);
867
- if (taskRow?.status === "complete" && taskRow.verify && bashCalls.length === 0) {
868
- logWarning("safety", "task marked complete with verification commands but no bash calls were executed");
864
+ const claimedCommands = getVerificationEvidence(sMid, sSid, sTid)
865
+ .map((row) => row.command)
866
+ .filter((command): command is string => typeof command === "string" && command.trim().length > 0);
867
+ if (taskRow?.status === "complete" && claimedCommands.length > 0 && bashCalls.length === 0) {
868
+ logWarning("safety", "task claimed verification command evidence but no execution tool calls were recorded");
869
869
  ctx.ui.notify(
870
- `Safety: task ${sTid} has verification commands but no bash calls were recorded`,
870
+ `Safety: task ${sTid} claimed command evidence but no execution tool calls were recorded`,
871
871
  "warning",
872
872
  );
873
873
  }