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 * as fs from 'node:fs';
|
|
3
2
|
import * as path from 'node:path';
|
|
4
3
|
import * as os from 'node:os';
|
|
@@ -17,8 +16,8 @@ import {
|
|
|
17
16
|
parseRequirementsSections,
|
|
18
17
|
migrateFromMarkdown,
|
|
19
18
|
} from '../md-importer.ts';
|
|
20
|
-
|
|
21
|
-
|
|
19
|
+
import { describe, test, beforeEach, afterEach } from 'node:test';
|
|
20
|
+
import assert from 'node:assert/strict';
|
|
22
21
|
|
|
23
22
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
24
23
|
// Fixtures
|
|
@@ -135,43 +134,37 @@ function cleanupDir(dir: string): void {
|
|
|
135
134
|
// md-importer: parseDecisionsTable
|
|
136
135
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
137
136
|
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
{
|
|
137
|
+
test('md-importer: parseDecisionsTable', () => {
|
|
141
138
|
const decisions = parseDecisionsTable(DECISIONS_MD);
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
{
|
|
139
|
+
assert.deepStrictEqual(decisions.length, 4, 'should parse 4 decisions');
|
|
140
|
+
assert.deepStrictEqual(decisions[0].id, 'D001', 'first decision should be D001');
|
|
141
|
+
assert.deepStrictEqual(decisions[0].decision, 'SQLite library', 'D001 decision text');
|
|
142
|
+
assert.deepStrictEqual(decisions[0].choice, 'better-sqlite3', 'D001 choice');
|
|
143
|
+
assert.deepStrictEqual(decisions[0].scope, 'library', 'D001 scope');
|
|
144
|
+
assert.deepStrictEqual(decisions[0].revisable, 'No', 'D001 revisable');
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
test('md-importer: supersession detection', () => {
|
|
153
148
|
const decisions = parseDecisionsTable(DECISIONS_MD);
|
|
154
149
|
|
|
155
150
|
// D010 amends D001 → D001.superseded_by = D010
|
|
156
151
|
const d001 = decisions.find(d => d.id === 'D001');
|
|
157
|
-
|
|
152
|
+
assert.deepStrictEqual(d001?.superseded_by, 'D010', 'D001 should be superseded by D010');
|
|
158
153
|
|
|
159
154
|
// D020 amends D010 → D010.superseded_by = D020
|
|
160
155
|
const d010 = decisions.find(d => d.id === 'D010');
|
|
161
|
-
|
|
156
|
+
assert.deepStrictEqual(d010?.superseded_by, 'D020', 'D010 should be superseded by D020');
|
|
162
157
|
|
|
163
158
|
// D002 is not amended
|
|
164
159
|
const d002 = decisions.find(d => d.id === 'D002');
|
|
165
|
-
|
|
160
|
+
assert.deepStrictEqual(d002?.superseded_by, null, 'D002 should not be superseded');
|
|
166
161
|
|
|
167
162
|
// D020 is the latest in chain, not superseded
|
|
168
163
|
const d020 = decisions.find(d => d.id === 'D020');
|
|
169
|
-
|
|
170
|
-
}
|
|
164
|
+
assert.deepStrictEqual(d020?.superseded_by, null, 'D020 should not be superseded');
|
|
165
|
+
});
|
|
171
166
|
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
{
|
|
167
|
+
test('md-importer: malformed/empty rows skipped', () => {
|
|
175
168
|
const malformedInput = `# Decisions
|
|
176
169
|
|
|
177
170
|
| # | When | Scope | Decision | Choice | Rationale | Revisable? |
|
|
@@ -182,24 +175,20 @@ console.log('=== md-importer: malformed/empty rows skipped ===');
|
|
|
182
175
|
| D003 | M001 | arch | Config | JSON | Simple | Yes |
|
|
183
176
|
`;
|
|
184
177
|
const decisions = parseDecisionsTable(malformedInput);
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
console.log('=== md-importer: made_by backward compatibility (old 7-column format) ===');
|
|
178
|
+
assert.deepStrictEqual(decisions.length, 2, 'should skip rows without D-prefix IDs');
|
|
179
|
+
assert.deepStrictEqual(decisions[0].id, 'D001', 'first valid row');
|
|
180
|
+
assert.deepStrictEqual(decisions[1].id, 'D003', 'second valid row (skipping malformed)');
|
|
181
|
+
});
|
|
191
182
|
|
|
192
|
-
{
|
|
183
|
+
test('md-importer: made_by backward compatibility (old 7-column format)', () => {
|
|
193
184
|
const decisions = parseDecisionsTable(DECISIONS_MD);
|
|
194
185
|
// Old format has no Made By column — should default to 'agent'
|
|
195
186
|
for (const d of decisions) {
|
|
196
|
-
|
|
187
|
+
assert.deepStrictEqual(d.made_by, 'agent', `${d.id} made_by defaults to agent for legacy format`);
|
|
197
188
|
}
|
|
198
|
-
}
|
|
199
|
-
|
|
200
|
-
console.log('=== md-importer: made_by column parsing (new 8-column format) ===');
|
|
189
|
+
});
|
|
201
190
|
|
|
202
|
-
{
|
|
191
|
+
test('md-importer: made_by column parsing (new 8-column format)', () => {
|
|
203
192
|
const newFormatMd = `# Decisions Register
|
|
204
193
|
|
|
205
194
|
| # | When | Scope | Decision | Choice | Rationale | Revisable? | Made By |
|
|
@@ -210,62 +199,58 @@ console.log('=== md-importer: made_by column parsing (new 8-column format) ===')
|
|
|
210
199
|
| D004 | M002 | impl | Cache strategy | LRU | Predictable | No | bogus |
|
|
211
200
|
`;
|
|
212
201
|
const decisions = parseDecisionsTable(newFormatMd);
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
}
|
|
202
|
+
assert.deepStrictEqual(decisions.length, 4, 'should parse 4 decisions with new format');
|
|
203
|
+
assert.deepStrictEqual(decisions[0].made_by, 'human', 'D001 made_by = human');
|
|
204
|
+
assert.deepStrictEqual(decisions[1].made_by, 'agent', 'D002 made_by = agent');
|
|
205
|
+
assert.deepStrictEqual(decisions[2].made_by, 'collaborative', 'D003 made_by = collaborative');
|
|
206
|
+
assert.deepStrictEqual(decisions[3].made_by, 'agent', 'D004 invalid made_by defaults to agent');
|
|
207
|
+
});
|
|
219
208
|
|
|
220
209
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
221
210
|
// md-importer: parseRequirementsSections
|
|
222
211
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
223
212
|
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
{
|
|
213
|
+
test('md-importer: parseRequirementsSections', () => {
|
|
227
214
|
const reqs = parseRequirementsSections(REQUIREMENTS_MD);
|
|
228
|
-
|
|
215
|
+
assert.deepStrictEqual(reqs.length, 5, 'should parse 5 unique requirements');
|
|
229
216
|
|
|
230
217
|
const r001 = reqs.find(r => r.id === 'R001');
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
218
|
+
assert.ok(!!r001, 'R001 should exist');
|
|
219
|
+
assert.deepStrictEqual(r001?.class, 'core-capability', 'R001 class');
|
|
220
|
+
assert.deepStrictEqual(r001?.status, 'active', 'R001 status');
|
|
221
|
+
assert.deepStrictEqual(r001?.description, 'A SQLite database with typed wrappers', 'R001 description');
|
|
222
|
+
assert.deepStrictEqual(r001?.why, 'Foundation for storage', 'R001 why');
|
|
223
|
+
assert.deepStrictEqual(r001?.source, 'user', 'R001 source');
|
|
224
|
+
assert.deepStrictEqual(r001?.primary_owner, 'M001/S01', 'R001 primary_owner');
|
|
225
|
+
assert.deepStrictEqual(r001?.supporting_slices, 'none', 'R001 supporting_slices');
|
|
226
|
+
assert.deepStrictEqual(r001?.validation, 'unmapped', 'R001 validation');
|
|
227
|
+
assert.deepStrictEqual(r001?.notes, 'WAL mode enabled', 'R001 notes');
|
|
228
|
+
assert.ok(r001?.full_content?.includes('### R001') ?? false, 'R001 full_content should have heading');
|
|
242
229
|
|
|
243
230
|
// Validated section — R017 (abbreviated format with "Validated by" / "Proof" bullets)
|
|
244
231
|
const r017 = reqs.find(r => r.id === 'R017');
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
232
|
+
assert.ok(!!r017, 'R017 should exist');
|
|
233
|
+
assert.deepStrictEqual(r017?.status, 'validated', 'R017 status from validated section');
|
|
234
|
+
assert.deepStrictEqual(r017?.validation, 'M001/S01', 'R017 validation (from "Validated by" bullet)');
|
|
235
|
+
assert.deepStrictEqual(r017?.notes, '50 decisions queried in 0.62ms', 'R017 notes (from "Proof" bullet)');
|
|
249
236
|
|
|
250
237
|
// Deferred requirement
|
|
251
238
|
const r030 = reqs.find(r => r.id === 'R030');
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
239
|
+
assert.deepStrictEqual(r030?.status, 'deferred', 'R030 status should be deferred');
|
|
240
|
+
assert.deepStrictEqual(r030?.class, 'differentiator', 'R030 class');
|
|
241
|
+
assert.deepStrictEqual(r030?.description, 'Rust crate for embeddings', 'R030 description');
|
|
255
242
|
|
|
256
243
|
// Out of scope
|
|
257
244
|
const r040 = reqs.find(r => r.id === 'R040');
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
}
|
|
245
|
+
assert.deepStrictEqual(r040?.status, 'out-of-scope', 'R040 status should be out-of-scope');
|
|
246
|
+
assert.deepStrictEqual(r040?.class, 'anti-feature', 'R040 class');
|
|
247
|
+
});
|
|
261
248
|
|
|
262
249
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
263
250
|
// md-importer: migrateFromMarkdown orchestrator
|
|
264
251
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
265
252
|
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
{
|
|
253
|
+
test('md-importer: migrateFromMarkdown orchestrator', () => {
|
|
269
254
|
const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'gsd-import-test-'));
|
|
270
255
|
createFixtureTree(tmpDir);
|
|
271
256
|
|
|
@@ -273,53 +258,51 @@ console.log('=== md-importer: migrateFromMarkdown orchestrator ===');
|
|
|
273
258
|
openDatabase(':memory:');
|
|
274
259
|
const result = migrateFromMarkdown(tmpDir);
|
|
275
260
|
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
261
|
+
assert.deepStrictEqual(result.decisions, 4, 'should import 4 decisions');
|
|
262
|
+
assert.deepStrictEqual(result.requirements, 5, 'should import 5 requirements');
|
|
263
|
+
assert.ok(result.artifacts > 0, 'should import some artifacts');
|
|
279
264
|
|
|
280
265
|
// Verify decisions queryable
|
|
281
266
|
const d001 = getDecisionById('D001');
|
|
282
|
-
|
|
283
|
-
|
|
267
|
+
assert.ok(!!d001, 'D001 should be queryable');
|
|
268
|
+
assert.deepStrictEqual(d001?.superseded_by, 'D010', 'D001 superseded_by should be D010');
|
|
284
269
|
|
|
285
270
|
// Verify requirements queryable
|
|
286
271
|
const r001 = getRequirementById('R001');
|
|
287
|
-
|
|
288
|
-
|
|
272
|
+
assert.ok(!!r001, 'R001 should be queryable');
|
|
273
|
+
assert.deepStrictEqual(r001?.status, 'active', 'R001 status from DB');
|
|
289
274
|
|
|
290
275
|
// Verify active views
|
|
291
276
|
const activeD = getActiveDecisions();
|
|
292
|
-
|
|
277
|
+
assert.deepStrictEqual(activeD.length, 2, 'should have 2 active decisions (D002, D020)');
|
|
293
278
|
|
|
294
279
|
// Verify artifacts table
|
|
295
280
|
const adapter = _getAdapter();
|
|
296
281
|
const artifacts = adapter?.prepare('SELECT count(*) as c FROM artifacts').get();
|
|
297
|
-
|
|
282
|
+
assert.ok((artifacts?.c as number) > 0, 'artifacts table should have rows');
|
|
298
283
|
|
|
299
284
|
// Verify hierarchy correctness
|
|
300
285
|
const roadmap = adapter?.prepare('SELECT * FROM artifacts WHERE artifact_type = :type').get({ ':type': 'ROADMAP' });
|
|
301
|
-
|
|
302
|
-
|
|
286
|
+
assert.ok(!!roadmap, 'ROADMAP artifact should exist');
|
|
287
|
+
assert.deepStrictEqual(roadmap?.milestone_id, 'M001', 'ROADMAP should be in M001');
|
|
303
288
|
|
|
304
289
|
const taskPlan = adapter?.prepare('SELECT * FROM artifacts WHERE task_id = :taskId AND artifact_type = :type').get({
|
|
305
290
|
':taskId': 'T01',
|
|
306
291
|
':type': 'PLAN',
|
|
307
292
|
});
|
|
308
|
-
|
|
293
|
+
assert.ok(!!taskPlan, 'T01-PLAN artifact should exist');
|
|
309
294
|
|
|
310
295
|
closeDatabase();
|
|
311
296
|
} finally {
|
|
312
297
|
cleanupDir(tmpDir);
|
|
313
298
|
}
|
|
314
|
-
}
|
|
299
|
+
});
|
|
315
300
|
|
|
316
301
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
317
302
|
// md-importer: idempotent re-import
|
|
318
303
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
319
304
|
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
{
|
|
305
|
+
test('md-importer: idempotent re-import', () => {
|
|
323
306
|
const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'gsd-idemp-test-'));
|
|
324
307
|
createFixtureTree(tmpDir);
|
|
325
308
|
|
|
@@ -328,9 +311,9 @@ console.log('=== md-importer: idempotent re-import ===');
|
|
|
328
311
|
const r1 = migrateFromMarkdown(tmpDir);
|
|
329
312
|
const r2 = migrateFromMarkdown(tmpDir);
|
|
330
313
|
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
314
|
+
assert.deepStrictEqual(r1.decisions, r2.decisions, 'double import should produce same decision count');
|
|
315
|
+
assert.deepStrictEqual(r1.requirements, r2.requirements, 'double import should produce same requirement count');
|
|
316
|
+
assert.deepStrictEqual(r1.artifacts, r2.artifacts, 'double import should produce same artifact count');
|
|
334
317
|
|
|
335
318
|
// Verify no duplicates
|
|
336
319
|
const adapter = _getAdapter();
|
|
@@ -338,23 +321,21 @@ console.log('=== md-importer: idempotent re-import ===');
|
|
|
338
321
|
const rc = adapter?.prepare('SELECT count(*) as c FROM requirements').get()?.c as number;
|
|
339
322
|
const ac = adapter?.prepare('SELECT count(*) as c FROM artifacts').get()?.c as number;
|
|
340
323
|
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
324
|
+
assert.deepStrictEqual(dc, r1.decisions, 'DB decision count matches import count');
|
|
325
|
+
assert.deepStrictEqual(rc, r1.requirements, 'DB requirement count matches import count');
|
|
326
|
+
assert.deepStrictEqual(ac, r1.artifacts, 'DB artifact count matches import count');
|
|
344
327
|
|
|
345
328
|
closeDatabase();
|
|
346
329
|
} finally {
|
|
347
330
|
cleanupDir(tmpDir);
|
|
348
331
|
}
|
|
349
|
-
}
|
|
332
|
+
});
|
|
350
333
|
|
|
351
334
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
352
335
|
// md-importer: missing file graceful handling
|
|
353
336
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
354
337
|
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
{
|
|
338
|
+
test('md-importer: missing file handling', () => {
|
|
358
339
|
const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'gsd-empty-test-'));
|
|
359
340
|
// Create empty .gsd/ with no files
|
|
360
341
|
fs.mkdirSync(path.join(tmpDir, '.gsd'), { recursive: true });
|
|
@@ -363,43 +344,39 @@ console.log('=== md-importer: missing file handling ===');
|
|
|
363
344
|
openDatabase(':memory:');
|
|
364
345
|
const result = migrateFromMarkdown(tmpDir);
|
|
365
346
|
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
347
|
+
assert.deepStrictEqual(result.decisions, 0, 'missing DECISIONS.md → 0 decisions');
|
|
348
|
+
assert.deepStrictEqual(result.requirements, 0, 'missing REQUIREMENTS.md → 0 requirements');
|
|
349
|
+
assert.deepStrictEqual(result.artifacts, 0, 'empty tree → 0 artifacts');
|
|
369
350
|
|
|
370
351
|
closeDatabase();
|
|
371
352
|
} finally {
|
|
372
353
|
cleanupDir(tmpDir);
|
|
373
354
|
}
|
|
374
|
-
}
|
|
355
|
+
});
|
|
375
356
|
|
|
376
357
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
377
358
|
// md-importer: schema v1→v2 migration on existing DBs
|
|
378
359
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
379
360
|
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
{
|
|
361
|
+
test('md-importer: schema v1→v2 migration', () => {
|
|
383
362
|
// This test verifies that opening a fresh DB auto-migrates to current schema version
|
|
384
363
|
openDatabase(':memory:');
|
|
385
364
|
const adapter = _getAdapter();
|
|
386
365
|
const version = adapter?.prepare('SELECT MAX(version) as v FROM schema_version').get();
|
|
387
|
-
|
|
366
|
+
assert.deepStrictEqual(version?.v, 10, 'new DB should be at schema version 10');
|
|
388
367
|
|
|
389
368
|
// Artifacts table should exist
|
|
390
369
|
const tableCheck = adapter?.prepare("SELECT count(*) as c FROM sqlite_master WHERE type='table' AND name='artifacts'").get();
|
|
391
|
-
|
|
370
|
+
assert.deepStrictEqual(tableCheck?.c, 1, 'artifacts table should exist');
|
|
392
371
|
|
|
393
372
|
closeDatabase();
|
|
394
|
-
}
|
|
373
|
+
});
|
|
395
374
|
|
|
396
375
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
397
376
|
// md-importer: round-trip fidelity
|
|
398
377
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
399
378
|
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
{
|
|
379
|
+
test('md-importer: round-trip fidelity', () => {
|
|
403
380
|
const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'gsd-roundtrip-test-'));
|
|
404
381
|
createFixtureTree(tmpDir);
|
|
405
382
|
|
|
@@ -409,32 +386,31 @@ console.log('=== md-importer: round-trip fidelity ===');
|
|
|
409
386
|
|
|
410
387
|
// Round-trip: verify imported field values match source
|
|
411
388
|
const d002 = getDecisionById('D002');
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
389
|
+
assert.deepStrictEqual(d002?.when_context, 'M001', 'D002 when_context round-trip');
|
|
390
|
+
assert.deepStrictEqual(d002?.scope, 'arch', 'D002 scope round-trip');
|
|
391
|
+
assert.deepStrictEqual(d002?.decision, 'DB location', 'D002 decision round-trip');
|
|
392
|
+
assert.deepStrictEqual(d002?.choice, '.gsd/gsd.db', 'D002 choice round-trip');
|
|
393
|
+
assert.deepStrictEqual(d002?.rationale, 'Derived state', 'D002 rationale round-trip');
|
|
417
394
|
|
|
418
395
|
const r002 = getRequirementById('R002');
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
396
|
+
assert.deepStrictEqual(r002?.class, 'failure-visibility', 'R002 class round-trip');
|
|
397
|
+
assert.deepStrictEqual(r002?.description, 'Falls back to markdown if SQLite unavailable', 'R002 description round-trip');
|
|
398
|
+
assert.deepStrictEqual(r002?.why, 'Must not break on exotic platforms', 'R002 why round-trip');
|
|
399
|
+
assert.deepStrictEqual(r002?.primary_owner, 'M001/S01', 'R002 primary_owner round-trip');
|
|
400
|
+
assert.deepStrictEqual(r002?.supporting_slices, 'M001/S03', 'R002 supporting_slices round-trip');
|
|
401
|
+
assert.deepStrictEqual(r002?.notes, 'Transparent fallback', 'R002 notes round-trip');
|
|
402
|
+
assert.deepStrictEqual(r002?.validation, 'unmapped', 'R002 validation round-trip');
|
|
426
403
|
|
|
427
404
|
// Verify artifact content is stored
|
|
428
405
|
const adapter = _getAdapter();
|
|
429
406
|
const project = adapter?.prepare("SELECT * FROM artifacts WHERE path = :path").get({ ':path': 'PROJECT.md' });
|
|
430
|
-
|
|
407
|
+
assert.ok((project?.full_content as string)?.includes('Test Project'), 'PROJECT.md content round-trip');
|
|
431
408
|
|
|
432
409
|
closeDatabase();
|
|
433
410
|
} finally {
|
|
434
411
|
cleanupDir(tmpDir);
|
|
435
412
|
}
|
|
436
|
-
}
|
|
413
|
+
});
|
|
437
414
|
|
|
438
415
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
439
416
|
|
|
440
|
-
report();
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { createTestContext } from './test-helpers.ts';
|
|
2
1
|
import { parseMemoryResponse, _resetExtractionState } from '../memory-extractor.ts';
|
|
3
2
|
import {
|
|
4
3
|
openDatabase,
|
|
@@ -10,15 +9,14 @@ import {
|
|
|
10
9
|
getActiveMemoriesRanked,
|
|
11
10
|
} from '../memory-store.ts';
|
|
12
11
|
import type { MemoryAction } from '../memory-store.ts';
|
|
13
|
-
|
|
14
|
-
|
|
12
|
+
import { describe, test, beforeEach, afterEach } from 'node:test';
|
|
13
|
+
import assert from 'node:assert/strict';
|
|
15
14
|
|
|
16
15
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
17
16
|
// memory-extractor: parse valid JSON response
|
|
18
17
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
19
18
|
|
|
20
|
-
|
|
21
|
-
{
|
|
19
|
+
test('memory-extractor: parse valid JSON', () => {
|
|
22
20
|
const response = JSON.stringify([
|
|
23
21
|
{ action: 'CREATE', category: 'gotcha', content: 'esbuild drops binaries', confidence: 0.85 },
|
|
24
22
|
{ action: 'REINFORCE', id: 'MEM001' },
|
|
@@ -27,56 +25,52 @@ console.log('\n=== memory-extractor: parse valid JSON ===');
|
|
|
27
25
|
]);
|
|
28
26
|
|
|
29
27
|
const actions = parseMemoryResponse(response);
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
}
|
|
28
|
+
assert.deepStrictEqual(actions.length, 4, 'should parse 4 actions');
|
|
29
|
+
assert.deepStrictEqual(actions[0].action, 'CREATE', 'first action should be CREATE');
|
|
30
|
+
assert.deepStrictEqual((actions[0] as any).category, 'gotcha', 'CREATE category');
|
|
31
|
+
assert.deepStrictEqual((actions[0] as any).confidence, 0.85, 'CREATE confidence');
|
|
32
|
+
assert.deepStrictEqual(actions[1].action, 'REINFORCE', 'second action should be REINFORCE');
|
|
33
|
+
assert.deepStrictEqual(actions[2].action, 'UPDATE', 'third action should be UPDATE');
|
|
34
|
+
assert.deepStrictEqual(actions[3].action, 'SUPERSEDE', 'fourth action should be SUPERSEDE');
|
|
35
|
+
});
|
|
38
36
|
|
|
39
37
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
40
38
|
// memory-extractor: parse fenced JSON response
|
|
41
39
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
42
40
|
|
|
43
|
-
|
|
44
|
-
{
|
|
41
|
+
test('memory-extractor: parse fenced JSON', () => {
|
|
45
42
|
const response = '```json\n[\n {"action": "CREATE", "category": "convention", "content": "test memory"}\n]\n```';
|
|
46
43
|
|
|
47
44
|
const actions = parseMemoryResponse(response);
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
}
|
|
45
|
+
assert.deepStrictEqual(actions.length, 1, 'should parse 1 action from fenced JSON');
|
|
46
|
+
assert.deepStrictEqual(actions[0].action, 'CREATE', 'action should be CREATE');
|
|
47
|
+
});
|
|
51
48
|
|
|
52
49
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
53
50
|
// memory-extractor: parse empty array response
|
|
54
51
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
55
52
|
|
|
56
|
-
|
|
57
|
-
{
|
|
53
|
+
test('memory-extractor: parse empty array', () => {
|
|
58
54
|
const actions = parseMemoryResponse('[]');
|
|
59
|
-
|
|
60
|
-
}
|
|
55
|
+
assert.deepStrictEqual(actions.length, 0, 'empty array should parse to empty actions');
|
|
56
|
+
});
|
|
61
57
|
|
|
62
58
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
63
59
|
// memory-extractor: parse malformed response
|
|
64
60
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
65
61
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
}
|
|
62
|
+
test('memory-extractor: malformed responses', () => {
|
|
63
|
+
assert.deepStrictEqual(parseMemoryResponse('not json at all'), [], 'garbage text should return []');
|
|
64
|
+
assert.deepStrictEqual(parseMemoryResponse('{"action": "CREATE"}'), [], 'non-array should return []');
|
|
65
|
+
assert.deepStrictEqual(parseMemoryResponse(''), [], 'empty string should return []');
|
|
66
|
+
assert.deepStrictEqual(parseMemoryResponse('```\nbroken\n```'), [], 'fenced non-JSON should return []');
|
|
67
|
+
});
|
|
73
68
|
|
|
74
69
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
75
70
|
// memory-extractor: validation of required fields
|
|
76
71
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
77
72
|
|
|
78
|
-
|
|
79
|
-
{
|
|
73
|
+
test('memory-extractor: field validation', () => {
|
|
80
74
|
const response = JSON.stringify([
|
|
81
75
|
// Valid CREATE
|
|
82
76
|
{ action: 'CREATE', category: 'gotcha', content: 'valid' },
|
|
@@ -103,19 +97,18 @@ console.log('\n=== memory-extractor: field validation ===');
|
|
|
103
97
|
]);
|
|
104
98
|
|
|
105
99
|
const actions = parseMemoryResponse(response);
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
}
|
|
100
|
+
assert.deepStrictEqual(actions.length, 4, 'should only accept 4 valid actions');
|
|
101
|
+
assert.deepStrictEqual(actions[0].action, 'CREATE', 'first valid is CREATE');
|
|
102
|
+
assert.deepStrictEqual(actions[1].action, 'REINFORCE', 'second valid is REINFORCE');
|
|
103
|
+
assert.deepStrictEqual(actions[2].action, 'UPDATE', 'third valid is UPDATE');
|
|
104
|
+
assert.deepStrictEqual(actions[3].action, 'SUPERSEDE', 'fourth valid is SUPERSEDE');
|
|
105
|
+
});
|
|
112
106
|
|
|
113
107
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
114
108
|
// Integration: applyMemoryActions with mixed actions
|
|
115
109
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
116
110
|
|
|
117
|
-
|
|
118
|
-
{
|
|
111
|
+
test('integration: mixed action lifecycle', () => {
|
|
119
112
|
openDatabase(':memory:');
|
|
120
113
|
|
|
121
114
|
// Phase 1: Create initial memories
|
|
@@ -126,7 +119,7 @@ console.log('\n=== integration: mixed action lifecycle ===');
|
|
|
126
119
|
], 'plan-slice', 'M001/S01');
|
|
127
120
|
|
|
128
121
|
let active = getActiveMemoriesRanked(30);
|
|
129
|
-
|
|
122
|
+
assert.deepStrictEqual(active.length, 3, 'phase 1: 3 active memories');
|
|
130
123
|
|
|
131
124
|
// Phase 2: Reinforce one, update another, create new
|
|
132
125
|
applyMemoryActions([
|
|
@@ -136,13 +129,13 @@ console.log('\n=== integration: mixed action lifecycle ===');
|
|
|
136
129
|
], 'execute-task', 'M001/S01/T01');
|
|
137
130
|
|
|
138
131
|
active = getActiveMemoriesRanked(30);
|
|
139
|
-
|
|
140
|
-
|
|
132
|
+
assert.deepStrictEqual(active.length, 4, 'phase 2: 4 active memories');
|
|
133
|
+
assert.deepStrictEqual(
|
|
141
134
|
active.find(m => m.id === 'MEM001')?.content,
|
|
142
135
|
'npm run build requires tsc --noEmit first',
|
|
143
136
|
'MEM001 content should be updated',
|
|
144
137
|
);
|
|
145
|
-
|
|
138
|
+
assert.deepStrictEqual(active.find(m => m.id === 'MEM002')?.hit_count, 1, 'MEM002 should be reinforced');
|
|
146
139
|
|
|
147
140
|
// Phase 3: Supersede MEM001 with MEM005
|
|
148
141
|
applyMemoryActions([
|
|
@@ -151,30 +144,28 @@ console.log('\n=== integration: mixed action lifecycle ===');
|
|
|
151
144
|
], 'execute-task', 'M001/S01/T02');
|
|
152
145
|
|
|
153
146
|
active = getActiveMemoriesRanked(30);
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
147
|
+
assert.deepStrictEqual(active.length, 4, 'phase 3: 4 active (1 superseded, 1 created)');
|
|
148
|
+
assert.ok(!active.find(m => m.id === 'MEM001'), 'MEM001 should be superseded');
|
|
149
|
+
assert.ok(!!active.find(m => m.id === 'MEM005'), 'MEM005 should be active');
|
|
157
150
|
|
|
158
151
|
// Verify ranking: MEM003 (0.85) > MEM005 (0.9) but MEM002 has 1 hit
|
|
159
152
|
// MEM002: 0.8 * (1 + 1*0.1) = 0.88
|
|
160
153
|
// MEM003: 0.85 * 1.0 = 0.85
|
|
161
154
|
// MEM005: 0.9 * 1.0 = 0.9
|
|
162
155
|
// MEM004: 0.75 * 1.0 = 0.75
|
|
163
|
-
|
|
164
|
-
|
|
156
|
+
assert.deepStrictEqual(active[0].id, 'MEM005', 'MEM005 should rank first (0.9)');
|
|
157
|
+
assert.deepStrictEqual(active[1].id, 'MEM002', 'MEM002 should rank second (0.88)');
|
|
165
158
|
|
|
166
159
|
closeDatabase();
|
|
167
|
-
}
|
|
160
|
+
});
|
|
168
161
|
|
|
169
162
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
170
163
|
// memory-extractor: _resetExtractionState
|
|
171
164
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
172
165
|
|
|
173
|
-
|
|
174
|
-
{
|
|
166
|
+
test('memory-extractor: reset extraction state', () => {
|
|
175
167
|
// Just verify it doesn't throw
|
|
176
168
|
_resetExtractionState();
|
|
177
|
-
|
|
178
|
-
}
|
|
169
|
+
assert.ok(true, '_resetExtractionState should not throw');
|
|
170
|
+
});
|
|
179
171
|
|
|
180
|
-
report();
|