@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
@@ -14,7 +14,7 @@ All relevant context is preloaded below. Start immediately without re-reading th
14
14
 
15
15
  ## Already Planned? Soft Brake
16
16
 
17
- If `{{outputPath}}` exists with at least one slice line (e.g. `- [ ] **S01:`) AND `gsd_query` reports slice rows for this milestone, a prior `gsd_plan_milestone` call already persisted the plan. Do **not** re-call it; its UPSERT could overwrite existing planning. Skip to the ready phrase.
17
+ If `{{outputPath}}` exists with at least one slice line (e.g. `- [ ] **S01:`) AND `gsd_milestone_status` reports slice rows for this milestone, a prior `gsd_plan_milestone` call already persisted the plan. Do **not** re-call it; its UPSERT could overwrite existing planning. Skip to the ready phrase.
18
18
 
19
19
  If only the file or only DB rows exist, the prior write was incomplete; plan normally so the tool reconciles both.
20
20
 
@@ -44,7 +44,7 @@ If slice research is inlined, trust its architectural findings, but verify every
44
44
  6. Include Threat Surface (Q3), Requirement Impact (Q4), proof level, observability, integration closure, Failure Modes (Q5), Load Profile (Q6), and Negative Tests (Q7) only where applicable.
45
45
  7. Right-size tasks. Simple slices can be one task; split only when context, ownership, or verification boundaries justify it.
46
46
  8. Task `verify` commands must be safe, simple commands. Do not use shell pipes, redirects, semicolons, backticks, command substitution, output trimming, or grep regex alternation with `|`. If multiple checks are needed, create a small test file and run it with `node --test` or a package test script, or use separate simple commands joined only with `&&`. For absence checks, verify a pattern does not exist with `! grep -q 'pattern' file` or `! rg -q 'pattern' file`; do not use `grep -c` or `rg -c` to assert zero matches because count commands exit 1 when they find zero matches, and the verification gate treats that as failure.
47
- 9. Each task needs the exact `gsd_plan_slice.tasks[]` shape: `taskId`, `title`, `description`, `estimate`, `files`, `verify`, `inputs`, `expectedOutput`, and optional `observabilityImpact`. `description` should contain the Why / Do / Done-when narrative. `files`, `inputs`, and `expectedOutput` must be JSON arrays of strings, even when there is only one path (for example, `"inputs": ["src/index.ts"]`, never `"inputs": "src/index.ts"`). Use paths relative to `{{workingDirectory}}`; do not put absolute paths to the original checkout or any directory outside `{{workingDirectory}}` in `files`, `inputs`, `expectedOutput`, or verification commands. **`expectedOutput` must only list files the task actually creates or overwrites on disk.** Do NOT include files the task merely reads, verifies, or tests — those belong only in `inputs`. If a task is a pure verification or test task that produces no new files, `expectedOutput` may be `[]` or limited to test-result artifacts (e.g. a log or assertion output). A file that does not yet exist on disk and is needed as an `input` must be produced by an earlier task's `expectedOutput` — if no prior task creates it, add a task before this one that does.
47
+ 9. Each task needs the exact `gsd_plan_slice.tasks[]` shape: `taskId`, `title`, `description`, `estimate`, `files`, `verify`, `inputs`, `expectedOutput`, and optional `observabilityImpact`. `description` should contain the Why / Do / Done-when narrative. `files`, `inputs`, and `expectedOutput` must be JSON arrays of strings, even when there is only one path (for example, `"inputs": ["src/index.ts"]`, never `"inputs": "src/index.ts"`). Use paths relative to `{{workingDirectory}}`; do not put absolute paths to the original checkout or any directory outside `{{workingDirectory}}` in `files`, `inputs`, `expectedOutput`, or verification commands. **`expectedOutput` must only list files the task actually creates or overwrites on disk.** Do NOT include files the task merely reads, verifies, tests, or describes — those belong in `inputs`, `verify`, `description`, or slice success criteria. If a task is a pure verification or test task that produces no new files, `expectedOutput` must be `[]`; if it writes a test-result log or assertion output file, list only that concrete file path. A file that does not yet exist on disk and is needed as an `input` must be produced by an earlier task's `expectedOutput` — if no prior task creates it, add a task before this one that does.
48
48
  10. Persist with `gsd_plan_slice` using `milestoneId`, `sliceId`, `goal`, optional `successCriteria`/`proofLevel`/`integrationClosure`/`observabilityImpact`, and `tasks`. `gsd_plan_slice` handles task persistence transactionally and renders `{{outputPath}}` plus task plans; do not call `gsd_plan_task`. The DB-backed tool is the canonical write path. Do **not** rely on direct `PLAN.md` writes as the source of truth.
