gsd-pi 2.44.0-dev.62b5d6c → 2.44.0-dev.848dd4c

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 (190) hide show
  1. package/README.md +30 -12
  2. package/dist/resources/extensions/gsd/auto-start.js +10 -0
  3. package/dist/resources/extensions/gsd/commands/handlers/workflow.js +5 -0
  4. package/dist/web/standalone/.next/BUILD_ID +1 -1
  5. package/dist/web/standalone/.next/app-path-routes-manifest.json +18 -18
  6. package/dist/web/standalone/.next/build-manifest.json +2 -2
  7. package/dist/web/standalone/.next/prerender-manifest.json +3 -3
  8. package/dist/web/standalone/.next/server/app/_global-error.html +2 -2
  9. package/dist/web/standalone/.next/server/app/_global-error.rsc +1 -1
  10. package/dist/web/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  11. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
  12. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
  13. package/dist/web/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  14. package/dist/web/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  15. package/dist/web/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  16. package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
  17. package/dist/web/standalone/.next/server/app/_not-found.rsc +1 -1
  18. package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +1 -1
  19. package/dist/web/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  20. package/dist/web/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +1 -1
  21. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  22. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  23. package/dist/web/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
  24. package/dist/web/standalone/.next/server/app/index.html +1 -1
  25. package/dist/web/standalone/.next/server/app/index.rsc +1 -1
  26. package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +1 -1
  27. package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +1 -1
  28. package/dist/web/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
  29. package/dist/web/standalone/.next/server/app/index.segments/_index.segment.rsc +1 -1
  30. package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
  31. package/dist/web/standalone/.next/server/app-paths-manifest.json +18 -18
  32. package/dist/web/standalone/.next/server/pages/404.html +1 -1
  33. package/dist/web/standalone/.next/server/pages/500.html +2 -2
  34. package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
  35. package/package.json +1 -1
  36. package/packages/pi-coding-agent/dist/core/auth-storage.test.js +6 -8
  37. package/packages/pi-coding-agent/dist/core/auth-storage.test.js.map +1 -1
  38. package/packages/pi-coding-agent/dist/core/extensions/runner.test.js +24 -26
  39. package/packages/pi-coding-agent/dist/core/extensions/runner.test.js.map +1 -1
  40. package/packages/pi-coding-agent/dist/core/fs-utils.test.js +29 -48
  41. package/packages/pi-coding-agent/dist/core/fs-utils.test.js.map +1 -1
  42. package/packages/pi-coding-agent/dist/core/resolve-config-value.test.js +34 -44
  43. package/packages/pi-coding-agent/dist/core/resolve-config-value.test.js.map +1 -1
  44. package/packages/pi-coding-agent/dist/core/session-manager.test.js +30 -34
  45. package/packages/pi-coding-agent/dist/core/session-manager.test.js.map +1 -1
  46. package/packages/pi-coding-agent/dist/core/tools/edit-diff.test.js +10 -12
  47. package/packages/pi-coding-agent/dist/core/tools/edit-diff.test.js.map +1 -1
  48. package/packages/pi-coding-agent/dist/resources/extensions/memory/storage.test.js +43 -47
  49. package/packages/pi-coding-agent/dist/resources/extensions/memory/storage.test.js.map +1 -1
  50. package/packages/pi-coding-agent/src/core/auth-storage.test.ts +7 -7
  51. package/packages/pi-coding-agent/src/core/extensions/runner.test.ts +26 -26
  52. package/packages/pi-coding-agent/src/core/fs-utils.test.ts +31 -43
  53. package/packages/pi-coding-agent/src/core/resolve-config-value.test.ts +40 -45
  54. package/packages/pi-coding-agent/src/core/session-manager.test.ts +33 -33
  55. package/packages/pi-coding-agent/src/core/tools/edit-diff.test.ts +17 -17
  56. package/packages/pi-coding-agent/src/resources/extensions/memory/storage.test.ts +74 -74
  57. package/src/resources/extensions/gsd/auto-start.ts +14 -0
  58. package/src/resources/extensions/gsd/commands/handlers/workflow.ts +8 -0
  59. package/src/resources/extensions/gsd/tests/all-milestones-complete-merge.test.ts +99 -99
  60. package/src/resources/extensions/gsd/tests/auto-lock-creation.test.ts +14 -16
  61. package/src/resources/extensions/gsd/tests/auto-paused-session-validation.test.ts +43 -57
  62. package/src/resources/extensions/gsd/tests/auto-preflight.test.ts +11 -13
  63. package/src/resources/extensions/gsd/tests/auto-recovery.test.ts +465 -523
  64. package/src/resources/extensions/gsd/tests/auto-secrets-gate.test.ts +73 -75
  65. package/src/resources/extensions/gsd/tests/auto-start-needs-discussion.test.ts +34 -56
  66. package/src/resources/extensions/gsd/tests/auto-worktree-milestone-merge.test.ts +533 -656
  67. package/src/resources/extensions/gsd/tests/auto-worktree.test.ts +165 -143
  68. package/src/resources/extensions/gsd/tests/cache-staleness-regression.test.ts +29 -52
  69. package/src/resources/extensions/gsd/tests/captures.test.ts +148 -176
  70. package/src/resources/extensions/gsd/tests/claude-import-tui.test.ts +32 -33
  71. package/src/resources/extensions/gsd/tests/collect-from-manifest.test.ts +141 -143
  72. package/src/resources/extensions/gsd/tests/commands-inspect-open-db.test.ts +25 -25
  73. package/src/resources/extensions/gsd/tests/commands-logs.test.ts +81 -81
  74. package/src/resources/extensions/gsd/tests/complete-milestone.test.ts +38 -59
  75. package/src/resources/extensions/gsd/tests/complete-slice.test.ts +228 -263
  76. package/src/resources/extensions/gsd/tests/complete-task.test.ts +250 -302
  77. package/src/resources/extensions/gsd/tests/context-store.test.ts +354 -367
  78. package/src/resources/extensions/gsd/tests/continue-here.test.ts +68 -72
  79. package/src/resources/extensions/gsd/tests/cost-projection.test.ts +92 -106
  80. package/src/resources/extensions/gsd/tests/crash-recovery.test.ts +27 -35
  81. package/src/resources/extensions/gsd/tests/dashboard-budget.test.ts +220 -237
  82. package/src/resources/extensions/gsd/tests/db-writer.test.ts +390 -420
  83. package/src/resources/extensions/gsd/tests/definition-loader.test.ts +76 -92
  84. package/src/resources/extensions/gsd/tests/derive-state-crossval.test.ts +68 -83
  85. package/src/resources/extensions/gsd/tests/derive-state-db.test.ts +152 -183
  86. package/src/resources/extensions/gsd/tests/derive-state-deps.test.ts +78 -101
  87. package/src/resources/extensions/gsd/tests/derive-state.test.ts +192 -227
  88. package/src/resources/extensions/gsd/tests/detection.test.ts +232 -278
  89. package/src/resources/extensions/gsd/tests/dev-engine-wrapper.test.ts +30 -34
  90. package/src/resources/extensions/gsd/tests/dispatch-guard.test.ts +164 -180
  91. package/src/resources/extensions/gsd/tests/dispatch-missing-task-plans.test.ts +43 -49
  92. package/src/resources/extensions/gsd/tests/dispatch-uat-last-completed.test.ts +28 -32
  93. package/src/resources/extensions/gsd/tests/doctor-completion-deferral.test.ts +27 -29
  94. package/src/resources/extensions/gsd/tests/doctor-delimiter-fix.test.ts +34 -38
  95. package/src/resources/extensions/gsd/tests/doctor-enhancements.test.ts +54 -75
  96. package/src/resources/extensions/gsd/tests/doctor-environment-worktree.test.ts +21 -32
  97. package/src/resources/extensions/gsd/tests/doctor-environment.test.ts +72 -97
  98. package/src/resources/extensions/gsd/tests/doctor-fixlevel.test.ts +38 -44
  99. package/src/resources/extensions/gsd/tests/doctor-git.test.ts +104 -145
  100. package/src/resources/extensions/gsd/tests/doctor-proactive.test.ts +84 -106
  101. package/src/resources/extensions/gsd/tests/doctor-roadmap-summary-atomicity.test.ts +54 -60
  102. package/src/resources/extensions/gsd/tests/doctor-runtime.test.ts +72 -93
  103. package/src/resources/extensions/gsd/tests/doctor.test.ts +104 -134
  104. package/src/resources/extensions/gsd/tests/ensure-db-open.test.ts +123 -131
  105. package/src/resources/extensions/gsd/tests/exit-command.test.ts +20 -24
  106. package/src/resources/extensions/gsd/tests/feature-branch-lifecycle-integration.test.ts +48 -57
  107. package/src/resources/extensions/gsd/tests/files-loadfile-eisdir.test.ts +5 -7
  108. package/src/resources/extensions/gsd/tests/flag-file-db.test.ts +30 -42
  109. package/src/resources/extensions/gsd/tests/freeform-decisions.test.ts +198 -206
  110. package/src/resources/extensions/gsd/tests/git-locale.test.ts +13 -27
  111. package/src/resources/extensions/gsd/tests/git-service.test.ts +285 -388
  112. package/src/resources/extensions/gsd/tests/gitignore-tracked-gsd.test.ts +31 -39
  113. package/src/resources/extensions/gsd/tests/graph-operations.test.ts +63 -69
  114. package/src/resources/extensions/gsd/tests/gsd-db.test.ts +255 -264
  115. package/src/resources/extensions/gsd/tests/gsd-inspect.test.ts +108 -119
  116. package/src/resources/extensions/gsd/tests/gsd-recover.test.ts +81 -103
  117. package/src/resources/extensions/gsd/tests/gsd-tools.test.ts +229 -262
  118. package/src/resources/extensions/gsd/tests/headless-answers.test.ts +13 -13
  119. package/src/resources/extensions/gsd/tests/health-widget.test.ts +29 -37
  120. package/src/resources/extensions/gsd/tests/idle-recovery.test.ts +81 -102
  121. package/src/resources/extensions/gsd/tests/init-wizard.test.ts +16 -18
  122. package/src/resources/extensions/gsd/tests/integration-edge.test.ts +41 -46
  123. package/src/resources/extensions/gsd/tests/integration-lifecycle.test.ts +42 -53
  124. package/src/resources/extensions/gsd/tests/integration-mixed-milestones.test.ts +75 -91
  125. package/src/resources/extensions/gsd/tests/integration-proof.test.ts +18 -18
  126. package/src/resources/extensions/gsd/tests/markdown-renderer.test.ts +150 -194
  127. package/src/resources/extensions/gsd/tests/md-importer.test.ts +101 -125
  128. package/src/resources/extensions/gsd/tests/memory-extractor.test.ts +45 -54
  129. package/src/resources/extensions/gsd/tests/memory-store.test.ts +80 -93
  130. package/src/resources/extensions/gsd/tests/migrate-command.test.ts +57 -66
  131. package/src/resources/extensions/gsd/tests/migrate-hierarchy.test.ts +83 -93
  132. package/src/resources/extensions/gsd/tests/migrate-parser.test.ts +161 -170
  133. package/src/resources/extensions/gsd/tests/migrate-transformer.test.ts +125 -141
  134. package/src/resources/extensions/gsd/tests/migrate-validator-parsers.test.ts +107 -131
  135. package/src/resources/extensions/gsd/tests/migrate-writer-integration.test.ts +87 -96
  136. package/src/resources/extensions/gsd/tests/migrate-writer.test.ts +125 -164
  137. package/src/resources/extensions/gsd/tests/must-have-parser.test.ts +81 -94
  138. package/src/resources/extensions/gsd/tests/none-mode-gates.test.ts +35 -36
  139. package/src/resources/extensions/gsd/tests/overrides.test.ts +99 -106
  140. package/src/resources/extensions/gsd/tests/parallel-crash-recovery.test.ts +40 -47
  141. package/src/resources/extensions/gsd/tests/parallel-worker-monitoring.test.ts +25 -28
  142. package/src/resources/extensions/gsd/tests/parallel-workers-multi-milestone-e2e.test.ts +66 -83
  143. package/src/resources/extensions/gsd/tests/park-edge-cases.test.ts +54 -77
  144. package/src/resources/extensions/gsd/tests/park-milestone.test.ts +68 -115
  145. package/src/resources/extensions/gsd/tests/parsers.test.ts +546 -611
  146. package/src/resources/extensions/gsd/tests/paths.test.ts +72 -87
  147. package/src/resources/extensions/gsd/tests/post-unit-hooks.test.ts +77 -117
  148. package/src/resources/extensions/gsd/tests/prompt-db.test.ts +56 -56
  149. package/src/resources/extensions/gsd/tests/queue-draft-detection.test.ts +93 -119
  150. package/src/resources/extensions/gsd/tests/queue-order.test.ts +70 -82
  151. package/src/resources/extensions/gsd/tests/queue-reorder-e2e.test.ts +42 -55
  152. package/src/resources/extensions/gsd/tests/quick-auto-guard.test.ts +100 -0
  153. package/src/resources/extensions/gsd/tests/quick-branch-lifecycle.test.ts +45 -73
  154. package/src/resources/extensions/gsd/tests/reassess-prompt.test.ts +28 -38
  155. package/src/resources/extensions/gsd/tests/replan-slice.test.ts +73 -80
  156. package/src/resources/extensions/gsd/tests/repo-identity-worktree.test.ts +71 -74
  157. package/src/resources/extensions/gsd/tests/requirements.test.ts +70 -75
  158. package/src/resources/extensions/gsd/tests/retry-state-reset.test.ts +44 -66
  159. package/src/resources/extensions/gsd/tests/roadmap-parse-regression.test.ts +114 -181
  160. package/src/resources/extensions/gsd/tests/rule-registry.test.ts +63 -65
  161. package/src/resources/extensions/gsd/tests/run-uat.test.ts +66 -128
  162. package/src/resources/extensions/gsd/tests/session-lock-multipath.test.ts +18 -25
  163. package/src/resources/extensions/gsd/tests/session-lock-regression.test.ts +37 -44
  164. package/src/resources/extensions/gsd/tests/shared-wal.test.ts +19 -26
  165. package/src/resources/extensions/gsd/tests/sqlite-unavailable-gate.test.ts +63 -0
  166. package/src/resources/extensions/gsd/tests/stalled-tool-recovery.test.ts +6 -8
  167. package/src/resources/extensions/gsd/tests/symlink-numbered-variants.test.ts +22 -28
  168. package/src/resources/extensions/gsd/tests/token-savings.test.ts +54 -56
  169. package/src/resources/extensions/gsd/tests/tool-call-loop-guard.test.ts +23 -25
  170. package/src/resources/extensions/gsd/tests/tool-naming.test.ts +9 -11
  171. package/src/resources/extensions/gsd/tests/unique-milestone-ids.test.ts +66 -82
  172. package/src/resources/extensions/gsd/tests/unit-runtime.test.ts +46 -47
  173. package/src/resources/extensions/gsd/tests/visualizer-critical-path.test.ts +20 -22
  174. package/src/resources/extensions/gsd/tests/visualizer-data.test.ts +84 -86
  175. package/src/resources/extensions/gsd/tests/visualizer-overlay.test.ts +41 -43
  176. package/src/resources/extensions/gsd/tests/visualizer-views.test.ts +94 -96
  177. package/src/resources/extensions/gsd/tests/windows-path-normalization.test.ts +11 -13
  178. package/src/resources/extensions/gsd/tests/worker-registry.test.ts +27 -29
  179. package/src/resources/extensions/gsd/tests/workflow-templates.test.ts +50 -52
  180. package/src/resources/extensions/gsd/tests/worktree-bugfix.test.ts +10 -13
  181. package/src/resources/extensions/gsd/tests/worktree-db-integration.test.ts +14 -18
  182. package/src/resources/extensions/gsd/tests/worktree-db.test.ts +38 -39
  183. package/src/resources/extensions/gsd/tests/worktree-e2e.test.ts +17 -21
  184. package/src/resources/extensions/gsd/tests/worktree-health.test.ts +25 -30
  185. package/src/resources/extensions/gsd/tests/worktree-integration.test.ts +30 -37
  186. package/src/resources/extensions/gsd/tests/worktree-symlink-removal.test.ts +15 -22
  187. package/src/resources/extensions/gsd/tests/worktree-sync-milestones.test.ts +59 -66
  188. package/src/resources/extensions/gsd/tests/worktree.test.ts +44 -50
  189. /package/dist/web/standalone/.next/static/{fOnWQBjWXMKUs4bqTg530 → -zps1Q9mQmioAKLcQiCr8}/_buildManifest.js +0 -0
  190. /package/dist/web/standalone/.next/static/{fOnWQBjWXMKUs4bqTg530 → -zps1Q9mQmioAKLcQiCr8}/_ssgManifest.js +0 -0
