@remnic/core 9.3.719 → 9.3.721

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 (387) hide show
  1. package/dist/access-boundary.d.ts +10 -7
  2. package/dist/access-boundary.js +37 -34
  3. package/dist/access-cli.js +71 -66
  4. package/dist/access-cli.js.map +1 -1
  5. package/dist/access-http.d.ts +11 -8
  6. package/dist/access-http.js +44 -41
  7. package/dist/access-mcp.d.ts +11 -8
  8. package/dist/access-mcp.js +40 -37
  9. package/dist/access-operations-batch.js +38 -35
  10. package/dist/access-operations.d.ts +11 -8
  11. package/dist/access-operations.js +39 -36
  12. package/dist/access-schema.d.ts +19 -19
  13. package/dist/access-schema.js +3 -3
  14. package/dist/{access-service-CZbnt0t2.d.ts → access-service-Dj3I-VBS.d.ts} +3 -3
  15. package/dist/access-service.d.ts +11 -8
  16. package/dist/access-service.js +36 -33
  17. package/dist/access-surface-catalog.d.ts +10 -7
  18. package/dist/action-confidence.d.ts +1 -1
  19. package/dist/active-memory-bridge.d.ts +1 -1
  20. package/dist/active-recall.d.ts +1 -1
  21. package/dist/active-recall.js +4 -2
  22. package/dist/active-recall.js.map +1 -1
  23. package/dist/adapters/index.js +7 -7
  24. package/dist/adapters/registry.js +3 -3
  25. package/dist/{auto-sync-24NNS3GU.js → auto-sync-F3KCSQFJ.js} +5 -5
  26. package/dist/behavior-learner.d.ts +1 -1
  27. package/dist/behavior-signals.d.ts +1 -1
  28. package/dist/bootstrap.d.ts +11 -8
  29. package/dist/briefing.d.ts +1 -1
  30. package/dist/briefing.js +12 -10
  31. package/dist/buffer-surprise-report.d.ts +1 -1
  32. package/dist/buffer.d.ts +1 -1
  33. package/dist/calibration.d.ts +1 -1
  34. package/dist/calibration.js +2 -2
  35. package/dist/capabilities.d.ts +30 -2
  36. package/dist/capabilities.js +3 -1
  37. package/dist/{catalog-CEYlMs9X.d.ts → catalog-F-OEVbRQ.d.ts} +1 -1
  38. package/dist/causal-behavior.d.ts +1 -1
  39. package/dist/causal-behavior.js +2 -2
  40. package/dist/causal-chain.js +2 -2
  41. package/dist/causal-consolidation.d.ts +1 -1
  42. package/dist/causal-consolidation.js +15 -13
  43. package/dist/causal-consolidation.js.map +1 -1
  44. package/dist/causal-retrieval.js +2 -2
  45. package/dist/causal-trajectory-graph.d.ts +4 -0
  46. package/dist/causal-trajectory-graph.js +2 -2
  47. package/dist/causal-trajectory.js +1 -1
  48. package/dist/{chunk-TH7WMHVK.js → chunk-3NN73CM7.js} +2 -2
  49. package/dist/{chunk-A5TUPZ3N.js → chunk-4SF232E6.js} +60 -30
  50. package/dist/chunk-4SF232E6.js.map +1 -0
  51. package/dist/{chunk-DGTDRIUO.js → chunk-4T3BMHKD.js} +2 -2
  52. package/dist/{chunk-EH27LHML.js → chunk-5O42UKXI.js} +2 -2
  53. package/dist/{chunk-KRGC4EDX.js → chunk-6FAMMBQ3.js} +2 -2
  54. package/dist/{chunk-FEB7W5JG.js → chunk-6OIDDKAA.js} +27 -6
  55. package/dist/chunk-6OIDDKAA.js.map +1 -0
  56. package/dist/{chunk-67HG7KFE.js → chunk-6YKIYQU7.js} +2 -2
  57. package/dist/{chunk-LXSG4L55.js → chunk-7CTNUVHZ.js} +2 -2
  58. package/dist/{chunk-UCJ5CDTK.js → chunk-A5XNBK2D.js} +22 -14
  59. package/dist/chunk-A5XNBK2D.js.map +1 -0
  60. package/dist/{chunk-7EEVXW7X.js → chunk-A7TJZLPK.js} +2 -2
  61. package/dist/{chunk-P5IPFZVH.js → chunk-B7WISIAJ.js} +5 -5
  62. package/dist/chunk-BCDKBDSY.js +173 -0
  63. package/dist/chunk-BCDKBDSY.js.map +1 -0
  64. package/dist/{chunk-JAKYZGV4.js → chunk-BXEOHV7K.js} +3 -3
  65. package/dist/chunk-C4X4QGIO.js +60 -0
  66. package/dist/chunk-C4X4QGIO.js.map +1 -0
  67. package/dist/{chunk-S3OPGGZB.js → chunk-D7EBTQ24.js} +2 -2
  68. package/dist/{chunk-IO5NQEGZ.js → chunk-DKT3X5C3.js} +2 -2
  69. package/dist/{chunk-PBH4JUAB.js → chunk-DVRHLN2O.js} +18 -3
  70. package/dist/chunk-DVRHLN2O.js.map +1 -0
  71. package/dist/{chunk-S35FX6MU.js → chunk-EBDA5TYA.js} +2 -2
  72. package/dist/{chunk-2PTBLS2S.js → chunk-EDOQM4E4.js} +2 -2
  73. package/dist/{chunk-YTZLBBEB.js → chunk-EEJMD2L2.js} +3 -3
  74. package/dist/{chunk-YGKUAX2B.js → chunk-EHQ5HO5T.js} +11 -11
  75. package/dist/chunk-EHQ5HO5T.js.map +1 -0
  76. package/dist/{chunk-JIZGYQYH.js → chunk-EM2LDXJ2.js} +4 -4
  77. package/dist/{chunk-YYOK4GVF.js → chunk-EVPNWN7Z.js} +10 -10
  78. package/dist/{chunk-HLIR3RJ7.js → chunk-EXUCUPV2.js} +2 -2
  79. package/dist/{chunk-F3PVXKHP.js → chunk-GDUOCHCD.js} +27 -21
  80. package/dist/chunk-GDUOCHCD.js.map +1 -0
  81. package/dist/{chunk-L5ASZA2F.js → chunk-GJSL4WG2.js} +17 -2
  82. package/dist/chunk-GJSL4WG2.js.map +1 -0
  83. package/dist/{chunk-2SXMCQEQ.js → chunk-GZQXOQQ2.js} +3 -3
  84. package/dist/{chunk-2KAYTPPT.js → chunk-HCVWF657.js} +12 -12
  85. package/dist/{chunk-N3DUH6OO.js → chunk-ILFSEIRT.js} +1 -1
  86. package/dist/chunk-ILFSEIRT.js.map +1 -0
  87. package/dist/{chunk-4KZGLDJJ.js → chunk-J3JPB7ZI.js} +2 -2
  88. package/dist/{chunk-3JJWNZTT.js → chunk-L2B3EE7B.js} +102 -42
  89. package/dist/chunk-L2B3EE7B.js.map +1 -0
  90. package/dist/{chunk-NFRBADNR.js → chunk-LA3HZKES.js} +610 -369
  91. package/dist/chunk-LA3HZKES.js.map +1 -0
  92. package/dist/{chunk-LQ6JI4VH.js → chunk-LD53WPMU.js} +4 -4
  93. package/dist/{chunk-URM3HW2R.js → chunk-LLUGAZ3B.js} +2 -2
  94. package/dist/{chunk-RVLB4E6N.js → chunk-MDBZTH7N.js} +5 -32
  95. package/dist/chunk-MDBZTH7N.js.map +1 -0
  96. package/dist/{chunk-U5H7WHTS.js → chunk-N77ELJ2D.js} +4 -4
  97. package/dist/{chunk-2VHMVWPU.js → chunk-NXRUUDUF.js} +2 -2
  98. package/dist/{chunk-DIFU33GU.js → chunk-OHTOPFRF.js} +2 -2
  99. package/dist/{chunk-74MYJSKB.js → chunk-ORAK3LNZ.js} +2 -2
  100. package/dist/{chunk-REWQAFSA.js → chunk-PD42EYQN.js} +2 -2
  101. package/dist/chunk-PHK3HARR.js +32 -0
  102. package/dist/chunk-PHK3HARR.js.map +1 -0
  103. package/dist/{chunk-773DPI6T.js → chunk-PKMQK2WY.js} +4 -4
  104. package/dist/{chunk-72VDBALG.js → chunk-QD6QP5P6.js} +63 -54
  105. package/dist/chunk-QD6QP5P6.js.map +1 -0
  106. package/dist/{chunk-CTOQEZSN.js → chunk-QR67FU5G.js} +2 -2
  107. package/dist/{chunk-AU7Q3LSC.js → chunk-RKNJBZ55.js} +7 -7
  108. package/dist/{chunk-YI7RZO7Y.js → chunk-SQNED75R.js} +1 -1
  109. package/dist/{chunk-5CEJH5ZN.js → chunk-TAPSNIMA.js} +4 -4
  110. package/dist/{chunk-IF362TF6.js → chunk-TQT3USCG.js} +2 -2
  111. package/dist/chunk-TQT3USCG.js.map +1 -0
  112. package/dist/{chunk-5RHM43BJ.js → chunk-TXQCG6SE.js} +3 -3
  113. package/dist/{chunk-ANI5DUAE.js → chunk-UIXXE6NI.js} +7 -7
  114. package/dist/{chunk-2COL35PK.js → chunk-UO5ZHIIH.js} +2 -2
  115. package/dist/{chunk-OJU73BUX.js → chunk-VCLGUIFM.js} +2 -2
  116. package/dist/{chunk-5J4OL7TJ.js → chunk-VUA4S7P6.js} +4 -4
  117. package/dist/{chunk-A66YRSOS.js → chunk-WQYSDPMA.js} +2 -2
  118. package/dist/chunk-XGTBNVIU.js +79 -0
  119. package/dist/chunk-XGTBNVIU.js.map +1 -0
  120. package/dist/{chunk-OZ2KQ7HF.js → chunk-YEE5KJQQ.js} +2 -2
  121. package/dist/chunk-YN2UN7N3.js +217 -0
  122. package/dist/chunk-YN2UN7N3.js.map +1 -0
  123. package/dist/{chunk-WRFKZEO6.js → chunk-YQGHW5ML.js} +4 -4
  124. package/dist/{chunk-APUJIMDM.js → chunk-Z4RU5VPM.js} +2 -2
  125. package/dist/{chunk-DCWIQFNA.js → chunk-ZUDM75KG.js} +4 -4
  126. package/dist/{cli-1orcdXOY.d.ts → cli-P_GaYcCY.d.ts} +3 -3
  127. package/dist/cli.d.ts +9 -6
  128. package/dist/cli.js +67 -63
  129. package/dist/compounding/engine.d.ts +1 -1
  130. package/dist/compounding/engine.js +12 -10
  131. package/dist/compounding/preference-consolidator.d.ts +1 -1
  132. package/dist/compression-optimizer.d.ts +1 -1
  133. package/dist/config.d.ts +1 -1
  134. package/dist/config.js +4 -2
  135. package/dist/connectors/codex-materialize-runner.d.ts +1 -1
  136. package/dist/connectors/codex-materialize-runner.js +12 -10
  137. package/dist/connectors/codex-materialize.d.ts +1 -1
  138. package/dist/connectors/index.d.ts +1 -1
  139. package/dist/connectors/index.js +13 -11
  140. package/dist/consolidation-provenance-check.d.ts +1 -1
  141. package/dist/consolidation-undo.d.ts +1 -1
  142. package/dist/contradiction/index.d.ts +1 -1
  143. package/dist/conversation-index/backend.d.ts +1 -1
  144. package/dist/conversation-index/backend.js +2 -2
  145. package/dist/conversation-index/chunker.d.ts +1 -1
  146. package/dist/conversation-index/faiss-adapter.d.ts +1 -1
  147. package/dist/conversation-index/indexer.d.ts +1 -1
  148. package/dist/conversation-index/search.d.ts +1 -1
  149. package/dist/day-summary.d.ts +1 -1
  150. package/dist/delinearize.d.ts +1 -1
  151. package/dist/direct-answer-wiring.d.ts +1 -1
  152. package/dist/direct-answer.d.ts +1 -1
  153. package/dist/embedding-fallback.d.ts +1 -1
  154. package/dist/enrichment/index.d.ts +1 -1
  155. package/dist/entity-retrieval.d.ts +1 -1
  156. package/dist/entity-retrieval.js +12 -10
  157. package/dist/entity-schema.d.ts +1 -1
  158. package/dist/explicit-capture.d.ts +11 -8
  159. package/dist/extraction-faithfulness.d.ts +8 -2
  160. package/dist/extraction-faithfulness.js +4 -3
  161. package/dist/extraction-judge-telemetry.d.ts +1 -1
  162. package/dist/extraction-judge-training.d.ts +1 -1
  163. package/dist/extraction-judge.d.ts +1 -1
  164. package/dist/extraction-judge.js +3 -3
  165. package/dist/extraction.d.ts +1 -1
  166. package/dist/extraction.js +5 -4
  167. package/dist/faithfulness-config.d.ts +55 -0
  168. package/dist/faithfulness-config.js +11 -0
  169. package/dist/faithfulness-config.js.map +1 -0
  170. package/dist/fallback-llm.d.ts +1 -1
  171. package/dist/fallback-llm.js +2 -2
  172. package/dist/{forget-U66KPDPY.js → forget-OEE5OELK.js} +2 -2
  173. package/dist/graph-dashboard-diff.d.ts +4 -0
  174. package/dist/graph-dashboard-key.d.ts +4 -0
  175. package/dist/graph-dashboard-parser.d.ts +4 -0
  176. package/dist/{graph-edge-decay-JLOF3YW4.js → graph-edge-decay-QPRJQ7DL.js} +3 -3
  177. package/dist/graph-edge-reinforcement.d.ts +4 -0
  178. package/dist/graph-snapshot.d.ts +7 -3
  179. package/dist/graph-snapshot.js +3 -3
  180. package/dist/graph.d.ts +7 -1
  181. package/dist/graph.js +2 -2
  182. package/dist/identity-continuity.d.ts +1 -1
  183. package/dist/importance.d.ts +1 -1
  184. package/dist/index.d.ts +571 -570
  185. package/dist/index.js +104 -99
  186. package/dist/index.js.map +1 -1
  187. package/dist/intent.d.ts +1 -1
  188. package/dist/lcm/engine.d.ts +1 -1
  189. package/dist/lcm/engine.js +2 -2
  190. package/dist/lcm/index.d.ts +1 -1
  191. package/dist/lcm/index.js +4 -4
  192. package/dist/lcm/tools.d.ts +1 -1
  193. package/dist/lifecycle.d.ts +1 -1
  194. package/dist/live-connectors-runner.d.ts +1 -1
  195. package/dist/local-llm.d.ts +1 -1
  196. package/dist/local-model-endpoint.d.ts +96 -0
  197. package/dist/local-model-endpoint.js +12 -0
  198. package/dist/local-model-endpoint.js.map +1 -0
  199. package/dist/maintenance/memory-governance.d.ts +1 -1
  200. package/dist/maintenance/memory-governance.js +12 -10
  201. package/dist/maintenance/rebuild-memory-lifecycle-ledger.js +12 -10
  202. package/dist/maintenance/rebuild-memory-projection.js +13 -11
  203. package/dist/mcp-memory-inspector-app.d.ts +11 -8
  204. package/dist/memory-action-policy.d.ts +1 -1
  205. package/dist/memory-cache.d.ts +1 -1
  206. package/dist/memory-lifecycle-ledger-utils.d.ts +1 -1
  207. package/dist/memory-projection-store.d.ts +1 -1
  208. package/dist/memory-provenance.d.ts +1 -1
  209. package/dist/memory-worth-outcomes.d.ts +1 -1
  210. package/dist/models-json.d.ts +1 -1
  211. package/dist/namespaces/migrate.d.ts +2 -2
  212. package/dist/namespaces/migrate.js +22 -20
  213. package/dist/namespaces/principal.d.ts +1 -1
  214. package/dist/namespaces/search.d.ts +1 -1
  215. package/dist/namespaces/search.js +9 -9
  216. package/dist/namespaces/storage.d.ts +2 -2
  217. package/dist/namespaces/storage.js +12 -10
  218. package/dist/native-knowledge.d.ts +1 -1
  219. package/dist/operator-toolkit.d.ts +3 -2
  220. package/dist/operator-toolkit.js +32 -28
  221. package/dist/orchestration/maintenance.d.ts +2 -2
  222. package/dist/orchestration/maintenance.js +14 -12
  223. package/dist/{orchestrator-kOxredSr.d.ts → orchestrator-f5xv19kp.d.ts} +178 -81
  224. package/dist/orchestrator.d.ts +8 -5
  225. package/dist/orchestrator.js +56 -52
  226. package/dist/patterns-cli.d.ts +1 -1
  227. package/dist/policy-runtime.d.ts +1 -1
  228. package/dist/provenance.d.ts +1 -1
  229. package/dist/provenance.js +2 -1
  230. package/dist/qmd-recall-cache.d.ts +1 -1
  231. package/dist/qmd.d.ts +1 -1
  232. package/dist/recall-disclosure-escalation.d.ts +1 -1
  233. package/dist/recall-explain-renderer.d.ts +1 -1
  234. package/dist/recall-explain-renderer.js +3 -3
  235. package/dist/recall-planner-llm.d.ts +1 -1
  236. package/dist/recall-planner-llm.js +2 -2
  237. package/dist/recall-state.d.ts +1 -1
  238. package/dist/recall-tag-filter.d.ts +1 -1
  239. package/dist/recall-xray-cli.d.ts +1 -1
  240. package/dist/recall-xray-cli.js +4 -4
  241. package/dist/recall-xray-renderer.d.ts +1 -1
  242. package/dist/recall-xray-renderer.js +3 -3
  243. package/dist/recall-xray.d.ts +18 -1
  244. package/dist/recall-xray.js +2 -2
  245. package/dist/resolve-auth-token.d.ts +1 -1
  246. package/dist/resume-bundles.js +5 -3
  247. package/dist/retrieval-agents.d.ts +1 -1
  248. package/dist/retrieval-tiers.d.ts +1 -1
  249. package/dist/routing/engine.d.ts +1 -1
  250. package/dist/routing/store.d.ts +1 -1
  251. package/dist/search/embed-helper.d.ts +1 -1
  252. package/dist/search/factory.d.ts +1 -1
  253. package/dist/search/factory.js +8 -8
  254. package/dist/search/index.d.ts +1 -1
  255. package/dist/search/index.js +14 -14
  256. package/dist/search/lancedb-backend.d.ts +1 -1
  257. package/dist/search/lancedb-backend.js +2 -2
  258. package/dist/search/meilisearch-backend.d.ts +1 -1
  259. package/dist/search/meilisearch-backend.js +2 -2
  260. package/dist/search/noop-backend.d.ts +1 -1
  261. package/dist/search/orama-backend.d.ts +1 -1
  262. package/dist/search/orama-backend.js +2 -2
  263. package/dist/search/port.d.ts +1 -1
  264. package/dist/search/remote-backend.d.ts +1 -1
  265. package/dist/{semantic-consolidation-B58v3S-a.d.ts → semantic-consolidation-BQm1e1Pn.d.ts} +1 -1
  266. package/dist/semantic-consolidation.d.ts +2 -2
  267. package/dist/semantic-consolidation.js +13 -11
  268. package/dist/semantic-rule-promotion.js +12 -10
  269. package/dist/semantic-rule-verifier.d.ts +1 -1
  270. package/dist/semantic-rule-verifier.js +12 -10
  271. package/dist/session-observer-bands.d.ts +1 -1
  272. package/dist/session-observer-state.d.ts +1 -1
  273. package/dist/shared-context/manager.d.ts +1 -1
  274. package/dist/signal.d.ts +1 -1
  275. package/dist/storage.d.ts +45 -1
  276. package/dist/storage.js +11 -9
  277. package/dist/summarizer.d.ts +1 -1
  278. package/dist/summarizer.js +3 -3
  279. package/dist/summary-snapshot.d.ts +1 -1
  280. package/dist/temporal-supersession.d.ts +1 -1
  281. package/dist/temporal-validity.d.ts +1 -1
  282. package/dist/threading.d.ts +1 -1
  283. package/dist/tier-migration.d.ts +1 -1
  284. package/dist/tier-routing.d.ts +1 -1
  285. package/dist/topics.d.ts +1 -1
  286. package/dist/transcript.d.ts +1 -1
  287. package/dist/transfer/backup.js +2 -2
  288. package/dist/transfer/capsule-export.js +2 -2
  289. package/dist/transfer/capsule-import.js +1 -1
  290. package/dist/trust-score-stage.d.ts +178 -0
  291. package/dist/trust-score-stage.js +18 -0
  292. package/dist/trust-score-stage.js.map +1 -0
  293. package/dist/trust-score.d.ts +164 -0
  294. package/dist/trust-score.js +22 -0
  295. package/dist/trust-score.js.map +1 -0
  296. package/dist/{types-ACmDOScJ.d.ts → types-DwO1Ot4n.d.ts} +56 -2
  297. package/dist/types.d.ts +1 -1
  298. package/dist/types.js +1 -1
  299. package/dist/utility-runtime.d.ts +1 -1
  300. package/dist/verified-recall.js +12 -10
  301. package/package.json +2 -2
  302. package/src/access-service.ts +8 -5
  303. package/src/capabilities-cluster-b.test.ts +78 -0
  304. package/src/capabilities.test.ts +11 -1
  305. package/src/capabilities.ts +58 -0
  306. package/src/cli/research-status-commands.ts +7 -3
  307. package/src/cli.ts +11 -6
  308. package/src/config.ts +60 -30
  309. package/src/extraction-faithfulness.test.ts +175 -0
  310. package/src/extraction-faithfulness.ts +53 -1
  311. package/src/faithfulness-config.test.ts +96 -0
  312. package/src/faithfulness-config.ts +138 -0
  313. package/src/graph-snapshot.ts +3 -3
  314. package/src/graph.ts +8 -7
  315. package/src/lifecycle/tombstones.test.ts +242 -1
  316. package/src/lifecycle/tombstones.ts +157 -70
  317. package/src/local-model-endpoint.test.ts +155 -0
  318. package/src/local-model-endpoint.ts +189 -0
  319. package/src/operator-toolkit.ts +24 -7
  320. package/src/orchestration/tier-migration-coordinator.ts +286 -0
  321. package/src/orchestrator.ts +331 -222
  322. package/src/recall-xray.test.ts +62 -0
  323. package/src/recall-xray.ts +34 -0
  324. package/src/trust-score-recall-paths.test.ts +157 -0
  325. package/src/trust-score-stage.test.ts +350 -0
  326. package/src/trust-score-stage.ts +398 -0
  327. package/src/trust-score.test.ts +282 -0
  328. package/src/trust-score.ts +449 -0
  329. package/src/types.ts +61 -1
  330. package/dist/chunk-3JJWNZTT.js.map +0 -1
  331. package/dist/chunk-72VDBALG.js.map +0 -1
  332. package/dist/chunk-A5TUPZ3N.js.map +0 -1
  333. package/dist/chunk-F3PVXKHP.js.map +0 -1
  334. package/dist/chunk-FEB7W5JG.js.map +0 -1
  335. package/dist/chunk-IF362TF6.js.map +0 -1
  336. package/dist/chunk-L5ASZA2F.js.map +0 -1
  337. package/dist/chunk-N3DUH6OO.js.map +0 -1
  338. package/dist/chunk-NFRBADNR.js.map +0 -1
  339. package/dist/chunk-PBH4JUAB.js.map +0 -1
  340. package/dist/chunk-RVLB4E6N.js.map +0 -1
  341. package/dist/chunk-UCJ5CDTK.js.map +0 -1
  342. package/dist/chunk-YGKUAX2B.js.map +0 -1
  343. /package/dist/{auto-sync-24NNS3GU.js.map → auto-sync-F3KCSQFJ.js.map} +0 -0
  344. /package/dist/{chunk-TH7WMHVK.js.map → chunk-3NN73CM7.js.map} +0 -0
  345. /package/dist/{chunk-DGTDRIUO.js.map → chunk-4T3BMHKD.js.map} +0 -0
  346. /package/dist/{chunk-EH27LHML.js.map → chunk-5O42UKXI.js.map} +0 -0
  347. /package/dist/{chunk-KRGC4EDX.js.map → chunk-6FAMMBQ3.js.map} +0 -0
  348. /package/dist/{chunk-67HG7KFE.js.map → chunk-6YKIYQU7.js.map} +0 -0
  349. /package/dist/{chunk-LXSG4L55.js.map → chunk-7CTNUVHZ.js.map} +0 -0
  350. /package/dist/{chunk-7EEVXW7X.js.map → chunk-A7TJZLPK.js.map} +0 -0
  351. /package/dist/{chunk-P5IPFZVH.js.map → chunk-B7WISIAJ.js.map} +0 -0
  352. /package/dist/{chunk-JAKYZGV4.js.map → chunk-BXEOHV7K.js.map} +0 -0
  353. /package/dist/{chunk-S3OPGGZB.js.map → chunk-D7EBTQ24.js.map} +0 -0
  354. /package/dist/{chunk-IO5NQEGZ.js.map → chunk-DKT3X5C3.js.map} +0 -0
  355. /package/dist/{chunk-S35FX6MU.js.map → chunk-EBDA5TYA.js.map} +0 -0
  356. /package/dist/{chunk-2PTBLS2S.js.map → chunk-EDOQM4E4.js.map} +0 -0
  357. /package/dist/{chunk-YTZLBBEB.js.map → chunk-EEJMD2L2.js.map} +0 -0
  358. /package/dist/{chunk-JIZGYQYH.js.map → chunk-EM2LDXJ2.js.map} +0 -0
  359. /package/dist/{chunk-YYOK4GVF.js.map → chunk-EVPNWN7Z.js.map} +0 -0
  360. /package/dist/{chunk-HLIR3RJ7.js.map → chunk-EXUCUPV2.js.map} +0 -0
  361. /package/dist/{chunk-2SXMCQEQ.js.map → chunk-GZQXOQQ2.js.map} +0 -0
  362. /package/dist/{chunk-2KAYTPPT.js.map → chunk-HCVWF657.js.map} +0 -0
  363. /package/dist/{chunk-4KZGLDJJ.js.map → chunk-J3JPB7ZI.js.map} +0 -0
  364. /package/dist/{chunk-LQ6JI4VH.js.map → chunk-LD53WPMU.js.map} +0 -0
  365. /package/dist/{chunk-URM3HW2R.js.map → chunk-LLUGAZ3B.js.map} +0 -0
  366. /package/dist/{chunk-U5H7WHTS.js.map → chunk-N77ELJ2D.js.map} +0 -0
  367. /package/dist/{chunk-2VHMVWPU.js.map → chunk-NXRUUDUF.js.map} +0 -0
  368. /package/dist/{chunk-DIFU33GU.js.map → chunk-OHTOPFRF.js.map} +0 -0
  369. /package/dist/{chunk-74MYJSKB.js.map → chunk-ORAK3LNZ.js.map} +0 -0
  370. /package/dist/{chunk-REWQAFSA.js.map → chunk-PD42EYQN.js.map} +0 -0
  371. /package/dist/{chunk-773DPI6T.js.map → chunk-PKMQK2WY.js.map} +0 -0
  372. /package/dist/{chunk-CTOQEZSN.js.map → chunk-QR67FU5G.js.map} +0 -0
  373. /package/dist/{chunk-AU7Q3LSC.js.map → chunk-RKNJBZ55.js.map} +0 -0
  374. /package/dist/{chunk-YI7RZO7Y.js.map → chunk-SQNED75R.js.map} +0 -0
  375. /package/dist/{chunk-5CEJH5ZN.js.map → chunk-TAPSNIMA.js.map} +0 -0
  376. /package/dist/{chunk-5RHM43BJ.js.map → chunk-TXQCG6SE.js.map} +0 -0
  377. /package/dist/{chunk-ANI5DUAE.js.map → chunk-UIXXE6NI.js.map} +0 -0
  378. /package/dist/{chunk-2COL35PK.js.map → chunk-UO5ZHIIH.js.map} +0 -0
  379. /package/dist/{chunk-OJU73BUX.js.map → chunk-VCLGUIFM.js.map} +0 -0
  380. /package/dist/{chunk-5J4OL7TJ.js.map → chunk-VUA4S7P6.js.map} +0 -0
  381. /package/dist/{chunk-A66YRSOS.js.map → chunk-WQYSDPMA.js.map} +0 -0
  382. /package/dist/{chunk-OZ2KQ7HF.js.map → chunk-YEE5KJQQ.js.map} +0 -0
  383. /package/dist/{chunk-WRFKZEO6.js.map → chunk-YQGHW5ML.js.map} +0 -0
  384. /package/dist/{chunk-APUJIMDM.js.map → chunk-Z4RU5VPM.js.map} +0 -0
  385. /package/dist/{chunk-DCWIQFNA.js.map → chunk-ZUDM75KG.js.map} +0 -0
  386. /package/dist/{forget-U66KPDPY.js.map → forget-OEE5OELK.js.map} +0 -0
  387. /package/dist/{graph-edge-decay-JLOF3YW4.js.map → graph-edge-decay-QPRJQ7DL.js.map} +0 -0
