gsd-pi 2.43.0 → 2.44.0-dev.0b97ffd

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 (693) hide show
  1. package/README.md +30 -12
  2. package/dist/cli.js +13 -1
  3. package/dist/help-text.js +24 -0
  4. package/dist/resources/extensions/bg-shell/overlay.js +3 -0
  5. package/dist/resources/extensions/github-sync/sync.js +2 -1
  6. package/dist/resources/extensions/gsd/auto/loop.js +0 -2
  7. package/dist/resources/extensions/gsd/auto/phases.js +7 -12
  8. package/dist/resources/extensions/gsd/auto-dashboard.js +19 -18
  9. package/dist/resources/extensions/gsd/auto-direct-dispatch.js +34 -19
  10. package/dist/resources/extensions/gsd/auto-dispatch.js +36 -21
  11. package/dist/resources/extensions/gsd/auto-post-unit.js +128 -14
  12. package/dist/resources/extensions/gsd/auto-prompts.js +202 -92
  13. package/dist/resources/extensions/gsd/auto-recovery.js +83 -135
  14. package/dist/resources/extensions/gsd/auto-start.js +10 -0
  15. package/dist/resources/extensions/gsd/auto-supervisor.js +14 -0
  16. package/dist/resources/extensions/gsd/auto-timeout-recovery.js +4 -7
  17. package/dist/resources/extensions/gsd/auto-verification.js +5 -10
  18. package/dist/resources/extensions/gsd/auto-worktree.js +123 -30
  19. package/dist/resources/extensions/gsd/auto.js +1 -4
  20. package/dist/resources/extensions/gsd/bootstrap/db-tools.js +611 -0
  21. package/dist/resources/extensions/gsd/bootstrap/dynamic-tools.js +28 -3
  22. package/dist/resources/extensions/gsd/bootstrap/register-hooks.js +7 -0
  23. package/dist/resources/extensions/gsd/commands/catalog.js +3 -1
  24. package/dist/resources/extensions/gsd/commands/handlers/ops.js +15 -1
  25. package/dist/resources/extensions/gsd/commands/handlers/workflow.js +5 -0
  26. package/dist/resources/extensions/gsd/commands-handlers.js +1 -1
  27. package/dist/resources/extensions/gsd/commands-maintenance.js +78 -3
  28. package/dist/resources/extensions/gsd/dashboard-overlay.js +32 -31
  29. package/dist/resources/extensions/gsd/db-writer.js +95 -4
  30. package/dist/resources/extensions/gsd/dispatch-guard.js +35 -30
  31. package/dist/resources/extensions/gsd/doctor-checks.js +28 -11
  32. package/dist/resources/extensions/gsd/doctor-environment.js +28 -0
  33. package/dist/resources/extensions/gsd/doctor-types.js +0 -15
  34. package/dist/resources/extensions/gsd/doctor.js +46 -282
  35. package/dist/resources/extensions/gsd/file-watcher.js +5 -1
  36. package/dist/resources/extensions/gsd/files.js +14 -198
  37. package/dist/resources/extensions/gsd/git-service.js +4 -0
  38. package/dist/resources/extensions/gsd/gitignore.js +4 -0
  39. package/dist/resources/extensions/gsd/gsd-db.js +819 -197
  40. package/dist/resources/extensions/gsd/guided-flow.js +18 -8
  41. package/dist/resources/extensions/gsd/markdown-renderer.js +862 -0
  42. package/dist/resources/extensions/gsd/md-importer.js +182 -4
  43. package/dist/resources/extensions/gsd/native-git-bridge.js +10 -1
  44. package/dist/resources/extensions/gsd/parallel-eligibility.js +14 -19
  45. package/dist/resources/extensions/gsd/parallel-orchestrator.js +38 -0
  46. package/dist/resources/extensions/gsd/parsers-legacy.js +239 -0
  47. package/dist/resources/extensions/gsd/preferences-types.js +1 -0
  48. package/dist/resources/extensions/gsd/preferences-validation.js +9 -0
  49. package/dist/resources/extensions/gsd/preferences.js +1 -0
  50. package/dist/resources/extensions/gsd/prompts/complete-slice.md +22 -9
  51. package/dist/resources/extensions/gsd/prompts/discuss.md +2 -2
  52. package/dist/resources/extensions/gsd/prompts/execute-task.md +15 -5
  53. package/dist/resources/extensions/gsd/prompts/guided-complete-slice.md +1 -1
  54. package/dist/resources/extensions/gsd/prompts/guided-execute-task.md +1 -1
  55. package/dist/resources/extensions/gsd/prompts/guided-plan-milestone.md +1 -1
  56. package/dist/resources/extensions/gsd/prompts/plan-milestone.md +6 -10
  57. package/dist/resources/extensions/gsd/prompts/plan-slice.md +4 -7
  58. package/dist/resources/extensions/gsd/prompts/reactive-execute.md +3 -3
  59. package/dist/resources/extensions/gsd/prompts/reassess-roadmap.md +6 -7
  60. package/dist/resources/extensions/gsd/prompts/replan-slice.md +6 -6
  61. package/dist/resources/extensions/gsd/reactive-graph.js +33 -3
  62. package/dist/resources/extensions/gsd/skill-health.js +3 -1
  63. package/dist/resources/extensions/gsd/state.js +484 -30
  64. package/dist/resources/extensions/gsd/tools/complete-milestone.js +128 -0
  65. package/dist/resources/extensions/gsd/tools/complete-slice.js +244 -0
  66. package/dist/resources/extensions/gsd/tools/complete-task.js +204 -0
  67. package/dist/resources/extensions/gsd/tools/plan-milestone.js +205 -0
  68. package/dist/resources/extensions/gsd/tools/plan-slice.js +155 -0
  69. package/dist/resources/extensions/gsd/tools/plan-task.js +94 -0
  70. package/dist/resources/extensions/gsd/tools/reassess-roadmap.js +152 -0
  71. package/dist/resources/extensions/gsd/tools/replan-slice.js +146 -0
  72. package/dist/resources/extensions/gsd/triage-resolution.js +17 -1
  73. package/dist/resources/extensions/gsd/undo.js +197 -3
  74. package/dist/resources/extensions/gsd/visualizer-data.js +53 -16
  75. package/dist/resources/extensions/gsd/workspace-index.js +63 -39
  76. package/dist/web/standalone/.next/BUILD_ID +1 -1
  77. package/dist/web/standalone/.next/app-path-routes-manifest.json +18 -17
  78. package/dist/web/standalone/.next/build-manifest.json +4 -4
  79. package/dist/web/standalone/.next/prerender-manifest.json +3 -3
  80. package/dist/web/standalone/.next/react-loadable-manifest.json +1 -1
  81. package/dist/web/standalone/.next/required-server-files.json +4 -4
  82. package/dist/web/standalone/.next/routes-manifest.json +6 -0
  83. package/dist/web/standalone/.next/server/app/_global-error/page.js +3 -3
  84. package/dist/web/standalone/.next/server/app/_global-error/page_client-reference-manifest.js +1 -1
  85. package/dist/web/standalone/.next/server/app/_global-error.html +2 -2
  86. package/dist/web/standalone/.next/server/app/_global-error.rsc +1 -1
  87. package/dist/web/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  88. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
  89. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
  90. package/dist/web/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  91. package/dist/web/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  92. package/dist/web/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  93. package/dist/web/standalone/.next/server/app/_not-found/page.js +2 -2
  94. package/dist/web/standalone/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  95. package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
  96. package/dist/web/standalone/.next/server/app/_not-found.rsc +4 -4
  97. package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +4 -4
  98. package/dist/web/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  99. package/dist/web/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +4 -4
  100. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  101. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  102. package/dist/web/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +2 -2
  103. package/dist/web/standalone/.next/server/app/api/boot/route.js +1 -1
  104. package/dist/web/standalone/.next/server/app/api/boot/route_client-reference-manifest.js +1 -1
  105. package/dist/web/standalone/.next/server/app/api/bridge-terminal/input/route.js +1 -1
  106. package/dist/web/standalone/.next/server/app/api/bridge-terminal/input/route_client-reference-manifest.js +1 -1
  107. package/dist/web/standalone/.next/server/app/api/bridge-terminal/resize/route.js +1 -1
  108. package/dist/web/standalone/.next/server/app/api/bridge-terminal/resize/route_client-reference-manifest.js +1 -1
  109. package/dist/web/standalone/.next/server/app/api/bridge-terminal/stream/route.js +2 -2
  110. package/dist/web/standalone/.next/server/app/api/bridge-terminal/stream/route_client-reference-manifest.js +1 -1
  111. package/dist/web/standalone/.next/server/app/api/browse-directories/route.js +1 -1
  112. package/dist/web/standalone/.next/server/app/api/browse-directories/route_client-reference-manifest.js +1 -1
  113. package/dist/web/standalone/.next/server/app/api/captures/route.js +1 -1
  114. package/dist/web/standalone/.next/server/app/api/captures/route_client-reference-manifest.js +1 -1
  115. package/dist/web/standalone/.next/server/app/api/cleanup/route.js +1 -1
  116. package/dist/web/standalone/.next/server/app/api/cleanup/route_client-reference-manifest.js +1 -1
  117. package/dist/web/standalone/.next/server/app/api/dev-mode/route.js +1 -1
  118. package/dist/web/standalone/.next/server/app/api/dev-mode/route_client-reference-manifest.js +1 -1
  119. package/dist/web/standalone/.next/server/app/api/doctor/route.js +1 -1
  120. package/dist/web/standalone/.next/server/app/api/doctor/route_client-reference-manifest.js +1 -1
  121. package/dist/web/standalone/.next/server/app/api/export-data/route.js +1 -1
  122. package/dist/web/standalone/.next/server/app/api/export-data/route_client-reference-manifest.js +1 -1
  123. package/dist/web/standalone/.next/server/app/api/files/route.js +1 -1
  124. package/dist/web/standalone/.next/server/app/api/files/route_client-reference-manifest.js +1 -1
  125. package/dist/web/standalone/.next/server/app/api/forensics/route.js +1 -1
  126. package/dist/web/standalone/.next/server/app/api/forensics/route_client-reference-manifest.js +1 -1
  127. package/dist/web/standalone/.next/server/app/api/git/route.js +1 -1
  128. package/dist/web/standalone/.next/server/app/api/git/route_client-reference-manifest.js +1 -1
  129. package/dist/web/standalone/.next/server/app/api/history/route.js +1 -1
  130. package/dist/web/standalone/.next/server/app/api/history/route_client-reference-manifest.js +1 -1
  131. package/dist/web/standalone/.next/server/app/api/hooks/route.js +1 -1
  132. package/dist/web/standalone/.next/server/app/api/hooks/route_client-reference-manifest.js +1 -1
  133. package/dist/web/standalone/.next/server/app/api/inspect/route.js +1 -1
  134. package/dist/web/standalone/.next/server/app/api/inspect/route_client-reference-manifest.js +1 -1
  135. package/dist/web/standalone/.next/server/app/api/knowledge/route.js +1 -1
  136. package/dist/web/standalone/.next/server/app/api/knowledge/route_client-reference-manifest.js +1 -1
  137. package/dist/web/standalone/.next/server/app/api/live-state/route.js +1 -1
  138. package/dist/web/standalone/.next/server/app/api/live-state/route_client-reference-manifest.js +1 -1
  139. package/dist/web/standalone/.next/server/app/api/onboarding/route.js +1 -1
  140. package/dist/web/standalone/.next/server/app/api/onboarding/route_client-reference-manifest.js +1 -1
  141. package/dist/web/standalone/.next/server/app/api/preferences/route.js +1 -1
  142. package/dist/web/standalone/.next/server/app/api/preferences/route_client-reference-manifest.js +1 -1
  143. package/dist/web/standalone/.next/server/app/api/projects/route.js +1 -1
  144. package/dist/web/standalone/.next/server/app/api/projects/route_client-reference-manifest.js +1 -1
  145. package/dist/web/standalone/.next/server/app/api/recovery/route.js +1 -1
  146. package/dist/web/standalone/.next/server/app/api/recovery/route_client-reference-manifest.js +1 -1
  147. package/dist/web/standalone/.next/server/app/api/remote-questions/route.js +5 -5
  148. package/dist/web/standalone/.next/server/app/api/remote-questions/route_client-reference-manifest.js +1 -1
  149. package/dist/web/standalone/.next/server/app/api/session/browser/route.js +1 -1
  150. package/dist/web/standalone/.next/server/app/api/session/browser/route_client-reference-manifest.js +1 -1
  151. package/dist/web/standalone/.next/server/app/api/session/command/route.js +1 -1
  152. package/dist/web/standalone/.next/server/app/api/session/command/route_client-reference-manifest.js +1 -1
  153. package/dist/web/standalone/.next/server/app/api/session/events/route.js +2 -2
  154. package/dist/web/standalone/.next/server/app/api/session/events/route_client-reference-manifest.js +1 -1
  155. package/dist/web/standalone/.next/server/app/api/session/manage/route.js +1 -1
  156. package/dist/web/standalone/.next/server/app/api/session/manage/route_client-reference-manifest.js +1 -1
  157. package/dist/web/standalone/.next/server/app/api/settings-data/route.js +1 -1
  158. package/dist/web/standalone/.next/server/app/api/settings-data/route_client-reference-manifest.js +1 -1
  159. package/dist/web/standalone/.next/server/app/api/shutdown/route.js +1 -1
  160. package/dist/web/standalone/.next/server/app/api/shutdown/route_client-reference-manifest.js +1 -1
  161. package/dist/web/standalone/.next/server/app/api/skill-health/route.js +1 -1
  162. package/dist/web/standalone/.next/server/app/api/skill-health/route_client-reference-manifest.js +1 -1
  163. package/dist/web/standalone/.next/server/app/api/steer/route.js +1 -1
  164. package/dist/web/standalone/.next/server/app/api/steer/route_client-reference-manifest.js +1 -1
  165. package/dist/web/standalone/.next/server/app/api/switch-root/route.js +1 -0
  166. package/dist/web/standalone/.next/server/app/api/switch-root/route.js.nft.json +1 -0
  167. package/dist/web/standalone/.next/server/app/api/switch-root/route_client-reference-manifest.js +1 -0
  168. package/dist/web/standalone/.next/server/app/api/terminal/input/route.js +1 -1
  169. package/dist/web/standalone/.next/server/app/api/terminal/input/route_client-reference-manifest.js +1 -1
  170. package/dist/web/standalone/.next/server/app/api/terminal/resize/route.js +2 -2
  171. package/dist/web/standalone/.next/server/app/api/terminal/resize/route_client-reference-manifest.js +1 -1
  172. package/dist/web/standalone/.next/server/app/api/terminal/sessions/route.js +2 -2
  173. package/dist/web/standalone/.next/server/app/api/terminal/sessions/route_client-reference-manifest.js +1 -1
  174. package/dist/web/standalone/.next/server/app/api/terminal/stream/route.js +4 -4
  175. package/dist/web/standalone/.next/server/app/api/terminal/stream/route_client-reference-manifest.js +1 -1
  176. package/dist/web/standalone/.next/server/app/api/terminal/upload/route.js +1 -1
  177. package/dist/web/standalone/.next/server/app/api/terminal/upload/route_client-reference-manifest.js +1 -1
  178. package/dist/web/standalone/.next/server/app/api/undo/route.js +1 -1
  179. package/dist/web/standalone/.next/server/app/api/undo/route_client-reference-manifest.js +1 -1
  180. package/dist/web/standalone/.next/server/app/api/update/route.js +1 -1
  181. package/dist/web/standalone/.next/server/app/api/update/route_client-reference-manifest.js +1 -1
  182. package/dist/web/standalone/.next/server/app/api/visualizer/route.js +1 -1
  183. package/dist/web/standalone/.next/server/app/api/visualizer/route_client-reference-manifest.js +1 -1
  184. package/dist/web/standalone/.next/server/app/index.html +1 -1
  185. package/dist/web/standalone/.next/server/app/index.rsc +5 -5
  186. package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +2 -2
  187. package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +5 -5
  188. package/dist/web/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
  189. package/dist/web/standalone/.next/server/app/index.segments/_index.segment.rsc +4 -4
  190. package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +2 -2
  191. package/dist/web/standalone/.next/server/app/page.js +2 -2
  192. package/dist/web/standalone/.next/server/app/page_client-reference-manifest.js +1 -1
  193. package/dist/web/standalone/.next/server/app-paths-manifest.json +18 -17
  194. package/dist/web/standalone/.next/server/chunks/229.js +3 -3
  195. package/dist/web/standalone/.next/server/chunks/471.js +3 -3
  196. package/dist/web/standalone/.next/server/functions-config-manifest.json +1 -0
  197. package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
  198. package/dist/web/standalone/.next/server/middleware-react-loadable-manifest.js +1 -1
  199. package/dist/web/standalone/.next/server/middleware.js +2 -2
  200. package/dist/web/standalone/.next/server/next-font-manifest.js +1 -1
  201. package/dist/web/standalone/.next/server/next-font-manifest.json +1 -1
  202. package/dist/web/standalone/.next/server/pages/404.html +1 -1
  203. package/dist/web/standalone/.next/server/pages/500.html +2 -2
  204. package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
  205. package/dist/web/standalone/.next/static/alS4hoANx0TK4UVZY27da/_buildManifest.js +1 -0
  206. package/dist/web/standalone/.next/static/chunks/{4024.c195dc1fdd2adbea.js → 4024.0de81b543b28b9fe.js} +2 -2
  207. package/dist/web/standalone/.next/static/chunks/app/_global-error/{page-d07a2c023f1aef1e.js → page-d83ba70a25a85472.js} +1 -1
  208. package/dist/web/standalone/.next/static/chunks/app/_not-found/page-f2a7482d42a5614b.js +1 -0
  209. package/dist/web/standalone/.next/static/chunks/app/api/boot/{route-d07a2c023f1aef1e.js → route-d83ba70a25a85472.js} +1 -1
  210. package/dist/web/standalone/.next/static/chunks/app/api/bridge-terminal/input/{route-d07a2c023f1aef1e.js → route-d83ba70a25a85472.js} +1 -1
  211. package/dist/web/standalone/.next/static/chunks/app/api/bridge-terminal/resize/{route-d07a2c023f1aef1e.js → route-d83ba70a25a85472.js} +1 -1
  212. package/dist/web/standalone/.next/static/chunks/app/api/bridge-terminal/stream/route-d83ba70a25a85472.js +1 -0
  213. package/dist/web/standalone/.next/static/chunks/app/api/browse-directories/route-d83ba70a25a85472.js +1 -0
  214. package/dist/web/standalone/.next/static/chunks/app/api/captures/route-d83ba70a25a85472.js +1 -0
  215. package/dist/web/standalone/.next/static/chunks/app/api/cleanup/route-d83ba70a25a85472.js +1 -0
  216. package/dist/web/standalone/.next/static/chunks/app/api/dev-mode/route-d83ba70a25a85472.js +1 -0
  217. package/dist/web/standalone/.next/static/chunks/app/api/doctor/route-d83ba70a25a85472.js +1 -0
  218. package/dist/web/standalone/.next/static/chunks/app/api/export-data/route-d83ba70a25a85472.js +1 -0
  219. package/dist/web/standalone/.next/static/chunks/app/api/files/route-d83ba70a25a85472.js +1 -0
  220. package/dist/web/standalone/.next/static/chunks/app/api/forensics/route-d83ba70a25a85472.js +1 -0
  221. package/dist/web/standalone/.next/static/chunks/app/api/git/route-d83ba70a25a85472.js +1 -0
  222. package/dist/web/standalone/.next/static/chunks/app/api/history/route-d83ba70a25a85472.js +1 -0
  223. package/dist/web/standalone/.next/static/chunks/app/api/hooks/route-d83ba70a25a85472.js +1 -0
  224. package/dist/web/standalone/.next/static/chunks/app/api/inspect/route-d83ba70a25a85472.js +1 -0
  225. package/dist/web/standalone/.next/static/chunks/app/api/knowledge/route-d83ba70a25a85472.js +1 -0
  226. package/dist/web/standalone/.next/static/chunks/app/api/live-state/route-d83ba70a25a85472.js +1 -0
  227. package/dist/web/standalone/.next/static/chunks/app/api/onboarding/route-d83ba70a25a85472.js +1 -0
  228. package/dist/web/standalone/.next/static/chunks/app/api/preferences/route-d83ba70a25a85472.js +1 -0
  229. package/dist/web/standalone/.next/static/chunks/app/api/projects/route-d83ba70a25a85472.js +1 -0
  230. package/dist/web/standalone/.next/static/chunks/app/api/recovery/route-d83ba70a25a85472.js +1 -0
  231. package/dist/web/standalone/.next/static/chunks/app/api/remote-questions/route-d83ba70a25a85472.js +1 -0
  232. package/dist/web/standalone/.next/static/chunks/app/api/session/browser/route-d83ba70a25a85472.js +1 -0
  233. package/dist/web/standalone/.next/static/chunks/app/api/session/command/route-d83ba70a25a85472.js +1 -0
  234. package/dist/web/standalone/.next/static/chunks/app/api/session/events/route-d83ba70a25a85472.js +1 -0
  235. package/dist/web/standalone/.next/static/chunks/app/api/session/manage/route-d83ba70a25a85472.js +1 -0
  236. package/dist/web/standalone/.next/static/chunks/app/api/settings-data/route-d83ba70a25a85472.js +1 -0
  237. package/dist/web/standalone/.next/static/chunks/app/api/shutdown/route-d83ba70a25a85472.js +1 -0
  238. package/dist/web/standalone/.next/static/chunks/app/api/skill-health/route-d83ba70a25a85472.js +1 -0
  239. package/dist/web/standalone/.next/static/chunks/app/api/steer/route-d83ba70a25a85472.js +1 -0
  240. package/dist/web/standalone/.next/static/chunks/app/api/switch-root/route-d83ba70a25a85472.js +1 -0
  241. package/dist/web/standalone/.next/static/chunks/app/api/terminal/input/route-d83ba70a25a85472.js +1 -0
  242. package/dist/web/standalone/.next/static/chunks/app/api/terminal/resize/route-d83ba70a25a85472.js +1 -0
  243. package/dist/web/standalone/.next/static/chunks/app/api/terminal/sessions/route-d83ba70a25a85472.js +1 -0
  244. package/dist/web/standalone/.next/static/chunks/app/api/terminal/stream/route-d83ba70a25a85472.js +1 -0
  245. package/dist/web/standalone/.next/static/chunks/app/api/terminal/upload/route-d83ba70a25a85472.js +1 -0
  246. package/dist/web/standalone/.next/static/chunks/app/api/undo/route-d83ba70a25a85472.js +1 -0
  247. package/dist/web/standalone/.next/static/chunks/app/api/update/route-d83ba70a25a85472.js +1 -0
  248. package/dist/web/standalone/.next/static/chunks/app/api/visualizer/route-d83ba70a25a85472.js +1 -0
  249. package/dist/web/standalone/.next/static/chunks/app/layout-a16c7a7ecdf0c2cf.js +1 -0
  250. package/dist/web/standalone/.next/static/chunks/app/page-b9367c5ae13b99c6.js +1 -0
  251. package/dist/web/standalone/.next/static/chunks/{main-app-2f2ee7b85712c2bd.js → main-app-fdab67f7802d7832.js} +1 -1
  252. package/dist/web/standalone/.next/static/chunks/next/dist/client/components/builtin/app-error-d83ba70a25a85472.js +1 -0
  253. package/dist/web/standalone/.next/static/chunks/next/dist/client/components/builtin/forbidden-d83ba70a25a85472.js +1 -0
  254. package/dist/web/standalone/.next/static/chunks/next/dist/client/components/builtin/global-error-459824ffb8c323dd.js +1 -0
  255. package/dist/web/standalone/.next/static/chunks/next/dist/client/components/builtin/not-found-d83ba70a25a85472.js +1 -0
  256. package/dist/web/standalone/.next/static/chunks/next/dist/client/components/builtin/unauthorized-d83ba70a25a85472.js +1 -0
  257. package/dist/web/standalone/.next/static/chunks/{webpack-fa307370fcf9fb2c.js → webpack-9014b5adb127a98a.js} +1 -1
  258. package/dist/web/standalone/.next/static/css/8a727f372cf53002.css +1 -0
  259. package/dist/web/standalone/node_modules/node-pty/build/Makefile +2 -2
  260. package/dist/web/standalone/node_modules/node-pty/build/Release/pty.node +0 -0
  261. package/dist/web/standalone/node_modules/node-pty/build/pty.target.mk +14 -14
  262. package/dist/web/standalone/node_modules/node-pty/node-addon-api/node_addon_api.target.mk +14 -14
  263. package/dist/web/standalone/node_modules/node-pty/node-addon-api/node_addon_api_except.target.mk +14 -14
  264. package/dist/web/standalone/node_modules/node-pty/node-addon-api/node_addon_api_maybe.target.mk +14 -14
  265. package/dist/web/standalone/server.js +1 -1
  266. package/package.json +4 -4
  267. package/packages/pi-ai/dist/models.custom.d.ts +173 -0
  268. package/packages/pi-ai/dist/models.custom.d.ts.map +1 -0
  269. package/packages/pi-ai/dist/models.custom.js +170 -0
  270. package/packages/pi-ai/dist/models.custom.js.map +1 -0
  271. package/packages/pi-ai/dist/models.d.ts.map +1 -1
  272. package/packages/pi-ai/dist/models.js +16 -1
  273. package/packages/pi-ai/dist/models.js.map +1 -1
  274. package/packages/pi-ai/dist/models.test.d.ts +2 -0
  275. package/packages/pi-ai/dist/models.test.d.ts.map +1 -0
  276. package/packages/pi-ai/dist/models.test.js +67 -0
  277. package/packages/pi-ai/dist/models.test.js.map +1 -0
  278. package/packages/pi-ai/src/models.custom.ts +172 -0
  279. package/packages/pi-ai/src/models.test.ts +85 -0
  280. package/packages/pi-ai/src/models.ts +17 -1
  281. package/packages/pi-coding-agent/dist/core/agent-session.d.ts +10 -3
  282. package/packages/pi-coding-agent/dist/core/agent-session.d.ts.map +1 -1
  283. package/packages/pi-coding-agent/dist/core/agent-session.js +21 -34
  284. package/packages/pi-coding-agent/dist/core/agent-session.js.map +1 -1
  285. package/packages/pi-coding-agent/dist/core/auth-storage.test.js +6 -8
  286. package/packages/pi-coding-agent/dist/core/auth-storage.test.js.map +1 -1
  287. package/packages/pi-coding-agent/dist/core/compaction/branch-summarization.d.ts +2 -2
  288. package/packages/pi-coding-agent/dist/core/compaction/branch-summarization.d.ts.map +1 -1
  289. package/packages/pi-coding-agent/dist/core/compaction/branch-summarization.js.map +1 -1
  290. package/packages/pi-coding-agent/dist/core/compaction/compaction.d.ts +2 -2
  291. package/packages/pi-coding-agent/dist/core/compaction/compaction.d.ts.map +1 -1
  292. package/packages/pi-coding-agent/dist/core/compaction/compaction.js.map +1 -1
  293. package/packages/pi-coding-agent/dist/core/compaction-orchestrator.js +4 -4
  294. package/packages/pi-coding-agent/dist/core/compaction-orchestrator.js.map +1 -1
  295. package/packages/pi-coding-agent/dist/core/extensions/index.d.ts +1 -1
  296. package/packages/pi-coding-agent/dist/core/extensions/index.d.ts.map +1 -1
  297. package/packages/pi-coding-agent/dist/core/extensions/index.js.map +1 -1
  298. package/packages/pi-coding-agent/dist/core/extensions/loader.d.ts +6 -0
  299. package/packages/pi-coding-agent/dist/core/extensions/loader.d.ts.map +1 -1
  300. package/packages/pi-coding-agent/dist/core/extensions/loader.js +80 -0
  301. package/packages/pi-coding-agent/dist/core/extensions/loader.js.map +1 -1
  302. package/packages/pi-coding-agent/dist/core/extensions/loader.test.js +63 -0
  303. package/packages/pi-coding-agent/dist/core/extensions/loader.test.js.map +1 -1
  304. package/packages/pi-coding-agent/dist/core/extensions/runner.test.js +24 -26
  305. package/packages/pi-coding-agent/dist/core/extensions/runner.test.js.map +1 -1
  306. package/packages/pi-coding-agent/dist/core/extensions/types.d.ts +37 -0
  307. package/packages/pi-coding-agent/dist/core/extensions/types.d.ts.map +1 -1
  308. package/packages/pi-coding-agent/dist/core/extensions/types.js.map +1 -1
  309. package/packages/pi-coding-agent/dist/core/fallback-resolver.d.ts.map +1 -1
  310. package/packages/pi-coding-agent/dist/core/fallback-resolver.js +2 -3
  311. package/packages/pi-coding-agent/dist/core/fallback-resolver.js.map +1 -1
  312. package/packages/pi-coding-agent/dist/core/fallback-resolver.test.js +12 -2
  313. package/packages/pi-coding-agent/dist/core/fallback-resolver.test.js.map +1 -1
  314. package/packages/pi-coding-agent/dist/core/fs-utils.test.js +29 -48
  315. package/packages/pi-coding-agent/dist/core/fs-utils.test.js.map +1 -1
  316. package/packages/pi-coding-agent/dist/core/lifecycle-hooks.d.ts +38 -0
  317. package/packages/pi-coding-agent/dist/core/lifecycle-hooks.d.ts.map +1 -0
  318. package/packages/pi-coding-agent/dist/core/lifecycle-hooks.js +192 -0
  319. package/packages/pi-coding-agent/dist/core/lifecycle-hooks.js.map +1 -0
  320. package/packages/pi-coding-agent/dist/core/lsp/client.d.ts +5 -0
  321. package/packages/pi-coding-agent/dist/core/lsp/client.d.ts.map +1 -1
  322. package/packages/pi-coding-agent/dist/core/lsp/client.js +69 -21
  323. package/packages/pi-coding-agent/dist/core/lsp/client.js.map +1 -1
  324. package/packages/pi-coding-agent/dist/core/model-registry-auth-mode.test.d.ts +2 -0
  325. package/packages/pi-coding-agent/dist/core/model-registry-auth-mode.test.d.ts.map +1 -0
  326. package/packages/pi-coding-agent/dist/core/model-registry-auth-mode.test.js +255 -0
  327. package/packages/pi-coding-agent/dist/core/model-registry-auth-mode.test.js.map +1 -0
  328. package/packages/pi-coding-agent/dist/core/model-registry.d.ts +15 -0
  329. package/packages/pi-coding-agent/dist/core/model-registry.d.ts.map +1 -1
  330. package/packages/pi-coding-agent/dist/core/model-registry.js +40 -3
  331. package/packages/pi-coding-agent/dist/core/model-registry.js.map +1 -1
  332. package/packages/pi-coding-agent/dist/core/package-commands.d.ts +25 -0
  333. package/packages/pi-coding-agent/dist/core/package-commands.d.ts.map +1 -0
  334. package/packages/pi-coding-agent/dist/core/package-commands.js +253 -0
  335. package/packages/pi-coding-agent/dist/core/package-commands.js.map +1 -0
  336. package/packages/pi-coding-agent/dist/core/package-commands.test.d.ts +2 -0
  337. package/packages/pi-coding-agent/dist/core/package-commands.test.d.ts.map +1 -0
  338. package/packages/pi-coding-agent/dist/core/package-commands.test.js +225 -0
  339. package/packages/pi-coding-agent/dist/core/package-commands.test.js.map +1 -0
  340. package/packages/pi-coding-agent/dist/core/resolve-config-value.test.js +34 -44
  341. package/packages/pi-coding-agent/dist/core/resolve-config-value.test.js.map +1 -1
  342. package/packages/pi-coding-agent/dist/core/sdk.d.ts.map +1 -1
  343. package/packages/pi-coding-agent/dist/core/sdk.js +4 -0
  344. package/packages/pi-coding-agent/dist/core/sdk.js.map +1 -1
  345. package/packages/pi-coding-agent/dist/core/session-manager.test.js +30 -34
  346. package/packages/pi-coding-agent/dist/core/session-manager.test.js.map +1 -1
  347. package/packages/pi-coding-agent/dist/core/tools/edit-diff.test.js +10 -12
  348. package/packages/pi-coding-agent/dist/core/tools/edit-diff.test.js.map +1 -1
  349. package/packages/pi-coding-agent/dist/index.d.ts +3 -1
  350. package/packages/pi-coding-agent/dist/index.d.ts.map +1 -1
  351. package/packages/pi-coding-agent/dist/index.js +1 -0
  352. package/packages/pi-coding-agent/dist/index.js.map +1 -1
  353. package/packages/pi-coding-agent/dist/main.d.ts.map +1 -1
  354. package/packages/pi-coding-agent/dist/main.js +11 -199
  355. package/packages/pi-coding-agent/dist/main.js.map +1 -1
  356. package/packages/pi-coding-agent/dist/modes/interactive/components/footer.d.ts +6 -0
  357. package/packages/pi-coding-agent/dist/modes/interactive/components/footer.d.ts.map +1 -1
  358. package/packages/pi-coding-agent/dist/modes/interactive/components/footer.js +21 -0
  359. package/packages/pi-coding-agent/dist/modes/interactive/components/footer.js.map +1 -1
  360. package/packages/pi-coding-agent/dist/modes/interactive/theme/theme.d.ts +1 -1
  361. package/packages/pi-coding-agent/dist/modes/interactive/theme/theme.d.ts.map +1 -1
  362. package/packages/pi-coding-agent/dist/modes/interactive/theme/theme.js +8 -15
  363. package/packages/pi-coding-agent/dist/modes/interactive/theme/theme.js.map +1 -1
  364. package/packages/pi-coding-agent/dist/modes/print-mode.d.ts.map +1 -1
  365. package/packages/pi-coding-agent/dist/modes/print-mode.js +45 -34
  366. package/packages/pi-coding-agent/dist/modes/print-mode.js.map +1 -1
  367. package/packages/pi-coding-agent/dist/modes/rpc/rpc-client.d.ts +1 -0
  368. package/packages/pi-coding-agent/dist/modes/rpc/rpc-client.d.ts.map +1 -1
  369. package/packages/pi-coding-agent/dist/modes/rpc/rpc-client.js +7 -2
  370. package/packages/pi-coding-agent/dist/modes/rpc/rpc-client.js.map +1 -1
  371. package/packages/pi-coding-agent/dist/modes/rpc/rpc-mode.d.ts.map +1 -1
  372. package/packages/pi-coding-agent/dist/modes/rpc/rpc-mode.js +2 -1
  373. package/packages/pi-coding-agent/dist/modes/rpc/rpc-mode.js.map +1 -1
  374. package/packages/pi-coding-agent/dist/resources/extensions/memory/storage.test.js +43 -47
  375. package/packages/pi-coding-agent/dist/resources/extensions/memory/storage.test.js.map +1 -1
  376. package/packages/pi-coding-agent/package.json +1 -1
  377. package/packages/pi-coding-agent/src/core/agent-session.ts +26 -37
  378. package/packages/pi-coding-agent/src/core/auth-storage.test.ts +7 -7
  379. package/packages/pi-coding-agent/src/core/compaction/branch-summarization.ts +2 -2
  380. package/packages/pi-coding-agent/src/core/compaction/compaction.ts +3 -3
  381. package/packages/pi-coding-agent/src/core/compaction-orchestrator.ts +4 -4
  382. package/packages/pi-coding-agent/src/core/extensions/index.ts +5 -0
  383. package/packages/pi-coding-agent/src/core/extensions/loader.test.ts +96 -0
  384. package/packages/pi-coding-agent/src/core/extensions/loader.ts +89 -0
  385. package/packages/pi-coding-agent/src/core/extensions/runner.test.ts +26 -26
  386. package/packages/pi-coding-agent/src/core/extensions/types.ts +44 -0
  387. package/packages/pi-coding-agent/src/core/fallback-resolver.test.ts +15 -2
  388. package/packages/pi-coding-agent/src/core/fallback-resolver.ts +2 -3
  389. package/packages/pi-coding-agent/src/core/fs-utils.test.ts +31 -43
  390. package/packages/pi-coding-agent/src/core/lifecycle-hooks.ts +274 -0
  391. package/packages/pi-coding-agent/src/core/lsp/client.ts +83 -21
  392. package/packages/pi-coding-agent/src/core/model-registry-auth-mode.test.ts +288 -0
  393. package/packages/pi-coding-agent/src/core/model-registry.ts +39 -3
  394. package/packages/pi-coding-agent/src/core/package-commands.test.ts +240 -0
  395. package/packages/pi-coding-agent/src/core/package-commands.ts +310 -0
  396. package/packages/pi-coding-agent/src/core/resolve-config-value.test.ts +40 -45
  397. package/packages/pi-coding-agent/src/core/sdk.ts +4 -0
  398. package/packages/pi-coding-agent/src/core/session-manager.test.ts +33 -33
  399. package/packages/pi-coding-agent/src/core/tools/edit-diff.test.ts +17 -17
  400. package/packages/pi-coding-agent/src/index.ts +7 -0
  401. package/packages/pi-coding-agent/src/main.ts +11 -232
  402. package/packages/pi-coding-agent/src/modes/interactive/components/footer.ts +20 -0
  403. package/packages/pi-coding-agent/src/modes/interactive/theme/theme.ts +9 -16
  404. package/packages/pi-coding-agent/src/modes/print-mode.ts +42 -32
  405. package/packages/pi-coding-agent/src/modes/rpc/rpc-client.ts +8 -2
  406. package/packages/pi-coding-agent/src/modes/rpc/rpc-mode.ts +2 -1
  407. package/packages/pi-coding-agent/src/resources/extensions/memory/storage.test.ts +74 -74
  408. package/pkg/dist/modes/interactive/theme/theme.d.ts +1 -1
  409. package/pkg/dist/modes/interactive/theme/theme.d.ts.map +1 -1
  410. package/pkg/dist/modes/interactive/theme/theme.js +8 -15
  411. package/pkg/dist/modes/interactive/theme/theme.js.map +1 -1
  412. package/pkg/package.json +1 -1
  413. package/src/resources/extensions/bg-shell/overlay.ts +4 -0
  414. package/src/resources/extensions/github-sync/sync.ts +2 -1
  415. package/src/resources/extensions/gsd/auto/loop-deps.ts +0 -8
  416. package/src/resources/extensions/gsd/auto/loop.ts +0 -2
  417. package/src/resources/extensions/gsd/auto/phases.ts +7 -20
  418. package/src/resources/extensions/gsd/auto/types.ts +0 -1
  419. package/src/resources/extensions/gsd/auto-dashboard.ts +20 -16
  420. package/src/resources/extensions/gsd/auto-direct-dispatch.ts +34 -19
  421. package/src/resources/extensions/gsd/auto-dispatch.ts +38 -21
  422. package/src/resources/extensions/gsd/auto-post-unit.ts +150 -15
  423. package/src/resources/extensions/gsd/auto-prompts.ts +186 -103
  424. package/src/resources/extensions/gsd/auto-recovery.ts +77 -142
  425. package/src/resources/extensions/gsd/auto-start.ts +14 -0
  426. package/src/resources/extensions/gsd/auto-supervisor.ts +14 -0
  427. package/src/resources/extensions/gsd/auto-timeout-recovery.ts +6 -7
  428. package/src/resources/extensions/gsd/auto-verification.ts +4 -9
  429. package/src/resources/extensions/gsd/auto-worktree.ts +126 -30
  430. package/src/resources/extensions/gsd/auto.ts +0 -9
  431. package/src/resources/extensions/gsd/bootstrap/db-tools.ts +675 -4
  432. package/src/resources/extensions/gsd/bootstrap/dynamic-tools.ts +31 -3
  433. package/src/resources/extensions/gsd/bootstrap/register-hooks.ts +7 -0
  434. package/src/resources/extensions/gsd/commands/catalog.ts +3 -1
  435. package/src/resources/extensions/gsd/commands/handlers/ops.ts +15 -1
  436. package/src/resources/extensions/gsd/commands/handlers/workflow.ts +8 -0
  437. package/src/resources/extensions/gsd/commands-handlers.ts +1 -1
  438. package/src/resources/extensions/gsd/commands-maintenance.ts +86 -3
  439. package/src/resources/extensions/gsd/dashboard-overlay.ts +17 -13
  440. package/src/resources/extensions/gsd/db-writer.ts +105 -4
  441. package/src/resources/extensions/gsd/dispatch-guard.ts +32 -30
  442. package/src/resources/extensions/gsd/doctor-checks.ts +25 -11
  443. package/src/resources/extensions/gsd/doctor-environment.ts +31 -0
  444. package/src/resources/extensions/gsd/doctor-types.ts +0 -23
  445. package/src/resources/extensions/gsd/doctor.ts +45 -295
  446. package/src/resources/extensions/gsd/file-watcher.ts +4 -1
  447. package/src/resources/extensions/gsd/files.ts +16 -220
  448. package/src/resources/extensions/gsd/git-service.ts +4 -0
  449. package/src/resources/extensions/gsd/gitignore.ts +4 -0
  450. package/src/resources/extensions/gsd/gsd-db.ts +1157 -370
  451. package/src/resources/extensions/gsd/guided-flow.ts +20 -8
  452. package/src/resources/extensions/gsd/markdown-renderer.ts +1098 -0
  453. package/src/resources/extensions/gsd/md-importer.ts +211 -2
  454. package/src/resources/extensions/gsd/native-git-bridge.ts +12 -1
  455. package/src/resources/extensions/gsd/parallel-eligibility.ts +14 -18
  456. package/src/resources/extensions/gsd/parallel-orchestrator.ts +43 -0
  457. package/src/resources/extensions/gsd/parsers-legacy.ts +271 -0
  458. package/src/resources/extensions/gsd/preferences-types.ts +3 -0
  459. package/src/resources/extensions/gsd/preferences-validation.ts +9 -0
  460. package/src/resources/extensions/gsd/preferences.ts +1 -0
  461. package/src/resources/extensions/gsd/prompts/complete-slice.md +22 -9
  462. package/src/resources/extensions/gsd/prompts/discuss.md +2 -2
  463. package/src/resources/extensions/gsd/prompts/execute-task.md +15 -5
  464. package/src/resources/extensions/gsd/prompts/guided-complete-slice.md +1 -1
  465. package/src/resources/extensions/gsd/prompts/guided-execute-task.md +1 -1
  466. package/src/resources/extensions/gsd/prompts/guided-plan-milestone.md +1 -1
  467. package/src/resources/extensions/gsd/prompts/plan-milestone.md +6 -10
  468. package/src/resources/extensions/gsd/prompts/plan-slice.md +4 -7
  469. package/src/resources/extensions/gsd/prompts/reactive-execute.md +3 -3
  470. package/src/resources/extensions/gsd/prompts/reassess-roadmap.md +6 -7
  471. package/src/resources/extensions/gsd/prompts/replan-slice.md +6 -6
  472. package/src/resources/extensions/gsd/reactive-graph.ts +33 -3
  473. package/src/resources/extensions/gsd/skill-health.ts +2 -1
  474. package/src/resources/extensions/gsd/state.ts +547 -29
  475. package/src/resources/extensions/gsd/tests/all-milestones-complete-merge.test.ts +99 -99
  476. package/src/resources/extensions/gsd/tests/atomic-task-closeout.test.ts +8 -120
  477. package/src/resources/extensions/gsd/tests/auto-lock-creation.test.ts +14 -16
  478. package/src/resources/extensions/gsd/tests/auto-loop.test.ts +20 -11
  479. package/src/resources/extensions/gsd/tests/auto-paused-session-validation.test.ts +43 -57
  480. package/src/resources/extensions/gsd/tests/auto-preflight.test.ts +11 -13
  481. package/src/resources/extensions/gsd/tests/auto-recovery.test.ts +600 -513
  482. package/src/resources/extensions/gsd/tests/auto-secrets-gate.test.ts +73 -75
  483. package/src/resources/extensions/gsd/tests/auto-start-needs-discussion.test.ts +34 -56
  484. package/src/resources/extensions/gsd/tests/auto-stash-merge.test.ts +121 -0
  485. package/src/resources/extensions/gsd/tests/auto-worktree-milestone-merge.test.ts +540 -668
  486. package/src/resources/extensions/gsd/tests/auto-worktree.test.ts +165 -143
  487. package/src/resources/extensions/gsd/tests/cache-staleness-regression.test.ts +29 -52
  488. package/src/resources/extensions/gsd/tests/captures.test.ts +148 -176
  489. package/src/resources/extensions/gsd/tests/claude-import-tui.test.ts +32 -33
  490. package/src/resources/extensions/gsd/tests/collect-from-manifest.test.ts +141 -143
  491. package/src/resources/extensions/gsd/tests/commands-inspect-open-db.test.ts +25 -25
  492. package/src/resources/extensions/gsd/tests/commands-logs.test.ts +81 -81
  493. package/src/resources/extensions/gsd/tests/complete-milestone.test.ts +39 -60
  494. package/src/resources/extensions/gsd/tests/complete-slice.test.ts +375 -0
  495. package/src/resources/extensions/gsd/tests/complete-task.test.ts +387 -0
  496. package/src/resources/extensions/gsd/tests/context-store.test.ts +354 -367
  497. package/src/resources/extensions/gsd/tests/continue-here.test.ts +68 -72
  498. package/src/resources/extensions/gsd/tests/cost-projection.test.ts +92 -106
  499. package/src/resources/extensions/gsd/tests/crash-recovery.test.ts +27 -35
  500. package/src/resources/extensions/gsd/tests/custom-engine-loop-integration.test.ts +0 -2
  501. package/src/resources/extensions/gsd/tests/dashboard-budget.test.ts +220 -237
  502. package/src/resources/extensions/gsd/tests/db-writer.test.ts +390 -420
  503. package/src/resources/extensions/gsd/tests/definition-loader.test.ts +76 -92
  504. package/src/resources/extensions/gsd/tests/derive-state-crossval.test.ts +512 -0
  505. package/src/resources/extensions/gsd/tests/derive-state-db.test.ts +644 -84
  506. package/src/resources/extensions/gsd/tests/derive-state-deps.test.ts +78 -101
  507. package/src/resources/extensions/gsd/tests/derive-state.test.ts +192 -227
  508. package/src/resources/extensions/gsd/tests/detection.test.ts +232 -278
  509. package/src/resources/extensions/gsd/tests/dev-engine-wrapper.test.ts +30 -34
  510. package/src/resources/extensions/gsd/tests/dispatch-guard.test.ts +192 -161
  511. package/src/resources/extensions/gsd/tests/dispatch-missing-task-plans.test.ts +43 -49
  512. package/src/resources/extensions/gsd/tests/dispatch-uat-last-completed.test.ts +28 -32
  513. package/src/resources/extensions/gsd/tests/doctor-completion-deferral.test.ts +30 -90
  514. package/src/resources/extensions/gsd/tests/doctor-delimiter-fix.test.ts +34 -38
  515. package/src/resources/extensions/gsd/tests/doctor-enhancements.test.ts +57 -80
  516. package/src/resources/extensions/gsd/tests/doctor-environment-worktree.test.ts +164 -0
  517. package/src/resources/extensions/gsd/tests/doctor-environment.test.ts +72 -97
  518. package/src/resources/extensions/gsd/tests/doctor-fixlevel.test.ts +55 -153
  519. package/src/resources/extensions/gsd/tests/doctor-git.test.ts +104 -145
  520. package/src/resources/extensions/gsd/tests/doctor-proactive.test.ts +84 -106
  521. package/src/resources/extensions/gsd/tests/doctor-roadmap-summary-atomicity.test.ts +53 -97
  522. package/src/resources/extensions/gsd/tests/doctor-runtime.test.ts +72 -93
  523. package/src/resources/extensions/gsd/tests/doctor.test.ts +109 -149
  524. package/src/resources/extensions/gsd/tests/ensure-db-open.test.ts +123 -131
  525. package/src/resources/extensions/gsd/tests/exit-command.test.ts +20 -24
  526. package/src/resources/extensions/gsd/tests/feature-branch-lifecycle-integration.test.ts +48 -57
  527. package/src/resources/extensions/gsd/tests/files-loadfile-eisdir.test.ts +5 -7
  528. package/src/resources/extensions/gsd/tests/flag-file-db.test.ts +278 -0
  529. package/src/resources/extensions/gsd/tests/freeform-decisions.test.ts +232 -0
  530. package/src/resources/extensions/gsd/tests/git-locale.test.ts +13 -27
  531. package/src/resources/extensions/gsd/tests/git-service.test.ts +291 -390
  532. package/src/resources/extensions/gsd/tests/gitignore-tracked-gsd.test.ts +31 -39
  533. package/src/resources/extensions/gsd/tests/graph-operations.test.ts +63 -69
  534. package/src/resources/extensions/gsd/tests/gsd-db.test.ts +255 -264
  535. package/src/resources/extensions/gsd/tests/gsd-inspect.test.ts +108 -119
  536. package/src/resources/extensions/gsd/tests/gsd-recover.test.ts +440 -0
  537. package/src/resources/extensions/gsd/tests/gsd-tools.test.ts +229 -262
  538. package/src/resources/extensions/gsd/tests/headless-answers.test.ts +13 -13
  539. package/src/resources/extensions/gsd/tests/health-widget.test.ts +29 -37
  540. package/src/resources/extensions/gsd/tests/idle-recovery.test.ts +81 -270
  541. package/src/resources/extensions/gsd/tests/init-wizard.test.ts +16 -18
  542. package/src/resources/extensions/gsd/tests/integration-edge.test.ts +41 -46
  543. package/src/resources/extensions/gsd/tests/integration-lifecycle.test.ts +42 -53
  544. package/src/resources/extensions/gsd/tests/integration-mixed-milestones.test.ts +75 -91
  545. package/src/resources/extensions/gsd/tests/integration-proof.test.ts +643 -0
  546. package/src/resources/extensions/gsd/tests/journal-integration.test.ts +0 -3
  547. package/src/resources/extensions/gsd/tests/markdown-renderer.test.ts +1161 -0
  548. package/src/resources/extensions/gsd/tests/md-importer.test.ts +101 -125
  549. package/src/resources/extensions/gsd/tests/memory-extractor.test.ts +45 -54
  550. package/src/resources/extensions/gsd/tests/memory-store.test.ts +81 -94
  551. package/src/resources/extensions/gsd/tests/migrate-command.test.ts +57 -66
  552. package/src/resources/extensions/gsd/tests/migrate-hierarchy.test.ts +429 -0
  553. package/src/resources/extensions/gsd/tests/migrate-parser.test.ts +161 -170
  554. package/src/resources/extensions/gsd/tests/migrate-transformer.test.ts +125 -141
  555. package/src/resources/extensions/gsd/tests/migrate-validator-parsers.test.ts +107 -131
  556. package/src/resources/extensions/gsd/tests/migrate-writer-integration.test.ts +89 -97
  557. package/src/resources/extensions/gsd/tests/migrate-writer.test.ts +127 -164
  558. package/src/resources/extensions/gsd/tests/must-have-parser.test.ts +81 -94
  559. package/src/resources/extensions/gsd/tests/none-mode-gates.test.ts +35 -36
  560. package/src/resources/extensions/gsd/tests/overrides.test.ts +99 -106
  561. package/src/resources/extensions/gsd/tests/parallel-crash-recovery.test.ts +40 -47
  562. package/src/resources/extensions/gsd/tests/parallel-worker-monitoring.test.ts +25 -28
  563. package/src/resources/extensions/gsd/tests/parallel-workers-multi-milestone-e2e.test.ts +66 -83
  564. package/src/resources/extensions/gsd/tests/park-edge-cases.test.ts +54 -77
  565. package/src/resources/extensions/gsd/tests/park-milestone.test.ts +68 -115
  566. package/src/resources/extensions/gsd/tests/parsers.test.ts +548 -612
  567. package/src/resources/extensions/gsd/tests/paths.test.ts +72 -87
  568. package/src/resources/extensions/gsd/tests/plan-milestone.test.ts +176 -113
  569. package/src/resources/extensions/gsd/tests/plan-slice-prompt.test.ts +7 -0
  570. package/src/resources/extensions/gsd/tests/plan-slice.test.ts +179 -0
  571. package/src/resources/extensions/gsd/tests/plan-task.test.ts +145 -0
  572. package/src/resources/extensions/gsd/tests/planning-crossval.test.ts +305 -0
  573. package/src/resources/extensions/gsd/tests/post-unit-hooks.test.ts +77 -117
  574. package/src/resources/extensions/gsd/tests/prompt-contracts.test.ts +139 -0
  575. package/src/resources/extensions/gsd/tests/prompt-db.test.ts +56 -56
  576. package/src/resources/extensions/gsd/tests/queue-draft-detection.test.ts +93 -119
  577. package/src/resources/extensions/gsd/tests/queue-order.test.ts +70 -82
  578. package/src/resources/extensions/gsd/tests/queue-reorder-e2e.test.ts +42 -55
  579. package/src/resources/extensions/gsd/tests/quick-auto-guard.test.ts +100 -0
  580. package/src/resources/extensions/gsd/tests/quick-branch-lifecycle.test.ts +45 -73
  581. package/src/resources/extensions/gsd/tests/reassess-handler.test.ts +325 -0
  582. package/src/resources/extensions/gsd/tests/reassess-prompt.test.ts +28 -38
  583. package/src/resources/extensions/gsd/tests/replan-handler.test.ts +410 -0
  584. package/src/resources/extensions/gsd/tests/replan-slice.test.ts +73 -80
  585. package/src/resources/extensions/gsd/tests/repo-identity-worktree.test.ts +71 -74
  586. package/src/resources/extensions/gsd/tests/requirements.test.ts +70 -75
  587. package/src/resources/extensions/gsd/tests/retry-state-reset.test.ts +44 -66
  588. package/src/resources/extensions/gsd/tests/roadmap-parse-regression.test.ts +114 -181
  589. package/src/resources/extensions/gsd/tests/roadmap-slices.test.ts +1 -1
  590. package/src/resources/extensions/gsd/tests/rogue-file-detection.test.ts +296 -0
  591. package/src/resources/extensions/gsd/tests/rule-registry.test.ts +63 -65
  592. package/src/resources/extensions/gsd/tests/run-uat.test.ts +66 -128
  593. package/src/resources/extensions/gsd/tests/schema-v9-sequence.test.ts +176 -0
  594. package/src/resources/extensions/gsd/tests/session-lock-multipath.test.ts +18 -25
  595. package/src/resources/extensions/gsd/tests/session-lock-regression.test.ts +37 -44
  596. package/src/resources/extensions/gsd/tests/shared-wal.test.ts +209 -0
  597. package/src/resources/extensions/gsd/tests/sqlite-unavailable-gate.test.ts +63 -0
  598. package/src/resources/extensions/gsd/tests/stalled-tool-recovery.test.ts +6 -8
  599. package/src/resources/extensions/gsd/tests/symlink-numbered-variants.test.ts +22 -28
  600. package/src/resources/extensions/gsd/tests/token-cost-display.test.ts +118 -0
  601. package/src/resources/extensions/gsd/tests/token-savings.test.ts +54 -56
  602. package/src/resources/extensions/gsd/tests/tool-call-loop-guard.test.ts +23 -25
  603. package/src/resources/extensions/gsd/tests/tool-naming.test.ts +19 -13
  604. package/src/resources/extensions/gsd/tests/undo.test.ts +321 -1
  605. package/src/resources/extensions/gsd/tests/unique-milestone-ids.test.ts +66 -82
  606. package/src/resources/extensions/gsd/tests/unit-runtime.test.ts +46 -47
  607. package/src/resources/extensions/gsd/tests/validate-milestone.test.ts +1 -1
  608. package/src/resources/extensions/gsd/tests/verification-evidence.test.ts +0 -142
  609. package/src/resources/extensions/gsd/tests/visualizer-critical-path.test.ts +20 -22
  610. package/src/resources/extensions/gsd/tests/visualizer-data.test.ts +84 -86
  611. package/src/resources/extensions/gsd/tests/visualizer-overlay.test.ts +41 -43
  612. package/src/resources/extensions/gsd/tests/visualizer-views.test.ts +94 -96
  613. package/src/resources/extensions/gsd/tests/windows-path-normalization.test.ts +11 -13
  614. package/src/resources/extensions/gsd/tests/worker-registry.test.ts +27 -29
  615. package/src/resources/extensions/gsd/tests/workflow-templates.test.ts +50 -52
  616. package/src/resources/extensions/gsd/tests/worktree-bugfix.test.ts +10 -13
  617. package/src/resources/extensions/gsd/tests/worktree-db-integration.test.ts +14 -18
  618. package/src/resources/extensions/gsd/tests/worktree-db.test.ts +38 -39
  619. package/src/resources/extensions/gsd/tests/worktree-e2e.test.ts +17 -21
  620. package/src/resources/extensions/gsd/tests/worktree-health-dispatch.test.ts +12 -5
  621. package/src/resources/extensions/gsd/tests/worktree-health.test.ts +25 -30
  622. package/src/resources/extensions/gsd/tests/worktree-integration.test.ts +30 -37
  623. package/src/resources/extensions/gsd/tests/worktree-symlink-removal.test.ts +15 -22
  624. package/src/resources/extensions/gsd/tests/worktree-sync-milestones.test.ts +59 -66
  625. package/src/resources/extensions/gsd/tests/worktree.test.ts +44 -50
  626. package/src/resources/extensions/gsd/tools/complete-milestone.ts +176 -0
  627. package/src/resources/extensions/gsd/tools/complete-slice.ts +300 -0
  628. package/src/resources/extensions/gsd/tools/complete-task.ts +245 -0
  629. package/src/resources/extensions/gsd/tools/plan-milestone.ts +249 -0
  630. package/src/resources/extensions/gsd/tools/plan-slice.ts +194 -0
  631. package/src/resources/extensions/gsd/tools/plan-task.ts +116 -0
  632. package/src/resources/extensions/gsd/tools/reassess-roadmap.ts +203 -0
  633. package/src/resources/extensions/gsd/tools/replan-slice.ts +192 -0
  634. package/src/resources/extensions/gsd/triage-resolution.ts +20 -1
  635. package/src/resources/extensions/gsd/types.ts +50 -0
  636. package/src/resources/extensions/gsd/undo.ts +247 -3
  637. package/src/resources/extensions/gsd/visualizer-data.ts +54 -17
  638. package/src/resources/extensions/gsd/workspace-index.ts +64 -46
  639. package/dist/resources/extensions/gsd/auto-observability.js +0 -56
  640. package/dist/resources/extensions/gsd/observability-validator.js +0 -422
  641. package/dist/resources/extensions/gsd/roadmap-mutations.js +0 -110
  642. package/dist/web/standalone/.next/static/VvclDCW6TAWjEyLU-EYL1/_buildManifest.js +0 -1
  643. package/dist/web/standalone/.next/static/chunks/app/_not-found/page-e07acdb7dd069836.js +0 -1
  644. package/dist/web/standalone/.next/static/chunks/app/api/bridge-terminal/stream/route-d07a2c023f1aef1e.js +0 -1
  645. package/dist/web/standalone/.next/static/chunks/app/api/browse-directories/route-d07a2c023f1aef1e.js +0 -1
  646. package/dist/web/standalone/.next/static/chunks/app/api/captures/route-d07a2c023f1aef1e.js +0 -1
  647. package/dist/web/standalone/.next/static/chunks/app/api/cleanup/route-d07a2c023f1aef1e.js +0 -1
  648. package/dist/web/standalone/.next/static/chunks/app/api/dev-mode/route-d07a2c023f1aef1e.js +0 -1
  649. package/dist/web/standalone/.next/static/chunks/app/api/doctor/route-d07a2c023f1aef1e.js +0 -1
  650. package/dist/web/standalone/.next/static/chunks/app/api/export-data/route-d07a2c023f1aef1e.js +0 -1
  651. package/dist/web/standalone/.next/static/chunks/app/api/files/route-d07a2c023f1aef1e.js +0 -1
  652. package/dist/web/standalone/.next/static/chunks/app/api/forensics/route-d07a2c023f1aef1e.js +0 -1
  653. package/dist/web/standalone/.next/static/chunks/app/api/git/route-d07a2c023f1aef1e.js +0 -1
  654. package/dist/web/standalone/.next/static/chunks/app/api/history/route-d07a2c023f1aef1e.js +0 -1
  655. package/dist/web/standalone/.next/static/chunks/app/api/hooks/route-d07a2c023f1aef1e.js +0 -1
  656. package/dist/web/standalone/.next/static/chunks/app/api/inspect/route-d07a2c023f1aef1e.js +0 -1
  657. package/dist/web/standalone/.next/static/chunks/app/api/knowledge/route-d07a2c023f1aef1e.js +0 -1
  658. package/dist/web/standalone/.next/static/chunks/app/api/live-state/route-d07a2c023f1aef1e.js +0 -1
  659. package/dist/web/standalone/.next/static/chunks/app/api/onboarding/route-d07a2c023f1aef1e.js +0 -1
  660. package/dist/web/standalone/.next/static/chunks/app/api/preferences/route-d07a2c023f1aef1e.js +0 -1
  661. package/dist/web/standalone/.next/static/chunks/app/api/projects/route-d07a2c023f1aef1e.js +0 -1
  662. package/dist/web/standalone/.next/static/chunks/app/api/recovery/route-d07a2c023f1aef1e.js +0 -1
  663. package/dist/web/standalone/.next/static/chunks/app/api/remote-questions/route-d07a2c023f1aef1e.js +0 -1
  664. package/dist/web/standalone/.next/static/chunks/app/api/session/browser/route-d07a2c023f1aef1e.js +0 -1
  665. package/dist/web/standalone/.next/static/chunks/app/api/session/command/route-d07a2c023f1aef1e.js +0 -1
  666. package/dist/web/standalone/.next/static/chunks/app/api/session/events/route-d07a2c023f1aef1e.js +0 -1
  667. package/dist/web/standalone/.next/static/chunks/app/api/session/manage/route-d07a2c023f1aef1e.js +0 -1
  668. package/dist/web/standalone/.next/static/chunks/app/api/settings-data/route-d07a2c023f1aef1e.js +0 -1
  669. package/dist/web/standalone/.next/static/chunks/app/api/shutdown/route-d07a2c023f1aef1e.js +0 -1
  670. package/dist/web/standalone/.next/static/chunks/app/api/skill-health/route-d07a2c023f1aef1e.js +0 -1
  671. package/dist/web/standalone/.next/static/chunks/app/api/steer/route-d07a2c023f1aef1e.js +0 -1
  672. package/dist/web/standalone/.next/static/chunks/app/api/terminal/input/route-d07a2c023f1aef1e.js +0 -1
  673. package/dist/web/standalone/.next/static/chunks/app/api/terminal/resize/route-d07a2c023f1aef1e.js +0 -1
  674. package/dist/web/standalone/.next/static/chunks/app/api/terminal/sessions/route-d07a2c023f1aef1e.js +0 -1
  675. package/dist/web/standalone/.next/static/chunks/app/api/terminal/stream/route-d07a2c023f1aef1e.js +0 -1
  676. package/dist/web/standalone/.next/static/chunks/app/api/terminal/upload/route-d07a2c023f1aef1e.js +0 -1
  677. package/dist/web/standalone/.next/static/chunks/app/api/undo/route-d07a2c023f1aef1e.js +0 -1
  678. package/dist/web/standalone/.next/static/chunks/app/api/update/route-d07a2c023f1aef1e.js +0 -1
  679. package/dist/web/standalone/.next/static/chunks/app/api/visualizer/route-d07a2c023f1aef1e.js +0 -1
  680. package/dist/web/standalone/.next/static/chunks/app/layout-745c6ed5fea5fb06.js +0 -1
  681. package/dist/web/standalone/.next/static/chunks/app/page-801b53eff6e83579.js +0 -1
  682. package/dist/web/standalone/.next/static/chunks/next/dist/client/components/builtin/app-error-d07a2c023f1aef1e.js +0 -1
  683. package/dist/web/standalone/.next/static/chunks/next/dist/client/components/builtin/forbidden-d07a2c023f1aef1e.js +0 -1
  684. package/dist/web/standalone/.next/static/chunks/next/dist/client/components/builtin/global-error-e6255954dccfcf0a.js +0 -1
  685. package/dist/web/standalone/.next/static/chunks/next/dist/client/components/builtin/not-found-d07a2c023f1aef1e.js +0 -1
  686. package/dist/web/standalone/.next/static/chunks/next/dist/client/components/builtin/unauthorized-d07a2c023f1aef1e.js +0 -1
  687. package/dist/web/standalone/.next/static/css/123c0bb039697968.css +0 -1
  688. package/src/resources/extensions/gsd/auto-observability.ts +0 -74
  689. package/src/resources/extensions/gsd/observability-validator.ts +0 -456
  690. package/src/resources/extensions/gsd/roadmap-mutations.ts +0 -134
  691. package/src/resources/extensions/gsd/tests/doctor-task-done-missing-summary-slice-loop.test.ts +0 -174
  692. package/src/resources/extensions/gsd/tests/plan-quality-validator.test.ts +0 -474
  693. /package/dist/web/standalone/.next/static/{VvclDCW6TAWjEyLU-EYL1 → alS4hoANx0TK4UVZY27da}/_ssgManifest.js +0 -0
