@nicia-ai/typegraph 0.46.2 → 0.48.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (310) hide show
  1. package/README.md +20 -1
  2. package/dist/backend/drizzle/indexes.cjs +9 -9
  3. package/dist/backend/drizzle/indexes.d.cts +1 -1
  4. package/dist/backend/drizzle/indexes.d.ts +1 -1
  5. package/dist/backend/drizzle/indexes.js +5 -5
  6. package/dist/backend/index.cjs +58 -49
  7. package/dist/backend/index.d.cts +37 -55
  8. package/dist/backend/index.d.ts +37 -55
  9. package/dist/backend/index.js +11 -10
  10. package/dist/backend/postgres/index.cjs +27 -26
  11. package/dist/backend/postgres/index.d.cts +21 -7
  12. package/dist/backend/postgres/index.d.ts +21 -7
  13. package/dist/backend/postgres/index.js +14 -13
  14. package/dist/backend/postgres/pglite-store.cjs +34 -33
  15. package/dist/backend/postgres/pglite-store.cjs.map +1 -1
  16. package/dist/backend/postgres/pglite-store.d.cts +8 -8
  17. package/dist/backend/postgres/pglite-store.d.ts +8 -8
  18. package/dist/backend/postgres/pglite-store.js +30 -29
  19. package/dist/backend/postgres/pglite-store.js.map +1 -1
  20. package/dist/backend/postgres/pglite.cjs +16 -15
  21. package/dist/backend/postgres/pglite.d.cts +3 -3
  22. package/dist/backend/postgres/pglite.d.ts +3 -3
  23. package/dist/backend/postgres/pglite.js +15 -14
  24. package/dist/backend/sqlite/index.cjs +24 -23
  25. package/dist/backend/sqlite/index.d.cts +20 -7
  26. package/dist/backend/sqlite/index.d.ts +20 -7
  27. package/dist/backend/sqlite/index.js +12 -11
  28. package/dist/backend/sqlite/libsql.cjs +19 -18
  29. package/dist/backend/sqlite/libsql.cjs.map +1 -1
  30. package/dist/backend/sqlite/libsql.d.cts +3 -3
  31. package/dist/backend/sqlite/libsql.d.ts +3 -3
  32. package/dist/backend/sqlite/libsql.js +14 -13
  33. package/dist/backend/sqlite/libsql.js.map +1 -1
  34. package/dist/backend/sqlite/local-store.cjs +35 -34
  35. package/dist/backend/sqlite/local-store.cjs.map +1 -1
  36. package/dist/backend/sqlite/local-store.d.cts +8 -8
  37. package/dist/backend/sqlite/local-store.d.ts +8 -8
  38. package/dist/backend/sqlite/local-store.js +31 -30
  39. package/dist/backend/sqlite/local-store.js.map +1 -1
  40. package/dist/backend/sqlite/local.cjs +17 -16
  41. package/dist/backend/sqlite/local.d.cts +3 -3
  42. package/dist/backend/sqlite/local.d.ts +3 -3
  43. package/dist/backend/sqlite/local.js +15 -14
  44. package/dist/{chunk-TCT66A7N.cjs → chunk-26YKDNUD.cjs} +10 -10
  45. package/dist/{chunk-TCT66A7N.cjs.map → chunk-26YKDNUD.cjs.map} +1 -1
  46. package/dist/{chunk-E4OSGBCM.cjs → chunk-2HIYYMWY.cjs} +139 -136
  47. package/dist/chunk-2HIYYMWY.cjs.map +1 -0
  48. package/dist/chunk-2QGYQBS2.cjs +161 -0
  49. package/dist/chunk-2QGYQBS2.cjs.map +1 -0
  50. package/dist/{chunk-GWXXHOLU.js → chunk-2SYZXD54.js} +21 -18
  51. package/dist/chunk-2SYZXD54.js.map +1 -0
  52. package/dist/{chunk-KA4L2NZ3.js → chunk-2TYSSBU5.js} +14 -14
  53. package/dist/{chunk-KA4L2NZ3.js.map → chunk-2TYSSBU5.js.map} +1 -1
  54. package/dist/{chunk-QOJ2L2AD.js → chunk-3DMMGT6S.js} +3 -3
  55. package/dist/{chunk-QOJ2L2AD.js.map → chunk-3DMMGT6S.js.map} +1 -1
  56. package/dist/{chunk-F36KZDFX.cjs → chunk-3FPR224C.cjs} +91 -91
  57. package/dist/{chunk-F36KZDFX.cjs.map → chunk-3FPR224C.cjs.map} +1 -1
  58. package/dist/{chunk-32UETY6S.cjs → chunk-3KOY4FH3.cjs} +207 -28
  59. package/dist/chunk-3KOY4FH3.cjs.map +1 -0
  60. package/dist/{chunk-QU7QLJA4.cjs → chunk-3U5ABFDW.cjs} +26 -26
  61. package/dist/{chunk-QU7QLJA4.cjs.map → chunk-3U5ABFDW.cjs.map} +1 -1
  62. package/dist/{chunk-RFDEYLIK.js → chunk-4WMMBDS5.js} +7 -7
  63. package/dist/chunk-4WMMBDS5.js.map +1 -0
  64. package/dist/{chunk-R7EWHKID.js → chunk-5K2BKEBD.js} +19 -145
  65. package/dist/chunk-5K2BKEBD.js.map +1 -0
  66. package/dist/chunk-5PRKMB24.cjs +124 -0
  67. package/dist/chunk-5PRKMB24.cjs.map +1 -0
  68. package/dist/chunk-6LN7FIVU.js +107 -0
  69. package/dist/chunk-6LN7FIVU.js.map +1 -0
  70. package/dist/{chunk-D6GCNNMK.cjs → chunk-6NGW27EO.cjs} +10 -10
  71. package/dist/{chunk-D6GCNNMK.cjs.map → chunk-6NGW27EO.cjs.map} +1 -1
  72. package/dist/{chunk-WHS2Y43J.js → chunk-6SWPJEYN.js} +5 -5
  73. package/dist/{chunk-WHS2Y43J.js.map → chunk-6SWPJEYN.js.map} +1 -1
  74. package/dist/{chunk-7TKNK32B.js → chunk-7SQSZN2J.js} +5 -5
  75. package/dist/{chunk-7TKNK32B.js.map → chunk-7SQSZN2J.js.map} +1 -1
  76. package/dist/{chunk-UTVQPZPN.cjs → chunk-7XUYFFR4.cjs} +20 -20
  77. package/dist/chunk-7XUYFFR4.cjs.map +1 -0
  78. package/dist/{chunk-KXSMF6PL.js → chunk-ACGSAMD3.js} +39 -34
  79. package/dist/chunk-ACGSAMD3.js.map +1 -0
  80. package/dist/{chunk-PYKF7UIL.cjs → chunk-AOSSQDVO.cjs} +13 -13
  81. package/dist/{chunk-PYKF7UIL.cjs.map → chunk-AOSSQDVO.cjs.map} +1 -1
  82. package/dist/{chunk-AT3MLMJV.js → chunk-AX6JGVTZ.js} +91 -20
  83. package/dist/chunk-AX6JGVTZ.js.map +1 -0
  84. package/dist/{chunk-WA64ZSCA.cjs → chunk-BFUOME67.cjs} +11 -11
  85. package/dist/{chunk-WA64ZSCA.cjs.map → chunk-BFUOME67.cjs.map} +1 -1
  86. package/dist/{chunk-YQ4JONWE.js → chunk-BRRP76WO.js} +401 -131
  87. package/dist/chunk-BRRP76WO.js.map +1 -0
  88. package/dist/{chunk-FCIZNUW5.js → chunk-CHAOFCHV.js} +3 -3
  89. package/dist/{chunk-FCIZNUW5.js.map → chunk-CHAOFCHV.js.map} +1 -1
  90. package/dist/chunk-CICO5GXD.cjs +12 -0
  91. package/dist/{chunk-73MWKDFK.cjs.map → chunk-CICO5GXD.cjs.map} +1 -1
  92. package/dist/{chunk-S3G5VLSW.js → chunk-CT3ZKTOF.js} +7 -6
  93. package/dist/chunk-CT3ZKTOF.js.map +1 -0
  94. package/dist/{chunk-SXWLLQ3D.js → chunk-D5R42W5D.js} +4 -4
  95. package/dist/{chunk-SXWLLQ3D.js.map → chunk-D5R42W5D.js.map} +1 -1
  96. package/dist/{chunk-BZZI7B43.cjs → chunk-DO7SAMEK.cjs} +47 -47
  97. package/dist/{chunk-BZZI7B43.cjs.map → chunk-DO7SAMEK.cjs.map} +1 -1
  98. package/dist/chunk-DZAICA5J.js +147 -0
  99. package/dist/chunk-DZAICA5J.js.map +1 -0
  100. package/dist/{chunk-2ZWM2RFH.cjs → chunk-EDN4Y4ZG.cjs} +9 -9
  101. package/dist/{chunk-2ZWM2RFH.cjs.map → chunk-EDN4Y4ZG.cjs.map} +1 -1
  102. package/dist/{chunk-A4R3H7RU.cjs → chunk-EZEEWCFJ.cjs} +264 -361
  103. package/dist/chunk-EZEEWCFJ.cjs.map +1 -0
  104. package/dist/{chunk-BEB4OFZF.cjs → chunk-FA2AFWLP.cjs} +51 -59
  105. package/dist/chunk-FA2AFWLP.cjs.map +1 -0
  106. package/dist/{chunk-ZBVZ7HXD.cjs → chunk-FQPEMEWX.cjs} +12 -12
  107. package/dist/{chunk-ZBVZ7HXD.cjs.map → chunk-FQPEMEWX.cjs.map} +1 -1
  108. package/dist/{chunk-LNIWCQR4.js → chunk-GLHXM7C4.js} +3 -3
  109. package/dist/{chunk-LNIWCQR4.js.map → chunk-GLHXM7C4.js.map} +1 -1
  110. package/dist/{chunk-6BD6ZBKT.cjs → chunk-GYEG3ZLZ.cjs} +10 -10
  111. package/dist/{chunk-6BD6ZBKT.cjs.map → chunk-GYEG3ZLZ.cjs.map} +1 -1
  112. package/dist/{chunk-A7NMUYEL.cjs → chunk-HDL2ARHX.cjs} +27 -27
  113. package/dist/{chunk-A7NMUYEL.cjs.map → chunk-HDL2ARHX.cjs.map} +1 -1
  114. package/dist/{chunk-5ZJOB5YS.js → chunk-HEV35TGT.js} +4 -4
  115. package/dist/{chunk-5ZJOB5YS.js.map → chunk-HEV35TGT.js.map} +1 -1
  116. package/dist/{chunk-LHYSY6SE.js → chunk-I4GAZH5Z.js} +186 -9
  117. package/dist/chunk-I4GAZH5Z.js.map +1 -0
  118. package/dist/{chunk-BQM5P2NO.cjs → chunk-JB6T4GLL.cjs} +260 -223
  119. package/dist/chunk-JB6T4GLL.cjs.map +1 -0
  120. package/dist/{chunk-QPOQX2DI.cjs → chunk-JFRT7S3B.cjs} +1026 -369
  121. package/dist/chunk-JFRT7S3B.cjs.map +1 -0
  122. package/dist/{chunk-JCNTKRAK.js → chunk-JONXFAPO.js} +5 -5
  123. package/dist/{chunk-JCNTKRAK.js.map → chunk-JONXFAPO.js.map} +1 -1
  124. package/dist/{chunk-WEJHA7AD.js → chunk-KF6K7TGH.js} +109 -75
  125. package/dist/chunk-KF6K7TGH.js.map +1 -0
  126. package/dist/{chunk-EDJDDY6H.js → chunk-KKBDXCLQ.js} +4 -4
  127. package/dist/{chunk-EDJDDY6H.js.map → chunk-KKBDXCLQ.js.map} +1 -1
  128. package/dist/{chunk-ZHOAZD7Q.cjs → chunk-L3LQNR34.cjs} +161 -161
  129. package/dist/{chunk-ZHOAZD7Q.cjs.map → chunk-L3LQNR34.cjs.map} +1 -1
  130. package/dist/{chunk-AF3TWL5C.cjs → chunk-LRQKXRXO.cjs} +4 -4
  131. package/dist/{chunk-AF3TWL5C.cjs.map → chunk-LRQKXRXO.cjs.map} +1 -1
  132. package/dist/chunk-MKJ4KXL5.cjs +12 -0
  133. package/dist/{chunk-6LAXM4J2.cjs.map → chunk-MKJ4KXL5.cjs.map} +1 -1
  134. package/dist/{chunk-KCH7WQA5.cjs → chunk-MRDBBHIZ.cjs} +48 -2
  135. package/dist/chunk-MRDBBHIZ.cjs.map +1 -0
  136. package/dist/{chunk-FXNHZFZV.js → chunk-MYKHJI2J.js} +3 -3
  137. package/dist/{chunk-FXNHZFZV.js.map → chunk-MYKHJI2J.js.map} +1 -1
  138. package/dist/{chunk-Z43KWLZF.js → chunk-NACVVDWB.js} +213 -12
  139. package/dist/chunk-NACVVDWB.js.map +1 -0
  140. package/dist/{chunk-72WUT4GG.js → chunk-NVY5CJFP.js} +4 -4
  141. package/dist/{chunk-72WUT4GG.js.map → chunk-NVY5CJFP.js.map} +1 -1
  142. package/dist/{chunk-WYFB7BN3.js → chunk-OD4SSY2U.js} +166 -48
  143. package/dist/chunk-OD4SSY2U.js.map +1 -0
  144. package/dist/{chunk-ZIH42CRX.cjs → chunk-OI2GQ25X.cjs} +7 -7
  145. package/dist/{chunk-ZIH42CRX.cjs.map → chunk-OI2GQ25X.cjs.map} +1 -1
  146. package/dist/{chunk-AU3IJNNP.js → chunk-OMZ37BM2.js} +5 -5
  147. package/dist/{chunk-AU3IJNNP.js.map → chunk-OMZ37BM2.js.map} +1 -1
  148. package/dist/{chunk-C4DYN2SD.cjs → chunk-OORXGFL5.cjs} +48 -48
  149. package/dist/{chunk-C4DYN2SD.cjs.map → chunk-OORXGFL5.cjs.map} +1 -1
  150. package/dist/{chunk-HNYLMPQQ.cjs → chunk-OTUPEF2W.cjs} +149 -78
  151. package/dist/chunk-OTUPEF2W.cjs.map +1 -0
  152. package/dist/{chunk-BRN3RGD2.js → chunk-OVI7IXYX.js} +3 -3
  153. package/dist/{chunk-BRN3RGD2.js.map → chunk-OVI7IXYX.js.map} +1 -1
  154. package/dist/{chunk-WMKEGJG4.js → chunk-P6YLZTRE.js} +3 -3
  155. package/dist/{chunk-WMKEGJG4.js.map → chunk-P6YLZTRE.js.map} +1 -1
  156. package/dist/{chunk-C2ZP5YS6.cjs → chunk-PFBRJBUL.cjs} +278 -160
  157. package/dist/chunk-PFBRJBUL.cjs.map +1 -0
  158. package/dist/{chunk-EJSYWAEV.js → chunk-QTD6HX4O.js} +5 -5
  159. package/dist/{chunk-EJSYWAEV.js.map → chunk-QTD6HX4O.js.map} +1 -1
  160. package/dist/{chunk-JL5UEBWT.cjs → chunk-ROR5ZR6A.cjs} +45 -45
  161. package/dist/{chunk-JL5UEBWT.cjs.map → chunk-ROR5ZR6A.cjs.map} +1 -1
  162. package/dist/{chunk-EDHV4CBU.js → chunk-S5TERQVW.js} +37 -134
  163. package/dist/chunk-S5TERQVW.js.map +1 -0
  164. package/dist/{chunk-FSLTU57P.js → chunk-SDBBMD4N.js} +3 -3
  165. package/dist/{chunk-FSLTU57P.js.map → chunk-SDBBMD4N.js.map} +1 -1
  166. package/dist/{chunk-36LIJXWC.js → chunk-SHFBB23Q.js} +4 -4
  167. package/dist/{chunk-36LIJXWC.js.map → chunk-SHFBB23Q.js.map} +1 -1
  168. package/dist/chunk-SIT26LAZ.cjs +614 -0
  169. package/dist/chunk-SIT26LAZ.cjs.map +1 -0
  170. package/dist/{chunk-C6KE2HSQ.cjs → chunk-SR725PWF.cjs} +1662 -1392
  171. package/dist/chunk-SR725PWF.cjs.map +1 -0
  172. package/dist/{chunk-S7WIRFNS.cjs → chunk-TKZXMWQF.cjs} +13 -12
  173. package/dist/chunk-TKZXMWQF.cjs.map +1 -0
  174. package/dist/{chunk-5PDSCELM.cjs → chunk-TQHGE7UC.cjs} +29 -164
  175. package/dist/chunk-TQHGE7UC.cjs.map +1 -0
  176. package/dist/{chunk-XBHRKLZU.cjs → chunk-TUNPBZY3.cjs} +4 -4
  177. package/dist/{chunk-XBHRKLZU.cjs.map → chunk-TUNPBZY3.cjs.map} +1 -1
  178. package/dist/{chunk-KOAI3KIV.cjs → chunk-TZEGUG6D.cjs} +4 -4
  179. package/dist/{chunk-KOAI3KIV.cjs.map → chunk-TZEGUG6D.cjs.map} +1 -1
  180. package/dist/chunk-UGHG7PGX.js +592 -0
  181. package/dist/chunk-UGHG7PGX.js.map +1 -0
  182. package/dist/{chunk-N3QTGPC7.cjs → chunk-UMGZDVOA.cjs} +303 -101
  183. package/dist/chunk-UMGZDVOA.cjs.map +1 -0
  184. package/dist/{chunk-RHDOAQCT.cjs → chunk-WCCATHMB.cjs} +140 -135
  185. package/dist/chunk-WCCATHMB.cjs.map +1 -0
  186. package/dist/{chunk-DYPRZ53S.cjs → chunk-WODMN3Q2.cjs} +27 -27
  187. package/dist/{chunk-DYPRZ53S.cjs.map → chunk-WODMN3Q2.cjs.map} +1 -1
  188. package/dist/{chunk-OZJCH7ZS.js → chunk-XVWOYKIK.js} +3 -3
  189. package/dist/{chunk-OZJCH7ZS.js.map → chunk-XVWOYKIK.js.map} +1 -1
  190. package/dist/{chunk-IHRDX7K6.js → chunk-YIURNGXI.js} +46 -56
  191. package/dist/chunk-YIURNGXI.js.map +1 -0
  192. package/dist/{chunk-GWUWRZXW.js → chunk-YVF2EM7B.js} +3 -3
  193. package/dist/{chunk-GWUWRZXW.js.map → chunk-YVF2EM7B.js.map} +1 -1
  194. package/dist/{chunk-EAMVXR5B.js → chunk-ZDKTPBGV.js} +4 -4
  195. package/dist/{chunk-EAMVXR5B.js.map → chunk-ZDKTPBGV.js.map} +1 -1
  196. package/dist/{chunk-A2LJG5GM.js → chunk-ZFMCDJGB.js} +803 -148
  197. package/dist/chunk-ZFMCDJGB.js.map +1 -0
  198. package/dist/{chunk-62ZXBMSD.js → chunk-ZLONH2UD.js} +46 -3
  199. package/dist/chunk-ZLONH2UD.js.map +1 -0
  200. package/dist/core/index.cjs +28 -27
  201. package/dist/core/index.d.cts +4 -4
  202. package/dist/core/index.d.ts +4 -4
  203. package/dist/core/index.js +8 -7
  204. package/dist/ddl-BL1NZLMY.d.cts +78 -0
  205. package/dist/ddl-D_oW-EVO.d.ts +78 -0
  206. package/dist/graph-extension/index.cjs +20 -20
  207. package/dist/graph-extension/index.d.cts +4 -4
  208. package/dist/graph-extension/index.d.ts +4 -4
  209. package/dist/graph-extension/index.js +4 -4
  210. package/dist/graph-merge/index.cjs +3418 -706
  211. package/dist/graph-merge/index.cjs.map +1 -1
  212. package/dist/graph-merge/index.d.cts +550 -58
  213. package/dist/graph-merge/index.d.ts +550 -58
  214. package/dist/graph-merge/index.js +3191 -497
  215. package/dist/graph-merge/index.js.map +1 -1
  216. package/dist/{index-Deyu8IXL.d.ts → index-COs_z6nV.d.cts} +47 -3
  217. package/dist/{index-DrRS51Ld.d.cts → index-D7d1u2gr.d.ts} +47 -3
  218. package/dist/index.cjs +233 -216
  219. package/dist/index.cjs.map +1 -1
  220. package/dist/index.d.cts +12 -12
  221. package/dist/index.d.ts +12 -12
  222. package/dist/index.js +35 -34
  223. package/dist/index.js.map +1 -1
  224. package/dist/indexes/index.cjs +13 -13
  225. package/dist/indexes/index.d.cts +4 -4
  226. package/dist/indexes/index.d.ts +4 -4
  227. package/dist/indexes/index.js +6 -6
  228. package/dist/interchange/index.cjs +53 -52
  229. package/dist/interchange/index.cjs.map +1 -1
  230. package/dist/interchange/index.d.cts +17 -13
  231. package/dist/interchange/index.d.ts +17 -13
  232. package/dist/interchange/index.js +17 -16
  233. package/dist/interchange/index.js.map +1 -1
  234. package/dist/{manager-ptr3JLRP.d.cts → manager-CYaff3n3.d.cts} +2 -2
  235. package/dist/{manager-DX983gJ0.d.ts → manager-D9eRT_ns.d.ts} +2 -2
  236. package/dist/{materialize-indexes-CpXoggWU.d.ts → materialize-indexes-DJ3ykH59.d.ts} +1 -1
  237. package/dist/{materialize-indexes-BXz77YqO.d.cts → materialize-indexes-DTtAnvfY.d.cts} +1 -1
  238. package/dist/{postgres-DwgEXSPI.d.cts → postgres-CcL4bAHS.d.ts} +1 -1
  239. package/dist/{postgres-QdOMHTBz.d.ts → postgres-DMj39MEu.d.cts} +1 -1
  240. package/dist/profiler/index.d.cts +9 -9
  241. package/dist/profiler/index.d.ts +9 -9
  242. package/dist/provenance/index.cjs +46 -46
  243. package/dist/provenance/index.cjs.map +1 -1
  244. package/dist/provenance/index.d.cts +7 -7
  245. package/dist/provenance/index.d.ts +7 -7
  246. package/dist/provenance/index.js +13 -13
  247. package/dist/provenance/index.js.map +1 -1
  248. package/dist/repair-validity-windows-BFNZvd_H.d.cts +166 -0
  249. package/dist/repair-validity-windows-DlUSqwCg.d.ts +166 -0
  250. package/dist/schema/index.cjs +45 -44
  251. package/dist/schema/index.d.cts +6 -6
  252. package/dist/schema/index.d.ts +6 -6
  253. package/dist/schema/index.js +18 -17
  254. package/dist/{searchable-BOUuA83f.d.ts → searchable-DHJMh_nj.d.cts} +1 -1
  255. package/dist/{searchable-BrUh0pdc.d.cts → searchable-bpE1bBfc.d.ts} +1 -1
  256. package/dist/{sqlite-BfYcV61E.d.cts → sqlite-D7Oz0SQh.d.ts} +1 -1
  257. package/dist/{sqlite-DK40XVR1.d.ts → sqlite-YppHrNFb.d.cts} +1 -1
  258. package/dist/{store-FTqBpuJD.d.cts → store-JUVvtmR4.d.cts} +228 -92
  259. package/dist/{store-Bo1psfVZ.d.ts → store-UzrGvZOo.d.ts} +228 -92
  260. package/dist/{temporal-DPtn0HOu.d.ts → temporal-5GqpYCk2.d.cts} +1 -1
  261. package/dist/{temporal-CXewwLTB.d.cts → temporal-CbnLzTmM.d.ts} +1 -1
  262. package/dist/{types-Du78EvpN.d.ts → types-BqFZTQmV.d.cts} +60 -9
  263. package/dist/{types-Du78EvpN.d.cts → types-BqFZTQmV.d.ts} +60 -9
  264. package/dist/{types-CeT7xM4M.d.ts → types-Bx74kaTt.d.ts} +1 -1
  265. package/dist/{types-XR1nHERK.d.cts → types-CXAhpW-c.d.cts} +1 -1
  266. package/dist/{where-CJsp-YUf.d.cts → where-DI_Fqz-X.d.cts} +1 -1
  267. package/dist/{where-CMbFiWwp.d.ts → where-O-k2wgYm.d.ts} +1 -1
  268. package/package.json +1 -1
  269. package/dist/chunk-32UETY6S.cjs.map +0 -1
  270. package/dist/chunk-5PDSCELM.cjs.map +0 -1
  271. package/dist/chunk-62ZXBMSD.js.map +0 -1
  272. package/dist/chunk-6LAXM4J2.cjs +0 -12
  273. package/dist/chunk-73MWKDFK.cjs +0 -12
  274. package/dist/chunk-A2LJG5GM.js.map +0 -1
  275. package/dist/chunk-A4R3H7RU.cjs.map +0 -1
  276. package/dist/chunk-ANKOXPCC.cjs +0 -267
  277. package/dist/chunk-ANKOXPCC.cjs.map +0 -1
  278. package/dist/chunk-AT3MLMJV.js.map +0 -1
  279. package/dist/chunk-BEB4OFZF.cjs.map +0 -1
  280. package/dist/chunk-BQM5P2NO.cjs.map +0 -1
  281. package/dist/chunk-C2ZP5YS6.cjs.map +0 -1
  282. package/dist/chunk-C6KE2HSQ.cjs.map +0 -1
  283. package/dist/chunk-DSAWGCMP.js +0 -254
  284. package/dist/chunk-DSAWGCMP.js.map +0 -1
  285. package/dist/chunk-E4OSGBCM.cjs.map +0 -1
  286. package/dist/chunk-EDHV4CBU.js.map +0 -1
  287. package/dist/chunk-FA6Z7B5L.js +0 -244
  288. package/dist/chunk-FA6Z7B5L.js.map +0 -1
  289. package/dist/chunk-GVAMRRZR.cjs +0 -268
  290. package/dist/chunk-GVAMRRZR.cjs.map +0 -1
  291. package/dist/chunk-GWXXHOLU.js.map +0 -1
  292. package/dist/chunk-HNYLMPQQ.cjs.map +0 -1
  293. package/dist/chunk-IHRDX7K6.js.map +0 -1
  294. package/dist/chunk-KCH7WQA5.cjs.map +0 -1
  295. package/dist/chunk-KXSMF6PL.js.map +0 -1
  296. package/dist/chunk-LHYSY6SE.js.map +0 -1
  297. package/dist/chunk-N3QTGPC7.cjs.map +0 -1
  298. package/dist/chunk-QPOQX2DI.cjs.map +0 -1
  299. package/dist/chunk-R7EWHKID.js.map +0 -1
  300. package/dist/chunk-RFDEYLIK.js.map +0 -1
  301. package/dist/chunk-RHDOAQCT.cjs.map +0 -1
  302. package/dist/chunk-S3G5VLSW.js.map +0 -1
  303. package/dist/chunk-S7WIRFNS.cjs.map +0 -1
  304. package/dist/chunk-UTVQPZPN.cjs.map +0 -1
  305. package/dist/chunk-WEJHA7AD.js.map +0 -1
  306. package/dist/chunk-WYFB7BN3.js.map +0 -1
  307. package/dist/chunk-YQ4JONWE.js.map +0 -1
  308. package/dist/chunk-Z43KWLZF.js.map +0 -1
  309. package/dist/ddl-mE07h_EM.d.cts +0 -38
  310. package/dist/ddl-opIn8DfX.d.ts +0 -38
@@ -1,34 +1,35 @@
1
- import { createStoreWithSchema } from '../chunk-YQ4JONWE.js';
2
- import '../chunk-IHRDX7K6.js';
3
- import { importGraphStream, exportGraphStream, importGraph, exportGraph } from '../chunk-AT3MLMJV.js';
4
- import '../chunk-WEJHA7AD.js';
5
- import { storeBackend, storeRuntime, transactionBackend } from '../chunk-LHYSY6SE.js';
6
- import { computeSchemaHash, serializeSchema } from '../chunk-KA4L2NZ3.js';
7
- import { defineNode } from '../chunk-36LIJXWC.js';
8
- import { encodeTupleKey, normalizeIdentityPair, identityReferenceKey, identityAssertionSemanticKey } from '../chunk-A2LJG5GM.js';
9
- import { generateId, compareStrings, compareCodePoints, lockRecordedGraphWrite, readRevisionOrigin, readRecordedClock } from '../chunk-EDHV4CBU.js';
1
+ import { createStoreWithSchema } from '../chunk-BRRP76WO.js';
2
+ import '../chunk-YIURNGXI.js';
3
+ import { importGraphStream, exportGraphStream, importGraph, exportGraph } from '../chunk-AX6JGVTZ.js';
4
+ import '../chunk-KF6K7TGH.js';
5
+ import { storeBackend, storeRuntime, transactionBackend } from '../chunk-I4GAZH5Z.js';
6
+ import { computeSchemaHash, serializeSchema } from '../chunk-2TYSSBU5.js';
7
+ import { defineNode } from '../chunk-SHFBB23Q.js';
8
+ import { forceWriteTransactionRevision, normalizeIdentityPair, identityReferenceKey, identityValidityWindowsOverlap, identityAssertionSemanticKey } from '../chunk-ZFMCDJGB.js';
9
+ import { compareStrings, generateId, encodeTupleKey, lockRecordedGraphWrite, readRevisionOrigin, readRecordedClock, forceRecordedGraphRevision, advanceRevisionClock, compareCodePoints } from '../chunk-S5TERQVW.js';
10
10
  import { sortedReplacer } from '../chunk-JBQQ7THH.js';
11
- import '../chunk-DSAWGCMP.js';
12
- import { createSqlSchema } from '../chunk-72WUT4GG.js';
13
- import { snapshotExportContention } from '../chunk-FA6Z7B5L.js';
14
- import { getNodeKinds, getEdgeKinds, defineInternalGraph } from '../chunk-5ZJOB5YS.js';
15
- import { computeUniqueKey } from '../chunk-LNIWCQR4.js';
16
- import { canonicalizeDatabaseTimestamp } from '../chunk-R7EWHKID.js';
17
- import '../chunk-EDJDDY6H.js';
18
- import '../chunk-FSLTU57P.js';
19
- import '../chunk-WMKEGJG4.js';
11
+ import { snapshotExportContention } from '../chunk-UGHG7PGX.js';
12
+ import { createSqlSchema } from '../chunk-NVY5CJFP.js';
13
+ import '../chunk-6LN7FIVU.js';
14
+ import { getNodeKinds, getEdgeKinds, defineInternalGraph } from '../chunk-HEV35TGT.js';
15
+ import { validateEdgeEndpoints, computeUniqueKey } from '../chunk-GLHXM7C4.js';
16
+ import '../chunk-DZAICA5J.js';
17
+ import { canonicalizeDatabaseTimestamp } from '../chunk-5K2BKEBD.js';
18
+ import '../chunk-KKBDXCLQ.js';
19
+ import '../chunk-SDBBMD4N.js';
20
+ import '../chunk-P6YLZTRE.js';
20
21
  import { sha256Hex } from '../chunk-K34OPSJK.js';
