@lucern/graph-primitives 0.3.0-alpha.9 → 1.0.1

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 (202) hide show
  1. package/README.md +13 -12
  2. package/dist/beliefDecay.js +24 -17
  3. package/dist/beliefDecay.js.map +1 -1
  4. package/dist/beliefEvidenceLinks.js +32 -8
  5. package/dist/beliefEvidenceLinks.js.map +1 -1
  6. package/dist/confidencePropagationDispatch.js.map +1 -1
  7. package/dist/contradictions.js +32 -9
  8. package/dist/contradictions.js.map +1 -1
  9. package/dist/convex.d.ts +55 -12
  10. package/dist/convex.js.map +1 -1
  11. package/dist/edgeValidation.d.ts +25 -2
  12. package/dist/edges/index.d.ts +9 -2
  13. package/dist/edges/index.js.map +1 -1
  14. package/dist/edges/propagationTypes.d.ts +2 -3
  15. package/dist/edges/propagationTypes.js.map +1 -1
  16. package/dist/entityBridge.js +10 -3
  17. package/dist/entityBridge.js.map +1 -1
  18. package/dist/entityLifecycle.js +15 -3
  19. package/dist/entityLifecycle.js.map +1 -1
  20. package/dist/epistemicAnswers.js.map +1 -1
  21. package/dist/epistemicBeliefs.admin.d.ts +36 -0
  22. package/dist/epistemicBeliefs.admin.js +745 -0
  23. package/dist/epistemicBeliefs.admin.js.map +1 -0
  24. package/dist/epistemicBeliefs.backfills.d.ts +62 -0
  25. package/dist/epistemicBeliefs.backfills.js +1004 -0
  26. package/dist/epistemicBeliefs.backfills.js.map +1 -0
  27. package/dist/epistemicBeliefs.confidence.d.ts +45 -0
  28. package/dist/epistemicBeliefs.confidence.js +1285 -0
  29. package/dist/epistemicBeliefs.confidence.js.map +1 -0
  30. package/dist/epistemicBeliefs.core.d.ts +35 -0
  31. package/dist/epistemicBeliefs.core.js +1508 -0
  32. package/dist/epistemicBeliefs.core.js.map +1 -0
  33. package/dist/epistemicBeliefs.d.ts +12 -3
  34. package/dist/epistemicBeliefs.helpers.d.ts +168 -0
  35. package/dist/epistemicBeliefs.helpers.js +1060 -0
  36. package/dist/epistemicBeliefs.helpers.js.map +1 -0
  37. package/dist/epistemicBeliefs.internal.d.ts +30 -0
  38. package/dist/epistemicBeliefs.internal.js +1329 -0
  39. package/dist/epistemicBeliefs.internal.js.map +1 -0
  40. package/dist/epistemicBeliefs.js +1196 -1184
  41. package/dist/epistemicBeliefs.js.map +1 -1
  42. package/dist/epistemicBeliefs.lifecycle.d.ts +19 -0
  43. package/dist/epistemicBeliefs.lifecycle.js +1608 -0
  44. package/dist/epistemicBeliefs.lifecycle.js.map +1 -0
  45. package/dist/epistemicBeliefs.links.d.ts +30 -0
  46. package/dist/epistemicBeliefs.links.js +761 -0
  47. package/dist/epistemicBeliefs.links.js.map +1 -0
  48. package/dist/epistemicBeliefs.queries.d.ts +16 -0
  49. package/dist/epistemicBeliefs.queries.js +90 -0
  50. package/dist/epistemicBeliefs.queries.js.map +1 -0
  51. package/dist/epistemicContractHelpers.d.ts +1 -1
  52. package/dist/epistemicContractHelpers.js +1 -1
  53. package/dist/epistemicContracts.d.ts +5 -76
  54. package/dist/epistemicContracts.evaluators.d.ts +36 -0
  55. package/dist/epistemicContracts.evaluators.js +2506 -0
  56. package/dist/epistemicContracts.evaluators.js.map +1 -0
  57. package/dist/epistemicContracts.handlers.d.ts +40 -0
  58. package/dist/epistemicContracts.handlers.js +3029 -0
  59. package/dist/epistemicContracts.handlers.js.map +1 -0
  60. package/dist/epistemicContracts.js +2006 -5281
  61. package/dist/epistemicContracts.js.map +1 -1
  62. package/dist/epistemicContracts.metrics.d.ts +26 -0
  63. package/dist/epistemicContracts.metrics.js +427 -0
  64. package/dist/epistemicContracts.metrics.js.map +1 -0
  65. package/dist/epistemicContracts.types.d.ts +159 -0
  66. package/dist/epistemicContracts.types.js +3 -0
  67. package/dist/epistemicContracts.types.js.map +1 -0
  68. package/dist/epistemicEdgeCreation.d.ts +73 -0
  69. package/dist/epistemicEdgeCreation.js +450 -0
  70. package/dist/epistemicEdgeCreation.js.map +1 -0
  71. package/dist/epistemicEdges-BF-cn4i3.d.ts +43 -0
  72. package/dist/epistemicEdges.d.ts +8 -1
  73. package/dist/epistemicEdges.handlers.d.ts +20 -0
  74. package/dist/epistemicEdges.handlers.js +289 -0
  75. package/dist/epistemicEdges.handlers.js.map +1 -0
  76. package/dist/epistemicEdges.helpers.d.ts +27 -0
  77. package/dist/epistemicEdges.helpers.js +162 -0
  78. package/dist/epistemicEdges.helpers.js.map +1 -0
  79. package/dist/epistemicEdges.js +797 -875
  80. package/dist/epistemicEdges.js.map +1 -1
  81. package/dist/epistemicEdges.mutations.d.ts +39 -0
  82. package/dist/epistemicEdges.mutations.js +1365 -0
  83. package/dist/epistemicEdges.mutations.js.map +1 -0
  84. package/dist/epistemicEdges.queries.d.ts +95 -0
  85. package/dist/epistemicEdges.queries.js +851 -0
  86. package/dist/epistemicEdges.queries.js.map +1 -0
  87. package/dist/epistemicEdges.types.d.ts +32 -0
  88. package/dist/epistemicEdges.types.js +3 -0
  89. package/dist/epistemicEdges.types.js.map +1 -0
  90. package/dist/epistemicEvidence-DvfchNt7.d.ts +46 -0
  91. package/dist/epistemicEvidence.d.ts +5 -2
  92. package/dist/epistemicEvidence.js +801 -807
  93. package/dist/epistemicEvidence.js.map +1 -1
  94. package/dist/epistemicEvidenceHelpers.d.ts +71 -0
  95. package/dist/epistemicEvidenceHelpers.js +769 -0
  96. package/dist/epistemicEvidenceHelpers.js.map +1 -0
  97. package/dist/epistemicEvidenceMutations.d.ts +10 -0
  98. package/dist/epistemicEvidenceMutations.js +1421 -0
  99. package/dist/epistemicEvidenceMutations.js.map +1 -0
  100. package/dist/epistemicEvidenceQueries.d.ts +10 -0
  101. package/dist/epistemicEvidenceQueries.js +1049 -0
  102. package/dist/epistemicEvidenceQueries.js.map +1 -0
  103. package/dist/epistemicHelpers.d.ts +4 -2
  104. package/dist/epistemicHelpers.js +132 -127
  105. package/dist/epistemicHelpers.js.map +1 -1
  106. package/dist/epistemicLayerRules.d.ts +138 -0
  107. package/dist/epistemicLayerRules.js +481 -0
  108. package/dist/epistemicLayerRules.js.map +1 -0
  109. package/dist/epistemicLinking.js +1 -1
  110. package/dist/epistemicLinking.js.map +1 -1
  111. package/dist/epistemicNodeCreation.d.ts +101 -0
  112. package/dist/epistemicNodeCreation.js +709 -0
  113. package/dist/epistemicNodeCreation.js.map +1 -0
  114. package/dist/epistemicNodes-BCQxpYx_.d.ts +54 -0
  115. package/dist/epistemicNodes.d.ts +5 -1
  116. package/dist/epistemicNodes.helpers.d.ts +51 -0
  117. package/dist/epistemicNodes.helpers.js +73 -0
  118. package/dist/epistemicNodes.helpers.js.map +1 -0
  119. package/dist/epistemicNodes.internal.d.ts +34 -0
  120. package/dist/epistemicNodes.internal.js +658 -0
  121. package/dist/epistemicNodes.internal.js.map +1 -0
  122. package/dist/epistemicNodes.js +698 -693
  123. package/dist/epistemicNodes.js.map +1 -1
  124. package/dist/epistemicNodes.mutations.d.ts +34 -0
  125. package/dist/epistemicNodes.mutations.js +1153 -0
  126. package/dist/epistemicNodes.mutations.js.map +1 -0
  127. package/dist/epistemicNodes.queries.d.ts +36 -0
  128. package/dist/epistemicNodes.queries.js +619 -0
  129. package/dist/epistemicNodes.queries.js.map +1 -0
  130. package/dist/epistemicNodes.validators.d.ts +23 -0
  131. package/dist/epistemicNodes.validators.js +105 -0
  132. package/dist/epistemicNodes.validators.js.map +1 -0
  133. package/dist/epistemicQuestions-bwHd2FWE.d.ts +68 -0
  134. package/dist/epistemicQuestions.conviction.d.ts +52 -0
  135. package/dist/epistemicQuestions.conviction.js +1389 -0
  136. package/dist/epistemicQuestions.conviction.js.map +1 -0
  137. package/dist/epistemicQuestions.create.d.ts +29 -0
  138. package/dist/epistemicQuestions.create.js +1300 -0
  139. package/dist/epistemicQuestions.create.js.map +1 -0
  140. package/dist/epistemicQuestions.d.ts +10 -2
  141. package/dist/epistemicQuestions.evidence.d.ts +22 -0
  142. package/dist/epistemicQuestions.evidence.js +929 -0
  143. package/dist/epistemicQuestions.evidence.js.map +1 -0
  144. package/dist/epistemicQuestions.helpers.d.ts +69 -0
  145. package/dist/epistemicQuestions.helpers.js +824 -0
  146. package/dist/epistemicQuestions.helpers.js.map +1 -0
  147. package/dist/epistemicQuestions.js +2435 -2430
  148. package/dist/epistemicQuestions.js.map +1 -1
  149. package/dist/epistemicQuestions.lifecycle.d.ts +24 -0
  150. package/dist/epistemicQuestions.lifecycle.js +838 -0
  151. package/dist/epistemicQuestions.lifecycle.js.map +1 -0
  152. package/dist/epistemicQuestions.queries.d.ts +41 -0
  153. package/dist/epistemicQuestions.queries.js +1013 -0
  154. package/dist/epistemicQuestions.queries.js.map +1 -0
  155. package/dist/epistemicQuestions.sprint.d.ts +22 -0
  156. package/dist/epistemicQuestions.sprint.js +757 -0
  157. package/dist/epistemicQuestions.sprint.js.map +1 -0
  158. package/dist/epistemicQuestions.tail.d.ts +42 -0
  159. package/dist/epistemicQuestions.tail.js +1345 -0
  160. package/dist/epistemicQuestions.tail.js.map +1 -0
  161. package/dist/epistemicSources.js +6 -2
  162. package/dist/epistemicSources.js.map +1 -1
  163. package/dist/evaluators/index.d.ts +2 -2
  164. package/dist/evaluators/index.js +45 -5320
  165. package/dist/evaluators/index.js.map +1 -1
  166. package/dist/evaluators/lintCheckerEvaluator.d.ts +1 -1
  167. package/dist/evaluators/sentryCheckerEvaluator.d.ts +1 -1
  168. package/dist/evaluators/testRunnerEvaluator.d.ts +1 -1
  169. package/dist/evaluators/tscCheckerEvaluator.d.ts +1 -1
  170. package/dist/{graphTypes-CpgIuCdo.d.ts → graphTypes-B8VaIjnl.d.ts} +1 -1
  171. package/dist/graphTypes.d.ts +1 -1
  172. package/dist/{helpers-BYHIk5vU.d.ts → helpers-DNYfg6mo.d.ts} +2 -3
  173. package/dist/helpers.d.ts +2 -2
  174. package/dist/helpers.js.map +1 -1
  175. package/dist/{index-Dq-7R-gi.d.ts → index-C-Kyd7hD.d.ts} +1 -1
  176. package/dist/index.d.ts +160 -14
  177. package/dist/index.js +12291 -13001
  178. package/dist/index.js.map +1 -1
  179. package/dist/logicalRoleInference.js.map +1 -1
  180. package/dist/ontologyApproval.js +1 -1
  181. package/dist/ontologyApproval.js.map +1 -1
  182. package/dist/ontologyDefinitions.js +25 -7
  183. package/dist/ontologyDefinitions.js.map +1 -1
  184. package/dist/ontologyRegistry.js.map +1 -1
  185. package/dist/projectionReconciliation.js.map +1 -1
  186. package/dist/questionEvidenceLinks.js +28 -7
  187. package/dist/questionEvidenceLinks.js.map +1 -1
  188. package/dist/resolvers.js.map +1 -1
  189. package/dist/scopeResolverCompat.js.map +1 -1
  190. package/dist/topicProjectOverlay.js.map +1 -1
  191. package/dist/topicScope.js.map +1 -1
  192. package/dist/workflowBridge.js.map +1 -1
  193. package/dist/workspaceIsolation.js.map +1 -1
  194. package/package.json +4 -5
  195. package/dist/edgeValidation-CeI0wc0r.d.ts +0 -35
  196. package/dist/epistemicBeliefs-DzKjZAeC.d.ts +0 -377
  197. package/dist/epistemicEdges-CvlKnEyy.d.ts +0 -191
  198. package/dist/epistemicEvidence-xw6UUrwh.d.ts +0 -128
  199. package/dist/epistemicHelpers-DevrYgPN.d.ts +0 -329
  200. package/dist/epistemicNodes-DjSUfvyD.d.ts +0 -167
  201. package/dist/epistemicQuestions-B_nUclrH.d.ts +0 -214
  202. package/dist/index-Dct1T70K.d.ts +0 -25
