@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,1105 +1,640 @@
1
- import { v, ConvexError } from 'convex/values';
2
- import { checkProjectAccess } from '@lucern/access-control/access';
1
+ import { checkProjectAccess, requireScopeWriteAccess } from '@lucern/access-control/access';
3
2
  import { getCurrentUserId } from '@lucern/access-control/auth';
4
3
  import { permissiveReturn } from '@lucern/contracts/schema-helpers/validators';
5
- import { normalizeTupleContradictionPolicy, mkOpinion, conditionalDeduction, project, dampedDependencyCascade, hasProjectedOpinionChanged, confidenceFromSL, detectTupleContradiction, evaluateTupleContradictionTransition, deriveContractModulationPlan, deriveContractStatus, trustDiscount, applyNegativeSupport, cumulativeFusion, applyNegativeEvidence, readOpinionFromRecord, parseEvidentialEvaluatorConfig, compareMetricValue, buildEvidentialRationale, parseMetricCheckerConfig, getEvaluatorInputRecord, pickFiniteNumber, resolveComparisonResult, buildComparisonRationale, parseReferenceCheckCounterConfig, parseTemporalDeadlineConfig, parseMarketIndexComparatorConfig, createInheritedContractRecord } from '@lucern/confidence';
4
+ import { v } from 'convex/values';
5
+ import { unsafeConvexAnyApi } from '@lucern/contracts/convex/unsafeAnyApi';
6
+ import { componentsGeneric, internalMutationGeneric, queryGeneric, mutationGeneric } from 'convex/server';
7
+ import { normalizeTupleContradictionPolicy, mkOpinion, conditionalDeduction, project, dampedDependencyCascade, deriveContractModulationPlan, deriveContractStatus, trustDiscount, applyNegativeSupport, cumulativeFusion, applyNegativeEvidence, confidenceFromSL, detectTupleContradiction, evaluateTupleContradictionTransition, readOpinionFromRecord, parseEvidentialEvaluatorConfig, compareMetricValue, resolveComparisonResult, buildEvidentialRationale, parseMetricCheckerConfig, getEvaluatorInputRecord, pickFiniteNumber, buildComparisonRationale, parseReferenceCheckCounterConfig, parseTemporalDeadlineConfig, parseMarketIndexComparatorConfig, hasProjectedOpinionChanged, createInheritedContractRecord } from '@lucern/confidence';
6
8
  import '@lucern/access-control/audience';
7
- import { componentsGeneric, internalMutationGeneric, anyApi, queryGeneric, mutationGeneric } from 'convex/server';
9
+ import { throwStructuredMutationError } from '@lucern/access-control/structuredMutationError';
8
10
  import '@lucern/contracts/schema-helpers/spine/tables/epistemicNodes';
9
11
  import { generateGlobalId } from '@lucern/contracts/ids';
10
12
 
11
13
  // src/epistemicContracts.handlers.ts
14
+ var unsafeApi = unsafeConvexAnyApi(
15
+ "graph-primitives top-level module bundle lacks a committed Convex _generated/api surface"
16
+ );
17
+ var api = unsafeApi;
18
+ componentsGeneric();
19
+ var internal = unsafeApi;
20
+ var internalMutation = internalMutationGeneric;
21
+ var mutation = mutationGeneric;
22
+ var query = queryGeneric;
12
23
 
