@lucern/graph-primitives 0.1.0-alpha.4 → 0.3.0-alpha.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 (78) hide show
  1. package/dist/beliefDecay.js +229 -1115
  2. package/dist/beliefDecay.js.map +1 -1
  3. package/dist/beliefEvidenceLinks.js +53 -834
  4. package/dist/beliefEvidenceLinks.js.map +1 -1
  5. package/dist/confidencePropagationDispatch.d.ts +3 -3
  6. package/dist/confidencePropagationDispatch.js +30 -308
  7. package/dist/confidencePropagationDispatch.js.map +1 -1
  8. package/dist/contradictions.js +5 -797
  9. package/dist/contradictions.js.map +1 -1
  10. package/dist/edges/contradicts.js +1 -122
  11. package/dist/edges/contradicts.js.map +1 -1
  12. package/dist/edges/dependsOn.js +14 -172
  13. package/dist/edges/dependsOn.js.map +1 -1
  14. package/dist/edges/elaborates.js +1 -49
  15. package/dist/edges/elaborates.js.map +1 -1
  16. package/dist/edges/index.js +14 -277
  17. package/dist/edges/index.js.map +1 -1
  18. package/dist/edges/informs.js +1 -62
  19. package/dist/edges/informs.js.map +1 -1
  20. package/dist/edges/propagationTypes.d.ts +2 -2
  21. package/dist/edges/propagationTypes.js.map +1 -1
  22. package/dist/edges/refutes.js +1 -62
  23. package/dist/edges/refutes.js.map +1 -1
  24. package/dist/edges/supports.js +1 -122
  25. package/dist/edges/supports.js.map +1 -1
  26. package/dist/edges/utils.d.ts +6 -6
  27. package/dist/edges/utils.js +1 -130
  28. package/dist/edges/utils.js.map +1 -1
  29. package/dist/entityBridge.js +2 -17
  30. package/dist/entityBridge.js.map +1 -1
  31. package/dist/entityLifecycle.js +62 -848
  32. package/dist/entityLifecycle.js.map +1 -1
  33. package/dist/epistemicAnswers.js +27 -838
  34. package/dist/epistemicAnswers.js.map +1 -1
  35. package/dist/epistemicBeliefs.js +186 -2214
  36. package/dist/epistemicBeliefs.js.map +1 -1
  37. package/dist/epistemicContractHelpers.js +1 -318
  38. package/dist/epistemicContractHelpers.js.map +1 -1
  39. package/dist/epistemicContracts.js +163 -2467
  40. package/dist/epistemicContracts.js.map +1 -1
  41. package/dist/epistemicEdges.js +60 -863
  42. package/dist/epistemicEdges.js.map +1 -1
  43. package/dist/epistemicEvidence.js +116 -1647
  44. package/dist/epistemicEvidence.js.map +1 -1
  45. package/dist/epistemicHelpers.js +3 -2
  46. package/dist/epistemicHelpers.js.map +1 -1
  47. package/dist/epistemicLinking.js +2 -785
  48. package/dist/epistemicLinking.js.map +1 -1
  49. package/dist/epistemicNodes.js +34 -1427
  50. package/dist/epistemicNodes.js.map +1 -1
  51. package/dist/epistemicQuestions.js +88 -1637
  52. package/dist/epistemicQuestions.js.map +1 -1
  53. package/dist/epistemicSources.js +28 -1421
  54. package/dist/epistemicSources.js.map +1 -1
  55. package/dist/evaluators/index.js +163 -2467
  56. package/dist/evaluators/index.js.map +1 -1
  57. package/dist/index.js +486 -3649
  58. package/dist/index.js.map +1 -1
  59. package/dist/ontology-matching.js +1 -344
  60. package/dist/ontology-matching.js.map +1 -1
  61. package/dist/ontologyApproval.js +1 -13
  62. package/dist/ontologyApproval.js.map +1 -1
  63. package/dist/ontologyDefinitions.js +2 -17
  64. package/dist/ontologyDefinitions.js.map +1 -1
  65. package/dist/ontologyRegistry.js +2 -17
  66. package/dist/ontologyRegistry.js.map +1 -1
  67. package/dist/projectionReconciliation.js +2 -17
  68. package/dist/projectionReconciliation.js.map +1 -1
  69. package/dist/questionEvidenceLinks.js +242 -837
  70. package/dist/questionEvidenceLinks.js.map +1 -1
  71. package/dist/text-matching.js +1 -244
  72. package/dist/text-matching.js.map +1 -1
  73. package/dist/workflowBridge.d.ts +27 -0
  74. package/dist/workflowBridge.js +303 -0
  75. package/dist/workflowBridge.js.map +1 -0
  76. package/dist/workspaceIsolation.js +8 -609
  77. package/dist/workspaceIsolation.js.map +1 -1
  78. package/package.json +6 -6
@@ -1,1467 +1,16 @@
1
1
  import { v } from 'convex/values';
2
- import { componentsGeneric, defineTable, internalMutationGeneric, mutationGeneric, anyApi, queryGeneric, internalQueryGeneric } from 'convex/server';
2
+ import { normalizeTupleContradictionPolicy, mkOpinion, createInheritedContractRecord, confidenceFromSL, conditionalDeduction, project, dampedDependencyCascade, hasProjectedOpinionChanged, detectTupleContradiction, evaluateTupleContradictionTransition, readOpinionFromRecord, trustDiscount, applyNegativeSupport, cumulativeFusion, applyNegativeEvidence } from '@lucern/confidence';
3
+ import { checkScopeAccess, checkProjectAccess } from '@lucern/access-control/access';
4
+ import { canAudienceClassAccess, normalizeAudienceKey, classFromAudienceKey } from '@lucern/access-control/audience';
5
+ import { listAudienceRegistryRows } from '@lucern/access-control/audienceRegistry';
6
+ import { getCurrentUserId } from '@lucern/access-control/auth';
7
+ import { assertSchemaEnumValue } from '@lucern/contracts/schema-helpers/enumValidation';
8
+ import { permissiveReturn } from '@lucern/contracts/schema-helpers/validators';
9
+ import { componentsGeneric, internalMutationGeneric, mutationGeneric, anyApi, queryGeneric, internalQueryGeneric } from 'convex/server';
10
+ import { isNodeType, getLayerForNodeType } from '@lucern/contracts/schema-helpers/spine/tables/epistemicNodes';
3
11
 
4
12
  // src/epistemicBeliefs.ts
5
13
 
