@lucern/graph-primitives 1.0.28 → 1.0.30

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 (319) hide show
  1. package/dist/{beliefDecay-DZ6tkLYq.d.ts → beliefDecay-BmkEk5OJ.d.ts} +3 -3
  2. package/dist/beliefDecay.d.ts +1 -1
  3. package/dist/beliefDecay.js +448 -314
  4. package/dist/beliefDecay.js.map +1 -1
  5. package/dist/{beliefEvidenceLinks-CWOXxxJg.d.ts → beliefEvidenceLinks-BzfjON_6.d.ts} +13 -13
  6. package/dist/beliefEvidenceLinks.d.ts +1 -1
  7. package/dist/beliefEvidenceLinks.js +843 -624
  8. package/dist/beliefEvidenceLinks.js.map +1 -1
  9. package/dist/beliefEvidenceLinks.operational.d.ts +7 -5
  10. package/dist/beliefEvidenceLinks.operational.js +91 -18
  11. package/dist/beliefEvidenceLinks.operational.js.map +1 -1
  12. package/dist/beliefLifecycle.js.map +1 -1
  13. package/dist/confidencePropagationDispatch.d.ts +28 -27
  14. package/dist/confidencePropagationDispatch.js +157 -99
  15. package/dist/confidencePropagationDispatch.js.map +1 -1
  16. package/dist/{contradictions-51VLsESq.d.ts → contradictions-BATPuZTL.d.ts} +10 -10
  17. package/dist/contradictions.d.ts +1 -1
  18. package/dist/contradictions.js +398 -228
  19. package/dist/contradictions.js.map +1 -1
  20. package/dist/convex.d.ts +65 -30
  21. package/dist/convex.js +7 -3
  22. package/dist/convex.js.map +1 -1
  23. package/dist/debug.js.map +1 -1
  24. package/dist/edgeValidation.js +293 -85
  25. package/dist/edgeValidation.js.map +1 -1
  26. package/dist/edges/contains.d.ts +1 -1
  27. package/dist/edges/contains.js.map +1 -1
  28. package/dist/edges/contradicts.d.ts +1 -1
  29. package/dist/edges/contradicts.js.map +1 -1
  30. package/dist/edges/{dependsOn.d.ts → depends-on.d.ts} +1 -1
  31. package/dist/edges/{dependsOn.js → depends-on.js} +4 -4
  32. package/dist/edges/depends-on.js.map +1 -0
  33. package/dist/edges/{derivedFrom.d.ts → derived-from.d.ts} +1 -1
  34. package/dist/edges/{derivedFrom.js → derived-from.js} +3 -3
  35. package/dist/edges/derived-from.js.map +1 -0
  36. package/dist/edges/elaborates.d.ts +1 -1
  37. package/dist/edges/elaborates.js.map +1 -1
  38. package/dist/edges/index.d.ts +7 -3
  39. package/dist/edges/index.js +7 -4
  40. package/dist/edges/index.js.map +1 -1
  41. package/dist/edges/informs.d.ts +1 -1
  42. package/dist/edges/informs.js.map +1 -1
  43. package/dist/edges/{propagationTypes.d.ts → propagation-types.d.ts} +14 -14
  44. package/dist/edges/{propagationTypes.js → propagation-types.js} +3 -3
  45. package/dist/edges/propagation-types.js.map +1 -0
  46. package/dist/edges/refutes.d.ts +1 -1
  47. package/dist/edges/refutes.js.map +1 -1
  48. package/dist/edges/supports.d.ts +1 -1
  49. package/dist/edges/supports.js.map +1 -1
  50. package/dist/edges/tests.d.ts +1 -1
  51. package/dist/edges/tests.js.map +1 -1
  52. package/dist/edges/utils.d.ts +1 -1
  53. package/dist/edges/utils.js.map +1 -1
  54. package/dist/embeddingTrigger.d.ts +14 -6
  55. package/dist/embeddingTrigger.js +11 -14
  56. package/dist/embeddingTrigger.js.map +1 -1
  57. package/dist/{entityBridge-DMaKooYn.d.ts → entityBridge-BhVDM3pc.d.ts} +5 -5
  58. package/dist/entityBridge.d.ts +1 -1
  59. package/dist/entityBridge.js +602 -225
  60. package/dist/entityBridge.js.map +1 -1
  61. package/dist/entityCanonicalMatch.d.ts +14 -12
  62. package/dist/entityCanonicalMatch.js.map +1 -1
  63. package/dist/{entityLifecycle-CvgSK5FV.d.ts → entityLifecycle-BsfCz9pS.d.ts} +5 -9
  64. package/dist/entityLifecycle.d.ts +1 -1
  65. package/dist/entityLifecycle.js +857 -515
  66. package/dist/entityLifecycle.js.map +1 -1
  67. package/dist/{entityValidation-KLZ_Xl2D.d.ts → entityValidation-B1yNEHJx.d.ts} +7 -6
  68. package/dist/entityValidation.d.ts +3 -1
  69. package/dist/entityValidation.js +60 -8
  70. package/dist/entityValidation.js.map +1 -1
  71. package/dist/{epistemicAnswers-C5ib4z6_.d.ts → epistemicAnswers-f47YMu9U.d.ts} +6 -6
  72. package/dist/epistemicAnswers.d.ts +1 -1
  73. package/dist/epistemicAnswers.js +587 -545
  74. package/dist/epistemicAnswers.js.map +1 -1
  75. package/dist/epistemicBeliefs.admin.d.ts +8 -8
  76. package/dist/epistemicBeliefs.admin.js +366 -203
  77. package/dist/epistemicBeliefs.admin.js.map +1 -1
  78. package/dist/epistemicBeliefs.backfills.d.ts +8 -8
  79. package/dist/epistemicBeliefs.backfills.js +655 -308
  80. package/dist/epistemicBeliefs.backfills.js.map +1 -1
  81. package/dist/epistemicBeliefs.confidence.d.ts +19 -14
  82. package/dist/epistemicBeliefs.confidence.js +634 -423
  83. package/dist/epistemicBeliefs.confidence.js.map +1 -1
  84. package/dist/epistemicBeliefs.core.d.ts +6 -6
  85. package/dist/epistemicBeliefs.core.js +719 -411
  86. package/dist/epistemicBeliefs.core.js.map +1 -1
  87. package/dist/epistemicBeliefs.d.ts +11 -8
  88. package/dist/epistemicBeliefs.forkEvidence.d.ts +2 -0
  89. package/dist/epistemicBeliefs.forkEvidence.js +8 -28
  90. package/dist/epistemicBeliefs.forkEvidence.js.map +1 -1
  91. package/dist/epistemicBeliefs.helpers.d.ts +69 -74
  92. package/dist/epistemicBeliefs.helpers.js +359 -248
  93. package/dist/epistemicBeliefs.helpers.js.map +1 -1
  94. package/dist/epistemicBeliefs.internal.d.ts +5 -5
  95. package/dist/epistemicBeliefs.internal.js +1246 -1044
  96. package/dist/epistemicBeliefs.internal.js.map +1 -1
  97. package/dist/epistemicBeliefs.js +4922 -3608
  98. package/dist/epistemicBeliefs.js.map +1 -1
  99. package/dist/epistemicBeliefs.lifecycle.d.ts +5 -5
  100. package/dist/epistemicBeliefs.lifecycle.js +1137 -818
  101. package/dist/epistemicBeliefs.lifecycle.js.map +1 -1
  102. package/dist/epistemicBeliefs.links.d.ts +7 -7
  103. package/dist/epistemicBeliefs.links.js +408 -307
  104. package/dist/epistemicBeliefs.links.js.map +1 -1
  105. package/dist/epistemicBeliefs.queries.d.ts +4 -4
  106. package/dist/epistemicBeliefs.queries.js +175 -20
  107. package/dist/epistemicBeliefs.queries.js.map +1 -1
  108. package/dist/epistemicBeliefs.topicAnchor.d.ts +6 -4
  109. package/dist/epistemicBeliefs.topicAnchor.js +12 -5
  110. package/dist/epistemicBeliefs.topicAnchor.js.map +1 -1
  111. package/dist/epistemicContracts.d.ts +28 -3
  112. package/dist/epistemicContracts.evaluators.d.ts +2 -0
  113. package/dist/epistemicContracts.evaluators.js +1063 -613
  114. package/dist/epistemicContracts.evaluators.js.map +1 -1
  115. package/dist/epistemicContracts.handlers.d.ts +15 -32
  116. package/dist/epistemicContracts.handlers.js +2086 -1644
  117. package/dist/epistemicContracts.handlers.js.map +1 -1
  118. package/dist/epistemicContracts.js +1131 -672
  119. package/dist/epistemicContracts.js.map +1 -1
  120. package/dist/epistemicContracts.metrics.d.ts +2 -0
  121. package/dist/epistemicContracts.metrics.js +375 -158
  122. package/dist/epistemicContracts.metrics.js.map +1 -1
  123. package/dist/epistemicContracts.types.d.ts +87 -81
  124. package/dist/epistemicEdgeCreation.d.ts +2 -0
  125. package/dist/epistemicEdgeCreation.js +87 -16
  126. package/dist/epistemicEdgeCreation.js.map +1 -1
  127. package/dist/{epistemicEdges-BF-cn4i3.d.ts → epistemicEdges-BGBh0QSP.d.ts} +4 -7
  128. package/dist/epistemicEdges.d.ts +6 -5
  129. package/dist/epistemicEdges.handlers.d.ts +3 -3
  130. package/dist/epistemicEdges.handlers.js +129 -24
  131. package/dist/epistemicEdges.handlers.js.map +1 -1
  132. package/dist/epistemicEdges.helpers.d.ts +6 -4
  133. package/dist/epistemicEdges.helpers.js +37 -2
  134. package/dist/epistemicEdges.helpers.js.map +1 -1
  135. package/dist/epistemicEdges.js +1969 -1205
  136. package/dist/epistemicEdges.js.map +1 -1
  137. package/dist/epistemicEdges.mutations.d.ts +7 -7
  138. package/dist/epistemicEdges.mutations.js +960 -583
  139. package/dist/epistemicEdges.mutations.js.map +1 -1
  140. package/dist/epistemicEdges.queries.d.ts +16 -16
  141. package/dist/epistemicEdges.queries.js +639 -367
  142. package/dist/epistemicEdges.queries.js.map +1 -1
  143. package/dist/epistemicEdges.types.d.ts +10 -8
  144. package/dist/epistemicEvidence.d.ts +4 -1
  145. package/dist/epistemicEvidence.js +937 -536
  146. package/dist/epistemicEvidence.js.map +1 -1
  147. package/dist/epistemicEvidenceHelpers.d.ts +26 -10
  148. package/dist/epistemicEvidenceHelpers.js +239 -200
  149. package/dist/epistemicEvidenceHelpers.js.map +1 -1
  150. package/dist/epistemicEvidenceMutations.d.ts +8 -8
  151. package/dist/epistemicEvidenceMutations.js +844 -696
  152. package/dist/epistemicEvidenceMutations.js.map +1 -1
  153. package/dist/epistemicEvidenceQueries.d.ts +8 -8
  154. package/dist/epistemicEvidenceQueries.js +514 -238
  155. package/dist/epistemicEvidenceQueries.js.map +1 -1
  156. package/dist/epistemicHelpers.d.ts +4 -2
  157. package/dist/epistemicHelpers.js +308 -134
  158. package/dist/epistemicHelpers.js.map +1 -1
  159. package/dist/epistemicInsert.d.ts +16 -4
  160. package/dist/epistemicInsert.js +6 -3
  161. package/dist/epistemicInsert.js.map +1 -1
  162. package/dist/epistemicLayerRules.d.ts +10 -8
  163. package/dist/epistemicLayerRules.js +1 -5
  164. package/dist/epistemicLayerRules.js.map +1 -1
  165. package/dist/{epistemicLinking-CfE00tHJ.d.ts → epistemicLinking-CsCDv2cN.d.ts} +3 -3
  166. package/dist/epistemicLinking.d.ts +1 -1
  167. package/dist/epistemicLinking.js +177 -100
  168. package/dist/epistemicLinking.js.map +1 -1
  169. package/dist/epistemicNodeCreation.d.ts +2 -0
  170. package/dist/epistemicNodeCreation.js +203 -40
  171. package/dist/epistemicNodeCreation.js.map +1 -1
  172. package/dist/{epistemicNodes-BCQxpYx_.d.ts → epistemicNodes-CokAgBHg.d.ts} +3 -3
  173. package/dist/epistemicNodes.d.ts +3 -3
  174. package/dist/epistemicNodes.helpers.d.ts +24 -15
  175. package/dist/epistemicNodes.helpers.js.map +1 -1
  176. package/dist/epistemicNodes.internal.d.ts +6 -6
  177. package/dist/epistemicNodes.internal.js +389 -319
  178. package/dist/epistemicNodes.internal.js.map +1 -1
  179. package/dist/epistemicNodes.js +704 -508
  180. package/dist/epistemicNodes.js.map +1 -1
  181. package/dist/epistemicNodes.mutations.d.ts +6 -6
  182. package/dist/epistemicNodes.mutations.js +564 -467
  183. package/dist/epistemicNodes.mutations.js.map +1 -1
  184. package/dist/epistemicNodes.queries.d.ts +8 -8
  185. package/dist/epistemicNodes.queries.js +311 -314
  186. package/dist/epistemicNodes.queries.js.map +1 -1
  187. package/dist/epistemicNodes.validators.d.ts +2 -2
  188. package/dist/epistemicNodes.validators.js.map +1 -1
  189. package/dist/epistemicQuestions.conviction.d.ts +8 -8
  190. package/dist/epistemicQuestions.conviction.js +665 -484
  191. package/dist/epistemicQuestions.conviction.js.map +1 -1
  192. package/dist/epistemicQuestions.create.d.ts +4 -4
  193. package/dist/epistemicQuestions.create.js +640 -612
  194. package/dist/epistemicQuestions.create.js.map +1 -1
  195. package/dist/epistemicQuestions.d.ts +8 -5
  196. package/dist/epistemicQuestions.evidence.d.ts +2 -2
  197. package/dist/epistemicQuestions.evidence.js +475 -383
  198. package/dist/epistemicQuestions.evidence.js.map +1 -1
  199. package/dist/epistemicQuestions.helpers.d.ts +125 -24
  200. package/dist/epistemicQuestions.helpers.js +240 -209
  201. package/dist/epistemicQuestions.helpers.js.map +1 -1
  202. package/dist/epistemicQuestions.js +3474 -2823
  203. package/dist/epistemicQuestions.js.map +1 -1
  204. package/dist/epistemicQuestions.lifecycle.d.ts +2 -2
  205. package/dist/epistemicQuestions.lifecycle.js +607 -546
  206. package/dist/epistemicQuestions.lifecycle.js.map +1 -1
  207. package/dist/epistemicQuestions.queries.d.ts +12 -7
  208. package/dist/epistemicQuestions.queries.js +305 -244
  209. package/dist/epistemicQuestions.queries.js.map +1 -1
  210. package/dist/epistemicQuestions.sprint.d.ts +2 -2
  211. package/dist/epistemicQuestions.sprint.js +600 -394
  212. package/dist/epistemicQuestions.sprint.js.map +1 -1
  213. package/dist/epistemicQuestions.tail.d.ts +6 -6
  214. package/dist/epistemicQuestions.tail.js +572 -433
  215. package/dist/epistemicQuestions.tail.js.map +1 -1
  216. package/dist/{epistemicSources-dlKj58Jp.d.ts → epistemicSources-DQtaEkWs.d.ts} +4 -4
  217. package/dist/epistemicSources.d.ts +1 -1
  218. package/dist/epistemicSources.js +352 -312
  219. package/dist/epistemicSources.js.map +1 -1
  220. package/dist/evaluators/index.d.ts +8 -6
  221. package/dist/evaluators/index.js +399 -167
  222. package/dist/evaluators/index.js.map +1 -1
  223. package/dist/evaluators/lint-checker-evaluator.d.ts +16 -0
  224. package/dist/evaluators/{lintCheckerEvaluator.js → lint-checker-evaluator.js} +10 -5
  225. package/dist/evaluators/lint-checker-evaluator.js.map +1 -0
  226. package/dist/evaluators/{sentryCheckerEvaluator.d.ts → sentry-checker-evaluator.d.ts} +7 -2
  227. package/dist/evaluators/{sentryCheckerEvaluator.js → sentry-checker-evaluator.js} +3 -3
  228. package/dist/evaluators/sentry-checker-evaluator.js.map +1 -0
  229. package/dist/evaluators/shared.d.ts +2 -2
  230. package/dist/evaluators/shared.js +3 -1
  231. package/dist/evaluators/shared.js.map +1 -1
  232. package/dist/evaluators/{testRunnerEvaluator.d.ts → test-runner-evaluator.d.ts} +6 -1
  233. package/dist/evaluators/{testRunnerEvaluator.js → test-runner-evaluator.js} +6 -4
  234. package/dist/evaluators/test-runner-evaluator.js.map +1 -0
  235. package/dist/evaluators/tsc-checker-evaluator.d.ts +16 -0
  236. package/dist/evaluators/{tscCheckerEvaluator.js → tsc-checker-evaluator.js} +10 -5
  237. package/dist/evaluators/tsc-checker-evaluator.js.map +1 -0
  238. package/dist/graphTypes.js +6 -2
  239. package/dist/graphTypes.js.map +1 -1
  240. package/dist/helpers.d.ts +2 -0
  241. package/dist/helpers.js +313 -93
  242. package/dist/helpers.js.map +1 -1
  243. package/dist/{index-C-Kyd7hD.d.ts → index-DZxyC9Pb.d.ts} +7 -6
  244. package/dist/index.d.ts +87 -83
  245. package/dist/index.js +15677 -10594
  246. package/dist/index.js.map +1 -1
  247. package/dist/invariantEnforcement.d.ts +3 -3
  248. package/dist/invariantEnforcement.js.map +1 -1
  249. package/dist/logicalRoleInference.d.ts +2 -0
  250. package/dist/logicalRoleInference.js +1 -1
  251. package/dist/logicalRoleInference.js.map +1 -1
  252. package/dist/matcherFeedbackUtils.d.ts +2 -2
  253. package/dist/matcherFeedbackUtils.js.map +1 -1
  254. package/dist/{ontology-matching-C6rrz2VP.d.ts → ontology-matching-C-mYFrir.d.ts} +16 -16
  255. package/dist/ontology-matching.d.ts +1 -1
  256. package/dist/{ontologyApproval-CFYmqKmk.d.ts → ontologyApproval-BVt0feJi.d.ts} +10 -10
  257. package/dist/ontologyApproval.d.ts +1 -1
  258. package/dist/ontologyApproval.js +7 -1
  259. package/dist/ontologyApproval.js.map +1 -1
  260. package/dist/ontologyDefinitions.d.ts +14 -24
  261. package/dist/ontologyDefinitions.js +269 -34
  262. package/dist/ontologyDefinitions.js.map +1 -1
  263. package/dist/ontologyHelpers.d.ts +13 -13
  264. package/dist/ontologyHelpers.js.map +1 -1
  265. package/dist/{ontologyRegistry-B67rPJ16.d.ts → ontologyRegistry-CljS-ENv.d.ts} +2 -2
  266. package/dist/ontologyRegistry.d.ts +1 -1
  267. package/dist/ontologyRegistry.js +34 -6
  268. package/dist/ontologyRegistry.js.map +1 -1
  269. package/dist/{projectionReconciliation-jww2fBI0.d.ts → projectionReconciliation-DnrSgHSQ.d.ts} +4 -4
  270. package/dist/projectionReconciliation.d.ts +1 -1
  271. package/dist/projectionReconciliation.js +57 -10
  272. package/dist/projectionReconciliation.js.map +1 -1
  273. package/dist/{projectionStaleness-CmdbpjVK.d.ts → projectionStaleness-C8ImQ2zP.d.ts} +17 -17
  274. package/dist/projectionStaleness.d.ts +1 -1
  275. package/dist/projectionStaleness.js +8 -2
  276. package/dist/projectionStaleness.js.map +1 -1
  277. package/dist/proof-attestation.json +1 -1
  278. package/dist/{questionEvidenceLinks-DFlyPpAj.d.ts → questionEvidenceLinks-_nPRa-LY.d.ts} +10 -10
  279. package/dist/questionEvidenceLinks.d.ts +1 -1
  280. package/dist/questionEvidenceLinks.js +564 -347
  281. package/dist/questionEvidenceLinks.js.map +1 -1
  282. package/dist/{resolverTypes-CC8Ea2E2.d.ts → resolverTypes-BOXPxLET.d.ts} +8 -7
  283. package/dist/resolverTypes.d.ts +4 -2
  284. package/dist/{resolvers-Br1a6eLV.d.ts → resolvers-B1TIBmRO.d.ts} +3 -1
  285. package/dist/resolvers.d.ts +5 -3
  286. package/dist/resolvers.js +121 -77
  287. package/dist/resolvers.js.map +1 -1
  288. package/dist/scopeResolverCompat.d.ts +10 -7
  289. package/dist/scopeResolverCompat.js +106 -123
  290. package/dist/scopeResolverCompat.js.map +1 -1
  291. package/dist/{text-matching-DNg4M5Wd.d.ts → text-matching-DzFooju6.d.ts} +7 -7
  292. package/dist/text-matching.d.ts +1 -1
  293. package/dist/topicOntologyResolver.d.ts +22 -21
  294. package/dist/topicOntologyResolver.js +54 -32
  295. package/dist/topicOntologyResolver.js.map +1 -1
  296. package/dist/topicProjectOverlay.d.ts +30 -20
  297. package/dist/topicProjectOverlay.js +120 -76
  298. package/dist/topicProjectOverlay.js.map +1 -1
  299. package/dist/{topicScope-7zhyeGl7.d.ts → topicScope-DJVa0mLa.d.ts} +22 -7
  300. package/dist/topicScope.d.ts +3 -1
  301. package/dist/topicScope.js +104 -119
  302. package/dist/topicScope.js.map +1 -1
  303. package/dist/workflowBridge.d.ts +26 -15
  304. package/dist/workflowBridge.js +140 -144
  305. package/dist/workflowBridge.js.map +1 -1
  306. package/dist/workspaceIsolation.d.ts +14 -12
  307. package/dist/workspaceIsolation.js +108 -122
  308. package/dist/workspaceIsolation.js.map +1 -1
  309. package/package.json +4 -4
  310. package/dist/edges/dependsOn.js.map +0 -1
  311. package/dist/edges/derivedFrom.js.map +0 -1
  312. package/dist/edges/propagationTypes.js.map +0 -1
  313. package/dist/evaluators/lintCheckerEvaluator.d.ts +0 -11
  314. package/dist/evaluators/lintCheckerEvaluator.js.map +0 -1
  315. package/dist/evaluators/sentryCheckerEvaluator.js.map +0 -1
  316. package/dist/evaluators/testRunnerEvaluator.js.map +0 -1
  317. package/dist/evaluators/tscCheckerEvaluator.d.ts +0 -11
  318. package/dist/evaluators/tscCheckerEvaluator.js.map +0 -1
  319. package/dist/{epistemicQuestions-bwHd2FWE.d.ts → epistemicQuestions-Do1fhYm5.d.ts} +4 -4
