@lucern/graph-primitives 1.0.0 → 1.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (202) hide show
  1. package/README.md +13 -12
  2. package/dist/beliefDecay.js +24 -17
  3. package/dist/beliefDecay.js.map +1 -1
  4. package/dist/beliefEvidenceLinks.js +32 -8
  5. package/dist/beliefEvidenceLinks.js.map +1 -1
  6. package/dist/confidencePropagationDispatch.js.map +1 -1
  7. package/dist/contradictions.js +32 -9
  8. package/dist/contradictions.js.map +1 -1
  9. package/dist/convex.d.ts +55 -12
  10. package/dist/convex.js.map +1 -1
  11. package/dist/edgeValidation.d.ts +25 -2
  12. package/dist/edges/index.d.ts +9 -2
  13. package/dist/edges/index.js.map +1 -1
  14. package/dist/edges/propagationTypes.d.ts +2 -3
  15. package/dist/edges/propagationTypes.js.map +1 -1
  16. package/dist/entityBridge.js +10 -3
  17. package/dist/entityBridge.js.map +1 -1
  18. package/dist/entityLifecycle.js +15 -3
  19. package/dist/entityLifecycle.js.map +1 -1
  20. package/dist/epistemicAnswers.js.map +1 -1
  21. package/dist/epistemicBeliefs.admin.d.ts +36 -0
  22. package/dist/epistemicBeliefs.admin.js +745 -0
  23. package/dist/epistemicBeliefs.admin.js.map +1 -0
  24. package/dist/epistemicBeliefs.backfills.d.ts +62 -0
  25. package/dist/epistemicBeliefs.backfills.js +1004 -0
  26. package/dist/epistemicBeliefs.backfills.js.map +1 -0
  27. package/dist/epistemicBeliefs.confidence.d.ts +45 -0
  28. package/dist/epistemicBeliefs.confidence.js +1285 -0
  29. package/dist/epistemicBeliefs.confidence.js.map +1 -0
  30. package/dist/epistemicBeliefs.core.d.ts +35 -0
  31. package/dist/epistemicBeliefs.core.js +1508 -0
  32. package/dist/epistemicBeliefs.core.js.map +1 -0
  33. package/dist/epistemicBeliefs.d.ts +12 -3
  34. package/dist/epistemicBeliefs.helpers.d.ts +168 -0
  35. package/dist/epistemicBeliefs.helpers.js +1060 -0
  36. package/dist/epistemicBeliefs.helpers.js.map +1 -0
  37. package/dist/epistemicBeliefs.internal.d.ts +30 -0
  38. package/dist/epistemicBeliefs.internal.js +1329 -0
  39. package/dist/epistemicBeliefs.internal.js.map +1 -0
  40. package/dist/epistemicBeliefs.js +1196 -1184
  41. package/dist/epistemicBeliefs.js.map +1 -1
  42. package/dist/epistemicBeliefs.lifecycle.d.ts +19 -0
  43. package/dist/epistemicBeliefs.lifecycle.js +1608 -0
  44. package/dist/epistemicBeliefs.lifecycle.js.map +1 -0
  45. package/dist/epistemicBeliefs.links.d.ts +30 -0
  46. package/dist/epistemicBeliefs.links.js +761 -0
  47. package/dist/epistemicBeliefs.links.js.map +1 -0
  48. package/dist/epistemicBeliefs.queries.d.ts +16 -0
  49. package/dist/epistemicBeliefs.queries.js +90 -0
  50. package/dist/epistemicBeliefs.queries.js.map +1 -0
  51. package/dist/epistemicContractHelpers.d.ts +1 -1
  52. package/dist/epistemicContractHelpers.js +1 -1
  53. package/dist/epistemicContracts.d.ts +5 -76
  54. package/dist/epistemicContracts.evaluators.d.ts +36 -0
  55. package/dist/epistemicContracts.evaluators.js +2506 -0
  56. package/dist/epistemicContracts.evaluators.js.map +1 -0
  57. package/dist/epistemicContracts.handlers.d.ts +40 -0
  58. package/dist/epistemicContracts.handlers.js +3029 -0
  59. package/dist/epistemicContracts.handlers.js.map +1 -0
  60. package/dist/epistemicContracts.js +2006 -5281
  61. package/dist/epistemicContracts.js.map +1 -1
  62. package/dist/epistemicContracts.metrics.d.ts +26 -0
  63. package/dist/epistemicContracts.metrics.js +427 -0
  64. package/dist/epistemicContracts.metrics.js.map +1 -0
  65. package/dist/epistemicContracts.types.d.ts +159 -0
  66. package/dist/epistemicContracts.types.js +3 -0
  67. package/dist/epistemicContracts.types.js.map +1 -0
  68. package/dist/epistemicEdgeCreation.d.ts +73 -0
  69. package/dist/epistemicEdgeCreation.js +450 -0
  70. package/dist/epistemicEdgeCreation.js.map +1 -0
  71. package/dist/epistemicEdges-BF-cn4i3.d.ts +43 -0
  72. package/dist/epistemicEdges.d.ts +8 -1
  73. package/dist/epistemicEdges.handlers.d.ts +20 -0
  74. package/dist/epistemicEdges.handlers.js +289 -0
  75. package/dist/epistemicEdges.handlers.js.map +1 -0
  76. package/dist/epistemicEdges.helpers.d.ts +27 -0
  77. package/dist/epistemicEdges.helpers.js +162 -0
  78. package/dist/epistemicEdges.helpers.js.map +1 -0
  79. package/dist/epistemicEdges.js +797 -875
  80. package/dist/epistemicEdges.js.map +1 -1
  81. package/dist/epistemicEdges.mutations.d.ts +39 -0
  82. package/dist/epistemicEdges.mutations.js +1365 -0
  83. package/dist/epistemicEdges.mutations.js.map +1 -0
  84. package/dist/epistemicEdges.queries.d.ts +95 -0
  85. package/dist/epistemicEdges.queries.js +851 -0
  86. package/dist/epistemicEdges.queries.js.map +1 -0
  87. package/dist/epistemicEdges.types.d.ts +32 -0
  88. package/dist/epistemicEdges.types.js +3 -0
  89. package/dist/epistemicEdges.types.js.map +1 -0
  90. package/dist/epistemicEvidence-DvfchNt7.d.ts +46 -0
  91. package/dist/epistemicEvidence.d.ts +5 -2
  92. package/dist/epistemicEvidence.js +801 -807
  93. package/dist/epistemicEvidence.js.map +1 -1
  94. package/dist/epistemicEvidenceHelpers.d.ts +71 -0
  95. package/dist/epistemicEvidenceHelpers.js +769 -0
  96. package/dist/epistemicEvidenceHelpers.js.map +1 -0
  97. package/dist/epistemicEvidenceMutations.d.ts +10 -0
  98. package/dist/epistemicEvidenceMutations.js +1421 -0
  99. package/dist/epistemicEvidenceMutations.js.map +1 -0
  100. package/dist/epistemicEvidenceQueries.d.ts +10 -0
  101. package/dist/epistemicEvidenceQueries.js +1049 -0
  102. package/dist/epistemicEvidenceQueries.js.map +1 -0
  103. package/dist/epistemicHelpers.d.ts +4 -2
  104. package/dist/epistemicHelpers.js +132 -127
  105. package/dist/epistemicHelpers.js.map +1 -1
  106. package/dist/epistemicLayerRules.d.ts +138 -0
  107. package/dist/epistemicLayerRules.js +481 -0
  108. package/dist/epistemicLayerRules.js.map +1 -0
  109. package/dist/epistemicLinking.js +1 -1
  110. package/dist/epistemicLinking.js.map +1 -1
  111. package/dist/epistemicNodeCreation.d.ts +101 -0
  112. package/dist/epistemicNodeCreation.js +709 -0
  113. package/dist/epistemicNodeCreation.js.map +1 -0
  114. package/dist/epistemicNodes-BCQxpYx_.d.ts +54 -0
  115. package/dist/epistemicNodes.d.ts +5 -1
  116. package/dist/epistemicNodes.helpers.d.ts +51 -0
  117. package/dist/epistemicNodes.helpers.js +73 -0
  118. package/dist/epistemicNodes.helpers.js.map +1 -0
  119. package/dist/epistemicNodes.internal.d.ts +34 -0
  120. package/dist/epistemicNodes.internal.js +658 -0
  121. package/dist/epistemicNodes.internal.js.map +1 -0
  122. package/dist/epistemicNodes.js +698 -693
  123. package/dist/epistemicNodes.js.map +1 -1
  124. package/dist/epistemicNodes.mutations.d.ts +34 -0
  125. package/dist/epistemicNodes.mutations.js +1153 -0
  126. package/dist/epistemicNodes.mutations.js.map +1 -0
  127. package/dist/epistemicNodes.queries.d.ts +36 -0
  128. package/dist/epistemicNodes.queries.js +619 -0
  129. package/dist/epistemicNodes.queries.js.map +1 -0
  130. package/dist/epistemicNodes.validators.d.ts +23 -0
  131. package/dist/epistemicNodes.validators.js +105 -0
  132. package/dist/epistemicNodes.validators.js.map +1 -0
  133. package/dist/epistemicQuestions-bwHd2FWE.d.ts +68 -0
  134. package/dist/epistemicQuestions.conviction.d.ts +52 -0
  135. package/dist/epistemicQuestions.conviction.js +1389 -0
  136. package/dist/epistemicQuestions.conviction.js.map +1 -0
  137. package/dist/epistemicQuestions.create.d.ts +29 -0
  138. package/dist/epistemicQuestions.create.js +1300 -0
  139. package/dist/epistemicQuestions.create.js.map +1 -0
  140. package/dist/epistemicQuestions.d.ts +10 -2
  141. package/dist/epistemicQuestions.evidence.d.ts +22 -0
  142. package/dist/epistemicQuestions.evidence.js +929 -0
  143. package/dist/epistemicQuestions.evidence.js.map +1 -0
  144. package/dist/epistemicQuestions.helpers.d.ts +69 -0
  145. package/dist/epistemicQuestions.helpers.js +824 -0
  146. package/dist/epistemicQuestions.helpers.js.map +1 -0
  147. package/dist/epistemicQuestions.js +2435 -2430
  148. package/dist/epistemicQuestions.js.map +1 -1
  149. package/dist/epistemicQuestions.lifecycle.d.ts +24 -0
  150. package/dist/epistemicQuestions.lifecycle.js +838 -0
  151. package/dist/epistemicQuestions.lifecycle.js.map +1 -0
  152. package/dist/epistemicQuestions.queries.d.ts +41 -0
  153. package/dist/epistemicQuestions.queries.js +1013 -0
  154. package/dist/epistemicQuestions.queries.js.map +1 -0
  155. package/dist/epistemicQuestions.sprint.d.ts +22 -0
  156. package/dist/epistemicQuestions.sprint.js +757 -0
  157. package/dist/epistemicQuestions.sprint.js.map +1 -0
  158. package/dist/epistemicQuestions.tail.d.ts +42 -0
  159. package/dist/epistemicQuestions.tail.js +1345 -0
  160. package/dist/epistemicQuestions.tail.js.map +1 -0
  161. package/dist/epistemicSources.js +6 -2
  162. package/dist/epistemicSources.js.map +1 -1
  163. package/dist/evaluators/index.d.ts +2 -2
  164. package/dist/evaluators/index.js +45 -5320
  165. package/dist/evaluators/index.js.map +1 -1
  166. package/dist/evaluators/lintCheckerEvaluator.d.ts +1 -1
  167. package/dist/evaluators/sentryCheckerEvaluator.d.ts +1 -1
  168. package/dist/evaluators/testRunnerEvaluator.d.ts +1 -1
  169. package/dist/evaluators/tscCheckerEvaluator.d.ts +1 -1
  170. package/dist/{graphTypes-CpgIuCdo.d.ts → graphTypes-B8VaIjnl.d.ts} +1 -1
  171. package/dist/graphTypes.d.ts +1 -1
  172. package/dist/{helpers-BYHIk5vU.d.ts → helpers-DNYfg6mo.d.ts} +2 -3
  173. package/dist/helpers.d.ts +2 -2
  174. package/dist/helpers.js.map +1 -1
  175. package/dist/{index-Dq-7R-gi.d.ts → index-C-Kyd7hD.d.ts} +1 -1
  176. package/dist/index.d.ts +160 -14
  177. package/dist/index.js +12291 -13001
  178. package/dist/index.js.map +1 -1
  179. package/dist/logicalRoleInference.js.map +1 -1
  180. package/dist/ontologyApproval.js +1 -1
  181. package/dist/ontologyApproval.js.map +1 -1
  182. package/dist/ontologyDefinitions.js +25 -7
  183. package/dist/ontologyDefinitions.js.map +1 -1
  184. package/dist/ontologyRegistry.js.map +1 -1
  185. package/dist/projectionReconciliation.js.map +1 -1
  186. package/dist/questionEvidenceLinks.js +28 -7
  187. package/dist/questionEvidenceLinks.js.map +1 -1
  188. package/dist/resolvers.js.map +1 -1
  189. package/dist/scopeResolverCompat.js.map +1 -1
  190. package/dist/topicProjectOverlay.js.map +1 -1
  191. package/dist/topicScope.js.map +1 -1
  192. package/dist/workflowBridge.js.map +1 -1
  193. package/dist/workspaceIsolation.js.map +1 -1
  194. package/package.json +4 -5
  195. package/dist/edgeValidation-CeI0wc0r.d.ts +0 -35
  196. package/dist/epistemicBeliefs-DzKjZAeC.d.ts +0 -377
  197. package/dist/epistemicEdges-CvlKnEyy.d.ts +0 -191
  198. package/dist/epistemicEvidence-xw6UUrwh.d.ts +0 -128
  199. package/dist/epistemicHelpers-DevrYgPN.d.ts +0 -329
  200. package/dist/epistemicNodes-DjSUfvyD.d.ts +0 -167
  201. package/dist/epistemicQuestions-B_nUclrH.d.ts +0 -214
  202. package/dist/index-Dct1T70K.d.ts +0 -25
