@graphrefly/graphrefly 0.47.1 → 0.48.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (303) hide show
  1. package/dist/base/composition/index.cjs +28 -19
  2. package/dist/base/composition/index.cjs.map +1 -1
  3. package/dist/base/composition/index.d.cts +14 -5
  4. package/dist/base/composition/index.d.ts +14 -5
  5. package/dist/base/composition/index.js +9 -9
  6. package/dist/base/index.cjs +294 -164
  7. package/dist/base/index.cjs.map +1 -1
  8. package/dist/base/index.d.cts +2 -2
  9. package/dist/base/index.d.ts +2 -2
  10. package/dist/base/index.js +77 -72
  11. package/dist/base/io/index.cjs +145 -85
  12. package/dist/base/io/index.cjs.map +1 -1
  13. package/dist/base/io/index.d.cts +32 -5
  14. package/dist/base/io/index.d.ts +32 -5
  15. package/dist/base/io/index.js +5 -5
  16. package/dist/base/mutation/index.cjs +21 -0
  17. package/dist/base/mutation/index.cjs.map +1 -1
  18. package/dist/base/mutation/index.d.cts +23 -1
  19. package/dist/base/mutation/index.d.ts +23 -1
  20. package/dist/base/mutation/index.js +3 -1
  21. package/dist/base/sources/browser/index.cjs +18 -12
  22. package/dist/base/sources/browser/index.cjs.map +1 -1
  23. package/dist/base/sources/browser/index.d.cts +20 -2
  24. package/dist/base/sources/browser/index.d.ts +20 -2
  25. package/dist/base/sources/browser/index.js +18 -12
  26. package/dist/base/sources/browser/index.js.map +1 -1
  27. package/dist/base/sources/event/index.cjs +29 -1
  28. package/dist/base/sources/event/index.cjs.map +1 -1
  29. package/dist/base/sources/event/index.d.cts +67 -3
  30. package/dist/base/sources/event/index.d.ts +67 -3
  31. package/dist/base/sources/event/index.js +5 -2
  32. package/dist/base/sources/index.cjs +96 -50
  33. package/dist/base/sources/index.cjs.map +1 -1
  34. package/dist/base/sources/index.d.cts +1 -1
  35. package/dist/base/sources/index.d.ts +1 -1
  36. package/dist/base/sources/index.js +7 -4
  37. package/dist/base/sources/node/index.cjs +43 -37
  38. package/dist/base/sources/node/index.cjs.map +1 -1
  39. package/dist/base/sources/node/index.js +43 -37
  40. package/dist/base/sources/node/index.js.map +1 -1
  41. package/dist/{chunk-J5WFUEO4.js → chunk-23MAWVOJ.js} +3 -3
  42. package/dist/{chunk-YXCPV26R.js → chunk-3REMCHSS.js} +39 -27
  43. package/dist/chunk-3REMCHSS.js.map +1 -0
  44. package/dist/{chunk-CEVNQ74M.js → chunk-3YGXPUHW.js} +2 -2
  45. package/dist/{chunk-CEVNQ74M.js.map → chunk-3YGXPUHW.js.map} +1 -1
  46. package/dist/{chunk-EVYY4X5A.js → chunk-46X2EFQH.js} +16 -5
  47. package/dist/chunk-46X2EFQH.js.map +1 -0
  48. package/dist/{chunk-NY2PYHNC.js → chunk-5UY3PNFY.js} +12 -5
  49. package/dist/chunk-5UY3PNFY.js.map +1 -0
  50. package/dist/{chunk-RGMTUZCL.js → chunk-65OM4XLQ.js} +50 -4
  51. package/dist/chunk-65OM4XLQ.js.map +1 -0
  52. package/dist/{chunk-3PSLNJDU.js → chunk-6DQYBIHW.js} +314 -49
  53. package/dist/chunk-6DQYBIHW.js.map +1 -0
  54. package/dist/{chunk-LDCSZ72P.js → chunk-6YBER5UP.js} +3 -3
  55. package/dist/{chunk-LDCSZ72P.js.map → chunk-6YBER5UP.js.map} +1 -1
  56. package/dist/{chunk-7EGRP2VX.js → chunk-7BULJTL6.js} +2 -2
  57. package/dist/{chunk-7EGRP2VX.js.map → chunk-7BULJTL6.js.map} +1 -1
  58. package/dist/{chunk-VLAGJZSL.js → chunk-7T7WLEPM.js} +25 -4
  59. package/dist/chunk-7T7WLEPM.js.map +1 -0
  60. package/dist/{chunk-PKPO3JTZ.js → chunk-AQAKDE7F.js} +29 -11
  61. package/dist/chunk-AQAKDE7F.js.map +1 -0
  62. package/dist/{chunk-2OB3CEJS.js → chunk-B5Y5GPD5.js} +2 -2
  63. package/dist/{chunk-BXGZFGZ4.js → chunk-C5QD5DQX.js} +22 -1
  64. package/dist/chunk-C5QD5DQX.js.map +1 -0
  65. package/dist/{chunk-4XCHZRUJ.js → chunk-D5YGR4TP.js} +58 -7
  66. package/dist/chunk-D5YGR4TP.js.map +1 -0
  67. package/dist/{chunk-NPRP3MCV.js → chunk-DHDCOOJU.js} +2 -2
  68. package/dist/chunk-DHDCOOJU.js.map +1 -0
  69. package/dist/{chunk-MTTRCEJT.js → chunk-DVTDF5OI.js} +2 -2
  70. package/dist/{chunk-SOOKUYVM.js → chunk-F7EKHR32.js} +13 -9
  71. package/dist/chunk-F7EKHR32.js.map +1 -0
  72. package/dist/{chunk-A7KV5UK4.js → chunk-G7H6PN7P.js} +2 -2
  73. package/dist/{chunk-OCUDSN63.js → chunk-GGKHHG5Y.js} +110 -64
  74. package/dist/chunk-GGKHHG5Y.js.map +1 -0
  75. package/dist/{chunk-RAGGHLCV.js → chunk-GUNIRPEJ.js} +8 -6
  76. package/dist/{chunk-RAGGHLCV.js.map → chunk-GUNIRPEJ.js.map} +1 -1
  77. package/dist/{chunk-YJ4U2D2C.js → chunk-J5TBZFBD.js} +9 -7
  78. package/dist/chunk-J5TBZFBD.js.map +1 -0
  79. package/dist/{chunk-Y52CS6YA.js → chunk-JA67ZQG2.js} +2 -2
  80. package/dist/{chunk-Y52CS6YA.js.map → chunk-JA67ZQG2.js.map} +1 -1
  81. package/dist/{chunk-U225SKB4.js → chunk-K4ZYJ4EM.js} +569 -424
  82. package/dist/chunk-K4ZYJ4EM.js.map +1 -0
  83. package/dist/{chunk-Z4YXAUDN.js → chunk-KUFXLAEY.js} +11 -7
  84. package/dist/{chunk-Z4YXAUDN.js.map → chunk-KUFXLAEY.js.map} +1 -1
  85. package/dist/{chunk-IHTWQEDR.js → chunk-LTSI7ULC.js} +3 -3
  86. package/dist/{chunk-IHTWQEDR.js.map → chunk-LTSI7ULC.js.map} +1 -1
  87. package/dist/{chunk-DKNHAICT.js → chunk-MMHGYX44.js} +25 -9
  88. package/dist/chunk-MMHGYX44.js.map +1 -0
  89. package/dist/{chunk-K7PDZYQE.js → chunk-MQMTRKY3.js} +129 -50
  90. package/dist/chunk-MQMTRKY3.js.map +1 -0
  91. package/dist/{chunk-42FQ27MQ.js → chunk-MTODGQBR.js} +44 -179
  92. package/dist/chunk-MTODGQBR.js.map +1 -0
  93. package/dist/{chunk-O3MT7DYI.js → chunk-N6MNJNHB.js} +2 -2
  94. package/dist/{chunk-FVINAAKA.js → chunk-NBK6QQMG.js} +14 -13
  95. package/dist/{chunk-FVINAAKA.js.map → chunk-NBK6QQMG.js.map} +1 -1
  96. package/dist/{chunk-DM4OMPWK.js → chunk-NSA5K5G2.js} +2 -2
  97. package/dist/{chunk-MLTPJMH6.js → chunk-QQYULEZL.js} +2 -2
  98. package/dist/chunk-QSW4DFKE.js +31 -0
  99. package/dist/chunk-QSW4DFKE.js.map +1 -0
  100. package/dist/{chunk-PZWISPIQ.js → chunk-S7HN5FHL.js} +17 -11
  101. package/dist/chunk-S7HN5FHL.js.map +1 -0
  102. package/dist/{chunk-4S53H2KR.js → chunk-SUNCHMML.js} +2 -2
  103. package/dist/{chunk-4GYMCUDZ.js → chunk-T2U6N3FV.js} +7 -7
  104. package/dist/{chunk-RJOG4IJU.js → chunk-T5URUIIY.js} +50 -35
  105. package/dist/chunk-T5URUIIY.js.map +1 -0
  106. package/dist/{chunk-B4AKFXGE.js → chunk-TPTZZV25.js} +6 -6
  107. package/dist/chunk-TPTZZV25.js.map +1 -0
  108. package/dist/{chunk-BU3SEFA5.js → chunk-V46JWFGV.js} +7 -6
  109. package/dist/chunk-V46JWFGV.js.map +1 -0
  110. package/dist/{chunk-IJRR6YAI.js → chunk-VLDRAMP7.js} +18 -12
  111. package/dist/chunk-VLDRAMP7.js.map +1 -0
  112. package/dist/{chunk-6XZYT4SW.js → chunk-X6ESZDR6.js} +8 -9
  113. package/dist/chunk-X6ESZDR6.js.map +1 -0
  114. package/dist/{chunk-E5OZPDIW.js → chunk-X7BA5PWG.js} +7 -5
  115. package/dist/chunk-X7BA5PWG.js.map +1 -0
  116. package/dist/{chunk-CXANAIZU.js → chunk-XEWV254I.js} +3 -3
  117. package/dist/{chunk-CXANAIZU.js.map → chunk-XEWV254I.js.map} +1 -1
  118. package/dist/{chunk-V4Y3TM7U.js → chunk-YBJVKMTM.js} +38 -16
  119. package/dist/chunk-YBJVKMTM.js.map +1 -0
  120. package/dist/{chunk-7ADWWI2T.js → chunk-ZW32BPXV.js} +17 -6
  121. package/dist/chunk-ZW32BPXV.js.map +1 -0
  122. package/dist/compat/index.cjs +52 -5
  123. package/dist/compat/index.cjs.map +1 -1
  124. package/dist/compat/index.d.cts +1 -1
  125. package/dist/compat/index.d.ts +1 -1
  126. package/dist/compat/index.js +7 -7
  127. package/dist/compat/nestjs/index.cjs +52 -5
  128. package/dist/compat/nestjs/index.cjs.map +1 -1
  129. package/dist/compat/nestjs/index.d.cts +1 -1
  130. package/dist/compat/nestjs/index.d.ts +1 -1
  131. package/dist/compat/nestjs/index.js +4 -4
  132. package/dist/{fallback-Bx46zqky.d.cts → fallback-BROR6ZhO.d.cts} +1 -1
  133. package/dist/{fallback-pIWW8A2d.d.ts → fallback-DO80aM_3.d.ts} +1 -1
  134. package/dist/{index-B_p8tnvf.d.cts → index-D1z3XcF9.d.cts} +1 -0
  135. package/dist/{index-_HDSmPyp.d.ts → index-DZ6yua0Q.d.ts} +1 -0
  136. package/dist/index.cjs +2387 -1707
  137. package/dist/index.cjs.map +1 -1
  138. package/dist/index.d.cts +10 -10
  139. package/dist/index.d.ts +10 -10
  140. package/dist/index.js +173 -150
  141. package/dist/index.js.map +1 -1
  142. package/dist/presets/ai/index.cjs +88 -26
  143. package/dist/presets/ai/index.cjs.map +1 -1
  144. package/dist/presets/ai/index.js +14 -14
  145. package/dist/presets/harness/index.cjs +183 -51
  146. package/dist/presets/harness/index.cjs.map +1 -1
  147. package/dist/presets/harness/index.d.cts +15 -5
  148. package/dist/presets/harness/index.d.ts +15 -5
  149. package/dist/presets/harness/index.js +26 -26
  150. package/dist/presets/index.cjs +298 -101
  151. package/dist/presets/index.cjs.map +1 -1
  152. package/dist/presets/index.d.cts +2 -2
  153. package/dist/presets/index.d.ts +2 -2
  154. package/dist/presets/index.js +49 -49
  155. package/dist/presets/inspect/index.cjs +63 -14
  156. package/dist/presets/inspect/index.cjs.map +1 -1
  157. package/dist/presets/inspect/index.d.cts +1 -1
  158. package/dist/presets/inspect/index.d.ts +1 -1
  159. package/dist/presets/inspect/index.js +6 -6
  160. package/dist/presets/resilience/index.cjs +64 -44
  161. package/dist/presets/resilience/index.cjs.map +1 -1
  162. package/dist/presets/resilience/index.d.cts +12 -8
  163. package/dist/presets/resilience/index.d.ts +12 -8
  164. package/dist/presets/resilience/index.js +6 -6
  165. package/dist/{rate-limiter-DpVbSYdH.d.cts → rate-limiter-DC26FM8J.d.cts} +10 -1
  166. package/dist/{rate-limiter-CEALq4N1.d.ts → rate-limiter-DyWpwpQP.d.ts} +10 -1
  167. package/dist/{reactive-layout-fswlBUvX.d.ts → reactive-layout-BBBWH0V_.d.cts} +85 -4
  168. package/dist/{reactive-layout-fswlBUvX.d.cts → reactive-layout-BBBWH0V_.d.ts} +85 -4
  169. package/dist/solutions/index.cjs +239 -92
  170. package/dist/solutions/index.cjs.map +1 -1
  171. package/dist/solutions/index.d.cts +2 -2
  172. package/dist/solutions/index.d.ts +2 -2
  173. package/dist/solutions/index.js +32 -32
  174. package/dist/{spawnable-5mDY501F.d.cts → spawnable-B2IlW60f.d.cts} +23 -2
  175. package/dist/{spawnable-D3lR0oQu.d.ts → spawnable-tttFz2Nh.d.ts} +23 -2
  176. package/dist/testing/index.cjs +94 -0
  177. package/dist/testing/index.cjs.map +1 -0
  178. package/dist/testing/index.d.cts +59 -0
  179. package/dist/testing/index.d.ts +59 -0
  180. package/dist/testing/index.js +73 -0
  181. package/dist/testing/index.js.map +1 -0
  182. package/dist/{timeout-U5O4ESK3.js → timeout-BEABACRP.js} +2 -2
  183. package/dist/utils/ai/browser.cjs.map +1 -1
  184. package/dist/utils/ai/browser.d.cts +2 -2
  185. package/dist/utils/ai/browser.d.ts +2 -2
  186. package/dist/utils/ai/browser.js +10 -10
  187. package/dist/utils/ai/browser.js.map +1 -1
  188. package/dist/utils/ai/index.cjs +291 -191
  189. package/dist/utils/ai/index.cjs.map +1 -1
  190. package/dist/utils/ai/index.d.cts +108 -12
  191. package/dist/utils/ai/index.d.ts +108 -12
  192. package/dist/utils/ai/index.js +23 -21
  193. package/dist/utils/ai/node.cjs.map +1 -1
  194. package/dist/utils/ai/node.d.cts +5 -5
  195. package/dist/utils/ai/node.d.ts +5 -5
  196. package/dist/utils/ai/node.js +3 -3
  197. package/dist/utils/ai/node.js.map +1 -1
  198. package/dist/utils/cqrs/index.cjs +29 -3
  199. package/dist/utils/cqrs/index.cjs.map +1 -1
  200. package/dist/utils/cqrs/index.d.cts +12 -7
  201. package/dist/utils/cqrs/index.d.ts +12 -7
  202. package/dist/utils/cqrs/index.js +2 -2
  203. package/dist/utils/demo-shell/index.cjs +45 -19
  204. package/dist/utils/demo-shell/index.cjs.map +1 -1
  205. package/dist/utils/demo-shell/index.d.cts +1 -1
  206. package/dist/utils/demo-shell/index.d.ts +1 -1
  207. package/dist/utils/demo-shell/index.js +2 -2
  208. package/dist/utils/domain-templates/index.cjs +1 -1
  209. package/dist/utils/domain-templates/index.cjs.map +1 -1
  210. package/dist/utils/domain-templates/index.js +3 -3
  211. package/dist/utils/graphspec/index.cjs +1 -1
  212. package/dist/utils/graphspec/index.cjs.map +1 -1
  213. package/dist/utils/graphspec/index.js +3 -3
  214. package/dist/utils/harness/index.cjs +16 -10
  215. package/dist/utils/harness/index.cjs.map +1 -1
  216. package/dist/utils/harness/index.js +1 -1
  217. package/dist/utils/index.cjs +1692 -1192
  218. package/dist/utils/index.cjs.map +1 -1
  219. package/dist/utils/index.d.cts +7 -7
  220. package/dist/utils/index.d.ts +7 -7
  221. package/dist/utils/index.js +77 -59
  222. package/dist/utils/inspect/index.cjs +52 -4
  223. package/dist/utils/inspect/index.cjs.map +1 -1
  224. package/dist/utils/inspect/index.d.cts +32 -3
  225. package/dist/utils/inspect/index.d.ts +32 -3
  226. package/dist/utils/inspect/index.js +4 -4
  227. package/dist/utils/job-queue/index.cjs +46 -9
  228. package/dist/utils/job-queue/index.cjs.map +1 -1
  229. package/dist/utils/job-queue/index.d.cts +33 -3
  230. package/dist/utils/job-queue/index.d.ts +33 -3
  231. package/dist/utils/job-queue/index.js +2 -2
  232. package/dist/utils/memory/index.cjs +570 -425
  233. package/dist/utils/memory/index.cjs.map +1 -1
  234. package/dist/utils/memory/index.d.cts +261 -33
  235. package/dist/utils/memory/index.d.ts +261 -33
  236. package/dist/utils/memory/index.js +10 -2
  237. package/dist/utils/messaging/index.cjs.map +1 -1
  238. package/dist/utils/messaging/index.d.cts +4 -3
  239. package/dist/utils/messaging/index.d.ts +4 -3
  240. package/dist/utils/messaging/index.js +2 -2
  241. package/dist/utils/orchestration/index.cjs +14 -3
  242. package/dist/utils/orchestration/index.cjs.map +1 -1
  243. package/dist/utils/orchestration/index.js +3 -3
  244. package/dist/utils/process/index.cjs +32 -2
  245. package/dist/utils/process/index.cjs.map +1 -1
  246. package/dist/utils/process/index.d.cts +4 -3
  247. package/dist/utils/process/index.d.ts +4 -3
  248. package/dist/utils/process/index.js +3 -3
  249. package/dist/utils/reactive-layout/index.cjs +184 -55
  250. package/dist/utils/reactive-layout/index.cjs.map +1 -1
  251. package/dist/utils/reactive-layout/index.d.cts +128 -3
  252. package/dist/utils/reactive-layout/index.d.ts +128 -3
  253. package/dist/utils/reactive-layout/index.js +16 -8
  254. package/dist/utils/reduction/index.cjs +1 -1
  255. package/dist/utils/reduction/index.cjs.map +1 -1
  256. package/dist/utils/reduction/index.js +2 -2
  257. package/dist/utils/resilience/index.cjs +64 -43
  258. package/dist/utils/resilience/index.cjs.map +1 -1
  259. package/dist/utils/resilience/index.d.cts +1 -1
  260. package/dist/utils/resilience/index.d.ts +1 -1
  261. package/dist/utils/resilience/index.js +5 -5
  262. package/dist/utils/surface/index.cjs +1 -1
  263. package/dist/utils/surface/index.cjs.map +1 -1
  264. package/dist/utils/surface/index.js +4 -4
  265. package/package.json +15 -3
  266. package/dist/chunk-3PSLNJDU.js.map +0 -1
  267. package/dist/chunk-42FQ27MQ.js.map +0 -1
  268. package/dist/chunk-4XCHZRUJ.js.map +0 -1
  269. package/dist/chunk-6XZYT4SW.js.map +0 -1
  270. package/dist/chunk-7ADWWI2T.js.map +0 -1
  271. package/dist/chunk-B4AKFXGE.js.map +0 -1
  272. package/dist/chunk-BU3SEFA5.js.map +0 -1
  273. package/dist/chunk-BXGZFGZ4.js.map +0 -1
  274. package/dist/chunk-DKNHAICT.js.map +0 -1
  275. package/dist/chunk-E5OZPDIW.js.map +0 -1
  276. package/dist/chunk-EVYY4X5A.js.map +0 -1
  277. package/dist/chunk-IJRR6YAI.js.map +0 -1
  278. package/dist/chunk-K7PDZYQE.js.map +0 -1
  279. package/dist/chunk-NPRP3MCV.js.map +0 -1
  280. package/dist/chunk-NY2PYHNC.js.map +0 -1
  281. package/dist/chunk-OCUDSN63.js.map +0 -1
  282. package/dist/chunk-PKPO3JTZ.js.map +0 -1
  283. package/dist/chunk-PZWISPIQ.js.map +0 -1
  284. package/dist/chunk-RGMTUZCL.js.map +0 -1
  285. package/dist/chunk-RJOG4IJU.js.map +0 -1
  286. package/dist/chunk-SOOKUYVM.js.map +0 -1
  287. package/dist/chunk-U225SKB4.js.map +0 -1
  288. package/dist/chunk-V4Y3TM7U.js.map +0 -1
  289. package/dist/chunk-VLAGJZSL.js.map +0 -1
  290. package/dist/chunk-W2BOPXTI.js +0 -1
  291. package/dist/chunk-YJ4U2D2C.js.map +0 -1
  292. package/dist/chunk-YXCPV26R.js.map +0 -1
  293. package/dist/timeout-U5O4ESK3.js.map +0 -1
  294. /package/dist/{chunk-J5WFUEO4.js.map → chunk-23MAWVOJ.js.map} +0 -0
  295. /package/dist/{chunk-2OB3CEJS.js.map → chunk-B5Y5GPD5.js.map} +0 -0
  296. /package/dist/{chunk-MTTRCEJT.js.map → chunk-DVTDF5OI.js.map} +0 -0
  297. /package/dist/{chunk-A7KV5UK4.js.map → chunk-G7H6PN7P.js.map} +0 -0
  298. /package/dist/{chunk-O3MT7DYI.js.map → chunk-N6MNJNHB.js.map} +0 -0
  299. /package/dist/{chunk-DM4OMPWK.js.map → chunk-NSA5K5G2.js.map} +0 -0
  300. /package/dist/{chunk-MLTPJMH6.js.map → chunk-QQYULEZL.js.map} +0 -0
  301. /package/dist/{chunk-4S53H2KR.js.map → chunk-SUNCHMML.js.map} +0 -0
  302. /package/dist/{chunk-4GYMCUDZ.js.map → chunk-T2U6N3FV.js.map} +0 -0
  303. /package/dist/{chunk-W2BOPXTI.js.map → timeout-BEABACRP.js.map} +0 -0
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/presets/inspect/index.ts","../../../src/presets/inspect/composite.ts","../../../src/utils/inspect/audit.ts","../../../src/base/meta/domain-meta.ts","../../../src/utils/messaging/index.ts","../../../src/base/mutation/index.ts","../../../src/utils/inspect/lens.ts","../../../src/presets/inspect/guarded-execution.ts"],"sourcesContent":["/**\n * Inspect presets — opinionated compositions of inspect utils.\n *\n * @module\n */\n\nexport * from \"./composite.js\";\nexport * from \"./guarded-execution.js\";\n","/**\n * `inspect()` preset — Tier 9.1 γ-form γ-II + Q5-6 medium scope.\n *\n * Composes graph observability into a single mounted facade:\n * - `lens` (mounted as a `LensSubgraph` child) — exposes `topology` /\n * `health` / `flow` Nodes via `inspect.lens.*`.\n * - `audit` (mounted `AuditTrailGraph`) — every mutation on the wrapped\n * target captured as an audit entry.\n * - `explainTarget(from, to, opts?)` — facade over\n * `target.describe({ explain: {...} })`; supports both static and\n * reactive forms.\n * - `complianceSnapshot()` — one-shot tamper-evident snapshot pairing the\n * target's persisted state with the audit log.\n *\n * **Path-namespace boundary.** `inspect.describe()` shows InspectGraph's\n * OWN topology (the lens + audit subgraphs and any caller-added siblings) —\n * NOT the wrapped target's topology. Use `inspect.target.describe()` to\n * walk the target. `inspect.node(\"counter\")` resolves under the inspect\n * graph, NOT the target.\n *\n * **Why a Graph subclass.** Per Tier 9.1 γ-II lock: closure-bundled returns\n * (`{lens, audit, explain, ...}` of independently-constructed primitives)\n * hide topology — `describe()` from the wrapper can't walk into the\n * audit/lens pieces. Mounting them under a real `InspectGraph` keeps every\n * contained primitive visible in describe / explain across the boundary,\n * which is the point of the inspect preset.\n *\n * **Why the lens lives in a child `LensSubgraph` mount.** `Graph.destroy()`\n * signals TEARDOWN through `this._nodes` after disposers drain. If the\n * lens nodes were `add()`ed directly to InspectGraph's path table, they\n * would receive TEARDOWN at parent destroy — invalidating any externally\n * held `view.lens.topology.subscribe(...)` reference. Mounting via a child\n * subgraph contains the cascade: inspect's TEARDOWN visits the lens\n * subgraph's nodes through `mount → child._destroyClearOnly`, which clears\n * structure but does NOT broadcast TEARDOWN. The `lens.dispose()` disposer\n * still tears down the underlying observe handle as designed (D1 fix per\n * /qa lock).\n *\n * **Why `inspect()` mounts `graphLens` rather than rebuilding `health` / `flow`.**\n * Per Q3 yellow lock: rebuilding would duplicate the `topology → health`\n * and `dataFlow → flow` deriveds, doubling subscription cost and risking\n * semantic drift. Mounting graphLens once lets `inspect.lens` and the\n * standalone `graphLens(target)` factory share a single source of truth.\n *\n * @module\n */\n\nimport type { Actor, Node } from \"@graphrefly/pure-ts/core\";\nimport { placeholderArgs } from \"@graphrefly/pure-ts/core\";\nimport { type CausalChain, Graph } from \"@graphrefly/pure-ts/graph\";\nimport {\n\ttype AuditTrailGraph,\n\ttype AuditTrailOptions,\n\tauditTrail,\n\ttype ComplianceSnapshotResult,\n\tcomplianceSnapshot,\n\ttype PolicyGateGraph,\n} from \"../../utils/inspect/audit.js\";\nimport { type GraphLensView, graphLens } from \"../../utils/inspect/lens.js\";\n\n/** Options for {@link inspect}. */\nexport interface InspectOptions {\n\t/** Default actor recorded on `complianceSnapshot()` calls. */\n\tactor?: Actor;\n\t/**\n\t * Forwarded to the mounted {@link auditTrail} so callers can configure\n\t * retention / inclusion policy. Pre-allocated to keep `inspect()`\n\t * callable with no opts.\n\t */\n\taudit?: AuditTrailOptions;\n\t/** Optional name override for the `InspectGraph` itself. */\n\tname?: string;\n}\n\n/**\n * Thin Graph-subclass shell that owns the `graphLens(target)` Nodes so\n * inspect's TEARDOWN cascade reaches them via `_destroyClearOnly` (which\n * clears structure WITHOUT broadcasting TEARDOWN) instead of via\n * `signal([[TEARDOWN]])` (which DOES broadcast and would invalidate any\n * externally held lens-node subscription).\n *\n * @internal\n */\nclass LensSubgraph extends Graph {\n\treadonly view: GraphLensView;\n\n\tconstructor(target: Graph) {\n\t\tsuper(\"lens\");\n\t\tthis.view = graphLens(target);\n\t\tthis.add(this.view.topology, { name: \"topology\" });\n\t\tthis.add(this.view.health, { name: \"health\" });\n\t\tthis.add(this.view.flow, { name: \"flow\" });\n\t}\n}\n\n/**\n * Graph subclass returned by {@link inspect}. Mounts a `graphLens` view (as\n * a child `LensSubgraph`), an `auditTrail` (as a child subgraph), and\n * exposes `explainTarget()` + `complianceSnapshot()` facades over the\n * wrapped target.\n *\n * Mounted children (visible in `describe()`):\n * - `lens::topology` / `lens::health` / `lens::flow` — `graphLens(target)` Nodes.\n * - `audit::*` — the mounted {@link AuditTrailGraph}.\n *\n * @category observability\n */\nexport class InspectGraph extends Graph {\n\treadonly target: Graph;\n\t/**\n\t * Underlying lens view — reach individual Nodes via\n\t * `inspect.lens.topology` / `inspect.lens.health` / `inspect.lens.flow`.\n\t *\n\t * Direct `inspect.topology` / `inspect.health` / `inspect.flow`\n\t * accessors are NOT shipped because `Graph.topology` is already an\n\t * accessor on the base class with a different shape (`Node<TopologyEvent>`\n\t * — the mount/unmount stream of THIS graph, not the wrapped target's\n\t * describe snapshot). Going through `.lens.*` keeps the two concepts\n\t * cleanly separated.\n\t */\n\treadonly lens: GraphLensView;\n\t/** Mounted audit trail subgraph. */\n\treadonly audit: AuditTrailGraph;\n\n\tprivate readonly _defaultActor?: Actor;\n\tprivate readonly _lensSubgraph: LensSubgraph;\n\n\tconstructor(target: Graph, opts: InspectOptions = {}) {\n\t\tsuper(opts.name ?? `inspect(${target.name})`);\n\t\tthis.target = target;\n\t\tthis._defaultActor = opts.actor;\n\n\t\t// D1 (qa lock): lens lives inside a child mount so `inspect.destroy()`'s\n\t\t// TEARDOWN signal cascade reaches the lens nodes via\n\t\t// `_destroyClearOnly` (no broadcast) rather than via `_signalDeliver`\n\t\t// over `inspect._nodes` (which WOULD broadcast). External holders of\n\t\t// `view.lens.topology.subscribe(...)` see only the `lens.dispose()`\n\t\t// teardown of the underlying observe handle, not a stray TEARDOWN\n\t\t// from inspect's path table.\n\t\tthis._lensSubgraph = new LensSubgraph(target);\n\t\tthis.lens = this._lensSubgraph.view;\n\t\tthis.mount(\"lens\", this._lensSubgraph);\n\n\t\tthis.audit = auditTrail(target, opts.audit ?? {});\n\t\tthis.mount(\"audit\", this.audit);\n\n\t\t// Tear down the lens's underlying observe subscription on destroy.\n\t\t// The mounted subgraphs themselves tear down via mount lifecycle.\n\t\tthis.addDisposer(() => this.lens.dispose());\n\t}\n\n\t/**\n\t * Causal-chain facade over `target.describe({ explain: {...} })`. Supports\n\t * both static (one-shot {@link CausalChain}) and reactive\n\t * (`{ reactive: true }`) forms.\n\t *\n\t * Named `explainTarget` (not folded into a `describe` mode on this class)\n\t * because `inspect.describe(...)` walks `InspectGraph`'s OWN topology\n\t * (lens + audit subgraphs) rather than the wrapped target's. Use\n\t * `inspect.explainTarget(...)` for chains across the wrapped graph;\n\t * `inspect.describe({ explain: {...} })` for chains across the lens /\n\t * audit composition.\n\t */\n\texplainTarget(\n\t\tfrom: string | Node<string>,\n\t\tto: string | Node<string>,\n\t\topts?: {\n\t\t\tmaxDepth?: number | Node<number>;\n\t\t\tfindCycle?: boolean | Node<boolean>;\n\t\t},\n\t): CausalChain;\n\texplainTarget(\n\t\tfrom: string | Node<string>,\n\t\tto: string | Node<string>,\n\t\topts: {\n\t\t\treactive: true;\n\t\t\tmaxDepth?: number | Node<number>;\n\t\t\tfindCycle?: boolean | Node<boolean>;\n\t\t\tname?: string;\n\t\t},\n\t): { node: Node<CausalChain>; dispose: () => void };\n\texplainTarget(\n\t\tfrom: string | Node<string>,\n\t\tto: string | Node<string>,\n\t\topts?: {\n\t\t\treactive?: boolean;\n\t\t\tmaxDepth?: number | Node<number>;\n\t\t\tfindCycle?: boolean | Node<boolean>;\n\t\t\tname?: string;\n\t\t},\n\t): CausalChain | { node: Node<CausalChain>; dispose: () => void } {\n\t\t// Cast through the discriminated overload — TypeScript can't pick a\n\t\t// signature on `target.describe({explain})` because `opts` has the\n\t\t// union shape (reactive: boolean | undefined).\n\t\tconst explainArg: {\n\t\t\tfrom: string | Node<string>;\n\t\t\tto: string | Node<string>;\n\t\t\tmaxDepth?: number | Node<number>;\n\t\t\tfindCycle?: boolean | Node<boolean>;\n\t\t} = { from, to };\n\t\tif (opts?.maxDepth !== undefined) explainArg.maxDepth = opts.maxDepth;\n\t\tif (opts?.findCycle !== undefined) explainArg.findCycle = opts.findCycle;\n\t\tconst describeOpts: Record<string, unknown> = { explain: explainArg };\n\t\tif (opts?.reactive === true) describeOpts.reactive = true;\n\t\tif (opts?.name !== undefined) describeOpts.name = opts.name;\n\t\treturn (\n\t\t\tthis.target.describe as unknown as (\n\t\t\t\to: typeof describeOpts,\n\t\t\t) => CausalChain | { node: Node<CausalChain>; dispose: () => void }\n\t\t)(describeOpts);\n\t}\n\n\t/**\n\t * One-shot tamper-evident snapshot pairing the target's persisted state\n\t * with the audit log + (optional) policy-gate violations.\n\t *\n\t * Uses the inspect's mounted `audit` by default; pair with a separate\n\t * `policyGate` (mounted elsewhere) by passing `policies` explicitly.\n\t *\n\t * **Cryptographic strength caveat (echoed from {@link complianceSnapshot}):**\n\t * the returned `fingerprint` is a truncated SHA-256 (16 hex chars / ~64\n\t * bits) optimized for compact archival. Sufficient for casual integrity\n\t * checks and content-addressed dedup; for adversarial tamper-evidence,\n\t * pair with a full SHA-256 over the canonical JSON externally.\n\t */\n\tcomplianceSnapshot(opts?: {\n\t\tactor?: Actor;\n\t\tpolicies?: PolicyGateGraph;\n\t}): ComplianceSnapshotResult {\n\t\tconst actor = opts?.actor ?? this._defaultActor;\n\t\treturn complianceSnapshot(this.target, {\n\t\t\taudit: this.audit,\n\t\t\t...(actor != null ? { actor } : {}),\n\t\t\t...(opts?.policies != null ? { policies: opts.policies } : {}),\n\t\t});\n\t}\n}\n\n/**\n * Build an {@link InspectGraph} that mounts `graphLens` + `auditTrail` over\n * the wrapped target and exposes `explainTarget()` + `complianceSnapshot()`\n * facades.\n *\n * @example\n * ```ts\n * import { inspect } from \"@graphrefly/graphrefly/patterns/inspect\";\n *\n * const target = buildMyApp();\n * const view = inspect(target, { actor: { id: \"ops-bot\", role: \"monitor\" } });\n *\n * // Live observability\n * view.lens.health.subscribe((msgs) => console.log(\"health:\", msgs));\n * view.lens.flow.subscribe((msgs) => console.log(\"flow:\", msgs));\n *\n * // Causal explainability across the wrapped target\n * const chain = view.explainTarget(\"input\", \"output\");\n *\n * // Tamper-evident snapshot for archival\n * const snapshot = view.complianceSnapshot();\n * ```\n *\n * @category observability\n */\nexport function inspect(target: Graph, opts: InspectOptions = {}): InspectGraph {\n\tconst g = new InspectGraph(target, opts);\n\t// A1 (qa lock): self-tag so `inspect.describe().factory === \"inspect\"`,\n\t// matching the policyGate / pipelineGraph / harnessLoop precedent.\n\tg.tagFactory(\"inspect\", placeholderArgs(opts as unknown as Record<string, unknown>));\n\treturn g;\n}\n","/**\n * Audit, policy enforcement, and compliance export (roadmap §9.2).\n *\n * Three composed factories that wrap any {@link Graph} with the harness\n * accountability layer:\n *\n * - {@link auditTrail} — reactive mutation log with by-node/by-actor/by-time\n * queries.\n * - {@link policyGate} — reactive ABAC gate (Tier 2.3 rename of\n * `policyEnforcer`); in `\"audit\"` mode records would-be denials, in\n * `\"enforce\"` mode pushes guards onto target nodes so subsequent writes\n * throw {@link GuardDenied}.\n * - {@link complianceSnapshot} — point-in-time export of graph state +\n * audit trail + policies for regulatory archival.\n *\n * @module\n */\nimport type { Actor, GuardAction, NodeGuard, PolicyRuleData } from \"@graphrefly/pure-ts/core\";\nimport {\n\tbatch,\n\tDATA,\n\tdefaultHash,\n\tmonotonicNs,\n\ttype Node,\n\tNodeImpl,\n\tnode,\n\tplaceholderArgs,\n\tpolicyFromRules,\n\twallClockNs,\n} from \"@graphrefly/pure-ts/core\";\nimport { keepalive, reactiveLog } from \"@graphrefly/pure-ts/extra\";\nimport {\n\tGraph,\n\ttype GraphOptions,\n\ttype GraphPersistSnapshot,\n\ttype TopologyEvent,\n\twatchTopologyTree,\n} from \"@graphrefly/pure-ts/graph\";\nimport { domainMeta } from \"../../base/meta/domain-meta.js\";\nimport { TopicGraph } from \"../messaging/index.js\";\n\n// ---------------------------------------------------------------------------\n// Shared types\n// ---------------------------------------------------------------------------\n\n/** A single recorded mutation/event in an {@link AuditTrailGraph}. */\nexport interface AuditEntry {\n\tseq: number;\n\ttimestamp_ns: number;\n\twall_clock_ns: number;\n\tpath: string;\n\ttype:\n\t\t| \"data\"\n\t\t| \"dirty\"\n\t\t| \"resolved\"\n\t\t| \"invalidate\"\n\t\t| \"pause\"\n\t\t| \"resume\"\n\t\t| \"complete\"\n\t\t| \"error\"\n\t\t| \"teardown\";\n\tactor?: Actor;\n\tvalue?: unknown;\n\terror?: unknown;\n\tannotation?: string;\n}\n\nfunction auditMeta(kind: string, extra?: Record<string, unknown>): Record<string, unknown> {\n\treturn domainMeta(\"audit\", kind, extra);\n}\n\n// ---------------------------------------------------------------------------\n// auditTrail\n// ---------------------------------------------------------------------------\n\nconst DEFAULT_INCLUDE_TYPES: ReadonlySet<AuditEntry[\"type\"]> = new Set([\n\t\"data\",\n\t\"error\",\n\t\"complete\",\n\t\"teardown\",\n]);\n\n/** Options for {@link auditTrail}. */\nexport interface AuditTrailOptions {\n\tname?: string;\n\tgraph?: GraphOptions;\n\t/** Ring-buffer cap for the underlying `reactiveLog`. Default: unbounded. */\n\tmaxSize?: number;\n\t/**\n\t * Which event types to record. Default: `[\"data\", \"error\", \"complete\",\n\t * \"teardown\"]` — the user-meaningful set. Opt in to mid-wave protocol\n\t * events (`\"dirty\"`, `\"resolved\"`, `\"invalidate\"`, `\"pause\"`, `\"resume\"`)\n\t * by listing them explicitly. Note: those tier-1/tier-2 events do not\n\t * carry an `actor` (no `lastMutation` populated) — record them only for\n\t * protocol-level diagnostics.\n\t */\n\tincludeTypes?: readonly AuditEntry[\"type\"][];\n\t/** Per-event filter; return false to skip. */\n\tfilter?: (entry: AuditEntry) => boolean;\n}\n\n/**\n * Mounted audit log — `entries` exposes the reactive `AuditEntry[]`; query\n * helpers are sync convenience wrappers over the cached snapshot.\n */\nexport class AuditTrailGraph extends Graph {\n\treadonly entries: Node<readonly AuditEntry[]>;\n\treadonly count: Node<number>;\n\t/**\n\t * Effective set of event types this trail records (EH-18). Reflects\n\t * either the caller-supplied `opts.includeTypes` or the default set\n\t * (`[\"data\", \"error\", \"complete\", \"teardown\"]`). Captured at construction\n\t * — each instance owns its own clone, so a default-using trail can never\n\t * leak mutations into the module-level default set.\n\t *\n\t * **Mutation contract.** Type-system read-only via `ReadonlySet`. Runtime\n\t * mutation through an unsafe cast (`(audit.includeTypes as Set<...>)\n\t * .add(...)`) is unsupported — it would desync the field from the\n\t * recording closure, which captured the original `Set` reference at\n\t * construction. The runtime does NOT enforce immutability beyond the\n\t * type contract; consumers must respect it.\n\t *\n\t * Use this to validate that a `complianceSnapshot.fingerprint` was\n\t * computed against the same recording surface — fingerprints are stable\n\t * only when the recording set is identical across snapshots.\n\t */\n\treadonly includeTypes: ReadonlySet<AuditEntry[\"type\"]>;\n\tprivate readonly _log;\n\tprivate readonly _target: Graph;\n\n\tconstructor(target: Graph, opts: AuditTrailOptions) {\n\t\tsuper(opts.name ?? `${target.name}_audit`, opts.graph);\n\t\tthis._target = target;\n\t\tthis._log = reactiveLog<AuditEntry>([], {\n\t\t\tname: \"entries\",\n\t\t\t...(opts.maxSize != null ? { maxSize: opts.maxSize } : {}),\n\t\t});\n\t\tthis.entries = this._log.entries;\n\t\tthis.add(this.entries, { name: \"entries\" });\n\n\t\tthis.count = this.derived<number>(\n\t\t\t\"count\",\n\t\t\t[\"entries\"],\n\t\t\t(batchData, ctx) => {\n\t\t\t\tconst data = batchData.map((batch, i) =>\n\t\t\t\t\tbatch != null && batch.length > 0 ? batch.at(-1) : ctx.prevData[i],\n\t\t\t\t);\n\t\t\t\treturn [(data[0] as readonly AuditEntry[]).length];\n\t\t\t},\n\t\t\t{ meta: auditMeta(\"count\") },\n\t\t);\n\t\tthis.addDisposer(keepalive(this.count));\n\n\t\t// Always clone — DEFAULT_INCLUDE_TYPES is a module-level singleton and\n\t\t// must not be shared across instances (a cast-and-mutate via\n\t\t// `audit.includeTypes` would otherwise corrupt every default audit\n\t\t// trail in the process).\n\t\tconst includeTypes: Set<AuditEntry[\"type\"]> =\n\t\t\topts.includeTypes != null ? new Set(opts.includeTypes) : new Set(DEFAULT_INCLUDE_TYPES);\n\t\tthis.includeTypes = includeTypes;\n\t\tconst filter = opts.filter;\n\n\t\t// Monotonic per-trail. **Stagnates** (does not wrap) past\n\t\t// `Number.MAX_SAFE_INTEGER` — IEEE-754 imprecision means `seq + 1 === seq`\n\t\t// once `seq` exceeds 2^53; subsequent records would carry the same\n\t\t// stagnant value and break uniqueness. At 100k events/sec that's\n\t\t// ~3000 years — not a practical concern.\n\t\tlet seq = 0;\n\t\tconst handle = target.observe({ timeline: true, structured: true });\n\t\tconst offEvent = handle.onEvent((event) => {\n\t\t\t// `event.type` includes \"derived\" (causal-trace recompute marker) which\n\t\t\t// isn't a recordable mutation — skip it. Cast through narrowed type\n\t\t\t// after the discriminator check.\n\t\t\tif (event.type === \"derived\") return;\n\t\t\tconst type = event.type as AuditEntry[\"type\"];\n\t\t\tif (!includeTypes.has(type)) return;\n\t\t\tconst path = event.path ?? \"\";\n\t\t\tconst entry: AuditEntry = {\n\t\t\t\tseq: seq++,\n\t\t\t\ttimestamp_ns: event.timestamp_ns ?? monotonicNs(),\n\t\t\t\twall_clock_ns: wallClockNs(),\n\t\t\t\tpath,\n\t\t\t\ttype,\n\t\t\t};\n\t\t\t// Attribution + value enrichment.\n\t\t\tconst node = path ? safeNode(target, path) : undefined;\n\t\t\tconst lastMutation = node?.lastMutation;\n\t\t\tif (lastMutation != null) entry.actor = lastMutation.actor;\n\t\t\tif (type === \"data\") entry.value = (event as { data: unknown }).data;\n\t\t\tif (type === \"error\") entry.error = (event as { data: unknown }).data;\n\t\t\tconst annotation = path ? safeAnnotation(target, path) : undefined;\n\t\t\tif (annotation != null) entry.annotation = annotation;\n\t\t\tif (filter != null && !filter(entry)) return;\n\t\t\tthis._log.append(entry);\n\t\t});\n\n\t\tthis.addDisposer(() => {\n\t\t\toffEvent();\n\t\t\thandle.dispose();\n\t\t});\n\t\tthis.addDisposer(() => this._log.disposeAllViews());\n\t}\n\n\t/** All entries currently in the ring (snapshot). */\n\tall(): readonly AuditEntry[] {\n\t\treturn (this.entries.cache as readonly AuditEntry[] | undefined) ?? [];\n\t}\n\n\t/** Entries matching `path`. Order preserved. */\n\tbyNode(path: string): readonly AuditEntry[] {\n\t\treturn this.all().filter((e) => e.path === path);\n\t}\n\n\t/** Entries whose `actor.id` matches. Use `byActorType` for type filtering. */\n\tbyActor(actorId: string): readonly AuditEntry[] {\n\t\treturn this.all().filter((e) => e.actor?.id === actorId);\n\t}\n\n\t/** Entries whose `actor.type` matches (e.g. `\"llm\"`, `\"human\"`). */\n\tbyActorType(type: string): readonly AuditEntry[] {\n\t\treturn this.all().filter((e) => e.actor?.type === type);\n\t}\n\n\t/**\n\t * Entries with `timestamp_ns` in `[start_ns, end_ns)` (end exclusive).\n\t * Omit `end_ns` to query open-ended.\n\t */\n\tbyTimeRange(start_ns: number, end_ns?: number): readonly AuditEntry[] {\n\t\treturn this.all().filter((e) => {\n\t\t\tif (e.timestamp_ns < start_ns) return false;\n\t\t\tif (end_ns != null && e.timestamp_ns >= end_ns) return false;\n\t\t\treturn true;\n\t\t});\n\t}\n\n\t/** Reference to the audited graph (escape hatch for tooling). */\n\tget target(): Graph {\n\t\treturn this._target;\n\t}\n}\n\n/**\n * Wraps any {@link Graph} with a reactive audit trail recording every event\n * matching `includeTypes` (default: data + error + complete + teardown).\n *\n * Each entry carries `seq`, `timestamp_ns` (monotonic), `wall_clock_ns`,\n * `path`, `type`, and — when available — `actor`, `value`, `error`, and the\n * `graph.trace()` reasoning annotation for the path.\n *\n * The returned graph mounts an `entries` node + `count` derived. Query\n * helpers (`byNode`, `byActor`, `byTimeRange`) operate on the cached\n * snapshot synchronously.\n */\nexport function auditTrail(target: Graph, opts: AuditTrailOptions = {}): AuditTrailGraph {\n\treturn new AuditTrailGraph(target, opts);\n}\n\n// ---------------------------------------------------------------------------\n// policyGate (renamed from `policyEnforcer` per Tier 2.3 — joins the\n// gate-family disambiguation: `valve` (boolean) / `budgetGate` (numeric) /\n// `approvalGate` (human judgment) / `policyGate` (ABAC rules))\n// ---------------------------------------------------------------------------\n\n/** A single policy denial recorded by {@link PolicyGateGraph}. */\nexport interface PolicyViolation {\n\ttimestamp_ns: number;\n\twall_clock_ns: number;\n\tpath: string;\n\tactor: Actor;\n\taction: GuardAction;\n\tmode: \"audit\" | \"enforce\";\n\t/** `\"observed\"` (audit mode after-the-fact) or `\"blocked\"` (enforce mode pre-write). */\n\tresult: \"observed\" | \"blocked\";\n}\n\n/** Options for {@link policyGate}. */\nexport interface PolicyGateOptions {\n\tname?: string;\n\tgraph?: GraphOptions;\n\t/**\n\t * `\"audit\"` (default) — observe events and record would-be denials;\n\t * does not block writes. Audit mode requires `lastMutation` attribution\n\t * on the audited node — anonymous/internal writes (no `actor` passed,\n\t * unguarded node) are skipped silently because the policy cannot be\n\t * evaluated without an actor.\n\t *\n\t * `\"enforce\"` — push guards onto target nodes so disallowed writes\n\t * throw {@link GuardDenied}. Reverted on dispose.\n\t */\n\tmode?: \"audit\" | \"enforce\";\n\t/**\n\t * Restrict enforcement to specific node paths (qualified). When omitted,\n\t * applies to every node visible in `target.describe()` at construction\n\t * time (subgraphs are walked transitively) AND subscribes to the full\n\t * topology tree via {@link watchTopologyTree}, so nodes added to\n\t * `target` OR any transitively-mounted subgraph after construction are\n\t * guarded automatically (enforce mode only).\n\t *\n\t * Accepts a static `readonly string[]` or a reactive\n\t * `Node<readonly string[]>` (Tier 3.4 — F.9 reactive primitive carve-out).\n\t * When a `Node` is passed, the enforcer rebinds the guarded path set on\n\t * every emission: paths added to the new set get wrapped, paths removed\n\t * from the new set get released, and the audit-mode allow-list filter\n\t * uses the latest cached value. Static-array callers retain the current\n\t * \"caller owns the path set\" semantics.\n\t *\n\t * **Cost:** unrestricted mode runs `describe({detail:\"minimal\"})` once\n\t * at construction (O(N) over the graph tree) plus one topology\n\t * subscription per graph instance in the mount tree. Restricted mode\n\t * (static or reactive) skips both and disables `watchTopologyTree`\n\t * dynamic coverage — for reactive callers, the path-set Node is the\n\t * single source of truth for which paths are guarded.\n\t */\n\tpaths?: readonly string[] | Node<readonly string[]>;\n\t/**\n\t * Ring-buffer cap for the violations topic. Default: 1000. Static\n\t * number only — reactive form is deferred pending TopicGraph reactive\n\t * `retainedLimit` support (see Tier 10.8 design follow-up in\n\t * `docs/optimizations.md`).\n\t */\n\tviolationsLimit?: number;\n}\n\n/**\n * Reactive ABAC enforcement layer. Policies are reactive — pass a\n * `Node<readonly PolicyRuleData[]>` to allow LLMs (or any reactive source)\n * to update them at runtime; the enforcer rebinds its internal\n * {@link NodeGuard} on every push.\n */\nexport class PolicyGateGraph extends Graph {\n\treadonly policies: Node<readonly PolicyRuleData[]>;\n\treadonly violations: TopicGraph<PolicyViolation>;\n\treadonly violationCount: Node<number>;\n\tprivate readonly _target: Graph;\n\tprivate readonly _mode: \"audit\" | \"enforce\";\n\tprivate _currentGuard: NodeGuard;\n\n\tconstructor(\n\t\ttarget: Graph,\n\t\tpolicies: readonly PolicyRuleData[] | Node<readonly PolicyRuleData[]>,\n\t\topts: PolicyGateOptions,\n\t) {\n\t\tsuper(opts.name ?? `${target.name}_policy`, opts.graph);\n\t\tthis._target = target;\n\t\tthis._mode = opts.mode ?? \"audit\";\n\n\t\tconst policiesNode = isNode(policies)\n\t\t\t? policies\n\t\t\t: node<readonly PolicyRuleData[]>([], { name: \"policies\", initial: policies });\n\t\tthis.policies = policiesNode;\n\t\tthis.add(this.policies, { name: \"policies\" });\n\n\t\tthis.violations = new TopicGraph<PolicyViolation>(\"violations\", {\n\t\t\tretainedLimit: opts.violationsLimit ?? 1000,\n\t\t});\n\t\tthis.mount(\"violations\", this.violations);\n\n\t\tthis.violationCount = this.derived<number>(\n\t\t\t\"violationCount\",\n\t\t\t[\"violations::events\"],\n\t\t\t(batchData, ctx) => {\n\t\t\t\tconst data = batchData.map((batch, i) =>\n\t\t\t\t\tbatch != null && batch.length > 0 ? batch.at(-1) : ctx.prevData[i],\n\t\t\t\t);\n\t\t\t\treturn [(data[0] as readonly PolicyViolation[]).length];\n\t\t\t},\n\t\t\t{\n\t\t\t\tmeta: auditMeta(\"policy_violation_count\"),\n\t\t\t},\n\t\t);\n\t\tthis.addDisposer(keepalive(this.violationCount));\n\n\t\t// Factory-time seed (COMPOSITION-GUIDE §28): cache the latest rules\n\t\t// inside a closure, refresh on each subscribe-pushed update, and read\n\t\t// closure inside the guard so policy updates take effect immediately.\n\t\tconst initialRules = (policiesNode.cache as readonly PolicyRuleData[] | undefined) ?? [];\n\t\tlet latestRules: readonly PolicyRuleData[] = initialRules;\n\t\tthis._currentGuard = policyFromRules(latestRules);\n\t\tconst offPolicies = policiesNode.subscribe((msgs) => {\n\t\t\tfor (const m of msgs) {\n\t\t\t\tif (m[0] === DATA) {\n\t\t\t\t\tlatestRules = (m[1] as readonly PolicyRuleData[] | undefined) ?? [];\n\t\t\t\t\tthis._currentGuard = policyFromRules(latestRules);\n\t\t\t\t}\n\t\t\t}\n\t\t});\n\t\tthis.addDisposer(offPolicies);\n\n\t\t// Resolve `paths` option to its three modes:\n\t\t// (a) undefined → dynamic coverage via watchTopologyTree\n\t\t// (b) static readonly string[] → caller owns the set\n\t\t// (c) Node<readonly string[]> → reactive set; rebind on each emission\n\t\t// `latestPaths` is the closure-mirror of the current path set (or\n\t\t// undefined for dynamic coverage). It is read by:\n\t\t// - the audit-mode observe callback (allow-list filter)\n\t\t// - the reactive-paths rebind subscription (diff against next set)\n\t\t// Mirrors the `latestRules` pattern used for `policiesNode` above\n\t\t// (COMPOSITION-GUIDE §28 factory-time seed).\n\t\tconst pathsOpt = opts.paths;\n\t\tconst pathsNode: Node<readonly string[]> | undefined = isNode(pathsOpt)\n\t\t\t? (pathsOpt as Node<readonly string[]>)\n\t\t\t: undefined;\n\t\t// `pathsExplicit` mirrors the legacy \"caller provided a path set\" branch\n\t\t// — true for both static-array and Node-of-array forms; false only when\n\t\t// `opts.paths` is omitted (dynamic-coverage mode).\n\t\tconst pathsExplicit = pathsOpt != null;\n\t\tconst initialPaths: readonly string[] | undefined =\n\t\t\tpathsNode != null\n\t\t\t\t? ((pathsNode.cache as readonly string[] | undefined) ?? [])\n\t\t\t\t: pathsExplicit\n\t\t\t\t\t? [...(pathsOpt as readonly string[])]\n\t\t\t\t\t: undefined;\n\t\t// `latestPaths` is undefined ONLY in dynamic-coverage mode.\n\t\tlet latestPaths: readonly string[] | undefined = initialPaths;\n\t\t// Initial sweep set for the enforce-mode wrap loop.\n\t\tconst paths = latestPaths ?? collectPaths(target);\n\n\t\t// Audit-mode reactive-paths subscription. Enforce mode handles its own\n\t\t// subscription (it also needs to diff old↔new to wrap/release guards);\n\t\t// audit mode just needs `latestPaths` to track the latest cache so the\n\t\t// allow-list filter stays current. Wired here, before the mode branch,\n\t\t// so it runs in audit mode only — enforce mode wires its own\n\t\t// rebinding subscription with diffing logic (DRY would require an\n\t\t// always-on tracker that enforce mode then ignores; the small\n\t\t// duplication keeps the enforce-mode branch self-contained).\n\t\tif (this._mode !== \"enforce\" && pathsNode != null) {\n\t\t\tconst offAuditPaths = pathsNode.subscribe((msgs) => {\n\t\t\t\tfor (const m of msgs) {\n\t\t\t\t\tif (m[0] !== DATA) continue;\n\t\t\t\t\tlatestPaths = (m[1] as readonly string[] | undefined) ?? [];\n\t\t\t\t}\n\t\t\t});\n\t\t\tthis.addDisposer(offAuditPaths);\n\t\t}\n\n\t\tif (this._mode === \"enforce\") {\n\t\t\t// Track which paths are currently guarded so dynamic adds don't\n\t\t\t// double-wrap and removed nodes release guard handles.\n\t\t\tconst restorers = new Map<string, () => void>();\n\t\t\tconst wrapAndPush = (path: string): void => {\n\t\t\t\tif (restorers.has(path)) return;\n\t\t\t\tconst node = safeNode(target, path);\n\t\t\t\tif (!(node instanceof NodeImpl)) return;\n\t\t\t\tconst pathGuard: NodeGuard = (actor, action) => {\n\t\t\t\t\tconst ok = this._currentGuard(actor, action);\n\t\t\t\t\tif (!ok) {\n\t\t\t\t\t\tthis._publishViolation(actor, action, path, \"blocked\");\n\t\t\t\t\t}\n\t\t\t\t\treturn ok;\n\t\t\t\t};\n\t\t\t\trestorers.set(path, node._pushGuard(pathGuard));\n\t\t\t};\n\t\t\t// Initial sweep: guard every path present at construction.\n\t\t\tfor (const path of paths) wrapAndPush(path);\n\n\t\t\t// Reactive paths rebind: when `paths` is a Node, every DATA emission\n\t\t\t// replaces `latestPaths` and diffs against the previous set —\n\t\t\t// added paths get wrapped, removed paths release their guard. No\n\t\t\t// imperative orchestration; the diff falls out of the closure-mirror\n\t\t\t// + subscribe pattern (mirrors `policiesNode` above).\n\t\t\tif (pathsNode != null) {\n\t\t\t\tconst offReactivePaths = pathsNode.subscribe((msgs) => {\n\t\t\t\t\tfor (const m of msgs) {\n\t\t\t\t\t\tif (m[0] !== DATA) continue;\n\t\t\t\t\t\tconst next = (m[1] as readonly string[] | undefined) ?? [];\n\t\t\t\t\t\tconst nextSet = new Set(next);\n\t\t\t\t\t\tconst prevSet = new Set(latestPaths ?? []);\n\t\t\t\t\t\t// Wrap rebind in `batch()` (qa D7) — guards are imperative\n\t\t\t\t\t\t// graph mutations; if `paths` and `policies` co-emit in an\n\t\t\t\t\t\t// outer batch (atomic config swap), each handler's mutations\n\t\t\t\t\t\t// would otherwise unwind in arbitrary order, letting an\n\t\t\t\t\t\t// in-flight write hit a half-rebound guard set. Batching\n\t\t\t\t\t\t// coalesces release + wrap into a single deferred drain.\n\t\t\t\t\t\tbatch(() => {\n\t\t\t\t\t\t\t// Release paths that fell out of the new set.\n\t\t\t\t\t\t\tfor (const p of prevSet) {\n\t\t\t\t\t\t\t\tif (nextSet.has(p)) continue;\n\t\t\t\t\t\t\t\tconst r = restorers.get(p);\n\t\t\t\t\t\t\t\tif (r != null) {\n\t\t\t\t\t\t\t\t\tr();\n\t\t\t\t\t\t\t\t\trestorers.delete(p);\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t// Wrap newly-added paths.\n\t\t\t\t\t\t\tfor (const p of nextSet) {\n\t\t\t\t\t\t\t\tif (prevSet.has(p)) continue;\n\t\t\t\t\t\t\t\twrapAndPush(p);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tlatestPaths = next;\n\t\t\t\t\t\t});\n\t\t\t\t\t}\n\t\t\t\t});\n\t\t\t\tthis.addDisposer(offReactivePaths);\n\t\t\t}\n\n\t\t\t// Dynamic coverage: when `paths` was NOT explicitly provided, follow\n\t\t\t// the full topology tree (target + every transitively-mounted\n\t\t\t// subgraph, including subgraphs mounted after construction) so late\n\t\t\t// adds at any depth get guarded. `prefix` carries the qualified\n\t\t\t// path-prefix from `target` to the emitter graph.\n\t\t\tif (!pathsExplicit) {\n\t\t\t\tconst offTopology = watchTopologyTree(target, (event, emitter, prefix) => {\n\t\t\t\t\tif (event.kind === \"added\") {\n\t\t\t\t\t\tif (event.nodeKind === \"node\") {\n\t\t\t\t\t\t\twrapAndPush(`${prefix}${event.name}`);\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t// Mount added. Walk just the newly-mounted subgraph's local\n\t\t\t\t\t\t\t// paths (scoped describe — O(M) in the mounted subtree)\n\t\t\t\t\t\t\t// rather than re-describing the entire target tree. The\n\t\t\t\t\t\t\t// emitter is the PARENT of the new mount; resolve the child\n\t\t\t\t\t\t\t// via its `_mounts` map.\n\t\t\t\t\t\t\tconst child = emitter._mounts.get(event.name);\n\t\t\t\t\t\t\tif (!(child instanceof Graph)) return;\n\t\t\t\t\t\t\tconst mountPrefix = `${prefix}${event.name}::`;\n\t\t\t\t\t\t\tconst localPaths = collectPaths(child);\n\t\t\t\t\t\t\tfor (const localPath of localPaths) {\n\t\t\t\t\t\t\t\t// `localPath` is relative to `child`; qualify with the\n\t\t\t\t\t\t\t\t// mount prefix so guard keys stay target-rooted.\n\t\t\t\t\t\t\t\twrapAndPush(\n\t\t\t\t\t\t\t\t\tlocalPath === \"\" ? `${prefix}${event.name}` : `${mountPrefix}${localPath}`,\n\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t} else if (event.kind === \"removed\") {\n\t\t\t\t\t\t// TEARDOWN already unhooks the guard; release bookkeeping so\n\t\t\t\t\t\t// re-adds under the same qualified path re-wrap cleanly.\n\t\t\t\t\t\tif (event.nodeKind === \"node\") {\n\t\t\t\t\t\t\tconst qp = `${prefix}${event.name}`;\n\t\t\t\t\t\t\tconst r = restorers.get(qp);\n\t\t\t\t\t\t\tif (r != null) {\n\t\t\t\t\t\t\t\tr();\n\t\t\t\t\t\t\t\trestorers.delete(qp);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tconst mountQp = `${prefix}${event.name}`;\n\t\t\t\t\t\t\tconst mountPrefix = `${mountQp}::`;\n\t\t\t\t\t\t\tfor (const [p, r] of restorers) {\n\t\t\t\t\t\t\t\tif (p === mountQp || p.startsWith(mountPrefix)) {\n\t\t\t\t\t\t\t\t\tr();\n\t\t\t\t\t\t\t\t\trestorers.delete(p);\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t});\n\t\t\t\tthis.addDisposer(offTopology);\n\t\t\t} else {\n\t\t\t\t// Restricted mode: subscribe to target.topology (own-graph only —\n\t\t\t\t// explicit `paths` means caller owns the path set) so node removals\n\t\t\t\t// release their restorers instead of leaking until enforcer dispose.\n\t\t\t\tconst offCleanup = target.topology.subscribe((msgs) => {\n\t\t\t\t\tfor (const m of msgs) {\n\t\t\t\t\t\tif (m[0] !== DATA) continue;\n\t\t\t\t\t\tconst event = m[1] as TopologyEvent;\n\t\t\t\t\t\tif (event.kind !== \"removed\" || event.nodeKind !== \"node\") continue;\n\t\t\t\t\t\tconst r = restorers.get(event.name);\n\t\t\t\t\t\tif (r != null) {\n\t\t\t\t\t\t\tr();\n\t\t\t\t\t\t\trestorers.delete(event.name);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t});\n\t\t\t\tthis.addDisposer(offCleanup);\n\t\t\t}\n\t\t\tthis.addDisposer(() => {\n\t\t\t\tfor (const r of restorers.values()) r();\n\t\t\t\trestorers.clear();\n\t\t\t});\n\t\t} else {\n\t\t\t// Audit mode: observe writes, evaluate against current guard, record\n\t\t\t// violations without blocking. Use the structured observe stream so\n\t\t\t// `path` and `actor` attribution are supplied without per-node\n\t\t\t// subscription bookkeeping. B9: unattributed writes no longer skip\n\t\t\t// — the ObserveEvent always carries a well-formed `actor` (falling\n\t\t\t// back to `DEFAULT_ACTOR` for anonymous/internal writes), so the\n\t\t\t// policy is evaluated against every write.\n\t\t\tconst handle = target.observe({ timeline: true, structured: true });\n\t\t\tconst off = handle.onEvent((event) => {\n\t\t\t\tif (event.type !== \"data\" && event.type !== \"error\") return;\n\t\t\t\tconst path = event.path ?? \"\";\n\t\t\t\tif (!path) return;\n\t\t\t\t// `latestPaths` is the closure-mirror of the (possibly reactive)\n\t\t\t\t// path allow-list. Undefined = no restriction (dynamic-coverage\n\t\t\t\t// mode); reactive callers see the filter rebind on each emission\n\t\t\t\t// without re-creating the enforcer (Tier 3.4).\n\t\t\t\tif (latestPaths != null && !latestPaths.includes(path)) return;\n\t\t\t\t// Prefer the event-stamped actor (always populated for DATA/ERROR\n\t\t\t\t// post-B9). Fall back to lastMutation for back-compat with any\n\t\t\t\t// consumer stubbing observe events without the field.\n\t\t\t\tconst actor =\n\t\t\t\t\t(event as { actor?: Actor }).actor ?? safeNode(target, path)?.lastMutation?.actor;\n\t\t\t\tif (actor == null) return; // defensive — shouldn't happen post-B9\n\t\t\t\tconst action: GuardAction = \"write\";\n\t\t\t\tif (this._currentGuard(actor, action)) return;\n\t\t\t\tthis._publishViolation(actor, action, path, \"observed\");\n\t\t\t});\n\t\t\tthis.addDisposer(() => {\n\t\t\t\toff();\n\t\t\t\thandle.dispose();\n\t\t\t});\n\t\t}\n\t}\n\n\tprivate _publishViolation(\n\t\tactor: Actor,\n\t\taction: GuardAction,\n\t\tpath: string,\n\t\tresult: \"observed\" | \"blocked\",\n\t): void {\n\t\tthis.violations.publish({\n\t\t\ttimestamp_ns: monotonicNs(),\n\t\t\twall_clock_ns: wallClockNs(),\n\t\t\tpath,\n\t\t\tactor,\n\t\t\taction,\n\t\t\tmode: this._mode,\n\t\t\tresult,\n\t\t});\n\t}\n\n\t/** Snapshot of recorded violations. */\n\tall(): readonly PolicyViolation[] {\n\t\treturn this.violations.retained();\n\t}\n\n\tget mode(): \"audit\" | \"enforce\" {\n\t\treturn this._mode;\n\t}\n\n\tget target(): Graph {\n\t\treturn this._target;\n\t}\n}\n\n/**\n * Wraps a {@link Graph} with reactive policy enforcement. Pass either a\n * static rule list or a {@link Node} of rules (LLM-updatable). Records\n * `PolicyViolation` entries to `violations` topic; in `\"enforce\"` mode also\n * pushes guards onto target nodes so disallowed writes throw.\n *\n * Self-tags via `g.tagFactory(\"policyGate\", placeholderArgs(opts))` so\n * `graph.describe()` surfaces `factory: \"policyGate\"` provenance (Phase 2.5\n * DT5 ride-along, locked with the Tier 2.3 rename).\n */\nexport function policyGate(\n\ttarget: Graph,\n\tpolicies: readonly PolicyRuleData[] | Node<readonly PolicyRuleData[]>,\n\topts: PolicyGateOptions = {},\n): PolicyGateGraph {\n\tconst g = new PolicyGateGraph(target, policies, opts);\n\t// `placeholderArgs` walks `opts` for non-JSON fields (e.g. `policies` may\n\t// be a Node when the caller wants live-updatable rules; `opts.graph` is\n\t// `GraphOptions`). DT5 deferred tag; Tier 2.3 ride-along.\n\tg.tagFactory(\"policyGate\", placeholderArgs(opts as unknown as Record<string, unknown>));\n\treturn g;\n}\n\n// ---------------------------------------------------------------------------\n// complianceSnapshot\n// ---------------------------------------------------------------------------\n\n/** Options for {@link complianceSnapshot}. */\nexport interface ComplianceSnapshotOptions {\n\taudit?: AuditTrailGraph;\n\tpolicies?: PolicyGateGraph;\n\t/** Actor recorded as the snapshot taker. */\n\tactor?: Actor;\n}\n\n/** Output of {@link complianceSnapshot}. JSON-serializable. */\nexport interface ComplianceSnapshotResult {\n\tformat_version: 1;\n\ttimestamp_ns: number;\n\twall_clock_ns: number;\n\tactor?: Actor;\n\tgraph: GraphPersistSnapshot;\n\taudit?: { count: number; entries: AuditEntry[] };\n\tpolicies?: {\n\t\tmode: \"audit\" | \"enforce\";\n\t\trules: readonly PolicyRuleData[];\n\t\tviolations: readonly PolicyViolation[];\n\t};\n\t/**\n\t * Truncated SHA-256 hex (16 chars / ~64 bits) over a canonical encoding\n\t * of every field above (excluding `fingerprint` itself). Deterministic\n\t * across runs given identical inputs. Suitable for casual tamper-evidence\n\t * and content-addressed dedup; for full cryptographic strength, hash the\n\t * canonical JSON externally with Web Crypto / Node `crypto`.\n\t */\n\tfingerprint: string;\n}\n\n/**\n * One-shot point-in-time export of a {@link Graph}'s state plus optional\n * audit + policy bundles. Returns a JSON-serializable object with a\n * deterministic truncated-SHA-256 {@link ComplianceSnapshotResult.fingerprint}\n * over the canonical payload for tamper-evidence in regulatory archival.\n *\n * **Cryptographic strength:** the fingerprint is truncated to 64 bits for\n * compact archival. Collision-resistant for casual integrity checks but NOT\n * sufficient for adversarial tamper-evidence — pair with a full SHA-256\n * (or stronger) over the canonical JSON when regulatory requirements demand\n * collision resistance.\n */\nexport function complianceSnapshot(\n\ttarget: Graph,\n\topts: ComplianceSnapshotOptions = {},\n): ComplianceSnapshotResult {\n\tconst result: Omit<ComplianceSnapshotResult, \"fingerprint\"> = {\n\t\tformat_version: 1,\n\t\ttimestamp_ns: monotonicNs(),\n\t\twall_clock_ns: wallClockNs(),\n\t\tgraph: target.snapshot() as GraphPersistSnapshot,\n\t};\n\tif (opts.actor != null) result.actor = opts.actor;\n\tif (opts.audit != null) {\n\t\tconst entries = [...opts.audit.all()];\n\t\tresult.audit = { count: entries.length, entries };\n\t}\n\tif (opts.policies != null) {\n\t\tconst rules = (opts.policies.policies.cache as readonly PolicyRuleData[] | undefined) ?? [];\n\t\tresult.policies = {\n\t\t\tmode: opts.policies.mode,\n\t\t\trules,\n\t\t\tviolations: [...opts.policies.all()],\n\t\t};\n\t}\n\tconst fingerprint = computeFingerprint(result);\n\treturn { ...result, fingerprint };\n}\n\n// ---------------------------------------------------------------------------\n// Internals\n// ---------------------------------------------------------------------------\n\nfunction isNode<T>(x: unknown): x is Node<T> {\n\treturn typeof x === \"object\" && x !== null && \"subscribe\" in (x as object);\n}\n\nfunction safeNode(target: Graph, path: string): Node | undefined {\n\ttry {\n\t\treturn target.node(path);\n\t} catch {\n\t\treturn undefined;\n\t}\n}\n\nfunction safeAnnotation(target: Graph, path: string): string | undefined {\n\ttry {\n\t\treturn target.annotation(path);\n\t} catch {\n\t\treturn undefined;\n\t}\n}\n\n/**\n * Walks every locally-registered node path in `target`, descending through\n * mounted subgraphs. Returns qualified paths.\n */\nfunction collectPaths(target: Graph): string[] {\n\tconst described = target.describe({ detail: \"minimal\" });\n\treturn Object.keys(described.nodes);\n}\n\n/**\n * Stable canonical JSON → truncated SHA-256 hex fingerprint (16 hex chars,\n * ~64-bit). Uses the same vendored sync SHA-256 as `core/versioning.ts`\n * `defaultHash`, so cross-module fingerprints stay consistent.\n *\n * Canonicalization handles cycles (recursion-stack tracker), `undefined`,\n * `bigint`, `Map`, `Set`, `Date`, `RegExp`, and typed arrays via typed\n * markers — see {@link canonicalize}.\n *\n * **Note:** truncated to 16 hex chars (~64-bit) for compact archival. For\n * full 256-bit cryptographic strength, hash {@link complianceSnapshot} JSON\n * externally with Web Crypto / Node `crypto`.\n */\nfunction computeFingerprint(value: unknown): string {\n\t// Pre-stringify our canonical form so `defaultHash`'s\n\t// `canonicalizeForHash` (which rejects unsafe integers) only ever sees a\n\t// JSON string. Compliance payloads carry `timestamp_ns` values that\n\t// exceed `Number.MAX_SAFE_INTEGER` — JSON.stringify handles them fine,\n\t// the hash function only cares about deterministic input bytes.\n\treturn defaultHash(JSON.stringify(canonicalize(value)));\n}\n\n/**\n * Cycle-safe canonical encoding. Uses a recursion-stack `Set` (push on\n * descent, pop on return) so legitimate DAG re-references are encoded as\n * themselves; only true cycles produce a `__circular: true` marker. Typed\n * markers preserve `undefined` / `bigint` / `Map` / `Set` / `Date` / `RegExp`\n * / typed-array information that bare `JSON.stringify` would silently drop\n * or collide with strings.\n */\nfunction canonicalize(value: unknown): unknown {\n\tconst stack = new Set<object>();\n\tconst walk = (v: unknown): unknown => {\n\t\tif (v === undefined) return { __undefined: true };\n\t\tif (v === null) return null;\n\t\tconst t = typeof v;\n\t\tif (t === \"bigint\") return { __bigint: (v as bigint).toString() };\n\t\tif (t !== \"object\") return v;\n\t\tconst obj = v as object;\n\t\tif (stack.has(obj)) return { __circular: true };\n\t\tstack.add(obj);\n\t\ttry {\n\t\t\tif (Array.isArray(obj)) {\n\t\t\t\treturn (obj as unknown[]).map(walk);\n\t\t\t}\n\t\t\tif (obj instanceof Date) {\n\t\t\t\treturn { __date: obj.toISOString() };\n\t\t\t}\n\t\t\tif (obj instanceof RegExp) {\n\t\t\t\treturn { __regexp: { source: obj.source, flags: obj.flags } };\n\t\t\t}\n\t\t\tif (obj instanceof Map) {\n\t\t\t\tconst entries = [...(obj as Map<unknown, unknown>).entries()].map(([k, mv]) => [\n\t\t\t\t\twalk(k),\n\t\t\t\t\twalk(mv),\n\t\t\t\t]);\n\t\t\t\treturn { __map: entries };\n\t\t\t}\n\t\t\tif (obj instanceof Set) {\n\t\t\t\tconst items = [...(obj as Set<unknown>)].map(walk);\n\t\t\t\treturn { __set: items };\n\t\t\t}\n\t\t\tif (ArrayBuffer.isView(obj)) {\n\t\t\t\tconst ta = obj as unknown as { length: number; [i: number]: number };\n\t\t\t\tconst arr: number[] = new Array(ta.length);\n\t\t\t\tfor (let i = 0; i < ta.length; i++) arr[i] = ta[i] ?? 0;\n\t\t\t\treturn { __typed_array: { ctor: obj.constructor.name, data: arr } };\n\t\t\t}\n\t\t\tconst out: Record<string, unknown> = {};\n\t\t\tfor (const k of Object.keys(obj as Record<string, unknown>).sort()) {\n\t\t\t\tout[k] = walk((obj as Record<string, unknown>)[k]);\n\t\t\t}\n\t\t\treturn out;\n\t\t} finally {\n\t\t\tstack.delete(obj);\n\t\t}\n\t};\n\treturn walk(value);\n}\n\n// `explainPath` / `CausalChain` / `CausalStep` are exported from `graph/`\n// at module root; do not re-export here to keep the namespace boundary clean\n// and avoid duplicate-identifier issues in bundled .d.ts.\n","/**\n * Metadata helpers for pattern-layer nodes (Tier 2.2 promotion from\n * `patterns/_internal/`).\n *\n * Each domain (orchestration, messaging, reduction, ai, cqrs, domain_template,\n * memory, lens, audit, harness) shares the same metadata convention. Promoted\n * to `extra/` so non-patterns code (and downstream consumers building their\n * own domain primitives) can use the same shape.\n *\n * @module\n */\n\n/**\n * Build a domain metadata object for pattern-layer nodes.\n *\n * Each domain follows the same shape: `{ [domain]: true, [domain]_type: kind, ...extra }`.\n *\n * @param domain - The domain tag (e.g. `\"orchestration\"`, `\"ai\"`, `\"cqrs\"`).\n * @param kind - The specific type within the domain (e.g. `\"gate\"`, `\"prompt\"`).\n * @param extra - Additional metadata to merge.\n * @returns Metadata object.\n */\nexport function domainMeta(\n\tdomain: string,\n\tkind: string,\n\textra?: Record<string, unknown>,\n): Record<string, unknown> {\n\treturn {\n\t\t[domain]: true,\n\t\t[`${domain}_type`]: kind,\n\t\t...(extra ?? {}),\n\t};\n}\n","/**\n * Messaging patterns (roadmap §4.2).\n *\n * Pulsar-inspired messaging primitives modeled as graph factories:\n * - `topic()` for append-only topic streams with a retained window.\n * - `subscription()` for cursor-based consumers.\n * - `topicBridge()` for autonomous topic-to-topic relay.\n * - `messagingHub()` for a lazy topic registry.\n *\n * Plus the Phase 13.B standard `Message<T>` envelope and well-known topic\n * name constants ({@link PROMPTS_TOPIC} / {@link RESPONSES_TOPIC} /\n * {@link INJECTIONS_TOPIC} / {@link DEFERRED_TOPIC} / {@link SPAWNS_TOPIC})\n * — recommended (not enforced) wire shape for cross-graph topic payloads.\n *\n * Job queue / job flow primitives live in `patterns/job-queue` — they are a\n * distinct domain that happens to share reactive-log / reactive-map\n * infrastructure with topics.\n */\n\nexport {\n\ttype HubRemoveTopicRecord,\n\thubRemoveTopicKeyOf,\n\ttype MessagingAuditRecord,\n\ttype SubscriptionAckRecord,\n\ttype SubscriptionPullAndAckRecord,\n\tsubscriptionAckKeyOf,\n\tsubscriptionPullAndAckKeyOf,\n\ttype TopicPublishRecord,\n\ttopicPublishKeyOf,\n} from \"./audit-records.js\";\nexport {\n\tCONTEXT_TOPIC,\n\tDEFERRED_TOPIC,\n\tINJECTIONS_TOPIC,\n\ttype JsonSchema,\n\tPROMPTS_TOPIC,\n\tRESPONSES_TOPIC,\n\tSPAWNS_TOPIC,\n\tSTANDARD_TOPICS,\n\ttype StandardTopic,\n\tTODOS_TOPIC,\n\ttype TopicMessage,\n} from \"./message.js\";\n\nimport { batch, COMPLETE, DATA, type Node, node } from \"@graphrefly/pure-ts/core\";\nimport { keepalive, type ReactiveLogBundle, reactiveLog } from \"@graphrefly/pure-ts/extra\";\nimport { Graph, type GraphOptions } from \"@graphrefly/pure-ts/graph\";\nimport { domainMeta } from \"../../base/meta/domain-meta.js\";\nimport { mutate } from \"../../base/mutation/index.js\";\n\nconst DEFAULT_MAX_PER_PUMP = 256;\n\nfunction requireNonNegativeInt(value: number, label: string): number {\n\tif (!Number.isFinite(value) || !Number.isInteger(value) || value < 0) {\n\t\tthrow new Error(`${label} must be a non-negative integer`);\n\t}\n\treturn value;\n}\n\nfunction messagingMeta(kind: string, extra?: Record<string, unknown>): Record<string, unknown> {\n\treturn domainMeta(\"messaging\", kind, extra);\n}\n\nexport type TopicOptions = {\n\tgraph?: GraphOptions;\n\t/** Bounded retention; default 1024 per cross-cutting policy (Audit 2/4). */\n\tretainedLimit?: number;\n};\n\nconst DEFAULT_TOPIC_RETAINED_LIMIT = 1024;\n\nexport class TopicGraph<T> extends Graph {\n\tprivate readonly _log;\n\tprivate readonly _publishImpl: (value: T) => void;\n\treadonly events: Node<readonly T[]>;\n\t/**\n\t * Most recently published value. Stays in the protocol SENTINEL state\n\t * (`cache === undefined`, no DATA emitted) until the first publish, then\n\t * tracks the latest entry. Spec §5.12 reserves `undefined` as the\n\t * \"never sent DATA\" sentinel — and `TopicGraph.publish(undefined)` is\n\t * rejected — so `cache === undefined` unambiguously signals \"empty topic\"\n\t * even when `T` itself includes `null` (i.e., `topic<number | null>`).\n\t *\n\t * **Within a reactive fn:** detect the empty-topic case via\n\t * `ctx.prevData[i] === undefined` for the dep slot holding `topic.latest`,\n\t * or check `latest.cache === undefined` outside reactive code. No\n\t * separate `hasLatest` companion needed — the SENTINEL is the answer.\n\t */\n\treadonly latest: Node<T>;\n\n\tconstructor(name: string, opts: TopicOptions = {}) {\n\t\tsuper(name, opts.graph);\n\t\tthis._log = reactiveLog<T>([], {\n\t\t\tname: \"events\",\n\t\t\tmaxSize: opts.retainedLimit ?? DEFAULT_TOPIC_RETAINED_LIMIT,\n\t\t});\n\t\tthis.events = this._log.entries;\n\t\tthis.add(this.events, { name: \"events\" });\n\t\t// `this.derived(\"latest\", [\"events\"], …)` is expressible after the\n\t\t// 2026-04-30 self-resolve fix in `Graph._resolveFromSegments` — a\n\t\t// single-segment path matching the graph's own name (e.g.\n\t\t// `topic(\"events\").resolve(\"events\")`) no longer collapses to empty\n\t\t// and falls through to local-node lookup. Replaces the prior\n\t\t// `node([events], …) + this.add(...)` workaround.\n\t\t//\n\t\t// SENTINEL on empty: returning `[]` here yields a RESOLVED-only wave\n\t\t// (no DATA), so `latest.cache` stays `undefined` until the first\n\t\t// publish. `TopicGraph.publish(undefined)` is rejected (line below),\n\t\t// so `undefined` cache is unambiguously \"empty topic\" even when `T`\n\t\t// itself includes `null`. Drops the prior `hasLatest` companion as\n\t\t// redundant.\n\t\tthis.latest = this.derived<T>(\n\t\t\t\"latest\",\n\t\t\t[\"events\"],\n\t\t\t(batchData, ctx) => {\n\t\t\t\tconst data = batchData.map((batch, i) =>\n\t\t\t\t\tbatch != null && batch.length > 0 ? batch.at(-1) : ctx.prevData[i],\n\t\t\t\t);\n\t\t\t\tconst entries = data[0] as readonly T[];\n\t\t\t\treturn entries.length === 0 ? [] : [entries[entries.length - 1] as T];\n\t\t\t},\n\t\t\t{ meta: messagingMeta(\"topic_latest\") },\n\t\t);\n\t\tthis.addDisposer(keepalive(this.latest));\n\n\t\t// D1(a): on teardown, propagate COMPLETE on `events` so downstream\n\t\t// derived chains (including any externally-held SubscriptionGraph\n\t\t// sources) see the termination via their `terminalDeps` and can stop\n\t\t// serving stale caches. Tier-3 terminal per spec §2.2.\n\t\t//\n\t\t// EC16 (verified 2026-04-30): the COMPLETE-then-disposeAllViews order\n\t\t// is intentional. COMPLETE propagates SYNCHRONOUSLY through every\n\t\t// subscriber (cursor views, derived chains) so they self-unsubscribe\n\t\t// in their terminal handler before `disposeAllViews` runs. Swapping\n\t\t// the order would clear view caches before subscribers receive the\n\t\t// terminal — strictly worse. Reading `.cache` outside a reactive fn\n\t\t// across teardown is an anti-pattern (spec §5.12) and not a use case\n\t\t// this ordering needs to preserve.\n\t\tthis.addDisposer(() => {\n\t\t\tthis.events.down([[COMPLETE]]);\n\t\t});\n\t\t// P9: release any memoized tail/slice view keepalives held by the log.\n\t\t// TopicGraph itself doesn't call log.tail/slice, but plugins may have\n\t\t// attached views via `_log` — defensive (typical reactive subscribers\n\t\t// have already unsubscribed in their COMPLETE handler above).\n\t\tthis.addDisposer(() => this._log.disposeAllViews());\n\n\t\t// Tier 8 / COMPOSITION-GUIDE §35: route publish through `mutate`\n\t\t// for centralized freeze + re-throw semantics. No audit log surface\n\t\t// (per Tier 8 γ-0): the topic's `events` log already records every\n\t\t// successful publish, so a separate audit Node would be redundant.\n\t\t// `freeze: false` because topic payloads can be large and per-publish\n\t\t// cost matters on hot paths.\n\t\tthis._publishImpl = mutate<[T], void, never>(\n\t\t\t(value): void => {\n\t\t\t\tthis._log.append(value);\n\t\t\t},\n\t\t\t{ frame: \"inline\", freeze: false },\n\t\t);\n\t}\n\n\tpublish(value: T): void {\n\t\t// SENTINEL alignment (Wave B.1 Unit 11 lock): `undefined` is the\n\t\t// protocol-level \"never sent DATA\" sentinel — refusing it here\n\t\t// preserves `lastValue: Node<T | undefined>` semantics.\n\t\tif (value === undefined) {\n\t\t\tthrow new TypeError(\n\t\t\t\t`TopicGraph \"${this.name}\": publish(undefined) is not allowed (spec §5.12 SENTINEL).`,\n\t\t\t);\n\t\t}\n\t\tthis._publishImpl(value);\n\t}\n\n\t/**\n\t * Wire one or more append-log storage tiers (Audit 4). Each tier receives\n\t * appended events per wave; rollback honors the wave-as-transaction model.\n\t *\n\t * Named `attachEventStorage` (not `attachStorage`) to avoid colliding with\n\t * the inherited {@link Graph.attachSnapshotStorage} which takes the\n\t * paired `AttachSnapshotTierPair[]` shape (Phase 14.6) — distinct\n\t * concerns, distinct surfaces.\n\t *\n\t * @returns Disposer.\n\t */\n\tattachEventStorage(\n\t\ttiers: readonly import(\"@graphrefly/pure-ts/extra\").AppendLogStorageTier<T>[],\n\t): () => void {\n\t\treturn this._log.attachStorage(tiers);\n\t}\n\n\tretained(): readonly T[] {\n\t\treturn this.events.cache as readonly T[];\n\t}\n\n\t/** Internal log bundle — used by TopicBridgeGraph for `attach`. */\n\tget _logBundle() {\n\t\treturn this._log;\n\t}\n}\n\nexport type SubscriptionOptions = {\n\tgraph?: GraphOptions;\n\t/**\n\t * Starting cursor position.\n\t * @deprecated Use `from` instead.\n\t */\n\tcursor?: number;\n\t/**\n\t * Starting position for the subscription.\n\t * - `\"retained\"` (default) — cursor starts at 0; consumer sees all retained history.\n\t * - `\"now\"` — cursor starts at current topic length; consumer ignores history.\n\t * - `number` — explicit cursor position.\n\t */\n\tfrom?: \"now\" | \"retained\" | number;\n\t/**\n\t * When this signal node emits DATA, the subscription auto-advances cursor\n\t * to current `available.length`. Useful for \"ack everything when X happens\"\n\t * patterns. The reactive edge `advanceOn → cursor` is visible in `explain()`.\n\t */\n\tadvanceOn?: Node<unknown>;\n};\n\n/** Result of {@link SubscriptionGraph.pullAndAck}. */\nexport type PullAndAckResult<T> = {\n\titems: readonly T[];\n\tcursor: number;\n};\n\nexport class SubscriptionGraph<T> extends Graph {\n\treadonly cursor: Node<number>;\n\treadonly available: Node<readonly T[]>;\n\t/**\n\t * Reference to the upstream topic graph. Intentionally NOT mounted\n\t * under this subscription: a subscription is a VIEW over an\n\t * externally-owned topic. Double-mounting (e.g. hub-owned topic +\n\t * sub-mount here) would make either-side teardown leave the other\n\t * holding a dead reference. Node-level `derived([topicEvents], …)`\n\t * still wires the data dependency across graph boundaries. D1(e).\n\t */\n\treadonly topic: TopicGraph<T>;\n\n\tprivate _disposed = false;\n\tprivate readonly _ackImpl: (count: number | undefined) => number;\n\tprivate readonly _pullAndAckImpl: (limit: number | undefined) => PullAndAckResult<T>;\n\n\tconstructor(name: string, topicGraph: TopicGraph<T>, opts: SubscriptionOptions = {}) {\n\t\tsuper(name, opts.graph);\n\t\tthis.topic = topicGraph;\n\n\t\t// Resolve initial cursor from `from` option, falling back to legacy `cursor` option.\n\t\tlet initialCursor: number;\n\t\tif (opts.from !== undefined) {\n\t\t\tif (opts.from === \"retained\") {\n\t\t\t\tinitialCursor = 0;\n\t\t\t} else if (opts.from === \"now\") {\n\t\t\t\t// §28 sanctioned factory-time boundary read.\n\t\t\t\tinitialCursor = (topicGraph.events.cache as readonly T[]).length;\n\t\t\t} else {\n\t\t\t\tinitialCursor = requireNonNegativeInt(opts.from, \"subscription from\");\n\t\t\t}\n\t\t} else {\n\t\t\tinitialCursor = requireNonNegativeInt(opts.cursor ?? 0, \"subscription cursor\");\n\t\t}\n\n\t\tthis.cursor = this.state<number>(\"cursor\", initialCursor, {\n\t\t\tmeta: messagingMeta(\"subscription_cursor\"),\n\t\t});\n\n\t\t// B.1 Unit 12 lock: `available` depends directly on topic.events + cursor\n\t\t// via `view({ kind: \"fromCursor\" })`. No `source` passthrough node —\n\t\t// describe shows `topic::events → available` (cross-graph edge) and\n\t\t// `cursor → available` (local edge). One fewer node per subscription.\n\t\tthis.available = topicGraph._logBundle.view({ kind: \"fromCursor\", cursor: this.cursor });\n\t\tthis.add(this.available, { name: \"available\" });\n\t\tthis.addDisposer(keepalive(this.available));\n\n\t\t// Optional reactive auto-advance: when `advanceOn` emits a NEW DATA\n\t\t// (after construction), cursor advances by `available.length` atomically.\n\t\t// Edge visible in describe: advancePump depends on advanceOn.\n\t\t// `_advanceInitialized` guards against the initial push-on-subscribe fire\n\t\t// that would advance cursor before the user has a chance to read.\n\t\tif (opts.advanceOn !== undefined) {\n\t\t\tconst advanceOn = opts.advanceOn;\n\t\t\tlet advanceInitialized = false;\n\t\t\tconst advancePump = node<unknown>(\n\t\t\t\t[advanceOn],\n\t\t\t\t() => {\n\t\t\t\t\t// Skip the initial push-on-subscribe wave.\n\t\t\t\t\tif (!advanceInitialized) {\n\t\t\t\t\t\tadvanceInitialized = true;\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t\tif (this._disposed) return;\n\t\t\t\t\tconst avail = this.available.cache as readonly T[];\n\t\t\t\t\tif (avail.length === 0) return;\n\t\t\t\t\tconst next = (this.cursor.cache as number) + avail.length;\n\t\t\t\t\tthis.cursor.emit(next);\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tname: \"advancePump\",\n\t\t\t\t\tdescribeKind: \"effect\",\n\t\t\t\t\tmeta: messagingMeta(\"subscription_advance_pump\"),\n\t\t\t\t},\n\t\t\t);\n\t\t\tthis.add(advancePump, { name: \"advancePump\" });\n\t\t\tthis.addDisposer(keepalive(advancePump));\n\t\t}\n\n\t\t// Tier 8 / COMPOSITION-GUIDE §35: route ack + pullAndAck through\n\t\t// `mutate` for centralized freeze + re-throw semantics. No audit\n\t\t// log surface (per Tier 8 γ-0): the cursor's own emission stream already\n\t\t// records every advance, so a separate audit Node would be redundant.\n\t\t// `freeze: false` because count/limit are simple numbers; freezing is\n\t\t// pointless overhead. Disposed-checks stay outside the wrapper so a\n\t\t// no-op call doesn't unnecessarily run the wrapper.\n\t\tthis._ackImpl = mutate<[number | undefined], number, never>(\n\t\t\t(count): number => {\n\t\t\t\tconst available = this.available.cache as readonly T[];\n\t\t\t\tconst requested =\n\t\t\t\t\tcount === undefined\n\t\t\t\t\t\t? available.length\n\t\t\t\t\t\t: requireNonNegativeInt(count, \"subscription ack count\");\n\t\t\t\tconst step = Math.min(requested, available.length);\n\t\t\t\tif (step <= 0) return this.cursor.cache as number;\n\t\t\t\tconst next = (this.cursor.cache as number) + step;\n\t\t\t\t// F8: use emit() so the pipeline auto-prefixes DIRTY, runs equals\n\t\t\t\t// substitution, and produces a proper two-phase wave (the raw\n\t\t\t\t// `down([[DATA, next]])` path bypassed those contracts).\n\t\t\t\tthis.cursor.emit(next);\n\t\t\t\treturn next;\n\t\t\t},\n\t\t\t{ frame: \"inline\", freeze: false },\n\t\t);\n\n\t\tthis._pullAndAckImpl = mutate<[number | undefined], PullAndAckResult<T>, never>(\n\t\t\t(limit): PullAndAckResult<T> => {\n\t\t\t\tconst available = this.available.cache as readonly T[];\n\t\t\t\tconst max =\n\t\t\t\t\tlimit === undefined\n\t\t\t\t\t\t? available.length\n\t\t\t\t\t\t: requireNonNegativeInt(limit, \"subscription pullAndAck limit\");\n\t\t\t\tconst items = available.slice(0, max);\n\t\t\t\tif (items.length === 0) return { items, cursor: this.cursor.cache as number };\n\t\t\t\tconst next = (this.cursor.cache as number) + items.length;\n\t\t\t\tthis.cursor.emit(next);\n\t\t\t\treturn { items, cursor: next };\n\t\t\t},\n\t\t\t{ frame: \"inline\", freeze: false },\n\t\t);\n\t}\n\n\tack(count?: number): number {\n\t\tif (this._disposed) return this.cursor.cache as number;\n\t\treturn this._ackImpl(count);\n\t}\n\n\tpull(limit?: number): readonly T[] {\n\t\tif (this._disposed) return [];\n\t\tconst available = this.available.cache as readonly T[];\n\t\tconst max =\n\t\t\tlimit === undefined\n\t\t\t\t? available.length\n\t\t\t\t: requireNonNegativeInt(limit, \"subscription pull limit\");\n\t\treturn available.slice(0, max);\n\t}\n\n\t/**\n\t * Atomic pull-and-acknowledge. Returns `{ items, cursor }` where `cursor`\n\t * is the new cursor position after advancing. Under single-threaded JS the\n\t * snapshot and advance are atomic; PY callers use a per-subscription Lock.\n\t *\n\t * Replaces `pull(limit, { ack: true })`.\n\t */\n\tpullAndAck(limit?: number): PullAndAckResult<T> {\n\t\tif (this._disposed) return { items: [], cursor: this.cursor.cache as number };\n\t\treturn this._pullAndAckImpl(limit);\n\t}\n\n\t/**\n\t * Release internal subscriptions and mark the subscription torn-down.\n\t * Subsequent `pull`, `pullAndAck`, `ack` return empty / current cursor.\n\t * Emits COMPLETE on `cursor` so derived consumers (e.g. `available`) see\n\t * the termination signal. Also drains `addDisposer` callbacks (including\n\t * the `keepalive(advancePump)` subscription) so no keepalive leak occurs.\n\t */\n\tdispose(): void {\n\t\tif (this._disposed) return;\n\t\tthis._disposed = true;\n\t\tthis.cursor.down([[COMPLETE]]);\n\t\t// m4: drain addDisposer callbacks to release the keepalive subscription.\n\t\tthis.destroy();\n\t}\n}\n\nexport type TopicBridgeOptions<TIn, TOut> = {\n\tgraph?: GraphOptions;\n\tcursor?: number;\n\tmaxPerPump?: number;\n\t/**\n\t * Optional transform/filter applied to each item before republishing.\n\t *\n\t * **At-most-once with silent drop:** when `map` returns `undefined`, the\n\t * input is consumed from the source cursor but NOT republished. Filtered\n\t * items are not retained for retry. If you need filter-with-retry\n\t * semantics, do the filtering in a downstream subscription on the bridged\n\t * output rather than in the `map` function.\n\t */\n\tmap?: (value: TIn) => TOut | undefined;\n};\n\nexport class TopicBridgeGraph<TIn, TOut = TIn> extends Graph {\n\tprivate readonly _sourceSub;\n\treadonly bridgedCount: Node<number>;\n\t/**\n\t * Emits each mapped batch as DATA — gives downstream observers a reactive\n\t * stream of bridged values. Also the link target for `target._log.attach`.\n\t */\n\treadonly output: Node<readonly TOut[]>;\n\n\tconstructor(\n\t\tname: string,\n\t\tsourceTopic: TopicGraph<TIn>,\n\t\ttargetTopic: TopicGraph<TOut>,\n\t\topts: TopicBridgeOptions<TIn, TOut> = {},\n\t) {\n\t\tsuper(name, opts.graph);\n\t\tthis._sourceSub = subscription<TIn>(`${name}-subscription`, sourceTopic, {\n\t\t\tcursor: opts.cursor,\n\t\t});\n\t\tthis.mount(\"subscription\", this._sourceSub);\n\n\t\tconst maxPerPump = Math.max(\n\t\t\t1,\n\t\t\trequireNonNegativeInt(opts.maxPerPump ?? DEFAULT_MAX_PER_PUMP, \"topic bridge maxPerPump\"),\n\t\t);\n\t\tconst mapValue = opts.map ?? ((value: TIn) => value as unknown as TOut);\n\n\t\t// Reactive output node: derives a mapped batch from `available`.\n\t\t// §24 compliant — output is a real derived edge, visible in describe.\n\t\t// Replaces imperative publish loop. Items where mapValue returns undefined\n\t\t// are filtered out (opt-out / filter).\n\t\tthis.output = node<readonly TOut[]>(\n\t\t\t[this._sourceSub.available],\n\t\t\t(batchData, actions, ctx) => {\n\t\t\t\tconst data = batchData.map((batch, i) =>\n\t\t\t\t\tbatch != null && batch.length > 0 ? batch.at(-1) : ctx.prevData[i],\n\t\t\t\t);\n\t\t\t\tconst arr = data[0] as readonly TIn[];\n\t\t\t\tconst outBatch: TOut[] = [];\n\t\t\t\tconst take = Math.min(arr.length, maxPerPump);\n\t\t\t\tfor (let i = 0; i < take; i++) {\n\t\t\t\t\tconst mapped = mapValue(arr[i] as TIn);\n\t\t\t\t\tif (mapped !== undefined) outBatch.push(mapped);\n\t\t\t\t}\n\t\t\t\tactions.emit(outBatch);\n\t\t\t},\n\t\t\t{\n\t\t\t\tname: \"output\",\n\t\t\t\tdescribeKind: \"derived\",\n\t\t\t\tmeta: messagingMeta(\"topic_bridge_output\", { targetRef: targetTopic.name }),\n\t\t\t\tinitial: [],\n\t\t\t},\n\t\t);\n\t\tthis.add(this.output, { name: \"output\" });\n\t\tthis.addDisposer(keepalive(this.output));\n\n\t\t// bridgedCount: state node accumulating total bridged items.\n\t\t// Updated by ackPump after each batch — edge visible via ackPump dep on output.\n\t\tthis.bridgedCount = this.state<number>(\"bridgedCount\", 0, {\n\t\t\tmeta: messagingMeta(\"topic_bridge_count\"),\n\t\t});\n\t\tthis.addDisposer(keepalive(this.bridgedCount));\n\n\t\t// ackPump: effect that advances the subscription cursor and updates\n\t\t// bridgedCount after each batch. Runs after `output` settles.\n\t\t// Captures refs to `this.output`, `this._sourceSub`, `this.bridgedCount`\n\t\t// to avoid `this` inside the fn body.\n\t\tconst outputRef = this.output;\n\t\tconst subRef = this._sourceSub;\n\t\tconst countRef = this.bridgedCount;\n\t\tconst ackPump = this.effect(\n\t\t\t\"ackPump\",\n\t\t\t[\"output\"],\n\t\t\t() => {\n\t\t\t\tconst outBatch = outputRef.cache as readonly TOut[];\n\t\t\t\tif (outBatch.length === 0) return;\n\t\t\t\tconst availLen = (subRef.available.cache as readonly TIn[]).length;\n\t\t\t\tconst toAck = Math.min(availLen, maxPerPump);\n\t\t\t\tif (toAck > 0) {\n\t\t\t\t\tsubRef.ack(toAck);\n\t\t\t\t\tconst prev = (countRef.cache as number) ?? 0;\n\t\t\t\t\tcountRef.emit(prev + outBatch.length);\n\t\t\t\t}\n\t\t\t},\n\t\t\t{\n\t\t\t\tmeta: messagingMeta(\"topic_bridge_ack_pump\"),\n\t\t\t},\n\t\t);\n\t\tthis.addDisposer(keepalive(ackPump));\n\n\t\t// Wire output into target topic's log reactively.\n\t\t// _attachArrayToLog subscribes to output and publishes each item to targetTopic.\n\t\t// Teardown: disposer runs before mount teardown.\n\t\tconst detach = _attachArrayToLog(this.output, targetTopic);\n\t\tthis.addDisposer(detach);\n\t}\n}\n\n/**\n * Attaches each element of an array-valued Node to a TopicGraph's log.\n * Every DATA emission on `source` appends all items in the array to `targetTopic`.\n * Returns a disposer.\n */\nfunction _attachArrayToLog<T>(source: Node<readonly T[]>, targetTopic: TopicGraph<T>): () => void {\n\treturn source.subscribe((msgs) => {\n\t\tfor (const m of msgs) {\n\t\t\tif (m[0] !== DATA) continue;\n\t\t\tconst arr = m[1] as readonly T[];\n\t\t\tif (arr.length === 0) continue;\n\t\t\tbatch(() => {\n\t\t\t\tfor (const v of arr) targetTopic.publish(v);\n\t\t\t});\n\t\t}\n\t});\n}\n\n// ── TopicRegistry ─────────────────────────────────────────────────────────\n\n/**\n * Private pure data structure managing a named set of {@link TopicGraph}\n * instances. Extracted from {@link MessagingHubGraph} for separation of\n * concerns (B.2 Unit 14 lock: D — split into TopicRegistry + facade).\n *\n * Reusable if other domain consumers (e.g. cqrs.eventLogs) want a shared\n * topic registry later.\n *\n * @internal\n */\nexport class TopicRegistry {\n\tprivate readonly _map = new Map<string, TopicGraph<unknown>>();\n\t/** Reactive monotonic version counter. Advances on topic create/remove. */\n\treadonly version: Node<number>;\n\n\tconstructor(versionNode: Node<number>) {\n\t\tthis.version = versionNode;\n\t}\n\n\tget size(): number {\n\t\treturn this._map.size;\n\t}\n\n\thas(name: string): boolean {\n\t\treturn this._map.has(name);\n\t}\n\n\tget<T>(name: string): TopicGraph<T> | undefined {\n\t\treturn this._map.get(name) as TopicGraph<T> | undefined;\n\t}\n\n\tset<T>(name: string, t: TopicGraph<T>): void {\n\t\tthis._map.set(name, t as TopicGraph<unknown>);\n\t}\n\n\tdelete(name: string): boolean {\n\t\treturn this._map.delete(name);\n\t}\n\n\tkeys(): IterableIterator<string> {\n\t\treturn this._map.keys();\n\t}\n}\n\n// ── MessagingHubGraph ─────────────────────────────────────────────────────\n\nexport type MessagingHubOptions = {\n\tgraph?: GraphOptions;\n\t/**\n\t * Default `TopicOptions` applied to every topic created via `topic(name)`\n\t * without explicit options. Per-call opts override. Default: `{}`\n\t * (unbounded retention per topic unless `retainedLimit` is set per call).\n\t */\n\tdefaultTopicOptions?: TopicOptions;\n};\n\n/**\n * Lazy Pulsar-inspired topic registry. Manages a named set of {@link TopicGraph}\n * instances with retention + cursor semantics. Topics are created on first\n * access; `removeTopic(name)` unmounts and tears down via {@link Graph.remove}.\n *\n * Internally delegates to {@link TopicRegistry} for topic map management\n * (B.2 Unit 14 lock: D facade split).\n *\n * **Relationship to `pubsub()` in `src/extra/pubsub.ts`:** `pubsub` is a\n * lightweight last-value state hub (no retention, no cursors). `MessagingHubGraph`\n * is the full messaging hub — retained message logs, cursor-based subscriptions,\n * and pattern-layer lifecycle management.\n *\n * @category patterns\n */\nexport class MessagingHubGraph extends Graph {\n\tprivate readonly _registry: TopicRegistry;\n\t/** Reactive monotonic version counter — advances on topic create/remove. */\n\treadonly version: Node<number>;\n\tprivate readonly _defaultTopicOptions: TopicOptions;\n\tprivate readonly _removeTopicImpl: (name: string) => void;\n\n\tconstructor(name: string, opts: MessagingHubOptions = {}) {\n\t\tsuper(name, opts.graph);\n\t\t// B.2 Unit 14 lock: promote _version → version: Node<number>.\n\t\tconst versionNode = this.state<number>(\"version\", 0, {\n\t\t\tmeta: messagingMeta(\"hub_version\"),\n\t\t});\n\t\tthis.version = versionNode;\n\t\tthis._registry = new TopicRegistry(versionNode);\n\t\t// P8: shallow-copy caller-provided defaults so post-construction\n\t\t// mutations by the caller don't leak into every future `topic()` call.\n\t\tthis._defaultTopicOptions = { ...(opts.defaultTopicOptions ?? {}) };\n\n\t\t// Tier 8 / COMPOSITION-GUIDE §35: route the registry-delete branch of\n\t\t// `removeTopic` through `mutate` for centralized re-throw\n\t\t// semantics. No audit log surface (per Tier 8 γ-0).\n\t\t// `freeze: false` because the only arg is a string name (freeze pointless).\n\t\t// **Closure-state caveat (γ-4):** the inner `try/finally` mutates\n\t\t// `_registry` (a `Map`) and emits the version bump. mutate has no\n\t\t// `batch()` frame, so reactive emissions are NOT rolled back on throw —\n\t\t// and even if it did, `Map.delete` on closure state is invisible to the\n\t\t// batch and can't be unwound. The pre-existing try/finally on\n\t\t// `Graph.remove` is what guarantees registry/version converge to a\n\t\t// consistent state when `remove()` throws; `mutate` adds nothing\n\t\t// to that contract beyond the re-throw.\n\t\tthis._removeTopicImpl = mutate<[string], void, never>(\n\t\t\t(topicName): void => {\n\t\t\t\ttry {\n\t\t\t\t\tthis.remove(topicName); // unmounts, drops edges, tears down\n\t\t\t\t} finally {\n\t\t\t\t\tthis._registry.delete(topicName);\n\t\t\t\t\tconst cur = (this.version.cache as number) ?? 0;\n\t\t\t\t\tthis.version.emit(cur + 1);\n\t\t\t\t}\n\t\t\t},\n\t\t\t{ frame: \"inline\", freeze: false },\n\t\t);\n\t}\n\n\t/** Number of topics currently in the hub. */\n\tget size(): number {\n\t\treturn this._registry.size;\n\t}\n\n\t/** Checks topic existence without creating. */\n\thas(name: string): boolean {\n\t\treturn this._registry.has(name);\n\t}\n\n\t/** Iterator over topic names. */\n\ttopicNames(): IterableIterator<string> {\n\t\treturn this._registry.keys();\n\t}\n\n\t/**\n\t * Returns the {@link TopicGraph} for `name`, creating lazily on first call.\n\t * Subsequent calls with the same name return the same instance (options on\n\t * repeat calls are ignored — the topic is already configured).\n\t */\n\ttopic<T = unknown>(name: string, opts?: TopicOptions): TopicGraph<T> {\n\t\tlet t = this._registry.get<T>(name);\n\t\tif (t === undefined) {\n\t\t\tconst effective: TopicOptions = { ...this._defaultTopicOptions, ...(opts ?? {}) };\n\t\t\tt = new TopicGraph<T>(name, effective);\n\t\t\tthis._registry.set(name, t);\n\t\t\tthis.mount(name, t);\n\t\t\tconst cur = (this.version.cache as number) ?? 0;\n\t\t\tthis.version.emit(cur + 1);\n\t\t}\n\t\treturn t;\n\t}\n\n\t/**\n\t * Publishes a value to the topic, lazily creating it on first publish.\n\t *\n\t * **Late-subscriber caveat:** the topic is created lazily, so subscribers\n\t * that attach AFTER a publish only see the retained window (governed by\n\t * `retainedLimit` on `TopicOptions` / `defaultTopicOptions`). If\n\t * `retainedLimit === 0` is set explicitly, early publishes are\n\t * effectively dropped — prefer an unset `retainedLimit` (unbounded\n\t * retention) or subscribe before publishing when late-subscribers matter.\n\t */\n\tpublish<T = unknown>(name: string, value: T): void {\n\t\tthis.topic<T>(name).publish(value);\n\t}\n\n\t/**\n\t * Bulk publish — issues all publishes inside one outer batch. New topics\n\t * are created on demand. No-op if `entries` yields nothing.\n\t *\n\t * **Iterable consumption (F6):** `entries` is consumed once (single-pass)\n\t * INSIDE the batch frame. If the iterator throws mid-way, the batch is\n\t * discarded and NO publishes are visible to subscribers (all-or-nothing).\n\t * Pass an array or `Set` for multi-shot callers.\n\t */\n\tpublishMany(entries: Iterable<[string, unknown]>): void {\n\t\t// P2: iterate inside batch — no `[...entries]` materialization so large\n\t\t// / infinite iterables don't OOM, and iterator throws are contained.\n\t\tbatch(() => {\n\t\t\tfor (const [name, value] of entries) {\n\t\t\t\tthis.topic(name).publish(value);\n\t\t\t}\n\t\t});\n\t}\n\n\t/**\n\t * Creates a {@link SubscriptionGraph} over a named topic. The topic is\n\t * lazily created if missing. Subscription lifecycle is owned by the caller —\n\t * the hub does NOT mount the subscription.\n\t *\n\t * @param subName - Local name for the subscription graph.\n\t * @param topicName - Hub topic to subscribe to.\n\t * @param opts - `SubscriptionOptions` (initial cursor, etc.).\n\t */\n\tsubscribe<T = unknown>(\n\t\tsubName: string,\n\t\ttopicName: string,\n\t\topts?: SubscriptionOptions,\n\t): SubscriptionGraph<T> {\n\t\tconst t = this.topic<T>(topicName);\n\t\treturn new SubscriptionGraph<T>(subName, t, opts);\n\t}\n\n\t/**\n\t * Unmounts and tears down the topic's graph. Returns `true` if the topic\n\t * existed. Subscribers receive `TEARDOWN` via {@link Graph.remove}.\n\t *\n\t * **Closure-state caveat:** the registry mutation (`_registry.delete`) and\n\t * version bump happen in a `try/finally`, so registry/version converge to\n\t * a consistent state even when {@link Graph.remove} throws. `mutate`\n\t * does not roll back this mutation on throw — `Map.delete` on closure\n\t * state is invisible to any batch frame. The pre-existing try/finally is\n\t * load-bearing for that invariant.\n\t */\n\tremoveTopic(name: string): boolean {\n\t\tif (!this._registry.has(name)) return false;\n\t\t// P1 / P3: Graph.remove first — if it throws, `_registry` must NOT still\n\t\t// hold the broken half-disposed topic (otherwise the next\n\t\t// `hub.topic(name)` returns the corrupted reference). The `try/finally`\n\t\t// inside `_removeTopicImpl`'s action body preserves that invariant.\n\t\tthis._removeTopicImpl(name);\n\t\treturn true;\n\t}\n}\n\n/**\n * Creates a Pulsar-inspired topic graph (append-only retained stream + latest value).\n */\nexport function topic<T>(name: string, opts?: TopicOptions): TopicGraph<T> {\n\treturn new TopicGraph<T>(name, opts);\n}\n\n/**\n * Creates a lazy Pulsar-inspired messaging hub. Topics are created on first access\n * via `hub.topic(name)`; `hub.publish(name, value)` shortcuts through the registry.\n *\n * @example\n * ```ts\n * import { messagingHub } from \"@graphrefly/graphrefly/patterns/messaging\";\n *\n * const hub = messagingHub(\"main\", { defaultTopicOptions: { retainedLimit: 256 } });\n * hub.publish(\"orders\", { id: 1 });\n * hub.publishMany([[\"shipments\", { id: 1 }], [\"orders\", { id: 2 }]]);\n * const sub = hub.subscribe(\"orders-worker\", \"orders\", { cursor: 0 });\n * ```\n */\nexport function messagingHub(name: string, opts?: MessagingHubOptions): MessagingHubGraph {\n\treturn new MessagingHubGraph(name, opts);\n}\n\n/**\n * Creates a cursor-based subscription graph over a topic.\n */\nexport function subscription<T>(\n\tname: string,\n\ttopicGraph: TopicGraph<T>,\n\topts?: SubscriptionOptions,\n): SubscriptionGraph<T> {\n\treturn new SubscriptionGraph<T>(name, topicGraph, opts);\n}\n\n/**\n * Creates an autonomous cursor-based topic relay graph.\n *\n * When `opts.map` is provided, items where `map` returns `undefined` are\n * consumed from the source cursor but NOT republished (at-most-once with\n * silent drop). For filter-with-retry semantics, apply the filter in a\n * downstream subscription on the bridge's `output` node instead.\n */\nexport function topicBridge<TIn, TOut = TIn>(\n\tname: string,\n\tsourceTopic: TopicGraph<TIn>,\n\ttargetTopic: TopicGraph<TOut>,\n\topts?: TopicBridgeOptions<TIn, TOut>,\n): TopicBridgeGraph<TIn, TOut> {\n\treturn new TopicBridgeGraph<TIn, TOut>(name, sourceTopic, targetTopic, opts);\n}\n\n// ── LogProjector ──────────────────────────────────────────────────────────\n//\n// Promotion 2 (memo:Re Story 6.4 back-derivation, design-review-locked\n// 2026-05-16). A cursor-driven projector over a log/topic where a per-item\n// sink can poison-fail. memo:Re hand-rolled `createProjectorCursor` and got\n// the failure mode wrong: a bare `catch { break; }` conflated a *transient*\n// condition (a native feature not yet available → retry later) with a\n// *poison* entry (will never project) → a permanent head-of-line block of\n// every newer entry. The fix is a real, observable, subscribable dead-letter\n// topic + a typed failure policy.\n//\n// Built ON `subscription()` (TopicGraph source) / the bundle's `fromCursor`\n// view (ReactiveLogBundle source) — the same hardened cursor machinery\n// `SubscriptionGraph` itself uses — so the consumer never hand-rolls cursor\n// persistence/durability (the Med \"durability skew\" + parse-leniency findings\n// dissolve). Scope is deliberately bounded: `halt | deadLetter` only, NO\n// programmatic retry/backoff (compose a downstream subscription on\n// `deadLetter` for that — avoids the §44 wrap-imperative-as-primitive trap).\n\nexport type ProjectorPoisonPolicy = \"halt\" | \"deadLetter\";\n\n/** A poison entry routed to {@link LogProjectorGraph.deadLetter}. */\nexport type DeadLetterEntry<T> = {\n\treadonly item: T;\n\t/** `Error.message` (or `String(thrown)`) from the failing `sink`. */\n\treadonly error: string;\n\t/** Absolute 0-based log position of the poisoned item. */\n\treadonly cursorPos: number;\n};\n\nexport type LogProjectorOptions<T> = {\n\tgraph?: GraphOptions;\n\t/**\n\t * Per-item side-effecting projection.\n\t *\n\t * **Transient-vs-poison contract (read this).** The projector cannot tell\n\t * \"this will project later\" from \"this will never project\" — only the sink\n\t * knows. So the contract is:\n\t * - **Return normally** (sync return, or a resolved Promise) → the item is\n\t * *handled*; the cursor advances. Use this for the success path AND for\n\t * any transient/skip condition the sink wants to no-op (e.g. an optional\n\t * native feature not yet available — return without throwing; the entry\n\t * is simply considered done for this projector).\n\t * - **Throw / reject** → the item is *poison*; the {@link onPoison} policy\n\t * applies. Do NOT throw for transient conditions or the item is\n\t * dead-lettered (or halts the stream).\n\t */\n\treadonly sink: (item: T) => void | Promise<void>;\n\t/**\n\t * Behaviour when `sink` throws/rejects on an item (poison):\n\t * - `\"halt\"` (default) — stop projecting at the poison item; the cursor\n\t * does NOT advance past it (head-of-line stop). `position` stalls — that\n\t * is the observable signal. No retry/backoff is built in.\n\t * - `\"deadLetter\"` — publish `{ item, error, cursorPos }` to the\n\t * {@link LogProjectorGraph.deadLetter} topic and advance past it, so\n\t * newer entries still project.\n\t */\n\treadonly onPoison?: ProjectorPoisonPolicy;\n\t/**\n\t * Initial cursor position. `\"retained\"` (default) projects all history;\n\t * `\"now\"` skips existing entries (project only future appends); a number\n\t * starts at that absolute index.\n\t */\n\treadonly from?: \"retained\" | \"now\" | number;\n\t/** Retained window for the `deadLetter` topic. Default 1024. */\n\treadonly deadLetterRetainedLimit?: number;\n};\n\n/**\n * Cursor-driven projector over a {@link TopicGraph} or {@link ReactiveLogBundle}.\n *\n * Topology (mounted on the returned graph):\n * - `subscription` (TopicGraph source only) — the hardened\n * {@link SubscriptionGraph} cursor; or a local `cursor` state + the\n * bundle's `fromCursor` view (ReactiveLogBundle source).\n * - `drain` — an `effect` that, on every not-yet-projected wave, schedules a\n * serialized async pass that calls `sink` per item (mirrors the\n * `SubscriptionGraph.ackPump` / `TopicBridge.ackPump` effect precedent +\n * memo:Re's `inFlight` chain — one wave processed at a time).\n * - `deadLetter` — a real {@link TopicGraph} (NOT a callback): poison entries\n * are observable in `describe()` and subscribable, instead of memo:Re's\n * silent `break`.\n *\n * **No imperative reads.** Observe `position` (cursor) / subscribe to\n * `deadLetter`. `idle()` is a test-only await convenience.\n *\n * @category patterns\n */\nexport class LogProjectorGraph<T> extends Graph {\n\t/** Reactive count of fully-projected entries (the cursor; read-only). */\n\treadonly position: Node<number>;\n\t/**\n\t * Poison entries (populated when `onPoison: \"deadLetter\"`). A real topic —\n\t * subscribable + visible in `describe()`.\n\t */\n\treadonly deadLetter: TopicGraph<DeadLetterEntry<T>>;\n\tprivate _inFlight: Promise<void> = Promise.resolve();\n\n\tconstructor(\n\t\tname: string,\n\t\tsource: TopicGraph<T> | ReactiveLogBundle<T>,\n\t\topts: LogProjectorOptions<T>,\n\t) {\n\t\tsuper(name, opts.graph);\n\t\tconst onPoison: ProjectorPoisonPolicy = opts.onPoison ?? \"halt\";\n\t\tconst sink = opts.sink;\n\n\t\tconst dl = new TopicGraph<DeadLetterEntry<T>>(`${name}_dead_letter`, {\n\t\t\tretainedLimit: opts.deadLetterRetainedLimit ?? DEFAULT_TOPIC_RETAINED_LIMIT,\n\t\t});\n\t\tthis.mount(\"deadLetter\", dl);\n\t\tthis.deadLetter = dl;\n\n\t\t// Uniform cursor surface over either source kind. A TopicGraph reuses\n\t\t// the hardened SubscriptionGraph cursor; a ReactiveLogBundle uses a\n\t\t// local state cursor + the bundle's `fromCursor` view — the very\n\t\t// machinery SubscriptionGraph is itself built on.\n\t\tlet available: Node<readonly T[]>;\n\t\tlet cursorBase: () => number;\n\t\tlet advance: (n: number) => void;\n\n\t\tif (source instanceof TopicGraph) {\n\t\t\tconst sub = new SubscriptionGraph<T>(`${name}_subscription`, source, {\n\t\t\t\tfrom: opts.from ?? \"retained\",\n\t\t\t});\n\t\t\tthis.mount(\"subscription\", sub);\n\t\t\tavailable = sub.available;\n\t\t\tthis.position = sub.cursor;\n\t\t\tcursorBase = () => sub.cursor.cache as number;\n\t\t\tadvance = (n) => {\n\t\t\t\tif (n > 0) sub.ack(n);\n\t\t\t};\n\t\t} else {\n\t\t\tconst log = source;\n\t\t\tlet initialCursor: number;\n\t\t\tif (opts.from === \"now\") {\n\t\t\t\tinitialCursor = log.size;\n\t\t\t} else if (typeof opts.from === \"number\") {\n\t\t\t\tinitialCursor = requireNonNegativeInt(opts.from, \"logProjector from\");\n\t\t\t} else {\n\t\t\t\tinitialCursor = 0; // \"retained\"\n\t\t\t}\n\t\t\tconst cursor = this.state<number>(\"cursor\", initialCursor, {\n\t\t\t\tmeta: messagingMeta(\"log_projector_cursor\"),\n\t\t\t});\n\t\t\tthis.position = cursor;\n\t\t\tcursorBase = () => cursor.cache as number;\n\t\t\tavailable = log.view({ kind: \"fromCursor\", cursor });\n\t\t\tadvance = (n) => {\n\t\t\t\tif (n > 0) cursor.emit((cursor.cache as number) + n);\n\t\t\t};\n\t\t}\n\n\t\t// `halt` is a HARD LATCH (QA-C): on the first poison under `onPoison:\n\t\t// \"halt\"`, `sink` has been invoked exactly once on the poison item;\n\t\t// the projector then freezes — no further `sink` calls, no rescheduled\n\t\t// drains — so a later unrelated append cannot re-invoke the (possibly\n\t\t// side-effecting, non-idempotent) sink on the poison. The stalled\n\t\t// `position` + frozen stream IS the observable signal. v1 has no retry\n\t\t// (compose downstream of `deadLetter` if you need that).\n\t\tlet halted = false;\n\n\t\t// Serialized async drain. One wave processed at a time (the inFlight\n\t\t// chain) so an async `sink` cannot interleave; the cursor is advanced\n\t\t// ONCE per pass after the captured snapshot is processed (mirrors\n\t\t// memo:Re's `runPump` + the ackPump effect precedent).\n\t\tconst runDrain = async (): Promise<void> => {\n\t\t\tif (halted) return;\n\t\t\tconst snapshot = (available.cache as readonly T[] | undefined) ?? [];\n\t\t\tif (snapshot.length === 0) return;\n\t\t\tlet consumed = 0;\n\t\t\tfor (let i = 0; i < snapshot.length; i += 1) {\n\t\t\t\tconst item = snapshot[i] as T;\n\t\t\t\ttry {\n\t\t\t\t\tawait sink(item);\n\t\t\t\t\tconsumed += 1;\n\t\t\t\t} catch (e) {\n\t\t\t\t\tconst error = e instanceof Error ? e.message : String(e);\n\t\t\t\t\tif (onPoison === \"deadLetter\") {\n\t\t\t\t\t\tdl.publish({ item, error, cursorPos: cursorBase() + consumed });\n\t\t\t\t\t\tconsumed += 1; // advance past the poison\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t\t// \"halt\" — latch and stop here; do NOT advance past the\n\t\t\t\t\t// poison, do NOT re-invoke `sink` on any later wave.\n\t\t\t\t\thalted = true;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (consumed > 0) advance(consumed);\n\t\t};\n\t\tconst schedule = (): void => {\n\t\t\tif (halted) return; // latched — no further drains\n\t\t\tthis._inFlight = this._inFlight.then(runDrain, runDrain);\n\t\t};\n\n\t\t// Effect: every wave that exposes not-yet-projected entries schedules a\n\t\t// drain. Side-effecting (sink / cursor advance / dead-letter publish) →\n\t\t// an `effect` node, not a pure derived (COMPOSITION-GUIDE §35; exact\n\t\t// `SubscriptionGraph.advancePump` precedent — never calls `emit` on its\n\t\t// own node, kept warm via `keepalive`).\n\t\tconst drain = node<unknown>(\n\t\t\t[available],\n\t\t\t(batchData, _actions, ctx) => {\n\t\t\t\tconst b = batchData[0];\n\t\t\t\tconst snap = (b != null && b.length > 0 ? b.at(-1) : ctx.prevData[0]) as\n\t\t\t\t\t| readonly T[]\n\t\t\t\t\t| undefined;\n\t\t\t\tif (snap && snap.length > 0) schedule();\n\t\t\t},\n\t\t\t{\n\t\t\t\tname: \"drain\",\n\t\t\t\tdescribeKind: \"effect\",\n\t\t\t\tmeta: messagingMeta(\"log_projector_drain\"),\n\t\t\t},\n\t\t);\n\t\tthis.add(drain, { name: \"drain\" });\n\t\tthis.addDisposer(keepalive(drain));\n\t}\n\n\t/**\n\t * Await any in-flight drain pass. **Test convenience only** — the canonical\n\t * reactive observable is {@link LogProjectorGraph.position}.\n\t */\n\tidle(): Promise<void> {\n\t\treturn this._inFlight;\n\t}\n}\n\n/**\n * Creates a cursor-driven log/topic projector with a typed poison-failure\n * policy and an observable dead-letter topic.\n *\n * @example\n * ```ts\n * import { logProjector, topic } from \"@graphrefly/graphrefly\";\n *\n * const events = topic<Doc>(\"docs\");\n * const proj = logProjector(\"indexer\", events, {\n * sink: async (doc) => { await index(doc); }, // throw ⇒ poison\n * onPoison: \"deadLetter\",\n * });\n * proj.deadLetter.events.subscribe(/* observe poison *​/);\n * ```\n *\n * @remarks\n * **Use an UNBOUNDED source for durable / long-lived projection.** The cursor\n * is an absolute index; the underlying `fromCursor` view slices the source's\n * *current* entries array. A `TopicGraph` with a `retainedLimit` (or a\n * `ReactiveLogBundle` with `maxSize`) trims its head, so an absolute cursor\n * past the retained window reads the wrong offset (skips entries or stalls).\n * This is inherited `subscription()` / `fromCursor` behaviour, not specific to\n * `logProjector` — but it matters here because projection is typically\n * long-lived. For unbounded projection pass a source with NO `retainedLimit` /\n * `maxSize` (memo:Re's `changesetLog` is unbounded ✓).\n *\n * @category patterns\n */\nexport function logProjector<T>(\n\tname: string,\n\tsource: TopicGraph<T> | ReactiveLogBundle<T>,\n\topts: LogProjectorOptions<T>,\n): LogProjectorGraph<T> {\n\treturn new LogProjectorGraph<T>(name, source, opts);\n}\n","/**\n * Universal mutation framework (Phase 14 — DS-14 locked 2026-05-05).\n *\n * Single `mutate(act, opts)` factory replaces the prior `lightMutation` +\n * `wrapMutation` two-tier split (pre-1.0 break per Q-O2).\n *\n * Two frames:\n * - `\"inline\"` — no batch; up() runs raw. Seq bumps before action; persists\n * on throw. Hot-path-friendly for atomic single-write mutations.\n * - `\"transactional\"` — opens `batch(() => up(...))`. On throw: batch discards\n * deferred deliveries, then `down()` runs (if provided), then failure record.\n *\n * Phase-4 primitives share the same shape: imperative mutation methods +\n * closure state + reactive audit log + freeze-at-entry + rollback-on-throw.\n * This module factors out the common machinery so each primitive becomes\n * declarative wiring over typed audit records.\n */\n\nimport {\n\tbatch,\n\tDATA,\n\tDIRTY,\n\ttype Node,\n\ttype NodeGuard,\n\tnode,\n\tpolicy,\n\twallClockNs,\n} from \"@graphrefly/pure-ts/core\";\nimport {\n\ttype ReactiveLogBundle,\n\ttype ReactiveLogOptions,\n\treactiveLog,\n} from \"@graphrefly/pure-ts/extra\";\nimport { Graph } from \"@graphrefly/pure-ts/graph\";\n\n// ── tryIncrementBounded ──────────────────────────────────────────────────\n\n/**\n * Bounded increment for a self-owned counter state node.\n *\n * Reads `counter.cache`, bumps by `by` (default 1) if `cur + by <= cap`,\n * writes back. Returns `false` when the cap would be exceeded (no-op write).\n * Documented P3 exception: the counter is not a declared dep of the caller —\n * it's a private budget read+written from a single call site. This helper\n * keeps the `.cache` access in one named place so caller bodies (which may\n * be inside reactive fn execution paths) stay free of cross-node `.cache`\n * reads.\n *\n * **Safety today:**\n * 1. Single-threaded JS runner never invokes the caller concurrently.\n * 2. `counter.down` writes the cache synchronously before returning, so\n * synchronous re-entry through a downstream publish reads the\n * freshly-incremented value — no double-count.\n *\n * **Future risk:** under a free-threaded runner (PY no-GIL or hypothetical\n * concurrent TS runner), two concurrent firings could still race. Revisit\n * when that surfaces.\n *\n * @param counter - Self-owned counter Node. Caller is the sole writer.\n * @param cap - Upper bound (inclusive). Pass `Number.MAX_SAFE_INTEGER` for\n * \"effectively unbounded\" use cases (e.g. token meters).\n * @param by - Delta to add (default `1`). Must be a finite non-negative\n * number; callers should pre-validate. Overflow-safe via\n * `by > cap - cur` check rather than `cur + by >= cap`.\n */\nexport function tryIncrementBounded(counter: Node<number>, cap: number, by = 1): boolean {\n\tconst cur = (counter.cache as number | undefined) ?? 0;\n\tif (by > cap - cur) return false;\n\tcounter.down([[DIRTY], [DATA, cur + by]]);\n\treturn true;\n}\n\n// ── Audit record schema ──────────────────────────────────────────────────\n\n/** Shared base shape for every audit record. Per-primitive types extend this. */\nexport interface BaseAuditRecord {\n\treadonly t_ns: number;\n\treadonly seq?: number;\n\treadonly handlerVersion?: { id: string; version: string | number };\n}\n\n// ── Default audit guard ──────────────────────────────────────────────────\n\n/**\n * Allow `observe` and `signal`; deny external `write` on the audit log so\n * consumers can subscribe + signal-bridge but cannot inject fake records.\n */\nexport const DEFAULT_AUDIT_GUARD: NodeGuard = policy((allow, deny) => {\n\tallow(\"observe\");\n\tallow(\"signal\");\n\tdeny(\"write\");\n});\n\n// ── createAuditLog ───────────────────────────────────────────────────────\n\nexport type AuditLogOpts<R extends BaseAuditRecord> = {\n\tname: string;\n\t/** Bounded retention; default 1024 per Audit 2 / cross-cutting bounded-default policy. */\n\tretainedLimit?: number;\n\t/** Override the default audit guard. */\n\tguard?: NodeGuard;\n\t/** Mount the audit `entries` Node under this graph (and activate withLatest). */\n\tgraph?: Graph;\n\t/** Pass-through to {@link reactiveLog}. */\n\tversioning?: ReactiveLogOptions<R>[\"versioning\"];\n};\n\n/**\n * Build a reactive audit log with sane defaults: bounded retention, deny-write\n * guard, `withLatest()` companions activated. Returns the {@link ReactiveLogBundle}\n * directly — primitives expose this as `<primitive>.events` / `.decisions` /\n * `.dispatches` / `.invocations` and alias it as `.audit`.\n *\n * @category internal\n */\nexport function createAuditLog<R extends BaseAuditRecord>(\n\topts: AuditLogOpts<R>,\n): ReactiveLogBundle<R> {\n\tconst log = reactiveLog<R>([], {\n\t\tname: opts.name,\n\t\tmaxSize: opts.retainedLimit ?? 1024,\n\t\tguard: opts.guard ?? DEFAULT_AUDIT_GUARD,\n\t\t...(opts.versioning != null ? { versioning: opts.versioning } : {}),\n\t});\n\t// Lazy companion activation up-front so `bundle.lastValue` / `hasLatest`\n\t// are queryable without an explicit `withLatest()` call.\n\tlog.withLatest();\n\tif (opts.graph) {\n\t\topts.graph.add(log.entries, { name: opts.name });\n\t}\n\treturn log;\n}\n\n// ── Universal mutation factory (Phase 14 — DS-14 lock Q-O2/Q-O3) ────────\n//\n// Single `mutate(act, opts)` factory. Two frames:\n//\n// - `\"inline\"` — no batch frame; up() runs raw. Seq bumps before action;\n// persists on throw. Hot-path-friendly for atomic single-write mutations.\n//\n// - `\"transactional\"` — opens `batch(() => up(...))`. On throw: batch discards\n// deferred deliveries, then `down()` runs, then failure record persists.\n//\n// **Heuristic:** if your imperative method's body is one or two lines (mutate\n// state, emit), use `frame: \"inline\"`. If it runs a user-supplied handler or\n// has multiple steps that could leave inconsistent state mid-throw, use\n// `frame: \"transactional\"`.\n\nexport type FailureMeta = {\n\tt_ns: number;\n\tseq?: number;\n\terrorType: string;\n};\n\nexport type SuccessMeta = {\n\tt_ns: number;\n\tseq?: number;\n};\n\n/**\n * Mutation action shape. Plain function shorthand auto-wraps as `{ up: fn }`.\n *\n * - `up` — the mutation action (the \"up migration\").\n * - `down` — optional rollback for closure mutations that `batch()` can't\n * reach. Receives the SAME frozen args as `up`. Runs AFTER batch reactive\n * rollback, BEFORE the failure record. Throws inside `down` are\n * console.error'd without masking the original error. Only meaningful\n * with `frame: \"transactional\"`.\n */\nexport type MutationAct<TArgs extends readonly unknown[], TResult> = {\n\tup: (...args: TArgs) => TResult;\n\tdown?: (...args: TArgs) => void;\n};\n\nexport type MutationFrame = \"inline\" | \"transactional\";\n\nexport type MutateOpts<TArgs extends readonly unknown[], TResult, R extends BaseAuditRecord> = {\n\t/** Frame mode. `\"inline\"` = no batch; `\"transactional\"` = batch + rollback. */\n\tframe: MutationFrame;\n\t/**\n\t * Optional log to append records to. When omitted, the wrapper still\n\t * provides freeze / seq-advance / rollback-on-throw but skips record\n\t * emission — useful for primitives that want centralized mutation\n\t * semantics without a dedicated log surface (e.g. `Topic.publish`).\n\t */\n\tlog?: ReactiveLogBundle<R>;\n\t/** Build the success record from the action's args + result + meta. */\n\tonSuccessRecord?: (args: TArgs, result: TResult, meta: SuccessMeta) => R | undefined;\n\t/** Build the failure record from the args + error + meta. */\n\tonFailureRecord?: (args: TArgs, error: unknown, meta: FailureMeta) => R | undefined;\n\t/** Deep-freeze args at entry (default `true`). Opt out for hot paths. */\n\tfreeze?: boolean;\n\t/** Optional sequence cursor — auto-advanced and stamped onto records. */\n\tseq?: Node<number>;\n\t/** Optional handler version — stamped per Audit 5. */\n\thandlerVersion?: { id: string; version: string | number };\n};\n\nfunction deepFreeze<T>(value: T): T {\n\tif (value === null || typeof value !== \"object\" || Object.isFrozen(value)) return value;\n\tfor (const k of Object.keys(value as Record<string, unknown>)) {\n\t\tdeepFreeze((value as Record<string, unknown>)[k]);\n\t}\n\treturn Object.freeze(value);\n}\n\n/**\n * Universal mutation factory (Phase 14 — DS-14 Q-O2).\n *\n * Replaces the prior `lightMutation` + `wrapMutation` two-tier split.\n * Single factory with `frame: \"inline\" | \"transactional\"` discriminant.\n *\n * @param act - The mutation action. Either a plain function (auto-wrapped as\n * `{ up: fn }`) or a `{ up, down? }` object for explicit rollback.\n * @param opts - Configuration: frame, log, record builders, freeze, seq.\n * @returns A typed wrapper function with the same signature as `act.up`.\n */\nexport function mutate<TArgs extends readonly unknown[], TResult, R extends BaseAuditRecord>(\n\tact: MutationAct<TArgs, TResult> | ((...args: TArgs) => TResult),\n\topts: MutateOpts<TArgs, TResult, R>,\n): (...args: TArgs) => TResult {\n\tconst { up, down } = typeof act === \"function\" ? { up: act, down: undefined } : act;\n\tconst freeze = opts.freeze ?? true;\n\n\tif (opts.frame === \"inline\") {\n\t\treturn function wrapped(...args: TArgs): TResult {\n\t\t\tconst sealed = freeze ? (args.map(deepFreeze) as unknown as TArgs) : args;\n\t\t\tconst t_ns = wallClockNs();\n\t\t\tconst seq = opts.seq ? bumpCursor(opts.seq) : undefined;\n\t\t\ttry {\n\t\t\t\tconst result = up(...sealed);\n\t\t\t\tif (opts.log && opts.onSuccessRecord) {\n\t\t\t\t\tappendAudit<TArgs, TResult, R, SuccessMeta>(\n\t\t\t\t\t\topts.log,\n\t\t\t\t\t\topts.onSuccessRecord,\n\t\t\t\t\t\tsealed,\n\t\t\t\t\t\tresult,\n\t\t\t\t\t\t{ t_ns, seq },\n\t\t\t\t\t\topts.handlerVersion,\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\treturn result;\n\t\t\t} catch (err) {\n\t\t\t\tif (opts.log && opts.onFailureRecord) {\n\t\t\t\t\tconst errorType = err instanceof Error ? err.name : typeof err;\n\t\t\t\t\tappendAudit<TArgs, unknown, R, FailureMeta>(\n\t\t\t\t\t\topts.log,\n\t\t\t\t\t\topts.onFailureRecord,\n\t\t\t\t\t\tsealed,\n\t\t\t\t\t\terr,\n\t\t\t\t\t\t{ t_ns, seq, errorType },\n\t\t\t\t\t\topts.handlerVersion,\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\tthrow err;\n\t\t\t}\n\t\t};\n\t}\n\n\t// frame === \"transactional\"\n\treturn function wrapped(...args: TArgs): TResult {\n\t\tconst sealed = freeze ? (args.map(deepFreeze) as unknown as TArgs) : args;\n\t\tconst t_ns = wallClockNs();\n\t\tlet result: TResult;\n\t\tlet captured: unknown;\n\t\tlet captureSet = false;\n\t\tlet seq: number | undefined;\n\t\ttry {\n\t\t\tbatch(() => {\n\t\t\t\tif (opts.seq) seq = bumpCursor(opts.seq);\n\t\t\t\ttry {\n\t\t\t\t\tresult = up(...sealed);\n\t\t\t\t\tif (opts.log && opts.onSuccessRecord) {\n\t\t\t\t\t\tappendAudit<TArgs, TResult, R, SuccessMeta>(\n\t\t\t\t\t\t\topts.log,\n\t\t\t\t\t\t\topts.onSuccessRecord,\n\t\t\t\t\t\t\tsealed,\n\t\t\t\t\t\t\tresult,\n\t\t\t\t\t\t\t{ t_ns, seq },\n\t\t\t\t\t\t\topts.handlerVersion,\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\t\t\t\t} catch (err) {\n\t\t\t\t\tcaptured = err;\n\t\t\t\t\tcaptureSet = true;\n\t\t\t\t\tthrow err;\n\t\t\t\t}\n\t\t\t});\n\t\t} catch (outerErr) {\n\t\t\t// Fire `down` AFTER batch's reactive rollback, BEFORE failure record.\n\t\t\t// Gate on `captureSet` — if the throw came from outside the inner try\n\t\t\t// (framework-level batch error before action ran), don't fire down.\n\t\t\tif (captureSet && down) {\n\t\t\t\ttry {\n\t\t\t\t\tdown(...sealed);\n\t\t\t\t} catch (downErr) {\n\t\t\t\t\tconsole.error(\n\t\t\t\t\t\t`mutate: down hook threw — original action error preserved (${\n\t\t\t\t\t\t\tcaptured instanceof Error ? captured.name : typeof captured\n\t\t\t\t\t\t}). Down error:`,\n\t\t\t\t\t\tdownErr,\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (captureSet && opts.log && opts.onFailureRecord) {\n\t\t\t\tconst errorType = captured instanceof Error ? captured.name : typeof captured;\n\t\t\t\tappendAudit<TArgs, unknown, R, FailureMeta>(\n\t\t\t\t\topts.log,\n\t\t\t\t\topts.onFailureRecord,\n\t\t\t\t\tsealed,\n\t\t\t\t\tcaptured,\n\t\t\t\t\t{ t_ns, seq, errorType },\n\t\t\t\t\topts.handlerVersion,\n\t\t\t\t);\n\t\t\t}\n\t\t\tthrow captureSet ? captured : outerErr;\n\t\t}\n\t\treturn result!;\n\t};\n}\n\n/**\n * Advance a cursor node and return the new value. Emits `[DIRTY], [DATA, next]`\n * directly on the cursor — atomic outside a batch, rollback-discardable inside.\n *\n * Resets to `0` if the cursor cache is missing, non-numeric, `NaN`, or\n * non-finite (e.g. corrupted by `restore()` from a malformed snapshot, or\n * by a misbehaving codec). `??` alone would let `NaN` and `\"\"` pass through\n * and silently corrupt audit ordering downstream.\n *\n * **Silent reset diagnostic (EH-12).** When the cache holds a non-numeric\n * value at bump time, the cursor restarts at 0 and the next bump returns 1\n * — colliding with the seq stamped on the very first record after construct.\n * To make seq-monotonicity violations after a restore visible to operators,\n * the helper emits a one-shot `console.warn` per cursor instance describing\n * the offending value. The cursor is identified by a `WeakSet<Node<number>>`\n * so the warning fires exactly once per node — repeat malformed bumps stay\n * quiet to avoid log spam. Production callers wanting to suppress can swap\n * the global `console` (universal-safe code path; no Node-only API used).\n *\n * Works whether or not the cursor has any subscribers — `down` updates the\n * cache regardless, so primitives that bump before consumers attach (e.g.\n * `JobQueueGraph.enqueue`) still see a coherent sequence.\n *\n * @category internal\n */\nconst _bumpCursorWarned = new WeakSet<Node<number>>();\nexport function bumpCursor(seq: Node<number>): number {\n\tconst raw = seq.cache;\n\tconst valid = typeof raw === \"number\" && Number.isFinite(raw);\n\tif (!valid && raw !== undefined && !_bumpCursorWarned.has(seq)) {\n\t\t_bumpCursorWarned.add(seq);\n\t\tconsole.warn(\n\t\t\t`bumpCursor: cursor cache held a non-numeric value (${String(raw)}); resetting to 0. ` +\n\t\t\t\t\"Causes include: a snapshot codec round-tripping the cursor as a string / null / NaN, \" +\n\t\t\t\t\"OR a malformed initial seed (e.g. state<number>(NaN)). \" +\n\t\t\t\t\"Audit consumers may see colliding seq values after this point.\",\n\t\t);\n\t}\n\tconst cur = valid ? raw : 0;\n\tconst next = cur + 1;\n\tseq.down([[DIRTY], [DATA, next]]);\n\treturn next;\n}\n\n/**\n * Build a record via the supplied builder, stamp `handlerVersion` if present,\n * and append it to the audit log. `undefined` records are skipped (callers\n * pass an `onSuccess` / `onFailure` that returns `undefined` to opt out per\n * call).\n *\n * @category internal\n */\nexport function appendAudit<\n\tTArgs extends readonly unknown[],\n\tTValue,\n\tR extends BaseAuditRecord,\n\tM extends SuccessMeta | FailureMeta,\n>(\n\taudit: ReactiveLogBundle<R>,\n\tbuilder: (args: TArgs, value: TValue, meta: M) => R | undefined,\n\targs: TArgs,\n\tvalue: TValue,\n\tmeta: M,\n\thandlerVersion?: { id: string; version: string | number },\n): void {\n\tconst record = builder(args, value, meta);\n\tif (record === undefined) return;\n\tconst stamped = handlerVersion != null ? ({ ...record, handlerVersion } as R) : record;\n\taudit.append(stamped);\n}\n\n// ── registerCursor / registerCursorMap ───────────────────────────────────\n\n/**\n * Promote a closure counter to a state node mounted under `graph`.\n * Replaces ad-hoc `let _seq = 0` patterns with a node observable in\n * `describe()` and persistable via storage tiers.\n *\n * @category internal\n */\nexport function registerCursor(graph: Graph, name: string, initial = 0): Node<number> {\n\tconst cursor = node<number>([], { initial, name, describeKind: \"state\" });\n\tgraph.add(cursor, { name });\n\treturn cursor;\n}\n\n/**\n * Promote a closure `Map<K, number>` to N state nodes (one per key) mounted\n * under `<graph>::<name>::<key>`. Used by saga (per-event-type cursor).\n *\n * @category internal\n */\nexport function registerCursorMap<K extends string>(\n\tgraph: Graph,\n\tname: string,\n\tkeys: readonly K[],\n\tinitial = 0,\n): { readonly [P in K]: Node<number> } {\n\tconst out = {} as { [P in K]: Node<number> };\n\t// Mount cursors under a child plain-Graph so per-key node names stay flat\n\t// (path-separator `::` is reserved by Graph.add). Using `Graph` directly\n\t// rather than `graph.constructor` avoids spawning a typed subclass with\n\t// an incompatible constructor signature (e.g., CqrsGraph(name, opts)).\n\tconst sub = new Graph(name);\n\tfor (const k of keys) {\n\t\tconst cursor = node<number>([], {\n\t\t\tinitial,\n\t\t\tname: k,\n\t\t\tdescribeKind: \"state\",\n\t\t});\n\t\tsub.add(cursor, { name: k });\n\t\tout[k] = cursor;\n\t}\n\tgraph.mount(name, sub);\n\treturn out;\n}\n","/**\n * Reactive graph observability preset (Tier 5.3 reshape — Session A.4 lock).\n *\n * `graphLens(target)` is a thin compositor (~30 LOC of glue) over two\n * already-shipped reactive primitives:\n *\n * - `target.describe({ reactive: true })` — live topology snapshot.\n * - `target.observe({ reactive: true, tiers: [\"data\"] })` — coalesced data\n * changesets per outermost batch flush.\n *\n * It returns three Nodes plus a dispose function:\n *\n * - `topology: Node<GraphDescribeOutput>` — the live describe snapshot,\n * re-emitting on every settle of the target tree (structural change,\n * error/complete/teardown transition).\n * - `health: Node<HealthReport>` — `{ok, problems[]}` aggregated from the\n * live describe; `ok=false` when any node enters `\"errored\"` status,\n * with `upstreamCause` walked backward through deps.\n * - `flow: Node<ReadonlyMap<string, FlowEntry>>` — per-path DATA counters\n * accumulating as `target.observe({reactive: true, tiers: [\"data\"]})`\n * delivers changesets. The map is reconciled against `topology` on each\n * topology change so removed nodes drop their entries.\n *\n * Pre-Tier-5.3 shipped a `LensGraph` class with `stats` / `health` / `flow`\n * (as a `ReactiveMapBundle`) / `why(from, to)` / `flowEntryNode(...)`. The\n * surface collapsed because every concern was already a one-liner over\n * `describe({reactive:true})` + `observe({reactive:true, tiers})` once those\n * landed in Tier 1.5.1 / 1.5.2 — the class added no protocol-level concept,\n * just glue. Callers who need topology stats compose a derived over\n * `topology` directly; callers who need a causal chain call\n * `target.describe({ explain: { from, to }, reactive: true })`.\n *\n * The transitive topology-subscription helper {@link watchTopologyTree} is\n * re-exported here for downstream factories that need full-tree dynamic\n * coverage without taking a dep on `graph/`.\n *\n * @module\n */\nimport { type Node, node } from \"@graphrefly/pure-ts/core\";\nimport { keepalive } from \"@graphrefly/pure-ts/extra\";\nimport {\n\ttype Graph,\n\ttype GraphDescribeOutput,\n\ttype ObserveChangeset,\n\ttype ObserveEvent,\n\treachable,\n} from \"@graphrefly/pure-ts/graph\";\nimport { domainMeta } from \"../../base/meta/domain-meta.js\";\n\nexport { watchTopologyTree } from \"@graphrefly/pure-ts/graph\";\n\n// ---------------------------------------------------------------------------\n// Payload types\n// ---------------------------------------------------------------------------\n\n/** A single health problem entry. */\nexport interface HealthProblem {\n\tpath: string;\n\t/** V1 only reports `\"errored\"`. Future versions may add `\"completed\"`, `\"disconnected\"`. */\n\tstatus: \"errored\";\n\t/** First errored upstream ancestor along the dep chain, when one exists and is distinct from `path`. */\n\tupstreamCause?: string;\n}\n\n/** Aggregate health snapshot. `ok=true` iff `problems.length === 0`. */\nexport interface HealthReport {\n\tok: boolean;\n\tproblems: readonly HealthProblem[];\n}\n\n/** Per-path flow entry stored in the {@link GraphLensView.flow} map. */\nexport interface FlowEntry {\n\tpath: string;\n\t/** Cumulative DATA emissions observed since the lens activated. */\n\tcount: number;\n\t/** Monotonic ns at the most recent DATA emission for this path, or `null` if none yet. */\n\tlastUpdate_ns: number | null;\n}\n\n/** Output of {@link graphLens}. */\nexport interface GraphLensView {\n\t/** Live describe snapshot. Re-emits on structural change AND status transitions. */\n\ttopology: Node<GraphDescribeOutput>;\n\t/** Live `{ok, problems[]}`. Aggregated from `topology`; equality-deduped. */\n\thealth: Node<HealthReport>;\n\t/** Per-path DATA counter map. Re-emits per outermost batch flush. */\n\tflow: Node<ReadonlyMap<string, FlowEntry>>;\n\t/**\n\t * Tear down the underlying `describe({reactive:true})` subscription.\n\t * The `flow` node activates lazily; subscribing-then-unsubscribing reclaims\n\t * its observe stream automatically. Call `dispose()` once when finished.\n\t */\n\tdispose(): void;\n}\n\n// ---------------------------------------------------------------------------\n// Pure helpers (exported for testing / composition)\n// ---------------------------------------------------------------------------\n\n/** Build a `HealthReport` from a fresh `GraphDescribeOutput`. */\nexport function computeHealthReport(described: GraphDescribeOutput): HealthReport {\n\tconst problems: HealthProblem[] = [];\n\tfor (const [path, desc] of Object.entries(described.nodes)) {\n\t\tif (desc.status !== \"errored\") continue;\n\t\tconst entry: HealthProblem = { path, status: \"errored\" };\n\t\t// Walk upstream to find the first errored ancestor (if any) distinct from `path`.\n\t\t// Explicit empty options disambiguates the overload (otherwise both match\n\t\t// and TS picks the `withDetail:true` signature first).\n\t\tconst upstream = reachable(described, path, \"upstream\", {});\n\t\tfor (const p of upstream) {\n\t\t\tif (p === path) continue;\n\t\t\tif (described.nodes[p]?.status === \"errored\") {\n\t\t\t\tentry.upstreamCause = p;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\tproblems.push(entry);\n\t}\n\tproblems.sort((a, b) => (a.path < b.path ? -1 : a.path > b.path ? 1 : 0));\n\treturn { ok: problems.length === 0, problems };\n}\n\n/** Structural equality for {@link HealthReport} — used as the `health` derived's `equals`. */\nexport function healthReportEqual(a: HealthReport, b: HealthReport): boolean {\n\tif (a.ok !== b.ok) return false;\n\tif (a.problems.length !== b.problems.length) return false;\n\tfor (let i = 0; i < a.problems.length; i++) {\n\t\tconst x = a.problems[i]!;\n\t\tconst y = b.problems[i]!;\n\t\tif (x.path !== y.path) return false;\n\t\tif (x.status !== y.status) return false;\n\t\tif (x.upstreamCause !== y.upstreamCause) return false;\n\t}\n\treturn true;\n}\n\n// ---------------------------------------------------------------------------\n// graphLens preset\n// ---------------------------------------------------------------------------\n\n/**\n * Reactive observability preset over a target {@link Graph}.\n *\n * @param target - The graph to observe.\n *\n * @example\n * ```ts\n * const g = new Graph(\"app\");\n * g.add(state(0, { name: \"counter\" }), { name: \"counter\" });\n *\n * const lens = graphLens(g);\n * lens.topology.subscribe((msgs) => console.log(\"topology:\", msgs));\n * lens.health.subscribe((msgs) => console.log(\"health:\", msgs));\n * lens.flow.subscribe((msgs) => {\n * for (const [type, payload] of msgs) {\n * if (type === DATA) console.log(\"flow map size:\", (payload as ReadonlyMap<string, FlowEntry>).size);\n * }\n * });\n *\n * // Causal chains: use the underlying primitive directly — `graphLens` no\n * // longer wraps it, since `graph.describe({ explain: {...}, reactive: true })`\n * // already provides everything the old `lens.why()` did.\n * const why = g.describe({\n * explain: { from: \"counter\", to: \"consumer\" },\n * reactive: true,\n * });\n *\n * // Tear down when done.\n * lens.dispose();\n * ```\n *\n * @category observability\n */\nexport function graphLens(target: Graph): GraphLensView {\n\tconst topologyHandle = target.describe({\n\t\treactive: true,\n\t\tdetail: \"standard\",\n\t\treactiveName: \"graphLens.topology\",\n\t});\n\tconst topology = topologyHandle.node;\n\n\tconst health = node<HealthReport>(\n\t\t[topology],\n\t\t(batchData, actions, ctx) => {\n\t\t\tconst data = batchData.map((batch, i) =>\n\t\t\t\tbatch != null && batch.length > 0 ? batch.at(-1) : ctx.prevData[i],\n\t\t\t);\n\t\t\tactions.emit(computeHealthReport(data[0] as GraphDescribeOutput));\n\t\t},\n\t\t{\n\t\t\tname: \"graphLens.health\",\n\t\t\tdescribeKind: \"derived\",\n\t\t\tequals: healthReportEqual,\n\t\t\tmeta: domainMeta(\"lens\", \"health\"),\n\t\t},\n\t);\n\t// `topology` is already kept alive by `_describeReactive`'s internal\n\t// keepalive; `health` needs its own so the derived stays warm even if\n\t// the consumer subscribes lazily.\n\tconst stopHealthKeep = keepalive(health);\n\n\t// `flow` accumulates per-path counters across emissions. Closure-mirror\n\t// (COMPOSITION-GUIDE §28) holds the canonical map; the derived returns a\n\t// fresh ReadonlyMap projection per emit so consumers never observe an\n\t// in-place mutation. Topology drives reconciliation: paths that disappear\n\t// from the describe drop their counter entry.\n\t//\n\t// `lastAppliedFlush_ns` guards against double-applying the same changeset\n\t// when topology re-emits without a new dataFlow event (e.g. a node remove\n\t// re-fires `topology` while `dataFlow.cache` still holds the previous\n\t// changeset by reference). Using the monotonic `flushedAt_ns` cursor (qa\n\t// G2A — EC4 fix) is more robust than ref-comparison: an empty changeset\n\t// re-emitted with a fresh object identity but the same `flushedAt_ns`\n\t// won't trigger a stale-events replay, and a genuinely-new changeset\n\t// always advances the cursor.\n\tconst flowMap = new Map<string, FlowEntry>();\n\tlet lastAppliedFlush_ns = -1;\n\tconst dataFlow = target.observe({ reactive: true, tiers: [\"data\"] });\n\tconst flow = node<ReadonlyMap<string, FlowEntry>>(\n\t\t[dataFlow, topology],\n\t\t(batchData, actions, ctx) => {\n\t\t\tconst data = batchData.map((batch, i) =>\n\t\t\t\tbatch != null && batch.length > 0 ? batch.at(-1) : ctx.prevData[i],\n\t\t\t);\n\t\t\tconst changeset = data[0];\n\t\t\tconst described = data[1];\n\t\t\tconst c = changeset as ObserveChangeset | undefined;\n\t\t\tconst desc = described as GraphDescribeOutput | undefined;\n\n\t\t\t// Apply NEW changeset events first so a freshly-emitted node-remove\n\t\t\t// observed below cleanly drops the entry. (If we reconciled first\n\t\t\t// then applied, an event referencing a path the topology had already\n\t\t\t// dropped would re-create the entry only to be wiped on the next\n\t\t\t// topology re-emit — order events-then-topology to avoid that.)\n\t\t\t//\n\t\t\t// Skip when `events.length === 0`: an empty changeset has no work\n\t\t\t// to do. The cursor advances anyway so a future identical-content\n\t\t\t// changeset (different ref, same flushedAt_ns) is also skipped.\n\t\t\tif (c != null && c.flushedAt_ns > lastAppliedFlush_ns) {\n\t\t\t\tlastAppliedFlush_ns = c.flushedAt_ns;\n\t\t\t\tfor (const event of c.events as readonly ObserveEvent[]) {\n\t\t\t\t\tif (event.type !== \"data\") continue;\n\t\t\t\t\tconst path = event.path;\n\t\t\t\t\tif (path == null || path === \"\") continue;\n\t\t\t\t\tconst prior = flowMap.get(path);\n\t\t\t\t\tflowMap.set(path, {\n\t\t\t\t\t\tpath,\n\t\t\t\t\t\tcount: (prior?.count ?? 0) + 1,\n\t\t\t\t\t\tlastUpdate_ns: c.flushedAt_ns,\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Reconcile against current topology — drop entries whose paths\n\t\t\t// no longer exist in the describe.\n\t\t\tif (desc != null && flowMap.size > 0) {\n\t\t\t\tconst valid = new Set(Object.keys(desc.nodes));\n\t\t\t\tfor (const k of [...flowMap.keys()]) {\n\t\t\t\t\tif (!valid.has(k)) flowMap.delete(k);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Snapshot — consumers receive a frozen view.\n\t\t\tactions.emit(new Map(flowMap) as ReadonlyMap<string, FlowEntry>);\n\t\t},\n\t\t{\n\t\t\tname: \"graphLens.flow\",\n\t\t\tdescribeKind: \"derived\",\n\t\t\tmeta: domainMeta(\"lens\", \"flow\"),\n\t\t},\n\t);\n\tconst stopFlowKeep = keepalive(flow);\n\n\tlet disposed = false;\n\treturn {\n\t\ttopology,\n\t\thealth,\n\t\tflow,\n\t\tdispose() {\n\t\t\tif (disposed) return;\n\t\t\tdisposed = true;\n\t\t\tstopFlowKeep();\n\t\t\tstopHealthKeep();\n\t\t\ttopologyHandle.dispose();\n\t\t},\n\t};\n}\n","/**\n * Composable safety layer (roadmap §9.0b — Tier 5.1 Wave-B rebuild).\n *\n * {@link guardedExecution} wraps any {@link Graph} with:\n *\n * - {@link policyGate} — reactive ABAC (Tier 2.3 rename of `policyEnforcer`),\n * policies stored as a `Node` so LLMs / humans can update them at runtime.\n * Full transitive dynamic coverage via `watchTopologyTree`.\n * - Reactive {@link GuardedExecutionGraph.scopedDescribeNode} — a thin\n * delegate over `target.describe({ reactive: true, actor })` that re-derives\n * on every settle (topology change, error transition, actor swap).\n * - The enforcer's `violations` topic is republished as `violations` on\n * the wrapper, composable with {@link graphLens}'s `health`.\n * - The wrapper-level `lints` topic surfaces non-policy diagnostic warnings\n * (`empty-policies` / `audit-no-effect` / `no-actor`) so misconfigurations\n * are caught reactively rather than via thrown errors at scattered call sites.\n * - The `scope` derived publishes the current configuration tuple\n * (`{actor, mode, policiesCount}`) for dashboards.\n *\n * V1 scope: policies + actor + reactive scoped describe + lints + scope.\n * Budget-as-option is NOT in V1 — it requires a cost-tracking design that\n * hasn't landed yet. Callers who need a budget limit today append a\n * budget-aware {@link PolicyRuleData} to the policies list (check current\n * cost and `deny` when exhausted).\n *\n * @module\n */\nimport type { Actor, PolicyRuleData } from \"@graphrefly/pure-ts/core\";\nimport { DATA, monotonicNs, type Node, node } from \"@graphrefly/pure-ts/core\";\nimport { keepalive } from \"@graphrefly/pure-ts/extra\";\nimport {\n\ttype DescribeFilter,\n\tGraph,\n\ttype GraphDescribeOptions,\n\ttype GraphDescribeOutput,\n\ttype GraphOptions,\n} from \"@graphrefly/pure-ts/graph\";\nimport { domainMeta } from \"../../base/meta/domain-meta.js\";\nimport {\n\ttype PolicyGateGraph,\n\ttype PolicyViolation,\n\tpolicyGate,\n} from \"../../utils/inspect/audit.js\";\nimport { TopicGraph } from \"../../utils/messaging/index.js\";\n\nfunction isNode<T>(x: unknown): x is Node<T> {\n\treturn (\n\t\ttypeof x === \"object\" && x !== null && \"subscribe\" in (x as object) && \"down\" in (x as object)\n\t);\n}\n\nfunction guardedMeta(kind: string): Record<string, unknown> {\n\treturn domainMeta(\"guarded\", kind);\n}\n\n/** Diagnostic warning published on {@link GuardedExecutionGraph.lints}. */\nexport interface GuardedExecutionLint {\n\t/**\n\t * - `\"empty-policies\"` — `policies` Node emitted an empty array in\n\t * `mode: \"enforce\"`. Static empty arrays throw at construction; this\n\t * covers the reactive case.\n\t * - `\"audit-no-effect\"` — `mode: \"audit\"` plus the target has no per-node\n\t * guards, so `scopedDescribeNode` filters by per-node guards only and\n\t * policies will never gate visibility (they still populate `violations`\n\t * on writes).\n\t * - `\"no-actor\"` — neither a wrapper-configured nor per-call actor was\n\t * supplied. `scopedDescribeNode` falls back to \"describe everything\"\n\t * for the corresponding subscription.\n\t */\n\tkind: \"empty-policies\" | \"audit-no-effect\" | \"no-actor\";\n\tmessage: string;\n\ttimestamp_ns: number;\n}\n\n/** Configuration tuple published on {@link GuardedExecutionGraph.scope}. */\nexport interface GuardedScope {\n\t/** The wrapper-configured default actor, or `null` when none configured. */\n\tactor: Actor | null;\n\tmode: \"audit\" | \"enforce\";\n\t/** Current policy count (reactive — re-emits on `policies` Node updates). */\n\tpoliciesCount: number;\n}\n\n/** Options for {@link guardedExecution}. */\nexport interface GuardedExecutionOptions {\n\t/**\n\t * Policies enforced against every guarded write. Static list or a live\n\t * `Node<readonly PolicyRuleData[]>` (LLM-updatable).\n\t *\n\t * **Empty-policies handling:**\n\t * - Static empty array + `mode: \"enforce\"` throws `RangeError` at\n\t * construction (deny-by-default is almost certainly a misconfiguration).\n\t * - Node-supplied empty array + `mode: \"enforce\"` emits a one-time\n\t * `\"empty-policies\"` lint on first such emission (the wrapper can't\n\t * throw mid-run — surface the warning reactively).\n\t * - `mode: \"audit\"` tolerates empty policies (no guards stacked; policies\n\t * only feed the `violations` channel on writes).\n\t */\n\tpolicies: readonly PolicyRuleData[] | Node<readonly PolicyRuleData[]>;\n\t/**\n\t * Default actor used when the caller invokes\n\t * {@link GuardedExecutionGraph.scopedDescribeNode} without an override.\n\t * Accepts a static {@link Actor} or a `Node<Actor>` — when a Node is\n\t * supplied, the reactive describe re-derives on every actor emission so\n\t * harnesses binding a per-turn actor get a single live describe Node\n\t * instead of re-creating one per turn.\n\t *\n\t * Omit to scope per-call only. A `\"no-actor\"` lint fires once per instance\n\t * if neither a configured nor per-call actor is ever supplied (the\n\t * resulting describe is unscoped — full visibility).\n\t */\n\tactor?: Actor | Node<Actor>;\n\t/**\n\t * `\"enforce\"` (default) — push guards onto target nodes so disallowed\n\t * writes throw {@link GuardDenied}.\n\t * `\"audit\"` — record would-be denials to the `violations` topic without\n\t * blocking writes.\n\t */\n\tmode?: \"audit\" | \"enforce\";\n\t/** Ring-buffer cap for the `violations` topic. Default 1000 (inherited from policyGate). */\n\tviolationsLimit?: number;\n\t/** Ring-buffer cap for the `lints` topic. Default 64 — each lint kind fires at most once per instance. */\n\tlintsLimit?: number;\n\t/** Wrapper graph name. Default `${target.name}_guarded`. */\n\tname?: string;\n\t/** Wrapper graph options. */\n\tgraph?: GraphOptions;\n}\n\n/**\n * Wrapper over a target {@link Graph} providing reactive ABAC + reactive\n * scoped describe + diagnostic lints. Mounts a {@link PolicyGateGraph} under\n * `enforcer`, a {@link TopicGraph} of {@link GuardedExecutionLint} under\n * `lints`, and a `scope` derived publishing `{actor, mode, policiesCount}`.\n *\n * @category patterns\n */\nexport class GuardedExecutionGraph extends Graph {\n\treadonly enforcer: PolicyGateGraph;\n\treadonly violations: TopicGraph<PolicyViolation>;\n\treadonly lints: TopicGraph<GuardedExecutionLint>;\n\treadonly scope: Node<GuardedScope>;\n\t/**\n\t * Canonical reactive describe scoped to the wrapper's configured `actor`.\n\t * Subscribes ONCE at construction; lifecycle owned by the wrapper (disposed\n\t * on `wrapper.destroy()`). Use this property for the common case\n\t * (long-lived consumer wanting \"describe scoped to my actor\"); use\n\t * {@link scopedDescribeNode} only when a per-call actor override or\n\t * different `detail`/`fields` is required.\n\t *\n\t * Re-derives on every settle of the target graph: structural changes,\n\t * status transitions (errors flip nodes into `\"errored\"`), and actor\n\t * emissions (when a `Node<Actor>` is bound, including the SENTINEL bridge\n\t * applied internally — see qa G1B).\n\t */\n\treadonly scopedDescribe: Node<GraphDescribeOutput>;\n\tprivate readonly _target: Graph;\n\tprivate readonly _actorNode: Node<Actor | null>;\n\tprivate readonly _mode: \"audit\" | \"enforce\";\n\tprivate readonly _firedLintKinds = new Set<GuardedExecutionLint[\"kind\"]>();\n\n\tconstructor(target: Graph, opts: GuardedExecutionOptions) {\n\t\tsuper(opts.name ?? `${target.name}_guarded`, opts.graph);\n\t\tthis._target = target;\n\t\tthis._mode = opts.mode ?? \"enforce\";\n\n\t\tconst policiesOpt = opts.policies;\n\t\tconst policiesIsNode = isNode<readonly PolicyRuleData[]>(policiesOpt);\n\t\t// Static empty + enforce → throw (deny-by-default = misconfig).\n\t\tif (\n\t\t\t!policiesIsNode &&\n\t\t\tthis._mode === \"enforce\" &&\n\t\t\t(policiesOpt as readonly PolicyRuleData[]).length === 0\n\t\t) {\n\t\t\tthrow new RangeError(\n\t\t\t\t'guardedExecution: empty `policies` in `mode: \"enforce\"` denies every action. ' +\n\t\t\t\t\t'Pass at least `{ effect: \"allow\", action: \"*\" }` and layer deny rules on top.',\n\t\t\t);\n\t\t}\n\n\t\t// Lints topic — mounted before any potential first-DATA emission.\n\t\tthis.lints = new TopicGraph<GuardedExecutionLint>(\"lints\", {\n\t\t\tretainedLimit: opts.lintsLimit ?? 64,\n\t\t});\n\t\tthis.mount(\"lints\", this.lints);\n\n\t\t// Normalize `actor` to a Node<Actor | null>. `null` (a valid DATA in\n\t\t// the v5 sentinel-as-undefined model) means \"no actor configured\" —\n\t\t// `target.describe({ reactive: true })`'s `resolveActorOption` treats\n\t\t// `null` cache as \"no scoping\" (full visibility), and the `scope`\n\t\t// derived can publish `actor: null` cleanly. Using `node([], { initial: undefined })`\n\t\t// here would leave the actor node in sentinel state, which never fires\n\t\t// DATA on subscribe and would block the `scope` derived's first-run\n\t\t// gate from completing.\n\t\t//\n\t\t// qa G1B (EC2 fix): when the caller passes a `Node<Actor>`, that node's\n\t\t// cache may itself be SENTINEL at construction (e.g. a `producer`\n\t\t// awaiting first emission, or a deferred `derived`). Forwarding the raw\n\t\t// Node would re-introduce the same sentinel-stall on `scope`. Bridge\n\t\t// through a node bridge with a `null` initial so the\n\t\t// internal `_actorNode` always carries non-sentinel cache; the bridge\n\t\t// re-emits whenever the caller's Node emits, and forwards `null`\n\t\t// through unchanged.\n\t\tconst actorOpt = opts.actor;\n\t\tif (actorOpt == null) {\n\t\t\tthis._actorNode = node<Actor | null>([], { name: \"actor\", initial: null });\n\t\t} else if (isNode<Actor>(actorOpt)) {\n\t\t\tthis._actorNode = node<Actor | null>(\n\t\t\t\t[actorOpt],\n\t\t\t\t(batchData, actions, ctx) => {\n\t\t\t\t\tconst data = batchData.map((batch, i) =>\n\t\t\t\t\t\tbatch != null && batch.length > 0 ? batch.at(-1) : ctx.prevData[i],\n\t\t\t\t\t);\n\t\t\t\t\tactions.emit((data[0] as Actor | null | undefined) ?? null);\n\t\t\t\t},\n\t\t\t\t{ describeKind: \"derived\", name: \"actor\", initial: null },\n\t\t\t);\n\t\t} else {\n\t\t\tthis._actorNode = node<Actor | null>([], { name: \"actor\", initial: actorOpt });\n\t\t}\n\n\t\t// Mount the enforcer.\n\t\tconst enforcerOpts: { mode: \"audit\" | \"enforce\"; name: string; violationsLimit?: number } = {\n\t\t\tmode: this._mode,\n\t\t\tname: \"enforcer\",\n\t\t};\n\t\tif (opts.violationsLimit != null) enforcerOpts.violationsLimit = opts.violationsLimit;\n\t\tthis.enforcer = policyGate(target, opts.policies, enforcerOpts);\n\t\tthis.violations = this.enforcer.violations;\n\t\tthis.mount(\"enforcer\", this.enforcer);\n\n\t\t// Empty-policies one-time lint (Node form, enforce mode only).\n\t\tif (policiesIsNode && this._mode === \"enforce\") {\n\t\t\tconst policiesNode = policiesOpt as Node<readonly PolicyRuleData[]>;\n\t\t\t// Two paths here intentionally:\n\t\t\t// (1) Synchronous cache seed — fires immediately if the Node was\n\t\t\t// constructed with an empty array as its current cache. This\n\t\t\t// covers `node<…[]>([], { initial: [] })` and any pre-emitted derived.\n\t\t\t// (2) Subscribe path — catches subsequent empty emissions AND\n\t\t\t// handles the SENTINEL case (cache=undefined at construction)\n\t\t\t// where the Node fires its first DATA after the wrapper is\n\t\t\t// built. The `_firedLintKinds` Set keeps the lint one-shot\n\t\t\t// across both paths.\n\t\t\t// qa F6 (deferred): if a Node's initial cache is SENTINEL (undefined)\n\t\t\t// AND it never emits an empty array (only ever non-empty), the lint\n\t\t\t// never fires — that's correct behavior, the configuration is\n\t\t\t// effectively non-empty for the wrapper's lifetime.\n\t\t\tconst cached = policiesNode.cache as readonly PolicyRuleData[] | undefined;\n\t\t\tif (cached != null && cached.length === 0) {\n\t\t\t\tthis._fireLint(\n\t\t\t\t\t\"empty-policies\",\n\t\t\t\t\t'`policies` Node cached an empty array in `mode: \"enforce\"` — every action will be denied. Add at least one allow rule.',\n\t\t\t\t);\n\t\t\t}\n\t\t\tconst offEmpty = policiesNode.subscribe((msgs) => {\n\t\t\t\tfor (const m of msgs) {\n\t\t\t\t\tif (m[0] !== DATA) continue;\n\t\t\t\t\tconst v = m[1] as readonly PolicyRuleData[] | undefined;\n\t\t\t\t\tif (v == null || v.length === 0) {\n\t\t\t\t\t\tthis._fireLint(\n\t\t\t\t\t\t\t\"empty-policies\",\n\t\t\t\t\t\t\t'`policies` Node emitted an empty array in `mode: \"enforce\"` — every action will be denied. Add at least one allow rule.',\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t});\n\t\t\tthis.addDisposer(offEmpty);\n\t\t}\n\n\t\t// Audit-mode + no per-node guards on the target → fire once.\n\t\t// qa EC5 (deferred): the check is one-shot at construction. If the\n\t\t// caller later mounts a guarded node into the target, the lint stays\n\t\t// retained even though the configuration is now effective. Recommended\n\t\t// pattern: mount per-node guards on the target BEFORE wrapping.\n\t\t// Reactive recompute (subscribe to target.topology, clear lint when a\n\t\t// guard appears) is filed in `docs/optimizations.md` as a follow-up.\n\t\tif (this._mode === \"audit\") {\n\t\t\tconst described = target.describe({ detail: \"full\" });\n\t\t\tconst anyGuard = Object.values(described.nodes).some((n) => n.guard != null);\n\t\t\tif (!anyGuard) {\n\t\t\t\tthis._fireLint(\n\t\t\t\t\t\"audit-no-effect\",\n\t\t\t\t\t'`mode: \"audit\"` + target has no per-node guards — `scopedDescribeNode` filters by per-node guards only, so policy rules will not affect describe() visibility. Policies still populate the `violations` topic on writes.',\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\n\t\t// No-actor lint: fires at construction when neither configured nor\n\t\t// implicitly carried via a Node-cache. Per-call overrides on\n\t\t// `scopedDescribeNode` cannot retroactively suppress this — the\n\t\t// configured-default branch is what callers most often miss.\n\t\tif (actorOpt == null) {\n\t\t\tthis._fireLint(\n\t\t\t\t\"no-actor\",\n\t\t\t\t\"no actor configured — `wrapper.scopedDescribe` and `scopedDescribeNode()` will return an unscoped describe (full visibility) unless a per-call actor is supplied.\",\n\t\t\t);\n\t\t}\n\n\t\t// Scope derived: live tuple of {actor, mode, policiesCount}.\n\t\tthis.scope = node<GuardedScope>(\n\t\t\t[this._actorNode, this.enforcer.policies],\n\t\t\t(batchData, actions, ctx) => {\n\t\t\t\tconst data = batchData.map((batch, i) =>\n\t\t\t\t\tbatch != null && batch.length > 0 ? batch.at(-1) : ctx.prevData[i],\n\t\t\t\t);\n\t\t\t\tactions.emit({\n\t\t\t\t\tactor: (data[0] as Actor | null | undefined) ?? null,\n\t\t\t\t\tmode: this._mode,\n\t\t\t\t\tpoliciesCount: (data[1] as readonly PolicyRuleData[]).length,\n\t\t\t\t});\n\t\t\t},\n\t\t\t{\n\t\t\t\tname: \"scope\",\n\t\t\t\tdescribeKind: \"derived\",\n\t\t\t\tmeta: guardedMeta(\"scope\"),\n\t\t\t},\n\t\t);\n\t\tthis.add(this.scope, { name: \"scope\" });\n\t\tthis.addDisposer(keepalive(this.scope));\n\n\t\t// qa G1A \"same concept\": single canonical reactive describe bound to\n\t\t// the configured actor, mounted once at construction. Removes the\n\t\t// per-call handle proliferation from the prior `scopedDescribeNode`\n\t\t// pattern (the method is retained as the per-call escape hatch). The\n\t\t// reactive describe re-derives on actor swap / topology change /\n\t\t// status transition; lifecycle owned by the wrapper.\n\t\tconst scopedHandle = target.describe({\n\t\t\treactive: true,\n\t\t\t// F8 (Tier 5.2): `_actorNode` is `Node<Actor | null>`. The cast is\n\t\t\t// safe at runtime per the `resolveActorOption` null-tolerance\n\t\t\t// contract — `null` / `undefined` cache is treated as \"no scoping\"\n\t\t\t// (full visibility). See `scopedDescribeNode` for the matching\n\t\t\t// per-call site and graph.ts § \"Cache-undefined semantics\".\n\t\t\tactor: this._actorNode as Node<Actor>,\n\t\t\treactiveName: \"scopedDescribe\",\n\t\t});\n\t\tthis.scopedDescribe = scopedHandle.node;\n\t\tthis.add(this.scopedDescribe, { name: \"scopedDescribe\" });\n\t\tthis.addDisposer(scopedHandle.dispose);\n\t}\n\n\tprivate _fireLint(kind: GuardedExecutionLint[\"kind\"], message: string): void {\n\t\tif (this._firedLintKinds.has(kind)) return;\n\t\tthis._firedLintKinds.add(kind);\n\t\tthis.lints.publish({ kind, message, timestamp_ns: monotonicNs() });\n\t}\n\n\t/**\n\t * **Per-call escape hatch.** Prefer {@link scopedDescribe} (the mounted\n\t * property) for the common case of \"describe scoped to my actor.\" Use\n\t * this method ONLY when you need a per-call actor override or different\n\t * `detail`/`fields`/`filter`.\n\t *\n\t * Returns a live `Node<GraphDescribeOutput>` scoped to the supplied (or\n\t * configured) actor, plus an explicit `dispose` for caller-controlled\n\t * lifecycle. Re-derives on every settle of the target graph: structural\n\t * changes, status transitions, and actor emissions (when a `Node<Actor>`\n\t * is bound).\n\t *\n\t * **Lifecycle (qa G1A — EC1 fix).** Each call instantiates a fresh\n\t * `target.describe({reactive: true})` handle (with its own version state,\n\t * observe handle, transitive topology subscriptions, derived + keepalive).\n\t * The caller MUST invoke the returned `dispose()` when finished to release\n\t * these resources. Disposers ARE also tracked on the wrapper graph so\n\t * `wrapper.destroy()` cleans up any handles the caller forgot — but a\n\t * long-lived wrapper with heavy per-call usage will leak until destroy\n\t * unless `dispose()` is called explicitly.\n\t *\n\t * @param actorOverride - Optional per-call override. Static {@link Actor}\n\t * or `Node<Actor>`. Omit to use the wrapper-configured default.\n\t * @param opts - Standard {@link GraphDescribeOptions} fields (`detail`,\n\t * `fields`, `filter`). `actor` / `reactive` / `reactiveName` are\n\t * controlled by the wrapper.\n\t * @returns `{node, dispose}` — `node` is the live describe Node; `dispose`\n\t * tears down the underlying reactive describe subscription idempotently.\n\t */\n\tscopedDescribeNode(\n\t\tactorOverride?: Actor | Node<Actor>,\n\t\topts?: Omit<GraphDescribeOptions, \"actor\" | \"reactive\" | \"reactiveName\">,\n\t): { node: Node<GraphDescribeOutput>; dispose: () => void } {\n\t\tconst actorNode =\n\t\t\tactorOverride == null\n\t\t\t\t? this._actorNode\n\t\t\t\t: isNode<Actor>(actorOverride)\n\t\t\t\t\t? actorOverride\n\t\t\t\t\t: node<Actor>([], { name: \"actor_override\", initial: actorOverride });\n\t\tconst handle = this._target.describe({\n\t\t\treactive: true,\n\t\t\t// `_actorNode` is `Node<Actor | null>`. The `as Node<Actor>` cast is\n\t\t\t// safe at runtime: `_describeReactive` resolves the actor via\n\t\t\t// `resolveActorOption`, which treats `null`/`undefined` cache as\n\t\t\t// \"no scoping\" (full visibility). Documented in graph.ts §\n\t\t\t// \"Cache-undefined semantics.\"\n\t\t\tactor: actorNode as Node<Actor>,\n\t\t\t...(opts ?? {}),\n\t\t});\n\t\t// Track on the wrapper as a safety net for callers who forget to\n\t\t// dispose; explicit `dispose()` is still the canonical lifecycle path.\n\t\tthis.addDisposer(handle.dispose);\n\t\treturn { node: handle.node, dispose: handle.dispose };\n\t}\n\n\t/** The wrapped graph (escape hatch for tooling). */\n\tget target(): Graph {\n\t\treturn this._target;\n\t}\n}\n\n/**\n * Wrap a {@link Graph} with {@link policyGate} plus a reactive scoped describe\n * lens. Returns a {@link GuardedExecutionGraph} that can be mounted, diffed,\n * or composed with {@link graphLens}.\n *\n * @param target - The graph to guard.\n * @param opts - See {@link GuardedExecutionOptions}.\n *\n * @example\n * ```ts\n * const guarded = guardedExecution(app, {\n * actor: node<Actor>([], { initial: { type: \"human\", id: \"alice\" } }), // reactive — re-derive on swap\n * policies: [\n * { effect: \"allow\", action: \"read\", actorType: \"human\" },\n * { effect: \"deny\", action: \"write\", pathPattern: \"system::*\" },\n * ],\n * mode: \"enforce\",\n * });\n *\n * // Canonical: subscribe to the mounted reactive describe (no per-call leak).\n * guarded.scopedDescribe.subscribe((msgs) => { /* live describe per actor / topology change *\\/ });\n * // Per-call escape hatch (different actor / detail) — caller manages dispose.\n * const detailed = guarded.scopedDescribeNode(undefined, { detail: \"standard\" });\n * try { detailed.node.subscribe(/* … *\\/); } finally { detailed.dispose(); }\n * guarded.violations.events.subscribe(msgs => console.log(\"violations:\", msgs));\n * guarded.lints.events.subscribe(msgs => console.warn(\"lints:\", msgs));\n * guarded.scope.subscribe(msgs => console.log(\"scope:\", msgs));\n * ```\n *\n * @category patterns\n */\nexport function guardedExecution(\n\ttarget: Graph,\n\topts: GuardedExecutionOptions,\n): GuardedExecutionGraph {\n\treturn new GuardedExecutionGraph(target, opts);\n}\n\n// Re-export types useful for call sites.\nexport type { DescribeFilter };\n"],"mappings":"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","names":["import_core","import_graph","import_core","import_extra","import_graph","import_core","import_extra","import_graph","batch","batch","node","import_core","import_extra","import_graph","import_graph","batch","import_core","import_extra","import_graph","isNode","batch"]}
1
+ {"version":3,"sources":["../../../src/presets/inspect/index.ts","../../../src/presets/inspect/composite.ts","../../../src/utils/inspect/audit.ts","../../../src/base/meta/domain-meta.ts","../../../src/utils/messaging/index.ts","../../../src/base/mutation/index.ts","../../../src/utils/inspect/lens.ts","../../../src/presets/inspect/guarded-execution.ts"],"sourcesContent":["/**\n * Inspect presets — opinionated compositions of inspect utils.\n *\n * @module\n */\n\nexport * from \"./composite.js\";\nexport * from \"./guarded-execution.js\";\n","/**\n * `inspect()` preset — Tier 9.1 γ-form γ-II + Q5-6 medium scope.\n *\n * Composes graph observability into a single mounted facade:\n * - `lens` (mounted as a `LensSubgraph` child) — exposes `topology` /\n * `health` / `flow` Nodes via `inspect.lens.*`.\n * - `audit` (mounted `AuditTrailGraph`) — every mutation on the wrapped\n * target captured as an audit entry.\n * - `explainTarget(from, to, opts?)` — facade over\n * `target.describe({ explain: {...} })`; supports both static and\n * reactive forms.\n * - `complianceSnapshot()` — one-shot tamper-evident snapshot pairing the\n * target's persisted state with the audit log.\n *\n * **Path-namespace boundary.** `inspect.describe()` shows InspectGraph's\n * OWN topology (the lens + audit subgraphs and any caller-added siblings) —\n * NOT the wrapped target's topology. Use `inspect.target.describe()` to\n * walk the target. `inspect.node(\"counter\")` resolves under the inspect\n * graph, NOT the target.\n *\n * **Why a Graph subclass.** Per Tier 9.1 γ-II lock: closure-bundled returns\n * (`{lens, audit, explain, ...}` of independently-constructed primitives)\n * hide topology — `describe()` from the wrapper can't walk into the\n * audit/lens pieces. Mounting them under a real `InspectGraph` keeps every\n * contained primitive visible in describe / explain across the boundary,\n * which is the point of the inspect preset.\n *\n * **Why the lens lives in a child `LensSubgraph` mount.** `Graph.destroy()`\n * signals TEARDOWN through `this._nodes` after disposers drain. If the\n * lens nodes were `add()`ed directly to InspectGraph's path table, they\n * would receive TEARDOWN at parent destroy — invalidating any externally\n * held `view.lens.topology.subscribe(...)` reference. Mounting via a child\n * subgraph contains the cascade: inspect's TEARDOWN visits the lens\n * subgraph's nodes through `mount → child._destroyClearOnly`, which clears\n * structure but does NOT broadcast TEARDOWN. The `lens.dispose()` disposer\n * still tears down the underlying observe handle as designed (D1 fix per\n * /qa lock).\n *\n * **Why `inspect()` mounts `graphLens` rather than rebuilding `health` / `flow`.**\n * Per Q3 yellow lock: rebuilding would duplicate the `topology → health`\n * and `dataFlow → flow` deriveds, doubling subscription cost and risking\n * semantic drift. Mounting graphLens once lets `inspect.lens` and the\n * standalone `graphLens(target)` factory share a single source of truth.\n *\n * @module\n */\n\nimport type { Actor, Node } from \"@graphrefly/pure-ts/core\";\nimport { placeholderArgs } from \"@graphrefly/pure-ts/core\";\nimport { type CausalChain, Graph } from \"@graphrefly/pure-ts/graph\";\nimport {\n\ttype AuditTrailGraph,\n\ttype AuditTrailOptions,\n\tauditTrail,\n\ttype ComplianceSnapshotResult,\n\tcomplianceSnapshot,\n\ttype PolicyGateGraph,\n} from \"../../utils/inspect/audit.js\";\nimport { type GraphLensView, graphLens } from \"../../utils/inspect/lens.js\";\n\n/** Options for {@link inspect}. */\nexport interface InspectOptions {\n\t/** Default actor recorded on `complianceSnapshot()` calls. */\n\tactor?: Actor;\n\t/**\n\t * Forwarded to the mounted {@link auditTrail} so callers can configure\n\t * retention / inclusion policy. Pre-allocated to keep `inspect()`\n\t * callable with no opts.\n\t */\n\taudit?: AuditTrailOptions;\n\t/** Optional name override for the `InspectGraph` itself. */\n\tname?: string;\n}\n\n/**\n * Thin Graph-subclass shell that owns the `graphLens(target)` Nodes so\n * inspect's TEARDOWN cascade reaches them via `_destroyClearOnly` (which\n * clears structure WITHOUT broadcasting TEARDOWN) instead of via\n * `signal([[TEARDOWN]])` (which DOES broadcast and would invalidate any\n * externally held lens-node subscription).\n *\n * @internal\n */\nclass LensSubgraph extends Graph {\n\treadonly view: GraphLensView;\n\n\tconstructor(target: Graph) {\n\t\tsuper(\"lens\");\n\t\tthis.view = graphLens(target);\n\t\tthis.add(this.view.topology, { name: \"topology\" });\n\t\tthis.add(this.view.health, { name: \"health\" });\n\t\tthis.add(this.view.flow, { name: \"flow\" });\n\t}\n}\n\n/**\n * Graph subclass returned by {@link inspect}. Mounts a `graphLens` view (as\n * a child `LensSubgraph`), an `auditTrail` (as a child subgraph), and\n * exposes `explainTarget()` + `complianceSnapshot()` facades over the\n * wrapped target.\n *\n * Mounted children (visible in `describe()`):\n * - `lens::topology` / `lens::health` / `lens::flow` — `graphLens(target)` Nodes.\n * - `audit::*` — the mounted {@link AuditTrailGraph}.\n *\n * @category observability\n */\nexport class InspectGraph extends Graph {\n\treadonly target: Graph;\n\t/**\n\t * Underlying lens view — reach individual Nodes via\n\t * `inspect.lens.topology` / `inspect.lens.health` / `inspect.lens.flow`.\n\t *\n\t * Direct `inspect.topology` / `inspect.health` / `inspect.flow`\n\t * accessors are NOT shipped because `Graph.topology` is already an\n\t * accessor on the base class with a different shape (`Node<TopologyEvent>`\n\t * — the mount/unmount stream of THIS graph, not the wrapped target's\n\t * describe snapshot). Going through `.lens.*` keeps the two concepts\n\t * cleanly separated.\n\t */\n\treadonly lens: GraphLensView;\n\t/** Mounted audit trail subgraph. */\n\treadonly audit: AuditTrailGraph;\n\n\tprivate readonly _defaultActor?: Actor;\n\tprivate readonly _lensSubgraph: LensSubgraph;\n\n\tconstructor(target: Graph, opts: InspectOptions = {}) {\n\t\tsuper(opts.name ?? `inspect(${target.name})`);\n\t\tthis.target = target;\n\t\tthis._defaultActor = opts.actor;\n\n\t\t// D1 (qa lock): lens lives inside a child mount so `inspect.destroy()`'s\n\t\t// TEARDOWN signal cascade reaches the lens nodes via\n\t\t// `_destroyClearOnly` (no broadcast) rather than via `_signalDeliver`\n\t\t// over `inspect._nodes` (which WOULD broadcast). External holders of\n\t\t// `view.lens.topology.subscribe(...)` see only the `lens.dispose()`\n\t\t// teardown of the underlying observe handle, not a stray TEARDOWN\n\t\t// from inspect's path table.\n\t\tthis._lensSubgraph = new LensSubgraph(target);\n\t\tthis.lens = this._lensSubgraph.view;\n\t\tthis.mount(\"lens\", this._lensSubgraph);\n\n\t\tthis.audit = auditTrail(target, opts.audit ?? {});\n\t\tthis.mount(\"audit\", this.audit);\n\n\t\t// Tear down the lens's underlying observe subscription on destroy.\n\t\t// The mounted subgraphs themselves tear down via mount lifecycle.\n\t\tthis.addDisposer(() => this.lens.dispose());\n\t}\n\n\t/**\n\t * Causal-chain facade over `target.describe({ explain: {...} })`. Supports\n\t * both static (one-shot {@link CausalChain}) and reactive\n\t * (`{ reactive: true }`) forms.\n\t *\n\t * Named `explainTarget` (not folded into a `describe` mode on this class)\n\t * because `inspect.describe(...)` walks `InspectGraph`'s OWN topology\n\t * (lens + audit subgraphs) rather than the wrapped target's. Use\n\t * `inspect.explainTarget(...)` for chains across the wrapped graph;\n\t * `inspect.describe({ explain: {...} })` for chains across the lens /\n\t * audit composition.\n\t */\n\texplainTarget(\n\t\tfrom: string | Node<string>,\n\t\tto: string | Node<string>,\n\t\topts?: {\n\t\t\tmaxDepth?: number | Node<number>;\n\t\t\tfindCycle?: boolean | Node<boolean>;\n\t\t},\n\t): CausalChain;\n\texplainTarget(\n\t\tfrom: string | Node<string>,\n\t\tto: string | Node<string>,\n\t\topts: {\n\t\t\treactive: true;\n\t\t\tmaxDepth?: number | Node<number>;\n\t\t\tfindCycle?: boolean | Node<boolean>;\n\t\t\tname?: string;\n\t\t},\n\t): { node: Node<CausalChain>; dispose: () => void };\n\texplainTarget(\n\t\tfrom: string | Node<string>,\n\t\tto: string | Node<string>,\n\t\topts?: {\n\t\t\treactive?: boolean;\n\t\t\tmaxDepth?: number | Node<number>;\n\t\t\tfindCycle?: boolean | Node<boolean>;\n\t\t\tname?: string;\n\t\t},\n\t): CausalChain | { node: Node<CausalChain>; dispose: () => void } {\n\t\t// Cast through the discriminated overload — TypeScript can't pick a\n\t\t// signature on `target.describe({explain})` because `opts` has the\n\t\t// union shape (reactive: boolean | undefined).\n\t\tconst explainArg: {\n\t\t\tfrom: string | Node<string>;\n\t\t\tto: string | Node<string>;\n\t\t\tmaxDepth?: number | Node<number>;\n\t\t\tfindCycle?: boolean | Node<boolean>;\n\t\t} = { from, to };\n\t\tif (opts?.maxDepth !== undefined) explainArg.maxDepth = opts.maxDepth;\n\t\tif (opts?.findCycle !== undefined) explainArg.findCycle = opts.findCycle;\n\t\tconst describeOpts: Record<string, unknown> = { explain: explainArg };\n\t\tif (opts?.reactive === true) describeOpts.reactive = true;\n\t\tif (opts?.name !== undefined) describeOpts.name = opts.name;\n\t\treturn (\n\t\t\tthis.target.describe as unknown as (\n\t\t\t\to: typeof describeOpts,\n\t\t\t) => CausalChain | { node: Node<CausalChain>; dispose: () => void }\n\t\t)(describeOpts);\n\t}\n\n\t/**\n\t * One-shot tamper-evident snapshot pairing the target's persisted state\n\t * with the audit log + (optional) policy-gate violations.\n\t *\n\t * Uses the inspect's mounted `audit` by default; pair with a separate\n\t * `policyGate` (mounted elsewhere) by passing `policies` explicitly.\n\t *\n\t * **Cryptographic strength caveat (echoed from {@link complianceSnapshot}):**\n\t * the returned `fingerprint` is a truncated SHA-256 (16 hex chars / ~64\n\t * bits) optimized for compact archival. Sufficient for casual integrity\n\t * checks and content-addressed dedup; for adversarial tamper-evidence,\n\t * pair with a full SHA-256 over the canonical JSON externally.\n\t */\n\tcomplianceSnapshot(opts?: {\n\t\tactor?: Actor;\n\t\tpolicies?: PolicyGateGraph;\n\t}): ComplianceSnapshotResult {\n\t\tconst actor = opts?.actor ?? this._defaultActor;\n\t\treturn complianceSnapshot(this.target, {\n\t\t\taudit: this.audit,\n\t\t\t...(actor != null ? { actor } : {}),\n\t\t\t...(opts?.policies != null ? { policies: opts.policies } : {}),\n\t\t});\n\t}\n}\n\n/**\n * Build an {@link InspectGraph} that mounts `graphLens` + `auditTrail` over\n * the wrapped target and exposes `explainTarget()` + `complianceSnapshot()`\n * facades.\n *\n * @example\n * ```ts\n * import { inspect } from \"@graphrefly/graphrefly/presets/inspect\";\n *\n * const target = buildMyApp();\n * const view = inspect(target, { actor: { id: \"ops-bot\", role: \"monitor\" } });\n *\n * // Live observability\n * view.lens.health.subscribe((msgs) => console.log(\"health:\", msgs));\n * view.lens.flow.subscribe((msgs) => console.log(\"flow:\", msgs));\n *\n * // Causal explainability across the wrapped target\n * const chain = view.explainTarget(\"input\", \"output\");\n *\n * // Tamper-evident snapshot for archival\n * const snapshot = view.complianceSnapshot();\n * ```\n *\n * @category observability\n */\nexport function inspect(target: Graph, opts: InspectOptions = {}): InspectGraph {\n\tconst g = new InspectGraph(target, opts);\n\t// A1 (qa lock): self-tag so `inspect.describe().factory === \"inspect\"`,\n\t// matching the policyGate / pipelineGraph / harnessLoop precedent.\n\tg.tagFactory(\"inspect\", placeholderArgs(opts as unknown as Record<string, unknown>));\n\treturn g;\n}\n","/**\n * Audit, policy enforcement, and compliance export (roadmap §9.2).\n *\n * Three composed factories that wrap any {@link Graph} with the harness\n * accountability layer:\n *\n * - {@link auditTrail} — reactive mutation log with by-node/by-actor/by-time\n * queries.\n * - {@link policyGate} — reactive ABAC gate (Tier 2.3 rename of\n * `policyEnforcer`); in `\"audit\"` mode records would-be denials, in\n * `\"enforce\"` mode pushes guards onto target nodes so subsequent writes\n * throw {@link GuardDenied}.\n * - {@link complianceSnapshot} — point-in-time export of graph state +\n * audit trail + policies for regulatory archival.\n *\n * @module\n */\nimport type { Actor, GuardAction, NodeGuard, PolicyRuleData } from \"@graphrefly/pure-ts/core\";\nimport {\n\tbatch,\n\tDATA,\n\tdefaultHash,\n\tmonotonicNs,\n\ttype Node,\n\tNodeImpl,\n\tnode,\n\tplaceholderArgs,\n\tpolicyFromRules,\n\twallClockNs,\n} from \"@graphrefly/pure-ts/core\";\nimport { keepalive, reactiveLog } from \"@graphrefly/pure-ts/extra\";\nimport {\n\tGraph,\n\ttype GraphOptions,\n\ttype GraphPersistSnapshot,\n\ttype TopologyEvent,\n\twatchTopologyTree,\n} from \"@graphrefly/pure-ts/graph\";\nimport { domainMeta } from \"../../base/meta/domain-meta.js\";\nimport { TopicGraph } from \"../messaging/index.js\";\n\n// ---------------------------------------------------------------------------\n// Shared types\n// ---------------------------------------------------------------------------\n\n/** A single recorded mutation/event in an {@link AuditTrailGraph}. */\nexport interface AuditEntry {\n\tseq: number;\n\ttimestamp_ns: number;\n\twall_clock_ns: number;\n\tpath: string;\n\ttype:\n\t\t| \"data\"\n\t\t| \"dirty\"\n\t\t| \"resolved\"\n\t\t| \"invalidate\"\n\t\t| \"pause\"\n\t\t| \"resume\"\n\t\t| \"complete\"\n\t\t| \"error\"\n\t\t| \"teardown\";\n\tactor?: Actor;\n\tvalue?: unknown;\n\terror?: unknown;\n\tannotation?: string;\n}\n\nfunction auditMeta(kind: string, extra?: Record<string, unknown>): Record<string, unknown> {\n\treturn domainMeta(\"audit\", kind, extra);\n}\n\n// ---------------------------------------------------------------------------\n// auditTrail\n// ---------------------------------------------------------------------------\n\nconst DEFAULT_INCLUDE_TYPES: ReadonlySet<AuditEntry[\"type\"]> = new Set([\n\t\"data\",\n\t\"error\",\n\t\"complete\",\n\t\"teardown\",\n]);\n\n/** Options for {@link auditTrail}. */\nexport interface AuditTrailOptions {\n\tname?: string;\n\tgraph?: GraphOptions;\n\t/** Ring-buffer cap for the underlying `reactiveLog`. Default: unbounded. */\n\tmaxSize?: number;\n\t/**\n\t * Which event types to record. Default: `[\"data\", \"error\", \"complete\",\n\t * \"teardown\"]` — the user-meaningful set. Opt in to mid-wave protocol\n\t * events (`\"dirty\"`, `\"resolved\"`, `\"invalidate\"`, `\"pause\"`, `\"resume\"`)\n\t * by listing them explicitly. Note: those tier-1/tier-2 events do not\n\t * carry an `actor` (no `lastMutation` populated) — record them only for\n\t * protocol-level diagnostics.\n\t */\n\tincludeTypes?: readonly AuditEntry[\"type\"][];\n\t/** Per-event filter; return false to skip. */\n\tfilter?: (entry: AuditEntry) => boolean;\n}\n\n/**\n * Mounted audit log — `entries` exposes the reactive `AuditEntry[]`; query\n * helpers are sync convenience wrappers over the cached snapshot.\n */\nexport class AuditTrailGraph extends Graph {\n\treadonly entries: Node<readonly AuditEntry[]>;\n\treadonly count: Node<number>;\n\t/**\n\t * Effective set of event types this trail records (EH-18). Reflects\n\t * either the caller-supplied `opts.includeTypes` or the default set\n\t * (`[\"data\", \"error\", \"complete\", \"teardown\"]`). Captured at construction\n\t * — each instance owns its own clone, so a default-using trail can never\n\t * leak mutations into the module-level default set.\n\t *\n\t * **Mutation contract.** Type-system read-only via `ReadonlySet`. Runtime\n\t * mutation through an unsafe cast (`(audit.includeTypes as Set<...>)\n\t * .add(...)`) is unsupported — it would desync the field from the\n\t * recording closure, which captured the original `Set` reference at\n\t * construction. The runtime does NOT enforce immutability beyond the\n\t * type contract; consumers must respect it.\n\t *\n\t * Use this to validate that a `complianceSnapshot.fingerprint` was\n\t * computed against the same recording surface — fingerprints are stable\n\t * only when the recording set is identical across snapshots.\n\t */\n\treadonly includeTypes: ReadonlySet<AuditEntry[\"type\"]>;\n\tprivate readonly _log;\n\tprivate readonly _target: Graph;\n\n\tconstructor(target: Graph, opts: AuditTrailOptions) {\n\t\tsuper(opts.name ?? `${target.name}_audit`, opts.graph);\n\t\tthis._target = target;\n\t\tthis._log = reactiveLog<AuditEntry>([], {\n\t\t\tname: \"entries\",\n\t\t\t...(opts.maxSize != null ? { maxSize: opts.maxSize } : {}),\n\t\t});\n\t\tthis.entries = this._log.entries;\n\t\tthis.add(this.entries, { name: \"entries\" });\n\n\t\tthis.count = this.derived<number>(\n\t\t\t\"count\",\n\t\t\t[\"entries\"],\n\t\t\t(batchData, ctx) => {\n\t\t\t\tconst data = batchData.map((batch, i) =>\n\t\t\t\t\tbatch != null && batch.length > 0 ? batch.at(-1) : ctx.prevData[i],\n\t\t\t\t);\n\t\t\t\treturn [(data[0] as readonly AuditEntry[]).length];\n\t\t\t},\n\t\t\t{ meta: auditMeta(\"count\") },\n\t\t);\n\t\tthis.addDisposer(keepalive(this.count));\n\n\t\t// Always clone — DEFAULT_INCLUDE_TYPES is a module-level singleton and\n\t\t// must not be shared across instances (a cast-and-mutate via\n\t\t// `audit.includeTypes` would otherwise corrupt every default audit\n\t\t// trail in the process).\n\t\tconst includeTypes: Set<AuditEntry[\"type\"]> =\n\t\t\topts.includeTypes != null ? new Set(opts.includeTypes) : new Set(DEFAULT_INCLUDE_TYPES);\n\t\tthis.includeTypes = includeTypes;\n\t\tconst filter = opts.filter;\n\n\t\t// Monotonic per-trail. **Stagnates** (does not wrap) past\n\t\t// `Number.MAX_SAFE_INTEGER` — IEEE-754 imprecision means `seq + 1 === seq`\n\t\t// once `seq` exceeds 2^53; subsequent records would carry the same\n\t\t// stagnant value and break uniqueness. At 100k events/sec that's\n\t\t// ~3000 years — not a practical concern.\n\t\tlet seq = 0;\n\t\tconst handle = target.observe({ timeline: true, structured: true });\n\t\tconst offEvent = handle.onEvent((event) => {\n\t\t\t// `event.type` includes \"derived\" (causal-trace recompute marker) which\n\t\t\t// isn't a recordable mutation — skip it. Cast through narrowed type\n\t\t\t// after the discriminator check.\n\t\t\tif (event.type === \"derived\") return;\n\t\t\tconst type = event.type as AuditEntry[\"type\"];\n\t\t\tif (!includeTypes.has(type)) return;\n\t\t\tconst path = event.path ?? \"\";\n\t\t\tconst entry: AuditEntry = {\n\t\t\t\tseq: seq++,\n\t\t\t\ttimestamp_ns: event.timestamp_ns ?? monotonicNs(),\n\t\t\t\twall_clock_ns: wallClockNs(),\n\t\t\t\tpath,\n\t\t\t\ttype,\n\t\t\t};\n\t\t\t// Attribution + value enrichment.\n\t\t\tconst node = path ? safeNode(target, path) : undefined;\n\t\t\tconst lastMutation = node?.lastMutation;\n\t\t\tif (lastMutation != null) entry.actor = lastMutation.actor;\n\t\t\tif (type === \"data\") entry.value = (event as { data: unknown }).data;\n\t\t\tif (type === \"error\") entry.error = (event as { data: unknown }).data;\n\t\t\tconst annotation = path ? safeAnnotation(target, path) : undefined;\n\t\t\tif (annotation != null) entry.annotation = annotation;\n\t\t\tif (filter != null && !filter(entry)) return;\n\t\t\tthis._log.append(entry);\n\t\t});\n\n\t\tthis.addDisposer(() => {\n\t\t\toffEvent();\n\t\t\thandle.dispose();\n\t\t});\n\t\tthis.addDisposer(() => this._log.disposeAllViews());\n\t}\n\n\t/** All entries currently in the ring (snapshot). */\n\tall(): readonly AuditEntry[] {\n\t\treturn (this.entries.cache as readonly AuditEntry[] | undefined) ?? [];\n\t}\n\n\t/** Entries matching `path`. Order preserved. */\n\tbyNode(path: string): readonly AuditEntry[] {\n\t\treturn this.all().filter((e) => e.path === path);\n\t}\n\n\t/** Entries whose `actor.id` matches. Use `byActorType` for type filtering. */\n\tbyActor(actorId: string): readonly AuditEntry[] {\n\t\treturn this.all().filter((e) => e.actor?.id === actorId);\n\t}\n\n\t/** Entries whose `actor.type` matches (e.g. `\"llm\"`, `\"human\"`). */\n\tbyActorType(type: string): readonly AuditEntry[] {\n\t\treturn this.all().filter((e) => e.actor?.type === type);\n\t}\n\n\t/**\n\t * Entries with `timestamp_ns` in `[start_ns, end_ns)` (end exclusive).\n\t * Omit `end_ns` to query open-ended.\n\t */\n\tbyTimeRange(start_ns: number, end_ns?: number): readonly AuditEntry[] {\n\t\treturn this.all().filter((e) => {\n\t\t\tif (e.timestamp_ns < start_ns) return false;\n\t\t\tif (end_ns != null && e.timestamp_ns >= end_ns) return false;\n\t\t\treturn true;\n\t\t});\n\t}\n\n\t/** Reference to the audited graph (escape hatch for tooling). */\n\tget target(): Graph {\n\t\treturn this._target;\n\t}\n}\n\n/**\n * Wraps any {@link Graph} with a reactive audit trail recording every event\n * matching `includeTypes` (default: data + error + complete + teardown).\n *\n * Each entry carries `seq`, `timestamp_ns` (monotonic), `wall_clock_ns`,\n * `path`, `type`, and — when available — `actor`, `value`, `error`, and the\n * `graph.trace()` reasoning annotation for the path.\n *\n * The returned graph mounts an `entries` node + `count` derived. Query\n * helpers (`byNode`, `byActor`, `byTimeRange`) operate on the cached\n * snapshot synchronously.\n */\nexport function auditTrail(target: Graph, opts: AuditTrailOptions = {}): AuditTrailGraph {\n\treturn new AuditTrailGraph(target, opts);\n}\n\n// ---------------------------------------------------------------------------\n// policyGate (renamed from `policyEnforcer` per Tier 2.3 — joins the\n// gate-family disambiguation: `valve` (boolean) / `budgetGate` (numeric) /\n// `approvalGate` (human judgment) / `policyGate` (ABAC rules))\n// ---------------------------------------------------------------------------\n\n/** A single policy denial recorded by {@link PolicyGateGraph}. */\nexport interface PolicyViolation {\n\ttimestamp_ns: number;\n\twall_clock_ns: number;\n\tpath: string;\n\tactor: Actor;\n\taction: GuardAction;\n\tmode: \"audit\" | \"enforce\";\n\t/** `\"observed\"` (audit mode after-the-fact) or `\"blocked\"` (enforce mode pre-write). */\n\tresult: \"observed\" | \"blocked\";\n}\n\n/** Options for {@link policyGate}. */\nexport interface PolicyGateOptions {\n\tname?: string;\n\tgraph?: GraphOptions;\n\t/**\n\t * `\"audit\"` (default) — observe events and record would-be denials;\n\t * does not block writes. Audit mode requires `lastMutation` attribution\n\t * on the audited node — anonymous/internal writes (no `actor` passed,\n\t * unguarded node) are skipped silently because the policy cannot be\n\t * evaluated without an actor.\n\t *\n\t * `\"enforce\"` — push guards onto target nodes so disallowed writes\n\t * throw {@link GuardDenied}. Reverted on dispose.\n\t */\n\tmode?: \"audit\" | \"enforce\";\n\t/**\n\t * Restrict enforcement to specific node paths (qualified). When omitted,\n\t * applies to every node visible in `target.describe()` at construction\n\t * time (subgraphs are walked transitively) AND subscribes to the full\n\t * topology tree via {@link watchTopologyTree}, so nodes added to\n\t * `target` OR any transitively-mounted subgraph after construction are\n\t * guarded automatically (enforce mode only).\n\t *\n\t * Accepts a static `readonly string[]` or a reactive\n\t * `Node<readonly string[]>` (Tier 3.4 — F.9 reactive primitive carve-out).\n\t * When a `Node` is passed, the enforcer rebinds the guarded path set on\n\t * every emission: paths added to the new set get wrapped, paths removed\n\t * from the new set get released, and the audit-mode allow-list filter\n\t * uses the latest cached value. Static-array callers retain the current\n\t * \"caller owns the path set\" semantics.\n\t *\n\t * **Cost:** unrestricted mode runs `describe({detail:\"minimal\"})` once\n\t * at construction (O(N) over the graph tree) plus one topology\n\t * subscription per graph instance in the mount tree. Restricted mode\n\t * (static or reactive) skips both and disables `watchTopologyTree`\n\t * dynamic coverage — for reactive callers, the path-set Node is the\n\t * single source of truth for which paths are guarded.\n\t */\n\tpaths?: readonly string[] | Node<readonly string[]>;\n\t/**\n\t * Ring-buffer cap for the violations topic. Default: 1000. Static\n\t * number only — reactive form is deferred pending TopicGraph reactive\n\t * `retainedLimit` support (see Tier 10.8 design follow-up in\n\t * `docs/optimizations.md`).\n\t */\n\tviolationsLimit?: number;\n}\n\n/**\n * Reactive ABAC enforcement layer. Policies are reactive — pass a\n * `Node<readonly PolicyRuleData[]>` to allow LLMs (or any reactive source)\n * to update them at runtime; the enforcer rebinds its internal\n * {@link NodeGuard} on every push.\n */\nexport class PolicyGateGraph extends Graph {\n\treadonly policies: Node<readonly PolicyRuleData[]>;\n\treadonly violations: TopicGraph<PolicyViolation>;\n\treadonly violationCount: Node<number>;\n\tprivate readonly _target: Graph;\n\tprivate readonly _mode: \"audit\" | \"enforce\";\n\tprivate _currentGuard: NodeGuard;\n\n\tconstructor(\n\t\ttarget: Graph,\n\t\tpolicies: readonly PolicyRuleData[] | Node<readonly PolicyRuleData[]>,\n\t\topts: PolicyGateOptions,\n\t) {\n\t\tsuper(opts.name ?? `${target.name}_policy`, opts.graph);\n\t\tthis._target = target;\n\t\tthis._mode = opts.mode ?? \"audit\";\n\n\t\tconst policiesNode = isNode(policies)\n\t\t\t? policies\n\t\t\t: node<readonly PolicyRuleData[]>([], { name: \"policies\", initial: policies });\n\t\tthis.policies = policiesNode;\n\t\tthis.add(this.policies, { name: \"policies\" });\n\n\t\tthis.violations = new TopicGraph<PolicyViolation>(\"violations\", {\n\t\t\tretainedLimit: opts.violationsLimit ?? 1000,\n\t\t});\n\t\tthis.mount(\"violations\", this.violations);\n\n\t\tthis.violationCount = this.derived<number>(\n\t\t\t\"violationCount\",\n\t\t\t[\"violations::events\"],\n\t\t\t(batchData, ctx) => {\n\t\t\t\tconst data = batchData.map((batch, i) =>\n\t\t\t\t\tbatch != null && batch.length > 0 ? batch.at(-1) : ctx.prevData[i],\n\t\t\t\t);\n\t\t\t\treturn [(data[0] as readonly PolicyViolation[]).length];\n\t\t\t},\n\t\t\t{\n\t\t\t\tmeta: auditMeta(\"policy_violation_count\"),\n\t\t\t},\n\t\t);\n\t\tthis.addDisposer(keepalive(this.violationCount));\n\n\t\t// Factory-time seed (COMPOSITION-GUIDE §28): cache the latest rules\n\t\t// inside a closure, refresh on each subscribe-pushed update, and read\n\t\t// closure inside the guard so policy updates take effect immediately.\n\t\tconst initialRules = (policiesNode.cache as readonly PolicyRuleData[] | undefined) ?? [];\n\t\tlet latestRules: readonly PolicyRuleData[] = initialRules;\n\t\tthis._currentGuard = policyFromRules(latestRules);\n\t\tconst offPolicies = policiesNode.subscribe((msgs) => {\n\t\t\tfor (const m of msgs) {\n\t\t\t\tif (m[0] === DATA) {\n\t\t\t\t\tlatestRules = (m[1] as readonly PolicyRuleData[] | undefined) ?? [];\n\t\t\t\t\tthis._currentGuard = policyFromRules(latestRules);\n\t\t\t\t}\n\t\t\t}\n\t\t});\n\t\tthis.addDisposer(offPolicies);\n\n\t\t// Resolve `paths` option to its three modes:\n\t\t// (a) undefined → dynamic coverage via watchTopologyTree\n\t\t// (b) static readonly string[] → caller owns the set\n\t\t// (c) Node<readonly string[]> → reactive set; rebind on each emission\n\t\t// `latestPaths` is the closure-mirror of the current path set (or\n\t\t// undefined for dynamic coverage). It is read by:\n\t\t// - the audit-mode observe callback (allow-list filter)\n\t\t// - the reactive-paths rebind subscription (diff against next set)\n\t\t// Mirrors the `latestRules` pattern used for `policiesNode` above\n\t\t// (COMPOSITION-GUIDE §28 factory-time seed).\n\t\tconst pathsOpt = opts.paths;\n\t\tconst pathsNode: Node<readonly string[]> | undefined = isNode(pathsOpt)\n\t\t\t? (pathsOpt as Node<readonly string[]>)\n\t\t\t: undefined;\n\t\t// `pathsExplicit` mirrors the legacy \"caller provided a path set\" branch\n\t\t// — true for both static-array and Node-of-array forms; false only when\n\t\t// `opts.paths` is omitted (dynamic-coverage mode).\n\t\tconst pathsExplicit = pathsOpt != null;\n\t\tconst initialPaths: readonly string[] | undefined =\n\t\t\tpathsNode != null\n\t\t\t\t? ((pathsNode.cache as readonly string[] | undefined) ?? [])\n\t\t\t\t: pathsExplicit\n\t\t\t\t\t? [...(pathsOpt as readonly string[])]\n\t\t\t\t\t: undefined;\n\t\t// `latestPaths` is undefined ONLY in dynamic-coverage mode.\n\t\tlet latestPaths: readonly string[] | undefined = initialPaths;\n\t\t// Initial sweep set for the enforce-mode wrap loop.\n\t\tconst paths = latestPaths ?? collectPaths(target);\n\n\t\t// Audit-mode reactive-paths subscription. Enforce mode handles its own\n\t\t// subscription (it also needs to diff old↔new to wrap/release guards);\n\t\t// audit mode just needs `latestPaths` to track the latest cache so the\n\t\t// allow-list filter stays current. Wired here, before the mode branch,\n\t\t// so it runs in audit mode only — enforce mode wires its own\n\t\t// rebinding subscription with diffing logic (DRY would require an\n\t\t// always-on tracker that enforce mode then ignores; the small\n\t\t// duplication keeps the enforce-mode branch self-contained).\n\t\tif (this._mode !== \"enforce\" && pathsNode != null) {\n\t\t\tconst offAuditPaths = pathsNode.subscribe((msgs) => {\n\t\t\t\tfor (const m of msgs) {\n\t\t\t\t\tif (m[0] !== DATA) continue;\n\t\t\t\t\tlatestPaths = (m[1] as readonly string[] | undefined) ?? [];\n\t\t\t\t}\n\t\t\t});\n\t\t\tthis.addDisposer(offAuditPaths);\n\t\t}\n\n\t\tif (this._mode === \"enforce\") {\n\t\t\t// Track which paths are currently guarded so dynamic adds don't\n\t\t\t// double-wrap and removed nodes release guard handles.\n\t\t\tconst restorers = new Map<string, () => void>();\n\t\t\tconst wrapAndPush = (path: string): void => {\n\t\t\t\tif (restorers.has(path)) return;\n\t\t\t\tconst node = safeNode(target, path);\n\t\t\t\tif (!(node instanceof NodeImpl)) return;\n\t\t\t\tconst pathGuard: NodeGuard = (actor, action) => {\n\t\t\t\t\tconst ok = this._currentGuard(actor, action);\n\t\t\t\t\tif (!ok) {\n\t\t\t\t\t\tthis._publishViolation(actor, action, path, \"blocked\");\n\t\t\t\t\t}\n\t\t\t\t\treturn ok;\n\t\t\t\t};\n\t\t\t\trestorers.set(path, node._pushGuard(pathGuard));\n\t\t\t};\n\t\t\t// Initial sweep: guard every path present at construction.\n\t\t\tfor (const path of paths) wrapAndPush(path);\n\n\t\t\t// Reactive paths rebind: when `paths` is a Node, every DATA emission\n\t\t\t// replaces `latestPaths` and diffs against the previous set —\n\t\t\t// added paths get wrapped, removed paths release their guard. No\n\t\t\t// imperative orchestration; the diff falls out of the closure-mirror\n\t\t\t// + subscribe pattern (mirrors `policiesNode` above).\n\t\t\tif (pathsNode != null) {\n\t\t\t\tconst offReactivePaths = pathsNode.subscribe((msgs) => {\n\t\t\t\t\tfor (const m of msgs) {\n\t\t\t\t\t\tif (m[0] !== DATA) continue;\n\t\t\t\t\t\tconst next = (m[1] as readonly string[] | undefined) ?? [];\n\t\t\t\t\t\tconst nextSet = new Set(next);\n\t\t\t\t\t\tconst prevSet = new Set(latestPaths ?? []);\n\t\t\t\t\t\t// Wrap rebind in `batch()` (qa D7) — guards are imperative\n\t\t\t\t\t\t// graph mutations; if `paths` and `policies` co-emit in an\n\t\t\t\t\t\t// outer batch (atomic config swap), each handler's mutations\n\t\t\t\t\t\t// would otherwise unwind in arbitrary order, letting an\n\t\t\t\t\t\t// in-flight write hit a half-rebound guard set. Batching\n\t\t\t\t\t\t// coalesces release + wrap into a single deferred drain.\n\t\t\t\t\t\tbatch(() => {\n\t\t\t\t\t\t\t// Release paths that fell out of the new set.\n\t\t\t\t\t\t\tfor (const p of prevSet) {\n\t\t\t\t\t\t\t\tif (nextSet.has(p)) continue;\n\t\t\t\t\t\t\t\tconst r = restorers.get(p);\n\t\t\t\t\t\t\t\tif (r != null) {\n\t\t\t\t\t\t\t\t\tr();\n\t\t\t\t\t\t\t\t\trestorers.delete(p);\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t// Wrap newly-added paths.\n\t\t\t\t\t\t\tfor (const p of nextSet) {\n\t\t\t\t\t\t\t\tif (prevSet.has(p)) continue;\n\t\t\t\t\t\t\t\twrapAndPush(p);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tlatestPaths = next;\n\t\t\t\t\t\t});\n\t\t\t\t\t}\n\t\t\t\t});\n\t\t\t\tthis.addDisposer(offReactivePaths);\n\t\t\t}\n\n\t\t\t// Dynamic coverage: when `paths` was NOT explicitly provided, follow\n\t\t\t// the full topology tree (target + every transitively-mounted\n\t\t\t// subgraph, including subgraphs mounted after construction) so late\n\t\t\t// adds at any depth get guarded. `prefix` carries the qualified\n\t\t\t// path-prefix from `target` to the emitter graph.\n\t\t\tif (!pathsExplicit) {\n\t\t\t\tconst offTopology = watchTopologyTree(target, (event, emitter, prefix) => {\n\t\t\t\t\tif (event.kind === \"added\") {\n\t\t\t\t\t\tif (event.nodeKind === \"node\") {\n\t\t\t\t\t\t\twrapAndPush(`${prefix}${event.name}`);\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t// Mount added. Walk just the newly-mounted subgraph's local\n\t\t\t\t\t\t\t// paths (scoped describe — O(M) in the mounted subtree)\n\t\t\t\t\t\t\t// rather than re-describing the entire target tree. The\n\t\t\t\t\t\t\t// emitter is the PARENT of the new mount; resolve the child\n\t\t\t\t\t\t\t// via its `_mounts` map.\n\t\t\t\t\t\t\tconst child = emitter._mounts.get(event.name);\n\t\t\t\t\t\t\tif (!(child instanceof Graph)) return;\n\t\t\t\t\t\t\tconst mountPrefix = `${prefix}${event.name}::`;\n\t\t\t\t\t\t\tconst localPaths = collectPaths(child);\n\t\t\t\t\t\t\tfor (const localPath of localPaths) {\n\t\t\t\t\t\t\t\t// `localPath` is relative to `child`; qualify with the\n\t\t\t\t\t\t\t\t// mount prefix so guard keys stay target-rooted.\n\t\t\t\t\t\t\t\twrapAndPush(\n\t\t\t\t\t\t\t\t\tlocalPath === \"\" ? `${prefix}${event.name}` : `${mountPrefix}${localPath}`,\n\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t} else if (event.kind === \"removed\") {\n\t\t\t\t\t\t// TEARDOWN already unhooks the guard; release bookkeeping so\n\t\t\t\t\t\t// re-adds under the same qualified path re-wrap cleanly.\n\t\t\t\t\t\tif (event.nodeKind === \"node\") {\n\t\t\t\t\t\t\tconst qp = `${prefix}${event.name}`;\n\t\t\t\t\t\t\tconst r = restorers.get(qp);\n\t\t\t\t\t\t\tif (r != null) {\n\t\t\t\t\t\t\t\tr();\n\t\t\t\t\t\t\t\trestorers.delete(qp);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tconst mountQp = `${prefix}${event.name}`;\n\t\t\t\t\t\t\tconst mountPrefix = `${mountQp}::`;\n\t\t\t\t\t\t\tfor (const [p, r] of restorers) {\n\t\t\t\t\t\t\t\tif (p === mountQp || p.startsWith(mountPrefix)) {\n\t\t\t\t\t\t\t\t\tr();\n\t\t\t\t\t\t\t\t\trestorers.delete(p);\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t});\n\t\t\t\tthis.addDisposer(offTopology);\n\t\t\t} else {\n\t\t\t\t// Restricted mode: subscribe to target.topology (own-graph only —\n\t\t\t\t// explicit `paths` means caller owns the path set) so node removals\n\t\t\t\t// release their restorers instead of leaking until enforcer dispose.\n\t\t\t\tconst offCleanup = target.topology.subscribe((msgs) => {\n\t\t\t\t\tfor (const m of msgs) {\n\t\t\t\t\t\tif (m[0] !== DATA) continue;\n\t\t\t\t\t\tconst event = m[1] as TopologyEvent;\n\t\t\t\t\t\tif (event.kind !== \"removed\" || event.nodeKind !== \"node\") continue;\n\t\t\t\t\t\tconst r = restorers.get(event.name);\n\t\t\t\t\t\tif (r != null) {\n\t\t\t\t\t\t\tr();\n\t\t\t\t\t\t\trestorers.delete(event.name);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t});\n\t\t\t\tthis.addDisposer(offCleanup);\n\t\t\t}\n\t\t\tthis.addDisposer(() => {\n\t\t\t\tfor (const r of restorers.values()) r();\n\t\t\t\trestorers.clear();\n\t\t\t});\n\t\t} else {\n\t\t\t// Audit mode: observe writes, evaluate against current guard, record\n\t\t\t// violations without blocking. Use the structured observe stream so\n\t\t\t// `path` and `actor` attribution are supplied without per-node\n\t\t\t// subscription bookkeeping. B9: unattributed writes no longer skip\n\t\t\t// — the ObserveEvent always carries a well-formed `actor` (falling\n\t\t\t// back to `DEFAULT_ACTOR` for anonymous/internal writes), so the\n\t\t\t// policy is evaluated against every write.\n\t\t\tconst handle = target.observe({ timeline: true, structured: true });\n\t\t\tconst off = handle.onEvent((event) => {\n\t\t\t\tif (event.type !== \"data\" && event.type !== \"error\") return;\n\t\t\t\tconst path = event.path ?? \"\";\n\t\t\t\tif (!path) return;\n\t\t\t\t// `latestPaths` is the closure-mirror of the (possibly reactive)\n\t\t\t\t// path allow-list. Undefined = no restriction (dynamic-coverage\n\t\t\t\t// mode); reactive callers see the filter rebind on each emission\n\t\t\t\t// without re-creating the enforcer (Tier 3.4).\n\t\t\t\tif (latestPaths != null && !latestPaths.includes(path)) return;\n\t\t\t\t// Prefer the event-stamped actor (always populated for DATA/ERROR\n\t\t\t\t// post-B9). Fall back to lastMutation for back-compat with any\n\t\t\t\t// consumer stubbing observe events without the field.\n\t\t\t\tconst actor =\n\t\t\t\t\t(event as { actor?: Actor }).actor ?? safeNode(target, path)?.lastMutation?.actor;\n\t\t\t\tif (actor == null) return; // defensive — shouldn't happen post-B9\n\t\t\t\tconst action: GuardAction = \"write\";\n\t\t\t\tif (this._currentGuard(actor, action)) return;\n\t\t\t\tthis._publishViolation(actor, action, path, \"observed\");\n\t\t\t});\n\t\t\tthis.addDisposer(() => {\n\t\t\t\toff();\n\t\t\t\thandle.dispose();\n\t\t\t});\n\t\t}\n\t}\n\n\tprivate _publishViolation(\n\t\tactor: Actor,\n\t\taction: GuardAction,\n\t\tpath: string,\n\t\tresult: \"observed\" | \"blocked\",\n\t): void {\n\t\tthis.violations.publish({\n\t\t\ttimestamp_ns: monotonicNs(),\n\t\t\twall_clock_ns: wallClockNs(),\n\t\t\tpath,\n\t\t\tactor,\n\t\t\taction,\n\t\t\tmode: this._mode,\n\t\t\tresult,\n\t\t});\n\t}\n\n\t/** Snapshot of recorded violations. */\n\tall(): readonly PolicyViolation[] {\n\t\treturn this.violations.retained();\n\t}\n\n\tget mode(): \"audit\" | \"enforce\" {\n\t\treturn this._mode;\n\t}\n\n\tget target(): Graph {\n\t\treturn this._target;\n\t}\n}\n\n/**\n * Wraps a {@link Graph} with reactive policy enforcement. Pass either a\n * static rule list or a {@link Node} of rules (LLM-updatable). Records\n * `PolicyViolation` entries to `violations` topic; in `\"enforce\"` mode also\n * pushes guards onto target nodes so disallowed writes throw.\n *\n * Self-tags via `g.tagFactory(\"policyGate\", placeholderArgs(opts))` so\n * `graph.describe()` surfaces `factory: \"policyGate\"` provenance (Phase 2.5\n * DT5 ride-along, locked with the Tier 2.3 rename).\n */\nexport function policyGate(\n\ttarget: Graph,\n\tpolicies: readonly PolicyRuleData[] | Node<readonly PolicyRuleData[]>,\n\topts: PolicyGateOptions = {},\n): PolicyGateGraph {\n\tconst g = new PolicyGateGraph(target, policies, opts);\n\t// `placeholderArgs` walks `opts` for non-JSON fields (e.g. `policies` may\n\t// be a Node when the caller wants live-updatable rules; `opts.graph` is\n\t// `GraphOptions`). DT5 deferred tag; Tier 2.3 ride-along.\n\tg.tagFactory(\"policyGate\", placeholderArgs(opts as unknown as Record<string, unknown>));\n\treturn g;\n}\n\n// ---------------------------------------------------------------------------\n// complianceSnapshot\n// ---------------------------------------------------------------------------\n\n/** Options for {@link complianceSnapshot}. */\nexport interface ComplianceSnapshotOptions {\n\taudit?: AuditTrailGraph;\n\tpolicies?: PolicyGateGraph;\n\t/** Actor recorded as the snapshot taker. */\n\tactor?: Actor;\n}\n\n/** Output of {@link complianceSnapshot}. JSON-serializable. */\nexport interface ComplianceSnapshotResult {\n\tformat_version: 1;\n\ttimestamp_ns: number;\n\twall_clock_ns: number;\n\tactor?: Actor;\n\tgraph: GraphPersistSnapshot;\n\taudit?: { count: number; entries: AuditEntry[] };\n\tpolicies?: {\n\t\tmode: \"audit\" | \"enforce\";\n\t\trules: readonly PolicyRuleData[];\n\t\tviolations: readonly PolicyViolation[];\n\t};\n\t/**\n\t * Truncated SHA-256 hex (16 chars / ~64 bits) over a canonical encoding\n\t * of every field above (excluding `fingerprint` itself). Deterministic\n\t * across runs given identical inputs. Suitable for casual tamper-evidence\n\t * and content-addressed dedup; for full cryptographic strength, hash the\n\t * canonical JSON externally with Web Crypto / Node `crypto`.\n\t */\n\tfingerprint: string;\n}\n\n/**\n * One-shot point-in-time export of a {@link Graph}'s state plus optional\n * audit + policy bundles. Returns a JSON-serializable object with a\n * deterministic truncated-SHA-256 {@link ComplianceSnapshotResult.fingerprint}\n * over the canonical payload for tamper-evidence in regulatory archival.\n *\n * **Cryptographic strength:** the fingerprint is truncated to 64 bits for\n * compact archival. Collision-resistant for casual integrity checks but NOT\n * sufficient for adversarial tamper-evidence — pair with a full SHA-256\n * (or stronger) over the canonical JSON when regulatory requirements demand\n * collision resistance.\n */\nexport function complianceSnapshot(\n\ttarget: Graph,\n\topts: ComplianceSnapshotOptions = {},\n): ComplianceSnapshotResult {\n\tconst result: Omit<ComplianceSnapshotResult, \"fingerprint\"> = {\n\t\tformat_version: 1,\n\t\ttimestamp_ns: monotonicNs(),\n\t\twall_clock_ns: wallClockNs(),\n\t\tgraph: target.snapshot() as GraphPersistSnapshot,\n\t};\n\tif (opts.actor != null) result.actor = opts.actor;\n\tif (opts.audit != null) {\n\t\tconst entries = [...opts.audit.all()];\n\t\tresult.audit = { count: entries.length, entries };\n\t}\n\tif (opts.policies != null) {\n\t\tconst rules = (opts.policies.policies.cache as readonly PolicyRuleData[] | undefined) ?? [];\n\t\tresult.policies = {\n\t\t\tmode: opts.policies.mode,\n\t\t\trules,\n\t\t\tviolations: [...opts.policies.all()],\n\t\t};\n\t}\n\tconst fingerprint = computeFingerprint(result);\n\treturn { ...result, fingerprint };\n}\n\n// ---------------------------------------------------------------------------\n// Internals\n// ---------------------------------------------------------------------------\n\nfunction isNode<T>(x: unknown): x is Node<T> {\n\treturn typeof x === \"object\" && x !== null && \"subscribe\" in (x as object);\n}\n\nfunction safeNode(target: Graph, path: string): Node | undefined {\n\ttry {\n\t\treturn target.node(path);\n\t} catch {\n\t\t// F-CATCH deliberate-exception: read-only introspection helper. Paths\n\t\t// come from a describe()-derived walk that can race node removal; a\n\t\t// missing node is a normal condition here, not an error to surface.\n\t\treturn undefined;\n\t}\n}\n\nfunction safeAnnotation(target: Graph, path: string): string | undefined {\n\ttry {\n\t\treturn target.annotation(path);\n\t} catch {\n\t\t// F-CATCH deliberate-exception: same rationale as safeNode — a missing\n\t\t// annotation during a best-effort audit walk is expected, not an error.\n\t\treturn undefined;\n\t}\n}\n\n/**\n * Walks every locally-registered node path in `target`, descending through\n * mounted subgraphs. Returns qualified paths.\n */\nfunction collectPaths(target: Graph): string[] {\n\tconst described = target.describe({ detail: \"minimal\" });\n\treturn Object.keys(described.nodes);\n}\n\n/**\n * Stable canonical JSON → truncated SHA-256 hex fingerprint (16 hex chars,\n * ~64-bit). Uses the same vendored sync SHA-256 as `core/versioning.ts`\n * `defaultHash`, so cross-module fingerprints stay consistent.\n *\n * Canonicalization handles cycles (recursion-stack tracker), `undefined`,\n * `bigint`, `Map`, `Set`, `Date`, `RegExp`, and typed arrays via typed\n * markers — see {@link canonicalize}.\n *\n * **Note:** truncated to 16 hex chars (~64-bit) for compact archival. For\n * full 256-bit cryptographic strength, hash {@link complianceSnapshot} JSON\n * externally with Web Crypto / Node `crypto`.\n */\nfunction computeFingerprint(value: unknown): string {\n\t// Pre-stringify our canonical form so `defaultHash`'s\n\t// `canonicalizeForHash` (which rejects unsafe integers) only ever sees a\n\t// JSON string. Compliance payloads carry `timestamp_ns` values that\n\t// exceed `Number.MAX_SAFE_INTEGER` — JSON.stringify handles them fine,\n\t// the hash function only cares about deterministic input bytes.\n\treturn defaultHash(JSON.stringify(canonicalize(value)));\n}\n\n/**\n * Cycle-safe canonical encoding. Uses a recursion-stack `Set` (push on\n * descent, pop on return) so legitimate DAG re-references are encoded as\n * themselves; only true cycles produce a `__circular: true` marker. Typed\n * markers preserve `undefined` / `bigint` / `Map` / `Set` / `Date` / `RegExp`\n * / typed-array information that bare `JSON.stringify` would silently drop\n * or collide with strings.\n */\nfunction canonicalize(value: unknown): unknown {\n\tconst stack = new Set<object>();\n\tconst walk = (v: unknown): unknown => {\n\t\tif (v === undefined) return { __undefined: true };\n\t\tif (v === null) return null;\n\t\tconst t = typeof v;\n\t\tif (t === \"bigint\") return { __bigint: (v as bigint).toString() };\n\t\tif (t !== \"object\") return v;\n\t\tconst obj = v as object;\n\t\tif (stack.has(obj)) return { __circular: true };\n\t\tstack.add(obj);\n\t\ttry {\n\t\t\tif (Array.isArray(obj)) {\n\t\t\t\treturn (obj as unknown[]).map(walk);\n\t\t\t}\n\t\t\tif (obj instanceof Date) {\n\t\t\t\treturn { __date: obj.toISOString() };\n\t\t\t}\n\t\t\tif (obj instanceof RegExp) {\n\t\t\t\treturn { __regexp: { source: obj.source, flags: obj.flags } };\n\t\t\t}\n\t\t\tif (obj instanceof Map) {\n\t\t\t\tconst entries = [...(obj as Map<unknown, unknown>).entries()].map(([k, mv]) => [\n\t\t\t\t\twalk(k),\n\t\t\t\t\twalk(mv),\n\t\t\t\t]);\n\t\t\t\treturn { __map: entries };\n\t\t\t}\n\t\t\tif (obj instanceof Set) {\n\t\t\t\tconst items = [...(obj as Set<unknown>)].map(walk);\n\t\t\t\treturn { __set: items };\n\t\t\t}\n\t\t\tif (ArrayBuffer.isView(obj)) {\n\t\t\t\tconst ta = obj as unknown as { length: number; [i: number]: number };\n\t\t\t\tconst arr: number[] = new Array(ta.length);\n\t\t\t\tfor (let i = 0; i < ta.length; i++) arr[i] = ta[i] ?? 0;\n\t\t\t\treturn { __typed_array: { ctor: obj.constructor.name, data: arr } };\n\t\t\t}\n\t\t\tconst out: Record<string, unknown> = {};\n\t\t\tfor (const k of Object.keys(obj as Record<string, unknown>).sort()) {\n\t\t\t\tout[k] = walk((obj as Record<string, unknown>)[k]);\n\t\t\t}\n\t\t\treturn out;\n\t\t} finally {\n\t\t\tstack.delete(obj);\n\t\t}\n\t};\n\treturn walk(value);\n}\n\n// `explainPath` / `CausalChain` / `CausalStep` are exported from `graph/`\n// at module root; do not re-export here to keep the namespace boundary clean\n// and avoid duplicate-identifier issues in bundled .d.ts.\n","/**\n * Metadata helpers for pattern-layer nodes (Tier 2.2 promotion from\n * `patterns/_internal/`).\n *\n * Each domain (orchestration, messaging, reduction, ai, cqrs, domain_template,\n * memory, lens, audit, harness) shares the same metadata convention. Promoted\n * to `extra/` so non-patterns code (and downstream consumers building their\n * own domain primitives) can use the same shape.\n *\n * @module\n */\n\n/**\n * Build a domain metadata object for pattern-layer nodes.\n *\n * Each domain follows the same shape: `{ [domain]: true, [domain]_type: kind, ...extra }`.\n *\n * @param domain - The domain tag (e.g. `\"orchestration\"`, `\"ai\"`, `\"cqrs\"`).\n * @param kind - The specific type within the domain (e.g. `\"gate\"`, `\"prompt\"`).\n * @param extra - Additional metadata to merge.\n * @returns Metadata object.\n */\nexport function domainMeta(\n\tdomain: string,\n\tkind: string,\n\textra?: Record<string, unknown>,\n): Record<string, unknown> {\n\treturn {\n\t\t[domain]: true,\n\t\t[`${domain}_type`]: kind,\n\t\t...(extra ?? {}),\n\t};\n}\n","/**\n * Messaging patterns (roadmap §4.2).\n *\n * Pulsar-inspired messaging primitives modeled as graph factories:\n * - `topic()` for append-only topic streams with a retained window.\n * - `subscription()` for cursor-based consumers.\n * - `topicBridge()` for autonomous topic-to-topic relay.\n * - `messagingHub()` for a lazy topic registry.\n *\n * Plus the Phase 13.B standard `Message<T>` envelope and well-known topic\n * name constants ({@link PROMPTS_TOPIC} / {@link RESPONSES_TOPIC} /\n * {@link INJECTIONS_TOPIC} / {@link DEFERRED_TOPIC} / {@link SPAWNS_TOPIC})\n * — recommended (not enforced) wire shape for cross-graph topic payloads.\n *\n * Job queue / job flow primitives live in `patterns/job-queue` — they are a\n * distinct domain that happens to share reactive-log / reactive-map\n * infrastructure with topics.\n */\n\nexport {\n\ttype HubRemoveTopicRecord,\n\thubRemoveTopicKeyOf,\n\ttype MessagingAuditRecord,\n\ttype SubscriptionAckRecord,\n\ttype SubscriptionPullAndAckRecord,\n\tsubscriptionAckKeyOf,\n\tsubscriptionPullAndAckKeyOf,\n\ttype TopicPublishRecord,\n\ttopicPublishKeyOf,\n} from \"./audit-records.js\";\nexport {\n\tCONTEXT_TOPIC,\n\tDEFERRED_TOPIC,\n\tINJECTIONS_TOPIC,\n\ttype JsonSchema,\n\tPROMPTS_TOPIC,\n\tRESPONSES_TOPIC,\n\tSPAWNS_TOPIC,\n\tSTANDARD_TOPICS,\n\ttype StandardTopic,\n\tTODOS_TOPIC,\n\ttype TopicMessage,\n} from \"./message.js\";\n\nimport { batch, COMPLETE, DATA, type Node, node } from \"@graphrefly/pure-ts/core\";\nimport { keepalive, type ReactiveLogBundle, reactiveLog } from \"@graphrefly/pure-ts/extra\";\nimport { Graph, type GraphOptions } from \"@graphrefly/pure-ts/graph\";\nimport { domainMeta } from \"../../base/meta/domain-meta.js\";\nimport { mutate } from \"../../base/mutation/index.js\";\n\nconst DEFAULT_MAX_PER_PUMP = 256;\n\nfunction requireNonNegativeInt(value: number, label: string): number {\n\tif (!Number.isFinite(value) || !Number.isInteger(value) || value < 0) {\n\t\tthrow new Error(`${label} must be a non-negative integer`);\n\t}\n\treturn value;\n}\n\nfunction messagingMeta(kind: string, extra?: Record<string, unknown>): Record<string, unknown> {\n\treturn domainMeta(\"messaging\", kind, extra);\n}\n\nexport type TopicOptions = {\n\tgraph?: GraphOptions;\n\t/** Bounded retention; default 1024 per cross-cutting policy (Audit 2/4). */\n\tretainedLimit?: number;\n};\n\nconst DEFAULT_TOPIC_RETAINED_LIMIT = 1024;\n\nexport class TopicGraph<T> extends Graph {\n\tprivate readonly _log;\n\tprivate readonly _publishImpl: (value: T) => void;\n\treadonly events: Node<readonly T[]>;\n\t/**\n\t * Most recently published value. Stays in the protocol SENTINEL state\n\t * (`cache === undefined`, no DATA emitted) until the first publish, then\n\t * tracks the latest entry. Spec §5.12 reserves `undefined` as the\n\t * \"never sent DATA\" sentinel — and `TopicGraph.publish(undefined)` is\n\t * rejected — so `cache === undefined` unambiguously signals \"empty topic\"\n\t * even when `T` itself includes `null` (i.e., `topic<number | null>`).\n\t *\n\t * **Within a reactive fn:** detect the empty-topic case via\n\t * `ctx.prevData[i] === undefined` for the dep slot holding `topic.latest`,\n\t * or check `latest.cache === undefined` outside reactive code. No\n\t * separate `hasLatest` companion needed — the SENTINEL is the answer.\n\t */\n\treadonly latest: Node<T>;\n\n\tconstructor(name: string, opts: TopicOptions = {}) {\n\t\tsuper(name, opts.graph);\n\t\tthis._log = reactiveLog<T>([], {\n\t\t\tname: \"events\",\n\t\t\tmaxSize: opts.retainedLimit ?? DEFAULT_TOPIC_RETAINED_LIMIT,\n\t\t});\n\t\tthis.events = this._log.entries;\n\t\tthis.add(this.events, { name: \"events\" });\n\t\t// `this.derived(\"latest\", [\"events\"], …)` is expressible after the\n\t\t// 2026-04-30 self-resolve fix in `Graph._resolveFromSegments` — a\n\t\t// single-segment path matching the graph's own name (e.g.\n\t\t// `topic(\"events\").resolve(\"events\")`) no longer collapses to empty\n\t\t// and falls through to local-node lookup. Replaces the prior\n\t\t// `node([events], …) + this.add(...)` workaround.\n\t\t//\n\t\t// SENTINEL on empty: returning `[]` here yields a RESOLVED-only wave\n\t\t// (no DATA), so `latest.cache` stays `undefined` until the first\n\t\t// publish. `TopicGraph.publish(undefined)` is rejected (line below),\n\t\t// so `undefined` cache is unambiguously \"empty topic\" even when `T`\n\t\t// itself includes `null`. Drops the prior `hasLatest` companion as\n\t\t// redundant.\n\t\tthis.latest = this.derived<T>(\n\t\t\t\"latest\",\n\t\t\t[\"events\"],\n\t\t\t(batchData, ctx) => {\n\t\t\t\tconst data = batchData.map((batch, i) =>\n\t\t\t\t\tbatch != null && batch.length > 0 ? batch.at(-1) : ctx.prevData[i],\n\t\t\t\t);\n\t\t\t\tconst entries = data[0] as readonly T[];\n\t\t\t\treturn entries.length === 0 ? [] : [entries[entries.length - 1] as T];\n\t\t\t},\n\t\t\t{ meta: messagingMeta(\"topic_latest\") },\n\t\t);\n\t\tthis.addDisposer(keepalive(this.latest));\n\n\t\t// D1(a): on teardown, propagate COMPLETE on `events` so downstream\n\t\t// derived chains (including any externally-held SubscriptionGraph\n\t\t// sources) see the termination via their `terminalDeps` and can stop\n\t\t// serving stale caches. Tier-3 terminal per spec §2.2.\n\t\t//\n\t\t// EC16 (verified 2026-04-30): the COMPLETE-then-disposeAllViews order\n\t\t// is intentional. COMPLETE propagates SYNCHRONOUSLY through every\n\t\t// subscriber (cursor views, derived chains) so they self-unsubscribe\n\t\t// in their terminal handler before `disposeAllViews` runs. Swapping\n\t\t// the order would clear view caches before subscribers receive the\n\t\t// terminal — strictly worse. Reading `.cache` outside a reactive fn\n\t\t// across teardown is an anti-pattern (spec §5.12) and not a use case\n\t\t// this ordering needs to preserve.\n\t\tthis.addDisposer(() => {\n\t\t\tthis.events.down([[COMPLETE]]);\n\t\t});\n\t\t// P9: release any memoized tail/slice view keepalives held by the log.\n\t\t// TopicGraph itself doesn't call log.tail/slice, but plugins may have\n\t\t// attached views via `_log` — defensive (typical reactive subscribers\n\t\t// have already unsubscribed in their COMPLETE handler above).\n\t\tthis.addDisposer(() => this._log.disposeAllViews());\n\n\t\t// Tier 8 / COMPOSITION-GUIDE §35: route publish through `mutate`\n\t\t// for centralized freeze + re-throw semantics. No audit log surface\n\t\t// (per Tier 8 γ-0): the topic's `events` log already records every\n\t\t// successful publish, so a separate audit Node would be redundant.\n\t\t// `freeze: false` because topic payloads can be large and per-publish\n\t\t// cost matters on hot paths.\n\t\tthis._publishImpl = mutate<[T], void, never>(\n\t\t\t(value): void => {\n\t\t\t\tthis._log.append(value);\n\t\t\t},\n\t\t\t{ frame: \"inline\", freeze: false },\n\t\t);\n\t}\n\n\tpublish(value: T): void {\n\t\t// SENTINEL alignment (Wave B.1 Unit 11 lock): `undefined` is the\n\t\t// protocol-level \"never sent DATA\" sentinel — refusing it here\n\t\t// preserves `lastValue: Node<T | undefined>` semantics.\n\t\tif (value === undefined) {\n\t\t\tthrow new TypeError(\n\t\t\t\t`TopicGraph \"${this.name}\": publish(undefined) is not allowed (spec §5.12 SENTINEL).`,\n\t\t\t);\n\t\t}\n\t\tthis._publishImpl(value);\n\t}\n\n\t/**\n\t * Wire one or more append-log storage tiers (Audit 4). Each tier receives\n\t * appended events per wave; rollback honors the wave-as-transaction model.\n\t *\n\t * Named `attachEventStorage` (not `attachStorage`) to avoid colliding with\n\t * the inherited {@link Graph.attachSnapshotStorage} which takes the\n\t * paired `AttachSnapshotTierPair[]` shape (Phase 14.6) — distinct\n\t * concerns, distinct surfaces.\n\t *\n\t * @returns Disposer.\n\t */\n\tattachEventStorage(\n\t\ttiers: readonly import(\"@graphrefly/pure-ts/extra\").AppendLogStorageTier<T>[],\n\t): () => void {\n\t\treturn this._log.attachStorage(tiers);\n\t}\n\n\tretained(): readonly T[] {\n\t\treturn this.events.cache as readonly T[];\n\t}\n\n\t/** Internal log bundle — used by TopicBridgeGraph for `attach`. */\n\tget _logBundle() {\n\t\treturn this._log;\n\t}\n}\n\nexport type SubscriptionOptions = {\n\tgraph?: GraphOptions;\n\t/**\n\t * Starting cursor position.\n\t * @deprecated Use `from` instead.\n\t */\n\tcursor?: number;\n\t/**\n\t * Starting position for the subscription.\n\t * - `\"retained\"` (default) — cursor starts at 0; consumer sees all retained history.\n\t * - `\"now\"` — cursor starts at current topic length; consumer ignores history.\n\t * - `number` — explicit cursor position.\n\t */\n\tfrom?: \"now\" | \"retained\" | number;\n\t/**\n\t * When this signal node emits DATA, the subscription auto-advances cursor\n\t * to current `available.length`. Useful for \"ack everything when X happens\"\n\t * patterns. The reactive edge `advanceOn → cursor` is visible in `explain()`.\n\t */\n\tadvanceOn?: Node<unknown>;\n};\n\n/** Result of {@link SubscriptionGraph.pullAndAck}. */\nexport type PullAndAckResult<T> = {\n\titems: readonly T[];\n\tcursor: number;\n};\n\nexport class SubscriptionGraph<T> extends Graph {\n\treadonly cursor: Node<number>;\n\treadonly available: Node<readonly T[]>;\n\t/**\n\t * Reference to the upstream topic graph. Intentionally NOT mounted\n\t * under this subscription: a subscription is a VIEW over an\n\t * externally-owned topic. Double-mounting (e.g. hub-owned topic +\n\t * sub-mount here) would make either-side teardown leave the other\n\t * holding a dead reference. Node-level `derived([topicEvents], …)`\n\t * still wires the data dependency across graph boundaries. D1(e).\n\t */\n\treadonly topic: TopicGraph<T>;\n\n\tprivate _disposed = false;\n\tprivate readonly _ackImpl: (count: number | undefined) => number;\n\tprivate readonly _pullAndAckImpl: (limit: number | undefined) => PullAndAckResult<T>;\n\n\tconstructor(name: string, topicGraph: TopicGraph<T>, opts: SubscriptionOptions = {}) {\n\t\tsuper(name, opts.graph);\n\t\tthis.topic = topicGraph;\n\n\t\t// Resolve initial cursor from `from` option, falling back to legacy `cursor` option.\n\t\tlet initialCursor: number;\n\t\tif (opts.from !== undefined) {\n\t\t\tif (opts.from === \"retained\") {\n\t\t\t\tinitialCursor = 0;\n\t\t\t} else if (opts.from === \"now\") {\n\t\t\t\t// §28 sanctioned factory-time boundary read.\n\t\t\t\tinitialCursor = (topicGraph.events.cache as readonly T[]).length;\n\t\t\t} else {\n\t\t\t\tinitialCursor = requireNonNegativeInt(opts.from, \"subscription from\");\n\t\t\t}\n\t\t} else {\n\t\t\tinitialCursor = requireNonNegativeInt(opts.cursor ?? 0, \"subscription cursor\");\n\t\t}\n\n\t\tthis.cursor = this.state<number>(\"cursor\", initialCursor, {\n\t\t\tmeta: messagingMeta(\"subscription_cursor\"),\n\t\t});\n\n\t\t// B.1 Unit 12 lock: `available` depends directly on topic.events + cursor\n\t\t// via `view({ kind: \"fromCursor\" })`. No `source` passthrough node —\n\t\t// describe shows `topic::events → available` (cross-graph edge) and\n\t\t// `cursor → available` (local edge). One fewer node per subscription.\n\t\tthis.available = topicGraph._logBundle.view({ kind: \"fromCursor\", cursor: this.cursor });\n\t\tthis.add(this.available, { name: \"available\" });\n\t\tthis.addDisposer(keepalive(this.available));\n\n\t\t// Optional reactive auto-advance: when `advanceOn` emits a NEW DATA\n\t\t// (after construction), cursor advances by `available.length` atomically.\n\t\t// Edge visible in describe: advancePump depends on advanceOn.\n\t\t// `_advanceInitialized` guards against the initial push-on-subscribe fire\n\t\t// that would advance cursor before the user has a chance to read.\n\t\tif (opts.advanceOn !== undefined) {\n\t\t\tconst advanceOn = opts.advanceOn;\n\t\t\tlet advanceInitialized = false;\n\t\t\tconst advancePump = node<unknown>(\n\t\t\t\t[advanceOn],\n\t\t\t\t() => {\n\t\t\t\t\t// Skip the initial push-on-subscribe wave.\n\t\t\t\t\tif (!advanceInitialized) {\n\t\t\t\t\t\tadvanceInitialized = true;\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t\tif (this._disposed) return;\n\t\t\t\t\tconst avail = this.available.cache as readonly T[];\n\t\t\t\t\tif (avail.length === 0) return;\n\t\t\t\t\tconst next = (this.cursor.cache as number) + avail.length;\n\t\t\t\t\tthis.cursor.emit(next);\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tname: \"advancePump\",\n\t\t\t\t\tdescribeKind: \"effect\",\n\t\t\t\t\tmeta: messagingMeta(\"subscription_advance_pump\"),\n\t\t\t\t},\n\t\t\t);\n\t\t\tthis.add(advancePump, { name: \"advancePump\" });\n\t\t\tthis.addDisposer(keepalive(advancePump));\n\t\t}\n\n\t\t// Tier 8 / COMPOSITION-GUIDE §35: route ack + pullAndAck through\n\t\t// `mutate` for centralized freeze + re-throw semantics. No audit\n\t\t// log surface (per Tier 8 γ-0): the cursor's own emission stream already\n\t\t// records every advance, so a separate audit Node would be redundant.\n\t\t// `freeze: false` because count/limit are simple numbers; freezing is\n\t\t// pointless overhead. Disposed-checks stay outside the wrapper so a\n\t\t// no-op call doesn't unnecessarily run the wrapper.\n\t\tthis._ackImpl = mutate<[number | undefined], number, never>(\n\t\t\t(count): number => {\n\t\t\t\tconst available = this.available.cache as readonly T[];\n\t\t\t\tconst requested =\n\t\t\t\t\tcount === undefined\n\t\t\t\t\t\t? available.length\n\t\t\t\t\t\t: requireNonNegativeInt(count, \"subscription ack count\");\n\t\t\t\tconst step = Math.min(requested, available.length);\n\t\t\t\tif (step <= 0) return this.cursor.cache as number;\n\t\t\t\tconst next = (this.cursor.cache as number) + step;\n\t\t\t\t// F8: use emit() so the pipeline auto-prefixes DIRTY, runs equals\n\t\t\t\t// substitution, and produces a proper two-phase wave (the raw\n\t\t\t\t// `down([[DATA, next]])` path bypassed those contracts).\n\t\t\t\tthis.cursor.emit(next);\n\t\t\t\treturn next;\n\t\t\t},\n\t\t\t{ frame: \"inline\", freeze: false },\n\t\t);\n\n\t\tthis._pullAndAckImpl = mutate<[number | undefined], PullAndAckResult<T>, never>(\n\t\t\t(limit): PullAndAckResult<T> => {\n\t\t\t\tconst available = this.available.cache as readonly T[];\n\t\t\t\tconst max =\n\t\t\t\t\tlimit === undefined\n\t\t\t\t\t\t? available.length\n\t\t\t\t\t\t: requireNonNegativeInt(limit, \"subscription pullAndAck limit\");\n\t\t\t\tconst items = available.slice(0, max);\n\t\t\t\tif (items.length === 0) return { items, cursor: this.cursor.cache as number };\n\t\t\t\tconst next = (this.cursor.cache as number) + items.length;\n\t\t\t\tthis.cursor.emit(next);\n\t\t\t\treturn { items, cursor: next };\n\t\t\t},\n\t\t\t{ frame: \"inline\", freeze: false },\n\t\t);\n\t}\n\n\tack(count?: number): number {\n\t\tif (this._disposed) return this.cursor.cache as number;\n\t\treturn this._ackImpl(count);\n\t}\n\n\tpull(limit?: number): readonly T[] {\n\t\tif (this._disposed) return [];\n\t\tconst available = this.available.cache as readonly T[];\n\t\tconst max =\n\t\t\tlimit === undefined\n\t\t\t\t? available.length\n\t\t\t\t: requireNonNegativeInt(limit, \"subscription pull limit\");\n\t\treturn available.slice(0, max);\n\t}\n\n\t/**\n\t * Atomic pull-and-acknowledge. Returns `{ items, cursor }` where `cursor`\n\t * is the new cursor position after advancing. Under single-threaded JS the\n\t * snapshot and advance are atomic; PY callers use a per-subscription Lock.\n\t *\n\t * Replaces `pull(limit, { ack: true })`.\n\t */\n\tpullAndAck(limit?: number): PullAndAckResult<T> {\n\t\tif (this._disposed) return { items: [], cursor: this.cursor.cache as number };\n\t\treturn this._pullAndAckImpl(limit);\n\t}\n\n\t/**\n\t * Release internal subscriptions and mark the subscription torn-down.\n\t * Subsequent `pull`, `pullAndAck`, `ack` return empty / current cursor.\n\t * Emits COMPLETE on `cursor` so derived consumers (e.g. `available`) see\n\t * the termination signal. Also drains `addDisposer` callbacks (including\n\t * the `keepalive(advancePump)` subscription) so no keepalive leak occurs.\n\t */\n\tdispose(): void {\n\t\tif (this._disposed) return;\n\t\tthis._disposed = true;\n\t\tthis.cursor.down([[COMPLETE]]);\n\t\t// m4: drain addDisposer callbacks to release the keepalive subscription.\n\t\tthis.destroy();\n\t}\n}\n\nexport type TopicBridgeOptions<TIn, TOut> = {\n\tgraph?: GraphOptions;\n\tcursor?: number;\n\tmaxPerPump?: number;\n\t/**\n\t * Optional transform/filter applied to each item before republishing.\n\t *\n\t * **At-most-once with silent drop:** when `map` returns `undefined`, the\n\t * input is consumed from the source cursor but NOT republished. Filtered\n\t * items are not retained for retry. If you need filter-with-retry\n\t * semantics, do the filtering in a downstream subscription on the bridged\n\t * output rather than in the `map` function.\n\t */\n\tmap?: (value: TIn) => TOut | undefined;\n};\n\nexport class TopicBridgeGraph<TIn, TOut = TIn> extends Graph {\n\tprivate readonly _sourceSub;\n\treadonly bridgedCount: Node<number>;\n\t/**\n\t * Emits each mapped batch as DATA — gives downstream observers a reactive\n\t * stream of bridged values. Also the link target for `target._log.attach`.\n\t */\n\treadonly output: Node<readonly TOut[]>;\n\n\tconstructor(\n\t\tname: string,\n\t\tsourceTopic: TopicGraph<TIn>,\n\t\ttargetTopic: TopicGraph<TOut>,\n\t\topts: TopicBridgeOptions<TIn, TOut> = {},\n\t) {\n\t\tsuper(name, opts.graph);\n\t\tthis._sourceSub = subscription<TIn>(`${name}-subscription`, sourceTopic, {\n\t\t\tcursor: opts.cursor,\n\t\t});\n\t\tthis.mount(\"subscription\", this._sourceSub);\n\n\t\tconst maxPerPump = Math.max(\n\t\t\t1,\n\t\t\trequireNonNegativeInt(opts.maxPerPump ?? DEFAULT_MAX_PER_PUMP, \"topic bridge maxPerPump\"),\n\t\t);\n\t\tconst mapValue = opts.map ?? ((value: TIn) => value as unknown as TOut);\n\n\t\t// Reactive output node: derives a mapped batch from `available`.\n\t\t// §24 compliant — output is a real derived edge, visible in describe.\n\t\t// Replaces imperative publish loop. Items where mapValue returns undefined\n\t\t// are filtered out (opt-out / filter).\n\t\tthis.output = node<readonly TOut[]>(\n\t\t\t[this._sourceSub.available],\n\t\t\t(batchData, actions, ctx) => {\n\t\t\t\tconst data = batchData.map((batch, i) =>\n\t\t\t\t\tbatch != null && batch.length > 0 ? batch.at(-1) : ctx.prevData[i],\n\t\t\t\t);\n\t\t\t\tconst arr = data[0] as readonly TIn[];\n\t\t\t\tconst outBatch: TOut[] = [];\n\t\t\t\tconst take = Math.min(arr.length, maxPerPump);\n\t\t\t\tfor (let i = 0; i < take; i++) {\n\t\t\t\t\tconst mapped = mapValue(arr[i] as TIn);\n\t\t\t\t\tif (mapped !== undefined) outBatch.push(mapped);\n\t\t\t\t}\n\t\t\t\tactions.emit(outBatch);\n\t\t\t},\n\t\t\t{\n\t\t\t\tname: \"output\",\n\t\t\t\tdescribeKind: \"derived\",\n\t\t\t\tmeta: messagingMeta(\"topic_bridge_output\", { targetRef: targetTopic.name }),\n\t\t\t\tinitial: [],\n\t\t\t},\n\t\t);\n\t\tthis.add(this.output, { name: \"output\" });\n\t\tthis.addDisposer(keepalive(this.output));\n\n\t\t// bridgedCount: state node accumulating total bridged items.\n\t\t// Updated by ackPump after each batch — edge visible via ackPump dep on output.\n\t\tthis.bridgedCount = this.state<number>(\"bridgedCount\", 0, {\n\t\t\tmeta: messagingMeta(\"topic_bridge_count\"),\n\t\t});\n\t\tthis.addDisposer(keepalive(this.bridgedCount));\n\n\t\t// ackPump: effect that advances the subscription cursor and updates\n\t\t// bridgedCount after each batch. Runs after `output` settles.\n\t\t// Captures refs to `this.output`, `this._sourceSub`, `this.bridgedCount`\n\t\t// to avoid `this` inside the fn body.\n\t\tconst outputRef = this.output;\n\t\tconst subRef = this._sourceSub;\n\t\tconst countRef = this.bridgedCount;\n\t\tconst ackPump = this.effect(\n\t\t\t\"ackPump\",\n\t\t\t[\"output\"],\n\t\t\t() => {\n\t\t\t\tconst outBatch = outputRef.cache as readonly TOut[];\n\t\t\t\tif (outBatch.length === 0) return;\n\t\t\t\tconst availLen = (subRef.available.cache as readonly TIn[]).length;\n\t\t\t\tconst toAck = Math.min(availLen, maxPerPump);\n\t\t\t\tif (toAck > 0) {\n\t\t\t\t\tsubRef.ack(toAck);\n\t\t\t\t\tconst prev = (countRef.cache as number) ?? 0;\n\t\t\t\t\tcountRef.emit(prev + outBatch.length);\n\t\t\t\t}\n\t\t\t},\n\t\t\t{\n\t\t\t\tmeta: messagingMeta(\"topic_bridge_ack_pump\"),\n\t\t\t},\n\t\t);\n\t\tthis.addDisposer(keepalive(ackPump));\n\n\t\t// Wire output into target topic's log reactively.\n\t\t// _attachArrayToLog subscribes to output and publishes each item to targetTopic.\n\t\t// Teardown: disposer runs before mount teardown.\n\t\tconst detach = _attachArrayToLog(this.output, targetTopic);\n\t\tthis.addDisposer(detach);\n\t}\n}\n\n/**\n * Attaches each element of an array-valued Node to a TopicGraph's log.\n * Every DATA emission on `source` appends all items in the array to `targetTopic`.\n * Returns a disposer.\n */\nfunction _attachArrayToLog<T>(source: Node<readonly T[]>, targetTopic: TopicGraph<T>): () => void {\n\treturn source.subscribe((msgs) => {\n\t\tfor (const m of msgs) {\n\t\t\tif (m[0] !== DATA) continue;\n\t\t\tconst arr = m[1] as readonly T[];\n\t\t\tif (arr.length === 0) continue;\n\t\t\tbatch(() => {\n\t\t\t\tfor (const v of arr) targetTopic.publish(v);\n\t\t\t});\n\t\t}\n\t});\n}\n\n// ── TopicRegistry ─────────────────────────────────────────────────────────\n\n/**\n * Private pure data structure managing a named set of {@link TopicGraph}\n * instances. Extracted from {@link MessagingHubGraph} for separation of\n * concerns (B.2 Unit 14 lock: D — split into TopicRegistry + facade).\n *\n * Reusable if other domain consumers (e.g. cqrs.eventLogs) want a shared\n * topic registry later.\n *\n * @internal\n */\nexport class TopicRegistry {\n\tprivate readonly _map = new Map<string, TopicGraph<unknown>>();\n\t/** Reactive monotonic version counter. Advances on topic create/remove. */\n\treadonly version: Node<number>;\n\n\tconstructor(versionNode: Node<number>) {\n\t\tthis.version = versionNode;\n\t}\n\n\tget size(): number {\n\t\treturn this._map.size;\n\t}\n\n\thas(name: string): boolean {\n\t\treturn this._map.has(name);\n\t}\n\n\tget<T>(name: string): TopicGraph<T> | undefined {\n\t\treturn this._map.get(name) as TopicGraph<T> | undefined;\n\t}\n\n\tset<T>(name: string, t: TopicGraph<T>): void {\n\t\tthis._map.set(name, t as TopicGraph<unknown>);\n\t}\n\n\tdelete(name: string): boolean {\n\t\treturn this._map.delete(name);\n\t}\n\n\tkeys(): IterableIterator<string> {\n\t\treturn this._map.keys();\n\t}\n}\n\n// ── MessagingHubGraph ─────────────────────────────────────────────────────\n\nexport type MessagingHubOptions = {\n\tgraph?: GraphOptions;\n\t/**\n\t * Default `TopicOptions` applied to every topic created via `topic(name)`\n\t * without explicit options. Per-call opts override. Default: `{}`\n\t * (unbounded retention per topic unless `retainedLimit` is set per call).\n\t */\n\tdefaultTopicOptions?: TopicOptions;\n};\n\n/**\n * Lazy Pulsar-inspired topic registry. Manages a named set of {@link TopicGraph}\n * instances with retention + cursor semantics. Topics are created on first\n * access; `removeTopic(name)` unmounts and tears down via {@link Graph.remove}.\n *\n * Internally delegates to {@link TopicRegistry} for topic map management\n * (B.2 Unit 14 lock: D facade split).\n *\n * **Relationship to `pubsub()` in `src/extra/pubsub.ts`:** `pubsub` is a\n * lightweight last-value state hub (no retention, no cursors). `MessagingHubGraph`\n * is the full messaging hub — retained message logs, cursor-based subscriptions,\n * and pattern-layer lifecycle management.\n *\n * @category patterns\n */\nexport class MessagingHubGraph extends Graph {\n\tprivate readonly _registry: TopicRegistry;\n\t/** Reactive monotonic version counter — advances on topic create/remove. */\n\treadonly version: Node<number>;\n\tprivate readonly _defaultTopicOptions: TopicOptions;\n\tprivate readonly _removeTopicImpl: (name: string) => void;\n\n\tconstructor(name: string, opts: MessagingHubOptions = {}) {\n\t\tsuper(name, opts.graph);\n\t\t// B.2 Unit 14 lock: promote _version → version: Node<number>.\n\t\tconst versionNode = this.state<number>(\"version\", 0, {\n\t\t\tmeta: messagingMeta(\"hub_version\"),\n\t\t});\n\t\tthis.version = versionNode;\n\t\tthis._registry = new TopicRegistry(versionNode);\n\t\t// P8: shallow-copy caller-provided defaults so post-construction\n\t\t// mutations by the caller don't leak into every future `topic()` call.\n\t\tthis._defaultTopicOptions = { ...(opts.defaultTopicOptions ?? {}) };\n\n\t\t// Tier 8 / COMPOSITION-GUIDE §35: route the registry-delete branch of\n\t\t// `removeTopic` through `mutate` for centralized re-throw\n\t\t// semantics. No audit log surface (per Tier 8 γ-0).\n\t\t// `freeze: false` because the only arg is a string name (freeze pointless).\n\t\t// **Closure-state caveat (γ-4):** the inner `try/finally` mutates\n\t\t// `_registry` (a `Map`) and emits the version bump. mutate has no\n\t\t// `batch()` frame, so reactive emissions are NOT rolled back on throw —\n\t\t// and even if it did, `Map.delete` on closure state is invisible to the\n\t\t// batch and can't be unwound. The pre-existing try/finally on\n\t\t// `Graph.remove` is what guarantees registry/version converge to a\n\t\t// consistent state when `remove()` throws; `mutate` adds nothing\n\t\t// to that contract beyond the re-throw.\n\t\tthis._removeTopicImpl = mutate<[string], void, never>(\n\t\t\t(topicName): void => {\n\t\t\t\ttry {\n\t\t\t\t\tthis.remove(topicName); // unmounts, drops edges, tears down\n\t\t\t\t} finally {\n\t\t\t\t\tthis._registry.delete(topicName);\n\t\t\t\t\tconst cur = (this.version.cache as number) ?? 0;\n\t\t\t\t\tthis.version.emit(cur + 1);\n\t\t\t\t}\n\t\t\t},\n\t\t\t{ frame: \"inline\", freeze: false },\n\t\t);\n\t}\n\n\t/** Number of topics currently in the hub. */\n\tget size(): number {\n\t\treturn this._registry.size;\n\t}\n\n\t/** Checks topic existence without creating. */\n\thas(name: string): boolean {\n\t\treturn this._registry.has(name);\n\t}\n\n\t/** Iterator over topic names. */\n\ttopicNames(): IterableIterator<string> {\n\t\treturn this._registry.keys();\n\t}\n\n\t/**\n\t * Returns the {@link TopicGraph} for `name`, creating lazily on first call.\n\t * Subsequent calls with the same name return the same instance (options on\n\t * repeat calls are ignored — the topic is already configured).\n\t */\n\ttopic<T = unknown>(name: string, opts?: TopicOptions): TopicGraph<T> {\n\t\tlet t = this._registry.get<T>(name);\n\t\tif (t === undefined) {\n\t\t\tconst effective: TopicOptions = { ...this._defaultTopicOptions, ...(opts ?? {}) };\n\t\t\tt = new TopicGraph<T>(name, effective);\n\t\t\tthis._registry.set(name, t);\n\t\t\tthis.mount(name, t);\n\t\t\tconst cur = (this.version.cache as number) ?? 0;\n\t\t\tthis.version.emit(cur + 1);\n\t\t}\n\t\treturn t;\n\t}\n\n\t/**\n\t * Publishes a value to the topic, lazily creating it on first publish.\n\t *\n\t * **Late-subscriber caveat:** the topic is created lazily, so subscribers\n\t * that attach AFTER a publish only see the retained window (governed by\n\t * `retainedLimit` on `TopicOptions` / `defaultTopicOptions`). If\n\t * `retainedLimit === 0` is set explicitly, early publishes are\n\t * effectively dropped — prefer an unset `retainedLimit` (unbounded\n\t * retention) or subscribe before publishing when late-subscribers matter.\n\t */\n\tpublish<T = unknown>(name: string, value: T): void {\n\t\tthis.topic<T>(name).publish(value);\n\t}\n\n\t/**\n\t * Bulk publish — issues all publishes inside one outer batch. New topics\n\t * are created on demand. No-op if `entries` yields nothing.\n\t *\n\t * **Iterable consumption (F6):** `entries` is consumed once (single-pass)\n\t * INSIDE the batch frame. If the iterator throws mid-way, the batch is\n\t * discarded and NO publishes are visible to subscribers (all-or-nothing).\n\t * Pass an array or `Set` for multi-shot callers.\n\t */\n\tpublishMany(entries: Iterable<[string, unknown]>): void {\n\t\t// P2: iterate inside batch — no `[...entries]` materialization so large\n\t\t// / infinite iterables don't OOM, and iterator throws are contained.\n\t\tbatch(() => {\n\t\t\tfor (const [name, value] of entries) {\n\t\t\t\tthis.topic(name).publish(value);\n\t\t\t}\n\t\t});\n\t}\n\n\t/**\n\t * Creates a {@link SubscriptionGraph} over a named topic. The topic is\n\t * lazily created if missing. Subscription lifecycle is owned by the caller —\n\t * the hub does NOT mount the subscription.\n\t *\n\t * @param subName - Local name for the subscription graph.\n\t * @param topicName - Hub topic to subscribe to.\n\t * @param opts - `SubscriptionOptions` (initial cursor, etc.).\n\t */\n\tsubscribe<T = unknown>(\n\t\tsubName: string,\n\t\ttopicName: string,\n\t\topts?: SubscriptionOptions,\n\t): SubscriptionGraph<T> {\n\t\tconst t = this.topic<T>(topicName);\n\t\treturn new SubscriptionGraph<T>(subName, t, opts);\n\t}\n\n\t/**\n\t * Unmounts and tears down the topic's graph. Returns `true` if the topic\n\t * existed. Subscribers receive `TEARDOWN` via {@link Graph.remove}.\n\t *\n\t * **Closure-state caveat:** the registry mutation (`_registry.delete`) and\n\t * version bump happen in a `try/finally`, so registry/version converge to\n\t * a consistent state even when {@link Graph.remove} throws. `mutate`\n\t * does not roll back this mutation on throw — `Map.delete` on closure\n\t * state is invisible to any batch frame. The pre-existing try/finally is\n\t * load-bearing for that invariant.\n\t */\n\tremoveTopic(name: string): boolean {\n\t\tif (!this._registry.has(name)) return false;\n\t\t// P1 / P3: Graph.remove first — if it throws, `_registry` must NOT still\n\t\t// hold the broken half-disposed topic (otherwise the next\n\t\t// `hub.topic(name)` returns the corrupted reference). The `try/finally`\n\t\t// inside `_removeTopicImpl`'s action body preserves that invariant.\n\t\tthis._removeTopicImpl(name);\n\t\treturn true;\n\t}\n}\n\n/**\n * Creates a Pulsar-inspired topic graph (append-only retained stream + latest value).\n */\nexport function topic<T>(name: string, opts?: TopicOptions): TopicGraph<T> {\n\treturn new TopicGraph<T>(name, opts);\n}\n\n/**\n * Creates a lazy Pulsar-inspired messaging hub. Topics are created on first access\n * via `hub.topic(name)`; `hub.publish(name, value)` shortcuts through the registry.\n *\n * @example\n * ```ts\n * import { messagingHub } from \"@graphrefly/graphrefly\";\n *\n * const hub = messagingHub(\"main\", { defaultTopicOptions: { retainedLimit: 256 } });\n * hub.publish(\"orders\", { id: 1 });\n * hub.publishMany([[\"shipments\", { id: 1 }], [\"orders\", { id: 2 }]]);\n * const sub = hub.subscribe(\"orders-worker\", \"orders\", { cursor: 0 });\n * ```\n */\nexport function messagingHub(name: string, opts?: MessagingHubOptions): MessagingHubGraph {\n\treturn new MessagingHubGraph(name, opts);\n}\n\n/**\n * Creates a cursor-based subscription graph over a topic.\n */\nexport function subscription<T>(\n\tname: string,\n\ttopicGraph: TopicGraph<T>,\n\topts?: SubscriptionOptions,\n): SubscriptionGraph<T> {\n\treturn new SubscriptionGraph<T>(name, topicGraph, opts);\n}\n\n/**\n * Creates an autonomous cursor-based topic relay graph.\n *\n * When `opts.map` is provided, items where `map` returns `undefined` are\n * consumed from the source cursor but NOT republished (at-most-once with\n * silent drop). For filter-with-retry semantics, apply the filter in a\n * downstream subscription on the bridge's `output` node instead.\n */\nexport function topicBridge<TIn, TOut = TIn>(\n\tname: string,\n\tsourceTopic: TopicGraph<TIn>,\n\ttargetTopic: TopicGraph<TOut>,\n\topts?: TopicBridgeOptions<TIn, TOut>,\n): TopicBridgeGraph<TIn, TOut> {\n\treturn new TopicBridgeGraph<TIn, TOut>(name, sourceTopic, targetTopic, opts);\n}\n\n// ── LogProjector ──────────────────────────────────────────────────────────\n//\n// Promotion 2 (memo:Re Story 6.4 back-derivation, design-review-locked\n// 2026-05-16). A cursor-driven projector over a log/topic where a per-item\n// sink can poison-fail. memo:Re hand-rolled `createProjectorCursor` and got\n// the failure mode wrong: a bare `catch { break; }` conflated a *transient*\n// condition (a native feature not yet available → retry later) with a\n// *poison* entry (will never project) → a permanent head-of-line block of\n// every newer entry. The fix is a real, observable, subscribable dead-letter\n// topic + a typed failure policy.\n//\n// Built ON `subscription()` (TopicGraph source) / the bundle's `fromCursor`\n// view (ReactiveLogBundle source) — the same hardened cursor machinery\n// `SubscriptionGraph` itself uses — so the consumer never hand-rolls cursor\n// persistence/durability (the Med \"durability skew\" + parse-leniency findings\n// dissolve). Scope is deliberately bounded: `halt | deadLetter` only, NO\n// programmatic retry/backoff (compose a downstream subscription on\n// `deadLetter` for that — avoids the §44 wrap-imperative-as-primitive trap).\n\nexport type ProjectorPoisonPolicy = \"halt\" | \"deadLetter\";\n\n/** A poison entry routed to {@link LogProjectorGraph.deadLetter}. */\nexport type DeadLetterEntry<T> = {\n\treadonly item: T;\n\t/** `Error.message` (or `String(thrown)`) from the failing `sink`. */\n\treadonly error: string;\n\t/** Absolute 0-based log position of the poisoned item. */\n\treadonly cursorPos: number;\n};\n\nexport type LogProjectorOptions<T> = {\n\tgraph?: GraphOptions;\n\t/**\n\t * Per-item side-effecting projection.\n\t *\n\t * **Transient-vs-poison contract (read this).** The projector cannot tell\n\t * \"this will project later\" from \"this will never project\" — only the sink\n\t * knows. So the contract is:\n\t * - **Return normally** (sync return, or a resolved Promise) → the item is\n\t * *handled*; the cursor advances. Use this for the success path AND for\n\t * any transient/skip condition the sink wants to no-op (e.g. an optional\n\t * native feature not yet available — return without throwing; the entry\n\t * is simply considered done for this projector).\n\t * - **Throw / reject** → the item is *poison*; the {@link onPoison} policy\n\t * applies. Do NOT throw for transient conditions or the item is\n\t * dead-lettered (or halts the stream).\n\t */\n\treadonly sink: (item: T) => void | Promise<void>;\n\t/**\n\t * Behaviour when `sink` throws/rejects on an item (poison):\n\t * - `\"halt\"` (default) — stop projecting at the poison item; the cursor\n\t * does NOT advance past it (head-of-line stop). `position` stalls — that\n\t * is the observable signal. No retry/backoff is built in.\n\t * - `\"deadLetter\"` — publish `{ item, error, cursorPos }` to the\n\t * {@link LogProjectorGraph.deadLetter} topic and advance past it, so\n\t * newer entries still project.\n\t */\n\treadonly onPoison?: ProjectorPoisonPolicy;\n\t/**\n\t * Initial cursor position. `\"retained\"` (default) projects all history;\n\t * `\"now\"` skips existing entries (project only future appends); a number\n\t * starts at that absolute index.\n\t */\n\treadonly from?: \"retained\" | \"now\" | number;\n\t/** Retained window for the `deadLetter` topic. Default 1024. */\n\treadonly deadLetterRetainedLimit?: number;\n};\n\n/**\n * Cursor-driven projector over a {@link TopicGraph} or {@link ReactiveLogBundle}.\n *\n * Topology (mounted on the returned graph):\n * - `subscription` (TopicGraph source only) — the hardened\n * {@link SubscriptionGraph} cursor; or a local `cursor` state + the\n * bundle's `fromCursor` view (ReactiveLogBundle source).\n * - `drain` — an `effect` that, on every not-yet-projected wave, schedules a\n * serialized async pass that calls `sink` per item (mirrors the\n * `SubscriptionGraph.ackPump` / `TopicBridge.ackPump` effect precedent +\n * memo:Re's `inFlight` chain — one wave processed at a time).\n * - `deadLetter` — a real {@link TopicGraph} (NOT a callback): poison entries\n * are observable in `describe()` and subscribable, instead of memo:Re's\n * silent `break`.\n *\n * **No imperative reads.** Observe `position` (cursor) / subscribe to\n * `deadLetter`. `idle()` is a test-only await convenience.\n *\n * @category patterns\n */\nexport class LogProjectorGraph<T> extends Graph {\n\t/** Reactive count of fully-projected entries (the cursor; read-only). */\n\treadonly position: Node<number>;\n\t/**\n\t * Poison entries (populated when `onPoison: \"deadLetter\"`). A real topic —\n\t * subscribable + visible in `describe()`.\n\t */\n\treadonly deadLetter: TopicGraph<DeadLetterEntry<T>>;\n\tprivate _inFlight: Promise<void> = Promise.resolve();\n\n\tconstructor(\n\t\tname: string,\n\t\tsource: TopicGraph<T> | ReactiveLogBundle<T>,\n\t\topts: LogProjectorOptions<T>,\n\t) {\n\t\tsuper(name, opts.graph);\n\t\tconst onPoison: ProjectorPoisonPolicy = opts.onPoison ?? \"halt\";\n\t\tconst sink = opts.sink;\n\n\t\tconst dl = new TopicGraph<DeadLetterEntry<T>>(`${name}_dead_letter`, {\n\t\t\tretainedLimit: opts.deadLetterRetainedLimit ?? DEFAULT_TOPIC_RETAINED_LIMIT,\n\t\t});\n\t\tthis.mount(\"deadLetter\", dl);\n\t\tthis.deadLetter = dl;\n\n\t\t// Uniform cursor surface over either source kind. A TopicGraph reuses\n\t\t// the hardened SubscriptionGraph cursor; a ReactiveLogBundle uses a\n\t\t// local state cursor + the bundle's `fromCursor` view — the very\n\t\t// machinery SubscriptionGraph is itself built on.\n\t\tlet available: Node<readonly T[]>;\n\t\tlet cursorBase: () => number;\n\t\tlet advance: (n: number) => void;\n\n\t\tif (source instanceof TopicGraph) {\n\t\t\tconst sub = new SubscriptionGraph<T>(`${name}_subscription`, source, {\n\t\t\t\tfrom: opts.from ?? \"retained\",\n\t\t\t});\n\t\t\tthis.mount(\"subscription\", sub);\n\t\t\tavailable = sub.available;\n\t\t\tthis.position = sub.cursor;\n\t\t\tcursorBase = () => sub.cursor.cache as number;\n\t\t\tadvance = (n) => {\n\t\t\t\tif (n > 0) sub.ack(n);\n\t\t\t};\n\t\t} else {\n\t\t\tconst log = source;\n\t\t\tlet initialCursor: number;\n\t\t\tif (opts.from === \"now\") {\n\t\t\t\tinitialCursor = log.size;\n\t\t\t} else if (typeof opts.from === \"number\") {\n\t\t\t\tinitialCursor = requireNonNegativeInt(opts.from, \"logProjector from\");\n\t\t\t} else {\n\t\t\t\tinitialCursor = 0; // \"retained\"\n\t\t\t}\n\t\t\tconst cursor = this.state<number>(\"cursor\", initialCursor, {\n\t\t\t\tmeta: messagingMeta(\"log_projector_cursor\"),\n\t\t\t});\n\t\t\tthis.position = cursor;\n\t\t\tcursorBase = () => cursor.cache as number;\n\t\t\tavailable = log.view({ kind: \"fromCursor\", cursor });\n\t\t\tadvance = (n) => {\n\t\t\t\tif (n > 0) cursor.emit((cursor.cache as number) + n);\n\t\t\t};\n\t\t}\n\n\t\t// `halt` is a HARD LATCH (QA-C): on the first poison under `onPoison:\n\t\t// \"halt\"`, `sink` has been invoked exactly once on the poison item;\n\t\t// the projector then freezes — no further `sink` calls, no rescheduled\n\t\t// drains — so a later unrelated append cannot re-invoke the (possibly\n\t\t// side-effecting, non-idempotent) sink on the poison. The stalled\n\t\t// `position` + frozen stream IS the observable signal. v1 has no retry\n\t\t// (compose downstream of `deadLetter` if you need that).\n\t\tlet halted = false;\n\n\t\t// Serialized async drain. One wave processed at a time (the inFlight\n\t\t// chain) so an async `sink` cannot interleave; the cursor is advanced\n\t\t// ONCE per pass after the captured snapshot is processed (mirrors\n\t\t// memo:Re's `runPump` + the ackPump effect precedent).\n\t\tconst runDrain = async (): Promise<void> => {\n\t\t\tif (halted) return;\n\t\t\tconst snapshot = (available.cache as readonly T[] | undefined) ?? [];\n\t\t\tif (snapshot.length === 0) return;\n\t\t\tlet consumed = 0;\n\t\t\tfor (let i = 0; i < snapshot.length; i += 1) {\n\t\t\t\tconst item = snapshot[i] as T;\n\t\t\t\ttry {\n\t\t\t\t\tawait sink(item);\n\t\t\t\t\tconsumed += 1;\n\t\t\t\t} catch (e) {\n\t\t\t\t\tconst error = e instanceof Error ? e.message : String(e);\n\t\t\t\t\tif (onPoison === \"deadLetter\") {\n\t\t\t\t\t\tdl.publish({ item, error, cursorPos: cursorBase() + consumed });\n\t\t\t\t\t\tconsumed += 1; // advance past the poison\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t\t// \"halt\" — latch and stop here; do NOT advance past the\n\t\t\t\t\t// poison, do NOT re-invoke `sink` on any later wave.\n\t\t\t\t\thalted = true;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (consumed > 0) advance(consumed);\n\t\t};\n\t\tconst schedule = (): void => {\n\t\t\tif (halted) return; // latched — no further drains\n\t\t\tthis._inFlight = this._inFlight.then(runDrain, runDrain);\n\t\t};\n\n\t\t// Effect: every wave that exposes not-yet-projected entries schedules a\n\t\t// drain. Side-effecting (sink / cursor advance / dead-letter publish) →\n\t\t// an `effect` node, not a pure derived (COMPOSITION-GUIDE §35; exact\n\t\t// `SubscriptionGraph.advancePump` precedent — never calls `emit` on its\n\t\t// own node, kept warm via `keepalive`).\n\t\tconst drain = node<unknown>(\n\t\t\t[available],\n\t\t\t(batchData, _actions, ctx) => {\n\t\t\t\tconst b = batchData[0];\n\t\t\t\tconst snap = (b != null && b.length > 0 ? b.at(-1) : ctx.prevData[0]) as\n\t\t\t\t\t| readonly T[]\n\t\t\t\t\t| undefined;\n\t\t\t\tif (snap && snap.length > 0) schedule();\n\t\t\t},\n\t\t\t{\n\t\t\t\tname: \"drain\",\n\t\t\t\tdescribeKind: \"effect\",\n\t\t\t\tmeta: messagingMeta(\"log_projector_drain\"),\n\t\t\t},\n\t\t);\n\t\tthis.add(drain, { name: \"drain\" });\n\t\tthis.addDisposer(keepalive(drain));\n\t}\n\n\t/**\n\t * Await any in-flight drain pass. **Test convenience only** — the canonical\n\t * reactive observable is {@link LogProjectorGraph.position}.\n\t */\n\tidle(): Promise<void> {\n\t\treturn this._inFlight;\n\t}\n}\n\n/**\n * Creates a cursor-driven log/topic projector with a typed poison-failure\n * policy and an observable dead-letter topic.\n *\n * @example\n * ```ts\n * import { logProjector, topic } from \"@graphrefly/graphrefly\";\n *\n * const events = topic<Doc>(\"docs\");\n * const proj = logProjector(\"indexer\", events, {\n * sink: async (doc) => { await index(doc); }, // throw ⇒ poison\n * onPoison: \"deadLetter\",\n * });\n * proj.deadLetter.events.subscribe(/* observe poison *​/);\n * ```\n *\n * @remarks\n * **Use an UNBOUNDED source for durable / long-lived projection.** The cursor\n * is an absolute index; the underlying `fromCursor` view slices the source's\n * *current* entries array. A `TopicGraph` with a `retainedLimit` (or a\n * `ReactiveLogBundle` with `maxSize`) trims its head, so an absolute cursor\n * past the retained window reads the wrong offset (skips entries or stalls).\n * This is inherited `subscription()` / `fromCursor` behaviour, not specific to\n * `logProjector` — but it matters here because projection is typically\n * long-lived. For unbounded projection pass a source with NO `retainedLimit` /\n * `maxSize` (memo:Re's `changesetLog` is unbounded ✓).\n *\n * @category patterns\n */\nexport function logProjector<T>(\n\tname: string,\n\tsource: TopicGraph<T> | ReactiveLogBundle<T>,\n\topts: LogProjectorOptions<T>,\n): LogProjectorGraph<T> {\n\treturn new LogProjectorGraph<T>(name, source, opts);\n}\n","/**\n * Universal mutation framework (Phase 14 — DS-14 locked 2026-05-05).\n *\n * Single `mutate(act, opts)` factory replaces the prior `lightMutation` +\n * `wrapMutation` two-tier split (pre-1.0 break per Q-O2).\n *\n * Two frames:\n * - `\"inline\"` — no batch; up() runs raw. Seq bumps before action; persists\n * on throw. Hot-path-friendly for atomic single-write mutations.\n * - `\"transactional\"` — opens `batch(() => up(...))`. On throw: batch discards\n * deferred deliveries, then `down()` runs (if provided), then failure record.\n *\n * Phase-4 primitives share the same shape: imperative mutation methods +\n * closure state + reactive audit log + freeze-at-entry + rollback-on-throw.\n * This module factors out the common machinery so each primitive becomes\n * declarative wiring over typed audit records.\n */\n\nimport {\n\tbatch,\n\tDATA,\n\tDIRTY,\n\ttype Node,\n\ttype NodeGuard,\n\tnode,\n\tpolicy,\n\twallClockNs,\n} from \"@graphrefly/pure-ts/core\";\nimport {\n\ttype ReactiveLogBundle,\n\ttype ReactiveLogOptions,\n\treactiveLog,\n} from \"@graphrefly/pure-ts/extra\";\nimport { Graph } from \"@graphrefly/pure-ts/graph\";\n\n// ── tryIncrementBounded ──────────────────────────────────────────────────\n\n/**\n * Bounded increment for a self-owned counter state node.\n *\n * Reads `counter.cache`, bumps by `by` (default 1) if `cur + by <= cap`,\n * writes back. Returns `false` when the cap would be exceeded (no-op write).\n * Documented P3 exception: the counter is not a declared dep of the caller —\n * it's a private budget read+written from a single call site. This helper\n * keeps the `.cache` access in one named place so caller bodies (which may\n * be inside reactive fn execution paths) stay free of cross-node `.cache`\n * reads.\n *\n * **Safety today:**\n * 1. Single-threaded JS runner never invokes the caller concurrently.\n * 2. `counter.down` writes the cache synchronously before returning, so\n * synchronous re-entry through a downstream publish reads the\n * freshly-incremented value — no double-count.\n *\n * **Future risk:** under a free-threaded runner (PY no-GIL or hypothetical\n * concurrent TS runner), two concurrent firings could still race. Revisit\n * when that surfaces.\n *\n * @param counter - Self-owned counter Node. Caller is the sole writer.\n * @param cap - Upper bound (inclusive). Pass `Number.MAX_SAFE_INTEGER` for\n * \"effectively unbounded\" use cases (e.g. token meters).\n * @param by - Delta to add (default `1`). Must be a finite non-negative\n * number; callers should pre-validate. Overflow-safe via\n * `by > cap - cur` check rather than `cur + by >= cap`.\n */\nexport function tryIncrementBounded(counter: Node<number>, cap: number, by = 1): boolean {\n\tconst cur = (counter.cache as number | undefined) ?? 0;\n\tif (by > cap - cur) return false;\n\tcounter.down([[DIRTY], [DATA, cur + by]]);\n\treturn true;\n}\n\n// ── Audit record schema ──────────────────────────────────────────────────\n\n/** Shared base shape for every audit record. Per-primitive types extend this. */\nexport interface BaseAuditRecord {\n\treadonly t_ns: number;\n\treadonly seq?: number;\n\treadonly handlerVersion?: { id: string; version: string | number };\n}\n\n// ── Default audit guard ──────────────────────────────────────────────────\n\n/**\n * Allow `observe` and `signal`; deny external `write` on the audit log so\n * consumers can subscribe + signal-bridge but cannot inject fake records.\n */\nexport const DEFAULT_AUDIT_GUARD: NodeGuard = policy((allow, deny) => {\n\tallow(\"observe\");\n\tallow(\"signal\");\n\tdeny(\"write\");\n});\n\n// ── createAuditLog ───────────────────────────────────────────────────────\n\nexport type AuditLogOpts<R extends BaseAuditRecord> = {\n\tname: string;\n\t/** Bounded retention; default 1024 per Audit 2 / cross-cutting bounded-default policy. */\n\tretainedLimit?: number;\n\t/** Override the default audit guard. */\n\tguard?: NodeGuard;\n\t/** Mount the audit `entries` Node under this graph (and activate withLatest). */\n\tgraph?: Graph;\n\t/** Pass-through to {@link reactiveLog}. */\n\tversioning?: ReactiveLogOptions<R>[\"versioning\"];\n};\n\n/**\n * Build a reactive audit log with sane defaults: bounded retention, deny-write\n * guard, `withLatest()` companions activated. Returns the {@link ReactiveLogBundle}\n * directly — primitives expose this as `<primitive>.events` / `.decisions` /\n * `.dispatches` / `.invocations` and alias it as `.audit`.\n *\n * @category internal\n */\nexport function createAuditLog<R extends BaseAuditRecord>(\n\topts: AuditLogOpts<R>,\n): ReactiveLogBundle<R> {\n\tconst log = reactiveLog<R>([], {\n\t\tname: opts.name,\n\t\tmaxSize: opts.retainedLimit ?? 1024,\n\t\tguard: opts.guard ?? DEFAULT_AUDIT_GUARD,\n\t\t...(opts.versioning != null ? { versioning: opts.versioning } : {}),\n\t});\n\t// Lazy companion activation up-front so `bundle.lastValue` / `hasLatest`\n\t// are queryable without an explicit `withLatest()` call.\n\tlog.withLatest();\n\tif (opts.graph) {\n\t\topts.graph.add(log.entries, { name: opts.name });\n\t}\n\treturn log;\n}\n\n/**\n * Read-only projection of a {@link ReactiveLogBundle}. Exposes only the\n * observation surface (`entries` / `size` / `at` / `withLatest` / `lastValue`\n * / `view` / `scan` / `mutationLog`) — the mutators (`append` / `appendMany`\n * / `clear` / `trimHead` / `attach` / `attachStorage`) and the lifecycle\n * disposers are intentionally absent.\n */\nexport type ReadonlyAuditLog<R> = Pick<\n\tReactiveLogBundle<R>,\n\t\"entries\" | \"size\" | \"at\" | \"withLatest\" | \"lastValue\" | \"view\" | \"scan\" | \"mutationLog\"\n>;\n\n/**\n * Wrap an audit log so the `.audit` alias a primitive exposes (the Audit-2\n * `.audit` duplication convention — `saga.audit` / `cqrs.audit` /\n * `jobQueue.audit` / `processManager.audit`) is a **stable read-only view**,\n * not the live mutable bundle. Closes M7: a consumer calling\n * `someGraph.audit.append(...)` would otherwise silently mutate the owning\n * primitive's canonical log. The returned object is frozen and shares the\n * underlying log's nodes (zero copy) — reads are byte-identical to the live\n * bundle; mutators are simply not present (compile-time) and the object is\n * frozen (run-time defense for JS callers).\n *\n * @category internal\n */\nexport function readonlyAuditLog<R>(log: ReactiveLogBundle<R>): ReadonlyAuditLog<R> {\n\treturn Object.freeze({\n\t\tget entries() {\n\t\t\treturn log.entries;\n\t\t},\n\t\tget size() {\n\t\t\treturn log.size;\n\t\t},\n\t\tget lastValue() {\n\t\t\treturn log.lastValue;\n\t\t},\n\t\tget mutationLog() {\n\t\t\treturn log.mutationLog;\n\t\t},\n\t\tat: log.at.bind(log),\n\t\twithLatest: log.withLatest.bind(log),\n\t\tview: log.view.bind(log),\n\t\tscan: log.scan.bind(log),\n\t}) satisfies ReadonlyAuditLog<R>;\n}\n\n// ── Universal mutation factory (Phase 14 — DS-14 lock Q-O2/Q-O3) ────────\n//\n// Single `mutate(act, opts)` factory. Two frames:\n//\n// - `\"inline\"` — no batch frame; up() runs raw. Seq bumps before action;\n// persists on throw. Hot-path-friendly for atomic single-write mutations.\n//\n// - `\"transactional\"` — opens `batch(() => up(...))`. On throw: batch discards\n// deferred deliveries, then `down()` runs, then failure record persists.\n//\n// **Heuristic:** if your imperative method's body is one or two lines (mutate\n// state, emit), use `frame: \"inline\"`. If it runs a user-supplied handler or\n// has multiple steps that could leave inconsistent state mid-throw, use\n// `frame: \"transactional\"`.\n\nexport type FailureMeta = {\n\tt_ns: number;\n\tseq?: number;\n\terrorType: string;\n};\n\nexport type SuccessMeta = {\n\tt_ns: number;\n\tseq?: number;\n};\n\n/**\n * Mutation action shape. Plain function shorthand auto-wraps as `{ up: fn }`.\n *\n * - `up` — the mutation action (the \"up migration\").\n * - `down` — optional rollback for closure mutations that `batch()` can't\n * reach. Receives the SAME frozen args as `up`. Runs AFTER batch reactive\n * rollback, BEFORE the failure record. Throws inside `down` are\n * console.error'd without masking the original error. Only meaningful\n * with `frame: \"transactional\"`.\n */\nexport type MutationAct<TArgs extends readonly unknown[], TResult> = {\n\tup: (...args: TArgs) => TResult;\n\tdown?: (...args: TArgs) => void;\n};\n\nexport type MutationFrame = \"inline\" | \"transactional\";\n\nexport type MutateOpts<TArgs extends readonly unknown[], TResult, R extends BaseAuditRecord> = {\n\t/** Frame mode. `\"inline\"` = no batch; `\"transactional\"` = batch + rollback. */\n\tframe: MutationFrame;\n\t/**\n\t * Optional log to append records to. When omitted, the wrapper still\n\t * provides freeze / seq-advance / rollback-on-throw but skips record\n\t * emission — useful for primitives that want centralized mutation\n\t * semantics without a dedicated log surface (e.g. `Topic.publish`).\n\t */\n\tlog?: ReactiveLogBundle<R>;\n\t/** Build the success record from the action's args + result + meta. */\n\tonSuccessRecord?: (args: TArgs, result: TResult, meta: SuccessMeta) => R | undefined;\n\t/** Build the failure record from the args + error + meta. */\n\tonFailureRecord?: (args: TArgs, error: unknown, meta: FailureMeta) => R | undefined;\n\t/** Deep-freeze args at entry (default `true`). Opt out for hot paths. */\n\tfreeze?: boolean;\n\t/** Optional sequence cursor — auto-advanced and stamped onto records. */\n\tseq?: Node<number>;\n\t/** Optional handler version — stamped per Audit 5. */\n\thandlerVersion?: { id: string; version: string | number };\n};\n\nfunction deepFreeze<T>(value: T): T {\n\tif (value === null || typeof value !== \"object\" || Object.isFrozen(value)) return value;\n\tfor (const k of Object.keys(value as Record<string, unknown>)) {\n\t\tdeepFreeze((value as Record<string, unknown>)[k]);\n\t}\n\treturn Object.freeze(value);\n}\n\n/**\n * Universal mutation factory (Phase 14 — DS-14 Q-O2).\n *\n * Replaces the prior `lightMutation` + `wrapMutation` two-tier split.\n * Single factory with `frame: \"inline\" | \"transactional\"` discriminant.\n *\n * @param act - The mutation action. Either a plain function (auto-wrapped as\n * `{ up: fn }`) or a `{ up, down? }` object for explicit rollback.\n * @param opts - Configuration: frame, log, record builders, freeze, seq.\n * @returns A typed wrapper function with the same signature as `act.up`.\n */\nexport function mutate<TArgs extends readonly unknown[], TResult, R extends BaseAuditRecord>(\n\tact: MutationAct<TArgs, TResult> | ((...args: TArgs) => TResult),\n\topts: MutateOpts<TArgs, TResult, R>,\n): (...args: TArgs) => TResult {\n\tconst { up, down } = typeof act === \"function\" ? { up: act, down: undefined } : act;\n\tconst freeze = opts.freeze ?? true;\n\n\tif (opts.frame === \"inline\") {\n\t\treturn function wrapped(...args: TArgs): TResult {\n\t\t\tconst sealed = freeze ? (args.map(deepFreeze) as unknown as TArgs) : args;\n\t\t\tconst t_ns = wallClockNs();\n\t\t\tconst seq = opts.seq ? bumpCursor(opts.seq) : undefined;\n\t\t\ttry {\n\t\t\t\tconst result = up(...sealed);\n\t\t\t\tif (opts.log && opts.onSuccessRecord) {\n\t\t\t\t\tappendAudit<TArgs, TResult, R, SuccessMeta>(\n\t\t\t\t\t\topts.log,\n\t\t\t\t\t\topts.onSuccessRecord,\n\t\t\t\t\t\tsealed,\n\t\t\t\t\t\tresult,\n\t\t\t\t\t\t{ t_ns, seq },\n\t\t\t\t\t\topts.handlerVersion,\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\treturn result;\n\t\t\t} catch (err) {\n\t\t\t\tif (opts.log && opts.onFailureRecord) {\n\t\t\t\t\tconst errorType = err instanceof Error ? err.name : typeof err;\n\t\t\t\t\tappendAudit<TArgs, unknown, R, FailureMeta>(\n\t\t\t\t\t\topts.log,\n\t\t\t\t\t\topts.onFailureRecord,\n\t\t\t\t\t\tsealed,\n\t\t\t\t\t\terr,\n\t\t\t\t\t\t{ t_ns, seq, errorType },\n\t\t\t\t\t\topts.handlerVersion,\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\tthrow err;\n\t\t\t}\n\t\t};\n\t}\n\n\t// frame === \"transactional\"\n\treturn function wrapped(...args: TArgs): TResult {\n\t\tconst sealed = freeze ? (args.map(deepFreeze) as unknown as TArgs) : args;\n\t\tconst t_ns = wallClockNs();\n\t\tlet result: TResult;\n\t\tlet captured: unknown;\n\t\tlet captureSet = false;\n\t\tlet seq: number | undefined;\n\t\ttry {\n\t\t\tbatch(() => {\n\t\t\t\tif (opts.seq) seq = bumpCursor(opts.seq);\n\t\t\t\ttry {\n\t\t\t\t\tresult = up(...sealed);\n\t\t\t\t\tif (opts.log && opts.onSuccessRecord) {\n\t\t\t\t\t\tappendAudit<TArgs, TResult, R, SuccessMeta>(\n\t\t\t\t\t\t\topts.log,\n\t\t\t\t\t\t\topts.onSuccessRecord,\n\t\t\t\t\t\t\tsealed,\n\t\t\t\t\t\t\tresult,\n\t\t\t\t\t\t\t{ t_ns, seq },\n\t\t\t\t\t\t\topts.handlerVersion,\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\t\t\t\t} catch (err) {\n\t\t\t\t\tcaptured = err;\n\t\t\t\t\tcaptureSet = true;\n\t\t\t\t\tthrow err;\n\t\t\t\t}\n\t\t\t});\n\t\t} catch (outerErr) {\n\t\t\t// Fire `down` AFTER batch's reactive rollback, BEFORE failure record.\n\t\t\t// Gate on `captureSet` — if the throw came from outside the inner try\n\t\t\t// (framework-level batch error before action ran), don't fire down.\n\t\t\tif (captureSet && down) {\n\t\t\t\ttry {\n\t\t\t\t\tdown(...sealed);\n\t\t\t\t} catch (downErr) {\n\t\t\t\t\tconsole.error(\n\t\t\t\t\t\t`mutate: down hook threw — original action error preserved (${\n\t\t\t\t\t\t\tcaptured instanceof Error ? captured.name : typeof captured\n\t\t\t\t\t\t}). Down error:`,\n\t\t\t\t\t\tdownErr,\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (captureSet && opts.log && opts.onFailureRecord) {\n\t\t\t\tconst errorType = captured instanceof Error ? captured.name : typeof captured;\n\t\t\t\tappendAudit<TArgs, unknown, R, FailureMeta>(\n\t\t\t\t\topts.log,\n\t\t\t\t\topts.onFailureRecord,\n\t\t\t\t\tsealed,\n\t\t\t\t\tcaptured,\n\t\t\t\t\t{ t_ns, seq, errorType },\n\t\t\t\t\topts.handlerVersion,\n\t\t\t\t);\n\t\t\t}\n\t\t\tthrow captureSet ? captured : outerErr;\n\t\t}\n\t\treturn result!;\n\t};\n}\n\n/**\n * Advance a cursor node and return the new value. Emits `[DIRTY], [DATA, next]`\n * directly on the cursor — atomic outside a batch, rollback-discardable inside.\n *\n * Resets to `0` if the cursor cache is missing, non-numeric, `NaN`, or\n * non-finite (e.g. corrupted by `restore()` from a malformed snapshot, or\n * by a misbehaving codec). `??` alone would let `NaN` and `\"\"` pass through\n * and silently corrupt audit ordering downstream.\n *\n * **Silent reset diagnostic (EH-12).** When the cache holds a non-numeric\n * value at bump time, the cursor restarts at 0 and the next bump returns 1\n * — colliding with the seq stamped on the very first record after construct.\n * To make seq-monotonicity violations after a restore visible to operators,\n * the helper emits a one-shot `console.warn` per cursor instance describing\n * the offending value. The cursor is identified by a `WeakSet<Node<number>>`\n * so the warning fires exactly once per node — repeat malformed bumps stay\n * quiet to avoid log spam. Production callers wanting to suppress can swap\n * the global `console` (universal-safe code path; no Node-only API used).\n *\n * Works whether or not the cursor has any subscribers — `down` updates the\n * cache regardless, so primitives that bump before consumers attach (e.g.\n * `JobQueueGraph.enqueue`) still see a coherent sequence.\n *\n * @category internal\n */\nconst _bumpCursorWarned = new WeakSet<Node<number>>();\nexport function bumpCursor(seq: Node<number>): number {\n\tconst raw = seq.cache;\n\tconst valid = typeof raw === \"number\" && Number.isFinite(raw);\n\tif (!valid && raw !== undefined && !_bumpCursorWarned.has(seq)) {\n\t\t_bumpCursorWarned.add(seq);\n\t\tconsole.warn(\n\t\t\t`bumpCursor: cursor cache held a non-numeric value (${String(raw)}); resetting to 0. ` +\n\t\t\t\t\"Causes include: a snapshot codec round-tripping the cursor as a string / null / NaN, \" +\n\t\t\t\t\"OR a malformed initial seed (e.g. state<number>(NaN)). \" +\n\t\t\t\t\"Audit consumers may see colliding seq values after this point.\",\n\t\t);\n\t}\n\tconst cur = valid ? raw : 0;\n\tconst next = cur + 1;\n\tseq.down([[DIRTY], [DATA, next]]);\n\treturn next;\n}\n\n/**\n * Build a record via the supplied builder, stamp `handlerVersion` if present,\n * and append it to the audit log. `undefined` records are skipped (callers\n * pass an `onSuccess` / `onFailure` that returns `undefined` to opt out per\n * call).\n *\n * @category internal\n */\nexport function appendAudit<\n\tTArgs extends readonly unknown[],\n\tTValue,\n\tR extends BaseAuditRecord,\n\tM extends SuccessMeta | FailureMeta,\n>(\n\taudit: ReactiveLogBundle<R>,\n\tbuilder: (args: TArgs, value: TValue, meta: M) => R | undefined,\n\targs: TArgs,\n\tvalue: TValue,\n\tmeta: M,\n\thandlerVersion?: { id: string; version: string | number },\n): void {\n\tconst record = builder(args, value, meta);\n\tif (record === undefined) return;\n\tconst stamped = handlerVersion != null ? ({ ...record, handlerVersion } as R) : record;\n\taudit.append(stamped);\n}\n\n// ── registerCursor / registerCursorMap ───────────────────────────────────\n\n/**\n * Promote a closure counter to a state node mounted under `graph`.\n * Replaces ad-hoc `let _seq = 0` patterns with a node observable in\n * `describe()` and persistable via storage tiers.\n *\n * @category internal\n */\nexport function registerCursor(graph: Graph, name: string, initial = 0): Node<number> {\n\tconst cursor = node<number>([], { initial, name, describeKind: \"state\" });\n\tgraph.add(cursor, { name });\n\treturn cursor;\n}\n\n/**\n * Promote a closure `Map<K, number>` to N state nodes (one per key) mounted\n * under `<graph>::<name>::<key>`. Used by saga (per-event-type cursor).\n *\n * @category internal\n */\nexport function registerCursorMap<K extends string>(\n\tgraph: Graph,\n\tname: string,\n\tkeys: readonly K[],\n\tinitial = 0,\n): { readonly [P in K]: Node<number> } {\n\tconst out = {} as { [P in K]: Node<number> };\n\t// Mount cursors under a child plain-Graph so per-key node names stay flat\n\t// (path-separator `::` is reserved by Graph.add). Using `Graph` directly\n\t// rather than `graph.constructor` avoids spawning a typed subclass with\n\t// an incompatible constructor signature (e.g., CqrsGraph(name, opts)).\n\tconst sub = new Graph(name);\n\tfor (const k of keys) {\n\t\tconst cursor = node<number>([], {\n\t\t\tinitial,\n\t\t\tname: k,\n\t\t\tdescribeKind: \"state\",\n\t\t});\n\t\tsub.add(cursor, { name: k });\n\t\tout[k] = cursor;\n\t}\n\tgraph.mount(name, sub);\n\treturn out;\n}\n","/**\n * Reactive graph observability preset (Tier 5.3 reshape — Session A.4 lock).\n *\n * `graphLens(target)` is a thin compositor (~30 LOC of glue) over two\n * already-shipped reactive primitives:\n *\n * - `target.describe({ reactive: true })` — live topology snapshot.\n * - `target.observe({ reactive: true, tiers: [\"data\"] })` — coalesced data\n * changesets per outermost batch flush.\n *\n * It returns three Nodes plus a dispose function:\n *\n * - `topology: Node<GraphDescribeOutput>` — the live describe snapshot,\n * re-emitting on every settle of the target tree (structural change,\n * error/complete/teardown transition).\n * - `health: Node<HealthReport>` — `{ok, problems[]}` aggregated from the\n * live describe; `ok=false` when any node enters `\"errored\"` status,\n * with `upstreamCause` walked backward through deps.\n * - `flow: Node<ReadonlyMap<string, FlowEntry>>` — per-path DATA counters\n * accumulating as `target.observe({reactive: true, tiers: [\"data\"]})`\n * delivers changesets. The map is reconciled against `topology` on each\n * topology change so removed nodes drop their entries.\n *\n * Pre-Tier-5.3 shipped a `LensGraph` class with `stats` / `health` / `flow`\n * (as a `ReactiveMapBundle`) / `why(from, to)` / `flowEntryNode(...)`. The\n * surface collapsed because every concern was already a one-liner over\n * `describe({reactive:true})` + `observe({reactive:true, tiers})` once those\n * landed in Tier 1.5.1 / 1.5.2 — the class added no protocol-level concept,\n * just glue. Callers who need topology stats compose a derived over\n * `topology` directly; callers who need a causal chain call\n * `target.describe({ explain: { from, to }, reactive: true })`.\n *\n * The transitive topology-subscription helper {@link watchTopologyTree} is\n * re-exported here for downstream factories that need full-tree dynamic\n * coverage without taking a dep on `graph/`.\n *\n * @module\n */\nimport { type Node, node, wallClockNs } from \"@graphrefly/pure-ts/core\";\nimport {\n\tkeepalive,\n\ttype LensFlowChange,\n\ttype LensFlowChangePayload,\n\ttype ReactiveLogBundle,\n\treactiveLog,\n} from \"@graphrefly/pure-ts/extra\";\nimport {\n\ttype Graph,\n\ttype GraphDescribeOutput,\n\ttype ObserveChangeset,\n\ttype ObserveEvent,\n\treachable,\n} from \"@graphrefly/pure-ts/graph\";\nimport { domainMeta } from \"../../base/meta/domain-meta.js\";\n\nexport { watchTopologyTree } from \"@graphrefly/pure-ts/graph\";\n\n// ---------------------------------------------------------------------------\n// Payload types\n// ---------------------------------------------------------------------------\n\n/** A single health problem entry. */\nexport interface HealthProblem {\n\tpath: string;\n\t/** V1 only reports `\"errored\"`. Future versions may add `\"completed\"`, `\"disconnected\"`. */\n\tstatus: \"errored\";\n\t/** First errored upstream ancestor along the dep chain, when one exists and is distinct from `path`. */\n\tupstreamCause?: string;\n}\n\n/** Aggregate health snapshot. `ok=true` iff `problems.length === 0`. */\nexport interface HealthReport {\n\tok: boolean;\n\tproblems: readonly HealthProblem[];\n}\n\n/** Per-path flow entry stored in the {@link GraphLensView.flow} map. */\nexport interface FlowEntry {\n\tpath: string;\n\t/** Cumulative DATA emissions observed since the lens activated. */\n\tcount: number;\n\t/** Monotonic ns at the most recent DATA emission for this path, or `null` if none yet. */\n\tlastUpdate_ns: number | null;\n}\n\n/**\n * Options for {@link graphLens}.\n *\n * Shape mirrors `reactiveMap`/`pubsub`'s `mutationLog` option so the\n * delta-companion ergonomics are uniform across structures.\n */\nexport interface GraphLensOptions {\n\t/**\n\t * Enable the {@link GraphLensView.flowMutations} delta companion log.\n\t * `true` for defaults, or `{ maxSize, name }` to bound / name the\n\t * underlying `reactiveLog`. Off by default — zero overhead (no buffer\n\t * accumulation, no companion node) when omitted.\n\t */\n\tmutations?: true | { maxSize?: number; name?: string };\n}\n\n/** Output of {@link graphLens}. */\nexport interface GraphLensView {\n\t/** Live describe snapshot. Re-emits on structural change AND status transitions. */\n\ttopology: Node<GraphDescribeOutput>;\n\t/** Live `{ok, problems[]}`. Aggregated from `topology`; equality-deduped. */\n\thealth: Node<HealthReport>;\n\t/** Per-path DATA counter map. Re-emits per outermost batch flush. */\n\tflow: Node<ReadonlyMap<string, FlowEntry>>;\n\t/**\n\t * Delta companion to {@link flow}. Present iff {@link GraphLensOptions.mutations}\n\t * was configured. Each per-path `tick` (a DATA emission incremented\n\t * the counter) and each `evict` (a path dropped during topology\n\t * reconciliation) appends one typed {@link LensFlowChange} record in\n\t * the same batch frame as the corresponding `flow` snapshot — an\n\t * O(1)-per-event peer of the snapshot, mirroring `reactiveMap`'s\n\t * `mutationLog`. Lets consumers tail *what changed* without diffing\n\t * successive `flow` map snapshots.\n\t */\n\tflowMutations?: ReactiveLogBundle<LensFlowChange>;\n\t/**\n\t * Tear down the underlying `describe({reactive:true})` subscription.\n\t * The `flow` node activates lazily; subscribing-then-unsubscribing reclaims\n\t * its observe stream automatically. Call `dispose()` once when finished.\n\t */\n\tdispose(): void;\n}\n\n// ---------------------------------------------------------------------------\n// Pure helpers (exported for testing / composition)\n// ---------------------------------------------------------------------------\n\n/** Build a `HealthReport` from a fresh `GraphDescribeOutput`. */\nexport function computeHealthReport(described: GraphDescribeOutput): HealthReport {\n\tconst problems: HealthProblem[] = [];\n\tfor (const [path, desc] of Object.entries(described.nodes)) {\n\t\tif (desc.status !== \"errored\") continue;\n\t\tconst entry: HealthProblem = { path, status: \"errored\" };\n\t\t// Walk upstream to find the first errored ancestor (if any) distinct from `path`.\n\t\t// Explicit empty options disambiguates the overload (otherwise both match\n\t\t// and TS picks the `withDetail:true` signature first).\n\t\tconst upstream = reachable(described, path, \"upstream\", {});\n\t\tfor (const p of upstream) {\n\t\t\tif (p === path) continue;\n\t\t\tif (described.nodes[p]?.status === \"errored\") {\n\t\t\t\tentry.upstreamCause = p;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\tproblems.push(entry);\n\t}\n\tproblems.sort((a, b) => (a.path < b.path ? -1 : a.path > b.path ? 1 : 0));\n\treturn { ok: problems.length === 0, problems };\n}\n\n/** Structural equality for {@link HealthReport} — used as the `health` derived's `equals`. */\nexport function healthReportEqual(a: HealthReport, b: HealthReport): boolean {\n\tif (a.ok !== b.ok) return false;\n\tif (a.problems.length !== b.problems.length) return false;\n\tfor (let i = 0; i < a.problems.length; i++) {\n\t\tconst x = a.problems[i]!;\n\t\tconst y = b.problems[i]!;\n\t\tif (x.path !== y.path) return false;\n\t\tif (x.status !== y.status) return false;\n\t\tif (x.upstreamCause !== y.upstreamCause) return false;\n\t}\n\treturn true;\n}\n\n// ---------------------------------------------------------------------------\n// graphLens preset\n// ---------------------------------------------------------------------------\n\n/**\n * Reactive observability preset over a target {@link Graph}.\n *\n * @param target - The graph to observe.\n *\n * @example\n * ```ts\n * const g = new Graph(\"app\");\n * g.add(state(0, { name: \"counter\" }), { name: \"counter\" });\n *\n * const lens = graphLens(g);\n * lens.topology.subscribe((msgs) => console.log(\"topology:\", msgs));\n * lens.health.subscribe((msgs) => console.log(\"health:\", msgs));\n * lens.flow.subscribe((msgs) => {\n * for (const [type, payload] of msgs) {\n * if (type === DATA) console.log(\"flow map size:\", (payload as ReadonlyMap<string, FlowEntry>).size);\n * }\n * });\n *\n * // Causal chains: use the underlying primitive directly — `graphLens` no\n * // longer wraps it, since `graph.describe({ explain: {...}, reactive: true })`\n * // already provides everything the old `lens.why()` did.\n * const why = g.describe({\n * explain: { from: \"counter\", to: \"consumer\" },\n * reactive: true,\n * });\n *\n * // Tear down when done.\n * lens.dispose();\n * ```\n *\n * @category observability\n */\nexport function graphLens(target: Graph, opts: GraphLensOptions = {}): GraphLensView {\n\tconst mutLogOpt = opts.mutations;\n\tconst mutationsEnabled = mutLogOpt != null;\n\t// Closure buffer: the `flow` derived fills this per wave with the\n\t// tick/evict deltas it just applied; a sibling effect drains it into\n\t// `flowMutations` in the SAME wave (sanctioned side-effect-in-effect\n\t// pattern — mirrors the bridge.ts `topic.publish`-from-effect and\n\t// reactiveMap's pending-changes-flushed-in-the-snapshot-batch shape).\n\t// Single source of truth for the diff stays the `flow` fn.\n\tconst pendingFlowChanges: LensFlowChangePayload[] = [];\n\t// Hoisted (QA-P3) so the `flow` fn's buffer push can short-circuit\n\t// once `dispose()` ran — a still-warm `flow` (external subscriber)\n\t// must not keep growing `pendingFlowChanges` with no drain alive.\n\tlet disposed = false;\n\n\tconst topologyHandle = target.describe({\n\t\treactive: true,\n\t\tdetail: \"standard\",\n\t\treactiveName: \"graphLens.topology\",\n\t});\n\tconst topology = topologyHandle.node;\n\n\tconst health = node<HealthReport>(\n\t\t[topology],\n\t\t(batchData, actions, ctx) => {\n\t\t\tconst data = batchData.map((batch, i) =>\n\t\t\t\tbatch != null && batch.length > 0 ? batch.at(-1) : ctx.prevData[i],\n\t\t\t);\n\t\t\tactions.emit(computeHealthReport(data[0] as GraphDescribeOutput));\n\t\t},\n\t\t{\n\t\t\tname: \"graphLens.health\",\n\t\t\tdescribeKind: \"derived\",\n\t\t\tequals: healthReportEqual,\n\t\t\tmeta: domainMeta(\"lens\", \"health\"),\n\t\t},\n\t);\n\t// `topology` is already kept alive by `_describeReactive`'s internal\n\t// keepalive; `health` needs its own so the derived stays warm even if\n\t// the consumer subscribes lazily.\n\tconst stopHealthKeep = keepalive(health);\n\n\t// `flow` accumulates per-path counters across emissions. Closure-mirror\n\t// (COMPOSITION-GUIDE §28) holds the canonical map; the derived returns a\n\t// fresh ReadonlyMap projection per emit so consumers never observe an\n\t// in-place mutation. Topology drives reconciliation: paths that disappear\n\t// from the describe drop their counter entry.\n\t//\n\t// `lastAppliedFlush_ns` guards against double-applying the same changeset\n\t// when topology re-emits without a new dataFlow event (e.g. a node remove\n\t// re-fires `topology` while `dataFlow.cache` still holds the previous\n\t// changeset by reference). Using the monotonic `flushedAt_ns` cursor (qa\n\t// G2A — EC4 fix) is more robust than ref-comparison: an empty changeset\n\t// re-emitted with a fresh object identity but the same `flushedAt_ns`\n\t// won't trigger a stale-events replay, and a genuinely-new changeset\n\t// always advances the cursor.\n\tconst flowMap = new Map<string, FlowEntry>();\n\tlet lastAppliedFlush_ns = -1;\n\tconst dataFlow = target.observe({ reactive: true, tiers: [\"data\"] });\n\tconst flow = node<ReadonlyMap<string, FlowEntry>>(\n\t\t[dataFlow, topology],\n\t\t(batchData, actions, ctx) => {\n\t\t\tconst data = batchData.map((batch, i) =>\n\t\t\t\tbatch != null && batch.length > 0 ? batch.at(-1) : ctx.prevData[i],\n\t\t\t);\n\t\t\tconst changeset = data[0];\n\t\t\tconst described = data[1];\n\t\t\tconst c = changeset as ObserveChangeset | undefined;\n\t\t\tconst desc = described as GraphDescribeOutput | undefined;\n\n\t\t\t// Apply NEW changeset events first so a freshly-emitted node-remove\n\t\t\t// observed below cleanly drops the entry. (If we reconciled first\n\t\t\t// then applied, an event referencing a path the topology had already\n\t\t\t// dropped would re-create the entry only to be wiped on the next\n\t\t\t// topology re-emit — order events-then-topology to avoid that.)\n\t\t\t//\n\t\t\t// Skip when `events.length === 0`: an empty changeset has no work\n\t\t\t// to do. The cursor advances anyway so a future identical-content\n\t\t\t// changeset (different ref, same flushedAt_ns) is also skipped.\n\t\t\tif (c != null && c.flushedAt_ns > lastAppliedFlush_ns) {\n\t\t\t\tlastAppliedFlush_ns = c.flushedAt_ns;\n\t\t\t\tfor (const event of c.events as readonly ObserveEvent[]) {\n\t\t\t\t\tif (event.type !== \"data\") continue;\n\t\t\t\t\tconst path = event.path;\n\t\t\t\t\tif (path == null || path === \"\") continue;\n\t\t\t\t\tconst prior = flowMap.get(path);\n\t\t\t\t\tconst count = (prior?.count ?? 0) + 1;\n\t\t\t\t\tflowMap.set(path, {\n\t\t\t\t\t\tpath,\n\t\t\t\t\t\tcount,\n\t\t\t\t\t\tlastUpdate_ns: c.flushedAt_ns,\n\t\t\t\t\t});\n\t\t\t\t\tif (mutationsEnabled && !disposed) {\n\t\t\t\t\t\tpendingFlowChanges.push({ kind: \"tick\", path, count });\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Reconcile against current topology — drop entries whose paths\n\t\t\t// no longer exist in the describe.\n\t\t\tif (desc != null && flowMap.size > 0) {\n\t\t\t\tconst valid = new Set(Object.keys(desc.nodes));\n\t\t\t\tfor (const k of [...flowMap.keys()]) {\n\t\t\t\t\tif (!valid.has(k)) {\n\t\t\t\t\t\tflowMap.delete(k);\n\t\t\t\t\t\tif (mutationsEnabled && !disposed) {\n\t\t\t\t\t\t\tpendingFlowChanges.push({ kind: \"evict\", path: k });\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Snapshot — consumers receive a frozen view.\n\t\t\tactions.emit(new Map(flowMap) as ReadonlyMap<string, FlowEntry>);\n\t\t},\n\t\t{\n\t\t\tname: \"graphLens.flow\",\n\t\t\tdescribeKind: \"derived\",\n\t\t\tmeta: domainMeta(\"lens\", \"flow\"),\n\t\t},\n\t);\n\t// ── flow delta companion (Phase 14 — last DS-14-substrate thread) ────────\n\t// `flow` is otherwise just a snapshot map; tailing *what changed*\n\t// previously required diffing successive map snapshots. The companion\n\t// surfaces each per-path `tick` / `evict` as a typed `LensFlowChange`,\n\t// O(1) per event, in the same batch frame as the snapshot — mirroring\n\t// `reactiveMap`/`pubsub` `mutationLog`. Opt-in (zero overhead off).\n\t//\n\t// QA-P1: the drain effect is created + kept alive BEFORE\n\t// `keepalive(flow)` (below) so it is subscribed to `flow` before\n\t// `flow`'s first activation. Otherwise `keepalive(flow)`'s\n\t// push-on-subscribe would replay cached changeset(s) into\n\t// `pendingFlowChanges` before the drain exists, collapsing pre-wiring\n\t// deltas into the drain's first flush and breaking the \"same batch\n\t// frame as the snapshot\" contract (COMPOSITION-GUIDE §2 — wire\n\t// observers before emitters).\n\tlet flowMutations: ReactiveLogBundle<LensFlowChange> | undefined;\n\tlet stopFlowMutKeep: (() => void) | undefined;\n\tif (mutLogOpt != null) {\n\t\tconst log = reactiveLog<LensFlowChange>(undefined, {\n\t\t\tname:\n\t\t\t\tmutLogOpt === true\n\t\t\t\t\t? \"graphLens.flowMutations\"\n\t\t\t\t\t: (mutLogOpt.name ?? \"graphLens.flowMutations\"),\n\t\t\tmaxSize: mutLogOpt === true ? undefined : mutLogOpt.maxSize,\n\t\t});\n\t\tflowMutations = log;\n\t\tlet mutVersion = 0;\n\t\t// Effect dependent on `flow`: it fires in the SAME wave `flow`\n\t\t// emits, so the appended records coalesce into the same outermost\n\t\t// batch flush as the snapshot (the `mutationLog` \"same batch\n\t\t// frame\" contract). Side-effecting append from an `effect` node is\n\t\t// the sanctioned home (cf. bridge.ts `topic.publish`-from-effect);\n\t\t// `flow`'s fn stays the single source of the diff.\n\t\tconst flowMutationsDrain = node(\n\t\t\t[flow],\n\t\t\t(_batchData, _actions, _ctx) => {\n\t\t\t\tif (pendingFlowChanges.length === 0) return;\n\t\t\t\tconst drained = pendingFlowChanges.splice(0);\n\t\t\t\tfor (const change of drained) {\n\t\t\t\t\tlog.append({\n\t\t\t\t\t\tstructure: \"lensFlow\",\n\t\t\t\t\t\tversion: ++mutVersion,\n\t\t\t\t\t\tt_ns: wallClockNs(),\n\t\t\t\t\t\tlifecycle: \"data\",\n\t\t\t\t\t\tchange,\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t},\n\t\t\t{\n\t\t\t\tname: \"graphLens.flowMutationsDrain\",\n\t\t\t\tdescribeKind: \"effect\",\n\t\t\t\tmeta: domainMeta(\"lens\", \"flowMutations\"),\n\t\t\t},\n\t\t);\n\t\tstopFlowMutKeep = keepalive(flowMutationsDrain);\n\t}\n\n\t// AFTER the drain is wired (QA-P1) so `flow`'s push-on-subscribe\n\t// reaches the drain in the same wave.\n\tconst stopFlowKeep = keepalive(flow);\n\n\treturn {\n\t\ttopology,\n\t\thealth,\n\t\tflow,\n\t\t...(flowMutations ? { flowMutations } : {}),\n\t\tdispose() {\n\t\t\tif (disposed) return;\n\t\t\tdisposed = true;\n\t\t\tstopFlowMutKeep?.();\n\t\t\t// QA-P2: release the reactiveLog bundle's own internal\n\t\t\t// keepalives (view caches / lastValue / nested mutLog), not\n\t\t\t// just the drain effect's keepalive.\n\t\t\tflowMutations?.dispose();\n\t\t\t// QA-P3: a still-warm `flow` (external subscriber) keeps\n\t\t\t// running its fn post-dispose; the buffer push is gated on\n\t\t\t// `!disposed`, but drop any residue so it can't be replayed.\n\t\t\tpendingFlowChanges.length = 0;\n\t\t\tstopFlowKeep();\n\t\t\tstopHealthKeep();\n\t\t\ttopologyHandle.dispose();\n\t\t},\n\t};\n}\n","/**\n * Composable safety layer (roadmap §9.0b — Tier 5.1 Wave-B rebuild).\n *\n * {@link guardedExecution} wraps any {@link Graph} with:\n *\n * - {@link policyGate} — reactive ABAC (Tier 2.3 rename of `policyEnforcer`),\n * policies stored as a `Node` so LLMs / humans can update them at runtime.\n * Full transitive dynamic coverage via `watchTopologyTree`.\n * - Reactive {@link GuardedExecutionGraph.scopedDescribeNode} — a thin\n * delegate over `target.describe({ reactive: true, actor })` that re-derives\n * on every settle (topology change, error transition, actor swap).\n * - The enforcer's `violations` topic is republished as `violations` on\n * the wrapper, composable with {@link graphLens}'s `health`.\n * - The wrapper-level `lints` topic surfaces non-policy diagnostic warnings\n * (`empty-policies` / `audit-no-effect` / `no-actor`) so misconfigurations\n * are caught reactively rather than via thrown errors at scattered call sites.\n * - The `scope` derived publishes the current configuration tuple\n * (`{actor, mode, policiesCount}`) for dashboards.\n *\n * V1 scope: policies + actor + reactive scoped describe + lints + scope.\n * Budget-as-option is NOT in V1 — it requires a cost-tracking design that\n * hasn't landed yet. Callers who need a budget limit today append a\n * budget-aware {@link PolicyRuleData} to the policies list (check current\n * cost and `deny` when exhausted).\n *\n * @module\n */\nimport type { Actor, PolicyRuleData } from \"@graphrefly/pure-ts/core\";\nimport { DATA, monotonicNs, type Node, node } from \"@graphrefly/pure-ts/core\";\nimport { keepalive } from \"@graphrefly/pure-ts/extra\";\nimport {\n\ttype DescribeFilter,\n\tGraph,\n\ttype GraphDescribeOptions,\n\ttype GraphDescribeOutput,\n\ttype GraphOptions,\n} from \"@graphrefly/pure-ts/graph\";\nimport { domainMeta } from \"../../base/meta/domain-meta.js\";\nimport {\n\ttype PolicyGateGraph,\n\ttype PolicyViolation,\n\tpolicyGate,\n} from \"../../utils/inspect/audit.js\";\nimport { TopicGraph } from \"../../utils/messaging/index.js\";\n\nfunction isNode<T>(x: unknown): x is Node<T> {\n\treturn (\n\t\ttypeof x === \"object\" && x !== null && \"subscribe\" in (x as object) && \"down\" in (x as object)\n\t);\n}\n\nfunction guardedMeta(kind: string): Record<string, unknown> {\n\treturn domainMeta(\"guarded\", kind);\n}\n\n/** Diagnostic warning published on {@link GuardedExecutionGraph.lints}. */\nexport interface GuardedExecutionLint {\n\t/**\n\t * - `\"empty-policies\"` — `policies` Node emitted an empty array in\n\t * `mode: \"enforce\"`. Static empty arrays throw at construction; this\n\t * covers the reactive case.\n\t * - `\"audit-no-effect\"` — `mode: \"audit\"` plus the target has no per-node\n\t * guards, so `scopedDescribeNode` filters by per-node guards only and\n\t * policies will never gate visibility (they still populate `violations`\n\t * on writes).\n\t * - `\"no-actor\"` — neither a wrapper-configured nor per-call actor was\n\t * supplied. `scopedDescribeNode` falls back to \"describe everything\"\n\t * for the corresponding subscription.\n\t */\n\tkind: \"empty-policies\" | \"audit-no-effect\" | \"no-actor\";\n\tmessage: string;\n\ttimestamp_ns: number;\n}\n\n/** Configuration tuple published on {@link GuardedExecutionGraph.scope}. */\nexport interface GuardedScope {\n\t/** The wrapper-configured default actor, or `null` when none configured. */\n\tactor: Actor | null;\n\tmode: \"audit\" | \"enforce\";\n\t/** Current policy count (reactive — re-emits on `policies` Node updates). */\n\tpoliciesCount: number;\n}\n\n/** Options for {@link guardedExecution}. */\nexport interface GuardedExecutionOptions {\n\t/**\n\t * Policies enforced against every guarded write. Static list or a live\n\t * `Node<readonly PolicyRuleData[]>` (LLM-updatable).\n\t *\n\t * **Empty-policies handling:**\n\t * - Static empty array + `mode: \"enforce\"` throws `RangeError` at\n\t * construction (deny-by-default is almost certainly a misconfiguration).\n\t * - Node-supplied empty array + `mode: \"enforce\"` emits a one-time\n\t * `\"empty-policies\"` lint on first such emission (the wrapper can't\n\t * throw mid-run — surface the warning reactively).\n\t * - `mode: \"audit\"` tolerates empty policies (no guards stacked; policies\n\t * only feed the `violations` channel on writes).\n\t */\n\tpolicies: readonly PolicyRuleData[] | Node<readonly PolicyRuleData[]>;\n\t/**\n\t * Default actor used when the caller invokes\n\t * {@link GuardedExecutionGraph.scopedDescribeNode} without an override.\n\t * Accepts a static {@link Actor} or a `Node<Actor>` — when a Node is\n\t * supplied, the reactive describe re-derives on every actor emission so\n\t * harnesses binding a per-turn actor get a single live describe Node\n\t * instead of re-creating one per turn.\n\t *\n\t * Omit to scope per-call only. A `\"no-actor\"` lint fires once per instance\n\t * if neither a configured nor per-call actor is ever supplied (the\n\t * resulting describe is unscoped — full visibility).\n\t */\n\tactor?: Actor | Node<Actor>;\n\t/**\n\t * `\"enforce\"` (default) — push guards onto target nodes so disallowed\n\t * writes throw {@link GuardDenied}.\n\t * `\"audit\"` — record would-be denials to the `violations` topic without\n\t * blocking writes.\n\t */\n\tmode?: \"audit\" | \"enforce\";\n\t/** Ring-buffer cap for the `violations` topic. Default 1000 (inherited from policyGate). */\n\tviolationsLimit?: number;\n\t/** Ring-buffer cap for the `lints` topic. Default 64 — each lint kind fires at most once per instance. */\n\tlintsLimit?: number;\n\t/** Wrapper graph name. Default `${target.name}_guarded`. */\n\tname?: string;\n\t/** Wrapper graph options. */\n\tgraph?: GraphOptions;\n}\n\n/**\n * Wrapper over a target {@link Graph} providing reactive ABAC + reactive\n * scoped describe + diagnostic lints. Mounts a {@link PolicyGateGraph} under\n * `enforcer`, a {@link TopicGraph} of {@link GuardedExecutionLint} under\n * `lints`, and a `scope` derived publishing `{actor, mode, policiesCount}`.\n *\n * @category patterns\n */\nexport class GuardedExecutionGraph extends Graph {\n\treadonly enforcer: PolicyGateGraph;\n\treadonly violations: TopicGraph<PolicyViolation>;\n\treadonly lints: TopicGraph<GuardedExecutionLint>;\n\treadonly scope: Node<GuardedScope>;\n\t/**\n\t * Canonical reactive describe scoped to the wrapper's configured `actor`.\n\t * Subscribes ONCE at construction; lifecycle owned by the wrapper (disposed\n\t * on `wrapper.destroy()`). Use this property for the common case\n\t * (long-lived consumer wanting \"describe scoped to my actor\"); use\n\t * {@link scopedDescribeNode} only when a per-call actor override or\n\t * different `detail`/`fields` is required.\n\t *\n\t * Re-derives on every settle of the target graph: structural changes,\n\t * status transitions (errors flip nodes into `\"errored\"`), and actor\n\t * emissions (when a `Node<Actor>` is bound, including the SENTINEL bridge\n\t * applied internally — see qa G1B).\n\t */\n\treadonly scopedDescribe: Node<GraphDescribeOutput>;\n\tprivate readonly _target: Graph;\n\tprivate readonly _actorNode: Node<Actor | null>;\n\tprivate readonly _mode: \"audit\" | \"enforce\";\n\tprivate readonly _firedLintKinds = new Set<GuardedExecutionLint[\"kind\"]>();\n\n\tconstructor(target: Graph, opts: GuardedExecutionOptions) {\n\t\tsuper(opts.name ?? `${target.name}_guarded`, opts.graph);\n\t\tthis._target = target;\n\t\tthis._mode = opts.mode ?? \"enforce\";\n\n\t\tconst policiesOpt = opts.policies;\n\t\tconst policiesIsNode = isNode<readonly PolicyRuleData[]>(policiesOpt);\n\t\t// Static empty + enforce → throw (deny-by-default = misconfig).\n\t\tif (\n\t\t\t!policiesIsNode &&\n\t\t\tthis._mode === \"enforce\" &&\n\t\t\t(policiesOpt as readonly PolicyRuleData[]).length === 0\n\t\t) {\n\t\t\tthrow new RangeError(\n\t\t\t\t'guardedExecution: empty `policies` in `mode: \"enforce\"` denies every action. ' +\n\t\t\t\t\t'Pass at least `{ effect: \"allow\", action: \"*\" }` and layer deny rules on top.',\n\t\t\t);\n\t\t}\n\n\t\t// Lints topic — mounted before any potential first-DATA emission.\n\t\tthis.lints = new TopicGraph<GuardedExecutionLint>(\"lints\", {\n\t\t\tretainedLimit: opts.lintsLimit ?? 64,\n\t\t});\n\t\tthis.mount(\"lints\", this.lints);\n\n\t\t// Normalize `actor` to a Node<Actor | null>. `null` (a valid DATA in\n\t\t// the v5 sentinel-as-undefined model) means \"no actor configured\" —\n\t\t// `target.describe({ reactive: true })`'s `resolveActorOption` treats\n\t\t// `null` cache as \"no scoping\" (full visibility), and the `scope`\n\t\t// derived can publish `actor: null` cleanly. Using `node([], { initial: undefined })`\n\t\t// here would leave the actor node in sentinel state, which never fires\n\t\t// DATA on subscribe and would block the `scope` derived's first-run\n\t\t// gate from completing.\n\t\t//\n\t\t// qa G1B (EC2 fix): when the caller passes a `Node<Actor>`, that node's\n\t\t// cache may itself be SENTINEL at construction (e.g. a `producer`\n\t\t// awaiting first emission, or a deferred `derived`). Forwarding the raw\n\t\t// Node would re-introduce the same sentinel-stall on `scope`. Bridge\n\t\t// through a node bridge with a `null` initial so the\n\t\t// internal `_actorNode` always carries non-sentinel cache; the bridge\n\t\t// re-emits whenever the caller's Node emits, and forwards `null`\n\t\t// through unchanged.\n\t\tconst actorOpt = opts.actor;\n\t\tif (actorOpt == null) {\n\t\t\tthis._actorNode = node<Actor | null>([], { name: \"actor\", initial: null });\n\t\t} else if (isNode<Actor>(actorOpt)) {\n\t\t\tthis._actorNode = node<Actor | null>(\n\t\t\t\t[actorOpt],\n\t\t\t\t(batchData, actions, ctx) => {\n\t\t\t\t\tconst data = batchData.map((batch, i) =>\n\t\t\t\t\t\tbatch != null && batch.length > 0 ? batch.at(-1) : ctx.prevData[i],\n\t\t\t\t\t);\n\t\t\t\t\tactions.emit((data[0] as Actor | null | undefined) ?? null);\n\t\t\t\t},\n\t\t\t\t{ describeKind: \"derived\", name: \"actor\", initial: null },\n\t\t\t);\n\t\t} else {\n\t\t\tthis._actorNode = node<Actor | null>([], { name: \"actor\", initial: actorOpt });\n\t\t}\n\n\t\t// Mount the enforcer.\n\t\tconst enforcerOpts: { mode: \"audit\" | \"enforce\"; name: string; violationsLimit?: number } = {\n\t\t\tmode: this._mode,\n\t\t\tname: \"enforcer\",\n\t\t};\n\t\tif (opts.violationsLimit != null) enforcerOpts.violationsLimit = opts.violationsLimit;\n\t\tthis.enforcer = policyGate(target, opts.policies, enforcerOpts);\n\t\tthis.violations = this.enforcer.violations;\n\t\tthis.mount(\"enforcer\", this.enforcer);\n\n\t\t// Empty-policies one-time lint (Node form, enforce mode only).\n\t\tif (policiesIsNode && this._mode === \"enforce\") {\n\t\t\tconst policiesNode = policiesOpt as Node<readonly PolicyRuleData[]>;\n\t\t\t// Two paths here intentionally:\n\t\t\t// (1) Synchronous cache seed — fires immediately if the Node was\n\t\t\t// constructed with an empty array as its current cache. This\n\t\t\t// covers `node<…[]>([], { initial: [] })` and any pre-emitted derived.\n\t\t\t// (2) Subscribe path — catches subsequent empty emissions AND\n\t\t\t// handles the SENTINEL case (cache=undefined at construction)\n\t\t\t// where the Node fires its first DATA after the wrapper is\n\t\t\t// built. The `_firedLintKinds` Set keeps the lint one-shot\n\t\t\t// across both paths.\n\t\t\t// qa F6 (deferred): if a Node's initial cache is SENTINEL (undefined)\n\t\t\t// AND it never emits an empty array (only ever non-empty), the lint\n\t\t\t// never fires — that's correct behavior, the configuration is\n\t\t\t// effectively non-empty for the wrapper's lifetime.\n\t\t\tconst cached = policiesNode.cache as readonly PolicyRuleData[] | undefined;\n\t\t\tif (cached != null && cached.length === 0) {\n\t\t\t\tthis._fireLint(\n\t\t\t\t\t\"empty-policies\",\n\t\t\t\t\t'`policies` Node cached an empty array in `mode: \"enforce\"` — every action will be denied. Add at least one allow rule.',\n\t\t\t\t);\n\t\t\t}\n\t\t\tconst offEmpty = policiesNode.subscribe((msgs) => {\n\t\t\t\tfor (const m of msgs) {\n\t\t\t\t\tif (m[0] !== DATA) continue;\n\t\t\t\t\tconst v = m[1] as readonly PolicyRuleData[] | undefined;\n\t\t\t\t\tif (v == null || v.length === 0) {\n\t\t\t\t\t\tthis._fireLint(\n\t\t\t\t\t\t\t\"empty-policies\",\n\t\t\t\t\t\t\t'`policies` Node emitted an empty array in `mode: \"enforce\"` — every action will be denied. Add at least one allow rule.',\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t});\n\t\t\tthis.addDisposer(offEmpty);\n\t\t}\n\n\t\t// Audit-mode + no per-node guards on the target → fire once.\n\t\t// qa EC5 (deferred): the check is one-shot at construction. If the\n\t\t// caller later mounts a guarded node into the target, the lint stays\n\t\t// retained even though the configuration is now effective. Recommended\n\t\t// pattern: mount per-node guards on the target BEFORE wrapping.\n\t\t// Reactive recompute (subscribe to target.topology, clear lint when a\n\t\t// guard appears) is filed in `docs/optimizations.md` as a follow-up.\n\t\tif (this._mode === \"audit\") {\n\t\t\tconst described = target.describe({ detail: \"full\" });\n\t\t\tconst anyGuard = Object.values(described.nodes).some((n) => n.guard != null);\n\t\t\tif (!anyGuard) {\n\t\t\t\tthis._fireLint(\n\t\t\t\t\t\"audit-no-effect\",\n\t\t\t\t\t'`mode: \"audit\"` + target has no per-node guards — `scopedDescribeNode` filters by per-node guards only, so policy rules will not affect describe() visibility. Policies still populate the `violations` topic on writes.',\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\n\t\t// No-actor lint: fires at construction when neither configured nor\n\t\t// implicitly carried via a Node-cache. Per-call overrides on\n\t\t// `scopedDescribeNode` cannot retroactively suppress this — the\n\t\t// configured-default branch is what callers most often miss.\n\t\tif (actorOpt == null) {\n\t\t\tthis._fireLint(\n\t\t\t\t\"no-actor\",\n\t\t\t\t\"no actor configured — `wrapper.scopedDescribe` and `scopedDescribeNode()` will return an unscoped describe (full visibility) unless a per-call actor is supplied.\",\n\t\t\t);\n\t\t}\n\n\t\t// Scope derived: live tuple of {actor, mode, policiesCount}.\n\t\tthis.scope = node<GuardedScope>(\n\t\t\t[this._actorNode, this.enforcer.policies],\n\t\t\t(batchData, actions, ctx) => {\n\t\t\t\tconst data = batchData.map((batch, i) =>\n\t\t\t\t\tbatch != null && batch.length > 0 ? batch.at(-1) : ctx.prevData[i],\n\t\t\t\t);\n\t\t\t\tactions.emit({\n\t\t\t\t\tactor: (data[0] as Actor | null | undefined) ?? null,\n\t\t\t\t\tmode: this._mode,\n\t\t\t\t\tpoliciesCount: (data[1] as readonly PolicyRuleData[]).length,\n\t\t\t\t});\n\t\t\t},\n\t\t\t{\n\t\t\t\tname: \"scope\",\n\t\t\t\tdescribeKind: \"derived\",\n\t\t\t\tmeta: guardedMeta(\"scope\"),\n\t\t\t},\n\t\t);\n\t\tthis.add(this.scope, { name: \"scope\" });\n\t\tthis.addDisposer(keepalive(this.scope));\n\n\t\t// qa G1A \"same concept\": single canonical reactive describe bound to\n\t\t// the configured actor, mounted once at construction. Removes the\n\t\t// per-call handle proliferation from the prior `scopedDescribeNode`\n\t\t// pattern (the method is retained as the per-call escape hatch). The\n\t\t// reactive describe re-derives on actor swap / topology change /\n\t\t// status transition; lifecycle owned by the wrapper.\n\t\tconst scopedHandle = target.describe({\n\t\t\treactive: true,\n\t\t\t// F8 (resolved 2026-05-18): `GraphDescribeOptions.actor` is now\n\t\t\t// `Actor | Node<Actor | null>`, so `_actorNode` (a\n\t\t\t// `Node<Actor | null>`) passes without a cast. A `null`/`undefined`\n\t\t\t// cache resolves to \"no scoping\" (full visibility) per\n\t\t\t// `resolveActorOption`. See graph.ts § \"Cache-undefined/null\n\t\t\t// semantics\".\n\t\t\tactor: this._actorNode,\n\t\t\treactiveName: \"scopedDescribe\",\n\t\t});\n\t\tthis.scopedDescribe = scopedHandle.node;\n\t\tthis.add(this.scopedDescribe, { name: \"scopedDescribe\" });\n\t\tthis.addDisposer(scopedHandle.dispose);\n\t}\n\n\tprivate _fireLint(kind: GuardedExecutionLint[\"kind\"], message: string): void {\n\t\tif (this._firedLintKinds.has(kind)) return;\n\t\tthis._firedLintKinds.add(kind);\n\t\tthis.lints.publish({ kind, message, timestamp_ns: monotonicNs() });\n\t}\n\n\t/**\n\t * **Per-call escape hatch.** Prefer {@link scopedDescribe} (the mounted\n\t * property) for the common case of \"describe scoped to my actor.\" Use\n\t * this method ONLY when you need a per-call actor override or different\n\t * `detail`/`fields`/`filter`.\n\t *\n\t * Returns a live `Node<GraphDescribeOutput>` scoped to the supplied (or\n\t * configured) actor, plus an explicit `dispose` for caller-controlled\n\t * lifecycle. Re-derives on every settle of the target graph: structural\n\t * changes, status transitions, and actor emissions (when a `Node<Actor>`\n\t * is bound).\n\t *\n\t * **Lifecycle (qa G1A — EC1 fix).** Each call instantiates a fresh\n\t * `target.describe({reactive: true})` handle (with its own version state,\n\t * observe handle, transitive topology subscriptions, derived + keepalive).\n\t * The caller MUST invoke the returned `dispose()` when finished to release\n\t * these resources. Disposers ARE also tracked on the wrapper graph so\n\t * `wrapper.destroy()` cleans up any handles the caller forgot — but a\n\t * long-lived wrapper with heavy per-call usage will leak until destroy\n\t * unless `dispose()` is called explicitly.\n\t *\n\t * @param actorOverride - Optional per-call override. Static {@link Actor}\n\t * or `Node<Actor>`. Omit to use the wrapper-configured default.\n\t * @param opts - Standard {@link GraphDescribeOptions} fields (`detail`,\n\t * `fields`, `filter`). `actor` / `reactive` / `reactiveName` are\n\t * controlled by the wrapper.\n\t * @returns `{node, dispose}` — `node` is the live describe Node; `dispose`\n\t * tears down the underlying reactive describe subscription idempotently.\n\t */\n\tscopedDescribeNode(\n\t\tactorOverride?: Actor | Node<Actor>,\n\t\topts?: Omit<GraphDescribeOptions, \"actor\" | \"reactive\" | \"reactiveName\">,\n\t): { node: Node<GraphDescribeOutput>; dispose: () => void } {\n\t\tconst actorNode =\n\t\t\tactorOverride == null\n\t\t\t\t? this._actorNode\n\t\t\t\t: isNode<Actor>(actorOverride)\n\t\t\t\t\t? actorOverride\n\t\t\t\t\t: node<Actor>([], { name: \"actor_override\", initial: actorOverride });\n\t\tconst handle = this._target.describe({\n\t\t\treactive: true,\n\t\t\t// F8 (resolved 2026-05-18): `actor` accepts `Node<Actor | null>`\n\t\t\t// directly. `actorNode` is `_actorNode` (`Node<Actor | null>`) or\n\t\t\t// an override (`Node<Actor>`/`Node<Actor | null>`); both assign\n\t\t\t// without a cast. `_describeReactive` resolves via\n\t\t\t// `resolveActorOption` (null/undefined cache → no scoping).\n\t\t\tactor: actorNode,\n\t\t\t...(opts ?? {}),\n\t\t});\n\t\t// Track on the wrapper as a safety net for callers who forget to\n\t\t// dispose; explicit `dispose()` is still the canonical lifecycle path.\n\t\tthis.addDisposer(handle.dispose);\n\t\treturn { node: handle.node, dispose: handle.dispose };\n\t}\n\n\t/** The wrapped graph (escape hatch for tooling). */\n\tget target(): Graph {\n\t\treturn this._target;\n\t}\n}\n\n/**\n * Wrap a {@link Graph} with {@link policyGate} plus a reactive scoped describe\n * lens. Returns a {@link GuardedExecutionGraph} that can be mounted, diffed,\n * or composed with {@link graphLens}.\n *\n * @param target - The graph to guard.\n * @param opts - See {@link GuardedExecutionOptions}.\n *\n * @example\n * ```ts\n * const guarded = guardedExecution(app, {\n * actor: node<Actor>([], { initial: { type: \"human\", id: \"alice\" } }), // reactive — re-derive on swap\n * policies: [\n * { effect: \"allow\", action: \"read\", actorType: \"human\" },\n * { effect: \"deny\", action: \"write\", pathPattern: \"system::*\" },\n * ],\n * mode: \"enforce\",\n * });\n *\n * // Canonical: subscribe to the mounted reactive describe (no per-call leak).\n * guarded.scopedDescribe.subscribe((msgs) => { /* live describe per actor / topology change *\\/ });\n * // Per-call escape hatch (different actor / detail) — caller manages dispose.\n * const detailed = guarded.scopedDescribeNode(undefined, { detail: \"standard\" });\n * try { detailed.node.subscribe(/* … *\\/); } finally { detailed.dispose(); }\n * guarded.violations.events.subscribe(msgs => console.log(\"violations:\", msgs));\n * guarded.lints.events.subscribe(msgs => console.warn(\"lints:\", msgs));\n * guarded.scope.subscribe(msgs => console.log(\"scope:\", msgs));\n * ```\n *\n * @category patterns\n */\nexport function guardedExecution(\n\ttarget: Graph,\n\topts: GuardedExecutionOptions,\n): GuardedExecutionGraph {\n\treturn new GuardedExecutionGraph(target, opts);\n}\n\n// Re-export types useful for call sites.\nexport type { DescribeFilter };\n"],"mappings":"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","names":["import_core","import_graph","import_core","import_extra","import_graph","import_core","import_extra","import_graph","batch","batch","node","import_core","import_extra","import_graph","import_graph","batch","import_core","import_extra","import_graph","isNode","batch"]}