@cleocode/core 2026.4.29 → 2026.4.31

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (89) hide show
  1. package/dist/bootstrap.d.ts +35 -0
  2. package/dist/bootstrap.d.ts.map +1 -1
  3. package/dist/code/index.d.ts +8 -4
  4. package/dist/code/index.d.ts.map +1 -1
  5. package/dist/code/parser.d.ts +22 -9
  6. package/dist/code/parser.d.ts.map +1 -1
  7. package/dist/hooks/handlers/session-hooks.d.ts +11 -4
  8. package/dist/hooks/handlers/session-hooks.d.ts.map +1 -1
  9. package/dist/hooks/payload-schemas.d.ts +6 -6
  10. package/dist/index.d.ts +2 -0
  11. package/dist/index.d.ts.map +1 -1
  12. package/dist/index.js +3859 -3008
  13. package/dist/index.js.map +4 -4
  14. package/dist/internal.d.ts +10 -7
  15. package/dist/internal.d.ts.map +1 -1
  16. package/dist/lib/tree-sitter-languages.d.ts +11 -7
  17. package/dist/lib/tree-sitter-languages.d.ts.map +1 -1
  18. package/dist/memory/auto-extract.d.ts +27 -15
  19. package/dist/memory/auto-extract.d.ts.map +1 -1
  20. package/dist/memory/brain-backfill.d.ts +59 -0
  21. package/dist/memory/brain-backfill.d.ts.map +1 -0
  22. package/dist/memory/brain-purge.d.ts +51 -0
  23. package/dist/memory/brain-purge.d.ts.map +1 -0
  24. package/dist/memory/brain-retrieval.d.ts.map +1 -1
  25. package/dist/memory/brain-search.d.ts.map +1 -1
  26. package/dist/memory/decisions.d.ts.map +1 -1
  27. package/dist/memory/engine-compat.d.ts +71 -0
  28. package/dist/memory/engine-compat.d.ts.map +1 -1
  29. package/dist/memory/graph-auto-populate.d.ts +65 -0
  30. package/dist/memory/graph-auto-populate.d.ts.map +1 -0
  31. package/dist/memory/graph-queries.d.ts +127 -0
  32. package/dist/memory/graph-queries.d.ts.map +1 -0
  33. package/dist/memory/learnings.d.ts +2 -0
  34. package/dist/memory/learnings.d.ts.map +1 -1
  35. package/dist/memory/patterns.d.ts +2 -0
  36. package/dist/memory/patterns.d.ts.map +1 -1
  37. package/dist/memory/quality-scoring.d.ts +90 -0
  38. package/dist/memory/quality-scoring.d.ts.map +1 -0
  39. package/dist/sessions/session-memory-bridge.d.ts +16 -10
  40. package/dist/sessions/session-memory-bridge.d.ts.map +1 -1
  41. package/dist/store/brain-accessor.d.ts +7 -0
  42. package/dist/store/brain-accessor.d.ts.map +1 -1
  43. package/dist/store/brain-schema.d.ts +185 -11
  44. package/dist/store/brain-schema.d.ts.map +1 -1
  45. package/dist/store/brain-sqlite.d.ts.map +1 -1
  46. package/dist/store/nexus-schema.d.ts +480 -2
  47. package/dist/store/nexus-schema.d.ts.map +1 -1
  48. package/dist/store/tasks-schema.d.ts +9 -9
  49. package/dist/store/validation-schemas.d.ts +44 -28
  50. package/dist/store/validation-schemas.d.ts.map +1 -1
  51. package/dist/system/dependencies.d.ts +43 -0
  52. package/dist/system/dependencies.d.ts.map +1 -0
  53. package/dist/system/health.d.ts +3 -0
  54. package/dist/system/health.d.ts.map +1 -1
  55. package/dist/tasks/complete.d.ts.map +1 -1
  56. package/package.json +19 -19
  57. package/src/bootstrap.ts +124 -0
  58. package/src/code/index.ts +20 -4
  59. package/src/code/parser.ts +310 -110
  60. package/src/hooks/handlers/__tests__/hook-automation-e2e.test.ts +19 -45
  61. package/src/hooks/handlers/__tests__/session-hooks.test.ts +42 -54
  62. package/src/hooks/handlers/session-hooks.ts +11 -33
  63. package/src/index.ts +14 -0
  64. package/src/internal.ts +37 -7
  65. package/src/lib/tree-sitter-languages.ts +11 -7
  66. package/src/memory/__tests__/auto-extract.test.ts +20 -82
  67. package/src/memory/__tests__/embedding-pipeline.test.ts +389 -0
  68. package/src/memory/auto-extract.ts +34 -120
  69. package/src/memory/brain-backfill.ts +471 -0
  70. package/src/memory/brain-purge.ts +315 -0
  71. package/src/memory/brain-retrieval.ts +43 -2
  72. package/src/memory/brain-search.ts +23 -6
  73. package/src/memory/decisions.ts +76 -3
  74. package/src/memory/engine-compat.ts +168 -0
  75. package/src/memory/graph-auto-populate.ts +173 -0
  76. package/src/memory/graph-queries.ts +424 -0
  77. package/src/memory/learnings.ts +55 -7
  78. package/src/memory/patterns.ts +66 -13
  79. package/src/memory/quality-scoring.ts +173 -0
  80. package/src/sessions/__tests__/session-memory-bridge.test.ts +27 -49
  81. package/src/sessions/session-memory-bridge.ts +19 -47
  82. package/src/store/__tests__/brain-accessor-pageindex.test.ts +93 -22
  83. package/src/store/brain-accessor.ts +48 -2
  84. package/src/store/brain-schema.ts +165 -13
  85. package/src/store/brain-sqlite.ts +35 -0
  86. package/src/store/nexus-schema.ts +257 -3
  87. package/src/system/dependencies.ts +534 -0
  88. package/src/system/health.ts +126 -22
  89. package/src/tasks/complete.ts +40 -0
