@remnic/core 9.3.729 → 9.3.730

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 (345) hide show
  1. package/dist/access-boundary.d.ts +6 -5
  2. package/dist/access-boundary.js +13 -13
  3. package/dist/access-cli.js +34 -33
  4. package/dist/access-cli.js.map +1 -1
  5. package/dist/access-http.d.ts +6 -5
  6. package/dist/access-http.js +19 -19
  7. package/dist/access-mcp.d.ts +6 -5
  8. package/dist/access-mcp.js +18 -18
  9. package/dist/access-operations-batch.js +16 -16
  10. package/dist/access-operations.d.ts +11 -10
  11. package/dist/access-operations.js +17 -17
  12. package/dist/access-schema.d.ts +42 -42
  13. package/dist/{access-service-BXMJqkTA.d.ts → access-service-Byoe3HsT.d.ts} +18 -3
  14. package/dist/access-service.d.ts +6 -5
  15. package/dist/access-service.js +12 -12
  16. package/dist/access-surface-catalog.d.ts +6 -5
  17. package/dist/action-confidence.d.ts +1 -1
  18. package/dist/active-memory-bridge.d.ts +1 -1
  19. package/dist/active-recall.d.ts +1 -1
  20. package/dist/active-recall.js +2 -2
  21. package/dist/{auto-sync-F3KCSQFJ.js → auto-sync-G3V5AN4C.js} +2 -2
  22. package/dist/behavior-learner.d.ts +1 -1
  23. package/dist/behavior-signals.d.ts +1 -1
  24. package/dist/bootstrap.d.ts +5 -4
  25. package/dist/briefing.d.ts +1 -1
  26. package/dist/briefing.js +3 -3
  27. package/dist/buffer-surprise-report.d.ts +1 -1
  28. package/dist/buffer.d.ts +1 -1
  29. package/dist/calibration.d.ts +1 -1
  30. package/dist/capabilities.d.ts +55 -2
  31. package/dist/capabilities.js +5 -1
  32. package/dist/{catalog-bMczlO2G.d.ts → catalog-DM1nk7cM.d.ts} +1 -1
  33. package/dist/causal-behavior.d.ts +1 -1
  34. package/dist/causal-consolidation.d.ts +1 -1
  35. package/dist/causal-consolidation.js +4 -4
  36. package/dist/causal-trajectory-graph.d.ts +1 -1
  37. package/dist/{chunk-4NOI577P.js → chunk-24ZLC5QN.js} +2 -2
  38. package/dist/{chunk-DMQWV6SQ.js → chunk-5RZOFIDL.js} +2 -2
  39. package/dist/{chunk-UM5AWJNQ.js → chunk-646B5ERJ.js} +62 -37
  40. package/dist/chunk-646B5ERJ.js.map +1 -0
  41. package/dist/{chunk-H5BWGCRZ.js → chunk-6TUWSUUD.js} +51 -47
  42. package/dist/{chunk-H5BWGCRZ.js.map → chunk-6TUWSUUD.js.map} +1 -1
  43. package/dist/{chunk-36SEDFAU.js → chunk-C5WN74HH.js} +2 -2
  44. package/dist/{chunk-MLKWJOAJ.js → chunk-CGPHMNL4.js} +2 -2
  45. package/dist/{chunk-R6OVFAX6.js → chunk-DJECGBBM.js} +7 -2
  46. package/dist/{chunk-R6OVFAX6.js.map → chunk-DJECGBBM.js.map} +1 -1
  47. package/dist/{chunk-3RCBIOOJ.js → chunk-ESF7TRYJ.js} +18 -2
  48. package/dist/{chunk-3RCBIOOJ.js.map → chunk-ESF7TRYJ.js.map} +1 -1
  49. package/dist/{chunk-EY6MDMHK.js → chunk-F6OEXJQQ.js} +2 -2
  50. package/dist/{chunk-M4V2JVSZ.js → chunk-FFUCCKPN.js} +2 -2
  51. package/dist/{chunk-O6YH5F47.js → chunk-FYWUNVFZ.js} +8 -8
  52. package/dist/{chunk-37436GML.js → chunk-GEXI357K.js} +2 -2
  53. package/dist/{chunk-YGIQORJ3.js → chunk-GGL34GCW.js} +3 -3
  54. package/dist/{chunk-4H4F3BFL.js → chunk-GXTYEG7Q.js} +3 -3
  55. package/dist/{chunk-4H4F3BFL.js.map → chunk-GXTYEG7Q.js.map} +1 -1
  56. package/dist/{chunk-WAS3NV46.js → chunk-H3MB3F62.js} +2 -2
  57. package/dist/{chunk-UTHQPSRC.js → chunk-HHNSLPX7.js} +11 -8
  58. package/dist/chunk-HHNSLPX7.js.map +1 -0
  59. package/dist/{chunk-76NHR6ZL.js → chunk-HJWQJUKQ.js} +4 -4
  60. package/dist/{chunk-Y4YD5TYW.js → chunk-IIBFCZA7.js} +3 -3
  61. package/dist/{chunk-UVPL565C.js → chunk-IQOEW5YV.js} +2 -2
  62. package/dist/{chunk-V4GNSDFH.js → chunk-KQYGNGON.js} +2 -2
  63. package/dist/{chunk-CBWI22JN.js → chunk-KSJCXXOU.js} +2 -2
  64. package/dist/{chunk-M4AYVEWS.js → chunk-LUCARK4W.js} +12 -11
  65. package/dist/chunk-LUCARK4W.js.map +1 -0
  66. package/dist/{chunk-I5REUWLH.js → chunk-NIZQH6AU.js} +3 -3
  67. package/dist/{chunk-4ESAR77N.js → chunk-NOU77OHX.js} +2 -2
  68. package/dist/{chunk-BQCHEV7O.js → chunk-O4ZIBPIW.js} +3 -3
  69. package/dist/{chunk-HOHVIVDR.js → chunk-OBJUKYDO.js} +2 -2
  70. package/dist/{chunk-YKBHOBJK.js → chunk-OEHCMALU.js} +56 -4
  71. package/dist/chunk-OEHCMALU.js.map +1 -0
  72. package/dist/{chunk-YOLZS7NY.js → chunk-OTQE66IX.js} +2 -2
  73. package/dist/{chunk-HEDZUSCS.js → chunk-OUITHS6V.js} +246 -368
  74. package/dist/chunk-OUITHS6V.js.map +1 -0
  75. package/dist/{chunk-4T3BMHKD.js → chunk-OZECYVGI.js} +20 -7
  76. package/dist/chunk-OZECYVGI.js.map +1 -0
  77. package/dist/{chunk-C2PG7IEH.js → chunk-P3VJCTVE.js} +2 -2
  78. package/dist/{chunk-EH5ED3C6.js → chunk-PMAQRUN4.js} +4 -4
  79. package/dist/chunk-PMAQRUN4.js.map +1 -0
  80. package/dist/{chunk-7RLCCNHU.js → chunk-Q6BAA57B.js} +4 -3
  81. package/dist/chunk-Q6BAA57B.js.map +1 -0
  82. package/dist/{chunk-HGNOCPZX.js → chunk-QZHF4BWN.js} +6 -6
  83. package/dist/chunk-SYX73XD7.js +283 -0
  84. package/dist/chunk-SYX73XD7.js.map +1 -0
  85. package/dist/{chunk-ASKXIAHD.js → chunk-WDJCZQB4.js} +2 -2
  86. package/dist/{chunk-AQLOP5F4.js → chunk-WNGXQMRI.js} +95 -6
  87. package/dist/chunk-WNGXQMRI.js.map +1 -0
  88. package/dist/{chunk-JYLFZWUX.js → chunk-XYUFGHUW.js} +2 -2
  89. package/dist/{chunk-3TSTGI6F.js → chunk-ZQUAVZNG.js} +2 -2
  90. package/dist/{chunk-4NR2PDZ5.js → chunk-ZVHRSR2R.js} +14 -4
  91. package/dist/chunk-ZVHRSR2R.js.map +1 -0
  92. package/dist/{cli-DmAiDd8A.d.ts → cli-Dt-tuiYH.d.ts} +3 -3
  93. package/dist/cli.d.ts +7 -6
  94. package/dist/cli.js +35 -35
  95. package/dist/compounding/engine.d.ts +12 -1
  96. package/dist/compounding/engine.js +3 -3
  97. package/dist/compounding/preference-consolidator.d.ts +1 -1
  98. package/dist/compression-optimizer.d.ts +1 -1
  99. package/dist/config.d.ts +1 -1
  100. package/dist/config.js +2 -2
  101. package/dist/connectors/codex-materialize-runner.d.ts +1 -1
  102. package/dist/connectors/codex-materialize-runner.js +3 -3
  103. package/dist/connectors/codex-materialize.d.ts +1 -1
  104. package/dist/connectors/index.d.ts +1 -1
  105. package/dist/connectors/index.js +3 -3
  106. package/dist/consolidation-provenance-check.d.ts +1 -1
  107. package/dist/consolidation-undo.d.ts +1 -1
  108. package/dist/contradiction/index.d.ts +1 -1
  109. package/dist/contradiction/index.js +1 -1
  110. package/dist/conversation-index/backend.d.ts +1 -1
  111. package/dist/conversation-index/chunker.d.ts +1 -1
  112. package/dist/conversation-index/faiss-adapter.d.ts +1 -1
  113. package/dist/conversation-index/indexer.d.ts +1 -1
  114. package/dist/conversation-index/search.d.ts +1 -1
  115. package/dist/day-summary.d.ts +1 -1
  116. package/dist/delinearize.d.ts +1 -1
  117. package/dist/direct-answer-wiring.d.ts +1 -1
  118. package/dist/direct-answer.d.ts +1 -1
  119. package/dist/embedding-fallback.d.ts +1 -1
  120. package/dist/embedding-fallback.js +2 -2
  121. package/dist/enrichment/index.d.ts +1 -1
  122. package/dist/entity-retrieval.d.ts +1 -1
  123. package/dist/entity-retrieval.js +3 -3
  124. package/dist/entity-schema.d.ts +1 -1
  125. package/dist/explicit-capture.d.ts +6 -4
  126. package/dist/explicit-capture.js +1 -1
  127. package/dist/extraction-faithfulness.d.ts +1 -1
  128. package/dist/extraction-judge-telemetry.d.ts +1 -1
  129. package/dist/extraction-judge-training.d.ts +1 -1
  130. package/dist/extraction-judge.d.ts +1 -1
  131. package/dist/extraction.d.ts +1 -1
  132. package/dist/extraction.js +3 -3
  133. package/dist/fallback-llm.d.ts +1 -1
  134. package/dist/{first-start-migration-22LPFHD5.js → first-start-migration-OOR57AYR.js} +2 -2
  135. package/dist/graph-dashboard-diff.d.ts +1 -1
  136. package/dist/graph-dashboard-key.d.ts +1 -1
  137. package/dist/graph-dashboard-parser.d.ts +1 -1
  138. package/dist/graph-edge-reinforcement.d.ts +1 -1
  139. package/dist/graph-snapshot.d.ts +1 -1
  140. package/dist/graph.d.ts +1 -1
  141. package/dist/identity-continuity.d.ts +1 -1
  142. package/dist/importance.d.ts +1 -1
  143. package/dist/index.d.ts +296 -11
  144. package/dist/index.js +359 -47
  145. package/dist/index.js.map +1 -1
  146. package/dist/intent.d.ts +1 -1
  147. package/dist/lcm/engine.d.ts +1 -1
  148. package/dist/lcm/index.d.ts +1 -1
  149. package/dist/lcm/tools.d.ts +1 -1
  150. package/dist/lifecycle.d.ts +1 -1
  151. package/dist/live-connectors-runner.d.ts +1 -1
  152. package/dist/local-llm.d.ts +1 -1
  153. package/dist/local-model-endpoint.d.ts +1 -1
  154. package/dist/maintenance/memory-governance.d.ts +1 -1
  155. package/dist/maintenance/memory-governance.js +3 -3
  156. package/dist/maintenance/rebuild-memory-lifecycle-ledger.js +3 -3
  157. package/dist/maintenance/rebuild-memory-projection.js +4 -4
  158. package/dist/mcp-memory-inspector-app.d.ts +6 -5
  159. package/dist/memory-action-policy.d.ts +1 -1
  160. package/dist/memory-cache.d.ts +1 -1
  161. package/dist/memory-lifecycle-ledger-utils.d.ts +1 -1
  162. package/dist/memory-projection-store.d.ts +1 -1
  163. package/dist/memory-provenance.d.ts +1 -1
  164. package/dist/memory-worth-outcomes.d.ts +1 -1
  165. package/dist/models-json.d.ts +1 -1
  166. package/dist/namespaces/migrate.d.ts +2 -2
  167. package/dist/namespaces/migrate.js +9 -9
  168. package/dist/namespaces/principal.d.ts +1 -1
  169. package/dist/namespaces/search.d.ts +1 -1
  170. package/dist/namespaces/search.js +5 -5
  171. package/dist/namespaces/storage.d.ts +2 -2
  172. package/dist/namespaces/storage.js +3 -3
  173. package/dist/native-knowledge.d.ts +1 -1
  174. package/dist/operator-toolkit.d.ts +1 -1
  175. package/dist/operator-toolkit.js +14 -14
  176. package/dist/orchestration/compression-guideline-coordinator.d.ts +105 -0
  177. package/dist/orchestration/compression-guideline-coordinator.js +12 -0
  178. package/dist/orchestration/maintenance.d.ts +2 -2
  179. package/dist/orchestration/maintenance.js +5 -5
  180. package/dist/{orchestrator-BR0sGsME.d.ts → orchestrator-B4KjaOtY.d.ts} +24 -8
  181. package/dist/orchestrator.d.ts +5 -4
  182. package/dist/orchestrator.js +28 -26
  183. package/dist/patterns-cli.d.ts +1 -1
  184. package/dist/policy-runtime.d.ts +1 -1
  185. package/dist/provenance.d.ts +1 -1
  186. package/dist/provenance.js +1 -1
  187. package/dist/qmd-recall-cache.d.ts +1 -1
  188. package/dist/qmd.d.ts +1 -1
  189. package/dist/recall-disclosure-escalation.d.ts +1 -1
  190. package/dist/recall-explain-renderer.d.ts +1 -1
  191. package/dist/recall-planner-llm.d.ts +1 -1
  192. package/dist/recall-state.d.ts +1 -1
  193. package/dist/recall-tag-filter.d.ts +1 -1
  194. package/dist/recall-xray-cli.d.ts +1 -1
  195. package/dist/recall-xray-renderer.d.ts +1 -1
  196. package/dist/recall-xray.d.ts +1 -1
  197. package/dist/{resolution-MN36NW5P.js → resolution-33PIX3TT.js} +2 -2
  198. package/dist/resolution-33PIX3TT.js.map +1 -0
  199. package/dist/resolve-auth-token.d.ts +1 -1
  200. package/dist/resume-bundles.js +3 -3
  201. package/dist/retrieval-agents.d.ts +1 -1
  202. package/dist/retrieval-tiers.d.ts +1 -1
  203. package/dist/routing/engine.d.ts +1 -1
  204. package/dist/routing/store.d.ts +1 -1
  205. package/dist/schemas.d.ts +84 -84
  206. package/dist/search/embed-helper.d.ts +1 -1
  207. package/dist/search/embed-helper.js +2 -2
  208. package/dist/search/factory.d.ts +1 -1
  209. package/dist/search/factory.js +4 -4
  210. package/dist/search/index.d.ts +1 -1
  211. package/dist/search/index.js +8 -8
  212. package/dist/search/lancedb-backend.d.ts +1 -1
  213. package/dist/search/meilisearch-backend.d.ts +1 -1
  214. package/dist/search/noop-backend.d.ts +1 -1
  215. package/dist/search/orama-backend.d.ts +1 -1
  216. package/dist/search/port.d.ts +1 -1
  217. package/dist/search/remote-backend.d.ts +1 -1
  218. package/dist/{semantic-consolidation-Cl15ksZy.d.ts → semantic-consolidation-CstTvHPo.d.ts} +1 -1
  219. package/dist/semantic-consolidation.d.ts +2 -2
  220. package/dist/semantic-consolidation.js +4 -4
  221. package/dist/semantic-rule-promotion.js +3 -3
  222. package/dist/semantic-rule-verifier.d.ts +1 -1
  223. package/dist/semantic-rule-verifier.js +3 -3
  224. package/dist/session-observer-bands.d.ts +1 -1
  225. package/dist/session-observer-state.d.ts +1 -1
  226. package/dist/shared-context/manager.d.ts +3 -3
  227. package/dist/signal.d.ts +1 -1
  228. package/dist/storage.d.ts +22 -3
  229. package/dist/storage.js +2 -2
  230. package/dist/summarizer.d.ts +1 -1
  231. package/dist/summary-snapshot.d.ts +1 -1
  232. package/dist/temporal-supersession.d.ts +1 -1
  233. package/dist/temporal-validity.d.ts +1 -1
  234. package/dist/threading.d.ts +1 -1
  235. package/dist/tier-migration.d.ts +1 -1
  236. package/dist/tier-routing.d.ts +1 -1
  237. package/dist/topics.d.ts +1 -1
  238. package/dist/transcript.d.ts +1 -1
  239. package/dist/transfer/types.d.ts +46 -46
  240. package/dist/trust-score-stage.d.ts +1 -1
  241. package/dist/trust-score.d.ts +1 -1
  242. package/dist/{types-ChQb9D6s.d.ts → types-VkxElCHR.d.ts} +8 -1
  243. package/dist/types.d.ts +1 -1
  244. package/dist/utility-runtime.d.ts +1 -1
  245. package/dist/verified-recall.js +3 -3
  246. package/package.json +12 -2
  247. package/src/access-service-coding-write.test.ts +2 -2
  248. package/src/access-service.ts +33 -14
  249. package/src/bitemporal-dedup-backfill.test.ts +10 -10
  250. package/src/bitemporal-frontmatter.test.ts +3 -3
  251. package/src/capabilities.test.ts +185 -0
  252. package/src/capabilities.ts +110 -0
  253. package/src/cli/creation-ledger-commands.ts +22 -21
  254. package/src/coding/architecture-surfaces.test.ts +25 -1
  255. package/src/coding/architecture-surfaces.ts +47 -19
  256. package/src/coding/decision-surfaces.ts +12 -3
  257. package/src/coding/review-context-structural.test.ts +303 -0
  258. package/src/coding/review-context.ts +79 -0
  259. package/src/coding/structural-context.test.ts +259 -0
  260. package/src/coding/structural-context.ts +315 -0
  261. package/src/coding/structural-subprocess-provider.test.ts +430 -0
  262. package/src/coding/structural-subprocess-provider.ts +355 -0
  263. package/src/compounding/engine.test.ts +90 -0
  264. package/src/compounding/engine.ts +25 -1
  265. package/src/contradiction/contradiction.test.ts +1 -1
  266. package/src/contradiction/resolution.ts +9 -1
  267. package/src/correction/correction-access-wiring.ts +21 -3
  268. package/src/correction/correction-contract.ts +7 -0
  269. package/src/correction/correction-planner.test.ts +231 -0
  270. package/src/correction/correction-planner.ts +73 -0
  271. package/src/correction/correction-service.ts +29 -0
  272. package/src/correction/correction-storage-integration.test.ts +2 -2
  273. package/src/dedup/semantic.test.ts +1 -1
  274. package/src/explicit-capture.ts +7 -4
  275. package/src/extraction-eventtime-wiring.test.ts +3 -3
  276. package/src/extraction-redaction-rules.test.ts +172 -0
  277. package/src/index.ts +29 -0
  278. package/src/maintenance/forget.test.ts +1 -1
  279. package/src/maintenance/memory-governance.test.ts +1 -1
  280. package/src/maintenance/pattern-reinforcement.test.ts +2 -2
  281. package/src/operator-toolkit.ts +4 -3
  282. package/src/orchestration/compression-guideline-coordinator.ts +449 -0
  283. package/src/orchestrator.ts +345 -464
  284. package/src/procedural/procedure-miner.test.ts +2 -2
  285. package/src/provenance-extraction.test.ts +382 -4
  286. package/src/provenance.ts +158 -22
  287. package/src/recall-handles-wiring.test.ts +1 -1
  288. package/src/search/factory.ts +4 -3
  289. package/src/semantic-rule-promotion.ts +1 -1
  290. package/src/source-attribution-roundtrip.test.ts +2 -2
  291. package/src/storage-contract/atomicity.test.ts +1 -1
  292. package/src/storage-contract/cache-coherence.test.ts +2 -2
  293. package/src/storage-contract/content-hash.test.ts +1 -1
  294. package/src/storage-contract/namespace-routing.test.ts +1 -1
  295. package/src/storage-contract/round-trip.test.ts +3 -3
  296. package/src/storage.ts +23 -2
  297. package/src/temporal-supersession.test.ts +12 -11
  298. package/src/temporal-validity.test.ts +11 -11
  299. package/src/wearables/cli.test.ts +30 -0
  300. package/src/wearables/cli.ts +3 -0
  301. package/src/wearables/memory-gen.test.ts +57 -1
  302. package/src/wearables/memory-gen.ts +31 -7
  303. package/src/wearables/pipeline.test.ts +40 -1
  304. package/src/wearables/pipeline.ts +4 -0
  305. package/src/wearables/service.test.ts +2 -2
  306. package/src/wearables/storage-io.test.ts +3 -3
  307. package/src/wearables/types.ts +7 -0
  308. package/dist/chunk-4NR2PDZ5.js.map +0 -1
  309. package/dist/chunk-4T3BMHKD.js.map +0 -1
  310. package/dist/chunk-7RLCCNHU.js.map +0 -1
  311. package/dist/chunk-AQLOP5F4.js.map +0 -1
  312. package/dist/chunk-EH5ED3C6.js.map +0 -1
  313. package/dist/chunk-HEDZUSCS.js.map +0 -1
  314. package/dist/chunk-M4AYVEWS.js.map +0 -1
  315. package/dist/chunk-UM5AWJNQ.js.map +0 -1
  316. package/dist/chunk-UTHQPSRC.js.map +0 -1
  317. package/dist/chunk-YKBHOBJK.js.map +0 -1
  318. /package/dist/{auto-sync-F3KCSQFJ.js.map → auto-sync-G3V5AN4C.js.map} +0 -0
  319. /package/dist/{chunk-4NOI577P.js.map → chunk-24ZLC5QN.js.map} +0 -0
  320. /package/dist/{chunk-DMQWV6SQ.js.map → chunk-5RZOFIDL.js.map} +0 -0
  321. /package/dist/{chunk-36SEDFAU.js.map → chunk-C5WN74HH.js.map} +0 -0
  322. /package/dist/{chunk-MLKWJOAJ.js.map → chunk-CGPHMNL4.js.map} +0 -0
  323. /package/dist/{chunk-EY6MDMHK.js.map → chunk-F6OEXJQQ.js.map} +0 -0
  324. /package/dist/{chunk-M4V2JVSZ.js.map → chunk-FFUCCKPN.js.map} +0 -0
  325. /package/dist/{chunk-O6YH5F47.js.map → chunk-FYWUNVFZ.js.map} +0 -0
  326. /package/dist/{chunk-37436GML.js.map → chunk-GEXI357K.js.map} +0 -0
  327. /package/dist/{chunk-YGIQORJ3.js.map → chunk-GGL34GCW.js.map} +0 -0
  328. /package/dist/{chunk-WAS3NV46.js.map → chunk-H3MB3F62.js.map} +0 -0
  329. /package/dist/{chunk-76NHR6ZL.js.map → chunk-HJWQJUKQ.js.map} +0 -0
  330. /package/dist/{chunk-Y4YD5TYW.js.map → chunk-IIBFCZA7.js.map} +0 -0
  331. /package/dist/{chunk-UVPL565C.js.map → chunk-IQOEW5YV.js.map} +0 -0
  332. /package/dist/{chunk-V4GNSDFH.js.map → chunk-KQYGNGON.js.map} +0 -0
  333. /package/dist/{chunk-CBWI22JN.js.map → chunk-KSJCXXOU.js.map} +0 -0
  334. /package/dist/{chunk-I5REUWLH.js.map → chunk-NIZQH6AU.js.map} +0 -0
  335. /package/dist/{chunk-4ESAR77N.js.map → chunk-NOU77OHX.js.map} +0 -0
  336. /package/dist/{chunk-BQCHEV7O.js.map → chunk-O4ZIBPIW.js.map} +0 -0
  337. /package/dist/{chunk-HOHVIVDR.js.map → chunk-OBJUKYDO.js.map} +0 -0
  338. /package/dist/{chunk-YOLZS7NY.js.map → chunk-OTQE66IX.js.map} +0 -0
  339. /package/dist/{chunk-C2PG7IEH.js.map → chunk-P3VJCTVE.js.map} +0 -0
  340. /package/dist/{chunk-HGNOCPZX.js.map → chunk-QZHF4BWN.js.map} +0 -0
  341. /package/dist/{chunk-ASKXIAHD.js.map → chunk-WDJCZQB4.js.map} +0 -0
  342. /package/dist/{chunk-JYLFZWUX.js.map → chunk-XYUFGHUW.js.map} +0 -0
  343. /package/dist/{chunk-3TSTGI6F.js.map → chunk-ZQUAVZNG.js.map} +0 -0
  344. /package/dist/{first-start-migration-22LPFHD5.js.map → first-start-migration-OOR57AYR.js.map} +0 -0
  345. /package/dist/{resolution-MN36NW5P.js.map → orchestration/compression-guideline-coordinator.js.map} +0 -0
