@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,550 +1,143 @@
1
- import { v, ConvexError } from 'convex/values';
1
+ import { requireScopeWriteAccess } from '@lucern/access-control/access';
2
2
  import { permissiveReturn } from '@lucern/contracts/schema-helpers/validators';
3
- import { componentsGeneric, anyApi, internalMutationGeneric, mutationGeneric } from 'convex/server';
3
+ import { v } from 'convex/values';
4
+ import { unsafeConvexAnyApi } from '@lucern/contracts/convex/unsafeAnyApi';
5
+ import { componentsGeneric, internalMutationGeneric, mutationGeneric } from 'convex/server';
6
+ import { normalizeTupleContradictionPolicy, mkOpinion, conditionalDeduction, project, dampedDependencyCascade, trustDiscount, applyNegativeSupport, cumulativeFusion, applyNegativeEvidence, confidenceFromSL, detectTupleContradiction, evaluateTupleContradictionTransition, readOpinionFromRecord, hasProjectedOpinionChanged } from '@lucern/confidence';
4
7
  import '@lucern/contracts/schema-helpers/spine/tables/epistemicNodes';
5
- import { normalizeTupleContradictionPolicy, mkOpinion, conditionalDeduction, project, dampedDependencyCascade, hasProjectedOpinionChanged, confidenceFromSL, detectTupleContradiction, evaluateTupleContradictionTransition, trustDiscount, applyNegativeSupport, cumulativeFusion, applyNegativeEvidence, readOpinionFromRecord } from '@lucern/confidence';
6
- import { checkProjectAccess } from '@lucern/access-control/access';
7
8
  import '@lucern/access-control/audience';
8
9
  import { getCurrentUserId } from '@lucern/access-control/auth';
10
+ import { throwStructuredMutationError } from '@lucern/access-control/structuredMutationError';
9
11
 
10
12
  // src/epistemicBeliefs.lifecycle.ts
11
- var api = anyApi;
13
+ var unsafeApi = unsafeConvexAnyApi(
14
+ "graph-primitives top-level module bundle lacks a committed Convex _generated/api surface"
15
+ );
16
+ var api = unsafeApi;
12
17
  componentsGeneric();
13
- var internal = anyApi;
18
+ var internal = unsafeApi;
14
19
  var internalMutation = internalMutationGeneric;
15
20
  var mutation = mutationGeneric;
16
21
 
