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