@remnic/core 9.51.1 → 9.52.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.
Files changed (234) hide show
  1. package/dist/access-admin-ops-surface.d.ts +6 -5
  2. package/dist/access-admin-ops-surface.js +16 -16
  3. package/dist/access-authorization-probe.d.ts +6 -5
  4. package/dist/access-authorization-probe.js +17 -17
  5. package/dist/access-boundary.d.ts +6 -5
  6. package/dist/access-boundary.js +16 -16
  7. package/dist/access-cli.js +39 -39
  8. package/dist/access-extraction-force-flush.d.ts +6 -5
  9. package/dist/access-extraction-force-flush.js +16 -16
  10. package/dist/access-http-lcm-compaction.d.ts +6 -5
  11. package/dist/access-http-lifecycle-flush.d.ts +6 -5
  12. package/dist/access-http-offline-stream.d.ts +6 -5
  13. package/dist/access-http-query.js +16 -16
  14. package/dist/access-http.d.ts +6 -5
  15. package/dist/access-http.js +22 -22
  16. package/dist/access-identity-continuity-surface.d.ts +5 -4
  17. package/dist/access-identity-continuity-surface.js +16 -16
  18. package/dist/access-lcm-surface.d.ts +6 -5
  19. package/dist/access-lcm-surface.js +16 -16
  20. package/dist/access-mcp.d.ts +6 -5
  21. package/dist/access-mcp.js +20 -20
  22. package/dist/access-namespace-preflight.js +16 -16
  23. package/dist/access-observe-write-surface.d.ts +6 -5
  24. package/dist/access-observe-write-surface.js +16 -16
  25. package/dist/access-offline-manifest.d.ts +5 -4
  26. package/dist/access-operations-batch.js +17 -17
  27. package/dist/access-operations.d.ts +9 -8
  28. package/dist/access-operations.js +19 -19
  29. package/dist/access-recall-concurrency.d.ts +6 -5
  30. package/dist/access-recall-concurrency.js +16 -16
  31. package/dist/access-recall-response.d.ts +6 -5
  32. package/dist/access-recall-response.js +16 -16
  33. package/dist/access-recall-surface.d.ts +6 -5
  34. package/dist/access-recall-surface.js +16 -16
  35. package/dist/access-schema.d.ts +88 -88
  36. package/dist/{access-service-BY0KJ44c.d.ts → access-service-Cbk3bNbD.d.ts} +3 -3
  37. package/dist/access-service-helpers.d.ts +6 -5
  38. package/dist/access-service.d.ts +6 -5
  39. package/dist/access-service.js +16 -16
  40. package/dist/access-surface-catalog.d.ts +6 -5
  41. package/dist/access-wearables-meetings-surface.d.ts +2 -2
  42. package/dist/bootstrap.d.ts +5 -4
  43. package/dist/briefing.d.ts +1 -1
  44. package/dist/briefing.js +7 -7
  45. package/dist/buffer.d.ts +1 -1
  46. package/dist/{capsule-merge-XMFBXVDP.js → capsule-merge-HCJJMLPS.js} +4 -4
  47. package/dist/causal-consolidation.js +8 -8
  48. package/dist/{chunk-PWN32TAG.js → chunk-2ASAS3CQ.js} +2 -2
  49. package/dist/{chunk-WQM326GS.js → chunk-2LY6GIIE.js} +2 -2
  50. package/dist/{chunk-JL43BVDE.js → chunk-2OIQDTDC.js} +2 -2
  51. package/dist/{chunk-33SV3E43.js → chunk-3OY7KXHM.js} +3 -3
  52. package/dist/{chunk-AIUBBHSF.js → chunk-5UZ6UCOZ.js} +3 -3
  53. package/dist/chunk-5WITETWU.js +591 -0
  54. package/dist/chunk-5WITETWU.js.map +1 -0
  55. package/dist/{chunk-UT5BV5OG.js → chunk-7HXZJAID.js} +2 -2
  56. package/dist/{chunk-M7BXDKPR.js → chunk-7M6NYQWB.js} +3 -3
  57. package/dist/{chunk-TPTECFWR.js → chunk-B6LZ4IBH.js} +3 -3
  58. package/dist/{chunk-J5LLX6IL.js → chunk-BF6SXNQJ.js} +10 -10
  59. package/dist/{chunk-4PC4PRJE.js → chunk-BKIFTXHR.js} +2 -2
  60. package/dist/{chunk-VVG2BOKA.js → chunk-BMEYMKWX.js} +2 -2
  61. package/dist/{chunk-DCZBSE4F.js → chunk-D5YOZ66N.js} +18 -18
  62. package/dist/{chunk-BHXYAVUM.js → chunk-DLG5V6ND.js} +2 -2
  63. package/dist/{chunk-52UKFTUK.js → chunk-F4EU6VUC.js} +2 -2
  64. package/dist/{chunk-M4A44LUA.js → chunk-GSTIBXBW.js} +2 -2
  65. package/dist/{chunk-W5XVQFLM.js → chunk-GXFSQ6SD.js} +2 -2
  66. package/dist/{chunk-XADV5IZ3.js → chunk-HDIUYMSB.js} +5 -5
  67. package/dist/{chunk-FXTYIA46.js → chunk-J272WKX2.js} +2 -2
  68. package/dist/{chunk-ENTCVTQR.js → chunk-JJRVPWFY.js} +11 -1
  69. package/dist/chunk-JJRVPWFY.js.map +1 -0
  70. package/dist/{chunk-JYL3BVVZ.js → chunk-JSEJH2YQ.js} +2 -2
  71. package/dist/{chunk-5UDMFE3Q.js → chunk-MLXZD4WM.js} +2 -2
  72. package/dist/{chunk-PUSLMZBQ.js → chunk-NOEMGE56.js} +2 -2
  73. package/dist/{chunk-C5QLI33R.js → chunk-QINJITDS.js} +2 -2
  74. package/dist/{chunk-45BRYON6.js → chunk-QPOHMO6M.js} +2 -2
  75. package/dist/{chunk-ZQOCRIGA.js → chunk-QYN5PXDZ.js} +3 -3
  76. package/dist/{chunk-PA45XCUD.js → chunk-RANKJI5T.js} +2 -2
  77. package/dist/{chunk-ZHY5TMJG.js → chunk-RMHMUUBW.js} +2392 -628
  78. package/dist/chunk-RMHMUUBW.js.map +1 -0
  79. package/dist/{chunk-37ZO6LLJ.js → chunk-RYLEDKQ2.js} +2 -2
  80. package/dist/{chunk-CV6BBVYV.js → chunk-TEMSWHQD.js} +2 -2
  81. package/dist/{chunk-QGR6OVGT.js → chunk-VFSAWRMZ.js} +2 -2
  82. package/dist/{chunk-NLK2VZYZ.js → chunk-VXCNQYTC.js} +2 -2
  83. package/dist/{chunk-AZ2QBGYB.js → chunk-W7Y5JDVB.js} +1 -1
  84. package/dist/{chunk-VBHPOWBJ.js → chunk-WD5B5AIE.js} +1051 -591
  85. package/dist/chunk-WD5B5AIE.js.map +1 -0
  86. package/dist/{chunk-OYESWJ43.js → chunk-WLRD2TXF.js} +15 -10
  87. package/dist/chunk-WLRD2TXF.js.map +1 -0
  88. package/dist/{chunk-4F6X3RXW.js → chunk-X57M2Q4I.js} +2 -2
  89. package/dist/{chunk-6MTSFX7A.js → chunk-X7XEDOSL.js} +8 -8
  90. package/dist/{chunk-UEVLYVNW.js → chunk-YXKVLYXU.js} +2 -2
  91. package/dist/{chunk-OQAL6SIR.js → chunk-ZMSZVISA.js} +2 -2
  92. package/dist/{cli-CnOkH9hA.d.ts → cli-DMAFI0qt.d.ts} +3 -3
  93. package/dist/cli.d.ts +7 -6
  94. package/dist/cli.js +33 -33
  95. package/dist/compounding/engine.d.ts +1 -1
  96. package/dist/compounding/engine.js +7 -7
  97. package/dist/connectors/codex-materialize-runner.js +7 -7
  98. package/dist/connectors/index.js +7 -7
  99. package/dist/consolidation-provenance-check.d.ts +1 -1
  100. package/dist/consolidation-provenance-check.js +4 -4
  101. package/dist/consolidation-undo.d.ts +1 -1
  102. package/dist/consolidation-undo.js +3 -3
  103. package/dist/contradiction/index.d.ts +1 -1
  104. package/dist/convergence-refresh.d.ts +1 -1
  105. package/dist/convergence-refresh.js +9 -9
  106. package/dist/corpus-watermark.js +9 -9
  107. package/dist/dependency-propagation-delivery-D63r24pH.d.ts +41 -0
  108. package/dist/entity-retrieval.d.ts +1 -1
  109. package/dist/entity-retrieval.js +7 -7
  110. package/dist/explicit-capture.d.ts +5 -4
  111. package/dist/explicit-capture.js +4 -4
  112. package/dist/external-wiki-access.d.ts +6 -5
  113. package/dist/external-wiki-access.js +17 -17
  114. package/dist/external-wiki-mcp-tools.d.ts +6 -5
  115. package/dist/extraction.js +7 -7
  116. package/dist/{forget-UJDJHG6R.js → forget-HDS4N6CG.js} +3 -3
  117. package/dist/harmonic-retrieval.js +1 -1
  118. package/dist/index.d.ts +687 -686
  119. package/dist/index.js +39 -39
  120. package/dist/maintenance/memory-governance.js +7 -7
  121. package/dist/maintenance/rebuild-memory-lifecycle-ledger.d.ts +1 -1
  122. package/dist/maintenance/rebuild-memory-lifecycle-ledger.js +7 -7
  123. package/dist/maintenance/rebuild-memory-projection.d.ts +1 -1
  124. package/dist/maintenance/rebuild-memory-projection.js +8 -8
  125. package/dist/{maintenance-CKwxpdRx.d.ts → maintenance-kBII2qUI.d.ts} +3 -2
  126. package/dist/mcp-memory-inspector-app.d.ts +6 -5
  127. package/dist/memory-worth-outcomes.d.ts +1 -1
  128. package/dist/namespaces/migrate.d.ts +1 -1
  129. package/dist/namespaces/migrate.js +8 -8
  130. package/dist/namespaces/storage.d.ts +1 -1
  131. package/dist/namespaces/storage.js +7 -7
  132. package/dist/operator-doctor-corpus.js +10 -10
  133. package/dist/operator-toolkit.d.ts +1 -1
  134. package/dist/operator-toolkit.js +15 -15
  135. package/dist/orchestration/compression-guideline-coordinator.d.ts +1 -1
  136. package/dist/orchestration/maintenance.d.ts +2 -2
  137. package/dist/orchestration/maintenance.js +12 -12
  138. package/dist/{orchestrator-BshGam-o.d.ts → orchestrator-DHy5LtTy.d.ts} +20 -35
  139. package/dist/orchestrator.d.ts +5 -4
  140. package/dist/orchestrator.js +39 -39
  141. package/dist/page-versioning.js +3 -3
  142. package/dist/schemas.d.ts +138 -138
  143. package/dist/semantic-consolidation.js +8 -8
  144. package/dist/semantic-rule-promotion.js +7 -7
  145. package/dist/semantic-rule-verifier.js +7 -7
  146. package/dist/{service-BIgVM7fL.d.ts → service-PT2_z7xm.d.ts} +1 -1
  147. package/dist/shared-context/manager.d.ts +8 -8
  148. package/dist/source-agent-qualifier.js +7 -7
  149. package/dist/{storage-CVfGWqCI.d.ts → storage-BmVudhk4.d.ts} +46 -27
  150. package/dist/storage.d.ts +1 -1
  151. package/dist/storage.js +6 -6
  152. package/dist/temporal-supersession.d.ts +51 -26
  153. package/dist/temporal-supersession.js +3 -1
  154. package/dist/tier-migration.d.ts +1 -1
  155. package/dist/transfer/capsule-import.js +4 -4
  156. package/dist/transfer/types.d.ts +66 -66
  157. package/dist/verified-recall.js +7 -7
  158. package/package.json +2 -2
  159. package/src/lifecycle/tombstones.ts +30 -0
  160. package/src/orchestration/consolidation-run.ts +324 -106
  161. package/src/orchestration/contradiction-linking-coordinator.ts +237 -64
  162. package/src/orchestration/dependency-propagation-delivery.test.ts +2353 -0
  163. package/src/orchestration/dependency-propagation-delivery.ts +1143 -0
  164. package/src/orchestration/dependency-propagation-queue-state.ts +220 -0
  165. package/src/orchestration/dependency-propagation-recovery-replay.ts +237 -0
  166. package/src/orchestration/dependency-propagation-replay-reconciliation.ts +73 -0
  167. package/src/orchestration/dependency-propagation-triggers.test.ts +1069 -11
  168. package/src/orchestration/dependency-propagation.test.ts +82 -31
  169. package/src/orchestration/dependency-propagation.ts +55 -17
  170. package/src/orchestration/extraction-persist-deps.ts +2 -2
  171. package/src/orchestration/extraction-persist.ts +14 -15
  172. package/src/orchestration/extraction-queue-coordinator.ts +23 -1
  173. package/src/orchestration/extraction-shutdown.ts +29 -0
  174. package/src/orchestration/maintenance.ts +13 -7
  175. package/src/orchestration/turn-ingestion.ts +5 -1
  176. package/src/orchestrator-background-writes.test.ts +265 -1
  177. package/src/orchestrator-extraction-queue.test.ts +39 -0
  178. package/src/orchestrator.ts +59 -58
  179. package/src/storage/committed-invalidation.ts +34 -0
  180. package/src/storage/deletion-revision-store.ts +529 -0
  181. package/src/storage/supersession-audit.ts +41 -0
  182. package/src/storage/supersession-side-effects.ts +52 -25
  183. package/src/storage-contract/failure-semantics.test.ts +12 -0
  184. package/src/storage-contract/round-trip.test.ts +184 -2
  185. package/src/storage-deletion-revisions.test.ts +213 -1
  186. package/src/storage-frontmatter-cas.test.ts +141 -0
  187. package/src/storage-memory-lifecycle-ledger.test.ts +372 -0
  188. package/src/storage-replay.test.ts +149 -0
  189. package/src/storage.ts +345 -358
  190. package/src/temporal-supersession.test.ts +431 -2
  191. package/src/temporal-supersession.ts +567 -107
  192. package/src/testing/subjects/dependency-propagation.test.ts +179 -38
  193. package/dist/chunk-ENTCVTQR.js.map +0 -1
  194. package/dist/chunk-EWMTOVMY.js +0 -484
  195. package/dist/chunk-EWMTOVMY.js.map +0 -1
  196. package/dist/chunk-OYESWJ43.js.map +0 -1
  197. package/dist/chunk-VBHPOWBJ.js.map +0 -1
  198. package/dist/chunk-ZHY5TMJG.js.map +0 -1
  199. /package/dist/{capsule-merge-XMFBXVDP.js.map → capsule-merge-HCJJMLPS.js.map} +0 -0
  200. /package/dist/{chunk-PWN32TAG.js.map → chunk-2ASAS3CQ.js.map} +0 -0
  201. /package/dist/{chunk-WQM326GS.js.map → chunk-2LY6GIIE.js.map} +0 -0
  202. /package/dist/{chunk-JL43BVDE.js.map → chunk-2OIQDTDC.js.map} +0 -0
  203. /package/dist/{chunk-33SV3E43.js.map → chunk-3OY7KXHM.js.map} +0 -0
  204. /package/dist/{chunk-AIUBBHSF.js.map → chunk-5UZ6UCOZ.js.map} +0 -0
  205. /package/dist/{chunk-UT5BV5OG.js.map → chunk-7HXZJAID.js.map} +0 -0
  206. /package/dist/{chunk-M7BXDKPR.js.map → chunk-7M6NYQWB.js.map} +0 -0
  207. /package/dist/{chunk-TPTECFWR.js.map → chunk-B6LZ4IBH.js.map} +0 -0
  208. /package/dist/{chunk-J5LLX6IL.js.map → chunk-BF6SXNQJ.js.map} +0 -0
  209. /package/dist/{chunk-4PC4PRJE.js.map → chunk-BKIFTXHR.js.map} +0 -0
  210. /package/dist/{chunk-VVG2BOKA.js.map → chunk-BMEYMKWX.js.map} +0 -0
  211. /package/dist/{chunk-DCZBSE4F.js.map → chunk-D5YOZ66N.js.map} +0 -0
  212. /package/dist/{chunk-BHXYAVUM.js.map → chunk-DLG5V6ND.js.map} +0 -0
  213. /package/dist/{chunk-52UKFTUK.js.map → chunk-F4EU6VUC.js.map} +0 -0
  214. /package/dist/{chunk-M4A44LUA.js.map → chunk-GSTIBXBW.js.map} +0 -0
  215. /package/dist/{chunk-W5XVQFLM.js.map → chunk-GXFSQ6SD.js.map} +0 -0
  216. /package/dist/{chunk-XADV5IZ3.js.map → chunk-HDIUYMSB.js.map} +0 -0
  217. /package/dist/{chunk-FXTYIA46.js.map → chunk-J272WKX2.js.map} +0 -0
  218. /package/dist/{chunk-JYL3BVVZ.js.map → chunk-JSEJH2YQ.js.map} +0 -0
  219. /package/dist/{chunk-5UDMFE3Q.js.map → chunk-MLXZD4WM.js.map} +0 -0
  220. /package/dist/{chunk-PUSLMZBQ.js.map → chunk-NOEMGE56.js.map} +0 -0
  221. /package/dist/{chunk-C5QLI33R.js.map → chunk-QINJITDS.js.map} +0 -0
  222. /package/dist/{chunk-45BRYON6.js.map → chunk-QPOHMO6M.js.map} +0 -0
  223. /package/dist/{chunk-ZQOCRIGA.js.map → chunk-QYN5PXDZ.js.map} +0 -0
  224. /package/dist/{chunk-PA45XCUD.js.map → chunk-RANKJI5T.js.map} +0 -0
  225. /package/dist/{chunk-37ZO6LLJ.js.map → chunk-RYLEDKQ2.js.map} +0 -0
  226. /package/dist/{chunk-CV6BBVYV.js.map → chunk-TEMSWHQD.js.map} +0 -0
  227. /package/dist/{chunk-QGR6OVGT.js.map → chunk-VFSAWRMZ.js.map} +0 -0
  228. /package/dist/{chunk-NLK2VZYZ.js.map → chunk-VXCNQYTC.js.map} +0 -0
  229. /package/dist/{chunk-AZ2QBGYB.js.map → chunk-W7Y5JDVB.js.map} +0 -0
  230. /package/dist/{chunk-4F6X3RXW.js.map → chunk-X57M2Q4I.js.map} +0 -0
  231. /package/dist/{chunk-6MTSFX7A.js.map → chunk-X7XEDOSL.js.map} +0 -0
  232. /package/dist/{chunk-UEVLYVNW.js.map → chunk-YXKVLYXU.js.map} +0 -0
  233. /package/dist/{chunk-OQAL6SIR.js.map → chunk-ZMSZVISA.js.map} +0 -0
  234. /package/dist/{forget-UJDJHG6R.js.map → forget-HDS4N6CG.js.map} +0 -0