49
49
  11. Self-audit before finishing: goal/demo closure, requirement coverage, deliverable coverage audit (cross-check every file listed in CONTEXT.md `## Scope` / `### In Scope` against task `files` or `expectedOutput`), locked decisions, concrete paths, dependency order, wiring, scope size, proof truthfulness, feature completeness, and quality gates. Quality gates: non-trivial slices/tasks include specific Q3-Q7 coverage where applicable.
50
50
  12. If planning creates structural decisions, call `gsd_decision_save` for each; the tool persists the decision and regenerates `.gsd/DECISIONS.md`.
@@ -64,7 +64,7 @@ Then:
64
64
  2. {{skillActivation}} Record the installed skills you expect executors to use in each task plan's `skills_used` frontmatter.
65
65
  3. Define slice-level verification: the objective stopping condition. Plan real test files with real assertions; for simple slices, executable commands are fine.
66
66
  4. For non-trivial slices, plan observability / proof level / integration closure, threat surface, and requirement impact. Omit entirely for simple slices.
67
- 5. Decompose the slice into tasks that fit one context window each. Every task passed to `gsd_plan_slice` must use the exact keys `taskId`, `title`, `description`, `estimate`, `files`, `verify`, `inputs`, `expectedOutput`, and optional `observabilityImpact`. Put Why / Do / Done-when detail in `description`. `files`, `inputs`, and `expectedOutput` must be JSON arrays of strings, even for one path (for example, `"expectedOutput": ["src/index.ts"]`, never `"expectedOutput": "src/index.ts"`).
67
+ 5. Decompose the slice into tasks that fit one context window each. Every task passed to `gsd_plan_slice` must use the exact keys `taskId`, `title`, `description`, `estimate`, `files`, `verify`, `inputs`, `expectedOutput`, and optional `observabilityImpact`. Put Why / Do / Done-when detail in `description`. `files`, `inputs`, and `expectedOutput` must be JSON arrays of strings, even for one path (for example, `"expectedOutput": ["src/index.ts"]`, never `"expectedOutput": "src/index.ts"`). `expectedOutput` is path-only: list only files the task creates or overwrites, and use `[]` for pure verification tasks.
68
68
  6. **Persist planning state through `gsd_plan_slice`.** Call it with the full payload. The tool writes to the DB and renders `{{outputPath}}` and `{{slicePath}}/tasks/T##-PLAN.md` automatically. Do NOT rely on direct `PLAN.md` writes.
69
69
  7. **Self-audit the plan.** If every task were completed exactly as written, the slice goal/demo should be true. Every must-have maps to a task. Inputs and Expected Output are backtick-wrapped file paths.
70
70
  8. If refinement produced structural decisions that diverge from the sketch, call `gsd_decision_save` for each; the tool persists the decision and regenerates `.gsd/DECISIONS.md`.
@@ -27,7 +27,7 @@ You are the UAT runner. Execute every check defined in `{{uatPath}}` as deeply a
27
27
  ### Automation rules by mode
28
28
 
29
29
  - `artifact-driven` — verify with shell commands, scripts, file reads, and artifact structure checks.
30
- - `browser-executable` — use gsd-browser tools to navigate to the target URL and verify expected behavior. Prefer `mcp__gsd-browser__browser_*` tools when namespaced, or direct `browser_*` tools when surfaced without a namespace. Capture screenshots as evidence. Record pass/fail with specific assertions.
30
+ - `browser-executable` — use browser tools to navigate to the target URL and verify expected behavior. Prefer direct `browser_*` tools when available. Capture screenshots as evidence. Record pass/fail with specific assertions.
31
31
  - `runtime-executable` — execute the specified command or script. Capture stdout/stderr as evidence. Record pass/fail based on exit code and output.