@@ -1,19 +1,12 @@
1
- import { v } from 'convex/values';
2
- import { requireProjectAccess, checkScopeAccess, checkProjectAccess } from '@lucern/access-control/access';
3
1
  import { canAudienceClassAccess, normalizeAudienceKey, classFromAudienceKey } from '@lucern/access-control/audience';
4
- import { listAudienceRegistryRows } from '@lucern/access-control/audienceRegistry';
2
+ import { componentsGeneric, anyApi, mutationGeneric, internalMutationGeneric, queryGeneric, internalQueryGeneric } from 'convex/server';
3
+ import { v } from 'convex/values';
4
+ import { requireProjectAccess, checkProjectAccess, checkScopeAccess } from '@lucern/access-control/access';
5
5
  import { permissiveReturn } from '@lucern/contracts/schema-helpers/validators';
6
- import { componentsGeneric, anyApi, mutationGeneric, queryGeneric, internalQueryGeneric, internalMutationGeneric } from 'convex/server';
7
6
  import { isNodeType, getLayerForNodeType } from '@lucern/contracts/schema-helpers/spine/tables/epistemicNodes';
7
+ import { listAudienceRegistryRows } from '@lucern/access-control/audienceRegistry';
8
8
 
9
- // src/epistemicEvidence.ts
10
- var api = anyApi;
11
- componentsGeneric();
12
- var internal = anyApi;
13
- var internalMutation = internalMutationGeneric;
14
- var internalQuery = internalQueryGeneric;
15
- var mutation = mutationGeneric;
16
- var query = queryGeneric;
9
+ // src/epistemicEvidenceHelpers.ts
17
10
 
18
11
  // src/debug.ts
19
12
  function isGraphPrimitiveDebugEnabled() {
@@ -26,47 +19,13 @@ function debugGraphPrimitiveFallback(message, context) {
26
19
  }
27
20
  console.debug(message, context ?? {});
28
21
  }
