gsd-pi 2.44.0-dev.848dd4c → 2.44.0

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