@soleri/core 2.4.0 → 2.6.0
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/dist/brain/brain.d.ts +7 -0
- package/dist/brain/brain.d.ts.map +1 -1
- package/dist/brain/brain.js +56 -9
- package/dist/brain/brain.js.map +1 -1
- package/dist/brain/intelligence.d.ts +1 -0
- package/dist/brain/intelligence.d.ts.map +1 -1
- package/dist/brain/intelligence.js +164 -148
- package/dist/brain/intelligence.js.map +1 -1
- package/dist/brain/types.d.ts +2 -2
- package/dist/brain/types.d.ts.map +1 -1
- package/dist/cognee/client.d.ts +3 -0
- package/dist/cognee/client.d.ts.map +1 -1
- package/dist/cognee/client.js +17 -0
- package/dist/cognee/client.js.map +1 -1
- package/dist/cognee/sync-manager.d.ts +94 -0
- package/dist/cognee/sync-manager.d.ts.map +1 -0
- package/dist/cognee/sync-manager.js +293 -0
- package/dist/cognee/sync-manager.js.map +1 -0
- package/dist/control/identity-manager.d.ts +3 -1
- package/dist/control/identity-manager.d.ts.map +1 -1
- package/dist/control/identity-manager.js +49 -51
- package/dist/control/identity-manager.js.map +1 -1
- package/dist/control/intent-router.d.ts +1 -0
- package/dist/control/intent-router.d.ts.map +1 -1
- package/dist/control/intent-router.js +32 -32
- package/dist/control/intent-router.js.map +1 -1
- package/dist/curator/curator.d.ts +9 -1
- package/dist/curator/curator.d.ts.map +1 -1
- package/dist/curator/curator.js +104 -92
- package/dist/curator/curator.js.map +1 -1
- package/dist/errors/classify.d.ts +13 -0
- package/dist/errors/classify.d.ts.map +1 -0
- package/dist/errors/classify.js +97 -0
- package/dist/errors/classify.js.map +1 -0
- package/dist/errors/index.d.ts +6 -0
- package/dist/errors/index.d.ts.map +1 -0
- package/dist/errors/index.js +4 -0
- package/dist/errors/index.js.map +1 -0
- package/dist/errors/retry.d.ts +40 -0
- package/dist/errors/retry.d.ts.map +1 -0
- package/dist/errors/retry.js +97 -0
- package/dist/errors/retry.js.map +1 -0
- package/dist/errors/types.d.ts +48 -0
- package/dist/errors/types.d.ts.map +1 -0
- package/dist/errors/types.js +59 -0
- package/dist/errors/types.js.map +1 -0
- package/dist/governance/governance.d.ts +1 -0
- package/dist/governance/governance.d.ts.map +1 -1
- package/dist/governance/governance.js +51 -68
- package/dist/governance/governance.js.map +1 -1
- package/dist/index.d.ts +26 -5
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +22 -3
- package/dist/index.js.map +1 -1
- package/dist/intake/content-classifier.d.ts +14 -0
- package/dist/intake/content-classifier.d.ts.map +1 -0
- package/dist/intake/content-classifier.js +125 -0
- package/dist/intake/content-classifier.js.map +1 -0
- package/dist/intake/dedup-gate.d.ts +17 -0
- package/dist/intake/dedup-gate.d.ts.map +1 -0
- package/dist/intake/dedup-gate.js +66 -0
- package/dist/intake/dedup-gate.js.map +1 -0
- package/dist/intake/intake-pipeline.d.ts +63 -0
- package/dist/intake/intake-pipeline.d.ts.map +1 -0
- package/dist/intake/intake-pipeline.js +373 -0
- package/dist/intake/intake-pipeline.js.map +1 -0
- package/dist/intake/types.d.ts +65 -0
- package/dist/intake/types.d.ts.map +1 -0
- package/dist/intake/types.js +3 -0
- package/dist/intake/types.js.map +1 -0
- package/dist/intelligence/loader.js +1 -1
- package/dist/intelligence/loader.js.map +1 -1
- package/dist/intelligence/types.d.ts +3 -1
- package/dist/intelligence/types.d.ts.map +1 -1
- package/dist/loop/loop-manager.d.ts +58 -7
- package/dist/loop/loop-manager.d.ts.map +1 -1
- package/dist/loop/loop-manager.js +280 -6
- package/dist/loop/loop-manager.js.map +1 -1
- package/dist/loop/types.d.ts +69 -1
- package/dist/loop/types.d.ts.map +1 -1
- package/dist/loop/types.js +4 -1
- package/dist/loop/types.js.map +1 -1
- package/dist/persistence/index.d.ts +4 -0
- package/dist/persistence/index.d.ts.map +1 -0
- package/dist/persistence/index.js +3 -0
- package/dist/persistence/index.js.map +1 -0
- package/dist/persistence/postgres-provider.d.ts +46 -0
- package/dist/persistence/postgres-provider.d.ts.map +1 -0
- package/dist/persistence/postgres-provider.js +115 -0
- package/dist/persistence/postgres-provider.js.map +1 -0
- package/dist/persistence/sqlite-provider.d.ts +28 -0
- package/dist/persistence/sqlite-provider.d.ts.map +1 -0
- package/dist/persistence/sqlite-provider.js +97 -0
- package/dist/persistence/sqlite-provider.js.map +1 -0
- package/dist/persistence/types.d.ts +58 -0
- package/dist/persistence/types.d.ts.map +1 -0
- package/dist/persistence/types.js +8 -0
- package/dist/persistence/types.js.map +1 -0
- package/dist/planning/gap-analysis.d.ts +47 -4
- package/dist/planning/gap-analysis.d.ts.map +1 -1
- package/dist/planning/gap-analysis.js +190 -13
- package/dist/planning/gap-analysis.js.map +1 -1
- package/dist/planning/gap-types.d.ts +1 -1
- package/dist/planning/gap-types.d.ts.map +1 -1
- package/dist/planning/gap-types.js.map +1 -1
- package/dist/planning/planner.d.ts +277 -9
- package/dist/planning/planner.d.ts.map +1 -1
- package/dist/planning/planner.js +611 -46
- package/dist/planning/planner.js.map +1 -1
- package/dist/playbooks/generic/brainstorming.d.ts +9 -0
- package/dist/playbooks/generic/brainstorming.d.ts.map +1 -0
- package/dist/playbooks/generic/brainstorming.js +105 -0
- package/dist/playbooks/generic/brainstorming.js.map +1 -0
- package/dist/playbooks/generic/code-review.d.ts +11 -0
- package/dist/playbooks/generic/code-review.d.ts.map +1 -0
- package/dist/playbooks/generic/code-review.js +176 -0
- package/dist/playbooks/generic/code-review.js.map +1 -0
- package/dist/playbooks/generic/subagent-execution.d.ts +9 -0
- package/dist/playbooks/generic/subagent-execution.d.ts.map +1 -0
- package/dist/playbooks/generic/subagent-execution.js +68 -0
- package/dist/playbooks/generic/subagent-execution.js.map +1 -0
- package/dist/playbooks/generic/systematic-debugging.d.ts +9 -0
- package/dist/playbooks/generic/systematic-debugging.d.ts.map +1 -0
- package/dist/playbooks/generic/systematic-debugging.js +87 -0
- package/dist/playbooks/generic/systematic-debugging.js.map +1 -0
- package/dist/playbooks/generic/tdd.d.ts +9 -0
- package/dist/playbooks/generic/tdd.d.ts.map +1 -0
- package/dist/playbooks/generic/tdd.js +70 -0
- package/dist/playbooks/generic/tdd.js.map +1 -0
- package/dist/playbooks/generic/verification.d.ts +9 -0
- package/dist/playbooks/generic/verification.d.ts.map +1 -0
- package/dist/playbooks/generic/verification.js +74 -0
- package/dist/playbooks/generic/verification.js.map +1 -0
- package/dist/playbooks/index.d.ts +4 -0
- package/dist/playbooks/index.d.ts.map +1 -0
- package/dist/playbooks/index.js +5 -0
- package/dist/playbooks/index.js.map +1 -0
- package/dist/playbooks/playbook-registry.d.ts +42 -0
- package/dist/playbooks/playbook-registry.d.ts.map +1 -0
- package/dist/playbooks/playbook-registry.js +227 -0
- package/dist/playbooks/playbook-registry.js.map +1 -0
- package/dist/playbooks/playbook-seeder.d.ts +47 -0
- package/dist/playbooks/playbook-seeder.d.ts.map +1 -0
- package/dist/playbooks/playbook-seeder.js +104 -0
- package/dist/playbooks/playbook-seeder.js.map +1 -0
- package/dist/playbooks/playbook-types.d.ts +132 -0
- package/dist/playbooks/playbook-types.d.ts.map +1 -0
- package/dist/playbooks/playbook-types.js +12 -0
- package/dist/playbooks/playbook-types.js.map +1 -0
- package/dist/project/project-registry.d.ts +4 -4
- package/dist/project/project-registry.d.ts.map +1 -1
- package/dist/project/project-registry.js +30 -57
- package/dist/project/project-registry.js.map +1 -1
- package/dist/prompts/index.d.ts +4 -0
- package/dist/prompts/index.d.ts.map +1 -0
- package/dist/prompts/index.js +3 -0
- package/dist/prompts/index.js.map +1 -0
- package/dist/prompts/parser.d.ts +17 -0
- package/dist/prompts/parser.d.ts.map +1 -0
- package/dist/prompts/parser.js +47 -0
- package/dist/prompts/parser.js.map +1 -0
- package/dist/prompts/template-manager.d.ts +25 -0
- package/dist/prompts/template-manager.d.ts.map +1 -0
- package/dist/prompts/template-manager.js +71 -0
- package/dist/prompts/template-manager.js.map +1 -0
- package/dist/prompts/types.d.ts +26 -0
- package/dist/prompts/types.d.ts.map +1 -0
- package/dist/prompts/types.js +5 -0
- package/dist/prompts/types.js.map +1 -0
- package/dist/runtime/admin-extra-ops.d.ts +5 -3
- package/dist/runtime/admin-extra-ops.d.ts.map +1 -1
- package/dist/runtime/admin-extra-ops.js +348 -11
- package/dist/runtime/admin-extra-ops.js.map +1 -1
- package/dist/runtime/admin-ops.d.ts.map +1 -1
- package/dist/runtime/admin-ops.js +10 -3
- package/dist/runtime/admin-ops.js.map +1 -1
- package/dist/runtime/capture-ops.d.ts.map +1 -1
- package/dist/runtime/capture-ops.js +20 -2
- package/dist/runtime/capture-ops.js.map +1 -1
- package/dist/runtime/cognee-sync-ops.d.ts +12 -0
- package/dist/runtime/cognee-sync-ops.d.ts.map +1 -0
- package/dist/runtime/cognee-sync-ops.js +55 -0
- package/dist/runtime/cognee-sync-ops.js.map +1 -0
- package/dist/runtime/core-ops.d.ts +8 -6
- package/dist/runtime/core-ops.d.ts.map +1 -1
- package/dist/runtime/core-ops.js +226 -9
- package/dist/runtime/core-ops.js.map +1 -1
- package/dist/runtime/curator-extra-ops.d.ts +2 -2
- package/dist/runtime/curator-extra-ops.d.ts.map +1 -1
- package/dist/runtime/curator-extra-ops.js +15 -3
- package/dist/runtime/curator-extra-ops.js.map +1 -1
- package/dist/runtime/domain-ops.js +2 -2
- package/dist/runtime/domain-ops.js.map +1 -1
- package/dist/runtime/grading-ops.d.ts.map +1 -1
- package/dist/runtime/grading-ops.js.map +1 -1
- package/dist/runtime/intake-ops.d.ts +14 -0
- package/dist/runtime/intake-ops.d.ts.map +1 -0
- package/dist/runtime/intake-ops.js +110 -0
- package/dist/runtime/intake-ops.js.map +1 -0
- package/dist/runtime/loop-ops.d.ts +5 -4
- package/dist/runtime/loop-ops.d.ts.map +1 -1
- package/dist/runtime/loop-ops.js +84 -12
- package/dist/runtime/loop-ops.js.map +1 -1
- package/dist/runtime/memory-cross-project-ops.d.ts.map +1 -1
- package/dist/runtime/memory-cross-project-ops.js.map +1 -1
- package/dist/runtime/memory-extra-ops.js +5 -5
- package/dist/runtime/memory-extra-ops.js.map +1 -1
- package/dist/runtime/orchestrate-ops.d.ts.map +1 -1
- package/dist/runtime/orchestrate-ops.js +8 -2
- package/dist/runtime/orchestrate-ops.js.map +1 -1
- package/dist/runtime/planning-extra-ops.d.ts +13 -5
- package/dist/runtime/planning-extra-ops.d.ts.map +1 -1
- package/dist/runtime/planning-extra-ops.js +381 -18
- package/dist/runtime/planning-extra-ops.js.map +1 -1
- package/dist/runtime/playbook-ops.d.ts +14 -0
- package/dist/runtime/playbook-ops.d.ts.map +1 -0
- package/dist/runtime/playbook-ops.js +141 -0
- package/dist/runtime/playbook-ops.js.map +1 -0
- package/dist/runtime/project-ops.d.ts.map +1 -1
- package/dist/runtime/project-ops.js +7 -2
- package/dist/runtime/project-ops.js.map +1 -1
- package/dist/runtime/runtime.d.ts.map +1 -1
- package/dist/runtime/runtime.js +28 -9
- package/dist/runtime/runtime.js.map +1 -1
- package/dist/runtime/types.d.ts +8 -0
- package/dist/runtime/types.d.ts.map +1 -1
- package/dist/runtime/vault-extra-ops.d.ts +4 -2
- package/dist/runtime/vault-extra-ops.d.ts.map +1 -1
- package/dist/runtime/vault-extra-ops.js +383 -4
- package/dist/runtime/vault-extra-ops.js.map +1 -1
- package/dist/vault/playbook.d.ts +34 -0
- package/dist/vault/playbook.d.ts.map +1 -0
- package/dist/vault/playbook.js +60 -0
- package/dist/vault/playbook.js.map +1 -0
- package/dist/vault/vault.d.ts +52 -32
- package/dist/vault/vault.d.ts.map +1 -1
- package/dist/vault/vault.js +300 -181
- package/dist/vault/vault.js.map +1 -1
- package/package.json +9 -3
- package/src/__tests__/admin-extra-ops.test.ts +62 -15
- package/src/__tests__/admin-ops.test.ts +2 -2
- package/src/__tests__/brain.test.ts +3 -3
- package/src/__tests__/cognee-integration.test.ts +80 -0
- package/src/__tests__/cognee-sync-manager.test.ts +103 -0
- package/src/__tests__/core-ops.test.ts +36 -4
- package/src/__tests__/curator-extra-ops.test.ts +24 -2
- package/src/__tests__/errors.test.ts +388 -0
- package/src/__tests__/grading-ops.test.ts +28 -7
- package/src/__tests__/intake-pipeline.test.ts +162 -0
- package/src/__tests__/loop-ops.test.ts +74 -3
- package/src/__tests__/memory-cross-project-ops.test.ts +3 -1
- package/src/__tests__/orchestrate-ops.test.ts +8 -3
- package/src/__tests__/persistence.test.ts +291 -0
- package/src/__tests__/planner.test.ts +99 -21
- package/src/__tests__/planning-extra-ops.test.ts +168 -10
- package/src/__tests__/playbook-registry.test.ts +326 -0
- package/src/__tests__/playbook-seeder.test.ts +163 -0
- package/src/__tests__/playbook.test.ts +389 -0
- package/src/__tests__/postgres-provider.test.ts +58 -0
- package/src/__tests__/project-ops.test.ts +18 -4
- package/src/__tests__/template-manager.test.ts +222 -0
- package/src/__tests__/vault-extra-ops.test.ts +82 -7
- package/src/__tests__/vault.test.ts +184 -0
- package/src/brain/brain.ts +71 -9
- package/src/brain/intelligence.ts +258 -307
- package/src/brain/types.ts +2 -2
- package/src/cognee/client.ts +18 -0
- package/src/cognee/sync-manager.ts +389 -0
- package/src/control/identity-manager.ts +77 -75
- package/src/control/intent-router.ts +55 -57
- package/src/curator/curator.ts +199 -139
- package/src/errors/classify.ts +102 -0
- package/src/errors/index.ts +5 -0
- package/src/errors/retry.ts +132 -0
- package/src/errors/types.ts +81 -0
- package/src/governance/governance.ts +90 -107
- package/src/index.ts +116 -3
- package/src/intake/content-classifier.ts +146 -0
- package/src/intake/dedup-gate.ts +92 -0
- package/src/intake/intake-pipeline.ts +503 -0
- package/src/intake/types.ts +69 -0
- package/src/intelligence/loader.ts +1 -1
- package/src/intelligence/types.ts +3 -1
- package/src/loop/loop-manager.ts +325 -7
- package/src/loop/types.ts +72 -1
- package/src/persistence/index.ts +9 -0
- package/src/persistence/postgres-provider.ts +157 -0
- package/src/persistence/sqlite-provider.ts +115 -0
- package/src/persistence/types.ts +74 -0
- package/src/planning/gap-analysis.ts +286 -17
- package/src/planning/gap-types.ts +4 -1
- package/src/planning/planner.ts +828 -55
- package/src/playbooks/generic/brainstorming.ts +110 -0
- package/src/playbooks/generic/code-review.ts +181 -0
- package/src/playbooks/generic/subagent-execution.ts +74 -0
- package/src/playbooks/generic/systematic-debugging.ts +92 -0
- package/src/playbooks/generic/tdd.ts +75 -0
- package/src/playbooks/generic/verification.ts +79 -0
- package/src/playbooks/index.ts +27 -0
- package/src/playbooks/playbook-registry.ts +284 -0
- package/src/playbooks/playbook-seeder.ts +119 -0
- package/src/playbooks/playbook-types.ts +162 -0
- package/src/project/project-registry.ts +81 -74
- package/src/prompts/index.ts +3 -0
- package/src/prompts/parser.ts +59 -0
- package/src/prompts/template-manager.ts +77 -0
- package/src/prompts/types.ts +28 -0
- package/src/runtime/admin-extra-ops.ts +391 -13
- package/src/runtime/admin-ops.ts +17 -6
- package/src/runtime/capture-ops.ts +25 -6
- package/src/runtime/cognee-sync-ops.ts +63 -0
- package/src/runtime/core-ops.ts +258 -8
- package/src/runtime/curator-extra-ops.ts +17 -3
- package/src/runtime/domain-ops.ts +2 -2
- package/src/runtime/grading-ops.ts +11 -2
- package/src/runtime/intake-ops.ts +126 -0
- package/src/runtime/loop-ops.ts +96 -13
- package/src/runtime/memory-cross-project-ops.ts +1 -2
- package/src/runtime/memory-extra-ops.ts +5 -5
- package/src/runtime/orchestrate-ops.ts +8 -2
- package/src/runtime/planning-extra-ops.ts +414 -23
- package/src/runtime/playbook-ops.ts +169 -0
- package/src/runtime/project-ops.ts +9 -3
- package/src/runtime/runtime.ts +36 -10
- package/src/runtime/types.ts +8 -0
- package/src/runtime/vault-extra-ops.ts +425 -4
- package/src/vault/playbook.ts +87 -0
- package/src/vault/vault.ts +419 -235
|
@@ -269,15 +269,17 @@ describe('createLoopOps', () => {
|
|
|
269
269
|
return op;
|
|
270
270
|
}
|
|
271
271
|
|
|
272
|
-
it('exports
|
|
273
|
-
expect(ops).toHaveLength(
|
|
272
|
+
it('exports 9 loop ops', () => {
|
|
273
|
+
expect(ops).toHaveLength(9);
|
|
274
274
|
const names = ops.map((o) => o.name).sort();
|
|
275
275
|
expect(names).toEqual([
|
|
276
|
+
'loop_anomaly_check',
|
|
276
277
|
'loop_cancel',
|
|
277
278
|
'loop_complete',
|
|
278
279
|
'loop_history',
|
|
279
280
|
'loop_is_active',
|
|
280
281
|
'loop_iterate',
|
|
282
|
+
'loop_iterate_gate',
|
|
281
283
|
'loop_start',
|
|
282
284
|
'loop_status',
|
|
283
285
|
]);
|
|
@@ -384,8 +386,77 @@ describe('createLoopOps', () => {
|
|
|
384
386
|
expect(result.active).toBe(false);
|
|
385
387
|
});
|
|
386
388
|
|
|
389
|
+
it('loop_iterate_gate accepts durationMs parameter', async () => {
|
|
390
|
+
await findOp('loop_start').handler({ mode: 'token-migration', prompt: 'Test with timing' });
|
|
391
|
+
|
|
392
|
+
const result = (await findOp('loop_iterate_gate').handler({
|
|
393
|
+
lastOutput: 'Some LLM output that does not complete',
|
|
394
|
+
durationMs: 15000,
|
|
395
|
+
})) as Record<string, unknown>;
|
|
396
|
+
|
|
397
|
+
expect(result.decision).toBeDefined();
|
|
398
|
+
// Should not have anomaly warning for normal duration
|
|
399
|
+
expect(result.anomalyWarning).toBeUndefined();
|
|
400
|
+
});
|
|
401
|
+
|
|
402
|
+
it('loop_iterate_gate reports anomaly for fast + low-score iteration', async () => {
|
|
403
|
+
await findOp('loop_start').handler({ mode: 'token-migration', prompt: 'Test anomaly' });
|
|
404
|
+
|
|
405
|
+
// First iterate to set a low score baseline, then gate with fast duration
|
|
406
|
+
await findOp('loop_iterate').handler({ passed: false, validationScore: 20 });
|
|
407
|
+
|
|
408
|
+
const result = (await findOp('loop_iterate_gate').handler({
|
|
409
|
+
lastOutput: 'Quick response with low quality',
|
|
410
|
+
durationMs: 500, // Very fast — below 5000ms threshold for token-migration
|
|
411
|
+
})) as Record<string, unknown>;
|
|
412
|
+
|
|
413
|
+
// The gate still works, anomaly warning may or may not appear depending on score
|
|
414
|
+
expect(result.decision).toBeDefined();
|
|
415
|
+
});
|
|
416
|
+
|
|
417
|
+
it('loop_anomaly_check returns inactive when no loop', async () => {
|
|
418
|
+
const result = (await findOp('loop_anomaly_check').handler({})) as Record<string, unknown>;
|
|
419
|
+
expect(result.active).toBe(false);
|
|
420
|
+
expect(result.anomalies).toEqual([]);
|
|
421
|
+
expect(result.summary).toBe('No active loop');
|
|
422
|
+
});
|
|
423
|
+
|
|
424
|
+
it('loop_anomaly_check detects consecutive failures', async () => {
|
|
425
|
+
await findOp('loop_start').handler({ mode: 'custom', prompt: 'Failing loop' });
|
|
426
|
+
await findOp('loop_iterate').handler({ passed: false, validationScore: 20 });
|
|
427
|
+
await findOp('loop_iterate').handler({ passed: false, validationScore: 25 });
|
|
428
|
+
await findOp('loop_iterate').handler({ passed: false, validationScore: 30 });
|
|
429
|
+
|
|
430
|
+
const result = (await findOp('loop_anomaly_check').handler({})) as {
|
|
431
|
+
active: boolean;
|
|
432
|
+
hasAnomalies: boolean;
|
|
433
|
+
anomalies: string[];
|
|
434
|
+
totalIterations: number;
|
|
435
|
+
};
|
|
436
|
+
|
|
437
|
+
expect(result.active).toBe(true);
|
|
438
|
+
expect(result.totalIterations).toBe(3);
|
|
439
|
+
expect(result.hasAnomalies).toBe(true);
|
|
440
|
+
expect(result.anomalies.some((a) => a.includes('consecutive failing'))).toBe(true);
|
|
441
|
+
});
|
|
442
|
+
|
|
443
|
+
it('loop_anomaly_check returns no anomalies for passing loop', async () => {
|
|
444
|
+
await findOp('loop_start').handler({ mode: 'custom', prompt: 'Good loop' });
|
|
445
|
+
await findOp('loop_iterate').handler({ passed: true, validationScore: 90 });
|
|
446
|
+
|
|
447
|
+
const result = (await findOp('loop_anomaly_check').handler({})) as {
|
|
448
|
+
active: boolean;
|
|
449
|
+
hasAnomalies: boolean;
|
|
450
|
+
anomalies: string[];
|
|
451
|
+
};
|
|
452
|
+
|
|
453
|
+
expect(result.active).toBe(true);
|
|
454
|
+
expect(result.hasAnomalies).toBe(false);
|
|
455
|
+
expect(result.anomalies).toEqual([]);
|
|
456
|
+
});
|
|
457
|
+
|
|
387
458
|
it('assigns correct auth levels', () => {
|
|
388
|
-
const readOps = ['loop_status', 'loop_history', 'loop_is_active'];
|
|
459
|
+
const readOps = ['loop_status', 'loop_history', 'loop_is_active', 'loop_anomaly_check'];
|
|
389
460
|
const writeOps = ['loop_start', 'loop_iterate', 'loop_cancel', 'loop_complete'];
|
|
390
461
|
|
|
391
462
|
for (const name of readOps) {
|
|
@@ -125,7 +125,9 @@ describe('Memory Cross-Project Ops', () => {
|
|
|
125
125
|
|
|
126
126
|
const project = runtime.projectRegistry.getByPath('/test/project-b');
|
|
127
127
|
expect(project!.metadata.custom).toBe('value');
|
|
128
|
-
expect((project!.metadata.memoryConfig as Record<string, unknown>).crossProjectEnabled).toBe(
|
|
128
|
+
expect((project!.metadata.memoryConfig as Record<string, unknown>).crossProjectEnabled).toBe(
|
|
129
|
+
false,
|
|
130
|
+
);
|
|
129
131
|
});
|
|
130
132
|
});
|
|
131
133
|
|
|
@@ -64,7 +64,10 @@ describe('createOrchestrateOps', () => {
|
|
|
64
64
|
objective: 'Build a new button component',
|
|
65
65
|
scope: 'src/components/Button',
|
|
66
66
|
domain: 'component',
|
|
67
|
-
})) as {
|
|
67
|
+
})) as {
|
|
68
|
+
plan: { id: string; objective: string; decisions: string[] };
|
|
69
|
+
recommendations: unknown[];
|
|
70
|
+
};
|
|
68
71
|
|
|
69
72
|
expect(result.plan).toBeDefined();
|
|
70
73
|
expect(result.plan.objective).toBe('Build a new button component');
|
|
@@ -130,7 +133,7 @@ describe('createOrchestrateOps', () => {
|
|
|
130
133
|
});
|
|
131
134
|
|
|
132
135
|
const op = findOp('orchestrate_execute');
|
|
133
|
-
await expect(op.handler({ planId: plan.id })).rejects.toThrow(/
|
|
136
|
+
await expect(op.handler({ planId: plan.id })).rejects.toThrow(/Invalid transition/);
|
|
134
137
|
});
|
|
135
138
|
});
|
|
136
139
|
|
|
@@ -138,13 +141,14 @@ describe('createOrchestrateOps', () => {
|
|
|
138
141
|
|
|
139
142
|
describe('orchestrate_complete', () => {
|
|
140
143
|
it('should complete plan, end session, and extract knowledge', async () => {
|
|
141
|
-
// Full lifecycle: create -> approve -> execute -> complete
|
|
144
|
+
// Full lifecycle: create -> approve -> execute -> reconciling -> complete
|
|
142
145
|
const plan = runtime.planner.create({
|
|
143
146
|
objective: 'Full lifecycle test',
|
|
144
147
|
scope: 'test',
|
|
145
148
|
});
|
|
146
149
|
runtime.planner.approve(plan.id);
|
|
147
150
|
runtime.planner.startExecution(plan.id);
|
|
151
|
+
runtime.planner.startReconciliation(plan.id);
|
|
148
152
|
|
|
149
153
|
// Start a brain session
|
|
150
154
|
const session = runtime.brainIntelligence.lifecycle({
|
|
@@ -176,6 +180,7 @@ describe('createOrchestrateOps', () => {
|
|
|
176
180
|
const plan = runtime.planner.create({ objective: 'Abandoned test', scope: 'test' });
|
|
177
181
|
runtime.planner.approve(plan.id);
|
|
178
182
|
runtime.planner.startExecution(plan.id);
|
|
183
|
+
runtime.planner.startReconciliation(plan.id);
|
|
179
184
|
|
|
180
185
|
const session = runtime.brainIntelligence.lifecycle({
|
|
181
186
|
action: 'start',
|
|
@@ -0,0 +1,291 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest';
|
|
2
|
+
import { SQLitePersistenceProvider } from '../persistence/sqlite-provider.js';
|
|
3
|
+
import { Vault } from '../vault/vault.js';
|
|
4
|
+
|
|
5
|
+
// ─── SQLitePersistenceProvider ────────────────────────────────────────
|
|
6
|
+
|
|
7
|
+
describe('SQLitePersistenceProvider', () => {
|
|
8
|
+
it('execSql creates tables', () => {
|
|
9
|
+
const p = new SQLitePersistenceProvider(':memory:');
|
|
10
|
+
p.execSql('CREATE TABLE test (id TEXT PRIMARY KEY, val TEXT)');
|
|
11
|
+
p.run('INSERT INTO test (id, val) VALUES (?, ?)', ['a', 'hello']);
|
|
12
|
+
const row = p.get<{ id: string; val: string }>('SELECT * FROM test WHERE id = ?', ['a']);
|
|
13
|
+
expect(row).toEqual({ id: 'a', val: 'hello' });
|
|
14
|
+
p.close();
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
it('run with named params', () => {
|
|
18
|
+
const p = new SQLitePersistenceProvider(':memory:');
|
|
19
|
+
p.execSql('CREATE TABLE t (id TEXT PRIMARY KEY, name TEXT)');
|
|
20
|
+
const result = p.run('INSERT INTO t (id, name) VALUES (@id, @name)', {
|
|
21
|
+
id: '1',
|
|
22
|
+
name: 'test',
|
|
23
|
+
});
|
|
24
|
+
expect(result.changes).toBe(1);
|
|
25
|
+
p.close();
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
it('run with positional params', () => {
|
|
29
|
+
const p = new SQLitePersistenceProvider(':memory:');
|
|
30
|
+
p.execSql('CREATE TABLE t (id TEXT PRIMARY KEY)');
|
|
31
|
+
const result = p.run('INSERT INTO t (id) VALUES (?)', ['x']);
|
|
32
|
+
expect(result.changes).toBe(1);
|
|
33
|
+
p.close();
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
it('run with no params', () => {
|
|
37
|
+
const p = new SQLitePersistenceProvider(':memory:');
|
|
38
|
+
p.execSql('CREATE TABLE t (id INTEGER PRIMARY KEY AUTOINCREMENT)');
|
|
39
|
+
const result = p.run('INSERT INTO t DEFAULT VALUES');
|
|
40
|
+
expect(result.changes).toBe(1);
|
|
41
|
+
expect(result.lastInsertRowid).toBe(1);
|
|
42
|
+
p.close();
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
it('get returns undefined for no match', () => {
|
|
46
|
+
const p = new SQLitePersistenceProvider(':memory:');
|
|
47
|
+
p.execSql('CREATE TABLE t (id TEXT PRIMARY KEY)');
|
|
48
|
+
const row = p.get('SELECT * FROM t WHERE id = ?', ['nonexistent']);
|
|
49
|
+
expect(row).toBeUndefined();
|
|
50
|
+
p.close();
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
it('all returns empty for no matches', () => {
|
|
54
|
+
const p = new SQLitePersistenceProvider(':memory:');
|
|
55
|
+
p.execSql('CREATE TABLE t (id TEXT PRIMARY KEY)');
|
|
56
|
+
const rows = p.all('SELECT * FROM t');
|
|
57
|
+
expect(rows).toEqual([]);
|
|
58
|
+
p.close();
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
it('all returns multiple rows', () => {
|
|
62
|
+
const p = new SQLitePersistenceProvider(':memory:');
|
|
63
|
+
p.execSql('CREATE TABLE t (id TEXT PRIMARY KEY, val INTEGER)');
|
|
64
|
+
p.run('INSERT INTO t VALUES (?, ?)', ['a', 1]);
|
|
65
|
+
p.run('INSERT INTO t VALUES (?, ?)', ['b', 2]);
|
|
66
|
+
p.run('INSERT INTO t VALUES (?, ?)', ['c', 3]);
|
|
67
|
+
const rows = p.all<{ id: string; val: number }>('SELECT * FROM t ORDER BY id');
|
|
68
|
+
expect(rows).toHaveLength(3);
|
|
69
|
+
expect(rows[0].id).toBe('a');
|
|
70
|
+
expect(rows[2].val).toBe(3);
|
|
71
|
+
p.close();
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
it('transaction commits on success', () => {
|
|
75
|
+
const p = new SQLitePersistenceProvider(':memory:');
|
|
76
|
+
p.execSql('CREATE TABLE t (id TEXT PRIMARY KEY)');
|
|
77
|
+
p.transaction(() => {
|
|
78
|
+
p.run('INSERT INTO t VALUES (?)', ['a']);
|
|
79
|
+
p.run('INSERT INTO t VALUES (?)', ['b']);
|
|
80
|
+
});
|
|
81
|
+
const rows = p.all('SELECT * FROM t');
|
|
82
|
+
expect(rows).toHaveLength(2);
|
|
83
|
+
p.close();
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
it('transaction rolls back on error', () => {
|
|
87
|
+
const p = new SQLitePersistenceProvider(':memory:');
|
|
88
|
+
p.execSql('CREATE TABLE t (id TEXT PRIMARY KEY)');
|
|
89
|
+
expect(() =>
|
|
90
|
+
p.transaction(() => {
|
|
91
|
+
p.run('INSERT INTO t VALUES (?)', ['a']);
|
|
92
|
+
throw new Error('rollback');
|
|
93
|
+
}),
|
|
94
|
+
).toThrow('rollback');
|
|
95
|
+
const rows = p.all('SELECT * FROM t');
|
|
96
|
+
expect(rows).toHaveLength(0);
|
|
97
|
+
p.close();
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
it('transaction returns value', () => {
|
|
101
|
+
const p = new SQLitePersistenceProvider(':memory:');
|
|
102
|
+
p.execSql('CREATE TABLE t (id TEXT PRIMARY KEY)');
|
|
103
|
+
const result = p.transaction(() => {
|
|
104
|
+
p.run('INSERT INTO t VALUES (?)', ['a']);
|
|
105
|
+
return 42;
|
|
106
|
+
});
|
|
107
|
+
expect(result).toBe(42);
|
|
108
|
+
p.close();
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
it('getDatabase returns raw db', () => {
|
|
112
|
+
const p = new SQLitePersistenceProvider(':memory:');
|
|
113
|
+
const db = p.getDatabase();
|
|
114
|
+
expect(db).toBeDefined();
|
|
115
|
+
// Verify it's a real better-sqlite3 database
|
|
116
|
+
expect(typeof db.prepare).toBe('function');
|
|
117
|
+
p.close();
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
it('close prevents further operations', () => {
|
|
121
|
+
const p = new SQLitePersistenceProvider(':memory:');
|
|
122
|
+
p.close();
|
|
123
|
+
expect(() => p.run('SELECT 1')).toThrow();
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
it('backend returns sqlite', () => {
|
|
127
|
+
const p = new SQLitePersistenceProvider(':memory:');
|
|
128
|
+
expect(p.backend).toBe('sqlite');
|
|
129
|
+
p.close();
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
it('ftsSearch returns matches', () => {
|
|
133
|
+
const p = new SQLitePersistenceProvider(':memory:');
|
|
134
|
+
p.execSql(`
|
|
135
|
+
CREATE TABLE docs (rowid INTEGER PRIMARY KEY, title TEXT, body TEXT);
|
|
136
|
+
CREATE VIRTUAL TABLE docs_fts USING fts5(title, body, content=docs, content_rowid=rowid);
|
|
137
|
+
INSERT INTO docs (rowid, title, body) VALUES (1, 'Hello World', 'This is a test document');
|
|
138
|
+
INSERT INTO docs (rowid, title, body) VALUES (2, 'Goodbye', 'Another test here');
|
|
139
|
+
INSERT INTO docs_fts (rowid, title, body) VALUES (1, 'Hello World', 'This is a test document');
|
|
140
|
+
INSERT INTO docs_fts (rowid, title, body) VALUES (2, 'Goodbye', 'Another test here');
|
|
141
|
+
`);
|
|
142
|
+
const results = p.ftsSearch<{ title: string; body: string }>('docs', 'hello');
|
|
143
|
+
expect(results).toHaveLength(1);
|
|
144
|
+
expect(results[0].title).toBe('Hello World');
|
|
145
|
+
p.close();
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
it('ftsSearch respects limit/offset', () => {
|
|
149
|
+
const p = new SQLitePersistenceProvider(':memory:');
|
|
150
|
+
p.execSql(`
|
|
151
|
+
CREATE TABLE items (rowid INTEGER PRIMARY KEY, name TEXT);
|
|
152
|
+
CREATE VIRTUAL TABLE items_fts USING fts5(name, content=items, content_rowid=rowid);
|
|
153
|
+
INSERT INTO items (rowid, name) VALUES (1, 'test alpha');
|
|
154
|
+
INSERT INTO items (rowid, name) VALUES (2, 'test beta');
|
|
155
|
+
INSERT INTO items (rowid, name) VALUES (3, 'test gamma');
|
|
156
|
+
INSERT INTO items_fts (rowid, name) VALUES (1, 'test alpha');
|
|
157
|
+
INSERT INTO items_fts (rowid, name) VALUES (2, 'test beta');
|
|
158
|
+
INSERT INTO items_fts (rowid, name) VALUES (3, 'test gamma');
|
|
159
|
+
`);
|
|
160
|
+
const limited = p.ftsSearch<{ name: string }>('items', 'test', { limit: 2 });
|
|
161
|
+
expect(limited).toHaveLength(2);
|
|
162
|
+
const offset = p.ftsSearch<{ name: string }>('items', 'test', { limit: 1, offset: 2 });
|
|
163
|
+
expect(offset).toHaveLength(1);
|
|
164
|
+
p.close();
|
|
165
|
+
});
|
|
166
|
+
|
|
167
|
+
it('ftsSearch applies filters', () => {
|
|
168
|
+
const p = new SQLitePersistenceProvider(':memory:');
|
|
169
|
+
p.execSql(`
|
|
170
|
+
CREATE TABLE notes (rowid INTEGER PRIMARY KEY, title TEXT, category TEXT);
|
|
171
|
+
CREATE VIRTUAL TABLE notes_fts USING fts5(title, content=notes, content_rowid=rowid);
|
|
172
|
+
INSERT INTO notes (rowid, title, category) VALUES (1, 'design pattern', 'arch');
|
|
173
|
+
INSERT INTO notes (rowid, title, category) VALUES (2, 'design review', 'process');
|
|
174
|
+
INSERT INTO notes_fts (rowid, title) VALUES (1, 'design pattern');
|
|
175
|
+
INSERT INTO notes_fts (rowid, title) VALUES (2, 'design review');
|
|
176
|
+
`);
|
|
177
|
+
const filtered = p.ftsSearch<{ title: string; category: string }>('notes', 'design', {
|
|
178
|
+
filters: { category: 'arch' },
|
|
179
|
+
});
|
|
180
|
+
expect(filtered).toHaveLength(1);
|
|
181
|
+
expect(filtered[0].category).toBe('arch');
|
|
182
|
+
p.close();
|
|
183
|
+
});
|
|
184
|
+
|
|
185
|
+
it('ftsRebuild no-ops gracefully', () => {
|
|
186
|
+
const p = new SQLitePersistenceProvider(':memory:');
|
|
187
|
+
// No FTS table exists — should not throw
|
|
188
|
+
expect(() => p.ftsRebuild('nonexistent')).not.toThrow();
|
|
189
|
+
p.close();
|
|
190
|
+
});
|
|
191
|
+
});
|
|
192
|
+
|
|
193
|
+
// ─── Vault with PersistenceProvider ───────────────────────────────────
|
|
194
|
+
|
|
195
|
+
describe('Vault with PersistenceProvider', () => {
|
|
196
|
+
it('accepts string path (backward compat)', () => {
|
|
197
|
+
const vault = new Vault(':memory:');
|
|
198
|
+
expect(vault.stats().totalEntries).toBe(0);
|
|
199
|
+
vault.close();
|
|
200
|
+
});
|
|
201
|
+
|
|
202
|
+
it('accepts SQLitePersistenceProvider', () => {
|
|
203
|
+
const provider = new SQLitePersistenceProvider(':memory:');
|
|
204
|
+
provider.run('PRAGMA journal_mode = WAL');
|
|
205
|
+
provider.run('PRAGMA foreign_keys = ON');
|
|
206
|
+
const vault = new Vault(provider);
|
|
207
|
+
expect(vault.stats().totalEntries).toBe(0);
|
|
208
|
+
vault.close();
|
|
209
|
+
});
|
|
210
|
+
|
|
211
|
+
it('createWithSQLite factory works', () => {
|
|
212
|
+
const vault = Vault.createWithSQLite(':memory:');
|
|
213
|
+
vault.add({
|
|
214
|
+
id: 'test-1',
|
|
215
|
+
type: 'pattern',
|
|
216
|
+
domain: 'core',
|
|
217
|
+
title: 'Test Pattern',
|
|
218
|
+
severity: 'suggestion',
|
|
219
|
+
description: 'A test',
|
|
220
|
+
tags: ['test'],
|
|
221
|
+
});
|
|
222
|
+
expect(vault.stats().totalEntries).toBe(1);
|
|
223
|
+
vault.close();
|
|
224
|
+
});
|
|
225
|
+
|
|
226
|
+
it('getProvider returns the provider', () => {
|
|
227
|
+
const provider = new SQLitePersistenceProvider(':memory:');
|
|
228
|
+
provider.run('PRAGMA journal_mode = WAL');
|
|
229
|
+
provider.run('PRAGMA foreign_keys = ON');
|
|
230
|
+
const vault = new Vault(provider);
|
|
231
|
+
expect(vault.getProvider()).toBe(provider);
|
|
232
|
+
vault.close();
|
|
233
|
+
});
|
|
234
|
+
|
|
235
|
+
it('getDb works with SQLite provider', () => {
|
|
236
|
+
const vault = new Vault(':memory:');
|
|
237
|
+
const db = vault.getDb();
|
|
238
|
+
expect(typeof db.prepare).toBe('function');
|
|
239
|
+
vault.close();
|
|
240
|
+
});
|
|
241
|
+
|
|
242
|
+
it('all vault operations work through provider', () => {
|
|
243
|
+
const vault = new Vault(':memory:');
|
|
244
|
+
|
|
245
|
+
// seed + get
|
|
246
|
+
vault.seed([
|
|
247
|
+
{
|
|
248
|
+
id: 'e1',
|
|
249
|
+
type: 'pattern',
|
|
250
|
+
domain: 'test',
|
|
251
|
+
title: 'Pattern One',
|
|
252
|
+
severity: 'suggestion',
|
|
253
|
+
description: 'First pattern',
|
|
254
|
+
tags: ['a', 'b'],
|
|
255
|
+
},
|
|
256
|
+
{
|
|
257
|
+
id: 'e2',
|
|
258
|
+
type: 'anti-pattern',
|
|
259
|
+
domain: 'test',
|
|
260
|
+
title: 'Anti Pattern',
|
|
261
|
+
severity: 'warning',
|
|
262
|
+
description: 'Avoid this',
|
|
263
|
+
tags: ['c'],
|
|
264
|
+
},
|
|
265
|
+
]);
|
|
266
|
+
expect(vault.get('e1')?.title).toBe('Pattern One');
|
|
267
|
+
expect(vault.stats().totalEntries).toBe(2);
|
|
268
|
+
|
|
269
|
+
// search
|
|
270
|
+
const results = vault.search('pattern');
|
|
271
|
+
expect(results.length).toBeGreaterThan(0);
|
|
272
|
+
|
|
273
|
+
// list
|
|
274
|
+
const all = vault.list({ domain: 'test' });
|
|
275
|
+
expect(all).toHaveLength(2);
|
|
276
|
+
|
|
277
|
+
// update
|
|
278
|
+
const updated = vault.update('e1', { title: 'Updated' });
|
|
279
|
+
expect(updated?.title).toBe('Updated');
|
|
280
|
+
|
|
281
|
+
// remove
|
|
282
|
+
expect(vault.remove('e2')).toBe(true);
|
|
283
|
+
expect(vault.stats().totalEntries).toBe(1);
|
|
284
|
+
|
|
285
|
+
// bulk remove
|
|
286
|
+
expect(vault.bulkRemove(['e1'])).toBe(1);
|
|
287
|
+
expect(vault.stats().totalEntries).toBe(0);
|
|
288
|
+
|
|
289
|
+
vault.close();
|
|
290
|
+
});
|
|
291
|
+
});
|
|
@@ -95,7 +95,7 @@ describe('Planner', () => {
|
|
|
95
95
|
it('should throw when approving non-draft plan', () => {
|
|
96
96
|
const plan = planner.create({ objective: 'Already approved', scope: 'test' });
|
|
97
97
|
planner.approve(plan.id);
|
|
98
|
-
expect(() => planner.approve(plan.id)).toThrow('
|
|
98
|
+
expect(() => planner.approve(plan.id)).toThrow('Invalid transition');
|
|
99
99
|
});
|
|
100
100
|
|
|
101
101
|
it('should throw for unknown plan', () => {
|
|
@@ -113,7 +113,7 @@ describe('Planner', () => {
|
|
|
113
113
|
|
|
114
114
|
it('should throw when executing non-approved plan', () => {
|
|
115
115
|
const plan = planner.create({ objective: 'Not approved', scope: 'test' });
|
|
116
|
-
expect(() => planner.startExecution(plan.id)).toThrow('
|
|
116
|
+
expect(() => planner.startExecution(plan.id)).toThrow('Invalid transition');
|
|
117
117
|
});
|
|
118
118
|
});
|
|
119
119
|
|
|
@@ -178,17 +178,25 @@ describe('Planner', () => {
|
|
|
178
178
|
});
|
|
179
179
|
|
|
180
180
|
describe('complete', () => {
|
|
181
|
-
it('should transition
|
|
181
|
+
it('should transition reconciling to completed', () => {
|
|
182
182
|
const plan = planner.create({ objective: 'Complete me', scope: 'test' });
|
|
183
183
|
planner.approve(plan.id);
|
|
184
184
|
planner.startExecution(plan.id);
|
|
185
|
+
planner.startReconciliation(plan.id);
|
|
185
186
|
const completed = planner.complete(plan.id);
|
|
186
187
|
expect(completed.status).toBe('completed');
|
|
187
188
|
});
|
|
188
189
|
|
|
189
|
-
it('should throw when completing
|
|
190
|
+
it('should throw when completing from executing (must go through reconciling)', () => {
|
|
191
|
+
const plan = planner.create({ objective: 'Not reconciling', scope: 'test' });
|
|
192
|
+
planner.approve(plan.id);
|
|
193
|
+
planner.startExecution(plan.id);
|
|
194
|
+
expect(() => planner.complete(plan.id)).toThrow('Invalid transition');
|
|
195
|
+
});
|
|
196
|
+
|
|
197
|
+
it('should throw when completing from draft', () => {
|
|
190
198
|
const plan = planner.create({ objective: 'Not executing', scope: 'test' });
|
|
191
|
-
expect(() => planner.complete(plan.id)).toThrow('
|
|
199
|
+
expect(() => planner.complete(plan.id)).toThrow('Invalid transition');
|
|
192
200
|
});
|
|
193
201
|
});
|
|
194
202
|
|
|
@@ -210,7 +218,7 @@ describe('Planner', () => {
|
|
|
210
218
|
});
|
|
211
219
|
|
|
212
220
|
describe('getActive', () => {
|
|
213
|
-
it('should return draft, approved, and
|
|
221
|
+
it('should return brainstorming, draft, approved, executing, validating, and reconciling plans', () => {
|
|
214
222
|
planner.create({ objective: 'Draft', scope: 'a' });
|
|
215
223
|
const p2 = planner.create({ objective: 'Approved', scope: 'b' });
|
|
216
224
|
const p3 = planner.create({ objective: 'Executing', scope: 'c' });
|
|
@@ -220,6 +228,7 @@ describe('Planner', () => {
|
|
|
220
228
|
planner.startExecution(p3.id);
|
|
221
229
|
planner.approve(p4.id);
|
|
222
230
|
planner.startExecution(p4.id);
|
|
231
|
+
planner.startReconciliation(p4.id);
|
|
223
232
|
planner.complete(p4.id);
|
|
224
233
|
const active = planner.getActive();
|
|
225
234
|
expect(active).toHaveLength(3);
|
|
@@ -237,10 +246,22 @@ describe('Planner', () => {
|
|
|
237
246
|
'Set TTL to 5 minutes since average data freshness requirement is 10 minutes',
|
|
238
247
|
],
|
|
239
248
|
tasks: [
|
|
240
|
-
{
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
249
|
+
{
|
|
250
|
+
title: 'Set up Redis client',
|
|
251
|
+
description: 'Install and configure Redis connection pool',
|
|
252
|
+
},
|
|
253
|
+
{
|
|
254
|
+
title: 'Add cache middleware',
|
|
255
|
+
description: 'Express middleware for transparent caching',
|
|
256
|
+
},
|
|
257
|
+
{
|
|
258
|
+
title: 'Add invalidation logic',
|
|
259
|
+
description: 'Purge cache on write operations to ensure consistency',
|
|
260
|
+
},
|
|
261
|
+
{
|
|
262
|
+
title: 'Write integration tests',
|
|
263
|
+
description: 'Test cache hit/miss scenarios with Redis',
|
|
264
|
+
},
|
|
244
265
|
{ title: 'Add monitoring', description: 'Track and verify cache hit rate metrics' },
|
|
245
266
|
],
|
|
246
267
|
});
|
|
@@ -325,13 +346,17 @@ describe('Planner', () => {
|
|
|
325
346
|
const plan = planner.create({
|
|
326
347
|
objective: 'Build a comprehensive authentication system for the application',
|
|
327
348
|
scope: 'Backend authentication module',
|
|
328
|
-
decisions: [
|
|
329
|
-
'Use JWT tokens because they are stateless and work well with microservices',
|
|
330
|
-
],
|
|
349
|
+
decisions: ['Use JWT tokens because they are stateless and work well with microservices'],
|
|
331
350
|
tasks: [
|
|
332
351
|
{ title: 'Create auth middleware', description: 'JWT validation middleware for Express' },
|
|
333
|
-
{
|
|
334
|
-
|
|
352
|
+
{
|
|
353
|
+
title: 'Add login endpoint',
|
|
354
|
+
description: 'POST /auth/login with credential validation',
|
|
355
|
+
},
|
|
356
|
+
{
|
|
357
|
+
title: 'Add refresh tokens',
|
|
358
|
+
description: 'Implement token refresh flow with rotation',
|
|
359
|
+
},
|
|
335
360
|
{ title: 'Write auth tests', description: 'Integration tests for all auth endpoints' },
|
|
336
361
|
],
|
|
337
362
|
});
|
|
@@ -354,9 +379,7 @@ describe('Planner', () => {
|
|
|
354
379
|
const plan = planner.create({
|
|
355
380
|
objective: 'Maybe perhaps build something simple and easy, possibly soon, etc',
|
|
356
381
|
scope: 'Various things, probably several modules, somehow',
|
|
357
|
-
decisions: [
|
|
358
|
-
'Use some appropriate approach because it seems good due to various reasons',
|
|
359
|
-
],
|
|
382
|
+
decisions: ['Use some appropriate approach because it seems good due to various reasons'],
|
|
360
383
|
tasks: [
|
|
361
384
|
{ title: 'Do some stuff', description: 'Maybe implement various things somehow' },
|
|
362
385
|
{ title: 'Maybe test', description: 'Perhaps write some tests probably' },
|
|
@@ -374,7 +397,10 @@ describe('Planner', () => {
|
|
|
374
397
|
});
|
|
375
398
|
|
|
376
399
|
it('should store check in plan history', () => {
|
|
377
|
-
const plan = planner.create({
|
|
400
|
+
const plan = planner.create({
|
|
401
|
+
objective: 'History test plan objective',
|
|
402
|
+
scope: 'test scope',
|
|
403
|
+
});
|
|
378
404
|
planner.grade(plan.id);
|
|
379
405
|
planner.grade(plan.id);
|
|
380
406
|
const history = planner.getCheckHistory(plan.id);
|
|
@@ -383,7 +409,10 @@ describe('Planner', () => {
|
|
|
383
409
|
});
|
|
384
410
|
|
|
385
411
|
it('should persist latestCheck', () => {
|
|
386
|
-
const plan = planner.create({
|
|
412
|
+
const plan = planner.create({
|
|
413
|
+
objective: 'Persist test plan objective',
|
|
414
|
+
scope: 'test scope',
|
|
415
|
+
});
|
|
387
416
|
const check = planner.grade(plan.id);
|
|
388
417
|
const latest = planner.getLatestCheck(plan.id);
|
|
389
418
|
expect(latest).not.toBeNull();
|
|
@@ -557,7 +586,7 @@ describe('Planner', () => {
|
|
|
557
586
|
});
|
|
558
587
|
|
|
559
588
|
describe('full lifecycle', () => {
|
|
560
|
-
it('should support draft → approved → executing → completed with tasks', () => {
|
|
589
|
+
it('should support draft → approved → executing → reconciling → completed with tasks', () => {
|
|
561
590
|
const plan = planner.create({
|
|
562
591
|
objective: 'Full lifecycle test',
|
|
563
592
|
scope: 'integration',
|
|
@@ -582,11 +611,60 @@ describe('Planner', () => {
|
|
|
582
611
|
planner.updateTask(plan.id, 'task-2', 'completed');
|
|
583
612
|
planner.updateTask(plan.id, 'task-3', 'skipped');
|
|
584
613
|
|
|
614
|
+
planner.startReconciliation(plan.id);
|
|
615
|
+
expect(planner.get(plan.id)!.status).toBe('reconciling');
|
|
616
|
+
|
|
585
617
|
const final = planner.complete(plan.id);
|
|
586
618
|
expect(final.status).toBe('completed');
|
|
587
619
|
expect(final.tasks[0].status).toBe('completed');
|
|
588
620
|
expect(final.tasks[1].status).toBe('completed');
|
|
589
621
|
expect(final.tasks[2].status).toBe('skipped');
|
|
590
622
|
});
|
|
623
|
+
|
|
624
|
+
it('should support brainstorming → draft → approved → executing lifecycle', () => {
|
|
625
|
+
const plan = planner.create({
|
|
626
|
+
objective: 'Brainstorming lifecycle test',
|
|
627
|
+
scope: 'integration',
|
|
628
|
+
initialStatus: 'brainstorming',
|
|
629
|
+
});
|
|
630
|
+
expect(plan.status).toBe('brainstorming');
|
|
631
|
+
|
|
632
|
+
planner.promoteToDraft(plan.id);
|
|
633
|
+
expect(planner.get(plan.id)!.status).toBe('draft');
|
|
634
|
+
|
|
635
|
+
planner.approve(plan.id);
|
|
636
|
+
expect(planner.get(plan.id)!.status).toBe('approved');
|
|
637
|
+
});
|
|
638
|
+
|
|
639
|
+
it('should support validating state', () => {
|
|
640
|
+
const plan = planner.create({
|
|
641
|
+
objective: 'Validation lifecycle test',
|
|
642
|
+
scope: 'integration',
|
|
643
|
+
tasks: [{ title: 'Task 1', description: 'Test task' }],
|
|
644
|
+
});
|
|
645
|
+
planner.approve(plan.id);
|
|
646
|
+
planner.startExecution(plan.id);
|
|
647
|
+
planner.startValidation(plan.id);
|
|
648
|
+
expect(planner.get(plan.id)!.status).toBe('validating');
|
|
649
|
+
|
|
650
|
+
// Can update tasks during validation
|
|
651
|
+
planner.updateTask(plan.id, 'task-1', 'completed');
|
|
652
|
+
|
|
653
|
+
// Can go back to executing from validating
|
|
654
|
+
planner.startExecution(plan.id);
|
|
655
|
+
expect(planner.get(plan.id)!.status).toBe('executing');
|
|
656
|
+
});
|
|
657
|
+
|
|
658
|
+
it('should support archiving completed plans', () => {
|
|
659
|
+
const plan = planner.create({ objective: 'Archive test', scope: 'test' });
|
|
660
|
+
planner.approve(plan.id);
|
|
661
|
+
planner.startExecution(plan.id);
|
|
662
|
+
planner.startReconciliation(plan.id);
|
|
663
|
+
planner.complete(plan.id);
|
|
664
|
+
|
|
665
|
+
const archived = planner.archive();
|
|
666
|
+
expect(archived).toHaveLength(1);
|
|
667
|
+
expect(archived[0].status).toBe('archived');
|
|
668
|
+
});
|
|
591
669
|
});
|
|
592
670
|
});
|