recallnest 2.2.0 → 2.2.1

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 (232) hide show
  1. package/.env +4 -0
  2. package/config.json +55 -0
  3. package/logs/daily-recall.error.log +0 -0
  4. package/logs/daily-recall.log +30 -0
  5. package/logs/ingest-2026-04-11.log +110 -0
  6. package/logs/ingest-2026-04-12.log +90 -0
  7. package/logs/launchagent-stderr.log +0 -0
  8. package/logs/launchagent-stdout.log +0 -0
  9. package/package.json +1 -1
  10. package/.claude-plugin/CLAUDE.md +0 -29
  11. package/.claude-plugin/marketplace.json +0 -18
  12. package/.dockerignore +0 -11
  13. package/.github/workflows/ci.yml +0 -28
  14. package/.mcp.json +0 -9
  15. package/CHANGELOG.md +0 -51
  16. package/DEMO.md +0 -94
  17. package/Dockerfile +0 -20
  18. package/EVAL.md +0 -89
  19. package/FAILURES.md +0 -75
  20. package/OPERATIONS.md +0 -59
  21. package/ROADMAP.md +0 -245
  22. package/SKILL.md +0 -250
  23. package/assets/dashboard.png +0 -0
  24. package/assets/knowledge-graph.png +0 -0
  25. package/assets/recallnest-workbench.png +0 -0
  26. package/assets/screenshots/ui-full.png +0 -0
  27. package/assets/screenshots/ui-overview.png +0 -0
  28. package/assets/screenshots/ui-search.png +0 -0
  29. package/assets/ui/app.js +0 -1031
  30. package/assets/ui/index.html +0 -224
  31. package/assets/ui/styles.css +0 -781
  32. package/assets/wechat-qr.jpg +0 -0
  33. package/bun.lock +0 -337
  34. package/daily-reflections/2026-03-05.md +0 -15
  35. package/daily-reflections/2026-03-06.md +0 -15
  36. package/daily-reflections/2026-03-07.md +0 -7
  37. package/daily-reflections/2026-03-08.md +0 -34
  38. package/daily-reflections/2026-03-09.md +0 -35
  39. package/daily-reflections/2026-03-10.md +0 -36
  40. package/daily-reflections/2026-03-11.md +0 -35
  41. package/daily-reflections/2026-03-12.md +0 -36
  42. package/daily-reflections/2026-03-13.md +0 -35
  43. package/daily-reflections/2026-03-14.md +0 -35
  44. package/daily-reflections/2026-03-15.md +0 -35
  45. package/daily-reflections/2026-03-16.md +0 -41
  46. package/daily-reflections/2026-03-17.md +0 -10
  47. package/daily-reflections/2026-03-18.md +0 -10
  48. package/daily-reflections/2026-03-19.md +0 -10
  49. package/daily-reflections/2026-03-20.md +0 -10
  50. package/daily-reflections/2026-03-21.md +0 -10
  51. package/daily-reflections/2026-03-22.md +0 -10
  52. package/daily-reflections/2026-03-23.md +0 -10
  53. package/daily-reflections/2026-03-24.md +0 -10
  54. package/daily-reflections/2026-03-25.md +0 -32
  55. package/daily-reflections/2026-03-26.md +0 -37
  56. package/daily-reflections/2026-03-27.md +0 -35
  57. package/daily-reflections/2026-03-28.md +0 -35
  58. package/daily-reflections/2026-03-29.md +0 -35
  59. package/daily-reflections/2026-03-30.md +0 -36
  60. package/daily-reflections/2026-03-31.md +0 -35
  61. package/daily-reflections/2026-04-01.md +0 -35
  62. package/daily-reflections/2026-04-02.md +0 -35
  63. package/daily-reflections/2026-04-03.md +0 -37
  64. package/daily-reflections/2026-04-04.md +0 -37
  65. package/daily-reflections/2026-04-05.md +0 -35
  66. package/daily-reflections/2026-04-06.md +0 -36
  67. package/daily-reflections/2026-04-07.md +0 -35
  68. package/daily-reflections/2026-04-08.md +0 -35
  69. package/daily-reflections/2026-04-12.md +0 -35
  70. package/data/alias-map.example.json +0 -22
  71. package/docs/api-reference.md +0 -1291
  72. package/docs/architecture.md +0 -168
  73. package/docs/memory-boundary-contract.md +0 -226
  74. package/docs/memory-categories.md +0 -119
  75. package/docs/pipeline-audit-ultramemory.md +0 -177
  76. package/docs/plans/2026-03-08-query-expansion.md +0 -270
  77. package/docs/plans/2026-03-16-continuity-layer.md +0 -196
  78. package/docs/plans/2026-04-01-recall-boost.md +0 -100
  79. package/docs/plans/mcp-tiering-plan.md +0 -126
  80. package/docs/research/recall-quality-audit-2026-03-22.md +0 -153
  81. package/docs/self-evolution.md +0 -198
  82. package/docs/superpowers/plans/2026-03-19-ingest-pipeline-upgrade.md +0 -782
  83. package/docs/superpowers/plans/2026-04-11-constructive-retrieval-emotion.md +0 -1195
  84. package/docs/superpowers/specs/2026-03-19-ingest-pipeline-upgrade-design.md +0 -77
  85. package/docs/superpowers/specs/2026-03-23-pull-claude-desktop-design.md +0 -95
  86. package/docs/superpowers/specs/2026-04-11-constructive-retrieval-emotion-design.md +0 -398
  87. package/eval/continuity/results-2026-03-16.md +0 -117
  88. package/eval/continuity/results-2026-03-19.md +0 -453
  89. package/eval/continuity/runbook.md +0 -110
  90. package/eval/continuity/seed-cases.md +0 -54
  91. package/eval/reports/2026-03-06-abstract-query-baseline.md +0 -96
  92. package/eval/reports/2026-03-06-aws-ssh-baseline.md +0 -82
  93. package/eval/reports/2026-03-06-baseline.md +0 -82
  94. package/eval/reports/query-expansion.md +0 -138
  95. package/glama.json +0 -6
  96. package/lm +0 -19
  97. package/plan-philosophy-upgrades.md +0 -363
  98. package/plan-product-upgrades.md +0 -155
  99. package/plan-v1.2-distributable.md +0 -105
  100. package/plan.md +0 -140
  101. package/research-memory-evolution.md +0 -440
  102. package/research-rl-agent-memory.md +0 -330
  103. package/scripts/archive-distilled-junk.ts +0 -116
  104. package/scripts/backfill-anchors.ts +0 -130
  105. package/scripts/backfill-l0l1l2.ts +0 -98
  106. package/scripts/blind-scan.ts +0 -588
  107. package/scripts/cleanup-step1-exact-dups.ts +0 -117
  108. package/scripts/cleanup-step2-template-dups.ts +0 -113
  109. package/scripts/cleanup-step3-stale-content.ts +0 -149
  110. package/scripts/cleanup-step4-orphan-scopes.ts +0 -131
  111. package/scripts/delete-distilled-junk.ts +0 -88
  112. package/scripts/diagnose-vector-search.ts +0 -132
  113. package/scripts/distill-facts.ts +0 -305
  114. package/scripts/health-check.ts +0 -343
  115. package/scripts/incremental-ingest.sh +0 -71
  116. package/scripts/migrate-language.ts +0 -75
  117. package/scripts/mlp-health-check.ts +0 -382
  118. package/scripts/pull-claude-desktop.ts +0 -515
  119. package/scripts/re-ingest-cc.sh +0 -66
  120. package/scripts/rebalance-memory-health.ts +0 -181
  121. package/scripts/run-desktop-ingest.sh +0 -11
  122. package/scripts/start-server.sh +0 -12
  123. package/scripts/verify-emotion-decay.ts +0 -148
  124. package/scripts/viz-dashboard.html +0 -423
  125. package/scripts/viz-export.ts +0 -703
  126. package/src/__tests__/access-tracker-novelty.test.ts +0 -228
  127. package/src/__tests__/activity-counter.test.ts +0 -96
  128. package/src/__tests__/admission-control.test.ts +0 -206
  129. package/src/__tests__/anchor-generator.test.ts +0 -67
  130. package/src/__tests__/auto-consolidation.test.ts +0 -168
  131. package/src/__tests__/auto-recall.test.ts +0 -131
  132. package/src/__tests__/babel-integration.test.ts +0 -139
  133. package/src/__tests__/batch-dedup.test.ts +0 -65
  134. package/src/__tests__/capture-engine.test.ts +0 -1144
  135. package/src/__tests__/capture-heuristic.test.ts +0 -171
  136. package/src/__tests__/cascade-forget.test.ts +0 -210
  137. package/src/__tests__/category-min-scores.test.ts +0 -33
  138. package/src/__tests__/cc8-cc1-essential-context.test.ts +0 -298
  139. package/src/__tests__/circuit-breaker.test.ts +0 -99
  140. package/src/__tests__/cluster-consolidation.test.ts +0 -712
  141. package/src/__tests__/confidence-metadata.test.ts +0 -125
  142. package/src/__tests__/confidence-tracker.test.ts +0 -249
  143. package/src/__tests__/conflict-advisor.test.ts +0 -206
  144. package/src/__tests__/conflict-escalation.test.ts +0 -149
  145. package/src/__tests__/conflict-lifecycle.test.ts +0 -83
  146. package/src/__tests__/conflict-output.test.ts +0 -85
  147. package/src/__tests__/conflict-store.test.ts +0 -374
  148. package/src/__tests__/consolidation-engine.test.ts +0 -181
  149. package/src/__tests__/constructive-retrieval.test.ts +0 -509
  150. package/src/__tests__/context-collapse-renderer.test.ts +0 -214
  151. package/src/__tests__/context-composer-pins.test.ts +0 -64
  152. package/src/__tests__/context-composer-task-selection.test.ts +0 -24
  153. package/src/__tests__/context-reconstructor.test.ts +0 -65
  154. package/src/__tests__/context-renderer.test.ts +0 -91
  155. package/src/__tests__/continuity-eval.test.ts +0 -275
  156. package/src/__tests__/continuity-schema.test.ts +0 -97
  157. package/src/__tests__/conversation-importer.test.ts +0 -692
  158. package/src/__tests__/core-recall-eval.test.ts +0 -235
  159. package/src/__tests__/core-summary.test.ts +0 -109
  160. package/src/__tests__/data-checkup.test.ts +0 -225
  161. package/src/__tests__/decay-exemption.test.ts +0 -84
  162. package/src/__tests__/dedup-actions.test.ts +0 -58
  163. package/src/__tests__/dedup-guard.test.ts +0 -39
  164. package/src/__tests__/deterministic-id.test.ts +0 -39
  165. package/src/__tests__/distill-lock.test.ts +0 -176
  166. package/src/__tests__/doctor.test.ts +0 -303
  167. package/src/__tests__/dream-pipeline.test.ts +0 -232
  168. package/src/__tests__/drill-down.test.ts +0 -108
  169. package/src/__tests__/embedder.test.ts +0 -122
  170. package/src/__tests__/emotion-decay.test.ts +0 -46
  171. package/src/__tests__/emotion-detector.test.ts +0 -56
  172. package/src/__tests__/emotion-phase1-integration.test.ts +0 -153
  173. package/src/__tests__/emotion-retrieval.test.ts +0 -56
  174. package/src/__tests__/entity-resolver.test.ts +0 -58
  175. package/src/__tests__/ethics-layer.test.ts +0 -476
  176. package/src/__tests__/event-segmenter.test.ts +0 -153
  177. package/src/__tests__/feature-flags-integration.test.ts +0 -54
  178. package/src/__tests__/frequency-tracker.test.ts +0 -113
  179. package/src/__tests__/graph-export.test.ts +0 -489
  180. package/src/__tests__/hotness-blend.test.ts +0 -76
  181. package/src/__tests__/ingest.test.ts +0 -360
  182. package/src/__tests__/interference-detector.test.ts +0 -203
  183. package/src/__tests__/kg-extractor.test.ts +0 -184
  184. package/src/__tests__/kg-store.test.ts +0 -198
  185. package/src/__tests__/llm-consolidation.test.ts +0 -226
  186. package/src/__tests__/mcp-efficiency.test.ts +0 -288
  187. package/src/__tests__/memory-boundaries.test.ts +0 -237
  188. package/src/__tests__/memory-evolution-b2-b3-e1.test.ts +0 -243
  189. package/src/__tests__/memory-evolution.test.ts +0 -271
  190. package/src/__tests__/memory-health-rebalance.test.ts +0 -144
  191. package/src/__tests__/memory-lint.test.ts +0 -271
  192. package/src/__tests__/memory-output.test.ts +0 -215
  193. package/src/__tests__/multi-vector.test.ts +0 -242
  194. package/src/__tests__/narrative-architecture.test.ts +0 -456
  195. package/src/__tests__/pending-review.test.ts +0 -77
  196. package/src/__tests__/post-retrieval-filter.test.ts +0 -178
  197. package/src/__tests__/ppr-traversal.test.ts +0 -256
  198. package/src/__tests__/prediction-engine.test.ts +0 -349
  199. package/src/__tests__/predictive-prospective-memory.test.ts +0 -416
  200. package/src/__tests__/preference-guard-smoke.test.ts +0 -211
  201. package/src/__tests__/preference-matcher.test.ts +0 -230
  202. package/src/__tests__/preference-slots.test.ts +0 -205
  203. package/src/__tests__/prospective-memory.test.ts +0 -283
  204. package/src/__tests__/query-entity-detector.test.ts +0 -171
  205. package/src/__tests__/query-expander-alias.test.ts +0 -89
  206. package/src/__tests__/query-expander.test.ts +0 -34
  207. package/src/__tests__/recall-governor.test.ts +0 -227
  208. package/src/__tests__/recall-mode.test.ts +0 -64
  209. package/src/__tests__/result-synthesizer.test.ts +0 -198
  210. package/src/__tests__/resume-context.test.ts +0 -4898
  211. package/src/__tests__/retrieval-trace.test.ts +0 -213
  212. package/src/__tests__/retriever-boundary-weight.test.ts +0 -136
  213. package/src/__tests__/retroactive-boost.test.ts +0 -223
  214. package/src/__tests__/salience-scoring.test.ts +0 -106
  215. package/src/__tests__/scope-inventory.test.ts +0 -200
  216. package/src/__tests__/scope-policy.test.ts +0 -69
  217. package/src/__tests__/session-checkpoint.test.ts +0 -259
  218. package/src/__tests__/session-distiller.test.ts +0 -611
  219. package/src/__tests__/session-gc.test.ts +0 -215
  220. package/src/__tests__/skill-engine.test.ts +0 -546
  221. package/src/__tests__/skill-promotion.test.ts +0 -349
  222. package/src/__tests__/store-scope-audit.test.ts +0 -81
  223. package/src/__tests__/supersede-chain.test.ts +0 -154
  224. package/src/__tests__/temporal-parser.test.ts +0 -241
  225. package/src/__tests__/temporal-validity.test.ts +0 -160
  226. package/src/__tests__/term-registry.test.ts +0 -121
  227. package/src/__tests__/tool-output-compressor.test.ts +0 -133
  228. package/src/__tests__/topic-tag.test.ts +0 -113
  229. package/src/__tests__/trusted-memory.test.ts +0 -828
  230. package/src/__tests__/version-manager.test.ts +0 -213
  231. package/src/__tests__/workflow-observation.test.ts +0 -400
  232. package/src/__tests__/write-verifier.test.ts +0 -122
