gsd-pi 2.41.0 → 2.42.0

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 (293) hide show
  1. package/README.md +69 -29
  2. package/dist/cli-web-branch.d.ts +6 -0
  3. package/dist/cli-web-branch.js +17 -0
  4. package/dist/onboarding.js +2 -1
  5. package/dist/resources/extensions/gsd/auto/loop.js +89 -1
  6. package/dist/resources/extensions/gsd/auto/phases.js +28 -10
  7. package/dist/resources/extensions/gsd/auto/session.js +6 -0
  8. package/dist/resources/extensions/gsd/auto-dashboard.js +8 -2
  9. package/dist/resources/extensions/gsd/auto-dispatch.js +19 -2
  10. package/dist/resources/extensions/gsd/auto-post-unit.js +7 -0
  11. package/dist/resources/extensions/gsd/auto-recovery.js +12 -4
  12. package/dist/resources/extensions/gsd/auto-start.js +8 -3
  13. package/dist/resources/extensions/gsd/auto-worktree.js +147 -13
  14. package/dist/resources/extensions/gsd/auto.js +64 -2
  15. package/dist/resources/extensions/gsd/bootstrap/db-tools.js +199 -164
  16. package/dist/resources/extensions/gsd/bootstrap/journal-tools.js +62 -0
  17. package/dist/resources/extensions/gsd/bootstrap/register-extension.js +2 -0
  18. package/dist/resources/extensions/gsd/bootstrap/register-hooks.js +16 -0
  19. package/dist/resources/extensions/gsd/bootstrap/tool-call-loop-guard.js +7 -2
  20. package/dist/resources/extensions/gsd/commands/catalog.js +40 -1
  21. package/dist/resources/extensions/gsd/commands/handlers/core.js +1 -0
  22. package/dist/resources/extensions/gsd/commands/handlers/ops.js +5 -0
  23. package/dist/resources/extensions/gsd/commands/handlers/workflow.js +146 -0
  24. package/dist/resources/extensions/gsd/context-injector.js +74 -0
  25. package/dist/resources/extensions/gsd/context-store.js +4 -3
  26. package/dist/resources/extensions/gsd/custom-execution-policy.js +47 -0
  27. package/dist/resources/extensions/gsd/custom-verification.js +145 -0
  28. package/dist/resources/extensions/gsd/custom-workflow-engine.js +164 -0
  29. package/dist/resources/extensions/gsd/dashboard-overlay.js +1 -0
  30. package/dist/resources/extensions/gsd/db-writer.js +5 -2
  31. package/dist/resources/extensions/gsd/definition-loader.js +352 -0
  32. package/dist/resources/extensions/gsd/detection.js +1 -1
  33. package/dist/resources/extensions/gsd/dev-execution-policy.js +24 -0
  34. package/dist/resources/extensions/gsd/dev-workflow-engine.js +82 -0
  35. package/dist/resources/extensions/gsd/doctor.js +11 -1
  36. package/dist/resources/extensions/gsd/engine-resolver.js +40 -0
  37. package/dist/resources/extensions/gsd/engine-types.js +8 -0
  38. package/dist/resources/extensions/gsd/execution-policy.js +8 -0
  39. package/dist/resources/extensions/gsd/exit-command.js +12 -2
  40. package/dist/resources/extensions/gsd/export.js +9 -13
  41. package/dist/resources/extensions/gsd/extension-manifest.json +2 -2
  42. package/dist/resources/extensions/gsd/files.js +28 -11
  43. package/dist/resources/extensions/gsd/forensics.js +10 -3
  44. package/dist/resources/extensions/gsd/git-service.js +5 -1
  45. package/dist/resources/extensions/gsd/graph.js +225 -0
  46. package/dist/resources/extensions/gsd/gsd-db.js +25 -8
  47. package/dist/resources/extensions/gsd/guided-flow-queue.js +1 -1
  48. package/dist/resources/extensions/gsd/guided-flow.js +7 -3
  49. package/dist/resources/extensions/gsd/journal.js +85 -0
  50. package/dist/resources/extensions/gsd/md-importer.js +5 -0
  51. package/dist/resources/extensions/gsd/milestone-ids.js +1 -1
  52. package/dist/resources/extensions/gsd/native-git-bridge.js +2 -2
  53. package/dist/resources/extensions/gsd/post-unit-hooks.js +24 -412
  54. package/dist/resources/extensions/gsd/preferences-types.js +1 -0
  55. package/dist/resources/extensions/gsd/preferences.js +1 -0
  56. package/dist/resources/extensions/gsd/prompt-loader.js +34 -4
  57. package/dist/resources/extensions/gsd/prompts/complete-milestone.md +11 -10
  58. package/dist/resources/extensions/gsd/prompts/discuss-headless.md +2 -2
  59. package/dist/resources/extensions/gsd/prompts/discuss.md +1 -1
  60. package/dist/resources/extensions/gsd/prompts/queue.md +1 -1
  61. package/dist/resources/extensions/gsd/repo-identity.js +46 -2
  62. package/dist/resources/extensions/gsd/rule-registry.js +489 -0
  63. package/dist/resources/extensions/gsd/rule-types.js +6 -0
  64. package/dist/resources/extensions/gsd/run-manager.js +134 -0
  65. package/dist/resources/extensions/gsd/service-tier.js +138 -0
  66. package/dist/resources/extensions/gsd/structured-data-formatter.js +2 -1
  67. package/dist/resources/extensions/gsd/templates/decisions.md +2 -2
  68. package/dist/resources/extensions/gsd/workflow-engine.js +7 -0
  69. package/dist/resources/extensions/gsd/workflow-templates.js +13 -1
  70. package/dist/resources/extensions/gsd/worktree-manager.js +20 -6
  71. package/dist/resources/extensions/gsd/worktree-resolver.js +19 -2
  72. package/dist/resources/extensions/subagent/index.js +7 -3
  73. package/dist/resources/extensions/voice/index.js +4 -4
  74. package/dist/resources/skills/create-workflow/SKILL.md +103 -0
  75. package/dist/resources/skills/create-workflow/references/feature-patterns.md +128 -0
  76. package/dist/resources/skills/create-workflow/references/verification-policies.md +76 -0
  77. package/dist/resources/skills/create-workflow/references/yaml-schema-v1.md +46 -0
  78. package/dist/resources/skills/create-workflow/templates/blog-post-pipeline.yaml +60 -0
  79. package/dist/resources/skills/create-workflow/templates/code-audit.yaml +60 -0
  80. package/dist/resources/skills/create-workflow/templates/release-checklist.yaml +66 -0
  81. package/dist/resources/skills/create-workflow/templates/workflow-definition.yaml +32 -0
  82. package/dist/resources/skills/create-workflow/workflows/create-from-scratch.md +104 -0
  83. package/dist/resources/skills/create-workflow/workflows/create-from-template.md +72 -0
  84. package/dist/web/standalone/.next/BUILD_ID +1 -1
  85. package/dist/web/standalone/.next/app-path-routes-manifest.json +12 -12
  86. package/dist/web/standalone/.next/build-manifest.json +3 -3
  87. package/dist/web/standalone/.next/prerender-manifest.json +3 -3
  88. package/dist/web/standalone/.next/react-loadable-manifest.json +1 -1
  89. package/dist/web/standalone/.next/server/app/_global-error.html +2 -2
  90. package/dist/web/standalone/.next/server/app/_global-error.rsc +1 -1
  91. package/dist/web/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  92. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
  93. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
  94. package/dist/web/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  95. package/dist/web/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  96. package/dist/web/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  97. package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
  98. package/dist/web/standalone/.next/server/app/_not-found.rsc +1 -1
  99. package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +1 -1
  100. package/dist/web/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  101. package/dist/web/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +1 -1
  102. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  103. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  104. package/dist/web/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
  105. package/dist/web/standalone/.next/server/app/api/boot/route.js +1 -1
  106. package/dist/web/standalone/.next/server/app/api/captures/route.js +1 -1
  107. package/dist/web/standalone/.next/server/app/api/cleanup/route.js +1 -1
  108. package/dist/web/standalone/.next/server/app/api/doctor/route.js +1 -1
  109. package/dist/web/standalone/.next/server/app/api/export-data/route.js +1 -1
  110. package/dist/web/standalone/.next/server/app/api/forensics/route.js +1 -1
  111. package/dist/web/standalone/.next/server/app/api/history/route.js +1 -1
  112. package/dist/web/standalone/.next/server/app/api/hooks/route.js +1 -1
  113. package/dist/web/standalone/.next/server/app/api/recovery/route.js +1 -1
  114. package/dist/web/standalone/.next/server/app/api/settings-data/route.js +1 -1
  115. package/dist/web/standalone/.next/server/app/api/skill-health/route.js +1 -1
  116. package/dist/web/standalone/.next/server/app/api/terminal/input/route.js +1 -1
  117. package/dist/web/standalone/.next/server/app/api/terminal/resize/route.js +1 -1
  118. package/dist/web/standalone/.next/server/app/api/undo/route.js +1 -1
  119. package/dist/web/standalone/.next/server/app/api/visualizer/route.js +1 -1
  120. package/dist/web/standalone/.next/server/app/index.html +1 -1
  121. package/dist/web/standalone/.next/server/app/index.rsc +1 -1
  122. package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +1 -1
  123. package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +1 -1
  124. package/dist/web/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
  125. package/dist/web/standalone/.next/server/app/index.segments/_index.segment.rsc +1 -1
  126. package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
  127. package/dist/web/standalone/.next/server/app-paths-manifest.json +12 -12
  128. package/dist/web/standalone/.next/server/chunks/229.js +3 -3
  129. package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
  130. package/dist/web/standalone/.next/server/middleware-react-loadable-manifest.js +1 -1
  131. package/dist/web/standalone/.next/server/pages/404.html +1 -1
  132. package/dist/web/standalone/.next/server/pages/500.html +2 -2
  133. package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
  134. package/dist/web/standalone/.next/static/chunks/4024.c195dc1fdd2adbea.js +9 -0
  135. package/dist/web/standalone/.next/static/chunks/{webpack-9afaaebf6042a1d7.js → webpack-fa307370fcf9fb2c.js} +1 -1
  136. package/dist/web-mode.d.ts +2 -0
  137. package/dist/web-mode.js +29 -7
  138. package/package.json +1 -1
  139. package/packages/native/src/__tests__/text.test.mjs +33 -0
  140. package/packages/pi-coding-agent/dist/core/discovery-cache.test.js +3 -1
  141. package/packages/pi-coding-agent/dist/core/discovery-cache.test.js.map +1 -1
  142. package/packages/pi-coding-agent/dist/modes/interactive/components/login-dialog.d.ts.map +1 -1
  143. package/packages/pi-coding-agent/dist/modes/interactive/components/login-dialog.js +10 -7
  144. package/packages/pi-coding-agent/dist/modes/interactive/components/login-dialog.js.map +1 -1
  145. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  146. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js +4 -0
  147. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js.map +1 -1
  148. package/packages/pi-coding-agent/package.json +1 -1
  149. package/packages/pi-coding-agent/src/core/discovery-cache.test.ts +4 -2
  150. package/packages/pi-coding-agent/src/modes/interactive/components/login-dialog.ts +11 -7
  151. package/packages/pi-coding-agent/src/modes/interactive/interactive-mode.ts +5 -0
  152. package/pkg/package.json +1 -1
  153. package/src/resources/extensions/gsd/auto/loop-deps.ts +5 -1
  154. package/src/resources/extensions/gsd/auto/loop.ts +101 -1
  155. package/src/resources/extensions/gsd/auto/phases.ts +30 -10
  156. package/src/resources/extensions/gsd/auto/session.ts +6 -0
  157. package/src/resources/extensions/gsd/auto/types.ts +4 -0
  158. package/src/resources/extensions/gsd/auto-dashboard.ts +9 -2
  159. package/src/resources/extensions/gsd/auto-dispatch.ts +25 -5
  160. package/src/resources/extensions/gsd/auto-post-unit.ts +8 -0
  161. package/src/resources/extensions/gsd/auto-recovery.ts +12 -4
  162. package/src/resources/extensions/gsd/auto-start.ts +8 -3
  163. package/src/resources/extensions/gsd/auto-worktree.ts +162 -18
  164. package/src/resources/extensions/gsd/auto.ts +71 -2
  165. package/src/resources/extensions/gsd/bootstrap/db-tools.ts +209 -162
  166. package/src/resources/extensions/gsd/bootstrap/journal-tools.ts +62 -0
  167. package/src/resources/extensions/gsd/bootstrap/register-extension.ts +2 -0
  168. package/src/resources/extensions/gsd/bootstrap/register-hooks.ts +13 -0
  169. package/src/resources/extensions/gsd/bootstrap/tool-call-loop-guard.ts +9 -2
  170. package/src/resources/extensions/gsd/commands/catalog.ts +40 -1
  171. package/src/resources/extensions/gsd/commands/handlers/core.ts +1 -0
  172. package/src/resources/extensions/gsd/commands/handlers/ops.ts +5 -0
  173. package/src/resources/extensions/gsd/commands/handlers/workflow.ts +164 -0
  174. package/src/resources/extensions/gsd/context-injector.ts +100 -0
  175. package/src/resources/extensions/gsd/context-store.ts +4 -3
  176. package/src/resources/extensions/gsd/custom-execution-policy.ts +73 -0
  177. package/src/resources/extensions/gsd/custom-verification.ts +180 -0
  178. package/src/resources/extensions/gsd/custom-workflow-engine.ts +216 -0
  179. package/src/resources/extensions/gsd/dashboard-overlay.ts +1 -0
  180. package/src/resources/extensions/gsd/db-writer.ts +6 -2
  181. package/src/resources/extensions/gsd/definition-loader.ts +462 -0
  182. package/src/resources/extensions/gsd/detection.ts +1 -1
  183. package/src/resources/extensions/gsd/dev-execution-policy.ts +51 -0
  184. package/src/resources/extensions/gsd/dev-workflow-engine.ts +110 -0
  185. package/src/resources/extensions/gsd/doctor.ts +12 -1
  186. package/src/resources/extensions/gsd/engine-resolver.ts +57 -0
  187. package/src/resources/extensions/gsd/engine-types.ts +71 -0
  188. package/src/resources/extensions/gsd/execution-policy.ts +43 -0
  189. package/src/resources/extensions/gsd/exit-command.ts +14 -2
  190. package/src/resources/extensions/gsd/export.ts +8 -15
  191. package/src/resources/extensions/gsd/extension-manifest.json +2 -2
  192. package/src/resources/extensions/gsd/files.ts +29 -12
  193. package/src/resources/extensions/gsd/forensics.ts +9 -3
  194. package/src/resources/extensions/gsd/git-service.ts +5 -4
  195. package/src/resources/extensions/gsd/graph.ts +312 -0
  196. package/src/resources/extensions/gsd/gsd-db.ts +37 -8
  197. package/src/resources/extensions/gsd/guided-flow-queue.ts +1 -1
  198. package/src/resources/extensions/gsd/guided-flow.ts +7 -3
  199. package/src/resources/extensions/gsd/journal.ts +134 -0
  200. package/src/resources/extensions/gsd/md-importer.ts +6 -0
  201. package/src/resources/extensions/gsd/milestone-ids.ts +1 -1
  202. package/src/resources/extensions/gsd/native-git-bridge.ts +2 -2
  203. package/src/resources/extensions/gsd/post-unit-hooks.ts +24 -462
  204. package/src/resources/extensions/gsd/preferences-types.ts +3 -0
  205. package/src/resources/extensions/gsd/preferences.ts +1 -0
  206. package/src/resources/extensions/gsd/prompt-loader.ts +35 -4
  207. package/src/resources/extensions/gsd/prompts/complete-milestone.md +11 -10
  208. package/src/resources/extensions/gsd/prompts/discuss-headless.md +2 -2
  209. package/src/resources/extensions/gsd/prompts/discuss.md +1 -1
  210. package/src/resources/extensions/gsd/prompts/queue.md +1 -1
  211. package/src/resources/extensions/gsd/repo-identity.ts +47 -2
  212. package/src/resources/extensions/gsd/rule-registry.ts +599 -0
  213. package/src/resources/extensions/gsd/rule-types.ts +68 -0
  214. package/src/resources/extensions/gsd/run-manager.ts +180 -0
  215. package/src/resources/extensions/gsd/service-tier.ts +171 -0
  216. package/src/resources/extensions/gsd/structured-data-formatter.ts +3 -1
  217. package/src/resources/extensions/gsd/templates/decisions.md +2 -2
  218. package/src/resources/extensions/gsd/tests/auto-loop.test.ts +103 -120
  219. package/src/resources/extensions/gsd/tests/auto-recovery.test.ts +85 -0
  220. package/src/resources/extensions/gsd/tests/auto-secrets-gate.test.ts +2 -2
  221. package/src/resources/extensions/gsd/tests/auto-worktree-milestone-merge.test.ts +202 -0
  222. package/src/resources/extensions/gsd/tests/bundled-workflow-defs.test.ts +180 -0
  223. package/src/resources/extensions/gsd/tests/captures.test.ts +12 -1
  224. package/src/resources/extensions/gsd/tests/commands-workflow-custom.test.ts +283 -0
  225. package/src/resources/extensions/gsd/tests/context-injector.test.ts +313 -0
  226. package/src/resources/extensions/gsd/tests/context-store.test.ts +10 -5
  227. package/src/resources/extensions/gsd/tests/continue-here.test.ts +20 -20
  228. package/src/resources/extensions/gsd/tests/custom-engine-loop-integration.test.ts +540 -0
  229. package/src/resources/extensions/gsd/tests/custom-verification.test.ts +382 -0
  230. package/src/resources/extensions/gsd/tests/custom-workflow-engine.test.ts +339 -0
  231. package/src/resources/extensions/gsd/tests/dashboard-custom-engine.test.ts +87 -0
  232. package/src/resources/extensions/gsd/tests/db-writer.test.ts +10 -0
  233. package/src/resources/extensions/gsd/tests/definition-loader.test.ts +778 -0
  234. package/src/resources/extensions/gsd/tests/dev-engine-wrapper.test.ts +318 -0
  235. package/src/resources/extensions/gsd/tests/doctor-completion-deferral.test.ts +15 -10
  236. package/src/resources/extensions/gsd/tests/doctor-fixlevel.test.ts +5 -4
  237. package/src/resources/extensions/gsd/tests/doctor-roadmap-summary-atomicity.test.ts +167 -0
  238. package/src/resources/extensions/gsd/tests/doctor-task-done-missing-summary-slice-loop.test.ts +174 -0
  239. package/src/resources/extensions/gsd/tests/e2e-workflow-pipeline-integration.test.ts +476 -0
  240. package/src/resources/extensions/gsd/tests/engine-interfaces-contract.test.ts +271 -0
  241. package/src/resources/extensions/gsd/tests/exit-command.test.ts +55 -0
  242. package/src/resources/extensions/gsd/tests/graph-operations.test.ts +599 -0
  243. package/src/resources/extensions/gsd/tests/gsd-db.test.ts +8 -1
  244. package/src/resources/extensions/gsd/tests/gsd-tools.test.ts +7 -7
  245. package/src/resources/extensions/gsd/tests/iterate-engine-integration.test.ts +429 -0
  246. package/src/resources/extensions/gsd/tests/journal-integration.test.ts +513 -0
  247. package/src/resources/extensions/gsd/tests/journal-query-tool.test.ts +147 -0
  248. package/src/resources/extensions/gsd/tests/journal.test.ts +386 -0
  249. package/src/resources/extensions/gsd/tests/md-importer.test.ts +31 -1
  250. package/src/resources/extensions/gsd/tests/memory-store.test.ts +2 -2
  251. package/src/resources/extensions/gsd/tests/milestone-id-reservation.test.ts +1 -1
  252. package/src/resources/extensions/gsd/tests/parsers.test.ts +110 -0
  253. package/src/resources/extensions/gsd/tests/preferences.test.ts +47 -25
  254. package/src/resources/extensions/gsd/tests/prompt-db.test.ts +3 -1
  255. package/src/resources/extensions/gsd/tests/repo-identity-worktree.test.ts +61 -1
  256. package/src/resources/extensions/gsd/tests/routing-history.test.ts +11 -22
  257. package/src/resources/extensions/gsd/tests/rule-registry.test.ts +413 -0
  258. package/src/resources/extensions/gsd/tests/run-manager.test.ts +229 -0
  259. package/src/resources/extensions/gsd/tests/service-tier.test.ts +98 -0
  260. package/src/resources/extensions/gsd/tests/skill-lifecycle.test.ts +2 -2
  261. package/src/resources/extensions/gsd/tests/stalled-tool-recovery.test.ts +102 -0
  262. package/src/resources/extensions/gsd/tests/structured-data-formatter.test.ts +4 -3
  263. package/src/resources/extensions/gsd/tests/tool-call-loop-guard.test.ts +45 -0
  264. package/src/resources/extensions/gsd/tests/tool-naming.test.ts +117 -0
  265. package/src/resources/extensions/gsd/tests/triage-dispatch.test.ts +6 -1
  266. package/src/resources/extensions/gsd/tests/windows-path-normalization.test.ts +99 -0
  267. package/src/resources/extensions/gsd/tests/worktree-db-integration.test.ts +1 -0
  268. package/src/resources/extensions/gsd/tests/worktree-db.test.ts +4 -0
  269. package/src/resources/extensions/gsd/tests/worktree-health-dispatch.test.ts +178 -0
  270. package/src/resources/extensions/gsd/tests/worktree-manager.test.ts +195 -105
  271. package/src/resources/extensions/gsd/tests/worktree-resolver.test.ts +78 -3
  272. package/src/resources/extensions/gsd/tests/worktree-symlink-removal.test.ts +140 -0
  273. package/src/resources/extensions/gsd/tests/worktree-sync-milestones.test.ts +74 -0
  274. package/src/resources/extensions/gsd/types.ts +3 -0
  275. package/src/resources/extensions/gsd/workflow-engine.ts +38 -0
  276. package/src/resources/extensions/gsd/workflow-templates.ts +12 -1
  277. package/src/resources/extensions/gsd/worktree-manager.ts +21 -6
  278. package/src/resources/extensions/gsd/worktree-resolver.ts +30 -9
  279. package/src/resources/extensions/subagent/index.ts +7 -3
  280. package/src/resources/extensions/voice/index.ts +4 -4
  281. package/src/resources/skills/create-workflow/SKILL.md +103 -0
  282. package/src/resources/skills/create-workflow/references/feature-patterns.md +128 -0
  283. package/src/resources/skills/create-workflow/references/verification-policies.md +76 -0
  284. package/src/resources/skills/create-workflow/references/yaml-schema-v1.md +46 -0
  285. package/src/resources/skills/create-workflow/templates/blog-post-pipeline.yaml +60 -0
  286. package/src/resources/skills/create-workflow/templates/code-audit.yaml +60 -0
  287. package/src/resources/skills/create-workflow/templates/release-checklist.yaml +66 -0
  288. package/src/resources/skills/create-workflow/templates/workflow-definition.yaml +32 -0
  289. package/src/resources/skills/create-workflow/workflows/create-from-scratch.md +104 -0
  290. package/src/resources/skills/create-workflow/workflows/create-from-template.md +72 -0
  291. package/dist/web/standalone/.next/static/chunks/4024.279c423e4661ece1.js +0 -9
  292. /package/dist/web/standalone/.next/static/{Ute3pMouVczQyT15qrBBO → QSuuaY2tP8h_zzhHcVxNj}/_buildManifest.js +0 -0
  293. /package/dist/web/standalone/.next/static/{Ute3pMouVczQyT15qrBBO → QSuuaY2tP8h_zzhHcVxNj}/_ssgManifest.js +0 -0
