@lucern/graph-primitives 1.0.0 → 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
@@ -4,7 +4,105 @@ import { permissiveReturn } from '@lucern/contracts/schema-helpers/validators';
4
4
  import { componentsGeneric, anyApi, queryGeneric, mutationGeneric, internalMutationGeneric, internalQueryGeneric } from 'convex/server';
5
5
  import { isNodeType, getLayerForNodeType } from '@lucern/contracts/schema-helpers/spine/tables/epistemicNodes';
6
6
 
7
- // src/epistemicNodes.ts
7
+ // src/epistemicNodes.validators.ts
8
+ var epistemicLayerValidator = v.union(
9
+ v.literal("L4"),
10
+ v.literal("L3"),
11
+ v.literal("L2"),
12
+ v.literal("L1"),
13
+ v.literal("ontological"),
14
+ v.literal("organizational")
15
+ );
16
+ var l4NodeTypeValidator = v.union(v.literal("decision"));
17
+ var l3NodeTypeValidator = v.union(
18
+ v.literal("belief"),
19
+ v.literal("question"),
20
+ v.literal("theme"),
21
+ v.literal("deal")
22
+ );
23
+ var l2NodeTypeValidator = v.union(
24
+ v.literal("claim"),
25
+ v.literal("evidence"),
26
+ v.literal("synthesis")
27
+ );
28
+ var l1NodeTypeValidator = v.union(
29
+ v.literal("atomic_fact"),
30
+ v.literal("excerpt"),
31
+ v.literal("source")
32
+ );
33
+ var epistemicNodeTypeValidator = v.union(
34
+ // L4: Audit targets
35
+ v.literal("decision"),
36
+ // L3: Traversal anchors
37
+ v.literal("belief"),
38
+ v.literal("question"),
39
+ v.literal("theme"),
40
+ v.literal("deal"),
41
+ // L2: Compression boundary
42
+ v.literal("claim"),
43
+ v.literal("evidence"),
44
+ v.literal("synthesis"),
45
+ v.literal("answer"),
46
+ // L1: Terminal leaves
47
+ v.literal("atomic_fact"),
48
+ v.literal("excerpt"),
49
+ v.literal("source")
50
+ );
51
+ var ontologicalNodeTypeValidator = v.union(
52
+ v.literal("company"),
53
+ v.literal("person"),
54
+ v.literal("investor"),
55
+ v.literal("function"),
56
+ v.literal("value_chain")
57
+ );
58
+ var organizationalNodeTypeValidator = v.union(v.literal("topic"));
59
+ var nodeTypeValidator = v.union(
60
+ // L4: Audit targets
61
+ v.literal("decision"),
62
+ // L3: Traversal anchors
63
+ v.literal("belief"),
64
+ v.literal("question"),
65
+ v.literal("theme"),
66
+ v.literal("deal"),
67
+ // L2: Compression boundary
68
+ v.literal("claim"),
69
+ v.literal("evidence"),
70
+ v.literal("synthesis"),
71
+ v.literal("answer"),
72
+ // L1: Terminal leaves
73
+ v.literal("atomic_fact"),
74
+ v.literal("excerpt"),
75
+ v.literal("source"),
76
+ // Ontological
77
+ v.literal("company"),
78
+ v.literal("person"),
79
+ v.literal("investor"),
80
+ v.literal("function"),
81
+ v.literal("value_chain"),
82
+ // Organizational
83
+ v.literal("topic")
84
+ );
85
+ var sourceTypeValidator = v.union(
86
+ v.literal("human"),
87
+ v.literal("ai_extracted"),
88
+ v.literal("ai_generated"),
89
+ v.literal("imported"),
90
+ v.literal("system")
91
+ // System-generated (migrations, classifiers)
92
+ );
93
+ var statusValidator = v.union(
94
+ v.literal("active"),
95
+ v.literal("superseded"),
96
+ v.literal("archived"),
97
+ v.literal("deleted")
98
+ );
99
+ var verificationStatusValidator = v.union(
100
+ v.literal("unverified"),
101
+ v.literal("human_verified"),
102
+ v.literal("ai_verified"),
103
+ v.literal("contradicted"),
104
+ v.literal("outdated")
105
+ );
8
106
  var api = anyApi;
9
107
  componentsGeneric();
10
108
  var internal = anyApi;
@@ -24,344 +122,80 @@ function debugGraphPrimitiveFallback(message, context) {
24
122
  }
25
123
  console.debug(message, context ?? {});
26
124
  }