@@ -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 readNonEmptyString(value) {
367
- if (typeof value !== "string") {
519
+ function asMappedProjectId(topic) {
520
+ if (!topic) {
368
521
  return;
369
522
  }
370
- const normalized = value.trim();
371
- return normalized.length > 0 ? normalized : void 0;
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
- return value.map((entry) => readNonEmptyString(entry)).filter((entry) => Boolean(entry));
378
- }
379
- function readMetadata(topic) {
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 readLegacyProjectId(value) {
383
- if (!value) {
531
+ function normalizeScopeValue(value) {
532
+ if (typeof value !== "string") {
384
533
  return;
385
534
  }
386
- return readNonEmptyString(value[LEGACY_SCOPE_FIELD]);
535
+ const normalized = value.trim();
536
+ return normalized.length > 0 ? normalized : void 0;
387
537
  }
388
- function coerceVisibility(value) {
389
- return value === "private" || value === "team" || value === "firm" || value === "external" || value === "public" ? value : void 0;
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 coerceStatus(value) {
392
- return value === "active" || value === "archived" || value === "watching" ? value : void 0;
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 mapProjectType(topic, metadata) {
395
- const explicit = readNonEmptyString(metadata.projectType);
396
- if (explicit) {
397
- return explicit;
575
+ async function tryResolveHostTopicById(ctx, topicId) {
576
+ if (typeof ctx.runQuery !== "function") {
577
+ return null;
398
578
  }
399
- if (topic.type === "theme") {
400
- return "thematic";
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 isMissingLucernChildComponentError(error) {
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
- const topic = await ctx.runQuery(api.topics.get, {
447
- id: String(scopeId)
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
- "[topicProjectOverlay] Failed to resolve topic by ID query",
604
+ "[topicScope] Failed to resolve topic by legacy scope",
455
605
  {
456
606
  error,
457
- scopeId
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 resolveTopicProjectOverlay(ctx, scopeId, options = {}) {
512
- const topic = await resolveTopicDoc(ctx, scopeId);
513
- if (!topic) {
514
- return null;
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
- if (options.projectLikeOnly !== false && !isProjectLikeTopic(topic)) {
517
- return null;
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 materializeTopicProjectOverlay(topic, options.idMode);
632
+ return { tenantId, workspaceId };
520
633
  }
521
- async function listTopicProjectOverlays(ctx, options = {}) {
522
- let allTopics = [];
523
- if (ctx?.db?.query && typeof ctx.db.query === "function") {
634
+ async function resolveTopicProjectScope(ctx, args) {
635
+ if (args.topicId) {
636
+ let topic = null;
524
637
  try {
525
- allTopics = await ctx.db.query("topics").collect();
638
+ topic = await ctx.db.get(
639
+ args.topicId
640
+ );
526
641
  } catch (error) {
527
642
  debugGraphPrimitiveFallback(
528
- "[topicProjectOverlay] Failed to read topics table; falling back to API",
529
- { error }
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
- if (allTopics.length === 0 && typeof ctx.runQuery === "function") {
535
- allTopics = (await ctx.runQuery(api.topics.list, {}) ?? []) || [];
536
- }
537
- return allTopics.filter(
538
- (topic) => options.projectLikeOnly === false || isProjectLikeTopic(topic)
539
- ).map((topic) => materializeTopicProjectOverlay(topic, options.idMode));
540
- }
541
- async function patchTopicProjectOverlay(ctx, scopeId, value) {
542
- const topic = await resolveTopicDoc(ctx, scopeId);
543
- if (!topic) {
544
- return null;
545
- }
546
- const nextMetadata = { ...readMetadata(topic) };
547
- const patch = {};
548
- const topicUpdateArgs = {
549
- id: String(topic._id)
550
- };
551
- for (const [key, rawValue] of Object.entries(value)) {
552
- switch (key) {
553
- case "_id":
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
- patch.updatedAt = Date.now();
607
- patch.metadata = nextMetadata;
608
- topicUpdateArgs.metadata = nextMetadata;
609
- if (typeof ctx.runMutation === "function") {
679
+ if (args.projectId) {
680
+ let directTopic = null;
610
681
  try {
611
- await ctx.runMutation(api.topics.update, topicUpdateArgs);
682
+ directTopic = await ctx.db.get(
683
+ args.projectId
684
+ );
612
685
  } catch (error) {
613
- if (!isMissingLucernChildComponentError(error) || !ctx?.db || typeof ctx.db.patch !== "function") {
614
- throw error;
615
- }
616
- await ctx.db.patch(String(topic._id), patch);
617
- }
618
- } else if (ctx?.db && typeof ctx.db.patch === "function") {
619
- await ctx.db.patch(String(topic._id), patch);
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 getByTopic = getByProject;
1315
- function dedupeWorkspaceEdges(edges) {
1316
- const seen = /* @__PURE__ */ new Set();
1317
- const deduped = [];
1318
- for (const edge of edges) {
1319
- const key = String(edge._id);
1320
- if (seen.has(key)) {
1321
- continue;
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
- seen.add(key);
1324
- deduped.push(edge);
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 deduped;
1389
+ return materializeTopicProjectOverlay({
1390
+ ...topic,
1391
+ ...patch,
1392
+ metadata: nextMetadata
1393
+ });
1327
1394
  }
1328
- function edgeMatchesWorkspaceReasoningScope(edge, scope) {
1329
- return scope.topicId !== void 0 && edge.topicId === scope.topicId || scope.projectId !== void 0 && edge.projectId === scope.projectId;
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
- async function collectScopedEdges(ctx, scope, scanLimit) {
1332
- const queries = [];
1333
- if (scope.topicId) {
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 (scope.projectId) {
1339
- queries.push(
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 dedupeWorkspaceEdges((await Promise.all(queries)).flat());
1410
+ return "unknown error";
1344
1411
  }
1345
- var listAll = query({
1346
- args: {
1347
- limit: v.optional(v.number())
1348
- },
1349
- returns: permissiveReturn,
1350
- handler: async (ctx, args) => {
1351
- const pageSize = Math.max(
1352
- 1,
1353
- Math.min(Math.floor(args.limit ?? 5e3), 1e4)
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?.description || e.description
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
- // Cast through any because EdgeType may include types not yet in
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