6
- // ../confidence/src/v1/operations/subjectiveLogic/index.ts
7
- function opinion(belief, disbelief, uncertainty, baseRate = 0.5) {
8
- const b = Math.max(0, Math.min(1, belief));
9
- const d = Math.max(0, Math.min(1, disbelief));
10
- const u = Math.max(0, Math.min(1, uncertainty));
11
- const a = Math.max(0, Math.min(1, baseRate));
12
- const sum = b + d + u;
13
- if (sum === 0) {
14
- return { b: 0, d: 0, u: 1, a };
15
- }
16
- return {
17
- b: b / sum,
18
- d: d / sum,
19
- u: u / sum,
20
- a
21
- };
22
- }
23
- function vacuous(baseRate = 0.5) {
24
- return { b: 0, d: 0, u: 1, a: baseRate };
25
- }
26
- function project(o) {
27
- return o.b + o.a * o.u;
28
- }
29
- function cumulativeFusion(left, right) {
30
- if (left.u === 0 && right.u === 0) {
31
- return opinion(
32
- (left.b + right.b) / 2,
33
- (left.d + right.d) / 2,
34
- 0,
35
- (left.a + right.a) / 2
36
- );
37
- }
38
- const k = left.u + right.u - left.u * right.u;
39
- if (k === 0) {
40
- return vacuous((left.a + right.a) / 2);
41
- }
42
- return opinion(
43
- (left.b * right.u + right.b * left.u) / k,
44
- (left.d * right.u + right.d * left.u) / k,
45
- left.u * right.u / k,
46
- (left.a + right.a) / 2
47
- );
48
- }
49
- function trustDiscount(sourceOpinion, trust) {
50
- const weight = Math.max(0, Math.min(1, Math.abs(trust)));
51
- return opinion(
52
- weight * sourceOpinion.b,
53
- weight * sourceOpinion.d,
54
- 1 - weight * (sourceOpinion.b + sourceOpinion.d),
55
- sourceOpinion.a
56
- );
57
- }
58
- var EPSILON = 1e-9;
59
- function childBaseRateFallback(ifTrue, ifFalse, fallbackBaseRate) {
60
- if (fallbackBaseRate !== void 0) {
61
- return Math.max(0, Math.min(1, fallbackBaseRate));
62
- }
63
- if (Math.abs(ifTrue.a - ifFalse.a) <= EPSILON) {
64
- return ifTrue.a;
65
- }
66
- return (ifTrue.a + ifFalse.a) / 2;
67
- }
68
- function computeConditionalDeductionBaseRate(opinionA, ifTrue, ifFalse, fallbackBaseRate) {
69
- const denominator = 1 - opinionA.a * ifTrue.u - (1 - opinionA.a) * ifFalse.u;
70
- if (ifTrue.u + ifFalse.u < 2 - EPSILON && Math.abs(denominator) > EPSILON) {
71
- const baseRate = (opinionA.a * ifTrue.b + (1 - opinionA.a) * ifFalse.b) / denominator;
72
- if (baseRate >= -EPSILON && baseRate <= 1 + EPSILON) {
73
- return Math.max(0, Math.min(1, baseRate));
74
- }
75
- }
76
- return fallbackBaseRate;
77
- }
78
- function safeCorrectionTerm(numerator, denominator) {
79
- if (Math.abs(denominator) <= EPSILON) {
80
- return void 0;
81
- }
82
- const value = numerator / denominator;
83
- if (!Number.isFinite(value)) {
84
- return void 0;
85
- }
86
- return Math.max(0, value);
87
- }
88
- function conditionalDeduction(opinionA, ifTrue, ifFalse, fallbackBaseRate) {
89
- const fallbackChildBaseRate = childBaseRateFallback(
90
- ifTrue,
91
- ifFalse,
92
- fallbackBaseRate
93
- );
94
- const childBaseRate = computeConditionalDeductionBaseRate(
95
- opinionA,
96
- ifTrue,
97
- ifFalse,
98
- fallbackChildBaseRate
99
- );
100
- const projectedAntecedent = project(opinionA);
101
- const projectedAntecedentComplement = 1 - projectedAntecedent;
102
- const intermediateBelief = opinionA.b * ifTrue.b + opinionA.d * ifFalse.b + opinionA.u * (ifTrue.b * opinionA.a + ifFalse.b * (1 - opinionA.a));
103
- const intermediateDisbelief = opinionA.b * ifTrue.d + opinionA.d * ifFalse.d + opinionA.u * (ifTrue.d * opinionA.a + ifFalse.d * (1 - opinionA.a));
104
- const intermediateUncertainty = opinionA.b * ifTrue.u + opinionA.d * ifFalse.u + opinionA.u * (ifTrue.u * opinionA.a + ifFalse.u * (1 - opinionA.a));
105
- const projectedVacuousDeduction = ifTrue.b * opinionA.a + ifFalse.b * (1 - opinionA.a) + childBaseRate * (ifTrue.u * opinionA.a + ifFalse.u * (1 - opinionA.a));
106
- const projectedConditionalA = ifTrue.b + childBaseRate * (1 - ifTrue.b - ifTrue.d);
107
- let correction = 0;
108
- if (ifTrue.b > ifFalse.b && ifTrue.d > ifFalse.d || ifTrue.b <= ifFalse.b && ifTrue.d <= ifFalse.d) {
109
- correction = 0;
110
- } else if (ifTrue.b > ifFalse.b && ifTrue.d <= ifFalse.d) {
111
- const beliefGap = ifTrue.b - ifFalse.b;
112
- const disbeliefGap = ifFalse.d - ifTrue.d;
113
- if (projectedVacuousDeduction <= projectedConditionalA && projectedAntecedent <= opinionA.a) {
114
- correction = safeCorrectionTerm(
115
- opinionA.a * opinionA.u * (intermediateBelief - ifTrue.b),
116
- projectedAntecedent * childBaseRate
117
- ) ?? 0;
118
- } else if (projectedVacuousDeduction <= projectedConditionalA && projectedAntecedent > opinionA.a) {
119
- correction = safeCorrectionTerm(
120
- opinionA.a * opinionA.u * (intermediateDisbelief - ifTrue.d) * beliefGap,
121
- projectedAntecedentComplement * childBaseRate * disbeliefGap
122
- ) ?? 0;
123
- } else if (projectedVacuousDeduction > projectedConditionalA && projectedAntecedent <= opinionA.a) {
124
- correction = safeCorrectionTerm(
125
- (1 - opinionA.a) * opinionA.u * (intermediateBelief - ifTrue.b) * disbeliefGap,
126
- projectedAntecedent * (1 - childBaseRate) * beliefGap
127
- ) ?? 0;
128
- } else {
129
- correction = safeCorrectionTerm(
130
- (1 - opinionA.a) * opinionA.u * (intermediateDisbelief - ifTrue.d),
131
- projectedAntecedentComplement * (1 - childBaseRate)
132
- ) ?? 0;
133
- }
134
- } else {
135
- const beliefGap = ifFalse.b - ifTrue.b;
136
- const disbeliefGap = ifTrue.d - ifFalse.d;
137
- if (projectedVacuousDeduction <= projectedConditionalA && projectedAntecedent <= opinionA.a) {
138
- correction = safeCorrectionTerm(
139
- (1 - opinionA.a) * opinionA.u * (intermediateDisbelief - ifTrue.d) * beliefGap,
140
- projectedAntecedent * childBaseRate * disbeliefGap
141
- ) ?? 0;
142
- } else if (projectedVacuousDeduction <= projectedConditionalA && projectedAntecedent > opinionA.a) {
143
- correction = safeCorrectionTerm(
144
- (1 - opinionA.a) * opinionA.u * (intermediateBelief - ifTrue.b),
145
- projectedAntecedentComplement * childBaseRate
146
- ) ?? 0;
147
- } else if (projectedVacuousDeduction > projectedConditionalA && projectedAntecedent <= opinionA.a) {
148
- correction = safeCorrectionTerm(
149
- opinionA.a * opinionA.u * (intermediateDisbelief - ifTrue.d),
150
- projectedAntecedent * (1 - childBaseRate)
151
- ) ?? 0;
152
- } else {
153
- correction = safeCorrectionTerm(
154
- opinionA.a * opinionA.u * (intermediateBelief - ifTrue.b) * disbeliefGap,
155
- projectedAntecedentComplement * (1 - childBaseRate) * beliefGap
156
- ) ?? 0;
157
- }
158
- }
159
- return opinion(
160
- intermediateBelief - childBaseRate * correction,
161
- intermediateDisbelief - (1 - childBaseRate) * correction,
162
- intermediateUncertainty + correction,
163
- childBaseRate
164
- );
165
- }
166
- function negate(o) {
167
- return { b: o.d, d: o.b, u: o.u, a: 1 - o.a };
168
- }
169
- function constraintFusion(left, right, mode = "pressure") {
170
- if (mode === "redistribute") {
171
- const leftProjected = project(left);
172
- const rightProjected = project(right);
173
- const total = leftProjected + rightProjected;
174
- if (total <= 1) {
175
- return { o1: left, o2: right };
176
- }
177
- const scale = 1 / total;
178
- return {
179
- o1: opinion(
180
- left.b * scale,
181
- left.d + left.b * (1 - scale),
182
- left.u,
183
- left.a
184
- ),
185
- o2: opinion(
186
- right.b * scale,
187
- right.d + right.b * (1 - scale),
188
- right.u,
189
- right.a
190
- )
191
- };
192
- }
193
- const pressureLeft = right.b * 0.5;
194
- const pressureRight = left.b * 0.5;
195
- return {
196
- o1: opinion(
197
- left.b - pressureLeft * 0.3,
198
- left.d + pressureLeft * 0.3,
199
- left.u,
200
- left.a
201
- ),
202
- o2: opinion(
203
- right.b - pressureRight * 0.3,
204
- right.d + pressureRight * 0.3,
205
- right.u,
206
- right.a
207
- )
208
- };
209
- }
210
-
211
- // ../confidence/src/v1/operations/scoring.ts
212
- function finiteNumber(value) {
213
- return typeof value === "number" && Number.isFinite(value) ? value : void 0;
214
- }
215
- function clamp01(value) {
216
- return Math.max(0, Math.min(1, value));
217
- }
218
- function confidenceFromOpinion(opinion2) {
219
- return clamp01(opinion2.b + opinion2.a * opinion2.u);
220
- }
221
- function confidenceFromSL(belief, _disbelief, uncertainty, baseRate = 0.5) {
222
- return confidenceFromOpinion({
223
- b: belief,
224
- u: uncertainty,
225
- a: baseRate
226
- });
227
- }
228
- function readOpinionFromRecord(source, fallback = {}) {
229
- const record = source && typeof source === "object" ? source : {};
230
- return {
231
- b: finiteNumber(record.b) ?? finiteNumber(record.belief) ?? finiteNumber(record.slBelief) ?? finiteNumber(record.opinion_b) ?? fallback.b ?? 0,
232
- d: finiteNumber(record.d) ?? finiteNumber(record.disbelief) ?? finiteNumber(record.slDisbelief) ?? finiteNumber(record.opinion_d) ?? fallback.d ?? 0,
233
- u: finiteNumber(record.u) ?? finiteNumber(record.uncertainty) ?? finiteNumber(record.slUncertainty) ?? finiteNumber(record.opinion_u) ?? fallback.u ?? 1,
234
- a: finiteNumber(record.a) ?? finiteNumber(record.baseRate) ?? finiteNumber(record.slBaseRate) ?? finiteNumber(record.opinion_a) ?? fallback.a ?? 0.5
235
- };
236
- }
237
- function hasProjectedOpinionChanged(current, next, tolerance = 0.01) {
238
- return Math.abs(confidenceFromOpinion(next) - confidenceFromOpinion(current)) >= tolerance;
239
- }
240
-
241
- // ../confidence/src/v1/operations/contradiction/detectTupleContradiction.ts
242
- var DEFAULT_TUPLE_CONTRADICTION_BELIEF_THRESHOLD = 0.7;
243
- var DEFAULT_TUPLE_CONTRADICTION_DISBELIEF_THRESHOLD = 0.7;
244
- function normalizeTupleContradictionPolicy(policy = {}) {
245
- return {
246
- beliefThreshold: clamp01(
247
- policy.beliefThreshold ?? DEFAULT_TUPLE_CONTRADICTION_BELIEF_THRESHOLD
248
- ),
249
- disbeliefThreshold: clamp01(
250
- policy.disbeliefThreshold ?? DEFAULT_TUPLE_CONTRADICTION_DISBELIEF_THRESHOLD
251
- )
252
- };
253
- }
254
- function detectTupleContradiction(opinion2, tauB = DEFAULT_TUPLE_CONTRADICTION_BELIEF_THRESHOLD, tauD = DEFAULT_TUPLE_CONTRADICTION_DISBELIEF_THRESHOLD) {
255
- return opinion2.b > tauB && opinion2.d > tauD;
256
- }
257
- function evaluateTupleContradictionTransition(args) {
258
- const policy = normalizeTupleContradictionPolicy(args.policy);
259
- const tupleContradicted = detectTupleContradiction(
260
- args.opinion,
261
- policy.beliefThreshold,
262
- policy.disbeliefThreshold
263
- );
264
- const previousTupleContradicted = Boolean(args.previousTupleContradicted);
265
- return {
266
- tupleContradicted,
267
- crossedIntoTupleContradiction: !previousTupleContradicted && tupleContradicted,
268
- crossedOutOfTupleContradiction: previousTupleContradicted && !tupleContradicted,
269
- policy
270
- };
271
- }
272
-
273
- // ../confidence/src/v1/operations/dynamics/cascade.ts
274
- function dampedDependencyOpinion(dependencyOpinion, beliefOpinion, mode = "continuous", threshold = 0.3) {
275
- const dependencyProjection = project(dependencyOpinion);
276
- if (mode === "threshold") {
277
- if (dependencyProjection < threshold) {
278
- return opinion(
279
- 0,
280
- beliefOpinion.d + beliefOpinion.b * 0.5,
281
- 0.5,
282
- beliefOpinion.a
283
- );
284
- }
285
- return beliefOpinion;
286
- }
287
- const dampingFactor = Math.pow(dependencyProjection, 0.5);
288
- return opinion(
289
- beliefOpinion.b * dampingFactor,
290
- beliefOpinion.d + beliefOpinion.b * (1 - dampingFactor) * 0.3,
291
- beliefOpinion.u + beliefOpinion.b * (1 - dampingFactor) * 0.7,
292
- beliefOpinion.a
293
- );
294
- }
295
- function dampedDependencyCascade(dependencyOpinion, beliefOpinion, mode = "continuous") {
296
- return {
297
- opinion: dampedDependencyOpinion(dependencyOpinion, beliefOpinion, mode),
298
- operator: "dependency_cascade",
299
- rationale: `Damped dependency cascade (${mode}): prerequisite at ${project(
300
- dependencyOpinion
301
- ).toFixed(2)}`
302
- };
303
- }
304
-
305
- // ../confidence/src/v1/operations/dynamics/defeat.ts
306
- function applyNegativeSupport(source, target, weight, metadata = {}) {
307
- if (metadata.constraint === "xor") {
308
- const result = constraintFusion(
309
- source,
310
- target,
311
- metadata.normalization ?? "pressure"
312
- );
313
- return {
314
- opinion: result.o2,
315
- operator: "constraint_fusion",
316
- rationale: `XOR constraint: source belief at ${project(source).toFixed(
317
- 2
318
- )} pressures target`
319
- };
320
- }
321
- const discounted = trustDiscount(negate(source), Math.abs(weight));
322
- return {
323
- opinion: cumulativeFusion(target, discounted),
324
- operator: "cumulative_fusion",
325
- rationale: `Contradicting evidence (weight=${weight.toFixed(
326
- 2
327
- )}) from source at ${project(source).toFixed(2)}`
328
- };
329
- }
330
- function applyNegativeEvidence(source, target, weight) {
331
- const discounted = trustDiscount(negate(source), Math.abs(weight));
332
- return {
333
- opinion: cumulativeFusion(target, discounted),
334
- operator: "cumulative_fusion",
335
- rationale: `Contradicting evidence (weight=${weight.toFixed(2)})`
336
- };
337
- }
338
-
339
- // ../confidence/src/v1/operations/contracts/epistemicContract.ts
340
- function generateContractId() {
341
- if (typeof crypto !== "undefined" && typeof crypto.randomUUID === "function") {
342
- return crypto.randomUUID();
343
- }
344
- return `contract-${Date.now()}-${Math.random().toString(36).slice(2, 10)}`;
345
- }
346
- function createInheritedContractRecord(contract, args) {
347
- return {
348
- beliefNodeId: args.beliefNodeId,
349
- contractId: generateContractId(),
350
- title: contract.title,
351
- description: contract.description,
352
- conditionType: contract.conditionType,
353
- direction: contract.direction,
354
- condition: contract.condition,
355
- deadline: contract.deadline,
356
- compositeOf: contract.compositeOf,
357
- compositeOperator: contract.compositeOperator,
358
- modulation: contract.modulation,
359
- evaluationSchedule: contract.evaluationSchedule,
360
- periodicIntervalMs: contract.periodicIntervalMs,
361
- status: "active",
362
- lineageSource: "inherited",
363
- inheritedFromContractId: contract.contractId,
364
- inheritedFromBeliefNodeId: contract.beliefNodeId,
365
- inheritedAt: args.now,
366
- topicId: args.topicId,
367
- createdAt: args.now,
368
- createdBy: args.createdBy,
369
- updatedAt: args.now
370
- };
371
- }
372
- var api = anyApi;
373
- componentsGeneric();
374
-
375
- // ../access-control/src/topicProjectOverlay.ts
376
- var LEGACY_SCOPE_FIELD = "graphScopeProjectId";
377
- function readNonEmptyString(value) {
378
- if (typeof value !== "string") {
379
- return;
380
- }
381
- const normalized = value.trim();
382
- return normalized.length > 0 ? normalized : void 0;
383
- }
384
- function readStringArray(value) {
385
- if (!Array.isArray(value)) {
386
- return [];
387
- }
388
- return value.map((entry) => readNonEmptyString(entry)).filter((entry) => Boolean(entry));
389
- }
390
- function readMetadata(topic) {
391
- return topic.metadata && typeof topic.metadata === "object" ? topic.metadata : {};
392
- }
393
- function readLegacyProjectId(value) {
394
- if (!value) {
395
- return;
396
- }
397
- return readNonEmptyString(value[LEGACY_SCOPE_FIELD]);
398
- }
399
- function coerceVisibility(value) {
400
- return value === "private" || value === "team" || value === "firm" || value === "external" || value === "public" ? value : void 0;
401
- }
402
- function coerceStatus(value) {
403
- return value === "active" || value === "archived" || value === "watching" ? value : void 0;
404
- }
405
- function mapProjectType(topic, metadata) {
406
- const explicit = readNonEmptyString(metadata.projectType);
407
- if (explicit) {
408
- return explicit;
409
- }
410
- if (topic.type === "theme") {
411
- return "thematic";
412
- }
413
- return readNonEmptyString(topic.type) || "general";
414
- }
415
- function isProjectLikeTopic(topic) {
416
- const metadata = readMetadata(topic);
417
- return topic.type === "theme" || topic.type === "thematic" || topic.type === "deal" || topic.type === "monitoring" || readLegacyProjectId(topic) !== void 0 || readNonEmptyString(metadata.projectType) !== void 0;
418
- }
419
- async function resolveTopicDoc(ctx, scopeId) {
420
- if (ctx?.db && typeof ctx.db.get === "function") {
421
- try {
422
- const directTopic = await ctx.db.get(scopeId);
423
- if (directTopic) {
424
- return directTopic;
425
- }
426
- } catch {
427
- }
428
- }
429
- if (typeof ctx.runQuery !== "function") {
430
- return null;
431
- }
432
- try {
433
- const topic = await ctx.runQuery(api.topics.get, {
434
- id: String(scopeId)
435
- });
436
- if (topic?.name !== void 0 && topic?.type !== void 0) {
437
- return topic;
438
- }
439
- } catch {
440
- }
441
- try {
442
- const topic = await ctx.runQuery(api.topics.getByLegacyScopeId, {
443
- projectId: String(scopeId)
444
- });
445
- if (topic?.name !== void 0 && topic?.type !== void 0) {
446
- return topic;
447
- }
448
- } catch {
449
- }
450
- return null;
451
- }
452
- function materializeTopicProjectOverlay(topic, idMode = "legacy") {
453
- const metadata = readMetadata(topic);
454
- const topicId = String(topic._id);
455
- const legacyProjectId = readLegacyProjectId(topic) || readLegacyProjectId(metadata) || readNonEmptyString(metadata.legacyProjectId);
456
- const storageProjectId = legacyProjectId || topicId;
457
- const outwardId = idMode === "topic" ? topicId : storageProjectId;
458
- const visibility = coerceVisibility(topic.visibility) || coerceVisibility(metadata.visibility) || "private";
459
- const status = coerceStatus(topic.status) || coerceStatus(metadata.status) || "active";
460
- const createdAt = typeof topic.createdAt === "number" ? topic.createdAt : typeof topic._creationTime === "number" ? topic._creationTime : 0;
461
- const updatedAt = typeof topic.updatedAt === "number" ? topic.updatedAt : typeof metadata.updatedAt === "number" ? metadata.updatedAt : createdAt;
462
- return {
463
- ...metadata,
464
- _id: outwardId,
465
- projectId: outwardId,
466
- topicId,
467
- storageProjectId,
468
- legacyProjectId,
469
- name: readNonEmptyString(topic.name) || "Untitled Theme",
470
- type: mapProjectType(topic, metadata),
471
- description: readNonEmptyString(topic.description),
472
- ownerId: readNonEmptyString(metadata.ownerId) || readNonEmptyString(topic.createdBy) || "system",
473
- sharedWith: readStringArray(metadata.sharedWith),
474
- visibility,
475
- tenantId: readNonEmptyString(topic.tenantId) || readNonEmptyString(metadata.tenantId),
476
- workspaceId: readNonEmptyString(topic.workspaceId) || readNonEmptyString(metadata.workspaceId),
477
- status,
478
- tags: readStringArray(metadata.tags),
479
- chatCount: typeof metadata.chatCount === "number" ? metadata.chatCount : 0,
480
- artifactCount: typeof metadata.artifactCount === "number" ? metadata.artifactCount : 0,
481
- lastActivityAt: typeof metadata.lastActivityAt === "number" ? metadata.lastActivityAt : updatedAt,
482
- _creationTime: typeof topic._creationTime === "number" ? topic._creationTime : createdAt,
483
- createdAt,
484
- updatedAt
485
- };
486
- }
487
- async function resolveTopicProjectOverlay(ctx, scopeId, options = {}) {
488
- const topic = await resolveTopicDoc(ctx, scopeId);
489
- if (!topic) {
490
- return null;
491
- }
492
- if (options.projectLikeOnly !== false && !isProjectLikeTopic(topic)) {
493
- return null;
494
- }
495
- return materializeTopicProjectOverlay(topic, options.idMode);
496
- }
497
- async function listTopicProjectOverlays(ctx, options = {}) {
498
- let allTopics = [];
499
- if (ctx?.db?.query && typeof ctx.db.query === "function") {
500
- try {
501
- allTopics = await ctx.db.query("topics").collect();
502
- } catch {
503
- allTopics = [];
504
- }
505
- }
506
- if (allTopics.length === 0 && typeof ctx.runQuery === "function") {
507
- allTopics = (await ctx.runQuery(api.topics.list, {}) ?? []) || [];
508
- }
509
- return allTopics.filter(
510
- (topic) => options.projectLikeOnly === false || isProjectLikeTopic(topic)
511
- ).map((topic) => materializeTopicProjectOverlay(topic, options.idMode));
512
- }
513
-
514
- // ../access-control/src/projectGrantsBridge.ts
515
- var PROJECT_GRANT_STATUSES = ["active", "revoked", "expired"];
516
- function normalizeString(value) {
517
- if (typeof value !== "string") {
518
- return;
519
- }
520
- const trimmed = value.trim();
521
- return trimmed.length > 0 ? trimmed : void 0;
522
- }
523
- async function resolveGrantScopeIds(ctx, args) {
524
- const topicId = normalizeString(args.topicId);
525
- const projectId = normalizeString(args.projectId);
526
- for (const scopeId of [topicId, projectId]) {
527
- if (!scopeId) {
528
- continue;
529
- }
530
- try {
531
- const overlay = await resolveTopicProjectOverlay(ctx, scopeId, {
532
- idMode: "legacy",
533
- projectLikeOnly: false
534
- });
535
- if (overlay) {
536
- return {
537
- topicId: normalizeString(overlay.topicId) ?? topicId,
538
- projectId: normalizeString(overlay.projectId) ?? projectId ?? scopeId
539
- };
540
- }
541
- } catch {
542
- }
543
- }
544
- return { topicId, projectId };
545
- }
546
- async function normalizeProjectGrantRow(ctx, row) {
547
- const scope = await resolveGrantScopeIds(ctx, {
548
- topicId: row.topicId,
549
- projectId: row.projectId
550
- });
551
- return {
552
- ...row,
553
- ...scope.topicId ? { topicId: scope.topicId } : {},
554
- ...scope.projectId ?? scope.topicId ? { projectId: scope.projectId ?? scope.topicId } : {}
555
- };
556
- }
557
- async function normalizeProjectGrantRows(ctx, rows) {
558
- return await Promise.all(rows.map((row) => normalizeProjectGrantRow(ctx, row)));
559
- }
560
- async function listProjectGrantsByPrincipal(ctx, principalId) {
561
- const rows = await Promise.all(
562
- PROJECT_GRANT_STATUSES.map(
563
- (status) => ctx.db.query("projectGrants").withIndex(
564
- "by_principal_status",
565
- (q) => q.eq("principalId", principalId).eq("status", status)
566
- ).collect()
567
- )
568
- );
569
- return await normalizeProjectGrantRows(ctx, rows.flat());
570
- }
571
- async function listProjectGrantsByGroup(ctx, groupId) {
572
- const rows = await Promise.all(
573
- PROJECT_GRANT_STATUSES.map(
574
- (status) => ctx.db.query("projectGrants").withIndex(
575
- "by_group_status",
576
- (q) => q.eq("groupId", groupId).eq("status", status)
577
- ).collect()
578
- )
579
- );
580
- return await normalizeProjectGrantRows(ctx, rows.flat());
581
- }
582
- function buildScopeMatchers(inputScopeId, resolved) {
583
- return new Set(
584
- [inputScopeId, resolved.topicId, resolved.projectId].map((value) => normalizeString(value)).filter((value) => Boolean(value))
585
- );
586
- }
587
- function matchesResolvedScope(row, scopeIds) {
588
- const rowTopicId = normalizeString(row.topicId);
589
- const rowProjectId = normalizeString(row.projectId);
590
- return rowTopicId !== void 0 && scopeIds.has(rowTopicId) || rowProjectId !== void 0 && scopeIds.has(rowProjectId);
591
- }
592
- async function bridgeListProjectGrantsByTopicAndPrincipal(ctx, topicId, principalId) {
593
- const resolved = await resolveGrantScopeIds(ctx, { topicId });
594
- const scopeIds = buildScopeMatchers(topicId, resolved);
595
- const rows = await listProjectGrantsByPrincipal(ctx, principalId);
596
- return rows.filter((row) => matchesResolvedScope(row, scopeIds));
597
- }
598
- async function bridgeListProjectGrantsByTopicAndGroup(ctx, topicId, groupId) {
599
- const resolved = await resolveGrantScopeIds(ctx, { topicId });
600
- const scopeIds = buildScopeMatchers(topicId, resolved);
601
- const rows = await listProjectGrantsByGroup(ctx, groupId);
602
- return rows.filter((row) => matchesResolvedScope(row, scopeIds));
603
- }
604
- async function bridgeListProjectGrantsByPrincipalStatus(ctx, principalId, status) {
605
- const rows = await listProjectGrantsByPrincipal(ctx, principalId);
606
- return rows.filter((row) => row.status === status);
607
- }
608
- async function bridgeListProjectGrantsByGroupStatus(ctx, groupId, status) {
609
- const rows = await listProjectGrantsByGroup(ctx, groupId);
610
- return rows.filter((row) => row.status === status);
611
- }
612
- async function bridgeInsertProjectGrant(ctx, value) {
613
- const resolved = await resolveGrantScopeIds(ctx, value);
614
- return await ctx.db.insert("projectGrants", {
615
- ...value,
616
- ...resolved.topicId ? { topicId: resolved.topicId } : {},
617
- ...resolved.projectId ?? resolved.topicId ? { projectId: resolved.projectId ?? resolved.topicId } : {}
618
- });
619
- }
620
-
621
- // ../access-control/src/resolvers.ts
622
- async function findUserByClerkId(ctx, clerkId) {
623
- const normalizedClerkId = clerkId.trim();
624
- if (!normalizedClerkId) {
625
- return null;
626
- }
627
- if (typeof ctx.runQuery === "function") {
628
- try {
629
- const bridgedUser = await ctx.runQuery(api.users.getUserByClerkId, {
630
- clerkId: normalizedClerkId
631
- });
632
- if (bridgedUser) {
633
- return bridgedUser;
634
- }
635
- } catch {
636
- }
637
- }
638
- try {
639
- const users = await ctx.db.query("users").collect();
640
- return users.find((user) => String(user.clerkId ?? "") === normalizedClerkId) ?? null;
641
- } catch {
642
- return null;
643
- }
644
- }
645
- async function findUserByPrincipalId(ctx, principalId) {
646
- const normalizedPrincipalId = principalId.trim();
647
- if (!normalizedPrincipalId) {
648
- return null;
649
- }
650
- try {
651
- const users = await ctx.db.query("users").collect();
652
- return users.find(
653
- (user) => String(user.defaultPrincipalId ?? "") === normalizedPrincipalId
654
- ) ?? null;
655
- } catch {
656
- return null;
657
- }
658
- }
659
- async function findAgentByPrincipalId(ctx, principalId) {
660
- const normalizedPrincipalId = principalId.trim();
661
- if (!normalizedPrincipalId) {
662
- return null;
663
- }
664
- if (typeof ctx.runQuery === "function") {
665
- try {
666
- const bridgedAgent = await ctx.runQuery(
667
- api.agents.getAgentByPrincipalId,
668
- {
669
- principalId: normalizedPrincipalId
670
- }
671
- );
672
- if (bridgedAgent) {
673
- return bridgedAgent;
674
- }
675
- } catch {
676
- }
677
- }
678
- try {
679
- const agents = await ctx.db.query("agents").collect();
680
- return agents.find(
681
- (agent) => String(agent.principalId ?? "") === normalizedPrincipalId
682
- ) ?? null;
683
- } catch {
684
- return null;
685
- }
686
- }
687
- function defaultResolvers() {
688
- return {
689
- async getProject(ctx, topicId) {
690
- return await resolveTopicProjectOverlay(ctx, topicId, {
691
- idMode: "legacy",
692
- projectLikeOnly: false
693
- });
694
- },
695
- async listTopics(ctx) {
696
- return await listTopicProjectOverlays(ctx, { idMode: "legacy" });
697
- },
698
- async listTopicsByOwner(ctx, ownerId) {
699
- const topics = await listTopicProjectOverlays(ctx, { idMode: "legacy" });
700
- return topics.filter((topic) => topic.ownerId === ownerId);
701
- },
702
- async listTopicsByVisibility(ctx, visibility) {
703
- const topics = await listTopicProjectOverlays(ctx, { idMode: "legacy" });
704
- return topics.filter((topic) => topic.visibility === visibility);
705
- },
706
- async listProjectGrantsByProjectAndPrincipal(ctx, topicId, principalId) {
707
- return await bridgeListProjectGrantsByTopicAndPrincipal(
708
- ctx,
709
- topicId,
710
- principalId
711
- );
712
- },
713
- async listProjectGrantsByProjectAndGroup(ctx, topicId, groupId) {
714
- return await bridgeListProjectGrantsByTopicAndGroup(ctx, topicId, groupId);
715
- },
716
- async listProjectGrantsByPrincipalStatus(ctx, principalId, status) {
717
- return await bridgeListProjectGrantsByPrincipalStatus(
718
- ctx,
719
- principalId,
720
- status
721
- );
722
- },
723
- async listProjectGrantsByGroupStatus(ctx, groupId, status) {
724
- return await bridgeListProjectGrantsByGroupStatus(ctx, groupId, status);
725
- },
726
- async insertProjectGrant(ctx, value) {
727
- return await bridgeInsertProjectGrant(ctx, value);
728
- },
729
- async getAgentByPrincipalId(ctx, principalId) {
730
- return await findAgentByPrincipalId(ctx, principalId);
731
- },
732
- async getUserByClerkId(ctx, clerkId) {
733
- return await findUserByClerkId(ctx, clerkId);
734
- },
735
- async getUserByPrincipalId(ctx, principalId) {
736
- return await findUserByPrincipalId(ctx, principalId);
737
- }
738
- };
739
- }
740
- var resolverOverrides = {};
741
- function resolveAccessControlAppResolvers(_ctx) {
742
- return {
743
- ...defaultResolvers(),
744
- ...resolverOverrides
745
- };
746
- }
747
-
748
- // ../access-control/src/principalContext.ts
749
- function requireCanonicalResolvedUser(user, clerkId) {
750
- const resolved = user;
751
- if (!resolved) {
752
- throw new Error(
753
- `[AccessControl] Canonical user identity required for ${clerkId}. Sync users.upsertUser before user-bound access checks.`
754
- );
755
- }
756
- const { mcRole, defaultTenantId, defaultWorkspaceId, defaultPrincipalId } = resolved;
757
- if (mcRole !== "platform_admin" && mcRole !== "tenant_admin" && mcRole !== "workspace_admin" && mcRole !== "editor" && mcRole !== "viewer" && mcRole !== "auditor" && mcRole !== "service_agent") {
758
- throw new Error(
759
- `[AccessControl] Canonical MC role required for ${clerkId}. Re-sync Master Control identity before user-bound access checks.`
760
- );
761
- }
762
- if (typeof defaultTenantId !== "string" || defaultTenantId.trim().length === 0) {
763
- throw new Error(
764
- `[AccessControl] Canonical home tenant required for ${clerkId}. Re-sync Master Control identity before user-bound access checks.`
765
- );
766
- }
767
- if (typeof defaultWorkspaceId !== "string" || defaultWorkspaceId.trim().length === 0) {
768
- throw new Error(
769
- `[AccessControl] Canonical home workspace required for ${clerkId}. Re-sync Master Control identity before user-bound access checks.`
770
- );
771
- }
772
- if (typeof defaultPrincipalId !== "string" || defaultPrincipalId.trim().length === 0) {
773
- throw new Error(
774
- `[AccessControl] Canonical federated principal required for ${clerkId}. Re-sync Master Control identity before user-bound access checks.`
775
- );
776
- }
777
- return {
778
- mcRole,
779
- defaultTenantId: defaultTenantId.trim(),
780
- defaultWorkspaceId: defaultWorkspaceId.trim(),
781
- defaultPrincipalId: defaultPrincipalId.trim()
782
- };
783
- }
784
- function isPrincipalIdInput(value) {
785
- return value.startsWith("user:") || value.startsWith("group:") || value.startsWith("service:") || value.startsWith("agent:") || value.startsWith("external_viewer:");
786
- }
787
- async function resolveCanonicalUserRecord(ctx, actorId) {
788
- const normalizedActorId = actorId.trim();
789
- const clerkId = isPrincipalIdInput(normalizedActorId) && normalizedActorId.startsWith("user:") ? normalizedActorId.slice("user:".length) : normalizedActorId;
790
- const resolvers = resolveAccessControlAppResolvers();
791
- const resolvedByClerkId = await resolvers.getUserByClerkId(ctx, clerkId);
792
- if (resolvedByClerkId) {
793
- return {
794
- resolvedUser: resolvedByClerkId,
795
- clerkId,
796
- contextClerkId: clerkId
797
- };
798
- }
799
- const resolvedByPrincipalId = await resolvers.getUserByPrincipalId(
800
- ctx,
801
- normalizedActorId
802
- );
803
- return {
804
- resolvedUser: resolvedByPrincipalId ?? null,
805
- clerkId,
806
- contextClerkId: normalizedActorId.startsWith("user:") && clerkId.length > 0 ? clerkId : normalizedActorId
807
- };
808
- }
809
- function uniqRoles(roles) {
810
- const roleSet = /* @__PURE__ */ new Set();
811
- for (const role of roles) {
812
- if (role === "platform_admin" || role === "tenant_admin" || role === "workspace_admin" || role === "editor" || role === "viewer" || role === "auditor" || role === "service_agent") {
813
- roleSet.add(role);
814
- }
815
- }
816
- return [...roleSet];
817
- }
818
- function normalizeGroupIds(value) {
819
- if (!Array.isArray(value)) {
820
- return [];
821
- }
822
- return [...new Set(
823
- value.filter((entry) => typeof entry === "string").map((entry) => entry.trim()).filter(Boolean)
824
- )];
825
- }
826
- function requireServiceAgentUser(user, actorId) {
827
- const canonicalUser = requireCanonicalResolvedUser(user, actorId);
828
- if (canonicalUser.mcRole !== "service_agent") {
829
- throw new Error(
830
- `[AccessControl] Canonical service_agent identity required for ${actorId}. Sync users.upsertUser before agent-bound access checks.`
831
- );
832
- }
833
- return canonicalUser;
834
- }
835
- function requireCanonicalResolvedAgent(agent, actorId) {
836
- const resolved = agent;
837
- if (!resolved) {
838
- throw new Error(
839
- `[AccessControl] Agent "${actorId}" not found in agents or users table.`
840
- );
841
- }
842
- if (typeof resolved.principalId !== "string" || resolved.principalId.trim().length === 0) {
843
- throw new Error(
844
- `[AccessControl] Canonical agent principalId required for ${actorId}.`
845
- );
846
- }
847
- if (typeof resolved.tenantId !== "string" || resolved.tenantId.trim().length === 0) {
848
- throw new Error(
849
- `[AccessControl] Canonical home tenant required for ${actorId}.`
850
- );
851
- }
852
- if (typeof resolved.workspaceId !== "string" || resolved.workspaceId.trim().length === 0) {
853
- throw new Error(
854
- `[AccessControl] Canonical home workspace required for ${actorId}.`
855
- );
856
- }
857
- return {
858
- principalId: resolved.principalId.trim(),
859
- tenantId: resolved.tenantId.trim(),
860
- workspaceId: resolved.workspaceId.trim(),
861
- roles: uniqRoles(Array.isArray(resolved.roles) ? resolved.roles : []) ?? ["service_agent"],
862
- groupIds: normalizeGroupIds(resolved.groupIds)
863
- };
864
- }
865
- async function resolvePrincipalContext(ctx, actorId) {
866
- if (actorId.startsWith("agent:")) {
867
- const resolvers = resolveAccessControlAppResolvers();
868
- const resolvedAgent = await resolvers.getAgentByPrincipalId(ctx, actorId);
869
- if (resolvedAgent) {
870
- const agent = requireCanonicalResolvedAgent(
871
- resolvedAgent,
872
- actorId
873
- );
874
- return {
875
- principalId: agent.principalId,
876
- principalType: "service",
877
- clerkId: actorId,
878
- tenantId: agent.tenantId,
879
- workspaceId: agent.workspaceId,
880
- roles: agent.roles.length > 0 ? agent.roles : ["service_agent"],
881
- groupIds: agent.groupIds,
882
- isPlatformAdmin: false,
883
- isTenantAdmin: false,
884
- isWorkspaceAdmin: false,
885
- isSystemFallback: false
886
- };
887
- }
888
- const resolvedUser2 = await resolvers.getUserByClerkId(
889
- ctx,
890
- actorId
891
- );
892
- if (!resolvedUser2) {
893
- throw new Error(
894
- `[AccessControl] Agent "${actorId}" not found in agents or users table.`
895
- );
896
- }
897
- const user2 = requireServiceAgentUser(
898
- resolvedUser2,
899
- actorId
900
- );
901
- console.warn(
902
- `[AccessControl] Deprecated legacy service-agent fallback for ${actorId}; migrate this principal into identity.agents.`
903
- );
904
- return {
905
- principalId: user2.defaultPrincipalId,
906
- principalType: "service",
907
- clerkId: actorId,
908
- tenantId: user2.defaultTenantId,
909
- workspaceId: user2.defaultWorkspaceId,
910
- roles: ["service_agent"],
911
- groupIds: normalizeGroupIds(resolvedUser2?.principalGroupIds),
912
- isPlatformAdmin: false,
913
- isTenantAdmin: false,
914
- isWorkspaceAdmin: false,
915
- isSystemFallback: false
916
- };
917
- }
918
- const {
919
- resolvedUser,
920
- contextClerkId
921
- } = await resolveCanonicalUserRecord(ctx, actorId);
922
- const user = requireCanonicalResolvedUser(
923
- resolvedUser,
924
- contextClerkId
925
- );
926
- if (!user.defaultPrincipalId) {
927
- throw new Error(
928
- `[AccessControl] Canonical federated principal required for ${contextClerkId}. Re-sync Master Control identity before user-bound access checks.`
929
- );
930
- }
931
- if (user.mcRole === "service_agent") {
932
- return {
933
- principalId: user.defaultPrincipalId,
934
- principalType: "service",
935
- clerkId: contextClerkId,
936
- tenantId: user.defaultTenantId,
937
- workspaceId: user.defaultWorkspaceId,
938
- roles: ["service_agent"],
939
- groupIds: normalizeGroupIds(resolvedUser?.principalGroupIds),
940
- isPlatformAdmin: false,
941
- isTenantAdmin: false,
942
- isWorkspaceAdmin: false,
943
- isSystemFallback: false
944
- };
945
- }
946
- const principalId = user.defaultPrincipalId;
947
- const effectiveRole = user.mcRole;
948
- const roles = effectiveRole === "platform_admin" ? ["platform_admin", "tenant_admin"] : effectiveRole === "tenant_admin" ? ["tenant_admin"] : [effectiveRole];
949
- const tenantId = user.defaultTenantId;
950
- const workspaceId = user.defaultWorkspaceId;
951
- const isPlatformAdmin = effectiveRole === "platform_admin";
952
- return {
953
- principalId,
954
- principalType: "user",
955
- clerkId: contextClerkId,
956
- tenantId,
957
- workspaceId,
958
- roles: uniqRoles(roles),
959
- groupIds: normalizeGroupIds(resolvedUser?.principalGroupIds),
960
- isPlatformAdmin,
961
- isTenantAdmin: isPlatformAdmin || effectiveRole === "tenant_admin",
962
- isWorkspaceAdmin: isPlatformAdmin || effectiveRole === "tenant_admin" || effectiveRole === "workspace_admin",
963
- isSystemFallback: false
964
- };
965
- }
966
-
967
- // ../access-control/src/access.ts
968
- function isTopicInPrincipalTenant(topic, principalTenantId) {
969
- if (!topic.tenantId) {
970
- return false;
971
- }
972
- if (!principalTenantId) {
973
- return false;
974
- }
975
- return String(topic.tenantId) === String(principalTenantId);
976
- }
977
- function isTopicInPrincipalWorkspace(topic, principalWorkspaceId) {
978
- if (!topic.workspaceId) {
979
- return false;
980
- }
981
- if (!principalWorkspaceId) {
982
- return false;
983
- }
984
- return String(topic.workspaceId) === String(principalWorkspaceId);
985
- }
986
- function isLegacyUnscopedTopic(topic) {
987
- return !topic.tenantId || !topic.workspaceId;
988
- }
989
- function isGrantScopeAlignedToTopic(topic, grant) {
990
- if (topic.tenantId && grant.tenantId && String(topic.tenantId) !== String(grant.tenantId)) {
991
- return false;
992
- }
993
- if (topic.workspaceId && grant.workspaceId && String(topic.workspaceId) !== String(grant.workspaceId)) {
994
- return false;
995
- }
996
- return true;
997
- }
998
- function isGrantSourceAllowedForVisibility(visibility, source) {
999
- if (source !== "external_share") {
1000
- return true;
1001
- }
1002
- return visibility === "external" || visibility === "public";
1003
- }
1004
- function isGrantActive(grant) {
1005
- if (grant.status !== "active") {
1006
- return false;
1007
- }
1008
- if (grant.expiresAt !== void 0 && grant.expiresAt <= Date.now()) {
1009
- return false;
1010
- }
1011
- return true;
1012
- }
1013
- async function hasPrincipalGrant(ctx, args) {
1014
- const grants = await resolveAccessControlAppResolvers().listProjectGrantsByProjectAndPrincipal(
1015
- ctx,
1016
- args.topic._id,
1017
- args.principalId
1018
- );
1019
- if (grants.some(
1020
- (grant) => isGrantActive(grant) && isGrantScopeAlignedToTopic(args.topic, grant) && isGrantSourceAllowedForVisibility(
1021
- args.topic.visibility,
1022
- grant.source
1023
- ) && (!args.principalIsExternal || args.topic.visibility === "public" || grant.source === "external_share")
1024
- )) {
1025
- return true;
1026
- }
1027
- return false;
1028
- }
1029
- async function hasGroupGrant(ctx, args) {
1030
- if (args.groupIds.length === 0) {
1031
- return false;
1032
- }
1033
- for (const groupId of args.groupIds) {
1034
- const grants = await resolveAccessControlAppResolvers().listProjectGrantsByProjectAndGroup(ctx, args.topic._id, groupId);
1035
- if (grants.some(
1036
- (grant) => isGrantActive(grant) && isGrantScopeAlignedToTopic(args.topic, grant) && isGrantSourceAllowedForVisibility(
1037
- args.topic.visibility,
1038
- grant.source
1039
- )
1040
- )) {
1041
- return true;
1042
- }
1043
- }
1044
- return false;
1045
- }
1046
- function isExternalPrincipal(_ctx, _args) {
1047
- return false;
1048
- }
1049
- async function evaluateTopicAccessDetailed(ctx, args) {
1050
- if (args.legacyUserId) {
1051
- return {
1052
- hasAccess: true,
1053
- isAdmin: false,
1054
- isOwner: false,
1055
- isShared: false,
1056
- hasGrant: true,
1057
- isFirmVisible: true,
1058
- isExternalVisible: false,
1059
- isPublicVisible: false,
1060
- isTenantScopeMatch: true,
1061
- isWorkspaceScopeMatch: true,
1062
- isPrincipalExternal: false
1063
- };
1064
- }
1065
- const topic = await resolveAccessControlAppResolvers().getProject(
1066
- ctx,
1067
- args.topicId
1068
- );
1069
- if (!topic) {
1070
- return {
1071
- hasAccess: false,
1072
- isAdmin: false,
1073
- isOwner: false,
1074
- isShared: false,
1075
- hasGrant: false,
1076
- isFirmVisible: false,
1077
- isExternalVisible: false,
1078
- isPublicVisible: false,
1079
- isTenantScopeMatch: false,
1080
- isWorkspaceScopeMatch: false,
1081
- isPrincipalExternal: false
1082
- };
1083
- }
1084
- const { principalContext, legacyUserId } = args;
1085
- const userIsAdmin = principalContext.isPlatformAdmin;
1086
- const isOwner = topic.ownerId === legacyUserId;
1087
- const isShared = (topic.sharedWith ?? []).includes(legacyUserId);
1088
- const principalIsExternal = await isExternalPrincipal(ctx, {
1089
- groupIds: principalContext.groupIds,
1090
- topicTenantId: topic.tenantId,
1091
- topicWorkspaceId: topic.workspaceId
1092
- });
1093
- const hasPrincipalGrantResult = await hasPrincipalGrant(ctx, {
1094
- topic,
1095
- principalId: principalContext.principalId,
1096
- principalIsExternal
1097
- });
1098
- const hasGroupGrantResult = await hasGroupGrant(ctx, {
1099
- topic,
1100
- groupIds: principalContext.groupIds
1101
- });
1102
- const hasGrant = isShared || hasPrincipalGrantResult || hasGroupGrantResult;
1103
- const legacyUnscoped = isLegacyUnscopedTopic(topic);
1104
- const tenantScopeMatch = isTopicInPrincipalTenant(
1105
- topic,
1106
- principalContext.tenantId
1107
- );
1108
- const workspaceScopeMatch = isTopicInPrincipalWorkspace(
1109
- topic,
1110
- principalContext.workspaceId
1111
- );
1112
- const isPublicVisible = topic.visibility === "public";
1113
- const isFirmVisible = topic.visibility === "firm" && !legacyUnscoped && tenantScopeMatch && workspaceScopeMatch && !principalIsExternal;
1114
- const hasScopedGrant = hasGrant && (legacyUnscoped || tenantScopeMatch && workspaceScopeMatch);
1115
- const isExternalVisible = topic.visibility === "external" && hasScopedGrant;
1116
- const hasAccess = userIsAdmin || isOwner || hasScopedGrant || isPublicVisible || isFirmVisible;
1117
- return {
1118
- hasAccess,
1119
- isAdmin: userIsAdmin,
1120
- isOwner,
1121
- isShared,
1122
- hasGrant,
1123
- isFirmVisible,
1124
- isExternalVisible,
1125
- isPublicVisible,
1126
- isTenantScopeMatch: tenantScopeMatch,
1127
- isWorkspaceScopeMatch: workspaceScopeMatch,
1128
- isPrincipalExternal: principalIsExternal
1129
- };
1130
- }
1131
- async function checkTopicAccessDetailed(ctx, topicId, userId) {
1132
- const principalContext = await resolvePrincipalContext(ctx, userId);
1133
- return evaluateTopicAccessDetailed(ctx, {
1134
- topicId,
1135
- legacyUserId: userId,
1136
- principalContext
1137
- });
1138
- }
1139
- async function checkTopicAccess(ctx, topicId, userId) {
1140
- const result = await checkTopicAccessDetailed(ctx, topicId, userId);
1141
- return result.hasAccess;
1142
- }
1143
- async function checkScopeAccess(ctx, scopeId, userId) {
1144
- try {
1145
- const topic = await ctx.db.get(scopeId);
1146
- if (topic && topic.name !== void 0 && topic.type !== void 0) {
1147
- return true;
1148
- }
1149
- } catch {
1150
- }
1151
- try {
1152
- return await checkTopicAccess(ctx, scopeId, userId);
1153
- } catch {
1154
- return false;
1155
- }
1156
- }
1157
- var checkProjectAccess = checkTopicAccess;
1158
-
1159
- // ../access-control/src/audience.ts
1160
- var AUDIENCE_CLASS_RANK = {
1161
- public: 0,
1162
- restricted_external: 1,
1163
- internal: 2
1164
- };
1165
- function normalizeKey(key) {
1166
- return (key ?? "").trim().toLowerCase().replace(/[^a-z0-9:_-]+/g, "_").replace(/_+/g, "_").replace(/^_+|_+$/g, "");
1167
- }
1168
- function normalizeAudienceKey(key) {
1169
- return normalizeKey(key);
1170
- }
1171
- function classFromAudienceKey(audienceKey, fallback = "internal") {
1172
- const key = normalizeKey(audienceKey);
1173
- if (!key) {
1174
- return fallback;
1175
- }
1176
- if (key === "internal") {
1177
- return "internal";
1178
- }
1179
- if (key === "public") {
1180
- return "public";
1181
- }
1182
- if (key === "lp" || key === "external" || key === "client" || key === "partner" || key === "portfolio" || key === "network" || key === "restricted_external") {
1183
- return "restricted_external";
1184
- }
1185
- return fallback;
1186
- }
1187
- function canAudienceClassAccess(viewerClass, resourceClass) {
1188
- return AUDIENCE_CLASS_RANK[viewerClass] >= AUDIENCE_CLASS_RANK[resourceClass];
1189
- }
1190
-
1191
- // ../access-control/src/audienceRegistry.ts
1192
- var DEFAULT_AUDIENCES = [
1193
- {
1194
- audienceKey: "internal",
1195
- audienceLabel: "Internal",
1196
- audienceClass: "internal"
1197
- },
1198
- {
1199
- audienceKey: "lp",
1200
- audienceLabel: "Limited Partners",
1201
- audienceClass: "restricted_external"
1202
- },
1203
- {
1204
- audienceKey: "public",
1205
- audienceLabel: "Public",
1206
- audienceClass: "public"
1207
- }
1208
- ];
1209
- var AUDIENCE_CLASS_PRIORITY = {
1210
- internal: 0,
1211
- restricted_external: 1,
1212
- public: 2
1213
- };
1214
- function normalizeRegistryRow(row) {
1215
- return {
1216
- audienceKey: normalizeAudienceKey(row.audienceKey),
1217
- audienceLabel: row.audienceLabel,
1218
- audienceClass: row.audienceClass,
1219
- workspaceId: row.workspaceId
1220
- };
1221
- }
1222
- function dedupeRegistryRows(rows) {
1223
- const byKey = /* @__PURE__ */ new Map();
1224
- for (const row of rows) {
1225
- const key = normalizeAudienceKey(row.audienceKey);
1226
- if (!key) {
1227
- continue;
1228
- }
1229
- const existing = byKey.get(key);
1230
- const isWorkspaceScoped = row.workspaceId !== void 0;
1231
- const existingWorkspaceScoped = existing?.workspaceId !== void 0;
1232
- if (!existing || isWorkspaceScoped && !existingWorkspaceScoped) {
1233
- byKey.set(key, {
1234
- ...row,
1235
- audienceKey: key
1236
- });
1237
- }
1238
- }
1239
- const normalized = [...byKey.values()];
1240
- normalized.sort((a, b) => {
1241
- const classDelta = AUDIENCE_CLASS_PRIORITY[a.audienceClass] - AUDIENCE_CLASS_PRIORITY[b.audienceClass];
1242
- if (classDelta !== 0) {
1243
- return classDelta;
1244
- }
1245
- return a.audienceKey.localeCompare(b.audienceKey);
1246
- });
1247
- return normalized;
1248
- }
1249
- async function queryRegistryRows(ctx, args) {
1250
- if (!args.tenantId) {
1251
- return [...DEFAULT_AUDIENCES];
1252
- }
1253
- const rows = await ctx.db.query("platformAudiences").withIndex("by_tenantId", (q) => q.eq("tenantId", args.tenantId)).collect();
1254
- const workspaceIdString = args.workspaceId ? String(args.workspaceId) : null;
1255
- const tenantScoped = rows.filter((row) => row.status === "active");
1256
- const applicable = tenantScoped.filter((row) => {
1257
- if (!row.workspaceId) {
1258
- return true;
1259
- }
1260
- if (!workspaceIdString) {
1261
- return false;
1262
- }
1263
- return String(row.workspaceId) === workspaceIdString;
1264
- });
1265
- return dedupeRegistryRows([
1266
- ...DEFAULT_AUDIENCES,
1267
- ...applicable.map(
1268
- (row) => normalizeRegistryRow({
1269
- audienceKey: row.audienceKey,
1270
- audienceLabel: row.audienceLabel,
1271
- audienceClass: row.audienceClass,
1272
- workspaceId: row.workspaceId
1273
- })
1274
- )
1275
- ]);
1276
- }
1277
- async function listAudienceRegistryRows(ctx, args) {
1278
- return queryRegistryRows(ctx, args);
1279
- }
1280
-
1281
- // ../access-control/src/auth.ts
1282
- async function getCurrentUserId(ctx) {
1283
- const identity = await ctx.auth.getUserIdentity();
1284
- return identity?.subject ?? null;
1285
- }
1286
- componentsGeneric();
1287
- var internal = anyApi;
1288
-
1289
- // ../schema-management/src/enumValidation.ts
1290
- var BUILTIN_ENUM_FALLBACK = {
1291
- topic_type: /* @__PURE__ */ new Set([
1292
- "domain",
1293
- "theme",
1294
- "deal",
1295
- "strategy",
1296
- "constitution",
1297
- "project",
1298
- "portfolio",
1299
- "architecture",
1300
- "capability",
1301
- "runtime",
1302
- "interface",
1303
- "governance",
1304
- "operations",
1305
- "security",
1306
- "data"
1307
- ]),
1308
- branch_schema: /* @__PURE__ */ new Set(["pillar", "track", "dimension", "axis", "phase"]),
1309
- lens_perspective_type: /* @__PURE__ */ new Set([
1310
- "investigation",
1311
- "monitoring",
1312
- "analysis",
1313
- "comparison",
1314
- "taxonomy"
1315
- ]),
1316
- belief_type: /* @__PURE__ */ new Set([
1317
- "belief",
1318
- "hypothesis",
1319
- "principle",
1320
- "invariant",
1321
- "assumption",
1322
- "tenet",
1323
- "prior",
1324
- "preference",
1325
- "goal",
1326
- "forecast",
1327
- "decision",
1328
- "constraint",
1329
- "tradeoff",
1330
- "policy",
1331
- "implementation_choice",
1332
- // Coding intelligence domain
1333
- "implementation_decision",
1334
- "interface_contract",
1335
- "migration_state",
1336
- "code_pattern",
1337
- "deprecation_notice"
1338
- ]),
1339
- edge_type: /* @__PURE__ */ new Set([
1340
- // === 6 CANONICAL EPISTEMIC TYPES ===
1341
- "supports",
1342
- // L3↔L3: belief bears on belief (weight -1 to +1)
1343
- "informs",
1344
- // L2→L3: evidence bears on belief
1345
- "depends_on",
1346
- // L3→L3, Q→Q: structural gate
1347
- "derived_from",
1348
- // Any→Any: provenance chain
1349
- "contains",
1350
- // Any→Any: hierarchy, scoping, membership
1351
- "tests",
1352
- // Q→L3: question interrogates belief
1353
- // === STRUCTURAL / LIFECYCLE ===
1354
- "supersedes",
1355
- "responds_to",
1356
- "belongs_to",
1357
- "relates_to_thesis",
1358
- // === ONTOLOGICAL (tenant-extensible) ===
1359
- "works_at",
1360
- "invested_in",
1361
- "competes_with",
1362
- "participates_in",
1363
- "founded_by",
1364
- "evaluates",
1365
- "performs",
1366
- "function_in",
1367
- "impacts",
1368
- "raised_from",
1369
- "mentioned_in",
1370
- "perspective_on",
1371
- "plays_theme"
1372
- ]),
1373
- worktree_type: /* @__PURE__ */ new Set([
1374
- "belief_test",
1375
- "existential",
1376
- "contradiction",
1377
- "refinement",
1378
- "coverage",
1379
- "discovery",
1380
- "clarification",
1381
- "confirmation"
1382
- ]),
1383
- worktree_phase: /* @__PURE__ */ new Set([
1384
- "cluster_mapping",
1385
- "hypothesis_formation",
1386
- "question_generation",
1387
- "evidence_collection",
1388
- "synthesis",
1389
- "decision",
1390
- "retrospective"
1391
- ]),
1392
- activity_type: /* @__PURE__ */ new Set([
1393
- "create",
1394
- "update",
1395
- "review",
1396
- "merge",
1397
- "archive",
1398
- "comment",
1399
- "status_change",
1400
- "evidence_added",
1401
- "question_added"
1402
- ])
1403
- };
1404
- function normalizeEnumValue(value) {
1405
- return value.trim().toLowerCase();
1406
- }
1407
- async function validateSchemaEnumValue(ctx, args) {
1408
- const normalized = normalizeEnumValue(args.value);
1409
- if (!normalized) {
1410
- return { valid: false, source: "none" };
1411
- }
1412
- try {
1413
- const validFromSchema = await ctx.runQuery(
1414
- internal.schemaConfig.internalValidate,
1415
- {
1416
- category: args.category,
1417
- value: normalized,
1418
- tenantId: args.tenantId
1419
- }
1420
- );
1421
- if (validFromSchema) {
1422
- return { valid: true, source: "schema" };
1423
- }
1424
- } catch {
1425
- }
1426
- const fallback = BUILTIN_ENUM_FALLBACK[args.category];
1427
- if (fallback.has(normalized)) {
1428
- return { valid: true, source: "builtin" };
1429
- }
1430
- return { valid: false, source: "none" };
1431
- }
1432
- async function assertSchemaEnumValue(ctx, args) {
1433
- if (typeof args.value !== "string") {
1434
- return;
1435
- }
1436
- const normalized = normalizeEnumValue(args.value);
1437
- if (!normalized) {
1438
- return;
1439
- }
1440
- const validation = await validateSchemaEnumValue(ctx, {
1441
- category: args.category,
1442
- value: normalized,
1443
- tenantId: args.tenantId
1444
- });
1445
- if (!validation.valid) {
1446
- const tenantHint = args.tenantId ? ` for tenant ${args.tenantId}` : "";
1447
- throw new Error(
1448
- `[${args.context}] Invalid value "${normalized}" for category "${args.category}"${tenantHint}. Add it to schemaEnumConfig before use.`
1449
- );
1450
- }
1451
- return normalized;
1452
- }
1453
- var permissiveReturn = v.optional(v.any());
1454
- var looseJsonObject = v.record(v.string(), v.any());
1455
- var looseJsonArray = v.array(v.any());
1456
- v.union(
1457
- v.string(),
1458
- v.number(),
1459
- v.boolean(),
1460
- v.null(),
1461
- looseJsonObject,
1462
- looseJsonArray
1463
- );
1464
-
1465
14
  // src/beliefLifecycle.ts