32
32
  - `live-runtime` — exercise the real runtime path. Start or connect to the app/service if needed, use browser/runtime/network checks, and verify observable behavior.
33
33
  - `mixed` — run all automatable artifact-driven and live-runtime checks. Separate any remaining human-only checks explicitly.
@@ -48,7 +48,7 @@ Choose the lightest tool that proves the check honestly:
48
48
  - Run `node` / other script invocations
49
49
  - Read files and verify their contents
50
50
  - Check that expected artifacts exist and have correct structure
51
- - For live/runtime/UI checks, exercise the real flow with gsd-browser when applicable and inspect runtime/network/console state
51
+ - For live/runtime/UI checks, exercise the real flow with browser tools when applicable and inspect runtime/network/console state
52
52
  - When a check cannot be honestly automated, gather the best objective evidence you can and mark it `NEEDS-HUMAN`
53
53
 
54
54
  For each check, record:
@@ -118,7 +118,7 @@ Templates are in `{{templatesDir}}`.
118
118
 
119
119
  **Secrets:** Use `secure_env_collect`. Never ask the user to edit `.env` files or paste secrets.
120
120
 
121
- **Browser verification:** Verify frontend work against a running app with gsd-browser by default. Use `browser_find`/`browser_snapshot_refs` for discovery, refs/selectors -> `browser_batch` for actions, `browser_assert` for verification, and `browser_diff` -> console/network logs -> full inspection as last resort. If tools are MCP-namespaced, prefer `mcp__gsd-browser__browser_*`. Retry only with a new hypothesis.
121
+ **Browser verification:** Verify frontend work against a running app with browser tools by default. Use `browser_find`/`browser_snapshot_refs` for discovery, refs/selectors -> `browser_batch` for actions, `browser_assert` for verification, and `browser_diff` -> console/network logs -> full inspection as last resort. If browser tools are MCP-namespaced, use that host-provided browser surface. Retry only with a new hypothesis.
122
122
 
123
123
  **Database:** Never query `.gsd/gsd.db` directly via `sqlite3`, `better-sqlite3`, or `node -e require('better-sqlite3')`; the engine owns a single-writer WAL connection. Use `gsd_milestone_status`, `gsd_journal_query`, or other `gsd_*` tools.
124
124
 
@@ -193,7 +193,12 @@ export function parseRoadmapSlices(content: string): RoadmapSliceEntry[] {
193
193
  ? expandDependencies(depsMatch[1]!.split(",").map(s => s.trim()))
194
194
  : [];
195
195
 
196
- currentSlice = { id, title, risk, depends, done, demo: "" };
196
+ // ADR-011: the renderer writes a `[sketch]` badge for sketch slices.
197
+ // Parse it back so the is_sketch flag survives a markdown → DB re-import
198
+ // (e.g. /gsd recover); otherwise the flag was silently lost.
199
+ const isSketch = /\[sketch\]/i.test(rest);
200
+
201
+ currentSlice = { id, title, risk, depends, done, demo: "", isSketch };
197
202
  continue;
198
203
  }
199
204
 
@@ -11,7 +11,7 @@ import { logWarning } from "../workflow-logger.js";
11
11
  // ─── Types ──────────────────────────────────────────────────────────────────
12
12
 
13
13
  export interface ContentViolation {
14
- severity: "warning";
14
+ severity: "error" | "warning";
15
15
  reason: string;
16
16
  }
17
17
 
