@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.
- package/dist/beliefDecay.js +229 -1115
- 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 +27 -838
- package/dist/epistemicAnswers.js.map +1 -1
- package/dist/epistemicBeliefs.js +186 -2214
- package/dist/epistemicBeliefs.js.map +1 -1
- package/dist/epistemicContractHelpers.js +1 -318
- package/dist/epistemicContractHelpers.js.map +1 -1
- package/dist/epistemicContracts.js +163 -2467
- package/dist/epistemicContracts.js.map +1 -1
- package/dist/epistemicEdges.js +60 -863
- package/dist/epistemicEdges.js.map +1 -1
- package/dist/epistemicEvidence.js +116 -1647
- package/dist/epistemicEvidence.js.map +1 -1
- package/dist/epistemicHelpers.js +3 -2
- package/dist/epistemicHelpers.js.map +1 -1
- package/dist/epistemicLinking.js +2 -785
- package/dist/epistemicLinking.js.map +1 -1
- package/dist/epistemicNodes.js +34 -1427
- package/dist/epistemicNodes.js.map +1 -1
- package/dist/epistemicQuestions.js +88 -1637
- package/dist/epistemicQuestions.js.map +1 -1
- package/dist/epistemicSources.js +28 -1421
- package/dist/epistemicSources.js.map +1 -1
- package/dist/evaluators/index.js +163 -2467
- package/dist/evaluators/index.js.map +1 -1
- package/dist/index.js +486 -3649
- 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 +242 -837
- 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 +8 -609
- package/dist/workspaceIsolation.js.map +1 -1
- package/package.json +6 -6
package/dist/epistemicBeliefs.js
CHANGED
|
@@ -1,1467 +1,16 @@
|
|
|
1
1
|
import { v } from 'convex/values';
|
|
2
|
-
import {
|
|
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(
|
|
1482
|
-
if (typeof
|
|
30
|
+
function normalizeBeliefConfidence(confidence) {
|
|
31
|
+
if (typeof confidence !== "number" || !Number.isFinite(confidence)) {
|
|
1483
32
|
return null;
|
|
1484
33
|
}
|
|
1485
|
-
if (
|
|
1486
|
-
return
|
|
34
|
+
if (confidence >= 0 && confidence <= 1) {
|
|
35
|
+
return confidence;
|
|
1487
36
|
}
|
|
1488
|
-
if (
|
|
1489
|
-
return
|
|
37
|
+
if (confidence > 1 && confidence <= 100) {
|
|
38
|
+
return confidence / 100;
|
|
1490
39
|
}
|
|
1491
40
|
return null;
|
|
1492
41
|
}
|
|
1493
|
-
function isResolvedByConfidence(
|
|
1494
|
-
const normalized = normalizeBeliefConfidence(
|
|
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(
|
|
1501
|
-
if (!
|
|
49
|
+
function hasResolvedPredictionOutcome(predictionMeta) {
|
|
50
|
+
if (!predictionMeta || typeof predictionMeta !== "object") {
|
|
1502
51
|
return false;
|
|
1503
52
|
}
|
|
1504
|
-
const 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
|
|
105
|
+
var api = anyApi;
|
|
1557
106
|
componentsGeneric();
|
|
1558
|
-
var
|
|
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
|
-
|
|
1670
|
-
|
|
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
|
-
|
|
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[
|
|
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[
|
|
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(
|
|
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(
|
|
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(
|
|
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
|
|
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 (
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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:
|
|
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:
|
|
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
|
|
2984
|
-
function
|
|
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
|
|
964
|
+
function readStringArray(value) {
|
|
2992
965
|
if (!Array.isArray(value)) {
|
|
2993
966
|
return [];
|
|
2994
967
|
}
|
|
2995
|
-
return value.map((entry) =>
|
|
968
|
+
return value.map((entry) => readNonEmptyString(entry)).filter((entry) => Boolean(entry));
|
|
2996
969
|
}
|
|
2997
|
-
function
|
|
970
|
+
function readMetadata(topic) {
|
|
2998
971
|
return topic.metadata && typeof topic.metadata === "object" ? topic.metadata : {};
|
|
2999
972
|
}
|
|
3000
|
-
function
|
|
973
|
+
function readLegacyProjectId(value) {
|
|
3001
974
|
if (!value) {
|
|
3002
975
|
return;
|
|
3003
976
|
}
|
|
3004
|
-
return
|
|
977
|
+
return readNonEmptyString(value[LEGACY_SCOPE_FIELD2]);
|
|
3005
978
|
}
|
|
3006
|
-
function
|
|
979
|
+
function coerceVisibility(value) {
|
|
3007
980
|
return value === "private" || value === "team" || value === "firm" || value === "external" || value === "public" ? value : void 0;
|
|
3008
981
|
}
|
|
3009
|
-
function
|
|
982
|
+
function coerceStatus(value) {
|
|
3010
983
|
return value === "active" || value === "archived" || value === "watching" ? value : void 0;
|
|
3011
984
|
}
|
|
3012
|
-
function
|
|
3013
|
-
const explicit =
|
|
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
|
|
993
|
+
return readNonEmptyString(topic.type) || "general";
|
|
3021
994
|
}
|
|
3022
|
-
function
|
|
3023
|
-
const metadata =
|
|
3024
|
-
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;
|
|
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
|
|
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(
|
|
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(
|
|
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
|
|
3066
|
-
const metadata =
|
|
1038
|
+
function materializeTopicProjectOverlay(topic, idMode = "legacy") {
|
|
1039
|
+
const metadata = readMetadata(topic);
|
|
3067
1040
|
const topicId = String(topic._id);
|
|
3068
|
-
const 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 =
|
|
3072
|
-
const status =
|
|
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:
|
|
3083
|
-
type:
|
|
3084
|
-
description:
|
|
3085
|
-
ownerId:
|
|
3086
|
-
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:
|
|
3089
|
-
workspaceId:
|
|
1061
|
+
tenantId: readNonEmptyString(topic.tenantId) || readNonEmptyString(metadata.tenantId),
|
|
1062
|
+
workspaceId: readNonEmptyString(topic.workspaceId) || readNonEmptyString(metadata.workspaceId),
|
|
3090
1063
|
status,
|
|
3091
|
-
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
|
|
3101
|
-
const topic = await
|
|
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 && !
|
|
1078
|
+
if (options.projectLikeOnly !== false && !isProjectLikeTopic(topic)) {
|
|
3106
1079
|
return null;
|
|
3107
1080
|
}
|
|
3108
|
-
return
|
|
1081
|
+
return materializeTopicProjectOverlay(topic, options.idMode);
|
|
3109
1082
|
}
|
|
3110
|
-
async function
|
|
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(
|
|
1093
|
+
allTopics = (await ctx.runQuery(api.topics.list, {}) ?? []) || [];
|
|
3121
1094
|
}
|
|
3122
1095
|
return allTopics.filter(
|
|
3123
|
-
(topic) => options.projectLikeOnly === false ||
|
|
3124
|
-
).map((topic) =>
|
|
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
|
|
1100
|
+
const topic = await resolveTopicDoc(ctx, scopeId);
|
|
3128
1101
|
if (!topic) {
|
|
3129
1102
|
return null;
|
|
3130
1103
|
}
|
|
3131
|
-
const nextMetadata = { ...
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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(
|
|
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
|
|
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
|
|
1216
|
+
function defaultResolvers() {
|
|
3244
1217
|
return {
|
|
3245
1218
|
async getProject(ctx, projectId) {
|
|
3246
|
-
return await
|
|
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
|
|
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
|
|
1237
|
+
var resolverOverrides = {};
|
|
3265
1238
|
function resolveGraphPrimitivesAppResolvers(_ctx) {
|
|
3266
1239
|
return {
|
|
3267
|
-
...
|
|
3268
|
-
...
|
|
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
|
|
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
|
-
|
|
3344
|
-
|
|
3345
|
-
|
|
3346
|
-
|
|
3347
|
-
|
|
3348
|
-
});
|
|
1316
|
+
try {
|
|
1317
|
+
return readOpinionFromRecord(source);
|
|
1318
|
+
} catch {
|
|
1319
|
+
return mkOpinion(0, 0, 1, 0.5);
|
|
1320
|
+
}
|
|
3349
1321
|
}
|
|
3350
|
-
const
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
1365
|
+
const content = `belief:${text.trim().toLowerCase().replace(/\s+/g, " ")}`;
|
|
3398
1366
|
let hash = 5381;
|
|
3399
|
-
for (let i = 0; i <
|
|
3400
|
-
hash = (hash << 5) + hash +
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
3478
|
-
(q) =>
|
|
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
|
|
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
|
|
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,
|
|
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,
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
3830
|
-
|
|
3831
|
-
|
|
3832
|
-
|
|
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,
|
|
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
|
-
|
|
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
|
-
|
|
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,
|
|
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
|
-
|
|
4311
|
-
(q) =>
|
|
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,
|
|
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,
|
|
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
|
-
|
|
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.
|
|
4546
|
-
// a: prior probability [0,1]
|
|
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
|
-
|
|
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,
|
|
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
|
-
|
|
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,
|
|
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,
|
|
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
|
-
|
|
4951
|
-
(q) =>
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
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
|
-
|
|
5517
|
-
(q) =>
|
|
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,
|
|
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
|
|
3950
|
+
const opinion = deriveSyntheticBackfillOpinion(node);
|
|
5979
3951
|
await ctx.db.insert(
|
|
5980
3952
|
"beliefConfidence",
|
|
5981
3953
|
buildBeliefConfidenceRow({
|
|
5982
3954
|
beliefId: node._id,
|
|
5983
|
-
belief:
|
|
5984
|
-
disbelief:
|
|
5985
|
-
uncertainty:
|
|
5986
|
-
baseRate:
|
|
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,
|
|
4201
|
+
await ctx.scheduler.runAfter(0, internal.neo4jEdgeAPI.createEdge, {
|
|
6230
4202
|
globalId: edgeGlobalId,
|
|
6231
4203
|
fromGlobalId: belief.globalId,
|
|
6232
4204
|
toGlobalId: theme.globalId,
|