package/README.md CHANGED
@@ -24,35 +24,75 @@ One command. Walk away. Come back to a built project with clean git history.
24
24
 
25
25
  ---
26
26
 
27
- ## What's New in v2.39–v2.40
28
-
29
- - **GitHub sync extension** — auto-sync milestones, slices, and tasks to GitHub Issues, PRs, and Milestones. Opt in with `github.enabled: true` in preferences. Requires `gh` CLI.
30
- - **Skill tool resolution** — skills are now resolved and activated automatically in dispatched prompts based on `always_use_skills`, `prefer_skills`, and `skill_rules` preferences. Skills are matched to dispatch context at build time.
31
- - **Health check phase 2** — `/gsd doctor` issues now surface in real time across the dashboard widget, workflow visualizer, and HTML reports with severity levels (error/warning/info).
32
- - **Forensics upgrade** — `/gsd forensics` is now a full-access GSD debugger with structured anomaly detection (stuck loops, cost spikes, timeouts, missing artifacts), unit traces, and LLM-guided root-cause analysis.
33
- - **Auto PR on milestone completion** — set `git.auto_pr: true` to automatically create a draft PR when a milestone completes. Requires `auto_push: true` and `gh` CLI.
34
- - **RUNTIME.md template** — declare project-level runtime context (API endpoints, env vars, deployment info) in `.gsd/RUNTIME.md`. Inlined into task execution prompts to prevent hallucination.
35
- - **Welcome screen** — branded startup UI showing version, active model, available tool keys, and quick-start commands.
36
- - **`GSD_HOME` and `GSD_PROJECT_ID` env vars** — override the global `~/.gsd` directory and per-project identity hash for CI/CD and multi-clone environments.
37
- - **Browser and runtime UAT types** — new `browser-executable` and `runtime-executable` UAT types control when auto-mode pauses for validation.
38
- - **Pipeline decomposition** auto-loop rewritten from recursive dispatch to a linear phase pipeline (pre-dispatch → dispatch → post-unit → verification → stuck detection) for better debuggability.
39
- - **Sliding-window stuck detection** — replaces the simple counter with a pattern-aware sliding window, reducing false positives on legitimate retries.
40
- - **Data-loss recovery** — automatic detection and recovery of `.gsd/` data loss from v2.30.0–v2.38.0 migration issues, with atomic migration and rollback on failure.
41
- - **Model preferences in guided flow** — per-phase model selection now applies in step mode, not just auto mode.
42
-
43
- See the full [Changelog](./CHANGELOG.md) for details.
44
-
45
- ### Previous highlights (v2.34–v2.38)
46
-
47
- - **Reactive task execution (ADR-004)** — graph-derived parallel task dispatch within slices
48
- - **Anthropic Vertex AI provider** — Claude on Google Vertex AI
49
- - **cmux integration** — sidebar status, progress bars, and notifications for cmux terminal multiplexer users
50
- - **Redesigned dashboard** — two-column layout with 4 widget modes (full small min off)
51
- - **AGENTS.md support** — deprecated `agent-instructions.md` in favor of standard `AGENTS.md` / `CLAUDE.md`
52
- - **AI-powered triage** — automated issue and PR triage via Claude Haiku
53
- - **Auto-generated OpenRouter registry** — model registry built from OpenRouter API
54
- - **`/gsd changelog`**LLM-summarized release notes for any version
55
- - **Search budget enforcement** — session-level cap prevents unbounded web search
27
+ ## What's New in v2.41.0
28
+
29
+ ### New Features
30
+
31
+ - **Browser-based web interface** — run GSD from the browser with `gsd --web`. Full project management, real-time progress, and multi-project support via server-sent events. (#1717)
32
+ - **Doctor: worktree lifecycle checks** — `/gsd doctor` now validates worktree health, detects orphaned worktrees, consolidates cleanup, and enhances `/worktree list` with lifecycle status. (#1814)
33
+ - **CI: docs-only PR detection** — PRs that only change documentation skip build and test steps, with a new prompt injection scan for security. (#1699)
34
+ - **Custom Models guide** — new documentation for adding custom providers (Ollama, vLLM, LM Studio, proxies) via `models.json`. (#1670)
35
+
36
+ ### Data Loss Prevention (Critical Fixes)
37
+
38
+ This release includes 7 fixes preventing silent data loss in auto-mode:
39
+
40
+ - **Hallucination guard** — execute-task agents that complete with zero tool calls are now rejected as hallucinated. Previously, agents could produce detailed but fabricated summaries without writing any code, wasting ~$25/milestone. (#1838)
41
+ - **Merge anchor verification** — before deleting a milestone worktree/branch, GSD now verifies the code is actually on the integration branch. Prevents orphaning commits when squash-merge produces an empty diff. (#1829)
42
+ - **Dirty working tree detection** — `nativeMergeSquash` now distinguishes dirty-tree rejections from content conflicts, preventing silent commit loss when synced `.gsd/` files block the merge. (#1752)
43
+ - **Doctor cleanup safety** — the `orphaned_completed_units` check no longer auto-fixes during post-task health checks. Previously, timing races could cause the doctor to remove valid completion keys, reverting users to earlier tasks. (#1825)
44
+ - **Root file reverse-sync** — worktree teardown now syncs root-level `.gsd/` files (PROJECT.md, REQUIREMENTS.md, completed-units.json) back to the project root. Previously these were lost on milestone closeout. (#1831)
45
+ - **Empty merge guard** — milestone branches with unanchored code changes are preserved instead of deleted when squash-merge produces nothing to commit. (#1755)
46
+ - **Crash-safe task closeout** — orphaned checkboxes in PLAN.md are unchecked on retry, preventing phantom task completion. (#1759)
47
+
48
+ ### Auto-Mode Stability
49
+
50
+ - **Terminal hang fix** — `stopAuto()` now resolves pending promises, preventing the terminal from freezing permanently after stopping auto-mode. (#1818)
51
+ - **Signal handler coverage** — SIGHUP and SIGINT now clean up lock files, not just SIGTERM. Prevents stranded locks on VS-Code crash. (#1821)
52
+ - **Needs-discussion routing** — milestones in `needs-discussion` phase now route to the smart entry UI instead of hard-stopping, breaking the infinite loop. (#1820)
53
+ - **Infrastructure error handling** — auto-mode stops immediately on ENOSPC, ENOMEM, and similar unrecoverable errors instead of retrying. (#1780)
54
+ - **Dependency-aware dispatch**slice dispatch now uses declared `depends_on` instead of positional ordering. (#1770)
55
+ - **Queue mode depth verification** — the write gate now processes depth verification in queue mode, fixing a deadlock where CONTEXT.md writes were permanently blocked. (#1823)
56
+
57
+ ### Roadmap Parser Improvements
58
+
59
+ - **Table format support** — roadmaps using markdown tables (`| S01 | Title | Risk | Status |`) are now parsed correctly. (#1741)
60
+ - **Prose header fallback** — when `## Slices` contains H3 headers instead of checkboxes, the prose parser is invoked as a fallback. (#1744)
61
+ - **Completion marker detection** — prose headers with `✓` or `(Complete)` markers are correctly identified as done. (#1816)
62
+ - **Zero-slice stub handling** — stub roadmaps from `/gsd queue` return `pre-planning` instead of `blocked`. (#1826)
63
+ - **Immediate roadmap fix** — roadmap checkbox and UAT stub are fixed immediately after last task instead of deferring to `complete-slice`. (#1819)
64
+
65
+ ### State & Git Improvements
66
+
67
+ - **CONTEXT-DRAFT.md fallback** — `depends_on` is read from CONTEXT-DRAFT.md when CONTEXT.md doesn't exist, preventing draft milestones from being promoted past dependency constraints. (#1743)
68
+ - **Unborn branch support** — `nativeBranchExists` handles repos with zero commits, preventing dispatch deadlock on new repos. (#1815)
69
+ - **Ghost milestone detection** — empty `.gsd/milestones/` directories are skipped instead of crashing `deriveState()`. (#1817)
70
+ - **Default branch detection** — milestone merge detects `master` vs `main` instead of hardcoding. (#1669)
71
+ - **Milestone title extraction** — titles are pulled from CONTEXT.md headings when no ROADMAP exists. (#1729)
72
+
73
+ ### Windows & Platform
74
+
75
+ - **Windows path handling** — 8.3 short paths, `pathToFileURL` for ESM imports, and `realpathSync.native` fixes across the test suite and verification gate. (#1804)
76
+ - **DEP0190 fix** — `spawnSync` deprecation warning eliminated by passing commands to shell explicitly. (#1827)
77
+ - **Web build skip on Windows** — Next.js webpack EPERM errors on system directories are handled gracefully.
78
+
79
+ ### Developer Experience
80
+
81
+ - **@ file finder fix** — typing `@` no longer freezes the TUI. The fix adds debounce, dedup, and empty-query short-circuit. (#1832)
82
+ - **Tool-call loop guard** — detects and breaks infinite tool-call loops within a single unit, preventing stack overflow. (#1801)
83
+ - **Completion deferral fix** — roadmap checkbox and UAT stub are fixed at task level, closing the fragile handoff window between last task and `complete-slice`. (#1819)
84
+
85
+ See the full [Changelog](./CHANGELOG.md) for all 70+ fixes in this release.
86
+
87
+ ### Previous highlights (v2.39–v2.40)
88
+
89
+ - **GitHub sync extension** — auto-sync milestones to GitHub Issues, PRs, and Milestones
90
+ - **Skill tool resolution** — skills auto-activate in dispatched prompts
91
+ - **Health check phase 2** — real-time doctor issues in dashboard and visualizer
92
+ - **Forensics upgrade** — full-access GSD debugger with anomaly detection
93
+ - **Pipeline decomposition** — auto-loop rewritten as linear phase pipeline
94
+ - **Sliding-window stuck detection** — pattern-aware, fewer false positives
95
+ - **Data-loss recovery** — automatic detection and recovery from v2.30–v2.38 migration issues
56
96
 
57
97
  ---
58
98
 
@@ -13,6 +13,12 @@ export interface CliFlags {
13
13
  web?: boolean;
14
14
  /** Optional project path for web mode: `gsd --web <path>` or `gsd web start <path>` */
15
15
  webPath?: string;
16
+ /** Custom host to bind web server to: `--host 0.0.0.0` */
17
+ webHost?: string;
18
+ /** Custom port for web server: `--port 8080` */
19
+ webPort?: number;
20
+ /** Additional allowed origins for CORS: `--allowed-origins http://192.168.1.10:8080` */
21
+ webAllowedOrigins?: string[];
16
22
  help?: boolean;
17
23
  version?: boolean;
18
24
  }
@@ -29,6 +29,20 @@ export function parseCliArgs(argv) {
29
29
  flags.webPath = args[++i];
30
30
  }
31
31
  }
32
+ else if (arg === '--host' && i + 1 < args.length) {
33
+ flags.webHost = args[++i];
34
+ }
35
+ else if (arg === '--port' && i + 1 < args.length) {
36
+ const portStr = args[++i];
37
+ const port = parseInt(portStr, 10);
38
+ if (Number.isFinite(port) && port > 0 && port < 65536) {
39
+ flags.webPort = port;
40
+ }
41
+ }
42
+ else if (arg === '--allowed-origins' && i + 1 < args.length) {
43
+ const origins = args[++i].split(',').map(o => o.trim()).filter(Boolean);
44
+ flags.webAllowedOrigins = (flags.webAllowedOrigins ?? []).concat(origins);
45
+ }
32
46
  else if (arg === '--model' && i + 1 < args.length) {
33
47
  flags.model = args[++i];
34
48
  }
@@ -216,6 +230,9 @@ export async function runWebCliBranch(flags, deps = {}) {
216
230
  cwd: currentCwd,
217
231
  projectSessionsDir,
218
232
  agentDir,
233
+ host: flags.webHost,
234
+ port: flags.webPort,
235
+ allowedOrigins: flags.webAllowedOrigins,
219
236
  });
220
237
  if (!status.ok) {
221
238
  emitWebModeFailure(stderr, status);
@@ -94,7 +94,8 @@ async function loadPico() {
94
94
  /** Open a URL in the system browser (best-effort, non-blocking) */
95
95
  function openBrowser(url) {
96
96
  if (process.platform === 'win32') {
97
- execFile('cmd', ['/c', 'start', '', url], () => { });
97
+ // PowerShell's Start-Process handles URLs with '&' safely; cmd /c start does not.
98
+ execFile('powershell', ['-c', `Start-Process '${url.replace(/'/g, "''")}'`], () => { });
98
99
  }
99
100
  else {
100
101
  const cmd = process.platform === 'darwin' ? 'open' : 'xdg-open';
@@ -6,11 +6,13 @@
6
6
  *
7
7
  * Imports from: auto/types, auto/resolve, auto/phases
8
8
  */
9
+ import { randomUUID } from "node:crypto";
9
10
  import { MAX_LOOP_ITERATIONS, } from "./types.js";
10
11
  import { _clearCurrentResolve } from "./resolve.js";
11
12
  import { runPreDispatch, runDispatch, runGuards, runUnitPhase, runFinalize, } from "./phases.js";
12
13
  import { debugLog } from "../debug-logger.js";
13
14
  import { isInfrastructureError } from "./infra-errors.js";
15
+ import { resolveEngine } from "../engine-resolver.js";
14
16
  /**
15
17
  * Main auto-mode execution loop. Iterates: derive → dispatch → guards →
16
18
  * runUnit → finalize → repeat. Exits when s.active becomes false or a
@@ -27,6 +29,10 @@ export async function autoLoop(ctx, pi, s, deps) {
27
29
  while (s.active) {
28
30
  iteration++;
29
31
  debugLog("autoLoop", { phase: "loop-top", iteration });
32
+ // ── Journal: per-iteration flow grouping ──
33
+ const flowId = randomUUID();
34
+ let seqCounter = 0;
35
+ const nextSeq = () => ++seqCounter;
30
36
  if (iteration > MAX_LOOP_ITERATIONS) {
31
37
  debugLog("autoLoop", {
32
38
  phase: "exit",
@@ -53,6 +59,7 @@ export async function autoLoop(ctx, pi, s, deps) {
53
59
  unitType: sidecarItem.unitType,
54
60
  unitId: sidecarItem.unitId,
55
61
  });
62
+ deps.emitJournalEvent({ ts: new Date().toISOString(), flowId, seq: nextSeq(), eventType: "sidecar-dequeue", data: { kind: sidecarItem.kind, unitType: sidecarItem.unitType, unitId: sidecarItem.unitId } });
56
63
  }
57
64
  const sessionLockBase = deps.lockBase();
58
65
  if (sessionLockBase) {
@@ -73,8 +80,88 @@ export async function autoLoop(ctx, pi, s, deps) {
73
80
  break;
74
81
  }
75
82
  }
76
- const ic = { ctx, pi, s, deps, prefs, iteration };
83
+ const ic = { ctx, pi, s, deps, prefs, iteration, flowId, nextSeq };
84
+ deps.emitJournalEvent({ ts: new Date().toISOString(), flowId, seq: nextSeq(), eventType: "iteration-start", data: { iteration } });
77
85
  let iterData;
86
+ // ── Custom engine path ──────────────────────────────────────────────
87
+ // When activeEngineId is a non-dev value, bypass runPreDispatch and
88
+ // runDispatch entirely — the custom engine drives its own state via
89
+ // GRAPH.yaml. Shares runGuards and runUnitPhase with the dev path.
90
+ // After unit execution, verifies then reconciles via the engine layer.
91
+ //
92
+ // GSD_ENGINE_BYPASS=1 skips the engine layer entirely — falls through
93
+ // to the dev path below.
94
+ if (s.activeEngineId != null && s.activeEngineId !== "dev" && !sidecarItem && process.env.GSD_ENGINE_BYPASS !== "1") {
95
+ debugLog("autoLoop", { phase: "custom-engine-derive", iteration, engineId: s.activeEngineId });
96
+ const { engine, policy } = resolveEngine({
97
+ activeEngineId: s.activeEngineId,
98
+ activeRunDir: s.activeRunDir,
99
+ });
100
+ const engineState = await engine.deriveState(s.basePath);
101
+ if (engineState.isComplete) {
102
+ await deps.stopAuto(ctx, pi, "Workflow complete");
103
+ break;
104
+ }
105
+ debugLog("autoLoop", { phase: "custom-engine-dispatch", iteration });
106
+ const dispatch = await engine.resolveDispatch(engineState, { basePath: s.basePath });
107
+ if (dispatch.action === "stop") {
108
+ await deps.stopAuto(ctx, pi, dispatch.reason ?? "Engine stopped");
109
+ break;
110
+ }
111
+ if (dispatch.action === "skip") {
112
+ continue;
113
+ }
114
+ // dispatch.action === "dispatch"
115
+ const step = dispatch.step;
116
+ const gsdState = await deps.deriveState(s.basePath);
117
+ iterData = {
118
+ unitType: step.unitType,
119
+ unitId: step.unitId,
120
+ prompt: step.prompt,
121
+ finalPrompt: step.prompt,
122
+ pauseAfterUatDispatch: false,
123
+ observabilityIssues: [],
124
+ state: gsdState,
125
+ mid: s.currentMilestoneId ?? "workflow",
126
+ midTitle: "Workflow",
127
+ isRetry: false,
128
+ previousTier: undefined,
129
+ };
130
+ // ── Progress widget (mirrors dev path in runDispatch) ──
131
+ deps.updateProgressWidget(ctx, iterData.unitType, iterData.unitId, iterData.state);
132
+ // ── Guards (shared with dev path) ──
133
+ const guardsResult = await runGuards(ic, s.currentMilestoneId ?? "workflow");
134
+ if (guardsResult.action === "break")
135
+ break;
136
+ // ── Unit execution (shared with dev path) ──
137
+ const unitPhaseResult = await runUnitPhase(ic, iterData, loopState);
138
+ if (unitPhaseResult.action === "break")
139
+ break;
140
+ // ── Verify first, then reconcile (only mark complete on pass) ──
141
+ debugLog("autoLoop", { phase: "custom-engine-verify", iteration, unitId: iterData.unitId });
142
+ const verifyResult = await policy.verify(iterData.unitType, iterData.unitId, { basePath: s.basePath });
143
+ if (verifyResult === "pause") {
144
+ await deps.pauseAuto(ctx, pi);
145
+ break;
146
+ }
147
+ if (verifyResult === "retry") {
148
+ debugLog("autoLoop", { phase: "custom-engine-verify-retry", iteration, unitId: iterData.unitId });
149
+ continue;
150
+ }
151
+ // Verification passed — mark step complete
152
+ debugLog("autoLoop", { phase: "custom-engine-reconcile", iteration, unitId: iterData.unitId });
153
+ await engine.reconcile(engineState, {
154
+ unitType: iterData.unitType,
155
+ unitId: iterData.unitId,
156
+ startedAt: s.currentUnit?.startedAt ?? Date.now(),
157
+ finishedAt: Date.now(),
158
+ });
159
+ deps.clearUnitTimeout();
160
+ consecutiveErrors = 0;
161
+ deps.emitJournalEvent({ ts: new Date().toISOString(), flowId, seq: nextSeq(), eventType: "iteration-end", data: { iteration } });
162
+ debugLog("autoLoop", { phase: "iteration-complete", iteration });
163
+ continue;
164
+ }
78
165
  if (!sidecarItem) {
79
166
  // ── Phase 1: Pre-dispatch ─────────────────────────────────────────
80
167
  const preDispatchResult = await runPreDispatch(ic, loopState);
@@ -121,6 +208,7 @@ export async function autoLoop(ctx, pi, s, deps) {
121
208
  if (finalizeResult.action === "continue")
122
209
  continue;
123
210
  consecutiveErrors = 0; // Iteration completed successfully
211
+ deps.emitJournalEvent({ ts: new Date().toISOString(), flowId, seq: nextSeq(), eventType: "iteration-end", data: { iteration } });
124
212
  debugLog("autoLoop", { phase: "iteration-complete", iteration });
125
213
  }
126
214
  catch (loopErr) {
@@ -13,6 +13,7 @@ import { runUnit } from "./run-unit.js";
13
13
  import { debugLog } from "../debug-logger.js";
14
14
  import { gsdRoot } from "../paths.js";
15
15
  import { atomicWriteSync } from "../atomic-write.js";
16
+ import { PROJECT_FILES } from "../detection.js";
16
17
  import { join } from "node:path";
17
18
  // ─── generateMilestoneReport ──────────────────────────────────────────────────
18
19
  /**
@@ -121,6 +122,7 @@ export async function runPreDispatch(ic, loopState) {
121
122
  });
122
123
  // ── Milestone transition ────────────────────────────────────────────
123
124
  if (mid && s.currentMilestoneId && mid !== s.currentMilestoneId) {
125
+ deps.emitJournalEvent({ ts: new Date().toISOString(), flowId: ic.flowId, seq: ic.nextSeq(), eventType: "milestone-transition", data: { from: s.currentMilestoneId, to: mid } });
124
126
  ctx.ui.notify(`Milestone ${s.currentMilestoneId} complete. Advancing to ${mid}: ${midTitle}.`, "info");
125
127
  deps.sendDesktopNotification("GSD", `Milestone ${s.currentMilestoneId} complete!`, "success", "milestone");
126
128
  deps.logCmuxEvent(prefs, `Milestone ${s.currentMilestoneId} complete. Advancing to ${mid}.`, "success");
@@ -248,6 +250,7 @@ export async function runPreDispatch(ic, loopState) {
248
250
  await deps.stopAuto(ctx, pi, `No active milestone — ${incomplete.length} incomplete (${ids}), see diagnostic above`);
249
251
  }
250
252
  debugLog("autoLoop", { phase: "exit", reason: "no-active-milestone" });
253
+ deps.emitJournalEvent({ ts: new Date().toISOString(), flowId: ic.flowId, seq: ic.nextSeq(), eventType: "terminal", data: { reason: "no-active-milestone" } });
251
254
  return { action: "break", reason: "no-active-milestone" };
252
255
  }
253
256
  if (!midTitle) {
@@ -295,6 +298,7 @@ export async function runPreDispatch(ic, loopState) {
295
298
  deps.logCmuxEvent(prefs, `Milestone ${mid} complete.`, "success");
296
299
  await closeoutAndStop(ctx, pi, s, deps, `Milestone ${mid} complete`);
297
300
  debugLog("autoLoop", { phase: "exit", reason: "milestone-complete" });
301
+ deps.emitJournalEvent({ ts: new Date().toISOString(), flowId: ic.flowId, seq: ic.nextSeq(), eventType: "terminal", data: { reason: "milestone-complete", milestoneId: mid } });
298
302
  return { action: "break", reason: "milestone-complete" };
299
303
  }
300
304
  // Terminal: blocked
@@ -305,6 +309,7 @@ export async function runPreDispatch(ic, loopState) {
305
309
  deps.sendDesktopNotification("GSD", blockerMsg, "error", "attention");
306
310
  deps.logCmuxEvent(prefs, blockerMsg, "error");
307
311
  debugLog("autoLoop", { phase: "exit", reason: "blocked" });
312
+ deps.emitJournalEvent({ ts: new Date().toISOString(), flowId: ic.flowId, seq: ic.nextSeq(), eventType: "terminal", data: { reason: "blocked", blockers: state.blockers } });
308
313
  return { action: "break", reason: "blocked" };
309
314
  }
310
315
  return { action: "next", data: { state, mid, midTitle } };
@@ -328,6 +333,7 @@ export async function runDispatch(ic, preData, loopState) {
328
333
  session: s,
329
334
  });
330
335
  if (dispatchResult.action === "stop") {
336
+ deps.emitJournalEvent({ ts: new Date().toISOString(), flowId: ic.flowId, seq: ic.nextSeq(), eventType: "dispatch-stop", rule: dispatchResult.matchedRule, data: { reason: dispatchResult.reason } });
331
337
  await closeoutAndStop(ctx, pi, s, deps, dispatchResult.reason);
332
338
  debugLog("autoLoop", { phase: "exit", reason: "dispatch-stop" });
333
339
  return { action: "break", reason: "dispatch-stop" };
@@ -337,6 +343,7 @@ export async function runDispatch(ic, preData, loopState) {
337
343
  await new Promise((r) => setImmediate(r));
338
344
  return { action: "continue" };
339
345
  }
346
+ deps.emitJournalEvent({ ts: new Date().toISOString(), flowId: ic.flowId, seq: ic.nextSeq(), eventType: "dispatch-match", rule: dispatchResult.matchedRule, data: { unitType: dispatchResult.unitType, unitId: dispatchResult.unitId } });
340
347
  let unitType = dispatchResult.unitType;
341
348
  let unitId = dispatchResult.unitId;
342
349
  let prompt = dispatchResult.prompt;
@@ -402,6 +409,7 @@ export async function runDispatch(ic, preData, loopState) {
402
409
  const preDispatchResult = deps.runPreDispatchHooks(unitType, unitId, prompt, s.basePath);
403
410
  if (preDispatchResult.firedHooks.length > 0) {
404
411
  ctx.ui.notify(`Pre-dispatch hook${preDispatchResult.firedHooks.length > 1 ? "s" : ""}: ${preDispatchResult.firedHooks.join(", ")}`, "info");
412
+ deps.emitJournalEvent({ ts: new Date().toISOString(), flowId: ic.flowId, seq: ic.nextSeq(), eventType: "pre-dispatch-hook", data: { firedHooks: preDispatchResult.firedHooks, action: preDispatchResult.action } });
405
413
  }
406
414
  if (preDispatchResult.action === "skip") {
407
415
  ctx.ui.notify(`Skipping ${unitType} ${unitId} (pre-dispatch hook).`, "info");
@@ -544,25 +552,27 @@ export async function runUnitPhase(ic, iterData, loopState, sidecarItem) {
544
552
  unitType,
545
553
  unitId,
546
554
  });
547
- // ── Worktree health check (#1833) ───────────────────────────────────
555
+ // ── Worktree health check (#1833, #1843) ────────────────────────────
548
556
  // Verify the working directory is a valid git checkout with project
549
557
  // files before dispatching work. A broken worktree causes agents to
550
558
  // hallucinate summaries since they cannot read or write any files.
559
+ // Uses the shared PROJECT_FILES list from detection.ts to support all
560
+ // ecosystems (Rust, Go, Python, Java, etc.), not just JS.
551
561
  if (s.basePath && unitType === "execute-task") {
552
562
  const gitMarker = join(s.basePath, ".git");
553
563
  const hasGit = deps.existsSync(gitMarker);
554
- const hasPackageJson = deps.existsSync(join(s.basePath, "package.json"));
555
- const hasSrcDir = deps.existsSync(join(s.basePath, "src"));
556
564
  if (!hasGit) {
557
565
  const msg = `Worktree health check failed: ${s.basePath} has no .git — refusing to dispatch ${unitType} ${unitId}`;
558
- debugLog("runUnitPhase", { phase: "worktree-health-fail", basePath: s.basePath, hasGit, hasPackageJson, hasSrcDir });
566
+ debugLog("runUnitPhase", { phase: "worktree-health-fail", basePath: s.basePath, hasGit });
559
567
  ctx.ui.notify(msg, "error");
560
568
  await deps.stopAuto(ctx, pi, msg);
561
569
  return { action: "break", reason: "worktree-invalid" };
562
570
  }
563
- if (!hasPackageJson && !hasSrcDir) {
564
- const msg = `Worktree health check failed: ${s.basePath} has no package.json or src/ — refusing to dispatch ${unitType} ${unitId}`;
565
- debugLog("runUnitPhase", { phase: "worktree-health-fail", basePath: s.basePath, hasGit, hasPackageJson, hasSrcDir });
571
+ const hasProjectFile = PROJECT_FILES.some((f) => deps.existsSync(join(s.basePath, f)));
572
+ const hasSrcDir = deps.existsSync(join(s.basePath, "src"));
573
+ if (!hasProjectFile && !hasSrcDir) {
574
+ const msg = `Worktree health check failed: ${s.basePath} has no recognized project files — refusing to dispatch ${unitType} ${unitId}`;
575
+ debugLog("runUnitPhase", { phase: "worktree-health-fail", basePath: s.basePath, hasProjectFile, hasSrcDir });
566
576
  ctx.ui.notify(msg, "error");
567
577
  await deps.stopAuto(ctx, pi, msg);
568
578
  return { action: "break", reason: "worktree-invalid" };
@@ -574,6 +584,8 @@ export async function runUnitPhase(ic, iterData, loopState, sidecarItem) {
574
584
  s.currentUnit.id === unitId);
575
585
  const previousTier = s.currentUnitRouting?.tier;
576
586
  s.currentUnit = { type: unitType, id: unitId, startedAt: Date.now() };
587
+ const unitStartSeq = ic.nextSeq();
588
+ deps.emitJournalEvent({ ts: new Date().toISOString(), flowId: ic.flowId, seq: unitStartSeq, eventType: "unit-start", data: { unitType, unitId } });
577
589
  deps.captureAvailableSkills();
578
590
  deps.writeUnitRuntimeRecord(s.basePath, unitType, unitId, s.currentUnit.startedAt, {
579
591
  phase: "dispatched",
@@ -683,7 +695,12 @@ export async function runUnitPhase(ic, iterData, loopState, sidecarItem) {
683
695
  unitId,
684
696
  prefs,
685
697
  buildSnapshotOpts: () => deps.buildSnapshotOpts(unitType, unitId),
686
- buildRecoveryContext: () => ({}),
698
+ buildRecoveryContext: () => ({
699
+ basePath: s.basePath,
700
+ verbose: s.verbose,
701
+ currentUnitStartedAt: s.currentUnit?.startedAt ?? Date.now(),
702
+ unitRecoveryCount: s.unitRecoveryCount,
703
+ }),
687
704
  pauseAuto: deps.pauseAuto,
688
705
  });
689
706
  // Write preliminary lock (no session path yet — runUnit creates a new session).
@@ -759,8 +776,8 @@ export async function runUnitPhase(ic, iterData, loopState, sidecarItem) {
759
776
  if (s.currentUnitRouting) {
760
777
  deps.recordOutcome(unitType, s.currentUnitRouting.tier, true);
761
778
  }
762
- const isHookUnit = unitType.startsWith("hook/");
763
- const artifactVerified = isHookUnit ||
779
+ const skipArtifactVerification = unitType.startsWith("hook/") || unitType === "custom-step";
780
+ const artifactVerified = skipArtifactVerification ||
764
781
  deps.verifyExpectedArtifact(unitType, unitId, s.basePath);
765
782
  if (artifactVerified) {
766
783
  s.completedUnits.push({
@@ -783,6 +800,7 @@ export async function runUnitPhase(ic, iterData, loopState, sidecarItem) {
783
800
  s.unitDispatchCount.delete(`${unitType}/${unitId}`);
784
801
  s.unitRecoveryCount.delete(`${unitType}/${unitId}`);
785
802
  }
803
+ deps.emitJournalEvent({ ts: new Date().toISOString(), flowId: ic.flowId, seq: ic.nextSeq(), eventType: "unit-end", data: { unitType, unitId, status: unitResult.status, artifactVerified }, causedBy: { flowId: ic.flowId, seq: unitStartSeq } });
786
804
  return { action: "next", data: { unitStartedAt: s.currentUnit.startedAt } };
787
805
  }
788
806
  // ─── runFinalize ──────────────────────────────────────────────────────────────
@@ -27,6 +27,8 @@ export class AutoSession {
27
27
  paused = false;
28
28
  stepMode = false;
29
29
  verbose = false;
30
+ activeEngineId = null;
31
+ activeRunDir = null;
30
32
  cmdCtx = null;
31
33
  // ── Paths ────────────────────────────────────────────────────────────────
32
34
  basePath = "";
@@ -113,6 +115,8 @@ export class AutoSession {
113
115
  this.paused = false;
114
116
  this.stepMode = false;
115
117
  this.verbose = false;
118
+ this.activeEngineId = null;
119
+ this.activeRunDir = null;
116
120
  this.cmdCtx = null;
117
121
  // Paths
118
122
  this.basePath = "";
@@ -156,6 +160,8 @@ export class AutoSession {
156
160
  paused: this.paused,
157
161
  stepMode: this.stepMode,
158
162
  basePath: this.basePath,
163
+ activeEngineId: this.activeEngineId,
164
+ activeRunDir: this.activeRunDir,
159
165
  currentMilestoneId: this.currentMilestoneId,
160
166
  currentUnit: this.currentUnit,
161
167
  completedUnits: this.completedUnits.length,
@@ -18,6 +18,7 @@ import { GLYPH, INDENT } from "../shared/mod.js";
18
18
  import { computeProgressScore } from "./progress-score.js";
19
19
  import { getActiveWorktreeName } from "./worktree-command.js";
20
20
  import { loadEffectiveGSDPreferences, getGlobalGSDPreferencesPath } from "./preferences.js";
21
+ import { resolveServiceTierIcon, getEffectiveServiceTier } from "./service-tier.js";
21
22
  // ─── UAT Slice Extraction ─────────────────────────────────────────────────────
22
23
  /**
23
24
  * Extract the target slice ID from a run-uat unit ID (e.g. "M001/S01" → "S01").
@@ -45,6 +46,7 @@ export function unitVerb(unitType) {
45
46
  case "rewrite-docs": return "rewriting";
46
47
  case "reassess-roadmap": return "reassessing";
47
48
  case "run-uat": return "running UAT";
49
+ case "custom-step": return "executing workflow step";
48
50
  default: return unitType;
49
51
  }
50
52
  }
@@ -63,6 +65,7 @@ export function unitPhaseLabel(unitType) {
63
65
  case "rewrite-docs": return "REWRITE";
64
66
  case "reassess-roadmap": return "REASSESS";
65
67
  case "run-uat": return "UAT";
68
+ case "custom-step": return "WORKFLOW";
66
69
  default: return unitType.toUpperCase();
67
70
  }
68
71
  }
@@ -370,6 +373,8 @@ export function updateProgressWidget(ctx, unitType, unitId, state, accessors, ti
370
373
  }
371
374
  // Pre-fetch last commit for display
372
375
  refreshLastCommit(accessors.getBasePath());
376
+ // Cache the effective service tier at widget creation time (reads preferences)
377
+ const effectiveServiceTier = getEffectiveServiceTier();
373
378
  ctx.ui.setWidget("gsd-progress", (tui, theme) => {
374
379
  let pulseBright = true;
375
380
  let cachedLines;
@@ -471,9 +476,10 @@ export function updateProgressWidget(ctx, unitType, unitId, state, accessors, ti
471
476
  // Model display — shown in context section, not stats
472
477
  const modelId = cmdCtx?.model?.id ?? "";
473
478
  const modelProvider = cmdCtx?.model?.provider ?? "";
474
- const modelDisplay = modelProvider && modelId
479
+ const tierIcon = resolveServiceTierIcon(effectiveServiceTier, modelId);
480
+ const modelDisplay = (modelProvider && modelId
475
481
  ? `${modelProvider}/${modelId}`
476
- : modelId;
482
+ : modelId) + (tierIcon ? ` ${tierIcon}` : "");
477
483
  // ── Mode: off — return empty ──────────────────────────────────
478
484
  if (widgetMode === "off") {
479
485
  cachedLines = [];
@@ -24,7 +24,7 @@ function missingSliceStop(mid, phase) {
24
24
  // ─── Rewrite Circuit Breaker ──────────────────────────────────────────────
25
25
  const MAX_REWRITE_ATTEMPTS = 3;
26
26
  // ─── Rules ────────────────────────────────────────────────────────────────
27
- const DISPATCH_RULES = [
27
+ export const DISPATCH_RULES = [
28
28
  {
29
29
  name: "rewrite-docs (override gate)",
30
30
  match: async ({ mid, midTitle, state, basePath, session }) => {
@@ -480,22 +480,39 @@ const DISPATCH_RULES = [
480
480
  },
481
481
  },
482
482
  ];
483
+ import { getRegistry } from "./rule-registry.js";
483
484
  // ─── Resolver ─────────────────────────────────────────────────────────────
484
485
  /**
485
486
  * Evaluate dispatch rules in order. Returns the first matching action,
486
487
  * or a "stop" action if no rule matches (unhandled phase).
488
+ *
489
+ * Delegates to the RuleRegistry when initialized; falls back to inline
490
+ * loop over DISPATCH_RULES for backward compatibility (tests that import
491
+ * resolveDispatch directly without registry initialization).
487
492
  */
488
493
  export async function resolveDispatch(ctx) {
494
+ // Delegate to registry when available
495
+ try {
496
+ const registry = getRegistry();
497
+ return await registry.evaluateDispatch(ctx);
498
+ }
499
+ catch {
500
+ // Registry not initialized — fall back to inline loop
501
+ }
489
502
  for (const rule of DISPATCH_RULES) {
490
503
  const result = await rule.match(ctx);
491
- if (result)
504
+ if (result) {
505
+ if (result.action !== "skip")
506
+ result.matchedRule = rule.name;
492
507
  return result;
508
+ }
493
509
  }
494
510
  // No rule matched — unhandled phase
495
511
  return {
496
512
  action: "stop",
497
513
  reason: `Unhandled phase "${ctx.state.phase}" — run /gsd doctor to diagnose.`,
498
514
  level: "info",
515
+ matchedRule: "<no-match>",
499
516
  };
500
517
  }
501
518
  /** Exposed for testing — returns the rule names in evaluation order. */
@@ -31,6 +31,7 @@ import { existsSync, unlinkSync } from "node:fs";
31
31
  import { join } from "node:path";
32
32
  import { uncheckTaskInPlan } from "./undo.js";
33
33
  import { atomicWriteSync } from "./atomic-write.js";
34
+ import { _resetHasChangesCache } from "./native-git-bridge.js";
34
35
  /** Throttle STATE.md rebuilds — at most once per 30 seconds */
35
36
  const STATE_REBUILD_MIN_INTERVAL_MS = 30_000;
36
37
  /**
@@ -103,6 +104,12 @@ export async function postUnitPreVerification(pctx, opts) {
103
104
  }
104
105
  }
105
106
  }
107
+ // Invalidate the nativeHasChanges cache before auto-commit (#1853).
108
+ // The cache has a 10-second TTL and is keyed by basePath. A stale
109
+ // `false` result causes autoCommit to skip staging entirely, leaving
110
+ // code files only in the working tree where they are destroyed by
111
+ // `git worktree remove --force` during teardown.
112
+ _resetHasChangesCache();
106
113
  const commitMsg = autoCommitCurrentBranch(s.basePath, s.currentUnit.type, s.currentUnit.id, taskContext);
107
114
  if (commitMsg) {
108
115
  ctx.ui.notify(`Committed: ${commitMsg.split("\n")[0]}`, "info");
@@ -263,10 +263,15 @@ export function verifyExpectedArtifact(unitType, unitId, base) {
263
263
  // plan has no tasks, creating an infinite skip loop (#699).
264
264
  if (unitType === "plan-slice") {
265
265
  const planContent = readFileSync(absPath, "utf-8");
266
- if (!/^- \[[xX ]\] \*\*T\d+:/m.test(planContent))
266
+ // Accept checkbox-style (- [x] **T01: ...) or heading-style (### T01 -- / ### T01: / ### T01 —)
267
+ const hasCheckboxTask = /^- \[[xX ]\] \*\*T\d+:/m.test(planContent);
268
+ const hasHeadingTask = /^#{2,4}\s+T\d+\s*(?:--|—|:)/m.test(planContent);
269
+ if (!hasCheckboxTask && !hasHeadingTask)
267
270
  return false;
268
271
  }
269
- // execute-task must also have its checkbox marked [x] in the slice plan
272
+ // execute-task must also have its checkbox marked [x] in the slice plan.
273
+ // Heading-style plans (### T01 -- Title) have no checkbox — the task summary
274
+ // file existence (checked above via resolveExpectedArtifactPath) is sufficient.
270
275
  if (unitType === "execute-task") {
271
276
  const parts = unitId.split("/");
272
277
  const mid = parts[0];
@@ -277,8 +282,11 @@ export function verifyExpectedArtifact(unitType, unitId, base) {
277
282
  if (planAbs && existsSync(planAbs)) {
278
283
  const planContent = readFileSync(planAbs, "utf-8");
279
284
  const escapedTid = tid.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
280
- const re = new RegExp(`^- \\[[xX]\\] \\*\\*${escapedTid}:`, "m");
281
- if (!re.test(planContent))
285
+ const cbRe = new RegExp(`^- \\[[xX]\\] \\*\\*${escapedTid}:`, "m");
286
+ const hdRe = new RegExp(`^#{2,4}\\s+${escapedTid}\\s*(?:--|—|:)`, "m");
287
+ // Heading-style entries count as verified (no checkbox to toggle);
288
+ // checkbox-style entries require [x].
289
+ if (!cbRe.test(planContent) && !hdRe.test(planContent))
282
290
  return false;
283
291
  }
284
292
  }