13
- // src/epistemicContracts.metrics.ts
14
- var ACTIVE_CONTRADICTION_STATUSES = /* @__PURE__ */ new Set([
15
- "unresolved",
16
- "investigating",
17
- "accepted_as_permanent"
18
- ]);
19
- var DEPENDENT_EDGE_TYPES = /* @__PURE__ */ new Set([
20
- "depends_on"
21
- ]);
22
- function classifyContradictionStatus(status) {
23
- if (typeof status !== "string") {
24
- return "active";
24
+ // src/debug.ts
25
+ function isGraphPrimitiveDebugEnabled() {
26
+ const env = globalThis.process?.env;
27
+ return env?.LUCERN_COMPAT_FALLBACK_DEBUG === "1" || env?.LUCERN_GRAPH_DEBUG === "1";
28
+ }
29
+ function debugGraphPrimitiveFallback(message, context) {
30
+ if (!isGraphPrimitiveDebugEnabled()) {
31
+ return;
25
32
  }
26
- if (ACTIVE_CONTRADICTION_STATUSES.has(status)) {
33
+ console.debug(message, context ?? {});
34
+ }
35
+
36
+ // src/beliefLifecycle.ts
37
+ var BELIEF_STATUS_VALUES = [
38
+ "assumption",
39
+ "hypothesis",
40
+ "active",
41
+ "superseded",
42
+ "resolved_true",
43
+ "resolved_false"
44
+ ];
45
+ function isBeliefLifecycleStatus(value) {
46
+ return typeof value === "string" && BELIEF_STATUS_VALUES.includes(value);
47
+ }
48
+ function normalizeLegacyBeliefStatus(value) {
49
+ if (isBeliefLifecycleStatus(value)) {
50
+ return value;
51
+ }
52
+ if (value === "belief" || value === "established" || value === "emerging") {
27
53
  return "active";
28
54
  }
29
- if (status === "resolved_support" || status === "resolved_contra" || status === "belief_forked") {
30
- return "resolved";
55
+ if (value === "fact" || value === "confirmed") {
56
+ return "resolved_true";
31
57
  }
32
- return "resolved";
58
+ if (value === "disconfirmed" || value === "expired") {
59
+ return "resolved_false";
60
+ }
61
+ if (value === "deprecated") {
62
+ return "superseded";
63
+ }
64
+ return null;
33
65
  }
34
- function getEdgeTimestamp(edge) {
35
- if (typeof edge.updatedAt === "number") {
36
- return edge.updatedAt;
66
+ function normalizeBeliefConfidence(confidence) {
67
+ if (typeof confidence !== "number" || !Number.isFinite(confidence)) {
68
+ return null;
37
69
  }
38
- if (typeof edge.createdAt === "number") {
39
- return edge.createdAt;
70
+ if (confidence >= 0 && confidence <= 1) {
71
+ return confidence;
40
72
  }
41
- if (typeof edge._creationTime === "number") {
42
- return edge._creationTime;
73
+ if (confidence > 1 && confidence <= 100) {
74
+ return confidence / 100;
43
75
  }
44
76
  return null;
45
77
  }
46
- async function getEvidenceLinks(ctx, beliefNodeId) {
47
- const edges = await ctx.db.query("epistemicEdges").withIndex(
48
- "by_to_type",
49
- (q) => q.eq("toNodeId", beliefNodeId).eq("edgeType", "informs")
50
- ).collect();
51
- if (edges.length === 0) {
52
- return [];
78
+ function isResolvedByConfidence(confidence) {
79
+ const normalized = normalizeBeliefConfidence(confidence);
80
+ if (normalized === null) {
81
+ return false;
53
82
  }
54
- const nodes = await Promise.all(edges.map((edge) => ctx.db.get(edge.fromNodeId)));
55
- return edges.flatMap((edge, index) => {
56
- const node = nodes[index];
57
- if (!node || node.nodeType !== "evidence" || node.status === "archived") {
58
- return [];
59
- }
60
- return [{ edge, node }];
61
- });
83
+ return normalized <= 0 || normalized >= 1;
62
84
  }
63
- function getEvidenceTags(node) {
64
- const metadata = node.metadata && typeof node.metadata === "object" ? node.metadata : null;
65
- const tags = metadata?.tags;
66
- if (!Array.isArray(tags)) {
67
- return [];
85
+ function getPredictionMetaFromMetadata(metadata) {
86
+ return metadata?.predictionMeta;
87
+ }
88
+ function resolvedPredictionStatus(predictionMeta) {
89
+ if (!predictionMeta || typeof predictionMeta !== "object") {
90
+ return null;
68
91
  }
69
- return tags.filter((tag) => typeof tag === "string");
92
+ const outcome = predictionMeta.outcome;
93
+ if (outcome === "confirmed") {
94
+ return "resolved_true";
95
+ }
96
+ if (outcome === "disconfirmed" || outcome === "expired") {
97
+ return "resolved_false";
98
+ }
99
+ return null;
70
100
  }
71
- async function computeEvidenceCountMetric(ctx, beliefNodeId) {
72
- return (await getEvidenceLinks(ctx, beliefNodeId)).length;
101
+ function shouldTreatBeliefAsResolved(opts) {
102
+ if (isResolvedByConfidence(opts.confidence)) {
103
+ const normalized = normalizeBeliefConfidence(opts.confidence);
104
+ return normalized === 0 ? "resolved_false" : "resolved_true";
105
+ }
106
+ const directPredictionStatus = resolvedPredictionStatus(opts.predictionMeta);
107
+ if (directPredictionStatus) {
108
+ return directPredictionStatus;
109
+ }
110
+ const metadataPredictionStatus = resolvedPredictionStatus(
111
+ getPredictionMetaFromMetadata(opts.metadata)
112
+ );
113
+ if (metadataPredictionStatus) {
114
+ return metadataPredictionStatus;
115
+ }
116
+ return null;
73
117
  }
74
- async function computeTaggedEvidenceCount(args) {
75
- const expectedTag = args.caseSensitive ? args.tag : args.tag.toLowerCase();
76
- const matchedEvidenceIds = (await getEvidenceLinks(args.ctx, args.beliefNodeId)).filter(
77
- ({ node }) => getEvidenceTags(node).some(
78
- (tag) => (args.caseSensitive ? tag : tag.toLowerCase()) === expectedTag
79
- )
80
- ).map(({ node }) => String(node._id));
81
- return {
82
- count: matchedEvidenceIds.length,
83
- matchedEvidenceIds
84
- };
118
+ function resolveBeliefLifecycleStatus(opts) {
119
+ const resolvedStatus = shouldTreatBeliefAsResolved(opts);
120
+ if (resolvedStatus) {
121
+ return resolvedStatus;
122
+ }
123
+ const direct = opts.beliefStatus;
124
+ const normalizedDirect = normalizeLegacyBeliefStatus(direct);
125
+ if (normalizedDirect) {
126
+ const normalized = normalizeBeliefConfidence(opts.confidence);
127
+ if (normalized !== null && isPreValidationBeliefStatus(normalizedDirect)) {
128
+ return "active";
129
+ }
130
+ return normalizedDirect;
131
+ }
132
+ const metaStatus = opts.metadata?.beliefStatus;
133
+ const normalizedMetaStatus = normalizeLegacyBeliefStatus(metaStatus);
134
+ if (normalizedMetaStatus) {
135
+ const normalized = normalizeBeliefConfidence(opts.confidence);
136
+ if (normalized !== null && isPreValidationBeliefStatus(normalizedMetaStatus)) {
137
+ return "active";
138
+ }
139
+ return normalizedMetaStatus;
140
+ }
141
+ return "assumption";
85
142
  }
86
- async function computeContradictionCounts(ctx, beliefNodeId) {
87
- const contradictions = await ctx.db.query("contradictions").withIndex("by_beliefId", (q) => q.eq("beliefId", beliefNodeId)).collect();
88
- return contradictions.reduce(
89
- (counts, contradiction) => {
90
- const status = contradiction.resolutionStatus ?? contradiction.status ?? "unresolved";
91
- if (classifyContradictionStatus(status) === "active") {
92
- counts.activeCount += 1;
93
- } else {
94
- counts.resolvedCount += 1;
95
- }
96
- return counts;
97
- },
98
- { activeCount: 0, resolvedCount: 0 }
99
- );
143
+ function isPreValidationBeliefStatus(status) {
144
+ return status === "assumption" || status === "hypothesis";
100
145
  }
101
- async function computeEvidenceFreshness(ctx, beliefNodeId, now = Date.now()) {
102
- const timestamps = (await getEvidenceLinks(ctx, beliefNodeId)).map(({ edge }) => getEdgeTimestamp(edge)).filter((value) => value !== null);
103
- if (timestamps.length === 0) {
104
- return {
105
- newestAgeMs: null,
106
- oldestAgeMs: null,
107
- newestEdgeAt: null,
108
- oldestEdgeAt: null,
109
- edgeCount: 0
110
- };
146
+ function promoteBeliefStatusAfterScoring(status, opts) {
147
+ const resolvedStatus = shouldTreatBeliefAsResolved({ ...opts });
148
+ if (resolvedStatus) {
149
+ return resolvedStatus;
150
+ }
151
+ if (isPreValidationBeliefStatus(status)) {
152
+ return "active";
153
+ }
154
+ return status;
155
+ }
156
+ var LEGACY_SCOPE_FIELD = "graphScopeProjectId";
157
+ async function resolveTopicNodeScopeOrNull(ctx, ref) {
158
+ if (!ctx?.db || typeof ctx.db.query !== "function") {
159
+ return null;
160
+ }
161
+ let node = null;
162
+ try {
163
+ const byGlobalId = await ctx.db.query("epistemicNodes").withIndex("by_globalId", (q) => q.eq("globalId", ref)).first();
164
+ if (byGlobalId && byGlobalId.nodeType === "topic") {
165
+ node = byGlobalId;
166
+ }
167
+ } catch (error) {
168
+ debugGraphPrimitiveFallback(
169
+ "[topicScope] topic-node scope lookup by globalId failed",
170
+ { error, ref }
171
+ );
172
+ }
173
+ if (!node) {
174
+ return null;
175
+ }
176
+ const scopeKey = normalizeScopeValue(node.topicId) ?? normalizeScopeValue(node.globalId);
177
+ if (!scopeKey) {
178
+ return null;
111
179
  }
112
- const newestEdgeAt = Math.max(...timestamps);
113
- const oldestEdgeAt = Math.min(...timestamps);
114
180
  return {
115
- newestAgeMs: Math.max(0, now - newestEdgeAt),
116
- oldestAgeMs: Math.max(0, now - oldestEdgeAt),
117
- newestEdgeAt,
118
- oldestEdgeAt,
119
- edgeCount: timestamps.length
181
+ topicId: scopeKey,
182
+ projectId: asMappedProjectId(node),
183
+ source: "topic_node"
120
184
  };
121
185
  }
122
- async function computeDependentBeliefCount(ctx, beliefNodeId) {
123
- const incomingEdges = await ctx.db.query("epistemicEdges").withIndex("by_to", (q) => q.eq("toNodeId", beliefNodeId)).collect();
124
- const dependencyEdges = incomingEdges.filter(
125
- (edge) => DEPENDENT_EDGE_TYPES.has(edge.edgeType)
126
- );
127
- if (dependencyEdges.length === 0) {
128
- return 0;
186
+ function asMappedProjectId(topic) {
187
+ if (!topic) {
188
+ return;
129
189
  }
130
- const dependentBeliefs = await Promise.all(
131
- dependencyEdges.map((edge) => ctx.db.get(edge.fromNodeId))
190
+ const directLegacyProjectId = normalizeScopeValue(
191
+ topic[LEGACY_SCOPE_FIELD]
132
192
  );
133
- const uniqueBeliefIds = /* @__PURE__ */ new Set();
134
- for (const node of dependentBeliefs) {
135
- if (node && node.nodeType === "belief" && node.status !== "archived" && node.status !== "deleted") {
136
- uniqueBeliefIds.add(String(node._id));
137
- }
193
+ if (directLegacyProjectId) {
194
+ return directLegacyProjectId;
138
195
  }
139
- return uniqueBeliefIds.size;
196
+ const metadata = topic.metadata || {};
197
+ const candidate = metadata[LEGACY_SCOPE_FIELD] || metadata.legacyProjectId || metadata.projectId || metadata.scopeProjectId;
198
+ return typeof candidate === "string" ? normalizeScopeValue(candidate) : void 0;
140
199
  }
141
- async function snapshotEvidentialMetric(args) {
142
- switch (args.metric) {
143
- case "evidence_count": {
144
- const count = await computeEvidenceCountMetric(args.ctx, args.beliefNodeId);
145
- return {
146
- metric: args.metric,
147
- value: count,
148
- data: { evidenceCount: count }
149
- };
150
- }
151
- case "contradiction_status": {
152
- const counts = await computeContradictionCounts(args.ctx, args.beliefNodeId);
153
- return {
154
- metric: args.metric,
155
- value: counts.activeCount,
156
- data: counts
157
- };
158
- }
159
- case "edge_freshness": {
160
- const freshness = await computeEvidenceFreshness(
161
- args.ctx,
162
- args.beliefNodeId,
163
- args.now
164
- );
165
- return {
166
- metric: args.metric,
167
- value: freshness.newestAgeMs,
168
- data: freshness
169
- };
200
+ function normalizeScopeValue(value) {
201
+ if (typeof value !== "string") {
202
+ return;
203
+ }
204
+ const normalized = value.trim();
205
+ return normalized.length > 0 ? normalized : void 0;
206
+ }
207
+ function pickPrimaryTopic(candidates) {
208
+ return [...candidates].sort((a, b) => {
209
+ const depthA = a.depth ?? 9999;
210
+ const depthB = b.depth ?? 9999;
211
+ if (depthA !== depthB) {
212
+ return depthA - depthB;
170
213
  }
171
- case "dependent_count": {
172
- const count = await computeDependentBeliefCount(args.ctx, args.beliefNodeId);
173
- return {
174
- metric: args.metric,
175
- value: count,
176
- data: { dependentCount: count }
177
- };
214
+ const createdA = a.createdAt ?? Number.MAX_SAFE_INTEGER;
215
+ const createdB = b.createdAt ?? Number.MAX_SAFE_INTEGER;
216
+ if (createdA !== createdB) {
217
+ return createdA - createdB;
178
218
  }
179
- default:
180
- return {
181
- metric: args.metric,
182
- value: null,
183
- data: {}
184
- };
219
+ return String(a.name || "").localeCompare(String(b.name || ""));
220
+ })[0];
221
+ }
222
+ async function findTopicsByScopeAlias(ctx, scopeId) {
223
+ const query2 = ctx.db.query("topics");
224
+ try {
225
+ return await query2.withIndex(
226
+ "by_graph_scope_project",
227
+ (q) => q.eq(LEGACY_SCOPE_FIELD, scopeId)
228
+ ).collect();
229
+ } catch (error) {
230
+ debugGraphPrimitiveFallback(
231
+ "[topicScope] Failed to resolve scope alias via index",
232
+ {
233
+ error,
234
+ scopeId
235
+ }
236
+ );
237
+ const topics = await query2.collect();
238
+ return topics.filter((topic) => {
239
+ const normalizedGlobalId = normalizeScopeValue(topic.globalId);
240
+ const mappedProjectId = asMappedProjectId(topic);
241
+ return String(topic._id) === scopeId || normalizedGlobalId === scopeId || mappedProjectId === scopeId;
242
+ });
185
243
  }
186
244
  }
187
- async function evaluateBuiltInEvidentialContract(args) {
188
- const config = parseEvidentialEvaluatorConfig(args.contract.condition.evaluatorConfig);
189
- const snapshot = await snapshotEvidentialMetric({
190
- ctx: args.ctx,
191
- beliefNodeId: args.belief._id,
192
- metric: config.metric,
193
- now: args.now
194
- });
195
- const comparisonSatisfied = snapshot.value !== null && compareMetricValue(config.operator, snapshot.value, config.threshold);
196
- const result = args.contract.direction === "falsifies" ? comparisonSatisfied ? "disconfirmed" : "confirmed" : comparisonSatisfied ? "confirmed" : "disconfirmed";
197
- return {
198
- result,
199
- rationale: buildEvidentialRationale({
200
- config,
201
- snapshot,
202
- comparisonSatisfied,
203
- result
204
- }),
205
- data: {
206
- ...snapshot.data,
207
- metric: config.metric,
208
- observedValue: snapshot.value,
209
- operator: config.operator,
210
- threshold: config.threshold,
211
- action: config.action ?? "append_sl_scoring",
212
- actionParams: config.actionParams
213
- }
214
- };
245
+ async function tryResolveHostTopicById(ctx, topicId) {
246
+ if (typeof ctx.runQuery !== "function") {
247
+ return null;
248
+ }
249
+ try {
250
+ return await ctx.runQuery(api.topics.get, {
251
+ id: topicId
252
+ }) ?? null;
253
+ } catch (error) {
254
+ debugGraphPrimitiveFallback(
255
+ "[topicScope] Failed to resolve topic by host query",
256
+ {
257
+ error,
258
+ topicId
259
+ }
260
+ );
261
+ return null;
262
+ }
215
263
  }
216
- async function evaluateMetricCheckerContract(args) {
217
- const config = parseMetricCheckerConfig(args.contract.condition.evaluatorConfig);
218
- const input = getEvaluatorInputRecord(args.inputData, "metricData");
219
- const metric = typeof input.metric === "string" && input.metric.length > 0 ? input.metric : config.metric;
220
- const observedValue = pickFiniteNumber(input, [
221
- "observedValue",
222
- "currentValue",
223
- "metricValue",
224
- "value"
225
- ]) ?? config.observedValue ?? config.currentValue ?? config.metricValue ?? null;
226
- if (observedValue === null) {
227
- return {
228
- result: "inconclusive",
229
- rationale: `metric_checker is awaiting data for ${metric ?? args.contract.condition.expression}.`,
230
- data: {
231
- metric,
232
- observedValue: null,
233
- operator: config.operator,
234
- threshold: config.threshold,
235
- unit: config.unit
264
+ async function tryResolveHostTopicByLegacyScope(ctx, legacyScopeId) {
265
+ if (typeof ctx.runQuery !== "function") {
266
+ return null;
267
+ }
268
+ try {
269
+ return await ctx.runQuery(api.topics.getByLegacyScopeId, {
270
+ projectId: legacyScopeId
271
+ }) ?? null;
272
+ } catch (error) {
273
+ debugGraphPrimitiveFallback(
274
+ "[topicScope] Failed to resolve topic by legacy scope",
275
+ {
276
+ error,
277
+ legacyScopeId
236
278
  }
237
- };
279
+ );
280
+ return null;
238
281
  }
239
- const comparisonSatisfied = compareMetricValue(
240
- config.operator,
241
- observedValue,
242
- config.threshold
243
- );
244
- const result = resolveComparisonResult(args.contract.direction, comparisonSatisfied);
245
- return {
246
- result,
247
- rationale: buildComparisonRationale({
248
- label: metric ?? "metric",
249
- observedValue,
250
- operator: config.operator,
251
- threshold: config.threshold,
252
- comparisonSatisfied,
253
- result,
254
- unit: config.unit
255
- }),
256
- data: {
257
- metric,
258
- observedValue,
259
- operator: config.operator,
260
- threshold: config.threshold,
261
- unit: config.unit
282
+ }
283
+ async function resolveInheritedWorkspaceScope(ctx, topic) {
284
+ const MAX_DEPTH = 10;
285
+ let tenantId = normalizeScopeValue(topic.tenantId);
286
+ let workspaceId = normalizeScopeValue(topic.workspaceId);
287
+ if (tenantId && workspaceId) {
288
+ return { tenantId, workspaceId };
289
+ }
290
+ let current = topic;
291
+ for (let i = 0; i < MAX_DEPTH && current?.parentTopicId; i++) {
292
+ current = await ctx.db.get(current.parentTopicId);
293
+ if (!current) {
294
+ break;
262
295
  }
263
- };
296
+ if (!tenantId) {
297
+ tenantId = normalizeScopeValue(current.tenantId);
298
+ }
299
+ if (!workspaceId) {
300
+ workspaceId = normalizeScopeValue(current.workspaceId);
301
+ }
302
+ if (tenantId && workspaceId) {
303
+ break;
304
+ }
305
+ }
306
+ return { tenantId, workspaceId };
264
307
  }
265
- async function evaluateReferenceCheckCounterContract(args) {
266
- const config = parseReferenceCheckCounterConfig(args.contract.condition.evaluatorConfig);
267
- const input = getEvaluatorInputRecord(args.inputData, "referenceCheckData");
268
- const tag = typeof input.tag === "string" && input.tag.trim().length > 0 ? input.tag.trim() : config.tag;
269
- const snapshot = await computeTaggedEvidenceCount({
270
- ctx: args.ctx,
271
- beliefNodeId: args.belief._id,
272
- tag,
273
- caseSensitive: config.caseSensitive
274
- });
275
- const comparisonSatisfied = compareMetricValue(
276
- config.operator,
277
- snapshot.count,
278
- config.threshold
308
+ async function resolveTopicProjectScope(ctx, args) {
309
+ if (args.topicId) {
310
+ return await resolveScopeFromTopicId(ctx, args.topicId);
311
+ }
312
+ if (args.projectId) {
313
+ return await resolveScopeFromLegacyProjectId(ctx, args.projectId);
314
+ }
315
+ throw new Error(
316
+ "Missing scope: provide topicId (preferred) or legacy projectId alias."
279
317
  );
280
- const result = resolveComparisonResult(args.contract.direction, comparisonSatisfied);
281
- return {
282
- result,
283
- rationale: buildComparisonRationale({
284
- label: `reference checks tagged "${tag}"`,
285
- observedValue: snapshot.count,
286
- operator: config.operator,
287
- threshold: config.threshold,
288
- comparisonSatisfied,
289
- result
290
- }),
291
- data: {
292
- tag,
293
- observedValue: snapshot.count,
294
- referenceCheckCount: snapshot.count,
295
- matchedEvidenceIds: snapshot.matchedEvidenceIds,
296
- operator: config.operator,
297
- threshold: config.threshold,
298
- caseSensitive: config.caseSensitive ?? false
299
- }
300
- };
301
318
  }
302
- async function evaluateTemporalDeadlineContract(args) {
303
- if (typeof args.contract.deadline !== "number" || !Number.isFinite(args.contract.deadline)) {
304
- throw new Error(
305
- "temporal_deadline requires contract.deadline to be set to a finite timestamp."
306
- );
319
+ async function resolveScopeFromTopicId(ctx, topicId) {
320
+ const topic = await resolveTopicDocFromTopicId(ctx, topicId);
321
+ if (topic) {
322
+ return await buildTopicScope(ctx, topic, "topic");
307
323
  }
308
- const config = parseTemporalDeadlineConfig(args.contract.condition.evaluatorConfig);
309
- const input = getEvaluatorInputRecord(args.inputData, "temporalData");
310
- const label = (typeof input.label === "string" && input.label.length > 0 ? input.label : config.label) ?? args.contract.title ?? args.contract.condition.expression;
311
- const completedAt = pickFiniteNumber(input, [
312
- "completedAt",
313
- "observedAt",
314
- "satisfiedAt",
315
- "achievedAt"
316
- ]) ?? config.completedAt ?? config.observedAt ?? config.satisfiedAt ?? config.achievedAt;
317
- const completed = input.completed === true || config.completed === true || completedAt !== void 0;
318
- if (completed) {
319
- if (completedAt !== void 0 && completedAt > args.contract.deadline) {
320
- return {
321
- result: "expired",
322
- rationale: `${label} completed at ${completedAt}, after deadline ${args.contract.deadline}.`,
323
- data: {
324
- label,
325
- deadline: args.contract.deadline,
326
- completed: true,
327
- completedAt,
328
- missedDeadline: true,
329
- overdueByMs: completedAt - args.contract.deadline
330
- }
331
- };
332
- }
333
- const result = args.contract.direction === "falsifies" ? "disconfirmed" : "confirmed";
334
- return {
335
- result,
336
- rationale: `${label} completed before deadline ${args.contract.deadline}.`,
337
- data: {
338
- label,
339
- deadline: args.contract.deadline,
340
- completed: true,
341
- completedAt: completedAt ?? null,
342
- missedDeadline: false
343
- }
344
- };
324
+ const nodeScope = await resolveTopicNodeScopeOrNull(ctx, String(topicId));
325
+ if (nodeScope) {
326
+ return nodeScope;
327
+ }
328
+ throw new Error(`Topic not found: ${String(topicId)}`);
329
+ }
330
+ async function resolveTopicDocFromTopicId(ctx, topicId) {
331
+ const direct = await tryReadTopicDoc(ctx, topicId, {
332
+ failureLog: "[topicScope] Failed to load topic by direct id",
333
+ idLogKey: "topicId"
334
+ });
335
+ if (direct) {
336
+ return direct;
337
+ }
338
+ const hostTopic = await tryResolveHostTopicById(ctx, String(topicId));
339
+ if (hostTopic) {
340
+ return hostTopic;
345
341
  }
346
- if (args.now > args.contract.deadline) {
342
+ return pickPrimaryTopic(await findTopicsByScopeAlias(ctx, String(topicId))) ?? null;
343
+ }
344
+ async function resolveScopeFromLegacyProjectId(ctx, legacyProjectId) {
345
+ const directTopic = await resolveDirectLegacyProjectTopic(
346
+ ctx,
347
+ legacyProjectId
348
+ );
349
+ if (directTopic) {
350
+ return await buildTopicScope(ctx, directTopic, "topic_inferred", {
351
+ fallbackProjectId: legacyProjectId
352
+ });
353
+ }
354
+ const primary = pickPrimaryTopic(
355
+ await findTopicsByScopeAlias(ctx, legacyProjectId)
356
+ );
357
+ if (primary) {
358
+ return await buildTopicScope(ctx, primary, "project_mapped_topic", {
359
+ fallbackProjectId: legacyProjectId
360
+ });
361
+ }
362
+ const nodeScope = await resolveTopicNodeScopeOrNull(ctx, legacyProjectId);
363
+ if (nodeScope) {
347
364
  return {
348
- result: "expired",
349
- rationale: `${label} missed deadline ${args.contract.deadline}; temporal contract expired.`,
350
- data: {
351
- label,
352
- deadline: args.contract.deadline,
353
- completed: false,
354
- overdueByMs: args.now - args.contract.deadline
355
- }
365
+ ...nodeScope,
366
+ projectId: nodeScope.projectId ?? legacyProjectId
356
367
  };
357
368
  }
369
+ throw new Error(
370
+ `Legacy project scope ${legacyProjectId} has no mapped topic.`
371
+ );
372
+ }
373
+ async function resolveDirectLegacyProjectTopic(ctx, legacyProjectId) {
374
+ const directTopic = await tryReadTopicDoc(ctx, legacyProjectId, {
375
+ failureLog: "[topicScope] Failed to load direct project topic",
376
+ idLogKey: "projectId"
377
+ });
378
+ return directTopic ?? tryResolveHostTopicByLegacyScope(ctx, legacyProjectId);
379
+ }
380
+ async function tryReadTopicDoc(ctx, id, log) {
381
+ try {
382
+ return await ctx.db.get(id);
383
+ } catch (error) {
384
+ debugGraphPrimitiveFallback(log.failureLog, {
385
+ error,
386
+ [log.idLogKey]: id
387
+ });
388
+ return null;
389
+ }
390
+ }
391
+ async function buildTopicScope(ctx, topic, source, options = {}) {
392
+ const inherited = await resolveInheritedWorkspaceScope(ctx, topic);
393
+ const mapped = asMappedProjectId(topic);
358
394
  return {
359
- result: "inconclusive",
360
- rationale: `${label} is still before deadline ${args.contract.deadline}; awaiting outcome.`,
361
- data: {
362
- label,
363
- deadline: args.contract.deadline,
364
- completed: false,
365
- timeRemainingMs: args.contract.deadline - args.now
366
- }
395
+ topicId: topic._id,
396
+ ...mapped || options.fallbackProjectId ? { projectId: mapped ?? options.fallbackProjectId } : {},
397
+ tenantId: inherited.tenantId,
398
+ workspaceId: inherited.workspaceId,
399
+ source
367
400
  };
368
401
  }
369
- async function evaluateMarketIndexComparatorContract(args) {
370
- const config = parseMarketIndexComparatorConfig(args.contract.condition.evaluatorConfig);
371
- const input = getEvaluatorInputRecord(args.inputData, "marketIndexData");
372
- const subject = typeof input.subject === "string" && input.subject.length > 0 ? input.subject : config.subject;
373
- const benchmark = typeof input.benchmark === "string" && input.benchmark.length > 0 ? input.benchmark : config.benchmark;
374
- const subjectValue = pickFiniteNumber(input, ["subjectValue", "primaryValue", "leftValue"]) ?? config.subjectValue ?? config.primaryValue ?? null;
375
- const benchmarkValue = pickFiniteNumber(input, ["benchmarkValue", "comparisonValue", "rightValue"]) ?? config.benchmarkValue ?? config.comparisonValue ?? null;
376
- if (subjectValue === null || benchmarkValue === null) {
377
- return {
378
- result: "inconclusive",
379
- rationale: "market_index_comparator is awaiting both subject and benchmark values.",
380
- data: {
381
- subject,
382
- subjectValue,
383
- benchmark,
384
- benchmarkValue,
385
- operator: config.operator,
386
- threshold: config.threshold
387
- }
388
- };
389
- }
390
- if (benchmarkValue === 0) {
391
- throw new Error(
392
- "market_index_comparator cannot compare against a zero benchmark value."
393
- );
402
+ ({
403
+ projectId: v.optional(v.string()),
404
+ topicId: v.optional(v.string())
405
+ });
406
+ function normalizeScopeValue2(value) {
407
+ if (typeof value !== "string") {
408
+ return;
394
409
  }
395
- const differentialPercent = (subjectValue - benchmarkValue) / Math.abs(benchmarkValue) * 100;
396
- const comparisonSatisfied = compareMetricValue(
397
- config.operator,
398
- differentialPercent,
399
- config.threshold
400
- );
401
- const result = resolveComparisonResult(args.contract.direction, comparisonSatisfied);
402
- return {
403
- result,
404
- rationale: buildComparisonRationale({
405
- label: `${subject ?? "subject"} vs ${benchmark ?? "benchmark"} differential`,
406
- observedValue: differentialPercent,
407
- operator: config.operator,
408
- threshold: config.threshold,
409
- comparisonSatisfied,
410
- result,
411
- unit: "%"
412
- }),
413
- data: {
414
- subject,
415
- subjectValue,
416
- benchmark,
417
- benchmarkValue,
418
- differentialPercent,
419
- operator: config.operator,
420
- threshold: config.threshold
421
- }
422
- };
423
- }
424
- var METRIC_COMPARATOR_EVALUATOR_NAMES = {
425
- evidentialAliases: /* @__PURE__ */ new Set(["evidential", "built_in_evidential", "builtin_evidential"]),
426
- metricChecker: "metric_checker",
427
- referenceCheckCounter: "reference_check_counter",
428
- temporalDeadline: "temporal_deadline",
429
- marketIndexComparator: "market_index_comparator"
430
- };
431
-
432
- // src/beliefLifecycle.ts
433
- var BELIEF_STATUS_VALUES = [
434
- "assumption",
435
- "hypothesis",
436
- "active",
437
- "superseded",
438
- "resolved_true",
439
- "resolved_false"
440
- ];
441
- function isBeliefLifecycleStatus(value) {
442
- return typeof value === "string" && BELIEF_STATUS_VALUES.includes(value);
410
+ const normalized = value.trim();
411
+ return normalized.length > 0 ? normalized : void 0;
443
412
  }
444
- function normalizeLegacyBeliefStatus(value) {
445
- if (isBeliefLifecycleStatus(value)) {
446
- return value;
447
- }
448
- if (value === "belief" || value === "established" || value === "emerging") {
449
- return "active";
450
- }
451
- if (value === "fact" || value === "confirmed") {
452
- return "resolved_true";
453
- }
454
- if (value === "disconfirmed" || value === "expired") {
455
- return "resolved_false";
413
+ function nodeMatchesWorkspaceReasoningScope(node, scope) {
414
+ if (!node) {
415
+ return false;
456
416
  }
457
- if (value === "deprecated") {
458
- return "superseded";
417
+ const scopeTenantId = normalizeScopeValue2(scope.tenantId);
418
+ const scopeWorkspaceId = normalizeScopeValue2(scope.workspaceId);
419
+ const nodeTenantId = normalizeScopeValue2(node.tenantId);
420
+ const nodeWorkspaceId = normalizeScopeValue2(node.workspaceId);
421
+ const epistemicLayer = typeof node.epistemicLayer === "string" ? node.epistemicLayer : void 0;
422
+ if (scopeTenantId && nodeTenantId && scopeTenantId !== nodeTenantId) {
423
+ return false;
459
424
  }
460
- return null;
461
- }
462
- function normalizeBeliefConfidence(confidence) {
463
- if (typeof confidence !== "number" || !Number.isFinite(confidence)) {
464
- return null;
425
+ if (epistemicLayer === "ontological" && nodeWorkspaceId === void 0) {
426
+ return true;
465
427
  }
466
- if (confidence >= 0 && confidence <= 1) {
467
- return confidence;
428
+ if (!scopeWorkspaceId && node.publicationStatus === "published") {
429
+ return true;
468
430
  }
469
- if (confidence > 1 && confidence <= 100) {
470
- return confidence / 100;
431
+ if (!scopeWorkspaceId) {
432
+ return nodeWorkspaceId === void 0;
471
433
  }
472
- return null;
434
+ return scopeWorkspaceId === nodeWorkspaceId;
473
435
  }
474
- function isResolvedByConfidence(confidence) {
475
- const normalized = normalizeBeliefConfidence(confidence);
476
- if (normalized === null) {
436
+ function edgeMatchesWorkspaceReasoningScope(edge, scope) {
437
+ if (!edge) {
477
438
  return false;
478
439
  }
479
- return normalized <= 0 || normalized >= 1;
480
- }
481
- function getPredictionMetaFromMetadata(metadata) {
482
- return metadata?.predictionMeta;
483
- }
484
- function resolvedPredictionStatus(predictionMeta) {
485
- if (!predictionMeta || typeof predictionMeta !== "object") {
486
- return null;
487
- }
488
- const outcome = predictionMeta.outcome;
489
- if (outcome === "confirmed") {
490
- return "resolved_true";
440
+ const scopeTenantId = normalizeScopeValue2(scope.tenantId);
441
+ const scopeWorkspaceId = normalizeScopeValue2(scope.workspaceId);
442
+ const edgeTenantId = normalizeScopeValue2(edge.tenantId);
443
+ const edgeWorkspaceId = normalizeScopeValue2(edge.workspaceId);
444
+ if (scopeTenantId && edgeTenantId && scopeTenantId !== edgeTenantId) {
445
+ return false;
491
446
  }
492
- if (outcome === "disconfirmed" || outcome === "expired") {
493
- return "resolved_false";
447
+ if (!scopeWorkspaceId) {
448
+ return edgeWorkspaceId === void 0;
494
449
  }
495
- return null;
450
+ return scopeWorkspaceId === edgeWorkspaceId;
496
451
  }
497
- function shouldTreatBeliefAsResolved(opts) {
498
- if (isResolvedByConfidence(opts.confidence)) {
499
- const normalized = normalizeBeliefConfidence(opts.confidence);
500
- return normalized === 0 ? "resolved_false" : "resolved_true";
501
- }
502
- const directPredictionStatus = resolvedPredictionStatus(opts.predictionMeta);
503
- if (directPredictionStatus) {
504
- return directPredictionStatus;
452
+ async function resolveNodeScopeForWorkspaceIsolation(ctx, node) {
453
+ const epistemicLayer = typeof node?.epistemicLayer === "string" ? node.epistemicLayer : void 0;
454
+ const resolved = {
455
+ tenantId: normalizeScopeValue2(node?.tenantId),
456
+ workspaceId: normalizeScopeValue2(node?.workspaceId),
457
+ epistemicLayer,
458
+ nodeType: typeof node?.nodeType === "string" ? node.nodeType : void 0
459
+ };
460
+ if (!node) {
461
+ return resolved;
505
462
  }
506
- const metadataPredictionStatus = resolvedPredictionStatus(
507
- getPredictionMetaFromMetadata(opts.metadata)
508
- );
509
- if (metadataPredictionStatus) {
510
- return metadataPredictionStatus;
463
+ if (resolved.epistemicLayer === "ontological") {
464
+ return resolved;
511
465
  }
512
- return null;
513
- }
514
- function resolveBeliefLifecycleStatus(opts) {
515
- const resolvedStatus = shouldTreatBeliefAsResolved(opts);
516
- if (resolvedStatus) {
517
- return resolvedStatus;
466
+ if (resolved.tenantId || resolved.workspaceId) {
467
+ return resolved;
518
468
  }
519
- const direct = opts.beliefStatus;
520
- const normalizedDirect = normalizeLegacyBeliefStatus(direct);
521
- if (normalizedDirect) {
522
- const normalized = normalizeBeliefConfidence(opts.confidence);
523
- if (normalized !== null && isPreValidationBeliefStatus(normalizedDirect)) {
524
- return "active";
525
- }
526
- return normalizedDirect;
469
+ const topicId = normalizeScopeValue2(node.topicId);
470
+ if (topicId) {
471
+ const topicScope = await resolveTopicProjectScope(ctx, {
472
+ topicId
473
+ });
474
+ return {
475
+ ...resolved,
476
+ tenantId: topicScope.tenantId,
477
+ workspaceId: topicScope.workspaceId
478
+ };
527
479
  }
528
- const metaStatus = opts.metadata?.beliefStatus;
529
- const normalizedMetaStatus = normalizeLegacyBeliefStatus(metaStatus);
530
- if (normalizedMetaStatus) {
531
- const normalized = normalizeBeliefConfidence(opts.confidence);
532
- if (normalized !== null && isPreValidationBeliefStatus(normalizedMetaStatus)) {
533
- return "active";
534
- }
535
- return normalizedMetaStatus;
480
+ if (node.projectId) {
481
+ const topicScope = await resolveTopicProjectScope(ctx, {
482
+ projectId: String(node.projectId)
483
+ });
484
+ return {
485
+ ...resolved,
486
+ tenantId: topicScope.tenantId,
487
+ workspaceId: topicScope.workspaceId
488
+ };
536
489
  }
537
- return "assumption";
490
+ return resolved;
538
491
  }
539
- function isPreValidationBeliefStatus(status) {
540
- return status === "assumption" || status === "hypothesis";
492
+ v.id("epistemicNodes");
493
+ var DEFAULT_CONFIDENCE_POLICY = {
494
+ scoringMode: "after_worktree",
495
+ tupleContradiction: normalizeTupleContradictionPolicy()
496
+ };
497
+ function buildBeliefConfidenceRow(args) {
498
+ return {
499
+ beliefId: args.beliefId,
500
+ confidence: confidenceFromSL(
501
+ args.belief,
502
+ args.disbelief,
503
+ args.uncertainty,
504
+ args.baseRate
505
+ ),
506
+ belief: args.belief,
507
+ disbelief: args.disbelief,
508
+ uncertainty: args.uncertainty,
509
+ baseRate: args.baseRate,
510
+ slOperator: args.slOperator ?? "prior_seed",
511
+ trigger: args.trigger,
512
+ ...args.rationale ? { rationale: args.rationale } : {},
513
+ assessedBy: args.assessedBy,
514
+ assessedAt: args.assessedAt,
515
+ ...args.triggeringEvidenceId ? {
516
+ triggeringEvidenceId: args.triggeringEvidenceId,
517
+ triggeringEvidenceIds: [String(args.triggeringEvidenceId)]
518
+ } : {},
519
+ ...args.triggeringContradictionId ? {
520
+ triggeringContradictionId: args.triggeringContradictionId
521
+ } : {},
522
+ ...args.triggeringWorktreeId ? { triggeringWorktreeId: args.triggeringWorktreeId } : {}
523
+ };
541
524
  }
542
- function promoteBeliefStatusAfterScoring(status, opts) {
543
- const resolvedStatus = shouldTreatBeliefAsResolved({ ...opts });
544
- if (resolvedStatus) {
545
- return resolvedStatus;
546
- }
547
- if (isPreValidationBeliefStatus(status)) {
548
- return "active";
525
+ function readTupleContradictedFlag(value) {
526
+ return typeof value === "boolean" ? value : void 0;
527
+ }
528
+ function readBeliefOpinionSnapshot(node, metadata) {
529
+ try {
530
+ return readOpinionFromRecord({
531
+ ...metadata,
532
+ opinion_b: node.opinion_b,
533
+ opinion_d: node.opinion_d,
534
+ opinion_u: node.opinion_u,
535
+ opinion_a: node.opinion_a
536
+ });
537
+ } catch (error) {
538
+ debugGraphPrimitiveFallback(
539
+ "[epistemicBeliefs] Failed to read belief opinion snapshot",
540
+ {
541
+ error,
542
+ beliefId: node._id
543
+ }
544
+ );
545
+ return mkOpinion(0, 0, 1, 0.5);
549
546
  }
550
- return status;
551
547
  }
552
- var api = anyApi;
553
- componentsGeneric();
554
- var internal = anyApi;
555
- var internalMutation = internalMutationGeneric;
556
- var mutation = mutationGeneric;
557
- var query = queryGeneric;
558
-
559
- // src/debug.ts
560
- function isGraphPrimitiveDebugEnabled() {
561
- const env = globalThis.process?.env;
562
- return env?.LUCERN_COMPAT_FALLBACK_DEBUG === "1" || env?.LUCERN_GRAPH_DEBUG === "1";
563
- }
564
- function debugGraphPrimitiveFallback(message, context) {
565
- if (!isGraphPrimitiveDebugEnabled()) {
566
- return;
567
- }
568
- console.debug(message, context ?? {});
569
- }
570
- var LEGACY_SCOPE_FIELD = "graphScopeProjectId";
571
- async function resolveTopicNodeScopeOrNull(ctx, ref) {
572
- if (!ctx?.db || typeof ctx.db.query !== "function") {
573
- return null;
574
- }
575
- let node = null;
576
- try {
577
- const byGlobalId = await ctx.db.query("epistemicNodes").withIndex("by_globalId", (q) => q.eq("globalId", ref)).first();
578
- if (byGlobalId && byGlobalId.nodeType === "topic") {
579
- node = byGlobalId;
580
- }
581
- } catch (error) {
582
- debugGraphPrimitiveFallback(
583
- "[topicScope] topic-node scope lookup by globalId failed",
584
- { error, ref }
585
- );
586
- }
587
- if (!node) {
588
- return null;
548
+ function deriveTupleContradictionSeverity(node) {
549
+ const metadata = node.metadata || {};
550
+ const criticality = typeof metadata.criticality === "string" ? metadata.criticality : void 0;
551
+ if (criticality === "blocking") {
552
+ return "critical";
589
553
  }
590
- const scopeKey = normalizeScopeValue(node.topicId) ?? normalizeScopeValue(node.globalId);
591
- if (!scopeKey) {
592
- return null;
554
+ if (criticality === "supporting") {
555
+ return "minor";
593
556
  }
594
- return {
595
- topicId: scopeKey,
596
- projectId: asMappedProjectId(node),
597
- source: "topic_node"
598
- };
557
+ return "significant";
599
558
  }
600
- function asMappedProjectId(topic) {
601
- if (!topic) {
602
- return;
603
- }
604
- const directLegacyProjectId = normalizeScopeValue(topic[LEGACY_SCOPE_FIELD]);
605
- if (directLegacyProjectId) {
606
- return directLegacyProjectId;
607
- }
608
- const metadata = topic.metadata || {};
609
- const candidate = metadata[LEGACY_SCOPE_FIELD] || metadata.legacyProjectId || metadata.projectId || metadata.scopeProjectId;
610
- return candidate ? candidate : void 0;
559
+ function formatTupleContradictionDescription(args) {
560
+ return `Tuple-space contradiction detected: b=${args.opinion.b.toFixed(2)} > ${args.policy.beliefThreshold.toFixed(2)} and d=${args.opinion.d.toFixed(2)} > ${args.policy.disbeliefThreshold.toFixed(2)}.`;
611
561
  }
612
- function normalizeScopeValue(value) {
613
- if (typeof value !== "string") {
614
- return;
615
- }
616
- const normalized = value.trim();
617
- return normalized.length > 0 ? normalized : void 0;
562
+ function resolveBeliefStatus(node, metadata) {
563
+ return resolveBeliefLifecycleStatus({
564
+ beliefStatus: node.beliefStatus,
565
+ confidence: node.confidence,
566
+ predictionMeta: node.predictionMeta,
567
+ metadata
568
+ });
618
569
  }
619
- function pickPrimaryTopic(candidates) {
620
- return [...candidates].sort((a, b) => {
621
- const depthA = a.depth ?? 9999;
622
- const depthB = b.depth ?? 9999;
623
- if (depthA !== depthB) {
624
- return depthA - depthB;
625
- }
626
- const createdA = a.createdAt ?? Number.MAX_SAFE_INTEGER;
627
- const createdB = b.createdAt ?? Number.MAX_SAFE_INTEGER;
628
- if (createdA !== createdB) {
629
- return createdA - createdB;
570
+ async function hasCompletedWorktreeForBelief(ctx, beliefNodeId) {
571
+ const clusterMembership = await ctx.db.query("worktreeBeliefCluster").withIndex(
572
+ "by_belief",
573
+ (q) => q.eq("beliefId", beliefNodeId)
574
+ ).collect();
575
+ for (const membership of clusterMembership) {
576
+ const worktree = await ctx.db.get(membership.worktreeId);
577
+ if (worktree?.status === "completed" || worktree?.status === "merged") {
578
+ return true;
630
579
  }
631
- return String(a.name || "").localeCompare(String(b.name || ""));
632
- })[0];
633
- }
634
- async function findTopicsByScopeAlias(ctx, scopeId) {
635
- try {
636
- return await ctx.db.query("topics").withIndex(
637
- "by_graph_scope_project",
638
- (q) => q.eq(LEGACY_SCOPE_FIELD, scopeId)
639
- ).collect();
640
- } catch (error) {
641
- debugGraphPrimitiveFallback(
642
- "[topicScope] Failed to resolve scope alias via index",
643
- {
644
- error,
645
- scopeId
646
- }
647
- );
648
- const topics = await ctx.db.query("topics").collect();
649
- return topics.filter((topic) => {
650
- const normalizedGlobalId = normalizeScopeValue(topic.globalId);
651
- const mappedProjectId = asMappedProjectId(topic);
652
- return String(topic._id) === scopeId || normalizedGlobalId === scopeId || mappedProjectId === scopeId;
653
- });
654
580
  }
581
+ return false;
655
582
  }
656
- async function tryResolveHostTopicById(ctx, topicId) {
657
- if (typeof ctx.runQuery !== "function") {
658
- return null;
659
- }
583
+ async function getActiveConfidencePolicy(ctx) {
660
584
  try {
661
- return await ctx.runQuery(api.topics.get, {
662
- id: topicId
663
- }) ?? null;
585
+ const activeConfig = await ctx.db.query("logicSprintScoring").withIndex(
586
+ "by_active",
587
+ (q) => q.eq("isActive", true)
588
+ ).first();
589
+ return {
590
+ scoringMode: activeConfig?.confidencePolicy === "always" ? "always" : DEFAULT_CONFIDENCE_POLICY.scoringMode,
591
+ tupleContradiction: normalizeTupleContradictionPolicy(
592
+ activeConfig?.tupleContradictionPolicy
593
+ )
594
+ };
664
595
  } catch (error) {
665
596
  debugGraphPrimitiveFallback(
666
- "[topicScope] Failed to resolve topic by host query",
597
+ "[epistemicBeliefs] Failed to load active confidence policy",
667
598
  {
668
- error,
669
- topicId
599
+ error
670
600
  }
671
601
  );
672
- return null;
602
+ return DEFAULT_CONFIDENCE_POLICY;
673
603
  }
674
604
  }
675
- async function tryResolveHostTopicByLegacyScope(ctx, legacyScopeId) {
676
- if (typeof ctx.runQuery !== "function") {
677
- return null;
678
- }
679
- try {
680
- return await ctx.runQuery(api.topics.getByLegacyScopeId, {
681
- projectId: legacyScopeId
682
- }) ?? null;
683
- } catch (error) {
684
- debugGraphPrimitiveFallback(
685
- "[topicScope] Failed to resolve topic by legacy scope",
686
- {
687
- error,
688
- legacyScopeId
689
- }
690
- );
691
- return null;
692
- }
605
+
606
+ // src/edges/contains.ts
607
+ var containsPropagationSpec = {
608
+ edgeType: "contains",
609
+ direction: "outgoing",
610
+ transitivity: "none",
611
+ damping: 1,
612
+ maxHops: 1,
613
+ operator: () => null,
614
+ description: "Structural containment only. Traversed for explicit semantics, but it never propagates opinions."
615
+ };
616
+ function readEdgeMetadata(edge) {
617
+ return {
618
+ constraint: edge.constraint ?? void 0,
619
+ normalization: edge.normalization ?? void 0,
620
+ propagation: edge.propagation ?? void 0,
621
+ conditionalA: edge.conditionalA ?? void 0,
622
+ conditionalNotA: edge.conditionalNotA ?? void 0
623
+ };
693
624
  }
694
- async function resolveInheritedWorkspaceScope(ctx, topic) {
695
- const MAX_DEPTH = 10;
696
- let tenantId = normalizeScopeValue(topic.tenantId);
697
- let workspaceId = normalizeScopeValue(topic.workspaceId);
698
- if (tenantId && workspaceId) {
699
- return { tenantId, workspaceId };
700
- }
701
- let current = topic;
702
- for (let i = 0; i < MAX_DEPTH && current?.parentTopicId; i++) {
703
- current = await ctx.db.get(current.parentTopicId);
704
- if (!current) break;
705
- if (!tenantId) {
706
- tenantId = normalizeScopeValue(current.tenantId);
707
- }
708
- if (!workspaceId) {
709
- workspaceId = normalizeScopeValue(current.workspaceId);
710
- }
711
- if (tenantId && workspaceId) break;
625
+ function applyPerHopDamping(sourceOpinion, damping) {
626
+ if (damping >= 1) {
627
+ return sourceOpinion;
712
628
  }
713
- return { tenantId, workspaceId };
629
+ return trustDiscount(sourceOpinion, Math.max(0, damping));
714
630
  }
715
- async function resolveTopicProjectScope(ctx, args) {
716
- if (args.topicId) {
717
- let topic = null;
718
- try {
719
- topic = await ctx.db.get(
720
- args.topicId
721
- );
722
- } catch (error) {
723
- debugGraphPrimitiveFallback(
724
- "[topicScope] Failed to load topic by direct id",
725
- {
726
- error,
727
- topicId: args.topicId
728
- }
729
- );
730
- }
731
- if (!topic) {
732
- topic = await tryResolveHostTopicById(ctx, String(args.topicId));
733
- }
734
- if (!topic) {
735
- topic = pickPrimaryTopic(
736
- await findTopicsByScopeAlias(ctx, String(args.topicId))
737
- ) ?? null;
738
- }
739
- if (!topic) {
740
- const nodeScope = await resolveTopicNodeScopeOrNull(
741
- ctx,
742
- String(args.topicId)
743
- );
744
- if (nodeScope) {
745
- return nodeScope;
746
- }
747
- throw new Error(`Topic not found: ${String(args.topicId)}`);
748
- }
749
- const inherited = await resolveInheritedWorkspaceScope(ctx, topic);
750
- const mapped = asMappedProjectId(topic);
751
- if (mapped) {
752
- return {
753
- topicId: topic._id,
754
- projectId: mapped,
755
- tenantId: inherited.tenantId,
756
- workspaceId: inherited.workspaceId,
757
- source: "topic"
758
- };
759
- }
760
- return {
761
- topicId: topic._id,
762
- tenantId: inherited.tenantId,
763
- workspaceId: inherited.workspaceId,
764
- source: "topic"
765
- };
766
- }
767
- if (args.projectId) {
768
- let directTopic = null;
769
- try {
770
- directTopic = await ctx.db.get(
771
- args.projectId
772
- );
773
- } catch (error) {
774
- debugGraphPrimitiveFallback(
775
- "[topicScope] Failed to load direct project topic",
776
- {
777
- error,
778
- projectId: args.projectId
779
- }
780
- );
781
- }
782
- if (directTopic) {
783
- const inherited = await resolveInheritedWorkspaceScope(ctx, directTopic);
784
- const mapped = asMappedProjectId(directTopic);
785
- return {
786
- topicId: directTopic._id,
787
- projectId: mapped ?? args.projectId,
788
- tenantId: inherited.tenantId,
789
- workspaceId: inherited.workspaceId,
790
- source: "topic_inferred"
791
- };
792
- }
793
- directTopic = await tryResolveHostTopicByLegacyScope(ctx, args.projectId);
794
- if (directTopic) {
795
- const inherited = await resolveInheritedWorkspaceScope(ctx, directTopic);
796
- const mapped = asMappedProjectId(directTopic);
797
- return {
798
- topicId: directTopic._id,
799
- projectId: mapped ?? args.projectId,
800
- tenantId: inherited.tenantId,
801
- workspaceId: inherited.workspaceId,
802
- source: "topic_inferred"
803
- };
804
- }
805
- const topics = await findTopicsByScopeAlias(ctx, args.projectId);
806
- const primary = pickPrimaryTopic(topics);
807
- if (primary) {
808
- const inherited = await resolveInheritedWorkspaceScope(ctx, primary);
809
- return {
810
- topicId: primary._id,
811
- projectId: args.projectId,
812
- tenantId: inherited.tenantId,
813
- workspaceId: inherited.workspaceId,
814
- source: "project_mapped_topic"
815
- };
816
- }
817
- const nodeScope = await resolveTopicNodeScopeOrNull(
818
- ctx,
819
- String(args.projectId)
820
- );
821
- if (nodeScope) {
822
- return {
823
- ...nodeScope,
824
- projectId: nodeScope.projectId ?? String(args.projectId)
825
- };
826
- }
827
- throw new Error(
828
- `Legacy project scope ${String(args.projectId)} has no mapped topic.`
829
- );
830
- }
831
- throw new Error(
832
- "Missing scope: provide topicId (preferred) or legacy projectId alias."
833
- );
834
- }
835
- ({
836
- projectId: v.optional(v.string()),
837
- topicId: v.optional(v.string())
838
- });
839
- function normalizeScopeValue2(value) {
840
- if (typeof value !== "string") {
841
- return;
842
- }
843
- const normalized = value.trim();
844
- return normalized.length > 0 ? normalized : void 0;
845
- }
846
- function nodeMatchesWorkspaceReasoningScope(node, scope) {
847
- if (!node) {
848
- return false;
849
- }
850
- const scopeTenantId = normalizeScopeValue2(scope.tenantId);
851
- const scopeWorkspaceId = normalizeScopeValue2(scope.workspaceId);
852
- const nodeTenantId = normalizeScopeValue2(node.tenantId);
853
- const nodeWorkspaceId = normalizeScopeValue2(node.workspaceId);
854
- const epistemicLayer = typeof node.epistemicLayer === "string" ? node.epistemicLayer : void 0;
855
- if (scopeTenantId && nodeTenantId && scopeTenantId !== nodeTenantId) {
856
- return false;
857
- }
858
- if (epistemicLayer === "ontological" && nodeWorkspaceId === void 0) {
859
- return true;
860
- }
861
- if (!scopeWorkspaceId && node.publicationStatus === "published") {
862
- return true;
863
- }
864
- if (!scopeWorkspaceId) {
865
- return nodeWorkspaceId === void 0;
866
- }
867
- return scopeWorkspaceId === nodeWorkspaceId;
868
- }
869
- function edgeMatchesWorkspaceReasoningScope(edge, scope) {
870
- if (!edge) {
871
- return false;
872
- }
873
- const scopeTenantId = normalizeScopeValue2(scope.tenantId);
874
- const scopeWorkspaceId = normalizeScopeValue2(scope.workspaceId);
875
- const edgeTenantId = normalizeScopeValue2(edge.tenantId);
876
- const edgeWorkspaceId = normalizeScopeValue2(edge.workspaceId);
877
- if (scopeTenantId && edgeTenantId && scopeTenantId !== edgeTenantId) {
878
- return false;
879
- }
880
- if (!scopeWorkspaceId) {
881
- return edgeWorkspaceId === void 0;
882
- }
883
- return scopeWorkspaceId === edgeWorkspaceId;
884
- }
885
- async function resolveNodeScopeForWorkspaceIsolation(ctx, node) {
886
- const epistemicLayer = typeof node?.epistemicLayer === "string" ? node.epistemicLayer : void 0;
887
- const resolved = {
888
- tenantId: normalizeScopeValue2(node?.tenantId),
889
- workspaceId: normalizeScopeValue2(node?.workspaceId),
890
- epistemicLayer,
891
- nodeType: typeof node?.nodeType === "string" ? node.nodeType : void 0
892
- };
893
- if (!node) {
894
- return resolved;
895
- }
896
- if (resolved.epistemicLayer === "ontological") {
897
- return resolved;
898
- }
899
- if (resolved.tenantId || resolved.workspaceId) {
900
- return resolved;
901
- }
902
- if (node.topicId) {
903
- const topicScope = await resolveTopicProjectScope(ctx, {
904
- topicId: node.topicId
905
- });
906
- return {
907
- ...resolved,
908
- tenantId: topicScope.tenantId,
909
- workspaceId: topicScope.workspaceId
910
- };
911
- }
912
- if (node.projectId) {
913
- const topicScope = await resolveTopicProjectScope(ctx, {
914
- projectId: String(node.projectId)
915
- });
916
- return {
917
- ...resolved,
918
- tenantId: topicScope.tenantId,
919
- workspaceId: topicScope.workspaceId
920
- };
921
- }
922
- return resolved;
923
- }
924
-
925
- // src/epistemicBeliefs.helpers.ts
926
- v.id("epistemicNodes");
927
- var DEFAULT_CONFIDENCE_POLICY = {
928
- scoringMode: "after_worktree",
929
- tupleContradiction: normalizeTupleContradictionPolicy()
930
- };
931
- function throwStructuredMutationError(args) {
932
- const data = {
933
- structuredMutationError: true,
934
- message: args.message,
935
- status: args.status,
936
- code: args.code,
937
- invariantCode: args.invariantCode,
938
- suggestion: args.suggestion,
939
- details: args.details
940
- };
941
- const error = new ConvexError(
942
- data
943
- );
944
- error.status = args.status;
945
- error.code = args.code;
946
- error.invariantCode = args.invariantCode;
947
- error.suggestion = args.suggestion;
948
- error.details = args.details;
949
- throw error;
950
- }
951
- function buildBeliefConfidenceRow(args) {
952
- return {
953
- beliefId: args.beliefId,
954
- confidence: confidenceFromSL(
955
- args.belief,
956
- args.disbelief,
957
- args.uncertainty,
958
- args.baseRate
959
- ),
960
- belief: args.belief,
961
- disbelief: args.disbelief,
962
- uncertainty: args.uncertainty,
963
- baseRate: args.baseRate,
964
- slOperator: args.slOperator ?? "prior_seed",
965
- trigger: args.trigger,
966
- ...args.rationale ? { rationale: args.rationale } : {},
967
- assessedBy: args.assessedBy,
968
- assessedAt: args.assessedAt,
969
- ...args.triggeringEvidenceId ? {
970
- triggeringEvidenceId: args.triggeringEvidenceId,
971
- triggeringEvidenceIds: [String(args.triggeringEvidenceId)]
972
- } : {},
973
- ...args.triggeringContradictionId ? {
974
- triggeringContradictionId: args.triggeringContradictionId
975
- } : {},
976
- ...args.triggeringWorktreeId ? { triggeringWorktreeId: args.triggeringWorktreeId } : {}
977
- };
978
- }
979
- function readTupleContradictedFlag(value) {
980
- return typeof value === "boolean" ? value : void 0;
981
- }
982
- function readBeliefOpinionSnapshot(node, metadata) {
983
- try {
984
- return readOpinionFromRecord({
985
- ...metadata,
986
- opinion_b: node.opinion_b,
987
- opinion_d: node.opinion_d,
988
- opinion_u: node.opinion_u,
989
- opinion_a: node.opinion_a
990
- });
991
- } catch (error) {
992
- debugGraphPrimitiveFallback(
993
- "[epistemicBeliefs] Failed to read belief opinion snapshot",
994
- {
995
- error,
996
- beliefId: node._id
997
- }
998
- );
999
- return mkOpinion(0, 0, 1, 0.5);
1000
- }
1001
- }
1002
- function deriveTupleContradictionSeverity(node) {
1003
- const metadata = node.metadata || {};
1004
- const criticality = typeof metadata.criticality === "string" ? metadata.criticality : void 0;
1005
- if (criticality === "blocking") {
1006
- return "critical";
1007
- }
1008
- if (criticality === "supporting") {
1009
- return "minor";
1010
- }
1011
- return "significant";
1012
- }
1013
- function formatTupleContradictionDescription(args) {
1014
- return `Tuple-space contradiction detected: b=${args.opinion.b.toFixed(2)} > ${args.policy.beliefThreshold.toFixed(2)} and d=${args.opinion.d.toFixed(2)} > ${args.policy.disbeliefThreshold.toFixed(2)}.`;
1015
- }
1016
- function resolveBeliefStatus(node, metadata) {
1017
- return resolveBeliefLifecycleStatus({
1018
- beliefStatus: node.beliefStatus,
1019
- confidence: node.confidence,
1020
- predictionMeta: node.predictionMeta,
1021
- metadata
1022
- });
1023
- }
1024
- async function hasCompletedWorktreeForBelief(ctx, beliefNodeId) {
1025
- const clusterMembership = await ctx.db.query("worktreeBeliefCluster").withIndex("by_belief", (q) => q.eq("beliefId", beliefNodeId)).collect();
1026
- for (const membership of clusterMembership) {
1027
- const worktree = await ctx.db.get(membership.worktreeId);
1028
- if (worktree?.status === "completed" || worktree?.status === "merged") {
1029
- return true;
1030
- }
1031
- }
1032
- return false;
1033
- }
1034
- async function getActiveConfidencePolicy(ctx) {
1035
- try {
1036
- const activeConfig = await ctx.db.query("logicSprintScoring").withIndex("by_active", (q) => q.eq("isActive", true)).first();
1037
- return {
1038
- scoringMode: activeConfig?.confidencePolicy === "always" ? "always" : DEFAULT_CONFIDENCE_POLICY.scoringMode,
1039
- tupleContradiction: normalizeTupleContradictionPolicy(
1040
- activeConfig?.tupleContradictionPolicy
1041
- )
1042
- };
1043
- } catch (error) {
1044
- debugGraphPrimitiveFallback(
1045
- "[epistemicBeliefs] Failed to load active confidence policy",
1046
- {
1047
- error
1048
- }
1049
- );
1050
- return DEFAULT_CONFIDENCE_POLICY;
1051
- }
1052
- }
1053
- async function requireProjectWriteAccess(ctx, projectId, userId) {
1054
- const hasAccess = await checkProjectAccess(
1055
- ctx,
1056
- projectId,
1057
- userId
1058
- );
1059
- if (!hasAccess) {
1060
- throwStructuredMutationError({
1061
- message: `Project write access denied for topic ${projectId}.`,
1062
- status: 403,
1063
- code: "PROJECT_ACCESS_DENIED",
1064
- invariantCode: "policy.scope_required",
1065
- 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.",
1066
- details: { topicId: projectId, principalId: userId }
1067
- });
1068
- }
1069
- }
1070
-
1071
- // src/edges/contains.ts
1072
- var containsPropagationSpec = {
1073
- edgeType: "contains",
1074
- direction: "outgoing",
1075
- transitivity: "none",
1076
- damping: 1,
1077
- maxHops: 1,
1078
- operator: () => null,
1079
- description: "Structural containment only. Traversed for explicit semantics, but it never propagates opinions."
1080
- };
1081
- function readEdgeMetadata(edge) {
1082
- return {
1083
- constraint: edge.constraint ?? void 0,
1084
- normalization: edge.normalization ?? void 0,
1085
- propagation: edge.propagation ?? void 0,
1086
- conditionalA: edge.conditionalA ?? void 0,
1087
- conditionalNotA: edge.conditionalNotA ?? void 0
1088
- };
1089
- }
1090
- function applyPerHopDamping(sourceOpinion, damping) {
1091
- if (damping >= 1) {
1092
- return sourceOpinion;
1093
- }
1094
- return trustDiscount(sourceOpinion, Math.max(0, damping));
1095
- }
1096
- function annotateRationale(result, spec, hop) {
1097
- return {
1098
- ...result,
1099
- rationale: `hop=${hop} edge=${spec.edgeType} damping=${spec.damping.toFixed(
1100
- 2
1101
- )} :: ${result.rationale}`
1102
- };
631
+ function annotateRationale(result, spec, hop) {
632
+ return {
633
+ ...result,
634
+ rationale: `hop=${hop} edge=${spec.edgeType} damping=${spec.damping.toFixed(
635
+ 2
636
+ )} :: ${result.rationale}`
637
+ };
1103
638
  }
1104
639
  function propagatePositiveSupport(sourceOpinion, targetOpinion, edgeWeight) {
1105
640
  const discounted = trustDiscount(sourceOpinion, Math.abs(edgeWeight));
@@ -1203,7 +738,7 @@ var dependsOnPropagationSpec = {
1203
738
  description: "Structural gating. Textbook conditional deduction when edge conditionals exist, otherwise damped dependency cascade through downstream chains."
1204
739
  };
1205
740
 
1206
- // src/edges/derivedFrom.ts
741
+ // src/edges/derived-from.ts
1207
742
  var derivedFromPropagationSpec = {
1208
743
  edgeType: "derived_from",
1209
744
  direction: "incoming",
@@ -1256,7 +791,7 @@ var informsPropagationSpec = {
1256
791
  description: "Evidence-bearing influence. Informs can chain through the graph with light per-hop damping."
1257
792
  };
1258
793
 
1259
- // src/edges/propagationTypes.ts
794
+ // src/edges/propagation-types.ts
1260
795
  function isPropagationTraversalDirection(direction) {
1261
796
  return direction === "outgoing" || direction === "incoming";
1262
797
  }
@@ -1329,6 +864,9 @@ var testsPropagationSpec = {
1329
864
  };
1330
865
 
1331
866
  // src/edges/index.ts
867
+ var canContinueTransitively2 = canContinueTransitively;
868
+ var canTraverseHop2 = canTraverseHop;
869
+ var isPropagationTraversalDirection2 = isPropagationTraversalDirection;
1332
870
  var EDGE_PROPAGATION_SPECS = [
1333
871
  supportsPropagationSpec,
1334
872
  informsPropagationSpec,
@@ -1345,16 +883,15 @@ function getEdgePropagationSpecs() {
1345
883
  return EDGE_PROPAGATION_SPECS;
1346
884
  }
1347
885
  function getTraversalDirections(direction) {
1348
- if (isPropagationTraversalDirection(direction)) {
886
+ if (isPropagationTraversalDirection2(direction)) {
1349
887
  return [direction];
1350
888
  }
1351
889
  return ["outgoing", "incoming"];
1352
890
  }
1353
891
 
1354
892
  // src/confidencePropagationDispatch.ts
1355
- function resolveTraversalTargetNodeId(edge, direction) {
1356
- const targetNodeId = direction === "outgoing" ? edge.toNodeId : edge.fromNodeId;
1357
- return targetNodeId ?? void 0;
893
+ function nodeIdToCacheKey(nodeId) {
894
+ return String(nodeId);
1358
895
  }
1359
896
  function readNodeOpinion(node) {
1360
897
  const metadata = node.metadata ?? {};
@@ -1370,118 +907,354 @@ function readNodeOpinion(node) {
1370
907
  return mkOpinion(0, 0, 1, 0.5);
1371
908
  }
1372
909
  }
1373
- async function collectConfidencePropagationDispatches(args) {
1374
- const dispatchesByTargetId = /* @__PURE__ */ new Map();
1375
- const opinionCache = /* @__PURE__ */ new Map();
1376
- const nodeCache = /* @__PURE__ */ new Map();
1377
- const traversalSpecs = args.traversalSpecs ?? getEdgePropagationSpecs();
1378
- const queue = [
1379
- {
1380
- nodeId: args.sourceNodeId,
1381
- opinion: args.sourceOpinion,
1382
- hop: 0,
1383
- visitedNodeIds: /* @__PURE__ */ new Set([String(args.sourceNodeId)])
1384
- }
1385
- ];
1386
- const loadNode = async (nodeId) => {
1387
- const cacheKey = String(nodeId);
1388
- if (!nodeCache.has(cacheKey)) {
1389
- nodeCache.set(cacheKey, await args.getNode(nodeId));
1390
- }
1391
- return nodeCache.get(cacheKey) ?? null;
910
+ function resolveTraversalTargetNodeId(edge, direction) {
911
+ const targetNodeId = direction === "outgoing" ? edge.toNodeId : edge.fromNodeId;
912
+ return targetNodeId ?? void 0;
913
+ }
914
+ function buildInitialState(sourceNodeId, sourceOpinion) {
915
+ return {
916
+ nodeId: sourceNodeId,
917
+ opinion: sourceOpinion,
918
+ hop: 0,
919
+ visitedNodeIds: /* @__PURE__ */ new Set([nodeIdToCacheKey(sourceNodeId)])
1392
920
  };
1393
- while (queue.length > 0) {
1394
- const state = queue.shift();
1395
- if (!state) {
1396
- continue;
1397
- }
1398
- for (const spec of traversalSpecs) {
1399
- const nextHop = state.hop + 1;
1400
- if (!canTraverseHop(spec, nextHop)) {
1401
- continue;
1402
- }
1403
- for (const direction of getTraversalDirections(spec.direction)) {
1404
- const edges = await args.queryEdges({
1405
- nodeId: state.nodeId,
1406
- spec,
1407
- direction,
1408
- hop: nextHop
1409
- });
1410
- for (const edge of edges) {
1411
- if (args.sourceScope && !edgeMatchesWorkspaceReasoningScope(edge, args.sourceScope)) {
1412
- continue;
1413
- }
1414
- const targetNodeId = resolveTraversalTargetNodeId(edge, direction);
1415
- if (!targetNodeId) {
1416
- continue;
1417
- }
1418
- if (state.visitedNodeIds.has(String(targetNodeId))) {
1419
- continue;
1420
- }
1421
- const targetNode = await loadNode(targetNodeId);
1422
- if (!targetNode || targetNode.nodeType !== "belief") {
1423
- continue;
1424
- }
1425
- if (args.sourceScope && !nodeMatchesWorkspaceReasoningScope(targetNode, args.sourceScope)) {
1426
- continue;
1427
- }
1428
- const cacheKey = String(targetNodeId);
1429
- const targetOpinion = opinionCache.get(cacheKey) ?? readNodeOpinion(targetNode);
1430
- const result = spec.operator(state.opinion, targetOpinion, edge, {
1431
- hop: nextHop,
1432
- sourceNodeId: state.nodeId,
1433
- targetNodeId,
1434
- traversedDirection: direction,
1435
- spec
1436
- });
1437
- if (!result || !hasProjectedOpinionChanged(targetOpinion, result.opinion)) {
1438
- continue;
1439
- }
1440
- const projectedOpinion = mkOpinion(
1441
- result.opinion.b,
1442
- result.opinion.d,
1443
- result.opinion.u,
1444
- result.opinion.a
1445
- );
1446
- opinionCache.set(cacheKey, projectedOpinion);
1447
- const existingDispatch = dispatchesByTargetId.get(cacheKey);
1448
- dispatchesByTargetId.set(cacheKey, {
1449
- targetNodeId,
1450
- edgeType: spec.edgeType,
1451
- traversedDirection: direction,
1452
- weight: edge.weight ?? 1,
1453
- opinion: projectedOpinion,
1454
- operator: result.operator,
1455
- rationale: existingDispatch ? `${existingDispatch.rationale}; ${result.rationale}` : result.rationale,
1456
- hop: nextHop
1457
- });
1458
- if (canContinueTransitively(spec, nextHop)) {
1459
- queue.push({
1460
- nodeId: targetNodeId,
1461
- opinion: projectedOpinion,
1462
- hop: nextHop,
1463
- visitedNodeIds: /* @__PURE__ */ new Set([
1464
- ...state.visitedNodeIds,
1465
- String(targetNodeId)
1466
- ])
1467
- });
1468
- }
1469
- }
1470
- }
921
+ }
922
+ function extendVisited(currentVisited, targetNodeId) {
923
+ return /* @__PURE__ */ new Set([...currentVisited, nodeIdToCacheKey(targetNodeId)]);
924
+ }
925
+ function makeTransitiveState(current, targetNodeId, projectedOpinion, nextHop) {
926
+ return {
927
+ nodeId: targetNodeId,
928
+ opinion: projectedOpinion,
929
+ hop: nextHop,
930
+ visitedNodeIds: extendVisited(current.visitedNodeIds, targetNodeId)
931
+ };
932
+ }
933
+ function makeDispatchRecord(targetNodeId, spec, direction, edge, projectedOpinion, nextHop, result, existingDispatch) {
934
+ return {
935
+ targetNodeId,
936
+ edgeType: spec.edgeType,
937
+ traversedDirection: direction,
938
+ weight: edge.weight ?? 1,
939
+ opinion: projectedOpinion,
940
+ operator: result.operator,
941
+ rationale: existingDispatch ? `${existingDispatch.rationale}; ${result.rationale}` : result.rationale,
942
+ hop: nextHop
943
+ };
944
+ }
945
+ async function loadCachedNode(scope, nodeId) {
946
+ const cacheKey = nodeIdToCacheKey(nodeId);
947
+ if (!scope.nodeCache.has(cacheKey)) {
948
+ scope.nodeCache.set(cacheKey, await scope.args.getNode(nodeId));
949
+ }
950
+ return scope.nodeCache.get(cacheKey) ?? null;
951
+ }
952
+ async function collectTargetDispatch(state, nextHop, spec, direction, edge, queue, scope) {
953
+ const sourceScope = scope.args.sourceScope;
954
+ if (sourceScope && !edgeMatchesWorkspaceReasoningScope(edge, sourceScope)) {
955
+ return;
956
+ }
957
+ const targetNodeId = resolveTraversalTargetNodeId(edge, direction);
958
+ if (!targetNodeId) {
959
+ return;
960
+ }
961
+ const targetNodeIdKey = nodeIdToCacheKey(targetNodeId);
962
+ if (state.visitedNodeIds.has(targetNodeIdKey)) {
963
+ return;
964
+ }
965
+ const targetNode = await loadCachedNode(scope, targetNodeId);
966
+ if (targetNode?.nodeType !== "belief") {
967
+ return;
968
+ }
969
+ if (sourceScope && !nodeMatchesWorkspaceReasoningScope(targetNode, sourceScope)) {
970
+ return;
971
+ }
972
+ const targetOpinion = scope.opinionCache.get(targetNodeIdKey) ?? readNodeOpinion(targetNode);
973
+ const result = spec.operator(state.opinion, targetOpinion, edge, {
974
+ hop: nextHop,
975
+ sourceNodeId: state.nodeId,
976
+ targetNodeId,
977
+ traversedDirection: direction,
978
+ spec
979
+ });
980
+ if (!(result && hasProjectedOpinionChanged(targetOpinion, result.opinion))) {
981
+ return;
982
+ }
983
+ const projectedOpinion = mkOpinion(
984
+ result.opinion.b,
985
+ result.opinion.d,
986
+ result.opinion.u,
987
+ result.opinion.a
988
+ );
989
+ scope.opinionCache.set(targetNodeIdKey, projectedOpinion);
990
+ const existingDispatch = scope.dispatchesByTargetId.get(targetNodeIdKey);
991
+ scope.dispatchesByTargetId.set(
992
+ targetNodeIdKey,
993
+ makeDispatchRecord(
994
+ targetNodeId,
995
+ spec,
996
+ direction,
997
+ edge,
998
+ projectedOpinion,
999
+ nextHop,
1000
+ result,
1001
+ existingDispatch
1002
+ )
1003
+ );
1004
+ if (canContinueTransitively2(spec, nextHop)) {
1005
+ queue.push(
1006
+ makeTransitiveState(state, targetNodeId, projectedOpinion, nextHop)
1007
+ );
1008
+ }
1009
+ }
1010
+ async function processTraversalSpec(state, nextHop, spec, queue, scope) {
1011
+ const sourceNodeId = state.nodeId;
1012
+ for (const direction of getTraversalDirections(spec.direction)) {
1013
+ const edges = await scope.args.queryEdges({
1014
+ nodeId: sourceNodeId,
1015
+ spec,
1016
+ direction,
1017
+ hop: nextHop
1018
+ });
1019
+ for (const edge of edges) {
1020
+ await collectTargetDispatch(
1021
+ state,
1022
+ nextHop,
1023
+ spec,
1024
+ direction,
1025
+ edge,
1026
+ queue,
1027
+ scope
1028
+ );
1029
+ }
1030
+ }
1031
+ }
1032
+ async function processQueuedState(state, queue, scope) {
1033
+ const nextHop = state.hop + 1;
1034
+ for (const spec of scope.traversalSpecs) {
1035
+ if (!canTraverseHop2(spec, nextHop)) {
1036
+ continue;
1471
1037
  }
1038
+ await processTraversalSpec(state, nextHop, spec, queue, scope);
1472
1039
  }
1473
- return Array.from(dispatchesByTargetId.values()).sort((left, right) => {
1040
+ }
1041
+ function sortDispatches(dispatches) {
1042
+ return Array.from(dispatches).sort((left, right) => {
1474
1043
  if (left.hop !== right.hop) {
1475
1044
  return left.hop - right.hop;
1476
1045
  }
1477
1046
  return String(left.targetNodeId).localeCompare(String(right.targetNodeId));
1478
1047
  });
1479
1048
  }
1049
+ async function collectConfidencePropagationDispatches(args) {
1050
+ const dispatchesByTargetId = /* @__PURE__ */ new Map();
1051
+ const opinionCache = /* @__PURE__ */ new Map();
1052
+ const nodeCache = /* @__PURE__ */ new Map();
1053
+ const traversalSpecs = args.traversalSpecs ?? getEdgePropagationSpecs();
1054
+ const scope = {
1055
+ args,
1056
+ dispatchesByTargetId,
1057
+ opinionCache,
1058
+ nodeCache,
1059
+ traversalSpecs
1060
+ };
1061
+ const queue = [
1062
+ buildInitialState(args.sourceNodeId, args.sourceOpinion)
1063
+ ];
1064
+ while (queue.length > 0) {
1065
+ const state = queue.shift();
1066
+ if (!state) {
1067
+ continue;
1068
+ }
1069
+ await processQueuedState(state, queue, scope);
1070
+ }
1071
+ return sortDispatches(dispatchesByTargetId.values());
1072
+ }
1480
1073
 
1481
1074
  // src/epistemicBeliefs.confidence.ts
1075
+ function isRecord(value) {
1076
+ return Boolean(value) && typeof value === "object" && !Array.isArray(value);
1077
+ }
1078
+ function readConvexId(value) {
1079
+ return typeof value === "string" && value.trim().length > 0 ? value : void 0;
1080
+ }
1081
+ function readOptionalBoolean(value) {
1082
+ return typeof value === "boolean" ? value : void 0;
1083
+ }
1084
+ function readOptionalNumber(value) {
1085
+ return typeof value === "number" && Number.isFinite(value) ? value : void 0;
1086
+ }
1087
+ function readOptionalString(value) {
1088
+ return typeof value === "string" && value.trim().length > 0 ? value : void 0;
1089
+ }
1090
+ function readRecord(value) {
1091
+ return isRecord(value) ? value : void 0;
1092
+ }
1093
+ function readConfidenceBeliefNode(value) {
1094
+ if (!isRecord(value)) {
1095
+ return null;
1096
+ }
1097
+ const id = readConvexId(value._id);
1098
+ const nodeType = readOptionalString(value.nodeType);
1099
+ const projectId = readOptionalString(value.projectId);
1100
+ if (!(id && nodeType === "belief" && projectId)) {
1101
+ return null;
1102
+ }
1103
+ const node = {
1104
+ _id: id,
1105
+ nodeType,
1106
+ projectId
1107
+ };
1108
+ const confidence = readOptionalNumber(value.confidence);
1109
+ const epistemicLayer = readOptionalString(value.epistemicLayer);
1110
+ const globalId = readOptionalString(value.globalId);
1111
+ const metadata = readRecord(value.metadata);
1112
+ const opinionA = readOptionalNumber(value.opinion_a);
1113
+ const opinionB = readOptionalNumber(value.opinion_b);
1114
+ const opinionD = readOptionalNumber(value.opinion_d);
1115
+ const opinionU = readOptionalNumber(value.opinion_u);
1116
+ const predictionMeta = readRecord(value.predictionMeta);
1117
+ const publicationStatus = readOptionalString(value.publicationStatus);
1118
+ const status = readOptionalString(value.status);
1119
+ const tenantId = readOptionalString(value.tenantId);
1120
+ const topicId = readOptionalString(value.topicId);
1121
+ const tupleContradicted = readOptionalBoolean(value.tupleContradicted);
1122
+ const workspaceId = readOptionalString(value.workspaceId);
1123
+ if (confidence !== void 0) {
1124
+ node.confidence = confidence;
1125
+ }
1126
+ if (epistemicLayer !== void 0) {
1127
+ node.epistemicLayer = epistemicLayer;
1128
+ }
1129
+ if (globalId !== void 0) {
1130
+ node.globalId = globalId;
1131
+ }
1132
+ if (metadata !== void 0) {
1133
+ node.metadata = metadata;
1134
+ }
1135
+ if (opinionA !== void 0) {
1136
+ node.opinion_a = opinionA;
1137
+ }
1138
+ if (opinionB !== void 0) {
1139
+ node.opinion_b = opinionB;
1140
+ }
1141
+ if (opinionD !== void 0) {
1142
+ node.opinion_d = opinionD;
1143
+ }
1144
+ if (opinionU !== void 0) {
1145
+ node.opinion_u = opinionU;
1146
+ }
1147
+ if (predictionMeta !== void 0) {
1148
+ node.predictionMeta = predictionMeta;
1149
+ }
1150
+ if (publicationStatus !== void 0) {
1151
+ node.publicationStatus = publicationStatus;
1152
+ }
1153
+ if (status !== void 0) {
1154
+ node.status = status;
1155
+ }
1156
+ if (tenantId !== void 0) {
1157
+ node.tenantId = tenantId;
1158
+ }
1159
+ if (topicId !== void 0) {
1160
+ node.topicId = topicId;
1161
+ }
1162
+ if (tupleContradicted !== void 0) {
1163
+ node.tupleContradicted = tupleContradicted;
1164
+ }
1165
+ if (workspaceId !== void 0) {
1166
+ node.workspaceId = workspaceId;
1167
+ }
1168
+ return node;
1169
+ }
1170
+ function readPropagationEdge(value) {
1171
+ if (!isRecord(value)) {
1172
+ return null;
1173
+ }
1174
+ const edgeType = readOptionalString(value.edgeType);
1175
+ if (!edgeType) {
1176
+ return null;
1177
+ }
1178
+ const edge = { edgeType };
1179
+ const fromNodeId = readConvexId(value.fromNodeId);
1180
+ const tenantId = readOptionalString(value.tenantId);
1181
+ const toNodeId = readConvexId(value.toNodeId);
1182
+ const weight = readOptionalNumber(value.weight);
1183
+ const workspaceId = readOptionalString(value.workspaceId);
1184
+ if (fromNodeId !== void 0) {
1185
+ edge.fromNodeId = fromNodeId;
1186
+ }
1187
+ if (tenantId !== void 0) {
1188
+ edge.tenantId = tenantId;
1189
+ }
1190
+ if (toNodeId !== void 0) {
1191
+ edge.toNodeId = toNodeId;
1192
+ }
1193
+ if (weight !== void 0) {
1194
+ edge.weight = weight;
1195
+ }
1196
+ if (workspaceId !== void 0) {
1197
+ edge.workspaceId = workspaceId;
1198
+ }
1199
+ return edge;
1200
+ }
1201
+ function readRowList(values, reader) {
1202
+ return values.flatMap((value) => {
1203
+ const row = reader(value);
1204
+ return row ? [row] : [];
1205
+ });
1206
+ }
1482
1207
  async function applyBeliefConfidenceChange(ctx, args) {
1483
1208
  const now = Date.now();
1484
- const node = await ctx.db.get(args.nodeId);
1209
+ const node = await requireConfidenceBeliefNode(ctx, args);
1210
+ const state = await buildConfidenceChangeState(ctx, node, args);
1211
+ await assertConfidenceScoringPolicySatisfied(ctx, args, state);
1212
+ const tupleContradictionId = await createTupleContradictionIfNeeded(
1213
+ ctx,
1214
+ args,
1215
+ node,
1216
+ state
1217
+ );
1218
+ await patchBeliefConfidenceState(ctx, args, state);
1219
+ await scheduleFirstScoringThemeEdges(ctx, args, node, state);
1220
+ const beliefConfidenceId = await insertBeliefConfidenceRecord(
1221
+ ctx,
1222
+ args,
1223
+ state,
1224
+ tupleContradictionId,
1225
+ now
1226
+ );
1227
+ await ctx.scheduler.runAfter(0, internal.neo4jSync.syncNodeToNeo4j, {
1228
+ nodeId: args.nodeId,
1229
+ operation: "upsert"
1230
+ });
1231
+ await insertConfidenceAudit(
1232
+ ctx,
1233
+ args,
1234
+ node,
1235
+ state,
1236
+ tupleContradictionId,
1237
+ now
1238
+ );
1239
+ await insertTupleTransitionAuditIfNeeded(
1240
+ ctx,
1241
+ args,
1242
+ node,
1243
+ state,
1244
+ tupleContradictionId,
1245
+ now
1246
+ );
1247
+ await scheduleConfidenceFollowups(ctx, args, node, state);
1248
+ return {
1249
+ nodeId: args.nodeId,
1250
+ previousConfidence: state.previousConfidence,
1251
+ newConfidence: state.derivedConfidence,
1252
+ opinion: state.nextOpinion,
1253
+ beliefConfidenceId
1254
+ };
1255
+ }
1256
+ async function requireConfidenceBeliefNode(ctx, args) {
1257
+ const node = readConfidenceBeliefNode(await ctx.db.get(args.nodeId));
1485
1258
  if (!node) {
1486
1259
  throwStructuredMutationError({
1487
1260
  message: "Node not found.",
@@ -1492,59 +1265,28 @@ async function applyBeliefConfidenceChange(ctx, args) {
1492
1265
  details: { nodeId: args.nodeId }
1493
1266
  });
1494
1267
  }
1495
- if (node.nodeType !== "belief") {
1496
- throwStructuredMutationError({
1497
- 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.`,
1498
- status: 400,
1499
- code: "INVALID_ARGUMENT",
1500
- invariantCode: "entity.no_confidence",
1501
- suggestion: "Use entityLifecycle.updateEntityAttributes for entity mutations. appendSlScoring is for belief nodes only.",
1502
- details: { nodeId: args.nodeId, nodeType: node.nodeType }
1503
- });
1504
- }
1505
- if (!node.projectId) {
1506
- throwStructuredMutationError({
1507
- message: "Belief has no project scope.",
1508
- status: 400,
1509
- code: "MISSING_SCOPE",
1510
- invariantCode: "belief.project_required",
1511
- suggestion: "Belief must have a projectId before SL scoring can be appended.",
1512
- details: { nodeId: args.nodeId }
1513
- });
1514
- }
1515
- await requireProjectWriteAccess(
1516
- ctx,
1517
- node.projectId,
1518
- args.authenticatedUserId
1519
- );
1520
- const existingMetadata = node.metadata || {};
1268
+ await requireScopeWriteAccess(ctx, node.projectId, args.authenticatedUserId);
1269
+ return node;
1270
+ }
1271
+ async function buildConfidenceChangeState(ctx, node, args) {
1272
+ const existingMetadata = readNodeMetadata(node);
1521
1273
  const currentBeliefStatus = resolveBeliefStatus(node, existingMetadata);
1522
1274
  const confidencePolicy = await getActiveConfidencePolicy(ctx);
1523
- if (confidencePolicy.scoringMode === "after_worktree" && isPreValidationBeliefStatus(currentBeliefStatus)) {
1524
- const hasCompletedWorktree = await hasCompletedWorktreeForBelief(
1525
- ctx,
1526
- args.nodeId
1527
- );
1528
- if (!hasCompletedWorktree) {
1529
- throwStructuredMutationError({
1530
- message: "Cannot score belief before worktree completion. Complete a worktree that tests this belief first.",
1531
- status: 409,
1532
- code: "CONFLICT",
1533
- invariantCode: "belief.confidence_append_only",
1534
- suggestion: "Complete a worktree linked to this belief before recording SL scoring.",
1535
- details: { nodeId: args.nodeId }
1536
- });
1537
- }
1538
- }
1539
1275
  const previousConfidence = node.confidence || 0.5;
1540
1276
  const predictionMeta = node.predictionMeta || existingMetadata.predictionMeta;
1541
1277
  const previousOpinion = readBeliefOpinionSnapshot(node, existingMetadata);
1542
- const slB = args.belief;
1543
- const slD = args.disbelief;
1544
- const slU = args.uncertainty;
1545
- const slA = args.baseRate ?? 0.5;
1546
- const nextOpinion = { b: slB, d: slD, u: slU, a: slA };
1547
- const derivedConfidence = confidenceFromSL(slB, slD, slU, slA);
1278
+ const nextOpinion = {
1279
+ b: args.belief,
1280
+ d: args.disbelief,
1281
+ u: args.uncertainty,
1282
+ a: args.baseRate ?? 0.5
1283
+ };
1284
+ const derivedConfidence = confidenceFromSL(
1285
+ nextOpinion.b,
1286
+ nextOpinion.d,
1287
+ nextOpinion.u,
1288
+ nextOpinion.a
1289
+ );
1548
1290
  const isFirstScoring = typeof node.confidence !== "number" || !Number.isFinite(node.confidence);
1549
1291
  const previousTupleContradicted = readTupleContradictedFlag(node.tupleContradicted) ?? readTupleContradictedFlag(existingMetadata.tupleContradicted) ?? detectTupleContradiction(
1550
1292
  previousOpinion,
@@ -1565,79 +1307,121 @@ async function applyBeliefConfidenceChange(ctx, args) {
1565
1307
  predictionMeta,
1566
1308
  metadata: existingMetadata
1567
1309
  });
1568
- let tupleContradictionId;
1569
- if (tupleTransition.crossedIntoTupleContradiction) {
1570
- tupleContradictionId = await ctx.runMutation(
1571
- "contradictions:create",
1572
- {
1573
- projectId: node.projectId,
1574
- topicId: node.topicId,
1575
- beliefId: args.nodeId,
1576
- beliefBId: args.nodeId,
1577
- supportingInsightIds: [],
1578
- contradictingInsightIds: [],
1579
- severity: deriveTupleContradictionSeverity(node),
1580
- source: "tuple_space",
1581
- detectionMethod: "agent",
1582
- description: tupleContradictionDescription,
1583
- createdBy: args.authenticatedUserId
1584
- }
1585
- );
1310
+ const storedRationale = args.rationale ?? `Confidence changed from ${previousConfidence.toFixed(2)} (nodeId: ${args.nodeId})`;
1311
+ return {
1312
+ confidencePolicy,
1313
+ currentBeliefStatus,
1314
+ derivedConfidence,
1315
+ existingMetadata,
1316
+ isFirstScoring,
1317
+ newBeliefStatus,
1318
+ nextOpinion,
1319
+ previousConfidence,
1320
+ previousTupleContradicted,
1321
+ storedRationale,
1322
+ tupleContradictionDescription,
1323
+ tupleTransition
1324
+ };
1325
+ }
1326
+ function readNodeMetadata(node) {
1327
+ return node.metadata ?? {};
1328
+ }
1329
+ async function assertConfidenceScoringPolicySatisfied(ctx, args, state) {
1330
+ if (state.confidencePolicy.scoringMode !== "after_worktree" || !isPreValidationBeliefStatus(state.currentBeliefStatus)) {
1331
+ return;
1332
+ }
1333
+ const hasCompletedWorktree = await hasCompletedWorktreeForBelief(
1334
+ ctx,
1335
+ args.nodeId
1336
+ );
1337
+ if (hasCompletedWorktree) {
1338
+ return;
1586
1339
  }
1340
+ throwStructuredMutationError({
1341
+ message: "Cannot score belief before worktree completion. Complete a worktree that tests this belief first.",
1342
+ status: 409,
1343
+ code: "CONFLICT",
1344
+ invariantCode: "belief.confidence_append_only",
1345
+ suggestion: "Complete a worktree linked to this belief before recording SL scoring.",
1346
+ details: { nodeId: args.nodeId }
1347
+ });
1348
+ }
1349
+ async function createTupleContradictionIfNeeded(ctx, args, node, state) {
1350
+ if (!state.tupleTransition.crossedIntoTupleContradiction) {
1351
+ return;
1352
+ }
1353
+ return await ctx.runMutation("contradictions:create", {
1354
+ projectId: node.projectId,
1355
+ topicId: node.topicId,
1356
+ beliefId: args.nodeId,
1357
+ beliefBId: args.nodeId,
1358
+ supportingInsightIds: [],
1359
+ contradictingInsightIds: [],
1360
+ severity: deriveTupleContradictionSeverity(node),
1361
+ source: "tuple_space",
1362
+ detectionMethod: "agent",
1363
+ description: state.tupleContradictionDescription,
1364
+ createdBy: args.authenticatedUserId
1365
+ });
1366
+ }
1367
+ async function patchBeliefConfidenceState(ctx, args, state) {
1587
1368
  await ctx.db.patch(args.nodeId, {
1588
- confidence: derivedConfidence,
1589
- beliefStatus: newBeliefStatus,
1590
- tupleContradicted: tupleTransition.tupleContradicted,
1591
- updatedAt: now,
1592
- // Store SL opinion fields at node level for fast access
1593
- opinion_b: slB,
1594
- opinion_d: slD,
1595
- opinion_u: slU,
1596
- opinion_a: slA,
1369
+ confidence: state.derivedConfidence,
1370
+ beliefStatus: state.newBeliefStatus,
1371
+ tupleContradicted: state.tupleTransition.tupleContradicted,
1372
+ updatedAt: Date.now(),
1373
+ opinion_b: state.nextOpinion.b,
1374
+ opinion_d: state.nextOpinion.d,
1375
+ opinion_u: state.nextOpinion.u,
1376
+ opinion_a: state.nextOpinion.a,
1597
1377
  metadata: {
1598
- ...existingMetadata,
1599
- beliefStatus: newBeliefStatus,
1600
- slBelief: slB,
1601
- slDisbelief: slD,
1602
- slUncertainty: slU,
1603
- slBaseRate: slA,
1604
- tupleContradicted: tupleTransition.tupleContradicted
1378
+ ...state.existingMetadata,
1379
+ beliefStatus: state.newBeliefStatus,
1380
+ slBelief: state.nextOpinion.b,
1381
+ slDisbelief: state.nextOpinion.d,
1382
+ slUncertainty: state.nextOpinion.u,
1383
+ slBaseRate: state.nextOpinion.a,
1384
+ tupleContradicted: state.tupleTransition.tupleContradicted
1605
1385
  }
1606
1386
  });
1607
- if (isFirstScoring) {
1608
- const nodeTopicId = node.topicId;
1609
- const themeNodes = await ctx.db.query("epistemicNodes").withIndex(
1610
- "by_topic",
1611
- (q) => q.eq("topicId", nodeTopicId || node.projectId)
1612
- ).filter((q) => q.eq(q.field("nodeType"), "theme")).collect();
1613
- for (const theme of themeNodes) {
1614
- if (theme.globalId && node.globalId) {
1615
- await ctx.scheduler.runAfter(0, internal.neo4jEdgeAPI.createEdge, {
1616
- globalId: `edge-${node.globalId}-relates_to_thesis-${theme.globalId}`,
1617
- fromGlobalId: node.globalId,
1618
- toGlobalId: theme.globalId,
1619
- edgeType: "relates_to_thesis",
1620
- weight: derivedConfidence,
1621
- createdBy: args.authenticatedUserId,
1622
- topicId: String(node.projectId),
1623
- fromNodeType: "belief",
1624
- toNodeType: "theme",
1625
- fromLayer: "L3",
1626
- toLayer: "L3"
1627
- });
1628
- }
1387
+ }
1388
+ async function scheduleFirstScoringThemeEdges(ctx, args, node, state) {
1389
+ if (!state.isFirstScoring) {
1390
+ return;
1391
+ }
1392
+ const themeNodes = await ctx.db.query("epistemicNodes").withIndex(
1393
+ "by_topic",
1394
+ (q) => q.eq("topicId", node.topicId || node.projectId)
1395
+ ).filter((q) => q.eq(q.field("nodeType"), "theme")).collect();
1396
+ for (const theme of themeNodes) {
1397
+ if (!(theme.globalId && node.globalId)) {
1398
+ continue;
1629
1399
  }
1400
+ await ctx.scheduler.runAfter(0, internal.neo4jEdgeAPI.createEdge, {
1401
+ globalId: `edge-${node.globalId}-relates_to_thesis-${theme.globalId}`,
1402
+ fromGlobalId: node.globalId,
1403
+ toGlobalId: theme.globalId,
1404
+ edgeType: "relates_to_thesis",
1405
+ weight: state.derivedConfidence,
1406
+ createdBy: args.authenticatedUserId,
1407
+ topicId: String(node.projectId),
1408
+ fromNodeType: "belief",
1409
+ toNodeType: "theme",
1410
+ fromLayer: "L3",
1411
+ toLayer: "L3"
1412
+ });
1630
1413
  }
1631
- const storedRationale = args.rationale ?? `Confidence changed from ${previousConfidence.toFixed(2)} (nodeId: ${args.nodeId})`;
1632
- const beliefConfidenceId = await ctx.db.insert("beliefConfidence", {
1414
+ }
1415
+ async function insertBeliefConfidenceRecord(ctx, args, state, tupleContradictionId, now) {
1416
+ return await ctx.db.insert("beliefConfidence", {
1633
1417
  ...buildBeliefConfidenceRow({
1634
1418
  beliefId: args.nodeId,
1635
- belief: slB,
1636
- disbelief: slD,
1637
- uncertainty: slU,
1638
- baseRate: slA,
1419
+ belief: state.nextOpinion.b,
1420
+ disbelief: state.nextOpinion.d,
1421
+ uncertainty: state.nextOpinion.u,
1422
+ baseRate: state.nextOpinion.a,
1639
1423
  trigger: args.trigger,
1640
- rationale: storedRationale,
1424
+ rationale: state.storedRationale,
1641
1425
  assessedBy: args.authenticatedUserId,
1642
1426
  assessedAt: now,
1643
1427
  slOperator: args.slOperator,
@@ -1646,25 +1430,23 @@ async function applyBeliefConfidenceChange(ctx, args) {
1646
1430
  triggeringWorktreeId: args.triggeringWorktreeId
1647
1431
  })
1648
1432
  });
1649
- await ctx.scheduler.runAfter(0, internal.neo4jSync.syncNodeToNeo4j, {
1650
- nodeId: args.nodeId,
1651
- operation: "upsert"
1652
- });
1433
+ }
1434
+ async function insertConfidenceAudit(ctx, args, node, state, tupleContradictionId, now) {
1653
1435
  await ctx.db.insert("epistemicAudit", {
1654
1436
  entityType: "belief",
1655
1437
  entityId: args.nodeId,
1656
1438
  changeType: "confidence_changed",
1657
1439
  previousState: {
1658
- confidence: previousConfidence,
1659
- tupleContradicted: previousTupleContradicted
1440
+ confidence: state.previousConfidence,
1441
+ tupleContradicted: state.previousTupleContradicted
1660
1442
  },
1661
1443
  newState: {
1662
- opinion: nextOpinion,
1663
- confidence: derivedConfidence,
1444
+ opinion: state.nextOpinion,
1445
+ confidence: state.derivedConfidence,
1664
1446
  trigger: args.trigger,
1665
- rationale: storedRationale,
1666
- tupleContradicted: tupleTransition.tupleContradicted,
1667
- tupleContradictionPolicy: tupleTransition.policy,
1447
+ rationale: state.storedRationale,
1448
+ tupleContradicted: state.tupleTransition.tupleContradicted,
1449
+ tupleContradictionPolicy: state.tupleTransition.policy,
1668
1450
  ...tupleContradictionId ? { tupleContradictionId: String(tupleContradictionId) } : {}
1669
1451
  },
1670
1452
  changedBy: args.authenticatedUserId,
@@ -1673,28 +1455,39 @@ async function applyBeliefConfidenceChange(ctx, args) {
1673
1455
  projectId: node.projectId,
1674
1456
  topicId: node.topicId
1675
1457
  });
1676
- if (tupleTransition.crossedIntoTupleContradiction || tupleTransition.crossedOutOfTupleContradiction) {
1677
- await ctx.db.insert("epistemicAudit", {
1678
- entityType: "belief",
1679
- entityId: args.nodeId,
1680
- changeType: "updated",
1681
- previousState: { tupleContradicted: previousTupleContradicted },
1682
- newState: {
1683
- tupleContradicted: tupleTransition.tupleContradicted,
1684
- action: tupleTransition.crossedIntoTupleContradiction ? "tuple_contradiction_detected" : "tuple_contradiction_cleared",
1685
- opinion: nextOpinion,
1686
- tupleContradictionPolicy: tupleTransition.policy,
1687
- ...tupleContradictionId ? { tupleContradictionId: String(tupleContradictionId) } : {}
1688
- },
1689
- 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.`,
1690
- changedBy: args.authenticatedUserId,
1691
- isAgent: false,
1692
- changedAt: now,
1693
- projectId: node.projectId,
1694
- topicId: node.topicId
1695
- });
1458
+ }
1459
+ async function insertTupleTransitionAuditIfNeeded(ctx, args, node, state, tupleContradictionId, now) {
1460
+ if (!(state.tupleTransition.crossedIntoTupleContradiction || state.tupleTransition.crossedOutOfTupleContradiction)) {
1461
+ return;
1462
+ }
1463
+ await ctx.db.insert("epistemicAudit", {
1464
+ entityType: "belief",
1465
+ entityId: args.nodeId,
1466
+ changeType: "updated",
1467
+ previousState: { tupleContradicted: state.previousTupleContradicted },
1468
+ newState: {
1469
+ tupleContradicted: state.tupleTransition.tupleContradicted,
1470
+ action: state.tupleTransition.crossedIntoTupleContradiction ? "tuple_contradiction_detected" : "tuple_contradiction_cleared",
1471
+ opinion: state.nextOpinion,
1472
+ tupleContradictionPolicy: state.tupleTransition.policy,
1473
+ ...tupleContradictionId ? { tupleContradictionId: String(tupleContradictionId) } : {}
1474
+ },
1475
+ rationale: tupleAuditRationale(state),
1476
+ changedBy: args.authenticatedUserId,
1477
+ isAgent: false,
1478
+ changedAt: now,
1479
+ projectId: node.projectId,
1480
+ topicId: node.topicId
1481
+ });
1482
+ }
1483
+ function tupleAuditRationale(state) {
1484
+ if (state.tupleTransition.crossedIntoTupleContradiction) {
1485
+ return state.tupleContradictionDescription;
1696
1486
  }
1697
- if (Math.abs(derivedConfidence - previousConfidence) >= 0.15) {
1487
+ return `Tuple-space contradiction cleared: b=${state.nextOpinion.b.toFixed(2)}, d=${state.nextOpinion.d.toFixed(2)} no longer exceed the configured policy thresholds.`;
1488
+ }
1489
+ async function scheduleConfidenceFollowups(ctx, args, node, state) {
1490
+ if (Math.abs(state.derivedConfidence - state.previousConfidence) >= 0.15) {
1698
1491
  await ctx.scheduler.runAfter(
1699
1492
  5e3,
1700
1493
  internal.bi.contradictionSemanticDetector.scanAffectedBeliefs,
@@ -1711,13 +1504,6 @@ async function applyBeliefConfidenceChange(ctx, args) {
1711
1504
  { nodeId: args.nodeId }
1712
1505
  );
1713
1506
  }
1714
- return {
1715
- nodeId: args.nodeId,
1716
- previousConfidence,
1717
- newConfidence: derivedConfidence,
1718
- opinion: { b: slB, d: slD, u: slU, a: slA },
1719
- beliefConfidenceId
1720
- };
1721
1507
  }
1722
1508
  function propagationPressureLabel(edgeType, weight) {
1723
1509
  if (edgeType === "contradicts" || edgeType === "refutes") {
@@ -1745,7 +1531,7 @@ internalMutation({
1745
1531
  args.opinion_u,
1746
1532
  args.opinion_a
1747
1533
  );
1748
- const sourceNode = await ctx.db.get(args.nodeId);
1534
+ const sourceNode = readConfidenceBeliefNode(await ctx.db.get(args.nodeId));
1749
1535
  const sourceScope = await resolveNodeScopeForWorkspaceIsolation(
1750
1536
  ctx,
1751
1537
  sourceNode
@@ -1754,16 +1540,20 @@ internalMutation({
1754
1540
  sourceNodeId: args.nodeId,
1755
1541
  sourceOpinion,
1756
1542
  sourceScope,
1757
- queryEdges: async ({ nodeId, spec, direction }) => {
1758
- return await ctx.db.query("epistemicEdges").withIndex(
1543
+ queryEdges: async ({ nodeId, spec, direction }) => readRowList(
1544
+ await ctx.db.query("epistemicEdges").withIndex(
1759
1545
  direction === "outgoing" ? "by_from_type" : "by_to_type",
1760
1546
  (q) => direction === "outgoing" ? q.eq("fromNodeId", nodeId).eq("edgeType", spec.edgeType) : q.eq("toNodeId", nodeId).eq("edgeType", spec.edgeType)
1761
- ).collect();
1762
- },
1763
- getNode: async (nodeId) => await ctx.db.get(nodeId)
1547
+ ).collect(),
1548
+ readPropagationEdge
1549
+ ),
1550
+ getNode: async (nodeId) => readConfidenceBeliefNode(await ctx.db.get(nodeId))
1764
1551
  });
1765
1552
  for (const dispatch of dispatches) {
1766
- const pressureLabel = propagationPressureLabel(dispatch.edgeType, dispatch.weight);
1553
+ const pressureLabel = propagationPressureLabel(
1554
+ dispatch.edgeType,
1555
+ dispatch.weight
1556
+ );
1767
1557
  await applyBeliefConfidenceChange(ctx, {
1768
1558
  nodeId: dispatch.targetNodeId,
1769
1559
  belief: dispatch.opinion.b,
@@ -1787,253 +1577,645 @@ internalMutation({
1787
1577
  }
1788
1578
  });
1789
1579
 
1790
- // src/epistemicContracts.evaluators.ts
1791
- var evaluatorRegistry = /* @__PURE__ */ new Map();
1792
- var BUILT_IN_EVIDENTIAL_ALIASES2 = METRIC_COMPARATOR_EVALUATOR_NAMES.evidentialAliases;
1793
- var BUILT_IN_METRIC_CHECKER2 = METRIC_COMPARATOR_EVALUATOR_NAMES.metricChecker;
1794
- var BUILT_IN_REFERENCE_CHECK_COUNTER2 = METRIC_COMPARATOR_EVALUATOR_NAMES.referenceCheckCounter;
1795
- var BUILT_IN_TEMPORAL_DEADLINE2 = METRIC_COMPARATOR_EVALUATOR_NAMES.temporalDeadline;
1796
- var BUILT_IN_MARKET_INDEX_COMPARATOR2 = METRIC_COMPARATOR_EVALUATOR_NAMES.marketIndexComparator;
1797
- var MAX_CONTRACT_EVALUATION_BATCH_SIZE = 50;
1798
- function ensureBuiltInEvaluators() {
1799
- for (const name of BUILT_IN_EVIDENTIAL_ALIASES2) {
1800
- if (evaluatorRegistry.has(name)) {
1801
- continue;
1802
- }
1803
- evaluatorRegistry.set(name, {
1804
- name,
1805
- evaluate: evaluateBuiltInEvidentialContract
1806
- });
1580
+ // src/epistemicContracts.metrics.ts
1581
+ var ACTIVE_CONTRADICTION_STATUSES = /* @__PURE__ */ new Set([
1582
+ "unresolved",
1583
+ "investigating",
1584
+ "accepted_as_permanent"
1585
+ ]);
1586
+ var DEPENDENT_EDGE_TYPES = /* @__PURE__ */ new Set(["depends_on"]);
1587
+ function classifyContradictionStatus(status) {
1588
+ if (typeof status !== "string") {
1589
+ return "active";
1807
1590
  }
1808
- for (const evaluator of ENGINEERING_EPISTEMIC_EVALUATORS) {
1809
- if (evaluatorRegistry.has(evaluator.name)) {
1810
- continue;
1811
- }
1812
- evaluatorRegistry.set(evaluator.name, evaluator);
1591
+ if (ACTIVE_CONTRADICTION_STATUSES.has(status)) {
1592
+ return "active";
1813
1593
  }
1814
- const researchEvaluators = [
1815
- [BUILT_IN_METRIC_CHECKER2, evaluateMetricCheckerContract],
1816
- [BUILT_IN_REFERENCE_CHECK_COUNTER2, evaluateReferenceCheckCounterContract],
1817
- [BUILT_IN_TEMPORAL_DEADLINE2, evaluateTemporalDeadlineContract],
1818
- [BUILT_IN_MARKET_INDEX_COMPARATOR2, evaluateMarketIndexComparatorContract]
1819
- ];
1820
- for (const [name, evaluate] of researchEvaluators) {
1821
- if (evaluatorRegistry.has(name)) {
1822
- continue;
1823
- }
1824
- evaluatorRegistry.set(name, { name, evaluate });
1594
+ if (status === "resolved_support" || status === "resolved_contra" || status === "belief_forked") {
1595
+ return "resolved";
1825
1596
  }
1597
+ return "resolved";
1826
1598
  }
1827
- function normalizeTrigger(trigger) {
1828
- if (trigger === "evidence_added") {
1829
- return "evidence_added";
1599
+ function isRecord2(value) {
1600
+ return Boolean(value) && typeof value === "object" && !Array.isArray(value);
1601
+ }
1602
+ function readOptionalNumber2(value) {
1603
+ return typeof value === "number" && Number.isFinite(value) ? value : void 0;
1604
+ }
1605
+ function readOptionalString2(value) {
1606
+ return typeof value === "string" && value.trim().length > 0 ? value : void 0;
1607
+ }
1608
+ function readConvexId2(value) {
1609
+ const normalized = readOptionalString2(value);
1610
+ return normalized;
1611
+ }
1612
+ function readMetricNodeDoc(value) {
1613
+ if (!isRecord2(value)) {
1614
+ return null;
1830
1615
  }
1831
- if (trigger === "periodic") {
1832
- return "periodic";
1616
+ const id = readConvexId2(value._id);
1617
+ const nodeType = readOptionalString2(value.nodeType);
1618
+ if (!(id && nodeType)) {
1619
+ return null;
1833
1620
  }
1834
- if (trigger === "manual") {
1835
- return "manual";
1621
+ const node = { _id: id, nodeType };
1622
+ const globalId = readOptionalString2(value.globalId);
1623
+ if (globalId !== void 0) {
1624
+ node.globalId = globalId;
1836
1625
  }
1837
- return "event_driven";
1838
- }
1839
- function resolveSchedulesForTrigger(trigger) {
1840
- if (trigger === "evidence_added") {
1841
- return /* @__PURE__ */ new Set(["on_evidence", "event_driven"]);
1626
+ if ("metadata" in value) {
1627
+ node.metadata = value.metadata;
1842
1628
  }
1843
- if (trigger === "periodic") {
1844
- return /* @__PURE__ */ new Set(["periodic"]);
1629
+ const status = readOptionalString2(value.status);
1630
+ if (status !== void 0) {
1631
+ node.status = status;
1845
1632
  }
1846
- if (trigger === "manual") {
1847
- return /* @__PURE__ */ new Set(["on_demand", "event_driven"]);
1633
+ return node;
1634
+ }
1635
+ function readIncomingEdgeRow(value) {
1636
+ if (!isRecord2(value)) {
1637
+ return null;
1848
1638
  }
1849
- return /* @__PURE__ */ new Set(["event_driven"]);
1639
+ const fromNodeId = readOptionalString2(value.fromNodeId);
1640
+ if (!fromNodeId) {
1641
+ return null;
1642
+ }
1643
+ const edge = { fromNodeId };
1644
+ const id = readConvexId2(value._id);
1645
+ if (id !== void 0) {
1646
+ edge._id = id;
1647
+ }
1648
+ const edgeType = readOptionalString2(value.edgeType);
1649
+ if (edgeType !== void 0) {
1650
+ edge.edgeType = edgeType;
1651
+ }
1652
+ const fromGlobalId = readOptionalString2(value.fromGlobalId);
1653
+ if (fromGlobalId !== void 0) {
1654
+ edge.fromGlobalId = fromGlobalId;
1655
+ }
1656
+ const fromUuid = readOptionalString2(value.fromUuid);
1657
+ if (fromUuid !== void 0) {
1658
+ edge.fromUuid = fromUuid;
1659
+ }
1660
+ const sourceGlobalId = readOptionalString2(value.sourceGlobalId);
1661
+ if (sourceGlobalId !== void 0) {
1662
+ edge.sourceGlobalId = sourceGlobalId;
1663
+ }
1664
+ const targetGlobalId = readOptionalString2(value.targetGlobalId);
1665
+ if (targetGlobalId !== void 0) {
1666
+ edge.targetGlobalId = targetGlobalId;
1667
+ }
1668
+ const toGlobalId = readOptionalString2(value.toGlobalId);
1669
+ if (toGlobalId !== void 0) {
1670
+ edge.toGlobalId = toGlobalId;
1671
+ }
1672
+ const toNodeId = readOptionalString2(value.toNodeId);
1673
+ if (toNodeId !== void 0) {
1674
+ edge.toNodeId = toNodeId;
1675
+ }
1676
+ const toUuid = readOptionalString2(value.toUuid);
1677
+ if (toUuid !== void 0) {
1678
+ edge.toUuid = toUuid;
1679
+ }
1680
+ const weight = readOptionalNumber2(value.weight);
1681
+ if (weight !== void 0) {
1682
+ edge.weight = weight;
1683
+ }
1684
+ for (const timestampField of ["_creationTime", "createdAt", "updatedAt"]) {
1685
+ const timestamp = readOptionalNumber2(value[timestampField]);
1686
+ if (timestamp !== void 0) {
1687
+ edge[timestampField] = timestamp;
1688
+ }
1689
+ }
1690
+ return edge;
1850
1691
  }
1851
- async function executeContractEvaluation(args) {
1852
- ensureBuiltInEvaluators();
1853
- const evaluator = evaluatorRegistry.get(args.contract.condition.evaluator);
1854
- let evaluation;
1855
- if (evaluator) {
1692
+ function getEdgeTimestamp(edge) {
1693
+ if (typeof edge.updatedAt === "number") {
1694
+ return edge.updatedAt;
1695
+ }
1696
+ if (typeof edge.createdAt === "number") {
1697
+ return edge.createdAt;
1698
+ }
1699
+ if (typeof edge._creationTime === "number") {
1700
+ return edge._creationTime;
1701
+ }
1702
+ return null;
1703
+ }
1704
+ async function collectNodeEndpointRefs(ctx, nodeId) {
1705
+ const refs = /* @__PURE__ */ new Set([String(nodeId)]);
1706
+ const node = readMetricNodeDoc(await ctx.db.get(nodeId));
1707
+ if (node?.globalId) {
1708
+ refs.add(node.globalId);
1709
+ }
1710
+ return [...refs];
1711
+ }
1712
+ async function collectIncomingEdgeRows(ctx, nodeId, edgeType) {
1713
+ const refs = await collectNodeEndpointRefs(ctx, nodeId);
1714
+ const seen = /* @__PURE__ */ new Set();
1715
+ const edges = [];
1716
+ for (const ref of refs) {
1717
+ const rows = edgeType === void 0 ? await ctx.db.query("epistemicEdges").withIndex("by_to", (q) => q.eq("toNodeId", ref)).collect() : await ctx.db.query("epistemicEdges").withIndex(
1718
+ "by_to_type",
1719
+ (q) => q.eq("toNodeId", ref).eq("edgeType", edgeType)
1720
+ ).collect();
1721
+ for (const row of rows) {
1722
+ const edge = readIncomingEdgeRow(row);
1723
+ if (!edge) {
1724
+ continue;
1725
+ }
1726
+ if (edgeType !== void 0 && edge.edgeType !== edgeType) {
1727
+ continue;
1728
+ }
1729
+ const key = edge._id === void 0 ? `${edge.fromNodeId}->${edge.toNodeId ?? ref}:${edge.edgeType ?? ""}` : String(edge._id);
1730
+ if (seen.has(key)) {
1731
+ continue;
1732
+ }
1733
+ seen.add(key);
1734
+ edges.push(edge);
1735
+ }
1736
+ }
1737
+ return edges;
1738
+ }
1739
+ function sourceEndpointRefs(edge) {
1740
+ return [
1741
+ edge.fromNodeId,
1742
+ edge.sourceGlobalId,
1743
+ edge.fromGlobalId,
1744
+ edge.fromUuid
1745
+ ].filter((value) => value !== void 0);
1746
+ }
1747
+ async function resolveEndpointNode(ctx, refs) {
1748
+ const candidates = refs.map((value) => value.trim()).filter(
1749
+ (value, index, values) => value.length > 0 && values.indexOf(value) === index
1750
+ );
1751
+ for (const candidate of candidates) {
1856
1752
  try {
1857
- evaluation = await evaluator.evaluate({
1858
- belief: args.belief,
1859
- contract: args.contract,
1860
- ctx: args.ctx,
1861
- now: args.now,
1862
- resultData: args.resultData,
1863
- trigger: args.trigger,
1864
- inputData: args.inputData
1865
- });
1866
- } catch (error) {
1867
- evaluation = {
1868
- result: "error",
1869
- rationale: error instanceof Error ? error.message : "Unknown evaluator error."
1870
- };
1753
+ const direct = readMetricNodeDoc(
1754
+ await ctx.db.get(candidate)
1755
+ );
1756
+ if (direct) {
1757
+ return direct;
1758
+ }
1759
+ } catch {
1760
+ }
1761
+ const byGlobalId = readMetricNodeDoc(
1762
+ await ctx.db.query("epistemicNodes").withIndex("by_globalId", (q) => q.eq("globalId", candidate)).first()
1763
+ );
1764
+ if (byGlobalId) {
1765
+ return byGlobalId;
1871
1766
  }
1872
- } else {
1873
- evaluation = {
1874
- result: "error",
1875
- rationale: `No epistemic evaluator registered for "${args.contract.condition.evaluator}".`
1876
- };
1877
1767
  }
1878
- const confidenceBefore = typeof args.currentConfidence === "number" ? args.currentConfidence : typeof args.belief.confidence === "number" ? args.belief.confidence : 0.5;
1879
- const modulationPlan = deriveContractModulationPlan({
1880
- currentConfidence: confidenceBefore,
1881
- modulation: args.contract.modulation,
1882
- result: evaluation.result,
1883
- resultConfidence: evaluation.confidence
1884
- });
1885
- let beliefConfidenceId;
1886
- let confidenceAfter = confidenceBefore;
1887
- if (modulationPlan) {
1888
- const contractB = Math.max(0, Math.min(1, modulationPlan.confidenceAfter));
1889
- const modulationResult = await applyBeliefConfidenceChange(args.ctx, {
1890
- nodeId: args.contract.beliefNodeId,
1891
- belief: contractB,
1892
- disbelief: 1 - contractB,
1893
- uncertainty: 0,
1894
- baseRate: 0.5,
1895
- trigger: modulationPlan.trigger,
1896
- rationale: `Epistemic contract "${args.contract.title}" ${evaluation.result}: ${evaluation.rationale}`,
1897
- authenticatedUserId: args.authenticatedUserId
1898
- });
1899
- beliefConfidenceId = modulationResult.beliefConfidenceId;
1900
- confidenceAfter = typeof modulationResult.newConfidence === "number" ? modulationResult.newConfidence : modulationPlan.confidenceAfter;
1768
+ return null;
1769
+ }
1770
+ async function getEvidenceLinks(ctx, beliefNodeId) {
1771
+ const edges = await collectIncomingEdgeRows(ctx, beliefNodeId, "informs");
1772
+ if (edges.length === 0) {
1773
+ return [];
1901
1774
  }
1902
- const evaluationId = await args.ctx.db.insert("contractEvaluations", {
1903
- contractId: args.contract.contractId,
1904
- beliefNodeId: args.contract.beliefNodeId,
1905
- result: evaluation.result,
1906
- evaluatedAt: args.now,
1907
- evaluator: args.contract.condition.evaluator,
1908
- trigger: args.trigger,
1909
- resultData: evaluation.data,
1910
- modulationApplied: Boolean(modulationPlan),
1911
- confidenceDelta: modulationPlan?.confidenceDelta,
1912
- confidenceBefore: modulationPlan?.confidenceBefore,
1913
- confidenceAfter: modulationPlan ? confidenceAfter : void 0,
1914
- beliefConfidenceId,
1915
- modulationRationale: evaluation.rationale,
1916
- topicId: args.contract.topicId
1917
- });
1918
- const nextStatus = deriveContractStatus(evaluation.result, args.contract.status);
1919
- await args.ctx.db.patch(
1920
- args.contract._id,
1921
- {
1922
- status: nextStatus,
1923
- lastEvaluatedAt: args.now,
1924
- evaluationCount: (args.contract.evaluationCount ?? 0) + 1,
1925
- updatedAt: args.now
1775
+ const links = [];
1776
+ for (const edge of edges) {
1777
+ const node = await resolveEndpointNode(ctx, sourceEndpointRefs(edge));
1778
+ if (node?.nodeType !== "evidence" || node.status === "archived") {
1779
+ continue;
1926
1780
  }
1781
+ links.push({ edge, node });
1782
+ }
1783
+ return links;
1784
+ }
1785
+ function getEvidenceTags(node) {
1786
+ const metadata = node.metadata && typeof node.metadata === "object" ? node.metadata : null;
1787
+ const tags = metadata?.tags;
1788
+ if (!Array.isArray(tags)) {
1789
+ return [];
1790
+ }
1791
+ return tags.filter((tag) => typeof tag === "string");
1792
+ }
1793
+ async function computeEvidenceCountMetric(ctx, beliefNodeId) {
1794
+ return (await getEvidenceLinks(ctx, beliefNodeId)).length;
1795
+ }
1796
+ async function computeTaggedEvidenceCount(args) {
1797
+ const expectedTag = args.caseSensitive ? args.tag : args.tag.toLowerCase();
1798
+ const matchedEvidenceIds = (await getEvidenceLinks(args.ctx, args.beliefNodeId)).filter(
1799
+ ({ node }) => getEvidenceTags(node).some(
1800
+ (tag) => (args.caseSensitive ? tag : tag.toLowerCase()) === expectedTag
1801
+ )
1802
+ ).map(({ node }) => String(node._id));
1803
+ return {
1804
+ count: matchedEvidenceIds.length,
1805
+ matchedEvidenceIds
1806
+ };
1807
+ }
1808
+ async function computeContradictionCounts(ctx, beliefNodeId) {
1809
+ const contradictionDb = ctx.db;
1810
+ const contradictions = await contradictionDb.query("contradictions").withIndex("by_beliefId", (q) => q.eq("beliefId", beliefNodeId)).collect();
1811
+ return contradictions.reduce(
1812
+ (counts, contradiction) => {
1813
+ const status = contradiction.resolutionStatus ?? contradiction.status ?? "unresolved";
1814
+ if (classifyContradictionStatus(status) === "active") {
1815
+ counts.activeCount += 1;
1816
+ } else {
1817
+ counts.resolvedCount += 1;
1818
+ }
1819
+ return counts;
1820
+ },
1821
+ { activeCount: 0, resolvedCount: 0 }
1927
1822
  );
1823
+ }
1824
+ async function computeEvidenceFreshness(ctx, beliefNodeId, now = Date.now()) {
1825
+ const timestamps = (await getEvidenceLinks(ctx, beliefNodeId)).map(({ edge }) => getEdgeTimestamp(edge)).filter((value) => value !== null);
1826
+ if (timestamps.length === 0) {
1827
+ return {
1828
+ newestAgeMs: null,
1829
+ oldestAgeMs: null,
1830
+ newestEdgeAt: null,
1831
+ oldestEdgeAt: null,
1832
+ edgeCount: 0
1833
+ };
1834
+ }
1835
+ const newestEdgeAt = Math.max(...timestamps);
1836
+ const oldestEdgeAt = Math.min(...timestamps);
1928
1837
  return {
1929
- evaluationId,
1930
- result: evaluation.result,
1931
- status: nextStatus,
1932
- confidenceBefore,
1933
- confidenceAfter,
1934
- rationale: evaluation.rationale,
1935
- data: evaluation.data,
1936
- currentConfidence: confidenceAfter
1838
+ newestAgeMs: Math.max(0, now - newestEdgeAt),
1839
+ oldestAgeMs: Math.max(0, now - oldestEdgeAt),
1840
+ newestEdgeAt,
1841
+ oldestEdgeAt,
1842
+ edgeCount: timestamps.length
1937
1843
  };
1938
1844
  }
1939
- async function evaluateContractsForTriggerBatch(args) {
1940
- const startedAt = Date.now();
1941
- const contracts = await loadContractsForTrigger({
1845
+ async function computeDependentBeliefCount(ctx, beliefNodeId) {
1846
+ const incomingEdges = await collectIncomingEdgeRows(ctx, beliefNodeId);
1847
+ const dependencyEdges = incomingEdges.filter(
1848
+ (edge) => edge.edgeType ? DEPENDENT_EDGE_TYPES.has(edge.edgeType) : false
1849
+ );
1850
+ if (dependencyEdges.length === 0) {
1851
+ return 0;
1852
+ }
1853
+ const uniqueBeliefIds = /* @__PURE__ */ new Set();
1854
+ for (const edge of dependencyEdges) {
1855
+ const node = await resolveEndpointNode(ctx, sourceEndpointRefs(edge));
1856
+ if (node && node.nodeType === "belief" && node.status !== "archived" && node.status !== "deleted") {
1857
+ uniqueBeliefIds.add(String(node._id));
1858
+ }
1859
+ }
1860
+ return uniqueBeliefIds.size;
1861
+ }
1862
+ async function snapshotEvidentialMetric(args) {
1863
+ switch (args.metric) {
1864
+ case "evidence_count": {
1865
+ const count = await computeEvidenceCountMetric(
1866
+ args.ctx,
1867
+ args.beliefNodeId
1868
+ );
1869
+ return {
1870
+ metric: args.metric,
1871
+ value: count,
1872
+ data: { evidenceCount: count }
1873
+ };
1874
+ }
1875
+ case "contradiction_status": {
1876
+ const counts = await computeContradictionCounts(
1877
+ args.ctx,
1878
+ args.beliefNodeId
1879
+ );
1880
+ return {
1881
+ metric: args.metric,
1882
+ value: counts.activeCount,
1883
+ data: counts
1884
+ };
1885
+ }
1886
+ case "edge_freshness": {
1887
+ const freshness = await computeEvidenceFreshness(
1888
+ args.ctx,
1889
+ args.beliefNodeId,
1890
+ args.now
1891
+ );
1892
+ return {
1893
+ metric: args.metric,
1894
+ value: freshness.newestAgeMs,
1895
+ data: freshness
1896
+ };
1897
+ }
1898
+ case "dependent_count": {
1899
+ const count = await computeDependentBeliefCount(
1900
+ args.ctx,
1901
+ args.beliefNodeId
1902
+ );
1903
+ return {
1904
+ metric: args.metric,
1905
+ value: count,
1906
+ data: { dependentCount: count }
1907
+ };
1908
+ }
1909
+ default:
1910
+ return {
1911
+ metric: args.metric,
1912
+ value: null,
1913
+ data: {}
1914
+ };
1915
+ }
1916
+ }
1917
+ async function evaluateBuiltInEvidentialContract(args) {
1918
+ const config = parseEvidentialEvaluatorConfig(
1919
+ args.contract.condition.evaluatorConfig
1920
+ );
1921
+ const snapshot = await snapshotEvidentialMetric({
1942
1922
  ctx: args.ctx,
1943
1923
  beliefNodeId: args.belief._id,
1944
- trigger: normalizeTrigger(args.trigger),
1945
- contractIds: args.contractIds
1924
+ metric: config.metric,
1925
+ now: args.now
1946
1926
  });
1947
- const batchLimit = Math.max(
1948
- 1,
1949
- Math.min(
1950
- args.maxBatchSize ?? MAX_CONTRACT_EVALUATION_BATCH_SIZE,
1951
- MAX_CONTRACT_EVALUATION_BATCH_SIZE
1952
- )
1927
+ const comparisonSatisfied = snapshot.value !== null && compareMetricValue(config.operator, snapshot.value, config.threshold);
1928
+ const result = resolveComparisonResult(
1929
+ args.contract.direction,
1930
+ comparisonSatisfied
1953
1931
  );
1954
- const currentBatch = contracts.slice(0, batchLimit);
1955
- const overflowContracts = contracts.slice(batchLimit);
1956
- let runningConfidence = typeof args.belief.confidence === "number" ? args.belief.confidence : 0.5;
1957
- const results = [];
1958
- for (const contract of currentBatch) {
1959
- const evaluation = await executeContractEvaluation({
1960
- ctx: args.ctx,
1961
- belief: args.belief,
1962
- contract,
1963
- now: Date.now(),
1964
- trigger: normalizeTrigger(args.trigger),
1965
- inputData: args.inputData,
1966
- authenticatedUserId: args.authenticatedUserId,
1967
- currentConfidence: runningConfidence
1968
- });
1969
- runningConfidence = evaluation.currentConfidence;
1970
- args.belief.confidence = runningConfidence;
1971
- results.push({
1972
- contractId: contract.contractId,
1973
- result: evaluation.result,
1974
- status: evaluation.status,
1975
- confidenceAfter: evaluation.confidenceAfter
1976
- });
1977
- }
1978
- if (overflowContracts.length > 0) {
1979
- await args.ctx.scheduler.runAfter(
1980
- 0,
1981
- "epistemicContracts.processContractEvaluationOverflow",
1982
- {
1983
- beliefNodeId: args.belief._id,
1984
- trigger: normalizeTrigger(args.trigger),
1985
- contractIds: overflowContracts.map((contract) => contract.contractId),
1986
- inputData: args.inputData,
1987
- authenticatedUserId: args.authenticatedUserId,
1988
- maxBatchSize: batchLimit
1989
- }
1932
+ return {
1933
+ result,
1934
+ rationale: buildEvidentialRationale({
1935
+ config,
1936
+ snapshot,
1937
+ comparisonSatisfied,
1938
+ result
1939
+ }),
1940
+ data: {
1941
+ ...snapshot.data,
1942
+ metric: config.metric,
1943
+ observedValue: snapshot.value,
1944
+ operator: config.operator,
1945
+ threshold: config.threshold,
1946
+ action: config.action ?? "append_sl_scoring",
1947
+ actionParams: config.actionParams
1948
+ }
1949
+ };
1950
+ }
1951
+ function evaluateMetricCheckerContract(args) {
1952
+ return Promise.resolve().then(() => {
1953
+ const config = parseMetricCheckerConfig(
1954
+ args.contract.condition.evaluatorConfig
1990
1955
  );
1991
- }
1992
- const executionTimeMs = Date.now() - startedAt;
1993
- console.info("[epistemicContracts] processed contract evaluation batch", {
1994
- beliefNodeId: String(args.belief._id),
1995
- trigger: normalizeTrigger(args.trigger),
1996
- batchSize: currentBatch.length,
1997
- overflowCount: overflowContracts.length,
1998
- executionTimeMs
1956
+ const input = getEvaluatorInputRecord(args.inputData, "metricData");
1957
+ const metric = typeof input.metric === "string" && input.metric.length > 0 ? input.metric : config.metric;
1958
+ const observedValue = pickFiniteNumber(input, [
1959
+ "observedValue",
1960
+ "currentValue",
1961
+ "metricValue",
1962
+ "value"
1963
+ ]) ?? config.observedValue ?? config.currentValue ?? config.metricValue ?? null;
1964
+ if (observedValue === null) {
1965
+ return {
1966
+ result: "inconclusive",
1967
+ rationale: `metric_checker is awaiting data for ${metric ?? args.contract.condition.expression}.`,
1968
+ data: {
1969
+ metric,
1970
+ observedValue: null,
1971
+ operator: config.operator,
1972
+ threshold: config.threshold,
1973
+ unit: config.unit
1974
+ }
1975
+ };
1976
+ }
1977
+ const comparisonSatisfied = compareMetricValue(
1978
+ config.operator,
1979
+ observedValue,
1980
+ config.threshold
1981
+ );
1982
+ const result = resolveComparisonResult(
1983
+ args.contract.direction,
1984
+ comparisonSatisfied
1985
+ );
1986
+ return {
1987
+ result,
1988
+ rationale: buildComparisonRationale({
1989
+ label: metric ?? "metric",
1990
+ observedValue,
1991
+ operator: config.operator,
1992
+ threshold: config.threshold,
1993
+ comparisonSatisfied,
1994
+ result,
1995
+ unit: config.unit
1996
+ }),
1997
+ data: {
1998
+ metric,
1999
+ observedValue,
2000
+ operator: config.operator,
2001
+ threshold: config.threshold,
2002
+ unit: config.unit
2003
+ }
2004
+ };
2005
+ });
2006
+ }
2007
+ async function evaluateReferenceCheckCounterContract(args) {
2008
+ const config = parseReferenceCheckCounterConfig(
2009
+ args.contract.condition.evaluatorConfig
2010
+ );
2011
+ const input = getEvaluatorInputRecord(args.inputData, "referenceCheckData");
2012
+ const tag = typeof input.tag === "string" && input.tag.trim().length > 0 ? input.tag.trim() : config.tag;
2013
+ const snapshot = await computeTaggedEvidenceCount({
2014
+ ctx: args.ctx,
2015
+ beliefNodeId: args.belief._id,
2016
+ tag,
2017
+ caseSensitive: config.caseSensitive
1999
2018
  });
2019
+ const comparisonSatisfied = compareMetricValue(
2020
+ config.operator,
2021
+ snapshot.count,
2022
+ config.threshold
2023
+ );
2024
+ const result = resolveComparisonResult(
2025
+ args.contract.direction,
2026
+ comparisonSatisfied
2027
+ );
2000
2028
  return {
2001
- totalContracts: contracts.length,
2002
- processedCount: currentBatch.length,
2003
- overflowCount: overflowContracts.length,
2004
- scheduledOverflow: overflowContracts.length > 0,
2005
- batchSize: currentBatch.length,
2006
- executionTimeMs,
2007
- trigger: normalizeTrigger(args.trigger),
2008
- results
2029
+ result,
2030
+ rationale: buildComparisonRationale({
2031
+ label: `reference checks tagged "${tag}"`,
2032
+ observedValue: snapshot.count,
2033
+ operator: config.operator,
2034
+ threshold: config.threshold,
2035
+ comparisonSatisfied,
2036
+ result
2037
+ }),
2038
+ data: {
2039
+ tag,
2040
+ observedValue: snapshot.count,
2041
+ referenceCheckCount: snapshot.count,
2042
+ matchedEvidenceIds: snapshot.matchedEvidenceIds,
2043
+ operator: config.operator,
2044
+ threshold: config.threshold,
2045
+ caseSensitive: config.caseSensitive ?? false
2046
+ }
2009
2047
  };
2010
2048
  }
2011
- async function loadContractsForBelief(args) {
2012
- return await args.ctx.db.query("epistemicContracts").withIndex("by_belief", (q) => q.eq("beliefNodeId", args.beliefNodeId)).collect();
2049
+ function resolveCompletedTemporalDeadlineResult(args) {
2050
+ const { completedAt, context, deadline, label } = args;
2051
+ if (completedAt !== void 0 && completedAt > deadline) {
2052
+ return {
2053
+ result: "expired",
2054
+ rationale: `${label} completed at ${completedAt}, after deadline ${deadline}.`,
2055
+ data: {
2056
+ label,
2057
+ deadline,
2058
+ completed: true,
2059
+ completedAt,
2060
+ missedDeadline: true,
2061
+ overdueByMs: completedAt - deadline
2062
+ }
2063
+ };
2064
+ }
2065
+ const result = context.contract.direction === "falsifies" ? "disconfirmed" : "confirmed";
2066
+ return {
2067
+ result,
2068
+ rationale: `${label} completed before deadline ${deadline}.`,
2069
+ data: {
2070
+ label,
2071
+ deadline,
2072
+ completed: true,
2073
+ completedAt: completedAt ?? null,
2074
+ missedDeadline: false
2075
+ }
2076
+ };
2013
2077
  }
2014
- async function loadContractsForTrigger(args) {
2015
- const contracts = await loadContractsForBelief(args);
2016
- ensureBuiltInEvaluators();
2017
- const allowedSchedules = resolveSchedulesForTrigger(args.trigger);
2018
- const contractIdFilter = args.contractIds && args.contractIds.length > 0 ? new Set(args.contractIds) : null;
2019
- return contracts.filter((contract) => {
2020
- if (contract.status === "archived") {
2021
- return false;
2078
+ function resolveOpenTemporalDeadlineResult(args) {
2079
+ const { context, deadline, label } = args;
2080
+ if (context.now > deadline) {
2081
+ return {
2082
+ result: "expired",
2083
+ rationale: `${label} missed deadline ${deadline}; temporal contract expired.`,
2084
+ data: {
2085
+ label,
2086
+ deadline,
2087
+ completed: false,
2088
+ overdueByMs: context.now - deadline
2089
+ }
2090
+ };
2091
+ }
2092
+ return {
2093
+ result: "inconclusive",
2094
+ rationale: `${label} is still before deadline ${deadline}; awaiting outcome.`,
2095
+ data: {
2096
+ label,
2097
+ deadline,
2098
+ completed: false,
2099
+ timeRemainingMs: deadline - context.now
2022
2100
  }
2023
- if (contract.conditionType === "composite") {
2024
- return false;
2101
+ };
2102
+ }
2103
+ function evaluateTemporalDeadlineContract(args) {
2104
+ return Promise.resolve().then(() => {
2105
+ if (typeof args.contract.deadline !== "number" || !Number.isFinite(args.contract.deadline)) {
2106
+ throw new Error(
2107
+ "temporal_deadline requires contract.deadline to be set to a finite timestamp."
2108
+ );
2025
2109
  }
2026
- if (!evaluatorRegistry.has(contract.condition.evaluator)) {
2027
- return false;
2110
+ const deadline = args.contract.deadline;
2111
+ const config = parseTemporalDeadlineConfig(
2112
+ args.contract.condition.evaluatorConfig
2113
+ );
2114
+ const input = getEvaluatorInputRecord(args.inputData, "temporalData");
2115
+ const label = (typeof input.label === "string" && input.label.length > 0 ? input.label : config.label) ?? args.contract.title ?? args.contract.condition.expression;
2116
+ const completedAt = pickFiniteNumber(input, [
2117
+ "completedAt",
2118
+ "observedAt",
2119
+ "satisfiedAt",
2120
+ "achievedAt"
2121
+ ]) ?? config.completedAt ?? config.observedAt ?? config.satisfiedAt ?? config.achievedAt;
2122
+ const completed = input.completed === true || config.completed === true || completedAt !== void 0;
2123
+ if (completed) {
2124
+ return resolveCompletedTemporalDeadlineResult({
2125
+ completedAt,
2126
+ context: args,
2127
+ deadline,
2128
+ label
2129
+ });
2028
2130
  }
2029
- if (contractIdFilter) {
2030
- return contractIdFilter.has(contract.contractId);
2131
+ return resolveOpenTemporalDeadlineResult({
2132
+ context: args,
2133
+ deadline,
2134
+ label
2135
+ });
2136
+ });
2137
+ }
2138
+ function evaluateMarketIndexComparatorContract(args) {
2139
+ return Promise.resolve().then(() => {
2140
+ const config = parseMarketIndexComparatorConfig(
2141
+ args.contract.condition.evaluatorConfig
2142
+ );
2143
+ const input = getEvaluatorInputRecord(args.inputData, "marketIndexData");
2144
+ const subject = typeof input.subject === "string" && input.subject.length > 0 ? input.subject : config.subject;
2145
+ const benchmark = typeof input.benchmark === "string" && input.benchmark.length > 0 ? input.benchmark : config.benchmark;
2146
+ const subjectValue = pickFiniteNumber(input, ["subjectValue", "primaryValue", "leftValue"]) ?? config.subjectValue ?? config.primaryValue ?? null;
2147
+ const benchmarkValue = pickFiniteNumber(input, [
2148
+ "benchmarkValue",
2149
+ "comparisonValue",
2150
+ "rightValue"
2151
+ ]) ?? config.benchmarkValue ?? config.comparisonValue ?? null;
2152
+ if (subjectValue === null || benchmarkValue === null) {
2153
+ return {
2154
+ result: "inconclusive",
2155
+ rationale: "market_index_comparator is awaiting both subject and benchmark values.",
2156
+ data: {
2157
+ subject,
2158
+ subjectValue,
2159
+ benchmark,
2160
+ benchmarkValue,
2161
+ operator: config.operator,
2162
+ threshold: config.threshold
2163
+ }
2164
+ };
2031
2165
  }
2032
- return allowedSchedules.has(contract.evaluationSchedule);
2166
+ if (benchmarkValue === 0) {
2167
+ throw new Error(
2168
+ "market_index_comparator cannot compare against a zero benchmark value."
2169
+ );
2170
+ }
2171
+ const differentialPercent = (subjectValue - benchmarkValue) / Math.abs(benchmarkValue) * 100;
2172
+ const comparisonSatisfied = compareMetricValue(
2173
+ config.operator,
2174
+ differentialPercent,
2175
+ config.threshold
2176
+ );
2177
+ const result = resolveComparisonResult(
2178
+ args.contract.direction,
2179
+ comparisonSatisfied
2180
+ );
2181
+ return {
2182
+ result,
2183
+ rationale: buildComparisonRationale({
2184
+ label: `${subject ?? "subject"} vs ${benchmark ?? "benchmark"} differential`,
2185
+ observedValue: differentialPercent,
2186
+ operator: config.operator,
2187
+ threshold: config.threshold,
2188
+ comparisonSatisfied,
2189
+ result,
2190
+ unit: "%"
2191
+ }),
2192
+ data: {
2193
+ subject,
2194
+ subjectValue,
2195
+ benchmark,
2196
+ benchmarkValue,
2197
+ differentialPercent,
2198
+ operator: config.operator,
2199
+ threshold: config.threshold
2200
+ }
2201
+ };
2033
2202
  });
2034
2203
  }
2204
+ var METRIC_COMPARATOR_EVALUATOR_NAMES = {
2205
+ evidentialAliases: /* @__PURE__ */ new Set([
2206
+ "evidential",
2207
+ "built_in_evidential",
2208
+ "builtin_evidential"
2209
+ ]),
2210
+ metricChecker: "metric_checker",
2211
+ referenceCheckCounter: "reference_check_counter",
2212
+ temporalDeadline: "temporal_deadline",
2213
+ marketIndexComparator: "market_index_comparator"
2214
+ };
2035
2215
 
2036
2216
  // src/evaluators/shared.ts
2217
+ var WINDOWS_PATH_SEPARATORS = /\\/g;
2218
+ var LEADING_DOT_SLASH = /^\.\//;
2037
2219
  function asArray(value) {
2038
2220
  return Array.isArray(value) ? value : [];
2039
2221
  }
@@ -2075,7 +2257,7 @@ function extractTextCandidates(value) {
2075
2257
  return Array.from(new Set(candidates));
2076
2258
  }
2077
2259
  function normalizeFilePath(value) {
2078
- return value.replace(/\\/g, "/").replace(/^\.\//, "");
2260
+ return value.replace(WINDOWS_PATH_SEPARATORS, "/").replace(LEADING_DOT_SLASH, "");
2079
2261
  }
2080
2262
  function normalizeToolResultEnvelope(value) {
2081
2263
  const record = asRecord(value);
@@ -2129,7 +2311,7 @@ function somePatternMatches(filePath, patterns) {
2129
2311
  return patterns.some((pattern) => patternMatchesPath(filePath, pattern));
2130
2312
  }
2131
2313
 
2132
- // src/evaluators/lintCheckerEvaluator.ts
2314
+ // src/evaluators/lint-checker-evaluator.ts
2133
2315
  function parseConfig(condition) {
2134
2316
  const record = asRecord(condition.evaluatorConfig);
2135
2317
  if (!record) {
@@ -2201,7 +2383,10 @@ var lintCheckerEvaluator = {
2201
2383
  }
2202
2384
  const envelope = normalizeToolResultEnvelope(args.resultData);
2203
2385
  const exitCode = asNumber(envelope.exitCode);
2204
- const matchedDiagnostics = getMatchedDiagnostics(args.contract, args.resultData);
2386
+ const matchedDiagnostics = getMatchedDiagnostics(
2387
+ args.contract,
2388
+ args.resultData
2389
+ );
2205
2390
  if (matchedDiagnostics.length === 0 && exitCode !== 0 && exitCode !== null) {
2206
2391
  return {
2207
2392
  result: "inconclusive",
@@ -2227,7 +2412,7 @@ var lintCheckerEvaluator = {
2227
2412
  }
2228
2413
  };
2229
2414
 
2230
- // src/evaluators/sentryCheckerEvaluator.ts
2415
+ // src/evaluators/sentry-checker-evaluator.ts
2231
2416
  function parseConfig2(condition) {
2232
2417
  const record = asRecord(condition.evaluatorConfig);
2233
2418
  if (!record) {
@@ -2312,7 +2497,7 @@ var sentryCheckerEvaluator = {
2312
2497
  }
2313
2498
  };
2314
2499
 
2315
- // src/evaluators/testRunnerEvaluator.ts
2500
+ // src/evaluators/test-runner-evaluator.ts
2316
2501
  function parseConfig3(condition) {
2317
2502
  const record = asRecord(condition.evaluatorConfig);
2318
2503
  if (!record) {
@@ -2458,7 +2643,7 @@ var testRunnerEvaluator = {
2458
2643
  }
2459
2644
  };
2460
2645
 
2461
- // src/evaluators/tscCheckerEvaluator.ts
2646
+ // src/evaluators/tsc-checker-evaluator.ts
2462
2647
  function parseConfig4(condition) {
2463
2648
  const record = asRecord(condition.evaluatorConfig);
2464
2649
  if (!record) {
@@ -2470,118 +2655,383 @@ function parseConfig4(condition) {
2470
2655
  if (filePatterns.length === 0) {
2471
2656
  throw new Error("tsc_checker requires at least one file pattern.");
2472
2657
  }
2473
- return { filePatterns };
2658
+ return { filePatterns };
2659
+ }
2660
+ function parseStructuredDiagnostics(resultData) {
2661
+ const envelope = normalizeToolResultEnvelope(resultData);
2662
+ const record = asRecord(envelope.report) ?? asRecord(envelope.data) ?? asRecord(resultData);
2663
+ if (!record) {
2664
+ return [];
2665
+ }
2666
+ return asArray(record.diagnostics).flatMap((entry) => {
2667
+ const diagnostic = asRecord(entry);
2668
+ if (!diagnostic) {
2669
+ return [];
2670
+ }
2671
+ const filePath = asString(diagnostic.filePath) ?? asString(diagnostic.file) ?? asString(asRecord(diagnostic.location)?.filePath) ?? asString(asRecord(asRecord(diagnostic.location)?.path)?.file);
2672
+ const message = asString(diagnostic.message) ?? "TypeScript error";
2673
+ return [
2674
+ {
2675
+ code: asString(diagnostic.code) ?? void 0,
2676
+ filePath: filePath ? normalizeFilePath(filePath) : null,
2677
+ message
2678
+ }
2679
+ ];
2680
+ });
2681
+ }
2682
+ function parseTextDiagnostics(resultData) {
2683
+ const diagnostics = [];
2684
+ const patterns = [
2685
+ /^(.+?)\((\d+),(\d+)\): error TS(\d+): (.+)$/gm,
2686
+ /^(.+?):(\d+):(\d+) - error TS(\d+): (.+)$/gm
2687
+ ];
2688
+ for (const text of extractTextCandidates(resultData)) {
2689
+ for (const pattern of patterns) {
2690
+ for (const match of text.matchAll(pattern)) {
2691
+ diagnostics.push({
2692
+ code: match[4],
2693
+ filePath: normalizeFilePath(match[1] ?? ""),
2694
+ message: match[5] ?? "TypeScript error"
2695
+ });
2696
+ }
2697
+ }
2698
+ }
2699
+ return diagnostics;
2700
+ }
2701
+ function parseDiagnostics2(resultData) {
2702
+ const structured = parseStructuredDiagnostics(resultData);
2703
+ return structured.length > 0 ? structured : parseTextDiagnostics(resultData);
2704
+ }
2705
+ function getMatchedDiagnostics2(contract, resultData) {
2706
+ const config = parseConfig4(contract.condition);
2707
+ return parseDiagnostics2(resultData).filter(
2708
+ (diagnostic) => somePatternMatches(diagnostic.filePath, config.filePatterns)
2709
+ );
2710
+ }
2711
+ var tscCheckerEvaluator = {
2712
+ name: "tsc_checker",
2713
+ matches({ contract, resultData }) {
2714
+ const envelope = normalizeToolResultEnvelope(resultData);
2715
+ const exitCode = asNumber(envelope.exitCode);
2716
+ if (exitCode === 0) {
2717
+ return true;
2718
+ }
2719
+ return getMatchedDiagnostics2(contract, resultData).length > 0;
2720
+ },
2721
+ evaluate(args) {
2722
+ const config = parseConfig4(args.contract.condition);
2723
+ if (!args.resultData) {
2724
+ return {
2725
+ result: "error",
2726
+ rationale: "tsc_checker requires TypeScript diagnostic resultData."
2727
+ };
2728
+ }
2729
+ const envelope = normalizeToolResultEnvelope(args.resultData);
2730
+ const exitCode = asNumber(envelope.exitCode);
2731
+ const matchedDiagnostics = getMatchedDiagnostics2(
2732
+ args.contract,
2733
+ args.resultData
2734
+ );
2735
+ if (matchedDiagnostics.length === 0 && exitCode !== 0 && exitCode !== null) {
2736
+ return {
2737
+ result: "inconclusive",
2738
+ rationale: "TypeScript reported errors, but none matched the configured file patterns."
2739
+ };
2740
+ }
2741
+ const conditionSatisfied = exitCode === 0 || exitCode !== null && matchedDiagnostics.length === 0;
2742
+ return {
2743
+ result: deriveDirectionalResult(
2744
+ args.contract.direction,
2745
+ conditionSatisfied
2746
+ ),
2747
+ rationale: conditionSatisfied ? `TypeScript reported no matching diagnostics for ${config.filePatterns.join(", ")}.` : `TypeScript found ${matchedDiagnostics.length} matching diagnostic(s): ${matchedDiagnostics.map(
2748
+ (diagnostic) => diagnostic.filePath ? `${diagnostic.filePath}${diagnostic.code ? ` (TS${diagnostic.code})` : ""}` : diagnostic.message
2749
+ ).join(", ")}.`,
2750
+ data: {
2751
+ exitCode,
2752
+ filePatterns: config.filePatterns,
2753
+ matchedDiagnostics
2754
+ }
2755
+ };
2756
+ }
2757
+ };
2758
+
2759
+ // src/evaluators/index.ts
2760
+ var ENGINEERING_EPISTEMIC_EVALUATORS = [
2761
+ testRunnerEvaluator,
2762
+ tscCheckerEvaluator,
2763
+ lintCheckerEvaluator,
2764
+ sentryCheckerEvaluator
2765
+ ];
2766
+ var ENGINEERING_EVALUATOR_NAMES = new Set(
2767
+ ENGINEERING_EPISTEMIC_EVALUATORS.map((evaluator) => evaluator.name)
2768
+ );
2769
+ function getEngineeringEpistemicEvaluator(name) {
2770
+ return ENGINEERING_EPISTEMIC_EVALUATORS.find(
2771
+ (evaluator) => evaluator.name === name
2772
+ );
2773
+ }
2774
+
2775
+ // src/epistemicContracts.evaluators.ts
2776
+ var evaluatorRegistry = /* @__PURE__ */ new Map();
2777
+ var BUILT_IN_EVIDENTIAL_ALIASES2 = METRIC_COMPARATOR_EVALUATOR_NAMES.evidentialAliases;
2778
+ var BUILT_IN_METRIC_CHECKER2 = METRIC_COMPARATOR_EVALUATOR_NAMES.metricChecker;
2779
+ var BUILT_IN_REFERENCE_CHECK_COUNTER2 = METRIC_COMPARATOR_EVALUATOR_NAMES.referenceCheckCounter;
2780
+ var BUILT_IN_TEMPORAL_DEADLINE2 = METRIC_COMPARATOR_EVALUATOR_NAMES.temporalDeadline;
2781
+ var BUILT_IN_MARKET_INDEX_COMPARATOR2 = METRIC_COMPARATOR_EVALUATOR_NAMES.marketIndexComparator;
2782
+ var MAX_CONTRACT_EVALUATION_BATCH_SIZE = 50;
2783
+ function confidenceSeed(args) {
2784
+ if (typeof args.currentConfidence === "number") {
2785
+ return args.currentConfidence;
2786
+ }
2787
+ if (typeof args.beliefConfidence === "number") {
2788
+ return args.beliefConfidence;
2789
+ }
2790
+ return 0.5;
2791
+ }
2792
+ function contractDocId(contract) {
2793
+ return contract._id;
2794
+ }
2795
+ function ensureBuiltInEvaluators() {
2796
+ for (const name of BUILT_IN_EVIDENTIAL_ALIASES2) {
2797
+ if (evaluatorRegistry.has(name)) {
2798
+ continue;
2799
+ }
2800
+ evaluatorRegistry.set(name, {
2801
+ name,
2802
+ evaluate: evaluateBuiltInEvidentialContract
2803
+ });
2804
+ }
2805
+ for (const evaluator of ENGINEERING_EPISTEMIC_EVALUATORS) {
2806
+ if (evaluatorRegistry.has(evaluator.name)) {
2807
+ continue;
2808
+ }
2809
+ evaluatorRegistry.set(evaluator.name, evaluator);
2810
+ }
2811
+ const researchEvaluators = [
2812
+ [BUILT_IN_METRIC_CHECKER2, evaluateMetricCheckerContract],
2813
+ [BUILT_IN_REFERENCE_CHECK_COUNTER2, evaluateReferenceCheckCounterContract],
2814
+ [BUILT_IN_TEMPORAL_DEADLINE2, evaluateTemporalDeadlineContract],
2815
+ [BUILT_IN_MARKET_INDEX_COMPARATOR2, evaluateMarketIndexComparatorContract]
2816
+ ];
2817
+ for (const [name, evaluate] of researchEvaluators) {
2818
+ if (evaluatorRegistry.has(name)) {
2819
+ continue;
2820
+ }
2821
+ evaluatorRegistry.set(name, { name, evaluate });
2822
+ }
2823
+ }
2824
+ function normalizeTrigger(trigger) {
2825
+ if (trigger === "evidence_added") {
2826
+ return "evidence_added";
2827
+ }
2828
+ if (trigger === "periodic") {
2829
+ return "periodic";
2830
+ }
2831
+ if (trigger === "manual") {
2832
+ return "manual";
2833
+ }
2834
+ return "event_driven";
2835
+ }
2836
+ function resolveSchedulesForTrigger(trigger) {
2837
+ if (trigger === "evidence_added") {
2838
+ return /* @__PURE__ */ new Set(["on_evidence", "event_driven"]);
2839
+ }
2840
+ if (trigger === "periodic") {
2841
+ return /* @__PURE__ */ new Set(["periodic"]);
2842
+ }
2843
+ if (trigger === "manual") {
2844
+ return /* @__PURE__ */ new Set(["on_demand", "event_driven"]);
2845
+ }
2846
+ return /* @__PURE__ */ new Set(["event_driven"]);
2847
+ }
2848
+ async function executeContractEvaluation(args) {
2849
+ ensureBuiltInEvaluators();
2850
+ const evaluator = evaluatorRegistry.get(args.contract.condition.evaluator);
2851
+ let evaluation;
2852
+ if (evaluator) {
2853
+ try {
2854
+ evaluation = await evaluator.evaluate({
2855
+ belief: args.belief,
2856
+ contract: args.contract,
2857
+ ctx: args.ctx,
2858
+ now: args.now,
2859
+ resultData: args.resultData,
2860
+ trigger: args.trigger,
2861
+ inputData: args.inputData
2862
+ });
2863
+ } catch (error) {
2864
+ evaluation = {
2865
+ result: "error",
2866
+ rationale: error instanceof Error ? error.message : "Unknown evaluator error."
2867
+ };
2868
+ }
2869
+ } else {
2870
+ evaluation = {
2871
+ result: "error",
2872
+ rationale: `No epistemic evaluator registered for "${args.contract.condition.evaluator}".`
2873
+ };
2874
+ }
2875
+ const confidenceBefore = confidenceSeed({
2876
+ beliefConfidence: args.belief.confidence,
2877
+ currentConfidence: args.currentConfidence
2878
+ });
2879
+ const modulationPlan = deriveContractModulationPlan({
2880
+ currentConfidence: confidenceBefore,
2881
+ modulation: args.contract.modulation,
2882
+ result: evaluation.result,
2883
+ resultConfidence: evaluation.confidence
2884
+ });
2885
+ let beliefConfidenceId;
2886
+ let confidenceAfter = confidenceBefore;
2887
+ if (modulationPlan) {
2888
+ const contractB = Math.max(0, Math.min(1, modulationPlan.confidenceAfter));
2889
+ const modulationResult = await applyBeliefConfidenceChange(args.ctx, {
2890
+ nodeId: args.contract.beliefNodeId,
2891
+ belief: contractB,
2892
+ disbelief: 1 - contractB,
2893
+ uncertainty: 0,
2894
+ baseRate: 0.5,
2895
+ trigger: modulationPlan.trigger,
2896
+ rationale: `Epistemic contract "${args.contract.title}" ${evaluation.result}: ${evaluation.rationale}`,
2897
+ authenticatedUserId: args.authenticatedUserId
2898
+ });
2899
+ beliefConfidenceId = modulationResult.beliefConfidenceId;
2900
+ confidenceAfter = typeof modulationResult.newConfidence === "number" ? modulationResult.newConfidence : modulationPlan.confidenceAfter;
2901
+ }
2902
+ const evaluationId = await args.ctx.db.insert("contractEvaluations", {
2903
+ contractId: args.contract.contractId,
2904
+ beliefNodeId: args.contract.beliefNodeId,
2905
+ result: evaluation.result,
2906
+ evaluatedAt: args.now,
2907
+ evaluator: args.contract.condition.evaluator,
2908
+ trigger: args.trigger,
2909
+ resultData: evaluation.data,
2910
+ modulationApplied: Boolean(modulationPlan),
2911
+ confidenceDelta: modulationPlan?.confidenceDelta,
2912
+ confidenceBefore: modulationPlan?.confidenceBefore,
2913
+ confidenceAfter: modulationPlan ? confidenceAfter : void 0,
2914
+ beliefConfidenceId,
2915
+ modulationRationale: evaluation.rationale,
2916
+ topicId: args.contract.topicId
2917
+ });
2918
+ const nextStatus = deriveContractStatus(
2919
+ evaluation.result,
2920
+ args.contract.status
2921
+ );
2922
+ await args.ctx.db.patch(contractDocId(args.contract), {
2923
+ status: nextStatus,
2924
+ lastEvaluatedAt: args.now,
2925
+ evaluationCount: (args.contract.evaluationCount ?? 0) + 1,
2926
+ updatedAt: args.now
2927
+ });
2928
+ return {
2929
+ evaluationId,
2930
+ result: evaluation.result,
2931
+ status: nextStatus,
2932
+ confidenceBefore,
2933
+ confidenceAfter,
2934
+ rationale: evaluation.rationale,
2935
+ data: evaluation.data,
2936
+ currentConfidence: confidenceAfter
2937
+ };
2474
2938
  }
2475
- function parseStructuredDiagnostics(resultData) {
2476
- const envelope = normalizeToolResultEnvelope(resultData);
2477
- const record = asRecord(envelope.report) ?? asRecord(envelope.data) ?? asRecord(resultData);
2478
- if (!record) {
2479
- return [];
2939
+ async function evaluateContractsForTriggerBatch(args) {
2940
+ const startedAt = Date.now();
2941
+ const contracts = await loadContractsForTrigger({
2942
+ ctx: args.ctx,
2943
+ beliefNodeId: args.belief._id,
2944
+ trigger: normalizeTrigger(args.trigger),
2945
+ contractIds: args.contractIds
2946
+ });
2947
+ const batchLimit = Math.max(
2948
+ 1,
2949
+ Math.min(
2950
+ args.maxBatchSize ?? MAX_CONTRACT_EVALUATION_BATCH_SIZE,
2951
+ MAX_CONTRACT_EVALUATION_BATCH_SIZE
2952
+ )
2953
+ );
2954
+ const currentBatch = contracts.slice(0, batchLimit);
2955
+ const overflowContracts = contracts.slice(batchLimit);
2956
+ let runningConfidence = typeof args.belief.confidence === "number" ? args.belief.confidence : 0.5;
2957
+ const results = [];
2958
+ for (const contract of currentBatch) {
2959
+ const evaluation = await executeContractEvaluation({
2960
+ ctx: args.ctx,
2961
+ belief: args.belief,
2962
+ contract,
2963
+ now: Date.now(),
2964
+ trigger: normalizeTrigger(args.trigger),
2965
+ inputData: args.inputData,
2966
+ authenticatedUserId: args.authenticatedUserId,
2967
+ currentConfidence: runningConfidence
2968
+ });
2969
+ runningConfidence = evaluation.currentConfidence;
2970
+ args.belief.confidence = runningConfidence;
2971
+ results.push({
2972
+ contractId: contract.contractId,
2973
+ result: evaluation.result,
2974
+ status: evaluation.status,
2975
+ confidenceAfter: evaluation.confidenceAfter
2976
+ });
2480
2977
  }
2481
- return asArray(record.diagnostics).flatMap((entry) => {
2482
- const diagnostic = asRecord(entry);
2483
- if (!diagnostic) {
2484
- return [];
2485
- }
2486
- const filePath = asString(diagnostic.filePath) ?? asString(diagnostic.file) ?? asString(asRecord(diagnostic.location)?.filePath) ?? asString(asRecord(asRecord(diagnostic.location)?.path)?.file);
2487
- const message = asString(diagnostic.message) ?? "TypeScript error";
2488
- return [
2978
+ if (overflowContracts.length > 0) {
2979
+ await args.ctx.scheduler.runAfter(
2980
+ 0,
2981
+ "epistemicContracts.processContractEvaluationOverflow",
2489
2982
  {
2490
- code: asString(diagnostic.code) ?? void 0,
2491
- filePath: filePath ? normalizeFilePath(filePath) : null,
2492
- message
2493
- }
2494
- ];
2495
- });
2496
- }
2497
- function parseTextDiagnostics(resultData) {
2498
- const diagnostics = [];
2499
- const patterns = [
2500
- /^(.+?)\((\d+),(\d+)\): error TS(\d+): (.+)$/gm,
2501
- /^(.+?):(\d+):(\d+) - error TS(\d+): (.+)$/gm
2502
- ];
2503
- for (const text of extractTextCandidates(resultData)) {
2504
- for (const pattern of patterns) {
2505
- for (const match of text.matchAll(pattern)) {
2506
- diagnostics.push({
2507
- code: match[4],
2508
- filePath: normalizeFilePath(match[1] ?? ""),
2509
- message: match[5] ?? "TypeScript error"
2510
- });
2983
+ beliefNodeId: args.belief._id,
2984
+ trigger: normalizeTrigger(args.trigger),
2985
+ contractIds: overflowContracts.map((contract) => contract.contractId),
2986
+ inputData: args.inputData,
2987
+ authenticatedUserId: args.authenticatedUserId,
2988
+ maxBatchSize: batchLimit
2511
2989
  }
2512
- }
2990
+ );
2513
2991
  }
2514
- return diagnostics;
2515
- }
2516
- function parseDiagnostics2(resultData) {
2517
- const structured = parseStructuredDiagnostics(resultData);
2518
- return structured.length > 0 ? structured : parseTextDiagnostics(resultData);
2992
+ const executionTimeMs = Date.now() - startedAt;
2993
+ console.info("[epistemicContracts] processed contract evaluation batch", {
2994
+ beliefNodeId: String(args.belief._id),
2995
+ trigger: normalizeTrigger(args.trigger),
2996
+ batchSize: currentBatch.length,
2997
+ overflowCount: overflowContracts.length,
2998
+ executionTimeMs
2999
+ });
3000
+ return {
3001
+ totalContracts: contracts.length,
3002
+ processedCount: currentBatch.length,
3003
+ overflowCount: overflowContracts.length,
3004
+ scheduledOverflow: overflowContracts.length > 0,
3005
+ batchSize: currentBatch.length,
3006
+ executionTimeMs,
3007
+ trigger: normalizeTrigger(args.trigger),
3008
+ results
3009
+ };
2519
3010
  }
2520
- function getMatchedDiagnostics2(contract, resultData) {
2521
- const config = parseConfig4(contract.condition);
2522
- return parseDiagnostics2(resultData).filter(
2523
- (diagnostic) => somePatternMatches(diagnostic.filePath, config.filePatterns)
2524
- );
3011
+ async function loadContractsForBelief(args) {
3012
+ const rows = await args.ctx.db.query("epistemicContracts").withIndex("by_belief", (q) => q.eq("beliefNodeId", args.beliefNodeId)).collect();
3013
+ return rows;
2525
3014
  }
2526
- var tscCheckerEvaluator = {
2527
- name: "tsc_checker",
2528
- matches({ contract, resultData }) {
2529
- const envelope = normalizeToolResultEnvelope(resultData);
2530
- const exitCode = asNumber(envelope.exitCode);
2531
- if (exitCode === 0) {
2532
- return true;
3015
+ async function loadContractsForTrigger(args) {
3016
+ const contracts = await loadContractsForBelief(args);
3017
+ ensureBuiltInEvaluators();
3018
+ const allowedSchedules = resolveSchedulesForTrigger(args.trigger);
3019
+ const contractIdFilter = args.contractIds && args.contractIds.length > 0 ? new Set(args.contractIds) : null;
3020
+ return contracts.filter((contract) => {
3021
+ if (contract.status === "archived") {
3022
+ return false;
2533
3023
  }
2534
- return getMatchedDiagnostics2(contract, resultData).length > 0;
2535
- },
2536
- evaluate(args) {
2537
- const config = parseConfig4(args.contract.condition);
2538
- if (!args.resultData) {
2539
- return {
2540
- result: "error",
2541
- rationale: "tsc_checker requires TypeScript diagnostic resultData."
2542
- };
3024
+ if (contract.conditionType === "composite") {
3025
+ return false;
2543
3026
  }
2544
- const envelope = normalizeToolResultEnvelope(args.resultData);
2545
- const exitCode = asNumber(envelope.exitCode);
2546
- const matchedDiagnostics = getMatchedDiagnostics2(args.contract, args.resultData);
2547
- if (matchedDiagnostics.length === 0 && exitCode !== 0 && exitCode !== null) {
2548
- return {
2549
- result: "inconclusive",
2550
- rationale: "TypeScript reported errors, but none matched the configured file patterns."
2551
- };
3027
+ if (!evaluatorRegistry.has(contract.condition.evaluator)) {
3028
+ return false;
2552
3029
  }
2553
- const conditionSatisfied = exitCode === 0 || exitCode !== null && matchedDiagnostics.length === 0;
2554
- return {
2555
- result: deriveDirectionalResult(
2556
- args.contract.direction,
2557
- conditionSatisfied
2558
- ),
2559
- rationale: conditionSatisfied ? `TypeScript reported no matching diagnostics for ${config.filePatterns.join(", ")}.` : `TypeScript found ${matchedDiagnostics.length} matching diagnostic(s): ${matchedDiagnostics.map(
2560
- (diagnostic) => diagnostic.filePath ? `${diagnostic.filePath}${diagnostic.code ? ` (TS${diagnostic.code})` : ""}` : diagnostic.message
2561
- ).join(", ")}.`,
2562
- data: {
2563
- exitCode,
2564
- filePatterns: config.filePatterns,
2565
- matchedDiagnostics
2566
- }
2567
- };
2568
- }
2569
- };
2570
-
2571
- // src/evaluators/index.ts
2572
- var ENGINEERING_EPISTEMIC_EVALUATORS = [
2573
- testRunnerEvaluator,
2574
- tscCheckerEvaluator,
2575
- lintCheckerEvaluator,
2576
- sentryCheckerEvaluator
2577
- ];
2578
- var ENGINEERING_EVALUATOR_NAMES = new Set(
2579
- ENGINEERING_EPISTEMIC_EVALUATORS.map((evaluator) => evaluator.name)
2580
- );
2581
- function getEngineeringEpistemicEvaluator(name) {
2582
- return ENGINEERING_EPISTEMIC_EVALUATORS.find(
2583
- (evaluator) => evaluator.name === name
2584
- );
3030
+ if (contractIdFilter) {
3031
+ return contractIdFilter.has(contract.contractId);
3032
+ }
3033
+ return allowedSchedules.has(contract.evaluationSchedule);
3034
+ });
2585
3035
  }
2586
3036
 
2587
3037
  // src/epistemicContracts.handlers.ts
@@ -2597,6 +3047,24 @@ function resolveSchedulesForTrigger2(trigger) {
2597
3047
  }
2598
3048
  return /* @__PURE__ */ new Set(["event_driven"]);
2599
3049
  }
3050
+ function contractTables(ctx) {
3051
+ return ctx.db;
3052
+ }
3053
+ function assertBeliefNode(value, message = "Belief not found.") {
3054
+ if (!value || typeof value !== "object" || value.nodeType !== "belief") {
3055
+ throw new Error(message);
3056
+ }
3057
+ return value;
3058
+ }
3059
+ function normalizeEngineeringTrigger(trigger) {
3060
+ if (trigger === "manual") {
3061
+ return "manual";
3062
+ }
3063
+ if (trigger === "evidence_added") {
3064
+ return "evidence_added";
3065
+ }
3066
+ return trigger;
3067
+ }
2600
3068
  async function requireAuth(ctx) {
2601
3069
  const userId = await getCurrentUserId(ctx);
2602
3070
  if (!userId) {
@@ -2632,17 +3100,21 @@ async function requireTopicReadAccess(ctx, beliefs, userId) {
2632
3100
  projectIds.add(belief.projectId);
2633
3101
  }
2634
3102
  for (const projectId of projectIds) {
2635
- const hasAccess = await checkProjectAccess(ctx, projectId, userId);
3103
+ const hasAccess = await checkProjectAccess(
3104
+ ctx,
3105
+ projectId,
3106
+ userId
3107
+ );
2636
3108
  if (!hasAccess) {
2637
3109
  throw new Error("Project access required.");
2638
3110
  }
2639
3111
  }
2640
3112
  }
2641
3113
  async function getContractByContractId(ctx, contractId) {
2642
- return await ctx.db.query("epistemicContracts").withIndex("by_contractId", (q) => q.eq("contractId", contractId)).first();
3114
+ return await contractTables(ctx).query("epistemicContracts").withIndex("by_contractId", (q) => q.eq("contractId", contractId)).first();
2643
3115
  }
2644
3116
  async function getLatestEvaluation(ctx, contractId) {
2645
- const evaluations = await ctx.db.query("contractEvaluations").withIndex("by_contract_time", (q) => q.eq("contractId", contractId)).order("desc").take(1);
3117
+ const evaluations = await contractTables(ctx).query("contractEvaluations").withIndex("by_contract_time", (q) => q.eq("contractId", contractId)).order("desc").take(1);
2646
3118
  return evaluations[0] ?? null;
2647
3119
  }
2648
3120
  var evaluateEvidenceCount = query({
@@ -2652,10 +3124,7 @@ var evaluateEvidenceCount = query({
2652
3124
  returns: permissiveReturn,
2653
3125
  handler: async (ctx, args) => {
2654
3126
  const userId = await requireAuth(ctx);
2655
- const belief = await ctx.db.get(args.beliefNodeId);
2656
- if (!belief || belief.nodeType !== "belief") {
2657
- throw new Error("Belief not found.");
2658
- }
3127
+ const belief = assertBeliefNode(await ctx.db.get(args.beliefNodeId));
2659
3128
  await requireBeliefProjectAccess(ctx, belief, userId);
2660
3129
  return await computeEvidenceCountMetric(ctx, args.beliefNodeId);
2661
3130
  }
@@ -2667,10 +3136,7 @@ var evaluateContradictionStatus = query({
2667
3136
  returns: permissiveReturn,
2668
3137
  handler: async (ctx, args) => {
2669
3138
  const userId = await requireAuth(ctx);
2670
- const belief = await ctx.db.get(args.beliefNodeId);
2671
- if (!belief || belief.nodeType !== "belief") {
2672
- throw new Error("Belief not found.");
2673
- }
3139
+ const belief = assertBeliefNode(await ctx.db.get(args.beliefNodeId));
2674
3140
  await requireBeliefProjectAccess(ctx, belief, userId);
2675
3141
  return await computeContradictionCounts(ctx, args.beliefNodeId);
2676
3142
  }
@@ -2682,10 +3148,7 @@ var evaluateEdgeFreshness = query({
2682
3148
  returns: permissiveReturn,
2683
3149
  handler: async (ctx, args) => {
2684
3150
  const userId = await requireAuth(ctx);
2685
- const belief = await ctx.db.get(args.beliefNodeId);
2686
- if (!belief || belief.nodeType !== "belief") {
2687
- throw new Error("Belief not found.");
2688
- }
3151
+ const belief = assertBeliefNode(await ctx.db.get(args.beliefNodeId));
2689
3152
  await requireBeliefProjectAccess(ctx, belief, userId);
2690
3153
  return await computeEvidenceFreshness(ctx, args.beliefNodeId);
2691
3154
  }
@@ -2697,10 +3160,7 @@ var evaluateDependentBeliefCount = query({
2697
3160
  returns: permissiveReturn,
2698
3161
  handler: async (ctx, args) => {
2699
3162
  const userId = await requireAuth(ctx);
2700
- const belief = await ctx.db.get(args.beliefNodeId);
2701
- if (!belief || belief.nodeType !== "belief") {
2702
- throw new Error("Belief not found.");
2703
- }
3163
+ const belief = assertBeliefNode(await ctx.db.get(args.beliefNodeId));
2704
3164
  await requireBeliefProjectAccess(ctx, belief, userId);
2705
3165
  return await computeDependentBeliefCount(ctx, args.beliefNodeId);
2706
3166
  }
@@ -2764,10 +3224,7 @@ var createEpistemicContract = mutation({
2764
3224
  ctx,
2765
3225
  args.authenticatedUserId
2766
3226
  );
2767
- const belief = await ctx.db.get(args.beliefNodeId);
2768
- if (!belief || belief.nodeType !== "belief") {
2769
- throw new Error("Belief not found.");
2770
- }
3227
+ const belief = assertBeliefNode(await ctx.db.get(args.beliefNodeId));
2771
3228
  await requireBeliefProjectAccess(ctx, belief, userId);
2772
3229
  const now = Date.now();
2773
3230
  const contractId = generateGlobalId();
@@ -2822,10 +3279,10 @@ var evaluateContract = mutation({
2822
3279
  if (contract.status === "archived") {
2823
3280
  throw new Error("Archived contracts cannot be evaluated.");
2824
3281
  }
2825
- const belief = await ctx.db.get(contract.beliefNodeId);
2826
- if (!belief || belief.nodeType !== "belief") {
2827
- throw new Error("Belief not found for contract.");
2828
- }
3282
+ const belief = assertBeliefNode(
3283
+ await ctx.db.get(contract.beliefNodeId),
3284
+ "Belief not found for contract."
3285
+ );
2829
3286
  await requireBeliefProjectAccess(ctx, belief, userId);
2830
3287
  const evaluation = await executeContractEvaluation({
2831
3288
  ctx,
@@ -2863,14 +3320,11 @@ var evaluateEngineeringContracts = mutation({
2863
3320
  ctx,
2864
3321
  args.authenticatedUserId
2865
3322
  );
2866
- const belief = await ctx.db.get(args.beliefNodeId);
2867
- if (!belief || belief.nodeType !== "belief") {
2868
- throw new Error("Belief not found.");
2869
- }
3323
+ const belief = assertBeliefNode(await ctx.db.get(args.beliefNodeId));
2870
3324
  await requireBeliefProjectAccess(ctx, belief, userId);
2871
3325
  const trigger = args.trigger ?? "event_driven";
2872
3326
  const allowedSchedules = resolveSchedulesForTrigger2(
2873
- trigger === "manual" ? "manual" : trigger === "evidence_added" ? "evidence_added" : trigger
3327
+ normalizeEngineeringTrigger(trigger)
2874
3328
  );
2875
3329
  const payloadByEvaluator = /* @__PURE__ */ new Map([
2876
3330
  ["test_runner", args.testOutput],
@@ -2949,10 +3403,7 @@ var evaluateContractsForTrigger = mutation({
2949
3403
  ctx,
2950
3404
  args.authenticatedUserId
2951
3405
  );
2952
- const belief = await ctx.db.get(args.beliefNodeId);
2953
- if (!belief || belief.nodeType !== "belief") {
2954
- throw new Error("Belief not found.");
2955
- }
3406
+ const belief = assertBeliefNode(await ctx.db.get(args.beliefNodeId));
2956
3407
  await requireBeliefProjectAccess(ctx, belief, userId);
2957
3408
  return await evaluateContractsForTriggerBatch({
2958
3409
  ctx,
@@ -2975,10 +3426,7 @@ var processContractEvaluationOverflow = internalMutation({
2975
3426
  },
2976
3427
  returns: permissiveReturn,
2977
3428
  handler: async (ctx, args) => {
2978
- const belief = await ctx.db.get(args.beliefNodeId);
2979
- if (!belief || belief.nodeType !== "belief") {
2980
- throw new Error("Belief not found.");
2981
- }
3429
+ const belief = assertBeliefNode(await ctx.db.get(args.beliefNodeId));
2982
3430
  return await evaluateContractsForTriggerBatch({
2983
3431
  ctx,
2984
3432
  belief,
@@ -3017,15 +3465,9 @@ var getContractStatus = query({
3017
3465
  const contract = await getContractByContractId(ctx, args.contractId);
3018
3466
  contracts = contract ? [contract] : [];
3019
3467
  } else if (args.beliefNodeId) {
3020
- const belief = await ctx.db.get(args.beliefNodeId);
3021
- if (!belief || belief.nodeType !== "belief") {
3022
- throw new Error("Belief not found.");
3023
- }
3468
+ const belief = assertBeliefNode(await ctx.db.get(args.beliefNodeId));
3024
3469
  await requireBeliefProjectAccess(ctx, belief, userId);
3025
- contracts = await ctx.db.query("epistemicContracts").withIndex(
3026
- "by_belief",
3027
- (q) => q.eq("beliefNodeId", args.beliefNodeId)
3028
- ).collect();
3470
+ contracts = await contractTables(ctx).query("epistemicContracts").withIndex("by_belief", (q) => q.eq("beliefNodeId", args.beliefNodeId)).collect();
3029
3471
  }
3030
3472
  if (contracts.length > 0) {
3031
3473
  const contractBeliefDocs = await Promise.all(
@@ -3033,15 +3475,14 @@ var getContractStatus = query({
3033
3475
  );
3034
3476
  const contractBeliefs = [];
3035
3477
  for (const belief of contractBeliefDocs) {
3036
- if (!belief || belief.nodeType !== "belief") {
3037
- throw new Error("Belief not found.");
3038
- }
3039
- contractBeliefs.push(belief);
3478
+ contractBeliefs.push(assertBeliefNode(belief));
3040
3479
  }
3041
3480
  await requireTopicReadAccess(ctx, contractBeliefs, userId);
3042
3481
  }
3043
3482
  if (args.status) {
3044
- contracts = contracts.filter((contract) => contract.status === args.status);
3483
+ contracts = contracts.filter(
3484
+ (contract) => contract.status === args.status
3485
+ );
3045
3486
  }
3046
3487
  const rows = await Promise.all(
3047
3488
  contracts.map(async (contract) => ({
@@ -3060,21 +3501,22 @@ var getContractCoverage = query({
3060
3501
  },
3061
3502
  returns: permissiveReturn,
3062
3503
  handler: async (ctx, args) => {
3063
- const contracts = await ctx.db.query("epistemicContracts").withIndex("by_topic", (q) => q.eq("topicId", args.topicId)).collect();
3504
+ const contracts = await contractTables(ctx).query("epistemicContracts").withIndex("by_topic", (q) => q.eq("topicId", args.topicId)).collect();
3064
3505
  const userId = await resolveAuthenticatedUserId(
3065
3506
  ctx,
3066
3507
  args.authenticatedUserId
3067
3508
  );
3068
- const beliefs = await ctx.db.query("epistemicNodes").withIndex(
3509
+ const beliefRows = await ctx.db.query("epistemicNodes").withIndex(
3069
3510
  "by_topic_type",
3070
3511
  (q) => q.eq("topicId", args.topicId).eq("nodeType", "belief")
3071
3512
  ).collect();
3513
+ const beliefs = beliefRows.map((belief) => assertBeliefNode(belief));
3072
3514
  await requireTopicReadAccess(ctx, beliefs, userId);
3073
3515
  const recentEvaluationLimit = Math.max(
3074
3516
  0,
3075
3517
  Math.min(args.recentEvaluationLimit ?? 5, 25)
3076
3518
  );
3077
- const recentEvaluations = recentEvaluationLimit === 0 ? [] : await ctx.db.query("contractEvaluations").withIndex("by_topic_time", (q) => q.eq("topicId", args.topicId)).order("desc").take(recentEvaluationLimit) ?? [];
3519
+ const recentEvaluations = recentEvaluationLimit === 0 ? [] : await contractTables(ctx).query("contractEvaluations").withIndex("by_topic_time", (q) => q.eq("topicId", args.topicId)).order("desc").take(recentEvaluationLimit);
3078
3520
  const contractBeliefIds = new Set(
3079
3521
  contracts.filter((contract) => contract.status !== "archived").map((contract) => String(contract.beliefNodeId))
3080
3522
  );