@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
@@ -2,14 +2,14 @@ import { v } from 'convex/values';
2
2
  import { normalizeTupleContradictionPolicy, mkOpinion, createInheritedContractRecord, confidenceFromSL, conditionalDeduction, project, dampedDependencyCascade, hasProjectedOpinionChanged, detectTupleContradiction, evaluateTupleContradictionTransition, readOpinionFromRecord, trustDiscount, applyNegativeSupport, cumulativeFusion, applyNegativeEvidence } from '@lucern/confidence';
3
3
  import { checkScopeAccess, checkProjectAccess } from '@lucern/access-control/access';
4
4
  import { canAudienceClassAccess, normalizeAudienceKey, classFromAudienceKey } from '@lucern/access-control/audience';
5
- import { listAudienceRegistryRows } from '@lucern/access-control/audienceRegistry';
6
5
  import { getCurrentUserId } from '@lucern/access-control/auth';
7
- import { assertSchemaEnumValue } from '@lucern/contracts/schema-helpers/enumValidation';
8
- import { permissiveReturn } from '@lucern/contracts/schema-helpers/validators';
9
6
  import { componentsGeneric, anyApi, internalMutationGeneric, mutationGeneric, queryGeneric, internalQueryGeneric } from 'convex/server';
10
7
  import { isNodeType, getLayerForNodeType } from '@lucern/contracts/schema-helpers/spine/tables/epistemicNodes';
8
+ import { permissiveReturn } from '@lucern/contracts/schema-helpers/validators';
9
+ import { assertSchemaEnumValue } from '@lucern/contracts/schema-helpers/enumValidation';
10
+ import { listAudienceRegistryRows } from '@lucern/access-control/audienceRegistry';
11
11
 
12
- // src/epistemicBeliefs.ts
12
+ // src/epistemicBeliefs.helpers.ts
13
13
 
14
14
  // src/beliefLifecycle.ts