@@ -491,3 +491,234 @@ test("#1678: never_store plan redacts the request text in the persisted pending-
491
491
  assert.ok(reloaded!.request.text.includes("redacted"));
492
492
  });
493
493
  });
494
+
495
+ // ---------------------------------------------------------------------------
496
+ // #1713 Item 2: stale applying plan recovery
497
+ // ---------------------------------------------------------------------------
498
+
499
+ test("#1713: markConsumed(applying) stamps applyingAt", async () => {
500
+ await withTempDir(async (dir) => {
501
+ const state: StubState = {
502
+ candidatesById: new Map([
503
+ ["mem-x", makeCandidate({ memoryId: "mem-x", content: "old fact" })],
504
+ ]),
505
+ llmResult: {
506
+ classification: "outdated",
507
+ confidence: 0.9,
508
+ actions: [{ kind: "supersede", loserId: "mem-x", replacement: { content: "new fact" } }],
509
+ relevance: [],
510
+ warnings: [],
511
+ },
512
+ writeReplacementCalls: 0,
513
+ propagateCalls: 0,
514
+ retireCalls: 0,
515
+ };
516
+ const planner = new CorrectionPlanner(makeDeps(dir, state));
517
+ const plan = await planner.plan({ text: "correct this" }, ["default"]);
518
+ await planner.markConsumed("default", plan.planId, "applying");
519
+ const reloaded = await planner.loadPlan("default", plan.planId);
520
+ assert.equal(reloaded!.status, "applying");
521
+ assert.ok(reloaded!.applyingAt, "applyingAt must be stamped when entering applying state");
522
+ });
523
+ });
524
+
525
+ test("#1713: markConsumed(applied) clears applyingAt", async () => {
526
+ await withTempDir(async (dir) => {
527
+ const state: StubState = {
528
+ candidatesById: new Map([
529
+ ["mem-x", makeCandidate({ memoryId: "mem-x", content: "old fact" })],
530
+ ]),
531
+ llmResult: {
532
+ classification: "outdated",
533
+ confidence: 0.9,
534
+ actions: [{ kind: "supersede", loserId: "mem-x", replacement: { content: "new fact" } }],
535
+ relevance: [],
536
+ warnings: [],
537
+ },
538
+ writeReplacementCalls: 0,
539
+ propagateCalls: 0,
540
+ retireCalls: 0,
541
+ };
542
+ const planner = new CorrectionPlanner(makeDeps(dir, state));
543
+ const plan = await planner.plan({ text: "correct this" }, ["default"]);
544
+ await planner.markConsumed("default", plan.planId, "applying");
545
+ await planner.markConsumed("default", plan.planId, "applied");
546
+ const reloaded = await planner.loadPlan("default", plan.planId);
547
+ assert.equal(reloaded!.status, "applied");
548
+ assert.ok(!reloaded!.applyingAt, "applyingAt must be cleared on terminal status");
549
+ });
550
+ });
551
+
552
+ test("#1713: recoverStaleApplyingPlans discards stale applying plans", async () => {
553
+ await withTempDir(async (dir) => {
554
+ const state: StubState = {
555
+ candidatesById: new Map([
556
+ ["mem-x", makeCandidate({ memoryId: "mem-x", content: "secret-token-1234" })],
557
+ ]),
558
+ llmResult: {
559
+ classification: "never_store",
560
+ confidence: 0.9,
561
+ actions: [{ kind: "redaction_rule", pattern: "secret-token-\\d+" }],
562
+ relevance: [],
563
+ warnings: [],
564
+ },
565
+ writeReplacementCalls: 0,
566
+ propagateCalls: 0,
567
+ retireCalls: 0,
568
+ };
569
+ const planTime = new Date("2026-03-15T12:00:00Z");
570
+ const recoveryTime = new Date("2026-03-15T13:00:00Z");
571
+ const deps = makeDeps(dir, state);
572
+ deps.now = () => planTime;
573
+ const planner = new CorrectionPlanner(deps);
574
+ const plan = await planner.plan({ text: "never store this secret", targetIds: ["mem-x"] }, ["default"]);
575
+ await planner.markConsumed("default", plan.planId, "applying");
576
+
577
+ let reloaded = await planner.loadPlan("default", plan.planId);
578
+ assert.equal(reloaded!.status, "applying");
579
+
580
+ const recovered = await planner.recoverStaleApplyingPlans("default", { now: recoveryTime });
581
+ assert.equal(recovered.length, 1);
582
+ assert.equal(recovered[0], plan.planId);
583
+
584
+ // Read the raw file (loadPlan/parsePlan rejects the scrubbed pattern by
585
+ // design — validation rejects placeholder patterns on re-load).
586
+ const { readFile: rf } = await import("node:fs/promises");
587
+ const rawPlan = JSON.parse(
588
+ await rf(dir + "/state/corrections/pending/" + plan.planId + ".json", "utf-8"),
589
+ );
590
+ assert.equal(rawPlan.status, "discarded");
591
+ assert.ok(!rawPlan.applyingAt, "applyingAt cleared on discard");
592
+ const redactionAction = rawPlan.actions.find(
593
+ (a: { kind: string }) => a.kind === "redaction_rule",
594
+ );
595
+ assert.ok(redactionAction, "redaction_rule action should still exist");
596
+ assert.match(String(redactionAction.pattern), /redacted/);
597
+ });
598
+ });
599
+
600
+ test("#1713: recoverStaleApplyingPlans leaves fresh applying plans alone", async () => {
601
+ await withTempDir(async (dir) => {
602
+ const state: StubState = {
603
+ candidatesById: new Map([
604
+ ["mem-x", makeCandidate({ memoryId: "mem-x", content: "old fact" })],
605
+ ]),
606
+ llmResult: {
607
+ classification: "outdated",
608
+ confidence: 0.9,
609
+ actions: [{ kind: "supersede", loserId: "mem-x", replacement: { content: "new fact" } }],
610
+ relevance: [],
611
+ warnings: [],
612
+ },
613
+ writeReplacementCalls: 0,
614
+ propagateCalls: 0,
615
+ retireCalls: 0,
616
+ };
617
+ const fixedNow = new Date("2026-03-15T12:00:00Z");
618
+ const deps = makeDeps(dir, state);
619
+ const planner = new CorrectionPlanner(deps);
620
+ const plan = await planner.plan({ text: "correct this" }, ["default"]);
621
+ await planner.markConsumed("default", plan.planId, "applying");
622
+
623
+ const recovered = await planner.recoverStaleApplyingPlans("default", {
624
+ now: new Date(fixedNow.getTime() + 2 * 60 * 1000),
625
+ });
626
+ assert.equal(recovered.length, 0);
627
+
628
+ const reloaded = await planner.loadPlan("default", plan.planId);
629
+ assert.equal(reloaded!.status, "applying");
630
+ });
631
+ });
632
+
633
+ test("#1713: recoverStaleApplyingPlans returns empty for namespace with no plans", async () => {
634
+ await withTempDir(async (dir) => {
635
+ const state: StubState = {
636
+ candidatesById: new Map(),
637
+ llmResult: { classification: "outdated", confidence: 0.5, actions: [], relevance: [], warnings: [] },
638
+ writeReplacementCalls: 0,
639
+ propagateCalls: 0,
640
+ retireCalls: 0,
641
+ };
642
+ const planner = new CorrectionPlanner(makeDeps(dir, state));
643
+ const recovered = await planner.recoverStaleApplyingPlans("default");
644
+ assert.deepEqual(recovered, []);
645
+ });
646
+ });
647
+
648
+ test("#1713: recoverStaleApplyingPlans recovers pre-fix plans via expiresAt fallback", async () => {
649
+ await withTempDir(async (dir) => {
650
+ const state: StubState = {
651
+ candidatesById: new Map([
652
+ ["mem-x", makeCandidate({ memoryId: "mem-x", content: "old fact" })],
653
+ ]),
654
+ llmResult: {
655
+ classification: "outdated",
656
+ confidence: 0.9,
657
+ actions: [{ kind: "supersede", loserId: "mem-x", replacement: { content: "new fact" } }],
658
+ relevance: [],
659
+ warnings: [],
660
+ },
661
+ writeReplacementCalls: 0,
662
+ propagateCalls: 0,
663
+ retireCalls: 0,
664
+ };
665
+ const fixedNow = new Date("2026-03-15T12:00:00Z");
666
+ const deps = makeDeps(dir, state);
667
+ const planner = new CorrectionPlanner(deps);
668
+ const plan = await planner.plan({ text: "correct this" }, ["default"]);
669
+
670
+ await planner.markConsumed("default", plan.planId, "applying");
671
+ // Simulate a pre-fix plan: manually strip applyingAt from the file
672
+ const { readFile: rf, writeFile: wf } = await import("node:fs/promises");
673
+ const planFile = dir + "/state/corrections/pending/" + plan.planId + ".json";
674
+ const planJson = JSON.parse(await rf(planFile, "utf-8"));
675
+ delete planJson.applyingAt;
676
+ await wf(planFile, JSON.stringify(planJson) + "\n", "utf-8");
677
+
678
+ // Recovery 25h later (past the 24h expiry, no applyingAt → falls back to expiresAt)
679
+ const recovered = await planner.recoverStaleApplyingPlans("default", {
680
+ now: new Date(fixedNow.getTime() + 25 * 60 * 60 * 1000),
681
+ });
682
+ assert.equal(recovered.length, 1);
683
+ assert.equal(recovered[0], plan.planId);
684
+ });
685
+ });
686
+
687
+ test("#1713: recoverStaleApplyingPlans recovers pre-fix plans immediately at expiry (no extra TTL wait)", async () => {
688
+ await withTempDir(async (dir) => {
689
+ const state: StubState = {
690
+ candidatesById: new Map([
691
+ ["mem-x", makeCandidate({ memoryId: "mem-x", content: "old fact" })],
692
+ ]),
693
+ llmResult: {
694
+ classification: "outdated",
695
+ confidence: 0.9,
696
+ actions: [{ kind: "supersede", loserId: "mem-x", replacement: { content: "new fact" } }],
697
+ relevance: [],
698
+ warnings: [],
699
+ },
700
+ writeReplacementCalls: 0,
701
+ propagateCalls: 0,
702
+ retireCalls: 0,
703
+ };
704
+ const fixedNow = new Date("2026-03-15T12:00:00Z");
705
+ const deps = makeDeps(dir, state);
706
+ const planner = new CorrectionPlanner(deps);
707
+ const plan = await planner.plan({ text: "correct this" }, ["default"]);
708
+
709
+ await planner.markConsumed("default", plan.planId, "applying");
710
+ // Simulate a pre-fix plan: manually strip applyingAt from the file
711
+ const { readFile: rf, writeFile: wf } = await import("node:fs/promises");
712
+ const planFile = dir + "/state/corrections/pending/" + plan.planId + ".json";
713
+ const planJson = JSON.parse(await rf(planFile, "utf-8"));
714
+ delete planJson.applyingAt;
715
+ await wf(planFile, JSON.stringify(planJson) + "\n", "utf-8");
716
+
717
+ // Recovery at exactly expiresAt: plan is already expired -> recover now
718
+ // (review thread ff034716: no extra TTL wait for pre-fix plans)
719
+ const recovered = await planner.recoverStaleApplyingPlans("default", {
720
+ now: new Date(planJson.expiresAt),
721
+ });
722
+ assert.equal(recovered.length, 1, "pre-fix plan at expiry must be recovered immediately");
723
+ });
724
+ });
@@ -478,6 +478,15 @@ export class CorrectionPlanner {
478
478
  const plan = parsePlan(raw);
479
479
  if (!plan) return;
480
480
  plan.status = status;
481
+ // #1713 Item 2: stamp applyingAt when entering the applying state so
482
+ // startup recovery can detect plans stuck mid-apply (process death).
483
+ // Cleared on terminal transitions; harmless residue on non-applying
484
+ // plans is ignored by the recovery scan.
485
+ if (status === "applying") {
486
+ plan.applyingAt = this.deps.now().toISOString();
487
+ } else {
488
+ delete plan.applyingAt;
489
+ }
481
490
  // #1669 thread P1: scrub redaction_rule patterns from consumed plans so
482
491
  // a never-store pattern does not persist on disk after apply/discard.
483
492
  // The executor reloads via loadPlan only between "applying" (step 1) and
@@ -508,6 +517,70 @@ export class CorrectionPlanner {
508
517
  }
509
518
  });
