@opengsd/gsd-pi 1.9.0 → 1.10.0-dev.a5e42e99

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 (502) hide show
  1. package/README.md +10 -9
  2. package/dist/headless.js +2 -1
  3. package/dist/resources/.managed-resources-content-hash +1 -1
  4. package/dist/resources/GSD-WORKFLOW.md +2 -2
  5. package/dist/resources/extensions/gsd/artifact-verification.js +3 -4
  6. package/dist/resources/extensions/gsd/auto/loop.js +24 -4
  7. package/dist/resources/extensions/gsd/auto/run-unit.js +19 -1
  8. package/dist/resources/extensions/gsd/auto/unit-phase.js +37 -5
  9. package/dist/resources/extensions/gsd/auto-artifact-paths.js +14 -8
  10. package/dist/resources/extensions/gsd/auto-dispatch.js +2 -0
  11. package/dist/resources/extensions/gsd/auto-model-selection.js +1 -0
  12. package/dist/resources/extensions/gsd/auto-prompts.js +102 -21
  13. package/dist/resources/extensions/gsd/auto-recovery.js +10 -5
  14. package/dist/resources/extensions/gsd/auto-runtime-state.js +5 -5
  15. package/dist/resources/extensions/gsd/auto-start.js +30 -10
  16. package/dist/resources/extensions/gsd/auto-tool-tracking.js +40 -0
  17. package/dist/resources/extensions/gsd/auto-verification.js +25 -12
  18. package/dist/resources/extensions/gsd/auto-worktree-cleanup.js +4 -0
  19. package/dist/resources/extensions/gsd/auto-worktree-sync.js +7 -3
  20. package/dist/resources/extensions/gsd/auto-worktree-teardown.js +1 -1
  21. package/dist/resources/extensions/gsd/auto.js +15 -5
  22. package/dist/resources/extensions/gsd/bootstrap/db-tools.js +2 -0
  23. package/dist/resources/extensions/gsd/bootstrap/register-hooks.js +33 -7
  24. package/dist/resources/extensions/gsd/bootstrap/system-context.js +10 -3
  25. package/dist/resources/extensions/gsd/bootstrap/write-gate.js +7 -26
  26. package/dist/resources/extensions/gsd/commands-verdict.js +10 -9
  27. package/dist/resources/extensions/gsd/compat/compat-marker.js +80 -5
  28. package/dist/resources/extensions/gsd/constants.js +12 -13
  29. package/dist/resources/extensions/gsd/context-masker.js +97 -21
  30. package/dist/resources/extensions/gsd/db/engine.js +5 -2
  31. package/dist/resources/extensions/gsd/docs/preferences-reference.md +33 -2
  32. package/dist/resources/extensions/gsd/doctor-engine-checks.js +40 -12
  33. package/dist/resources/extensions/gsd/error-classifier.js +12 -2
  34. package/dist/resources/extensions/gsd/exec-sandbox.js +1 -0
  35. package/dist/resources/extensions/gsd/flat-phase-migration.js +12 -0
  36. package/dist/resources/extensions/gsd/git-service.js +19 -7
  37. package/dist/resources/extensions/gsd/gsd-db.js +75 -31
  38. package/dist/resources/extensions/gsd/guided-flow.js +11 -5
  39. package/dist/resources/extensions/gsd/markdown-renderer.js +5 -21
  40. package/dist/resources/extensions/gsd/md-importer.js +5 -6
  41. package/dist/resources/extensions/gsd/model-cost-table.js +6 -1
  42. package/dist/resources/extensions/gsd/model-router.js +16 -0
  43. package/dist/resources/extensions/gsd/paths.js +66 -15
  44. package/dist/resources/extensions/gsd/planning-subagent-policy.js +37 -0
  45. package/dist/resources/extensions/gsd/planning-subagent-registry.js +33 -0
  46. package/dist/resources/extensions/gsd/preferences-models.js +50 -10
  47. package/dist/resources/extensions/gsd/preferences-types.js +6 -0
  48. package/dist/resources/extensions/gsd/preferences-validation.js +86 -4
  49. package/dist/resources/extensions/gsd/preferences.js +24 -0
  50. package/dist/resources/extensions/gsd/prompts/validate-milestone.md +5 -5
  51. package/dist/resources/extensions/gsd/provider-payload-policy.js +16 -5
  52. package/dist/resources/extensions/gsd/safety/destructive-guard.js +1 -1
  53. package/dist/resources/extensions/gsd/skill-activation.js +17 -3
  54. package/dist/resources/extensions/gsd/state-reconciliation/drift/external-markdown-edit.js +98 -2
  55. package/dist/resources/extensions/gsd/state-reconciliation/drift/external-planning-edit.js +4 -1
  56. package/dist/resources/extensions/gsd/state-reconciliation/drift/roadmap.js +5 -2
  57. package/dist/resources/extensions/gsd/stop-notice.js +9 -0
  58. package/dist/resources/extensions/gsd/templates/PREFERENCES.md +8 -0
  59. package/dist/resources/extensions/gsd/tools/complete-task.js +12 -5
  60. package/dist/resources/extensions/gsd/tools/plan-slice.js +5 -1
  61. package/dist/resources/extensions/gsd/tools/reopen-milestone.js +14 -5
  62. package/dist/resources/extensions/gsd/tools/reopen-slice.js +15 -5
  63. package/dist/resources/extensions/gsd/tools/reopen-task.js +16 -10
  64. package/dist/resources/extensions/gsd/unit-context-composer.js +28 -5
  65. package/dist/resources/extensions/gsd/verification-evidence.js +5 -3
  66. package/dist/resources/extensions/gsd/workflow-projections.js +2 -3
  67. package/dist/resources/extensions/gsd/workspace.js +2 -1
  68. package/dist/resources/extensions/gsd/worktree-state-projection.js +20 -18
  69. package/dist/resources/extensions/ollama/ollama-chat-provider.js +2 -2
  70. package/dist/resources/extensions/shared/interview-ui.js +43 -6
  71. package/dist/resources/extensions/subagent/index.js +1 -1
  72. package/dist/startup-model-validation.d.ts +1 -1
  73. package/dist/tsconfig.extensions.tsbuildinfo +1 -1
  74. package/dist/web/standalone/.next/BUILD_ID +1 -1
  75. package/dist/web/standalone/.next/app-path-routes-manifest.json +9 -9
  76. package/dist/web/standalone/.next/build-manifest.json +3 -3
  77. package/dist/web/standalone/.next/prerender-manifest.json +3 -3
  78. package/dist/web/standalone/.next/required-server-files.json +3 -3
  79. package/dist/web/standalone/.next/server/app/_global-error/page.js +3 -3
  80. package/dist/web/standalone/.next/server/app/_global-error/page_client-reference-manifest.js +1 -1
  81. package/dist/web/standalone/.next/server/app/_global-error.html +1 -1
  82. package/dist/web/standalone/.next/server/app/_global-error.rsc +1 -1
  83. package/dist/web/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  84. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
  85. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
  86. package/dist/web/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  87. package/dist/web/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  88. package/dist/web/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  89. package/dist/web/standalone/.next/server/app/_not-found/page.js +2 -2
  90. package/dist/web/standalone/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  91. package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
  92. package/dist/web/standalone/.next/server/app/_not-found.rsc +3 -3
  93. package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +3 -3
  94. package/dist/web/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  95. package/dist/web/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +3 -3
  96. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  97. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  98. package/dist/web/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
  99. package/dist/web/standalone/.next/server/app/api/boot/route.js +1 -1
  100. package/dist/web/standalone/.next/server/app/api/boot/route_client-reference-manifest.js +1 -1
  101. package/dist/web/standalone/.next/server/app/api/bridge-terminal/input/route.js +1 -1
  102. package/dist/web/standalone/.next/server/app/api/bridge-terminal/input/route_client-reference-manifest.js +1 -1
  103. package/dist/web/standalone/.next/server/app/api/bridge-terminal/resize/route.js +1 -1
  104. package/dist/web/standalone/.next/server/app/api/bridge-terminal/resize/route_client-reference-manifest.js +1 -1
  105. package/dist/web/standalone/.next/server/app/api/bridge-terminal/stream/route.js +2 -2
  106. package/dist/web/standalone/.next/server/app/api/bridge-terminal/stream/route_client-reference-manifest.js +1 -1
  107. package/dist/web/standalone/.next/server/app/api/browse-directories/route.js +1 -1
  108. package/dist/web/standalone/.next/server/app/api/browse-directories/route_client-reference-manifest.js +1 -1
  109. package/dist/web/standalone/.next/server/app/api/captures/route.js +1 -1
  110. package/dist/web/standalone/.next/server/app/api/captures/route_client-reference-manifest.js +1 -1
  111. package/dist/web/standalone/.next/server/app/api/cleanup/route.js +1 -1
  112. package/dist/web/standalone/.next/server/app/api/cleanup/route_client-reference-manifest.js +1 -1
  113. package/dist/web/standalone/.next/server/app/api/dev-mode/route.js +1 -1
  114. package/dist/web/standalone/.next/server/app/api/dev-mode/route_client-reference-manifest.js +1 -1
  115. package/dist/web/standalone/.next/server/app/api/doctor/route.js +1 -1
  116. package/dist/web/standalone/.next/server/app/api/doctor/route_client-reference-manifest.js +1 -1
  117. package/dist/web/standalone/.next/server/app/api/experimental/route.js +2 -2
  118. package/dist/web/standalone/.next/server/app/api/experimental/route_client-reference-manifest.js +1 -1
  119. package/dist/web/standalone/.next/server/app/api/export-data/route.js +1 -1
  120. package/dist/web/standalone/.next/server/app/api/export-data/route_client-reference-manifest.js +1 -1
  121. package/dist/web/standalone/.next/server/app/api/files/route.js +1 -1
  122. package/dist/web/standalone/.next/server/app/api/files/route_client-reference-manifest.js +1 -1
  123. package/dist/web/standalone/.next/server/app/api/forensics/route.js +1 -1
  124. package/dist/web/standalone/.next/server/app/api/forensics/route_client-reference-manifest.js +1 -1
  125. package/dist/web/standalone/.next/server/app/api/git/route.js +1 -1
  126. package/dist/web/standalone/.next/server/app/api/git/route_client-reference-manifest.js +1 -1
  127. package/dist/web/standalone/.next/server/app/api/history/route.js +1 -1
  128. package/dist/web/standalone/.next/server/app/api/history/route_client-reference-manifest.js +1 -1
  129. package/dist/web/standalone/.next/server/app/api/hooks/route.js +1 -1
  130. package/dist/web/standalone/.next/server/app/api/hooks/route_client-reference-manifest.js +1 -1
  131. package/dist/web/standalone/.next/server/app/api/inspect/route.js +1 -1
  132. package/dist/web/standalone/.next/server/app/api/inspect/route_client-reference-manifest.js +1 -1
  133. package/dist/web/standalone/.next/server/app/api/knowledge/route.js +1 -1
  134. package/dist/web/standalone/.next/server/app/api/knowledge/route_client-reference-manifest.js +1 -1
  135. package/dist/web/standalone/.next/server/app/api/live-state/route.js +1 -1
  136. package/dist/web/standalone/.next/server/app/api/live-state/route_client-reference-manifest.js +1 -1
  137. package/dist/web/standalone/.next/server/app/api/mcp-connections/route.js +1 -1
  138. package/dist/web/standalone/.next/server/app/api/mcp-connections/route_client-reference-manifest.js +1 -1
  139. package/dist/web/standalone/.next/server/app/api/notifications/route.js +2 -2
  140. package/dist/web/standalone/.next/server/app/api/notifications/route_client-reference-manifest.js +1 -1
  141. package/dist/web/standalone/.next/server/app/api/onboarding/route.js +1 -1
  142. package/dist/web/standalone/.next/server/app/api/onboarding/route_client-reference-manifest.js +1 -1
  143. package/dist/web/standalone/.next/server/app/api/preferences/route.js +1 -1
  144. package/dist/web/standalone/.next/server/app/api/preferences/route_client-reference-manifest.js +1 -1
  145. package/dist/web/standalone/.next/server/app/api/projects/route.js +1 -1
  146. package/dist/web/standalone/.next/server/app/api/projects/route_client-reference-manifest.js +1 -1
  147. package/dist/web/standalone/.next/server/app/api/recovery/route.js +1 -1
  148. package/dist/web/standalone/.next/server/app/api/recovery/route_client-reference-manifest.js +1 -1
  149. package/dist/web/standalone/.next/server/app/api/remote-questions/route.js +2 -2
  150. package/dist/web/standalone/.next/server/app/api/remote-questions/route_client-reference-manifest.js +1 -1
  151. package/dist/web/standalone/.next/server/app/api/session/browser/route.js +1 -1
  152. package/dist/web/standalone/.next/server/app/api/session/browser/route_client-reference-manifest.js +1 -1
  153. package/dist/web/standalone/.next/server/app/api/session/command/route.js +1 -1
  154. package/dist/web/standalone/.next/server/app/api/session/command/route_client-reference-manifest.js +1 -1
  155. package/dist/web/standalone/.next/server/app/api/session/events/route.js +2 -2
  156. package/dist/web/standalone/.next/server/app/api/session/events/route_client-reference-manifest.js +1 -1
  157. package/dist/web/standalone/.next/server/app/api/session/manage/route.js +1 -1
  158. package/dist/web/standalone/.next/server/app/api/session/manage/route_client-reference-manifest.js +1 -1
  159. package/dist/web/standalone/.next/server/app/api/settings-data/route.js +1 -1
  160. package/dist/web/standalone/.next/server/app/api/settings-data/route_client-reference-manifest.js +1 -1
  161. package/dist/web/standalone/.next/server/app/api/shutdown/route.js +1 -1
  162. package/dist/web/standalone/.next/server/app/api/shutdown/route_client-reference-manifest.js +1 -1
  163. package/dist/web/standalone/.next/server/app/api/skill-health/route.js +1 -1
  164. package/dist/web/standalone/.next/server/app/api/skill-health/route_client-reference-manifest.js +1 -1
  165. package/dist/web/standalone/.next/server/app/api/steer/route.js +1 -1
  166. package/dist/web/standalone/.next/server/app/api/steer/route_client-reference-manifest.js +1 -1
  167. package/dist/web/standalone/.next/server/app/api/switch-root/route.js +1 -1
  168. package/dist/web/standalone/.next/server/app/api/switch-root/route_client-reference-manifest.js +1 -1
  169. package/dist/web/standalone/.next/server/app/api/terminal/input/route.js +1 -1
  170. package/dist/web/standalone/.next/server/app/api/terminal/input/route_client-reference-manifest.js +1 -1
  171. package/dist/web/standalone/.next/server/app/api/terminal/resize/route.js +2 -2
  172. package/dist/web/standalone/.next/server/app/api/terminal/resize/route_client-reference-manifest.js +1 -1
  173. package/dist/web/standalone/.next/server/app/api/terminal/sessions/route.js +1 -1
  174. package/dist/web/standalone/.next/server/app/api/terminal/sessions/route_client-reference-manifest.js +1 -1
  175. package/dist/web/standalone/.next/server/app/api/terminal/stream/route.js +4 -4
  176. package/dist/web/standalone/.next/server/app/api/terminal/stream/route_client-reference-manifest.js +1 -1
  177. package/dist/web/standalone/.next/server/app/api/terminal/upload/route.js +1 -1
  178. package/dist/web/standalone/.next/server/app/api/terminal/upload/route_client-reference-manifest.js +1 -1
  179. package/dist/web/standalone/.next/server/app/api/undo/route.js +1 -1
  180. package/dist/web/standalone/.next/server/app/api/undo/route_client-reference-manifest.js +1 -1
  181. package/dist/web/standalone/.next/server/app/api/update/route.js +1 -1
  182. package/dist/web/standalone/.next/server/app/api/update/route_client-reference-manifest.js +1 -1
  183. package/dist/web/standalone/.next/server/app/api/visualizer/route.js +1 -1
  184. package/dist/web/standalone/.next/server/app/api/visualizer/route_client-reference-manifest.js +1 -1
  185. package/dist/web/standalone/.next/server/app/index.html +1 -1
  186. package/dist/web/standalone/.next/server/app/index.rsc +4 -4
  187. package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +2 -2
  188. package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +4 -4
  189. package/dist/web/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
  190. package/dist/web/standalone/.next/server/app/index.segments/_index.segment.rsc +3 -3
  191. package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
  192. package/dist/web/standalone/.next/server/app/page.js +2 -2
  193. package/dist/web/standalone/.next/server/app/page_client-reference-manifest.js +1 -1
  194. package/dist/web/standalone/.next/server/app-paths-manifest.json +9 -9
  195. package/dist/web/standalone/.next/server/chunks/1991.js +2 -2
  196. package/dist/web/standalone/.next/server/chunks/2842.js +4 -4
  197. package/dist/web/standalone/.next/server/chunks/5124.js +1 -1
  198. package/dist/web/standalone/.next/server/chunks/8357.js +3 -3
  199. package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
  200. package/dist/web/standalone/.next/server/middleware.js +3 -3
  201. package/dist/web/standalone/.next/server/pages/404.html +1 -1
  202. package/dist/web/standalone/.next/server/pages/500.html +1 -1
  203. package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
  204. package/dist/web/standalone/.next/static/chunks/app/_not-found/page-a6fb1847f67f167c.js +1 -0
  205. package/dist/web/standalone/.next/static/chunks/app/{layout-b35cbfff38aaf4cf.js → layout-4ae2d68984392bbf.js} +1 -1
  206. package/dist/web/standalone/.next/static/chunks/app/page-72a856634ad14c10.js +1 -0
  207. package/dist/web/standalone/.next/static/chunks/{main-app-590a74400e35f685.js → main-app-90d1d8d5e5d2dc6b.js} +1 -1
  208. package/dist/web/standalone/.next/static/chunks/next/dist/client/components/builtin/global-error-1115d46ac61b9823.js +1 -0
  209. package/dist/web/standalone/node_modules/@gsd/native/package.json +1 -1
  210. package/dist/web/standalone/node_modules/node-pty/build/Makefile +2 -2
  211. package/dist/web/standalone/node_modules/node-pty/build/Release/pty.node +0 -0
  212. package/dist/web/standalone/node_modules/node-pty/build/pty.target.mk +14 -14
  213. package/dist/web/standalone/server.js +1 -1
  214. package/integrations/hermes/open_gsd_hermes/gsd_client.py +1 -1
  215. package/integrations/hermes/pyproject.toml +1 -1
  216. package/package.json +8 -6
  217. package/packages/cloud-mcp-gateway/package.json +2 -2
  218. package/packages/contracts/dist/rpc.d.ts +1 -1
  219. package/packages/contracts/dist/rpc.d.ts.map +1 -1
  220. package/packages/contracts/dist/rpc.js +1 -1
  221. package/packages/contracts/dist/rpc.js.map +1 -1
  222. package/packages/contracts/dist/rpc.test.js +1 -1
  223. package/packages/contracts/dist/rpc.test.js.map +1 -1
  224. package/packages/contracts/package.json +1 -1
  225. package/packages/daemon/package.json +4 -4
  226. package/packages/gsd-agent-core/dist/compaction/compaction.d.ts.map +1 -1
  227. package/packages/gsd-agent-core/dist/compaction/compaction.js +36 -4
  228. package/packages/gsd-agent-core/dist/compaction/compaction.js.map +1 -1
  229. package/packages/gsd-agent-core/dist/sdk.d.ts.map +1 -1
  230. package/packages/gsd-agent-core/dist/sdk.js +1 -0
  231. package/packages/gsd-agent-core/dist/sdk.js.map +1 -1
  232. package/packages/gsd-agent-core/package.json +5 -5
  233. package/packages/gsd-agent-modes/dist/cli/args.js +2 -2
  234. package/packages/gsd-agent-modes/dist/cli/args.js.map +1 -1
  235. package/packages/gsd-agent-modes/dist/modes/interactive/components/settings-selector.d.ts.map +1 -1
  236. package/packages/gsd-agent-modes/dist/modes/interactive/components/settings-selector.js +1 -0
  237. package/packages/gsd-agent-modes/dist/modes/interactive/components/settings-selector.js.map +1 -1
  238. package/packages/gsd-agent-modes/dist/modes/interactive/components/thinking-selector.d.ts.map +1 -1
  239. package/packages/gsd-agent-modes/dist/modes/interactive/components/thinking-selector.js +1 -0
  240. package/packages/gsd-agent-modes/dist/modes/interactive/components/thinking-selector.js.map +1 -1
  241. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-autocomplete.d.ts.map +1 -1
  242. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-autocomplete.js +1 -0
  243. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-autocomplete.js.map +1 -1
  244. package/packages/gsd-agent-modes/package.json +7 -7
  245. package/packages/gsd-cloud/README.md +21 -5
  246. package/packages/gsd-cloud/dist/background-cli.test.d.ts +2 -0
  247. package/packages/gsd-cloud/dist/background-cli.test.d.ts.map +1 -0
  248. package/packages/gsd-cloud/dist/background-cli.test.js +325 -0
  249. package/packages/gsd-cloud/dist/background-cli.test.js.map +1 -0
  250. package/packages/gsd-cloud/dist/cli.d.ts.map +1 -1
  251. package/packages/gsd-cloud/dist/cli.js +146 -16
  252. package/packages/gsd-cloud/dist/cli.js.map +1 -1
  253. package/packages/gsd-cloud/dist/cloud-config.d.ts +1 -1
  254. package/packages/gsd-cloud/dist/cloud-config.d.ts.map +1 -1
  255. package/packages/gsd-cloud/dist/cloud-config.js +7 -1
  256. package/packages/gsd-cloud/dist/cloud-config.js.map +1 -1
  257. package/packages/gsd-cloud/dist/cloud-runtime.d.ts +7 -4
  258. package/packages/gsd-cloud/dist/cloud-runtime.d.ts.map +1 -1
  259. package/packages/gsd-cloud/dist/cloud-runtime.js +115 -31
  260. package/packages/gsd-cloud/dist/cloud-runtime.js.map +1 -1
  261. package/packages/gsd-cloud/dist/cloud-runtime.test.js +224 -59
  262. package/packages/gsd-cloud/dist/cloud-runtime.test.js.map +1 -1
  263. package/packages/gsd-cloud/dist/runtime-artifacts.d.ts +3 -0
  264. package/packages/gsd-cloud/dist/runtime-artifacts.d.ts.map +1 -0
  265. package/packages/gsd-cloud/dist/runtime-artifacts.js +36 -0
  266. package/packages/gsd-cloud/dist/runtime-artifacts.js.map +1 -0
  267. package/packages/gsd-cloud/dist/runtime-process.d.ts +32 -0
  268. package/packages/gsd-cloud/dist/runtime-process.d.ts.map +1 -0
  269. package/packages/gsd-cloud/dist/runtime-process.js +640 -0
  270. package/packages/gsd-cloud/dist/runtime-process.js.map +1 -0
  271. package/packages/gsd-cloud/dist/runtime-process.test.d.ts +2 -0
  272. package/packages/gsd-cloud/dist/runtime-process.test.d.ts.map +1 -0
  273. package/packages/gsd-cloud/dist/runtime-process.test.js +543 -0
  274. package/packages/gsd-cloud/dist/runtime-process.test.js.map +1 -0
  275. package/packages/gsd-cloud/dist/runtime-telemetry.d.ts +117 -0
  276. package/packages/gsd-cloud/dist/runtime-telemetry.d.ts.map +1 -0
  277. package/packages/gsd-cloud/dist/runtime-telemetry.js +291 -0
  278. package/packages/gsd-cloud/dist/runtime-telemetry.js.map +1 -0
  279. package/packages/gsd-cloud/dist/runtime-telemetry.test.d.ts +2 -0
  280. package/packages/gsd-cloud/dist/runtime-telemetry.test.d.ts.map +1 -0
  281. package/packages/gsd-cloud/dist/runtime-telemetry.test.js +434 -0
  282. package/packages/gsd-cloud/dist/runtime-telemetry.test.js.map +1 -0
  283. package/packages/gsd-cloud/package.json +2 -2
  284. package/packages/mcp-server/README.md +7 -2
  285. package/packages/mcp-server/dist/cli-runner.d.ts +3 -0
  286. package/packages/mcp-server/dist/cli-runner.d.ts.map +1 -1
  287. package/packages/mcp-server/dist/cli-runner.js +119 -33
  288. package/packages/mcp-server/dist/cli-runner.js.map +1 -1
  289. package/packages/mcp-server/dist/server.d.ts.map +1 -1
  290. package/packages/mcp-server/dist/server.js +5 -5
  291. package/packages/mcp-server/dist/server.js.map +1 -1
  292. package/packages/mcp-server/dist/workflow-tools.d.ts +4 -3
  293. package/packages/mcp-server/dist/workflow-tools.d.ts.map +1 -1
  294. package/packages/mcp-server/dist/workflow-tools.js.map +1 -1
  295. package/packages/mcp-server/package.json +4 -4
  296. package/packages/native/package.json +1 -1
  297. package/packages/pi-agent-core/dist/agent.d.ts +5 -1
  298. package/packages/pi-agent-core/dist/agent.d.ts.map +1 -1
  299. package/packages/pi-agent-core/dist/agent.js +2 -0
  300. package/packages/pi-agent-core/dist/agent.js.map +1 -1
  301. package/packages/pi-agent-core/dist/types.d.ts +2 -2
  302. package/packages/pi-agent-core/dist/types.d.ts.map +1 -1
  303. package/packages/pi-agent-core/dist/types.js.map +1 -1
  304. package/packages/pi-agent-core/package.json +1 -1
  305. package/packages/pi-ai/dist/models.d.ts.map +1 -1
  306. package/packages/pi-ai/dist/models.generated.d.ts +187 -0
  307. package/packages/pi-ai/dist/models.generated.d.ts.map +1 -1
  308. package/packages/pi-ai/dist/models.generated.js +145 -0
  309. package/packages/pi-ai/dist/models.generated.js.map +1 -1
  310. package/packages/pi-ai/dist/models.js +18 -6
  311. package/packages/pi-ai/dist/models.js.map +1 -1
  312. package/packages/pi-ai/dist/providers/amazon-bedrock.js +3 -2
  313. package/packages/pi-ai/dist/providers/amazon-bedrock.js.map +1 -1
  314. package/packages/pi-ai/dist/providers/anthropic.js +6 -1
  315. package/packages/pi-ai/dist/providers/anthropic.js.map +1 -1
  316. package/packages/pi-ai/dist/providers/azure-openai-responses.d.ts +1 -1
  317. package/packages/pi-ai/dist/providers/azure-openai-responses.d.ts.map +1 -1
  318. package/packages/pi-ai/dist/providers/azure-openai-responses.js.map +1 -1
  319. package/packages/pi-ai/dist/providers/google-vertex.js.map +1 -1
  320. package/packages/pi-ai/dist/providers/google.js.map +1 -1
  321. package/packages/pi-ai/dist/providers/openai-codex-responses.d.ts +1 -1
  322. package/packages/pi-ai/dist/providers/openai-codex-responses.d.ts.map +1 -1
  323. package/packages/pi-ai/dist/providers/openai-codex-responses.js +1 -1
  324. package/packages/pi-ai/dist/providers/openai-codex-responses.js.map +1 -1
  325. package/packages/pi-ai/dist/providers/openai-completions.d.ts +1 -1
  326. package/packages/pi-ai/dist/providers/openai-completions.d.ts.map +1 -1
  327. package/packages/pi-ai/dist/providers/openai-completions.js +103 -0
  328. package/packages/pi-ai/dist/providers/openai-completions.js.map +1 -1
  329. package/packages/pi-ai/dist/providers/openai-responses.d.ts +1 -1
  330. package/packages/pi-ai/dist/providers/openai-responses.d.ts.map +1 -1
  331. package/packages/pi-ai/dist/providers/openai-responses.js +1 -1
  332. package/packages/pi-ai/dist/providers/openai-responses.js.map +1 -1
  333. package/packages/pi-ai/dist/providers/simple-options.d.ts +1 -1
  334. package/packages/pi-ai/dist/providers/simple-options.d.ts.map +1 -1
  335. package/packages/pi-ai/dist/providers/simple-options.js +1 -1
  336. package/packages/pi-ai/dist/providers/simple-options.js.map +1 -1
  337. package/packages/pi-ai/dist/types.d.ts +9 -1
  338. package/packages/pi-ai/dist/types.d.ts.map +1 -1
  339. package/packages/pi-ai/dist/types.js.map +1 -1
  340. package/packages/pi-ai/package.json +1 -1
  341. package/packages/pi-coding-agent/dist/core/capability-patches.js +1 -1
  342. package/packages/pi-coding-agent/dist/core/capability-patches.js.map +1 -1
  343. package/packages/pi-coding-agent/dist/core/model-registry.d.ts.map +1 -1
  344. package/packages/pi-coding-agent/dist/core/model-registry.js +1 -0
  345. package/packages/pi-coding-agent/dist/core/model-registry.js.map +1 -1
  346. package/packages/pi-coding-agent/dist/core/model-resolver.js +2 -2
  347. package/packages/pi-coding-agent/dist/core/model-resolver.js.map +1 -1
  348. package/packages/pi-coding-agent/dist/core/settings-manager.d.ts +14 -4
  349. package/packages/pi-coding-agent/dist/core/settings-manager.d.ts.map +1 -1
  350. package/packages/pi-coding-agent/dist/core/settings-manager.js +8 -0
  351. package/packages/pi-coding-agent/dist/core/settings-manager.js.map +1 -1
  352. package/packages/pi-coding-agent/dist/core/skills.d.ts +5 -0
  353. package/packages/pi-coding-agent/dist/core/skills.d.ts.map +1 -1
  354. package/packages/pi-coding-agent/dist/core/skills.js +54 -0
  355. package/packages/pi-coding-agent/dist/core/skills.js.map +1 -1
  356. package/packages/pi-coding-agent/dist/theme/theme.d.ts +1 -1
  357. package/packages/pi-coding-agent/dist/theme/theme.d.ts.map +1 -1
  358. package/packages/pi-coding-agent/dist/theme/theme.js +1 -0
  359. package/packages/pi-coding-agent/dist/theme/theme.js.map +1 -1
  360. package/packages/pi-coding-agent/package.json +7 -7
  361. package/packages/pi-tui/package.json +2 -2
  362. package/packages/rpc-client/package.json +2 -2
  363. package/pkg/dist/theme/theme.d.ts +1 -1
  364. package/pkg/dist/theme/theme.d.ts.map +1 -1
  365. package/pkg/dist/theme/theme.js +1 -0
  366. package/pkg/dist/theme/theme.js.map +1 -1
  367. package/pkg/package.json +1 -1
  368. package/src/resources/GSD-WORKFLOW.md +2 -2
  369. package/src/resources/extensions/gsd/artifact-verification.ts +3 -8
  370. package/src/resources/extensions/gsd/auto/loop.ts +48 -4
  371. package/src/resources/extensions/gsd/auto/run-unit.ts +20 -1
  372. package/src/resources/extensions/gsd/auto/unit-phase.ts +41 -5
  373. package/src/resources/extensions/gsd/auto-artifact-paths.ts +14 -7
  374. package/src/resources/extensions/gsd/auto-dispatch.ts +1 -0
  375. package/src/resources/extensions/gsd/auto-model-selection.ts +1 -0
  376. package/src/resources/extensions/gsd/auto-prompts.ts +118 -19
  377. package/src/resources/extensions/gsd/auto-recovery.ts +10 -8
  378. package/src/resources/extensions/gsd/auto-runtime-state.ts +12 -7
  379. package/src/resources/extensions/gsd/auto-start.ts +32 -9
  380. package/src/resources/extensions/gsd/auto-tool-tracking.ts +56 -0
  381. package/src/resources/extensions/gsd/auto-verification.ts +46 -12
  382. package/src/resources/extensions/gsd/auto-worktree-cleanup.ts +4 -0
  383. package/src/resources/extensions/gsd/auto-worktree-sync.ts +10 -3
  384. package/src/resources/extensions/gsd/auto-worktree-teardown.ts +1 -1
  385. package/src/resources/extensions/gsd/auto.ts +20 -7
  386. package/src/resources/extensions/gsd/bootstrap/db-tools.ts +1 -0
  387. package/src/resources/extensions/gsd/bootstrap/register-hooks.ts +41 -12
  388. package/src/resources/extensions/gsd/bootstrap/system-context.ts +11 -3
  389. package/src/resources/extensions/gsd/bootstrap/write-gate.ts +10 -31
  390. package/src/resources/extensions/gsd/commands-verdict.ts +16 -9
  391. package/src/resources/extensions/gsd/compat/compat-marker.ts +99 -5
  392. package/src/resources/extensions/gsd/constants.ts +14 -13
  393. package/src/resources/extensions/gsd/context-masker.ts +98 -2
  394. package/src/resources/extensions/gsd/db/engine.ts +5 -2
  395. package/src/resources/extensions/gsd/docs/preferences-reference.md +33 -2
  396. package/src/resources/extensions/gsd/doctor-engine-checks.ts +45 -11
  397. package/src/resources/extensions/gsd/error-classifier.ts +13 -2
  398. package/src/resources/extensions/gsd/exec-sandbox.ts +1 -0
  399. package/src/resources/extensions/gsd/flat-phase-migration.ts +14 -0
  400. package/src/resources/extensions/gsd/git-service.ts +19 -6
  401. package/src/resources/extensions/gsd/gsd-db.ts +85 -33
  402. package/src/resources/extensions/gsd/guided-flow.ts +11 -4
  403. package/src/resources/extensions/gsd/markdown-renderer.ts +5 -23
  404. package/src/resources/extensions/gsd/md-importer.ts +5 -5
  405. package/src/resources/extensions/gsd/model-cost-table.ts +14 -1
  406. package/src/resources/extensions/gsd/model-router.ts +17 -0
  407. package/src/resources/extensions/gsd/paths.ts +73 -15
  408. package/src/resources/extensions/gsd/planning-subagent-policy.ts +58 -0
  409. package/src/resources/extensions/gsd/planning-subagent-registry.ts +45 -0
  410. package/src/resources/extensions/gsd/preferences-models.ts +54 -10
  411. package/src/resources/extensions/gsd/preferences-types.ts +26 -2
  412. package/src/resources/extensions/gsd/preferences-validation.ts +108 -3
  413. package/src/resources/extensions/gsd/preferences.ts +38 -0
  414. package/src/resources/extensions/gsd/prompts/validate-milestone.md +5 -5
  415. package/src/resources/extensions/gsd/provider-payload-policy.ts +18 -4
  416. package/src/resources/extensions/gsd/safety/destructive-guard.ts +1 -1
  417. package/src/resources/extensions/gsd/skill-activation.ts +17 -3
  418. package/src/resources/extensions/gsd/state-reconciliation/drift/external-markdown-edit.ts +132 -2
  419. package/src/resources/extensions/gsd/state-reconciliation/drift/external-planning-edit.ts +4 -1
  420. package/src/resources/extensions/gsd/state-reconciliation/drift/roadmap.ts +4 -2
  421. package/src/resources/extensions/gsd/stop-notice.ts +11 -0
  422. package/src/resources/extensions/gsd/templates/PREFERENCES.md +8 -0
  423. package/src/resources/extensions/gsd/tests/auto-artifact-paths.test.ts +7 -7
  424. package/src/resources/extensions/gsd/tests/auto-loop.test.ts +264 -0
  425. package/src/resources/extensions/gsd/tests/auto-recovery.test.ts +39 -0
  426. package/src/resources/extensions/gsd/tests/auto-start-index-lock.test.ts +21 -1
  427. package/src/resources/extensions/gsd/tests/auto-wrapup-inflight-guard.test.ts +74 -1
  428. package/src/resources/extensions/gsd/tests/before-provider-context-management.test.ts +55 -0
  429. package/src/resources/extensions/gsd/tests/commands-verdict.test.ts +10 -0
  430. package/src/resources/extensions/gsd/tests/compat-marker.test.ts +25 -0
  431. package/src/resources/extensions/gsd/tests/complete-task.test.ts +76 -1
  432. package/src/resources/extensions/gsd/tests/context-masker.test.ts +204 -8
  433. package/src/resources/extensions/gsd/tests/db-engine-cjs-compat.test.ts +12 -0
  434. package/src/resources/extensions/gsd/tests/db-tools-alias-suppression.test.ts +62 -0
  435. package/src/resources/extensions/gsd/tests/destructive-confirmation.test.ts +33 -0
  436. package/src/resources/extensions/gsd/tests/destructive-guard.test.ts +14 -0
  437. package/src/resources/extensions/gsd/tests/doctor-scope-db-unavailable.test.ts +147 -0
  438. package/src/resources/extensions/gsd/tests/exec-sandbox.test.ts +33 -1
  439. package/src/resources/extensions/gsd/tests/external-markdown-edit.test.ts +38 -1
  440. package/src/resources/extensions/gsd/tests/flat-phase-migration.test.ts +8 -9
  441. package/src/resources/extensions/gsd/tests/flat-phase-validation-path.test.ts +20 -1
  442. package/src/resources/extensions/gsd/tests/gsd-db.test.ts +32 -0
  443. package/src/resources/extensions/gsd/tests/guided-discuss-milestone-prompt-rendering.test.ts +22 -0
  444. package/src/resources/extensions/gsd/tests/integration/git-service.test.ts +13 -0
  445. package/src/resources/extensions/gsd/tests/integration/paths.test.ts +22 -4
  446. package/src/resources/extensions/gsd/tests/integration-branch-meta-location.test.ts +62 -0
  447. package/src/resources/extensions/gsd/tests/markdown-renderer.test.ts +5 -5
  448. package/src/resources/extensions/gsd/tests/model-cost-table.test.ts +23 -0
  449. package/src/resources/extensions/gsd/tests/model-isolation.test.ts +55 -0
  450. package/src/resources/extensions/gsd/tests/model-router.test.ts +1 -1
  451. package/src/resources/extensions/gsd/tests/parallel-skill-prompt-integration.test.ts +7 -5
  452. package/src/resources/extensions/gsd/tests/plan-slice.test.ts +24 -0
  453. package/src/resources/extensions/gsd/tests/post-exec-retry-bypass.test.ts +38 -1
  454. package/src/resources/extensions/gsd/tests/preferences.test.ts +151 -0
  455. package/src/resources/extensions/gsd/tests/prefs-wizard-coverage.test.ts +7 -0
  456. package/src/resources/extensions/gsd/tests/prompt-budget-enforcement.test.ts +87 -0
  457. package/src/resources/extensions/gsd/tests/provider-errors.test.ts +121 -1
  458. package/src/resources/extensions/gsd/tests/reactive-executor.test.ts +39 -0
  459. package/src/resources/extensions/gsd/tests/register-hooks-loop-guard-auto.test.ts +105 -0
  460. package/src/resources/extensions/gsd/tests/service-tier.test.ts +4 -0
  461. package/src/resources/extensions/gsd/tests/single-writer-v3-tool-surface.test.ts +33 -7
  462. package/src/resources/extensions/gsd/tests/skill-activation.test.ts +20 -16
  463. package/src/resources/extensions/gsd/tests/state-reconciliation-drift.test.ts +52 -0
  464. package/src/resources/extensions/gsd/tests/stop-notice.test.ts +21 -1
  465. package/src/resources/extensions/gsd/tests/system-context-message-routing.test.ts +13 -7
  466. package/src/resources/extensions/gsd/tests/terminated-transient.test.ts +14 -0
  467. package/src/resources/extensions/gsd/tests/thinking-level-resolution.test.ts +1 -1
  468. package/src/resources/extensions/gsd/tests/tool-invocation-error-loop-break.test.ts +19 -0
  469. package/src/resources/extensions/gsd/tests/tool-naming.test.ts +8 -0
  470. package/src/resources/extensions/gsd/tests/unit-context-composer.test.ts +39 -0
  471. package/src/resources/extensions/gsd/tests/validate-milestone-prompt-verification-classes.test.ts +7 -0
  472. package/src/resources/extensions/gsd/tests/verification-evidence.test.ts +12 -0
  473. package/src/resources/extensions/gsd/tests/workflow-authority-faults.test.ts +238 -0
  474. package/src/resources/extensions/gsd/tests/workflow-authority-fixture.test.ts +48 -0
  475. package/src/resources/extensions/gsd/tests/workflow-authority-fixture.ts +141 -0
  476. package/src/resources/extensions/gsd/tests/workflow-authority-projection-conflict.test.ts +177 -0
  477. package/src/resources/extensions/gsd/tests/workflow-fault-harness.test.ts +39 -0
  478. package/src/resources/extensions/gsd/tests/workflow-fault-harness.ts +72 -0
  479. package/src/resources/extensions/gsd/tests/workflow-projections.test.ts +4 -4
  480. package/src/resources/extensions/gsd/tests/worktree-state-projection.test.ts +2 -2
  481. package/src/resources/extensions/gsd/tests/worktree-sync-milestones.test.ts +60 -0
  482. package/src/resources/extensions/gsd/tests/write-gate-planning-unit.test.ts +74 -0
  483. package/src/resources/extensions/gsd/tools/complete-task.ts +20 -5
  484. package/src/resources/extensions/gsd/tools/plan-slice.ts +5 -1
  485. package/src/resources/extensions/gsd/tools/reopen-milestone.ts +24 -5
  486. package/src/resources/extensions/gsd/tools/reopen-slice.ts +25 -5
  487. package/src/resources/extensions/gsd/tools/reopen-task.ts +23 -9
  488. package/src/resources/extensions/gsd/unit-context-composer.ts +34 -8
  489. package/src/resources/extensions/gsd/verification-evidence.ts +5 -2
  490. package/src/resources/extensions/gsd/workflow-projections.ts +2 -3
  491. package/src/resources/extensions/gsd/workspace.ts +2 -1
  492. package/src/resources/extensions/gsd/worktree-state-projection.ts +21 -24
  493. package/src/resources/extensions/ollama/ollama-chat-provider.ts +2 -2
  494. package/src/resources/extensions/ollama/tests/ollama-chat-provider-think.test.ts +4 -0
  495. package/src/resources/extensions/shared/interview-ui.ts +47 -8
  496. package/src/resources/extensions/shared/tests/interview-ui-border.test.ts +29 -0
  497. package/src/resources/extensions/subagent/index.ts +1 -1
  498. package/dist/web/standalone/.next/static/chunks/app/_not-found/page-49f565245e1e4afe.js +0 -1
  499. package/dist/web/standalone/.next/static/chunks/app/page-bc4c723c6a19c6f0.js +0 -1
  500. package/dist/web/standalone/.next/static/chunks/next/dist/client/components/builtin/global-error-ec3eaffc9785ba48.js +0 -1
  501. /package/dist/web/standalone/.next/static/{BE5XoklToI6lZjRzpXih8 → xwUaOqui04jIIFVsIBLiL}/_buildManifest.js +0 -0
  502. /package/dist/web/standalone/.next/static/{BE5XoklToI6lZjRzpXih8 → xwUaOqui04jIIFVsIBLiL}/_ssgManifest.js +0 -0