17
- // src/debug.ts
18
- function isGraphPrimitiveDebugEnabled() {
19
- const env = globalThis.process?.env;
20
- return env?.LUCERN_COMPAT_FALLBACK_DEBUG === "1" || env?.LUCERN_GRAPH_DEBUG === "1";
22
+ // src/beliefLifecycle.ts
23
+ var BELIEF_STATUS_VALUES = [
24
+ "assumption",
25
+ "hypothesis",
26
+ "active",
27
+ "superseded",
28
+ "resolved_true",
29
+ "resolved_false"
30
+ ];
31
+ function isBeliefLifecycleStatus(value) {
32
+ return typeof value === "string" && BELIEF_STATUS_VALUES.includes(value);
21
33
  }
22
- function debugGraphPrimitiveFallback(message, context) {
23
- if (!isGraphPrimitiveDebugEnabled()) {
24
- return;
34
+ function normalizeLegacyBeliefStatus(value) {
35
+ if (isBeliefLifecycleStatus(value)) {
36
+ return value;
25
37
  }
26
- console.debug(message, context ?? {});
27
- }
28
-
29
- // src/topicScope.ts
30
- var LEGACY_SCOPE_FIELD = "graphScopeProjectId";
31
- async function resolveTopicNodeScopeOrNull(ctx, ref) {
32
- if (!ctx?.db || typeof ctx.db.query !== "function") {
33
- return null;
38
+ if (value === "belief" || value === "established" || value === "emerging") {
39
+ return "active";
34
40
  }
35
- let node = null;
36
- try {
37
- const byGlobalId = await ctx.db.query("epistemicNodes").withIndex("by_globalId", (q) => q.eq("globalId", ref)).first();
38
- if (byGlobalId && byGlobalId.nodeType === "topic") {
39
- node = byGlobalId;
40
- }
41
- } catch (error) {
42
- debugGraphPrimitiveFallback(
43
- "[topicScope] topic-node scope lookup by globalId failed",
44
- { error, ref }
45
- );
41
+ if (value === "fact" || value === "confirmed") {
42
+ return "resolved_true";
46
43
  }
47
- if (!node) {
48
- return null;
44
+ if (value === "disconfirmed" || value === "expired") {
45
+ return "resolved_false";
49
46
  }
50
- const scopeKey = normalizeScopeValue(node.topicId) ?? normalizeScopeValue(node.globalId);
51
- if (!scopeKey) {
52
- return null;
47
+ if (value === "deprecated") {
48
+ return "superseded";
53
49
  }
54
- return {
55
- topicId: scopeKey,
56
- projectId: asMappedProjectId(node),
57
- source: "topic_node"
58
- };
50
+ return null;
59
51
  }
60
- function asMappedProjectId(topic) {
61
- if (!topic) {
62
- return;
52
+ function normalizeBeliefConfidence(confidence) {
53
+ if (typeof confidence !== "number" || !Number.isFinite(confidence)) {
54
+ return null;
63
55
  }
64
- const directLegacyProjectId = normalizeScopeValue(topic[LEGACY_SCOPE_FIELD]);
65
- if (directLegacyProjectId) {
66
- return directLegacyProjectId;
56
+ if (confidence >= 0 && confidence <= 1) {
57
+ return confidence;
67
58
  }
68
- const metadata = topic.metadata || {};
69
- const candidate = metadata[LEGACY_SCOPE_FIELD] || metadata.legacyProjectId || metadata.projectId || metadata.scopeProjectId;
70
- return candidate ? candidate : void 0;
71
- }
72
- function normalizeScopeValue(value) {
73
- if (typeof value !== "string") {
74
- return;
59
+ if (confidence > 1 && confidence <= 100) {
60
+ return confidence / 100;
75
61
  }
76
- const normalized = value.trim();
77
- return normalized.length > 0 ? normalized : void 0;
78
- }
79
- function pickPrimaryTopic(candidates) {
80
- return [...candidates].sort((a, b) => {
81
- const depthA = a.depth ?? 9999;
82
- const depthB = b.depth ?? 9999;
83
- if (depthA !== depthB) {
84
- return depthA - depthB;
85
- }
86
- const createdA = a.createdAt ?? Number.MAX_SAFE_INTEGER;
87
- const createdB = b.createdAt ?? Number.MAX_SAFE_INTEGER;
88
- if (createdA !== createdB) {
89
- return createdA - createdB;
90
- }
91
- return String(a.name || "").localeCompare(String(b.name || ""));
92
- })[0];
62
+ return null;
93
63
  }
94
- async function findTopicsByScopeAlias(ctx, scopeId) {
95
- try {
96
- return await ctx.db.query("topics").withIndex(
97
- "by_graph_scope_project",
98
- (q) => q.eq(LEGACY_SCOPE_FIELD, scopeId)
99
- ).collect();
100
- } catch (error) {
101
- debugGraphPrimitiveFallback(
102
- "[topicScope] Failed to resolve scope alias via index",
103
- {
104
- error,
105
- scopeId
106
- }
107
- );
108
- const topics = await ctx.db.query("topics").collect();
109
- return topics.filter((topic) => {
110
- const normalizedGlobalId = normalizeScopeValue(topic.globalId);
111
- const mappedProjectId = asMappedProjectId(topic);
112
- return String(topic._id) === scopeId || normalizedGlobalId === scopeId || mappedProjectId === scopeId;
113
- });
64
+ function isResolvedByConfidence(confidence) {
65
+ const normalized = normalizeBeliefConfidence(confidence);
66
+ if (normalized === null) {
67
+ return false;
114
68
  }
69
+ return normalized <= 0 || normalized >= 1;
115
70
  }
116
- async function tryResolveHostTopicById(ctx, topicId) {
117
- if (typeof ctx.runQuery !== "function") {
71
+ function getPredictionMetaFromMetadata(metadata) {
72
+ return metadata?.predictionMeta;
73
+ }
74
+ function resolvedPredictionStatus(predictionMeta) {
75
+ if (!predictionMeta || typeof predictionMeta !== "object") {
118
76
  return null;
119
77
  }
120
- try {
121
- return await ctx.runQuery(api.topics.get, {
122
- id: topicId
123
- }) ?? null;
124
- } catch (error) {
125
- debugGraphPrimitiveFallback(
126
- "[topicScope] Failed to resolve topic by host query",
127
- {
128
- error,
129
- topicId
130
- }
131
- );
132
- return null;
78
+ const outcome = predictionMeta.outcome;
79
+ if (outcome === "confirmed") {
80
+ return "resolved_true";
81
+ }
82
+ if (outcome === "disconfirmed" || outcome === "expired") {
83
+ return "resolved_false";
133
84
  }
85
+ return null;
134
86
  }
135
- async function tryResolveHostTopicByLegacyScope(ctx, legacyScopeId) {
136
- if (typeof ctx.runQuery !== "function") {
137
- return null;
87
+ function shouldTreatBeliefAsResolved(opts) {
88
+ if (isResolvedByConfidence(opts.confidence)) {
89
+ const normalized = normalizeBeliefConfidence(opts.confidence);
90
+ return normalized === 0 ? "resolved_false" : "resolved_true";
138
91
  }
139
- try {
140
- return await ctx.runQuery(api.topics.getByLegacyScopeId, {
141
- projectId: legacyScopeId
142
- }) ?? null;
143
- } catch (error) {
144
- debugGraphPrimitiveFallback(
145
- "[topicScope] Failed to resolve topic by legacy scope",
146
- {
147
- error,
148
- legacyScopeId
149
- }
150
- );
151
- return null;
92
+ const directPredictionStatus = resolvedPredictionStatus(opts.predictionMeta);
93
+ if (directPredictionStatus) {
94
+ return directPredictionStatus;
152
95
  }
96
+ const metadataPredictionStatus = resolvedPredictionStatus(
97
+ getPredictionMetaFromMetadata(opts.metadata)
98
+ );
99
+ if (metadataPredictionStatus) {
100
+ return metadataPredictionStatus;
101
+ }
102
+ return null;
153
103
  }
154
- async function resolveInheritedWorkspaceScope(ctx, topic) {
155
- const MAX_DEPTH = 10;
156
- let tenantId = normalizeScopeValue(topic.tenantId);
157
- let workspaceId = normalizeScopeValue(topic.workspaceId);
158
- if (tenantId && workspaceId) {
159
- return { tenantId, workspaceId };
104
+ function resolveBeliefLifecycleStatus(opts) {
105
+ const resolvedStatus = shouldTreatBeliefAsResolved(opts);
106
+ if (resolvedStatus) {
107
+ return resolvedStatus;
160
108
  }
161
- let current = topic;
162
- for (let i = 0; i < MAX_DEPTH && current?.parentTopicId; i++) {
163
- current = await ctx.db.get(current.parentTopicId);
164
- if (!current) break;
165
- if (!tenantId) {
166
- tenantId = normalizeScopeValue(current.tenantId);
109
+ const direct = opts.beliefStatus;
110
+ const normalizedDirect = normalizeLegacyBeliefStatus(direct);
111
+ if (normalizedDirect) {
112
+ const normalized = normalizeBeliefConfidence(opts.confidence);
113
+ if (normalized !== null && isPreValidationBeliefStatus(normalizedDirect)) {
114
+ return "active";
167
115
  }
168
- if (!workspaceId) {
169
- workspaceId = normalizeScopeValue(current.workspaceId);
116
+ return normalizedDirect;
117
+ }
118
+ const metaStatus = opts.metadata?.beliefStatus;
119
+ const normalizedMetaStatus = normalizeLegacyBeliefStatus(metaStatus);
120
+ if (normalizedMetaStatus) {
121
+ const normalized = normalizeBeliefConfidence(opts.confidence);
122
+ if (normalized !== null && isPreValidationBeliefStatus(normalizedMetaStatus)) {
123
+ return "active";
170
124
  }
171
- if (tenantId && workspaceId) break;
125
+ return normalizedMetaStatus;
172
126
  }
173
- return { tenantId, workspaceId };
127
+ return "assumption";
174
128
  }
175
- async function resolveTopicProjectScope(ctx, args) {
176
- if (args.topicId) {
177
- let topic = null;
178
- try {
179
- topic = await ctx.db.get(
180
- args.topicId
181
- );
182
- } catch (error) {
183
- debugGraphPrimitiveFallback(
184
- "[topicScope] Failed to load topic by direct id",
185
- {
186
- error,
187
- topicId: args.topicId
188
- }
189
- );
190
- }
191
- if (!topic) {
192
- topic = await tryResolveHostTopicById(ctx, String(args.topicId));
193
- }
194
- if (!topic) {
195
- topic = pickPrimaryTopic(
196
- await findTopicsByScopeAlias(ctx, String(args.topicId))
197
- ) ?? null;
198
- }
199
- if (!topic) {
200
- const nodeScope = await resolveTopicNodeScopeOrNull(
201
- ctx,
202
- String(args.topicId)
203
- );
204
- if (nodeScope) {
205
- return nodeScope;
206
- }
207
- throw new Error(`Topic not found: ${String(args.topicId)}`);
208
- }
209
- const inherited = await resolveInheritedWorkspaceScope(ctx, topic);
210
- const mapped = asMappedProjectId(topic);
211
- if (mapped) {
212
- return {
213
- topicId: topic._id,
214
- projectId: mapped,
215
- tenantId: inherited.tenantId,
216
- workspaceId: inherited.workspaceId,
217
- source: "topic"
218
- };
219
- }
220
- return {
221
- topicId: topic._id,
222
- tenantId: inherited.tenantId,
223
- workspaceId: inherited.workspaceId,
224
- source: "topic"
225
- };
129
+ function isPreValidationBeliefStatus(status) {
130
+ return status === "assumption" || status === "hypothesis";
131
+ }
132
+ function promoteBeliefStatusAfterScoring(status, opts) {
133
+ const resolvedStatus = shouldTreatBeliefAsResolved({ ...opts });
134
+ if (resolvedStatus) {
135
+ return resolvedStatus;
226
136
  }
227
- if (args.projectId) {
228
- let directTopic = null;
229
- try {
230
- directTopic = await ctx.db.get(
231
- args.projectId
232
- );
233
- } catch (error) {
234
- debugGraphPrimitiveFallback(
235
- "[topicScope] Failed to load direct project topic",
236
- {
237
- error,
238
- projectId: args.projectId
239
- }
240
- );
241
- }
242
- if (directTopic) {
243
- const inherited = await resolveInheritedWorkspaceScope(ctx, directTopic);
244
- const mapped = asMappedProjectId(directTopic);
245
- return {
246
- topicId: directTopic._id,
247
- projectId: mapped ?? args.projectId,
248
- tenantId: inherited.tenantId,
249
- workspaceId: inherited.workspaceId,
250
- source: "topic_inferred"
251
- };
252
- }
253
- directTopic = await tryResolveHostTopicByLegacyScope(ctx, args.projectId);
254
- if (directTopic) {
255
- const inherited = await resolveInheritedWorkspaceScope(ctx, directTopic);
256
- const mapped = asMappedProjectId(directTopic);
257
- return {
258
- topicId: directTopic._id,
259
- projectId: mapped ?? args.projectId,
260
- tenantId: inherited.tenantId,
261
- workspaceId: inherited.workspaceId,
262
- source: "topic_inferred"
263
- };
264
- }
265
- const topics = await findTopicsByScopeAlias(ctx, args.projectId);
266
- const primary = pickPrimaryTopic(topics);
267
- if (primary) {
268
- const inherited = await resolveInheritedWorkspaceScope(ctx, primary);
269
- return {
270
- topicId: primary._id,
271
- projectId: args.projectId,
272
- tenantId: inherited.tenantId,
273
- workspaceId: inherited.workspaceId,
274
- source: "project_mapped_topic"
275
- };
276
- }
277
- const nodeScope = await resolveTopicNodeScopeOrNull(
278
- ctx,
279
- String(args.projectId)
280
- );
281
- if (nodeScope) {
282
- return {
283
- ...nodeScope,
284
- projectId: nodeScope.projectId ?? String(args.projectId)
285
- };
286
- }
287
- throw new Error(
288
- `Legacy project scope ${String(args.projectId)} has no mapped topic.`
289
- );
137
+ if (isPreValidationBeliefStatus(status)) {
138
+ return "active";
290
139
  }
291
- throw new Error(
292
- "Missing scope: provide topicId (preferred) or legacy projectId alias."
293
- );
294
- }
295
- ({
296
- projectId: v.optional(v.string()),
297
- topicId: v.optional(v.string())
298
- });
299
-
300
- // src/workspaceIsolation.ts
301
- function normalizeScopeValue2(value) {
302
- if (typeof value !== "string") {
303
- return;
304
- }
305
- const normalized = value.trim();
306
- return normalized.length > 0 ? normalized : void 0;
307
- }
308
- function throwWorkspaceIsolationError(args) {
309
- const error = new Error(args.message);
310
- error.status = 409;
311
- error.code = "INVARIANT_VIOLATION";
312
- error.invariantCode = args.invariantCode;
313
- error.suggestion = args.suggestion;
314
- error.details = args.details;
315
- throw error;
316
- }
317
- function nodeMatchesWorkspaceReasoningScope(node, scope) {
318
- if (!node) {
319
- return false;
320
- }
321
- const scopeTenantId = normalizeScopeValue2(scope.tenantId);
322
- const scopeWorkspaceId = normalizeScopeValue2(scope.workspaceId);
323
- const nodeTenantId = normalizeScopeValue2(node.tenantId);
324
- const nodeWorkspaceId = normalizeScopeValue2(node.workspaceId);
325
- const epistemicLayer = typeof node.epistemicLayer === "string" ? node.epistemicLayer : void 0;
326
- if (scopeTenantId && nodeTenantId && scopeTenantId !== nodeTenantId) {
327
- return false;
328
- }
329
- if (epistemicLayer === "ontological" && nodeWorkspaceId === void 0) {
330
- return true;
331
- }
332
- if (!scopeWorkspaceId && node.publicationStatus === "published") {
333
- return true;
334
- }
335
- if (!scopeWorkspaceId) {
336
- return nodeWorkspaceId === void 0;
337
- }
338
- return scopeWorkspaceId === nodeWorkspaceId;
339
- }
340
- function edgeMatchesWorkspaceReasoningScope(edge, scope) {
341
- if (!edge) {
342
- return false;
343
- }
344
- const scopeTenantId = normalizeScopeValue2(scope.tenantId);
345
- const scopeWorkspaceId = normalizeScopeValue2(scope.workspaceId);
346
- const edgeTenantId = normalizeScopeValue2(edge.tenantId);
347
- const edgeWorkspaceId = normalizeScopeValue2(edge.workspaceId);
348
- if (scopeTenantId && edgeTenantId && scopeTenantId !== edgeTenantId) {
349
- return false;
350
- }
351
- if (!scopeWorkspaceId) {
352
- return edgeWorkspaceId === void 0;
353
- }
354
- return scopeWorkspaceId === edgeWorkspaceId;
355
- }
356
- async function resolveNodeScopeForWorkspaceIsolation(ctx, node) {
357
- const epistemicLayer = typeof node?.epistemicLayer === "string" ? node.epistemicLayer : void 0;
358
- const resolved = {
359
- tenantId: normalizeScopeValue2(node?.tenantId),
360
- workspaceId: normalizeScopeValue2(node?.workspaceId),
361
- epistemicLayer,
362
- nodeType: typeof node?.nodeType === "string" ? node.nodeType : void 0
363
- };
364
- if (!node) {
365
- return resolved;
366
- }
367
- if (resolved.epistemicLayer === "ontological") {
368
- return resolved;
369
- }
370
- if (resolved.tenantId || resolved.workspaceId) {
371
- return resolved;
372
- }
373
- if (node.topicId) {
374
- const topicScope = await resolveTopicProjectScope(ctx, {
375
- topicId: node.topicId
376
- });
377
- return {
378
- ...resolved,
379
- tenantId: topicScope.tenantId,
380
- workspaceId: topicScope.workspaceId
381
- };
382
- }
383
- if (node.projectId) {
384
- const topicScope = await resolveTopicProjectScope(ctx, {
385
- projectId: String(node.projectId)
386
- });
387
- return {
388
- ...resolved,
389
- tenantId: topicScope.tenantId,
390
- workspaceId: topicScope.workspaceId
391
- };
392
- }
393
- return resolved;
394
- }
395
- function resolveRuntimePackMutationContext(args) {
396
- if (!args.runtimeToolName && !args.runtimePackKey && !args.runtimePackInstallScope) {
397
- return;
398
- }
399
- return {
400
- toolName: args.runtimeToolName,
401
- packKey: args.runtimePackKey,
402
- packInstallScope: args.runtimePackInstallScope
403
- };
404
- }
405
- function assertTenantPackWorkspaceMutationAllowed(args) {
406
- if (!args.runtime?.packKey || args.runtime.packInstallScope !== "tenant") {
407
- return;
408
- }
409
- const targetWorkspaceId = normalizeScopeValue2(args.target.workspaceId);
410
- const targetLayer = typeof args.target.epistemicLayer === "string" ? args.target.epistemicLayer : void 0;
411
- if (!targetWorkspaceId || targetLayer === "ontological") {
412
- return;
413
- }
414
- throwWorkspaceIsolationError({
415
- message: `Tenant-scoped pack "${args.runtime.packKey}" cannot mutate workspace-scoped reasoning state.`,
416
- invariantCode: "workspace.tenant_pack_reasoning_write_forbidden",
417
- suggestion: "Use a workspace-scoped pack for workspace-local graph mutations, or route the change through tenant-global canonical entity flows.",
418
- details: {
419
- mutationName: args.mutationName,
420
- toolName: args.runtime.toolName,
421
- packKey: args.runtime.packKey,
422
- targetWorkspaceId,
423
- targetNodeType: args.target.nodeType,
424
- targetLayer
425
- }
426
- });
427
- }
428
-
429
- // src/beliefLifecycle.ts
430
- var BELIEF_STATUS_VALUES = [
431
- "assumption",
432
- "hypothesis",
433
- "active",
434
- "superseded",
435
- "resolved_true",
436
- "resolved_false"
437
- ];
438
- function isBeliefLifecycleStatus(value) {
439
- return typeof value === "string" && BELIEF_STATUS_VALUES.includes(value);
440
- }
441
- function normalizeLegacyBeliefStatus(value) {
442
- if (isBeliefLifecycleStatus(value)) {
443
- return value;
444
- }
445
- if (value === "belief" || value === "established" || value === "emerging") {
446
- return "active";
447
- }
448
- if (value === "fact" || value === "confirmed") {
449
- return "resolved_true";
450
- }
451
- if (value === "disconfirmed" || value === "expired") {
452
- return "resolved_false";
453
- }
454
- if (value === "deprecated") {
455
- return "superseded";
456
- }
457
- return null;
458
- }
459
- function normalizeBeliefConfidence(confidence) {
460
- if (typeof confidence !== "number" || !Number.isFinite(confidence)) {
461
- return null;
462
- }
463
- if (confidence >= 0 && confidence <= 1) {
464
- return confidence;
465
- }
466
- if (confidence > 1 && confidence <= 100) {
467
- return confidence / 100;
468
- }
469
- return null;
470
- }
471
- function isResolvedByConfidence(confidence) {
472
- const normalized = normalizeBeliefConfidence(confidence);
473
- if (normalized === null) {
474
- return false;
475
- }
476
- return normalized <= 0 || normalized >= 1;
477
- }
478
- function getPredictionMetaFromMetadata(metadata) {
479
- return metadata?.predictionMeta;
480
- }
481
- function resolvedPredictionStatus(predictionMeta) {
482
- if (!predictionMeta || typeof predictionMeta !== "object") {
483
- return null;
484
- }
485
- const outcome = predictionMeta.outcome;
486
- if (outcome === "confirmed") {
487
- return "resolved_true";
488
- }
489
- if (outcome === "disconfirmed" || outcome === "expired") {
490
- return "resolved_false";
491
- }
492
- return null;
493
- }
494
- function shouldTreatBeliefAsResolved(opts) {
495
- if (isResolvedByConfidence(opts.confidence)) {
496
- const normalized = normalizeBeliefConfidence(opts.confidence);
497
- return normalized === 0 ? "resolved_false" : "resolved_true";
498
- }
499
- const directPredictionStatus = resolvedPredictionStatus(opts.predictionMeta);
500
- if (directPredictionStatus) {
501
- return directPredictionStatus;
502
- }
503
- const metadataPredictionStatus = resolvedPredictionStatus(
504
- getPredictionMetaFromMetadata(opts.metadata)
505
- );
506
- if (metadataPredictionStatus) {
507
- return metadataPredictionStatus;
508
- }
509
- return null;
510
- }
511
- function resolveBeliefLifecycleStatus(opts) {
512
- const resolvedStatus = shouldTreatBeliefAsResolved(opts);
513
- if (resolvedStatus) {
514
- return resolvedStatus;
515
- }
516
- const direct = opts.beliefStatus;
517
- const normalizedDirect = normalizeLegacyBeliefStatus(direct);
518
- if (normalizedDirect) {
519
- const normalized = normalizeBeliefConfidence(opts.confidence);
520
- if (normalized !== null && isPreValidationBeliefStatus(normalizedDirect)) {
521
- return "active";
522
- }
523
- return normalizedDirect;
524
- }
525
- const metaStatus = opts.metadata?.beliefStatus;
526
- const normalizedMetaStatus = normalizeLegacyBeliefStatus(metaStatus);
527
- if (normalizedMetaStatus) {
528
- const normalized = normalizeBeliefConfidence(opts.confidence);
529
- if (normalized !== null && isPreValidationBeliefStatus(normalizedMetaStatus)) {
530
- return "active";
531
- }
532
- return normalizedMetaStatus;
533
- }
534
- return "assumption";
535
- }
536
- function isPreValidationBeliefStatus(status) {
537
- return status === "assumption" || status === "hypothesis";
538
- }
539
- function promoteBeliefStatusAfterScoring(status, opts) {
540
- const resolvedStatus = shouldTreatBeliefAsResolved({ ...opts });
541
- if (resolvedStatus) {
542
- return resolvedStatus;
543
- }
544
- if (isPreValidationBeliefStatus(status)) {
545
- return "active";
546
- }
547
- return status;
140
+ return status;
548
141
  }
549
142
 
550
143
  // src/edges/contains.ts
@@ -682,7 +275,7 @@ var dependsOnPropagationSpec = {
682
275
  description: "Structural gating. Textbook conditional deduction when edge conditionals exist, otherwise damped dependency cascade through downstream chains."
683
276
  };
684
277
 
685
- // src/edges/derivedFrom.ts
278
+ // src/edges/derived-from.ts
686
279
  var derivedFromPropagationSpec = {
687
280
  edgeType: "derived_from",
688
281
  direction: "incoming",
@@ -735,7 +328,7 @@ var informsPropagationSpec = {
735
328
  description: "Evidence-bearing influence. Informs can chain through the graph with light per-hop damping."
736
329
  };
737
330
 
738
- // src/edges/propagationTypes.ts
331
+ // src/edges/propagation-types.ts
739
332
  function isPropagationTraversalDirection(direction) {
740
333
  return direction === "outgoing" || direction === "incoming";
741
334
  }
@@ -808,6 +401,9 @@ var testsPropagationSpec = {
808
401
  };
809
402
 
810
403
  // src/edges/index.ts
404
+ var canContinueTransitively2 = canContinueTransitively;
405
+ var canTraverseHop2 = canTraverseHop;
406
+ var isPropagationTraversalDirection2 = isPropagationTraversalDirection;
811
407
  var EDGE_PROPAGATION_SPECS = [
812
408
  supportsPropagationSpec,
813
409
  informsPropagationSpec,
@@ -824,16 +420,409 @@ function getEdgePropagationSpecs() {
824
420
  return EDGE_PROPAGATION_SPECS;
825
421
  }
826
422
  function getTraversalDirections(direction) {
827
- if (isPropagationTraversalDirection(direction)) {
423
+ if (isPropagationTraversalDirection2(direction)) {
828
424
  return [direction];
829
425
  }
830
- return ["outgoing", "incoming"];
426
+ return ["outgoing", "incoming"];
427
+ }
428
+
429
+ // src/debug.ts
430
+ function isGraphPrimitiveDebugEnabled() {
431
+ const env = globalThis.process?.env;
432
+ return env?.LUCERN_COMPAT_FALLBACK_DEBUG === "1" || env?.LUCERN_GRAPH_DEBUG === "1";
433
+ }
434
+ function debugGraphPrimitiveFallback(message, context) {
435
+ if (!isGraphPrimitiveDebugEnabled()) {
436
+ return;
437
+ }
438
+ console.debug(message, context ?? {});
439
+ }
440
+
441
+ // src/topicScope.ts
442
+ var LEGACY_SCOPE_FIELD = "graphScopeProjectId";
443
+ async function resolveTopicNodeScopeOrNull(ctx, ref) {
444
+ if (!ctx?.db || typeof ctx.db.query !== "function") {
445
+ return null;
446
+ }
447
+ let node = null;
448
+ try {
449
+ const byGlobalId = await ctx.db.query("epistemicNodes").withIndex("by_globalId", (q) => q.eq("globalId", ref)).first();
450
+ if (byGlobalId && byGlobalId.nodeType === "topic") {
451
+ node = byGlobalId;
452
+ }
453
+ } catch (error) {
454
+ debugGraphPrimitiveFallback(
455
+ "[topicScope] topic-node scope lookup by globalId failed",
456
+ { error, ref }
457
+ );
458
+ }
459
+ if (!node) {
460
+ return null;
461
+ }
462
+ const scopeKey = normalizeScopeValue(node.topicId) ?? normalizeScopeValue(node.globalId);
463
+ if (!scopeKey) {
464
+ return null;
465
+ }
466
+ return {
467
+ topicId: scopeKey,
468
+ projectId: asMappedProjectId(node),
469
+ source: "topic_node"
470
+ };
471
+ }
472
+ function asMappedProjectId(topic) {
473
+ if (!topic) {
474
+ return;
475
+ }
476
+ const directLegacyProjectId = normalizeScopeValue(
477
+ topic[LEGACY_SCOPE_FIELD]
478
+ );
479
+ if (directLegacyProjectId) {
480
+ return directLegacyProjectId;
481
+ }
482
+ const metadata = topic.metadata || {};
483
+ const candidate = metadata[LEGACY_SCOPE_FIELD] || metadata.legacyProjectId || metadata.projectId || metadata.scopeProjectId;
484
+ return typeof candidate === "string" ? normalizeScopeValue(candidate) : void 0;
485
+ }
486
+ function normalizeScopeValue(value) {
487
+ if (typeof value !== "string") {
488
+ return;
489
+ }
490
+ const normalized = value.trim();
491
+ return normalized.length > 0 ? normalized : void 0;
492
+ }
493
+ function pickPrimaryTopic(candidates) {
494
+ return [...candidates].sort((a, b) => {
495
+ const depthA = a.depth ?? 9999;
496
+ const depthB = b.depth ?? 9999;
497
+ if (depthA !== depthB) {
498
+ return depthA - depthB;
499
+ }
500
+ const createdA = a.createdAt ?? Number.MAX_SAFE_INTEGER;
501
+ const createdB = b.createdAt ?? Number.MAX_SAFE_INTEGER;
502
+ if (createdA !== createdB) {
503
+ return createdA - createdB;
504
+ }
505
+ return String(a.name || "").localeCompare(String(b.name || ""));
506
+ })[0];
507
+ }
508
+ async function findTopicsByScopeAlias(ctx, scopeId) {
509
+ const query = ctx.db.query("topics");
510
+ try {
511
+ return await query.withIndex(
512
+ "by_graph_scope_project",
513
+ (q) => q.eq(LEGACY_SCOPE_FIELD, scopeId)
514
+ ).collect();
515
+ } catch (error) {
516
+ debugGraphPrimitiveFallback(
517
+ "[topicScope] Failed to resolve scope alias via index",
518
+ {
519
+ error,
520
+ scopeId
521
+ }
522
+ );
523
+ const topics = await query.collect();
524
+ return topics.filter((topic) => {
525
+ const normalizedGlobalId = normalizeScopeValue(topic.globalId);
526
+ const mappedProjectId = asMappedProjectId(topic);
527
+ return String(topic._id) === scopeId || normalizedGlobalId === scopeId || mappedProjectId === scopeId;
528
+ });
529
+ }
530
+ }
531
+ async function tryResolveHostTopicById(ctx, topicId) {
532
+ if (typeof ctx.runQuery !== "function") {
533
+ return null;
534
+ }
535
+ try {
536
+ return await ctx.runQuery(api.topics.get, {
537
+ id: topicId
538
+ }) ?? null;
539
+ } catch (error) {
540
+ debugGraphPrimitiveFallback(
541
+ "[topicScope] Failed to resolve topic by host query",
542
+ {
543
+ error,
544
+ topicId
545
+ }
546
+ );
547
+ return null;
548
+ }
549
+ }
550
+ async function tryResolveHostTopicByLegacyScope(ctx, legacyScopeId) {
551
+ if (typeof ctx.runQuery !== "function") {
552
+ return null;
553
+ }
554
+ try {
555
+ return await ctx.runQuery(api.topics.getByLegacyScopeId, {
556
+ projectId: legacyScopeId
557
+ }) ?? null;
558
+ } catch (error) {
559
+ debugGraphPrimitiveFallback(
560
+ "[topicScope] Failed to resolve topic by legacy scope",
561
+ {
562
+ error,
563
+ legacyScopeId
564
+ }
565
+ );
566
+ return null;
567
+ }
568
+ }
569
+ async function resolveInheritedWorkspaceScope(ctx, topic) {
570
+ const MAX_DEPTH = 10;
571
+ let tenantId = normalizeScopeValue(topic.tenantId);
572
+ let workspaceId = normalizeScopeValue(topic.workspaceId);
573
+ if (tenantId && workspaceId) {
574
+ return { tenantId, workspaceId };
575
+ }
576
+ let current = topic;
577
+ for (let i = 0; i < MAX_DEPTH && current?.parentTopicId; i++) {
578
+ current = await ctx.db.get(current.parentTopicId);
579
+ if (!current) {
580
+ break;
581
+ }
582
+ if (!tenantId) {
583
+ tenantId = normalizeScopeValue(current.tenantId);
584
+ }
585
+ if (!workspaceId) {
586
+ workspaceId = normalizeScopeValue(current.workspaceId);
587
+ }
588
+ if (tenantId && workspaceId) {
589
+ break;
590
+ }
591
+ }
592
+ return { tenantId, workspaceId };
593
+ }
594
+ async function resolveTopicProjectScope(ctx, args) {
595
+ if (args.topicId) {
596
+ return await resolveScopeFromTopicId(ctx, args.topicId);
597
+ }
598
+ if (args.projectId) {
599
+ return await resolveScopeFromLegacyProjectId(ctx, args.projectId);
600
+ }
601
+ throw new Error(
602
+ "Missing scope: provide topicId (preferred) or legacy projectId alias."
603
+ );
604
+ }
605
+ async function resolveScopeFromTopicId(ctx, topicId) {
606
+ const topic = await resolveTopicDocFromTopicId(ctx, topicId);
607
+ if (topic) {
608
+ return await buildTopicScope(ctx, topic, "topic");
609
+ }
610
+ const nodeScope = await resolveTopicNodeScopeOrNull(ctx, String(topicId));
611
+ if (nodeScope) {
612
+ return nodeScope;
613
+ }
614
+ throw new Error(`Topic not found: ${String(topicId)}`);
615
+ }
616
+ async function resolveTopicDocFromTopicId(ctx, topicId) {
617
+ const direct = await tryReadTopicDoc(ctx, topicId, {
618
+ failureLog: "[topicScope] Failed to load topic by direct id",
619
+ idLogKey: "topicId"
620
+ });
621
+ if (direct) {
622
+ return direct;
623
+ }
624
+ const hostTopic = await tryResolveHostTopicById(ctx, String(topicId));
625
+ if (hostTopic) {
626
+ return hostTopic;
627
+ }
628
+ return pickPrimaryTopic(await findTopicsByScopeAlias(ctx, String(topicId))) ?? null;
629
+ }
630
+ async function resolveScopeFromLegacyProjectId(ctx, legacyProjectId) {
631
+ const directTopic = await resolveDirectLegacyProjectTopic(
632
+ ctx,
633
+ legacyProjectId
634
+ );
635
+ if (directTopic) {
636
+ return await buildTopicScope(ctx, directTopic, "topic_inferred", {
637
+ fallbackProjectId: legacyProjectId
638
+ });
639
+ }
640
+ const primary = pickPrimaryTopic(
641
+ await findTopicsByScopeAlias(ctx, legacyProjectId)
642
+ );
643
+ if (primary) {
644
+ return await buildTopicScope(ctx, primary, "project_mapped_topic", {
645
+ fallbackProjectId: legacyProjectId
646
+ });
647
+ }
648
+ const nodeScope = await resolveTopicNodeScopeOrNull(ctx, legacyProjectId);
649
+ if (nodeScope) {
650
+ return {
651
+ ...nodeScope,
652
+ projectId: nodeScope.projectId ?? legacyProjectId
653
+ };
654
+ }
655
+ throw new Error(
656
+ `Legacy project scope ${legacyProjectId} has no mapped topic.`
657
+ );
658
+ }
659
+ async function resolveDirectLegacyProjectTopic(ctx, legacyProjectId) {
660
+ const directTopic = await tryReadTopicDoc(ctx, legacyProjectId, {
661
+ failureLog: "[topicScope] Failed to load direct project topic",
662
+ idLogKey: "projectId"
663
+ });
664
+ return directTopic ?? tryResolveHostTopicByLegacyScope(ctx, legacyProjectId);
665
+ }
666
+ async function tryReadTopicDoc(ctx, id, log) {
667
+ try {
668
+ return await ctx.db.get(id);
669
+ } catch (error) {
670
+ debugGraphPrimitiveFallback(log.failureLog, {
671
+ error,
672
+ [log.idLogKey]: id
673
+ });
674
+ return null;
675
+ }
676
+ }
677
+ async function buildTopicScope(ctx, topic, source, options = {}) {
678
+ const inherited = await resolveInheritedWorkspaceScope(ctx, topic);
679
+ const mapped = asMappedProjectId(topic);
680
+ return {
681
+ topicId: topic._id,
682
+ ...mapped || options.fallbackProjectId ? { projectId: mapped ?? options.fallbackProjectId } : {},
683
+ tenantId: inherited.tenantId,
684
+ workspaceId: inherited.workspaceId,
685
+ source
686
+ };
687
+ }
688
+ ({
689
+ projectId: v.optional(v.string()),
690
+ topicId: v.optional(v.string())
691
+ });
692
+
693
+ // src/workspaceIsolation.ts
694
+ function normalizeScopeValue2(value) {
695
+ if (typeof value !== "string") {
696
+ return;
697
+ }
698
+ const normalized = value.trim();
699
+ return normalized.length > 0 ? normalized : void 0;
700
+ }
701
+ function throwWorkspaceIsolationError(args) {
702
+ const error = new Error(args.message);
703
+ error.status = 409;
704
+ error.code = "INVARIANT_VIOLATION";
705
+ error.invariantCode = args.invariantCode;
706
+ error.suggestion = args.suggestion;
707
+ error.details = args.details;
708
+ throw error;
709
+ }
710
+ function nodeMatchesWorkspaceReasoningScope(node, scope) {
711
+ if (!node) {
712
+ return false;
713
+ }
714
+ const scopeTenantId = normalizeScopeValue2(scope.tenantId);
715
+ const scopeWorkspaceId = normalizeScopeValue2(scope.workspaceId);
716
+ const nodeTenantId = normalizeScopeValue2(node.tenantId);
717
+ const nodeWorkspaceId = normalizeScopeValue2(node.workspaceId);
718
+ const epistemicLayer = typeof node.epistemicLayer === "string" ? node.epistemicLayer : void 0;
719
+ if (scopeTenantId && nodeTenantId && scopeTenantId !== nodeTenantId) {
720
+ return false;
721
+ }
722
+ if (epistemicLayer === "ontological" && nodeWorkspaceId === void 0) {
723
+ return true;
724
+ }
725
+ if (!scopeWorkspaceId && node.publicationStatus === "published") {
726
+ return true;
727
+ }
728
+ if (!scopeWorkspaceId) {
729
+ return nodeWorkspaceId === void 0;
730
+ }
731
+ return scopeWorkspaceId === nodeWorkspaceId;
732
+ }
733
+ function edgeMatchesWorkspaceReasoningScope(edge, scope) {
734
+ if (!edge) {
735
+ return false;
736
+ }
737
+ const scopeTenantId = normalizeScopeValue2(scope.tenantId);
738
+ const scopeWorkspaceId = normalizeScopeValue2(scope.workspaceId);
739
+ const edgeTenantId = normalizeScopeValue2(edge.tenantId);
740
+ const edgeWorkspaceId = normalizeScopeValue2(edge.workspaceId);
741
+ if (scopeTenantId && edgeTenantId && scopeTenantId !== edgeTenantId) {
742
+ return false;
743
+ }
744
+ if (!scopeWorkspaceId) {
745
+ return edgeWorkspaceId === void 0;
746
+ }
747
+ return scopeWorkspaceId === edgeWorkspaceId;
748
+ }
749
+ async function resolveNodeScopeForWorkspaceIsolation(ctx, node) {
750
+ const epistemicLayer = typeof node?.epistemicLayer === "string" ? node.epistemicLayer : void 0;
751
+ const resolved = {
752
+ tenantId: normalizeScopeValue2(node?.tenantId),
753
+ workspaceId: normalizeScopeValue2(node?.workspaceId),
754
+ epistemicLayer,
755
+ nodeType: typeof node?.nodeType === "string" ? node.nodeType : void 0
756
+ };
757
+ if (!node) {
758
+ return resolved;
759
+ }
760
+ if (resolved.epistemicLayer === "ontological") {
761
+ return resolved;
762
+ }
763
+ if (resolved.tenantId || resolved.workspaceId) {
764
+ return resolved;
765
+ }
766
+ const topicId = normalizeScopeValue2(node.topicId);
767
+ if (topicId) {
768
+ const topicScope = await resolveTopicProjectScope(ctx, {
769
+ topicId
770
+ });
771
+ return {
772
+ ...resolved,
773
+ tenantId: topicScope.tenantId,
774
+ workspaceId: topicScope.workspaceId
775
+ };
776
+ }
777
+ if (node.projectId) {
778
+ const topicScope = await resolveTopicProjectScope(ctx, {
779
+ projectId: String(node.projectId)
780
+ });
781
+ return {
782
+ ...resolved,
783
+ tenantId: topicScope.tenantId,
784
+ workspaceId: topicScope.workspaceId
785
+ };
786
+ }
787
+ return resolved;
788
+ }
789
+ function resolveRuntimePackMutationContext(args) {
790
+ if (!(args.runtimeToolName || args.runtimePackKey || args.runtimePackInstallScope)) {
791
+ return;
792
+ }
793
+ return {
794
+ toolName: args.runtimeToolName,
795
+ packKey: args.runtimePackKey,
796
+ packInstallScope: args.runtimePackInstallScope
797
+ };
798
+ }
799
+ function assertTenantPackWorkspaceMutationAllowed(args) {
800
+ if (!args.runtime?.packKey || args.runtime.packInstallScope !== "tenant") {
801
+ return;
802
+ }
803
+ const targetWorkspaceId = normalizeScopeValue2(args.target.workspaceId);
804
+ const targetLayer = typeof args.target.epistemicLayer === "string" ? args.target.epistemicLayer : void 0;
805
+ if (!targetWorkspaceId || targetLayer === "ontological") {
806
+ return;
807
+ }
808
+ throwWorkspaceIsolationError({
809
+ message: `Tenant-scoped pack "${args.runtime.packKey}" cannot mutate workspace-scoped reasoning state.`,
810
+ invariantCode: "workspace.tenant_pack_reasoning_write_forbidden",
811
+ suggestion: "Use a workspace-scoped pack for workspace-local graph mutations, or route the change through tenant-global canonical entity flows.",
812
+ details: {
813
+ mutationName: args.mutationName,
814
+ toolName: args.runtime.toolName,
815
+ packKey: args.runtime.packKey,
816
+ targetWorkspaceId,
817
+ targetNodeType: args.target.nodeType,
818
+ targetLayer
819
+ }
820
+ });
831
821
  }
832
822
 
833
823
  // src/confidencePropagationDispatch.ts
834
- function resolveTraversalTargetNodeId(edge, direction) {
835
- const targetNodeId = direction === "outgoing" ? edge.toNodeId : edge.fromNodeId;
836
- return targetNodeId ?? void 0;
824
+ function nodeIdToCacheKey(nodeId) {
825
+ return String(nodeId);
837
826
  }
838
827
  function readNodeOpinion(node) {
839
828
  const metadata = node.metadata ?? {};
@@ -849,137 +838,281 @@ function readNodeOpinion(node) {
849
838
  return mkOpinion(0, 0, 1, 0.5);
850
839
  }
851
840
  }
841
+ function resolveTraversalTargetNodeId(edge, direction) {
842
+ const targetNodeId = direction === "outgoing" ? edge.toNodeId : edge.fromNodeId;
843
+ return targetNodeId ?? void 0;
844
+ }
845
+ function buildInitialState(sourceNodeId, sourceOpinion) {
846
+ return {
847
+ nodeId: sourceNodeId,
848
+ opinion: sourceOpinion,
849
+ hop: 0,
850
+ visitedNodeIds: /* @__PURE__ */ new Set([nodeIdToCacheKey(sourceNodeId)])
851
+ };
852
+ }
853
+ function extendVisited(currentVisited, targetNodeId) {
854
+ return /* @__PURE__ */ new Set([...currentVisited, nodeIdToCacheKey(targetNodeId)]);
855
+ }
856
+ function makeTransitiveState(current, targetNodeId, projectedOpinion, nextHop) {
857
+ return {
858
+ nodeId: targetNodeId,
859
+ opinion: projectedOpinion,
860
+ hop: nextHop,
861
+ visitedNodeIds: extendVisited(current.visitedNodeIds, targetNodeId)
862
+ };
863
+ }
864
+ function makeDispatchRecord(targetNodeId, spec, direction, edge, projectedOpinion, nextHop, result, existingDispatch) {
865
+ return {
866
+ targetNodeId,
867
+ edgeType: spec.edgeType,
868
+ traversedDirection: direction,
869
+ weight: edge.weight ?? 1,
870
+ opinion: projectedOpinion,
871
+ operator: result.operator,
872
+ rationale: existingDispatch ? `${existingDispatch.rationale}; ${result.rationale}` : result.rationale,
873
+ hop: nextHop
874
+ };
875
+ }
876
+ async function loadCachedNode(scope, nodeId) {
877
+ const cacheKey = nodeIdToCacheKey(nodeId);
878
+ if (!scope.nodeCache.has(cacheKey)) {
879
+ scope.nodeCache.set(cacheKey, await scope.args.getNode(nodeId));
880
+ }
881
+ return scope.nodeCache.get(cacheKey) ?? null;
882
+ }
883
+ async function collectTargetDispatch(state, nextHop, spec, direction, edge, queue, scope) {
884
+ const sourceScope = scope.args.sourceScope;
885
+ if (sourceScope && !edgeMatchesWorkspaceReasoningScope(edge, sourceScope)) {
886
+ return;
887
+ }
888
+ const targetNodeId = resolveTraversalTargetNodeId(edge, direction);
889
+ if (!targetNodeId) {
890
+ return;
891
+ }
892
+ const targetNodeIdKey = nodeIdToCacheKey(targetNodeId);
893
+ if (state.visitedNodeIds.has(targetNodeIdKey)) {
894
+ return;
895
+ }
896
+ const targetNode = await loadCachedNode(scope, targetNodeId);
897
+ if (targetNode?.nodeType !== "belief") {
898
+ return;
899
+ }
900
+ if (sourceScope && !nodeMatchesWorkspaceReasoningScope(targetNode, sourceScope)) {
901
+ return;
902
+ }
903
+ const targetOpinion = scope.opinionCache.get(targetNodeIdKey) ?? readNodeOpinion(targetNode);
904
+ const result = spec.operator(state.opinion, targetOpinion, edge, {
905
+ hop: nextHop,
906
+ sourceNodeId: state.nodeId,
907
+ targetNodeId,
908
+ traversedDirection: direction,
909
+ spec
910
+ });
911
+ if (!(result && hasProjectedOpinionChanged(targetOpinion, result.opinion))) {
912
+ return;
913
+ }
914
+ const projectedOpinion = mkOpinion(
915
+ result.opinion.b,
916
+ result.opinion.d,
917
+ result.opinion.u,
918
+ result.opinion.a
919
+ );
920
+ scope.opinionCache.set(targetNodeIdKey, projectedOpinion);
921
+ const existingDispatch = scope.dispatchesByTargetId.get(targetNodeIdKey);
922
+ scope.dispatchesByTargetId.set(
923
+ targetNodeIdKey,
924
+ makeDispatchRecord(
925
+ targetNodeId,
926
+ spec,
927
+ direction,
928
+ edge,
929
+ projectedOpinion,
930
+ nextHop,
931
+ result,
932
+ existingDispatch
933
+ )
934
+ );
935
+ if (canContinueTransitively2(spec, nextHop)) {
936
+ queue.push(
937
+ makeTransitiveState(state, targetNodeId, projectedOpinion, nextHop)
938
+ );
939
+ }
940
+ }
941
+ async function processTraversalSpec(state, nextHop, spec, queue, scope) {
942
+ const sourceNodeId = state.nodeId;
943
+ for (const direction of getTraversalDirections(spec.direction)) {
944
+ const edges = await scope.args.queryEdges({
945
+ nodeId: sourceNodeId,
946
+ spec,
947
+ direction,
948
+ hop: nextHop
949
+ });
950
+ for (const edge of edges) {
951
+ await collectTargetDispatch(
952
+ state,
953
+ nextHop,
954
+ spec,
955
+ direction,
956
+ edge,
957
+ queue,
958
+ scope
959
+ );
960
+ }
961
+ }
962
+ }
963
+ async function processQueuedState(state, queue, scope) {
964
+ const nextHop = state.hop + 1;
965
+ for (const spec of scope.traversalSpecs) {
966
+ if (!canTraverseHop2(spec, nextHop)) {
967
+ continue;
968
+ }
969
+ await processTraversalSpec(state, nextHop, spec, queue, scope);
970
+ }
971
+ }
972
+ function sortDispatches(dispatches) {
973
+ return Array.from(dispatches).sort((left, right) => {
974
+ if (left.hop !== right.hop) {
975
+ return left.hop - right.hop;
976
+ }
977
+ return String(left.targetNodeId).localeCompare(String(right.targetNodeId));
978
+ });
979
+ }
852
980
  async function collectConfidencePropagationDispatches(args) {
853
981
  const dispatchesByTargetId = /* @__PURE__ */ new Map();
854
982
  const opinionCache = /* @__PURE__ */ new Map();
855
983
  const nodeCache = /* @__PURE__ */ new Map();
856
984
  const traversalSpecs = args.traversalSpecs ?? getEdgePropagationSpecs();
985
+ const scope = {
986
+ args,
987
+ dispatchesByTargetId,
988
+ opinionCache,
989
+ nodeCache,
990
+ traversalSpecs
991
+ };
857
992
  const queue = [
858
- {
859
- nodeId: args.sourceNodeId,
860
- opinion: args.sourceOpinion,
861
- hop: 0,
862
- visitedNodeIds: /* @__PURE__ */ new Set([String(args.sourceNodeId)])
863
- }
993
+ buildInitialState(args.sourceNodeId, args.sourceOpinion)
864
994
  ];
865
- const loadNode = async (nodeId) => {
866
- const cacheKey = String(nodeId);
867
- if (!nodeCache.has(cacheKey)) {
868
- nodeCache.set(cacheKey, await args.getNode(nodeId));
869
- }
870
- return nodeCache.get(cacheKey) ?? null;
871
- };
872
995
  while (queue.length > 0) {
873
996
  const state = queue.shift();
874
997
  if (!state) {
875
998
  continue;
876
999
  }
877
- for (const spec of traversalSpecs) {
878
- const nextHop = state.hop + 1;
879
- if (!canTraverseHop(spec, nextHop)) {
880
- continue;
881
- }
882
- for (const direction of getTraversalDirections(spec.direction)) {
883
- const edges = await args.queryEdges({
884
- nodeId: state.nodeId,
885
- spec,
886
- direction,
887
- hop: nextHop
888
- });
889
- for (const edge of edges) {
890
- if (args.sourceScope && !edgeMatchesWorkspaceReasoningScope(edge, args.sourceScope)) {
891
- continue;
892
- }
893
- const targetNodeId = resolveTraversalTargetNodeId(edge, direction);
894
- if (!targetNodeId) {
895
- continue;
896
- }
897
- if (state.visitedNodeIds.has(String(targetNodeId))) {
898
- continue;
899
- }
900
- const targetNode = await loadNode(targetNodeId);
901
- if (!targetNode || targetNode.nodeType !== "belief") {
902
- continue;
903
- }
904
- if (args.sourceScope && !nodeMatchesWorkspaceReasoningScope(targetNode, args.sourceScope)) {
905
- continue;
906
- }
907
- const cacheKey = String(targetNodeId);
908
- const targetOpinion = opinionCache.get(cacheKey) ?? readNodeOpinion(targetNode);
909
- const result = spec.operator(state.opinion, targetOpinion, edge, {
910
- hop: nextHop,
911
- sourceNodeId: state.nodeId,
912
- targetNodeId,
913
- traversedDirection: direction,
914
- spec
915
- });
916
- if (!result || !hasProjectedOpinionChanged(targetOpinion, result.opinion)) {
917
- continue;
918
- }
919
- const projectedOpinion = mkOpinion(
920
- result.opinion.b,
921
- result.opinion.d,
922
- result.opinion.u,
923
- result.opinion.a
924
- );
925
- opinionCache.set(cacheKey, projectedOpinion);
926
- const existingDispatch = dispatchesByTargetId.get(cacheKey);
927
- dispatchesByTargetId.set(cacheKey, {
928
- targetNodeId,
929
- edgeType: spec.edgeType,
930
- traversedDirection: direction,
931
- weight: edge.weight ?? 1,
932
- opinion: projectedOpinion,
933
- operator: result.operator,
934
- rationale: existingDispatch ? `${existingDispatch.rationale}; ${result.rationale}` : result.rationale,
935
- hop: nextHop
936
- });
937
- if (canContinueTransitively(spec, nextHop)) {
938
- queue.push({
939
- nodeId: targetNodeId,
940
- opinion: projectedOpinion,
941
- hop: nextHop,
942
- visitedNodeIds: /* @__PURE__ */ new Set([
943
- ...state.visitedNodeIds,
944
- String(targetNodeId)
945
- ])
946
- });
947
- }
948
- }
949
- }
950
- }
1000
+ await processQueuedState(state, queue, scope);
951
1001
  }
952
- return Array.from(dispatchesByTargetId.values()).sort((left, right) => {
953
- if (left.hop !== right.hop) {
954
- return left.hop - right.hop;
955
- }
956
- return String(left.targetNodeId).localeCompare(String(right.targetNodeId));
957
- });
1002
+ return sortDispatches(dispatchesByTargetId.values());
958
1003
  }
959
1004
  v.id("epistemicNodes");
960
1005
  var DEFAULT_CONFIDENCE_POLICY = {
961
1006
  scoringMode: "after_worktree",
962
1007
  tupleContradiction: normalizeTupleContradictionPolicy()
963
1008
  };
964
- function throwStructuredMutationError(args) {
965
- const data = {
966
- structuredMutationError: true,
967
- message: args.message,
968
- status: args.status,
969
- code: args.code,
970
- invariantCode: args.invariantCode,
971
- suggestion: args.suggestion,
972
- details: args.details
973
- };
974
- const error = new ConvexError(
975
- data
1009
+ function readFiniteNumber(value) {
1010
+ return typeof value === "number" && Number.isFinite(value) ? value : void 0;
1011
+ }
1012
+ function isRecord(value) {
1013
+ return Boolean(value) && typeof value === "object" && !Array.isArray(value);
1014
+ }
1015
+ function readOptionalString(value) {
1016
+ return typeof value === "string" && value.trim().length > 0 ? value : void 0;
1017
+ }
1018
+ function readStringArray(value) {
1019
+ if (!Array.isArray(value)) {
1020
+ return;
1021
+ }
1022
+ const strings = value.filter(
1023
+ (item) => typeof item === "string" && item.length > 0
976
1024
  );
977
- error.status = args.status;
978
- error.code = args.code;
979
- error.invariantCode = args.invariantCode;
980
- error.suggestion = args.suggestion;
981
- error.details = args.details;
982
- throw error;
1025
+ return strings.length === value.length ? strings : void 0;
1026
+ }
1027
+ function readRecord(value) {
1028
+ return isRecord(value) ? value : void 0;
1029
+ }
1030
+ function readBeliefNodeView(value) {
1031
+ if (!isRecord(value)) {
1032
+ return null;
1033
+ }
1034
+ const id = readOptionalString(value._id);
1035
+ const nodeType = readOptionalString(value.nodeType);
1036
+ if (!(id && nodeType === "belief")) {
1037
+ return null;
1038
+ }
1039
+ const node = {
1040
+ _id: id,
1041
+ nodeType
1042
+ };
1043
+ const creationTime = readFiniteNumber(value._creationTime);
1044
+ if (creationTime !== void 0) {
1045
+ node._creationTime = creationTime;
1046
+ }
1047
+ const metadata = readRecord(value.metadata);
1048
+ if (metadata !== void 0) {
1049
+ node.metadata = metadata;
1050
+ }
1051
+ const opinionA = readFiniteNumber(value.opinion_a);
1052
+ if (opinionA !== void 0) {
1053
+ node.opinion_a = opinionA;
1054
+ }
1055
+ const opinionB = readFiniteNumber(value.opinion_b);
1056
+ if (opinionB !== void 0) {
1057
+ node.opinion_b = opinionB;
1058
+ }
1059
+ const opinionD = readFiniteNumber(value.opinion_d);
1060
+ if (opinionD !== void 0) {
1061
+ node.opinion_d = opinionD;
1062
+ }
1063
+ const opinionU = readFiniteNumber(value.opinion_u);
1064
+ if (opinionU !== void 0) {
1065
+ node.opinion_u = opinionU;
1066
+ }
1067
+ const tupleContradicted = typeof value.tupleContradicted === "boolean" ? value.tupleContradicted : void 0;
1068
+ if (tupleContradicted !== void 0) {
1069
+ node.tupleContradicted = tupleContradicted;
1070
+ }
1071
+ const stringFields = {
1072
+ anonymizationClass: value.anonymizationClass,
1073
+ audienceLabel: value.audienceLabel,
1074
+ canonicalText: value.canonicalText,
1075
+ createdBy: value.createdBy,
1076
+ epistemicLayer: value.epistemicLayer,
1077
+ exportClass: value.exportClass,
1078
+ globalId: value.globalId,
1079
+ projectId: value.projectId,
1080
+ publicationStatus: value.publicationStatus,
1081
+ sensitivityTier: value.sensitivityTier,
1082
+ status: value.status,
1083
+ tenantId: value.tenantId,
1084
+ topicId: value.topicId,
1085
+ userId: value.userId,
1086
+ workspaceId: value.workspaceId
1087
+ };
1088
+ for (const [field, fieldValue] of Object.entries(stringFields)) {
1089
+ const normalized = readOptionalString(fieldValue);
1090
+ if (normalized !== void 0) {
1091
+ node[field] = normalized;
1092
+ }
1093
+ }
1094
+ const createdAt = readFiniteNumber(value.createdAt);
1095
+ if (createdAt !== void 0) {
1096
+ node.createdAt = createdAt;
1097
+ }
1098
+ const updatedAt = readFiniteNumber(value.updatedAt);
1099
+ if (updatedAt !== void 0) {
1100
+ node.updatedAt = updatedAt;
1101
+ }
1102
+ if (value.beliefStatus !== void 0) {
1103
+ node.beliefStatus = value.beliefStatus;
1104
+ }
1105
+ if (value.confidence !== void 0) {
1106
+ node.confidence = value.confidence;
1107
+ }
1108
+ if (value.predictionMeta !== void 0) {
1109
+ node.predictionMeta = value.predictionMeta;
1110
+ }
1111
+ const policyTags = readStringArray(value.policyTags);
1112
+ if (policyTags !== void 0) {
1113
+ node.policyTags = policyTags;
1114
+ }
1115
+ return node;
983
1116
  }
984
1117
  function buildBeliefConfidenceRow(args) {
985
1118
  return {
@@ -1055,7 +1188,10 @@ function resolveBeliefStatus(node, metadata) {
1055
1188
  });
1056
1189
  }
1057
1190
  async function hasCompletedWorktreeForBelief(ctx, beliefNodeId) {
1058
- const clusterMembership = await ctx.db.query("worktreeBeliefCluster").withIndex("by_belief", (q) => q.eq("beliefId", beliefNodeId)).collect();
1191
+ const clusterMembership = await ctx.db.query("worktreeBeliefCluster").withIndex(
1192
+ "by_belief",
1193
+ (q) => q.eq("beliefId", beliefNodeId)
1194
+ ).collect();
1059
1195
  for (const membership of clusterMembership) {
1060
1196
  const worktree = await ctx.db.get(membership.worktreeId);
1061
1197
  if (worktree?.status === "completed" || worktree?.status === "merged") {
@@ -1066,7 +1202,10 @@ async function hasCompletedWorktreeForBelief(ctx, beliefNodeId) {
1066
1202
  }
1067
1203
  async function getActiveConfidencePolicy(ctx) {
1068
1204
  try {
1069
- const activeConfig = await ctx.db.query("logicSprintScoring").withIndex("by_active", (q) => q.eq("isActive", true)).first();
1205
+ const activeConfig = await ctx.db.query("logicSprintScoring").withIndex(
1206
+ "by_active",
1207
+ (q) => q.eq("isActive", true)
1208
+ ).first();
1070
1209
  return {
1071
1210
  scoringMode: activeConfig?.confidencePolicy === "always" ? "always" : DEFAULT_CONFIDENCE_POLICY.scoringMode,
1072
1211
  tupleContradiction: normalizeTupleContradictionPolicy(
@@ -1098,28 +1237,191 @@ async function requireAuthenticatedUserId(ctx) {
1098
1237
  }
1099
1238
  return userId;
1100
1239
  }
1101
- async function requireProjectWriteAccess(ctx, projectId, userId) {
1102
- const hasAccess = await checkProjectAccess(
1103
- ctx,
1104
- projectId,
1105
- userId
1106
- );
1107
- if (!hasAccess) {
1108
- throwStructuredMutationError({
1109
- message: `Project write access denied for topic ${projectId}.`,
1110
- status: 403,
1111
- code: "PROJECT_ACCESS_DENIED",
1112
- invariantCode: "policy.scope_required",
1113
- suggestion: "The acting principal lacks project-write access to this topic. Request a topic grant (or, if the principal created this topic, run the creator-grant backfill) and retry.",
1114
- details: { topicId: projectId, principalId: userId }
1115
- });
1116
- }
1117
- }
1118
1240
 
1119
1241
  // src/epistemicBeliefs.confidence.ts
1242
+ function isRecord2(value) {
1243
+ return Boolean(value) && typeof value === "object" && !Array.isArray(value);
1244
+ }
1245
+ function readConvexId(value) {
1246
+ return typeof value === "string" && value.trim().length > 0 ? value : void 0;
1247
+ }
1248
+ function readOptionalBoolean(value) {
1249
+ return typeof value === "boolean" ? value : void 0;
1250
+ }
1251
+ function readOptionalNumber(value) {
1252
+ return typeof value === "number" && Number.isFinite(value) ? value : void 0;
1253
+ }
1254
+ function readOptionalString2(value) {
1255
+ return typeof value === "string" && value.trim().length > 0 ? value : void 0;
1256
+ }
1257
+ function readRecord2(value) {
1258
+ return isRecord2(value) ? value : void 0;
1259
+ }
1260
+ function readConfidenceBeliefNode(value) {
1261
+ if (!isRecord2(value)) {
1262
+ return null;
1263
+ }
1264
+ const id = readConvexId(value._id);
1265
+ const nodeType = readOptionalString2(value.nodeType);
1266
+ const projectId = readOptionalString2(value.projectId);
1267
+ if (!(id && nodeType === "belief" && projectId)) {
1268
+ return null;
1269
+ }
1270
+ const node = {
1271
+ _id: id,
1272
+ nodeType,
1273
+ projectId
1274
+ };
1275
+ const confidence = readOptionalNumber(value.confidence);
1276
+ const epistemicLayer = readOptionalString2(value.epistemicLayer);
1277
+ const globalId = readOptionalString2(value.globalId);
1278
+ const metadata = readRecord2(value.metadata);
1279
+ const opinionA = readOptionalNumber(value.opinion_a);
1280
+ const opinionB = readOptionalNumber(value.opinion_b);
1281
+ const opinionD = readOptionalNumber(value.opinion_d);
1282
+ const opinionU = readOptionalNumber(value.opinion_u);
1283
+ const predictionMeta = readRecord2(value.predictionMeta);
1284
+ const publicationStatus = readOptionalString2(value.publicationStatus);
1285
+ const status = readOptionalString2(value.status);
1286
+ const tenantId = readOptionalString2(value.tenantId);
1287
+ const topicId = readOptionalString2(value.topicId);
1288
+ const tupleContradicted = readOptionalBoolean(value.tupleContradicted);
1289
+ const workspaceId = readOptionalString2(value.workspaceId);
1290
+ if (confidence !== void 0) {
1291
+ node.confidence = confidence;
1292
+ }
1293
+ if (epistemicLayer !== void 0) {
1294
+ node.epistemicLayer = epistemicLayer;
1295
+ }
1296
+ if (globalId !== void 0) {
1297
+ node.globalId = globalId;
1298
+ }
1299
+ if (metadata !== void 0) {
1300
+ node.metadata = metadata;
1301
+ }
1302
+ if (opinionA !== void 0) {
1303
+ node.opinion_a = opinionA;
1304
+ }
1305
+ if (opinionB !== void 0) {
1306
+ node.opinion_b = opinionB;
1307
+ }
1308
+ if (opinionD !== void 0) {
1309
+ node.opinion_d = opinionD;
1310
+ }
1311
+ if (opinionU !== void 0) {
1312
+ node.opinion_u = opinionU;
1313
+ }
1314
+ if (predictionMeta !== void 0) {
1315
+ node.predictionMeta = predictionMeta;
1316
+ }
1317
+ if (publicationStatus !== void 0) {
1318
+ node.publicationStatus = publicationStatus;
1319
+ }
1320
+ if (status !== void 0) {
1321
+ node.status = status;
1322
+ }
1323
+ if (tenantId !== void 0) {
1324
+ node.tenantId = tenantId;
1325
+ }
1326
+ if (topicId !== void 0) {
1327
+ node.topicId = topicId;
1328
+ }
1329
+ if (tupleContradicted !== void 0) {
1330
+ node.tupleContradicted = tupleContradicted;
1331
+ }
1332
+ if (workspaceId !== void 0) {
1333
+ node.workspaceId = workspaceId;
1334
+ }
1335
+ return node;
1336
+ }
1337
+ function readPropagationEdge(value) {
1338
+ if (!isRecord2(value)) {
1339
+ return null;
1340
+ }
1341
+ const edgeType = readOptionalString2(value.edgeType);
1342
+ if (!edgeType) {
1343
+ return null;
1344
+ }
1345
+ const edge = { edgeType };
1346
+ const fromNodeId = readConvexId(value.fromNodeId);
1347
+ const tenantId = readOptionalString2(value.tenantId);
1348
+ const toNodeId = readConvexId(value.toNodeId);
1349
+ const weight = readOptionalNumber(value.weight);
1350
+ const workspaceId = readOptionalString2(value.workspaceId);
1351
+ if (fromNodeId !== void 0) {
1352
+ edge.fromNodeId = fromNodeId;
1353
+ }
1354
+ if (tenantId !== void 0) {
1355
+ edge.tenantId = tenantId;
1356
+ }
1357
+ if (toNodeId !== void 0) {
1358
+ edge.toNodeId = toNodeId;
1359
+ }
1360
+ if (weight !== void 0) {
1361
+ edge.weight = weight;
1362
+ }
1363
+ if (workspaceId !== void 0) {
1364
+ edge.workspaceId = workspaceId;
1365
+ }
1366
+ return edge;
1367
+ }
1368
+ function readRowList(values, reader) {
1369
+ return values.flatMap((value) => {
1370
+ const row = reader(value);
1371
+ return row ? [row] : [];
1372
+ });
1373
+ }
1120
1374
  async function applyBeliefConfidenceChange(ctx, args) {
1121
1375
  const now = Date.now();
1122
- const node = await ctx.db.get(args.nodeId);
1376
+ const node = await requireConfidenceBeliefNode(ctx, args);
1377
+ const state = await buildConfidenceChangeState(ctx, node, args);
1378
+ await assertConfidenceScoringPolicySatisfied(ctx, args, state);
1379
+ const tupleContradictionId = await createTupleContradictionIfNeeded(
1380
+ ctx,
1381
+ args,
1382
+ node,
1383
+ state
1384
+ );
1385
+ await patchBeliefConfidenceState(ctx, args, state);
1386
+ await scheduleFirstScoringThemeEdges(ctx, args, node, state);
1387
+ const beliefConfidenceId = await insertBeliefConfidenceRecord(
1388
+ ctx,
1389
+ args,
1390
+ state,
1391
+ tupleContradictionId,
1392
+ now
1393
+ );
1394
+ await ctx.scheduler.runAfter(0, internal.neo4jSync.syncNodeToNeo4j, {
1395
+ nodeId: args.nodeId,
1396
+ operation: "upsert"
1397
+ });
1398
+ await insertConfidenceAudit(
1399
+ ctx,
1400
+ args,
1401
+ node,
1402
+ state,
1403
+ tupleContradictionId,
1404
+ now
1405
+ );
1406
+ await insertTupleTransitionAuditIfNeeded(
1407
+ ctx,
1408
+ args,
1409
+ node,
1410
+ state,
1411
+ tupleContradictionId,
1412
+ now
1413
+ );
1414
+ await scheduleConfidenceFollowups(ctx, args, node, state);
1415
+ return {
1416
+ nodeId: args.nodeId,
1417
+ previousConfidence: state.previousConfidence,
1418
+ newConfidence: state.derivedConfidence,
1419
+ opinion: state.nextOpinion,
1420
+ beliefConfidenceId
1421
+ };
1422
+ }
1423
+ async function requireConfidenceBeliefNode(ctx, args) {
1424
+ const node = readConfidenceBeliefNode(await ctx.db.get(args.nodeId));
1123
1425
  if (!node) {
1124
1426
  throwStructuredMutationError({
1125
1427
  message: "Node not found.",
@@ -1130,59 +1432,28 @@ async function applyBeliefConfidenceChange(ctx, args) {
1130
1432
  details: { nodeId: args.nodeId }
1131
1433
  });
1132
1434
  }
1133
- if (node.nodeType !== "belief") {
1134
- throwStructuredMutationError({
1135
- message: `appendSlScoring only applies to belief nodes. Received nodeType "${node.nodeType}". Entity nodes (company, person, investor, etc.) do not have confidence \u2014 use entityLifecycle.updateEntityAttributes for mutable entity data.`,
1136
- status: 400,
1137
- code: "INVALID_ARGUMENT",
1138
- invariantCode: "entity.no_confidence",
1139
- suggestion: "Use entityLifecycle.updateEntityAttributes for entity mutations. appendSlScoring is for belief nodes only.",
1140
- details: { nodeId: args.nodeId, nodeType: node.nodeType }
1141
- });
1142
- }
1143
- if (!node.projectId) {
1144
- throwStructuredMutationError({
1145
- message: "Belief has no project scope.",
1146
- status: 400,
1147
- code: "MISSING_SCOPE",
1148
- invariantCode: "belief.project_required",
1149
- suggestion: "Belief must have a projectId before SL scoring can be appended.",
1150
- details: { nodeId: args.nodeId }
1151
- });
1152
- }
1153
- await requireProjectWriteAccess(
1154
- ctx,
1155
- node.projectId,
1156
- args.authenticatedUserId
1157
- );
1158
- const existingMetadata = node.metadata || {};
1435
+ await requireScopeWriteAccess(ctx, node.projectId, args.authenticatedUserId);
1436
+ return node;
1437
+ }
1438
+ async function buildConfidenceChangeState(ctx, node, args) {
1439
+ const existingMetadata = readNodeMetadata(node);
1159
1440
  const currentBeliefStatus = resolveBeliefStatus(node, existingMetadata);
1160
1441
  const confidencePolicy = await getActiveConfidencePolicy(ctx);
1161
- if (confidencePolicy.scoringMode === "after_worktree" && isPreValidationBeliefStatus(currentBeliefStatus)) {
1162
- const hasCompletedWorktree = await hasCompletedWorktreeForBelief(
1163
- ctx,
1164
- args.nodeId
1165
- );
1166
- if (!hasCompletedWorktree) {
1167
- throwStructuredMutationError({
1168
- message: "Cannot score belief before worktree completion. Complete a worktree that tests this belief first.",
1169
- status: 409,
1170
- code: "CONFLICT",
1171
- invariantCode: "belief.confidence_append_only",
1172
- suggestion: "Complete a worktree linked to this belief before recording SL scoring.",
1173
- details: { nodeId: args.nodeId }
1174
- });
1175
- }
1176
- }
1177
1442
  const previousConfidence = node.confidence || 0.5;
1178
1443
  const predictionMeta = node.predictionMeta || existingMetadata.predictionMeta;
1179
1444
  const previousOpinion = readBeliefOpinionSnapshot(node, existingMetadata);
1180
- const slB = args.belief;
1181
- const slD = args.disbelief;
1182
- const slU = args.uncertainty;
1183
- const slA = args.baseRate ?? 0.5;
1184
- const nextOpinion = { b: slB, d: slD, u: slU, a: slA };
1185
- const derivedConfidence = confidenceFromSL(slB, slD, slU, slA);
1445
+ const nextOpinion = {
1446
+ b: args.belief,
1447
+ d: args.disbelief,
1448
+ u: args.uncertainty,
1449
+ a: args.baseRate ?? 0.5
1450
+ };
1451
+ const derivedConfidence = confidenceFromSL(
1452
+ nextOpinion.b,
1453
+ nextOpinion.d,
1454
+ nextOpinion.u,
1455
+ nextOpinion.a
1456
+ );
1186
1457
  const isFirstScoring = typeof node.confidence !== "number" || !Number.isFinite(node.confidence);
1187
1458
  const previousTupleContradicted = readTupleContradictedFlag(node.tupleContradicted) ?? readTupleContradictedFlag(existingMetadata.tupleContradicted) ?? detectTupleContradiction(
1188
1459
  previousOpinion,
@@ -1203,79 +1474,121 @@ async function applyBeliefConfidenceChange(ctx, args) {
1203
1474
  predictionMeta,
1204
1475
  metadata: existingMetadata
1205
1476
  });
1206
- let tupleContradictionId;
1207
- if (tupleTransition.crossedIntoTupleContradiction) {
1208
- tupleContradictionId = await ctx.runMutation(
1209
- "contradictions:create",
1210
- {
1211
- projectId: node.projectId,
1212
- topicId: node.topicId,
1213
- beliefId: args.nodeId,
1214
- beliefBId: args.nodeId,
1215
- supportingInsightIds: [],
1216
- contradictingInsightIds: [],
1217
- severity: deriveTupleContradictionSeverity(node),
1218
- source: "tuple_space",
1219
- detectionMethod: "agent",
1220
- description: tupleContradictionDescription,
1221
- createdBy: args.authenticatedUserId
1222
- }
1223
- );
1477
+ const storedRationale = args.rationale ?? `Confidence changed from ${previousConfidence.toFixed(2)} (nodeId: ${args.nodeId})`;
1478
+ return {
1479
+ confidencePolicy,
1480
+ currentBeliefStatus,
1481
+ derivedConfidence,
1482
+ existingMetadata,
1483
+ isFirstScoring,
1484
+ newBeliefStatus,
1485
+ nextOpinion,
1486
+ previousConfidence,
1487
+ previousTupleContradicted,
1488
+ storedRationale,
1489
+ tupleContradictionDescription,
1490
+ tupleTransition
1491
+ };
1492
+ }
1493
+ function readNodeMetadata(node) {
1494
+ return node.metadata ?? {};
1495
+ }
1496
+ async function assertConfidenceScoringPolicySatisfied(ctx, args, state) {
1497
+ if (state.confidencePolicy.scoringMode !== "after_worktree" || !isPreValidationBeliefStatus(state.currentBeliefStatus)) {
1498
+ return;
1499
+ }
1500
+ const hasCompletedWorktree = await hasCompletedWorktreeForBelief(
1501
+ ctx,
1502
+ args.nodeId
1503
+ );
1504
+ if (hasCompletedWorktree) {
1505
+ return;
1506
+ }
1507
+ throwStructuredMutationError({
1508
+ message: "Cannot score belief before worktree completion. Complete a worktree that tests this belief first.",
1509
+ status: 409,
1510
+ code: "CONFLICT",
1511
+ invariantCode: "belief.confidence_append_only",
1512
+ suggestion: "Complete a worktree linked to this belief before recording SL scoring.",
1513
+ details: { nodeId: args.nodeId }
1514
+ });
1515
+ }
1516
+ async function createTupleContradictionIfNeeded(ctx, args, node, state) {
1517
+ if (!state.tupleTransition.crossedIntoTupleContradiction) {
1518
+ return;
1224
1519
  }
1520
+ return await ctx.runMutation("contradictions:create", {
1521
+ projectId: node.projectId,
1522
+ topicId: node.topicId,
1523
+ beliefId: args.nodeId,
1524
+ beliefBId: args.nodeId,
1525
+ supportingInsightIds: [],
1526
+ contradictingInsightIds: [],
1527
+ severity: deriveTupleContradictionSeverity(node),
1528
+ source: "tuple_space",
1529
+ detectionMethod: "agent",
1530
+ description: state.tupleContradictionDescription,
1531
+ createdBy: args.authenticatedUserId
1532
+ });
1533
+ }
1534
+ async function patchBeliefConfidenceState(ctx, args, state) {
1225
1535
  await ctx.db.patch(args.nodeId, {
1226
- confidence: derivedConfidence,
1227
- beliefStatus: newBeliefStatus,
1228
- tupleContradicted: tupleTransition.tupleContradicted,
1229
- updatedAt: now,
1230
- // Store SL opinion fields at node level for fast access
1231
- opinion_b: slB,
1232
- opinion_d: slD,
1233
- opinion_u: slU,
1234
- opinion_a: slA,
1536
+ confidence: state.derivedConfidence,
1537
+ beliefStatus: state.newBeliefStatus,
1538
+ tupleContradicted: state.tupleTransition.tupleContradicted,
1539
+ updatedAt: Date.now(),
1540
+ opinion_b: state.nextOpinion.b,
1541
+ opinion_d: state.nextOpinion.d,
1542
+ opinion_u: state.nextOpinion.u,
1543
+ opinion_a: state.nextOpinion.a,
1235
1544
  metadata: {
1236
- ...existingMetadata,
1237
- beliefStatus: newBeliefStatus,
1238
- slBelief: slB,
1239
- slDisbelief: slD,
1240
- slUncertainty: slU,
1241
- slBaseRate: slA,
1242
- tupleContradicted: tupleTransition.tupleContradicted
1545
+ ...state.existingMetadata,
1546
+ beliefStatus: state.newBeliefStatus,
1547
+ slBelief: state.nextOpinion.b,
1548
+ slDisbelief: state.nextOpinion.d,
1549
+ slUncertainty: state.nextOpinion.u,
1550
+ slBaseRate: state.nextOpinion.a,
1551
+ tupleContradicted: state.tupleTransition.tupleContradicted
1243
1552
  }
1244
1553
  });
1245
- if (isFirstScoring) {
1246
- const nodeTopicId = node.topicId;
1247
- const themeNodes = await ctx.db.query("epistemicNodes").withIndex(
1248
- "by_topic",
1249
- (q) => q.eq("topicId", nodeTopicId || node.projectId)
1250
- ).filter((q) => q.eq(q.field("nodeType"), "theme")).collect();
1251
- for (const theme of themeNodes) {
1252
- if (theme.globalId && node.globalId) {
1253
- await ctx.scheduler.runAfter(0, internal.neo4jEdgeAPI.createEdge, {
1254
- globalId: `edge-${node.globalId}-relates_to_thesis-${theme.globalId}`,
1255
- fromGlobalId: node.globalId,
1256
- toGlobalId: theme.globalId,
1257
- edgeType: "relates_to_thesis",
1258
- weight: derivedConfidence,
1259
- createdBy: args.authenticatedUserId,
1260
- topicId: String(node.projectId),
1261
- fromNodeType: "belief",
1262
- toNodeType: "theme",
1263
- fromLayer: "L3",
1264
- toLayer: "L3"
1265
- });
1266
- }
1554
+ }
1555
+ async function scheduleFirstScoringThemeEdges(ctx, args, node, state) {
1556
+ if (!state.isFirstScoring) {
1557
+ return;
1558
+ }
1559
+ const themeNodes = await ctx.db.query("epistemicNodes").withIndex(
1560
+ "by_topic",
1561
+ (q) => q.eq("topicId", node.topicId || node.projectId)
1562
+ ).filter((q) => q.eq(q.field("nodeType"), "theme")).collect();
1563
+ for (const theme of themeNodes) {
1564
+ if (!(theme.globalId && node.globalId)) {
1565
+ continue;
1267
1566
  }
1567
+ await ctx.scheduler.runAfter(0, internal.neo4jEdgeAPI.createEdge, {
1568
+ globalId: `edge-${node.globalId}-relates_to_thesis-${theme.globalId}`,
1569
+ fromGlobalId: node.globalId,
1570
+ toGlobalId: theme.globalId,
1571
+ edgeType: "relates_to_thesis",
1572
+ weight: state.derivedConfidence,
1573
+ createdBy: args.authenticatedUserId,
1574
+ topicId: String(node.projectId),
1575
+ fromNodeType: "belief",
1576
+ toNodeType: "theme",
1577
+ fromLayer: "L3",
1578
+ toLayer: "L3"
1579
+ });
1268
1580
  }
1269
- const storedRationale = args.rationale ?? `Confidence changed from ${previousConfidence.toFixed(2)} (nodeId: ${args.nodeId})`;
1270
- const beliefConfidenceId = await ctx.db.insert("beliefConfidence", {
1581
+ }
1582
+ async function insertBeliefConfidenceRecord(ctx, args, state, tupleContradictionId, now) {
1583
+ return await ctx.db.insert("beliefConfidence", {
1271
1584
  ...buildBeliefConfidenceRow({
1272
1585
  beliefId: args.nodeId,
1273
- belief: slB,
1274
- disbelief: slD,
1275
- uncertainty: slU,
1276
- baseRate: slA,
1586
+ belief: state.nextOpinion.b,
1587
+ disbelief: state.nextOpinion.d,
1588
+ uncertainty: state.nextOpinion.u,
1589
+ baseRate: state.nextOpinion.a,
1277
1590
  trigger: args.trigger,
1278
- rationale: storedRationale,
1591
+ rationale: state.storedRationale,
1279
1592
  assessedBy: args.authenticatedUserId,
1280
1593
  assessedAt: now,
1281
1594
  slOperator: args.slOperator,
@@ -1284,25 +1597,23 @@ async function applyBeliefConfidenceChange(ctx, args) {
1284
1597
  triggeringWorktreeId: args.triggeringWorktreeId
1285
1598
  })
1286
1599
  });
1287
- await ctx.scheduler.runAfter(0, internal.neo4jSync.syncNodeToNeo4j, {
1288
- nodeId: args.nodeId,
1289
- operation: "upsert"
1290
- });
1600
+ }
1601
+ async function insertConfidenceAudit(ctx, args, node, state, tupleContradictionId, now) {
1291
1602
  await ctx.db.insert("epistemicAudit", {
1292
1603
  entityType: "belief",
1293
1604
  entityId: args.nodeId,
1294
1605
  changeType: "confidence_changed",
1295
1606
  previousState: {
1296
- confidence: previousConfidence,
1297
- tupleContradicted: previousTupleContradicted
1607
+ confidence: state.previousConfidence,
1608
+ tupleContradicted: state.previousTupleContradicted
1298
1609
  },
1299
1610
  newState: {
1300
- opinion: nextOpinion,
1301
- confidence: derivedConfidence,
1611
+ opinion: state.nextOpinion,
1612
+ confidence: state.derivedConfidence,
1302
1613
  trigger: args.trigger,
1303
- rationale: storedRationale,
1304
- tupleContradicted: tupleTransition.tupleContradicted,
1305
- tupleContradictionPolicy: tupleTransition.policy,
1614
+ rationale: state.storedRationale,
1615
+ tupleContradicted: state.tupleTransition.tupleContradicted,
1616
+ tupleContradictionPolicy: state.tupleTransition.policy,
1306
1617
  ...tupleContradictionId ? { tupleContradictionId: String(tupleContradictionId) } : {}
1307
1618
  },
1308
1619
  changedBy: args.authenticatedUserId,
@@ -1311,28 +1622,39 @@ async function applyBeliefConfidenceChange(ctx, args) {
1311
1622
  projectId: node.projectId,
1312
1623
  topicId: node.topicId
1313
1624
  });
1314
- if (tupleTransition.crossedIntoTupleContradiction || tupleTransition.crossedOutOfTupleContradiction) {
1315
- await ctx.db.insert("epistemicAudit", {
1316
- entityType: "belief",
1317
- entityId: args.nodeId,
1318
- changeType: "updated",
1319
- previousState: { tupleContradicted: previousTupleContradicted },
1320
- newState: {
1321
- tupleContradicted: tupleTransition.tupleContradicted,
1322
- action: tupleTransition.crossedIntoTupleContradiction ? "tuple_contradiction_detected" : "tuple_contradiction_cleared",
1323
- opinion: nextOpinion,
1324
- tupleContradictionPolicy: tupleTransition.policy,
1325
- ...tupleContradictionId ? { tupleContradictionId: String(tupleContradictionId) } : {}
1326
- },
1327
- rationale: tupleTransition.crossedIntoTupleContradiction ? tupleContradictionDescription : `Tuple-space contradiction cleared: b=${nextOpinion.b.toFixed(2)}, d=${nextOpinion.d.toFixed(2)} no longer exceed the configured policy thresholds.`,
1328
- changedBy: args.authenticatedUserId,
1329
- isAgent: false,
1330
- changedAt: now,
1331
- projectId: node.projectId,
1332
- topicId: node.topicId
1333
- });
1625
+ }
1626
+ async function insertTupleTransitionAuditIfNeeded(ctx, args, node, state, tupleContradictionId, now) {
1627
+ if (!(state.tupleTransition.crossedIntoTupleContradiction || state.tupleTransition.crossedOutOfTupleContradiction)) {
1628
+ return;
1629
+ }
1630
+ await ctx.db.insert("epistemicAudit", {
1631
+ entityType: "belief",
1632
+ entityId: args.nodeId,
1633
+ changeType: "updated",
1634
+ previousState: { tupleContradicted: state.previousTupleContradicted },
1635
+ newState: {
1636
+ tupleContradicted: state.tupleTransition.tupleContradicted,
1637
+ action: state.tupleTransition.crossedIntoTupleContradiction ? "tuple_contradiction_detected" : "tuple_contradiction_cleared",
1638
+ opinion: state.nextOpinion,
1639
+ tupleContradictionPolicy: state.tupleTransition.policy,
1640
+ ...tupleContradictionId ? { tupleContradictionId: String(tupleContradictionId) } : {}
1641
+ },
1642
+ rationale: tupleAuditRationale(state),
1643
+ changedBy: args.authenticatedUserId,
1644
+ isAgent: false,
1645
+ changedAt: now,
1646
+ projectId: node.projectId,
1647
+ topicId: node.topicId
1648
+ });
1649
+ }
1650
+ function tupleAuditRationale(state) {
1651
+ if (state.tupleTransition.crossedIntoTupleContradiction) {
1652
+ return state.tupleContradictionDescription;
1334
1653
  }
1335
- if (Math.abs(derivedConfidence - previousConfidence) >= 0.15) {
1654
+ return `Tuple-space contradiction cleared: b=${state.nextOpinion.b.toFixed(2)}, d=${state.nextOpinion.d.toFixed(2)} no longer exceed the configured policy thresholds.`;
1655
+ }
1656
+ async function scheduleConfidenceFollowups(ctx, args, node, state) {
1657
+ if (Math.abs(state.derivedConfidence - state.previousConfidence) >= 0.15) {
1336
1658
  await ctx.scheduler.runAfter(
1337
1659
  5e3,
1338
1660
  internal.bi.contradictionSemanticDetector.scanAffectedBeliefs,
@@ -1349,13 +1671,6 @@ async function applyBeliefConfidenceChange(ctx, args) {
1349
1671
  { nodeId: args.nodeId }
1350
1672
  );
1351
1673
  }
1352
- return {
1353
- nodeId: args.nodeId,
1354
- previousConfidence,
1355
- newConfidence: derivedConfidence,
1356
- opinion: { b: slB, d: slD, u: slU, a: slA },
1357
- beliefConfidenceId
1358
- };
1359
1674
  }
1360
1675
  function propagationPressureLabel(edgeType, weight) {
1361
1676
  if (edgeType === "contradicts" || edgeType === "refutes") {
@@ -1383,7 +1698,7 @@ internalMutation({
1383
1698
  args.opinion_u,
1384
1699
  args.opinion_a
1385
1700
  );
1386
- const sourceNode = await ctx.db.get(args.nodeId);
1701
+ const sourceNode = readConfidenceBeliefNode(await ctx.db.get(args.nodeId));
1387
1702
  const sourceScope = await resolveNodeScopeForWorkspaceIsolation(
1388
1703
  ctx,
1389
1704
  sourceNode
@@ -1392,16 +1707,20 @@ internalMutation({
1392
1707
  sourceNodeId: args.nodeId,
1393
1708
  sourceOpinion,
1394
1709
  sourceScope,
1395
- queryEdges: async ({ nodeId, spec, direction }) => {
1396
- return await ctx.db.query("epistemicEdges").withIndex(
1710
+ queryEdges: async ({ nodeId, spec, direction }) => readRowList(
1711
+ await ctx.db.query("epistemicEdges").withIndex(
1397
1712
  direction === "outgoing" ? "by_from_type" : "by_to_type",
1398
1713
  (q) => direction === "outgoing" ? q.eq("fromNodeId", nodeId).eq("edgeType", spec.edgeType) : q.eq("toNodeId", nodeId).eq("edgeType", spec.edgeType)
1399
- ).collect();
1400
- },
1401
- getNode: async (nodeId) => await ctx.db.get(nodeId)
1714
+ ).collect(),
1715
+ readPropagationEdge
1716
+ ),
1717
+ getNode: async (nodeId) => readConfidenceBeliefNode(await ctx.db.get(nodeId))
1402
1718
  });
1403
1719
  for (const dispatch of dispatches) {
1404
- const pressureLabel = propagationPressureLabel(dispatch.edgeType, dispatch.weight);
1720
+ const pressureLabel = propagationPressureLabel(
1721
+ dispatch.edgeType,
1722
+ dispatch.weight
1723
+ );
1405
1724
  await applyBeliefConfidenceChange(ctx, {
1406
1725
  nodeId: dispatch.targetNodeId,
1407
1726
  belief: dispatch.opinion.b,
@@ -1524,16 +1843,16 @@ var updateStatus = mutation({
1524
1843
  handler: async (ctx, args) => {
1525
1844
  const authenticatedUserId = await requireAuthenticatedUserId(ctx);
1526
1845
  const now = Date.now();
1527
- const node = await ctx.db.get(args.nodeId);
1528
- if (!node || node.nodeType !== "belief") {
1846
+ const node = readBeliefNodeView(await ctx.db.get(args.nodeId));
1847
+ if (!node) {
1529
1848
  throw new Error("Belief not found");
1530
1849
  }
1531
1850
  if (!node.projectId) {
1532
1851
  throw new Error("Belief has no project scope");
1533
1852
  }
1534
- await requireProjectWriteAccess(ctx, node.projectId, authenticatedUserId);
1853
+ await requireScopeWriteAccess(ctx, node.projectId, authenticatedUserId);
1535
1854
  const previousStatus = node.status;
1536
- const metadata = node.metadata || {};
1855
+ const metadata = node.metadata ?? {};
1537
1856
  await ctx.db.patch(args.nodeId, {
1538
1857
  status: args.status,
1539
1858
  updatedAt: now,
@@ -1569,14 +1888,14 @@ var archive = mutation({
1569
1888
  returns: permissiveReturn,
1570
1889
  handler: async (ctx, args) => {
1571
1890
  const authenticatedUserId = await requireAuthenticatedUserId(ctx);
1572
- const node = await ctx.db.get(args.nodeId);
1573
- if (!node || node.nodeType !== "belief") {
1891
+ const node = readBeliefNodeView(await ctx.db.get(args.nodeId));
1892
+ if (!node) {
1574
1893
  throw new Error("Belief not found");
1575
1894
  }
1576
1895
  if (!node.projectId) {
1577
1896
  throw new Error("Belief has no project scope");
1578
1897
  }
1579
- await requireProjectWriteAccess(ctx, node.projectId, authenticatedUserId);
1898
+ await requireScopeWriteAccess(ctx, node.projectId, authenticatedUserId);
1580
1899
  return await ctx.runMutation(
1581
1900
  // Use updateStatus internally
1582
1901
  internal.epistemicBeliefs.updateStatusInternal,
@@ -1599,15 +1918,15 @@ var updateRationale = mutation({
1599
1918
  handler: async (ctx, args) => {
1600
1919
  const authenticatedUserId = await requireAuthenticatedUserId(ctx);
1601
1920
  const now = Date.now();
1602
- const node = await ctx.db.get(args.nodeId);
1603
- if (!node || node.nodeType !== "belief") {
1921
+ const node = readBeliefNodeView(await ctx.db.get(args.nodeId));
1922
+ if (!node) {
1604
1923
  throw new Error("Belief not found");
1605
1924
  }
1606
1925
  if (!node.projectId) {
1607
1926
  throw new Error("Belief has no project scope");
1608
1927
  }
1609
- await requireProjectWriteAccess(ctx, node.projectId, authenticatedUserId);
1610
- const metadata = node.metadata || {};
1928
+ await requireScopeWriteAccess(ctx, node.projectId, authenticatedUserId);
1929
+ const metadata = node.metadata ?? {};
1611
1930
  const previousRationale = typeof metadata.rationale === "string" ? metadata.rationale : void 0;
1612
1931
  const nextRationale = args.rationale?.trim();
1613
1932
  await ctx.db.patch(args.nodeId, {
@@ -1660,8 +1979,8 @@ var updateStatusInternal = internalMutation({
1660
1979
  returns: permissiveReturn,
1661
1980
  handler: async (ctx, args) => {
1662
1981
  const now = Date.now();
1663
- const node = await ctx.db.get(args.nodeId);
1664
- if (!node || node.nodeType !== "belief") {
1982
+ const node = readBeliefNodeView(await ctx.db.get(args.nodeId));
1983
+ if (!node) {
1665
1984
  throw new Error("Belief not found");
1666
1985
  }
1667
1986
  assertTenantPackWorkspaceMutationAllowed({