@lucern/graph-primitives 0.3.0-alpha.0 → 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.
- package/dist/beliefDecay.js +37 -1104
- package/dist/beliefDecay.js.map +1 -1
- package/dist/beliefEvidenceLinks.js +53 -834
- package/dist/beliefEvidenceLinks.js.map +1 -1
- package/dist/confidencePropagationDispatch.d.ts +3 -3
- package/dist/confidencePropagationDispatch.js +30 -308
- package/dist/confidencePropagationDispatch.js.map +1 -1
- package/dist/contradictions.js +5 -797
- package/dist/contradictions.js.map +1 -1
- package/dist/edges/contradicts.js +1 -122
- package/dist/edges/contradicts.js.map +1 -1
- package/dist/edges/dependsOn.js +14 -172
- package/dist/edges/dependsOn.js.map +1 -1
- package/dist/edges/elaborates.js +1 -49
- package/dist/edges/elaborates.js.map +1 -1
- package/dist/edges/index.js +14 -277
- package/dist/edges/index.js.map +1 -1
- package/dist/edges/informs.js +1 -62
- package/dist/edges/informs.js.map +1 -1
- package/dist/edges/propagationTypes.d.ts +2 -2
- package/dist/edges/propagationTypes.js.map +1 -1
- package/dist/edges/refutes.js +1 -62
- package/dist/edges/refutes.js.map +1 -1
- package/dist/edges/supports.js +1 -122
- package/dist/edges/supports.js.map +1 -1
- package/dist/edges/utils.d.ts +6 -6
- package/dist/edges/utils.js +1 -130
- package/dist/edges/utils.js.map +1 -1
- package/dist/entityBridge.js +2 -17
- package/dist/entityBridge.js.map +1 -1
- package/dist/entityLifecycle.js +62 -848
- package/dist/entityLifecycle.js.map +1 -1
- package/dist/epistemicAnswers.js +6 -802
- package/dist/epistemicAnswers.js.map +1 -1
- package/dist/epistemicBeliefs.js +125 -1594
- package/dist/epistemicBeliefs.js.map +1 -1
- package/dist/epistemicContractHelpers.js +1 -318
- package/dist/epistemicContractHelpers.js.map +1 -1
- package/dist/epistemicContracts.js +129 -1874
- package/dist/epistemicContracts.js.map +1 -1
- package/dist/epistemicEdges.js +60 -863
- package/dist/epistemicEdges.js.map +1 -1
- package/dist/epistemicEvidence.js +69 -1041
- package/dist/epistemicEvidence.js.map +1 -1
- package/dist/epistemicLinking.js +2 -785
- package/dist/epistemicLinking.js.map +1 -1
- package/dist/epistemicNodes.js +9 -866
- package/dist/epistemicNodes.js.map +1 -1
- package/dist/epistemicQuestions.js +66 -1071
- package/dist/epistemicQuestions.js.map +1 -1
- package/dist/epistemicSources.js +23 -880
- package/dist/epistemicSources.js.map +1 -1
- package/dist/evaluators/index.js +129 -1874
- package/dist/evaluators/index.js.map +1 -1
- package/dist/index.js +182 -2744
- package/dist/index.js.map +1 -1
- package/dist/ontology-matching.js +1 -344
- package/dist/ontology-matching.js.map +1 -1
- package/dist/ontologyApproval.js +1 -13
- package/dist/ontologyApproval.js.map +1 -1
- package/dist/ontologyDefinitions.js +2 -17
- package/dist/ontologyDefinitions.js.map +1 -1
- package/dist/ontologyRegistry.js +2 -17
- package/dist/ontologyRegistry.js.map +1 -1
- package/dist/projectionReconciliation.js +2 -17
- package/dist/projectionReconciliation.js.map +1 -1
- package/dist/questionEvidenceLinks.js +60 -841
- package/dist/questionEvidenceLinks.js.map +1 -1
- package/dist/text-matching.js +1 -244
- package/dist/text-matching.js.map +1 -1
- package/dist/workflowBridge.d.ts +27 -0
- package/dist/workflowBridge.js +303 -0
- package/dist/workflowBridge.js.map +1 -0
- package/dist/workspaceIsolation.js +2 -52
- package/dist/workspaceIsolation.js.map +1 -1
- package/package.json +6 -5
package/dist/epistemicBeliefs.js
CHANGED
|
@@ -1,1440 +1,16 @@
|
|
|
1
1
|
import { v } from 'convex/values';
|
|
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';
|
|
2
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
|
-
|
|
1287
|
-
// ../../packages/contracts/src/schema-helpers/enumValidation.ts
|
|
1288
|
-
var BUILTIN_ENUM_FALLBACK = {
|
|
1289
|
-
topic_type: /* @__PURE__ */ new Set([
|
|
1290
|
-
"domain",
|
|
1291
|
-
"theme",
|
|
1292
|
-
"deal",
|
|
1293
|
-
"strategy",
|
|
1294
|
-
"constitution",
|
|
1295
|
-
"project",
|
|
1296
|
-
"portfolio",
|
|
1297
|
-
"architecture",
|
|
1298
|
-
"capability",
|
|
1299
|
-
"runtime",
|
|
1300
|
-
"interface",
|
|
1301
|
-
"governance",
|
|
1302
|
-
"operations",
|
|
1303
|
-
"security",
|
|
1304
|
-
"data"
|
|
1305
|
-
]),
|
|
1306
|
-
branch_schema: /* @__PURE__ */ new Set(["pillar", "track", "dimension", "axis", "phase"]),
|
|
1307
|
-
lens_perspective_type: /* @__PURE__ */ new Set([
|
|
1308
|
-
"investigation",
|
|
1309
|
-
"monitoring",
|
|
1310
|
-
"analysis",
|
|
1311
|
-
"comparison",
|
|
1312
|
-
"taxonomy"
|
|
1313
|
-
]),
|
|
1314
|
-
belief_type: /* @__PURE__ */ new Set([
|
|
1315
|
-
"belief",
|
|
1316
|
-
"hypothesis",
|
|
1317
|
-
"principle",
|
|
1318
|
-
"invariant",
|
|
1319
|
-
"assumption",
|
|
1320
|
-
"tenet",
|
|
1321
|
-
"prior",
|
|
1322
|
-
"preference",
|
|
1323
|
-
"goal",
|
|
1324
|
-
"forecast",
|
|
1325
|
-
"decision",
|
|
1326
|
-
"constraint",
|
|
1327
|
-
"tradeoff",
|
|
1328
|
-
"policy",
|
|
1329
|
-
"implementation_choice",
|
|
1330
|
-
"implementation_decision",
|
|
1331
|
-
"interface_contract",
|
|
1332
|
-
"migration_state",
|
|
1333
|
-
"code_pattern",
|
|
1334
|
-
"deprecation_notice"
|
|
1335
|
-
]),
|
|
1336
|
-
edge_type: /* @__PURE__ */ new Set([
|
|
1337
|
-
"supports",
|
|
1338
|
-
"informs",
|
|
1339
|
-
"depends_on",
|
|
1340
|
-
"derived_from",
|
|
1341
|
-
"contains",
|
|
1342
|
-
"tests",
|
|
1343
|
-
"supersedes",
|
|
1344
|
-
"responds_to",
|
|
1345
|
-
"belongs_to",
|
|
1346
|
-
"relates_to_thesis",
|
|
1347
|
-
"works_at",
|
|
1348
|
-
"invested_in",
|
|
1349
|
-
"competes_with",
|
|
1350
|
-
"participates_in",
|
|
1351
|
-
"founded_by",
|
|
1352
|
-
"evaluates",
|
|
1353
|
-
"performs",
|
|
1354
|
-
"function_in",
|
|
1355
|
-
"impacts",
|
|
1356
|
-
"raised_from",
|
|
1357
|
-
"mentioned_in",
|
|
1358
|
-
"perspective_on",
|
|
1359
|
-
"plays_theme"
|
|
1360
|
-
]),
|
|
1361
|
-
worktree_type: /* @__PURE__ */ new Set([
|
|
1362
|
-
"belief_test",
|
|
1363
|
-
"existential",
|
|
1364
|
-
"contradiction",
|
|
1365
|
-
"refinement",
|
|
1366
|
-
"coverage",
|
|
1367
|
-
"discovery",
|
|
1368
|
-
"clarification",
|
|
1369
|
-
"confirmation"
|
|
1370
|
-
]),
|
|
1371
|
-
worktree_phase: /* @__PURE__ */ new Set([
|
|
1372
|
-
"cluster_mapping",
|
|
1373
|
-
"hypothesis_formation",
|
|
1374
|
-
"question_generation",
|
|
1375
|
-
"evidence_collection",
|
|
1376
|
-
"synthesis",
|
|
1377
|
-
"decision",
|
|
1378
|
-
"retrospective"
|
|
1379
|
-
]),
|
|
1380
|
-
activity_type: /* @__PURE__ */ new Set([
|
|
1381
|
-
"create",
|
|
1382
|
-
"update",
|
|
1383
|
-
"review",
|
|
1384
|
-
"merge",
|
|
1385
|
-
"archive",
|
|
1386
|
-
"comment",
|
|
1387
|
-
"status_change",
|
|
1388
|
-
"evidence_added",
|
|
1389
|
-
"question_added"
|
|
1390
|
-
])
|
|
1391
|
-
};
|
|
1392
|
-
function normalizeEnumValue(value) {
|
|
1393
|
-
return value.trim().toLowerCase();
|
|
1394
|
-
}
|
|
1395
|
-
async function validateSchemaEnumValue(_ctx, args) {
|
|
1396
|
-
const normalized = normalizeEnumValue(args.value);
|
|
1397
|
-
if (!normalized) {
|
|
1398
|
-
return { valid: false, source: "none" };
|
|
1399
|
-
}
|
|
1400
|
-
if (BUILTIN_ENUM_FALLBACK[args.category].has(normalized)) {
|
|
1401
|
-
return { valid: true, source: "builtin" };
|
|
1402
|
-
}
|
|
1403
|
-
return { valid: false, source: "none" };
|
|
1404
|
-
}
|
|
1405
|
-
async function assertSchemaEnumValue(ctx, args) {
|
|
1406
|
-
if (typeof args.value !== "string") {
|
|
1407
|
-
return;
|
|
1408
|
-
}
|
|
1409
|
-
const normalized = normalizeEnumValue(args.value);
|
|
1410
|
-
if (!normalized) {
|
|
1411
|
-
return;
|
|
1412
|
-
}
|
|
1413
|
-
const validation = await validateSchemaEnumValue(ctx, {
|
|
1414
|
-
category: args.category,
|
|
1415
|
-
value: normalized,
|
|
1416
|
-
tenantId: args.tenantId
|
|
1417
|
-
});
|
|
1418
|
-
if (!validation.valid) {
|
|
1419
|
-
const tenantHint = args.tenantId ? ` for tenant ${args.tenantId}` : "";
|
|
1420
|
-
throw new Error(
|
|
1421
|
-
`[${args.context}] Invalid value "${normalized}" for category "${args.category}"${tenantHint}. Add it to the contracts schema enum manifest before use.`
|
|
1422
|
-
);
|
|
1423
|
-
}
|
|
1424
|
-
return normalized;
|
|
1425
|
-
}
|
|
1426
|
-
var permissiveReturn = v.optional(v.any());
|
|
1427
|
-
var looseJsonObject = v.record(v.string(), v.any());
|
|
1428
|
-
var looseJsonArray = v.array(v.any());
|
|
1429
|
-
v.union(
|
|
1430
|
-
v.string(),
|
|
1431
|
-
v.number(),
|
|
1432
|
-
v.boolean(),
|
|
1433
|
-
v.null(),
|
|
1434
|
-
looseJsonObject,
|
|
1435
|
-
looseJsonArray
|
|
1436
|
-
);
|
|
1437
|
-
|
|
1438
14
|
// src/beliefLifecycle.ts
|
|
1439
15
|
var BELIEF_STATUS_VALUES = [
|
|
1440
16
|
"assumption",
|
|
@@ -1526,7 +102,7 @@ function promoteBeliefStatusAfterScoring(status, opts) {
|
|
|
1526
102
|
}
|
|
1527
103
|
return status === "fact" ? "fact" : "belief";
|
|
1528
104
|
}
|
|
1529
|
-
var
|
|
105
|
+
var api = anyApi;
|
|
1530
106
|
componentsGeneric();
|
|
1531
107
|
var internal = anyApi;
|
|
1532
108
|
var internalMutation = internalMutationGeneric;
|
|
@@ -1544,8 +120,6 @@ var containsPropagationSpec = {
|
|
|
1544
120
|
operator: () => null,
|
|
1545
121
|
description: "Structural containment only. Traversed for explicit semantics, but it never propagates opinions."
|
|
1546
122
|
};
|
|
1547
|
-
|
|
1548
|
-
// src/edges/utils.ts
|
|
1549
123
|
function readEdgeMetadata(edge) {
|
|
1550
124
|
return {
|
|
1551
125
|
constraint: edge.constraint ?? void 0,
|
|
@@ -1622,8 +196,6 @@ var contradictsPropagationSpec = {
|
|
|
1622
196
|
},
|
|
1623
197
|
description: "Legacy contradiction edges move negative pressure in either direction, but never beyond one hop."
|
|
1624
198
|
};
|
|
1625
|
-
|
|
1626
|
-
// src/edges/dependsOn.ts
|
|
1627
199
|
var dependsOnPropagationSpec = {
|
|
1628
200
|
edgeType: "depends_on",
|
|
1629
201
|
direction: "incoming",
|
|
@@ -1639,8 +211,18 @@ var dependsOnPropagationSpec = {
|
|
|
1639
211
|
if (metadata.conditionalA && metadata.conditionalNotA) {
|
|
1640
212
|
const deducedOpinion = conditionalDeduction(
|
|
1641
213
|
dampedSource,
|
|
1642
|
-
|
|
1643
|
-
|
|
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
|
+
),
|
|
1644
226
|
targetOpinion.a
|
|
1645
227
|
);
|
|
1646
228
|
return annotateRationale(
|
|
@@ -1812,70 +394,17 @@ function getTraversalDirections(direction) {
|
|
|
1812
394
|
}
|
|
1813
395
|
return ["outgoing", "incoming"];
|
|
1814
396
|
}
|
|
1815
|
-
|
|
1816
|
-
// ../../packages/contracts/src/schema-helpers/spine/tables/epistemicNodes.ts
|
|
1817
|
-
var NODE_TYPES = [
|
|
1818
|
-
"decision",
|
|
1819
|
-
"belief",
|
|
1820
|
-
"question",
|
|
1821
|
-
"theme",
|
|
1822
|
-
"deal",
|
|
1823
|
-
"topic",
|
|
1824
|
-
"claim",
|
|
1825
|
-
"evidence",
|
|
1826
|
-
"synthesis",
|
|
1827
|
-
"answer",
|
|
1828
|
-
"atomic_fact",
|
|
1829
|
-
"excerpt",
|
|
1830
|
-
"source",
|
|
1831
|
-
"company",
|
|
1832
|
-
"person",
|
|
1833
|
-
"investor",
|
|
1834
|
-
"function",
|
|
1835
|
-
"value_chain"
|
|
1836
|
-
];
|
|
1837
|
-
function isNodeType(value) {
|
|
1838
|
-
return NODE_TYPES.includes(value);
|
|
1839
|
-
}
|
|
1840
|
-
function getLayerForNodeType(type) {
|
|
1841
|
-
switch (type) {
|
|
1842
|
-
case "decision":
|
|
1843
|
-
return "L4";
|
|
1844
|
-
case "belief":
|
|
1845
|
-
case "question":
|
|
1846
|
-
case "theme":
|
|
1847
|
-
case "deal":
|
|
1848
|
-
return "L3";
|
|
1849
|
-
case "claim":
|
|
1850
|
-
case "evidence":
|
|
1851
|
-
case "synthesis":
|
|
1852
|
-
case "answer":
|
|
1853
|
-
return "L2";
|
|
1854
|
-
case "atomic_fact":
|
|
1855
|
-
case "excerpt":
|
|
1856
|
-
case "source":
|
|
1857
|
-
return "L1";
|
|
1858
|
-
case "topic":
|
|
1859
|
-
return "organizational";
|
|
1860
|
-
case "company":
|
|
1861
|
-
case "person":
|
|
1862
|
-
case "investor":
|
|
1863
|
-
case "function":
|
|
1864
|
-
case "value_chain":
|
|
1865
|
-
return "ontological";
|
|
1866
|
-
}
|
|
1867
|
-
}
|
|
1868
|
-
var LEGACY_SCOPE_FIELD2 = "graphScopeProjectId";
|
|
397
|
+
var LEGACY_SCOPE_FIELD = "graphScopeProjectId";
|
|
1869
398
|
function asMappedProjectId(topic) {
|
|
1870
399
|
if (!topic) {
|
|
1871
400
|
return;
|
|
1872
401
|
}
|
|
1873
|
-
const directLegacyProjectId = normalizeScopeValue(topic[
|
|
402
|
+
const directLegacyProjectId = normalizeScopeValue(topic[LEGACY_SCOPE_FIELD]);
|
|
1874
403
|
if (directLegacyProjectId) {
|
|
1875
404
|
return directLegacyProjectId;
|
|
1876
405
|
}
|
|
1877
406
|
const metadata = topic.metadata || {};
|
|
1878
|
-
const candidate = metadata[
|
|
407
|
+
const candidate = metadata[LEGACY_SCOPE_FIELD] || metadata.legacyProjectId || metadata.projectId || metadata.scopeProjectId;
|
|
1879
408
|
return candidate ? candidate : void 0;
|
|
1880
409
|
}
|
|
1881
410
|
function normalizeScopeValue(value) {
|
|
@@ -1904,7 +433,7 @@ async function findTopicsByScopeAlias(ctx, scopeId) {
|
|
|
1904
433
|
try {
|
|
1905
434
|
return await ctx.db.query("topics").withIndex(
|
|
1906
435
|
"by_graph_scope_project",
|
|
1907
|
-
(q) => q.eq(
|
|
436
|
+
(q) => q.eq(LEGACY_SCOPE_FIELD, scopeId)
|
|
1908
437
|
).collect();
|
|
1909
438
|
} catch {
|
|
1910
439
|
const topics = await ctx.db.query("topics").collect();
|
|
@@ -1920,7 +449,7 @@ async function tryResolveHostTopicById(ctx, topicId) {
|
|
|
1920
449
|
return null;
|
|
1921
450
|
}
|
|
1922
451
|
try {
|
|
1923
|
-
return await ctx.runQuery(
|
|
452
|
+
return await ctx.runQuery(api.topics.get, {
|
|
1924
453
|
id: topicId
|
|
1925
454
|
}) ?? null;
|
|
1926
455
|
} catch {
|
|
@@ -1932,7 +461,7 @@ async function tryResolveHostTopicByLegacyScope(ctx, legacyScopeId) {
|
|
|
1932
461
|
return null;
|
|
1933
462
|
}
|
|
1934
463
|
try {
|
|
1935
|
-
return await ctx.runQuery(
|
|
464
|
+
return await ctx.runQuery(api.topics.getByLegacyScopeId, {
|
|
1936
465
|
projectId: legacyScopeId
|
|
1937
466
|
}) ?? null;
|
|
1938
467
|
} catch {
|
|
@@ -2209,16 +738,17 @@ function resolveTraversalTargetNodeId(edge, direction) {
|
|
|
2209
738
|
}
|
|
2210
739
|
function readNodeOpinion(node) {
|
|
2211
740
|
const metadata = node.metadata ?? {};
|
|
2212
|
-
|
|
2213
|
-
{
|
|
741
|
+
try {
|
|
742
|
+
return readOpinionFromRecord({
|
|
2214
743
|
...metadata,
|
|
2215
744
|
opinion_b: node.opinion_b,
|
|
2216
745
|
opinion_d: node.opinion_d,
|
|
2217
746
|
opinion_u: node.opinion_u,
|
|
2218
747
|
opinion_a: node.opinion_a
|
|
2219
|
-
}
|
|
2220
|
-
|
|
2221
|
-
|
|
748
|
+
});
|
|
749
|
+
} catch {
|
|
750
|
+
return mkOpinion(0, 0, 1, 0.5);
|
|
751
|
+
}
|
|
2222
752
|
}
|
|
2223
753
|
async function collectConfidencePropagationDispatches(args) {
|
|
2224
754
|
const dispatchesByTargetId = /* @__PURE__ */ new Map();
|
|
@@ -2287,14 +817,20 @@ async function collectConfidencePropagationDispatches(args) {
|
|
|
2287
817
|
if (!result || !hasProjectedOpinionChanged(targetOpinion, result.opinion)) {
|
|
2288
818
|
continue;
|
|
2289
819
|
}
|
|
2290
|
-
|
|
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);
|
|
2291
827
|
const existingDispatch = dispatchesByTargetId.get(cacheKey);
|
|
2292
828
|
dispatchesByTargetId.set(cacheKey, {
|
|
2293
829
|
targetNodeId,
|
|
2294
830
|
edgeType: spec.edgeType,
|
|
2295
831
|
traversedDirection: direction,
|
|
2296
832
|
weight: edge.weight ?? 1,
|
|
2297
|
-
opinion:
|
|
833
|
+
opinion: projectedOpinion,
|
|
2298
834
|
operator: result.operator,
|
|
2299
835
|
rationale: existingDispatch ? `${existingDispatch.rationale}; ${result.rationale}` : result.rationale,
|
|
2300
836
|
hop: nextHop
|
|
@@ -2302,7 +838,7 @@ async function collectConfidencePropagationDispatches(args) {
|
|
|
2302
838
|
if (canContinueTransitively(spec, nextHop)) {
|
|
2303
839
|
queue.push({
|
|
2304
840
|
nodeId: targetNodeId,
|
|
2305
|
-
opinion:
|
|
841
|
+
opinion: projectedOpinion,
|
|
2306
842
|
hop: nextHop,
|
|
2307
843
|
visitedNodeIds: /* @__PURE__ */ new Set([
|
|
2308
844
|
...state.visitedNodeIds,
|
|
@@ -2417,48 +953,48 @@ async function getQuestionsAnsweredByEvidence(ctx, evidenceId) {
|
|
|
2417
953
|
}
|
|
2418
954
|
|
|
2419
955
|
// src/topicProjectOverlay.ts
|
|
2420
|
-
var
|
|
2421
|
-
function
|
|
956
|
+
var LEGACY_SCOPE_FIELD2 = "graphScopeProjectId";
|
|
957
|
+
function readNonEmptyString(value) {
|
|
2422
958
|
if (typeof value !== "string") {
|
|
2423
959
|
return;
|
|
2424
960
|
}
|
|
2425
961
|
const normalized = value.trim();
|
|
2426
962
|
return normalized.length > 0 ? normalized : void 0;
|
|
2427
963
|
}
|
|
2428
|
-
function
|
|
964
|
+
function readStringArray(value) {
|
|
2429
965
|
if (!Array.isArray(value)) {
|
|
2430
966
|
return [];
|
|
2431
967
|
}
|
|
2432
|
-
return value.map((entry) =>
|
|
968
|
+
return value.map((entry) => readNonEmptyString(entry)).filter((entry) => Boolean(entry));
|
|
2433
969
|
}
|
|
2434
|
-
function
|
|
970
|
+
function readMetadata(topic) {
|
|
2435
971
|
return topic.metadata && typeof topic.metadata === "object" ? topic.metadata : {};
|
|
2436
972
|
}
|
|
2437
|
-
function
|
|
973
|
+
function readLegacyProjectId(value) {
|
|
2438
974
|
if (!value) {
|
|
2439
975
|
return;
|
|
2440
976
|
}
|
|
2441
|
-
return
|
|
977
|
+
return readNonEmptyString(value[LEGACY_SCOPE_FIELD2]);
|
|
2442
978
|
}
|
|
2443
|
-
function
|
|
979
|
+
function coerceVisibility(value) {
|
|
2444
980
|
return value === "private" || value === "team" || value === "firm" || value === "external" || value === "public" ? value : void 0;
|
|
2445
981
|
}
|
|
2446
|
-
function
|
|
982
|
+
function coerceStatus(value) {
|
|
2447
983
|
return value === "active" || value === "archived" || value === "watching" ? value : void 0;
|
|
2448
984
|
}
|
|
2449
|
-
function
|
|
2450
|
-
const explicit =
|
|
985
|
+
function mapProjectType(topic, metadata) {
|
|
986
|
+
const explicit = readNonEmptyString(metadata.projectType);
|
|
2451
987
|
if (explicit) {
|
|
2452
988
|
return explicit;
|
|
2453
989
|
}
|
|
2454
990
|
if (topic.type === "theme") {
|
|
2455
991
|
return "thematic";
|
|
2456
992
|
}
|
|
2457
|
-
return
|
|
993
|
+
return readNonEmptyString(topic.type) || "general";
|
|
2458
994
|
}
|
|
2459
|
-
function
|
|
2460
|
-
const metadata =
|
|
2461
|
-
return topic.type === "theme" || topic.type === "thematic" || topic.type === "deal" || topic.type === "monitoring" ||
|
|
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;
|
|
2462
998
|
}
|
|
2463
999
|
function isMissingLucernChildComponentError(error) {
|
|
2464
1000
|
const message = error instanceof Error ? error.message : String(error);
|
|
@@ -2466,7 +1002,7 @@ function isMissingLucernChildComponentError(error) {
|
|
|
2466
1002
|
'Child component ComponentName(Identifier("lucern")) not found'
|
|
2467
1003
|
) || message.includes("Child component") && message.includes("lucern") && message.includes("not found");
|
|
2468
1004
|
}
|
|
2469
|
-
async function
|
|
1005
|
+
async function resolveTopicDoc(ctx, scopeId) {
|
|
2470
1006
|
if (ctx?.db && typeof ctx.db.get === "function") {
|
|
2471
1007
|
try {
|
|
2472
1008
|
const directTopic = await ctx.db.get(scopeId);
|
|
@@ -2480,7 +1016,7 @@ async function resolveTopicDoc2(ctx, scopeId) {
|
|
|
2480
1016
|
return null;
|
|
2481
1017
|
}
|
|
2482
1018
|
try {
|
|
2483
|
-
const topic = await ctx.runQuery(
|
|
1019
|
+
const topic = await ctx.runQuery(api.topics.get, {
|
|
2484
1020
|
id: String(scopeId)
|
|
2485
1021
|
});
|
|
2486
1022
|
if (topic?.name !== void 0 && topic?.type !== void 0) {
|
|
@@ -2489,7 +1025,7 @@ async function resolveTopicDoc2(ctx, scopeId) {
|
|
|
2489
1025
|
} catch {
|
|
2490
1026
|
}
|
|
2491
1027
|
try {
|
|
2492
|
-
const topic = await ctx.runQuery(
|
|
1028
|
+
const topic = await ctx.runQuery(api.topics.getByLegacyScopeId, {
|
|
2493
1029
|
projectId: String(scopeId)
|
|
2494
1030
|
});
|
|
2495
1031
|
if (topic?.name !== void 0 && topic?.type !== void 0) {
|
|
@@ -2499,14 +1035,14 @@ async function resolveTopicDoc2(ctx, scopeId) {
|
|
|
2499
1035
|
}
|
|
2500
1036
|
return null;
|
|
2501
1037
|
}
|
|
2502
|
-
function
|
|
2503
|
-
const metadata =
|
|
1038
|
+
function materializeTopicProjectOverlay(topic, idMode = "legacy") {
|
|
1039
|
+
const metadata = readMetadata(topic);
|
|
2504
1040
|
const topicId = String(topic._id);
|
|
2505
|
-
const legacyProjectId =
|
|
1041
|
+
const legacyProjectId = readLegacyProjectId(topic) || readLegacyProjectId(metadata) || readNonEmptyString(metadata.legacyProjectId);
|
|
2506
1042
|
const storageProjectId = legacyProjectId || topicId;
|
|
2507
1043
|
const outwardId = idMode === "topic" ? topicId : storageProjectId;
|
|
2508
|
-
const visibility =
|
|
2509
|
-
const status =
|
|
1044
|
+
const visibility = coerceVisibility(topic.visibility) || coerceVisibility(metadata.visibility) || "private";
|
|
1045
|
+
const status = coerceStatus(topic.status) || coerceStatus(metadata.status) || "active";
|
|
2510
1046
|
const createdAt = typeof topic.createdAt === "number" ? topic.createdAt : typeof topic._creationTime === "number" ? topic._creationTime : 0;
|
|
2511
1047
|
const updatedAt = typeof topic.updatedAt === "number" ? topic.updatedAt : typeof metadata.updatedAt === "number" ? metadata.updatedAt : createdAt;
|
|
2512
1048
|
return {
|
|
@@ -2516,16 +1052,16 @@ function materializeTopicProjectOverlay2(topic, idMode = "legacy") {
|
|
|
2516
1052
|
topicId,
|
|
2517
1053
|
storageProjectId,
|
|
2518
1054
|
legacyProjectId,
|
|
2519
|
-
name:
|
|
2520
|
-
type:
|
|
2521
|
-
description:
|
|
2522
|
-
ownerId:
|
|
2523
|
-
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),
|
|
2524
1060
|
visibility,
|
|
2525
|
-
tenantId:
|
|
2526
|
-
workspaceId:
|
|
1061
|
+
tenantId: readNonEmptyString(topic.tenantId) || readNonEmptyString(metadata.tenantId),
|
|
1062
|
+
workspaceId: readNonEmptyString(topic.workspaceId) || readNonEmptyString(metadata.workspaceId),
|
|
2527
1063
|
status,
|
|
2528
|
-
tags:
|
|
1064
|
+
tags: readStringArray(metadata.tags),
|
|
2529
1065
|
chatCount: typeof metadata.chatCount === "number" ? metadata.chatCount : 0,
|
|
2530
1066
|
artifactCount: typeof metadata.artifactCount === "number" ? metadata.artifactCount : 0,
|
|
2531
1067
|
lastActivityAt: typeof metadata.lastActivityAt === "number" ? metadata.lastActivityAt : updatedAt,
|
|
@@ -2534,17 +1070,17 @@ function materializeTopicProjectOverlay2(topic, idMode = "legacy") {
|
|
|
2534
1070
|
updatedAt
|
|
2535
1071
|
};
|
|
2536
1072
|
}
|
|
2537
|
-
async function
|
|
2538
|
-
const topic = await
|
|
1073
|
+
async function resolveTopicProjectOverlay(ctx, scopeId, options = {}) {
|
|
1074
|
+
const topic = await resolveTopicDoc(ctx, scopeId);
|
|
2539
1075
|
if (!topic) {
|
|
2540
1076
|
return null;
|
|
2541
1077
|
}
|
|
2542
|
-
if (options.projectLikeOnly !== false && !
|
|
1078
|
+
if (options.projectLikeOnly !== false && !isProjectLikeTopic(topic)) {
|
|
2543
1079
|
return null;
|
|
2544
1080
|
}
|
|
2545
|
-
return
|
|
1081
|
+
return materializeTopicProjectOverlay(topic, options.idMode);
|
|
2546
1082
|
}
|
|
2547
|
-
async function
|
|
1083
|
+
async function listTopicProjectOverlays(ctx, options = {}) {
|
|
2548
1084
|
let allTopics = [];
|
|
2549
1085
|
if (ctx?.db?.query && typeof ctx.db.query === "function") {
|
|
2550
1086
|
try {
|
|
@@ -2554,18 +1090,18 @@ async function listTopicProjectOverlays2(ctx, options = {}) {
|
|
|
2554
1090
|
}
|
|
2555
1091
|
}
|
|
2556
1092
|
if (allTopics.length === 0 && typeof ctx.runQuery === "function") {
|
|
2557
|
-
allTopics = (await ctx.runQuery(
|
|
1093
|
+
allTopics = (await ctx.runQuery(api.topics.list, {}) ?? []) || [];
|
|
2558
1094
|
}
|
|
2559
1095
|
return allTopics.filter(
|
|
2560
|
-
(topic) => options.projectLikeOnly === false ||
|
|
2561
|
-
).map((topic) =>
|
|
1096
|
+
(topic) => options.projectLikeOnly === false || isProjectLikeTopic(topic)
|
|
1097
|
+
).map((topic) => materializeTopicProjectOverlay(topic, options.idMode));
|
|
2562
1098
|
}
|
|
2563
1099
|
async function patchTopicProjectOverlay(ctx, scopeId, value) {
|
|
2564
|
-
const topic = await
|
|
1100
|
+
const topic = await resolveTopicDoc(ctx, scopeId);
|
|
2565
1101
|
if (!topic) {
|
|
2566
1102
|
return null;
|
|
2567
1103
|
}
|
|
2568
|
-
const nextMetadata = { ...
|
|
1104
|
+
const nextMetadata = { ...readMetadata(topic) };
|
|
2569
1105
|
const patch = {};
|
|
2570
1106
|
const topicUpdateArgs = {
|
|
2571
1107
|
id: String(topic._id)
|
|
@@ -2590,7 +1126,7 @@ async function patchTopicProjectOverlay(ctx, scopeId, value) {
|
|
|
2590
1126
|
`patchTopicProjectOverlay cannot mutate ${key} via component-owned topics`
|
|
2591
1127
|
);
|
|
2592
1128
|
case "status": {
|
|
2593
|
-
const status =
|
|
1129
|
+
const status = coerceStatus(rawValue);
|
|
2594
1130
|
if (status) {
|
|
2595
1131
|
patch.status = status;
|
|
2596
1132
|
topicUpdateArgs.status = status;
|
|
@@ -2598,7 +1134,7 @@ async function patchTopicProjectOverlay(ctx, scopeId, value) {
|
|
|
2598
1134
|
break;
|
|
2599
1135
|
}
|
|
2600
1136
|
case "visibility": {
|
|
2601
|
-
const visibility =
|
|
1137
|
+
const visibility = coerceVisibility(rawValue);
|
|
2602
1138
|
if (visibility) {
|
|
2603
1139
|
patch.visibility = visibility;
|
|
2604
1140
|
topicUpdateArgs.visibility = visibility;
|
|
@@ -2606,7 +1142,7 @@ async function patchTopicProjectOverlay(ctx, scopeId, value) {
|
|
|
2606
1142
|
break;
|
|
2607
1143
|
}
|
|
2608
1144
|
case "type": {
|
|
2609
|
-
const projectType =
|
|
1145
|
+
const projectType = readNonEmptyString(rawValue);
|
|
2610
1146
|
if (projectType) {
|
|
2611
1147
|
nextMetadata.projectType = projectType;
|
|
2612
1148
|
} else {
|
|
@@ -2630,7 +1166,7 @@ async function patchTopicProjectOverlay(ctx, scopeId, value) {
|
|
|
2630
1166
|
topicUpdateArgs.metadata = nextMetadata;
|
|
2631
1167
|
if (typeof ctx.runMutation === "function") {
|
|
2632
1168
|
try {
|
|
2633
|
-
await ctx.runMutation(
|
|
1169
|
+
await ctx.runMutation(api.topics.update, topicUpdateArgs);
|
|
2634
1170
|
} catch (error) {
|
|
2635
1171
|
if (!isMissingLucernChildComponentError(error) || !ctx?.db || typeof ctx.db.patch !== "function") {
|
|
2636
1172
|
throw error;
|
|
@@ -2644,7 +1180,7 @@ async function patchTopicProjectOverlay(ctx, scopeId, value) {
|
|
|
2644
1180
|
"Cannot patch topic without component adapter (ctx.runMutation unavailable)"
|
|
2645
1181
|
);
|
|
2646
1182
|
}
|
|
2647
|
-
return
|
|
1183
|
+
return materializeTopicProjectOverlay(
|
|
2648
1184
|
{
|
|
2649
1185
|
...topic,
|
|
2650
1186
|
...patch,
|
|
@@ -2677,10 +1213,10 @@ async function patchProjectWithTolerance(ctx, projectId, value) {
|
|
|
2677
1213
|
});
|
|
2678
1214
|
}
|
|
2679
1215
|
}
|
|
2680
|
-
function
|
|
1216
|
+
function defaultResolvers() {
|
|
2681
1217
|
return {
|
|
2682
1218
|
async getProject(ctx, projectId) {
|
|
2683
|
-
return await
|
|
1219
|
+
return await resolveTopicProjectOverlay(ctx, projectId, {
|
|
2684
1220
|
idMode: "legacy",
|
|
2685
1221
|
projectLikeOnly: false
|
|
2686
1222
|
});
|
|
@@ -2689,7 +1225,7 @@ function defaultResolvers2() {
|
|
|
2689
1225
|
await patchProjectWithTolerance(ctx, projectId, value);
|
|
2690
1226
|
},
|
|
2691
1227
|
async listTopics(ctx) {
|
|
2692
|
-
return await
|
|
1228
|
+
return await listTopicProjectOverlays(ctx, {
|
|
2693
1229
|
idMode: "legacy"
|
|
2694
1230
|
});
|
|
2695
1231
|
},
|
|
@@ -2698,11 +1234,11 @@ function defaultResolvers2() {
|
|
|
2698
1234
|
}
|
|
2699
1235
|
};
|
|
2700
1236
|
}
|
|
2701
|
-
var
|
|
1237
|
+
var resolverOverrides = {};
|
|
2702
1238
|
function resolveGraphPrimitivesAppResolvers(_ctx) {
|
|
2703
1239
|
return {
|
|
2704
|
-
...
|
|
2705
|
-
...
|
|
1240
|
+
...defaultResolvers(),
|
|
1241
|
+
...resolverOverrides
|
|
2706
1242
|
};
|
|
2707
1243
|
}
|
|
2708
1244
|
|
|
@@ -2727,7 +1263,7 @@ function throwStructuredMutationError(args) {
|
|
|
2727
1263
|
function readFiniteNumber(value) {
|
|
2728
1264
|
return typeof value === "number" && Number.isFinite(value) ? value : void 0;
|
|
2729
1265
|
}
|
|
2730
|
-
function
|
|
1266
|
+
function clamp01(value) {
|
|
2731
1267
|
return Math.max(0, Math.min(1, value));
|
|
2732
1268
|
}
|
|
2733
1269
|
function assertBaseRateInRange(baseRate, field = "baseRate") {
|
|
@@ -2777,20 +1313,14 @@ function deriveSyntheticBackfillOpinion(source) {
|
|
|
2777
1313
|
const uncertainty = readFiniteNumber(source.opinion_u) ?? readFiniteNumber(source.uncertainty);
|
|
2778
1314
|
const baseRate = readFiniteNumber(source.opinion_a) ?? readFiniteNumber(source.baseRate);
|
|
2779
1315
|
if (belief !== void 0 || disbelief !== void 0 || uncertainty !== void 0 || baseRate !== void 0) {
|
|
2780
|
-
|
|
2781
|
-
|
|
2782
|
-
|
|
2783
|
-
|
|
2784
|
-
|
|
2785
|
-
});
|
|
1316
|
+
try {
|
|
1317
|
+
return readOpinionFromRecord(source);
|
|
1318
|
+
} catch {
|
|
1319
|
+
return mkOpinion(0, 0, 1, 0.5);
|
|
1320
|
+
}
|
|
2786
1321
|
}
|
|
2787
|
-
const confidence =
|
|
2788
|
-
return
|
|
2789
|
-
b: confidence,
|
|
2790
|
-
d: 1 - confidence,
|
|
2791
|
-
u: 0,
|
|
2792
|
-
a: 0.5
|
|
2793
|
-
};
|
|
1322
|
+
const confidence = clamp01(readFiniteNumber(source.confidence) ?? 0);
|
|
1323
|
+
return mkOpinion(confidence, 1 - confidence, 0, 0.5);
|
|
2794
1324
|
}
|
|
2795
1325
|
function clampBeliefLimit(limit, fallback = DEFAULT_PROJECT_BELIEF_LIMIT) {
|
|
2796
1326
|
if (!Number.isFinite(limit)) {
|
|
@@ -2805,16 +1335,17 @@ function readTupleContradictedFlag(value) {
|
|
|
2805
1335
|
return typeof value === "boolean" ? value : void 0;
|
|
2806
1336
|
}
|
|
2807
1337
|
function readBeliefOpinionSnapshot(node, metadata) {
|
|
2808
|
-
|
|
2809
|
-
{
|
|
1338
|
+
try {
|
|
1339
|
+
return readOpinionFromRecord({
|
|
2810
1340
|
...metadata,
|
|
2811
1341
|
opinion_b: node.opinion_b,
|
|
2812
1342
|
opinion_d: node.opinion_d,
|
|
2813
1343
|
opinion_u: node.opinion_u,
|
|
2814
1344
|
opinion_a: node.opinion_a
|
|
2815
|
-
}
|
|
2816
|
-
|
|
2817
|
-
|
|
1345
|
+
});
|
|
1346
|
+
} catch {
|
|
1347
|
+
return mkOpinion(0, 0, 1, 0.5);
|
|
1348
|
+
}
|
|
2818
1349
|
}
|
|
2819
1350
|
function deriveTupleContradictionSeverity(node) {
|
|
2820
1351
|
const metadata = node.metadata || {};
|
|
@@ -3262,12 +1793,12 @@ var propagateConfidenceChange = internalMutation({
|
|
|
3262
1793
|
},
|
|
3263
1794
|
returns: permissiveReturn,
|
|
3264
1795
|
handler: async (ctx, args) => {
|
|
3265
|
-
const sourceOpinion =
|
|
3266
|
-
|
|
3267
|
-
|
|
3268
|
-
|
|
3269
|
-
|
|
3270
|
-
|
|
1796
|
+
const sourceOpinion = mkOpinion(
|
|
1797
|
+
args.opinion_b,
|
|
1798
|
+
args.opinion_d,
|
|
1799
|
+
args.opinion_u,
|
|
1800
|
+
args.opinion_a
|
|
1801
|
+
);
|
|
3271
1802
|
const sourceNode = await ctx.db.get(args.nodeId);
|
|
3272
1803
|
const sourceScope = await resolveNodeScopeForWorkspaceIsolation(
|
|
3273
1804
|
ctx,
|
|
@@ -3374,7 +1905,7 @@ var create = mutation({
|
|
|
3374
1905
|
expectedBy: v.optional(v.number())
|
|
3375
1906
|
})
|
|
3376
1907
|
),
|
|
3377
|
-
baseRate: v.number(),
|
|
1908
|
+
baseRate: v.optional(v.number()),
|
|
3378
1909
|
metadata: v.optional(v.any())
|
|
3379
1910
|
// Additional metadata including isConditional
|
|
3380
1911
|
},
|
|
@@ -3405,7 +1936,7 @@ var create = mutation({
|
|
|
3405
1936
|
);
|
|
3406
1937
|
}
|
|
3407
1938
|
const now = Date.now();
|
|
3408
|
-
const baseRate = assertBaseRateInRange(args.baseRate);
|
|
1939
|
+
const baseRate = assertBaseRateInRange(args.baseRate ?? 0.5);
|
|
3409
1940
|
const initialBeliefStatus = args.worktreeId ? "hypothesis" : "assumption";
|
|
3410
1941
|
const initialEpistemicStatus = args.worktreeId ? "hypothesis" : "assumption";
|
|
3411
1942
|
const seedOpinion = {
|
|
@@ -3979,8 +2510,8 @@ var modulateConfidence = mutation({
|
|
|
3979
2510
|
// d: evidence AGAINST [0,1]
|
|
3980
2511
|
uncertainty: v.number(),
|
|
3981
2512
|
// u: lack of evidence [0,1]
|
|
3982
|
-
baseRate: v.
|
|
3983
|
-
// a: prior probability [0,1]
|
|
2513
|
+
baseRate: v.number(),
|
|
2514
|
+
// a: prior probability [0,1]
|
|
3984
2515
|
trigger: v.union(
|
|
3985
2516
|
v.literal("evidence_added"),
|
|
3986
2517
|
v.literal("evidence_removed"),
|
|
@@ -5131,7 +3662,7 @@ var internalCreate = internalMutation({
|
|
|
5131
3662
|
args: {
|
|
5132
3663
|
...optionalBeliefScopeArgs,
|
|
5133
3664
|
formulation: v.string(),
|
|
5134
|
-
baseRate: v.number(),
|
|
3665
|
+
baseRate: v.optional(v.number()),
|
|
5135
3666
|
confidence: v.optional(
|
|
5136
3667
|
v.union(v.literal("high"), v.literal("medium"), v.literal("low"))
|
|
5137
3668
|
),
|
|
@@ -5167,7 +3698,7 @@ var internalCreate = internalMutation({
|
|
|
5167
3698
|
returns: permissiveReturn,
|
|
5168
3699
|
handler: async (ctx, args) => {
|
|
5169
3700
|
const now = Date.now();
|
|
5170
|
-
const baseRate = assertBaseRateInRange(args.baseRate);
|
|
3701
|
+
const baseRate = assertBaseRateInRange(args.baseRate ?? 0.5);
|
|
5171
3702
|
const scope = await resolveTopicProjectScope(ctx, {
|
|
5172
3703
|
topicId: args.topicId,
|
|
5173
3704
|
projectId: args.projectId
|
|
@@ -5416,15 +3947,15 @@ var backfillSyntheticOpinionHistory = internalMutation({
|
|
|
5416
3947
|
skippedHasHistory++;
|
|
5417
3948
|
continue;
|
|
5418
3949
|
}
|
|
5419
|
-
const
|
|
3950
|
+
const opinion = deriveSyntheticBackfillOpinion(node);
|
|
5420
3951
|
await ctx.db.insert(
|
|
5421
3952
|
"beliefConfidence",
|
|
5422
3953
|
buildBeliefConfidenceRow({
|
|
5423
3954
|
beliefId: node._id,
|
|
5424
|
-
belief:
|
|
5425
|
-
disbelief:
|
|
5426
|
-
uncertainty:
|
|
5427
|
-
baseRate:
|
|
3955
|
+
belief: opinion.b,
|
|
3956
|
+
disbelief: opinion.d,
|
|
3957
|
+
uncertainty: opinion.u,
|
|
3958
|
+
baseRate: opinion.a,
|
|
5428
3959
|
trigger: "backfill_synthetic",
|
|
5429
3960
|
rationale: "LK-6 backfill: synthesized t0 from node-level opinion fields (no prior beliefConfidence row found).",
|
|
5430
3961
|
assessedAt: readFiniteNumber(node.createdAt) ?? readFiniteNumber(node.updatedAt) ?? Date.now(),
|