@lucern/graph-primitives 1.0.29 → 1.0.31

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 +395 -225
  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 +854 -480
  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 +365 -166
  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 -289
  80. package/dist/epistemicBeliefs.backfills.js.map +1 -1
  81. package/dist/epistemicBeliefs.confidence.d.ts +19 -15
  82. package/dist/epistemicBeliefs.confidence.js +633 -386
  83. package/dist/epistemicBeliefs.confidence.js.map +1 -1
  84. package/dist/epistemicBeliefs.core.d.ts +6 -6
  85. package/dist/epistemicBeliefs.core.js +717 -371
  86. package/dist/epistemicBeliefs.core.js.map +1 -1
  87. package/dist/epistemicBeliefs.d.ts +11 -9
  88. package/dist/epistemicBeliefs.forkEvidence.d.ts +2 -0
  89. package/dist/epistemicBeliefs.forkEvidence.js +8 -8
  90. package/dist/epistemicBeliefs.forkEvidence.js.map +1 -1
  91. package/dist/epistemicBeliefs.helpers.d.ts +68 -49
  92. package/dist/epistemicBeliefs.helpers.js +358 -211
  93. package/dist/epistemicBeliefs.helpers.js.map +1 -1
  94. package/dist/epistemicBeliefs.internal.d.ts +5 -5
  95. package/dist/epistemicBeliefs.internal.js +1248 -1026
  96. package/dist/epistemicBeliefs.internal.js.map +1 -1
  97. package/dist/epistemicBeliefs.js +4942 -3590
  98. package/dist/epistemicBeliefs.js.map +1 -1
  99. package/dist/epistemicBeliefs.lifecycle.d.ts +5 -5
  100. package/dist/epistemicBeliefs.lifecycle.js +1138 -781
  101. package/dist/epistemicBeliefs.lifecycle.js.map +1 -1
  102. package/dist/epistemicBeliefs.links.d.ts +7 -7
  103. package/dist/epistemicBeliefs.links.js +404 -267
  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 +1062 -576
  114. package/dist/epistemicContracts.evaluators.js.map +1 -1
  115. package/dist/epistemicContracts.handlers.d.ts +15 -32
  116. package/dist/epistemicContracts.handlers.js +1829 -1351
  117. package/dist/epistemicContracts.handlers.js.map +1 -1
  118. package/dist/epistemicContracts.js +1131 -636
  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 +1966 -1202
  136. package/dist/epistemicEdges.js.map +1 -1
  137. package/dist/epistemicEdges.mutations.d.ts +7 -7
  138. package/dist/epistemicEdges.mutations.js +956 -579
  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 +933 -532
  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 +840 -692
  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 +700 -504
  180. package/dist/epistemicNodes.js.map +1 -1
  181. package/dist/epistemicNodes.mutations.d.ts +6 -6
  182. package/dist/epistemicNodes.mutations.js +560 -463
  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 +351 -311
  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 +86 -83
  245. package/dist/index.js +16914 -11760
  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,12 +1,16 @@
1
- import { v } from 'convex/values';
2
1
  import { checkProjectAccess } from '@lucern/access-control/access';
3
2
  import { permissiveReturn } from '@lucern/contracts/schema-helpers/validators';
4
- import { componentsGeneric, mutationGeneric, anyApi, queryGeneric } from 'convex/server';
3
+ import { v } from 'convex/values';
4
+ import { unsafeConvexAnyApi } from '@lucern/contracts/convex/unsafeAnyApi';
5
+ import { componentsGeneric, mutationGeneric, queryGeneric } from 'convex/server';
5
6
 
6
7
  // src/questionEvidenceLinks.ts
7
- var api = anyApi;
8
+ var unsafeApi = unsafeConvexAnyApi(
9
+ "graph-primitives top-level module bundle lacks a committed Convex _generated/api surface"
10
+ );
11
+ var api = unsafeApi;
8
12
  componentsGeneric();
9
- var internal = anyApi;
13
+ var internal = unsafeApi;
10
14
  var mutation = mutationGeneric;
11
15
  var query = queryGeneric;
12
16
 
