@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
@@ -0,0 +1,1153 @@
1
+ import { v } from 'convex/values';
2
+ import { requireProjectAccess } from '@lucern/access-control/access';
3
+ import { permissiveReturn } from '@lucern/contracts/schema-helpers/validators';
4
+ import { componentsGeneric, anyApi, mutationGeneric } from 'convex/server';
5
+ import { isNodeType, getLayerForNodeType } from '@lucern/contracts/schema-helpers/spine/tables/epistemicNodes';
6
+
7
+ // src/epistemicNodes.mutations.ts
8
+ var api = anyApi;
9
+ componentsGeneric();
10
+ var internal = anyApi;
11
+ var mutation = mutationGeneric;
12
+
13
+ // src/debug.ts
14
+ function isGraphPrimitiveDebugEnabled() {
15
+ const env = globalThis.process?.env;
16
+ return env?.LUCERN_COMPAT_FALLBACK_DEBUG === "1" || env?.LUCERN_GRAPH_DEBUG === "1";
17
+ }
18
+ function debugGraphPrimitiveFallback(message, context) {
19
+ if (!isGraphPrimitiveDebugEnabled()) {
20
+ return;
21
+ }
22
+ console.debug(message, context ?? {});
23
+ }
24
+
25
+ // src/graphTypes.ts
26
+ function getNodeLayer(nodeType) {
27
+ const L4_TYPES = ["decision"];
28
+ const L3_TYPES = ["belief", "question", "theme", "deal"];
29
+ const L2_TYPES = ["claim", "evidence", "synthesis", "answer"];
30
+ const L1_TYPES = ["atomic_fact", "excerpt", "source"];
31
+ const ONTOLOGICAL_TYPES = [
32
+ "company",
33
+ "person",
34
+ "investor",
35
+ "function",
36
+ "value_chain"
37
+ ];
38
+ const ORGANIZATIONAL_TYPES = ["topic"];
39
+ if (L4_TYPES.includes(nodeType)) {
40
+ return "L4";
41
+ }
42
+ if (L3_TYPES.includes(nodeType)) {
43
+ return "L3";
44
+ }
45
+ if (L2_TYPES.includes(nodeType)) {
46
+ return "L2";
47
+ }
48
+ if (L1_TYPES.includes(nodeType)) {
49
+ return "L1";
50
+ }
51
+ if (ONTOLOGICAL_TYPES.includes(nodeType)) {
52
+ return "ontological";
53
+ }
54
+ if (ORGANIZATIONAL_TYPES.includes(nodeType)) {
55
+ return "organizational";
56
+ }
57
+ console.warn(`[GraphTypes] Unknown nodeType "${nodeType}", defaulting to L2`);
58
+ return "L2";
59
+ }
60
+ var LEGACY_SCOPE_FIELD = "graphScopeProjectId";
61
+ function asMappedProjectId(topic) {
62
+ if (!topic) {
63
+ return;
64
+ }
65
+ const directLegacyProjectId = normalizeScopeValue(topic[LEGACY_SCOPE_FIELD]);
66
+ if (directLegacyProjectId) {
67
+ return directLegacyProjectId;
68
+ }
69
+ const metadata = topic.metadata || {};
70
+ const candidate = metadata[LEGACY_SCOPE_FIELD] || metadata.legacyProjectId || metadata.projectId || metadata.scopeProjectId;
71
+ return candidate ? candidate : void 0;
72
+ }
73
+ function normalizeScopeValue(value) {
74
+ if (typeof value !== "string") {
75
+ return;
76
+ }
77
+ const normalized = value.trim();
78
+ return normalized.length > 0 ? normalized : void 0;
79
+ }
80
+ function pickPrimaryTopic(candidates) {
81
+ return [...candidates].sort((a, b) => {
82
+ const depthA = a.depth ?? 9999;
83
+ const depthB = b.depth ?? 9999;
84
+ if (depthA !== depthB) {
85
+ return depthA - depthB;
86
+ }
87
+ const createdA = a.createdAt ?? Number.MAX_SAFE_INTEGER;
88
+ const createdB = b.createdAt ?? Number.MAX_SAFE_INTEGER;
89
+ if (createdA !== createdB) {
90
+ return createdA - createdB;
91
+ }
92
+ return String(a.name || "").localeCompare(String(b.name || ""));
93
+ })[0];
94
+ }
95
+ async function findTopicsByScopeAlias(ctx, scopeId) {
96
+ try {
97
+ return await ctx.db.query("topics").withIndex(
98
+ "by_graph_scope_project",
99
+ (q) => q.eq(LEGACY_SCOPE_FIELD, scopeId)
100
+ ).collect();
101
+ } catch (error) {
102
+ debugGraphPrimitiveFallback(
103
+ "[topicScope] Failed to resolve scope alias via index",
104
+ {
105
+ error,
106
+ scopeId
107
+ }
108
+ );
109
+ const topics = await ctx.db.query("topics").collect();
110
+ return topics.filter((topic) => {
111
+ const normalizedGlobalId = normalizeScopeValue(topic.globalId);
112
+ const mappedProjectId = asMappedProjectId(topic);
113
+ return String(topic._id) === scopeId || normalizedGlobalId === scopeId || mappedProjectId === scopeId;
114
+ });
115
+ }
116
+ }
117
+ async function tryResolveHostTopicById(ctx, topicId) {
118
+ if (typeof ctx.runQuery !== "function") {
119
+ return null;
120
+ }
121
+ try {
122
+ return await ctx.runQuery(api.topics.get, {
123
+ id: topicId
124
+ }) ?? null;
125
+ } catch (error) {
126
+ debugGraphPrimitiveFallback(
127
+ "[topicScope] Failed to resolve topic by host query",
128
+ {
129
+ error,
130
+ topicId
131
+ }
132
+ );
133
+ return null;
134
+ }
135
+ }
136
+ async function tryResolveHostTopicByLegacyScope(ctx, legacyScopeId) {
137
+ if (typeof ctx.runQuery !== "function") {
138
+ return null;
139
+ }
140
+ try {
141
+ return await ctx.runQuery(api.topics.getByLegacyScopeId, {
142
+ projectId: legacyScopeId
143
+ }) ?? null;
144
+ } catch (error) {
145
+ debugGraphPrimitiveFallback(
146
+ "[topicScope] Failed to resolve topic by legacy scope",
147
+ {
148
+ error,
149
+ legacyScopeId
150
+ }
151
+ );
152
+ return null;
153
+ }
154
+ }
155
+ async function resolveInheritedWorkspaceScope(ctx, topic) {
156
+ const MAX_DEPTH = 10;
157
+ let tenantId = normalizeScopeValue(topic.tenantId);
158
+ let workspaceId = normalizeScopeValue(topic.workspaceId);
159
+ if (tenantId && workspaceId) {
160
+ return { tenantId, workspaceId };
161
+ }
162
+ let current = topic;
163
+ for (let i = 0; i < MAX_DEPTH && current?.parentTopicId; i++) {
164
+ current = await ctx.db.get(current.parentTopicId);
165
+ if (!current) break;
166
+ if (!tenantId) {
167
+ tenantId = normalizeScopeValue(current.tenantId);
168
+ }
169
+ if (!workspaceId) {
170
+ workspaceId = normalizeScopeValue(current.workspaceId);
171
+ }
172
+ if (tenantId && workspaceId) break;
173
+ }
174
+ return { tenantId, workspaceId };
175
+ }
176
+ async function resolveTopicProjectScope(ctx, args) {
177
+ if (args.topicId) {
178
+ let topic = null;
179
+ try {
180
+ topic = await ctx.db.get(
181
+ args.topicId
182
+ );
183
+ } catch (error) {
184
+ debugGraphPrimitiveFallback(
185
+ "[topicScope] Failed to load topic by direct id",
186
+ {
187
+ error,
188
+ topicId: args.topicId
189
+ }
190
+ );
191
+ }
192
+ if (!topic) {
193
+ topic = await tryResolveHostTopicById(ctx, String(args.topicId));
194
+ }
195
+ if (!topic) {
196
+ topic = pickPrimaryTopic(
197
+ await findTopicsByScopeAlias(ctx, String(args.topicId))
198
+ ) ?? null;
199
+ }
200
+ if (!topic) {
201
+ throw new Error(`Topic not found: ${String(args.topicId)}`);
202
+ }
203
+ const inherited = await resolveInheritedWorkspaceScope(ctx, topic);
204
+ const mapped = asMappedProjectId(topic);
205
+ if (mapped) {
206
+ return {
207
+ topicId: topic._id,
208
+ projectId: mapped,
209
+ tenantId: inherited.tenantId,
210
+ workspaceId: inherited.workspaceId,
211
+ source: "topic"
212
+ };
213
+ }
214
+ return {
215
+ topicId: topic._id,
216
+ tenantId: inherited.tenantId,
217
+ workspaceId: inherited.workspaceId,
218
+ source: "topic"
219
+ };
220
+ }
221
+ if (args.projectId) {
222
+ let directTopic = null;
223
+ try {
224
+ directTopic = await ctx.db.get(
225
+ args.projectId
226
+ );
227
+ } catch (error) {
228
+ debugGraphPrimitiveFallback(
229
+ "[topicScope] Failed to load direct project topic",
230
+ {
231
+ error,
232
+ projectId: args.projectId
233
+ }
234
+ );
235
+ }
236
+ if (directTopic) {
237
+ const inherited = await resolveInheritedWorkspaceScope(ctx, directTopic);
238
+ const mapped = asMappedProjectId(directTopic);
239
+ return {
240
+ topicId: directTopic._id,
241
+ projectId: mapped ?? args.projectId,
242
+ tenantId: inherited.tenantId,
243
+ workspaceId: inherited.workspaceId,
244
+ source: "topic_inferred"
245
+ };
246
+ }
247
+ directTopic = await tryResolveHostTopicByLegacyScope(ctx, args.projectId);
248
+ if (directTopic) {
249
+ const inherited = await resolveInheritedWorkspaceScope(ctx, directTopic);
250
+ const mapped = asMappedProjectId(directTopic);
251
+ return {
252
+ topicId: directTopic._id,
253
+ projectId: mapped ?? args.projectId,
254
+ tenantId: inherited.tenantId,
255
+ workspaceId: inherited.workspaceId,
256
+ source: "topic_inferred"
257
+ };
258
+ }
259
+ const topics = await findTopicsByScopeAlias(ctx, args.projectId);
260
+ const primary = pickPrimaryTopic(topics);
261
+ if (primary) {
262
+ const inherited = await resolveInheritedWorkspaceScope(ctx, primary);
263
+ return {
264
+ topicId: primary._id,
265
+ projectId: args.projectId,
266
+ tenantId: inherited.tenantId,
267
+ workspaceId: inherited.workspaceId,
268
+ source: "project_mapped_topic"
269
+ };
270
+ }
271
+ throw new Error(
272
+ `Legacy project scope ${String(args.projectId)} has no mapped topic.`
273
+ );
274
+ }
275
+ throw new Error(
276
+ "Missing scope: provide topicId (preferred) or legacy projectId alias."
277
+ );
278
+ }
279
+ var optionalScopeArgs = {
280
+ projectId: v.optional(v.string()),
281
+ topicId: v.optional(v.string())
282
+ };
283
+
284
+ // src/beliefLifecycle.ts
285
+ var RESOLVED_PREDICTION_OUTCOMES = [
286
+ "confirmed",
287
+ "disconfirmed",
288
+ "partial",
289
+ "expired"
290
+ ];
291
+ function hasResolvedPredictionOutcome(predictionMeta) {
292
+ if (!predictionMeta || typeof predictionMeta !== "object") {
293
+ return false;
294
+ }
295
+ const outcome = predictionMeta.outcome;
296
+ return typeof outcome === "string" && RESOLVED_PREDICTION_OUTCOMES.includes(outcome);
297
+ }
298
+
299
+ // src/invariantEnforcement.ts
300
+ var FORBIDDEN_GENERIC_BELIEF_METADATA_KEYS = /* @__PURE__ */ new Set([
301
+ "beliefStatus",
302
+ "epistemicStatus",
303
+ "forkedBy",
304
+ "forkedFrom",
305
+ "forkReason",
306
+ "forkTimestamp",
307
+ "status",
308
+ "supersededBy",
309
+ "supersedes"
310
+ ]);
311
+ var ONTOLOGICAL_NODE_TYPES = /* @__PURE__ */ new Set([
312
+ "company",
313
+ "person",
314
+ "investor",
315
+ "function",
316
+ "value_chain"
317
+ ]);
318
+ function throwInvariantError(args) {
319
+ const error = new Error(args.message);
320
+ error.status = args.status ?? 409;
321
+ error.code = args.code ?? "INVARIANT_VIOLATION";
322
+ error.invariantCode = args.invariantCode;
323
+ error.suggestion = args.suggestion;
324
+ error.details = args.details;
325
+ throw error;
326
+ }
327
+ function isBeliefNode(node) {
328
+ return node?.nodeType === "belief";
329
+ }
330
+ function isOntologicalNode(node) {
331
+ return typeof node?.nodeType === "string" && ONTOLOGICAL_NODE_TYPES.has(node.nodeType);
332
+ }
333
+ function isScoredBeliefNode(node) {
334
+ if (!isBeliefNode(node)) {
335
+ return false;
336
+ }
337
+ const metadata = node.metadata && typeof node.metadata === "object" ? node.metadata : void 0;
338
+ const numericConfidence = typeof node.confidence === "number" && Number.isFinite(node.confidence);
339
+ if (numericConfidence) {
340
+ return true;
341
+ }
342
+ return hasResolvedPredictionOutcome(node.predictionMeta) || hasResolvedPredictionOutcome(metadata?.predictionMeta);
343
+ }
344
+ function getForbiddenMetadataKeys(metadata) {
345
+ if (!metadata || typeof metadata !== "object" || Array.isArray(metadata)) {
346
+ return [];
347
+ }
348
+ return Object.keys(metadata).filter(
349
+ (key) => FORBIDDEN_GENERIC_BELIEF_METADATA_KEYS.has(key)
350
+ );
351
+ }
352
+ function assertBeliefNodeGenericUpdateAllowed(args) {
353
+ if (!isBeliefNode(args.node)) {
354
+ return;
355
+ }
356
+ if (Object.hasOwn(args.updates, "confidence")) {
357
+ throwInvariantError({
358
+ message: "Belief confidence is append-only. Generic node updates cannot set confidence directly.",
359
+ invariantCode: "belief.confidence_append_only",
360
+ suggestion: "Use epistemicBeliefs.modulateConfidence() so the beliefConfidence ledger and audit trail are updated together.",
361
+ details: { mutationName: args.mutationName, nodeId: args.node._id }
362
+ });
363
+ }
364
+ if (Object.hasOwn(args.updates, "status")) {
365
+ throwInvariantError({
366
+ message: "Belief status transitions must use the dedicated belief lifecycle APIs.",
367
+ invariantCode: "belief.status_transition_requires_belief_api",
368
+ suggestion: "Use epistemicBeliefs.updateStatus() or epistemicBeliefs.archive() so status transitions emit the correct audit event.",
369
+ details: { mutationName: args.mutationName, nodeId: args.node._id }
370
+ });
371
+ }
372
+ const forbiddenMetadataKeys = getForbiddenMetadataKeys(args.updates.metadata);
373
+ if (forbiddenMetadataKeys.length > 0) {
374
+ throwInvariantError({
375
+ message: "Belief lineage and lifecycle metadata cannot be rewritten through generic node updates.",
376
+ invariantCode: "belief.lineage_requires_fork_belief",
377
+ suggestion: "Use epistemicBeliefs.forkBelief() for lineage changes and dedicated belief lifecycle mutations for status changes.",
378
+ details: {
379
+ mutationName: args.mutationName,
380
+ nodeId: args.node._id,
381
+ forbiddenMetadataKeys
382
+ }
383
+ });
384
+ }
385
+ if (isScoredBeliefNode(args.node) && (Object.hasOwn(args.updates, "canonicalText") || Object.hasOwn(args.updates, "contentHash"))) {
386
+ throwInvariantError({
387
+ message: "Cannot refine a scored belief in place. Scored formulations are immutable.",
388
+ invariantCode: "belief.formulation_immutable_after_scoring",
389
+ suggestion: "Use epistemicBeliefs.forkBelief() to evolve the formulation while preserving lineage.",
390
+ details: { mutationName: args.mutationName, nodeId: args.node._id }
391
+ });
392
+ }
393
+ }
394
+ function assertBeliefNodeArchiveAllowed(args) {
395
+ if (!isBeliefNode(args.node)) {
396
+ return;
397
+ }
398
+ throwInvariantError({
399
+ message: "Belief archiving must go through the dedicated belief lifecycle API.",
400
+ invariantCode: "belief.status_transition_requires_belief_api",
401
+ suggestion: "Use epistemicBeliefs.archive() so the belief lifecycle audit trail stays consistent.",
402
+ details: { mutationName: args.mutationName, nodeId: args.node._id }
403
+ });
404
+ }
405
+ function assertBeliefNodeVerifyAllowed(args) {
406
+ if (!isBeliefNode(args.node) || args.confidence === void 0) {
407
+ return;
408
+ }
409
+ throwInvariantError({
410
+ message: "Belief verification cannot set confidence directly. Confidence changes must stay append-only.",
411
+ invariantCode: "belief.confidence_append_only",
412
+ suggestion: "Call epistemicBeliefs.modulateConfidence() after verification so the confidence history is preserved.",
413
+ details: { mutationName: args.mutationName, nodeId: args.node._id }
414
+ });
415
+ }
416
+ function assertBeliefNodeSupersedeAllowed(args) {
417
+ if (!isBeliefNode(args.node)) {
418
+ return;
419
+ }
420
+ throwInvariantError({
421
+ message: "Belief lineage changes must use forkBelief(), not the generic supersede path.",
422
+ invariantCode: "belief.lineage_requires_fork_belief",
423
+ suggestion: "Use epistemicBeliefs.forkBelief() so the child belief, supersedes edge, and audit trail are created together.",
424
+ details: { mutationName: args.mutationName, nodeId: args.node._id }
425
+ });
426
+ }
427
+ function assertOntologicalNodeGenericCreateAllowed(args) {
428
+ if (!ONTOLOGICAL_NODE_TYPES.has(args.nodeType)) {
429
+ return;
430
+ }
431
+ throwInvariantError({
432
+ message: "Ontological entities must be created through the dedicated entity lifecycle API.",
433
+ invariantCode: "entity.create_requires_entity_lifecycle",
434
+ suggestion: "Use entityLifecycle.createEntity() so tenant-global canonical scope and deduplication are enforced.",
435
+ details: {
436
+ mutationName: args.mutationName,
437
+ nodeType: args.nodeType
438
+ }
439
+ });
440
+ }
441
+ function assertOntologicalNodeGenericUpdateAllowed(args) {
442
+ if (!isOntologicalNode(args.node)) {
443
+ return;
444
+ }
445
+ throwInvariantError({
446
+ message: "Ontological entities must be updated through the dedicated entity lifecycle API.",
447
+ invariantCode: "entity.update_requires_entity_lifecycle",
448
+ suggestion: "Use entityLifecycle.updateEntityAttributes() so canonical entity mutations stay type-safe and audited.",
449
+ details: {
450
+ mutationName: args.mutationName,
451
+ nodeId: args.node._id,
452
+ nodeType: args.node.nodeType
453
+ }
454
+ });
455
+ }
456
+ function assertOntologicalNodeArchiveAllowed(args) {
457
+ if (!isOntologicalNode(args.node)) {
458
+ return;
459
+ }
460
+ throwInvariantError({
461
+ message: "Ontological entities must be archived through the dedicated entity lifecycle API.",
462
+ invariantCode: "entity.archive_requires_entity_lifecycle",
463
+ suggestion: "Use entityLifecycle.archiveEntity() so entity archival emits the correct audit trail and review hooks.",
464
+ details: {
465
+ mutationName: args.mutationName,
466
+ nodeId: args.node._id,
467
+ nodeType: args.node.nodeType
468
+ }
469
+ });
470
+ }
471
+ function assertOntologicalNodeSupersedeAllowed(args) {
472
+ if (!isOntologicalNode(args.node)) {
473
+ return;
474
+ }
475
+ throwInvariantError({
476
+ message: "Ontological entities do not use the generic supersede path.",
477
+ invariantCode: "entity.supersede_requires_entity_lifecycle",
478
+ suggestion: "Use entityLifecycle.updateEntityAttributes() to edit an entity in place or entityLifecycle.mergeEntities() to collapse duplicates.",
479
+ details: {
480
+ mutationName: args.mutationName,
481
+ nodeId: args.node._id,
482
+ nodeType: args.node.nodeType
483
+ }
484
+ });
485
+ }
486
+ function normalizeScopeValue2(value) {
487
+ if (typeof value !== "string") {
488
+ return;
489
+ }
490
+ const normalized = value.trim();
491
+ return normalized.length > 0 ? normalized : void 0;
492
+ }
493
+ function throwWorkspaceIsolationError(args) {
494
+ const error = new Error(args.message);
495
+ error.status = 409;
496
+ error.code = "INVARIANT_VIOLATION";
497
+ error.invariantCode = args.invariantCode;
498
+ error.suggestion = args.suggestion;
499
+ error.details = args.details;
500
+ throw error;
501
+ }
502
+ function assertWorkspaceScopedEpistemicNodeScope(args) {
503
+ const layer = isNodeType(args.nodeType) ? getLayerForNodeType(args.nodeType) : void 0;
504
+ if (layer === "ontological") {
505
+ return;
506
+ }
507
+ const workspaceId = normalizeScopeValue2(args.scope.workspaceId);
508
+ if (workspaceId) {
509
+ return;
510
+ }
511
+ throwWorkspaceIsolationError({
512
+ message: "Workspace-scoped reasoning isolation requires workspaceId on non-ontological node creation.",
513
+ invariantCode: "workspace.scope_required_for_epistemic_nodes",
514
+ suggestion: "Resolve the topic/project scope through a workspace-bound topic before creating epistemic nodes.",
515
+ details: {
516
+ mutationName: args.mutationName,
517
+ nodeType: args.nodeType,
518
+ topicId: args.scope.topicId,
519
+ projectId: args.scope.projectId
520
+ }
521
+ });
522
+ }
523
+ function resolveRuntimePackMutationContext(args) {
524
+ if (!args.runtimeToolName && !args.runtimePackKey && !args.runtimePackInstallScope) {
525
+ return;
526
+ }
527
+ return {
528
+ toolName: args.runtimeToolName,
529
+ packKey: args.runtimePackKey,
530
+ packInstallScope: args.runtimePackInstallScope
531
+ };
532
+ }
533
+ function assertTenantPackWorkspaceMutationAllowed(args) {
534
+ if (!args.runtime?.packKey || args.runtime.packInstallScope !== "tenant") {
535
+ return;
536
+ }
537
+ const targetWorkspaceId = normalizeScopeValue2(args.target.workspaceId);
538
+ const targetLayer = typeof args.target.epistemicLayer === "string" ? args.target.epistemicLayer : void 0;
539
+ if (!targetWorkspaceId || targetLayer === "ontological") {
540
+ return;
541
+ }
542
+ throwWorkspaceIsolationError({
543
+ message: `Tenant-scoped pack "${args.runtime.packKey}" cannot mutate workspace-scoped reasoning state.`,
544
+ invariantCode: "workspace.tenant_pack_reasoning_write_forbidden",
545
+ suggestion: "Use a workspace-scoped pack for workspace-local graph mutations, or route the change through tenant-global canonical entity flows.",
546
+ details: {
547
+ mutationName: args.mutationName,
548
+ toolName: args.runtime.toolName,
549
+ packKey: args.runtime.packKey,
550
+ targetWorkspaceId,
551
+ targetNodeType: args.target.nodeType,
552
+ targetLayer
553
+ }
554
+ });
555
+ }
556
+
557
+ // src/epistemicNodes.helpers.ts
558
+ function buildNodeStatusSuccessResult() {
559
+ return { success: true };
560
+ }
561
+ function buildNodeArchivedResult() {
562
+ return {
563
+ success: true,
564
+ effectiveStatus: "archived"
565
+ };
566
+ }
567
+ v.union(
568
+ v.literal("L4"),
569
+ v.literal("L3"),
570
+ v.literal("L2"),
571
+ v.literal("L1"),
572
+ v.literal("ontological"),
573
+ v.literal("organizational")
574
+ );
575
+ v.union(v.literal("decision"));
576
+ v.union(
577
+ v.literal("belief"),
578
+ v.literal("question"),
579
+ v.literal("theme"),
580
+ v.literal("deal")
581
+ );
582
+ v.union(
583
+ v.literal("claim"),
584
+ v.literal("evidence"),
585
+ v.literal("synthesis")
586
+ );
587
+ v.union(
588
+ v.literal("atomic_fact"),
589
+ v.literal("excerpt"),
590
+ v.literal("source")
591
+ );
592
+ v.union(
593
+ // L4: Audit targets
594
+ v.literal("decision"),
595
+ // L3: Traversal anchors
596
+ v.literal("belief"),
597
+ v.literal("question"),
598
+ v.literal("theme"),
599
+ v.literal("deal"),
600
+ // L2: Compression boundary
601
+ v.literal("claim"),
602
+ v.literal("evidence"),
603
+ v.literal("synthesis"),
604
+ v.literal("answer"),
605
+ // L1: Terminal leaves
606
+ v.literal("atomic_fact"),
607
+ v.literal("excerpt"),
608
+ v.literal("source")
609
+ );
610
+ v.union(
611
+ v.literal("company"),
612
+ v.literal("person"),
613
+ v.literal("investor"),
614
+ v.literal("function"),
615
+ v.literal("value_chain")
616
+ );
617
+ v.union(v.literal("topic"));
618
+ var nodeTypeValidator = v.union(
619
+ // L4: Audit targets
620
+ v.literal("decision"),
621
+ // L3: Traversal anchors
622
+ v.literal("belief"),
623
+ v.literal("question"),
624
+ v.literal("theme"),
625
+ v.literal("deal"),
626
+ // L2: Compression boundary
627
+ v.literal("claim"),
628
+ v.literal("evidence"),
629
+ v.literal("synthesis"),
630
+ v.literal("answer"),
631
+ // L1: Terminal leaves
632
+ v.literal("atomic_fact"),
633
+ v.literal("excerpt"),
634
+ v.literal("source"),
635
+ // Ontological
636
+ v.literal("company"),
637
+ v.literal("person"),
638
+ v.literal("investor"),
639
+ v.literal("function"),
640
+ v.literal("value_chain"),
641
+ // Organizational
642
+ v.literal("topic")
643
+ );
644
+ var sourceTypeValidator = v.union(
645
+ v.literal("human"),
646
+ v.literal("ai_extracted"),
647
+ v.literal("ai_generated"),
648
+ v.literal("imported"),
649
+ v.literal("system")
650
+ // System-generated (migrations, classifiers)
651
+ );
652
+ var statusValidator = v.union(
653
+ v.literal("active"),
654
+ v.literal("superseded"),
655
+ v.literal("archived"),
656
+ v.literal("deleted")
657
+ );
658
+ var verificationStatusValidator = v.union(
659
+ v.literal("unverified"),
660
+ v.literal("human_verified"),
661
+ v.literal("ai_verified"),
662
+ v.literal("contradicted"),
663
+ v.literal("outdated")
664
+ );
665
+
666
+ // src/epistemicNodes.mutations.ts
667
+ var optionalNodeScopeArgs = optionalScopeArgs;
668
+ var create = mutation({
669
+ args: {
670
+ globalId: v.string(),
671
+ nodeType: nodeTypeValidator,
672
+ subtype: v.optional(v.string()),
673
+ // company: private|corporate|portfolio, investor: vc|lp|cvc|pe|etc
674
+ canonicalText: v.string(),
675
+ contentHash: v.string(),
676
+ content: v.optional(v.string()),
677
+ contentType: v.optional(v.string()),
678
+ title: v.optional(v.string()),
679
+ tags: v.optional(v.array(v.string())),
680
+ domain: v.optional(v.string()),
681
+ // For companies: website domain
682
+ metadata: v.optional(v.any()),
683
+ externalIds: v.optional(
684
+ v.object({
685
+ crunchbase: v.optional(v.string()),
686
+ linkedin: v.optional(v.string()),
687
+ pitchbook: v.optional(v.string()),
688
+ twitter: v.optional(v.string()),
689
+ website: v.optional(v.string())
690
+ })
691
+ ),
692
+ sourceType: sourceTypeValidator,
693
+ aiProvider: v.optional(v.string()),
694
+ extractedFromNodeId: v.optional(v.id("epistemicNodes")),
695
+ confidence: v.optional(v.number()),
696
+ verificationStatus: v.optional(verificationStatusValidator),
697
+ ...optionalNodeScopeArgs,
698
+ createdBy: v.string(),
699
+ runtimeToolName: v.optional(v.string()),
700
+ runtimePackKey: v.optional(v.string()),
701
+ runtimePackInstallScope: v.optional(
702
+ v.union(v.literal("tenant"), v.literal("workspace"))
703
+ )
704
+ },
705
+ returns: permissiveReturn,
706
+ handler: async (ctx, args) => {
707
+ const now = Date.now();
708
+ assertOntologicalNodeGenericCreateAllowed({
709
+ nodeType: args.nodeType,
710
+ mutationName: "epistemicNodes.create"
711
+ });
712
+ const existing = await ctx.db.query("epistemicNodes").withIndex("by_contentHash", (q) => q.eq("contentHash", args.contentHash)).first();
713
+ if (existing && existing.status === "active") {
714
+ return { nodeId: existing._id, isDuplicate: true };
715
+ }
716
+ const epistemicLayer = getNodeLayer(args.nodeType);
717
+ const resolvedScope = args.topicId || args.projectId ? await resolveTopicProjectScope(ctx, {
718
+ topicId: args.topicId,
719
+ projectId: args.projectId
720
+ }) : void 0;
721
+ if (resolvedScope) {
722
+ assertWorkspaceScopedEpistemicNodeScope({
723
+ scope: resolvedScope,
724
+ nodeType: args.nodeType,
725
+ mutationName: "epistemicNodes.create"
726
+ });
727
+ assertTenantPackWorkspaceMutationAllowed({
728
+ runtime: resolveRuntimePackMutationContext(args),
729
+ target: {
730
+ tenantId: resolvedScope.tenantId,
731
+ workspaceId: resolvedScope.workspaceId,
732
+ nodeType: args.nodeType,
733
+ epistemicLayer
734
+ },
735
+ mutationName: "epistemicNodes.create"
736
+ });
737
+ } else if (epistemicLayer !== "ontological") {
738
+ throw new Error(
739
+ "Workspace-scoped reasoning isolation requires topicId or projectId for non-ontological node creation."
740
+ );
741
+ }
742
+ const nodeId = await ctx.db.insert("epistemicNodes", {
743
+ globalId: args.globalId,
744
+ nodeType: args.nodeType,
745
+ epistemicLayer,
746
+ // Phase 2B: Auto-derived from nodeType
747
+ subtype: args.subtype,
748
+ canonicalText: args.canonicalText,
749
+ contentHash: args.contentHash,
750
+ content: args.content,
751
+ contentType: args.contentType,
752
+ title: args.title,
753
+ tags: args.tags,
754
+ domain: args.domain,
755
+ metadata: args.metadata,
756
+ externalIds: args.externalIds,
757
+ sourceType: args.sourceType,
758
+ aiProvider: args.aiProvider,
759
+ extractedFromNodeId: args.extractedFromNodeId,
760
+ confidence: args.confidence,
761
+ verificationStatus: args.verificationStatus ?? "unverified",
762
+ status: "active",
763
+ topicId: resolvedScope?.topicId ?? args.topicId,
764
+ projectId: resolvedScope?.projectId ?? args.projectId,
765
+ tenantId: resolvedScope?.tenantId,
766
+ workspaceId: resolvedScope?.workspaceId,
767
+ createdBy: args.createdBy,
768
+ createdAt: now,
769
+ updatedAt: now
770
+ });
771
+ await ctx.db.insert("epistemicAudit", {
772
+ entityType: "node",
773
+ entityId: String(nodeId),
774
+ changeType: "created",
775
+ changedAt: now,
776
+ changedBy: args.createdBy,
777
+ isAgent: false,
778
+ newState: {
779
+ nodeType: args.nodeType,
780
+ canonicalText: args.canonicalText.slice(0, 200),
781
+ status: "active",
782
+ verificationStatus: args.verificationStatus ?? "unverified",
783
+ confidence: args.confidence
784
+ },
785
+ projectId: resolvedScope?.projectId ?? args.projectId,
786
+ triggeringAction: "epistemicNodes.create"
787
+ });
788
+ await ctx.scheduler.runAfter(0, internal.neo4jSync.syncNodeToNeo4j, {
789
+ nodeId,
790
+ operation: "upsert"
791
+ });
792
+ return { nodeId, isDuplicate: false };
793
+ }
794
+ });
795
+ var update = mutation({
796
+ args: {
797
+ nodeId: v.id("epistemicNodes"),
798
+ subtype: v.optional(v.string()),
799
+ canonicalText: v.optional(v.string()),
800
+ contentHash: v.optional(v.string()),
801
+ content: v.optional(v.string()),
802
+ contentType: v.optional(v.string()),
803
+ title: v.optional(v.string()),
804
+ tags: v.optional(v.array(v.string())),
805
+ domain: v.optional(v.string()),
806
+ metadata: v.optional(v.any()),
807
+ externalIds: v.optional(
808
+ v.object({
809
+ crunchbase: v.optional(v.string()),
810
+ linkedin: v.optional(v.string()),
811
+ pitchbook: v.optional(v.string()),
812
+ twitter: v.optional(v.string()),
813
+ website: v.optional(v.string())
814
+ })
815
+ ),
816
+ confidence: v.optional(v.number()),
817
+ verificationStatus: v.optional(verificationStatusValidator),
818
+ status: v.optional(statusValidator),
819
+ userId: v.optional(v.string()),
820
+ // EK-4: SL opinion fields (Kernel v2)
821
+ opinion_b: v.optional(v.number()),
822
+ opinion_d: v.optional(v.number()),
823
+ opinion_u: v.optional(v.number()),
824
+ opinion_a: v.optional(v.number())
825
+ },
826
+ returns: permissiveReturn,
827
+ handler: async (ctx, args) => {
828
+ const { nodeId, userId, ...updates } = args;
829
+ const node = await ctx.db.get(nodeId);
830
+ if (!node) {
831
+ throw new Error("Node not found");
832
+ }
833
+ if (node.projectId && userId) {
834
+ await requireProjectAccess(ctx, node.projectId, userId);
835
+ }
836
+ const cleanUpdates = {};
837
+ for (const [key, value] of Object.entries(updates)) {
838
+ if (value !== void 0) {
839
+ cleanUpdates[key] = value;
840
+ }
841
+ }
842
+ if (node.nodeType === "belief") {
843
+ assertBeliefNodeGenericUpdateAllowed({
844
+ node,
845
+ updates: cleanUpdates,
846
+ mutationName: "epistemicNodes.update"
847
+ });
848
+ }
849
+ assertOntologicalNodeGenericUpdateAllowed({
850
+ node,
851
+ mutationName: "epistemicNodes.update"
852
+ });
853
+ cleanUpdates.updatedAt = Date.now();
854
+ await ctx.db.patch(nodeId, cleanUpdates);
855
+ if (node.projectId) {
856
+ await ctx.db.insert("epistemicAudit", {
857
+ entityType: "node",
858
+ entityId: String(nodeId),
859
+ changeType: "updated",
860
+ changedAt: Date.now(),
861
+ changedBy: userId || node.createdBy,
862
+ isAgent: false,
863
+ previousState: {
864
+ status: node.status,
865
+ confidence: node.confidence,
866
+ canonicalText: node.canonicalText?.slice(0, 200)
867
+ },
868
+ newState: cleanUpdates,
869
+ projectId: node.projectId
870
+ });
871
+ }
872
+ await ctx.scheduler.runAfter(0, internal.neo4jSync.syncNodeToNeo4j, {
873
+ nodeId,
874
+ operation: "upsert"
875
+ });
876
+ return buildNodeStatusSuccessResult();
877
+ }
878
+ });
879
+ var supersede = mutation({
880
+ args: {
881
+ oldNodeId: v.id("epistemicNodes"),
882
+ newGlobalId: v.string(),
883
+ newCanonicalText: v.string(),
884
+ newContentHash: v.string(),
885
+ createdBy: v.string(),
886
+ reason: v.optional(v.string())
887
+ },
888
+ returns: permissiveReturn,
889
+ handler: async (ctx, args) => {
890
+ const oldNode = await ctx.db.get(args.oldNodeId);
891
+ if (!oldNode) {
892
+ throw new Error("Node not found");
893
+ }
894
+ if (oldNode.projectId) {
895
+ await requireProjectAccess(ctx, oldNode.projectId, args.createdBy);
896
+ }
897
+ assertBeliefNodeSupersedeAllowed({
898
+ node: oldNode,
899
+ mutationName: "epistemicNodes.supersede"
900
+ });
901
+ assertOntologicalNodeSupersedeAllowed({
902
+ node: oldNode,
903
+ mutationName: "epistemicNodes.supersede"
904
+ });
905
+ const now = Date.now();
906
+ const epistemicLayer = oldNode.epistemicLayer || getNodeLayer(oldNode.nodeType);
907
+ const newNodeId = await ctx.db.insert("epistemicNodes", {
908
+ globalId: args.newGlobalId,
909
+ nodeType: oldNode.nodeType,
910
+ epistemicLayer,
911
+ // Phase 2B: Inherit layer (supersession is same-layer only)
912
+ canonicalText: args.newCanonicalText,
913
+ contentHash: args.newContentHash,
914
+ content: oldNode.content,
915
+ contentType: oldNode.contentType,
916
+ title: oldNode.title,
917
+ tags: oldNode.tags,
918
+ metadata: oldNode.metadata,
919
+ sourceType: "human",
920
+ // Supersession is always human-initiated
921
+ confidence: oldNode.confidence,
922
+ verificationStatus: "unverified",
923
+ // New version needs re-verification
924
+ status: "active",
925
+ projectId: oldNode.projectId,
926
+ createdBy: args.createdBy,
927
+ createdAt: now,
928
+ updatedAt: now
929
+ });
930
+ await ctx.db.patch(args.oldNodeId, {
931
+ status: "superseded",
932
+ supersededBy: newNodeId,
933
+ updatedAt: now
934
+ });
935
+ if (oldNode.projectId) {
936
+ await ctx.db.insert("epistemicAudit", {
937
+ entityType: "node",
938
+ entityId: String(args.oldNodeId),
939
+ changeType: "superseded",
940
+ changedAt: now,
941
+ changedBy: args.createdBy,
942
+ isAgent: false,
943
+ previousState: {
944
+ status: oldNode.status,
945
+ canonicalText: oldNode.canonicalText?.slice(0, 200)
946
+ },
947
+ newState: {
948
+ status: "superseded",
949
+ supersededBy: String(newNodeId),
950
+ newCanonicalText: args.newCanonicalText.slice(0, 200)
951
+ },
952
+ rationale: args.reason,
953
+ projectId: oldNode.projectId
954
+ });
955
+ }
956
+ return { newNodeId, oldNodeId: args.oldNodeId };
957
+ }
958
+ });
959
+ var archive = mutation({
960
+ args: {
961
+ nodeId: v.id("epistemicNodes"),
962
+ userId: v.optional(v.string())
963
+ },
964
+ returns: permissiveReturn,
965
+ handler: async (ctx, args) => {
966
+ const node = await ctx.db.get(args.nodeId);
967
+ if (!node) {
968
+ throw new Error("Node not found");
969
+ }
970
+ if (node.projectId && args.userId) {
971
+ await requireProjectAccess(ctx, node.projectId, args.userId);
972
+ }
973
+ assertBeliefNodeArchiveAllowed({
974
+ node,
975
+ mutationName: "epistemicNodes.archive"
976
+ });
977
+ assertOntologicalNodeArchiveAllowed({
978
+ node,
979
+ mutationName: "epistemicNodes.archive"
980
+ });
981
+ await ctx.db.patch(args.nodeId, {
982
+ status: "archived",
983
+ updatedAt: Date.now()
984
+ });
985
+ if (node.projectId) {
986
+ await ctx.db.insert("epistemicAudit", {
987
+ entityType: "node",
988
+ entityId: String(args.nodeId),
989
+ changeType: "archived",
990
+ changedAt: Date.now(),
991
+ changedBy: args.userId || node.createdBy,
992
+ isAgent: false,
993
+ previousState: { status: node.status },
994
+ newState: { status: "archived" },
995
+ projectId: node.projectId
996
+ });
997
+ }
998
+ return buildNodeArchivedResult();
999
+ }
1000
+ });
1001
+ var verify = mutation({
1002
+ args: {
1003
+ nodeId: v.id("epistemicNodes"),
1004
+ verificationStatus: verificationStatusValidator,
1005
+ confidence: v.optional(v.number()),
1006
+ userId: v.optional(v.string())
1007
+ },
1008
+ returns: permissiveReturn,
1009
+ handler: async (ctx, args) => {
1010
+ const node = await ctx.db.get(args.nodeId);
1011
+ if (!node) {
1012
+ throw new Error("Node not found");
1013
+ }
1014
+ assertBeliefNodeVerifyAllowed({
1015
+ node,
1016
+ confidence: args.confidence,
1017
+ mutationName: "epistemicNodes.verify"
1018
+ });
1019
+ const updates = {
1020
+ verificationStatus: args.verificationStatus,
1021
+ updatedAt: Date.now()
1022
+ };
1023
+ if (args.confidence !== void 0) {
1024
+ updates.confidence = args.confidence;
1025
+ }
1026
+ await ctx.db.patch(args.nodeId, updates);
1027
+ await ctx.db.insert("epistemicAudit", {
1028
+ entityType: "node",
1029
+ entityId: String(args.nodeId),
1030
+ changeType: "updated",
1031
+ changedAt: Date.now(),
1032
+ changedBy: args.userId ?? node.createdBy ?? "system:verification",
1033
+ isAgent: false,
1034
+ previousState: {
1035
+ verificationStatus: node.verificationStatus,
1036
+ confidence: node.confidence
1037
+ },
1038
+ newState: {
1039
+ verificationStatus: args.verificationStatus,
1040
+ confidence: args.confidence ?? node.confidence
1041
+ },
1042
+ projectId: node.projectId,
1043
+ triggeringAction: "epistemicNodes.verify"
1044
+ });
1045
+ return buildNodeStatusSuccessResult();
1046
+ }
1047
+ });
1048
+ var batchCreate = mutation({
1049
+ args: {
1050
+ nodes: v.array(
1051
+ v.object({
1052
+ ...optionalNodeScopeArgs,
1053
+ globalId: v.string(),
1054
+ nodeType: nodeTypeValidator,
1055
+ subtype: v.optional(v.string()),
1056
+ canonicalText: v.string(),
1057
+ contentHash: v.string(),
1058
+ content: v.optional(v.string()),
1059
+ contentType: v.optional(v.string()),
1060
+ title: v.optional(v.string()),
1061
+ tags: v.optional(v.array(v.string())),
1062
+ domain: v.optional(v.string()),
1063
+ metadata: v.optional(v.any()),
1064
+ externalIds: v.optional(
1065
+ v.object({
1066
+ crunchbase: v.optional(v.string()),
1067
+ linkedin: v.optional(v.string()),
1068
+ pitchbook: v.optional(v.string()),
1069
+ twitter: v.optional(v.string()),
1070
+ website: v.optional(v.string())
1071
+ })
1072
+ ),
1073
+ sourceType: sourceTypeValidator,
1074
+ aiProvider: v.optional(v.string()),
1075
+ confidence: v.optional(v.number()),
1076
+ verificationStatus: v.optional(verificationStatusValidator),
1077
+ createdBy: v.string()
1078
+ })
1079
+ )
1080
+ },
1081
+ returns: permissiveReturn,
1082
+ handler: async (ctx, args) => {
1083
+ const resolveNodeScope = async (node) => {
1084
+ if (!(node.topicId || node.projectId)) {
1085
+ return void 0;
1086
+ }
1087
+ try {
1088
+ return await resolveTopicProjectScope(ctx, {
1089
+ topicId: node.topicId,
1090
+ projectId: node.projectId
1091
+ });
1092
+ } catch (error) {
1093
+ debugGraphPrimitiveFallback(
1094
+ "[epistemicNodes] Failed to resolve scope for batch create node",
1095
+ {
1096
+ error,
1097
+ topicId: node.topicId,
1098
+ projectId: node.projectId
1099
+ }
1100
+ );
1101
+ return void 0;
1102
+ }
1103
+ };
1104
+ const now = Date.now();
1105
+ const results = [];
1106
+ for (const node of args.nodes) {
1107
+ assertOntologicalNodeGenericCreateAllowed({
1108
+ nodeType: node.nodeType,
1109
+ mutationName: "epistemicNodes.batchCreate"
1110
+ });
1111
+ const epistemicLayer = getNodeLayer(node.nodeType);
1112
+ const resolvedScope = await resolveNodeScope(node);
1113
+ const nodeId = await ctx.db.insert("epistemicNodes", {
1114
+ ...node,
1115
+ epistemicLayer,
1116
+ // Phase 2B: Auto-derived from nodeType
1117
+ verificationStatus: node.verificationStatus ?? "unverified",
1118
+ status: "active",
1119
+ topicId: resolvedScope?.topicId ?? node.topicId,
1120
+ projectId: resolvedScope?.projectId ?? node.projectId,
1121
+ createdAt: now,
1122
+ updatedAt: now
1123
+ });
1124
+ results.push({ globalId: node.globalId, nodeId });
1125
+ await ctx.db.insert("epistemicAudit", {
1126
+ entityType: "node",
1127
+ entityId: String(nodeId),
1128
+ changeType: "created",
1129
+ changedAt: now,
1130
+ changedBy: node.createdBy,
1131
+ isAgent: false,
1132
+ newState: {
1133
+ nodeType: node.nodeType,
1134
+ canonicalText: node.canonicalText.slice(0, 200),
1135
+ status: "active",
1136
+ verificationStatus: node.verificationStatus ?? "unverified",
1137
+ confidence: node.confidence
1138
+ },
1139
+ projectId: resolvedScope?.projectId ?? node.projectId,
1140
+ triggeringAction: "epistemicNodes.batchCreate"
1141
+ });
1142
+ await ctx.scheduler.runAfter(0, internal.neo4jSync.syncNodeToNeo4j, {
1143
+ nodeId,
1144
+ operation: "upsert"
1145
+ });
1146
+ }
1147
+ return { created: results.length, results };
1148
+ }
1149
+ });
1150
+
1151
+ export { archive, batchCreate, create, supersede, update, verify };
1152
+ //# sourceMappingURL=epistemicNodes.mutations.js.map
1153
+ //# sourceMappingURL=epistemicNodes.mutations.js.map