@remnic/core 9.3.728 → 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 (348) 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 +7 -6
  11. package/dist/access-operations.js +17 -17
  12. package/dist/access-schema.d.ts +4 -4
  13. package/dist/{access-service-Bgpjxo4f.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-QNEUZIAH.js → chunk-OUITHS6V.js} +381 -433
  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-BLME420h.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-DqPAz8ot.d.ts → orchestrator-B4KjaOtY.d.ts} +128 -34
  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 +32 -32
  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 +1 -1
  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 +22 -22
  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/orchestration/extraction-queue-coordinator.ts +175 -0
  284. package/src/orchestrator-extraction-queue.test.ts +26 -38
  285. package/src/orchestrator-flush.test.ts +10 -15
  286. package/src/orchestrator.ts +359 -542
  287. package/src/procedural/procedure-miner.test.ts +2 -2
  288. package/src/provenance-extraction.test.ts +382 -4
  289. package/src/provenance.ts +158 -22
  290. package/src/recall-handles-wiring.test.ts +1 -1
  291. package/src/search/factory.ts +4 -3
  292. package/src/semantic-rule-promotion.ts +1 -1
  293. package/src/source-attribution-roundtrip.test.ts +2 -2
  294. package/src/storage-contract/atomicity.test.ts +1 -1
  295. package/src/storage-contract/cache-coherence.test.ts +2 -2
  296. package/src/storage-contract/content-hash.test.ts +1 -1
  297. package/src/storage-contract/namespace-routing.test.ts +1 -1
  298. package/src/storage-contract/round-trip.test.ts +3 -3
  299. package/src/storage.ts +23 -2
  300. package/src/temporal-supersession.test.ts +12 -11
  301. package/src/temporal-validity.test.ts +11 -11
  302. package/src/wearables/cli.test.ts +30 -0
  303. package/src/wearables/cli.ts +3 -0
  304. package/src/wearables/memory-gen.test.ts +57 -1
  305. package/src/wearables/memory-gen.ts +31 -7
  306. package/src/wearables/pipeline.test.ts +40 -1
  307. package/src/wearables/pipeline.ts +4 -0
  308. package/src/wearables/service.test.ts +2 -2
  309. package/src/wearables/storage-io.test.ts +3 -3
  310. package/src/wearables/types.ts +7 -0
  311. package/dist/chunk-4NR2PDZ5.js.map +0 -1
  312. package/dist/chunk-4T3BMHKD.js.map +0 -1
  313. package/dist/chunk-7RLCCNHU.js.map +0 -1
  314. package/dist/chunk-AQLOP5F4.js.map +0 -1
  315. package/dist/chunk-EH5ED3C6.js.map +0 -1
  316. package/dist/chunk-M4AYVEWS.js.map +0 -1
  317. package/dist/chunk-QNEUZIAH.js.map +0 -1
  318. package/dist/chunk-UM5AWJNQ.js.map +0 -1
  319. package/dist/chunk-UTHQPSRC.js.map +0 -1
  320. package/dist/chunk-YKBHOBJK.js.map +0 -1
  321. /package/dist/{auto-sync-F3KCSQFJ.js.map → auto-sync-G3V5AN4C.js.map} +0 -0
  322. /package/dist/{chunk-4NOI577P.js.map → chunk-24ZLC5QN.js.map} +0 -0
  323. /package/dist/{chunk-DMQWV6SQ.js.map → chunk-5RZOFIDL.js.map} +0 -0
  324. /package/dist/{chunk-36SEDFAU.js.map → chunk-C5WN74HH.js.map} +0 -0
  325. /package/dist/{chunk-MLKWJOAJ.js.map → chunk-CGPHMNL4.js.map} +0 -0
  326. /package/dist/{chunk-EY6MDMHK.js.map → chunk-F6OEXJQQ.js.map} +0 -0
  327. /package/dist/{chunk-M4V2JVSZ.js.map → chunk-FFUCCKPN.js.map} +0 -0
  328. /package/dist/{chunk-O6YH5F47.js.map → chunk-FYWUNVFZ.js.map} +0 -0
  329. /package/dist/{chunk-37436GML.js.map → chunk-GEXI357K.js.map} +0 -0
  330. /package/dist/{chunk-YGIQORJ3.js.map → chunk-GGL34GCW.js.map} +0 -0
  331. /package/dist/{chunk-WAS3NV46.js.map → chunk-H3MB3F62.js.map} +0 -0
  332. /package/dist/{chunk-76NHR6ZL.js.map → chunk-HJWQJUKQ.js.map} +0 -0
  333. /package/dist/{chunk-Y4YD5TYW.js.map → chunk-IIBFCZA7.js.map} +0 -0
  334. /package/dist/{chunk-UVPL565C.js.map → chunk-IQOEW5YV.js.map} +0 -0
  335. /package/dist/{chunk-V4GNSDFH.js.map → chunk-KQYGNGON.js.map} +0 -0
  336. /package/dist/{chunk-CBWI22JN.js.map → chunk-KSJCXXOU.js.map} +0 -0
  337. /package/dist/{chunk-I5REUWLH.js.map → chunk-NIZQH6AU.js.map} +0 -0
  338. /package/dist/{chunk-4ESAR77N.js.map → chunk-NOU77OHX.js.map} +0 -0
  339. /package/dist/{chunk-BQCHEV7O.js.map → chunk-O4ZIBPIW.js.map} +0 -0
  340. /package/dist/{chunk-HOHVIVDR.js.map → chunk-OBJUKYDO.js.map} +0 -0
  341. /package/dist/{chunk-YOLZS7NY.js.map → chunk-OTQE66IX.js.map} +0 -0
  342. /package/dist/{chunk-C2PG7IEH.js.map → chunk-P3VJCTVE.js.map} +0 -0
  343. /package/dist/{chunk-HGNOCPZX.js.map → chunk-QZHF4BWN.js.map} +0 -0
  344. /package/dist/{chunk-ASKXIAHD.js.map → chunk-WDJCZQB4.js.map} +0 -0
  345. /package/dist/{chunk-JYLFZWUX.js.map → chunk-XYUFGHUW.js.map} +0 -0
  346. /package/dist/{chunk-3TSTGI6F.js.map → chunk-ZQUAVZNG.js.map} +0 -0
  347. /package/dist/{first-start-migration-22LPFHD5.js.map → first-start-migration-OOR57AYR.js.map} +0 -0
  348. /package/dist/{resolution-MN36NW5P.js.map → orchestration/compression-guideline-coordinator.js.map} +0 -0
