@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/epistemicEdges.js
CHANGED
|
@@ -3,13 +3,7 @@ import { checkScopeAccess, requireProjectAccess } from '@lucern/access-control/a
|
|
|
3
3
|
import { permissiveReturn } from '@lucern/contracts/schema-helpers/validators';
|
|
4
4
|
import { componentsGeneric, anyApi, queryGeneric, mutationGeneric, internalMutationGeneric } from 'convex/server';
|
|
5
5
|
|
|
6
|
-
// src/epistemicEdges.ts
|
|
7
|
-
var api = anyApi;
|
|
8
|
-
componentsGeneric();
|
|
9
|
-
var internal = anyApi;
|
|
10
|
-
var internalMutation = internalMutationGeneric;
|
|
11
|
-
var mutation = mutationGeneric;
|
|
12
|
-
var query = queryGeneric;
|
|
6
|
+
// src/epistemicEdges.queries.ts
|
|
13
7
|
|
|
14
8
|
// src/debug.ts
|
|
15
9
|
function isGraphPrimitiveDebugEnabled() {
|
|
@@ -22,6 +16,167 @@ function debugGraphPrimitiveFallback(message, context) {
|
|
|
22
16
|
}
|
|
23
17
|
console.debug(message, context ?? {});
|
|
24
18
|
}
|
|
19
|
+
var api = anyApi;
|
|
20
|
+
componentsGeneric();
|
|
21
|
+
var internal = anyApi;
|
|
22
|
+
var internalMutation = internalMutationGeneric;
|
|
23
|
+
var mutation = mutationGeneric;
|
|
24
|
+
var query = queryGeneric;
|
|
25
|
+
var epistemicLayerValidator = v.union(
|
|
26
|
+
v.literal("L4"),
|
|
27
|
+
v.literal("L3"),
|
|
28
|
+
v.literal("L2"),
|
|
29
|
+
v.literal("L1"),
|
|
30
|
+
v.literal("ontological"),
|
|
31
|
+
v.literal("organizational")
|
|
32
|
+
);
|
|
33
|
+
var subjectiveOpinionValidator = v.object({
|
|
34
|
+
b: v.number(),
|
|
35
|
+
d: v.number(),
|
|
36
|
+
u: v.number(),
|
|
37
|
+
a: v.number()
|
|
38
|
+
});
|
|
39
|
+
var edgeTypeValidator = v.union(
|
|
40
|
+
// --- L4 Decision Edges (Phase 2A) ---
|
|
41
|
+
v.literal("based_on_belief"),
|
|
42
|
+
v.literal("based_on_question"),
|
|
43
|
+
v.literal("blocked_by_contradiction"),
|
|
44
|
+
v.literal("informed_by_theme"),
|
|
45
|
+
// --- Evidence Flow (L2 → L3, L2 → L1) ---
|
|
46
|
+
v.literal("derived_from"),
|
|
47
|
+
v.literal("responds_to"),
|
|
48
|
+
v.literal("informs"),
|
|
49
|
+
v.literal("tests"),
|
|
50
|
+
v.literal("explores"),
|
|
51
|
+
v.literal("qualifies"),
|
|
52
|
+
// --- Synthesis (L2 → L2, L2 → L1) ---
|
|
53
|
+
// "based_on" removed — use "derived_from" instead
|
|
54
|
+
// --- Theme Relationships (L3 → L3) ---
|
|
55
|
+
v.literal("relates_to_thesis"),
|
|
56
|
+
v.literal("belongs_to"),
|
|
57
|
+
v.literal("plays_theme"),
|
|
58
|
+
v.literal("scoped_by"),
|
|
59
|
+
// --- Deal/Company ---
|
|
60
|
+
v.literal("evaluates"),
|
|
61
|
+
// --- People (ontological → ontological, ontological → L3) ---
|
|
62
|
+
v.literal("perspective_on"),
|
|
63
|
+
v.literal("works_at"),
|
|
64
|
+
v.literal("mentioned_in"),
|
|
65
|
+
v.literal("founded_by"),
|
|
66
|
+
// --- Value Chain (ontological → ontological) ---
|
|
67
|
+
v.literal("participates_in"),
|
|
68
|
+
v.literal("performs"),
|
|
69
|
+
v.literal("function_in"),
|
|
70
|
+
v.literal("impacts"),
|
|
71
|
+
// --- Investment (ontological → ontological) ---
|
|
72
|
+
v.literal("invested_in"),
|
|
73
|
+
v.literal("raised_from"),
|
|
74
|
+
// --- Lifecycle (same layer only) ---
|
|
75
|
+
v.literal("supersedes"),
|
|
76
|
+
v.literal("same_as"),
|
|
77
|
+
// --- Same-Type Relationships: Belief ↔ Belief (L3 → L3) ---
|
|
78
|
+
v.literal("depends_on"),
|
|
79
|
+
v.literal("supports"),
|
|
80
|
+
v.literal("contains"),
|
|
81
|
+
// --- Belief Cluster Mapping (L3 → L3) ---
|
|
82
|
+
v.literal("counterfactual_of"),
|
|
83
|
+
v.literal("cascade_to"),
|
|
84
|
+
v.literal("cascade_from"),
|
|
85
|
+
v.literal("mutually_exclusive"),
|
|
86
|
+
v.literal("correlates_with"),
|
|
87
|
+
v.literal("amplifies"),
|
|
88
|
+
v.literal("precondition_for"),
|
|
89
|
+
v.literal("in_tension_with"),
|
|
90
|
+
// --- Belief ↔ Belief: Epistemic Impact (Confidence Propagation) ---
|
|
91
|
+
v.literal("falsified_by"),
|
|
92
|
+
v.literal("exclusive_with"),
|
|
93
|
+
v.literal("contradicts"),
|
|
94
|
+
v.literal("collapses_if"),
|
|
95
|
+
v.literal("strengthened_by"),
|
|
96
|
+
v.literal("weakened_by"),
|
|
97
|
+
v.literal("alternative_to"),
|
|
98
|
+
v.literal("subsumes"),
|
|
99
|
+
v.literal("validated_by"),
|
|
100
|
+
v.literal("required_for"),
|
|
101
|
+
v.literal("blocks"),
|
|
102
|
+
// --- Same-Type Relationships: Question ↔ Question (L3 → L3) ---
|
|
103
|
+
v.literal("prerequisite_for"),
|
|
104
|
+
v.literal("parallel_to"),
|
|
105
|
+
// --- Same-Type Relationships: Evidence ↔ Evidence (L2 → L2) ---
|
|
106
|
+
v.literal("corroborates"),
|
|
107
|
+
v.literal("extends"),
|
|
108
|
+
v.literal("same_source_as"),
|
|
109
|
+
v.literal("same_theme_as"),
|
|
110
|
+
// --- NEW: Deep Epistemic Analysis Edges (Phase: Schema Upgrade) ---
|
|
111
|
+
v.literal("assumes"),
|
|
112
|
+
v.literal("would_predict"),
|
|
113
|
+
v.literal("analogous_to"),
|
|
114
|
+
v.literal("independent_of"),
|
|
115
|
+
// --- Entity↔Belief Bridge (OE-B) ---
|
|
116
|
+
v.literal("competes_with")
|
|
117
|
+
);
|
|
118
|
+
function buildEdgeStatusSuccessResult() {
|
|
119
|
+
return { success: true };
|
|
120
|
+
}
|
|
121
|
+
function buildEdgeNotFoundResult() {
|
|
122
|
+
const result = {};
|
|
123
|
+
result.success = false;
|
|
124
|
+
result.error = "Edge not found";
|
|
125
|
+
return result;
|
|
126
|
+
}
|
|
127
|
+
function buildEdgeMirrorSkippedResult() {
|
|
128
|
+
return {
|
|
129
|
+
success: false,
|
|
130
|
+
reason: "source_not_in_convex"
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
function buildEdgeMirrorMissingResult() {
|
|
134
|
+
return {
|
|
135
|
+
success: false,
|
|
136
|
+
reason: "not_found"
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
function buildEdgeMirrorWriteResult(edgeId, existed) {
|
|
140
|
+
return {
|
|
141
|
+
success: true,
|
|
142
|
+
edgeId,
|
|
143
|
+
existed
|
|
144
|
+
};
|
|
145
|
+
}
|
|
146
|
+
function edgeMatchesWorkspaceReasoningScope(edge, scope) {
|
|
147
|
+
return scope.topicId !== void 0 && edge.topicId === scope.topicId || scope.projectId !== void 0 && edge.projectId === scope.projectId;
|
|
148
|
+
}
|
|
149
|
+
async function collectScopedEdges(ctx, scope, scanLimit) {
|
|
150
|
+
const queries = [];
|
|
151
|
+
if (scope.topicId) {
|
|
152
|
+
queries.push(
|
|
153
|
+
ctx.db.query("epistemicEdges").withIndex(
|
|
154
|
+
"by_topic",
|
|
155
|
+
(q) => q.eq("topicId", scope.topicId)
|
|
156
|
+
).order("desc").take(scanLimit)
|
|
157
|
+
);
|
|
158
|
+
}
|
|
159
|
+
if (scope.projectId) {
|
|
160
|
+
queries.push(
|
|
161
|
+
ctx.db.query("epistemicEdges").withIndex(
|
|
162
|
+
"by_topic",
|
|
163
|
+
(q) => q.eq("topicId", scope.projectId)
|
|
164
|
+
).order("desc").take(scanLimit)
|
|
165
|
+
);
|
|
166
|
+
}
|
|
167
|
+
const seen = /* @__PURE__ */ new Set();
|
|
168
|
+
const deduped = [];
|
|
169
|
+
const flattened = (await Promise.all(queries)).flat();
|
|
170
|
+
for (const edge of flattened) {
|
|
171
|
+
const key = String(edge._id);
|
|
172
|
+
if (seen.has(key)) {
|
|
173
|
+
continue;
|
|
174
|
+
}
|
|
175
|
+
seen.add(key);
|
|
176
|
+
deduped.push(edge);
|
|
177
|
+
}
|
|
178
|
+
return deduped;
|
|
179
|
+
}
|
|
25
180
|
|
|
26
181
|
// src/graphTypes.ts
|
|
27
182
|
var EDGE_TYPE_TO_REL = {
|
|
@@ -360,508 +515,181 @@ function validateEdgeLayers(edgeType, fromLayer, toLayer) {
|
|
|
360
515
|
}
|
|
361
516
|
return { valid: true };
|
|
362
517
|
}
|
|
363
|
-
|
|
364
|
-
// src/topicProjectOverlay.ts
|
|
365
518
|
var LEGACY_SCOPE_FIELD = "graphScopeProjectId";
|
|
366
|
-
function
|
|
367
|
-
if (
|
|
519
|
+
function asMappedProjectId(topic) {
|
|
520
|
+
if (!topic) {
|
|
368
521
|
return;
|
|
369
522
|
}
|
|
370
|
-
const
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
function readStringArray(value) {
|
|
374
|
-
if (!Array.isArray(value)) {
|
|
375
|
-
return [];
|
|
523
|
+
const directLegacyProjectId = normalizeScopeValue(topic[LEGACY_SCOPE_FIELD]);
|
|
524
|
+
if (directLegacyProjectId) {
|
|
525
|
+
return directLegacyProjectId;
|
|
376
526
|
}
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
return topic.metadata && typeof topic.metadata === "object" ? topic.metadata : {};
|
|
527
|
+
const metadata = topic.metadata || {};
|
|
528
|
+
const candidate = metadata[LEGACY_SCOPE_FIELD] || metadata.legacyProjectId || metadata.projectId || metadata.scopeProjectId;
|
|
529
|
+
return candidate ? candidate : void 0;
|
|
381
530
|
}
|
|
382
|
-
function
|
|
383
|
-
if (
|
|
531
|
+
function normalizeScopeValue(value) {
|
|
532
|
+
if (typeof value !== "string") {
|
|
384
533
|
return;
|
|
385
534
|
}
|
|
386
|
-
|
|
535
|
+
const normalized = value.trim();
|
|
536
|
+
return normalized.length > 0 ? normalized : void 0;
|
|
387
537
|
}
|
|
388
|
-
function
|
|
389
|
-
return
|
|
538
|
+
function pickPrimaryTopic(candidates) {
|
|
539
|
+
return [...candidates].sort((a, b) => {
|
|
540
|
+
const depthA = a.depth ?? 9999;
|
|
541
|
+
const depthB = b.depth ?? 9999;
|
|
542
|
+
if (depthA !== depthB) {
|
|
543
|
+
return depthA - depthB;
|
|
544
|
+
}
|
|
545
|
+
const createdA = a.createdAt ?? Number.MAX_SAFE_INTEGER;
|
|
546
|
+
const createdB = b.createdAt ?? Number.MAX_SAFE_INTEGER;
|
|
547
|
+
if (createdA !== createdB) {
|
|
548
|
+
return createdA - createdB;
|
|
549
|
+
}
|
|
550
|
+
return String(a.name || "").localeCompare(String(b.name || ""));
|
|
551
|
+
})[0];
|
|
390
552
|
}
|
|
391
|
-
function
|
|
392
|
-
|
|
553
|
+
async function findTopicsByScopeAlias(ctx, scopeId) {
|
|
554
|
+
try {
|
|
555
|
+
return await ctx.db.query("topics").withIndex(
|
|
556
|
+
"by_graph_scope_project",
|
|
557
|
+
(q) => q.eq(LEGACY_SCOPE_FIELD, scopeId)
|
|
558
|
+
).collect();
|
|
559
|
+
} catch (error) {
|
|
560
|
+
debugGraphPrimitiveFallback(
|
|
561
|
+
"[topicScope] Failed to resolve scope alias via index",
|
|
562
|
+
{
|
|
563
|
+
error,
|
|
564
|
+
scopeId
|
|
565
|
+
}
|
|
566
|
+
);
|
|
567
|
+
const topics = await ctx.db.query("topics").collect();
|
|
568
|
+
return topics.filter((topic) => {
|
|
569
|
+
const normalizedGlobalId = normalizeScopeValue(topic.globalId);
|
|
570
|
+
const mappedProjectId = asMappedProjectId(topic);
|
|
571
|
+
return String(topic._id) === scopeId || normalizedGlobalId === scopeId || mappedProjectId === scopeId;
|
|
572
|
+
});
|
|
573
|
+
}
|
|
393
574
|
}
|
|
394
|
-
function
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
return explicit;
|
|
575
|
+
async function tryResolveHostTopicById(ctx, topicId) {
|
|
576
|
+
if (typeof ctx.runQuery !== "function") {
|
|
577
|
+
return null;
|
|
398
578
|
}
|
|
399
|
-
|
|
400
|
-
return
|
|
579
|
+
try {
|
|
580
|
+
return await ctx.runQuery(api.topics.get, {
|
|
581
|
+
id: topicId
|
|
582
|
+
}) ?? null;
|
|
583
|
+
} catch (error) {
|
|
584
|
+
debugGraphPrimitiveFallback(
|
|
585
|
+
"[topicScope] Failed to resolve topic by host query",
|
|
586
|
+
{
|
|
587
|
+
error,
|
|
588
|
+
topicId
|
|
589
|
+
}
|
|
590
|
+
);
|
|
591
|
+
return null;
|
|
401
592
|
}
|
|
402
|
-
return readNonEmptyString(topic.type) || "general";
|
|
403
|
-
}
|
|
404
|
-
function isProjectLikeTopic(topic) {
|
|
405
|
-
const metadata = readMetadata(topic);
|
|
406
|
-
return topic.type === "theme" || topic.type === "thematic" || topic.type === "deal" || topic.type === "monitoring" || readLegacyProjectId(topic) !== void 0 || readNonEmptyString(metadata.projectType) !== void 0;
|
|
407
593
|
}
|
|
408
|
-
function
|
|
409
|
-
const message = getErrorMessage(error);
|
|
410
|
-
return message.includes(
|
|
411
|
-
'Child component ComponentName(Identifier("lucern")) not found'
|
|
412
|
-
) || message.includes("Child component") && message.includes("lucern") && message.includes("not found");
|
|
413
|
-
}
|
|
414
|
-
function getErrorMessage(error) {
|
|
415
|
-
if (error instanceof Error) {
|
|
416
|
-
return error.message;
|
|
417
|
-
}
|
|
418
|
-
if (typeof error === "object" && error !== null && "message" in error && typeof error.message === "string") {
|
|
419
|
-
return error.message;
|
|
420
|
-
}
|
|
421
|
-
return "unknown error";
|
|
422
|
-
}
|
|
423
|
-
async function resolveTopicDoc(ctx, scopeId) {
|
|
424
|
-
if (ctx?.db && typeof ctx.db.get === "function") {
|
|
425
|
-
try {
|
|
426
|
-
const directTopic = await ctx.db.get(
|
|
427
|
-
scopeId
|
|
428
|
-
);
|
|
429
|
-
if (directTopic) {
|
|
430
|
-
return directTopic;
|
|
431
|
-
}
|
|
432
|
-
} catch (error) {
|
|
433
|
-
debugGraphPrimitiveFallback(
|
|
434
|
-
"[topicProjectOverlay] Failed to resolve topic by direct ID",
|
|
435
|
-
{
|
|
436
|
-
error,
|
|
437
|
-
scopeId
|
|
438
|
-
}
|
|
439
|
-
);
|
|
440
|
-
}
|
|
441
|
-
}
|
|
594
|
+
async function tryResolveHostTopicByLegacyScope(ctx, legacyScopeId) {
|
|
442
595
|
if (typeof ctx.runQuery !== "function") {
|
|
443
596
|
return null;
|
|
444
597
|
}
|
|
445
598
|
try {
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
});
|
|
449
|
-
if (topic?.name !== void 0 && topic?.type !== void 0) {
|
|
450
|
-
return topic;
|
|
451
|
-
}
|
|
599
|
+
return await ctx.runQuery(api.topics.getByLegacyScopeId, {
|
|
600
|
+
projectId: legacyScopeId
|
|
601
|
+
}) ?? null;
|
|
452
602
|
} catch (error) {
|
|
453
603
|
debugGraphPrimitiveFallback(
|
|
454
|
-
"[
|
|
604
|
+
"[topicScope] Failed to resolve topic by legacy scope",
|
|
455
605
|
{
|
|
456
606
|
error,
|
|
457
|
-
|
|
607
|
+
legacyScopeId
|
|
458
608
|
}
|
|
459
609
|
);
|
|
610
|
+
return null;
|
|
460
611
|
}
|
|
461
|
-
try {
|
|
462
|
-
const topic = await ctx.runQuery(api.topics.getByLegacyScopeId, {
|
|
463
|
-
projectId: String(scopeId)
|
|
464
|
-
});
|
|
465
|
-
if (topic?.name !== void 0 && topic?.type !== void 0) {
|
|
466
|
-
return topic;
|
|
467
|
-
}
|
|
468
|
-
} catch (error) {
|
|
469
|
-
debugGraphPrimitiveFallback(
|
|
470
|
-
"[topicProjectOverlay] Failed to resolve topic by legacy scope ID",
|
|
471
|
-
{ error, scopeId }
|
|
472
|
-
);
|
|
473
|
-
}
|
|
474
|
-
return null;
|
|
475
|
-
}
|
|
476
|
-
function materializeTopicProjectOverlay(topic, idMode = "legacy") {
|
|
477
|
-
const metadata = readMetadata(topic);
|
|
478
|
-
const topicId = String(topic._id);
|
|
479
|
-
const legacyProjectId = readLegacyProjectId(topic) || readLegacyProjectId(metadata) || readNonEmptyString(metadata.legacyProjectId);
|
|
480
|
-
const storageProjectId = legacyProjectId || topicId;
|
|
481
|
-
const outwardId = idMode === "topic" ? topicId : storageProjectId;
|
|
482
|
-
const visibility = coerceVisibility(topic.visibility) || coerceVisibility(metadata.visibility) || "private";
|
|
483
|
-
const status = coerceStatus(topic.status) || coerceStatus(metadata.status) || "active";
|
|
484
|
-
const createdAt = typeof topic.createdAt === "number" ? topic.createdAt : typeof topic._creationTime === "number" ? topic._creationTime : 0;
|
|
485
|
-
const updatedAt = typeof topic.updatedAt === "number" ? topic.updatedAt : typeof metadata.updatedAt === "number" ? metadata.updatedAt : createdAt;
|
|
486
|
-
return {
|
|
487
|
-
...metadata,
|
|
488
|
-
_id: outwardId,
|
|
489
|
-
projectId: outwardId,
|
|
490
|
-
topicId,
|
|
491
|
-
storageProjectId,
|
|
492
|
-
legacyProjectId,
|
|
493
|
-
name: readNonEmptyString(topic.name) || "Untitled Theme",
|
|
494
|
-
type: mapProjectType(topic, metadata),
|
|
495
|
-
description: readNonEmptyString(topic.description),
|
|
496
|
-
ownerId: readNonEmptyString(metadata.ownerId) || readNonEmptyString(topic.createdBy) || "system",
|
|
497
|
-
sharedWith: readStringArray(metadata.sharedWith),
|
|
498
|
-
visibility,
|
|
499
|
-
tenantId: readNonEmptyString(topic.tenantId) || readNonEmptyString(metadata.tenantId),
|
|
500
|
-
workspaceId: readNonEmptyString(topic.workspaceId) || readNonEmptyString(metadata.workspaceId),
|
|
501
|
-
status,
|
|
502
|
-
tags: readStringArray(metadata.tags),
|
|
503
|
-
chatCount: typeof metadata.chatCount === "number" ? metadata.chatCount : 0,
|
|
504
|
-
artifactCount: typeof metadata.artifactCount === "number" ? metadata.artifactCount : 0,
|
|
505
|
-
lastActivityAt: typeof metadata.lastActivityAt === "number" ? metadata.lastActivityAt : updatedAt,
|
|
506
|
-
_creationTime: typeof topic._creationTime === "number" ? topic._creationTime : createdAt,
|
|
507
|
-
createdAt,
|
|
508
|
-
updatedAt
|
|
509
|
-
};
|
|
510
612
|
}
|
|
511
|
-
async function
|
|
512
|
-
const
|
|
513
|
-
|
|
514
|
-
|
|
613
|
+
async function resolveInheritedWorkspaceScope(ctx, topic) {
|
|
614
|
+
const MAX_DEPTH = 10;
|
|
615
|
+
let tenantId = normalizeScopeValue(topic.tenantId);
|
|
616
|
+
let workspaceId = normalizeScopeValue(topic.workspaceId);
|
|
617
|
+
if (tenantId && workspaceId) {
|
|
618
|
+
return { tenantId, workspaceId };
|
|
515
619
|
}
|
|
516
|
-
|
|
517
|
-
|
|
620
|
+
let current = topic;
|
|
621
|
+
for (let i = 0; i < MAX_DEPTH && current?.parentTopicId; i++) {
|
|
622
|
+
current = await ctx.db.get(current.parentTopicId);
|
|
623
|
+
if (!current) break;
|
|
624
|
+
if (!tenantId) {
|
|
625
|
+
tenantId = normalizeScopeValue(current.tenantId);
|
|
626
|
+
}
|
|
627
|
+
if (!workspaceId) {
|
|
628
|
+
workspaceId = normalizeScopeValue(current.workspaceId);
|
|
629
|
+
}
|
|
630
|
+
if (tenantId && workspaceId) break;
|
|
518
631
|
}
|
|
519
|
-
return
|
|
632
|
+
return { tenantId, workspaceId };
|
|
520
633
|
}
|
|
521
|
-
async function
|
|
522
|
-
|
|
523
|
-
|
|
634
|
+
async function resolveTopicProjectScope(ctx, args) {
|
|
635
|
+
if (args.topicId) {
|
|
636
|
+
let topic = null;
|
|
524
637
|
try {
|
|
525
|
-
|
|
638
|
+
topic = await ctx.db.get(
|
|
639
|
+
args.topicId
|
|
640
|
+
);
|
|
526
641
|
} catch (error) {
|
|
527
642
|
debugGraphPrimitiveFallback(
|
|
528
|
-
"[
|
|
529
|
-
{
|
|
643
|
+
"[topicScope] Failed to load topic by direct id",
|
|
644
|
+
{
|
|
645
|
+
error,
|
|
646
|
+
topicId: args.topicId
|
|
647
|
+
}
|
|
530
648
|
);
|
|
531
|
-
allTopics = [];
|
|
532
649
|
}
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
}
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
case "projectId":
|
|
555
|
-
case "topicId":
|
|
556
|
-
case "legacyProjectId":
|
|
557
|
-
case "storageProjectId":
|
|
558
|
-
break;
|
|
559
|
-
case "name":
|
|
560
|
-
case "description":
|
|
561
|
-
patch[key] = rawValue;
|
|
562
|
-
topicUpdateArgs[key] = rawValue;
|
|
563
|
-
break;
|
|
564
|
-
case "tenantId":
|
|
565
|
-
case "workspaceId":
|
|
566
|
-
case "ownerId":
|
|
567
|
-
throw new Error(
|
|
568
|
-
`patchTopicProjectOverlay cannot mutate ${key} via component-owned topics`
|
|
569
|
-
);
|
|
570
|
-
case "status": {
|
|
571
|
-
const status = coerceStatus(rawValue);
|
|
572
|
-
if (status) {
|
|
573
|
-
patch.status = status;
|
|
574
|
-
topicUpdateArgs.status = status;
|
|
575
|
-
}
|
|
576
|
-
break;
|
|
577
|
-
}
|
|
578
|
-
case "visibility": {
|
|
579
|
-
const visibility = coerceVisibility(rawValue);
|
|
580
|
-
if (visibility) {
|
|
581
|
-
patch.visibility = visibility;
|
|
582
|
-
topicUpdateArgs.visibility = visibility;
|
|
583
|
-
}
|
|
584
|
-
break;
|
|
585
|
-
}
|
|
586
|
-
case "type": {
|
|
587
|
-
const projectType = readNonEmptyString(rawValue);
|
|
588
|
-
if (projectType) {
|
|
589
|
-
nextMetadata.projectType = projectType;
|
|
590
|
-
} else {
|
|
591
|
-
delete nextMetadata.projectType;
|
|
592
|
-
}
|
|
593
|
-
break;
|
|
594
|
-
}
|
|
595
|
-
case "updatedAt":
|
|
596
|
-
case "createdAt":
|
|
597
|
-
break;
|
|
598
|
-
default:
|
|
599
|
-
if (rawValue === void 0) {
|
|
600
|
-
delete nextMetadata[key];
|
|
601
|
-
} else {
|
|
602
|
-
nextMetadata[key] = rawValue;
|
|
603
|
-
}
|
|
650
|
+
if (!topic) {
|
|
651
|
+
topic = await tryResolveHostTopicById(ctx, String(args.topicId));
|
|
652
|
+
}
|
|
653
|
+
if (!topic) {
|
|
654
|
+
topic = pickPrimaryTopic(
|
|
655
|
+
await findTopicsByScopeAlias(ctx, String(args.topicId))
|
|
656
|
+
) ?? null;
|
|
657
|
+
}
|
|
658
|
+
if (!topic) {
|
|
659
|
+
throw new Error(`Topic not found: ${String(args.topicId)}`);
|
|
660
|
+
}
|
|
661
|
+
const inherited = await resolveInheritedWorkspaceScope(ctx, topic);
|
|
662
|
+
const mapped = asMappedProjectId(topic);
|
|
663
|
+
if (mapped) {
|
|
664
|
+
return {
|
|
665
|
+
topicId: topic._id,
|
|
666
|
+
projectId: mapped,
|
|
667
|
+
tenantId: inherited.tenantId,
|
|
668
|
+
workspaceId: inherited.workspaceId,
|
|
669
|
+
source: "topic"
|
|
670
|
+
};
|
|
604
671
|
}
|
|
672
|
+
return {
|
|
673
|
+
topicId: topic._id,
|
|
674
|
+
tenantId: inherited.tenantId,
|
|
675
|
+
workspaceId: inherited.workspaceId,
|
|
676
|
+
source: "topic"
|
|
677
|
+
};
|
|
605
678
|
}
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
topicUpdateArgs.metadata = nextMetadata;
|
|
609
|
-
if (typeof ctx.runMutation === "function") {
|
|
679
|
+
if (args.projectId) {
|
|
680
|
+
let directTopic = null;
|
|
610
681
|
try {
|
|
611
|
-
await ctx.
|
|
682
|
+
directTopic = await ctx.db.get(
|
|
683
|
+
args.projectId
|
|
684
|
+
);
|
|
612
685
|
} catch (error) {
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
} else {
|
|
621
|
-
throw new Error(
|
|
622
|
-
"Cannot patch topic without component adapter (ctx.runMutation unavailable)"
|
|
623
|
-
);
|
|
624
|
-
}
|
|
625
|
-
return materializeTopicProjectOverlay({
|
|
626
|
-
...topic,
|
|
627
|
-
...patch,
|
|
628
|
-
metadata: nextMetadata
|
|
629
|
-
});
|
|
630
|
-
}
|
|
631
|
-
|
|
632
|
-
// src/resolvers.ts
|
|
633
|
-
function isMissingLucernChildComponentError2(error) {
|
|
634
|
-
const message = getErrorMessage2(error);
|
|
635
|
-
return message.includes(
|
|
636
|
-
'Child component ComponentName(Identifier("lucern")) not found'
|
|
637
|
-
) || message.includes("Child component") && message.includes("lucern") && message.includes("not found");
|
|
638
|
-
}
|
|
639
|
-
function getErrorMessage2(error) {
|
|
640
|
-
if (error instanceof Error) {
|
|
641
|
-
return error.message;
|
|
642
|
-
}
|
|
643
|
-
if (typeof error === "object" && error !== null && "message" in error && typeof error.message === "string") {
|
|
644
|
-
return error.message;
|
|
645
|
-
}
|
|
646
|
-
return "unknown error";
|
|
647
|
-
}
|
|
648
|
-
function isAdvisoryTopicPatch(value) {
|
|
649
|
-
const advisoryKeys = /* @__PURE__ */ new Set(["lastActivityAt", "updatedAt"]);
|
|
650
|
-
const keys = Object.keys(value);
|
|
651
|
-
return keys.length > 0 && keys.every((key) => advisoryKeys.has(key));
|
|
652
|
-
}
|
|
653
|
-
async function patchProjectWithTolerance(ctx, projectId, value) {
|
|
654
|
-
try {
|
|
655
|
-
await patchTopicProjectOverlay(ctx, projectId, value);
|
|
656
|
-
} catch (error) {
|
|
657
|
-
if (!isAdvisoryTopicPatch(value) || !isMissingLucernChildComponentError2(error)) {
|
|
658
|
-
throw error;
|
|
659
|
-
}
|
|
660
|
-
console.warn(
|
|
661
|
-
"[lucern graph-primitives] Non-fatal advisory topic patch failure",
|
|
662
|
-
{
|
|
663
|
-
projectId,
|
|
664
|
-
keys: Object.keys(value),
|
|
665
|
-
error: getErrorMessage2(error)
|
|
666
|
-
}
|
|
667
|
-
);
|
|
668
|
-
}
|
|
669
|
-
}
|
|
670
|
-
function defaultResolvers() {
|
|
671
|
-
return {
|
|
672
|
-
getProject: (ctx, projectId) => resolveTopicProjectOverlay(ctx, projectId, {
|
|
673
|
-
idMode: "legacy",
|
|
674
|
-
projectLikeOnly: false
|
|
675
|
-
}),
|
|
676
|
-
patchProject: (ctx, projectId, value) => patchProjectWithTolerance(ctx, projectId, value),
|
|
677
|
-
listTopics: (ctx) => listTopicProjectOverlays(ctx, {
|
|
678
|
-
idMode: "legacy"
|
|
679
|
-
}),
|
|
680
|
-
getFinalArtifact: (ctx, artifactId) => ctx.db.get(artifactId)
|
|
681
|
-
};
|
|
682
|
-
}
|
|
683
|
-
var resolverOverrides = {};
|
|
684
|
-
function resolveGraphPrimitivesAppResolvers(_ctx) {
|
|
685
|
-
return {
|
|
686
|
-
...defaultResolvers(),
|
|
687
|
-
...resolverOverrides
|
|
688
|
-
};
|
|
689
|
-
}
|
|
690
|
-
var LEGACY_SCOPE_FIELD2 = "graphScopeProjectId";
|
|
691
|
-
function asMappedProjectId(topic) {
|
|
692
|
-
if (!topic) {
|
|
693
|
-
return;
|
|
694
|
-
}
|
|
695
|
-
const directLegacyProjectId = normalizeScopeValue(topic[LEGACY_SCOPE_FIELD2]);
|
|
696
|
-
if (directLegacyProjectId) {
|
|
697
|
-
return directLegacyProjectId;
|
|
698
|
-
}
|
|
699
|
-
const metadata = topic.metadata || {};
|
|
700
|
-
const candidate = metadata[LEGACY_SCOPE_FIELD2] || metadata.legacyProjectId || metadata.projectId || metadata.scopeProjectId;
|
|
701
|
-
return candidate ? candidate : void 0;
|
|
702
|
-
}
|
|
703
|
-
function normalizeScopeValue(value) {
|
|
704
|
-
if (typeof value !== "string") {
|
|
705
|
-
return;
|
|
706
|
-
}
|
|
707
|
-
const normalized = value.trim();
|
|
708
|
-
return normalized.length > 0 ? normalized : void 0;
|
|
709
|
-
}
|
|
710
|
-
function pickPrimaryTopic(candidates) {
|
|
711
|
-
return [...candidates].sort((a, b) => {
|
|
712
|
-
const depthA = a.depth ?? 9999;
|
|
713
|
-
const depthB = b.depth ?? 9999;
|
|
714
|
-
if (depthA !== depthB) {
|
|
715
|
-
return depthA - depthB;
|
|
716
|
-
}
|
|
717
|
-
const createdA = a.createdAt ?? Number.MAX_SAFE_INTEGER;
|
|
718
|
-
const createdB = b.createdAt ?? Number.MAX_SAFE_INTEGER;
|
|
719
|
-
if (createdA !== createdB) {
|
|
720
|
-
return createdA - createdB;
|
|
721
|
-
}
|
|
722
|
-
return String(a.name || "").localeCompare(String(b.name || ""));
|
|
723
|
-
})[0];
|
|
724
|
-
}
|
|
725
|
-
async function findTopicsByScopeAlias(ctx, scopeId) {
|
|
726
|
-
try {
|
|
727
|
-
return await ctx.db.query("topics").withIndex(
|
|
728
|
-
"by_graph_scope_project",
|
|
729
|
-
(q) => q.eq(LEGACY_SCOPE_FIELD2, scopeId)
|
|
730
|
-
).collect();
|
|
731
|
-
} catch (error) {
|
|
732
|
-
debugGraphPrimitiveFallback(
|
|
733
|
-
"[topicScope] Failed to resolve scope alias via index",
|
|
734
|
-
{
|
|
735
|
-
error,
|
|
736
|
-
scopeId
|
|
737
|
-
}
|
|
738
|
-
);
|
|
739
|
-
const topics = await ctx.db.query("topics").collect();
|
|
740
|
-
return topics.filter((topic) => {
|
|
741
|
-
const normalizedGlobalId = normalizeScopeValue(topic.globalId);
|
|
742
|
-
const mappedProjectId = asMappedProjectId(topic);
|
|
743
|
-
return String(topic._id) === scopeId || normalizedGlobalId === scopeId || mappedProjectId === scopeId;
|
|
744
|
-
});
|
|
745
|
-
}
|
|
746
|
-
}
|
|
747
|
-
async function tryResolveHostTopicById(ctx, topicId) {
|
|
748
|
-
if (typeof ctx.runQuery !== "function") {
|
|
749
|
-
return null;
|
|
750
|
-
}
|
|
751
|
-
try {
|
|
752
|
-
return await ctx.runQuery(api.topics.get, {
|
|
753
|
-
id: topicId
|
|
754
|
-
}) ?? null;
|
|
755
|
-
} catch (error) {
|
|
756
|
-
debugGraphPrimitiveFallback(
|
|
757
|
-
"[topicScope] Failed to resolve topic by host query",
|
|
758
|
-
{
|
|
759
|
-
error,
|
|
760
|
-
topicId
|
|
761
|
-
}
|
|
762
|
-
);
|
|
763
|
-
return null;
|
|
764
|
-
}
|
|
765
|
-
}
|
|
766
|
-
async function tryResolveHostTopicByLegacyScope(ctx, legacyScopeId) {
|
|
767
|
-
if (typeof ctx.runQuery !== "function") {
|
|
768
|
-
return null;
|
|
769
|
-
}
|
|
770
|
-
try {
|
|
771
|
-
return await ctx.runQuery(api.topics.getByLegacyScopeId, {
|
|
772
|
-
projectId: legacyScopeId
|
|
773
|
-
}) ?? null;
|
|
774
|
-
} catch (error) {
|
|
775
|
-
debugGraphPrimitiveFallback(
|
|
776
|
-
"[topicScope] Failed to resolve topic by legacy scope",
|
|
777
|
-
{
|
|
778
|
-
error,
|
|
779
|
-
legacyScopeId
|
|
780
|
-
}
|
|
781
|
-
);
|
|
782
|
-
return null;
|
|
783
|
-
}
|
|
784
|
-
}
|
|
785
|
-
async function resolveInheritedWorkspaceScope(ctx, topic) {
|
|
786
|
-
const MAX_DEPTH = 10;
|
|
787
|
-
let tenantId = normalizeScopeValue(topic.tenantId);
|
|
788
|
-
let workspaceId = normalizeScopeValue(topic.workspaceId);
|
|
789
|
-
if (tenantId && workspaceId) {
|
|
790
|
-
return { tenantId, workspaceId };
|
|
791
|
-
}
|
|
792
|
-
let current = topic;
|
|
793
|
-
for (let i = 0; i < MAX_DEPTH && current?.parentTopicId; i++) {
|
|
794
|
-
current = await ctx.db.get(current.parentTopicId);
|
|
795
|
-
if (!current) break;
|
|
796
|
-
if (!tenantId) {
|
|
797
|
-
tenantId = normalizeScopeValue(current.tenantId);
|
|
798
|
-
}
|
|
799
|
-
if (!workspaceId) {
|
|
800
|
-
workspaceId = normalizeScopeValue(current.workspaceId);
|
|
801
|
-
}
|
|
802
|
-
if (tenantId && workspaceId) break;
|
|
803
|
-
}
|
|
804
|
-
return { tenantId, workspaceId };
|
|
805
|
-
}
|
|
806
|
-
async function resolveTopicProjectScope(ctx, args) {
|
|
807
|
-
if (args.topicId) {
|
|
808
|
-
let topic = null;
|
|
809
|
-
try {
|
|
810
|
-
topic = await ctx.db.get(
|
|
811
|
-
args.topicId
|
|
812
|
-
);
|
|
813
|
-
} catch (error) {
|
|
814
|
-
debugGraphPrimitiveFallback(
|
|
815
|
-
"[topicScope] Failed to load topic by direct id",
|
|
816
|
-
{
|
|
817
|
-
error,
|
|
818
|
-
topicId: args.topicId
|
|
819
|
-
}
|
|
820
|
-
);
|
|
821
|
-
}
|
|
822
|
-
if (!topic) {
|
|
823
|
-
topic = await tryResolveHostTopicById(ctx, String(args.topicId));
|
|
824
|
-
}
|
|
825
|
-
if (!topic) {
|
|
826
|
-
topic = pickPrimaryTopic(
|
|
827
|
-
await findTopicsByScopeAlias(ctx, String(args.topicId))
|
|
828
|
-
) ?? null;
|
|
829
|
-
}
|
|
830
|
-
if (!topic) {
|
|
831
|
-
throw new Error(`Topic not found: ${String(args.topicId)}`);
|
|
832
|
-
}
|
|
833
|
-
const inherited = await resolveInheritedWorkspaceScope(ctx, topic);
|
|
834
|
-
const mapped = asMappedProjectId(topic);
|
|
835
|
-
if (mapped) {
|
|
836
|
-
return {
|
|
837
|
-
topicId: topic._id,
|
|
838
|
-
projectId: mapped,
|
|
839
|
-
tenantId: inherited.tenantId,
|
|
840
|
-
workspaceId: inherited.workspaceId,
|
|
841
|
-
source: "topic"
|
|
842
|
-
};
|
|
843
|
-
}
|
|
844
|
-
return {
|
|
845
|
-
topicId: topic._id,
|
|
846
|
-
tenantId: inherited.tenantId,
|
|
847
|
-
workspaceId: inherited.workspaceId,
|
|
848
|
-
source: "topic"
|
|
849
|
-
};
|
|
850
|
-
}
|
|
851
|
-
if (args.projectId) {
|
|
852
|
-
let directTopic = null;
|
|
853
|
-
try {
|
|
854
|
-
directTopic = await ctx.db.get(
|
|
855
|
-
args.projectId
|
|
856
|
-
);
|
|
857
|
-
} catch (error) {
|
|
858
|
-
debugGraphPrimitiveFallback(
|
|
859
|
-
"[topicScope] Failed to load direct project topic",
|
|
860
|
-
{
|
|
861
|
-
error,
|
|
862
|
-
projectId: args.projectId
|
|
863
|
-
}
|
|
864
|
-
);
|
|
686
|
+
debugGraphPrimitiveFallback(
|
|
687
|
+
"[topicScope] Failed to load direct project topic",
|
|
688
|
+
{
|
|
689
|
+
error,
|
|
690
|
+
projectId: args.projectId
|
|
691
|
+
}
|
|
692
|
+
);
|
|
865
693
|
}
|
|
866
694
|
if (directTopic) {
|
|
867
695
|
const inherited = await resolveInheritedWorkspaceScope(ctx, directTopic);
|
|
@@ -911,191 +739,7 @@ var optionalScopeArgs = {
|
|
|
911
739
|
topicId: v.optional(v.string())
|
|
912
740
|
};
|
|
913
741
|
|
|
914
|
-
// src/epistemicEdges.ts
|
|
915
|
-
var epistemicLayerValidator = v.union(
|
|
916
|
-
v.literal("L4"),
|
|
917
|
-
v.literal("L3"),
|
|
918
|
-
v.literal("L2"),
|
|
919
|
-
v.literal("L1"),
|
|
920
|
-
v.literal("ontological"),
|
|
921
|
-
v.literal("organizational")
|
|
922
|
-
);
|
|
923
|
-
var subjectiveOpinionValidator = v.object({
|
|
924
|
-
b: v.number(),
|
|
925
|
-
d: v.number(),
|
|
926
|
-
u: v.number(),
|
|
927
|
-
a: v.number()
|
|
928
|
-
});
|
|
929
|
-
var edgeTypeValidator = v.union(
|
|
930
|
-
// --- L4 Decision Edges (Phase 2A) ---
|
|
931
|
-
v.literal("based_on_belief"),
|
|
932
|
-
// Decision → Belief (L4 → L3)
|
|
933
|
-
v.literal("based_on_question"),
|
|
934
|
-
// Decision → Question (L4 → L3)
|
|
935
|
-
v.literal("blocked_by_contradiction"),
|
|
936
|
-
// Decision → Contradiction (L4 → L3)
|
|
937
|
-
v.literal("informed_by_theme"),
|
|
938
|
-
// Decision → Theme (L4 → L3)
|
|
939
|
-
// --- Evidence Flow (L2 → L3, L2 → L1) ---
|
|
940
|
-
v.literal("derived_from"),
|
|
941
|
-
// Evidence/Synthesis → Source/Question (provenance)
|
|
942
|
-
v.literal("responds_to"),
|
|
943
|
-
// Answer → Question (L2 → L3)
|
|
944
|
-
v.literal("informs"),
|
|
945
|
-
// Evidence → Belief (L2 → L3) - use weight: -1 to +1
|
|
946
|
-
v.literal("tests"),
|
|
947
|
-
// Question → Belief (L3 → L3)
|
|
948
|
-
v.literal("explores"),
|
|
949
|
-
// Question → Belief assumption (L3 → L3)
|
|
950
|
-
v.literal("qualifies"),
|
|
951
|
-
// Evidence → Belief (L2 → L3)
|
|
952
|
-
// --- Synthesis (L2 → L2, L2 → L1) ---
|
|
953
|
-
// "based_on" removed — use "derived_from" instead
|
|
954
|
-
// --- Theme Relationships (L3 → L3) ---
|
|
955
|
-
v.literal("relates_to_thesis"),
|
|
956
|
-
// Belief → Theme (L3 → L3) - use weight: -1 to +1
|
|
957
|
-
v.literal("belongs_to"),
|
|
958
|
-
// Any → Theme (? → L3)
|
|
959
|
-
v.literal("plays_theme"),
|
|
960
|
-
// Deal → Theme (L3 → L3)
|
|
961
|
-
v.literal("scoped_by"),
|
|
962
|
-
// Belief/Question → Topic (L3 → organizational)
|
|
963
|
-
// --- Deal/Company ---
|
|
964
|
-
v.literal("evaluates"),
|
|
965
|
-
// Deal → Company (L3 → ontological)
|
|
966
|
-
// --- People (ontological → ontological, ontological → L3) ---
|
|
967
|
-
v.literal("perspective_on"),
|
|
968
|
-
// Person → Belief/Theme
|
|
969
|
-
v.literal("works_at"),
|
|
970
|
-
// Person → Company/Investor
|
|
971
|
-
v.literal("mentioned_in"),
|
|
972
|
-
// Person/Company → Source (ontological → L1)
|
|
973
|
-
v.literal("founded_by"),
|
|
974
|
-
// Company → Person (ontological → ontological)
|
|
975
|
-
// --- Value Chain (ontological → ontological) ---
|
|
976
|
-
v.literal("participates_in"),
|
|
977
|
-
// Company → ValueChain
|
|
978
|
-
v.literal("performs"),
|
|
979
|
-
// Company → Function
|
|
980
|
-
v.literal("function_in"),
|
|
981
|
-
// Function → ValueChain
|
|
982
|
-
v.literal("impacts"),
|
|
983
|
-
// Theme → ValueChain (L3 → ontological)
|
|
984
|
-
// --- Investment (ontological → ontological) ---
|
|
985
|
-
v.literal("invested_in"),
|
|
986
|
-
// Investor → Company
|
|
987
|
-
v.literal("raised_from"),
|
|
988
|
-
// Company → Investor
|
|
989
|
-
// --- Lifecycle (same layer only) ---
|
|
990
|
-
v.literal("supersedes"),
|
|
991
|
-
// NewNode → OldNode (same layer)
|
|
992
|
-
v.literal("same_as"),
|
|
993
|
-
// Duplicate detection (same layer)
|
|
994
|
-
// --- Same-Type Relationships: Belief ↔ Belief (L3 → L3) ---
|
|
995
|
-
v.literal("depends_on"),
|
|
996
|
-
// Belief B requires Belief A to be true
|
|
997
|
-
v.literal("supports"),
|
|
998
|
-
// Beliefs strengthen each other (weight > 0)
|
|
999
|
-
v.literal("contains"),
|
|
1000
|
-
// Parent contains child (hierarchical containment)
|
|
1001
|
-
// --- Belief Cluster Mapping (L3 → L3) - For Thesis Validation Sprints ---
|
|
1002
|
-
// These edge types capture the full range of belief-to-belief relationships
|
|
1003
|
-
// needed for cluster mapping, confidence propagation, and thesis testing.
|
|
1004
|
-
v.literal("counterfactual_of"),
|
|
1005
|
-
// If A is TRUE, B FAILS (inverse dependency)
|
|
1006
|
-
v.literal("cascade_to"),
|
|
1007
|
-
// If B fails, A also fails (downstream impact)
|
|
1008
|
-
v.literal("cascade_from"),
|
|
1009
|
-
// Changes propagate from A to B (upstream dependency)
|
|
1010
|
-
v.literal("mutually_exclusive"),
|
|
1011
|
-
// A and B cannot BOTH be true (zero-sum)
|
|
1012
|
-
v.literal("correlates_with"),
|
|
1013
|
-
// A and B move together non-causally (observed pattern)
|
|
1014
|
-
v.literal("amplifies"),
|
|
1015
|
-
// A strengthens B unidirectionally (one-way boost)
|
|
1016
|
-
v.literal("precondition_for"),
|
|
1017
|
-
// A must validate before B can be tested (temporal)
|
|
1018
|
-
v.literal("in_tension_with"),
|
|
1019
|
-
// A and B pull opposite directions (soft conflict)
|
|
1020
|
-
// --- Belief ↔ Belief: Epistemic Impact (Confidence Propagation) ---
|
|
1021
|
-
v.literal("falsified_by"),
|
|
1022
|
-
// If B is validated, A becomes false
|
|
1023
|
-
v.literal("exclusive_with"),
|
|
1024
|
-
// A XOR B (sum of confidences ≤ 1)
|
|
1025
|
-
v.literal("contradicts"),
|
|
1026
|
-
// A directly contradicts B
|
|
1027
|
-
v.literal("collapses_if"),
|
|
1028
|
-
// If A falls below threshold, B collapses
|
|
1029
|
-
v.literal("strengthened_by"),
|
|
1030
|
-
// If A rises, B rises
|
|
1031
|
-
v.literal("weakened_by"),
|
|
1032
|
-
// If A rises, B falls
|
|
1033
|
-
v.literal("alternative_to"),
|
|
1034
|
-
// Competing explanations
|
|
1035
|
-
v.literal("subsumes"),
|
|
1036
|
-
// A is more general, includes B
|
|
1037
|
-
v.literal("validated_by"),
|
|
1038
|
-
// If B is true, A gains confidence
|
|
1039
|
-
v.literal("required_for"),
|
|
1040
|
-
// A must be true for B to be meaningful
|
|
1041
|
-
v.literal("blocks"),
|
|
1042
|
-
// A being unresolved blocks B
|
|
1043
|
-
// --- Same-Type Relationships: Question ↔ Question (L3 → L3) ---
|
|
1044
|
-
v.literal("prerequisite_for"),
|
|
1045
|
-
// Question A must be answered first
|
|
1046
|
-
v.literal("parallel_to"),
|
|
1047
|
-
// Same topic, different angles
|
|
1048
|
-
// --- Same-Type Relationships: Evidence ↔ Evidence (L2 → L2) ---
|
|
1049
|
-
v.literal("corroborates"),
|
|
1050
|
-
// Independent support for same conclusion
|
|
1051
|
-
v.literal("extends"),
|
|
1052
|
-
// Adds depth to other evidence
|
|
1053
|
-
v.literal("same_source_as"),
|
|
1054
|
-
// Same document/study
|
|
1055
|
-
v.literal("same_theme_as"),
|
|
1056
|
-
// Same topic/entity
|
|
1057
|
-
// --- NEW: Deep Epistemic Analysis Edges (Phase: Schema Upgrade) ---
|
|
1058
|
-
v.literal("assumes"),
|
|
1059
|
-
// Hidden dependency discovered by AI (Belief B implicitly assumes A)
|
|
1060
|
-
v.literal("would_predict"),
|
|
1061
|
-
// Pre-registered prediction for validation (If Belief true, expect Evidence)
|
|
1062
|
-
v.literal("analogous_to"),
|
|
1063
|
-
// Explicit analogy with disanalogies (Belief A like Belief B because...)
|
|
1064
|
-
v.literal("independent_of"),
|
|
1065
|
-
// True evidence independence marker (Evidence A independent of B)
|
|
1066
|
-
// --- Entity↔Belief Bridge (OE-B) ---
|
|
1067
|
-
// "about_entity" and "entity_referenced_in" removed — use "contains" instead
|
|
1068
|
-
v.literal("competes_with")
|
|
1069
|
-
// Company → Company (ontological → ontological)
|
|
1070
|
-
);
|
|
1071
|
-
function buildEdgeStatusSuccessResult() {
|
|
1072
|
-
return { success: true };
|
|
1073
|
-
}
|
|
1074
|
-
function buildEdgeNotFoundResult() {
|
|
1075
|
-
const result = {};
|
|
1076
|
-
result.success = false;
|
|
1077
|
-
result.error = "Edge not found";
|
|
1078
|
-
return result;
|
|
1079
|
-
}
|
|
1080
|
-
function buildEdgeMirrorSkippedResult() {
|
|
1081
|
-
return {
|
|
1082
|
-
success: false,
|
|
1083
|
-
reason: "source_not_in_convex"
|
|
1084
|
-
};
|
|
1085
|
-
}
|
|
1086
|
-
function buildEdgeMirrorMissingResult() {
|
|
1087
|
-
return {
|
|
1088
|
-
success: false,
|
|
1089
|
-
reason: "not_found"
|
|
1090
|
-
};
|
|
1091
|
-
}
|
|
1092
|
-
function buildEdgeMirrorWriteResult(edgeId, existed) {
|
|
1093
|
-
return {
|
|
1094
|
-
success: true,
|
|
1095
|
-
edgeId,
|
|
1096
|
-
existed
|
|
1097
|
-
};
|
|
1098
|
-
}
|
|
742
|
+
// src/epistemicEdges.queries.ts
|
|
1099
743
|
var get = query({
|
|
1100
744
|
args: { edgeId: v.id("epistemicEdges") },
|
|
1101
745
|
returns: permissiveReturn,
|
|
@@ -1311,76 +955,504 @@ var getByProject = query({
|
|
|
1311
955
|
return edges.filter((edge) => edgeMatchesWorkspaceReasoningScope(edge, scope)).slice(0, pageSize);
|
|
1312
956
|
}
|
|
1313
957
|
});
|
|
1314
|
-
var
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
|
-
|
|
958
|
+
var listAll = query({
|
|
959
|
+
args: {
|
|
960
|
+
limit: v.optional(v.number())
|
|
961
|
+
},
|
|
962
|
+
returns: permissiveReturn,
|
|
963
|
+
handler: async (ctx, args) => {
|
|
964
|
+
const pageSize = Math.max(
|
|
965
|
+
1,
|
|
966
|
+
Math.min(Math.floor(args.limit ?? 5e3), 1e4)
|
|
967
|
+
);
|
|
968
|
+
return await ctx.db.query("epistemicEdges").order("desc").take(pageSize);
|
|
969
|
+
}
|
|
970
|
+
});
|
|
971
|
+
var findContradictions = query({
|
|
972
|
+
args: {
|
|
973
|
+
nodeId: v.id("epistemicNodes")
|
|
974
|
+
},
|
|
975
|
+
returns: permissiveReturn,
|
|
976
|
+
handler: async (ctx, args) => {
|
|
977
|
+
const edges = await ctx.db.query("epistemicEdges").withIndex(
|
|
978
|
+
"by_to_type",
|
|
979
|
+
(q) => q.eq("toNodeId", args.nodeId).eq("edgeType", "informs")
|
|
980
|
+
).collect();
|
|
981
|
+
return edges.filter((e) => (e.weight ?? 0) < 0);
|
|
982
|
+
}
|
|
983
|
+
});
|
|
984
|
+
var findSupport = query({
|
|
985
|
+
args: {
|
|
986
|
+
nodeId: v.id("epistemicNodes")
|
|
987
|
+
},
|
|
988
|
+
returns: permissiveReturn,
|
|
989
|
+
handler: async (ctx, args) => {
|
|
990
|
+
const edges = await ctx.db.query("epistemicEdges").withIndex(
|
|
991
|
+
"by_to_type",
|
|
992
|
+
(q) => q.eq("toNodeId", args.nodeId).eq("edgeType", "informs")
|
|
993
|
+
).collect();
|
|
994
|
+
return edges.filter((e) => (e.weight ?? 0) >= 0);
|
|
995
|
+
}
|
|
996
|
+
});
|
|
997
|
+
var getLineage = query({
|
|
998
|
+
args: {
|
|
999
|
+
nodeId: v.id("epistemicNodes"),
|
|
1000
|
+
maxDepth: v.optional(v.number()),
|
|
1001
|
+
// Phase 2D: Layer-aware traversal options
|
|
1002
|
+
minLayer: v.optional(v.number()),
|
|
1003
|
+
// 1=L1, 2=L2, 3=L3, 4=L4
|
|
1004
|
+
maxLayer: v.optional(v.number()),
|
|
1005
|
+
mode: v.optional(
|
|
1006
|
+
v.union(
|
|
1007
|
+
v.literal("anchor_down"),
|
|
1008
|
+
v.literal("anchor_up"),
|
|
1009
|
+
v.literal("same_layer"),
|
|
1010
|
+
v.literal("decision_trace")
|
|
1011
|
+
)
|
|
1012
|
+
)
|
|
1013
|
+
},
|
|
1014
|
+
returns: permissiveReturn,
|
|
1015
|
+
handler: async (ctx, args) => {
|
|
1016
|
+
const maxDepth = args.maxDepth ?? 10;
|
|
1017
|
+
const mode = args.mode ?? "anchor_down";
|
|
1018
|
+
const minLayer = args.minLayer ?? getDefaultMinLayer(mode);
|
|
1019
|
+
const maxLayer = args.maxLayer ?? 4;
|
|
1020
|
+
const lineage = [];
|
|
1021
|
+
const visited = /* @__PURE__ */ new Set();
|
|
1022
|
+
const startNode = await ctx.db.get(args.nodeId);
|
|
1023
|
+
if (!startNode) {
|
|
1024
|
+
return lineage;
|
|
1025
|
+
}
|
|
1026
|
+
const startLayer = startNode.epistemicLayer || getNodeLayer(startNode.nodeType);
|
|
1027
|
+
const queue = [{ nodeId: args.nodeId, depth: 0, currentLayer: startLayer }];
|
|
1028
|
+
while (queue.length > 0) {
|
|
1029
|
+
const current = queue.shift();
|
|
1030
|
+
if (!current || current.depth >= maxDepth) {
|
|
1031
|
+
continue;
|
|
1032
|
+
}
|
|
1033
|
+
const nodeIdStr = current.nodeId.toString();
|
|
1034
|
+
if (visited.has(nodeIdStr)) {
|
|
1035
|
+
continue;
|
|
1036
|
+
}
|
|
1037
|
+
visited.add(nodeIdStr);
|
|
1038
|
+
const edges = await ctx.db.query("epistemicEdges").withIndex("by_from", (q) => q.eq("fromNodeId", current.nodeId)).collect();
|
|
1039
|
+
const lineageEdges = edges.filter((e) => e.edgeType === "derived_from");
|
|
1040
|
+
for (const edge of lineageEdges) {
|
|
1041
|
+
if (!edge.toNodeId) {
|
|
1042
|
+
continue;
|
|
1043
|
+
}
|
|
1044
|
+
const targetNode = await ctx.db.get(edge.toNodeId);
|
|
1045
|
+
if (!targetNode || visited.has(edge.toNodeId.toString())) {
|
|
1046
|
+
continue;
|
|
1047
|
+
}
|
|
1048
|
+
const targetLayer = edge.toLayer || targetNode.epistemicLayer || getNodeLayer(targetNode.nodeType);
|
|
1049
|
+
const shouldContinue = shouldContinueTraversal(
|
|
1050
|
+
current.currentLayer,
|
|
1051
|
+
targetLayer,
|
|
1052
|
+
{ mode, minLayer, maxLayer }
|
|
1053
|
+
);
|
|
1054
|
+
if (!shouldContinue) {
|
|
1055
|
+
continue;
|
|
1056
|
+
}
|
|
1057
|
+
lineage.push({
|
|
1058
|
+
node: targetNode,
|
|
1059
|
+
depth: current.depth + 1,
|
|
1060
|
+
edgeType: edge.edgeType,
|
|
1061
|
+
layer: targetLayer
|
|
1062
|
+
});
|
|
1063
|
+
queue.push({
|
|
1064
|
+
nodeId: edge.toNodeId,
|
|
1065
|
+
depth: current.depth + 1,
|
|
1066
|
+
currentLayer: targetLayer
|
|
1067
|
+
});
|
|
1068
|
+
}
|
|
1069
|
+
}
|
|
1070
|
+
return lineage;
|
|
1071
|
+
}
|
|
1072
|
+
});
|
|
1073
|
+
var getEvidenceForBelief = query({
|
|
1074
|
+
args: {
|
|
1075
|
+
beliefNodeId: v.id("epistemicNodes")
|
|
1076
|
+
},
|
|
1077
|
+
returns: permissiveReturn,
|
|
1078
|
+
handler: async (ctx, args) => {
|
|
1079
|
+
const informsEdges = await ctx.db.query("epistemicEdges").withIndex(
|
|
1080
|
+
"by_to_type",
|
|
1081
|
+
(q) => q.eq("toNodeId", args.beliefNodeId).eq("edgeType", "informs")
|
|
1082
|
+
).collect();
|
|
1083
|
+
const supportEdges = informsEdges.filter((e) => (e.weight ?? 0) >= 0);
|
|
1084
|
+
const contradictEdges = informsEdges.filter((e) => (e.weight ?? 0) < 0);
|
|
1085
|
+
const supportingEvidence = await Promise.all(
|
|
1086
|
+
supportEdges.map(async (edge) => ({
|
|
1087
|
+
edge,
|
|
1088
|
+
node: await ctx.db.get(edge.fromNodeId)
|
|
1089
|
+
}))
|
|
1090
|
+
);
|
|
1091
|
+
const contradictingEvidence = await Promise.all(
|
|
1092
|
+
contradictEdges.map(async (edge) => ({
|
|
1093
|
+
edge,
|
|
1094
|
+
node: await ctx.db.get(edge.fromNodeId)
|
|
1095
|
+
}))
|
|
1096
|
+
);
|
|
1097
|
+
const filteredSupporting = supportingEvidence.filter((e) => e.node !== null);
|
|
1098
|
+
const filteredContradicting = contradictingEvidence.filter(
|
|
1099
|
+
(e) => e.node !== null
|
|
1100
|
+
);
|
|
1101
|
+
return {
|
|
1102
|
+
supporting: filteredSupporting,
|
|
1103
|
+
contradicting: filteredContradicting,
|
|
1104
|
+
supportCount: filteredSupporting.length,
|
|
1105
|
+
contradictCount: filteredContradicting.length
|
|
1106
|
+
};
|
|
1107
|
+
}
|
|
1108
|
+
});
|
|
1109
|
+
var getClusterEdges = query({
|
|
1110
|
+
args: {
|
|
1111
|
+
beliefIds: v.array(v.string())
|
|
1112
|
+
},
|
|
1113
|
+
returns: permissiveReturn,
|
|
1114
|
+
handler: async (ctx, args) => {
|
|
1115
|
+
if (args.beliefIds.length === 0) {
|
|
1116
|
+
return [];
|
|
1117
|
+
}
|
|
1118
|
+
const beliefIdSet = new Set(args.beliefIds);
|
|
1119
|
+
const allEdges = await ctx.db.query("epistemicEdges").collect();
|
|
1120
|
+
return allEdges.filter((edge) => {
|
|
1121
|
+
const fromInCluster = beliefIdSet.has(String(edge.fromNodeId)) || edge.sourceGlobalId && beliefIdSet.has(edge.sourceGlobalId);
|
|
1122
|
+
const toInCluster = beliefIdSet.has(String(edge.toNodeId)) || edge.targetGlobalId && beliefIdSet.has(edge.targetGlobalId);
|
|
1123
|
+
return fromInCluster && toInCluster;
|
|
1124
|
+
});
|
|
1125
|
+
}
|
|
1126
|
+
});
|
|
1127
|
+
|
|
1128
|
+
// src/topicProjectOverlay.ts
|
|
1129
|
+
var LEGACY_SCOPE_FIELD2 = "graphScopeProjectId";
|
|
1130
|
+
function readNonEmptyString(value) {
|
|
1131
|
+
if (typeof value !== "string") {
|
|
1132
|
+
return;
|
|
1133
|
+
}
|
|
1134
|
+
const normalized = value.trim();
|
|
1135
|
+
return normalized.length > 0 ? normalized : void 0;
|
|
1136
|
+
}
|
|
1137
|
+
function readStringArray(value) {
|
|
1138
|
+
if (!Array.isArray(value)) {
|
|
1139
|
+
return [];
|
|
1140
|
+
}
|
|
1141
|
+
return value.map((entry) => readNonEmptyString(entry)).filter((entry) => Boolean(entry));
|
|
1142
|
+
}
|
|
1143
|
+
function readMetadata(topic) {
|
|
1144
|
+
return topic.metadata && typeof topic.metadata === "object" ? topic.metadata : {};
|
|
1145
|
+
}
|
|
1146
|
+
function readLegacyProjectId(value) {
|
|
1147
|
+
if (!value) {
|
|
1148
|
+
return;
|
|
1149
|
+
}
|
|
1150
|
+
return readNonEmptyString(value[LEGACY_SCOPE_FIELD2]);
|
|
1151
|
+
}
|
|
1152
|
+
function coerceVisibility(value) {
|
|
1153
|
+
return value === "private" || value === "team" || value === "firm" || value === "external" || value === "public" ? value : void 0;
|
|
1154
|
+
}
|
|
1155
|
+
function coerceStatus(value) {
|
|
1156
|
+
return value === "active" || value === "archived" || value === "watching" ? value : void 0;
|
|
1157
|
+
}
|
|
1158
|
+
function mapProjectType(topic, metadata) {
|
|
1159
|
+
const explicit = readNonEmptyString(metadata.projectType);
|
|
1160
|
+
if (explicit) {
|
|
1161
|
+
return explicit;
|
|
1162
|
+
}
|
|
1163
|
+
if (topic.type === "theme") {
|
|
1164
|
+
return "thematic";
|
|
1165
|
+
}
|
|
1166
|
+
return readNonEmptyString(topic.type) || "general";
|
|
1167
|
+
}
|
|
1168
|
+
function isProjectLikeTopic(topic) {
|
|
1169
|
+
const metadata = readMetadata(topic);
|
|
1170
|
+
return topic.type === "theme" || topic.type === "thematic" || topic.type === "deal" || topic.type === "monitoring" || readLegacyProjectId(topic) !== void 0 || readNonEmptyString(metadata.projectType) !== void 0;
|
|
1171
|
+
}
|
|
1172
|
+
function isMissingLucernChildComponentError(error) {
|
|
1173
|
+
const message = getErrorMessage(error);
|
|
1174
|
+
return message.includes(
|
|
1175
|
+
'Child component ComponentName(Identifier("lucern")) not found'
|
|
1176
|
+
) || message.includes("Child component") && message.includes("lucern") && message.includes("not found");
|
|
1177
|
+
}
|
|
1178
|
+
function getErrorMessage(error) {
|
|
1179
|
+
if (error instanceof Error) {
|
|
1180
|
+
return error.message;
|
|
1181
|
+
}
|
|
1182
|
+
if (typeof error === "object" && error !== null && "message" in error && typeof error.message === "string") {
|
|
1183
|
+
return error.message;
|
|
1184
|
+
}
|
|
1185
|
+
return "unknown error";
|
|
1186
|
+
}
|
|
1187
|
+
async function resolveTopicDoc(ctx, scopeId) {
|
|
1188
|
+
if (ctx?.db && typeof ctx.db.get === "function") {
|
|
1189
|
+
try {
|
|
1190
|
+
const directTopic = await ctx.db.get(
|
|
1191
|
+
scopeId
|
|
1192
|
+
);
|
|
1193
|
+
if (directTopic) {
|
|
1194
|
+
return directTopic;
|
|
1195
|
+
}
|
|
1196
|
+
} catch (error) {
|
|
1197
|
+
debugGraphPrimitiveFallback(
|
|
1198
|
+
"[topicProjectOverlay] Failed to resolve topic by direct ID",
|
|
1199
|
+
{
|
|
1200
|
+
error,
|
|
1201
|
+
scopeId
|
|
1202
|
+
}
|
|
1203
|
+
);
|
|
1204
|
+
}
|
|
1205
|
+
}
|
|
1206
|
+
if (typeof ctx.runQuery !== "function") {
|
|
1207
|
+
return null;
|
|
1208
|
+
}
|
|
1209
|
+
try {
|
|
1210
|
+
const topic = await ctx.runQuery(api.topics.get, {
|
|
1211
|
+
id: String(scopeId)
|
|
1212
|
+
});
|
|
1213
|
+
if (topic?.name !== void 0 && topic?.type !== void 0) {
|
|
1214
|
+
return topic;
|
|
1215
|
+
}
|
|
1216
|
+
} catch (error) {
|
|
1217
|
+
debugGraphPrimitiveFallback(
|
|
1218
|
+
"[topicProjectOverlay] Failed to resolve topic by ID query",
|
|
1219
|
+
{
|
|
1220
|
+
error,
|
|
1221
|
+
scopeId
|
|
1222
|
+
}
|
|
1223
|
+
);
|
|
1224
|
+
}
|
|
1225
|
+
try {
|
|
1226
|
+
const topic = await ctx.runQuery(api.topics.getByLegacyScopeId, {
|
|
1227
|
+
projectId: String(scopeId)
|
|
1228
|
+
});
|
|
1229
|
+
if (topic?.name !== void 0 && topic?.type !== void 0) {
|
|
1230
|
+
return topic;
|
|
1231
|
+
}
|
|
1232
|
+
} catch (error) {
|
|
1233
|
+
debugGraphPrimitiveFallback(
|
|
1234
|
+
"[topicProjectOverlay] Failed to resolve topic by legacy scope ID",
|
|
1235
|
+
{ error, scopeId }
|
|
1236
|
+
);
|
|
1237
|
+
}
|
|
1238
|
+
return null;
|
|
1239
|
+
}
|
|
1240
|
+
function materializeTopicProjectOverlay(topic, idMode = "legacy") {
|
|
1241
|
+
const metadata = readMetadata(topic);
|
|
1242
|
+
const topicId = String(topic._id);
|
|
1243
|
+
const legacyProjectId = readLegacyProjectId(topic) || readLegacyProjectId(metadata) || readNonEmptyString(metadata.legacyProjectId);
|
|
1244
|
+
const storageProjectId = legacyProjectId || topicId;
|
|
1245
|
+
const outwardId = idMode === "topic" ? topicId : storageProjectId;
|
|
1246
|
+
const visibility = coerceVisibility(topic.visibility) || coerceVisibility(metadata.visibility) || "private";
|
|
1247
|
+
const status = coerceStatus(topic.status) || coerceStatus(metadata.status) || "active";
|
|
1248
|
+
const createdAt = typeof topic.createdAt === "number" ? topic.createdAt : typeof topic._creationTime === "number" ? topic._creationTime : 0;
|
|
1249
|
+
const updatedAt = typeof topic.updatedAt === "number" ? topic.updatedAt : typeof metadata.updatedAt === "number" ? metadata.updatedAt : createdAt;
|
|
1250
|
+
return {
|
|
1251
|
+
...metadata,
|
|
1252
|
+
_id: outwardId,
|
|
1253
|
+
projectId: outwardId,
|
|
1254
|
+
topicId,
|
|
1255
|
+
storageProjectId,
|
|
1256
|
+
legacyProjectId,
|
|
1257
|
+
name: readNonEmptyString(topic.name) || "Untitled Theme",
|
|
1258
|
+
type: mapProjectType(topic, metadata),
|
|
1259
|
+
description: readNonEmptyString(topic.description),
|
|
1260
|
+
ownerId: readNonEmptyString(metadata.ownerId) || readNonEmptyString(topic.createdBy) || "system",
|
|
1261
|
+
sharedWith: readStringArray(metadata.sharedWith),
|
|
1262
|
+
visibility,
|
|
1263
|
+
tenantId: readNonEmptyString(topic.tenantId) || readNonEmptyString(metadata.tenantId),
|
|
1264
|
+
workspaceId: readNonEmptyString(topic.workspaceId) || readNonEmptyString(metadata.workspaceId),
|
|
1265
|
+
status,
|
|
1266
|
+
tags: readStringArray(metadata.tags),
|
|
1267
|
+
chatCount: typeof metadata.chatCount === "number" ? metadata.chatCount : 0,
|
|
1268
|
+
artifactCount: typeof metadata.artifactCount === "number" ? metadata.artifactCount : 0,
|
|
1269
|
+
lastActivityAt: typeof metadata.lastActivityAt === "number" ? metadata.lastActivityAt : updatedAt,
|
|
1270
|
+
_creationTime: typeof topic._creationTime === "number" ? topic._creationTime : createdAt,
|
|
1271
|
+
createdAt,
|
|
1272
|
+
updatedAt
|
|
1273
|
+
};
|
|
1274
|
+
}
|
|
1275
|
+
async function resolveTopicProjectOverlay(ctx, scopeId, options = {}) {
|
|
1276
|
+
const topic = await resolveTopicDoc(ctx, scopeId);
|
|
1277
|
+
if (!topic) {
|
|
1278
|
+
return null;
|
|
1279
|
+
}
|
|
1280
|
+
if (options.projectLikeOnly !== false && !isProjectLikeTopic(topic)) {
|
|
1281
|
+
return null;
|
|
1282
|
+
}
|
|
1283
|
+
return materializeTopicProjectOverlay(topic, options.idMode);
|
|
1284
|
+
}
|
|
1285
|
+
async function listTopicProjectOverlays(ctx, options = {}) {
|
|
1286
|
+
let allTopics = [];
|
|
1287
|
+
if (ctx?.db?.query && typeof ctx.db.query === "function") {
|
|
1288
|
+
try {
|
|
1289
|
+
allTopics = await ctx.db.query("topics").collect();
|
|
1290
|
+
} catch (error) {
|
|
1291
|
+
debugGraphPrimitiveFallback(
|
|
1292
|
+
"[topicProjectOverlay] Failed to read topics table; falling back to API",
|
|
1293
|
+
{ error }
|
|
1294
|
+
);
|
|
1295
|
+
allTopics = [];
|
|
1296
|
+
}
|
|
1297
|
+
}
|
|
1298
|
+
if (allTopics.length === 0 && typeof ctx.runQuery === "function") {
|
|
1299
|
+
allTopics = (await ctx.runQuery(api.topics.list, {}) ?? []) || [];
|
|
1300
|
+
}
|
|
1301
|
+
return allTopics.filter(
|
|
1302
|
+
(topic) => options.projectLikeOnly === false || isProjectLikeTopic(topic)
|
|
1303
|
+
).map((topic) => materializeTopicProjectOverlay(topic, options.idMode));
|
|
1304
|
+
}
|
|
1305
|
+
async function patchTopicProjectOverlay(ctx, scopeId, value) {
|
|
1306
|
+
const topic = await resolveTopicDoc(ctx, scopeId);
|
|
1307
|
+
if (!topic) {
|
|
1308
|
+
return null;
|
|
1309
|
+
}
|
|
1310
|
+
const nextMetadata = { ...readMetadata(topic) };
|
|
1311
|
+
const patch = {};
|
|
1312
|
+
const topicUpdateArgs = {
|
|
1313
|
+
id: String(topic._id)
|
|
1314
|
+
};
|
|
1315
|
+
for (const [key, rawValue] of Object.entries(value)) {
|
|
1316
|
+
switch (key) {
|
|
1317
|
+
case "_id":
|
|
1318
|
+
case "projectId":
|
|
1319
|
+
case "topicId":
|
|
1320
|
+
case "legacyProjectId":
|
|
1321
|
+
case "storageProjectId":
|
|
1322
|
+
break;
|
|
1323
|
+
case "name":
|
|
1324
|
+
case "description":
|
|
1325
|
+
patch[key] = rawValue;
|
|
1326
|
+
topicUpdateArgs[key] = rawValue;
|
|
1327
|
+
break;
|
|
1328
|
+
case "tenantId":
|
|
1329
|
+
case "workspaceId":
|
|
1330
|
+
case "ownerId":
|
|
1331
|
+
throw new Error(
|
|
1332
|
+
`patchTopicProjectOverlay cannot mutate ${key} via component-owned topics`
|
|
1333
|
+
);
|
|
1334
|
+
case "status": {
|
|
1335
|
+
const status = coerceStatus(rawValue);
|
|
1336
|
+
if (status) {
|
|
1337
|
+
patch.status = status;
|
|
1338
|
+
topicUpdateArgs.status = status;
|
|
1339
|
+
}
|
|
1340
|
+
break;
|
|
1341
|
+
}
|
|
1342
|
+
case "visibility": {
|
|
1343
|
+
const visibility = coerceVisibility(rawValue);
|
|
1344
|
+
if (visibility) {
|
|
1345
|
+
patch.visibility = visibility;
|
|
1346
|
+
topicUpdateArgs.visibility = visibility;
|
|
1347
|
+
}
|
|
1348
|
+
break;
|
|
1349
|
+
}
|
|
1350
|
+
case "type": {
|
|
1351
|
+
const projectType = readNonEmptyString(rawValue);
|
|
1352
|
+
if (projectType) {
|
|
1353
|
+
nextMetadata.projectType = projectType;
|
|
1354
|
+
} else {
|
|
1355
|
+
delete nextMetadata.projectType;
|
|
1356
|
+
}
|
|
1357
|
+
break;
|
|
1358
|
+
}
|
|
1359
|
+
case "updatedAt":
|
|
1360
|
+
case "createdAt":
|
|
1361
|
+
break;
|
|
1362
|
+
default:
|
|
1363
|
+
if (rawValue === void 0) {
|
|
1364
|
+
delete nextMetadata[key];
|
|
1365
|
+
} else {
|
|
1366
|
+
nextMetadata[key] = rawValue;
|
|
1367
|
+
}
|
|
1368
|
+
}
|
|
1369
|
+
}
|
|
1370
|
+
patch.updatedAt = Date.now();
|
|
1371
|
+
patch.metadata = nextMetadata;
|
|
1372
|
+
topicUpdateArgs.metadata = nextMetadata;
|
|
1373
|
+
if (typeof ctx.runMutation === "function") {
|
|
1374
|
+
try {
|
|
1375
|
+
await ctx.runMutation(api.topics.update, topicUpdateArgs);
|
|
1376
|
+
} catch (error) {
|
|
1377
|
+
if (!isMissingLucernChildComponentError(error) || !ctx?.db || typeof ctx.db.patch !== "function") {
|
|
1378
|
+
throw error;
|
|
1379
|
+
}
|
|
1380
|
+
await ctx.db.patch(String(topic._id), patch);
|
|
1322
1381
|
}
|
|
1323
|
-
|
|
1324
|
-
|
|
1382
|
+
} else if (ctx?.db && typeof ctx.db.patch === "function") {
|
|
1383
|
+
await ctx.db.patch(String(topic._id), patch);
|
|
1384
|
+
} else {
|
|
1385
|
+
throw new Error(
|
|
1386
|
+
"Cannot patch topic without component adapter (ctx.runMutation unavailable)"
|
|
1387
|
+
);
|
|
1325
1388
|
}
|
|
1326
|
-
return
|
|
1389
|
+
return materializeTopicProjectOverlay({
|
|
1390
|
+
...topic,
|
|
1391
|
+
...patch,
|
|
1392
|
+
metadata: nextMetadata
|
|
1393
|
+
});
|
|
1327
1394
|
}
|
|
1328
|
-
|
|
1329
|
-
|
|
1395
|
+
|
|
1396
|
+
// src/resolvers.ts
|
|
1397
|
+
function isMissingLucernChildComponentError2(error) {
|
|
1398
|
+
const message = getErrorMessage2(error);
|
|
1399
|
+
return message.includes(
|
|
1400
|
+
'Child component ComponentName(Identifier("lucern")) not found'
|
|
1401
|
+
) || message.includes("Child component") && message.includes("lucern") && message.includes("not found");
|
|
1330
1402
|
}
|
|
1331
|
-
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
queries.push(
|
|
1335
|
-
ctx.db.query("epistemicEdges").withIndex("by_topic", (q) => q.eq("topicId", scope.topicId)).order("desc").take(scanLimit)
|
|
1336
|
-
);
|
|
1403
|
+
function getErrorMessage2(error) {
|
|
1404
|
+
if (error instanceof Error) {
|
|
1405
|
+
return error.message;
|
|
1337
1406
|
}
|
|
1338
|
-
if (
|
|
1339
|
-
|
|
1340
|
-
ctx.db.query("epistemicEdges").withIndex("by_topic", (q) => q.eq("topicId", scope.projectId)).order("desc").take(scanLimit)
|
|
1341
|
-
);
|
|
1407
|
+
if (typeof error === "object" && error !== null && "message" in error && typeof error.message === "string") {
|
|
1408
|
+
return error.message;
|
|
1342
1409
|
}
|
|
1343
|
-
return
|
|
1410
|
+
return "unknown error";
|
|
1344
1411
|
}
|
|
1345
|
-
|
|
1346
|
-
|
|
1347
|
-
|
|
1348
|
-
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
|
|
1353
|
-
|
|
1412
|
+
function isAdvisoryTopicPatch(value) {
|
|
1413
|
+
const advisoryKeys = /* @__PURE__ */ new Set(["lastActivityAt", "updatedAt"]);
|
|
1414
|
+
const keys = Object.keys(value);
|
|
1415
|
+
return keys.length > 0 && keys.every((key) => advisoryKeys.has(key));
|
|
1416
|
+
}
|
|
1417
|
+
async function patchProjectWithTolerance(ctx, projectId, value) {
|
|
1418
|
+
try {
|
|
1419
|
+
await patchTopicProjectOverlay(ctx, projectId, value);
|
|
1420
|
+
} catch (error) {
|
|
1421
|
+
if (!isAdvisoryTopicPatch(value) || !isMissingLucernChildComponentError2(error)) {
|
|
1422
|
+
throw error;
|
|
1423
|
+
}
|
|
1424
|
+
console.warn(
|
|
1425
|
+
"[lucern graph-primitives] Non-fatal advisory topic patch failure",
|
|
1426
|
+
{
|
|
1427
|
+
projectId,
|
|
1428
|
+
keys: Object.keys(value),
|
|
1429
|
+
error: getErrorMessage2(error)
|
|
1430
|
+
}
|
|
1354
1431
|
);
|
|
1355
|
-
return await ctx.db.query("epistemicEdges").order("desc").take(pageSize);
|
|
1356
|
-
}
|
|
1357
|
-
});
|
|
1358
|
-
var findContradictions = query({
|
|
1359
|
-
args: {
|
|
1360
|
-
nodeId: v.id("epistemicNodes")
|
|
1361
|
-
},
|
|
1362
|
-
returns: permissiveReturn,
|
|
1363
|
-
handler: async (ctx, args) => {
|
|
1364
|
-
const edges = await ctx.db.query("epistemicEdges").withIndex(
|
|
1365
|
-
"by_to_type",
|
|
1366
|
-
(q) => q.eq("toNodeId", args.nodeId).eq("edgeType", "informs")
|
|
1367
|
-
).collect();
|
|
1368
|
-
return edges.filter((e) => (e.weight ?? 0) < 0);
|
|
1369
|
-
}
|
|
1370
|
-
});
|
|
1371
|
-
var findSupport = query({
|
|
1372
|
-
args: {
|
|
1373
|
-
nodeId: v.id("epistemicNodes")
|
|
1374
|
-
},
|
|
1375
|
-
returns: permissiveReturn,
|
|
1376
|
-
handler: async (ctx, args) => {
|
|
1377
|
-
const edges = await ctx.db.query("epistemicEdges").withIndex(
|
|
1378
|
-
"by_to_type",
|
|
1379
|
-
(q) => q.eq("toNodeId", args.nodeId).eq("edgeType", "informs")
|
|
1380
|
-
).collect();
|
|
1381
|
-
return edges.filter((e) => (e.weight ?? 0) >= 0);
|
|
1382
1432
|
}
|
|
1383
|
-
}
|
|
1433
|
+
}
|
|
1434
|
+
function defaultResolvers() {
|
|
1435
|
+
return {
|
|
1436
|
+
getProject: (ctx, projectId) => resolveTopicProjectOverlay(ctx, projectId, {
|
|
1437
|
+
idMode: "legacy",
|
|
1438
|
+
projectLikeOnly: false
|
|
1439
|
+
}),
|
|
1440
|
+
patchProject: (ctx, projectId, value) => patchProjectWithTolerance(ctx, projectId, value),
|
|
1441
|
+
listTopics: (ctx) => listTopicProjectOverlays(ctx, {
|
|
1442
|
+
idMode: "legacy"
|
|
1443
|
+
}),
|
|
1444
|
+
getFinalArtifact: (ctx, artifactId) => ctx.db.get(artifactId)
|
|
1445
|
+
};
|
|
1446
|
+
}
|
|
1447
|
+
var resolverOverrides = {};
|
|
1448
|
+
function resolveGraphPrimitivesAppResolvers(_ctx) {
|
|
1449
|
+
return {
|
|
1450
|
+
...defaultResolvers(),
|
|
1451
|
+
...resolverOverrides
|
|
1452
|
+
};
|
|
1453
|
+
}
|
|
1454
|
+
|
|
1455
|
+
// src/epistemicEdges.mutations.ts
|
|
1384
1456
|
var create = mutation({
|
|
1385
1457
|
args: {
|
|
1386
1458
|
globalId: v.string(),
|
|
@@ -1390,12 +1462,9 @@ var create = mutation({
|
|
|
1390
1462
|
weight: v.optional(v.number()),
|
|
1391
1463
|
confidence: v.optional(v.number()),
|
|
1392
1464
|
context: v.optional(v.string()),
|
|
1393
|
-
// NOTE: 'relation' field has been removed. Use 'weight' instead.
|
|
1394
1465
|
derivationType: v.optional(v.string()),
|
|
1395
|
-
// For derived_from edges
|
|
1396
1466
|
createdBy: v.string(),
|
|
1397
1467
|
...optionalScopeArgs,
|
|
1398
|
-
// Phase 2C: Allow skipping validation for migrations
|
|
1399
1468
|
skipLayerValidation: v.optional(v.boolean())
|
|
1400
1469
|
},
|
|
1401
1470
|
returns: permissiveReturn,
|
|
@@ -1486,10 +1555,8 @@ var update = mutation({
|
|
|
1486
1555
|
weight: v.optional(v.number()),
|
|
1487
1556
|
confidence: v.optional(v.number()),
|
|
1488
1557
|
context: v.optional(v.string()),
|
|
1489
|
-
// NOTE: 'relation' field has been removed. Use 'weight' instead.
|
|
1490
1558
|
derivationType: v.optional(v.string()),
|
|
1491
1559
|
userId: v.optional(v.string()),
|
|
1492
|
-
// EK-4: SL opinion metadata (Kernel v2 schema fields)
|
|
1493
1560
|
constraint: v.optional(v.string()),
|
|
1494
1561
|
propagation: v.optional(v.string()),
|
|
1495
1562
|
blocking: v.optional(v.boolean()),
|
|
@@ -1574,8 +1641,6 @@ var remove = mutation({
|
|
|
1574
1641
|
},
|
|
1575
1642
|
projectId: edge.projectId
|
|
1576
1643
|
});
|
|
1577
|
-
}
|
|
1578
|
-
if (edge.projectId) {
|
|
1579
1644
|
await resolveGraphPrimitivesAppResolvers().patchProject(
|
|
1580
1645
|
ctx,
|
|
1581
1646
|
edge.projectId,
|
|
@@ -1627,13 +1692,11 @@ var batchCreate = mutation({
|
|
|
1627
1692
|
weight: v.optional(v.number()),
|
|
1628
1693
|
confidence: v.optional(v.number()),
|
|
1629
1694
|
context: v.optional(v.string()),
|
|
1630
|
-
// NOTE: 'relation' field has been removed. Use 'weight' instead.
|
|
1631
1695
|
derivationType: v.optional(v.string()),
|
|
1632
1696
|
createdBy: v.string(),
|
|
1633
1697
|
...optionalScopeArgs
|
|
1634
1698
|
})
|
|
1635
1699
|
),
|
|
1636
|
-
// Phase 2C: Allow skipping validation for migrations
|
|
1637
1700
|
skipLayerValidation: v.optional(v.boolean())
|
|
1638
1701
|
},
|
|
1639
1702
|
returns: permissiveReturn,
|
|
@@ -1653,11 +1716,7 @@ var batchCreate = mutation({
|
|
|
1653
1716
|
const fromLayer = fromNode.epistemicLayer || getNodeLayer(fromNode.nodeType);
|
|
1654
1717
|
const toLayer = toNode.epistemicLayer || getNodeLayer(toNode.nodeType);
|
|
1655
1718
|
if (!args.skipLayerValidation) {
|
|
1656
|
-
const validation = validateEdgeLayers(
|
|
1657
|
-
edge.edgeType,
|
|
1658
|
-
fromLayer,
|
|
1659
|
-
toLayer
|
|
1660
|
-
);
|
|
1719
|
+
const validation = validateEdgeLayers(edge.edgeType, fromLayer, toLayer);
|
|
1661
1720
|
if (!validation.valid) {
|
|
1662
1721
|
errors.push({
|
|
1663
1722
|
globalId: edge.globalId,
|
|
@@ -1706,132 +1765,14 @@ var batchCreate = mutation({
|
|
|
1706
1765
|
return { created: results.length, results, errors };
|
|
1707
1766
|
}
|
|
1708
1767
|
});
|
|
1709
|
-
var getLineage = query({
|
|
1710
|
-
args: {
|
|
1711
|
-
nodeId: v.id("epistemicNodes"),
|
|
1712
|
-
maxDepth: v.optional(v.number()),
|
|
1713
|
-
// Phase 2D: Layer-aware traversal options
|
|
1714
|
-
minLayer: v.optional(v.number()),
|
|
1715
|
-
// 1=L1, 2=L2, 3=L3, 4=L4
|
|
1716
|
-
maxLayer: v.optional(v.number()),
|
|
1717
|
-
mode: v.optional(
|
|
1718
|
-
v.union(
|
|
1719
|
-
v.literal("anchor_down"),
|
|
1720
|
-
v.literal("anchor_up"),
|
|
1721
|
-
v.literal("same_layer"),
|
|
1722
|
-
v.literal("decision_trace")
|
|
1723
|
-
)
|
|
1724
|
-
)
|
|
1725
|
-
},
|
|
1726
|
-
returns: permissiveReturn,
|
|
1727
|
-
handler: async (ctx, args) => {
|
|
1728
|
-
const maxDepth = args.maxDepth ?? 10;
|
|
1729
|
-
const mode = args.mode ?? "anchor_down";
|
|
1730
|
-
const minLayer = args.minLayer ?? getDefaultMinLayer(mode);
|
|
1731
|
-
const maxLayer = args.maxLayer ?? 4;
|
|
1732
|
-
const lineage = [];
|
|
1733
|
-
const visited = /* @__PURE__ */ new Set();
|
|
1734
|
-
const startNode = await ctx.db.get(args.nodeId);
|
|
1735
|
-
if (!startNode) {
|
|
1736
|
-
return lineage;
|
|
1737
|
-
}
|
|
1738
|
-
const startLayer = startNode.epistemicLayer || getNodeLayer(startNode.nodeType);
|
|
1739
|
-
const queue = [{ nodeId: args.nodeId, depth: 0, currentLayer: startLayer }];
|
|
1740
|
-
while (queue.length > 0) {
|
|
1741
|
-
const current = queue.shift();
|
|
1742
|
-
if (!current || current.depth >= maxDepth) {
|
|
1743
|
-
continue;
|
|
1744
|
-
}
|
|
1745
|
-
const nodeIdStr = current.nodeId.toString();
|
|
1746
|
-
if (visited.has(nodeIdStr)) {
|
|
1747
|
-
continue;
|
|
1748
|
-
}
|
|
1749
|
-
visited.add(nodeIdStr);
|
|
1750
|
-
const edges = await ctx.db.query("epistemicEdges").withIndex("by_from", (q) => q.eq("fromNodeId", current.nodeId)).collect();
|
|
1751
|
-
const lineageEdges = edges.filter((e) => e.edgeType === "derived_from");
|
|
1752
|
-
for (const edge of lineageEdges) {
|
|
1753
|
-
if (!edge.toNodeId) {
|
|
1754
|
-
continue;
|
|
1755
|
-
}
|
|
1756
|
-
const targetNode = await ctx.db.get(edge.toNodeId);
|
|
1757
|
-
if (!targetNode || visited.has(edge.toNodeId.toString())) {
|
|
1758
|
-
continue;
|
|
1759
|
-
}
|
|
1760
|
-
const targetLayer = edge.toLayer || targetNode.epistemicLayer || getNodeLayer(targetNode.nodeType);
|
|
1761
|
-
const shouldContinue = shouldContinueTraversal(
|
|
1762
|
-
current.currentLayer,
|
|
1763
|
-
targetLayer,
|
|
1764
|
-
{ mode, minLayer, maxLayer }
|
|
1765
|
-
);
|
|
1766
|
-
if (!shouldContinue) {
|
|
1767
|
-
continue;
|
|
1768
|
-
}
|
|
1769
|
-
lineage.push({
|
|
1770
|
-
node: targetNode,
|
|
1771
|
-
depth: current.depth + 1,
|
|
1772
|
-
edgeType: edge.edgeType,
|
|
1773
|
-
layer: targetLayer
|
|
1774
|
-
});
|
|
1775
|
-
queue.push({
|
|
1776
|
-
nodeId: edge.toNodeId,
|
|
1777
|
-
depth: current.depth + 1,
|
|
1778
|
-
currentLayer: targetLayer
|
|
1779
|
-
});
|
|
1780
|
-
}
|
|
1781
|
-
}
|
|
1782
|
-
return lineage;
|
|
1783
|
-
}
|
|
1784
|
-
});
|
|
1785
|
-
var getEvidenceForBelief = query({
|
|
1786
|
-
args: {
|
|
1787
|
-
beliefNodeId: v.id("epistemicNodes")
|
|
1788
|
-
},
|
|
1789
|
-
returns: permissiveReturn,
|
|
1790
|
-
handler: async (ctx, args) => {
|
|
1791
|
-
const informsEdges = await ctx.db.query("epistemicEdges").withIndex(
|
|
1792
|
-
"by_to_type",
|
|
1793
|
-
(q) => q.eq("toNodeId", args.beliefNodeId).eq("edgeType", "informs")
|
|
1794
|
-
).collect();
|
|
1795
|
-
const supportEdges = informsEdges.filter((e) => (e.weight ?? 0) >= 0);
|
|
1796
|
-
const contradictEdges = informsEdges.filter((e) => (e.weight ?? 0) < 0);
|
|
1797
|
-
const supportingEvidence = await Promise.all(
|
|
1798
|
-
supportEdges.map(async (edge) => ({
|
|
1799
|
-
edge,
|
|
1800
|
-
node: await ctx.db.get(edge.fromNodeId)
|
|
1801
|
-
}))
|
|
1802
|
-
);
|
|
1803
|
-
const contradictingEvidence = await Promise.all(
|
|
1804
|
-
contradictEdges.map(async (edge) => ({
|
|
1805
|
-
edge,
|
|
1806
|
-
node: await ctx.db.get(edge.fromNodeId)
|
|
1807
|
-
}))
|
|
1808
|
-
);
|
|
1809
|
-
const filteredSupporting = supportingEvidence.filter(
|
|
1810
|
-
(e) => e.node !== null
|
|
1811
|
-
);
|
|
1812
|
-
const filteredContradicting = contradictingEvidence.filter(
|
|
1813
|
-
(e) => e.node !== null
|
|
1814
|
-
);
|
|
1815
|
-
return {
|
|
1816
|
-
supporting: filteredSupporting,
|
|
1817
|
-
contradicting: filteredContradicting,
|
|
1818
|
-
supportCount: filteredSupporting.length,
|
|
1819
|
-
contradictCount: filteredContradicting.length
|
|
1820
|
-
};
|
|
1821
|
-
}
|
|
1822
|
-
});
|
|
1823
1768
|
var cleanupDeprecatedEdges = mutation({
|
|
1824
1769
|
args: {
|
|
1825
1770
|
...optionalScopeArgs,
|
|
1826
1771
|
dryRun: v.optional(v.boolean())
|
|
1827
|
-
// If true, just reports what would be deleted
|
|
1828
1772
|
},
|
|
1829
1773
|
returns: permissiveReturn,
|
|
1830
1774
|
handler: async (ctx, args) => {
|
|
1831
|
-
const DEPRECATED_TYPES = [
|
|
1832
|
-
"contradicts"
|
|
1833
|
-
// Old generic contradict edge (use weight: -1 on informs)
|
|
1834
|
-
];
|
|
1775
|
+
const DEPRECATED_TYPES = ["contradicts"];
|
|
1835
1776
|
const scopeId = args.topicId || args.projectId;
|
|
1836
1777
|
const allEdges = scopeId ? await ctx.db.query("epistemicEdges").withIndex("by_topic", (q) => q.eq("topicId", scopeId)).collect() : [];
|
|
1837
1778
|
const deprecatedEdges = allEdges.filter(
|
|
@@ -1846,7 +1787,7 @@ var cleanupDeprecatedEdges = mutation({
|
|
|
1846
1787
|
type: e.edgeType,
|
|
1847
1788
|
from: e.fromNodeId,
|
|
1848
1789
|
to: e.toNodeId,
|
|
1849
|
-
description: e.context
|
|
1790
|
+
description: typeof e.context === "object" && e.context && "description" in e.context ? e.context.description : e.description
|
|
1850
1791
|
})),
|
|
1851
1792
|
message: `Would delete ${deprecatedEdges.length} deprecated edges`
|
|
1852
1793
|
};
|
|
@@ -1895,24 +1836,6 @@ var deleteEdges = mutation({
|
|
|
1895
1836
|
};
|
|
1896
1837
|
}
|
|
1897
1838
|
});
|
|
1898
|
-
var getClusterEdges = query({
|
|
1899
|
-
args: {
|
|
1900
|
-
beliefIds: v.array(v.string())
|
|
1901
|
-
},
|
|
1902
|
-
returns: permissiveReturn,
|
|
1903
|
-
handler: async (ctx, args) => {
|
|
1904
|
-
if (args.beliefIds.length === 0) {
|
|
1905
|
-
return [];
|
|
1906
|
-
}
|
|
1907
|
-
const beliefIdSet = new Set(args.beliefIds);
|
|
1908
|
-
const allEdges = await ctx.db.query("epistemicEdges").collect();
|
|
1909
|
-
return allEdges.filter((edge) => {
|
|
1910
|
-
const fromInCluster = beliefIdSet.has(String(edge.fromNodeId)) || edge.sourceGlobalId && beliefIdSet.has(edge.sourceGlobalId);
|
|
1911
|
-
const toInCluster = beliefIdSet.has(String(edge.toNodeId)) || edge.targetGlobalId && beliefIdSet.has(edge.targetGlobalId);
|
|
1912
|
-
return fromInCluster && toInCluster;
|
|
1913
|
-
});
|
|
1914
|
-
}
|
|
1915
|
-
});
|
|
1916
1839
|
var mirrorEdgeToConvex = internalMutation({
|
|
1917
1840
|
args: {
|
|
1918
1841
|
globalId: v.string(),
|
|
@@ -1929,7 +1852,6 @@ var mirrorEdgeToConvex = internalMutation({
|
|
|
1929
1852
|
toLayer: v.optional(v.string()),
|
|
1930
1853
|
fromNodeType: v.optional(v.string()),
|
|
1931
1854
|
toNodeType: v.optional(v.string()),
|
|
1932
|
-
// === CLASSIFICATION FIELDS ===
|
|
1933
1855
|
reasoningMethod: v.optional(v.string()),
|
|
1934
1856
|
logicalRole: v.optional(v.string()),
|
|
1935
1857
|
temporalClass: v.optional(v.string()),
|
|
@@ -1962,11 +1884,9 @@ var mirrorEdgeToConvex = internalMutation({
|
|
|
1962
1884
|
globalId: args.globalId,
|
|
1963
1885
|
fromNodeId: fromNode._id,
|
|
1964
1886
|
toNodeId: toNode?._id,
|
|
1965
|
-
// Optional - may be undefined for cross-graph edges
|
|
1966
1887
|
sourceGlobalId: args.fromGlobalId,
|
|
1967
1888
|
targetGlobalId: args.toGlobalId,
|
|
1968
|
-
//
|
|
1969
|
-
// Convex _generated types (deployed schema lags source schema).
|
|
1889
|
+
// Preserve the canonical string value even when the local schema lags.
|
|
1970
1890
|
edgeType: args.edgeType,
|
|
1971
1891
|
weight: args.weight,
|
|
1972
1892
|
confidence: args.confidence,
|
|
@@ -1986,7 +1906,6 @@ var mirrorEdgeToConvex = internalMutation({
|
|
|
1986
1906
|
toLayer: args.toLayer,
|
|
1987
1907
|
fromNodeType: args.fromNodeType,
|
|
1988
1908
|
toNodeType: args.toNodeType,
|
|
1989
|
-
// Classification fields
|
|
1990
1909
|
reasoningMethod: args.reasoningMethod,
|
|
1991
1910
|
logicalRole: args.logicalRole,
|
|
1992
1911
|
temporalClass: args.temporalClass,
|
|
@@ -2073,6 +1992,9 @@ var updateEdgeInConvex = internalMutation({
|
|
|
2073
1992
|
}
|
|
2074
1993
|
});
|
|
2075
1994
|
|
|
1995
|
+
// src/epistemicEdges.ts
|
|
1996
|
+
var getByTopic = getByProject;
|
|
1997
|
+
|
|
2076
1998
|
export { batchCreate, cleanupDeprecatedEdges, create, deleteEdgeFromConvex, deleteEdges, edgeTypeValidator, epistemicLayerValidator, findContradictions, findSupport, get, getBetween, getByGlobalId, getByNodes, getByProject, getByProjectAndType, getBySourceNode, getByTargetNode, getByTopic, getClusterEdges, getEvidenceForBelief, getFromNode, getLineage, getToNode, listAll, mirrorEdgeToConvex, remove, removeBetween, update, updateEdgeInConvex };
|
|
2077
1999
|
//# sourceMappingURL=epistemicEdges.js.map
|
|
2078
2000
|
//# sourceMappingURL=epistemicEdges.js.map
|