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.
- package/README.md +30 -12
- package/dist/resources/extensions/gsd/auto-start.js +10 -0
- package/dist/resources/extensions/gsd/commands/handlers/workflow.js +5 -0
- package/dist/web/standalone/.next/BUILD_ID +1 -1
- package/dist/web/standalone/.next/app-path-routes-manifest.json +18 -18
- 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 +18 -18
- 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/commands/handlers/workflow.ts +8 -0
- 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 +152 -183
- 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/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/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 → -zps1Q9mQmioAKLcQiCr8}/_buildManifest.js +0 -0
- /package/dist/web/standalone/.next/static/{fOnWQBjWXMKUs4bqTg530 → -zps1Q9mQmioAKLcQiCr8}/_ssgManifest.js +0 -0
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { createTestContext } from './test-helpers.ts';
|
|
2
1
|
import {
|
|
3
2
|
openDatabase,
|
|
4
3
|
closeDatabase,
|
|
@@ -21,94 +20,90 @@ import {
|
|
|
21
20
|
formatMemoriesForPrompt,
|
|
22
21
|
} from '../memory-store.ts';
|
|
23
22
|
import type { MemoryAction } from '../memory-store.ts';
|
|
24
|
-
|
|
25
|
-
|
|
23
|
+
import { describe, test, beforeEach, afterEach } from 'node:test';
|
|
24
|
+
import assert from 'node:assert/strict';
|
|
26
25
|
|
|
27
26
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
28
27
|
// memory-store: fallback when DB not open
|
|
29
28
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
30
29
|
|
|
31
|
-
|
|
32
|
-
{
|
|
30
|
+
test('memory-store: fallback returns empty when DB not open', () => {
|
|
33
31
|
closeDatabase();
|
|
34
|
-
|
|
32
|
+
assert.ok(!isDbAvailable(), 'DB should not be available');
|
|
35
33
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
}
|
|
34
|
+
assert.deepStrictEqual(getActiveMemories(), [], 'getActiveMemories returns [] when DB closed');
|
|
35
|
+
assert.deepStrictEqual(getActiveMemoriesRanked(), [], 'getActiveMemoriesRanked returns [] when DB closed');
|
|
36
|
+
assert.deepStrictEqual(nextMemoryId(), 'MEM001', 'nextMemoryId returns MEM001 when DB closed');
|
|
37
|
+
assert.deepStrictEqual(createMemory({ category: 'test', content: 'test' }), null, 'createMemory returns null when DB closed');
|
|
38
|
+
assert.ok(!reinforceMemory('MEM001'), 'reinforceMemory returns false when DB closed');
|
|
39
|
+
assert.ok(!isUnitProcessed('test/key'), 'isUnitProcessed returns false when DB closed');
|
|
40
|
+
});
|
|
43
41
|
|
|
44
42
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
45
43
|
// memory-store: CRUD operations
|
|
46
44
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
47
45
|
|
|
48
|
-
|
|
49
|
-
{
|
|
46
|
+
test('memory-store: create and query memories', () => {
|
|
50
47
|
openDatabase(':memory:');
|
|
51
48
|
|
|
52
49
|
// Create memories
|
|
53
50
|
const id1 = createMemory({ category: 'gotcha', content: 'esbuild drops .node binaries' });
|
|
54
|
-
|
|
55
|
-
|
|
51
|
+
assert.ok(id1 !== null, 'createMemory should return an ID');
|
|
52
|
+
assert.deepStrictEqual(id1, 'MEM001', 'first memory ID should be MEM001');
|
|
56
53
|
|
|
57
54
|
const id2 = createMemory({ category: 'convention', content: 'use :memory: for tests', confidence: 0.9 });
|
|
58
|
-
|
|
55
|
+
assert.deepStrictEqual(id2, 'MEM002', 'second memory ID should be MEM002');
|
|
59
56
|
|
|
60
57
|
const id3 = createMemory({ category: 'architecture', content: 'extensions discovered from src/resources/' });
|
|
61
|
-
|
|
58
|
+
assert.deepStrictEqual(id3, 'MEM003', 'third memory ID should be MEM003');
|
|
62
59
|
|
|
63
60
|
// Query all active
|
|
64
61
|
const active = getActiveMemories();
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
62
|
+
assert.deepStrictEqual(active.length, 3, 'should have 3 active memories');
|
|
63
|
+
assert.deepStrictEqual(active[0].category, 'gotcha', 'first memory category');
|
|
64
|
+
assert.deepStrictEqual(active[0].content, 'esbuild drops .node binaries', 'first memory content');
|
|
65
|
+
assert.deepStrictEqual(active[1].confidence, 0.9, 'second memory confidence');
|
|
69
66
|
|
|
70
67
|
closeDatabase();
|
|
71
|
-
}
|
|
68
|
+
});
|
|
72
69
|
|
|
73
70
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
74
71
|
// memory-store: update and reinforce
|
|
75
72
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
76
73
|
|
|
77
|
-
|
|
78
|
-
{
|
|
74
|
+
test('memory-store: update and reinforce', () => {
|
|
79
75
|
openDatabase(':memory:');
|
|
80
76
|
|
|
81
77
|
createMemory({ category: 'gotcha', content: 'original content' });
|
|
82
78
|
|
|
83
79
|
// Update content
|
|
84
80
|
const updated = updateMemoryContent('MEM001', 'revised content', 0.95);
|
|
85
|
-
|
|
81
|
+
assert.ok(updated, 'updateMemoryContent should return true');
|
|
86
82
|
|
|
87
83
|
const active = getActiveMemories();
|
|
88
|
-
|
|
89
|
-
|
|
84
|
+
assert.deepStrictEqual(active[0].content, 'revised content', 'content should be updated');
|
|
85
|
+
assert.deepStrictEqual(active[0].confidence, 0.95, 'confidence should be updated');
|
|
90
86
|
|
|
91
87
|
// Reinforce
|
|
92
88
|
const reinforced = reinforceMemory('MEM001');
|
|
93
|
-
|
|
89
|
+
assert.ok(reinforced, 'reinforceMemory should return true');
|
|
94
90
|
|
|
95
91
|
const after = getActiveMemories();
|
|
96
|
-
|
|
92
|
+
assert.deepStrictEqual(after[0].hit_count, 1, 'hit_count should be 1 after reinforce');
|
|
97
93
|
|
|
98
94
|
// Reinforce again
|
|
99
95
|
reinforceMemory('MEM001');
|
|
100
96
|
const after2 = getActiveMemories();
|
|
101
|
-
|
|
97
|
+
assert.deepStrictEqual(after2[0].hit_count, 2, 'hit_count should be 2 after second reinforce');
|
|
102
98
|
|
|
103
99
|
closeDatabase();
|
|
104
|
-
}
|
|
100
|
+
});
|
|
105
101
|
|
|
106
102
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
107
103
|
// memory-store: supersede
|
|
108
104
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
109
105
|
|
|
110
|
-
|
|
111
|
-
{
|
|
106
|
+
test('memory-store: supersede', () => {
|
|
112
107
|
openDatabase(':memory:');
|
|
113
108
|
|
|
114
109
|
createMemory({ category: 'convention', content: 'old convention' });
|
|
@@ -117,18 +112,17 @@ console.log('\n=== memory-store: supersede ===');
|
|
|
117
112
|
supersedeMemory('MEM001', 'MEM002');
|
|
118
113
|
|
|
119
114
|
const active = getActiveMemories();
|
|
120
|
-
|
|
121
|
-
|
|
115
|
+
assert.deepStrictEqual(active.length, 1, 'should have 1 active memory after supersede');
|
|
116
|
+
assert.deepStrictEqual(active[0].id, 'MEM002', 'active memory should be MEM002');
|
|
122
117
|
|
|
123
118
|
closeDatabase();
|
|
124
|
-
}
|
|
119
|
+
});
|
|
125
120
|
|
|
126
121
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
127
122
|
// memory-store: ranked query ordering
|
|
128
123
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
129
124
|
|
|
130
|
-
|
|
131
|
-
{
|
|
125
|
+
test('memory-store: ranked query ordering', () => {
|
|
132
126
|
openDatabase(':memory:');
|
|
133
127
|
|
|
134
128
|
// Low confidence, no hits
|
|
@@ -142,45 +136,43 @@ console.log('\n=== memory-store: ranked query ordering ===');
|
|
|
142
136
|
for (let i = 0; i < 10; i++) reinforceMemory('MEM003');
|
|
143
137
|
|
|
144
138
|
const ranked = getActiveMemoriesRanked(10);
|
|
145
|
-
|
|
139
|
+
assert.deepStrictEqual(ranked.length, 3, 'should have 3 ranked memories');
|
|
146
140
|
// MEM003: 0.7 * (1 + 10*0.1) = 0.7 * 2.0 = 1.4
|
|
147
141
|
// MEM002: 0.95 * (1 + 0*0.1) = 0.95
|
|
148
142
|
// MEM001: 0.5 * (1 + 0*0.1) = 0.5
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
143
|
+
assert.deepStrictEqual(ranked[0].id, 'MEM003', 'highest ranked should be MEM003 (reinforced)');
|
|
144
|
+
assert.deepStrictEqual(ranked[1].id, 'MEM002', 'second ranked should be MEM002 (high confidence)');
|
|
145
|
+
assert.deepStrictEqual(ranked[2].id, 'MEM001', 'lowest ranked should be MEM001');
|
|
152
146
|
|
|
153
147
|
// Test limit
|
|
154
148
|
const limited = getActiveMemoriesRanked(2);
|
|
155
|
-
|
|
149
|
+
assert.deepStrictEqual(limited.length, 2, 'limit should cap results');
|
|
156
150
|
|
|
157
151
|
closeDatabase();
|
|
158
|
-
}
|
|
152
|
+
});
|
|
159
153
|
|
|
160
154
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
161
155
|
// memory-store: processed unit tracking
|
|
162
156
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
163
157
|
|
|
164
|
-
|
|
165
|
-
{
|
|
158
|
+
test('memory-store: processed unit tracking', () => {
|
|
166
159
|
openDatabase(':memory:');
|
|
167
160
|
|
|
168
|
-
|
|
161
|
+
assert.ok(!isUnitProcessed('execute-task/M001/S01/T01'), 'should not be processed initially');
|
|
169
162
|
|
|
170
163
|
markUnitProcessed('execute-task/M001/S01/T01', '/path/to/activity.jsonl');
|
|
171
164
|
|
|
172
|
-
|
|
173
|
-
|
|
165
|
+
assert.ok(isUnitProcessed('execute-task/M001/S01/T01'), 'should be processed after marking');
|
|
166
|
+
assert.ok(!isUnitProcessed('execute-task/M001/S01/T02'), 'different key should not be processed');
|
|
174
167
|
|
|
175
168
|
closeDatabase();
|
|
176
|
-
}
|
|
169
|
+
});
|
|
177
170
|
|
|
178
171
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
179
172
|
// memory-store: enforce memory cap
|
|
180
173
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
181
174
|
|
|
182
|
-
|
|
183
|
-
{
|
|
175
|
+
test('memory-store: enforce memory cap', () => {
|
|
184
176
|
openDatabase(':memory:');
|
|
185
177
|
|
|
186
178
|
// Create 5 memories with varying confidence
|
|
@@ -194,23 +186,22 @@ console.log('\n=== memory-store: enforce memory cap ===');
|
|
|
194
186
|
enforceMemoryCap(3);
|
|
195
187
|
|
|
196
188
|
const active = getActiveMemories();
|
|
197
|
-
|
|
189
|
+
assert.deepStrictEqual(active.length, 3, 'should have 3 active memories after cap enforcement');
|
|
198
190
|
|
|
199
191
|
// The 2 lowest-ranked (MEM003=0.3 and MEM002=0.5) should be superseded
|
|
200
192
|
const ids = active.map(m => m.id).sort();
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
193
|
+
assert.ok(ids.includes('MEM001'), 'MEM001 (0.9) should survive');
|
|
194
|
+
assert.ok(ids.includes('MEM004'), 'MEM004 (0.95) should survive');
|
|
195
|
+
assert.ok(ids.includes('MEM005'), 'MEM005 (0.7) should survive');
|
|
204
196
|
|
|
205
197
|
closeDatabase();
|
|
206
|
-
}
|
|
198
|
+
});
|
|
207
199
|
|
|
208
200
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
209
201
|
// memory-store: applyMemoryActions transaction
|
|
210
202
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
211
203
|
|
|
212
|
-
|
|
213
|
-
{
|
|
204
|
+
test('memory-store: applyMemoryActions', () => {
|
|
214
205
|
openDatabase(':memory:');
|
|
215
206
|
|
|
216
207
|
const actions: MemoryAction[] = [
|
|
@@ -221,7 +212,7 @@ console.log('\n=== memory-store: applyMemoryActions ===');
|
|
|
221
212
|
applyMemoryActions(actions, 'execute-task', 'M001/S01/T01');
|
|
222
213
|
|
|
223
214
|
let active = getActiveMemories();
|
|
224
|
-
|
|
215
|
+
assert.deepStrictEqual(active.length, 2, 'should have 2 memories after CREATE actions');
|
|
225
216
|
|
|
226
217
|
// Now apply UPDATE + REINFORCE
|
|
227
218
|
const updateActions: MemoryAction[] = [
|
|
@@ -232,8 +223,8 @@ console.log('\n=== memory-store: applyMemoryActions ===');
|
|
|
232
223
|
applyMemoryActions(updateActions, 'execute-task', 'M001/S01/T02');
|
|
233
224
|
|
|
234
225
|
active = getActiveMemories();
|
|
235
|
-
|
|
236
|
-
|
|
226
|
+
assert.deepStrictEqual(active.find(m => m.id === 'MEM001')?.content, 'updated gotcha', 'MEM001 should be updated');
|
|
227
|
+
assert.deepStrictEqual(active.find(m => m.id === 'MEM002')?.hit_count, 1, 'MEM002 should be reinforced');
|
|
237
228
|
|
|
238
229
|
// SUPERSEDE
|
|
239
230
|
const supersedeActions: MemoryAction[] = [
|
|
@@ -244,19 +235,18 @@ console.log('\n=== memory-store: applyMemoryActions ===');
|
|
|
244
235
|
applyMemoryActions(supersedeActions, 'execute-task', 'M001/S01/T03');
|
|
245
236
|
|
|
246
237
|
active = getActiveMemories();
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
238
|
+
assert.deepStrictEqual(active.length, 2, 'should have 2 active after supersede');
|
|
239
|
+
assert.ok(!active.find(m => m.id === 'MEM001'), 'MEM001 should be superseded');
|
|
240
|
+
assert.ok(!!active.find(m => m.id === 'MEM003'), 'MEM003 should be active');
|
|
250
241
|
|
|
251
242
|
closeDatabase();
|
|
252
|
-
}
|
|
243
|
+
});
|
|
253
244
|
|
|
254
245
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
255
246
|
// memory-store: formatMemoriesForPrompt
|
|
256
247
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
257
248
|
|
|
258
|
-
|
|
259
|
-
{
|
|
249
|
+
test('memory-store: formatMemoriesForPrompt', () => {
|
|
260
250
|
openDatabase(':memory:');
|
|
261
251
|
|
|
262
252
|
createMemory({ category: 'gotcha', content: 'esbuild drops .node binaries' });
|
|
@@ -267,18 +257,18 @@ console.log('\n=== memory-store: formatMemoriesForPrompt ===');
|
|
|
267
257
|
const memories = getActiveMemoriesRanked(30);
|
|
268
258
|
const formatted = formatMemoriesForPrompt(memories);
|
|
269
259
|
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
260
|
+
assert.ok(formatted.includes('## Project Memory (auto-learned)'), 'should have header');
|
|
261
|
+
assert.ok(formatted.includes('### Gotcha'), 'should have gotcha category');
|
|
262
|
+
assert.ok(formatted.includes('### Convention'), 'should have convention category');
|
|
263
|
+
assert.ok(formatted.includes('### Architecture'), 'should have architecture category');
|
|
264
|
+
assert.ok(formatted.includes('- esbuild drops .node binaries'), 'should have gotcha content');
|
|
265
|
+
assert.ok(formatted.includes('- use :memory: for tests'), 'should have convention content');
|
|
276
266
|
|
|
277
267
|
// Test empty memories
|
|
278
268
|
closeDatabase();
|
|
279
269
|
openDatabase(':memory:');
|
|
280
270
|
const emptyFormatted = formatMemoriesForPrompt([]);
|
|
281
|
-
|
|
271
|
+
assert.deepStrictEqual(emptyFormatted, '', 'empty memories should return empty string');
|
|
282
272
|
|
|
283
273
|
// Test token budget truncation
|
|
284
274
|
closeDatabase();
|
|
@@ -288,58 +278,55 @@ console.log('\n=== memory-store: formatMemoriesForPrompt ===');
|
|
|
288
278
|
}
|
|
289
279
|
const budgetMemories = getActiveMemoriesRanked(30);
|
|
290
280
|
const truncated = formatMemoriesForPrompt(budgetMemories, 500);
|
|
291
|
-
|
|
281
|
+
assert.ok(truncated.length < 2500, `formatted length ${truncated.length} should be under budget`);
|
|
292
282
|
|
|
293
283
|
closeDatabase();
|
|
294
|
-
}
|
|
284
|
+
});
|
|
295
285
|
|
|
296
286
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
297
287
|
// memory-store: ID generation
|
|
298
288
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
299
289
|
|
|
300
|
-
|
|
301
|
-
{
|
|
290
|
+
test('memory-store: ID generation', () => {
|
|
302
291
|
openDatabase(':memory:');
|
|
303
292
|
|
|
304
|
-
|
|
293
|
+
assert.deepStrictEqual(nextMemoryId(), 'MEM001', 'first ID should be MEM001');
|
|
305
294
|
|
|
306
295
|
createMemory({ category: 'test', content: 'test' });
|
|
307
|
-
|
|
296
|
+
assert.deepStrictEqual(nextMemoryId(), 'MEM002', 'after first create, next should be MEM002');
|
|
308
297
|
|
|
309
298
|
// Create several more
|
|
310
299
|
for (let i = 0; i < 98; i++) createMemory({ category: 'test', content: `test ${i}` });
|
|
311
|
-
|
|
300
|
+
assert.deepStrictEqual(nextMemoryId(), 'MEM100', 'after 99 creates, next should be MEM100');
|
|
312
301
|
|
|
313
302
|
closeDatabase();
|
|
314
|
-
}
|
|
303
|
+
});
|
|
315
304
|
|
|
316
305
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
317
306
|
// memory-store: schema migration (v2 → v3)
|
|
318
307
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
319
308
|
|
|
320
|
-
|
|
321
|
-
{
|
|
309
|
+
test('memory-store: schema includes memories table', () => {
|
|
322
310
|
openDatabase(':memory:');
|
|
323
311
|
|
|
324
312
|
const adapter = _getAdapter()!;
|
|
325
313
|
|
|
326
314
|
// Verify memories table exists
|
|
327
315
|
const memCount = adapter.prepare('SELECT count(*) as cnt FROM memories').get();
|
|
328
|
-
|
|
316
|
+
assert.deepStrictEqual(memCount?.['cnt'], 0, 'memories table should exist and be empty');
|
|
329
317
|
|
|
330
318
|
// Verify memory_processed_units table exists
|
|
331
319
|
const procCount = adapter.prepare('SELECT count(*) as cnt FROM memory_processed_units').get();
|
|
332
|
-
|
|
320
|
+
assert.deepStrictEqual(procCount?.['cnt'], 0, 'memory_processed_units table should exist and be empty');
|
|
333
321
|
|
|
334
322
|
// Verify active_memories view exists
|
|
335
323
|
const viewCount = adapter.prepare('SELECT count(*) as cnt FROM active_memories').get();
|
|
336
|
-
|
|
324
|
+
assert.deepStrictEqual(viewCount?.['cnt'], 0, 'active_memories view should exist');
|
|
337
325
|
|
|
338
326
|
// Verify schema version is 10 (after M001 planning migrations)
|
|
339
327
|
const version = adapter.prepare('SELECT MAX(version) as v FROM schema_version').get();
|
|
340
|
-
|
|
328
|
+
assert.deepStrictEqual(version?.['v'], 10, 'schema version should be 10');
|
|
341
329
|
|
|
342
330
|
closeDatabase();
|
|
343
|
-
}
|
|
331
|
+
});
|
|
344
332
|
|
|
345
|
-
report();
|
|
@@ -15,9 +15,9 @@ import {
|
|
|
15
15
|
writeGSDDirectory,
|
|
16
16
|
} from '../migrate/index.ts';
|
|
17
17
|
import { deriveState } from '../state.ts';
|
|
18
|
-
import {
|
|
18
|
+
import { describe, test, beforeEach, afterEach } from 'node:test';
|
|
19
|
+
import assert from 'node:assert/strict';
|
|
19
20
|
|
|
20
|
-
const { assertEq, assertTrue, report } = createTestContext();
|
|
21
21
|
// ─── Fixture Helpers ───────────────────────────────────────────────────────
|
|
22
22
|
|
|
23
23
|
const SAMPLE_PROJECT = `# Integration Test Project
|
|
@@ -195,11 +195,9 @@ function createCompleteFixture(): string {
|
|
|
195
195
|
// Tests
|
|
196
196
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
197
197
|
|
|
198
|
-
async function main(): Promise<void> {
|
|
199
|
-
|
|
200
198
|
// ─── Test 1: Path resolution — .planning appended when missing ─────────
|
|
201
|
-
|
|
202
|
-
|
|
199
|
+
|
|
200
|
+
test('Path resolution: .planning appended when source path lacks it', () => {
|
|
203
201
|
const base = createCompleteFixture();
|
|
204
202
|
try {
|
|
205
203
|
// Simulate the command's path resolution logic
|
|
@@ -207,16 +205,16 @@ async function main(): Promise<void> {
|
|
|
207
205
|
if (!sourcePath.endsWith('.planning')) {
|
|
208
206
|
sourcePath = join(sourcePath, '.planning');
|
|
209
207
|
}
|
|
210
|
-
|
|
211
|
-
|
|
208
|
+
assert.ok(sourcePath.endsWith('.planning'), 'path-resolution: .planning appended');
|
|
209
|
+
assert.ok(existsSync(sourcePath), 'path-resolution: appended path exists');
|
|
212
210
|
} finally {
|
|
213
211
|
rmSync(base, { recursive: true, force: true });
|
|
214
212
|
}
|
|
215
|
-
|
|
213
|
+
});
|
|
216
214
|
|
|
217
215
|
// ─── Test 2: Path resolution — .planning used as-is ────────────────────
|
|
218
|
-
|
|
219
|
-
|
|
216
|
+
|
|
217
|
+
test('Path resolution: .planning used as-is when already present', () => {
|
|
220
218
|
const base = createCompleteFixture();
|
|
221
219
|
try {
|
|
222
220
|
const planningPath = join(base, '.planning');
|
|
@@ -224,39 +222,39 @@ async function main(): Promise<void> {
|
|
|
224
222
|
if (!sourcePath.endsWith('.planning')) {
|
|
225
223
|
sourcePath = join(sourcePath, '.planning');
|
|
226
224
|
}
|
|
227
|
-
|
|
228
|
-
|
|
225
|
+
assert.deepStrictEqual(sourcePath, resolve(planningPath), 'path-resolution: .planning not double-appended');
|
|
226
|
+
assert.ok(existsSync(sourcePath), 'path-resolution: direct path exists');
|
|
229
227
|
} finally {
|
|
230
228
|
rmSync(base, { recursive: true, force: true });
|
|
231
229
|
}
|
|
232
|
-
|
|
230
|
+
});
|
|
233
231
|
|
|
234
232
|
// ─── Test 3: Validation gating — non-existent path ─────────────────────
|
|
235
|
-
|
|
236
|
-
|
|
233
|
+
|
|
234
|
+
test('Validation gating: non-existent path returns invalid', async () => {
|
|
237
235
|
const fakePath = join(tmpdir(), 'gsd-cmd-nonexistent-' + Date.now(), '.planning');
|
|
238
236
|
const result = await validatePlanningDirectory(fakePath);
|
|
239
|
-
|
|
240
|
-
|
|
237
|
+
assert.deepStrictEqual(result.valid, false, 'validation: non-existent path is invalid');
|
|
238
|
+
assert.ok(result.issues.length > 0, 'validation: has issues for non-existent path');
|
|
241
239
|
const hasFatal = result.issues.some(i => i.severity === 'fatal');
|
|
242
|
-
|
|
243
|
-
|
|
240
|
+
assert.ok(hasFatal, 'validation: non-existent path has fatal issue');
|
|
241
|
+
});
|
|
244
242
|
|
|
245
243
|
// ─── Test 4: Validation gating — valid fixture passes ──────────────────
|
|
246
|
-
|
|
247
|
-
|
|
244
|
+
|
|
245
|
+
test('Validation gating: valid fixture passes validation', async () => {
|
|
248
246
|
const base = createCompleteFixture();
|
|
249
247
|
try {
|
|
250
248
|
const result = await validatePlanningDirectory(join(base, '.planning'));
|
|
251
|
-
|
|
249
|
+
assert.ok(result.valid === true, 'validation: valid fixture passes');
|
|
252
250
|
} finally {
|
|
253
251
|
rmSync(base, { recursive: true, force: true });
|
|
254
252
|
}
|
|
255
|
-
|
|
253
|
+
});
|
|
256
254
|
|
|
257
255
|
// ─── Test 5: Full pipeline round-trip ──────────────────────────────────
|
|
258
|
-
|
|
259
|
-
|
|
256
|
+
|
|
257
|
+
test('Full pipeline: parse → transform → preview → write → deriveState', async () => {
|
|
260
258
|
const base = createCompleteFixture();
|
|
261
259
|
const writeTarget = mkdtempSync(join(tmpdir(), 'gsd-cmd-write-'));
|
|
262
260
|
try {
|
|
@@ -264,17 +262,17 @@ async function main(): Promise<void> {
|
|
|
264
262
|
|
|
265
263
|
// (a) Validate
|
|
266
264
|
const validation = await validatePlanningDirectory(planningPath);
|
|
267
|
-
|
|
265
|
+
assert.ok(validation.valid === true, 'pipeline: validation passes');
|
|
268
266
|
|
|
269
267
|
// (b) Parse
|
|
270
268
|
const parsed = await parsePlanningDirectory(planningPath);
|
|
271
|
-
|
|
272
|
-
|
|
269
|
+
assert.ok(parsed.roadmap !== null, 'pipeline: roadmap parsed');
|
|
270
|
+
assert.ok(Object.keys(parsed.phases).length >= 2, 'pipeline: phases parsed');
|
|
273
271
|
|
|
274
272
|
// (c) Transform
|
|
275
273
|
const project = transformToGSD(parsed);
|
|
276
|
-
|
|
277
|
-
|
|
274
|
+
assert.ok(project.milestones.length >= 1, 'pipeline: has milestones');
|
|
275
|
+
assert.ok(project.milestones[0].slices.length >= 1, 'pipeline: has slices');
|
|
278
276
|
|
|
279
277
|
// Count totals for preview verification
|
|
280
278
|
let totalTasks = 0;
|
|
@@ -294,76 +292,69 @@ async function main(): Promise<void> {
|
|
|
294
292
|
|
|
295
293
|
// (d) Preview — verify counts match project data
|
|
296
294
|
const preview = generatePreview(project);
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
295
|
+
assert.deepStrictEqual(preview.milestoneCount, project.milestones.length, 'pipeline: preview milestoneCount');
|
|
296
|
+
assert.deepStrictEqual(preview.totalSlices, totalSlices, 'pipeline: preview totalSlices');
|
|
297
|
+
assert.deepStrictEqual(preview.totalTasks, totalTasks, 'pipeline: preview totalTasks');
|
|
298
|
+
assert.deepStrictEqual(preview.doneSlices, doneSlices, 'pipeline: preview doneSlices');
|
|
299
|
+
assert.deepStrictEqual(preview.doneTasks, doneTasks, 'pipeline: preview doneTasks');
|
|
302
300
|
|
|
303
301
|
// Completion percentages
|
|
304
302
|
const expectedSlicePct = totalSlices > 0 ? Math.round((doneSlices / totalSlices) * 100) : 0;
|
|
305
303
|
const expectedTaskPct = totalTasks > 0 ? Math.round((doneTasks / totalTasks) * 100) : 0;
|
|
306
|
-
|
|
307
|
-
|
|
304
|
+
assert.deepStrictEqual(preview.sliceCompletionPct, expectedSlicePct, 'pipeline: preview sliceCompletionPct');
|
|
305
|
+
assert.deepStrictEqual(preview.taskCompletionPct, expectedTaskPct, 'pipeline: preview taskCompletionPct');
|
|
308
306
|
|
|
309
307
|
// Requirements in preview
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
308
|
+
assert.deepStrictEqual(preview.requirements.active, 1, 'pipeline: preview requirements active');
|
|
309
|
+
assert.deepStrictEqual(preview.requirements.validated, 1, 'pipeline: preview requirements validated');
|
|
310
|
+
assert.deepStrictEqual(preview.requirements.total, 2, 'pipeline: preview requirements total');
|
|
313
311
|
|
|
314
312
|
// (e) Write
|
|
315
313
|
const result = await writeGSDDirectory(project, writeTarget);
|
|
316
|
-
|
|
314
|
+
assert.ok(result.paths.length > 0, 'pipeline: files written');
|
|
317
315
|
|
|
318
316
|
// Key files exist
|
|
319
317
|
const gsd = join(writeTarget, '.gsd');
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
318
|
+
assert.ok(existsSync(join(gsd, 'PROJECT.md')), 'pipeline: PROJECT.md written');
|
|
319
|
+
assert.ok(existsSync(join(gsd, 'STATE.md')), 'pipeline: STATE.md written');
|
|
320
|
+
assert.ok(existsSync(join(gsd, 'REQUIREMENTS.md')), 'pipeline: REQUIREMENTS.md written');
|
|
323
321
|
|
|
324
322
|
const m001 = join(gsd, 'milestones', 'M001');
|
|
325
|
-
|
|
326
|
-
|
|
323
|
+
assert.ok(existsSync(join(m001, 'M001-ROADMAP.md')), 'pipeline: M001-ROADMAP.md written');
|
|
324
|
+
assert.ok(existsSync(join(m001, 'M001-CONTEXT.md')), 'pipeline: M001-CONTEXT.md written');
|
|
327
325
|
|
|
328
326
|
// At least one slice plan exists
|
|
329
327
|
const s01Plan = join(m001, 'slices', 'S01', 'S01-PLAN.md');
|
|
330
|
-
|
|
328
|
+
assert.ok(existsSync(s01Plan), 'pipeline: S01-PLAN.md written');
|
|
331
329
|
|
|
332
330
|
// (f) deriveState — coherent state from written output
|
|
333
331
|
console.log(' --- deriveState ---');
|
|
334
332
|
const state = await deriveState(writeTarget);
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
333
|
+
assert.ok(state.phase !== undefined, 'pipeline: deriveState returns phase');
|
|
334
|
+
assert.ok(state.activeMilestone !== null, 'pipeline: deriveState has activeMilestone');
|
|
335
|
+
assert.deepStrictEqual(state.activeMilestone!.id, 'M001', 'pipeline: deriveState activeMilestone is M001');
|
|
336
|
+
assert.ok(state.progress!.slices !== undefined, 'pipeline: deriveState has slices progress');
|
|
337
|
+
assert.ok(state.progress!.tasks !== undefined, 'pipeline: deriveState has tasks progress');
|
|
340
338
|
|
|
341
339
|
} finally {
|
|
342
340
|
rmSync(base, { recursive: true, force: true });
|
|
343
341
|
rmSync(writeTarget, { recursive: true, force: true });
|
|
344
342
|
}
|
|
345
|
-
|
|
343
|
+
});
|
|
346
344
|
|
|
347
345
|
// ─── Test 6: .gsd/ exists detection ────────────────────────────────────
|
|
348
|
-
|
|
349
|
-
|
|
346
|
+
|
|
347
|
+
test('.gsd/ exists detection', () => {
|
|
350
348
|
const base = mkdtempSync(join(tmpdir(), 'gsd-cmd-exists-'));
|
|
351
349
|
try {
|
|
352
350
|
// No .gsd/ yet
|
|
353
|
-
|
|
351
|
+
assert.ok(!existsSync(join(base, '.gsd')), 'exists-detection: .gsd absent initially');
|
|
354
352
|
|
|
355
353
|
// Create .gsd/
|
|
356
354
|
mkdirSync(join(base, '.gsd'), { recursive: true });
|
|
357
|
-
|
|
355
|
+
assert.ok(existsSync(join(base, '.gsd')), 'exists-detection: .gsd detected after creation');
|
|
358
356
|
} finally {
|
|
359
357
|
rmSync(base, { recursive: true, force: true });
|
|
360
358
|
}
|
|
361
|
-
}
|
|
362
|
-
|
|
363
|
-
report();
|
|
364
|
-
}
|
|
365
|
-
|
|
366
|
-
main().catch((err) => {
|
|
367
|
-
console.error('Unhandled error:', err);
|
|
368
|
-
process.exit(1);
|
|
369
359
|
});
|
|
360
|
+
|