@@ -0,0 +1,355 @@
1
+ /**
2
+ * Built-in subprocess structural-context provider + doctor probe
3
+ * (issue #1548 Track A PR 5).
4
+ *
5
+ * Shells out to a configured external binary via `execFile` with an argv
6
+ * ARRAY (never a shell string — rule 10). codebase-memory-mcp's CLI mode is
7
+ * the canonical target, but the provider is command-agnostic: any binary
8
+ * that accepts `<symbols-subcommand> <json-arg>` and emits a JSON object
9
+ * `{ symbols: [{ symbol, path?, kind? }] }` satisfies it.
10
+ *
11
+ * Probe is cached once per instance (rule 11). Every failure path is a
12
+ * tagged outcome — `provider_unavailable` / `provider_timeout` /
13
+ * `provider_malformed` / `provider_error` — never a throw (rule 34).
14
+ *
15
+ * The async {@link probeStructuralProviderForDoctor} constructs a one-shot
16
+ * provider from config so `remnic doctor` can render
17
+ * "configured / probed / last error code" without a registered instance.
18
+ */
19
+ import { execFile } from "node:child_process";
20
+ import { promisify } from "node:util";
21
+ import { accessSync, constants, statSync } from "node:fs";
22
+
23
+ import type { PluginConfig } from "../types.js";
24
+ import { expandTildePath } from "../utils/path.js";
25
+ import { isCodingGraphInstalled } from "./optional-coding-graph.js";
26
+ import {
27
+ describeStructuralProviderStatus,
28
+ type ArchitectureHintsResult,
29
+ type StructuralContextProvider,
30
+ type StructuralProviderStatus,
31
+ type StructuralContextErrorCode,
32
+ type StructuralSymbol,
33
+ type SymbolsForDiffResult,
34
+ } from "./structural-context.js";
35
+
36
+ const execFileAsync = promisify(execFile);
37
+
38
+ /** Injectable spawn shape so tests substitute a stub (rule 33). */
39
+ export type StructuralSpawnFn = (
40
+ command: string,
41
+ argv: readonly string[],
42
+ options: { timeout: number; signal?: AbortSignal },
43
+ ) => Promise<{ stdout: string; stderr: string }>;
44
+
45
+ export interface SubprocessProviderOptions {
46
+ /** Absolute path to the binary (rule 24 — statSync at probe time). */
47
+ readonly command: string;
48
+ /**
49
+ * Extra argv appended after the symbols-for-diff subcommand
50
+ * (rule 10 — array, never a shell string).
51
+ */
52
+ readonly args?: readonly string[];
53
+ /** Per-call deadline in ms. Default 5000. */
54
+ readonly timeoutMs?: number;
55
+ /**
56
+ * Subcommand the provider invokes for symbol expansion.
57
+ * Default `"symbols-for-diff"`.
58
+ */
59
+ readonly symbolsSubcommand?: string;
60
+ /** Test seam — defaults to promisified `execFile`. */
61
+ readonly spawn?: StructuralSpawnFn;
62
+ }
63
+
64
+ interface ProbeState {
65
+ readonly available: boolean;
66
+ readonly detail?: string;
67
+ }
68
+
69
+ const DEFAULT_TIMEOUT_MS = 5000;
70
+ const DEFAULT_SYMBOLS_SUBCOMMAND = "symbols-for-diff";
71
+
72
+ /**
73
+ * Construct a structural-context provider backed by an external subprocess.
74
+ * The instance is self-contained: callers register it via
75
+ * `registerStructuralContextProvider(scope, provider)` and dispose via the
76
+ * returned unregister handle.
77
+ */
78
+ export function createSubprocessStructuralProvider(
79
+ options: SubprocessProviderOptions,
80
+ ): StructuralContextProvider {
81
+ // Rule 17 — expand a leading ~ so a home-relative binary path
82
+ // (e.g. ~/bin/cbm) resolves instead of degrading to unavailable.
83
+ // Mirrors the codegraph-runtime.ts precedent for operator paths.
84
+ const command = expandTildePath(options.command.trim());
85
+ const extraArgs = options.args ?? [];
86
+ const timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS;
87
+ const symbolsSubcommand = options.symbolsSubcommand ?? DEFAULT_SYMBOLS_SUBCOMMAND;
88
+ const spawn: StructuralSpawnFn = options.spawn ?? defaultSpawn;
89
+
90
+ let probeState: ProbeState | null = null;
91
+ const id = `subprocess(${command})`;
92
+
93
+ async function ensureProbe(): Promise<ProbeState> {
94
+ if (probeState !== null) return probeState;
95
+ if (!command) {
96
+ probeState = { available: false, detail: "structuralProviderCommand is empty" };
97
+ return probeState;
98
+ }
99
+ // Rule 24 — file existence check at probe time. An operator pointing at
100
+ // a missing/renamed binary gets a DISTINCT `provider_unavailable` code,
101
+ // never a silent empty result.
102
+ let statOk = false;
103
+ let notExecutable = false;
104
+ try {
105
+ statOk = statSync(command).isFile();
106
+ } catch {
107
+ statOk = false;
108
+ }
109
+ // P2 hardening: a non-executable regular file would report available
110
+ // here but fail later with EACCES/provider_error. On POSIX, enforce
111
+ // executable access so `remnic doctor` surfaces the real state. Windows
112
+ // has no reliable exec bit (Node X_OK is always true there), so the
113
+ // check is POSIX-only.
114
+ if (statOk && process.platform !== "win32") {
115
+ try {
116
+ accessSync(command, constants.X_OK);
117
+ } catch {
118
+ notExecutable = true;
119
+ }
120
+ }
121
+ probeState = !statOk
122
+ ? { available: false, detail: `binary not found: ${command}` }
123
+ : notExecutable
124
+ ? { available: false, detail: `binary not executable: ${command}` }
125
+ : { available: true };
126
+ return probeState;
127
+ }
128
+
129
+ return {
130
+ id,
131
+ async probe() {
132
+ return ensureProbe();
133
+ },
134
+ async symbolsForDiff(
135
+ diff: string,
136
+ callOpts,
137
+ ): Promise<SymbolsForDiffResult> {
138
+ const probe = await ensureProbe();
139
+ if (!probe.available) {
140
+ return { ok: false, code: "provider_unavailable", detail: probe.detail };
141
+ }
142
+ // Rule 10 — argv array; the diff is carried as a JSON argument so the
143
+ // binary never sees a shell and never re-interpolates paths.
144
+ const payload = JSON.stringify({ diff });
145
+ const argv = [symbolsSubcommand, payload, ...extraArgs];
146
+ try {
147
+ const { stdout } = await spawn(command, argv, {
148
+ timeout: timeoutMs,
149
+ signal: callOpts?.signal,
150
+ });
151
+ return parseSymbolJson(stdout);
152
+ } catch (err) {
153
+ return classifySpawnError(err);
154
+ }
155
+ },
156
+ async architectureHints(
157
+ root: string,
158
+ callOpts,
159
+ ): Promise<ArchitectureHintsResult> {
160
+ const probe = await ensureProbe();
161
+ if (!probe.available) {
162
+ return { ok: false, code: "provider_unavailable", detail: probe.detail };
163
+ }
164
+ const argv = ["architecture-hints", root, ...extraArgs];
165
+ try {
166
+ const { stdout } = await spawn(command, argv, {
167
+ timeout: timeoutMs,
168
+ signal: callOpts?.signal,
169
+ });
170
+ return parseHintsJson(stdout);
171
+ } catch (err) {
172
+ return classifySpawnError(err);
173
+ }
174
+ },
175
+ close() {
176
+ probeState = null;
177
+ },
178
+ };
179
+ }
180
+
181
+ // ──────────────────────────────────────────────────────────────────────────
182
+ // JSON validation — rule 18 (object-not-null), rule 51 (shape), rule 34
183
+ // ──────────────────────────────────────────────────────────────────────────
184
+
185
+ function parseSymbolJson(stdout: string): SymbolsForDiffResult {
186
+ let value: unknown;
187
+ try {
188
+ value = JSON.parse(stdout);
189
+ } catch {
190
+ return {
191
+ ok: false,
192
+ code: "provider_malformed",
193
+ detail: "stdout was not valid JSON",
194
+ };
195
+ }
196
+ if (value === null || typeof value !== "object" || Array.isArray(value)) {
197
+ return {
198
+ ok: false,
199
+ code: "provider_malformed",
200
+ detail: "provider output was not a JSON object",
201
+ };
202
+ }
203
+ const obj = value as Record<string, unknown>;
204
+ const symbolsRaw = obj.symbols;
205
+ if (!Array.isArray(symbolsRaw)) {
206
+ return {
207
+ ok: false,
208
+ code: "provider_malformed",
209
+ detail: "provider output .symbols is not an array",
210
+ };
211
+ }
212
+ const symbols: StructuralSymbol[] = [];
213
+ for (const entry of symbolsRaw) {
214
+ const item = readStructuralSymbol(entry);
215
+ if (item !== null) symbols.push(item);
216
+ }
217
+ return { ok: true, symbols };
218
+ }
219
+
220
+ function parseHintsJson(stdout: string): ArchitectureHintsResult {
221
+ let value: unknown;
222
+ try {
223
+ value = JSON.parse(stdout);
224
+ } catch {
225
+ return {
226
+ ok: false,
227
+ code: "provider_malformed",
228
+ detail: "stdout was not valid JSON",
229
+ };
230
+ }
231
+ if (!Array.isArray(value)) {
232
+ return {
233
+ ok: false,
234
+ code: "provider_malformed",
235
+ detail: "provider hints output was not a JSON array",
236
+ };
237
+ }
238
+ const hints = value.filter((h): h is string => typeof h === "string" && h.length > 0);
239
+ return { ok: true, hints };
240
+ }
241
+
242
+ function readStructuralSymbol(entry: unknown): StructuralSymbol | null {
243
+ if (entry === null || typeof entry !== "object" || Array.isArray(entry)) {
244
+ return null;
245
+ }
246
+ const rec = entry as Record<string, unknown>;
247
+ const symbol = typeof rec.symbol === "string" ? rec.symbol.trim() : "";
248
+ if (!symbol) return null;
249
+ const path = typeof rec.path === "string" && rec.path.length > 0 ? rec.path : undefined;
250
+ const kind = typeof rec.kind === "string" && rec.kind.length > 0 ? rec.kind : undefined;
251
+ const item: StructuralSymbol =
252
+ path !== undefined && kind !== undefined
253
+ ? { symbol, path, kind }
254
+ : path !== undefined
255
+ ? { symbol, path }
256
+ : kind !== undefined
257
+ ? { symbol, kind }
258
+ : { symbol };
259
+ return item;
260
+ }
261
+
262
+ type StructuralContextError = {
263
+ readonly ok: false;
264
+ readonly code: StructuralContextErrorCode;
265
+ readonly detail?: string;
266
+ };
267
+
268
+ function classifySpawnError(err: unknown): StructuralContextError {
269
+ const message = err instanceof Error ? err.message : String(err);
270
+ // Node sets the child's `.killed` flag and surfaces an ETIMEDOUT-style
271
+ // message when `timeout` elapses; the regex keeps this robust to Node
272
+ // version wording differences.
273
+ if (/timeout|timed out|ETIMEDOUT/i.test(message)) {
274
+ return { ok: false, code: "provider_timeout", detail: message };
275
+ }
276
+ return { ok: false, code: "provider_error", detail: message };
277
+ }
278
+
279
+ function defaultSpawn(
280
+ command: string,
281
+ argv: readonly string[],
282
+ options: { timeout: number; signal?: AbortSignal },
283
+ ): Promise<{ stdout: string; stderr: string }> {
284
+ return execFileAsync(command, [...argv], {
285
+ timeout: options.timeout,
286
+ signal: options.signal,
287
+ maxBuffer: 4 * 1024 * 1024,
288
+ });
289
+ }
290
+
291
+ // ──────────────────────────────────────────────────────────────────────────
292
+ // Doctor probe — construct a one-shot provider from config and probe it
293
+ // ──────────────────────────────────────────────────────────────────────────
294
+
295
+ /**
296
+ * Augment the pure {@link describeStructuralProviderStatus} with a LIVE probe
297
+ * so `remnic doctor` can render "configured / probed / last error code".
298
+ *
299
+ * - `"none"` → inactive (no probe).
300
+ * - `"subprocess"` → builds a throwaway provider from
301
+ * `structuralProviderCommand`, probes it, disposes it.
302
+ * - `"native"` → asks `isCodingGraphInstalled()` whether the optional
303
+ * @remnic/coding-graph peer is present (the adapter that
304
+ * turns the engine into a provider lands in #1551).
305
+ *
306
+ * Never throws — a probe failure populates `probed.available = false`.
307
+ */
308
+ export async function probeStructuralProviderForDoctor(
309
+ config: PluginConfig,
310
+ ): Promise<StructuralProviderStatus> {
311
+ const status = describeStructuralProviderStatus(config);
312
+ if (!status.active) return status;
313
+
314
+ const ck = config.codingKnowledge;
315
+ if (ck.structuralProvider === "subprocess") {
316
+ if (!ck.structuralProviderCommand) {
317
+ return {
318
+ ...status,
319
+ probed: { available: false, detail: "structuralProviderCommand is empty" },
320
+ };
321
+ }
322
+ const provider = createSubprocessStructuralProvider({
323
+ command: ck.structuralProviderCommand,
324
+ });
325
+ try {
326
+ const probed = await safeProbe(provider);
327
+ return { ...status, probed };
328
+ } finally {
329
+ void provider.close?.();
330
+ }
331
+ }
332
+
333
+ if (ck.structuralProvider === "native") {
334
+ const installed = await isCodingGraphInstalled().catch(() => false);
335
+ return {
336
+ ...status,
337
+ probed: {
338
+ available: installed,
339
+ detail: installed ? undefined : "@remnic/coding-graph not installed",
340
+ },
341
+ };
342
+ }
343
+
344
+ return status;
345
+ }
346
+
347
+ async function safeProbe(
348
+ provider: StructuralContextProvider,
349
+ ): Promise<{ available: boolean; detail?: string }> {
350
+ try {
351
+ return await provider.probe();
352
+ } catch (err) {
353
+ return { available: false, detail: err instanceof Error ? err.message : String(err) };
354
+ }
355
+ }
@@ -0,0 +1,90 @@
1
+ import assert from "node:assert/strict";
2
+ import { mkdir, mkdtemp, rm, writeFile } from "node:fs/promises";
3
+ import os from "node:os";
4
+ import path from "node:path";
5
+ import { test } from "node:test";
6
+
7
+ import { StorageManager, type PluginConfig } from "../index.js";
8
+ import { CompoundingEngine, type CompoundingPromotionReport } from "./engine.js";
9
+
10
+ /**
11
+ * Issue #1645 — a tombstone-blocked promotion write lands pending_review (no
12
+ * active copy). promoteCandidate must divert blocked ids to a distinct
13
+ * `tombstoneBlocked` report field and NEVER report them as promoted — mirroring
14
+ * the postWriteGuard consumption pattern in the orchestrator extraction path.
15
+ *
16
+ * The tombstone chokepoint currently gates on category === "fact", so a
17
+ * rule/principle/preference promotion cannot be blocked by a real tombstone
18
+ * today. The defensive consumption is still correct (the gate may widen), so
19
+ * this test exercises the diversion logic with a storage double that returns
20
+ * tombstoneBlocked: true — pinning the CALLER contract, not the gate.
21
+ */
22
+ test("#1645: tombstone-blocked promotion is diverted, not reported as promoted", async () => {
23
+ const dir = await mkdtemp(path.join(os.tmpdir(), "remnic-compounding-"));
24
+ try {
25
+ const weekId = "2026-W27";
26
+ const candidateContent = "Always write tests before shipping code.";
27
+ await mkdir(path.join(dir, "compounding", "weekly"), { recursive: true });
28
+ await writeFile(
29
+ path.join(dir, "compounding", "weekly", `${weekId}.json`),
30
+ JSON.stringify({
31
+ version: 2,
32
+ generatedAt: "2026-07-07T00:00:00Z",
33
+ weekId,
34
+ promotionCandidates: [
35
+ {
36
+ id: "cand-1",
37
+ sourceType: "action-outcome",
38
+ subject: "testing",
39
+ category: "rule",
40
+ content: candidateContent,
41
+ score: 0.9,
42
+ rationale: "high signal",
43
+ outcome: null,
44
+ provenance: [],
45
+ agent: null,
46
+ workflow: null,
47
+ },
48
+ ],
49
+ }),
50
+ "utf-8",
51
+ );
52
+
53
+ const config = {
54
+ memoryDir: dir,
55
+ workspaceDir: dir,
56
+ compoundingEnabled: true,
57
+ compoundingSemanticEnabled: true,
58
+ } as unknown as PluginConfig;
59
+ const engine = new CompoundingEngine(config, new StorageManager(dir));
60
+
61
+ // Storage double: writeMemory reports tombstoneBlocked (simulating the
62
+ // chokepoint downgrading the promotion to pending_review).
63
+ let writeCallCount = 0;
64
+ const blockedStorage = {
65
+ dir,
66
+ async readAllMemories() {
67
+ return [];
68
+ },
69
+ async writeMemory() {
70
+ writeCallCount += 1;
71
+ return { id: "blocked-promo-1", tombstoneBlocked: true, blockedBy: "tomb-1" };
72
+ },
73
+ } as unknown as StorageManager;
74
+
75
+ const report: CompoundingPromotionReport = await engine.promoteCandidate({
76
+ weekId,
77
+ candidateId: "cand-1",
78
+ storage: blockedStorage,
79
+ });
80
+
81
+ assert.equal(writeCallCount, 1, "the promotion write still happens (pending_review row)");
82
+ assert.equal(report.promoted.length, 0, "blocked write must NOT appear in promoted");
83
+ assert.equal(report.tombstoneBlocked.length, 1, "blocked write diverted to tombstoneBlocked");
84
+ assert.equal(report.tombstoneBlocked[0]?.id, "blocked-promo-1");
85
+ assert.equal(report.tombstoneBlocked[0]?.candidateId, "cand-1");
86
+ assert.equal(report.tombstoneBlocked[0]?.content, candidateContent);
87
+ } finally {
88
+ await rm(dir, { recursive: true, force: true });
89
+ }
90
+ });
@@ -154,6 +154,17 @@ export interface CompoundingPromotionReport {
154
154
  reason: "disabled" | "weekly-artifact-missing" | "candidate-not-found" | "duplicate-guidance";
155
155
  existingMemoryId?: string;
156
156
  }>;