21
22
  import '../chunk-2EBMPJBO.js';
22
- import '../chunk-EAMVXR5B.js';
23
- import { asCompiledStatementSql, sql, asCompiledRowsSql } from '../chunk-QOJ2L2AD.js';
23
+ import '../chunk-ZDKTPBGV.js';
24
+ import { asCompiledStatementSql, sql, asCompiledRowsSql } from '../chunk-3DMMGT6S.js';
24
25
  import '../chunk-AMKPPFUO.js';
25
26
  import '../chunk-W6OG3HDG.js';
26
27
  import '../chunk-PC2UBAUB.js';
27
- import { ok, err, isErr, computeTransitiveClosure, isReachable } from '../chunk-SXWLLQ3D.js';
28
- export { isErr, isOk, unwrap } from '../chunk-SXWLLQ3D.js';
28
+ import { ok, err, isErr, computeTransitiveClosure, isReachable } from '../chunk-D5R42W5D.js';
29
+ export { isErr, isOk, unwrap } from '../chunk-D5R42W5D.js';
29
30
  import { META_EDGE_EQUIVALENT_TO, META_EDGE_SAME_AS } from '../chunk-JXXDZTMN.js';
30
- import '../chunk-OZJCH7ZS.js';
31
- import { TypeGraphError, ConfigurationError, IdentityContradictionError, NodeNotFoundError } from '../chunk-62ZXBMSD.js';
31
+ import '../chunk-XVWOYKIK.js';
32
+ import { TypeGraphError, ConfigurationError, IdentityContradictionError, NodeNotFoundError, IdentityEndpointValidityError } from '../chunk-ZLONH2UD.js';
32
33
  import '../chunk-FWJKWH6U.js';
33
34
  import { createDataKeyedBag, hasOwnKey, requireDefined, readOwnProperty } from '../chunk-DYATYRY4.js';
34
35
  import { z } from 'zod';
@@ -106,8 +107,20 @@ var MERGE_ERROR_CODES = {
106
107
  branch: "GRAPH_MERGE_BRANCH_ERROR",
107
108
  similarityUnavailable: "GRAPH_MERGE_SIMILARITY_UNAVAILABLE",
108
109
  conflict: "GRAPH_MERGE_CONFLICT",
110
+ constraintConflict: "GRAPH_MERGE_CONSTRAINT_CONFLICT",
109
111
  identityConflict: "GRAPH_MERGE_IDENTITY_CONFLICT",
110
- baseVersionMismatch: "GRAPH_MERGE_BASE_VERSION_MISMATCH"
112
+ baseVersionMismatch: "GRAPH_MERGE_BASE_VERSION_MISMATCH",
113
+ planCapability: "GRAPH_MERGE_PLAN_CAPABILITY",
114
+ planInvalid: "GRAPH_MERGE_PLAN_INVALID",
115
+ planVersionUnsupported: "GRAPH_MERGE_PLAN_VERSION_UNSUPPORTED",
116
+ planDigestMismatch: "GRAPH_MERGE_PLAN_DIGEST_MISMATCH",
117
+ planTargetMismatch: "GRAPH_MERGE_PLAN_TARGET_MISMATCH",
118
+ planSchemaMismatch: "GRAPH_MERGE_PLAN_SCHEMA_MISMATCH",
119
+ planOriginMismatch: "GRAPH_MERGE_PLAN_ORIGIN_MISMATCH",
120
+ planStale: "GRAPH_MERGE_PLAN_STALE",
121
+ planningStale: "GRAPH_MERGE_PLANNING_STALE",
122
+ candidateSource: "GRAPH_MERGE_CANDIDATE_SOURCE",
123
+ evidence: "GRAPH_MERGE_EVIDENCE"
111
124
  };