@@ -20,8 +20,7 @@
20
20
  * identity consolidation, recall section config, fast LLM) arrives as
21
21
  * injectable delegates.
22
22
  *
23
- * Behavior-preserving move from orchestrator.ts. No logic changes the
24
- * orchestrator keeps a thin delegating method so existing call sites
23
+ * The orchestrator keeps a thin delegating method so existing call sites
25
24
  * (runConsolidationNow, maintenance scheduler) and tests continue to work.
26
25
  */
27
26
 
@@ -32,7 +31,12 @@ import { applyCommitmentLedgerLifecycle } from "../commitment-ledger.js";
32
31
  import { recordDreamsPhaseRun } from "../maintenance/dreams-ledger.js";
33
32
  import { deindexMemoriesBatchAsync } from "../temporal-index-batch.js";
34
33
  import { isActiveMemoryStatus } from "../memory-lifecycle-ledger-utils.js";
35
- import { propagateInvalidation } from "./dependency-propagation.js";
34
+ import type {
35
+ DependencyPropagationDeliveryPort,
36
+ DependencyPropagationPreparationToken,
37
+ } from "./dependency-propagation-delivery.js";
38
+ import { canonicalize } from "./dependency-propagation-queue-state.js";
39
+ import type { PropagationEvent } from "./dependency-propagation.js";
36
40
  import {
37
41
  resolveCapabilities,
38
42
  resolveConsolidationCapabilities,
@@ -73,11 +77,13 @@ export interface ConsolidationRunCoordinatorDeps {
73
77
  getStorageRouter: () => NamespaceStorageRouter;
74
78
  getNamespaceCatalog?: () => NamespaceCatalog;
75
79
  getExtraction: () => ExtractionEngine;
80
+ storageDirNamespace: (storageDir: string) => string;
76
81
  embeddingFallback: EmbeddingFallback;
77
82
  tmtBuilder: TmtBuilder;
78
83
  consolidationObservers: Set<
79
84
  (observation: ConsolidationObservation) => Promise<void> | void
80
85
  >;
86
+ getDependencyPropagationDelivery: () => DependencyPropagationDeliveryPort;
81
87
  getAccessTrackingBuffer: () => Map<
82
88
  string,
83
89
  { count: number; lastAccessed: string }
@@ -113,6 +119,185 @@ type HarmonicStore = {
113
119
  abstractionNodeStoreDir?: string;
114
120
  };
115
121
 
122
+ type PendingDependencyPropagation = {
123
+ delivery: DependencyPropagationDeliveryPort;
124
+ event: PropagationEvent;
125
+ preparation: DependencyPropagationPreparationToken | null;
126
+ };
127
+
128
+ async function prepareDependencyPropagation(
129
+ getDelivery: () => DependencyPropagationDeliveryPort,
130
+ event: PropagationEvent,
131
+ enabled: boolean,
132
+ ): Promise<PendingDependencyPropagation | null> {
133
+ if (!enabled) return null;
134
+ let delivery: DependencyPropagationDeliveryPort;
135
+ try {
136
+ delivery = getDelivery();
137
+ } catch (error) {
138
+ log.warn(`consolidation dependency propagation getter failed: ${error}`);
139
+ return null;
140
+ }
141
+ let preparation: DependencyPropagationPreparationToken | null = null;
142
+ try {
143
+ preparation = await delivery.prepare(event);
144
+ } catch (error) {
145
+ log.warn(`consolidation dependency propagation preparation failed: ${error}`);
146
+ }
147
+ return { delivery, event, preparation };
148
+ }
149
+
150
+ function clonePropagationMemory(memory: MemoryFile): MemoryFile {
151
+ return structuredClone(memory);
152
+ }
153
+
154
+ function sameMemorySnapshot(a: MemoryFile, b: MemoryFile): boolean {
155
+ return (
156
+ a.content === b.content &&
157
+ JSON.stringify(canonicalize(a.frontmatter)) === JSON.stringify(canonicalize(b.frontmatter))
158
+ );
159
+ }
160
+
161
+ async function prepareCurrentDependencyPropagation(
162
+ storage: StorageManager,
163
+ getDelivery: () => DependencyPropagationDeliveryPort,
164
+ sourceId: string,
165
+ buildEvent: (source: MemoryFile) => PropagationEvent,
166
+ enabled: boolean,
167
+ ): Promise<{ source: MemoryFile; pending: PendingDependencyPropagation | null } | null> {
168
+ const loaded = await storage.getMemoryById(sourceId);
169
+ if (!loaded) return null;
170
+ let source = clonePropagationMemory(loaded);
171
+ let pending = await prepareDependencyPropagation(getDelivery, buildEvent(source), enabled);
172
+ if (!pending) return { source, pending: null };
173
+ let latest: MemoryFile | null;
174
+ try {
175
+ latest = await storage.getMemoryById(sourceId);
176
+ } catch (error) {
177
+ await cancelDependencyPropagation(pending);
178
+ throw error;
179
+ }
180
+ if (!latest) {
181
+ await cancelDependencyPropagation(pending);
182
+ return null;
183
+ }
184
+ if (!sameMemorySnapshot(source, latest)) {
185
+ await cancelDependencyPropagation(pending);
186
+ source = clonePropagationMemory(latest);
187
+ pending = await prepareDependencyPropagation(getDelivery, buildEvent(source), enabled);
188
+ }
189
+ return { source, pending };
190
+ }
191
+ async function cancelDependencyPropagation(
192
+ pending: PendingDependencyPropagation,
193
+ ): Promise<void> {
194
+ if (!pending.preparation?.ownsPreparedJob) return;
195
+ try {
196
+ await pending.delivery.cancel(pending.preparation);
197
+ } catch (error) {
198
+ log.warn(`consolidation dependency propagation cancellation failed: ${error}`);
199
+ }
200
+ }
201
+
202
+ async function deferDependencyPropagation(
203
+ pending: PendingDependencyPropagation | null,
204
+ ): Promise<void> {
205
+ if (!pending) return;
206
+ let token = pending.preparation;
207
+ if (token === null) {
208
+ try {
209
+ token = await pending.delivery.prepare(pending.event);
210
+ } catch (error) {
211
+ log.warn(`consolidation dependency propagation recovery preparation failed: ${error}`);
212
+ }
213
+ }
214
+ if (token === null) return;
215
+ try {
216
+ await pending.delivery.deferPrepared(token);
217
+ } catch (error) {
218
+ log.warn(`consolidation dependency propagation defer failed: ${error}`);
219
+ }
220
+ }
221
+
222
+ async function afterDependencyPropagationMutation(
223
+ pending: PendingDependencyPropagation,
224
+ ): Promise<void> {
225
+ try {
226
+ await pending.delivery.afterMutation(pending.preparation, pending.event);
227
+ } catch (error) {
228
+ log.warn(`consolidation dependency propagation delivery failed: ${error}`);
229
+ }
230
+ }
231
+ type InvalidationCommitState = "committed" | "not-committed" | "unknown";
232
+
233
+ async function classifyInvalidation(
234
+ storage: StorageManager,
235
+ memoryId: string,
236
+ expectedSnapshot: MemoryFile | null,
237
+ options: {
238
+ checkInvalidationProof?: boolean;
239
+ missingMeansCommitted?: boolean;
240
+ matchesCommitted?: (current: MemoryFile) => boolean;
241
+ } = {},
242
+ ): Promise<InvalidationCommitState> {
243
+ if (options.checkInvalidationProof !== false && expectedSnapshot) {
244
+ if (!storage.hasCommittedInvalidation) {
245
+ log.warn(`consolidation invalidation proof capability missing for ${memoryId}`);
246
+ } else {
247
+ try {
248
+ if (await storage.hasCommittedInvalidation(expectedSnapshot)) return "committed";
249
+ } catch (error) {
250
+ log.warn(`consolidation invalidation proof read failed for ${memoryId}: ${error}`);
251
+ }
252
+ }
253
+ }
254
+ try {
255
+ const current = await storage.getMemoryById(memoryId);
256
+ if (!current) {
257
+ if (options.missingMeansCommitted === false) return "not-committed";
258
+ if (expectedSnapshot && !storage.hasCommittedInvalidation) return "unknown";
259
+ return "committed";
260
+ }
261
+ if (options.matchesCommitted?.(current)) return "committed";
262
+ if (expectedSnapshot && sameMemorySnapshot(current, expectedSnapshot)) {
263
+ return "not-committed";
264
+ }
265
+ return "unknown";
266
+ } catch (error) {
267
+ log.warn(`consolidation invalidation state read failed for ${memoryId}: ${error}`);
268
+ return "unknown";
269
+ }
270
+ }
271
+
272
+ async function settleFailedConsolidationPropagation(
273
+ pending: PendingDependencyPropagation | null,
274
+ commitState: InvalidationCommitState,
275
+ memoryId: string,
276
+ ): Promise<void> {
277
+ if (!pending) return;
278
+ let token = pending.preparation;
279
+ if (commitState === "not-committed") {
280
+ if (token !== null) await cancelDependencyPropagation({ ...pending, preparation: token });
281
+ return;
282
+ }
283
+ if (token === null && (commitState === "committed" || commitState === "unknown")) {
284
+ try {
285
+ token = await pending.delivery.prepare(pending.event);
286
+ } catch (error) {
287
+ log.warn(
288
+ `consolidation dependency propagation recovery preparation failed for ${memoryId}: ${error}`,
289
+ );
290
+ }
291
+ }
292
+ if (token !== null) {
293
+ try {
294
+ await pending.delivery.deferPrepared(token);
295
+ } catch (error) {
296
+ log.warn(`consolidation dependency propagation defer failed for ${memoryId}: ${error}`);
297
+ }
298
+ }
299
+ }
300
+
116
301
  type HarmonicCatalogCursor = {
117
302
  nextStorageDir?: string;
118
303
  };
@@ -286,66 +471,75 @@ export class ConsolidationRunCoordinator {
286
471
  const profile = await storage.readProfile();
287
472
  const result = await this.deps.getExtraction().consolidate(recent, older, profile);
288
473
 
289
- // Build a lookup map from the already-loaded corpus to avoid repeated
290
- // readAllMemories() scans inside getMemoryById for pre-action deindex reads.
291
- const memoryLookup = resolveIndexingCapabilities(config).queryAwareIndexing
292
- ? new Map(allMemories.map((m) => [m.frontmatter.id, m]))
293
- : null;
294
-
295
474
  // Collect deindex entries from INVALIDATE/MERGE actions and de-index them in
296
- // one batch, instead of a full index read-modify-write per memory. The
297
- // queryAwareIndexing guard is preserved: memoryLookup is null when it is
298
- // disabled, so toInvalidate/toMergeInvalidate stay null and nothing is
299
- // collected. The flush runs in `finally` so memories already invalidated on
300
- // disk are still de-indexed if a later iteration throws; it runs exactly once
301
- // (normal completion or throw) and any loop error still propagates after it.
475
+ // one batch, instead of a full index read-modify-write per memory. The flush
476
+ // runs in `finally` so memories already invalidated on disk are still
477
+ // de-indexed if a later iteration throws; it runs exactly once and any loop
478
+ // error still propagates after it.
302
479
  const itemsDeindexBatch: Array<{ path: string; createdAt: string; tags: string[] }> = [];
303
480
 
304
481
  try {
305
482
  for (const item of result.items) {
306
483
  switch (item.action) {
307
484
  case "INVALIDATE": {
308
- // Capture path/frontmatter before invalidation for index cleanup
485
+ const propagationEnabled =
486
+ config.dependencyPropagation.enabled &&
487
+ config.dependencyPropagation.maxDependents > 0;
488
+ const prepared = await prepareCurrentDependencyPropagation(
489
+ storage,
490
+ this.deps.getDependencyPropagationDelivery,
491
+ item.existingId,
492
+ (propagationOld) => ({
493
+ oldMemory: propagationOld,
494
+ replacementId: null,
495
+ replacementContent: null,
496
+ cause: "consolidation_invalidate",
497
+ namespaceScope: this.deps.storageDirNamespace(storage.dir),
498
+ }),
499
+ propagationEnabled,
500
+ );
501
+ const propagationOld = prepared?.source ?? null;
502
+ const pending = prepared?.pending ?? null;
503
+ const expectedSnapshot = pending ? propagationOld : null;
309
504
  const toInvalidate = resolveIndexingCapabilities(config).queryAwareIndexing
310
- ? (memoryLookup?.get(item.existingId) ?? null)
505
+ ? propagationOld
311
506
  : null;
312
- // Always capture the full pre-delete snapshot for propagation.
313
- const propagationOld = allMemories.find(
314
- (memory) => memory.frontmatter.id === item.existingId,
315
- );
316
- if (await storage.invalidateMemory(item.existingId)) {
317
- invalidated += 1;
318
- memoryItemMutated = true;
319
- await this.deps.embeddingFallback.removeFromIndex(item.existingId);
320
- if (propagationOld) {
321
- try {
322
- await propagateInvalidation(
323
- {
324
- storage,
325
- extraction: this.deps.getExtraction(),
326
- config,
327
- },
328
- {
329
- oldMemory: propagationOld,
330
- replacementId: null,
331
- replacementContent: null,
332
- cause: "consolidation_invalidate",
333
- namespaceScope: config.defaultNamespace,
334
- },
335
- );
336
- } catch (propagationErr) {
337
- log.warn(
338
- `consolidation dependency propagation failed for ${item.existingId}: ${propagationErr}`,
339
- );
340
- }
341
- }
342
- if (toInvalidate?.path && toInvalidate.frontmatter?.created) {
343
- itemsDeindexBatch.push({
344
- path: toInvalidate.path,
345
- createdAt: toInvalidate.frontmatter.created,
346
- tags: toInvalidate.frontmatter.tags ?? [],
347
- });
348
- }
507
+ const recordCommitProof = pending?.preparation?.ownsPreparedJob === true;
508
+ let didInvalidate: boolean;
509
+ try {
510
+ didInvalidate = await storage.invalidateMemory(
511
+ item.existingId,
512
+ expectedSnapshot ?? undefined,
513
+ { recordCommitProof },
514
+ );
515
+ } catch (error) {
516
+ const commitState = await classifyInvalidation(storage, item.existingId, expectedSnapshot);
517
+ await settleFailedConsolidationPropagation(
518
+ pending,
519
+ commitState,
520
+ item.existingId,
521
+ );
522
+ throw error;
523
+ }
524
+ if (!didInvalidate) {
525
+ const commitState = await classifyInvalidation(storage, item.existingId, expectedSnapshot);
526
+ await settleFailedConsolidationPropagation(
527
+ pending,
528
+ commitState,
529
+ item.existingId,
530
+ );
531
+ break;
532
+ }
533
+ invalidated += 1;
534
+ memoryItemMutated = true;
535
+ if (pending) await afterDependencyPropagationMutation(pending);
536
+ await this.deps.embeddingFallback.removeFromIndex(item.existingId);
537
+ if (toInvalidate?.path && toInvalidate.frontmatter?.created) {
538
+ itemsDeindexBatch.push({
539
+ path: toInvalidate.path,
540
+ createdAt: toInvalidate.frontmatter.created,
541
+ tags: toInvalidate.frontmatter.tags ?? [],
542
+ });
349
543
  }
350
544
  break;
351
545
  }
@@ -366,63 +560,87 @@ export class ConsolidationRunCoordinator {
366
560
  break;
367
561
  case "MERGE":
368
562
  if (item.updatedContent && item.mergeWith) {
369
- await storage.updateMemory(
370
- item.existingId,
371
- item.updatedContent,
372
- {
373
- supersedes: item.mergeWith,
374
- lineage: [item.existingId, item.mergeWith],
375
- },
563
+ const updatedContent = item.updatedContent;
564
+ const mergeWith = item.mergeWith;
565
+ const propagationEnabled =
566
+ config.dependencyPropagation.enabled &&
567
+ config.dependencyPropagation.maxDependents > 0;
568
+ const prepared = await prepareCurrentDependencyPropagation(
569
+ storage,
570
+ this.deps.getDependencyPropagationDelivery,
571
+ item.mergeWith,
572
+ (propagationOld) => ({
573
+ oldMemory: propagationOld,
574
+ replacementId: item.existingId,
575
+ replacementContent: updatedContent,
576
+ cause: "consolidation_merge",
577
+ namespaceScope: this.deps.storageDirNamespace(storage.dir),
578
+ }),
579
+ propagationEnabled,
376
580
  );
581
+ const propagationOld = prepared?.source ?? null;
582
+ const pending = prepared?.pending ?? null;
583
+ const expectedSnapshot = pending ? propagationOld : null;
584
+ const toMergeInvalidate = resolveIndexingCapabilities(config).queryAwareIndexing
585
+ ? propagationOld
586
+ : null;
587
+ let didUpdate: boolean;
588
+ try {
589
+ didUpdate = await storage.updateMemory(
590
+ item.existingId,
591
+ updatedContent,
592
+ {
593
+ supersedes: item.mergeWith,
594
+ lineage: [item.existingId, item.mergeWith],
595
+ },
596
+ );
597
+ } catch (error) {
598
+ await deferDependencyPropagation(pending);
599
+ throw error;
600
+ }
601
+ if (!didUpdate) {
602
+ if (pending) await cancelDependencyPropagation(pending);
603
+ break;
604
+ }
377
605
  memoryItemMutated = true;
378
606
  await this.deps.indexPersistedMemory(storage, item.existingId);
379
607
  // updateMemory() only changes content/updated/supersedes/lineage — path, created, and tags
380
608
  // are preserved, so the temporal/tag index entry for the survivor is already correct.
381
- // Capture before invalidation for index cleanup
382
- const toMergeInvalidate = resolveIndexingCapabilities(config).queryAwareIndexing
383
- ? (memoryLookup?.get(item.mergeWith) ?? null)
384
- : null;
385
- // Capture the doomed memory before invalidation, independent of
386
- // queryAwareIndexing and its optional lookup map.
387
- const propagationOld = allMemories.find(
388
- (memory) => memory.frontmatter.id === item.mergeWith,
389
- );
390
- if (await storage.invalidateMemory(item.mergeWith)) {
391
- invalidated += 1;
392
- merged += 1;
393
- await this.deps.embeddingFallback.removeFromIndex(item.mergeWith);
394
- if (propagationOld) {
395
- try {
396
- await propagateInvalidation(
397
- {
398
- storage,
399
- extraction: this.deps.getExtraction(),
400
- config,
401
- },
402
- {
403
- oldMemory: propagationOld,
404
- replacementId: item.existingId,
405
- replacementContent: item.updatedContent,
406
- cause: "consolidation_merge",
407
- namespaceScope: config.defaultNamespace,
408
- },
409
- );
410
- } catch (propagationErr) {
411
- log.warn(
412
- `consolidation dependency propagation failed for ${item.mergeWith}: ${propagationErr}`,
413
- );
414
- }
415
- }
416
- if (
417
- toMergeInvalidate?.path &&
418
- toMergeInvalidate.frontmatter?.created
419
- ) {
420
- itemsDeindexBatch.push({
421
- path: toMergeInvalidate.path,
422
- createdAt: toMergeInvalidate.frontmatter.created,
423
- tags: toMergeInvalidate.frontmatter.tags ?? [],
424
- });
425
- }
609
+ let didInvalidate: boolean;
610
+ try {
611
+ didInvalidate = await storage.invalidateMemory(
612
+ item.mergeWith,
613
+ expectedSnapshot ?? undefined,
614
+ { recordCommitProof: pending?.preparation?.ownsPreparedJob === true },
615
+ );
616
+ } catch (error) {
617
+ const commitState = await classifyInvalidation(storage, item.mergeWith, expectedSnapshot);
618
+ await settleFailedConsolidationPropagation(
619
+ pending,
620
+ commitState,
621
+ item.mergeWith,
622
+ );
623
+ throw error;
624
+ }
625
+ if (!didInvalidate) {
626
+ const commitState = await classifyInvalidation(storage, item.mergeWith, expectedSnapshot);
627
+ await settleFailedConsolidationPropagation(
628
+ pending,
629
+ commitState,
630
+ item.mergeWith,
631
+ );
632
+ break;
633
+ }
634
+ invalidated += 1;
635
+ merged += 1;
636
+ if (pending) await afterDependencyPropagationMutation(pending);
637
+ await this.deps.embeddingFallback.removeFromIndex(item.mergeWith);
638
+ if (toMergeInvalidate?.path && toMergeInvalidate.frontmatter?.created) {
639
+ itemsDeindexBatch.push({
640
+ path: toMergeInvalidate.path,
641
+ createdAt: toMergeInvalidate.frontmatter.created,
642
+ tags: toMergeInvalidate.frontmatter.tags ?? [],
643
+ });
426
644
  }
427
645
  }
428
646
  break;