@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
@@ -3,8 +3,8 @@
3
3
  // Focused tests for `resolveSkillManifest` and `filterSkillsByManifest`.
4
4
  // Covers the wildcard semantics, the newly seeded unit-type entries
5
5
  // (complete-milestone, validate-milestone, reassess-roadmap, research-slice,
6
- // plan-slice, refine-slice, replan-slice, run-uat), and the deliberate
7
- // wildcard fallback for the execute-task hot path (RFC #4779).
6
+ // plan-slice, refine-slice, replan-slice, run-uat, complete-slice), and the
7
+ // deliberate wildcard fallback for the execute-task hot path (RFC #4779).
8
8
 
9
9
  import test from "node:test";
10
10
  import assert from "node:assert/strict";
@@ -23,6 +23,7 @@ const NEWLY_WIRED_UNIT_TYPES = [
23
23
  "refine-slice",
24
24
  "replan-slice",
25
25
  "run-uat",
26
+ "complete-slice",
26
27
  ] as const;
27
28
 
28
29
  test("resolveSkillManifest returns null for undefined unit type (wildcard)", () => {
@@ -65,7 +66,7 @@ test("resolveSkillManifest: slice-level manifests include decompose-into-slices"
65
66
  });
66
67
 
67
68
  test("resolveSkillManifest: validation / completion flows include verify-before-complete", () => {
68
- for (const unitType of ["complete-milestone", "validate-milestone", "run-uat"] as const) {
69
+ for (const unitType of ["complete-milestone", "validate-milestone", "run-uat", "complete-slice"] as const) {
69
70
  const allowlist = resolveSkillManifest(unitType);
70
71
  assert.ok(
71
72
  allowlist?.includes("verify-before-complete"),
@@ -7,6 +7,8 @@ import { tmpdir } from "node:os";
7
7
  import { DISPATCH_RULES } from "../auto-dispatch.ts";
8
8
  import {
9
9
  closeDatabase,
10
+ getArtifact,
11
+ getAssessment,
10
12
  getLatestAssessmentByScope,
11
13
  insertMilestone,
12
14
  insertSlice,
@@ -46,6 +48,12 @@ test("skipped validation dispatch persists the validation file and DB assessment
46
48
 
47
49
  assert.deepEqual(action, { action: "skip" });
48
50
  assert.equal(existsSync(join(milestoneDir, "M001-VALIDATION.md")), true);
51
+ assert.equal(existsSync(join(sliceDir, "S01-ASSESSMENT.md")), true);
52
+ const artifactPath = "milestones/M001/slices/S01/S01-ASSESSMENT.md";
53
+ const assessmentPath = `.gsd/${artifactPath}`;
54
+ assert.equal(getArtifact(artifactPath)?.artifact_type, "ASSESSMENT");
55
+ assert.equal(getAssessment(assessmentPath)?.scope, "run-uat");
56
+ assert.equal(getAssessment(assessmentPath)?.status, "pass");
49
57
  assert.equal(
50
58
  getLatestAssessmentByScope("M001", "milestone-validation")?.status,
51
59
  "pass",
@@ -0,0 +1,275 @@
1
+ import test from "node:test";
2
+ import assert from "node:assert/strict";
3
+ import { mkdirSync, mkdtempSync, writeFileSync } from "node:fs";
4
+ import { tmpdir } from "node:os";
5
+ import { join } from "node:path";
6
+
7
+ import {
8
+ SourceObservationStore,
9
+ injectSourceContextBlockIntoPayload,
10
+ observeSourcePath,
11
+ planDeclaredSourceEntries,
12
+ } from "../source-observations.js";
13
+ import { AutoSession } from "../auto/session.js";
14
+ import { truncateContextResultMessages, truncateResponsesInputResultItems } from "../context-masker.js";
15
+ import type { TaskRow } from "../db-task-slice-rows.js";
16
+
17
+ function makeTask(overrides: Partial<TaskRow> = {}): TaskRow {
18
+ return {
19
+ milestone_id: "M001",
20
+ slice_id: "S01",
21
+ id: "T01",
22
+ title: "Task",
23
+ status: "pending",
24
+ one_liner: "",
25
+ narrative: "",
26
+ verification_result: "",
27
+ duration: "",
28
+ completed_at: null,
29
+ blocker_discovered: false,
30
+ deviations: "",
31
+ known_issues: "",
32
+ key_files: [],
33
+ key_decisions: [],
34
+ full_summary_md: "",
35
+ description: "",
36
+ estimate: "",
37
+ files: [],
38
+ verify: "",
39
+ inputs: [],
40
+ expected_output: [],
41
+ observability_impact: "",
42
+ full_plan_md: "",
43
+ sequence: 1,
44
+ blocker_source: "",
45
+ escalation_pending: 0,
46
+ escalation_awaiting_review: 0,
47
+ escalation_artifact_path: null,
48
+ escalation_override_applied_at: null,
49
+ ...overrides,
50
+ };
51
+ }
52
+
53
+ function tempProject(): string {
54
+ return mkdtempSync(join(tmpdir(), "gsd-source-observations-"));
55
+ }
56
+
57
+ function beginStore(basePath: string): SourceObservationStore {
58
+ const store = new SourceObservationStore();
59
+ store.beginUnit({ unitType: "execute-task", unitId: "M001/S01/T01", startedAt: 123, basePath });
60
+ return store;
61
+ }
62
+
63
+ test("plan-declared source entries use task.files and concrete task.inputs, not expectedOutput", () => {
64
+ const task = makeTask({
65
+ files: ["src/app.ts"],
66
+ inputs: ["Current enum shape", "`src/input.ts` - existing input"],
67
+ expected_output: ["src/generated.ts"],
68
+ });
69
+
70
+ assert.deepEqual(planDeclaredSourceEntries(task), [
71
+ { path: "src/app.ts", field: "files" },
72
+ { path: "src/input.ts", field: "inputs" },
73
+ ]);
74
+ });
75
+
76
+ test("preloaded plan observations render whole files and unavailable statuses", () => {
77
+ const basePath = tempProject();
78
+ mkdirSync(join(basePath, "src"), { recursive: true });
79
+ writeFileSync(join(basePath, "src", "app.ts"), "export const value = 1;\n");
80
+ mkdirSync(join(basePath, "src", "directory"), { recursive: true });
81
+ writeFileSync(join(basePath, "image.png"), Buffer.from([0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a]));
82
+
83
+ const store = beginStore(basePath);
84
+ store.observePlanTask(makeTask({
85
+ files: [
86
+ "src/app.ts",
87
+ "src/missing.ts",
88
+ "src/*.ts",
89
+ "src/directory/",
90
+ "image.png",
91
+ ],
92
+ }));
93
+
94
+ const block = store.renderActiveBlock();
95
+ assert.ok(block);
96
+ assert.match(block, /## Source Context Block/);
97
+ assert.match(block, /#### src\/app\.ts/);
98
+ assert.match(block, /export const value = 1;/);
99
+ assert.match(block, /src\/missing\.ts: missing/);
100
+ assert.match(block, /src\/\*\.ts: glob/);
101
+ assert.match(block, /src\/directory: directory/);
102
+ assert.match(block, /image\.png: binary\/image/);
103
+ });
104
+
105
+ test("narrow reads of under-threshold files auto-upgrade to whole-file observations", () => {
106
+ const basePath = tempProject();
107
+ mkdirSync(join(basePath, "src"), { recursive: true });
108
+ writeFileSync(join(basePath, "src", "app.ts"), ["line one", "line two", "line three"].join("\n"));
109
+
110
+ const store = beginStore(basePath);
111
+ store.observeRead({ path: "src/app.ts", offset: 2, limit: 1 });
112
+
113
+ const block = store.renderActiveBlock();
114
+ assert.ok(block);
115
+ assert.match(block, /line one/);
116
+ assert.match(block, /line two/);
117
+ assert.match(block, /line three/);
118
+ });
119
+
120
+ test("successful file mutations refresh active whole-file observations", () => {
121
+ const basePath = tempProject();
122
+ writeFileSync(join(basePath, "app.ts"), "export const value = 'before';\n");
123
+
124
+ const store = beginStore(basePath);
125
+ store.observeRead({ path: "app.ts" });
126
+
127
+ assert.match(store.renderActiveBlock() ?? "", /before/);
128
+
129
+ writeFileSync(join(basePath, "app.ts"), "export const value = 'after';\n");
130
+ store.observeMutation({ path: "app.ts" });
131
+
132
+ const block = store.renderActiveBlock() ?? "";
133
+ assert.match(block, /after/);
134
+ assert.doesNotMatch(block, /before/);
135
+ });
136
+
137
+ test("successful writes promote missing plan observations to whole files", () => {
138
+ const basePath = tempProject();
139
+ const store = beginStore(basePath);
140
+ store.observePlanTask(makeTask({ files: ["generated.ts"] }));
141
+
142
+ assert.match(store.renderActiveBlock() ?? "", /generated\.ts: missing/);
143
+
144
+ writeFileSync(join(basePath, "generated.ts"), "export const generated = true;\n");
145
+ store.observeMutation({ path: "generated.ts" });
146
+
147
+ const block = store.renderActiveBlock() ?? "";
148
+ assert.match(block, /#### generated\.ts/);
149
+ assert.match(block, /export const generated = true;/);
150
+ assert.doesNotMatch(block, /generated\.ts: missing/);
151
+ });
152
+
153
+ test("over-threshold files are explicit unavailable observations", () => {
154
+ const basePath = tempProject();
155
+ writeFileSync(join(basePath, "large.txt"), "a".repeat(51 * 1024));
156
+
157
+ const observation = observeSourcePath(basePath, "large.txt", "plan");
158
+
159
+ assert.equal(observation.status, "over-threshold");
160
+ assert.match(observation.reason ?? "", /exceeds/);
161
+ });
162
+
163
+ test("outside-root paths are unavailable and never inlined", () => {
164
+ const root = tempProject();
165
+ const basePath = join(root, "project");
166
+ const outsidePath = join(root, "outside");
167
+ mkdirSync(basePath, { recursive: true });
168
+ mkdirSync(outsidePath, { recursive: true });
169
+ writeFileSync(join(outsidePath, "secret.txt"), "do not inline me\n");
170
+
171
+ const absoluteObservation = observeSourcePath(basePath, join(outsidePath, "secret.txt"), "read");
172
+ const relativeObservation = observeSourcePath(basePath, "../outside/secret.txt", "read");
173
+
174
+ assert.equal(absoluteObservation.status, "unresolved selector");
175
+ assert.equal(relativeObservation.status, "unresolved selector");
176
+ assert.match(absoluteObservation.reason ?? "", /outside active Unit root/);
177
+ assert.match(relativeObservation.reason ?? "", /outside active Unit root/);
178
+ assert.equal(absoluteObservation.text, undefined);
179
+ assert.equal(relativeObservation.text, undefined);
180
+ });
181
+
182
+ test("source observations only render for execute-task units", () => {
183
+ const basePath = tempProject();
184
+ writeFileSync(join(basePath, "plan.md"), "planning context\n");
185
+
186
+ const store = new SourceObservationStore();
187
+ store.beginUnit({ unitType: "plan-slice", unitId: "M001/S01", startedAt: 123, basePath });
188
+ store.observeRead({ path: "plan.md" });
189
+
190
+ assert.equal(store.renderActiveBlock(), null);
191
+ });
192
+
193
+ test("source context block injection survives tool-result truncation for messages payloads", () => {
194
+ const payload = {
195
+ messages: truncateContextResultMessages([
196
+ { role: "toolResult", content: [{ type: "text", text: "x".repeat(200) }], toolCallId: "read-1", toolName: "read", isError: false },
197
+ ] as any, 10),
198
+ };
199
+
200
+ const injected = injectSourceContextBlockIntoPayload(payload, "## Source Context Block\n\nfull source text");
201
+
202
+ assert.match((injected.messages as any[])[0].content[0].text, /truncated/);
203
+ assert.equal((injected.messages as any[])[1].content[0].text, "## Source Context Block\n\nfull source text");
204
+ });
205
+
206
+ test("source context block injection supports Responses input payloads", () => {
207
+ const payload = {
208
+ input: truncateResponsesInputResultItems([
209
+ { type: "function_call_output", call_id: "read-1", output: "x".repeat(200) },
210
+ ] as any, 10),
211
+ };
212
+
213
+ const injected = injectSourceContextBlockIntoPayload(payload, "## Source Context Block\n\nfull source text");
214
+
215
+ assert.match((injected.input as any[])[0].output, /truncated/);
216
+ assert.equal((injected.input as any[])[1].content[0].text, "## Source Context Block\n\nfull source text");
217
+ });
218
+
219
+ test("unit-close degradation removes active whole-file source text", () => {
220
+ const basePath = tempProject();
221
+ writeFileSync(join(basePath, "app.ts"), "export const value = 1;");
222
+ const store = beginStore(basePath);
223
+ store.observeRead({ path: "app.ts" });
224
+
225
+ assert.match(store.renderActiveBlock() ?? "", /export const value = 1/);
226
+
227
+ store.degradeUnit({ unitType: "execute-task", unitId: "M001/S01/T01", startedAt: 123 });
228
+
229
+ assert.equal(store.renderActiveBlock(), null);
230
+ });
231
+
232
+ test("AutoSession current-unit clear removes active source observations", () => {
233
+ const basePath = tempProject();
234
+ writeFileSync(join(basePath, "app.ts"), "export const value = 1;");
235
+ const session = new AutoSession();
236
+ session.basePath = basePath;
237
+ session.setCurrentUnit({
238
+ type: "execute-task",
239
+ id: "M001/S01/T01",
240
+ startedAt: 123,
241
+ workspaceRoot: basePath,
242
+ });
243
+ session.sourceObservations.observeRead({ path: "app.ts" });
244
+
245
+ assert.match(session.sourceObservations.renderActiveBlock() ?? "", /export const value = 1/);
246
+
247
+ session.clearCurrentUnit();
248
+
249
+ assert.equal(session.sourceObservations.renderActiveBlock(), null);
250
+ });
251
+
252
+ test("AutoSession clears source observations when switching to non-execute units", () => {
253
+ const basePath = tempProject();
254
+ writeFileSync(join(basePath, "app.ts"), "export const value = 1;");
255
+ const session = new AutoSession();
256
+ session.basePath = basePath;
257
+ session.setCurrentUnit({
258
+ type: "execute-task",
259
+ id: "M001/S01/T01",
260
+ startedAt: 123,
261
+ workspaceRoot: basePath,
262
+ });
263
+ session.sourceObservations.observeRead({ path: "app.ts" });
264
+
265
+ assert.match(session.sourceObservations.renderActiveBlock() ?? "", /export const value = 1/);
266
+
267
+ session.setCurrentUnit({
268
+ type: "plan-slice",
269
+ id: "M001/S01",
270
+ startedAt: 124,
271
+ workspaceRoot: basePath,
272
+ });
273
+
274
+ assert.equal(session.sourceObservations.renderActiveBlock(), null);
275
+ });
@@ -100,6 +100,7 @@ describe("stale queued milestone selection (#3470)", () => {
100
100
  const state = await deriveStateFromDb(base);
101
101
 
102
102
  assert.equal(state.activeMilestone?.id, "M068", "Queued milestone with draft should become active");
103
+ assert.equal(state.phase, "needs-discussion", "Queued milestone with draft should resume discussion");
103
104
  });
104
105
 
105
106
  test("queued milestone WITH slices can still be selected as active", async () => {
@@ -144,4 +145,46 @@ describe("stale queued milestone selection (#3470)", () => {
144
145
  assert.equal(entry!.status, "pending", `${id} should be pending, not active`);
145
146
  }
146
147
  });
148
+
149
+ test("queued milestone with stale CONTEXT-DRAFT does not block real active milestone", async () => {
150
+ base = createFixtureBase();
151
+ openDatabase(":memory:");
152
+
153
+ // M068: queued, no slices, has a CONTEXT-DRAFT (abandoned discussion)
154
+ insertMilestone({ id: "M068", title: "Abandoned Draft", status: "queued" });
155
+ writeFile(base, "milestones/M068/M068-CONTEXT-DRAFT.md", "# M068: Abandoned Draft\n\nDraft left over from an abandoned session.");
156
+
157
+ // M070: real active milestone with execution artifacts
158
+ insertMilestone({ id: "M070", title: "Real Active", status: "active" });
159
+ insertSlice({ id: "S01", milestoneId: "M070", title: "Slice One", status: "active", risk: "low", depends: [] });
160
+ insertTask({ id: "T01", sliceId: "S01", milestoneId: "M070", title: "Task One", status: "pending" });
161
+
162
+ writeFile(base, "milestones/M070/M070-CONTEXT.md", "# M070: Real Active\n\nThis is the real milestone.");
163
+ writeFile(base, "milestones/M070/M070-ROADMAP.md", "# M070: Real Active\n\n## Slices\n\n- [ ] **S01: Slice One**");
164
+ writeFile(base, "milestones/M070/slices/S01/S01-PLAN.md", "# S01: Slice One\n\n## Tasks\n\n- [ ] **T01: Task One**");
165
+
166
+ invalidateStateCache();
167
+ const state = await deriveStateFromDb(base);
168
+
169
+ assert.equal(state.activeMilestone?.id, "M070", "Stale draft on M068 must not block real active M070");
170
+
171
+ const m068Entry = state.registry.find((e: any) => e.id === "M068");
172
+ assert.ok(m068Entry, "M068 should still appear in registry");
173
+ assert.equal(m068Entry!.status, "pending", "M068 with stale draft should be pending, not active");
174
+ });
175
+
176
+ test("queued milestone with CONTEXT-DRAFT becomes active with needs-discussion phase when nothing else is active", async () => {
177
+ base = createFixtureBase();
178
+ openDatabase(":memory:");
179
+
180
+ // M068: queued, no slices, has a CONTEXT-DRAFT — should resume discussion
181
+ insertMilestone({ id: "M068", title: "Draft Only", status: "queued" });
182
+ writeFile(base, "milestones/M068/M068-CONTEXT-DRAFT.md", "# M068: Draft Only\n\nPartial context from first question round.");
183
+
184
+ invalidateStateCache();
185
+ const state = await deriveStateFromDb(base);
186
+
187
+ assert.equal(state.activeMilestone?.id, "M068", "Queued milestone with draft should become active when nothing else is");
188
+ assert.equal(state.phase, "needs-discussion", "Should resume discussion for queued milestone with draft");
189
+ });
147
190
  });
@@ -152,35 +152,50 @@ test("ADR-017 (#5700): repair failure throws ReconciliationFailedError with shap
152
152
  );
153
153
  });
154
154
 
155
- test("ADR-017 (#5700): detector failure throws ReconciliationFailedError with shape", async () => {
156
- const handler: DriftHandler = {
157
- kind: "stale-sketch-flag",
155
+ test("ADR-017 (#5700): a detector failure degrades to a blocker without aborting other handlers", async () => {
156
+ // A single detector throwing (e.g. a transient file read error) must NOT
157
+ // abort the whole cycle and hide every later handler's drift. It is collected
158
+ // as a blocker (so dispatch is still gated) while the remaining detectors run
159
+ // and their drift is repaired — graceful degradation, not fail-fast.
160
+ const throwingHandler: DriftHandler = {
161
+ kind: "stale-render",
158
162
  detect: () => {
159
163
  throw new Error("simulated detect failure");
160
164
  },
161
165
  repair: () => {
162
- /* detect fails before repair */
166
+ /* never reached: detect throws */
163
167
  },
164
168
  };
165
169
 
166
- await assert.rejects(
167
- () =>
168
- reconcileBeforeDispatch("/project", {
169
- invalidateStateCache: () => {},
170
- deriveState: async () => makeState(),
171
- registry: [handler],
172
- }),
173
- (err: unknown) => {
174
- assert.ok(err instanceof ReconciliationFailedError, "must be ReconciliationFailedError");
175
- assert.equal(err.failures.length, 1);
176
- assert.equal(err.failures[0]?.drift.kind, "stale-sketch-flag");
177
- assert.ok(err.failures[0]?.cause instanceof Error);
178
- assert.equal((err.failures[0]?.cause as Error).message, "simulated detect failure");
179
- assert.equal(err.pass, 0);
180
- assert.equal(err.detectionFailures.length, 0);
181
- assert.equal(err.persistentDrift.length, 0);
182
- return true;
170
+ let repairCount = 0;
171
+ const workingHandler: DriftHandler = {
172
+ kind: "stale-sketch-flag",
173
+ detect: () =>
174
+ repairCount === 0
175
+ ? [{ kind: "stale-sketch-flag", mid: "M001", sid: "S02" }]
176
+ : [],
177
+ repair: () => {
178
+ repairCount++;
183
179
  },
180
+ };
181
+
182
+ const result = await reconcileBeforeDispatch("/project", {
183
+ invalidateStateCache: () => {},
184
+ deriveState: async () => makeState(),
185
+ registry: [throwingHandler, workingHandler],
186
+ });
187
+
188
+ assert.equal(result.ok, true, "cycle must not abort on a single detector failure");
189
+ // The working handler (registered AFTER the thrower) still detected + repaired.
190
+ assert.equal(repairCount, 1, "later handler must still run despite earlier detect failure");
191
+ assert.equal(result.repaired.length, 1);
192
+ assert.equal(result.repaired[0]?.kind, "stale-sketch-flag");
193
+ // The detector failure surfaces as a blocker so dispatch is still gated.
194
+ assert.ok(
195
+ result.blockers.some(
196
+ (b) => b.includes("stale-render") && b.includes("simulated detect failure"),
197
+ ),
198
+ "detect failure must surface as a blocker",
184
199
  );
185
200
  });
186
201
 
@@ -746,6 +761,46 @@ test("ADR-017 (#5702): missing UAT.md clears stale full_uat_md from DB", async (
746
761
  );
747
762
  });
748
763
 
764
+ test("ADR-017 (#5702): stale-render plan repair works with descriptor-layout milestone dir", async (t) => {
765
+ // Regression for bugbot finding: repairStaleRenderFromBasePath was passing the
766
+ // raw dir segment (e.g. M001-DESCRIPTOR) straight to renderPlanCheckboxes, which
767
+ // queries the DB as getSliceTasks("M001-DESCRIPTOR", …) → empty → throws.
768
+ // After the fix it calls canonicalizeMilestoneId() first.
769
+ const base = mkdtempSync(join(tmpdir(), "gsd-adr017-descriptor-"));
770
+ // Use a descriptor-style directory name (M001-DESCRIPTOR → DB milestone M001)
771
+ const milestoneDir = "M001-DESCRIPTOR";
772
+ const sliceDir = join(base, ".gsd", "milestones", milestoneDir, "slices", "S01");
773
+ mkdirSync(join(sliceDir, "tasks"), { recursive: true });
774
+ t.after(() => {
775
+ try { closeDatabase(); } catch { /* noop */ }
776
+ rmTreeQuiet(base);
777
+ });
778
+
779
+ openDatabase(join(base, ".gsd", "gsd.db"));
780
+ clearRendererCaches();
781
+ // DB uses the canonical ID (M001), directory uses the descriptor name.
782
+ insertMilestone({ id: "M001", title: "Descriptor Test", status: "active" });
783
+ insertSlice({ id: "S01", milestoneId: "M001", title: "Slice", status: "pending" });
784
+ insertTask({ id: "T01", sliceId: "S01", milestoneId: "M001", title: "Task One", status: "done" });
785
+
786
+ const planPath = join(sliceDir, "S01-PLAN.md");
787
+ writeFileSync(planPath, makeStalePlanContent("S01", [
788
+ { id: "T01", title: "Task One", done: false },
789
+ ]));
790
+ clearRendererCaches();
791
+
792
+ const result = await reconcileBeforeDispatch(base, {
793
+ invalidateStateCache: () => {},
794
+ deriveState: async () => makeState(),
795
+ });
796
+
797
+ assert.equal(result.ok, true, "reconcile should succeed with descriptor-layout milestone dir");
798
+ const renderRepaired = result.repaired.find((d) => d.kind === "stale-render");
799
+ assert.ok(renderRepaired, "stale-render drift should be repaired");
800
+ const repairedContent = readFileSync(planPath, "utf-8");
801
+ assert.match(repairedContent, /\[x\][^\n]*T01:/, "T01 checkbox should be checked after repair");
802
+ });
803
+
749
804
  // ─── #5703: stale-worker drift ───────────────────────────────────────────────
750
805
 
751
806
  const DEAD_PID = 999_999_999; // far above any realistic system PID; process.kill(pid, 0) → ESRCH