@graphrefly/graphrefly 0.47.2 → 0.48.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 (266) hide show
  1. package/dist/base/composition/index.cjs +4 -3
  2. package/dist/base/composition/index.cjs.map +1 -1
  3. package/dist/base/composition/index.d.cts +14 -5
  4. package/dist/base/composition/index.d.ts +14 -5
  5. package/dist/base/composition/index.js +8 -8
  6. package/dist/base/index.cjs +152 -78
  7. package/dist/base/index.cjs.map +1 -1
  8. package/dist/base/index.d.cts +2 -2
  9. package/dist/base/index.d.ts +2 -2
  10. package/dist/base/index.js +75 -70
  11. package/dist/base/io/index.cjs +31 -17
  12. package/dist/base/io/index.cjs.map +1 -1
  13. package/dist/base/io/index.d.cts +32 -5
  14. package/dist/base/io/index.d.ts +32 -5
  15. package/dist/base/io/index.js +1 -1
  16. package/dist/base/mutation/index.cjs +21 -0
  17. package/dist/base/mutation/index.cjs.map +1 -1
  18. package/dist/base/mutation/index.d.cts +23 -1
  19. package/dist/base/mutation/index.d.ts +23 -1
  20. package/dist/base/mutation/index.js +3 -1
  21. package/dist/base/sources/browser/index.cjs +5 -3
  22. package/dist/base/sources/browser/index.cjs.map +1 -1
  23. package/dist/base/sources/browser/index.d.cts +20 -2
  24. package/dist/base/sources/browser/index.d.ts +20 -2
  25. package/dist/base/sources/browser/index.js +5 -3
  26. package/dist/base/sources/browser/index.js.map +1 -1
  27. package/dist/base/sources/event/index.cjs +28 -0
  28. package/dist/base/sources/event/index.cjs.map +1 -1
  29. package/dist/base/sources/event/index.d.cts +67 -3
  30. package/dist/base/sources/event/index.d.ts +67 -3
  31. package/dist/base/sources/event/index.js +4 -1
  32. package/dist/base/sources/index.cjs +75 -37
  33. package/dist/base/sources/index.cjs.map +1 -1
  34. package/dist/base/sources/index.d.cts +1 -1
  35. package/dist/base/sources/index.d.ts +1 -1
  36. package/dist/base/sources/index.js +5 -2
  37. package/dist/{chunk-R6ZCSXKX.js → chunk-23MAWVOJ.js} +3 -3
  38. package/dist/{chunk-MS3WPRJR.js → chunk-3REMCHSS.js} +6 -6
  39. package/dist/chunk-3REMCHSS.js.map +1 -0
  40. package/dist/{chunk-CEVNQ74M.js → chunk-3YGXPUHW.js} +2 -2
  41. package/dist/{chunk-CEVNQ74M.js.map → chunk-3YGXPUHW.js.map} +1 -1
  42. package/dist/{chunk-6ZLCPUXS.js → chunk-46X2EFQH.js} +15 -4
  43. package/dist/chunk-46X2EFQH.js.map +1 -0
  44. package/dist/{chunk-NY2PYHNC.js → chunk-5UY3PNFY.js} +12 -5
  45. package/dist/chunk-5UY3PNFY.js.map +1 -0
  46. package/dist/{chunk-FQSQONOU.js → chunk-65OM4XLQ.js} +49 -3
  47. package/dist/chunk-65OM4XLQ.js.map +1 -0
  48. package/dist/{chunk-3PSLNJDU.js → chunk-6DQYBIHW.js} +314 -49
  49. package/dist/chunk-6DQYBIHW.js.map +1 -0
  50. package/dist/{chunk-LDCSZ72P.js → chunk-6YBER5UP.js} +3 -3
  51. package/dist/{chunk-LDCSZ72P.js.map → chunk-6YBER5UP.js.map} +1 -1
  52. package/dist/{chunk-3O3NKZJW.js → chunk-7T7WLEPM.js} +24 -3
  53. package/dist/chunk-7T7WLEPM.js.map +1 -0
  54. package/dist/{chunk-PKPO3JTZ.js → chunk-AQAKDE7F.js} +29 -11
  55. package/dist/chunk-AQAKDE7F.js.map +1 -0
  56. package/dist/{chunk-6MRSX3YK.js → chunk-B5Y5GPD5.js} +2 -2
  57. package/dist/{chunk-BXGZFGZ4.js → chunk-C5QD5DQX.js} +22 -1
  58. package/dist/chunk-C5QD5DQX.js.map +1 -0
  59. package/dist/{chunk-4XCHZRUJ.js → chunk-D5YGR4TP.js} +58 -7
  60. package/dist/chunk-D5YGR4TP.js.map +1 -0
  61. package/dist/{chunk-NPRP3MCV.js → chunk-DHDCOOJU.js} +2 -2
  62. package/dist/chunk-DHDCOOJU.js.map +1 -0
  63. package/dist/{chunk-VP3TIUDF.js → chunk-DVTDF5OI.js} +2 -2
  64. package/dist/{chunk-OXD5LFQP.js → chunk-G7H6PN7P.js} +2 -2
  65. package/dist/{chunk-EL5VHUGK.js → chunk-GGKHHG5Y.js} +32 -18
  66. package/dist/chunk-GGKHHG5Y.js.map +1 -0
  67. package/dist/{chunk-446I4EGD.js → chunk-J5TBZFBD.js} +2 -2
  68. package/dist/{chunk-7AVQIGF6.js → chunk-K4ZYJ4EM.js} +554 -460
  69. package/dist/chunk-K4ZYJ4EM.js.map +1 -0
  70. package/dist/{chunk-QFE5BQH7.js → chunk-LTSI7ULC.js} +2 -2
  71. package/dist/{chunk-5GVURVIG.js → chunk-MMHGYX44.js} +12 -2
  72. package/dist/{chunk-5GVURVIG.js.map → chunk-MMHGYX44.js.map} +1 -1
  73. package/dist/{chunk-KRFGO5QH.js → chunk-MQMTRKY3.js} +118 -43
  74. package/dist/chunk-MQMTRKY3.js.map +1 -0
  75. package/dist/{chunk-42FQ27MQ.js → chunk-MTODGQBR.js} +44 -179
  76. package/dist/chunk-MTODGQBR.js.map +1 -0
  77. package/dist/{chunk-FVINAAKA.js → chunk-NBK6QQMG.js} +14 -13
  78. package/dist/{chunk-FVINAAKA.js.map → chunk-NBK6QQMG.js.map} +1 -1
  79. package/dist/{chunk-KNU73RZW.js → chunk-NSA5K5G2.js} +2 -2
  80. package/dist/{chunk-MLTPJMH6.js → chunk-QQYULEZL.js} +2 -2
  81. package/dist/chunk-QSW4DFKE.js +31 -0
  82. package/dist/chunk-QSW4DFKE.js.map +1 -0
  83. package/dist/{chunk-VAZXUK6G.js → chunk-SUNCHMML.js} +2 -2
  84. package/dist/{chunk-EP4WVQLX.js → chunk-T2U6N3FV.js} +6 -6
  85. package/dist/{chunk-T7SP3EYR.js → chunk-T5URUIIY.js} +33 -24
  86. package/dist/chunk-T5URUIIY.js.map +1 -0
  87. package/dist/{chunk-VNXAF2KE.js → chunk-TPTZZV25.js} +6 -6
  88. package/dist/chunk-TPTZZV25.js.map +1 -0
  89. package/dist/{chunk-IOJDYUA7.js → chunk-V46JWFGV.js} +6 -5
  90. package/dist/chunk-V46JWFGV.js.map +1 -0
  91. package/dist/{chunk-WGDEBIP4.js → chunk-X6ESZDR6.js} +5 -6
  92. package/dist/chunk-X6ESZDR6.js.map +1 -0
  93. package/dist/{chunk-N65E26UL.js → chunk-XEWV254I.js} +2 -2
  94. package/dist/{chunk-N65E26UL.js.map → chunk-XEWV254I.js.map} +1 -1
  95. package/dist/{chunk-PTWADEH3.js → chunk-YBJVKMTM.js} +34 -14
  96. package/dist/chunk-YBJVKMTM.js.map +1 -0
  97. package/dist/{chunk-DDTS7F5O.js → chunk-ZW32BPXV.js} +12 -3
  98. package/dist/chunk-ZW32BPXV.js.map +1 -0
  99. package/dist/compat/index.cjs +51 -4
  100. package/dist/compat/index.cjs.map +1 -1
  101. package/dist/compat/index.d.cts +1 -1
  102. package/dist/compat/index.d.ts +1 -1
  103. package/dist/compat/index.js +6 -6
  104. package/dist/compat/nestjs/index.cjs +51 -4
  105. package/dist/compat/nestjs/index.cjs.map +1 -1
  106. package/dist/compat/nestjs/index.d.cts +1 -1
  107. package/dist/compat/nestjs/index.d.ts +1 -1
  108. package/dist/compat/nestjs/index.js +3 -3
  109. package/dist/{fallback-Bx46zqky.d.cts → fallback-BROR6ZhO.d.cts} +1 -1
  110. package/dist/{fallback-pIWW8A2d.d.ts → fallback-DO80aM_3.d.ts} +1 -1
  111. package/dist/{index-B_p8tnvf.d.cts → index-D1z3XcF9.d.cts} +1 -0
  112. package/dist/{index-_HDSmPyp.d.ts → index-DZ6yua0Q.d.ts} +1 -0
  113. package/dist/index.cjs +2215 -1676
  114. package/dist/index.cjs.map +1 -1
  115. package/dist/index.d.cts +10 -10
  116. package/dist/index.d.ts +10 -10
  117. package/dist/index.js +169 -146
  118. package/dist/index.js.map +1 -1
  119. package/dist/presets/ai/index.cjs +46 -0
  120. package/dist/presets/ai/index.cjs.map +1 -1
  121. package/dist/presets/ai/index.js +12 -12
  122. package/dist/presets/harness/index.cjs +130 -18
  123. package/dist/presets/harness/index.cjs.map +1 -1
  124. package/dist/presets/harness/index.d.cts +15 -5
  125. package/dist/presets/harness/index.d.ts +15 -5
  126. package/dist/presets/harness/index.js +22 -22
  127. package/dist/presets/index.cjs +222 -53
  128. package/dist/presets/index.cjs.map +1 -1
  129. package/dist/presets/index.d.cts +2 -2
  130. package/dist/presets/index.d.ts +2 -2
  131. package/dist/presets/index.js +45 -45
  132. package/dist/presets/inspect/index.cjs +63 -14
  133. package/dist/presets/inspect/index.cjs.map +1 -1
  134. package/dist/presets/inspect/index.d.cts +1 -1
  135. package/dist/presets/inspect/index.d.ts +1 -1
  136. package/dist/presets/inspect/index.js +6 -6
  137. package/dist/presets/resilience/index.cjs +29 -21
  138. package/dist/presets/resilience/index.cjs.map +1 -1
  139. package/dist/presets/resilience/index.d.cts +12 -8
  140. package/dist/presets/resilience/index.d.ts +12 -8
  141. package/dist/presets/resilience/index.js +3 -3
  142. package/dist/{rate-limiter-DpVbSYdH.d.cts → rate-limiter-DC26FM8J.d.cts} +10 -1
  143. package/dist/{rate-limiter-CEALq4N1.d.ts → rate-limiter-DyWpwpQP.d.ts} +10 -1
  144. package/dist/{reactive-layout-fswlBUvX.d.ts → reactive-layout-BBBWH0V_.d.cts} +85 -4
  145. package/dist/{reactive-layout-fswlBUvX.d.cts → reactive-layout-BBBWH0V_.d.ts} +85 -4
  146. package/dist/solutions/index.cjs +168 -47
  147. package/dist/solutions/index.cjs.map +1 -1
  148. package/dist/solutions/index.d.cts +2 -2
  149. package/dist/solutions/index.d.ts +2 -2
  150. package/dist/solutions/index.js +28 -28
  151. package/dist/{spawnable-5mDY501F.d.cts → spawnable-B2IlW60f.d.cts} +23 -2
  152. package/dist/{spawnable-D3lR0oQu.d.ts → spawnable-tttFz2Nh.d.ts} +23 -2
  153. package/dist/testing/index.cjs +94 -0
  154. package/dist/testing/index.cjs.map +1 -0
  155. package/dist/testing/index.d.cts +59 -0
  156. package/dist/testing/index.d.ts +59 -0
  157. package/dist/testing/index.js +73 -0
  158. package/dist/testing/index.js.map +1 -0
  159. package/dist/utils/ai/browser.cjs.map +1 -1
  160. package/dist/utils/ai/browser.d.cts +2 -2
  161. package/dist/utils/ai/browser.d.ts +2 -2
  162. package/dist/utils/ai/browser.js +6 -6
  163. package/dist/utils/ai/browser.js.map +1 -1
  164. package/dist/utils/ai/index.cjs +250 -166
  165. package/dist/utils/ai/index.cjs.map +1 -1
  166. package/dist/utils/ai/index.d.cts +108 -12
  167. package/dist/utils/ai/index.d.ts +108 -12
  168. package/dist/utils/ai/index.js +21 -19
  169. package/dist/utils/ai/node.cjs.map +1 -1
  170. package/dist/utils/ai/node.d.cts +5 -5
  171. package/dist/utils/ai/node.d.ts +5 -5
  172. package/dist/utils/ai/node.js +2 -2
  173. package/dist/utils/ai/node.js.map +1 -1
  174. package/dist/utils/cqrs/index.cjs +29 -3
  175. package/dist/utils/cqrs/index.cjs.map +1 -1
  176. package/dist/utils/cqrs/index.d.cts +12 -7
  177. package/dist/utils/cqrs/index.d.ts +12 -7
  178. package/dist/utils/cqrs/index.js +2 -2
  179. package/dist/utils/demo-shell/index.cjs +45 -19
  180. package/dist/utils/demo-shell/index.cjs.map +1 -1
  181. package/dist/utils/demo-shell/index.d.cts +1 -1
  182. package/dist/utils/demo-shell/index.d.ts +1 -1
  183. package/dist/utils/demo-shell/index.js +2 -2
  184. package/dist/utils/domain-templates/index.cjs.map +1 -1
  185. package/dist/utils/domain-templates/index.js +3 -3
  186. package/dist/utils/graphspec/index.cjs.map +1 -1
  187. package/dist/utils/graphspec/index.js +3 -3
  188. package/dist/utils/index.cjs +1642 -1225
  189. package/dist/utils/index.cjs.map +1 -1
  190. package/dist/utils/index.d.cts +7 -7
  191. package/dist/utils/index.d.ts +7 -7
  192. package/dist/utils/index.js +72 -54
  193. package/dist/utils/inspect/index.cjs +52 -4
  194. package/dist/utils/inspect/index.cjs.map +1 -1
  195. package/dist/utils/inspect/index.d.cts +32 -3
  196. package/dist/utils/inspect/index.d.ts +32 -3
  197. package/dist/utils/inspect/index.js +4 -4
  198. package/dist/utils/job-queue/index.cjs +46 -9
  199. package/dist/utils/job-queue/index.cjs.map +1 -1
  200. package/dist/utils/job-queue/index.d.cts +33 -3
  201. package/dist/utils/job-queue/index.d.ts +33 -3
  202. package/dist/utils/job-queue/index.js +2 -2
  203. package/dist/utils/memory/index.cjs +556 -462
  204. package/dist/utils/memory/index.cjs.map +1 -1
  205. package/dist/utils/memory/index.d.cts +203 -24
  206. package/dist/utils/memory/index.d.ts +203 -24
  207. package/dist/utils/memory/index.js +10 -2
  208. package/dist/utils/messaging/index.cjs.map +1 -1
  209. package/dist/utils/messaging/index.d.cts +4 -3
  210. package/dist/utils/messaging/index.d.ts +4 -3
  211. package/dist/utils/messaging/index.js +2 -2
  212. package/dist/utils/orchestration/index.cjs +9 -0
  213. package/dist/utils/orchestration/index.cjs.map +1 -1
  214. package/dist/utils/orchestration/index.js +3 -3
  215. package/dist/utils/process/index.cjs +32 -2
  216. package/dist/utils/process/index.cjs.map +1 -1
  217. package/dist/utils/process/index.d.cts +4 -3
  218. package/dist/utils/process/index.d.ts +4 -3
  219. package/dist/utils/process/index.js +2 -2
  220. package/dist/utils/reactive-layout/index.cjs +184 -55
  221. package/dist/utils/reactive-layout/index.cjs.map +1 -1
  222. package/dist/utils/reactive-layout/index.d.cts +128 -3
  223. package/dist/utils/reactive-layout/index.d.ts +128 -3
  224. package/dist/utils/reactive-layout/index.js +16 -8
  225. package/dist/utils/reduction/index.cjs.map +1 -1
  226. package/dist/utils/reduction/index.js +2 -2
  227. package/dist/utils/resilience/index.cjs +29 -20
  228. package/dist/utils/resilience/index.cjs.map +1 -1
  229. package/dist/utils/resilience/index.d.cts +1 -1
  230. package/dist/utils/resilience/index.d.ts +1 -1
  231. package/dist/utils/resilience/index.js +2 -2
  232. package/dist/utils/surface/index.cjs.map +1 -1
  233. package/dist/utils/surface/index.js +4 -4
  234. package/package.json +15 -3
  235. package/dist/chunk-3O3NKZJW.js.map +0 -1
  236. package/dist/chunk-3PSLNJDU.js.map +0 -1
  237. package/dist/chunk-42FQ27MQ.js.map +0 -1
  238. package/dist/chunk-4XCHZRUJ.js.map +0 -1
  239. package/dist/chunk-6ZLCPUXS.js.map +0 -1
  240. package/dist/chunk-7AVQIGF6.js.map +0 -1
  241. package/dist/chunk-BXGZFGZ4.js.map +0 -1
  242. package/dist/chunk-DDTS7F5O.js.map +0 -1
  243. package/dist/chunk-EL5VHUGK.js.map +0 -1
  244. package/dist/chunk-FQSQONOU.js.map +0 -1
  245. package/dist/chunk-IOJDYUA7.js.map +0 -1
  246. package/dist/chunk-KRFGO5QH.js.map +0 -1
  247. package/dist/chunk-MS3WPRJR.js.map +0 -1
  248. package/dist/chunk-NPRP3MCV.js.map +0 -1
  249. package/dist/chunk-NY2PYHNC.js.map +0 -1
  250. package/dist/chunk-PKPO3JTZ.js.map +0 -1
  251. package/dist/chunk-PTWADEH3.js.map +0 -1
  252. package/dist/chunk-T7SP3EYR.js.map +0 -1
  253. package/dist/chunk-VNXAF2KE.js.map +0 -1
  254. package/dist/chunk-W2BOPXTI.js +0 -1
  255. package/dist/chunk-W2BOPXTI.js.map +0 -1
  256. package/dist/chunk-WGDEBIP4.js.map +0 -1
  257. /package/dist/{chunk-R6ZCSXKX.js.map → chunk-23MAWVOJ.js.map} +0 -0
  258. /package/dist/{chunk-6MRSX3YK.js.map → chunk-B5Y5GPD5.js.map} +0 -0
  259. /package/dist/{chunk-VP3TIUDF.js.map → chunk-DVTDF5OI.js.map} +0 -0
  260. /package/dist/{chunk-OXD5LFQP.js.map → chunk-G7H6PN7P.js.map} +0 -0
  261. /package/dist/{chunk-446I4EGD.js.map → chunk-J5TBZFBD.js.map} +0 -0
  262. /package/dist/{chunk-QFE5BQH7.js.map → chunk-LTSI7ULC.js.map} +0 -0
  263. /package/dist/{chunk-KNU73RZW.js.map → chunk-NSA5K5G2.js.map} +0 -0
  264. /package/dist/{chunk-MLTPJMH6.js.map → chunk-QQYULEZL.js.map} +0 -0
  265. /package/dist/{chunk-VAZXUK6G.js.map → chunk-SUNCHMML.js.map} +0 -0
  266. /package/dist/{chunk-EP4WVQLX.js.map → chunk-T2U6N3FV.js.map} +0 -0