157
+ /**
158
+ * Promotions the #1579 tombstone chokepoint downgraded to pending_review (no
159
+ * active copy). Reported distinctly so callers never treat a blocked write as
160
+ * a successfully promoted active memory (#1645).
161
+ */
162
+ tombstoneBlocked: Array<{
163
+ id: string;
164
+ candidateId: string;
165
+ category: "principle" | "rule" | "preference";
166
+ content: string;
167
+ }>;
157
168
  }
158
169
 
159
170
  type WeeklyActionEvent = {
@@ -634,6 +645,7 @@ export class CompoundingEngine {
634
645
  weekId: opts.weekId,
635
646
  promoted: [],
636
647
  skipped: [],
648
+ tombstoneBlocked: [],
637
649
  };
638
650
  if (!report.enabled) {
639
651
  report.skipped.push({ weekId: opts.weekId, candidateId: opts.candidateId, reason: "disabled" });
@@ -695,13 +707,25 @@ export class CompoundingEngine {
695
707
  return report;
696
708
  }
697
709
 
698
- const id = await storage.writeMemory(candidate.category, persistedContent, {
710
+ const { id, tombstoneBlocked } = await storage.writeMemory(candidate.category, persistedContent, {
699
711
  source: "compounding-promotion",
700
712
  tags: uniqueTags,
701
713
  confidence,
702
714
  lineage,
703
715
  memoryKind: "note",
704
716
  });
717
+ // #1645: a tombstone-blocked promotion lands pending_review (no active
718
+ // copy) — divert it to the tombstoneBlocked list so the report never
719
+ // claims a blocked write as a successfully promoted active memory.
720
+ if (tombstoneBlocked) {
721
+ report.tombstoneBlocked.push({
722
+ id,
723
+ candidateId: opts.candidateId,
724
+ category: candidate.category,
725
+ content: persistedContent,
726
+ });
727
+ return report;
728
+ }
705
729
  report.promoted.push({
706
730
  id,
707
731
  candidateId: opts.candidateId,
@@ -131,7 +131,7 @@ function makeResolutionStorage(options: {
131
131
  memory.frontmatter.derived_from = writeOptions.derivedFrom;
132
132
  memory.frontmatter.derived_via = writeOptions.derivedVia as MemoryFrontmatter["derived_via"];
133
133
  memories.set(id, memory);
134
- return id;
134
+ return { id: id, tombstoneBlocked: false };
135
135
  },
136
136
  async supersedeMemory(oldId: string, newId: string, reason: string) {
137
137
  supersedeCalls.push({ oldId, newId, reason });
@@ -386,7 +386,7 @@ async function prepareMergeReplacement(
386
386
  const category = options.mergedCategory ?? mergedMemoryCategory(sourceA, sourceB);
387
387
  let mergedId: string;
388
388
  try {
389
- mergedId = await storage.writeMemory(category, mergedContent, {
389
+ const mergeResult = await storage.writeMemory(category, mergedContent, {
390
390
  actor: "contradiction-resolution",
391
391
  confidence: Math.min(sourceA.frontmatter.confidence ?? 0.8, sourceB.frontmatter.confidence ?? 0.8),
392
392
  tags: ["contradiction-resolution", "merge"],
@@ -394,6 +394,14 @@ async function prepareMergeReplacement(
394
394
  lineage: [idA, idB],
395
395
  derivedVia: "merge",
396
396
  });
397
+ mergedId = mergeResult.id;
398
+ if (mergeResult.tombstoneBlocked) {
399
+ // #1645: merged content matched a tombstone (pending_review). Don't
400
+ // supersede both sources to a non-active replacement — that retires
401
+ // the only active copies. Clean up the pending merge and abort the pair.
402
+ await cleanupMemoryId(storage, mergedId);
403
+ return { ok: false, message: `Merged memory for ${pairId} was tombstone-blocked (pending_review); not resolving — sources kept active` };
404
+ }
397
405
  } catch (err) {
398
406
  log.warn(
399
407
  "[contradiction-resolution] merged memory creation failed for %s: %s",
@@ -401,7 +401,7 @@ async function writeReplacementMemory(
401
401
  // writeMemory is the single storage chokepoint — catalog/dedup/reindex fire
402
402
  // here (rule 43). Tombstone blocking also fires here (#1579), so a
403
403
  // resurrected fact lands as pending_review rather than silently overwriting.
404
- const id = await storage.writeMemory(
404
+ const { id, tombstoneBlocked } = await storage.writeMemory(
405
405
  (draft.category ?? "fact") as Parameters<typeof storage.writeMemory>[0],
406
406
  draft.content,
407
407
  {
@@ -415,6 +415,16 @@ async function writeReplacementMemory(
415
415
  ...(draft.supersedes ? { supersedes: draft.supersedes } : {}),
416
416
  },
417
417
  );
418
+ if (tombstoneBlocked) {
419
+ // #1645 (review thread): the replacement content matched a tombstone, so it
420
+ // landed pending_review (non-active). Returning the id would let the
421
+ // executor record the supersede as applied and Phase 2 retire the loser
422
+ // with `supersededBy` pointing at a non-active replacement — stranding
423
+ // the fact behind a tombstone. Fail the correction cleanly instead.
424
+ throw new CorrectionContractError(
425
+ `correction replacement was tombstone-blocked (pending_review ${id}) — cannot supersede with a non-active replacement`,
426
+ );
427
+ }
418
428
  return id;
419
429
  }
420
430
 
@@ -505,7 +515,7 @@ async function rescopeMemoryFn(
505
515
  const destContent = fm.structuredAttributes
506
516
  ? stripAttributesSuffix(memory.content)
507
517
  : memory.content;
508
- const destId = await destStorage.writeMemory(fm.category, destContent, {
518
+ const { id: destId, tombstoneBlocked: destBlocked } = await destStorage.writeMemory(fm.category, destContent, {
509
519
  source: `correction:rescope:${namespace}`,
510
520
  ...(typeof fm.confidence === "number" ? { confidence: fm.confidence } : {}),
511
521
  ...(Array.isArray(fm.tags) ? { tags: fm.tags } : {}),
@@ -517,6 +527,14 @@ async function rescopeMemoryFn(
517
527
  ...(Array.isArray(fm.links) ? { links: fm.links } : {}),
518
528
  ...(fm.intentGoal ? { intentGoal: fm.intentGoal } : {}),
519
529
  });
530
+ if (destBlocked) {
531
+ // #1645: destination tombstone-blocked the rescope (pending_review). Don't
532
+ // archive the source — that deletes the only active copy while the
533
+ // replacement sits behind review. Fail the rescope; source stays active.
534
+ throw new CorrectionContractError(
535
+ `rescope of ${memoryId} into "${toNamespace}" was tombstone-blocked (pending_review ${destId}) — keeping source active`,
536
+ );
537
+ }
520
538
  // Unlink the source by archiving (non-destructive — rule 25). If the archive
521
539
  // fails AFTER the destination write succeeded, compensate by archiving the
522
540
  // destination too so no duplicate ACTIVE fact remains, then re-throw so the
@@ -650,7 +668,7 @@ async function appendAuditRecordFn(
650
668
  // Corrections are themselves memories, searchable and namespaced (issue
651
669
  // #1580 design §4). Write a correction-category memory capturing the
652
670
  // plan + outcome as the audit trail.
653
- const id = await storage.writeMemory("correction", buildAuditBody(record), {
671
+ const { id: id } = await storage.writeMemory("correction", buildAuditBody(record), {
654
672
  source: "correction-contract",
655
673
  confidence: 1.0,
656
674
  tags: ["correction-audit", `plan:${record.planId}`, `classification:${record.classification}`],
@@ -168,6 +168,13 @@ export interface CorrectionPlan {
168
168
  * never be re-applied wholesale (it would duplicate succeeded actions).
169
169
  */
170
170
  status?: "pending" | "applying" | "applied" | "discarded" | "partial";
171
+ /**
172
+ * ISO timestamp recorded when the plan transitioned to `applying` (#1713
173
+ * Item 2). Startup recovery uses this to detect plans stuck in `applying`
174
+ * past the stale-applying TTL (process died mid-apply) and discard+scrub
175
+ * them. Absent on plans that never entered `applying`.
176
+ */
177
+ applyingAt?: string;
171
178
  }
172
179
 
173
180
  /**