@@ -1,386 +1,327 @@
1
- import { v } from 'convex/values';
2
- import { componentsGeneric, anyApi, mutationGeneric, queryGeneric } from 'convex/server';
3
1
  import { checkProjectAccess } from '@lucern/access-control/access';
4
2
  import { permissiveReturn } from '@lucern/contracts/schema-helpers/validators';
5
- import { generateGlobalId, assertUuidV7Identity, assertStorageEdgeVocabulary, assertUuidShapedEdgeEndpoint } from '@lucern/contracts/ids';
3
+ import { v } from 'convex/values';
4
+ import { unsafeConvexAnyApi } from '@lucern/contracts/convex/unsafeAnyApi';
5
+ import { componentsGeneric, mutationGeneric, queryGeneric } from 'convex/server';
6
6
  import { assertEdgePolicyAllowed, edgePolicyManifest } from '@lucern/contracts';
7
+ import { generateGlobalId, assertUuidV7Identity, assertStorageEdgeVocabulary, assertUuidShapedEdgeEndpoint } from '@lucern/contracts/ids';
7
8
 
8
9
  // src/beliefEvidenceLinks.ts
9
-
10
- // src/matcherFeedbackUtils.ts
11
- function isOperationalLinkStatus(status) {
12
- return status !== "suggested" && status !== "dismissed";
13
- }
14
- function mergeLinkSuggestionStatus(existing, requested) {
15
- if (requested === void 0 || requested === null) {
16
- return existing ?? void 0;
17
- }
18
- if (existing && isOperationalLinkStatus(existing) && !isOperationalLinkStatus(requested)) {
19
- return existing;
10
+ var unsafeApi = unsafeConvexAnyApi(
11
+ "graph-primitives top-level module bundle lacks a committed Convex _generated/api surface"
12
+ );
13
+ var api = unsafeApi;
14
+ componentsGeneric();
15
+ var internal = unsafeApi;
16
+ var mutation = mutationGeneric;
17
+ var query = queryGeneric;
18
+ async function assertExistingNodeEndpoint(ctx, endpointRole, endpoint) {
19
+ assertUuidShapedEdgeEndpoint(endpointRole, endpoint);
20
+ const node = await ctx.db.query("epistemicNodes").withIndex(
21
+ "by_globalId",
22
+ (q) => q.eq("globalId", endpoint)
23
+ ).first();
24
+ if (!node) {
25
+ throw new Error(
26
+ `edge_endpoint_not_canonical: epistemicEdges insert requires ${endpointRole} to be the globalId of an existing epistemicNodes row, received ${endpoint} (no node with that globalId)`
27
+ );
20
28
  }
21
- return requested;
22
- }
23
- function resolveReviewedLinkStatus(requested) {
24
- return requested;
25
29
  }
26
- function deriveMatcherReviewStatus(input) {
27
- if (input.explicitReviewStatus) {
28
- return input.explicitReviewStatus;
29
- }
30
- if (input.autoAccepted) {
31
- return "auto_accepted";
30
+ async function insertEpistemicEdge(ctx, doc) {
31
+ assertUuidV7Identity("epistemicEdges", doc.globalId);
32
+ assertStorageEdgeVocabulary(doc.edgeType);
33
+ if (!doc.fromNodeId || typeof doc.fromNodeId !== "string") {
34
+ throw new Error(
35
+ "edge_endpoint_missing: epistemicEdges insert requires a non-empty fromNodeId"
36
+ );
32
37
  }
33
- if (input.linkStatus === "suggested") {
34
- return "pending";
38
+ if (!doc.toNodeId || typeof doc.toNodeId !== "string") {
39
+ throw new Error(
40
+ "edge_endpoint_missing: epistemicEdges insert requires a non-empty toNodeId"
41
+ );
35
42
  }
36
- if (input.linkStatus === "dismissed") {
37
- return "rejected";
43
+ await assertExistingNodeEndpoint(ctx, "fromNodeId", doc.fromNodeId);
44
+ await assertExistingNodeEndpoint(ctx, "toNodeId", doc.toNodeId);
45
+ if (doc.fromNodeType && doc.toNodeType && doc.edgeType !== "extracted_from") {
46
+ assertEdgePolicyAllowed(
47
+ edgePolicyManifest,
48
+ doc.edgeType,
49
+ {
50
+ kind: "epistemic_node",
51
+ nodeId: doc.fromNodeId,
52
+ nodeType: doc.fromNodeType
53
+ },
54
+ {
55
+ kind: "epistemic_node",
56
+ nodeId: doc.toNodeId,
57
+ nodeType: doc.toNodeType
58
+ }
59
+ );
38
60
  }
39
- return "accepted";
61
+ return ctx.db.insert("epistemicEdges", doc);
40
62
  }
41
- var api = anyApi;
42
- componentsGeneric();
43
- var internal = anyApi;
44
- var mutation = mutationGeneric;
45
- var query = queryGeneric;
46
63
 
47
- // src/debug.ts
48
- function isGraphPrimitiveDebugEnabled() {
49
- const env = globalThis.process?.env;
50
- return env?.LUCERN_COMPAT_FALLBACK_DEBUG === "1" || env?.LUCERN_GRAPH_DEBUG === "1";
64
+ // src/beliefEvidenceLinks.operational.ts
65
+ function isRecord(value) {
66
+ return typeof value === "object" && value !== null;
51
67
  }
52
- function debugGraphPrimitiveFallback(message, context) {
53
- if (!isGraphPrimitiveDebugEnabled()) {
54
- return;
55
- }
56
- console.debug(message, context ?? {});
68
+ function readConvexId(value) {
69
+ return typeof value === "string" && value.length > 0 ? value : null;
57
70
  }
58
-
59
- // src/topicScope.ts
60
- var LEGACY_SCOPE_FIELD = "graphScopeProjectId";
61
- async function resolveTopicNodeScopeOrNull(ctx, ref) {
62
- if (!ctx?.db || typeof ctx.db.query !== "function") {
71
+ function readEpistemicEdgeDoc(value) {
72
+ if (!isRecord(value)) {
63
73
  return null;
64
74
  }
65
- let node = null;
66
- try {
67
- const byGlobalId = await ctx.db.query("epistemicNodes").withIndex("by_globalId", (q) => q.eq("globalId", ref)).first();
68
- if (byGlobalId && byGlobalId.nodeType === "topic") {
69
- node = byGlobalId;
70
- }
71
- } catch (error) {
72
- debugGraphPrimitiveFallback(
73
- "[topicScope] topic-node scope lookup by globalId failed",
74
- { error, ref }
75
- );
76
- }
77
- if (!node) {
78
- return null;
79
- }
80
- const scopeKey = normalizeScopeValue(node.topicId) ?? normalizeScopeValue(node.globalId);
81
- if (!scopeKey) {
75
+ const id = readConvexId(value._id);
76
+ if (!id || typeof value.edgeType !== "string" || typeof value.globalId !== "string") {
82
77
  return null;
83
78
  }
84
79
  return {
85
- topicId: scopeKey,
86
- projectId: asMappedProjectId(node),
87
- source: "topic_node"
80
+ _id: id,
81
+ edgeType: value.edgeType,
82
+ globalId: value.globalId
88
83
  };
89
84
  }
90
- function asMappedProjectId(topic) {
91
- if (!topic) {
92
- return;
93
- }
94
- const directLegacyProjectId = normalizeScopeValue(topic[LEGACY_SCOPE_FIELD]);
95
- if (directLegacyProjectId) {
96
- return directLegacyProjectId;
97
- }
98
- const metadata = topic.metadata || {};
99
- const candidate = metadata[LEGACY_SCOPE_FIELD] || metadata.legacyProjectId || metadata.projectId || metadata.scopeProjectId;
100
- return candidate ? candidate : void 0;
101
- }
102
- function normalizeScopeValue(value) {
103
- if (typeof value !== "string") {
104
- return;
85
+ function readInsightIdArray(value) {
86
+ if (!Array.isArray(value)) {
87
+ return [];
105
88
  }
106
- const normalized = value.trim();
107
- return normalized.length > 0 ? normalized : void 0;
108
- }
109
- function pickPrimaryTopic(candidates) {
110
- return [...candidates].sort((a, b) => {
111
- const depthA = a.depth ?? 9999;
112
- const depthB = b.depth ?? 9999;
113
- if (depthA !== depthB) {
114
- return depthA - depthB;
115
- }
116
- const createdA = a.createdAt ?? Number.MAX_SAFE_INTEGER;
117
- const createdB = b.createdAt ?? Number.MAX_SAFE_INTEGER;
118
- if (createdA !== createdB) {
119
- return createdA - createdB;
120
- }
121
- return String(a.name || "").localeCompare(String(b.name || ""));
122
- })[0];
89
+ return value.flatMap((entry) => {
90
+ const id = readConvexId(entry);
91
+ return id ? [id] : [];
92
+ });
123
93
  }
124
- async function findTopicsByScopeAlias(ctx, scopeId) {
125
- try {
126
- return await ctx.db.query("topics").withIndex(
127
- "by_graph_scope_project",
128
- (q) => q.eq(LEGACY_SCOPE_FIELD, scopeId)
129
- ).collect();
130
- } catch (error) {
131
- debugGraphPrimitiveFallback(
132
- "[topicScope] Failed to resolve scope alias via index",
133
- {
134
- error,
135
- scopeId
136
- }
137
- );
138
- const topics = await ctx.db.query("topics").collect();
139
- return topics.filter((topic) => {
140
- const normalizedGlobalId = normalizeScopeValue(topic.globalId);
141
- const mappedProjectId = asMappedProjectId(topic);
142
- return String(topic._id) === scopeId || normalizedGlobalId === scopeId || mappedProjectId === scopeId;
143
- });
94
+ function readInsightIdCollections(value) {
95
+ if (!isRecord(value)) {
96
+ return {
97
+ contradictingInsightIds: [],
98
+ supportingInsightIds: []
99
+ };
144
100
  }
101
+ return {
102
+ contradictingInsightIds: readInsightIdArray(value.contradictingInsightIds),
103
+ supportingInsightIds: readInsightIdArray(value.supportingInsightIds)
104
+ };
145
105
  }
146
- async function tryResolveHostTopicById(ctx, topicId) {
147
- if (typeof ctx.runQuery !== "function") {
106
+ function readSpineNodeDoc(value) {
107
+ if (!isRecord(value)) {
148
108
  return null;
149
109
  }
150
- try {
151
- return await ctx.runQuery(api.topics.get, {
152
- id: topicId
153
- }) ?? null;
154
- } catch (error) {
155
- debugGraphPrimitiveFallback(
156
- "[topicScope] Failed to resolve topic by host query",
157
- {
158
- error,
159
- topicId
160
- }
161
- );
110
+ const id = readConvexId(value._id);
111
+ if (!id || typeof value.globalId !== "string" || typeof value.nodeType !== "string") {
162
112
  return null;
163
113
  }
114
+ return {
115
+ _id: id,
116
+ globalId: value.globalId,
117
+ nodeType: value.nodeType
118
+ };
164
119
  }
165
- async function tryResolveHostTopicByLegacyScope(ctx, legacyScopeId) {
166
- if (typeof ctx.runQuery !== "function") {
167
- return null;
168
- }
169
- try {
170
- return await ctx.runQuery(api.topics.getByLegacyScopeId, {
171
- projectId: legacyScopeId
172
- }) ?? null;
173
- } catch (error) {
174
- debugGraphPrimitiveFallback(
175
- "[topicScope] Failed to resolve topic by legacy scope",
176
- {
177
- error,
178
- legacyScopeId
179
- }
120
+ async function insertOperationalEpistemicEdge(ctx, doc) {
121
+ await insertEpistemicEdge(
122
+ ctx,
123
+ doc
124
+ );
125
+ }
126
+ function assertEvidenceImpact(args) {
127
+ if (!Number.isFinite(args.weight) || args.weight === 0 || args.weight < -1 || args.weight > 1) {
128
+ throw new Error(
129
+ "Belief evidence links require explicit nonzero weight in [-1, 1]"
180
130
  );
181
- return null;
182
131
  }
183
- }
184
- async function resolveInheritedWorkspaceScope(ctx, topic) {
185
- const MAX_DEPTH = 10;
186
- let tenantId = normalizeScopeValue(topic.tenantId);
187
- let workspaceId = normalizeScopeValue(topic.workspaceId);
188
- if (tenantId && workspaceId) {
189
- return { tenantId, workspaceId };
132
+ if (args.relation === "supports" && args.weight < 0) {
133
+ throw new Error("Supporting evidence links require positive weight");
190
134
  }
191
- let current = topic;
192
- for (let i = 0; i < MAX_DEPTH && current?.parentTopicId; i++) {
193
- current = await ctx.db.get(current.parentTopicId);
194
- if (!current) break;
195
- if (!tenantId) {
196
- tenantId = normalizeScopeValue(current.tenantId);
197
- }
198
- if (!workspaceId) {
199
- workspaceId = normalizeScopeValue(current.workspaceId);
200
- }
201
- if (tenantId && workspaceId) break;
135
+ if (args.relation === "contradicts" && args.weight > 0) {
136
+ throw new Error("Contradicting evidence links require negative weight");
202
137
  }
203
- return { tenantId, workspaceId };
204
138
  }
205
- async function resolveTopicProjectScope(ctx, args) {
206
- if (args.topicId) {
207
- let topic = null;
208
- try {
209
- topic = await ctx.db.get(
210
- args.topicId
211
- );
212
- } catch (error) {
213
- debugGraphPrimitiveFallback(
214
- "[topicScope] Failed to load topic by direct id",
215
- {
216
- error,
217
- topicId: args.topicId
139
+ async function applyOperationalLinkEffects(ctx, args) {
140
+ assertEvidenceImpact({ relation: args.relation, weight: args.weight });
141
+ const confidence = Math.abs(args.weight);
142
+ const currentSupporting = args.beliefNode.supportingInsightIds ?? [];
143
+ const currentContradicting = args.beliefNode.contradictingInsightIds ?? [];
144
+ if (args.relation === "supports" && !currentSupporting.includes(args.insightId)) {
145
+ await ctx.db.patch(args.beliefId, {
146
+ supportingInsightIds: [...currentSupporting, args.insightId],
147
+ contradictingInsightIds: currentContradicting.filter(
148
+ (id) => id !== args.insightId
149
+ )
150
+ });
151
+ } else if (args.relation === "contradicts" && !currentContradicting.includes(args.insightId)) {
152
+ await ctx.db.patch(args.beliefId, {
153
+ contradictingInsightIds: [...currentContradicting, args.insightId],
154
+ supportingInsightIds: currentSupporting.filter(
155
+ (id) => id !== args.insightId
156
+ )
157
+ });
158
+ }
159
+ try {
160
+ const beliefSpineNode = readSpineNodeDoc(await ctx.db.get(args.beliefId));
161
+ const evidenceSpineNode = readSpineNodeDoc(
162
+ await ctx.db.get(args.insightId)
163
+ );
164
+ if (beliefSpineNode?.nodeType === "belief" && evidenceSpineNode?.nodeType === "evidence") {
165
+ const existingEdges = (await ctx.db.query("epistemicEdges").withIndex(
166
+ "by_from_to",
167
+ (q) => q.eq("fromNodeId", evidenceSpineNode.globalId).eq("toNodeId", beliefSpineNode.globalId)
168
+ ).collect()).map(readEpistemicEdgeDoc).filter((edge) => edge !== null);
169
+ for (const edge of existingEdges) {
170
+ if (edge.edgeType === "informs") {
171
+ await ctx.scheduler.runAfter(0, internal.neo4jEdgeAPI.deleteEdge, {
172
+ globalId: edge.globalId
173
+ });
174
+ await ctx.db.delete(edge._id);
218
175
  }
219
- );
220
- }
221
- if (!topic) {
222
- topic = await tryResolveHostTopicById(ctx, String(args.topicId));
223
- }
224
- if (!topic) {
225
- topic = pickPrimaryTopic(
226
- await findTopicsByScopeAlias(ctx, String(args.topicId))
227
- ) ?? null;
228
- }
229
- if (!topic) {
230
- const nodeScope = await resolveTopicNodeScopeOrNull(
231
- ctx,
232
- String(args.topicId)
233
- );
234
- if (nodeScope) {
235
- return nodeScope;
236
176
  }
237
- throw new Error(`Topic not found: ${String(args.topicId)}`);
238
- }
239
- const inherited = await resolveInheritedWorkspaceScope(ctx, topic);
240
- const mapped = asMappedProjectId(topic);
241
- if (mapped) {
242
- return {
243
- topicId: topic._id,
244
- projectId: mapped,
245
- tenantId: inherited.tenantId,
246
- workspaceId: inherited.workspaceId,
247
- source: "topic"
248
- };
177
+ const globalId = generateGlobalId();
178
+ const context = args.rationale || `Linked as ${args.relation}`;
179
+ await insertOperationalEpistemicEdge(ctx, {
180
+ globalId,
181
+ // C2-RR.4 Defect E — canonical UUIDv7 endpoints, not Convex doc ids.
182
+ fromNodeId: evidenceSpineNode.globalId,
183
+ toNodeId: beliefSpineNode.globalId,
184
+ sourceGlobalId: evidenceSpineNode.globalId,
185
+ targetGlobalId: beliefSpineNode.globalId,
186
+ edgeType: "informs",
187
+ weight: args.weight,
188
+ confidence,
189
+ context,
190
+ reasoningMethod: "testimonial",
191
+ derivationType: "evidence_sl_scoring",
192
+ metadata: {
193
+ relation: args.relation,
194
+ confidence,
195
+ impactScore: args.weight,
196
+ invariant: "evidence.belief_impact_required"
197
+ },
198
+ createdBy: args.createdBy,
199
+ createdAt: Date.now(),
200
+ updatedAt: Date.now(),
201
+ projectId: args.beliefNode.projectId,
202
+ topicId: args.beliefNode.topicId ? String(args.beliefNode.topicId) : void 0,
203
+ fromNodeType: "evidence",
204
+ toNodeType: "belief",
205
+ fromLayer: "L2",
206
+ toLayer: "L3"
207
+ });
208
+ await ctx.scheduler.runAfter(0, internal.neo4jEdgeAPI.createEdge, {
209
+ globalId,
210
+ fromGlobalId: evidenceSpineNode.globalId,
211
+ toGlobalId: beliefSpineNode.globalId,
212
+ edgeType: "informs",
213
+ weight: args.weight,
214
+ confidence,
215
+ context,
216
+ projectId: args.beliefNode.projectId ? String(args.beliefNode.projectId) : void 0,
217
+ createdBy: args.createdBy,
218
+ fromNodeType: "evidence",
219
+ toNodeType: "belief",
220
+ fromLayer: "L2",
221
+ toLayer: "L3"
222
+ });
249
223
  }
250
- return {
251
- topicId: topic._id,
252
- tenantId: inherited.tenantId,
253
- workspaceId: inherited.workspaceId,
254
- source: "topic"
255
- };
224
+ } catch (e) {
225
+ console.error("[EpistemicSpine] Failed to create informs edge:", e);
256
226
  }
257
- if (args.projectId) {
258
- let directTopic = null;
259
- try {
260
- directTopic = await ctx.db.get(
261
- args.projectId
262
- );
263
- } catch (error) {
264
- debugGraphPrimitiveFallback(
265
- "[topicScope] Failed to load direct project topic",
266
- {
267
- error,
268
- projectId: args.projectId
269
- }
270
- );
271
- }
272
- if (directTopic) {
273
- const inherited = await resolveInheritedWorkspaceScope(ctx, directTopic);
274
- const mapped = asMappedProjectId(directTopic);
275
- return {
276
- topicId: directTopic._id,
277
- projectId: mapped ?? args.projectId,
278
- tenantId: inherited.tenantId,
279
- workspaceId: inherited.workspaceId,
280
- source: "topic_inferred"
281
- };
282
- }
283
- directTopic = await tryResolveHostTopicByLegacyScope(ctx, args.projectId);
284
- if (directTopic) {
285
- const inherited = await resolveInheritedWorkspaceScope(ctx, directTopic);
286
- const mapped = asMappedProjectId(directTopic);
287
- return {
288
- topicId: directTopic._id,
289
- projectId: mapped ?? args.projectId,
290
- tenantId: inherited.tenantId,
291
- workspaceId: inherited.workspaceId,
292
- source: "topic_inferred"
293
- };
294
- }
295
- const topics = await findTopicsByScopeAlias(ctx, args.projectId);
296
- const primary = pickPrimaryTopic(topics);
297
- if (primary) {
298
- const inherited = await resolveInheritedWorkspaceScope(ctx, primary);
299
- return {
300
- topicId: primary._id,
301
- projectId: args.projectId,
302
- tenantId: inherited.tenantId,
303
- workspaceId: inherited.workspaceId,
304
- source: "project_mapped_topic"
305
- };
227
+ if (args.beliefNode.projectId) {
228
+ await ctx.scheduler.runAfter(0, "verificationActions:deepVerifyEvidence", {
229
+ insightId: args.insightId,
230
+ targetType: "belief",
231
+ targetId: args.beliefId,
232
+ projectId: args.beliefNode.projectId,
233
+ userId: args.createdBy
234
+ });
235
+ }
236
+ }
237
+ async function removeOperationalLinkEffects(ctx, args) {
238
+ const belief = await ctx.db.get(args.beliefId);
239
+ if (belief) {
240
+ const { contradictingInsightIds, supportingInsightIds } = readInsightIdCollections(belief);
241
+ if (args.relation === "supports") {
242
+ await ctx.db.patch(args.beliefId, {
243
+ supportingInsightIds: supportingInsightIds.filter(
244
+ (id) => id !== args.insightId
245
+ )
246
+ });
247
+ } else {
248
+ await ctx.db.patch(args.beliefId, {
249
+ contradictingInsightIds: contradictingInsightIds.filter(
250
+ (id) => id !== args.insightId
251
+ )
252
+ });
306
253
  }
307
- const nodeScope = await resolveTopicNodeScopeOrNull(
308
- ctx,
309
- String(args.projectId)
254
+ }
255
+ try {
256
+ const beliefSpineNode = readSpineNodeDoc(await ctx.db.get(args.beliefId));
257
+ const evidenceSpineNode = readSpineNodeDoc(
258
+ await ctx.db.get(args.insightId)
310
259
  );
311
- if (nodeScope) {
312
- return {
313
- ...nodeScope,
314
- projectId: nodeScope.projectId ?? String(args.projectId)
315
- };
260
+ if (beliefSpineNode?.nodeType === "belief" && evidenceSpineNode?.nodeType === "evidence") {
261
+ const edges = (await ctx.db.query("epistemicEdges").withIndex(
262
+ "by_from_to",
263
+ (q) => q.eq("fromNodeId", evidenceSpineNode._id).eq("toNodeId", beliefSpineNode._id)
264
+ ).collect()).map(readEpistemicEdgeDoc).filter((edge) => edge !== null);
265
+ for (const edge of edges) {
266
+ if (edge.edgeType === "informs") {
267
+ await ctx.scheduler.runAfter(0, internal.neo4jEdgeAPI.deleteEdge, {
268
+ globalId: edge.globalId
269
+ });
270
+ await ctx.db.delete(edge._id);
271
+ }
272
+ }
316
273
  }
317
- throw new Error(
318
- `Legacy project scope ${String(args.projectId)} has no mapped topic.`
319
- );
274
+ } catch (e) {
275
+ console.error("[EpistemicSpine] Failed to remove informs edge:", e);
320
276
  }
321
- throw new Error(
322
- "Missing scope: provide topicId (preferred) or legacy projectId alias."
323
- );
324
277
  }
325
- var optionalScopeArgs = {
326
- projectId: v.optional(v.string()),
327
- topicId: v.optional(v.string())
328
- };
329
278
 
330
- // src/scopeResolverCompat.ts
331
- var scopeArgs = optionalScopeArgs;
332
- function normalizeScopeValue2(value) {
333
- if (typeof value !== "string") {
334
- return;
279
+ // src/matcherFeedbackUtils.ts
280
+ function isOperationalLinkStatus(status) {
281
+ return status !== "suggested" && status !== "dismissed";
282
+ }
283
+ function mergeLinkSuggestionStatus(existing, requested) {
284
+ if (requested === void 0 || requested === null) {
285
+ return existing ?? void 0;
335
286
  }
336
- const normalized = value.trim();
337
- return normalized.length > 0 ? normalized : void 0;
287
+ if (existing && isOperationalLinkStatus(existing) && !isOperationalLinkStatus(requested)) {
288
+ return existing;
289
+ }
290
+ return requested;
338
291
  }
339
- async function resolveScope(ctx, args) {
340
- const topicId = normalizeScopeValue2(args.topicId);
341
- const projectId = normalizeScopeValue2(args.projectId);
342
- if (!topicId && !projectId) {
343
- return null;
292
+ function resolveReviewedLinkStatus(requested) {
293
+ return requested;
294
+ }
295
+ function deriveMatcherReviewStatus(input) {
296
+ if (input.explicitReviewStatus) {
297
+ return input.explicitReviewStatus;
344
298
  }
345
- try {
346
- return await resolveTopicProjectScope(ctx, {
347
- topicId,
348
- projectId
349
- });
350
- } catch (error) {
351
- debugGraphPrimitiveFallback(
352
- "[scopeResolverCompat] Failed to resolve scope",
353
- {
354
- error,
355
- topicId,
356
- projectId
357
- }
358
- );
359
- return null;
299
+ if (input.autoAccepted) {
300
+ return "auto_accepted";
301
+ }
302
+ if (input.linkStatus === "suggested") {
303
+ return "pending";
304
+ }
305
+ if (input.linkStatus === "dismissed") {
306
+ return "rejected";
360
307
  }
308
+ return "accepted";
361
309
  }
362
- async function resolveScopeSoft(ctx, args) {
363
- const resolved = await resolveScope(ctx, args);
364
- if (resolved) {
365
- const topicId2 = normalizeScopeValue2(resolved.topicId);
366
- return {
367
- ...topicId2 ? { topicId: topicId2 } : {},
368
- ...resolved.projectId ? { projectId: resolved.projectId } : {},
369
- ...resolved.tenantId ? { tenantId: resolved.tenantId } : {},
370
- ...resolved.workspaceId ? { workspaceId: resolved.workspaceId } : {},
371
- ...resolved.source ? { source: resolved.source } : {}
372
- };
310
+
311
+ // src/debug.ts
312
+ function isGraphPrimitiveDebugEnabled() {
313
+ const env = globalThis.process?.env;
314
+ return env?.LUCERN_COMPAT_FALLBACK_DEBUG === "1" || env?.LUCERN_GRAPH_DEBUG === "1";
315
+ }
316
+ function debugGraphPrimitiveFallback(message, context) {
317
+ if (!isGraphPrimitiveDebugEnabled()) {
318
+ return;
373
319
  }
374
- const topicId = normalizeScopeValue2(args.topicId);
375
- const projectId = normalizeScopeValue2(args.projectId);
376
- return {
377
- ...topicId ? { topicId } : {},
378
- ...projectId ? { projectId } : {}
379
- };
320
+ console.debug(message, context ?? {});
380
321
  }
381
322
 
382
323
  // src/topicProjectOverlay.ts
383
- var LEGACY_SCOPE_FIELD2 = "graphScopeProjectId";
324
+ var LEGACY_SCOPE_FIELD = "graphScopeProjectId";
384
325
  function readNonEmptyString(value) {
385
326
  if (typeof value !== "string") {
386
327
  return;
@@ -397,11 +338,15 @@ function readStringArray(value) {
397
338
  function readMetadata(topic) {
398
339
  return topic.metadata && typeof topic.metadata === "object" ? topic.metadata : {};
399
340
  }
341
+ function omitMetadataKey(metadata, key) {
342
+ const { [key]: _omitted, ...rest } = metadata;
343
+ return rest;
344
+ }
400
345
  function readLegacyProjectId(value) {
401
346
  if (!value) {
402
347
  return;
403
348
  }
404
- return readNonEmptyString(value[LEGACY_SCOPE_FIELD2]);
349
+ return readNonEmptyString(value[LEGACY_SCOPE_FIELD]);
405
350
  }
406
351
  function coerceVisibility(value) {
407
352
  return value === "private" || value === "team" || value === "firm" || value === "external" || value === "public" ? value : void 0;
@@ -477,9 +422,12 @@ async function resolveTopicDoc(ctx, scopeId) {
477
422
  );
478
423
  }
479
424
  try {
480
- const topic = await ctx.runQuery(api.topics.getByLegacyScopeId, {
481
- projectId: String(scopeId)
482
- });
425
+ const topic = await ctx.runQuery(
426
+ api.topics.getByLegacyScopeId,
427
+ {
428
+ projectId: String(scopeId)
429
+ }
430
+ );
483
431
  if (topic?.name !== void 0 && topic?.type !== void 0) {
484
432
  return topic;
485
433
  }
@@ -499,8 +447,18 @@ function materializeTopicProjectOverlay(topic, idMode = "legacy") {
499
447
  const outwardId = idMode === "topic" ? topicId : storageProjectId;
500
448
  const visibility = coerceVisibility(topic.visibility) || coerceVisibility(metadata.visibility) || "private";
501
449
  const status = coerceStatus(topic.status) || coerceStatus(metadata.status) || "active";
502
- const createdAt = typeof topic.createdAt === "number" ? topic.createdAt : typeof topic._creationTime === "number" ? topic._creationTime : 0;
503
- const updatedAt = typeof topic.updatedAt === "number" ? topic.updatedAt : typeof metadata.updatedAt === "number" ? metadata.updatedAt : createdAt;
450
+ let createdAt = 0;
451
+ if (typeof topic.createdAt === "number") {
452
+ createdAt = topic.createdAt;
453
+ } else if (typeof topic._creationTime === "number") {
454
+ createdAt = topic._creationTime;
455
+ }
456
+ let updatedAt = createdAt;
457
+ if (typeof topic.updatedAt === "number") {
458
+ updatedAt = topic.updatedAt;
459
+ } else if (typeof metadata.updatedAt === "number") {
460
+ updatedAt = metadata.updatedAt;
461
+ }
504
462
  return {
505
463
  ...metadata,
506
464
  _id: outwardId,
@@ -569,90 +527,113 @@ async function patchTopicProjectOverlay(ctx, scopeId, value) {
569
527
  if (!topic) {
570
528
  return null;
571
529
  }
572
- const nextMetadata = { ...readMetadata(topic) };
573
- const patch = {};
574
- const topicUpdateArgs = {
575
- id: String(topic._id)
530
+ const plan = buildTopicProjectOverlayPatchPlan(topic, value);
531
+ await applyTopicProjectOverlayPatch(ctx, topic, plan);
532
+ return materializeTopicProjectOverlay({
533
+ ...topic,
534
+ ...plan.patch,
535
+ metadata: plan.nextMetadata
536
+ });
537
+ }
538
+ function buildTopicProjectOverlayPatchPlan(topic, value) {
539
+ const plan = {
540
+ nextMetadata: { ...readMetadata(topic) },
541
+ patch: {},
542
+ topicUpdateArgs: {
543
+ id: String(topic._id)
544
+ }
576
545
  };
577
546
  for (const [key, rawValue] of Object.entries(value)) {
578
- switch (key) {
579
- case "_id":
580
- case "projectId":
581
- case "topicId":
582
- case "legacyProjectId":
583
- case "storageProjectId":
584
- break;
585
- case "name":
586
- case "description":
587
- patch[key] = rawValue;
588
- topicUpdateArgs[key] = rawValue;
589
- break;
590
- case "tenantId":
591
- case "workspaceId":
592
- case "ownerId":
593
- throw new Error(
594
- `patchTopicProjectOverlay cannot mutate ${key} via component-owned topics`
595
- );
596
- case "status": {
597
- const status = coerceStatus(rawValue);
598
- if (status) {
599
- patch.status = status;
600
- topicUpdateArgs.status = status;
601
- }
602
- break;
603
- }
604
- case "visibility": {
605
- const visibility = coerceVisibility(rawValue);
606
- if (visibility) {
607
- patch.visibility = visibility;
608
- topicUpdateArgs.visibility = visibility;
609
- }
610
- break;
611
- }
612
- case "type": {
613
- const projectType = readNonEmptyString(rawValue);
614
- if (projectType) {
615
- nextMetadata.projectType = projectType;
616
- } else {
617
- delete nextMetadata.projectType;
618
- }
619
- break;
620
- }
621
- case "updatedAt":
622
- case "createdAt":
623
- break;
624
- default:
625
- if (rawValue === void 0) {
626
- delete nextMetadata[key];
627
- } else {
628
- nextMetadata[key] = rawValue;
629
- }
630
- }
547
+ applyTopicProjectOverlayPatchEntry(plan, key, rawValue);
548
+ }
549
+ plan.patch.updatedAt = Date.now();
550
+ plan.patch.metadata = plan.nextMetadata;
551
+ plan.topicUpdateArgs.metadata = plan.nextMetadata;
552
+ return plan;
553
+ }
554
+ function applyTopicProjectOverlayPatchEntry(plan, key, rawValue) {
555
+ switch (key) {
556
+ case "_id":
557
+ case "projectId":
558
+ case "topicId":
559
+ case "legacyProjectId":
560
+ case "storageProjectId":
561
+ case "updatedAt":
562
+ case "createdAt":
563
+ return;
564
+ case "name":
565
+ case "description":
566
+ plan.patch[key] = rawValue;
567
+ plan.topicUpdateArgs[key] = rawValue;
568
+ return;
569
+ case "tenantId":
570
+ case "workspaceId":
571
+ case "ownerId":
572
+ throw new Error(
573
+ `patchTopicProjectOverlay cannot mutate ${key} via component-owned topics`
574
+ );
575
+ case "status":
576
+ applyTopicStatusPatch(plan, rawValue);
577
+ return;
578
+ case "visibility":
579
+ applyTopicVisibilityPatch(plan, rawValue);
580
+ return;
581
+ case "type":
582
+ applyTopicProjectTypePatch(plan, rawValue);
583
+ return;
584
+ default:
585
+ applyTopicMetadataPatch(plan, key, rawValue);
586
+ }
587
+ }
588
+ function applyTopicStatusPatch(plan, rawValue) {
589
+ const status = coerceStatus(rawValue);
590
+ if (status) {
591
+ plan.patch.status = status;
592
+ plan.topicUpdateArgs.status = status;
593
+ }
594
+ }
595
+ function applyTopicVisibilityPatch(plan, rawValue) {
596
+ const visibility = coerceVisibility(rawValue);
597
+ if (visibility) {
598
+ plan.patch.visibility = visibility;
599
+ plan.topicUpdateArgs.visibility = visibility;
600
+ }
601
+ }
602
+ function applyTopicProjectTypePatch(plan, rawValue) {
603
+ const projectType = readNonEmptyString(rawValue);
604
+ if (projectType) {
605
+ plan.nextMetadata.projectType = projectType;
606
+ return;
607
+ }
608
+ plan.nextMetadata = omitMetadataKey(plan.nextMetadata, "projectType");
609
+ }
610
+ function applyTopicMetadataPatch(plan, key, rawValue) {
611
+ if (rawValue === void 0) {
612
+ plan.nextMetadata = omitMetadataKey(plan.nextMetadata, key);
613
+ return;
631
614
  }
632
- patch.updatedAt = Date.now();
633
- patch.metadata = nextMetadata;
634
- topicUpdateArgs.metadata = nextMetadata;
615
+ plan.nextMetadata[key] = rawValue;
616
+ }
617
+ async function applyTopicProjectOverlayPatch(ctx, topic, plan) {
635
618
  if (typeof ctx.runMutation === "function") {
636
619
  try {
637
- await ctx.runMutation(api.topics.update, topicUpdateArgs);
620
+ await ctx.runMutation(api.topics.update, plan.topicUpdateArgs);
638
621
  } catch (error) {
639
- if (!isMissingLucernChildComponentError(error) || !ctx?.db || typeof ctx.db.patch !== "function") {
622
+ if (!canPatchTopicViaLocalDb(ctx, error)) {
640
623
  throw error;
641
624
  }
642
- await ctx.db.patch(String(topic._id), patch);
625
+ await ctx.db.patch(topic._id, plan.patch);
643
626
  }
644
627
  } else if (ctx?.db && typeof ctx.db.patch === "function") {
645
- await ctx.db.patch(String(topic._id), patch);
628
+ await ctx.db.patch(topic._id, plan.patch);
646
629
  } else {
647
630
  throw new Error(
648
631
  "Cannot patch topic without component adapter (ctx.runMutation unavailable)"
649
632
  );
650
633
  }
651
- return materializeTopicProjectOverlay({
652
- ...topic,
653
- ...patch,
654
- metadata: nextMetadata
655
- });
634
+ }
635
+ function canPatchTopicViaLocalDb(ctx, error) {
636
+ return isMissingLucernChildComponentError(error) && Boolean(ctx?.db) && typeof ctx.db?.patch === "function";
656
637
  }
657
638
 
658
639
  // src/resolvers.ts
@@ -680,7 +661,7 @@ async function patchProjectWithTolerance(ctx, projectId, value) {
680
661
  try {
681
662
  await patchTopicProjectOverlay(ctx, projectId, value);
682
663
  } catch (error) {
683
- if (!isAdvisoryTopicPatch(value) || !isMissingLucernChildComponentError2(error)) {
664
+ if (!(isAdvisoryTopicPatch(value) && isMissingLucernChildComponentError2(error))) {
684
665
  throw error;
685
666
  }
686
667
  console.warn(
@@ -713,196 +694,307 @@ function resolveGraphPrimitivesAppResolvers(_ctx) {
713
694
  ...resolverOverrides
714
695
  };
715
696
  }
716
- async function assertExistingNodeEndpoint(ctx, endpointRole, endpoint) {
717
- assertUuidShapedEdgeEndpoint(endpointRole, endpoint);
718
- const node = await ctx.db.query("epistemicNodes").withIndex("by_globalId", (q) => q.eq("globalId", endpoint)).first();
719
- if (!node) {
720
- throw new Error(
721
- `edge_endpoint_not_canonical: epistemicEdges insert requires ${endpointRole} to be the globalId of an existing epistemicNodes row, received ${endpoint} (no node with that globalId)`
697
+ var LEGACY_SCOPE_FIELD2 = "graphScopeProjectId";
698
+ async function resolveTopicNodeScopeOrNull(ctx, ref) {
699
+ if (!ctx?.db || typeof ctx.db.query !== "function") {
700
+ return null;
701
+ }
702
+ let node = null;
703
+ try {
704
+ const byGlobalId = await ctx.db.query("epistemicNodes").withIndex("by_globalId", (q) => q.eq("globalId", ref)).first();
705
+ if (byGlobalId && byGlobalId.nodeType === "topic") {
706
+ node = byGlobalId;
707
+ }
708
+ } catch (error) {
709
+ debugGraphPrimitiveFallback(
710
+ "[topicScope] topic-node scope lookup by globalId failed",
711
+ { error, ref }
722
712
  );
723
713
  }
714
+ if (!node) {
715
+ return null;
716
+ }
717
+ const scopeKey = normalizeScopeValue(node.topicId) ?? normalizeScopeValue(node.globalId);
718
+ if (!scopeKey) {
719
+ return null;
720
+ }
721
+ return {
722
+ topicId: scopeKey,
723
+ projectId: asMappedProjectId(node),
724
+ source: "topic_node"
725
+ };
724
726
  }
725
- async function insertEpistemicEdge(ctx, doc) {
726
- assertUuidV7Identity("epistemicEdges", doc.globalId);
727
- assertStorageEdgeVocabulary(doc.edgeType);
728
- if (!doc.fromNodeId || typeof doc.fromNodeId !== "string") {
729
- throw new Error(
730
- "edge_endpoint_missing: epistemicEdges insert requires a non-empty fromNodeId"
731
- );
727
+ function asMappedProjectId(topic) {
728
+ if (!topic) {
729
+ return;
732
730
  }
733
- if (!doc.toNodeId || typeof doc.toNodeId !== "string") {
734
- throw new Error(
735
- "edge_endpoint_missing: epistemicEdges insert requires a non-empty toNodeId"
731
+ const directLegacyProjectId = normalizeScopeValue(
732
+ topic[LEGACY_SCOPE_FIELD2]
733
+ );
734
+ if (directLegacyProjectId) {
735
+ return directLegacyProjectId;
736
+ }
737
+ const metadata = topic.metadata || {};
738
+ const candidate = metadata[LEGACY_SCOPE_FIELD2] || metadata.legacyProjectId || metadata.projectId || metadata.scopeProjectId;
739
+ return typeof candidate === "string" ? normalizeScopeValue(candidate) : void 0;
740
+ }
741
+ function normalizeScopeValue(value) {
742
+ if (typeof value !== "string") {
743
+ return;
744
+ }
745
+ const normalized = value.trim();
746
+ return normalized.length > 0 ? normalized : void 0;
747
+ }
748
+ function pickPrimaryTopic(candidates) {
749
+ return [...candidates].sort((a, b) => {
750
+ const depthA = a.depth ?? 9999;
751
+ const depthB = b.depth ?? 9999;
752
+ if (depthA !== depthB) {
753
+ return depthA - depthB;
754
+ }
755
+ const createdA = a.createdAt ?? Number.MAX_SAFE_INTEGER;
756
+ const createdB = b.createdAt ?? Number.MAX_SAFE_INTEGER;
757
+ if (createdA !== createdB) {
758
+ return createdA - createdB;
759
+ }
760
+ return String(a.name || "").localeCompare(String(b.name || ""));
761
+ })[0];
762
+ }
763
+ async function findTopicsByScopeAlias(ctx, scopeId) {
764
+ const query2 = ctx.db.query("topics");
765
+ try {
766
+ return await query2.withIndex(
767
+ "by_graph_scope_project",
768
+ (q) => q.eq(LEGACY_SCOPE_FIELD2, scopeId)
769
+ ).collect();
770
+ } catch (error) {
771
+ debugGraphPrimitiveFallback(
772
+ "[topicScope] Failed to resolve scope alias via index",
773
+ {
774
+ error,
775
+ scopeId
776
+ }
736
777
  );
778
+ const topics = await query2.collect();
779
+ return topics.filter((topic) => {
780
+ const normalizedGlobalId = normalizeScopeValue(topic.globalId);
781
+ const mappedProjectId = asMappedProjectId(topic);
782
+ return String(topic._id) === scopeId || normalizedGlobalId === scopeId || mappedProjectId === scopeId;
783
+ });
737
784
  }
738
- await assertExistingNodeEndpoint(ctx, "fromNodeId", doc.fromNodeId);
739
- await assertExistingNodeEndpoint(ctx, "toNodeId", doc.toNodeId);
740
- if (doc.fromNodeType && doc.toNodeType && doc.edgeType !== "extracted_from") {
741
- assertEdgePolicyAllowed(
742
- edgePolicyManifest,
743
- doc.edgeType,
785
+ }
786
+ async function tryResolveHostTopicById(ctx, topicId) {
787
+ if (typeof ctx.runQuery !== "function") {
788
+ return null;
789
+ }
790
+ try {
791
+ return await ctx.runQuery(api.topics.get, {
792
+ id: topicId
793
+ }) ?? null;
794
+ } catch (error) {
795
+ debugGraphPrimitiveFallback(
796
+ "[topicScope] Failed to resolve topic by host query",
744
797
  {
745
- kind: "epistemic_node",
746
- nodeId: doc.fromNodeId,
747
- nodeType: doc.fromNodeType
748
- },
798
+ error,
799
+ topicId
800
+ }
801
+ );
802
+ return null;
803
+ }
804
+ }
805
+ async function tryResolveHostTopicByLegacyScope(ctx, legacyScopeId) {
806
+ if (typeof ctx.runQuery !== "function") {
807
+ return null;
808
+ }
809
+ try {
810
+ return await ctx.runQuery(api.topics.getByLegacyScopeId, {
811
+ projectId: legacyScopeId
812
+ }) ?? null;
813
+ } catch (error) {
814
+ debugGraphPrimitiveFallback(
815
+ "[topicScope] Failed to resolve topic by legacy scope",
749
816
  {
750
- kind: "epistemic_node",
751
- nodeId: doc.toNodeId,
752
- nodeType: doc.toNodeType
817
+ error,
818
+ legacyScopeId
753
819
  }
754
820
  );
821
+ return null;
822
+ }
823
+ }
824
+ async function resolveInheritedWorkspaceScope(ctx, topic) {
825
+ const MAX_DEPTH = 10;
826
+ let tenantId = normalizeScopeValue(topic.tenantId);
827
+ let workspaceId = normalizeScopeValue(topic.workspaceId);
828
+ if (tenantId && workspaceId) {
829
+ return { tenantId, workspaceId };
830
+ }
831
+ let current = topic;
832
+ for (let i = 0; i < MAX_DEPTH && current?.parentTopicId; i++) {
833
+ current = await ctx.db.get(current.parentTopicId);
834
+ if (!current) {
835
+ break;
836
+ }
837
+ if (!tenantId) {
838
+ tenantId = normalizeScopeValue(current.tenantId);
839
+ }
840
+ if (!workspaceId) {
841
+ workspaceId = normalizeScopeValue(current.workspaceId);
842
+ }
843
+ if (tenantId && workspaceId) {
844
+ break;
845
+ }
755
846
  }
756
- return ctx.db.insert("epistemicEdges", doc);
847
+ return { tenantId, workspaceId };
757
848
  }
758
-
759
- // src/beliefEvidenceLinks.operational.ts
760
- function assertEvidenceImpact(args) {
761
- if (!Number.isFinite(args.weight) || args.weight === 0 || args.weight < -1 || args.weight > 1) {
762
- throw new Error("Belief evidence links require explicit nonzero weight in [-1, 1]");
849
+ async function resolveTopicProjectScope(ctx, args) {
850
+ if (args.topicId) {
851
+ return await resolveScopeFromTopicId(ctx, args.topicId);
763
852
  }
764
- if (args.relation === "supports" && args.weight < 0) {
765
- throw new Error("Supporting evidence links require positive weight");
853
+ if (args.projectId) {
854
+ return await resolveScopeFromLegacyProjectId(ctx, args.projectId);
766
855
  }
767
- if (args.relation === "contradicts" && args.weight > 0) {
768
- throw new Error("Contradicting evidence links require negative weight");
856
+ throw new Error(
857
+ "Missing scope: provide topicId (preferred) or legacy projectId alias."
858
+ );
859
+ }
860
+ async function resolveScopeFromTopicId(ctx, topicId) {
861
+ const topic = await resolveTopicDocFromTopicId(ctx, topicId);
862
+ if (topic) {
863
+ return await buildTopicScope(ctx, topic, "topic");
769
864
  }
865
+ const nodeScope = await resolveTopicNodeScopeOrNull(ctx, String(topicId));
866
+ if (nodeScope) {
867
+ return nodeScope;
868
+ }
869
+ throw new Error(`Topic not found: ${String(topicId)}`);
770
870
  }
771
- async function applyOperationalLinkEffects(ctx, args) {
772
- assertEvidenceImpact({ relation: args.relation, weight: args.weight });
773
- const confidence = Math.abs(args.weight);
774
- const currentSupporting = args.beliefNode.supportingInsightIds ?? [];
775
- const currentContradicting = args.beliefNode.contradictingInsightIds ?? [];
776
- if (args.relation === "supports" && !currentSupporting.includes(args.insightId)) {
777
- await ctx.db.patch(args.beliefId, {
778
- supportingInsightIds: [...currentSupporting, args.insightId],
779
- contradictingInsightIds: currentContradicting.filter(
780
- (id) => id !== args.insightId
781
- )
871
+ async function resolveTopicDocFromTopicId(ctx, topicId) {
872
+ const direct = await tryReadTopicDoc(ctx, topicId, {
873
+ failureLog: "[topicScope] Failed to load topic by direct id",
874
+ idLogKey: "topicId"
875
+ });
876
+ if (direct) {
877
+ return direct;
878
+ }
879
+ const hostTopic = await tryResolveHostTopicById(ctx, String(topicId));
880
+ if (hostTopic) {
881
+ return hostTopic;
882
+ }
883
+ return pickPrimaryTopic(await findTopicsByScopeAlias(ctx, String(topicId))) ?? null;
884
+ }
885
+ async function resolveScopeFromLegacyProjectId(ctx, legacyProjectId) {
886
+ const directTopic = await resolveDirectLegacyProjectTopic(
887
+ ctx,
888
+ legacyProjectId
889
+ );
890
+ if (directTopic) {
891
+ return await buildTopicScope(ctx, directTopic, "topic_inferred", {
892
+ fallbackProjectId: legacyProjectId
782
893
  });
783
- } else if (args.relation === "contradicts" && !currentContradicting.includes(args.insightId)) {
784
- await ctx.db.patch(args.beliefId, {
785
- contradictingInsightIds: [...currentContradicting, args.insightId],
786
- supportingInsightIds: currentSupporting.filter(
787
- (id) => id !== args.insightId
788
- )
894
+ }
895
+ const primary = pickPrimaryTopic(
896
+ await findTopicsByScopeAlias(ctx, legacyProjectId)
897
+ );
898
+ if (primary) {
899
+ return await buildTopicScope(ctx, primary, "project_mapped_topic", {
900
+ fallbackProjectId: legacyProjectId
789
901
  });
790
902
  }
791
- try {
792
- const beliefSpineNode = await ctx.db.get(args.beliefId);
793
- const evidenceSpineNode = await ctx.db.get(args.insightId);
794
- if (beliefSpineNode?.nodeType === "belief" && evidenceSpineNode?.nodeType === "evidence") {
795
- const existingEdges = await ctx.db.query("epistemicEdges").withIndex(
796
- "by_from_to",
797
- (q) => q.eq("fromNodeId", evidenceSpineNode.globalId).eq("toNodeId", beliefSpineNode.globalId)
798
- ).collect();
799
- for (const edge of existingEdges) {
800
- if (edge.edgeType === "informs") {
801
- await ctx.scheduler.runAfter(0, internal.neo4jEdgeAPI.deleteEdge, {
802
- globalId: edge.globalId
803
- });
804
- await ctx.db.delete(edge._id);
805
- }
806
- }
807
- const globalId = generateGlobalId();
808
- const context = args.rationale || `Linked as ${args.relation}`;
809
- await insertEpistemicEdge(ctx, {
810
- globalId,
811
- // C2-RR.4 Defect E — canonical UUIDv7 endpoints, not Convex doc ids.
812
- fromNodeId: evidenceSpineNode.globalId,
813
- toNodeId: beliefSpineNode.globalId,
814
- sourceGlobalId: evidenceSpineNode.globalId,
815
- targetGlobalId: beliefSpineNode.globalId,
816
- edgeType: "informs",
817
- weight: args.weight,
818
- confidence,
819
- context,
820
- reasoningMethod: "testimonial",
821
- derivationType: "evidence_sl_scoring",
822
- metadata: {
823
- relation: args.relation,
824
- confidence,
825
- impactScore: args.weight,
826
- invariant: "evidence.belief_impact_required"
827
- },
828
- createdBy: args.createdBy,
829
- createdAt: Date.now(),
830
- updatedAt: Date.now(),
831
- projectId: args.beliefNode.projectId,
832
- topicId: args.beliefNode.topicId ? String(args.beliefNode.topicId) : void 0,
833
- fromNodeType: "evidence",
834
- toNodeType: "belief",
835
- fromLayer: "L2",
836
- toLayer: "L3"
837
- });
838
- await ctx.scheduler.runAfter(0, internal.neo4jEdgeAPI.createEdge, {
839
- globalId,
840
- fromGlobalId: evidenceSpineNode.globalId,
841
- toGlobalId: beliefSpineNode.globalId,
842
- edgeType: "informs",
843
- weight: args.weight,
844
- confidence,
845
- context,
846
- projectId: args.beliefNode.projectId ? String(args.beliefNode.projectId) : void 0,
847
- createdBy: args.createdBy,
848
- fromNodeType: "evidence",
849
- toNodeType: "belief",
850
- fromLayer: "L2",
851
- toLayer: "L3"
852
- });
853
- }
854
- } catch (e) {
855
- console.error("[EpistemicSpine] Failed to create informs edge:", e);
903
+ const nodeScope = await resolveTopicNodeScopeOrNull(ctx, legacyProjectId);
904
+ if (nodeScope) {
905
+ return {
906
+ ...nodeScope,
907
+ projectId: nodeScope.projectId ?? legacyProjectId
908
+ };
856
909
  }
857
- if (args.beliefNode.projectId) {
858
- await ctx.scheduler.runAfter(0, "verificationActions:deepVerifyEvidence", {
859
- insightId: args.insightId,
860
- targetType: "belief",
861
- targetId: args.beliefId,
862
- projectId: args.beliefNode.projectId,
863
- userId: args.createdBy
910
+ throw new Error(
911
+ `Legacy project scope ${legacyProjectId} has no mapped topic.`
912
+ );
913
+ }
914
+ async function resolveDirectLegacyProjectTopic(ctx, legacyProjectId) {
915
+ const directTopic = await tryReadTopicDoc(ctx, legacyProjectId, {
916
+ failureLog: "[topicScope] Failed to load direct project topic",
917
+ idLogKey: "projectId"
918
+ });
919
+ return directTopic ?? tryResolveHostTopicByLegacyScope(ctx, legacyProjectId);
920
+ }
921
+ async function tryReadTopicDoc(ctx, id, log) {
922
+ try {
923
+ return await ctx.db.get(id);
924
+ } catch (error) {
925
+ debugGraphPrimitiveFallback(log.failureLog, {
926
+ error,
927
+ [log.idLogKey]: id
864
928
  });
929
+ return null;
865
930
  }
866
931
  }
867
- async function removeOperationalLinkEffects(ctx, args) {
868
- const belief = await ctx.db.get(args.beliefId);
869
- if (belief) {
870
- const currentSupporting = belief.supportingInsightIds ?? [];
871
- const currentContradicting = belief.contradictingInsightIds ?? [];
872
- if (args.relation === "supports") {
873
- await ctx.db.patch(args.beliefId, {
874
- supportingInsightIds: currentSupporting.filter(
875
- (id) => id !== args.insightId
876
- )
877
- });
878
- } else {
879
- await ctx.db.patch(args.beliefId, {
880
- contradictingInsightIds: currentContradicting.filter(
881
- (id) => id !== args.insightId
882
- )
883
- });
884
- }
932
+ async function buildTopicScope(ctx, topic, source, options = {}) {
933
+ const inherited = await resolveInheritedWorkspaceScope(ctx, topic);
934
+ const mapped = asMappedProjectId(topic);
935
+ return {
936
+ topicId: topic._id,
937
+ ...mapped || options.fallbackProjectId ? { projectId: mapped ?? options.fallbackProjectId } : {},
938
+ tenantId: inherited.tenantId,
939
+ workspaceId: inherited.workspaceId,
940
+ source
941
+ };
942
+ }
943
+ var optionalScopeArgs = {
944
+ projectId: v.optional(v.string()),
945
+ topicId: v.optional(v.string())
946
+ };
947
+
948
+ // src/scopeResolverCompat.ts
949
+ var scopeArgs = optionalScopeArgs;
950
+ function normalizeScopeValue2(value) {
951
+ if (typeof value !== "string") {
952
+ return;
953
+ }
954
+ const normalized = value.trim();
955
+ return normalized.length > 0 ? normalized : void 0;
956
+ }
957
+ async function resolveScope(ctx, args) {
958
+ const topicId = normalizeScopeValue2(args.topicId);
959
+ const projectId = normalizeScopeValue2(args.projectId);
960
+ if (!(topicId || projectId)) {
961
+ return null;
885
962
  }
886
963
  try {
887
- const beliefSpineNode = await ctx.db.get(args.beliefId);
888
- const evidenceSpineNode = await ctx.db.get(args.insightId);
889
- if (beliefSpineNode?.nodeType === "belief" && evidenceSpineNode?.nodeType === "evidence") {
890
- const edges = await ctx.db.query("epistemicEdges").withIndex(
891
- "by_from_to",
892
- (q) => q.eq("fromNodeId", evidenceSpineNode._id).eq("toNodeId", beliefSpineNode._id)
893
- ).collect();
894
- for (const edge of edges) {
895
- if (edge.edgeType === "informs") {
896
- await ctx.scheduler.runAfter(0, internal.neo4jEdgeAPI.deleteEdge, {
897
- globalId: edge.globalId
898
- });
899
- await ctx.db.delete(edge._id);
900
- }
964
+ return await resolveTopicProjectScope(ctx, {
965
+ topicId,
966
+ projectId
967
+ });
968
+ } catch (error) {
969
+ debugGraphPrimitiveFallback(
970
+ "[scopeResolverCompat] Failed to resolve scope",
971
+ {
972
+ error,
973
+ topicId,
974
+ projectId
901
975
  }
902
- }
903
- } catch (e) {
904
- console.error("[EpistemicSpine] Failed to remove informs edge:", e);
976
+ );
977
+ return null;
978
+ }
979
+ }
980
+ async function resolveScopeSoft(ctx, args) {
981
+ const resolved = await resolveScope(ctx, args);
982
+ if (resolved) {
983
+ const topicId2 = normalizeScopeValue2(resolved.topicId);
984
+ return {
985
+ ...topicId2 ? { topicId: topicId2 } : {},
986
+ ...resolved.projectId ? { projectId: resolved.projectId } : {},
987
+ ...resolved.tenantId ? { tenantId: resolved.tenantId } : {},
988
+ ...resolved.workspaceId ? { workspaceId: resolved.workspaceId } : {},
989
+ ...resolved.source ? { source: resolved.source } : {}
990
+ };
905
991
  }
992
+ const topicId = normalizeScopeValue2(args.topicId);
993
+ const projectId = normalizeScopeValue2(args.projectId);
994
+ return {
995
+ ...topicId ? { topicId } : {},
996
+ ...projectId ? { projectId } : {}
997
+ };
906
998
  }
907
999
 
908
1000
  // src/beliefEvidenceLinks.ts
@@ -931,6 +1023,101 @@ var matcherMetadataValidator = v.object({
931
1023
  signalSnapshot: v.optional(v.any()),
932
1024
  outcomeMetadata: v.optional(v.any())
933
1025
  });
1026
+ function isRecord2(value) {
1027
+ return typeof value === "object" && value !== null;
1028
+ }
1029
+ function readConvexId2(value) {
1030
+ return typeof value === "string" && value.length > 0 ? value : null;
1031
+ }
1032
+ function readOptionalNumber(value) {
1033
+ return typeof value === "number" && Number.isFinite(value) ? value : void 0;
1034
+ }
1035
+ function readOptionalString(value) {
1036
+ return typeof value === "string" ? value : void 0;
1037
+ }
1038
+ function readLinkSuggestionStatus(value) {
1039
+ return value === "suggested" || value === "approved" || value === "dismissed" ? value : void 0;
1040
+ }
1041
+ function readRelation(value) {
1042
+ return value === "supports" || value === "contradicts" ? value : null;
1043
+ }
1044
+ function readNodeIdArray(value) {
1045
+ if (!Array.isArray(value)) {
1046
+ return;
1047
+ }
1048
+ return value.flatMap((entry) => {
1049
+ const id = readConvexId2(entry);
1050
+ return id ? [id] : [];
1051
+ });
1052
+ }
1053
+ function readEpistemicNode(value) {
1054
+ if (!isRecord2(value)) {
1055
+ return null;
1056
+ }
1057
+ const id = readConvexId2(value._id);
1058
+ if (!id) {
1059
+ return null;
1060
+ }
1061
+ const confidence = readOptionalNumber(value.confidence) ?? readOptionalString(value.confidence);
1062
+ return {
1063
+ _id: id,
1064
+ belief: readOptionalString(value.belief),
1065
+ canonicalText: readOptionalString(value.canonicalText),
1066
+ confidence,
1067
+ contradictingInsightIds: readNodeIdArray(value.contradictingInsightIds),
1068
+ globalId: readOptionalString(value.globalId),
1069
+ nodeType: readOptionalString(value.nodeType),
1070
+ projectId: readOptionalString(value.projectId),
1071
+ status: readOptionalString(value.status),
1072
+ supportingInsightIds: readNodeIdArray(value.supportingInsightIds),
1073
+ topicId: readOptionalString(value.topicId)
1074
+ };
1075
+ }
1076
+ function readBeliefEvidenceLink(value) {
1077
+ if (!isRecord2(value)) {
1078
+ return null;
1079
+ }
1080
+ const id = readConvexId2(value._id);
1081
+ const beliefId = readConvexId2(value.beliefId);
1082
+ const insightId = readConvexId2(value.insightId);
1083
+ const relation = readRelation(value.relation);
1084
+ const createdBy = readOptionalString(value.createdBy);
1085
+ if (!(id && beliefId && insightId && relation && createdBy)) {
1086
+ return null;
1087
+ }
1088
+ return {
1089
+ _id: id,
1090
+ beliefId,
1091
+ confidence: readOptionalNumber(value.confidence),
1092
+ createdAt: readOptionalNumber(value.createdAt),
1093
+ createdBy,
1094
+ insightId,
1095
+ rationale: readOptionalString(value.rationale),
1096
+ relation,
1097
+ status: readLinkSuggestionStatus(value.status)
1098
+ };
1099
+ }
1100
+ function readProjectBeliefSummary(value) {
1101
+ if (!isRecord2(value)) {
1102
+ return null;
1103
+ }
1104
+ const id = readConvexId2(value._id);
1105
+ if (!id || typeof value.belief !== "string" || typeof value.confidence !== "string" || typeof value.status !== "string") {
1106
+ return null;
1107
+ }
1108
+ return {
1109
+ _id: id,
1110
+ belief: value.belief,
1111
+ confidence: value.confidence,
1112
+ status: value.status
1113
+ };
1114
+ }
1115
+ function readRowList(values, reader) {
1116
+ return values.flatMap((value) => {
1117
+ const row = reader(value);
1118
+ return row ? [row] : [];
1119
+ });
1120
+ }
934
1121
  async function markProjectGraphDirty(ctx, projectId) {
935
1122
  if (!projectId) {
936
1123
  return;
@@ -988,16 +1175,18 @@ var create = mutation({
988
1175
  handler: async (ctx, args) => {
989
1176
  const resolvedBeliefId = args.beliefId;
990
1177
  const resolvedInsightId = args.insightId;
991
- const beliefNode = await ctx.db.get(resolvedBeliefId);
1178
+ const beliefNode = readEpistemicNode(await ctx.db.get(resolvedBeliefId));
992
1179
  if (!beliefNode) {
993
1180
  throw new Error("Belief node not found");
994
1181
  }
995
- const insightNode = await ctx.db.get(resolvedInsightId);
1182
+ const insightNode = readEpistemicNode(await ctx.db.get(resolvedInsightId));
996
1183
  if (!insightNode) {
997
1184
  throw new Error("Evidence node not found");
998
1185
  }
999
1186
  if (!Number.isFinite(args.weight) || args.weight === 0 || args.weight < -1 || args.weight > 1) {
1000
- throw new Error("Belief evidence links require explicit nonzero weight in [-1, 1]");
1187
+ throw new Error(
1188
+ "Belief evidence links require explicit nonzero weight in [-1, 1]"
1189
+ );
1001
1190
  }
1002
1191
  if (args.relation === "supports" && args.weight < 0) {
1003
1192
  throw new Error("Supporting evidence links require positive weight");
@@ -1016,10 +1205,13 @@ var create = mutation({
1016
1205
  throw new Error("No permission to link evidence to this belief");
1017
1206
  }
1018
1207
  }
1019
- const existingLinks = await ctx.db.query("beliefEvidenceLinks").withIndex(
1020
- "by_beliefId",
1021
- (q) => q.eq("beliefId", args.beliefId)
1022
- ).collect();
1208
+ const existingLinks = readRowList(
1209
+ await ctx.db.query("beliefEvidenceLinks").withIndex(
1210
+ "by_beliefId",
1211
+ (q) => q.eq("beliefId", args.beliefId)
1212
+ ).collect(),
1213
+ readBeliefEvidenceLink
1214
+ );
1023
1215
  const duplicate = existingLinks.find(
1024
1216
  (link) => link.insightId === args.insightId
1025
1217
  );
@@ -1099,14 +1291,14 @@ var remove = mutation({
1099
1291
  },
1100
1292
  returns: permissiveReturn,
1101
1293
  handler: async (ctx, args) => {
1102
- const link = await ctx.db.get(args.linkId);
1294
+ const link = readBeliefEvidenceLink(await ctx.db.get(args.linkId));
1103
1295
  if (!link) {
1104
1296
  throw new Error("Link not found");
1105
1297
  }
1106
1298
  if (link.createdBy !== args.userId) {
1107
1299
  throw new Error("Only the creator can remove this link");
1108
1300
  }
1109
- const belief = await ctx.db.get(link.beliefId);
1301
+ const belief = readEpistemicNode(await ctx.db.get(link.beliefId));
1110
1302
  if (isOperationalLinkStatus(link.status)) {
1111
1303
  await removeOperationalLinkEffects(ctx, {
1112
1304
  beliefId: link.beliefId,
@@ -1127,11 +1319,11 @@ var update = mutation({
1127
1319
  },
1128
1320
  returns: permissiveReturn,
1129
1321
  handler: async (ctx, args) => {
1130
- const link = await ctx.db.get(args.linkId);
1322
+ const link = readBeliefEvidenceLink(await ctx.db.get(args.linkId));
1131
1323
  if (!link) {
1132
1324
  throw new Error("Link not found");
1133
1325
  }
1134
- const belief = await ctx.db.get(link.beliefId);
1326
+ const belief = readEpistemicNode(await ctx.db.get(link.beliefId));
1135
1327
  if (!belief) {
1136
1328
  throw new Error("Belief not found");
1137
1329
  }
@@ -1167,11 +1359,11 @@ var reviewSuggestion = mutation({
1167
1359
  },
1168
1360
  returns: permissiveReturn,
1169
1361
  handler: async (ctx, args) => {
1170
- const link = await ctx.db.get(args.linkId);
1362
+ const link = readBeliefEvidenceLink(await ctx.db.get(args.linkId));
1171
1363
  if (!link) {
1172
1364
  throw new Error("Link not found");
1173
1365
  }
1174
- const belief = await ctx.db.get(link.beliefId);
1366
+ const belief = readEpistemicNode(await ctx.db.get(link.beliefId));
1175
1367
  if (!belief) {
1176
1368
  throw new Error("Belief not found");
1177
1369
  }
@@ -1225,10 +1417,13 @@ var getByBelief = query({
1225
1417
  },
1226
1418
  returns: permissiveReturn,
1227
1419
  handler: async (ctx, args) => {
1228
- const links = await ctx.db.query("beliefEvidenceLinks").withIndex(
1229
- "by_beliefId",
1230
- (q) => q.eq("beliefId", args.beliefId)
1231
- ).collect();
1420
+ const links = readRowList(
1421
+ await ctx.db.query("beliefEvidenceLinks").withIndex(
1422
+ "by_beliefId",
1423
+ (q) => q.eq("beliefId", args.beliefId)
1424
+ ).collect(),
1425
+ readBeliefEvidenceLink
1426
+ );
1232
1427
  return links.filter(
1233
1428
  (link) => isOperationalLinkStatus(link.status)
1234
1429
  );
@@ -1240,10 +1435,13 @@ var getPendingSuggestions = query({
1240
1435
  },
1241
1436
  returns: permissiveReturn,
1242
1437
  handler: async (ctx, args) => {
1243
- const links = await ctx.db.query("beliefEvidenceLinks").withIndex(
1244
- "by_beliefId",
1245
- (q) => q.eq("beliefId", args.beliefId)
1246
- ).collect();
1438
+ const links = readRowList(
1439
+ await ctx.db.query("beliefEvidenceLinks").withIndex(
1440
+ "by_beliefId",
1441
+ (q) => q.eq("beliefId", args.beliefId)
1442
+ ).collect(),
1443
+ readBeliefEvidenceLink
1444
+ );
1247
1445
  return links.filter((link) => link.status === "suggested");
1248
1446
  }
1249
1447
  });
@@ -1253,10 +1451,13 @@ var getSupportingLinks = query({
1253
1451
  },
1254
1452
  returns: permissiveReturn,
1255
1453
  handler: async (ctx, args) => {
1256
- const links = await ctx.db.query("beliefEvidenceLinks").withIndex(
1257
- "by_relation",
1258
- (q) => q.eq("beliefId", args.beliefId).eq("relation", "supports")
1259
- ).collect();
1454
+ const links = readRowList(
1455
+ await ctx.db.query("beliefEvidenceLinks").withIndex(
1456
+ "by_relation",
1457
+ (q) => q.eq("beliefId", args.beliefId).eq("relation", "supports")
1458
+ ).collect(),
1459
+ readBeliefEvidenceLink
1460
+ );
1260
1461
  return links.filter(
1261
1462
  (link) => isOperationalLinkStatus(link.status)
1262
1463
  );
@@ -1268,10 +1469,13 @@ var getContradictingLinks = query({
1268
1469
  },
1269
1470
  returns: permissiveReturn,
1270
1471
  handler: async (ctx, args) => {
1271
- const links = await ctx.db.query("beliefEvidenceLinks").withIndex(
1272
- "by_relation",
1273
- (q) => q.eq("beliefId", args.beliefId).eq("relation", "contradicts")
1274
- ).collect();
1472
+ const links = readRowList(
1473
+ await ctx.db.query("beliefEvidenceLinks").withIndex(
1474
+ "by_relation",
1475
+ (q) => q.eq("beliefId", args.beliefId).eq("relation", "contradicts")
1476
+ ).collect(),
1477
+ readBeliefEvidenceLink
1478
+ );
1275
1479
  return links.filter(
1276
1480
  (link) => isOperationalLinkStatus(link.status)
1277
1481
  );
@@ -1283,10 +1487,13 @@ var getByInsight = query({
1283
1487
  },
1284
1488
  returns: permissiveReturn,
1285
1489
  handler: async (ctx, args) => {
1286
- const links = await ctx.db.query("beliefEvidenceLinks").withIndex(
1287
- "by_insightId",
1288
- (q) => q.eq("insightId", args.insightId)
1289
- ).collect();
1490
+ const links = readRowList(
1491
+ await ctx.db.query("beliefEvidenceLinks").withIndex(
1492
+ "by_insightId",
1493
+ (q) => q.eq("insightId", args.insightId)
1494
+ ).collect(),
1495
+ readBeliefEvidenceLink
1496
+ );
1290
1497
  return links.filter(
1291
1498
  (link) => isOperationalLinkStatus(link.status)
1292
1499
  );
@@ -1298,10 +1505,13 @@ var getCounts = query({
1298
1505
  },
1299
1506
  returns: permissiveReturn,
1300
1507
  handler: async (ctx, args) => {
1301
- const links = await ctx.db.query("beliefEvidenceLinks").withIndex(
1302
- "by_beliefId",
1303
- (q) => q.eq("beliefId", args.beliefId)
1304
- ).collect();
1508
+ const links = readRowList(
1509
+ await ctx.db.query("beliefEvidenceLinks").withIndex(
1510
+ "by_beliefId",
1511
+ (q) => q.eq("beliefId", args.beliefId)
1512
+ ).collect(),
1513
+ readBeliefEvidenceLink
1514
+ );
1305
1515
  const activeLinks = links.filter(
1306
1516
  (link) => isOperationalLinkStatus(link.status)
1307
1517
  );
@@ -1326,7 +1536,7 @@ var getEvidenceWithDetails = query({
1326
1536
  },
1327
1537
  returns: permissiveReturn,
1328
1538
  handler: async (ctx, args) => {
1329
- const belief = await ctx.db.get(args.beliefId);
1539
+ const belief = readEpistemicNode(await ctx.db.get(args.beliefId));
1330
1540
  if (!belief) {
1331
1541
  return null;
1332
1542
  }
@@ -1340,17 +1550,20 @@ var getEvidenceWithDetails = query({
1340
1550
  return null;
1341
1551
  }
1342
1552
  }
1343
- const links = await ctx.db.query("beliefEvidenceLinks").withIndex(
1344
- "by_beliefId",
1345
- (q) => q.eq("beliefId", args.beliefId)
1346
- ).collect();
1553
+ const links = readRowList(
1554
+ await ctx.db.query("beliefEvidenceLinks").withIndex(
1555
+ "by_beliefId",
1556
+ (q) => q.eq("beliefId", args.beliefId)
1557
+ ).collect(),
1558
+ readBeliefEvidenceLink
1559
+ );
1347
1560
  const linksWithDetails = await Promise.all(
1348
1561
  links.filter(
1349
1562
  (link) => isOperationalLinkStatus(
1350
1563
  link.status
1351
1564
  )
1352
1565
  ).map(async (link) => {
1353
- const insight = await ctx.db.get(link.insightId);
1566
+ const insight = readEpistemicNode(await ctx.db.get(link.insightId));
1354
1567
  return {
1355
1568
  ...link,
1356
1569
  insight
@@ -1392,20 +1605,23 @@ var getByProject = query({
1392
1605
  return { links: [], beliefDetails: {} };
1393
1606
  }
1394
1607
  const pageSize = Math.max(1, Math.min(Math.floor(args.limit ?? 300), 1e3));
1395
- const beliefs = await ctx.runQuery(
1396
- internal.epistemicBeliefs.internalGetByProject,
1397
- {
1608
+ const beliefs = readRowList(
1609
+ await ctx.runQuery(internal.epistemicBeliefs.internalGetByProject, {
1398
1610
  projectId,
1399
1611
  limit: pageSize
1400
- }
1612
+ }),
1613
+ readProjectBeliefSummary
1401
1614
  );
1402
1615
  const beliefIds = beliefs.map((b) => b._id);
1403
1616
  const allLinks = await Promise.all(
1404
1617
  beliefIds.map(async (beliefId) => {
1405
- const links = await ctx.db.query("beliefEvidenceLinks").withIndex(
1406
- "by_beliefId",
1407
- (q) => q.eq("beliefId", beliefId)
1408
- ).take(200);
1618
+ const links = readRowList(
1619
+ await ctx.db.query("beliefEvidenceLinks").withIndex(
1620
+ "by_beliefId",
1621
+ (q) => q.eq("beliefId", beliefId)
1622
+ ).take(200),
1623
+ readBeliefEvidenceLink
1624
+ );
1409
1625
  return links;
1410
1626
  })
1411
1627
  );
@@ -1431,13 +1647,16 @@ var getLinkedBeliefsForInsight = query({
1431
1647
  },
1432
1648
  returns: permissiveReturn,
1433
1649
  handler: async (ctx, args) => {
1434
- const links = await ctx.db.query("beliefEvidenceLinks").withIndex(
1435
- "by_insightId",
1436
- (q) => q.eq("insightId", args.insightId)
1437
- ).collect();
1650
+ const links = readRowList(
1651
+ await ctx.db.query("beliefEvidenceLinks").withIndex(
1652
+ "by_insightId",
1653
+ (q) => q.eq("insightId", args.insightId)
1654
+ ).collect(),
1655
+ readBeliefEvidenceLink
1656
+ );
1438
1657
  const linkedBeliefs = await Promise.all(
1439
1658
  links.map(async (link) => {
1440
- const belief = await ctx.db.get(link.beliefId);
1659
+ const belief = readEpistemicNode(await ctx.db.get(link.beliefId));
1441
1660
  return {
1442
1661
  linkId: link._id,
1443
1662
  beliefId: link.beliefId,