@@ -1,125 +0,0 @@
1
- /**
2
- * F1: Memory Confidence Meta-tags — tests for structured confidence,
3
- * source-based assignment, retrieval integration, and backward compat.
4
- */
5
-
6
- import { describe, expect, test } from "bun:test";
7
- import {
8
- assignDefaultConfidence,
9
- getConfidence,
10
- getConfidenceMetadata,
11
- CONFIDENCE_DEFAULT,
12
- type ConfidenceMetadata,
13
- } from "../confidence-tracker.js";
14
- import type { MemoryEntry } from "../store.js";
15
-
16
- // ---------------------------------------------------------------------------
17
- // Helper
18
- // ---------------------------------------------------------------------------
19
-
20
- function makeEntry(metadataObj: Record<string, unknown>): MemoryEntry {
21
- return {
22
- id: "test-001",
23
- text: "test",
24
- vector: [],
25
- category: "events",
26
- scope: "test",
27
- importance: 0.7,
28
- timestamp: Date.now(),
29
- metadata: JSON.stringify(metadataObj),
30
- };
31
- }
32
-
33
- // ---------------------------------------------------------------------------
34
- // assignDefaultConfidence
35
- // ---------------------------------------------------------------------------
36
-
37
- describe("F1: assignDefaultConfidence", () => {
38
- test("manual source → direct reliability, score 0.9", () => {
39
- const c = assignDefaultConfidence("manual");
40
- expect(c.score).toBe(0.9);
41
- expect(c.reliability).toBe("direct");
42
- });
43
-
44
- test("agent source → inferred reliability, score 0.7", () => {
45
- const c = assignDefaultConfidence("agent");
46
- expect(c.score).toBe(0.7);
47
- expect(c.reliability).toBe("inferred");
48
- });
49
-
50
- test("conversation_import source → hearsay, score 0.5", () => {
51
- const c = assignDefaultConfidence("conversation_import");
52
- expect(c.score).toBe(0.5);
53
- expect(c.reliability).toBe("hearsay");
54
- });
55
-
56
- test("session_distill source → inferred, score 0.6", () => {
57
- const c = assignDefaultConfidence("session_distill");
58
- expect(c.score).toBe(0.6);
59
- expect(c.reliability).toBe("inferred");
60
- });
61
-
62
- test("explicit override takes precedence", () => {
63
- const c = assignDefaultConfidence("agent", { score: 0.95, reliability: "direct" });
64
- expect(c.score).toBe(0.95);
65
- expect(c.reliability).toBe("direct");
66
- });
67
-
68
- test("explicit score only, reliability defaults to inferred", () => {
69
- const c = assignDefaultConfidence("manual", { score: 0.3 });
70
- expect(c.score).toBe(0.3);
71
- expect(c.reliability).toBe("inferred");
72
- });
73
- });
74
-
75
- // ---------------------------------------------------------------------------
76
- // getConfidence — backward compat
77
- // ---------------------------------------------------------------------------
78
-
79
- describe("F1: getConfidence backward compatibility", () => {
80
- test("reads flat number confidence (legacy format)", () => {
81
- const entry = makeEntry({ confidence: 0.42 });
82
- expect(getConfidence(entry)).toBe(0.42);
83
- });
84
-
85
- test("reads structured confidence object", () => {
86
- const entry = makeEntry({ confidence: { score: 0.88, reliability: "direct" } });
87
- expect(getConfidence(entry)).toBe(0.88);
88
- });
89
-
90
- test("returns default when confidence missing", () => {
91
- const entry = makeEntry({});
92
- expect(getConfidence(entry)).toBe(CONFIDENCE_DEFAULT);
93
- });
94
- });
95
-
96
- // ---------------------------------------------------------------------------
97
- // getConfidenceMetadata
98
- // ---------------------------------------------------------------------------
99
-
100
- describe("F1: getConfidenceMetadata", () => {
101
- test("extracts structured ConfidenceMetadata", () => {
102
- const entry = makeEntry({
103
- confidence: { score: 0.75, reliability: "hearsay", verifiedAt: 1000, verifiedBy: "user" },
104
- });
105
- const meta = getConfidenceMetadata(entry);
106
- expect(meta).not.toBeNull();
107
- expect(meta!.score).toBe(0.75);
108
- expect(meta!.reliability).toBe("hearsay");
109
- expect(meta!.verifiedAt).toBe(1000);
110
- expect(meta!.verifiedBy).toBe("user");
111
- });
112
-
113
- test("wraps legacy flat number into ConfidenceMetadata", () => {
114
- const entry = makeEntry({ confidence: 0.55 });
115
- const meta = getConfidenceMetadata(entry);
116
- expect(meta).not.toBeNull();
117
- expect(meta!.score).toBe(0.55);
118
- expect(meta!.reliability).toBe("inferred");
119
- });
120
-
121
- test("returns null when confidence missing", () => {
122
- const entry = makeEntry({});
123
- expect(getConfidenceMetadata(entry)).toBeNull();
124
- });
125
- });
@@ -1,249 +0,0 @@
1
- import { describe, expect, it } from "bun:test";
2
-
3
- import {
4
- confirmMemory,
5
- correctMemory,
6
- contradictMemory,
7
- buildConfirmPatch,
8
- buildCorrectPatch,
9
- buildContradictPatch,
10
- getConfidence,
11
- applyConfidenceWeight,
12
- CONFIDENCE_DEFAULT,
13
- CONFIDENCE_CONFIRMED,
14
- CONFIDENCE_CORRECTED,
15
- CONFIDENCE_CONTRADICTED,
16
- } from "../confidence-tracker.js";
17
- import type { MemoryEntry } from "../store.js";
18
-
19
- // ---------------------------------------------------------------------------
20
- // Helpers
21
- // ---------------------------------------------------------------------------
22
-
23
- function makeEntry(id: string, text: string, overrides: Partial<MemoryEntry> = {}): MemoryEntry {
24
- return {
25
- id,
26
- text,
27
- vector: [1, 0, 0],
28
- category: "events",
29
- scope: "project:test",
30
- importance: 0.7,
31
- timestamp: Date.now(),
32
- metadata: "{}",
33
- ...overrides,
34
- };
35
- }
36
-
37
- function createMockStore(entries: MemoryEntry[]) {
38
- const data = new Map(entries.map(e => [e.id, { ...e }]));
39
- const updates: Array<{ id: string; metadata?: string }> = [];
40
-
41
- return {
42
- updates,
43
- data,
44
- async getById(id: string) {
45
- return data.get(id) ?? null;
46
- },
47
- async update(id: string, upd: any, _scopeFilter?: string[]) {
48
- const entry = data.get(id);
49
- if (!entry) return null;
50
- if (upd.metadata !== undefined) entry.metadata = upd.metadata;
51
- updates.push({ id, ...upd });
52
- return entry;
53
- },
54
- } as any;
55
- }
56
-
57
- // ---------------------------------------------------------------------------
58
- // Tests
59
- // ---------------------------------------------------------------------------
60
-
61
- describe("confidence-tracker", () => {
62
- describe("getConfidence", () => {
63
- it("returns default for entries without confidence", () => {
64
- const entry = makeEntry("e1", "test");
65
- expect(getConfidence(entry)).toBe(CONFIDENCE_DEFAULT);
66
- });
67
-
68
- it("returns stored confidence", () => {
69
- const entry = makeEntry("e1", "test", {
70
- metadata: JSON.stringify({ confidence: 0.9 }),
71
- });
72
- expect(getConfidence(entry)).toBe(0.9);
73
- });
74
-
75
- it("returns default for malformed metadata", () => {
76
- const entry = makeEntry("e1", "test", { metadata: "not-json" });
77
- expect(getConfidence(entry)).toBe(CONFIDENCE_DEFAULT);
78
- });
79
- });
80
-
81
- describe("confirmMemory", () => {
82
- it("sets confidence to 1.0", async () => {
83
- const entry = makeEntry("e1", "user likes TypeScript");
84
- const store = createMockStore([entry]);
85
-
86
- const result = await confirmMemory(store, "e1", "project:test");
87
- expect(result).not.toBeNull();
88
- expect(result!.newConfidence).toBe(CONFIDENCE_CONFIRMED);
89
- expect(result!.oldConfidence).toBe(CONFIDENCE_DEFAULT);
90
-
91
- const meta = JSON.parse(store.data.get("e1")!.metadata!);
92
- expect(meta.confidence).toBe(1.0);
93
- expect(meta.confidence_history).toHaveLength(1);
94
- expect(meta.confidence_history[0].action).toBe("confirmed");
95
- });
96
-
97
- it("returns null for missing entry", async () => {
98
- const store = createMockStore([]);
99
- const result = await confirmMemory(store, "missing", "project:test");
100
- expect(result).toBeNull();
101
- });
102
- });
103
-
104
- describe("correctMemory", () => {
105
- it("drops confidence to 0.3", async () => {
106
- const entry = makeEntry("e1", "user uses Python");
107
- const store = createMockStore([entry]);
108
-
109
- const result = await correctMemory(store, "e1", "project:test", "new-entry-id");
110
- expect(result).not.toBeNull();
111
- expect(result!.newConfidence).toBe(CONFIDENCE_CORRECTED);
112
-
113
- const meta = JSON.parse(store.data.get("e1")!.metadata!);
114
- expect(meta.confidence).toBe(0.3);
115
- expect(meta.corrected_by).toBe("new-entry-id");
116
- expect(meta.confidence_history[0].correctedBy).toBe("new-entr"); // 8 chars
117
- });
118
-
119
- it("works without correctedById", async () => {
120
- const entry = makeEntry("e1", "old info");
121
- const store = createMockStore([entry]);
122
-
123
- const result = await correctMemory(store, "e1", "project:test");
124
- expect(result!.newConfidence).toBe(CONFIDENCE_CORRECTED);
125
-
126
- const meta = JSON.parse(store.data.get("e1")!.metadata!);
127
- expect(meta.corrected_by).toBeUndefined();
128
- });
129
- });
130
-
131
- describe("contradictMemory", () => {
132
- it("drops confidence to 0.0", async () => {
133
- const entry = makeEntry("e1", "wrong fact");
134
- const store = createMockStore([entry]);
135
-
136
- const result = await contradictMemory(store, "e1", "project:test");
137
- expect(result).not.toBeNull();
138
- expect(result!.newConfidence).toBe(CONFIDENCE_CONTRADICTED);
139
-
140
- const meta = JSON.parse(store.data.get("e1")!.metadata!);
141
- expect(meta.confidence).toBe(0.0);
142
- });
143
- });
144
-
145
- describe("applyConfidenceWeight", () => {
146
- it("no penalty at confidence=1.0", () => {
147
- const entry = makeEntry("e1", "test", {
148
- metadata: JSON.stringify({ confidence: 1.0 }),
149
- });
150
- expect(applyConfidenceWeight(1.0, entry)).toBe(1.0);
151
- });
152
-
153
- it("slight penalty at default confidence=0.7", () => {
154
- const entry = makeEntry("e1", "test");
155
- const weighted = applyConfidenceWeight(1.0, entry);
156
- expect(weighted).toBeCloseTo(0.85, 2);
157
- });
158
-
159
- it("significant penalty at corrected confidence=0.3", () => {
160
- const entry = makeEntry("e1", "test", {
161
- metadata: JSON.stringify({ confidence: 0.3 }),
162
- });
163
- const weighted = applyConfidenceWeight(1.0, entry);
164
- expect(weighted).toBeCloseTo(0.65, 2);
165
- });
166
-
167
- it("heavy penalty at contradicted confidence=0.0", () => {
168
- const entry = makeEntry("e1", "test", {
169
- metadata: JSON.stringify({ confidence: 0.0 }),
170
- });
171
- const weighted = applyConfidenceWeight(1.0, entry);
172
- expect(weighted).toBe(0.5);
173
- });
174
-
175
- it("scales with input score", () => {
176
- const entry = makeEntry("e1", "test", {
177
- metadata: JSON.stringify({ confidence: 0.3 }),
178
- });
179
- const weighted = applyConfidenceWeight(0.8, entry);
180
- expect(weighted).toBeCloseTo(0.8 * 0.65, 2);
181
- });
182
- });
183
-
184
- describe("patch builders (store-agnostic)", () => {
185
- it("buildConfirmPatch returns metadata patch without touching store", () => {
186
- const entry = makeEntry("e1", "test fact");
187
- const patch = buildConfirmPatch(entry);
188
- expect(patch).not.toBeNull();
189
- expect(patch!.metadata.confidence).toBe(CONFIDENCE_CONFIRMED);
190
- expect(patch!.update.entryId).toBe("e1");
191
- expect(patch!.update.newConfidence).toBe(CONFIDENCE_CONFIRMED);
192
- // Original entry metadata should NOT be mutated
193
- expect(JSON.parse(entry.metadata!).confidence).toBeUndefined();
194
- });
195
-
196
- it("buildCorrectPatch includes correctedBy in history", () => {
197
- const entry = makeEntry("e1", "old info");
198
- const patch = buildCorrectPatch(entry, "new-id-123");
199
- expect(patch).not.toBeNull();
200
- expect(patch!.metadata.confidence).toBe(CONFIDENCE_CORRECTED);
201
- expect(patch!.metadata.corrected_by).toBe("new-id-123");
202
- const history = patch!.metadata.confidence_history as Array<{ correctedBy?: string }>;
203
- expect(history[0].correctedBy).toBe("new-id-1"); // 8 chars
204
- });
205
-
206
- it("buildContradictPatch returns zero confidence", () => {
207
- const entry = makeEntry("e1", "wrong");
208
- const patch = buildContradictPatch(entry);
209
- expect(patch!.metadata.confidence).toBe(CONFIDENCE_CONTRADICTED);
210
- });
211
-
212
- it("all patch builders return null for null entry", () => {
213
- expect(buildConfirmPatch(null)).toBeNull();
214
- expect(buildCorrectPatch(null)).toBeNull();
215
- expect(buildContradictPatch(null)).toBeNull();
216
- });
217
-
218
- it("caps confidence history at 20 entries", () => {
219
- const longHistory = Array.from({ length: 25 }, (_, i) => ({
220
- action: "confirmed",
221
- from: 0.5,
222
- to: 1.0,
223
- date: `2026-01-${String(i + 1).padStart(2, "0")}`,
224
- }));
225
- const entry = makeEntry("e1", "test", {
226
- metadata: JSON.stringify({ confidence: 0.5, confidence_history: longHistory }),
227
- });
228
- const patch = buildConfirmPatch(entry);
229
- const history = patch!.metadata.confidence_history as unknown[];
230
- expect(history.length).toBeLessThanOrEqual(20);
231
- });
232
- });
233
-
234
- describe("confidence history", () => {
235
- it("accumulates multiple changes", async () => {
236
- const entry = makeEntry("e1", "evolving fact");
237
- const store = createMockStore([entry]);
238
-
239
- await correctMemory(store, "e1", "project:test");
240
- await confirmMemory(store, "e1", "project:test");
241
-
242
- const meta = JSON.parse(store.data.get("e1")!.metadata!);
243
- expect(meta.confidence_history).toHaveLength(2);
244
- expect(meta.confidence_history[0].action).toBe("corrected");
245
- expect(meta.confidence_history[1].action).toBe("confirmed");
246
- expect(meta.confidence).toBe(CONFIDENCE_CONFIRMED);
247
- });
248
- });
249
- });
@@ -1,206 +0,0 @@
1
- import { describe, expect, it } from "bun:test";
2
-
3
- import { buildConflictCandidateRecord } from "../conflict-engine.js";
4
- import { buildConflictAuditSummary, clusterConflicts, summarizeConflictAdvice } from "../conflict-advisor.js";
5
-
6
- /** Returns an ISO timestamp N days before now (defaults to 4 days = firmly "stale"). */
7
- function daysAgo(n: number): string {
8
- return new Date(Date.now() - n * 24 * 60 * 60 * 1000).toISOString();
9
- }
10
-
11
- function createConflictRecord(overrides: Record<string, unknown> = {}) {
12
- const defaultTs = daysAgo(4); // 4 days ago → "stale" (3-6 days range)
13
- return {
14
- ...buildConflictCandidateRecord({
15
- canonicalKey: "user-reply-style",
16
- category: "preferences",
17
- fingerprint: "user-reply-style--durable-1--source-1--new-text",
18
- reason: "promotion_conflicts_with_existing_durable",
19
- existing: {
20
- memoryId: "durable-1",
21
- text: "User prefers concise, direct, technical replies.",
22
- category: "preferences",
23
- scope: "memory:agent",
24
- importance: 0.84,
25
- metadata: "{\"source\":\"agent\"}",
26
- canonicalKey: "user-reply-style",
27
- },
28
- incoming: {
29
- text: "User prefers concise direct technical replies.",
30
- category: "preferences",
31
- scope: "memory:agent",
32
- importance: 0.78,
33
- metadata: "{\"source\":\"agent\"}",
34
- source: "agent",
35
- sourceMemoryId: "source-1",
36
- sourceCategory: "events",
37
- },
38
- createdAt: defaultTs,
39
- updatedAt: defaultTs,
40
- }),
41
- ...overrides,
42
- };
43
- }
44
-
45
- describe("summarizeConflictAdvice", () => {
46
- it("suggests keep_existing for text-equivalent conflicts", () => {
47
- const advice = summarizeConflictAdvice(createConflictRecord() as any);
48
-
49
- expect(advice.suggestedResolution).toBe("keep_existing");
50
- expect(advice.confidence).toBe("high");
51
- expect(advice.similarity).toBe(1);
52
- });
53
-
54
- it("suggests keep_existing for cross-category canonical key conflicts", () => {
55
- const advice = summarizeConflictAdvice(createConflictRecord({
56
- reason: "canonical_key_conflicts_with_existing_durable",
57
- category: "events",
58
- incoming: {
59
- text: "Reply-style observation imported as an event.",
60
- category: "events",
61
- scope: "memory:agent",
62
- importance: 0.78,
63
- metadata: "{\"source\":\"agent\"}",
64
- source: "agent",
65
- sourceMemoryId: "source-2",
66
- sourceCategory: "events",
67
- },
68
- }) as any);
69
-
70
- expect(advice.suggestedResolution).toBe("keep_existing");
71
- expect(["medium", "high"]).toContain(advice.confidence);
72
- expect(advice.reasons.some((reason) => reason.includes("canonical key"))).toBe(true);
73
- });
74
-
75
- it("suggests accept_incoming when the incoming text is a tighter rewrite of the same memory", () => {
76
- const advice = summarizeConflictAdvice(createConflictRecord({
77
- existing: {
78
- memoryId: "durable-1",
79
- text: "User prefers concise grounded technical replies and avoids sales language in the final copy.",
80
- category: "preferences",
81
- scope: "memory:agent",
82
- importance: 0.84,
83
- metadata: "{\"source\":\"agent\"}",
84
- canonicalKey: "user-reply-style",
85
- },
86
- incoming: {
87
- text: "User prefers concise grounded technical replies and avoids sales language.",
88
- category: "preferences",
89
- scope: "memory:agent",
90
- importance: 0.78,
91
- metadata: "{\"source\":\"agent\"}",
92
- source: "agent",
93
- sourceMemoryId: "source-1",
94
- sourceCategory: "events",
95
- },
96
- }) as any);
97
-
98
- expect(advice.suggestedResolution).toBe("accept_incoming");
99
- expect(advice.confidence).toBe("medium");
100
- expect(advice.similarity).toBeGreaterThanOrEqual(0.72);
101
- expect(advice.mergeSuggestion).toBeUndefined();
102
- });
103
-
104
- it("offers a merged wording suggestion for same-category manual_review conflicts", () => {
105
- const advice = summarizeConflictAdvice(createConflictRecord({
106
- existing: {
107
- memoryId: "durable-1",
108
- text: "User prefers concise grounded technical replies; avoid sales language.",
109
- category: "preferences",
110
- scope: "memory:agent",
111
- importance: 0.84,
112
- metadata: "{\"source\":\"agent\"}",
113
- canonicalKey: "user-reply-style",
114
- },
115
- incoming: {
116
- text: "User prefers concise grounded technical replies; keep the tone colloquial.",
117
- category: "preferences",
118
- scope: "memory:agent",
119
- importance: 0.78,
120
- metadata: "{\"source\":\"agent\"}",
121
- source: "agent",
122
- sourceMemoryId: "source-1",
123
- sourceCategory: "events",
124
- },
125
- }) as any);
126
-
127
- expect(advice.suggestedResolution).toBe("manual_review");
128
- expect(advice.mergeSuggestion).toContain("User prefers concise grounded technical replies");
129
- expect(advice.mergeSuggestion).toContain("avoid sales language");
130
- expect(advice.mergeSuggestion).toContain("keep the tone colloquial");
131
- expect(advice.reasons.some((reason) => reason.includes("merged durable wording"))).toBe(true);
132
- });
133
- });
134
-
135
- describe("clusterConflicts", () => {
136
- it("groups conflicts by canonical key, reason, and category", () => {
137
- const clusters = clusterConflicts([
138
- createConflictRecord({
139
- conflictId: "00000000-0000-0000-0000-000000000001",
140
- createdAt: daysAgo(4),
141
- updatedAt: daysAgo(4),
142
- }) as any,
143
- createConflictRecord({
144
- conflictId: "00000000-0000-0000-0000-000000000002",
145
- createdAt: daysAgo(4),
146
- updatedAt: daysAgo(3),
147
- }) as any,
148
- createConflictRecord({
149
- canonicalKey: "project-entity-owner",
150
- conflictId: "00000000-0000-0000-0000-000000000003",
151
- fingerprint: "project-entity-owner--durable-1--source-1--new-text",
152
- }) as any,
153
- ]);
154
-
155
- expect(clusters).toHaveLength(2);
156
- expect(clusters[0]?.canonicalKey).toBe("user-reply-style");
157
- expect(clusters[0]?.totalCount).toBe(2);
158
- expect(clusters[0]?.latestConflictId).toBe("00000000-0000-0000-0000-000000000002");
159
- expect(clusters[0]?.attention).toBe("stale");
160
- });
161
- });
162
-
163
- describe("buildConflictAuditSummary", () => {
164
- it("highlights escalated or stale open clusters first", () => {
165
- const summary = buildConflictAuditSummary([
166
- createConflictRecord({
167
- conflictId: "00000000-0000-0000-0000-000000000001",
168
- createdAt: daysAgo(14),
169
- updatedAt: daysAgo(14),
170
- }) as any,
171
- createConflictRecord({
172
- canonicalKey: "project-entity-owner",
173
- conflictId: "00000000-0000-0000-0000-000000000002",
174
- fingerprint: "project-entity-owner--durable-1--source-1--new-text",
175
- createdAt: daysAgo(1),
176
- updatedAt: daysAgo(1),
177
- }) as any,
178
- createConflictRecord({
179
- conflictId: "00000000-0000-0000-0000-000000000003",
180
- status: "kept-existing",
181
- resolvedAt: daysAgo(0),
182
- updatedAt: daysAgo(0),
183
- }) as any,
184
- ], 3);
185
-
186
- expect(summary.totalConflicts).toBe(3);
187
- expect(summary.openConflicts).toBe(2);
188
- expect(summary.attentionCounts.resolved).toBe(1);
189
- expect(summary.priorityClusters[0]?.attention).toBe("escalated");
190
- expect(summary.suggestedActions.some((action) => action.includes("escalated"))).toBe(true);
191
- });
192
-
193
- it("returns a no-open-conflicts message when nothing is pending", () => {
194
- const summary = buildConflictAuditSummary([
195
- createConflictRecord({
196
- conflictId: "00000000-0000-0000-0000-000000000001",
197
- status: "kept-existing",
198
- resolvedAt: "2026-03-16T00:00:00.000Z",
199
- }) as any,
200
- ], 3);
201
-
202
- expect(summary.openConflicts).toBe(0);
203
- expect(summary.priorityClusters).toHaveLength(0);
204
- expect(summary.suggestedActions).toEqual(["No open conflicts need review."]);
205
- });
206
- });