112
125
  function toTypeGraphErrorOptions(category, options) {
113
126
  return {
@@ -164,6 +177,31 @@ var MergeConflictError = class extends MergeError {
164
177
  this.name = "MergeConflictError";
165
178
  }
166
179
  };
180
+ var MergeConstraintConflictError = class extends MergeError {
181
+ static errorCategory = "constraint";
182
+ code = MERGE_ERROR_CODES.constraintConflict;
183
+ constructor(cause) {
184
+ super(`The resolved merge would violate ${cause.name}: ${cause.message}`, {
185
+ cause,
186
+ details: {
187
+ ...cause.details,
188
+ constraintCode: cause.code,
189
+ constraintErrorName: cause.name,
190
+ constraintDetails: cause.details
191
+ },
192
+ suggestion: cause.suggestion ?? "Change the branch data or target state so the resolved graph satisfies its constraints, then retry the merge."
193
+ });
194
+ this.name = "MergeConstraintConflictError";
195
+ }
196
+ };
197
+ function translateMergeCommitError(error) {
198
+ if (error instanceof MergeError) return error;
199
+ if (!(error instanceof TypeGraphError)) return error;
200
+ if (error.category !== "constraint" || error.code.startsWith("IDENTITY_")) {
201
+ return error;
202
+ }
203
+ return new MergeConstraintConflictError(error);
204
+ }
167
205
  var IdentityMergeConflictError = class extends MergeError {
168
206
  code = MERGE_ERROR_CODES.identityConflict;
169
207
  constructor(message, options = {}) {
@@ -181,56 +219,496 @@ var BaseVersionMismatchError = class extends MergeError {
181
219
  this.name = "BaseVersionMismatchError";
182
220
  }
183
221
  };
222
+ var MergePlanCapabilityError = class extends MergeError {
223
+ static errorCategory = "user";
224
+ code = MERGE_ERROR_CODES.planCapability;
225
+ constructor(message, options = {}) {
226
+ super(message, options);
227
+ this.name = "MergePlanCapabilityError";
228
+ }
229
+ };
230
+ var InvalidMergePlanError = class extends MergeError {
231
+ static errorCategory = "user";
232
+ code = MERGE_ERROR_CODES.planInvalid;
233
+ constructor(message, options = {}) {
234
+ super(message, options);
235
+ this.name = "InvalidMergePlanError";
236
+ }
237
+ };
238
+ var UnsupportedMergePlanVersionError = class extends InvalidMergePlanError {
239
+ code = MERGE_ERROR_CODES.planVersionUnsupported;
240
+ constructor(message, options = {}) {
241
+ super(message, options);
242
+ this.name = "UnsupportedMergePlanVersionError";
243
+ }
244
+ };
245
+ var MergePlanDigestMismatchError = class extends InvalidMergePlanError {
246
+ code = MERGE_ERROR_CODES.planDigestMismatch;
247
+ constructor(message, options = {}) {
248
+ super(message, options);
249
+ this.name = "MergePlanDigestMismatchError";
250
+ }
251
+ };
252
+ var MergePlanTargetMismatchError = class extends InvalidMergePlanError {
253
+ code = MERGE_ERROR_CODES.planTargetMismatch;
254
+ constructor(message, options = {}) {
255
+ super(message, options);
256
+ this.name = "MergePlanTargetMismatchError";
257
+ }
258
+ };
259
+ var MergePlanSchemaMismatchError = class extends InvalidMergePlanError {
260
+ code = MERGE_ERROR_CODES.planSchemaMismatch;
261
+ constructor(message, options = {}) {
262
+ super(message, options);
263
+ this.name = "MergePlanSchemaMismatchError";
264
+ }
265
+ };
266
+ var MergePlanOriginMismatchError = class extends InvalidMergePlanError {
267
+ code = MERGE_ERROR_CODES.planOriginMismatch;
268
+ constructor(message, options = {}) {
269
+ super(message, options);
270
+ this.name = "MergePlanOriginMismatchError";
271
+ }
272
+ };
273
+ var StaleMergePlanError = class extends MergeError {
274
+ code = MERGE_ERROR_CODES.planStale;
275
+ constructor(message, options = {}) {
276
+ super(message, {
277
+ ...options,
278
+ suggestion: options.suggestion ?? "Create a new merge plan against the target's current revision, review it, and apply that plan instead."
279
+ });
280
+ this.name = "StaleMergePlanError";
281
+ }
282
+ };
283
+ var MergePlanningStaleError = class extends StaleMergePlanError {
284
+ code = MERGE_ERROR_CODES.planningStale;
285
+ constructor(message, options = {}) {
286
+ super(message, options);
287
+ this.name = "MergePlanningStaleError";
288
+ }
289
+ };
290
+ var CandidateSourceError = class extends MergeError {
291
+ code = MERGE_ERROR_CODES.candidateSource;
292
+ constructor(message, options = {}) {
293
+ super(message, options);
294
+ this.name = "CandidateSourceError";
295
+ }
296
+ };
297
+ var MatchEvidenceError = class extends MergeError {
298
+ code = MERGE_ERROR_CODES.evidence;
299
+ constructor(message, options = {}) {
300
+ super(message, options);
301
+ this.name = "MatchEvidenceError";
302
+ }
303
+ };
184
304
  function describeCause(cause) {
185
305
  return cause instanceof Error ? cause.message : String(cause);
186
306
  }
187
307
 
188
- // src/graph-merge/union-find.ts
189
- var UnionFind = class {
190
- constructor(compare) {
191
- this.compare = compare;
308
+ // src/graph-merge/temporal-identity-closure.ts
309
+ function disjointCacheKey(left, right) {
310
+ return left <= right ? `${left}\0${right}` : `${right}\0${left}`;
311
+ }
312
+ function areDisjoint(state, left, right) {
313
+ const key = disjointCacheKey(left, right);
314
+ const cached = state.disjointCache.get(key);
315
+ if (cached !== void 0) return cached;
316
+ const result = state.context.areDisjoint(left, right);
317
+ state.disjointCache.set(key, result);
318
+ return result;
319
+ }
320
+ function findRoot(components, index) {
321
+ let root = index;
322
+ while (components.parent[root] !== root) {
323
+ root = requireDefined(components.parent[root]);
192
324
  }
193
- compare;
194
- parent = /* @__PURE__ */ new Map();
195
- /** Adds `value` as its own singleton set if it is not already present. */
196
- add(value) {
197
- if (!this.parent.has(value)) {
198
- this.parent.set(value, value);
325
+ return root;
326
+ }
327
+ function componentKindsConflict(state, leftKinds, rightKinds) {
328
+ for (const left of leftKinds.keys()) {
329
+ for (const right of rightKinds.keys()) {
330
+ if (left !== right && areDisjoint(state, left, right)) return true;
199
331
  }
200
332
  }
201
- /** Returns the representative of `value`'s set, compressing the path to it. */
202
- find(value) {
203
- this.add(value);
204
- let root = value;
205
- while (this.parent.get(root) !== root) {
206
- root = requireDefined(this.parent.get(root));
333
+ return false;
334
+ }
335
+ function unionReferences(state, leftIndex, rightIndex) {
336
+ const { components } = state;
337
+ const leftRoot = findRoot(components, leftIndex);
338
+ const rightRoot = findRoot(components, rightIndex);
339
+ if (leftRoot === rightRoot) return;
340
+ const [parentRoot, childRoot] = requireDefined(components.sizes[leftRoot]) >= requireDefined(components.sizes[rightRoot]) ? [leftRoot, rightRoot] : [rightRoot, leftRoot];
341
+ const parentKinds = requireDefined(components.kindFirstReference[parentRoot]);
342
+ const childKinds = requireDefined(components.kindFirstReference[childRoot]);
343
+ const parentDifferent = requireDefined(
344
+ components.differentAssertions[parentRoot]
345
+ );
346
+ const childDifferent = requireDefined(
347
+ components.differentAssertions[childRoot]
348
+ );
349
+ const previousParentSize = requireDefined(components.sizes[parentRoot]);
350
+ const previousViolation = requireDefined(
351
+ components.violatesOntology[parentRoot]
352
+ );
353
+ const changedKinds = [...childKinds].flatMap(
354
+ ([kind, childFirstReference]) => {
355
+ const previousFirstReference = parentKinds.get(kind);
356
+ return previousFirstReference === void 0 || childFirstReference < previousFirstReference ? [
357
+ {
358
+ kind,
359
+ previousFirstReference,
360
+ nextFirstReference: childFirstReference
361
+ }
362
+ ] : [];
207
363
  }
208
- let cursor = value;
209
- while (cursor !== root) {
210
- const next = requireDefined(this.parent.get(cursor));
211
- this.parent.set(cursor, root);
212
- cursor = next;
364
+ );
365
+ const addedDifferent = [...childDifferent].filter(
366
+ (assertionIndex) => !parentDifferent.has(assertionIndex)
367
+ );
368
+ const addedContradictions = [];
369
+ const crossesDisjointKinds = componentKindsConflict(
370
+ state,
371
+ parentKinds,
372
+ childKinds
373
+ );
374
+ components.parent[childRoot] = parentRoot;
375
+ components.sizes[parentRoot] = previousParentSize + requireDefined(components.sizes[childRoot]);
376
+ for (const { kind, nextFirstReference } of changedKinds) {
377
+ parentKinds.set(kind, nextFirstReference);
378
+ }
379
+ for (const assertionIndex of addedDifferent) {
380
+ parentDifferent.add(assertionIndex);
381
+ }
382
+ components.violatesOntology[parentRoot] = previousViolation || requireDefined(components.violatesOntology[childRoot]) || crossesDisjointKinds;
383
+ for (const assertionIndex of childDifferent) {
384
+ const assertion = requireDefined(state.assertions[assertionIndex]);
385
+ const aIndex = requireDefined(
386
+ state.referenceIndexByKey.get(mergeKeyOf(assertion.a))
387
+ );
388
+ const bIndex = requireDefined(
389
+ state.referenceIndexByKey.get(mergeKeyOf(assertion.b))
390
+ );
391
+ if (findRoot(components, aIndex) === findRoot(components, bIndex) && !state.contradictoryAssertions.has(assertionIndex)) {
392
+ state.contradictoryAssertions.add(assertionIndex);
393
+ addedContradictions.push(assertionIndex);
213
394
  }
214
- return root;
215
395
  }
216
- /** Merges the sets of `left` and `right`; the `compare`-minimal root wins. */
217
- union(left, right) {
218
- const leftRoot = this.find(left);
219
- const rightRoot = this.find(right);
220
- if (leftRoot === rightRoot) {
221
- return;
396
+ state.undo.push(() => {
397
+ for (const assertionIndex of addedContradictions) {
398
+ state.contradictoryAssertions.delete(assertionIndex);
222
399
  }
223
- if (this.compare(leftRoot, rightRoot) <= 0) {
224
- this.parent.set(rightRoot, leftRoot);
400
+ components.violatesOntology[parentRoot] = previousViolation;
401
+ for (const assertionIndex of addedDifferent) {
402
+ parentDifferent.delete(assertionIndex);
403
+ }
404
+ for (const { kind, previousFirstReference } of changedKinds) {
405
+ if (previousFirstReference === void 0) parentKinds.delete(kind);
406
+ else parentKinds.set(kind, previousFirstReference);
407
+ }
408
+ components.sizes[parentRoot] = previousParentSize;
409
+ components.parent[childRoot] = childRoot;
410
+ });
411
+ }
412
+ function activateReference(state, referenceIndex) {
413
+ if (state.activeReferences[referenceIndex] === true) return;
414
+ const reference = requireDefined(state.references[referenceIndex]);
415
+ const peers = [...state.activeReferencesById.get(reference.id) ?? []];
416
+ let addedSharedIdViolations = 0;
417
+ for (const peerIndex of peers) {
418
+ const peer = requireDefined(state.references[peerIndex]);
419
+ if (areDisjoint(state, reference.kind, peer.kind)) {
420
+ addedSharedIdViolations += 1;
421
+ }
422
+ }
423
+ state.sharedIdViolationCount.value += addedSharedIdViolations;
424
+ state.activeReferences[referenceIndex] = true;
425
+ const activeForId = state.activeReferencesById.get(reference.id) ?? /* @__PURE__ */ new Set();
426
+ activeForId.add(referenceIndex);
427
+ state.activeReferencesById.set(reference.id, activeForId);
428
+ state.undo.push(() => {
429
+ activeForId.delete(referenceIndex);
430
+ if (activeForId.size === 0) {
431
+ state.activeReferencesById.delete(reference.id);
432
+ }
433
+ state.activeReferences[referenceIndex] = false;
434
+ state.sharedIdViolationCount.value -= addedSharedIdViolations;
435
+ });
436
+ if (state.context.sameIdAcrossKinds === "fold") {
437
+ for (const peerIndex of peers) {
438
+ unionReferences(state, referenceIndex, peerIndex);
439
+ }
440
+ }
441
+ }
442
+ function addDifferentAssertion(state, assertionIndex) {
443
+ const assertion = requireDefined(state.assertions[assertionIndex]);
444
+ const aIndex = requireDefined(
445
+ state.referenceIndexByKey.get(mergeKeyOf(assertion.a))
446
+ );
447
+ const bIndex = requireDefined(
448
+ state.referenceIndexByKey.get(mergeKeyOf(assertion.b))
449
+ );
450
+ const aRoot = findRoot(state.components, aIndex);
451
+ const bRoot = findRoot(state.components, bIndex);
452
+ if (aRoot === bRoot) {
453
+ state.contradictoryAssertions.add(assertionIndex);
454
+ state.undo.push(() => {
455
+ state.contradictoryAssertions.delete(assertionIndex);
456
+ });
457
+ return;
458
+ }
459
+ const aDifferent = requireDefined(
460
+ state.components.differentAssertions[aRoot]
461
+ );
462
+ const bDifferent = requireDefined(
463
+ state.components.differentAssertions[bRoot]
464
+ );
465
+ aDifferent.add(assertionIndex);
466
+ bDifferent.add(assertionIndex);
467
+ state.undo.push(() => {
468
+ aDifferent.delete(assertionIndex);
469
+ bDifferent.delete(assertionIndex);
470
+ });
471
+ }
472
+ function rollback(state, snapshot) {
473
+ while (state.undo.length > snapshot) {
474
+ requireDefined(state.undo.pop())();
475
+ }
476
+ }
477
+ function firstDisjointPair(state, kinds) {
478
+ for (const [index, left] of kinds.entries()) {
479
+ for (const right of kinds.slice(index + 1)) {
480
+ if (areDisjoint(state, left, right)) return [left, right];
481
+ }
482
+ }
483
+ return void 0;
484
+ }
485
+ function firstAssertionIndex(assertionIndexes) {
486
+ let first = Number.POSITIVE_INFINITY;
487
+ for (const assertionIndex of assertionIndexes) {
488
+ first = Math.min(first, assertionIndex);
489
+ }
490
+ return first;
491
+ }
492
+ function activeClassMembers(state, root) {
493
+ return state.references.filter(
494
+ (_reference, index) => state.activeReferences[index] === true && findRoot(state.components, index) === root
495
+ ).toSorted(
496
+ (left, right) => compareMergeKeys(
497
+ mergeKey(left.kind, left.id),
498
+ mergeKey(right.kind, right.id)
499
+ )
500
+ );
501
+ }
502
+ function componentKindsInReferenceOrder(state, root) {
503
+ return [...requireDefined(state.components.kindFirstReference[root])].toSorted((left, right) => left[1] - right[1]).map(([kind]) => kind);
504
+ }
505
+ function assertCoordinateConsistent(state) {
506
+ if (state.sharedIdViolationCount.value > 0) {
507
+ const activeById = /* @__PURE__ */ new Map();
508
+ for (const [index, reference] of state.references.entries()) {
509
+ if (state.activeReferences[index] !== true) continue;
510
+ const references = activeById.get(reference.id) ?? [];
511
+ references.push(reference);
512
+ activeById.set(reference.id, references);
513
+ }
514
+ for (const [sharedId, references] of activeById) {
515
+ const disjointKinds = firstDisjointPair(state, [
516
+ ...new Set(references.map((reference) => reference.kind))
517
+ ]);
518
+ if (disjointKinds === void 0) continue;
519
+ throw new IdentityMergeConflictError(
520
+ "The merged graph would give one id to two ontology-disjoint kinds.",
521
+ { details: { disjointKinds, sharedId } }
522
+ );
523
+ }
524
+ }
525
+ if (state.contradictoryAssertions.size > 0) {
526
+ const assertionIndex = firstAssertionIndex(state.contradictoryAssertions);
527
+ const assertion = requireDefined(state.assertions[assertionIndex]);
528
+ const root = findRoot(
529
+ state.components,
530
+ requireDefined(state.referenceIndexByKey.get(mergeKeyOf(assertion.a)))
531
+ );
532
+ throw new IdentityMergeConflictError(
533
+ "The merged identity ledger would assert one pair of nodes is both the same and different.",
534
+ {
535
+ details: {
536
+ assertion,
537
+ sameClass: activeClassMembers(state, root)
538
+ }
539
+ }
540
+ );
541
+ }
542
+ const checkedRoots = /* @__PURE__ */ new Set();
543
+ for (const [referenceIndex, active] of state.activeReferences.entries()) {
544
+ if (!active) continue;
545
+ const root = findRoot(state.components, referenceIndex);
546
+ if (checkedRoots.has(root)) continue;
547
+ checkedRoots.add(root);
548
+ if (!requireDefined(state.components.violatesOntology[root])) continue;
549
+ const sameClass = activeClassMembers(state, root);
550
+ const disjointKinds = firstDisjointPair(
551
+ state,
552
+ componentKindsInReferenceOrder(state, root)
553
+ );
554
+ if (disjointKinds === void 0) continue;
555
+ throw new IdentityMergeConflictError(
556
+ "The merged identity ledger would join two ontology-disjoint kinds into one class.",
557
+ { details: { disjointKinds, sameClass } }
558
+ );
559
+ }
560
+ }
561
+ function addRangeEvent(events, node, left, right, start, end, add) {
562
+ if (start <= left && right <= end) {
563
+ add(requireDefined(events[node]));
564
+ return;
565
+ }
566
+ const middle = Math.floor((left + right) / 2);
567
+ if (start < middle) {
568
+ addRangeEvent(events, node * 2, left, middle, start, end, add);
569
+ }
570
+ if (middle < end) {
571
+ addRangeEvent(events, node * 2 + 1, middle, right, start, end, add);
572
+ }
573
+ }
574
+ function visitTemporalEvents(events, state, node, left, right) {
575
+ const snapshot = state.undo.length;
576
+ const event = requireDefined(events[node]);
577
+ for (const referenceIndex of event.references) {
578
+ activateReference(state, referenceIndex);
579
+ }
580
+ for (const assertionIndex of event.sameAssertions) {
581
+ const assertion = requireDefined(state.assertions[assertionIndex]);
582
+ unionReferences(
583
+ state,
584
+ requireDefined(state.referenceIndexByKey.get(mergeKeyOf(assertion.a))),
585
+ requireDefined(state.referenceIndexByKey.get(mergeKeyOf(assertion.b)))
586
+ );
587
+ }
588
+ for (const assertionIndex of event.differentAssertions) {
589
+ addDifferentAssertion(state, assertionIndex);
590
+ }
591
+ if (right - left === 1) {
592
+ assertCoordinateConsistent(state);
593
+ } else {
594
+ const middle = Math.floor((left + right) / 2);
595
+ visitTemporalEvents(events, state, node * 2, left, middle);
596
+ visitTemporalEvents(events, state, node * 2 + 1, middle, right);
597
+ }
598
+ rollback(state, snapshot);
599
+ }
600
+ function mergeRanges(ranges) {
601
+ const merged = [];
602
+ for (const range of ranges.toSorted(
603
+ (left, right) => left.start - right.start
604
+ )) {
605
+ const previous = merged.at(-1);
606
+ if (previous === void 0 || previous.end < range.start) {
607
+ merged.push({ ...range });
225
608
  } else {
226
- this.parent.set(leftRoot, rightRoot);
609
+ previous.end = Math.max(previous.end, range.end);
227
610
  }
228
611
  }
229
- /** Every value seen by {@link add}/{@link find}/{@link union}, insertion order. */
230
- members() {
231
- return [...this.parent.keys()];
612
+ return merged;
613
+ }
614
+ function assertTemporalIdentityClosureConsistent(assertions, identityContext, nodeUniverse) {
615
+ const boundaries = /* @__PURE__ */ new Set();
616
+ for (const assertion of assertions) {
617
+ boundaries.add(assertion.validFrom);
618
+ if (assertion.validTo !== void 0) boundaries.add(assertion.validTo);
232
619
  }
233
- };
620
+ const sortedBoundaries = [...boundaries].toSorted();
621
+ const leafCount = Math.max(1, sortedBoundaries.length);
622
+ const boundaryIndex = new Map(
623
+ sortedBoundaries.map((boundary, index) => [boundary, index])
624
+ );
625
+ const assertionRanges = assertions.map(
626
+ (assertion, assertionIndex) => ({
627
+ assertionIndex,
628
+ start: requireDefined(boundaryIndex.get(assertion.validFrom)),
629
+ end: assertion.validTo === void 0 ? leafCount : requireDefined(boundaryIndex.get(assertion.validTo))
630
+ })
631
+ );
632
+ const references = [];
633
+ const referenceIndexByKey = /* @__PURE__ */ new Map();
634
+ const addReference = (reference) => {
635
+ const key = mergeKey(reference.kind, reference.id);
636
+ const existing = referenceIndexByKey.get(key);
637
+ if (existing !== void 0) return existing;
638
+ const index = references.length;
639
+ references.push(reference);
640
+ referenceIndexByKey.set(key, index);
641
+ return index;
642
+ };
643
+ const alwaysActive = new Set(nodeUniverse.map((node) => addReference(node)));
644
+ const rangesByReference = /* @__PURE__ */ new Map();
645
+ for (const range of assertionRanges) {
646
+ if (range.start >= range.end) continue;
647
+ const assertion = requireDefined(assertions[range.assertionIndex]);
648
+ for (const endpoint of [assertion.a, assertion.b]) {
649
+ const referenceIndex = addReference(endpoint);
650
+ const ranges = rangesByReference.get(referenceIndex) ?? [];
651
+ ranges.push({ start: range.start, end: range.end });
652
+ rangesByReference.set(referenceIndex, ranges);
653
+ }
654
+ }
655
+ const events = Array.from(
656
+ { length: leafCount * 4 },
657
+ () => ({ references: [], sameAssertions: [], differentAssertions: [] })
658
+ );
659
+ for (const [referenceIndex, ranges] of rangesByReference) {
660
+ if (alwaysActive.has(referenceIndex)) continue;
661
+ for (const range of mergeRanges(ranges)) {
662
+ addRangeEvent(
663
+ events,
664
+ 1,
665
+ 0,
666
+ leafCount,
667
+ range.start,
668
+ range.end,
669
+ (event) => event.references.push(referenceIndex)
670
+ );
671
+ }
672
+ }
673
+ for (const range of assertionRanges) {
674
+ if (range.start >= range.end) continue;
675
+ const assertion = requireDefined(assertions[range.assertionIndex]);
676
+ addRangeEvent(
677
+ events,
678
+ 1,
679
+ 0,
680
+ leafCount,
681
+ range.start,
682
+ range.end,
683
+ (event) => assertion.relation === "same" ? event.sameAssertions.push(range.assertionIndex) : event.differentAssertions.push(range.assertionIndex)
684
+ );
685
+ }
686
+ const state = {
687
+ assertions,
688
+ context: identityContext,
689
+ references,
690
+ referenceIndexByKey,
691
+ activeReferences: references.map(() => false),
692
+ activeReferencesById: /* @__PURE__ */ new Map(),
693
+ components: {
694
+ parent: references.map((_reference, index) => index),
695
+ sizes: references.map(() => 1),
696
+ kindFirstReference: references.map(
697
+ (reference, index) => /* @__PURE__ */ new Map([[reference.kind, index]])
698
+ ),
699
+ differentAssertions: references.map(() => /* @__PURE__ */ new Set()),
700
+ violatesOntology: references.map(() => false)
701
+ },
702
+ contradictoryAssertions: /* @__PURE__ */ new Set(),
703
+ undo: [],
704
+ disjointCache: /* @__PURE__ */ new Map(),
705
+ sharedIdViolationCount: { value: 0 }
706
+ };
707
+ for (const referenceIndex of alwaysActive)
708
+ activateReference(state, referenceIndex);
709
+ state.undo.length = 0;
710
+ visitTemporalEvents(events, state, 1, 0, leafCount);
711
+ }
234
712
 
235
713
  // src/graph-merge/merge-identity.ts
236
714
  function endpointTuple(assertion) {
@@ -246,12 +724,18 @@ function identitySemanticKey(assertion) {
246
724
  assertion.b
247
725
  );
248
726
  }
727
+ function identityDedupeKey(assertion) {
728
+ const semantic = identitySemanticKey(assertion);
729
+ if (assertion.validTo === void 0) return semantic;
730
+ return encodeTupleKey([semantic, assertion.validFrom, assertion.validTo]);
731
+ }
249
732
  function compareIdentitySurvivors(left, right) {
250
733
  const byValidity = compareCodePoints(left.validFrom, right.validFrom);
251
734
  return byValidity === 0 ? compareCodePoints(left.id, right.id) : byValidity;
252
735
  }
253
736
  var DUPLICATE_IDENTITY_ASSERTION_DROP_REASON = "identity:duplicate-assertion";
254
737
  var REDUNDANT_IDENTITY_ASSERTION_DROP_REASON = "identity:endpoints-collapsed";
738
+ var EMPTY_REMAPPED_IDENTITY_WINDOW_DROP_REASON = "identity:empty-remapped-window";
255
739
  function droppedIdentityAssertion(assertion, reason) {
256
740
  return { kind: "identity", id: assertion.id, reason };
257
741
  }
@@ -259,7 +743,7 @@ function dedupeIdentityAssertions(assertions, committedIds) {
259
743
  const survivorBySemantic = /* @__PURE__ */ new Map();
260
744
  const dropped = [];
261
745
  for (const assertion of assertions) {
262
- const key = identitySemanticKey(assertion);
746
+ const key = identityDedupeKey(assertion);
263
747
  const previous = survivorBySemantic.get(key);
264
748
  if (previous === void 0) {
265
749
  survivorBySemantic.set(key, assertion);
@@ -278,7 +762,7 @@ function dedupeIdentityAssertions(assertions, committedIds) {
278
762
  }
279
763
  return {
280
764
  survivors: [...survivorBySemantic.values()].toSorted(
281
- (left, right) => compareCodePoints(identitySemanticKey(left), identitySemanticKey(right))
765
+ (left, right) => compareCodePoints(identityDedupeKey(left), identityDedupeKey(right))
282
766
  ),
283
767
  dropped
284
768
  };
@@ -292,13 +776,26 @@ function assertNoOpposingIdentityRelations(assertions) {
292
776
  byEndpoint.set(key, group);
293
777
  }
294
778
  for (const [endpoint, group] of byEndpoint) {
295
- if (new Set(group.map((assertion) => assertion.relation)).size < 2) {
296
- continue;
297
- }
298
- throw new IdentityMergeConflictError(
299
- "Branches asserted opposing identity relations for one endpoint pair.",
300
- { details: { endpoint, assertions: group } }
779
+ const same = group.filter((assertion) => assertion.relation === "same");
780
+ const different = group.filter(
781
+ (assertion) => assertion.relation === "different"
301
782
  );
783
+ for (const sameAssertion of same) {
784
+ for (const differentAssertion of different) {
785
+ if (!identityValidityWindowsOverlap(sameAssertion, differentAssertion)) {
786
+ continue;
787
+ }
788
+ throw new IdentityMergeConflictError(
789
+ "Branches asserted opposing identity relations for one endpoint pair.",
790
+ {
791
+ details: {
792
+ endpoint,
793
+ assertions: [sameAssertion, differentAssertion]
794
+ }
795
+ }
796
+ );
797
+ }
798
+ }
302
799
  }
303
800
  }
304
801
  function assertNoRetractReassertRace(staging) {
@@ -415,9 +912,10 @@ function resolveIdentityEndpoint(endpoint, canonicalOf, retypeMap) {
415
912
  id: idOf(canonical)
416
913
  };
417
914
  }
418
- function remapIdentityAssertionEndpoints(assertions, canonicalOf, retypeMap, storedRowsById) {
915
+ function remapIdentityAssertionEndpoints(assertions, canonicalOf, retypeMap, storedRowsById, endpointWindows = /* @__PURE__ */ new Map()) {
419
916
  const remapped = [];
420
917
  const dropped = [];
918
+ const narrowingWarningById = /* @__PURE__ */ new Map();
421
919
  for (const assertion of assertions) {
422
920
  const remappedA = resolveIdentityEndpoint(
423
921
  assertion.a,
@@ -445,7 +943,47 @@ function remapIdentityAssertionEndpoints(assertions, canonicalOf, retypeMap, sto
445
943
  continue;
446
944
  }
447
945
  const [a, b] = normalizeIdentityPair(remappedA, remappedB);
448
- const result = { ...assertion, a, b };
946
+ const endpointWindowA = endpointWindows.get(mergeKeyOf(a));
947
+ const endpointWindowB = endpointWindows.get(mergeKeyOf(b));
948
+ const validFrom = [
949
+ assertion.validFrom,
950
+ endpointWindowA?.validFrom,
951
+ endpointWindowB?.validFrom
952
+ ].filter((bound) => bound !== void 0).reduce(
953
+ (latest, bound) => compareCodePoints(latest, bound) < 0 ? bound : latest
954
+ );
955
+ const validTo = [
956
+ assertion.validTo,
957
+ endpointWindowA?.validTo,
958
+ endpointWindowB?.validTo
959
+ ].filter((bound) => bound !== void 0).reduce(
960
+ (earliest, bound) => earliest === void 0 || compareCodePoints(bound, earliest) < 0 ? bound : earliest,
961
+ void 0
962
+ );
963
+ if (validTo !== void 0 && compareCodePoints(validFrom, validTo) >= 0) {
964
+ dropped.push(
965
+ droppedIdentityAssertion(
966
+ assertion,
967
+ EMPTY_REMAPPED_IDENTITY_WINDOW_DROP_REASON
968
+ )
969
+ );
970
+ continue;
971
+ }
972
+ if (validFrom !== assertion.validFrom || validTo !== assertion.validTo) {
973
+ const originalUpper = assertion.validTo ?? "open";
974
+ const remappedUpper = validTo ?? "open";
975
+ narrowingWarningById.set(
976
+ assertion.id,
977
+ `Identity assertion ${JSON.stringify(assertion.id)} was narrowed from [${assertion.validFrom}, ${originalUpper}) to [${validFrom}, ${remappedUpper}) to fit its remapped endpoint windows.`
978
+ );
979
+ }
980
+ const result = {
981
+ ...assertion,
982
+ a,
983
+ b,
984
+ validFrom,
985
+ ...validTo === void 0 ? {} : { validTo }
986
+ };
449
987
  const stored = storedRowsById.get(assertion.id);
450
988
  if (stored !== void 0 && assertionTruthKey(stored) === assertionTruthKey(assertion) && assertionTruthKey(result) !== assertionTruthKey(assertion)) {
451
989
  throw new IdentityMergeConflictError(
@@ -468,7 +1006,11 @@ function remapIdentityAssertionEndpoints(assertions, canonicalOf, retypeMap, sto
468
1006
  assertNoOpposingIdentityRelations(deduped.survivors);
469
1007
  return {
470
1008
  assertions: deduped.survivors,
471
- dropped: [...dropped, ...deduped.dropped]
1009
+ dropped: [...dropped, ...deduped.dropped],
1010
+ warnings: deduped.survivors.flatMap((assertion) => {
1011
+ const warning = narrowingWarningById.get(assertion.id);
1012
+ return warning === void 0 ? [] : [warning];
1013
+ })
472
1014
  };
473
1015
  }
474
1016
  function assertIdentityEndpointsNotDeleted(assertions, nodeDeletions, retypeMap) {
@@ -505,89 +1047,11 @@ function assertNoContradictoryIdentityClosure(plannedAssertions, retractions, ba
505
1047
  b: resolveIdentityEndpoint(assertion.b, canonicalOf, retypeMap)
506
1048
  }))
507
1049
  ];
508
- const sameClasses = new UnionFind(
509
- (left, right) => compareMergeKeys(left, right)
510
- );
511
- for (const node of nodeUniverse) {
512
- sameClasses.add(mergeKey(node.kind, node.id));
513
- }
514
- for (const assertion of mergedLedger) {
515
- if (assertion.relation === "same") {
516
- sameClasses.union(mergeKeyOf(assertion.a), mergeKeyOf(assertion.b));
517
- }
518
- }
519
- const membersOfClass = (root) => sameClasses.members().filter((member) => sameClasses.find(member) === root).map((member) => ({ kind: kindOf(member), id: idOf(member) })).toSorted(
520
- (left, right) => compareMergeKeys(mergeKeyOf(left), mergeKeyOf(right))
1050
+ assertTemporalIdentityClosureConsistent(
1051
+ mergedLedger,
1052
+ identityContext,
1053
+ nodeUniverse
521
1054
  );
522
- const firstDisjointPair = (kinds) => {
523
- for (const [index, left] of kinds.entries()) {
524
- for (const right of kinds.slice(index + 1)) {
525
- if (identityContext.areDisjoint(left, right)) return [left, right];
526
- }
527
- }
528
- return void 0;
529
- };
530
- const refsById = /* @__PURE__ */ new Map();
531
- const addRef = (kind, id) => {
532
- const keys = refsById.get(id) ?? [];
533
- keys.push(mergeKey(kind, id));
534
- refsById.set(id, keys);
535
- };
536
- for (const node of nodeUniverse) addRef(node.kind, node.id);
537
- for (const assertion of mergedLedger) {
538
- addRef(assertion.a.kind, assertion.a.id);
539
- addRef(assertion.b.kind, assertion.b.id);
540
- }
541
- for (const keys of refsById.values()) {
542
- const disjointKinds = firstDisjointPair([
543
- ...new Set(keys.map((key) => kindOf(key)))
544
- ]);
545
- if (disjointKinds === void 0) continue;
546
- throw new IdentityMergeConflictError(
547
- "The merged graph would give one id to two ontology-disjoint kinds.",
548
- {
549
- details: {
550
- disjointKinds,
551
- sharedId: idOf(requireDefined(keys[0]))
552
- }
553
- }
554
- );
555
- }
556
- if (identityContext.sameIdAcrossKinds === "fold") {
557
- for (const keys of refsById.values()) {
558
- const [first, ...rest] = keys;
559
- if (first === void 0) continue;
560
- for (const other of rest) sameClasses.union(first, other);
561
- }
562
- }
563
- for (const assertion of mergedLedger) {
564
- if (assertion.relation !== "different") {
565
- continue;
566
- }
567
- const root = sameClasses.find(mergeKeyOf(assertion.a));
568
- if (root !== sameClasses.find(mergeKeyOf(assertion.b))) {
569
- continue;
570
- }
571
- throw new IdentityMergeConflictError(
572
- "The merged identity ledger would assert one pair of nodes is both the same and different.",
573
- { details: { assertion, sameClass: membersOfClass(root) } }
574
- );
575
- }
576
- const kindsByRoot = /* @__PURE__ */ new Map();
577
- for (const member of sameClasses.members()) {
578
- const root = sameClasses.find(member);
579
- const kinds = kindsByRoot.get(root) ?? /* @__PURE__ */ new Set();
580
- kinds.add(kindOf(member));
581
- kindsByRoot.set(root, kinds);
582
- }
583
- for (const [root, kinds] of kindsByRoot) {
584
- const disjointKinds = firstDisjointPair([...kinds]);
585
- if (disjointKinds === void 0) continue;
586
- throw new IdentityMergeConflictError(
587
- "The merged identity ledger would join two ontology-disjoint kinds into one class.",
588
- { details: { disjointKinds, sameClass: membersOfClass(root) } }
589
- );
590
- }
591
1055
  }
592
1056
  async function buildIdentityPeerProbe(target, probedIds, targetPeers, plan, profile) {
593
1057
  const finalIds = /* @__PURE__ */ new Set([
@@ -608,9 +1072,9 @@ async function buildIdentityPeerProbe(target, probedIds, targetPeers, plan, prof
608
1072
  seedsByKey.set(mergeKey(kind, id), { kind, id });
609
1073
  };
610
1074
  for (const entity of plan.canonicalEntities) {
611
- const sourceKey = mergeKey(entity.kind, entity.canonicalId);
612
- if (plan.nodeDeletions.has(sourceKey)) continue;
613
- addSeed(plan.retypeMap.get(sourceKey) ?? entity.kind, entity.canonicalId);
1075
+ const sourceKey2 = mergeKey(entity.kind, entity.canonicalId);
1076
+ if (plan.nodeDeletions.has(sourceKey2)) continue;
1077
+ addSeed(plan.retypeMap.get(sourceKey2) ?? entity.kind, entity.canonicalId);
614
1078
  }
615
1079
  for (const assertion of plan.identityAssertions) {
616
1080
  addSeed(assertion.a.kind, assertion.a.id);
@@ -789,9 +1253,9 @@ function affectedIdentityClassSeeds(plan, profile) {
789
1253
  }
790
1254
  if (profile === "fold") {
791
1255
  for (const entity of plan.canonicalEntities) {
792
- const sourceKey = mergeKey(entity.kind, entity.canonicalId);
793
- if (plan.nodeDeletions.has(sourceKey)) continue;
794
- addSeed(plan.retypeMap.get(sourceKey) ?? entity.kind, entity.canonicalId);
1256
+ const sourceKey2 = mergeKey(entity.kind, entity.canonicalId);
1257
+ if (plan.nodeDeletions.has(sourceKey2)) continue;
1258
+ addSeed(plan.retypeMap.get(sourceKey2) ?? entity.kind, entity.canonicalId);
795
1259
  }
796
1260
  }
797
1261
  return [...seedsByKey.values()];
@@ -1126,30 +1590,22 @@ function classifyRetraction(assertion, deletedByKey, forkAssertion) {
1126
1590
  }
1127
1591
  return EXPLICIT_RETRACTION;
1128
1592
  }
1129
- async function classifyRetractions(forkStore, retracted, deletedNodes) {
1130
- if (retracted.length === 0 || deletedNodes.length === 0) {
1131
- return retracted.map((assertion) => ({
1132
- assertion,
1133
- cause: EXPLICIT_RETRACTION
1134
- }));
1135
- }
1593
+ function classifyRetractions(retracted, deletedNodes, forkRowsById) {
1594
+ if (retracted.length === 0) return [];
1136
1595
  const deletedByKey = new Map(
1137
1596
  deletedNodes.map((deletion) => [
1138
1597
  mergeKey(deletion.kind, deletion.id),
1139
1598
  deletion
1140
1599
  ])
1141
1600
  );
1142
- const forkRowsById = await storeRuntime(forkStore).identityAssertionRowsByIds(
1143
- retracted.map((assertion) => assertion.id)
1144
- );
1145
- return retracted.map((assertion) => ({
1146
- assertion,
1147
- cause: classifyRetraction(
1148
- assertion,
1149
- deletedByKey,
1150
- forkRowsById.get(assertion.id)
1151
- )
1152
- }));
1601
+ return retracted.map((assertion) => {
1602
+ const forkAssertion = forkRowsById.get(assertion.id);
1603
+ const endedAssertion = forkAssertion?.validTo !== void 0 && assertionIdentityKey(forkAssertion) === assertionIdentityKey(assertion) ? forkAssertion : assertion;
1604
+ return {
1605
+ assertion: endedAssertion,
1606
+ cause: classifyRetraction(assertion, deletedByKey, forkAssertion)
1607
+ };
1608
+ });
1153
1609
  }
1154
1610
  function byId(left, right) {
1155
1611
  return compareStrings(left.id, right.id);
@@ -1159,8 +1615,8 @@ async function diffAgainstBase(baseStore, forkStore, captureForkState = true) {
1159
1615
  const nodeKinds = getNodeKinds(graph);
1160
1616
  const edgeKinds = getEdgeKinds(graph);
1161
1617
  const [baseIdentity, forkIdentity] = await Promise.all([
1162
- storeRuntime(baseStore).readCurrentIdentityAssertions("state"),
1163
- storeRuntime(forkStore).readCurrentIdentityAssertions("state")
1618
+ storeRuntime(baseStore).readCurrentIdentityAssertions("archival"),
1619
+ storeRuntime(forkStore).readCurrentIdentityAssertions("archival")
1164
1620
  ]);
1165
1621
  const baseIdentityById = new Map(
1166
1622
  baseIdentity.map((assertion) => [assertion.id, assertion])
@@ -1252,10 +1708,10 @@ async function diffAgainstBase(baseStore, forkStore, captureForkState = true) {
1252
1708
  const fork = forkIdentityById.get(assertion.id);
1253
1709
  return fork === void 0 || assertionTruthKey(fork) !== assertionTruthKey(assertion);
1254
1710
  }).toSorted((left, right) => compareStrings(left.id, right.id));
1255
- const classifiedRetractions = await classifyRetractions(
1256
- forkStore,
1711
+ const classifiedRetractions = classifyRetractions(
1257
1712
  retractedAssertions,
1258
- deletedNodes
1713
+ deletedNodes,
1714
+ forkIdentityById
1259
1715
  );
1260
1716
  return {
1261
1717
  nodes: {
@@ -1281,7 +1737,7 @@ async function diffAgainstBase(baseStore, forkStore, captureForkState = true) {
1281
1737
  // unsupported id reuse as a typed conflict.
1282
1738
  new: forkIdentity.filter((assertion) => {
1283
1739
  const base = baseIdentityById.get(assertion.id);
1284
- return base === void 0 || assertionTruthKey(base) !== assertionTruthKey(assertion);
1740
+ return base === void 0 || assertionIdentityKey(base) !== assertionIdentityKey(assertion) && assertionTruthKey(base) !== assertionTruthKey(assertion);
1285
1741
  }).toSorted((left, right) => compareStrings(left.id, right.id)),
1286
1742
  retracted: classifiedRetractions
1287
1743
  },
@@ -1429,12 +1885,24 @@ function contentComponentOf(version) {
1429
1885
  // src/graph-merge/working-copy.ts
1430
1886
  var CLONE_IMPORT_BATCH_SIZE = 1e3;
1431
1887
  function cloneWorkingCopyStrategy(makeBackend) {
1888
+ return cloneWorkingCopyWithGraphStrategy(
1889
+ makeBackend,
1890
+ (baseStore) => baseStore.graph
1891
+ );
1892
+ }
1893
+ function cloneIngestionWorkingCopyStrategy(makeBackend) {
1894
+ return cloneWorkingCopyWithGraphStrategy(
1895
+ makeBackend,
1896
+ (baseStore) => graphWithoutNodeUniqueness(baseStore.graph)
1897
+ );
1898
+ }
1899
+ function cloneWorkingCopyWithGraphStrategy(makeBackend, graphForClone) {
1432
1900
  return {
1433
1901
  create: async (baseStore) => {
1434
1902
  const backend = await makeBackend();
1435
1903
  try {
1436
1904
  const [freshStore] = await createStoreWithSchema(
1437
- baseStore.graph,
1905
+ graphForClone(baseStore),
1438
1906
  backend,
1439
1907
  {
1440
1908
  // Keep descendants branchable with the same O(1) anchor contract,
@@ -1483,6 +1951,30 @@ function cloneWorkingCopyStrategy(makeBackend) {
1483
1951
  }
1484
1952
  };
1485
1953
  }
1954
+ function graphWithoutNodeUniqueness(graph) {
1955
+ const nodes = Object.fromEntries(
1956
+ Object.entries(graph.nodes).map(([name, registration]) => {
1957
+ const { unique: _omitted, ...withoutUnique } = registration;
1958
+ return [name, Object.freeze(withoutUnique)];
1959
+ })
1960
+ );
1961
+ const extension = graph.extension?.nodes === void 0 ? graph.extension : Object.freeze({
1962
+ ...graph.extension,
1963
+ nodes: Object.freeze(
1964
+ Object.fromEntries(
1965
+ Object.entries(graph.extension.nodes).map(([name, node]) => {
1966
+ const { unique: _omitted, ...withoutUnique } = node;
1967
+ return [name, Object.freeze(withoutUnique)];
1968
+ })
1969
+ )
1970
+ )
1971
+ });
1972
+ return Object.freeze({
1973
+ ...graph,
1974
+ nodes: Object.freeze(nodes),
1975
+ extension
1976
+ });
1977
+ }
1486
1978
 
1487
1979
  // src/graph-merge/branch.ts
1488
1980
  async function branch(baseStore, makeBackend, options, strategy) {
@@ -1491,7 +1983,7 @@ async function branch(baseStore, makeBackend, options, strategy) {
1491
1983
  const id = options?.id ?? asBranchId(generateId());
1492
1984
  const workingCopyStrategy = strategy ?? cloneWorkingCopyStrategy(makeBackend);
1493
1985
  const store = await workingCopyStrategy.create(baseStore);
1494
- const schemaAnchor = await readSchemaAnchor(store);
1986
+ const schemaAnchor = await captureBranchSchemaAnchor(store);
1495
1987
  return ok({
1496
1988
  id,
1497
1989
  base,
@@ -1506,7 +1998,7 @@ async function branch(baseStore, makeBackend, options, strategy) {
1506
1998
  );
1507
1999
  }
1508
2000
  }
1509
- async function readSchemaAnchor(store) {
2001
+ async function captureBranchSchemaAnchor(store) {
1510
2002
  try {
1511
2003
  const schemaRow = await storeBackend(store).getActiveSchema(store.graphId);
1512
2004
  return schemaRow === void 0 ? void 0 : { version: schemaRow.version, hash: schemaRow.schema_hash };
@@ -1519,6 +2011,53 @@ async function readSchemaAnchor(store) {
1519
2011
  }
1520
2012
  }
1521
2013
 
2014
+ // src/graph-merge/ingestion-branch.ts
2015
+ var PRIVATE_BRANCHES = /* @__PURE__ */ new WeakMap();
2016
+ async function ingestionBranch(baseStore, makeBackend, options) {
2017
+ try {
2018
+ const created = await branch(
2019
+ baseStore,
2020
+ makeBackend,
2021
+ options,
2022
+ cloneIngestionWorkingCopyStrategy(makeBackend)
2023
+ );
2024
+ if (isErr(created)) {
2025
+ return err(
2026
+ new BranchError("Failed to create constraint-aware ingestion branch", {
2027
+ cause: created.error
2028
+ })
2029
+ );
2030
+ }
2031
+ const privateBranch = created.data;
2032
+ const { base, id, store } = privateBranch;
2033
+ const handle = Object.freeze({
2034
+ id,
2035
+ base,
2036
+ nodes: store.nodes,
2037
+ edges: store.edges,
2038
+ close: async () => storeBackend(store).close()
2039
+ });
2040
+ PRIVATE_BRANCHES.set(handle, privateBranch);
2041
+ return ok(handle);
2042
+ } catch (error) {
2043
+ return err(
2044
+ new BranchError("Failed to create constraint-aware ingestion branch", {
2045
+ cause: error
2046
+ })
2047
+ );
2048
+ }
2049
+ }
2050
+ function unwrapMergeBranch(input) {
2051
+ const privateBranch = PRIVATE_BRANCHES.get(input);
2052
+ if (privateBranch !== void 0) {
2053
+ return privateBranch;
2054
+ }
2055
+ return input;
2056
+ }
2057
+ function unwrapMergeBranches(inputs) {
2058
+ return inputs.map((input) => unwrapMergeBranch(input));
2059
+ }
2060
+
1522
2061
  // src/graph-merge/blocking.ts
1523
2062
  var UNBLOCKED_BUCKET_KEY = "unblocked";
1524
2063
  var KEY_PART_SEPARATOR = "\0";
@@ -1527,6 +2066,14 @@ var UNIQUE_KEY_PREFIX = "u";
1527
2066
  function isUniqueBucketKey(key) {
1528
2067
  return key.startsWith(`${UNIQUE_KEY_PREFIX}${KEY_PART_SEPARATOR}`);
1529
2068
  }
2069
+ function uniqueConstraintNameForBucket(key) {
2070
+ if (!isUniqueBucketKey(key)) {
2071
+ return void 0;
2072
+ }
2073
+ const constraintStart = UNIQUE_KEY_PREFIX.length + KEY_PART_SEPARATOR.length;
2074
+ const constraintEnd = key.indexOf(KEY_PART_SEPARATOR, constraintStart);
2075
+ return constraintEnd === -1 ? void 0 : key.slice(constraintStart, constraintEnd);
2076
+ }
1530
2077
  function constraintSignature(node, constraint) {
1531
2078
  const props = node;
1532
2079
  for (const field of constraint.fields) {
@@ -1874,7 +2421,10 @@ function canonicalizeCluster(cluster, members, policy, branchRank, weights, cano
1874
2421
  canonicalId,
1875
2422
  memberIds,
1876
2423
  kind,
1877
- branchOrigins
2424
+ branchOrigins,
2425
+ // The merge orchestrator replaces this with the witness selected from the
2426
+ // post-guard surviving edge set. Canonicalization never re-derives evidence.
2427
+ decisiveEdges: []
1878
2428
  };
1879
2429
  const windowSource = members.find(
1880
2430
  (member) => member.branchId === COMMITTED_TARGET_BRANCH && member.kind === kind && member.id === canonicalId
@@ -1886,10 +2436,59 @@ function canonicalizeCluster(cluster, members, policy, branchRank, weights, cano
1886
2436
  resolution,
1887
2437
  conflicts,
1888
2438
  ...windowSource.validFrom === void 0 ? {} : { validFrom: windowSource.validFrom },
1889
- ...windowSource.validTo === void 0 ? {} : { validTo: windowSource.validTo }
2439
+ ...windowSource.validTo === void 0 ? {} : { validTo: windowSource.validTo },
2440
+ ...windowSource.validFrom === void 0 ? {} : { endpointValidFrom: windowSource.validFrom },
2441
+ ...windowSource.validTo === void 0 ? {} : { endpointValidTo: windowSource.validTo }
1890
2442
  };
1891
2443
  }
1892
2444
 
2445
+ // src/graph-merge/union-find.ts
2446
+ var UnionFind = class {
2447
+ constructor(compare) {
2448
+ this.compare = compare;
2449
+ }
2450
+ compare;
2451
+ parent = /* @__PURE__ */ new Map();
2452
+ /** Adds `value` as its own singleton set if it is not already present. */
2453
+ add(value) {
2454
+ if (!this.parent.has(value)) {
2455
+ this.parent.set(value, value);
2456
+ }
2457
+ }
2458
+ /** Returns the representative of `value`'s set, compressing the path to it. */
2459
+ find(value) {
2460
+ this.add(value);
2461
+ let root = value;
2462
+ while (this.parent.get(root) !== root) {
2463
+ root = requireDefined(this.parent.get(root));
2464
+ }
2465
+ let cursor = value;
2466
+ while (cursor !== root) {
2467
+ const next = requireDefined(this.parent.get(cursor));
2468
+ this.parent.set(cursor, root);
2469
+ cursor = next;
2470
+ }
2471
+ return root;
2472
+ }
2473
+ /** Merges the sets of `left` and `right`; the `compare`-minimal root wins. */
2474
+ union(left, right) {
2475
+ const leftRoot = this.find(left);
2476
+ const rightRoot = this.find(right);
2477
+ if (leftRoot === rightRoot) {
2478
+ return;
2479
+ }
2480
+ if (this.compare(leftRoot, rightRoot) <= 0) {
2481
+ this.parent.set(rightRoot, leftRoot);
2482
+ } else {
2483
+ this.parent.set(leftRoot, rightRoot);
2484
+ }
2485
+ }
2486
+ /** Every value seen by {@link add}/{@link find}/{@link union}, insertion order. */
2487
+ members() {
2488
+ return [...this.parent.keys()];
2489
+ }
2490
+ };
2491
+
1893
2492
  // src/graph-merge/closures.ts
1894
2493
  var SUB_CLASS_OF_META_EDGE = "subClassOf";
1895
2494
  var EQUIVALENCE_META_EDGES = /* @__PURE__ */ new Set([
@@ -1944,6 +2543,101 @@ function isReachable2(closure, from, to) {
1944
2543
  return isReachable(closure.closure, fromRep, toRep);
1945
2544
  }
1946
2545
 
2546
+ // src/graph-merge/evidence.ts
2547
+ function entityRef(key) {
2548
+ return { kind: kindOf(key), id: idOf(key) };
2549
+ }
2550
+ var SOURCE_KIND_ORDER = {
2551
+ block: 0,
2552
+ unique: 1,
2553
+ baseUnique: 2,
2554
+ baseIndex: 3,
2555
+ keyless: 4,
2556
+ retype: 5,
2557
+ custom: 6
2558
+ };
2559
+ function sourceKey(source) {
2560
+ switch (source.kind) {
2561
+ case "block":
2562
+ case "keyless":
2563
+ case "retype": {
2564
+ return JSON.stringify([SOURCE_KIND_ORDER[source.kind], source.sourceId]);
2565
+ }
2566
+ case "unique":
2567
+ case "baseUnique": {
2568
+ return JSON.stringify([
2569
+ SOURCE_KIND_ORDER[source.kind],
2570
+ source.sourceId,
2571
+ source.constraintName
2572
+ ]);
2573
+ }
2574
+ case "baseIndex": {
2575
+ return JSON.stringify([
2576
+ SOURCE_KIND_ORDER[source.kind],
2577
+ source.sourceId,
2578
+ source.indexName
2579
+ ]);
2580
+ }
2581
+ case "custom": {
2582
+ return JSON.stringify([
2583
+ SOURCE_KIND_ORDER.custom,
2584
+ source.sourceId,
2585
+ source.metadata === void 0 ? "" : canonicalValueKey(source.metadata)
2586
+ ]);
2587
+ }
2588
+ default: {
2589
+ const exhaustive = source;
2590
+ return exhaustive;
2591
+ }
2592
+ }
2593
+ }
2594
+ function compareMatchSources(left, right) {
2595
+ return compareStrings(sourceKey(left), sourceKey(right));
2596
+ }
2597
+ function compareEntityRefs(left, right) {
2598
+ return compareMergeKeys(
2599
+ mergeKey(left.kind, left.id),
2600
+ mergeKey(right.kind, right.id)
2601
+ );
2602
+ }
2603
+ function normalizeMatchSources(sources) {
2604
+ const byKey = /* @__PURE__ */ new Map();
2605
+ for (const source of sources) {
2606
+ byKey.set(sourceKey(source), source);
2607
+ }
2608
+ return [...byKey].sort(([, left], [, right]) => compareMatchSources(left, right)).map(([, source]) => source);
2609
+ }
2610
+ var DEFAULT_HYBRID_WEIGHT = 0.5;
2611
+ function describeMatchStrategy(strategy) {
2612
+ switch (strategy.kind) {
2613
+ case "fulltext": {
2614
+ return { kind: "fulltext", fields: [...strategy.fields] };
2615
+ }
2616
+ case "vector": {
2617
+ return { kind: "vector", fields: [strategy.field] };
2618
+ }
2619
+ case "hybrid": {
2620
+ const vector = strategy.weights?.vector ?? DEFAULT_HYBRID_WEIGHT;
2621
+ const fulltext = strategy.weights?.fulltext ?? DEFAULT_HYBRID_WEIGHT;
2622
+ const total = vector + fulltext;
2623
+ const weights = total === 0 ? { vector: DEFAULT_HYBRID_WEIGHT, fulltext: DEFAULT_HYBRID_WEIGHT } : { vector: vector / total, fulltext: fulltext / total };
2624
+ return { kind: "hybrid", fields: [...strategy.fields], weights };
2625
+ }
2626
+ case "custom": {
2627
+ return { kind: "custom" };
2628
+ }
2629
+ default: {
2630
+ const exhaustive = strategy;
2631
+ return exhaustive;
2632
+ }
2633
+ }
2634
+ }
2635
+ function compareMatchEvidence(left, right) {
2636
+ const byA = compareEntityRefs(left.a, right.a);
2637
+ if (byA !== 0) return byA;
2638
+ return compareEntityRefs(left.b, right.b);
2639
+ }
2640
+
1947
2641
  // src/graph-merge/similarity.ts
1948
2642
  var MIN_SCORE = 0;
1949
2643
  var MAX_SCORE = 1;
@@ -2073,18 +2767,18 @@ function cosineFromLookup(embeddings, leftText, rightText) {
2073
2767
  }
2074
2768
  return cosineSimilarity(leftVector, rightVector);
2075
2769
  }
2076
- var DEFAULT_HYBRID_WEIGHT = 0.5;
2770
+ var DEFAULT_HYBRID_WEIGHT2 = 0.5;
2077
2771
  function nonNegativeFinite(value) {
2078
2772
  return Number.isFinite(value) ? Math.max(0, value) : 0;
2079
2773
  }
2080
2774
  function normalizeHybridWeights(weights) {
2081
- const vector = nonNegativeFinite(weights?.vector ?? DEFAULT_HYBRID_WEIGHT);
2775
+ const vector = nonNegativeFinite(weights?.vector ?? DEFAULT_HYBRID_WEIGHT2);
2082
2776
  const fulltext = nonNegativeFinite(
2083
- weights?.fulltext ?? DEFAULT_HYBRID_WEIGHT
2777
+ weights?.fulltext ?? DEFAULT_HYBRID_WEIGHT2
2084
2778
  );
2085
2779
  const total = vector + fulltext;
2086
2780
  if (total === 0) {
2087
- return { vector: DEFAULT_HYBRID_WEIGHT, fulltext: DEFAULT_HYBRID_WEIGHT };
2781
+ return { vector: DEFAULT_HYBRID_WEIGHT2, fulltext: DEFAULT_HYBRID_WEIGHT2 };
2088
2782
  }
2089
2783
  return { vector: vector / total, fulltext: fulltext / total };
2090
2784
  }
@@ -2123,7 +2817,36 @@ function prepareNode(node, strategy) {
2123
2817
  function scorePrepared(left, right, strategy, ctx) {
2124
2818
  switch (strategy.kind) {
2125
2819
  case "custom": {
2126
- const raw = strategy.score(left.node, right.node);
2820
+ let raw;
2821
+ try {
2822
+ raw = strategy.score(left.node, right.node);
2823
+ } catch (error) {
2824
+ return err(
2825
+ new MatchEvidenceError("Custom similarity scorer failed.", {
2826
+ details: {
2827
+ kind: left.node.kind,
2828
+ sourceId: "similarity:custom",
2829
+ operation: "score"
2830
+ },
2831
+ cause: error
2832
+ })
2833
+ );
2834
+ }
2835
+ if (!Number.isFinite(raw)) {
2836
+ return err(
2837
+ new MatchEvidenceError(
2838
+ "Custom similarity scorer returned a non-finite score.",
2839
+ {
2840
+ details: {
2841
+ kind: left.node.kind,
2842
+ sourceId: "similarity:custom",
2843
+ operation: "score",
2844
+ score: String(raw)
2845
+ }
2846
+ }
2847
+ )
2848
+ );
2849
+ }
2127
2850
  return ok(clampScore(raw));
2128
2851
  }
2129
2852
  case "fulltext": {
@@ -2191,25 +2914,51 @@ function compareCandidateEdges(left, right) {
2191
2914
  function endpointKey(a, b) {
2192
2915
  return JSON.stringify([a, b]);
2193
2916
  }
2194
- function scoreCandidates(input, resolveConfig, ctx, ceilingPolicy, maxComparisonsPerKind) {
2917
+ function withSources(edge, sources) {
2918
+ return {
2919
+ ...edge,
2920
+ evidence: {
2921
+ ...edge.evidence,
2922
+ sources: normalizeMatchSources([...edge.evidence.sources, ...sources])
2923
+ }
2924
+ };
2925
+ }
2926
+ function scoreCandidates(input, resolveConfig, ctx, ceilingPolicy, maxComparisonsPerKind, diagnosticLimit = 0) {
2195
2927
  const { similarity, threshold } = resolveConfig;
2196
- const seen = /* @__PURE__ */ new Set();
2197
- const forced = [];
2928
+ const forcedByKey = /* @__PURE__ */ new Map();
2198
2929
  for (const edge of input.forcedEdges) {
2199
2930
  const key = endpointKey(edge.a, edge.b);
2200
- if (!seen.has(key)) {
2201
- seen.add(key);
2202
- forced.push(edge);
2203
- }
2931
+ const previous = forcedByKey.get(key);
2932
+ forcedByKey.set(
2933
+ key,
2934
+ previous === void 0 ? edge : withSources(previous, edge.evidence.sources)
2935
+ );
2204
2936
  }
2205
- const fuzzy = [];
2937
+ const fuzzyByKey = /* @__PURE__ */ new Map();
2206
2938
  for (const pair of input.pairs) {
2207
2939
  const key = endpointKey(pair.a, pair.b);
2208
- if (!seen.has(key)) {
2209
- seen.add(key);
2210
- fuzzy.push(pair);
2940
+ const forced2 = forcedByKey.get(key);
2941
+ if (forced2 !== void 0) {
2942
+ forcedByKey.set(key, withSources(forced2, pair.sources));
2943
+ continue;
2211
2944
  }
2945
+ const previous = fuzzyByKey.get(key);
2946
+ fuzzyByKey.set(
2947
+ key,
2948
+ previous === void 0 ? pair : {
2949
+ ...previous,
2950
+ sources: normalizeMatchSources([
2951
+ ...previous.sources,
2952
+ ...pair.sources
2953
+ ])
2954
+ }
2955
+ );
2212
2956
  }
2957
+ const forced = [...forcedByKey.values()];
2958
+ const fuzzy = [...fuzzyByKey.values()].sort((left, right) => {
2959
+ const byA = compareMergeKeys(left.a, right.a);
2960
+ return byA === 0 ? compareMergeKeys(left.b, right.b) : byA;
2961
+ });
2213
2962
  if (maxComparisonsPerKind !== void 0 && fuzzy.length > maxComparisonsPerKind) {
2214
2963
  if (ceilingPolicy === "error") {
2215
2964
  return err(
@@ -2236,23 +2985,81 @@ function scoreCandidates(input, resolveConfig, ctx, ceilingPolicy, maxComparison
2236
2985
  limit: maxComparisonsPerKind,
2237
2986
  message: `Skipped similarity for this kind: ${fuzzy.length} candidate pairs exceeded maxComparisonsPerKind ${maxComparisonsPerKind}; nodes will merge by id and exact unique match only.`
2238
2987
  }
2239
- ]
2988
+ ],
2989
+ diagnostics: [],
2990
+ diagnosticsTotal: 0
2240
2991
  });
2241
2992
  }
2242
2993
  const scorer = createPairScorer(similarity, ctx);
2243
2994
  const edges = [...forced];
2244
- for (const { left, right, a, b } of fuzzy) {
2995
+ const diagnostics = [];
2996
+ let diagnosticsTotal = 0;
2997
+ for (const { left, right, a, b, sources } of fuzzy) {
2245
2998
  const scored = scorer(a, left, b, right);
2246
2999
  if (isErr(scored)) {
3000
+ if (similarity.kind === "custom" && scored.error instanceof MatchEvidenceError) {
3001
+ return err(
3002
+ new MatchEvidenceError(scored.error.message, {
3003
+ cause: scored.error,
3004
+ details: {
3005
+ kind: left.kind,
3006
+ operation: "score",
3007
+ sourceIds: normalizeMatchSources(sources).map(
3008
+ (source) => source.sourceId
3009
+ ),
3010
+ endpoints: [entityRef(a), entityRef(b)]
3011
+ }
3012
+ })
3013
+ );
3014
+ }
2247
3015
  return err(scored.error);
2248
3016
  }
2249
- if (scored.data >= threshold) {
2250
- edges.push({ a, b, score: scored.data });
3017
+ if (!Number.isFinite(scored.data)) {
3018
+ return err(
3019
+ new MatchEvidenceError(
3020
+ "Similarity scorer returned a non-finite score.",
3021
+ {
3022
+ details: {
3023
+ kind: left.kind,
3024
+ operation: "score",
3025
+ sourceIds: normalizeMatchSources(sources).map(
3026
+ (source) => source.sourceId
3027
+ ),
3028
+ score: String(scored.data)
3029
+ }
3030
+ }
3031
+ )
3032
+ );
3033
+ }
3034
+ const noComparableValues = scored.data < 0;
3035
+ const publicScore = noComparableValues ? 0 : scored.data;
3036
+ const accepted = !noComparableValues && scored.data >= threshold;
3037
+ diagnosticsTotal += 1;
3038
+ const retainsDiagnostic = diagnostics.length < diagnosticLimit;
3039
+ if (accepted || retainsDiagnostic) {
3040
+ const evidence = {
3041
+ a: entityRef(a),
3042
+ b: entityRef(b),
3043
+ sources: normalizeMatchSources(sources),
3044
+ decision: "scored",
3045
+ strategy: describeMatchStrategy(similarity),
3046
+ score: publicScore,
3047
+ threshold
3048
+ };
3049
+ if (accepted) edges.push({ a, b, score: scored.data, evidence });
3050
+ if (!retainsDiagnostic) continue;
3051
+ diagnostics.push({
3052
+ evidence,
3053
+ scoreDecision: accepted ? "accepted" : "rejected",
3054
+ ...noComparableValues ? { reason: "noComparableValues" } : {}
3055
+ });
2251
3056
  }
2252
3057
  }
2253
3058
  return ok({
2254
3059
  edges: edges.sort((left, right) => compareCandidateEdges(left, right)),
2255
- warnings: []
3060
+ warnings: [],
3061
+ diagnostics,
3062
+ diagnosticsTotal
2256
3063
  });
2257
3064
  }
2258
3065
 
@@ -2354,13 +3161,14 @@ function weakestEdge(edges) {
2354
3161
  }
2355
3162
  function splitUntil(members, edges, isOffending) {
2356
3163
  let surviving = internalEdges(members, edges);
3164
+ const removedEdges = [];
2357
3165
  for (; ; ) {
2358
3166
  const components = buildComponents(surviving, members, true);
2359
3167
  const offending = components.filter(
2360
3168
  (component) => isOffending(component.members, surviving)
2361
3169
  );
2362
3170
  if (offending.length === 0) {
2363
- return components;
3171
+ return { clusters: components, survivingEdges: surviving, removedEdges };
2364
3172
  }
2365
3173
  let splittable;
2366
3174
  for (const component of offending) {
@@ -2372,11 +3180,16 @@ function splitUntil(members, edges, isOffending) {
2372
3180
  }
2373
3181
  if (splittable === void 0) {
2374
3182
  const offendingSet = new Set(offending);
2375
- return components.flatMap(
2376
- (component) => offendingSet.has(component) ? component.members.map((id) => ({ members: [id] })) : [component]
2377
- );
3183
+ return {
3184
+ clusters: components.flatMap(
3185
+ (component) => offendingSet.has(component) ? component.members.map((id) => ({ members: [id] })) : [component]
3186
+ ),
3187
+ survivingEdges: surviving,
3188
+ removedEdges
3189
+ };
2378
3190
  }
2379
3191
  const weakest = weakestEdge(splittable);
3192
+ removedEdges.push(weakest);
2380
3193
  surviving = surviving.filter((edge) => edge !== weakest);
2381
3194
  }
2382
3195
  }
@@ -2390,16 +3203,30 @@ function splitByDropWeakest(members, edges, maxDiameter) {
2390
3203
  function connectedComponents(candidateEdges, nodeIds) {
2391
3204
  return buildComponents(candidateEdges, nodeIds);
2392
3205
  }
2393
- function enforceDiameter(clusters, candidateEdges, clusterMaxDiameter) {
3206
+ function enforceDiameterWithEdges(clusters, candidateEdges, clusterMaxDiameter) {
2394
3207
  const guarded = [];
3208
+ let survivingEdges = [...candidateEdges];
3209
+ const excludedEdges = [];
2395
3210
  for (const component of clusters) {
2396
3211
  if (exceedsDiameter(component.members, candidateEdges, clusterMaxDiameter)) {
2397
- guarded.push(
2398
- ...splitByDropWeakest(
2399
- component.members,
2400
- candidateEdges,
2401
- clusterMaxDiameter
2402
- )
3212
+ const split = splitByDropWeakest(
3213
+ component.members,
3214
+ survivingEdges,
3215
+ clusterMaxDiameter
3216
+ );
3217
+ guarded.push(...split.clusters);
3218
+ const memberSet = new Set(component.members);
3219
+ survivingEdges = [
3220
+ ...survivingEdges.filter(
3221
+ (edge) => !(memberSet.has(edge.a) && memberSet.has(edge.b))
3222
+ ),
3223
+ ...split.survivingEdges
3224
+ ].sort((left, right) => compareCandidateEdges(left, right));
3225
+ excludedEdges.push(
3226
+ ...split.removedEdges.map((edge) => ({
3227
+ edge,
3228
+ reason: "diameter"
3229
+ }))
2403
3230
  );
2404
3231
  } else {
2405
3232
  guarded.push(component);
@@ -2411,7 +3238,7 @@ function enforceDiameter(clusters, candidateEdges, clusterMaxDiameter) {
2411
3238
  requireDefined(right.members[0])
2412
3239
  )
2413
3240
  );
2414
- return guarded;
3241
+ return { clusters: guarded, survivingEdges, excludedEdges };
2415
3242
  }
2416
3243
  function countBaseMembers(members, baseIds) {
2417
3244
  let count = 0;
@@ -2431,10 +3258,19 @@ function splitByBaseMultiplicity(members, edges, baseIds) {
2431
3258
  }
2432
3259
  function enforceBaseGuard(clusters, candidateEdges, baseIds) {
2433
3260
  if (baseIds.size === 0) {
2434
- return { clusters, events: [] };
3261
+ return {
3262
+ clusters,
3263
+ events: [],
3264
+ survivingEdges: [...candidateEdges].sort(
3265
+ (left, right) => compareCandidateEdges(left, right)
3266
+ ),
3267
+ excludedEdges: []
3268
+ };
2435
3269
  }
2436
3270
  const result = [];
2437
3271
  const events = [];
3272
+ let survivingEdges = [...candidateEdges];
3273
+ const excludedEdges = [];
2438
3274
  for (const cluster of clusters) {
2439
3275
  const clusterBaseIds = cluster.members.filter((id) => baseIds.has(id));
2440
3276
  if (clusterBaseIds.length < 2) {
@@ -2449,8 +3285,24 @@ function enforceBaseGuard(clusters, candidateEdges, baseIds) {
2449
3285
  (left, right) => compareMergeKeys(left, right)
2450
3286
  )
2451
3287
  });
2452
- result.push(
2453
- ...splitByBaseMultiplicity(cluster.members, candidateEdges, baseIds)
3288
+ const split = splitByBaseMultiplicity(
3289
+ cluster.members,
3290
+ survivingEdges,
3291
+ baseIds
3292
+ );
3293
+ result.push(...split.clusters);
3294
+ const memberSet = new Set(cluster.members);
3295
+ survivingEdges = [
3296
+ ...survivingEdges.filter(
3297
+ (edge) => !(memberSet.has(edge.a) && memberSet.has(edge.b))
3298
+ ),
3299
+ ...split.survivingEdges
3300
+ ].sort((left, right) => compareCandidateEdges(left, right));
3301
+ excludedEdges.push(
3302
+ ...split.removedEdges.map((edge) => ({
3303
+ edge,
3304
+ reason: "baseAmbiguity"
3305
+ }))
2454
3306
  );
2455
3307
  }
2456
3308
  result.sort(
@@ -2459,7 +3311,20 @@ function enforceBaseGuard(clusters, candidateEdges, baseIds) {
2459
3311
  requireDefined(right.members[0])
2460
3312
  )
2461
3313
  );
2462
- return { clusters: result, events };
3314
+ return { clusters: result, events, survivingEdges, excludedEdges };
3315
+ }
3316
+ function decisiveEdgesForCluster(cluster, survivingEdges) {
3317
+ const members = new Set(cluster.members);
3318
+ const forest = new UnionFind(compareMergeKeys);
3319
+ for (const member of cluster.members) forest.add(member);
3320
+ const decisive = [];
3321
+ for (const edge of internalEdges(cluster.members, survivingEdges)) {
3322
+ if (!members.has(edge.a) || !members.has(edge.b)) continue;
3323
+ if (forest.find(edge.a) === forest.find(edge.b)) continue;
3324
+ forest.union(edge.a, edge.b);
3325
+ decisive.push(edge.evidence);
3326
+ }
3327
+ return decisive;
2463
3328
  }
2464
3329
 
2465
3330
  // src/graph-merge/delete-modify.ts
@@ -2756,8 +3621,9 @@ var WINDOW_NOT_APPLICABLE_DROP_REASON = "window-not-applicable";
2756
3621
  function earliestEnd(claims) {
2757
3622
  let earliest;
2758
3623
  for (const claim of claims) {
2759
- if (earliest === void 0 || compareStrings(claim.validTo, earliest) < 0) {
2760
- earliest = claim.validTo;
3624
+ if (claim.change.kind !== "set") continue;
3625
+ if (earliest === void 0 || compareStrings(claim.change.validTo, earliest) < 0) {
3626
+ earliest = claim.change.validTo;
2761
3627
  }
2762
3628
  }
2763
3629
  return earliest;
@@ -2766,18 +3632,26 @@ function resolveEndClaims(claims, preferredBranchId) {
2766
3632
  const preferred = claims.filter(
2767
3633
  (claim) => claim.branchId === preferredBranchId
2768
3634
  );
2769
- return earliestEnd(preferred.length > 0 ? preferred : claims);
3635
+ const candidates = preferred.length > 0 ? preferred : claims;
3636
+ const earliest = earliestEnd(candidates);
3637
+ return earliest === void 0 ? candidates.some((claim) => claim.change.kind === "clear") ? { kind: "clear" } : void 0 : { kind: "set", validTo: earliest };
2770
3638
  }
2771
3639
  function claimingBranches(claims) {
2772
3640
  return [...new Set(claims.map((claim) => claim.branchId))].sort(
2773
3641
  (left, right) => compareStrings(left, right)
2774
3642
  );
2775
3643
  }
3644
+ function sameValidToChange(left, right) {
3645
+ if (left.kind !== right.kind) return false;
3646
+ if (left.kind === "clear") return true;
3647
+ return right.kind === "set" && left.validTo === right.validTo;
3648
+ }
2776
3649
  function classifyDelta(base, fork) {
3650
+ const lowerBoundMoved = fork.validFrom !== base.validFrom;
2777
3651
  const endCleared = fork.validTo === void 0 && base.validTo !== void 0;
2778
3652
  return {
2779
- applicableEnd: fork.validTo !== void 0 && fork.validTo !== base.validTo ? fork.validTo : void 0,
2780
- unapplicable: endCleared || fork.validFrom !== base.validFrom
3653
+ applicableEnd: fork.validTo !== void 0 && fork.validTo !== base.validTo ? { kind: "set", validTo: fork.validTo } : endCleared && !lowerBoundMoved ? { kind: "clear" } : void 0,
3654
+ unapplicable: lowerBoundMoved
2781
3655
  };
2782
3656
  }
2783
3657
  function resolvePopulation(entity, deltas, deletions, preferredBranchId, dropItem) {
@@ -2812,7 +3686,7 @@ function resolvePopulation(entity, deltas, deletions, preferredBranchId, dropIte
2812
3686
  continue;
2813
3687
  }
2814
3688
  if (applicableEnd !== void 0) {
2815
- claims.push({ branchId: delta.branchId, validTo: applicableEnd });
3689
+ claims.push({ branchId: delta.branchId, change: applicableEnd });
2816
3690
  }
2817
3691
  if (unapplicable && !reportedUnapplicable) {
2818
3692
  reportedUnapplicable = true;
@@ -2826,27 +3700,29 @@ function resolvePopulation(entity, deltas, deletions, preferredBranchId, dropIte
2826
3700
  entity,
2827
3701
  kind: first.kind,
2828
3702
  id: first.id,
2829
- validTo: targetEnd,
3703
+ ...targetEnd.kind === "set" ? { validTo: targetEnd.validTo } : { clearValidTo: true },
2830
3704
  claimedBy: claimingBranches(claims),
2831
3705
  precedence: VALIDITY_END_TARGET_PRECEDENCE
2832
3706
  });
2833
3707
  }
2834
3708
  continue;
2835
3709
  }
2836
- const resolved = earliestEnd(claims);
3710
+ const resolved = resolveEndClaims(claims, void 0);
2837
3711
  if (resolved === void 0) {
2838
3712
  continue;
2839
3713
  }
2840
3714
  ends.set(identity, resolved);
2841
3715
  credits.set(
2842
3716
  identity,
2843
- claimingBranches(claims.filter((claim) => claim.validTo === resolved))
3717
+ claimingBranches(
3718
+ claims.filter((claim) => sameValidToChange(claim.change, resolved))
3719
+ )
2844
3720
  );
2845
3721
  resolutions.push({
2846
3722
  entity,
2847
3723
  kind: first.kind,
2848
3724
  id: first.id,
2849
- validTo: resolved,
3725
+ ...resolved.kind === "set" ? { validTo: resolved.validTo } : { clearValidTo: true },
2850
3726
  claimedBy: claimingBranches(claims)
2851
3727
  });
2852
3728
  }
@@ -3070,17 +3946,19 @@ function foldEdgeSet(foldSet, context, branchRank, preferredBranchId) {
3070
3946
  const fromKind = kindOf(survivor.fromKey);
3071
3947
  const toId = idOf(survivor.toKey);
3072
3948
  const toKind = kindOf(survivor.toKey);
3073
- const preferredSurvivorEnd = survivor.staged.branchId === preferredBranchId ? survivor.staged.validTo : void 0;
3949
+ const preferredSurvivorEnd = survivor.staged.branchId === preferredBranchId ? typeof survivor.staged.validTo === "string" ? { kind: "set", validTo: survivor.staged.validTo } : survivor.staged.clearValidTo === true ? { kind: "clear" } : void 0 : void 0;
3074
3950
  const foldedEnd = preferredSurvivorEnd ?? resolveEndClaims(
3075
- foldSet.filter((edge) => edge.staged.validTo !== void 0).map((edge) => ({
3951
+ foldSet.filter(
3952
+ (edge) => edge.staged.validTo !== void 0 || edge.staged.clearValidTo === true
3953
+ ).map((edge) => ({
3076
3954
  branchId: edge.staged.branchId,
3077
- validTo: requireDefined(edge.staged.validTo)
3955
+ change: typeof edge.staged.validTo === "string" ? { kind: "set", validTo: edge.staged.validTo } : { kind: "clear" }
3078
3956
  })),
3079
3957
  preferredBranchId
3080
3958
  );
3081
3959
  const window = {
3082
3960
  ...survivor.staged.validFrom === void 0 ? {} : { validFrom: survivor.staged.validFrom },
3083
- ...foldedEnd === void 0 ? {} : { validTo: foldedEnd }
3961
+ ...foldedEnd?.kind === "set" ? { validTo: foldedEnd.validTo } : foldedEnd?.kind === "clear" ? { clearValidTo: true } : {}
3084
3962
  };
3085
3963
  const contentKeys = new Set(foldSet.map((edge) => edge.dedupeKey));
3086
3964
  if (contentKeys.size === 1) {
@@ -3225,7 +4103,10 @@ var mergeOptionsScalarSchema = z.object({
3225
4103
  provenance: z.boolean().default(MERGE_OPTION_DEFAULTS.provenance),
3226
4104
  persistProvenance: z.boolean().default(MERGE_OPTION_DEFAULTS.persistProvenance),
3227
4105
  maxComparisonsPerKind: z.number().int({ message: "maxComparisonsPerKind must be an integer" }).min(0, { message: "maxComparisonsPerKind must be >= 0" }).optional(),
3228
- clusterMaxDiameter: z.number().positive({ message: "clusterMaxDiameter must be positive" }).optional()
4106
+ clusterMaxDiameter: z.number().positive({ message: "clusterMaxDiameter must be positive" }).optional(),
4107
+ candidateDiagnostics: z.object({
4108
+ limit: z.number().int({ message: "candidateDiagnostics.limit must be an integer" }).min(0, { message: "candidateDiagnostics.limit must be >= 0" })
4109
+ }).strict().optional()
3229
4110
  });
3230
4111
  function validatePropertyConflictPolicy(policy, label) {
3231
4112
  if (typeof policy === "string" && !propertyConflictPolicySchema.safeParse(policy).success) {
@@ -3288,7 +4169,8 @@ function normalizeMergeOptions(options = {}) {
3288
4169
  provenance: options.provenance,
3289
4170
  persistProvenance: options.persistProvenance,
3290
4171
  ...options.maxComparisonsPerKind === void 0 ? {} : { maxComparisonsPerKind: options.maxComparisonsPerKind },
3291
- ...options.clusterMaxDiameter === void 0 ? {} : { clusterMaxDiameter: options.clusterMaxDiameter }
4172
+ ...options.clusterMaxDiameter === void 0 ? {} : { clusterMaxDiameter: options.clusterMaxDiameter },
4173
+ ...options.candidateDiagnostics === void 0 ? {} : { candidateDiagnostics: options.candidateDiagnostics }
3292
4174
  });
3293
4175
  const onPropertyConflict = validatePropertyConflictPolicy(
3294
4176
  options.onPropertyConflict ?? MERGE_OPTION_DEFAULTS.onPropertyConflict,
@@ -3319,78 +4201,693 @@ function normalizeMergeOptions(options = {}) {
3319
4201
  ...options.target === void 0 ? {} : { target: options.target },
3320
4202
  ...scalar.maxComparisonsPerKind === void 0 ? {} : { maxComparisonsPerKind: scalar.maxComparisonsPerKind },
3321
4203
  ...scalar.clusterMaxDiameter === void 0 ? {} : { clusterMaxDiameter: scalar.clusterMaxDiameter },
4204
+ ...scalar.candidateDiagnostics === void 0 ? {} : { candidateDiagnostics: scalar.candidateDiagnostics },
3322
4205
  ...options.branchOrder === void 0 ? {} : { branchOrder: options.branchOrder },
3323
4206
  ...provenanceWeights === void 0 ? {} : { provenanceWeights }
3324
4207
  };
3325
4208
  }
3326
- var PROVENANCE_KIND = "Provenance";
3327
- var PROVENANCE_OWNER_KIND = "ProvenanceOwner";
3328
- var Provenance = defineNode(PROVENANCE_KIND, {
3329
- schema: z.object({
3330
- targetGraphId: z.string(),
3331
- role: z.enum(["node", "edge"]),
3332
- canonicalId: z.string(),
3333
- canonicalKind: z.string(),
3334
- branchId: z.string(),
3335
- sourceId: z.string()
3336
- })
3337
- });
3338
- var PROVENANCE_OWNER = "@nicia-ai/typegraph/merge-provenance";
3339
- var PROVENANCE_OWNER_VERSION = 1;
3340
- var PROVENANCE_OWNER_ID = "merge-provenance-owner";
3341
- var ProvenanceOwner = defineNode(PROVENANCE_OWNER_KIND, {
3342
- schema: z.object({
3343
- owner: z.literal(PROVENANCE_OWNER),
3344
- version: z.literal(PROVENANCE_OWNER_VERSION),
3345
- targetGraphId: z.string()
3346
- })
4209
+ var MERGE_PLAN_FORMAT_VERSION = 1;
4210
+ var MERGE_PLAN_DIGEST_ALGORITHM = "sha256";
4211
+ var nonEmptyStringSchema = z.string().min(1);
4212
+ var finiteNumberSchema = z.number();
4213
+ var nonNegativeIntegerSchema = finiteNumberSchema.int().nonnegative();
4214
+ var jsonObjectSchema = z.record(z.string(), z.json());
4215
+ var mergePlanEntityRefSchema = z.object({ kind: nonEmptyStringSchema, id: nonEmptyStringSchema }).strict();
4216
+ var mergePlanSchemaFenceSchema = z.object({
4217
+ managed: z.boolean(),
4218
+ version: nonNegativeIntegerSchema,
4219
+ hash: nonEmptyStringSchema
4220
+ }).strict();
4221
+ var mergePlanRevisionFenceSchema = z.object({
4222
+ origin: nonEmptyStringSchema,
4223
+ revision: nonEmptyStringSchema.nullable()
4224
+ }).strict();
4225
+ var mergePlanTargetFenceSchema = z.object({
4226
+ graphId: nonEmptyStringSchema,
4227
+ schema: mergePlanSchemaFenceSchema,
4228
+ revision: mergePlanRevisionFenceSchema
4229
+ }).strict();
4230
+ var branchAnchorSchema = z.object({
4231
+ branchId: nonEmptyStringSchema,
4232
+ baseVersion: nonEmptyStringSchema
4233
+ }).strict();
4234
+ var snapshotAnchorsSchema = z.object({
4235
+ kind: z.literal("snapshot"),
4236
+ base: z.object({
4237
+ graphId: nonEmptyStringSchema,
4238
+ baseVersion: nonEmptyStringSchema
4239
+ }).strict(),
4240
+ branches: z.array(branchAnchorSchema)
4241
+ }).strict();
4242
+ var incrementalAnchorsSchema = z.object({
4243
+ kind: z.literal("incremental"),
4244
+ forkPoint: z.object({
4245
+ graphId: nonEmptyStringSchema,
4246
+ baseVersion: nonEmptyStringSchema,
4247
+ schema: mergePlanSchemaFenceSchema
4248
+ }).strict(),
4249
+ branches: z.array(branchAnchorSchema)
4250
+ }).strict();
4251
+ var mergePlanAnchorsSchema = z.discriminatedUnion("kind", [
4252
+ snapshotAnchorsSchema,
4253
+ incrementalAnchorsSchema
4254
+ ]);
4255
+ var nodeUpsertSchema = z.object({
4256
+ kind: nonEmptyStringSchema,
4257
+ id: nonEmptyStringSchema,
4258
+ setProps: jsonObjectSchema,
4259
+ unsetProps: z.array(nonEmptyStringSchema),
4260
+ validFrom: nonEmptyStringSchema.optional(),
4261
+ validTo: nonEmptyStringSchema.optional()
4262
+ }).strict();
4263
+ var edgeUpsertSchema = z.object({
4264
+ kind: nonEmptyStringSchema,
4265
+ id: nonEmptyStringSchema,
4266
+ from: mergePlanEntityRefSchema,
4267
+ to: mergePlanEntityRefSchema,
4268
+ setProps: jsonObjectSchema,
4269
+ unsetProps: z.array(nonEmptyStringSchema),
4270
+ validFrom: nonEmptyStringSchema.optional(),
4271
+ validTo: nonEmptyStringSchema.optional()
4272
+ }).strict();
4273
+ var mergePlanIdentityAssertionSchema = z.object({
4274
+ id: nonEmptyStringSchema,
4275
+ relation: z.enum(["same", "different"]),
4276
+ a: mergePlanEntityRefSchema,
4277
+ b: mergePlanEntityRefSchema,
4278
+ validFrom: nonEmptyStringSchema,
4279
+ validTo: nonEmptyStringSchema.optional(),
4280
+ endedBy: mergePlanEntityRefSchema.optional()
4281
+ }).strict();
4282
+ var mergePlanWritesSchema = z.object({
4283
+ nodeDeletes: z.array(mergePlanEntityRefSchema),
4284
+ nodeUpserts: z.array(nodeUpsertSchema),
4285
+ edgeDeletes: z.array(mergePlanEntityRefSchema),
4286
+ edgeUpserts: z.array(edgeUpsertSchema),
4287
+ identityAssertions: z.array(mergePlanIdentityAssertionSchema),
4288
+ identityRetractions: z.array(mergePlanIdentityAssertionSchema)
4289
+ }).strict();
4290
+ var proposedRoleSchema = z.object({
4291
+ upserts: nonNegativeIntegerSchema,
4292
+ deletions: nonNegativeIntegerSchema
4293
+ }).strict();
4294
+ var mergePlanProposedSummarySchema = z.object({
4295
+ nodes: proposedRoleSchema,
4296
+ edges: proposedRoleSchema,
4297
+ identity: z.object({
4298
+ assertions: nonNegativeIntegerSchema,
4299
+ retractions: nonNegativeIntegerSchema
4300
+ }).strict()
4301
+ }).strict();
4302
+ var mergePlanGuardsSchema = z.object({
4303
+ canonicalMappings: z.array(
4304
+ z.object({
4305
+ member: mergePlanEntityRefSchema,
4306
+ canonical: mergePlanEntityRefSchema
4307
+ }).strict()
4308
+ ),
4309
+ retypes: z.array(
4310
+ z.object({
4311
+ entity: mergePlanEntityRefSchema,
4312
+ toKind: nonEmptyStringSchema
4313
+ }).strict()
4314
+ ),
4315
+ deletedNodes: z.array(mergePlanEntityRefSchema),
4316
+ incremental: z.object({
4317
+ tombstoneResurrection: z.literal("refuse"),
4318
+ lossyUpdates: z.literal("refuse"),
4319
+ edgeIdentity: z.literal("preserve")
4320
+ }).strict().optional()
4321
+ }).strict();
4322
+ var matchSourceSchema = z.discriminatedUnion("kind", [
4323
+ z.object({ kind: z.literal("block"), sourceId: nonEmptyStringSchema }).strict(),
4324
+ z.object({
4325
+ kind: z.literal("unique"),
4326
+ sourceId: nonEmptyStringSchema,
4327
+ constraintName: nonEmptyStringSchema
4328
+ }).strict(),
4329
+ z.object({
4330
+ kind: z.literal("baseUnique"),
4331
+ sourceId: nonEmptyStringSchema,
4332
+ constraintName: nonEmptyStringSchema
4333
+ }).strict(),
4334
+ z.object({
4335
+ kind: z.literal("baseIndex"),
4336
+ sourceId: nonEmptyStringSchema,
4337
+ indexName: nonEmptyStringSchema
4338
+ }).strict(),
4339
+ z.object({ kind: z.literal("keyless"), sourceId: nonEmptyStringSchema }).strict(),
4340
+ z.object({ kind: z.literal("retype"), sourceId: nonEmptyStringSchema }).strict(),
4341
+ z.object({
4342
+ kind: z.literal("custom"),
4343
+ sourceId: nonEmptyStringSchema,
4344
+ metadata: z.json().optional()
4345
+ }).strict()
4346
+ ]);
4347
+ var similarityStrategySchema = z.discriminatedUnion("kind", [
4348
+ z.object({
4349
+ kind: z.literal("fulltext"),
4350
+ fields: z.array(nonEmptyStringSchema)
4351
+ }).strict(),
4352
+ z.object({
4353
+ kind: z.literal("vector"),
4354
+ fields: z.array(nonEmptyStringSchema)
4355
+ }).strict(),
4356
+ z.object({
4357
+ kind: z.literal("hybrid"),
4358
+ fields: z.array(nonEmptyStringSchema),
4359
+ weights: z.object({ vector: finiteNumberSchema, fulltext: finiteNumberSchema }).strict()
4360
+ }).strict(),
4361
+ z.object({ kind: z.literal("custom") }).strict()
4362
+ ]);
4363
+ var definitionalEvidenceSchema = z.object({
4364
+ a: mergePlanEntityRefSchema,
4365
+ b: mergePlanEntityRefSchema,
4366
+ sources: z.array(matchSourceSchema),
4367
+ decision: z.literal("definitional")
4368
+ }).strict();
4369
+ var scoredEvidenceSchema = z.object({
4370
+ a: mergePlanEntityRefSchema,
4371
+ b: mergePlanEntityRefSchema,
4372
+ sources: z.array(matchSourceSchema),
4373
+ decision: z.literal("scored"),
4374
+ strategy: similarityStrategySchema,
4375
+ score: finiteNumberSchema,
4376
+ threshold: finiteNumberSchema
4377
+ }).strict();
4378
+ var mergePlanMatchEvidenceSchema = z.discriminatedUnion("decision", [
4379
+ definitionalEvidenceSchema,
4380
+ scoredEvidenceSchema
4381
+ ]);
4382
+ var entityResolutionSchema = z.object({
4383
+ canonicalId: nonEmptyStringSchema,
4384
+ memberIds: z.array(nonEmptyStringSchema),
4385
+ kind: nonEmptyStringSchema,
4386
+ branchOrigins: z.array(nonEmptyStringSchema),
4387
+ decisiveEdges: z.array(mergePlanMatchEvidenceSchema)
4388
+ }).strict();
4389
+ var diagnosticsSchema = z.object({
4390
+ entries: z.array(
4391
+ z.object({
4392
+ evidence: mergePlanMatchEvidenceSchema,
4393
+ scoreDecision: z.enum(["accepted", "rejected"]),
4394
+ reason: z.literal("noComparableValues").optional(),
4395
+ clusterDisposition: z.union([
4396
+ z.literal("retained"),
4397
+ z.object({
4398
+ kind: z.literal("excluded"),
4399
+ reason: z.enum(["diameter", "baseAmbiguity"])
4400
+ }).strict()
4401
+ ]).optional()
4402
+ }).strict()
4403
+ ),
4404
+ total: nonNegativeIntegerSchema,
4405
+ limit: nonNegativeIntegerSchema,
4406
+ truncated: z.boolean()
4407
+ }).strict();
4408
+ var mergePlanReviewSchema = z.object({
4409
+ resolutions: z.array(entityResolutionSchema),
4410
+ conflicts: z.array(
4411
+ z.object({
4412
+ entityId: nonEmptyStringSchema,
4413
+ kind: nonEmptyStringSchema,
4414
+ property: nonEmptyStringSchema,
4415
+ values: z.array(
4416
+ z.object({ branchId: nonEmptyStringSchema, value: z.json() }).strict()
4417
+ ),
4418
+ resolution: z.json()
4419
+ }).strict()
4420
+ ),
4421
+ deleteModifyConflicts: z.array(
4422
+ z.object({
4423
+ entityId: nonEmptyStringSchema,
4424
+ kind: nonEmptyStringSchema,
4425
+ deletedBy: nonEmptyStringSchema,
4426
+ modifiedBy: nonEmptyStringSchema,
4427
+ resolution: z.enum(["deleteWins", "modifyWins", "flag"])
4428
+ }).strict()
4429
+ ),
4430
+ typeReconciliations: z.array(
4431
+ z.object({
4432
+ entityId: nonEmptyStringSchema,
4433
+ fromTypes: z.array(nonEmptyStringSchema),
4434
+ toType: nonEmptyStringSchema,
4435
+ decisiveEdges: z.array(mergePlanMatchEvidenceSchema).optional()
4436
+ }).strict()
4437
+ ),
4438
+ dropped: z.array(
4439
+ z.discriminatedUnion("kind", [
4440
+ z.object({
4441
+ kind: z.literal("node"),
4442
+ id: nonEmptyStringSchema,
4443
+ reason: nonEmptyStringSchema
4444
+ }).strict(),
4445
+ z.object({
4446
+ kind: z.literal("edge"),
4447
+ id: nonEmptyStringSchema,
4448
+ reason: nonEmptyStringSchema
4449
+ }).strict(),
4450
+ z.object({
4451
+ kind: z.literal("identity"),
4452
+ id: nonEmptyStringSchema,
4453
+ reason: nonEmptyStringSchema
4454
+ }).strict()
4455
+ ])
4456
+ ),
4457
+ validityEnds: z.array(
4458
+ z.object({
4459
+ entity: z.enum(["node", "edge"]),
4460
+ kind: nonEmptyStringSchema,
4461
+ id: nonEmptyStringSchema,
4462
+ validTo: nonEmptyStringSchema,
4463
+ claimedBy: z.array(nonEmptyStringSchema),
4464
+ precedence: z.literal("target").optional()
4465
+ }).strict()
4466
+ ),
4467
+ baseAmbiguities: z.array(
4468
+ z.object({
4469
+ baseIds: z.array(mergePlanEntityRefSchema),
4470
+ memberIds: z.array(mergePlanEntityRefSchema)
4471
+ }).strict()
4472
+ ),
4473
+ provenanceRecords: z.array(
4474
+ z.object({
4475
+ role: z.enum(["node", "edge"]),
4476
+ canonicalId: nonEmptyStringSchema,
4477
+ canonicalKind: nonEmptyStringSchema,
4478
+ branchId: nonEmptyStringSchema,
4479
+ sourceId: nonEmptyStringSchema
4480
+ }).strict()
4481
+ ),
4482
+ warnings: z.array(z.string()),
4483
+ diagnostics: diagnosticsSchema.optional()
4484
+ }).strict();
4485
+ var mergePlanDigestSchema = z.object({
4486
+ algorithm: z.literal(MERGE_PLAN_DIGEST_ALGORITHM),
4487
+ value: z.string().regex(/^[\da-f]{64}$/)
4488
+ }).strict();
4489
+ var mergePlanArtifactV1BaseSchema = z.object({
4490
+ formatVersion: z.literal(MERGE_PLAN_FORMAT_VERSION),
4491
+ digest: mergePlanDigestSchema,
4492
+ mode: z.enum(["snapshot", "incremental"]),
4493
+ target: mergePlanTargetFenceSchema,
4494
+ anchors: mergePlanAnchorsSchema,
4495
+ proposed: mergePlanProposedSummarySchema,
4496
+ writes: mergePlanWritesSchema,
4497
+ guards: mergePlanGuardsSchema,
4498
+ review: mergePlanReviewSchema,
4499
+ provenance: z.object({ includeInReport: z.boolean(), persist: z.boolean() }).strict()
4500
+ }).strict();
4501
+ var mergePlanArtifactV1InputBaseSchema = mergePlanArtifactV1BaseSchema.omit({
4502
+ digest: true
3347
4503
  });
3348
- function provenanceGraphId(targetGraphId) {
3349
- return `${targetGraphId}::merge-provenance`;
3350
- }
3351
- function buildOwnedProvenanceGraph(targetGraphId) {
3352
- return defineInternalGraph({
3353
- id: provenanceGraphId(targetGraphId),
3354
- nodes: {
3355
- Provenance: { type: Provenance },
3356
- ProvenanceOwner: { type: ProvenanceOwner }
3357
- },
3358
- edges: {}
3359
- });
3360
- }
3361
- function buildProvenanceGraph(targetGraphId) {
3362
- return defineInternalGraph({
3363
- id: provenanceGraphId(targetGraphId),
3364
- nodes: { Provenance: { type: Provenance } },
3365
- edges: {}
3366
- });
3367
- }
3368
- async function openProvenanceStore(...args) {
3369
- const [backend, targetGraphId] = args.length === 1 ? [storeBackend(args[0]), args[0].graphId] : args;
3370
- const graph = buildOwnedProvenanceGraph(targetGraphId);
3371
- const preflight = await inspectSidecarGraphId(backend, graph, targetGraphId);
3372
- if (preflight.state === "refuse") {
3373
- throw provenanceGraphIdCollision(graph.id, targetGraphId, preflight.reason);
4504
+ function addSemanticIssues(artifact, ctx) {
4505
+ if (artifact.mode !== artifact.anchors.kind) {
4506
+ ctx.addIssue({
4507
+ code: "custom",
4508
+ path: ["anchors", "kind"],
4509
+ message: `Expected ${artifact.mode} anchors for a ${artifact.mode} plan.`
4510
+ });
3374
4511
  }
3375
- if (preflight.state !== "owned") {
3376
- const claim = await claimSidecarOwnership(backend, graph, targetGraphId);
3377
- if (claim.state === "refuse") {
3378
- throw provenanceGraphIdCollision(graph.id, targetGraphId, claim.reason);
3379
- }
4512
+ if (artifact.mode === "incremental" !== (artifact.guards.incremental !== void 0)) {
4513
+ ctx.addIssue({
4514
+ code: "custom",
4515
+ path: ["guards", "incremental"],
4516
+ message: "Incremental write guards must be present exactly for incremental plans."
4517
+ });
3380
4518
  }
3381
- const [store] = await createStoreWithSchema(graph, backend);
3382
- return store;
3383
- }
3384
- async function claimSidecarOwnership(backend, graph, targetGraphId) {
3385
- const fence = backend.schemaWriteTransaction;
3386
- if (fence === void 0) {
3387
- throw provenanceClaimUnfenced(graph.id, targetGraphId);
4519
+ const expectedCounts = {
4520
+ nodeUpserts: artifact.writes.nodeUpserts.length,
4521
+ nodeDeletions: artifact.writes.nodeDeletes.length,
4522
+ edgeUpserts: artifact.writes.edgeUpserts.length,
4523
+ edgeDeletions: artifact.writes.edgeDeletes.length,
4524
+ assertions: artifact.writes.identityAssertions.length,
4525
+ retractions: artifact.writes.identityRetractions.length
4526
+ };
4527
+ const receivedCounts = {
4528
+ nodeUpserts: artifact.proposed.nodes.upserts,
4529
+ nodeDeletions: artifact.proposed.nodes.deletions,
4530
+ edgeUpserts: artifact.proposed.edges.upserts,
4531
+ edgeDeletions: artifact.proposed.edges.deletions,
4532
+ assertions: artifact.proposed.identity.assertions,
4533
+ retractions: artifact.proposed.identity.retractions
4534
+ };
4535
+ if (JSON.stringify(expectedCounts) !== JSON.stringify(receivedCounts)) {
4536
+ ctx.addIssue({
4537
+ code: "custom",
4538
+ path: ["proposed"],
4539
+ message: "Proposed counts must equal the resolved write-set counts."
4540
+ });
3388
4541
  }
3389
- return fence(graph.id, async (tx) => {
3390
- await drainUnfencedRowWriters(tx);
3391
- const verdict = await inspectSidecarGraphId(tx, graph, targetGraphId);
3392
- if (verdict.state === "refuse" || verdict.state === "owned") return verdict;
3393
- await tx.insertNode({
4542
+ addOperationIssues(
4543
+ artifact.writes.nodeDeletes,
4544
+ artifact.writes.nodeUpserts,
4545
+ "node",
4546
+ ctx
4547
+ );
4548
+ addOperationIssues(
4549
+ artifact.writes.edgeDeletes,
4550
+ artifact.writes.edgeUpserts,
4551
+ "edge",
4552
+ ctx
4553
+ );
4554
+ for (const [index, upsert] of artifact.writes.nodeUpserts.entries()) {
4555
+ addPropertyPatchIssues(upsert, ["writes", "nodeUpserts", index], ctx);
4556
+ }
4557
+ for (const [index, upsert] of artifact.writes.edgeUpserts.entries()) {
4558
+ addPropertyPatchIssues(upsert, ["writes", "edgeUpserts", index], ctx);
4559
+ }
4560
+ const canonicalByMember = /* @__PURE__ */ new Map();
4561
+ let invalidCanonicalMappings = false;
4562
+ for (const mapping of artifact.guards.canonicalMappings) {
4563
+ const member = entityKey(mapping.member);
4564
+ const canonical = entityKey(mapping.canonical);
4565
+ if (canonicalByMember.has(member)) invalidCanonicalMappings = true;
4566
+ canonicalByMember.set(member, canonical);
4567
+ }
4568
+ for (const canonical of canonicalByMember.values()) {
4569
+ if (canonicalByMember.get(canonical) !== canonical) {
4570
+ invalidCanonicalMappings = true;
4571
+ }
4572
+ }
4573
+ if (invalidCanonicalMappings) {
4574
+ ctx.addIssue({
4575
+ code: "custom",
4576
+ path: ["guards", "canonicalMappings"],
4577
+ message: "Canonical mappings must name each member once and include a self-mapped canonical root."
4578
+ });
4579
+ }
4580
+ const diagnostics = artifact.review.diagnostics;
4581
+ if (diagnostics !== void 0) {
4582
+ const expectedRetained = Math.min(diagnostics.total, diagnostics.limit);
4583
+ if (diagnostics.entries.length !== expectedRetained || diagnostics.truncated !== diagnostics.total > diagnostics.entries.length) {
4584
+ ctx.addIssue({
4585
+ code: "custom",
4586
+ path: ["review", "diagnostics"],
4587
+ message: "Diagnostic retention and truncation metadata must exactly match total and limit."
4588
+ });
4589
+ }
4590
+ for (const [index, diagnostic] of diagnostics.entries.entries()) {
4591
+ addEvidenceIssues(
4592
+ diagnostic.evidence,
4593
+ ["review", "diagnostics", "entries", index, "evidence"],
4594
+ ctx
4595
+ );
4596
+ const accepted = diagnostic.evidence.decision === "definitional" || diagnostic.evidence.score >= diagnostic.evidence.threshold && diagnostic.reason === void 0;
4597
+ if (diagnostic.scoreDecision !== (accepted ? "accepted" : "rejected") || diagnostic.evidence.decision === "scored" && diagnostic.reason === "noComparableValues" && diagnostic.evidence.score !== 0 || diagnostic.evidence.decision === "definitional" && (diagnostic.reason !== void 0 || typeof diagnostic.clusterDisposition !== "object") || diagnostic.scoreDecision === "rejected" && diagnostic.clusterDisposition !== void 0) {
4598
+ ctx.addIssue({
4599
+ code: "custom",
4600
+ path: ["review", "diagnostics", "entries", index],
4601
+ message: "Diagnostic decision, reason, and cluster disposition must agree with its score evidence."
4602
+ });
4603
+ }
4604
+ }
4605
+ }
4606
+ for (const [index, resolution] of artifact.review.resolutions.entries()) {
4607
+ addResolutionEvidenceIssues(artifact, resolution, index, ctx);
4608
+ }
4609
+ for (const [
4610
+ reconciliationIndex,
4611
+ reconciliation
4612
+ ] of artifact.review.typeReconciliations.entries()) {
4613
+ for (const [edgeIndex, evidence] of (reconciliation.decisiveEdges ?? []).entries()) {
4614
+ addEvidenceIssues(
4615
+ evidence,
4616
+ [
4617
+ "review",
4618
+ "typeReconciliations",
4619
+ reconciliationIndex,
4620
+ "decisiveEdges",
4621
+ edgeIndex
4622
+ ],
4623
+ ctx
4624
+ );
4625
+ }
4626
+ }
4627
+ }
4628
+ function addResolutionEvidenceIssues(artifact, resolution, resolutionIndex, ctx) {
4629
+ const retypedKinds = new Map(
4630
+ artifact.guards.retypes.map((retype) => [
4631
+ entityKey(retype.entity),
4632
+ retype.toKind
4633
+ ])
4634
+ );
4635
+ const clusterMembers = artifact.guards.canonicalMappings.filter((mapping) => {
4636
+ const canonicalKind = retypedKinds.get(entityKey(mapping.canonical)) ?? mapping.canonical.kind;
4637
+ return mapping.canonical.id === resolution.canonicalId && canonicalKind === resolution.kind;
4638
+ }).map((mapping) => mapping.member);
4639
+ const uniqueMembers = new Map(
4640
+ clusterMembers.map((member) => [entityKey(member), member])
4641
+ );
4642
+ const memberIds = new Set(
4643
+ [...uniqueMembers.values()].map((member) => member.id)
4644
+ );
4645
+ const declaredMemberIds = new Set(resolution.memberIds);
4646
+ if (uniqueMembers.size < 2 || declaredMemberIds.size !== resolution.memberIds.length || memberIds.size !== declaredMemberIds.size || [...memberIds].some((id) => !declaredMemberIds.has(id)) || resolution.decisiveEdges.length !== uniqueMembers.size - 1) {
4647
+ ctx.addIssue({
4648
+ code: "custom",
4649
+ path: ["review", "resolutions", resolutionIndex],
4650
+ message: "A resolution must name its complete guarded cluster and carry exactly N-1 decisive edges."
4651
+ });
4652
+ return;
4653
+ }
4654
+ const parent = new Map([...uniqueMembers.keys()].map((key) => [key, key]));
4655
+ const find = (key) => {
4656
+ const next = parent.get(key);
4657
+ if (next === void 0 || next === key) return key;
4658
+ const root = find(next);
4659
+ parent.set(key, root);
4660
+ return root;
4661
+ };
4662
+ let invalidTree = false;
4663
+ for (const [edgeIndex, evidence] of resolution.decisiveEdges.entries()) {
4664
+ addEvidenceIssues(
4665
+ evidence,
4666
+ ["review", "resolutions", resolutionIndex, "decisiveEdges", edgeIndex],
4667
+ ctx
4668
+ );
4669
+ const a = entityKey(evidence.a);
4670
+ const b = entityKey(evidence.b);
4671
+ if (!uniqueMembers.has(a) || !uniqueMembers.has(b)) {
4672
+ invalidTree = true;
4673
+ continue;
4674
+ }
4675
+ const rootA = find(a);
4676
+ const rootB = find(b);
4677
+ if (rootA === rootB) invalidTree = true;
4678
+ else parent.set(rootB, rootA);
4679
+ }
4680
+ if (invalidTree || new Set([...uniqueMembers.keys()].map((key) => find(key))).size !== 1) {
4681
+ ctx.addIssue({
4682
+ code: "custom",
4683
+ path: ["review", "resolutions", resolutionIndex, "decisiveEdges"],
4684
+ message: "Decisive edges must be an acyclic connected witness over the guarded cluster."
4685
+ });
4686
+ }
4687
+ }
4688
+ function addEvidenceIssues(evidence, path, ctx) {
4689
+ const sourcesCanonical = evidence.sources.every((source, index) => {
4690
+ const previous = evidence.sources[index - 1];
4691
+ return previous === void 0 || compareMatchSources(previous, source) < 0;
4692
+ });
4693
+ const invalidStrategy = evidence.decision === "scored" && (evidence.strategy.kind === "vector" && evidence.strategy.fields.length !== 1 || evidence.strategy.kind === "hybrid" && (evidence.strategy.weights.vector < 0 || evidence.strategy.weights.vector > 1 || evidence.strategy.weights.fulltext < 0 || evidence.strategy.weights.fulltext > 1 || Math.abs(
4694
+ evidence.strategy.weights.vector + evidence.strategy.weights.fulltext - 1
4695
+ ) > Number.EPSILON));
4696
+ if (compareEntityRefs(evidence.a, evidence.b) >= 0 || evidence.sources.length === 0 || !sourcesCanonical || evidence.decision === "scored" && (evidence.score < 0 || evidence.score > 1 || evidence.threshold < 0 || evidence.threshold > 1) || invalidStrategy) {
4697
+ ctx.addIssue({
4698
+ code: "custom",
4699
+ path: [...path],
4700
+ message: "Evidence endpoints and sources must be canonical, and scored values must be within [0, 1]."
4701
+ });
4702
+ }
4703
+ }
4704
+ function addOperationIssues(deletions, upserts, role, ctx) {
4705
+ const deletionKeys = deletions.map((entry) => entityKey(entry));
4706
+ const upsertKeys = upserts.map((entry) => entityKey(entry));
4707
+ if (new Set(deletionKeys).size !== deletionKeys.length || new Set(upsertKeys).size !== upsertKeys.length) {
4708
+ ctx.addIssue({
4709
+ code: "custom",
4710
+ path: ["writes"],
4711
+ message: `The ${role} write set must not contain duplicate identities.`
4712
+ });
4713
+ }
4714
+ const deleted = new Set(deletionKeys);
4715
+ if (upsertKeys.some((key) => deleted.has(key))) {
4716
+ ctx.addIssue({
4717
+ code: "custom",
4718
+ path: ["writes"],
4719
+ message: `The same ${role} identity cannot be both deleted and upserted.`
4720
+ });
4721
+ }
4722
+ }
4723
+ function addPropertyPatchIssues(patch, path, ctx) {
4724
+ const unset = new Set(patch.unsetProps);
4725
+ if (unset.size !== patch.unsetProps.length || Object.keys(patch.setProps).some((key) => unset.has(key))) {
4726
+ ctx.addIssue({
4727
+ code: "custom",
4728
+ path: [...path],
4729
+ message: "unsetProps must be duplicate-free and disjoint from setProps keys."
4730
+ });
4731
+ }
4732
+ }
4733
+ function entityKey(entity) {
4734
+ return JSON.stringify([entity.kind, entity.id]);
4735
+ }
4736
+ var mergePlanArtifactV1InputSchema = mergePlanArtifactV1InputBaseSchema.superRefine(
4737
+ (artifact, ctx) => addSemanticIssues(artifact, ctx)
4738
+ );
4739
+ var mergePlanArtifactV1Schema = mergePlanArtifactV1BaseSchema.superRefine(
4740
+ (artifact, ctx) => addSemanticIssues(artifact, ctx)
4741
+ );
4742
+
4743
+ // src/graph-merge/plan-canonical.ts
4744
+ function canonicalMergePlanJson(artifact) {
4745
+ const digestless = "digest" in artifact ? omitDigest(artifact) : artifact;
4746
+ return JSON.stringify(digestless, sortedReplacer);
4747
+ }
4748
+ async function computeMergePlanDigest(artifact) {
4749
+ return sha256Hex(canonicalMergePlanJson(artifact), 32);
4750
+ }
4751
+ async function finalizeMergePlanArtifact(artifact) {
4752
+ const finalized = {
4753
+ ...artifact,
4754
+ digest: {
4755
+ algorithm: MERGE_PLAN_DIGEST_ALGORITHM,
4756
+ value: await computeMergePlanDigest(artifact)
4757
+ }
4758
+ };
4759
+ const canonical = JSON.parse(
4760
+ JSON.stringify(finalized, sortedReplacer)
4761
+ );
4762
+ return canonical;
4763
+ }
4764
+ function omitDigest(artifact) {
4765
+ const { digest: _digest, ...digestless } = artifact;
4766
+ return digestless;
4767
+ }
4768
+
4769
+ // src/graph-merge/plan-wire.ts
4770
+ function parseMergePlanArtifact(input) {
4771
+ const version = readFormatVersion(input);
4772
+ if (version !== MERGE_PLAN_FORMAT_VERSION) {
4773
+ return {
4774
+ success: false,
4775
+ error: { kind: "unsupported-version", received: version }
4776
+ };
4777
+ }
4778
+ const parsed = mergePlanArtifactV1Schema.safeParse(input);
4779
+ if (!parsed.success) {
4780
+ return {
4781
+ success: false,
4782
+ error: { kind: "malformed", issues: parsed.error.issues }
4783
+ };
4784
+ }
4785
+ return {
4786
+ success: true,
4787
+ artifact: parsed.data
4788
+ };
4789
+ }
4790
+ async function verifyMergePlanDigest(artifact) {
4791
+ const expected = await computeMergePlanDigest(artifact);
4792
+ const received = artifact.digest.value;
4793
+ return expected === received ? { valid: true } : { valid: false, expected, received };
4794
+ }
4795
+ async function validateMergePlanArtifact(input) {
4796
+ const parsed = parseMergePlanArtifact(input);
4797
+ if (!parsed.success) return parsed;
4798
+ const digest = await verifyMergePlanDigest(parsed.artifact);
4799
+ if (!digest.valid) {
4800
+ return {
4801
+ success: false,
4802
+ error: {
4803
+ kind: "digest-mismatch",
4804
+ expected: digest.expected,
4805
+ received: digest.received
4806
+ }
4807
+ };
4808
+ }
4809
+ return parsed;
4810
+ }
4811
+ async function constructMergePlanArtifact(input) {
4812
+ const validatedInput = mergePlanArtifactV1InputSchema.parse(input);
4813
+ return finalizeMergePlanArtifact(
4814
+ validatedInput
4815
+ );
4816
+ }
4817
+ function readFormatVersion(input) {
4818
+ if (input === null || typeof input !== "object" || Array.isArray(input)) {
4819
+ return void 0;
4820
+ }
4821
+ return Reflect.get(input, "formatVersion");
4822
+ }
4823
+ var PROVENANCE_KIND = "Provenance";
4824
+ var PROVENANCE_OWNER_KIND = "ProvenanceOwner";
4825
+ var Provenance = defineNode(PROVENANCE_KIND, {
4826
+ schema: z.object({
4827
+ targetGraphId: z.string(),
4828
+ role: z.enum(["node", "edge"]),
4829
+ canonicalId: z.string(),
4830
+ canonicalKind: z.string(),
4831
+ branchId: z.string(),
4832
+ sourceId: z.string()
4833
+ })
4834
+ });
4835
+ var PROVENANCE_OWNER = "@nicia-ai/typegraph/merge-provenance";
4836
+ var PROVENANCE_OWNER_VERSION = 1;
4837
+ var PROVENANCE_OWNER_ID = "merge-provenance-owner";
4838
+ var ProvenanceOwner = defineNode(PROVENANCE_OWNER_KIND, {
4839
+ schema: z.object({
4840
+ owner: z.literal(PROVENANCE_OWNER),
4841
+ version: z.literal(PROVENANCE_OWNER_VERSION),
4842
+ targetGraphId: z.string()
4843
+ })
4844
+ });
4845
+ function provenanceGraphId(targetGraphId) {
4846
+ return `${targetGraphId}::merge-provenance`;
4847
+ }
4848
+ function buildOwnedProvenanceGraph(targetGraphId) {
4849
+ return defineInternalGraph({
4850
+ id: provenanceGraphId(targetGraphId),
4851
+ nodes: {
4852
+ Provenance: { type: Provenance },
4853
+ ProvenanceOwner: { type: ProvenanceOwner }
4854
+ },
4855
+ edges: {}
4856
+ });
4857
+ }
4858
+ function buildProvenanceGraph(targetGraphId) {
4859
+ return defineInternalGraph({
4860
+ id: provenanceGraphId(targetGraphId),
4861
+ nodes: { Provenance: { type: Provenance } },
4862
+ edges: {}
4863
+ });
4864
+ }
4865
+ async function openProvenanceStore(...args) {
4866
+ const [backend, targetGraphId] = args.length === 1 ? [storeBackend(args[0]), args[0].graphId] : args;
4867
+ const graph = buildOwnedProvenanceGraph(targetGraphId);
4868
+ const preflight = await inspectSidecarGraphId(backend, graph, targetGraphId);
4869
+ if (preflight.state === "refuse") {
4870
+ throw provenanceGraphIdCollision(graph.id, targetGraphId, preflight.reason);
4871
+ }
4872
+ if (preflight.state !== "owned") {
4873
+ const claim = await claimSidecarOwnership(backend, graph, targetGraphId);
4874
+ if (claim.state === "refuse") {
4875
+ throw provenanceGraphIdCollision(graph.id, targetGraphId, claim.reason);
4876
+ }
4877
+ }
4878
+ const [store] = await createStoreWithSchema(graph, backend);
4879
+ return store;
4880
+ }
4881
+ async function claimSidecarOwnership(backend, graph, targetGraphId) {
4882
+ const fence = backend.schemaWriteTransaction;
4883
+ if (fence === void 0) {
4884
+ throw provenanceClaimUnfenced(graph.id, targetGraphId);
4885
+ }
4886
+ return fence(graph.id, async (tx) => {
4887
+ await drainUnfencedRowWriters(tx);
4888
+ const verdict = await inspectSidecarGraphId(tx, graph, targetGraphId);
4889
+ if (verdict.state === "refuse" || verdict.state === "owned") return verdict;
4890
+ await tx.insertNode({
3394
4891
  graphId: graph.id,
3395
4892
  kind: PROVENANCE_OWNER_KIND,
3396
4893
  id: PROVENANCE_OWNER_ID,
@@ -3673,10 +5170,10 @@ async function readProvenance(store, query = {}) {
3673
5170
  }
3674
5171
 
3675
5172
  // src/graph-merge/sources.ts
3676
- function orderEndpoints(left, right) {
5173
+ function orderEndpoints(left, right, sources) {
3677
5174
  const leftKey = mergeKeyOf(left);
3678
5175
  const rightKey = mergeKeyOf(right);
3679
- return compareMergeKeys(leftKey, rightKey) <= 0 ? { a: leftKey, b: rightKey, left, right } : { a: rightKey, b: leftKey, left: right, right: left };
5176
+ return compareMergeKeys(leftKey, rightKey) <= 0 ? { a: leftKey, b: rightKey, left, right, sources } : { a: rightKey, b: leftKey, left: right, right: left, sources };
3680
5177
  }
3681
5178
  function sortMembersById(members) {
3682
5179
  return [...members].sort((left, right) => compareStrings(left.id, right.id));
@@ -3719,7 +5216,8 @@ function windowedPairs(members, config) {
3719
5216
  pairs.push(
3720
5217
  orderEndpoints(
3721
5218
  requireDefined(keyed[index]).node,
3722
- requireDefined(keyed[index_]).node
5219
+ requireDefined(keyed[index_]).node,
5220
+ [{ kind: "keyless", sourceId: "keyless" }]
3723
5221
  )
3724
5222
  );
3725
5223
  }
@@ -3733,8 +5231,22 @@ function pairsFromBlocks(blocks, keyless) {
3733
5231
  continue;
3734
5232
  }
3735
5233
  const members = sortMembersById(blocks.get(bucketKey) ?? []);
3736
- if (keyless !== void 0 && bucketKey === UNBLOCKED_BUCKET_KEY) {
3737
- pairs.push(...windowedPairs(members, keyless));
5234
+ if (bucketKey === UNBLOCKED_BUCKET_KEY) {
5235
+ if (keyless !== void 0) {
5236
+ pairs.push(...windowedPairs(members, keyless));
5237
+ continue;
5238
+ }
5239
+ for (let index = 0; index < members.length; index += 1) {
5240
+ for (let index_ = index + 1; index_ < members.length; index_ += 1) {
5241
+ pairs.push(
5242
+ orderEndpoints(
5243
+ requireDefined(members[index]),
5244
+ requireDefined(members[index_]),
5245
+ [{ kind: "keyless", sourceId: "keyless" }]
5246
+ )
5247
+ );
5248
+ }
5249
+ }
3738
5250
  continue;
3739
5251
  }
3740
5252
  for (let index = 0; index < members.length; index += 1) {
@@ -3742,7 +5254,8 @@ function pairsFromBlocks(blocks, keyless) {
3742
5254
  pairs.push(
3743
5255
  orderEndpoints(
3744
5256
  requireDefined(members[index]),
3745
- requireDefined(members[index_])
5257
+ requireDefined(members[index_]),
5258
+ [{ kind: "block", sourceId: "exactKey" }]
3746
5259
  )
3747
5260
  );
3748
5261
  }
@@ -3757,13 +5270,37 @@ function forcedEdgesFromBlocks(blocks) {
3757
5270
  continue;
3758
5271
  }
3759
5272
  const members = sortMembersById(blocks.get(bucketKey) ?? []);
5273
+ const constraintName = uniqueConstraintNameForBucket(bucketKey);
5274
+ if (constraintName === void 0) {
5275
+ throw new CandidateSourceError(
5276
+ "Unique candidate bucket has no constraint name.",
5277
+ {
5278
+ details: {
5279
+ operation: "candidateEvidence",
5280
+ sourceId: "unique",
5281
+ kind: members[0]?.kind ?? "unknown"
5282
+ }
5283
+ }
5284
+ );
5285
+ }
3760
5286
  for (let index = 0; index < members.length; index += 1) {
3761
5287
  for (let index_ = index + 1; index_ < members.length; index_ += 1) {
3762
5288
  const { a, b } = orderEndpoints(
3763
5289
  requireDefined(members[index]),
3764
- requireDefined(members[index_])
5290
+ requireDefined(members[index_]),
5291
+ []
3765
5292
  );
3766
- edges.push({ a, b, score: FORCED_MATCH_SCORE });
5293
+ edges.push({
5294
+ a,
5295
+ b,
5296
+ score: FORCED_MATCH_SCORE,
5297
+ evidence: {
5298
+ a: entityRef(a),
5299
+ b: entityRef(b),
5300
+ sources: [{ kind: "unique", sourceId: "unique", constraintName }],
5301
+ decision: "definitional"
5302
+ }
5303
+ });
3767
5304
  }
3768
5305
  }
3769
5306
  }
@@ -3797,7 +5334,13 @@ function ontologyRetypeEdges(stagedIdentities, isRetypeCompatible) {
3797
5334
  edges.push({
3798
5335
  a: requireDefined(members[0]),
3799
5336
  b: requireDefined(members[index]),
3800
- score: FORCED_MATCH_SCORE
5337
+ score: FORCED_MATCH_SCORE,
5338
+ evidence: {
5339
+ a: entityRef(requireDefined(members[0])),
5340
+ b: entityRef(requireDefined(members[index])),
5341
+ sources: [{ kind: "retype", sourceId: "retype" }],
5342
+ decision: "definitional"
5343
+ }
3801
5344
  });
3802
5345
  }
3803
5346
  }
@@ -3864,9 +5407,16 @@ var baseUniqueSource = {
3864
5407
  async generate(scope) {
3865
5408
  const { kind, nodes, uniqueConstraints, store } = scope;
3866
5409
  if (nodes === void 0 || uniqueConstraints === void 0 || store === void 0) {
3867
- throw new MergeError(
5410
+ throw new CandidateSourceError(
3868
5411
  "baseUniqueSource requires nodes, uniqueConstraints, and store in the source scope.",
3869
- { details: { kind, source: "baseUnique" } }
5412
+ {
5413
+ details: {
5414
+ kind,
5415
+ source: "baseUnique",
5416
+ sourceId: "baseUnique",
5417
+ operation: "generate"
5418
+ }
5419
+ }
3870
5420
  );
3871
5421
  }
3872
5422
  const collection = store.nodes[kind];
@@ -3884,14 +5434,23 @@ var baseUniqueSource = {
3884
5434
  try {
3885
5435
  return await collection.bulkFindByConstraint(constraint.name, items);
3886
5436
  } catch (error) {
3887
- throw new MergeError(
5437
+ throw new CandidateSourceError(
3888
5438
  `baseUniqueSource: new-vs-base lookup for constraint "${constraint.name}" on kind "${kind}" failed \u2014 a staged node's props could not be validated against the kind's schema for constraint-key computation.`,
3889
- { details: { kind, constraint: constraint.name }, cause: error }
5439
+ {
5440
+ details: {
5441
+ kind,
5442
+ sourceId: "baseUnique",
5443
+ operation: "lookupConstraint",
5444
+ constraint: constraint.name
5445
+ },
5446
+ cause: error
5447
+ }
3890
5448
  );
3891
5449
  }
3892
5450
  })
3893
5451
  );
3894
- for (const matches of matchesByConstraint) {
5452
+ for (const [constraintIndex, matches] of matchesByConstraint.entries()) {
5453
+ const constraint = requireDefined(constraints[constraintIndex]);
3895
5454
  for (const [index, node] of nodes.entries()) {
3896
5455
  const base = matches[index];
3897
5456
  if (base === void 0) {
@@ -3905,14 +5464,32 @@ var baseUniqueSource = {
3905
5464
  origin: "base",
3906
5465
  id: base.id,
3907
5466
  kind: base.kind,
3908
- props: nodeProps(base)
5467
+ props: nodeProps(base),
5468
+ ...base.meta.validFrom === void 0 ? {} : { validFrom: base.meta.validFrom },
5469
+ ...base.meta.validTo === void 0 ? {} : { validTo: base.meta.validTo }
3909
5470
  });
3910
5471
  }
3911
5472
  if (stagedKey === baseKey) {
3912
5473
  continue;
3913
5474
  }
3914
5475
  const { a, b } = compareMergeKeys(stagedKey, baseKey) <= 0 ? { a: stagedKey, b: baseKey } : { a: baseKey, b: stagedKey };
3915
- forcedEdges.push({ a, b, score: FORCED_MATCH_SCORE });
5476
+ forcedEdges.push({
5477
+ a,
5478
+ b,
5479
+ score: FORCED_MATCH_SCORE,
5480
+ evidence: {
5481
+ a: entityRef(a),
5482
+ b: entityRef(b),
5483
+ sources: [
5484
+ {
5485
+ kind: "baseUnique",
5486
+ sourceId: "baseUnique",
5487
+ constraintName: constraint.name
5488
+ }
5489
+ ],
5490
+ decision: "definitional"
5491
+ }
5492
+ });
3916
5493
  }
3917
5494
  }
3918
5495
  return {
@@ -3932,9 +5509,16 @@ var baseKeySource = {
3932
5509
  return { pairs: [], forcedEdges: [], baseMembers: [] };
3933
5510
  }
3934
5511
  if (nodes === void 0 || store === void 0) {
3935
- throw new MergeError(
5512
+ throw new CandidateSourceError(
3936
5513
  "baseKeySource requires nodes and store in the source scope.",
3937
- { details: { kind, source: "baseKey" } }
5514
+ {
5515
+ details: {
5516
+ kind,
5517
+ source: "baseKey",
5518
+ sourceId: "baseKey",
5519
+ operation: "generate"
5520
+ }
5521
+ }
3938
5522
  );
3939
5523
  }
3940
5524
  const collection = store.nodes[kind];
@@ -3946,9 +5530,17 @@ var baseKeySource = {
3946
5530
  try {
3947
5531
  matchesByNode = await collection.bulkFindByIndex(blockIndex, items);
3948
5532
  } catch (error) {
3949
- throw new MergeError(
5533
+ throw new CandidateSourceError(
3950
5534
  `baseKeySource: new-vs-base lookup on index "${blockIndex}" for kind "${kind}" failed \u2014 the index is undeclared or a staged node's props are incompatible with its indexed fields.`,
3951
- { details: { kind, index: blockIndex }, cause: error }
5535
+ {
5536
+ details: {
5537
+ kind,
5538
+ sourceId: "baseKey",
5539
+ operation: "lookupIndex",
5540
+ index: blockIndex
5541
+ },
5542
+ cause: error
5543
+ }
3952
5544
  );
3953
5545
  }
3954
5546
  const pairs = [];
@@ -3963,13 +5555,19 @@ var baseKeySource = {
3963
5555
  origin: "base",
3964
5556
  id: base.id,
3965
5557
  kind: base.kind,
3966
- props: nodeProps(base)
5558
+ props: nodeProps(base),
5559
+ ...base.meta.validFrom === void 0 ? {} : { validFrom: base.meta.validFrom },
5560
+ ...base.meta.validTo === void 0 ? {} : { validTo: base.meta.validTo }
3967
5561
  });
3968
5562
  }
3969
5563
  if (stagedKey === baseKey) {
3970
5564
  continue;
3971
5565
  }
3972
- pairs.push(orderEndpoints(stagedNode, base));
5566
+ pairs.push(
5567
+ orderEndpoints(stagedNode, base, [
5568
+ { kind: "baseIndex", sourceId: "baseKey", indexName: blockIndex }
5569
+ ])
5570
+ );
3973
5571
  }
3974
5572
  }
3975
5573
  return {
@@ -4321,6 +5919,8 @@ async function generateAllCandidates(target, staging, options, introspectionKind
4321
5919
  const allEdges = [];
4322
5920
  const warnings = [];
4323
5921
  const baseMembers = [];
5922
+ const diagnostics = [];
5923
+ let diagnosticsTotal = 0;
4324
5924
  const byKind = newNodesByKind(staging);
4325
5925
  const sources = useBaseSources ? [...CANDIDATE_SOURCES, baseUniqueSource, baseKeySource] : CANDIDATE_SOURCES;
4326
5926
  const baseStore = target;
@@ -4329,7 +5929,13 @@ async function generateAllCandidates(target, staging, options, introspectionKind
4329
5929
  async ([kind, items]) => {
4330
5930
  const resolveConfig = options.resolve[kind];
4331
5931
  if (resolveConfig === void 0) {
4332
- return ok({ edges: [], warnings: [], baseMembers: [] });
5932
+ return ok({
5933
+ edges: [],
5934
+ warnings: [],
5935
+ baseMembers: [],
5936
+ diagnostics: [],
5937
+ diagnosticsTotal: 0
5938
+ });
4333
5939
  }
4334
5940
  const nodes = items.map((staged) => asNode(staged));
4335
5941
  const uniqueConstraints = uniqueConstraintsFor(
@@ -4370,7 +5976,8 @@ async function generateAllCandidates(target, staging, options, introspectionKind
4370
5976
  resolveConfig,
4371
5977
  kindCtx,
4372
5978
  options.onComparisonCeiling,
4373
- options.maxComparisonsPerKind
5979
+ options.maxComparisonsPerKind,
5980
+ options.candidateDiagnostics?.limit ?? 0
4374
5981
  );
4375
5982
  if (isErr(scored)) {
4376
5983
  return err(scored.error);
@@ -4380,7 +5987,9 @@ async function generateAllCandidates(target, staging, options, introspectionKind
4380
5987
  warnings: scored.data.warnings.map(
4381
5988
  (warning) => `[${kind}] ${warning.message}`
4382
5989
  ),
4383
- baseMembers: kindBaseMembers
5990
+ baseMembers: kindBaseMembers,
5991
+ diagnostics: scored.data.diagnostics,
5992
+ diagnosticsTotal: scored.data.diagnosticsTotal
4384
5993
  });
4385
5994
  }
4386
5995
  )
@@ -4392,6 +6001,14 @@ async function generateAllCandidates(target, staging, options, introspectionKind
4392
6001
  allEdges.push(...result.data.edges);
4393
6002
  warnings.push(...result.data.warnings);
4394
6003
  baseMembers.push(...result.data.baseMembers);
6004
+ if (options.candidateDiagnostics !== void 0) {
6005
+ diagnostics.push(...result.data.diagnostics);
6006
+ diagnostics.sort(
6007
+ (left, right) => compareMatchEvidence(left.evidence, right.evidence)
6008
+ );
6009
+ diagnostics.splice(options.candidateDiagnostics.limit);
6010
+ }
6011
+ diagnosticsTotal += result.data.diagnosticsTotal;
4395
6012
  }
4396
6013
  return ok({
4397
6014
  // The ONE shared `(a, b)` edge comparator (id-first `(kind, id)` order), so this
@@ -4402,7 +6019,9 @@ async function generateAllCandidates(target, staging, options, introspectionKind
4402
6019
  // base members would tie under a bare-id compare).
4403
6020
  baseMembers: baseMembers.sort(
4404
6021
  (left, right) => compareMergeKeys(mergeKeyOf(left), mergeKeyOf(right))
4405
- )
6022
+ ),
6023
+ diagnostics,
6024
+ diagnosticsTotal
4406
6025
  });
4407
6026
  }
4408
6027
  function clusterMembersFor(cluster, newNodesById, baseMembersById) {
@@ -4426,7 +6045,9 @@ function clusterMembersFor(cluster, newNodesById, baseMembersById) {
4426
6045
  id: base.id,
4427
6046
  kind: base.kind,
4428
6047
  branchId: BASE_PROVENANCE_BRANCH,
4429
- props: base.props
6048
+ props: base.props,
6049
+ ...base.validFrom === void 0 ? {} : { validFrom: base.validFrom },
6050
+ ...base.validTo === void 0 ? {} : { validTo: base.validTo }
4430
6051
  });
4431
6052
  }
4432
6053
  }
@@ -4459,7 +6080,7 @@ function buildStagedEdges(staging, modifiedEdges, edgeValidityEnds, edgeDeletion
4459
6080
  for (const item of modifiedEdges) {
4460
6081
  const identity = mergeKeyOf(item.edge);
4461
6082
  stagedIdentities.add(identity);
4462
- const validTo = edgeValidityEnds.get(identity);
6083
+ const validToChange = edgeValidityEnds.get(identity);
4463
6084
  staged.push({
4464
6085
  id: item.edge.id,
4465
6086
  kind: item.edge.kind,
@@ -4471,13 +6092,13 @@ function buildStagedEdges(staging, modifiedEdges, edgeValidityEnds, edgeDeletion
4471
6092
  props: item.edge.forkProps,
4472
6093
  baseProps: item.edge.baseProps,
4473
6094
  branchId: item.branchId,
4474
- ...validTo === void 0 ? {} : { validTo }
6095
+ ...validToChange?.kind === "set" ? { validTo: validToChange.validTo } : validToChange?.kind === "clear" ? { clearValidTo: true } : {}
4475
6096
  });
4476
6097
  }
4477
6098
  for (const item of staging.windowedEdges) {
4478
6099
  const identity = mergeKeyOf(item.edge);
4479
- const validTo = edgeValidityEnds.get(identity);
4480
- if (validTo === void 0 || stagedIdentities.has(identity) || edgeDeletions.has(identity)) {
6100
+ const validToChange = edgeValidityEnds.get(identity);
6101
+ if (validToChange === void 0 || stagedIdentities.has(identity) || edgeDeletions.has(identity)) {
4481
6102
  continue;
4482
6103
  }
4483
6104
  stagedIdentities.add(identity);
@@ -4493,7 +6114,7 @@ function buildStagedEdges(staging, modifiedEdges, edgeValidityEnds, edgeDeletion
4493
6114
  props: item.edge.props,
4494
6115
  baseProps: item.edge.baseProps,
4495
6116
  branchId: item.branchId,
4496
- validTo
6117
+ ...validToChange.kind === "set" ? { validTo: validToChange.validTo } : { clearValidTo: true }
4497
6118
  });
4498
6119
  }
4499
6120
  return { edges: staged, windowOnlyCarried };
@@ -4541,7 +6162,7 @@ function buildProvenanceIndex(records) {
4541
6162
  };
4542
6163
  }
4543
6164
  var EMPTY_WEIGHTS = /* @__PURE__ */ new Map();
4544
- function planMerge(staging, candidateEdges, candidateWarnings, baseMembers, options, branchRank, subClassClosure, identityContext, storedIdentityRowsById, targetPeers, preferredBranchId) {
6165
+ function buildInternalMergePlan(staging, candidateEdges, candidateWarnings, candidateDiagnostics, candidateDiagnosticsTotal, baseMembers, options, branchRank, subClassClosure, identityContext, storedIdentityRowsById, targetPeers, preferredBranchId) {
4545
6166
  const identity = planIdentityChanges(staging, storedIdentityRowsById);
4546
6167
  const provenanceRecords = [];
4547
6168
  const recordedContributions = /* @__PURE__ */ new Set();
@@ -4593,7 +6214,56 @@ function planMerge(staging, candidateEdges, candidateWarnings, baseMembers, opti
4593
6214
  clusterEdges,
4594
6215
  baseIds
4595
6216
  );
4596
- const clusters = options.clusterMaxDiameter === void 0 ? guard.clusters : enforceDiameter(guard.clusters, clusterEdges, options.clusterMaxDiameter);
6217
+ const diameterGuard = options.clusterMaxDiameter === void 0 ? {
6218
+ clusters: guard.clusters,
6219
+ survivingEdges: guard.survivingEdges,
6220
+ excludedEdges: []
6221
+ } : enforceDiameterWithEdges(
6222
+ guard.clusters,
6223
+ clusterEdges,
6224
+ options.clusterMaxDiameter
6225
+ );
6226
+ const clusters = diameterGuard.clusters;
6227
+ const baseSurvivingEdges = new Set(guard.survivingEdges);
6228
+ const survivingEdges = diameterGuard.survivingEdges.filter(
6229
+ (edge) => baseSurvivingEdges.has(edge)
6230
+ );
6231
+ const excludedByEndpoints = /* @__PURE__ */ new Map();
6232
+ for (const excluded of [
6233
+ ...guard.excludedEdges,
6234
+ ...diameterGuard.excludedEdges
6235
+ ]) {
6236
+ excludedByEndpoints.set(
6237
+ JSON.stringify([excluded.edge.a, excluded.edge.b]),
6238
+ excluded.reason
6239
+ );
6240
+ }
6241
+ const diagnosticsWithDisposition = candidateDiagnostics.map((diagnostic) => {
6242
+ if (diagnostic.evidence.decision === "definitional") return diagnostic;
6243
+ if (diagnostic.scoreDecision === "rejected") return diagnostic;
6244
+ const a = mergeKey(diagnostic.evidence.a.kind, diagnostic.evidence.a.id);
6245
+ const b = mergeKey(diagnostic.evidence.b.kind, diagnostic.evidence.b.id);
6246
+ const reason = excludedByEndpoints.get(JSON.stringify([a, b]));
6247
+ return {
6248
+ ...diagnostic,
6249
+ clusterDisposition: reason === void 0 ? "retained" : { kind: "excluded", reason }
6250
+ };
6251
+ });
6252
+ const definitionalExclusions = [
6253
+ ...guard.excludedEdges,
6254
+ ...diameterGuard.excludedEdges
6255
+ ].filter((excluded) => excluded.edge.evidence.decision === "definitional").map((excluded) => ({
6256
+ evidence: excluded.edge.evidence,
6257
+ scoreDecision: "accepted",
6258
+ clusterDisposition: {
6259
+ kind: "excluded",
6260
+ reason: excluded.reason
6261
+ }
6262
+ }));
6263
+ const retainedDiagnostics = [
6264
+ ...diagnosticsWithDisposition,
6265
+ ...definitionalExclusions
6266
+ ].sort((left, right) => compareMatchEvidence(left.evidence, right.evidence));
4597
6267
  const baseAmbiguities = guard.events.map((event) => ({
4598
6268
  baseIds: event.baseIds.map((key) => ({ kind: kindOf(key), id: idOf(key) })),
4599
6269
  memberIds: event.memberIds.map((key) => ({
@@ -4603,6 +6273,7 @@ function planMerge(staging, candidateEdges, candidateWarnings, baseMembers, opti
4603
6273
  }));
4604
6274
  const canonicalEntities = [];
4605
6275
  const resolutions = [];
6276
+ const clusterByCanonicalIdentity = /* @__PURE__ */ new Map();
4606
6277
  const propertyConflicts = [];
4607
6278
  const reconcileInputs = [];
4608
6279
  for (const cluster of clusters) {
@@ -4622,6 +6293,10 @@ function planMerge(staging, candidateEdges, candidateWarnings, baseMembers, opti
4622
6293
  preferredBranchId
4623
6294
  );
4624
6295
  canonicalEntities.push(entity);
6296
+ clusterByCanonicalIdentity.set(
6297
+ mergeKey(entity.kind, entity.canonicalId),
6298
+ cluster
6299
+ );
4625
6300
  reconcileInputs.push({
4626
6301
  canonicalId: mergeKey(entity.kind, entity.canonicalId),
4627
6302
  memberKinds: members.map((member) => member.kind)
@@ -4630,7 +6305,10 @@ function planMerge(staging, candidateEdges, candidateWarnings, baseMembers, opti
4630
6305
  cluster.members.map((member) => idOf(member))
4631
6306
  );
4632
6307
  if (distinctMergedIds.size > 1) {
4633
- resolutions.push(entity.resolution);
6308
+ resolutions.push({
6309
+ ...entity.resolution,
6310
+ decisiveEdges: decisiveEdgesForCluster(cluster, survivingEdges)
6311
+ });
4634
6312
  }
4635
6313
  for (const conflict of entity.conflicts) {
4636
6314
  propertyConflicts.push(conflict);
@@ -4761,6 +6439,21 @@ function planMerge(staging, candidateEdges, candidateWarnings, baseMembers, opti
4761
6439
  subClassClosure,
4762
6440
  options.reconcileTypes
4763
6441
  );
6442
+ const reconciledClusterByEntityId = /* @__PURE__ */ new Map();
6443
+ for (const identity2 of reconciliation.retypeMap.keys()) {
6444
+ const entityId = idOf(identity2);
6445
+ const cluster = clusterByCanonicalIdentity.get(identity2);
6446
+ if (cluster !== void 0 && !reconciledClusterByEntityId.has(entityId)) {
6447
+ reconciledClusterByEntityId.set(entityId, cluster);
6448
+ }
6449
+ }
6450
+ const typeReconciliations = reconciliation.reconciliations.map((item) => {
6451
+ const cluster = reconciledClusterByEntityId.get(item.entityId);
6452
+ return cluster === void 0 ? item : {
6453
+ ...item,
6454
+ decisiveEdges: decisiveEdgesForCluster(cluster, survivingEdges)
6455
+ };
6456
+ });
4764
6457
  const entityByIdentity = /* @__PURE__ */ new Map();
4765
6458
  for (const entity of canonicalEntities) {
4766
6459
  entityByIdentity.set(mergeKey(entity.kind, entity.canonicalId), entity);
@@ -4769,11 +6462,24 @@ function planMerge(staging, candidateEdges, candidateWarnings, baseMembers, opti
4769
6462
  clusters,
4770
6463
  (cluster) => pickClusterCanonical(cluster, entityByIdentity)
4771
6464
  );
6465
+ const canonicalEndpointWindows = /* @__PURE__ */ new Map();
6466
+ for (const entity of canonicalEntities) {
6467
+ if (entity.endpointValidFrom === void 0 && entity.endpointValidTo === void 0) {
6468
+ continue;
6469
+ }
6470
+ const sourceKey2 = mergeKey(entity.kind, entity.canonicalId);
6471
+ const finalKind = reconciliation.retypeMap.get(sourceKey2) ?? entity.kind;
6472
+ canonicalEndpointWindows.set(mergeKey(finalKind, entity.canonicalId), {
6473
+ ...entity.endpointValidFrom === void 0 ? {} : { validFrom: entity.endpointValidFrom },
6474
+ ...entity.endpointValidTo === void 0 ? {} : { validTo: entity.endpointValidTo }
6475
+ });
6476
+ }
4772
6477
  const identityRemap = remapIdentityAssertionEndpoints(
4773
6478
  identity.assertions,
4774
6479
  canonicalOf,
4775
6480
  reconciliation.retypeMap,
4776
- storedIdentityRowsById
6481
+ storedIdentityRowsById,
6482
+ canonicalEndpointWindows
4777
6483
  );
4778
6484
  assertIdentityEndpointsNotDeleted(
4779
6485
  identityRemap.assertions,
@@ -4897,7 +6603,7 @@ function planMerge(staging, candidateEdges, candidateWarnings, baseMembers, opti
4897
6603
  mergeKey(right.kind, right.entityId)
4898
6604
  )
4899
6605
  ),
4900
- typeReconciliations: reconciliation.reconciliations,
6606
+ typeReconciliations,
4901
6607
  dropped,
4902
6608
  baseAmbiguities: baseAmbiguities.sort(
4903
6609
  (left, right) => compareMergeKeys(
@@ -4912,7 +6618,18 @@ function planMerge(staging, candidateEdges, candidateWarnings, baseMembers, opti
4912
6618
  )
4913
6619
  ),
4914
6620
  provenanceRecords,
4915
- warnings: candidateWarnings,
6621
+ warnings: [...candidateWarnings, ...identityRemap.warnings],
6622
+ ...options.candidateDiagnostics === void 0 ? {} : {
6623
+ candidateDiagnostics: {
6624
+ entries: retainedDiagnostics.slice(
6625
+ 0,
6626
+ options.candidateDiagnostics.limit
6627
+ ),
6628
+ total: candidateDiagnosticsTotal + definitionalExclusions.length,
6629
+ limit: options.candidateDiagnostics.limit,
6630
+ truncated: candidateDiagnosticsTotal + definitionalExclusions.length > options.candidateDiagnostics.limit
6631
+ }
6632
+ },
4916
6633
  identityAssertions: identityRemap.assertions,
4917
6634
  identityRetractions: survivingRetractions
4918
6635
  };
@@ -4963,13 +6680,13 @@ function plannedNodeWrites(plan) {
4963
6680
  continue;
4964
6681
  }
4965
6682
  written.add(identity);
4966
- const validTo = plan.nodeValidityEnds.get(identity);
6683
+ const validToChange = plan.nodeValidityEnds.get(identity);
4967
6684
  writes.push({
4968
6685
  identity,
4969
6686
  kind: modification.node.kind,
4970
6687
  id: modification.node.id,
4971
6688
  modification: modification.node,
4972
- ...validTo === void 0 ? {} : { validTo }
6689
+ ...validToChange?.kind === "set" ? { validTo: validToChange.validTo } : validToChange?.kind === "clear" ? { clearValidTo: true } : {}
4973
6690
  });
4974
6691
  }
4975
6692
  for (const entity of plan.canonicalEntities) {
@@ -4980,7 +6697,7 @@ function plannedNodeWrites(plan) {
4980
6697
  const kind = plan.retypeMap.get(sourceIdentity) ?? entity.kind;
4981
6698
  const modification = modificationsByIdentity.get(sourceIdentity);
4982
6699
  written.add(sourceIdentity);
4983
- const validTo = entity.validTo ?? plan.nodeValidityEnds.get(sourceIdentity);
6700
+ const validToChange = plan.nodeValidityEnds.get(sourceIdentity);
4984
6701
  writes.push({
4985
6702
  identity: mergeKey(kind, entity.canonicalId),
4986
6703
  kind,
@@ -4988,7 +6705,7 @@ function plannedNodeWrites(plan) {
4988
6705
  entity,
4989
6706
  ...modification === void 0 ? {} : { modification },
4990
6707
  ...entity.validFrom === void 0 ? {} : { validFrom: entity.validFrom },
4991
- ...validTo === void 0 ? {} : { validTo }
6708
+ ...typeof entity.validTo === "string" ? { validTo: entity.validTo } : validToChange?.kind === "set" ? { validTo: validToChange.validTo } : validToChange?.kind === "clear" ? { clearValidTo: true } : {}
4992
6709
  });
4993
6710
  }
4994
6711
  for (const identity of [...plan.nodeValidityEnds.keys()].sort(
@@ -4998,11 +6715,12 @@ function plannedNodeWrites(plan) {
4998
6715
  continue;
4999
6716
  }
5000
6717
  const kind = plan.retypeMap.get(identity) ?? kindOf(identity);
6718
+ const validToChange = requireDefined(plan.nodeValidityEnds.get(identity));
5001
6719
  writes.push({
5002
6720
  identity: mergeKey(kind, idOf(identity)),
5003
6721
  kind,
5004
6722
  id: idOf(identity),
5005
- validTo: requireDefined(plan.nodeValidityEnds.get(identity))
6723
+ ...validToChange.kind === "set" ? { validTo: validToChange.validTo } : { clearValidTo: true }
5006
6724
  });
5007
6725
  }
5008
6726
  return writes;
@@ -5013,67 +6731,181 @@ function nodeWriteProps(write, modificationProps) {
5013
6731
  ...write.modification === void 0 ? void 0 : modificationProps(write.modification)
5014
6732
  };
5015
6733
  }
5016
- async function applyMergePlan(plan, nodesApi, edgesApi, target, txBackend) {
5017
- const committedNodeIds = /* @__PURE__ */ new Set();
5018
- for (const [identity, kind] of plan.nodeDeletions) {
5019
- const collection = nodeCollection(nodesApi, kind);
5020
- await collection.delete(idOf(identity));
6734
+ function nodeWriteWindowOptions(write) {
6735
+ const validFrom = write.validFrom === void 0 ? {} : { validFrom: write.validFrom };
6736
+ if (write.clearValidTo === true) {
6737
+ return { ...validFrom, clearValidTo: true };
5021
6738
  }
5022
- for (const write of plannedNodeWrites(plan)) {
5023
- const collection = nodeCollection(nodesApi, write.kind);
5024
- await collection.upsertByIdFromRecord(
5025
- write.id,
5026
- nodeWriteProps(
5027
- write,
5028
- (modification) => commitModificationProps(modification.baseProps, modification.forkProps)
5029
- ),
5030
- {
5031
- ...write.validFrom === void 0 ? {} : { validFrom: write.validFrom },
5032
- ...write.validTo === void 0 ? {} : { validTo: write.validTo }
5033
- }
6739
+ return {
6740
+ ...validFrom,
6741
+ ...write.validTo === void 0 ? {} : { validTo: write.validTo }
6742
+ };
6743
+ }
6744
+ async function applyNodeRows(target, txBackend, nodesApi, deletions, upserts) {
6745
+ const afterImages = /* @__PURE__ */ new Map();
6746
+ const upsertsByKind = /* @__PURE__ */ new Map();
6747
+ for (const upsert of upserts) {
6748
+ const items = upsertsByKind.get(upsert.kind);
6749
+ if (items === void 0) upsertsByKind.set(upsert.kind, [upsert]);
6750
+ else items.push(upsert);
6751
+ }
6752
+ for (const [kind, items] of upsertsByKind) {
6753
+ const currentRows = await nodeCollection(nodesApi, kind).getByIds(
6754
+ items.map((item) => item.id),
6755
+ INCLUDE_TOMBSTONES
5034
6756
  );
5035
- committedNodeIds.add(write.identity);
5036
- }
5037
- for (const [identity, kind] of plan.edgeDeletions) {
5038
- await edgeCollection(edgesApi, kind).delete(idOf(identity));
6757
+ const currentById = /* @__PURE__ */ new Map();
6758
+ for (const node of currentRows) {
6759
+ if (node !== void 0) currentById.set(node.id, nodeProps2(node));
6760
+ }
6761
+ for (const item of items) {
6762
+ const props = createDataKeyedBag();
6763
+ const unset = new Set(
6764
+ Object.entries(item.props).filter(([, value]) => value === void 0).map(([key]) => key)
6765
+ );
6766
+ for (const [key, value] of Object.entries(
6767
+ currentById.get(item.id) ?? {}
6768
+ )) {
6769
+ if (!unset.has(key)) props[key] = value;
6770
+ }
6771
+ for (const [key, value] of Object.entries(item.props)) {
6772
+ if (value !== void 0) props[key] = value;
6773
+ }
6774
+ afterImages.set(mergeKey(kind, item.id), props);
6775
+ }
5039
6776
  }
5040
- const edgesByKind = /* @__PURE__ */ new Map();
5041
- for (const edge of plan.mergedEdges) {
5042
- const edgeBaseProps = plan.inheritedEdgeBaseProps.get(edge.id);
5043
- const props = edgeBaseProps === void 0 ? edge.props : commitModificationProps(edgeBaseProps, edge.props);
5044
- const item = {
5045
- id: edge.id,
5046
- from: finalEdgeEndpoint(plan, edge.fromKind, edge.fromId),
5047
- to: finalEdgeEndpoint(plan, edge.toKind, edge.toId),
5048
- props,
5049
- ...edge.validFrom === void 0 ? {} : { validFrom: edge.validFrom },
5050
- ...edge.validTo === void 0 ? {} : { validTo: edge.validTo }
5051
- };
5052
- const existing = edgesByKind.get(edge.kind);
5053
- if (existing === void 0) {
5054
- edgesByKind.set(edge.kind, [item]);
5055
- } else {
5056
- existing.push(item);
6777
+ return storeRuntime(target).applyResolvedNodeUniqueness(
6778
+ txBackend,
6779
+ {
6780
+ upserts: upserts.map((upsert) => ({
6781
+ kind: upsert.kind,
6782
+ id: upsert.id,
6783
+ props: requireDefined(
6784
+ afterImages.get(mergeKey(upsert.kind, upsert.id))
6785
+ )
6786
+ })),
6787
+ releases: deletions
6788
+ },
6789
+ async () => {
6790
+ for (const deletion of deletions) {
6791
+ await nodeCollection(nodesApi, deletion.kind).delete(deletion.id);
6792
+ }
6793
+ const committed = /* @__PURE__ */ new Set();
6794
+ for (const upsert of upserts) {
6795
+ await nodeCollection(nodesApi, upsert.kind).upsertByIdFromRecord(
6796
+ upsert.id,
6797
+ upsert.props,
6798
+ nodeWriteWindowOptions(upsert)
6799
+ );
6800
+ committed.add(mergeKey(upsert.kind, upsert.id));
6801
+ }
6802
+ return committed.size;
5057
6803
  }
6804
+ );
6805
+ }
6806
+ async function applyEdgeRows(edgesApi, deletions, upserts) {
6807
+ for (const deletion of deletions) {
6808
+ await edgeCollection(edgesApi, deletion.kind).delete(deletion.id);
6809
+ }
6810
+ const byKind = /* @__PURE__ */ new Map();
6811
+ for (const upsert of upserts) {
6812
+ const items = byKind.get(upsert.kind);
6813
+ if (items === void 0) byKind.set(upsert.kind, [upsert.item]);
6814
+ else items.push(upsert.item);
5058
6815
  }
5059
- let committedEdges = 0;
5060
- for (const [kind, items] of edgesByKind) {
6816
+ let committed = 0;
6817
+ for (const [kind, items] of byKind) {
5061
6818
  await edgeCollection(edgesApi, kind).bulkUpsertById(items);
5062
- committedEdges += items.length;
6819
+ committed += items.length;
5063
6820
  }
5064
- let appliedIdentity;
6821
+ return committed;
6822
+ }
6823
+ async function applyIdentityRows(target, txBackend, assertions, retractions, assertConsistent) {
5065
6824
  try {
5066
- appliedIdentity = await storeRuntime(target).applyIdentityMergeAtTarget(
6825
+ const applied = await storeRuntime(target).applyIdentityMergeAtTarget(
5067
6826
  txBackend,
5068
- plan.identityRetractions.map((retraction) => retraction.id),
5069
- plan.identityAssertions
6827
+ retractions,
6828
+ assertions
5070
6829
  );
5071
- await assertMergedIdentityClassesConsistent(target, txBackend, plan);
6830
+ if (assertConsistent !== void 0) await assertConsistent();
6831
+ return { asserted: applied.created, retracted: applied.retracted };
5072
6832
  } catch (error) {
5073
6833
  throw translateIdentityCommitError(error);
5074
6834
  }
5075
- return {
5076
- nodes: committedNodeIds.size,
6835
+ }
6836
+ async function applyInternalMergePlan(plan, nodesApi, edgesApi, target, txBackend) {
6837
+ const nodeDeletions = [...plan.nodeDeletions].map(([identity, kind]) => ({
6838
+ kind,
6839
+ id: idOf(identity)
6840
+ }));
6841
+ const nodeUpserts = plannedNodeWrites(plan).map((write) => ({
6842
+ kind: write.kind,
6843
+ id: write.id,
6844
+ props: nodeWriteProps(
6845
+ write,
6846
+ (modification) => commitModificationProps(modification.baseProps, modification.forkProps)
6847
+ ),
6848
+ ...write.validFrom === void 0 ? {} : { validFrom: write.validFrom },
6849
+ ...write.clearValidTo === true ? { clearValidTo: true } : write.validTo === void 0 ? {} : { validTo: write.validTo }
6850
+ }));
6851
+ const validityEndedNodes = new Set(
6852
+ nodeUpserts.filter((write) => "validTo" in write).map((write) => mergeKey(write.kind, write.id))
6853
+ );
6854
+ const earlyIdentityRetractions = plan.identityRetractions.filter(
6855
+ (retraction) => validityEndedNodes.has(mergeKeyOf(retraction.a)) || validityEndedNodes.has(mergeKeyOf(retraction.b))
6856
+ );
6857
+ const earlyIdentity = await applyIdentityRows(
6858
+ target,
6859
+ txBackend,
6860
+ [],
6861
+ earlyIdentityRetractions
6862
+ );
6863
+ let committedNodes;
6864
+ try {
6865
+ committedNodes = await applyNodeRows(
6866
+ target,
6867
+ txBackend,
6868
+ nodesApi,
6869
+ nodeDeletions,
6870
+ nodeUpserts
6871
+ );
6872
+ } catch (error) {
6873
+ throw error instanceof IdentityEndpointValidityError ? translateIdentityCommitError(error) : error;
6874
+ }
6875
+ const edgeDeletions = [...plan.edgeDeletions].map(([identity, kind]) => ({
6876
+ kind,
6877
+ id: idOf(identity)
6878
+ }));
6879
+ const edgeUpserts = [];
6880
+ for (const edge of plan.mergedEdges) {
6881
+ const edgeBaseProps = plan.inheritedEdgeBaseProps.get(edge.id);
6882
+ const props = edgeBaseProps === void 0 ? edge.props : commitModificationProps(edgeBaseProps, edge.props);
6883
+ edgeUpserts.push({
6884
+ kind: edge.kind,
6885
+ item: {
6886
+ id: edge.id,
6887
+ from: finalEdgeEndpoint(plan, edge.fromKind, edge.fromId),
6888
+ to: finalEdgeEndpoint(plan, edge.toKind, edge.toId),
6889
+ props,
6890
+ ...edge.validFrom === void 0 ? {} : { validFrom: edge.validFrom },
6891
+ ...edge.clearValidTo === true ? { clearValidTo: true } : edge.validTo === void 0 ? {} : { validTo: edge.validTo }
6892
+ }
6893
+ });
6894
+ }
6895
+ const committedEdges = await applyEdgeRows(
6896
+ edgesApi,
6897
+ edgeDeletions,
6898
+ edgeUpserts
6899
+ );
6900
+ const appliedIdentity = await applyIdentityRows(
6901
+ target,
6902
+ txBackend,
6903
+ plan.identityAssertions,
6904
+ plan.identityRetractions,
6905
+ () => assertMergedIdentityClassesConsistent(target, txBackend, plan)
6906
+ );
6907
+ return {
6908
+ nodes: committedNodes,
5077
6909
  edges: committedEdges,
5078
6910
  // ACTUAL ledger effects from the applier: rows created (idempotent
5079
6911
  // exact/pair matches the target already held are excluded — the normal
@@ -5082,8 +6914,8 @@ async function applyMergePlan(plan, nodesApi, edgesApi, target, txBackend) {
5082
6914
  // failure aborts the whole merge, so the counts never describe a
5083
6915
  // partial commit.
5084
6916
  identity: {
5085
- asserted: appliedIdentity.created,
5086
- retracted: appliedIdentity.retracted
6917
+ asserted: appliedIdentity.asserted,
6918
+ retracted: earlyIdentity.retracted + appliedIdentity.retracted
5087
6919
  }
5088
6920
  };
5089
6921
  }
@@ -5094,26 +6926,39 @@ async function commitPlan(target, plan, expectedBaseVersion) {
5094
6926
  { details: { capability: "transactions" } }
5095
6927
  );
5096
6928
  }
5097
- return withTxConflictRetry(
5098
- () => target.transaction(async (tx) => {
5099
- if (expectedBaseVersion !== void 0) {
5100
- await assertTargetUnchanged(
6929
+ return runMergeCommit(
6930
+ () => withTxConflictRetry(
6931
+ () => target.transaction(async (tx) => {
6932
+ if (expectedBaseVersion !== void 0) {
6933
+ await assertTargetUnchanged(
6934
+ transactionBackend(tx),
6935
+ target,
6936
+ expectedBaseVersion
6937
+ );
6938
+ }
6939
+ await assertPlannedIdentityIdsFresh(
6940
+ target,
5101
6941
  transactionBackend(tx),
6942
+ plan
6943
+ );
6944
+ return applyInternalMergePlan(
6945
+ plan,
6946
+ tx.nodes,
6947
+ tx.edges,
5102
6948
  target,
5103
- expectedBaseVersion
6949
+ transactionBackend(tx)
5104
6950
  );
5105
- }
5106
- await assertPlannedIdentityIdsFresh(target, transactionBackend(tx), plan);
5107
- return applyMergePlan(
5108
- plan,
5109
- tx.nodes,
5110
- tx.edges,
5111
- target,
5112
- transactionBackend(tx)
5113
- );
5114
- }, mergeCommitTransactionOptions(target))
6951
+ }, mergeCommitTransactionOptions(target))
6952
+ )
5115
6953
  );
5116
6954
  }
6955
+ async function runMergeCommit(commit) {
6956
+ try {
6957
+ return await commit();
6958
+ } catch (error) {
6959
+ throw translateMergeCommitError(error);
6960
+ }
6961
+ }
5117
6962
  var MERGE_COMMIT_TX_OPTIONS = {
5118
6963
  isolationLevel: "serializable"
5119
6964
  };
@@ -5250,7 +7095,169 @@ async function tryOpenProvenanceStore(target) {
5250
7095
  );
5251
7096
  }
5252
7097
  }
5253
- async function resolveMerge(store, target, branches, options, useBaseSources, incremental, expectedBaseVersion) {
7098
+ function assertPublicPlanCapability(target) {
7099
+ if (!storeBackend(target).capabilities.transactions) {
7100
+ throw new MergePlanCapabilityError(
7101
+ "Public merge plans require a transaction-capable target backend.",
7102
+ { details: { capability: "transactions" } }
7103
+ );
7104
+ }
7105
+ if (!target.revisionTrackingEnabled) {
7106
+ throw new MergePlanCapabilityError(
7107
+ "Public merge plans require durable target revision tracking.",
7108
+ {
7109
+ details: { capability: "revisionTracking" },
7110
+ suggestion: "Create the target Store with { revisionTracking: true } (or history enabled), then create a new plan."
7111
+ }
7112
+ );
7113
+ }
7114
+ }
7115
+ async function captureMergePlanTargetFence(target) {
7116
+ assertPublicPlanCapability(target);
7117
+ const [activeSchema, schemaHash, origin, revision] = await Promise.all([
7118
+ storeBackend(target).getActiveSchema(target.graphId),
7119
+ computeSchemaComponent(target),
7120
+ target.revisionOriginNow(),
7121
+ target.revisionNow()
7122
+ ]);
7123
+ return {
7124
+ graphId: target.graphId,
7125
+ schema: {
7126
+ managed: activeSchema !== void 0,
7127
+ version: activeSchema?.version ?? 1,
7128
+ hash: activeSchema?.schema_hash ?? schemaHash
7129
+ },
7130
+ revision: { origin, revision: revision ?? null }
7131
+ };
7132
+ }
7133
+ function sameMergePlanTargetFence(left, right) {
7134
+ return left.graphId === right.graphId && left.schema.managed === right.schema.managed && left.schema.version === right.schema.version && left.schema.hash === right.schema.hash && left.revision.origin === right.revision.origin && left.revision.revision === right.revision.revision;
7135
+ }
7136
+ async function assertPlanningFenceUnchanged(target, startingFence) {
7137
+ const endingFence = await captureMergePlanTargetFence(target);
7138
+ if (sameMergePlanTargetFence(startingFence, endingFence)) return;
7139
+ throw new MergePlanningStaleError(
7140
+ "The merge target changed while the plan was being computed; no reviewable plan was produced.",
7141
+ { details: { startingFence, endingFence } }
7142
+ );
7143
+ }
7144
+ function splitWireProps(props) {
7145
+ const setProps = createDataKeyedBag();
7146
+ const unsetProps = [];
7147
+ for (const key of Object.keys(props).sort(compareStrings)) {
7148
+ const value = props[key];
7149
+ if (value === void 0) {
7150
+ unsetProps.push(key);
7151
+ } else {
7152
+ setProps[key] = value;
7153
+ }
7154
+ }
7155
+ return { setProps, unsetProps };
7156
+ }
7157
+ function wireEntityRef(kind, id) {
7158
+ return { kind, id };
7159
+ }
7160
+ function resolvedNodeUpserts(plan) {
7161
+ return plannedNodeWrites(plan).map((write) => ({
7162
+ kind: write.kind,
7163
+ id: write.id,
7164
+ ...splitWireProps(
7165
+ nodeWriteProps(
7166
+ write,
7167
+ (modification) => commitModificationProps(modification.baseProps, modification.forkProps)
7168
+ )
7169
+ ),
7170
+ ...write.validFrom === void 0 ? {} : { validFrom: write.validFrom },
7171
+ ...write.validTo === void 0 ? {} : { validTo: write.validTo }
7172
+ }));
7173
+ }
7174
+ async function resolvedMergeArtifact(resolved, mode, targetFence, anchors) {
7175
+ const { plan, options } = resolved;
7176
+ const nodeUpserts = resolvedNodeUpserts(plan);
7177
+ const edgeUpserts = plan.mergedEdges.map((edge) => {
7178
+ const baseProps = plan.inheritedEdgeBaseProps.get(edge.id);
7179
+ const props = baseProps === void 0 ? edge.props : commitModificationProps(baseProps, edge.props);
7180
+ return {
7181
+ kind: edge.kind,
7182
+ id: edge.id,
7183
+ from: finalEdgeEndpoint(plan, edge.fromKind, edge.fromId),
7184
+ to: finalEdgeEndpoint(plan, edge.toKind, edge.toId),
7185
+ ...splitWireProps(props),
7186
+ ...edge.validFrom === void 0 ? {} : { validFrom: edge.validFrom },
7187
+ ...edge.validTo === void 0 ? {} : { validTo: edge.validTo }
7188
+ };
7189
+ });
7190
+ const input = {
7191
+ formatVersion: 1,
7192
+ mode,
7193
+ target: targetFence,
7194
+ anchors,
7195
+ proposed: {
7196
+ nodes: {
7197
+ upserts: nodeUpserts.length,
7198
+ deletions: plan.nodeDeletions.size
7199
+ },
7200
+ edges: {
7201
+ upserts: edgeUpserts.length,
7202
+ deletions: plan.edgeDeletions.size
7203
+ },
7204
+ identity: {
7205
+ assertions: plan.identityAssertions.length,
7206
+ retractions: plan.identityRetractions.length
7207
+ }
7208
+ },
7209
+ writes: {
7210
+ nodeDeletes: [...plan.nodeDeletions].map(
7211
+ ([identity, kind]) => wireEntityRef(kind, idOf(identity))
7212
+ ),
7213
+ nodeUpserts,
7214
+ edgeDeletes: [...plan.edgeDeletions].map(
7215
+ ([identity, kind]) => wireEntityRef(kind, idOf(identity))
7216
+ ),
7217
+ edgeUpserts,
7218
+ identityAssertions: plan.identityAssertions,
7219
+ identityRetractions: plan.identityRetractions
7220
+ },
7221
+ guards: {
7222
+ canonicalMappings: [...plan.canonicalOf].sort(([left], [right]) => compareMergeKeys(left, right)).map(([member, canonical]) => ({
7223
+ member: wireEntityRef(kindOf(member), idOf(member)),
7224
+ canonical: wireEntityRef(kindOf(canonical), idOf(canonical))
7225
+ })),
7226
+ retypes: [...plan.retypeMap].sort(([left], [right]) => compareMergeKeys(left, right)).map(([entity, toKind]) => ({
7227
+ entity: wireEntityRef(kindOf(entity), idOf(entity)),
7228
+ toKind
7229
+ })),
7230
+ deletedNodes: [...plan.nodeDeletions].map(
7231
+ ([identity, kind]) => wireEntityRef(kind, idOf(identity))
7232
+ ),
7233
+ ...resolved.incrementalGuard === void 0 ? {} : {
7234
+ incremental: {
7235
+ tombstoneResurrection: "refuse",
7236
+ lossyUpdates: "refuse",
7237
+ edgeIdentity: "preserve"
7238
+ }
7239
+ }
7240
+ },
7241
+ review: {
7242
+ resolutions: plan.resolutions,
7243
+ conflicts: plan.propertyConflicts,
7244
+ deleteModifyConflicts: plan.deleteModifyConflicts,
7245
+ typeReconciliations: plan.typeReconciliations,
7246
+ dropped: plan.dropped,
7247
+ validityEnds: plan.validityEnds,
7248
+ baseAmbiguities: plan.baseAmbiguities,
7249
+ provenanceRecords: plan.provenanceRecords,
7250
+ warnings: plan.warnings,
7251
+ ...plan.candidateDiagnostics === void 0 ? {} : { diagnostics: plan.candidateDiagnostics }
7252
+ },
7253
+ provenance: {
7254
+ includeInReport: options.provenance,
7255
+ persist: options.persistProvenance
7256
+ }
7257
+ };
7258
+ return constructMergePlanArtifact(input);
7259
+ }
7260
+ async function resolveMerge(store, target, branches, options, useBaseSources, incremental, expectedBaseVersion, complete) {
5254
7261
  let targetBranchSeen = false;
5255
7262
  for (const branch2 of branches) {
5256
7263
  if (branch2.id === BASE_PROVENANCE_BRANCH) {
@@ -5303,10 +7310,6 @@ async function resolveMerge(store, target, branches, options, useBaseSources, in
5303
7310
  );
5304
7311
  }
5305
7312
  }
5306
- const provenanceStore = options.persistProvenance ? await tryOpenProvenanceStore(target) : void 0;
5307
- if (provenanceStore !== void 0 && isErr(provenanceStore)) {
5308
- return provenanceStore;
5309
- }
5310
7313
  try {
5311
7314
  const preferredBranchId = incremental?.targetBranchId;
5312
7315
  const staging = await stageBranches(store, branches, preferredBranchId);
@@ -5375,10 +7378,12 @@ async function resolveMerge(store, target, branches, options, useBaseSources, in
5375
7378
  sameIdAcrossKinds: target.graph.identity?.sameIdAcrossKinds,
5376
7379
  areDisjoint: (left, right) => target.registry.areDisjoint(left, right)
5377
7380
  };
5378
- const plan = planMerge(
7381
+ const plan = buildInternalMergePlan(
5379
7382
  staging,
5380
7383
  candidates.data.edges,
5381
7384
  candidates.data.warnings,
7385
+ candidates.data.diagnostics,
7386
+ candidates.data.diagnosticsTotal,
5382
7387
  candidates.data.baseMembers,
5383
7388
  options,
5384
7389
  branchRank,
@@ -5434,29 +7439,731 @@ async function resolveMerge(store, target, branches, options, useBaseSources, in
5434
7439
  );
5435
7440
  identityGuard = built.probe;
5436
7441
  }
5437
- const merged = incremental === void 0 ? await commitPlan(target, plan, expectedBaseVersion) : await commitIncrementalPlan(target, plan, {
7442
+ const incrementalGuard = incremental === void 0 ? void 0 : {
5438
7443
  stagedNewByKind,
5439
7444
  options,
5440
7445
  introspectionKinds,
5441
7446
  forkPoint: incremental.forkPoint,
5442
7447
  ...identityGuard === void 0 ? {} : { identityPeerProbe: identityGuard },
5443
- // The committed (kind, id) keys the base sources matched at PLAN time;
5444
- // anything NEW the in-tx re-probe surfaces is a window write.
5445
7448
  plannedBaseMatchKeys: new Set(
5446
7449
  candidates.data.baseMembers.map((member) => mergeKeyOf(member))
5447
7450
  ),
5448
7451
  targetNodeVersions: staging.targetNodeVersions,
5449
7452
  targetEdgeSignatures: staging.targetEdgeSignatures
5450
- });
5451
- const provenance = options.provenance ? buildProvenanceIndex(plan.provenanceRecords) : { byBranch: () => ({ nodeIds: [], edgeIds: [] }) };
5452
- const warnings = [...plan.warnings];
7453
+ };
7454
+ return ok(
7455
+ await complete({
7456
+ target,
7457
+ plan,
7458
+ options,
7459
+ ...expectedBaseVersion === void 0 ? {} : { expectedBaseVersion },
7460
+ ...incrementalGuard === void 0 ? {} : { incrementalGuard }
7461
+ })
7462
+ );
7463
+ } catch (error) {
7464
+ if (error instanceof MergeError) {
7465
+ return err(error);
7466
+ }
7467
+ return err(
7468
+ new MergeError(
7469
+ `Merge failed while staging or committing: ${describeCause(error)}`,
7470
+ { cause: error }
7471
+ )
7472
+ );
7473
+ }
7474
+ }
7475
+ async function commitResolvedMerge(resolved) {
7476
+ const { target, plan, options } = resolved;
7477
+ const provenanceStore = options.persistProvenance ? await tryOpenProvenanceStore(target) : void 0;
7478
+ if (provenanceStore !== void 0 && isErr(provenanceStore)) {
7479
+ throw provenanceStore.error;
7480
+ }
7481
+ const merged = resolved.incrementalGuard === void 0 ? await commitPlan(target, plan, resolved.expectedBaseVersion) : await commitIncrementalPlan(target, plan, resolved.incrementalGuard);
7482
+ const provenance = options.provenance ? buildProvenanceIndex(plan.provenanceRecords) : { byBranch: () => ({ nodeIds: [], edgeIds: [] }) };
7483
+ const warnings = [...plan.warnings];
7484
+ let provenancePersisted;
7485
+ if (provenanceStore !== void 0 && !isErr(provenanceStore)) {
7486
+ try {
7487
+ const count = await persistProvenanceRecords(
7488
+ provenanceStore.data,
7489
+ target.graphId,
7490
+ plan.provenanceRecords
7491
+ );
7492
+ provenancePersisted = {
7493
+ graphId: provenanceGraphId(target.graphId),
7494
+ count
7495
+ };
7496
+ } catch (error) {
7497
+ warnings.push(
7498
+ "provenance persistence failed (graph committed; provenance not persisted): " + (error instanceof Error ? error.message : String(error))
7499
+ );
7500
+ }
7501
+ }
7502
+ return {
7503
+ merged,
7504
+ resolutions: plan.resolutions,
7505
+ conflicts: plan.propertyConflicts,
7506
+ deleteModifyConflicts: plan.deleteModifyConflicts,
7507
+ typeReconciliations: plan.typeReconciliations,
7508
+ dropped: plan.dropped,
7509
+ validityEnds: plan.validityEnds,
7510
+ baseAmbiguities: plan.baseAmbiguities,
7511
+ provenance,
7512
+ warnings,
7513
+ ...plan.candidateDiagnostics === void 0 ? {} : { candidateDiagnostics: plan.candidateDiagnostics },
7514
+ ...provenancePersisted === void 0 ? {} : { provenancePersisted }
7515
+ };
7516
+ }
7517
+ function incrementalBaseConflictPolicyError(options) {
7518
+ return new InvalidMergeOptionsError(
7519
+ 'mergeIncremental() requires onBasePropertyConflict: "flag" (keep-base); a non-keep-base policy could overwrite a newer committed base value with a stale branch value.',
7520
+ {
7521
+ details: {
7522
+ option: "onBasePropertyConflict",
7523
+ accepted: "flag",
7524
+ received: options.onBasePropertyConflict
7525
+ }
7526
+ }
7527
+ );
7528
+ }
7529
+ function incrementalSchemaError() {
7530
+ return new MergeError(
7531
+ "mergeIncremental() requires target and forkPoint to share a schema; schema drift is not supported (the target content may differ \u2014 only the schema must match).",
7532
+ { details: {} }
7533
+ );
7534
+ }
7535
+ async function planMerge(store, branchInputs, optionsInput = {}) {
7536
+ const branches = unwrapMergeBranches(branchInputs);
7537
+ const normalized = tryNormalize(optionsInput);
7538
+ if (isErr(normalized)) return err(normalized.error);
7539
+ const options = normalized.data;
7540
+ const target = options.target ?? store;
7541
+ let targetFence;
7542
+ try {
7543
+ targetFence = await captureMergePlanTargetFence(target);
7544
+ } catch (error) {
7545
+ return err(
7546
+ error instanceof MergeError ? error : new MergePlanCapabilityError(
7547
+ `Unable to capture the target's durable plan fence: ${describeCause(error)}`,
7548
+ { cause: error }
7549
+ )
7550
+ );
7551
+ }
7552
+ const precondition = await validateBaseVersions(target, branches);
7553
+ if (isErr(precondition)) return err(precondition.error);
7554
+ const anchors = {
7555
+ kind: "snapshot",
7556
+ base: { graphId: store.graphId, baseVersion: precondition.data },
7557
+ branches: [...branches].sort((left, right) => compareStrings(left.id, right.id)).map((branch2) => ({
7558
+ branchId: branch2.id,
7559
+ baseVersion: branch2.base
7560
+ }))
7561
+ };
7562
+ return resolveMerge(
7563
+ store,
7564
+ target,
7565
+ branches,
7566
+ options,
7567
+ false,
7568
+ void 0,
7569
+ precondition.data,
7570
+ async (resolved) => {
7571
+ await assertPlanningFenceUnchanged(target, targetFence);
7572
+ return resolvedMergeArtifact(resolved, "snapshot", targetFence, anchors);
7573
+ }
7574
+ );
7575
+ }
7576
+ async function planMergeIncremental(args) {
7577
+ const { forkPoint, target } = args;
7578
+ const branches = unwrapMergeBranches(args.branches);
7579
+ const normalized = tryNormalize(args.options ?? {}, ["target"]);
7580
+ if (isErr(normalized)) return err(normalized.error);
7581
+ const options = normalized.data;
7582
+ if (options.onBasePropertyConflict !== "flag") {
7583
+ return err(incrementalBaseConflictPolicyError(options));
7584
+ }
7585
+ let targetFence;
7586
+ try {
7587
+ targetFence = await captureMergePlanTargetFence(target);
7588
+ } catch (error) {
7589
+ return err(
7590
+ error instanceof MergeError ? error : new MergePlanCapabilityError(
7591
+ `Unable to capture the target's durable plan fence: ${describeCause(error)}`,
7592
+ { cause: error }
7593
+ )
7594
+ );
7595
+ }
7596
+ const forkPrecondition = await validateForkPointVersions(forkPoint, branches);
7597
+ if (isErr(forkPrecondition)) return err(forkPrecondition.error);
7598
+ const forkVersion = forkPrecondition.data;
7599
+ const [forkSchema, targetSchema] = await Promise.all([
7600
+ computeSchemaComponent(forkPoint),
7601
+ computeSchemaComponent(target)
7602
+ ]);
7603
+ if (forkSchema !== targetSchema) return err(incrementalSchemaError());
7604
+ const forkActiveSchema = await storeBackend(forkPoint).getActiveSchema(
7605
+ forkPoint.graphId
7606
+ );
7607
+ const targetBranch = {
7608
+ id: COMMITTED_TARGET_BRANCH,
7609
+ base: forkVersion,
7610
+ store: target
7611
+ };
7612
+ const anchors = {
7613
+ kind: "incremental",
7614
+ forkPoint: {
7615
+ graphId: forkPoint.graphId,
7616
+ baseVersion: forkVersion,
7617
+ schema: {
7618
+ managed: forkActiveSchema !== void 0,
7619
+ version: forkActiveSchema?.version ?? 1,
7620
+ hash: forkActiveSchema?.schema_hash ?? forkSchema
7621
+ }
7622
+ },
7623
+ branches: [...branches].sort((left, right) => compareStrings(left.id, right.id)).map((branch2) => ({
7624
+ branchId: branch2.id,
7625
+ baseVersion: branch2.base
7626
+ }))
7627
+ };
7628
+ return resolveMerge(
7629
+ forkPoint,
7630
+ target,
7631
+ [targetBranch, ...branches],
7632
+ options,
7633
+ true,
7634
+ {
7635
+ targetBranchId: COMMITTED_TARGET_BRANCH,
7636
+ forkPoint: { store: forkPoint, version: forkVersion }
7637
+ },
7638
+ void 0,
7639
+ async (resolved) => {
7640
+ await assertPlanningFenceUnchanged(target, targetFence);
7641
+ await assertForkPointUnchanged({
7642
+ store: forkPoint,
7643
+ version: forkVersion
7644
+ });
7645
+ return resolvedMergeArtifact(
7646
+ resolved,
7647
+ "incremental",
7648
+ targetFence,
7649
+ anchors
7650
+ );
7651
+ }
7652
+ );
7653
+ }
7654
+ function mergePlanValidationError(failure) {
7655
+ switch (failure.kind) {
7656
+ case "unsupported-version": {
7657
+ return new UnsupportedMergePlanVersionError(
7658
+ `Unsupported merge plan format version ${String(failure.received)}.`,
7659
+ { details: { received: failure.received } }
7660
+ );
7661
+ }
7662
+ case "digest-mismatch": {
7663
+ return new MergePlanDigestMismatchError(
7664
+ "The merge plan digest does not match its canonical content.",
7665
+ { details: { expected: failure.expected, received: failure.received } }
7666
+ );
7667
+ }
7668
+ case "malformed": {
7669
+ return new InvalidMergePlanError("The merge plan is malformed.", {
7670
+ details: { issues: failure.issues }
7671
+ });
7672
+ }
7673
+ default: {
7674
+ const exhaustive = failure;
7675
+ return exhaustive;
7676
+ }
7677
+ }
7678
+ }
7679
+ function wireWriteProps(setProps, unsetProps) {
7680
+ const props = createDataKeyedBag();
7681
+ for (const [key, value] of Object.entries(setProps)) props[key] = value;
7682
+ for (const key of unsetProps) props[key] = void 0;
7683
+ return props;
7684
+ }
7685
+ function resolvedWireProps(current, setProps, unsetProps) {
7686
+ const props = createDataKeyedBag();
7687
+ const unset = new Set(unsetProps);
7688
+ for (const [key, value] of Object.entries(current)) {
7689
+ if (!unset.has(key)) props[key] = value;
7690
+ }
7691
+ for (const [key, value] of Object.entries(setProps)) props[key] = value;
7692
+ return props;
7693
+ }
7694
+ function invalidPlanForTarget(message, details) {
7695
+ return new InvalidMergePlanError(message, { details });
7696
+ }
7697
+ async function preflightWireMergeWrites(target, nodesApi, edgesApi, artifact) {
7698
+ const graphNodes = target.graph.nodes;
7699
+ const graphEdges = target.graph.edges;
7700
+ const assertNodeKind = (kind) => {
7701
+ if (!hasOwnKey(graphNodes, kind)) {
7702
+ throw invalidPlanForTarget(
7703
+ `The merge plan references unknown node kind "${kind}".`,
7704
+ { kind, role: "node" }
7705
+ );
7706
+ }
7707
+ };
7708
+ const assertEdgeKind = (kind) => {
7709
+ if (!hasOwnKey(graphEdges, kind) || graphEdges[kind] === void 0) {
7710
+ throw invalidPlanForTarget(
7711
+ `The merge plan references unknown edge kind "${kind}".`,
7712
+ { kind, role: "edge" }
7713
+ );
7714
+ }
7715
+ };
7716
+ for (const item of [
7717
+ ...artifact.writes.nodeDeletes,
7718
+ ...artifact.writes.nodeUpserts,
7719
+ ...artifact.guards.deletedNodes,
7720
+ ...artifact.guards.canonicalMappings.flatMap((mapping) => [
7721
+ mapping.member,
7722
+ mapping.canonical
7723
+ ]),
7724
+ ...artifact.guards.retypes.map((retype) => retype.entity),
7725
+ ...artifact.writes.identityAssertions.flatMap((assertion) => [
7726
+ assertion.a,
7727
+ assertion.b
7728
+ ]),
7729
+ ...artifact.writes.identityRetractions.flatMap((assertion) => [
7730
+ assertion.a,
7731
+ assertion.b
7732
+ ])
7733
+ ]) {
7734
+ assertNodeKind(item.kind);
7735
+ }
7736
+ for (const retype of artifact.guards.retypes) assertNodeKind(retype.toKind);
7737
+ for (const item of [
7738
+ ...artifact.writes.edgeDeletes,
7739
+ ...artifact.writes.edgeUpserts
7740
+ ]) {
7741
+ assertEdgeKind(item.kind);
7742
+ }
7743
+ const plannedNodes = new Set(
7744
+ artifact.writes.nodeUpserts.map(
7745
+ (upsert) => mergeKey(upsert.kind, upsert.id)
7746
+ )
7747
+ );
7748
+ const deletedNodes = new Set(
7749
+ artifact.writes.nodeDeletes.map(
7750
+ (deletion) => mergeKey(deletion.kind, deletion.id)
7751
+ )
7752
+ );
7753
+ const endpointRows = /* @__PURE__ */ new Map();
7754
+ const readEndpoint = async (endpoint) => {
7755
+ const key = mergeKey(endpoint.kind, endpoint.id);
7756
+ if (endpointRows.has(key)) return endpointRows.get(key);
7757
+ const row = (await nodeCollection(nodesApi, endpoint.kind).getByIds(
7758
+ [endpoint.id],
7759
+ INCLUDE_TOMBSTONES
7760
+ ))[0];
7761
+ endpointRows.set(key, row);
7762
+ return row;
7763
+ };
7764
+ const nodeUpsertsByKind = /* @__PURE__ */ new Map();
7765
+ for (const upsert of artifact.writes.nodeUpserts) {
7766
+ const upserts = nodeUpsertsByKind.get(upsert.kind) ?? [];
7767
+ upserts.push(upsert);
7768
+ nodeUpsertsByKind.set(upsert.kind, upserts);
7769
+ }
7770
+ const currentNodesByIdentity = /* @__PURE__ */ new Map();
7771
+ for (const [kind, upserts] of nodeUpsertsByKind) {
7772
+ const rows = await nodeCollection(nodesApi, kind).getByIds(
7773
+ upserts.map((upsert) => upsert.id),
7774
+ INCLUDE_TOMBSTONES
7775
+ );
7776
+ for (const [index, upsert] of upserts.entries()) {
7777
+ const row = rows[index];
7778
+ const identity = mergeKey(kind, upsert.id);
7779
+ currentNodesByIdentity.set(identity, row);
7780
+ endpointRows.set(identity, row);
7781
+ }
7782
+ }
7783
+ for (const upsert of artifact.writes.nodeUpserts) {
7784
+ const current = currentNodesByIdentity.get(
7785
+ mergeKey(upsert.kind, upsert.id)
7786
+ );
7787
+ if (artifact.guards.incremental !== void 0 && current?.meta.deletedAt !== void 0) {
7788
+ throw invalidPlanForTarget(
7789
+ `The incremental merge plan would resurrect soft-deleted committed node "${upsert.id}".`,
7790
+ { kind: upsert.kind, id: upsert.id, role: "node" }
7791
+ );
7792
+ }
7793
+ const props = resolvedWireProps(
7794
+ current === void 0 ? {} : nodeProps2(current),
7795
+ upsert.setProps,
7796
+ upsert.unsetProps
7797
+ );
7798
+ const schema = nodeSchemaFor(target, upsert.kind);
7799
+ if (schema !== void 0 && !schema.safeParse(props).success) {
7800
+ throw invalidPlanForTarget(
7801
+ `The merge plan's node write for "${upsert.id}" does not validate against the active schema.`,
7802
+ { kind: upsert.kind, id: upsert.id, role: "node" }
7803
+ );
7804
+ }
7805
+ if (artifact.guards.incremental !== void 0 && current !== void 0) {
7806
+ const analysis = analyzeRevalidatingWrite(
7807
+ schema,
7808
+ nodeProps2(current),
7809
+ wireWriteProps(upsert.setProps, upsert.unsetProps)
7810
+ );
7811
+ assertValidRevalidatingWrite(
7812
+ analysis,
7813
+ `The incremental merge plan found committed node "${upsert.id}" with props that do not validate against the active schema.`,
7814
+ { kind: upsert.kind, id: upsert.id, role: "node" }
7815
+ );
7816
+ if (analysis.stripsCurrentProps && analysis.schemaWouldChange) {
7817
+ throw invalidPlanForTarget(
7818
+ `The incremental merge plan would strip existing props while updating committed node "${upsert.id}".`,
7819
+ { kind: upsert.kind, id: upsert.id, role: "node" }
7820
+ );
7821
+ }
7822
+ }
7823
+ }
7824
+ const currentEdgeIds = /* @__PURE__ */ new Set();
7825
+ const edgeUpsertsByKind = /* @__PURE__ */ new Map();
7826
+ for (const upsert of artifact.writes.edgeUpserts) {
7827
+ const upserts = edgeUpsertsByKind.get(upsert.kind) ?? [];
7828
+ upserts.push(upsert);
7829
+ edgeUpsertsByKind.set(upsert.kind, upserts);
7830
+ }
7831
+ const currentEdgesByIdentity = /* @__PURE__ */ new Map();
7832
+ for (const [kind, upserts] of edgeUpsertsByKind) {
7833
+ const rows = await edgeCollection(edgesApi, kind).getByIds(
7834
+ upserts.map((upsert) => upsert.id),
7835
+ INCLUDE_TOMBSTONES
7836
+ );
7837
+ for (const [index, upsert] of upserts.entries()) {
7838
+ const row = rows[index];
7839
+ currentEdgesByIdentity.set(mergeKey(kind, upsert.id), row);
7840
+ if (row !== void 0) currentEdgeIds.add(upsert.id);
7841
+ }
7842
+ }
7843
+ for (const upsert of artifact.writes.edgeUpserts) {
7844
+ const registration = requireDefined(graphEdges[upsert.kind]);
7845
+ const endpointError = validateEdgeEndpoints(
7846
+ upsert.kind,
7847
+ upsert.from.kind,
7848
+ upsert.to.kind,
7849
+ registration,
7850
+ target.registry
7851
+ );
7852
+ if (endpointError !== void 0) {
7853
+ throw invalidPlanForTarget(endpointError.message, {
7854
+ kind: upsert.kind,
7855
+ id: upsert.id,
7856
+ role: "edge"
7857
+ });
7858
+ }
7859
+ for (const endpoint of [upsert.from, upsert.to]) {
7860
+ assertNodeKind(endpoint.kind);
7861
+ const key = mergeKey(endpoint.kind, endpoint.id);
7862
+ if (deletedNodes.has(key)) {
7863
+ throw invalidPlanForTarget(
7864
+ `The merge plan deletes an endpoint required by edge "${upsert.id}".`,
7865
+ { edgeKind: upsert.kind, edgeId: upsert.id, endpoint }
7866
+ );
7867
+ }
7868
+ if (plannedNodes.has(key)) continue;
7869
+ const row = await readEndpoint(endpoint);
7870
+ if (row === void 0 || row.meta.deletedAt !== void 0) {
7871
+ throw invalidPlanForTarget(
7872
+ `The merge plan references a missing edge endpoint "${endpoint.id}".`,
7873
+ { edgeKind: upsert.kind, edgeId: upsert.id, endpoint }
7874
+ );
7875
+ }
7876
+ }
7877
+ const current = currentEdgesByIdentity.get(
7878
+ mergeKey(upsert.kind, upsert.id)
7879
+ );
7880
+ if (artifact.guards.incremental !== void 0 && current?.meta.deletedAt !== void 0) {
7881
+ throw invalidPlanForTarget(
7882
+ `The incremental merge plan would resurrect soft-deleted committed edge "${upsert.id}".`,
7883
+ { kind: upsert.kind, id: upsert.id, role: "edge" }
7884
+ );
7885
+ }
7886
+ const props = resolvedWireProps(
7887
+ current === void 0 ? {} : edgeProps(current),
7888
+ upsert.setProps,
7889
+ upsert.unsetProps
7890
+ );
7891
+ const schema = edgeSchemaFor(target, upsert.kind);
7892
+ if (schema !== void 0 && !schema.safeParse(props).success) {
7893
+ throw invalidPlanForTarget(
7894
+ `The merge plan's edge write for "${upsert.id}" does not validate against the active schema.`,
7895
+ { kind: upsert.kind, id: upsert.id, role: "edge" }
7896
+ );
7897
+ }
7898
+ if (artifact.guards.incremental !== void 0 && current !== void 0) {
7899
+ const analysis = analyzeRevalidatingWrite(
7900
+ schema,
7901
+ edgeProps(current),
7902
+ wireWriteProps(upsert.setProps, upsert.unsetProps)
7903
+ );
7904
+ assertValidRevalidatingWrite(
7905
+ analysis,
7906
+ `The incremental merge plan found committed edge "${upsert.id}" with props that do not validate against the active schema.`,
7907
+ { kind: upsert.kind, id: upsert.id, role: "edge" }
7908
+ );
7909
+ if (analysis.stripsCurrentProps && analysis.schemaWouldChange) {
7910
+ throw invalidPlanForTarget(
7911
+ `The incremental merge plan would strip existing props while updating committed edge "${upsert.id}".`,
7912
+ { kind: upsert.kind, id: upsert.id, role: "edge" }
7913
+ );
7914
+ }
7915
+ }
7916
+ if (current !== void 0 && (current.fromKind !== upsert.from.kind || current.fromId !== upsert.from.id || current.toKind !== upsert.to.kind || current.toId !== upsert.to.id)) {
7917
+ throw invalidPlanForTarget(
7918
+ `The merge plan would change the immutable endpoints of edge "${upsert.id}".`,
7919
+ { kind: upsert.kind, id: upsert.id, role: "edge" }
7920
+ );
7921
+ }
7922
+ }
7923
+ const missingEdgeIds = artifact.writes.edgeUpserts.map((upsert) => upsert.id).filter((id) => !currentEdgeIds.has(id));
7924
+ if (missingEdgeIds.length > 0) {
7925
+ const plannedEdgeById = new Map(
7926
+ artifact.writes.edgeUpserts.map((upsert) => [upsert.id, upsert])
7927
+ );
7928
+ for (const kind of Object.keys(target.graph.edges)) {
7929
+ const rows = await edgeCollection(edgesApi, kind).getByIds(
7930
+ missingEdgeIds,
7931
+ INCLUDE_TOMBSTONES
7932
+ );
7933
+ for (const [index, row] of rows.entries()) {
7934
+ if (row === void 0) continue;
7935
+ const id = requireDefined(missingEdgeIds[index]);
7936
+ const planned = plannedEdgeById.get(id);
7937
+ if (planned !== void 0 && planned.kind !== row.kind) {
7938
+ throw invalidPlanForTarget(
7939
+ `The merge plan reuses edge id "${id}" across kinds.`,
7940
+ { id, committedKind: row.kind, plannedKind: planned.kind }
7941
+ );
7942
+ }
7943
+ }
7944
+ }
7945
+ }
7946
+ }
7947
+ async function assertMergePlanFenceInsideTransaction(target, txBackend, artifact) {
7948
+ await lockRecordedGraphWrite(txBackend, target.graphId);
7949
+ const activeSchema = await txBackend.getActiveSchema(target.graphId);
7950
+ const liveSchema = {
7951
+ managed: activeSchema !== void 0,
7952
+ version: activeSchema?.version ?? 1,
7953
+ hash: activeSchema?.schema_hash ?? await computeSchemaComponent(target)
7954
+ };
7955
+ if (liveSchema.managed !== artifact.target.schema.managed || liveSchema.version !== artifact.target.schema.version || liveSchema.hash !== artifact.target.schema.hash) {
7956
+ throw new MergePlanSchemaMismatchError(
7957
+ "The target's active schema no longer matches the merge plan.",
7958
+ { details: { expected: artifact.target.schema, live: liveSchema } }
7959
+ );
7960
+ }
7961
+ const liveOrigin = await readRevisionOrigin(
7962
+ txBackend,
7963
+ target.revisionSchema,
7964
+ target.graphId
7965
+ );
7966
+ if (liveOrigin !== artifact.target.revision.origin) {
7967
+ throw new MergePlanOriginMismatchError(
7968
+ "The merge plan belongs to a different target revision origin.",
7969
+ {
7970
+ details: {
7971
+ expectedOrigin: artifact.target.revision.origin,
7972
+ liveOrigin
7973
+ }
7974
+ }
7975
+ );
7976
+ }
7977
+ const liveRevision = await readRecordedClock(
7978
+ txBackend,
7979
+ target.revisionSchema,
7980
+ target.graphId
7981
+ ) ?? null;
7982
+ if (liveRevision !== artifact.target.revision.revision) {
7983
+ throw new StaleMergePlanError(
7984
+ "The target revision changed after this merge plan was created; the plan was not applied.",
7985
+ {
7986
+ details: {
7987
+ expectedRevision: artifact.target.revision.revision,
7988
+ liveRevision
7989
+ }
7990
+ }
7991
+ );
7992
+ }
7993
+ }
7994
+ function identityConsistencySeeds(artifact, profile) {
7995
+ const deleted = new Set(
7996
+ artifact.writes.nodeDeletes.map(
7997
+ (entity) => mergeKey(entity.kind, entity.id)
7998
+ )
7999
+ );
8000
+ const byIdentity = /* @__PURE__ */ new Map();
8001
+ const add = (entity) => {
8002
+ const identity = mergeKey(entity.kind, entity.id);
8003
+ if (!deleted.has(identity)) byIdentity.set(identity, entity);
8004
+ };
8005
+ for (const assertion of [
8006
+ ...artifact.writes.identityAssertions,
8007
+ ...artifact.writes.identityRetractions
8008
+ ]) {
8009
+ add(assertion.a);
8010
+ add(assertion.b);
8011
+ }
8012
+ if (profile === "fold") {
8013
+ for (const upsert of artifact.writes.nodeUpserts) add(upsert);
8014
+ }
8015
+ return [...byIdentity].sort(([left], [right]) => compareMergeKeys(left, right)).map(([, entity]) => entity);
8016
+ }
8017
+ async function applyWireMergeWrites(target, nodesApi, edgesApi, txBackend, artifact) {
8018
+ const committedNodes = await applyNodeRows(
8019
+ target,
8020
+ txBackend,
8021
+ nodesApi,
8022
+ artifact.writes.nodeDeletes,
8023
+ artifact.writes.nodeUpserts.map((upsert) => ({
8024
+ kind: upsert.kind,
8025
+ id: upsert.id,
8026
+ props: wireWriteProps(upsert.setProps, upsert.unsetProps),
8027
+ ...upsert.validFrom === void 0 ? {} : { validFrom: upsert.validFrom },
8028
+ ...upsert.validTo === void 0 ? {} : { validTo: upsert.validTo }
8029
+ }))
8030
+ );
8031
+ const committedEdges = await applyEdgeRows(
8032
+ edgesApi,
8033
+ artifact.writes.edgeDeletes,
8034
+ artifact.writes.edgeUpserts.map((upsert) => ({
8035
+ kind: upsert.kind,
8036
+ item: {
8037
+ id: upsert.id,
8038
+ from: upsert.from,
8039
+ to: upsert.to,
8040
+ props: wireWriteProps(upsert.setProps, upsert.unsetProps),
8041
+ ...upsert.validFrom === void 0 ? {} : { validFrom: upsert.validFrom },
8042
+ ...upsert.validTo === void 0 ? {} : { validTo: upsert.validTo }
8043
+ }
8044
+ }))
8045
+ );
8046
+ const identityAssertions = artifact.writes.identityAssertions;
8047
+ const identityRetractions = artifact.writes.identityRetractions;
8048
+ const appliedIdentity = await applyIdentityRows(
8049
+ target,
8050
+ txBackend,
8051
+ identityAssertions,
8052
+ identityRetractions,
8053
+ () => storeRuntime(target).assertIdentityClassesConsistentAtTarget(
8054
+ txBackend,
8055
+ identityConsistencySeeds(
8056
+ artifact,
8057
+ target.graph.identity?.sameIdAcrossKinds
8058
+ )
8059
+ )
8060
+ );
8061
+ if (!forceRecordedGraphRevision(txBackend, target.graphId) && !forceWriteTransactionRevision(txBackend)) {
8062
+ await advanceRevisionClock(
8063
+ txBackend,
8064
+ target.revisionSchema,
8065
+ target.graphId,
8066
+ true
8067
+ );
8068
+ }
8069
+ return {
8070
+ nodes: committedNodes,
8071
+ edges: committedEdges,
8072
+ identity: {
8073
+ asserted: appliedIdentity.asserted,
8074
+ retracted: appliedIdentity.retracted
8075
+ }
8076
+ };
8077
+ }
8078
+ function reportFromArtifact(artifact, merged, warnings, provenancePersisted) {
8079
+ const provenanceRecords = artifact.review.provenanceRecords;
8080
+ return {
8081
+ merged,
8082
+ resolutions: artifact.review.resolutions,
8083
+ conflicts: artifact.review.conflicts,
8084
+ deleteModifyConflicts: artifact.review.deleteModifyConflicts,
8085
+ typeReconciliations: artifact.review.typeReconciliations,
8086
+ dropped: artifact.review.dropped,
8087
+ validityEnds: artifact.review.validityEnds,
8088
+ baseAmbiguities: artifact.review.baseAmbiguities,
8089
+ provenance: artifact.provenance.includeInReport ? buildProvenanceIndex(provenanceRecords) : { byBranch: () => ({ nodeIds: [], edgeIds: [] }) },
8090
+ warnings,
8091
+ ...artifact.review.diagnostics === void 0 ? {} : {
8092
+ candidateDiagnostics: artifact.review.diagnostics
8093
+ },
8094
+ ...provenancePersisted === void 0 ? {} : { provenancePersisted }
8095
+ };
8096
+ }
8097
+ async function applyMergePlan(target, input) {
8098
+ let validation;
8099
+ try {
8100
+ validation = await validateMergePlanArtifact(input);
8101
+ } catch (error) {
8102
+ return err(
8103
+ new InvalidMergePlanError(
8104
+ `Merge plan validation failed: ${describeCause(error)}`,
8105
+ { cause: error }
8106
+ )
8107
+ );
8108
+ }
8109
+ if (!validation.success)
8110
+ return err(mergePlanValidationError(validation.error));
8111
+ const artifact = validation.artifact;
8112
+ if (artifact.target.graphId !== target.graphId) {
8113
+ return err(
8114
+ new MergePlanTargetMismatchError(
8115
+ "The merge plan names a different target graph.",
8116
+ {
8117
+ details: {
8118
+ expectedGraphId: artifact.target.graphId,
8119
+ receivedGraphId: target.graphId
8120
+ }
8121
+ }
8122
+ )
8123
+ );
8124
+ }
8125
+ try {
8126
+ assertPublicPlanCapability(target);
8127
+ const provenanceStore = artifact.provenance.persist ? await tryOpenProvenanceStore(target) : void 0;
8128
+ if (provenanceStore !== void 0 && isErr(provenanceStore)) {
8129
+ return err(provenanceStore.error);
8130
+ }
8131
+ const merged = await withTxConflictRetry(
8132
+ () => target.transaction(async (tx) => {
8133
+ const txBackend = transactionBackend(tx);
8134
+ await assertMergePlanFenceInsideTransaction(
8135
+ target,
8136
+ txBackend,
8137
+ artifact
8138
+ );
8139
+ await preflightWireMergeWrites(
8140
+ target,
8141
+ tx.nodes,
8142
+ tx.edges,
8143
+ artifact
8144
+ );
8145
+ await assertPlannedIdentityIdsFresh(target, txBackend, {
8146
+ identityAssertions: artifact.writes.identityAssertions,
8147
+ identityRetractions: artifact.writes.identityRetractions
8148
+ });
8149
+ return applyWireMergeWrites(
8150
+ target,
8151
+ tx.nodes,
8152
+ tx.edges,
8153
+ txBackend,
8154
+ artifact
8155
+ );
8156
+ }, mergeCommitTransactionOptions(target))
8157
+ );
8158
+ const warnings = [...artifact.review.warnings];
5453
8159
  let provenancePersisted;
5454
8160
  if (provenanceStore !== void 0 && !isErr(provenanceStore)) {
5455
8161
  try {
8162
+ const records = artifact.review.provenanceRecords;
5456
8163
  const count = await persistProvenanceRecords(
5457
8164
  provenanceStore.data,
5458
8165
  target.graphId,
5459
- plan.provenanceRecords
8166
+ records
5460
8167
  );
5461
8168
  provenancePersisted = {
5462
8169
  graphId: provenanceGraphId(target.graphId),
@@ -5464,37 +8171,27 @@ async function resolveMerge(store, target, branches, options, useBaseSources, in
5464
8171
  };
5465
8172
  } catch (error) {
5466
8173
  warnings.push(
5467
- "provenance persistence failed (graph committed; provenance not persisted): " + (error instanceof Error ? error.message : String(error))
8174
+ "provenance persistence failed (graph committed; provenance not persisted): " + describeCause(error)
5468
8175
  );
5469
8176
  }
5470
8177
  }
5471
- const report = {
5472
- merged,
5473
- resolutions: plan.resolutions,
5474
- conflicts: plan.propertyConflicts,
5475
- deleteModifyConflicts: plan.deleteModifyConflicts,
5476
- typeReconciliations: plan.typeReconciliations,
5477
- dropped: plan.dropped,
5478
- validityEnds: plan.validityEnds,
5479
- baseAmbiguities: plan.baseAmbiguities,
5480
- provenance,
5481
- warnings,
5482
- ...provenancePersisted === void 0 ? {} : { provenancePersisted }
5483
- };
5484
- return ok(report);
8178
+ return ok(
8179
+ reportFromArtifact(artifact, merged, warnings, provenancePersisted)
8180
+ );
5485
8181
  } catch (error) {
5486
- if (error instanceof MergeError) {
5487
- return err(error);
5488
- }
8182
+ const translated = translateMergeCommitError(error);
5489
8183
  return err(
5490
- new MergeError(
5491
- `Merge failed while staging or committing: ${describeCause(error)}`,
5492
- { cause: error }
8184
+ translated instanceof MergeError ? translated : new MergeError(
8185
+ `Merge plan apply failed: ${describeCause(translated)}`,
8186
+ {
8187
+ cause: translated
8188
+ }
5493
8189
  )
5494
8190
  );
5495
8191
  }
5496
8192
  }
5497
- async function merge(store, branches, optionsInput = {}) {
8193
+ async function merge(store, branchInputs, optionsInput = {}) {
8194
+ const branches = unwrapMergeBranches(branchInputs);
5498
8195
  const normalized = tryNormalize(optionsInput);
5499
8196
  if (isErr(normalized)) {
5500
8197
  return err(normalized.error);
@@ -5512,7 +8209,8 @@ async function merge(store, branches, optionsInput = {}) {
5512
8209
  options,
5513
8210
  false,
5514
8211
  void 0,
5515
- precondition.data
8212
+ precondition.data,
8213
+ commitResolvedMerge
5516
8214
  );
5517
8215
  }
5518
8216
  async function validateForkPointVersions(forkPoint, branches) {
@@ -5931,55 +8629,54 @@ async function commitIncrementalPlan(target, plan, guard) {
5931
8629
  { details: { capability: "transactions" } }
5932
8630
  );
5933
8631
  }
5934
- return withTxConflictRetry(
5935
- () => target.transaction(async (tx) => {
5936
- if (target.revisionTrackingEnabled) {
5937
- await lockRecordedGraphWrite(transactionBackend(tx), target.graphId);
5938
- }
5939
- await assertForkPointUnchanged(guard.forkPoint);
5940
- const nodesApi = tx.nodes;
5941
- const edgesApi = tx.edges;
5942
- await assertBaseResolutionStable(tx, guard);
5943
- await assertPlannedIdentityIdsFresh(target, transactionBackend(tx), plan);
5944
- await assertIdentityPeersStable(
5945
- target,
5946
- transactionBackend(tx),
5947
- guard.identityPeerProbe,
5948
- plan
5949
- );
5950
- await assertInheritedTargetUnchanged(nodesApi, edgesApi, guard, plan);
5951
- await validateIncrementalNodeWrites(target, nodesApi, plan);
5952
- await validateIncrementalEdgeWrites(target, edgesApi, plan);
5953
- return applyMergePlan(
5954
- plan,
5955
- nodesApi,
5956
- edgesApi,
5957
- target,
5958
- transactionBackend(tx)
5959
- );
5960
- }, mergeCommitTransactionOptions(target))
8632
+ return runMergeCommit(
8633
+ () => withTxConflictRetry(
8634
+ () => target.transaction(async (tx) => {
8635
+ if (target.revisionTrackingEnabled) {
8636
+ await lockRecordedGraphWrite(transactionBackend(tx), target.graphId);
8637
+ }
8638
+ await assertForkPointUnchanged(guard.forkPoint);
8639
+ const nodesApi = tx.nodes;
8640
+ const edgesApi = tx.edges;
8641
+ await assertBaseResolutionStable(
8642
+ tx,
8643
+ guard
8644
+ );
8645
+ await assertPlannedIdentityIdsFresh(
8646
+ target,
8647
+ transactionBackend(tx),
8648
+ plan
8649
+ );
8650
+ await assertIdentityPeersStable(
8651
+ target,
8652
+ transactionBackend(tx),
8653
+ guard.identityPeerProbe,
8654
+ plan
8655
+ );
8656
+ await assertInheritedTargetUnchanged(nodesApi, edgesApi, guard, plan);
8657
+ await validateIncrementalNodeWrites(target, nodesApi, plan);
8658
+ await validateIncrementalEdgeWrites(target, edgesApi, plan);
8659
+ return applyInternalMergePlan(
8660
+ plan,
8661
+ nodesApi,
8662
+ edgesApi,
8663
+ target,
8664
+ transactionBackend(tx)
8665
+ );
8666
+ }, mergeCommitTransactionOptions(target))
8667
+ )
5961
8668
  );
5962
8669
  }
5963
8670
  async function mergeIncremental(args) {
5964
- const { forkPoint, target, branches } = args;
8671
+ const { forkPoint, target } = args;
8672
+ const branches = unwrapMergeBranches(args.branches);
5965
8673
  const normalized = tryNormalize(args.options ?? {}, ["target"]);
5966
8674
  if (isErr(normalized)) {
5967
8675
  return err(normalized.error);
5968
8676
  }
5969
8677
  const options = normalized.data;
5970
8678
  if (options.onBasePropertyConflict !== "flag") {
5971
- return err(
5972
- new InvalidMergeOptionsError(
5973
- 'mergeIncremental() requires onBasePropertyConflict: "flag" (keep-base); a non-keep-base policy could overwrite a newer committed base value with a stale branch value.',
5974
- {
5975
- details: {
5976
- option: "onBasePropertyConflict",
5977
- accepted: "flag",
5978
- received: options.onBasePropertyConflict
5979
- }
5980
- }
5981
- )
5982
- );
8679
+ return err(incrementalBaseConflictPolicyError(options));
5983
8680
  }
5984
8681
  const forkPrecondition = await validateForkPointVersions(forkPoint, branches);
5985
8682
  if (isErr(forkPrecondition)) {
@@ -5991,12 +8688,7 @@ async function mergeIncremental(args) {
5991
8688
  computeSchemaComponent(target)
5992
8689
  ]);
5993
8690
  if (forkSchema !== targetSchema) {
5994
- return err(
5995
- new MergeError(
5996
- "mergeIncremental() requires target and forkPoint to share a schema; schema drift is not supported (the target content may differ \u2014 only the schema must match).",
5997
- { details: {} }
5998
- )
5999
- );
8691
+ return err(incrementalSchemaError());
6000
8692
  }
6001
8693
  const targetBranch = {
6002
8694
  id: COMMITTED_TARGET_BRANCH,
@@ -6015,10 +8707,12 @@ async function mergeIncremental(args) {
6015
8707
  // it is re-established there rather than assumed to have held for the
6016
8708
  // whole of planning (see `assertForkPointUnchanged`).
6017
8709
  forkPoint: { store: forkPoint, version: forkVersion }
6018
- }
8710
+ },
8711
+ void 0,
8712
+ commitResolvedMerge
6019
8713
  );
6020
8714
  }
6021
8715
 
6022
- export { BaseVersionMismatchError, BranchError, IdentityMergeConflictError, InvalidMergeOptionsError, MERGE_ERROR_CODES, MERGE_OPTION_DEFAULTS, MergeConflictError, MergeError, SimilarityUnavailableError, VALIDITY_END_TARGET_PRECEDENCE, asBaseVersion, asBranchId, branch, cloneWorkingCopyStrategy, computeBaseVersion, merge, mergeIncremental, normalizeMergeOptions, openProvenanceStore, persistProvenanceRecords, provenanceGraphId, readProvenance };
8716
+ export { BaseVersionMismatchError, BranchError, CandidateSourceError, IdentityMergeConflictError, InvalidMergeOptionsError, InvalidMergePlanError, MERGE_ERROR_CODES, MERGE_OPTION_DEFAULTS, MERGE_PLAN_DIGEST_ALGORITHM, MERGE_PLAN_FORMAT_VERSION, MatchEvidenceError, MergeConflictError, MergeConstraintConflictError, MergeError, MergePlanCapabilityError, MergePlanDigestMismatchError, MergePlanOriginMismatchError, MergePlanSchemaMismatchError, MergePlanTargetMismatchError, MergePlanningStaleError, SimilarityUnavailableError, StaleMergePlanError, UnsupportedMergePlanVersionError, VALIDITY_END_TARGET_PRECEDENCE, applyMergePlan, asBaseVersion, asBranchId, branch, cloneWorkingCopyStrategy, computeBaseVersion, ingestionBranch, merge, mergeIncremental, normalizeMergeOptions, openProvenanceStore, persistProvenanceRecords, planMerge, planMergeIncremental, provenanceGraphId, readProvenance };
6023
8717
  //# sourceMappingURL=index.js.map
6024
8718
  //# sourceMappingURL=index.js.map