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,5 @@
1
- import { createTestContext } from './test-helpers.ts';
1
+ import { describe, test } from 'node:test';
2
+ import assert from 'node:assert/strict';
2
3
  import * as fs from 'node:fs';
3
4
  import * as path from 'node:path';
4
5
  import * as os from 'node:os';
@@ -18,8 +19,6 @@ import {
18
19
  _resetProvider,
19
20
  } from '../gsd-db.ts';
20
21
 
21
- const { assertEq, assertTrue, report } = createTestContext();
22
-
23
22
  // ═══════════════════════════════════════════════════════════════════════════
24
23
  // Helper: create a temp file path for file-backed DB tests
25
24
  // ═══════════════════════════════════════════════════════════════════════════
@@ -47,314 +46,306 @@ function cleanup(dbPath: string): void {
47
46
  // gsd-db tests
48
47
  // ═══════════════════════════════════════════════════════════════════════════
49
48
 
50
- console.log('\n=== gsd-db: provider detection ===');
51
- {
52
- const provider = getDbProvider();
53
- assertTrue(provider !== null, 'provider should be non-null');
54
- assertTrue(
55
- provider === 'node:sqlite' || provider === 'better-sqlite3',
56
- `provider should be a known name, got: ${provider}`,
57
- );
58
- }
59
-
60
- console.log('\n=== gsd-db: fresh DB schema init (memory) ===');
61
- {
62
- const ok = openDatabase(':memory:');
63
- assertTrue(ok, 'openDatabase should return true');
64
- assertTrue(isDbAvailable(), 'isDbAvailable should be true after open');
49
+ describe('gsd-db', () => {
50
+ test('gsd-db: provider detection', () => {
51
+ const provider = getDbProvider();
52
+ assert.ok(provider !== null, 'provider should be non-null');
53
+ assert.ok(
54
+ provider === 'node:sqlite' || provider === 'better-sqlite3',
55
+ `provider should be a known name, got: ${provider}`,
56
+ );
57
+ });
65
58
 
66
- // Check schema_version table
67
- const adapter = _getAdapter()!;
68
- const version = adapter.prepare('SELECT MAX(version) as version FROM schema_version').get();
69
- assertEq(version?.['version'], 10, 'schema version should be 10');
59
+ test('gsd-db: fresh DB schema init (memory)', () => {
60
+ const ok = openDatabase(':memory:');
61
+ assert.ok(ok, 'openDatabase should return true');
62
+ assert.ok(isDbAvailable(), 'isDbAvailable should be true after open');
70
63
 
71
- // Check tables exist by querying them
72
- const dRows = adapter.prepare('SELECT count(*) as cnt FROM decisions').get();
73
- assertEq(dRows?.['cnt'], 0, 'decisions table should exist and be empty');
64
+ // Check schema_version table
65
+ const adapter = _getAdapter()!;
66
+ const version = adapter.prepare('SELECT MAX(version) as version FROM schema_version').get();
67
+ assert.deepStrictEqual(version?.['version'], 10, 'schema version should be 10');
74
68
 
75
- const rRows = adapter.prepare('SELECT count(*) as cnt FROM requirements').get();
76
- assertEq(rRows?.['cnt'], 0, 'requirements table should exist and be empty');
69
+ // Check tables exist by querying them
70
+ const dRows = adapter.prepare('SELECT count(*) as cnt FROM decisions').get();
71
+ assert.deepStrictEqual(dRows?.['cnt'], 0, 'decisions table should exist and be empty');
77
72
 
78
- closeDatabase();
79
- assertTrue(!isDbAvailable(), 'isDbAvailable should be false after close');
80
- }
73
+ const rRows = adapter.prepare('SELECT count(*) as cnt FROM requirements').get();
74
+ assert.deepStrictEqual(rRows?.['cnt'], 0, 'requirements table should exist and be empty');
81
75
 
82
- console.log('\n=== gsd-db: double-init idempotency ===');
83
- {
84
- const dbPath = tempDbPath();
85
- openDatabase(dbPath);
86
-
87
- // Insert a decision so we can verify it survives re-init
88
- insertDecision({
89
- id: 'D001',
90
- when_context: 'test',
91
- scope: 'global',
92
- decision: 'test decision',
93
- choice: 'option A',
94
- rationale: 'because',
95
- revisable: 'yes',
96
- made_by: 'agent',
97
- superseded_by: null,
76
+ closeDatabase();
77
+ assert.ok(!isDbAvailable(), 'isDbAvailable should be false after close');
98
78
  });
99
79
 
100
- closeDatabase();
80
+ test('gsd-db: double-init idempotency', () => {
81
+ const dbPath = tempDbPath();
82
+ openDatabase(dbPath);
83
+
84
+ // Insert a decision so we can verify it survives re-init
85
+ insertDecision({
86
+ id: 'D001',
87
+ when_context: 'test',
88
+ scope: 'global',
89
+ decision: 'test decision',
90
+ choice: 'option A',
91
+ rationale: 'because',
92
+ revisable: 'yes',
93
+ made_by: 'agent',
94
+ superseded_by: null,
95
+ });
101
96
 
102
- // Re-open same DB — schema init should be idempotent
103
- openDatabase(dbPath);
104
- const d = getDecisionById('D001');
105
- assertTrue(d !== null, 'decision should survive re-init');
106
- assertEq(d?.id, 'D001', 'decision ID preserved after re-init');
97
+ closeDatabase();
107
98
 
108
- // Schema version should still be 1 (not duplicated)
109
- const adapter = _getAdapter()!;
110
- const versions = adapter.prepare('SELECT count(*) as cnt FROM schema_version').get();
111
- assertEq(versions?.['cnt'], 1, 'schema_version should have exactly 1 row after double-init');
99
+ // Re-open same DB schema init should be idempotent
100
+ openDatabase(dbPath);
101
+ const d = getDecisionById('D001');
102
+ assert.ok(d !== null, 'decision should survive re-init');
103
+ assert.deepStrictEqual(d?.id, 'D001', 'decision ID preserved after re-init');
112
104
 
113
- cleanup(dbPath);
114
- }
105
+ // Schema version should still be 1 (not duplicated)
106
+ const adapter = _getAdapter()!;
107
+ const versions = adapter.prepare('SELECT count(*) as cnt FROM schema_version').get();
108
+ assert.deepStrictEqual(versions?.['cnt'], 1, 'schema_version should have exactly 1 row after double-init');
115
109
 
116
- console.log('\n=== gsd-db: insert + get decision ===');
117
- {
118
- openDatabase(':memory:');
119
- insertDecision({
120
- id: 'D042',
121
- when_context: 'during sprint 3',
122
- scope: 'M001/S02',
123
- decision: 'use SQLite for storage',
124
- choice: 'node:sqlite',
125
- rationale: 'built-in, zero deps',
126
- revisable: 'yes, if perf insufficient',
127
- made_by: 'agent',
128
- superseded_by: null,
110
+ cleanup(dbPath);
129
111
  });
130
112
 
131
- const d = getDecisionById('D042');
132
- assertTrue(d !== null, 'should find inserted decision');
133
- assertEq(d?.id, 'D042', 'decision id');
134
- assertEq(d?.scope, 'M001/S02', 'decision scope');
135
- assertEq(d?.choice, 'node:sqlite', 'decision choice');
136
- assertTrue(typeof d?.seq === 'number' && d.seq > 0, 'seq should be auto-assigned positive number');
137
- assertEq(d?.superseded_by, null, 'superseded_by should be null');
113
+ test('gsd-db: insert + get decision', () => {
114
+ openDatabase(':memory:');
115
+ insertDecision({
116
+ id: 'D042',
117
+ when_context: 'during sprint 3',
118
+ scope: 'M001/S02',
119
+ decision: 'use SQLite for storage',
120
+ choice: 'node:sqlite',
121
+ rationale: 'built-in, zero deps',
122
+ revisable: 'yes, if perf insufficient',
123
+ made_by: 'agent',
124
+ superseded_by: null,
125
+ });
138
126
 
139
- // Non-existent
140
- const missing = getDecisionById('D999');
141
- assertEq(missing, null, 'non-existent decision returns null');
127
+ const d = getDecisionById('D042');
128
+ assert.ok(d !== null, 'should find inserted decision');
129
+ assert.deepStrictEqual(d?.id, 'D042', 'decision id');
130
+ assert.deepStrictEqual(d?.scope, 'M001/S02', 'decision scope');
131
+ assert.deepStrictEqual(d?.choice, 'node:sqlite', 'decision choice');
132
+ assert.ok(typeof d?.seq === 'number' && d.seq > 0, 'seq should be auto-assigned positive number');
133
+ assert.deepStrictEqual(d?.superseded_by, null, 'superseded_by should be null');
142
134
 
143
- closeDatabase();
144
- }
135
+ // Non-existent
136
+ const missing = getDecisionById('D999');
137
+ assert.deepStrictEqual(missing, null, 'non-existent decision returns null');
145
138
 
146
- console.log('\n=== gsd-db: insert + get requirement ===');
147
- {
148
- openDatabase(':memory:');
149
- insertRequirement({
150
- id: 'R007',
151
- class: 'functional',
152
- status: 'active',
153
- description: 'System must persist decisions',
154
- why: 'decisions inform future agents',
155
- source: 'M001-CONTEXT',
156
- primary_owner: 'S01',
157
- supporting_slices: 'S02, S03',
158
- validation: 'insert and query roundtrip',
159
- notes: 'high priority',
160
- full_content: 'Full text of requirement...',
161
- superseded_by: null,
139
+ closeDatabase();
162
140
  });
163
141
 
164
- const r = getRequirementById('R007');
165
- assertTrue(r !== null, 'should find inserted requirement');
166
- assertEq(r?.id, 'R007', 'requirement id');
167
- assertEq(r?.class, 'functional', 'requirement class');
168
- assertEq(r?.status, 'active', 'requirement status');
169
- assertEq(r?.primary_owner, 'S01', 'requirement primary_owner');
170
- assertEq(r?.superseded_by, null, 'superseded_by should be null');
142
+ test('gsd-db: insert + get requirement', () => {
143
+ openDatabase(':memory:');
144
+ insertRequirement({
145
+ id: 'R007',
146
+ class: 'functional',
147
+ status: 'active',
148
+ description: 'System must persist decisions',
149
+ why: 'decisions inform future agents',
150
+ source: 'M001-CONTEXT',
151
+ primary_owner: 'S01',
152
+ supporting_slices: 'S02, S03',
153
+ validation: 'insert and query roundtrip',
154
+ notes: 'high priority',
155
+ full_content: 'Full text of requirement...',
156
+ superseded_by: null,
157
+ });
171
158
 
172
- // Non-existent
173
- const missing = getRequirementById('R999');
174
- assertEq(missing, null, 'non-existent requirement returns null');
159
+ const r = getRequirementById('R007');
160
+ assert.ok(r !== null, 'should find inserted requirement');
161
+ assert.deepStrictEqual(r?.id, 'R007', 'requirement id');
162
+ assert.deepStrictEqual(r?.class, 'functional', 'requirement class');
163
+ assert.deepStrictEqual(r?.status, 'active', 'requirement status');
164
+ assert.deepStrictEqual(r?.primary_owner, 'S01', 'requirement primary_owner');
165
+ assert.deepStrictEqual(r?.superseded_by, null, 'superseded_by should be null');
175
166
 
176
- closeDatabase();
177
- }
167
+ // Non-existent
168
+ const missing = getRequirementById('R999');
169
+ assert.deepStrictEqual(missing, null, 'non-existent requirement returns null');
178
170
 
179
- console.log('\n=== gsd-db: active_decisions view excludes superseded ===');
180
- {
181
- openDatabase(':memory:');
182
-
183
- insertDecision({
184
- id: 'D001',
185
- when_context: 'early',
186
- scope: 'global',
187
- decision: 'use JSON files',
188
- choice: 'JSON',
189
- rationale: 'simple',
190
- revisable: 'yes',
191
- made_by: 'agent',
192
- superseded_by: 'D002', // superseded!
171
+ closeDatabase();
193
172
  });
194
173
 
195
- insertDecision({
196
- id: 'D002',
197
- when_context: 'later',
198
- scope: 'global',
199
- decision: 'use SQLite',
200
- choice: 'SQLite',
201
- rationale: 'better querying',
202
- revisable: 'yes',
203
- made_by: 'agent',
204
- superseded_by: null, // active
205
- });
174
+ test('gsd-db: active_decisions view excludes superseded', () => {
175
+ openDatabase(':memory:');
176
+
177
+ insertDecision({
178
+ id: 'D001',
179
+ when_context: 'early',
180
+ scope: 'global',
181
+ decision: 'use JSON files',
182
+ choice: 'JSON',
183
+ rationale: 'simple',
184
+ revisable: 'yes',
185
+ made_by: 'agent',
186
+ superseded_by: 'D002', // superseded!
187
+ });
206
188
 
207
- insertDecision({
208
- id: 'D003',
209
- when_context: 'same time',
210
- scope: 'local',
211
- decision: 'use WAL mode',
212
- choice: 'WAL',
213
- rationale: 'concurrent reads',
214
- revisable: 'no',
215
- made_by: 'agent',
216
- superseded_by: null, // active
217
- });
189
+ insertDecision({
190
+ id: 'D002',
191
+ when_context: 'later',
192
+ scope: 'global',
193
+ decision: 'use SQLite',
194
+ choice: 'SQLite',
195
+ rationale: 'better querying',
196
+ revisable: 'yes',
197
+ made_by: 'agent',
198
+ superseded_by: null, // active
199
+ });
218
200
 
219
- const active = getActiveDecisions();
220
- assertEq(active.length, 2, 'active_decisions should return 2 (not the superseded one)');
221
- const ids = active.map(d => d.id).sort();
222
- assertEq(ids, ['D002', 'D003'], 'active decisions should be D002 and D003');
201
+ insertDecision({
202
+ id: 'D003',
203
+ when_context: 'same time',
204
+ scope: 'local',
205
+ decision: 'use WAL mode',
206
+ choice: 'WAL',
207
+ rationale: 'concurrent reads',
208
+ revisable: 'no',
209
+ made_by: 'agent',
210
+ superseded_by: null, // active
211
+ });
223
212
 
224
- // Verify D001 is still in the raw table
225
- const d1 = getDecisionById('D001');
226
- assertTrue(d1 !== null, 'superseded decision still exists in raw table');
227
- assertEq(d1?.superseded_by, 'D002', 'superseded_by is set');
213
+ const active = getActiveDecisions();
214
+ assert.deepStrictEqual(active.length, 2, 'active_decisions should return 2 (not the superseded one)');
215
+ const ids = active.map(d => d.id).sort();
216
+ assert.deepStrictEqual(ids, ['D002', 'D003'], 'active decisions should be D002 and D003');
228
217
 
229
- closeDatabase();
230
- }
218
+ // Verify D001 is still in the raw table
219
+ const d1 = getDecisionById('D001');
220
+ assert.ok(d1 !== null, 'superseded decision still exists in raw table');
221
+ assert.deepStrictEqual(d1?.superseded_by, 'D002', 'superseded_by is set');
231
222
 
232
- console.log('\n=== gsd-db: active_requirements view excludes superseded ===');
233
- {
234
- openDatabase(':memory:');
235
-
236
- insertRequirement({
237
- id: 'R001',
238
- class: 'functional',
239
- status: 'active',
240
- description: 'old requirement',
241
- why: 'was needed',
242
- source: 'M001',
243
- primary_owner: 'S01',
244
- supporting_slices: '',
245
- validation: 'test',
246
- notes: '',
247
- full_content: '',
248
- superseded_by: 'R002', // superseded!
223
+ closeDatabase();
249
224
  });
250
225
 
251
- insertRequirement({
252
- id: 'R002',
253
- class: 'functional',
254
- status: 'active',
255
- description: 'new requirement',
256
- why: 'replaces R001',
257
- source: 'M001',
258
- primary_owner: 'S01',
259
- supporting_slices: '',
260
- validation: 'test',
261
- notes: '',
262
- full_content: '',
263
- superseded_by: null, // active
264
- });
226
+ test('gsd-db: active_requirements view excludes superseded', () => {
227
+ openDatabase(':memory:');
228
+
229
+ insertRequirement({
230
+ id: 'R001',
231
+ class: 'functional',
232
+ status: 'active',
233
+ description: 'old requirement',
234
+ why: 'was needed',
235
+ source: 'M001',
236
+ primary_owner: 'S01',
237
+ supporting_slices: '',
238
+ validation: 'test',
239
+ notes: '',
240
+ full_content: '',
241
+ superseded_by: 'R002', // superseded!
242
+ });
265
243
 
266
- const active = getActiveRequirements();
267
- assertEq(active.length, 1, 'active_requirements should return 1');
268
- assertEq(active[0]?.id, 'R002', 'only R002 should be active');
244
+ insertRequirement({
245
+ id: 'R002',
246
+ class: 'functional',
247
+ status: 'active',
248
+ description: 'new requirement',
249
+ why: 'replaces R001',
250
+ source: 'M001',
251
+ primary_owner: 'S01',
252
+ supporting_slices: '',
253
+ validation: 'test',
254
+ notes: '',
255
+ full_content: '',
256
+ superseded_by: null, // active
257
+ });
269
258
 
270
- // R001 still in raw table
271
- const r1 = getRequirementById('R001');
272
- assertTrue(r1 !== null, 'superseded requirement still in raw table');
259
+ const active = getActiveRequirements();
260
+ assert.deepStrictEqual(active.length, 1, 'active_requirements should return 1');
261
+ assert.deepStrictEqual(active[0]?.id, 'R002', 'only R002 should be active');
273
262
 
274
- closeDatabase();
275
- }
263
+ // R001 still in raw table
264
+ const r1 = getRequirementById('R001');
265
+ assert.ok(r1 !== null, 'superseded requirement still in raw table');
276
266
 
277
- console.log('\n=== gsd-db: WAL mode on file-backed DB ===');
278
- {
279
- const dbPath = tempDbPath();
280
- openDatabase(dbPath);
267
+ closeDatabase();
268
+ });
281
269
 
282
- const adapter = _getAdapter()!;
283
- const mode = adapter.prepare('PRAGMA journal_mode').get();
284
- assertEq(mode?.['journal_mode'], 'wal', 'journal_mode should be wal for file-backed DB');
270
+ test('gsd-db: WAL mode on file-backed DB', () => {
271
+ const dbPath = tempDbPath();
272
+ openDatabase(dbPath);
285
273
 
286
- cleanup(dbPath);
287
- }
274
+ const adapter = _getAdapter()!;
275
+ const mode = adapter.prepare('PRAGMA journal_mode').get();
276
+ assert.deepStrictEqual(mode?.['journal_mode'], 'wal', 'journal_mode should be wal for file-backed DB');
288
277
 
289
- console.log('\n=== gsd-db: transaction rollback on error ===');
290
- {
291
- openDatabase(':memory:');
292
-
293
- // Insert a decision normally
294
- insertDecision({
295
- id: 'D010',
296
- when_context: 'test',
297
- scope: 'test',
298
- decision: 'test',
299
- choice: 'test',
300
- rationale: 'test',
301
- revisable: 'test',
302
- made_by: 'agent',
303
- superseded_by: null,
278
+ cleanup(dbPath);
304
279
  });
305
280
 
306
- // Try a transaction that fails the insert inside should be rolled back
307
- let threw = false;
308
- try {
309
- transaction(() => {
310
- insertDecision({
311
- id: 'D011',
312
- when_context: 'should be rolled back',
313
- scope: 'test',
314
- decision: 'test',
315
- choice: 'test',
316
- rationale: 'test',
317
- revisable: 'test',
318
- made_by: 'agent',
319
- superseded_by: null,
320
- });
321
- throw new Error('intentional failure');
281
+ test('gsd-db: transaction rollback on error', () => {
282
+ openDatabase(':memory:');
283
+
284
+ // Insert a decision normally
285
+ insertDecision({
286
+ id: 'D010',
287
+ when_context: 'test',
288
+ scope: 'test',
289
+ decision: 'test',
290
+ choice: 'test',
291
+ rationale: 'test',
292
+ revisable: 'test',
293
+ made_by: 'agent',
294
+ superseded_by: null,
322
295
  });
323
- } catch (err) {
324
- if ((err as Error).message === 'intentional failure') {
325
- threw = true;
296
+
297
+ // Try a transaction that fails the insert inside should be rolled back
298
+ let threw = false;
299
+ try {
300
+ transaction(() => {
301
+ insertDecision({
302
+ id: 'D011',
303
+ when_context: 'should be rolled back',
304
+ scope: 'test',
305
+ decision: 'test',
306
+ choice: 'test',
307
+ rationale: 'test',
308
+ revisable: 'test',
309
+ made_by: 'agent',
310
+ superseded_by: null,
311
+ });
312
+ throw new Error('intentional failure');
313
+ });
314
+ } catch (err) {
315
+ if ((err as Error).message === 'intentional failure') {
316
+ threw = true;
317
+ }
326
318
  }
327
- }
328
319
 
329
- assertTrue(threw, 'transaction should re-throw the error');
330
- const d11 = getDecisionById('D011');
331
- assertEq(d11, null, 'D011 should be rolled back (not found)');
320
+ assert.ok(threw, 'transaction should re-throw the error');
321
+ const d11 = getDecisionById('D011');
322
+ assert.deepStrictEqual(d11, null, 'D011 should be rolled back (not found)');
332
323
 
333
- // D010 should still be there
334
- const d10 = getDecisionById('D010');
335
- assertTrue(d10 !== null, 'D010 should survive the failed transaction');
324
+ // D010 should still be there
325
+ const d10 = getDecisionById('D010');
326
+ assert.ok(d10 !== null, 'D010 should survive the failed transaction');
336
327
 
337
- closeDatabase();
338
- }
328
+ closeDatabase();
329
+ });
339
330
 
340
- console.log('\n=== gsd-db: query wrappers return null/empty when DB unavailable ===');
341
- {
342
- // Ensure DB is closed
343
- closeDatabase();
344
- assertTrue(!isDbAvailable(), 'DB should not be available');
331
+ test('gsd-db: query wrappers return null/empty when DB unavailable', () => {
332
+ // Ensure DB is closed
333
+ closeDatabase();
334
+ assert.ok(!isDbAvailable(), 'DB should not be available');
345
335
 
346
- const d = getDecisionById('D001');
347
- assertEq(d, null, 'getDecisionById returns null when DB closed');
336
+ const d = getDecisionById('D001');
337
+ assert.deepStrictEqual(d, null, 'getDecisionById returns null when DB closed');
348
338
 
349
- const r = getRequirementById('R001');
350
- assertEq(r, null, 'getRequirementById returns null when DB closed');
339
+ const r = getRequirementById('R001');
340
+ assert.deepStrictEqual(r, null, 'getRequirementById returns null when DB closed');
351
341
 
352
- const ad = getActiveDecisions();
353
- assertEq(ad, [], 'getActiveDecisions returns [] when DB closed');
342
+ const ad = getActiveDecisions();
343
+ assert.deepStrictEqual(ad, [], 'getActiveDecisions returns [] when DB closed');
354
344
 
355
- const ar = getActiveRequirements();
356
- assertEq(ar, [], 'getActiveRequirements returns [] when DB closed');
357
- }
345
+ const ar = getActiveRequirements();
346
+ assert.deepStrictEqual(ar, [], 'getActiveRequirements returns [] when DB closed');
347
+ });
348
+
349
+ // ─── Final Report ──────────────────────────────────────────────────────────
358
350
 
359
- // ─── Final Report ──────────────────────────────────────────────────────────
360
- report();
351
+ });