510
519
  }
520
+
521
+ /**
522
+ * Stale-applying TTL: a plan in `applying` state longer than this is
523
+ * assumed to be from a crashed process and is recovered on startup
524
+ * (#1713 Item 2). 10 minutes is generous for an apply that normally
525
+ * completes in seconds, yet short enough to avoid leaving stale state.
526
+ */
527
+ static readonly STALE_APPLYING_TTL_MS = 10 * 60 * 1000;
528
+
529
+ /**
530
+ * Recover stale `applying` plans past the stale-applying TTL (#1713 Item 2).
531
+ *
532
+ * A plan stuck in `applying` means the process died mid-apply. Re-applying
533
+ * wholesale would duplicate succeeded actions (replacements, tombstones,
534
+ * audits), so recovery discards + scrubs the plan instead. The operator
535
+ * inspects the outcome and files a NEW plan for any actions that did not
536
+ * complete. Plans without `applyingAt` (predating this fix) are recovered
537
+ * when they are past their `expiresAt` timestamp.
538
+ *
539
+ * @returns the ids of recovered (discarded) plans.
540
+ */
541
+ async recoverStaleApplyingPlans(
542
+ namespace: string,
543
+ opts?: { ttlMs?: number; now?: Date },
544
+ ): Promise<string[]> {
545
+ const ttl = opts?.ttlMs ?? CorrectionPlanner.STALE_APPLYING_TTL_MS;
546
+ const now = (opts?.now ?? this.deps.now()).getTime();
547
+ const dir = await this.pendingDir(namespace);
548
+ let files: string[];
549
+ try {
550
+ files = await readdir(dir);
551
+ } catch (err) {
552
+ const code = (err as NodeJS.ErrnoException)?.code;
553
+ if (code === "ENOENT") return [];
554
+ throw err;
555
+ }
556
+ const recovered: string[] = [];
557
+ for (const f of files) {
558
+ if (!f.endsWith(".json")) continue;
559
+ const planId = f.replace(/\.json$/, "");
560
+ let plan: CorrectionPlan | null;
561
+ try {
562
+ plan = await this.loadPlan(namespace, planId);
563
+ } catch {
564
+ continue; // malformed — skip (rule 34).
565
+ }
566
+ if (!plan || plan.status !== "applying") continue;
567
+ // Post-fix plans (with applyingAt): stale if the applying timestamp is
568
+ // past the TTL (process died mid-apply). Pre-fix plans (no applyingAt):
569
+ // stale as soon as they have expired — no additional TTL wait, since the
570
+ // plan is already past its intended lifetime (review thread ff034716).
571
+ const stale = plan.applyingAt
572
+ ? now - new Date(plan.applyingAt).getTime() >= ttl
573
+ : now >= new Date(plan.expiresAt).getTime();
574
+ if (!stale) continue;
575
+ try {
576
+ await this.markConsumed(namespace, planId, "discarded");
577
+ recovered.push(planId);
578
+ } catch {
579
+ // Best-effort: a single failed discard must not abort the scan.
580
+ }
581
+ }
582
+ return recovered;
583
+ }
511
584
  }
