@opengsd/gsd-pi 1.1.1-dev.a5a2de8 → 1.1.1-dev.b2556262

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 (325) hide show
  1. package/dist/headless-recover.js +56 -1
  2. package/dist/resources/.managed-resources-content-hash +1 -1
  3. package/dist/resources/extensions/browser-tools/engine/managed-gsd-browser.js +18 -2
  4. package/dist/resources/extensions/browser-tools/engine/selection.js +1 -1
  5. package/dist/resources/extensions/browser-tools/extension-manifest.json +1 -1
  6. package/dist/resources/extensions/browser-tools/index.js +68 -24
  7. package/dist/resources/extensions/browser-tools/state.js +12 -0
  8. package/dist/resources/extensions/browser-tools/tools/session.js +3 -2
  9. package/dist/resources/extensions/browser-tools/utils.js +3 -3
  10. package/dist/resources/extensions/browser-tools/web-app-detect.js +52 -0
  11. package/dist/resources/extensions/gsd/auto/loop.js +4 -2
  12. package/dist/resources/extensions/gsd/auto/phases.js +87 -12
  13. package/dist/resources/extensions/gsd/auto/session.js +22 -1
  14. package/dist/resources/extensions/gsd/auto/workflow-kernel.js +1 -0
  15. package/dist/resources/extensions/gsd/auto-dispatch.js +81 -13
  16. package/dist/resources/extensions/gsd/auto-model-selection.js +154 -9
  17. package/dist/resources/extensions/gsd/auto-post-unit.js +19 -2
  18. package/dist/resources/extensions/gsd/auto-prompts.js +26 -21
  19. package/dist/resources/extensions/gsd/auto-recovery.js +4 -2
  20. package/dist/resources/extensions/gsd/auto-runtime-state.js +3 -0
  21. package/dist/resources/extensions/gsd/auto-start.js +1 -1
  22. package/dist/resources/extensions/gsd/auto-timers.js +24 -10
  23. package/dist/resources/extensions/gsd/auto.js +40 -15
  24. package/dist/resources/extensions/gsd/bootstrap/db-tools.js +3 -3
  25. package/dist/resources/extensions/gsd/bootstrap/register-hooks.js +192 -77
  26. package/dist/resources/extensions/gsd/bootstrap/system-context.js +1 -1
  27. package/dist/resources/extensions/gsd/closeout-wizard.js +32 -9
  28. package/dist/resources/extensions/gsd/commands/handlers/auto.js +10 -0
  29. package/dist/resources/extensions/gsd/commands/handlers/ops.js +2 -9
  30. package/dist/resources/extensions/gsd/commands-maintenance.js +93 -15
  31. package/dist/resources/extensions/gsd/commands-mcp-status.js +1 -1
  32. package/dist/resources/extensions/gsd/commands-prefs-wizard.js +2 -2
  33. package/dist/resources/extensions/gsd/config-overlay.js +1 -0
  34. package/dist/resources/extensions/gsd/context-masker.js +129 -5
  35. package/dist/resources/extensions/gsd/db-writer.js +35 -0
  36. package/dist/resources/extensions/gsd/docs/preferences-reference.md +50 -1
  37. package/dist/resources/extensions/gsd/gsd-db.js +480 -172
  38. package/dist/resources/extensions/gsd/guided-flow.js +4 -1
  39. package/dist/resources/extensions/gsd/markdown-renderer.js +37 -53
  40. package/dist/resources/extensions/gsd/md-importer.js +38 -3
  41. package/dist/resources/extensions/gsd/migration-auto-check.js +126 -31
  42. package/dist/resources/extensions/gsd/parsers-legacy.js +23 -0
  43. package/dist/resources/extensions/gsd/planner-handoff.js +98 -0
  44. package/dist/resources/extensions/gsd/planning-path-scope.js +22 -4
  45. package/dist/resources/extensions/gsd/pre-execution-checks.js +10 -2
  46. package/dist/resources/extensions/gsd/preferences-models.js +111 -43
  47. package/dist/resources/extensions/gsd/preferences-types.js +13 -0
  48. package/dist/resources/extensions/gsd/preferences-validation.js +68 -3
  49. package/dist/resources/extensions/gsd/preferences.js +4 -1
  50. package/dist/resources/extensions/gsd/prompts/gate-evaluate.md +1 -1
  51. package/dist/resources/extensions/gsd/prompts/plan-milestone.md +1 -1
  52. package/dist/resources/extensions/gsd/prompts/plan-slice.md +1 -1
  53. package/dist/resources/extensions/gsd/prompts/refine-slice.md +1 -1
  54. package/dist/resources/extensions/gsd/prompts/run-uat.md +2 -2
  55. package/dist/resources/extensions/gsd/prompts/system.md +1 -1
  56. package/dist/resources/extensions/gsd/roadmap-slices.js +5 -1
  57. package/dist/resources/extensions/gsd/safety/content-validator.js +6 -4
  58. package/dist/resources/extensions/gsd/skill-manifest.js +12 -0
  59. package/dist/resources/extensions/gsd/source-observations.js +306 -0
  60. package/dist/resources/extensions/gsd/state-reconciliation/drift/completion.js +15 -8
  61. package/dist/resources/extensions/gsd/state-reconciliation/drift/stale-render.js +33 -5
  62. package/dist/resources/extensions/gsd/state-reconciliation/drift/stale-worker.js +34 -13
  63. package/dist/resources/extensions/gsd/state-reconciliation/index.js +39 -14
  64. package/dist/resources/extensions/gsd/state-reconciliation/spawn-gate.js +4 -4
  65. package/dist/resources/extensions/gsd/state.js +7 -3
  66. package/dist/resources/extensions/gsd/tool-contract.js +15 -1
  67. package/dist/resources/extensions/gsd/tool-presentation-plan.js +24 -2
  68. package/dist/resources/extensions/gsd/tools/complete-slice.js +28 -0
  69. package/dist/resources/extensions/gsd/tools/plan-slice.js +42 -11
  70. package/dist/resources/extensions/gsd/tools/plan-task.js +7 -1
  71. package/dist/resources/extensions/gsd/tools/workflow-tool-executors.js +62 -406
  72. package/dist/resources/extensions/gsd/uat-policy.js +130 -0
  73. package/dist/resources/extensions/gsd/uat-run.js +414 -0
  74. package/dist/resources/extensions/gsd/unit-context-manifest.js +3 -4
  75. package/dist/resources/extensions/gsd/unit-tool-contracts.js +38 -14
  76. package/dist/resources/extensions/gsd/verdict-parser.js +3 -8
  77. package/dist/resources/extensions/gsd/workflow-manifest.js +132 -5
  78. package/dist/resources/extensions/gsd/workflow-mcp.js +2 -3
  79. package/dist/resources/extensions/gsd/workflow-projections.js +8 -0
  80. package/dist/resources/extensions/gsd/worktree-manager.js +26 -0
  81. package/dist/resources/extensions/gsd/worktree-reentry.js +96 -0
  82. package/dist/resources/extensions/gsd/worktree-state-projection.js +18 -17
  83. package/dist/resources/extensions/shared/gsd-browser-cli.js +6 -0
  84. package/dist/resources/extensions/subagent/agents.js +1 -0
  85. package/dist/resources/extensions/subagent/index.js +27 -12
  86. package/dist/resources/extensions/subagent/launch.js +7 -2
  87. package/dist/web/standalone/.next/BUILD_ID +1 -1
  88. package/dist/web/standalone/.next/app-path-routes-manifest.json +6 -6
  89. package/dist/web/standalone/.next/build-manifest.json +2 -2
  90. package/dist/web/standalone/.next/prerender-manifest.json +3 -3
  91. package/dist/web/standalone/.next/server/app/_global-error.html +1 -1
  92. package/dist/web/standalone/.next/server/app/_global-error.rsc +1 -1
  93. package/dist/web/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  94. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
  95. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
  96. package/dist/web/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  97. package/dist/web/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  98. package/dist/web/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  99. package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
  100. package/dist/web/standalone/.next/server/app/_not-found.rsc +1 -1
  101. package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +1 -1
  102. package/dist/web/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  103. package/dist/web/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +1 -1
  104. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  105. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  106. package/dist/web/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
  107. package/dist/web/standalone/.next/server/app/index.html +1 -1
  108. package/dist/web/standalone/.next/server/app/index.rsc +1 -1
  109. package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +1 -1
  110. package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +1 -1
  111. package/dist/web/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
  112. package/dist/web/standalone/.next/server/app/index.segments/_index.segment.rsc +1 -1
  113. package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
  114. package/dist/web/standalone/.next/server/app-paths-manifest.json +6 -6
  115. package/dist/web/standalone/.next/server/chunks/8357.js +1 -1
  116. package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
  117. package/dist/web/standalone/.next/server/pages/404.html +1 -1
  118. package/dist/web/standalone/.next/server/pages/500.html +1 -1
  119. package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
  120. package/dist/web/standalone/node_modules/@gsd/native/dist/native.js +22 -0
  121. package/dist/web/standalone/node_modules/node-pty/build/Makefile +1 -1
  122. package/package.json +4 -4
  123. package/packages/cloud-mcp-gateway/package.json +2 -2
  124. package/packages/contracts/package.json +1 -1
  125. package/packages/daemon/package.json +4 -4
  126. package/packages/gsd-agent-core/package.json +5 -5
  127. package/packages/gsd-agent-modes/dist/modes/interactive/components/assistant-message.d.ts.map +1 -1
  128. package/packages/gsd-agent-modes/dist/modes/interactive/components/assistant-message.js +21 -23
  129. package/packages/gsd-agent-modes/dist/modes/interactive/components/assistant-message.js.map +1 -1
  130. package/packages/gsd-agent-modes/dist/modes/interactive/components/tool-execution.d.ts +3 -0
  131. package/packages/gsd-agent-modes/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
  132. package/packages/gsd-agent-modes/dist/modes/interactive/components/tool-execution.js +25 -0
  133. package/packages/gsd-agent-modes/dist/modes/interactive/components/tool-execution.js.map +1 -1
  134. package/packages/gsd-agent-modes/dist/modes/interactive/components/transcript-design.d.ts +1 -0
  135. package/packages/gsd-agent-modes/dist/modes/interactive/components/transcript-design.d.ts.map +1 -1
  136. package/packages/gsd-agent-modes/dist/modes/interactive/components/transcript-design.js +66 -12
  137. package/packages/gsd-agent-modes/dist/modes/interactive/components/transcript-design.js.map +1 -1
  138. package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-controller.d.ts.map +1 -1
  139. package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-controller.js +18 -11
  140. package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-controller.js.map +1 -1
  141. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-chat-render.d.ts.map +1 -1
  142. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-chat-render.js +16 -0
  143. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-chat-render.js.map +1 -1
  144. package/packages/gsd-agent-modes/package.json +7 -7
  145. package/packages/mcp-server/dist/workflow-tools.js +1 -1
  146. package/packages/mcp-server/dist/workflow-tools.js.map +1 -1
  147. package/packages/mcp-server/package.json +3 -3
  148. package/packages/native/dist/native.js +22 -0
  149. package/packages/native/package.json +1 -1
  150. package/packages/pi-agent-core/package.json +1 -1
  151. package/packages/pi-ai/dist/image-models.generated.d.ts +30 -0
  152. package/packages/pi-ai/dist/image-models.generated.d.ts.map +1 -1
  153. package/packages/pi-ai/dist/image-models.generated.js +30 -0
  154. package/packages/pi-ai/dist/image-models.generated.js.map +1 -1
  155. package/packages/pi-ai/dist/models.generated.d.ts +174 -29
  156. package/packages/pi-ai/dist/models.generated.d.ts.map +1 -1
  157. package/packages/pi-ai/dist/models.generated.js +178 -54
  158. package/packages/pi-ai/dist/models.generated.js.map +1 -1
  159. package/packages/pi-ai/dist/providers/transform-messages.d.ts.map +1 -1
  160. package/packages/pi-ai/dist/providers/transform-messages.js +8 -1
  161. package/packages/pi-ai/dist/providers/transform-messages.js.map +1 -1
  162. package/packages/pi-ai/package.json +1 -1
  163. package/packages/pi-coding-agent/dist/core/settings-manager.js +1 -1
  164. package/packages/pi-coding-agent/dist/core/settings-manager.js.map +1 -1
  165. package/packages/pi-coding-agent/dist/theme/themes.js +1 -1
  166. package/packages/pi-coding-agent/dist/theme/themes.js.map +1 -1
  167. package/packages/pi-coding-agent/package.json +7 -7
  168. package/packages/pi-tui/dist/utils.d.ts +11 -0
  169. package/packages/pi-tui/dist/utils.d.ts.map +1 -1
  170. package/packages/pi-tui/dist/utils.js +119 -6
  171. package/packages/pi-tui/dist/utils.js.map +1 -1
  172. package/packages/pi-tui/package.json +2 -1
  173. package/packages/rpc-client/package.json +2 -2
  174. package/pkg/dist/theme/themes.js +1 -1
  175. package/pkg/dist/theme/themes.js.map +1 -1
  176. package/pkg/package.json +1 -1
  177. package/scripts/install/handoff.js +16 -3
  178. package/src/resources/extensions/browser-tools/engine/managed-gsd-browser.ts +21 -2
  179. package/src/resources/extensions/browser-tools/engine/selection.ts +1 -1
  180. package/src/resources/extensions/browser-tools/extension-manifest.json +1 -1
  181. package/src/resources/extensions/browser-tools/index.ts +75 -27
  182. package/src/resources/extensions/browser-tools/state.ts +13 -0
  183. package/src/resources/extensions/browser-tools/tests/browser-engine-selection.test.mjs +2 -2
  184. package/src/resources/extensions/browser-tools/tests/browser-tools-unit.test.cjs +57 -0
  185. package/src/resources/extensions/browser-tools/tests/gsd-browser-launch-config.test.mjs +37 -0
  186. package/src/resources/extensions/browser-tools/tests/web-app-detect.test.mjs +68 -0
  187. package/src/resources/extensions/browser-tools/tools/session.ts +4 -2
  188. package/src/resources/extensions/browser-tools/utils.ts +3 -3
  189. package/src/resources/extensions/browser-tools/web-app-detect.ts +63 -0
  190. package/src/resources/extensions/gsd/auto/loop-deps.ts +1 -0
  191. package/src/resources/extensions/gsd/auto/loop.ts +4 -2
  192. package/src/resources/extensions/gsd/auto/phases.ts +89 -15
  193. package/src/resources/extensions/gsd/auto/session.ts +24 -1
  194. package/src/resources/extensions/gsd/auto/workflow-kernel.ts +1 -0
  195. package/src/resources/extensions/gsd/auto-dispatch.ts +117 -12
  196. package/src/resources/extensions/gsd/auto-model-selection.ts +190 -12
  197. package/src/resources/extensions/gsd/auto-post-unit.ts +20 -2
  198. package/src/resources/extensions/gsd/auto-prompts.ts +25 -22
  199. package/src/resources/extensions/gsd/auto-recovery.ts +22 -3
  200. package/src/resources/extensions/gsd/auto-runtime-state.ts +5 -0
  201. package/src/resources/extensions/gsd/auto-start.ts +1 -1
  202. package/src/resources/extensions/gsd/auto-timers.ts +25 -9
  203. package/src/resources/extensions/gsd/auto.ts +41 -14
  204. package/src/resources/extensions/gsd/bootstrap/db-tools.ts +3 -3
  205. package/src/resources/extensions/gsd/bootstrap/register-hooks.ts +250 -78
  206. package/src/resources/extensions/gsd/bootstrap/system-context.ts +1 -1
  207. package/src/resources/extensions/gsd/closeout-wizard.ts +47 -13
  208. package/src/resources/extensions/gsd/commands/handlers/auto.ts +9 -0
  209. package/src/resources/extensions/gsd/commands/handlers/ops.ts +2 -17
  210. package/src/resources/extensions/gsd/commands-maintenance.ts +124 -13
  211. package/src/resources/extensions/gsd/commands-mcp-status.ts +1 -1
  212. package/src/resources/extensions/gsd/commands-prefs-wizard.ts +2 -2
  213. package/src/resources/extensions/gsd/config-overlay.ts +1 -0
  214. package/src/resources/extensions/gsd/context-masker.ts +152 -5
  215. package/src/resources/extensions/gsd/db-writer.ts +38 -0
  216. package/src/resources/extensions/gsd/docs/preferences-reference.md +50 -1
  217. package/src/resources/extensions/gsd/gsd-db.ts +564 -186
  218. package/src/resources/extensions/gsd/guided-flow.ts +4 -1
  219. package/src/resources/extensions/gsd/markdown-renderer.ts +44 -66
  220. package/src/resources/extensions/gsd/md-importer.ts +49 -2
  221. package/src/resources/extensions/gsd/migration-auto-check.ts +154 -34
  222. package/src/resources/extensions/gsd/parsers-legacy.ts +20 -0
  223. package/src/resources/extensions/gsd/planner-handoff.ts +149 -0
  224. package/src/resources/extensions/gsd/planning-path-scope.ts +22 -4
  225. package/src/resources/extensions/gsd/pre-execution-checks.ts +9 -2
  226. package/src/resources/extensions/gsd/preferences-models.ts +113 -43
  227. package/src/resources/extensions/gsd/preferences-types.ts +47 -0
  228. package/src/resources/extensions/gsd/preferences-validation.ts +76 -2
  229. package/src/resources/extensions/gsd/preferences.ts +5 -0
  230. package/src/resources/extensions/gsd/prompts/gate-evaluate.md +1 -1
  231. package/src/resources/extensions/gsd/prompts/plan-milestone.md +1 -1
  232. package/src/resources/extensions/gsd/prompts/plan-slice.md +1 -1
  233. package/src/resources/extensions/gsd/prompts/refine-slice.md +1 -1
  234. package/src/resources/extensions/gsd/prompts/run-uat.md +2 -2
  235. package/src/resources/extensions/gsd/prompts/system.md +1 -1
  236. package/src/resources/extensions/gsd/roadmap-slices.ts +6 -1
  237. package/src/resources/extensions/gsd/safety/content-validator.ts +8 -5
  238. package/src/resources/extensions/gsd/skill-manifest.ts +12 -0
  239. package/src/resources/extensions/gsd/source-observations.ts +402 -0
  240. package/src/resources/extensions/gsd/state-reconciliation/drift/completion.ts +20 -8
  241. package/src/resources/extensions/gsd/state-reconciliation/drift/stale-render.ts +44 -5
  242. package/src/resources/extensions/gsd/state-reconciliation/drift/stale-worker.ts +39 -11
  243. package/src/resources/extensions/gsd/state-reconciliation/index.ts +45 -15
  244. package/src/resources/extensions/gsd/state-reconciliation/spawn-gate.ts +4 -4
  245. package/src/resources/extensions/gsd/state.ts +7 -4
  246. package/src/resources/extensions/gsd/tests/auto-loop.test.ts +114 -0
  247. package/src/resources/extensions/gsd/tests/auto-model-selection-tool-poisoning.test.ts +66 -4
  248. package/src/resources/extensions/gsd/tests/auto-model-selection.test.ts +299 -1
  249. package/src/resources/extensions/gsd/tests/auto-paused-ui-cleanup.test.ts +32 -0
  250. package/src/resources/extensions/gsd/tests/auto-phases-lifecycle.test.ts +75 -3
  251. package/src/resources/extensions/gsd/tests/auto-recovery.test.ts +22 -1
  252. package/src/resources/extensions/gsd/tests/auto-supervisor.test.mjs +4 -0
  253. package/src/resources/extensions/gsd/tests/before-provider-context-management.test.ts +145 -0
  254. package/src/resources/extensions/gsd/tests/bundled-skill-triggers.test.ts +9 -0
  255. package/src/resources/extensions/gsd/tests/closeout-wizard.test.ts +44 -0
  256. package/src/resources/extensions/gsd/tests/commands-dispatcher-unmerged-milestone.test.ts +26 -1
  257. package/src/resources/extensions/gsd/tests/complete-slice-verification-gate.test.ts +118 -0
  258. package/src/resources/extensions/gsd/tests/content-validator.test.ts +74 -0
  259. package/src/resources/extensions/gsd/tests/context-masker.test.ts +56 -1
  260. package/src/resources/extensions/gsd/tests/custom-engine-loop-integration.test.ts +17 -2
  261. package/src/resources/extensions/gsd/tests/dispatch-rule-coverage.test.ts +24 -0
  262. package/src/resources/extensions/gsd/tests/doctor-scope-db-unavailable.test.ts +1 -11
  263. package/src/resources/extensions/gsd/tests/gate-dispatch.test.ts +64 -0
  264. package/src/resources/extensions/gsd/tests/gate-storage.test.ts +15 -0
  265. package/src/resources/extensions/gsd/tests/gsd-recover.test.ts +62 -1
  266. package/src/resources/extensions/gsd/tests/integration/run-uat.test.ts +4 -1
  267. package/src/resources/extensions/gsd/tests/interrupted-session-auto.test.ts +27 -0
  268. package/src/resources/extensions/gsd/tests/journal-integration.test.ts +16 -0
  269. package/src/resources/extensions/gsd/tests/markdown-renderer.test.ts +42 -0
  270. package/src/resources/extensions/gsd/tests/mcp-project-config.test.ts +7 -1
  271. package/src/resources/extensions/gsd/tests/mcp-status.test.ts +1 -1
  272. package/src/resources/extensions/gsd/tests/migration-auto-check.test.ts +99 -0
  273. package/src/resources/extensions/gsd/tests/plan-slice.test.ts +99 -2
  274. package/src/resources/extensions/gsd/tests/plan-task.test.ts +19 -0
  275. package/src/resources/extensions/gsd/tests/planner-handoff.test.ts +100 -0
  276. package/src/resources/extensions/gsd/tests/preferences.test.ts +14 -0
  277. package/src/resources/extensions/gsd/tests/prefs-wizard-coverage.test.ts +1 -0
  278. package/src/resources/extensions/gsd/tests/prompt-contracts.test.ts +133 -0
  279. package/src/resources/extensions/gsd/tests/provider-switch-observer.test.ts +55 -0
  280. package/src/resources/extensions/gsd/tests/register-hooks-depth-verification.test.ts +101 -1
  281. package/src/resources/extensions/gsd/tests/repository-registry.test.ts +2 -2
  282. package/src/resources/extensions/gsd/tests/runtime-invariant-modules.test.ts +28 -0
  283. package/src/resources/extensions/gsd/tests/schema-v21-sequence.test.ts +5 -3
  284. package/src/resources/extensions/gsd/tests/schema-v27-v28-sequence.test.ts +162 -18
  285. package/src/resources/extensions/gsd/tests/skill-manifest.test.ts +4 -3
  286. package/src/resources/extensions/gsd/tests/skipped-validation-db-atomicity.test.ts +8 -0
  287. package/src/resources/extensions/gsd/tests/source-observations.test.ts +275 -0
  288. package/src/resources/extensions/gsd/tests/stale-queued-milestone.test.ts +43 -0
  289. package/src/resources/extensions/gsd/tests/state-reconciliation-drift.test.ts +76 -21
  290. package/src/resources/extensions/gsd/tests/thinking-level-resolution.test.ts +203 -0
  291. package/src/resources/extensions/gsd/tests/uat-policy.test.ts +170 -0
  292. package/src/resources/extensions/gsd/tests/unit-context-manifest.test.ts +7 -1
  293. package/src/resources/extensions/gsd/tests/workflow-kernel.test.ts +7 -0
  294. package/src/resources/extensions/gsd/tests/workflow-manifest.test.ts +306 -1
  295. package/src/resources/extensions/gsd/tests/workflow-mcp.test.ts +77 -10
  296. package/src/resources/extensions/gsd/tests/workflow-tool-executors.test.ts +260 -5
  297. package/src/resources/extensions/gsd/tests/worktree-db.test.ts +511 -1
  298. package/src/resources/extensions/gsd/tests/worktree-reentry.test.ts +102 -0
  299. package/src/resources/extensions/gsd/tests/worktree-state-projection.test.ts +44 -0
  300. package/src/resources/extensions/gsd/tool-contract.ts +29 -1
  301. package/src/resources/extensions/gsd/tool-presentation-plan.ts +41 -6
  302. package/src/resources/extensions/gsd/tools/complete-slice.ts +29 -0
  303. package/src/resources/extensions/gsd/tools/plan-slice.ts +54 -12
  304. package/src/resources/extensions/gsd/tools/plan-task.ts +8 -1
  305. package/src/resources/extensions/gsd/tools/workflow-tool-executors.ts +71 -489
  306. package/src/resources/extensions/gsd/types.ts +1 -0
  307. package/src/resources/extensions/gsd/uat-policy.ts +191 -0
  308. package/src/resources/extensions/gsd/uat-run.ts +550 -0
  309. package/src/resources/extensions/gsd/unit-context-manifest.ts +3 -4
  310. package/src/resources/extensions/gsd/unit-tool-contracts.ts +38 -14
  311. package/src/resources/extensions/gsd/verdict-parser.ts +3 -10
  312. package/src/resources/extensions/gsd/workflow-manifest.ts +193 -7
  313. package/src/resources/extensions/gsd/workflow-mcp.ts +2 -3
  314. package/src/resources/extensions/gsd/workflow-projections.ts +9 -0
  315. package/src/resources/extensions/gsd/worktree-manager.ts +32 -0
  316. package/src/resources/extensions/gsd/worktree-reentry.ts +103 -0
  317. package/src/resources/extensions/gsd/worktree-state-projection.ts +22 -22
  318. package/src/resources/extensions/shared/gsd-browser-cli.ts +6 -0
  319. package/src/resources/extensions/shared/tests/format-utils.test.ts +8 -3
  320. package/src/resources/extensions/subagent/agents.ts +4 -0
  321. package/src/resources/extensions/subagent/index.ts +28 -3
  322. package/src/resources/extensions/subagent/launch.ts +8 -0
  323. package/src/resources/extensions/subagent/tests/model-override.test.ts +31 -0
  324. /package/dist/web/standalone/.next/static/{9y3LeeR2uGr2yRj9RjY3D → tJOKQbQRO-9MiFDO8DIDS}/_buildManifest.js +0 -0
  325. /package/dist/web/standalone/.next/static/{9y3LeeR2uGr2yRj9RjY3D → tJOKQbQRO-9MiFDO8DIDS}/_ssgManifest.js +0 -0