@@ -46,6 +46,9 @@ export function validateContent(
46
46
 
47
47
  type ContentValidatorFn = (content: string) => ContentViolation[];
48
48
 
49
+ const TASK_MARKER_RE = /^\s*(?:-\s+\[[ xX]\]\s+\*\*T\d+:|#{2,4}\s+T\d+\b)/gm;
50
+ const SLICE_MARKER_RE = /^\s*(?:-\s+\[[ xX]\]\s+\*\*S\d+:|#{2,4}\s+S\d+\b)/gm;
51
+
49
52
  const VALIDATORS: Record<string, ContentValidatorFn> = {
50
53
  "plan-slice": validatePlanSlice,
51
54
  "plan-milestone": validatePlanMilestone,
@@ -55,10 +58,10 @@ function validatePlanSlice(content: string): ContentViolation[] {
55
58
  const violations: ContentViolation[] = [];
56
59
 
57
60
  // Must have at least 1 task entry — single-task slices are valid (#3649)
58
- const taskCount = (content.match(/- \[[ x]\] \*\*T\d+/g) || []).length;
61
+ const taskCount = (content.match(TASK_MARKER_RE) || []).length;
59
62
  if (taskCount < 1) {
60
63
  violations.push({
61
- severity: "warning",
64
+ severity: "error",
62
65
  reason: `Slice plan has ${taskCount} task(s) — expected at least 1`,
63
66
  });
64
67
  }
@@ -86,10 +89,10 @@ function validatePlanMilestone(content: string): ContentViolation[] {
86
89
  const violations: ContentViolation[] = [];
87
90
 
88
91
  // Must have at least 1 slice entry
89
- const sliceCount = (content.match(/##\s+S\d+/g) || []).length;
92
+ const sliceCount = (content.match(SLICE_MARKER_RE) || []).length;
90
93
  if (sliceCount < 1) {
91
94
  violations.push({
92
- severity: "warning",
95
+ severity: "error",
93
96
  reason: `Milestone roadmap has ${sliceCount} slice(s) — expected at least 1`,
94
97
  });
95
98
  }
@@ -117,6 +117,18 @@ const UNIT_TYPE_SKILL_MANIFEST: Record<string, string[]> = {
117
117
  "review",
118
118
  "accessibility",
119
119
  ],
120
+ // Slice closeout — the "closer" role: verify assembled task work, write the
121
+ // downstream-ready summary + UAT, optionally drive reviewer/security/tester
122
+ // subagents. Predictable skill set, mirrors `complete-milestone`.
123
+ "complete-slice": [
124
+ "verify-before-complete",
125
+ "test",
126
+ "review",
127
+ "security-review",
128
+ "write-docs",
129
+ "observability",
130
+ "handoff",
131
+ ],
120
132
  // `execute-task` intentionally omitted — implementation hot path covers a
121
133
  // wide surface of technologies; wildcard fallback preserves today's
122
134
  // behavior until per-task skill hints can be derived from task-plan
@@ -0,0 +1,402 @@
1
+ // Project/App: gsd-pi
2
+ // File Purpose: Active-unit source observations for provider payload context.
3
+
4
+ import { existsSync, readFileSync, statSync } from "node:fs";
5
+ import { isAbsolute, relative, resolve, sep } from "node:path";
6
+
7
+ import type { TaskRow } from "./db-task-slice-rows.js";
8
+ import { extractPlanningPathReference, normalizeFilePath } from "./pre-execution-checks.js";
9
+
10
+ export const WHOLE_FILE_OBSERVATION_MAX_BYTES = 50 * 1024;
11
+ export const WHOLE_FILE_OBSERVATION_MAX_LINES = 2000;
12
+
13
+ export type SourceObservationStatus =
14
+ | "whole"
15
+ | "missing"
16
+ | "binary/image"
17
+ | "over-threshold"
18
+ | "glob"
19
+ | "directory"
20
+ | "unresolved selector";
21
+
22
+ export interface SourceObservationUnit {
23
+ unitType: string;
24
+ unitId: string;
25
+ startedAt: number;
26
+ basePath: string;
27
+ }
28
+
29
+ export type SourceObservationSource = "plan" | "read" | "mutation";
30
+
31
+ export interface SourceObservation {
32
+ path: string;
33
+ absolutePath: string | null;
34
+ status: SourceObservationStatus;
35
+ source: SourceObservationSource;
36
+ text?: string;
37
+ bytes?: number;
38
+ lines?: number;
39
+ reason?: string;
40
+ }
41
+
42
+ interface ActiveSourceObservationSet {
43
+ unit: SourceObservationUnit;
44
+ observations: Map<string, SourceObservation>;
45
+ }
46
+
47
+ const SOURCE_CONTEXT_TITLE = "## Source Context Block";
48
+ const SOURCE_OBSERVATION_UNIT_TYPE = "execute-task";
49
+
50
+ export function supportsSourceObservationsForUnit(unitType: string): boolean {
51
+ return unitType === SOURCE_OBSERVATION_UNIT_TYPE;
52
+ }
53
+
54
+ export class SourceObservationStore {
55
+ private active: ActiveSourceObservationSet | null = null;
56
+
57
+ beginUnit(unit: SourceObservationUnit): void {
58
+ if (!supportsSourceObservationsForUnit(unit.unitType)) {
59
+ this.clear();
60
+ return;
61
+ }
62
+ if (this.matches(unit)) return;
63
+ this.active = { unit: { ...unit }, observations: new Map() };
64
+ }
65
+
66
+ clear(): void {
67
+ this.active = null;
68
+ }
69
+
70
+ degradeUnit(unit: Pick<SourceObservationUnit, "unitType" | "unitId" | "startedAt">): void {
71
+ if (!this.active) return;
72
+ const current = this.active.unit;
73
+ if (
74
+ current.unitType === unit.unitType &&
75
+ current.unitId === unit.unitId &&
76
+ current.startedAt === unit.startedAt
77
+ ) {
78
+ this.active = null;
79
+ }
80
+ }
81
+
82
+ observePlanTask(task: TaskRow): void {
83
+ if (!this.active) return;
84
+ for (const entry of planDeclaredSourceEntries(task)) {
85
+ this.observePath(entry.path, "plan");
86
+ }
87
+ }
88
+
89
+ observeRead(input: { path?: unknown; file_path?: unknown; [key: string]: unknown }): void {
90
+ if (!this.active) return;
91
+ const rawPath = readPathFromInput(input);
92
+ if (!rawPath.trim()) return;
93
+ this.observePath(rawPath, "read");
94
+ }
95
+
96
+ observeMutation(input: { path?: unknown; file_path?: unknown; [key: string]: unknown }): void {
97
+ if (!this.active) return;
98
+ const rawPath = readPathFromInput(input);
99
+ if (!rawPath.trim()) return;
100
+ this.observePath(rawPath, "mutation", { replaceExisting: true });
101
+ }
102
+
103
+ renderActiveBlock(): string | null {
104
+ if (!this.active || this.active.observations.size === 0) return null;
105
+ if (!supportsSourceObservationsForUnit(this.active.unit.unitType)) return null;
106
+ const observations = [...this.active.observations.values()]
107
+ .sort((a, b) => a.path.localeCompare(b.path));
108
+ const lines = [
109
+ SOURCE_CONTEXT_TITLE,
110
+ `Active Unit: ${this.active.unit.unitType} ${this.active.unit.unitId}`,
111
+ "",
112
+ "The files below are protected active-Unit source context. " +
113
+ "Use this block instead of rereading small files just to recover line windows.",
114
+ ];
115
+
116
+ const wholeFiles = observations.filter((observation) => observation.status === "whole");
117
+ const unavailable = observations.filter((observation) => observation.status !== "whole");
118
+
119
+ if (wholeFiles.length > 0) {
120
+ lines.push("", "### Whole-File Observations");
121
+ for (const observation of wholeFiles) {
122
+ lines.push(
123
+ "",
124
+ `#### ${observation.path}`,
125
+ `Status: whole-file (${observation.lines ?? 0} lines, ${formatSize(observation.bytes ?? 0)})`,
126
+ fencedSource(observation.text ?? ""),
127
+ );
128
+ }
129
+ }
130
+
131
+ if (unavailable.length > 0) {
132
+ lines.push("", "### Unavailable Source Observations");
133
+ for (const observation of unavailable) {
134
+ const detail = observation.reason ? ` - ${observation.reason}` : "";
135
+ lines.push(`- ${observation.path}: ${observation.status}${detail}`);
136
+ }
137
+ }
138
+
139
+ return lines.join("\n");
140
+ }
141
+
142
+ private matches(unit: SourceObservationUnit): boolean {
143
+ if (!this.active) return false;
144
+ const current = this.active.unit;
145
+ return current.unitType === unit.unitType &&
146
+ current.unitId === unit.unitId &&
147
+ current.startedAt === unit.startedAt &&
148
+ current.basePath === unit.basePath;
149
+ }
150
+
151
+ private observePath(
152
+ rawPath: string,
153
+ source: SourceObservationSource,
154
+ options: { replaceExisting?: boolean } = {},
155
+ ): void {
156
+ if (!this.active) return;
157
+ const observation = observeSourcePath(this.active.unit.basePath, rawPath, source);
158
+ const key = observation.absolutePath ?? `${observation.status}:${observation.path}`;
159
+ const existing = this.active.observations.get(key);
160
+ if (options.replaceExisting || !existing || observation.status === "whole" || existing.status !== "whole") {
161
+ this.active.observations.set(key, observation);
162
+ }
163
+ }
164
+ }
165
+
166
+ export function planDeclaredSourceEntries(
167
+ task: TaskRow,
168
+ ): Array<{ path: string; field: "files" | "inputs" }> {
169
+ const entries: Array<{ path: string; field: "files" | "inputs" }> = [];
170
+ for (const file of task.files) {
171
+ const path = extractPlanningPathReference(file) ?? file.trim();
172
+ if (path) entries.push({ path, field: "files" });
173
+ }
174
+ for (const input of task.inputs) {
175
+ const path = extractPlanningPathReference(input);
176
+ if (path) entries.push({ path, field: "inputs" });
177
+ }
178
+ return entries;
179
+ }
180
+
181
+ export function observeSourcePath(
182
+ basePath: string,
183
+ rawPath: string,
184
+ source: SourceObservationSource,
185
+ ): SourceObservation {
186
+ const normalizedRaw = normalizeFilePath(rawPath.trim());
187
+ const displayPath = normalizedRaw || rawPath.trim();
188
+ if (!normalizedRaw) {
189
+ return unavailable(displayPath || "<empty>", null, "unresolved selector", source);
190
+ }
191
+ if (containsGlobPattern(normalizedRaw)) {
192
+ return unavailable(displayPath, null, "glob", source, "glob selectors are not whole files");
193
+ }
194
+ if (rawPath.trim().endsWith("/")) {
195
+ return unavailable(displayPath, null, "directory", source, "directory selectors are not whole files");
196
+ }
197
+
198
+ const rootPath = resolve(basePath);
199
+ const absolutePath = isAbsolute(normalizedRaw) ? resolve(normalizedRaw) : resolve(rootPath, normalizedRaw);
200
+ const path = formatObservationPath(rootPath, absolutePath, displayPath);
201
+
202
+ if (!isPathInsideRoot(rootPath, absolutePath)) {
203
+ return unavailable(displayPath, absolutePath, "unresolved selector", source, "path is outside active Unit root");
204
+ }
205
+
206
+ if (!existsSync(absolutePath)) {
207
+ return unavailable(path, absolutePath, "missing", source, "file does not exist in the active Unit root");
208
+ }
209
+
210
+ let stat;
211
+ try {
212
+ stat = statSync(absolutePath);
213
+ } catch {
214
+ return unavailable(path, absolutePath, "missing", source, "file could not be inspected");
215
+ }
216
+
217
+ if (stat.isDirectory()) {
218
+ return unavailable(path, absolutePath, "directory", source, "directory selectors are not whole files");
219
+ }
220
+ if (!stat.isFile()) {
221
+ return unavailable(path, absolutePath, "unresolved selector", source, "path is not a regular file");
222
+ }
223
+ if (stat.size > WHOLE_FILE_OBSERVATION_MAX_BYTES) {
224
+ return unavailable(
225
+ path,
226
+ absolutePath,
227
+ "over-threshold",
228
+ source,
229
+ `${formatSize(stat.size)} exceeds ${formatSize(WHOLE_FILE_OBSERVATION_MAX_BYTES)}`,
230
+ );
231
+ }
232
+
233
+ let buffer: Buffer;
234
+ try {
235
+ buffer = readFileSync(absolutePath);
236
+ } catch {
237
+ return unavailable(path, absolutePath, "missing", source, "file could not be read");
238
+ }
239
+
240
+ if (isBinaryOrImage(buffer)) {
241
+ return unavailable(path, absolutePath, "binary/image", source, "binary or image files are not inlined as source text");
242
+ }
243
+
244
+ const text = buffer.toString("utf8");
245
+ const lines = countLines(text);
246
+ if (lines > WHOLE_FILE_OBSERVATION_MAX_LINES) {
247
+ return unavailable(
248
+ path,
249
+ absolutePath,
250
+ "over-threshold",
251
+ source,
252
+ `${lines} lines exceeds ${WHOLE_FILE_OBSERVATION_MAX_LINES}`,
253
+ );
254
+ }
255
+
256
+ return {
257
+ path,
258
+ absolutePath,
259
+ status: "whole",
260
+ source,
261
+ text,
262
+ bytes: buffer.byteLength,
263
+ lines,
264
+ };
265
+ }
266
+
267
+ export function injectSourceContextBlockIntoPayload(
268
+ payload: Record<string, unknown>,
269
+ block: string,
270
+ ): Record<string, unknown> {
271
+ const messages = payload.messages;
272
+ if (Array.isArray(messages)) {
273
+ return {
274
+ ...payload,
275
+ messages: [
276
+ ...withoutExistingSourceContextMessages(messages),
277
+ { role: "user", content: [{ type: "text", text: block }] },
278
+ ],
279
+ };
280
+ }
281
+
282
+ const input = payload.input;
283
+ if (Array.isArray(input)) {
284
+ return {
285
+ ...payload,
286
+ input: [
287
+ ...withoutExistingSourceContextItems(input),
288
+ { role: "user", content: [{ type: "input_text", text: block }] },
289
+ ],
290
+ };
291
+ }
292
+
293
+ return payload;
294
+ }
295
+
296
+ function unavailable(
297
+ path: string,
298
+ absolutePath: string | null,
299
+ status: Exclude<SourceObservationStatus, "whole">,
300
+ source: SourceObservationSource,
301
+ reason?: string,
302
+ ): SourceObservation {
303
+ return { path, absolutePath, status, source, reason };
304
+ }
305
+
306
+ function formatObservationPath(basePath: string, absolutePath: string, fallback: string): string {
307
+ const rel = relative(basePath, absolutePath);
308
+ if (rel && !rel.startsWith("..") && !isAbsolute(rel)) {
309
+ return rel.split(sep).join("/");
310
+ }
311
+ return fallback;
312
+ }
313
+
314
+ function isPathInsideRoot(rootPath: string, absolutePath: string): boolean {
315
+ const rel = relative(rootPath, absolutePath);
316
+ return rel === "" || (!rel.startsWith("..") && !isAbsolute(rel));
317
+ }
318
+
319
+ function containsGlobPattern(candidate: string): boolean {
320
+ return ["*", "?", "[", "]", "{", "}"].some((char) => candidate.includes(char));
321
+ }
322
+
323
+ function readPathFromInput(input: { path?: unknown; file_path?: unknown }): string {
324
+ if (typeof input.path === "string") return input.path;
325
+ if (typeof input.file_path === "string") return input.file_path;
326
+ return "";
327
+ }
328
+
329
+ function formatSize(bytes: number): string {
330
+ if (bytes < 1024) return `${bytes}B`;
331
+ if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)}KB`;
332
+ return `${(bytes / (1024 * 1024)).toFixed(1)}MB`;
333
+ }
334
+
335
+ function isBinaryOrImage(buffer: Buffer): boolean {
336
+ if (buffer.includes(0)) return true;
337
+ if (startsWith(buffer, [0xff, 0xd8, 0xff])) return true;
338
+ if (startsWith(buffer, [0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a])) return true;
339
+ if (startsWithAscii(buffer, "GIF")) return true;
340
+ if (
341
+ startsWithAscii(buffer, "RIFF") &&
342
+ buffer.length >= 12 &&
343
+ buffer.subarray(8, 12).toString("ascii") === "WEBP"
344
+ ) return true;
345
+ return false;
346
+ }
347
+
348
+ function startsWith(buffer: Buffer, signature: readonly number[]): boolean {
349
+ if (buffer.length < signature.length) return false;
350
+ return signature.every((byte, index) => buffer[index] === byte);
351
+ }
352
+
353
+ function startsWithAscii(buffer: Buffer, text: string): boolean {
354
+ return buffer.length >= text.length && buffer.subarray(0, text.length).toString("ascii") === text;
355
+ }
356
+
357
+ function countLines(text: string): number {
358
+ if (text.length === 0) return 0;
359
+ return text.endsWith("\n") ? text.split("\n").length - 1 : text.split("\n").length;
360
+ }
361
+
362
+ function fencedSource(text: string): string {
363
+ const longest = longestBacktickRun(text);
364
+ const fence = longest >= 3 ? "`".repeat(longest + 1) : "```";
365
+ return `${fence}\n${text}\n${fence}`;
366
+ }
367
+
368
+ function longestBacktickRun(text: string): number {
369
+ let longest = 0;
370
+ let current = 0;
371
+ for (const char of text) {
372
+ if (char === "`") {
373
+ current += 1;
374
+ longest = Math.max(longest, current);
375
+ } else {
376
+ current = 0;
377
+ }
378
+ }
379
+ return longest;
380
+ }
381
+
382
+ function firstText(content: unknown): string | null {
383
+ if (typeof content === "string") return content;
384
+ if (!Array.isArray(content)) return null;
385
+ const block = content.find((entry) =>
386
+ entry && typeof entry === "object" && "text" in entry,
387
+ ) as { text?: unknown } | undefined;
388
+ return typeof block?.text === "string" ? block.text : null;
389
+ }
390
+
391
+ function isSourceContextMessage(message: unknown): boolean {
392
+ if (!message || typeof message !== "object") return false;
393
+ return firstText((message as { content?: unknown }).content)?.startsWith(SOURCE_CONTEXT_TITLE) === true;
394
+ }
395
+
396
+ function withoutExistingSourceContextMessages(messages: unknown[]): unknown[] {
397
+ return messages.filter((message) => !isSourceContextMessage(message));
398
+ }
399
+
400
+ function withoutExistingSourceContextItems(items: unknown[]): unknown[] {
401
+ return items.filter((item) => !isSourceContextMessage(item));
402
+ }
@@ -9,6 +9,7 @@
9
9
  import { existsSync, statSync } from "node:fs";
10
10
 
11
11
  import {
12
+ getAllMilestones,
12
13
  getMilestone,
13
14
  getMilestoneSlices,
14
15
  getSliceTasks,
@@ -41,17 +42,30 @@ function summaryMtimeIso(path: string): string | null {
41
42
  }
42
43
 
43
44
  export function detectMissingCompletionTimestampDrift(
44
- state: GSDState,
45
+ _state: GSDState,
45
46
  ctx: DriftContext,
46
47
  ): CompletionTimestampDrift[] {
47
48
  if (!isDbAvailable()) return [];
48
- const mid = state.activeMilestone?.id;
49
- if (!mid) return [];
50
-
51
- const milestone = getMilestone(mid);
52
- if (!milestone) return [];
53
49
 
50
+ // Scan every milestone, not just the active one. Markdown artifacts exist on
51
+ // disk for all milestones, so a user can manually complete a queued/parked
52
+ // milestone (edit the roadmap + drop a SUMMARY) and leave completed_at=null
53
+ // in the DB. Gating on the active milestone left that drift unrepaired until
54
+ // the milestone happened to become active.
54
55
  const drifts: CompletionTimestampDrift[] = [];
56
+ for (const { id: mid } of getAllMilestones()) {
57
+ collectMilestoneCompletionDrift(mid, ctx, drifts);
58
+ }
59
+ return drifts;
60
+ }
61
+
62
+ function collectMilestoneCompletionDrift(
63
+ mid: string,
64
+ ctx: DriftContext,
65
+ drifts: CompletionTimestampDrift[],
66
+ ): void {
67
+ const milestone = getMilestone(mid);
68
+ if (!milestone) return;
55
69
 
56
70
  // Milestone-level
57
71
  if (
@@ -105,8 +119,6 @@ export function detectMissingCompletionTimestampDrift(
105
119
  }
106
120
  }
107
121
  }
108
-
109
- return drifts;
110
122
  }
111
123
 
112
124
  export function repairMissingCompletionTimestamp(