@@ -1,4 +1,3 @@
1
- import { createTestContext } from './test-helpers.ts';
2
1
  import * as path from 'node:path';
3
2
  import * as os from 'node:os';
4
3
  import * as fs from 'node:fs';
@@ -38,8 +37,8 @@ import {
38
37
  } from '../files.ts';
39
38
  import { clearPathCache, _clearGsdRootCache } from '../paths.ts';
40
39
  import { invalidateStateCache } from '../state.ts';
41
-
42
- const { assertEq, assertTrue, assertMatch, report } = createTestContext();
40
+ import { describe, test, beforeEach, afterEach } from 'node:test';
41
+ import assert from 'node:assert/strict';
43
42
 
44
43
  // ═══════════════════════════════════════════════════════════════════════════
45
44
  // Helpers
@@ -174,29 +173,27 @@ function makeTaskSummaryContent(taskId: string): string {
174
173
  // DB Accessor Tests
175
174
  // ═══════════════════════════════════════════════════════════════════════════
176
175
 
177
- console.log('\n── markdown-renderer: DB accessor basics ──');
178
-
179
- {
176
+ test('── markdown-renderer: DB accessor basics ──', () => {
180
177
  openDatabase(':memory:');
181
178
 
182
179
  // getAllMilestones — empty
183
180
  const empty = getAllMilestones();
184
- assertEq(empty.length, 0, 'getAllMilestones returns empty when no milestones');
181
+ assert.deepStrictEqual(empty.length, 0, 'getAllMilestones returns empty when no milestones');
185
182
 
186
183
  // Insert and retrieve
187
184
  insertMilestone({ id: 'M001', title: 'Test MS', status: 'active' });
188
185
  insertMilestone({ id: 'M002', title: 'Second MS', status: 'active' });
189
186
 
190
187
  const all = getAllMilestones();
191
- assertEq(all.length, 2, 'getAllMilestones returns 2 milestones');
192
- assertEq(all[0].id, 'M001', 'first milestone is M001');
193
- assertEq(all[1].id, 'M002', 'second milestone is M002');
194
- assertEq(all[0].title, 'Test MS', 'milestone title correct');
195
- assertEq(all[0].status, 'active', 'milestone status correct');
188
+ assert.deepStrictEqual(all.length, 2, 'getAllMilestones returns 2 milestones');
189
+ assert.deepStrictEqual(all[0].id, 'M001', 'first milestone is M001');
190
+ assert.deepStrictEqual(all[1].id, 'M002', 'second milestone is M002');
191
+ assert.deepStrictEqual(all[0].title, 'Test MS', 'milestone title correct');
192
+ assert.deepStrictEqual(all[0].status, 'active', 'milestone status correct');
196
193
 
197
194
  // getMilestoneSlices — empty
198
195
  const noSlices = getMilestoneSlices('M001');
199
- assertEq(noSlices.length, 0, 'getMilestoneSlices returns empty when no slices');
196
+ assert.deepStrictEqual(noSlices.length, 0, 'getMilestoneSlices returns empty when no slices');
200
197
 
201
198
  // Insert slices and retrieve
202
199
  insertSlice({ id: 'S01', milestoneId: 'M001', title: 'Slice 1', status: 'complete' });
@@ -204,26 +201,24 @@ console.log('\n── markdown-renderer: DB accessor basics ──');
204
201
  insertSlice({ id: 'S01', milestoneId: 'M002', title: 'M2 Slice', status: 'pending' });
205
202
 
206
203
  const m1Slices = getMilestoneSlices('M001');
207
- assertEq(m1Slices.length, 2, 'M001 has 2 slices');
208
- assertEq(m1Slices[0].id, 'S01', 'first slice is S01');
209
- assertEq(m1Slices[0].status, 'complete', 'S01 status is complete');
210
- assertEq(m1Slices[1].id, 'S02', 'second slice is S02');
211
- assertEq(m1Slices[1].status, 'pending', 'S02 status is pending');
204
+ assert.deepStrictEqual(m1Slices.length, 2, 'M001 has 2 slices');
205
+ assert.deepStrictEqual(m1Slices[0].id, 'S01', 'first slice is S01');
206
+ assert.deepStrictEqual(m1Slices[0].status, 'complete', 'S01 status is complete');
207
+ assert.deepStrictEqual(m1Slices[1].id, 'S02', 'second slice is S02');
208
+ assert.deepStrictEqual(m1Slices[1].status, 'pending', 'S02 status is pending');
212
209
 
213
210
  const m2Slices = getMilestoneSlices('M002');
214
- assertEq(m2Slices.length, 1, 'M002 has 1 slice');
211
+ assert.deepStrictEqual(m2Slices.length, 1, 'M002 has 1 slice');
215
212
 
216
213
  closeDatabase();
217
- }
214
+ });
218
215
 
219
- console.log('\n── markdown-renderer: getArtifact accessor ──');
220
-
221
- {
216
+ test('── markdown-renderer: getArtifact accessor ──', () => {
222
217
  openDatabase(':memory:');
223
218
 
224
219
  // Not found
225
220
  const missing = getArtifact('nonexistent/path');
226
- assertEq(missing, null, 'getArtifact returns null for missing path');
221
+ assert.deepStrictEqual(missing, null, 'getArtifact returns null for missing path');
227
222
 
228
223
  // Insert and retrieve
229
224
  insertArtifact({
@@ -236,21 +231,19 @@ console.log('\n── markdown-renderer: getArtifact accessor ──');
236
231
  });
237
232
 
238
233
  const found = getArtifact('milestones/M001/M001-ROADMAP.md');
239
- assertTrue(found !== null, 'getArtifact returns non-null for existing path');
240
- assertEq(found!.artifact_type, 'ROADMAP', 'artifact type correct');
241
- assertEq(found!.milestone_id, 'M001', 'milestone_id correct');
242
- assertEq(found!.full_content, '# Roadmap content', 'content correct');
234
+ assert.ok(found !== null, 'getArtifact returns non-null for existing path');
235
+ assert.deepStrictEqual(found!.artifact_type, 'ROADMAP', 'artifact type correct');
236
+ assert.deepStrictEqual(found!.milestone_id, 'M001', 'milestone_id correct');
237
+ assert.deepStrictEqual(found!.full_content, '# Roadmap content', 'content correct');
243
238
 
244
239
  closeDatabase();
245
- }
240
+ });
246
241
 
247
242
  // ═══════════════════════════════════════════════════════════════════════════
248
243
  // Roadmap Checkbox Round-Trip
249
244
  // ═══════════════════════════════════════════════════════════════════════════
250
245
 
251
- console.log('\n── markdown-renderer: renderRoadmapCheckboxes round-trip ──');
252
-
253
- {
246
+ test('── markdown-renderer: renderRoadmapCheckboxes round-trip ──', async () => {
254
247
  const tmpDir = makeTmpDir();
255
248
  const dbPath = path.join(tmpDir, '.gsd', 'gsd.db');
256
249
  openDatabase(dbPath);
@@ -275,36 +268,34 @@ console.log('\n── markdown-renderer: renderRoadmapCheckboxes round-trip ─
275
268
 
276
269
  // Render — should set S01 [x] and leave S02 [ ]
277
270
  const ok = await renderRoadmapCheckboxes(tmpDir, 'M001');
278
- assertTrue(ok, 'renderRoadmapCheckboxes returns true');
271
+ assert.ok(ok, 'renderRoadmapCheckboxes returns true');
279
272
 
280
273
  // Read rendered file and parse
281
274
  const rendered = fs.readFileSync(roadmapPath, 'utf-8');
282
275
  clearAllCaches();
283
276
  const parsed = parseRoadmap(rendered);
284
277
 
285
- assertEq(parsed.slices.length, 2, 'roadmap has 2 slices after render');
278
+ assert.deepStrictEqual(parsed.slices.length, 2, 'roadmap has 2 slices after render');
286
279
 
287
280
  const s01 = parsed.slices.find(s => s.id === 'S01');
288
281
  const s02 = parsed.slices.find(s => s.id === 'S02');
289
- assertTrue(!!s01, 'S01 found in parsed roadmap');
290
- assertTrue(!!s02, 'S02 found in parsed roadmap');
291
- assertTrue(s01!.done, 'S01 is checked (done) after render');
292
- assertTrue(!s02!.done, 'S02 is unchecked (pending) after render');
282
+ assert.ok(!!s01, 'S01 found in parsed roadmap');
283
+ assert.ok(!!s02, 'S02 found in parsed roadmap');
284
+ assert.ok(s01!.done, 'S01 is checked (done) after render');
285
+ assert.ok(!s02!.done, 'S02 is unchecked (pending) after render');
293
286
 
294
287
  // Verify artifact stored in DB
295
288
  const artifact = getArtifact('milestones/M001/M001-ROADMAP.md');
296
- assertTrue(artifact !== null, 'roadmap artifact stored in DB after render');
297
- assertTrue(artifact!.full_content.includes('[x] **S01:'), 'DB artifact has S01 checked');
298
- assertTrue(artifact!.full_content.includes('[ ] **S02:'), 'DB artifact has S02 unchecked');
289
+ assert.ok(artifact !== null, 'roadmap artifact stored in DB after render');
290
+ assert.ok(artifact!.full_content.includes('[x] **S01:'), 'DB artifact has S01 checked');
291
+ assert.ok(artifact!.full_content.includes('[ ] **S02:'), 'DB artifact has S02 unchecked');
299
292
  } finally {
300
293
  closeDatabase();
301
294
  cleanupDir(tmpDir);
302
295
  }
303
- }
304
-
305
- console.log('\n── markdown-renderer: renderRoadmapCheckboxes bidirectional ──');
296
+ });
306
297
 
307
- {
298
+ test('── markdown-renderer: renderRoadmapCheckboxes bidirectional ──', async () => {
308
299
  const tmpDir = makeTmpDir();
309
300
  const dbPath = path.join(tmpDir, '.gsd', 'gsd.db');
310
301
  openDatabase(dbPath);
@@ -328,7 +319,7 @@ console.log('\n── markdown-renderer: renderRoadmapCheckboxes bidirectional
328
319
  clearAllCaches();
329
320
 
330
321
  const ok = await renderRoadmapCheckboxes(tmpDir, 'M001');
331
- assertTrue(ok, 'bidirectional render returns true');
322
+ assert.ok(ok, 'bidirectional render returns true');
332
323
 
333
324
  const rendered = fs.readFileSync(roadmapPath, 'utf-8');
334
325
  clearAllCaches();
@@ -336,21 +327,19 @@ console.log('\n── markdown-renderer: renderRoadmapCheckboxes bidirectional
336
327
 
337
328
  const s01 = parsed.slices.find(s => s.id === 'S01');
338
329
  const s02 = parsed.slices.find(s => s.id === 'S02');
339
- assertTrue(!s01!.done, 'S01 unchecked (DB says pending, was checked on disk)');
340
- assertTrue(s02!.done, 'S02 checked (DB says complete, was unchecked on disk)');
330
+ assert.ok(!s01!.done, 'S01 unchecked (DB says pending, was checked on disk)');
331
+ assert.ok(s02!.done, 'S02 checked (DB says complete, was unchecked on disk)');
341
332
  } finally {
342
333
  closeDatabase();
343
334
  cleanupDir(tmpDir);
344
335
  }
345
- }
336
+ });
346
337
 
347
338
  // ═══════════════════════════════════════════════════════════════════════════
348
339
  // Plan Checkbox Round-Trip
349
340
  // ═══════════════════════════════════════════════════════════════════════════
350
341
 
351
- console.log('\n── markdown-renderer: renderPlanCheckboxes round-trip ──');
352
-
353
- {
342
+ test('── markdown-renderer: renderPlanCheckboxes round-trip ──', async () => {
354
343
  const tmpDir = makeTmpDir();
355
344
  const dbPath = path.join(tmpDir, '.gsd', 'gsd.db');
356
345
  openDatabase(dbPath);
@@ -376,29 +365,27 @@ console.log('\n── markdown-renderer: renderPlanCheckboxes round-trip ──'
376
365
  clearAllCaches();
377
366
 
378
367
  const ok = await renderPlanCheckboxes(tmpDir, 'M001', 'S01');
379
- assertTrue(ok, 'renderPlanCheckboxes returns true');
368
+ assert.ok(ok, 'renderPlanCheckboxes returns true');
380
369
 
381
370
  const rendered = fs.readFileSync(planPath, 'utf-8');
382
371
  clearAllCaches();
383
372
  const parsed = parsePlan(rendered);
384
373
 
385
- assertEq(parsed.tasks.length, 3, 'plan has 3 tasks after render');
374
+ assert.deepStrictEqual(parsed.tasks.length, 3, 'plan has 3 tasks after render');
386
375
 
387
376
  const t01 = parsed.tasks.find(t => t.id === 'T01');
388
377
  const t02 = parsed.tasks.find(t => t.id === 'T02');
389
378
  const t03 = parsed.tasks.find(t => t.id === 'T03');
390
- assertTrue(t01!.done, 'T01 checked (done in DB)');
391
- assertTrue(t02!.done, 'T02 checked (done in DB)');
392
- assertTrue(!t03!.done, 'T03 unchecked (pending in DB)');
379
+ assert.ok(t01!.done, 'T01 checked (done in DB)');
380
+ assert.ok(t02!.done, 'T02 checked (done in DB)');
381
+ assert.ok(!t03!.done, 'T03 unchecked (pending in DB)');
393
382
  } finally {
394
383
  closeDatabase();
395
384
  cleanupDir(tmpDir);
396
385
  }
397
- }
386
+ });
398
387
 
399
- console.log('\n── markdown-renderer: renderPlanCheckboxes bidirectional ──');
400
-
401
- {
388
+ test('── markdown-renderer: renderPlanCheckboxes bidirectional ──', async () => {
402
389
  const tmpDir = makeTmpDir();
403
390
  const dbPath = path.join(tmpDir, '.gsd', 'gsd.db');
404
391
  openDatabase(dbPath);
@@ -422,7 +409,7 @@ console.log('\n── markdown-renderer: renderPlanCheckboxes bidirectional ─
422
409
  clearAllCaches();
423
410
 
424
411
  const ok = await renderPlanCheckboxes(tmpDir, 'M001', 'S01');
425
- assertTrue(ok, 'bidirectional plan render returns true');
412
+ assert.ok(ok, 'bidirectional plan render returns true');
426
413
 
427
414
  const rendered = fs.readFileSync(planPath, 'utf-8');
428
415
  clearAllCaches();
@@ -430,17 +417,15 @@ console.log('\n── markdown-renderer: renderPlanCheckboxes bidirectional ─
430
417
 
431
418
  const t01 = parsed.tasks.find(t => t.id === 'T01');
432
419
  const t02 = parsed.tasks.find(t => t.id === 'T02');
433
- assertTrue(!t01!.done, 'T01 unchecked (DB says pending, was checked)');
434
- assertTrue(t02!.done, 'T02 checked (DB says done, was unchecked)');
420
+ assert.ok(!t01!.done, 'T01 unchecked (DB says pending, was checked)');
421
+ assert.ok(t02!.done, 'T02 checked (DB says done, was unchecked)');
435
422
  } finally {
436
423
  closeDatabase();
437
424
  cleanupDir(tmpDir);
438
425
  }
439
- }
440
-
441
- console.log('\n── markdown-renderer: renderPlanFromDb creates parse-compatible slice plan + task plan files ──');
426
+ });
442
427
 
443
- {
428
+ test('── markdown-renderer: renderPlanFromDb creates parse-compatible slice plan + task plan files ──', async () => {
444
429
  const tmpDir = makeTmpDir();
445
430
  const dbPath = path.join(tmpDir, '.gsd', 'gsd.db');
446
431
  openDatabase(dbPath);
@@ -498,50 +483,48 @@ console.log('\n── markdown-renderer: renderPlanFromDb creates parse-compatib
498
483
  });
499
484
 
500
485
  const rendered = await renderPlanFromDb(tmpDir, 'M001', 'S02');
501
- assertTrue(fs.existsSync(rendered.planPath), 'slice plan written to disk');
502
- assertEq(rendered.taskPlanPaths.length, 2, 'task plan paths returned for each task');
503
- assertTrue(rendered.taskPlanPaths.every((p) => fs.existsSync(p)), 'all task plan files written to disk');
486
+ assert.ok(fs.existsSync(rendered.planPath), 'slice plan written to disk');
487
+ assert.strictEqual(rendered.taskPlanPaths.length, 2, 'task plan paths returned for each task');
488
+ assert.ok(rendered.taskPlanPaths.every((p) => fs.existsSync(p)), 'all task plan files written to disk');
504
489
 
505
490
  const planContent = fs.readFileSync(rendered.planPath, 'utf-8');
506
491
  clearAllCaches();
507
492
  const parsedPlan = parsePlan(planContent);
508
- assertEq(parsedPlan.id, 'S02', 'rendered slice plan parses with correct slice id');
509
- assertEq(parsedPlan.goal, 'Render slice plans from DB state.', 'rendered slice plan preserves goal');
510
- assertEq(parsedPlan.demo, 'Rendered plans exist on disk.', 'rendered slice plan preserves demo');
511
- assertEq(parsedPlan.mustHaves.length, 2, 'rendered slice plan exposes must-haves');
512
- assertEq(parsedPlan.tasks.length, 2, 'rendered slice plan exposes all tasks');
513
- assertEq(parsedPlan.tasks[0].id, 'T01', 'first task parses correctly');
514
- assertTrue(parsedPlan.tasks[0].description.includes('DB-backed slice plan renderer'), 'task description preserved in slice plan');
515
- assertEq(parsedPlan.tasks[0].files?.[0], 'src/resources/extensions/gsd/markdown-renderer.ts', 'files list preserved in slice plan');
516
- assertEq(parsedPlan.tasks[0].verify, 'node --test markdown-renderer.test.ts', 'verify line preserved in slice plan');
493
+ assert.strictEqual(parsedPlan.id, 'S02', 'rendered slice plan parses with correct slice id');
494
+ assert.strictEqual(parsedPlan.goal, 'Render slice plans from DB state.', 'rendered slice plan preserves goal');
495
+ assert.strictEqual(parsedPlan.demo, 'Rendered plans exist on disk.', 'rendered slice plan preserves demo');
496
+ assert.strictEqual(parsedPlan.mustHaves.length, 2, 'rendered slice plan exposes must-haves');
497
+ assert.strictEqual(parsedPlan.tasks.length, 2, 'rendered slice plan exposes all tasks');
498
+ assert.strictEqual(parsedPlan.tasks[0].id, 'T01', 'first task parses correctly');
499
+ assert.ok(parsedPlan.tasks[0].description.includes('DB-backed slice plan renderer'), 'task description preserved in slice plan');
500
+ assert.strictEqual(parsedPlan.tasks[0].files?.[0], 'src/resources/extensions/gsd/markdown-renderer.ts', 'files list preserved in slice plan');
501
+ assert.strictEqual(parsedPlan.tasks[0].verify, 'node --test markdown-renderer.test.ts', 'verify line preserved in slice plan');
517
502
 
518
503
  const planArtifact = getArtifact('milestones/M001/slices/S02/S02-PLAN.md');
519
- assertTrue(planArtifact !== null, 'slice plan artifact stored in DB');
520
- assertTrue(planArtifact!.full_content.includes('## Tasks'), 'stored plan artifact contains task section');
504
+ assert.ok(planArtifact !== null, 'slice plan artifact stored in DB');
505
+ assert.ok(planArtifact!.full_content.includes('## Tasks'), 'stored plan artifact contains task section');
521
506
 
522
507
  const taskPlanPath = path.join(tmpDir, '.gsd', 'milestones', 'M001', 'slices', 'S02', 'tasks', 'T01-PLAN.md');
523
508
  const taskPlanContent = fs.readFileSync(taskPlanPath, 'utf-8');
524
509
  const taskPlanFile = parseTaskPlanFile(taskPlanContent);
525
- assertEq(taskPlanFile.frontmatter.estimated_steps, 1, 'task plan frontmatter exposes estimated_steps');
526
- assertEq(taskPlanFile.frontmatter.estimated_files, 1, 'task plan frontmatter exposes estimated_files');
527
- assertEq(taskPlanFile.frontmatter.skills_used.length, 0, 'task plan frontmatter uses conservative empty skills list');
528
- assertMatch(taskPlanContent, /^# T01: Render slice plan/m, 'task plan renders task heading');
529
- assertMatch(taskPlanContent, /^## Inputs$/m, 'task plan renders Inputs section');
530
- assertMatch(taskPlanContent, /^## Expected Output$/m, 'task plan renders Expected Output section');
531
- assertMatch(taskPlanContent, /^## Verification$/m, 'task plan renders Verification section');
510
+ assert.strictEqual(taskPlanFile.frontmatter.estimated_steps, 1, 'task plan frontmatter exposes estimated_steps');
511
+ assert.strictEqual(taskPlanFile.frontmatter.estimated_files, 1, 'task plan frontmatter exposes estimated_files');
512
+ assert.strictEqual(taskPlanFile.frontmatter.skills_used.length, 0, 'task plan frontmatter uses conservative empty skills list');
513
+ assert.match(taskPlanContent, /^# T01: Render slice plan/m, 'task plan renders task heading');
514
+ assert.match(taskPlanContent, /^## Inputs$/m, 'task plan renders Inputs section');
515
+ assert.match(taskPlanContent, /^## Expected Output$/m, 'task plan renders Expected Output section');
516
+ assert.match(taskPlanContent, /^## Verification$/m, 'task plan renders Verification section');
532
517
 
533
518
  const taskArtifact = getArtifact('milestones/M001/slices/S02/tasks/T01-PLAN.md');
534
- assertTrue(taskArtifact !== null, 'task plan artifact stored in DB');
535
- assertTrue(taskArtifact!.full_content.includes('skills_used: []'), 'stored task plan artifact preserves conservative skills_used');
519
+ assert.ok(taskArtifact !== null, 'task plan artifact stored in DB');
520
+ assert.ok(taskArtifact!.full_content.includes('skills_used: []'), 'stored task plan artifact preserves conservative skills_used');
536
521
  } finally {
537
522
  closeDatabase();
538
523
  cleanupDir(tmpDir);
539
524
  }
540
- }
541
-
542
- console.log('\n── markdown-renderer: renderTaskPlanFromDb throws for missing task ──');
525
+ });
543
526
 
544
- {
527
+ test('── markdown-renderer: renderTaskPlanFromDb throws for missing task ──', async () => {
545
528
  const tmpDir = makeTmpDir();
546
529
  const dbPath = path.join(tmpDir, '.gsd', 'gsd.db');
547
530
  openDatabase(dbPath);
@@ -557,23 +540,21 @@ console.log('\n── markdown-renderer: renderTaskPlanFromDb throws for missing
557
540
  await renderTaskPlanFromDb(tmpDir, 'M001', 'S02', 'T99');
558
541
  } catch (error) {
559
542
  threw = true;
560
- assertMatch(String((error as Error).message), /task M001\/S02\/T99 not found/, 'renderTaskPlanFromDb should fail clearly when task row is missing');
543
+ assert.match(String((error as Error).message), /task M001\/S02\/T99 not found/, 'renderTaskPlanFromDb should fail clearly when task row is missing');
561
544
  }
562
- assertTrue(threw, 'renderTaskPlanFromDb throws when the task row is missing');
545
+ assert.ok(threw, 'renderTaskPlanFromDb throws when the task row is missing');
563
546
  } finally {
564
547
  closeDatabase();
565
548
  cleanupDir(tmpDir);
566
549
  }
567
- }
550
+ });
568
551
 
569
552
 
570
553
  // ═══════════════════════════════════════════════════════════════════════════
571
554
  // Task Summary Rendering
572
555
  // ═══════════════════════════════════════════════════════════════════════════
573
556
 
574
- console.log('\n── markdown-renderer: renderTaskSummary round-trip ──');
575
-
576
- {
557
+ test('── markdown-renderer: renderTaskSummary round-trip ──', async () => {
577
558
  const tmpDir = makeTmpDir();
578
559
  const dbPath = path.join(tmpDir, '.gsd', 'gsd.db');
579
560
  openDatabase(dbPath);
@@ -596,33 +577,31 @@ console.log('\n── markdown-renderer: renderTaskSummary round-trip ──');
596
577
  });
597
578
 
598
579
  const ok = await renderTaskSummary(tmpDir, 'M001', 'S01', 'T01');
599
- assertTrue(ok, 'renderTaskSummary returns true');
580
+ assert.ok(ok, 'renderTaskSummary returns true');
600
581
 
601
582
  // Verify file exists on disk
602
583
  const summaryPath = path.join(
603
584
  tmpDir, '.gsd', 'milestones', 'M001', 'slices', 'S01', 'tasks', 'T01-SUMMARY.md',
604
585
  );
605
- assertTrue(fs.existsSync(summaryPath), 'T01-SUMMARY.md written to disk');
586
+ assert.ok(fs.existsSync(summaryPath), 'T01-SUMMARY.md written to disk');
606
587
 
607
588
  // Parse and verify
608
589
  const rendered = fs.readFileSync(summaryPath, 'utf-8');
609
590
  clearAllCaches();
610
591
  const parsed = parseSummary(rendered);
611
- assertEq(parsed.frontmatter.id, 'T01', 'parsed summary has correct id');
612
- assertEq(parsed.frontmatter.parent, 'S01', 'parsed summary has correct parent');
613
- assertEq(parsed.frontmatter.milestone, 'M001', 'parsed summary has correct milestone');
614
- assertEq(parsed.frontmatter.duration, '45m', 'parsed summary has correct duration');
615
- assertTrue(parsed.title.includes('T01'), 'parsed summary title contains task ID');
616
- assertTrue(parsed.whatHappened.includes('Built the test feature'), 'whatHappened content preserved');
592
+ assert.deepStrictEqual(parsed.frontmatter.id, 'T01', 'parsed summary has correct id');
593
+ assert.deepStrictEqual(parsed.frontmatter.parent, 'S01', 'parsed summary has correct parent');
594
+ assert.deepStrictEqual(parsed.frontmatter.milestone, 'M001', 'parsed summary has correct milestone');
595
+ assert.deepStrictEqual(parsed.frontmatter.duration, '45m', 'parsed summary has correct duration');
596
+ assert.ok(parsed.title.includes('T01'), 'parsed summary title contains task ID');
597
+ assert.ok(parsed.whatHappened.includes('Built the test feature'), 'whatHappened content preserved');
617
598
  } finally {
618
599
  closeDatabase();
619
600
  cleanupDir(tmpDir);
620
601
  }
621
- }
622
-
623
- console.log('\n── markdown-renderer: renderTaskSummary skips empty ──');
602
+ });
624
603
 
625
- {
604
+ test('── markdown-renderer: renderTaskSummary skips empty ──', async () => {
626
605
  const tmpDir = makeTmpDir();
627
606
  const dbPath = path.join(tmpDir, '.gsd', 'gsd.db');
628
607
  openDatabase(dbPath);
@@ -643,20 +622,18 @@ console.log('\n── markdown-renderer: renderTaskSummary skips empty ──');
643
622
  });
644
623
 
645
624
  const ok = await renderTaskSummary(tmpDir, 'M001', 'S01', 'T01');
646
- assertTrue(!ok, 'renderTaskSummary returns false for empty summary');
625
+ assert.ok(!ok, 'renderTaskSummary returns false for empty summary');
647
626
  } finally {
648
627
  closeDatabase();
649
628
  cleanupDir(tmpDir);
650
629
  }
651
- }
630
+ });
652
631
 
653
632
  // ═══════════════════════════════════════════════════════════════════════════
654
633
  // Slice Summary Rendering
655
634
  // ═══════════════════════════════════════════════════════════════════════════
656
635
 
657
- console.log('\n── markdown-renderer: renderSliceSummary round-trip ──');
658
-
659
- {
636
+ test('── markdown-renderer: renderSliceSummary round-trip ──', async () => {
660
637
  const tmpDir = makeTmpDir();
661
638
  const dbPath = path.join(tmpDir, '.gsd', 'gsd.db');
662
639
  openDatabase(dbPath);
@@ -680,38 +657,36 @@ console.log('\n── markdown-renderer: renderSliceSummary round-trip ──');
680
657
  });
681
658
 
682
659
  const ok = await renderSliceSummary(tmpDir, 'M001', 'S01');
683
- assertTrue(ok, 'renderSliceSummary returns true');
660
+ assert.ok(ok, 'renderSliceSummary returns true');
684
661
 
685
662
  // Verify SUMMARY file
686
663
  const summaryPath = path.join(
687
664
  tmpDir, '.gsd', 'milestones', 'M001', 'slices', 'S01', 'S01-SUMMARY.md',
688
665
  );
689
- assertTrue(fs.existsSync(summaryPath), 'S01-SUMMARY.md written to disk');
666
+ assert.ok(fs.existsSync(summaryPath), 'S01-SUMMARY.md written to disk');
690
667
 
691
668
  const summaryContent = fs.readFileSync(summaryPath, 'utf-8');
692
- assertTrue(summaryContent.includes('Test Slice Summary'), 'summary content correct');
669
+ assert.ok(summaryContent.includes('Test Slice Summary'), 'summary content correct');
693
670
 
694
671
  // Verify UAT file
695
672
  const uatPath = path.join(
696
673
  tmpDir, '.gsd', 'milestones', 'M001', 'slices', 'S01', 'S01-UAT.md',
697
674
  );
698
- assertTrue(fs.existsSync(uatPath), 'S01-UAT.md written to disk');
675
+ assert.ok(fs.existsSync(uatPath), 'S01-UAT.md written to disk');
699
676
 
700
677
  const uatContent = fs.readFileSync(uatPath, 'utf-8');
701
- assertTrue(uatContent.includes('artifact-driven'), 'UAT content correct');
678
+ assert.ok(uatContent.includes('artifact-driven'), 'UAT content correct');
702
679
  } finally {
703
680
  closeDatabase();
704
681
  cleanupDir(tmpDir);
705
682
  }
706
- }
683
+ });
707
684
 