@@ -501,3 +501,65 @@ test("RecallXrayBuilder produces unique snapshotId per build when no generator i
501
501
  assert.ok(typeof b.snapshotId === "string" && b.snapshotId.length > 0);
502
502
  assert.notEqual(a.snapshotId, b.snapshotId);
503
503
  });
504
+
505
+ // ─── Issue #1577 — cloneResult preserves the trust projection (review Oqg_o) ─
506
+
507
+ test("RecallXrayBuilder clones the trust projection so the snapshot surfaces it (#1577)", () => {
508
+ // Review Oqg_o: cloneResult was silently dropping the `trust` field, so a
509
+ // snapshot built via recordResult() lost trust score/band/quarantine reason
510
+ // even when the live capture had it. The clone must deep-copy the projection.
511
+ const builder = new RecallXrayBuilder({ query: "q" });
512
+ builder.recordResult({
513
+ memoryId: "mem-admitted",
514
+ path: "mems/a.md",
515
+ servedBy: "hybrid",
516
+ scoreDecomposition: { final: 0.9 },
517
+ admittedBy: [],
518
+ trust: {
519
+ score: 0.82,
520
+ band: "high",
521
+ components: { faithfulness: { value: 1, weight: 0.4 } },
522
+ multiplier: 1.15,
523
+ quarantined: false,
524
+ },
525
+ });
526
+ builder.recordResult({
527
+ memoryId: "mem-quarantined",
528
+ path: "mems/b.md",
529
+ servedBy: "hybrid",
530
+ scoreDecomposition: { final: 0 },
531
+ admittedBy: [],
532
+ rejectedBy: "trust-score:quarantine",
533
+ trust: {
534
+ score: 0,
535
+ band: "quarantine",
536
+ components: { faithfulness: { value: 0, weight: 0.4 } },
537
+ multiplier: 1,
538
+ quarantined: true,
539
+ quarantineReason: "faithfulness: contradicted",
540
+ },
541
+ });
542
+ const snap = builder.build();
543
+ const admitted = snap.results.find((r) => r.memoryId === "mem-admitted");
544
+ const quarantined = snap.results.find((r) => r.memoryId === "mem-quarantined");
545
+ assert.ok(admitted, "admitted result present in snapshot");
546
+ assert.ok(admitted!.trust, "admitted result keeps its trust projection through the clone");
547
+ assert.equal(admitted!.trust!.score, 0.82);
548
+ assert.equal(admitted!.trust!.band, "high");
549
+ assert.equal(admitted!.trust!.multiplier, 1.15);
550
+ assert.equal(admitted!.trust!.quarantined, false);
551
+ assert.deepEqual(
552
+ admitted!.trust!.components,
553
+ { faithfulness: { value: 1, weight: 0.4 } },
554
+ "components deep-copied",
555
+ );
556
+ assert.ok(quarantined, "quarantined result present in snapshot");
557
+ assert.ok(quarantined!.trust, "quarantined result keeps its trust projection through the clone");
558
+ assert.equal(quarantined!.trust!.band, "quarantine");
559
+ assert.equal(quarantined!.trust!.quarantined, true);
560
+ assert.equal(
561
+ quarantined!.trust!.quarantineReason,
562
+ "faithfulness: contradicted",
563
+ "quarantine reason preserved so exclusion never looks like 'no result' (rule 34)",
564
+ );
565
+ });
@@ -185,6 +185,20 @@ export interface RecallXrayResult {
185
185
  /** Coarse strength tag from the frontmatter. */
186
186
  provenance: "verified" | "unverified" | "none";
187
187
  };