@@ -6,8 +6,8 @@
6
6
  // (drift to import). gsd-core is oblivious to this file and ignores it.
7
7
 
8
8
  import { createHash } from "node:crypto";
9
- import { existsSync, readFileSync, renameSync, unlinkSync, writeFileSync, mkdirSync } from "node:fs";
10
- import { dirname, join, isAbsolute, normalize } from "node:path";
9
+ import { existsSync, readFileSync, realpathSync, renameSync, unlinkSync, writeFileSync, mkdirSync } from "node:fs";
10
+ import { dirname, join, isAbsolute, normalize, relative, resolve } from "node:path";
11
11
 
12
12
  /** Current marker schema version. Bump on breaking format changes + migrate. */
13
13
  export const COMPAT_MARKER_SCHEMA = 2;
@@ -54,6 +54,13 @@ export interface CompatMarker {
54
54
  piVersion: string;
55
55
  }
56
56
 
57
+ export interface ReadCompatMarkerOptions {
58
+ /** Rewrite invalid-but-derivable marker keys back to safe root-relative keys. */
59
+ healInvalidKeys?: boolean;
60
+ /** Quarantine malformed/unsafe markers. Disable only for read-only previews. */
61
+ quarantineInvalid?: boolean;
62
+ }
63
+
57
64
  /** Marker returned when no marker exists yet (fresh project, first gsd-pi run). */