708
685
  // ═══════════════════════════════════════════════════════════════════════════
709
686
  // renderAllFromDb
710
687
  // ═══════════════════════════════════════════════════════════════════════════
711
688
 
712
- console.log('\n── markdown-renderer: renderAllFromDb produces all files ──');
713
-
714
- {
689
+ test('── markdown-renderer: renderAllFromDb produces all files ──', async () => {
715
690
  const tmpDir = makeTmpDir();
716
691
  const dbPath = path.join(tmpDir, '.gsd', 'gsd.db');
717
692
  openDatabase(dbPath);
@@ -779,8 +754,8 @@ console.log('\n── markdown-renderer: renderAllFromDb produces all files ─
779
754
 
780
755
  const result = await renderAllFromDb(tmpDir);
781
756
 
782
- assertTrue(result.rendered > 0, 'renderAllFromDb rendered some files');
783
- assertEq(result.errors.length, 0, 'renderAllFromDb had no errors');
757
+ assert.ok(result.rendered > 0, 'renderAllFromDb rendered some files');
758
+ assert.deepStrictEqual(result.errors.length, 0, 'renderAllFromDb had no errors');
784
759
 
785
760
  // Verify M001 roadmap has S01 checked
786
761
  const m1Roadmap = fs.readFileSync(
@@ -789,7 +764,7 @@ console.log('\n── markdown-renderer: renderAllFromDb produces all files ─
789
764
  clearAllCaches();
790
765
  const parsed1 = parseRoadmap(m1Roadmap);
791
766
  const s01 = parsed1.slices.find(s => s.id === 'S01');
792
- assertTrue(s01!.done, 'M001 S01 checked after renderAll');
767
+ assert.ok(s01!.done, 'M001 S01 checked after renderAll');
793
768
 
794
769
  // Verify M001/S01 plan has T01 checked
795
770
  const m1s1Plan = fs.readFileSync(
@@ -797,26 +772,24 @@ console.log('\n── markdown-renderer: renderAllFromDb produces all files ─
797
772
  );
798
773
  clearAllCaches();
799
774
  const parsedPlan = parsePlan(m1s1Plan);
800
- assertTrue(parsedPlan.tasks[0].done, 'M001/S01 T01 checked after renderAll');
775
+ assert.ok(parsedPlan.tasks[0].done, 'M001/S01 T01 checked after renderAll');
801
776
 
802
777
  // Verify task summary written
803
778
  const taskSummaryPath = path.join(
804
779
  tmpDir, '.gsd', 'milestones', 'M001', 'slices', 'S01', 'tasks', 'T01-SUMMARY.md',
805
780
  );
806
- assertTrue(fs.existsSync(taskSummaryPath), 'T01 summary written by renderAll');
781
+ assert.ok(fs.existsSync(taskSummaryPath), 'T01 summary written by renderAll');
807
782
  } finally {
808
783
  closeDatabase();
809
784
  cleanupDir(tmpDir);
810
785
  }
811
- }
786
+ });
812
787
 
813
788
  // ═══════════════════════════════════════════════════════════════════════════
814
789
  // Graceful Degradation (Disk Fallback)
815
790
  // ═══════════════════════════════════════════════════════════════════════════
816
791
 
817
- console.log('\n── markdown-renderer: graceful fallback reads from disk when artifact not in DB ──');
818
-
819
- {
792
+ test('── markdown-renderer: graceful fallback reads from disk when artifact not in DB ──', async () => {
820
793
  const tmpDir = makeTmpDir();
821
794
  const dbPath = path.join(tmpDir, '.gsd', 'gsd.db');
822
795
  openDatabase(dbPath);
@@ -838,29 +811,27 @@ console.log('\n── markdown-renderer: graceful fallback reads from disk when
838
811
 
839
812
  // Verify no artifact in DB
840
813
  const before = getArtifact('milestones/M001/M001-ROADMAP.md');
841
- assertEq(before, null, 'artifact not in DB before render');
814
+ assert.deepStrictEqual(before, null, 'artifact not in DB before render');
842
815
 
843
816
  // Render — should read from disk, store in DB
844
817
  const ok = await renderRoadmapCheckboxes(tmpDir, 'M001');
845
- assertTrue(ok, 'render succeeds with disk fallback');
818
+ assert.ok(ok, 'render succeeds with disk fallback');
846
819
 
847
820
  // Verify artifact now in DB (stored after reading from disk)
848
821
  const after = getArtifact('milestones/M001/M001-ROADMAP.md');
849
- assertTrue(after !== null, 'artifact stored in DB after disk fallback render');
850
- assertTrue(after!.full_content.includes('[x] **S01:'), 'DB artifact reflects rendered state');
822
+ assert.ok(after !== null, 'artifact stored in DB after disk fallback render');
823
+ assert.ok(after!.full_content.includes('[x] **S01:'), 'DB artifact reflects rendered state');
851
824
  } finally {
852
825
  closeDatabase();
853
826
  cleanupDir(tmpDir);
854
827
  }
855
- }
828
+ });
856
829
 
857
830
  // ═══════════════════════════════════════════════════════════════════════════
858
831
  // stderr warnings (graceful degradation diagnostics)
859
832
  // ═══════════════════════════════════════════════════════════════════════════
860
833
 
861
- console.log('\n── markdown-renderer: stderr warning on missing content ──');
862
-
863
- {
834
+ test('── markdown-renderer: stderr warning on missing content ──', async () => {
864
835
  openDatabase(':memory:');
865
836
 
866
837
  // No milestone/slices in DB, no files on disk — should return false and emit stderr
@@ -868,18 +839,16 @@ console.log('\n── markdown-renderer: stderr warning on missing content ─
868
839
  // No slices inserted — should warn about no slices
869
840
 
870
841
  const ok = await renderRoadmapCheckboxes('/nonexistent/path', 'M001');
871
- assertTrue(!ok, 'returns false when no slices in DB');
842
+ assert.ok(!ok, 'returns false when no slices in DB');
872
843
 
873
844
  closeDatabase();
874
- }
845
+ });
875
846
 
876
847
  // ═══════════════════════════════════════════════════════════════════════════
877
848
  // Stale Detection — Plan Checkbox Mismatch
878
849
  // ═══════════════════════════════════════════════════════════════════════════
879
850
 
880
- console.log('\n── markdown-renderer: detectStaleRenders finds plan checkbox mismatch ──');
881
-
882
- {
851
+ test('── markdown-renderer: detectStaleRenders finds plan checkbox mismatch ──', () => {
883
852
  const tmpDir = makeTmpDir();
884
853
  const dbPath = path.join(tmpDir, '.gsd', 'gsd.db');
885
854
  openDatabase(dbPath);
@@ -910,27 +879,25 @@ console.log('\n── markdown-renderer: detectStaleRenders finds plan checkbox
910
879
  // The stale detection should find T02 as stale.
911
880
  const stale = detectStaleRenders(tmpDir);
912
881
 
913
- assertTrue(stale.length > 0, 'detectStaleRenders should find stale entries');
882
+ assert.ok(stale.length > 0, 'detectStaleRenders should find stale entries');
914
883
  const t02Stale = stale.find(s => s.reason.includes('T02'));
915
- assertTrue(!!t02Stale, 'should detect T02 as stale (done in DB, unchecked in plan)');
916
- assertTrue(t02Stale!.reason.includes('done in DB but unchecked'), 'reason should explain the mismatch');
884
+ assert.ok(!!t02Stale, 'should detect T02 as stale (done in DB, unchecked in plan)');
885
+ assert.ok(t02Stale!.reason.includes('done in DB but unchecked'), 'reason should explain the mismatch');
917
886
 
918
887
  // T01 should NOT be stale — it's checked and done
919
888
  const t01Stale = stale.find(s => s.reason.includes('T01'));
920
- assertEq(t01Stale, undefined, 'T01 should not be stale (done and checked)');
889
+ assert.deepStrictEqual(t01Stale, undefined, 'T01 should not be stale (done and checked)');
921
890
  } finally {
922
891
  closeDatabase();
923
892
  cleanupDir(tmpDir);
924
893
  }
925
- }
894
+ });
926
895
 
927
896
  // ═══════════════════════════════════════════════════════════════════════════
928
897
  // Stale Repair — Plan Checkbox
929
898
  // ═══════════════════════════════════════════════════════════════════════════
930
899
 
931
- console.log('\n── markdown-renderer: repairStaleRenders fixes plan and second detect returns empty ──');
932
-
933
- {
900
+ test('── markdown-renderer: repairStaleRenders fixes plan and second detect returns empty ──', async () => {
934
901
  const tmpDir = makeTmpDir();
935
902
  const dbPath = path.join(tmpDir, '.gsd', 'gsd.db');
936
903
  openDatabase(dbPath);
@@ -956,34 +923,32 @@ console.log('\n── markdown-renderer: repairStaleRenders fixes plan and secon
956
923
 
957
924
  // Verify stale before repair
958
925
  const staleBefore = detectStaleRenders(tmpDir);
959
- assertTrue(staleBefore.length > 0, 'should have stale entries before repair');
926
+ assert.ok(staleBefore.length > 0, 'should have stale entries before repair');
960
927
 
961
928
  // Repair
962
929
  const repaired = await repairStaleRenders(tmpDir);
963
- assertTrue(repaired > 0, 'repairStaleRenders should repair at least 1 file');
930
+ assert.ok(repaired > 0, 'repairStaleRenders should repair at least 1 file');
964
931
 
965
932
  // After repair, detect again — should be empty
966
933
  clearAllCaches();
967
934
  const staleAfter = detectStaleRenders(tmpDir);
968
- assertEq(staleAfter.length, 0, 'detectStaleRenders should return empty after repair');
935
+ assert.deepStrictEqual(staleAfter.length, 0, 'detectStaleRenders should return empty after repair');
969
936
 
970
937
  // Verify the plan file was actually updated
971
938
  const repairedContent = fs.readFileSync(planPath, 'utf-8');
972
- assertTrue(repairedContent.includes('[x] **T01:'), 'T01 should be checked after repair');
973
- assertTrue(repairedContent.includes('[x] **T02:'), 'T02 should be checked after repair');
939
+ assert.ok(repairedContent.includes('[x] **T01:'), 'T01 should be checked after repair');
940
+ assert.ok(repairedContent.includes('[x] **T02:'), 'T02 should be checked after repair');
974
941
  } finally {
975
942
  closeDatabase();
976
943
  cleanupDir(tmpDir);
977
944
  }
978
- }
945
+ });
979
946
 
980
947
  // ═══════════════════════════════════════════════════════════════════════════
981
948
  // Stale Detection — Roadmap Checkbox Mismatch
982
949
  // ═══════════════════════════════════════════════════════════════════════════
983
950
 
984
- console.log('\n── markdown-renderer: detectStaleRenders finds roadmap checkbox mismatch ──');
985
-
986
- {
951
+ test('── markdown-renderer: detectStaleRenders finds roadmap checkbox mismatch ──', () => {
987
952
  const tmpDir = makeTmpDir();
988
953
  const dbPath = path.join(tmpDir, '.gsd', 'gsd.db');
989
954
  openDatabase(dbPath);
@@ -1007,23 +972,21 @@ console.log('\n── markdown-renderer: detectStaleRenders finds roadmap checkb
1007
972
 
1008
973
  const stale = detectStaleRenders(tmpDir);
1009
974
  const s01Stale = stale.find(s => s.reason.includes('S01'));
1010
- assertTrue(!!s01Stale, 'should detect S01 as stale (complete in DB, unchecked in roadmap)');
975
+ assert.ok(!!s01Stale, 'should detect S01 as stale (complete in DB, unchecked in roadmap)');
1011
976
 
1012
977
  const s02Stale = stale.find(s => s.reason.includes('S02'));
1013
- assertEq(s02Stale, undefined, 'S02 should not be stale (pending and unchecked — matches)');
978
+ assert.deepStrictEqual(s02Stale, undefined, 'S02 should not be stale (pending and unchecked — matches)');
1014
979
  } finally {
1015
980
  closeDatabase();
1016
981
  cleanupDir(tmpDir);
1017
982
  }
1018
- }
983
+ });
1019
984
 
1020
985
  // ═══════════════════════════════════════════════════════════════════════════
1021
986
  // Stale Detection — Missing Task Summary
1022
987
  // ═══════════════════════════════════════════════════════════════════════════
1023
988
 
1024
- console.log('\n── markdown-renderer: detectStaleRenders finds missing task summary ──');
1025
-
1026
- {
989
+ test('── markdown-renderer: detectStaleRenders finds missing task summary ──', () => {
1027
990
  const tmpDir = makeTmpDir();
1028
991
  const dbPath = path.join(tmpDir, '.gsd', 'gsd.db');
1029
992
  openDatabase(dbPath);
@@ -1058,21 +1021,19 @@ console.log('\n── markdown-renderer: detectStaleRenders finds missing task s
1058
1021
 
1059
1022
  const stale = detectStaleRenders(tmpDir);
1060
1023
  const summaryStale = stale.find(s => s.reason.includes('SUMMARY.md missing'));
1061
- assertTrue(!!summaryStale, 'should detect missing T01-SUMMARY.md');
1062
- assertTrue(summaryStale!.reason.includes('T01'), 'reason should mention T01');
1024
+ assert.ok(!!summaryStale, 'should detect missing T01-SUMMARY.md');
1025
+ assert.ok(summaryStale!.reason.includes('T01'), 'reason should mention T01');
1063
1026
  } finally {
1064
1027
  closeDatabase();
1065
1028
  cleanupDir(tmpDir);
1066
1029
  }
1067
- }
1030
+ });
1068
1031
 
1069
1032
  // ═══════════════════════════════════════════════════════════════════════════
1070
1033
  // Stale Repair — Missing Task Summary
1071
1034
  // ═══════════════════════════════════════════════════════════════════════════
1072
1035
 
1073
- console.log('\n── markdown-renderer: repairStaleRenders writes missing task summary ──');
1074
-
1075
- {
1036
+ test('── markdown-renderer: repairStaleRenders writes missing task summary ──', async () => {
1076
1037
  const tmpDir = makeTmpDir();
1077
1038
  const dbPath = path.join(tmpDir, '.gsd', 'gsd.db');
1078
1039
  openDatabase(dbPath);
@@ -1104,32 +1065,30 @@ console.log('\n── markdown-renderer: repairStaleRenders writes missing task
1104
1065
 
1105
1066
  // Repair
1106
1067
  const repaired = await repairStaleRenders(tmpDir);
1107
- assertTrue(repaired > 0, 'should repair missing summary');
1068
+ assert.ok(repaired > 0, 'should repair missing summary');
1108
1069
 
1109
1070
  // Verify file written
1110
1071
  const summaryPath = path.join(
1111
1072
  tmpDir, '.gsd', 'milestones', 'M001', 'slices', 'S01', 'tasks', 'T01-SUMMARY.md',
1112
1073
  );
1113
- assertTrue(fs.existsSync(summaryPath), 'T01-SUMMARY.md should exist after repair');
1074
+ assert.ok(fs.existsSync(summaryPath), 'T01-SUMMARY.md should exist after repair');
1114
1075
 
1115
1076
  // Second detect should be empty
1116
1077
  clearAllCaches();
1117
1078
  const staleAfter = detectStaleRenders(tmpDir);
1118
1079
  const summaryStale = staleAfter.find(s => s.reason.includes('SUMMARY.md missing') && s.reason.includes('T01'));
1119
- assertEq(summaryStale, undefined, 'missing summary should be fixed after repair');
1080
+ assert.deepStrictEqual(summaryStale, undefined, 'missing summary should be fixed after repair');
1120
1081
  } finally {
1121
1082
  closeDatabase();
1122
1083
  cleanupDir(tmpDir);
1123
1084
  }
1124
- }
1085
+ });
1125
1086
 
1126
1087
  // ═══════════════════════════════════════════════════════════════════════════
1127
1088
  // Stale Repair — Idempotency
1128
1089
  // ═══════════════════════════════════════════════════════════════════════════
1129
1090
 
1130
- console.log('\n── markdown-renderer: repairStaleRenders idempotency — fully synced returns 0 ──');
1131
-
1132
- {
1091
+ test('── markdown-renderer: repairStaleRenders idempotency — fully synced returns 0 ──', async () => {
1133
1092
  const tmpDir = makeTmpDir();
1134
1093
  const dbPath = path.join(tmpDir, '.gsd', 'gsd.db');
1135
1094
  openDatabase(dbPath);
@@ -1152,20 +1111,18 @@ console.log('\n── markdown-renderer: repairStaleRenders idempotency — full
1152
1111
 
1153
1112
  // No stale entries when everything is in sync (no summary to check since no fullSummaryMd)
1154
1113
  const repaired = await repairStaleRenders(tmpDir);
1155
- assertEq(repaired, 0, 'repairStaleRenders should return 0 on fully synced project');
1114
+ assert.deepStrictEqual(repaired, 0, 'repairStaleRenders should return 0 on fully synced project');
1156
1115
  } finally {
1157
1116
  closeDatabase();
1158
1117
  cleanupDir(tmpDir);
1159
1118
  }
1160
- }
1119
+ });
1161
1120
 
1162
1121
  // ═══════════════════════════════════════════════════════════════════════════
1163
1122
  // Stale Detection — Missing Slice Summary + UAT
1164
1123
  // ═══════════════════════════════════════════════════════════════════════════
1165
1124
 
1166
- console.log('\n── markdown-renderer: detectStaleRenders finds missing slice summary and UAT ──');
1167
-
1168
- {
1125
+ test('── markdown-renderer: detectStaleRenders finds missing slice summary and UAT ──', () => {
1169
1126
  const tmpDir = makeTmpDir();
1170
1127
  const dbPath = path.join(tmpDir, '.gsd', 'gsd.db');
1171
1128
  openDatabase(dbPath);
@@ -1192,14 +1149,13 @@ console.log('\n── markdown-renderer: detectStaleRenders finds missing slice
1192
1149
  const summaryStale = stale.find(s => s.reason.includes('SUMMARY.md missing') && s.reason.includes('S01'));
1193
1150
  const uatStale = stale.find(s => s.reason.includes('UAT.md missing') && s.reason.includes('S01'));
1194
1151
 
1195
- assertTrue(!!summaryStale, 'should detect missing S01-SUMMARY.md');
1196
- assertTrue(!!uatStale, 'should detect missing S01-UAT.md');
1152
+ assert.ok(!!summaryStale, 'should detect missing S01-SUMMARY.md');
1153
+ assert.ok(!!uatStale, 'should detect missing S01-UAT.md');
1197
1154
  } finally {
1198
1155
  closeDatabase();
1199
1156
  cleanupDir(tmpDir);
1200
1157
  }
1201
- }
1158
+ });
1202
1159
 
1203
1160
  // ═══════════════════════════════════════════════════════════════════════════
1204
1161
 
1205
- report();