15
15
  var BELIEF_STATUS_VALUES = [
@@ -110,375 +110,417 @@ var internalQuery = internalQueryGeneric;
110
110
  var mutation = mutationGeneric;
111
111
  var query = queryGeneric;
112
112
 
113
- // src/edges/contains.ts
114
- var containsPropagationSpec = {
115
- edgeType: "contains",
116
- direction: "outgoing",
117
- transitivity: "none",
118
- damping: 1,
119
- maxHops: 1,
120
- operator: () => null,
121
- description: "Structural containment only. Traversed for explicit semantics, but it never propagates opinions."
122
- };
123
- function readEdgeMetadata(edge) {
124
- return {
125
- constraint: edge.constraint ?? void 0,
126
- normalization: edge.normalization ?? void 0,
127
- propagation: edge.propagation ?? void 0,
128
- conditionalA: edge.conditionalA ?? void 0,
129
- conditionalNotA: edge.conditionalNotA ?? void 0
130
- };
113
+ // src/debug.ts
114
+ function isGraphPrimitiveDebugEnabled() {
115
+ const env = globalThis.process?.env;
116
+ return env?.LUCERN_COMPAT_FALLBACK_DEBUG === "1" || env?.LUCERN_GRAPH_DEBUG === "1";
131
117
  }
132
- function applyPerHopDamping(sourceOpinion, damping) {
133
- if (damping >= 1) {
134
- return sourceOpinion;
118
+ function debugGraphPrimitiveFallback(message, context) {
119
+ if (!isGraphPrimitiveDebugEnabled()) {
120
+ return;
135
121
  }
136
- return trustDiscount(sourceOpinion, Math.max(0, damping));
122
+ console.debug(message, context ?? {});
137
123
  }
138
- function annotateRationale(result, spec, hop) {
139
- return {
140
- ...result,
141
- rationale: `hop=${hop} edge=${spec.edgeType} damping=${spec.damping.toFixed(
142
- 2
143
- )} :: ${result.rationale}`
144
- };
124
+
125
+ // src/topicProjectOverlay.ts
126
+ var LEGACY_SCOPE_FIELD = "graphScopeProjectId";
127
+ function readNonEmptyString(value) {
128
+ if (typeof value !== "string") {
129
+ return;
130
+ }
131
+ const normalized = value.trim();
132
+ return normalized.length > 0 ? normalized : void 0;
145
133
  }
146
- function propagatePositiveSupport(sourceOpinion, targetOpinion, edgeWeight) {
147
- const discounted = trustDiscount(sourceOpinion, Math.abs(edgeWeight));
148
- return {
149
- opinion: cumulativeFusion(targetOpinion, discounted),
150
- operator: "cumulative_fusion",
151
- rationale: `Supporting evidence (weight=${edgeWeight.toFixed(
152
- 2
153
- )}) from source at ${project(sourceOpinion).toFixed(2)}`
154
- };
134
+ function readStringArray(value) {
135
+ if (!Array.isArray(value)) {
136
+ return [];
137
+ }
138
+ return value.map((entry) => readNonEmptyString(entry)).filter((entry) => Boolean(entry));
155
139
  }
156
- function propagatePositiveInform(sourceOpinion, targetOpinion, edgeWeight) {
157
- const discounted = trustDiscount(sourceOpinion, Math.abs(edgeWeight));
158
- return {
159
- opinion: cumulativeFusion(targetOpinion, discounted),
160
- operator: "cumulative_fusion",
161
- rationale: `Supporting evidence (weight=${edgeWeight.toFixed(2)})`
162
- };
140
+ function readMetadata(topic) {
141
+ return topic.metadata && typeof topic.metadata === "object" ? topic.metadata : {};
163
142
  }
164
- function propagateNegativeSupportWithMetadata(sourceOpinion, targetOpinion, edgeWeight, edge) {
165
- return applyNegativeSupport(
166
- sourceOpinion,
167
- targetOpinion,
168
- edgeWeight,
169
- readEdgeMetadata(edge)
170
- );
143
+ function readLegacyProjectId(value) {
144
+ if (!value) {
145
+ return;
146
+ }
147
+ return readNonEmptyString(value[LEGACY_SCOPE_FIELD]);
171
148
  }
172
- var propagateNegativeInform = applyNegativeEvidence;
173
-
174
- // src/edges/contradicts.ts
175
- var contradictsPropagationSpec = {
176
- edgeType: "contradicts",
177
- direction: "bidirectional",
178
- transitivity: "none",
179
- damping: 0.85,
180
- maxHops: 1,
181
- operator: (sourceOpinion, targetOpinion, edge, context) => {
182
- const dampedSource = applyPerHopDamping(
183
- sourceOpinion,
184
- context.spec.damping
185
- );
186
- const negativeWeight = -Math.abs(edge.weight ?? 1);
187
- const result = propagateNegativeSupportWithMetadata(
188
- dampedSource,
189
- targetOpinion,
190
- negativeWeight,
191
- edge
192
- );
193
- return annotateRationale(result, context.spec, context.hop);
194
- },
195
- description: "Legacy contradiction edges move negative pressure in either direction, but never beyond one hop."
196
- };
197
- var dependsOnPropagationSpec = {
198
- edgeType: "depends_on",
199
- direction: "incoming",
200
- transitivity: "damped",
201
- damping: 0.8,
202
- maxHops: "unbounded",
203
- operator: (sourceOpinion, targetOpinion, edge, context) => {
204
- const dampedSource = applyPerHopDamping(
205
- sourceOpinion,
206
- context.spec.damping
207
- );
208
- const metadata = readEdgeMetadata(edge);
209
- if (metadata.conditionalA && metadata.conditionalNotA) {
210
- const deducedOpinion = conditionalDeduction(
211
- dampedSource,
212
- mkOpinion(
213
- metadata.conditionalA.b,
214
- metadata.conditionalA.d,
215
- metadata.conditionalA.u,
216
- metadata.conditionalA.a
217
- ),
218
- mkOpinion(
219
- metadata.conditionalNotA.b,
220
- metadata.conditionalNotA.d,
221
- metadata.conditionalNotA.u,
222
- metadata.conditionalNotA.a
223
- ),
224
- targetOpinion.a
149
+ function coerceVisibility(value) {
150
+ return value === "private" || value === "team" || value === "firm" || value === "external" || value === "public" ? value : void 0;
151
+ }
152
+ function coerceStatus(value) {
153
+ return value === "active" || value === "archived" || value === "watching" ? value : void 0;
154
+ }
155
+ function mapProjectType(topic, metadata) {
156
+ const explicit = readNonEmptyString(metadata.projectType);
157
+ if (explicit) {
158
+ return explicit;
159
+ }
160
+ if (topic.type === "theme") {
161
+ return "thematic";
162
+ }
163
+ return readNonEmptyString(topic.type) || "general";
164
+ }
165
+ function isProjectLikeTopic(topic) {
166
+ const metadata = readMetadata(topic);
167
+ return topic.type === "theme" || topic.type === "thematic" || topic.type === "deal" || topic.type === "monitoring" || readLegacyProjectId(topic) !== void 0 || readNonEmptyString(metadata.projectType) !== void 0;
168
+ }
169
+ function isMissingLucernChildComponentError(error) {
170
+ const message = getErrorMessage(error);
171
+ return message.includes(
172
+ 'Child component ComponentName(Identifier("lucern")) not found'
173
+ ) || message.includes("Child component") && message.includes("lucern") && message.includes("not found");
174
+ }
175
+ function getErrorMessage(error) {
176
+ if (error instanceof Error) {
177
+ return error.message;
178
+ }
179
+ if (typeof error === "object" && error !== null && "message" in error && typeof error.message === "string") {
180
+ return error.message;
181
+ }
182
+ return "unknown error";
183
+ }
184
+ async function resolveTopicDoc(ctx, scopeId) {
185
+ if (ctx?.db && typeof ctx.db.get === "function") {
186
+ try {
187
+ const directTopic = await ctx.db.get(
188
+ scopeId
225
189
  );
226
- return annotateRationale(
190
+ if (directTopic) {
191
+ return directTopic;
192
+ }
193
+ } catch (error) {
194
+ debugGraphPrimitiveFallback(
195
+ "[topicProjectOverlay] Failed to resolve topic by direct ID",
227
196
  {
228
- opinion: deducedOpinion,
229
- operator: "conditional_deduction",
230
- rationale: `Conditional deduction: prerequisite at ${project(
231
- dampedSource
232
- ).toFixed(2)}`
233
- },
234
- context.spec,
235
- context.hop
197
+ error,
198
+ scopeId
199
+ }
236
200
  );
237
201
  }
238
- const result = dampedDependencyCascade(
239
- dampedSource,
240
- targetOpinion,
241
- metadata.propagation ?? "continuous"
242
- );
243
- return annotateRationale(result, context.spec, context.hop);
244
- },
245
- description: "Structural gating. Textbook conditional deduction when edge conditionals exist, otherwise damped dependency cascade through downstream chains."
246
- };
247
-
248
- // src/edges/derivedFrom.ts
249
- var derivedFromPropagationSpec = {
250
- edgeType: "derived_from",
251
- direction: "incoming",
252
- transitivity: "none",
253
- damping: 1,
254
- maxHops: 1,
255
- operator: () => null,
256
- description: "Provenance only. The traversal surface stays explicit, but confidence does not move across derived_from edges."
257
- };
258
-
259
- // src/edges/elaborates.ts
260
- var elaboratesPropagationSpec = {
261
- edgeType: "elaborates",
262
- direction: "outgoing",
263
- transitivity: "damped",
264
- damping: 0.7,
265
- maxHops: 2,
266
- operator: (sourceOpinion, targetOpinion, edge, context) => {
267
- const dampedSource = applyPerHopDamping(
268
- sourceOpinion,
269
- context.spec.damping
270
- );
271
- const contextualWeight = Math.min(Math.abs(edge.weight ?? 0.35), 0.35);
272
- const result = propagatePositiveInform(
273
- dampedSource,
274
- targetOpinion,
275
- contextualWeight
202
+ }
203
+ if (typeof ctx.runQuery !== "function") {
204
+ return null;
205
+ }
206
+ try {
207
+ const topic = await ctx.runQuery(api.topics.get, {
208
+ id: String(scopeId)
209
+ });
210
+ if (topic?.name !== void 0 && topic?.type !== void 0) {
211
+ return topic;
212
+ }
213
+ } catch (error) {
214
+ debugGraphPrimitiveFallback(
215
+ "[topicProjectOverlay] Failed to resolve topic by ID query",
216
+ {
217
+ error,
218
+ scopeId
219
+ }
276
220
  );
277
- return annotateRationale(result, context.spec, context.hop);
278
- },
279
- description: "Context-rich supporting detail. Elaborates carries a small positive effect with short, damped chaining."
280
- };
281
-
282
- // src/edges/informs.ts
283
- var informsPropagationSpec = {
284
- edgeType: "informs",
285
- direction: "outgoing",
286
- transitivity: "full",
287
- damping: 0.92,
288
- maxHops: "unbounded",
289
- operator: (sourceOpinion, targetOpinion, edge, context) => {
290
- const dampedSource = applyPerHopDamping(
291
- sourceOpinion,
292
- context.spec.damping
293
- );
294
- const weight = edge.weight ?? 1;
295
- const result = weight < 0 ? propagateNegativeInform(dampedSource, targetOpinion, weight) : propagatePositiveInform(dampedSource, targetOpinion, weight);
296
- return annotateRationale(result, context.spec, context.hop);
297
- },
298
- description: "Evidence-bearing influence. Informs can chain through the graph with light per-hop damping."
299
- };
300
-
301
- // src/edges/propagationTypes.ts
302
- function isPropagationTraversalDirection(direction) {
303
- return direction === "outgoing" || direction === "incoming";
304
- }
305
- function canTraverseHop(spec, nextHop) {
306
- return spec.maxHops === "unbounded" || nextHop <= spec.maxHops;
307
- }
308
- function canContinueTransitively(spec, currentHop) {
309
- if (spec.transitivity === "none") {
310
- return false;
311
221
  }
312
- return spec.maxHops === "unbounded" || currentHop < spec.maxHops;
313
- }
314
-
315
- // src/edges/refutes.ts
316
- var refutesPropagationSpec = {
317
- edgeType: "refutes",
318
- direction: "outgoing",
319
- transitivity: "none",
320
- damping: 0.9,
321
- maxHops: 1,
322
- operator: (sourceOpinion, targetOpinion, edge, context) => {
323
- const dampedSource = applyPerHopDamping(
324
- sourceOpinion,
325
- context.spec.damping
326
- );
327
- const negativeWeight = -Math.abs(edge.weight ?? 1);
328
- const result = propagateNegativeInform(
329
- dampedSource,
330
- targetOpinion,
331
- negativeWeight
332
- );
333
- return annotateRationale(result, context.spec, context.hop);
334
- },
335
- description: "Explicit negative evidence semantics. Refutes is treated as strong one-hop counter-evidence."
336
- };
337
-
338
- // src/edges/supports.ts
339
- var supportsPropagationSpec = {
340
- edgeType: "supports",
341
- direction: "outgoing",
342
- transitivity: "full",
343
- damping: 0.85,
344
- maxHops: "unbounded",
345
- operator: (sourceOpinion, targetOpinion, edge, context) => {
346
- const dampedSource = applyPerHopDamping(
347
- sourceOpinion,
348
- context.spec.damping
222
+ try {
223
+ const topic = await ctx.runQuery(api.topics.getByLegacyScopeId, {
224
+ projectId: String(scopeId)
225
+ });
226
+ if (topic?.name !== void 0 && topic?.type !== void 0) {
227
+ return topic;
228
+ }
229
+ } catch (error) {
230
+ debugGraphPrimitiveFallback(
231
+ "[topicProjectOverlay] Failed to resolve topic by legacy scope ID",
232
+ { error, scopeId }
349
233
  );
350
- const weight = edge.weight ?? 1;
351
- const result = weight < 0 ? propagateNegativeSupportWithMetadata(
352
- dampedSource,
353
- targetOpinion,
354
- weight,
355
- edge
356
- ) : propagatePositiveSupport(dampedSource, targetOpinion, weight);
357
- return annotateRationale(result, context.spec, context.hop);
358
- },
359
- description: "Belief-to-belief influence. Supports chains transitively with moderate per-hop damping."
360
- };
361
-
362
- // src/edges/tests.ts
363
- var testsPropagationSpec = {
364
- edgeType: "tests",
365
- direction: "outgoing",
366
- transitivity: "none",
367
- damping: 1,
368
- maxHops: 1,
369
- operator: () => null,
370
- description: "Interrogation linkage only. Tests edges do not directly move confidence."
371
- };
372
-
373
- // src/edges/index.ts
374
- var EDGE_PROPAGATION_SPECS = [
375
- supportsPropagationSpec,
376
- informsPropagationSpec,
377
- dependsOnPropagationSpec,
378
- derivedFromPropagationSpec,
379
- containsPropagationSpec,
380
- testsPropagationSpec,
381
- contradictsPropagationSpec,
382
- refutesPropagationSpec,
383
- elaboratesPropagationSpec
384
- ];
385
- new Map(EDGE_PROPAGATION_SPECS.map((spec) => [spec.edgeType, spec]));
386
- function getEdgePropagationSpecs() {
387
- return EDGE_PROPAGATION_SPECS;
388
- }
389
- function getTraversalDirections(direction) {
390
- if (isPropagationTraversalDirection(direction)) {
391
- return [direction];
392
234
  }
393
- return ["outgoing", "incoming"];
394
- }
395
-
396
- // src/debug.ts
397
- function isGraphPrimitiveDebugEnabled() {
398
- const env = globalThis.process?.env;
399
- return env?.LUCERN_COMPAT_FALLBACK_DEBUG === "1" || env?.LUCERN_GRAPH_DEBUG === "1";
235
+ return null;
400
236
  }
401
- function debugGraphPrimitiveFallback(message, context) {
402
- if (!isGraphPrimitiveDebugEnabled()) {
403
- return;
404
- }
405
- console.debug(message, context ?? {});
237
+ function materializeTopicProjectOverlay(topic, idMode = "legacy") {
238
+ const metadata = readMetadata(topic);
239
+ const topicId = String(topic._id);
240
+ const legacyProjectId = readLegacyProjectId(topic) || readLegacyProjectId(metadata) || readNonEmptyString(metadata.legacyProjectId);
241
+ const storageProjectId = legacyProjectId || topicId;
242
+ const outwardId = idMode === "topic" ? topicId : storageProjectId;
243
+ const visibility = coerceVisibility(topic.visibility) || coerceVisibility(metadata.visibility) || "private";
244
+ const status = coerceStatus(topic.status) || coerceStatus(metadata.status) || "active";
245
+ const createdAt = typeof topic.createdAt === "number" ? topic.createdAt : typeof topic._creationTime === "number" ? topic._creationTime : 0;
246
+ const updatedAt = typeof topic.updatedAt === "number" ? topic.updatedAt : typeof metadata.updatedAt === "number" ? metadata.updatedAt : createdAt;
247
+ return {
248
+ ...metadata,
249
+ _id: outwardId,
250
+ projectId: outwardId,
251
+ topicId,
252
+ storageProjectId,
253
+ legacyProjectId,
254
+ name: readNonEmptyString(topic.name) || "Untitled Theme",
255
+ type: mapProjectType(topic, metadata),
256
+ description: readNonEmptyString(topic.description),
257
+ ownerId: readNonEmptyString(metadata.ownerId) || readNonEmptyString(topic.createdBy) || "system",
258
+ sharedWith: readStringArray(metadata.sharedWith),
259
+ visibility,
260
+ tenantId: readNonEmptyString(topic.tenantId) || readNonEmptyString(metadata.tenantId),
261
+ workspaceId: readNonEmptyString(topic.workspaceId) || readNonEmptyString(metadata.workspaceId),
262
+ status,
263
+ tags: readStringArray(metadata.tags),
264
+ chatCount: typeof metadata.chatCount === "number" ? metadata.chatCount : 0,
265
+ artifactCount: typeof metadata.artifactCount === "number" ? metadata.artifactCount : 0,
266
+ lastActivityAt: typeof metadata.lastActivityAt === "number" ? metadata.lastActivityAt : updatedAt,
267
+ _creationTime: typeof topic._creationTime === "number" ? topic._creationTime : createdAt,
268
+ createdAt,
269
+ updatedAt
270
+ };
406
271
  }
407
-
408
- // src/topicScope.ts
409
- var LEGACY_SCOPE_FIELD = "graphScopeProjectId";
410
- function asMappedProjectId(topic) {
272
+ async function resolveTopicProjectOverlay(ctx, scopeId, options = {}) {
273
+ const topic = await resolveTopicDoc(ctx, scopeId);
411
274
  if (!topic) {
412
- return;
413
- }
414
- const directLegacyProjectId = normalizeScopeValue(topic[LEGACY_SCOPE_FIELD]);
415
- if (directLegacyProjectId) {
416
- return directLegacyProjectId;
275
+ return null;
417
276
  }
418
- const metadata = topic.metadata || {};
419
- const candidate = metadata[LEGACY_SCOPE_FIELD] || metadata.legacyProjectId || metadata.projectId || metadata.scopeProjectId;
420
- return candidate ? candidate : void 0;
421
- }
422
- function normalizeScopeValue(value) {
423
- if (typeof value !== "string") {
424
- return;
277
+ if (options.projectLikeOnly !== false && !isProjectLikeTopic(topic)) {
278
+ return null;
425
279
  }
426
- const normalized = value.trim();
427
- return normalized.length > 0 ? normalized : void 0;
280
+ return materializeTopicProjectOverlay(topic, options.idMode);
428
281
  }
429
- function pickPrimaryTopic(candidates) {
430
- return [...candidates].sort((a, b) => {
431
- const depthA = a.depth ?? 9999;
432
- const depthB = b.depth ?? 9999;
433
- if (depthA !== depthB) {
434
- return depthA - depthB;
435
- }
436
- const createdA = a.createdAt ?? Number.MAX_SAFE_INTEGER;
437
- const createdB = b.createdAt ?? Number.MAX_SAFE_INTEGER;
438
- if (createdA !== createdB) {
439
- return createdA - createdB;
282
+ async function listTopicProjectOverlays(ctx, options = {}) {
283
+ let allTopics = [];
284
+ if (ctx?.db?.query && typeof ctx.db.query === "function") {
285
+ try {
286
+ allTopics = await ctx.db.query("topics").collect();
287
+ } catch (error) {
288
+ debugGraphPrimitiveFallback(
289
+ "[topicProjectOverlay] Failed to read topics table; falling back to API",
290
+ { error }
291
+ );
292
+ allTopics = [];
440
293
  }
441
- return String(a.name || "").localeCompare(String(b.name || ""));
442
- })[0];
443
- }
444
- async function findTopicsByScopeAlias(ctx, scopeId) {
445
- try {
446
- return await ctx.db.query("topics").withIndex(
447
- "by_graph_scope_project",
448
- (q) => q.eq(LEGACY_SCOPE_FIELD, scopeId)
449
- ).collect();
450
- } catch (error) {
451
- debugGraphPrimitiveFallback(
452
- "[topicScope] Failed to resolve scope alias via index",
453
- {
454
- error,
455
- scopeId
456
- }
457
- );
458
- const topics = await ctx.db.query("topics").collect();
459
- return topics.filter((topic) => {
460
- const normalizedGlobalId = normalizeScopeValue(topic.globalId);
461
- const mappedProjectId = asMappedProjectId(topic);
462
- return String(topic._id) === scopeId || normalizedGlobalId === scopeId || mappedProjectId === scopeId;
463
- });
464
294
  }
295
+ if (allTopics.length === 0 && typeof ctx.runQuery === "function") {
296
+ allTopics = (await ctx.runQuery(api.topics.list, {}) ?? []) || [];
297
+ }
298
+ return allTopics.filter(
299
+ (topic) => options.projectLikeOnly === false || isProjectLikeTopic(topic)
300
+ ).map((topic) => materializeTopicProjectOverlay(topic, options.idMode));
465
301
  }
466
- async function tryResolveHostTopicById(ctx, topicId) {
467
- if (typeof ctx.runQuery !== "function") {
302
+ async function patchTopicProjectOverlay(ctx, scopeId, value) {
303
+ const topic = await resolveTopicDoc(ctx, scopeId);
304
+ if (!topic) {
468
305
  return null;
469
306
  }
470
- try {
471
- return await ctx.runQuery(api.topics.get, {
472
- id: topicId
473
- }) ?? null;
474
- } catch (error) {
475
- debugGraphPrimitiveFallback(
476
- "[topicScope] Failed to resolve topic by host query",
477
- {
478
- error,
479
- topicId
480
- }
481
- );
307
+ const nextMetadata = { ...readMetadata(topic) };
308
+ const patch = {};
309
+ const topicUpdateArgs = {
310
+ id: String(topic._id)
311
+ };
312
+ for (const [key, rawValue] of Object.entries(value)) {
313
+ switch (key) {
314
+ case "_id":
315
+ case "projectId":
316
+ case "topicId":
317
+ case "legacyProjectId":
318
+ case "storageProjectId":
319
+ break;
320
+ case "name":
321
+ case "description":
322
+ patch[key] = rawValue;
323
+ topicUpdateArgs[key] = rawValue;
324
+ break;
325
+ case "tenantId":
326
+ case "workspaceId":
327
+ case "ownerId":
328
+ throw new Error(
329
+ `patchTopicProjectOverlay cannot mutate ${key} via component-owned topics`
330
+ );
331
+ case "status": {
332
+ const status = coerceStatus(rawValue);
333
+ if (status) {
334
+ patch.status = status;
335
+ topicUpdateArgs.status = status;
336
+ }
337
+ break;
338
+ }
339
+ case "visibility": {
340
+ const visibility = coerceVisibility(rawValue);
341
+ if (visibility) {
342
+ patch.visibility = visibility;
343
+ topicUpdateArgs.visibility = visibility;
344
+ }
345
+ break;
346
+ }
347
+ case "type": {
348
+ const projectType = readNonEmptyString(rawValue);
349
+ if (projectType) {
350
+ nextMetadata.projectType = projectType;
351
+ } else {
352
+ delete nextMetadata.projectType;
353
+ }
354
+ break;
355
+ }
356
+ case "updatedAt":
357
+ case "createdAt":
358
+ break;
359
+ default:
360
+ if (rawValue === void 0) {
361
+ delete nextMetadata[key];
362
+ } else {
363
+ nextMetadata[key] = rawValue;
364
+ }
365
+ }
366
+ }
367
+ patch.updatedAt = Date.now();
368
+ patch.metadata = nextMetadata;
369
+ topicUpdateArgs.metadata = nextMetadata;
370
+ if (typeof ctx.runMutation === "function") {
371
+ try {
372
+ await ctx.runMutation(api.topics.update, topicUpdateArgs);
373
+ } catch (error) {
374
+ if (!isMissingLucernChildComponentError(error) || !ctx?.db || typeof ctx.db.patch !== "function") {
375
+ throw error;
376
+ }
377
+ await ctx.db.patch(String(topic._id), patch);
378
+ }
379
+ } else if (ctx?.db && typeof ctx.db.patch === "function") {
380
+ await ctx.db.patch(String(topic._id), patch);
381
+ } else {
382
+ throw new Error(
383
+ "Cannot patch topic without component adapter (ctx.runMutation unavailable)"
384
+ );
385
+ }
386
+ return materializeTopicProjectOverlay({
387
+ ...topic,
388
+ ...patch,
389
+ metadata: nextMetadata
390
+ });
391
+ }
392
+
393
+ // src/resolvers.ts
394
+ function isMissingLucernChildComponentError2(error) {
395
+ const message = getErrorMessage2(error);
396
+ return message.includes(
397
+ 'Child component ComponentName(Identifier("lucern")) not found'
398
+ ) || message.includes("Child component") && message.includes("lucern") && message.includes("not found");
399
+ }
400
+ function getErrorMessage2(error) {
401
+ if (error instanceof Error) {
402
+ return error.message;
403
+ }
404
+ if (typeof error === "object" && error !== null && "message" in error && typeof error.message === "string") {
405
+ return error.message;
406
+ }
407
+ return "unknown error";
408
+ }
409
+ function isAdvisoryTopicPatch(value) {
410
+ const advisoryKeys = /* @__PURE__ */ new Set(["lastActivityAt", "updatedAt"]);
411
+ const keys = Object.keys(value);
412
+ return keys.length > 0 && keys.every((key) => advisoryKeys.has(key));
413
+ }
414
+ async function patchProjectWithTolerance(ctx, projectId, value) {
415
+ try {
416
+ await patchTopicProjectOverlay(ctx, projectId, value);
417
+ } catch (error) {
418
+ if (!isAdvisoryTopicPatch(value) || !isMissingLucernChildComponentError2(error)) {
419
+ throw error;
420
+ }
421
+ console.warn(
422
+ "[lucern graph-primitives] Non-fatal advisory topic patch failure",
423
+ {
424
+ projectId,
425
+ keys: Object.keys(value),
426
+ error: getErrorMessage2(error)
427
+ }
428
+ );
429
+ }
430
+ }
431
+ function defaultResolvers() {
432
+ return {
433
+ getProject: (ctx, projectId) => resolveTopicProjectOverlay(ctx, projectId, {
434
+ idMode: "legacy",
435
+ projectLikeOnly: false
436
+ }),
437
+ patchProject: (ctx, projectId, value) => patchProjectWithTolerance(ctx, projectId, value),
438
+ listTopics: (ctx) => listTopicProjectOverlays(ctx, {
439
+ idMode: "legacy"
440
+ }),
441
+ getFinalArtifact: (ctx, artifactId) => ctx.db.get(artifactId)
442
+ };
443
+ }
444
+ var resolverOverrides = {};
445
+ function resolveGraphPrimitivesAppResolvers(_ctx) {
446
+ return {
447
+ ...defaultResolvers(),
448
+ ...resolverOverrides
449
+ };
450
+ }
451
+ var LEGACY_SCOPE_FIELD2 = "graphScopeProjectId";
452
+ function asMappedProjectId(topic) {
453
+ if (!topic) {
454
+ return;
455
+ }
456
+ const directLegacyProjectId = normalizeScopeValue(topic[LEGACY_SCOPE_FIELD2]);
457
+ if (directLegacyProjectId) {
458
+ return directLegacyProjectId;
459
+ }
460
+ const metadata = topic.metadata || {};
461
+ const candidate = metadata[LEGACY_SCOPE_FIELD2] || metadata.legacyProjectId || metadata.projectId || metadata.scopeProjectId;
462
+ return candidate ? candidate : void 0;
463
+ }
464
+ function normalizeScopeValue(value) {
465
+ if (typeof value !== "string") {
466
+ return;
467
+ }
468
+ const normalized = value.trim();
469
+ return normalized.length > 0 ? normalized : void 0;
470
+ }
471
+ function pickPrimaryTopic(candidates) {
472
+ return [...candidates].sort((a, b) => {
473
+ const depthA = a.depth ?? 9999;
474
+ const depthB = b.depth ?? 9999;
475
+ if (depthA !== depthB) {
476
+ return depthA - depthB;
477
+ }
478
+ const createdA = a.createdAt ?? Number.MAX_SAFE_INTEGER;
479
+ const createdB = b.createdAt ?? Number.MAX_SAFE_INTEGER;
480
+ if (createdA !== createdB) {
481
+ return createdA - createdB;
482
+ }
483
+ return String(a.name || "").localeCompare(String(b.name || ""));
484
+ })[0];
485
+ }
486
+ async function findTopicsByScopeAlias(ctx, scopeId) {
487
+ try {
488
+ return await ctx.db.query("topics").withIndex(
489
+ "by_graph_scope_project",
490
+ (q) => q.eq(LEGACY_SCOPE_FIELD2, scopeId)
491
+ ).collect();
492
+ } catch (error) {
493
+ debugGraphPrimitiveFallback(
494
+ "[topicScope] Failed to resolve scope alias via index",
495
+ {
496
+ error,
497
+ scopeId
498
+ }
499
+ );
500
+ const topics = await ctx.db.query("topics").collect();
501
+ return topics.filter((topic) => {
502
+ const normalizedGlobalId = normalizeScopeValue(topic.globalId);
503
+ const mappedProjectId = asMappedProjectId(topic);
504
+ return String(topic._id) === scopeId || normalizedGlobalId === scopeId || mappedProjectId === scopeId;
505
+ });
506
+ }
507
+ }
508
+ async function tryResolveHostTopicById(ctx, topicId) {
509
+ if (typeof ctx.runQuery !== "function") {
510
+ return null;
511
+ }
512
+ try {
513
+ return await ctx.runQuery(api.topics.get, {
514
+ id: topicId
515
+ }) ?? null;
516
+ } catch (error) {
517
+ debugGraphPrimitiveFallback(
518
+ "[topicScope] Failed to resolve topic by host query",
519
+ {
520
+ error,
521
+ topicId
522
+ }
523
+ );
482
524
  return null;
483
525
  }
484
526
  }
@@ -629,8 +671,6 @@ var optionalScopeArgs = {
629
671
  projectId: v.optional(v.string()),
630
672
  topicId: v.optional(v.string())
631
673
  };
632
-
633
- // src/workspaceIsolation.ts
634
674
  function normalizeScopeValue2(value) {
635
675
  if (typeof value !== "string") {
636
676
  return;
@@ -780,896 +820,786 @@ function assertTenantPackWorkspaceMutationAllowed(args) {
780
820
  });
781
821
  }
782
822
 
783
- // src/confidencePropagationDispatch.ts
784
- function resolveTraversalTargetNodeId(edge, direction) {
785
- const targetNodeId = direction === "outgoing" ? edge.toNodeId : edge.fromNodeId;
786
- return targetNodeId ?? void 0;
823
+ // src/epistemicBeliefs.helpers.ts
824
+ var insightIdUnion = v.id("epistemicNodes");
825
+ var DEFAULT_PROJECT_BELIEF_LIMIT = 250;
826
+ var MAX_PROJECT_BELIEF_LIMIT = 1e3;
827
+ var optionalBeliefScopeArgs = optionalScopeArgs;
828
+ var DEFAULT_CONFIDENCE_POLICY = {
829
+ scoringMode: "after_worktree",
830
+ tupleContradiction: normalizeTupleContradictionPolicy()
831
+ };
832
+ function throwStructuredMutationError(args) {
833
+ const error = new Error(args.message);
834
+ error.status = args.status;
835
+ error.code = args.code;
836
+ error.invariantCode = args.invariantCode;
837
+ error.suggestion = args.suggestion;
838
+ error.details = args.details;
839
+ throw error;
787
840
  }
788
- function readNodeOpinion(node) {
789
- const metadata = node.metadata ?? {};
790
- try {
791
- return readOpinionFromRecord({
792
- ...metadata,
793
- opinion_b: node.opinion_b,
794
- opinion_d: node.opinion_d,
795
- opinion_u: node.opinion_u,
796
- opinion_a: node.opinion_a
797
- });
798
- } catch {
799
- return mkOpinion(0, 0, 1, 0.5);
800
- }
841
+ function readFiniteNumber(value) {
842
+ return typeof value === "number" && Number.isFinite(value) ? value : void 0;
801
843
  }
802
- async function collectConfidencePropagationDispatches(args) {
803
- const dispatchesByTargetId = /* @__PURE__ */ new Map();
804
- const opinionCache = /* @__PURE__ */ new Map();
805
- const nodeCache = /* @__PURE__ */ new Map();
806
- const traversalSpecs = args.traversalSpecs ?? getEdgePropagationSpecs();
807
- const queue = [
808
- {
809
- nodeId: args.sourceNodeId,
810
- opinion: args.sourceOpinion,
811
- hop: 0,
812
- visitedNodeIds: /* @__PURE__ */ new Set([String(args.sourceNodeId)])
813
- }
814
- ];
815
- const loadNode = async (nodeId) => {
816
- const cacheKey = String(nodeId);
817
- if (!nodeCache.has(cacheKey)) {
818
- nodeCache.set(cacheKey, await args.getNode(nodeId));
819
- }
820
- return nodeCache.get(cacheKey) ?? null;
844
+ function clamp01(value) {
845
+ return Math.max(0, Math.min(1, value));
846
+ }
847
+ function assertBaseRateInRange(baseRate, field = "baseRate") {
848
+ if (baseRate < 0 || baseRate > 1) {
849
+ throwStructuredMutationError({
850
+ message: `${field} must be within [0, 1].`,
851
+ status: 400,
852
+ code: "INVALID_ARGUMENT",
853
+ invariantCode: "request.valid_shape",
854
+ suggestion: `Clamp ${field} into the inclusive [0, 1] interval.`,
855
+ details: { field, baseRate }
856
+ });
857
+ }
858
+ return baseRate;
859
+ }
860
+ function buildBeliefConfidenceRow(args) {
861
+ return {
862
+ beliefId: args.beliefId,
863
+ confidence: confidenceFromSL(
864
+ args.belief,
865
+ args.disbelief,
866
+ args.uncertainty,
867
+ args.baseRate
868
+ ),
869
+ belief: args.belief,
870
+ disbelief: args.disbelief,
871
+ uncertainty: args.uncertainty,
872
+ baseRate: args.baseRate,
873
+ slOperator: args.slOperator ?? "manual_assessment",
874
+ trigger: args.trigger,
875
+ ...args.rationale ? { rationale: args.rationale } : {},
876
+ assessedBy: args.assessedBy,
877
+ assessedAt: args.assessedAt,
878
+ ...args.triggeringEvidenceId ? {
879
+ triggeringEvidenceId: args.triggeringEvidenceId,
880
+ triggeringEvidenceIds: [String(args.triggeringEvidenceId)]
881
+ } : {},
882
+ ...args.triggeringContradictionId ? {
883
+ triggeringContradictionId: args.triggeringContradictionId
884
+ } : {},
885
+ ...args.triggeringWorktreeId ? { triggeringWorktreeId: args.triggeringWorktreeId } : {}
821
886
  };
822
- while (queue.length > 0) {
823
- const state = queue.shift();
824
- if (!state) {
825
- continue;
826
- }
827
- for (const spec of traversalSpecs) {
828
- const nextHop = state.hop + 1;
829
- if (!canTraverseHop(spec, nextHop)) {
830
- continue;
831
- }
832
- for (const direction of getTraversalDirections(spec.direction)) {
833
- const edges = await args.queryEdges({
834
- nodeId: state.nodeId,
835
- spec,
836
- direction,
837
- hop: nextHop
838
- });
839
- for (const edge of edges) {
840
- if (args.sourceScope && !edgeMatchesWorkspaceReasoningScope(edge, args.sourceScope)) {
841
- continue;
842
- }
843
- const targetNodeId = resolveTraversalTargetNodeId(edge, direction);
844
- if (!targetNodeId) {
845
- continue;
846
- }
847
- if (state.visitedNodeIds.has(String(targetNodeId))) {
848
- continue;
849
- }
850
- const targetNode = await loadNode(targetNodeId);
851
- if (!targetNode || targetNode.nodeType !== "belief") {
852
- continue;
853
- }
854
- if (args.sourceScope && !nodeMatchesWorkspaceReasoningScope(targetNode, args.sourceScope)) {
855
- continue;
856
- }
857
- const cacheKey = String(targetNodeId);
858
- const targetOpinion = opinionCache.get(cacheKey) ?? readNodeOpinion(targetNode);
859
- const result = spec.operator(state.opinion, targetOpinion, edge, {
860
- hop: nextHop,
861
- sourceNodeId: state.nodeId,
862
- targetNodeId,
863
- traversedDirection: direction,
864
- spec
865
- });
866
- if (!result || !hasProjectedOpinionChanged(targetOpinion, result.opinion)) {
867
- continue;
868
- }
869
- const projectedOpinion = mkOpinion(
870
- result.opinion.b,
871
- result.opinion.d,
872
- result.opinion.u,
873
- result.opinion.a
874
- );
875
- opinionCache.set(cacheKey, projectedOpinion);
876
- const existingDispatch = dispatchesByTargetId.get(cacheKey);
877
- dispatchesByTargetId.set(cacheKey, {
878
- targetNodeId,
879
- edgeType: spec.edgeType,
880
- traversedDirection: direction,
881
- weight: edge.weight ?? 1,
882
- opinion: projectedOpinion,
883
- operator: result.operator,
884
- rationale: existingDispatch ? `${existingDispatch.rationale}; ${result.rationale}` : result.rationale,
885
- hop: nextHop
886
- });
887
- if (canContinueTransitively(spec, nextHop)) {
888
- queue.push({
889
- nodeId: targetNodeId,
890
- opinion: projectedOpinion,
891
- hop: nextHop,
892
- visitedNodeIds: /* @__PURE__ */ new Set([
893
- ...state.visitedNodeIds,
894
- String(targetNodeId)
895
- ])
896
- });
897
- }
887
+ }
888
+ function buildBeliefStatusSuccessResult() {
889
+ return { success: true };
890
+ }
891
+ function buildBeliefEvidenceNotFoundResult() {
892
+ const result = {};
893
+ result.success = false;
894
+ result.message = "Evidence node not found";
895
+ return result;
896
+ }
897
+ function deriveSyntheticBackfillOpinion(source) {
898
+ const belief = readFiniteNumber(source.opinion_b) ?? readFiniteNumber(source.belief);
899
+ const disbelief = readFiniteNumber(source.opinion_d) ?? readFiniteNumber(source.disbelief);
900
+ const uncertainty = readFiniteNumber(source.opinion_u) ?? readFiniteNumber(source.uncertainty);
901
+ const baseRate = readFiniteNumber(source.opinion_a) ?? readFiniteNumber(source.baseRate);
902
+ if (belief !== void 0 || disbelief !== void 0 || uncertainty !== void 0 || baseRate !== void 0) {
903
+ try {
904
+ return readOpinionFromRecord(source);
905
+ } catch (error) {
906
+ debugGraphPrimitiveFallback(
907
+ "[epistemicBeliefs] Failed to decode legacy belief opinion",
908
+ {
909
+ error
898
910
  }
899
- }
911
+ );
912
+ return mkOpinion(0, 0, 1, 0.5);
900
913
  }
901
914
  }
902
- return Array.from(dispatchesByTargetId.values()).sort((left, right) => {
903
- if (left.hop !== right.hop) {
904
- return left.hop - right.hop;
905
- }
906
- return String(left.targetNodeId).localeCompare(String(right.targetNodeId));
907
- });
915
+ const confidence = clamp01(readFiniteNumber(source.confidence) ?? 0);
916
+ return mkOpinion(confidence, 1 - confidence, 0, 0.5);
908
917
  }
909
-
910
- // src/embeddingTrigger.ts
911
- async function scheduleEmbeddingGeneration(args) {
918
+ function clampBeliefLimit(limit, fallback = DEFAULT_PROJECT_BELIEF_LIMIT) {
919
+ if (!Number.isFinite(limit)) {
920
+ return fallback;
921
+ }
922
+ return Math.max(
923
+ 1,
924
+ Math.min(Math.floor(limit), MAX_PROJECT_BELIEF_LIMIT)
925
+ );
926
+ }
927
+ function readTupleContradictedFlag(value) {
928
+ return typeof value === "boolean" ? value : void 0;
929
+ }
930
+ function readBeliefOpinionSnapshot(node, metadata) {
912
931
  try {
913
- await args.ctx.scheduler.runAfter(
914
- 0,
915
- "embeddingActions:generateEpistemicNodeEmbedding",
916
- {
917
- nodeId: args.nodeId,
918
- projectId: args.projectId ? String(args.projectId) : void 0,
919
- topicId: args.topicId ? String(args.topicId) : void 0,
920
- createdBy: args.createdBy,
921
- nodeType: args.nodeType,
922
- text: args.text.slice(0, 2e4),
923
- hasAnswer: args.hasAnswer,
924
- confidence: args.confidence
925
- }
926
- );
932
+ return readOpinionFromRecord({
933
+ ...metadata,
934
+ opinion_b: node.opinion_b,
935
+ opinion_d: node.opinion_d,
936
+ opinion_u: node.opinion_u,
937
+ opinion_a: node.opinion_a
938
+ });
927
939
  } catch (error) {
928
940
  debugGraphPrimitiveFallback(
929
- "[embeddingTrigger] Failed to schedule embedding generation",
941
+ "[epistemicBeliefs] Failed to read belief opinion snapshot",
930
942
  {
931
943
  error,
932
- nodeId: String(args.nodeId),
933
- nodeType: args.nodeType
944
+ beliefId: node._id
934
945
  }
935
946
  );
947
+ return mkOpinion(0, 0, 1, 0.5);
936
948
  }
937
949
  }
938
-
939
- // src/globalId.ts
940
- function generateGlobalId() {
941
- const bytes = new Uint8Array(16);
942
- crypto.getRandomValues(bytes);
943
- bytes[6] = bytes[6] & 15 | 64;
944
- bytes[8] = bytes[8] & 63 | 128;
945
- const hex = Array.from(bytes, (b) => b.toString(16).padStart(2, "0")).join(
946
- ""
947
- );
948
- return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20)}`;
949
- }
950
-
951
- // src/logicalRoleInference.ts
952
- var PILLAR_IMPORTANCE = {
953
- market: 1,
954
- competition: 2,
955
- product: 3,
956
- team: 4,
957
- financials: 5,
958
- timing: 6,
959
- other: 10
960
- };
961
- async function computeLogicalRole(ctx, evidenceId, beliefId) {
962
- const belief = await ctx.db.get(beliefId);
963
- if (!belief || belief.nodeType !== "belief") {
964
- return "contributory";
950
+ function deriveTupleContradictionSeverity(node) {
951
+ const metadata = node.metadata || {};
952
+ const criticality = typeof metadata.criticality === "string" ? metadata.criticality : void 0;
953
+ if (criticality === "blocking") {
954
+ return "critical";
965
955
  }
966
- const beliefMetadata = belief.metadata;
967
- const pillar = beliefMetadata?.pillar || "other";
968
- const pillarRank = PILLAR_IMPORTANCE[pillar] ?? 10;
969
- const isSynthesized = await checkIfSynthesizedHypothesis(ctx, beliefId);
970
- const testingQuestions = await getTestingQuestionsForBelief(ctx, beliefId);
971
- const answeredQuestions = await getQuestionsAnsweredByEvidence(
972
- ctx,
973
- evidenceId
974
- );
975
- const directlyTests = testingQuestions.filter(
976
- (questionId) => answeredQuestions.includes(questionId)
977
- );
978
- if (directlyTests.length === 0) {
979
- return "contributory";
956
+ if (criticality === "supporting") {
957
+ return "minor";
980
958
  }
981
- if (isSynthesized && pillarRank <= 2) {
982
- return directlyTests.length > 1 ? "necessary_sufficient" : "necessary";
983
- }
984
- if (isSynthesized) {
985
- return "necessary";
986
- }
987
- return directlyTests.length > 1 ? "necessary" : "contributory";
988
- }
989
- async function checkIfSynthesizedHypothesis(ctx, beliefId) {
990
- const belief = await ctx.db.get(beliefId);
991
- if (!belief) {
992
- return false;
993
- }
994
- const metadata = belief.metadata;
995
- return metadata?.isSynthesized === true || metadata?.beliefStatus === "hypothesis";
996
- }
997
- async function getTestingQuestionsForBelief(ctx, beliefId) {
998
- const testEdges = await ctx.db.query("epistemicEdges").withIndex(
999
- "by_to_type",
1000
- (q) => q.eq("toNodeId", beliefId).eq("edgeType", "tests")
1001
- ).collect();
1002
- return testEdges.map((edge) => edge.fromNodeId).filter((id) => id !== void 0);
959
+ return "significant";
1003
960
  }
1004
- async function getQuestionsAnsweredByEvidence(ctx, evidenceId) {
1005
- const answerEdges = await ctx.db.query("epistemicEdges").withIndex(
1006
- "by_from_type",
1007
- (q) => q.eq("fromNodeId", evidenceId).eq("edgeType", "derived_from")
1008
- ).collect();
1009
- return answerEdges.map((edge) => edge.toNodeId).filter((id) => id !== void 0);
961
+ function formatTupleContradictionDescription(args) {
962
+ return `Tuple-space contradiction detected: b=${args.opinion.b.toFixed(2)} > ${args.policy.beliefThreshold.toFixed(2)} and d=${args.opinion.d.toFixed(2)} > ${args.policy.disbeliefThreshold.toFixed(2)}.`;
1010
963
  }
1011
-
1012
- // src/topicProjectOverlay.ts
1013
- var LEGACY_SCOPE_FIELD2 = "graphScopeProjectId";
1014
- function readNonEmptyString(value) {
1015
- if (typeof value !== "string") {
1016
- return;
964
+ function generateContentHash(text) {
965
+ const content = `belief:${text.trim().toLowerCase().replace(/\s+/g, " ")}`;
966
+ let hash = 5381;
967
+ for (let i = 0; i < content.length; i++) {
968
+ hash = (hash << 5) + hash + content.charCodeAt(i);
969
+ hash &= hash;
1017
970
  }
1018
- const normalized = value.trim();
1019
- return normalized.length > 0 ? normalized : void 0;
971
+ return Math.abs(hash).toString(16).padStart(8, "0");
1020
972
  }
1021
- function readStringArray(value) {
1022
- if (!Array.isArray(value)) {
1023
- return [];
973
+ function resolveBeliefWorktreeId(metadata) {
974
+ const worktreeId = metadata?.worktreeId;
975
+ if (typeof worktreeId === "string" && worktreeId.trim().length > 0) {
976
+ return worktreeId;
1024
977
  }
1025
- return value.map((entry) => readNonEmptyString(entry)).filter((entry) => Boolean(entry));
1026
- }
1027
- function readMetadata(topic) {
1028
- return topic.metadata && typeof topic.metadata === "object" ? topic.metadata : {};
978
+ const sprintId = metadata?.sprintId;
979
+ return typeof sprintId === "string" && sprintId.trim().length > 0 ? sprintId : void 0;
1029
980
  }
1030
- function readLegacyProjectId(value) {
1031
- if (!value) {
1032
- return;
981
+ function normalizePillar(pillar) {
982
+ if (!pillar) {
983
+ return "other";
1033
984
  }
1034
- return readNonEmptyString(value[LEGACY_SCOPE_FIELD2]);
1035
- }
1036
- function coerceVisibility(value) {
1037
- return value === "private" || value === "team" || value === "firm" || value === "external" || value === "public" ? value : void 0;
1038
- }
1039
- function coerceStatus(value) {
1040
- return value === "active" || value === "archived" || value === "watching" ? value : void 0;
985
+ const lower = pillar.toLowerCase();
986
+ const validPillars = [
987
+ "market",
988
+ "competition",
989
+ "product",
990
+ "team",
991
+ "financials",
992
+ "regulatory",
993
+ "timing",
994
+ "customer",
995
+ "technology",
996
+ "distribution",
997
+ "deal",
998
+ "risks"
999
+ ];
1000
+ return validPillars.find((p) => lower.includes(p)) || "other";
1041
1001
  }
1042
- function mapProjectType(topic, metadata) {
1043
- const explicit = readNonEmptyString(metadata.projectType);
1044
- if (explicit) {
1045
- return explicit;
1046
- }
1047
- if (topic.type === "theme") {
1048
- return "thematic";
1002
+ async function markBeliefGraphDirty(ctx, scope) {
1003
+ const projectId = typeof scope.projectId === "string" && scope.projectId.trim().length > 0 ? scope.projectId : void 0;
1004
+ const topicId = typeof scope.topicId === "string" && scope.topicId.trim().length > 0 ? scope.topicId : void 0;
1005
+ if (!projectId && !topicId) {
1006
+ return;
1049
1007
  }
1050
- return readNonEmptyString(topic.type) || "general";
1051
- }
1052
- function isProjectLikeTopic(topic) {
1053
- const metadata = readMetadata(topic);
1054
- return topic.type === "theme" || topic.type === "thematic" || topic.type === "deal" || topic.type === "monitoring" || readLegacyProjectId(topic) !== void 0 || readNonEmptyString(metadata.projectType) !== void 0;
1055
- }
1056
- function isMissingLucernChildComponentError(error) {
1057
- const message = getErrorMessage(error);
1058
- return message.includes(
1059
- 'Child component ComponentName(Identifier("lucern")) not found'
1060
- ) || message.includes("Child component") && message.includes("lucern") && message.includes("not found");
1061
- }
1062
- function getErrorMessage(error) {
1063
- if (error instanceof Error) {
1064
- return error.message;
1008
+ if (projectId) {
1009
+ await ctx.scheduler.runAfter(
1010
+ 0,
1011
+ internal.graphAnalysisCache.markCacheStaleInternal,
1012
+ { projectId }
1013
+ );
1065
1014
  }
1066
- if (typeof error === "object" && error !== null && "message" in error && typeof error.message === "string") {
1067
- return error.message;
1015
+ if (topicId) {
1016
+ await ctx.scheduler.runAfter(
1017
+ 0,
1018
+ internal.graphAnalysisCache.markCacheStaleByTopic,
1019
+ { topicId }
1020
+ );
1068
1021
  }
1069
- return "unknown error";
1070
- }
1071
- async function resolveTopicDoc(ctx, scopeId) {
1072
- if (ctx?.db && typeof ctx.db.get === "function") {
1073
- try {
1074
- const directTopic = await ctx.db.get(
1075
- scopeId
1076
- );
1077
- if (directTopic) {
1078
- return directTopic;
1079
- }
1080
- } catch (error) {
1081
- debugGraphPrimitiveFallback(
1082
- "[topicProjectOverlay] Failed to resolve topic by direct ID",
1083
- {
1084
- error,
1085
- scopeId
1086
- }
1087
- );
1022
+ await resolveGraphPrimitivesAppResolvers().patchProject(
1023
+ ctx,
1024
+ topicId ?? projectId,
1025
+ {
1026
+ lastActivityAt: Date.now()
1088
1027
  }
1089
- }
1090
- if (typeof ctx.runQuery !== "function") {
1028
+ );
1029
+ }
1030
+ async function resolveBeliefScopeOrNull(ctx, args) {
1031
+ if (!args.projectId && !args.topicId) {
1091
1032
  return null;
1092
1033
  }
1093
1034
  try {
1094
- const topic = await ctx.runQuery(api.topics.get, {
1095
- id: String(scopeId)
1035
+ return await resolveTopicProjectScope(ctx, {
1036
+ projectId: args.projectId ?? void 0,
1037
+ topicId: args.topicId ?? void 0
1096
1038
  });
1097
- if (topic?.name !== void 0 && topic?.type !== void 0) {
1098
- return topic;
1099
- }
1100
1039
  } catch (error) {
1101
1040
  debugGraphPrimitiveFallback(
1102
- "[topicProjectOverlay] Failed to resolve topic by ID query",
1041
+ "[epistemicBeliefs] Failed to resolve belief scope",
1103
1042
  {
1104
1043
  error,
1105
- scopeId
1044
+ projectId: args.projectId,
1045
+ topicId: args.topicId
1106
1046
  }
1107
1047
  );
1048
+ return null;
1108
1049
  }
1109
- try {
1110
- const topic = await ctx.runQuery(api.topics.getByLegacyScopeId, {
1111
- projectId: String(scopeId)
1112
- });
1113
- if (topic?.name !== void 0 && topic?.type !== void 0) {
1114
- return topic;
1115
- }
1116
- } catch (error) {
1117
- debugGraphPrimitiveFallback(
1118
- "[topicProjectOverlay] Failed to resolve topic by legacy scope ID",
1119
- { error, scopeId }
1120
- );
1050
+ }
1051
+ async function getBeliefNodesForScope(ctx, scope, args) {
1052
+ const baseQuery = ctx.db.query("epistemicNodes").withIndex(
1053
+ "by_topic_type",
1054
+ (q) => q.eq("topicId", scope.topicId).eq("nodeType", "belief")
1055
+ );
1056
+ const nodes = typeof args?.scanLimit === "number" ? await baseQuery.order("desc").take(args.scanLimit) : await baseQuery.collect();
1057
+ const scopedNodes = nodes.filter(
1058
+ (node) => nodeMatchesWorkspaceReasoningScope(node, scope)
1059
+ );
1060
+ if (!args?.status) {
1061
+ return scopedNodes;
1121
1062
  }
1122
- return null;
1063
+ return scopedNodes.filter((node) => node.status === args.status);
1123
1064
  }
1124
- function materializeTopicProjectOverlay(topic, idMode = "legacy") {
1125
- const metadata = readMetadata(topic);
1126
- const topicId = String(topic._id);
1127
- const legacyProjectId = readLegacyProjectId(topic) || readLegacyProjectId(metadata) || readNonEmptyString(metadata.legacyProjectId);
1128
- const storageProjectId = legacyProjectId || topicId;
1129
- const outwardId = idMode === "topic" ? topicId : storageProjectId;
1130
- const visibility = coerceVisibility(topic.visibility) || coerceVisibility(metadata.visibility) || "private";
1131
- const status = coerceStatus(topic.status) || coerceStatus(metadata.status) || "active";
1132
- const createdAt = typeof topic.createdAt === "number" ? topic.createdAt : typeof topic._creationTime === "number" ? topic._creationTime : 0;
1133
- const updatedAt = typeof topic.updatedAt === "number" ? topic.updatedAt : typeof metadata.updatedAt === "number" ? metadata.updatedAt : createdAt;
1134
- return {
1135
- ...metadata,
1136
- _id: outwardId,
1137
- projectId: outwardId,
1138
- topicId,
1139
- storageProjectId,
1140
- legacyProjectId,
1141
- name: readNonEmptyString(topic.name) || "Untitled Theme",
1142
- type: mapProjectType(topic, metadata),
1143
- description: readNonEmptyString(topic.description),
1144
- ownerId: readNonEmptyString(metadata.ownerId) || readNonEmptyString(topic.createdBy) || "system",
1145
- sharedWith: readStringArray(metadata.sharedWith),
1146
- visibility,
1147
- tenantId: readNonEmptyString(topic.tenantId) || readNonEmptyString(metadata.tenantId),
1148
- workspaceId: readNonEmptyString(topic.workspaceId) || readNonEmptyString(metadata.workspaceId),
1149
- status,
1150
- tags: readStringArray(metadata.tags),
1151
- chatCount: typeof metadata.chatCount === "number" ? metadata.chatCount : 0,
1152
- artifactCount: typeof metadata.artifactCount === "number" ? metadata.artifactCount : 0,
1153
- lastActivityAt: typeof metadata.lastActivityAt === "number" ? metadata.lastActivityAt : updatedAt,
1154
- _creationTime: typeof topic._creationTime === "number" ? topic._creationTime : createdAt,
1155
- createdAt,
1156
- updatedAt
1065
+ function createBeliefAudienceResolver(registryRows) {
1066
+ const audienceClassByKey = new Map(
1067
+ registryRows.map((row) => [
1068
+ normalizeAudienceKey(row.audienceKey),
1069
+ row.audienceClass
1070
+ ])
1071
+ );
1072
+ return (audienceKey, fallback) => {
1073
+ const key = normalizeAudienceKey(audienceKey);
1074
+ if (!key) {
1075
+ return fallback;
1076
+ }
1077
+ return audienceClassByKey.get(key) ?? classFromAudienceKey(key, fallback);
1157
1078
  };
1158
1079
  }
1159
- async function resolveTopicProjectOverlay(ctx, scopeId, options = {}) {
1160
- const topic = await resolveTopicDoc(ctx, scopeId);
1161
- if (!topic) {
1162
- return null;
1163
- }
1164
- if (options.projectLikeOnly !== false && !isProjectLikeTopic(topic)) {
1165
- return null;
1166
- }
1167
- return materializeTopicProjectOverlay(topic, options.idMode);
1168
- }
1169
- async function listTopicProjectOverlays(ctx, options = {}) {
1170
- let allTopics = [];
1171
- if (ctx?.db?.query && typeof ctx.db.query === "function") {
1172
- try {
1173
- allTopics = await ctx.db.query("topics").collect();
1174
- } catch (error) {
1175
- debugGraphPrimitiveFallback(
1176
- "[topicProjectOverlay] Failed to read topics table; falling back to API",
1177
- { error }
1178
- );
1179
- allTopics = [];
1180
- }
1181
- }
1182
- if (allTopics.length === 0 && typeof ctx.runQuery === "function") {
1183
- allTopics = (await ctx.runQuery(api.topics.list, {}) ?? []) || [];
1184
- }
1185
- return allTopics.filter(
1186
- (topic) => options.projectLikeOnly === false || isProjectLikeTopic(topic)
1187
- ).map((topic) => materializeTopicProjectOverlay(topic, options.idMode));
1188
- }
1189
- async function patchTopicProjectOverlay(ctx, scopeId, value) {
1190
- const topic = await resolveTopicDoc(ctx, scopeId);
1191
- if (!topic) {
1192
- return null;
1193
- }
1194
- const nextMetadata = { ...readMetadata(topic) };
1195
- const patch = {};
1196
- const topicUpdateArgs = {
1197
- id: String(topic._id)
1080
+ function flattenBeliefNode(node) {
1081
+ const meta = node.metadata || {};
1082
+ const worktreeId = resolveBeliefWorktreeId(meta);
1083
+ const tupleContradicted = readTupleContradictedFlag(
1084
+ node.tupleContradicted
1085
+ ) ?? readTupleContradictedFlag(meta.tupleContradicted) ?? false;
1086
+ return {
1087
+ _id: node._id,
1088
+ _epistemicNodeId: node._id,
1089
+ _creationTime: node._creationTime,
1090
+ belief: node.canonicalText,
1091
+ formulation: node.canonicalText,
1092
+ projectId: node.projectId,
1093
+ topicId: node.topicId,
1094
+ userId: node.userId || node.createdBy || "",
1095
+ confidence: meta.confidence || "untested",
1096
+ status: node.status,
1097
+ beliefStatus: resolveBeliefStatus(node, meta),
1098
+ topic: meta.topic || meta.pillar || "other",
1099
+ pillar: meta.pillar || meta.topic || "",
1100
+ category: meta.category || "",
1101
+ subcategory: meta.subcategory || "",
1102
+ categoryIcon: meta.categoryIcon || "",
1103
+ supportingEvidence: meta.supportingEvidenceIds || [],
1104
+ contradictingEvidence: meta.contradictingEvidenceIds || [],
1105
+ testingQuestions: meta.testingQuestionIds || [],
1106
+ linkedInsights: meta.linkedInsightIds || [],
1107
+ createdAt: node.createdAt,
1108
+ updatedAt: node.updatedAt || node.createdAt,
1109
+ tupleContradicted,
1110
+ sprintId: meta.sprintId || void 0,
1111
+ worktreeId,
1112
+ sourceBeliefIds: meta.sourceBeliefIds || void 0,
1113
+ criticality: meta.criticality || "unanalyzed",
1114
+ rationale: meta.rationale || "",
1115
+ audienceLabel: node.audienceLabel,
1116
+ policyTags: node.policyTags,
1117
+ sensitivityTier: node.sensitivityTier,
1118
+ exportClass: node.exportClass,
1119
+ anonymizationClass: node.anonymizationClass
1198
1120
  };
1199
- for (const [key, rawValue] of Object.entries(value)) {
1200
- switch (key) {
1201
- case "_id":
1202
- case "projectId":
1203
- case "topicId":
1204
- case "legacyProjectId":
1205
- case "storageProjectId":
1206
- break;
1207
- case "name":
1208
- case "description":
1209
- patch[key] = rawValue;
1210
- topicUpdateArgs[key] = rawValue;
1211
- break;
1212
- case "tenantId":
1213
- case "workspaceId":
1214
- case "ownerId":
1215
- throw new Error(
1216
- `patchTopicProjectOverlay cannot mutate ${key} via component-owned topics`
1217
- );
1218
- case "status": {
1219
- const status = coerceStatus(rawValue);
1220
- if (status) {
1221
- patch.status = status;
1222
- topicUpdateArgs.status = status;
1223
- }
1224
- break;
1225
- }
1226
- case "visibility": {
1227
- const visibility = coerceVisibility(rawValue);
1228
- if (visibility) {
1229
- patch.visibility = visibility;
1230
- topicUpdateArgs.visibility = visibility;
1231
- }
1232
- break;
1233
- }
1234
- case "type": {
1235
- const projectType = readNonEmptyString(rawValue);
1236
- if (projectType) {
1237
- nextMetadata.projectType = projectType;
1238
- } else {
1239
- delete nextMetadata.projectType;
1240
- }
1241
- break;
1242
- }
1243
- case "updatedAt":
1244
- case "createdAt":
1245
- break;
1246
- default:
1247
- if (rawValue === void 0) {
1248
- delete nextMetadata[key];
1249
- } else {
1250
- nextMetadata[key] = rawValue;
1251
- }
1252
- }
1253
- }
1254
- patch.updatedAt = Date.now();
1255
- patch.metadata = nextMetadata;
1256
- topicUpdateArgs.metadata = nextMetadata;
1257
- if (typeof ctx.runMutation === "function") {
1258
- try {
1259
- await ctx.runMutation(api.topics.update, topicUpdateArgs);
1260
- } catch (error) {
1261
- if (!isMissingLucernChildComponentError(error) || !ctx?.db || typeof ctx.db.patch !== "function") {
1262
- throw error;
1263
- }
1264
- await ctx.db.patch(String(topic._id), patch);
1265
- }
1266
- } else if (ctx?.db && typeof ctx.db.patch === "function") {
1267
- await ctx.db.patch(String(topic._id), patch);
1268
- } else {
1269
- throw new Error(
1270
- "Cannot patch topic without component adapter (ctx.runMutation unavailable)"
1271
- );
1272
- }
1273
- return materializeTopicProjectOverlay({
1274
- ...topic,
1275
- ...patch,
1276
- metadata: nextMetadata
1277
- });
1278
1121
  }
1279
-
1280
- // src/resolvers.ts
1281
- function isMissingLucernChildComponentError2(error) {
1282
- const message = getErrorMessage2(error);
1283
- return message.includes(
1284
- 'Child component ComponentName(Identifier("lucern")) not found'
1285
- ) || message.includes("Child component") && message.includes("lucern") && message.includes("not found");
1122
+ function resolveBeliefStatus(node, metadata) {
1123
+ return resolveBeliefLifecycleStatus({
1124
+ beliefStatus: node.beliefStatus,
1125
+ confidence: node.confidence,
1126
+ predictionMeta: node.predictionMeta,
1127
+ metadata
1128
+ });
1286
1129
  }
1287
- function getErrorMessage2(error) {
1288
- if (error instanceof Error) {
1289
- return error.message;
1290
- }
1291
- if (typeof error === "object" && error !== null && "message" in error && typeof error.message === "string") {
1292
- return error.message;
1130
+ async function hasCompletedWorktreeForBelief(ctx, beliefNodeId) {
1131
+ const clusterMembership = await ctx.db.query("worktreeBeliefCluster").withIndex("by_belief", (q) => q.eq("beliefId", beliefNodeId)).collect();
1132
+ for (const membership of clusterMembership) {
1133
+ const worktree = await ctx.db.get(membership.worktreeId);
1134
+ if (worktree?.status === "completed" || worktree?.status === "merged") {
1135
+ return true;
1136
+ }
1293
1137
  }
1294
- return "unknown error";
1295
- }
1296
- function isAdvisoryTopicPatch(value) {
1297
- const advisoryKeys = /* @__PURE__ */ new Set(["lastActivityAt", "updatedAt"]);
1298
- const keys = Object.keys(value);
1299
- return keys.length > 0 && keys.every((key) => advisoryKeys.has(key));
1138
+ return false;
1300
1139
  }
1301
- async function patchProjectWithTolerance(ctx, projectId, value) {
1140
+ async function getActiveConfidencePolicy(ctx) {
1302
1141
  try {
1303
- await patchTopicProjectOverlay(ctx, projectId, value);
1142
+ const activeConfig = await ctx.db.query("logicSprintScoring").withIndex("by_active", (q) => q.eq("isActive", true)).first();
1143
+ return {
1144
+ scoringMode: activeConfig?.confidencePolicy === "always" ? "always" : DEFAULT_CONFIDENCE_POLICY.scoringMode,
1145
+ tupleContradiction: normalizeTupleContradictionPolicy(
1146
+ activeConfig?.tupleContradictionPolicy
1147
+ )
1148
+ };
1304
1149
  } catch (error) {
1305
- if (!isAdvisoryTopicPatch(value) || !isMissingLucernChildComponentError2(error)) {
1306
- throw error;
1307
- }
1308
- console.warn(
1309
- "[lucern graph-primitives] Non-fatal advisory topic patch failure",
1150
+ debugGraphPrimitiveFallback(
1151
+ "[epistemicBeliefs] Failed to load active confidence policy",
1310
1152
  {
1311
- projectId,
1312
- keys: Object.keys(value),
1313
- error: getErrorMessage2(error)
1153
+ error
1314
1154
  }
1315
1155
  );
1156
+ return DEFAULT_CONFIDENCE_POLICY;
1316
1157
  }
1317
1158
  }
1318
- function defaultResolvers() {
1319
- return {
1320
- getProject: (ctx, projectId) => resolveTopicProjectOverlay(ctx, projectId, {
1321
- idMode: "legacy",
1322
- projectLikeOnly: false
1323
- }),
1324
- patchProject: (ctx, projectId, value) => patchProjectWithTolerance(ctx, projectId, value),
1325
- listTopics: (ctx) => listTopicProjectOverlays(ctx, {
1326
- idMode: "legacy"
1327
- }),
1328
- getFinalArtifact: (ctx, artifactId) => ctx.db.get(artifactId)
1329
- };
1159
+ async function requireAuthenticatedUserId(ctx) {
1160
+ const userId = await getCurrentUserId(
1161
+ ctx
1162
+ );
1163
+ if (!userId) {
1164
+ throwStructuredMutationError({
1165
+ message: "Authentication required.",
1166
+ status: 401,
1167
+ code: "AUTHENTICATION_REQUIRED",
1168
+ invariantCode: "auth.required",
1169
+ suggestion: "Provide a valid bearer token before invoking belief mutations."
1170
+ });
1171
+ }
1172
+ return userId;
1330
1173
  }
1331
- var resolverOverrides = {};
1332
- function resolveGraphPrimitivesAppResolvers(_ctx) {
1333
- return {
1334
- ...defaultResolvers(),
1335
- ...resolverOverrides
1336
- };
1174
+ async function requireProjectWriteAccess(ctx, projectId, userId) {
1175
+ const hasAccess = await checkProjectAccess(
1176
+ ctx,
1177
+ projectId,
1178
+ userId
1179
+ );
1180
+ if (!hasAccess) {
1181
+ throwStructuredMutationError({
1182
+ message: "Project access required.",
1183
+ status: 403,
1184
+ code: "FORBIDDEN",
1185
+ invariantCode: "policy.scope_required",
1186
+ suggestion: "Request write access for the project and retry.",
1187
+ details: { projectId, userId }
1188
+ });
1189
+ }
1337
1190
  }
1338
1191
 
1339
- // src/epistemicBeliefs.ts
1340
- var insightIdUnion = v.id("epistemicNodes");
1341
- var DEFAULT_PROJECT_BELIEF_LIMIT = 250;
1342
- var MAX_PROJECT_BELIEF_LIMIT = 1e3;
1343
- var optionalBeliefScopeArgs = optionalScopeArgs;
1344
- var DEFAULT_CONFIDENCE_POLICY = {
1345
- scoringMode: "after_worktree",
1346
- tupleContradiction: normalizeTupleContradictionPolicy()
1347
- };
1348
- function throwStructuredMutationError(args) {
1349
- const error = new Error(args.message);
1350
- error.status = args.status;
1351
- error.code = args.code;
1352
- error.invariantCode = args.invariantCode;
1353
- error.suggestion = args.suggestion;
1354
- error.details = args.details;
1355
- throw error;
1356
- }
1357
- function readFiniteNumber(value) {
1358
- return typeof value === "number" && Number.isFinite(value) ? value : void 0;
1359
- }
1360
- function clamp01(value) {
1361
- return Math.max(0, Math.min(1, value));
1192
+ // src/edges/contains.ts
1193
+ var containsPropagationSpec = {
1194
+ edgeType: "contains",
1195
+ direction: "outgoing",
1196
+ transitivity: "none",
1197
+ damping: 1,
1198
+ maxHops: 1,
1199
+ operator: () => null,
1200
+ description: "Structural containment only. Traversed for explicit semantics, but it never propagates opinions."
1201
+ };
1202
+ function readEdgeMetadata(edge) {
1203
+ return {
1204
+ constraint: edge.constraint ?? void 0,
1205
+ normalization: edge.normalization ?? void 0,
1206
+ propagation: edge.propagation ?? void 0,
1207
+ conditionalA: edge.conditionalA ?? void 0,
1208
+ conditionalNotA: edge.conditionalNotA ?? void 0
1209
+ };
1362
1210
  }
1363
- function assertBaseRateInRange(baseRate, field = "baseRate") {
1364
- if (baseRate < 0 || baseRate > 1) {
1365
- throwStructuredMutationError({
1366
- message: `${field} must be within [0, 1].`,
1367
- status: 400,
1368
- code: "INVALID_ARGUMENT",
1369
- invariantCode: "request.valid_shape",
1370
- suggestion: `Clamp ${field} into the inclusive [0, 1] interval.`,
1371
- details: { field, baseRate }
1372
- });
1211
+ function applyPerHopDamping(sourceOpinion, damping) {
1212
+ if (damping >= 1) {
1213
+ return sourceOpinion;
1373
1214
  }
1374
- return baseRate;
1215
+ return trustDiscount(sourceOpinion, Math.max(0, damping));
1375
1216
  }
1376
- function buildBeliefConfidenceRow(args) {
1217
+ function annotateRationale(result, spec, hop) {
1377
1218
  return {
1378
- beliefId: args.beliefId,
1379
- confidence: confidenceFromSL(
1380
- args.belief,
1381
- args.disbelief,
1382
- args.uncertainty,
1383
- args.baseRate
1384
- ),
1385
- belief: args.belief,
1386
- disbelief: args.disbelief,
1387
- uncertainty: args.uncertainty,
1388
- baseRate: args.baseRate,
1389
- slOperator: args.slOperator ?? "manual_assessment",
1390
- trigger: args.trigger,
1391
- ...args.rationale ? { rationale: args.rationale } : {},
1392
- assessedBy: args.assessedBy,
1393
- assessedAt: args.assessedAt,
1394
- ...args.triggeringEvidenceId ? {
1395
- triggeringEvidenceId: args.triggeringEvidenceId,
1396
- triggeringEvidenceIds: [String(args.triggeringEvidenceId)]
1397
- } : {},
1398
- ...args.triggeringContradictionId ? {
1399
- triggeringContradictionId: args.triggeringContradictionId
1400
- } : {},
1401
- ...args.triggeringWorktreeId ? { triggeringWorktreeId: args.triggeringWorktreeId } : {}
1219
+ ...result,
1220
+ rationale: `hop=${hop} edge=${spec.edgeType} damping=${spec.damping.toFixed(
1221
+ 2
1222
+ )} :: ${result.rationale}`
1402
1223
  };
1403
1224
  }
1404
- function buildBeliefStatusSuccessResult() {
1405
- return { success: true };
1406
- }
1407
- function buildBeliefEvidenceNotFoundResult() {
1408
- const result = {};
1409
- result.success = false;
1410
- result.message = "Evidence node not found";
1411
- return result;
1225
+ function propagatePositiveSupport(sourceOpinion, targetOpinion, edgeWeight) {
1226
+ const discounted = trustDiscount(sourceOpinion, Math.abs(edgeWeight));
1227
+ return {
1228
+ opinion: cumulativeFusion(targetOpinion, discounted),
1229
+ operator: "cumulative_fusion",
1230
+ rationale: `Supporting evidence (weight=${edgeWeight.toFixed(
1231
+ 2
1232
+ )}) from source at ${project(sourceOpinion).toFixed(2)}`
1233
+ };
1412
1234
  }
1413
- function deriveSyntheticBackfillOpinion(source) {
1414
- const belief = readFiniteNumber(source.opinion_b) ?? readFiniteNumber(source.belief);
1415
- const disbelief = readFiniteNumber(source.opinion_d) ?? readFiniteNumber(source.disbelief);
1416
- const uncertainty = readFiniteNumber(source.opinion_u) ?? readFiniteNumber(source.uncertainty);
1417
- const baseRate = readFiniteNumber(source.opinion_a) ?? readFiniteNumber(source.baseRate);
1418
- if (belief !== void 0 || disbelief !== void 0 || uncertainty !== void 0 || baseRate !== void 0) {
1419
- try {
1420
- return readOpinionFromRecord(source);
1421
- } catch (error) {
1422
- debugGraphPrimitiveFallback(
1423
- "[epistemicBeliefs] Failed to decode legacy belief opinion",
1424
- {
1425
- error
1426
- }
1427
- );
1428
- return mkOpinion(0, 0, 1, 0.5);
1429
- }
1430
- }
1431
- const confidence = clamp01(readFiniteNumber(source.confidence) ?? 0);
1432
- return mkOpinion(confidence, 1 - confidence, 0, 0.5);
1235
+ function propagatePositiveInform(sourceOpinion, targetOpinion, edgeWeight) {
1236
+ const discounted = trustDiscount(sourceOpinion, Math.abs(edgeWeight));
1237
+ return {
1238
+ opinion: cumulativeFusion(targetOpinion, discounted),
1239
+ operator: "cumulative_fusion",
1240
+ rationale: `Supporting evidence (weight=${edgeWeight.toFixed(2)})`
1241
+ };
1433
1242
  }
1434
- function clampBeliefLimit(limit, fallback = DEFAULT_PROJECT_BELIEF_LIMIT) {
1435
- if (!Number.isFinite(limit)) {
1436
- return fallback;
1437
- }
1438
- return Math.max(
1439
- 1,
1440
- Math.min(Math.floor(limit), MAX_PROJECT_BELIEF_LIMIT)
1243
+ function propagateNegativeSupportWithMetadata(sourceOpinion, targetOpinion, edgeWeight, edge) {
1244
+ return applyNegativeSupport(
1245
+ sourceOpinion,
1246
+ targetOpinion,
1247
+ edgeWeight,
1248
+ readEdgeMetadata(edge)
1441
1249
  );
1442
1250
  }
1443
- function readTupleContradictedFlag(value) {
1444
- return typeof value === "boolean" ? value : void 0;
1445
- }
1446
- function readBeliefOpinionSnapshot(node, metadata) {
1447
- try {
1448
- return readOpinionFromRecord({
1449
- ...metadata,
1450
- opinion_b: node.opinion_b,
1451
- opinion_d: node.opinion_d,
1452
- opinion_u: node.opinion_u,
1453
- opinion_a: node.opinion_a
1454
- });
1455
- } catch (error) {
1456
- debugGraphPrimitiveFallback(
1457
- "[epistemicBeliefs] Failed to read belief opinion snapshot",
1458
- {
1459
- error,
1460
- beliefId: node._id
1461
- }
1251
+ var propagateNegativeInform = applyNegativeEvidence;
1252
+
1253
+ // src/edges/contradicts.ts
1254
+ var contradictsPropagationSpec = {
1255
+ edgeType: "contradicts",
1256
+ direction: "bidirectional",
1257
+ transitivity: "none",
1258
+ damping: 0.85,
1259
+ maxHops: 1,
1260
+ operator: (sourceOpinion, targetOpinion, edge, context) => {
1261
+ const dampedSource = applyPerHopDamping(
1262
+ sourceOpinion,
1263
+ context.spec.damping
1462
1264
  );
1463
- return mkOpinion(0, 0, 1, 0.5);
1464
- }
1465
- }
1466
- function deriveTupleContradictionSeverity(node) {
1467
- const metadata = node.metadata || {};
1468
- const criticality = typeof metadata.criticality === "string" ? metadata.criticality : void 0;
1469
- if (criticality === "blocking") {
1470
- return "critical";
1471
- }
1472
- if (criticality === "supporting") {
1473
- return "minor";
1474
- }
1475
- return "significant";
1476
- }
1477
- function formatTupleContradictionDescription(args) {
1478
- return `Tuple-space contradiction detected: b=${args.opinion.b.toFixed(2)} > ${args.policy.beliefThreshold.toFixed(2)} and d=${args.opinion.d.toFixed(2)} > ${args.policy.disbeliefThreshold.toFixed(2)}.`;
1479
- }
1480
- function generateContentHash(text) {
1481
- const content = `belief:${text.trim().toLowerCase().replace(/\s+/g, " ")}`;
1482
- let hash = 5381;
1483
- for (let i = 0; i < content.length; i++) {
1484
- hash = (hash << 5) + hash + content.charCodeAt(i);
1485
- hash &= hash;
1486
- }
1487
- return Math.abs(hash).toString(16).padStart(8, "0");
1488
- }
1489
- function resolveBeliefWorktreeId(metadata) {
1490
- const worktreeId = metadata?.worktreeId;
1491
- if (typeof worktreeId === "string" && worktreeId.trim().length > 0) {
1492
- return worktreeId;
1493
- }
1494
- const sprintId = metadata?.sprintId;
1495
- return typeof sprintId === "string" && sprintId.trim().length > 0 ? sprintId : void 0;
1496
- }
1497
- function normalizePillar(pillar) {
1498
- if (!pillar) {
1499
- return "other";
1500
- }
1501
- const lower = pillar.toLowerCase();
1502
- const validPillars = [
1503
- "market",
1504
- "competition",
1505
- "product",
1506
- "team",
1507
- "financials",
1508
- "regulatory",
1509
- "timing",
1510
- "customer",
1511
- "technology",
1512
- "distribution",
1513
- "deal",
1514
- "risks"
1515
- ];
1516
- return validPillars.find((p) => lower.includes(p)) || "other";
1517
- }
1518
- async function markBeliefGraphDirty(ctx, scope) {
1519
- const projectId = typeof scope.projectId === "string" && scope.projectId.trim().length > 0 ? scope.projectId : void 0;
1520
- const topicId = typeof scope.topicId === "string" && scope.topicId.trim().length > 0 ? scope.topicId : void 0;
1521
- if (!projectId && !topicId) {
1522
- return;
1523
- }
1524
- if (projectId) {
1525
- await ctx.scheduler.runAfter(
1526
- 0,
1527
- internal.graphAnalysisCache.markCacheStaleInternal,
1528
- { projectId }
1265
+ const negativeWeight = -Math.abs(edge.weight ?? 1);
1266
+ const result = propagateNegativeSupportWithMetadata(
1267
+ dampedSource,
1268
+ targetOpinion,
1269
+ negativeWeight,
1270
+ edge
1529
1271
  );
1530
- }
1531
- if (topicId) {
1532
- await ctx.scheduler.runAfter(
1533
- 0,
1534
- internal.graphAnalysisCache.markCacheStaleByTopic,
1535
- { topicId }
1272
+ return annotateRationale(result, context.spec, context.hop);
1273
+ },
1274
+ description: "Legacy contradiction edges move negative pressure in either direction, but never beyond one hop."
1275
+ };
1276
+ var dependsOnPropagationSpec = {
1277
+ edgeType: "depends_on",
1278
+ direction: "incoming",
1279
+ transitivity: "damped",
1280
+ damping: 0.8,
1281
+ maxHops: "unbounded",
1282
+ operator: (sourceOpinion, targetOpinion, edge, context) => {
1283
+ const dampedSource = applyPerHopDamping(
1284
+ sourceOpinion,
1285
+ context.spec.damping
1536
1286
  );
1537
- }
1538
- await resolveGraphPrimitivesAppResolvers().patchProject(
1539
- ctx,
1540
- topicId ?? projectId,
1541
- {
1542
- lastActivityAt: Date.now()
1287
+ const metadata = readEdgeMetadata(edge);
1288
+ if (metadata.conditionalA && metadata.conditionalNotA) {
1289
+ const deducedOpinion = conditionalDeduction(
1290
+ dampedSource,
1291
+ mkOpinion(
1292
+ metadata.conditionalA.b,
1293
+ metadata.conditionalA.d,
1294
+ metadata.conditionalA.u,
1295
+ metadata.conditionalA.a
1296
+ ),
1297
+ mkOpinion(
1298
+ metadata.conditionalNotA.b,
1299
+ metadata.conditionalNotA.d,
1300
+ metadata.conditionalNotA.u,
1301
+ metadata.conditionalNotA.a
1302
+ ),
1303
+ targetOpinion.a
1304
+ );
1305
+ return annotateRationale(
1306
+ {
1307
+ opinion: deducedOpinion,
1308
+ operator: "conditional_deduction",
1309
+ rationale: `Conditional deduction: prerequisite at ${project(
1310
+ dampedSource
1311
+ ).toFixed(2)}`
1312
+ },
1313
+ context.spec,
1314
+ context.hop
1315
+ );
1543
1316
  }
1544
- );
1545
- }
1546
- async function resolveBeliefScopeOrNull(ctx, args) {
1547
- if (!args.projectId && !args.topicId) {
1548
- return null;
1549
- }
1550
- try {
1551
- return await resolveTopicProjectScope(ctx, {
1552
- projectId: args.projectId ?? void 0,
1553
- topicId: args.topicId ?? void 0
1554
- });
1555
- } catch (error) {
1556
- debugGraphPrimitiveFallback(
1557
- "[epistemicBeliefs] Failed to resolve belief scope",
1558
- {
1559
- error,
1560
- projectId: args.projectId,
1561
- topicId: args.topicId
1562
- }
1317
+ const result = dampedDependencyCascade(
1318
+ dampedSource,
1319
+ targetOpinion,
1320
+ metadata.propagation ?? "continuous"
1563
1321
  );
1564
- return null;
1565
- }
1566
- }
1567
- async function getBeliefNodesForScope(ctx, scope, args) {
1568
- const baseQuery = ctx.db.query("epistemicNodes").withIndex(
1569
- "by_topic_type",
1570
- (q) => q.eq("topicId", scope.topicId).eq("nodeType", "belief")
1571
- );
1572
- const nodes = typeof args?.scanLimit === "number" ? await baseQuery.order("desc").take(args.scanLimit) : await baseQuery.collect();
1573
- const scopedNodes = nodes.filter(
1574
- (node) => nodeMatchesWorkspaceReasoningScope(node, scope)
1575
- );
1576
- if (!args?.status) {
1577
- return scopedNodes;
1578
- }
1579
- return scopedNodes.filter((node) => node.status === args.status);
1322
+ return annotateRationale(result, context.spec, context.hop);
1323
+ },
1324
+ description: "Structural gating. Textbook conditional deduction when edge conditionals exist, otherwise damped dependency cascade through downstream chains."
1325
+ };
1326
+
1327
+ // src/edges/derivedFrom.ts
1328
+ var derivedFromPropagationSpec = {
1329
+ edgeType: "derived_from",
1330
+ direction: "incoming",
1331
+ transitivity: "none",
1332
+ damping: 1,
1333
+ maxHops: 1,
1334
+ operator: () => null,
1335
+ description: "Provenance only. The traversal surface stays explicit, but confidence does not move across derived_from edges."
1336
+ };
1337
+
1338
+ // src/edges/elaborates.ts
1339
+ var elaboratesPropagationSpec = {
1340
+ edgeType: "elaborates",
1341
+ direction: "outgoing",
1342
+ transitivity: "damped",
1343
+ damping: 0.7,
1344
+ maxHops: 2,
1345
+ operator: (sourceOpinion, targetOpinion, edge, context) => {
1346
+ const dampedSource = applyPerHopDamping(
1347
+ sourceOpinion,
1348
+ context.spec.damping
1349
+ );
1350
+ const contextualWeight = Math.min(Math.abs(edge.weight ?? 0.35), 0.35);
1351
+ const result = propagatePositiveInform(
1352
+ dampedSource,
1353
+ targetOpinion,
1354
+ contextualWeight
1355
+ );
1356
+ return annotateRationale(result, context.spec, context.hop);
1357
+ },
1358
+ description: "Context-rich supporting detail. Elaborates carries a small positive effect with short, damped chaining."
1359
+ };
1360
+
1361
+ // src/edges/informs.ts
1362
+ var informsPropagationSpec = {
1363
+ edgeType: "informs",
1364
+ direction: "outgoing",
1365
+ transitivity: "full",
1366
+ damping: 0.92,
1367
+ maxHops: "unbounded",
1368
+ operator: (sourceOpinion, targetOpinion, edge, context) => {
1369
+ const dampedSource = applyPerHopDamping(
1370
+ sourceOpinion,
1371
+ context.spec.damping
1372
+ );
1373
+ const weight = edge.weight ?? 1;
1374
+ const result = weight < 0 ? propagateNegativeInform(dampedSource, targetOpinion, weight) : propagatePositiveInform(dampedSource, targetOpinion, weight);
1375
+ return annotateRationale(result, context.spec, context.hop);
1376
+ },
1377
+ description: "Evidence-bearing influence. Informs can chain through the graph with light per-hop damping."
1378
+ };
1379
+
1380
+ // src/edges/propagationTypes.ts
1381
+ function isPropagationTraversalDirection(direction) {
1382
+ return direction === "outgoing" || direction === "incoming";
1580
1383
  }
1581
- function createBeliefAudienceResolver(registryRows) {
1582
- const audienceClassByKey = new Map(
1583
- registryRows.map((row) => [
1584
- normalizeAudienceKey(row.audienceKey),
1585
- row.audienceClass
1586
- ])
1587
- );
1588
- return (audienceKey, fallback) => {
1589
- const key = normalizeAudienceKey(audienceKey);
1590
- if (!key) {
1591
- return fallback;
1592
- }
1593
- return audienceClassByKey.get(key) ?? classFromAudienceKey(key, fallback);
1594
- };
1384
+ function canTraverseHop(spec, nextHop) {
1385
+ return spec.maxHops === "unbounded" || nextHop <= spec.maxHops;
1595
1386
  }
1596
- function flattenBeliefNode(node) {
1597
- const meta = node.metadata || {};
1598
- const worktreeId = resolveBeliefWorktreeId(meta);
1599
- const tupleContradicted = readTupleContradictedFlag(node.tupleContradicted) ?? readTupleContradictedFlag(meta.tupleContradicted) ?? false;
1600
- return {
1601
- _id: node._id,
1602
- _epistemicNodeId: node._id,
1603
- _creationTime: node._creationTime,
1604
- belief: node.canonicalText,
1605
- formulation: node.canonicalText,
1606
- projectId: node.projectId,
1607
- topicId: node.topicId,
1608
- userId: node.userId || node.createdBy || "",
1609
- confidence: meta.confidence || "untested",
1610
- status: node.status,
1611
- beliefStatus: resolveBeliefStatus(node, meta),
1612
- topic: meta.topic || meta.pillar || "other",
1613
- pillar: meta.pillar || meta.topic || "",
1614
- category: meta.category || "",
1615
- subcategory: meta.subcategory || "",
1616
- categoryIcon: meta.categoryIcon || "",
1617
- supportingEvidence: meta.supportingEvidenceIds || [],
1618
- contradictingEvidence: meta.contradictingEvidenceIds || [],
1619
- testingQuestions: meta.testingQuestionIds || [],
1620
- linkedInsights: meta.linkedInsightIds || [],
1621
- createdAt: node.createdAt,
1622
- updatedAt: node.updatedAt || node.createdAt,
1623
- tupleContradicted,
1624
- sprintId: meta.sprintId || void 0,
1625
- worktreeId,
1626
- sourceBeliefIds: meta.sourceBeliefIds || void 0,
1627
- criticality: meta.criticality || "unanalyzed",
1628
- rationale: meta.rationale || "",
1629
- audienceLabel: node.audienceLabel,
1630
- policyTags: node.policyTags,
1631
- sensitivityTier: node.sensitivityTier,
1632
- exportClass: node.exportClass,
1633
- anonymizationClass: node.anonymizationClass
1634
- };
1387
+ function canContinueTransitively(spec, currentHop) {
1388
+ if (spec.transitivity === "none") {
1389
+ return false;
1390
+ }
1391
+ return spec.maxHops === "unbounded" || currentHop < spec.maxHops;
1635
1392
  }
1636
- function resolveBeliefStatus(node, metadata) {
1637
- return resolveBeliefLifecycleStatus({
1638
- beliefStatus: node.beliefStatus,
1639
- confidence: node.confidence,
1640
- predictionMeta: node.predictionMeta,
1641
- metadata
1642
- });
1393
+
1394
+ // src/edges/refutes.ts
1395
+ var refutesPropagationSpec = {
1396
+ edgeType: "refutes",
1397
+ direction: "outgoing",
1398
+ transitivity: "none",
1399
+ damping: 0.9,
1400
+ maxHops: 1,
1401
+ operator: (sourceOpinion, targetOpinion, edge, context) => {
1402
+ const dampedSource = applyPerHopDamping(
1403
+ sourceOpinion,
1404
+ context.spec.damping
1405
+ );
1406
+ const negativeWeight = -Math.abs(edge.weight ?? 1);
1407
+ const result = propagateNegativeInform(
1408
+ dampedSource,
1409
+ targetOpinion,
1410
+ negativeWeight
1411
+ );
1412
+ return annotateRationale(result, context.spec, context.hop);
1413
+ },
1414
+ description: "Explicit negative evidence semantics. Refutes is treated as strong one-hop counter-evidence."
1415
+ };
1416
+
1417
+ // src/edges/supports.ts
1418
+ var supportsPropagationSpec = {
1419
+ edgeType: "supports",
1420
+ direction: "outgoing",
1421
+ transitivity: "full",
1422
+ damping: 0.85,
1423
+ maxHops: "unbounded",
1424
+ operator: (sourceOpinion, targetOpinion, edge, context) => {
1425
+ const dampedSource = applyPerHopDamping(
1426
+ sourceOpinion,
1427
+ context.spec.damping
1428
+ );
1429
+ const weight = edge.weight ?? 1;
1430
+ const result = weight < 0 ? propagateNegativeSupportWithMetadata(
1431
+ dampedSource,
1432
+ targetOpinion,
1433
+ weight,
1434
+ edge
1435
+ ) : propagatePositiveSupport(dampedSource, targetOpinion, weight);
1436
+ return annotateRationale(result, context.spec, context.hop);
1437
+ },
1438
+ description: "Belief-to-belief influence. Supports chains transitively with moderate per-hop damping."
1439
+ };
1440
+
1441
+ // src/edges/tests.ts
1442
+ var testsPropagationSpec = {
1443
+ edgeType: "tests",
1444
+ direction: "outgoing",
1445
+ transitivity: "none",
1446
+ damping: 1,
1447
+ maxHops: 1,
1448
+ operator: () => null,
1449
+ description: "Interrogation linkage only. Tests edges do not directly move confidence."
1450
+ };
1451
+
1452
+ // src/edges/index.ts
1453
+ var EDGE_PROPAGATION_SPECS = [
1454
+ supportsPropagationSpec,
1455
+ informsPropagationSpec,
1456
+ dependsOnPropagationSpec,
1457
+ derivedFromPropagationSpec,
1458
+ containsPropagationSpec,
1459
+ testsPropagationSpec,
1460
+ contradictsPropagationSpec,
1461
+ refutesPropagationSpec,
1462
+ elaboratesPropagationSpec
1463
+ ];
1464
+ new Map(EDGE_PROPAGATION_SPECS.map((spec) => [spec.edgeType, spec]));
1465
+ function getEdgePropagationSpecs() {
1466
+ return EDGE_PROPAGATION_SPECS;
1643
1467
  }
1644
- async function hasCompletedWorktreeForBelief(ctx, beliefNodeId) {
1645
- const clusterMembership = await ctx.db.query("worktreeBeliefCluster").withIndex("by_belief", (q) => q.eq("beliefId", beliefNodeId)).collect();
1646
- for (const membership of clusterMembership) {
1647
- const worktree = await ctx.db.get(membership.worktreeId);
1648
- if (worktree?.status === "completed" || worktree?.status === "merged") {
1649
- return true;
1650
- }
1468
+ function getTraversalDirections(direction) {
1469
+ if (isPropagationTraversalDirection(direction)) {
1470
+ return [direction];
1651
1471
  }
1652
- return false;
1472
+ return ["outgoing", "incoming"];
1473
+ }
1474
+
1475
+ // src/confidencePropagationDispatch.ts
1476
+ function resolveTraversalTargetNodeId(edge, direction) {
1477
+ const targetNodeId = direction === "outgoing" ? edge.toNodeId : edge.fromNodeId;
1478
+ return targetNodeId ?? void 0;
1653
1479
  }
1654
- async function getActiveConfidencePolicy(ctx) {
1480
+ function readNodeOpinion(node) {
1481
+ const metadata = node.metadata ?? {};
1655
1482
  try {
1656
- const activeConfig = await ctx.db.query("logicSprintScoring").withIndex("by_active", (q) => q.eq("isActive", true)).first();
1657
- return {
1658
- scoringMode: activeConfig?.confidencePolicy === "always" ? "always" : DEFAULT_CONFIDENCE_POLICY.scoringMode,
1659
- tupleContradiction: normalizeTupleContradictionPolicy(
1660
- activeConfig?.tupleContradictionPolicy
1661
- )
1662
- };
1663
- } catch (error) {
1664
- debugGraphPrimitiveFallback(
1665
- "[epistemicBeliefs] Failed to load active confidence policy",
1666
- {
1667
- error
1483
+ return readOpinionFromRecord({
1484
+ ...metadata,
1485
+ opinion_b: node.opinion_b,
1486
+ opinion_d: node.opinion_d,
1487
+ opinion_u: node.opinion_u,
1488
+ opinion_a: node.opinion_a
1489
+ });
1490
+ } catch {
1491
+ return mkOpinion(0, 0, 1, 0.5);
1492
+ }
1493
+ }
1494
+ async function collectConfidencePropagationDispatches(args) {
1495
+ const dispatchesByTargetId = /* @__PURE__ */ new Map();
1496
+ const opinionCache = /* @__PURE__ */ new Map();
1497
+ const nodeCache = /* @__PURE__ */ new Map();
1498
+ const traversalSpecs = args.traversalSpecs ?? getEdgePropagationSpecs();
1499
+ const queue = [
1500
+ {
1501
+ nodeId: args.sourceNodeId,
1502
+ opinion: args.sourceOpinion,
1503
+ hop: 0,
1504
+ visitedNodeIds: /* @__PURE__ */ new Set([String(args.sourceNodeId)])
1505
+ }
1506
+ ];
1507
+ const loadNode = async (nodeId) => {
1508
+ const cacheKey = String(nodeId);
1509
+ if (!nodeCache.has(cacheKey)) {
1510
+ nodeCache.set(cacheKey, await args.getNode(nodeId));
1511
+ }
1512
+ return nodeCache.get(cacheKey) ?? null;
1513
+ };
1514
+ while (queue.length > 0) {
1515
+ const state = queue.shift();
1516
+ if (!state) {
1517
+ continue;
1518
+ }
1519
+ for (const spec of traversalSpecs) {
1520
+ const nextHop = state.hop + 1;
1521
+ if (!canTraverseHop(spec, nextHop)) {
1522
+ continue;
1668
1523
  }
1669
- );
1670
- return DEFAULT_CONFIDENCE_POLICY;
1524
+ for (const direction of getTraversalDirections(spec.direction)) {
1525
+ const edges = await args.queryEdges({
1526
+ nodeId: state.nodeId,
1527
+ spec,
1528
+ direction,
1529
+ hop: nextHop
1530
+ });
1531
+ for (const edge of edges) {
1532
+ if (args.sourceScope && !edgeMatchesWorkspaceReasoningScope(edge, args.sourceScope)) {
1533
+ continue;
1534
+ }
1535
+ const targetNodeId = resolveTraversalTargetNodeId(edge, direction);
1536
+ if (!targetNodeId) {
1537
+ continue;
1538
+ }
1539
+ if (state.visitedNodeIds.has(String(targetNodeId))) {
1540
+ continue;
1541
+ }
1542
+ const targetNode = await loadNode(targetNodeId);
1543
+ if (!targetNode || targetNode.nodeType !== "belief") {
1544
+ continue;
1545
+ }
1546
+ if (args.sourceScope && !nodeMatchesWorkspaceReasoningScope(targetNode, args.sourceScope)) {
1547
+ continue;
1548
+ }
1549
+ const cacheKey = String(targetNodeId);
1550
+ const targetOpinion = opinionCache.get(cacheKey) ?? readNodeOpinion(targetNode);
1551
+ const result = spec.operator(state.opinion, targetOpinion, edge, {
1552
+ hop: nextHop,
1553
+ sourceNodeId: state.nodeId,
1554
+ targetNodeId,
1555
+ traversedDirection: direction,
1556
+ spec
1557
+ });
1558
+ if (!result || !hasProjectedOpinionChanged(targetOpinion, result.opinion)) {
1559
+ continue;
1560
+ }
1561
+ const projectedOpinion = mkOpinion(
1562
+ result.opinion.b,
1563
+ result.opinion.d,
1564
+ result.opinion.u,
1565
+ result.opinion.a
1566
+ );
1567
+ opinionCache.set(cacheKey, projectedOpinion);
1568
+ const existingDispatch = dispatchesByTargetId.get(cacheKey);
1569
+ dispatchesByTargetId.set(cacheKey, {
1570
+ targetNodeId,
1571
+ edgeType: spec.edgeType,
1572
+ traversedDirection: direction,
1573
+ weight: edge.weight ?? 1,
1574
+ opinion: projectedOpinion,
1575
+ operator: result.operator,
1576
+ rationale: existingDispatch ? `${existingDispatch.rationale}; ${result.rationale}` : result.rationale,
1577
+ hop: nextHop
1578
+ });
1579
+ if (canContinueTransitively(spec, nextHop)) {
1580
+ queue.push({
1581
+ nodeId: targetNodeId,
1582
+ opinion: projectedOpinion,
1583
+ hop: nextHop,
1584
+ visitedNodeIds: /* @__PURE__ */ new Set([
1585
+ ...state.visitedNodeIds,
1586
+ String(targetNodeId)
1587
+ ])
1588
+ });
1589
+ }
1590
+ }
1591
+ }
1592
+ }
1671
1593
  }
1594
+ return Array.from(dispatchesByTargetId.values()).sort((left, right) => {
1595
+ if (left.hop !== right.hop) {
1596
+ return left.hop - right.hop;
1597
+ }
1598
+ return String(left.targetNodeId).localeCompare(String(right.targetNodeId));
1599
+ });
1672
1600
  }
1601
+
1602
+ // src/epistemicBeliefs.confidence.ts
1673
1603
  async function applyBeliefConfidenceChange(ctx, args) {
1674
1604
  const now = Date.now();
1675
1605
  const node = await ctx.db.get(args.nodeId);
@@ -1976,32 +1906,49 @@ var propagateConfidenceChange = internalMutation({
1976
1906
  };
1977
1907
  }
1978
1908
  });
1979
- async function requireAuthenticatedUserId(ctx) {
1980
- const userId = await getCurrentUserId(ctx);
1981
- if (!userId) {
1982
- throwStructuredMutationError({
1983
- message: "Authentication required.",
1984
- status: 401,
1985
- code: "AUTHENTICATION_REQUIRED",
1986
- invariantCode: "auth.required",
1987
- suggestion: "Provide a valid bearer token before invoking belief mutations."
1988
- });
1909
+
1910
+ // src/embeddingTrigger.ts
1911
+ async function scheduleEmbeddingGeneration(args) {
1912
+ try {
1913
+ await args.ctx.scheduler.runAfter(
1914
+ 0,
1915
+ "embeddingActions:generateEpistemicNodeEmbedding",
1916
+ {
1917
+ nodeId: args.nodeId,
1918
+ projectId: args.projectId ? String(args.projectId) : void 0,
1919
+ topicId: args.topicId ? String(args.topicId) : void 0,
1920
+ createdBy: args.createdBy,
1921
+ nodeType: args.nodeType,
1922
+ text: args.text.slice(0, 2e4),
1923
+ hasAnswer: args.hasAnswer,
1924
+ confidence: args.confidence
1925
+ }
1926
+ );
1927
+ } catch (error) {
1928
+ debugGraphPrimitiveFallback(
1929
+ "[embeddingTrigger] Failed to schedule embedding generation",
1930
+ {
1931
+ error,
1932
+ nodeId: String(args.nodeId),
1933
+ nodeType: args.nodeType
1934
+ }
1935
+ );
1989
1936
  }
1990
- return userId;
1991
1937
  }
1992
- async function requireProjectWriteAccess(ctx, projectId, userId) {
1993
- const hasAccess = await checkProjectAccess(ctx, projectId, userId);
1994
- if (!hasAccess) {
1995
- throwStructuredMutationError({
1996
- message: "Project access required.",
1997
- status: 403,
1998
- code: "FORBIDDEN",
1999
- invariantCode: "policy.scope_required",
2000
- suggestion: "Request write access for the project and retry.",
2001
- details: { projectId, userId }
2002
- });
2003
- }
1938
+
1939
+ // src/globalId.ts
1940
+ function generateGlobalId() {
1941
+ const bytes = new Uint8Array(16);
1942
+ crypto.getRandomValues(bytes);
1943
+ bytes[6] = bytes[6] & 15 | 64;
1944
+ bytes[8] = bytes[8] & 63 | 128;
1945
+ const hex = Array.from(bytes, (b) => b.toString(16).padStart(2, "0")).join(
1946
+ ""
1947
+ );
1948
+ return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20)}`;
2004
1949
  }
1950
+
1951
+ // src/epistemicBeliefs.core.ts
2005
1952
  var create = mutation({
2006
1953
  args: {
2007
1954
  ...optionalBeliefScopeArgs,
@@ -2419,11 +2366,11 @@ var getByProject = query({
2419
2366
  return [];
2420
2367
  }
2421
2368
  }
2422
- const query2 = ctx.db.query("epistemicNodes").withIndex(
2369
+ const query6 = ctx.db.query("epistemicNodes").withIndex(
2423
2370
  "by_topic_type",
2424
2371
  (q) => q.eq("topicId", scope.topicId).eq("nodeType", "belief")
2425
2372
  );
2426
- const nodes = await query2.order("desc").take(scanLimit);
2373
+ const nodes = await query6.order("desc").take(scanLimit);
2427
2374
  const scopedNodes = nodes.filter(
2428
2375
  (node) => nodeMatchesWorkspaceReasoningScope(node, scope)
2429
2376
  );
@@ -2453,11 +2400,11 @@ var getByTopic = query({
2453
2400
  const scope = await resolveTopicProjectScope(ctx, {
2454
2401
  topicId: args.topicId
2455
2402
  });
2456
- const query2 = ctx.db.query("epistemicNodes").withIndex(
2403
+ const query6 = ctx.db.query("epistemicNodes").withIndex(
2457
2404
  "by_topic_type",
2458
2405
  (q) => q.eq("topicId", args.topicId).eq("nodeType", "belief")
2459
2406
  );
2460
- const nodes = await query2.order("desc").take(scanLimit);
2407
+ const nodes = await query6.order("desc").take(scanLimit);
2461
2408
  const scopedNodes = nodes.filter(
2462
2409
  (node) => nodeMatchesWorkspaceReasoningScope(node, {
2463
2410
  tenantId: scope.tenantId,
@@ -2962,7 +2909,9 @@ var getByIds = query({
2962
2909
  },
2963
2910
  returns: permissiveReturn,
2964
2911
  handler: async (ctx, args) => {
2965
- const beliefs = await Promise.all(args.nodeIds.map((id) => ctx.db.get(id)));
2912
+ const beliefs = await Promise.all(
2913
+ args.nodeIds.map((id) => ctx.db.get(id))
2914
+ );
2966
2915
  return beliefs.filter((b) => b !== null && b.nodeType === "belief");
2967
2916
  }
2968
2917
  });
@@ -2985,6 +2934,69 @@ var getByWorktree = query({
2985
2934
  return beliefs;
2986
2935
  }
2987
2936
  });
2937
+
2938
+ // src/logicalRoleInference.ts
2939
+ var PILLAR_IMPORTANCE = {
2940
+ market: 1,
2941
+ competition: 2,
2942
+ product: 3,
2943
+ team: 4,
2944
+ financials: 5,
2945
+ timing: 6,
2946
+ other: 10
2947
+ };
2948
+ async function computeLogicalRole(ctx, evidenceId, beliefId) {
2949
+ const belief = await ctx.db.get(beliefId);
2950
+ if (!belief || belief.nodeType !== "belief") {
2951
+ return "contributory";
2952
+ }
2953
+ const beliefMetadata = belief.metadata;
2954
+ const pillar = beliefMetadata?.pillar || "other";
2955
+ const pillarRank = PILLAR_IMPORTANCE[pillar] ?? 10;
2956
+ const isSynthesized = await checkIfSynthesizedHypothesis(ctx, beliefId);
2957
+ const testingQuestions = await getTestingQuestionsForBelief(ctx, beliefId);
2958
+ const answeredQuestions = await getQuestionsAnsweredByEvidence(
2959
+ ctx,
2960
+ evidenceId
2961
+ );
2962
+ const directlyTests = testingQuestions.filter(
2963
+ (questionId) => answeredQuestions.includes(questionId)
2964
+ );
2965
+ if (directlyTests.length === 0) {
2966
+ return "contributory";
2967
+ }
2968
+ if (isSynthesized && pillarRank <= 2) {
2969
+ return directlyTests.length > 1 ? "necessary_sufficient" : "necessary";
2970
+ }
2971
+ if (isSynthesized) {
2972
+ return "necessary";
2973
+ }
2974
+ return directlyTests.length > 1 ? "necessary" : "contributory";
2975
+ }
2976
+ async function checkIfSynthesizedHypothesis(ctx, beliefId) {
2977
+ const belief = await ctx.db.get(beliefId);
2978
+ if (!belief) {
2979
+ return false;
2980
+ }
2981
+ const metadata = belief.metadata;
2982
+ return metadata?.isSynthesized === true || metadata?.beliefStatus === "hypothesis";
2983
+ }
2984
+ async function getTestingQuestionsForBelief(ctx, beliefId) {
2985
+ const testEdges = await ctx.db.query("epistemicEdges").withIndex(
2986
+ "by_to_type",
2987
+ (q) => q.eq("toNodeId", beliefId).eq("edgeType", "tests")
2988
+ ).collect();
2989
+ return testEdges.map((edge) => edge.fromNodeId).filter((id) => id !== void 0);
2990
+ }
2991
+ async function getQuestionsAnsweredByEvidence(ctx, evidenceId) {
2992
+ const answerEdges = await ctx.db.query("epistemicEdges").withIndex(
2993
+ "by_from_type",
2994
+ (q) => q.eq("fromNodeId", evidenceId).eq("edgeType", "derived_from")
2995
+ ).collect();
2996
+ return answerEdges.map((edge) => edge.toNodeId).filter((id) => id !== void 0);
2997
+ }
2998
+
2999
+ // src/epistemicBeliefs.links.ts
2988
3000
  var updatePillar = mutation({
2989
3001
  args: {
2990
3002
  nodeId: v.id("epistemicNodes"),
@@ -3775,11 +3787,11 @@ var internalGetByTopic = internalQuery({
3775
3787
  });
3776
3788
  const resolveAudienceClass = createBeliefAudienceResolver(registryRows);
3777
3789
  const viewerClass = resolveAudienceClass(audienceMode, "public");
3778
- const query2 = ctx.db.query("epistemicNodes").withIndex(
3790
+ const query6 = ctx.db.query("epistemicNodes").withIndex(
3779
3791
  "by_topic_type",
3780
3792
  (q) => q.eq("topicId", args.topicId).eq("nodeType", "belief")
3781
3793
  );
3782
- const nodes = await query2.order("desc").take(scanLimit);
3794
+ const nodes = await query6.order("desc").take(scanLimit);
3783
3795
  let filtered = nodes.filter(
3784
3796
  (node) => canAudienceClassAccess(
3785
3797
  viewerClass,