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,3 +1,5 @@
1
+ import { describe, test } from 'node:test';
2
+ import assert from 'node:assert/strict';
1
3
  import { mkdtempSync, mkdirSync, rmSync, writeFileSync } from 'node:fs';
2
4
  import { join } from 'node:path';
3
5
  import { tmpdir } from 'node:os';
@@ -12,10 +14,6 @@ import {
12
14
  insertSlice,
13
15
  insertTask,
14
16
  } from '../gsd-db.ts';
15
- import { createTestContext } from './test-helpers.ts';
16
-
17
- const { assertEq, assertTrue, report } = createTestContext();
18
-
19
17
  // ─── Fixture Helpers ───────────────────────────────────────────────────────
20
18
 
21
19
  function createFixtureBase(): string {
@@ -100,11 +98,10 @@ const REQUIREMENTS_CONTENT = `# Requirements
100
98
  - Description: Already validated.
101
99
  `;
102
100
 
103
- async function main(): Promise<void> {
101
+ describe('derive-state-db', async () => {
104
102
 
105
103
  // ─── Test 1: DB-backed deriveState produces identical GSDState ─────────
106
- console.log('\n=== derive-state-db: DB path matches file path ===');
107
- {
104
+ test('derive-state-db: DB path matches file path', async () => {
108
105
  const base = createFixtureBase();
109
106
  try {
110
107
  // Write files to disk (for file-only path)
@@ -120,7 +117,7 @@ async function main(): Promise<void> {
120
117
 
121
118
  // Now open DB, insert matching artifacts
122
119
  openDatabase(':memory:');
123
- assertTrue(isDbAvailable(), 'db-match: DB is available after open');
120
+ assert.ok(isDbAvailable(), 'db-match: DB is available after open');
124
121
 
125
122
  insertArtifactRow('milestones/M001/M001-ROADMAP.md', ROADMAP_CONTENT, {
126
123
  artifact_type: 'roadmap',
@@ -140,36 +137,35 @@ async function main(): Promise<void> {
140
137
  const dbState = await deriveState(base);
141
138
 
142
139
  // Field-by-field equality
143
- assertEq(dbState.phase, fileState.phase, 'db-match: phase matches');
144
- assertEq(dbState.activeMilestone?.id, fileState.activeMilestone?.id, 'db-match: activeMilestone.id matches');
145
- assertEq(dbState.activeMilestone?.title, fileState.activeMilestone?.title, 'db-match: activeMilestone.title matches');
146
- assertEq(dbState.activeSlice?.id, fileState.activeSlice?.id, 'db-match: activeSlice.id matches');
147
- assertEq(dbState.activeSlice?.title, fileState.activeSlice?.title, 'db-match: activeSlice.title matches');
148
- assertEq(dbState.activeTask?.id, fileState.activeTask?.id, 'db-match: activeTask.id matches');
149
- assertEq(dbState.activeTask?.title, fileState.activeTask?.title, 'db-match: activeTask.title matches');
150
- assertEq(dbState.blockers, fileState.blockers, 'db-match: blockers match');
151
- assertEq(dbState.registry.length, fileState.registry.length, 'db-match: registry length matches');
152
- assertEq(dbState.registry[0]?.status, fileState.registry[0]?.status, 'db-match: registry[0] status matches');
153
- assertEq(dbState.requirements?.active, fileState.requirements?.active, 'db-match: requirements.active matches');
154
- assertEq(dbState.requirements?.validated, fileState.requirements?.validated, 'db-match: requirements.validated matches');
155
- assertEq(dbState.requirements?.total, fileState.requirements?.total, 'db-match: requirements.total matches');
156
- assertEq(dbState.progress?.milestones?.done, fileState.progress?.milestones?.done, 'db-match: milestones.done matches');
157
- assertEq(dbState.progress?.milestones?.total, fileState.progress?.milestones?.total, 'db-match: milestones.total matches');
158
- assertEq(dbState.progress?.slices?.done, fileState.progress?.slices?.done, 'db-match: slices.done matches');
159
- assertEq(dbState.progress?.slices?.total, fileState.progress?.slices?.total, 'db-match: slices.total matches');
160
- assertEq(dbState.progress?.tasks?.done, fileState.progress?.tasks?.done, 'db-match: tasks.done matches');
161
- assertEq(dbState.progress?.tasks?.total, fileState.progress?.tasks?.total, 'db-match: tasks.total matches');
140
+ assert.deepStrictEqual(dbState.phase, fileState.phase, 'db-match: phase matches');
141
+ assert.deepStrictEqual(dbState.activeMilestone?.id, fileState.activeMilestone?.id, 'db-match: activeMilestone.id matches');
142
+ assert.deepStrictEqual(dbState.activeMilestone?.title, fileState.activeMilestone?.title, 'db-match: activeMilestone.title matches');
143
+ assert.deepStrictEqual(dbState.activeSlice?.id, fileState.activeSlice?.id, 'db-match: activeSlice.id matches');
144
+ assert.deepStrictEqual(dbState.activeSlice?.title, fileState.activeSlice?.title, 'db-match: activeSlice.title matches');
145
+ assert.deepStrictEqual(dbState.activeTask?.id, fileState.activeTask?.id, 'db-match: activeTask.id matches');
146
+ assert.deepStrictEqual(dbState.activeTask?.title, fileState.activeTask?.title, 'db-match: activeTask.title matches');
147
+ assert.deepStrictEqual(dbState.blockers, fileState.blockers, 'db-match: blockers match');
148
+ assert.deepStrictEqual(dbState.registry.length, fileState.registry.length, 'db-match: registry length matches');
149
+ assert.deepStrictEqual(dbState.registry[0]?.status, fileState.registry[0]?.status, 'db-match: registry[0] status matches');
150
+ assert.deepStrictEqual(dbState.requirements?.active, fileState.requirements?.active, 'db-match: requirements.active matches');
151
+ assert.deepStrictEqual(dbState.requirements?.validated, fileState.requirements?.validated, 'db-match: requirements.validated matches');
152
+ assert.deepStrictEqual(dbState.requirements?.total, fileState.requirements?.total, 'db-match: requirements.total matches');
153
+ assert.deepStrictEqual(dbState.progress?.milestones?.done, fileState.progress?.milestones?.done, 'db-match: milestones.done matches');
154
+ assert.deepStrictEqual(dbState.progress?.milestones?.total, fileState.progress?.milestones?.total, 'db-match: milestones.total matches');
155
+ assert.deepStrictEqual(dbState.progress?.slices?.done, fileState.progress?.slices?.done, 'db-match: slices.done matches');
156
+ assert.deepStrictEqual(dbState.progress?.slices?.total, fileState.progress?.slices?.total, 'db-match: slices.total matches');
157
+ assert.deepStrictEqual(dbState.progress?.tasks?.done, fileState.progress?.tasks?.done, 'db-match: tasks.done matches');
158
+ assert.deepStrictEqual(dbState.progress?.tasks?.total, fileState.progress?.tasks?.total, 'db-match: tasks.total matches');
162
159
 
163
160
  closeDatabase();
164
161
  } finally {
165
162
  closeDatabase();
166
163
  cleanup(base);
167
164
  }
168
- }
165
+ });
169
166
 
170
167
  // ─── Test 2: Fallback when DB unavailable ─────────────────────────────
171
- console.log('\n=== derive-state-db: fallback when DB unavailable ===');
172
- {
168
+ test('derive-state-db: fallback when DB unavailable', async () => {
173
169
  const base = createFixtureBase();
174
170
  try {
175
171
  writeFile(base, 'milestones/M001/M001-ROADMAP.md', ROADMAP_CONTENT);
@@ -178,22 +174,21 @@ async function main(): Promise<void> {
178
174
  writeFile(base, 'milestones/M001/slices/S01/tasks/T01-PLAN.md', '# T01 Plan');
179
175
 
180
176
  // No DB open — isDbAvailable() is false
181
- assertTrue(!isDbAvailable(), 'fallback: DB is not available');
177
+ assert.ok(!isDbAvailable(), 'fallback: DB is not available');
182
178
  invalidateStateCache();
183
179
  const state = await deriveState(base);
184
180
 
185
- assertEq(state.phase, 'executing', 'fallback: phase is executing');
186
- assertEq(state.activeMilestone?.id, 'M001', 'fallback: activeMilestone is M001');
187
- assertEq(state.activeSlice?.id, 'S01', 'fallback: activeSlice is S01');
188
- assertEq(state.activeTask?.id, 'T01', 'fallback: activeTask is T01');
181
+ assert.deepStrictEqual(state.phase, 'executing', 'fallback: phase is executing');
182
+ assert.deepStrictEqual(state.activeMilestone?.id, 'M001', 'fallback: activeMilestone is M001');
183
+ assert.deepStrictEqual(state.activeSlice?.id, 'S01', 'fallback: activeSlice is S01');
184
+ assert.deepStrictEqual(state.activeTask?.id, 'T01', 'fallback: activeTask is T01');
189
185
  } finally {
190
186
  cleanup(base);
191
187
  }
192
- }
188
+ });
193
189
 
194
190
  // ─── Test 3: Empty DB falls back to file reads ────────────────────────
195
- console.log('\n=== derive-state-db: empty DB falls back to files ===');
196
- {
191
+ test('derive-state-db: empty DB falls back to files', async () => {
197
192
  const base = createFixtureBase();
198
193
  try {
199
194
  writeFile(base, 'milestones/M001/M001-ROADMAP.md', ROADMAP_CONTENT);
@@ -203,27 +198,26 @@ async function main(): Promise<void> {
203
198
 
204
199
  // Open DB but insert nothing — empty artifacts table
205
200
  openDatabase(':memory:');
206
- assertTrue(isDbAvailable(), 'empty-db: DB is available');
201
+ assert.ok(isDbAvailable(), 'empty-db: DB is available');
207
202
 
208
203
  invalidateStateCache();
209
204
  const state = await deriveState(base);
210
205
 
211
206
  // Should still work via cachedLoadFile → loadFile disk fallback
212
- assertEq(state.phase, 'executing', 'empty-db: phase is executing');
213
- assertEq(state.activeMilestone?.id, 'M001', 'empty-db: activeMilestone is M001');
214
- assertEq(state.activeSlice?.id, 'S01', 'empty-db: activeSlice is S01');
215
- assertEq(state.activeTask?.id, 'T01', 'empty-db: activeTask is T01');
207
+ assert.deepStrictEqual(state.phase, 'executing', 'empty-db: phase is executing');
208
+ assert.deepStrictEqual(state.activeMilestone?.id, 'M001', 'empty-db: activeMilestone is M001');
209
+ assert.deepStrictEqual(state.activeSlice?.id, 'S01', 'empty-db: activeSlice is S01');
210
+ assert.deepStrictEqual(state.activeTask?.id, 'T01', 'empty-db: activeTask is T01');
216
211
 
217
212
  closeDatabase();
218
213
  } finally {
219
214
  closeDatabase();
220
215
  cleanup(base);
221
216
  }
222
- }
217
+ });
223
218
 
224
219
  // ─── Test 4: Partial DB content fills gaps from disk ──────────────────
225
- console.log('\n=== derive-state-db: partial DB fills gaps from disk ===');
226
- {
220
+ test('derive-state-db: partial DB fills gaps from disk', async () => {
227
221
  const base = createFixtureBase();
228
222
  try {
229
223
  // Write all files to disk
@@ -244,25 +238,24 @@ async function main(): Promise<void> {
244
238
  const state = await deriveState(base);
245
239
 
246
240
  // Should work: roadmap from DB, plan from disk fallback
247
- assertEq(state.phase, 'executing', 'partial-db: phase is executing');
248
- assertEq(state.activeMilestone?.id, 'M001', 'partial-db: activeMilestone is M001');
249
- assertEq(state.activeSlice?.id, 'S01', 'partial-db: activeSlice is S01');
250
- assertEq(state.activeTask?.id, 'T01', 'partial-db: activeTask is T01');
241
+ assert.deepStrictEqual(state.phase, 'executing', 'partial-db: phase is executing');
242
+ assert.deepStrictEqual(state.activeMilestone?.id, 'M001', 'partial-db: activeMilestone is M001');
243
+ assert.deepStrictEqual(state.activeSlice?.id, 'S01', 'partial-db: activeSlice is S01');
244
+ assert.deepStrictEqual(state.activeTask?.id, 'T01', 'partial-db: activeTask is T01');
251
245
  // Requirements loaded from disk fallback
252
- assertEq(state.requirements?.active, 2, 'partial-db: requirements.active from disk');
253
- assertEq(state.requirements?.validated, 1, 'partial-db: requirements.validated from disk');
254
- assertEq(state.requirements?.total, 3, 'partial-db: requirements.total from disk');
246
+ assert.deepStrictEqual(state.requirements?.active, 2, 'partial-db: requirements.active from disk');
247
+ assert.deepStrictEqual(state.requirements?.validated, 1, 'partial-db: requirements.validated from disk');
248
+ assert.deepStrictEqual(state.requirements?.total, 3, 'partial-db: requirements.total from disk');
255
249
 
256
250
  closeDatabase();
257
251
  } finally {
258
252
  closeDatabase();
259
253
  cleanup(base);
260
254
  }
261
- }
255
+ });
262
256
 
263
257
  // ─── Test 5: Requirements counting from disk (DB no longer used for content) ─
264
- console.log('\n=== derive-state-db: requirements from disk content ===');
265
- {
258
+ test('derive-state-db: requirements from disk content', async () => {
266
259
  const base = createFixtureBase();
267
260
  try {
268
261
  // Write minimal milestone dir (needed for milestone discovery)
@@ -274,17 +267,16 @@ async function main(): Promise<void> {
274
267
  const state = await deriveState(base);
275
268
 
276
269
  // Requirements should come from disk
277
- assertEq(state.requirements?.active, 2, 'req-from-disk: requirements.active = 2');
278
- assertEq(state.requirements?.validated, 1, 'req-from-disk: requirements.validated = 1');
279
- assertEq(state.requirements?.total, 3, 'req-from-disk: requirements.total = 3');
270
+ assert.deepStrictEqual(state.requirements?.active, 2, 'req-from-disk: requirements.active = 2');
271
+ assert.deepStrictEqual(state.requirements?.validated, 1, 'req-from-disk: requirements.validated = 1');
272
+ assert.deepStrictEqual(state.requirements?.total, 3, 'req-from-disk: requirements.total = 3');
280
273
  } finally {
281
274
  cleanup(base);
282
275
  }
283
- }
276
+ });
284
277
 
285
278
  // ─── Test 6: DB content with multi-milestone registry ─────────────────
286
- console.log('\n=== derive-state-db: multi-milestone from DB ===');
287
- {
279
+ test('derive-state-db: multi-milestone from DB', async () => {
288
280
  const base = createFixtureBase();
289
281
 
290
282
  const completedRoadmap = `# M001: First Milestone
@@ -337,24 +329,23 @@ async function main(): Promise<void> {
337
329
  invalidateStateCache();
338
330
  const state = await deriveState(base);
339
331
 
340
- assertEq(state.registry.length, 2, 'multi-ms-db: registry has 2 entries');
341
- assertEq(state.registry[0]?.id, 'M001', 'multi-ms-db: registry[0] is M001');
342
- assertEq(state.registry[0]?.status, 'complete', 'multi-ms-db: M001 is complete');
343
- assertEq(state.registry[1]?.id, 'M002', 'multi-ms-db: registry[1] is M002');
344
- assertEq(state.registry[1]?.status, 'active', 'multi-ms-db: M002 is active');
345
- assertEq(state.activeMilestone?.id, 'M002', 'multi-ms-db: activeMilestone is M002');
346
- assertEq(state.phase, 'planning', 'multi-ms-db: phase is planning (no plan for S01)');
332
+ assert.deepStrictEqual(state.registry.length, 2, 'multi-ms-db: registry has 2 entries');
333
+ assert.deepStrictEqual(state.registry[0]?.id, 'M001', 'multi-ms-db: registry[0] is M001');
334
+ assert.deepStrictEqual(state.registry[0]?.status, 'complete', 'multi-ms-db: M001 is complete');
335
+ assert.deepStrictEqual(state.registry[1]?.id, 'M002', 'multi-ms-db: registry[1] is M002');
336
+ assert.deepStrictEqual(state.registry[1]?.status, 'active', 'multi-ms-db: M002 is active');
337
+ assert.deepStrictEqual(state.activeMilestone?.id, 'M002', 'multi-ms-db: activeMilestone is M002');
338
+ assert.deepStrictEqual(state.phase, 'planning', 'multi-ms-db: phase is planning (no plan for S01)');
347
339
 
348
340
  closeDatabase();
349
341
  } finally {
350
342
  closeDatabase();
351
343
  cleanup(base);
352
344
  }
353
- }
345
+ });
354
346
 
355
347
  // ─── Test 7: Cache invalidation works for DB path ─────────────────────
356
- console.log('\n=== derive-state-db: cache invalidation ===');
357
- {
348
+ test('derive-state-db: cache invalidation', async () => {
358
349
  const base = createFixtureBase();
359
350
  try {
360
351
  writeFile(base, 'milestones/M001/M001-ROADMAP.md', ROADMAP_CONTENT);
@@ -375,7 +366,7 @@ async function main(): Promise<void> {
375
366
 
376
367
  invalidateStateCache();
377
368
  const state1 = await deriveState(base);
378
- assertEq(state1.activeTask?.id, 'T01', 'cache-inv: first call gets T01');
369
+ assert.deepStrictEqual(state1.activeTask?.id, 'T01', 'cache-inv: first call gets T01');
379
370
 
380
371
  // Simulate task completion by updating the plan in DB
381
372
  const updatedPlan = PLAN_CONTENT.replace('- [ ] **T01:', '- [x] **T01:');
@@ -389,28 +380,27 @@ async function main(): Promise<void> {
389
380
 
390
381
  // Without invalidation, should return cached result (T01 still active)
391
382
  const state2 = await deriveState(base);
392
- assertEq(state2.activeTask?.id, 'T01', 'cache-inv: cached result still has T01');
383
+ assert.deepStrictEqual(state2.activeTask?.id, 'T01', 'cache-inv: cached result still has T01');
393
384
 
394
385
  // After invalidation, should pick up updated content
395
386
  invalidateStateCache();
396
387
  const state3 = await deriveState(base);
397
- assertEq(state3.phase, 'summarizing', 'cache-inv: after invalidation, phase is summarizing (all tasks done)');
398
- assertEq(state3.activeTask, null, 'cache-inv: activeTask is null after all done');
388
+ assert.deepStrictEqual(state3.phase, 'summarizing', 'cache-inv: after invalidation, phase is summarizing (all tasks done)');
389
+ assert.deepStrictEqual(state3.activeTask, null, 'cache-inv: activeTask is null after all done');
399
390
 
400
391
  closeDatabase();
401
392
  } finally {
402
393
  closeDatabase();
403
394
  cleanup(base);
404
395
  }
405
- }
396
+ });
406
397
 
407
398
  // ═════════════════════════════════════════════════════════════════════════
408
399
  // New: deriveStateFromDb() cross-validation tests
409
400
  // ═════════════════════════════════════════════════════════════════════════
410
401
 
411
402
  // ─── Test 8: Pre-planning — milestone exists, no roadmap, no slices ───
412
- console.log('\n=== derive-state-db: pre-planning via DB ===');
413
- {
403
+ test('derive-state-db: pre-planning via DB', async () => {
414
404
  const base = createFixtureBase();
415
405
  try {
416
406
  // Create milestone dir on disk with a CONTEXT file (not a ghost)
@@ -427,23 +417,22 @@ async function main(): Promise<void> {
427
417
  invalidateStateCache();
428
418
  const dbState = await deriveStateFromDb(base);
429
419
 
430
- assertEq(dbState.phase, fileState.phase, 'pre-plan-db: phase matches');
431
- assertEq(dbState.activeMilestone?.id, fileState.activeMilestone?.id, 'pre-plan-db: activeMilestone.id matches');
432
- assertEq(dbState.activeSlice, fileState.activeSlice, 'pre-plan-db: activeSlice matches');
433
- assertEq(dbState.activeTask, fileState.activeTask, 'pre-plan-db: activeTask matches');
434
- assertEq(dbState.registry.length, fileState.registry.length, 'pre-plan-db: registry length matches');
435
- assertEq(dbState.registry[0]?.status, fileState.registry[0]?.status, 'pre-plan-db: registry[0] status matches');
420
+ assert.deepStrictEqual(dbState.phase, fileState.phase, 'pre-plan-db: phase matches');
421
+ assert.deepStrictEqual(dbState.activeMilestone?.id, fileState.activeMilestone?.id, 'pre-plan-db: activeMilestone.id matches');
422
+ assert.deepStrictEqual(dbState.activeSlice, fileState.activeSlice, 'pre-plan-db: activeSlice matches');
423
+ assert.deepStrictEqual(dbState.activeTask, fileState.activeTask, 'pre-plan-db: activeTask matches');
424
+ assert.deepStrictEqual(dbState.registry.length, fileState.registry.length, 'pre-plan-db: registry length matches');
425
+ assert.deepStrictEqual(dbState.registry[0]?.status, fileState.registry[0]?.status, 'pre-plan-db: registry[0] status matches');
436
426
 
437
427
  closeDatabase();
438
428
  } finally {
439
429
  closeDatabase();
440
430
  cleanup(base);
441
431
  }
442
- }
432
+ });
443
433
 
444
434
  // ─── Test 9: Executing — active task with partial completion ──────────
445
- console.log('\n=== derive-state-db: executing via DB ===');
446
- {
435
+ test('derive-state-db: executing via DB', async () => {
447
436
  const base = createFixtureBase();
448
437
  try {
449
438
  // Build filesystem fixture
@@ -466,24 +455,23 @@ async function main(): Promise<void> {
466
455
  invalidateStateCache();
467
456
  const dbState = await deriveStateFromDb(base);
468
457
 
469
- assertEq(dbState.phase, 'executing', 'exec-db: phase is executing');
470
- assertEq(dbState.activeMilestone?.id, 'M001', 'exec-db: activeMilestone is M001');
471
- assertEq(dbState.activeSlice?.id, 'S01', 'exec-db: activeSlice is S01');
472
- assertEq(dbState.activeTask?.id, 'T01', 'exec-db: activeTask is T01');
473
- assertEq(dbState.progress?.tasks?.done, 1, 'exec-db: tasks.done = 1');
474
- assertEq(dbState.progress?.tasks?.total, 2, 'exec-db: tasks.total = 2');
475
- assertEq(dbState.phase, fileState.phase, 'exec-db: phase matches filesystem');
458
+ assert.deepStrictEqual(dbState.phase, 'executing', 'exec-db: phase is executing');
459
+ assert.deepStrictEqual(dbState.activeMilestone?.id, 'M001', 'exec-db: activeMilestone is M001');
460
+ assert.deepStrictEqual(dbState.activeSlice?.id, 'S01', 'exec-db: activeSlice is S01');
461
+ assert.deepStrictEqual(dbState.activeTask?.id, 'T01', 'exec-db: activeTask is T01');
462
+ assert.deepStrictEqual(dbState.progress?.tasks?.done, 1, 'exec-db: tasks.done = 1');
463
+ assert.deepStrictEqual(dbState.progress?.tasks?.total, 2, 'exec-db: tasks.total = 2');
464
+ assert.deepStrictEqual(dbState.phase, fileState.phase, 'exec-db: phase matches filesystem');
476
465
 
477
466
  closeDatabase();
478
467
  } finally {
479
468
  closeDatabase();
480
469
  cleanup(base);
481
470
  }
482
- }
471
+ });
483
472
 
484
473
  // ─── Test 10: Summarizing — all tasks complete, no slice summary ──────
485
- console.log('\n=== derive-state-db: summarizing via DB ===');
486
- {
474
+ test('derive-state-db: summarizing via DB', async () => {
487
475
  const base = createFixtureBase();
488
476
  try {
489
477
  const allDonePlan = `# S01: First Slice
@@ -517,21 +505,20 @@ async function main(): Promise<void> {
517
505
  invalidateStateCache();
518
506
  const dbState = await deriveStateFromDb(base);
519
507
 
520
- assertEq(dbState.phase, 'summarizing', 'summarize-db: phase is summarizing');
521
- assertEq(dbState.phase, fileState.phase, 'summarize-db: phase matches filesystem');
522
- assertEq(dbState.activeSlice?.id, 'S01', 'summarize-db: activeSlice is S01');
523
- assertEq(dbState.activeTask, null, 'summarize-db: activeTask is null');
508
+ assert.deepStrictEqual(dbState.phase, 'summarizing', 'summarize-db: phase is summarizing');
509
+ assert.deepStrictEqual(dbState.phase, fileState.phase, 'summarize-db: phase matches filesystem');
510
+ assert.deepStrictEqual(dbState.activeSlice?.id, 'S01', 'summarize-db: activeSlice is S01');
511
+ assert.deepStrictEqual(dbState.activeTask, null, 'summarize-db: activeTask is null');
524
512
 
525
513
  closeDatabase();
526
514
  } finally {
527
515
  closeDatabase();
528
516
  cleanup(base);
529
517
  }
530
- }
518
+ });
531
519
 
532
520
  // ─── Test 11: Complete — all milestones complete ──────────────────────
533
- console.log('\n=== derive-state-db: all complete via DB ===');
534
- {
521
+ test('derive-state-db: all complete via DB', async () => {
535
522
  const base = createFixtureBase();
536
523
  try {
537
524
  const completedRoadmap = `# M001: Done Milestone
@@ -557,21 +544,20 @@ async function main(): Promise<void> {
557
544
  invalidateStateCache();
558
545
  const dbState = await deriveStateFromDb(base);
559
546
 
560
- assertEq(dbState.phase, 'complete', 'complete-db: phase is complete');
561
- assertEq(dbState.phase, fileState.phase, 'complete-db: phase matches filesystem');
562
- assertEq(dbState.registry.length, 1, 'complete-db: registry has 1 entry');
563
- assertEq(dbState.registry[0]?.status, 'complete', 'complete-db: M001 is complete');
547
+ assert.deepStrictEqual(dbState.phase, 'complete', 'complete-db: phase is complete');
548
+ assert.deepStrictEqual(dbState.phase, fileState.phase, 'complete-db: phase matches filesystem');
549
+ assert.deepStrictEqual(dbState.registry.length, 1, 'complete-db: registry has 1 entry');
550
+ assert.deepStrictEqual(dbState.registry[0]?.status, 'complete', 'complete-db: M001 is complete');
564
551
 
565
552
  closeDatabase();
566
553
  } finally {
567
554
  closeDatabase();
568
555
  cleanup(base);
569
556
  }
570
- }
557
+ });
571
558
 
572
559
  // ─── Test 12: Blocked — slice deps unmet ──────────────────────────────
573
- console.log('\n=== derive-state-db: blocked slice via DB ===');
574
- {
560
+ test('derive-state-db: blocked slice via DB', async () => {
575
561
  const base = createFixtureBase();
576
562
  try {
577
563
  // Roadmap with S02 depending on S01, but S01 not done
@@ -601,20 +587,19 @@ async function main(): Promise<void> {
601
587
  invalidateStateCache();
602
588
  const dbState = await deriveStateFromDb(base);
603
589
 
604
- assertEq(dbState.phase, 'blocked', 'blocked-db: phase is blocked');
605
- assertEq(dbState.phase, fileState.phase, 'blocked-db: phase matches filesystem');
606
- assertTrue(dbState.blockers.length > 0, 'blocked-db: has blockers');
590
+ assert.deepStrictEqual(dbState.phase, 'blocked', 'blocked-db: phase is blocked');
591
+ assert.deepStrictEqual(dbState.phase, fileState.phase, 'blocked-db: phase matches filesystem');
592
+ assert.ok(dbState.blockers.length > 0, 'blocked-db: has blockers');
607
593
 
608
594
  closeDatabase();
609
595
  } finally {
610
596
  closeDatabase();
611
597
  cleanup(base);
612
598
  }
613
- }
599
+ });
614
600
 
615
601
  // ─── Test 13: Parked milestone ────────────────────────────────────────
616
- console.log('\n=== derive-state-db: parked milestone via DB ===');
617
- {
602
+ test('derive-state-db: parked milestone via DB', async () => {
618
603
  const base = createFixtureBase();
619
604
  try {
620
605
  writeFile(base, 'milestones/M001/M001-ROADMAP.md', ROADMAP_CONTENT);
@@ -631,20 +616,19 @@ async function main(): Promise<void> {
631
616
  invalidateStateCache();
632
617
  const dbState = await deriveStateFromDb(base);
633
618
 
634
- assertEq(dbState.phase, fileState.phase, 'parked-db: phase matches filesystem');
635
- assertEq(dbState.activeMilestone?.id, 'M002', 'parked-db: activeMilestone is M002');
636
- assertTrue(dbState.registry.some(e => e.id === 'M001' && e.status === 'parked'), 'parked-db: M001 is parked in registry');
619
+ assert.deepStrictEqual(dbState.phase, fileState.phase, 'parked-db: phase matches filesystem');
620
+ assert.deepStrictEqual(dbState.activeMilestone?.id, 'M002', 'parked-db: activeMilestone is M002');
621
+ assert.ok(dbState.registry.some(e => e.id === 'M001' && e.status === 'parked'), 'parked-db: M001 is parked in registry');
637
622
 
638
623
  closeDatabase();
639
624
  } finally {
640
625
  closeDatabase();
641
626
  cleanup(base);
642
627
  }
643
- }
628
+ });
644
629
 
645
630
  // ─── Test 14: Validating-milestone — all slices done, no terminal validation ─
646
- console.log('\n=== derive-state-db: validating-milestone via DB ===');
647
- {
631
+ test('derive-state-db: validating-milestone via DB', async () => {
648
632
  const base = createFixtureBase();
649
633
  try {
650
634
  const doneRoadmap = `# M001: Validate Test
@@ -669,20 +653,19 @@ async function main(): Promise<void> {
669
653
  invalidateStateCache();
670
654
  const dbState = await deriveStateFromDb(base);
671
655
 
672
- assertEq(dbState.phase, 'validating-milestone', 'validate-db: phase is validating-milestone');
673
- assertEq(dbState.phase, fileState.phase, 'validate-db: phase matches filesystem');
674
- assertEq(dbState.activeMilestone?.id, 'M001', 'validate-db: activeMilestone is M001');
656
+ assert.deepStrictEqual(dbState.phase, 'validating-milestone', 'validate-db: phase is validating-milestone');
657
+ assert.deepStrictEqual(dbState.phase, fileState.phase, 'validate-db: phase matches filesystem');
658
+ assert.deepStrictEqual(dbState.activeMilestone?.id, 'M001', 'validate-db: activeMilestone is M001');
675
659
 
676
660
  closeDatabase();
677
661
  } finally {
678
662
  closeDatabase();
679
663
  cleanup(base);
680
664
  }
681
- }
665
+ });
682
666
 
683
667
  // ─── Test 15: Completing-milestone — terminal validation, no summary ──
684
- console.log('\n=== derive-state-db: completing-milestone via DB ===');
685
- {
668
+ test('derive-state-db: completing-milestone via DB', async () => {
686
669
  const base = createFixtureBase();
687
670
  try {
688
671
  const doneRoadmap = `# M001: Complete Test
@@ -707,19 +690,18 @@ async function main(): Promise<void> {
707
690
  invalidateStateCache();
708
691
  const dbState = await deriveStateFromDb(base);
709
692
 
710
- assertEq(dbState.phase, 'completing-milestone', 'completing-db: phase is completing-milestone');
711
- assertEq(dbState.phase, fileState.phase, 'completing-db: phase matches filesystem');
693
+ assert.deepStrictEqual(dbState.phase, 'completing-milestone', 'completing-db: phase is completing-milestone');
694
+ assert.deepStrictEqual(dbState.phase, fileState.phase, 'completing-db: phase matches filesystem');
712
695
 
713
696
  closeDatabase();
714
697
  } finally {
715
698
  closeDatabase();
716
699
  cleanup(base);
717
700
  }
718
- }
701
+ });
719
702
 
720
703
  // ─── Test 16: Replanning-slice — REPLAN-TRIGGER file exists ───────────
721
- console.log('\n=== derive-state-db: replanning-slice via DB ===');
722
- {
704
+ test('derive-state-db: replanning-slice via DB', async () => {
723
705
  const base = createFixtureBase();
724
706
  try {
725
707
  writeFile(base, 'milestones/M001/M001-ROADMAP.md', ROADMAP_CONTENT);
@@ -749,19 +731,18 @@ async function main(): Promise<void> {
749
731
  invalidateStateCache();
750
732
  const dbState = await deriveStateFromDb(base);
751
733
 
752
- assertEq(dbState.phase, 'replanning-slice', 'replan-db: phase is replanning-slice');
753
- assertEq(dbState.phase, fileState.phase, 'replan-db: phase matches filesystem');
734
+ assert.deepStrictEqual(dbState.phase, 'replanning-slice', 'replan-db: phase is replanning-slice');
735
+ assert.deepStrictEqual(dbState.phase, fileState.phase, 'replan-db: phase matches filesystem');
754
736
 
755
737
  closeDatabase();
756
738
  } finally {
757
739
  closeDatabase();
758
740
  cleanup(base);
759
741
  }
760
- }
742
+ });
761
743
 
762
744
  // ─── Test 17: Performance — deriveStateFromDb < 1ms on populated DB ───
763
- console.log('\n=== derive-state-db: performance assertion ===');
764
- {
745
+ test('derive-state-db: performance assertion', async () => {
765
746
  const base = createFixtureBase();
766
747
  try {
767
748
  writeFile(base, 'milestones/M001/M001-ROADMAP.md', ROADMAP_CONTENT);
@@ -789,18 +770,17 @@ async function main(): Promise<void> {
789
770
  console.log(` deriveStateFromDb() took ${elapsed.toFixed(3)}ms`);
790
771
  // Use 10ms threshold — catches real regressions without flaking on
791
772
  // CI runners under load (1ms threshold failed at 1.050ms on GitHub Actions)
792
- assertTrue(elapsed < 10, `perf-db: deriveStateFromDb() <10ms (got ${elapsed.toFixed(3)}ms)`);
773
+ assert.ok(elapsed < 10, `perf-db: deriveStateFromDb() <10ms (got ${elapsed.toFixed(3)}ms)`);
793
774
 
794
775
  closeDatabase();
795
776
  } finally {
796
777
  closeDatabase();
797
778
  cleanup(base);
798
779
  }
799
- }
780
+ });
800
781
 
801
782
  // ─── Test 18: Multi-milestone with deps — M001 complete, M002 depends on M001, M003 depends on M002 ─
802
- console.log('\n=== derive-state-db: multi-milestone deps via DB ===');
803
- {
783
+ test('derive-state-db: multi-milestone deps via DB', async () => {
804
784
  const base = createFixtureBase();
805
785
  try {
806
786
  const m1Roadmap = `# M001: First
@@ -841,29 +821,28 @@ async function main(): Promise<void> {
841
821
  invalidateStateCache();
842
822
  const dbState = await deriveStateFromDb(base);
843
823
 
844
- assertEq(dbState.registry.length, fileState.registry.length, 'multi-deps-db: registry length matches');
845
- assertEq(dbState.activeMilestone?.id, 'M002', 'multi-deps-db: activeMilestone is M002 (M001 complete, M003 dep unmet)');
846
- assertEq(dbState.activeMilestone?.id, fileState.activeMilestone?.id, 'multi-deps-db: activeMilestone matches filesystem');
847
- assertEq(dbState.phase, fileState.phase, 'multi-deps-db: phase matches filesystem');
824
+ assert.deepStrictEqual(dbState.registry.length, fileState.registry.length, 'multi-deps-db: registry length matches');
825
+ assert.deepStrictEqual(dbState.activeMilestone?.id, 'M002', 'multi-deps-db: activeMilestone is M002 (M001 complete, M003 dep unmet)');
826
+ assert.deepStrictEqual(dbState.activeMilestone?.id, fileState.activeMilestone?.id, 'multi-deps-db: activeMilestone matches filesystem');
827
+ assert.deepStrictEqual(dbState.phase, fileState.phase, 'multi-deps-db: phase matches filesystem');
848
828
 
849
829
  // Check registry statuses
850
830
  const m1reg = dbState.registry.find(e => e.id === 'M001');
851
831
  const m2reg = dbState.registry.find(e => e.id === 'M002');
852
832
  const m3reg = dbState.registry.find(e => e.id === 'M003');
853
- assertEq(m1reg?.status, 'complete', 'multi-deps-db: M001 is complete');
854
- assertEq(m2reg?.status, 'active', 'multi-deps-db: M002 is active');
855
- assertEq(m3reg?.status, 'pending', 'multi-deps-db: M003 is pending (dep M002 unmet)');
833
+ assert.deepStrictEqual(m1reg?.status, 'complete', 'multi-deps-db: M001 is complete');
834
+ assert.deepStrictEqual(m2reg?.status, 'active', 'multi-deps-db: M002 is active');
835
+ assert.deepStrictEqual(m3reg?.status, 'pending', 'multi-deps-db: M003 is pending (dep M002 unmet)');
856
836
 
857
837
  closeDatabase();
858
838
  } finally {
859
839
  closeDatabase();
860
840
  cleanup(base);
861
841
  }
862
- }
842
+ });
863
843
 
864
844
  // ─── Test 19: K002 — both 'complete' and 'done' treated as done ───────
865
- console.log('\n=== derive-state-db: K002 status handling ===');
866
- {
845
+ test('derive-state-db: K002 status handling', async () => {
867
846
  const base = createFixtureBase();
868
847
  try {
869
848
  writeFile(base, 'milestones/M001/M001-ROADMAP.md', ROADMAP_CONTENT);
@@ -882,20 +861,19 @@ async function main(): Promise<void> {
882
861
  invalidateStateCache();
883
862
  const dbState = await deriveStateFromDb(base);
884
863
 
885
- assertEq(dbState.phase, 'executing', 'k002-db: phase is executing');
886
- assertEq(dbState.activeTask?.id, 'T01', 'k002-db: activeTask is T01 (T02 done)');
887
- assertEq(dbState.progress?.tasks?.done, 1, 'k002-db: tasks.done counts done status');
864
+ assert.deepStrictEqual(dbState.phase, 'executing', 'k002-db: phase is executing');
865
+ assert.deepStrictEqual(dbState.activeTask?.id, 'T01', 'k002-db: activeTask is T01 (T02 done)');
866
+ assert.deepStrictEqual(dbState.progress?.tasks?.done, 1, 'k002-db: tasks.done counts done status');
888
867
 
889
868
  closeDatabase();
890
869
  } finally {
891
870
  closeDatabase();
892
871
  cleanup(base);
893
872
  }
894
- }
873
+ });
895
874
 
896
875
  // ─── Test 20: Dual-path wiring — deriveState() uses DB when populated ─
897
- console.log('\n=== derive-state-db: dual-path wiring ===');
898
- {
876
+ test('derive-state-db: dual-path wiring', async () => {
899
877
  const base = createFixtureBase();
900
878
  try {
901
879
  writeFile(base, 'milestones/M001/M001-ROADMAP.md', ROADMAP_CONTENT);
@@ -914,21 +892,20 @@ async function main(): Promise<void> {
914
892
  invalidateStateCache();
915
893
  const state = await deriveState(base);
916
894
 
917
- assertEq(state.phase, 'executing', 'dual-path: phase is executing');
918
- assertEq(state.activeMilestone?.id, 'M001', 'dual-path: activeMilestone is M001');
919
- assertEq(state.activeSlice?.id, 'S01', 'dual-path: activeSlice is S01');
920
- assertEq(state.activeTask?.id, 'T01', 'dual-path: activeTask is T01');
895
+ assert.deepStrictEqual(state.phase, 'executing', 'dual-path: phase is executing');
896
+ assert.deepStrictEqual(state.activeMilestone?.id, 'M001', 'dual-path: activeMilestone is M001');
897
+ assert.deepStrictEqual(state.activeSlice?.id, 'S01', 'dual-path: activeSlice is S01');
898
+ assert.deepStrictEqual(state.activeTask?.id, 'T01', 'dual-path: activeTask is T01');
921
899
 
922
900
  closeDatabase();
923
901
  } finally {
924
902
  closeDatabase();
925
903
  cleanup(base);
926
904
  }
927
- }
905
+ });
928
906
 
929
907
  // ─── Test 21: Ghost milestone skipped ─────────────────────────────────
930
- console.log('\n=== derive-state-db: ghost milestone skipped ===');
931
- {
908
+ test('derive-state-db: ghost milestone skipped', async () => {
932
909
  const base = createFixtureBase();
933
910
  try {
934
911
  // Ghost: milestone dir exists with only META.json, no context/roadmap/summary
@@ -949,21 +926,20 @@ async function main(): Promise<void> {
949
926
  const dbState = await deriveStateFromDb(base);
950
927
 
951
928
  // Ghost should be skipped — M002 should be active
952
- assertEq(dbState.activeMilestone?.id, 'M002', 'ghost-db: activeMilestone is M002 (ghost skipped)');
953
- assertEq(dbState.activeMilestone?.id, fileState.activeMilestone?.id, 'ghost-db: matches filesystem');
929
+ assert.deepStrictEqual(dbState.activeMilestone?.id, 'M002', 'ghost-db: activeMilestone is M002 (ghost skipped)');
930
+ assert.deepStrictEqual(dbState.activeMilestone?.id, fileState.activeMilestone?.id, 'ghost-db: matches filesystem');
954
931
  // Ghost should not appear in registry
955
- assertTrue(!dbState.registry.some(e => e.id === 'M001'), 'ghost-db: M001 not in registry');
932
+ assert.ok(!dbState.registry.some(e => e.id === 'M001'), 'ghost-db: M001 not in registry');
956
933
 
957
934
  closeDatabase();
958
935
  } finally {
959
936
  closeDatabase();
960
937
  cleanup(base);
961
938
  }
962
- }
939
+ });
963
940
 
964
941
  // ─── Test 22: Needs-discussion — CONTEXT-DRAFT exists ─────────────────
965
- console.log('\n=== derive-state-db: needs-discussion via DB ===');
966
- {
942
+ test('derive-state-db: needs-discussion via DB', async () => {
967
943
  const base = createFixtureBase();
968
944
  try {
969
945
  writeFile(base, 'milestones/M001/M001-CONTEXT-DRAFT.md', '# M001: Draft\n\nDraft content.');
@@ -977,20 +953,13 @@ async function main(): Promise<void> {
977
953
  invalidateStateCache();
978
954
  const dbState = await deriveStateFromDb(base);
979
955
 
980
- assertEq(dbState.phase, 'needs-discussion', 'discuss-db: phase is needs-discussion');
981
- assertEq(dbState.phase, fileState.phase, 'discuss-db: phase matches filesystem');
956
+ assert.deepStrictEqual(dbState.phase, 'needs-discussion', 'discuss-db: phase is needs-discussion');
957
+ assert.deepStrictEqual(dbState.phase, fileState.phase, 'discuss-db: phase matches filesystem');
982
958
 
983
959
  closeDatabase();
984
960
  } finally {
985
961
  closeDatabase();
986
962
  cleanup(base);
987
963
  }
988
- }
989
-
990
- report();
991
- }
992
-
993
- main().catch((error) => {
994
- console.error(error);
995
- process.exit(1);
964
+ });
996
965
  });