512
585
 
513
586
  // ---------------------------------------------------------------------------
@@ -170,6 +170,35 @@ export class CorrectionService {
170
170
  await this.planner.deletePlan(namespace, planId);
171
171
  }
172
172
 
173
+ /**
174
+ * Recover stale `applying` plans across the given namespaces (#1713 Item 2).
175
+ *
176
+ * Startup maintenance: scans each namespace for plans stuck in `applying`
177
+ * past the stale-applying TTL (process died mid-apply) and discards+scrubs
178
+ * them. Unlike plan/apply/listPending, this is NOT a per-caller request —
179
+ * it operates on all supplied namespaces directly because it is an
180
+ * internal maintenance operation invoked by the orchestrator at init.
181
+ *
182
+ * @returns the total count of recovered plans across all namespaces.
183
+ */
184
+ async recoverStaleApplyingPlans(namespaces: readonly string[]): Promise<number> {
185
+ // Intentionally NOT gated on isEnabled(): stale-applying recovery is a
186
+ // maintenance/cleanup operation, not a user-facing correction. Plans left
187
+ // in 'applying' after a mid-apply crash hold unscrubbed redaction_rule
188
+ // patterns on disk; they must be discarded+scrubbed even if the correction
189
+ // feature is currently disabled (review thread 9e1f07f0).
190
+ let recovered = 0;
191
+ for (const namespace of namespaces) {
192
+ try {
193
+ const ids = await this.planner.recoverStaleApplyingPlans(namespace);
194
+ recovered += ids.length;
195
+ } catch {
196
+ // Best-effort: a single namespace failure must not abort the sweep.
197
+ }
198
+ }
199
+ return recovered;
200
+ }
201
+
173
202
  private requireEnabled(): void {
174
203
  if (!this.deps.isEnabled()) {
175
204
  throw new CorrectionContractError(
@@ -86,7 +86,7 @@ test("#1672 item 3: applyEditMemory recomputes contentHash from the patched body
86
86
  const { storage, cleanup } = await makeStorage();
87
87
  try {
88
88
  const wiring = { orchestrator: { getStorage: async () => storage } } as any;
89
- const id = await storage.writeMemory("fact", "the database is MySQL", { source: "test" });
89
+ const { id: id } = await storage.writeMemory("fact", "the database is MySQL", { source: "test" });
90
90
  const before = await storage.getMemoryById(id);
91
91
  assert.ok(before?.frontmatter.contentHash, "writeMemory must set an initial contentHash");
92
92
  const oldHash = before!.frontmatter.contentHash!;
@@ -110,7 +110,7 @@ test("#1672 item 3: applyEditMemory leaves non-fact contentHash untouched (only
110
110
  const { storage, cleanup } = await makeStorage();
111
111
  try {
112
112
  const wiring = { orchestrator: { getStorage: async () => storage } } as any;
113
- const id = await storage.writeMemory("preference", "likes dark mode", { source: "test" });
113
+ const { id: id } = await storage.writeMemory("preference", "likes dark mode", { source: "test" });
114
114
  await applyEditMemory(wiring, "default", id, "likes light mode");
115
115
  const after = await storage.getMemoryById(id);
116
116
  assert.equal(after!.content, "likes light mode");
@@ -901,7 +901,7 @@ test("round 6: chunked parent writeMemory carries supersedes in frontmatter", as
901
901
  const storage = new StorageManager(memDir);
902
902
  const OLD_ID = "fact-old-abc-123";
903
903
 
904
- const newId = await storage.writeMemory("fact", "the user prefers dark mode", {
904
+ const { id: newId } = await storage.writeMemory("fact", "the user prefers dark mode", {
905
905
  confidence: 0.9,
906
906
  tags: ["chunked", "preference"],
907
907
  supersedes: OLD_ID,
@@ -418,7 +418,7 @@ export async function persistExplicitCapture(
418
418
  orchestrator: Orchestrator,
419
419
  candidate: ValidExplicitCapture,
420
420
  source: ExplicitCaptureSource,
421
- ): Promise<{ id: string; duplicateOf?: string }> {
421
+ ): Promise<{ id: string; duplicateOf?: string; tombstoneBlocked?: boolean }> {
422
422
  const resolvedNamespace = candidate.namespacePreResolved
423
423
  ? asTrimmed(candidate.namespace)
424
424
  : resolveExplicitCaptureNamespace(orchestrator, candidate.namespace);
@@ -428,7 +428,10 @@ export async function persistExplicitCapture(
428
428
  }
429
429
 
430
430
  const storage = await orchestrator.getStorage(resolvedNamespace);
431
- const id = await storage.writeMemory(candidate.category, candidate.content, {
431
+ // #1645 (review thread yG-): surface the tombstone block so callers
432
+ // (memory_store tool, access-service HTTP/MCP) can report the capture as
433
+ // queued for review instead of a successfully stored active memory.
434
+ const { id, tombstoneBlocked } = await storage.writeMemory(candidate.category, candidate.content, {
432
435
  confidence: candidate.confidence,
433
436
  tags: candidate.tags,
434
437
  entityRef: candidate.entityRef,
@@ -450,7 +453,7 @@ export async function persistExplicitCapture(
450
453
  };
451
454
  await storage.appendMemoryLifecycleEvents([event]);
452
455
 
453
- return { id };
456
+ return { id, tombstoneBlocked };
454
457
  }
455
458
 
456
459
  function buildExplicitCaptureReviewContent(input: ExplicitCaptureInput, reason: string): string {
@@ -527,7 +530,7 @@ export async function queueExplicitCaptureForReview(
527
530
  : "fact";
528
531
  const requestedTags = sanitizeReviewTags(input.tags);
529
532
  const storage = await orchestrator.getStorage(queueNamespace);
530
- const id = await storage.writeMemory(reviewCategory, content, {
533
+ const { id: id } = await storage.writeMemory(reviewCategory, content, {
531
534
  confidence: 0.2,
532
535
  tags: Array.from(new Set([...EXPLICIT_CAPTURE_REVIEW_TAGS, ...requestedTags])),
533
536
  entityRef: sanitizeReviewMetadata(input.entityRef),
@@ -153,7 +153,7 @@ test("storage: invalid_at round-trips through writeMemoryFrontmatter → readAll
153
153
  const storage = new StorageManager(dir);
154
154
  await storage.ensureDirectories();
155
155
 
156
- const id = await storage.writeMemory("fact", "Old stack was Node 18.", {
156
+ const { id: id } = await storage.writeMemory("fact", "Old stack was Node 18.", {
157
157
  validAt: "2025-01-01T00:00:00.000Z",
158
158
  observedAt: "2025-06-01T00:00:00.000Z",
159
159
  eventTimeSource: "extracted",
@@ -188,7 +188,7 @@ test("storage: writeMemory accepts invalidAt directly (end-bound at extraction w
188
188
  try {
189
189
  const storage = new StorageManager(dir);
190
190
  await storage.ensureDirectories();
191
- const id = await storage.writeMemory("fact", "We used MongoDB until June 2025.", {
191
+ const { id: id } = await storage.writeMemory("fact", "We used MongoDB until June 2025.", {
192
192
  validAt: "2024-01-01T00:00:00.000Z",
193
193
  invalidAt: "2025-06-01T00:00:00.000Z",
194
194
  observedAt: "2025-06-20T00:00:00.000Z",
@@ -222,7 +222,7 @@ test("storage: writeChunk propagates invalidAt/observedAt/eventTimeSource from t
222
222
  try {
223
223
  const storage = new StorageManager(dir);
224
224
  await storage.ensureDirectories();
225
- const parentId = await storage.writeMemory("fact", "We used MongoDB until June 2025.", {
225
+ const { id: parentId } = await storage.writeMemory("fact", "We used MongoDB until June 2025.", {
226
226
  validAt: "2024-01-01T00:00:00.000Z",
227
227
  invalidAt: "2025-06-01T00:00:00.000Z",
228
228
  observedAt: "2025-06-20T00:00:00.000Z",
@@ -16,6 +16,7 @@ import {
16
16
  compileRedactionPattern,
17
17
  contentMatchesRedactionRules,
18
18
  loadRedactionRules,
19
+ type CompiledRedactionRule,
19
20
  } from "./extraction-redaction-rules.js";
20
21
  import { validateRedactionPattern } from "./correction/correction-contract.js";
21
22
 
@@ -211,3 +212,174 @@ test("#1669 P2: compileRedactionPattern empty body never matches", () => {
211
212
  assert.equal(rule.matcher("anything"), false);
212
213
  assert.equal(rule.matcher(""), false);
213
214
  });
215
+
216
+ // ---------------------------------------------------------------------------
217
+ // #1713 Item 1: pre-judge redaction filter consults target namespace rules.
218
+ // The orchestrator's pre-judge filter builds a combined rule set from source +
219
+ // shared + routed target namespace dirs. This test verifies that loadRedactionRules
220
+ // correctly returns rules from each dir and that contentMatchesRedactionRules
221
+ // catches patterns from ANY of the combined dirs (not just the source).
222
+ // ---------------------------------------------------------------------------
223
+
224
+ test("#1713 Item 1: combined rules from source + target dirs catch cross-namespace patterns", async () => {
225
+ await withTempDir(async (sourceDir) => {
226
+ await withTempDir(async (targetDir) => {
227
+ // Source namespace has a rule for "source-secret"
228
+ await writeRule(sourceDir, "source-secret");
229
+ // Target namespace has a rule for "target-secret"
230
+ await writeRule(targetDir, "target-confidential");
231
+
232
+ // Simulate what the orchestrator's redactionRulesFor(...dirs) does:
233
+ // load from each dir and combine.
234
+ const sourceRules = await loadRedactionRules(sourceDir);
235
+ const targetRules = await loadRedactionRules(targetDir);
236
+ const combined = [...sourceRules, ...targetRules];
237
+
238
+ assert.equal(combined.length, 2);
239
+ // Source-namespace rule catches source content
240
+ assert.ok(
241
+ contentMatchesRedactionRules("leaked source-secret here", combined),
242
+ "source-namespace rule must catch matching content",
243
+ );
244
+ // Target-namespace rule catches target content (the #1713 fix)
245
+ assert.ok(
246
+ contentMatchesRedactionRules("leaked target-confidential here", combined),
247
+ "target-namespace rule must catch matching content when dirs are combined",
248
+ );
249
+ // Clean content passes
250
+ assert.ok(
251
+ !contentMatchesRedactionRules("clean content", combined),
252
+ "non-matching content must not be redacted",
253
+ );
254
+ });
255
+ });
256
+ });
257
+
258
+ test("#1713 Item 1: source-only rules miss target-namespace patterns (the bug this fixes)", async () => {
259
+ await withTempDir(async (sourceDir) => {
260
+ await withTempDir(async (targetDir) => {
261
+ // Only the TARGET namespace has the redaction rule
262
+ await writeRule(targetDir, "cross-ns-secret");
263
+
264
+ // The OLD behavior (pre-fix): only source rules loaded at pre-judge point
265
+ const sourceOnlyRules = await loadRedactionRules(sourceDir);
266
+ assert.equal(sourceOnlyRules.length, 0);
267
+ assert.ok(
268
+ !contentMatchesRedactionRules("cross-ns-secret in content", sourceOnlyRules),
269
+ "source-only rules miss the target-namespace pattern (the pre-fix gap)",
270
+ );
271
+
272
+ // The NEW behavior: source + target rules combined
273
+ const combined = [
274
+ ...(await loadRedactionRules(sourceDir)),
275
+ ...(await loadRedactionRules(targetDir)),
276
+ ];
277
+ assert.equal(combined.length, 1);
278
+ assert.ok(
279
+ contentMatchesRedactionRules("cross-ns-secret in content", combined),
280
+ "combined rules catch the target-namespace pattern (the fix)",
281
+ );
282
+ });
283
+ });
284
+ });
285
+
286
+ test("#1713 (codex PRRT_kwDORJXyws6PBj5X): scope-classification routed shared target rules consulted at pre-judge", async () => {
287
+ // Scope classification routes a scope=global fact to the shared namespace
288
+ // independent of routing rules. The pre-judge redaction filter must consult
289
+ // the SHARED namespace's rules for such a fact, or a never-store pattern
290
+ // registered only under shared is missed until the write gate and the content
291
+ // reaches the extraction judge/training path. This test mirrors the
292
+ // orchestrator's per-fact scope-routing decision + rule combining.
293
+ await withTempDir(async (sourceDir) => {
294
+ await withTempDir(async (sharedDir) => {
295
+ // Only the SHARED namespace carries the never-store rule.
296
+ await writeRule(sharedDir, "shared-only-secret");
297
+
298
+ // A scope=global fact the orchestrator will route to shared.
299
+ const fact = { scope: "global" as string, content: "leaked shared-only-secret here" };
300
+ const sharedNamespace: string = "shared";
301
+ const sourceNamespace: string = "default"; // source !== shared
302
+
303
+ // Mirror of the orchestrator's scope-routing condition (persistExtraction
304
+ // pre-judge per-fact block): a fact is scope-routed to shared when no
305
+ // routing rule set an explicit namespace, scope classification is on, the
306
+ // fact is global, shared writes are allowed, and the source is not already
307
+ // shared.
308
+ const preRoutedNamespaceByFact: string | undefined = undefined;
309
+ const scopeClassificationEnabled = true;
310
+ const namespacesEnabled = true;
311
+ const profileAllowsSharedWrites = true;
312
+ let factNs: string | undefined = preRoutedNamespaceByFact;
313
+ if (
314
+ !factNs &&
315
+ scopeClassificationEnabled &&
316
+ namespacesEnabled &&
317
+ fact.scope === "global" &&
318
+ profileAllowsSharedWrites &&
319
+ sourceNamespace !== sharedNamespace
320
+ ) {
321
+ factNs = sharedNamespace;
322
+ }
323
+ assert.equal(factNs, sharedNamespace, "global fact must be scope-routed to shared");
324
+
325
+ // Base (source-only) rules + routed target (shared) rules combined — what
326
+ // the orchestrator builds for this fact.
327
+ const baseRules: CompiledRedactionRule[] = await loadRedactionRules(sourceDir);
328
+ const targetRules: CompiledRedactionRule[] =
329
+ factNs === sharedNamespace ? await loadRedactionRules(sharedDir) : [];
330
+ const factRedactionRules: CompiledRedactionRule[] = [...baseRules, ...targetRules];
331
+
332
+ assert.equal(baseRules.length, 0, "source namespace has no rules");
333
+ assert.equal(targetRules.length, 1, "shared namespace carries the never-store rule");
334
+
335
+ // Pre-fix (source-only) behavior: the shared-only pattern is missed at
336
+ // pre-judge — the gap the codex thread flags.
337
+ assert.ok(
338
+ !contentMatchesRedactionRules(fact.content, baseRules),
339
+ "source-only rules miss the shared-namespace never-store pattern (the gap)",
340
+ );
341
+ // Post-fix: combined source+shared rules catch it before the judge.
342
+ assert.ok(
343
+ contentMatchesRedactionRules(fact.content, factRedactionRules),
344
+ "combined source+shared rules catch the shared never-store at pre-judge (the fix)",
345
+ );
346
+ });
347
+ });
348
+ });
349
+
350
+ test("#1713 (codex): non-global fact is NOT scope-routed to shared", async () => {
351
+ // A scope-local fact must not consult shared rules at pre-judge — only
352
+ // routing-rule targets and scope=global facts do. Guards against over-redacting
353
+ // unrelated facts with shared-only never-store patterns (codex sibling threads).
354
+ await withTempDir(async (sourceDir) => {
355
+ await withTempDir(async (sharedDir) => {
356
+ await writeRule(sharedDir, "shared-only-secret");
357
+
358
+ const fact = { scope: "local" as string, content: "leaked shared-only-secret here" };
359
+ const sharedNamespace: string = "shared";
360
+ const sourceNamespace: string = "default";
361
+
362
+ const preRoutedNamespaceByFact: string | undefined = undefined;
363
+ const scopeClassificationEnabled = true;
364
+ const namespacesEnabled = true;
365
+ const profileAllowsSharedWrites = true;
366
+ let factNs: string | undefined = preRoutedNamespaceByFact;
367
+ if (
368
+ !factNs &&
369
+ scopeClassificationEnabled &&
370
+ namespacesEnabled &&
371
+ fact.scope === "global" &&
372
+ profileAllowsSharedWrites &&
373
+ sourceNamespace !== sharedNamespace
374
+ ) {
375
+ factNs = sharedNamespace;
376
+ }
377
+ assert.equal(factNs, undefined, "non-global fact must not be scope-routed to shared");
378
+
379
+ const baseRules: CompiledRedactionRule[] = await loadRedactionRules(sourceDir);
380
+ const targetRules: CompiledRedactionRule[] = [];
381
+ const factRedactionRules: CompiledRedactionRule[] = [...baseRules, ...targetRules];
382
+ assert.equal(factRedactionRules.length, 0, "no rules consulted for a scope-local fact");
383
+ });
384
+ });
385
+ });