gsd-pi 2.44.0-dev.62b5d6c → 2.44.0-dev.a5271fc

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 (198) 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/bootstrap/system-context.js +46 -12
  4. package/dist/resources/extensions/gsd/commands/handlers/workflow.js +5 -0
  5. package/dist/resources/extensions/gsd/preferences.js +9 -1
  6. package/dist/resources/extensions/gsd/state.js +19 -2
  7. package/dist/web/standalone/.next/BUILD_ID +1 -1
  8. package/dist/web/standalone/.next/app-path-routes-manifest.json +19 -19
  9. package/dist/web/standalone/.next/build-manifest.json +2 -2
  10. package/dist/web/standalone/.next/prerender-manifest.json +3 -3
  11. package/dist/web/standalone/.next/server/app/_global-error.html +2 -2
  12. package/dist/web/standalone/.next/server/app/_global-error.rsc +1 -1
  13. package/dist/web/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  14. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
  15. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
  16. package/dist/web/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  17. package/dist/web/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  18. package/dist/web/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  19. package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
  20. package/dist/web/standalone/.next/server/app/_not-found.rsc +1 -1
  21. package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +1 -1
  22. package/dist/web/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  23. package/dist/web/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +1 -1
  24. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  25. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  26. package/dist/web/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
  27. package/dist/web/standalone/.next/server/app/index.html +1 -1
  28. package/dist/web/standalone/.next/server/app/index.rsc +1 -1
  29. package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +1 -1
  30. package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +1 -1
  31. package/dist/web/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
  32. package/dist/web/standalone/.next/server/app/index.segments/_index.segment.rsc +1 -1
  33. package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
  34. package/dist/web/standalone/.next/server/app-paths-manifest.json +19 -19
  35. package/dist/web/standalone/.next/server/pages/404.html +1 -1
  36. package/dist/web/standalone/.next/server/pages/500.html +2 -2
  37. package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
  38. package/package.json +1 -1
  39. package/packages/pi-coding-agent/dist/core/auth-storage.test.js +6 -8
  40. package/packages/pi-coding-agent/dist/core/auth-storage.test.js.map +1 -1
  41. package/packages/pi-coding-agent/dist/core/extensions/runner.test.js +24 -26
  42. package/packages/pi-coding-agent/dist/core/extensions/runner.test.js.map +1 -1
  43. package/packages/pi-coding-agent/dist/core/fs-utils.test.js +29 -48
  44. package/packages/pi-coding-agent/dist/core/fs-utils.test.js.map +1 -1
  45. package/packages/pi-coding-agent/dist/core/resolve-config-value.test.js +34 -44
  46. package/packages/pi-coding-agent/dist/core/resolve-config-value.test.js.map +1 -1
  47. package/packages/pi-coding-agent/dist/core/session-manager.test.js +30 -34
  48. package/packages/pi-coding-agent/dist/core/session-manager.test.js.map +1 -1
  49. package/packages/pi-coding-agent/dist/core/tools/edit-diff.test.js +10 -12
  50. package/packages/pi-coding-agent/dist/core/tools/edit-diff.test.js.map +1 -1
  51. package/packages/pi-coding-agent/dist/resources/extensions/memory/storage.test.js +43 -47
  52. package/packages/pi-coding-agent/dist/resources/extensions/memory/storage.test.js.map +1 -1
  53. package/packages/pi-coding-agent/src/core/auth-storage.test.ts +7 -7
  54. package/packages/pi-coding-agent/src/core/extensions/runner.test.ts +26 -26
  55. package/packages/pi-coding-agent/src/core/fs-utils.test.ts +31 -43
  56. package/packages/pi-coding-agent/src/core/resolve-config-value.test.ts +40 -45
  57. package/packages/pi-coding-agent/src/core/session-manager.test.ts +33 -33
  58. package/packages/pi-coding-agent/src/core/tools/edit-diff.test.ts +17 -17
  59. package/packages/pi-coding-agent/src/resources/extensions/memory/storage.test.ts +74 -74
  60. package/src/resources/extensions/gsd/auto-start.ts +14 -0
  61. package/src/resources/extensions/gsd/bootstrap/system-context.ts +48 -11
  62. package/src/resources/extensions/gsd/commands/handlers/workflow.ts +8 -0
  63. package/src/resources/extensions/gsd/preferences.ts +11 -1
  64. package/src/resources/extensions/gsd/state.ts +19 -1
  65. package/src/resources/extensions/gsd/tests/all-milestones-complete-merge.test.ts +99 -99
  66. package/src/resources/extensions/gsd/tests/auto-lock-creation.test.ts +14 -16
  67. package/src/resources/extensions/gsd/tests/auto-paused-session-validation.test.ts +43 -57
  68. package/src/resources/extensions/gsd/tests/auto-preflight.test.ts +11 -13
  69. package/src/resources/extensions/gsd/tests/auto-recovery.test.ts +465 -523
  70. package/src/resources/extensions/gsd/tests/auto-secrets-gate.test.ts +73 -75
  71. package/src/resources/extensions/gsd/tests/auto-start-needs-discussion.test.ts +34 -56
  72. package/src/resources/extensions/gsd/tests/auto-worktree-milestone-merge.test.ts +533 -656
  73. package/src/resources/extensions/gsd/tests/auto-worktree.test.ts +165 -143
  74. package/src/resources/extensions/gsd/tests/cache-staleness-regression.test.ts +29 -52
  75. package/src/resources/extensions/gsd/tests/captures.test.ts +148 -176
  76. package/src/resources/extensions/gsd/tests/claude-import-tui.test.ts +32 -33
  77. package/src/resources/extensions/gsd/tests/collect-from-manifest.test.ts +141 -143
  78. package/src/resources/extensions/gsd/tests/commands-inspect-open-db.test.ts +25 -25
  79. package/src/resources/extensions/gsd/tests/commands-logs.test.ts +81 -81
  80. package/src/resources/extensions/gsd/tests/complete-milestone.test.ts +38 -59
  81. package/src/resources/extensions/gsd/tests/complete-slice.test.ts +228 -263
  82. package/src/resources/extensions/gsd/tests/complete-task.test.ts +250 -302
  83. package/src/resources/extensions/gsd/tests/context-store.test.ts +354 -367
  84. package/src/resources/extensions/gsd/tests/continue-here.test.ts +68 -72
  85. package/src/resources/extensions/gsd/tests/cost-projection.test.ts +92 -106
  86. package/src/resources/extensions/gsd/tests/crash-recovery.test.ts +27 -35
  87. package/src/resources/extensions/gsd/tests/dashboard-budget.test.ts +220 -237
  88. package/src/resources/extensions/gsd/tests/db-writer.test.ts +390 -420
  89. package/src/resources/extensions/gsd/tests/definition-loader.test.ts +76 -92
  90. package/src/resources/extensions/gsd/tests/derive-state-crossval.test.ts +68 -83
  91. package/src/resources/extensions/gsd/tests/derive-state-db.test.ts +183 -181
  92. package/src/resources/extensions/gsd/tests/derive-state-deps.test.ts +78 -101
  93. package/src/resources/extensions/gsd/tests/derive-state.test.ts +192 -227
  94. package/src/resources/extensions/gsd/tests/detection.test.ts +232 -278
  95. package/src/resources/extensions/gsd/tests/dev-engine-wrapper.test.ts +30 -34
  96. package/src/resources/extensions/gsd/tests/dispatch-guard.test.ts +164 -180
  97. package/src/resources/extensions/gsd/tests/dispatch-missing-task-plans.test.ts +43 -49
  98. package/src/resources/extensions/gsd/tests/dispatch-uat-last-completed.test.ts +28 -32
  99. package/src/resources/extensions/gsd/tests/doctor-completion-deferral.test.ts +27 -29
  100. package/src/resources/extensions/gsd/tests/doctor-delimiter-fix.test.ts +34 -38
  101. package/src/resources/extensions/gsd/tests/doctor-enhancements.test.ts +54 -75
  102. package/src/resources/extensions/gsd/tests/doctor-environment-worktree.test.ts +21 -32
  103. package/src/resources/extensions/gsd/tests/doctor-environment.test.ts +72 -97
  104. package/src/resources/extensions/gsd/tests/doctor-fixlevel.test.ts +38 -44
  105. package/src/resources/extensions/gsd/tests/doctor-git.test.ts +104 -145
  106. package/src/resources/extensions/gsd/tests/doctor-proactive.test.ts +84 -106
  107. package/src/resources/extensions/gsd/tests/doctor-roadmap-summary-atomicity.test.ts +54 -60
  108. package/src/resources/extensions/gsd/tests/doctor-runtime.test.ts +72 -93
  109. package/src/resources/extensions/gsd/tests/doctor.test.ts +104 -134
  110. package/src/resources/extensions/gsd/tests/ensure-db-open.test.ts +123 -131
  111. package/src/resources/extensions/gsd/tests/exit-command.test.ts +20 -24
  112. package/src/resources/extensions/gsd/tests/feature-branch-lifecycle-integration.test.ts +48 -57
  113. package/src/resources/extensions/gsd/tests/files-loadfile-eisdir.test.ts +5 -7
  114. package/src/resources/extensions/gsd/tests/flag-file-db.test.ts +30 -42
  115. package/src/resources/extensions/gsd/tests/freeform-decisions.test.ts +198 -206
  116. package/src/resources/extensions/gsd/tests/git-locale.test.ts +13 -27
  117. package/src/resources/extensions/gsd/tests/git-service.test.ts +285 -388
  118. package/src/resources/extensions/gsd/tests/gitignore-tracked-gsd.test.ts +31 -39
  119. package/src/resources/extensions/gsd/tests/graph-operations.test.ts +63 -69
  120. package/src/resources/extensions/gsd/tests/gsd-db.test.ts +255 -264
  121. package/src/resources/extensions/gsd/tests/gsd-inspect.test.ts +108 -119
  122. package/src/resources/extensions/gsd/tests/gsd-recover.test.ts +81 -103
  123. package/src/resources/extensions/gsd/tests/gsd-tools.test.ts +229 -262
  124. package/src/resources/extensions/gsd/tests/headless-answers.test.ts +13 -13
  125. package/src/resources/extensions/gsd/tests/health-widget.test.ts +29 -37
  126. package/src/resources/extensions/gsd/tests/idle-recovery.test.ts +81 -102
  127. package/src/resources/extensions/gsd/tests/init-wizard.test.ts +16 -18
  128. package/src/resources/extensions/gsd/tests/integration-edge.test.ts +41 -46
  129. package/src/resources/extensions/gsd/tests/integration-lifecycle.test.ts +42 -53
  130. package/src/resources/extensions/gsd/tests/integration-mixed-milestones.test.ts +75 -91
  131. package/src/resources/extensions/gsd/tests/integration-proof.test.ts +18 -18
  132. package/src/resources/extensions/gsd/tests/knowledge.test.ts +89 -0
  133. package/src/resources/extensions/gsd/tests/markdown-renderer.test.ts +150 -194
  134. package/src/resources/extensions/gsd/tests/md-importer.test.ts +101 -125
  135. package/src/resources/extensions/gsd/tests/memory-extractor.test.ts +45 -54
  136. package/src/resources/extensions/gsd/tests/memory-store.test.ts +80 -93
  137. package/src/resources/extensions/gsd/tests/migrate-command.test.ts +57 -66
  138. package/src/resources/extensions/gsd/tests/migrate-hierarchy.test.ts +83 -93
  139. package/src/resources/extensions/gsd/tests/migrate-parser.test.ts +161 -170
  140. package/src/resources/extensions/gsd/tests/migrate-transformer.test.ts +125 -141
  141. package/src/resources/extensions/gsd/tests/migrate-validator-parsers.test.ts +107 -131
  142. package/src/resources/extensions/gsd/tests/migrate-writer-integration.test.ts +87 -96
  143. package/src/resources/extensions/gsd/tests/migrate-writer.test.ts +125 -164
  144. package/src/resources/extensions/gsd/tests/must-have-parser.test.ts +81 -94
  145. package/src/resources/extensions/gsd/tests/none-mode-gates.test.ts +35 -36
  146. package/src/resources/extensions/gsd/tests/overrides.test.ts +99 -106
  147. package/src/resources/extensions/gsd/tests/parallel-crash-recovery.test.ts +40 -47
  148. package/src/resources/extensions/gsd/tests/parallel-worker-monitoring.test.ts +25 -28
  149. package/src/resources/extensions/gsd/tests/parallel-workers-multi-milestone-e2e.test.ts +66 -83
  150. package/src/resources/extensions/gsd/tests/park-edge-cases.test.ts +54 -77
  151. package/src/resources/extensions/gsd/tests/park-milestone.test.ts +68 -115
  152. package/src/resources/extensions/gsd/tests/parsers.test.ts +546 -611
  153. package/src/resources/extensions/gsd/tests/paths.test.ts +72 -87
  154. package/src/resources/extensions/gsd/tests/post-unit-hooks.test.ts +77 -117
  155. package/src/resources/extensions/gsd/tests/preferences.test.ts +27 -0
  156. package/src/resources/extensions/gsd/tests/prompt-db.test.ts +56 -56
  157. package/src/resources/extensions/gsd/tests/queue-draft-detection.test.ts +93 -119
  158. package/src/resources/extensions/gsd/tests/queue-order.test.ts +70 -82
  159. package/src/resources/extensions/gsd/tests/queue-reorder-e2e.test.ts +42 -55
  160. package/src/resources/extensions/gsd/tests/quick-auto-guard.test.ts +100 -0
  161. package/src/resources/extensions/gsd/tests/quick-branch-lifecycle.test.ts +45 -73
  162. package/src/resources/extensions/gsd/tests/reassess-prompt.test.ts +28 -38
  163. package/src/resources/extensions/gsd/tests/replan-slice.test.ts +73 -80
  164. package/src/resources/extensions/gsd/tests/repo-identity-worktree.test.ts +71 -74
  165. package/src/resources/extensions/gsd/tests/requirements.test.ts +70 -75
  166. package/src/resources/extensions/gsd/tests/retry-state-reset.test.ts +44 -66
  167. package/src/resources/extensions/gsd/tests/roadmap-parse-regression.test.ts +114 -181
  168. package/src/resources/extensions/gsd/tests/rule-registry.test.ts +63 -65
  169. package/src/resources/extensions/gsd/tests/run-uat.test.ts +66 -128
  170. package/src/resources/extensions/gsd/tests/session-lock-multipath.test.ts +18 -25
  171. package/src/resources/extensions/gsd/tests/session-lock-regression.test.ts +37 -44
  172. package/src/resources/extensions/gsd/tests/shared-wal.test.ts +19 -26
  173. package/src/resources/extensions/gsd/tests/sqlite-unavailable-gate.test.ts +63 -0
  174. package/src/resources/extensions/gsd/tests/stalled-tool-recovery.test.ts +6 -8
  175. package/src/resources/extensions/gsd/tests/symlink-numbered-variants.test.ts +22 -28
  176. package/src/resources/extensions/gsd/tests/token-savings.test.ts +54 -56
  177. package/src/resources/extensions/gsd/tests/tool-call-loop-guard.test.ts +23 -25
  178. package/src/resources/extensions/gsd/tests/tool-naming.test.ts +9 -11
  179. package/src/resources/extensions/gsd/tests/unique-milestone-ids.test.ts +66 -82
  180. package/src/resources/extensions/gsd/tests/unit-runtime.test.ts +46 -47
  181. package/src/resources/extensions/gsd/tests/visualizer-critical-path.test.ts +20 -22
  182. package/src/resources/extensions/gsd/tests/visualizer-data.test.ts +84 -86
  183. package/src/resources/extensions/gsd/tests/visualizer-overlay.test.ts +41 -43
  184. package/src/resources/extensions/gsd/tests/visualizer-views.test.ts +94 -96
  185. package/src/resources/extensions/gsd/tests/windows-path-normalization.test.ts +11 -13
  186. package/src/resources/extensions/gsd/tests/worker-registry.test.ts +27 -29
  187. package/src/resources/extensions/gsd/tests/workflow-templates.test.ts +50 -52
  188. package/src/resources/extensions/gsd/tests/worktree-bugfix.test.ts +10 -13
  189. package/src/resources/extensions/gsd/tests/worktree-db-integration.test.ts +14 -18
  190. package/src/resources/extensions/gsd/tests/worktree-db.test.ts +38 -39
  191. package/src/resources/extensions/gsd/tests/worktree-e2e.test.ts +17 -21
  192. package/src/resources/extensions/gsd/tests/worktree-health.test.ts +25 -30
  193. package/src/resources/extensions/gsd/tests/worktree-integration.test.ts +30 -37
  194. package/src/resources/extensions/gsd/tests/worktree-symlink-removal.test.ts +15 -22
  195. package/src/resources/extensions/gsd/tests/worktree-sync-milestones.test.ts +59 -66
  196. package/src/resources/extensions/gsd/tests/worktree.test.ts +44 -50
  197. /package/dist/web/standalone/.next/static/{fOnWQBjWXMKUs4bqTg530 → JyimLR2pZuvKEzv26gI3w}/_buildManifest.js +0 -0
  198. /package/dist/web/standalone/.next/static/{fOnWQBjWXMKUs4bqTg530 → JyimLR2pZuvKEzv26gI3w}/_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
+ });