@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.
- package/README.md +13 -12
- package/dist/beliefDecay.js +24 -17
- package/dist/beliefDecay.js.map +1 -1
- package/dist/beliefEvidenceLinks.js +32 -8
- package/dist/beliefEvidenceLinks.js.map +1 -1
- package/dist/confidencePropagationDispatch.js.map +1 -1
- package/dist/contradictions.js +32 -9
- package/dist/contradictions.js.map +1 -1
- package/dist/convex.d.ts +55 -12
- package/dist/convex.js.map +1 -1
- package/dist/edgeValidation.d.ts +25 -2
- package/dist/edges/index.d.ts +9 -2
- package/dist/edges/index.js.map +1 -1
- package/dist/edges/propagationTypes.d.ts +2 -3
- package/dist/edges/propagationTypes.js.map +1 -1
- package/dist/entityBridge.js +10 -3
- package/dist/entityBridge.js.map +1 -1
- package/dist/entityLifecycle.js +15 -3
- package/dist/entityLifecycle.js.map +1 -1
- package/dist/epistemicAnswers.js.map +1 -1
- package/dist/epistemicBeliefs.admin.d.ts +36 -0
- package/dist/epistemicBeliefs.admin.js +745 -0
- package/dist/epistemicBeliefs.admin.js.map +1 -0
- package/dist/epistemicBeliefs.backfills.d.ts +62 -0
- package/dist/epistemicBeliefs.backfills.js +1004 -0
- package/dist/epistemicBeliefs.backfills.js.map +1 -0
- package/dist/epistemicBeliefs.confidence.d.ts +45 -0
- package/dist/epistemicBeliefs.confidence.js +1285 -0
- package/dist/epistemicBeliefs.confidence.js.map +1 -0
- package/dist/epistemicBeliefs.core.d.ts +35 -0
- package/dist/epistemicBeliefs.core.js +1508 -0
- package/dist/epistemicBeliefs.core.js.map +1 -0
- package/dist/epistemicBeliefs.d.ts +12 -3
- package/dist/epistemicBeliefs.helpers.d.ts +168 -0
- package/dist/epistemicBeliefs.helpers.js +1060 -0
- package/dist/epistemicBeliefs.helpers.js.map +1 -0
- package/dist/epistemicBeliefs.internal.d.ts +30 -0
- package/dist/epistemicBeliefs.internal.js +1329 -0
- package/dist/epistemicBeliefs.internal.js.map +1 -0
- package/dist/epistemicBeliefs.js +1196 -1184
- package/dist/epistemicBeliefs.js.map +1 -1
- package/dist/epistemicBeliefs.lifecycle.d.ts +19 -0
- package/dist/epistemicBeliefs.lifecycle.js +1608 -0
- package/dist/epistemicBeliefs.lifecycle.js.map +1 -0
- package/dist/epistemicBeliefs.links.d.ts +30 -0
- package/dist/epistemicBeliefs.links.js +761 -0
- package/dist/epistemicBeliefs.links.js.map +1 -0
- package/dist/epistemicBeliefs.queries.d.ts +16 -0
- package/dist/epistemicBeliefs.queries.js +90 -0
- package/dist/epistemicBeliefs.queries.js.map +1 -0
- package/dist/epistemicContractHelpers.d.ts +1 -1
- package/dist/epistemicContractHelpers.js +1 -1
- package/dist/epistemicContracts.d.ts +5 -76
- package/dist/epistemicContracts.evaluators.d.ts +36 -0
- package/dist/epistemicContracts.evaluators.js +2506 -0
- package/dist/epistemicContracts.evaluators.js.map +1 -0
- package/dist/epistemicContracts.handlers.d.ts +40 -0
- package/dist/epistemicContracts.handlers.js +3029 -0
- package/dist/epistemicContracts.handlers.js.map +1 -0
- package/dist/epistemicContracts.js +2006 -5281
- package/dist/epistemicContracts.js.map +1 -1
- package/dist/epistemicContracts.metrics.d.ts +26 -0
- package/dist/epistemicContracts.metrics.js +427 -0
- package/dist/epistemicContracts.metrics.js.map +1 -0
- package/dist/epistemicContracts.types.d.ts +159 -0
- package/dist/epistemicContracts.types.js +3 -0
- package/dist/epistemicContracts.types.js.map +1 -0
- package/dist/epistemicEdgeCreation.d.ts +73 -0
- package/dist/epistemicEdgeCreation.js +450 -0
- package/dist/epistemicEdgeCreation.js.map +1 -0
- package/dist/epistemicEdges-BF-cn4i3.d.ts +43 -0
- package/dist/epistemicEdges.d.ts +8 -1
- package/dist/epistemicEdges.handlers.d.ts +20 -0
- package/dist/epistemicEdges.handlers.js +289 -0
- package/dist/epistemicEdges.handlers.js.map +1 -0
- package/dist/epistemicEdges.helpers.d.ts +27 -0
- package/dist/epistemicEdges.helpers.js +162 -0
- package/dist/epistemicEdges.helpers.js.map +1 -0
- package/dist/epistemicEdges.js +797 -875
- package/dist/epistemicEdges.js.map +1 -1
- package/dist/epistemicEdges.mutations.d.ts +39 -0
- package/dist/epistemicEdges.mutations.js +1365 -0
- package/dist/epistemicEdges.mutations.js.map +1 -0
- package/dist/epistemicEdges.queries.d.ts +95 -0
- package/dist/epistemicEdges.queries.js +851 -0
- package/dist/epistemicEdges.queries.js.map +1 -0
- package/dist/epistemicEdges.types.d.ts +32 -0
- package/dist/epistemicEdges.types.js +3 -0
- package/dist/epistemicEdges.types.js.map +1 -0
- package/dist/epistemicEvidence-DvfchNt7.d.ts +46 -0
- package/dist/epistemicEvidence.d.ts +5 -2
- package/dist/epistemicEvidence.js +801 -807
- package/dist/epistemicEvidence.js.map +1 -1
- package/dist/epistemicEvidenceHelpers.d.ts +71 -0
- package/dist/epistemicEvidenceHelpers.js +769 -0
- package/dist/epistemicEvidenceHelpers.js.map +1 -0
- package/dist/epistemicEvidenceMutations.d.ts +10 -0
- package/dist/epistemicEvidenceMutations.js +1421 -0
- package/dist/epistemicEvidenceMutations.js.map +1 -0
- package/dist/epistemicEvidenceQueries.d.ts +10 -0
- package/dist/epistemicEvidenceQueries.js +1049 -0
- package/dist/epistemicEvidenceQueries.js.map +1 -0
- package/dist/epistemicHelpers.d.ts +4 -2
- package/dist/epistemicHelpers.js +132 -127
- package/dist/epistemicHelpers.js.map +1 -1
- package/dist/epistemicLayerRules.d.ts +138 -0
- package/dist/epistemicLayerRules.js +481 -0
- package/dist/epistemicLayerRules.js.map +1 -0
- package/dist/epistemicLinking.js +1 -1
- package/dist/epistemicLinking.js.map +1 -1
- package/dist/epistemicNodeCreation.d.ts +101 -0
- package/dist/epistemicNodeCreation.js +709 -0
- package/dist/epistemicNodeCreation.js.map +1 -0
- package/dist/epistemicNodes-BCQxpYx_.d.ts +54 -0
- package/dist/epistemicNodes.d.ts +5 -1
- package/dist/epistemicNodes.helpers.d.ts +51 -0
- package/dist/epistemicNodes.helpers.js +73 -0
- package/dist/epistemicNodes.helpers.js.map +1 -0
- package/dist/epistemicNodes.internal.d.ts +34 -0
- package/dist/epistemicNodes.internal.js +658 -0
- package/dist/epistemicNodes.internal.js.map +1 -0
- package/dist/epistemicNodes.js +698 -693
- package/dist/epistemicNodes.js.map +1 -1
- package/dist/epistemicNodes.mutations.d.ts +34 -0
- package/dist/epistemicNodes.mutations.js +1153 -0
- package/dist/epistemicNodes.mutations.js.map +1 -0
- package/dist/epistemicNodes.queries.d.ts +36 -0
- package/dist/epistemicNodes.queries.js +619 -0
- package/dist/epistemicNodes.queries.js.map +1 -0
- package/dist/epistemicNodes.validators.d.ts +23 -0
- package/dist/epistemicNodes.validators.js +105 -0
- package/dist/epistemicNodes.validators.js.map +1 -0
- package/dist/epistemicQuestions-bwHd2FWE.d.ts +68 -0
- package/dist/epistemicQuestions.conviction.d.ts +52 -0
- package/dist/epistemicQuestions.conviction.js +1389 -0
- package/dist/epistemicQuestions.conviction.js.map +1 -0
- package/dist/epistemicQuestions.create.d.ts +29 -0
- package/dist/epistemicQuestions.create.js +1300 -0
- package/dist/epistemicQuestions.create.js.map +1 -0
- package/dist/epistemicQuestions.d.ts +10 -2
- package/dist/epistemicQuestions.evidence.d.ts +22 -0
- package/dist/epistemicQuestions.evidence.js +929 -0
- package/dist/epistemicQuestions.evidence.js.map +1 -0
- package/dist/epistemicQuestions.helpers.d.ts +69 -0
- package/dist/epistemicQuestions.helpers.js +824 -0
- package/dist/epistemicQuestions.helpers.js.map +1 -0
- package/dist/epistemicQuestions.js +2435 -2430
- package/dist/epistemicQuestions.js.map +1 -1
- package/dist/epistemicQuestions.lifecycle.d.ts +24 -0
- package/dist/epistemicQuestions.lifecycle.js +838 -0
- package/dist/epistemicQuestions.lifecycle.js.map +1 -0
- package/dist/epistemicQuestions.queries.d.ts +41 -0
- package/dist/epistemicQuestions.queries.js +1013 -0
- package/dist/epistemicQuestions.queries.js.map +1 -0
- package/dist/epistemicQuestions.sprint.d.ts +22 -0
- package/dist/epistemicQuestions.sprint.js +757 -0
- package/dist/epistemicQuestions.sprint.js.map +1 -0
- package/dist/epistemicQuestions.tail.d.ts +42 -0
- package/dist/epistemicQuestions.tail.js +1345 -0
- package/dist/epistemicQuestions.tail.js.map +1 -0
- package/dist/epistemicSources.js +6 -2
- package/dist/epistemicSources.js.map +1 -1
- package/dist/evaluators/index.d.ts +2 -2
- package/dist/evaluators/index.js +45 -5320
- package/dist/evaluators/index.js.map +1 -1
- package/dist/evaluators/lintCheckerEvaluator.d.ts +1 -1
- package/dist/evaluators/sentryCheckerEvaluator.d.ts +1 -1
- package/dist/evaluators/testRunnerEvaluator.d.ts +1 -1
- package/dist/evaluators/tscCheckerEvaluator.d.ts +1 -1
- package/dist/{graphTypes-CpgIuCdo.d.ts → graphTypes-B8VaIjnl.d.ts} +1 -1
- package/dist/graphTypes.d.ts +1 -1
- package/dist/{helpers-BYHIk5vU.d.ts → helpers-DNYfg6mo.d.ts} +2 -3
- package/dist/helpers.d.ts +2 -2
- package/dist/helpers.js.map +1 -1
- package/dist/{index-Dq-7R-gi.d.ts → index-C-Kyd7hD.d.ts} +1 -1
- package/dist/index.d.ts +160 -14
- package/dist/index.js +12291 -13001
- package/dist/index.js.map +1 -1
- package/dist/logicalRoleInference.js.map +1 -1
- package/dist/ontologyApproval.js +1 -1
- package/dist/ontologyApproval.js.map +1 -1
- package/dist/ontologyDefinitions.js +25 -7
- package/dist/ontologyDefinitions.js.map +1 -1
- package/dist/ontologyRegistry.js.map +1 -1
- package/dist/projectionReconciliation.js.map +1 -1
- package/dist/questionEvidenceLinks.js +28 -7
- package/dist/questionEvidenceLinks.js.map +1 -1
- package/dist/resolvers.js.map +1 -1
- package/dist/scopeResolverCompat.js.map +1 -1
- package/dist/topicProjectOverlay.js.map +1 -1
- package/dist/topicScope.js.map +1 -1
- package/dist/workflowBridge.js.map +1 -1
- package/dist/workspaceIsolation.js.map +1 -1
- package/package.json +4 -5
- package/dist/edgeValidation-CeI0wc0r.d.ts +0 -35
- package/dist/epistemicBeliefs-DzKjZAeC.d.ts +0 -377
- package/dist/epistemicEdges-CvlKnEyy.d.ts +0 -191
- package/dist/epistemicEvidence-xw6UUrwh.d.ts +0 -128
- package/dist/epistemicHelpers-DevrYgPN.d.ts +0 -329
- package/dist/epistemicNodes-DjSUfvyD.d.ts +0 -167
- package/dist/epistemicQuestions-B_nUclrH.d.ts +0 -214
- 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
|