gsd-pi 2.43.0 → 2.44.0-dev.62b5d6c

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 (568) hide show
  1. package/dist/cli.js +13 -1
  2. package/dist/help-text.js +24 -0
  3. package/dist/resources/extensions/bg-shell/overlay.js +3 -0
  4. package/dist/resources/extensions/github-sync/sync.js +2 -1
  5. package/dist/resources/extensions/gsd/auto/loop.js +0 -2
  6. package/dist/resources/extensions/gsd/auto/phases.js +7 -12
  7. package/dist/resources/extensions/gsd/auto-dashboard.js +19 -18
  8. package/dist/resources/extensions/gsd/auto-direct-dispatch.js +34 -19
  9. package/dist/resources/extensions/gsd/auto-dispatch.js +36 -21
  10. package/dist/resources/extensions/gsd/auto-post-unit.js +128 -14
  11. package/dist/resources/extensions/gsd/auto-prompts.js +202 -92
  12. package/dist/resources/extensions/gsd/auto-recovery.js +83 -135
  13. package/dist/resources/extensions/gsd/auto-supervisor.js +14 -0
  14. package/dist/resources/extensions/gsd/auto-timeout-recovery.js +4 -7
  15. package/dist/resources/extensions/gsd/auto-verification.js +5 -10
  16. package/dist/resources/extensions/gsd/auto-worktree.js +123 -30
  17. package/dist/resources/extensions/gsd/auto.js +1 -4
  18. package/dist/resources/extensions/gsd/bootstrap/db-tools.js +611 -0
  19. package/dist/resources/extensions/gsd/bootstrap/dynamic-tools.js +28 -3
  20. package/dist/resources/extensions/gsd/bootstrap/register-hooks.js +7 -0
  21. package/dist/resources/extensions/gsd/commands/catalog.js +3 -1
  22. package/dist/resources/extensions/gsd/commands/handlers/ops.js +15 -1
  23. package/dist/resources/extensions/gsd/commands-handlers.js +1 -1
  24. package/dist/resources/extensions/gsd/commands-maintenance.js +78 -3
  25. package/dist/resources/extensions/gsd/dashboard-overlay.js +32 -31
  26. package/dist/resources/extensions/gsd/db-writer.js +95 -4
  27. package/dist/resources/extensions/gsd/dispatch-guard.js +35 -30
  28. package/dist/resources/extensions/gsd/doctor-checks.js +28 -11
  29. package/dist/resources/extensions/gsd/doctor-environment.js +28 -0
  30. package/dist/resources/extensions/gsd/doctor-types.js +0 -15
  31. package/dist/resources/extensions/gsd/doctor.js +46 -282
  32. package/dist/resources/extensions/gsd/file-watcher.js +5 -1
  33. package/dist/resources/extensions/gsd/files.js +14 -198
  34. package/dist/resources/extensions/gsd/git-service.js +4 -0
  35. package/dist/resources/extensions/gsd/gitignore.js +4 -0
  36. package/dist/resources/extensions/gsd/gsd-db.js +819 -197
  37. package/dist/resources/extensions/gsd/guided-flow.js +18 -8
  38. package/dist/resources/extensions/gsd/markdown-renderer.js +862 -0
  39. package/dist/resources/extensions/gsd/md-importer.js +182 -4
  40. package/dist/resources/extensions/gsd/native-git-bridge.js +10 -1
  41. package/dist/resources/extensions/gsd/parallel-eligibility.js +14 -19
  42. package/dist/resources/extensions/gsd/parallel-orchestrator.js +38 -0
  43. package/dist/resources/extensions/gsd/parsers-legacy.js +239 -0
  44. package/dist/resources/extensions/gsd/preferences-types.js +1 -0
  45. package/dist/resources/extensions/gsd/preferences-validation.js +9 -0
  46. package/dist/resources/extensions/gsd/preferences.js +1 -0
  47. package/dist/resources/extensions/gsd/prompts/complete-slice.md +22 -9
  48. package/dist/resources/extensions/gsd/prompts/discuss.md +2 -2
  49. package/dist/resources/extensions/gsd/prompts/execute-task.md +15 -5
  50. package/dist/resources/extensions/gsd/prompts/guided-complete-slice.md +1 -1
  51. package/dist/resources/extensions/gsd/prompts/guided-execute-task.md +1 -1
  52. package/dist/resources/extensions/gsd/prompts/guided-plan-milestone.md +1 -1
  53. package/dist/resources/extensions/gsd/prompts/plan-milestone.md +6 -10
  54. package/dist/resources/extensions/gsd/prompts/plan-slice.md +4 -7
  55. package/dist/resources/extensions/gsd/prompts/reactive-execute.md +3 -3
  56. package/dist/resources/extensions/gsd/prompts/reassess-roadmap.md +6 -7
  57. package/dist/resources/extensions/gsd/prompts/replan-slice.md +6 -6
  58. package/dist/resources/extensions/gsd/reactive-graph.js +33 -3
  59. package/dist/resources/extensions/gsd/skill-health.js +3 -1
  60. package/dist/resources/extensions/gsd/state.js +484 -30
  61. package/dist/resources/extensions/gsd/tools/complete-milestone.js +128 -0
  62. package/dist/resources/extensions/gsd/tools/complete-slice.js +244 -0
  63. package/dist/resources/extensions/gsd/tools/complete-task.js +204 -0
  64. package/dist/resources/extensions/gsd/tools/plan-milestone.js +205 -0
  65. package/dist/resources/extensions/gsd/tools/plan-slice.js +155 -0
  66. package/dist/resources/extensions/gsd/tools/plan-task.js +94 -0
  67. package/dist/resources/extensions/gsd/tools/reassess-roadmap.js +152 -0
  68. package/dist/resources/extensions/gsd/tools/replan-slice.js +146 -0
  69. package/dist/resources/extensions/gsd/triage-resolution.js +17 -1
  70. package/dist/resources/extensions/gsd/undo.js +197 -3
  71. package/dist/resources/extensions/gsd/visualizer-data.js +53 -16
  72. package/dist/resources/extensions/gsd/workspace-index.js +63 -39
  73. package/dist/web/standalone/.next/BUILD_ID +1 -1
  74. package/dist/web/standalone/.next/app-path-routes-manifest.json +17 -16
  75. package/dist/web/standalone/.next/build-manifest.json +4 -4
  76. package/dist/web/standalone/.next/prerender-manifest.json +3 -3
  77. package/dist/web/standalone/.next/react-loadable-manifest.json +1 -1
  78. package/dist/web/standalone/.next/required-server-files.json +4 -4
  79. package/dist/web/standalone/.next/routes-manifest.json +6 -0
  80. package/dist/web/standalone/.next/server/app/_global-error/page.js +3 -3
  81. package/dist/web/standalone/.next/server/app/_global-error/page_client-reference-manifest.js +1 -1
  82. package/dist/web/standalone/.next/server/app/_global-error.html +2 -2
  83. package/dist/web/standalone/.next/server/app/_global-error.rsc +1 -1
  84. package/dist/web/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  85. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
  86. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
  87. package/dist/web/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  88. package/dist/web/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  89. package/dist/web/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  90. package/dist/web/standalone/.next/server/app/_not-found/page.js +2 -2
  91. package/dist/web/standalone/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  92. package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
  93. package/dist/web/standalone/.next/server/app/_not-found.rsc +4 -4
  94. package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +4 -4
  95. package/dist/web/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  96. package/dist/web/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +4 -4
  97. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  98. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  99. package/dist/web/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +2 -2
  100. package/dist/web/standalone/.next/server/app/api/boot/route.js +1 -1
  101. package/dist/web/standalone/.next/server/app/api/boot/route_client-reference-manifest.js +1 -1
  102. package/dist/web/standalone/.next/server/app/api/bridge-terminal/input/route.js +1 -1
  103. package/dist/web/standalone/.next/server/app/api/bridge-terminal/input/route_client-reference-manifest.js +1 -1
  104. package/dist/web/standalone/.next/server/app/api/bridge-terminal/resize/route.js +1 -1
  105. package/dist/web/standalone/.next/server/app/api/bridge-terminal/resize/route_client-reference-manifest.js +1 -1
  106. package/dist/web/standalone/.next/server/app/api/bridge-terminal/stream/route.js +2 -2
  107. package/dist/web/standalone/.next/server/app/api/bridge-terminal/stream/route_client-reference-manifest.js +1 -1
  108. package/dist/web/standalone/.next/server/app/api/browse-directories/route.js +1 -1
  109. package/dist/web/standalone/.next/server/app/api/browse-directories/route_client-reference-manifest.js +1 -1
  110. package/dist/web/standalone/.next/server/app/api/captures/route.js +1 -1
  111. package/dist/web/standalone/.next/server/app/api/captures/route_client-reference-manifest.js +1 -1
  112. package/dist/web/standalone/.next/server/app/api/cleanup/route.js +1 -1
  113. package/dist/web/standalone/.next/server/app/api/cleanup/route_client-reference-manifest.js +1 -1
  114. package/dist/web/standalone/.next/server/app/api/dev-mode/route.js +1 -1
  115. package/dist/web/standalone/.next/server/app/api/dev-mode/route_client-reference-manifest.js +1 -1
  116. package/dist/web/standalone/.next/server/app/api/doctor/route.js +1 -1
  117. package/dist/web/standalone/.next/server/app/api/doctor/route_client-reference-manifest.js +1 -1
  118. package/dist/web/standalone/.next/server/app/api/export-data/route.js +1 -1
  119. package/dist/web/standalone/.next/server/app/api/export-data/route_client-reference-manifest.js +1 -1
  120. package/dist/web/standalone/.next/server/app/api/files/route.js +1 -1
  121. package/dist/web/standalone/.next/server/app/api/files/route_client-reference-manifest.js +1 -1
  122. package/dist/web/standalone/.next/server/app/api/forensics/route.js +1 -1
  123. package/dist/web/standalone/.next/server/app/api/forensics/route_client-reference-manifest.js +1 -1
  124. package/dist/web/standalone/.next/server/app/api/git/route.js +1 -1
  125. package/dist/web/standalone/.next/server/app/api/git/route_client-reference-manifest.js +1 -1
  126. package/dist/web/standalone/.next/server/app/api/history/route.js +1 -1
  127. package/dist/web/standalone/.next/server/app/api/history/route_client-reference-manifest.js +1 -1
  128. package/dist/web/standalone/.next/server/app/api/hooks/route.js +1 -1
  129. package/dist/web/standalone/.next/server/app/api/hooks/route_client-reference-manifest.js +1 -1
  130. package/dist/web/standalone/.next/server/app/api/inspect/route.js +1 -1
  131. package/dist/web/standalone/.next/server/app/api/inspect/route_client-reference-manifest.js +1 -1
  132. package/dist/web/standalone/.next/server/app/api/knowledge/route.js +1 -1
  133. package/dist/web/standalone/.next/server/app/api/knowledge/route_client-reference-manifest.js +1 -1
  134. package/dist/web/standalone/.next/server/app/api/live-state/route.js +1 -1
  135. package/dist/web/standalone/.next/server/app/api/live-state/route_client-reference-manifest.js +1 -1
  136. package/dist/web/standalone/.next/server/app/api/onboarding/route.js +1 -1
  137. package/dist/web/standalone/.next/server/app/api/onboarding/route_client-reference-manifest.js +1 -1
  138. package/dist/web/standalone/.next/server/app/api/preferences/route.js +1 -1
  139. package/dist/web/standalone/.next/server/app/api/preferences/route_client-reference-manifest.js +1 -1
  140. package/dist/web/standalone/.next/server/app/api/projects/route.js +1 -1
  141. package/dist/web/standalone/.next/server/app/api/projects/route_client-reference-manifest.js +1 -1
  142. package/dist/web/standalone/.next/server/app/api/recovery/route.js +1 -1
  143. package/dist/web/standalone/.next/server/app/api/recovery/route_client-reference-manifest.js +1 -1
  144. package/dist/web/standalone/.next/server/app/api/remote-questions/route.js +5 -5
  145. package/dist/web/standalone/.next/server/app/api/remote-questions/route_client-reference-manifest.js +1 -1
  146. package/dist/web/standalone/.next/server/app/api/session/browser/route.js +1 -1
  147. package/dist/web/standalone/.next/server/app/api/session/browser/route_client-reference-manifest.js +1 -1
  148. package/dist/web/standalone/.next/server/app/api/session/command/route.js +1 -1
  149. package/dist/web/standalone/.next/server/app/api/session/command/route_client-reference-manifest.js +1 -1
  150. package/dist/web/standalone/.next/server/app/api/session/events/route.js +2 -2
  151. package/dist/web/standalone/.next/server/app/api/session/events/route_client-reference-manifest.js +1 -1
  152. package/dist/web/standalone/.next/server/app/api/session/manage/route.js +1 -1
  153. package/dist/web/standalone/.next/server/app/api/session/manage/route_client-reference-manifest.js +1 -1
  154. package/dist/web/standalone/.next/server/app/api/settings-data/route.js +1 -1
  155. package/dist/web/standalone/.next/server/app/api/settings-data/route_client-reference-manifest.js +1 -1
  156. package/dist/web/standalone/.next/server/app/api/shutdown/route.js +1 -1
  157. package/dist/web/standalone/.next/server/app/api/shutdown/route_client-reference-manifest.js +1 -1
  158. package/dist/web/standalone/.next/server/app/api/skill-health/route.js +1 -1
  159. package/dist/web/standalone/.next/server/app/api/skill-health/route_client-reference-manifest.js +1 -1
  160. package/dist/web/standalone/.next/server/app/api/steer/route.js +1 -1
  161. package/dist/web/standalone/.next/server/app/api/steer/route_client-reference-manifest.js +1 -1
  162. package/dist/web/standalone/.next/server/app/api/switch-root/route.js +1 -0
  163. package/dist/web/standalone/.next/server/app/api/switch-root/route.js.nft.json +1 -0
  164. package/dist/web/standalone/.next/server/app/api/switch-root/route_client-reference-manifest.js +1 -0
  165. package/dist/web/standalone/.next/server/app/api/terminal/input/route.js +1 -1
  166. package/dist/web/standalone/.next/server/app/api/terminal/input/route_client-reference-manifest.js +1 -1
  167. package/dist/web/standalone/.next/server/app/api/terminal/resize/route.js +2 -2
  168. package/dist/web/standalone/.next/server/app/api/terminal/resize/route_client-reference-manifest.js +1 -1
  169. package/dist/web/standalone/.next/server/app/api/terminal/sessions/route.js +2 -2
  170. package/dist/web/standalone/.next/server/app/api/terminal/sessions/route_client-reference-manifest.js +1 -1
  171. package/dist/web/standalone/.next/server/app/api/terminal/stream/route.js +4 -4
  172. package/dist/web/standalone/.next/server/app/api/terminal/stream/route_client-reference-manifest.js +1 -1
  173. package/dist/web/standalone/.next/server/app/api/terminal/upload/route.js +1 -1
  174. package/dist/web/standalone/.next/server/app/api/terminal/upload/route_client-reference-manifest.js +1 -1
  175. package/dist/web/standalone/.next/server/app/api/undo/route.js +1 -1
  176. package/dist/web/standalone/.next/server/app/api/undo/route_client-reference-manifest.js +1 -1
  177. package/dist/web/standalone/.next/server/app/api/update/route.js +1 -1
  178. package/dist/web/standalone/.next/server/app/api/update/route_client-reference-manifest.js +1 -1
  179. package/dist/web/standalone/.next/server/app/api/visualizer/route.js +1 -1
  180. package/dist/web/standalone/.next/server/app/api/visualizer/route_client-reference-manifest.js +1 -1
  181. package/dist/web/standalone/.next/server/app/index.html +1 -1
  182. package/dist/web/standalone/.next/server/app/index.rsc +5 -5
  183. package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +2 -2
  184. package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +5 -5
  185. package/dist/web/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
  186. package/dist/web/standalone/.next/server/app/index.segments/_index.segment.rsc +4 -4
  187. package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +2 -2
  188. package/dist/web/standalone/.next/server/app/page.js +2 -2
  189. package/dist/web/standalone/.next/server/app/page_client-reference-manifest.js +1 -1
  190. package/dist/web/standalone/.next/server/app-paths-manifest.json +17 -16
  191. package/dist/web/standalone/.next/server/chunks/229.js +3 -3
  192. package/dist/web/standalone/.next/server/chunks/471.js +3 -3
  193. package/dist/web/standalone/.next/server/functions-config-manifest.json +1 -0
  194. package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
  195. package/dist/web/standalone/.next/server/middleware-react-loadable-manifest.js +1 -1
  196. package/dist/web/standalone/.next/server/middleware.js +2 -2
  197. package/dist/web/standalone/.next/server/next-font-manifest.js +1 -1
  198. package/dist/web/standalone/.next/server/next-font-manifest.json +1 -1
  199. package/dist/web/standalone/.next/server/pages/404.html +1 -1
  200. package/dist/web/standalone/.next/server/pages/500.html +2 -2
  201. package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
  202. package/dist/web/standalone/.next/static/chunks/{4024.c195dc1fdd2adbea.js → 4024.0de81b543b28b9fe.js} +2 -2
  203. package/dist/web/standalone/.next/static/chunks/app/_global-error/{page-d07a2c023f1aef1e.js → page-d83ba70a25a85472.js} +1 -1
  204. package/dist/web/standalone/.next/static/chunks/app/_not-found/page-f2a7482d42a5614b.js +1 -0
  205. package/dist/web/standalone/.next/static/chunks/app/api/boot/{route-d07a2c023f1aef1e.js → route-d83ba70a25a85472.js} +1 -1
  206. package/dist/web/standalone/.next/static/chunks/app/api/bridge-terminal/input/{route-d07a2c023f1aef1e.js → route-d83ba70a25a85472.js} +1 -1
  207. package/dist/web/standalone/.next/static/chunks/app/api/bridge-terminal/resize/{route-d07a2c023f1aef1e.js → route-d83ba70a25a85472.js} +1 -1
  208. package/dist/web/standalone/.next/static/chunks/app/api/bridge-terminal/stream/route-d83ba70a25a85472.js +1 -0
  209. package/dist/web/standalone/.next/static/chunks/app/api/browse-directories/route-d83ba70a25a85472.js +1 -0
  210. package/dist/web/standalone/.next/static/chunks/app/api/captures/route-d83ba70a25a85472.js +1 -0
  211. package/dist/web/standalone/.next/static/chunks/app/api/cleanup/route-d83ba70a25a85472.js +1 -0
  212. package/dist/web/standalone/.next/static/chunks/app/api/dev-mode/route-d83ba70a25a85472.js +1 -0
  213. package/dist/web/standalone/.next/static/chunks/app/api/doctor/route-d83ba70a25a85472.js +1 -0
  214. package/dist/web/standalone/.next/static/chunks/app/api/export-data/route-d83ba70a25a85472.js +1 -0
  215. package/dist/web/standalone/.next/static/chunks/app/api/files/route-d83ba70a25a85472.js +1 -0
  216. package/dist/web/standalone/.next/static/chunks/app/api/forensics/route-d83ba70a25a85472.js +1 -0
  217. package/dist/web/standalone/.next/static/chunks/app/api/git/route-d83ba70a25a85472.js +1 -0
  218. package/dist/web/standalone/.next/static/chunks/app/api/history/route-d83ba70a25a85472.js +1 -0
  219. package/dist/web/standalone/.next/static/chunks/app/api/hooks/route-d83ba70a25a85472.js +1 -0
  220. package/dist/web/standalone/.next/static/chunks/app/api/inspect/route-d83ba70a25a85472.js +1 -0
  221. package/dist/web/standalone/.next/static/chunks/app/api/knowledge/route-d83ba70a25a85472.js +1 -0
  222. package/dist/web/standalone/.next/static/chunks/app/api/live-state/route-d83ba70a25a85472.js +1 -0
  223. package/dist/web/standalone/.next/static/chunks/app/api/onboarding/route-d83ba70a25a85472.js +1 -0
  224. package/dist/web/standalone/.next/static/chunks/app/api/preferences/route-d83ba70a25a85472.js +1 -0
  225. package/dist/web/standalone/.next/static/chunks/app/api/projects/route-d83ba70a25a85472.js +1 -0
  226. package/dist/web/standalone/.next/static/chunks/app/api/recovery/route-d83ba70a25a85472.js +1 -0
  227. package/dist/web/standalone/.next/static/chunks/app/api/remote-questions/route-d83ba70a25a85472.js +1 -0
  228. package/dist/web/standalone/.next/static/chunks/app/api/session/browser/route-d83ba70a25a85472.js +1 -0
  229. package/dist/web/standalone/.next/static/chunks/app/api/session/command/route-d83ba70a25a85472.js +1 -0
  230. package/dist/web/standalone/.next/static/chunks/app/api/session/events/route-d83ba70a25a85472.js +1 -0
  231. package/dist/web/standalone/.next/static/chunks/app/api/session/manage/route-d83ba70a25a85472.js +1 -0
  232. package/dist/web/standalone/.next/static/chunks/app/api/settings-data/route-d83ba70a25a85472.js +1 -0
  233. package/dist/web/standalone/.next/static/chunks/app/api/shutdown/route-d83ba70a25a85472.js +1 -0
  234. package/dist/web/standalone/.next/static/chunks/app/api/skill-health/route-d83ba70a25a85472.js +1 -0
  235. package/dist/web/standalone/.next/static/chunks/app/api/steer/route-d83ba70a25a85472.js +1 -0
  236. package/dist/web/standalone/.next/static/chunks/app/api/switch-root/route-d83ba70a25a85472.js +1 -0
  237. package/dist/web/standalone/.next/static/chunks/app/api/terminal/input/route-d83ba70a25a85472.js +1 -0
  238. package/dist/web/standalone/.next/static/chunks/app/api/terminal/resize/route-d83ba70a25a85472.js +1 -0
  239. package/dist/web/standalone/.next/static/chunks/app/api/terminal/sessions/route-d83ba70a25a85472.js +1 -0
  240. package/dist/web/standalone/.next/static/chunks/app/api/terminal/stream/route-d83ba70a25a85472.js +1 -0
  241. package/dist/web/standalone/.next/static/chunks/app/api/terminal/upload/route-d83ba70a25a85472.js +1 -0
  242. package/dist/web/standalone/.next/static/chunks/app/api/undo/route-d83ba70a25a85472.js +1 -0
  243. package/dist/web/standalone/.next/static/chunks/app/api/update/route-d83ba70a25a85472.js +1 -0
  244. package/dist/web/standalone/.next/static/chunks/app/api/visualizer/route-d83ba70a25a85472.js +1 -0
  245. package/dist/web/standalone/.next/static/chunks/app/layout-a16c7a7ecdf0c2cf.js +1 -0
  246. package/dist/web/standalone/.next/static/chunks/app/page-b9367c5ae13b99c6.js +1 -0
  247. package/dist/web/standalone/.next/static/chunks/{main-app-2f2ee7b85712c2bd.js → main-app-fdab67f7802d7832.js} +1 -1
  248. package/dist/web/standalone/.next/static/chunks/next/dist/client/components/builtin/app-error-d83ba70a25a85472.js +1 -0
  249. package/dist/web/standalone/.next/static/chunks/next/dist/client/components/builtin/forbidden-d83ba70a25a85472.js +1 -0
  250. package/dist/web/standalone/.next/static/chunks/next/dist/client/components/builtin/global-error-459824ffb8c323dd.js +1 -0
  251. package/dist/web/standalone/.next/static/chunks/next/dist/client/components/builtin/not-found-d83ba70a25a85472.js +1 -0
  252. package/dist/web/standalone/.next/static/chunks/next/dist/client/components/builtin/unauthorized-d83ba70a25a85472.js +1 -0
  253. package/dist/web/standalone/.next/static/chunks/{webpack-fa307370fcf9fb2c.js → webpack-9014b5adb127a98a.js} +1 -1
  254. package/dist/web/standalone/.next/static/css/8a727f372cf53002.css +1 -0
  255. package/dist/web/standalone/.next/static/fOnWQBjWXMKUs4bqTg530/_buildManifest.js +1 -0
  256. package/dist/web/standalone/node_modules/node-pty/build/Makefile +2 -2
  257. package/dist/web/standalone/node_modules/node-pty/build/Release/pty.node +0 -0
  258. package/dist/web/standalone/node_modules/node-pty/build/pty.target.mk +14 -14
  259. package/dist/web/standalone/node_modules/node-pty/node-addon-api/node_addon_api.target.mk +14 -14
  260. package/dist/web/standalone/node_modules/node-pty/node-addon-api/node_addon_api_except.target.mk +14 -14
  261. package/dist/web/standalone/node_modules/node-pty/node-addon-api/node_addon_api_maybe.target.mk +14 -14
  262. package/dist/web/standalone/server.js +1 -1
  263. package/package.json +4 -4
  264. package/packages/pi-ai/dist/models.custom.d.ts +173 -0
  265. package/packages/pi-ai/dist/models.custom.d.ts.map +1 -0
  266. package/packages/pi-ai/dist/models.custom.js +170 -0
  267. package/packages/pi-ai/dist/models.custom.js.map +1 -0
  268. package/packages/pi-ai/dist/models.d.ts.map +1 -1
  269. package/packages/pi-ai/dist/models.js +16 -1
  270. package/packages/pi-ai/dist/models.js.map +1 -1
  271. package/packages/pi-ai/dist/models.test.d.ts +2 -0
  272. package/packages/pi-ai/dist/models.test.d.ts.map +1 -0
  273. package/packages/pi-ai/dist/models.test.js +67 -0
  274. package/packages/pi-ai/dist/models.test.js.map +1 -0
  275. package/packages/pi-ai/src/models.custom.ts +172 -0
  276. package/packages/pi-ai/src/models.test.ts +85 -0
  277. package/packages/pi-ai/src/models.ts +17 -1
  278. package/packages/pi-coding-agent/dist/core/agent-session.d.ts +10 -3
  279. package/packages/pi-coding-agent/dist/core/agent-session.d.ts.map +1 -1
  280. package/packages/pi-coding-agent/dist/core/agent-session.js +21 -34
  281. package/packages/pi-coding-agent/dist/core/agent-session.js.map +1 -1
  282. package/packages/pi-coding-agent/dist/core/compaction/branch-summarization.d.ts +2 -2
  283. package/packages/pi-coding-agent/dist/core/compaction/branch-summarization.d.ts.map +1 -1
  284. package/packages/pi-coding-agent/dist/core/compaction/branch-summarization.js.map +1 -1
  285. package/packages/pi-coding-agent/dist/core/compaction/compaction.d.ts +2 -2
  286. package/packages/pi-coding-agent/dist/core/compaction/compaction.d.ts.map +1 -1
  287. package/packages/pi-coding-agent/dist/core/compaction/compaction.js.map +1 -1
  288. package/packages/pi-coding-agent/dist/core/compaction-orchestrator.js +4 -4
  289. package/packages/pi-coding-agent/dist/core/compaction-orchestrator.js.map +1 -1
  290. package/packages/pi-coding-agent/dist/core/extensions/index.d.ts +1 -1
  291. package/packages/pi-coding-agent/dist/core/extensions/index.d.ts.map +1 -1
  292. package/packages/pi-coding-agent/dist/core/extensions/index.js.map +1 -1
  293. package/packages/pi-coding-agent/dist/core/extensions/loader.d.ts +6 -0
  294. package/packages/pi-coding-agent/dist/core/extensions/loader.d.ts.map +1 -1
  295. package/packages/pi-coding-agent/dist/core/extensions/loader.js +80 -0
  296. package/packages/pi-coding-agent/dist/core/extensions/loader.js.map +1 -1
  297. package/packages/pi-coding-agent/dist/core/extensions/loader.test.js +63 -0
  298. package/packages/pi-coding-agent/dist/core/extensions/loader.test.js.map +1 -1
  299. package/packages/pi-coding-agent/dist/core/extensions/types.d.ts +37 -0
  300. package/packages/pi-coding-agent/dist/core/extensions/types.d.ts.map +1 -1
  301. package/packages/pi-coding-agent/dist/core/extensions/types.js.map +1 -1
  302. package/packages/pi-coding-agent/dist/core/fallback-resolver.d.ts.map +1 -1
  303. package/packages/pi-coding-agent/dist/core/fallback-resolver.js +2 -3
  304. package/packages/pi-coding-agent/dist/core/fallback-resolver.js.map +1 -1
  305. package/packages/pi-coding-agent/dist/core/fallback-resolver.test.js +12 -2
  306. package/packages/pi-coding-agent/dist/core/fallback-resolver.test.js.map +1 -1
  307. package/packages/pi-coding-agent/dist/core/lifecycle-hooks.d.ts +38 -0
  308. package/packages/pi-coding-agent/dist/core/lifecycle-hooks.d.ts.map +1 -0
  309. package/packages/pi-coding-agent/dist/core/lifecycle-hooks.js +192 -0
  310. package/packages/pi-coding-agent/dist/core/lifecycle-hooks.js.map +1 -0
  311. package/packages/pi-coding-agent/dist/core/lsp/client.d.ts +5 -0
  312. package/packages/pi-coding-agent/dist/core/lsp/client.d.ts.map +1 -1
  313. package/packages/pi-coding-agent/dist/core/lsp/client.js +69 -21
  314. package/packages/pi-coding-agent/dist/core/lsp/client.js.map +1 -1
  315. package/packages/pi-coding-agent/dist/core/model-registry-auth-mode.test.d.ts +2 -0
  316. package/packages/pi-coding-agent/dist/core/model-registry-auth-mode.test.d.ts.map +1 -0
  317. package/packages/pi-coding-agent/dist/core/model-registry-auth-mode.test.js +255 -0
  318. package/packages/pi-coding-agent/dist/core/model-registry-auth-mode.test.js.map +1 -0
  319. package/packages/pi-coding-agent/dist/core/model-registry.d.ts +15 -0
  320. package/packages/pi-coding-agent/dist/core/model-registry.d.ts.map +1 -1
  321. package/packages/pi-coding-agent/dist/core/model-registry.js +40 -3
  322. package/packages/pi-coding-agent/dist/core/model-registry.js.map +1 -1
  323. package/packages/pi-coding-agent/dist/core/package-commands.d.ts +25 -0
  324. package/packages/pi-coding-agent/dist/core/package-commands.d.ts.map +1 -0
  325. package/packages/pi-coding-agent/dist/core/package-commands.js +253 -0
  326. package/packages/pi-coding-agent/dist/core/package-commands.js.map +1 -0
  327. package/packages/pi-coding-agent/dist/core/package-commands.test.d.ts +2 -0
  328. package/packages/pi-coding-agent/dist/core/package-commands.test.d.ts.map +1 -0
  329. package/packages/pi-coding-agent/dist/core/package-commands.test.js +225 -0
  330. package/packages/pi-coding-agent/dist/core/package-commands.test.js.map +1 -0
  331. package/packages/pi-coding-agent/dist/core/sdk.d.ts.map +1 -1
  332. package/packages/pi-coding-agent/dist/core/sdk.js +4 -0
  333. package/packages/pi-coding-agent/dist/core/sdk.js.map +1 -1
  334. package/packages/pi-coding-agent/dist/index.d.ts +3 -1
  335. package/packages/pi-coding-agent/dist/index.d.ts.map +1 -1
  336. package/packages/pi-coding-agent/dist/index.js +1 -0
  337. package/packages/pi-coding-agent/dist/index.js.map +1 -1
  338. package/packages/pi-coding-agent/dist/main.d.ts.map +1 -1
  339. package/packages/pi-coding-agent/dist/main.js +11 -199
  340. package/packages/pi-coding-agent/dist/main.js.map +1 -1
  341. package/packages/pi-coding-agent/dist/modes/interactive/components/footer.d.ts +6 -0
  342. package/packages/pi-coding-agent/dist/modes/interactive/components/footer.d.ts.map +1 -1
  343. package/packages/pi-coding-agent/dist/modes/interactive/components/footer.js +21 -0
  344. package/packages/pi-coding-agent/dist/modes/interactive/components/footer.js.map +1 -1
  345. package/packages/pi-coding-agent/dist/modes/interactive/theme/theme.d.ts +1 -1
  346. package/packages/pi-coding-agent/dist/modes/interactive/theme/theme.d.ts.map +1 -1
  347. package/packages/pi-coding-agent/dist/modes/interactive/theme/theme.js +8 -15
  348. package/packages/pi-coding-agent/dist/modes/interactive/theme/theme.js.map +1 -1
  349. package/packages/pi-coding-agent/dist/modes/print-mode.d.ts.map +1 -1
  350. package/packages/pi-coding-agent/dist/modes/print-mode.js +45 -34
  351. package/packages/pi-coding-agent/dist/modes/print-mode.js.map +1 -1
  352. package/packages/pi-coding-agent/dist/modes/rpc/rpc-client.d.ts +1 -0
  353. package/packages/pi-coding-agent/dist/modes/rpc/rpc-client.d.ts.map +1 -1
  354. package/packages/pi-coding-agent/dist/modes/rpc/rpc-client.js +7 -2
  355. package/packages/pi-coding-agent/dist/modes/rpc/rpc-client.js.map +1 -1
  356. package/packages/pi-coding-agent/dist/modes/rpc/rpc-mode.d.ts.map +1 -1
  357. package/packages/pi-coding-agent/dist/modes/rpc/rpc-mode.js +2 -1
  358. package/packages/pi-coding-agent/dist/modes/rpc/rpc-mode.js.map +1 -1
  359. package/packages/pi-coding-agent/package.json +1 -1
  360. package/packages/pi-coding-agent/src/core/agent-session.ts +26 -37
  361. package/packages/pi-coding-agent/src/core/compaction/branch-summarization.ts +2 -2
  362. package/packages/pi-coding-agent/src/core/compaction/compaction.ts +3 -3
  363. package/packages/pi-coding-agent/src/core/compaction-orchestrator.ts +4 -4
  364. package/packages/pi-coding-agent/src/core/extensions/index.ts +5 -0
  365. package/packages/pi-coding-agent/src/core/extensions/loader.test.ts +96 -0
  366. package/packages/pi-coding-agent/src/core/extensions/loader.ts +89 -0
  367. package/packages/pi-coding-agent/src/core/extensions/types.ts +44 -0
  368. package/packages/pi-coding-agent/src/core/fallback-resolver.test.ts +15 -2
  369. package/packages/pi-coding-agent/src/core/fallback-resolver.ts +2 -3
  370. package/packages/pi-coding-agent/src/core/lifecycle-hooks.ts +274 -0
  371. package/packages/pi-coding-agent/src/core/lsp/client.ts +83 -21
  372. package/packages/pi-coding-agent/src/core/model-registry-auth-mode.test.ts +288 -0
  373. package/packages/pi-coding-agent/src/core/model-registry.ts +39 -3
  374. package/packages/pi-coding-agent/src/core/package-commands.test.ts +240 -0
  375. package/packages/pi-coding-agent/src/core/package-commands.ts +310 -0
  376. package/packages/pi-coding-agent/src/core/sdk.ts +4 -0
  377. package/packages/pi-coding-agent/src/index.ts +7 -0
  378. package/packages/pi-coding-agent/src/main.ts +11 -232
  379. package/packages/pi-coding-agent/src/modes/interactive/components/footer.ts +20 -0
  380. package/packages/pi-coding-agent/src/modes/interactive/theme/theme.ts +9 -16
  381. package/packages/pi-coding-agent/src/modes/print-mode.ts +42 -32
  382. package/packages/pi-coding-agent/src/modes/rpc/rpc-client.ts +8 -2
  383. package/packages/pi-coding-agent/src/modes/rpc/rpc-mode.ts +2 -1
  384. package/pkg/dist/modes/interactive/theme/theme.d.ts +1 -1
  385. package/pkg/dist/modes/interactive/theme/theme.d.ts.map +1 -1
  386. package/pkg/dist/modes/interactive/theme/theme.js +8 -15
  387. package/pkg/dist/modes/interactive/theme/theme.js.map +1 -1
  388. package/pkg/package.json +1 -1
  389. package/src/resources/extensions/bg-shell/overlay.ts +4 -0
  390. package/src/resources/extensions/github-sync/sync.ts +2 -1
  391. package/src/resources/extensions/gsd/auto/loop-deps.ts +0 -8
  392. package/src/resources/extensions/gsd/auto/loop.ts +0 -2
  393. package/src/resources/extensions/gsd/auto/phases.ts +7 -20
  394. package/src/resources/extensions/gsd/auto/types.ts +0 -1
  395. package/src/resources/extensions/gsd/auto-dashboard.ts +20 -16
  396. package/src/resources/extensions/gsd/auto-direct-dispatch.ts +34 -19
  397. package/src/resources/extensions/gsd/auto-dispatch.ts +38 -21
  398. package/src/resources/extensions/gsd/auto-post-unit.ts +150 -15
  399. package/src/resources/extensions/gsd/auto-prompts.ts +186 -103
  400. package/src/resources/extensions/gsd/auto-recovery.ts +77 -142
  401. package/src/resources/extensions/gsd/auto-supervisor.ts +14 -0
  402. package/src/resources/extensions/gsd/auto-timeout-recovery.ts +6 -7
  403. package/src/resources/extensions/gsd/auto-verification.ts +4 -9
  404. package/src/resources/extensions/gsd/auto-worktree.ts +126 -30
  405. package/src/resources/extensions/gsd/auto.ts +0 -9
  406. package/src/resources/extensions/gsd/bootstrap/db-tools.ts +675 -4
  407. package/src/resources/extensions/gsd/bootstrap/dynamic-tools.ts +31 -3
  408. package/src/resources/extensions/gsd/bootstrap/register-hooks.ts +7 -0
  409. package/src/resources/extensions/gsd/commands/catalog.ts +3 -1
  410. package/src/resources/extensions/gsd/commands/handlers/ops.ts +15 -1
  411. package/src/resources/extensions/gsd/commands-handlers.ts +1 -1
  412. package/src/resources/extensions/gsd/commands-maintenance.ts +86 -3
  413. package/src/resources/extensions/gsd/dashboard-overlay.ts +17 -13
  414. package/src/resources/extensions/gsd/db-writer.ts +105 -4
  415. package/src/resources/extensions/gsd/dispatch-guard.ts +32 -30
  416. package/src/resources/extensions/gsd/doctor-checks.ts +25 -11
  417. package/src/resources/extensions/gsd/doctor-environment.ts +31 -0
  418. package/src/resources/extensions/gsd/doctor-types.ts +0 -23
  419. package/src/resources/extensions/gsd/doctor.ts +45 -295
  420. package/src/resources/extensions/gsd/file-watcher.ts +4 -1
  421. package/src/resources/extensions/gsd/files.ts +16 -220
  422. package/src/resources/extensions/gsd/git-service.ts +4 -0
  423. package/src/resources/extensions/gsd/gitignore.ts +4 -0
  424. package/src/resources/extensions/gsd/gsd-db.ts +1157 -370
  425. package/src/resources/extensions/gsd/guided-flow.ts +20 -8
  426. package/src/resources/extensions/gsd/markdown-renderer.ts +1098 -0
  427. package/src/resources/extensions/gsd/md-importer.ts +211 -2
  428. package/src/resources/extensions/gsd/native-git-bridge.ts +12 -1
  429. package/src/resources/extensions/gsd/parallel-eligibility.ts +14 -18
  430. package/src/resources/extensions/gsd/parallel-orchestrator.ts +43 -0
  431. package/src/resources/extensions/gsd/parsers-legacy.ts +271 -0
  432. package/src/resources/extensions/gsd/preferences-types.ts +3 -0
  433. package/src/resources/extensions/gsd/preferences-validation.ts +9 -0
  434. package/src/resources/extensions/gsd/preferences.ts +1 -0
  435. package/src/resources/extensions/gsd/prompts/complete-slice.md +22 -9
  436. package/src/resources/extensions/gsd/prompts/discuss.md +2 -2
  437. package/src/resources/extensions/gsd/prompts/execute-task.md +15 -5
  438. package/src/resources/extensions/gsd/prompts/guided-complete-slice.md +1 -1
  439. package/src/resources/extensions/gsd/prompts/guided-execute-task.md +1 -1
  440. package/src/resources/extensions/gsd/prompts/guided-plan-milestone.md +1 -1
  441. package/src/resources/extensions/gsd/prompts/plan-milestone.md +6 -10
  442. package/src/resources/extensions/gsd/prompts/plan-slice.md +4 -7
  443. package/src/resources/extensions/gsd/prompts/reactive-execute.md +3 -3
  444. package/src/resources/extensions/gsd/prompts/reassess-roadmap.md +6 -7
  445. package/src/resources/extensions/gsd/prompts/replan-slice.md +6 -6
  446. package/src/resources/extensions/gsd/reactive-graph.ts +33 -3
  447. package/src/resources/extensions/gsd/skill-health.ts +2 -1
  448. package/src/resources/extensions/gsd/state.ts +547 -29
  449. package/src/resources/extensions/gsd/tests/atomic-task-closeout.test.ts +8 -120
  450. package/src/resources/extensions/gsd/tests/auto-loop.test.ts +20 -11
  451. package/src/resources/extensions/gsd/tests/auto-preflight.test.ts +1 -1
  452. package/src/resources/extensions/gsd/tests/auto-recovery.test.ts +150 -5
  453. package/src/resources/extensions/gsd/tests/auto-stash-merge.test.ts +121 -0
  454. package/src/resources/extensions/gsd/tests/auto-worktree-milestone-merge.test.ts +15 -20
  455. package/src/resources/extensions/gsd/tests/complete-milestone.test.ts +1 -1
  456. package/src/resources/extensions/gsd/tests/complete-slice.test.ts +410 -0
  457. package/src/resources/extensions/gsd/tests/complete-task.test.ts +439 -0
  458. package/src/resources/extensions/gsd/tests/custom-engine-loop-integration.test.ts +0 -2
  459. package/src/resources/extensions/gsd/tests/derive-state-crossval.test.ts +527 -0
  460. package/src/resources/extensions/gsd/tests/derive-state-db.test.ts +593 -2
  461. package/src/resources/extensions/gsd/tests/dispatch-guard.test.ts +104 -57
  462. package/src/resources/extensions/gsd/tests/doctor-completion-deferral.test.ts +22 -80
  463. package/src/resources/extensions/gsd/tests/doctor-enhancements.test.ts +5 -7
  464. package/src/resources/extensions/gsd/tests/doctor-environment-worktree.test.ts +175 -0
  465. package/src/resources/extensions/gsd/tests/doctor-fixlevel.test.ts +38 -130
  466. package/src/resources/extensions/gsd/tests/doctor-roadmap-summary-atomicity.test.ts +38 -76
  467. package/src/resources/extensions/gsd/tests/doctor.test.ts +9 -19
  468. package/src/resources/extensions/gsd/tests/flag-file-db.test.ts +290 -0
  469. package/src/resources/extensions/gsd/tests/freeform-decisions.test.ts +240 -0
  470. package/src/resources/extensions/gsd/tests/git-service.test.ts +6 -2
  471. package/src/resources/extensions/gsd/tests/gsd-db.test.ts +1 -1
  472. package/src/resources/extensions/gsd/tests/gsd-recover.test.ts +462 -0
  473. package/src/resources/extensions/gsd/tests/idle-recovery.test.ts +4 -172
  474. package/src/resources/extensions/gsd/tests/integration-proof.test.ts +643 -0
  475. package/src/resources/extensions/gsd/tests/journal-integration.test.ts +0 -3
  476. package/src/resources/extensions/gsd/tests/markdown-renderer.test.ts +1205 -0
  477. package/src/resources/extensions/gsd/tests/md-importer.test.ts +1 -1
  478. package/src/resources/extensions/gsd/tests/memory-store.test.ts +2 -2
  479. package/src/resources/extensions/gsd/tests/migrate-hierarchy.test.ts +439 -0
  480. package/src/resources/extensions/gsd/tests/migrate-writer-integration.test.ts +2 -1
  481. package/src/resources/extensions/gsd/tests/migrate-writer.test.ts +2 -0
  482. package/src/resources/extensions/gsd/tests/parsers.test.ts +2 -1
  483. package/src/resources/extensions/gsd/tests/plan-milestone.test.ts +176 -113
  484. package/src/resources/extensions/gsd/tests/plan-slice-prompt.test.ts +7 -0
  485. package/src/resources/extensions/gsd/tests/plan-slice.test.ts +179 -0
  486. package/src/resources/extensions/gsd/tests/plan-task.test.ts +145 -0
  487. package/src/resources/extensions/gsd/tests/planning-crossval.test.ts +305 -0
  488. package/src/resources/extensions/gsd/tests/prompt-contracts.test.ts +139 -0
  489. package/src/resources/extensions/gsd/tests/reassess-handler.test.ts +325 -0
  490. package/src/resources/extensions/gsd/tests/replan-handler.test.ts +410 -0
  491. package/src/resources/extensions/gsd/tests/roadmap-slices.test.ts +1 -1
  492. package/src/resources/extensions/gsd/tests/rogue-file-detection.test.ts +296 -0
  493. package/src/resources/extensions/gsd/tests/schema-v9-sequence.test.ts +176 -0
  494. package/src/resources/extensions/gsd/tests/shared-wal.test.ts +216 -0
  495. package/src/resources/extensions/gsd/tests/token-cost-display.test.ts +118 -0
  496. package/src/resources/extensions/gsd/tests/tool-naming.test.ts +11 -3
  497. package/src/resources/extensions/gsd/tests/undo.test.ts +321 -1
  498. package/src/resources/extensions/gsd/tests/validate-milestone.test.ts +1 -1
  499. package/src/resources/extensions/gsd/tests/verification-evidence.test.ts +0 -142
  500. package/src/resources/extensions/gsd/tests/worktree-health-dispatch.test.ts +12 -5
  501. package/src/resources/extensions/gsd/tools/complete-milestone.ts +176 -0
  502. package/src/resources/extensions/gsd/tools/complete-slice.ts +300 -0
  503. package/src/resources/extensions/gsd/tools/complete-task.ts +245 -0
  504. package/src/resources/extensions/gsd/tools/plan-milestone.ts +249 -0
  505. package/src/resources/extensions/gsd/tools/plan-slice.ts +194 -0
  506. package/src/resources/extensions/gsd/tools/plan-task.ts +116 -0
  507. package/src/resources/extensions/gsd/tools/reassess-roadmap.ts +203 -0
  508. package/src/resources/extensions/gsd/tools/replan-slice.ts +192 -0
  509. package/src/resources/extensions/gsd/triage-resolution.ts +20 -1
  510. package/src/resources/extensions/gsd/types.ts +50 -0
  511. package/src/resources/extensions/gsd/undo.ts +247 -3
  512. package/src/resources/extensions/gsd/visualizer-data.ts +54 -17
  513. package/src/resources/extensions/gsd/workspace-index.ts +64 -46
  514. package/dist/resources/extensions/gsd/auto-observability.js +0 -56
  515. package/dist/resources/extensions/gsd/observability-validator.js +0 -422
  516. package/dist/resources/extensions/gsd/roadmap-mutations.js +0 -110
  517. package/dist/web/standalone/.next/static/VvclDCW6TAWjEyLU-EYL1/_buildManifest.js +0 -1
  518. package/dist/web/standalone/.next/static/chunks/app/_not-found/page-e07acdb7dd069836.js +0 -1
  519. package/dist/web/standalone/.next/static/chunks/app/api/bridge-terminal/stream/route-d07a2c023f1aef1e.js +0 -1
  520. package/dist/web/standalone/.next/static/chunks/app/api/browse-directories/route-d07a2c023f1aef1e.js +0 -1
  521. package/dist/web/standalone/.next/static/chunks/app/api/captures/route-d07a2c023f1aef1e.js +0 -1
  522. package/dist/web/standalone/.next/static/chunks/app/api/cleanup/route-d07a2c023f1aef1e.js +0 -1
  523. package/dist/web/standalone/.next/static/chunks/app/api/dev-mode/route-d07a2c023f1aef1e.js +0 -1
  524. package/dist/web/standalone/.next/static/chunks/app/api/doctor/route-d07a2c023f1aef1e.js +0 -1
  525. package/dist/web/standalone/.next/static/chunks/app/api/export-data/route-d07a2c023f1aef1e.js +0 -1
  526. package/dist/web/standalone/.next/static/chunks/app/api/files/route-d07a2c023f1aef1e.js +0 -1
  527. package/dist/web/standalone/.next/static/chunks/app/api/forensics/route-d07a2c023f1aef1e.js +0 -1
  528. package/dist/web/standalone/.next/static/chunks/app/api/git/route-d07a2c023f1aef1e.js +0 -1
  529. package/dist/web/standalone/.next/static/chunks/app/api/history/route-d07a2c023f1aef1e.js +0 -1
  530. package/dist/web/standalone/.next/static/chunks/app/api/hooks/route-d07a2c023f1aef1e.js +0 -1
  531. package/dist/web/standalone/.next/static/chunks/app/api/inspect/route-d07a2c023f1aef1e.js +0 -1
  532. package/dist/web/standalone/.next/static/chunks/app/api/knowledge/route-d07a2c023f1aef1e.js +0 -1
  533. package/dist/web/standalone/.next/static/chunks/app/api/live-state/route-d07a2c023f1aef1e.js +0 -1
  534. package/dist/web/standalone/.next/static/chunks/app/api/onboarding/route-d07a2c023f1aef1e.js +0 -1
  535. package/dist/web/standalone/.next/static/chunks/app/api/preferences/route-d07a2c023f1aef1e.js +0 -1
  536. package/dist/web/standalone/.next/static/chunks/app/api/projects/route-d07a2c023f1aef1e.js +0 -1
  537. package/dist/web/standalone/.next/static/chunks/app/api/recovery/route-d07a2c023f1aef1e.js +0 -1
  538. package/dist/web/standalone/.next/static/chunks/app/api/remote-questions/route-d07a2c023f1aef1e.js +0 -1
  539. package/dist/web/standalone/.next/static/chunks/app/api/session/browser/route-d07a2c023f1aef1e.js +0 -1
  540. package/dist/web/standalone/.next/static/chunks/app/api/session/command/route-d07a2c023f1aef1e.js +0 -1
  541. package/dist/web/standalone/.next/static/chunks/app/api/session/events/route-d07a2c023f1aef1e.js +0 -1
  542. package/dist/web/standalone/.next/static/chunks/app/api/session/manage/route-d07a2c023f1aef1e.js +0 -1
  543. package/dist/web/standalone/.next/static/chunks/app/api/settings-data/route-d07a2c023f1aef1e.js +0 -1
  544. package/dist/web/standalone/.next/static/chunks/app/api/shutdown/route-d07a2c023f1aef1e.js +0 -1
  545. package/dist/web/standalone/.next/static/chunks/app/api/skill-health/route-d07a2c023f1aef1e.js +0 -1
  546. package/dist/web/standalone/.next/static/chunks/app/api/steer/route-d07a2c023f1aef1e.js +0 -1
  547. package/dist/web/standalone/.next/static/chunks/app/api/terminal/input/route-d07a2c023f1aef1e.js +0 -1
  548. package/dist/web/standalone/.next/static/chunks/app/api/terminal/resize/route-d07a2c023f1aef1e.js +0 -1
  549. package/dist/web/standalone/.next/static/chunks/app/api/terminal/sessions/route-d07a2c023f1aef1e.js +0 -1
  550. package/dist/web/standalone/.next/static/chunks/app/api/terminal/stream/route-d07a2c023f1aef1e.js +0 -1
  551. package/dist/web/standalone/.next/static/chunks/app/api/terminal/upload/route-d07a2c023f1aef1e.js +0 -1
  552. package/dist/web/standalone/.next/static/chunks/app/api/undo/route-d07a2c023f1aef1e.js +0 -1
  553. package/dist/web/standalone/.next/static/chunks/app/api/update/route-d07a2c023f1aef1e.js +0 -1
  554. package/dist/web/standalone/.next/static/chunks/app/api/visualizer/route-d07a2c023f1aef1e.js +0 -1
  555. package/dist/web/standalone/.next/static/chunks/app/layout-745c6ed5fea5fb06.js +0 -1
  556. package/dist/web/standalone/.next/static/chunks/app/page-801b53eff6e83579.js +0 -1
  557. package/dist/web/standalone/.next/static/chunks/next/dist/client/components/builtin/app-error-d07a2c023f1aef1e.js +0 -1
  558. package/dist/web/standalone/.next/static/chunks/next/dist/client/components/builtin/forbidden-d07a2c023f1aef1e.js +0 -1
  559. package/dist/web/standalone/.next/static/chunks/next/dist/client/components/builtin/global-error-e6255954dccfcf0a.js +0 -1
  560. package/dist/web/standalone/.next/static/chunks/next/dist/client/components/builtin/not-found-d07a2c023f1aef1e.js +0 -1
  561. package/dist/web/standalone/.next/static/chunks/next/dist/client/components/builtin/unauthorized-d07a2c023f1aef1e.js +0 -1
  562. package/dist/web/standalone/.next/static/css/123c0bb039697968.css +0 -1
  563. package/src/resources/extensions/gsd/auto-observability.ts +0 -74
  564. package/src/resources/extensions/gsd/observability-validator.ts +0 -456
  565. package/src/resources/extensions/gsd/roadmap-mutations.ts +0 -134
  566. package/src/resources/extensions/gsd/tests/doctor-task-done-missing-summary-slice-loop.test.ts +0 -174
  567. package/src/resources/extensions/gsd/tests/plan-quality-validator.test.ts +0 -474
  568. /package/dist/web/standalone/.next/static/{VvclDCW6TAWjEyLU-EYL1 → fOnWQBjWXMKUs4bqTg530}/_ssgManifest.js +0 -0