58
65
  export const EMPTY_MARKER: CompatMarker = {
59
66
  schema: COMPAT_MARKER_SCHEMA,
@@ -81,6 +88,37 @@ export function computeProjectionSha(content: string): string {
81
88
  return createHash("sha256").update(normalizeForHash(content)).digest("hex").slice(0, 16);
82
89
  }
83
90
 
91
+ function normalizeRealPath(p: string): string {
92
+ try {
93
+ return realpathSync.native(p);
94
+ } catch {
95
+ return resolve(p);
96
+ }
97
+ }
98
+
99
+ function rootRelativeKey(rootPath: string, absPath: string): string | null {
100
+ const root = normalizeRealPath(rootPath);
101
+ const abs = normalizeRealPath(absPath);
102
+ const rel = relative(root, abs);
103
+ if (!rel || rel === ".." || rel.startsWith(`..${sepForRelative(rel)}`) || isAbsolute(rel)) {
104
+ return null;
105
+ }
106
+ return rel.replace(/\\/g, "/");
107
+ }
108
+
109
+ function sepForRelative(rel: string): string {
110
+ return rel.includes("\\") ? "\\" : "/";
111
+ }
112
+
113
+ export function deriveCompatProjectionKey(absPath: string, roots: readonly string[]): string {
114
+ for (const root of roots) {
115
+ const key = rootRelativeKey(root, absPath);
116
+ if (key) return key;
117
+ }
118
+ const fallbackRoot = roots[roots.length - 1] ?? dirname(absPath);
119
+ return relative(fallbackRoot, absPath).replace(/\\/g, "/");
120
+ }
121
+
84
122
  /**
85
123
  * Read & validate the marker. A missing marker → EMPTY_MARKER (treat every
86
124
  * projection as external on next reconcile). A malformed marker is quarantined
@@ -88,7 +126,12 @@ export function computeProjectionSha(content: string): string {
88
126
  * EMPTY_MARKER. A schema-mismatch returns EMPTY_MARKER (forward-compat: refuse
89
127
  * to act on a future format we don't understand).
90
128
  */
91
- export function readCompatMarker(basePath: string): CompatMarker {
129
+ export function readCompatMarker(
130
+ basePath: string,
131
+ options: ReadCompatMarkerOptions = {},
132
+ ): CompatMarker {
133
+ const healInvalidKeys = options.healInvalidKeys ?? true;
134
+ const quarantineInvalid = options.quarantineInvalid ?? true;
92
135
  const path = compatMarkerPath(basePath);
93
136
  if (!existsSync(path)) return emptyMarker();
94
137
 
@@ -103,12 +146,16 @@ export function readCompatMarker(basePath: string): CompatMarker {
103
146
  try {
104
147
  parsed = JSON.parse(raw);
105
148
  } catch {
106
- quarantine(basePath, raw);
149
+ if (quarantineInvalid) quarantine(basePath, raw);
107
150
  return emptyMarker();
108
151
  }
109
152
 
153
+ if (healInvalidKeys && healMarkerProjectionKeys(basePath, parsed) && isValidMarker(parsed)) {
154
+ writeCompatMarker(basePath, parsed);
155
+ }
156
+
110
157
  if (!isValidMarker(parsed)) {
111
- quarantine(basePath, raw);
158
+ if (quarantineInvalid) quarantine(basePath, raw);
112
159
  return emptyMarker();
113
160
  }
114
161
  // Promote older markers by defaulting absent fields. Schema 1 → 2 only adds
@@ -245,6 +292,53 @@ function isValidProjectionMap(x: unknown): boolean {
245
292
  return true;
246
293
  }
247
294
 
295
+ function healMarkerProjectionKeys(basePath: string, marker: unknown): marker is CompatMarker {
296
+ if (typeof marker !== "object" || marker === null) return false;
297
+ const m = marker as Record<string, unknown>;
298
+ let changed = false;
299
+
300
+ if (typeof m.projections === "object" && m.projections !== null) {
301
+ changed = healProjectionMapKeys(basePath, ".gsd", m.projections as Record<string, ProjectionEntry>) || changed;
302
+ }
303
+
304
+ const planning = m.planning;
305
+ if (typeof planning === "object" && planning !== null) {
306
+ const p = planning as Record<string, unknown>;
307
+ if (typeof p.projections === "object" && p.projections !== null) {
308
+ changed = healProjectionMapKeys(basePath, ".planning", p.projections as Record<string, ProjectionEntry>) || changed;
309
+ }
310
+ if (typeof p.passthrough === "object" && p.passthrough !== null) {
311
+ changed = healProjectionMapKeys(basePath, ".planning", p.passthrough as Record<string, ProjectionEntry>) || changed;
312
+ }
313
+ }
314
+
315
+ return changed;
316
+ }
317
+
318
+ function healProjectionMapKeys(
319
+ basePath: string,
320
+ rootName: ".gsd" | ".planning",
321
+ map: Record<string, ProjectionEntry>,
322
+ ): boolean {
323
+ let changed = false;
324
+ const root = join(basePath, rootName);
325
+
326
+ for (const key of Object.keys(map)) {
327
+ if (isSafeProjectionKey(key)) continue;
328
+ if (key.includes("\0") || isAbsolute(key) || /^[A-Za-z]:/.test(key)) continue;
329
+ const healedKey = rootRelativeKey(root, resolve(root, key));
330
+ if (!healedKey || !isSafeProjectionKey(healedKey)) continue;
331
+
332
+ if (!map[healedKey]) {
333
+ map[healedKey] = map[key]!;
334
+ }
335
+ delete map[key];
336
+ changed = true;
337
+ }
338
+
339
+ return changed;
340
+ }
341
+
248
342
  function isValidPlanningMarker(x: unknown): x is PlanningMarker {
249
343
  if (typeof x !== "object" || x === null) return false;
250
344
  const p = x as Record<string, unknown>;
@@ -31,36 +31,37 @@ export const CACHE_MAX = 50;
31
31
  * By scoping tools to this allowlist during discuss dispatches, the grammar
32
32
  * sent to the provider stays well under provider limits.
33
33
  *
34
- * Included tools and why:
34
+ * Included tools and why (canonical names only — aliases are no longer
35
+ * advertised to models by default, see plan 035):
35
36
  * - gsd_summary_save: writes CONTEXT.md artifacts (all discuss prompts)
36
- * - gsd_save_summary: alias for above
37
37
  * - gsd_decision_save: records decisions (discuss.md output phase)
38
- * - gsd_save_decision: alias for above
39
38
  * - gsd_plan_milestone: writes roadmap (discuss.md single/multi milestone)
40
- * - gsd_milestone_plan: alias for above
41
39
  * - gsd_milestone_generate_id: generates milestone IDs (discuss.md multi-milestone)
42
- * - gsd_generate_milestone_id: alias for above
43
40
  * - gsd_requirement_save: creates requirements during discuss
44
- * - gsd_save_requirement: alias for above
45
41
  * - gsd_requirement_update: updates requirements during discuss
46
- * - gsd_update_requirement: alias for above
47
42
  */
48
43
  export const DISCUSS_TOOLS_ALLOWLIST: readonly string[] = [
49
44
  // Context / summary writing
50
45
  "gsd_summary_save",
51
- "gsd_save_summary",
52
46
  // Decision recording
53
47
  "gsd_decision_save",
54
- "gsd_save_decision",
55
48
  // Milestone planning (needed for discuss.md output phase)
56
49
  "gsd_plan_milestone",
57
- "gsd_milestone_plan",
58
50
  // Milestone ID generation (multi-milestone flow)
59
51
  "gsd_milestone_generate_id",
60
- "gsd_generate_milestone_id",
61
52
  // Requirement updates
62
53
  "gsd_requirement_save",
63
- "gsd_save_requirement",
64
54
  "gsd_requirement_update",
65
- "gsd_update_requirement",
66
55
  ];
56
+
57
+ // ─── Context Injection ────────────────────────────────────────────────────────
58
+
59
+ /**
60
+ * Leading marker stamped on every buildContextMessage() output
61
+ * (bootstrap/system-context.ts). Single source of truth for the producer and
62
+ * the consumer: the provider payload policy (filterSupersededContextInjections
63
+ * in context-masker.ts) matches this prefix to find and dedupe injected
64
+ * memory/guided/forensics messages after convertToLlm strips their customType.
65
+ * Both sides import it from here — do not inline the literal in either module.
66
+ */
67
+ export const GSD_CONTEXT_MESSAGE_SENTINEL = "[GSD Context Injection]";
@@ -1,3 +1,5 @@
1
+ import { GSD_CONTEXT_MESSAGE_SENTINEL } from "./constants.js";
2
+
1
3
  /**
2
4
  * Observation masking for GSD auto-mode sessions.
3
5
  *
@@ -75,6 +77,26 @@ function findTurnBoundary(messages: MaskableMessage[], keepRecentTurns: number):
75
77
  return 0;
76
78
  }
77
79
 
80
+ function countRealUserTurns(messages: MaskableMessage[]): number {
81
+ let count = 0;
82
+ for (const m of messages) {
83
+ if (m.role === "user" && !isBashResultUserMessage(m)) count++;
84
+ }
85
+ return count;
86
+ }
87
+
88
+ /**
89
+ * Quantizes how many recent turns stay unmasked so the boundary only moves
90
+ * forward once per full block of `keepRecentTurns` new turns, instead of
91
+ * every turn. Keeps the masked prefix byte-stable within a block, which is
92
+ * what lets the LLM provider's prompt cache survive across turns.
93
+ */
94
+ function quantizedKeepTurns(totalTurns: number, keepRecentTurns: number): number {
95
+ if (keepRecentTurns <= 0 || totalTurns < 2 * keepRecentTurns) return totalTurns;
96
+ const maskedTurns = Math.floor((totalTurns - keepRecentTurns) / keepRecentTurns) * keepRecentTurns;
97
+ return totalTurns - maskedTurns;
98
+ }
99
+
78
100
  /**
79
101
  * Detect user messages that originated from bashExecution.
80
102
  * After convertToLlm, these are {role: "user", content: [{type:"text", text:"Ran `cmd`\n..."}]}.
@@ -95,7 +117,8 @@ function isMaskableMessage(m: MaskableMessage): boolean {
95
117
 
96
118
  export function createObservationMask(keepRecentTurns: number = 8) {
97
119
  return (messages: MaskableMessage[]): MaskableMessage[] => {
98
- const boundary = findTurnBoundary(messages, keepRecentTurns);
120
+ const totalTurns = countRealUserTurns(messages);
121
+ const boundary = findTurnBoundary(messages, quantizedKeepTurns(totalTurns, keepRecentTurns));
99
122
  if (boundary === 0) return messages;
100
123
 
101
124
  return messages.map((m, i) => {
@@ -126,6 +149,14 @@ function findResponsesTurnBoundary(items: ResponsesInputItem[], keepRecentTurns:
126
149
  return 0;
127
150
  }
128
151
 
152
+ function countResponsesRealUserTurns(items: ResponsesInputItem[]): number {
153
+ let count = 0;
154
+ for (const item of items) {
155
+ if (item.role === "user" && !isResponsesBashResultUserItem(item)) count++;
156
+ }
157
+ return count;
158
+ }
159
+
129
160
  /**
130
161
  * Observation masking for OpenAI/Codex Responses API payloads.
131
162
  *
@@ -135,7 +166,8 @@ function findResponsesTurnBoundary(items: ResponsesInputItem[], keepRecentTurns:
135
166
  */
136
167
  export function createResponsesInputObservationMask(keepRecentTurns: number = 8) {
137
168
  return (items: ResponsesInputItem[]): ResponsesInputItem[] => {
138
- const boundary = findResponsesTurnBoundary(items, keepRecentTurns);
169
+ const totalTurns = countResponsesRealUserTurns(items);
170
+ const boundary = findResponsesTurnBoundary(items, quantizedKeepTurns(totalTurns, keepRecentTurns));
139
171
  if (boundary === 0) return items;
140
172
 
141
173
  return items.map((item, i) => {
@@ -219,3 +251,67 @@ export function truncateResponsesInputResultItems(items: ResponsesInputItem[], m
219
251
  return item;
220
252
  });
221
253
  }
254
+
255
+ // GSD injects at most one context message per turn (memory/guided/forensics —
256
+ // see buildContextMessage in bootstrap/system-context.ts), marked with
257
+ // GSD_CONTEXT_MESSAGE_SENTINEL. convertToLlm strips the distinguishing
258
+ // customType before the payload reaches this hook, so detection is by content
259
+ // prefix instead. Pre-sentinel session history lacks the sentinel, so we also
260
+ // match the stable *bracketed* GSD block labels those injections begin with,
261
+ // letting resumed sessions dedupe older memory/guided blocks.
262
+ //
263
+ // Only GSD-specific bracketed markers belong here — never generic prose. A
264
+ // message is dropped from the payload when it matches, so a natural-language
265
+ // prefix (e.g. the forensics prompt "Debug GSD itself.") could silently delete
266
+ // a real user message that happens to start the same way. Legacy forensics
267
+ // injections without memory are therefore left un-deduped by design; those
268
+ // with memory still match via the "[GSD Context Metadata]" wrapper.
269
+ const LEGACY_GSD_CONTEXT_INJECTION_PREFIXES = [
270
+ "[GSD Context Metadata]",
271
+ "[MEMORY — Critical and prompt-relevant memories from the GSD memory store]",
272
+ "[GSD Guided Execute Context]",
273
+ ] as const;
274
+
275
+ function isGsdContextInjectionText(text: string | undefined): boolean {
276
+ if (typeof text !== "string") return false;
277
+ if (text.startsWith(GSD_CONTEXT_MESSAGE_SENTINEL)) return true;
278
+ return LEGACY_GSD_CONTEXT_INJECTION_PREFIXES.some((prefix) => text.startsWith(prefix));
279
+ }
280
+
281
+ function isGsdContextInjectionMessage(m: MaskableMessage): boolean {
282
+ if (m.role !== "user") return false;
283
+ return isGsdContextInjectionText(firstTextFromContent(m.content));
284
+ }
285
+
286
+ /**
287
+ * Removes every GSD context-injection user message except the latest one.
288
+ * Each turn re-injects a near-identical memory/guided/forensics block; left
289
+ * in place they duplicate verbatim across an N-turn session. Removal (not
290
+ * masking) — an empty placeholder still costs tokens and shifts message
291
+ * positions, breaking cache byte-stability. Pure function: stored history is
292
+ * never mutated, only the outgoing payload array.
293
+ */
294
+ export function filterSupersededContextInjections(messages: MaskableMessage[]): MaskableMessage[] {
295
+ let lastIndex = -1;
296
+ for (let i = 0; i < messages.length; i++) {
297
+ if (isGsdContextInjectionMessage(messages[i])) lastIndex = i;
298
+ }
299
+ if (lastIndex === -1) return messages;
300
+ return messages.filter((m, i) => i === lastIndex || !isGsdContextInjectionMessage(m));
301
+ }
302
+
303
+ function isResponsesGsdContextInjectionItem(item: ResponsesInputItem): boolean {
304
+ if (item.role !== "user") return false;
305
+ return isGsdContextInjectionText(firstTextFromContent(item.content));
306
+ }
307
+
308
+ export function filterSupersededResponsesContextInjections(
309
+ items: ResponsesInputItem[],
310
+ ): ResponsesInputItem[] {
311
+ let lastIndex = -1;
312
+ for (let i = 0; i < items.length; i++) {
313
+ if (isResponsesGsdContextInjectionItem(items[i])) lastIndex = i;
314
+ }
315
+ if (lastIndex === -1) return items;
316
+ return items.filter((item, i) => i === lastIndex || !isResponsesGsdContextInjectionItem(item));
317
+ }
@@ -10,7 +10,7 @@
10
10
  // writer layer.
11
11
  import { createRequire } from "node:module";
12
12
  import { existsSync, copyFileSync, mkdirSync, realpathSync } from "node:fs";
13
- import { dirname, join } from "node:path";
13
+ import { dirname, join, resolve } from "node:path";
14
14
  import { GSDError, GSD_STALE_STATE } from "../errors.js";
15
15
  import type { GsdWorkspace, MilestoneScope } from "../workspace.js";
16
16
  import { logError, logWarning } from "../workflow-logger.js";
@@ -71,7 +71,10 @@ let _gsdRequire: ReturnType<typeof createRequire> | null | undefined;
71
71
  function getGsdRequire(): ReturnType<typeof createRequire> | null {
72
72
  if (_gsdRequire !== undefined) return _gsdRequire;
73
73
  try {
74
- _gsdRequire = createRequire(import.meta.url);
74
+ // Next.js may emit this module into a CommonJS chunk. Avoid ESM-only module
75
+ // metadata syntax here; it is a hard parse error there.
76
+ const packageRoot = process.env.GSD_WEB_PACKAGE_ROOT || process.env.GSD_PKG_ROOT || process.cwd();
77
+ _gsdRequire = createRequire(resolve(packageRoot, "package.json"));
75
78
  } catch {
76
79
  _gsdRequire = null;
77
80
  }
@@ -124,11 +124,11 @@ Diagnostics record the file path, scope (global/project), severity (error/warnin
124
124
  - `validation` — used for gate evaluation, roadmap reassessment, milestone validation, and doc rewrites. Falls back to `planning` if unset.
125
125
  - `uat` — used for UAT runs. Falls back to `completion` if unset.
126
126
 
127
- - `thinking`: per-phase reasoning effort (ADR-026), separate from `models`. Same phase keys as `models`. Values: `off`, `minimal`, `low`, `medium`, `high`, `xhigh`. Thinking travels with the model — model choice and reasoning effort are independent controls, so you can run one model across phases at different reasoning levels.
127
+ - `thinking`: per-phase reasoning effort (ADR-026), separate from `models`. Same phase keys as `models`. Values: `off`, `minimal`, `low`, `medium`, `high`, `xhigh`, `max`. Thinking travels with the model — model choice and reasoning effort are independent controls, so you can run one model across phases at different reasoning levels.
128
128
  - Two equivalent ways to set it: inline as `models.<phase>.thinking`, or as a separate `thinking:` block keyed by phase. For the same phase, the inline value wins over the block; project preferences win over global; a phase that's unset inherits via the same sibling chain as `models` (e.g. `discuss → planning`).
129
129
  - If no thinking is configured for a phase, the session level (set via `/model`) is used, exactly as before — this is fully backward-compatible.
130
130
  - `execute-task` (code-writing) has a measured reasoning floor of `medium`: at lower levels models stop planning edits and thrash on file re-reads. The floor applies to the session/default path. An **explicit** `execution` thinking level bypasses the floor and is honored verbatim (with a one-time advisory) — set `execution: low` deliberately if you want it.
131
- - Levels a model can't support are clamped to the nearest supported level at dispatch and never sent to the provider, so a model/level mismatch never fails a unit mid-run. `xhigh` support is model-dependent; current examples include OpenAI Codex Max models and Claude Sonnet 5 on Amazon Bedrock.
131
+ - Levels a model can't support are clamped to the nearest supported level at dispatch and never sent to the provider, so a model/level mismatch never fails a unit mid-run. `xhigh` and `max` support are model-dependent; current examples include `gpt-5.6-sol`, `gpt-5.6-terra`, and `gpt-5.6-luna`, which advertise both.
132
132
 
133
133
  - `skill_staleness_days`: number — skills unused for this many days get deprioritized during discovery. Set to `0` to disable staleness tracking. Default: `60`.
134
134
 
@@ -357,6 +357,37 @@ In `"parent"` mode, slice/task `targetRepositories` default to the declared chil
357
357
  - `"replace"` requires `prompt`.
358
358
  - `"skip"` is valid with no additional fields.
359
359
 
360
+ - `planning_subagent_registry`: object — optional classification for custom planning agents. Each key is an agent id and must set:
361
+ - `read_only_specialist`: boolean — set to `true` to classify the custom agent as safe for planning dispatch.
362
+
363
+ - `planning_subagents`: object — project-local widening for read-only planning subagent dispatch. Supported unit keys are `plan-milestone` and `plan-slice`; each has:
364
+ - `allowed`: string[] — additional planning-safe agents for that unit.
365
+
366
+ Agents must be built-in read-only planning specialists (`mnemo`, `scout`, `planner`, `reviewer`, `security`, or `tester`) or registered in `planning_subagent_registry` with `read_only_specialist: true`. The write gate still blocks source writes outside `.gsd/**`, unrestricted bash, stale subagent calls without agent identities, agents outside the read-only registry, and agents not listed for the active unit.
367
+
368
+ Example:
369
+
370
+ ```yaml
371
+ planning_subagent_registry:
372
+ my-custom-planner:
373
+ read_only_specialist: true
374
+
375
+ planning_subagents:
376
+ plan-milestone:
377
+ allowed:
378
+ - scout
379
+ - planner
380
+ - my-custom-planner
381
+ - security
382
+ plan-slice:
383
+ allowed:
384
+ - scout
385
+ - planner
386
+ - my-custom-planner
387
+ - reviewer
388
+ - security
389
+ ```
390
+
360
391
  **Known unit types for `before`/`after`:** `research-milestone`, `plan-milestone`, `research-slice`, `plan-slice`, `execute-task`, `complete-slice`, `replan-slice`, `reassess-roadmap`, `run-uat`.
361
392
 
362
393
  - `experimental`: opt-in experimental features. All features here are **off by default** — you must explicitly set each one to `true` to enable it. Features in this block may change or be removed without a deprecation cycle while in experimental status. Keys:
@@ -15,13 +15,13 @@ import {
15
15
  import { MEMORIES_FTS_REBUILT_KEY } from "./db-memory-fts-schema.js";
16
16
  import { isAfter, latestExplicitReopenAt } from "./milestone-reopen-events.js";
17
17
  import {
18
- buildTaskFileName,
19
18
  gsdProjectionRoot,
20
19
  gsdRoot,
21
20
  resolveGsdPathContract,
22
21
  resolveMilestoneFile,
23
22
  resolveMilestonePath,
24
23
  resolveSliceFile,
24
+ resolveTaskFile,
25
25
  } from "./paths.js";
26
26
  import { deriveState } from "./state.js";
27
27
  import { isClosedStatus } from "./status-guards.js";
@@ -32,6 +32,7 @@ import { parseRoadmapSlices } from "./roadmap-slices.js";
32
32
  import { parsePlan } from "./parsers-legacy.js";
33
33
  import { parseSummary } from "./files.js";
34
34
  import { LAYOUT_SEGMENTS } from "./layout-policy.js";
35
+ import { resolveCanonicalMilestoneRoot } from "./worktree-manager.js";
35
36
 
36
37
  const USER_AUTHORED_ARTIFACT_TYPES = new Set(["CONTEXT", "RESEARCH"]);
37
38
 
@@ -95,9 +96,29 @@ function reportCheckboxDbStatusDivergence(
95
96
  });
96
97
  }
97
98
 
99
+ function bareDuplicateMilestoneId(milestoneId: string): string | null {
100
+ const match = milestoneId.match(/^(M\d{3})-[a-z0-9]{6}$/i);
101
+ return match?.[1]?.toUpperCase() ?? null;
102
+ }
103
+
104
+ function checkboxDbStatusMilestoneIds(basePath: string, milestoneIds: string[]): string[] {
105
+ if (!hasFlatPhaseLayout(basePath)) return milestoneIds;
106
+
107
+ // Flat-phase directories are keyed by phase number. A restored
108
+ // M003-xxxxxx row alongside M003 is a stale duplicate for the same
109
+ // phases/03-* projection, so checking both can compare DB status against two
110
+ // competing PLAN files and report a persistent false divergence.
111
+ const allIds = new Set(milestoneIds.map((id) => id.toUpperCase()));
112
+ return milestoneIds.filter((milestoneId) => {
113
+ const bareId = bareDuplicateMilestoneId(milestoneId);
114
+ return !bareId || !allIds.has(bareId);
115
+ });
116
+ }
117
+
98
118
  function checkProjectionCheckboxDbStatus(basePath: string, milestoneIds: string[], issues: DoctorIssue[]): void {
99
119
  for (const milestoneId of milestoneIds) {
100
- const roadmapPath = resolveMilestoneFile(basePath, milestoneId, "ROADMAP");
120
+ const artifactBasePath = resolveCanonicalMilestoneRoot(basePath, milestoneId);
121
+ const roadmapPath = resolveMilestoneFile(artifactBasePath, milestoneId, "ROADMAP");
101
122
  const slices = getMilestoneSlices(milestoneId);
102
123
 
103
124
  if (roadmapPath && existsSync(roadmapPath)) {
@@ -123,7 +144,7 @@ function checkProjectionCheckboxDbStatus(basePath: string, milestoneIds: string[
123
144
  }
124
145
 
125
146
  for (const slice of slices) {
126
- const planPath = resolveSliceFile(basePath, milestoneId, slice.id, "PLAN");
147
+ const planPath = resolveSliceFile(artifactBasePath, milestoneId, slice.id, "PLAN");
127
148
  if (!planPath || !existsSync(planPath)) continue;
128
149
  try {
129
150
  const plan = readFileSync(planPath, "utf-8");
@@ -173,7 +194,7 @@ function artifactExistsOnDisk(basePath: string, artifactPath: string, row?: Arti
173
194
  return resolveArtifactDiskPath(basePath, artifactPath, row) !== null;
174
195
  }
175
196
 
176
- function resolveLiteralArtifactDiskPath(basePath: string, artifactPath: string): string | null {
197
+ function resolveLiteralArtifactDiskPath(basePath: string, artifactPath: string, row?: ArtifactRow): string | null {
177
198
  const relativeArtifactPath = artifactPathRelativeToGsd(artifactPath);
178
199
  if (isAbsolute(relativeArtifactPath)) {
179
200
  return existsSync(relativeArtifactPath) ? relativeArtifactPath : null;
@@ -182,6 +203,15 @@ function resolveLiteralArtifactDiskPath(basePath: string, artifactPath: string):
182
203
  const candidate = join(root, relativeArtifactPath);
183
204
  if (isPathInside(root, candidate) && existsSync(candidate)) return candidate;
184
205
  }
206
+ if (row?.milestone_id) {
207
+ const artifactBasePath = resolveCanonicalMilestoneRoot(basePath, row.milestone_id);
208
+ if (artifactBasePath !== basePath) {
209
+ for (const root of [gsdProjectionRoot(artifactBasePath), gsdRoot(artifactBasePath)]) {
210
+ const candidate = join(root, relativeArtifactPath);
211
+ if (isPathInside(root, candidate) && existsSync(candidate)) return candidate;
212
+ }
213
+ }
214
+ }
185
215
  return null;
186
216
  }
187
217
 
@@ -208,10 +238,8 @@ function resolveFlatPhaseArtifactDiskPath(basePath: string, row: ArtifactRow): s
208
238
  if (!artifactType) return null;
209
239
 
210
240
  if (row.slice_id && row.task_id) {
211
- const phaseDir = resolveMilestonePath(basePath, row.milestone_id);
212
- if (!phaseDir || !isFlatPhaseDiskPath(basePath, phaseDir)) return null;
213
- const candidate = join(phaseDir, buildTaskFileName(row.task_id, artifactType));
214
- return existsSync(candidate) ? candidate : null;
241
+ const candidate = resolveTaskFile(basePath, row.milestone_id, row.slice_id, row.task_id, artifactType);
242
+ return candidate && isFlatPhaseDiskPath(basePath, candidate) ? candidate : null;
215
243
  }
216
244
 
217
245
  const candidate = row.slice_id
@@ -226,7 +254,7 @@ function resolveArtifactDiskPath(basePath: string, artifactPath: string, row?: A
226
254
  const flatPath = resolveFlatPhaseArtifactDiskPath(basePath, row);
227
255
  if (flatPath) return flatPath;
228
256
  }
229
- return resolveLiteralArtifactDiskPath(basePath, artifactPath);
257
+ return resolveLiteralArtifactDiskPath(basePath, artifactPath, row);
230
258
  }
231
259
 
232
260
  function taskExistsInPlan(basePath: string, milestoneId: string, sliceId: string, taskId: string): boolean {
@@ -436,7 +464,9 @@ function staleMilestonesArtifactRowFixable(basePath: string, row: ArtifactRow, a
436
464
 
437
465
  function stalePhasesArtifactRowFixable(basePath: string, row: ArtifactRow, artifactRows: ArtifactRow[]): boolean {
438
466
  const issuePath = artifactPathRelativeToGsd(row.path);
439
- return issuePath.startsWith(`${LAYOUT_SEGMENTS.level1}/`) && hasPresentMilestonesReplacement(basePath, row, artifactRows);
467
+ return issuePath.startsWith(`${LAYOUT_SEGMENTS.level1}/`) &&
468
+ (hasPresentFlatPhaseReplacement(basePath, row, artifactRows) ||
469
+ hasPresentMilestonesReplacement(basePath, row, artifactRows));
440
470
  }
441
471
 
442
472
  function staleArtifactRowFixable(basePath: string, row: ArtifactRow, artifactRows: ArtifactRow[]): boolean {
@@ -809,7 +839,11 @@ export async function checkEngineHealth(
809
839
  // block doctor.
810
840
  try {
811
841
  if (isDbAvailable()) {
812
- checkProjectionCheckboxDbStatus(basePath, getAllMilestones().map((milestone) => milestone.id), issues);
842
+ checkProjectionCheckboxDbStatus(
843
+ basePath,
844
+ checkboxDbStatusMilestoneIds(basePath, getAllMilestones().map((milestone) => milestone.id)),
845
+ issues,
846
+ );
813
847
  }
814
848
  } catch {
815
849
  // Non-fatal: checkbox-vs-DB divergence check must never block doctor
@@ -65,8 +65,12 @@ const NETWORK_RE = /network|ECONNRESET|ETIMEDOUT|ECONNREFUSED|socket hang up|web
65
65
  // transient errors so auto-mode can retry with reduced budget or fall back to a larger-context model.
66
66
  // See: https://github.com/open-gsd/gsd-pi/issues/4528
67
67
  const SERVER_RE = /internal(?: server)?[ _-]?error|server[ _-]?error|500|502|503|overloaded|server_error|api_error|service.?unavailable|context (?:window|length) exceed|context window exceed/i;
68
+ // Provider adapters throw this when an upstream stop reason reaches Pi before
69
+ // the adapter knows how to map it. Keep the match scoped so plain "unknown"
70
+ // runtime errors still pause for inspection instead of being retried forever.
71
+ const PROVIDER_STOP_REASON_RE = /unhandled stop reason:\s*(?:abort|unknown)\b/i;
68
72
  // ECONNRESET/ECONNREFUSED are in NETWORK_RE (same-model retry first).
69
- const CONNECTION_RE = /terminated|connection.?(?:refused|error)|other side closed|EPIPE|network.?(?:is\s+)?unavailable|stream_exhausted(?:_without_result)?/i;
73
+ const CONNECTION_RE = /terminated|\babort\b|connection.?(?:refused|error)|other side closed|EPIPE|network.?(?:is\s+)?unavailable|stream_exhausted(?:_without_result)?/i;
70
74
  // Catch-all for V8 JSON.parse errors: all modern variants end with "in JSON at position \d+".
71
75
  // This eliminates the need to enumerate every error message variant individually.
72
76
  const STREAM_RE = /in JSON at position \d+|Unexpected end of JSON|SyntaxError.*JSON/i;
@@ -103,7 +107,7 @@ const UNSUPPORTED_MODEL_SCOPE_RE = /\b(?:account|plan|tier|subscription)\b/i;
103
107
  * 3. Network (ECONNRESET, ETIMEDOUT, socket hang up, fetch failed, dns)
104
108
  * 4. Stream truncation (malformed JSON from mid-stream cut)
105
109
  * 5. Server (500/502/503, overloaded, server_error)
106
- * 6. Connection (terminated, ECONNREFUSED, EPIPE, other side closed)
110
+ * 6. Connection (terminated, abort/unknown stop reason, ECONNREFUSED, EPIPE, other side closed)
107
111
  * 7. Model error (400 invalid argument / payload rejected for this model)
108
112
  * 8. Unknown
109
113
  */
@@ -163,6 +167,13 @@ export function classifyError(errorMsg: string, retryAfterMs?: number): ErrorCla
163
167
  return { kind: "rate-limit", retryAfterMs: delayMs };
164
168
  }
165
169
 
170
+ // Provider stop-reason adapter gaps are usually request/stream failures from
171
+ // the current model. Treat the scoped adapter message as fallback-eligible,
172
+ // but keep generic unknown errors on the indefinite pause path.
173
+ if (PROVIDER_STOP_REASON_RE.test(errorMsg)) {
174
+ return { kind: "connection", retryAfterMs: retryAfterMs ?? 15_000 };
175
+ }
176
+
166
177
  // 3. Network errors — same-model retry candidate
167
178
  if (NETWORK_RE.test(errorMsg)) {
168
179
  // Exclude if also matches permanent signals (already handled above for
@@ -111,6 +111,7 @@ export const EXEC_DEFAULTS = {
111
111
  "LOGNAME",
112
112
  "TMPDIR",
113
113
  "NODE_OPTIONS",
114
+ "NODE_PATH",
114
115
  "PYTHONPATH",
115
116
  "PYTHONIOENCODING",
116
117
  ] as const,
@@ -155,6 +155,19 @@ function isInsideFlatProjectionBackup(backupDir: string, src: string): boolean {
155
155
  return src === phaseBackupDir || src.startsWith(`${phaseBackupDir}/`) || src.startsWith(`${phaseBackupDir}\\`);
156
156
  }
157
157
 
158
+ function removeFlatProjectionBackup(backupDir: string): void {
159
+ const phaseBackupDir = join(backupDir, "__phases");
160
+ if (!existsSync(phaseBackupDir)) return;
161
+ try {
162
+ removePathWithRetries(phaseBackupDir);
163
+ } catch (err) {
164
+ logWarning(
165
+ "migration",
166
+ `flat-phase migration succeeded but could not remove temporary ${LAYOUT_SEGMENTS.level1}/ backup: ${(err as Error).message}`,
167
+ );
168
+ }
169
+ }
170
+
158
171
  function restoreFlatProjectionFromBackup(basePath: string, backupDir: string): void {
159
172
  const phaseBackupDir = join(backupDir, "__phases");
160
173
  if (!existsSync(phaseBackupDir)) return;
@@ -494,4 +507,5 @@ export async function migrateToFlatPhase(basePath: string): Promise<void> {
494
507
  `flat-phase migration succeeded but could not remove ${LEGACY_MIGRATING_SEGMENT}: ${(err as Error).message}`,
495
508
  );
496
509
  }
510
+ removeFlatProjectionBackup(backupDir);
497
511
  }
@@ -385,10 +385,16 @@ const runtimeFilesCleanedUpRepos = new Set<string>();
385
385
  // ─── Integration Branch Metadata ───────────────────────────────────────────
386
386
 
387
387
  /**
388
- * Path to the milestone metadata file that stores the integration branch.
389
- * Format: .gsd/milestones/<MID>/<MID>-META.json
388
+ * Path to the milestone integration-branch metadata file.
389
+ * Format: .gsd/<MID>-META.json (deliberately OUTSIDE milestones/<MID>/ so it
390
+ * cannot poison isLegacyMilestonesLayout — see ADR-045).
390
391
  */
391
- function milestoneMetaPath(basePath: string, milestoneId: string): string {
392
+ export function milestoneMetaPath(basePath: string, milestoneId: string): string {
393
+ return join(gsdRoot(basePath), `${milestoneId}-META.json`);
394
+ }
395
+
396
+ /** Legacy location (pre-ADR-045). Read-only fallback for existing on-disk trees. */
397
+ function legacyMilestoneMetaPath(basePath: string, milestoneId: string): string {
392
398
  return join(gsdRoot(basePath), "milestones", milestoneId, `${milestoneId}-META.json`);
393
399
  }
394
400
 
@@ -398,8 +404,12 @@ function milestoneMetaPath(basePath: string, milestoneId: string): string {
398
404
  */
399
405
  export function readIntegrationBranch(basePath: string, milestoneId: string): string | null {
400
406
  try {
401
- const metaFile = milestoneMetaPath(basePath, milestoneId);
402
- if (!existsSync(metaFile)) return null;
407
+ let metaFile = milestoneMetaPath(basePath, milestoneId);
408
+ if (!existsSync(metaFile)) {
409
+ const legacy = legacyMilestoneMetaPath(basePath, milestoneId);
410
+ if (!existsSync(legacy)) return null;
411
+ metaFile = legacy;
412
+ }
403
413
  const data = JSON.parse(readFileSync(metaFile, "utf-8"));
404
414
  const branch = data?.integrationBranch;
405
415
  if (typeof branch === "string" && branch.trim() !== "" && VALID_BRANCH_NAME.test(branch)) {
@@ -451,7 +461,10 @@ export function writeIntegrationBranch(
451
461
  if (existingBranch === branch) return;
452
462
 
453
463
  const metaFile = milestoneMetaPath(basePath, milestoneId);
454
- mkdirSync(join(gsdRoot(basePath), "milestones", milestoneId), { recursive: true });
464
+ // Ensure the flat .gsd/ root exists (never milestones/<MID>/ that would
465
+ // poison layout detection, ADR-045). In production .gsd/ already exists; this
466
+ // guards fresh trees so the write below can't ENOENT.
467
+ mkdirSync(gsdRoot(basePath), { recursive: true });
455
468
 
456
469
  // Merge with existing metadata if present
457
470
  let existing: Record<string, unknown> = {};