@@ -1,77 +1,65 @@
1
1
  import { beforeEach, describe, expect, it, vi } from 'vitest';
2
2
 
3
- const observeBrainMock = vi.fn();
3
+ const mocks = vi.hoisted(() => ({
4
+ maybeRefreshMemoryBridge: vi.fn(),
5
+ gradeSession: vi.fn(),
6
+ loadConfig: vi.fn(),
7
+ }));
8
+
9
+ vi.mock('../memory-bridge-refresh.js', () => ({
10
+ maybeRefreshMemoryBridge: mocks.maybeRefreshMemoryBridge,
11
+ }));
12
+
13
+ vi.mock('../../../sessions/session-grade.js', () => ({
14
+ gradeSession: mocks.gradeSession,
15
+ }));
4
16
 
5
- vi.mock('../../../memory/brain-retrieval.js', () => ({
6
- observeBrain: observeBrainMock,
17
+ vi.mock('../../../config.js', () => ({
18
+ loadConfig: mocks.loadConfig,
7
19
  }));
8
20
 
9
21
  import { handleSessionEnd, handleSessionStart } from '../session-hooks.js';
10
22
 
11
23
  describe('session hook handlers', () => {
12
24
  beforeEach(() => {
13
- observeBrainMock.mockReset();
25
+ mocks.maybeRefreshMemoryBridge.mockReset();
26
+ mocks.maybeRefreshMemoryBridge.mockResolvedValue(undefined);
27
+ mocks.gradeSession.mockResolvedValue(undefined);
28
+ mocks.loadConfig.mockResolvedValue({ brain: { autoCapture: false } });
14
29
  });
15
30
 
16
- it('swallows missing brain schema errors on session start', async () => {
17
- observeBrainMock.mockRejectedValue(
18
- new Error('SQLITE_ERROR: no such table: brain_observations'),
19
- );
31
+ it('handleSessionStart refreshes the memory bridge', async () => {
32
+ await handleSessionStart('/tmp/project', {
33
+ sessionId: 'ses-1',
34
+ timestamp: '2026-03-04T00:00:00.000Z',
35
+ name: 'Test Session',
36
+ scope: 'T5306',
37
+ });
20
38
 
21
- await expect(
22
- handleSessionStart('/tmp/project', {
23
- sessionId: 'ses-1',
24
- timestamp: '2026-03-04T00:00:00.000Z',
25
- name: 'Test Session',
26
- scope: 'T5306',
27
- }),
28
- ).resolves.toBeUndefined();
39
+ expect(mocks.maybeRefreshMemoryBridge).toHaveBeenCalledTimes(1);
40
+ expect(mocks.maybeRefreshMemoryBridge).toHaveBeenCalledWith('/tmp/project');
29
41
  });
30
42
 
31
- it('swallows missing brain schema errors on session end', async () => {
32
- observeBrainMock.mockRejectedValue(new Error('no such table: brain_decisions'));
43
+ it('handleSessionEnd refreshes the memory bridge', async () => {
44
+ await handleSessionEnd('/tmp/project', {
45
+ sessionId: 'ses-2',
46
+ timestamp: '2026-03-04T00:30:00.000Z',
47
+ duration: 1800,
48
+ tasksCompleted: ['T5306', 'T5307'],
49
+ });
50
+
51
+ expect(mocks.maybeRefreshMemoryBridge).toHaveBeenCalledTimes(1);
52
+ expect(mocks.maybeRefreshMemoryBridge).toHaveBeenCalledWith('/tmp/project');
53
+ });
33
54
 
55
+ it('handleSessionEnd resolves normally with no tasks', async () => {
34
56
  await expect(
35
57
  handleSessionEnd('/tmp/project', {
36
- sessionId: 'ses-1',
58
+ sessionId: 'ses-3',
37
59
  timestamp: '2026-03-04T00:30:00.000Z',
38
- duration: 1800,
60
+ duration: 900,
39
61
  tasksCompleted: [],
40
62
  }),
41
63
  ).resolves.toBeUndefined();
42
64
  });
43
-
44
- it('rethrows non-schema errors', async () => {
45
- observeBrainMock.mockRejectedValue(new Error('database is locked'));
46
-
47
- await expect(
48
- handleSessionStart('/tmp/project', {
49
- sessionId: 'ses-1',
50
- timestamp: '2026-03-04T00:00:00.000Z',
51
- name: 'Test Session',
52
- scope: 'T5306',
53
- }),
54
- ).rejects.toThrow('database is locked');
55
- });
56
-
57
- it('records session context when observe succeeds', async () => {
58
- observeBrainMock.mockResolvedValue(undefined);
59
-
60
- await handleSessionEnd('/tmp/project', {
61
- sessionId: 'ses-2',
62
- timestamp: '2026-03-04T00:45:00.000Z',
63
- duration: 2700,
64
- tasksCompleted: ['T5306', 'T5307'],
65
- });
66
-
67
- expect(observeBrainMock).toHaveBeenCalledTimes(1);
68
- expect(observeBrainMock).toHaveBeenCalledWith(
69
- '/tmp/project',
70
- expect.objectContaining({
71
- title: 'Session end: ses-2',
72
- type: 'change',
73
- sourceSessionId: 'ses-2',
74
- }),
75
- );
76
- });
77
65
  });
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * Session Hook Handlers - Phase 2D of T5237
3
3
  *
4
- * Handlers that capture session lifecycle events to BRAIN via memory.observe.
4
+ * Handlers that capture session lifecycle events.
5
5
  * Auto-registers on module load.
6
6
  *
7
7
  * T138: Triggers memory bridge refresh on session start and end.
@@ -10,65 +10,43 @@
10
10
  * T5158: Auto-snapshots SQLite databases (tasks.db + brain.db) via
11
11
  * VACUUM INTO on SessionEnd to preserve a recovery point now that
12
12
  * the databases are no longer tracked in project git (ADR-013).
13
+ * T527: Removed duplicate session observeBrain writes — session data already
14
+ * lives in the sessions table; writing it again to brain_observations
15
+ * was pure noise.
13
16
  */
14
17
 
15
18
  import { hooks } from '../registry.js';
16
19
  import type { SessionEndPayload, SessionStartPayload } from '../types.js';
17
- import { isMissingBrainSchemaError } from './handler-helpers.js';
18
20
  import { maybeRefreshMemoryBridge } from './memory-bridge-refresh.js';
19
21
 
20
22
  /**
21
- * Handle SessionStart - capture initial session context
23
+ * Handle SessionStart - refresh memory bridge on session start.
22
24
  *
23
25
  * T138: Refresh memory bridge on session start.
24
26
  * T139: Regenerate bridge with session scope context.
27
+ * T527: Removed duplicate observeBrain write — session data is already
28
+ * persisted in the sessions table.
25
29
  */
26
30
  export async function handleSessionStart(
27
31
  projectRoot: string,
28
- payload: SessionStartPayload,
32
+ _payload: SessionStartPayload,
29
33
  ): Promise<void> {
30
- const { observeBrain } = await import('../../memory/brain-retrieval.js');
31
-
32
- try {
33
- await observeBrain(projectRoot, {
34
- text: `Session started: ${payload.name}\nScope: ${JSON.stringify(payload.scope)}\nAgent: ${payload.agent || 'unknown'}`,
35
- title: `Session start: ${payload.name}`,
36
- type: 'discovery',
37
- sourceSessionId: payload.sessionId,
38
- sourceType: 'agent',
39
- });
40
- } catch (err) {
41
- if (!isMissingBrainSchemaError(err)) throw err;
42
- }
43
-
44
34
  // T138/T139: Refresh memory bridge after session starts (best-effort)
45
35
  await maybeRefreshMemoryBridge(projectRoot);
46
36
  }
47
37
 
48
38
  /**
49
- * Handle SessionEnd - capture session summary
39
+ * Handle SessionEnd - run post-session tasks and refresh memory bridge.
50
40
  *
51
41
  * T138: Refresh memory bridge after session ends.
52
42
  * T144: Extract transcript observations via cross-provider adapter.
43
+ * T527: Removed duplicate observeBrain write — session data is already
44
+ * persisted in the sessions table.
53
45
  */
54
46
  export async function handleSessionEnd(
55
47
  projectRoot: string,
56
48
  payload: SessionEndPayload,
57
49
  ): Promise<void> {
58
- const { observeBrain } = await import('../../memory/brain-retrieval.js');
59
-
60
- try {
61
- await observeBrain(projectRoot, {
62
- text: `Session ended: ${payload.sessionId}\nDuration: ${payload.duration}s\nTasks completed: ${payload.tasksCompleted.join(', ') || 'none'}`,
63
- title: `Session end: ${payload.sessionId}`,
64
- type: 'change',
65
- sourceSessionId: payload.sessionId,
66
- sourceType: 'agent',
67
- });
68
- } catch (err) {
69
- if (!isMissingBrainSchemaError(err)) throw err;
70
- }
71
-
72
50
  // Auto-grade session and feed insights to brain.db (best-effort)
73
51
  try {
74
52
  const { gradeSession } = await import('../../sessions/session-grade.js');
package/src/index.ts CHANGED
@@ -299,6 +299,20 @@ export {
299
299
  timelineBrain,
300
300
  } from './memory/brain-retrieval.js';
301
301
  export { searchBrain } from './memory/brain-search.js';
302
+ export type {
303
+ DecisionQualityInput,
304
+ LearningQualityInput,
305
+ ObservationQualityInput,
306
+ PatternQualityInput,
307
+ } from './memory/quality-scoring.js';
308
+ // Quality scoring (T531) — exported for backfill (T530) and future hooks
309
+ export {
310
+ computeDecisionQuality,
311
+ computeLearningQuality,
312
+ computeObservationQuality,
313
+ computePatternQuality,
314
+ QUALITY_SCORE_THRESHOLD,
315
+ } from './memory/quality-scoring.js';
302
316
  // Migration (flat re-exports for backward compatibility)
303
317
  export {
304
318
  compareSemver,
package/src/internal.ts CHANGED
@@ -22,6 +22,16 @@ export * from './index.js';
22
22
  // Extended flat exports (required by @cleocode/cleo)
23
23
  // ---------------------------------------------------------------------------
24
24
 
25
+ // Code analysis (Smart Explore) — canonical source: @cleocode/nexus
26
+ export {
27
+ batchParse,
28
+ isTreeSitterAvailable,
29
+ parseFile,
30
+ type SmartSearchOptions,
31
+ smartOutline,
32
+ smartSearch,
33
+ smartUnfold,
34
+ } from '@cleocode/nexus';
25
35
  export { exportTasks } from './admin/export.js';
26
36
  export { exportTasksPackage } from './admin/export-tasks.js';
27
37
  // Admin
@@ -36,17 +46,17 @@ export type { AuditEntry } from './audit.js';
36
46
  // Backfill
37
47
  export type { BackfillOptions, BackfillResult, BackfillTaskChange } from './backfill/index.js';
38
48
  export { backfillTasks, generateAcFromDescription } from './backfill/index.js';
39
- export type { BootstrapContext, BootstrapOptions } from './bootstrap.js';
49
+ export type {
50
+ BootstrapContext,
51
+ BootstrapOptions,
52
+ BootstrapVerificationResult,
53
+ } from './bootstrap.js';
40
54
  // Bootstrap (global setup)
41
55
  export {
42
56
  bootstrapGlobalCleo,
43
57
  installSkillsGlobally,
58
+ verifyBootstrapComplete,
44
59
  } from './bootstrap.js';
45
- export { smartOutline } from './code/outline.js';
46
- // Code analysis (Smart Explore)
47
- export { batchParse, isTreeSitterAvailable, parseFile } from './code/parser.js';
48
- export { type SmartSearchOptions, smartSearch } from './code/search.js';
49
- export { smartUnfold } from './code/unfold.js';
50
60
  export type { ViolationLogEntry } from './compliance/protocol-enforcement.js';
51
61
  // Compliance
52
62
  export { ProtocolEnforcer, protocolEnforcer } from './compliance/protocol-enforcement.js';
@@ -63,7 +73,6 @@ export { validatePayload } from './hooks/payload-schemas.js';
63
73
  // Hooks
64
74
  export type { HookEvent, ProviderHookEvent } from './hooks/provider-hooks.js';
65
75
  export { isProviderHookEvent } from './hooks/types.js';
66
-
67
76
  // Init (additional)
68
77
  export { isAutoInitEnabled } from './init.js';
69
78
  export {
@@ -146,6 +155,8 @@ export {
146
155
  } from './lifecycle/index.js';
147
156
  export { STAGE_DEFINITIONS } from './lifecycle/stages.js';
148
157
  export { instantiateTessera, showTessera } from './lifecycle/tessera-engine.js';
158
+ export type { BrainBackfillResult } from './memory/brain-backfill.js';
159
+ export { backfillBrainGraph } from './memory/brain-backfill.js';
149
160
  // Memory — brain lifecycle (temporal decay + consolidation)
150
161
  export type { ConsolidationResult, DecayResult } from './memory/brain-lifecycle.js';
151
162
  export { applyTemporalDecay, consolidateMemories } from './memory/brain-lifecycle.js';
@@ -159,6 +170,8 @@ export type {
159
170
  BrainMaintenanceResult,
160
171
  } from './memory/brain-maintenance.js';
161
172
  export { runBrainMaintenance } from './memory/brain-maintenance.js';
173
+ export type { PurgeResult } from './memory/brain-purge.js';
174
+ export { purgeBrainNoise } from './memory/brain-purge.js';
162
175
  export type {
163
176
  PopulateEmbeddingsOptions,
164
177
  PopulateEmbeddingsResult,
@@ -174,9 +187,13 @@ export {
174
187
  memoryFetch,
175
188
  memoryFind,
176
189
  memoryGraphAdd,
190
+ memoryGraphContext,
177
191
  memoryGraphNeighbors,
192
+ memoryGraphRelated,
178
193
  memoryGraphRemove,
179
194
  memoryGraphShow,
195
+ memoryGraphStatsFull,
196
+ memoryGraphTrace,
180
197
  memoryLearningFind,
181
198
  memoryLearningStats,
182
199
  memoryLearningStore,
@@ -193,6 +210,13 @@ export {
193
210
  memoryTimeline,
194
211
  memoryUnlink,
195
212
  } from './memory/engine-compat.js';
213
+ // Memory — graph traversal query functions (T535)
214
+ export type {
215
+ GraphStats,
216
+ NodeContext,
217
+ RelatedNode,
218
+ TraceNode,
219
+ } from './memory/graph-queries.js';
196
220
  // Memory — pipeline manifests
197
221
  export {
198
222
  pipelineManifestAppend,
@@ -491,6 +515,12 @@ export type { BackupEntry, BackupResult, RestoreResult } from './system/backup.j
491
515
  export { listSystemBackups, restoreBackup } from './system/backup.js';
492
516
  export type { CleanupResult } from './system/cleanup.js';
493
517
  export { cleanupSystem } from './system/cleanup.js';
518
+ // System — dependency registry
519
+ export {
520
+ checkAllDependencies,
521
+ checkDependency,
522
+ getDependencySpecs,
523
+ } from './system/dependencies.js';
494
524
  export type { DiagnosticsResult, HealthResult } from './system/health.js';
495
525
  export { getSystemDiagnostics, getSystemHealth, startupHealthCheck } from './system/health.js';
496
526
  export type {
@@ -1,10 +1,11 @@
1
1
  /**
2
2
  * Tree-sitter language detection and grammar resolution.
3
3
  *
4
- * Maps file extensions to tree-sitter grammar packages for use by
5
- * the Smart Explore code analysis pipeline.
4
+ * Maps file extensions to tree-sitter language identifiers used by
5
+ * the Smart Explore code analysis pipeline (outline, search, unfold).
6
+ * Grammar loading is handled internally by parser.ts via native Node bindings.
6
7
  *
7
- * @task T148
8
+ * @task T509
8
9
  */
9
10
 
10
11
  /** Supported tree-sitter language identifiers. */
@@ -60,9 +61,9 @@ const GRAMMAR_PACKAGE_MAP: Record<TreeSitterLanguage, string> = {
60
61
  };
61
62
 
62
63
  /**
63
- * Detect tree-sitter language from a file path.
64
+ * Detect tree-sitter language from a file path or file name.
64
65
  *
65
- * @param filePath - Path to a source file
66
+ * @param filePath - Path to a source file (or bare file name)
66
67
  * @returns The detected language, or undefined if unsupported
67
68
  */
68
69
  export function detectLanguage(filePath: string): TreeSitterLanguage | undefined {
@@ -74,6 +75,9 @@ export function detectLanguage(filePath: string): TreeSitterLanguage | undefined
74
75
  /**
75
76
  * Get the npm grammar package name for a language.
76
77
  *
78
+ * The package name matches the native Node binding that parser.ts loads
79
+ * via {@link createRequire}. Useful for diagnostics and install hints.
80
+ *
77
81
  * @param language - A tree-sitter language identifier
78
82
  * @returns The npm package name containing the grammar
79
83
  */
@@ -81,8 +85,8 @@ export function grammarPackage(language: TreeSitterLanguage): string {
81
85
  return GRAMMAR_PACKAGE_MAP[language];
82
86
  }
83
87
 
84
- /** All supported file extensions. */
88
+ /** All supported file extensions (without leading dot). */
85
89
  export const SUPPORTED_EXTENSIONS = Object.keys(EXTENSION_MAP);
86
90
 
87
- /** All supported languages. */
91
+ /** All supported languages (deduplicated). */
88
92
  export const SUPPORTED_LANGUAGES: TreeSitterLanguage[] = [...new Set(Object.values(EXTENSION_MAP))];
@@ -1,7 +1,11 @@
1
1
  /**
2
2
  * Unit tests for auto-extract memory pipeline.
3
3
  *
4
- * All external dependencies are mocked — this tests extraction logic only.
4
+ * extractTaskCompletionMemory and extractSessionEndMemory are disabled no-ops
5
+ * per T523 CA1 specification. Tests verify the no-op contract holds.
6
+ *
7
+ * @task T526
8
+ * @epic T523
5
9
  */
6
10
 
7
11
  import type { Task } from '@cleocode/contracts';
@@ -21,7 +25,7 @@ vi.mock('../decisions.js', () => ({
21
25
  storeDecision: vi.fn().mockResolvedValue(undefined),
22
26
  }));
23
27
 
24
- // Mock getAccessor used inside auto-extract for pattern detection
28
+ // Mock getAccessor should never be called by disabled functions
25
29
  vi.mock('../../store/data-accessor.js', () => ({
26
30
  getAccessor: vi.fn(),
27
31
  }));
@@ -80,24 +84,16 @@ beforeEach(() => {
80
84
  });
81
85
 
82
86
  describe('extractTaskCompletionMemory', () => {
83
- it('creates a learning from completed task', async () => {
87
+ it('is a no-op does not write learnings', async () => {
84
88
  const task = makeTask({ id: 'T001', title: 'Fix auth bug', description: 'Auth was broken' });
85
89
  setupAccessor([task]);
86
90
 
87
91
  await extractTaskCompletionMemory('/mock/root', task);
88
92
 
89
- expect(storeLearning).toHaveBeenCalledWith(
90
- '/mock/root',
91
- expect.objectContaining({
92
- insight: `Completed: ${task.title} — ${task.description}`,
93
- source: `task-completion:${task.id}`,
94
- confidence: 0.7,
95
- actionable: true,
96
- }),
97
- );
93
+ expect(storeLearning).not.toHaveBeenCalled();
98
94
  });
99
95
 
100
- it('creates dependency learning when task has dependencies', async () => {
96
+ it('is a no-op does not write dependency learnings', async () => {
101
97
  const task = makeTask({
102
98
  id: 'T002',
103
99
  title: 'Deploy feature',
@@ -108,28 +104,10 @@ describe('extractTaskCompletionMemory', () => {
108
104
 
109
105
  await extractTaskCompletionMemory('/mock/root', task);
110
106
 
111
- const calls = (storeLearning as ReturnType<typeof vi.fn>).mock.calls;
112
- const depCall = calls.find((c) =>
113
- (c[1] as { insight: string }).insight.includes('depended on'),
114
- );
115
- expect(depCall).toBeDefined();
116
- expect((depCall![1] as { insight: string }).insight).toContain('T001, T003');
117
- expect((depCall![1] as { source: string }).source).toBe('task-completion:T002');
118
- });
119
-
120
- it('does not create dependency learning when task has no dependencies', async () => {
121
- const task = makeTask({ id: 'T003', title: 'Solo task', depends: [] });
122
- setupAccessor([task]);
123
-
124
- await extractTaskCompletionMemory('/mock/root', task);
125
-
126
- const depCall = (storeLearning as ReturnType<typeof vi.fn>).mock.calls.find((c) =>
127
- (c[1] as { insight: string }).insight.includes('depended on'),
128
- );
129
- expect(depCall).toBeUndefined();
107
+ expect(storeLearning).not.toHaveBeenCalled();
130
108
  });
131
109
 
132
- it('stores a pattern when a label appears 3+ times in done tasks', async () => {
110
+ it('is a no-op does not write label patterns', async () => {
133
111
  const completedTasks = [
134
112
  makeTask({ id: 'T010', title: 'A', labels: ['bug'] }),
135
113
  makeTask({ id: 'T011', title: 'B', labels: ['bug'] }),
@@ -140,17 +118,11 @@ describe('extractTaskCompletionMemory', () => {
140
118
 
141
119
  await extractTaskCompletionMemory('/mock/root', trigger);
142
120
 
143
- expect(storePattern).toHaveBeenCalledWith(
144
- '/mock/root',
145
- expect.objectContaining({
146
- type: 'success',
147
- impact: 'medium',
148
- }),
149
- );
121
+ expect(storePattern).not.toHaveBeenCalled();
122
+ expect(getAccessor).not.toHaveBeenCalled();
150
123
  });
151
124
 
152
- it('does not throw on error', async () => {
153
- (storeLearning as ReturnType<typeof vi.fn>).mockRejectedValueOnce(new Error('db error'));
125
+ it('resolves to undefined without throwing', async () => {
154
126
  setupAccessor([]);
155
127
 
156
128
  await expect(
@@ -160,7 +132,7 @@ describe('extractTaskCompletionMemory', () => {
160
132
  });
161
133
 
162
134
  describe('extractSessionEndMemory', () => {
163
- it('creates a decision when tasks are completed', async () => {
135
+ it('is a no-op does not write a session decision', async () => {
164
136
  const tasks = [
165
137
  makeTask({ id: 'T001', title: 'Task one' }),
166
138
  makeTask({ id: 'T002', title: 'Task two' }),
@@ -169,32 +141,10 @@ describe('extractSessionEndMemory', () => {
169
141
 
170
142
  await extractSessionEndMemory('/mock/root', session, tasks);
171
143
 
172
- expect(storeDecision).toHaveBeenCalledWith(
173
- '/mock/root',
174
- expect.objectContaining({
175
- type: 'process',
176
- confidence: 'medium',
177
- }),
178
- );
179
- const call = (storeDecision as ReturnType<typeof vi.fn>).mock.calls[0][1] as {
180
- decision: string;
181
- rationale: string;
182
- };
183
- expect(call.decision).toContain('S-001');
184
- expect(call.decision).toContain('T001');
185
- expect(call.decision).toContain('T002');
186
- expect(call.rationale).toContain('test scope');
187
- });
188
-
189
- it('does not create a decision when no tasks completed', async () => {
190
- const session = makeSessionData({ tasksCompleted: [] });
191
-
192
- await extractSessionEndMemory('/mock/root', session, []);
193
-
194
144
  expect(storeDecision).not.toHaveBeenCalled();
195
145
  });
196
146
 
197
- it('creates per-task learnings', async () => {
147
+ it('is a no-op — does not write per-task learnings', async () => {
198
148
  const tasks = [
199
149
  makeTask({ id: 'T001', title: 'Task one', description: 'Desc one' }),
200
150
  makeTask({ id: 'T002', title: 'Task two', description: 'Desc two' }),
@@ -203,15 +153,10 @@ describe('extractSessionEndMemory', () => {
203
153
 
204
154
  await extractSessionEndMemory('/mock/root', session, tasks);
205
155
 
206
- const calls = (storeLearning as ReturnType<typeof vi.fn>).mock.calls;
207
- expect(calls.length).toBe(2);
208
- calls.forEach((c) => {
209
- expect((c[1] as { source: string }).source).toBe('session-end:S-002');
210
- expect((c[1] as { confidence: number }).confidence).toBe(0.7);
211
- });
156
+ expect(storeLearning).not.toHaveBeenCalled();
212
157
  });
213
158
 
214
- it('stores a workflow pattern when 2+ tasks share a label', async () => {
159
+ it('is a no-op does not write workflow patterns', async () => {
215
160
  const tasks = [
216
161
  makeTask({ id: 'T001', title: 'A', labels: ['feature'] }),
217
162
  makeTask({ id: 'T002', title: 'B', labels: ['feature'] }),
@@ -220,17 +165,10 @@ describe('extractSessionEndMemory', () => {
220
165
 
221
166
  await extractSessionEndMemory('/mock/root', session, tasks);
222
167
 
223
- expect(storePattern).toHaveBeenCalledWith(
224
- '/mock/root',
225
- expect.objectContaining({
226
- type: 'workflow',
227
- impact: 'medium',
228
- }),
229
- );
168
+ expect(storePattern).not.toHaveBeenCalled();
230
169
  });
231
170
 
232
- it('does not throw on error', async () => {
233
- (storeDecision as ReturnType<typeof vi.fn>).mockRejectedValueOnce(new Error('db fail'));
171
+ it('resolves to undefined without throwing', async () => {
234
172
  const tasks = [makeTask({ id: 'T001', title: 'X' })];
235
173
  const session = makeSessionData({ tasksCompleted: ['T001'] });
236
174