1466
15
  var BELIEF_STATUS_VALUES = [
1467
16
  "assumption",
@@ -1478,30 +27,30 @@ var RESOLVED_PREDICTION_OUTCOMES = [
1478
27
  function isBeliefLifecycleStatus(value) {
1479
28
  return typeof value === "string" && BELIEF_STATUS_VALUES.includes(value);
1480
29
  }
1481
- function normalizeBeliefConfidence(confidence2) {
1482
- if (typeof confidence2 !== "number" || !Number.isFinite(confidence2)) {
30
+ function normalizeBeliefConfidence(confidence) {
31
+ if (typeof confidence !== "number" || !Number.isFinite(confidence)) {
1483
32
  return null;
1484
33
  }
1485
- if (confidence2 >= 0 && confidence2 <= 1) {
1486
- return confidence2;
34
+ if (confidence >= 0 && confidence <= 1) {
35
+ return confidence;
1487
36
  }
1488
- if (confidence2 > 1 && confidence2 <= 100) {
1489
- return confidence2 / 100;
37
+ if (confidence > 1 && confidence <= 100) {
38
+ return confidence / 100;
1490
39
  }
1491
40
  return null;
1492
41
  }
1493
- function isResolvedByConfidence(confidence2) {
1494
- const normalized = normalizeBeliefConfidence(confidence2);
42
+ function isResolvedByConfidence(confidence) {
43
+ const normalized = normalizeBeliefConfidence(confidence);
1495
44
  if (normalized === null) {
1496
45
  return false;
1497
46
  }
1498
47
  return normalized <= 0 || normalized >= 1;
1499
48
  }
1500
- function hasResolvedPredictionOutcome(predictionMeta2) {
1501
- if (!predictionMeta2 || typeof predictionMeta2 !== "object") {
49
+ function hasResolvedPredictionOutcome(predictionMeta) {
50
+ if (!predictionMeta || typeof predictionMeta !== "object") {
1502
51
  return false;
1503
52
  }
1504
- const outcome = predictionMeta2.outcome;
53
+ const outcome = predictionMeta.outcome;
1505
54
  return typeof outcome === "string" && RESOLVED_PREDICTION_OUTCOMES.includes(outcome);
1506
55
  }
1507
56
  function getPredictionMetaFromMetadata(metadata) {
@@ -1553,9 +102,9 @@ function promoteBeliefStatusAfterScoring(status, opts) {
1553
102
  }
1554
103
  return status === "fact" ? "fact" : "belief";
1555
104
  }
1556
- var api2 = anyApi;
105
+ var api = anyApi;
1557
106
  componentsGeneric();
1558
- var internal2 = anyApi;
107
+ var internal = anyApi;
1559
108
  var internalMutation = internalMutationGeneric;
1560
109
  var internalQuery = internalQueryGeneric;
1561
110
  var mutation = mutationGeneric;
@@ -1571,8 +120,6 @@ var containsPropagationSpec = {
1571
120
  operator: () => null,
1572
121
  description: "Structural containment only. Traversed for explicit semantics, but it never propagates opinions."
1573
122
  };
1574
-
1575
- // src/edges/utils.ts
1576
123
  function readEdgeMetadata(edge) {
1577
124
  return {
1578
125
  constraint: edge.constraint ?? void 0,
@@ -1649,8 +196,6 @@ var contradictsPropagationSpec = {
1649
196
  },
1650
197
  description: "Legacy contradiction edges move negative pressure in either direction, but never beyond one hop."
1651
198
  };
1652
-
1653
- // src/edges/dependsOn.ts
1654
199
  var dependsOnPropagationSpec = {
1655
200
  edgeType: "depends_on",
1656
201
  direction: "incoming",
@@ -1666,8 +211,18 @@ var dependsOnPropagationSpec = {
1666
211
  if (metadata.conditionalA && metadata.conditionalNotA) {
1667
212
  const deducedOpinion = conditionalDeduction(
1668
213
  dampedSource,
1669
- metadata.conditionalA,
1670
- metadata.conditionalNotA,
214
+ mkOpinion(
215
+ metadata.conditionalA.b,
216
+ metadata.conditionalA.d,
217
+ metadata.conditionalA.u,
218
+ metadata.conditionalA.a
219
+ ),
220
+ mkOpinion(
221
+ metadata.conditionalNotA.b,
222
+ metadata.conditionalNotA.d,
223
+ metadata.conditionalNotA.u,
224
+ metadata.conditionalNotA.a
225
+ ),
1671
226
  targetOpinion.a
1672
227
  );
1673
228
  return annotateRationale(
@@ -1839,606 +394,17 @@ function getTraversalDirections(direction) {
1839
394
  }
1840
395
  return ["outgoing", "incoming"];
1841
396
  }
1842
- v.number();
1843
- v.union(
1844
- v.literal("very_high"),
1845
- // 0.9+
1846
- v.literal("high"),
1847
- // 0.7-0.9
1848
- v.literal("medium"),
1849
- // 0.4-0.7
1850
- v.literal("low"),
1851
- // 0.2-0.4
1852
- v.literal("very_low")
1853
- // 0-0.2
1854
- );
1855
- v.union(
1856
- v.literal(1),
1857
- // Critical
1858
- v.literal(2),
1859
- // High
1860
- v.literal(3),
1861
- // Medium
1862
- v.literal(4),
1863
- // Low
1864
- v.literal(5)
1865
- // Backlog
1866
- );
1867
- v.union(
1868
- v.literal("critical"),
1869
- v.literal("high"),
1870
- v.literal("medium"),
1871
- v.literal("low"),
1872
- v.literal("backlog")
1873
- );
1874
- v.union(
1875
- v.literal("active"),
1876
- v.literal("paused"),
1877
- v.literal("completed"),
1878
- v.literal("archived")
1879
- );
1880
- v.union(
1881
- v.literal("pending"),
1882
- v.literal("processing"),
1883
- v.literal("completed"),
1884
- v.literal("failed")
1885
- );
1886
- v.object({
1887
- crunchbaseId: v.optional(v.string()),
1888
- linkedinUrl: v.optional(v.string()),
1889
- pitchbookId: v.optional(v.string()),
1890
- twitterUrl: v.optional(v.string()),
1891
- domain: v.optional(v.string())
1892
- });
1893
- var sourceType = v.union(
1894
- v.literal("proprietary"),
1895
- // Internal Stack research
1896
- v.literal("primary"),
1897
- // Direct interviews, calls
1898
- v.literal("secondary"),
1899
- // Published sources
1900
- v.literal("ai_generated"),
1901
- // AI-synthesized
1902
- v.literal("user_input"),
1903
- // Manual user entry
1904
- v.literal("inferred")
1905
- // System inference
1906
- );
1907
- v.object({
1908
- sourceType: v.optional(sourceType),
1909
- sourceId: v.optional(v.string()),
1910
- // Reference to source entity
1911
- sourceUrl: v.optional(v.string()),
1912
- sourceDate: v.optional(v.number()),
1913
- sourceName: v.optional(v.string())
1914
- });
1915
- v.object({
1916
- cursor: v.optional(v.string()),
1917
- limit: v.optional(v.number())
1918
- });
1919
- v.object({
1920
- hasMore: v.boolean(),
1921
- nextCursor: v.optional(v.string()),
1922
- totalCount: v.optional(v.number())
1923
- });
1924
- var richTextContent = v.object({
1925
- type: v.literal("doc"),
1926
- content: looseJsonArray
1927
- });
1928
- v.union(v.string(), richTextContent);
1929
- v.object({
1930
- promptTokens: v.optional(v.number()),
1931
- completionTokens: v.optional(v.number()),
1932
- totalTokens: v.optional(v.number())
1933
- });
1934
- v.object({
1935
- fileName: v.optional(v.string()),
1936
- fileSize: v.optional(v.number()),
1937
- mimeType: v.optional(v.string()),
1938
- storageId: v.optional(v.id("_storage")),
1939
- externalUrl: v.optional(v.string())
1940
- });
1941
-
1942
- // ../schema-management/src/spine/tables/epistemicNodes.ts
1943
- var nodeType = v.union(
1944
- // --- L4: Audit Targets (decisions, outcomes) ---
1945
- v.literal("decision"),
1946
- // Investment decision with knowledge horizon snapshot
1947
- // --- L3: Traversal Anchors (epistemic structure) ---
1948
- v.literal("belief"),
1949
- // Structured conviction (immutable formulation)
1950
- v.literal("question"),
1951
- // Unit of uncertainty
1952
- v.literal("theme"),
1953
- // Investment thesis / conviction cluster
1954
- v.literal("deal"),
1955
- // Investment evaluation process
1956
- v.literal("topic"),
1957
- // Hierarchical knowledge container
1958
- // --- L2: Compression Boundary (minimum reasoning unit) ---
1959
- v.literal("claim"),
1960
- // Atomic assertion that can be true/false
1961
- v.literal("evidence"),
1962
- // Interpreted signal linked to beliefs
1963
- v.literal("synthesis"),
1964
- // Primers, deep research
1965
- v.literal("answer"),
1966
- // Immutable answer snapshot for a question
1967
- // --- L1: Terminal Leaves (non-traversable, grounding) ---
1968
- v.literal("atomic_fact"),
1969
- // Raw fact from source (not interpreted)
1970
- v.literal("excerpt"),
1971
- // Direct quote from source document
1972
- v.literal("source"),
1973
- // News, documents, transcripts
1974
- // --- Ontological Entities (things in the world) ---
1975
- v.literal("company"),
1976
- // Organization (subtype: private, corporate, portfolio)
1977
- v.literal("person"),
1978
- // Individual (founder, expert, LP, contact)
1979
- v.literal("investor"),
1980
- // Investment entity (subtype: vc, lp, cvc, pe, family_office, angel)
1981
- v.literal("function"),
1982
- // What a company does (from classifier)
1983
- v.literal("value_chain")
1984
- // Market structure / value flow
1985
- );
1986
- var epistemicLayer = v.union(
1987
- v.literal("L4"),
1988
- // Decisions, outcomes - audit targets
1989
- v.literal("L3"),
1990
- // Beliefs, questions, themes - traversal anchors
1991
- v.literal("L2"),
1992
- // Claims, evidence, synthesis - compression boundary
1993
- v.literal("L1"),
1994
- // Atomic facts, excerpts, sources - terminal leaves
1995
- v.literal("ontological"),
1996
- // Companies, people, etc - not epistemic
1997
- v.literal("organizational")
1998
- // Topics, lenses, worktrees — structural containers
1999
- );
2000
- var nodeStatus = v.union(
2001
- v.literal("active"),
2002
- v.literal("superseded"),
2003
- // Replaced by newer version
2004
- v.literal("archived"),
2005
- v.literal("deleted")
2006
- );
2007
- var sourceType2 = v.union(
2008
- v.literal("human"),
2009
- // User created directly
2010
- v.literal("ai_extracted"),
2011
- // LLM extracted from a source
2012
- v.literal("ai_generated"),
2013
- // LLM synthesized/created
2014
- v.literal("imported"),
2015
- // External system import
2016
- v.literal("system"),
2017
- // System-generated (migrations, classifiers)
2018
- v.literal("verified"),
2019
- // Human-verified source
2020
- v.literal("proprietary")
2021
- // Proprietary/internal data
2022
- );
2023
- var verificationStatus = v.union(
2024
- v.literal("unverified"),
2025
- v.literal("human_verified"),
2026
- v.literal("ai_verified"),
2027
- v.literal("contradicted"),
2028
- v.literal("outdated")
2029
- );
2030
- var syncStatus = v.union(
2031
- v.literal("synced"),
2032
- // Node and edges fully synced to Neo4j
2033
- v.literal("pending_edges"),
2034
- // Node created, edges being created
2035
- v.literal("edge_creation_failed")
2036
- // Edge creation failed, needs retry
2037
- );
2038
- var audienceLabel = v.string();
2039
- var sensitivityTier = v.union(
2040
- v.literal("low"),
2041
- v.literal("medium"),
2042
- v.literal("high"),
2043
- v.literal("restricted")
2044
- );
2045
- var exportClass = v.union(
2046
- v.literal("internal_only"),
2047
- v.literal("client_safe"),
2048
- v.literal("public_safe"),
2049
- v.literal("restricted")
2050
- );
2051
- var anonymizationClass = v.union(
2052
- v.literal("none"),
2053
- v.literal("standard"),
2054
- v.literal("strict")
2055
- );
2056
- var epistemicStatus = v.union(
2057
- v.literal("hypothesis"),
2058
- // Initial conjecture, low evidence
2059
- v.literal("emerging"),
2060
- // Building evidence, gaining traction
2061
- v.literal("established"),
2062
- // Well-evidenced, core to thesis
2063
- v.literal("challenged"),
2064
- // Contradicting evidence appeared
2065
- v.literal("assumption"),
2066
- // Taken as given, not actively tested
2067
- v.literal("deprecated")
2068
- // Superseded or abandoned
2069
- );
2070
- var beliefStatus = v.union(
2071
- v.literal("assumption"),
2072
- v.literal("hypothesis"),
2073
- v.literal("belief"),
2074
- v.literal("fact")
2075
- );
2076
- var reversibility = v.union(
2077
- v.literal("irreversible"),
2078
- // One-way door decision
2079
- v.literal("hard_to_reverse"),
2080
- // Significant cost to undo
2081
- v.literal("reversible"),
2082
- // Can change course with moderate effort
2083
- v.literal("trivial")
2084
- // Easy to adjust
2085
- );
2086
- var predictionOutcome = v.union(
2087
- v.literal("pending"),
2088
- v.literal("confirmed"),
2089
- v.literal("disconfirmed"),
2090
- v.literal("partial"),
2091
- v.literal("expired")
2092
- );
2093
- var predictionMeta = v.object({
2094
- isPrediction: v.boolean(),
2095
- registeredAt: v.number(),
2096
- // When prediction was made
2097
- expectedBy: v.optional(v.number()),
2098
- // When we expect resolution
2099
- outcome: v.optional(predictionOutcome),
2100
- outcomeRecordedAt: v.optional(v.number()),
2101
- outcomeEvidenceId: v.optional(v.string()),
2102
- // globalId of confirming evidence
2103
- confidenceAtPrediction: v.optional(v.number()),
2104
- // 0-1
2105
- actualVsPredicted: v.optional(v.string())
2106
- // Notes on how outcome compared
2107
- });
2108
- var methodology = v.union(
2109
- // Primary Research (high value)
2110
- v.literal("primary_research"),
2111
- // Direct investigation
2112
- v.literal("expert_interview"),
2113
- // Expert call/interview
2114
- v.literal("customer_interview"),
2115
- // Customer research
2116
- v.literal("field_observation"),
2117
- // On-site observation
2118
- v.literal("proprietary_data"),
2119
- // Internal data analysis
2120
- // Secondary Research
2121
- v.literal("desk_research"),
2122
- // Public sources
2123
- v.literal("regulatory_filing"),
2124
- // SEC, regulatory docs
2125
- v.literal("news_article"),
2126
- // News/press
2127
- v.literal("academic_paper"),
2128
- // Academic research
2129
- // AI-Assisted
2130
- v.literal("ai_synthesis"),
2131
- // AI-generated synthesis
2132
- v.literal("ai_extraction")
2133
- // AI-extracted from source
2134
- );
2135
- var informationAsymmetry = v.union(
2136
- v.literal("proprietary"),
2137
- // Only we have this
2138
- v.literal("early"),
2139
- // We're early but others will get it
2140
- v.literal("common")
2141
- // Everyone has access
2142
- );
2143
- var temporalNature = v.union(
2144
- v.literal("factual"),
2145
- // Resolved outcome. Grounded in reality.
2146
- v.literal("forecast"),
2147
- // Prediction. Will resolve. Discounted weight.
2148
- v.literal("unknown")
2149
- // Not yet classified.
2150
- );
2151
- var questionType = v.union(
2152
- v.literal("validation"),
2153
- // Does evidence support this belief?
2154
- v.literal("falsification"),
2155
- // What would prove this belief wrong?
2156
- v.literal("assumption_probe"),
2157
- // Is this unstated assumption true?
2158
- v.literal("prediction_test"),
2159
- // Will this predicted outcome occur?
2160
- v.literal("counterfactual"),
2161
- // What would we expect if X were false?
2162
- v.literal("discovery"),
2163
- // What don't we know yet?
2164
- v.literal("clarification"),
2165
- // What does X actually mean?
2166
- v.literal("comparison"),
2167
- // How does X compare to Y?
2168
- v.literal("causal"),
2169
- // What caused X?
2170
- v.literal("mechanism"),
2171
- // How does X work?
2172
- v.literal("general")
2173
- // Unclassified
2174
- );
2175
- var questionPriority = v.union(
2176
- v.literal("critical"),
2177
- // Blocks decision-making
2178
- v.literal("high"),
2179
- // Important for thesis
2180
- v.literal("medium"),
2181
- // Would be nice to know
2182
- v.literal("low")
2183
- // Background/curiosity
2184
- );
2185
- var answerQuality = v.union(
2186
- v.literal("definitive"),
2187
- // Clear, well-supported
2188
- v.literal("strong"),
2189
- // Good evidence, high confidence
2190
- v.literal("moderate"),
2191
- // Some evidence
2192
- v.literal("weak"),
2193
- // Limited evidence
2194
- v.literal("speculative"),
2195
- // Mostly conjecture
2196
- v.literal("unanswered")
2197
- // No answer yet
2198
- );
2199
- var consensusView = v.union(
2200
- v.literal("aligned"),
2201
- // We agree with market consensus
2202
- v.literal("ahead_of"),
2203
- // We see this before consensus does
2204
- v.literal("contrarian"),
2205
- // We actively disagree with consensus
2206
- v.literal("orthogonal"),
2207
- // We're looking at something consensus isn't discussing
2208
- v.literal("unknown")
2209
- // We don't know what consensus thinks
2210
- );
2211
- var themeConviction = v.union(
2212
- v.literal("high"),
2213
- // Strong conviction, actively deploying
2214
- v.literal("medium"),
2215
- // Building conviction
2216
- v.literal("low"),
2217
- // Exploring, not convicted
2218
- v.literal("negative")
2219
- // Actively avoiding
2220
- );
2221
- var decisionType = v.union(
2222
- v.literal("invest"),
2223
- v.literal("pass"),
2224
- v.literal("follow_on"),
2225
- v.literal("exit"),
2226
- v.literal("deep_dive"),
2227
- v.literal("monitor"),
2228
- v.literal("deprioritize"),
2229
- v.literal("thesis_adopt"),
2230
- v.literal("thesis_revise"),
2231
- v.literal("thesis_abandon")
2232
- );
2233
- var decisionOutcome = v.union(
2234
- v.literal("pending"),
2235
- v.literal("successful"),
2236
- v.literal("unsuccessful"),
2237
- v.literal("mixed"),
2238
- v.literal("unknown")
2239
- );
2240
- var externalIds2 = v.object({
2241
- crunchbase: v.optional(v.string()),
2242
- linkedin: v.optional(v.string()),
2243
- pitchbook: v.optional(v.string()),
2244
- twitter: v.optional(v.string()),
2245
- website: v.optional(v.string())
2246
- });
2247
- defineTable({
2248
- // === IDENTITY ===
2249
- globalId: v.string(),
2250
- // UUID - survives migration to Neo4j
2251
- // === TYPE ===
2252
- nodeType,
2253
- // === EPISTEMIC LAYER ===
2254
- epistemicLayer: v.optional(epistemicLayer),
2255
- // === SUBTYPE (for typed entities) ===
2256
- subtype: v.optional(v.string()),
2257
- // company: private|corporate|portfolio, investor: vc|lp|cvc|pe|family_office|angel
2258
- // === CONTENT ===
2259
- canonicalText: v.string(),
2260
- // The core content (belief statement, company name, etc.)
2261
- contentHash: v.string(),
2262
- // SHA256(nodeType + canonicalText) for deduplication
2263
- // Extended content (for sources/syntheses)
2264
- content: v.optional(v.string()),
2265
- // Full text for documents/articles
2266
- contentType: v.optional(v.string()),
2267
- // "markdown", "html", "pdf", "text"
2268
- // === METADATA ===
2269
- title: v.optional(v.string()),
2270
- // Display title
2271
- tags: v.optional(v.array(v.string())),
2272
- domain: v.optional(v.string()),
2273
- // For companies: website domain
2274
- // Type-specific metadata (flexible object - LEGACY)
2275
- // New code should use the typed fields below when available
2276
- metadata: v.optional(looseJsonObject),
2277
- // === POLICY / ENTITLEMENT ===
2278
- tenantId: v.optional(v.string()),
2279
- workspaceId: v.optional(v.string()),
2280
- ownerPrincipalId: v.optional(v.string()),
2281
- audienceLabel: v.optional(audienceLabel),
2282
- policyTags: v.optional(v.array(v.string())),
2283
- sensitivityTier: v.optional(sensitivityTier),
2284
- exportClass: v.optional(exportClass),
2285
- anonymizationClass: v.optional(anonymizationClass),
2286
- // === PUBLICATION (visibility-based, not copy-based) ===
2287
- // Publication expands who can see a workspace-local node — the node stays
2288
- // in its workspace, like a microservice exposing part of its API surface.
2289
- // Rules-based: pack/tenant-level publicationRules auto-evaluate on
2290
- // confidence changes and node creation. No manual click-by-click.
2291
- publicationStatus: v.optional(
2292
- v.union(
2293
- v.literal("unpublished"),
2294
- // Default: workspace-local only
2295
- v.literal("published"),
2296
- // Visible at tenant scope (rules matched)
2297
- v.literal("suppressed")
2298
- // Manually blocked even if rules match
2299
- )
2300
- ),
2301
- publishedAt: v.optional(v.number()),
2302
- // When publication status last changed to published
2303
- publishedBy: v.optional(v.string()),
2304
- // userId or "system:publication_rules" for auto-publish
2305
- // === TYPED METADATA FIELDS ===
2306
- // --- Belief ---
2307
- // Belief type — validated against schemaEnumConfig category "belief_type"
2308
- // Platform core: hypothesis, belief, principle, invariant, assumption,
2309
- // tenet, prior, preference, goal, forecast
2310
- beliefType: v.optional(v.string()),
2311
- beliefStatus: v.optional(beliefStatus),
2312
- epistemicStatus: v.optional(epistemicStatus),
2313
- reversibility: v.optional(reversibility),
2314
- predictionMeta: v.optional(predictionMeta),
2315
- // Consensus tracking (for non-consensus detection)
2316
- consensusView: v.optional(consensusView),
2317
- consensusConfidence: v.optional(v.number()),
2318
- // 0-1: What we think consensus confidence is
2319
- consensusSource: v.optional(v.string()),
2320
- // Where we got the consensus view (twitter, reports, etc.)
2321
- // --- Evidence ---
2322
- methodology: v.optional(methodology),
2323
- informationAsymmetry: v.optional(informationAsymmetry),
2324
- temporalNature: v.optional(temporalNature),
2325
- // --- Question ---
2326
- questionType: v.optional(questionType),
2327
- questionPriority: v.optional(questionPriority),
2328
- answerQuality: v.optional(answerQuality),
2329
- // --- Theme ---
2330
- themeConviction: v.optional(themeConviction),
2331
- // Market timing (for "early on theme" detection)
2332
- marketAwarenessDate: v.optional(v.number()),
2333
- // When this theme became broadly discussed
2334
- marketAwarenessSource: v.optional(v.string()),
2335
- // How we know (first major report, twitter volume spike, etc.)
2336
- earlySignalIds: v.optional(v.array(v.string())),
2337
- // globalIds of evidence we had before market awareness
2338
- // --- Decision ---
2339
- decisionType: v.optional(decisionType),
2340
- decisionOutcome: v.optional(decisionOutcome),
2341
- // === EXTERNAL IDS (for ontological entities) ===
2342
- externalIds: v.optional(externalIds2),
2343
- // === PROVENANCE ===
2344
- sourceType: sourceType2,
2345
- aiProvider: v.optional(v.string()),
2346
- // "claude", "gemini", "gpt-4", etc.
2347
- extractedFromNodeId: v.optional(v.id("epistemicNodes")),
2348
- // Quick reference to source
2349
- // === EXTRACTION CONTEXT ===
2350
- extractionModel: v.optional(v.string()),
2351
- // "claude-sonnet-4-20250514"
2352
- extractionPromptName: v.optional(v.string()),
2353
- // "lucern/extract-evidence"
2354
- extractionPromptVersion: v.optional(v.number()),
2355
- extractionTemperature: v.optional(v.number()),
2356
- extractionLangfuseTraceId: v.optional(v.string()),
2357
- // === GROUNDING VERIFICATION ===
2358
- groundingVerified: v.optional(v.boolean()),
2359
- groundingConfidence: v.optional(v.number()),
2360
- // 0-1 match quality
2361
- groundingMatchedText: v.optional(v.string()),
2362
- // Actual text from source
2363
- groundingStartOffset: v.optional(v.number()),
2364
- groundingEndOffset: v.optional(v.number()),
2365
- groundingRejectionReason: v.optional(v.string()),
2366
- // === CONFIDENCE & VERIFICATION ===
2367
- confidence: v.optional(v.number()),
2368
- // 0-1 projected probability P(x) = b + a*u
2369
- verificationStatus: v.optional(verificationStatus),
2370
- // === SL OPINION (Subjective Logic — Kernel v2) ===
2371
- // Replaces scalar confidence with rich epistemic state.
2372
- // b + d + u = 1. P(x) = b + a*u is stored in `confidence` for backward compat.
2373
- opinion_b: v.optional(v.number()),
2374
- // Belief: evidence FOR (0-1)
2375
- opinion_d: v.optional(v.number()),
2376
- // Disbelief: evidence AGAINST (0-1)
2377
- opinion_u: v.optional(v.number()),
2378
- // Uncertainty: absence of evidence (0-1)
2379
- opinion_a: v.optional(v.number()),
2380
- // Base rate / prior probability (0-1)
2381
- tupleContradicted: v.optional(v.boolean()),
2382
- // Single-belief tuple-space contradiction flag
2383
- // === LIFECYCLE ===
2384
- status: nodeStatus,
2385
- supersededBy: v.optional(v.id("epistemicNodes")),
2386
- // === OWNERSHIP ===
2387
- topicId: v.optional(v.string()),
2388
- // Canonical scope container (topic-first model)
2389
- projectId: v.optional(v.string()),
2390
- // DEPRECATED: Use belongs_to edges
2391
- createdBy: v.string(),
2392
- // Clerk user ID
2393
- createdAt: v.number(),
2394
- updatedAt: v.number(),
2395
- // === NEO4J SYNC STATUS ===
2396
- syncStatus: v.optional(syncStatus),
2397
- syncError: v.optional(v.string())
2398
- // Error message if sync failed
2399
- }).index("by_globalId", ["globalId"]).index("by_contentHash", ["contentHash"]).index("by_nodeType", ["nodeType"]).index("by_subtype", ["nodeType", "subtype"]).index("by_domain", ["domain"]).index("by_project", ["projectId"]).index("by_project_type", ["projectId", "nodeType"]).index("by_topic", ["topicId"]).index("by_topic_type", ["topicId", "nodeType"]).index("by_tenantId", ["tenantId"]).index("by_workspaceId", ["workspaceId"]).index("by_tenant_workspace", ["tenantId", "workspaceId"]).index("by_audienceLabel", ["audienceLabel"]).index("by_sensitivityTier", ["sensitivityTier"]).index("by_exportClass", ["exportClass"]).index("by_status", ["status"]).index("by_sourceType", ["sourceType"]).index("by_verification", ["verificationStatus"]).index("by_layer", ["epistemicLayer"]).index("by_layer_type", ["epistemicLayer", "nodeType"]).index("by_syncStatus", ["syncStatus"]).index("by_publicationStatus", ["publicationStatus"]).index("by_tenant_publicationStatus", ["tenantId", "publicationStatus"]).index("by_belief_status", ["nodeType", "beliefStatus"]).index("by_epistemic_status", ["nodeType", "epistemicStatus"]).index("by_temporal_nature", ["nodeType", "temporalNature"]).index("by_methodology", ["nodeType", "methodology"]).index("by_reversibility", ["nodeType", "reversibility"]).index("by_questionType", ["nodeType", "questionType"]).index("by_questionPriority", ["nodeType", "questionPriority"]).searchIndex("search_canonicalText", {
2400
- searchField: "canonicalText",
2401
- filterFields: ["nodeType", "projectId", "topicId", "status"]
2402
- });
2403
- function getLayerForNodeType(type) {
2404
- switch (type) {
2405
- case "decision":
2406
- return "L4";
2407
- case "belief":
2408
- case "question":
2409
- case "theme":
2410
- case "deal":
2411
- return "L3";
2412
- case "claim":
2413
- case "evidence":
2414
- case "synthesis":
2415
- case "answer":
2416
- return "L2";
2417
- case "atomic_fact":
2418
- case "excerpt":
2419
- case "source":
2420
- return "L1";
2421
- case "topic":
2422
- return "organizational";
2423
- case "company":
2424
- case "person":
2425
- case "investor":
2426
- case "function":
2427
- case "value_chain":
2428
- return "ontological";
2429
- }
2430
- }
2431
- var LEGACY_SCOPE_FIELD2 = "graphScopeProjectId";
397
+ var LEGACY_SCOPE_FIELD = "graphScopeProjectId";
2432
398
  function asMappedProjectId(topic) {
2433
399
  if (!topic) {
2434
400
  return;
2435
401
  }
2436
- const directLegacyProjectId = normalizeScopeValue(topic[LEGACY_SCOPE_FIELD2]);
402
+ const directLegacyProjectId = normalizeScopeValue(topic[LEGACY_SCOPE_FIELD]);
2437
403
  if (directLegacyProjectId) {
2438
404
  return directLegacyProjectId;
2439
405
  }
2440
406
  const metadata = topic.metadata || {};
2441
- const candidate = metadata[LEGACY_SCOPE_FIELD2] || metadata.legacyProjectId || metadata.projectId || metadata.scopeProjectId;
407
+ const candidate = metadata[LEGACY_SCOPE_FIELD] || metadata.legacyProjectId || metadata.projectId || metadata.scopeProjectId;
2442
408
  return candidate ? candidate : void 0;
2443
409
  }
2444
410
  function normalizeScopeValue(value) {
@@ -2467,7 +433,7 @@ async function findTopicsByScopeAlias(ctx, scopeId) {
2467
433
  try {
2468
434
  return await ctx.db.query("topics").withIndex(
2469
435
  "by_graph_scope_project",
2470
- (q) => q.eq(LEGACY_SCOPE_FIELD2, scopeId)
436
+ (q) => q.eq(LEGACY_SCOPE_FIELD, scopeId)
2471
437
  ).collect();
2472
438
  } catch {
2473
439
  const topics = await ctx.db.query("topics").collect();
@@ -2483,7 +449,7 @@ async function tryResolveHostTopicById(ctx, topicId) {
2483
449
  return null;
2484
450
  }
2485
451
  try {
2486
- return await ctx.runQuery(api2.topics.get, {
452
+ return await ctx.runQuery(api.topics.get, {
2487
453
  id: topicId
2488
454
  }) ?? null;
2489
455
  } catch {
@@ -2495,7 +461,7 @@ async function tryResolveHostTopicByLegacyScope(ctx, legacyScopeId) {
2495
461
  return null;
2496
462
  }
2497
463
  try {
2498
- return await ctx.runQuery(api2.topics.getByLegacyScopeId, {
464
+ return await ctx.runQuery(api.topics.getByLegacyScopeId, {
2499
465
  projectId: legacyScopeId
2500
466
  }) ?? null;
2501
467
  } catch {
@@ -2633,7 +599,7 @@ function throwWorkspaceIsolationError(args) {
2633
599
  throw error;
2634
600
  }
2635
601
  function assertWorkspaceScopedEpistemicNodeScope(args) {
2636
- const layer = getLayerForNodeType(args.nodeType);
602
+ const layer = isNodeType(args.nodeType) ? getLayerForNodeType(args.nodeType) : void 0;
2637
603
  if (layer === "ontological") {
2638
604
  return;
2639
605
  }
@@ -2661,11 +627,11 @@ function nodeMatchesWorkspaceReasoningScope(node, scope) {
2661
627
  const scopeWorkspaceId = normalizeScopeValue2(scope.workspaceId);
2662
628
  const nodeTenantId = normalizeScopeValue2(node.tenantId);
2663
629
  const nodeWorkspaceId = normalizeScopeValue2(node.workspaceId);
2664
- const epistemicLayer2 = typeof node.epistemicLayer === "string" ? node.epistemicLayer : void 0;
630
+ const epistemicLayer = typeof node.epistemicLayer === "string" ? node.epistemicLayer : void 0;
2665
631
  if (scopeTenantId && nodeTenantId && scopeTenantId !== nodeTenantId) {
2666
632
  return false;
2667
633
  }
2668
- if (epistemicLayer2 === "ontological" && nodeWorkspaceId === void 0) {
634
+ if (epistemicLayer === "ontological" && nodeWorkspaceId === void 0) {
2669
635
  return true;
2670
636
  }
2671
637
  if (!scopeWorkspaceId && node.publicationStatus === "published") {
@@ -2693,11 +659,11 @@ function edgeMatchesWorkspaceReasoningScope(edge, scope) {
2693
659
  return scopeWorkspaceId === edgeWorkspaceId;
2694
660
  }
2695
661
  async function resolveNodeScopeForWorkspaceIsolation(ctx, node) {
2696
- const epistemicLayer2 = typeof node?.epistemicLayer === "string" ? node.epistemicLayer : void 0;
662
+ const epistemicLayer = typeof node?.epistemicLayer === "string" ? node.epistemicLayer : void 0;
2697
663
  const resolved = {
2698
664
  tenantId: normalizeScopeValue2(node?.tenantId),
2699
665
  workspaceId: normalizeScopeValue2(node?.workspaceId),
2700
- epistemicLayer: epistemicLayer2,
666
+ epistemicLayer,
2701
667
  nodeType: typeof node?.nodeType === "string" ? node.nodeType : void 0
2702
668
  };
2703
669
  if (!node) {
@@ -2772,16 +738,17 @@ function resolveTraversalTargetNodeId(edge, direction) {
2772
738
  }
2773
739
  function readNodeOpinion(node) {
2774
740
  const metadata = node.metadata ?? {};
2775
- return readOpinionFromRecord(
2776
- {
741
+ try {
742
+ return readOpinionFromRecord({
2777
743
  ...metadata,
2778
744
  opinion_b: node.opinion_b,
2779
745
  opinion_d: node.opinion_d,
2780
746
  opinion_u: node.opinion_u,
2781
747
  opinion_a: node.opinion_a
2782
- },
2783
- { b: 0, d: 0, u: 1, a: 0.5 }
2784
- );
748
+ });
749
+ } catch {
750
+ return mkOpinion(0, 0, 1, 0.5);
751
+ }
2785
752
  }
2786
753
  async function collectConfidencePropagationDispatches(args) {
2787
754
  const dispatchesByTargetId = /* @__PURE__ */ new Map();
@@ -2850,14 +817,20 @@ async function collectConfidencePropagationDispatches(args) {
2850
817
  if (!result || !hasProjectedOpinionChanged(targetOpinion, result.opinion)) {
2851
818
  continue;
2852
819
  }
2853
- opinionCache.set(cacheKey, result.opinion);
820
+ const projectedOpinion = mkOpinion(
821
+ result.opinion.b,
822
+ result.opinion.d,
823
+ result.opinion.u,
824
+ result.opinion.a
825
+ );
826
+ opinionCache.set(cacheKey, projectedOpinion);
2854
827
  const existingDispatch = dispatchesByTargetId.get(cacheKey);
2855
828
  dispatchesByTargetId.set(cacheKey, {
2856
829
  targetNodeId,
2857
830
  edgeType: spec.edgeType,
2858
831
  traversedDirection: direction,
2859
832
  weight: edge.weight ?? 1,
2860
- opinion: result.opinion,
833
+ opinion: projectedOpinion,
2861
834
  operator: result.operator,
2862
835
  rationale: existingDispatch ? `${existingDispatch.rationale}; ${result.rationale}` : result.rationale,
2863
836
  hop: nextHop
@@ -2865,7 +838,7 @@ async function collectConfidencePropagationDispatches(args) {
2865
838
  if (canContinueTransitively(spec, nextHop)) {
2866
839
  queue.push({
2867
840
  nodeId: targetNodeId,
2868
- opinion: result.opinion,
841
+ opinion: projectedOpinion,
2869
842
  hop: nextHop,
2870
843
  visitedNodeIds: /* @__PURE__ */ new Set([
2871
844
  ...state.visitedNodeIds,
@@ -2980,48 +953,48 @@ async function getQuestionsAnsweredByEvidence(ctx, evidenceId) {
2980
953
  }
2981
954
 
2982
955
  // src/topicProjectOverlay.ts
2983
- var LEGACY_SCOPE_FIELD3 = "graphScopeProjectId";
2984
- function readNonEmptyString2(value) {
956
+ var LEGACY_SCOPE_FIELD2 = "graphScopeProjectId";
957
+ function readNonEmptyString(value) {
2985
958
  if (typeof value !== "string") {
2986
959
  return;
2987
960
  }
2988
961
  const normalized = value.trim();
2989
962
  return normalized.length > 0 ? normalized : void 0;
2990
963
  }
2991
- function readStringArray2(value) {
964
+ function readStringArray(value) {
2992
965
  if (!Array.isArray(value)) {
2993
966
  return [];
2994
967
  }
2995
- return value.map((entry) => readNonEmptyString2(entry)).filter((entry) => Boolean(entry));
968
+ return value.map((entry) => readNonEmptyString(entry)).filter((entry) => Boolean(entry));
2996
969
  }
2997
- function readMetadata2(topic) {
970
+ function readMetadata(topic) {
2998
971
  return topic.metadata && typeof topic.metadata === "object" ? topic.metadata : {};
2999
972
  }
3000
- function readLegacyProjectId2(value) {
973
+ function readLegacyProjectId(value) {
3001
974
  if (!value) {
3002
975
  return;
3003
976
  }
3004
- return readNonEmptyString2(value[LEGACY_SCOPE_FIELD3]);
977
+ return readNonEmptyString(value[LEGACY_SCOPE_FIELD2]);
3005
978
  }
3006
- function coerceVisibility2(value) {
979
+ function coerceVisibility(value) {
3007
980
  return value === "private" || value === "team" || value === "firm" || value === "external" || value === "public" ? value : void 0;
3008
981
  }
3009
- function coerceStatus2(value) {
982
+ function coerceStatus(value) {
3010
983
  return value === "active" || value === "archived" || value === "watching" ? value : void 0;
3011
984
  }
3012
- function mapProjectType2(topic, metadata) {
3013
- const explicit = readNonEmptyString2(metadata.projectType);
985
+ function mapProjectType(topic, metadata) {
986
+ const explicit = readNonEmptyString(metadata.projectType);
3014
987
  if (explicit) {
3015
988
  return explicit;
3016
989
  }
3017
990
  if (topic.type === "theme") {
3018
991
  return "thematic";
3019
992
  }
3020
- return readNonEmptyString2(topic.type) || "general";
993
+ return readNonEmptyString(topic.type) || "general";
3021
994
  }
3022
- function isProjectLikeTopic2(topic) {
3023
- const metadata = readMetadata2(topic);
3024
- return topic.type === "theme" || topic.type === "thematic" || topic.type === "deal" || topic.type === "monitoring" || readLegacyProjectId2(topic) !== void 0 || readNonEmptyString2(metadata.projectType) !== void 0;
995
+ function isProjectLikeTopic(topic) {
996
+ const metadata = readMetadata(topic);
997
+ return topic.type === "theme" || topic.type === "thematic" || topic.type === "deal" || topic.type === "monitoring" || readLegacyProjectId(topic) !== void 0 || readNonEmptyString(metadata.projectType) !== void 0;
3025
998
  }
3026
999
  function isMissingLucernChildComponentError(error) {
3027
1000
  const message = error instanceof Error ? error.message : String(error);
@@ -3029,7 +1002,7 @@ function isMissingLucernChildComponentError(error) {
3029
1002
  'Child component ComponentName(Identifier("lucern")) not found'
3030
1003
  ) || message.includes("Child component") && message.includes("lucern") && message.includes("not found");
3031
1004
  }
3032
- async function resolveTopicDoc2(ctx, scopeId) {
1005
+ async function resolveTopicDoc(ctx, scopeId) {
3033
1006
  if (ctx?.db && typeof ctx.db.get === "function") {
3034
1007
  try {
3035
1008
  const directTopic = await ctx.db.get(scopeId);
@@ -3043,7 +1016,7 @@ async function resolveTopicDoc2(ctx, scopeId) {
3043
1016
  return null;
3044
1017
  }
3045
1018
  try {
3046
- const topic = await ctx.runQuery(api2.topics.get, {
1019
+ const topic = await ctx.runQuery(api.topics.get, {
3047
1020
  id: String(scopeId)
3048
1021
  });
3049
1022
  if (topic?.name !== void 0 && topic?.type !== void 0) {
@@ -3052,7 +1025,7 @@ async function resolveTopicDoc2(ctx, scopeId) {
3052
1025
  } catch {
3053
1026
  }
3054
1027
  try {
3055
- const topic = await ctx.runQuery(api2.topics.getByLegacyScopeId, {
1028
+ const topic = await ctx.runQuery(api.topics.getByLegacyScopeId, {
3056
1029
  projectId: String(scopeId)
3057
1030
  });
3058
1031
  if (topic?.name !== void 0 && topic?.type !== void 0) {
@@ -3062,14 +1035,14 @@ async function resolveTopicDoc2(ctx, scopeId) {
3062
1035
  }
3063
1036
  return null;
3064
1037
  }
3065
- function materializeTopicProjectOverlay2(topic, idMode = "legacy") {
3066
- const metadata = readMetadata2(topic);
1038
+ function materializeTopicProjectOverlay(topic, idMode = "legacy") {
1039
+ const metadata = readMetadata(topic);
3067
1040
  const topicId = String(topic._id);
3068
- const legacyProjectId = readLegacyProjectId2(topic) || readLegacyProjectId2(metadata) || readNonEmptyString2(metadata.legacyProjectId);
1041
+ const legacyProjectId = readLegacyProjectId(topic) || readLegacyProjectId(metadata) || readNonEmptyString(metadata.legacyProjectId);
3069
1042
  const storageProjectId = legacyProjectId || topicId;
3070
1043
  const outwardId = idMode === "topic" ? topicId : storageProjectId;
3071
- const visibility = coerceVisibility2(topic.visibility) || coerceVisibility2(metadata.visibility) || "private";
3072
- const status = coerceStatus2(topic.status) || coerceStatus2(metadata.status) || "active";
1044
+ const visibility = coerceVisibility(topic.visibility) || coerceVisibility(metadata.visibility) || "private";
1045
+ const status = coerceStatus(topic.status) || coerceStatus(metadata.status) || "active";
3073
1046
  const createdAt = typeof topic.createdAt === "number" ? topic.createdAt : typeof topic._creationTime === "number" ? topic._creationTime : 0;
3074
1047
  const updatedAt = typeof topic.updatedAt === "number" ? topic.updatedAt : typeof metadata.updatedAt === "number" ? metadata.updatedAt : createdAt;
3075
1048
  return {
@@ -3079,16 +1052,16 @@ function materializeTopicProjectOverlay2(topic, idMode = "legacy") {
3079
1052
  topicId,
3080
1053
  storageProjectId,
3081
1054
  legacyProjectId,
3082
- name: readNonEmptyString2(topic.name) || "Untitled Theme",
3083
- type: mapProjectType2(topic, metadata),
3084
- description: readNonEmptyString2(topic.description),
3085
- ownerId: readNonEmptyString2(metadata.ownerId) || readNonEmptyString2(topic.createdBy) || "system",
3086
- sharedWith: readStringArray2(metadata.sharedWith),
1055
+ name: readNonEmptyString(topic.name) || "Untitled Theme",
1056
+ type: mapProjectType(topic, metadata),
1057
+ description: readNonEmptyString(topic.description),
1058
+ ownerId: readNonEmptyString(metadata.ownerId) || readNonEmptyString(topic.createdBy) || "system",
1059
+ sharedWith: readStringArray(metadata.sharedWith),
3087
1060
  visibility,
3088
- tenantId: readNonEmptyString2(topic.tenantId) || readNonEmptyString2(metadata.tenantId),
3089
- workspaceId: readNonEmptyString2(topic.workspaceId) || readNonEmptyString2(metadata.workspaceId),
1061
+ tenantId: readNonEmptyString(topic.tenantId) || readNonEmptyString(metadata.tenantId),
1062
+ workspaceId: readNonEmptyString(topic.workspaceId) || readNonEmptyString(metadata.workspaceId),
3090
1063
  status,
3091
- tags: readStringArray2(metadata.tags),
1064
+ tags: readStringArray(metadata.tags),
3092
1065
  chatCount: typeof metadata.chatCount === "number" ? metadata.chatCount : 0,
3093
1066
  artifactCount: typeof metadata.artifactCount === "number" ? metadata.artifactCount : 0,
3094
1067
  lastActivityAt: typeof metadata.lastActivityAt === "number" ? metadata.lastActivityAt : updatedAt,
@@ -3097,17 +1070,17 @@ function materializeTopicProjectOverlay2(topic, idMode = "legacy") {
3097
1070
  updatedAt
3098
1071
  };
3099
1072
  }
3100
- async function resolveTopicProjectOverlay2(ctx, scopeId, options = {}) {
3101
- const topic = await resolveTopicDoc2(ctx, scopeId);
1073
+ async function resolveTopicProjectOverlay(ctx, scopeId, options = {}) {
1074
+ const topic = await resolveTopicDoc(ctx, scopeId);
3102
1075
  if (!topic) {
3103
1076
  return null;
3104
1077
  }
3105
- if (options.projectLikeOnly !== false && !isProjectLikeTopic2(topic)) {
1078
+ if (options.projectLikeOnly !== false && !isProjectLikeTopic(topic)) {
3106
1079
  return null;
3107
1080
  }
3108
- return materializeTopicProjectOverlay2(topic, options.idMode);
1081
+ return materializeTopicProjectOverlay(topic, options.idMode);
3109
1082
  }
3110
- async function listTopicProjectOverlays2(ctx, options = {}) {
1083
+ async function listTopicProjectOverlays(ctx, options = {}) {
3111
1084
  let allTopics = [];
3112
1085
  if (ctx?.db?.query && typeof ctx.db.query === "function") {
3113
1086
  try {
@@ -3117,18 +1090,18 @@ async function listTopicProjectOverlays2(ctx, options = {}) {
3117
1090
  }
3118
1091
  }
3119
1092
  if (allTopics.length === 0 && typeof ctx.runQuery === "function") {
3120
- allTopics = (await ctx.runQuery(api2.topics.list, {}) ?? []) || [];
1093
+ allTopics = (await ctx.runQuery(api.topics.list, {}) ?? []) || [];
3121
1094
  }
3122
1095
  return allTopics.filter(
3123
- (topic) => options.projectLikeOnly === false || isProjectLikeTopic2(topic)
3124
- ).map((topic) => materializeTopicProjectOverlay2(topic, options.idMode));
1096
+ (topic) => options.projectLikeOnly === false || isProjectLikeTopic(topic)
1097
+ ).map((topic) => materializeTopicProjectOverlay(topic, options.idMode));
3125
1098
  }
3126
1099
  async function patchTopicProjectOverlay(ctx, scopeId, value) {
3127
- const topic = await resolveTopicDoc2(ctx, scopeId);
1100
+ const topic = await resolveTopicDoc(ctx, scopeId);
3128
1101
  if (!topic) {
3129
1102
  return null;
3130
1103
  }
3131
- const nextMetadata = { ...readMetadata2(topic) };
1104
+ const nextMetadata = { ...readMetadata(topic) };
3132
1105
  const patch = {};
3133
1106
  const topicUpdateArgs = {
3134
1107
  id: String(topic._id)
@@ -3153,7 +1126,7 @@ async function patchTopicProjectOverlay(ctx, scopeId, value) {
3153
1126
  `patchTopicProjectOverlay cannot mutate ${key} via component-owned topics`
3154
1127
  );
3155
1128
  case "status": {
3156
- const status = coerceStatus2(rawValue);
1129
+ const status = coerceStatus(rawValue);
3157
1130
  if (status) {
3158
1131
  patch.status = status;
3159
1132
  topicUpdateArgs.status = status;
@@ -3161,7 +1134,7 @@ async function patchTopicProjectOverlay(ctx, scopeId, value) {
3161
1134
  break;
3162
1135
  }
3163
1136
  case "visibility": {
3164
- const visibility = coerceVisibility2(rawValue);
1137
+ const visibility = coerceVisibility(rawValue);
3165
1138
  if (visibility) {
3166
1139
  patch.visibility = visibility;
3167
1140
  topicUpdateArgs.visibility = visibility;
@@ -3169,7 +1142,7 @@ async function patchTopicProjectOverlay(ctx, scopeId, value) {
3169
1142
  break;
3170
1143
  }
3171
1144
  case "type": {
3172
- const projectType = readNonEmptyString2(rawValue);
1145
+ const projectType = readNonEmptyString(rawValue);
3173
1146
  if (projectType) {
3174
1147
  nextMetadata.projectType = projectType;
3175
1148
  } else {
@@ -3193,7 +1166,7 @@ async function patchTopicProjectOverlay(ctx, scopeId, value) {
3193
1166
  topicUpdateArgs.metadata = nextMetadata;
3194
1167
  if (typeof ctx.runMutation === "function") {
3195
1168
  try {
3196
- await ctx.runMutation(api2.topics.update, topicUpdateArgs);
1169
+ await ctx.runMutation(api.topics.update, topicUpdateArgs);
3197
1170
  } catch (error) {
3198
1171
  if (!isMissingLucernChildComponentError(error) || !ctx?.db || typeof ctx.db.patch !== "function") {
3199
1172
  throw error;
@@ -3207,7 +1180,7 @@ async function patchTopicProjectOverlay(ctx, scopeId, value) {
3207
1180
  "Cannot patch topic without component adapter (ctx.runMutation unavailable)"
3208
1181
  );
3209
1182
  }
3210
- return materializeTopicProjectOverlay2(
1183
+ return materializeTopicProjectOverlay(
3211
1184
  {
3212
1185
  ...topic,
3213
1186
  ...patch,
@@ -3240,10 +1213,10 @@ async function patchProjectWithTolerance(ctx, projectId, value) {
3240
1213
  });
3241
1214
  }
3242
1215
  }
3243
- function defaultResolvers2() {
1216
+ function defaultResolvers() {
3244
1217
  return {
3245
1218
  async getProject(ctx, projectId) {
3246
- return await resolveTopicProjectOverlay2(ctx, projectId, {
1219
+ return await resolveTopicProjectOverlay(ctx, projectId, {
3247
1220
  idMode: "legacy",
3248
1221
  projectLikeOnly: false
3249
1222
  });
@@ -3252,7 +1225,7 @@ function defaultResolvers2() {
3252
1225
  await patchProjectWithTolerance(ctx, projectId, value);
3253
1226
  },
3254
1227
  async listTopics(ctx) {
3255
- return await listTopicProjectOverlays2(ctx, {
1228
+ return await listTopicProjectOverlays(ctx, {
3256
1229
  idMode: "legacy"
3257
1230
  });
3258
1231
  },
@@ -3261,11 +1234,11 @@ function defaultResolvers2() {
3261
1234
  }
3262
1235
  };
3263
1236
  }
3264
- var resolverOverrides2 = {};
1237
+ var resolverOverrides = {};
3265
1238
  function resolveGraphPrimitivesAppResolvers(_ctx) {
3266
1239
  return {
3267
- ...defaultResolvers2(),
3268
- ...resolverOverrides2
1240
+ ...defaultResolvers(),
1241
+ ...resolverOverrides
3269
1242
  };
3270
1243
  }
3271
1244
 
@@ -3290,7 +1263,7 @@ function throwStructuredMutationError(args) {
3290
1263
  function readFiniteNumber(value) {
3291
1264
  return typeof value === "number" && Number.isFinite(value) ? value : void 0;
3292
1265
  }
3293
- function clamp012(value) {
1266
+ function clamp01(value) {
3294
1267
  return Math.max(0, Math.min(1, value));
3295
1268
  }
3296
1269
  function assertBaseRateInRange(baseRate, field = "baseRate") {
@@ -3340,20 +1313,14 @@ function deriveSyntheticBackfillOpinion(source) {
3340
1313
  const uncertainty = readFiniteNumber(source.opinion_u) ?? readFiniteNumber(source.uncertainty);
3341
1314
  const baseRate = readFiniteNumber(source.opinion_a) ?? readFiniteNumber(source.baseRate);
3342
1315
  if (belief !== void 0 || disbelief !== void 0 || uncertainty !== void 0 || baseRate !== void 0) {
3343
- return readOpinionFromRecord(source, {
3344
- b: 0,
3345
- d: 0,
3346
- u: 1,
3347
- a: 0.5
3348
- });
1316
+ try {
1317
+ return readOpinionFromRecord(source);
1318
+ } catch {
1319
+ return mkOpinion(0, 0, 1, 0.5);
1320
+ }
3349
1321
  }
3350
- const confidence2 = clamp012(readFiniteNumber(source.confidence) ?? 0);
3351
- return {
3352
- b: confidence2,
3353
- d: 1 - confidence2,
3354
- u: 0,
3355
- a: 0.5
3356
- };
1322
+ const confidence = clamp01(readFiniteNumber(source.confidence) ?? 0);
1323
+ return mkOpinion(confidence, 1 - confidence, 0, 0.5);
3357
1324
  }
3358
1325
  function clampBeliefLimit(limit, fallback = DEFAULT_PROJECT_BELIEF_LIMIT) {
3359
1326
  if (!Number.isFinite(limit)) {
@@ -3368,16 +1335,17 @@ function readTupleContradictedFlag(value) {
3368
1335
  return typeof value === "boolean" ? value : void 0;
3369
1336
  }
3370
1337
  function readBeliefOpinionSnapshot(node, metadata) {
3371
- return readOpinionFromRecord(
3372
- {
1338
+ try {
1339
+ return readOpinionFromRecord({
3373
1340
  ...metadata,
3374
1341
  opinion_b: node.opinion_b,
3375
1342
  opinion_d: node.opinion_d,
3376
1343
  opinion_u: node.opinion_u,
3377
1344
  opinion_a: node.opinion_a
3378
- },
3379
- { b: 0, d: 0, u: 1, a: 0.5 }
3380
- );
1345
+ });
1346
+ } catch {
1347
+ return mkOpinion(0, 0, 1, 0.5);
1348
+ }
3381
1349
  }
3382
1350
  function deriveTupleContradictionSeverity(node) {
3383
1351
  const metadata = node.metadata || {};
@@ -3394,10 +1362,10 @@ function formatTupleContradictionDescription(args) {
3394
1362
  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)}.`;
3395
1363
  }
3396
1364
  function generateContentHash(text) {
3397
- const content2 = `belief:${text.trim().toLowerCase().replace(/\s+/g, " ")}`;
1365
+ const content = `belief:${text.trim().toLowerCase().replace(/\s+/g, " ")}`;
3398
1366
  let hash = 5381;
3399
- for (let i = 0; i < content2.length; i++) {
3400
- hash = (hash << 5) + hash + content2.charCodeAt(i);
1367
+ for (let i = 0; i < content.length; i++) {
1368
+ hash = (hash << 5) + hash + content.charCodeAt(i);
3401
1369
  hash &= hash;
3402
1370
  }
3403
1371
  return Math.abs(hash).toString(16).padStart(8, "0");
@@ -3440,14 +1408,14 @@ async function markBeliefGraphDirty(ctx, scope) {
3440
1408
  if (projectId) {
3441
1409
  await ctx.scheduler.runAfter(
3442
1410
  0,
3443
- internal2.graphAnalysisCache.markCacheStaleInternal,
1411
+ internal.graphAnalysisCache.markCacheStaleInternal,
3444
1412
  { projectId }
3445
1413
  );
3446
1414
  }
3447
1415
  if (topicId) {
3448
1416
  await ctx.scheduler.runAfter(
3449
1417
  0,
3450
- internal2.graphAnalysisCache.markCacheStaleByTopic,
1418
+ internal.graphAnalysisCache.markCacheStaleByTopic,
3451
1419
  { topicId }
3452
1420
  );
3453
1421
  }
@@ -3474,8 +1442,8 @@ async function resolveBeliefScopeOrNull(ctx, args) {
3474
1442
  }
3475
1443
  async function getBeliefNodesForScope(ctx, scope, args) {
3476
1444
  const baseQuery = ctx.db.query("epistemicNodes").withIndex(
3477
- scope.topicId ? "by_topic_type" : "by_project_type",
3478
- (q) => scope.topicId ? q.eq("topicId", scope.topicId).eq("nodeType", "belief") : q.eq("projectId", scope.projectId).eq("nodeType", "belief")
1445
+ "by_topic_type",
1446
+ (q) => q.eq("topicId", scope.topicId).eq("nodeType", "belief")
3479
1447
  );
3480
1448
  const nodes = typeof args?.scanLimit === "number" ? await baseQuery.order("desc").take(args.scanLimit) : await baseQuery.collect();
3481
1449
  const scopedNodes = nodes.filter(
@@ -3626,7 +1594,7 @@ async function applyBeliefConfidenceChange(ctx, args) {
3626
1594
  }
3627
1595
  }
3628
1596
  const previousConfidence = node.confidence || 0.5;
3629
- const predictionMeta2 = node.predictionMeta || existingMetadata.predictionMeta;
1597
+ const predictionMeta = node.predictionMeta || existingMetadata.predictionMeta;
3630
1598
  const previousOpinion = readBeliefOpinionSnapshot(node, existingMetadata);
3631
1599
  const slB = args.belief;
3632
1600
  const slD = args.disbelief;
@@ -3653,7 +1621,7 @@ async function applyBeliefConfidenceChange(ctx, args) {
3653
1621
  currentBeliefStatus,
3654
1622
  {
3655
1623
  confidence: derivedConfidence,
3656
- predictionMeta: predictionMeta2,
1624
+ predictionMeta,
3657
1625
  metadata: existingMetadata
3658
1626
  }
3659
1627
  );
@@ -3698,7 +1666,7 @@ async function applyBeliefConfidenceChange(ctx, args) {
3698
1666
  const themeNodes = await ctx.db.query("epistemicNodes").withIndex("by_topic", (q) => q.eq("topicId", nodeTopicId || node.projectId)).filter((q) => q.eq(q.field("nodeType"), "theme")).collect();
3699
1667
  for (const theme of themeNodes) {
3700
1668
  if (theme.globalId && node.globalId) {
3701
- await ctx.scheduler.runAfter(0, internal2.neo4jEdgeAPI.createEdge, {
1669
+ await ctx.scheduler.runAfter(0, internal.neo4jEdgeAPI.createEdge, {
3702
1670
  globalId: `edge-${node.globalId}-relates_to_thesis-${theme.globalId}`,
3703
1671
  fromGlobalId: node.globalId,
3704
1672
  toGlobalId: theme.globalId,
@@ -3732,7 +1700,7 @@ async function applyBeliefConfidenceChange(ctx, args) {
3732
1700
  triggeringWorktreeId: args.triggeringWorktreeId
3733
1701
  })
3734
1702
  });
3735
- await ctx.scheduler.runAfter(0, internal2.neo4jSync.syncNodeToNeo4j, {
1703
+ await ctx.scheduler.runAfter(0, internal.neo4jSync.syncNodeToNeo4j, {
3736
1704
  nodeId: args.nodeId,
3737
1705
  operation: "upsert"
3738
1706
  });
@@ -3783,7 +1751,7 @@ async function applyBeliefConfidenceChange(ctx, args) {
3783
1751
  if (Math.abs(derivedConfidence - previousConfidence) >= 0.15) {
3784
1752
  await ctx.scheduler.runAfter(
3785
1753
  5e3,
3786
- internal2.bi.contradictionSemanticDetector.scanAffectedBeliefs,
1754
+ internal.bi.contradictionSemanticDetector.scanAffectedBeliefs,
3787
1755
  {
3788
1756
  beliefId: args.nodeId,
3789
1757
  projectId: node.projectId
@@ -3793,7 +1761,7 @@ async function applyBeliefConfidenceChange(ctx, args) {
3793
1761
  if (node.workspaceId && node.tenantId) {
3794
1762
  await ctx.scheduler.runAfter(
3795
1763
  0,
3796
- internal2.publication.evaluateNodePublication,
1764
+ internal.publication.evaluateNodePublication,
3797
1765
  { nodeId: args.nodeId }
3798
1766
  );
3799
1767
  }
@@ -3825,12 +1793,12 @@ var propagateConfidenceChange = internalMutation({
3825
1793
  },
3826
1794
  returns: permissiveReturn,
3827
1795
  handler: async (ctx, args) => {
3828
- const sourceOpinion = {
3829
- b: args.opinion_b,
3830
- d: args.opinion_d,
3831
- u: args.opinion_u,
3832
- a: args.opinion_a
3833
- };
1796
+ const sourceOpinion = mkOpinion(
1797
+ args.opinion_b,
1798
+ args.opinion_d,
1799
+ args.opinion_u,
1800
+ args.opinion_a
1801
+ );
3834
1802
  const sourceNode = await ctx.db.get(args.nodeId);
3835
1803
  const sourceScope = await resolveNodeScopeForWorkspaceIsolation(
3836
1804
  ctx,
@@ -3937,7 +1905,7 @@ var create = mutation({
3937
1905
  expectedBy: v.optional(v.number())
3938
1906
  })
3939
1907
  ),
3940
- baseRate: v.number(),
1908
+ baseRate: v.optional(v.number()),
3941
1909
  metadata: v.optional(v.any())
3942
1910
  // Additional metadata including isConditional
3943
1911
  },
@@ -3968,7 +1936,7 @@ var create = mutation({
3968
1936
  );
3969
1937
  }
3970
1938
  const now = Date.now();
3971
- const baseRate = assertBaseRateInRange(args.baseRate);
1939
+ const baseRate = assertBaseRateInRange(args.baseRate ?? 0.5);
3972
1940
  const initialBeliefStatus = args.worktreeId ? "hypothesis" : "assumption";
3973
1941
  const initialEpistemicStatus = args.worktreeId ? "hypothesis" : "assumption";
3974
1942
  const seedOpinion = {
@@ -4037,7 +2005,7 @@ var create = mutation({
4037
2005
  slOperator: "manual_assessment"
4038
2006
  })
4039
2007
  );
4040
- await ctx.scheduler.runAfter(0, internal2.neo4jSync.syncNodeToNeo4j, {
2008
+ await ctx.scheduler.runAfter(0, internal.neo4jSync.syncNodeToNeo4j, {
4041
2009
  nodeId,
4042
2010
  operation: "upsert"
4043
2011
  });
@@ -4096,7 +2064,7 @@ Rationale: ${args.rationale}` : args.formulation
4096
2064
  await ctx.scheduler.runAfter(
4097
2065
  2e3,
4098
2066
  // 2 second delay
4099
- internal2.nodeClassification.scheduleClassification,
2067
+ internal.nodeClassification.scheduleClassification,
4100
2068
  {
4101
2069
  nodeId,
4102
2070
  nodeType: "belief",
@@ -4120,7 +2088,7 @@ Rationale: ${args.rationale}` : args.formulation
4120
2088
  await ctx.scheduler.runAfter(
4121
2089
  3e3,
4122
2090
  // 3 second delay — after entity extraction
4123
- internal2.beliefTemporalClassifier.classifyBelief,
2091
+ internal.beliefTemporalClassifier.classifyBelief,
4124
2092
  {
4125
2093
  nodeId,
4126
2094
  projectId: scope.projectId,
@@ -4221,7 +2189,7 @@ var refineBelief = mutation({
4221
2189
  patch.content = args.rationale;
4222
2190
  }
4223
2191
  await ctx.db.patch(args.nodeId, patch);
4224
- await ctx.scheduler.runAfter(0, internal2.neo4jSync.syncNodeToNeo4j, {
2192
+ await ctx.scheduler.runAfter(0, internal.neo4jSync.syncNodeToNeo4j, {
4225
2193
  nodeId: args.nodeId,
4226
2194
  operation: "upsert"
4227
2195
  });
@@ -4307,8 +2275,8 @@ var getByProject = query({
4307
2275
  }
4308
2276
  }
4309
2277
  const query2 = ctx.db.query("epistemicNodes").withIndex(
4310
- scope.topicId ? "by_topic_type" : "by_project_type",
4311
- (q) => scope.topicId ? q.eq("topicId", scope.topicId).eq("nodeType", "belief") : q.eq("projectId", scope.projectId).eq("nodeType", "belief")
2278
+ "by_topic_type",
2279
+ (q) => q.eq("topicId", scope.topicId).eq("nodeType", "belief")
4312
2280
  );
4313
2281
  const nodes = await query2.order("desc").take(scanLimit);
4314
2282
  const scopedNodes = nodes.filter(
@@ -4457,11 +2425,11 @@ var forkBelief = mutation({
4457
2425
  })
4458
2426
  );
4459
2427
  }
4460
- await ctx.scheduler.runAfter(0, internal2.neo4jSync.syncNodeToNeo4j, {
2428
+ await ctx.scheduler.runAfter(0, internal.neo4jSync.syncNodeToNeo4j, {
4461
2429
  nodeId: newNodeId,
4462
2430
  operation: "upsert"
4463
2431
  });
4464
- await ctx.scheduler.runAfter(5e3, internal2.neo4jEdgeAPI.createEdge, {
2432
+ await ctx.scheduler.runAfter(5e3, internal.neo4jEdgeAPI.createEdge, {
4465
2433
  globalId: generateGlobalId(),
4466
2434
  fromGlobalId: newBeliefGlobalId,
4467
2435
  toGlobalId: parent.globalId,
@@ -4517,7 +2485,7 @@ var forkBelief = mutation({
4517
2485
  }
4518
2486
  await ctx.scheduler.runAfter(
4519
2487
  3e3,
4520
- internal2.beliefTemporalClassifier.classifyBelief,
2488
+ internal.beliefTemporalClassifier.classifyBelief,
4521
2489
  {
4522
2490
  nodeId: newNodeId,
4523
2491
  projectId: parent.projectId,
@@ -4542,8 +2510,8 @@ var modulateConfidence = mutation({
4542
2510
  // d: evidence AGAINST [0,1]
4543
2511
  uncertainty: v.number(),
4544
2512
  // u: lack of evidence [0,1]
4545
- baseRate: v.optional(v.number()),
4546
- // a: prior probability [0,1], defaults to 0.5
2513
+ baseRate: v.number(),
2514
+ // a: prior probability [0,1]
4547
2515
  trigger: v.union(
4548
2516
  v.literal("evidence_added"),
4549
2517
  v.literal("evidence_removed"),
@@ -4595,7 +2563,7 @@ var modulateConfidence = mutation({
4595
2563
  if (Math.abs(result.newConfidence - result.previousConfidence) >= 0.01) {
4596
2564
  await ctx.scheduler.runAfter(
4597
2565
  0,
4598
- internal2.epistemicBeliefs.propagateConfidenceChange,
2566
+ internal.epistemicBeliefs.propagateConfidenceChange,
4599
2567
  {
4600
2568
  nodeId: args.nodeId,
4601
2569
  opinion_b: args.belief,
@@ -4647,7 +2615,7 @@ var updateStatus = mutation({
4647
2615
  status: args.status
4648
2616
  }
4649
2617
  });
4650
- await ctx.scheduler.runAfter(0, internal2.neo4jSync.syncNodeToNeo4j, {
2618
+ await ctx.scheduler.runAfter(0, internal.neo4jSync.syncNodeToNeo4j, {
4651
2619
  nodeId: args.nodeId,
4652
2620
  operation: "upsert"
4653
2621
  });
@@ -4684,7 +2652,7 @@ var archive = mutation({
4684
2652
  await requireProjectWriteAccess(ctx, node.projectId, authenticatedUserId);
4685
2653
  return await ctx.runMutation(
4686
2654
  // Use updateStatus internally
4687
- internal2.epistemicBeliefs.updateStatusInternal,
2655
+ internal.epistemicBeliefs.updateStatusInternal,
4688
2656
  {
4689
2657
  nodeId: args.nodeId,
4690
2658
  status: "archived",
@@ -4722,7 +2690,7 @@ var updateRationale = mutation({
4722
2690
  },
4723
2691
  updatedAt: now
4724
2692
  });
4725
- await ctx.scheduler.runAfter(0, internal2.neo4jSync.syncNodeToNeo4j, {
2693
+ await ctx.scheduler.runAfter(0, internal.neo4jSync.syncNodeToNeo4j, {
4726
2694
  nodeId: args.nodeId,
4727
2695
  operation: "upsert"
4728
2696
  });
@@ -4918,7 +2886,7 @@ var updatePillar = mutation({
4918
2886
  },
4919
2887
  updatedAt: now
4920
2888
  });
4921
- await ctx.scheduler.runAfter(0, internal2.neo4jSync.syncNodeToNeo4j, {
2889
+ await ctx.scheduler.runAfter(0, internal.neo4jSync.syncNodeToNeo4j, {
4922
2890
  nodeId: args.nodeId,
4923
2891
  operation: "upsert"
4924
2892
  });
@@ -4946,9 +2914,13 @@ var getCountByStatus = query({
4946
2914
  },
4947
2915
  returns: permissiveReturn,
4948
2916
  handler: async (ctx, args) => {
2917
+ const scope = await resolveBeliefScopeOrNull(ctx, args);
2918
+ if (!scope) {
2919
+ return { active: 0, superseded: 0, archived: 0, total: 0 };
2920
+ }
4949
2921
  const nodes = await ctx.db.query("epistemicNodes").withIndex(
4950
- args.topicId ? "by_topic_type" : "by_project_type",
4951
- (q) => args.topicId ? q.eq("topicId", args.topicId).eq("nodeType", "belief") : q.eq("projectId", args.projectId).eq("nodeType", "belief")
2922
+ "by_topic_type",
2923
+ (q) => q.eq("topicId", scope.topicId).eq("nodeType", "belief")
4952
2924
  ).collect();
4953
2925
  const counts = {
4954
2926
  active: 0,
@@ -5011,7 +2983,7 @@ var linkBeliefs = mutation({
5011
2983
  );
5012
2984
  const now = Date.now();
5013
2985
  const edgeGlobalId = generateGlobalId();
5014
- await ctx.scheduler.runAfter(0, internal2.neo4jEdgeAPI.createEdge, {
2986
+ await ctx.scheduler.runAfter(0, internal.neo4jEdgeAPI.createEdge, {
5015
2987
  globalId: edgeGlobalId,
5016
2988
  fromGlobalId: fromNode.globalId,
5017
2989
  toGlobalId: toNode.globalId,
@@ -5073,7 +3045,7 @@ var linkEvidence = mutation({
5073
3045
  const weight = args.type === "supporting" ? 1 : -1;
5074
3046
  const logicalRole = evidenceNodeId ? await computeLogicalRole(ctx, evidenceNodeId, args.beliefNodeId) : "contributory";
5075
3047
  const edgeGlobalId = generateGlobalId();
5076
- await ctx.scheduler.runAfter(0, internal2.neo4jEdgeAPI.createEdge, {
3048
+ await ctx.scheduler.runAfter(0, internal.neo4jEdgeAPI.createEdge, {
5077
3049
  globalId: edgeGlobalId,
5078
3050
  fromGlobalId: evidenceGlobalId,
5079
3051
  toGlobalId: belief.globalId,
@@ -5128,7 +3100,7 @@ var unlinkEvidence = mutation({
5128
3100
  (q) => q.eq("fromNodeId", evidenceNode._id).eq("toNodeId", args.beliefNodeId)
5129
3101
  ).first();
5130
3102
  if (edge) {
5131
- await ctx.scheduler.runAfter(0, internal2.neo4jEdgeAPI.deleteEdge, {
3103
+ await ctx.scheduler.runAfter(0, internal.neo4jEdgeAPI.deleteEdge, {
5132
3104
  globalId: edge.globalId
5133
3105
  });
5134
3106
  await ctx.db.delete(edge._id);
@@ -5244,7 +3216,7 @@ var deleteRelationship = mutation({
5244
3216
  toNodeId: edge.toNodeId,
5245
3217
  edgeType: edge.edgeType
5246
3218
  };
5247
- await ctx.scheduler.runAfter(0, internal2.neo4jEdgeAPI.deleteEdge, {
3219
+ await ctx.scheduler.runAfter(0, internal.neo4jEdgeAPI.deleteEdge, {
5248
3220
  globalId: edge.globalId
5249
3221
  });
5250
3222
  await ctx.db.delete(args.edgeId);
@@ -5295,7 +3267,7 @@ var updateCriticality = mutation({
5295
3267
  },
5296
3268
  updatedAt: now
5297
3269
  });
5298
- await ctx.scheduler.runAfter(0, internal2.neo4jSync.syncNodeToNeo4j, {
3270
+ await ctx.scheduler.runAfter(0, internal.neo4jSync.syncNodeToNeo4j, {
5299
3271
  nodeId: args.nodeId,
5300
3272
  operation: "upsert"
5301
3273
  });
@@ -5373,7 +3345,7 @@ var batchUpdateCriticality = mutation({
5373
3345
  },
5374
3346
  updatedAt: now
5375
3347
  });
5376
- await ctx.scheduler.runAfter(0, internal2.neo4jSync.syncNodeToNeo4j, {
3348
+ await ctx.scheduler.runAfter(0, internal.neo4jSync.syncNodeToNeo4j, {
5377
3349
  nodeId: node._id,
5378
3350
  operation: "upsert"
5379
3351
  });
@@ -5513,8 +3485,8 @@ var getByPillar = query({
5513
3485
  return [];
5514
3486
  }
5515
3487
  const nodes = await ctx.db.query("epistemicNodes").withIndex(
5516
- scope.topicId ? "by_topic_type" : "by_project_type",
5517
- (q) => scope.topicId ? q.eq("topicId", scope.topicId).eq("nodeType", "belief") : q.eq("projectId", scope.projectId).eq("nodeType", "belief")
3488
+ "by_topic_type",
3489
+ (q) => q.eq("topicId", scope.topicId).eq("nodeType", "belief")
5518
3490
  ).collect();
5519
3491
  return nodes.filter((n) => {
5520
3492
  const metadata = n.metadata;
@@ -5690,7 +3662,7 @@ var internalCreate = internalMutation({
5690
3662
  args: {
5691
3663
  ...optionalBeliefScopeArgs,
5692
3664
  formulation: v.string(),
5693
- baseRate: v.number(),
3665
+ baseRate: v.optional(v.number()),
5694
3666
  confidence: v.optional(
5695
3667
  v.union(v.literal("high"), v.literal("medium"), v.literal("low"))
5696
3668
  ),
@@ -5726,7 +3698,7 @@ var internalCreate = internalMutation({
5726
3698
  returns: permissiveReturn,
5727
3699
  handler: async (ctx, args) => {
5728
3700
  const now = Date.now();
5729
- const baseRate = assertBaseRateInRange(args.baseRate);
3701
+ const baseRate = assertBaseRateInRange(args.baseRate ?? 0.5);
5730
3702
  const scope = await resolveTopicProjectScope(ctx, {
5731
3703
  topicId: args.topicId,
5732
3704
  projectId: args.projectId
@@ -5817,7 +3789,7 @@ var internalCreate = internalMutation({
5817
3789
  slOperator: "manual_assessment"
5818
3790
  })
5819
3791
  );
5820
- await ctx.scheduler.runAfter(0, internal2.neo4jSync.syncNodeToNeo4j, {
3792
+ await ctx.scheduler.runAfter(0, internal.neo4jSync.syncNodeToNeo4j, {
5821
3793
  nodeId,
5822
3794
  operation: "upsert"
5823
3795
  });
@@ -5975,15 +3947,15 @@ var backfillSyntheticOpinionHistory = internalMutation({
5975
3947
  skippedHasHistory++;
5976
3948
  continue;
5977
3949
  }
5978
- const opinion2 = deriveSyntheticBackfillOpinion(node);
3950
+ const opinion = deriveSyntheticBackfillOpinion(node);
5979
3951
  await ctx.db.insert(
5980
3952
  "beliefConfidence",
5981
3953
  buildBeliefConfidenceRow({
5982
3954
  beliefId: node._id,
5983
- belief: opinion2.b,
5984
- disbelief: opinion2.d,
5985
- uncertainty: opinion2.u,
5986
- baseRate: opinion2.a,
3955
+ belief: opinion.b,
3956
+ disbelief: opinion.d,
3957
+ uncertainty: opinion.u,
3958
+ baseRate: opinion.a,
5987
3959
  trigger: "backfill_synthetic",
5988
3960
  rationale: "LK-6 backfill: synthesized t0 from node-level opinion fields (no prior beliefConfidence row found).",
5989
3961
  assessedAt: readFiniteNumber(node.createdAt) ?? readFiniteNumber(node.updatedAt) ?? Date.now(),
@@ -6226,7 +4198,7 @@ var backfillScoredBeliefEdges = internalMutation({
6226
4198
  continue;
6227
4199
  }
6228
4200
  if (!dryRun) {
6229
- await ctx.scheduler.runAfter(0, internal2.neo4jEdgeAPI.createEdge, {
4201
+ await ctx.scheduler.runAfter(0, internal.neo4jEdgeAPI.createEdge, {
6230
4202
  globalId: edgeGlobalId,
6231
4203
  fromGlobalId: belief.globalId,
6232
4204
  toGlobalId: theme.globalId,