@@ -72,6 +76,10 @@ function readStringArray(value) {
72
76
  function readMetadata(topic) {
73
77
  return topic.metadata && typeof topic.metadata === "object" ? topic.metadata : {};
74
78
  }
79
+ function omitMetadataKey(metadata, key) {
80
+ const { [key]: _omitted, ...rest } = metadata;
81
+ return rest;
82
+ }
75
83
  function readLegacyProjectId(value) {
76
84
  if (!value) {
77
85
  return;
@@ -152,9 +160,12 @@ async function resolveTopicDoc(ctx, scopeId) {
152
160
  );
153
161
  }
154
162
  try {
155
- const topic = await ctx.runQuery(api.topics.getByLegacyScopeId, {
156
- projectId: String(scopeId)
157
- });
163
+ const topic = await ctx.runQuery(
164
+ api.topics.getByLegacyScopeId,
165
+ {
166
+ projectId: String(scopeId)
167
+ }
168
+ );
158
169
  if (topic?.name !== void 0 && topic?.type !== void 0) {
159
170
  return topic;
160
171
  }
@@ -174,8 +185,18 @@ function materializeTopicProjectOverlay(topic, idMode = "legacy") {
174
185
  const outwardId = idMode === "topic" ? topicId : storageProjectId;
175
186
  const visibility = coerceVisibility(topic.visibility) || coerceVisibility(metadata.visibility) || "private";
176
187
  const status = coerceStatus(topic.status) || coerceStatus(metadata.status) || "active";
177
- const createdAt = typeof topic.createdAt === "number" ? topic.createdAt : typeof topic._creationTime === "number" ? topic._creationTime : 0;
178
- const updatedAt = typeof topic.updatedAt === "number" ? topic.updatedAt : typeof metadata.updatedAt === "number" ? metadata.updatedAt : createdAt;
188
+ let createdAt = 0;
189
+ if (typeof topic.createdAt === "number") {
190
+ createdAt = topic.createdAt;
191
+ } else if (typeof topic._creationTime === "number") {
192
+ createdAt = topic._creationTime;
193
+ }
194
+ let updatedAt = createdAt;
195
+ if (typeof topic.updatedAt === "number") {
196
+ updatedAt = topic.updatedAt;
197
+ } else if (typeof metadata.updatedAt === "number") {
198
+ updatedAt = metadata.updatedAt;
199
+ }
179
200
  return {
180
201
  ...metadata,
181
202
  _id: outwardId,
@@ -244,90 +265,113 @@ async function patchTopicProjectOverlay(ctx, scopeId, value) {
244
265
  if (!topic) {
245
266
  return null;
246
267
  }
247
- const nextMetadata = { ...readMetadata(topic) };
248
- const patch = {};
249
- const topicUpdateArgs = {
250
- id: String(topic._id)
268
+ const plan = buildTopicProjectOverlayPatchPlan(topic, value);
269
+ await applyTopicProjectOverlayPatch(ctx, topic, plan);
270
+ return materializeTopicProjectOverlay({
271
+ ...topic,
272
+ ...plan.patch,
273
+ metadata: plan.nextMetadata
274
+ });
275
+ }
276
+ function buildTopicProjectOverlayPatchPlan(topic, value) {
277
+ const plan = {
278
+ nextMetadata: { ...readMetadata(topic) },
279
+ patch: {},
280
+ topicUpdateArgs: {
281
+ id: String(topic._id)
282
+ }
251
283
  };
252
284
  for (const [key, rawValue] of Object.entries(value)) {
253
- switch (key) {
254
- case "_id":
255
- case "projectId":
256
- case "topicId":
257
- case "legacyProjectId":
258
- case "storageProjectId":
259
- break;
260
- case "name":
261
- case "description":
262
- patch[key] = rawValue;
263
- topicUpdateArgs[key] = rawValue;
264
- break;
265
- case "tenantId":
266
- case "workspaceId":
267
- case "ownerId":
268
- throw new Error(
269
- `patchTopicProjectOverlay cannot mutate ${key} via component-owned topics`
270
- );
271
- case "status": {
272
- const status = coerceStatus(rawValue);
273
- if (status) {
274
- patch.status = status;
275
- topicUpdateArgs.status = status;
276
- }
277
- break;
278
- }
279
- case "visibility": {
280
- const visibility = coerceVisibility(rawValue);
281
- if (visibility) {
282
- patch.visibility = visibility;
283
- topicUpdateArgs.visibility = visibility;
284
- }
285
- break;
286
- }
287
- case "type": {
288
- const projectType = readNonEmptyString(rawValue);
289
- if (projectType) {
290
- nextMetadata.projectType = projectType;
291
- } else {
292
- delete nextMetadata.projectType;
293
- }
294
- break;
295
- }
296
- case "updatedAt":
297
- case "createdAt":
298
- break;
299
- default:
300
- if (rawValue === void 0) {
301
- delete nextMetadata[key];
302
- } else {
303
- nextMetadata[key] = rawValue;
304
- }
305
- }
285
+ applyTopicProjectOverlayPatchEntry(plan, key, rawValue);
286
+ }
287
+ plan.patch.updatedAt = Date.now();
288
+ plan.patch.metadata = plan.nextMetadata;
289
+ plan.topicUpdateArgs.metadata = plan.nextMetadata;
290
+ return plan;
291
+ }
292
+ function applyTopicProjectOverlayPatchEntry(plan, key, rawValue) {
293
+ switch (key) {
294
+ case "_id":
295
+ case "projectId":
296
+ case "topicId":
297
+ case "legacyProjectId":
298
+ case "storageProjectId":
299
+ case "updatedAt":
300
+ case "createdAt":
301
+ return;
302
+ case "name":
303
+ case "description":
304
+ plan.patch[key] = rawValue;
305
+ plan.topicUpdateArgs[key] = rawValue;
306
+ return;
307
+ case "tenantId":
308
+ case "workspaceId":
309
+ case "ownerId":
310
+ throw new Error(
311
+ `patchTopicProjectOverlay cannot mutate ${key} via component-owned topics`
312
+ );
313
+ case "status":
314
+ applyTopicStatusPatch(plan, rawValue);
315
+ return;
316
+ case "visibility":
317
+ applyTopicVisibilityPatch(plan, rawValue);
318
+ return;
319
+ case "type":
320
+ applyTopicProjectTypePatch(plan, rawValue);
321
+ return;
322
+ default:
323
+ applyTopicMetadataPatch(plan, key, rawValue);
306
324
  }
307
- patch.updatedAt = Date.now();
308
- patch.metadata = nextMetadata;
309
- topicUpdateArgs.metadata = nextMetadata;
325
+ }
326
+ function applyTopicStatusPatch(plan, rawValue) {
327
+ const status = coerceStatus(rawValue);
328
+ if (status) {
329
+ plan.patch.status = status;
330
+ plan.topicUpdateArgs.status = status;
331
+ }
332
+ }
333
+ function applyTopicVisibilityPatch(plan, rawValue) {
334
+ const visibility = coerceVisibility(rawValue);
335
+ if (visibility) {
336
+ plan.patch.visibility = visibility;
337
+ plan.topicUpdateArgs.visibility = visibility;
338
+ }
339
+ }
340
+ function applyTopicProjectTypePatch(plan, rawValue) {
341
+ const projectType = readNonEmptyString(rawValue);
342
+ if (projectType) {
343
+ plan.nextMetadata.projectType = projectType;
344
+ return;
345
+ }
346
+ plan.nextMetadata = omitMetadataKey(plan.nextMetadata, "projectType");
347
+ }
348
+ function applyTopicMetadataPatch(plan, key, rawValue) {
349
+ if (rawValue === void 0) {
350
+ plan.nextMetadata = omitMetadataKey(plan.nextMetadata, key);
351
+ return;
352
+ }
353
+ plan.nextMetadata[key] = rawValue;
354
+ }
355
+ async function applyTopicProjectOverlayPatch(ctx, topic, plan) {
310
356
  if (typeof ctx.runMutation === "function") {
311
357
  try {
312
- await ctx.runMutation(api.topics.update, topicUpdateArgs);
358
+ await ctx.runMutation(api.topics.update, plan.topicUpdateArgs);
313
359
  } catch (error) {
314
- if (!isMissingLucernChildComponentError(error) || !ctx?.db || typeof ctx.db.patch !== "function") {
360
+ if (!canPatchTopicViaLocalDb(ctx, error)) {
315
361
  throw error;
316
362
  }
317
- await ctx.db.patch(String(topic._id), patch);
363
+ await ctx.db.patch(topic._id, plan.patch);
318
364
  }
319
365
  } else if (ctx?.db && typeof ctx.db.patch === "function") {
320
- await ctx.db.patch(String(topic._id), patch);
366
+ await ctx.db.patch(topic._id, plan.patch);
321
367
  } else {
322
368
  throw new Error(
323
369
  "Cannot patch topic without component adapter (ctx.runMutation unavailable)"
324
370
  );
325
371
  }
326
- return materializeTopicProjectOverlay({
327
- ...topic,
328
- ...patch,
329
- metadata: nextMetadata
330
- });
372
+ }
373
+ function canPatchTopicViaLocalDb(ctx, error) {
374
+ return isMissingLucernChildComponentError(error) && Boolean(ctx?.db) && typeof ctx.db?.patch === "function";
331
375
  }
332
376
 
333
377
  // src/resolvers.ts
@@ -355,7 +399,7 @@ async function patchProjectWithTolerance(ctx, projectId, value) {
355
399
  try {
356
400
  await patchTopicProjectOverlay(ctx, projectId, value);
357
401
  } catch (error) {
358
- if (!isAdvisoryTopicPatch(value) || !isMissingLucernChildComponentError2(error)) {
402
+ if (!(isAdvisoryTopicPatch(value) && isMissingLucernChildComponentError2(error))) {
359
403
  throw error;
360
404
  }
361
405
  console.warn(
@@ -422,13 +466,15 @@ function asMappedProjectId(topic) {
422
466
  if (!topic) {
423
467
  return;
424
468
  }
425
- const directLegacyProjectId = normalizeScopeValue(topic[LEGACY_SCOPE_FIELD2]);
469
+ const directLegacyProjectId = normalizeScopeValue(
470
+ topic[LEGACY_SCOPE_FIELD2]
471
+ );
426
472
  if (directLegacyProjectId) {
427
473
  return directLegacyProjectId;
428
474
  }
429
475
  const metadata = topic.metadata || {};
430
476
  const candidate = metadata[LEGACY_SCOPE_FIELD2] || metadata.legacyProjectId || metadata.projectId || metadata.scopeProjectId;
431
- return candidate ? candidate : void 0;
477
+ return typeof candidate === "string" ? normalizeScopeValue(candidate) : void 0;
432
478
  }
433
479
  function normalizeScopeValue(value) {
434
480
  if (typeof value !== "string") {
@@ -453,8 +499,9 @@ function pickPrimaryTopic(candidates) {
453
499
  })[0];
454
500
  }
455
501
  async function findTopicsByScopeAlias(ctx, scopeId) {
502
+ const query2 = ctx.db.query("topics");
456
503
  try {
457
- return await ctx.db.query("topics").withIndex(
504
+ return await query2.withIndex(
458
505
  "by_graph_scope_project",
459
506
  (q) => q.eq(LEGACY_SCOPE_FIELD2, scopeId)
460
507
  ).collect();
@@ -466,7 +513,7 @@ async function findTopicsByScopeAlias(ctx, scopeId) {
466
513
  scopeId
467
514
  }
468
515
  );
469
- const topics = await ctx.db.query("topics").collect();
516
+ const topics = await query2.collect();
470
517
  return topics.filter((topic) => {
471
518
  const normalizedGlobalId = normalizeScopeValue(topic.globalId);
472
519
  const mappedProjectId = asMappedProjectId(topic);
@@ -522,137 +569,115 @@ async function resolveInheritedWorkspaceScope(ctx, topic) {
522
569
  let current = topic;
523
570
  for (let i = 0; i < MAX_DEPTH && current?.parentTopicId; i++) {
524
571
  current = await ctx.db.get(current.parentTopicId);
525
- if (!current) break;
572
+ if (!current) {
573
+ break;
574
+ }
526
575
  if (!tenantId) {
527
576
  tenantId = normalizeScopeValue(current.tenantId);
528
577
  }
529
578
  if (!workspaceId) {
530
579
  workspaceId = normalizeScopeValue(current.workspaceId);
531
580
  }
532
- if (tenantId && workspaceId) break;
581
+ if (tenantId && workspaceId) {
582
+ break;
583
+ }
533
584
  }
534
585
  return { tenantId, workspaceId };
535
586
  }
536
587
  async function resolveTopicProjectScope(ctx, args) {
537
588
  if (args.topicId) {
538
- let topic = null;
539
- try {
540
- topic = await ctx.db.get(
541
- args.topicId
542
- );
543
- } catch (error) {
544
- debugGraphPrimitiveFallback(
545
- "[topicScope] Failed to load topic by direct id",
546
- {
547
- error,
548
- topicId: args.topicId
549
- }
550
- );
551
- }
552
- if (!topic) {
553
- topic = await tryResolveHostTopicById(ctx, String(args.topicId));
554
- }
555
- if (!topic) {
556
- topic = pickPrimaryTopic(
557
- await findTopicsByScopeAlias(ctx, String(args.topicId))
558
- ) ?? null;
559
- }
560
- if (!topic) {
561
- const nodeScope = await resolveTopicNodeScopeOrNull(
562
- ctx,
563
- String(args.topicId)
564
- );
565
- if (nodeScope) {
566
- return nodeScope;
567
- }
568
- throw new Error(`Topic not found: ${String(args.topicId)}`);
569
- }
570
- const inherited = await resolveInheritedWorkspaceScope(ctx, topic);
571
- const mapped = asMappedProjectId(topic);
572
- if (mapped) {
573
- return {
574
- topicId: topic._id,
575
- projectId: mapped,
576
- tenantId: inherited.tenantId,
577
- workspaceId: inherited.workspaceId,
578
- source: "topic"
579
- };
580
- }
581
- return {
582
- topicId: topic._id,
583
- tenantId: inherited.tenantId,
584
- workspaceId: inherited.workspaceId,
585
- source: "topic"
586
- };
589
+ return await resolveScopeFromTopicId(ctx, args.topicId);
587
590
  }
588
591
  if (args.projectId) {
589
- let directTopic = null;
590
- try {
591
- directTopic = await ctx.db.get(
592
- args.projectId
593
- );
594
- } catch (error) {
595
- debugGraphPrimitiveFallback(
596
- "[topicScope] Failed to load direct project topic",
597
- {
598
- error,
599
- projectId: args.projectId
600
- }
601
- );
602
- }
603
- if (directTopic) {
604
- const inherited = await resolveInheritedWorkspaceScope(ctx, directTopic);
605
- const mapped = asMappedProjectId(directTopic);
606
- return {
607
- topicId: directTopic._id,
608
- projectId: mapped ?? args.projectId,
609
- tenantId: inherited.tenantId,
610
- workspaceId: inherited.workspaceId,
611
- source: "topic_inferred"
612
- };
613
- }
614
- directTopic = await tryResolveHostTopicByLegacyScope(ctx, args.projectId);
615
- if (directTopic) {
616
- const inherited = await resolveInheritedWorkspaceScope(ctx, directTopic);
617
- const mapped = asMappedProjectId(directTopic);
618
- return {
619
- topicId: directTopic._id,
620
- projectId: mapped ?? args.projectId,
621
- tenantId: inherited.tenantId,
622
- workspaceId: inherited.workspaceId,
623
- source: "topic_inferred"
624
- };
625
- }
626
- const topics = await findTopicsByScopeAlias(ctx, args.projectId);
627
- const primary = pickPrimaryTopic(topics);
628
- if (primary) {
629
- const inherited = await resolveInheritedWorkspaceScope(ctx, primary);
630
- return {
631
- topicId: primary._id,
632
- projectId: args.projectId,
633
- tenantId: inherited.tenantId,
634
- workspaceId: inherited.workspaceId,
635
- source: "project_mapped_topic"
636
- };
637
- }
638
- const nodeScope = await resolveTopicNodeScopeOrNull(
639
- ctx,
640
- String(args.projectId)
641
- );
642
- if (nodeScope) {
643
- return {
644
- ...nodeScope,
645
- projectId: nodeScope.projectId ?? String(args.projectId)
646
- };
647
- }
648
- throw new Error(
649
- `Legacy project scope ${String(args.projectId)} has no mapped topic.`
650
- );
592
+ return await resolveScopeFromLegacyProjectId(ctx, args.projectId);
651
593
  }
652
594
  throw new Error(
653
595
  "Missing scope: provide topicId (preferred) or legacy projectId alias."
654
596
  );
655
597
  }
598
+ async function resolveScopeFromTopicId(ctx, topicId) {
599
+ const topic = await resolveTopicDocFromTopicId(ctx, topicId);
600
+ if (topic) {
601
+ return await buildTopicScope(ctx, topic, "topic");
602
+ }
603
+ const nodeScope = await resolveTopicNodeScopeOrNull(ctx, String(topicId));
604
+ if (nodeScope) {
605
+ return nodeScope;
606
+ }
607
+ throw new Error(`Topic not found: ${String(topicId)}`);
608
+ }
609
+ async function resolveTopicDocFromTopicId(ctx, topicId) {
610
+ const direct = await tryReadTopicDoc(ctx, topicId, {
611
+ failureLog: "[topicScope] Failed to load topic by direct id",
612
+ idLogKey: "topicId"
613
+ });
614
+ if (direct) {
615
+ return direct;
616
+ }
617
+ const hostTopic = await tryResolveHostTopicById(ctx, String(topicId));
618
+ if (hostTopic) {
619
+ return hostTopic;
620
+ }
621
+ return pickPrimaryTopic(await findTopicsByScopeAlias(ctx, String(topicId))) ?? null;
622
+ }
623
+ async function resolveScopeFromLegacyProjectId(ctx, legacyProjectId) {
624
+ const directTopic = await resolveDirectLegacyProjectTopic(
625
+ ctx,
626
+ legacyProjectId
627
+ );
628
+ if (directTopic) {
629
+ return await buildTopicScope(ctx, directTopic, "topic_inferred", {
630
+ fallbackProjectId: legacyProjectId
631
+ });
632
+ }
633
+ const primary = pickPrimaryTopic(
634
+ await findTopicsByScopeAlias(ctx, legacyProjectId)
635
+ );
636
+ if (primary) {
637
+ return await buildTopicScope(ctx, primary, "project_mapped_topic", {
638
+ fallbackProjectId: legacyProjectId
639
+ });
640
+ }
641
+ const nodeScope = await resolveTopicNodeScopeOrNull(ctx, legacyProjectId);
642
+ if (nodeScope) {
643
+ return {
644
+ ...nodeScope,
645
+ projectId: nodeScope.projectId ?? legacyProjectId
646
+ };
647
+ }
648
+ throw new Error(
649
+ `Legacy project scope ${legacyProjectId} has no mapped topic.`
650
+ );
651
+ }
652
+ async function resolveDirectLegacyProjectTopic(ctx, legacyProjectId) {
653
+ const directTopic = await tryReadTopicDoc(ctx, legacyProjectId, {
654
+ failureLog: "[topicScope] Failed to load direct project topic",
655
+ idLogKey: "projectId"
656
+ });
657
+ return directTopic ?? tryResolveHostTopicByLegacyScope(ctx, legacyProjectId);
658
+ }
659
+ async function tryReadTopicDoc(ctx, id, log) {
660
+ try {
661
+ return await ctx.db.get(id);
662
+ } catch (error) {
663
+ debugGraphPrimitiveFallback(log.failureLog, {
664
+ error,
665
+ [log.idLogKey]: id
666
+ });
667
+ return null;
668
+ }
669
+ }
670
+ async function buildTopicScope(ctx, topic, source, options = {}) {
671
+ const inherited = await resolveInheritedWorkspaceScope(ctx, topic);
672
+ const mapped = asMappedProjectId(topic);
673
+ return {
674
+ topicId: topic._id,
675
+ ...mapped || options.fallbackProjectId ? { projectId: mapped ?? options.fallbackProjectId } : {},
676
+ tenantId: inherited.tenantId,
677
+ workspaceId: inherited.workspaceId,
678
+ source
679
+ };
680
+ }
656
681
  var optionalScopeArgs = {
657
682
  projectId: v.optional(v.string()),
658
683
  topicId: v.optional(v.string())
@@ -684,6 +709,251 @@ var matcherMetadataValidator = v.object({
684
709
  signalSnapshot: v.optional(v.any()),
685
710
  outcomeMetadata: v.optional(v.any())
686
711
  });
712
+ function isRecord(value) {
713
+ return typeof value === "object" && value !== null && !Array.isArray(value);
714
+ }
715
+ function readConvexId(value) {
716
+ return typeof value === "string" && value.length > 0 ? value : null;
717
+ }
718
+ function readOptionalNumber(value) {
719
+ return typeof value === "number" && Number.isFinite(value) ? value : void 0;
720
+ }
721
+ function readRequiredNumber(value, fieldName) {
722
+ const numberValue = readOptionalNumber(value);
723
+ if (numberValue === void 0) {
724
+ throw new Error(`questionEvidenceLinks row has invalid ${fieldName}.`);
725
+ }
726
+ return numberValue;
727
+ }
728
+ function readOptionalString(value) {
729
+ return typeof value === "string" ? value : void 0;
730
+ }
731
+ function readStringArray2(value) {
732
+ return Array.isArray(value) ? value.filter((item) => typeof item === "string") : [];
733
+ }
734
+ function readLinkSuggestionStatus(value) {
735
+ if (value === void 0) {
736
+ return;
737
+ }
738
+ if (value === "suggested" || value === "approved" || value === "dismissed") {
739
+ return value;
740
+ }
741
+ throw new Error("questionEvidenceLinks row has invalid status.");
742
+ }
743
+ function readEvidenceImpact(value) {
744
+ if (value === void 0) {
745
+ return;
746
+ }
747
+ if (value === "supports" || value === "contradicts" || value === "neutral") {
748
+ return value;
749
+ }
750
+ throw new Error("questionEvidenceLinks row has invalid impact.");
751
+ }
752
+ function readMatcherReviewStatus(value) {
753
+ if (value === void 0) {
754
+ return;
755
+ }
756
+ if (value === "pending" || value === "accepted" || value === "rejected" || value === "auto_accepted" || value === "superseded") {
757
+ return value;
758
+ }
759
+ throw new Error("matcher metadata has invalid reviewStatus.");
760
+ }
761
+ function readMatcherMetadata(value) {
762
+ if (value === void 0) {
763
+ return;
764
+ }
765
+ if (!isRecord(value) || typeof value.surface !== "string") {
766
+ throw new Error("matcher metadata must include a string surface.");
767
+ }
768
+ return {
769
+ configSnapshot: value.configSnapshot,
770
+ matcherFamily: readOptionalString(value.matcherFamily),
771
+ matcherKey: readOptionalString(value.matcherKey),
772
+ matcherVersion: readOptionalString(value.matcherVersion),
773
+ outcomeMetadata: value.outcomeMetadata,
774
+ reviewStatus: readMatcherReviewStatus(value.reviewStatus),
775
+ signalSnapshot: value.signalSnapshot,
776
+ surface: value.surface
777
+ };
778
+ }
779
+ function readMetadata2(value) {
780
+ return isRecord(value) ? value : void 0;
781
+ }
782
+ function readEpistemicNode(value) {
783
+ if (!isRecord(value)) {
784
+ return null;
785
+ }
786
+ const id = readConvexId(value._id);
787
+ if (!id) {
788
+ return null;
789
+ }
790
+ return {
791
+ _creationTime: readOptionalNumber(value._creationTime),
792
+ _id: id,
793
+ canonicalText: readOptionalString(value.canonicalText),
794
+ createdAt: readOptionalNumber(value.createdAt),
795
+ globalId: readOptionalString(value.globalId),
796
+ metadata: readMetadata2(value.metadata),
797
+ nodeType: readOptionalString(value.nodeType),
798
+ projectId: readOptionalString(value.projectId),
799
+ status: readOptionalString(value.status),
800
+ title: readOptionalString(value.title),
801
+ topicId: readOptionalString(value.topicId),
802
+ verificationStatus: readOptionalString(value.verificationStatus)
803
+ };
804
+ }
805
+ function requireEpistemicNode(value, message) {
806
+ const node = readEpistemicNode(value);
807
+ if (!node) {
808
+ throw new Error(message);
809
+ }
810
+ return node;
811
+ }
812
+ function readQuestionEvidenceLink(value, context = "questionEvidenceLinks row") {
813
+ if (!isRecord(value)) {
814
+ throw new Error(`${context} must be an object.`);
815
+ }
816
+ const id = readConvexId(value._id);
817
+ const questionId = readConvexId(value.questionId);
818
+ const insightId = readConvexId(value.insightId);
819
+ const createdBy = readOptionalString(value.createdBy);
820
+ if (!(id && questionId && insightId && createdBy)) {
821
+ throw new Error(`${context} has invalid required identifiers.`);
822
+ }
823
+ if (typeof value.helpsAnswer !== "boolean") {
824
+ throw new Error(`${context} has invalid helpsAnswer.`);
825
+ }
826
+ return {
827
+ _id: id,
828
+ createdAt: readRequiredNumber(value.createdAt, "createdAt"),
829
+ createdBy,
830
+ helpsAnswer: value.helpsAnswer,
831
+ impact: readEvidenceImpact(value.impact),
832
+ impactScore: readOptionalNumber(value.impactScore),
833
+ insightId,
834
+ questionId,
835
+ rationale: readOptionalString(value.rationale),
836
+ relevance: readOptionalNumber(value.relevance),
837
+ status: readLinkSuggestionStatus(value.status)
838
+ };
839
+ }
840
+ function readQuestionEvidenceLinks(values) {
841
+ return values.map(
842
+ (value, index) => readQuestionEvidenceLink(value, `questionEvidenceLinks[${index}]`)
843
+ );
844
+ }
845
+ function readEpistemicEdge(value) {
846
+ if (!isRecord(value)) {
847
+ return null;
848
+ }
849
+ const id = readConvexId(value._id);
850
+ const createdAt = readOptionalNumber(value.createdAt);
851
+ if (!(id && createdAt !== void 0)) {
852
+ return null;
853
+ }
854
+ return {
855
+ _id: id,
856
+ context: readOptionalString(value.context),
857
+ createdAt,
858
+ createdBy: readOptionalString(value.createdBy),
859
+ fromGlobalId: readOptionalString(value.fromGlobalId),
860
+ fromNodeId: readConvexId(value.fromNodeId) ?? void 0,
861
+ globalId: readOptionalString(value.globalId),
862
+ weight: readOptionalNumber(value.weight)
863
+ };
864
+ }
865
+ async function fetchOperationalQuestionEvidenceLinks(ctx, questionId) {
866
+ const links = readQuestionEvidenceLinks(
867
+ await ctx.db.query("questionEvidenceLinks").withIndex("by_questionId", (q) => q.eq("questionId", questionId)).collect()
868
+ );
869
+ return await Promise.all(
870
+ links.filter((link) => isOperationalLinkStatus(link.status)).map(async (link) => {
871
+ const insight = readEpistemicNode(await ctx.db.get(link.insightId));
872
+ return {
873
+ ...link,
874
+ insight
875
+ };
876
+ })
877
+ );
878
+ }
879
+ function calculateEdgeImpact(edgeWeight) {
880
+ return edgeWeight == null ? 0 : Math.round(edgeWeight * 10);
881
+ }
882
+ async function buildEvidenceDetailFromEdge(ctx, edge, questionId, seenEvidenceIds) {
883
+ if (!edge.fromNodeId) {
884
+ return null;
885
+ }
886
+ const fromNodeId = String(edge.fromNodeId);
887
+ if (seenEvidenceIds.has(fromNodeId)) {
888
+ return null;
889
+ }
890
+ seenEvidenceIds.add(fromNodeId);
891
+ const evidenceNode = readEpistemicNode(await ctx.db.get(edge.fromNodeId));
892
+ if (evidenceNode?.nodeType !== "evidence" || evidenceNode.status !== "active") {
893
+ return null;
894
+ }
895
+ const meta = evidenceNode.metadata || {};
896
+ const impactScore = calculateEdgeImpact(edge.weight);
897
+ return {
898
+ _id: edge._id,
899
+ _creationTime: edge.createdAt,
900
+ questionId,
901
+ insightId: edge.fromNodeId,
902
+ impact: deriveImpact(impactScore, void 0),
903
+ impactScore,
904
+ rationale: edge.context ?? null,
905
+ userId: edge.createdBy,
906
+ createdAt: edge.createdAt,
907
+ insight: {
908
+ _id: evidenceNode._id,
909
+ _creationTime: evidenceNode.createdAt,
910
+ insight: evidenceNode.canonicalText || evidenceNode.title || readOptionalString(meta.snippet) || null,
911
+ text: evidenceNode.canonicalText || evidenceNode.title || readOptionalString(meta.snippet) || null,
912
+ title: evidenceNode.title ?? null,
913
+ canonicalText: evidenceNode.canonicalText ?? null,
914
+ source: readOptionalString(meta.sourceTitle) ?? null,
915
+ sourceUrl: readOptionalString(meta.sourceUrl) ?? null,
916
+ verificationStatus: evidenceNode.verificationStatus ?? "unverified"
917
+ }
918
+ };
919
+ }
920
+ async function fetchDerivedFromEdgesWithDetails(ctx, questionId, seenEvidenceIds) {
921
+ const edges = (await ctx.db.query("epistemicEdges").withIndex(
922
+ "by_to_type",
923
+ (q) => q.eq("toNodeId", questionId).eq("edgeType", "derived_from")
924
+ ).collect()).flatMap((edge) => {
925
+ const edgeRow = readEpistemicEdge(edge);
926
+ return edgeRow ? [edgeRow] : [];
927
+ });
928
+ const rows = [];
929
+ for (const edge of edges) {
930
+ const edgeRow = await buildEvidenceDetailFromEdge(
931
+ ctx,
932
+ edge,
933
+ questionId,
934
+ seenEvidenceIds
935
+ );
936
+ if (edgeRow) {
937
+ rows.push(edgeRow);
938
+ }
939
+ }
940
+ return rows;
941
+ }
942
+ function deriveImpact(impactScore, fallback) {
943
+ if (impactScore === void 0) {
944
+ return fallback;
945
+ }
946
+ if (impactScore > 0) {
947
+ return "supports";
948
+ }
949
+ if (impactScore < 0) {
950
+ return "contradicts";
951
+ }
952
+ return "neutral";
953
+ }
954
+ function readEpistemicMetadata(node) {
955
+ return node?.metadata ?? {};
956
+ }
687
957
  async function markProjectGraphDirty(ctx, projectId) {
688
958
  if (!projectId) {
689
959
  return;
@@ -724,8 +994,8 @@ async function recordMatcherDecision(ctx, args) {
724
994
  });
725
995
  }
726
996
  async function applyOperationalLinkEffects(ctx, args) {
727
- const meta = args.questionNode.metadata || {};
728
- const currentRelated = meta.relatedInsightIds || [];
997
+ const meta = readEpistemicMetadata(args.questionNode);
998
+ const currentRelated = readStringArray2(meta.relatedInsightIds);
729
999
  if (!currentRelated.includes(args.insightId)) {
730
1000
  await ctx.db.patch(args.questionId, {
731
1001
  updatedAt: Date.now(),
@@ -736,8 +1006,8 @@ async function applyOperationalLinkEffects(ctx, args) {
736
1006
  });
737
1007
  }
738
1008
  try {
739
- const questionNode = await ctx.db.get(args.questionId);
740
- const evidenceNode = await ctx.db.get(args.insightId);
1009
+ const questionNode = readEpistemicNode(await ctx.db.get(args.questionId));
1010
+ const evidenceNode = readEpistemicNode(await ctx.db.get(args.insightId));
741
1011
  if (questionNode && questionNode.nodeType === "question" && evidenceNode && evidenceNode.nodeType === "evidence" && questionNode.globalId && evidenceNode.globalId) {
742
1012
  const existingEdge = await ctx.db.query("epistemicEdges").withIndex(
743
1013
  "by_from_to",
@@ -751,7 +1021,7 @@ async function applyOperationalLinkEffects(ctx, args) {
751
1021
  toGlobalId: questionNode.globalId,
752
1022
  edgeType: "derived_from",
753
1023
  weight: args.relevance ?? 0.5,
754
- context: args.rationale || "Linked from question evidence links",
1024
+ context: args.rationale ?? "Linked from question evidence links",
755
1025
  projectId: args.questionNode.projectId ? String(args.questionNode.projectId) : void 0,
756
1026
  createdBy: args.createdBy,
757
1027
  fromNodeType: "evidence",
@@ -765,47 +1035,42 @@ async function applyOperationalLinkEffects(ctx, args) {
765
1035
  console.error("[EpistemicSpine] Failed to create derived_from edge:", e);
766
1036
  }
767
1037
  if (args.questionNode.projectId) {
768
- await ctx.scheduler.runAfter(
769
- 0,
770
- "verificationActions:deepVerifyEvidence",
771
- {
772
- insightId: args.insightId,
773
- targetType: "question",
774
- targetId: args.questionId,
775
- projectId: args.questionNode.projectId,
776
- userId: args.createdBy
777
- }
778
- );
1038
+ await ctx.scheduler.runAfter(0, "verificationActions:deepVerifyEvidence", {
1039
+ insightId: args.insightId,
1040
+ targetType: "question",
1041
+ targetId: args.questionId,
1042
+ projectId: args.questionNode.projectId,
1043
+ userId: args.createdBy
1044
+ });
779
1045
  }
780
1046
  }
781
1047
  async function removeOperationalLinkEffects(ctx, args) {
782
- const question = await ctx.db.get(args.questionId);
1048
+ const question = readEpistemicNode(await ctx.db.get(args.questionId));
783
1049
  if (question) {
784
- const meta = question.metadata || {};
785
- const currentRelated = meta.relatedInsightIds || [];
1050
+ const meta = readEpistemicMetadata(question);
1051
+ const currentRelated = readStringArray2(meta.relatedInsightIds);
786
1052
  await ctx.db.patch(args.questionId, {
787
1053
  updatedAt: Date.now(),
788
1054
  metadata: {
789
1055
  ...meta,
790
- relatedInsightIds: currentRelated.filter(
791
- (id) => id !== args.insightId
792
- )
1056
+ relatedInsightIds: currentRelated.filter((id) => id !== args.insightId)
793
1057
  }
794
1058
  });
795
1059
  }
796
1060
  try {
797
- const questionNode = await ctx.db.get(args.questionId);
798
- const evidenceNode = await ctx.db.get(args.insightId);
1061
+ const questionNode = readEpistemicNode(await ctx.db.get(args.questionId));
1062
+ const evidenceNode = readEpistemicNode(await ctx.db.get(args.insightId));
799
1063
  if (questionNode && questionNode.nodeType === "question" && evidenceNode && evidenceNode.nodeType === "evidence") {
800
1064
  const edge = await ctx.db.query("epistemicEdges").withIndex(
801
1065
  "by_from_to",
802
1066
  (q) => q.eq("fromNodeId", evidenceNode._id).eq("toNodeId", questionNode._id)
803
1067
  ).first();
804
- if (edge) {
1068
+ const edgeRow = readEpistemicEdge(edge);
1069
+ if (edgeRow?.globalId) {
805
1070
  await ctx.scheduler.runAfter(0, internal.neo4jEdgeAPI.deleteEdge, {
806
- globalId: edge.globalId
1071
+ globalId: edgeRow.globalId
807
1072
  });
808
- await ctx.db.delete(edge._id);
1073
+ await ctx.db.delete(edgeRow._id);
809
1074
  }
810
1075
  }
811
1076
  } catch (e) {
@@ -833,23 +1098,29 @@ var create = mutation({
833
1098
  },
834
1099
  returns: permissiveReturn,
835
1100
  handler: async (ctx, args) => {
836
- const question = await ctx.db.get(args.questionId);
837
- if (!question) {
1101
+ const matcherMetadata = readMatcherMetadata(args.matcherMetadata);
1102
+ const question = requireEpistemicNode(
1103
+ await ctx.db.get(args.questionId),
1104
+ "Question not found"
1105
+ );
1106
+ if (question.nodeType !== "question") {
838
1107
  throw new Error("Question not found");
839
1108
  }
840
- const insight = await ctx.db.get(args.insightId);
841
- if (!insight) {
1109
+ const insight = requireEpistemicNode(
1110
+ await ctx.db.get(args.insightId),
1111
+ "Insight not found"
1112
+ );
1113
+ if (insight.nodeType !== "evidence") {
842
1114
  throw new Error("Insight not found");
843
1115
  }
844
- const existingLinks = await ctx.db.query("questionEvidenceLinks").withIndex(
845
- "by_questionId",
846
- (q) => q.eq("questionId", args.questionId)
847
- ).collect();
1116
+ const existingLinks = readQuestionEvidenceLinks(
1117
+ await ctx.db.query("questionEvidenceLinks").withIndex("by_questionId", (q) => q.eq("questionId", args.questionId)).collect()
1118
+ );
848
1119
  const duplicate = existingLinks.find(
849
1120
  (link) => link.insightId === args.insightId
850
1121
  );
851
1122
  const impactScore = args.impactScore;
852
- const derivedImpact = impactScore !== void 0 ? impactScore > 0 ? "supports" : impactScore < 0 ? "contradicts" : "neutral" : args.impact;
1123
+ const derivedImpact = deriveImpact(impactScore, args.impact);
853
1124
  if (duplicate) {
854
1125
  const nextStatus = mergeLinkSuggestionStatus(
855
1126
  duplicate.status,
@@ -863,9 +1134,7 @@ var create = mutation({
863
1134
  impact: derivedImpact,
864
1135
  status: nextStatus
865
1136
  });
866
- if (isOperationalLinkStatus(nextStatus) && !isOperationalLinkStatus(
867
- duplicate.status
868
- )) {
1137
+ if (isOperationalLinkStatus(nextStatus) && !isOperationalLinkStatus(duplicate.status)) {
869
1138
  await applyOperationalLinkEffects(ctx, {
870
1139
  questionNode: question,
871
1140
  questionId: args.questionId,
@@ -881,7 +1150,7 @@ var create = mutation({
881
1150
  insightId: args.insightId,
882
1151
  linkId: duplicate._id,
883
1152
  linkStatus: nextStatus,
884
- matcherMetadata: args.matcherMetadata
1153
+ matcherMetadata
885
1154
  });
886
1155
  await markProjectGraphDirty(ctx, question.projectId);
887
1156
  return duplicate._id;
@@ -904,7 +1173,7 @@ var create = mutation({
904
1173
  insightId: args.insightId,
905
1174
  linkId,
906
1175
  linkStatus: args.status,
907
- matcherMetadata: args.matcherMetadata
1176
+ matcherMetadata
908
1177
  });
909
1178
  if (isOperationalLinkStatus(args.status)) {
910
1179
  await applyOperationalLinkEffects(ctx, {
@@ -935,12 +1204,13 @@ var updateImpact = mutation({
935
1204
  },
936
1205
  returns: permissiveReturn,
937
1206
  handler: async (ctx, args) => {
938
- const link = await ctx.db.get(args.linkId);
939
- if (!link) {
1207
+ const rawLink = await ctx.db.get(args.linkId);
1208
+ if (!rawLink) {
940
1209
  throw new Error("Link not found");
941
1210
  }
1211
+ const link = readQuestionEvidenceLink(rawLink, "updateImpact.link");
942
1212
  const newImpactScore = args.impactScore;
943
- const newImpact = newImpactScore !== void 0 ? newImpactScore > 0 ? "supports" : newImpactScore < 0 ? "contradicts" : "neutral" : args.impact;
1213
+ const newImpact = deriveImpact(newImpactScore, args.impact);
944
1214
  const updateData = {};
945
1215
  if (newImpact) {
946
1216
  updateData.impact = newImpact;
@@ -949,7 +1219,7 @@ var updateImpact = mutation({
949
1219
  updateData.impactScore = newImpactScore;
950
1220
  }
951
1221
  await ctx.db.patch(args.linkId, updateData);
952
- const question = await ctx.db.get(link.questionId);
1222
+ const question = readEpistemicNode(await ctx.db.get(link.questionId));
953
1223
  await markProjectGraphDirty(ctx, question?.projectId);
954
1224
  return { success: true, newImpact, newImpactScore };
955
1225
  }
@@ -963,11 +1233,12 @@ var reviewSuggestion = mutation({
963
1233
  },
964
1234
  returns: permissiveReturn,
965
1235
  handler: async (ctx, args) => {
966
- const link = await ctx.db.get(args.linkId);
967
- if (!link) {
1236
+ const rawLink = await ctx.db.get(args.linkId);
1237
+ if (!rawLink) {
968
1238
  throw new Error("Link not found");
969
1239
  }
970
- const question = await ctx.db.get(link.questionId);
1240
+ const link = readQuestionEvidenceLink(rawLink, "reviewSuggestion.link");
1241
+ const question = readEpistemicNode(await ctx.db.get(link.questionId));
971
1242
  if (!question) {
972
1243
  throw new Error("Question not found");
973
1244
  }
@@ -1024,14 +1295,15 @@ var remove = mutation({
1024
1295
  },
1025
1296
  returns: permissiveReturn,
1026
1297
  handler: async (ctx, args) => {
1027
- const link = await ctx.db.get(args.linkId);
1028
- if (!link) {
1298
+ const rawLink = await ctx.db.get(args.linkId);
1299
+ if (!rawLink) {
1029
1300
  throw new Error("Link not found");
1030
1301
  }
1302
+ const link = readQuestionEvidenceLink(rawLink, "remove.link");
1031
1303
  if (link.createdBy !== args.userId) {
1032
1304
  throw new Error("Only the creator can remove this link");
1033
1305
  }
1034
- const question = await ctx.db.get(link.questionId);
1306
+ const question = readEpistemicNode(await ctx.db.get(link.questionId));
1035
1307
  if (isOperationalLinkStatus(link.status)) {
1036
1308
  await removeOperationalLinkEffects(ctx, {
1037
1309
  questionId: link.questionId,
@@ -1048,13 +1320,10 @@ var getByQuestion = query({
1048
1320
  },
1049
1321
  returns: permissiveReturn,
1050
1322
  handler: async (ctx, args) => {
1051
- const links = await ctx.db.query("questionEvidenceLinks").withIndex(
1052
- "by_questionId",
1053
- (q) => q.eq("questionId", args.questionId)
1054
- ).collect();
1055
- return links.filter(
1056
- (link) => isOperationalLinkStatus(link.status)
1323
+ const links = readQuestionEvidenceLinks(
1324
+ await ctx.db.query("questionEvidenceLinks").withIndex("by_questionId", (q) => q.eq("questionId", args.questionId)).collect()
1057
1325
  );
1326
+ return links.filter((link) => isOperationalLinkStatus(link.status));
1058
1327
  }
1059
1328
  });
1060
1329
  var getPendingSuggestions = query({
@@ -1063,10 +1332,9 @@ var getPendingSuggestions = query({
1063
1332
  },
1064
1333
  returns: permissiveReturn,
1065
1334
  handler: async (ctx, args) => {
1066
- const links = await ctx.db.query("questionEvidenceLinks").withIndex(
1067
- "by_questionId",
1068
- (q) => q.eq("questionId", args.questionId)
1069
- ).collect();
1335
+ const links = readQuestionEvidenceLinks(
1336
+ await ctx.db.query("questionEvidenceLinks").withIndex("by_questionId", (q) => q.eq("questionId", args.questionId)).collect()
1337
+ );
1070
1338
  return links.filter((link) => link.status === "suggested");
1071
1339
  }
1072
1340
  });
@@ -1076,13 +1344,10 @@ var getByInsight = query({
1076
1344
  },
1077
1345
  returns: permissiveReturn,
1078
1346
  handler: async (ctx, args) => {
1079
- const links = await ctx.db.query("questionEvidenceLinks").withIndex(
1080
- "by_insightId",
1081
- (q) => q.eq("insightId", args.insightId)
1082
- ).collect();
1083
- return links.filter(
1084
- (link) => isOperationalLinkStatus(link.status)
1347
+ const links = readQuestionEvidenceLinks(
1348
+ await ctx.db.query("questionEvidenceLinks").withIndex("by_insightId", (q) => q.eq("insightId", args.insightId)).collect()
1085
1349
  );
1350
+ return links.filter((link) => isOperationalLinkStatus(link.status));
1086
1351
  }
1087
1352
  });
1088
1353
  var getEvidenceWithDetails = query({
@@ -1091,68 +1356,27 @@ var getEvidenceWithDetails = query({
1091
1356
  },
1092
1357
  returns: permissiveReturn,
1093
1358
  handler: async (ctx, args) => {
1094
- const questionNode = await ctx.db.get(args.questionId);
1095
- if (!questionNode || questionNode.nodeType !== "question") {
1359
+ const questionNode = readEpistemicNode(await ctx.db.get(args.questionId));
1360
+ if (questionNode?.nodeType !== "question") {
1096
1361
  return [];
1097
1362
  }
1098
- const links = await ctx.db.query("questionEvidenceLinks").withIndex(
1099
- "by_questionId",
1100
- (q) => q.eq("questionId", args.questionId)
1101
- ).collect();
1102
- const linksWithDetails = await Promise.all(
1103
- links.filter(
1104
- (link) => isOperationalLinkStatus(
1105
- link.status
1106
- )
1107
- ).map(async (link) => {
1108
- const insight = await ctx.db.get(link.insightId);
1109
- return {
1110
- ...link,
1111
- insight
1112
- };
1113
- })
1363
+ const linksWithInsight = await fetchOperationalQuestionEvidenceLinks(
1364
+ ctx,
1365
+ args.questionId
1114
1366
  );
1115
- const filtered = linksWithDetails.filter((l) => l.insight !== null);
1116
- const epistemicEdgeResults = [];
1117
- const seenEvidenceIds = new Set(filtered.map((l) => String(l.insightId)));
1118
- const edges = await ctx.db.query("epistemicEdges").withIndex(
1119
- "by_to_type",
1120
- (q) => q.eq("toNodeId", args.questionId).eq("edgeType", "derived_from")
1121
- ).collect();
1122
- for (const edge of edges) {
1123
- if (!edge.fromNodeId || seenEvidenceIds.has(String(edge.fromNodeId))) {
1124
- continue;
1125
- }
1126
- seenEvidenceIds.add(String(edge.fromNodeId));
1127
- const evidenceNode = await ctx.db.get(edge.fromNodeId);
1128
- if (!evidenceNode || evidenceNode.nodeType !== "evidence" || evidenceNode.status !== "active") {
1129
- continue;
1130
- }
1131
- const meta = evidenceNode.metadata || {};
1132
- epistemicEdgeResults.push({
1133
- _id: edge._id,
1134
- _creationTime: edge.createdAt,
1135
- questionId: args.questionId,
1136
- insightId: edge.fromNodeId,
1137
- impact: (edge.weight ?? 0) > 0 ? "supports" : (edge.weight ?? 0) < 0 ? "contradicts" : "neutral",
1138
- impactScore: edge.weight != null ? Math.round(edge.weight * 10) : 0,
1139
- rationale: edge.context || null,
1140
- userId: edge.createdBy,
1141
- createdAt: edge.createdAt,
1142
- insight: {
1143
- _id: evidenceNode._id,
1144
- _creationTime: evidenceNode.createdAt,
1145
- insight: evidenceNode.canonicalText || evidenceNode.title || meta.snippet || null,
1146
- text: evidenceNode.canonicalText || evidenceNode.title || meta.snippet || null,
1147
- title: evidenceNode.title || null,
1148
- canonicalText: evidenceNode.canonicalText || null,
1149
- source: meta.sourceTitle || null,
1150
- sourceUrl: meta.sourceUrl || null,
1151
- verificationStatus: evidenceNode.verificationStatus || "unverified"
1152
- }
1153
- });
1154
- }
1155
- return [...filtered, ...epistemicEdgeResults];
1367
+ const filteredLinks = linksWithInsight.filter(
1368
+ (link) => link.insight !== null
1369
+ );
1370
+ const seenEvidenceIds = new Set(
1371
+ filteredLinks.map((link) => String(link.insightId))
1372
+ );
1373
+ const edgeRows = await fetchDerivedFromEdgesWithDetails(
1374
+ ctx,
1375
+ args.questionId,
1376
+ seenEvidenceIds
1377
+ );
1378
+ const evidenceRows = [...filteredLinks, ...edgeRows];
1379
+ return evidenceRows;
1156
1380
  }
1157
1381
  });
1158
1382
  var getByProject = query({
@@ -1193,32 +1417,30 @@ var getByProject = query({
1193
1417
  }
1194
1418
  const pageSize = Math.max(1, Math.min(Math.floor(args.limit ?? 300), 1e3));
1195
1419
  const questionScanLimit = Math.min(pageSize * 2, 1e3);
1196
- const questions = await ctx.db.query("epistemicNodes").withIndex(
1420
+ const questions = (await ctx.db.query("epistemicNodes").withIndex(
1197
1421
  "by_topic_type",
1198
1422
  (q) => q.eq("topicId", scope.topicId).eq("nodeType", "question")
1199
- ).order("desc").take(questionScanLimit);
1423
+ ).order("desc").take(questionScanLimit)).flatMap((question) => {
1424
+ const row = readEpistemicNode(question);
1425
+ return row?.nodeType === "question" ? [row] : [];
1426
+ });
1200
1427
  const questionIds = questions.slice(0, pageSize).map((q) => q._id);
1201
1428
  const allLinks = await Promise.all(
1202
1429
  questionIds.map(async (questionId) => {
1203
- const links = await ctx.db.query("questionEvidenceLinks").withIndex(
1204
- "by_questionId",
1205
- (q) => q.eq("questionId", questionId)
1206
- ).collect();
1207
- return links.filter(
1208
- (link) => isOperationalLinkStatus(
1209
- link.status
1210
- )
1430
+ const links = readQuestionEvidenceLinks(
1431
+ await ctx.db.query("questionEvidenceLinks").withIndex("by_questionId", (q) => q.eq("questionId", questionId)).collect()
1211
1432
  );
1433
+ return links.filter((link) => isOperationalLinkStatus(link.status));
1212
1434
  })
1213
1435
  );
1214
1436
  const flattenedLinks = allLinks.flat().slice(0, pageSize * 5);
1215
1437
  const questionDetails = {};
1216
1438
  for (const question of questions) {
1217
1439
  const meta = question.metadata || {};
1218
- questionDetails[question._id] = {
1219
- _id: question._id,
1220
- question: question.canonicalText || question.title || "",
1221
- status: meta.status || question.status || "active"
1440
+ questionDetails[String(question._id)] = {
1441
+ _id: String(question._id),
1442
+ question: question.canonicalText ?? question.title ?? "",
1443
+ status: readOptionalString(meta.status) ?? question.status ?? "active"
1222
1444
  };
1223
1445
  }
1224
1446
  return {
@@ -1233,17 +1455,12 @@ var getLinkedQuestionsForInsight = query({
1233
1455
  },
1234
1456
  returns: permissiveReturn,
1235
1457
  handler: async (ctx, args) => {
1236
- const links = await ctx.db.query("questionEvidenceLinks").withIndex(
1237
- "by_insightId",
1238
- (q) => q.eq("insightId", args.insightId)
1239
- ).collect();
1458
+ const links = readQuestionEvidenceLinks(
1459
+ await ctx.db.query("questionEvidenceLinks").withIndex("by_insightId", (q) => q.eq("insightId", args.insightId)).collect()
1460
+ );
1240
1461
  const linkedQuestions = await Promise.all(
1241
- links.filter(
1242
- (link) => isOperationalLinkStatus(
1243
- link.status
1244
- )
1245
- ).map(async (link) => {
1246
- const question = await ctx.db.get(link.questionId);
1462
+ links.filter((link) => isOperationalLinkStatus(link.status)).map(async (link) => {
1463
+ const question = readEpistemicNode(await ctx.db.get(link.questionId));
1247
1464
  return {
1248
1465
  linkId: link._id,
1249
1466
  questionId: link.questionId,
@@ -1254,7 +1471,7 @@ var getLinkedQuestionsForInsight = query({
1254
1471
  createdAt: link.createdAt,
1255
1472
  question: question ? {
1256
1473
  _id: question._id,
1257
- question: question.question || question.canonicalText || "",
1474
+ question: question.canonicalText || question.title || "",
1258
1475
  status: question.status
1259
1476
  } : null
1260
1477
  };