@@ -0,0 +1,1205 @@
1
+ import { createTestContext } from './test-helpers.ts';
2
+ import * as path from 'node:path';
3
+ import * as os from 'node:os';
4
+ import * as fs from 'node:fs';
5
+ import {
6
+ openDatabase,
7
+ closeDatabase,
8
+ insertMilestone,
9
+ insertSlice,
10
+ insertTask,
11
+ insertArtifact,
12
+ getArtifact,
13
+ getAllMilestones,
14
+ getMilestoneSlices,
15
+ getSliceTasks,
16
+ updateSliceStatus,
17
+ _getAdapter,
18
+ } from '../gsd-db.ts';
19
+ import {
20
+ renderRoadmapCheckboxes,
21
+ renderPlanCheckboxes,
22
+ renderTaskSummary,
23
+ renderSliceSummary,
24
+ renderAllFromDb,
25
+ renderPlanFromDb,
26
+ renderTaskPlanFromDb,
27
+ detectStaleRenders,
28
+ repairStaleRenders,
29
+ } from '../markdown-renderer.ts';
30
+ import {
31
+ parseRoadmap,
32
+ parsePlan,
33
+ } from '../parsers-legacy.ts';
34
+ import {
35
+ parseSummary,
36
+ parseTaskPlanFile,
37
+ clearParseCache,
38
+ } from '../files.ts';
39
+ import { clearPathCache, _clearGsdRootCache } from '../paths.ts';
40
+ import { invalidateStateCache } from '../state.ts';
41
+
42
+ const { assertEq, assertTrue, assertMatch, report } = createTestContext();
43
+
44
+ // ═══════════════════════════════════════════════════════════════════════════
45
+ // Helpers
46
+ // ═══════════════════════════════════════════════════════════════════════════
47
+
48
+ function makeTmpDir(): string {
49
+ const dir = fs.mkdtempSync(path.join(os.tmpdir(), 'gsd-renderer-'));
50
+ fs.mkdirSync(path.join(dir, '.gsd'), { recursive: true });
51
+ return dir;
52
+ }
53
+
54
+ function cleanupDir(dir: string): void {
55
+ try {
56
+ fs.rmSync(dir, { recursive: true, force: true });
57
+ } catch { /* swallow */ }
58
+ }
59
+
60
+ function clearAllCaches(): void {
61
+ clearParseCache();
62
+ clearPathCache();
63
+ _clearGsdRootCache();
64
+ invalidateStateCache();
65
+ }
66
+
67
+ /**
68
+ * Create on-disk directory structure for a milestone/slice/task tree
69
+ * so that path resolvers work correctly.
70
+ */
71
+ function scaffoldDirs(tmpDir: string, mid: string, sliceIds: string[]): void {
72
+ const msDir = path.join(tmpDir, '.gsd', 'milestones', mid);
73
+ fs.mkdirSync(msDir, { recursive: true });
74
+
75
+ for (const sid of sliceIds) {
76
+ const sliceDir = path.join(msDir, 'slices', sid);
77
+ fs.mkdirSync(path.join(sliceDir, 'tasks'), { recursive: true });
78
+ }
79
+ }
80
+
81
+ // ─── Fixture: Roadmap Template ────────────────────────────────────────────
82
+
83
+ function makeRoadmapContent(slices: Array<{ id: string; title: string; done: boolean }>): string {
84
+ const lines: string[] = [];
85
+ lines.push('# M001 Roadmap');
86
+ lines.push('');
87
+ lines.push('**Vision:** Test milestone');
88
+ lines.push('');
89
+ lines.push('## Slices');
90
+ lines.push('');
91
+ for (const s of slices) {
92
+ const checkbox = s.done ? '[x]' : '[ ]';
93
+ lines.push(`- ${checkbox} **${s.id}: ${s.title}** \`risk:medium\` \`depends:[]\``);
94
+ }
95
+ lines.push('');
96
+ return lines.join('\n');
97
+ }
98
+
99
+ // ─── Fixture: Plan Template ───────────────────────────────────────────────
100
+
101
+ function makePlanContent(
102
+ sliceId: string,
103
+ tasks: Array<{ id: string; title: string; done: boolean }>,
104
+ ): string {
105
+ const lines: string[] = [];
106
+ lines.push(`# ${sliceId}: Test Slice`);
107
+ lines.push('');
108
+ lines.push('**Goal:** Test slice goal');
109
+ lines.push('**Demo:** Test demo');
110
+ lines.push('');
111
+ lines.push('## Must-Haves');
112
+ lines.push('');
113
+ lines.push('- Everything works');
114
+ lines.push('');
115
+ lines.push('## Tasks');
116
+ lines.push('');
117
+ for (const t of tasks) {
118
+ const checkbox = t.done ? '[x]' : '[ ]';
119
+ lines.push(`- ${checkbox} **${t.id}: ${t.title}** \`est:1h\``);
120
+ }
121
+ lines.push('');
122
+ return lines.join('\n');
123
+ }
124
+
125
+ // ─── Fixture: Task Summary Template ───────────────────────────────────────
126
+
127
+ function makeTaskSummaryContent(taskId: string): string {
128
+ return [
129
+ '---',
130
+ `id: ${taskId}`,
131
+ 'parent: S01',
132
+ 'milestone: M001',
133
+ 'duration: 45m',
134
+ 'verification_result: all-pass',
135
+ `completed_at: ${new Date().toISOString()}`,
136
+ 'blocker_discovered: false',
137
+ 'provides: []',
138
+ 'requires: []',
139
+ 'affects: []',
140
+ 'key_files:',
141
+ ' - src/test.ts',
142
+ 'key_decisions: []',
143
+ 'patterns_established: []',
144
+ 'drill_down_paths: []',
145
+ 'observability_surfaces: []',
146
+ '---',
147
+ '',
148
+ `# ${taskId}: Test Task Summary`,
149
+ '',
150
+ '**Implemented test functionality**',
151
+ '',
152
+ '## What Happened',
153
+ '',
154
+ 'Built the test feature.',
155
+ '',
156
+ '## Deviations',
157
+ '',
158
+ 'None.',
159
+ '',
160
+ '## Files Created/Modified',
161
+ '',
162
+ '- `src/test.ts` — main implementation',
163
+ '',
164
+ '## Verification Evidence',
165
+ '',
166
+ '| Command | Exit | Verdict | Duration |',
167
+ '|---------|------|---------|----------|',
168
+ '| `npm test` | 0 | ✅ pass | 2.1s |',
169
+ '',
170
+ ].join('\n');
171
+ }
172
+
173
+ // ═══════════════════════════════════════════════════════════════════════════
174
+ // DB Accessor Tests
175
+ // ═══════════════════════════════════════════════════════════════════════════
176
+
177
+ console.log('\n── markdown-renderer: DB accessor basics ──');
178
+
179
+ {
180
+ openDatabase(':memory:');
181
+
182
+ // getAllMilestones — empty
183
+ const empty = getAllMilestones();
184
+ assertEq(empty.length, 0, 'getAllMilestones returns empty when no milestones');
185
+
186
+ // Insert and retrieve
187
+ insertMilestone({ id: 'M001', title: 'Test MS', status: 'active' });
188
+ insertMilestone({ id: 'M002', title: 'Second MS', status: 'active' });
189
+
190
+ const all = getAllMilestones();
191
+ assertEq(all.length, 2, 'getAllMilestones returns 2 milestones');
192
+ assertEq(all[0].id, 'M001', 'first milestone is M001');
193
+ assertEq(all[1].id, 'M002', 'second milestone is M002');
194
+ assertEq(all[0].title, 'Test MS', 'milestone title correct');
195
+ assertEq(all[0].status, 'active', 'milestone status correct');
196
+
197
+ // getMilestoneSlices — empty
198
+ const noSlices = getMilestoneSlices('M001');
199
+ assertEq(noSlices.length, 0, 'getMilestoneSlices returns empty when no slices');
200
+
201
+ // Insert slices and retrieve
202
+ insertSlice({ id: 'S01', milestoneId: 'M001', title: 'Slice 1', status: 'complete' });
203
+ insertSlice({ id: 'S02', milestoneId: 'M001', title: 'Slice 2', status: 'pending' });
204
+ insertSlice({ id: 'S01', milestoneId: 'M002', title: 'M2 Slice', status: 'pending' });
205
+
206
+ const m1Slices = getMilestoneSlices('M001');
207
+ assertEq(m1Slices.length, 2, 'M001 has 2 slices');
208
+ assertEq(m1Slices[0].id, 'S01', 'first slice is S01');
209
+ assertEq(m1Slices[0].status, 'complete', 'S01 status is complete');
210
+ assertEq(m1Slices[1].id, 'S02', 'second slice is S02');
211
+ assertEq(m1Slices[1].status, 'pending', 'S02 status is pending');
212
+
213
+ const m2Slices = getMilestoneSlices('M002');
214
+ assertEq(m2Slices.length, 1, 'M002 has 1 slice');
215
+
216
+ closeDatabase();
217
+ }
218
+
219
+ console.log('\n── markdown-renderer: getArtifact accessor ──');
220
+
221
+ {
222
+ openDatabase(':memory:');
223
+
224
+ // Not found
225
+ const missing = getArtifact('nonexistent/path');
226
+ assertEq(missing, null, 'getArtifact returns null for missing path');
227
+
228
+ // Insert and retrieve
229
+ insertArtifact({
230
+ path: 'milestones/M001/M001-ROADMAP.md',
231
+ artifact_type: 'ROADMAP',
232
+ milestone_id: 'M001',
233
+ slice_id: null,
234
+ task_id: null,
235
+ full_content: '# Roadmap content',
236
+ });
237
+
238
+ const found = getArtifact('milestones/M001/M001-ROADMAP.md');
239
+ assertTrue(found !== null, 'getArtifact returns non-null for existing path');
240
+ assertEq(found!.artifact_type, 'ROADMAP', 'artifact type correct');
241
+ assertEq(found!.milestone_id, 'M001', 'milestone_id correct');
242
+ assertEq(found!.full_content, '# Roadmap content', 'content correct');
243
+
244
+ closeDatabase();
245
+ }
246
+
247
+ // ═══════════════════════════════════════════════════════════════════════════
248
+ // Roadmap Checkbox Round-Trip
249
+ // ═══════════════════════════════════════════════════════════════════════════
250
+
251
+ console.log('\n── markdown-renderer: renderRoadmapCheckboxes round-trip ──');
252
+
253
+ {
254
+ const tmpDir = makeTmpDir();
255
+ const dbPath = path.join(tmpDir, '.gsd', 'gsd.db');
256
+ openDatabase(dbPath);
257
+ clearAllCaches();
258
+
259
+ try {
260
+ scaffoldDirs(tmpDir, 'M001', ['S01', 'S02']);
261
+
262
+ // Seed DB with milestone and slices
263
+ insertMilestone({ id: 'M001', title: 'Test', status: 'active' });
264
+ insertSlice({ id: 'S01', milestoneId: 'M001', title: 'Core setup', status: 'complete' });
265
+ insertSlice({ id: 'S02', milestoneId: 'M001', title: 'Rendering', status: 'pending' });
266
+
267
+ // Write a roadmap file on disk with BOTH slices unchecked
268
+ const roadmapContent = makeRoadmapContent([
269
+ { id: 'S01', title: 'Core setup', done: false },
270
+ { id: 'S02', title: 'Rendering', done: false },
271
+ ]);
272
+ const roadmapPath = path.join(tmpDir, '.gsd', 'milestones', 'M001', 'M001-ROADMAP.md');
273
+ fs.writeFileSync(roadmapPath, roadmapContent);
274
+ clearAllCaches();
275
+
276
+ // Render — should set S01 [x] and leave S02 [ ]
277
+ const ok = await renderRoadmapCheckboxes(tmpDir, 'M001');
278
+ assertTrue(ok, 'renderRoadmapCheckboxes returns true');
279
+
280
+ // Read rendered file and parse
281
+ const rendered = fs.readFileSync(roadmapPath, 'utf-8');
282
+ clearAllCaches();
283
+ const parsed = parseRoadmap(rendered);
284
+
285
+ assertEq(parsed.slices.length, 2, 'roadmap has 2 slices after render');
286
+
287
+ const s01 = parsed.slices.find(s => s.id === 'S01');
288
+ const s02 = parsed.slices.find(s => s.id === 'S02');
289
+ assertTrue(!!s01, 'S01 found in parsed roadmap');
290
+ assertTrue(!!s02, 'S02 found in parsed roadmap');
291
+ assertTrue(s01!.done, 'S01 is checked (done) after render');
292
+ assertTrue(!s02!.done, 'S02 is unchecked (pending) after render');
293
+
294
+ // Verify artifact stored in DB
295
+ const artifact = getArtifact('milestones/M001/M001-ROADMAP.md');
296
+ assertTrue(artifact !== null, 'roadmap artifact stored in DB after render');
297
+ assertTrue(artifact!.full_content.includes('[x] **S01:'), 'DB artifact has S01 checked');
298
+ assertTrue(artifact!.full_content.includes('[ ] **S02:'), 'DB artifact has S02 unchecked');
299
+ } finally {
300
+ closeDatabase();
301
+ cleanupDir(tmpDir);
302
+ }
303
+ }
304
+
305
+ console.log('\n── markdown-renderer: renderRoadmapCheckboxes bidirectional ──');
306
+
307
+ {
308
+ const tmpDir = makeTmpDir();
309
+ const dbPath = path.join(tmpDir, '.gsd', 'gsd.db');
310
+ openDatabase(dbPath);
311
+ clearAllCaches();
312
+
313
+ try {
314
+ scaffoldDirs(tmpDir, 'M001', ['S01', 'S02']);
315
+
316
+ insertMilestone({ id: 'M001', title: 'Test', status: 'active' });
317
+ // S01 is PENDING in DB, but checked on disk — should be unchecked
318
+ insertSlice({ id: 'S01', milestoneId: 'M001', title: 'Core setup', status: 'pending' });
319
+ insertSlice({ id: 'S02', milestoneId: 'M001', title: 'Rendering', status: 'complete' });
320
+
321
+ // Write roadmap with S01 checked and S02 unchecked (opposite of DB state)
322
+ const roadmapContent = makeRoadmapContent([
323
+ { id: 'S01', title: 'Core setup', done: true },
324
+ { id: 'S02', title: 'Rendering', done: false },
325
+ ]);
326
+ const roadmapPath = path.join(tmpDir, '.gsd', 'milestones', 'M001', 'M001-ROADMAP.md');
327
+ fs.writeFileSync(roadmapPath, roadmapContent);
328
+ clearAllCaches();
329
+
330
+ const ok = await renderRoadmapCheckboxes(tmpDir, 'M001');
331
+ assertTrue(ok, 'bidirectional render returns true');
332
+
333
+ const rendered = fs.readFileSync(roadmapPath, 'utf-8');
334
+ clearAllCaches();
335
+ const parsed = parseRoadmap(rendered);
336
+
337
+ const s01 = parsed.slices.find(s => s.id === 'S01');
338
+ const s02 = parsed.slices.find(s => s.id === 'S02');
339
+ assertTrue(!s01!.done, 'S01 unchecked (DB says pending, was checked on disk)');
340
+ assertTrue(s02!.done, 'S02 checked (DB says complete, was unchecked on disk)');
341
+ } finally {
342
+ closeDatabase();
343
+ cleanupDir(tmpDir);
344
+ }
345
+ }
346
+
347
+ // ═══════════════════════════════════════════════════════════════════════════
348
+ // Plan Checkbox Round-Trip
349
+ // ═══════════════════════════════════════════════════════════════════════════
350
+
351
+ console.log('\n── markdown-renderer: renderPlanCheckboxes round-trip ──');
352
+
353
+ {
354
+ const tmpDir = makeTmpDir();
355
+ const dbPath = path.join(tmpDir, '.gsd', 'gsd.db');
356
+ openDatabase(dbPath);
357
+ clearAllCaches();
358
+
359
+ try {
360
+ scaffoldDirs(tmpDir, 'M001', ['S01']);
361
+
362
+ insertMilestone({ id: 'M001', title: 'Test', status: 'active' });
363
+ insertSlice({ id: 'S01', milestoneId: 'M001', title: 'Slice', status: 'pending' });
364
+ insertTask({ id: 'T01', sliceId: 'S01', milestoneId: 'M001', title: 'First task', status: 'done' });
365
+ insertTask({ id: 'T02', sliceId: 'S01', milestoneId: 'M001', title: 'Second task', status: 'done' });
366
+ insertTask({ id: 'T03', sliceId: 'S01', milestoneId: 'M001', title: 'Third task', status: 'pending' });
367
+
368
+ // Write plan with all tasks unchecked
369
+ const planContent = makePlanContent('S01', [
370
+ { id: 'T01', title: 'First task', done: false },
371
+ { id: 'T02', title: 'Second task', done: false },
372
+ { id: 'T03', title: 'Third task', done: false },
373
+ ]);
374
+ const planPath = path.join(tmpDir, '.gsd', 'milestones', 'M001', 'slices', 'S01', 'S01-PLAN.md');
375
+ fs.writeFileSync(planPath, planContent);
376
+ clearAllCaches();
377
+
378
+ const ok = await renderPlanCheckboxes(tmpDir, 'M001', 'S01');
379
+ assertTrue(ok, 'renderPlanCheckboxes returns true');
380
+
381
+ const rendered = fs.readFileSync(planPath, 'utf-8');
382
+ clearAllCaches();
383
+ const parsed = parsePlan(rendered);
384
+
385
+ assertEq(parsed.tasks.length, 3, 'plan has 3 tasks after render');
386
+
387
+ const t01 = parsed.tasks.find(t => t.id === 'T01');
388
+ const t02 = parsed.tasks.find(t => t.id === 'T02');
389
+ const t03 = parsed.tasks.find(t => t.id === 'T03');
390
+ assertTrue(t01!.done, 'T01 checked (done in DB)');
391
+ assertTrue(t02!.done, 'T02 checked (done in DB)');
392
+ assertTrue(!t03!.done, 'T03 unchecked (pending in DB)');
393
+ } finally {
394
+ closeDatabase();
395
+ cleanupDir(tmpDir);
396
+ }
397
+ }
398
+
399
+ console.log('\n── markdown-renderer: renderPlanCheckboxes bidirectional ──');
400
+
401
+ {
402
+ const tmpDir = makeTmpDir();
403
+ const dbPath = path.join(tmpDir, '.gsd', 'gsd.db');
404
+ openDatabase(dbPath);
405
+ clearAllCaches();
406
+
407
+ try {
408
+ scaffoldDirs(tmpDir, 'M001', ['S01']);
409
+
410
+ insertMilestone({ id: 'M001', title: 'Test', status: 'active' });
411
+ insertSlice({ id: 'S01', milestoneId: 'M001', title: 'Slice', status: 'pending' });
412
+ // T01 pending in DB but checked on disk
413
+ insertTask({ id: 'T01', sliceId: 'S01', milestoneId: 'M001', title: 'First task', status: 'pending' });
414
+ insertTask({ id: 'T02', sliceId: 'S01', milestoneId: 'M001', title: 'Second task', status: 'done' });
415
+
416
+ const planContent = makePlanContent('S01', [
417
+ { id: 'T01', title: 'First task', done: true }, // checked but DB says pending
418
+ { id: 'T02', title: 'Second task', done: false }, // unchecked but DB says done
419
+ ]);
420
+ const planPath = path.join(tmpDir, '.gsd', 'milestones', 'M001', 'slices', 'S01', 'S01-PLAN.md');
421
+ fs.writeFileSync(planPath, planContent);
422
+ clearAllCaches();
423
+
424
+ const ok = await renderPlanCheckboxes(tmpDir, 'M001', 'S01');
425
+ assertTrue(ok, 'bidirectional plan render returns true');
426
+
427
+ const rendered = fs.readFileSync(planPath, 'utf-8');
428
+ clearAllCaches();
429
+ const parsed = parsePlan(rendered);
430
+
431
+ const t01 = parsed.tasks.find(t => t.id === 'T01');
432
+ const t02 = parsed.tasks.find(t => t.id === 'T02');
433
+ assertTrue(!t01!.done, 'T01 unchecked (DB says pending, was checked)');
434
+ assertTrue(t02!.done, 'T02 checked (DB says done, was unchecked)');
435
+ } finally {
436
+ closeDatabase();
437
+ cleanupDir(tmpDir);
438
+ }
439
+ }
440
+
441
+ console.log('\n── markdown-renderer: renderPlanFromDb creates parse-compatible slice plan + task plan files ──');
442
+
443
+ {
444
+ const tmpDir = makeTmpDir();
445
+ const dbPath = path.join(tmpDir, '.gsd', 'gsd.db');
446
+ openDatabase(dbPath);
447
+ clearAllCaches();
448
+
449
+ try {
450
+ scaffoldDirs(tmpDir, 'M001', ['S02']);
451
+
452
+ insertMilestone({ id: 'M001', title: 'Milestone', status: 'active' });
453
+ insertSlice({
454
+ id: 'S02',
455
+ milestoneId: 'M001',
456
+ title: 'DB-backed planning',
457
+ status: 'pending',
458
+ demo: 'Rendered plans exist on disk.',
459
+ planning: {
460
+ goal: 'Render slice plans from DB state.',
461
+ successCriteria: '- Slice plan stays parse-compatible\n- Task plan files are regenerated',
462
+ proofLevel: 'integration',
463
+ integrationClosure: 'Wires DB planning rows to markdown artifacts.',
464
+ observabilityImpact: '- Run renderer contract tests\n- Inspect stale-render diagnostics on mismatch',
465
+ },
466
+ });
467
+ insertTask({
468
+ id: 'T01',
469
+ sliceId: 'S02',
470
+ milestoneId: 'M001',
471
+ title: 'Render slice plan',
472
+ status: 'pending',
473
+ planning: {
474
+ description: 'Implement the DB-backed slice plan renderer.',
475
+ estimate: '45m',
476
+ files: ['src/resources/extensions/gsd/markdown-renderer.ts'],
477
+ verify: 'node --test markdown-renderer.test.ts',
478
+ inputs: ['src/resources/extensions/gsd/markdown-renderer.ts'],
479
+ expectedOutput: ['src/resources/extensions/gsd/tests/markdown-renderer.test.ts'],
480
+ observabilityImpact: 'Renderer tests cover stale render failure paths.',
481
+ },
482
+ });
483
+ insertTask({
484
+ id: 'T02',
485
+ sliceId: 'S02',
486
+ milestoneId: 'M001',
487
+ title: 'Render task plan',
488
+ status: 'pending',
489
+ planning: {
490
+ description: 'Emit the task plan file with conservative frontmatter.',
491
+ estimate: '30m',
492
+ files: ['src/resources/extensions/gsd/files.ts'],
493
+ verify: 'node --test auto-recovery.test.ts',
494
+ inputs: ['src/resources/extensions/gsd/files.ts'],
495
+ expectedOutput: ['src/resources/extensions/gsd/tests/auto-recovery.test.ts'],
496
+ observabilityImpact: 'Missing task-plan files fail recovery verification.',
497
+ },
498
+ });
499
+
500
+ const rendered = await renderPlanFromDb(tmpDir, 'M001', 'S02');
501
+ assertTrue(fs.existsSync(rendered.planPath), 'slice plan written to disk');
502
+ assertEq(rendered.taskPlanPaths.length, 2, 'task plan paths returned for each task');
503
+ assertTrue(rendered.taskPlanPaths.every((p) => fs.existsSync(p)), 'all task plan files written to disk');
504
+
505
+ const planContent = fs.readFileSync(rendered.planPath, 'utf-8');
506
+ clearAllCaches();
507
+ const parsedPlan = parsePlan(planContent);
508
+ assertEq(parsedPlan.id, 'S02', 'rendered slice plan parses with correct slice id');
509
+ assertEq(parsedPlan.goal, 'Render slice plans from DB state.', 'rendered slice plan preserves goal');
510
+ assertEq(parsedPlan.demo, 'Rendered plans exist on disk.', 'rendered slice plan preserves demo');
511
+ assertEq(parsedPlan.mustHaves.length, 2, 'rendered slice plan exposes must-haves');
512
+ assertEq(parsedPlan.tasks.length, 2, 'rendered slice plan exposes all tasks');
513
+ assertEq(parsedPlan.tasks[0].id, 'T01', 'first task parses correctly');
514
+ assertTrue(parsedPlan.tasks[0].description.includes('DB-backed slice plan renderer'), 'task description preserved in slice plan');
515
+ assertEq(parsedPlan.tasks[0].files?.[0], 'src/resources/extensions/gsd/markdown-renderer.ts', 'files list preserved in slice plan');
516
+ assertEq(parsedPlan.tasks[0].verify, 'node --test markdown-renderer.test.ts', 'verify line preserved in slice plan');
517
+
518
+ const planArtifact = getArtifact('milestones/M001/slices/S02/S02-PLAN.md');
519
+ assertTrue(planArtifact !== null, 'slice plan artifact stored in DB');
520
+ assertTrue(planArtifact!.full_content.includes('## Tasks'), 'stored plan artifact contains task section');
521
+
522
+ const taskPlanPath = path.join(tmpDir, '.gsd', 'milestones', 'M001', 'slices', 'S02', 'tasks', 'T01-PLAN.md');
523
+ const taskPlanContent = fs.readFileSync(taskPlanPath, 'utf-8');
524
+ const taskPlanFile = parseTaskPlanFile(taskPlanContent);
525
+ assertEq(taskPlanFile.frontmatter.estimated_steps, 1, 'task plan frontmatter exposes estimated_steps');
526
+ assertEq(taskPlanFile.frontmatter.estimated_files, 1, 'task plan frontmatter exposes estimated_files');
527
+ assertEq(taskPlanFile.frontmatter.skills_used.length, 0, 'task plan frontmatter uses conservative empty skills list');
528
+ assertMatch(taskPlanContent, /^# T01: Render slice plan/m, 'task plan renders task heading');
529
+ assertMatch(taskPlanContent, /^## Inputs$/m, 'task plan renders Inputs section');
530
+ assertMatch(taskPlanContent, /^## Expected Output$/m, 'task plan renders Expected Output section');
531
+ assertMatch(taskPlanContent, /^## Verification$/m, 'task plan renders Verification section');
532
+
533
+ const taskArtifact = getArtifact('milestones/M001/slices/S02/tasks/T01-PLAN.md');
534
+ assertTrue(taskArtifact !== null, 'task plan artifact stored in DB');
535
+ assertTrue(taskArtifact!.full_content.includes('skills_used: []'), 'stored task plan artifact preserves conservative skills_used');
536
+ } finally {
537
+ closeDatabase();
538
+ cleanupDir(tmpDir);
539
+ }
540
+ }
541
+
542
+ console.log('\n── markdown-renderer: renderTaskPlanFromDb throws for missing task ──');
543
+
544
+ {
545
+ const tmpDir = makeTmpDir();
546
+ const dbPath = path.join(tmpDir, '.gsd', 'gsd.db');
547
+ openDatabase(dbPath);
548
+ clearAllCaches();
549
+
550
+ try {
551
+ scaffoldDirs(tmpDir, 'M001', ['S02']);
552
+ insertMilestone({ id: 'M001', title: 'Milestone', status: 'active' });
553
+ insertSlice({ id: 'S02', milestoneId: 'M001', title: 'Slice', status: 'pending' });
554
+
555
+ let threw = false;
556
+ try {
557
+ await renderTaskPlanFromDb(tmpDir, 'M001', 'S02', 'T99');
558
+ } catch (error) {
559
+ threw = true;
560
+ assertMatch(String((error as Error).message), /task M001\/S02\/T99 not found/, 'renderTaskPlanFromDb should fail clearly when task row is missing');
561
+ }
562
+ assertTrue(threw, 'renderTaskPlanFromDb throws when the task row is missing');
563
+ } finally {
564
+ closeDatabase();
565
+ cleanupDir(tmpDir);
566
+ }
567
+ }
568
+
569
+
570
+ // ═══════════════════════════════════════════════════════════════════════════
571
+ // Task Summary Rendering
572
+ // ═══════════════════════════════════════════════════════════════════════════
573
+
574
+ console.log('\n── markdown-renderer: renderTaskSummary round-trip ──');
575
+
576
+ {
577
+ const tmpDir = makeTmpDir();
578
+ const dbPath = path.join(tmpDir, '.gsd', 'gsd.db');
579
+ openDatabase(dbPath);
580
+ clearAllCaches();
581
+
582
+ try {
583
+ scaffoldDirs(tmpDir, 'M001', ['S01']);
584
+
585
+ insertMilestone({ id: 'M001', title: 'Test', status: 'active' });
586
+ insertSlice({ id: 'S01', milestoneId: 'M001', title: 'Slice', status: 'pending' });
587
+
588
+ const summaryContent = makeTaskSummaryContent('T01');
589
+ insertTask({
590
+ id: 'T01',
591
+ sliceId: 'S01',
592
+ milestoneId: 'M001',
593
+ title: 'Test Task',
594
+ status: 'done',
595
+ fullSummaryMd: summaryContent,
596
+ });
597
+
598
+ const ok = await renderTaskSummary(tmpDir, 'M001', 'S01', 'T01');
599
+ assertTrue(ok, 'renderTaskSummary returns true');
600
+
601
+ // Verify file exists on disk
602
+ const summaryPath = path.join(
603
+ tmpDir, '.gsd', 'milestones', 'M001', 'slices', 'S01', 'tasks', 'T01-SUMMARY.md',
604
+ );
605
+ assertTrue(fs.existsSync(summaryPath), 'T01-SUMMARY.md written to disk');
606
+
607
+ // Parse and verify
608
+ const rendered = fs.readFileSync(summaryPath, 'utf-8');
609
+ clearAllCaches();
610
+ const parsed = parseSummary(rendered);
611
+ assertEq(parsed.frontmatter.id, 'T01', 'parsed summary has correct id');
612
+ assertEq(parsed.frontmatter.parent, 'S01', 'parsed summary has correct parent');
613
+ assertEq(parsed.frontmatter.milestone, 'M001', 'parsed summary has correct milestone');
614
+ assertEq(parsed.frontmatter.duration, '45m', 'parsed summary has correct duration');
615
+ assertTrue(parsed.title.includes('T01'), 'parsed summary title contains task ID');
616
+ assertTrue(parsed.whatHappened.includes('Built the test feature'), 'whatHappened content preserved');
617
+ } finally {
618
+ closeDatabase();
619
+ cleanupDir(tmpDir);
620
+ }
621
+ }
622
+
623
+ console.log('\n── markdown-renderer: renderTaskSummary skips empty ──');
624
+
625
+ {
626
+ const tmpDir = makeTmpDir();
627
+ const dbPath = path.join(tmpDir, '.gsd', 'gsd.db');
628
+ openDatabase(dbPath);
629
+ clearAllCaches();
630
+
631
+ try {
632
+ scaffoldDirs(tmpDir, 'M001', ['S01']);
633
+
634
+ insertMilestone({ id: 'M001', title: 'Test', status: 'active' });
635
+ insertSlice({ id: 'S01', milestoneId: 'M001', title: 'Slice', status: 'pending' });
636
+ insertTask({
637
+ id: 'T01',
638
+ sliceId: 'S01',
639
+ milestoneId: 'M001',
640
+ title: 'Task without summary',
641
+ status: 'pending',
642
+ fullSummaryMd: '', // empty summary
643
+ });
644
+
645
+ const ok = await renderTaskSummary(tmpDir, 'M001', 'S01', 'T01');
646
+ assertTrue(!ok, 'renderTaskSummary returns false for empty summary');
647
+ } finally {
648
+ closeDatabase();
649
+ cleanupDir(tmpDir);
650
+ }
651
+ }
652
+
653
+ // ═══════════════════════════════════════════════════════════════════════════
654
+ // Slice Summary Rendering
655
+ // ═══════════════════════════════════════════════════════════════════════════
656
+
657
+ console.log('\n── markdown-renderer: renderSliceSummary round-trip ──');
658
+
659
+ {
660
+ const tmpDir = makeTmpDir();
661
+ const dbPath = path.join(tmpDir, '.gsd', 'gsd.db');
662
+ openDatabase(dbPath);
663
+ clearAllCaches();
664
+
665
+ try {
666
+ scaffoldDirs(tmpDir, 'M001', ['S01']);
667
+
668
+ insertMilestone({ id: 'M001', title: 'Test', status: 'active' });
669
+ insertSlice({ id: 'S01', milestoneId: 'M001', title: 'Slice', status: 'complete' });
670
+
671
+ // Update slice with summary and UAT content
672
+ // Since insertSlice uses INSERT OR IGNORE, we need to set the content via raw adapter
673
+ const db = await import('../gsd-db.ts');
674
+ const adapter = db._getAdapter()!;
675
+ adapter.prepare(
676
+ `UPDATE slices SET full_summary_md = :sm, full_uat_md = :um WHERE milestone_id = 'M001' AND id = 'S01'`,
677
+ ).run({
678
+ ':sm': '---\nid: S01\nparent: M001\nmilestone: M001\nduration: 2h\nverification_result: all-pass\ncompleted_at: 2025-01-01\nblocker_discovered: false\nprovides: []\nrequires: []\naffects: []\nkey_files:\n - src/index.ts\nkey_decisions: []\npatterns_established: []\ndrill_down_paths: []\nobservability_surfaces: []\n---\n\n# S01: Test Slice Summary\n\n**Completed core functionality**\n\n## What Happened\n\nBuilt the slice.\n\n## Deviations\n\nNone.\n',
679
+ ':um': '# S01 UAT\n\n## UAT Type\n\n- UAT mode: artifact-driven\n\n## Checks\n\n- All tests pass\n',
680
+ });
681
+
682
+ const ok = await renderSliceSummary(tmpDir, 'M001', 'S01');
683
+ assertTrue(ok, 'renderSliceSummary returns true');
684
+
685
+ // Verify SUMMARY file
686
+ const summaryPath = path.join(
687
+ tmpDir, '.gsd', 'milestones', 'M001', 'slices', 'S01', 'S01-SUMMARY.md',
688
+ );
689
+ assertTrue(fs.existsSync(summaryPath), 'S01-SUMMARY.md written to disk');
690
+
691
+ const summaryContent = fs.readFileSync(summaryPath, 'utf-8');
692
+ assertTrue(summaryContent.includes('Test Slice Summary'), 'summary content correct');
693
+
694
+ // Verify UAT file
695
+ const uatPath = path.join(
696
+ tmpDir, '.gsd', 'milestones', 'M001', 'slices', 'S01', 'S01-UAT.md',
697
+ );
698
+ assertTrue(fs.existsSync(uatPath), 'S01-UAT.md written to disk');
699
+
700
+ const uatContent = fs.readFileSync(uatPath, 'utf-8');
701
+ assertTrue(uatContent.includes('artifact-driven'), 'UAT content correct');
702
+ } finally {
703
+ closeDatabase();
704
+ cleanupDir(tmpDir);
705
+ }
706
+ }
707
+
708
+ // ═══════════════════════════════════════════════════════════════════════════
709
+ // renderAllFromDb
710
+ // ═══════════════════════════════════════════════════════════════════════════
711
+
712
+ console.log('\n── markdown-renderer: renderAllFromDb produces all files ──');
713
+
714
+ {
715
+ const tmpDir = makeTmpDir();
716
+ const dbPath = path.join(tmpDir, '.gsd', 'gsd.db');
717
+ openDatabase(dbPath);
718
+ clearAllCaches();
719
+
720
+ try {
721
+ // Setup: 2 milestones, M001 has 2 slices with tasks, M002 has 1 slice
722
+ scaffoldDirs(tmpDir, 'M001', ['S01', 'S02']);
723
+ scaffoldDirs(tmpDir, 'M002', ['S01']);
724
+
725
+ insertMilestone({ id: 'M001', title: 'First', status: 'active' });
726
+ insertMilestone({ id: 'M002', title: 'Second', status: 'active' });
727
+
728
+ insertSlice({ id: 'S01', milestoneId: 'M001', title: 'Core', status: 'complete' });
729
+ insertSlice({ id: 'S02', milestoneId: 'M001', title: 'Render', status: 'pending' });
730
+ insertSlice({ id: 'S01', milestoneId: 'M002', title: 'Future', status: 'pending' });
731
+
732
+ insertTask({ id: 'T01', sliceId: 'S01', milestoneId: 'M001', title: 'DB', status: 'done', fullSummaryMd: makeTaskSummaryContent('T01') });
733
+ insertTask({ id: 'T01', sliceId: 'S02', milestoneId: 'M001', title: 'Renderer', status: 'pending' });
734
+ insertTask({ id: 'T01', sliceId: 'S01', milestoneId: 'M002', title: 'Future task', status: 'pending' });
735
+
736
+ // Write roadmap and plan files on disk
737
+ const roadmap1 = makeRoadmapContent([
738
+ { id: 'S01', title: 'Core', done: false },
739
+ { id: 'S02', title: 'Render', done: false },
740
+ ]);
741
+ fs.writeFileSync(
742
+ path.join(tmpDir, '.gsd', 'milestones', 'M001', 'M001-ROADMAP.md'),
743
+ roadmap1,
744
+ );
745
+
746
+ const roadmap2 = makeRoadmapContent([
747
+ { id: 'S01', title: 'Future', done: false },
748
+ ]);
749
+ fs.writeFileSync(
750
+ path.join(tmpDir, '.gsd', 'milestones', 'M002', 'M002-ROADMAP.md'),
751
+ roadmap2,
752
+ );
753
+
754
+ const plan1 = makePlanContent('S01', [
755
+ { id: 'T01', title: 'DB', done: false },
756
+ ]);
757
+ fs.writeFileSync(
758
+ path.join(tmpDir, '.gsd', 'milestones', 'M001', 'slices', 'S01', 'S01-PLAN.md'),
759
+ plan1,
760
+ );
761
+
762
+ const plan2 = makePlanContent('S02', [
763
+ { id: 'T01', title: 'Renderer', done: false },
764
+ ]);
765
+ fs.writeFileSync(
766
+ path.join(tmpDir, '.gsd', 'milestones', 'M001', 'slices', 'S02', 'S02-PLAN.md'),
767
+ plan2,
768
+ );
769
+
770
+ const plan3 = makePlanContent('S01', [
771
+ { id: 'T01', title: 'Future task', done: false },
772
+ ]);
773
+ fs.writeFileSync(
774
+ path.join(tmpDir, '.gsd', 'milestones', 'M002', 'slices', 'S01', 'S01-PLAN.md'),
775
+ plan3,
776
+ );
777
+
778
+ clearAllCaches();
779
+
780
+ const result = await renderAllFromDb(tmpDir);
781
+
782
+ assertTrue(result.rendered > 0, 'renderAllFromDb rendered some files');
783
+ assertEq(result.errors.length, 0, 'renderAllFromDb had no errors');
784
+
785
+ // Verify M001 roadmap has S01 checked
786
+ const m1Roadmap = fs.readFileSync(
787
+ path.join(tmpDir, '.gsd', 'milestones', 'M001', 'M001-ROADMAP.md'), 'utf-8',
788
+ );
789
+ clearAllCaches();
790
+ const parsed1 = parseRoadmap(m1Roadmap);
791
+ const s01 = parsed1.slices.find(s => s.id === 'S01');
792
+ assertTrue(s01!.done, 'M001 S01 checked after renderAll');
793
+
794
+ // Verify M001/S01 plan has T01 checked
795
+ const m1s1Plan = fs.readFileSync(
796
+ path.join(tmpDir, '.gsd', 'milestones', 'M001', 'slices', 'S01', 'S01-PLAN.md'), 'utf-8',
797
+ );
798
+ clearAllCaches();
799
+ const parsedPlan = parsePlan(m1s1Plan);
800
+ assertTrue(parsedPlan.tasks[0].done, 'M001/S01 T01 checked after renderAll');
801
+
802
+ // Verify task summary written
803
+ const taskSummaryPath = path.join(
804
+ tmpDir, '.gsd', 'milestones', 'M001', 'slices', 'S01', 'tasks', 'T01-SUMMARY.md',
805
+ );
806
+ assertTrue(fs.existsSync(taskSummaryPath), 'T01 summary written by renderAll');
807
+ } finally {
808
+ closeDatabase();
809
+ cleanupDir(tmpDir);
810
+ }
811
+ }
812
+
813
+ // ═══════════════════════════════════════════════════════════════════════════
814
+ // Graceful Degradation (Disk Fallback)
815
+ // ═══════════════════════════════════════════════════════════════════════════
816
+
817
+ console.log('\n── markdown-renderer: graceful fallback reads from disk when artifact not in DB ──');
818
+
819
+ {
820
+ const tmpDir = makeTmpDir();
821
+ const dbPath = path.join(tmpDir, '.gsd', 'gsd.db');
822
+ openDatabase(dbPath);
823
+ clearAllCaches();
824
+
825
+ try {
826
+ scaffoldDirs(tmpDir, 'M001', ['S01']);
827
+
828
+ insertMilestone({ id: 'M001', title: 'Test', status: 'active' });
829
+ insertSlice({ id: 'S01', milestoneId: 'M001', title: 'Core', status: 'complete' });
830
+
831
+ // Write roadmap to disk but NOT in artifacts DB
832
+ const roadmapContent = makeRoadmapContent([
833
+ { id: 'S01', title: 'Core', done: false },
834
+ ]);
835
+ const roadmapPath = path.join(tmpDir, '.gsd', 'milestones', 'M001', 'M001-ROADMAP.md');
836
+ fs.writeFileSync(roadmapPath, roadmapContent);
837
+ clearAllCaches();
838
+
839
+ // Verify no artifact in DB
840
+ const before = getArtifact('milestones/M001/M001-ROADMAP.md');
841
+ assertEq(before, null, 'artifact not in DB before render');
842
+
843
+ // Render — should read from disk, store in DB
844
+ const ok = await renderRoadmapCheckboxes(tmpDir, 'M001');
845
+ assertTrue(ok, 'render succeeds with disk fallback');
846
+
847
+ // Verify artifact now in DB (stored after reading from disk)
848
+ const after = getArtifact('milestones/M001/M001-ROADMAP.md');
849
+ assertTrue(after !== null, 'artifact stored in DB after disk fallback render');
850
+ assertTrue(after!.full_content.includes('[x] **S01:'), 'DB artifact reflects rendered state');
851
+ } finally {
852
+ closeDatabase();
853
+ cleanupDir(tmpDir);
854
+ }
855
+ }
856
+
857
+ // ═══════════════════════════════════════════════════════════════════════════
858
+ // stderr warnings (graceful degradation diagnostics)
859
+ // ═══════════════════════════════════════════════════════════════════════════
860
+
861
+ console.log('\n── markdown-renderer: stderr warning on missing content ──');
862
+
863
+ {
864
+ openDatabase(':memory:');
865
+
866
+ // No milestone/slices in DB, no files on disk — should return false and emit stderr
867
+ insertMilestone({ id: 'M001', title: 'Test', status: 'active' });
868
+ // No slices inserted — should warn about no slices
869
+
870
+ const ok = await renderRoadmapCheckboxes('/nonexistent/path', 'M001');
871
+ assertTrue(!ok, 'returns false when no slices in DB');
872
+
873
+ closeDatabase();
874
+ }
875
+
876
+ // ═══════════════════════════════════════════════════════════════════════════
877
+ // Stale Detection — Plan Checkbox Mismatch
878
+ // ═══════════════════════════════════════════════════════════════════════════
879
+
880
+ console.log('\n── markdown-renderer: detectStaleRenders finds plan checkbox mismatch ──');
881
+
882
+ {
883
+ const tmpDir = makeTmpDir();
884
+ const dbPath = path.join(tmpDir, '.gsd', 'gsd.db');
885
+ openDatabase(dbPath);
886
+ clearAllCaches();
887
+
888
+ try {
889
+ scaffoldDirs(tmpDir, 'M001', ['S01']);
890
+
891
+ insertMilestone({ id: 'M001', title: 'Test', status: 'active' });
892
+ insertSlice({ id: 'S01', milestoneId: 'M001', title: 'Slice', status: 'pending' });
893
+
894
+ // T01 is done, T02 is also done in DB
895
+ insertTask({ id: 'T01', sliceId: 'S01', milestoneId: 'M001', title: 'First task', status: 'done' });
896
+ insertTask({ id: 'T02', sliceId: 'S01', milestoneId: 'M001', title: 'Second task', status: 'done' });
897
+
898
+ // Write plan with T01 checked but T02 unchecked
899
+ // T01 matches DB (done + checked) but T02 is stale (done but unchecked)
900
+ const planContent = makePlanContent('S01', [
901
+ { id: 'T01', title: 'First task', done: true },
902
+ { id: 'T02', title: 'Second task', done: false },
903
+ ]);
904
+ const planPath = path.join(tmpDir, '.gsd', 'milestones', 'M001', 'slices', 'S01', 'S01-PLAN.md');
905
+ fs.writeFileSync(planPath, planContent);
906
+ clearAllCaches();
907
+
908
+ // Render T01 to sync it, but leave T02 out of sync
909
+ // Actually, the plan was written with T01 already checked.
910
+ // The stale detection should find T02 as stale.
911
+ const stale = detectStaleRenders(tmpDir);
912
+
913
+ assertTrue(stale.length > 0, 'detectStaleRenders should find stale entries');
914
+ const t02Stale = stale.find(s => s.reason.includes('T02'));
915
+ assertTrue(!!t02Stale, 'should detect T02 as stale (done in DB, unchecked in plan)');
916
+ assertTrue(t02Stale!.reason.includes('done in DB but unchecked'), 'reason should explain the mismatch');
917
+
918
+ // T01 should NOT be stale — it's checked and done
919
+ const t01Stale = stale.find(s => s.reason.includes('T01'));
920
+ assertEq(t01Stale, undefined, 'T01 should not be stale (done and checked)');
921
+ } finally {
922
+ closeDatabase();
923
+ cleanupDir(tmpDir);
924
+ }
925
+ }
926
+
927
+ // ═══════════════════════════════════════════════════════════════════════════
928
+ // Stale Repair — Plan Checkbox
929
+ // ═══════════════════════════════════════════════════════════════════════════
930
+
931
+ console.log('\n── markdown-renderer: repairStaleRenders fixes plan and second detect returns empty ──');
932
+
933
+ {
934
+ const tmpDir = makeTmpDir();
935
+ const dbPath = path.join(tmpDir, '.gsd', 'gsd.db');
936
+ openDatabase(dbPath);
937
+ clearAllCaches();
938
+
939
+ try {
940
+ scaffoldDirs(tmpDir, 'M001', ['S01']);
941
+
942
+ insertMilestone({ id: 'M001', title: 'Test', status: 'active' });
943
+ insertSlice({ id: 'S01', milestoneId: 'M001', title: 'Slice', status: 'pending' });
944
+
945
+ insertTask({ id: 'T01', sliceId: 'S01', milestoneId: 'M001', title: 'First task', status: 'done' });
946
+ insertTask({ id: 'T02', sliceId: 'S01', milestoneId: 'M001', title: 'Second task', status: 'done' });
947
+
948
+ // Write plan with both tasks unchecked (both are stale since DB says done)
949
+ const planContent = makePlanContent('S01', [
950
+ { id: 'T01', title: 'First task', done: false },
951
+ { id: 'T02', title: 'Second task', done: false },
952
+ ]);
953
+ const planPath = path.join(tmpDir, '.gsd', 'milestones', 'M001', 'slices', 'S01', 'S01-PLAN.md');
954
+ fs.writeFileSync(planPath, planContent);
955
+ clearAllCaches();
956
+
957
+ // Verify stale before repair
958
+ const staleBefore = detectStaleRenders(tmpDir);
959
+ assertTrue(staleBefore.length > 0, 'should have stale entries before repair');
960
+
961
+ // Repair
962
+ const repaired = await repairStaleRenders(tmpDir);
963
+ assertTrue(repaired > 0, 'repairStaleRenders should repair at least 1 file');
964
+
965
+ // After repair, detect again — should be empty
966
+ clearAllCaches();
967
+ const staleAfter = detectStaleRenders(tmpDir);
968
+ assertEq(staleAfter.length, 0, 'detectStaleRenders should return empty after repair');
969
+
970
+ // Verify the plan file was actually updated
971
+ const repairedContent = fs.readFileSync(planPath, 'utf-8');
972
+ assertTrue(repairedContent.includes('[x] **T01:'), 'T01 should be checked after repair');
973
+ assertTrue(repairedContent.includes('[x] **T02:'), 'T02 should be checked after repair');
974
+ } finally {
975
+ closeDatabase();
976
+ cleanupDir(tmpDir);
977
+ }
978
+ }
979
+
980
+ // ═══════════════════════════════════════════════════════════════════════════
981
+ // Stale Detection — Roadmap Checkbox Mismatch
982
+ // ═══════════════════════════════════════════════════════════════════════════
983
+
984
+ console.log('\n── markdown-renderer: detectStaleRenders finds roadmap checkbox mismatch ──');
985
+
986
+ {
987
+ const tmpDir = makeTmpDir();
988
+ const dbPath = path.join(tmpDir, '.gsd', 'gsd.db');
989
+ openDatabase(dbPath);
990
+ clearAllCaches();
991
+
992
+ try {
993
+ scaffoldDirs(tmpDir, 'M001', ['S01', 'S02']);
994
+
995
+ insertMilestone({ id: 'M001', title: 'Test', status: 'active' });
996
+ insertSlice({ id: 'S01', milestoneId: 'M001', title: 'Core', status: 'complete' });
997
+ insertSlice({ id: 'S02', milestoneId: 'M001', title: 'Render', status: 'pending' });
998
+
999
+ // Write roadmap with both slices unchecked (S01 is stale — complete in DB but unchecked)
1000
+ const roadmapContent = makeRoadmapContent([
1001
+ { id: 'S01', title: 'Core', done: false },
1002
+ { id: 'S02', title: 'Render', done: false },
1003
+ ]);
1004
+ const roadmapPath = path.join(tmpDir, '.gsd', 'milestones', 'M001', 'M001-ROADMAP.md');
1005
+ fs.writeFileSync(roadmapPath, roadmapContent);
1006
+ clearAllCaches();
1007
+
1008
+ const stale = detectStaleRenders(tmpDir);
1009
+ const s01Stale = stale.find(s => s.reason.includes('S01'));
1010
+ assertTrue(!!s01Stale, 'should detect S01 as stale (complete in DB, unchecked in roadmap)');
1011
+
1012
+ const s02Stale = stale.find(s => s.reason.includes('S02'));
1013
+ assertEq(s02Stale, undefined, 'S02 should not be stale (pending and unchecked — matches)');
1014
+ } finally {
1015
+ closeDatabase();
1016
+ cleanupDir(tmpDir);
1017
+ }
1018
+ }
1019
+
1020
+ // ═══════════════════════════════════════════════════════════════════════════
1021
+ // Stale Detection — Missing Task Summary
1022
+ // ═══════════════════════════════════════════════════════════════════════════
1023
+
1024
+ console.log('\n── markdown-renderer: detectStaleRenders finds missing task summary ──');
1025
+
1026
+ {
1027
+ const tmpDir = makeTmpDir();
1028
+ const dbPath = path.join(tmpDir, '.gsd', 'gsd.db');
1029
+ openDatabase(dbPath);
1030
+ clearAllCaches();
1031
+
1032
+ try {
1033
+ scaffoldDirs(tmpDir, 'M001', ['S01']);
1034
+
1035
+ insertMilestone({ id: 'M001', title: 'Test', status: 'active' });
1036
+ insertSlice({ id: 'S01', milestoneId: 'M001', title: 'Slice', status: 'pending' });
1037
+
1038
+ // Task is done with full_summary_md, but no SUMMARY.md on disk
1039
+ const summaryContent = makeTaskSummaryContent('T01');
1040
+ insertTask({
1041
+ id: 'T01',
1042
+ sliceId: 'S01',
1043
+ milestoneId: 'M001',
1044
+ title: 'Task',
1045
+ status: 'done',
1046
+ fullSummaryMd: summaryContent,
1047
+ });
1048
+
1049
+ // Also write a plan so plan detection doesn't trigger (T01 is done but not checked)
1050
+ // We need a plan file so task plan detection works — but we specifically want to test
1051
+ // the missing summary case, so write plan with T01 checked
1052
+ const planContent = makePlanContent('S01', [
1053
+ { id: 'T01', title: 'Task', done: true },
1054
+ ]);
1055
+ const planPath = path.join(tmpDir, '.gsd', 'milestones', 'M001', 'slices', 'S01', 'S01-PLAN.md');
1056
+ fs.writeFileSync(planPath, planContent);
1057
+ clearAllCaches();
1058
+
1059
+ const stale = detectStaleRenders(tmpDir);
1060
+ const summaryStale = stale.find(s => s.reason.includes('SUMMARY.md missing'));
1061
+ assertTrue(!!summaryStale, 'should detect missing T01-SUMMARY.md');
1062
+ assertTrue(summaryStale!.reason.includes('T01'), 'reason should mention T01');
1063
+ } finally {
1064
+ closeDatabase();
1065
+ cleanupDir(tmpDir);
1066
+ }
1067
+ }
1068
+
1069
+ // ═══════════════════════════════════════════════════════════════════════════
1070
+ // Stale Repair — Missing Task Summary
1071
+ // ═══════════════════════════════════════════════════════════════════════════
1072
+
1073
+ console.log('\n── markdown-renderer: repairStaleRenders writes missing task summary ──');
1074
+
1075
+ {
1076
+ const tmpDir = makeTmpDir();
1077
+ const dbPath = path.join(tmpDir, '.gsd', 'gsd.db');
1078
+ openDatabase(dbPath);
1079
+ clearAllCaches();
1080
+
1081
+ try {
1082
+ scaffoldDirs(tmpDir, 'M001', ['S01']);
1083
+
1084
+ insertMilestone({ id: 'M001', title: 'Test', status: 'active' });
1085
+ insertSlice({ id: 'S01', milestoneId: 'M001', title: 'Slice', status: 'pending' });
1086
+
1087
+ const summaryContent = makeTaskSummaryContent('T01');
1088
+ insertTask({
1089
+ id: 'T01',
1090
+ sliceId: 'S01',
1091
+ milestoneId: 'M001',
1092
+ title: 'Task',
1093
+ status: 'done',
1094
+ fullSummaryMd: summaryContent,
1095
+ });
1096
+
1097
+ // Write plan with T01 checked so plan detection doesn't trigger
1098
+ const planContent = makePlanContent('S01', [
1099
+ { id: 'T01', title: 'Task', done: true },
1100
+ ]);
1101
+ const planPath = path.join(tmpDir, '.gsd', 'milestones', 'M001', 'slices', 'S01', 'S01-PLAN.md');
1102
+ fs.writeFileSync(planPath, planContent);
1103
+ clearAllCaches();
1104
+
1105
+ // Repair
1106
+ const repaired = await repairStaleRenders(tmpDir);
1107
+ assertTrue(repaired > 0, 'should repair missing summary');
1108
+
1109
+ // Verify file written
1110
+ const summaryPath = path.join(
1111
+ tmpDir, '.gsd', 'milestones', 'M001', 'slices', 'S01', 'tasks', 'T01-SUMMARY.md',
1112
+ );
1113
+ assertTrue(fs.existsSync(summaryPath), 'T01-SUMMARY.md should exist after repair');
1114
+
1115
+ // Second detect should be empty
1116
+ clearAllCaches();
1117
+ const staleAfter = detectStaleRenders(tmpDir);
1118
+ const summaryStale = staleAfter.find(s => s.reason.includes('SUMMARY.md missing') && s.reason.includes('T01'));
1119
+ assertEq(summaryStale, undefined, 'missing summary should be fixed after repair');
1120
+ } finally {
1121
+ closeDatabase();
1122
+ cleanupDir(tmpDir);
1123
+ }
1124
+ }
1125
+
1126
+ // ═══════════════════════════════════════════════════════════════════════════
1127
+ // Stale Repair — Idempotency
1128
+ // ═══════════════════════════════════════════════════════════════════════════
1129
+
1130
+ console.log('\n── markdown-renderer: repairStaleRenders idempotency — fully synced returns 0 ──');
1131
+
1132
+ {
1133
+ const tmpDir = makeTmpDir();
1134
+ const dbPath = path.join(tmpDir, '.gsd', 'gsd.db');
1135
+ openDatabase(dbPath);
1136
+ clearAllCaches();
1137
+
1138
+ try {
1139
+ scaffoldDirs(tmpDir, 'M001', ['S01']);
1140
+
1141
+ insertMilestone({ id: 'M001', title: 'Test', status: 'active' });
1142
+ insertSlice({ id: 'S01', milestoneId: 'M001', title: 'Slice', status: 'pending' });
1143
+ insertTask({ id: 'T01', sliceId: 'S01', milestoneId: 'M001', title: 'Task', status: 'done' });
1144
+
1145
+ // Write plan with T01 checked — matches DB
1146
+ const planContent = makePlanContent('S01', [
1147
+ { id: 'T01', title: 'Task', done: true },
1148
+ ]);
1149
+ const planPath = path.join(tmpDir, '.gsd', 'milestones', 'M001', 'slices', 'S01', 'S01-PLAN.md');
1150
+ fs.writeFileSync(planPath, planContent);
1151
+ clearAllCaches();
1152
+
1153
+ // No stale entries when everything is in sync (no summary to check since no fullSummaryMd)
1154
+ const repaired = await repairStaleRenders(tmpDir);
1155
+ assertEq(repaired, 0, 'repairStaleRenders should return 0 on fully synced project');
1156
+ } finally {
1157
+ closeDatabase();
1158
+ cleanupDir(tmpDir);
1159
+ }
1160
+ }
1161
+
1162
+ // ═══════════════════════════════════════════════════════════════════════════
1163
+ // Stale Detection — Missing Slice Summary + UAT
1164
+ // ═══════════════════════════════════════════════════════════════════════════
1165
+
1166
+ console.log('\n── markdown-renderer: detectStaleRenders finds missing slice summary and UAT ──');
1167
+
1168
+ {
1169
+ const tmpDir = makeTmpDir();
1170
+ const dbPath = path.join(tmpDir, '.gsd', 'gsd.db');
1171
+ openDatabase(dbPath);
1172
+ clearAllCaches();
1173
+
1174
+ try {
1175
+ scaffoldDirs(tmpDir, 'M001', ['S01']);
1176
+
1177
+ insertMilestone({ id: 'M001', title: 'Test', status: 'active' });
1178
+ insertSlice({ id: 'S01', milestoneId: 'M001', title: 'Slice', status: 'pending' });
1179
+
1180
+ // Update slice to complete with content via raw adapter
1181
+ const adapter = _getAdapter()!;
1182
+ adapter.prepare(
1183
+ `UPDATE slices SET status = 'complete', full_summary_md = :sm, full_uat_md = :um WHERE milestone_id = 'M001' AND id = 'S01'`,
1184
+ ).run({
1185
+ ':sm': '---\nid: S01\nparent: M001\nmilestone: M001\n---\n\n# S01: Summary\n\nDone.\n',
1186
+ ':um': '# S01 UAT\n\nAll pass.\n',
1187
+ });
1188
+
1189
+ clearAllCaches();
1190
+
1191
+ const stale = detectStaleRenders(tmpDir);
1192
+ const summaryStale = stale.find(s => s.reason.includes('SUMMARY.md missing') && s.reason.includes('S01'));
1193
+ const uatStale = stale.find(s => s.reason.includes('UAT.md missing') && s.reason.includes('S01'));
1194
+
1195
+ assertTrue(!!summaryStale, 'should detect missing S01-SUMMARY.md');
1196
+ assertTrue(!!uatStale, 'should detect missing S01-UAT.md');
1197
+ } finally {
1198
+ closeDatabase();
1199
+ cleanupDir(tmpDir);
1200
+ }
1201
+ }
1202
+
1203
+ // ═══════════════════════════════════════════════════════════════════════════
1204
+
1205
+ report();