@@ -1,13 +1,14 @@
1
- import { parseRoadmap, parsePlan, parseTaskPlanFile, parseSummary, parseContinue, parseRequirementCounts, parseSecretsManifest, formatSecretsManifest } from '../files.ts';
2
- import { createTestContext } from './test-helpers.ts';
3
-
4
- const { assertEq, assertTrue, report } = createTestContext();
1
+ import { describe, test } from 'node:test';
2
+ import assert from 'node:assert/strict';
3
+ import { parseRoadmap, parsePlan } from '../parsers-legacy.ts';
4
+ import { parseTaskPlanFile, parseSummary, parseContinue, parseRequirementCounts, parseSecretsManifest, formatSecretsManifest } from '../files.ts';
5
5
  // ═══════════════════════════════════════════════════════════════════════════
6
6
  // parseRoadmap tests
7
7
  // ═══════════════════════════════════════════════════════════════════════════
8
8
 
9
- console.log('\n=== parseRoadmap: full roadmap ===');
10
- {
9
+
10
+ describe('parsers', () => {
11
+ test('parseRoadmap: full roadmap', () => {
11
12
  const content = `# M001: GSD Extension — Hierarchical Planning
12
13
 
13
14
  **Vision:** Build a structured planning system for coding agents.
@@ -56,44 +57,43 @@ Consumes from S03:
56
57
 
57
58
  const r = parseRoadmap(content);
58
59
 
59
- assertEq(r.title, 'M001: GSD Extension — Hierarchical Planning', 'roadmap title');
60
- assertEq(r.vision, 'Build a structured planning system for coding agents.', 'roadmap vision');
61
- assertEq(r.successCriteria.length, 3, 'success criteria count');
62
- assertEq(r.successCriteria[0], 'All parsers have test coverage', 'first success criterion');
63
- assertEq(r.successCriteria[2], 'State derivation works correctly', 'third success criterion');
60
+ assert.deepStrictEqual(r.title, 'M001: GSD Extension — Hierarchical Planning', 'roadmap title');
61
+ assert.deepStrictEqual(r.vision, 'Build a structured planning system for coding agents.', 'roadmap vision');
62
+ assert.deepStrictEqual(r.successCriteria.length, 3, 'success criteria count');
63
+ assert.deepStrictEqual(r.successCriteria[0], 'All parsers have test coverage', 'first success criterion');
64
+ assert.deepStrictEqual(r.successCriteria[2], 'State derivation works correctly', 'third success criterion');
64
65
 
65
66
  // Slices
66
- assertEq(r.slices.length, 3, 'slice count');
67
-
68
- assertEq(r.slices[0].id, 'S01', 'S01 id');
69
- assertEq(r.slices[0].title, 'Types + File I/O', 'S01 title');
70
- assertEq(r.slices[0].risk, 'low', 'S01 risk');
71
- assertEq(r.slices[0].depends, [], 'S01 depends');
72
- assertEq(r.slices[0].done, true, 'S01 done');
73
- assertEq(r.slices[0].demo, 'All types defined and parsers work.', 'S01 demo');
74
-
75
- assertEq(r.slices[1].id, 'S02', 'S02 id');
76
- assertEq(r.slices[1].title, 'State Derivation', 'S02 title');
77
- assertEq(r.slices[1].risk, 'medium', 'S02 risk');
78
- assertEq(r.slices[1].depends, ['S01'], 'S02 depends');
79
- assertEq(r.slices[1].done, false, 'S02 done');
80
-
81
- assertEq(r.slices[2].id, 'S03', 'S03 id');
82
- assertEq(r.slices[2].risk, 'high', 'S03 risk');
83
- assertEq(r.slices[2].depends, ['S01', 'S02'], 'S03 depends');
84
- assertEq(r.slices[2].done, false, 'S03 done');
67
+ assert.deepStrictEqual(r.slices.length, 3, 'slice count');
68
+
69
+ assert.deepStrictEqual(r.slices[0].id, 'S01', 'S01 id');
70
+ assert.deepStrictEqual(r.slices[0].title, 'Types + File I/O', 'S01 title');
71
+ assert.deepStrictEqual(r.slices[0].risk, 'low', 'S01 risk');
72
+ assert.deepStrictEqual(r.slices[0].depends, [], 'S01 depends');
73
+ assert.deepStrictEqual(r.slices[0].done, true, 'S01 done');
74
+ assert.deepStrictEqual(r.slices[0].demo, 'All types defined and parsers work.', 'S01 demo');
75
+
76
+ assert.deepStrictEqual(r.slices[1].id, 'S02', 'S02 id');
77
+ assert.deepStrictEqual(r.slices[1].title, 'State Derivation', 'S02 title');
78
+ assert.deepStrictEqual(r.slices[1].risk, 'medium', 'S02 risk');
79
+ assert.deepStrictEqual(r.slices[1].depends, ['S01'], 'S02 depends');
80
+ assert.deepStrictEqual(r.slices[1].done, false, 'S02 done');
81
+
82
+ assert.deepStrictEqual(r.slices[2].id, 'S03', 'S03 id');
83
+ assert.deepStrictEqual(r.slices[2].risk, 'high', 'S03 risk');
84
+ assert.deepStrictEqual(r.slices[2].depends, ['S01', 'S02'], 'S03 depends');
85
+ assert.deepStrictEqual(r.slices[2].done, false, 'S03 done');
85
86
 
86
87
  // Boundary map
87
- assertEq(r.boundaryMap.length, 2, 'boundary map entry count');
88
- assertEq(r.boundaryMap[0].fromSlice, 'S01', 'bm[0] from');
89
- assertEq(r.boundaryMap[0].toSlice, 'S02', 'bm[0] to');
90
- assertTrue(r.boundaryMap[0].produces.includes('types.ts'), 'bm[0] produces mentions types.ts');
91
- assertEq(r.boundaryMap[1].fromSlice, 'S02', 'bm[1] from');
92
- assertEq(r.boundaryMap[1].toSlice, 'S03', 'bm[1] to');
93
- }
94
-
95
- console.log('\n=== parseRoadmap: empty slices section ===');
96
- {
88
+ assert.deepStrictEqual(r.boundaryMap.length, 2, 'boundary map entry count');
89
+ assert.deepStrictEqual(r.boundaryMap[0].fromSlice, 'S01', 'bm[0] from');
90
+ assert.deepStrictEqual(r.boundaryMap[0].toSlice, 'S02', 'bm[0] to');
91
+ assert.ok(r.boundaryMap[0].produces.includes('types.ts'), 'bm[0] produces mentions types.ts');
92
+ assert.deepStrictEqual(r.boundaryMap[1].fromSlice, 'S02', 'bm[1] from');
93
+ assert.deepStrictEqual(r.boundaryMap[1].toSlice, 'S03', 'bm[1] to');
94
+ });
95
+
96
+ test('parseRoadmap: empty slices section', () => {
97
97
  const content = `# M002: Empty Milestone
98
98
 
99
99
  **Vision:** Nothing yet.
@@ -104,13 +104,12 @@ console.log('\n=== parseRoadmap: empty slices section ===');
104
104
  `;
105
105
 
106
106
  const r = parseRoadmap(content);
107
- assertEq(r.title, 'M002: Empty Milestone', 'title with empty slices');
108
- assertEq(r.slices.length, 0, 'no slices parsed');
109
- assertEq(r.boundaryMap.length, 0, 'no boundary map entries');
110
- }
107
+ assert.deepStrictEqual(r.title, 'M002: Empty Milestone', 'title with empty slices');
108
+ assert.deepStrictEqual(r.slices.length, 0, 'no slices parsed');
109
+ assert.deepStrictEqual(r.boundaryMap.length, 0, 'no boundary map entries');
110
+ });
111
111
 
112
- console.log('\n=== parseRoadmap: malformed checkbox lines ===');
113
- {
112
+ test('parseRoadmap: malformed checkbox lines', () => {
114
113
  // Lines that don't match the expected bold pattern should be skipped
115
114
  const content = `# M003: Malformed
116
115
 
@@ -129,15 +128,14 @@ console.log('\n=== parseRoadmap: malformed checkbox lines ===');
129
128
 
130
129
  const r = parseRoadmap(content);
131
130
  // Only S02 and S03 should be parsed (malformed lines without bold markers are skipped)
132
- assertEq(r.slices.length, 2, 'only valid slices parsed from malformed input');
133
- assertEq(r.slices[0].id, 'S02', 'first valid slice is S02');
134
- assertEq(r.slices[0].done, true, 'S02 done');
135
- assertEq(r.slices[1].id, 'S03', 'second valid slice is S03');
136
- assertEq(r.slices[1].depends, ['S02'], 'S03 depends on S02');
137
- }
138
-
139
- console.log('\n=== parseRoadmap: lowercase vs uppercase X for done ===');
140
- {
131
+ assert.deepStrictEqual(r.slices.length, 2, 'only valid slices parsed from malformed input');
132
+ assert.deepStrictEqual(r.slices[0].id, 'S02', 'first valid slice is S02');
133
+ assert.deepStrictEqual(r.slices[0].done, true, 'S02 done');
134
+ assert.deepStrictEqual(r.slices[1].id, 'S03', 'second valid slice is S03');
135
+ assert.deepStrictEqual(r.slices[1].depends, ['S02'], 'S03 depends on S02');
136
+ });
137
+
138
+ test('parseRoadmap: lowercase vs uppercase X for done', () => {
141
139
  const content = `# M004: Case Test
142
140
 
143
141
  **Vision:** Test X case sensitivity.
@@ -155,14 +153,13 @@ console.log('\n=== parseRoadmap: lowercase vs uppercase X for done ===');
155
153
  `;
156
154
 
157
155
  const r = parseRoadmap(content);
158
- assertEq(r.slices.length, 3, 'all three slices parsed');
159
- assertEq(r.slices[0].done, true, 'lowercase x is done');
160
- assertEq(r.slices[1].done, true, 'uppercase X is done');
161
- assertEq(r.slices[2].done, false, 'space is not done');
162
- }
163
-
164
- console.log('\n=== parseRoadmap: missing boundary map ===');
165
- {
156
+ assert.deepStrictEqual(r.slices.length, 3, 'all three slices parsed');
157
+ assert.deepStrictEqual(r.slices[0].done, true, 'lowercase x is done');
158
+ assert.deepStrictEqual(r.slices[1].done, true, 'uppercase X is done');
159
+ assert.deepStrictEqual(r.slices[2].done, false, 'space is not done');
160
+ });
161
+
162
+ test('parseRoadmap: missing boundary map', () => {
166
163
  const content = `# M005: No Boundary Map
167
164
 
168
165
  **Vision:** A roadmap without a boundary map section.
@@ -179,29 +176,27 @@ console.log('\n=== parseRoadmap: missing boundary map ===');
179
176
  `;
180
177
 
181
178
  const r = parseRoadmap(content);
182
- assertEq(r.title, 'M005: No Boundary Map', 'title');
183
- assertEq(r.slices.length, 1, 'one slice');
184
- assertEq(r.boundaryMap.length, 0, 'empty boundary map when section missing');
185
- assertEq(r.successCriteria.length, 1, 'one success criterion');
186
- }
187
-
188
- console.log('\n=== parseRoadmap: no sections at all ===');
189
- {
179
+ assert.deepStrictEqual(r.title, 'M005: No Boundary Map', 'title');
180
+ assert.deepStrictEqual(r.slices.length, 1, 'one slice');
181
+ assert.deepStrictEqual(r.boundaryMap.length, 0, 'empty boundary map when section missing');
182
+ assert.deepStrictEqual(r.successCriteria.length, 1, 'one success criterion');
183
+ });
184
+
185
+ test('parseRoadmap: no sections at all', () => {
190
186
  const content = `# M006: Bare Minimum
191
187
 
192
188
  Just a title and nothing else.
193
189
  `;
194
190
 
195
191
  const r = parseRoadmap(content);
196
- assertEq(r.title, 'M006: Bare Minimum', 'title from bare roadmap');
197
- assertEq(r.vision, '', 'empty vision');
198
- assertEq(r.successCriteria.length, 0, 'no success criteria');
199
- assertEq(r.slices.length, 0, 'no slices');
200
- assertEq(r.boundaryMap.length, 0, 'no boundary map');
201
- }
202
-
203
- console.log('\n=== parseRoadmap: slice with no demo blockquote ===');
204
- {
192
+ assert.deepStrictEqual(r.title, 'M006: Bare Minimum', 'title from bare roadmap');
193
+ assert.deepStrictEqual(r.vision, '', 'empty vision');
194
+ assert.deepStrictEqual(r.successCriteria.length, 0, 'no success criteria');
195
+ assert.deepStrictEqual(r.slices.length, 0, 'no slices');
196
+ assert.deepStrictEqual(r.boundaryMap.length, 0, 'no boundary map');
197
+ });
198
+
199
+ test('parseRoadmap: slice with no demo blockquote', () => {
205
200
  const content = `# M007: No Demo
206
201
 
207
202
  **Vision:** Testing slices without demo lines.
@@ -213,13 +208,12 @@ console.log('\n=== parseRoadmap: slice with no demo blockquote ===');
213
208
  `;
214
209
 
215
210
  const r = parseRoadmap(content);
216
- assertEq(r.slices.length, 2, 'two slices without demos');
217
- assertEq(r.slices[0].demo, '', 'S01 demo empty');
218
- assertEq(r.slices[1].demo, '', 'S02 demo empty');
219
- }
211
+ assert.deepStrictEqual(r.slices.length, 2, 'two slices without demos');
212
+ assert.deepStrictEqual(r.slices[0].demo, '', 'S01 demo empty');
213
+ assert.deepStrictEqual(r.slices[1].demo, '', 'S02 demo empty');
214
+ });
220
215
 
221
- console.log('\n=== parseRoadmap: missing risk defaults to low ===');
222
- {
216
+ test('parseRoadmap: missing risk defaults to low', () => {
223
217
  const content = `# M008: Default Risk
224
218
 
225
219
  **Vision:** Test default risk.
@@ -231,16 +225,14 @@ console.log('\n=== parseRoadmap: missing risk defaults to low ===');
231
225
  `;
232
226
 
233
227
  const r = parseRoadmap(content);
234
- assertEq(r.slices.length, 1, 'one slice');
235
- assertEq(r.slices[0].risk, 'low', 'default risk is low');
236
- }
228
+ assert.deepStrictEqual(r.slices.length, 1, 'one slice');
229
+ assert.deepStrictEqual(r.slices[0].risk, 'low', 'default risk is low');
230
+ });
237
231
 
238
232
  // ═══════════════════════════════════════════════════════════════════════════
239
233
  // parsePlan tests
240
234
  // ═══════════════════════════════════════════════════════════════════════════
241
-
242
- console.log('\n=== parsePlan: full plan ===');
243
- {
235
+ test('parsePlan: full plan', () => {
244
236
  const content = `---
245
237
  estimated_steps: 6
246
238
  estimated_files: 3
@@ -276,42 +268,41 @@ skills_used:
276
268
  `;
277
269
 
278
270
  const taskPlan = parseTaskPlanFile(content);
279
- assertEq(taskPlan.frontmatter.estimated_steps, 6, 'task plan frontmatter estimated_steps');
280
- assertEq(taskPlan.frontmatter.estimated_files, 3, 'task plan frontmatter estimated_files');
281
- assertEq(taskPlan.frontmatter.skills_used.length, 2, 'task plan frontmatter skills_used count');
282
- assertEq(taskPlan.frontmatter.skills_used[0], 'typescript', 'first task plan skill');
283
- assertEq(taskPlan.frontmatter.skills_used[1], 'testing', 'second task plan skill');
271
+ assert.deepStrictEqual(taskPlan.frontmatter.estimated_steps, 6, 'task plan frontmatter estimated_steps');
272
+ assert.deepStrictEqual(taskPlan.frontmatter.estimated_files, 3, 'task plan frontmatter estimated_files');
273
+ assert.deepStrictEqual(taskPlan.frontmatter.skills_used.length, 2, 'task plan frontmatter skills_used count');
274
+ assert.deepStrictEqual(taskPlan.frontmatter.skills_used[0], 'typescript', 'first task plan skill');
275
+ assert.deepStrictEqual(taskPlan.frontmatter.skills_used[1], 'testing', 'second task plan skill');
284
276
 
285
277
  const p = parsePlan(content);
286
278
 
287
- assertEq(p.id, 'S01', 'plan id');
288
- assertEq(p.title, 'Parser Test Suite', 'plan title');
289
- assertEq(p.goal, 'All 5 parsers have test coverage with edge cases.', 'plan goal');
290
- assertEq(p.demo, '`node --test tests/parsers.test.ts` passes with zero failures.', 'plan demo');
279
+ assert.deepStrictEqual(p.id, 'S01', 'plan id');
280
+ assert.deepStrictEqual(p.title, 'Parser Test Suite', 'plan title');
281
+ assert.deepStrictEqual(p.goal, 'All 5 parsers have test coverage with edge cases.', 'plan goal');
282
+ assert.deepStrictEqual(p.demo, '`node --test tests/parsers.test.ts` passes with zero failures.', 'plan demo');
291
283
 
292
284
  // Must-haves
293
- assertEq(p.mustHaves.length, 3, 'must-have count');
294
- assertEq(p.mustHaves[0], 'parseRoadmap tests cover happy path and edge cases', 'first must-have');
285
+ assert.deepStrictEqual(p.mustHaves.length, 3, 'must-have count');
286
+ assert.deepStrictEqual(p.mustHaves[0], 'parseRoadmap tests cover happy path and edge cases', 'first must-have');
295
287
 
296
288
  // Tasks
297
- assertEq(p.tasks.length, 2, 'task count');
289
+ assert.deepStrictEqual(p.tasks.length, 2, 'task count');
298
290
 
299
- assertEq(p.tasks[0].id, 'T01', 'T01 id');
300
- assertEq(p.tasks[0].title, 'Test parseRoadmap and parsePlan', 'T01 title');
301
- assertEq(p.tasks[0].done, false, 'T01 not done');
302
- assertTrue(p.tasks[0].description.includes('comprehensive tests'), 'T01 description content');
291
+ assert.deepStrictEqual(p.tasks[0].id, 'T01', 'T01 id');
292
+ assert.deepStrictEqual(p.tasks[0].title, 'Test parseRoadmap and parsePlan', 'T01 title');
293
+ assert.deepStrictEqual(p.tasks[0].done, false, 'T01 not done');
294
+ assert.ok(p.tasks[0].description.includes('comprehensive tests'), 'T01 description content');
303
295
 
304
- assertEq(p.tasks[1].id, 'T02', 'T02 id');
305
- assertEq(p.tasks[1].title, 'Test parseSummary and parseContinue', 'T02 title');
306
- assertEq(p.tasks[1].done, true, 'T02 done');
296
+ assert.deepStrictEqual(p.tasks[1].id, 'T02', 'T02 id');
297
+ assert.deepStrictEqual(p.tasks[1].title, 'Test parseSummary and parseContinue', 'T02 title');
298
+ assert.deepStrictEqual(p.tasks[1].done, true, 'T02 done');
307
299
 
308
300
  // Files likely touched
309
- assertEq(p.filesLikelyTouched.length, 3, 'files likely touched count');
310
- assertTrue(p.filesLikelyTouched[0].includes('tests/parsers.test.ts'), 'first file');
311
- }
301
+ assert.deepStrictEqual(p.filesLikelyTouched.length, 3, 'files likely touched count');
302
+ assert.ok(p.filesLikelyTouched[0].includes('tests/parsers.test.ts'), 'first file');
303
+ });
312
304
 
313
- console.log('\n=== parseTaskPlanFile: defaults missing frontmatter fields ===');
314
- {
305
+ test('parseTaskPlanFile: defaults missing frontmatter fields', () => {
315
306
  const content = `# T01: Minimal task plan
316
307
 
317
308
  ## Description
@@ -320,13 +311,12 @@ No frontmatter here.
320
311
  `;
321
312
 
322
313
  const taskPlan = parseTaskPlanFile(content);
323
- assertEq(taskPlan.frontmatter.estimated_steps, undefined, 'estimated_steps defaults undefined');
324
- assertEq(taskPlan.frontmatter.estimated_files, undefined, 'estimated_files defaults undefined');
325
- assertEq(taskPlan.frontmatter.skills_used.length, 0, 'skills_used defaults empty array');
326
- }
314
+ assert.deepStrictEqual(taskPlan.frontmatter.estimated_steps, undefined, 'estimated_steps defaults undefined');
315
+ assert.deepStrictEqual(taskPlan.frontmatter.estimated_files, undefined, 'estimated_files defaults undefined');
316
+ assert.deepStrictEqual(taskPlan.frontmatter.skills_used.length, 0, 'skills_used defaults empty array');
317
+ });
327
318
 
328
- console.log('\n=== parseTaskPlanFile: accepts scalar skills_used and numeric strings ===');
329
- {
319
+ test('parseTaskPlanFile: accepts scalar skills_used and numeric strings', () => {
330
320
  const content = `---
331
321
  estimated_steps: "9"
332
322
  estimated_files: "4"
@@ -337,14 +327,13 @@ skills_used: react-best-practices
337
327
  `;
338
328
 
339
329
  const taskPlan = parseTaskPlanFile(content);
340
- assertEq(taskPlan.frontmatter.estimated_steps, 9, 'string estimated_steps parsed');
341
- assertEq(taskPlan.frontmatter.estimated_files, 4, 'string estimated_files parsed');
342
- assertEq(taskPlan.frontmatter.skills_used.length, 1, 'scalar skills_used normalized to array');
343
- assertEq(taskPlan.frontmatter.skills_used[0], 'react-best-practices', 'scalar skill preserved');
344
- }
345
-
346
- console.log('\n=== parseTaskPlanFile: filters blank skills_used items ===');
347
- {
330
+ assert.deepStrictEqual(taskPlan.frontmatter.estimated_steps, 9, 'string estimated_steps parsed');
331
+ assert.deepStrictEqual(taskPlan.frontmatter.estimated_files, 4, 'string estimated_files parsed');
332
+ assert.deepStrictEqual(taskPlan.frontmatter.skills_used.length, 1, 'scalar skills_used normalized to array');
333
+ assert.deepStrictEqual(taskPlan.frontmatter.skills_used[0], 'react-best-practices', 'scalar skill preserved');
334
+ });
335
+
336
+ test('parseTaskPlanFile: filters blank skills_used items', () => {
348
337
  const content = `---
349
338
  skills_used:
350
339
  - react
@@ -356,13 +345,12 @@ skills_used:
356
345
  `;
357
346
 
358
347
  const taskPlan = parseTaskPlanFile(content);
359
- assertEq(taskPlan.frontmatter.skills_used.length, 2, 'blank skill entries removed');
360
- assertEq(taskPlan.frontmatter.skills_used[0], 'react', 'first remaining skill');
361
- assertEq(taskPlan.frontmatter.skills_used[1], 'testing', 'second remaining skill');
362
- }
348
+ assert.deepStrictEqual(taskPlan.frontmatter.skills_used.length, 2, 'blank skill entries removed');
349
+ assert.deepStrictEqual(taskPlan.frontmatter.skills_used[0], 'react', 'first remaining skill');
350
+ assert.deepStrictEqual(taskPlan.frontmatter.skills_used[1], 'testing', 'second remaining skill');
351
+ });
363
352
 
364
- console.log('\n=== parseTaskPlanFile: invalid numeric frontmatter ignored ===');
365
- {
353
+ test('parseTaskPlanFile: invalid numeric frontmatter ignored', () => {
366
354
  const content = `---
367
355
  estimated_steps: many
368
356
  estimated_files: unknown
@@ -372,12 +360,11 @@ estimated_files: unknown
372
360
  `;
373
361
 
374
362
  const taskPlan = parseTaskPlanFile(content);
375
- assertEq(taskPlan.frontmatter.estimated_steps, undefined, 'invalid estimated_steps ignored');
376
- assertEq(taskPlan.frontmatter.estimated_files, undefined, 'invalid estimated_files ignored');
377
- }
363
+ assert.deepStrictEqual(taskPlan.frontmatter.estimated_steps, undefined, 'invalid estimated_steps ignored');
364
+ assert.deepStrictEqual(taskPlan.frontmatter.estimated_files, undefined, 'invalid estimated_files ignored');
365
+ });
378
366
 
379
- console.log('\n=== parseTaskPlanFile: parsePlan ignores task-plan frontmatter ===');
380
- {
367
+ test('parseTaskPlanFile: parsePlan ignores task-plan frontmatter', () => {
381
368
  const content = `---
382
369
  estimated_steps: 2
383
370
  estimated_files: 1
@@ -397,12 +384,11 @@ skills_used:
397
384
  `;
398
385
 
399
386
  const p = parsePlan(content);
400
- assertEq(p.id, 'S11', 'plan id still parsed with frontmatter');
401
- assertEq(p.tasks.length, 1, 'task still parsed with frontmatter');
402
- }
387
+ assert.deepStrictEqual(p.id, 'S11', 'plan id still parsed with frontmatter');
388
+ assert.deepStrictEqual(p.tasks.length, 1, 'task still parsed with frontmatter');
389
+ });
403
390
 
404
- console.log('\n=== parsePlan: multi-line task description concatenation ===');
405
- {
391
+ test('parsePlan: multi-line task description concatenation', () => {
406
392
  const content = `# S02: Multi-line Test
407
393
 
408
394
  **Goal:** Test multi-line descriptions.
@@ -429,16 +415,15 @@ console.log('\n=== parsePlan: multi-line task description concatenation ===');
429
415
 
430
416
  const p = parsePlan(content);
431
417
 
432
- assertEq(p.tasks.length, 2, 'two tasks');
433
- assertTrue(p.tasks[0].description.includes('First line'), 'T01 desc has first line');
434
- assertTrue(p.tasks[0].description.includes('Second line'), 'T01 desc has second line');
435
- assertTrue(p.tasks[0].description.includes('Third line'), 'T01 desc has third line');
436
- assertTrue(p.tasks[0].description.includes('description. Second'), 'lines joined with space');
437
- assertEq(p.tasks[1].description, 'Just one line.', 'T02 single-line desc');
438
- }
418
+ assert.deepStrictEqual(p.tasks.length, 2, 'two tasks');
419
+ assert.ok(p.tasks[0].description.includes('First line'), 'T01 desc has first line');
420
+ assert.ok(p.tasks[0].description.includes('Second line'), 'T01 desc has second line');
421
+ assert.ok(p.tasks[0].description.includes('Third line'), 'T01 desc has third line');
422
+ assert.ok(p.tasks[0].description.includes('description. Second'), 'lines joined with space');
423
+ assert.deepStrictEqual(p.tasks[1].description, 'Just one line.', 'T02 single-line desc');
424
+ });
439
425
 
440
- console.log('\n=== parsePlan: frontmatter does not pollute task descriptions ===');
441
- {
426
+ test('parsePlan: frontmatter does not pollute task descriptions', () => {
442
427
  const content = `---
443
428
  estimated_steps: 2
444
429
  estimated_files: 1
@@ -456,12 +441,11 @@ skills_used:
456
441
  `;
457
442
 
458
443
  const p = parsePlan(content);
459
- assertEq(p.tasks.length, 1, 'one task parsed with frontmatter');
460
- assertEq(p.tasks[0].description, 'First line of description. Second line of description.', 'frontmatter excluded from description');
461
- }
444
+ assert.deepStrictEqual(p.tasks.length, 1, 'one task parsed with frontmatter');
445
+ assert.deepStrictEqual(p.tasks[0].description, 'First line of description. Second line of description.', 'frontmatter excluded from description');
446
+ });
462
447
 
463
- console.log('\n=== parsePlan: task with missing estimate ===');
464
- {
448
+ test('parsePlan: task with missing estimate', () => {
465
449
  const content = `# S03: No Estimate
466
450
 
467
451
  **Goal:** Handle tasks without estimates.
@@ -477,15 +461,14 @@ console.log('\n=== parsePlan: task with missing estimate ===');
477
461
  `;
478
462
 
479
463
  const p = parsePlan(content);
480
- assertEq(p.tasks.length, 2, 'two tasks parsed');
481
- assertEq(p.tasks[0].id, 'T01', 'T01 id');
482
- assertEq(p.tasks[0].title, 'No Estimate Task', 'T01 title without estimate');
483
- assertEq(p.tasks[0].done, false, 'T01 not done');
484
- assertEq(p.tasks[1].id, 'T02', 'T02 id');
485
- }
486
-
487
- console.log('\n=== parsePlan: empty tasks section ===');
488
- {
464
+ assert.deepStrictEqual(p.tasks.length, 2, 'two tasks parsed');
465
+ assert.deepStrictEqual(p.tasks[0].id, 'T01', 'T01 id');
466
+ assert.deepStrictEqual(p.tasks[0].title, 'No Estimate Task', 'T01 title without estimate');
467
+ assert.deepStrictEqual(p.tasks[0].done, false, 'T01 not done');
468
+ assert.deepStrictEqual(p.tasks[1].id, 'T02', 'T02 id');
469
+ });
470
+
471
+ test('parsePlan: empty tasks section', () => {
489
472
  const content = `# S04: Empty Tasks
490
473
 
491
474
  **Goal:** No tasks yet.
@@ -503,14 +486,13 @@ console.log('\n=== parsePlan: empty tasks section ===');
503
486
  `;
504
487
 
505
488
  const p = parsePlan(content);
506
- assertEq(p.id, 'S04', 'plan id with empty tasks');
507
- assertEq(p.tasks.length, 0, 'no tasks');
508
- assertEq(p.mustHaves.length, 1, 'one must-have');
509
- assertEq(p.filesLikelyTouched.length, 1, 'one file');
510
- }
511
-
512
- console.log('\n=== parsePlan: no H1 ===');
513
- {
489
+ assert.deepStrictEqual(p.id, 'S04', 'plan id with empty tasks');
490
+ assert.deepStrictEqual(p.tasks.length, 0, 'no tasks');
491
+ assert.deepStrictEqual(p.mustHaves.length, 1, 'one must-have');
492
+ assert.deepStrictEqual(p.filesLikelyTouched.length, 1, 'one file');
493
+ });
494
+
495
+ test('parsePlan: no H1', () => {
514
496
  const content = `**Goal:** A plan without a heading.
515
497
  **Demo:** Still parses.
516
498
 
@@ -521,15 +503,14 @@ console.log('\n=== parsePlan: no H1 ===');
521
503
  `;
522
504
 
523
505
  const p = parsePlan(content);
524
- assertEq(p.id, '', 'empty id without H1');
525
- assertEq(p.title, '', 'empty title without H1');
526
- assertEq(p.goal, 'A plan without a heading.', 'goal still parsed');
527
- assertEq(p.tasks.length, 1, 'task still parsed');
528
- assertEq(p.tasks[0].id, 'T01', 'task id');
529
- }
530
-
531
- console.log('\n=== parsePlan: task estimate backtick in description ===');
532
- {
506
+ assert.deepStrictEqual(p.id, '', 'empty id without H1');
507
+ assert.deepStrictEqual(p.title, '', 'empty title without H1');
508
+ assert.deepStrictEqual(p.goal, 'A plan without a heading.', 'goal still parsed');
509
+ assert.deepStrictEqual(p.tasks.length, 1, 'task still parsed');
510
+ assert.deepStrictEqual(p.tasks[0].id, 'T01', 'task id');
511
+ });
512
+
513
+ test('parsePlan: task estimate backtick in description', () => {
533
514
  const content = `# S05: Estimate Handling
534
515
 
535
516
  **Goal:** Test estimate text handling.
@@ -542,14 +523,13 @@ console.log('\n=== parsePlan: task estimate backtick in description ===');
542
523
  `;
543
524
 
544
525
  const p = parsePlan(content);
545
- assertEq(p.tasks.length, 1, 'one task');
546
- assertEq(p.tasks[0].id, 'T01', 'task id');
547
- assertEq(p.tasks[0].title, 'With Estimate', 'title excludes estimate');
548
- assertTrue(p.tasks[0].description.includes('Main description'), 'description from continuation line');
549
- }
550
-
551
- console.log('\n=== parsePlan: uppercase X for done ===');
552
- {
526
+ assert.deepStrictEqual(p.tasks.length, 1, 'one task');
527
+ assert.deepStrictEqual(p.tasks[0].id, 'T01', 'task id');
528
+ assert.deepStrictEqual(p.tasks[0].title, 'With Estimate', 'title excludes estimate');
529
+ assert.ok(p.tasks[0].description.includes('Main description'), 'description from continuation line');
530
+ });
531
+
532
+ test('parsePlan: uppercase X for done', () => {
553
533
  const content = `# S06: Case Test
554
534
 
555
535
  **Goal:** Test case.
@@ -565,12 +545,11 @@ console.log('\n=== parsePlan: uppercase X for done ===');
565
545
  `;
566
546
 
567
547
  const p = parsePlan(content);
568
- assertEq(p.tasks[0].done, true, 'uppercase X is done');
569
- assertEq(p.tasks[1].done, true, 'lowercase x is done');
570
- }
548
+ assert.deepStrictEqual(p.tasks[0].done, true, 'uppercase X is done');
549
+ assert.deepStrictEqual(p.tasks[1].done, true, 'lowercase x is done');
550
+ });
571
551
 
572
- console.log('\n=== parsePlan: no Must-Haves section ===');
573
- {
552
+ test('parsePlan: no Must-Haves section', () => {
574
553
  const content = `# S07: No Must-Haves
575
554
 
576
555
  **Goal:** Test missing must-haves.
@@ -583,12 +562,11 @@ console.log('\n=== parsePlan: no Must-Haves section ===');
583
562
  `;
584
563
 
585
564
  const p = parsePlan(content);
586
- assertEq(p.mustHaves.length, 0, 'empty must-haves');
587
- assertEq(p.tasks.length, 1, 'task still parsed');
588
- }
565
+ assert.deepStrictEqual(p.mustHaves.length, 0, 'empty must-haves');
566
+ assert.deepStrictEqual(p.tasks.length, 1, 'task still parsed');
567
+ });
589
568
 
590
- console.log('\n=== parsePlan: no Files Likely Touched section ===');
591
- {
569
+ test('parsePlan: no Files Likely Touched section', () => {
592
570
  const content = `# S08: No Files
593
571
 
594
572
  **Goal:** Test missing files section.
@@ -601,11 +579,10 @@ console.log('\n=== parsePlan: no Files Likely Touched section ===');
601
579
  `;
602
580
 
603
581
  const p = parsePlan(content);
604
- assertEq(p.filesLikelyTouched.length, 0, 'empty files likely touched');
605
- }
582
+ assert.deepStrictEqual(p.filesLikelyTouched.length, 0, 'empty files likely touched');
583
+ });
606
584
 
607
- console.log('\n=== parsePlan: old-format task entries (no sublines) ===');
608
- {
585
+ test('parsePlan: old-format task entries (no sublines)', () => {
609
586
  const content = `# S09: Old Format
610
587
 
611
588
  **Goal:** Test old-format compatibility.
@@ -618,16 +595,15 @@ console.log('\n=== parsePlan: old-format task entries (no sublines) ===');
618
595
  `;
619
596
 
620
597
  const p = parsePlan(content);
621
- assertEq(p.tasks.length, 1, 'one task parsed');
622
- assertEq(p.tasks[0].id, 'T01', 'task id');
623
- assertEq(p.tasks[0].title, 'Classic Task', 'task title');
624
- assertEq(p.tasks[0].done, false, 'task not done');
625
- assertEq(p.tasks[0].files, undefined, 'files is undefined for old-format entry');
626
- assertEq(p.tasks[0].verify, undefined, 'verify is undefined for old-format entry');
627
- }
628
-
629
- console.log('\n=== parsePlan: new-format task entries with Files and Verify sublines ===');
630
- {
598
+ assert.deepStrictEqual(p.tasks.length, 1, 'one task parsed');
599
+ assert.deepStrictEqual(p.tasks[0].id, 'T01', 'task id');
600
+ assert.deepStrictEqual(p.tasks[0].title, 'Classic Task', 'task title');
601
+ assert.deepStrictEqual(p.tasks[0].done, false, 'task not done');
602
+ assert.deepStrictEqual(p.tasks[0].files, undefined, 'files is undefined for old-format entry');
603
+ assert.deepStrictEqual(p.tasks[0].verify, undefined, 'verify is undefined for old-format entry');
604
+ });
605
+
606
+ test('parsePlan: new-format task entries with Files and Verify sublines', () => {
631
607
  const content = `# S10: New Format
632
608
 
633
609
  **Goal:** Test new-format subline extraction.
@@ -642,18 +618,17 @@ console.log('\n=== parsePlan: new-format task entries with Files and Verify subl
642
618
  `;
643
619
 
644
620
  const p = parsePlan(content);
645
- assertEq(p.tasks.length, 1, 'one task parsed');
646
- assertEq(p.tasks[0].id, 'T01', 'task id');
647
- assertTrue(Array.isArray(p.tasks[0].files), 'files is an array');
648
- assertEq(p.tasks[0].files!.length, 2, 'files array has two entries');
649
- assertEq(p.tasks[0].files![0], 'types.ts', 'first file is types.ts');
650
- assertEq(p.tasks[0].files![1], 'files.ts', 'second file is files.ts');
651
- assertEq(p.tasks[0].verify, 'run the test suite', 'verify string extracted correctly');
652
- assertTrue(p.tasks[0].description.includes('Why: because we need typed plan entries'), 'Why line accumulates into description');
653
- }
654
-
655
- console.log('\n=== parsePlan: heading-style task entries (### T01 -- Title) ===');
656
- {
621
+ assert.deepStrictEqual(p.tasks.length, 1, 'one task parsed');
622
+ assert.deepStrictEqual(p.tasks[0].id, 'T01', 'task id');
623
+ assert.ok(Array.isArray(p.tasks[0].files), 'files is an array');
624
+ assert.deepStrictEqual(p.tasks[0].files!.length, 2, 'files array has two entries');
625
+ assert.deepStrictEqual(p.tasks[0].files![0], 'types.ts', 'first file is types.ts');
626
+ assert.deepStrictEqual(p.tasks[0].files![1], 'files.ts', 'second file is files.ts');
627
+ assert.deepStrictEqual(p.tasks[0].verify, 'run the test suite', 'verify string extracted correctly');
628
+ assert.ok(p.tasks[0].description.includes('Why: because we need typed plan entries'), 'Why line accumulates into description');
629
+ });
630
+
631
+ test('parsePlan: heading-style task entries (### T01 -- Title)', () => {
657
632
  const content = `# S11: Heading Style
658
633
 
659
634
  **Goal:** Test heading-style task parsing.
@@ -673,20 +648,19 @@ Some description for the second task.
673
648
  `;
674
649
 
675
650
  const p = parsePlan(content);
676
- assertEq(p.tasks.length, 2, 'heading-style task count');
677
- assertEq(p.tasks[0].id, 'T01', 'heading T01 id');
678
- assertEq(p.tasks[0].title, 'Implement feature', 'heading T01 title');
679
- assertEq(p.tasks[0].done, false, 'heading T01 not done (headings have no checkbox)');
680
- assertEq(p.tasks[0].files![0], 'src/feature.ts', 'heading T01 files extracted');
681
- assertEq(p.tasks[0].verify, 'npm test', 'heading T01 verify extracted');
682
- assertEq(p.tasks[1].id, 'T02', 'heading T02 id');
683
- assertEq(p.tasks[1].title, 'Write tests', 'heading T02 title');
684
- assertEq(p.tasks[1].estimate, '1h', 'heading T02 estimate');
685
- assertTrue(p.tasks[1].description.includes('Some description'), 'heading T02 description');
686
- }
687
-
688
- console.log('\n=== parsePlan: heading-style with colon separator (### T01: Title) ===');
689
- {
651
+ assert.deepStrictEqual(p.tasks.length, 2, 'heading-style task count');
652
+ assert.deepStrictEqual(p.tasks[0].id, 'T01', 'heading T01 id');
653
+ assert.deepStrictEqual(p.tasks[0].title, 'Implement feature', 'heading T01 title');
654
+ assert.deepStrictEqual(p.tasks[0].done, false, 'heading T01 not done (headings have no checkbox)');
655
+ assert.deepStrictEqual(p.tasks[0].files![0], 'src/feature.ts', 'heading T01 files extracted');
656
+ assert.deepStrictEqual(p.tasks[0].verify, 'npm test', 'heading T01 verify extracted');
657
+ assert.deepStrictEqual(p.tasks[1].id, 'T02', 'heading T02 id');
658
+ assert.deepStrictEqual(p.tasks[1].title, 'Write tests', 'heading T02 title');
659
+ assert.deepStrictEqual(p.tasks[1].estimate, '1h', 'heading T02 estimate');
660
+ assert.ok(p.tasks[1].description.includes('Some description'), 'heading T02 description');
661
+ });
662
+
663
+ test('parsePlan: heading-style with colon separator (### T01: Title)', () => {
690
664
  const content = `# S12: Heading Colon Style
691
665
 
692
666
  **Goal:** Test colon-separated heading tasks.
@@ -702,16 +676,15 @@ console.log('\n=== parsePlan: heading-style with colon separator (### T01: Title
702
676
  `;
703
677
 
704
678
  const p = parsePlan(content);
705
- assertEq(p.tasks.length, 2, 'colon heading task count');
706
- assertEq(p.tasks[0].id, 'T01', 'colon heading T01 id');
707
- assertEq(p.tasks[0].title, 'Setup project', 'colon heading T01 title');
708
- assertEq(p.tasks[1].id, 'T02', 'colon heading T02 id');
709
- assertEq(p.tasks[1].title, 'Add CI pipeline', 'colon heading T02 title');
710
- assertEq(p.tasks[1].estimate, '30m', 'colon heading T02 estimate');
711
- }
712
-
713
- console.log('\n=== parsePlan: heading-style with em-dash separator (### T01 — Title) ===');
714
- {
679
+ assert.deepStrictEqual(p.tasks.length, 2, 'colon heading task count');
680
+ assert.deepStrictEqual(p.tasks[0].id, 'T01', 'colon heading T01 id');
681
+ assert.deepStrictEqual(p.tasks[0].title, 'Setup project', 'colon heading T01 title');
682
+ assert.deepStrictEqual(p.tasks[1].id, 'T02', 'colon heading T02 id');
683
+ assert.deepStrictEqual(p.tasks[1].title, 'Add CI pipeline', 'colon heading T02 title');
684
+ assert.deepStrictEqual(p.tasks[1].estimate, '30m', 'colon heading T02 estimate');
685
+ });
686
+
687
+ test('parsePlan: heading-style with em-dash separator (### T01 — Title)', () => {
715
688
  const content = `# S13: Em-Dash Style
716
689
 
717
690
  **Goal:** Test em-dash separated heading tasks.
@@ -725,13 +698,12 @@ Widget description.
725
698
  `;
726
699
 
727
700
  const p = parsePlan(content);
728
- assertEq(p.tasks.length, 1, 'em-dash heading task count');
729
- assertEq(p.tasks[0].id, 'T01', 'em-dash heading T01 id');
730
- assertEq(p.tasks[0].title, 'Build the widget', 'em-dash heading T01 title');
731
- }
701
+ assert.deepStrictEqual(p.tasks.length, 1, 'em-dash heading task count');
702
+ assert.deepStrictEqual(p.tasks[0].id, 'T01', 'em-dash heading T01 id');
703
+ assert.deepStrictEqual(p.tasks[0].title, 'Build the widget', 'em-dash heading T01 title');
704
+ });
732
705
 
733
- console.log('\n=== parsePlan: mixed checkbox and heading-style tasks ===');
734
- {
706
+ test('parsePlan: mixed checkbox and heading-style tasks', () => {
735
707
  const content = `# S14: Mixed Format
736
708
 
737
709
  **Goal:** Test mixed formats.
@@ -751,23 +723,21 @@ A heading-style task.
751
723
  `;
752
724
 
753
725
  const p = parsePlan(content);
754
- assertEq(p.tasks.length, 3, 'mixed format task count');
755
- assertEq(p.tasks[0].id, 'T01', 'mixed T01 id');
756
- assertEq(p.tasks[0].done, false, 'mixed T01 not done');
757
- assertEq(p.tasks[1].id, 'T02', 'mixed T02 id');
758
- assertEq(p.tasks[1].title, 'Heading task', 'mixed T02 title');
759
- assertEq(p.tasks[1].estimate, '15m', 'mixed T02 estimate');
760
- assertEq(p.tasks[1].done, false, 'mixed T02 not done (heading style)');
761
- assertEq(p.tasks[2].id, 'T03', 'mixed T03 id');
762
- assertEq(p.tasks[2].done, true, 'mixed T03 done');
763
- }
726
+ assert.deepStrictEqual(p.tasks.length, 3, 'mixed format task count');
727
+ assert.deepStrictEqual(p.tasks[0].id, 'T01', 'mixed T01 id');
728
+ assert.deepStrictEqual(p.tasks[0].done, false, 'mixed T01 not done');
729
+ assert.deepStrictEqual(p.tasks[1].id, 'T02', 'mixed T02 id');
730
+ assert.deepStrictEqual(p.tasks[1].title, 'Heading task', 'mixed T02 title');
731
+ assert.deepStrictEqual(p.tasks[1].estimate, '15m', 'mixed T02 estimate');
732
+ assert.deepStrictEqual(p.tasks[1].done, false, 'mixed T02 not done (heading style)');
733
+ assert.deepStrictEqual(p.tasks[2].id, 'T03', 'mixed T03 id');
734
+ assert.deepStrictEqual(p.tasks[2].done, true, 'mixed T03 done');
735
+ });
764
736
 
765
737
  // ═══════════════════════════════════════════════════════════════════════════
766
738
  // parseSummary tests
767
739
  // ═══════════════════════════════════════════════════════════════════════════
768
-
769
- console.log('\n=== parseSummary: full summary with all frontmatter fields ===');
770
- {
740
+ test('parseSummary: full summary with all frontmatter fields', () => {
771
741
  const content = `---
772
742
  id: T01
773
743
  parent: S01
@@ -822,52 +792,51 @@ None.
822
792
  const s = parseSummary(content);
823
793
 
824
794
  // Frontmatter fields
825
- assertEq(s.frontmatter.id, 'T01', 'summary id');
826
- assertEq(s.frontmatter.parent, 'S01', 'summary parent');
827
- assertEq(s.frontmatter.milestone, 'M001', 'summary milestone');
828
- assertEq(s.frontmatter.provides.length, 2, 'provides count');
829
- assertEq(s.frontmatter.provides[0], 'parseRoadmap test coverage', 'first provides');
830
- assertEq(s.frontmatter.provides[1], 'parsePlan test coverage', 'second provides');
795
+ assert.deepStrictEqual(s.frontmatter.id, 'T01', 'summary id');
796
+ assert.deepStrictEqual(s.frontmatter.parent, 'S01', 'summary parent');
797
+ assert.deepStrictEqual(s.frontmatter.milestone, 'M001', 'summary milestone');
798
+ assert.deepStrictEqual(s.frontmatter.provides.length, 2, 'provides count');
799
+ assert.deepStrictEqual(s.frontmatter.provides[0], 'parseRoadmap test coverage', 'first provides');
800
+ assert.deepStrictEqual(s.frontmatter.provides[1], 'parsePlan test coverage', 'second provides');
831
801
 
832
802
  // requires (nested objects)
833
- assertEq(s.frontmatter.requires.length, 2, 'requires count');
834
- assertEq(s.frontmatter.requires[0].slice, 'S00', 'first requires slice');
835
- assertEq(s.frontmatter.requires[0].provides, 'type definitions', 'first requires provides');
836
- assertEq(s.frontmatter.requires[1].slice, 'S02', 'second requires slice');
837
- assertEq(s.frontmatter.requires[1].provides, 'state derivation', 'second requires provides');
838
-
839
- assertEq(s.frontmatter.affects.length, 1, 'affects count');
840
- assertEq(s.frontmatter.affects[0], 'auto-mode dispatch', 'affects value');
841
- assertEq(s.frontmatter.key_files.length, 2, 'key_files count');
842
- assertEq(s.frontmatter.key_decisions.length, 1, 'key_decisions count');
843
- assertEq(s.frontmatter.patterns_established.length, 1, 'patterns_established count');
844
- assertEq(s.frontmatter.drill_down_paths.length, 1, 'drill_down_paths count');
803
+ assert.deepStrictEqual(s.frontmatter.requires.length, 2, 'requires count');
804
+ assert.deepStrictEqual(s.frontmatter.requires[0].slice, 'S00', 'first requires slice');
805
+ assert.deepStrictEqual(s.frontmatter.requires[0].provides, 'type definitions', 'first requires provides');
806
+ assert.deepStrictEqual(s.frontmatter.requires[1].slice, 'S02', 'second requires slice');
807
+ assert.deepStrictEqual(s.frontmatter.requires[1].provides, 'state derivation', 'second requires provides');
808
+
809
+ assert.deepStrictEqual(s.frontmatter.affects.length, 1, 'affects count');
810
+ assert.deepStrictEqual(s.frontmatter.affects[0], 'auto-mode dispatch', 'affects value');
811
+ assert.deepStrictEqual(s.frontmatter.key_files.length, 2, 'key_files count');
812
+ assert.deepStrictEqual(s.frontmatter.key_decisions.length, 1, 'key_decisions count');
813
+ assert.deepStrictEqual(s.frontmatter.patterns_established.length, 1, 'patterns_established count');
814
+ assert.deepStrictEqual(s.frontmatter.drill_down_paths.length, 1, 'drill_down_paths count');
845
815
 
846
816
  // observability_surfaces extraction
847
- assertEq(s.frontmatter.observability_surfaces.length, 2, 'observability_surfaces count');
848
- assertEq(s.frontmatter.observability_surfaces[0], 'test pass/fail output from node --test', 'first observability surface');
849
- assertEq(s.frontmatter.observability_surfaces[1], 'exit code 1 on failure', 'second observability surface');
817
+ assert.deepStrictEqual(s.frontmatter.observability_surfaces.length, 2, 'observability_surfaces count');
818
+ assert.deepStrictEqual(s.frontmatter.observability_surfaces[0], 'test pass/fail output from node --test', 'first observability surface');
819
+ assert.deepStrictEqual(s.frontmatter.observability_surfaces[1], 'exit code 1 on failure', 'second observability surface');
850
820
 
851
- assertEq(s.frontmatter.duration, '23min', 'duration');
852
- assertEq(s.frontmatter.verification_result, 'pass', 'verification_result');
853
- assertEq(s.frontmatter.completed_at, '2025-03-10T08:00:00Z', 'completed_at');
821
+ assert.deepStrictEqual(s.frontmatter.duration, '23min', 'duration');
822
+ assert.deepStrictEqual(s.frontmatter.verification_result, 'pass', 'verification_result');
823
+ assert.deepStrictEqual(s.frontmatter.completed_at, '2025-03-10T08:00:00Z', 'completed_at');
854
824
 
855
825
  // Body fields
856
- assertEq(s.title, 'T01: Test parseRoadmap and parsePlan', 'summary title');
857
- assertEq(s.oneLiner, 'Created parsers.test.ts with 98 assertions across 16 test groups.', 'one-liner');
858
- assertTrue(s.whatHappened.includes('comprehensive tests'), 'whatHappened content');
859
- assertEq(s.deviations, 'None.', 'deviations');
826
+ assert.deepStrictEqual(s.title, 'T01: Test parseRoadmap and parsePlan', 'summary title');
827
+ assert.deepStrictEqual(s.oneLiner, 'Created parsers.test.ts with 98 assertions across 16 test groups.', 'one-liner');
828
+ assert.ok(s.whatHappened.includes('comprehensive tests'), 'whatHappened content');
829
+ assert.deepStrictEqual(s.deviations, 'None.', 'deviations');
860
830
 
861
831
  // Files modified
862
- assertEq(s.filesModified.length, 3, 'filesModified count');
863
- assertEq(s.filesModified[0].path, 'tests/parsers.test.ts', 'first file path');
864
- assertTrue(s.filesModified[0].description.includes('98 assertions'), 'first file description');
865
- assertEq(s.filesModified[1].path, 'types.ts', 'second file path');
866
- assertEq(s.filesModified[2].path, 'files.ts', 'third file path');
867
- }
868
-
869
- console.log('\n=== parseSummary: one-liner extraction (bold-wrapped line after H1) ===');
870
- {
832
+ assert.deepStrictEqual(s.filesModified.length, 3, 'filesModified count');
833
+ assert.deepStrictEqual(s.filesModified[0].path, 'tests/parsers.test.ts', 'first file path');
834
+ assert.ok(s.filesModified[0].description.includes('98 assertions'), 'first file description');
835
+ assert.deepStrictEqual(s.filesModified[1].path, 'types.ts', 'second file path');
836
+ assert.deepStrictEqual(s.filesModified[2].path, 'files.ts', 'third file path');
837
+ });
838
+
839
+ test('parseSummary: one-liner extraction (bold-wrapped line after H1)', () => {
871
840
  const content = `# S01: Parser Test Suite
872
841
 
873
842
  **All 5 parsers have test coverage with edge cases.**
@@ -878,12 +847,11 @@ Things happened.
878
847
  `;
879
848
 
880
849
  const s = parseSummary(content);
881
- assertEq(s.title, 'S01: Parser Test Suite', 'title');
882
- assertEq(s.oneLiner, 'All 5 parsers have test coverage with edge cases.', 'bold one-liner');
883
- }
850
+ assert.deepStrictEqual(s.title, 'S01: Parser Test Suite', 'title');
851
+ assert.deepStrictEqual(s.oneLiner, 'All 5 parsers have test coverage with edge cases.', 'bold one-liner');
852
+ });
884
853
 
885
- console.log('\n=== parseSummary: non-bold paragraph after H1 (empty one-liner) ===');
886
- {
854
+ test('parseSummary: non-bold paragraph after H1 (empty one-liner)', () => {
887
855
  const content = `# T02: Some Task
888
856
 
889
857
  This is just a regular paragraph, not bold.
@@ -894,12 +862,11 @@ Did stuff.
894
862
  `;
895
863
 
896
864
  const s = parseSummary(content);
897
- assertEq(s.title, 'T02: Some Task', 'title');
898
- assertEq(s.oneLiner, '', 'non-bold line results in empty one-liner');
899
- }
865
+ assert.deepStrictEqual(s.title, 'T02: Some Task', 'title');
866
+ assert.deepStrictEqual(s.oneLiner, '', 'non-bold line results in empty one-liner');
867
+ });
900
868
 
901
- console.log('\n=== parseSummary: files-modified parsing (backtick path — description format) ===');
902
- {
869
+ test('parseSummary: files-modified parsing (backtick path — description format)', () => {
903
870
  const content = `# T03: File Changes
904
871
 
905
872
  **One-liner.**
@@ -912,15 +879,14 @@ console.log('\n=== parseSummary: files-modified parsing (backtick path — descr
912
879
  `;
913
880
 
914
881
  const s = parseSummary(content);
915
- assertEq(s.filesModified.length, 3, 'three files');
916
- assertEq(s.filesModified[0].path, 'src/index.ts', 'first path');
917
- assertEq(s.filesModified[0].description, 'main entry point', 'first description');
918
- assertEq(s.filesModified[1].path, 'src/utils.ts', 'second path');
919
- assertEq(s.filesModified[2].path, 'README.md', 'third path');
920
- }
921
-
922
- console.log('\n=== parseSummary: missing frontmatter (safe defaults) ===');
923
- {
882
+ assert.deepStrictEqual(s.filesModified.length, 3, 'three files');
883
+ assert.deepStrictEqual(s.filesModified[0].path, 'src/index.ts', 'first path');
884
+ assert.deepStrictEqual(s.filesModified[0].description, 'main entry point', 'first description');
885
+ assert.deepStrictEqual(s.filesModified[1].path, 'src/utils.ts', 'second path');
886
+ assert.deepStrictEqual(s.filesModified[2].path, 'README.md', 'third path');
887
+ });
888
+
889
+ test('parseSummary: missing frontmatter (safe defaults)', () => {
924
890
  const content = `# T04: No Frontmatter
925
891
 
926
892
  **Did something.**
@@ -931,26 +897,25 @@ No frontmatter at all.
931
897
  `;
932
898
 
933
899
  const s = parseSummary(content);
934
- assertEq(s.frontmatter.id, '', 'default id empty');
935
- assertEq(s.frontmatter.parent, '', 'default parent empty');
936
- assertEq(s.frontmatter.milestone, '', 'default milestone empty');
937
- assertEq(s.frontmatter.provides.length, 0, 'default provides empty');
938
- assertEq(s.frontmatter.requires.length, 0, 'default requires empty');
939
- assertEq(s.frontmatter.affects.length, 0, 'default affects empty');
940
- assertEq(s.frontmatter.key_files.length, 0, 'default key_files empty');
941
- assertEq(s.frontmatter.key_decisions.length, 0, 'default key_decisions empty');
942
- assertEq(s.frontmatter.patterns_established.length, 0, 'default patterns_established empty');
943
- assertEq(s.frontmatter.drill_down_paths.length, 0, 'default drill_down_paths empty');
944
- assertEq(s.frontmatter.observability_surfaces.length, 0, 'default observability_surfaces empty');
945
- assertEq(s.frontmatter.duration, '', 'default duration empty');
946
- assertEq(s.frontmatter.verification_result, 'untested', 'default verification_result');
947
- assertEq(s.frontmatter.completed_at, '', 'default completed_at empty');
948
- assertEq(s.title, 'T04: No Frontmatter', 'title still parsed');
949
- assertEq(s.oneLiner, 'Did something.', 'one-liner still parsed');
950
- }
951
-
952
- console.log('\n=== parseSummary: empty body ===');
953
- {
900
+ assert.deepStrictEqual(s.frontmatter.id, '', 'default id empty');
901
+ assert.deepStrictEqual(s.frontmatter.parent, '', 'default parent empty');
902
+ assert.deepStrictEqual(s.frontmatter.milestone, '', 'default milestone empty');
903
+ assert.deepStrictEqual(s.frontmatter.provides.length, 0, 'default provides empty');
904
+ assert.deepStrictEqual(s.frontmatter.requires.length, 0, 'default requires empty');
905
+ assert.deepStrictEqual(s.frontmatter.affects.length, 0, 'default affects empty');
906
+ assert.deepStrictEqual(s.frontmatter.key_files.length, 0, 'default key_files empty');
907
+ assert.deepStrictEqual(s.frontmatter.key_decisions.length, 0, 'default key_decisions empty');
908
+ assert.deepStrictEqual(s.frontmatter.patterns_established.length, 0, 'default patterns_established empty');
909
+ assert.deepStrictEqual(s.frontmatter.drill_down_paths.length, 0, 'default drill_down_paths empty');
910
+ assert.deepStrictEqual(s.frontmatter.observability_surfaces.length, 0, 'default observability_surfaces empty');
911
+ assert.deepStrictEqual(s.frontmatter.duration, '', 'default duration empty');
912
+ assert.deepStrictEqual(s.frontmatter.verification_result, 'untested', 'default verification_result');
913
+ assert.deepStrictEqual(s.frontmatter.completed_at, '', 'default completed_at empty');
914
+ assert.deepStrictEqual(s.title, 'T04: No Frontmatter', 'title still parsed');
915
+ assert.deepStrictEqual(s.oneLiner, 'Did something.', 'one-liner still parsed');
916
+ });
917
+
918
+ test('parseSummary: empty body', () => {
954
919
  const content = `---
955
920
  id: T05
956
921
  parent: S01
@@ -959,16 +924,15 @@ milestone: M001
959
924
  `;
960
925
 
961
926
  const s = parseSummary(content);
962
- assertEq(s.frontmatter.id, 'T05', 'id from frontmatter');
963
- assertEq(s.title, '', 'empty title');
964
- assertEq(s.oneLiner, '', 'empty one-liner');
965
- assertEq(s.whatHappened, '', 'empty whatHappened');
966
- assertEq(s.deviations, '', 'empty deviations');
967
- assertEq(s.filesModified.length, 0, 'no files modified');
968
- }
969
-
970
- console.log('\n=== parseSummary: summary with requires array (nested objects) ===');
971
- {
927
+ assert.deepStrictEqual(s.frontmatter.id, 'T05', 'id from frontmatter');
928
+ assert.deepStrictEqual(s.title, '', 'empty title');
929
+ assert.deepStrictEqual(s.oneLiner, '', 'empty one-liner');
930
+ assert.deepStrictEqual(s.whatHappened, '', 'empty whatHappened');
931
+ assert.deepStrictEqual(s.deviations, '', 'empty deviations');
932
+ assert.deepStrictEqual(s.filesModified.length, 0, 'no files modified');
933
+ });
934
+
935
+ test('parseSummary: summary with requires array (nested objects)', () => {
972
936
  const content = `---
973
937
  id: T06
974
938
  parent: S02
@@ -1003,20 +967,18 @@ Tested.
1003
967
  `;
1004
968
 
1005
969
  const s = parseSummary(content);
1006
- assertEq(s.frontmatter.requires.length, 3, 'three requires entries');
1007
- assertEq(s.frontmatter.requires[0].slice, 'S01', 'first requires slice');
1008
- assertEq(s.frontmatter.requires[0].provides, 'parser functions', 'first requires provides');
1009
- assertEq(s.frontmatter.requires[1].slice, 'S00', 'second requires slice');
1010
- assertEq(s.frontmatter.requires[2].slice, 'S03', 'third requires slice');
1011
- assertEq(s.frontmatter.requires[2].provides, 'state engine', 'third requires provides');
1012
- }
970
+ assert.deepStrictEqual(s.frontmatter.requires.length, 3, 'three requires entries');
971
+ assert.deepStrictEqual(s.frontmatter.requires[0].slice, 'S01', 'first requires slice');
972
+ assert.deepStrictEqual(s.frontmatter.requires[0].provides, 'parser functions', 'first requires provides');
973
+ assert.deepStrictEqual(s.frontmatter.requires[1].slice, 'S00', 'second requires slice');
974
+ assert.deepStrictEqual(s.frontmatter.requires[2].slice, 'S03', 'third requires slice');
975
+ assert.deepStrictEqual(s.frontmatter.requires[2].provides, 'state engine', 'third requires provides');
976
+ });
1013
977
 
1014
978
  // ═══════════════════════════════════════════════════════════════════════════
1015
979
  // parseContinue tests
1016
980
  // ═══════════════════════════════════════════════════════════════════════════
1017
-
1018
- console.log('\n=== parseContinue: full continue file with all frontmatter fields ===');
1019
- {
981
+ test('parseContinue: full continue file with all frontmatter fields', () => {
1020
982
  const content = `---
1021
983
  milestone: M001
1022
984
  slice: S01
@@ -1051,24 +1013,23 @@ Run the full test suite with node --test.
1051
1013
  const c = parseContinue(content);
1052
1014
 
1053
1015
  // Frontmatter
1054
- assertEq(c.frontmatter.milestone, 'M001', 'continue milestone');
1055
- assertEq(c.frontmatter.slice, 'S01', 'continue slice');
1056
- assertEq(c.frontmatter.task, 'T02', 'continue task');
1057
- assertEq(c.frontmatter.step, 3, 'continue step');
1058
- assertEq(c.frontmatter.totalSteps, 5, 'continue totalSteps');
1059
- assertEq(c.frontmatter.status, 'in_progress', 'continue status');
1060
- assertEq(c.frontmatter.savedAt, '2025-03-10T08:30:00Z', 'continue savedAt');
1016
+ assert.deepStrictEqual(c.frontmatter.milestone, 'M001', 'continue milestone');
1017
+ assert.deepStrictEqual(c.frontmatter.slice, 'S01', 'continue slice');
1018
+ assert.deepStrictEqual(c.frontmatter.task, 'T02', 'continue task');
1019
+ assert.deepStrictEqual(c.frontmatter.step, 3, 'continue step');
1020
+ assert.deepStrictEqual(c.frontmatter.totalSteps, 5, 'continue totalSteps');
1021
+ assert.deepStrictEqual(c.frontmatter.status, 'in_progress', 'continue status');
1022
+ assert.deepStrictEqual(c.frontmatter.savedAt, '2025-03-10T08:30:00Z', 'continue savedAt');
1061
1023
 
1062
1024
  // Body sections
1063
- assertTrue(c.completedWork.includes('Steps 1-3 are done'), 'completedWork content');
1064
- assertTrue(c.remainingWork.includes('Steps 4-5'), 'remainingWork content');
1065
- assertTrue(c.decisions.includes('manual assert pattern'), 'decisions content');
1066
- assertTrue(c.context.includes('gsd-s01 worktree'), 'context content');
1067
- assertTrue(c.nextAction.includes('node --test'), 'nextAction content');
1068
- }
1069
-
1070
- console.log('\n=== parseContinue: string step/totalSteps parsed as integers ===');
1071
- {
1025
+ assert.ok(c.completedWork.includes('Steps 1-3 are done'), 'completedWork content');
1026
+ assert.ok(c.remainingWork.includes('Steps 4-5'), 'remainingWork content');
1027
+ assert.ok(c.decisions.includes('manual assert pattern'), 'decisions content');
1028
+ assert.ok(c.context.includes('gsd-s01 worktree'), 'context content');
1029
+ assert.ok(c.nextAction.includes('node --test'), 'nextAction content');
1030
+ });
1031
+
1032
+ test('parseContinue: string step/totalSteps parsed as integers', () => {
1072
1033
  const content = `---
1073
1034
  milestone: M002
1074
1035
  slice: S03
@@ -1101,14 +1062,13 @@ Continue.
1101
1062
  `;
1102
1063
 
1103
1064
  const c = parseContinue(content);
1104
- assertEq(c.frontmatter.step, 7, 'step parsed as integer 7');
1105
- assertEq(c.frontmatter.totalSteps, 12, 'totalSteps parsed as integer 12');
1106
- assertEq(typeof c.frontmatter.step, 'number', 'step is number type');
1107
- assertEq(typeof c.frontmatter.totalSteps, 'number', 'totalSteps is number type');
1108
- }
1109
-
1110
- console.log('\n=== parseContinue: NaN step values (non-numeric strings) ===');
1111
- {
1065
+ assert.deepStrictEqual(c.frontmatter.step, 7, 'step parsed as integer 7');
1066
+ assert.deepStrictEqual(c.frontmatter.totalSteps, 12, 'totalSteps parsed as integer 12');
1067
+ assert.deepStrictEqual(typeof c.frontmatter.step, 'number', 'step is number type');
1068
+ assert.deepStrictEqual(typeof c.frontmatter.totalSteps, 'number', 'totalSteps is number type');
1069
+ });
1070
+
1071
+ test('parseContinue: NaN step values (non-numeric strings)', () => {
1112
1072
  const content = `---
1113
1073
  milestone: M001
1114
1074
  slice: S01
@@ -1150,12 +1110,11 @@ Do things.
1150
1110
  const totalIsNaN = Number.isNaN(c.frontmatter.totalSteps);
1151
1111
  // The parser does parseInt which returns NaN for non-numeric strings
1152
1112
  // There's no || 0 fallback on the parseInt path, so NaN is expected
1153
- assertTrue(stepIsNaN, 'NaN step when non-numeric string');
1154
- assertTrue(totalIsNaN, 'NaN totalSteps when non-numeric string');
1155
- }
1113
+ assert.ok(stepIsNaN, 'NaN step when non-numeric string');
1114
+ assert.ok(totalIsNaN, 'NaN totalSteps when non-numeric string');
1115
+ });
1156
1116
 
1157
- console.log('\n=== parseContinue: all three status variants ===');
1158
- {
1117
+ test('parseContinue: all three status variants', () => {
1159
1118
  for (const status of ['in_progress', 'interrupted', 'compacted'] as const) {
1160
1119
  const content = `---
1161
1120
  milestone: M001
@@ -1173,12 +1132,11 @@ Work.
1173
1132
  `;
1174
1133
 
1175
1134
  const c = parseContinue(content);
1176
- assertEq(c.frontmatter.status, status, `status variant: ${status}`);
1135
+ assert.deepStrictEqual(c.frontmatter.status, status, `status variant: ${status}`);
1177
1136
  }
1178
- }
1137
+ });
1179
1138
 
1180
- console.log('\n=== parseContinue: missing frontmatter ===');
1181
- {
1139
+ test('parseContinue: missing frontmatter', () => {
1182
1140
  const content = `## Completed Work
1183
1141
 
1184
1142
  Some work done.
@@ -1201,24 +1159,23 @@ Next thing.
1201
1159
  `;
1202
1160
 
1203
1161
  const c = parseContinue(content);
1204
- assertEq(c.frontmatter.milestone, '', 'default milestone empty');
1205
- assertEq(c.frontmatter.slice, '', 'default slice empty');
1206
- assertEq(c.frontmatter.task, '', 'default task empty');
1207
- assertEq(c.frontmatter.step, 0, 'default step 0');
1208
- assertEq(c.frontmatter.totalSteps, 0, 'default totalSteps 0');
1209
- assertEq(c.frontmatter.status, 'in_progress', 'default status in_progress');
1210
- assertEq(c.frontmatter.savedAt, '', 'default savedAt empty');
1162
+ assert.deepStrictEqual(c.frontmatter.milestone, '', 'default milestone empty');
1163
+ assert.deepStrictEqual(c.frontmatter.slice, '', 'default slice empty');
1164
+ assert.deepStrictEqual(c.frontmatter.task, '', 'default task empty');
1165
+ assert.deepStrictEqual(c.frontmatter.step, 0, 'default step 0');
1166
+ assert.deepStrictEqual(c.frontmatter.totalSteps, 0, 'default totalSteps 0');
1167
+ assert.deepStrictEqual(c.frontmatter.status, 'in_progress', 'default status in_progress');
1168
+ assert.deepStrictEqual(c.frontmatter.savedAt, '', 'default savedAt empty');
1211
1169
 
1212
1170
  // Body sections still parse
1213
- assertTrue(c.completedWork.includes('Some work done'), 'completedWork without frontmatter');
1214
- assertTrue(c.remainingWork.includes('More to do'), 'remainingWork without frontmatter');
1215
- assertTrue(c.decisions.includes('A decision'), 'decisions without frontmatter');
1216
- assertTrue(c.context.includes('Some context'), 'context without frontmatter');
1217
- assertTrue(c.nextAction.includes('Next thing'), 'nextAction without frontmatter');
1218
- }
1219
-
1220
- console.log('\n=== parseContinue: body section extraction ===');
1221
- {
1171
+ assert.ok(c.completedWork.includes('Some work done'), 'completedWork without frontmatter');
1172
+ assert.ok(c.remainingWork.includes('More to do'), 'remainingWork without frontmatter');
1173
+ assert.ok(c.decisions.includes('A decision'), 'decisions without frontmatter');
1174
+ assert.ok(c.context.includes('Some context'), 'context without frontmatter');
1175
+ assert.ok(c.nextAction.includes('Next thing'), 'nextAction without frontmatter');
1176
+ });
1177
+
1178
+ test('parseContinue: body section extraction', () => {
1222
1179
  const content = `---
1223
1180
  milestone: M001
1224
1181
  slice: S01
@@ -1252,16 +1209,15 @@ Pick up at step 3: run the integration tests.
1252
1209
  `;
1253
1210
 
1254
1211
  const c = parseContinue(content);
1255
- assertTrue(c.completedWork.includes('First paragraph'), 'completedWork first paragraph');
1256
- assertTrue(c.completedWork.includes('Second paragraph'), 'completedWork second paragraph');
1257
- assertTrue(c.remainingWork.includes('step 3 and step 4'), 'remainingWork detail');
1258
- assertTrue(c.decisions.includes('approach A over approach B'), 'decisions detail');
1259
- assertTrue(c.context.includes('Node 22 required'), 'context detail');
1260
- assertTrue(c.nextAction.includes('step 3: run the integration tests'), 'nextAction detail');
1261
- }
1262
-
1263
- console.log('\n=== parseContinue: total_steps vs totalSteps key support ===');
1264
- {
1212
+ assert.ok(c.completedWork.includes('First paragraph'), 'completedWork first paragraph');
1213
+ assert.ok(c.completedWork.includes('Second paragraph'), 'completedWork second paragraph');
1214
+ assert.ok(c.remainingWork.includes('step 3 and step 4'), 'remainingWork detail');
1215
+ assert.ok(c.decisions.includes('approach A over approach B'), 'decisions detail');
1216
+ assert.ok(c.context.includes('Node 22 required'), 'context detail');
1217
+ assert.ok(c.nextAction.includes('step 3: run the integration tests'), 'nextAction detail');
1218
+ });
1219
+
1220
+ test('parseContinue: total_steps vs totalSteps key support', () => {
1265
1221
  // Test total_steps (snake_case) — the primary format
1266
1222
  const content1 = `---
1267
1223
  milestone: M001
@@ -1279,7 +1235,7 @@ Work.
1279
1235
  `;
1280
1236
 
1281
1237
  const c1 = parseContinue(content1);
1282
- assertEq(c1.frontmatter.totalSteps, 8, 'total_steps snake_case works');
1238
+ assert.deepStrictEqual(c1.frontmatter.totalSteps, 8, 'total_steps snake_case works');
1283
1239
 
1284
1240
  // Test totalSteps (camelCase) — the fallback
1285
1241
  const content2 = `---
@@ -1298,15 +1254,13 @@ Work.
1298
1254
  `;
1299
1255
 
1300
1256
  const c2 = parseContinue(content2);
1301
- assertEq(c2.frontmatter.totalSteps, 6, 'totalSteps camelCase works');
1302
- }
1257
+ assert.deepStrictEqual(c2.frontmatter.totalSteps, 6, 'totalSteps camelCase works');
1258
+ });
1303
1259
 
1304
1260
  // ═══════════════════════════════════════════════════════════════════════════
1305
1261
  // parseRequirementCounts tests
1306
1262
  // ═══════════════════════════════════════════════════════════════════════════
1307
-
1308
- console.log('\n=== parseRequirementCounts: full requirements file ===');
1309
- {
1263
+ test('parseRequirementCounts: full requirements file', () => {
1310
1264
  const content = `# Requirements
1311
1265
 
1312
1266
  ## Active
@@ -1343,27 +1297,25 @@ console.log('\n=== parseRequirementCounts: full requirements file ===');
1343
1297
  `;
1344
1298
 
1345
1299
  const counts = parseRequirementCounts(content);
1346
- assertEq(counts.active, 3, 'active count');
1347
- assertEq(counts.validated, 2, 'validated count');
1348
- assertEq(counts.deferred, 1, 'deferred count');
1349
- assertEq(counts.outOfScope, 2, 'outOfScope count');
1350
- assertEq(counts.blocked, 1, 'blocked count');
1351
- assertEq(counts.total, 8, 'total is sum of active+validated+deferred+outOfScope');
1352
- }
1353
-
1354
- console.log('\n=== parseRequirementCounts: null input returns all zeros ===');
1355
- {
1300
+ assert.deepStrictEqual(counts.active, 3, 'active count');
1301
+ assert.deepStrictEqual(counts.validated, 2, 'validated count');
1302
+ assert.deepStrictEqual(counts.deferred, 1, 'deferred count');
1303
+ assert.deepStrictEqual(counts.outOfScope, 2, 'outOfScope count');
1304
+ assert.deepStrictEqual(counts.blocked, 1, 'blocked count');
1305
+ assert.deepStrictEqual(counts.total, 8, 'total is sum of active+validated+deferred+outOfScope');
1306
+ });
1307
+
1308
+ test('parseRequirementCounts: null input returns all zeros', () => {
1356
1309
  const counts = parseRequirementCounts(null);
1357
- assertEq(counts.active, 0, 'null active');
1358
- assertEq(counts.validated, 0, 'null validated');
1359
- assertEq(counts.deferred, 0, 'null deferred');
1360
- assertEq(counts.outOfScope, 0, 'null outOfScope');
1361
- assertEq(counts.blocked, 0, 'null blocked');
1362
- assertEq(counts.total, 0, 'null total');
1363
- }
1364
-
1365
- console.log('\n=== parseRequirementCounts: empty sections return zero counts ===');
1366
- {
1310
+ assert.deepStrictEqual(counts.active, 0, 'null active');
1311
+ assert.deepStrictEqual(counts.validated, 0, 'null validated');
1312
+ assert.deepStrictEqual(counts.deferred, 0, 'null deferred');
1313
+ assert.deepStrictEqual(counts.outOfScope, 0, 'null outOfScope');
1314
+ assert.deepStrictEqual(counts.blocked, 0, 'null blocked');
1315
+ assert.deepStrictEqual(counts.total, 0, 'null total');
1316
+ });
1317
+
1318
+ test('parseRequirementCounts: empty sections return zero counts', () => {
1367
1319
  const content = `# Requirements
1368
1320
 
1369
1321
  ## Active
@@ -1376,16 +1328,15 @@ console.log('\n=== parseRequirementCounts: empty sections return zero counts ===
1376
1328
  `;
1377
1329
 
1378
1330
  const counts = parseRequirementCounts(content);
1379
- assertEq(counts.active, 0, 'empty active');
1380
- assertEq(counts.validated, 0, 'empty validated');
1381
- assertEq(counts.deferred, 0, 'empty deferred');
1382
- assertEq(counts.outOfScope, 0, 'empty outOfScope');
1383
- assertEq(counts.blocked, 0, 'empty blocked');
1384
- assertEq(counts.total, 0, 'empty total');
1385
- }
1386
-
1387
- console.log('\n=== parseRequirementCounts: blocked status counting ===');
1388
- {
1331
+ assert.deepStrictEqual(counts.active, 0, 'empty active');
1332
+ assert.deepStrictEqual(counts.validated, 0, 'empty validated');
1333
+ assert.deepStrictEqual(counts.deferred, 0, 'empty deferred');
1334
+ assert.deepStrictEqual(counts.outOfScope, 0, 'empty outOfScope');
1335
+ assert.deepStrictEqual(counts.blocked, 0, 'empty blocked');
1336
+ assert.deepStrictEqual(counts.total, 0, 'empty total');
1337
+ });
1338
+
1339
+ test('parseRequirementCounts: blocked status counting', () => {
1389
1340
  const content = `# Requirements
1390
1341
 
1391
1342
  ## Active
@@ -1410,13 +1361,12 @@ console.log('\n=== parseRequirementCounts: blocked status counting ===');
1410
1361
  `;
1411
1362
 
1412
1363
  const counts = parseRequirementCounts(content);
1413
- assertEq(counts.active, 3, 'active includes blocked items in Active section');
1414
- assertEq(counts.blocked, 3, 'blocked counts all blocked statuses across sections');
1415
- assertEq(counts.deferred, 1, 'deferred section count');
1416
- }
1364
+ assert.deepStrictEqual(counts.active, 3, 'active includes blocked items in Active section');
1365
+ assert.deepStrictEqual(counts.blocked, 3, 'blocked counts all blocked statuses across sections');
1366
+ assert.deepStrictEqual(counts.deferred, 1, 'deferred section count');
1367
+ });
1417
1368
 
1418
- console.log('\n=== parseRequirementCounts: total is sum of all section counts ===');
1419
- {
1369
+ test('parseRequirementCounts: total is sum of all section counts', () => {
1420
1370
  const content = `# Requirements
1421
1371
 
1422
1372
  ## Active
@@ -1450,20 +1400,18 @@ console.log('\n=== parseRequirementCounts: total is sum of all section counts ==
1450
1400
  `;
1451
1401
 
1452
1402
  const counts = parseRequirementCounts(content);
1453
- assertEq(counts.active, 1, 'one active');
1454
- assertEq(counts.validated, 2, 'two validated');
1455
- assertEq(counts.deferred, 3, 'three deferred');
1456
- assertEq(counts.outOfScope, 1, 'one outOfScope');
1457
- assertEq(counts.total, 7, 'total = 1 + 2 + 3 + 1');
1458
- assertEq(counts.total, counts.active + counts.validated + counts.deferred + counts.outOfScope, 'total is exact sum');
1459
- }
1403
+ assert.deepStrictEqual(counts.active, 1, 'one active');
1404
+ assert.deepStrictEqual(counts.validated, 2, 'two validated');
1405
+ assert.deepStrictEqual(counts.deferred, 3, 'three deferred');
1406
+ assert.deepStrictEqual(counts.outOfScope, 1, 'one outOfScope');
1407
+ assert.deepStrictEqual(counts.total, 7, 'total = 1 + 2 + 3 + 1');
1408
+ assert.deepStrictEqual(counts.total, counts.active + counts.validated + counts.deferred + counts.outOfScope, 'total is exact sum');
1409
+ });
1460
1410
 
1461
1411
  // ═══════════════════════════════════════════════════════════════════════════
1462
1412
  // parseSecretsManifest / formatSecretsManifest tests
1463
1413
  // ═══════════════════════════════════════════════════════════════════════════
1464
-
1465
- console.log('\n=== parseSecretsManifest: full manifest with 3 keys ===');
1466
- {
1414
+ test('parseSecretsManifest: full manifest with 3 keys', () => {
1467
1415
  const content = `# Secrets Manifest
1468
1416
 
1469
1417
  **Milestone:** M003
@@ -1507,37 +1455,36 @@ console.log('\n=== parseSecretsManifest: full manifest with 3 keys ===');
1507
1455
 
1508
1456
  const m = parseSecretsManifest(content);
1509
1457
 
1510
- assertEq(m.milestone, 'M003', 'manifest milestone');
1511
- assertEq(m.generatedAt, '2025-06-15T10:00:00Z', 'manifest generatedAt');
1512
- assertEq(m.entries.length, 3, 'three entries');
1458
+ assert.deepStrictEqual(m.milestone, 'M003', 'manifest milestone');
1459
+ assert.deepStrictEqual(m.generatedAt, '2025-06-15T10:00:00Z', 'manifest generatedAt');
1460
+ assert.deepStrictEqual(m.entries.length, 3, 'three entries');
1513
1461
 
1514
1462
  // First entry
1515
- assertEq(m.entries[0].key, 'OPENAI_API_KEY', 'entry 0 key');
1516
- assertEq(m.entries[0].service, 'OpenAI', 'entry 0 service');
1517
- assertEq(m.entries[0].dashboardUrl, 'https://platform.openai.com/api-keys', 'entry 0 dashboardUrl');
1518
- assertEq(m.entries[0].formatHint, 'starts with sk-', 'entry 0 formatHint');
1519
- assertEq(m.entries[0].status, 'pending', 'entry 0 status');
1520
- assertEq(m.entries[0].destination, 'dotenv', 'entry 0 destination');
1521
- assertEq(m.entries[0].guidance.length, 3, 'entry 0 guidance count');
1522
- assertEq(m.entries[0].guidance[0], 'Go to https://platform.openai.com/api-keys', 'entry 0 guidance[0]');
1523
- assertEq(m.entries[0].guidance[2], 'Copy the key immediately — it won\'t be shown again', 'entry 0 guidance[2]');
1463
+ assert.deepStrictEqual(m.entries[0].key, 'OPENAI_API_KEY', 'entry 0 key');
1464
+ assert.deepStrictEqual(m.entries[0].service, 'OpenAI', 'entry 0 service');
1465
+ assert.deepStrictEqual(m.entries[0].dashboardUrl, 'https://platform.openai.com/api-keys', 'entry 0 dashboardUrl');
1466
+ assert.deepStrictEqual(m.entries[0].formatHint, 'starts with sk-', 'entry 0 formatHint');
1467
+ assert.deepStrictEqual(m.entries[0].status, 'pending', 'entry 0 status');
1468
+ assert.deepStrictEqual(m.entries[0].destination, 'dotenv', 'entry 0 destination');
1469
+ assert.deepStrictEqual(m.entries[0].guidance.length, 3, 'entry 0 guidance count');
1470
+ assert.deepStrictEqual(m.entries[0].guidance[0], 'Go to https://platform.openai.com/api-keys', 'entry 0 guidance[0]');
1471
+ assert.deepStrictEqual(m.entries[0].guidance[2], 'Copy the key immediately — it won\'t be shown again', 'entry 0 guidance[2]');
1524
1472
 
1525
1473
  // Second entry
1526
- assertEq(m.entries[1].key, 'STRIPE_SECRET_KEY', 'entry 1 key');
1527
- assertEq(m.entries[1].service, 'Stripe', 'entry 1 service');
1528
- assertEq(m.entries[1].status, 'collected', 'entry 1 status');
1529
- assertEq(m.entries[1].formatHint, 'starts with sk_test_ or sk_live_', 'entry 1 formatHint');
1530
- assertEq(m.entries[1].guidance.length, 3, 'entry 1 guidance count');
1474
+ assert.deepStrictEqual(m.entries[1].key, 'STRIPE_SECRET_KEY', 'entry 1 key');
1475
+ assert.deepStrictEqual(m.entries[1].service, 'Stripe', 'entry 1 service');
1476
+ assert.deepStrictEqual(m.entries[1].status, 'collected', 'entry 1 status');
1477
+ assert.deepStrictEqual(m.entries[1].formatHint, 'starts with sk_test_ or sk_live_', 'entry 1 formatHint');
1478
+ assert.deepStrictEqual(m.entries[1].guidance.length, 3, 'entry 1 guidance count');
1531
1479
 
1532
1480
  // Third entry
1533
- assertEq(m.entries[2].key, 'SUPABASE_URL', 'entry 2 key');
1534
- assertEq(m.entries[2].status, 'skipped', 'entry 2 status');
1535
- assertEq(m.entries[2].destination, 'vercel', 'entry 2 destination');
1536
- assertEq(m.entries[2].guidance.length, 2, 'entry 2 guidance count');
1537
- }
1538
-
1539
- console.log('\n=== parseSecretsManifest: single-key manifest ===');
1540
- {
1481
+ assert.deepStrictEqual(m.entries[2].key, 'SUPABASE_URL', 'entry 2 key');
1482
+ assert.deepStrictEqual(m.entries[2].status, 'skipped', 'entry 2 status');
1483
+ assert.deepStrictEqual(m.entries[2].destination, 'vercel', 'entry 2 destination');
1484
+ assert.deepStrictEqual(m.entries[2].guidance.length, 2, 'entry 2 guidance count');
1485
+ });
1486
+
1487
+ test('parseSecretsManifest: single-key manifest', () => {
1541
1488
  const content = `# Secrets Manifest
1542
1489
 
1543
1490
  **Milestone:** M001
@@ -1556,15 +1503,14 @@ console.log('\n=== parseSecretsManifest: single-key manifest ===');
1556
1503
  `;
1557
1504
 
1558
1505
  const m = parseSecretsManifest(content);
1559
- assertEq(m.milestone, 'M001', 'single-key milestone');
1560
- assertEq(m.entries.length, 1, 'single entry');
1561
- assertEq(m.entries[0].key, 'DATABASE_URL', 'single entry key');
1562
- assertEq(m.entries[0].service, 'PostgreSQL', 'single entry service');
1563
- assertEq(m.entries[0].guidance.length, 2, 'single entry guidance count');
1564
- }
1565
-
1566
- console.log('\n=== parseSecretsManifest: empty/no-secrets manifest ===');
1567
- {
1506
+ assert.deepStrictEqual(m.milestone, 'M001', 'single-key milestone');
1507
+ assert.deepStrictEqual(m.entries.length, 1, 'single entry');
1508
+ assert.deepStrictEqual(m.entries[0].key, 'DATABASE_URL', 'single entry key');
1509
+ assert.deepStrictEqual(m.entries[0].service, 'PostgreSQL', 'single entry service');
1510
+ assert.deepStrictEqual(m.entries[0].guidance.length, 2, 'single entry guidance count');
1511
+ });
1512
+
1513
+ test('parseSecretsManifest: empty/no-secrets manifest', () => {
1568
1514
  const content = `# Secrets Manifest
1569
1515
 
1570
1516
  **Milestone:** M002
@@ -1572,13 +1518,12 @@ console.log('\n=== parseSecretsManifest: empty/no-secrets manifest ===');
1572
1518
  `;
1573
1519
 
1574
1520
  const m = parseSecretsManifest(content);
1575
- assertEq(m.milestone, 'M002', 'empty manifest milestone');
1576
- assertEq(m.generatedAt, '2025-06-15T14:00:00Z', 'empty manifest generatedAt');
1577
- assertEq(m.entries.length, 0, 'no entries in empty manifest');
1578
- }
1521
+ assert.deepStrictEqual(m.milestone, 'M002', 'empty manifest milestone');
1522
+ assert.deepStrictEqual(m.generatedAt, '2025-06-15T14:00:00Z', 'empty manifest generatedAt');
1523
+ assert.deepStrictEqual(m.entries.length, 0, 'no entries in empty manifest');
1524
+ });
1579
1525
 
1580
- console.log('\n=== parseSecretsManifest: missing optional fields default correctly ===');
1581
- {
1526
+ test('parseSecretsManifest: missing optional fields default correctly', () => {
1582
1527
  const content = `# Secrets Manifest
1583
1528
 
1584
1529
  **Milestone:** M004
@@ -1592,18 +1537,17 @@ console.log('\n=== parseSecretsManifest: missing optional fields default correct
1592
1537
  `;
1593
1538
 
1594
1539
  const m = parseSecretsManifest(content);
1595
- assertEq(m.entries.length, 1, 'one entry with missing fields');
1596
- assertEq(m.entries[0].key, 'SOME_API_KEY', 'key parsed');
1597
- assertEq(m.entries[0].service, 'SomeService', 'service parsed');
1598
- assertEq(m.entries[0].dashboardUrl, '', 'missing dashboardUrl defaults to empty string');
1599
- assertEq(m.entries[0].formatHint, '', 'missing formatHint defaults to empty string');
1600
- assertEq(m.entries[0].status, 'pending', 'missing status defaults to pending');
1601
- assertEq(m.entries[0].destination, 'dotenv', 'missing destination defaults to dotenv');
1602
- assertEq(m.entries[0].guidance.length, 1, 'guidance still parsed');
1603
- }
1604
-
1605
- console.log('\n=== parseSecretsManifest: all three status values parse ===');
1606
- {
1540
+ assert.deepStrictEqual(m.entries.length, 1, 'one entry with missing fields');
1541
+ assert.deepStrictEqual(m.entries[0].key, 'SOME_API_KEY', 'key parsed');
1542
+ assert.deepStrictEqual(m.entries[0].service, 'SomeService', 'service parsed');
1543
+ assert.deepStrictEqual(m.entries[0].dashboardUrl, '', 'missing dashboardUrl defaults to empty string');
1544
+ assert.deepStrictEqual(m.entries[0].formatHint, '', 'missing formatHint defaults to empty string');
1545
+ assert.deepStrictEqual(m.entries[0].status, 'pending', 'missing status defaults to pending');
1546
+ assert.deepStrictEqual(m.entries[0].destination, 'dotenv', 'missing destination defaults to dotenv');
1547
+ assert.deepStrictEqual(m.entries[0].guidance.length, 1, 'guidance still parsed');
1548
+ });
1549
+
1550
+ test('parseSecretsManifest: all three status values parse', () => {
1607
1551
  for (const status of ['pending', 'collected', 'skipped'] as const) {
1608
1552
  const content = `# Secrets Manifest
1609
1553
 
@@ -1619,12 +1563,11 @@ console.log('\n=== parseSecretsManifest: all three status values parse ===');
1619
1563
  `;
1620
1564
 
1621
1565
  const m = parseSecretsManifest(content);
1622
- assertEq(m.entries[0].status, status, `status variant: ${status}`);
1566
+ assert.deepStrictEqual(m.entries[0].status, status, `status variant: ${status}`);
1623
1567
  }
1624
- }
1568
+ });
1625
1569
 
1626
- console.log('\n=== parseSecretsManifest: invalid status defaults to pending ===');
1627
- {
1570
+ test('parseSecretsManifest: invalid status defaults to pending', () => {
1628
1571
  const content = `# Secrets Manifest
1629
1572
 
1630
1573
  **Milestone:** M006
@@ -1639,11 +1582,10 @@ console.log('\n=== parseSecretsManifest: invalid status defaults to pending ==='
1639
1582
  `;
1640
1583
 
1641
1584
  const m = parseSecretsManifest(content);
1642
- assertEq(m.entries[0].status, 'pending', 'invalid status defaults to pending');
1643
- }
1585
+ assert.deepStrictEqual(m.entries[0].status, 'pending', 'invalid status defaults to pending');
1586
+ });
1644
1587
 
1645
- console.log('\n=== parseSecretsManifest + formatSecretsManifest: round-trip ===');
1646
- {
1588
+ test('parseSecretsManifest + formatSecretsManifest: round-trip', () => {
1647
1589
  const original = `# Secrets Manifest
1648
1590
 
1649
1591
  **Milestone:** M007
@@ -1678,32 +1620,30 @@ console.log('\n=== parseSecretsManifest + formatSecretsManifest: round-trip ==='
1678
1620
  const parsed2 = parseSecretsManifest(formatted);
1679
1621
 
1680
1622
  // Verify semantic equality after round-trip
1681
- assertEq(parsed2.milestone, parsed1.milestone, 'round-trip milestone');
1682
- assertEq(parsed2.generatedAt, parsed1.generatedAt, 'round-trip generatedAt');
1683
- assertEq(parsed2.entries.length, parsed1.entries.length, 'round-trip entry count');
1623
+ assert.deepStrictEqual(parsed2.milestone, parsed1.milestone, 'round-trip milestone');
1624
+ assert.deepStrictEqual(parsed2.generatedAt, parsed1.generatedAt, 'round-trip generatedAt');
1625
+ assert.deepStrictEqual(parsed2.entries.length, parsed1.entries.length, 'round-trip entry count');
1684
1626
 
1685
1627
  for (let i = 0; i < parsed1.entries.length; i++) {
1686
1628
  const e1 = parsed1.entries[i];
1687
1629
  const e2 = parsed2.entries[i];
1688
- assertEq(e2.key, e1.key, `round-trip entry ${i} key`);
1689
- assertEq(e2.service, e1.service, `round-trip entry ${i} service`);
1690
- assertEq(e2.dashboardUrl, e1.dashboardUrl, `round-trip entry ${i} dashboardUrl`);
1691
- assertEq(e2.formatHint, e1.formatHint, `round-trip entry ${i} formatHint`);
1692
- assertEq(e2.status, e1.status, `round-trip entry ${i} status`);
1693
- assertEq(e2.destination, e1.destination, `round-trip entry ${i} destination`);
1694
- assertEq(e2.guidance.length, e1.guidance.length, `round-trip entry ${i} guidance length`);
1630
+ assert.deepStrictEqual(e2.key, e1.key, `round-trip entry ${i} key`);
1631
+ assert.deepStrictEqual(e2.service, e1.service, `round-trip entry ${i} service`);
1632
+ assert.deepStrictEqual(e2.dashboardUrl, e1.dashboardUrl, `round-trip entry ${i} dashboardUrl`);
1633
+ assert.deepStrictEqual(e2.formatHint, e1.formatHint, `round-trip entry ${i} formatHint`);
1634
+ assert.deepStrictEqual(e2.status, e1.status, `round-trip entry ${i} status`);
1635
+ assert.deepStrictEqual(e2.destination, e1.destination, `round-trip entry ${i} destination`);
1636
+ assert.deepStrictEqual(e2.guidance.length, e1.guidance.length, `round-trip entry ${i} guidance length`);
1695
1637
  for (let j = 0; j < e1.guidance.length; j++) {
1696
- assertEq(e2.guidance[j], e1.guidance[j], `round-trip entry ${i} guidance[${j}]`);
1638
+ assert.deepStrictEqual(e2.guidance[j], e1.guidance[j], `round-trip entry ${i} guidance[${j}]`);
1697
1639
  }
1698
1640
  }
1699
- }
1641
+ });
1700
1642
 
1701
1643
  // ═══════════════════════════════════════════════════════════════════════════
1702
1644
  // LLM-style round-trip tests — realistic manifest variations
1703
1645
  // ═══════════════════════════════════════════════════════════════════════════
1704
-
1705
- console.log('\n=== LLM round-trip: extra whitespace ===');
1706
- {
1646
+ test('LLM round-trip: extra whitespace', () => {
1707
1647
  // LLMs often produce inconsistent indentation and trailing spaces
1708
1648
  const messy = `# Secrets Manifest
1709
1649
 
@@ -1734,34 +1674,33 @@ console.log('\n=== LLM round-trip: extra whitespace ===');
1734
1674
  const formatted = formatSecretsManifest(parsed1);
1735
1675
  const parsed2 = parseSecretsManifest(formatted);
1736
1676
 
1737
- assertEq(parsed2.milestone, parsed1.milestone, 'whitespace round-trip milestone');
1738
- assertEq(parsed2.generatedAt, parsed1.generatedAt, 'whitespace round-trip generatedAt');
1739
- assertEq(parsed2.entries.length, parsed1.entries.length, 'whitespace round-trip entry count');
1740
- assertEq(parsed2.entries.length, 2, 'whitespace: two entries parsed');
1677
+ assert.deepStrictEqual(parsed2.milestone, parsed1.milestone, 'whitespace round-trip milestone');
1678
+ assert.deepStrictEqual(parsed2.generatedAt, parsed1.generatedAt, 'whitespace round-trip generatedAt');
1679
+ assert.deepStrictEqual(parsed2.entries.length, parsed1.entries.length, 'whitespace round-trip entry count');
1680
+ assert.deepStrictEqual(parsed2.entries.length, 2, 'whitespace: two entries parsed');
1741
1681
 
1742
1682
  for (let i = 0; i < parsed1.entries.length; i++) {
1743
1683
  const e1 = parsed1.entries[i];
1744
1684
  const e2 = parsed2.entries[i];
1745
- assertEq(e2.key, e1.key, `whitespace round-trip entry ${i} key`);
1746
- assertEq(e2.service, e1.service, `whitespace round-trip entry ${i} service`);
1747
- assertEq(e2.dashboardUrl, e1.dashboardUrl, `whitespace round-trip entry ${i} dashboardUrl`);
1748
- assertEq(e2.formatHint, e1.formatHint, `whitespace round-trip entry ${i} formatHint`);
1749
- assertEq(e2.status, e1.status, `whitespace round-trip entry ${i} status`);
1750
- assertEq(e2.destination, e1.destination, `whitespace round-trip entry ${i} destination`);
1751
- assertEq(e2.guidance.length, e1.guidance.length, `whitespace round-trip entry ${i} guidance length`);
1685
+ assert.deepStrictEqual(e2.key, e1.key, `whitespace round-trip entry ${i} key`);
1686
+ assert.deepStrictEqual(e2.service, e1.service, `whitespace round-trip entry ${i} service`);
1687
+ assert.deepStrictEqual(e2.dashboardUrl, e1.dashboardUrl, `whitespace round-trip entry ${i} dashboardUrl`);
1688
+ assert.deepStrictEqual(e2.formatHint, e1.formatHint, `whitespace round-trip entry ${i} formatHint`);
1689
+ assert.deepStrictEqual(e2.status, e1.status, `whitespace round-trip entry ${i} status`);
1690
+ assert.deepStrictEqual(e2.destination, e1.destination, `whitespace round-trip entry ${i} destination`);
1691
+ assert.deepStrictEqual(e2.guidance.length, e1.guidance.length, `whitespace round-trip entry ${i} guidance length`);
1752
1692
  for (let j = 0; j < e1.guidance.length; j++) {
1753
- assertEq(e2.guidance[j], e1.guidance[j], `whitespace round-trip entry ${i} guidance[${j}]`);
1693
+ assert.deepStrictEqual(e2.guidance[j], e1.guidance[j], `whitespace round-trip entry ${i} guidance[${j}]`);
1754
1694
  }
1755
1695
  }
1756
1696
 
1757
1697
  // Verify the parser correctly stripped trailing whitespace
1758
- assertEq(parsed1.milestone, 'M010', 'whitespace: milestone trimmed');
1759
- assertEq(parsed1.entries[0].key, 'OPENAI_API_KEY', 'whitespace: key trimmed');
1760
- assertEq(parsed1.entries[0].service, 'OpenAI', 'whitespace: service trimmed');
1761
- }
1698
+ assert.deepStrictEqual(parsed1.milestone, 'M010', 'whitespace: milestone trimmed');
1699
+ assert.deepStrictEqual(parsed1.entries[0].key, 'OPENAI_API_KEY', 'whitespace: key trimmed');
1700
+ assert.deepStrictEqual(parsed1.entries[0].service, 'OpenAI', 'whitespace: service trimmed');
1701
+ });
1762
1702
 
1763
- console.log('\n=== LLM round-trip: missing optional fields ===');
1764
- {
1703
+ test('LLM round-trip: missing optional fields', () => {
1765
1704
  // LLMs may omit Dashboard and Format hint lines entirely
1766
1705
  const minimal = `# Secrets Manifest
1767
1706
 
@@ -1789,32 +1728,31 @@ console.log('\n=== LLM round-trip: missing optional fields ===');
1789
1728
  const parsed1 = parseSecretsManifest(minimal);
1790
1729
 
1791
1730
  // Verify missing optional fields get defaults
1792
- assertEq(parsed1.entries[0].dashboardUrl, '', 'missing-optional: no dashboard → empty string');
1793
- assertEq(parsed1.entries[0].formatHint, '', 'missing-optional: no format hint → empty string');
1794
- assertEq(parsed1.entries[1].dashboardUrl, '', 'missing-optional: entry 2 no dashboard → empty string');
1795
- assertEq(parsed1.entries[1].formatHint, '', 'missing-optional: entry 2 no format hint → empty string');
1731
+ assert.deepStrictEqual(parsed1.entries[0].dashboardUrl, '', 'missing-optional: no dashboard → empty string');
1732
+ assert.deepStrictEqual(parsed1.entries[0].formatHint, '', 'missing-optional: no format hint → empty string');
1733
+ assert.deepStrictEqual(parsed1.entries[1].dashboardUrl, '', 'missing-optional: entry 2 no dashboard → empty string');
1734
+ assert.deepStrictEqual(parsed1.entries[1].formatHint, '', 'missing-optional: entry 2 no format hint → empty string');
1796
1735
 
1797
1736
  // Round-trip: formatter omits empty optional fields, re-parse preserves defaults
1798
1737
  const formatted = formatSecretsManifest(parsed1);
1799
1738
  const parsed2 = parseSecretsManifest(formatted);
1800
1739
 
1801
- assertEq(parsed2.entries.length, parsed1.entries.length, 'missing-optional round-trip entry count');
1740
+ assert.deepStrictEqual(parsed2.entries.length, parsed1.entries.length, 'missing-optional round-trip entry count');
1802
1741
 
1803
1742
  for (let i = 0; i < parsed1.entries.length; i++) {
1804
1743
  const e1 = parsed1.entries[i];
1805
1744
  const e2 = parsed2.entries[i];
1806
- assertEq(e2.key, e1.key, `missing-optional round-trip entry ${i} key`);
1807
- assertEq(e2.service, e1.service, `missing-optional round-trip entry ${i} service`);
1808
- assertEq(e2.dashboardUrl, e1.dashboardUrl, `missing-optional round-trip entry ${i} dashboardUrl`);
1809
- assertEq(e2.formatHint, e1.formatHint, `missing-optional round-trip entry ${i} formatHint`);
1810
- assertEq(e2.status, e1.status, `missing-optional round-trip entry ${i} status`);
1811
- assertEq(e2.destination, e1.destination, `missing-optional round-trip entry ${i} destination`);
1812
- assertEq(e2.guidance.length, e1.guidance.length, `missing-optional round-trip entry ${i} guidance length`);
1745
+ assert.deepStrictEqual(e2.key, e1.key, `missing-optional round-trip entry ${i} key`);
1746
+ assert.deepStrictEqual(e2.service, e1.service, `missing-optional round-trip entry ${i} service`);
1747
+ assert.deepStrictEqual(e2.dashboardUrl, e1.dashboardUrl, `missing-optional round-trip entry ${i} dashboardUrl`);
1748
+ assert.deepStrictEqual(e2.formatHint, e1.formatHint, `missing-optional round-trip entry ${i} formatHint`);
1749
+ assert.deepStrictEqual(e2.status, e1.status, `missing-optional round-trip entry ${i} status`);
1750
+ assert.deepStrictEqual(e2.destination, e1.destination, `missing-optional round-trip entry ${i} destination`);
1751
+ assert.deepStrictEqual(e2.guidance.length, e1.guidance.length, `missing-optional round-trip entry ${i} guidance length`);
1813
1752
  }
1814
- }
1753
+ });
1815
1754
 
1816
- console.log('\n=== LLM round-trip: extra blank lines ===');
1817
- {
1755
+ test('LLM round-trip: extra blank lines', () => {
1818
1756
  // LLMs sometimes insert excessive blank lines between sections
1819
1757
  const blanky = `# Secrets Manifest
1820
1758
 
@@ -1858,42 +1796,40 @@ console.log('\n=== LLM round-trip: extra blank lines ===');
1858
1796
 
1859
1797
  const parsed1 = parseSecretsManifest(blanky);
1860
1798
 
1861
- assertEq(parsed1.entries.length, 2, 'blank-lines: two entries parsed');
1862
- assertEq(parsed1.milestone, 'M012', 'blank-lines: milestone parsed');
1863
- assertEq(parsed1.entries[0].key, 'API_KEY_ONE', 'blank-lines: first key');
1864
- assertEq(parsed1.entries[0].guidance.length, 2, 'blank-lines: first entry guidance count');
1865
- assertEq(parsed1.entries[1].key, 'API_KEY_TWO', 'blank-lines: second key');
1866
- assertEq(parsed1.entries[1].status, 'skipped', 'blank-lines: second entry status');
1799
+ assert.deepStrictEqual(parsed1.entries.length, 2, 'blank-lines: two entries parsed');
1800
+ assert.deepStrictEqual(parsed1.milestone, 'M012', 'blank-lines: milestone parsed');
1801
+ assert.deepStrictEqual(parsed1.entries[0].key, 'API_KEY_ONE', 'blank-lines: first key');
1802
+ assert.deepStrictEqual(parsed1.entries[0].guidance.length, 2, 'blank-lines: first entry guidance count');
1803
+ assert.deepStrictEqual(parsed1.entries[1].key, 'API_KEY_TWO', 'blank-lines: second key');
1804
+ assert.deepStrictEqual(parsed1.entries[1].status, 'skipped', 'blank-lines: second entry status');
1867
1805
 
1868
1806
  // Round-trip produces clean output
1869
1807
  const formatted = formatSecretsManifest(parsed1);
1870
1808
  const parsed2 = parseSecretsManifest(formatted);
1871
1809
 
1872
- assertEq(parsed2.entries.length, parsed1.entries.length, 'blank-lines round-trip entry count');
1810
+ assert.deepStrictEqual(parsed2.entries.length, parsed1.entries.length, 'blank-lines round-trip entry count');
1873
1811
 
1874
1812
  for (let i = 0; i < parsed1.entries.length; i++) {
1875
1813
  const e1 = parsed1.entries[i];
1876
1814
  const e2 = parsed2.entries[i];
1877
- assertEq(e2.key, e1.key, `blank-lines round-trip entry ${i} key`);
1878
- assertEq(e2.service, e1.service, `blank-lines round-trip entry ${i} service`);
1879
- assertEq(e2.dashboardUrl, e1.dashboardUrl, `blank-lines round-trip entry ${i} dashboardUrl`);
1880
- assertEq(e2.formatHint, e1.formatHint, `blank-lines round-trip entry ${i} formatHint`);
1881
- assertEq(e2.status, e1.status, `blank-lines round-trip entry ${i} status`);
1882
- assertEq(e2.destination, e1.destination, `blank-lines round-trip entry ${i} destination`);
1883
- assertEq(e2.guidance.length, e1.guidance.length, `blank-lines round-trip entry ${i} guidance length`);
1815
+ assert.deepStrictEqual(e2.key, e1.key, `blank-lines round-trip entry ${i} key`);
1816
+ assert.deepStrictEqual(e2.service, e1.service, `blank-lines round-trip entry ${i} service`);
1817
+ assert.deepStrictEqual(e2.dashboardUrl, e1.dashboardUrl, `blank-lines round-trip entry ${i} dashboardUrl`);
1818
+ assert.deepStrictEqual(e2.formatHint, e1.formatHint, `blank-lines round-trip entry ${i} formatHint`);
1819
+ assert.deepStrictEqual(e2.status, e1.status, `blank-lines round-trip entry ${i} status`);
1820
+ assert.deepStrictEqual(e2.destination, e1.destination, `blank-lines round-trip entry ${i} destination`);
1821
+ assert.deepStrictEqual(e2.guidance.length, e1.guidance.length, `blank-lines round-trip entry ${i} guidance length`);
1884
1822
  }
1885
1823
 
1886
1824
  // Verify the formatted output is cleaner (fewer consecutive blank lines)
1887
1825
  const consecutiveBlanks = formatted.match(/\n{4,}/g);
1888
- assertTrue(consecutiveBlanks === null, 'blank-lines: formatted output has no 4+ consecutive newlines');
1889
- }
1826
+ assert.ok(consecutiveBlanks === null, 'blank-lines: formatted output has no 4+ consecutive newlines');
1827
+ });
1890
1828
 
1891
1829
  // ═══════════════════════════════════════════════════════════════════════════
1892
1830
  // parseRoadmap: boundary map with embedded code fences (#468)
1893
1831
  // ═══════════════════════════════════════════════════════════════════════════
1894
-
1895
- console.log('\n=== parseRoadmap: boundary map with code fences (#468) ===');
1896
- {
1832
+ test('parseRoadmap: boundary map with code fences (#468)', () => {
1897
1833
  const content = `# M001: Test
1898
1834
 
1899
1835
  **Vision:** Test
@@ -1922,10 +1858,10 @@ Consumes: nothing
1922
1858
  const r = parseRoadmap(content);
1923
1859
  const elapsed = Date.now() - start;
1924
1860
 
1925
- assertTrue(elapsed < 1000, `boundary map with code fences parsed in ${elapsed}ms (should be < 1s)`);
1926
- assertEq(r.slices.length, 2, 'code-fence roadmap: slice count');
1861
+ assert.ok(elapsed < 1000, `boundary map with code fences parsed in ${elapsed}ms (should be < 1s)`);
1862
+ assert.deepStrictEqual(r.slices.length, 2, 'code-fence roadmap: slice count');
1927
1863
  // Boundary map should still parse (may not capture perfectly with code fences, but must not hang)
1928
- assertTrue(r.boundaryMap.length >= 0, 'code-fence roadmap: boundary map parsed without hanging');
1929
- }
1864
+ assert.ok(r.boundaryMap.length >= 0, 'code-fence roadmap: boundary map parsed without hanging');
1865
+ });
1930
1866
 
1931
- report();
1867
+ });