27
-
28
- // src/graphTypes.ts
29
- function getNodeLayer(nodeType) {
30
- const L4_TYPES = ["decision"];
31
- const L3_TYPES = ["belief", "question", "theme", "deal"];
32
- const L2_TYPES = ["claim", "evidence", "synthesis", "answer"];
33
- const L1_TYPES = ["atomic_fact", "excerpt", "source"];
34
- const ONTOLOGICAL_TYPES = [
35
- "company",
36
- "person",
37
- "investor",
38
- "function",
39
- "value_chain"
40
- ];
41
- const ORGANIZATIONAL_TYPES = ["topic"];
42
- if (L4_TYPES.includes(nodeType)) {
43
- return "L4";
44
- }
45
- if (L3_TYPES.includes(nodeType)) {
46
- return "L3";
47
- }
48
- if (L2_TYPES.includes(nodeType)) {
49
- return "L2";
50
- }
51
- if (L1_TYPES.includes(nodeType)) {
52
- return "L1";
53
- }
54
- if (ONTOLOGICAL_TYPES.includes(nodeType)) {
55
- return "ontological";
125
+ var LEGACY_SCOPE_FIELD = "graphScopeProjectId";
126
+ function asMappedProjectId(topic) {
127
+ if (!topic) {
128
+ return;
56
129
  }
57
- if (ORGANIZATIONAL_TYPES.includes(nodeType)) {
58
- return "organizational";
130
+ const directLegacyProjectId = normalizeScopeValue(topic[LEGACY_SCOPE_FIELD]);
131
+ if (directLegacyProjectId) {
132
+ return directLegacyProjectId;
59
133
  }
60
- console.warn(`[GraphTypes] Unknown nodeType "${nodeType}", defaulting to L2`);
61
- return "L2";
134
+ const metadata = topic.metadata || {};
135
+ const candidate = metadata[LEGACY_SCOPE_FIELD] || metadata.legacyProjectId || metadata.projectId || metadata.scopeProjectId;
136
+ return candidate ? candidate : void 0;
62
137
  }
63
-
64
- // src/beliefLifecycle.ts
65
- var RESOLVED_PREDICTION_OUTCOMES = [
66
- "confirmed",
67
- "disconfirmed",
68
- "partial",
69
- "expired"
70
- ];
71
- function hasResolvedPredictionOutcome(predictionMeta) {
72
- if (!predictionMeta || typeof predictionMeta !== "object") {
73
- return false;
138
+ function normalizeScopeValue(value) {
139
+ if (typeof value !== "string") {
140
+ return;
74
141
  }
75
- const outcome = predictionMeta.outcome;
76
- return typeof outcome === "string" && RESOLVED_PREDICTION_OUTCOMES.includes(outcome);
77
- }
78
-
79
- // src/invariantEnforcement.ts
80
- var FORBIDDEN_GENERIC_BELIEF_METADATA_KEYS = /* @__PURE__ */ new Set([
81
- "beliefStatus",
82
- "epistemicStatus",
83
- "forkedBy",
84
- "forkedFrom",
85
- "forkReason",
86
- "forkTimestamp",
87
- "status",
88
- "supersededBy",
89
- "supersedes"
90
- ]);
91
- var ONTOLOGICAL_NODE_TYPES = /* @__PURE__ */ new Set([
92
- "company",
93
- "person",
94
- "investor",
95
- "function",
96
- "value_chain"
97
- ]);
98
- function throwInvariantError(args) {
99
- const error = new Error(args.message);
100
- error.status = args.status ?? 409;
101
- error.code = args.code ?? "INVARIANT_VIOLATION";
102
- error.invariantCode = args.invariantCode;
103
- error.suggestion = args.suggestion;
104
- error.details = args.details;
105
- throw error;
142
+ const normalized = value.trim();
143
+ return normalized.length > 0 ? normalized : void 0;
106
144
  }
107
- function isBeliefNode(node) {
108
- return node?.nodeType === "belief";
145
+ function pickPrimaryTopic(candidates) {
146
+ return [...candidates].sort((a, b) => {
147
+ const depthA = a.depth ?? 9999;
148
+ const depthB = b.depth ?? 9999;
149
+ if (depthA !== depthB) {
150
+ return depthA - depthB;
151
+ }
152
+ const createdA = a.createdAt ?? Number.MAX_SAFE_INTEGER;
153
+ const createdB = b.createdAt ?? Number.MAX_SAFE_INTEGER;
154
+ if (createdA !== createdB) {
155
+ return createdA - createdB;
156
+ }
157
+ return String(a.name || "").localeCompare(String(b.name || ""));
158
+ })[0];
109
159
  }
110
- function isOntologicalNode(node) {
111
- return typeof node?.nodeType === "string" && ONTOLOGICAL_NODE_TYPES.has(node.nodeType);
160
+ async function findTopicsByScopeAlias(ctx, scopeId) {
161
+ try {
162
+ return await ctx.db.query("topics").withIndex(
163
+ "by_graph_scope_project",
164
+ (q) => q.eq(LEGACY_SCOPE_FIELD, scopeId)
165
+ ).collect();
166
+ } catch (error) {
167
+ debugGraphPrimitiveFallback(
168
+ "[topicScope] Failed to resolve scope alias via index",
169
+ {
170
+ error,
171
+ scopeId
172
+ }
173
+ );
174
+ const topics = await ctx.db.query("topics").collect();
175
+ return topics.filter((topic) => {
176
+ const normalizedGlobalId = normalizeScopeValue(topic.globalId);
177
+ const mappedProjectId = asMappedProjectId(topic);
178
+ return String(topic._id) === scopeId || normalizedGlobalId === scopeId || mappedProjectId === scopeId;
179
+ });
180
+ }
112
181
  }
113
- function isScoredBeliefNode(node) {
114
- if (!isBeliefNode(node)) {
115
- return false;
182
+ async function tryResolveHostTopicById(ctx, topicId) {
183
+ if (typeof ctx.runQuery !== "function") {
184
+ return null;
116
185
  }
117
- const metadata = node.metadata && typeof node.metadata === "object" ? node.metadata : void 0;
118
- const numericConfidence = typeof node.confidence === "number" && Number.isFinite(node.confidence);
119
- if (numericConfidence) {
120
- return true;
121
- }
122
- return hasResolvedPredictionOutcome(node.predictionMeta) || hasResolvedPredictionOutcome(metadata?.predictionMeta);
123
- }
124
- function getForbiddenMetadataKeys(metadata) {
125
- if (!metadata || typeof metadata !== "object" || Array.isArray(metadata)) {
126
- return [];
127
- }
128
- return Object.keys(metadata).filter(
129
- (key) => FORBIDDEN_GENERIC_BELIEF_METADATA_KEYS.has(key)
130
- );
131
- }
132
- function assertBeliefNodeGenericUpdateAllowed(args) {
133
- if (!isBeliefNode(args.node)) {
134
- return;
135
- }
136
- if (Object.hasOwn(args.updates, "confidence")) {
137
- throwInvariantError({
138
- message: "Belief confidence is append-only. Generic node updates cannot set confidence directly.",
139
- invariantCode: "belief.confidence_append_only",
140
- suggestion: "Use epistemicBeliefs.modulateConfidence() so the beliefConfidence ledger and audit trail are updated together.",
141
- details: { mutationName: args.mutationName, nodeId: args.node._id }
142
- });
143
- }
144
- if (Object.hasOwn(args.updates, "status")) {
145
- throwInvariantError({
146
- message: "Belief status transitions must use the dedicated belief lifecycle APIs.",
147
- invariantCode: "belief.status_transition_requires_belief_api",
148
- suggestion: "Use epistemicBeliefs.updateStatus() or epistemicBeliefs.archive() so status transitions emit the correct audit event.",
149
- details: { mutationName: args.mutationName, nodeId: args.node._id }
150
- });
151
- }
152
- const forbiddenMetadataKeys = getForbiddenMetadataKeys(args.updates.metadata);
153
- if (forbiddenMetadataKeys.length > 0) {
154
- throwInvariantError({
155
- message: "Belief lineage and lifecycle metadata cannot be rewritten through generic node updates.",
156
- invariantCode: "belief.lineage_requires_fork_belief",
157
- suggestion: "Use epistemicBeliefs.forkBelief() for lineage changes and dedicated belief lifecycle mutations for status changes.",
158
- details: {
159
- mutationName: args.mutationName,
160
- nodeId: args.node._id,
161
- forbiddenMetadataKeys
162
- }
163
- });
164
- }
165
- if (isScoredBeliefNode(args.node) && (Object.hasOwn(args.updates, "canonicalText") || Object.hasOwn(args.updates, "contentHash"))) {
166
- throwInvariantError({
167
- message: "Cannot refine a scored belief in place. Scored formulations are immutable.",
168
- invariantCode: "belief.formulation_immutable_after_scoring",
169
- suggestion: "Use epistemicBeliefs.forkBelief() to evolve the formulation while preserving lineage.",
170
- details: { mutationName: args.mutationName, nodeId: args.node._id }
171
- });
172
- }
173
- }
174
- function assertBeliefNodeArchiveAllowed(args) {
175
- if (!isBeliefNode(args.node)) {
176
- return;
177
- }
178
- throwInvariantError({
179
- message: "Belief archiving must go through the dedicated belief lifecycle API.",
180
- invariantCode: "belief.status_transition_requires_belief_api",
181
- suggestion: "Use epistemicBeliefs.archive() so the belief lifecycle audit trail stays consistent.",
182
- details: { mutationName: args.mutationName, nodeId: args.node._id }
183
- });
184
- }
185
- function assertBeliefNodeVerifyAllowed(args) {
186
- if (!isBeliefNode(args.node) || args.confidence === void 0) {
187
- return;
188
- }
189
- throwInvariantError({
190
- message: "Belief verification cannot set confidence directly. Confidence changes must stay append-only.",
191
- invariantCode: "belief.confidence_append_only",
192
- suggestion: "Call epistemicBeliefs.modulateConfidence() after verification so the confidence history is preserved.",
193
- details: { mutationName: args.mutationName, nodeId: args.node._id }
194
- });
195
- }
196
- function assertBeliefNodeSupersedeAllowed(args) {
197
- if (!isBeliefNode(args.node)) {
198
- return;
199
- }
200
- throwInvariantError({
201
- message: "Belief lineage changes must use forkBelief(), not the generic supersede path.",
202
- invariantCode: "belief.lineage_requires_fork_belief",
203
- suggestion: "Use epistemicBeliefs.forkBelief() so the child belief, supersedes edge, and audit trail are created together.",
204
- details: { mutationName: args.mutationName, nodeId: args.node._id }
205
- });
206
- }
207
- function assertBeliefNodeHardDeleteAllowed(args) {
208
- if (!isBeliefNode(args.node)) {
209
- return;
210
- }
211
- if (!args.allowBeliefHardDelete) {
212
- throwInvariantError({
213
- message: "Belief hard delete is forbidden by default. Beliefs must retain lineage and audit history.",
214
- invariantCode: "belief.hard_delete_forbidden",
215
- suggestion: "Use epistemicBeliefs.archive() or epistemicBeliefs.forkBelief() instead. Only migration repair flows may opt into hard delete explicitly.",
216
- details: { mutationName: args.mutationName, nodeId: args.node._id }
217
- });
218
- }
219
- if (!args.reason.trim().toLowerCase().startsWith("migration:")) {
220
- throwInvariantError({
221
- message: "Belief hard delete bypasses require a migration-scoped rationale.",
222
- invariantCode: "belief.hard_delete_forbidden",
223
- suggestion: 'Retry with allowBeliefHardDelete: true and a reason starting with "migration:" only for one-off data repair flows.',
224
- details: {
225
- mutationName: args.mutationName,
226
- nodeId: args.node._id,
227
- reason: args.reason
228
- }
229
- });
230
- }
231
- }
232
- function assertOntologicalNodeGenericCreateAllowed(args) {
233
- if (!ONTOLOGICAL_NODE_TYPES.has(args.nodeType)) {
234
- return;
235
- }
236
- throwInvariantError({
237
- message: "Ontological entities must be created through the dedicated entity lifecycle API.",
238
- invariantCode: "entity.create_requires_entity_lifecycle",
239
- suggestion: "Use entityLifecycle.createEntity() so tenant-global canonical scope and deduplication are enforced.",
240
- details: {
241
- mutationName: args.mutationName,
242
- nodeType: args.nodeType
243
- }
244
- });
245
- }
246
- function assertOntologicalNodeGenericUpdateAllowed(args) {
247
- if (!isOntologicalNode(args.node)) {
248
- return;
249
- }
250
- throwInvariantError({
251
- message: "Ontological entities must be updated through the dedicated entity lifecycle API.",
252
- invariantCode: "entity.update_requires_entity_lifecycle",
253
- suggestion: "Use entityLifecycle.updateEntityAttributes() so canonical entity mutations stay type-safe and audited.",
254
- details: {
255
- mutationName: args.mutationName,
256
- nodeId: args.node._id,
257
- nodeType: args.node.nodeType
258
- }
259
- });
260
- }
261
- function assertOntologicalNodeArchiveAllowed(args) {
262
- if (!isOntologicalNode(args.node)) {
263
- return;
264
- }
265
- throwInvariantError({
266
- message: "Ontological entities must be archived through the dedicated entity lifecycle API.",
267
- invariantCode: "entity.archive_requires_entity_lifecycle",
268
- suggestion: "Use entityLifecycle.archiveEntity() so entity archival emits the correct audit trail and review hooks.",
269
- details: {
270
- mutationName: args.mutationName,
271
- nodeId: args.node._id,
272
- nodeType: args.node.nodeType
273
- }
274
- });
275
- }
276
- function assertOntologicalNodeSupersedeAllowed(args) {
277
- if (!isOntologicalNode(args.node)) {
278
- return;
279
- }
280
- throwInvariantError({
281
- message: "Ontological entities do not use the generic supersede path.",
282
- invariantCode: "entity.supersede_requires_entity_lifecycle",
283
- suggestion: "Use entityLifecycle.updateEntityAttributes() to edit an entity in place or entityLifecycle.mergeEntities() to collapse duplicates.",
284
- details: {
285
- mutationName: args.mutationName,
286
- nodeId: args.node._id,
287
- nodeType: args.node.nodeType
288
- }
289
- });
290
- }
291
- var LEGACY_SCOPE_FIELD = "graphScopeProjectId";
292
- function asMappedProjectId(topic) {
293
- if (!topic) {
294
- return;
295
- }
296
- const directLegacyProjectId = normalizeScopeValue(topic[LEGACY_SCOPE_FIELD]);
297
- if (directLegacyProjectId) {
298
- return directLegacyProjectId;
299
- }
300
- const metadata = topic.metadata || {};
301
- const candidate = metadata[LEGACY_SCOPE_FIELD] || metadata.legacyProjectId || metadata.projectId || metadata.scopeProjectId;
302
- return candidate ? candidate : void 0;
303
- }
304
- function normalizeScopeValue(value) {
305
- if (typeof value !== "string") {
306
- return;
307
- }
308
- const normalized = value.trim();
309
- return normalized.length > 0 ? normalized : void 0;
310
- }
311
- function pickPrimaryTopic(candidates) {
312
- return [...candidates].sort((a, b) => {
313
- const depthA = a.depth ?? 9999;
314
- const depthB = b.depth ?? 9999;
315
- if (depthA !== depthB) {
316
- return depthA - depthB;
317
- }
318
- const createdA = a.createdAt ?? Number.MAX_SAFE_INTEGER;
319
- const createdB = b.createdAt ?? Number.MAX_SAFE_INTEGER;
320
- if (createdA !== createdB) {
321
- return createdA - createdB;
322
- }
323
- return String(a.name || "").localeCompare(String(b.name || ""));
324
- })[0];
325
- }
326
- async function findTopicsByScopeAlias(ctx, scopeId) {
327
- try {
328
- return await ctx.db.query("topics").withIndex(
329
- "by_graph_scope_project",
330
- (q) => q.eq(LEGACY_SCOPE_FIELD, scopeId)
331
- ).collect();
332
- } catch (error) {
333
- debugGraphPrimitiveFallback(
334
- "[topicScope] Failed to resolve scope alias via index",
335
- {
336
- error,
337
- scopeId
338
- }
339
- );
340
- const topics = await ctx.db.query("topics").collect();
341
- return topics.filter((topic) => {
342
- const normalizedGlobalId = normalizeScopeValue(topic.globalId);
343
- const mappedProjectId = asMappedProjectId(topic);
344
- return String(topic._id) === scopeId || normalizedGlobalId === scopeId || mappedProjectId === scopeId;
345
- });
346
- }
347
- }
348
- async function tryResolveHostTopicById(ctx, topicId) {
349
- if (typeof ctx.runQuery !== "function") {
350
- return null;
351
- }
352
- try {
353
- return await ctx.runQuery(api.topics.get, {
354
- id: topicId
355
- }) ?? null;
356
- } catch (error) {
357
- debugGraphPrimitiveFallback(
358
- "[topicScope] Failed to resolve topic by host query",
359
- {
360
- error,
361
- topicId
362
- }
363
- );
364
- return null;
186
+ try {
187
+ return await ctx.runQuery(api.topics.get, {
188
+ id: topicId
189
+ }) ?? null;
190
+ } catch (error) {
191
+ debugGraphPrimitiveFallback(
192
+ "[topicScope] Failed to resolve topic by host query",
193
+ {
194
+ error,
195
+ topicId
196
+ }
197
+ );
198
+ return null;
365
199
  }
366
200
  }
367
201
  async function tryResolveHostTopicByLegacyScope(ctx, legacyScopeId) {
@@ -511,176 +345,78 @@ var optionalScopeArgs = {
511
345
  projectId: v.optional(v.string()),
512
346
  topicId: v.optional(v.string())
513
347
  };
514
- function normalizeScopeValue2(value) {
515
- if (typeof value !== "string") {
516
- return;
348
+
349
+ // src/epistemicNodes.helpers.ts
350
+ var DEFAULT_NODE_PAGE_SIZE = 250;
351
+ var MAX_NODE_PAGE_SIZE = 8e3;
352
+ function clampNodeLimit(limit, fallback = DEFAULT_NODE_PAGE_SIZE) {
353
+ if (!Number.isFinite(limit)) {
354
+ return fallback;
517
355
  }
518
- const normalized = value.trim();
519
- return normalized.length > 0 ? normalized : void 0;
356
+ return Math.max(1, Math.min(Math.floor(limit), MAX_NODE_PAGE_SIZE));
520
357
  }
521
- function throwWorkspaceIsolationError(args) {
522
- const error = new Error(args.message);
523
- error.status = 409;
524
- error.code = "INVARIANT_VIOLATION";
525
- error.invariantCode = args.invariantCode;
526
- error.suggestion = args.suggestion;
527
- error.details = args.details;
528
- throw error;
358
+ function buildNodeStatusSuccessResult() {
359
+ return { success: true };
529
360
  }
530
- function assertWorkspaceScopedEpistemicNodeScope(args) {
531
- const layer = isNodeType(args.nodeType) ? getLayerForNodeType(args.nodeType) : void 0;
532
- if (layer === "ontological") {
533
- return;
534
- }
535
- const workspaceId = normalizeScopeValue2(args.scope.workspaceId);
536
- if (workspaceId) {
537
- return;
538
- }
539
- throwWorkspaceIsolationError({
540
- message: "Workspace-scoped reasoning isolation requires workspaceId on non-ontological node creation.",
541
- invariantCode: "workspace.scope_required_for_epistemic_nodes",
542
- suggestion: "Resolve the topic/project scope through a workspace-bound topic before creating epistemic nodes.",
543
- details: {
544
- mutationName: args.mutationName,
545
- nodeType: args.nodeType,
546
- topicId: args.scope.topicId,
547
- projectId: args.scope.projectId
548
- }
549
- });
361
+ function buildNodeArchivedResult() {
362
+ return {
363
+ success: true,
364
+ effectiveStatus: "archived"
365
+ };
550
366
  }
551
- function resolveRuntimePackMutationContext(args) {
552
- if (!args.runtimeToolName && !args.runtimePackKey && !args.runtimePackInstallScope) {
553
- return;
554
- }
367
+ function buildNodeNotFoundResult() {
368
+ const result = {};
369
+ result.success = false;
370
+ result.error = "Node not found";
371
+ return result;
372
+ }
373
+ function buildNodeDeletedResult(deletedEdgeCount) {
555
374
  return {
556
- toolName: args.runtimeToolName,
557
- packKey: args.runtimePackKey,
558
- packInstallScope: args.runtimePackInstallScope
375
+ success: true,
376
+ deletedEdgeCount
559
377
  };
560
378
  }
561
- function assertTenantPackWorkspaceMutationAllowed(args) {
562
- if (!args.runtime?.packKey || args.runtime.packInstallScope !== "tenant") {
563
- return;
379
+ function dedupeWorkspaceNodes(nodes) {
380
+ const seen = /* @__PURE__ */ new Set();
381
+ const deduped = [];
382
+ for (const node of nodes) {
383
+ const key = String(node._id);
384
+ if (seen.has(key)) {
385
+ continue;
386
+ }
387
+ seen.add(key);
388
+ deduped.push(node);
564
389
  }
565
- const targetWorkspaceId = normalizeScopeValue2(args.target.workspaceId);
566
- const targetLayer = typeof args.target.epistemicLayer === "string" ? args.target.epistemicLayer : void 0;
567
- if (!targetWorkspaceId || targetLayer === "ontological") {
568
- return;
390
+ return deduped;
391
+ }
392
+ function nodeMatchesWorkspaceReasoningScope(node, scope) {
393
+ return scope.topicId !== void 0 && node.topicId === scope.topicId || scope.projectId !== void 0 && node.projectId === scope.projectId;
394
+ }
395
+ async function collectScopedNodes(ctx, scope, args) {
396
+ const queries = [];
397
+ if (scope.topicId) {
398
+ queries.push(
399
+ args.nodeType ? ctx.db.query("epistemicNodes").withIndex(
400
+ "by_topic_type",
401
+ (q) => q.eq("topicId", scope.topicId).eq("nodeType", args.nodeType)
402
+ ).order("desc").take(args.scanLimit) : ctx.db.query("epistemicNodes").withIndex("by_topic", (q) => q.eq("topicId", scope.topicId)).order("desc").take(args.scanLimit)
403
+ );
569
404
  }
570
- throwWorkspaceIsolationError({
571
- message: `Tenant-scoped pack "${args.runtime.packKey}" cannot mutate workspace-scoped reasoning state.`,
572
- invariantCode: "workspace.tenant_pack_reasoning_write_forbidden",
573
- suggestion: "Use a workspace-scoped pack for workspace-local graph mutations, or route the change through tenant-global canonical entity flows.",
574
- details: {
575
- mutationName: args.mutationName,
576
- toolName: args.runtime.toolName,
577
- packKey: args.runtime.packKey,
578
- targetWorkspaceId,
579
- targetNodeType: args.target.nodeType,
580
- targetLayer
581
- }
582
- });
405
+ if (scope.projectId && !scope.topicId) {
406
+ queries.push(
407
+ args.nodeType ? ctx.db.query("epistemicNodes").withIndex(
408
+ "by_topic_type",
409
+ (q) => q.eq("topicId", scope.projectId).eq("nodeType", args.nodeType)
410
+ ).order("desc").take(args.scanLimit) : ctx.db.query("epistemicNodes").withIndex("by_topic", (q) => q.eq("topicId", scope.projectId)).order("desc").take(args.scanLimit)
411
+ );
412
+ }
413
+ const combined = dedupeWorkspaceNodes((await Promise.all(queries)).flat());
414
+ return combined.filter(
415
+ (node) => nodeMatchesWorkspaceReasoningScope(node, scope)
416
+ );
583
417
  }
584
418
 
585
- // src/epistemicNodes.ts
586
- var epistemicLayerValidator = v.union(
587
- v.literal("L4"),
588
- v.literal("L3"),
589
- v.literal("L2"),
590
- v.literal("L1"),
591
- v.literal("ontological"),
592
- v.literal("organizational")
593
- );
594
- var l4NodeTypeValidator = v.union(v.literal("decision"));
595
- var l3NodeTypeValidator = v.union(
596
- v.literal("belief"),
597
- v.literal("question"),
598
- v.literal("theme"),
599
- v.literal("deal")
600
- );
601
- var l2NodeTypeValidator = v.union(
602
- v.literal("claim"),
603
- v.literal("evidence"),
604
- v.literal("synthesis")
605
- );
606
- var l1NodeTypeValidator = v.union(
607
- v.literal("atomic_fact"),
608
- v.literal("excerpt"),
609
- v.literal("source")
610
- );
611
- var epistemicNodeTypeValidator = v.union(
612
- // L4: Audit targets
613
- v.literal("decision"),
614
- // L3: Traversal anchors
615
- v.literal("belief"),
616
- v.literal("question"),
617
- v.literal("theme"),
618
- v.literal("deal"),
619
- // L2: Compression boundary
620
- v.literal("claim"),
621
- v.literal("evidence"),
622
- v.literal("synthesis"),
623
- v.literal("answer"),
624
- // L1: Terminal leaves
625
- v.literal("atomic_fact"),
626
- v.literal("excerpt"),
627
- v.literal("source")
628
- );
629
- var ontologicalNodeTypeValidator = v.union(
630
- v.literal("company"),
631
- v.literal("person"),
632
- v.literal("investor"),
633
- v.literal("function"),
634
- v.literal("value_chain")
635
- );
636
- var organizationalNodeTypeValidator = v.union(v.literal("topic"));
637
- var nodeTypeValidator = v.union(
638
- // L4: Audit targets
639
- v.literal("decision"),
640
- // L3: Traversal anchors
641
- v.literal("belief"),
642
- v.literal("question"),
643
- v.literal("theme"),
644
- v.literal("deal"),
645
- // L2: Compression boundary
646
- v.literal("claim"),
647
- v.literal("evidence"),
648
- v.literal("synthesis"),
649
- v.literal("answer"),
650
- // L1: Terminal leaves
651
- v.literal("atomic_fact"),
652
- v.literal("excerpt"),
653
- v.literal("source"),
654
- // Ontological
655
- v.literal("company"),
656
- v.literal("person"),
657
- v.literal("investor"),
658
- v.literal("function"),
659
- v.literal("value_chain"),
660
- // Organizational
661
- v.literal("topic")
662
- );
663
- var sourceTypeValidator = v.union(
664
- v.literal("human"),
665
- v.literal("ai_extracted"),
666
- v.literal("ai_generated"),
667
- v.literal("imported"),
668
- v.literal("system")
669
- // System-generated (migrations, classifiers)
670
- );
671
- var statusValidator = v.union(
672
- v.literal("active"),
673
- v.literal("superseded"),
674
- v.literal("archived"),
675
- v.literal("deleted")
676
- );
677
- var verificationStatusValidator = v.union(
678
- v.literal("unverified"),
679
- v.literal("human_verified"),
680
- v.literal("ai_verified"),
681
- v.literal("contradicted"),
682
- v.literal("outdated")
683
- );
419
+ // src/epistemicNodes.queries.ts
684
420
  var optionalNodeScopeArgs = optionalScopeArgs;
685
421
  var get = query({
686
422
  args: { nodeId: v.id("epistemicNodes") },
@@ -798,85 +534,17 @@ var getByProjectAndTypeLite = query({
798
534
  }));
799
535
  }
800
536
  });
801
- var DEFAULT_NODE_PAGE_SIZE = 250;
802
- var MAX_NODE_PAGE_SIZE = 8e3;
803
- function clampNodeLimit(limit, fallback = DEFAULT_NODE_PAGE_SIZE) {
804
- if (!Number.isFinite(limit)) {
805
- return fallback;
806
- }
807
- return Math.max(1, Math.min(Math.floor(limit), MAX_NODE_PAGE_SIZE));
808
- }
809
- function buildNodeStatusSuccessResult() {
810
- return { success: true };
811
- }
812
- function buildNodeArchivedResult() {
813
- return {
814
- success: true,
815
- effectiveStatus: "archived"
816
- };
817
- }
818
- function buildNodeNotFoundResult() {
819
- const result = {};
820
- result.success = false;
821
- result.error = "Node not found";
822
- return result;
823
- }
824
- function buildNodeDeletedResult(deletedEdgeCount) {
825
- return {
826
- success: true,
827
- deletedEdgeCount
828
- };
829
- }
830
- function dedupeWorkspaceNodes(nodes) {
831
- const seen = /* @__PURE__ */ new Set();
832
- const deduped = [];
833
- for (const node of nodes) {
834
- const key = String(node._id);
835
- if (seen.has(key)) {
836
- continue;
837
- }
838
- seen.add(key);
839
- deduped.push(node);
840
- }
841
- return deduped;
842
- }
843
- function nodeMatchesWorkspaceReasoningScope(node, scope) {
844
- return scope.topicId !== void 0 && node.topicId === scope.topicId || scope.projectId !== void 0 && node.projectId === scope.projectId;
845
- }
846
- async function collectScopedNodes(ctx, scope, args) {
847
- const queries = [];
848
- if (scope.topicId) {
849
- queries.push(
850
- args.nodeType ? ctx.db.query("epistemicNodes").withIndex(
851
- "by_topic_type",
852
- (q) => q.eq("topicId", scope.topicId).eq("nodeType", args.nodeType)
853
- ).order("desc").take(args.scanLimit) : ctx.db.query("epistemicNodes").withIndex("by_topic", (q) => q.eq("topicId", scope.topicId)).order("desc").take(args.scanLimit)
854
- );
855
- }
856
- if (scope.projectId && !scope.topicId) {
857
- queries.push(
858
- args.nodeType ? ctx.db.query("epistemicNodes").withIndex(
859
- "by_topic_type",
860
- (q) => q.eq("topicId", scope.projectId).eq("nodeType", args.nodeType)
861
- ).order("desc").take(args.scanLimit) : ctx.db.query("epistemicNodes").withIndex("by_topic", (q) => q.eq("topicId", scope.projectId)).order("desc").take(args.scanLimit)
862
- );
863
- }
864
- const combined = dedupeWorkspaceNodes((await Promise.all(queries)).flat());
865
- return combined.filter(
866
- (node) => nodeMatchesWorkspaceReasoningScope(node, scope)
867
- );
868
- }
869
- var getByProject = query({
870
- args: {
871
- ...optionalNodeScopeArgs,
872
- userId: v.optional(v.string()),
873
- includeArchived: v.optional(v.boolean()),
874
- limit: v.optional(v.number())
875
- },
876
- returns: permissiveReturn,
877
- handler: async (ctx, args) => {
878
- if (!args.projectId && !args.topicId) {
879
- return [];
537
+ var getByProject = query({
538
+ args: {
539
+ ...optionalNodeScopeArgs,
540
+ userId: v.optional(v.string()),
541
+ includeArchived: v.optional(v.boolean()),
542
+ limit: v.optional(v.number())
543
+ },
544
+ returns: permissiveReturn,
545
+ handler: async (ctx, args) => {
546
+ if (!args.projectId && !args.topicId) {
547
+ return [];
880
548
  }
881
549
  const pageSize = clampNodeLimit(args.limit);
882
550
  const scanLimit = Math.min(pageSize * 3, MAX_NODE_PAGE_SIZE);
@@ -887,14 +555,11 @@ var getByProject = query({
887
555
  topicId: args.topicId
888
556
  });
889
557
  } catch (error) {
890
- debugGraphPrimitiveFallback(
891
- "[epistemicNodes] Failed to resolve list scope",
892
- {
893
- error,
894
- projectId: args.projectId,
895
- topicId: args.topicId
896
- }
897
- );
558
+ debugGraphPrimitiveFallback("[epistemicNodes] Failed to resolve list scope", {
559
+ error,
560
+ projectId: args.projectId,
561
+ topicId: args.topicId
562
+ });
898
563
  return [];
899
564
  }
900
565
  if (args.userId) {
@@ -907,74 +572,410 @@ var getByProject = query({
907
572
  return [];
908
573
  }
909
574
  }
910
- const nodes = await collectScopedNodes(ctx, scope, { scanLimit });
911
- if (args.includeArchived) {
912
- return nodes.filter(
913
- (n) => n.status !== "deleted" && nodeMatchesWorkspaceReasoningScope(n, scope)
914
- ).slice(0, pageSize);
575
+ const nodes = await collectScopedNodes(ctx, scope, { scanLimit });
576
+ if (args.includeArchived) {
577
+ return nodes.filter(
578
+ (n) => n.status !== "deleted" && nodeMatchesWorkspaceReasoningScope(n, scope)
579
+ ).slice(0, pageSize);
580
+ }
581
+ return nodes.filter(
582
+ (n) => n.status === "active" && nodeMatchesWorkspaceReasoningScope(n, scope)
583
+ ).slice(0, pageSize);
584
+ }
585
+ });
586
+ var listAll = query({
587
+ args: {
588
+ limit: v.optional(v.number())
589
+ },
590
+ returns: permissiveReturn,
591
+ handler: async (ctx, args) => {
592
+ const pageSize = clampNodeLimit(args.limit ?? 2e3);
593
+ const scanLimit = Math.min(pageSize * 2, MAX_NODE_PAGE_SIZE * 2);
594
+ const nodes = await ctx.db.query("epistemicNodes").order("desc").take(scanLimit);
595
+ return nodes.filter((n) => n.status === "active").slice(0, pageSize);
596
+ }
597
+ });
598
+ var search = query({
599
+ args: {
600
+ searchQuery: v.string(),
601
+ ...optionalNodeScopeArgs,
602
+ nodeType: v.optional(nodeTypeValidator),
603
+ limit: v.optional(v.number())
604
+ },
605
+ returns: permissiveReturn,
606
+ handler: async (ctx, args) => {
607
+ const pageSize = clampNodeLimit(args.limit, 100);
608
+ let scope;
609
+ if (args.projectId || args.topicId) {
610
+ try {
611
+ scope = await resolveTopicProjectScope(ctx, {
612
+ projectId: args.projectId,
613
+ topicId: args.topicId
614
+ });
615
+ } catch (error) {
616
+ debugGraphPrimitiveFallback(
617
+ "[epistemicNodes] Failed to resolve search scope",
618
+ {
619
+ error,
620
+ projectId: args.projectId,
621
+ topicId: args.topicId
622
+ }
623
+ );
624
+ return [];
625
+ }
626
+ }
627
+ const searchResults = ctx.db.query("epistemicNodes").withSearchIndex("search_canonicalText", (q) => {
628
+ let search2 = q.search("canonicalText", args.searchQuery);
629
+ if (scope?.topicId) {
630
+ search2 = search2.eq("topicId", scope.topicId);
631
+ } else if (scope?.projectId) {
632
+ search2 = search2.eq("projectId", scope.projectId);
633
+ }
634
+ if (args.nodeType) {
635
+ search2 = search2.eq("nodeType", args.nodeType);
636
+ }
637
+ return search2.eq("status", "active");
638
+ });
639
+ return await searchResults.take(pageSize);
640
+ }
641
+ });
642
+
643
+ // src/graphTypes.ts
644
+ function getNodeLayer(nodeType) {
645
+ const L4_TYPES = ["decision"];
646
+ const L3_TYPES = ["belief", "question", "theme", "deal"];
647
+ const L2_TYPES = ["claim", "evidence", "synthesis", "answer"];
648
+ const L1_TYPES = ["atomic_fact", "excerpt", "source"];
649
+ const ONTOLOGICAL_TYPES = [
650
+ "company",
651
+ "person",
652
+ "investor",
653
+ "function",
654
+ "value_chain"
655
+ ];
656
+ const ORGANIZATIONAL_TYPES = ["topic"];
657
+ if (L4_TYPES.includes(nodeType)) {
658
+ return "L4";
659
+ }
660
+ if (L3_TYPES.includes(nodeType)) {
661
+ return "L3";
662
+ }
663
+ if (L2_TYPES.includes(nodeType)) {
664
+ return "L2";
665
+ }
666
+ if (L1_TYPES.includes(nodeType)) {
667
+ return "L1";
668
+ }
669
+ if (ONTOLOGICAL_TYPES.includes(nodeType)) {
670
+ return "ontological";
671
+ }
672
+ if (ORGANIZATIONAL_TYPES.includes(nodeType)) {
673
+ return "organizational";
674
+ }
675
+ console.warn(`[GraphTypes] Unknown nodeType "${nodeType}", defaulting to L2`);
676
+ return "L2";
677
+ }
678
+
679
+ // src/beliefLifecycle.ts
680
+ var RESOLVED_PREDICTION_OUTCOMES = [
681
+ "confirmed",
682
+ "disconfirmed",
683
+ "partial",
684
+ "expired"
685
+ ];
686
+ function hasResolvedPredictionOutcome(predictionMeta) {
687
+ if (!predictionMeta || typeof predictionMeta !== "object") {
688
+ return false;
689
+ }
690
+ const outcome = predictionMeta.outcome;
691
+ return typeof outcome === "string" && RESOLVED_PREDICTION_OUTCOMES.includes(outcome);
692
+ }
693
+
694
+ // src/invariantEnforcement.ts
695
+ var FORBIDDEN_GENERIC_BELIEF_METADATA_KEYS = /* @__PURE__ */ new Set([
696
+ "beliefStatus",
697
+ "epistemicStatus",
698
+ "forkedBy",
699
+ "forkedFrom",
700
+ "forkReason",
701
+ "forkTimestamp",
702
+ "status",
703
+ "supersededBy",
704
+ "supersedes"
705
+ ]);
706
+ var ONTOLOGICAL_NODE_TYPES = /* @__PURE__ */ new Set([
707
+ "company",
708
+ "person",
709
+ "investor",
710
+ "function",
711
+ "value_chain"
712
+ ]);
713
+ function throwInvariantError(args) {
714
+ const error = new Error(args.message);
715
+ error.status = args.status ?? 409;
716
+ error.code = args.code ?? "INVARIANT_VIOLATION";
717
+ error.invariantCode = args.invariantCode;
718
+ error.suggestion = args.suggestion;
719
+ error.details = args.details;
720
+ throw error;
721
+ }
722
+ function isBeliefNode(node) {
723
+ return node?.nodeType === "belief";
724
+ }
725
+ function isOntologicalNode(node) {
726
+ return typeof node?.nodeType === "string" && ONTOLOGICAL_NODE_TYPES.has(node.nodeType);
727
+ }
728
+ function isScoredBeliefNode(node) {
729
+ if (!isBeliefNode(node)) {
730
+ return false;
731
+ }
732
+ const metadata = node.metadata && typeof node.metadata === "object" ? node.metadata : void 0;
733
+ const numericConfidence = typeof node.confidence === "number" && Number.isFinite(node.confidence);
734
+ if (numericConfidence) {
735
+ return true;
736
+ }
737
+ return hasResolvedPredictionOutcome(node.predictionMeta) || hasResolvedPredictionOutcome(metadata?.predictionMeta);
738
+ }
739
+ function getForbiddenMetadataKeys(metadata) {
740
+ if (!metadata || typeof metadata !== "object" || Array.isArray(metadata)) {
741
+ return [];
742
+ }
743
+ return Object.keys(metadata).filter(
744
+ (key) => FORBIDDEN_GENERIC_BELIEF_METADATA_KEYS.has(key)
745
+ );
746
+ }
747
+ function assertBeliefNodeGenericUpdateAllowed(args) {
748
+ if (!isBeliefNode(args.node)) {
749
+ return;
750
+ }
751
+ if (Object.hasOwn(args.updates, "confidence")) {
752
+ throwInvariantError({
753
+ message: "Belief confidence is append-only. Generic node updates cannot set confidence directly.",
754
+ invariantCode: "belief.confidence_append_only",
755
+ suggestion: "Use epistemicBeliefs.modulateConfidence() so the beliefConfidence ledger and audit trail are updated together.",
756
+ details: { mutationName: args.mutationName, nodeId: args.node._id }
757
+ });
758
+ }
759
+ if (Object.hasOwn(args.updates, "status")) {
760
+ throwInvariantError({
761
+ message: "Belief status transitions must use the dedicated belief lifecycle APIs.",
762
+ invariantCode: "belief.status_transition_requires_belief_api",
763
+ suggestion: "Use epistemicBeliefs.updateStatus() or epistemicBeliefs.archive() so status transitions emit the correct audit event.",
764
+ details: { mutationName: args.mutationName, nodeId: args.node._id }
765
+ });
766
+ }
767
+ const forbiddenMetadataKeys = getForbiddenMetadataKeys(args.updates.metadata);
768
+ if (forbiddenMetadataKeys.length > 0) {
769
+ throwInvariantError({
770
+ message: "Belief lineage and lifecycle metadata cannot be rewritten through generic node updates.",
771
+ invariantCode: "belief.lineage_requires_fork_belief",
772
+ suggestion: "Use epistemicBeliefs.forkBelief() for lineage changes and dedicated belief lifecycle mutations for status changes.",
773
+ details: {
774
+ mutationName: args.mutationName,
775
+ nodeId: args.node._id,
776
+ forbiddenMetadataKeys
777
+ }
778
+ });
779
+ }
780
+ if (isScoredBeliefNode(args.node) && (Object.hasOwn(args.updates, "canonicalText") || Object.hasOwn(args.updates, "contentHash"))) {
781
+ throwInvariantError({
782
+ message: "Cannot refine a scored belief in place. Scored formulations are immutable.",
783
+ invariantCode: "belief.formulation_immutable_after_scoring",
784
+ suggestion: "Use epistemicBeliefs.forkBelief() to evolve the formulation while preserving lineage.",
785
+ details: { mutationName: args.mutationName, nodeId: args.node._id }
786
+ });
787
+ }
788
+ }
789
+ function assertBeliefNodeArchiveAllowed(args) {
790
+ if (!isBeliefNode(args.node)) {
791
+ return;
792
+ }
793
+ throwInvariantError({
794
+ message: "Belief archiving must go through the dedicated belief lifecycle API.",
795
+ invariantCode: "belief.status_transition_requires_belief_api",
796
+ suggestion: "Use epistemicBeliefs.archive() so the belief lifecycle audit trail stays consistent.",
797
+ details: { mutationName: args.mutationName, nodeId: args.node._id }
798
+ });
799
+ }
800
+ function assertBeliefNodeVerifyAllowed(args) {
801
+ if (!isBeliefNode(args.node) || args.confidence === void 0) {
802
+ return;
803
+ }
804
+ throwInvariantError({
805
+ message: "Belief verification cannot set confidence directly. Confidence changes must stay append-only.",
806
+ invariantCode: "belief.confidence_append_only",
807
+ suggestion: "Call epistemicBeliefs.modulateConfidence() after verification so the confidence history is preserved.",
808
+ details: { mutationName: args.mutationName, nodeId: args.node._id }
809
+ });
810
+ }
811
+ function assertBeliefNodeSupersedeAllowed(args) {
812
+ if (!isBeliefNode(args.node)) {
813
+ return;
814
+ }
815
+ throwInvariantError({
816
+ message: "Belief lineage changes must use forkBelief(), not the generic supersede path.",
817
+ invariantCode: "belief.lineage_requires_fork_belief",
818
+ suggestion: "Use epistemicBeliefs.forkBelief() so the child belief, supersedes edge, and audit trail are created together.",
819
+ details: { mutationName: args.mutationName, nodeId: args.node._id }
820
+ });
821
+ }
822
+ function assertBeliefNodeHardDeleteAllowed(args) {
823
+ if (!isBeliefNode(args.node)) {
824
+ return;
825
+ }
826
+ if (!args.allowBeliefHardDelete) {
827
+ throwInvariantError({
828
+ message: "Belief hard delete is forbidden by default. Beliefs must retain lineage and audit history.",
829
+ invariantCode: "belief.hard_delete_forbidden",
830
+ suggestion: "Use epistemicBeliefs.archive() or epistemicBeliefs.forkBelief() instead. Only migration repair flows may opt into hard delete explicitly.",
831
+ details: { mutationName: args.mutationName, nodeId: args.node._id }
832
+ });
833
+ }
834
+ if (!args.reason.trim().toLowerCase().startsWith("migration:")) {
835
+ throwInvariantError({
836
+ message: "Belief hard delete bypasses require a migration-scoped rationale.",
837
+ invariantCode: "belief.hard_delete_forbidden",
838
+ suggestion: 'Retry with allowBeliefHardDelete: true and a reason starting with "migration:" only for one-off data repair flows.',
839
+ details: {
840
+ mutationName: args.mutationName,
841
+ nodeId: args.node._id,
842
+ reason: args.reason
843
+ }
844
+ });
845
+ }
846
+ }
847
+ function assertOntologicalNodeGenericCreateAllowed(args) {
848
+ if (!ONTOLOGICAL_NODE_TYPES.has(args.nodeType)) {
849
+ return;
850
+ }
851
+ throwInvariantError({
852
+ message: "Ontological entities must be created through the dedicated entity lifecycle API.",
853
+ invariantCode: "entity.create_requires_entity_lifecycle",
854
+ suggestion: "Use entityLifecycle.createEntity() so tenant-global canonical scope and deduplication are enforced.",
855
+ details: {
856
+ mutationName: args.mutationName,
857
+ nodeType: args.nodeType
858
+ }
859
+ });
860
+ }
861
+ function assertOntologicalNodeGenericUpdateAllowed(args) {
862
+ if (!isOntologicalNode(args.node)) {
863
+ return;
864
+ }
865
+ throwInvariantError({
866
+ message: "Ontological entities must be updated through the dedicated entity lifecycle API.",
867
+ invariantCode: "entity.update_requires_entity_lifecycle",
868
+ suggestion: "Use entityLifecycle.updateEntityAttributes() so canonical entity mutations stay type-safe and audited.",
869
+ details: {
870
+ mutationName: args.mutationName,
871
+ nodeId: args.node._id,
872
+ nodeType: args.node.nodeType
873
+ }
874
+ });
875
+ }
876
+ function assertOntologicalNodeArchiveAllowed(args) {
877
+ if (!isOntologicalNode(args.node)) {
878
+ return;
879
+ }
880
+ throwInvariantError({
881
+ message: "Ontological entities must be archived through the dedicated entity lifecycle API.",
882
+ invariantCode: "entity.archive_requires_entity_lifecycle",
883
+ suggestion: "Use entityLifecycle.archiveEntity() so entity archival emits the correct audit trail and review hooks.",
884
+ details: {
885
+ mutationName: args.mutationName,
886
+ nodeId: args.node._id,
887
+ nodeType: args.node.nodeType
888
+ }
889
+ });
890
+ }
891
+ function assertOntologicalNodeSupersedeAllowed(args) {
892
+ if (!isOntologicalNode(args.node)) {
893
+ return;
894
+ }
895
+ throwInvariantError({
896
+ message: "Ontological entities do not use the generic supersede path.",
897
+ invariantCode: "entity.supersede_requires_entity_lifecycle",
898
+ suggestion: "Use entityLifecycle.updateEntityAttributes() to edit an entity in place or entityLifecycle.mergeEntities() to collapse duplicates.",
899
+ details: {
900
+ mutationName: args.mutationName,
901
+ nodeId: args.node._id,
902
+ nodeType: args.node.nodeType
915
903
  }
916
- return nodes.filter(
917
- (n) => n.status === "active" && nodeMatchesWorkspaceReasoningScope(n, scope)
918
- ).slice(0, pageSize);
904
+ });
905
+ }
906
+ function normalizeScopeValue2(value) {
907
+ if (typeof value !== "string") {
908
+ return;
919
909
  }
920
- });
921
- var getByTopic = getByProject;
922
- var listAll = query({
923
- args: {
924
- limit: v.optional(v.number())
925
- },
926
- returns: permissiveReturn,
927
- handler: async (ctx, args) => {
928
- const pageSize = clampNodeLimit(args.limit ?? 2e3);
929
- const scanLimit = Math.min(pageSize * 2, MAX_NODE_PAGE_SIZE * 2);
930
- const nodes = await ctx.db.query("epistemicNodes").order("desc").take(scanLimit);
931
- return nodes.filter((n) => n.status === "active").slice(0, pageSize);
910
+ const normalized = value.trim();
911
+ return normalized.length > 0 ? normalized : void 0;
912
+ }
913
+ function throwWorkspaceIsolationError(args) {
914
+ const error = new Error(args.message);
915
+ error.status = 409;
916
+ error.code = "INVARIANT_VIOLATION";
917
+ error.invariantCode = args.invariantCode;
918
+ error.suggestion = args.suggestion;
919
+ error.details = args.details;
920
+ throw error;
921
+ }
922
+ function assertWorkspaceScopedEpistemicNodeScope(args) {
923
+ const layer = isNodeType(args.nodeType) ? getLayerForNodeType(args.nodeType) : void 0;
924
+ if (layer === "ontological") {
925
+ return;
932
926
  }
933
- });
934
- var search = query({
935
- args: {
936
- searchQuery: v.string(),
937
- ...optionalNodeScopeArgs,
938
- nodeType: v.optional(nodeTypeValidator),
939
- limit: v.optional(v.number())
940
- },
941
- returns: permissiveReturn,
942
- handler: async (ctx, args) => {
943
- const pageSize = clampNodeLimit(args.limit, 100);
944
- let scope;
945
- if (args.projectId || args.topicId) {
946
- try {
947
- scope = await resolveTopicProjectScope(ctx, {
948
- projectId: args.projectId,
949
- topicId: args.topicId
950
- });
951
- } catch (error) {
952
- debugGraphPrimitiveFallback(
953
- "[epistemicNodes] Failed to resolve search scope",
954
- {
955
- error,
956
- projectId: args.projectId,
957
- topicId: args.topicId
958
- }
959
- );
960
- return [];
961
- }
927
+ const workspaceId = normalizeScopeValue2(args.scope.workspaceId);
928
+ if (workspaceId) {
929
+ return;
930
+ }
931
+ throwWorkspaceIsolationError({
932
+ message: "Workspace-scoped reasoning isolation requires workspaceId on non-ontological node creation.",
933
+ invariantCode: "workspace.scope_required_for_epistemic_nodes",
934
+ suggestion: "Resolve the topic/project scope through a workspace-bound topic before creating epistemic nodes.",
935
+ details: {
936
+ mutationName: args.mutationName,
937
+ nodeType: args.nodeType,
938
+ topicId: args.scope.topicId,
939
+ projectId: args.scope.projectId
962
940
  }
963
- const searchResults = ctx.db.query("epistemicNodes").withSearchIndex("search_canonicalText", (q) => {
964
- let search2 = q.search("canonicalText", args.searchQuery);
965
- if (scope?.topicId) {
966
- search2 = search2.eq("topicId", scope.topicId);
967
- } else if (scope?.projectId) {
968
- search2 = search2.eq("projectId", scope.projectId);
969
- }
970
- if (args.nodeType) {
971
- search2 = search2.eq("nodeType", args.nodeType);
972
- }
973
- return search2.eq("status", "active");
974
- });
975
- return await searchResults.take(pageSize);
941
+ });
942
+ }
943
+ function resolveRuntimePackMutationContext(args) {
944
+ if (!args.runtimeToolName && !args.runtimePackKey && !args.runtimePackInstallScope) {
945
+ return;
976
946
  }
977
- });
947
+ return {
948
+ toolName: args.runtimeToolName,
949
+ packKey: args.runtimePackKey,
950
+ packInstallScope: args.runtimePackInstallScope
951
+ };
952
+ }
953
+ function assertTenantPackWorkspaceMutationAllowed(args) {
954
+ if (!args.runtime?.packKey || args.runtime.packInstallScope !== "tenant") {
955
+ return;
956
+ }
957
+ const targetWorkspaceId = normalizeScopeValue2(args.target.workspaceId);
958
+ const targetLayer = typeof args.target.epistemicLayer === "string" ? args.target.epistemicLayer : void 0;
959
+ if (!targetWorkspaceId || targetLayer === "ontological") {
960
+ return;
961
+ }
962
+ throwWorkspaceIsolationError({
963
+ message: `Tenant-scoped pack "${args.runtime.packKey}" cannot mutate workspace-scoped reasoning state.`,
964
+ invariantCode: "workspace.tenant_pack_reasoning_write_forbidden",
965
+ suggestion: "Use a workspace-scoped pack for workspace-local graph mutations, or route the change through tenant-global canonical entity flows.",
966
+ details: {
967
+ mutationName: args.mutationName,
968
+ toolName: args.runtime.toolName,
969
+ packKey: args.runtime.packKey,
970
+ targetWorkspaceId,
971
+ targetNodeType: args.target.nodeType,
972
+ targetLayer
973
+ }
974
+ });
975
+ }
976
+
977
+ // src/epistemicNodes.mutations.ts
978
+ var optionalNodeScopeArgs2 = optionalScopeArgs;
978
979
  var create = mutation({
979
980
  args: {
980
981
  globalId: v.string(),
@@ -1004,7 +1005,7 @@ var create = mutation({
1004
1005
  extractedFromNodeId: v.optional(v.id("epistemicNodes")),
1005
1006
  confidence: v.optional(v.number()),
1006
1007
  verificationStatus: v.optional(verificationStatusValidator),
1007
- ...optionalNodeScopeArgs,
1008
+ ...optionalNodeScopeArgs2,
1008
1009
  createdBy: v.string(),
1009
1010
  runtimeToolName: v.optional(v.string()),
1010
1011
  runtimePackKey: v.optional(v.string()),
@@ -1308,56 +1309,6 @@ var archive = mutation({
1308
1309
  return buildNodeArchivedResult();
1309
1310
  }
1310
1311
  });
1311
- var hardDelete = internalMutation({
1312
- args: {
1313
- nodeId: v.id("epistemicNodes"),
1314
- reason: v.string(),
1315
- // Required: document why this is being hard-deleted
1316
- allowBeliefHardDelete: v.optional(v.boolean())
1317
- },
1318
- returns: permissiveReturn,
1319
- handler: async (ctx, args) => {
1320
- const node = await ctx.db.get(args.nodeId);
1321
- if (!node) {
1322
- return buildNodeNotFoundResult();
1323
- }
1324
- assertBeliefNodeHardDeleteAllowed({
1325
- node,
1326
- allowBeliefHardDelete: args.allowBeliefHardDelete ?? false,
1327
- reason: args.reason,
1328
- mutationName: "epistemicNodes.hardDelete"
1329
- });
1330
- await ctx.db.insert("epistemicAudit", {
1331
- entityType: "node",
1332
- entityId: args.nodeId,
1333
- changeType: "deleted",
1334
- projectId: node.projectId,
1335
- changedBy: "system:hard_delete",
1336
- changedAt: Date.now(),
1337
- isAgent: false,
1338
- previousState: {
1339
- nodeType: node.nodeType,
1340
- title: node.title || node.canonicalText?.slice(0, 100),
1341
- status: node.status
1342
- },
1343
- rationale: args.reason
1344
- });
1345
- const fromEdges = await ctx.db.query("epistemicEdges").withIndex("by_from", (q) => q.eq("fromNodeId", args.nodeId)).collect();
1346
- const toEdges = await ctx.db.query("epistemicEdges").withIndex("by_to", (q) => q.eq("toNodeId", args.nodeId)).collect();
1347
- const uniqueEdges = /* @__PURE__ */ new Map();
1348
- for (const edge of [...fromEdges, ...toEdges]) {
1349
- uniqueEdges.set(edge.globalId, edge);
1350
- }
1351
- for (const edge of uniqueEdges.values()) {
1352
- await ctx.scheduler.runAfter(0, internal.neo4jEdgeAPI.deleteEdge, {
1353
- globalId: edge.globalId
1354
- });
1355
- await ctx.db.delete(edge._id);
1356
- }
1357
- await ctx.db.delete(args.nodeId);
1358
- return buildNodeDeletedResult(uniqueEdges.size);
1359
- }
1360
- });
1361
1312
  var verify = mutation({
1362
1313
  args: {
1363
1314
  nodeId: v.id("epistemicNodes"),
@@ -1409,7 +1360,7 @@ var batchCreate = mutation({
1409
1360
  args: {
1410
1361
  nodes: v.array(
1411
1362
  v.object({
1412
- ...optionalNodeScopeArgs,
1363
+ ...optionalNodeScopeArgs2,
1413
1364
  globalId: v.string(),
1414
1365
  nodeType: nodeTypeValidator,
1415
1366
  subtype: v.optional(v.string()),
@@ -1507,9 +1458,10 @@ var batchCreate = mutation({
1507
1458
  return { created: results.length, results };
1508
1459
  }
1509
1460
  });
1461
+ var optionalNodeScopeArgs3 = optionalScopeArgs;
1510
1462
  var createInternal = internalMutation({
1511
1463
  args: {
1512
- ...optionalNodeScopeArgs,
1464
+ ...optionalNodeScopeArgs3,
1513
1465
  globalId: v.string(),
1514
1466
  nodeType: nodeTypeValidator,
1515
1467
  subtype: v.optional(v.string()),
@@ -1673,6 +1625,59 @@ var getNodesPendingEdgeSync = internalQuery({
1673
1625
  ).take(limit);
1674
1626
  }
1675
1627
  });
1628
+ var hardDelete = internalMutation({
1629
+ args: {
1630
+ nodeId: v.id("epistemicNodes"),
1631
+ reason: v.string(),
1632
+ // Required: document why this is being hard-deleted
1633
+ allowBeliefHardDelete: v.optional(v.boolean())
1634
+ },
1635
+ returns: permissiveReturn,
1636
+ handler: async (ctx, args) => {
1637
+ const node = await ctx.db.get(args.nodeId);
1638
+ if (!node) {
1639
+ return buildNodeNotFoundResult();
1640
+ }
1641
+ assertBeliefNodeHardDeleteAllowed({
1642
+ node,
1643
+ allowBeliefHardDelete: args.allowBeliefHardDelete ?? false,
1644
+ reason: args.reason,
1645
+ mutationName: "epistemicNodes.hardDelete"
1646
+ });
1647
+ await ctx.db.insert("epistemicAudit", {
1648
+ entityType: "node",
1649
+ entityId: args.nodeId,
1650
+ changeType: "deleted",
1651
+ projectId: node.projectId,
1652
+ changedBy: "system:hard_delete",
1653
+ changedAt: Date.now(),
1654
+ isAgent: false,
1655
+ previousState: {
1656
+ nodeType: node.nodeType,
1657
+ title: node.title || node.canonicalText?.slice(0, 100),
1658
+ status: node.status
1659
+ },
1660
+ rationale: args.reason
1661
+ });
1662
+ const fromEdges = await ctx.db.query("epistemicEdges").withIndex("by_from", (q) => q.eq("fromNodeId", args.nodeId)).collect();
1663
+ const toEdges = await ctx.db.query("epistemicEdges").withIndex("by_to", (q) => q.eq("toNodeId", args.nodeId)).collect();
1664
+ const uniqueEdges = /* @__PURE__ */ new Map();
1665
+ for (const edge of [...fromEdges, ...toEdges]) {
1666
+ uniqueEdges.set(edge.globalId, edge);
1667
+ }
1668
+ for (const edge of uniqueEdges.values()) {
1669
+ await ctx.scheduler.runAfter(0, internal.neo4jEdgeAPI.deleteEdge, {
1670
+ globalId: edge.globalId
1671
+ });
1672
+ await ctx.db.delete(edge._id);
1673
+ }
1674
+ await ctx.db.delete(args.nodeId);
1675
+ return buildNodeDeletedResult(uniqueEdges.size);
1676
+ }
1677
+ });
1678
+
1679
+ // src/epistemicNodes.ts
1680
+ var getByTopic = getByProject;
1676
1681
 
1677
1682
  export { archive, backfillTopicId, batchCreate, create, createInternal, epistemicLayerValidator, epistemicNodeTypeValidator, get, getByContentHash, getByGlobalId, getByProject, getByProjectAndType, getByProjectAndTypeLite, getByTopic, getInternal, getNodesPendingEdgeSync, hardDelete, l1NodeTypeValidator, l2NodeTypeValidator, l3NodeTypeValidator, l4NodeTypeValidator, listAll, nodeTypeValidator, ontologicalNodeTypeValidator, organizationalNodeTypeValidator, search, sourceTypeValidator, statusValidator, supersede, update, updateSyncStatus, verificationStatusValidator, verify };
1678
1683
  //# sourceMappingURL=epistemicNodes.js.map