@@ -9,10 +9,10 @@ import {
9
9
  createAuditLog,
10
10
  mutate,
11
11
  registerCursor
12
- } from "./chunk-BXGZFGZ4.js";
12
+ } from "./chunk-C5QD5DQX.js";
13
13
 
14
14
  // src/utils/memory/index.ts
15
- import { monotonicNs as monotonicNs2, NodeImpl, node as node9, wallClockNs as wallClockNs3 } from "@graphrefly/pure-ts/core";
15
+ import { monotonicNs as monotonicNs3, NodeImpl, node as node10, wallClockNs as wallClockNs3 } from "@graphrefly/pure-ts/core";
16
16
  import { fromTimer as fromTimer3, keepalive as keepalive6, reactiveMap } from "@graphrefly/pure-ts/extra";
17
17
  import { Graph as Graph2 } from "@graphrefly/pure-ts/graph";
18
18
 
@@ -51,479 +51,534 @@ function currentlyValid(f, asOf) {
51
51
  function lastOf(batch, prev) {
52
52
  return batch != null && batch.length > 0 ? batch.at(-1) : prev;
53
53
  }
54
- function reactiveFactStore(config) {
55
- const shardCount = Math.max(1, config.shardCount ?? 4);
56
- const maxIterations = Math.max(1, config.cascadeMaxIterations ?? 8);
57
- const reviewThreshold = config.reviewThreshold ?? 0.3;
58
- const shardBy = config.shardBy ?? ((f) => fnv1a(String(f.id)) % shardCount);
59
- let cascadeIteration = 0;
60
- const processedRoots = /* @__PURE__ */ new Set();
61
- const graph = new Graph("reactive_fact_store");
62
- const events = createAuditLog({
63
- name: "events",
64
- retainedLimit: 1024,
65
- graph
66
- });
67
- const seqCursor = registerCursor(graph, "seq", 0);
68
- const ingestLog = config.recordIngest ? reactiveLog([], { name: "ingest_log" }) : void 0;
69
- if (ingestLog) graph.addDisposer(() => ingestLog.dispose());
70
- const emptyStore = () => ({ byId: /* @__PURE__ */ new Map() });
71
- const shards = [];
72
- for (let s = 0; s < shardCount; s += 1) {
73
- const shard = node([], {
74
- initial: emptyStore(),
75
- name: `shard_${s}`,
76
- describeKind: "state",
77
- meta: factMeta("factstore", { shard: s })
54
+ var ReactiveFactStoreGraph = class extends Graph {
55
+ // Topic outputs (caller subscribes for custom processing).
56
+ /** Per-shard `state<FactStore<T>>` nodes (length = shard count). */
57
+ shards;
58
+ /** Unified read view across all shards (derived). */
59
+ factStore;
60
+ dependentsIndex;
61
+ answer;
62
+ cascade;
63
+ cascadeOverflow;
64
+ review;
65
+ consolidated;
66
+ events;
67
+ /**
68
+ * Payload-carrying, replayable log of every committed fragment. Present iff
69
+ * {@link ReactiveFactStoreConfig.recordIngest} is `true`. Unlike
70
+ * {@link ReactiveFactStoreGraph.events} (action-only audit), each entry is
71
+ * the full {@link MemoryFragment} — `attachStorage` it for a durable,
72
+ * replayable projection source (see `recordIngest` docs for the recipe).
73
+ */
74
+ ingestLog;
75
+ constructor(config) {
76
+ const shardCount = Math.max(1, config.shardCount ?? 4);
77
+ const maxIterations = Math.max(1, config.cascadeMaxIterations ?? 8);
78
+ const reviewThreshold = config.reviewThreshold ?? 0.3;
79
+ const shardBy = config.shardBy ?? ((f) => fnv1a(String(f.id)) % shardCount);
80
+ let cascadeIteration = 0;
81
+ const processedRoots = /* @__PURE__ */ new Set();
82
+ super("reactive_fact_store");
83
+ const events = createAuditLog({
84
+ name: "events",
85
+ retainedLimit: 1024,
86
+ graph: this
78
87
  });
79
- graph.add(shard, { name: `shard_${s}` });
80
- graph.addDisposer(keepalive(shard));
81
- shards.push(shard);
82
- }
83
- const shardIndexFor = (f) => {
84
- const key = shardBy(f);
85
- const n = typeof key === "number" ? key : fnv1a(String(key));
86
- const idx = (n % shardCount + shardCount) % shardCount;
87
- return idx;
88
- };
89
- const findShardOf = (id) => {
88
+ const seqCursor = registerCursor(this, "seq", 0);
89
+ const ingestLog = config.recordIngest ? reactiveLog([], { name: "ingest_log" }) : void 0;
90
+ if (ingestLog) this.addDisposer(() => ingestLog.dispose());
91
+ const emptyStore = () => ({ byId: /* @__PURE__ */ new Map() });
92
+ const shards = [];
90
93
  for (let s = 0; s < shardCount; s += 1) {
91
- const fs = shards[s].cache;
92
- if (fs?.byId.has(id)) return s;
93
- }
94
- return -1;
95
- };
96
- const allFacts = () => {
97
- const out2 = /* @__PURE__ */ new Map();
98
- for (const sh of shards) {
99
- const fs = sh.cache;
100
- if (!fs) continue;
101
- for (const [k, v] of fs.byId) out2.set(k, v);
102
- }
103
- return out2;
104
- };
105
- const commitFragment = (f) => {
106
- const idx = shardIndexFor(f);
107
- const cur = shards[idx].cache ?? emptyStore();
108
- const next = new Map(cur.byId);
109
- next.set(f.id, f);
110
- shards[idx].emit({ byId: next });
111
- };
112
- const replaceFragment = (id, mut) => {
113
- const idx = findShardOf(id);
114
- if (idx < 0) return false;
115
- const cur = shards[idx].cache;
116
- const prev = cur.byId.get(id);
117
- if (!prev) return false;
118
- const next = new Map(cur.byId);
119
- next.set(id, mut(prev));
120
- shards[idx].emit({ byId: next });
121
- return true;
122
- };
123
- const dependentsIndex = node([], {
124
- initial: /* @__PURE__ */ new Map(),
125
- name: "dependents_index",
126
- describeKind: "state",
127
- meta: factMeta("factstore", { role: "dependents_index" })
128
- });
129
- graph.add(dependentsIndex, { name: "dependents_index" });
130
- graph.addDisposer(keepalive(dependentsIndex));
131
- const indexFragment = (f, deps) => {
132
- const cur = dependentsIndex.cache;
133
- const next = /* @__PURE__ */ new Map();
134
- for (const [k, v] of cur) next.set(k, [...v]);
135
- for (const src of deps) {
136
- const bucket = next.get(src) ?? [];
137
- if (!bucket.includes(f.id)) bucket.push(f.id);
138
- next.set(src, bucket);
94
+ const shard = node([], {
95
+ initial: emptyStore(),
96
+ name: `shard_${s}`,
97
+ describeKind: "state",
98
+ meta: factMeta("factstore", { shard: s })
99
+ });
100
+ this.add(shard, { name: `shard_${s}` });
101
+ this.addDisposer(keepalive(shard));
102
+ shards.push(shard);
139
103
  }
140
- dependentsIndex.emit(next);
141
- };
142
- const factStore = node(
143
- shards,
144
- (batchData, actions, ctx) => {
145
- void batchData;
146
- void ctx;
147
- actions.emit({ byId: allFacts() });
148
- },
149
- {
150
- name: "fact_store",
151
- describeKind: "derived",
152
- initial: emptyStore(),
153
- meta: factMeta("factstore", { role: "read_view" }),
154
- // F10a: `allFacts()` builds a fresh Map every detector retrigger.
155
- // Fragments are immutable (replaced wholesale on mutation), so a
156
- // same-size + per-key-identity check is a sound structural equality
157
- // that stops `factStore` (and its `review` dependent) from re-firing
158
- // every cascade wave when nothing actually changed.
159
- equals: (a, b) => {
160
- if (a === b) return true;
161
- if (a.byId.size !== b.byId.size) return false;
162
- for (const [k, v] of a.byId) {
163
- if (b.byId.get(k) !== v) return false;
164
- }
165
- return true;
104
+ const shardIndexFor = (f) => {
105
+ const key = shardBy(f);
106
+ const n = typeof key === "number" ? key : fnv1a(String(key));
107
+ const idx = (n % shardCount + shardCount) % shardCount;
108
+ return idx;
109
+ };
110
+ const findShardOf = (id) => {
111
+ for (let s = 0; s < shardCount; s += 1) {
112
+ const fs = shards[s].cache;
113
+ if (fs?.byId.has(id)) return s;
166
114
  }
167
- }
168
- );
169
- graph.add(factStore, { name: "fact_store" });
170
- graph.addDisposer(keepalive(factStore));
171
- const extractOp = node(
172
- config.admissionFilter ? [config.ingest, config.admissionFilter] : [config.ingest],
173
- (batchData, actions, ctx) => {
174
- const f = lastOf(batchData[0], ctx.prevData[0]);
175
- if (f == null) {
176
- actions.emit(null);
177
- return;
115
+ return -1;
116
+ };
117
+ const allFacts = () => {
118
+ const out = /* @__PURE__ */ new Map();
119
+ for (const sh of shards) {
120
+ const fs = sh.cache;
121
+ if (!fs) continue;
122
+ for (const [k, v] of fs.byId) out.set(k, v);
178
123
  }
179
- if (config.admissionFilter) {
180
- const filter = lastOf(batchData[1], ctx.prevData[1]);
181
- if (filter && !filter(f)) {
124
+ return out;
125
+ };
126
+ const commitFragment = (f) => {
127
+ const idx = shardIndexFor(f);
128
+ const cur = shards[idx].cache ?? emptyStore();
129
+ const next = new Map(cur.byId);
130
+ next.set(f.id, f);
131
+ shards[idx].emit({ byId: next });
132
+ };
133
+ const replaceFragment = (id, mut) => {
134
+ const idx = findShardOf(id);
135
+ if (idx < 0) return false;
136
+ const cur = shards[idx].cache;
137
+ const prev = cur.byId.get(id);
138
+ if (!prev) return false;
139
+ const next = new Map(cur.byId);
140
+ next.set(id, mut(prev));
141
+ shards[idx].emit({ byId: next });
142
+ return true;
143
+ };
144
+ const dependentsIndex = node([], {
145
+ initial: /* @__PURE__ */ new Map(),
146
+ name: "dependents_index",
147
+ describeKind: "state",
148
+ meta: factMeta("factstore", { role: "dependents_index" })
149
+ });
150
+ this.add(dependentsIndex, { name: "dependents_index" });
151
+ this.addDisposer(keepalive(dependentsIndex));
152
+ const indexFragment = (f, deps) => {
153
+ const cur = dependentsIndex.cache;
154
+ const next = /* @__PURE__ */ new Map();
155
+ for (const [k, v] of cur) next.set(k, [...v]);
156
+ for (const src of deps) {
157
+ const bucket = next.get(src) ?? [];
158
+ if (!bucket.includes(f.id)) bucket.push(f.id);
159
+ next.set(src, bucket);
160
+ }
161
+ dependentsIndex.emit(next);
162
+ };
163
+ const factStore = node(
164
+ shards,
165
+ (batchData, actions, ctx) => {
166
+ void batchData;
167
+ void ctx;
168
+ actions.emit({ byId: allFacts() });
169
+ },
170
+ {
171
+ name: "fact_store",
172
+ describeKind: "derived",
173
+ initial: emptyStore(),
174
+ meta: factMeta("factstore", { role: "read_view" }),
175
+ // F10a: `allFacts()` builds a fresh Map every detector retrigger.
176
+ // Fragments are immutable (replaced wholesale on mutation), so a
177
+ // same-size + per-key-identity check is a sound structural equality
178
+ // that stops `factStore` (and its `review` dependent) from re-firing
179
+ // every cascade wave when nothing actually changed.
180
+ equals: (a, b) => {
181
+ if (a === b) return true;
182
+ if (a.byId.size !== b.byId.size) return false;
183
+ for (const [k, v] of a.byId) {
184
+ if (b.byId.get(k) !== v) return false;
185
+ }
186
+ return true;
187
+ }
188
+ }
189
+ );
190
+ this.add(factStore, { name: "fact_store" });
191
+ this.addDisposer(keepalive(factStore));
192
+ const extractOp = node(
193
+ config.admissionFilter ? [config.ingest, config.admissionFilter] : [config.ingest],
194
+ (batchData, actions, ctx) => {
195
+ const f = lastOf(batchData[0], ctx.prevData[0]);
196
+ if (f == null) {
182
197
  actions.emit(null);
183
198
  return;
184
199
  }
185
- }
186
- const deps = config.extractDependencies(f);
187
- cascadeIteration = 0;
188
- processedRoots.delete(f.id);
189
- commitFragment(f);
190
- indexFragment(f, deps);
191
- actions.emit(f);
192
- },
193
- {
194
- name: "extract_op",
195
- describeKind: "derived",
196
- meta: factMeta("extract")
197
- }
198
- );
199
- graph.add(extractOp, { name: "extract_op" });
200
- graph.addDisposer(keepalive(extractOp));
201
- const invalidationDetector = node(
202
- [...shards],
203
- (batchData, actions, ctx) => {
204
- void batchData;
205
- void ctx;
206
- const facts = allFacts();
207
- const index = dependentsIndex.cache;
208
- const out2 = [];
209
- const seen = /* @__PURE__ */ new Set();
210
- for (const f of facts.values()) {
211
- const obsolete = f.validTo !== void 0;
212
- if (!obsolete) continue;
213
- if (processedRoots.has(f.id)) continue;
214
- const dependents = index.get(f.id) ?? [];
215
- for (const dep of dependents) {
216
- const depFact = facts.get(dep);
217
- if (!depFact || depFact.validTo !== void 0) continue;
218
- const k = `${f.id}->${dep}`;
219
- if (seen.has(k)) continue;
220
- seen.add(k);
221
- out2.push({
222
- factId: dep,
223
- rootFactId: f.id,
224
- reason: "obsolete",
225
- // `obsolete` guard above guarantees `f.validTo` is set.
226
- rootValidTo: f.validTo,
227
- iteration: cascadeIteration + 1,
228
- causalReason: `dependentsIndex[${f.id}] \u2192 ${dep} (obsolete: validTo set)`
229
- });
200
+ if (config.admissionFilter) {
201
+ const filter = lastOf(batchData[1], ctx.prevData[1]);
202
+ if (filter && !filter(f)) {
203
+ actions.emit(null);
204
+ return;
205
+ }
230
206
  }
231
- processedRoots.add(f.id);
232
- }
233
- if (out2.length === 0) {
207
+ const deps = config.extractDependencies(f);
234
208
  cascadeIteration = 0;
209
+ processedRoots.delete(f.id);
210
+ commitFragment(f);
211
+ indexFragment(f, deps);
212
+ actions.emit(f);
213
+ },
214
+ {
215
+ name: "extract_op",
216
+ describeKind: "derived",
217
+ meta: factMeta("extract")
235
218
  }
236
- actions.emit(out2);
237
- },
238
- {
239
- name: "invalidation_detector",
240
- describeKind: "derived",
241
- initial: [],
242
- meta: factMeta("invalidation", { cycle: "cascade" })
243
- }
244
- );
245
- graph.add(invalidationDetector, { name: "invalidation_detector" });
246
- graph.addDisposer(keepalive(invalidationDetector));
247
- const cascade = node(
248
- [invalidationDetector],
249
- (batchData, actions, ctx) => {
250
- const evts = lastOf(batchData[0], ctx.prevData[0]) ?? [];
251
- actions.emit(evts);
252
- },
253
- {
254
- name: "cascade",
255
- describeKind: "derived",
256
- initial: [],
257
- meta: factMeta("cascade_topic", { cycle: "cascade" })
258
- }
259
- );
260
- graph.add(cascade, { name: "cascade" });
261
- graph.addDisposer(keepalive(cascade));
262
- const cascadeOverflow = node([], {
263
- initial: null,
264
- name: "cascade_overflow",
265
- describeKind: "state",
266
- meta: factMeta("cascade_overflow")
267
- });
268
- graph.add(cascadeOverflow, { name: "cascade_overflow" });
269
- graph.addDisposer(keepalive(cascadeOverflow));
270
- const cascadeProcessor = node(
271
- [cascade],
272
- (batchData, actions, ctx) => {
273
- const evts = lastOf(batchData[0], ctx.prevData[0]) ?? [];
274
- if (evts.length === 0) {
275
- actions.emit([]);
276
- return;
277
- }
278
- const byId = /* @__PURE__ */ new Map();
279
- for (const e of evts) if (!byId.has(e.factId)) byId.set(e.factId, e);
280
- cascadeIteration += 1;
281
- if (cascadeIteration > maxIterations) {
282
- const sample = [...byId.keys()].slice(0, OVERFLOW_SAMPLE_SIZE);
283
- const rootFactId = evts[0]?.rootFactId ?? "";
284
- cascadeOverflow.emit({
285
- droppedCount: byId.size,
286
- sample,
287
- rootFactId
288
- });
289
- events.append({
290
- action: "overflow",
291
- reason: "cascade",
292
- id: rootFactId,
293
- t_ns: wallClockNs(),
294
- seq: bumpCursor(seqCursor)
295
- });
296
- actions.emit([]);
297
- return;
298
- }
299
- for (const [id, e] of byId) {
300
- replaceFragment(
301
- id,
302
- (prev) => prev.validTo !== void 0 ? prev : { ...prev, validTo: e.rootValidTo }
303
- );
219
+ );
220
+ this.add(extractOp, { name: "extract_op" });
221
+ this.addDisposer(keepalive(extractOp));
222
+ const invalidationDetector = node(
223
+ [...shards],
224
+ (batchData, actions, ctx) => {
225
+ void batchData;
226
+ void ctx;
227
+ const facts = allFacts();
228
+ const index = dependentsIndex.cache;
229
+ const out = [];
230
+ const seen = /* @__PURE__ */ new Set();
231
+ for (const f of facts.values()) {
232
+ const obsolete = f.validTo !== void 0;
233
+ if (!obsolete) continue;
234
+ if (processedRoots.has(f.id)) continue;
235
+ const dependents = index.get(f.id) ?? [];
236
+ for (const dep of dependents) {
237
+ const depFact = facts.get(dep);
238
+ if (!depFact || depFact.validTo !== void 0) continue;
239
+ const k = `${f.id}->${dep}`;
240
+ if (seen.has(k)) continue;
241
+ seen.add(k);
242
+ out.push({
243
+ factId: dep,
244
+ rootFactId: f.id,
245
+ reason: "obsolete",
246
+ // `obsolete` guard above guarantees `f.validTo` is set.
247
+ rootValidTo: f.validTo,
248
+ iteration: cascadeIteration + 1,
249
+ causalReason: `dependentsIndex[${f.id}] \u2192 ${dep} (obsolete: validTo set)`
250
+ });
251
+ }
252
+ processedRoots.add(f.id);
253
+ }
254
+ if (out.length === 0) {
255
+ cascadeIteration = 0;
256
+ }
257
+ actions.emit(out);
258
+ },
259
+ {
260
+ name: "invalidation_detector",
261
+ describeKind: "derived",
262
+ initial: [],
263
+ meta: factMeta("invalidation", { cycle: "cascade" })
304
264
  }
305
- actions.emit([...byId.values()]);
306
- },
307
- {
308
- name: "cascade_processor",
309
- describeKind: "derived",
310
- initial: [],
311
- meta: factMeta("cascade_processor", { cycle: "cascade" })
312
- }
313
- );
314
- graph.add(cascadeProcessor, { name: "cascade_processor" });
315
- graph.addDisposer(keepalive(cascadeProcessor));
316
- const review = node(
317
- [factStore],
318
- (batchData, actions, ctx) => {
319
- const fs = lastOf(batchData[0], ctx.prevData[0]);
320
- if (fs == null) {
321
- actions.emit(null);
322
- return;
265
+ );
266
+ this.add(invalidationDetector, { name: "invalidation_detector" });
267
+ this.addDisposer(keepalive(invalidationDetector));
268
+ const cascade = node(
269
+ [invalidationDetector],
270
+ (batchData, actions, ctx) => {
271
+ const evts = lastOf(batchData[0], ctx.prevData[0]) ?? [];
272
+ actions.emit(evts);
273
+ },
274
+ {
275
+ name: "cascade",
276
+ describeKind: "derived",
277
+ initial: [],
278
+ meta: factMeta("cascade_topic", { cycle: "cascade" })
323
279
  }
324
- for (const f of fs.byId.values()) {
325
- if (f.confidence < reviewThreshold && f.validTo === void 0) {
326
- actions.emit({
327
- factId: f.id,
328
- confidence: f.confidence,
329
- threshold: reviewThreshold
280
+ );
281
+ this.add(cascade, { name: "cascade" });
282
+ this.addDisposer(keepalive(cascade));
283
+ const cascadeOverflow = node([], {
284
+ initial: null,
285
+ name: "cascade_overflow",
286
+ describeKind: "state",
287
+ meta: factMeta("cascade_overflow")
288
+ });
289
+ this.add(cascadeOverflow, { name: "cascade_overflow" });
290
+ this.addDisposer(keepalive(cascadeOverflow));
291
+ const cascadeProcessor = node(
292
+ [cascade],
293
+ (batchData, actions, ctx) => {
294
+ const evts = lastOf(batchData[0], ctx.prevData[0]) ?? [];
295
+ if (evts.length === 0) {
296
+ actions.emit([]);
297
+ return;
298
+ }
299
+ const byId = /* @__PURE__ */ new Map();
300
+ for (const e of evts) if (!byId.has(e.factId)) byId.set(e.factId, e);
301
+ cascadeIteration += 1;
302
+ if (cascadeIteration > maxIterations) {
303
+ const sample = [...byId.keys()].slice(0, OVERFLOW_SAMPLE_SIZE);
304
+ const rootFactId = evts[0]?.rootFactId ?? "";
305
+ cascadeOverflow.emit({
306
+ droppedCount: byId.size,
307
+ sample,
308
+ rootFactId
330
309
  });
310
+ events.append({
311
+ action: "overflow",
312
+ reason: "cascade",
313
+ id: rootFactId,
314
+ t_ns: wallClockNs(),
315
+ seq: bumpCursor(seqCursor)
316
+ });
317
+ actions.emit([]);
331
318
  return;
332
319
  }
320
+ for (const [id, e] of byId) {
321
+ replaceFragment(
322
+ id,
323
+ (prev) => prev.validTo !== void 0 ? prev : { ...prev, validTo: e.rootValidTo }
324
+ );
325
+ }
326
+ actions.emit([...byId.values()]);
327
+ },
328
+ {
329
+ name: "cascade_processor",
330
+ describeKind: "derived",
331
+ initial: [],
332
+ meta: factMeta("cascade_processor", { cycle: "cascade" })
333
333
  }
334
- actions.emit(null);
335
- },
336
- {
337
- name: "review",
338
- describeKind: "derived",
339
- initial: null,
340
- meta: factMeta("review"),
341
- // F10a: dedupe on the requested factId (null === no request) so a
342
- // stable low-confidence fact does not re-emit a review every wave.
343
- equals: (a, b) => (a?.factId ?? null) === (b?.factId ?? null)
344
- }
345
- );
346
- graph.add(review, { name: "review" });
347
- graph.addDisposer(keepalive(review));
348
- if (config.outcome) {
349
- const outcomeProcessor = node(
350
- config.scoring ? [config.outcome, config.scoring] : [config.outcome],
334
+ );
335
+ this.add(cascadeProcessor, { name: "cascade_processor" });
336
+ this.addDisposer(keepalive(cascadeProcessor));
337
+ const review = node(
338
+ [factStore],
351
339
  (batchData, actions, ctx) => {
352
- const sig = lastOf(batchData[0], ctx.prevData[0]);
353
- if (sig == null) {
340
+ const fs = lastOf(batchData[0], ctx.prevData[0]);
341
+ if (fs == null) {
354
342
  actions.emit(null);
355
343
  return;
356
344
  }
357
- replaceFragment(sig.factId, (prev) => {
358
- let nextConf = prev.confidence;
359
- if (config.scoring) {
360
- const policy = lastOf(batchData[1], ctx.prevData[1]);
361
- if (policy) {
362
- nextConf = policy(prev, makeReadHandle(allFacts()));
363
- }
364
- } else {
365
- nextConf = Math.max(0, Math.min(1, prev.confidence + sig.reward));
345
+ for (const f of fs.byId.values()) {
346
+ if (f.confidence < reviewThreshold && f.validTo === void 0) {
347
+ actions.emit({
348
+ factId: f.id,
349
+ confidence: f.confidence,
350
+ threshold: reviewThreshold
351
+ });
352
+ return;
366
353
  }
367
- return { ...prev, confidence: nextConf };
368
- });
369
- actions.emit(sig);
354
+ }
355
+ actions.emit(null);
370
356
  },
371
357
  {
372
- name: "outcome_processor",
358
+ name: "review",
373
359
  describeKind: "derived",
374
360
  initial: null,
375
- meta: factMeta("outcome")
361
+ meta: factMeta("review"),
362
+ // F10a: dedupe on the requested factId (null === no request) so a
363
+ // stable low-confidence fact does not re-emit a review every wave.
364
+ equals: (a, b) => (a?.factId ?? null) === (b?.factId ?? null)
376
365
  }
377
366
  );
378
- graph.add(outcomeProcessor, { name: "outcome_processor" });
379
- graph.addDisposer(keepalive(outcomeProcessor));
380
- }
381
- const answer = node(
382
- config.query ? [config.query, factStore] : [factStore],
383
- (batchData, actions, ctx) => {
384
- if (!config.query) {
385
- actions.emit(null);
386
- return;
387
- }
388
- const q = lastOf(batchData[0], ctx.prevData[0]);
389
- const fs = lastOf(batchData[1], ctx.prevData[1]);
390
- if (q == null) {
391
- actions.emit(null);
392
- return;
393
- }
394
- const store = fs ?? emptyStore();
395
- let results = [...store.byId.values()].filter((f) => {
396
- if (q.tags && q.tags.length > 0 && !q.tags.some((t) => f.tags.includes(t))) {
397
- return false;
367
+ this.add(review, { name: "review" });
368
+ this.addDisposer(keepalive(review));
369
+ if (config.outcome) {
370
+ const outcomeProcessor = node(
371
+ config.scoring ? [config.outcome, config.scoring] : [config.outcome],
372
+ (batchData, actions, ctx) => {
373
+ const sig = lastOf(batchData[0], ctx.prevData[0]);
374
+ if (sig == null) {
375
+ actions.emit(null);
376
+ return;
377
+ }
378
+ replaceFragment(sig.factId, (prev) => {
379
+ let nextConf = prev.confidence;
380
+ if (config.scoring) {
381
+ const policy = lastOf(batchData[1], ctx.prevData[1]);
382
+ if (policy) {
383
+ nextConf = policy(prev, makeReadHandle(allFacts()));
384
+ }
385
+ } else {
386
+ nextConf = Math.max(0, Math.min(1, prev.confidence + sig.reward));
387
+ }
388
+ return { ...prev, confidence: nextConf };
389
+ });
390
+ actions.emit(sig);
391
+ },
392
+ {
393
+ name: "outcome_processor",
394
+ describeKind: "derived",
395
+ initial: null,
396
+ meta: factMeta("outcome")
398
397
  }
399
- if (q.minConfidence !== void 0 && f.confidence < q.minConfidence) return false;
400
- if (!currentlyValid(f, q.asOf)) return false;
401
- return true;
402
- });
403
- results.sort((a, b) => b.confidence - a.confidence || Number(b.t_ns - a.t_ns));
404
- if (q.limit !== void 0) results = results.slice(0, Math.max(0, q.limit));
405
- actions.emit({ query: q, results });
406
- },
407
- {
408
- name: "answer",
409
- describeKind: "derived",
410
- initial: null,
411
- meta: factMeta("query", { role: "output" })
398
+ );
399
+ this.add(outcomeProcessor, { name: "outcome_processor" });
400
+ this.addDisposer(keepalive(outcomeProcessor));
412
401
  }
413
- );
414
- graph.add(answer, { name: "answer" });
415
- graph.addDisposer(keepalive(answer));
416
- const consolidated = node(
417
- config.consolidateTrigger ? [config.consolidateTrigger] : [],
418
- (batchData, actions, ctx) => {
419
- void batchData;
420
- void ctx;
421
- if (!config.consolidateTrigger || !config.consolidate) {
422
- actions.emit([]);
423
- return;
424
- }
425
- const fragments = config.consolidate(makeReadHandle(allFacts()));
426
- for (const f of fragments) {
427
- const deps = config.extractDependencies(f);
428
- processedRoots.delete(f.id);
429
- commitFragment(f);
430
- indexFragment(f, deps);
431
- events.append({
432
- action: "consolidate",
433
- id: f.id,
434
- t_ns: wallClockNs(),
435
- seq: bumpCursor(seqCursor)
402
+ const answer = node(
403
+ config.query ? [config.query, factStore] : [factStore],
404
+ (batchData, actions, ctx) => {
405
+ if (!config.query) {
406
+ actions.emit(null);
407
+ return;
408
+ }
409
+ const q = lastOf(batchData[0], ctx.prevData[0]);
410
+ const fs = lastOf(batchData[1], ctx.prevData[1]);
411
+ if (q == null) {
412
+ actions.emit(null);
413
+ return;
414
+ }
415
+ const store = fs ?? emptyStore();
416
+ let results = [...store.byId.values()].filter((f) => {
417
+ if (q.tags && q.tags.length > 0 && !q.tags.some((t) => f.tags.includes(t))) {
418
+ return false;
419
+ }
420
+ if (q.minConfidence !== void 0 && f.confidence < q.minConfidence) return false;
421
+ if (!currentlyValid(f, q.asOf)) return false;
422
+ return true;
436
423
  });
424
+ results.sort((a, b) => b.confidence - a.confidence || Number(b.t_ns - a.t_ns));
425
+ if (q.limit !== void 0) results = results.slice(0, Math.max(0, q.limit));
426
+ actions.emit({ query: q, results });
427
+ },
428
+ {
429
+ name: "answer",
430
+ describeKind: "derived",
431
+ initial: null,
432
+ meta: factMeta("query", { role: "output" })
437
433
  }
438
- actions.emit(fragments);
439
- },
440
- {
441
- name: "consolidated",
442
- describeKind: "derived",
443
- initial: [],
444
- meta: factMeta("consolidator")
445
- }
446
- );
447
- graph.add(consolidated, { name: "consolidated" });
448
- graph.addDisposer(keepalive(consolidated));
449
- if (config.decayTrigger) {
450
- const decayProcessor = node(
451
- config.decay ? [config.decayTrigger, config.decay] : [config.decayTrigger],
434
+ );
435
+ this.add(answer, { name: "answer" });
436
+ this.addDisposer(keepalive(answer));
437
+ const consolidated = node(
438
+ config.consolidateTrigger ? [config.consolidateTrigger] : [],
452
439
  (batchData, actions, ctx) => {
453
- const policy = config.decay ? lastOf(batchData[1], ctx.prevData[1]) : void 0;
454
- if (!policy) {
440
+ void batchData;
441
+ void ctx;
442
+ if (!config.consolidateTrigger || !config.consolidate) {
455
443
  actions.emit([]);
456
444
  return;
457
445
  }
458
- const now = BigInt(monotonicNs());
459
- const changes = [];
460
- for (const f of allFacts().values()) {
461
- if (f.validTo !== void 0) continue;
462
- const ageNs = now - f.t_ns;
463
- const raw = policy(f.confidence, ageNs);
464
- if (!Number.isFinite(raw)) continue;
465
- const next = raw < 0 ? 0 : raw > 1 ? 1 : raw;
466
- if (next === f.confidence) continue;
467
- changes.push({ id: f.id, next });
446
+ const fragments = config.consolidate(makeReadHandle(allFacts()));
447
+ for (const f of fragments) {
448
+ const deps = config.extractDependencies(f);
449
+ processedRoots.delete(f.id);
450
+ commitFragment(f);
451
+ indexFragment(f, deps);
452
+ events.append({
453
+ action: "consolidate",
454
+ id: f.id,
455
+ t_ns: wallClockNs(),
456
+ seq: bumpCursor(seqCursor)
457
+ });
468
458
  }
469
- const decayed = [];
470
- for (const { id, next } of changes) {
471
- const idx = findShardOf(id);
472
- const fs = idx < 0 ? void 0 : shards[idx].cache;
473
- const live = fs?.byId.get(id);
474
- if (!live || live.validTo !== void 0) continue;
475
- replaceFragment(
476
- id,
477
- (prev) => prev.validTo !== void 0 ? prev : { ...prev, confidence: next }
478
- );
479
- decayed.push({ ...live, confidence: next });
459
+ actions.emit(fragments);
460
+ },
461
+ {
462
+ name: "consolidated",
463
+ describeKind: "derived",
464
+ initial: [],
465
+ // Inspection completeness (COMPOSITION-GUIDE §24 "make the
466
+ // invisible edge visible"): `consolidated` write-backs feed the
467
+ // bounded cascade store (commit shard emit
468
+ // `invalidationDetector`) exactly like `cascadeProcessor`, but
469
+ // is NOT a cascade-cycle node. `feeds:"cascade"` surfaces it as
470
+ // a cascade-store mutator in `describe()`/`explain()`;
471
+ // `drivesRoot:false` — by the consolidator contract successors
472
+ // are fresh live facts (no `validTo`), so on the contract path
473
+ // they do not root the cascade. (An out-of-contract `consolidate`
474
+ // that emits a `validTo`-set fragment WOULD root it via the
475
+ // detector — the tag reflects the documented contract, not a
476
+ // structural impossibility. Contrast `decay_processor` below,
477
+ // whose `drivesRoot:false` IS structurally provable.)
478
+ meta: factMeta("consolidator", { feeds: "cascade", drivesRoot: false })
479
+ }
480
+ );
481
+ this.add(consolidated, { name: "consolidated" });
482
+ this.addDisposer(keepalive(consolidated));
483
+ if (config.decayTrigger) {
484
+ const decayProcessor = node(
485
+ config.decay ? [config.decayTrigger, config.decay] : [config.decayTrigger],
486
+ (batchData, actions, ctx) => {
487
+ const policy = config.decay ? lastOf(batchData[1], ctx.prevData[1]) : void 0;
488
+ if (!policy) {
489
+ actions.emit([]);
490
+ return;
491
+ }
492
+ const now = BigInt(monotonicNs());
493
+ const changes = [];
494
+ for (const f of allFacts().values()) {
495
+ if (f.validTo !== void 0) continue;
496
+ const ageNs = now - f.t_ns;
497
+ const raw = policy(f.confidence, ageNs);
498
+ if (!Number.isFinite(raw)) continue;
499
+ const next = raw < 0 ? 0 : raw > 1 ? 1 : raw;
500
+ if (next === f.confidence) continue;
501
+ changes.push({ id: f.id, next });
502
+ }
503
+ const decayed = [];
504
+ for (const { id, next } of changes) {
505
+ const idx = findShardOf(id);
506
+ const fs = idx < 0 ? void 0 : shards[idx].cache;
507
+ const live = fs?.byId.get(id);
508
+ if (!live || live.validTo !== void 0) continue;
509
+ replaceFragment(
510
+ id,
511
+ (prev) => prev.validTo !== void 0 ? prev : { ...prev, confidence: next }
512
+ );
513
+ decayed.push({ ...live, confidence: next });
514
+ }
515
+ if (decayed.length > 0) {
516
+ events.append({
517
+ action: "decay",
518
+ t_ns: wallClockNs(),
519
+ seq: bumpCursor(seqCursor),
520
+ count: decayed.length
521
+ });
522
+ }
523
+ actions.emit(decayed);
524
+ },
525
+ {
526
+ name: "decay_processor",
527
+ describeKind: "derived",
528
+ initial: [],
529
+ // Inspection completeness (same convention as `consolidated`
530
+ // above — kept uniform): `decay_processor` write-backs feed
531
+ // the cascade store via `replaceFragment` → shard emit →
532
+ // `invalidationDetector`. `feeds:"cascade"` surfaces it as a
533
+ // cascade-store mutator; `drivesRoot:false` — decay only
534
+ // mutates `confidence`, never `validTo`, and the detector
535
+ // roots on `validTo` only, so it provably cannot root.
536
+ meta: factMeta("decay", { feeds: "cascade", drivesRoot: false })
480
537
  }
481
- if (decayed.length > 0) {
538
+ );
539
+ this.add(decayProcessor, { name: "decay_processor" });
540
+ this.addDisposer(keepalive(decayProcessor));
541
+ }
542
+ const ingestAudit = node(
543
+ [extractOp],
544
+ (batchData, actions, ctx) => {
545
+ const f = lastOf(batchData[0], ctx.prevData[0]);
546
+ if (f != null) {
482
547
  events.append({
483
- action: "decay",
548
+ action: "ingest",
549
+ id: f.id,
484
550
  t_ns: wallClockNs(),
485
551
  seq: bumpCursor(seqCursor)
486
552
  });
553
+ ingestLog?.append(f);
487
554
  }
488
- actions.emit(decayed);
555
+ actions.emit(f ?? null);
489
556
  },
490
557
  {
491
- name: "decay_processor",
558
+ name: "_ingest_audit",
492
559
  describeKind: "derived",
493
- initial: [],
494
- meta: factMeta("decay")
560
+ initial: null,
561
+ meta: factMeta("audit")
495
562
  }
496
563
  );
497
- graph.add(decayProcessor, { name: "decay_processor" });
498
- graph.addDisposer(keepalive(decayProcessor));
564
+ this.add(ingestAudit, { name: "_ingest_audit" });
565
+ this.addDisposer(keepalive(ingestAudit));
566
+ this.shards = shards;
567
+ this.factStore = factStore;
568
+ this.dependentsIndex = dependentsIndex;
569
+ this.answer = answer;
570
+ this.cascade = cascade;
571
+ this.cascadeOverflow = cascadeOverflow;
572
+ this.review = review;
573
+ this.consolidated = consolidated;
574
+ this.events = events;
575
+ if (ingestLog) this.ingestLog = ingestLog;
499
576
  }
500
- const ingestAudit = node(
501
- [extractOp],
502
- (batchData, actions, ctx) => {
503
- const f = lastOf(batchData[0], ctx.prevData[0]);
504
- if (f != null) {
505
- events.append({
506
- action: "ingest",
507
- id: f.id,
508
- t_ns: wallClockNs(),
509
- seq: bumpCursor(seqCursor)
510
- });
511
- ingestLog?.append(f);
512
- }
513
- actions.emit(f ?? null);
514
- },
515
- {
516
- name: "_ingest_audit",
517
- describeKind: "derived",
518
- initial: null,
519
- meta: factMeta("audit")
520
- }
521
- );
522
- graph.add(ingestAudit, { name: "_ingest_audit" });
523
- graph.addDisposer(keepalive(ingestAudit));
524
- function itemNode(id) {
577
+ // ── itemNode reactive read ───────────────────────────────────────────
578
+ /** Reactive read: a single fact by id (SENTINEL until the fact exists). */
579
+ itemNode(id) {
525
580
  return node(
526
- [factStore],
581
+ [this.factStore],
527
582
  (batchData, actions, ctx) => {
528
583
  const fs = lastOf(batchData[0], ctx.prevData[0]);
529
584
  actions.emit(fs?.byId.get(id));
@@ -535,20 +590,9 @@ function reactiveFactStore(config) {
535
590
  }
536
591
  );
537
592
  }
538
- const out = Object.assign(graph, {
539
- shards,
540
- factStore,
541
- dependentsIndex,
542
- answer,
543
- cascade,
544
- cascadeOverflow,
545
- review,
546
- consolidated,
547
- events,
548
- ...ingestLog ? { ingestLog } : {},
549
- itemNode
550
- });
551
- return out;
593
+ };
594
+ function reactiveFactStore(config) {
595
+ return new ReactiveFactStoreGraph(config);
552
596
  }
553
597
 
554
598
  // src/utils/memory/persistent-fact-store.ts
@@ -972,6 +1016,52 @@ function shardByTenant(tenantOf, opts = {}) {
972
1016
  return { shardBy: (f) => tenantOf(f), shardCount };
973
1017
  }
974
1018
 
1019
+ // src/utils/memory/simple-fact-store.ts
1020
+ import { monotonicNs as monotonicNs2, node as node9 } from "@graphrefly/pure-ts/core";
1021
+ var DEFAULT_DECAY_HALF_LIFE_NS = 604800000000000n;
1022
+ var DEFAULT_DECAY_PERIOD_MS = 36e5;
1023
+ function simpleFactStore(opts = {}) {
1024
+ const ingest = node9([], { initial: void 0 });
1025
+ const extractDependencies = opts.extractDependencies ?? (() => []);
1026
+ const consolidationCfg = opts.consolidate ? consolidationRem(opts.consolidate) : void 0;
1027
+ const baseCfg = {
1028
+ ingest,
1029
+ extractDependencies,
1030
+ ...consolidationCfg ?? {}
1031
+ };
1032
+ const mem = opts.storage ? persistentReactiveFactStore({
1033
+ ...baseCfg,
1034
+ storage: opts.storage,
1035
+ ...opts.persistName !== void 0 ? { persistName: opts.persistName } : {},
1036
+ ...opts.codec !== void 0 ? { codec: opts.codec } : {}
1037
+ }) : reactiveFactStore(baseCfg);
1038
+ if (opts.decay !== false) {
1039
+ const d = opts.decay ?? {};
1040
+ decayExponential(mem, ingest, {
1041
+ ...d,
1042
+ halfLifeNs: d.halfLifeNs ?? DEFAULT_DECAY_HALF_LIFE_NS,
1043
+ periodMs: d.periodMs ?? DEFAULT_DECAY_PERIOD_MS
1044
+ });
1045
+ }
1046
+ const remember = (id, payload, ro) => {
1047
+ const fragment = {
1048
+ id,
1049
+ payload,
1050
+ t_ns: BigInt(monotonicNs2()),
1051
+ confidence: ro?.confidence ?? 1,
1052
+ tags: ro?.tags ?? [],
1053
+ sources: ro?.sources ?? [],
1054
+ ...ro?.validTo !== void 0 ? { validTo: ro.validTo } : {},
1055
+ ...ro?.validFrom !== void 0 ? { validFrom: ro.validFrom } : {},
1056
+ ...ro?.provenance !== void 0 ? { provenance: ro.provenance } : {}
1057
+ };
1058
+ ingest.emit(fragment);
1059
+ };
1060
+ const out = mem;
1061
+ out.remember = remember;
1062
+ return out;
1063
+ }
1064
+
975
1065
  // src/utils/memory/index.ts
976
1066
  var NS_PER_SEC = 1e9;
977
1067
  function memoryMeta(kind, extra) {
@@ -979,7 +1069,7 @@ function memoryMeta(kind, extra) {
979
1069
  }
980
1070
  function toNode(v, name) {
981
1071
  if (v instanceof NodeImpl) return v;
982
- return node9([], { initial: v, ...name ? { name } : void 0 });
1072
+ return node10([], { initial: v, ...name ? { name } : void 0 });
983
1073
  }
984
1074
  function ageSeconds(now, lastNs) {
985
1075
  return (now - lastNs) / NS_PER_SEC;
@@ -1039,7 +1129,7 @@ function collection(name, opts = {}) {
1039
1129
  return scoreInput;
1040
1130
  };
1041
1131
  const graph = new Graph2(name);
1042
- const retentionScore = (_k, v) => ranked ? decay(v.baseScore, ageSeconds(monotonicNs2(), v.lastAccessNs), decayRate, minScore) : v.lastAccessNs;
1132
+ const retentionScore = (_k, v) => ranked ? decay(v.baseScore, ageSeconds(monotonicNs3(), v.lastAccessNs), decayRate, minScore) : v.lastAccessNs;
1043
1133
  const items = reactiveMap({
1044
1134
  name: "items",
1045
1135
  ...maxSize !== void 0 ? { retention: { score: retentionScore, maxSize } } : {}
@@ -1049,15 +1139,15 @@ function collection(name, opts = {}) {
1049
1139
  if (ranked && decayRate > 0) {
1050
1140
  const intervalMs = opts.refreshIntervalMs ?? Math.max(1, 1e3 * Math.LN2 / (10 * decayRate));
1051
1141
  const tickCounter = fromTimer3(intervalMs, { period: intervalMs });
1052
- refreshTick = node9(
1142
+ refreshTick = node10(
1053
1143
  [tickCounter],
1054
1144
  (_batchData, actions) => {
1055
- actions.emit(monotonicNs2());
1145
+ actions.emit(monotonicNs3());
1056
1146
  },
1057
1147
  {
1058
1148
  name: "refresh_tick_ns",
1059
1149
  describeKind: "derived",
1060
- initial: monotonicNs2(),
1150
+ initial: monotonicNs3(),
1061
1151
  meta: memoryMeta("clock")
1062
1152
  }
1063
1153
  );
@@ -1068,7 +1158,7 @@ function collection(name, opts = {}) {
1068
1158
  const rankedDeps = [items.entries];
1069
1159
  if (refreshTick) rankedDeps.push(refreshTick);
1070
1160
  if (scoreNode) rankedDeps.push(scoreNode);
1071
- rankedNode = node9(
1161
+ rankedNode = node10(
1072
1162
  rankedDeps,
1073
1163
  (batchData, actions, ctx) => {
1074
1164
  const values = batchData.map(
@@ -1078,9 +1168,9 @@ function collection(name, opts = {}) {
1078
1168
  let now;
1079
1169
  if (refreshTick) {
1080
1170
  const tickValue = values[1];
1081
- now = typeof tickValue === "number" ? tickValue : monotonicNs2();
1171
+ now = typeof tickValue === "number" ? tickValue : monotonicNs3();
1082
1172
  } else {
1083
- now = monotonicNs2();
1173
+ now = monotonicNs3();
1084
1174
  }
1085
1175
  if (!snapshot || snapshot.size === 0) {
1086
1176
  actions.emit([]);
@@ -1105,7 +1195,7 @@ function collection(name, opts = {}) {
1105
1195
  );
1106
1196
  graph.add(rankedNode, { name: "ranked" });
1107
1197
  } else {
1108
- rankedNode = node9([], {
1198
+ rankedNode = node10([], {
1109
1199
  initial: [],
1110
1200
  name: "ranked",
1111
1201
  describeKind: "state",
@@ -1113,7 +1203,7 @@ function collection(name, opts = {}) {
1113
1203
  });
1114
1204
  graph.add(rankedNode, { name: "ranked" });
1115
1205
  }
1116
- const size = node9(
1206
+ const size = node10(
1117
1207
  [items.entries],
1118
1208
  (batchData, actions, ctx) => {
1119
1209
  const data = batchData.map(
@@ -1138,7 +1228,7 @@ function collection(name, opts = {}) {
1138
1228
  });
1139
1229
  const seqCursor = registerCursor(graph, "seq", 0);
1140
1230
  const upsertImpl = (id, value, _opts) => {
1141
- const now = monotonicNs2();
1231
+ const now = monotonicNs3();
1142
1232
  const prev = items.get(id);
1143
1233
  const baseScore = _opts?.score ?? readScoreFn()(value);
1144
1234
  items.set(id, {
@@ -1194,7 +1284,7 @@ function collection(name, opts = {}) {
1194
1284
  });
1195
1285
  function itemNode(id) {
1196
1286
  const idN = toNode(id, "id");
1197
- return node9(
1287
+ return node10(
1198
1288
  [items.entries, idN],
1199
1289
  (batchData, actions, ctx) => {
1200
1290
  const data = batchData.map(
@@ -1212,7 +1302,7 @@ function collection(name, opts = {}) {
1212
1302
  }
1213
1303
  function hasNode(id) {
1214
1304
  const idN = toNode(id, "id");
1215
- return node9(
1305
+ return node10(
1216
1306
  [items.entries, idN],
1217
1307
  (batchData, actions, ctx) => {
1218
1308
  const data = batchData.map(
@@ -1324,7 +1414,7 @@ function vectorIndex(opts = {}) {
1324
1414
  id,
1325
1415
  vector: [...vector],
1326
1416
  ...copiedMeta !== void 0 ? { meta: copiedMeta } : {},
1327
- upsertedAtNs: monotonicNs2()
1417
+ upsertedAtNs: monotonicNs3()
1328
1418
  };
1329
1419
  entries.set(id, record);
1330
1420
  };
@@ -1380,7 +1470,7 @@ function vectorIndex(opts = {}) {
1380
1470
  });
1381
1471
  function searchNode(query, k = 5) {
1382
1472
  const kN = toNode(k, "k");
1383
- return node9(
1473
+ return node10(
1384
1474
  [entries.entries, query, kN],
1385
1475
  (batchData, actions, ctx) => {
1386
1476
  const values = batchData.map(
@@ -1496,7 +1586,7 @@ function knowledgeGraph(name, opts = {}) {
1496
1586
  });
1497
1587
  graph.add(entitiesMap.entries, { name: "entities" });
1498
1588
  graph.add(edgesMap.entries, { name: "edges" });
1499
- const adjacencyOut = node9(
1589
+ const adjacencyOut = node10(
1500
1590
  [edgesMap.entries],
1501
1591
  (batchData, actions, ctx) => {
1502
1592
  const data = batchData.map(
@@ -1513,7 +1603,7 @@ function knowledgeGraph(name, opts = {}) {
1513
1603
  meta: memoryMeta("adjacency_out")
1514
1604
  }
1515
1605
  );
1516
- const adjacencyIn = node9(
1606
+ const adjacencyIn = node10(
1517
1607
  [edgesMap.entries],
1518
1608
  (batchData, actions, ctx) => {
1519
1609
  const data = batchData.map(
@@ -1534,7 +1624,7 @@ function knowledgeGraph(name, opts = {}) {
1534
1624
  graph.add(adjacencyIn, { name: "adjacencyIn" });
1535
1625
  graph.addDisposer(keepalive6(adjacencyOut));
1536
1626
  graph.addDisposer(keepalive6(adjacencyIn));
1537
- const entityCount = node9(
1627
+ const entityCount = node10(
1538
1628
  [entitiesMap.entries],
1539
1629
  (batchData, actions, ctx) => {
1540
1630
  const data = batchData.map(
@@ -1545,7 +1635,7 @@ function knowledgeGraph(name, opts = {}) {
1545
1635
  },
1546
1636
  { name: "entityCount", describeKind: "derived", initial: 0, meta: memoryMeta("entity_count") }
1547
1637
  );
1548
- const edgeCount = node9(
1638
+ const edgeCount = node10(
1549
1639
  [edgesMap.entries],
1550
1640
  (batchData, actions, ctx) => {
1551
1641
  const data = batchData.map(
@@ -1677,7 +1767,7 @@ function knowledgeGraph(name, opts = {}) {
1677
1767
  const idN = toNode(id, "id");
1678
1768
  const relN = relation !== void 0 ? toNode(relation, "relation") : void 0;
1679
1769
  const deps = relN ? [adjacencyOut, adjacencyIn, idN, relN] : [adjacencyOut, adjacencyIn, idN];
1680
- return node9(
1770
+ return node10(
1681
1771
  deps,
1682
1772
  (batchData, actions, ctx) => {
1683
1773
  const values = batchData.map(
@@ -1745,6 +1835,7 @@ function knowledgeGraph(name, opts = {}) {
1745
1835
  }
1746
1836
 
1747
1837
  export {
1838
+ ReactiveFactStoreGraph,
1748
1839
  reactiveFactStore,
1749
1840
  persistentReactiveFactStore,
1750
1841
  admissionLlmJudge,
@@ -1755,9 +1846,12 @@ export {
1755
1846
  invalidationTracer,
1756
1847
  scoringByOutcome,
1757
1848
  shardByTenant,
1849
+ DEFAULT_DECAY_HALF_LIFE_NS,
1850
+ DEFAULT_DECAY_PERIOD_MS,
1851
+ simpleFactStore,
1758
1852
  cosineSimilarity,
1759
1853
  collection,
1760
1854
  vectorIndex,
1761
1855
  knowledgeGraph
1762
1856
  };
1763
- //# sourceMappingURL=chunk-7AVQIGF6.js.map
1857
+ //# sourceMappingURL=chunk-K4ZYJ4EM.js.map