29
-
30
- // src/embeddingTrigger.ts
31
- async function scheduleEmbeddingGeneration(args) {
32
- try {
33
- await args.ctx.scheduler.runAfter(
34
- 0,
35
- "embeddingActions:generateEpistemicNodeEmbedding",
36
- {
37
- nodeId: args.nodeId,
38
- projectId: args.projectId ? String(args.projectId) : void 0,
39
- topicId: args.topicId ? String(args.topicId) : void 0,
40
- createdBy: args.createdBy,
41
- nodeType: args.nodeType,
42
- text: args.text.slice(0, 2e4),
43
- hasAnswer: args.hasAnswer,
44
- confidence: args.confidence
45
- }
46
- );
47
- } catch (error) {
48
- debugGraphPrimitiveFallback(
49
- "[embeddingTrigger] Failed to schedule embedding generation",
50
- {
51
- error,
52
- nodeId: String(args.nodeId),
53
- nodeType: args.nodeType
54
- }
55
- );
56
- }
57
- }
58
-
59
- // src/globalId.ts
60
- function generateGlobalId() {
61
- const bytes = new Uint8Array(16);
62
- crypto.getRandomValues(bytes);
63
- bytes[6] = bytes[6] & 15 | 64;
64
- bytes[8] = bytes[8] & 63 | 128;
65
- const hex = Array.from(bytes, (b) => b.toString(16).padStart(2, "0")).join(
66
- ""
67
- );
68
- return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20)}`;
69
- }
22
+ var api = anyApi;
23
+ componentsGeneric();
24
+ var internal = anyApi;
25
+ var internalMutation = internalMutationGeneric;
26
+ var internalQuery = internalQueryGeneric;
27
+ var mutation = mutationGeneric;
28
+ var query = queryGeneric;
70
29
 
71
30
  // src/topicProjectOverlay.ts
72
31
  var LEGACY_SCOPE_FIELD = "graphScopeProjectId";
@@ -617,101 +576,12 @@ var optionalScopeArgs = {
617
576
  projectId: v.optional(v.string()),
618
577
  topicId: v.optional(v.string())
619
578
  };
620
- function normalizeScopeValue2(value) {
621
- if (typeof value !== "string") {
622
- return;
623
- }
624
- const normalized = value.trim();
625
- return normalized.length > 0 ? normalized : void 0;
626
- }
627
- function throwWorkspaceIsolationError(args) {
628
- const error = new Error(args.message);
629
- error.status = 409;
630
- error.code = "INVARIANT_VIOLATION";
631
- error.invariantCode = args.invariantCode;
632
- error.suggestion = args.suggestion;
633
- error.details = args.details;
634
- throw error;
635
- }
636
- function assertWorkspaceScopedEpistemicNodeScope(args) {
637
- const layer = isNodeType(args.nodeType) ? getLayerForNodeType(args.nodeType) : void 0;
638
- if (layer === "ontological") {
639
- return;
640
- }
641
- const workspaceId = normalizeScopeValue2(args.scope.workspaceId);
642
- if (workspaceId) {
643
- return;
644
- }
645
- throwWorkspaceIsolationError({
646
- message: "Workspace-scoped reasoning isolation requires workspaceId on non-ontological node creation.",
647
- invariantCode: "workspace.scope_required_for_epistemic_nodes",
648
- suggestion: "Resolve the topic/project scope through a workspace-bound topic before creating epistemic nodes.",
649
- details: {
650
- mutationName: args.mutationName,
651
- nodeType: args.nodeType,
652
- topicId: args.scope.topicId,
653
- projectId: args.scope.projectId
654
- }
655
- });
656
- }
657
- function nodeMatchesWorkspaceReasoningScope(node, scope) {
658
- if (!node) {
659
- return false;
660
- }
661
- const scopeTenantId = normalizeScopeValue2(scope.tenantId);
662
- const scopeWorkspaceId = normalizeScopeValue2(scope.workspaceId);
663
- const nodeTenantId = normalizeScopeValue2(node.tenantId);
664
- const nodeWorkspaceId = normalizeScopeValue2(node.workspaceId);
665
- const epistemicLayer = typeof node.epistemicLayer === "string" ? node.epistemicLayer : void 0;
666
- if (scopeTenantId && nodeTenantId && scopeTenantId !== nodeTenantId) {
667
- return false;
668
- }
669
- if (epistemicLayer === "ontological" && nodeWorkspaceId === void 0) {
670
- return true;
671
- }
672
- if (!scopeWorkspaceId && node.publicationStatus === "published") {
673
- return true;
674
- }
675
- if (!scopeWorkspaceId) {
676
- return nodeWorkspaceId === void 0;
677
- }
678
- return scopeWorkspaceId === nodeWorkspaceId;
679
- }
680
- function resolveRuntimePackMutationContext(args) {
681
- if (!args.runtimeToolName && !args.runtimePackKey && !args.runtimePackInstallScope) {
682
- return;
683
- }
684
- return {
685
- toolName: args.runtimeToolName,
686
- packKey: args.runtimePackKey,
687
- packInstallScope: args.runtimePackInstallScope
688
- };
689
- }
690
- function assertTenantPackWorkspaceMutationAllowed(args) {
691
- if (!args.runtime?.packKey || args.runtime.packInstallScope !== "tenant") {
692
- return;
693
- }
694
- const targetWorkspaceId = normalizeScopeValue2(args.target.workspaceId);
695
- const targetLayer = typeof args.target.epistemicLayer === "string" ? args.target.epistemicLayer : void 0;
696
- if (!targetWorkspaceId || targetLayer === "ontological") {
697
- return;
698
- }
699
- throwWorkspaceIsolationError({
700
- message: `Tenant-scoped pack "${args.runtime.packKey}" cannot mutate workspace-scoped reasoning state.`,
701
- invariantCode: "workspace.tenant_pack_reasoning_write_forbidden",
702
- suggestion: "Use a workspace-scoped pack for workspace-local graph mutations, or route the change through tenant-global canonical entity flows.",
703
- details: {
704
- mutationName: args.mutationName,
705
- toolName: args.runtime.toolName,
706
- packKey: args.runtime.packKey,
707
- targetWorkspaceId,
708
- targetNodeType: args.target.nodeType,
709
- targetLayer
710
- }
711
- });
712
- }
713
579
 
714
- // src/epistemicEvidence.ts
580
+ // src/epistemicEvidenceHelpers.ts
581
+ var optionalEvidenceScopeArgs = optionalScopeArgs;
582
+ var DEFAULT_EVIDENCE_PAGE_SIZE = 250;
583
+ var MAX_EVIDENCE_PAGE_SIZE = 1e3;
584
+ var LEGACY_SPRINT_LINK_KEY = "linkedSprintId";
715
585
  function generateContentHash(text) {
716
586
  const content = `evidence:${text.trim().toLowerCase().replace(/\s+/g, " ").slice(0, 500)}`;
717
587
  let hash = 5381;
@@ -773,10 +643,6 @@ async function markProjectGraphDirty(ctx, projectId, topicId) {
773
643
  }
774
644
  );
775
645
  }
776
- var DEFAULT_EVIDENCE_PAGE_SIZE = 250;
777
- var MAX_EVIDENCE_PAGE_SIZE = 1e3;
778
- var LEGACY_SPRINT_LINK_KEY = "linkedSprintId";
779
- var optionalEvidenceScopeArgs = optionalScopeArgs;
780
646
  function clampEvidenceLimit(limit, fallback = DEFAULT_EVIDENCE_PAGE_SIZE) {
781
647
  if (!Number.isFinite(limit)) {
782
648
  return fallback;
@@ -839,7 +705,7 @@ async function getEvidenceNodesForScope(ctx, scope, args) {
839
705
  ).order("desc").take(scanLimit) : ctx.db.query("epistemicNodes").withIndex(
840
706
  "by_topic_type",
841
707
  (q) => q.eq("topicId", scope.topicId).eq("nodeType", "evidence")
842
- ).collect());
708
+ ).order("desc").collect());
843
709
  return dedupeEvidenceNodes(topicNodes).filter(
844
710
  (node) => evidenceMatchesScope(node, scope)
845
711
  );
@@ -877,111 +743,572 @@ function flattenEvidenceNode(n) {
877
743
  externalSourceType: meta.externalSourceType || void 0
878
744
  };
879
745
  }
880
- var create = mutation({
881
- args: {
882
- ...optionalEvidenceScopeArgs,
883
- text: v.string(),
884
- title: v.optional(v.string()),
885
- content: v.optional(v.string()),
886
- contentType: v.optional(v.string()),
887
- kind: v.optional(v.string()),
888
- tags: v.optional(v.array(v.string())),
889
- sourceType: v.optional(v.string()),
890
- externalSourceType: v.optional(v.string()),
891
- sourceUrl: v.optional(v.string()),
892
- sourceQuestionId: v.optional(v.string()),
893
- userId: v.string(),
894
- rationale: v.string(),
895
- // Classification fields (from AI tools)
896
- methodology: v.optional(v.string()),
897
- informationAsymmetry: v.optional(v.string()),
898
- sourceDescription: v.optional(v.string()),
899
- metadata: v.optional(v.any()),
900
- // Optional linking to beliefs
901
- linkedBeliefNodeId: v.optional(v.id("epistemicNodes")),
902
- evidenceRelation: v.optional(
903
- v.union(v.literal("supports"), v.literal("contradicts"))
904
- ),
905
- confidence: v.optional(v.number())
906
- },
907
- returns: permissiveReturn,
908
- handler: async (ctx, args) => {
909
- const scope = await resolveTopicProjectScope(ctx, {
910
- topicId: args.topicId,
911
- projectId: args.projectId
912
- });
913
- assertWorkspaceScopedEpistemicNodeScope({
914
- scope,
915
- nodeType: "evidence",
916
- mutationName: "epistemicEvidence.create"
917
- });
918
- if (scope.projectId) {
919
- await requireProjectAccess(ctx, scope.projectId, args.userId);
920
- }
921
- const now = Date.now();
922
- const globalId = generateGlobalId();
923
- const contentHash = generateContentHash(args.text);
924
- const kind = normalizeKind(args.kind);
925
- const sourceType = normalizeSourceType(args.sourceType);
926
- const additionalMetadata = args.metadata && typeof args.metadata === "object" ? args.metadata : {};
927
- const nodeId = await ctx.db.insert("epistemicNodes", {
928
- globalId,
929
- topicId: scope.topicId,
930
- projectId: scope.projectId,
931
- tenantId: scope.tenantId,
932
- workspaceId: scope.workspaceId,
933
- nodeType: "evidence",
934
- canonicalText: args.text,
935
- contentHash,
936
- ...typeof args.title === "string" && args.title.trim().length > 0 ? { title: args.title.trim() } : {},
937
- ...typeof args.content === "string" && args.content.length > 0 ? { content: args.content } : {},
938
- ...typeof args.contentType === "string" && args.contentType.trim().length > 0 ? { contentType: args.contentType.trim() } : {},
939
- status: "active",
940
- epistemicLayer: "L2",
941
- // L2: Compression Boundary (Evidence/Claims)
942
- sourceType,
943
- createdAt: now,
944
- updatedAt: now,
945
- createdBy: args.userId,
946
- metadata: {
947
- kind,
948
- tags: args.tags || [],
949
- externalSourceType: args.externalSourceType,
950
- sourceUrl: args.sourceUrl,
951
- sourceQuestionId: args.sourceQuestionId,
952
- rationale: args.rationale,
953
- linkedBeliefNodeId: args.linkedBeliefNodeId,
954
- evidenceRelation: args.evidenceRelation,
955
- confidence: args.confidence,
956
- methodology: args.methodology,
957
- informationAsymmetry: args.informationAsymmetry,
958
- sourceDescription: args.sourceDescription,
959
- ...additionalMetadata
960
- }
961
- });
962
- await ctx.scheduler.runAfter(0, internal.neo4jSync.syncNodeToNeo4j, {
963
- nodeId,
964
- operation: "upsert"
965
- });
966
- await scheduleEmbeddingGeneration({
967
- ctx,
968
- nodeId,
969
- projectId: scope.projectId,
970
- topicId: scope.topicId,
971
- createdBy: args.userId,
972
- nodeType: "evidence",
973
- text: args.text
974
- });
975
- if (args.linkedBeliefNodeId && args.evidenceRelation) {
976
- const beliefNode = await ctx.db.get(args.linkedBeliefNodeId);
746
+ function formatEvidenceNode(n) {
747
+ const metadata = n.metadata || {};
748
+ const linkedWorktreeId = resolveEvidenceLinkedWorktreeId(metadata);
749
+ return {
750
+ _id: n._id,
751
+ _epistemicNodeId: n._id,
752
+ _creationTime: n.createdAt,
753
+ projectId: n.projectId,
754
+ topicId: n.topicId,
755
+ text: n.canonicalText,
756
+ kind: metadata.kind || "observation",
757
+ tags: metadata.tags || [],
758
+ sourceType: n.sourceType,
759
+ externalSourceType: metadata.externalSourceType,
760
+ externalSourceUrl: metadata.sourceUrl,
761
+ sourceArtifactId: metadata.sourceArtifactId,
762
+ sourceQuestionId: metadata.sourceQuestionId,
763
+ linkedWorktreeId,
764
+ [LEGACY_SPRINT_LINK_KEY]: metadata[LEGACY_SPRINT_LINK_KEY] || void 0,
765
+ sourceAnchor: metadata.sourceAnchor,
766
+ aiProvider: metadata.aiProvider,
767
+ verificationStatus: metadata.verificationStatus,
768
+ status: n.status,
769
+ createdBy: n.createdBy,
770
+ createdAt: n.createdAt,
771
+ updatedAt: n.updatedAt
772
+ };
773
+ }
774
+
775
+ // src/embeddingTrigger.ts
776
+ async function scheduleEmbeddingGeneration(args) {
777
+ try {
778
+ await args.ctx.scheduler.runAfter(
779
+ 0,
780
+ "embeddingActions:generateEpistemicNodeEmbedding",
781
+ {
782
+ nodeId: args.nodeId,
783
+ projectId: args.projectId ? String(args.projectId) : void 0,
784
+ topicId: args.topicId ? String(args.topicId) : void 0,
785
+ createdBy: args.createdBy,
786
+ nodeType: args.nodeType,
787
+ text: args.text.slice(0, 2e4),
788
+ hasAnswer: args.hasAnswer,
789
+ confidence: args.confidence
790
+ }
791
+ );
792
+ } catch (error) {
793
+ debugGraphPrimitiveFallback(
794
+ "[embeddingTrigger] Failed to schedule embedding generation",
795
+ {
796
+ error,
797
+ nodeId: String(args.nodeId),
798
+ nodeType: args.nodeType
799
+ }
800
+ );
801
+ }
802
+ }
803
+
804
+ // src/globalId.ts
805
+ function generateGlobalId() {
806
+ const bytes = new Uint8Array(16);
807
+ crypto.getRandomValues(bytes);
808
+ bytes[6] = bytes[6] & 15 | 64;
809
+ bytes[8] = bytes[8] & 63 | 128;
810
+ const hex = Array.from(bytes, (b) => b.toString(16).padStart(2, "0")).join(
811
+ ""
812
+ );
813
+ return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20)}`;
814
+ }
815
+ function normalizeScopeValue2(value) {
816
+ if (typeof value !== "string") {
817
+ return;
818
+ }
819
+ const normalized = value.trim();
820
+ return normalized.length > 0 ? normalized : void 0;
821
+ }
822
+ function throwWorkspaceIsolationError(args) {
823
+ const error = new Error(args.message);
824
+ error.status = 409;
825
+ error.code = "INVARIANT_VIOLATION";
826
+ error.invariantCode = args.invariantCode;
827
+ error.suggestion = args.suggestion;
828
+ error.details = args.details;
829
+ throw error;
830
+ }
831
+ function assertWorkspaceScopedEpistemicNodeScope(args) {
832
+ const layer = isNodeType(args.nodeType) ? getLayerForNodeType(args.nodeType) : void 0;
833
+ if (layer === "ontological") {
834
+ return;
835
+ }
836
+ const workspaceId = normalizeScopeValue2(args.scope.workspaceId);
837
+ if (workspaceId) {
838
+ return;
839
+ }
840
+ throwWorkspaceIsolationError({
841
+ message: "Workspace-scoped reasoning isolation requires workspaceId on non-ontological node creation.",
842
+ invariantCode: "workspace.scope_required_for_epistemic_nodes",
843
+ suggestion: "Resolve the topic/project scope through a workspace-bound topic before creating epistemic nodes.",
844
+ details: {
845
+ mutationName: args.mutationName,
846
+ nodeType: args.nodeType,
847
+ topicId: args.scope.topicId,
848
+ projectId: args.scope.projectId
849
+ }
850
+ });
851
+ }
852
+ function nodeMatchesWorkspaceReasoningScope(node, scope) {
853
+ if (!node) {
854
+ return false;
855
+ }
856
+ const scopeTenantId = normalizeScopeValue2(scope.tenantId);
857
+ const scopeWorkspaceId = normalizeScopeValue2(scope.workspaceId);
858
+ const nodeTenantId = normalizeScopeValue2(node.tenantId);
859
+ const nodeWorkspaceId = normalizeScopeValue2(node.workspaceId);
860
+ const epistemicLayer = typeof node.epistemicLayer === "string" ? node.epistemicLayer : void 0;
861
+ if (scopeTenantId && nodeTenantId && scopeTenantId !== nodeTenantId) {
862
+ return false;
863
+ }
864
+ if (epistemicLayer === "ontological" && nodeWorkspaceId === void 0) {
865
+ return true;
866
+ }
867
+ if (!scopeWorkspaceId && node.publicationStatus === "published") {
868
+ return true;
869
+ }
870
+ if (!scopeWorkspaceId) {
871
+ return nodeWorkspaceId === void 0;
872
+ }
873
+ return scopeWorkspaceId === nodeWorkspaceId;
874
+ }
875
+ function resolveRuntimePackMutationContext(args) {
876
+ if (!args.runtimeToolName && !args.runtimePackKey && !args.runtimePackInstallScope) {
877
+ return;
878
+ }
879
+ return {
880
+ toolName: args.runtimeToolName,
881
+ packKey: args.runtimePackKey,
882
+ packInstallScope: args.runtimePackInstallScope
883
+ };
884
+ }
885
+ function assertTenantPackWorkspaceMutationAllowed(args) {
886
+ if (!args.runtime?.packKey || args.runtime.packInstallScope !== "tenant") {
887
+ return;
888
+ }
889
+ const targetWorkspaceId = normalizeScopeValue2(args.target.workspaceId);
890
+ const targetLayer = typeof args.target.epistemicLayer === "string" ? args.target.epistemicLayer : void 0;
891
+ if (!targetWorkspaceId || targetLayer === "ontological") {
892
+ return;
893
+ }
894
+ throwWorkspaceIsolationError({
895
+ message: `Tenant-scoped pack "${args.runtime.packKey}" cannot mutate workspace-scoped reasoning state.`,
896
+ invariantCode: "workspace.tenant_pack_reasoning_write_forbidden",
897
+ suggestion: "Use a workspace-scoped pack for workspace-local graph mutations, or route the change through tenant-global canonical entity flows.",
898
+ details: {
899
+ mutationName: args.mutationName,
900
+ toolName: args.runtime.toolName,
901
+ packKey: args.runtime.packKey,
902
+ targetWorkspaceId,
903
+ targetNodeType: args.target.nodeType,
904
+ targetLayer
905
+ }
906
+ });
907
+ }
908
+
909
+ // src/epistemicEvidenceMutations.ts
910
+ var create = mutation({
911
+ args: {
912
+ ...optionalEvidenceScopeArgs,
913
+ text: v.string(),
914
+ title: v.optional(v.string()),
915
+ content: v.optional(v.string()),
916
+ contentType: v.optional(v.string()),
917
+ kind: v.optional(v.string()),
918
+ tags: v.optional(v.array(v.string())),
919
+ sourceType: v.optional(v.string()),
920
+ externalSourceType: v.optional(v.string()),
921
+ sourceUrl: v.optional(v.string()),
922
+ sourceQuestionId: v.optional(v.string()),
923
+ userId: v.string(),
924
+ rationale: v.string(),
925
+ // Classification fields (from AI tools)
926
+ methodology: v.optional(v.string()),
927
+ informationAsymmetry: v.optional(v.string()),
928
+ sourceDescription: v.optional(v.string()),
929
+ metadata: v.optional(v.any()),
930
+ // Optional linking to beliefs
931
+ linkedBeliefNodeId: v.optional(v.id("epistemicNodes")),
932
+ evidenceRelation: v.optional(
933
+ v.union(v.literal("supports"), v.literal("contradicts"))
934
+ ),
935
+ confidence: v.optional(v.number())
936
+ },
937
+ returns: permissiveReturn,
938
+ handler: async (ctx, args) => {
939
+ const scope = await resolveEvidenceScopeOrNull(ctx, args);
940
+ if (!scope) {
941
+ throw new Error("Invalid scope: projectId or topicId is required");
942
+ }
943
+ assertWorkspaceScopedEpistemicNodeScope({
944
+ scope,
945
+ nodeType: "evidence",
946
+ mutationName: "epistemicEvidence.create"
947
+ });
948
+ if (scope.projectId) {
949
+ await requireProjectAccess(ctx, scope.projectId, args.userId);
950
+ }
951
+ const now = Date.now();
952
+ const globalId = generateGlobalId();
953
+ const contentHash = generateContentHash(args.text);
954
+ const kind = normalizeKind(args.kind);
955
+ const sourceType = normalizeSourceType(args.sourceType);
956
+ const additionalMetadata = args.metadata && typeof args.metadata === "object" ? args.metadata : {};
957
+ const nodeId = await ctx.db.insert("epistemicNodes", {
958
+ globalId,
959
+ topicId: scope.topicId,
960
+ projectId: scope.projectId,
961
+ tenantId: scope.tenantId,
962
+ workspaceId: scope.workspaceId,
963
+ nodeType: "evidence",
964
+ canonicalText: args.text,
965
+ contentHash,
966
+ ...typeof args.title === "string" && args.title.trim().length > 0 ? { title: args.title.trim() } : {},
967
+ ...typeof args.content === "string" && args.content.length > 0 ? { content: args.content } : {},
968
+ ...typeof args.contentType === "string" && args.contentType.trim().length > 0 ? { contentType: args.contentType.trim() } : {},
969
+ status: "active",
970
+ epistemicLayer: "L2",
971
+ sourceType,
972
+ createdAt: now,
973
+ updatedAt: now,
974
+ createdBy: args.userId,
975
+ metadata: {
976
+ kind,
977
+ tags: args.tags || [],
978
+ externalSourceType: args.externalSourceType,
979
+ sourceUrl: args.sourceUrl,
980
+ sourceQuestionId: args.sourceQuestionId,
981
+ rationale: args.rationale,
982
+ linkedBeliefNodeId: args.linkedBeliefNodeId,
983
+ evidenceRelation: args.evidenceRelation,
984
+ confidence: args.confidence,
985
+ methodology: args.methodology,
986
+ informationAsymmetry: args.informationAsymmetry,
987
+ sourceDescription: args.sourceDescription,
988
+ ...additionalMetadata
989
+ }
990
+ });
991
+ await ctx.scheduler.runAfter(0, internal.neo4jSync.syncNodeToNeo4j, {
992
+ nodeId,
993
+ operation: "upsert"
994
+ });
995
+ await scheduleEmbeddingGeneration({
996
+ ctx,
997
+ nodeId,
998
+ projectId: scope.projectId,
999
+ topicId: scope.topicId,
1000
+ createdBy: args.userId,
1001
+ nodeType: "evidence",
1002
+ text: args.text
1003
+ });
1004
+ if (args.linkedBeliefNodeId && args.evidenceRelation) {
1005
+ const beliefNode = await ctx.db.get(args.linkedBeliefNodeId);
1006
+ if (beliefNode) {
1007
+ const weight = args.evidenceRelation === "supports" ? 1 : -1;
1008
+ await ctx.scheduler.runAfter(5e3, internal.neo4jEdgeAPI.createEdge, {
1009
+ globalId: crypto.randomUUID(),
1010
+ fromGlobalId: globalId,
1011
+ toGlobalId: beliefNode.globalId,
1012
+ edgeType: "informs",
1013
+ weight: weight * (args.confidence || 0.7),
1014
+ createdBy: args.userId,
1015
+ topicId: scope.projectId ? String(scope.projectId) : void 0,
1016
+ fromNodeType: "evidence",
1017
+ toNodeType: "belief",
1018
+ fromLayer: "L2",
1019
+ toLayer: "L3",
1020
+ metadata: {
1021
+ relation: args.evidenceRelation,
1022
+ confidence: args.confidence
1023
+ }
1024
+ });
1025
+ }
1026
+ }
1027
+ await ctx.db.insert("epistemicAudit", {
1028
+ entityType: "evidence",
1029
+ entityId: nodeId,
1030
+ changeType: "created",
1031
+ changedAt: now,
1032
+ changedBy: args.userId,
1033
+ isAgent: false,
1034
+ projectId: scope.projectId,
1035
+ rationale: args.rationale,
1036
+ newState: {
1037
+ text: args.text.slice(0, 200),
1038
+ kind,
1039
+ sourceType,
1040
+ linkedBeliefNodeId: args.linkedBeliefNodeId,
1041
+ evidenceRelation: args.evidenceRelation
1042
+ }
1043
+ });
1044
+ if (scope.projectId || scope.topicId) {
1045
+ await ctx.scheduler.runAfter(
1046
+ 0,
1047
+ "embeddingActions:generateEpistemicNodeEmbedding",
1048
+ {
1049
+ nodeId,
1050
+ projectId: scope.projectId,
1051
+ topicId: scope.topicId ? String(scope.topicId) : void 0,
1052
+ createdBy: args.userId,
1053
+ nodeType: "evidence",
1054
+ text: args.text
1055
+ }
1056
+ );
1057
+ }
1058
+ if (scope.projectId || scope.topicId) {
1059
+ await ctx.scheduler.runAfter(
1060
+ 2e3,
1061
+ internal.nodeClassification.scheduleClassification,
1062
+ {
1063
+ nodeId,
1064
+ nodeType: "evidence",
1065
+ projectId: scope.projectId,
1066
+ topicId: String(scope.topicId)
1067
+ }
1068
+ );
1069
+ }
1070
+ await markProjectGraphDirty(ctx, scope.projectId, String(scope.topicId));
1071
+ return { nodeId };
1072
+ }
1073
+ });
1074
+ var createAndLink = mutation({
1075
+ args: {
1076
+ ...optionalEvidenceScopeArgs,
1077
+ text: v.string(),
1078
+ kind: v.optional(v.string()),
1079
+ tags: v.optional(v.array(v.string())),
1080
+ sourceType: v.optional(v.string()),
1081
+ userId: v.string(),
1082
+ beliefNodeId: v.id("epistemicNodes"),
1083
+ relation: v.union(v.literal("supports"), v.literal("contradicts")),
1084
+ confidence: v.optional(v.number())
1085
+ },
1086
+ returns: permissiveReturn,
1087
+ handler: async (ctx, args) => {
1088
+ const scope = await resolveEvidenceScopeOrNull(ctx, args);
1089
+ if (!scope) {
1090
+ throw new Error("Invalid scope: projectId or topicId is required");
1091
+ }
1092
+ await requireProjectAccess(ctx, String(scope.topicId), args.userId);
1093
+ const now = Date.now();
1094
+ const globalId = generateGlobalId();
1095
+ const contentHash = generateContentHash(args.text);
1096
+ const kind = normalizeKind(args.kind);
1097
+ const sourceType = normalizeSourceType(args.sourceType);
1098
+ const confidence = args.confidence ?? 0.7;
1099
+ const nodeId = await ctx.db.insert("epistemicNodes", {
1100
+ globalId,
1101
+ topicId: scope.topicId,
1102
+ projectId: scope.projectId,
1103
+ tenantId: scope.tenantId,
1104
+ workspaceId: scope.workspaceId,
1105
+ nodeType: "evidence",
1106
+ canonicalText: args.text,
1107
+ contentHash,
1108
+ status: "active",
1109
+ epistemicLayer: "L2",
1110
+ sourceType,
1111
+ createdAt: now,
1112
+ updatedAt: now,
1113
+ createdBy: args.userId,
1114
+ metadata: {
1115
+ kind,
1116
+ tags: args.tags || [],
1117
+ linkedBeliefNodeId: args.beliefNodeId,
1118
+ evidenceRelation: args.relation,
1119
+ confidence
1120
+ }
1121
+ });
1122
+ await ctx.scheduler.runAfter(0, internal.neo4jSync.syncNodeToNeo4j, {
1123
+ nodeId,
1124
+ operation: "upsert"
1125
+ });
1126
+ const beliefNode = await ctx.db.get(args.beliefNodeId);
1127
+ if (!beliefNode) {
1128
+ throw new Error("Belief node not found for edge creation");
1129
+ }
1130
+ const weight = args.relation === "supports" ? confidence : -confidence;
1131
+ const edgeGlobalId = crypto.randomUUID();
1132
+ await ctx.scheduler.runAfter(5e3, internal.neo4jEdgeAPI.createEdge, {
1133
+ globalId: edgeGlobalId,
1134
+ fromGlobalId: globalId,
1135
+ toGlobalId: beliefNode.globalId,
1136
+ edgeType: "informs",
1137
+ weight,
1138
+ createdBy: args.userId,
1139
+ topicId: scope.projectId,
1140
+ fromNodeType: "evidence",
1141
+ toNodeType: "belief",
1142
+ fromLayer: "L2",
1143
+ toLayer: "L3",
1144
+ metadata: {
1145
+ relation: args.relation,
1146
+ confidence
1147
+ }
1148
+ });
1149
+ await markProjectGraphDirty(ctx, scope.projectId, String(scope.topicId));
1150
+ return { nodeId, edgeGlobalId };
1151
+ }
1152
+ });
1153
+ var updateStatus = mutation({
1154
+ args: {
1155
+ nodeId: v.id("epistemicNodes"),
1156
+ status: v.union(
1157
+ v.literal("active"),
1158
+ v.literal("archived"),
1159
+ v.literal("acted_on")
1160
+ ),
1161
+ userId: v.string()
1162
+ },
1163
+ returns: permissiveReturn,
1164
+ handler: async (ctx, args) => {
1165
+ const node = await ctx.db.get(args.nodeId);
1166
+ if (!node || node.nodeType !== "evidence") {
1167
+ throw new Error("Evidence not found");
1168
+ }
1169
+ const now = Date.now();
1170
+ await ctx.db.patch(args.nodeId, {
1171
+ status: args.status,
1172
+ updatedAt: now
1173
+ });
1174
+ await ctx.db.insert("epistemicAudit", {
1175
+ entityType: "evidence",
1176
+ entityId: args.nodeId,
1177
+ changeType: "status_changed",
1178
+ changedAt: now,
1179
+ changedBy: args.userId,
1180
+ isAgent: false,
1181
+ projectId: node.projectId,
1182
+ previousState: { status: node.status },
1183
+ newState: { status: args.status }
1184
+ });
1185
+ await ctx.scheduler.runAfter(0, internal.neo4jSync.syncNodeToNeo4j, {
1186
+ nodeId: args.nodeId,
1187
+ operation: "upsert"
1188
+ });
1189
+ await markProjectGraphDirty(ctx, node.projectId, node.topicId);
1190
+ return { nodeId: args.nodeId };
1191
+ }
1192
+ });
1193
+ var internalCreate = internalMutation({
1194
+ args: {
1195
+ ...optionalEvidenceScopeArgs,
1196
+ text: v.string(),
1197
+ title: v.optional(v.string()),
1198
+ content: v.optional(v.string()),
1199
+ contentType: v.optional(v.string()),
1200
+ kind: v.optional(v.string()),
1201
+ tags: v.optional(v.array(v.string())),
1202
+ sourceType: v.optional(v.string()),
1203
+ externalSourceType: v.optional(v.string()),
1204
+ sourceUrl: v.optional(v.string()),
1205
+ sourceQuestionId: v.optional(v.string()),
1206
+ userId: v.string(),
1207
+ rationale: v.string(),
1208
+ linkedBeliefNodeId: v.optional(v.id("epistemicNodes")),
1209
+ evidenceRelation: v.optional(v.string()),
1210
+ confidence: v.optional(v.number()),
1211
+ metadata: v.optional(v.any()),
1212
+ runtimeToolName: v.optional(v.string()),
1213
+ runtimePackKey: v.optional(v.string()),
1214
+ runtimePackInstallScope: v.optional(
1215
+ v.union(v.literal("tenant"), v.literal("workspace"))
1216
+ )
1217
+ },
1218
+ returns: permissiveReturn,
1219
+ handler: async (ctx, args) => {
1220
+ const now = Date.now();
1221
+ const scope = await resolveEvidenceScopeOrNull(ctx, args);
1222
+ if (!scope) {
1223
+ throw new Error("Invalid scope: projectId or topicId is required");
1224
+ }
1225
+ assertWorkspaceScopedEpistemicNodeScope({
1226
+ scope,
1227
+ nodeType: "evidence",
1228
+ mutationName: "epistemicEvidence.internalCreate"
1229
+ });
1230
+ assertTenantPackWorkspaceMutationAllowed({
1231
+ runtime: resolveRuntimePackMutationContext(args),
1232
+ target: {
1233
+ tenantId: scope.tenantId,
1234
+ workspaceId: scope.workspaceId,
1235
+ nodeType: "evidence",
1236
+ epistemicLayer: "L2"
1237
+ },
1238
+ mutationName: "epistemicEvidence.internalCreate"
1239
+ });
1240
+ const globalId = generateGlobalId();
1241
+ const contentHash = generateContentHash(args.text);
1242
+ const kind = normalizeKind(args.kind);
1243
+ const sourceType = normalizeSourceType(args.sourceType);
1244
+ const additionalMetadata = args.metadata && typeof args.metadata === "object" ? args.metadata : {};
1245
+ const nodeId = await ctx.db.insert("epistemicNodes", {
1246
+ globalId,
1247
+ topicId: scope.topicId,
1248
+ projectId: scope.projectId,
1249
+ tenantId: scope.tenantId,
1250
+ workspaceId: scope.workspaceId,
1251
+ nodeType: "evidence",
1252
+ canonicalText: args.text,
1253
+ contentHash,
1254
+ ...typeof args.title === "string" && args.title.trim().length > 0 ? { title: args.title.trim() } : {},
1255
+ ...typeof args.content === "string" && args.content.length > 0 ? { content: args.content } : {},
1256
+ ...typeof args.contentType === "string" && args.contentType.trim().length > 0 ? { contentType: args.contentType.trim() } : {},
1257
+ status: "active",
1258
+ epistemicLayer: "L2",
1259
+ sourceType,
1260
+ createdAt: now,
1261
+ updatedAt: now,
1262
+ createdBy: args.userId,
1263
+ metadata: {
1264
+ kind,
1265
+ tags: args.tags || [],
1266
+ externalSourceType: args.externalSourceType,
1267
+ sourceUrl: args.sourceUrl,
1268
+ sourceQuestionId: args.sourceQuestionId,
1269
+ rationale: args.rationale,
1270
+ linkedBeliefNodeId: args.linkedBeliefNodeId,
1271
+ evidenceRelation: args.evidenceRelation,
1272
+ confidence: args.confidence,
1273
+ ...additionalMetadata
1274
+ }
1275
+ });
1276
+ await ctx.db.insert("epistemicAudit", {
1277
+ entityType: "evidence",
1278
+ entityId: String(nodeId),
1279
+ changeType: "created",
1280
+ changedAt: now,
1281
+ changedBy: args.userId,
1282
+ isAgent: false,
1283
+ projectId: scope.projectId,
1284
+ rationale: args.rationale,
1285
+ newState: {
1286
+ text: args.text.slice(0, 200),
1287
+ kind,
1288
+ sourceType,
1289
+ externalSourceType: args.externalSourceType,
1290
+ sourceUrl: args.sourceUrl,
1291
+ linkedBeliefNodeId: args.linkedBeliefNodeId,
1292
+ evidenceRelation: args.evidenceRelation,
1293
+ confidence: args.confidence
1294
+ },
1295
+ triggeringAction: "epistemicEvidence.internalCreate"
1296
+ });
1297
+ await ctx.scheduler.runAfter(0, internal.neo4jSync.syncNodeToNeo4j, {
1298
+ nodeId,
1299
+ operation: "upsert"
1300
+ });
1301
+ if (args.linkedBeliefNodeId && args.evidenceRelation) {
1302
+ const beliefNode = await ctx.db.get(args.linkedBeliefNodeId);
977
1303
  if (beliefNode) {
978
- const weight = args.evidenceRelation === "supports" ? 1 : -1;
1304
+ const confidence = args.confidence ?? 0.7;
1305
+ const weight = args.evidenceRelation === "supports" ? confidence : -confidence;
979
1306
  await ctx.scheduler.runAfter(5e3, internal.neo4jEdgeAPI.createEdge, {
980
1307
  globalId: crypto.randomUUID(),
981
1308
  fromGlobalId: globalId,
982
1309
  toGlobalId: beliefNode.globalId,
983
1310
  edgeType: "informs",
984
- weight: weight * (args.confidence || 0.7),
1311
+ weight,
985
1312
  createdBy: args.userId,
986
1313
  topicId: scope.projectId ? String(scope.projectId) : void 0,
987
1314
  fromNodeType: "evidence",
@@ -990,28 +1317,11 @@ var create = mutation({
990
1317
  toLayer: "L3",
991
1318
  metadata: {
992
1319
  relation: args.evidenceRelation,
993
- confidence: args.confidence
1320
+ confidence
994
1321
  }
995
1322
  });
996
1323
  }
997
1324
  }
998
- await ctx.db.insert("epistemicAudit", {
999
- entityType: "evidence",
1000
- entityId: nodeId,
1001
- changeType: "created",
1002
- changedAt: now,
1003
- changedBy: args.userId,
1004
- isAgent: false,
1005
- projectId: scope.projectId,
1006
- rationale: args.rationale,
1007
- newState: {
1008
- text: args.text.slice(0, 200),
1009
- kind,
1010
- sourceType,
1011
- linkedBeliefNodeId: args.linkedBeliefNodeId,
1012
- evidenceRelation: args.evidenceRelation
1013
- }
1014
- });
1015
1325
  if (scope.projectId || scope.topicId) {
1016
1326
  await ctx.scheduler.runAfter(
1017
1327
  0,
@@ -1026,140 +1336,217 @@ var create = mutation({
1026
1336
  }
1027
1337
  );
1028
1338
  }
1029
- if (scope.projectId || scope.topicId) {
1030
- await ctx.scheduler.runAfter(
1031
- 2e3,
1032
- // 2 second delay
1033
- internal.nodeClassification.scheduleClassification,
1034
- {
1035
- nodeId,
1036
- nodeType: "evidence",
1037
- projectId: scope.projectId,
1038
- topicId: String(scope.topicId)
1039
- }
1040
- );
1041
- }
1042
1339
  await markProjectGraphDirty(ctx, scope.projectId, String(scope.topicId));
1043
1340
  return { nodeId };
1044
1341
  }
1045
1342
  });
1046
- var createAndLink = mutation({
1343
+ var updateVerificationStatus = mutation({
1047
1344
  args: {
1048
- ...optionalEvidenceScopeArgs,
1049
- text: v.string(),
1345
+ nodeId: v.id("epistemicNodes"),
1346
+ verificationHash: v.string(),
1347
+ verificationStatus: v.string(),
1348
+ lastVerificationId: v.optional(v.id("verificationResults"))
1349
+ },
1350
+ returns: permissiveReturn,
1351
+ handler: async (ctx, args) => {
1352
+ const node = await ctx.db.get(args.nodeId);
1353
+ if (!node || node.nodeType !== "evidence") {
1354
+ throw new Error("Evidence node not found");
1355
+ }
1356
+ const metadata = node.metadata || {};
1357
+ await ctx.db.patch(args.nodeId, {
1358
+ metadata: {
1359
+ ...metadata,
1360
+ verificationHash: args.verificationHash,
1361
+ verificationStatus: args.verificationStatus,
1362
+ lastVerificationId: args.lastVerificationId
1363
+ }
1364
+ });
1365
+ await markProjectGraphDirty(ctx, node.projectId, node.topicId);
1366
+ return { success: true };
1367
+ }
1368
+ });
1369
+ var update = mutation({
1370
+ args: {
1371
+ nodeId: v.optional(v.id("epistemicNodes")),
1372
+ insightId: v.optional(v.string()),
1373
+ text: v.optional(v.string()),
1050
1374
  kind: v.optional(v.string()),
1051
1375
  tags: v.optional(v.array(v.string())),
1052
- sourceType: v.optional(v.string()),
1053
1376
  userId: v.string(),
1054
- beliefNodeId: v.id("epistemicNodes"),
1055
- relation: v.union(v.literal("supports"), v.literal("contradicts")),
1056
- confidence: v.optional(v.number())
1377
+ externalSourceUrl: v.optional(v.string()),
1378
+ verificationStatus: v.optional(v.string())
1057
1379
  },
1058
1380
  returns: permissiveReturn,
1059
1381
  handler: async (ctx, args) => {
1060
- const scope = await resolveTopicProjectScope(ctx, {
1061
- topicId: args.topicId,
1062
- projectId: args.projectId
1063
- });
1064
- await requireProjectAccess(ctx, String(scope.topicId), args.userId);
1382
+ const resolvedId = args.nodeId ?? args.insightId;
1383
+ if (!resolvedId) {
1384
+ throw new Error("Either nodeId or insightId is required");
1385
+ }
1386
+ const node = await ctx.db.get(resolvedId);
1387
+ if (!node || node.nodeType !== "evidence") {
1388
+ throw new Error("Evidence node not found");
1389
+ }
1390
+ if (!node.projectId) {
1391
+ throw new Error("Evidence has no project scope");
1392
+ }
1393
+ await checkProjectAccess(ctx, node.projectId, args.userId);
1065
1394
  const now = Date.now();
1066
- const globalId = generateGlobalId();
1067
- const contentHash = generateContentHash(args.text);
1068
- const kind = normalizeKind(args.kind);
1069
- const sourceType = normalizeSourceType(args.sourceType);
1070
- const confidence = args.confidence ?? 0.7;
1071
- const nodeId = await ctx.db.insert("epistemicNodes", {
1072
- globalId,
1073
- topicId: scope.topicId,
1074
- projectId: scope.projectId,
1075
- tenantId: scope.tenantId,
1076
- workspaceId: scope.workspaceId,
1077
- nodeType: "evidence",
1078
- canonicalText: args.text,
1079
- contentHash,
1080
- status: "active",
1081
- epistemicLayer: "L2",
1082
- sourceType,
1083
- createdAt: now,
1084
- updatedAt: now,
1085
- createdBy: args.userId,
1086
- metadata: {
1087
- kind,
1088
- tags: args.tags || [],
1089
- linkedBeliefNodeId: args.beliefNodeId,
1090
- evidenceRelation: args.relation,
1091
- confidence
1092
- }
1395
+ const existingMeta = node.metadata || {};
1396
+ const metaUpdates = { ...existingMeta };
1397
+ if (args.kind !== void 0) {
1398
+ metaUpdates.kind = args.kind;
1399
+ }
1400
+ if (args.tags !== void 0) {
1401
+ metaUpdates.tags = args.tags;
1402
+ }
1403
+ if (args.externalSourceUrl !== void 0) {
1404
+ metaUpdates.externalSourceUrl = args.externalSourceUrl;
1405
+ }
1406
+ await ctx.db.patch(resolvedId, {
1407
+ canonicalText: args.text ?? node.canonicalText,
1408
+ metadata: metaUpdates,
1409
+ updatedAt: now
1093
1410
  });
1094
1411
  await ctx.scheduler.runAfter(0, internal.neo4jSync.syncNodeToNeo4j, {
1095
- nodeId,
1412
+ nodeId: resolvedId,
1096
1413
  operation: "upsert"
1097
1414
  });
1098
- const beliefNode = await ctx.db.get(args.beliefNodeId);
1099
- if (!beliefNode) {
1100
- throw new Error("Belief node not found for edge creation");
1101
- }
1102
- const weight = args.relation === "supports" ? confidence : -confidence;
1103
- const edgeGlobalId = crypto.randomUUID();
1104
- await ctx.scheduler.runAfter(5e3, internal.neo4jEdgeAPI.createEdge, {
1105
- globalId: edgeGlobalId,
1106
- fromGlobalId: globalId,
1107
- toGlobalId: beliefNode.globalId,
1108
- edgeType: "informs",
1109
- weight,
1110
- createdBy: args.userId,
1111
- topicId: scope.projectId,
1112
- fromNodeType: "evidence",
1113
- toNodeType: "belief",
1114
- fromLayer: "L2",
1115
- toLayer: "L3",
1116
- metadata: {
1117
- relation: args.relation,
1118
- confidence
1119
- }
1415
+ await ctx.db.insert("epistemicAudit", {
1416
+ entityType: "evidence",
1417
+ entityId: resolvedId,
1418
+ changeType: "updated",
1419
+ changedAt: now,
1420
+ changedBy: args.userId,
1421
+ isAgent: false,
1422
+ projectId: node.projectId,
1423
+ previousState: { text: node.canonicalText?.slice(0, 200) },
1424
+ newState: { text: (args.text ?? node.canonicalText)?.slice(0, 200) }
1120
1425
  });
1121
- await markProjectGraphDirty(ctx, scope.projectId, String(scope.topicId));
1122
- return { nodeId, edgeGlobalId };
1426
+ if (args.text !== void 0) {
1427
+ await ctx.scheduler.runAfter(
1428
+ 0,
1429
+ "embeddingActions:generateEpistemicNodeEmbedding",
1430
+ {
1431
+ nodeId: resolvedId,
1432
+ topicId: node.projectId,
1433
+ createdBy: node.createdBy,
1434
+ nodeType: "evidence",
1435
+ text: args.text
1436
+ }
1437
+ );
1438
+ }
1439
+ await markProjectGraphDirty(ctx, node.projectId, node.topicId);
1440
+ return { nodeId: resolvedId };
1123
1441
  }
1124
1442
  });
1125
- var updateStatus = mutation({
1443
+ var flagAsIncorrect = mutation({
1126
1444
  args: {
1127
- nodeId: v.id("epistemicNodes"),
1128
- status: v.union(
1129
- v.literal("active"),
1130
- v.literal("archived"),
1131
- v.literal("acted_on")
1132
- ),
1445
+ insightId: v.id("epistemicNodes"),
1446
+ reason: v.string(),
1447
+ suggestedCorrection: v.optional(v.string()),
1448
+ userId: v.string()
1449
+ },
1450
+ returns: permissiveReturn,
1451
+ handler: async (ctx, args) => {
1452
+ const now = Date.now();
1453
+ const node = await ctx.db.get(args.insightId);
1454
+ if (!node || node.nodeType !== "evidence") {
1455
+ throw new Error("Evidence not found in epistemic spine");
1456
+ }
1457
+ if (!node.projectId) {
1458
+ throw new Error("Evidence has no project scope");
1459
+ }
1460
+ await checkProjectAccess(ctx, node.projectId, args.userId);
1461
+ const existingMeta = node.metadata || {};
1462
+ await ctx.db.patch(node._id, {
1463
+ verificationStatus: "contradicted",
1464
+ metadata: {
1465
+ ...existingMeta,
1466
+ contradictionReason: args.reason,
1467
+ suggestedCorrection: args.suggestedCorrection,
1468
+ contradictedBy: args.userId,
1469
+ contradictedAt: now
1470
+ },
1471
+ updatedAt: now
1472
+ });
1473
+ await ctx.db.insert("verificationResults", {
1474
+ targetType: "insight",
1475
+ targetId: node._id,
1476
+ claimText: node.canonicalText || "Evidence text not available",
1477
+ verdict: "contradicted",
1478
+ confidence: 1,
1479
+ sources: [],
1480
+ reasoning: args.reason,
1481
+ suggestedRevision: args.suggestedCorrection,
1482
+ caveats: [],
1483
+ mode: "deep_verify",
1484
+ verifiedAt: now,
1485
+ durationMs: 0,
1486
+ projectId: node.projectId,
1487
+ userId: args.userId
1488
+ });
1489
+ await ctx.db.insert("epistemicAudit", {
1490
+ entityType: "evidence",
1491
+ entityId: node._id,
1492
+ changeType: "updated",
1493
+ changedAt: now,
1494
+ changedBy: args.userId,
1495
+ isAgent: false,
1496
+ projectId: node.projectId,
1497
+ newState: {
1498
+ reason: args.reason,
1499
+ suggestedCorrection: args.suggestedCorrection
1500
+ }
1501
+ });
1502
+ await markProjectGraphDirty(ctx, node.projectId, node.topicId);
1503
+ console.log(
1504
+ `[EpistemicEvidence] Evidence flagged as incorrect by ${args.userId}: "${args.insightId.slice(0, 20)}..."`
1505
+ );
1506
+ return { success: true };
1507
+ }
1508
+ });
1509
+ var remove = mutation({
1510
+ args: {
1511
+ nodeId: v.optional(v.id("epistemicNodes")),
1512
+ insightId: v.optional(v.string()),
1133
1513
  userId: v.string()
1134
1514
  },
1135
1515
  returns: permissiveReturn,
1136
1516
  handler: async (ctx, args) => {
1137
- const node = await ctx.db.get(args.nodeId);
1517
+ const resolvedId = args.nodeId ?? args.insightId;
1518
+ if (!resolvedId) {
1519
+ throw new Error("Either nodeId or insightId is required");
1520
+ }
1521
+ const node = await ctx.db.get(resolvedId);
1138
1522
  if (!node || node.nodeType !== "evidence") {
1139
- throw new Error("Evidence not found");
1523
+ throw new Error("Evidence node not found");
1524
+ }
1525
+ if (node.createdBy !== args.userId) {
1526
+ throw new Error("Only the creator can archive this evidence");
1140
1527
  }
1141
1528
  const now = Date.now();
1142
- await ctx.db.patch(args.nodeId, {
1143
- status: args.status,
1529
+ await ctx.db.patch(resolvedId, {
1530
+ status: "archived",
1144
1531
  updatedAt: now
1145
1532
  });
1533
+ await ctx.scheduler.runAfter(0, internal.neo4jSync.syncNodeToNeo4j, {
1534
+ nodeId: resolvedId,
1535
+ operation: "upsert"
1536
+ });
1146
1537
  await ctx.db.insert("epistemicAudit", {
1147
1538
  entityType: "evidence",
1148
- entityId: args.nodeId,
1149
- changeType: "status_changed",
1539
+ entityId: resolvedId,
1540
+ changeType: "archived",
1150
1541
  changedAt: now,
1151
1542
  changedBy: args.userId,
1152
1543
  isAgent: false,
1153
1544
  projectId: node.projectId,
1154
1545
  previousState: { status: node.status },
1155
- newState: { status: args.status }
1156
- });
1157
- await ctx.scheduler.runAfter(0, internal.neo4jSync.syncNodeToNeo4j, {
1158
- nodeId: args.nodeId,
1159
- operation: "upsert"
1546
+ newState: { status: "archived" }
1160
1547
  });
1161
1548
  await markProjectGraphDirty(ctx, node.projectId, node.topicId);
1162
- return { nodeId: args.nodeId };
1549
+ return { nodeId: resolvedId };
1163
1550
  }
1164
1551
  });
1165
1552
  var getById = query({
@@ -1174,9 +1561,7 @@ var getById = query({
1174
1561
  if (!id) {
1175
1562
  return null;
1176
1563
  }
1177
- const node = await ctx.db.get(
1178
- id
1179
- );
1564
+ const node = await ctx.db.get(id);
1180
1565
  if (!node || node.nodeType !== "evidence") {
1181
1566
  return null;
1182
1567
  }
@@ -1196,7 +1581,7 @@ var getByProject = query({
1196
1581
  return [];
1197
1582
  }
1198
1583
  const pageSize = clampEvidenceLimit(args.limit);
1199
- const scanLimit = Math.min(pageSize * 3, MAX_EVIDENCE_PAGE_SIZE);
1584
+ const scanLimit = Math.min(pageSize * 3, 1e3);
1200
1585
  let scope;
1201
1586
  try {
1202
1587
  scope = await resolveTopicProjectScope(ctx, {
@@ -1245,7 +1630,7 @@ var getByTopic = query({
1245
1630
  returns: permissiveReturn,
1246
1631
  handler: async (ctx, args) => {
1247
1632
  const pageSize = clampEvidenceLimit(args.limit);
1248
- const scanLimit = Math.min(pageSize * 3, MAX_EVIDENCE_PAGE_SIZE);
1633
+ const scanLimit = Math.min(pageSize * 3, 1e3);
1249
1634
  const scope = await resolveTopicProjectScope(ctx, {
1250
1635
  topicId: args.topicId
1251
1636
  });
@@ -1292,7 +1677,7 @@ var internalGetByProject = internalQuery({
1292
1677
  returns: permissiveReturn,
1293
1678
  handler: async (ctx, args) => {
1294
1679
  const pageSize = clampEvidenceLimit(args.limit, 500);
1295
- const scanLimit = Math.min(pageSize * 3, MAX_EVIDENCE_PAGE_SIZE);
1680
+ const scanLimit = Math.min(pageSize * 3, 1e3);
1296
1681
  const scope = await resolveEvidenceScopeOrNull(ctx, args);
1297
1682
  if (!scope) {
1298
1683
  return [];
@@ -1356,264 +1741,58 @@ var internalGetByTopic = internalQuery({
1356
1741
  returns: permissiveReturn,
1357
1742
  handler: async (ctx, args) => {
1358
1743
  const pageSize = clampEvidenceLimit(args.limit, 500);
1359
- const scanLimit = Math.min(pageSize * 3, MAX_EVIDENCE_PAGE_SIZE);
1744
+ const scanLimit = Math.min(pageSize * 3, 1e3);
1360
1745
  const audienceMode = args.audienceMode ?? "internal";
1361
1746
  const scope = await resolveTopicProjectScope(ctx, {
1362
1747
  topicId: args.topicId
1363
1748
  });
1364
1749
  const registryRows = await listAudienceRegistryRows(ctx, {
1365
- tenantId: scope.tenantId,
1366
- workspaceId: scope.workspaceId
1367
- });
1368
- const resolveAudienceClass = createEvidenceAudienceResolver(registryRows);
1369
- const viewerClass = resolveAudienceClass(audienceMode, "public");
1370
- const nodes = await ctx.db.query("epistemicNodes").withIndex(
1371
- "by_topic_type",
1372
- (q) => q.eq("topicId", args.topicId).eq("nodeType", "evidence")
1373
- ).order("desc").take(scanLimit);
1374
- const workspaceScopedNodes = nodes.filter(
1375
- (node) => nodeMatchesWorkspaceReasoningScope(node, {
1376
- tenantId: scope.tenantId,
1377
- workspaceId: scope.workspaceId
1378
- })
1379
- );
1380
- return workspaceScopedNodes.filter(
1381
- (n) => canAudienceClassAccess(
1382
- viewerClass,
1383
- resolveAudienceClass(n.audienceLabel, "internal")
1384
- ) && (!args.status || n.status === args.status)
1385
- ).slice(0, pageSize).map((n) => {
1386
- const metadata = n.metadata || {};
1387
- return {
1388
- _id: n._id,
1389
- _creationTime: n.createdAt,
1390
- projectId: n.projectId,
1391
- topicId: n.topicId,
1392
- text: n.canonicalText,
1393
- kind: metadata.kind || "observation",
1394
- tags: metadata.tags || [],
1395
- sourceType: n.sourceType,
1396
- externalSourceType: metadata.externalSourceType,
1397
- sourceUrl: metadata.sourceUrl,
1398
- status: n.status,
1399
- createdBy: n.createdBy,
1400
- createdAt: n.createdAt,
1401
- updatedAt: n.updatedAt,
1402
- audienceLabel: n.audienceLabel,
1403
- policyTags: n.policyTags,
1404
- sensitivityTier: n.sensitivityTier,
1405
- exportClass: n.exportClass,
1406
- anonymizationClass: n.anonymizationClass
1407
- };
1408
- });
1409
- }
1410
- });
1411
- var internalCreate = internalMutation({
1412
- args: {
1413
- ...optionalEvidenceScopeArgs,
1414
- text: v.string(),
1415
- title: v.optional(v.string()),
1416
- content: v.optional(v.string()),
1417
- contentType: v.optional(v.string()),
1418
- kind: v.optional(v.string()),
1419
- tags: v.optional(v.array(v.string())),
1420
- sourceType: v.optional(v.string()),
1421
- externalSourceType: v.optional(v.string()),
1422
- sourceUrl: v.optional(v.string()),
1423
- sourceQuestionId: v.optional(v.string()),
1424
- userId: v.string(),
1425
- rationale: v.string(),
1426
- linkedBeliefNodeId: v.optional(v.id("epistemicNodes")),
1427
- evidenceRelation: v.optional(v.string()),
1428
- confidence: v.optional(v.number()),
1429
- /** Optional extra metadata fields merged into the node's metadata object.
1430
- * Use for domain overlays like coding intelligence (codeAnchors, failedApproach, etc.) */
1431
- metadata: v.optional(v.any()),
1432
- runtimeToolName: v.optional(v.string()),
1433
- runtimePackKey: v.optional(v.string()),
1434
- runtimePackInstallScope: v.optional(
1435
- v.union(v.literal("tenant"), v.literal("workspace"))
1436
- )
1437
- },
1438
- returns: permissiveReturn,
1439
- handler: async (ctx, args) => {
1440
- const now = Date.now();
1441
- const scope = await resolveTopicProjectScope(ctx, {
1442
- topicId: args.topicId,
1443
- projectId: args.projectId
1444
- });
1445
- assertWorkspaceScopedEpistemicNodeScope({
1446
- scope,
1447
- nodeType: "evidence",
1448
- mutationName: "epistemicEvidence.internalCreate"
1449
- });
1450
- assertTenantPackWorkspaceMutationAllowed({
1451
- runtime: resolveRuntimePackMutationContext(args),
1452
- target: {
1453
- tenantId: scope.tenantId,
1454
- workspaceId: scope.workspaceId,
1455
- nodeType: "evidence",
1456
- epistemicLayer: "L2"
1457
- },
1458
- mutationName: "epistemicEvidence.internalCreate"
1459
- });
1460
- const globalId = generateGlobalId();
1461
- const contentHash = generateContentHash(args.text);
1462
- const kind = normalizeKind(args.kind);
1463
- const sourceType = normalizeSourceType(args.sourceType);
1464
- const additionalMetadata = args.metadata && typeof args.metadata === "object" ? args.metadata : {};
1465
- const nodeId = await ctx.db.insert("epistemicNodes", {
1466
- globalId,
1467
- topicId: scope.topicId,
1468
- projectId: scope.projectId,
1469
- tenantId: scope.tenantId,
1470
- workspaceId: scope.workspaceId,
1471
- nodeType: "evidence",
1472
- canonicalText: args.text,
1473
- contentHash,
1474
- ...typeof args.title === "string" && args.title.trim().length > 0 ? { title: args.title.trim() } : {},
1475
- ...typeof args.content === "string" && args.content.length > 0 ? { content: args.content } : {},
1476
- ...typeof args.contentType === "string" && args.contentType.trim().length > 0 ? { contentType: args.contentType.trim() } : {},
1477
- status: "active",
1478
- epistemicLayer: "L2",
1479
- sourceType,
1480
- createdAt: now,
1481
- updatedAt: now,
1482
- createdBy: args.userId,
1483
- metadata: {
1484
- kind,
1485
- tags: args.tags || [],
1486
- externalSourceType: args.externalSourceType,
1487
- sourceUrl: args.sourceUrl,
1488
- sourceQuestionId: args.sourceQuestionId,
1489
- rationale: args.rationale,
1490
- linkedBeliefNodeId: args.linkedBeliefNodeId,
1491
- evidenceRelation: args.evidenceRelation,
1492
- confidence: args.confidence,
1493
- ...additionalMetadata
1494
- }
1495
- });
1496
- await ctx.db.insert("epistemicAudit", {
1497
- entityType: "evidence",
1498
- entityId: String(nodeId),
1499
- changeType: "created",
1500
- changedAt: now,
1501
- changedBy: args.userId,
1502
- isAgent: false,
1503
- projectId: scope.projectId,
1504
- rationale: args.rationale,
1505
- newState: {
1506
- text: args.text.slice(0, 200),
1507
- kind,
1508
- sourceType,
1509
- externalSourceType: args.externalSourceType,
1510
- sourceUrl: args.sourceUrl,
1511
- linkedBeliefNodeId: args.linkedBeliefNodeId,
1512
- evidenceRelation: args.evidenceRelation,
1513
- confidence: args.confidence
1514
- },
1515
- triggeringAction: "epistemicEvidence.internalCreate"
1516
- });
1517
- await ctx.scheduler.runAfter(0, internal.neo4jSync.syncNodeToNeo4j, {
1518
- nodeId,
1519
- operation: "upsert"
1520
- });
1521
- if (args.linkedBeliefNodeId && args.evidenceRelation) {
1522
- const beliefNode = await ctx.db.get(args.linkedBeliefNodeId);
1523
- if (beliefNode) {
1524
- const confidence = args.confidence ?? 0.7;
1525
- const weight = args.evidenceRelation === "supports" ? confidence : -confidence;
1526
- await ctx.scheduler.runAfter(5e3, internal.neo4jEdgeAPI.createEdge, {
1527
- globalId: crypto.randomUUID(),
1528
- fromGlobalId: globalId,
1529
- toGlobalId: beliefNode.globalId,
1530
- edgeType: "informs",
1531
- weight,
1532
- createdBy: args.userId,
1533
- topicId: scope.projectId ? String(scope.projectId) : void 0,
1534
- fromNodeType: "evidence",
1535
- toNodeType: "belief",
1536
- fromLayer: "L2",
1537
- toLayer: "L3",
1538
- metadata: {
1539
- relation: args.evidenceRelation,
1540
- confidence
1541
- }
1542
- });
1543
- }
1544
- }
1545
- if (scope.projectId || scope.topicId) {
1546
- await ctx.scheduler.runAfter(
1547
- 0,
1548
- "embeddingActions:generateEpistemicNodeEmbedding",
1549
- {
1550
- nodeId,
1551
- projectId: scope.projectId,
1552
- topicId: scope.topicId ? String(scope.topicId) : void 0,
1553
- createdBy: args.userId,
1554
- nodeType: "evidence",
1555
- text: args.text
1556
- }
1557
- );
1558
- }
1559
- await markProjectGraphDirty(ctx, scope.projectId, String(scope.topicId));
1560
- return { nodeId };
1561
- }
1562
- });
1563
- var updateVerificationStatus = mutation({
1564
- args: {
1565
- nodeId: v.id("epistemicNodes"),
1566
- verificationHash: v.string(),
1567
- verificationStatus: v.string(),
1568
- lastVerificationId: v.optional(v.id("verificationResults"))
1569
- },
1570
- returns: permissiveReturn,
1571
- handler: async (ctx, args) => {
1572
- const node = await ctx.db.get(args.nodeId);
1573
- if (!node || node.nodeType !== "evidence") {
1574
- throw new Error("Evidence node not found");
1575
- }
1576
- const metadata = node.metadata || {};
1577
- await ctx.db.patch(args.nodeId, {
1578
- metadata: {
1579
- ...metadata,
1580
- verificationHash: args.verificationHash,
1581
- verificationStatus: args.verificationStatus,
1582
- lastVerificationId: args.lastVerificationId
1583
- }
1750
+ tenantId: scope.tenantId,
1751
+ workspaceId: scope.workspaceId
1752
+ });
1753
+ const resolveAudienceClass = createEvidenceAudienceResolver(registryRows);
1754
+ const viewerClass = resolveAudienceClass(audienceMode, "public");
1755
+ const nodes = await ctx.db.query("epistemicNodes").withIndex(
1756
+ "by_topic_type",
1757
+ (q) => q.eq("topicId", args.topicId).eq("nodeType", "evidence")
1758
+ ).order("desc").take(scanLimit);
1759
+ const workspaceScopedNodes = nodes.filter(
1760
+ (node) => nodeMatchesWorkspaceReasoningScope(node, {
1761
+ tenantId: scope.tenantId,
1762
+ workspaceId: scope.workspaceId
1763
+ })
1764
+ );
1765
+ return workspaceScopedNodes.filter(
1766
+ (n) => canAudienceClassAccess(
1767
+ viewerClass,
1768
+ resolveAudienceClass(n.audienceLabel, "internal")
1769
+ ) && (!args.status || n.status === args.status)
1770
+ ).slice(0, pageSize).map((n) => {
1771
+ const metadata = n.metadata || {};
1772
+ return {
1773
+ _id: n._id,
1774
+ _creationTime: n.createdAt,
1775
+ projectId: n.projectId,
1776
+ topicId: n.topicId,
1777
+ text: n.canonicalText,
1778
+ kind: metadata.kind || "observation",
1779
+ tags: metadata.tags || [],
1780
+ sourceType: n.sourceType,
1781
+ externalSourceType: metadata.externalSourceType,
1782
+ sourceUrl: metadata.sourceUrl,
1783
+ status: n.status,
1784
+ createdBy: n.createdBy,
1785
+ createdAt: n.createdAt,
1786
+ updatedAt: n.updatedAt,
1787
+ audienceLabel: n.audienceLabel,
1788
+ policyTags: n.policyTags,
1789
+ sensitivityTier: n.sensitivityTier,
1790
+ exportClass: n.exportClass,
1791
+ anonymizationClass: n.anonymizationClass
1792
+ };
1584
1793
  });
1585
- await markProjectGraphDirty(ctx, node.projectId, node.topicId);
1586
- return { success: true };
1587
1794
  }
1588
1795
  });
1589
- function formatEvidenceNode(n) {
1590
- const metadata = n.metadata || {};
1591
- const linkedWorktreeId = resolveEvidenceLinkedWorktreeId(metadata);
1592
- return {
1593
- _id: n._id,
1594
- _epistemicNodeId: n._id,
1595
- _creationTime: n.createdAt,
1596
- projectId: n.projectId,
1597
- topicId: n.topicId,
1598
- text: n.canonicalText,
1599
- kind: metadata.kind || "observation",
1600
- tags: metadata.tags || [],
1601
- sourceType: n.sourceType,
1602
- externalSourceType: metadata.externalSourceType,
1603
- externalSourceUrl: metadata.sourceUrl,
1604
- sourceArtifactId: metadata.sourceArtifactId,
1605
- sourceQuestionId: metadata.sourceQuestionId,
1606
- linkedWorktreeId,
1607
- [LEGACY_SPRINT_LINK_KEY]: metadata[LEGACY_SPRINT_LINK_KEY] || void 0,
1608
- sourceAnchor: metadata.sourceAnchor,
1609
- aiProvider: metadata.aiProvider,
1610
- verificationStatus: metadata.verificationStatus,
1611
- status: n.status,
1612
- createdBy: n.createdBy,
1613
- createdAt: n.createdAt,
1614
- updatedAt: n.updatedAt
1615
- };
1616
- }
1617
1796
  var getByProjectSystem = query({
1618
1797
  args: {
1619
1798
  ...optionalEvidenceScopeArgs,
@@ -1623,7 +1802,7 @@ var getByProjectSystem = query({
1623
1802
  returns: permissiveReturn,
1624
1803
  handler: async (ctx, args) => {
1625
1804
  const pageSize = clampEvidenceLimit(args.limit, 500);
1626
- const scanLimit = Math.min(pageSize * 3, MAX_EVIDENCE_PAGE_SIZE);
1805
+ const scanLimit = Math.min(pageSize * 3, 1e3);
1627
1806
  const scope = await resolveEvidenceScopeOrNull(ctx, args);
1628
1807
  if (!scope) {
1629
1808
  return [];
@@ -1674,191 +1853,6 @@ var getEvidenceBalance = query({
1674
1853
  return { supporting, challenging, total: evidenceEdges.length };
1675
1854
  }
1676
1855
  });
1677
- var update = mutation({
1678
- args: {
1679
- // Accept both native and legacy-shaped IDs
1680
- nodeId: v.optional(v.id("epistemicNodes")),
1681
- insightId: v.optional(v.string()),
1682
- text: v.optional(v.string()),
1683
- kind: v.optional(v.string()),
1684
- tags: v.optional(v.array(v.string())),
1685
- userId: v.string(),
1686
- externalSourceUrl: v.optional(v.string()),
1687
- verificationStatus: v.optional(v.string())
1688
- },
1689
- returns: permissiveReturn,
1690
- handler: async (ctx, args) => {
1691
- const resolvedId = args.nodeId ?? args.insightId;
1692
- if (!resolvedId) {
1693
- throw new Error("Either nodeId or insightId is required");
1694
- }
1695
- const node = await ctx.db.get(resolvedId);
1696
- if (!node || node.nodeType !== "evidence") {
1697
- throw new Error("Evidence node not found");
1698
- }
1699
- if (!node.projectId) {
1700
- throw new Error("Evidence has no project scope");
1701
- }
1702
- await checkProjectAccess(ctx, node.projectId, args.userId);
1703
- const now = Date.now();
1704
- const existingMeta = node.metadata || {};
1705
- const metaUpdates = { ...existingMeta };
1706
- if (args.kind !== void 0) {
1707
- metaUpdates.kind = args.kind;
1708
- }
1709
- if (args.tags !== void 0) {
1710
- metaUpdates.tags = args.tags;
1711
- }
1712
- if (args.externalSourceUrl !== void 0) {
1713
- metaUpdates.externalSourceUrl = args.externalSourceUrl;
1714
- }
1715
- await ctx.db.patch(resolvedId, {
1716
- canonicalText: args.text ?? node.canonicalText,
1717
- metadata: metaUpdates,
1718
- updatedAt: now
1719
- });
1720
- await ctx.scheduler.runAfter(0, internal.neo4jSync.syncNodeToNeo4j, {
1721
- nodeId: resolvedId,
1722
- operation: "upsert"
1723
- });
1724
- await ctx.db.insert("epistemicAudit", {
1725
- entityType: "evidence",
1726
- entityId: resolvedId,
1727
- changeType: "updated",
1728
- changedAt: now,
1729
- changedBy: args.userId,
1730
- isAgent: false,
1731
- projectId: node.projectId,
1732
- previousState: { text: node.canonicalText?.slice(0, 200) },
1733
- newState: { text: (args.text ?? node.canonicalText)?.slice(0, 200) }
1734
- });
1735
- if (args.text !== void 0) {
1736
- await ctx.scheduler.runAfter(
1737
- 0,
1738
- "embeddingActions:generateEpistemicNodeEmbedding",
1739
- {
1740
- nodeId: resolvedId,
1741
- topicId: node.projectId,
1742
- createdBy: node.createdBy,
1743
- nodeType: "evidence",
1744
- text: args.text
1745
- }
1746
- );
1747
- }
1748
- await markProjectGraphDirty(ctx, node.projectId, node.topicId);
1749
- return { nodeId: resolvedId };
1750
- }
1751
- });
1752
- var flagAsIncorrect = mutation({
1753
- args: {
1754
- insightId: v.id("epistemicNodes"),
1755
- reason: v.string(),
1756
- suggestedCorrection: v.optional(v.string()),
1757
- userId: v.string()
1758
- },
1759
- returns: permissiveReturn,
1760
- handler: async (ctx, args) => {
1761
- const now = Date.now();
1762
- const node = await ctx.db.get(args.insightId);
1763
- if (!node || node.nodeType !== "evidence") {
1764
- throw new Error("Evidence not found in epistemic spine");
1765
- }
1766
- if (!node.projectId) {
1767
- throw new Error("Evidence has no project scope");
1768
- }
1769
- await checkProjectAccess(ctx, node.projectId, args.userId);
1770
- const existingMeta = node.metadata || {};
1771
- await ctx.db.patch(node._id, {
1772
- verificationStatus: "contradicted",
1773
- metadata: {
1774
- ...existingMeta,
1775
- contradictionReason: args.reason,
1776
- suggestedCorrection: args.suggestedCorrection,
1777
- contradictedBy: args.userId,
1778
- contradictedAt: now
1779
- },
1780
- updatedAt: now
1781
- });
1782
- await ctx.db.insert("verificationResults", {
1783
- targetType: "insight",
1784
- targetId: node._id,
1785
- claimText: node.canonicalText || "Evidence text not available",
1786
- verdict: "contradicted",
1787
- confidence: 1,
1788
- sources: [],
1789
- reasoning: args.reason,
1790
- suggestedRevision: args.suggestedCorrection,
1791
- caveats: [],
1792
- mode: "deep_verify",
1793
- verifiedAt: now,
1794
- durationMs: 0,
1795
- projectId: node.projectId,
1796
- userId: args.userId
1797
- });
1798
- await ctx.db.insert("epistemicAudit", {
1799
- entityType: "evidence",
1800
- entityId: node._id,
1801
- changeType: "updated",
1802
- changedAt: now,
1803
- changedBy: args.userId,
1804
- isAgent: false,
1805
- projectId: node.projectId,
1806
- newState: {
1807
- reason: args.reason,
1808
- suggestedCorrection: args.suggestedCorrection
1809
- }
1810
- });
1811
- await markProjectGraphDirty(ctx, node.projectId, node.topicId);
1812
- console.log(
1813
- `[EpistemicEvidence] Evidence flagged as incorrect by ${args.userId}: "${args.insightId.slice(0, 20)}..."`
1814
- );
1815
- return { success: true };
1816
- }
1817
- });
1818
- var remove = mutation({
1819
- args: {
1820
- // Accept both native and legacy-shaped IDs
1821
- nodeId: v.optional(v.id("epistemicNodes")),
1822
- insightId: v.optional(v.string()),
1823
- userId: v.string()
1824
- },
1825
- returns: permissiveReturn,
1826
- handler: async (ctx, args) => {
1827
- const resolvedId = args.nodeId ?? args.insightId;
1828
- if (!resolvedId) {
1829
- throw new Error("Either nodeId or insightId is required");
1830
- }
1831
- const node = await ctx.db.get(resolvedId);
1832
- if (!node || node.nodeType !== "evidence") {
1833
- throw new Error("Evidence node not found");
1834
- }
1835
- if (node.createdBy !== args.userId) {
1836
- throw new Error("Only the creator can archive this evidence");
1837
- }
1838
- const now = Date.now();
1839
- await ctx.db.patch(resolvedId, {
1840
- status: "archived",
1841
- updatedAt: now
1842
- });
1843
- await ctx.scheduler.runAfter(0, internal.neo4jSync.syncNodeToNeo4j, {
1844
- nodeId: resolvedId,
1845
- operation: "upsert"
1846
- });
1847
- await ctx.db.insert("epistemicAudit", {
1848
- entityType: "evidence",
1849
- entityId: resolvedId,
1850
- changeType: "archived",
1851
- changedAt: now,
1852
- changedBy: args.userId,
1853
- isAgent: false,
1854
- projectId: node.projectId,
1855
- previousState: { status: node.status },
1856
- newState: { status: "archived" }
1857
- });
1858
- await markProjectGraphDirty(ctx, node.projectId, node.topicId);
1859
- return { nodeId: resolvedId };
1860
- }
1861
- });
1862
1856
 
1863
1857
  export { create, createAndLink, flagAsIncorrect, flattenEvidenceNode, getById, getByProject, getByProjectSystem, getByTopic, getEvidenceBalance, getForBelief, internalCreate, internalGetByProject, internalGetByTopic, remove, resolveEvidenceLinkedWorktreeId, update, updateStatus, updateVerificationStatus };
1864
1858
  //# sourceMappingURL=epistemicEvidence.js.map