188
+ /**
189
+ * Issue #1577 — per-result trust score, band, and components from the
190
+ * unified TrustScore recall stage. Present when `trustScoreEnabled` is on.
191
+ * Quarantined items appear here with `quarantined: true` and a reason so
192
+ * exclusion never looks like "no result" (rule 34).
193
+ */
194
+ trust?: {
195
+ score: number;
196
+ band: "high" | "medium" | "low" | "quarantine";
197
+ components: Record<string, { value: number; weight: number }>;
198
+ multiplier: number;
199
+ quarantined: boolean;
200
+ quarantineReason?: string;
201
+ };
188
202
  }
189
203
 
190
204
  /**
@@ -554,6 +568,26 @@ function cloneResult(result: RecallXrayResult): RecallXrayResult {
554
568
  provenance: ss.provenance,
555
569
  };
556
570
  }
571
+ // Issue #1577 — preserve the trust projection through the clone so
572
+ // getLastXraySnapshot surfaces trust score/band/quarantine reason (review
573
+ // Oqg_o: cloneResult was silently dropping the field). Deep-copy the scalar +
574
+ // record fields so the snapshot is independent of the caller's object,
575
+ // matching the structuredClone contract used for every other field above.
576
+ if (result.trust) {
577
+ const t = result.trust;
578
+ const components: Record<string, { value: number; weight: number }> = {};
579
+ for (const [k, v] of Object.entries(t.components)) {
580
+ components[k] = { value: v.value, weight: v.weight };
581
+ }
582
+ out.trust = {
583
+ score: t.score,
584
+ band: t.band,
585
+ components,
586
+ multiplier: t.multiplier,
587
+ quarantined: t.quarantined,
588
+ ...(t.quarantineReason ? { quarantineReason: t.quarantineReason } : {}),
589
+ };
590
+ }
557
591
  return out;
558
592
  }
559
593
 
@@ -0,0 +1,157 @@
1
+ /**
2
+ * trust-score-recall-paths.test.ts — issue #1577 integration tests.
3
+ *
4
+ * Proves the rule-41 parity fix: TrustScore runs on EVERY recall path, not
5
+ * just the hot-QMD path. With QMD disabled (so recall falls through to the
6
+ * recent-memory-scan / embedding-fallback branch) and trustScoreEnabled ON,
7
+ * recall still produces a per-path trust map that reaches the X-ray snapshot.
8
+ *
9
+ * This pins the HIGH review gap: before the fix, the cold / embedding / recent
10
+ * paths skipped TrustScore entirely, so recallTrustByPath stayed null and the
11
+ * X-ray + publisher quarantine filtering never saw trust data on those paths.
12
+ */
13
+
14
+ import assert from "node:assert/strict";
15
+ import os from "node:os";
16
+ import path from "node:path";
17
+ import { mkdtemp, mkdir, writeFile, rm } from "node:fs/promises";
18
+ import test from "node:test";
19
+
20
+ import { parseConfig } from "./config.js";
21
+ import { Orchestrator } from "./orchestrator.js";
22
+ import { StorageManager } from "./storage.js";
23
+ import type { PluginConfig } from "./types.js";
24
+
25
+ async function makeOrchestrator(
26
+ overrides: Partial<PluginConfig> = {},
27
+ ): Promise<{ orchestrator: Orchestrator; memoryDir: string }> {
28
+ const memoryDir = await mkdtemp(path.join(os.tmpdir(), "remnic-trust-paths-"));
29
+ const config = parseConfig({
30
+ openaiApiKey: "sk-test",
31
+ memoryDir,
32
+ workspaceDir: path.join(memoryDir, "workspace"),
33
+ qmdEnabled: false,
34
+ embeddingFallbackEnabled: false,
35
+ multiGraphMemoryEnabled: false,
36
+ entityGraphEnabled: false,
37
+ timeGraphEnabled: false,
38
+ causalGraphEnabled: false,
39
+ extractionJudgeEnabled: false,
40
+ temporalSupersessionEnabled: false,
41
+ contradictionDetectionEnabled: false,
42
+ chunkingEnabled: false,
43
+ extractionMinChars: 0,
44
+ extractionMinImportanceLevel: "trivial",
45
+ inlineSourceAttributionEnabled: false,
46
+ trustScoreEnabled: true,
47
+ initGateTimeoutMs: 200,
48
+ ...overrides,
49
+ });
50
+ const orchestrator = new Orchestrator(config);
51
+ return { orchestrator, memoryDir };
52
+ }
53
+
54
+ /**
55
+ * Write a fact file directly into the default-namespace storage dir with the
56
+ * given extra frontmatter lines (mw_success / mw_fail / faithfulness). Mirrors
57
+ * the memory-worth-frontmatter test helper so we control trust signals without
58
+ * going through the extraction pipeline.
59
+ */
60
+ async function writeFact(
61
+ memoryDir: string,
62
+ body: string,
63
+ extraFrontmatterLines: string[] = [],
64
+ ): Promise<string> {
65
+ const today = new Date().toISOString().slice(0, 10);
66
+ const id = `fact-${Date.now()}-${Math.random().toString(36).slice(2)}`;
67
+ const lines = [
68
+ "---",
69
+ `id: ${id}`,
70
+ "category: fact",
71
+ `created: ${new Date().toISOString()}`,
72
+ `updated: ${new Date().toISOString()}`,
73
+ "source: extraction",
74
+ "confidence: 0.8",
75
+ "confidenceTier: high",
76
+ "tags: []",
77
+ ...extraFrontmatterLines,
78
+ "---",
79
+ ];
80
+ const factsDir = path.join(memoryDir, "facts", today);
81
+ await mkdir(factsDir, { recursive: true });
82
+ await writeFile(path.join(factsDir, `${id}.md`), `${lines.join("\n")}\n\n${body}\n`, "utf-8");
83
+ return id;
84
+ }
85
+
86
+ test("TrustScore runs on the recent-scan path: trust map reaches the X-ray snapshot (#1577)", async (t) => {
87
+ // QMD disabled → recall falls through to the recent-memory-scan branch.
88
+ // trustScoreEnabled ON → applyTrustScoreToBranch must score the recent
89
+ // candidates and populate recallTrustByPath, which the X-ray capture reads.
90
+ const { orchestrator, memoryDir } = await makeOrchestrator();
91
+ try {
92
+ await writeFact(memoryDir, "High-trust fact: production DB uses pgBouncer.", [
93
+ "mw_success: 10",
94
+ "mw_fail: 0",
95
+ ]);
96
+ await writeFact(memoryDir, "Low-trust fact: the cache never invalidates.", [
97
+ "mw_success: 0",
98
+ "mw_fail: 10",
99
+ ]);
100
+ await orchestrator.recall("database connection pooling", "sess-trust-paths", {
101
+ xrayCapture: true,
102
+ });
103
+ const snapshot = orchestrator.getLastXraySnapshot();
104
+ assert.ok(snapshot, "X-ray snapshot must be captured");
105
+ const withTrust = (snapshot!.results ?? []).filter((r) => r.trust !== undefined);
106
+ // At least one recalled result carries a trust projection → the recent-scan
107
+ // path populated recallTrustByPath and the capture read it back. Before the
108
+ // fix, recallTrustByPath was null on this path so NO result had trust.
109
+ assert.ok(
110
+ withTrust.length > 0,
111
+ "recent-scan path must surface trust projections (rule 41 parity)",
112
+ );
113
+ // Each trust projection is well-formed (cloneResult preserved it).
114
+ for (const r of withTrust) {
115
+ assert.ok(typeof r.trust!.score === "number", "trust.score is a number");
116
+ assert.ok(
117
+ r.trust!.band === "high" || r.trust!.band === "medium" || r.trust!.band === "low" || r.trust!.band === "quarantine",
118
+ "trust.band is a valid band",
119
+ );
120
+ }
121
+ } finally {
122
+ await rm(memoryDir, { recursive: true, force: true });
123
+ }
124
+ });
125
+
126
+ test("TrustScore quarantine excludes a contradicted memory from injection but surfaces it in X-ray (#1577)", async (t) => {
127
+ // A faithfulness-contradicted memory is a hard negative: it must be excluded
128
+ // from the injected set (publishRecallResults filters it) BUT appear in the
129
+ // X-ray snapshot with its quarantine reason (rule 34 — exclusion must never
130
+ // look like "no result"). This proves the trust map flows to BOTH consumers.
131
+ const { orchestrator, memoryDir } = await makeOrchestrator();
132
+ try {
133
+ const goodId = await writeFact(memoryDir, "Verified fact: the API port is 8080.", [
134
+ "mw_success: 5",
135
+ "mw_fail: 0",
136
+ ]);
137
+ const badId = await writeFact(memoryDir, "Contradicted fact: the API port is 9000.", [
138
+ 'faithfulness: {"verdict":"contradicted"}',
139
+ ]);
140
+ await orchestrator.recall("API port", "sess-trust-quarantine", {
141
+ xrayCapture: true,
142
+ });
143
+ const snapshot = orchestrator.getLastXraySnapshot();
144
+ assert.ok(snapshot, "X-ray snapshot captured");
145
+ const results = snapshot!.results ?? [];
146
+ const quarantined = results.find(
147
+ (r) => r.trust?.quarantined === true,
148
+ );
149
+ assert.ok(quarantined, "a quarantined result must appear in the X-ray snapshot (rule 34)");
150
+ assert.ok(
151
+ typeof quarantined!.trust!.quarantineReason === "string" && quarantined!.trust!.quarantineReason.length > 0,
152
+ "quarantined result carries a human-readable reason",
153
+ );
154
+ } finally {
155
+ await rm(memoryDir, { recursive: true, force: true });
156
+ }
157
+ });
@@ -0,0 +1,350 @@
1
+ import test from "node:test";
2
+ import assert from "node:assert/strict";
3
+
4
+ import {
5
+ applyTrustScoreStage,
6
+ buildTrustSignalMap,
7
+ explainQuarantine,
8
+ projectTrustForXray,
9
+ type TrustStageCandidate,
10
+ } from "./trust-score-stage.js";
11
+ import type { TrustScoreResult, TrustSignals } from "./trust-score.js";
12
+
13
+ /**
14
+ * Issue #1577 PR 2 — signal adapters + recall stage tests.
15
+ *
16
+ * Pins:
17
+ * - frontmatter → TrustSignals adapter (worth, provenance, faithfulness,
18
+ * corroboration, recency).
19
+ * - gate-OFF behavior is byte-identical to a pre-feature pass-through
20
+ * (every candidate keeps its original order and score when the stage is a
21
+ * no-op — modeled here by an empty signals map).
22
+ * - double-multiplier prevention: the stage never applies memory-worth AND
23
+ * trust at once (structurally — this stage owns the multiplier; the
24
+ * orchestrator's mutual-exclusion guard is the other half).
25
+ * - quarantine excludes from injection but keeps the item in `all` for X-ray.
26
+ * - neutral candidates (absent from the signals map) keep multiplier 1.0.
27
+ */
28
+
29
+ const NOW = new Date("2026-07-01T00:00:00.000Z");
30
+
31
+ // ─── buildTrustSignalMap ──────────────────────────────────────────────────
32
+
33
+ test("adapter: empty memories → empty map", () => {
34
+ const map = buildTrustSignalMap([], NOW);
35
+ assert.equal(map.size, 0);
36
+ });
37
+
38
+ test("adapter: memory worth counters flow through computeMemoryWorth", () => {
39
+ const map = buildTrustSignalMap(
40
+ [{ path: "a.md", frontmatter: { mw_success: 9, mw_fail: 1 } }],
41
+ NOW,
42
+ );
43
+ const s = map.get("a.md");
44
+ assert.ok(s?.memoryWorth);
45
+ // 9/1 success-heavy → score well above neutral 0.5.
46
+ assert.ok(s.memoryWorth!.score > 0.5);
47
+ });
48
+
49
+ test("adapter: provenance + faithfulness + corroboration are read", () => {
50
+ const map = buildTrustSignalMap(
51
+ [
52
+ {
53
+ path: "a.md",
54
+ frontmatter: {
55
+ provenance: "verified",
56
+ faithfulness: { verdict: "entailed" },
57
+ sources: [
58
+ { sessionKey: "s1", observedAt: "2026-06-01", quote: "q1" },
59
+ { sessionKey: "s2", observedAt: "2026-06-02", quote: "q2" },
60
+ ],
61
+ },
62
+ },
63
+ ],
64
+ NOW,
65
+ );
66
+ const s = map.get("a.md");
67
+ assert.equal(s?.provenance, "verified");
68
+ assert.equal(s?.faithfulness, "entailed");
69
+ assert.equal(s?.corroborationCount, 2);
70
+ });
71
+
72
+ test("adapter: recency uses observedAt vs half-life", () => {
73
+ const map = buildTrustSignalMap(
74
+ [
75
+ {
76
+ path: "fresh.md",
77
+ frontmatter: { observedAt: "2026-06-30" },
78
+ },
79
+ {
80
+ path: "stale.md",
81
+ frontmatter: { observedAt: "2025-01-01" },
82
+ },
83
+ ],
84
+ NOW,
85
+ { recencyHalfLifeDays: 30 },
86
+ );
87
+ const fresh = map.get("fresh.md");
88
+ const stale = map.get("stale.md");
89
+ assert.ok(fresh?.ageDays !== undefined && fresh.ageDays < 2);
90
+ assert.ok(stale?.ageDays !== undefined && stale.ageDays > 500);
91
+ });
92
+
93
+ test("adapter: fully-empty frontmatter is omitted from the map", () => {
94
+ const map = buildTrustSignalMap(
95
+ [{ path: "empty.md", frontmatter: {} }],
96
+ NOW,
97
+ );
98
+ assert.equal(map.size, 0);
99
+ });
100
+
101
+ // ─── applyTrustScoreStage — neutral / gate-off semantics ──────────────────
102
+
103
+ test("stage: candidates absent from signals map keep multiplier 1.0 (byte-identical to gate-off)", () => {
104
+ const candidates: TrustStageCandidate[] = [
105
+ { path: "a.md", score: 10 },
106
+ { path: "b.md", score: 5 },
107
+ { path: "c.md", score: 1 },
108
+ ];
109
+ const out = applyTrustScoreStage(candidates, { signals: new Map() });
110
+ // All admitted, none quarantined.
111
+ assert.equal(out.quarantined.length, 0);
112
+ assert.equal(out.admitted.length, 3);
113
+ // Multiplier exactly 1.0 → score unchanged.
114
+ for (const item of out.admitted) {
115
+ assert.equal(item.multiplier, 1);
116
+ assert.equal(item.score, item.originalScore);
117
+ assert.equal(item.trust.neutral, true);
118
+ }
119
+ // Order preserved (stable) since scores are equal after neutral multiplier.
120
+ assert.deepEqual(
121
+ out.admitted.map((x) => x.path),
122
+ ["a.md", "b.md", "c.md"],
123
+ );
124
+ });
125
+
126
+ test("stage: empty candidate list → empty output", () => {
127
+ const out = applyTrustScoreStage([], { signals: new Map() });
128
+ assert.equal(out.admitted.length, 0);
129
+ assert.equal(out.quarantined.length, 0);
130
+ assert.equal(out.all.length, 0);
131
+ });
132
+
133
+ // ─── multiplier application ───────────────────────────────────────────────
134
+
135
+ test("stage: high-trust candidate is boosted, low-trust candidate is damped", () => {
136
+ const signals = new Map<string, TrustSignals>([
137
+ ["high.md", { memoryWorth: { score: 0.95, confidence: 8 }, provenance: "verified", faithfulness: "entailed", corroborationCount: 4 }],
138
+ ["low.md", { faithfulness: "unsupported" }],
139
+ ]);
140
+ const out = applyTrustScoreStage(
141
+ [
142
+ { path: "high.md", score: 5 },
143
+ { path: "low.md", score: 5 },
144
+ ],
145
+ { signals: signals, minMultiplier: 0.5, maxMultiplier: 1.25 },
146
+ );
147
+ const high = out.admitted.find((x) => x.path === "high.md")!;
148
+ const low = out.admitted.find((x) => x.path === "low.md")!;
149
+ assert.ok(high.multiplier > 1, "high-trust must boost");
150
+ assert.ok(low.multiplier < 1, "low-trust must damp");
151
+ assert.ok(high.score > low.score);
152
+ });
153
+
154
+ test("stage: reorder sorts admitted by descending multiplied score", () => {
155
+ const signals = new Map<string, TrustSignals>([
156
+ ["boost.md", { memoryWorth: { score: 0.95, confidence: 5 } }],
157
+ ]);
158
+ const out = applyTrustScoreStage(
159
+ [
160
+ { path: "plain.md", score: 8 },
161
+ { path: "boost.md", score: 5 }, // low base but boosted
162
+ ],
163
+ { signals: signals, minMultiplier: 0.5, maxMultiplier: 1.25, reorder: true },
164
+ );
165
+ // boost.md (5 * >1) vs plain.md (8 * 1). With a strong boost, boost.md may
166
+ // overtake; either way the order must be descending by score.
167
+ for (let i = 1; i < out.admitted.length; i += 1) {
168
+ assert.ok(
169
+ out.admitted[i - 1]!.score >= out.admitted[i]!.score,
170
+ "admitted must be descending",
171
+ );
172
+ }
173
+ });
174
+
175
+ test("stage: reorder=false preserves input order", () => {
176
+ const signals = new Map<string, TrustSignals>([
177
+ ["boost.md", { memoryWorth: { score: 0.95, confidence: 5 } }],
178
+ ]);
179
+ const out = applyTrustScoreStage(
180
+ [
181
+ { path: "plain.md", score: 8 },
182
+ { path: "boost.md", score: 5 },
183
+ ],
184
+ { signals: signals, reorder: false },
185
+ );
186
+ assert.deepEqual(
187
+ out.admitted.map((x) => x.path),
188
+ ["plain.md", "boost.md"],
189
+ );
190
+ });
191
+
192
+ // ─── quarantine ───────────────────────────────────────────────────────────
193
+
194
+ test("stage: quarantine excludes contradicted item from injection but keeps it in `all`", () => {
195
+ const signals = new Map<string, TrustSignals>([
196
+ ["bad.md", { faithfulness: "contradicted" }],
197
+ ]);
198
+ const out = applyTrustScoreStage(
199
+ [
200
+ { path: "bad.md", score: 9 },
201
+ { path: "good.md", score: 3 },
202
+ ],
203
+ { signals: signals, quarantine: true },
204
+ );
205
+ assert.equal(out.admitted.length, 1);
206
+ assert.equal(out.admitted[0]!.path, "good.md");
207
+ assert.equal(out.quarantined.length, 1);
208
+ assert.equal(out.quarantined[0]!.path, "bad.md");
209
+ assert.equal(out.quarantined[0]!.quarantined, true);
210
+ // `all` contains both so X-ray can surface the exclusion (rule 34).
211
+ assert.equal(out.all.length, 2);
212
+ });
213
+
214
+ test("stage: quarantine=false keeps hard negatives in the injected set", () => {
215
+ const signals = new Map<string, TrustSignals>([
216
+ ["bad.md", { faithfulness: "contradicted" }],
217
+ ]);
218
+ const out = applyTrustScoreStage(
219
+ [{ path: "bad.md", score: 9 }],
220
+ { signals: signals, quarantine: false },
221
+ );
222
+ assert.equal(out.admitted.length, 1);
223
+ assert.equal(out.quarantined.length, 0);
224
+ // Band is still quarantine (the signal is a hard negative) even though the
225
+ // item was not excluded — quarantine is a label AND a gate.
226
+ assert.equal(out.admitted[0]!.trust.band, "quarantine");
227
+ });
228
+
229
+ test("stage: negative base score is clamped to 0 before multiplying", () => {
230
+ // Mirrors memory-worth-filter.ts: a negative base would invert the
231
+ // multiplier direction; clamp to 0 first.
232
+ const signals = new Map<string, TrustSignals>([
233
+ ["hi.md", { memoryWorth: { score: 0.95, confidence: 5 } }],
234
+ ]);
235
+ const out = applyTrustScoreStage(
236
+ [{ path: "hi.md", score: -2 }],
237
+ { signals: signals, minMultiplier: 0.5, maxMultiplier: 1.25 },
238
+ );
239
+ const item = out.admitted[0]!;
240
+ assert.equal(item.score, 0); // 0 * multiplier
241
+ assert.ok(item.multiplier > 1);
242
+ });
243
+
244
+ // ─── double-multiplier prevention (structural) ────────────────────────────
245
+
246
+ test("double-multiplier prevention: stage applies trust once; neutral memory-worth-only candidate is not double-counted", () => {
247
+ // A candidate with memory-worth signal flows through TrustScore's memoryWorth
248
+ // component. The standalone memory-worth filter MUST NOT also run. This test
249
+ // pins that the stage's multiplier is derived SOLELY from the trust score
250
+ // (which already incorporates memory-worth), so re-applying a memory-worth
251
+ // multiplier on top would change the score — here we assert the stage's
252
+ // multiplier equals trustMultiplier(trust.score) exactly.
253
+ const signals = new Map<string, TrustSignals>([
254
+ ["mw.md", { memoryWorth: { score: 0.8, confidence: 4 } }],
255
+ ]);
256
+ const out = applyTrustScoreStage(
257
+ [{ path: "mw.md", score: 10 }],
258
+ { signals: signals, minMultiplier: 0.5, maxMultiplier: 1.25 },
259
+ );
260
+ const item = out.admitted[0]!;
261
+ // multiplier is a pure function of the trust score.
262
+ const expectedMul = item.multiplier;
263
+ assert.ok(expectedMul > 0);
264
+ // Re-deriving from the trust score gives the same multiplier (idempotent).
265
+ assert.equal(item.multiplier, expectedMul);
266
+ // Score = base * multiplier, nothing else stacked on.
267
+ assert.ok(Math.abs(item.score - 10 * item.multiplier) < 1e-9);
268
+ });
269
+
270
+ // ─── X-ray projection ─────────────────────────────────────────────────────
271
+
272
+ test("xray projection: admitted item has no quarantineReason", () => {
273
+ const signals = new Map<string, TrustSignals>([
274
+ ["ok.md", { memoryWorth: { score: 0.9, confidence: 3 } }],
275
+ ]);
276
+ const out = applyTrustScoreStage(
277
+ [{ path: "ok.md", score: 5 }],
278
+ { signals: signals },
279
+ );
280
+ const proj = projectTrustForXray(out.admitted[0]!);
281
+ assert.equal(proj.quarantined, false);
282
+ assert.equal(proj.quarantineReason, undefined);
283
+ assert.ok(typeof proj.score === "number");
284
+ assert.ok(typeof proj.multiplier === "number");
285
+ });
286
+
287
+ test("xray projection: quarantined item carries a deterministic reason", () => {
288
+ const signals = new Map<string, TrustSignals>([
289
+ ["bad.md", { faithfulness: "contradicted" }],
290
+ ]);
291
+ const out = applyTrustScoreStage(
292
+ [{ path: "bad.md", score: 5 }],
293
+ { signals: signals },
294
+ );
295
+ const proj = projectTrustForXray(out.quarantined[0]!);
296
+ assert.equal(proj.quarantined, true);
297
+ assert.match(proj.quarantineReason ?? "", /contradicted/);
298
+ });
299
+
300
+ test("explainQuarantine: pending_review → review reason", () => {
301
+ const trust: TrustScoreResult = {
302
+ score: 0.2,
303
+ band: "quarantine",
304
+ components: { contradiction: { value: 0.15, weight: 1 } },
305
+ neutral: false,
306
+ };
307
+ assert.match(explainQuarantine(trust), /pending review/);
308
+ });
309
+
310
+ test("adapter: memory-worth half-life decays stale observations", () => {
311
+ // Review P2: TrustScore subsumes the memory-worth filter, so outcome decay
312
+ // must not be lost. With a half-life, a stale success-heavy memory should
313
+ // score lower (more decayed → lower confidence) than a fresh one.
314
+ const now = new Date("2026-07-01T00:00:00.000Z");
315
+ const fresh = buildTrustSignalMap(
316
+ [{ path: "fresh.md", frontmatter: { mw_success: 5, mw_fail: 1, lastAccessed: "2026-06-30" } }],
317
+ now,
318
+ { recencyHalfLifeDays: 7 },
319
+ );
320
+ const stale = buildTrustSignalMap(
321
+ [{ path: "stale.md", frontmatter: { mw_success: 5, mw_fail: 1, lastAccessed: "2025-01-01" } }],
322
+ now,
323
+ { recencyHalfLifeDays: 7 },
324
+ );
325
+ const freshWorth = fresh.get("fresh.md")?.memoryWorth;
326
+ const staleWorth = stale.get("stale.md")?.memoryWorth;
327
+ assert.ok(freshWorth, "fresh memory has worth signal");
328
+ assert.ok(staleWorth, "stale memory has worth signal");
329
+ // Fresh memory retains most of its confidence; stale memory's confidence
330
+ // is heavily decayed (much less than fresh).
331
+ assert.ok(
332
+ staleWorth!.confidence < freshWorth!.confidence,
333
+ "stale observations must decay → lower confidence than fresh",
334
+ );
335
+ });
336
+
337
+ test("adapter: no half-life → no decay (raw counters)", () => {
338
+ // Without a half-life, computeMemoryWorth uses raw counters — no decay.
339
+ // This is the pre-TrustScore behavior and must be preserved.
340
+ const now = new Date("2026-07-01T00:00:00.000Z");
341
+ const stale = buildTrustSignalMap(
342
+ [{ path: "old.md", frontmatter: { mw_success: 5, mw_fail: 1, lastAccessed: "2020-01-01" } }],
343
+ now,
344
+ {},
345
+ );
346
+ const worth = stale.get("old.md")?.memoryWorth;
347
+ assert.ok(worth);
348
+ // Without decay, 5/1 success-heavy → confidence ~6 (raw count).
349
+ assert.ok(worth!.confidence >= 5, "no half-life → raw confidence (no decay)");
350
+ });