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.
- package/README.md +30 -12
- package/dist/resources/extensions/gsd/auto-start.js +10 -0
- package/dist/resources/extensions/gsd/bootstrap/system-context.js +46 -12
- package/dist/resources/extensions/gsd/commands/handlers/workflow.js +5 -0
- package/dist/resources/extensions/gsd/preferences.js +9 -1
- package/dist/resources/extensions/gsd/state.js +19 -2
- package/dist/web/standalone/.next/BUILD_ID +1 -1
- package/dist/web/standalone/.next/app-path-routes-manifest.json +19 -19
- package/dist/web/standalone/.next/build-manifest.json +2 -2
- package/dist/web/standalone/.next/prerender-manifest.json +3 -3
- package/dist/web/standalone/.next/server/app/_global-error.html +2 -2
- package/dist/web/standalone/.next/server/app/_global-error.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.html +1 -1
- package/dist/web/standalone/.next/server/app/index.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/_index.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app-paths-manifest.json +19 -19
- package/dist/web/standalone/.next/server/pages/404.html +1 -1
- package/dist/web/standalone/.next/server/pages/500.html +2 -2
- package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
- package/package.json +1 -1
- package/packages/pi-coding-agent/dist/core/auth-storage.test.js +6 -8
- package/packages/pi-coding-agent/dist/core/auth-storage.test.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/extensions/runner.test.js +24 -26
- package/packages/pi-coding-agent/dist/core/extensions/runner.test.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/fs-utils.test.js +29 -48
- package/packages/pi-coding-agent/dist/core/fs-utils.test.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/resolve-config-value.test.js +34 -44
- package/packages/pi-coding-agent/dist/core/resolve-config-value.test.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/session-manager.test.js +30 -34
- package/packages/pi-coding-agent/dist/core/session-manager.test.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/tools/edit-diff.test.js +10 -12
- package/packages/pi-coding-agent/dist/core/tools/edit-diff.test.js.map +1 -1
- package/packages/pi-coding-agent/dist/resources/extensions/memory/storage.test.js +43 -47
- package/packages/pi-coding-agent/dist/resources/extensions/memory/storage.test.js.map +1 -1
- package/packages/pi-coding-agent/src/core/auth-storage.test.ts +7 -7
- package/packages/pi-coding-agent/src/core/extensions/runner.test.ts +26 -26
- package/packages/pi-coding-agent/src/core/fs-utils.test.ts +31 -43
- package/packages/pi-coding-agent/src/core/resolve-config-value.test.ts +40 -45
- package/packages/pi-coding-agent/src/core/session-manager.test.ts +33 -33
- package/packages/pi-coding-agent/src/core/tools/edit-diff.test.ts +17 -17
- package/packages/pi-coding-agent/src/resources/extensions/memory/storage.test.ts +74 -74
- package/src/resources/extensions/gsd/auto-start.ts +14 -0
- package/src/resources/extensions/gsd/bootstrap/system-context.ts +48 -11
- package/src/resources/extensions/gsd/commands/handlers/workflow.ts +8 -0
- package/src/resources/extensions/gsd/preferences.ts +11 -1
- package/src/resources/extensions/gsd/state.ts +19 -1
- package/src/resources/extensions/gsd/tests/all-milestones-complete-merge.test.ts +99 -99
- package/src/resources/extensions/gsd/tests/auto-lock-creation.test.ts +14 -16
- package/src/resources/extensions/gsd/tests/auto-paused-session-validation.test.ts +43 -57
- package/src/resources/extensions/gsd/tests/auto-preflight.test.ts +11 -13
- package/src/resources/extensions/gsd/tests/auto-recovery.test.ts +465 -523
- package/src/resources/extensions/gsd/tests/auto-secrets-gate.test.ts +73 -75
- package/src/resources/extensions/gsd/tests/auto-start-needs-discussion.test.ts +34 -56
- package/src/resources/extensions/gsd/tests/auto-worktree-milestone-merge.test.ts +533 -656
- package/src/resources/extensions/gsd/tests/auto-worktree.test.ts +165 -143
- package/src/resources/extensions/gsd/tests/cache-staleness-regression.test.ts +29 -52
- package/src/resources/extensions/gsd/tests/captures.test.ts +148 -176
- package/src/resources/extensions/gsd/tests/claude-import-tui.test.ts +32 -33
- package/src/resources/extensions/gsd/tests/collect-from-manifest.test.ts +141 -143
- package/src/resources/extensions/gsd/tests/commands-inspect-open-db.test.ts +25 -25
- package/src/resources/extensions/gsd/tests/commands-logs.test.ts +81 -81
- package/src/resources/extensions/gsd/tests/complete-milestone.test.ts +38 -59
- package/src/resources/extensions/gsd/tests/complete-slice.test.ts +228 -263
- package/src/resources/extensions/gsd/tests/complete-task.test.ts +250 -302
- package/src/resources/extensions/gsd/tests/context-store.test.ts +354 -367
- package/src/resources/extensions/gsd/tests/continue-here.test.ts +68 -72
- package/src/resources/extensions/gsd/tests/cost-projection.test.ts +92 -106
- package/src/resources/extensions/gsd/tests/crash-recovery.test.ts +27 -35
- package/src/resources/extensions/gsd/tests/dashboard-budget.test.ts +220 -237
- package/src/resources/extensions/gsd/tests/db-writer.test.ts +390 -420
- package/src/resources/extensions/gsd/tests/definition-loader.test.ts +76 -92
- package/src/resources/extensions/gsd/tests/derive-state-crossval.test.ts +68 -83
- package/src/resources/extensions/gsd/tests/derive-state-db.test.ts +183 -181
- package/src/resources/extensions/gsd/tests/derive-state-deps.test.ts +78 -101
- package/src/resources/extensions/gsd/tests/derive-state.test.ts +192 -227
- package/src/resources/extensions/gsd/tests/detection.test.ts +232 -278
- package/src/resources/extensions/gsd/tests/dev-engine-wrapper.test.ts +30 -34
- package/src/resources/extensions/gsd/tests/dispatch-guard.test.ts +164 -180
- package/src/resources/extensions/gsd/tests/dispatch-missing-task-plans.test.ts +43 -49
- package/src/resources/extensions/gsd/tests/dispatch-uat-last-completed.test.ts +28 -32
- package/src/resources/extensions/gsd/tests/doctor-completion-deferral.test.ts +27 -29
- package/src/resources/extensions/gsd/tests/doctor-delimiter-fix.test.ts +34 -38
- package/src/resources/extensions/gsd/tests/doctor-enhancements.test.ts +54 -75
- package/src/resources/extensions/gsd/tests/doctor-environment-worktree.test.ts +21 -32
- package/src/resources/extensions/gsd/tests/doctor-environment.test.ts +72 -97
- package/src/resources/extensions/gsd/tests/doctor-fixlevel.test.ts +38 -44
- package/src/resources/extensions/gsd/tests/doctor-git.test.ts +104 -145
- package/src/resources/extensions/gsd/tests/doctor-proactive.test.ts +84 -106
- package/src/resources/extensions/gsd/tests/doctor-roadmap-summary-atomicity.test.ts +54 -60
- package/src/resources/extensions/gsd/tests/doctor-runtime.test.ts +72 -93
- package/src/resources/extensions/gsd/tests/doctor.test.ts +104 -134
- package/src/resources/extensions/gsd/tests/ensure-db-open.test.ts +123 -131
- package/src/resources/extensions/gsd/tests/exit-command.test.ts +20 -24
- package/src/resources/extensions/gsd/tests/feature-branch-lifecycle-integration.test.ts +48 -57
- package/src/resources/extensions/gsd/tests/files-loadfile-eisdir.test.ts +5 -7
- package/src/resources/extensions/gsd/tests/flag-file-db.test.ts +30 -42
- package/src/resources/extensions/gsd/tests/freeform-decisions.test.ts +198 -206
- package/src/resources/extensions/gsd/tests/git-locale.test.ts +13 -27
- package/src/resources/extensions/gsd/tests/git-service.test.ts +285 -388
- package/src/resources/extensions/gsd/tests/gitignore-tracked-gsd.test.ts +31 -39
- package/src/resources/extensions/gsd/tests/graph-operations.test.ts +63 -69
- package/src/resources/extensions/gsd/tests/gsd-db.test.ts +255 -264
- package/src/resources/extensions/gsd/tests/gsd-inspect.test.ts +108 -119
- package/src/resources/extensions/gsd/tests/gsd-recover.test.ts +81 -103
- package/src/resources/extensions/gsd/tests/gsd-tools.test.ts +229 -262
- package/src/resources/extensions/gsd/tests/headless-answers.test.ts +13 -13
- package/src/resources/extensions/gsd/tests/health-widget.test.ts +29 -37
- package/src/resources/extensions/gsd/tests/idle-recovery.test.ts +81 -102
- package/src/resources/extensions/gsd/tests/init-wizard.test.ts +16 -18
- package/src/resources/extensions/gsd/tests/integration-edge.test.ts +41 -46
- package/src/resources/extensions/gsd/tests/integration-lifecycle.test.ts +42 -53
- package/src/resources/extensions/gsd/tests/integration-mixed-milestones.test.ts +75 -91
- package/src/resources/extensions/gsd/tests/integration-proof.test.ts +18 -18
- package/src/resources/extensions/gsd/tests/knowledge.test.ts +89 -0
- package/src/resources/extensions/gsd/tests/markdown-renderer.test.ts +150 -194
- package/src/resources/extensions/gsd/tests/md-importer.test.ts +101 -125
- package/src/resources/extensions/gsd/tests/memory-extractor.test.ts +45 -54
- package/src/resources/extensions/gsd/tests/memory-store.test.ts +80 -93
- package/src/resources/extensions/gsd/tests/migrate-command.test.ts +57 -66
- package/src/resources/extensions/gsd/tests/migrate-hierarchy.test.ts +83 -93
- package/src/resources/extensions/gsd/tests/migrate-parser.test.ts +161 -170
- package/src/resources/extensions/gsd/tests/migrate-transformer.test.ts +125 -141
- package/src/resources/extensions/gsd/tests/migrate-validator-parsers.test.ts +107 -131
- package/src/resources/extensions/gsd/tests/migrate-writer-integration.test.ts +87 -96
- package/src/resources/extensions/gsd/tests/migrate-writer.test.ts +125 -164
- package/src/resources/extensions/gsd/tests/must-have-parser.test.ts +81 -94
- package/src/resources/extensions/gsd/tests/none-mode-gates.test.ts +35 -36
- package/src/resources/extensions/gsd/tests/overrides.test.ts +99 -106
- package/src/resources/extensions/gsd/tests/parallel-crash-recovery.test.ts +40 -47
- package/src/resources/extensions/gsd/tests/parallel-worker-monitoring.test.ts +25 -28
- package/src/resources/extensions/gsd/tests/parallel-workers-multi-milestone-e2e.test.ts +66 -83
- package/src/resources/extensions/gsd/tests/park-edge-cases.test.ts +54 -77
- package/src/resources/extensions/gsd/tests/park-milestone.test.ts +68 -115
- package/src/resources/extensions/gsd/tests/parsers.test.ts +546 -611
- package/src/resources/extensions/gsd/tests/paths.test.ts +72 -87
- package/src/resources/extensions/gsd/tests/post-unit-hooks.test.ts +77 -117
- package/src/resources/extensions/gsd/tests/preferences.test.ts +27 -0
- package/src/resources/extensions/gsd/tests/prompt-db.test.ts +56 -56
- package/src/resources/extensions/gsd/tests/queue-draft-detection.test.ts +93 -119
- package/src/resources/extensions/gsd/tests/queue-order.test.ts +70 -82
- package/src/resources/extensions/gsd/tests/queue-reorder-e2e.test.ts +42 -55
- package/src/resources/extensions/gsd/tests/quick-auto-guard.test.ts +100 -0
- package/src/resources/extensions/gsd/tests/quick-branch-lifecycle.test.ts +45 -73
- package/src/resources/extensions/gsd/tests/reassess-prompt.test.ts +28 -38
- package/src/resources/extensions/gsd/tests/replan-slice.test.ts +73 -80
- package/src/resources/extensions/gsd/tests/repo-identity-worktree.test.ts +71 -74
- package/src/resources/extensions/gsd/tests/requirements.test.ts +70 -75
- package/src/resources/extensions/gsd/tests/retry-state-reset.test.ts +44 -66
- package/src/resources/extensions/gsd/tests/roadmap-parse-regression.test.ts +114 -181
- package/src/resources/extensions/gsd/tests/rule-registry.test.ts +63 -65
- package/src/resources/extensions/gsd/tests/run-uat.test.ts +66 -128
- package/src/resources/extensions/gsd/tests/session-lock-multipath.test.ts +18 -25
- package/src/resources/extensions/gsd/tests/session-lock-regression.test.ts +37 -44
- package/src/resources/extensions/gsd/tests/shared-wal.test.ts +19 -26
- package/src/resources/extensions/gsd/tests/sqlite-unavailable-gate.test.ts +63 -0
- package/src/resources/extensions/gsd/tests/stalled-tool-recovery.test.ts +6 -8
- package/src/resources/extensions/gsd/tests/symlink-numbered-variants.test.ts +22 -28
- package/src/resources/extensions/gsd/tests/token-savings.test.ts +54 -56
- package/src/resources/extensions/gsd/tests/tool-call-loop-guard.test.ts +23 -25
- package/src/resources/extensions/gsd/tests/tool-naming.test.ts +9 -11
- package/src/resources/extensions/gsd/tests/unique-milestone-ids.test.ts +66 -82
- package/src/resources/extensions/gsd/tests/unit-runtime.test.ts +46 -47
- package/src/resources/extensions/gsd/tests/visualizer-critical-path.test.ts +20 -22
- package/src/resources/extensions/gsd/tests/visualizer-data.test.ts +84 -86
- package/src/resources/extensions/gsd/tests/visualizer-overlay.test.ts +41 -43
- package/src/resources/extensions/gsd/tests/visualizer-views.test.ts +94 -96
- package/src/resources/extensions/gsd/tests/windows-path-normalization.test.ts +11 -13
- package/src/resources/extensions/gsd/tests/worker-registry.test.ts +27 -29
- package/src/resources/extensions/gsd/tests/workflow-templates.test.ts +50 -52
- package/src/resources/extensions/gsd/tests/worktree-bugfix.test.ts +10 -13
- package/src/resources/extensions/gsd/tests/worktree-db-integration.test.ts +14 -18
- package/src/resources/extensions/gsd/tests/worktree-db.test.ts +38 -39
- package/src/resources/extensions/gsd/tests/worktree-e2e.test.ts +17 -21
- package/src/resources/extensions/gsd/tests/worktree-health.test.ts +25 -30
- package/src/resources/extensions/gsd/tests/worktree-integration.test.ts +30 -37
- package/src/resources/extensions/gsd/tests/worktree-symlink-removal.test.ts +15 -22
- package/src/resources/extensions/gsd/tests/worktree-sync-milestones.test.ts +59 -66
- package/src/resources/extensions/gsd/tests/worktree.test.ts +44 -50
- /package/dist/web/standalone/.next/static/{fOnWQBjWXMKUs4bqTg530 → JyimLR2pZuvKEzv26gI3w}/_buildManifest.js +0 -0
- /package/dist/web/standalone/.next/static/{fOnWQBjWXMKUs4bqTg530 → JyimLR2pZuvKEzv26gI3w}/_ssgManifest.js +0 -0
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import {
|
|
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
|
-
|
|
51
|
-
{
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
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
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
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
|
-
|
|
72
|
-
|
|
73
|
-
|
|
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
|
-
|
|
76
|
-
|
|
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
|
-
|
|
79
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
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
|
-
|
|
140
|
-
|
|
141
|
-
|
|
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
|
-
|
|
144
|
-
|
|
135
|
+
// Non-existent
|
|
136
|
+
const missing = getDecisionById('D999');
|
|
137
|
+
assert.deepStrictEqual(missing, null, 'non-existent decision returns null');
|
|
145
138
|
|
|
146
|
-
|
|
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
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
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
|
-
|
|
173
|
-
|
|
174
|
-
|
|
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
|
-
|
|
177
|
-
|
|
167
|
+
// Non-existent
|
|
168
|
+
const missing = getRequirementById('R999');
|
|
169
|
+
assert.deepStrictEqual(missing, null, 'non-existent requirement returns null');
|
|
178
170
|
|
|
179
|
-
|
|
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
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
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
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
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
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
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
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
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
|
-
|
|
267
|
-
|
|
268
|
-
|
|
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
|
-
|
|
271
|
-
|
|
272
|
-
|
|
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
|
-
|
|
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
|
-
|
|
278
|
-
|
|
279
|
-
const dbPath = tempDbPath();
|
|
280
|
-
openDatabase(dbPath);
|
|
267
|
+
closeDatabase();
|
|
268
|
+
});
|
|
281
269
|
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
270
|
+
test('gsd-db: WAL mode on file-backed DB', () => {
|
|
271
|
+
const dbPath = tempDbPath();
|
|
272
|
+
openDatabase(dbPath);
|
|
285
273
|
|
|
286
|
-
|
|
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
|
-
|
|
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
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
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
|
-
|
|
324
|
-
|
|
325
|
-
|
|
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
|
-
|
|
330
|
-
|
|
331
|
-
|
|
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
|
-
|
|
334
|
-
|
|
335
|
-
|
|
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
|
-
|
|
338
|
-
}
|
|
328
|
+
closeDatabase();
|
|
329
|
+
});
|
|
339
330
|
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
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
|
-
|
|
347
|
-
|
|
336
|
+
const d = getDecisionById('D001');
|
|
337
|
+
assert.deepStrictEqual(d, null, 'getDecisionById returns null when DB closed');
|
|
348
338
|
|
|
349
|
-
|
|
350
|
-
|
|
339
|
+
const r = getRequirementById('R001');
|
|
340
|
+
assert.deepStrictEqual(r, null, 'getRequirementById returns null when DB closed');
|
|
351
341
|
|
|
352
|
-
|
|
353
|
-
|
|
342
|
+
const ad = getActiveDecisions();
|
|
343
|
+
assert.deepStrictEqual(ad, [], 'getActiveDecisions returns [] when DB closed');
|
|
354
344
|
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
}
|
|
345
|
+
const ar = getActiveRequirements();
|
|
346
|
+
assert.deepStrictEqual(ar, [], 'getActiveRequirements returns [] when DB closed');
|
|
347
|
+
});
|
|
348
|
+
|
|
349
|
+
// ─── Final Report ──────────────────────────────────────────────────────────
|
|
358
350
|
|
|
359
|
-
|
|
360
|
-
report();
|
|
351
|
+
});
|