@@ -5,10 +5,9 @@
5
5
  * (e.g. `passed` → `pass`) are applied consistently across the codebase.
6
6
  */
7
7
 
8
- import { extractUatType } from "./files.js";
9
- import type { UatType } from "./files.js";
10
8
  import { splitFrontmatter, parseFrontmatterMap } from "../shared/frontmatter.js";
11
9
  import { parse as parseYaml } from "yaml";
10
+ import { getDeclaredUatType, isPartialEligibleUatType, type UatType } from "./uat-policy.js";
12
11
 
13
12
  function normalizeVerdict(value: unknown): string | undefined {
14
13
  if (typeof value !== "string") return undefined;
@@ -103,12 +102,6 @@ export const UAT_ACCEPTABLE_VERDICTS: readonly string[] = ["pass", "passed"];
103
102
  * UAT types whose results may legitimately produce a `partial` verdict
104
103
  * when all automatable checks pass but human-only checks remain.
105
104
  */
106
- const PARTIAL_ELIGIBLE_UAT_TYPES: readonly UatType[] = [
107
- "mixed",
108
- "human-experience",
109
- "live-runtime",
110
- ];
111
-
112
105
  /**
113
106
  * Check whether a verdict is acceptable for a given UAT type.
114
107
  *
@@ -117,7 +110,7 @@ const PARTIAL_ELIGIBLE_UAT_TYPES: readonly UatType[] = [
117
110
  */
118
111
  export function isAcceptableUatVerdict(verdict: string, uatType: UatType | undefined): boolean {
119
112
  if (UAT_ACCEPTABLE_VERDICTS.includes(verdict)) return true;
120
- if (verdict === "partial" && uatType && (PARTIAL_ELIGIBLE_UAT_TYPES as readonly string[]).includes(uatType)) {
113
+ if (verdict === "partial" && isPartialEligibleUatType(uatType)) {
121
114
  return true;
122
115
  }
123
116
  return false;
@@ -147,5 +140,5 @@ export function isValidMilestoneVerdict(verdict: string): verdict is ValidationV
147
140
  * the codebase when a UAT file lacks an explicit `## UAT Type` section.
148
141
  */
149
142
  export function getUatType(content: string): UatType {
150
- return extractUatType(content) ?? "artifact-driven";
143
+ return getDeclaredUatType(content);
151
144
  }
@@ -6,24 +6,70 @@ import {
6
6
  readTransaction,
7
7
  restoreManifest,
8
8
  } from "./gsd-db.js";
9
- import type { MilestoneRow } from "./db-milestone-artifact-rows.js";
9
+ import type { ArtifactRow, MilestoneRow } from "./db-milestone-artifact-rows.js";
10
10
  import type { SliceRow, TaskRow } from "./db-task-slice-rows.js";
11
11
  import type { VerificationEvidenceRow } from "./db-verification-evidence-rows.js";
12
- import type { Decision } from "./types.js";
12
+ import type { Decision, GateRow, Requirement } from "./types.js";
13
13
  import { atomicWriteSync } from "./atomic-write.js";
14
+ import { getAllDecisionsFromMemories } from "./context-store.js";
15
+ import { backfillDecisionsToMemories } from "./memory-backfill.js";
16
+ import { invalidateAllCaches } from "./cache.js";
14
17
  import { readFileSync, existsSync, mkdirSync } from "node:fs";
15
- import { join } from "node:path";
18
+ import { isAbsolute, join, relative, resolve } from "node:path";
16
19
 
17
20
  // ─── Manifest Types ──────────────────────────────────────────────────────
18
21
 
22
+ export interface ManifestArtifactRow extends ArtifactRow {
23
+ content_hash: string | null;
24
+ }
25
+
26
+ export interface ReplanHistoryManifestRow {
27
+ id: number;
28
+ milestone_id: string;
29
+ slice_id: string | null;
30
+ task_id: string | null;
31
+ summary: string;
32
+ previous_artifact_path: string | null;
33
+ replacement_artifact_path: string | null;
34
+ created_at: string;
35
+ }
36
+
37
+ export interface AssessmentManifestRow {
38
+ path: string;
39
+ milestone_id: string;
40
+ slice_id: string | null;
41
+ task_id: string | null;
42
+ status: string;
43
+ scope: string;
44
+ full_content: string;
45
+ created_at: string;
46
+ }
47
+
48
+ export interface MilestoneCommitAttributionManifestRow {
49
+ commit_sha: string;
50
+ milestone_id: string;
51
+ slice_id: string | null;
52
+ task_id: string | null;
53
+ source: string;
54
+ confidence: number;
55
+ files_json: string;
56
+ created_at: string;
57
+ }
58
+
19
59
  export interface StateManifest {
20
60
  version: 1;
21
61
  exported_at: string; // ISO 8601
62
+ requirements?: Requirement[];
63
+ artifacts?: ManifestArtifactRow[];
22
64
  milestones: MilestoneRow[];
23
65
  slices: SliceRow[];
24
66
  tasks: TaskRow[];
25
67
  decisions: Decision[];
68
+ replan_history?: ReplanHistoryManifestRow[];
69
+ assessments?: AssessmentManifestRow[];
70
+ quality_gates?: GateRow[];
26
71
  verification_evidence: VerificationEvidenceRow[];
72
+ milestone_commit_attributions?: MilestoneCommitAttributionManifestRow[];
27
73
  }
28
74
 
29
75
  // ─── helpers ─────────────────────────────────────────────────────────────
@@ -51,11 +97,26 @@ export function toNumeric(value: unknown, fallback: number | null = null): numbe
51
97
  return fallback;
52
98
  }
53
99
 
100
+ function mergeDecisionSurfaces(legacyDecisions: Decision[], memoryDecisions: Decision[]): Decision[] {
101
+ const byId = new Map<string, Decision>();
102
+ for (const decision of legacyDecisions) {
103
+ byId.set(decision.id, decision);
104
+ }
105
+ for (const decision of memoryDecisions) {
106
+ byId.set(decision.id, decision);
107
+ }
108
+ return Array.from(byId.values()).sort((a, b) => {
109
+ const seqDelta = (a.seq ?? 0) - (b.seq ?? 0);
110
+ return seqDelta === 0 ? a.id.localeCompare(b.id) : seqDelta;
111
+ });
112
+ }
113
+
54
114
  // ─── snapshotState ───────────────────────────────────────────────────────
55
115
 
56
116
  /**
57
- * Capture complete DB state as a StateManifest.
58
- * Reads all rows from milestones, slices, tasks, decisions, verification_evidence.
117
+ * Capture DB-backed workflow state as a StateManifest.
118
+ * Runtime soft state and append-only audit streams stay outside this recovery
119
+ * substrate; correctness records and persisted evidence are included.
59
120
  *
60
121
  * Note: rows returned from raw queries are plain objects with TEXT columns for
61
122
  * JSON arrays. We parse them into typed Row objects using the same logic as
@@ -67,6 +128,34 @@ export function snapshotState(): StateManifest {
67
128
  // Wrap all reads in a deferred transaction so the snapshot is consistent
68
129
  // (all SELECTs see the same DB state even if a concurrent write lands between them).
69
130
  return readTransaction(() => {
131
+ const rawRequirements = db.prepare("SELECT * FROM requirements ORDER BY id").all() as Record<string, unknown>[];
132
+ const requirements: Requirement[] = rawRequirements.map((r) => ({
133
+ id: r["id"] as string,
134
+ class: (r["class"] as string) ?? "",
135
+ status: (r["status"] as string) ?? "",
136
+ description: (r["description"] as string) ?? "",
137
+ why: (r["why"] as string) ?? "",
138
+ source: (r["source"] as string) ?? "",
139
+ primary_owner: (r["primary_owner"] as string) ?? "",
140
+ supporting_slices: (r["supporting_slices"] as string) ?? "",
141
+ validation: (r["validation"] as string) ?? "",
142
+ notes: (r["notes"] as string) ?? "",
143
+ full_content: (r["full_content"] as string) ?? "",
144
+ superseded_by: (r["superseded_by"] as string) ?? null,
145
+ }));
146
+
147
+ const rawArtifacts = db.prepare("SELECT * FROM artifacts ORDER BY path").all() as Record<string, unknown>[];
148
+ const artifacts: ManifestArtifactRow[] = rawArtifacts.map((r) => ({
149
+ path: r["path"] as string,
150
+ artifact_type: (r["artifact_type"] as string) ?? "",
151
+ milestone_id: (r["milestone_id"] as string) ?? null,
152
+ slice_id: (r["slice_id"] as string) ?? null,
153
+ task_id: (r["task_id"] as string) ?? null,
154
+ full_content: (r["full_content"] as string) ?? "",
155
+ imported_at: (r["imported_at"] as string) ?? "",
156
+ content_hash: (r["content_hash"] as string) ?? null,
157
+ }));
158
+
70
159
  const rawMilestones = db.prepare(
71
160
  "SELECT * FROM milestones ORDER BY CASE WHEN sequence > 0 THEN 0 ELSE 1 END, sequence, id",
72
161
  ).all() as Record<string, unknown>[];
@@ -152,7 +241,7 @@ export function snapshotState(): StateManifest {
152
241
  }));
153
242
 
154
243
  const rawDecisions = db.prepare("SELECT * FROM decisions ORDER BY seq").all() as Record<string, unknown>[];
155
- const decisions: Decision[] = rawDecisions.map((r) => ({
244
+ const legacyDecisions: Decision[] = rawDecisions.map((r) => ({
156
245
  seq: toNumeric(r["seq"], 0) as number,
157
246
  id: r["id"] as string,
158
247
  when_context: (r["when_context"] as string) ?? "",
@@ -165,6 +254,45 @@ export function snapshotState(): StateManifest {
165
254
  source: (r["source"] as string) ?? "discussion",
166
255
  superseded_by: (r["superseded_by"] as string) ?? null,
167
256
  }));
257
+ const decisions = mergeDecisionSurfaces(legacyDecisions, getAllDecisionsFromMemories());
258
+
259
+ const rawReplanHistory = db.prepare("SELECT * FROM replan_history ORDER BY id").all() as Record<string, unknown>[];
260
+ const replan_history: ReplanHistoryManifestRow[] = rawReplanHistory.map((r) => ({
261
+ id: toNumeric(r["id"], 0) as number,
262
+ milestone_id: (r["milestone_id"] as string) ?? "",
263
+ slice_id: (r["slice_id"] as string) ?? null,
264
+ task_id: (r["task_id"] as string) ?? null,
265
+ summary: (r["summary"] as string) ?? "",
266
+ previous_artifact_path: (r["previous_artifact_path"] as string) ?? null,
267
+ replacement_artifact_path: (r["replacement_artifact_path"] as string) ?? null,
268
+ created_at: (r["created_at"] as string) ?? "",
269
+ }));
270
+
271
+ const rawAssessments = db.prepare("SELECT * FROM assessments ORDER BY path").all() as Record<string, unknown>[];
272
+ const assessments: AssessmentManifestRow[] = rawAssessments.map((r) => ({
273
+ path: r["path"] as string,
274
+ milestone_id: (r["milestone_id"] as string) ?? "",
275
+ slice_id: (r["slice_id"] as string) ?? null,
276
+ task_id: (r["task_id"] as string) ?? null,
277
+ status: (r["status"] as string) ?? "",
278
+ scope: (r["scope"] as string) ?? "",
279
+ full_content: (r["full_content"] as string) ?? "",
280
+ created_at: (r["created_at"] as string) ?? "",
281
+ }));
282
+
283
+ const rawQualityGates = db.prepare("SELECT * FROM quality_gates ORDER BY milestone_id, slice_id, gate_id, task_id").all() as Record<string, unknown>[];
284
+ const quality_gates: GateRow[] = rawQualityGates.map((r) => ({
285
+ milestone_id: r["milestone_id"] as string,
286
+ slice_id: r["slice_id"] as string,
287
+ gate_id: r["gate_id"] as GateRow["gate_id"],
288
+ scope: r["scope"] as GateRow["scope"],
289
+ task_id: (r["task_id"] as string) ?? "",
290
+ status: r["status"] as GateRow["status"],
291
+ verdict: r["status"] === "pending" ? null : (r["verdict"] as GateRow["verdict"]),
292
+ rationale: (r["rationale"] as string) ?? "",
293
+ findings: (r["findings"] as string) ?? "",
294
+ evaluated_at: (r["evaluated_at"] as string) ?? null,
295
+ }));
168
296
 
169
297
  const rawEvidence = db.prepare("SELECT * FROM verification_evidence ORDER BY id").all() as Record<string, unknown>[];
170
298
  const verification_evidence: VerificationEvidenceRow[] = rawEvidence.map((r) => ({
@@ -179,14 +307,34 @@ export function snapshotState(): StateManifest {
179
307
  created_at: r["created_at"] as string,
180
308
  }));
181
309
 
310
+ const rawCommitAttributions = db.prepare(
311
+ "SELECT * FROM milestone_commit_attributions ORDER BY milestone_id, commit_sha",
312
+ ).all() as Record<string, unknown>[];
313
+ const milestone_commit_attributions: MilestoneCommitAttributionManifestRow[] = rawCommitAttributions.map((r) => ({
314
+ commit_sha: r["commit_sha"] as string,
315
+ milestone_id: r["milestone_id"] as string,
316
+ slice_id: (r["slice_id"] as string) ?? null,
317
+ task_id: (r["task_id"] as string) ?? null,
318
+ source: (r["source"] as string) ?? "recorded",
319
+ confidence: toNumeric(r["confidence"], 1) as number,
320
+ files_json: (r["files_json"] as string) ?? "[]",
321
+ created_at: (r["created_at"] as string) ?? "",
322
+ }));
323
+
182
324
  const result: StateManifest = {
183
325
  version: 1,
184
326
  exported_at: new Date().toISOString(),
327
+ requirements,
328
+ artifacts,
185
329
  milestones,
186
330
  slices,
187
331
  tasks,
188
332
  decisions,
333
+ replan_history,
334
+ assessments,
335
+ quality_gates,
189
336
  verification_evidence,
337
+ milestone_commit_attributions,
190
338
  };
191
339
 
192
340
  return result;
@@ -232,20 +380,55 @@ export function readManifest(basePath: string): StateManifest | null {
232
380
  throw new Error(`Unsupported manifest version: ${parsed.version}`);
233
381
  }
234
382
 
235
- // Validate required fields to avoid cryptic errors during restore
383
+ // Validate required fields to avoid cryptic errors during restore.
236
384
  if (!Array.isArray(parsed.milestones) || !Array.isArray(parsed.slices) ||
237
385
  !Array.isArray(parsed.tasks) || !Array.isArray(parsed.decisions) ||
238
386
  !Array.isArray(parsed.verification_evidence)) {
239
387
  throw new Error("Malformed manifest: missing or invalid required arrays");
240
388
  }
241
389
 
390
+ for (const key of ["requirements", "artifacts", "replan_history", "assessments", "quality_gates", "milestone_commit_attributions"] as const) {
391
+ if (parsed[key] !== undefined && !Array.isArray(parsed[key])) {
392
+ throw new Error(`Malformed manifest: ${key} must be an array when present`);
393
+ }
394
+ }
395
+
242
396
  return parsed;
243
397
  }
244
398
 
399
+ function stripGsdPrefix(path: string): string {
400
+ return path.startsWith(".gsd/") ? path.slice(".gsd/".length) : path;
401
+ }
402
+
403
+ function artifactProjectionPath(basePath: string, artifactPath: string): string {
404
+ const gsdDir = resolve(basePath, ".gsd");
405
+ const fullPath = resolve(gsdDir, stripGsdPrefix(artifactPath));
406
+ const rel = relative(gsdDir, fullPath);
407
+ if (rel.startsWith("..") || isAbsolute(rel)) {
408
+ throw new Error(`Malformed manifest: artifact path escapes .gsd: ${artifactPath}`);
409
+ }
410
+ return fullPath;
411
+ }
412
+
413
+ function restoreArtifactProjections(basePath: string, manifest: StateManifest): void {
414
+ if (manifest.artifacts === undefined) return;
415
+
416
+ for (const artifact of manifest.artifacts) {
417
+ atomicWriteSync(
418
+ artifactProjectionPath(basePath, artifact.path),
419
+ artifact.full_content ?? "",
420
+ );
421
+ }
422
+ }
423
+
245
424
  // ─── bootstrapFromManifest ──────────────────────────────────────────────
246
425
 
247
426
  /**
248
427
  * Read state-manifest.json and restore DB state from it.
428
+ * Rehydrates artifact projection files for restored artifacts so file-based
429
+ * fallback paths see the same evidence as the DB.
430
+ * Re-mirrors restored legacy decisions into memories so the ADR-013
431
+ * memory-backed decision readers see bootstrapped decisions immediately.
249
432
  * Returns true if bootstrap succeeded, false if manifest file doesn't exist.
250
433
  */
251
434
  export function bootstrapFromManifest(basePath: string): boolean {
@@ -256,5 +439,8 @@ export function bootstrapFromManifest(basePath: string): boolean {
256
439
  }
257
440
 
258
441
  restoreManifest(manifest);
442
+ restoreArtifactProjections(basePath, manifest);
443
+ backfillDecisionsToMemories();
444
+ invalidateAllCaches();
259
445
  return true;
260
446
  }
@@ -487,10 +487,9 @@ export function getWorkflowTransportSupportError(
487
487
  }
488
488
 
489
489
  const uniqueRequired = [...new Set(requiredTools)];
490
- const piRuntimeRequired = uniqueRequired.filter((tool) => !MCP_WORKFLOW_TOOL_SURFACE.has(tool));
491
490
  const missing = (options.activeTools && options.activeTools.length > 0)
492
- ? piRuntimeRequired.filter((tool) => !hasRequiredTool(tool, options.activeTools!))
493
- : piRuntimeRequired;
491
+ ? uniqueRequired.filter((tool) => !hasRequiredTool(tool, options.activeTools!))
492
+ : uniqueRequired.filter((tool) => !MCP_WORKFLOW_TOOL_SURFACE.has(tool));
494
493
  if (missing.length === 0) return null;
495
494
 
496
495
  if (options.activeTools && options.activeTools.length > 0) {
@@ -546,6 +546,15 @@ export async function regenerateIfMissing(
546
546
  return false;
547
547
  }
548
548
 
549
+ // A sketch slice (or any slice not yet planned into tasks) legitimately has
550
+ // no PLAN.md to project. renderPlanFromDb throws on a zero-task slice, so
551
+ // skip cleanly here rather than logging a spurious failure. The PLAN.md will
552
+ // be created once the slice is refined and its first task is written through
553
+ // the single writer.
554
+ if (fileType === "PLAN" && getSliceTasks(milestoneId, sliceId).length === 0) {
555
+ return false;
556
+ }
557
+
549
558
  // Regenerate the missing file. Each renderer may swallow its own errors
550
559
  // (e.g. renderStateProjection), so confirm the file actually exists on
551
560
  // disk before reporting success — true must mean "file is there now".
@@ -259,6 +259,38 @@ export function resolveCanonicalMilestoneRoot(
259
259
  return wtPath;
260
260
  }
261
261
 
262
+ /**
263
+ * Build human-facing guidance for manually validating a milestone's work.
264
+ *
265
+ * When a milestone runs in a git worktree, its checkout lives under the hidden
266
+ * `.gsd/worktrees/<MID>/` path that a human can't easily discover. The UAT
267
+ * pause/handoff and the saved assessment use this to tell the human exactly
268
+ * where to `cd` to run or inspect the app before signing off on NEEDS-HUMAN
269
+ * checks, rather than leaving them to hunt for a buried path.
270
+ *
271
+ * Returns null when no milestone id is available.
272
+ */
273
+ export function buildManualValidationGuidance(
274
+ basePath: string,
275
+ milestoneId: string,
276
+ opts: { uatPath?: string } = {},
277
+ ): string | null {
278
+ if (!milestoneId) return null;
279
+ const validationRoot = resolveCanonicalMilestoneRoot(basePath, milestoneId);
280
+ const inWorktree = validationRoot.includes(`${sep}.gsd${sep}worktrees${sep}`);
281
+ const lines: string[] = [`Validate the work here: ${validationRoot}`];
282
+ if (inWorktree) {
283
+ lines.push(
284
+ "This milestone runs in a git worktree, so the code lives under the hidden " +
285
+ `\`.gsd/worktrees/\` path. Open it with: cd "${validationRoot}"`,
286
+ );
287
+ }
288
+ if (opts.uatPath) {
289
+ lines.push(`Follow the UAT checklist at: ${opts.uatPath}`);
290
+ }
291
+ return lines.join("\n");
292
+ }
293
+
262
294
  // ─── Core Operations ───────────────────────────────────────────────────────
263
295
 
264
296
  /**
@@ -0,0 +1,103 @@
1
+ // Project/App: gsd-pi
2
+ // File Purpose: Deterministically re-enter the active milestone's worktree on a
3
+ // cold start (after /quit + relaunch). Without this, Claude Code relaunches with
4
+ // cwd at the project root, and a bare /gsd leaves the agent there — forcing it to
5
+ // search the filesystem ("git worktree list", branch sniffing) to find its way
6
+ // back into the worktree. The worktree path is fully derivable from state, so we
7
+ // resolve and chdir into it directly instead.
8
+
9
+ import { readdirSync } from "node:fs";
10
+
11
+ import { enterAutoWorktree, getAutoWorktreePath } from "./auto-worktree.js";
12
+ import { getIsolationMode } from "./preferences.js";
13
+ import { worktreesDir } from "./worktree-manager.js";
14
+ import { isGsdWorktreePath, resolveWorktreeProjectRoot } from "./worktree-root.js";
15
+
16
+ interface LiveWorktree {
17
+ id: string;
18
+ path: string;
19
+ }
20
+
21
+ /**
22
+ * Enumerate the live (valid git) auto-worktrees under <projectRoot>/.gsd/worktrees/.
23
+ * Reuses getAutoWorktreePath's validation so stray directories are ignored.
24
+ */
25
+ function liveMilestoneWorktrees(projectRoot: string): LiveWorktree[] {
26
+ let names: string[];
27
+ try {
28
+ names = readdirSync(worktreesDir(projectRoot));
29
+ } catch {
30
+ return [];
31
+ }
32
+ const live: LiveWorktree[] = [];
33
+ for (const id of names) {
34
+ const path = getAutoWorktreePath(projectRoot, id);
35
+ if (path) live.push({ id, path });
36
+ }
37
+ return live;
38
+ }
39
+
40
+ /**
41
+ * If we're sitting at the project root with worktree isolation enabled and the
42
+ * active milestone has a live worktree, chdir into it. No-op when already inside
43
+ * a worktree, when isolation is off, or when the target is ambiguous.
44
+ *
45
+ * Single live worktree → enter it (covers the common case without deriveState).
46
+ * Multiple live worktrees → disambiguate by the active milestone from state;
47
+ * if that can't be resolved unambiguously, do nothing.
48
+ *
49
+ * Best-effort: any failure resolves to a no-op so it can never block startup.
50
+ *
51
+ * @returns the worktree path entered, or null when nothing was done.
52
+ */
53
+ export async function reenterActiveWorktreeIfNeeded(
54
+ basePath: string,
55
+ opts: { notify?: (message: string) => void } = {},
56
+ ): Promise<string | null> {
57
+ let projectRoot: string;
58
+ try {
59
+ projectRoot = resolveWorktreeProjectRoot(basePath);
60
+ } catch {
61
+ return null;
62
+ }
63
+
64
+ // Only worktree-isolation projects have worktrees to re-enter.
65
+ if (getIsolationMode(projectRoot) !== "worktree") return null;
66
+
67
+ // Already inside a worktree (warm session, or auto-mode already entered) —
68
+ // nothing to do.
69
+ let cwd: string;
70
+ try {
71
+ cwd = process.cwd();
72
+ } catch {
73
+ return null;
74
+ }
75
+ if (isGsdWorktreePath(cwd)) return null;
76
+
77
+ const live = liveMilestoneWorktrees(projectRoot);
78
+ if (live.length === 0) return null;
79
+
80
+ let target: LiveWorktree | null = live.length === 1 ? live[0]! : null;
81
+ if (!target) {
82
+ // Multiple live worktrees — disambiguate by the active milestone.
83
+ try {
84
+ const { deriveState } = await import("./state.js");
85
+ const state = await deriveState(projectRoot);
86
+ const activeId = state.activeMilestone?.id;
87
+ target = activeId ? live.find((w) => w.id === activeId) ?? null : null;
88
+ } catch {
89
+ target = null;
90
+ }
91
+ }
92
+ if (!target) return null;
93
+
94
+ try {
95
+ const entered = enterAutoWorktree(projectRoot, target.id);
96
+ opts.notify?.(`Resumed in worktree for ${target.id}.`);
97
+ return entered;
98
+ } catch {
99
+ // Worktree vanished or chdir failed — leave cwd as-is, caller falls back to
100
+ // the project root.
101
+ return null;
102
+ }
103
+ }
@@ -123,36 +123,29 @@ function forceOverwriteAssessmentsWithVerdict(
123
123
  }
124
124
  }
125
125
 
126
- function syncTopLevelMilestoneArtifacts(
126
+ function syncOtherMilestoneArtifacts(
127
127
  srcMilestonesDir: string,
128
128
  dstMilestonesDir: string,
129
+ currentMilestoneId: string,
129
130
  ): void {
130
131
  if (!existsSync(srcMilestonesDir)) return;
131
132
 
132
133
  try {
133
134
  for (const milestoneEntry of readdirSync(srcMilestonesDir, { withFileTypes: true })) {
134
135
  if (!milestoneEntry.isDirectory()) continue;
136
+ // The current milestone is already fully projected by the caller's
137
+ // additive safeCopyRecursive; skip it here to avoid redundant work.
138
+ if (milestoneEntry.name === currentMilestoneId) continue;
135
139
  const srcMilestoneDir = join(srcMilestonesDir, milestoneEntry.name);
136
140
  const dstMilestoneDir = join(dstMilestonesDir, milestoneEntry.name);
137
141
 
138
- try {
139
- mkdirSync(dstMilestoneDir, { recursive: true });
140
- for (const fileEntry of readdirSync(srcMilestoneDir, { withFileTypes: true })) {
141
- if (!fileEntry.isFile()) continue;
142
- if (!fileEntry.name.endsWith(".md") && !fileEntry.name.endsWith(".json")) continue;
143
-
144
- safeCopy(
145
- join(srcMilestoneDir, fileEntry.name),
146
- join(dstMilestoneDir, fileEntry.name),
147
- { force: false },
148
- );
149
- }
150
- } catch (err) {
151
- logWarning(
152
- "worktree",
153
- `milestone top-level artifact sync failed (${milestoneEntry.name}): ${err instanceof Error ? err.message : String(err)}`,
154
- );
155
- }
142
+ // Additively project the entire milestone subtree (force:false), not just
143
+ // top-level files. Prior completed milestones keep their per-slice and
144
+ // per-task SUMMARY.md / UAT.md on disk so the worktree's stale-render
145
+ // detector doesn't flag them as missing (DB has summary, disk doesn't).
146
+ // force:false preserves any worktree-local files (#1886 invariant) and
147
+ // only fills in files absent from the worktree projection.
148
+ safeCopyRecursive(srcMilestoneDir, dstMilestoneDir, { force: false });
156
149
  }
157
150
  } catch (err) {
158
151
  logWarning(
@@ -248,9 +241,16 @@ export function _projectRootToWorktreeImpl(
248
241
  { force: false },
249
242
  );
250
243
 
251
- // Additively project missing top-level milestone files for all milestones
252
- // so worktree-bound units can verify future-milestone context artifacts.
253
- syncTopLevelMilestoneArtifacts(join(prGsd, "milestones"), join(wtGsd, "milestones"));
244
+ // Additively project the full subtree of every OTHER milestone so
245
+ // worktree-bound units can read prior-milestone context artifacts and so
246
+ // completed milestones retain their per-slice/per-task SUMMARY.md and
247
+ // UAT.md on the worktree filesystem (otherwise the stale-render detector
248
+ // flags them as "complete in DB but missing on disk").
249
+ syncOtherMilestoneArtifacts(
250
+ join(prGsd, "milestones"),
251
+ join(wtGsd, "milestones"),
252
+ milestoneId,
253
+ );
254
254
 
255
255
  // Force-sync ASSESSMENT files that have a verdict from project root (#2821).
256
256
  // The additive-only copy above preserves worktree-local files, but
@@ -140,6 +140,10 @@ export function resolveGsdBrowserMcpLaunchConfig(
140
140
  : null;
141
141
  const sessionName =
142
142
  options.sessionName?.trim() || buildGsdBrowserSessionName(resolvedProjectRoot, options.sessionSuffix);
143
+ // Stable per-project identity key (no per-session suffix) so the browser
144
+ // profile/cookies persist across pi sessions for the same project. gsd-browser
145
+ // rejects --identity-scope unless --identity-key is also supplied.
146
+ const identityKey = env.GSD_BROWSER_IDENTITY_KEY?.trim() || buildGsdBrowserSessionName(resolvedProjectRoot);
143
147
  const command =
144
148
  explicitCommand
145
149
  || explicitCliPath
@@ -155,6 +159,8 @@ export function resolveGsdBrowserMcpLaunchConfig(
155
159
  sessionName,
156
160
  "--identity-scope",
157
161
  "project",
162
+ "--identity-key",
163
+ identityKey,
158
164
  "--identity-project",
159
165
  resolvedProjectRoot,
160
166
  ];
@@ -11,6 +11,7 @@ import {
11
11
  centerLine,
12
12
  fitColumns,
13
13
  } from "../layout-utils.js";
14
+ import { visibleWidth } from "@gsd/pi-tui";
14
15
 
15
16
  describe("formatDuration", () => {
16
17
  it("formats seconds", () => {
@@ -59,8 +60,10 @@ describe("joinColumns", () => {
59
60
 
60
61
  it("truncates when content overflows", () => {
61
62
  const result = joinColumns("a".repeat(20), "b".repeat(20), 30);
62
- // Should be truncated to 30 chars
63
- assert.ok(result.length <= 30);
63
+ // truncateToWidth is ANSI-aware and brackets the ellipsis with zero-width
64
+ // SGR resets, so the visible width — not the raw string length is the
65
+ // invariant that must hold within the column budget.
66
+ assert.ok(visibleWidth(result) <= 30);
64
67
  });
65
68
  });
66
69
 
@@ -72,7 +75,9 @@ describe("centerLine", () => {
72
75
 
73
76
  it("truncates when content exceeds width", () => {
74
77
  const result = centerLine("abcdefgh", 4);
75
- assert.ok(result.length <= 4);
78
+ // Visible width is the meaningful budget; the result may carry zero-width
79
+ // SGR reset codes around the ellipsis (see truncateToWidth).
80
+ assert.ok(visibleWidth(result) <= 4);
76
81
  });
77
82
  });
78
83
 
@@ -15,6 +15,8 @@ export interface AgentConfig {
15
15
  description: string;
16
16
  tools?: string[];
17
17
  model?: string;
18
+ /** Default reasoning effort for this agent, forwarded as `--thinking` (#508). */
19
+ thinking?: string;
18
20
  conflictsWith?: string[];
19
21
  systemPrompt: string;
20
22
  source: "user" | "project";
@@ -31,6 +33,7 @@ interface AgentFrontmatter extends Record<string, unknown> {
31
33
  description?: string;
32
34
  tools?: string | string[];
33
35
  model?: string;
36
+ thinking?: string;
34
37
  conflicts_with?: string;
35
38
  }
36
39
 
@@ -100,6 +103,7 @@ function loadAgentsFromDir(dir: string, source: "user" | "project"): AgentConfig
100
103
  description: frontmatter.description,
101
104
  tools: tools && tools.length > 0 ? tools : undefined,
102
105
  model: frontmatter.model,
106
+ thinking: typeof frontmatter.thinking === "string" ? frontmatter.thinking : undefined,
103
107
  conflictsWith,
104
108
  systemPrompt: body,
105
109
  source,