@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
@@ -0,0 +1,2506 @@
1
+ import { v } from 'convex/values';
2
+ import { normalizeTupleContradictionPolicy, mkOpinion, conditionalDeduction, project, dampedDependencyCascade, hasProjectedOpinionChanged, confidenceFromSL, detectTupleContradiction, evaluateTupleContradictionTransition, trustDiscount, applyNegativeSupport, cumulativeFusion, applyNegativeEvidence, readOpinionFromRecord, deriveContractModulationPlan, deriveContractStatus, parseEvidentialEvaluatorConfig, compareMetricValue, buildEvidentialRationale, parseMetricCheckerConfig, getEvaluatorInputRecord, pickFiniteNumber, resolveComparisonResult, buildComparisonRationale, parseReferenceCheckCounterConfig, parseTemporalDeadlineConfig, parseMarketIndexComparatorConfig } from '@lucern/confidence';
3
+ import { checkProjectAccess } from '@lucern/access-control/access';
4
+ import '@lucern/access-control/audience';
5
+ import '@lucern/access-control/auth';
6
+ import { componentsGeneric, internalMutationGeneric, anyApi } from 'convex/server';
7
+ import '@lucern/contracts/schema-helpers/spine/tables/epistemicNodes';
8
+ import { permissiveReturn } from '@lucern/contracts/schema-helpers/validators';
9
+
10
+ // src/epistemicBeliefs.helpers.ts
11
+
12
+ // src/beliefLifecycle.ts
13
+ var BELIEF_STATUS_VALUES = [
14
+ "assumption",
15
+ "hypothesis",
16
+ "belief",
17
+ "fact"
18
+ ];
19
+ var RESOLVED_PREDICTION_OUTCOMES = [
20
+ "confirmed",
21
+ "disconfirmed",
22
+ "partial",
23
+ "expired"
24
+ ];
25
+ function isBeliefLifecycleStatus(value) {
26
+ return typeof value === "string" && BELIEF_STATUS_VALUES.includes(value);
27
+ }
28
+ function normalizeBeliefConfidence(confidence) {
29
+ if (typeof confidence !== "number" || !Number.isFinite(confidence)) {
30
+ return null;
31
+ }
32
+ if (confidence >= 0 && confidence <= 1) {
33
+ return confidence;
34
+ }
35
+ if (confidence > 1 && confidence <= 100) {
36
+ return confidence / 100;
37
+ }
38
+ return null;
39
+ }
40
+ function isResolvedByConfidence(confidence) {
41
+ const normalized = normalizeBeliefConfidence(confidence);
42
+ if (normalized === null) {
43
+ return false;
44
+ }
45
+ return normalized <= 0 || normalized >= 1;
46
+ }
47
+ function hasResolvedPredictionOutcome(predictionMeta) {
48
+ if (!predictionMeta || typeof predictionMeta !== "object") {
49
+ return false;
50
+ }
51
+ const outcome = predictionMeta.outcome;
52
+ return typeof outcome === "string" && RESOLVED_PREDICTION_OUTCOMES.includes(outcome);
53
+ }
54
+ function getPredictionMetaFromMetadata(metadata) {
55
+ return metadata?.predictionMeta;
56
+ }
57
+ function shouldTreatBeliefAsFact(opts) {
58
+ if (isResolvedByConfidence(opts.confidence)) {
59
+ return true;
60
+ }
61
+ if (hasResolvedPredictionOutcome(opts.predictionMeta)) {
62
+ return true;
63
+ }
64
+ if (hasResolvedPredictionOutcome(getPredictionMetaFromMetadata(opts.metadata))) {
65
+ return true;
66
+ }
67
+ return false;
68
+ }
69
+ function resolveBeliefLifecycleStatus(opts) {
70
+ if (shouldTreatBeliefAsFact(opts)) {
71
+ return "fact";
72
+ }
73
+ const direct = opts.beliefStatus;
74
+ if (isBeliefLifecycleStatus(direct)) {
75
+ const normalized = normalizeBeliefConfidence(opts.confidence);
76
+ if (normalized !== null && isPreValidationBeliefStatus(direct)) {
77
+ return "belief";
78
+ }
79
+ return direct;
80
+ }
81
+ const metaStatus = opts.metadata?.beliefStatus;
82
+ if (isBeliefLifecycleStatus(metaStatus)) {
83
+ const normalized = normalizeBeliefConfidence(opts.confidence);
84
+ if (normalized !== null && isPreValidationBeliefStatus(metaStatus)) {
85
+ return "belief";
86
+ }
87
+ return metaStatus;
88
+ }
89
+ return "assumption";
90
+ }
91
+ function isPreValidationBeliefStatus(status) {
92
+ return status === "assumption" || status === "hypothesis";
93
+ }
94
+ function promoteBeliefStatusAfterScoring(status, opts) {
95
+ if (shouldTreatBeliefAsFact({ ...opts })) {
96
+ return "fact";
97
+ }
98
+ if (isPreValidationBeliefStatus(status)) {
99
+ return "belief";
100
+ }
101
+ return status === "fact" ? "fact" : "belief";
102
+ }
103
+ var api = anyApi;
104
+ componentsGeneric();
105
+ var internal = anyApi;
106
+ var internalMutation = internalMutationGeneric;
107
+
108
+ // src/debug.ts
109
+ function isGraphPrimitiveDebugEnabled() {
110
+ const env = globalThis.process?.env;
111
+ return env?.LUCERN_COMPAT_FALLBACK_DEBUG === "1" || env?.LUCERN_GRAPH_DEBUG === "1";
112
+ }
113
+ function debugGraphPrimitiveFallback(message, context) {
114
+ if (!isGraphPrimitiveDebugEnabled()) {
115
+ return;
116
+ }
117
+ console.debug(message, context ?? {});
118
+ }
119
+ var LEGACY_SCOPE_FIELD = "graphScopeProjectId";
120
+ function asMappedProjectId(topic) {
121
+ if (!topic) {
122
+ return;
123
+ }
124
+ const directLegacyProjectId = normalizeScopeValue(topic[LEGACY_SCOPE_FIELD]);
125
+ if (directLegacyProjectId) {
126
+ return directLegacyProjectId;
127
+ }
128
+ const metadata = topic.metadata || {};
129
+ const candidate = metadata[LEGACY_SCOPE_FIELD] || metadata.legacyProjectId || metadata.projectId || metadata.scopeProjectId;
130
+ return candidate ? candidate : void 0;
131
+ }
132
+ function normalizeScopeValue(value) {
133
+ if (typeof value !== "string") {
134
+ return;
135
+ }
136
+ const normalized = value.trim();
137
+ return normalized.length > 0 ? normalized : void 0;
138
+ }
139
+ function pickPrimaryTopic(candidates) {
140
+ return [...candidates].sort((a, b) => {
141
+ const depthA = a.depth ?? 9999;
142
+ const depthB = b.depth ?? 9999;
143
+ if (depthA !== depthB) {
144
+ return depthA - depthB;
145
+ }
146
+ const createdA = a.createdAt ?? Number.MAX_SAFE_INTEGER;
147
+ const createdB = b.createdAt ?? Number.MAX_SAFE_INTEGER;
148
+ if (createdA !== createdB) {
149
+ return createdA - createdB;
150
+ }
151
+ return String(a.name || "").localeCompare(String(b.name || ""));
152
+ })[0];
153
+ }
154
+ async function findTopicsByScopeAlias(ctx, scopeId) {
155
+ try {
156
+ return await ctx.db.query("topics").withIndex(
157
+ "by_graph_scope_project",
158
+ (q) => q.eq(LEGACY_SCOPE_FIELD, scopeId)
159
+ ).collect();
160
+ } catch (error) {
161
+ debugGraphPrimitiveFallback(
162
+ "[topicScope] Failed to resolve scope alias via index",
163
+ {
164
+ error,
165
+ scopeId
166
+ }
167
+ );
168
+ const topics = await ctx.db.query("topics").collect();
169
+ return topics.filter((topic) => {
170
+ const normalizedGlobalId = normalizeScopeValue(topic.globalId);
171
+ const mappedProjectId = asMappedProjectId(topic);
172
+ return String(topic._id) === scopeId || normalizedGlobalId === scopeId || mappedProjectId === scopeId;
173
+ });
174
+ }
175
+ }
176
+ async function tryResolveHostTopicById(ctx, topicId) {
177
+ if (typeof ctx.runQuery !== "function") {
178
+ return null;
179
+ }
180
+ try {
181
+ return await ctx.runQuery(api.topics.get, {
182
+ id: topicId
183
+ }) ?? null;
184
+ } catch (error) {
185
+ debugGraphPrimitiveFallback(
186
+ "[topicScope] Failed to resolve topic by host query",
187
+ {
188
+ error,
189
+ topicId
190
+ }
191
+ );
192
+ return null;
193
+ }
194
+ }
195
+ async function tryResolveHostTopicByLegacyScope(ctx, legacyScopeId) {
196
+ if (typeof ctx.runQuery !== "function") {
197
+ return null;
198
+ }
199
+ try {
200
+ return await ctx.runQuery(api.topics.getByLegacyScopeId, {
201
+ projectId: legacyScopeId
202
+ }) ?? null;
203
+ } catch (error) {
204
+ debugGraphPrimitiveFallback(
205
+ "[topicScope] Failed to resolve topic by legacy scope",
206
+ {
207
+ error,
208
+ legacyScopeId
209
+ }
210
+ );
211
+ return null;
212
+ }
213
+ }
214
+ async function resolveInheritedWorkspaceScope(ctx, topic) {
215
+ const MAX_DEPTH = 10;
216
+ let tenantId = normalizeScopeValue(topic.tenantId);
217
+ let workspaceId = normalizeScopeValue(topic.workspaceId);
218
+ if (tenantId && workspaceId) {
219
+ return { tenantId, workspaceId };
220
+ }
221
+ let current = topic;
222
+ for (let i = 0; i < MAX_DEPTH && current?.parentTopicId; i++) {
223
+ current = await ctx.db.get(current.parentTopicId);
224
+ if (!current) break;
225
+ if (!tenantId) {
226
+ tenantId = normalizeScopeValue(current.tenantId);
227
+ }
228
+ if (!workspaceId) {
229
+ workspaceId = normalizeScopeValue(current.workspaceId);
230
+ }
231
+ if (tenantId && workspaceId) break;
232
+ }
233
+ return { tenantId, workspaceId };
234
+ }
235
+ async function resolveTopicProjectScope(ctx, args) {
236
+ if (args.topicId) {
237
+ let topic = null;
238
+ try {
239
+ topic = await ctx.db.get(
240
+ args.topicId
241
+ );
242
+ } catch (error) {
243
+ debugGraphPrimitiveFallback(
244
+ "[topicScope] Failed to load topic by direct id",
245
+ {
246
+ error,
247
+ topicId: args.topicId
248
+ }
249
+ );
250
+ }
251
+ if (!topic) {
252
+ topic = await tryResolveHostTopicById(ctx, String(args.topicId));
253
+ }
254
+ if (!topic) {
255
+ topic = pickPrimaryTopic(
256
+ await findTopicsByScopeAlias(ctx, String(args.topicId))
257
+ ) ?? null;
258
+ }
259
+ if (!topic) {
260
+ throw new Error(`Topic not found: ${String(args.topicId)}`);
261
+ }
262
+ const inherited = await resolveInheritedWorkspaceScope(ctx, topic);
263
+ const mapped = asMappedProjectId(topic);
264
+ if (mapped) {
265
+ return {
266
+ topicId: topic._id,
267
+ projectId: mapped,
268
+ tenantId: inherited.tenantId,
269
+ workspaceId: inherited.workspaceId,
270
+ source: "topic"
271
+ };
272
+ }
273
+ return {
274
+ topicId: topic._id,
275
+ tenantId: inherited.tenantId,
276
+ workspaceId: inherited.workspaceId,
277
+ source: "topic"
278
+ };
279
+ }
280
+ if (args.projectId) {
281
+ let directTopic = null;
282
+ try {
283
+ directTopic = await ctx.db.get(
284
+ args.projectId
285
+ );
286
+ } catch (error) {
287
+ debugGraphPrimitiveFallback(
288
+ "[topicScope] Failed to load direct project topic",
289
+ {
290
+ error,
291
+ projectId: args.projectId
292
+ }
293
+ );
294
+ }
295
+ if (directTopic) {
296
+ const inherited = await resolveInheritedWorkspaceScope(ctx, directTopic);
297
+ const mapped = asMappedProjectId(directTopic);
298
+ return {
299
+ topicId: directTopic._id,
300
+ projectId: mapped ?? args.projectId,
301
+ tenantId: inherited.tenantId,
302
+ workspaceId: inherited.workspaceId,
303
+ source: "topic_inferred"
304
+ };
305
+ }
306
+ directTopic = await tryResolveHostTopicByLegacyScope(ctx, args.projectId);
307
+ if (directTopic) {
308
+ const inherited = await resolveInheritedWorkspaceScope(ctx, directTopic);
309
+ const mapped = asMappedProjectId(directTopic);
310
+ return {
311
+ topicId: directTopic._id,
312
+ projectId: mapped ?? args.projectId,
313
+ tenantId: inherited.tenantId,
314
+ workspaceId: inherited.workspaceId,
315
+ source: "topic_inferred"
316
+ };
317
+ }
318
+ const topics = await findTopicsByScopeAlias(ctx, args.projectId);
319
+ const primary = pickPrimaryTopic(topics);
320
+ if (primary) {
321
+ const inherited = await resolveInheritedWorkspaceScope(ctx, primary);
322
+ return {
323
+ topicId: primary._id,
324
+ projectId: args.projectId,
325
+ tenantId: inherited.tenantId,
326
+ workspaceId: inherited.workspaceId,
327
+ source: "project_mapped_topic"
328
+ };
329
+ }
330
+ throw new Error(
331
+ `Legacy project scope ${String(args.projectId)} has no mapped topic.`
332
+ );
333
+ }
334
+ throw new Error(
335
+ "Missing scope: provide topicId (preferred) or legacy projectId alias."
336
+ );
337
+ }
338
+ ({
339
+ projectId: v.optional(v.string()),
340
+ topicId: v.optional(v.string())
341
+ });
342
+ function normalizeScopeValue2(value) {
343
+ if (typeof value !== "string") {
344
+ return;
345
+ }
346
+ const normalized = value.trim();
347
+ return normalized.length > 0 ? normalized : void 0;
348
+ }
349
+ function nodeMatchesWorkspaceReasoningScope(node, scope) {
350
+ if (!node) {
351
+ return false;
352
+ }
353
+ const scopeTenantId = normalizeScopeValue2(scope.tenantId);
354
+ const scopeWorkspaceId = normalizeScopeValue2(scope.workspaceId);
355
+ const nodeTenantId = normalizeScopeValue2(node.tenantId);
356
+ const nodeWorkspaceId = normalizeScopeValue2(node.workspaceId);
357
+ const epistemicLayer = typeof node.epistemicLayer === "string" ? node.epistemicLayer : void 0;
358
+ if (scopeTenantId && nodeTenantId && scopeTenantId !== nodeTenantId) {
359
+ return false;
360
+ }
361
+ if (epistemicLayer === "ontological" && nodeWorkspaceId === void 0) {
362
+ return true;
363
+ }
364
+ if (!scopeWorkspaceId && node.publicationStatus === "published") {
365
+ return true;
366
+ }
367
+ if (!scopeWorkspaceId) {
368
+ return nodeWorkspaceId === void 0;
369
+ }
370
+ return scopeWorkspaceId === nodeWorkspaceId;
371
+ }
372
+ function edgeMatchesWorkspaceReasoningScope(edge, scope) {
373
+ if (!edge) {
374
+ return false;
375
+ }
376
+ const scopeTenantId = normalizeScopeValue2(scope.tenantId);
377
+ const scopeWorkspaceId = normalizeScopeValue2(scope.workspaceId);
378
+ const edgeTenantId = normalizeScopeValue2(edge.tenantId);
379
+ const edgeWorkspaceId = normalizeScopeValue2(edge.workspaceId);
380
+ if (scopeTenantId && edgeTenantId && scopeTenantId !== edgeTenantId) {
381
+ return false;
382
+ }
383
+ if (!scopeWorkspaceId) {
384
+ return edgeWorkspaceId === void 0;
385
+ }
386
+ return scopeWorkspaceId === edgeWorkspaceId;
387
+ }
388
+ async function resolveNodeScopeForWorkspaceIsolation(ctx, node) {
389
+ const epistemicLayer = typeof node?.epistemicLayer === "string" ? node.epistemicLayer : void 0;
390
+ const resolved = {
391
+ tenantId: normalizeScopeValue2(node?.tenantId),
392
+ workspaceId: normalizeScopeValue2(node?.workspaceId),
393
+ epistemicLayer,
394
+ nodeType: typeof node?.nodeType === "string" ? node.nodeType : void 0
395
+ };
396
+ if (!node) {
397
+ return resolved;
398
+ }
399
+ if (resolved.epistemicLayer === "ontological") {
400
+ return resolved;
401
+ }
402
+ if (resolved.tenantId || resolved.workspaceId) {
403
+ return resolved;
404
+ }
405
+ if (node.topicId) {
406
+ const topicScope = await resolveTopicProjectScope(ctx, {
407
+ topicId: node.topicId
408
+ });
409
+ return {
410
+ ...resolved,
411
+ tenantId: topicScope.tenantId,
412
+ workspaceId: topicScope.workspaceId
413
+ };
414
+ }
415
+ if (node.projectId) {
416
+ const topicScope = await resolveTopicProjectScope(ctx, {
417
+ projectId: String(node.projectId)
418
+ });
419
+ return {
420
+ ...resolved,
421
+ tenantId: topicScope.tenantId,
422
+ workspaceId: topicScope.workspaceId
423
+ };
424
+ }
425
+ return resolved;
426
+ }
427
+
428
+ // src/epistemicBeliefs.helpers.ts
429
+ v.id("epistemicNodes");
430
+ var DEFAULT_CONFIDENCE_POLICY = {
431
+ scoringMode: "after_worktree",
432
+ tupleContradiction: normalizeTupleContradictionPolicy()
433
+ };
434
+ function throwStructuredMutationError(args) {
435
+ const error = new Error(args.message);
436
+ error.status = args.status;
437
+ error.code = args.code;
438
+ error.invariantCode = args.invariantCode;
439
+ error.suggestion = args.suggestion;
440
+ error.details = args.details;
441
+ throw error;
442
+ }
443
+ function buildBeliefConfidenceRow(args) {
444
+ return {
445
+ beliefId: args.beliefId,
446
+ confidence: confidenceFromSL(
447
+ args.belief,
448
+ args.disbelief,
449
+ args.uncertainty,
450
+ args.baseRate
451
+ ),
452
+ belief: args.belief,
453
+ disbelief: args.disbelief,
454
+ uncertainty: args.uncertainty,
455
+ baseRate: args.baseRate,
456
+ slOperator: args.slOperator ?? "manual_assessment",
457
+ trigger: args.trigger,
458
+ ...args.rationale ? { rationale: args.rationale } : {},
459
+ assessedBy: args.assessedBy,
460
+ assessedAt: args.assessedAt,
461
+ ...args.triggeringEvidenceId ? {
462
+ triggeringEvidenceId: args.triggeringEvidenceId,
463
+ triggeringEvidenceIds: [String(args.triggeringEvidenceId)]
464
+ } : {},
465
+ ...args.triggeringContradictionId ? {
466
+ triggeringContradictionId: args.triggeringContradictionId
467
+ } : {},
468
+ ...args.triggeringWorktreeId ? { triggeringWorktreeId: args.triggeringWorktreeId } : {}
469
+ };
470
+ }
471
+ function readTupleContradictedFlag(value) {
472
+ return typeof value === "boolean" ? value : void 0;
473
+ }
474
+ function readBeliefOpinionSnapshot(node, metadata) {
475
+ try {
476
+ return readOpinionFromRecord({
477
+ ...metadata,
478
+ opinion_b: node.opinion_b,
479
+ opinion_d: node.opinion_d,
480
+ opinion_u: node.opinion_u,
481
+ opinion_a: node.opinion_a
482
+ });
483
+ } catch (error) {
484
+ debugGraphPrimitiveFallback(
485
+ "[epistemicBeliefs] Failed to read belief opinion snapshot",
486
+ {
487
+ error,
488
+ beliefId: node._id
489
+ }
490
+ );
491
+ return mkOpinion(0, 0, 1, 0.5);
492
+ }
493
+ }
494
+ function deriveTupleContradictionSeverity(node) {
495
+ const metadata = node.metadata || {};
496
+ const criticality = typeof metadata.criticality === "string" ? metadata.criticality : void 0;
497
+ if (criticality === "blocking") {
498
+ return "critical";
499
+ }
500
+ if (criticality === "supporting") {
501
+ return "minor";
502
+ }
503
+ return "significant";
504
+ }
505
+ function formatTupleContradictionDescription(args) {
506
+ 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)}.`;
507
+ }
508
+ function resolveBeliefStatus(node, metadata) {
509
+ return resolveBeliefLifecycleStatus({
510
+ beliefStatus: node.beliefStatus,
511
+ confidence: node.confidence,
512
+ predictionMeta: node.predictionMeta,
513
+ metadata
514
+ });
515
+ }
516
+ async function hasCompletedWorktreeForBelief(ctx, beliefNodeId) {
517
+ const clusterMembership = await ctx.db.query("worktreeBeliefCluster").withIndex("by_belief", (q) => q.eq("beliefId", beliefNodeId)).collect();
518
+ for (const membership of clusterMembership) {
519
+ const worktree = await ctx.db.get(membership.worktreeId);
520
+ if (worktree?.status === "completed" || worktree?.status === "merged") {
521
+ return true;
522
+ }
523
+ }
524
+ return false;
525
+ }
526
+ async function getActiveConfidencePolicy(ctx) {
527
+ try {
528
+ const activeConfig = await ctx.db.query("logicSprintScoring").withIndex("by_active", (q) => q.eq("isActive", true)).first();
529
+ return {
530
+ scoringMode: activeConfig?.confidencePolicy === "always" ? "always" : DEFAULT_CONFIDENCE_POLICY.scoringMode,
531
+ tupleContradiction: normalizeTupleContradictionPolicy(
532
+ activeConfig?.tupleContradictionPolicy
533
+ )
534
+ };
535
+ } catch (error) {
536
+ debugGraphPrimitiveFallback(
537
+ "[epistemicBeliefs] Failed to load active confidence policy",
538
+ {
539
+ error
540
+ }
541
+ );
542
+ return DEFAULT_CONFIDENCE_POLICY;
543
+ }
544
+ }
545
+ async function requireProjectWriteAccess(ctx, projectId, userId) {
546
+ const hasAccess = await checkProjectAccess(
547
+ ctx,
548
+ projectId,
549
+ userId
550
+ );
551
+ if (!hasAccess) {
552
+ throwStructuredMutationError({
553
+ message: "Project access required.",
554
+ status: 403,
555
+ code: "FORBIDDEN",
556
+ invariantCode: "policy.scope_required",
557
+ suggestion: "Request write access for the project and retry.",
558
+ details: { projectId, userId }
559
+ });
560
+ }
561
+ }
562
+
563
+ // src/edges/contains.ts
564
+ var containsPropagationSpec = {
565
+ edgeType: "contains",
566
+ direction: "outgoing",
567
+ transitivity: "none",
568
+ damping: 1,
569
+ maxHops: 1,
570
+ operator: () => null,
571
+ description: "Structural containment only. Traversed for explicit semantics, but it never propagates opinions."
572
+ };
573
+ function readEdgeMetadata(edge) {
574
+ return {
575
+ constraint: edge.constraint ?? void 0,
576
+ normalization: edge.normalization ?? void 0,
577
+ propagation: edge.propagation ?? void 0,
578
+ conditionalA: edge.conditionalA ?? void 0,
579
+ conditionalNotA: edge.conditionalNotA ?? void 0
580
+ };
581
+ }
582
+ function applyPerHopDamping(sourceOpinion, damping) {
583
+ if (damping >= 1) {
584
+ return sourceOpinion;
585
+ }
586
+ return trustDiscount(sourceOpinion, Math.max(0, damping));
587
+ }
588
+ function annotateRationale(result, spec, hop) {
589
+ return {
590
+ ...result,
591
+ rationale: `hop=${hop} edge=${spec.edgeType} damping=${spec.damping.toFixed(
592
+ 2
593
+ )} :: ${result.rationale}`
594
+ };
595
+ }
596
+ function propagatePositiveSupport(sourceOpinion, targetOpinion, edgeWeight) {
597
+ const discounted = trustDiscount(sourceOpinion, Math.abs(edgeWeight));
598
+ return {
599
+ opinion: cumulativeFusion(targetOpinion, discounted),
600
+ operator: "cumulative_fusion",
601
+ rationale: `Supporting evidence (weight=${edgeWeight.toFixed(
602
+ 2
603
+ )}) from source at ${project(sourceOpinion).toFixed(2)}`
604
+ };
605
+ }
606
+ function propagatePositiveInform(sourceOpinion, targetOpinion, edgeWeight) {
607
+ const discounted = trustDiscount(sourceOpinion, Math.abs(edgeWeight));
608
+ return {
609
+ opinion: cumulativeFusion(targetOpinion, discounted),
610
+ operator: "cumulative_fusion",
611
+ rationale: `Supporting evidence (weight=${edgeWeight.toFixed(2)})`
612
+ };
613
+ }
614
+ function propagateNegativeSupportWithMetadata(sourceOpinion, targetOpinion, edgeWeight, edge) {
615
+ return applyNegativeSupport(
616
+ sourceOpinion,
617
+ targetOpinion,
618
+ edgeWeight,
619
+ readEdgeMetadata(edge)
620
+ );
621
+ }
622
+ var propagateNegativeInform = applyNegativeEvidence;
623
+
624
+ // src/edges/contradicts.ts
625
+ var contradictsPropagationSpec = {
626
+ edgeType: "contradicts",
627
+ direction: "bidirectional",
628
+ transitivity: "none",
629
+ damping: 0.85,
630
+ maxHops: 1,
631
+ operator: (sourceOpinion, targetOpinion, edge, context) => {
632
+ const dampedSource = applyPerHopDamping(
633
+ sourceOpinion,
634
+ context.spec.damping
635
+ );
636
+ const negativeWeight = -Math.abs(edge.weight ?? 1);
637
+ const result = propagateNegativeSupportWithMetadata(
638
+ dampedSource,
639
+ targetOpinion,
640
+ negativeWeight,
641
+ edge
642
+ );
643
+ return annotateRationale(result, context.spec, context.hop);
644
+ },
645
+ description: "Legacy contradiction edges move negative pressure in either direction, but never beyond one hop."
646
+ };
647
+ var dependsOnPropagationSpec = {
648
+ edgeType: "depends_on",
649
+ direction: "incoming",
650
+ transitivity: "damped",
651
+ damping: 0.8,
652
+ maxHops: "unbounded",
653
+ operator: (sourceOpinion, targetOpinion, edge, context) => {
654
+ const dampedSource = applyPerHopDamping(
655
+ sourceOpinion,
656
+ context.spec.damping
657
+ );
658
+ const metadata = readEdgeMetadata(edge);
659
+ if (metadata.conditionalA && metadata.conditionalNotA) {
660
+ const deducedOpinion = conditionalDeduction(
661
+ dampedSource,
662
+ mkOpinion(
663
+ metadata.conditionalA.b,
664
+ metadata.conditionalA.d,
665
+ metadata.conditionalA.u,
666
+ metadata.conditionalA.a
667
+ ),
668
+ mkOpinion(
669
+ metadata.conditionalNotA.b,
670
+ metadata.conditionalNotA.d,
671
+ metadata.conditionalNotA.u,
672
+ metadata.conditionalNotA.a
673
+ ),
674
+ targetOpinion.a
675
+ );
676
+ return annotateRationale(
677
+ {
678
+ opinion: deducedOpinion,
679
+ operator: "conditional_deduction",
680
+ rationale: `Conditional deduction: prerequisite at ${project(
681
+ dampedSource
682
+ ).toFixed(2)}`
683
+ },
684
+ context.spec,
685
+ context.hop
686
+ );
687
+ }
688
+ const result = dampedDependencyCascade(
689
+ dampedSource,
690
+ targetOpinion,
691
+ metadata.propagation ?? "continuous"
692
+ );
693
+ return annotateRationale(result, context.spec, context.hop);
694
+ },
695
+ description: "Structural gating. Textbook conditional deduction when edge conditionals exist, otherwise damped dependency cascade through downstream chains."
696
+ };
697
+
698
+ // src/edges/derivedFrom.ts
699
+ var derivedFromPropagationSpec = {
700
+ edgeType: "derived_from",
701
+ direction: "incoming",
702
+ transitivity: "none",
703
+ damping: 1,
704
+ maxHops: 1,
705
+ operator: () => null,
706
+ description: "Provenance only. The traversal surface stays explicit, but confidence does not move across derived_from edges."
707
+ };
708
+
709
+ // src/edges/elaborates.ts
710
+ var elaboratesPropagationSpec = {
711
+ edgeType: "elaborates",
712
+ direction: "outgoing",
713
+ transitivity: "damped",
714
+ damping: 0.7,
715
+ maxHops: 2,
716
+ operator: (sourceOpinion, targetOpinion, edge, context) => {
717
+ const dampedSource = applyPerHopDamping(
718
+ sourceOpinion,
719
+ context.spec.damping
720
+ );
721
+ const contextualWeight = Math.min(Math.abs(edge.weight ?? 0.35), 0.35);
722
+ const result = propagatePositiveInform(
723
+ dampedSource,
724
+ targetOpinion,
725
+ contextualWeight
726
+ );
727
+ return annotateRationale(result, context.spec, context.hop);
728
+ },
729
+ description: "Context-rich supporting detail. Elaborates carries a small positive effect with short, damped chaining."
730
+ };
731
+
732
+ // src/edges/informs.ts
733
+ var informsPropagationSpec = {
734
+ edgeType: "informs",
735
+ direction: "outgoing",
736
+ transitivity: "full",
737
+ damping: 0.92,
738
+ maxHops: "unbounded",
739
+ operator: (sourceOpinion, targetOpinion, edge, context) => {
740
+ const dampedSource = applyPerHopDamping(
741
+ sourceOpinion,
742
+ context.spec.damping
743
+ );
744
+ const weight = edge.weight ?? 1;
745
+ const result = weight < 0 ? propagateNegativeInform(dampedSource, targetOpinion, weight) : propagatePositiveInform(dampedSource, targetOpinion, weight);
746
+ return annotateRationale(result, context.spec, context.hop);
747
+ },
748
+ description: "Evidence-bearing influence. Informs can chain through the graph with light per-hop damping."
749
+ };
750
+
751
+ // src/edges/propagationTypes.ts
752
+ function isPropagationTraversalDirection(direction) {
753
+ return direction === "outgoing" || direction === "incoming";
754
+ }
755
+ function canTraverseHop(spec, nextHop) {
756
+ return spec.maxHops === "unbounded" || nextHop <= spec.maxHops;
757
+ }
758
+ function canContinueTransitively(spec, currentHop) {
759
+ if (spec.transitivity === "none") {
760
+ return false;
761
+ }
762
+ return spec.maxHops === "unbounded" || currentHop < spec.maxHops;
763
+ }
764
+
765
+ // src/edges/refutes.ts
766
+ var refutesPropagationSpec = {
767
+ edgeType: "refutes",
768
+ direction: "outgoing",
769
+ transitivity: "none",
770
+ damping: 0.9,
771
+ maxHops: 1,
772
+ operator: (sourceOpinion, targetOpinion, edge, context) => {
773
+ const dampedSource = applyPerHopDamping(
774
+ sourceOpinion,
775
+ context.spec.damping
776
+ );
777
+ const negativeWeight = -Math.abs(edge.weight ?? 1);
778
+ const result = propagateNegativeInform(
779
+ dampedSource,
780
+ targetOpinion,
781
+ negativeWeight
782
+ );
783
+ return annotateRationale(result, context.spec, context.hop);
784
+ },
785
+ description: "Explicit negative evidence semantics. Refutes is treated as strong one-hop counter-evidence."
786
+ };
787
+
788
+ // src/edges/supports.ts
789
+ var supportsPropagationSpec = {
790
+ edgeType: "supports",
791
+ direction: "outgoing",
792
+ transitivity: "full",
793
+ damping: 0.85,
794
+ maxHops: "unbounded",
795
+ operator: (sourceOpinion, targetOpinion, edge, context) => {
796
+ const dampedSource = applyPerHopDamping(
797
+ sourceOpinion,
798
+ context.spec.damping
799
+ );
800
+ const weight = edge.weight ?? 1;
801
+ const result = weight < 0 ? propagateNegativeSupportWithMetadata(
802
+ dampedSource,
803
+ targetOpinion,
804
+ weight,
805
+ edge
806
+ ) : propagatePositiveSupport(dampedSource, targetOpinion, weight);
807
+ return annotateRationale(result, context.spec, context.hop);
808
+ },
809
+ description: "Belief-to-belief influence. Supports chains transitively with moderate per-hop damping."
810
+ };
811
+
812
+ // src/edges/tests.ts
813
+ var testsPropagationSpec = {
814
+ edgeType: "tests",
815
+ direction: "outgoing",
816
+ transitivity: "none",
817
+ damping: 1,
818
+ maxHops: 1,
819
+ operator: () => null,
820
+ description: "Interrogation linkage only. Tests edges do not directly move confidence."
821
+ };
822
+
823
+ // src/edges/index.ts
824
+ var EDGE_PROPAGATION_SPECS = [
825
+ supportsPropagationSpec,
826
+ informsPropagationSpec,
827
+ dependsOnPropagationSpec,
828
+ derivedFromPropagationSpec,
829
+ containsPropagationSpec,
830
+ testsPropagationSpec,
831
+ contradictsPropagationSpec,
832
+ refutesPropagationSpec,
833
+ elaboratesPropagationSpec
834
+ ];
835
+ new Map(EDGE_PROPAGATION_SPECS.map((spec) => [spec.edgeType, spec]));
836
+ function getEdgePropagationSpecs() {
837
+ return EDGE_PROPAGATION_SPECS;
838
+ }
839
+ function getTraversalDirections(direction) {
840
+ if (isPropagationTraversalDirection(direction)) {
841
+ return [direction];
842
+ }
843
+ return ["outgoing", "incoming"];
844
+ }
845
+
846
+ // src/confidencePropagationDispatch.ts
847
+ function resolveTraversalTargetNodeId(edge, direction) {
848
+ const targetNodeId = direction === "outgoing" ? edge.toNodeId : edge.fromNodeId;
849
+ return targetNodeId ?? void 0;
850
+ }
851
+ function readNodeOpinion(node) {
852
+ const metadata = node.metadata ?? {};
853
+ try {
854
+ return readOpinionFromRecord({
855
+ ...metadata,
856
+ opinion_b: node.opinion_b,
857
+ opinion_d: node.opinion_d,
858
+ opinion_u: node.opinion_u,
859
+ opinion_a: node.opinion_a
860
+ });
861
+ } catch {
862
+ return mkOpinion(0, 0, 1, 0.5);
863
+ }
864
+ }
865
+ async function collectConfidencePropagationDispatches(args) {
866
+ const dispatchesByTargetId = /* @__PURE__ */ new Map();
867
+ const opinionCache = /* @__PURE__ */ new Map();
868
+ const nodeCache = /* @__PURE__ */ new Map();
869
+ const traversalSpecs = args.traversalSpecs ?? getEdgePropagationSpecs();
870
+ const queue = [
871
+ {
872
+ nodeId: args.sourceNodeId,
873
+ opinion: args.sourceOpinion,
874
+ hop: 0,
875
+ visitedNodeIds: /* @__PURE__ */ new Set([String(args.sourceNodeId)])
876
+ }
877
+ ];
878
+ const loadNode = async (nodeId) => {
879
+ const cacheKey = String(nodeId);
880
+ if (!nodeCache.has(cacheKey)) {
881
+ nodeCache.set(cacheKey, await args.getNode(nodeId));
882
+ }
883
+ return nodeCache.get(cacheKey) ?? null;
884
+ };
885
+ while (queue.length > 0) {
886
+ const state = queue.shift();
887
+ if (!state) {
888
+ continue;
889
+ }
890
+ for (const spec of traversalSpecs) {
891
+ const nextHop = state.hop + 1;
892
+ if (!canTraverseHop(spec, nextHop)) {
893
+ continue;
894
+ }
895
+ for (const direction of getTraversalDirections(spec.direction)) {
896
+ const edges = await args.queryEdges({
897
+ nodeId: state.nodeId,
898
+ spec,
899
+ direction,
900
+ hop: nextHop
901
+ });
902
+ for (const edge of edges) {
903
+ if (args.sourceScope && !edgeMatchesWorkspaceReasoningScope(edge, args.sourceScope)) {
904
+ continue;
905
+ }
906
+ const targetNodeId = resolveTraversalTargetNodeId(edge, direction);
907
+ if (!targetNodeId) {
908
+ continue;
909
+ }
910
+ if (state.visitedNodeIds.has(String(targetNodeId))) {
911
+ continue;
912
+ }
913
+ const targetNode = await loadNode(targetNodeId);
914
+ if (!targetNode || targetNode.nodeType !== "belief") {
915
+ continue;
916
+ }
917
+ if (args.sourceScope && !nodeMatchesWorkspaceReasoningScope(targetNode, args.sourceScope)) {
918
+ continue;
919
+ }
920
+ const cacheKey = String(targetNodeId);
921
+ const targetOpinion = opinionCache.get(cacheKey) ?? readNodeOpinion(targetNode);
922
+ const result = spec.operator(state.opinion, targetOpinion, edge, {
923
+ hop: nextHop,
924
+ sourceNodeId: state.nodeId,
925
+ targetNodeId,
926
+ traversedDirection: direction,
927
+ spec
928
+ });
929
+ if (!result || !hasProjectedOpinionChanged(targetOpinion, result.opinion)) {
930
+ continue;
931
+ }
932
+ const projectedOpinion = mkOpinion(
933
+ result.opinion.b,
934
+ result.opinion.d,
935
+ result.opinion.u,
936
+ result.opinion.a
937
+ );
938
+ opinionCache.set(cacheKey, projectedOpinion);
939
+ const existingDispatch = dispatchesByTargetId.get(cacheKey);
940
+ dispatchesByTargetId.set(cacheKey, {
941
+ targetNodeId,
942
+ edgeType: spec.edgeType,
943
+ traversedDirection: direction,
944
+ weight: edge.weight ?? 1,
945
+ opinion: projectedOpinion,
946
+ operator: result.operator,
947
+ rationale: existingDispatch ? `${existingDispatch.rationale}; ${result.rationale}` : result.rationale,
948
+ hop: nextHop
949
+ });
950
+ if (canContinueTransitively(spec, nextHop)) {
951
+ queue.push({
952
+ nodeId: targetNodeId,
953
+ opinion: projectedOpinion,
954
+ hop: nextHop,
955
+ visitedNodeIds: /* @__PURE__ */ new Set([
956
+ ...state.visitedNodeIds,
957
+ String(targetNodeId)
958
+ ])
959
+ });
960
+ }
961
+ }
962
+ }
963
+ }
964
+ }
965
+ return Array.from(dispatchesByTargetId.values()).sort((left, right) => {
966
+ if (left.hop !== right.hop) {
967
+ return left.hop - right.hop;
968
+ }
969
+ return String(left.targetNodeId).localeCompare(String(right.targetNodeId));
970
+ });
971
+ }
972
+
973
+ // src/epistemicBeliefs.confidence.ts
974
+ async function applyBeliefConfidenceChange(ctx, args) {
975
+ const now = Date.now();
976
+ const node = await ctx.db.get(args.nodeId);
977
+ if (!node) {
978
+ throwStructuredMutationError({
979
+ message: "Node not found.",
980
+ status: 404,
981
+ code: "NOT_FOUND",
982
+ invariantCode: "belief.exists",
983
+ suggestion: "Verify nodeId points to an existing node before modulating confidence.",
984
+ details: { nodeId: args.nodeId }
985
+ });
986
+ }
987
+ if (node.nodeType !== "belief") {
988
+ throwStructuredMutationError({
989
+ message: `modulateConfidence only applies to belief nodes. Received nodeType "${node.nodeType}". Entity nodes (company, person, investor, etc.) do not have confidence \u2014 use entityLifecycle.updateEntityAttributes for mutable entity data.`,
990
+ status: 400,
991
+ code: "INVALID_ARGUMENT",
992
+ invariantCode: "entity.no_confidence",
993
+ suggestion: "Use entityLifecycle.updateEntityAttributes for entity mutations. modulateConfidence is for belief nodes only.",
994
+ details: { nodeId: args.nodeId, nodeType: node.nodeType }
995
+ });
996
+ }
997
+ if (!node.projectId) {
998
+ throwStructuredMutationError({
999
+ message: "Belief has no project scope.",
1000
+ status: 400,
1001
+ code: "MISSING_SCOPE",
1002
+ invariantCode: "belief.project_required",
1003
+ suggestion: "Belief must have a projectId to modulate confidence.",
1004
+ details: { nodeId: args.nodeId }
1005
+ });
1006
+ }
1007
+ await requireProjectWriteAccess(
1008
+ ctx,
1009
+ node.projectId,
1010
+ args.authenticatedUserId
1011
+ );
1012
+ const existingMetadata = node.metadata || {};
1013
+ const currentBeliefStatus = resolveBeliefStatus(node, existingMetadata);
1014
+ const confidencePolicy = await getActiveConfidencePolicy(ctx);
1015
+ if (confidencePolicy.scoringMode === "after_worktree" && isPreValidationBeliefStatus(currentBeliefStatus)) {
1016
+ const hasCompletedWorktree = await hasCompletedWorktreeForBelief(
1017
+ ctx,
1018
+ args.nodeId
1019
+ );
1020
+ if (!hasCompletedWorktree) {
1021
+ throwStructuredMutationError({
1022
+ message: "Cannot score belief before worktree completion. Complete a worktree that tests this belief first.",
1023
+ status: 409,
1024
+ code: "CONFLICT",
1025
+ invariantCode: "belief.confidence_append_only",
1026
+ suggestion: "Complete a worktree linked to this belief before recording confidence modulation.",
1027
+ details: { nodeId: args.nodeId }
1028
+ });
1029
+ }
1030
+ }
1031
+ const previousConfidence = node.confidence || 0.5;
1032
+ const predictionMeta = node.predictionMeta || existingMetadata.predictionMeta;
1033
+ const previousOpinion = readBeliefOpinionSnapshot(node, existingMetadata);
1034
+ const slB = args.belief;
1035
+ const slD = args.disbelief;
1036
+ const slU = args.uncertainty;
1037
+ const slA = args.baseRate ?? 0.5;
1038
+ const nextOpinion = { b: slB, d: slD, u: slU, a: slA };
1039
+ const derivedConfidence = confidenceFromSL(slB, slD, slU, slA);
1040
+ const isFirstScoring = typeof node.confidence !== "number" || !Number.isFinite(node.confidence);
1041
+ const previousTupleContradicted = readTupleContradictedFlag(node.tupleContradicted) ?? readTupleContradictedFlag(existingMetadata.tupleContradicted) ?? detectTupleContradiction(
1042
+ previousOpinion,
1043
+ confidencePolicy.tupleContradiction.beliefThreshold,
1044
+ confidencePolicy.tupleContradiction.disbeliefThreshold
1045
+ );
1046
+ const tupleTransition = evaluateTupleContradictionTransition({
1047
+ previousTupleContradicted,
1048
+ opinion: nextOpinion,
1049
+ policy: confidencePolicy.tupleContradiction
1050
+ });
1051
+ const tupleContradictionDescription = formatTupleContradictionDescription({
1052
+ opinion: nextOpinion,
1053
+ policy: tupleTransition.policy
1054
+ });
1055
+ const newBeliefStatus = promoteBeliefStatusAfterScoring(currentBeliefStatus, {
1056
+ confidence: derivedConfidence,
1057
+ predictionMeta,
1058
+ metadata: existingMetadata
1059
+ });
1060
+ let tupleContradictionId;
1061
+ if (tupleTransition.crossedIntoTupleContradiction) {
1062
+ tupleContradictionId = await ctx.runMutation(
1063
+ "contradictions:create",
1064
+ {
1065
+ projectId: node.projectId,
1066
+ topicId: node.topicId,
1067
+ beliefId: args.nodeId,
1068
+ beliefBId: args.nodeId,
1069
+ supportingInsightIds: [],
1070
+ contradictingInsightIds: [],
1071
+ severity: deriveTupleContradictionSeverity(node),
1072
+ source: "tuple_space",
1073
+ detectionMethod: "agent",
1074
+ description: tupleContradictionDescription,
1075
+ createdBy: args.authenticatedUserId
1076
+ }
1077
+ );
1078
+ }
1079
+ await ctx.db.patch(args.nodeId, {
1080
+ confidence: derivedConfidence,
1081
+ beliefStatus: newBeliefStatus,
1082
+ tupleContradicted: tupleTransition.tupleContradicted,
1083
+ updatedAt: now,
1084
+ // Store SL opinion fields at node level for fast access
1085
+ opinion_b: slB,
1086
+ opinion_d: slD,
1087
+ opinion_u: slU,
1088
+ opinion_a: slA,
1089
+ metadata: {
1090
+ ...existingMetadata,
1091
+ beliefStatus: newBeliefStatus,
1092
+ slBelief: slB,
1093
+ slDisbelief: slD,
1094
+ slUncertainty: slU,
1095
+ slBaseRate: slA,
1096
+ tupleContradicted: tupleTransition.tupleContradicted
1097
+ }
1098
+ });
1099
+ if (isFirstScoring) {
1100
+ const nodeTopicId = node.topicId;
1101
+ const themeNodes = await ctx.db.query("epistemicNodes").withIndex(
1102
+ "by_topic",
1103
+ (q) => q.eq("topicId", nodeTopicId || node.projectId)
1104
+ ).filter((q) => q.eq(q.field("nodeType"), "theme")).collect();
1105
+ for (const theme of themeNodes) {
1106
+ if (theme.globalId && node.globalId) {
1107
+ await ctx.scheduler.runAfter(0, internal.neo4jEdgeAPI.createEdge, {
1108
+ globalId: `edge-${node.globalId}-relates_to_thesis-${theme.globalId}`,
1109
+ fromGlobalId: node.globalId,
1110
+ toGlobalId: theme.globalId,
1111
+ edgeType: "relates_to_thesis",
1112
+ weight: derivedConfidence,
1113
+ createdBy: args.authenticatedUserId,
1114
+ topicId: String(node.projectId),
1115
+ fromNodeType: "belief",
1116
+ toNodeType: "theme",
1117
+ fromLayer: "L3",
1118
+ toLayer: "L3"
1119
+ });
1120
+ }
1121
+ }
1122
+ }
1123
+ const storedRationale = args.rationale ?? `Confidence changed from ${previousConfidence.toFixed(2)} (nodeId: ${args.nodeId})`;
1124
+ const beliefConfidenceId = await ctx.db.insert("beliefConfidence", {
1125
+ ...buildBeliefConfidenceRow({
1126
+ beliefId: args.nodeId,
1127
+ belief: slB,
1128
+ disbelief: slD,
1129
+ uncertainty: slU,
1130
+ baseRate: slA,
1131
+ trigger: args.trigger,
1132
+ rationale: storedRationale,
1133
+ assessedBy: args.authenticatedUserId,
1134
+ assessedAt: now,
1135
+ slOperator: args.slOperator,
1136
+ triggeringEvidenceId: args.triggeringEvidenceId,
1137
+ triggeringContradictionId: tupleContradictionId,
1138
+ triggeringWorktreeId: args.triggeringWorktreeId
1139
+ })
1140
+ });
1141
+ await ctx.scheduler.runAfter(0, internal.neo4jSync.syncNodeToNeo4j, {
1142
+ nodeId: args.nodeId,
1143
+ operation: "upsert"
1144
+ });
1145
+ await ctx.db.insert("epistemicAudit", {
1146
+ entityType: "belief",
1147
+ entityId: args.nodeId,
1148
+ changeType: "confidence_changed",
1149
+ previousState: {
1150
+ confidence: previousConfidence,
1151
+ tupleContradicted: previousTupleContradicted
1152
+ },
1153
+ newState: {
1154
+ opinion: nextOpinion,
1155
+ confidence: derivedConfidence,
1156
+ trigger: args.trigger,
1157
+ rationale: storedRationale,
1158
+ tupleContradicted: tupleTransition.tupleContradicted,
1159
+ tupleContradictionPolicy: tupleTransition.policy,
1160
+ ...tupleContradictionId ? { tupleContradictionId: String(tupleContradictionId) } : {}
1161
+ },
1162
+ changedBy: args.authenticatedUserId,
1163
+ isAgent: false,
1164
+ changedAt: now,
1165
+ projectId: node.projectId,
1166
+ topicId: node.topicId
1167
+ });
1168
+ if (tupleTransition.crossedIntoTupleContradiction || tupleTransition.crossedOutOfTupleContradiction) {
1169
+ await ctx.db.insert("epistemicAudit", {
1170
+ entityType: "belief",
1171
+ entityId: args.nodeId,
1172
+ changeType: "updated",
1173
+ previousState: { tupleContradicted: previousTupleContradicted },
1174
+ newState: {
1175
+ tupleContradicted: tupleTransition.tupleContradicted,
1176
+ action: tupleTransition.crossedIntoTupleContradiction ? "tuple_contradiction_detected" : "tuple_contradiction_cleared",
1177
+ opinion: nextOpinion,
1178
+ tupleContradictionPolicy: tupleTransition.policy,
1179
+ ...tupleContradictionId ? { tupleContradictionId: String(tupleContradictionId) } : {}
1180
+ },
1181
+ rationale: tupleTransition.crossedIntoTupleContradiction ? tupleContradictionDescription : `Tuple-space contradiction cleared: b=${nextOpinion.b.toFixed(2)}, d=${nextOpinion.d.toFixed(2)} no longer exceed the configured policy thresholds.`,
1182
+ changedBy: args.authenticatedUserId,
1183
+ isAgent: false,
1184
+ changedAt: now,
1185
+ projectId: node.projectId,
1186
+ topicId: node.topicId
1187
+ });
1188
+ }
1189
+ if (Math.abs(derivedConfidence - previousConfidence) >= 0.15) {
1190
+ await ctx.scheduler.runAfter(
1191
+ 5e3,
1192
+ internal.bi.contradictionSemanticDetector.scanAffectedBeliefs,
1193
+ {
1194
+ beliefId: args.nodeId,
1195
+ projectId: node.projectId
1196
+ }
1197
+ );
1198
+ }
1199
+ if (node.workspaceId && node.tenantId) {
1200
+ await ctx.scheduler.runAfter(
1201
+ 0,
1202
+ internal.publication.evaluateNodePublication,
1203
+ { nodeId: args.nodeId }
1204
+ );
1205
+ }
1206
+ return {
1207
+ nodeId: args.nodeId,
1208
+ previousConfidence,
1209
+ newConfidence: derivedConfidence,
1210
+ opinion: { b: slB, d: slD, u: slU, a: slA },
1211
+ beliefConfidenceId
1212
+ };
1213
+ }
1214
+ function propagationTriggerForEdge(edgeType, weight) {
1215
+ if (edgeType === "contradicts" || edgeType === "refutes") {
1216
+ return "contradiction_detected";
1217
+ }
1218
+ if ((edgeType === "supports" || edgeType === "informs") && weight < 0) {
1219
+ return "contradiction_detected";
1220
+ }
1221
+ return "evidence_added";
1222
+ }
1223
+ internalMutation({
1224
+ args: {
1225
+ nodeId: v.id("epistemicNodes"),
1226
+ opinion_b: v.number(),
1227
+ opinion_d: v.number(),
1228
+ opinion_u: v.number(),
1229
+ opinion_a: v.number(),
1230
+ userId: v.string()
1231
+ },
1232
+ returns: permissiveReturn,
1233
+ handler: async (ctx, args) => {
1234
+ const sourceOpinion = mkOpinion(
1235
+ args.opinion_b,
1236
+ args.opinion_d,
1237
+ args.opinion_u,
1238
+ args.opinion_a
1239
+ );
1240
+ const sourceNode = await ctx.db.get(args.nodeId);
1241
+ const sourceScope = await resolveNodeScopeForWorkspaceIsolation(
1242
+ ctx,
1243
+ sourceNode
1244
+ );
1245
+ const dispatches = await collectConfidencePropagationDispatches({
1246
+ sourceNodeId: args.nodeId,
1247
+ sourceOpinion,
1248
+ sourceScope,
1249
+ queryEdges: async ({ nodeId, spec, direction }) => {
1250
+ return await ctx.db.query("epistemicEdges").withIndex(
1251
+ direction === "outgoing" ? "by_from_type" : "by_to_type",
1252
+ (q) => direction === "outgoing" ? q.eq("fromNodeId", nodeId).eq("edgeType", spec.edgeType) : q.eq("toNodeId", nodeId).eq("edgeType", spec.edgeType)
1253
+ ).collect();
1254
+ },
1255
+ getNode: async (nodeId) => await ctx.db.get(nodeId)
1256
+ });
1257
+ for (const dispatch of dispatches) {
1258
+ await applyBeliefConfidenceChange(ctx, {
1259
+ nodeId: dispatch.targetNodeId,
1260
+ belief: dispatch.opinion.b,
1261
+ disbelief: dispatch.opinion.d,
1262
+ uncertainty: dispatch.opinion.u,
1263
+ baseRate: dispatch.opinion.a,
1264
+ trigger: propagationTriggerForEdge(dispatch.edgeType, dispatch.weight),
1265
+ rationale: `SL propagation via ${dispatch.edgeType} edge: ${dispatch.rationale}`,
1266
+ authenticatedUserId: args.userId,
1267
+ slOperator: dispatch.operator
1268
+ });
1269
+ }
1270
+ return {
1271
+ propagated: dispatches.map((dispatch) => ({
1272
+ targetNodeId: String(dispatch.targetNodeId),
1273
+ edgeType: dispatch.edgeType,
1274
+ operator: dispatch.operator
1275
+ })),
1276
+ count: dispatches.length
1277
+ };
1278
+ }
1279
+ });
1280
+
1281
+ // src/epistemicContracts.metrics.ts
1282
+ var ACTIVE_CONTRADICTION_STATUSES = /* @__PURE__ */ new Set([
1283
+ "unresolved",
1284
+ "investigating",
1285
+ "accepted_as_permanent"
1286
+ ]);
1287
+ var DEPENDENT_EDGE_TYPES = /* @__PURE__ */ new Set([
1288
+ "depends_on"
1289
+ ]);
1290
+ function classifyContradictionStatus(status) {
1291
+ if (typeof status !== "string") {
1292
+ return "active";
1293
+ }
1294
+ if (ACTIVE_CONTRADICTION_STATUSES.has(status)) {
1295
+ return "active";
1296
+ }
1297
+ if (status === "resolved_support" || status === "resolved_contra" || status === "belief_forked") {
1298
+ return "resolved";
1299
+ }
1300
+ return "resolved";
1301
+ }
1302
+ function getEdgeTimestamp(edge) {
1303
+ if (typeof edge.updatedAt === "number") {
1304
+ return edge.updatedAt;
1305
+ }
1306
+ if (typeof edge.createdAt === "number") {
1307
+ return edge.createdAt;
1308
+ }
1309
+ if (typeof edge._creationTime === "number") {
1310
+ return edge._creationTime;
1311
+ }
1312
+ return null;
1313
+ }
1314
+ async function getEvidenceLinks(ctx, beliefNodeId) {
1315
+ const edges = await ctx.db.query("epistemicEdges").withIndex(
1316
+ "by_to_type",
1317
+ (q) => q.eq("toNodeId", beliefNodeId).eq("edgeType", "informs")
1318
+ ).collect();
1319
+ if (edges.length === 0) {
1320
+ return [];
1321
+ }
1322
+ const nodes = await Promise.all(edges.map((edge) => ctx.db.get(edge.fromNodeId)));
1323
+ return edges.flatMap((edge, index) => {
1324
+ const node = nodes[index];
1325
+ if (!node || node.nodeType !== "evidence" || node.status === "archived") {
1326
+ return [];
1327
+ }
1328
+ return [{ edge, node }];
1329
+ });
1330
+ }
1331
+ function getEvidenceTags(node) {
1332
+ const metadata = node.metadata && typeof node.metadata === "object" ? node.metadata : null;
1333
+ const tags = metadata?.tags;
1334
+ if (!Array.isArray(tags)) {
1335
+ return [];
1336
+ }
1337
+ return tags.filter((tag) => typeof tag === "string");
1338
+ }
1339
+ async function computeEvidenceCountMetric(ctx, beliefNodeId) {
1340
+ return (await getEvidenceLinks(ctx, beliefNodeId)).length;
1341
+ }
1342
+ async function computeTaggedEvidenceCount(args) {
1343
+ const expectedTag = args.caseSensitive ? args.tag : args.tag.toLowerCase();
1344
+ const matchedEvidenceIds = (await getEvidenceLinks(args.ctx, args.beliefNodeId)).filter(
1345
+ ({ node }) => getEvidenceTags(node).some(
1346
+ (tag) => (args.caseSensitive ? tag : tag.toLowerCase()) === expectedTag
1347
+ )
1348
+ ).map(({ node }) => String(node._id));
1349
+ return {
1350
+ count: matchedEvidenceIds.length,
1351
+ matchedEvidenceIds
1352
+ };
1353
+ }
1354
+ async function computeContradictionCounts(ctx, beliefNodeId) {
1355
+ const contradictions = await ctx.db.query("contradictions").withIndex("by_beliefId", (q) => q.eq("beliefId", beliefNodeId)).collect();
1356
+ return contradictions.reduce(
1357
+ (counts, contradiction) => {
1358
+ const status = contradiction.resolutionStatus ?? contradiction.status ?? "unresolved";
1359
+ if (classifyContradictionStatus(status) === "active") {
1360
+ counts.activeCount += 1;
1361
+ } else {
1362
+ counts.resolvedCount += 1;
1363
+ }
1364
+ return counts;
1365
+ },
1366
+ { activeCount: 0, resolvedCount: 0 }
1367
+ );
1368
+ }
1369
+ async function computeEvidenceFreshness(ctx, beliefNodeId, now = Date.now()) {
1370
+ const timestamps = (await getEvidenceLinks(ctx, beliefNodeId)).map(({ edge }) => getEdgeTimestamp(edge)).filter((value) => value !== null);
1371
+ if (timestamps.length === 0) {
1372
+ return {
1373
+ newestAgeMs: null,
1374
+ oldestAgeMs: null,
1375
+ newestEdgeAt: null,
1376
+ oldestEdgeAt: null,
1377
+ edgeCount: 0
1378
+ };
1379
+ }
1380
+ const newestEdgeAt = Math.max(...timestamps);
1381
+ const oldestEdgeAt = Math.min(...timestamps);
1382
+ return {
1383
+ newestAgeMs: Math.max(0, now - newestEdgeAt),
1384
+ oldestAgeMs: Math.max(0, now - oldestEdgeAt),
1385
+ newestEdgeAt,
1386
+ oldestEdgeAt,
1387
+ edgeCount: timestamps.length
1388
+ };
1389
+ }
1390
+ async function computeDependentBeliefCount(ctx, beliefNodeId) {
1391
+ const incomingEdges = await ctx.db.query("epistemicEdges").withIndex("by_to", (q) => q.eq("toNodeId", beliefNodeId)).collect();
1392
+ const dependencyEdges = incomingEdges.filter(
1393
+ (edge) => DEPENDENT_EDGE_TYPES.has(edge.edgeType)
1394
+ );
1395
+ if (dependencyEdges.length === 0) {
1396
+ return 0;
1397
+ }
1398
+ const dependentBeliefs = await Promise.all(
1399
+ dependencyEdges.map((edge) => ctx.db.get(edge.fromNodeId))
1400
+ );
1401
+ const uniqueBeliefIds = /* @__PURE__ */ new Set();
1402
+ for (const node of dependentBeliefs) {
1403
+ if (node && node.nodeType === "belief" && node.status !== "archived" && node.status !== "deleted") {
1404
+ uniqueBeliefIds.add(String(node._id));
1405
+ }
1406
+ }
1407
+ return uniqueBeliefIds.size;
1408
+ }
1409
+ async function snapshotEvidentialMetric(args) {
1410
+ switch (args.metric) {
1411
+ case "evidence_count": {
1412
+ const count = await computeEvidenceCountMetric(args.ctx, args.beliefNodeId);
1413
+ return {
1414
+ metric: args.metric,
1415
+ value: count,
1416
+ data: { evidenceCount: count }
1417
+ };
1418
+ }
1419
+ case "contradiction_status": {
1420
+ const counts = await computeContradictionCounts(args.ctx, args.beliefNodeId);
1421
+ return {
1422
+ metric: args.metric,
1423
+ value: counts.activeCount,
1424
+ data: counts
1425
+ };
1426
+ }
1427
+ case "edge_freshness": {
1428
+ const freshness = await computeEvidenceFreshness(
1429
+ args.ctx,
1430
+ args.beliefNodeId,
1431
+ args.now
1432
+ );
1433
+ return {
1434
+ metric: args.metric,
1435
+ value: freshness.newestAgeMs,
1436
+ data: freshness
1437
+ };
1438
+ }
1439
+ case "dependent_count": {
1440
+ const count = await computeDependentBeliefCount(args.ctx, args.beliefNodeId);
1441
+ return {
1442
+ metric: args.metric,
1443
+ value: count,
1444
+ data: { dependentCount: count }
1445
+ };
1446
+ }
1447
+ default:
1448
+ return {
1449
+ metric: args.metric,
1450
+ value: null,
1451
+ data: {}
1452
+ };
1453
+ }
1454
+ }
1455
+ async function evaluateBuiltInEvidentialContract(args) {
1456
+ const config = parseEvidentialEvaluatorConfig(args.contract.condition.evaluatorConfig);
1457
+ const snapshot = await snapshotEvidentialMetric({
1458
+ ctx: args.ctx,
1459
+ beliefNodeId: args.belief._id,
1460
+ metric: config.metric,
1461
+ now: args.now
1462
+ });
1463
+ const comparisonSatisfied = snapshot.value !== null && compareMetricValue(config.operator, snapshot.value, config.threshold);
1464
+ const result = args.contract.direction === "falsifies" ? comparisonSatisfied ? "disconfirmed" : "confirmed" : comparisonSatisfied ? "confirmed" : "disconfirmed";
1465
+ return {
1466
+ result,
1467
+ rationale: buildEvidentialRationale({
1468
+ config,
1469
+ snapshot,
1470
+ comparisonSatisfied,
1471
+ result
1472
+ }),
1473
+ data: {
1474
+ ...snapshot.data,
1475
+ metric: config.metric,
1476
+ observedValue: snapshot.value,
1477
+ operator: config.operator,
1478
+ threshold: config.threshold,
1479
+ action: config.action ?? "modulate_confidence",
1480
+ actionParams: config.actionParams
1481
+ }
1482
+ };
1483
+ }
1484
+ async function evaluateMetricCheckerContract(args) {
1485
+ const config = parseMetricCheckerConfig(args.contract.condition.evaluatorConfig);
1486
+ const input = getEvaluatorInputRecord(args.inputData, "metricData");
1487
+ const metric = typeof input.metric === "string" && input.metric.length > 0 ? input.metric : config.metric;
1488
+ const observedValue = pickFiniteNumber(input, [
1489
+ "observedValue",
1490
+ "currentValue",
1491
+ "metricValue",
1492
+ "value"
1493
+ ]) ?? config.observedValue ?? config.currentValue ?? config.metricValue ?? null;
1494
+ if (observedValue === null) {
1495
+ return {
1496
+ result: "inconclusive",
1497
+ rationale: `metric_checker is awaiting data for ${metric ?? args.contract.condition.expression}.`,
1498
+ data: {
1499
+ metric,
1500
+ observedValue: null,
1501
+ operator: config.operator,
1502
+ threshold: config.threshold,
1503
+ unit: config.unit
1504
+ }
1505
+ };
1506
+ }
1507
+ const comparisonSatisfied = compareMetricValue(
1508
+ config.operator,
1509
+ observedValue,
1510
+ config.threshold
1511
+ );
1512
+ const result = resolveComparisonResult(args.contract.direction, comparisonSatisfied);
1513
+ return {
1514
+ result,
1515
+ rationale: buildComparisonRationale({
1516
+ label: metric ?? "metric",
1517
+ observedValue,
1518
+ operator: config.operator,
1519
+ threshold: config.threshold,
1520
+ comparisonSatisfied,
1521
+ result,
1522
+ unit: config.unit
1523
+ }),
1524
+ data: {
1525
+ metric,
1526
+ observedValue,
1527
+ operator: config.operator,
1528
+ threshold: config.threshold,
1529
+ unit: config.unit
1530
+ }
1531
+ };
1532
+ }
1533
+ async function evaluateReferenceCheckCounterContract(args) {
1534
+ const config = parseReferenceCheckCounterConfig(args.contract.condition.evaluatorConfig);
1535
+ const input = getEvaluatorInputRecord(args.inputData, "referenceCheckData");
1536
+ const tag = typeof input.tag === "string" && input.tag.trim().length > 0 ? input.tag.trim() : config.tag;
1537
+ const snapshot = await computeTaggedEvidenceCount({
1538
+ ctx: args.ctx,
1539
+ beliefNodeId: args.belief._id,
1540
+ tag,
1541
+ caseSensitive: config.caseSensitive
1542
+ });
1543
+ const comparisonSatisfied = compareMetricValue(
1544
+ config.operator,
1545
+ snapshot.count,
1546
+ config.threshold
1547
+ );
1548
+ const result = resolveComparisonResult(args.contract.direction, comparisonSatisfied);
1549
+ return {
1550
+ result,
1551
+ rationale: buildComparisonRationale({
1552
+ label: `reference checks tagged "${tag}"`,
1553
+ observedValue: snapshot.count,
1554
+ operator: config.operator,
1555
+ threshold: config.threshold,
1556
+ comparisonSatisfied,
1557
+ result
1558
+ }),
1559
+ data: {
1560
+ tag,
1561
+ observedValue: snapshot.count,
1562
+ referenceCheckCount: snapshot.count,
1563
+ matchedEvidenceIds: snapshot.matchedEvidenceIds,
1564
+ operator: config.operator,
1565
+ threshold: config.threshold,
1566
+ caseSensitive: config.caseSensitive ?? false
1567
+ }
1568
+ };
1569
+ }
1570
+ async function evaluateTemporalDeadlineContract(args) {
1571
+ if (typeof args.contract.deadline !== "number" || !Number.isFinite(args.contract.deadline)) {
1572
+ throw new Error(
1573
+ "temporal_deadline requires contract.deadline to be set to a finite timestamp."
1574
+ );
1575
+ }
1576
+ const config = parseTemporalDeadlineConfig(args.contract.condition.evaluatorConfig);
1577
+ const input = getEvaluatorInputRecord(args.inputData, "temporalData");
1578
+ const label = (typeof input.label === "string" && input.label.length > 0 ? input.label : config.label) ?? args.contract.title ?? args.contract.condition.expression;
1579
+ const completedAt = pickFiniteNumber(input, [
1580
+ "completedAt",
1581
+ "observedAt",
1582
+ "satisfiedAt",
1583
+ "achievedAt"
1584
+ ]) ?? config.completedAt ?? config.observedAt ?? config.satisfiedAt ?? config.achievedAt;
1585
+ const completed = input.completed === true || config.completed === true || completedAt !== void 0;
1586
+ if (completed) {
1587
+ if (completedAt !== void 0 && completedAt > args.contract.deadline) {
1588
+ return {
1589
+ result: "expired",
1590
+ rationale: `${label} completed at ${completedAt}, after deadline ${args.contract.deadline}.`,
1591
+ data: {
1592
+ label,
1593
+ deadline: args.contract.deadline,
1594
+ completed: true,
1595
+ completedAt,
1596
+ missedDeadline: true,
1597
+ overdueByMs: completedAt - args.contract.deadline
1598
+ }
1599
+ };
1600
+ }
1601
+ const result = args.contract.direction === "falsifies" ? "disconfirmed" : "confirmed";
1602
+ return {
1603
+ result,
1604
+ rationale: `${label} completed before deadline ${args.contract.deadline}.`,
1605
+ data: {
1606
+ label,
1607
+ deadline: args.contract.deadline,
1608
+ completed: true,
1609
+ completedAt: completedAt ?? null,
1610
+ missedDeadline: false
1611
+ }
1612
+ };
1613
+ }
1614
+ if (args.now > args.contract.deadline) {
1615
+ return {
1616
+ result: "expired",
1617
+ rationale: `${label} missed deadline ${args.contract.deadline}; temporal contract expired.`,
1618
+ data: {
1619
+ label,
1620
+ deadline: args.contract.deadline,
1621
+ completed: false,
1622
+ overdueByMs: args.now - args.contract.deadline
1623
+ }
1624
+ };
1625
+ }
1626
+ return {
1627
+ result: "inconclusive",
1628
+ rationale: `${label} is still before deadline ${args.contract.deadline}; awaiting outcome.`,
1629
+ data: {
1630
+ label,
1631
+ deadline: args.contract.deadline,
1632
+ completed: false,
1633
+ timeRemainingMs: args.contract.deadline - args.now
1634
+ }
1635
+ };
1636
+ }
1637
+ async function evaluateMarketIndexComparatorContract(args) {
1638
+ const config = parseMarketIndexComparatorConfig(args.contract.condition.evaluatorConfig);
1639
+ const input = getEvaluatorInputRecord(args.inputData, "marketIndexData");
1640
+ const subject = typeof input.subject === "string" && input.subject.length > 0 ? input.subject : config.subject;
1641
+ const benchmark = typeof input.benchmark === "string" && input.benchmark.length > 0 ? input.benchmark : config.benchmark;
1642
+ const subjectValue = pickFiniteNumber(input, ["subjectValue", "primaryValue", "leftValue"]) ?? config.subjectValue ?? config.primaryValue ?? null;
1643
+ const benchmarkValue = pickFiniteNumber(input, ["benchmarkValue", "comparisonValue", "rightValue"]) ?? config.benchmarkValue ?? config.comparisonValue ?? null;
1644
+ if (subjectValue === null || benchmarkValue === null) {
1645
+ return {
1646
+ result: "inconclusive",
1647
+ rationale: "market_index_comparator is awaiting both subject and benchmark values.",
1648
+ data: {
1649
+ subject,
1650
+ subjectValue,
1651
+ benchmark,
1652
+ benchmarkValue,
1653
+ operator: config.operator,
1654
+ threshold: config.threshold
1655
+ }
1656
+ };
1657
+ }
1658
+ if (benchmarkValue === 0) {
1659
+ throw new Error(
1660
+ "market_index_comparator cannot compare against a zero benchmark value."
1661
+ );
1662
+ }
1663
+ const differentialPercent = (subjectValue - benchmarkValue) / Math.abs(benchmarkValue) * 100;
1664
+ const comparisonSatisfied = compareMetricValue(
1665
+ config.operator,
1666
+ differentialPercent,
1667
+ config.threshold
1668
+ );
1669
+ const result = resolveComparisonResult(args.contract.direction, comparisonSatisfied);
1670
+ return {
1671
+ result,
1672
+ rationale: buildComparisonRationale({
1673
+ label: `${subject ?? "subject"} vs ${benchmark ?? "benchmark"} differential`,
1674
+ observedValue: differentialPercent,
1675
+ operator: config.operator,
1676
+ threshold: config.threshold,
1677
+ comparisonSatisfied,
1678
+ result,
1679
+ unit: "%"
1680
+ }),
1681
+ data: {
1682
+ subject,
1683
+ subjectValue,
1684
+ benchmark,
1685
+ benchmarkValue,
1686
+ differentialPercent,
1687
+ operator: config.operator,
1688
+ threshold: config.threshold
1689
+ }
1690
+ };
1691
+ }
1692
+ var METRIC_COMPARATOR_EVALUATOR_NAMES = {
1693
+ evidentialAliases: /* @__PURE__ */ new Set(["evidential", "built_in_evidential", "builtin_evidential"]),
1694
+ metricChecker: "metric_checker",
1695
+ referenceCheckCounter: "reference_check_counter",
1696
+ temporalDeadline: "temporal_deadline",
1697
+ marketIndexComparator: "market_index_comparator"
1698
+ };
1699
+
1700
+ // src/evaluators/shared.ts
1701
+ function asArray(value) {
1702
+ return Array.isArray(value) ? value : [];
1703
+ }
1704
+ function asNumber(value) {
1705
+ return typeof value === "number" && Number.isFinite(value) ? value : null;
1706
+ }
1707
+ function asRecord(value) {
1708
+ return value && typeof value === "object" && !Array.isArray(value) ? value : null;
1709
+ }
1710
+ function asString(value) {
1711
+ return typeof value === "string" && value.trim().length > 0 ? value.trim() : null;
1712
+ }
1713
+ function deriveDirectionalResult(direction, conditionSatisfied) {
1714
+ if (direction === "falsifies") {
1715
+ return conditionSatisfied ? "disconfirmed" : "confirmed";
1716
+ }
1717
+ return conditionSatisfied ? "confirmed" : "disconfirmed";
1718
+ }
1719
+ function extractTextCandidates(value) {
1720
+ const record = asRecord(value);
1721
+ if (!record) {
1722
+ const candidate = asString(value);
1723
+ return candidate ? [candidate] : [];
1724
+ }
1725
+ const candidates = [
1726
+ record.stdout,
1727
+ record.stderr,
1728
+ record.output,
1729
+ record.text,
1730
+ record.message,
1731
+ record.raw
1732
+ ].map(asString).filter((candidate) => Boolean(candidate));
1733
+ for (const nestedKey of ["report", "result", "data", "payload"]) {
1734
+ const nested = record[nestedKey];
1735
+ if (nested !== value) {
1736
+ candidates.push(...extractTextCandidates(nested));
1737
+ }
1738
+ }
1739
+ return Array.from(new Set(candidates));
1740
+ }
1741
+ function normalizeFilePath(value) {
1742
+ return value.replace(/\\/g, "/").replace(/^\.\//, "");
1743
+ }
1744
+ function normalizeToolResultEnvelope(value) {
1745
+ const record = asRecord(value);
1746
+ if (!record) {
1747
+ return {
1748
+ output: asString(value) ?? void 0
1749
+ };
1750
+ }
1751
+ const exitCode = asNumber(record.exitCode) ?? asNumber(record.code) ?? asNumber(record.status) ?? null;
1752
+ return {
1753
+ command: asString(record.command) ?? void 0,
1754
+ data: record.data ?? record.payload,
1755
+ exitCode,
1756
+ output: asString(record.output) ?? void 0,
1757
+ report: record.report ?? record.json ?? record.result ?? record.payload ?? void 0,
1758
+ stderr: asString(record.stderr) ?? void 0,
1759
+ stdout: asString(record.stdout) ?? void 0
1760
+ };
1761
+ }
1762
+ function parseJsonCandidate(value) {
1763
+ if (value === null || value === void 0) {
1764
+ return null;
1765
+ }
1766
+ if (typeof value !== "string") {
1767
+ return value;
1768
+ }
1769
+ try {
1770
+ return JSON.parse(value);
1771
+ } catch (error) {
1772
+ debugGraphPrimitiveFallback(
1773
+ "[evaluators/shared] Failed to parse JSON candidate",
1774
+ {
1775
+ error,
1776
+ valueType: typeof value
1777
+ }
1778
+ );
1779
+ return null;
1780
+ }
1781
+ }
1782
+ function patternMatchesPath(filePath, pattern) {
1783
+ const normalizedPath = normalizeFilePath(filePath);
1784
+ const normalizedPattern = normalizeFilePath(pattern);
1785
+ const escaped = normalizedPattern.replace(/[.+^${}()|[\]\\]/g, "\\$&");
1786
+ const regexPattern = escaped.replace(/\\\*\\\*/g, ".*").replace(/\\\*/g, "[^/]*").replace(/\\\?/g, "[^/]");
1787
+ return new RegExp(`^${regexPattern}$`).test(normalizedPath);
1788
+ }
1789
+ function somePatternMatches(filePath, patterns) {
1790
+ if (!filePath) {
1791
+ return false;
1792
+ }
1793
+ return patterns.some((pattern) => patternMatchesPath(filePath, pattern));
1794
+ }
1795
+
1796
+ // src/evaluators/lintCheckerEvaluator.ts
1797
+ function parseConfig(condition) {
1798
+ const record = asRecord(condition.evaluatorConfig);
1799
+ if (!record) {
1800
+ throw new Error(
1801
+ 'lint_checker requires evaluatorConfig with { filePatterns: string[], linter: "biome" }.'
1802
+ );
1803
+ }
1804
+ const filePatterns = asArray(record.filePatterns).map(asString).filter((pattern) => Boolean(pattern));
1805
+ if (filePatterns.length === 0) {
1806
+ throw new Error("lint_checker requires at least one file pattern.");
1807
+ }
1808
+ const linter = asString(record.linter);
1809
+ if (linter !== "biome") {
1810
+ throw new Error(`Unsupported linter: ${String(record.linter)}`);
1811
+ }
1812
+ return {
1813
+ filePatterns,
1814
+ linter
1815
+ };
1816
+ }
1817
+ function parseDiagnostics(resultData) {
1818
+ const envelope = normalizeToolResultEnvelope(resultData);
1819
+ const record = asRecord(envelope.report) ?? asRecord(envelope.data) ?? asRecord(resultData);
1820
+ if (!record) {
1821
+ return [];
1822
+ }
1823
+ return asArray(record.diagnostics).map((entry) => {
1824
+ const diagnostic = asRecord(entry);
1825
+ if (!diagnostic) {
1826
+ return null;
1827
+ }
1828
+ const location = asRecord(diagnostic.location);
1829
+ const pathRecord = asRecord(location?.path);
1830
+ const filePath = asString(diagnostic.filePath) ?? asString(diagnostic.file) ?? asString(location?.filePath) ?? asString(pathRecord?.file);
1831
+ const severity = asString(diagnostic.severity) ?? "error";
1832
+ const message = asString(diagnostic.message) ?? asString(diagnostic.description) ?? "Lint issue";
1833
+ return {
1834
+ filePath: filePath ? normalizeFilePath(filePath) : null,
1835
+ message,
1836
+ severity
1837
+ };
1838
+ }).filter(
1839
+ (diagnostic) => Boolean(diagnostic)
1840
+ );
1841
+ }
1842
+ function getMatchedDiagnostics(contract, resultData) {
1843
+ const config = parseConfig(contract.condition);
1844
+ return parseDiagnostics(resultData).filter(
1845
+ (diagnostic) => somePatternMatches(diagnostic.filePath, config.filePatterns)
1846
+ );
1847
+ }
1848
+ var lintCheckerEvaluator = {
1849
+ name: "lint_checker",
1850
+ matches({ contract, resultData }) {
1851
+ const envelope = normalizeToolResultEnvelope(resultData);
1852
+ const exitCode = asNumber(envelope.exitCode);
1853
+ if (exitCode === 0) {
1854
+ return true;
1855
+ }
1856
+ return getMatchedDiagnostics(contract, resultData).length > 0;
1857
+ },
1858
+ evaluate(args) {
1859
+ const config = parseConfig(args.contract.condition);
1860
+ if (!args.resultData) {
1861
+ return {
1862
+ result: "error",
1863
+ rationale: "lint_checker requires Biome lint resultData."
1864
+ };
1865
+ }
1866
+ const envelope = normalizeToolResultEnvelope(args.resultData);
1867
+ const exitCode = asNumber(envelope.exitCode);
1868
+ const matchedDiagnostics = getMatchedDiagnostics(args.contract, args.resultData);
1869
+ if (matchedDiagnostics.length === 0 && exitCode !== 0 && exitCode !== null) {
1870
+ return {
1871
+ result: "inconclusive",
1872
+ rationale: "Biome reported issues, but none matched the configured file patterns."
1873
+ };
1874
+ }
1875
+ const conditionSatisfied = exitCode === 0 || exitCode !== null && matchedDiagnostics.length === 0;
1876
+ return {
1877
+ result: deriveDirectionalResult(
1878
+ args.contract.direction,
1879
+ conditionSatisfied
1880
+ ),
1881
+ rationale: conditionSatisfied ? `Biome reported no matching lint diagnostics for ${config.filePatterns.join(", ")}.` : `Biome reported ${matchedDiagnostics.length} matching issue(s): ${matchedDiagnostics.map(
1882
+ (diagnostic) => diagnostic.filePath ? `${diagnostic.filePath} (${diagnostic.severity})` : diagnostic.message
1883
+ ).join(", ")}.`,
1884
+ data: {
1885
+ exitCode,
1886
+ filePatterns: config.filePatterns,
1887
+ linter: config.linter,
1888
+ matchedDiagnostics
1889
+ }
1890
+ };
1891
+ }
1892
+ };
1893
+
1894
+ // src/evaluators/sentryCheckerEvaluator.ts
1895
+ function parseConfig2(condition) {
1896
+ const record = asRecord(condition.evaluatorConfig);
1897
+ if (!record) {
1898
+ throw new Error(
1899
+ "sentry_checker requires evaluatorConfig with { module, windowDays }."
1900
+ );
1901
+ }
1902
+ const moduleName = asString(record.module);
1903
+ const windowDays = asNumber(record.windowDays);
1904
+ if (!moduleName || windowDays === null) {
1905
+ throw new Error(
1906
+ "sentry_checker requires a module name and numeric windowDays."
1907
+ );
1908
+ }
1909
+ return {
1910
+ module: moduleName,
1911
+ windowDays
1912
+ };
1913
+ }
1914
+ function parseIncidentCount(resultData) {
1915
+ const envelope = normalizeToolResultEnvelope(resultData);
1916
+ const record = asRecord(envelope.report) ?? asRecord(envelope.data) ?? asRecord(resultData);
1917
+ if (!record) {
1918
+ return {
1919
+ incidentCount: null,
1920
+ moduleName: null,
1921
+ windowDays: null
1922
+ };
1923
+ }
1924
+ const incidents = asArray(record.incidents);
1925
+ const incidentCount = asNumber(record.incidentCount) ?? (incidents.length > 0 ? incidents.length : null);
1926
+ return {
1927
+ incidentCount,
1928
+ moduleName: asString(record.module),
1929
+ windowDays: asNumber(record.windowDays)
1930
+ };
1931
+ }
1932
+ function matchesModule(contract, resultData) {
1933
+ const config = parseConfig2(contract.condition);
1934
+ const payload = parseIncidentCount(resultData);
1935
+ return !payload.moduleName || payload.moduleName === config.module;
1936
+ }
1937
+ var sentryCheckerEvaluator = {
1938
+ name: "sentry_checker",
1939
+ matches({ contract, resultData }) {
1940
+ return matchesModule(contract, resultData);
1941
+ },
1942
+ evaluate(args) {
1943
+ const config = parseConfig2(args.contract.condition);
1944
+ if (!args.resultData) {
1945
+ return {
1946
+ result: "error",
1947
+ rationale: "sentry_checker requires incident count resultData."
1948
+ };
1949
+ }
1950
+ const payload = parseIncidentCount(args.resultData);
1951
+ if (payload.incidentCount === null) {
1952
+ return {
1953
+ result: "error",
1954
+ rationale: "sentry_checker could not determine an incident count."
1955
+ };
1956
+ }
1957
+ if (!matchesModule(args.contract, args.resultData)) {
1958
+ return {
1959
+ result: "inconclusive",
1960
+ rationale: `Sentry result targeted ${payload.moduleName}, not ${config.module}.`
1961
+ };
1962
+ }
1963
+ const conditionSatisfied = payload.incidentCount === 0;
1964
+ return {
1965
+ result: deriveDirectionalResult(
1966
+ args.contract.direction,
1967
+ conditionSatisfied
1968
+ ),
1969
+ rationale: conditionSatisfied ? `Sentry reported zero incidents for ${config.module} over ${config.windowDays} day(s).` : `Sentry reported ${payload.incidentCount} incident(s) for ${config.module} over ${config.windowDays} day(s).`,
1970
+ data: {
1971
+ incidentCount: payload.incidentCount,
1972
+ module: config.module,
1973
+ windowDays: config.windowDays
1974
+ }
1975
+ };
1976
+ }
1977
+ };
1978
+
1979
+ // src/evaluators/testRunnerEvaluator.ts
1980
+ function parseConfig3(condition) {
1981
+ const record = asRecord(condition.evaluatorConfig);
1982
+ if (!record) {
1983
+ throw new Error(
1984
+ 'test_runner requires evaluatorConfig with { testPattern, runner: "vitest" }.'
1985
+ );
1986
+ }
1987
+ const testPattern = asString(record.testPattern);
1988
+ if (!testPattern) {
1989
+ throw new Error("test_runner requires a non-empty testPattern.");
1990
+ }
1991
+ const runner = asString(record.runner);
1992
+ if (runner !== "vitest") {
1993
+ throw new Error(`Unsupported test runner: ${String(record.runner)}`);
1994
+ }
1995
+ return {
1996
+ runner,
1997
+ testPattern
1998
+ };
1999
+ }
2000
+ function collectFailedTests(value) {
2001
+ const record = asRecord(value);
2002
+ if (!record) {
2003
+ return [];
2004
+ }
2005
+ const fullName = asString(record.fullName);
2006
+ const title = asString(record.title);
2007
+ const label = fullName ?? title ?? "unnamed test";
2008
+ const status = asString(record.status);
2009
+ const state = asString(asRecord(record.result)?.state);
2010
+ const isFailed = status === "failed" || status === "fail" || state === "failed" || state === "fail";
2011
+ const failures = isFailed ? [label] : [];
2012
+ for (const nestedKey of ["assertionResults", "tests", "tasks"]) {
2013
+ for (const nested of asArray(record[nestedKey])) {
2014
+ failures.push(...collectFailedTests(nested));
2015
+ }
2016
+ }
2017
+ return failures;
2018
+ }
2019
+ function collectPassedTests(value) {
2020
+ const record = asRecord(value);
2021
+ if (!record) {
2022
+ return [];
2023
+ }
2024
+ const fullName = asString(record.fullName);
2025
+ const title = asString(record.title);
2026
+ const label = fullName ?? title ?? "unnamed test";
2027
+ const status = asString(record.status);
2028
+ const state = asString(asRecord(record.result)?.state);
2029
+ const isPassed = status === "passed" || state === "passed";
2030
+ const passed = isPassed ? [label] : [];
2031
+ for (const nestedKey of ["assertionResults", "tests", "tasks"]) {
2032
+ for (const nested of asArray(record[nestedKey])) {
2033
+ passed.push(...collectPassedTests(nested));
2034
+ }
2035
+ }
2036
+ return passed;
2037
+ }
2038
+ function parseSuite(value) {
2039
+ const record = asRecord(value);
2040
+ if (!record) {
2041
+ return null;
2042
+ }
2043
+ const filePath = asString(record.name) ?? asString(record.file) ?? asString(record.filePath) ?? asString(record.filepath);
2044
+ if (!filePath) {
2045
+ return null;
2046
+ }
2047
+ return {
2048
+ failedTests: Array.from(new Set(collectFailedTests(record))),
2049
+ filePath: normalizeFilePath(filePath),
2050
+ passedTests: Array.from(new Set(collectPassedTests(record)))
2051
+ };
2052
+ }
2053
+ function parseVitestSuites(resultData) {
2054
+ const envelope = normalizeToolResultEnvelope(resultData);
2055
+ const candidates = [
2056
+ envelope.report,
2057
+ envelope.data,
2058
+ envelope.stdout,
2059
+ envelope.output,
2060
+ ...extractTextCandidates(resultData)
2061
+ ];
2062
+ for (const candidate of candidates) {
2063
+ const parsed = parseJsonCandidate(candidate);
2064
+ const report = asRecord(parsed);
2065
+ if (!report) {
2066
+ continue;
2067
+ }
2068
+ const suites = asArray(report.testResults).map(parseSuite).filter((suite) => Boolean(suite));
2069
+ if (suites.length > 0) {
2070
+ return suites;
2071
+ }
2072
+ const fileSuites = asArray(report.files).map(parseSuite).filter((suite) => Boolean(suite));
2073
+ if (fileSuites.length > 0) {
2074
+ return fileSuites;
2075
+ }
2076
+ }
2077
+ return [];
2078
+ }
2079
+ function getMatchedSuites(contract, resultData) {
2080
+ const config = parseConfig3(contract.condition);
2081
+ return parseVitestSuites(resultData).filter(
2082
+ (suite) => patternMatchesPath(suite.filePath, config.testPattern)
2083
+ );
2084
+ }
2085
+ var testRunnerEvaluator = {
2086
+ name: "test_runner",
2087
+ matches({ contract, resultData }) {
2088
+ return getMatchedSuites(contract, resultData).length > 0;
2089
+ },
2090
+ evaluate(args) {
2091
+ const config = parseConfig3(args.contract.condition);
2092
+ const matchedSuites = getMatchedSuites(args.contract, args.resultData);
2093
+ if (!args.resultData) {
2094
+ return {
2095
+ result: "error",
2096
+ rationale: "test_runner requires Vitest JSON resultData."
2097
+ };
2098
+ }
2099
+ if (matchedSuites.length === 0) {
2100
+ return {
2101
+ result: "inconclusive",
2102
+ rationale: `No Vitest suites matched ${config.testPattern}.`
2103
+ };
2104
+ }
2105
+ const failedTests = matchedSuites.flatMap((suite) => suite.failedTests);
2106
+ const passedTests = matchedSuites.flatMap((suite) => suite.passedTests);
2107
+ const conditionSatisfied = failedTests.length === 0;
2108
+ return {
2109
+ result: deriveDirectionalResult(
2110
+ args.contract.direction,
2111
+ conditionSatisfied
2112
+ ),
2113
+ rationale: conditionSatisfied ? `Vitest matched ${matchedSuites.length} suite(s) for ${config.testPattern}; all ${passedTests.length} test assertions passed.` : `Vitest matched ${matchedSuites.length} suite(s) for ${config.testPattern}; failing tests: ${failedTests.join(", ")}.`,
2114
+ data: {
2115
+ runner: config.runner,
2116
+ testPattern: config.testPattern,
2117
+ matchedFiles: matchedSuites.map((suite) => suite.filePath),
2118
+ failedTests,
2119
+ passedTestCount: passedTests.length
2120
+ }
2121
+ };
2122
+ }
2123
+ };
2124
+
2125
+ // src/evaluators/tscCheckerEvaluator.ts
2126
+ function parseConfig4(condition) {
2127
+ const record = asRecord(condition.evaluatorConfig);
2128
+ if (!record) {
2129
+ throw new Error(
2130
+ "tsc_checker requires evaluatorConfig with { filePatterns: string[] }."
2131
+ );
2132
+ }
2133
+ const filePatterns = asArray(record.filePatterns).map(asString).filter((pattern) => Boolean(pattern));
2134
+ if (filePatterns.length === 0) {
2135
+ throw new Error("tsc_checker requires at least one file pattern.");
2136
+ }
2137
+ return { filePatterns };
2138
+ }
2139
+ function parseStructuredDiagnostics(resultData) {
2140
+ const envelope = normalizeToolResultEnvelope(resultData);
2141
+ const record = asRecord(envelope.report) ?? asRecord(envelope.data) ?? asRecord(resultData);
2142
+ if (!record) {
2143
+ return [];
2144
+ }
2145
+ return asArray(record.diagnostics).flatMap((entry) => {
2146
+ const diagnostic = asRecord(entry);
2147
+ if (!diagnostic) {
2148
+ return [];
2149
+ }
2150
+ const filePath = asString(diagnostic.filePath) ?? asString(diagnostic.file) ?? asString(asRecord(diagnostic.location)?.filePath) ?? asString(asRecord(asRecord(diagnostic.location)?.path)?.file);
2151
+ const message = asString(diagnostic.message) ?? "TypeScript error";
2152
+ return [
2153
+ {
2154
+ code: asString(diagnostic.code) ?? void 0,
2155
+ filePath: filePath ? normalizeFilePath(filePath) : null,
2156
+ message
2157
+ }
2158
+ ];
2159
+ });
2160
+ }
2161
+ function parseTextDiagnostics(resultData) {
2162
+ const diagnostics = [];
2163
+ const patterns = [
2164
+ /^(.+?)\((\d+),(\d+)\): error TS(\d+): (.+)$/gm,
2165
+ /^(.+?):(\d+):(\d+) - error TS(\d+): (.+)$/gm
2166
+ ];
2167
+ for (const text of extractTextCandidates(resultData)) {
2168
+ for (const pattern of patterns) {
2169
+ for (const match of text.matchAll(pattern)) {
2170
+ diagnostics.push({
2171
+ code: match[4],
2172
+ filePath: normalizeFilePath(match[1] ?? ""),
2173
+ message: match[5] ?? "TypeScript error"
2174
+ });
2175
+ }
2176
+ }
2177
+ }
2178
+ return diagnostics;
2179
+ }
2180
+ function parseDiagnostics2(resultData) {
2181
+ const structured = parseStructuredDiagnostics(resultData);
2182
+ return structured.length > 0 ? structured : parseTextDiagnostics(resultData);
2183
+ }
2184
+ function getMatchedDiagnostics2(contract, resultData) {
2185
+ const config = parseConfig4(contract.condition);
2186
+ return parseDiagnostics2(resultData).filter(
2187
+ (diagnostic) => somePatternMatches(diagnostic.filePath, config.filePatterns)
2188
+ );
2189
+ }
2190
+ var tscCheckerEvaluator = {
2191
+ name: "tsc_checker",
2192
+ matches({ contract, resultData }) {
2193
+ const envelope = normalizeToolResultEnvelope(resultData);
2194
+ const exitCode = asNumber(envelope.exitCode);
2195
+ if (exitCode === 0) {
2196
+ return true;
2197
+ }
2198
+ return getMatchedDiagnostics2(contract, resultData).length > 0;
2199
+ },
2200
+ evaluate(args) {
2201
+ const config = parseConfig4(args.contract.condition);
2202
+ if (!args.resultData) {
2203
+ return {
2204
+ result: "error",
2205
+ rationale: "tsc_checker requires TypeScript diagnostic resultData."
2206
+ };
2207
+ }
2208
+ const envelope = normalizeToolResultEnvelope(args.resultData);
2209
+ const exitCode = asNumber(envelope.exitCode);
2210
+ const matchedDiagnostics = getMatchedDiagnostics2(args.contract, args.resultData);
2211
+ if (matchedDiagnostics.length === 0 && exitCode !== 0 && exitCode !== null) {
2212
+ return {
2213
+ result: "inconclusive",
2214
+ rationale: "TypeScript reported errors, but none matched the configured file patterns."
2215
+ };
2216
+ }
2217
+ const conditionSatisfied = exitCode === 0 || exitCode !== null && matchedDiagnostics.length === 0;
2218
+ return {
2219
+ result: deriveDirectionalResult(
2220
+ args.contract.direction,
2221
+ conditionSatisfied
2222
+ ),
2223
+ rationale: conditionSatisfied ? `TypeScript reported no matching diagnostics for ${config.filePatterns.join(", ")}.` : `TypeScript found ${matchedDiagnostics.length} matching diagnostic(s): ${matchedDiagnostics.map(
2224
+ (diagnostic) => diagnostic.filePath ? `${diagnostic.filePath}${diagnostic.code ? ` (TS${diagnostic.code})` : ""}` : diagnostic.message
2225
+ ).join(", ")}.`,
2226
+ data: {
2227
+ exitCode,
2228
+ filePatterns: config.filePatterns,
2229
+ matchedDiagnostics
2230
+ }
2231
+ };
2232
+ }
2233
+ };
2234
+
2235
+ // src/evaluators/index.ts
2236
+ var ENGINEERING_EPISTEMIC_EVALUATORS = [
2237
+ testRunnerEvaluator,
2238
+ tscCheckerEvaluator,
2239
+ lintCheckerEvaluator,
2240
+ sentryCheckerEvaluator
2241
+ ];
2242
+ new Set(
2243
+ ENGINEERING_EPISTEMIC_EVALUATORS.map((evaluator) => evaluator.name)
2244
+ );
2245
+
2246
+ // src/epistemicContracts.evaluators.ts
2247
+ var evaluatorRegistry = /* @__PURE__ */ new Map();
2248
+ var BUILT_IN_EVIDENTIAL_ALIASES2 = METRIC_COMPARATOR_EVALUATOR_NAMES.evidentialAliases;
2249
+ var BUILT_IN_METRIC_CHECKER2 = METRIC_COMPARATOR_EVALUATOR_NAMES.metricChecker;
2250
+ var BUILT_IN_REFERENCE_CHECK_COUNTER2 = METRIC_COMPARATOR_EVALUATOR_NAMES.referenceCheckCounter;
2251
+ var BUILT_IN_TEMPORAL_DEADLINE2 = METRIC_COMPARATOR_EVALUATOR_NAMES.temporalDeadline;
2252
+ var BUILT_IN_MARKET_INDEX_COMPARATOR2 = METRIC_COMPARATOR_EVALUATOR_NAMES.marketIndexComparator;
2253
+ var MAX_CONTRACT_EVALUATION_BATCH_SIZE = 50;
2254
+ function clearEpistemicEvaluators() {
2255
+ evaluatorRegistry.clear();
2256
+ ensureBuiltInEvaluators();
2257
+ }
2258
+ function registerEpistemicEvaluator(evaluator) {
2259
+ ensureBuiltInEvaluators();
2260
+ evaluatorRegistry.set(evaluator.name, evaluator);
2261
+ }
2262
+ function getRegisteredEpistemicEvaluators() {
2263
+ ensureBuiltInEvaluators();
2264
+ return Array.from(evaluatorRegistry.keys()).sort();
2265
+ }
2266
+ function ensureBuiltInEvaluators() {
2267
+ for (const name of BUILT_IN_EVIDENTIAL_ALIASES2) {
2268
+ if (evaluatorRegistry.has(name)) {
2269
+ continue;
2270
+ }
2271
+ evaluatorRegistry.set(name, {
2272
+ name,
2273
+ evaluate: evaluateBuiltInEvidentialContract
2274
+ });
2275
+ }
2276
+ for (const evaluator of ENGINEERING_EPISTEMIC_EVALUATORS) {
2277
+ if (evaluatorRegistry.has(evaluator.name)) {
2278
+ continue;
2279
+ }
2280
+ evaluatorRegistry.set(evaluator.name, evaluator);
2281
+ }
2282
+ const researchEvaluators = [
2283
+ [BUILT_IN_METRIC_CHECKER2, evaluateMetricCheckerContract],
2284
+ [BUILT_IN_REFERENCE_CHECK_COUNTER2, evaluateReferenceCheckCounterContract],
2285
+ [BUILT_IN_TEMPORAL_DEADLINE2, evaluateTemporalDeadlineContract],
2286
+ [BUILT_IN_MARKET_INDEX_COMPARATOR2, evaluateMarketIndexComparatorContract]
2287
+ ];
2288
+ for (const [name, evaluate] of researchEvaluators) {
2289
+ if (evaluatorRegistry.has(name)) {
2290
+ continue;
2291
+ }
2292
+ evaluatorRegistry.set(name, { name, evaluate });
2293
+ }
2294
+ }
2295
+ function normalizeTrigger(trigger) {
2296
+ if (trigger === "evidence_added") {
2297
+ return "evidence_added";
2298
+ }
2299
+ if (trigger === "periodic") {
2300
+ return "periodic";
2301
+ }
2302
+ if (trigger === "manual") {
2303
+ return "manual";
2304
+ }
2305
+ return "event_driven";
2306
+ }
2307
+ function resolveSchedulesForTrigger(trigger) {
2308
+ if (trigger === "evidence_added") {
2309
+ return /* @__PURE__ */ new Set(["on_evidence", "event_driven"]);
2310
+ }
2311
+ if (trigger === "periodic") {
2312
+ return /* @__PURE__ */ new Set(["periodic"]);
2313
+ }
2314
+ if (trigger === "manual") {
2315
+ return /* @__PURE__ */ new Set(["on_demand", "event_driven"]);
2316
+ }
2317
+ return /* @__PURE__ */ new Set(["event_driven"]);
2318
+ }
2319
+ async function executeContractEvaluation(args) {
2320
+ ensureBuiltInEvaluators();
2321
+ const evaluator = evaluatorRegistry.get(args.contract.condition.evaluator);
2322
+ let evaluation;
2323
+ if (evaluator) {
2324
+ try {
2325
+ evaluation = await evaluator.evaluate({
2326
+ belief: args.belief,
2327
+ contract: args.contract,
2328
+ ctx: args.ctx,
2329
+ now: args.now,
2330
+ resultData: args.resultData,
2331
+ trigger: args.trigger,
2332
+ inputData: args.inputData
2333
+ });
2334
+ } catch (error) {
2335
+ evaluation = {
2336
+ result: "error",
2337
+ rationale: error instanceof Error ? error.message : "Unknown evaluator error."
2338
+ };
2339
+ }
2340
+ } else {
2341
+ evaluation = {
2342
+ result: "error",
2343
+ rationale: `No epistemic evaluator registered for "${args.contract.condition.evaluator}".`
2344
+ };
2345
+ }
2346
+ const confidenceBefore = typeof args.currentConfidence === "number" ? args.currentConfidence : typeof args.belief.confidence === "number" ? args.belief.confidence : 0.5;
2347
+ const modulationPlan = deriveContractModulationPlan({
2348
+ currentConfidence: confidenceBefore,
2349
+ modulation: args.contract.modulation,
2350
+ result: evaluation.result,
2351
+ resultConfidence: evaluation.confidence
2352
+ });
2353
+ let beliefConfidenceId;
2354
+ let confidenceAfter = confidenceBefore;
2355
+ if (modulationPlan) {
2356
+ const contractB = Math.max(0, Math.min(1, modulationPlan.confidenceAfter));
2357
+ const modulationResult = await applyBeliefConfidenceChange(args.ctx, {
2358
+ nodeId: args.contract.beliefNodeId,
2359
+ belief: contractB,
2360
+ disbelief: 1 - contractB,
2361
+ uncertainty: 0,
2362
+ baseRate: 0.5,
2363
+ trigger: modulationPlan.trigger,
2364
+ rationale: `Epistemic contract "${args.contract.title}" ${evaluation.result}: ${evaluation.rationale}`,
2365
+ authenticatedUserId: args.authenticatedUserId
2366
+ });
2367
+ beliefConfidenceId = modulationResult.beliefConfidenceId;
2368
+ confidenceAfter = typeof modulationResult.newConfidence === "number" ? modulationResult.newConfidence : modulationPlan.confidenceAfter;
2369
+ }
2370
+ const evaluationId = await args.ctx.db.insert("contractEvaluations", {
2371
+ contractId: args.contract.contractId,
2372
+ beliefNodeId: args.contract.beliefNodeId,
2373
+ result: evaluation.result,
2374
+ evaluatedAt: args.now,
2375
+ evaluator: args.contract.condition.evaluator,
2376
+ trigger: args.trigger,
2377
+ resultData: evaluation.data,
2378
+ modulationApplied: Boolean(modulationPlan),
2379
+ confidenceDelta: modulationPlan?.confidenceDelta,
2380
+ confidenceBefore: modulationPlan?.confidenceBefore,
2381
+ confidenceAfter: modulationPlan ? confidenceAfter : void 0,
2382
+ beliefConfidenceId,
2383
+ modulationRationale: evaluation.rationale,
2384
+ topicId: args.contract.topicId
2385
+ });
2386
+ const nextStatus = deriveContractStatus(evaluation.result, args.contract.status);
2387
+ await args.ctx.db.patch(
2388
+ args.contract._id,
2389
+ {
2390
+ status: nextStatus,
2391
+ lastEvaluatedAt: args.now,
2392
+ evaluationCount: (args.contract.evaluationCount ?? 0) + 1,
2393
+ updatedAt: args.now
2394
+ }
2395
+ );
2396
+ return {
2397
+ evaluationId,
2398
+ result: evaluation.result,
2399
+ status: nextStatus,
2400
+ confidenceBefore,
2401
+ confidenceAfter,
2402
+ rationale: evaluation.rationale,
2403
+ data: evaluation.data,
2404
+ currentConfidence: confidenceAfter
2405
+ };
2406
+ }
2407
+ async function evaluateContractsForTriggerBatch(args) {
2408
+ const startedAt = Date.now();
2409
+ const contracts = await loadContractsForTrigger({
2410
+ ctx: args.ctx,
2411
+ beliefNodeId: args.belief._id,
2412
+ trigger: normalizeTrigger(args.trigger),
2413
+ contractIds: args.contractIds
2414
+ });
2415
+ const batchLimit = Math.max(
2416
+ 1,
2417
+ Math.min(
2418
+ args.maxBatchSize ?? MAX_CONTRACT_EVALUATION_BATCH_SIZE,
2419
+ MAX_CONTRACT_EVALUATION_BATCH_SIZE
2420
+ )
2421
+ );
2422
+ const currentBatch = contracts.slice(0, batchLimit);
2423
+ const overflowContracts = contracts.slice(batchLimit);
2424
+ let runningConfidence = typeof args.belief.confidence === "number" ? args.belief.confidence : 0.5;
2425
+ const results = [];
2426
+ for (const contract of currentBatch) {
2427
+ const evaluation = await executeContractEvaluation({
2428
+ ctx: args.ctx,
2429
+ belief: args.belief,
2430
+ contract,
2431
+ now: Date.now(),
2432
+ trigger: normalizeTrigger(args.trigger),
2433
+ inputData: args.inputData,
2434
+ authenticatedUserId: args.authenticatedUserId,
2435
+ currentConfidence: runningConfidence
2436
+ });
2437
+ runningConfidence = evaluation.currentConfidence;
2438
+ args.belief.confidence = runningConfidence;
2439
+ results.push({
2440
+ contractId: contract.contractId,
2441
+ result: evaluation.result,
2442
+ status: evaluation.status,
2443
+ confidenceAfter: evaluation.confidenceAfter
2444
+ });
2445
+ }
2446
+ if (overflowContracts.length > 0) {
2447
+ await args.ctx.scheduler.runAfter(
2448
+ 0,
2449
+ "epistemicContracts.processContractEvaluationOverflow",
2450
+ {
2451
+ beliefNodeId: args.belief._id,
2452
+ trigger: normalizeTrigger(args.trigger),
2453
+ contractIds: overflowContracts.map((contract) => contract.contractId),
2454
+ inputData: args.inputData,
2455
+ authenticatedUserId: args.authenticatedUserId,
2456
+ maxBatchSize: batchLimit
2457
+ }
2458
+ );
2459
+ }
2460
+ const executionTimeMs = Date.now() - startedAt;
2461
+ console.info("[epistemicContracts] processed contract evaluation batch", {
2462
+ beliefNodeId: String(args.belief._id),
2463
+ trigger: normalizeTrigger(args.trigger),
2464
+ batchSize: currentBatch.length,
2465
+ overflowCount: overflowContracts.length,
2466
+ executionTimeMs
2467
+ });
2468
+ return {
2469
+ totalContracts: contracts.length,
2470
+ processedCount: currentBatch.length,
2471
+ overflowCount: overflowContracts.length,
2472
+ scheduledOverflow: overflowContracts.length > 0,
2473
+ batchSize: currentBatch.length,
2474
+ executionTimeMs,
2475
+ trigger: normalizeTrigger(args.trigger),
2476
+ results
2477
+ };
2478
+ }
2479
+ async function loadContractsForBelief(args) {
2480
+ return await args.ctx.db.query("epistemicContracts").withIndex("by_belief", (q) => q.eq("beliefNodeId", args.beliefNodeId)).collect();
2481
+ }
2482
+ async function loadContractsForTrigger(args) {
2483
+ const contracts = await loadContractsForBelief(args);
2484
+ ensureBuiltInEvaluators();
2485
+ const allowedSchedules = resolveSchedulesForTrigger(args.trigger);
2486
+ const contractIdFilter = args.contractIds && args.contractIds.length > 0 ? new Set(args.contractIds) : null;
2487
+ return contracts.filter((contract) => {
2488
+ if (contract.status === "archived") {
2489
+ return false;
2490
+ }
2491
+ if (contract.conditionType === "composite") {
2492
+ return false;
2493
+ }
2494
+ if (!evaluatorRegistry.has(contract.condition.evaluator)) {
2495
+ return false;
2496
+ }
2497
+ if (contractIdFilter) {
2498
+ return contractIdFilter.has(contract.contractId);
2499
+ }
2500
+ return allowedSchedules.has(contract.evaluationSchedule);
2501
+ });
2502
+ }
2503
+
2504
+ export { clearEpistemicEvaluators, evaluateContractsForTriggerBatch, executeContractEvaluation, getRegisteredEpistemicEvaluators, loadContractsForBelief, registerEpistemicEvaluator };
2505
+ //# sourceMappingURL=epistemicContracts.evaluators.js.map
2506
+ //# sourceMappingURL=epistemicContracts.evaluators.js.map