@lucern/graph-primitives 0.3.0-alpha.0 → 0.3.0-alpha.10
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-Q_26RTc-.d.ts → beliefDecay-DZ6tkLYq.d.ts} +1 -1
- package/dist/beliefDecay.d.ts +1 -1
- package/dist/beliefDecay.js +188 -1144
- package/dist/beliefDecay.js.map +1 -1
- package/dist/{beliefEvidenceLinks-42FlR48t.d.ts → beliefEvidenceLinks-CWOXxxJg.d.ts} +1 -1
- package/dist/beliefEvidenceLinks.d.ts +1 -1
- package/dist/beliefEvidenceLinks.js +186 -871
- package/dist/beliefEvidenceLinks.js.map +1 -1
- package/dist/{beliefLifecycle-C-AehZgF.d.ts → beliefLifecycle-y8WLXqQj.d.ts} +1 -1
- package/dist/beliefLifecycle.d.ts +1 -1
- package/dist/confidencePropagationDispatch.d.ts +4 -4
- package/dist/confidencePropagationDispatch.js +31 -311
- package/dist/confidencePropagationDispatch.js.map +1 -1
- package/dist/{contradictions-Hdwl7zid.d.ts → contradictions-51VLsESq.d.ts} +1 -1
- package/dist/contradictions.d.ts +1 -1
- package/dist/contradictions.js +67 -800
- package/dist/contradictions.js.map +1 -1
- package/dist/debug.d.ts +4 -0
- package/dist/debug.js +34 -0
- package/dist/debug.js.map +1 -0
- 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 +15 -280
- package/dist/edges/index.js.map +1 -1
- package/dist/edges/informs.js +2 -65
- 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 +2 -65
- 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 +7 -7
- package/dist/edges/utils.js +2 -133
- package/dist/edges/utils.js.map +1 -1
- package/dist/embeddingTrigger.js +21 -1
- package/dist/embeddingTrigger.js.map +1 -1
- package/dist/entityBridge.js +3 -18
- package/dist/entityBridge.js.map +1 -1
- package/dist/{entityLifecycle-BkhRJ-XI.d.ts → entityLifecycle-CvgSK5FV.d.ts} +1 -1
- package/dist/entityLifecycle.d.ts +1 -1
- package/dist/entityLifecycle.js +193 -892
- package/dist/entityLifecycle.js.map +1 -1
- package/dist/{epistemicAnswers-DSP1slZ9.d.ts → epistemicAnswers-C5ib4z6_.d.ts} +1 -1
- package/dist/epistemicAnswers.d.ts +1 -1
- package/dist/epistemicAnswers.js +73 -810
- package/dist/epistemicAnswers.js.map +1 -1
- package/dist/{epistemicBeliefs-DtFVTp-k.d.ts → epistemicBeliefs-DzKjZAeC.d.ts} +3 -3
- package/dist/epistemicBeliefs.d.ts +2 -2
- package/dist/epistemicBeliefs.js +404 -1698
- package/dist/epistemicBeliefs.js.map +1 -1
- package/dist/epistemicContractHelpers.js +1 -318
- package/dist/epistemicContractHelpers.js.map +1 -1
- package/dist/epistemicContracts.d.ts +1 -1
- package/dist/epistemicContracts.js +417 -1980
- package/dist/epistemicContracts.js.map +1 -1
- package/dist/{epistemicEdges-DcA8ErUG.d.ts → epistemicEdges-CD5vxmlH.d.ts} +3 -3
- package/dist/epistemicEdges.d.ts +1 -1
- package/dist/epistemicEdges.js +248 -919
- package/dist/epistemicEdges.js.map +1 -1
- package/dist/{epistemicEvidence-Bo638XDP.d.ts → epistemicEvidence-xw6UUrwh.d.ts} +1 -1
- package/dist/epistemicEvidence.d.ts +1 -1
- package/dist/epistemicEvidence.js +229 -1087
- package/dist/epistemicEvidence.js.map +1 -1
- package/dist/{epistemicHelpers-Bd9xbaib.d.ts → epistemicHelpers-DevrYgPN.d.ts} +1 -1
- package/dist/epistemicHelpers.d.ts +1 -1
- package/dist/{epistemicLinking-CyeLOIzN.d.ts → epistemicLinking-CfE00tHJ.d.ts} +1 -1
- package/dist/epistemicLinking.d.ts +1 -1
- package/dist/epistemicLinking.js +3 -786
- package/dist/epistemicLinking.js.map +1 -1
- package/dist/{epistemicNodes-BpD6Koud.d.ts → epistemicNodes-NBrPW7fk.d.ts} +2 -2
- package/dist/epistemicNodes.d.ts +1 -1
- package/dist/epistemicNodes.js +172 -899
- package/dist/epistemicNodes.js.map +1 -1
- package/dist/{epistemicQuestions-CmEeY6zQ.d.ts → epistemicQuestions-B_nUclrH.d.ts} +1 -1
- package/dist/epistemicQuestions.d.ts +1 -1
- package/dist/epistemicQuestions.js +369 -1125
- package/dist/epistemicQuestions.js.map +1 -1
- package/dist/{epistemicSources-ZazxHOK1.d.ts → epistemicSources-dlKj58Jp.d.ts} +1 -1
- package/dist/epistemicSources.d.ts +1 -1
- package/dist/epistemicSources.js +86 -886
- package/dist/epistemicSources.js.map +1 -1
- package/dist/evaluators/index.js +417 -1980
- package/dist/evaluators/index.js.map +1 -1
- package/dist/evaluators/lintCheckerEvaluator.js.map +1 -1
- package/dist/evaluators/sentryCheckerEvaluator.js.map +1 -1
- package/dist/evaluators/shared.js +20 -1
- package/dist/evaluators/shared.js.map +1 -1
- package/dist/evaluators/testRunnerEvaluator.js +20 -1
- package/dist/evaluators/testRunnerEvaluator.js.map +1 -1
- package/dist/evaluators/tscCheckerEvaluator.js.map +1 -1
- package/dist/index.d.ts +20 -20
- package/dist/index.js +965 -3004
- package/dist/index.js.map +1 -1
- package/dist/{ontology-matching-Buhu23ss.d.ts → ontology-matching-C6rrz2VP.d.ts} +1 -1
- package/dist/ontology-matching.d.ts +1 -1
- package/dist/ontology-matching.js +1 -344
- package/dist/ontology-matching.js.map +1 -1
- package/dist/{ontologyApproval-Ba0Jjk1k.d.ts → ontologyApproval-CFYmqKmk.d.ts} +1 -1
- package/dist/ontologyApproval.d.ts +1 -1
- package/dist/ontologyApproval.js +1 -13
- package/dist/ontologyApproval.js.map +1 -1
- package/dist/ontologyDefinitions.js +6 -20
- package/dist/ontologyDefinitions.js.map +1 -1
- package/dist/ontologyHelpers.d.ts +1 -1
- package/dist/ontologyHelpers.js +4 -3
- package/dist/ontologyHelpers.js.map +1 -1
- package/dist/ontologyRegistry.js +2 -17
- package/dist/ontologyRegistry.js.map +1 -1
- package/dist/{projectionReconciliation-CxrXYGaB.d.ts → projectionReconciliation-jww2fBI0.d.ts} +1 -1
- package/dist/projectionReconciliation.d.ts +1 -1
- package/dist/projectionReconciliation.js +16 -37
- package/dist/projectionReconciliation.js.map +1 -1
- package/dist/{projectionStaleness-CAdpIsaW.d.ts → projectionStaleness-CmdbpjVK.d.ts} +1 -1
- package/dist/projectionStaleness.d.ts +1 -1
- package/dist/{questionEvidenceLinks-BdQD0TkM.d.ts → questionEvidenceLinks-DFlyPpAj.d.ts} +1 -1
- package/dist/questionEvidenceLinks.d.ts +1 -1
- package/dist/questionEvidenceLinks.js +199 -881
- package/dist/questionEvidenceLinks.js.map +1 -1
- package/dist/resolvers.js +86 -37
- package/dist/resolvers.js.map +1 -1
- package/dist/scopeResolverCompat.js +64 -7
- package/dist/scopeResolverCompat.js.map +1 -1
- package/dist/{text-matching-CMn2WnVD.d.ts → text-matching-DNg4M5Wd.d.ts} +1 -1
- package/dist/text-matching.d.ts +1 -1
- package/dist/text-matching.js +1 -244
- package/dist/text-matching.js.map +1 -1
- package/dist/topicProjectOverlay.js +56 -13
- package/dist/topicProjectOverlay.js.map +1 -1
- package/dist/topicScope.js +55 -6
- package/dist/topicScope.js.map +1 -1
- package/dist/workflowBridge.d.ts +27 -0
- package/dist/workflowBridge.js +352 -0
- package/dist/workflowBridge.js.map +1 -0
- package/dist/workspaceIsolation.js +56 -57
- package/dist/workspaceIsolation.js.map +1 -1
- package/package.json +6 -5
|
@@ -1,1722 +1,22 @@
|
|
|
1
1
|
import { v } from 'convex/values';
|
|
2
|
-
import {
|
|
2
|
+
import { checkScopeAccess, checkProjectAccess } from '@lucern/access-control/access';
|
|
3
|
+
import { getCurrentUserId } from '@lucern/access-control/auth';
|
|
4
|
+
import { permissiveReturn } from '@lucern/contracts/schema-helpers/validators';
|
|
5
|
+
import { componentsGeneric, anyApi, internalMutationGeneric, mutationGeneric, queryGeneric, internalQueryGeneric } from 'convex/server';
|
|
6
|
+
import { normalizeTupleContradictionPolicy, mkOpinion, createInheritedContractRecord, confidenceFromSL, conditionalDeduction, project, dampedDependencyCascade, hasProjectedOpinionChanged, detectTupleContradiction, evaluateTupleContradictionTransition, readOpinionFromRecord, deriveContractModulationPlan, deriveContractStatus, trustDiscount, applyNegativeSupport, cumulativeFusion, applyNegativeEvidence, parseEvidentialEvaluatorConfig, compareMetricValue, buildEvidentialRationale, parseMetricCheckerConfig, getEvaluatorInputRecord, pickFiniteNumber, resolveComparisonResult, buildComparisonRationale, parseReferenceCheckCounterConfig, parseTemporalDeadlineConfig, parseMarketIndexComparatorConfig } from '@lucern/confidence';
|
|
7
|
+
import { canAudienceClassAccess, normalizeAudienceKey, classFromAudienceKey } from '@lucern/access-control/audience';
|
|
8
|
+
import { listAudienceRegistryRows } from '@lucern/access-control/audienceRegistry';
|
|
9
|
+
import { assertSchemaEnumValue } from '@lucern/contracts/schema-helpers/enumValidation';
|
|
10
|
+
import { isNodeType, getLayerForNodeType } from '@lucern/contracts/schema-helpers/spine/tables/epistemicNodes';
|
|
3
11
|
|
|
4
12
|
// src/epistemicContracts.ts
|
|
5
13
|
var api = anyApi;
|
|
6
14
|
componentsGeneric();
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
var
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
return;
|
|
13
|
-
}
|
|
14
|
-
const normalized = value.trim();
|
|
15
|
-
return normalized.length > 0 ? normalized : void 0;
|
|
16
|
-
}
|
|
17
|
-
function readStringArray(value) {
|
|
18
|
-
if (!Array.isArray(value)) {
|
|
19
|
-
return [];
|
|
20
|
-
}
|
|
21
|
-
return value.map((entry) => readNonEmptyString(entry)).filter((entry) => Boolean(entry));
|
|
22
|
-
}
|
|
23
|
-
function readMetadata(topic) {
|
|
24
|
-
return topic.metadata && typeof topic.metadata === "object" ? topic.metadata : {};
|
|
25
|
-
}
|
|
26
|
-
function readLegacyProjectId(value) {
|
|
27
|
-
if (!value) {
|
|
28
|
-
return;
|
|
29
|
-
}
|
|
30
|
-
return readNonEmptyString(value[LEGACY_SCOPE_FIELD]);
|
|
31
|
-
}
|
|
32
|
-
function coerceVisibility(value) {
|
|
33
|
-
return value === "private" || value === "team" || value === "firm" || value === "external" || value === "public" ? value : void 0;
|
|
34
|
-
}
|
|
35
|
-
function coerceStatus(value) {
|
|
36
|
-
return value === "active" || value === "archived" || value === "watching" ? value : void 0;
|
|
37
|
-
}
|
|
38
|
-
function mapProjectType(topic, metadata) {
|
|
39
|
-
const explicit = readNonEmptyString(metadata.projectType);
|
|
40
|
-
if (explicit) {
|
|
41
|
-
return explicit;
|
|
42
|
-
}
|
|
43
|
-
if (topic.type === "theme") {
|
|
44
|
-
return "thematic";
|
|
45
|
-
}
|
|
46
|
-
return readNonEmptyString(topic.type) || "general";
|
|
47
|
-
}
|
|
48
|
-
function isProjectLikeTopic(topic) {
|
|
49
|
-
const metadata = readMetadata(topic);
|
|
50
|
-
return topic.type === "theme" || topic.type === "thematic" || topic.type === "deal" || topic.type === "monitoring" || readLegacyProjectId(topic) !== void 0 || readNonEmptyString(metadata.projectType) !== void 0;
|
|
51
|
-
}
|
|
52
|
-
async function resolveTopicDoc(ctx, scopeId) {
|
|
53
|
-
if (ctx?.db && typeof ctx.db.get === "function") {
|
|
54
|
-
try {
|
|
55
|
-
const directTopic = await ctx.db.get(scopeId);
|
|
56
|
-
if (directTopic) {
|
|
57
|
-
return directTopic;
|
|
58
|
-
}
|
|
59
|
-
} catch {
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
|
-
if (typeof ctx.runQuery !== "function") {
|
|
63
|
-
return null;
|
|
64
|
-
}
|
|
65
|
-
try {
|
|
66
|
-
const topic = await ctx.runQuery(api.topics.get, {
|
|
67
|
-
id: String(scopeId)
|
|
68
|
-
});
|
|
69
|
-
if (topic?.name !== void 0 && topic?.type !== void 0) {
|
|
70
|
-
return topic;
|
|
71
|
-
}
|
|
72
|
-
} catch {
|
|
73
|
-
}
|
|
74
|
-
try {
|
|
75
|
-
const topic = await ctx.runQuery(api.topics.getByLegacyScopeId, {
|
|
76
|
-
projectId: String(scopeId)
|
|
77
|
-
});
|
|
78
|
-
if (topic?.name !== void 0 && topic?.type !== void 0) {
|
|
79
|
-
return topic;
|
|
80
|
-
}
|
|
81
|
-
} catch {
|
|
82
|
-
}
|
|
83
|
-
return null;
|
|
84
|
-
}
|
|
85
|
-
function materializeTopicProjectOverlay(topic, idMode = "legacy") {
|
|
86
|
-
const metadata = readMetadata(topic);
|
|
87
|
-
const topicId = String(topic._id);
|
|
88
|
-
const legacyProjectId = readLegacyProjectId(topic) || readLegacyProjectId(metadata) || readNonEmptyString(metadata.legacyProjectId);
|
|
89
|
-
const storageProjectId = legacyProjectId || topicId;
|
|
90
|
-
const outwardId = idMode === "topic" ? topicId : storageProjectId;
|
|
91
|
-
const visibility = coerceVisibility(topic.visibility) || coerceVisibility(metadata.visibility) || "private";
|
|
92
|
-
const status = coerceStatus(topic.status) || coerceStatus(metadata.status) || "active";
|
|
93
|
-
const createdAt = typeof topic.createdAt === "number" ? topic.createdAt : typeof topic._creationTime === "number" ? topic._creationTime : 0;
|
|
94
|
-
const updatedAt = typeof topic.updatedAt === "number" ? topic.updatedAt : typeof metadata.updatedAt === "number" ? metadata.updatedAt : createdAt;
|
|
95
|
-
return {
|
|
96
|
-
...metadata,
|
|
97
|
-
_id: outwardId,
|
|
98
|
-
projectId: outwardId,
|
|
99
|
-
topicId,
|
|
100
|
-
storageProjectId,
|
|
101
|
-
legacyProjectId,
|
|
102
|
-
name: readNonEmptyString(topic.name) || "Untitled Theme",
|
|
103
|
-
type: mapProjectType(topic, metadata),
|
|
104
|
-
description: readNonEmptyString(topic.description),
|
|
105
|
-
ownerId: readNonEmptyString(metadata.ownerId) || readNonEmptyString(topic.createdBy) || "system",
|
|
106
|
-
sharedWith: readStringArray(metadata.sharedWith),
|
|
107
|
-
visibility,
|
|
108
|
-
tenantId: readNonEmptyString(topic.tenantId) || readNonEmptyString(metadata.tenantId),
|
|
109
|
-
workspaceId: readNonEmptyString(topic.workspaceId) || readNonEmptyString(metadata.workspaceId),
|
|
110
|
-
status,
|
|
111
|
-
tags: readStringArray(metadata.tags),
|
|
112
|
-
chatCount: typeof metadata.chatCount === "number" ? metadata.chatCount : 0,
|
|
113
|
-
artifactCount: typeof metadata.artifactCount === "number" ? metadata.artifactCount : 0,
|
|
114
|
-
lastActivityAt: typeof metadata.lastActivityAt === "number" ? metadata.lastActivityAt : updatedAt,
|
|
115
|
-
_creationTime: typeof topic._creationTime === "number" ? topic._creationTime : createdAt,
|
|
116
|
-
createdAt,
|
|
117
|
-
updatedAt
|
|
118
|
-
};
|
|
119
|
-
}
|
|
120
|
-
async function resolveTopicProjectOverlay(ctx, scopeId, options = {}) {
|
|
121
|
-
const topic = await resolveTopicDoc(ctx, scopeId);
|
|
122
|
-
if (!topic) {
|
|
123
|
-
return null;
|
|
124
|
-
}
|
|
125
|
-
if (options.projectLikeOnly !== false && !isProjectLikeTopic(topic)) {
|
|
126
|
-
return null;
|
|
127
|
-
}
|
|
128
|
-
return materializeTopicProjectOverlay(topic, options.idMode);
|
|
129
|
-
}
|
|
130
|
-
async function listTopicProjectOverlays(ctx, options = {}) {
|
|
131
|
-
let allTopics = [];
|
|
132
|
-
if (ctx?.db?.query && typeof ctx.db.query === "function") {
|
|
133
|
-
try {
|
|
134
|
-
allTopics = await ctx.db.query("topics").collect();
|
|
135
|
-
} catch {
|
|
136
|
-
allTopics = [];
|
|
137
|
-
}
|
|
138
|
-
}
|
|
139
|
-
if (allTopics.length === 0 && typeof ctx.runQuery === "function") {
|
|
140
|
-
allTopics = (await ctx.runQuery(api.topics.list, {}) ?? []) || [];
|
|
141
|
-
}
|
|
142
|
-
return allTopics.filter(
|
|
143
|
-
(topic) => options.projectLikeOnly === false || isProjectLikeTopic(topic)
|
|
144
|
-
).map((topic) => materializeTopicProjectOverlay(topic, options.idMode));
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
// ../access-control/src/projectGrantsBridge.ts
|
|
148
|
-
var PROJECT_GRANT_STATUSES = ["active", "revoked", "expired"];
|
|
149
|
-
function normalizeString(value) {
|
|
150
|
-
if (typeof value !== "string") {
|
|
151
|
-
return;
|
|
152
|
-
}
|
|
153
|
-
const trimmed = value.trim();
|
|
154
|
-
return trimmed.length > 0 ? trimmed : void 0;
|
|
155
|
-
}
|
|
156
|
-
async function resolveGrantScopeIds(ctx, args) {
|
|
157
|
-
const topicId = normalizeString(args.topicId);
|
|
158
|
-
const projectId = normalizeString(args.projectId);
|
|
159
|
-
for (const scopeId of [topicId, projectId]) {
|
|
160
|
-
if (!scopeId) {
|
|
161
|
-
continue;
|
|
162
|
-
}
|
|
163
|
-
try {
|
|
164
|
-
const overlay = await resolveTopicProjectOverlay(ctx, scopeId, {
|
|
165
|
-
idMode: "legacy",
|
|
166
|
-
projectLikeOnly: false
|
|
167
|
-
});
|
|
168
|
-
if (overlay) {
|
|
169
|
-
return {
|
|
170
|
-
topicId: normalizeString(overlay.topicId) ?? topicId,
|
|
171
|
-
projectId: normalizeString(overlay.projectId) ?? projectId ?? scopeId
|
|
172
|
-
};
|
|
173
|
-
}
|
|
174
|
-
} catch {
|
|
175
|
-
}
|
|
176
|
-
}
|
|
177
|
-
return { topicId, projectId };
|
|
178
|
-
}
|
|
179
|
-
async function normalizeProjectGrantRow(ctx, row) {
|
|
180
|
-
const scope = await resolveGrantScopeIds(ctx, {
|
|
181
|
-
topicId: row.topicId,
|
|
182
|
-
projectId: row.projectId
|
|
183
|
-
});
|
|
184
|
-
return {
|
|
185
|
-
...row,
|
|
186
|
-
...scope.topicId ? { topicId: scope.topicId } : {},
|
|
187
|
-
...scope.projectId ?? scope.topicId ? { projectId: scope.projectId ?? scope.topicId } : {}
|
|
188
|
-
};
|
|
189
|
-
}
|
|
190
|
-
async function normalizeProjectGrantRows(ctx, rows) {
|
|
191
|
-
return await Promise.all(rows.map((row) => normalizeProjectGrantRow(ctx, row)));
|
|
192
|
-
}
|
|
193
|
-
async function listProjectGrantsByPrincipal(ctx, principalId) {
|
|
194
|
-
const rows = await Promise.all(
|
|
195
|
-
PROJECT_GRANT_STATUSES.map(
|
|
196
|
-
(status) => ctx.db.query("projectGrants").withIndex(
|
|
197
|
-
"by_principal_status",
|
|
198
|
-
(q) => q.eq("principalId", principalId).eq("status", status)
|
|
199
|
-
).collect()
|
|
200
|
-
)
|
|
201
|
-
);
|
|
202
|
-
return await normalizeProjectGrantRows(ctx, rows.flat());
|
|
203
|
-
}
|
|
204
|
-
async function listProjectGrantsByGroup(ctx, groupId) {
|
|
205
|
-
const rows = await Promise.all(
|
|
206
|
-
PROJECT_GRANT_STATUSES.map(
|
|
207
|
-
(status) => ctx.db.query("projectGrants").withIndex(
|
|
208
|
-
"by_group_status",
|
|
209
|
-
(q) => q.eq("groupId", groupId).eq("status", status)
|
|
210
|
-
).collect()
|
|
211
|
-
)
|
|
212
|
-
);
|
|
213
|
-
return await normalizeProjectGrantRows(ctx, rows.flat());
|
|
214
|
-
}
|
|
215
|
-
function buildScopeMatchers(inputScopeId, resolved) {
|
|
216
|
-
return new Set(
|
|
217
|
-
[inputScopeId, resolved.topicId, resolved.projectId].map((value) => normalizeString(value)).filter((value) => Boolean(value))
|
|
218
|
-
);
|
|
219
|
-
}
|
|
220
|
-
function matchesResolvedScope(row, scopeIds) {
|
|
221
|
-
const rowTopicId = normalizeString(row.topicId);
|
|
222
|
-
const rowProjectId = normalizeString(row.projectId);
|
|
223
|
-
return rowTopicId !== void 0 && scopeIds.has(rowTopicId) || rowProjectId !== void 0 && scopeIds.has(rowProjectId);
|
|
224
|
-
}
|
|
225
|
-
async function bridgeListProjectGrantsByTopicAndPrincipal(ctx, topicId, principalId) {
|
|
226
|
-
const resolved = await resolveGrantScopeIds(ctx, { topicId });
|
|
227
|
-
const scopeIds = buildScopeMatchers(topicId, resolved);
|
|
228
|
-
const rows = await listProjectGrantsByPrincipal(ctx, principalId);
|
|
229
|
-
return rows.filter((row) => matchesResolvedScope(row, scopeIds));
|
|
230
|
-
}
|
|
231
|
-
async function bridgeListProjectGrantsByTopicAndGroup(ctx, topicId, groupId) {
|
|
232
|
-
const resolved = await resolveGrantScopeIds(ctx, { topicId });
|
|
233
|
-
const scopeIds = buildScopeMatchers(topicId, resolved);
|
|
234
|
-
const rows = await listProjectGrantsByGroup(ctx, groupId);
|
|
235
|
-
return rows.filter((row) => matchesResolvedScope(row, scopeIds));
|
|
236
|
-
}
|
|
237
|
-
async function bridgeListProjectGrantsByPrincipalStatus(ctx, principalId, status) {
|
|
238
|
-
const rows = await listProjectGrantsByPrincipal(ctx, principalId);
|
|
239
|
-
return rows.filter((row) => row.status === status);
|
|
240
|
-
}
|
|
241
|
-
async function bridgeListProjectGrantsByGroupStatus(ctx, groupId, status) {
|
|
242
|
-
const rows = await listProjectGrantsByGroup(ctx, groupId);
|
|
243
|
-
return rows.filter((row) => row.status === status);
|
|
244
|
-
}
|
|
245
|
-
async function bridgeInsertProjectGrant(ctx, value) {
|
|
246
|
-
const resolved = await resolveGrantScopeIds(ctx, value);
|
|
247
|
-
return await ctx.db.insert("projectGrants", {
|
|
248
|
-
...value,
|
|
249
|
-
...resolved.topicId ? { topicId: resolved.topicId } : {},
|
|
250
|
-
...resolved.projectId ?? resolved.topicId ? { projectId: resolved.projectId ?? resolved.topicId } : {}
|
|
251
|
-
});
|
|
252
|
-
}
|
|
253
|
-
|
|
254
|
-
// ../access-control/src/resolvers.ts
|
|
255
|
-
async function findUserByClerkId(ctx, clerkId) {
|
|
256
|
-
const normalizedClerkId = clerkId.trim();
|
|
257
|
-
if (!normalizedClerkId) {
|
|
258
|
-
return null;
|
|
259
|
-
}
|
|
260
|
-
if (typeof ctx.runQuery === "function") {
|
|
261
|
-
try {
|
|
262
|
-
const bridgedUser = await ctx.runQuery(api.users.getUserByClerkId, {
|
|
263
|
-
clerkId: normalizedClerkId
|
|
264
|
-
});
|
|
265
|
-
if (bridgedUser) {
|
|
266
|
-
return bridgedUser;
|
|
267
|
-
}
|
|
268
|
-
} catch {
|
|
269
|
-
}
|
|
270
|
-
}
|
|
271
|
-
try {
|
|
272
|
-
const users = await ctx.db.query("users").collect();
|
|
273
|
-
return users.find((user) => String(user.clerkId ?? "") === normalizedClerkId) ?? null;
|
|
274
|
-
} catch {
|
|
275
|
-
return null;
|
|
276
|
-
}
|
|
277
|
-
}
|
|
278
|
-
async function findUserByPrincipalId(ctx, principalId) {
|
|
279
|
-
const normalizedPrincipalId = principalId.trim();
|
|
280
|
-
if (!normalizedPrincipalId) {
|
|
281
|
-
return null;
|
|
282
|
-
}
|
|
283
|
-
try {
|
|
284
|
-
const users = await ctx.db.query("users").collect();
|
|
285
|
-
return users.find(
|
|
286
|
-
(user) => String(user.defaultPrincipalId ?? "") === normalizedPrincipalId
|
|
287
|
-
) ?? null;
|
|
288
|
-
} catch {
|
|
289
|
-
return null;
|
|
290
|
-
}
|
|
291
|
-
}
|
|
292
|
-
async function findAgentByPrincipalId(ctx, principalId) {
|
|
293
|
-
const normalizedPrincipalId = principalId.trim();
|
|
294
|
-
if (!normalizedPrincipalId) {
|
|
295
|
-
return null;
|
|
296
|
-
}
|
|
297
|
-
if (typeof ctx.runQuery === "function") {
|
|
298
|
-
try {
|
|
299
|
-
const bridgedAgent = await ctx.runQuery(
|
|
300
|
-
api.agents.getAgentByPrincipalId,
|
|
301
|
-
{
|
|
302
|
-
principalId: normalizedPrincipalId
|
|
303
|
-
}
|
|
304
|
-
);
|
|
305
|
-
if (bridgedAgent) {
|
|
306
|
-
return bridgedAgent;
|
|
307
|
-
}
|
|
308
|
-
} catch {
|
|
309
|
-
}
|
|
310
|
-
}
|
|
311
|
-
try {
|
|
312
|
-
const agents = await ctx.db.query("agents").collect();
|
|
313
|
-
return agents.find(
|
|
314
|
-
(agent) => String(agent.principalId ?? "") === normalizedPrincipalId
|
|
315
|
-
) ?? null;
|
|
316
|
-
} catch {
|
|
317
|
-
return null;
|
|
318
|
-
}
|
|
319
|
-
}
|
|
320
|
-
function defaultResolvers() {
|
|
321
|
-
return {
|
|
322
|
-
async getProject(ctx, topicId) {
|
|
323
|
-
return await resolveTopicProjectOverlay(ctx, topicId, {
|
|
324
|
-
idMode: "legacy",
|
|
325
|
-
projectLikeOnly: false
|
|
326
|
-
});
|
|
327
|
-
},
|
|
328
|
-
async listTopics(ctx) {
|
|
329
|
-
return await listTopicProjectOverlays(ctx, { idMode: "legacy" });
|
|
330
|
-
},
|
|
331
|
-
async listTopicsByOwner(ctx, ownerId) {
|
|
332
|
-
const topics = await listTopicProjectOverlays(ctx, { idMode: "legacy" });
|
|
333
|
-
return topics.filter((topic) => topic.ownerId === ownerId);
|
|
334
|
-
},
|
|
335
|
-
async listTopicsByVisibility(ctx, visibility) {
|
|
336
|
-
const topics = await listTopicProjectOverlays(ctx, { idMode: "legacy" });
|
|
337
|
-
return topics.filter((topic) => topic.visibility === visibility);
|
|
338
|
-
},
|
|
339
|
-
async listProjectGrantsByProjectAndPrincipal(ctx, topicId, principalId) {
|
|
340
|
-
return await bridgeListProjectGrantsByTopicAndPrincipal(
|
|
341
|
-
ctx,
|
|
342
|
-
topicId,
|
|
343
|
-
principalId
|
|
344
|
-
);
|
|
345
|
-
},
|
|
346
|
-
async listProjectGrantsByProjectAndGroup(ctx, topicId, groupId) {
|
|
347
|
-
return await bridgeListProjectGrantsByTopicAndGroup(ctx, topicId, groupId);
|
|
348
|
-
},
|
|
349
|
-
async listProjectGrantsByPrincipalStatus(ctx, principalId, status) {
|
|
350
|
-
return await bridgeListProjectGrantsByPrincipalStatus(
|
|
351
|
-
ctx,
|
|
352
|
-
principalId,
|
|
353
|
-
status
|
|
354
|
-
);
|
|
355
|
-
},
|
|
356
|
-
async listProjectGrantsByGroupStatus(ctx, groupId, status) {
|
|
357
|
-
return await bridgeListProjectGrantsByGroupStatus(ctx, groupId, status);
|
|
358
|
-
},
|
|
359
|
-
async insertProjectGrant(ctx, value) {
|
|
360
|
-
return await bridgeInsertProjectGrant(ctx, value);
|
|
361
|
-
},
|
|
362
|
-
async getAgentByPrincipalId(ctx, principalId) {
|
|
363
|
-
return await findAgentByPrincipalId(ctx, principalId);
|
|
364
|
-
},
|
|
365
|
-
async getUserByClerkId(ctx, clerkId) {
|
|
366
|
-
return await findUserByClerkId(ctx, clerkId);
|
|
367
|
-
},
|
|
368
|
-
async getUserByPrincipalId(ctx, principalId) {
|
|
369
|
-
return await findUserByPrincipalId(ctx, principalId);
|
|
370
|
-
}
|
|
371
|
-
};
|
|
372
|
-
}
|
|
373
|
-
var resolverOverrides = {};
|
|
374
|
-
function resolveAccessControlAppResolvers(_ctx) {
|
|
375
|
-
return {
|
|
376
|
-
...defaultResolvers(),
|
|
377
|
-
...resolverOverrides
|
|
378
|
-
};
|
|
379
|
-
}
|
|
380
|
-
|
|
381
|
-
// ../access-control/src/principalContext.ts
|
|
382
|
-
function requireCanonicalResolvedUser(user, clerkId) {
|
|
383
|
-
const resolved = user;
|
|
384
|
-
if (!resolved) {
|
|
385
|
-
throw new Error(
|
|
386
|
-
`[AccessControl] Canonical user identity required for ${clerkId}. Sync users.upsertUser before user-bound access checks.`
|
|
387
|
-
);
|
|
388
|
-
}
|
|
389
|
-
const { mcRole, defaultTenantId, defaultWorkspaceId, defaultPrincipalId } = resolved;
|
|
390
|
-
if (mcRole !== "platform_admin" && mcRole !== "tenant_admin" && mcRole !== "workspace_admin" && mcRole !== "editor" && mcRole !== "viewer" && mcRole !== "auditor" && mcRole !== "service_agent") {
|
|
391
|
-
throw new Error(
|
|
392
|
-
`[AccessControl] Canonical MC role required for ${clerkId}. Re-sync Master Control identity before user-bound access checks.`
|
|
393
|
-
);
|
|
394
|
-
}
|
|
395
|
-
if (typeof defaultTenantId !== "string" || defaultTenantId.trim().length === 0) {
|
|
396
|
-
throw new Error(
|
|
397
|
-
`[AccessControl] Canonical home tenant required for ${clerkId}. Re-sync Master Control identity before user-bound access checks.`
|
|
398
|
-
);
|
|
399
|
-
}
|
|
400
|
-
if (typeof defaultWorkspaceId !== "string" || defaultWorkspaceId.trim().length === 0) {
|
|
401
|
-
throw new Error(
|
|
402
|
-
`[AccessControl] Canonical home workspace required for ${clerkId}. Re-sync Master Control identity before user-bound access checks.`
|
|
403
|
-
);
|
|
404
|
-
}
|
|
405
|
-
if (typeof defaultPrincipalId !== "string" || defaultPrincipalId.trim().length === 0) {
|
|
406
|
-
throw new Error(
|
|
407
|
-
`[AccessControl] Canonical federated principal required for ${clerkId}. Re-sync Master Control identity before user-bound access checks.`
|
|
408
|
-
);
|
|
409
|
-
}
|
|
410
|
-
return {
|
|
411
|
-
mcRole,
|
|
412
|
-
defaultTenantId: defaultTenantId.trim(),
|
|
413
|
-
defaultWorkspaceId: defaultWorkspaceId.trim(),
|
|
414
|
-
defaultPrincipalId: defaultPrincipalId.trim()
|
|
415
|
-
};
|
|
416
|
-
}
|
|
417
|
-
function isPrincipalIdInput(value) {
|
|
418
|
-
return value.startsWith("user:") || value.startsWith("group:") || value.startsWith("service:") || value.startsWith("agent:") || value.startsWith("external_viewer:");
|
|
419
|
-
}
|
|
420
|
-
async function resolveCanonicalUserRecord(ctx, actorId) {
|
|
421
|
-
const normalizedActorId = actorId.trim();
|
|
422
|
-
const clerkId = isPrincipalIdInput(normalizedActorId) && normalizedActorId.startsWith("user:") ? normalizedActorId.slice("user:".length) : normalizedActorId;
|
|
423
|
-
const resolvers = resolveAccessControlAppResolvers();
|
|
424
|
-
const resolvedByClerkId = await resolvers.getUserByClerkId(ctx, clerkId);
|
|
425
|
-
if (resolvedByClerkId) {
|
|
426
|
-
return {
|
|
427
|
-
resolvedUser: resolvedByClerkId,
|
|
428
|
-
clerkId,
|
|
429
|
-
contextClerkId: clerkId
|
|
430
|
-
};
|
|
431
|
-
}
|
|
432
|
-
const resolvedByPrincipalId = await resolvers.getUserByPrincipalId(
|
|
433
|
-
ctx,
|
|
434
|
-
normalizedActorId
|
|
435
|
-
);
|
|
436
|
-
return {
|
|
437
|
-
resolvedUser: resolvedByPrincipalId ?? null,
|
|
438
|
-
clerkId,
|
|
439
|
-
contextClerkId: normalizedActorId.startsWith("user:") && clerkId.length > 0 ? clerkId : normalizedActorId
|
|
440
|
-
};
|
|
441
|
-
}
|
|
442
|
-
function uniqRoles(roles) {
|
|
443
|
-
const roleSet = /* @__PURE__ */ new Set();
|
|
444
|
-
for (const role of roles) {
|
|
445
|
-
if (role === "platform_admin" || role === "tenant_admin" || role === "workspace_admin" || role === "editor" || role === "viewer" || role === "auditor" || role === "service_agent") {
|
|
446
|
-
roleSet.add(role);
|
|
447
|
-
}
|
|
448
|
-
}
|
|
449
|
-
return [...roleSet];
|
|
450
|
-
}
|
|
451
|
-
function normalizeGroupIds(value) {
|
|
452
|
-
if (!Array.isArray(value)) {
|
|
453
|
-
return [];
|
|
454
|
-
}
|
|
455
|
-
return [...new Set(
|
|
456
|
-
value.filter((entry) => typeof entry === "string").map((entry) => entry.trim()).filter(Boolean)
|
|
457
|
-
)];
|
|
458
|
-
}
|
|
459
|
-
function requireServiceAgentUser(user, actorId) {
|
|
460
|
-
const canonicalUser = requireCanonicalResolvedUser(user, actorId);
|
|
461
|
-
if (canonicalUser.mcRole !== "service_agent") {
|
|
462
|
-
throw new Error(
|
|
463
|
-
`[AccessControl] Canonical service_agent identity required for ${actorId}. Sync users.upsertUser before agent-bound access checks.`
|
|
464
|
-
);
|
|
465
|
-
}
|
|
466
|
-
return canonicalUser;
|
|
467
|
-
}
|
|
468
|
-
function requireCanonicalResolvedAgent(agent, actorId) {
|
|
469
|
-
const resolved = agent;
|
|
470
|
-
if (!resolved) {
|
|
471
|
-
throw new Error(
|
|
472
|
-
`[AccessControl] Agent "${actorId}" not found in agents or users table.`
|
|
473
|
-
);
|
|
474
|
-
}
|
|
475
|
-
if (typeof resolved.principalId !== "string" || resolved.principalId.trim().length === 0) {
|
|
476
|
-
throw new Error(
|
|
477
|
-
`[AccessControl] Canonical agent principalId required for ${actorId}.`
|
|
478
|
-
);
|
|
479
|
-
}
|
|
480
|
-
if (typeof resolved.tenantId !== "string" || resolved.tenantId.trim().length === 0) {
|
|
481
|
-
throw new Error(
|
|
482
|
-
`[AccessControl] Canonical home tenant required for ${actorId}.`
|
|
483
|
-
);
|
|
484
|
-
}
|
|
485
|
-
if (typeof resolved.workspaceId !== "string" || resolved.workspaceId.trim().length === 0) {
|
|
486
|
-
throw new Error(
|
|
487
|
-
`[AccessControl] Canonical home workspace required for ${actorId}.`
|
|
488
|
-
);
|
|
489
|
-
}
|
|
490
|
-
return {
|
|
491
|
-
principalId: resolved.principalId.trim(),
|
|
492
|
-
tenantId: resolved.tenantId.trim(),
|
|
493
|
-
workspaceId: resolved.workspaceId.trim(),
|
|
494
|
-
roles: uniqRoles(Array.isArray(resolved.roles) ? resolved.roles : []) ?? ["service_agent"],
|
|
495
|
-
groupIds: normalizeGroupIds(resolved.groupIds)
|
|
496
|
-
};
|
|
497
|
-
}
|
|
498
|
-
async function resolvePrincipalContext(ctx, actorId) {
|
|
499
|
-
if (actorId.startsWith("agent:")) {
|
|
500
|
-
const resolvers = resolveAccessControlAppResolvers();
|
|
501
|
-
const resolvedAgent = await resolvers.getAgentByPrincipalId(ctx, actorId);
|
|
502
|
-
if (resolvedAgent) {
|
|
503
|
-
const agent = requireCanonicalResolvedAgent(
|
|
504
|
-
resolvedAgent,
|
|
505
|
-
actorId
|
|
506
|
-
);
|
|
507
|
-
return {
|
|
508
|
-
principalId: agent.principalId,
|
|
509
|
-
principalType: "service",
|
|
510
|
-
clerkId: actorId,
|
|
511
|
-
tenantId: agent.tenantId,
|
|
512
|
-
workspaceId: agent.workspaceId,
|
|
513
|
-
roles: agent.roles.length > 0 ? agent.roles : ["service_agent"],
|
|
514
|
-
groupIds: agent.groupIds,
|
|
515
|
-
isPlatformAdmin: false,
|
|
516
|
-
isTenantAdmin: false,
|
|
517
|
-
isWorkspaceAdmin: false,
|
|
518
|
-
isSystemFallback: false
|
|
519
|
-
};
|
|
520
|
-
}
|
|
521
|
-
const resolvedUser2 = await resolvers.getUserByClerkId(
|
|
522
|
-
ctx,
|
|
523
|
-
actorId
|
|
524
|
-
);
|
|
525
|
-
if (!resolvedUser2) {
|
|
526
|
-
throw new Error(
|
|
527
|
-
`[AccessControl] Agent "${actorId}" not found in agents or users table.`
|
|
528
|
-
);
|
|
529
|
-
}
|
|
530
|
-
const user2 = requireServiceAgentUser(
|
|
531
|
-
resolvedUser2,
|
|
532
|
-
actorId
|
|
533
|
-
);
|
|
534
|
-
console.warn(
|
|
535
|
-
`[AccessControl] Deprecated legacy service-agent fallback for ${actorId}; migrate this principal into identity.agents.`
|
|
536
|
-
);
|
|
537
|
-
return {
|
|
538
|
-
principalId: user2.defaultPrincipalId,
|
|
539
|
-
principalType: "service",
|
|
540
|
-
clerkId: actorId,
|
|
541
|
-
tenantId: user2.defaultTenantId,
|
|
542
|
-
workspaceId: user2.defaultWorkspaceId,
|
|
543
|
-
roles: ["service_agent"],
|
|
544
|
-
groupIds: normalizeGroupIds(resolvedUser2?.principalGroupIds),
|
|
545
|
-
isPlatformAdmin: false,
|
|
546
|
-
isTenantAdmin: false,
|
|
547
|
-
isWorkspaceAdmin: false,
|
|
548
|
-
isSystemFallback: false
|
|
549
|
-
};
|
|
550
|
-
}
|
|
551
|
-
const {
|
|
552
|
-
resolvedUser,
|
|
553
|
-
contextClerkId
|
|
554
|
-
} = await resolveCanonicalUserRecord(ctx, actorId);
|
|
555
|
-
const user = requireCanonicalResolvedUser(
|
|
556
|
-
resolvedUser,
|
|
557
|
-
contextClerkId
|
|
558
|
-
);
|
|
559
|
-
if (!user.defaultPrincipalId) {
|
|
560
|
-
throw new Error(
|
|
561
|
-
`[AccessControl] Canonical federated principal required for ${contextClerkId}. Re-sync Master Control identity before user-bound access checks.`
|
|
562
|
-
);
|
|
563
|
-
}
|
|
564
|
-
if (user.mcRole === "service_agent") {
|
|
565
|
-
return {
|
|
566
|
-
principalId: user.defaultPrincipalId,
|
|
567
|
-
principalType: "service",
|
|
568
|
-
clerkId: contextClerkId,
|
|
569
|
-
tenantId: user.defaultTenantId,
|
|
570
|
-
workspaceId: user.defaultWorkspaceId,
|
|
571
|
-
roles: ["service_agent"],
|
|
572
|
-
groupIds: normalizeGroupIds(resolvedUser?.principalGroupIds),
|
|
573
|
-
isPlatformAdmin: false,
|
|
574
|
-
isTenantAdmin: false,
|
|
575
|
-
isWorkspaceAdmin: false,
|
|
576
|
-
isSystemFallback: false
|
|
577
|
-
};
|
|
578
|
-
}
|
|
579
|
-
const principalId = user.defaultPrincipalId;
|
|
580
|
-
const effectiveRole = user.mcRole;
|
|
581
|
-
const roles = effectiveRole === "platform_admin" ? ["platform_admin", "tenant_admin"] : effectiveRole === "tenant_admin" ? ["tenant_admin"] : [effectiveRole];
|
|
582
|
-
const tenantId = user.defaultTenantId;
|
|
583
|
-
const workspaceId = user.defaultWorkspaceId;
|
|
584
|
-
const isPlatformAdmin = effectiveRole === "platform_admin";
|
|
585
|
-
return {
|
|
586
|
-
principalId,
|
|
587
|
-
principalType: "user",
|
|
588
|
-
clerkId: contextClerkId,
|
|
589
|
-
tenantId,
|
|
590
|
-
workspaceId,
|
|
591
|
-
roles: uniqRoles(roles),
|
|
592
|
-
groupIds: normalizeGroupIds(resolvedUser?.principalGroupIds),
|
|
593
|
-
isPlatformAdmin,
|
|
594
|
-
isTenantAdmin: isPlatformAdmin || effectiveRole === "tenant_admin",
|
|
595
|
-
isWorkspaceAdmin: isPlatformAdmin || effectiveRole === "tenant_admin" || effectiveRole === "workspace_admin",
|
|
596
|
-
isSystemFallback: false
|
|
597
|
-
};
|
|
598
|
-
}
|
|
599
|
-
|
|
600
|
-
// ../access-control/src/access.ts
|
|
601
|
-
function isTopicInPrincipalTenant(topic, principalTenantId) {
|
|
602
|
-
if (!topic.tenantId) {
|
|
603
|
-
return false;
|
|
604
|
-
}
|
|
605
|
-
if (!principalTenantId) {
|
|
606
|
-
return false;
|
|
607
|
-
}
|
|
608
|
-
return String(topic.tenantId) === String(principalTenantId);
|
|
609
|
-
}
|
|
610
|
-
function isTopicInPrincipalWorkspace(topic, principalWorkspaceId) {
|
|
611
|
-
if (!topic.workspaceId) {
|
|
612
|
-
return false;
|
|
613
|
-
}
|
|
614
|
-
if (!principalWorkspaceId) {
|
|
615
|
-
return false;
|
|
616
|
-
}
|
|
617
|
-
return String(topic.workspaceId) === String(principalWorkspaceId);
|
|
618
|
-
}
|
|
619
|
-
function isLegacyUnscopedTopic(topic) {
|
|
620
|
-
return !topic.tenantId || !topic.workspaceId;
|
|
621
|
-
}
|
|
622
|
-
function isGrantScopeAlignedToTopic(topic, grant) {
|
|
623
|
-
if (topic.tenantId && grant.tenantId && String(topic.tenantId) !== String(grant.tenantId)) {
|
|
624
|
-
return false;
|
|
625
|
-
}
|
|
626
|
-
if (topic.workspaceId && grant.workspaceId && String(topic.workspaceId) !== String(grant.workspaceId)) {
|
|
627
|
-
return false;
|
|
628
|
-
}
|
|
629
|
-
return true;
|
|
630
|
-
}
|
|
631
|
-
function isGrantSourceAllowedForVisibility(visibility, source) {
|
|
632
|
-
if (source !== "external_share") {
|
|
633
|
-
return true;
|
|
634
|
-
}
|
|
635
|
-
return visibility === "external" || visibility === "public";
|
|
636
|
-
}
|
|
637
|
-
function isGrantActive(grant) {
|
|
638
|
-
if (grant.status !== "active") {
|
|
639
|
-
return false;
|
|
640
|
-
}
|
|
641
|
-
if (grant.expiresAt !== void 0 && grant.expiresAt <= Date.now()) {
|
|
642
|
-
return false;
|
|
643
|
-
}
|
|
644
|
-
return true;
|
|
645
|
-
}
|
|
646
|
-
async function hasPrincipalGrant(ctx, args) {
|
|
647
|
-
const grants = await resolveAccessControlAppResolvers().listProjectGrantsByProjectAndPrincipal(
|
|
648
|
-
ctx,
|
|
649
|
-
args.topic._id,
|
|
650
|
-
args.principalId
|
|
651
|
-
);
|
|
652
|
-
if (grants.some(
|
|
653
|
-
(grant) => isGrantActive(grant) && isGrantScopeAlignedToTopic(args.topic, grant) && isGrantSourceAllowedForVisibility(
|
|
654
|
-
args.topic.visibility,
|
|
655
|
-
grant.source
|
|
656
|
-
) && (!args.principalIsExternal || args.topic.visibility === "public" || grant.source === "external_share")
|
|
657
|
-
)) {
|
|
658
|
-
return true;
|
|
659
|
-
}
|
|
660
|
-
return false;
|
|
661
|
-
}
|
|
662
|
-
async function hasGroupGrant(ctx, args) {
|
|
663
|
-
if (args.groupIds.length === 0) {
|
|
664
|
-
return false;
|
|
665
|
-
}
|
|
666
|
-
for (const groupId of args.groupIds) {
|
|
667
|
-
const grants = await resolveAccessControlAppResolvers().listProjectGrantsByProjectAndGroup(ctx, args.topic._id, groupId);
|
|
668
|
-
if (grants.some(
|
|
669
|
-
(grant) => isGrantActive(grant) && isGrantScopeAlignedToTopic(args.topic, grant) && isGrantSourceAllowedForVisibility(
|
|
670
|
-
args.topic.visibility,
|
|
671
|
-
grant.source
|
|
672
|
-
)
|
|
673
|
-
)) {
|
|
674
|
-
return true;
|
|
675
|
-
}
|
|
676
|
-
}
|
|
677
|
-
return false;
|
|
678
|
-
}
|
|
679
|
-
function isExternalPrincipal(_ctx, _args) {
|
|
680
|
-
return false;
|
|
681
|
-
}
|
|
682
|
-
async function evaluateTopicAccessDetailed(ctx, args) {
|
|
683
|
-
if (args.legacyUserId) {
|
|
684
|
-
return {
|
|
685
|
-
hasAccess: true,
|
|
686
|
-
isAdmin: false,
|
|
687
|
-
isOwner: false,
|
|
688
|
-
isShared: false,
|
|
689
|
-
hasGrant: true,
|
|
690
|
-
isFirmVisible: true,
|
|
691
|
-
isExternalVisible: false,
|
|
692
|
-
isPublicVisible: false,
|
|
693
|
-
isTenantScopeMatch: true,
|
|
694
|
-
isWorkspaceScopeMatch: true,
|
|
695
|
-
isPrincipalExternal: false
|
|
696
|
-
};
|
|
697
|
-
}
|
|
698
|
-
const topic = await resolveAccessControlAppResolvers().getProject(
|
|
699
|
-
ctx,
|
|
700
|
-
args.topicId
|
|
701
|
-
);
|
|
702
|
-
if (!topic) {
|
|
703
|
-
return {
|
|
704
|
-
hasAccess: false,
|
|
705
|
-
isAdmin: false,
|
|
706
|
-
isOwner: false,
|
|
707
|
-
isShared: false,
|
|
708
|
-
hasGrant: false,
|
|
709
|
-
isFirmVisible: false,
|
|
710
|
-
isExternalVisible: false,
|
|
711
|
-
isPublicVisible: false,
|
|
712
|
-
isTenantScopeMatch: false,
|
|
713
|
-
isWorkspaceScopeMatch: false,
|
|
714
|
-
isPrincipalExternal: false
|
|
715
|
-
};
|
|
716
|
-
}
|
|
717
|
-
const { principalContext, legacyUserId } = args;
|
|
718
|
-
const userIsAdmin = principalContext.isPlatformAdmin;
|
|
719
|
-
const isOwner = topic.ownerId === legacyUserId;
|
|
720
|
-
const isShared = (topic.sharedWith ?? []).includes(legacyUserId);
|
|
721
|
-
const principalIsExternal = await isExternalPrincipal(ctx, {
|
|
722
|
-
groupIds: principalContext.groupIds,
|
|
723
|
-
topicTenantId: topic.tenantId,
|
|
724
|
-
topicWorkspaceId: topic.workspaceId
|
|
725
|
-
});
|
|
726
|
-
const hasPrincipalGrantResult = await hasPrincipalGrant(ctx, {
|
|
727
|
-
topic,
|
|
728
|
-
principalId: principalContext.principalId,
|
|
729
|
-
principalIsExternal
|
|
730
|
-
});
|
|
731
|
-
const hasGroupGrantResult = await hasGroupGrant(ctx, {
|
|
732
|
-
topic,
|
|
733
|
-
groupIds: principalContext.groupIds
|
|
734
|
-
});
|
|
735
|
-
const hasGrant = isShared || hasPrincipalGrantResult || hasGroupGrantResult;
|
|
736
|
-
const legacyUnscoped = isLegacyUnscopedTopic(topic);
|
|
737
|
-
const tenantScopeMatch = isTopicInPrincipalTenant(
|
|
738
|
-
topic,
|
|
739
|
-
principalContext.tenantId
|
|
740
|
-
);
|
|
741
|
-
const workspaceScopeMatch = isTopicInPrincipalWorkspace(
|
|
742
|
-
topic,
|
|
743
|
-
principalContext.workspaceId
|
|
744
|
-
);
|
|
745
|
-
const isPublicVisible = topic.visibility === "public";
|
|
746
|
-
const isFirmVisible = topic.visibility === "firm" && !legacyUnscoped && tenantScopeMatch && workspaceScopeMatch && !principalIsExternal;
|
|
747
|
-
const hasScopedGrant = hasGrant && (legacyUnscoped || tenantScopeMatch && workspaceScopeMatch);
|
|
748
|
-
const isExternalVisible = topic.visibility === "external" && hasScopedGrant;
|
|
749
|
-
const hasAccess = userIsAdmin || isOwner || hasScopedGrant || isPublicVisible || isFirmVisible;
|
|
750
|
-
return {
|
|
751
|
-
hasAccess,
|
|
752
|
-
isAdmin: userIsAdmin,
|
|
753
|
-
isOwner,
|
|
754
|
-
isShared,
|
|
755
|
-
hasGrant,
|
|
756
|
-
isFirmVisible,
|
|
757
|
-
isExternalVisible,
|
|
758
|
-
isPublicVisible,
|
|
759
|
-
isTenantScopeMatch: tenantScopeMatch,
|
|
760
|
-
isWorkspaceScopeMatch: workspaceScopeMatch,
|
|
761
|
-
isPrincipalExternal: principalIsExternal
|
|
762
|
-
};
|
|
763
|
-
}
|
|
764
|
-
async function checkTopicAccessDetailed(ctx, topicId, userId) {
|
|
765
|
-
const principalContext = await resolvePrincipalContext(ctx, userId);
|
|
766
|
-
return evaluateTopicAccessDetailed(ctx, {
|
|
767
|
-
topicId,
|
|
768
|
-
legacyUserId: userId,
|
|
769
|
-
principalContext
|
|
770
|
-
});
|
|
771
|
-
}
|
|
772
|
-
async function checkTopicAccess(ctx, topicId, userId) {
|
|
773
|
-
const result = await checkTopicAccessDetailed(ctx, topicId, userId);
|
|
774
|
-
return result.hasAccess;
|
|
775
|
-
}
|
|
776
|
-
async function checkScopeAccess(ctx, scopeId, userId) {
|
|
777
|
-
try {
|
|
778
|
-
const topic = await ctx.db.get(scopeId);
|
|
779
|
-
if (topic && topic.name !== void 0 && topic.type !== void 0) {
|
|
780
|
-
return true;
|
|
781
|
-
}
|
|
782
|
-
} catch {
|
|
783
|
-
}
|
|
784
|
-
try {
|
|
785
|
-
return await checkTopicAccess(ctx, scopeId, userId);
|
|
786
|
-
} catch {
|
|
787
|
-
return false;
|
|
788
|
-
}
|
|
789
|
-
}
|
|
790
|
-
var checkProjectAccess = checkTopicAccess;
|
|
791
|
-
|
|
792
|
-
// ../access-control/src/auth.ts
|
|
793
|
-
async function getCurrentUserId(ctx) {
|
|
794
|
-
const identity = await ctx.auth.getUserIdentity();
|
|
795
|
-
return identity?.subject ?? null;
|
|
796
|
-
}
|
|
797
|
-
var permissiveReturn = v.optional(v.any());
|
|
798
|
-
var looseJsonObject = v.record(v.string(), v.any());
|
|
799
|
-
var looseJsonArray = v.array(v.any());
|
|
800
|
-
v.union(
|
|
801
|
-
v.string(),
|
|
802
|
-
v.number(),
|
|
803
|
-
v.boolean(),
|
|
804
|
-
v.null(),
|
|
805
|
-
looseJsonObject,
|
|
806
|
-
looseJsonArray
|
|
807
|
-
);
|
|
808
|
-
var api2 = anyApi;
|
|
809
|
-
componentsGeneric();
|
|
810
|
-
var internal = anyApi;
|
|
811
|
-
var internalMutation = internalMutationGeneric;
|
|
812
|
-
var internalQuery = internalQueryGeneric;
|
|
813
|
-
var mutation = mutationGeneric;
|
|
814
|
-
var query = queryGeneric;
|
|
815
|
-
|
|
816
|
-
// ../confidence/src/v1/operations/subjectiveLogic/index.ts
|
|
817
|
-
function opinion(belief, disbelief, uncertainty, baseRate = 0.5) {
|
|
818
|
-
const b = Math.max(0, Math.min(1, belief));
|
|
819
|
-
const d = Math.max(0, Math.min(1, disbelief));
|
|
820
|
-
const u = Math.max(0, Math.min(1, uncertainty));
|
|
821
|
-
const a = Math.max(0, Math.min(1, baseRate));
|
|
822
|
-
const sum = b + d + u;
|
|
823
|
-
if (sum === 0) {
|
|
824
|
-
return { b: 0, d: 0, u: 1, a };
|
|
825
|
-
}
|
|
826
|
-
return {
|
|
827
|
-
b: b / sum,
|
|
828
|
-
d: d / sum,
|
|
829
|
-
u: u / sum,
|
|
830
|
-
a
|
|
831
|
-
};
|
|
832
|
-
}
|
|
833
|
-
function vacuous(baseRate = 0.5) {
|
|
834
|
-
return { b: 0, d: 0, u: 1, a: baseRate };
|
|
835
|
-
}
|
|
836
|
-
function project(o) {
|
|
837
|
-
return o.b + o.a * o.u;
|
|
838
|
-
}
|
|
839
|
-
function cumulativeFusion(left, right) {
|
|
840
|
-
if (left.u === 0 && right.u === 0) {
|
|
841
|
-
return opinion(
|
|
842
|
-
(left.b + right.b) / 2,
|
|
843
|
-
(left.d + right.d) / 2,
|
|
844
|
-
0,
|
|
845
|
-
(left.a + right.a) / 2
|
|
846
|
-
);
|
|
847
|
-
}
|
|
848
|
-
const k = left.u + right.u - left.u * right.u;
|
|
849
|
-
if (k === 0) {
|
|
850
|
-
return vacuous((left.a + right.a) / 2);
|
|
851
|
-
}
|
|
852
|
-
return opinion(
|
|
853
|
-
(left.b * right.u + right.b * left.u) / k,
|
|
854
|
-
(left.d * right.u + right.d * left.u) / k,
|
|
855
|
-
left.u * right.u / k,
|
|
856
|
-
(left.a + right.a) / 2
|
|
857
|
-
);
|
|
858
|
-
}
|
|
859
|
-
function trustDiscount(sourceOpinion, trust) {
|
|
860
|
-
const weight = Math.max(0, Math.min(1, Math.abs(trust)));
|
|
861
|
-
return opinion(
|
|
862
|
-
weight * sourceOpinion.b,
|
|
863
|
-
weight * sourceOpinion.d,
|
|
864
|
-
1 - weight * (sourceOpinion.b + sourceOpinion.d),
|
|
865
|
-
sourceOpinion.a
|
|
866
|
-
);
|
|
867
|
-
}
|
|
868
|
-
var EPSILON = 1e-9;
|
|
869
|
-
function childBaseRateFallback(ifTrue, ifFalse, fallbackBaseRate) {
|
|
870
|
-
if (fallbackBaseRate !== void 0) {
|
|
871
|
-
return Math.max(0, Math.min(1, fallbackBaseRate));
|
|
872
|
-
}
|
|
873
|
-
if (Math.abs(ifTrue.a - ifFalse.a) <= EPSILON) {
|
|
874
|
-
return ifTrue.a;
|
|
875
|
-
}
|
|
876
|
-
return (ifTrue.a + ifFalse.a) / 2;
|
|
877
|
-
}
|
|
878
|
-
function computeConditionalDeductionBaseRate(opinionA, ifTrue, ifFalse, fallbackBaseRate) {
|
|
879
|
-
const denominator = 1 - opinionA.a * ifTrue.u - (1 - opinionA.a) * ifFalse.u;
|
|
880
|
-
if (ifTrue.u + ifFalse.u < 2 - EPSILON && Math.abs(denominator) > EPSILON) {
|
|
881
|
-
const baseRate = (opinionA.a * ifTrue.b + (1 - opinionA.a) * ifFalse.b) / denominator;
|
|
882
|
-
if (baseRate >= -EPSILON && baseRate <= 1 + EPSILON) {
|
|
883
|
-
return Math.max(0, Math.min(1, baseRate));
|
|
884
|
-
}
|
|
885
|
-
}
|
|
886
|
-
return fallbackBaseRate;
|
|
887
|
-
}
|
|
888
|
-
function safeCorrectionTerm(numerator, denominator) {
|
|
889
|
-
if (Math.abs(denominator) <= EPSILON) {
|
|
890
|
-
return void 0;
|
|
891
|
-
}
|
|
892
|
-
const value = numerator / denominator;
|
|
893
|
-
if (!Number.isFinite(value)) {
|
|
894
|
-
return void 0;
|
|
895
|
-
}
|
|
896
|
-
return Math.max(0, value);
|
|
897
|
-
}
|
|
898
|
-
function conditionalDeduction(opinionA, ifTrue, ifFalse, fallbackBaseRate) {
|
|
899
|
-
const fallbackChildBaseRate = childBaseRateFallback(
|
|
900
|
-
ifTrue,
|
|
901
|
-
ifFalse,
|
|
902
|
-
fallbackBaseRate
|
|
903
|
-
);
|
|
904
|
-
const childBaseRate = computeConditionalDeductionBaseRate(
|
|
905
|
-
opinionA,
|
|
906
|
-
ifTrue,
|
|
907
|
-
ifFalse,
|
|
908
|
-
fallbackChildBaseRate
|
|
909
|
-
);
|
|
910
|
-
const projectedAntecedent = project(opinionA);
|
|
911
|
-
const projectedAntecedentComplement = 1 - projectedAntecedent;
|
|
912
|
-
const intermediateBelief = opinionA.b * ifTrue.b + opinionA.d * ifFalse.b + opinionA.u * (ifTrue.b * opinionA.a + ifFalse.b * (1 - opinionA.a));
|
|
913
|
-
const intermediateDisbelief = opinionA.b * ifTrue.d + opinionA.d * ifFalse.d + opinionA.u * (ifTrue.d * opinionA.a + ifFalse.d * (1 - opinionA.a));
|
|
914
|
-
const intermediateUncertainty = opinionA.b * ifTrue.u + opinionA.d * ifFalse.u + opinionA.u * (ifTrue.u * opinionA.a + ifFalse.u * (1 - opinionA.a));
|
|
915
|
-
const projectedVacuousDeduction = ifTrue.b * opinionA.a + ifFalse.b * (1 - opinionA.a) + childBaseRate * (ifTrue.u * opinionA.a + ifFalse.u * (1 - opinionA.a));
|
|
916
|
-
const projectedConditionalA = ifTrue.b + childBaseRate * (1 - ifTrue.b - ifTrue.d);
|
|
917
|
-
let correction = 0;
|
|
918
|
-
if (ifTrue.b > ifFalse.b && ifTrue.d > ifFalse.d || ifTrue.b <= ifFalse.b && ifTrue.d <= ifFalse.d) {
|
|
919
|
-
correction = 0;
|
|
920
|
-
} else if (ifTrue.b > ifFalse.b && ifTrue.d <= ifFalse.d) {
|
|
921
|
-
const beliefGap = ifTrue.b - ifFalse.b;
|
|
922
|
-
const disbeliefGap = ifFalse.d - ifTrue.d;
|
|
923
|
-
if (projectedVacuousDeduction <= projectedConditionalA && projectedAntecedent <= opinionA.a) {
|
|
924
|
-
correction = safeCorrectionTerm(
|
|
925
|
-
opinionA.a * opinionA.u * (intermediateBelief - ifTrue.b),
|
|
926
|
-
projectedAntecedent * childBaseRate
|
|
927
|
-
) ?? 0;
|
|
928
|
-
} else if (projectedVacuousDeduction <= projectedConditionalA && projectedAntecedent > opinionA.a) {
|
|
929
|
-
correction = safeCorrectionTerm(
|
|
930
|
-
opinionA.a * opinionA.u * (intermediateDisbelief - ifTrue.d) * beliefGap,
|
|
931
|
-
projectedAntecedentComplement * childBaseRate * disbeliefGap
|
|
932
|
-
) ?? 0;
|
|
933
|
-
} else if (projectedVacuousDeduction > projectedConditionalA && projectedAntecedent <= opinionA.a) {
|
|
934
|
-
correction = safeCorrectionTerm(
|
|
935
|
-
(1 - opinionA.a) * opinionA.u * (intermediateBelief - ifTrue.b) * disbeliefGap,
|
|
936
|
-
projectedAntecedent * (1 - childBaseRate) * beliefGap
|
|
937
|
-
) ?? 0;
|
|
938
|
-
} else {
|
|
939
|
-
correction = safeCorrectionTerm(
|
|
940
|
-
(1 - opinionA.a) * opinionA.u * (intermediateDisbelief - ifTrue.d),
|
|
941
|
-
projectedAntecedentComplement * (1 - childBaseRate)
|
|
942
|
-
) ?? 0;
|
|
943
|
-
}
|
|
944
|
-
} else {
|
|
945
|
-
const beliefGap = ifFalse.b - ifTrue.b;
|
|
946
|
-
const disbeliefGap = ifTrue.d - ifFalse.d;
|
|
947
|
-
if (projectedVacuousDeduction <= projectedConditionalA && projectedAntecedent <= opinionA.a) {
|
|
948
|
-
correction = safeCorrectionTerm(
|
|
949
|
-
(1 - opinionA.a) * opinionA.u * (intermediateDisbelief - ifTrue.d) * beliefGap,
|
|
950
|
-
projectedAntecedent * childBaseRate * disbeliefGap
|
|
951
|
-
) ?? 0;
|
|
952
|
-
} else if (projectedVacuousDeduction <= projectedConditionalA && projectedAntecedent > opinionA.a) {
|
|
953
|
-
correction = safeCorrectionTerm(
|
|
954
|
-
(1 - opinionA.a) * opinionA.u * (intermediateBelief - ifTrue.b),
|
|
955
|
-
projectedAntecedentComplement * childBaseRate
|
|
956
|
-
) ?? 0;
|
|
957
|
-
} else if (projectedVacuousDeduction > projectedConditionalA && projectedAntecedent <= opinionA.a) {
|
|
958
|
-
correction = safeCorrectionTerm(
|
|
959
|
-
opinionA.a * opinionA.u * (intermediateDisbelief - ifTrue.d),
|
|
960
|
-
projectedAntecedent * (1 - childBaseRate)
|
|
961
|
-
) ?? 0;
|
|
962
|
-
} else {
|
|
963
|
-
correction = safeCorrectionTerm(
|
|
964
|
-
opinionA.a * opinionA.u * (intermediateBelief - ifTrue.b) * disbeliefGap,
|
|
965
|
-
projectedAntecedentComplement * (1 - childBaseRate) * beliefGap
|
|
966
|
-
) ?? 0;
|
|
967
|
-
}
|
|
968
|
-
}
|
|
969
|
-
return opinion(
|
|
970
|
-
intermediateBelief - childBaseRate * correction,
|
|
971
|
-
intermediateDisbelief - (1 - childBaseRate) * correction,
|
|
972
|
-
intermediateUncertainty + correction,
|
|
973
|
-
childBaseRate
|
|
974
|
-
);
|
|
975
|
-
}
|
|
976
|
-
function negate(o) {
|
|
977
|
-
return { b: o.d, d: o.b, u: o.u, a: 1 - o.a };
|
|
978
|
-
}
|
|
979
|
-
function constraintFusion(left, right, mode = "pressure") {
|
|
980
|
-
if (mode === "redistribute") {
|
|
981
|
-
const leftProjected = project(left);
|
|
982
|
-
const rightProjected = project(right);
|
|
983
|
-
const total = leftProjected + rightProjected;
|
|
984
|
-
if (total <= 1) {
|
|
985
|
-
return { o1: left, o2: right };
|
|
986
|
-
}
|
|
987
|
-
const scale = 1 / total;
|
|
988
|
-
return {
|
|
989
|
-
o1: opinion(
|
|
990
|
-
left.b * scale,
|
|
991
|
-
left.d + left.b * (1 - scale),
|
|
992
|
-
left.u,
|
|
993
|
-
left.a
|
|
994
|
-
),
|
|
995
|
-
o2: opinion(
|
|
996
|
-
right.b * scale,
|
|
997
|
-
right.d + right.b * (1 - scale),
|
|
998
|
-
right.u,
|
|
999
|
-
right.a
|
|
1000
|
-
)
|
|
1001
|
-
};
|
|
1002
|
-
}
|
|
1003
|
-
const pressureLeft = right.b * 0.5;
|
|
1004
|
-
const pressureRight = left.b * 0.5;
|
|
1005
|
-
return {
|
|
1006
|
-
o1: opinion(
|
|
1007
|
-
left.b - pressureLeft * 0.3,
|
|
1008
|
-
left.d + pressureLeft * 0.3,
|
|
1009
|
-
left.u,
|
|
1010
|
-
left.a
|
|
1011
|
-
),
|
|
1012
|
-
o2: opinion(
|
|
1013
|
-
right.b - pressureRight * 0.3,
|
|
1014
|
-
right.d + pressureRight * 0.3,
|
|
1015
|
-
right.u,
|
|
1016
|
-
right.a
|
|
1017
|
-
)
|
|
1018
|
-
};
|
|
1019
|
-
}
|
|
1020
|
-
|
|
1021
|
-
// ../confidence/src/v1/operations/scoring.ts
|
|
1022
|
-
function finiteNumber(value) {
|
|
1023
|
-
return typeof value === "number" && Number.isFinite(value) ? value : void 0;
|
|
1024
|
-
}
|
|
1025
|
-
function clamp01(value) {
|
|
1026
|
-
return Math.max(0, Math.min(1, value));
|
|
1027
|
-
}
|
|
1028
|
-
function confidenceFromOpinion(opinion2) {
|
|
1029
|
-
return clamp01(opinion2.b + opinion2.a * opinion2.u);
|
|
1030
|
-
}
|
|
1031
|
-
function confidenceFromSL(belief, _disbelief, uncertainty, baseRate = 0.5) {
|
|
1032
|
-
return confidenceFromOpinion({
|
|
1033
|
-
b: belief,
|
|
1034
|
-
u: uncertainty,
|
|
1035
|
-
a: baseRate
|
|
1036
|
-
});
|
|
1037
|
-
}
|
|
1038
|
-
function readOpinionFromRecord(source, fallback = {}) {
|
|
1039
|
-
const record = source && typeof source === "object" ? source : {};
|
|
1040
|
-
return {
|
|
1041
|
-
b: finiteNumber(record.b) ?? finiteNumber(record.belief) ?? finiteNumber(record.slBelief) ?? finiteNumber(record.opinion_b) ?? fallback.b ?? 0,
|
|
1042
|
-
d: finiteNumber(record.d) ?? finiteNumber(record.disbelief) ?? finiteNumber(record.slDisbelief) ?? finiteNumber(record.opinion_d) ?? fallback.d ?? 0,
|
|
1043
|
-
u: finiteNumber(record.u) ?? finiteNumber(record.uncertainty) ?? finiteNumber(record.slUncertainty) ?? finiteNumber(record.opinion_u) ?? fallback.u ?? 1,
|
|
1044
|
-
a: finiteNumber(record.a) ?? finiteNumber(record.baseRate) ?? finiteNumber(record.slBaseRate) ?? finiteNumber(record.opinion_a) ?? fallback.a ?? 0.5
|
|
1045
|
-
};
|
|
1046
|
-
}
|
|
1047
|
-
function hasProjectedOpinionChanged(current, next, tolerance = 0.01) {
|
|
1048
|
-
return Math.abs(confidenceFromOpinion(next) - confidenceFromOpinion(current)) >= tolerance;
|
|
1049
|
-
}
|
|
1050
|
-
|
|
1051
|
-
// ../confidence/src/v1/operations/contradiction/detectTupleContradiction.ts
|
|
1052
|
-
var DEFAULT_TUPLE_CONTRADICTION_BELIEF_THRESHOLD = 0.7;
|
|
1053
|
-
var DEFAULT_TUPLE_CONTRADICTION_DISBELIEF_THRESHOLD = 0.7;
|
|
1054
|
-
function normalizeTupleContradictionPolicy(policy = {}) {
|
|
1055
|
-
return {
|
|
1056
|
-
beliefThreshold: clamp01(
|
|
1057
|
-
policy.beliefThreshold ?? DEFAULT_TUPLE_CONTRADICTION_BELIEF_THRESHOLD
|
|
1058
|
-
),
|
|
1059
|
-
disbeliefThreshold: clamp01(
|
|
1060
|
-
policy.disbeliefThreshold ?? DEFAULT_TUPLE_CONTRADICTION_DISBELIEF_THRESHOLD
|
|
1061
|
-
)
|
|
1062
|
-
};
|
|
1063
|
-
}
|
|
1064
|
-
function detectTupleContradiction(opinion2, tauB = DEFAULT_TUPLE_CONTRADICTION_BELIEF_THRESHOLD, tauD = DEFAULT_TUPLE_CONTRADICTION_DISBELIEF_THRESHOLD) {
|
|
1065
|
-
return opinion2.b > tauB && opinion2.d > tauD;
|
|
1066
|
-
}
|
|
1067
|
-
function evaluateTupleContradictionTransition(args) {
|
|
1068
|
-
const policy = normalizeTupleContradictionPolicy(args.policy);
|
|
1069
|
-
const tupleContradicted = detectTupleContradiction(
|
|
1070
|
-
args.opinion,
|
|
1071
|
-
policy.beliefThreshold,
|
|
1072
|
-
policy.disbeliefThreshold
|
|
1073
|
-
);
|
|
1074
|
-
const previousTupleContradicted = Boolean(args.previousTupleContradicted);
|
|
1075
|
-
return {
|
|
1076
|
-
tupleContradicted,
|
|
1077
|
-
crossedIntoTupleContradiction: !previousTupleContradicted && tupleContradicted,
|
|
1078
|
-
crossedOutOfTupleContradiction: previousTupleContradicted && !tupleContradicted,
|
|
1079
|
-
policy
|
|
1080
|
-
};
|
|
1081
|
-
}
|
|
1082
|
-
|
|
1083
|
-
// ../confidence/src/v1/operations/dynamics/cascade.ts
|
|
1084
|
-
function dampedDependencyOpinion(dependencyOpinion, beliefOpinion, mode = "continuous", threshold = 0.3) {
|
|
1085
|
-
const dependencyProjection = project(dependencyOpinion);
|
|
1086
|
-
if (mode === "threshold") {
|
|
1087
|
-
if (dependencyProjection < threshold) {
|
|
1088
|
-
return opinion(
|
|
1089
|
-
0,
|
|
1090
|
-
beliefOpinion.d + beliefOpinion.b * 0.5,
|
|
1091
|
-
0.5,
|
|
1092
|
-
beliefOpinion.a
|
|
1093
|
-
);
|
|
1094
|
-
}
|
|
1095
|
-
return beliefOpinion;
|
|
1096
|
-
}
|
|
1097
|
-
const dampingFactor = Math.pow(dependencyProjection, 0.5);
|
|
1098
|
-
return opinion(
|
|
1099
|
-
beliefOpinion.b * dampingFactor,
|
|
1100
|
-
beliefOpinion.d + beliefOpinion.b * (1 - dampingFactor) * 0.3,
|
|
1101
|
-
beliefOpinion.u + beliefOpinion.b * (1 - dampingFactor) * 0.7,
|
|
1102
|
-
beliefOpinion.a
|
|
1103
|
-
);
|
|
1104
|
-
}
|
|
1105
|
-
function dampedDependencyCascade(dependencyOpinion, beliefOpinion, mode = "continuous") {
|
|
1106
|
-
return {
|
|
1107
|
-
opinion: dampedDependencyOpinion(dependencyOpinion, beliefOpinion, mode),
|
|
1108
|
-
operator: "dependency_cascade",
|
|
1109
|
-
rationale: `Damped dependency cascade (${mode}): prerequisite at ${project(
|
|
1110
|
-
dependencyOpinion
|
|
1111
|
-
).toFixed(2)}`
|
|
1112
|
-
};
|
|
1113
|
-
}
|
|
1114
|
-
|
|
1115
|
-
// ../confidence/src/v1/operations/dynamics/defeat.ts
|
|
1116
|
-
function applyNegativeSupport(source, target, weight, metadata = {}) {
|
|
1117
|
-
if (metadata.constraint === "xor") {
|
|
1118
|
-
const result = constraintFusion(
|
|
1119
|
-
source,
|
|
1120
|
-
target,
|
|
1121
|
-
metadata.normalization ?? "pressure"
|
|
1122
|
-
);
|
|
1123
|
-
return {
|
|
1124
|
-
opinion: result.o2,
|
|
1125
|
-
operator: "constraint_fusion",
|
|
1126
|
-
rationale: `XOR constraint: source belief at ${project(source).toFixed(
|
|
1127
|
-
2
|
|
1128
|
-
)} pressures target`
|
|
1129
|
-
};
|
|
1130
|
-
}
|
|
1131
|
-
const discounted = trustDiscount(negate(source), Math.abs(weight));
|
|
1132
|
-
return {
|
|
1133
|
-
opinion: cumulativeFusion(target, discounted),
|
|
1134
|
-
operator: "cumulative_fusion",
|
|
1135
|
-
rationale: `Contradicting evidence (weight=${weight.toFixed(
|
|
1136
|
-
2
|
|
1137
|
-
)}) from source at ${project(source).toFixed(2)}`
|
|
1138
|
-
};
|
|
1139
|
-
}
|
|
1140
|
-
function applyNegativeEvidence(source, target, weight) {
|
|
1141
|
-
const discounted = trustDiscount(negate(source), Math.abs(weight));
|
|
1142
|
-
return {
|
|
1143
|
-
opinion: cumulativeFusion(target, discounted),
|
|
1144
|
-
operator: "cumulative_fusion",
|
|
1145
|
-
rationale: `Contradicting evidence (weight=${weight.toFixed(2)})`
|
|
1146
|
-
};
|
|
1147
|
-
}
|
|
1148
|
-
|
|
1149
|
-
// ../confidence/src/v1/operations/contracts/epistemicContract.ts
|
|
1150
|
-
var BUILT_IN_METRIC_CHECKER = "metric_checker";
|
|
1151
|
-
var BUILT_IN_REFERENCE_CHECK_COUNTER = "reference_check_counter";
|
|
1152
|
-
var BUILT_IN_MARKET_INDEX_COMPARATOR = "market_index_comparator";
|
|
1153
|
-
function clampConfidence(value) {
|
|
1154
|
-
return Math.max(0, Math.min(1, value));
|
|
1155
|
-
}
|
|
1156
|
-
function generateContractId() {
|
|
1157
|
-
if (typeof crypto !== "undefined" && typeof crypto.randomUUID === "function") {
|
|
1158
|
-
return crypto.randomUUID();
|
|
1159
|
-
}
|
|
1160
|
-
return `contract-${Date.now()}-${Math.random().toString(36).slice(2, 10)}`;
|
|
1161
|
-
}
|
|
1162
|
-
function deriveContractStatus(result, currentStatus) {
|
|
1163
|
-
if (currentStatus === "archived") {
|
|
1164
|
-
return currentStatus;
|
|
1165
|
-
}
|
|
1166
|
-
switch (result) {
|
|
1167
|
-
case "confirmed":
|
|
1168
|
-
return "satisfied";
|
|
1169
|
-
case "disconfirmed":
|
|
1170
|
-
return "violated";
|
|
1171
|
-
case "expired":
|
|
1172
|
-
return "expired";
|
|
1173
|
-
default:
|
|
1174
|
-
return currentStatus === "satisfied" || currentStatus === "violated" || currentStatus === "expired" ? "active" : currentStatus;
|
|
1175
|
-
}
|
|
1176
|
-
}
|
|
1177
|
-
function deriveVerificationTrigger(result) {
|
|
1178
|
-
switch (result) {
|
|
1179
|
-
case "confirmed":
|
|
1180
|
-
return "verification_confirmed";
|
|
1181
|
-
case "disconfirmed":
|
|
1182
|
-
return "verification_disconfirmed";
|
|
1183
|
-
case "expired":
|
|
1184
|
-
return "verification_expired";
|
|
1185
|
-
case "partial":
|
|
1186
|
-
return "verification_partial";
|
|
1187
|
-
default:
|
|
1188
|
-
return null;
|
|
1189
|
-
}
|
|
1190
|
-
}
|
|
1191
|
-
function deriveContractModulationPlan(args) {
|
|
1192
|
-
const trigger = deriveVerificationTrigger(args.result);
|
|
1193
|
-
if (!trigger) {
|
|
1194
|
-
return null;
|
|
1195
|
-
}
|
|
1196
|
-
if (args.result === "confirmed") {
|
|
1197
|
-
const rawNext = args.currentConfidence + args.modulation.onConfirmed.delta;
|
|
1198
|
-
const confidenceAfter = clampConfidence(
|
|
1199
|
-
Math.min(
|
|
1200
|
-
args.modulation.onConfirmed.ceiling ?? Number.POSITIVE_INFINITY,
|
|
1201
|
-
rawNext
|
|
1202
|
-
)
|
|
1203
|
-
);
|
|
1204
|
-
return {
|
|
1205
|
-
trigger,
|
|
1206
|
-
confidenceBefore: args.currentConfidence,
|
|
1207
|
-
confidenceAfter,
|
|
1208
|
-
confidenceDelta: confidenceAfter - args.currentConfidence
|
|
1209
|
-
};
|
|
1210
|
-
}
|
|
1211
|
-
if (args.result === "disconfirmed") {
|
|
1212
|
-
const rawNext = args.currentConfidence + args.modulation.onDisconfirmed.delta;
|
|
1213
|
-
const confidenceAfter = clampConfidence(
|
|
1214
|
-
Math.max(args.modulation.onDisconfirmed.floor ?? 0, rawNext)
|
|
1215
|
-
);
|
|
1216
|
-
return {
|
|
1217
|
-
trigger,
|
|
1218
|
-
confidenceBefore: args.currentConfidence,
|
|
1219
|
-
confidenceAfter,
|
|
1220
|
-
confidenceDelta: confidenceAfter - args.currentConfidence
|
|
1221
|
-
};
|
|
1222
|
-
}
|
|
1223
|
-
if (args.result === "expired" && args.modulation.onExpired) {
|
|
1224
|
-
const confidenceAfter = clampConfidence(
|
|
1225
|
-
args.currentConfidence + args.modulation.onExpired.delta
|
|
1226
|
-
);
|
|
1227
|
-
return {
|
|
1228
|
-
trigger,
|
|
1229
|
-
confidenceBefore: args.currentConfidence,
|
|
1230
|
-
confidenceAfter,
|
|
1231
|
-
confidenceDelta: confidenceAfter - args.currentConfidence
|
|
1232
|
-
};
|
|
1233
|
-
}
|
|
1234
|
-
if (args.result === "partial" && args.modulation.onPartial) {
|
|
1235
|
-
if ((args.resultConfidence ?? 0) < args.modulation.onPartial.threshold) {
|
|
1236
|
-
return null;
|
|
1237
|
-
}
|
|
1238
|
-
const confidenceAfter = clampConfidence(
|
|
1239
|
-
args.currentConfidence + args.modulation.onPartial.delta
|
|
1240
|
-
);
|
|
1241
|
-
return {
|
|
1242
|
-
trigger,
|
|
1243
|
-
confidenceBefore: args.currentConfidence,
|
|
1244
|
-
confidenceAfter,
|
|
1245
|
-
confidenceDelta: confidenceAfter - args.currentConfidence
|
|
1246
|
-
};
|
|
1247
|
-
}
|
|
1248
|
-
return null;
|
|
1249
|
-
}
|
|
1250
|
-
function createInheritedContractRecord(contract, args) {
|
|
1251
|
-
return {
|
|
1252
|
-
beliefNodeId: args.beliefNodeId,
|
|
1253
|
-
contractId: generateContractId(),
|
|
1254
|
-
title: contract.title,
|
|
1255
|
-
description: contract.description,
|
|
1256
|
-
conditionType: contract.conditionType,
|
|
1257
|
-
direction: contract.direction,
|
|
1258
|
-
condition: contract.condition,
|
|
1259
|
-
deadline: contract.deadline,
|
|
1260
|
-
compositeOf: contract.compositeOf,
|
|
1261
|
-
compositeOperator: contract.compositeOperator,
|
|
1262
|
-
modulation: contract.modulation,
|
|
1263
|
-
evaluationSchedule: contract.evaluationSchedule,
|
|
1264
|
-
periodicIntervalMs: contract.periodicIntervalMs,
|
|
1265
|
-
status: "active",
|
|
1266
|
-
lineageSource: "inherited",
|
|
1267
|
-
inheritedFromContractId: contract.contractId,
|
|
1268
|
-
inheritedFromBeliefNodeId: contract.beliefNodeId,
|
|
1269
|
-
inheritedAt: args.now,
|
|
1270
|
-
topicId: args.topicId,
|
|
1271
|
-
createdAt: args.now,
|
|
1272
|
-
createdBy: args.createdBy,
|
|
1273
|
-
updatedAt: args.now
|
|
1274
|
-
};
|
|
1275
|
-
}
|
|
1276
|
-
function normalizeEvidentialAction(value) {
|
|
1277
|
-
return value === "modulate_confidence" || value === "flag_review" || value === "archive" ? value : void 0;
|
|
1278
|
-
}
|
|
1279
|
-
function parseComparisonOperator(value, evaluatorName) {
|
|
1280
|
-
if (value === "gte" || value === "lte" || value === "eq" || value === "gt" || value === "lt") {
|
|
1281
|
-
return value;
|
|
1282
|
-
}
|
|
1283
|
-
throw new Error(
|
|
1284
|
-
`${evaluatorName} requires operator to be one of gte, lte, eq, gt, or lt.`
|
|
1285
|
-
);
|
|
1286
|
-
}
|
|
1287
|
-
function parseNumericThreshold(value, evaluatorName) {
|
|
1288
|
-
if (typeof value === "number" && Number.isFinite(value)) {
|
|
1289
|
-
return value;
|
|
1290
|
-
}
|
|
1291
|
-
throw new Error(`${evaluatorName} requires a finite numeric threshold.`);
|
|
1292
|
-
}
|
|
1293
|
-
function pickFiniteNumber(config, keys) {
|
|
1294
|
-
for (const key of keys) {
|
|
1295
|
-
const value = config[key];
|
|
1296
|
-
if (typeof value === "number" && Number.isFinite(value)) {
|
|
1297
|
-
return value;
|
|
1298
|
-
}
|
|
1299
|
-
}
|
|
1300
|
-
return void 0;
|
|
1301
|
-
}
|
|
1302
|
-
function getEvaluatorInputRecord(inputData, nestedKey) {
|
|
1303
|
-
if (!inputData || typeof inputData !== "object") {
|
|
1304
|
-
return {};
|
|
1305
|
-
}
|
|
1306
|
-
const root = inputData;
|
|
1307
|
-
const nested = root[nestedKey];
|
|
1308
|
-
if (nested && typeof nested === "object") {
|
|
1309
|
-
return nested;
|
|
1310
|
-
}
|
|
1311
|
-
return root;
|
|
1312
|
-
}
|
|
1313
|
-
function parseEvidentialEvaluatorConfig(value) {
|
|
1314
|
-
if (!value || typeof value !== "object") {
|
|
1315
|
-
throw new Error(
|
|
1316
|
-
"Evidential contracts require condition.evaluatorConfig with metric/operator/threshold."
|
|
1317
|
-
);
|
|
1318
|
-
}
|
|
1319
|
-
const config = value;
|
|
1320
|
-
const metric = config.metric;
|
|
1321
|
-
const operator = config.operator;
|
|
1322
|
-
const threshold = config.threshold;
|
|
1323
|
-
if (metric !== "evidence_count" && metric !== "contradiction_status" && metric !== "edge_freshness" && metric !== "dependent_count") {
|
|
1324
|
-
throw new Error(`Unsupported evidential metric: ${String(metric)}`);
|
|
1325
|
-
}
|
|
1326
|
-
if (operator !== "gte" && operator !== "lte" && operator !== "eq" && operator !== "gt" && operator !== "lt") {
|
|
1327
|
-
throw new Error(`Unsupported evidential operator: ${String(operator)}`);
|
|
1328
|
-
}
|
|
1329
|
-
if (typeof threshold !== "number" || !Number.isFinite(threshold)) {
|
|
1330
|
-
throw new Error("Evidential contracts require a numeric threshold.");
|
|
1331
|
-
}
|
|
1332
|
-
const actionParams = config.actionParams && typeof config.actionParams === "object" && config.actionParams !== null ? config.actionParams : void 0;
|
|
1333
|
-
return {
|
|
1334
|
-
metric,
|
|
1335
|
-
operator,
|
|
1336
|
-
threshold,
|
|
1337
|
-
action: normalizeEvidentialAction(config.action),
|
|
1338
|
-
actionParams: actionParams && (typeof actionParams.targetConfidence === "number" || typeof actionParams.rationale === "string") ? {
|
|
1339
|
-
targetConfidence: typeof actionParams.targetConfidence === "number" ? actionParams.targetConfidence : void 0,
|
|
1340
|
-
rationale: typeof actionParams.rationale === "string" ? actionParams.rationale : void 0
|
|
1341
|
-
} : void 0
|
|
1342
|
-
};
|
|
1343
|
-
}
|
|
1344
|
-
function compareMetricValue(operator, left, right) {
|
|
1345
|
-
switch (operator) {
|
|
1346
|
-
case "gte":
|
|
1347
|
-
return left >= right;
|
|
1348
|
-
case "lte":
|
|
1349
|
-
return left <= right;
|
|
1350
|
-
case "eq":
|
|
1351
|
-
return left === right;
|
|
1352
|
-
case "gt":
|
|
1353
|
-
return left > right;
|
|
1354
|
-
case "lt":
|
|
1355
|
-
return left < right;
|
|
1356
|
-
default:
|
|
1357
|
-
return false;
|
|
1358
|
-
}
|
|
1359
|
-
}
|
|
1360
|
-
function resolveComparisonResult(direction, comparisonSatisfied) {
|
|
1361
|
-
return direction === "falsifies" ? comparisonSatisfied ? "disconfirmed" : "confirmed" : comparisonSatisfied ? "confirmed" : "disconfirmed";
|
|
1362
|
-
}
|
|
1363
|
-
function buildComparisonRationale(args) {
|
|
1364
|
-
const renderedObserved = args.observedValue === null ? "no data" : `${args.observedValue}${args.unit ? ` ${args.unit}` : ""}`;
|
|
1365
|
-
const renderedThreshold = `${args.threshold}${args.unit ? ` ${args.unit}` : ""}`;
|
|
1366
|
-
const clause = `${args.label} observed ${renderedObserved} against ${args.operator} ${renderedThreshold}`;
|
|
1367
|
-
if (args.observedValue === null) {
|
|
1368
|
-
return `${clause}; evaluator returned ${args.result} because no current data was available.`;
|
|
1369
|
-
}
|
|
1370
|
-
return `${clause}; comparison ${args.comparisonSatisfied ? "passed" : "failed"}, resulting in ${args.result}.`;
|
|
1371
|
-
}
|
|
1372
|
-
function buildEvidentialRationale(args) {
|
|
1373
|
-
const observed = args.snapshot.value === null ? "no data" : String(args.snapshot.value);
|
|
1374
|
-
const clause = `${args.snapshot.metric} observed ${observed} against ${args.config.operator} ${args.config.threshold}`;
|
|
1375
|
-
if (args.snapshot.value === null) {
|
|
1376
|
-
return `${clause}; evidential evaluator treated the comparison as unmet, resulting in ${args.result}.`;
|
|
1377
|
-
}
|
|
1378
|
-
return `${clause}; comparison ${args.comparisonSatisfied ? "passed" : "failed"}, resulting in ${args.result}.`;
|
|
1379
|
-
}
|
|
1380
|
-
function parseMetricCheckerConfig(value) {
|
|
1381
|
-
if (!value || typeof value !== "object") {
|
|
1382
|
-
throw new Error(
|
|
1383
|
-
"metric_checker requires condition.evaluatorConfig with observedValue/operator/threshold."
|
|
1384
|
-
);
|
|
1385
|
-
}
|
|
1386
|
-
const config = value;
|
|
1387
|
-
return {
|
|
1388
|
-
metric: typeof config.metric === "string" && config.metric.length > 0 ? config.metric : void 0,
|
|
1389
|
-
operator: parseComparisonOperator(config.operator, BUILT_IN_METRIC_CHECKER),
|
|
1390
|
-
threshold: parseNumericThreshold(config.threshold, BUILT_IN_METRIC_CHECKER),
|
|
1391
|
-
observedValue: pickFiniteNumber(config, ["observedValue", "value"]),
|
|
1392
|
-
currentValue: pickFiniteNumber(config, ["currentValue"]),
|
|
1393
|
-
metricValue: pickFiniteNumber(config, ["metricValue"]),
|
|
1394
|
-
unit: typeof config.unit === "string" && config.unit.length > 0 ? config.unit : void 0
|
|
1395
|
-
};
|
|
1396
|
-
}
|
|
1397
|
-
function parseReferenceCheckCounterConfig(value) {
|
|
1398
|
-
if (!value || typeof value !== "object") {
|
|
1399
|
-
throw new Error(
|
|
1400
|
-
"reference_check_counter requires condition.evaluatorConfig with tag/operator/threshold."
|
|
1401
|
-
);
|
|
1402
|
-
}
|
|
1403
|
-
const config = value;
|
|
1404
|
-
if (typeof config.tag !== "string" || config.tag.trim().length === 0) {
|
|
1405
|
-
throw new Error("reference_check_counter requires a non-empty tag.");
|
|
1406
|
-
}
|
|
1407
|
-
return {
|
|
1408
|
-
tag: config.tag.trim(),
|
|
1409
|
-
operator: parseComparisonOperator(
|
|
1410
|
-
config.operator,
|
|
1411
|
-
BUILT_IN_REFERENCE_CHECK_COUNTER
|
|
1412
|
-
),
|
|
1413
|
-
threshold: parseNumericThreshold(
|
|
1414
|
-
config.threshold,
|
|
1415
|
-
BUILT_IN_REFERENCE_CHECK_COUNTER
|
|
1416
|
-
),
|
|
1417
|
-
caseSensitive: config.caseSensitive === true
|
|
1418
|
-
};
|
|
1419
|
-
}
|
|
1420
|
-
function parseTemporalDeadlineConfig(value) {
|
|
1421
|
-
if (!value || typeof value !== "object") {
|
|
1422
|
-
return {};
|
|
1423
|
-
}
|
|
1424
|
-
const config = value;
|
|
1425
|
-
return {
|
|
1426
|
-
label: typeof config.label === "string" && config.label.length > 0 ? config.label : void 0,
|
|
1427
|
-
completed: config.completed === true,
|
|
1428
|
-
completedAt: pickFiniteNumber(config, ["completedAt"]),
|
|
1429
|
-
observedAt: pickFiniteNumber(config, ["observedAt"]),
|
|
1430
|
-
satisfiedAt: pickFiniteNumber(config, ["satisfiedAt"]),
|
|
1431
|
-
achievedAt: pickFiniteNumber(config, ["achievedAt"])
|
|
1432
|
-
};
|
|
1433
|
-
}
|
|
1434
|
-
function parseMarketIndexComparatorConfig(value) {
|
|
1435
|
-
if (!value || typeof value !== "object") {
|
|
1436
|
-
throw new Error(
|
|
1437
|
-
"market_index_comparator requires condition.evaluatorConfig with subjectValue/benchmarkValue/operator/threshold."
|
|
1438
|
-
);
|
|
1439
|
-
}
|
|
1440
|
-
const config = value;
|
|
1441
|
-
return {
|
|
1442
|
-
subject: typeof config.subject === "string" && config.subject.length > 0 ? config.subject : void 0,
|
|
1443
|
-
subjectValue: pickFiniteNumber(config, ["subjectValue", "leftValue"]),
|
|
1444
|
-
primaryValue: pickFiniteNumber(config, ["primaryValue"]),
|
|
1445
|
-
benchmark: typeof config.benchmark === "string" && config.benchmark.length > 0 ? config.benchmark : void 0,
|
|
1446
|
-
benchmarkValue: pickFiniteNumber(config, ["benchmarkValue", "rightValue"]),
|
|
1447
|
-
comparisonValue: pickFiniteNumber(config, ["comparisonValue"]),
|
|
1448
|
-
operator: parseComparisonOperator(
|
|
1449
|
-
config.operator,
|
|
1450
|
-
BUILT_IN_MARKET_INDEX_COMPARATOR
|
|
1451
|
-
),
|
|
1452
|
-
threshold: parseNumericThreshold(
|
|
1453
|
-
config.threshold,
|
|
1454
|
-
BUILT_IN_MARKET_INDEX_COMPARATOR
|
|
1455
|
-
)
|
|
1456
|
-
};
|
|
1457
|
-
}
|
|
1458
|
-
|
|
1459
|
-
// ../access-control/src/audience.ts
|
|
1460
|
-
var AUDIENCE_CLASS_RANK = {
|
|
1461
|
-
public: 0,
|
|
1462
|
-
restricted_external: 1,
|
|
1463
|
-
internal: 2
|
|
1464
|
-
};
|
|
1465
|
-
function normalizeKey(key) {
|
|
1466
|
-
return (key ?? "").trim().toLowerCase().replace(/[^a-z0-9:_-]+/g, "_").replace(/_+/g, "_").replace(/^_+|_+$/g, "");
|
|
1467
|
-
}
|
|
1468
|
-
function normalizeAudienceKey(key) {
|
|
1469
|
-
return normalizeKey(key);
|
|
1470
|
-
}
|
|
1471
|
-
function classFromAudienceKey(audienceKey, fallback = "internal") {
|
|
1472
|
-
const key = normalizeKey(audienceKey);
|
|
1473
|
-
if (!key) {
|
|
1474
|
-
return fallback;
|
|
1475
|
-
}
|
|
1476
|
-
if (key === "internal") {
|
|
1477
|
-
return "internal";
|
|
1478
|
-
}
|
|
1479
|
-
if (key === "public") {
|
|
1480
|
-
return "public";
|
|
1481
|
-
}
|
|
1482
|
-
if (key === "lp" || key === "external" || key === "client" || key === "partner" || key === "portfolio" || key === "network" || key === "restricted_external") {
|
|
1483
|
-
return "restricted_external";
|
|
1484
|
-
}
|
|
1485
|
-
return fallback;
|
|
1486
|
-
}
|
|
1487
|
-
function canAudienceClassAccess(viewerClass, resourceClass) {
|
|
1488
|
-
return AUDIENCE_CLASS_RANK[viewerClass] >= AUDIENCE_CLASS_RANK[resourceClass];
|
|
1489
|
-
}
|
|
1490
|
-
|
|
1491
|
-
// ../access-control/src/audienceRegistry.ts
|
|
1492
|
-
var DEFAULT_AUDIENCES = [
|
|
1493
|
-
{
|
|
1494
|
-
audienceKey: "internal",
|
|
1495
|
-
audienceLabel: "Internal",
|
|
1496
|
-
audienceClass: "internal"
|
|
1497
|
-
},
|
|
1498
|
-
{
|
|
1499
|
-
audienceKey: "lp",
|
|
1500
|
-
audienceLabel: "Limited Partners",
|
|
1501
|
-
audienceClass: "restricted_external"
|
|
1502
|
-
},
|
|
1503
|
-
{
|
|
1504
|
-
audienceKey: "public",
|
|
1505
|
-
audienceLabel: "Public",
|
|
1506
|
-
audienceClass: "public"
|
|
1507
|
-
}
|
|
1508
|
-
];
|
|
1509
|
-
var AUDIENCE_CLASS_PRIORITY = {
|
|
1510
|
-
internal: 0,
|
|
1511
|
-
restricted_external: 1,
|
|
1512
|
-
public: 2
|
|
1513
|
-
};
|
|
1514
|
-
function normalizeRegistryRow(row) {
|
|
1515
|
-
return {
|
|
1516
|
-
audienceKey: normalizeAudienceKey(row.audienceKey),
|
|
1517
|
-
audienceLabel: row.audienceLabel,
|
|
1518
|
-
audienceClass: row.audienceClass,
|
|
1519
|
-
workspaceId: row.workspaceId
|
|
1520
|
-
};
|
|
1521
|
-
}
|
|
1522
|
-
function dedupeRegistryRows(rows) {
|
|
1523
|
-
const byKey = /* @__PURE__ */ new Map();
|
|
1524
|
-
for (const row of rows) {
|
|
1525
|
-
const key = normalizeAudienceKey(row.audienceKey);
|
|
1526
|
-
if (!key) {
|
|
1527
|
-
continue;
|
|
1528
|
-
}
|
|
1529
|
-
const existing = byKey.get(key);
|
|
1530
|
-
const isWorkspaceScoped = row.workspaceId !== void 0;
|
|
1531
|
-
const existingWorkspaceScoped = existing?.workspaceId !== void 0;
|
|
1532
|
-
if (!existing || isWorkspaceScoped && !existingWorkspaceScoped) {
|
|
1533
|
-
byKey.set(key, {
|
|
1534
|
-
...row,
|
|
1535
|
-
audienceKey: key
|
|
1536
|
-
});
|
|
1537
|
-
}
|
|
1538
|
-
}
|
|
1539
|
-
const normalized = [...byKey.values()];
|
|
1540
|
-
normalized.sort((a, b) => {
|
|
1541
|
-
const classDelta = AUDIENCE_CLASS_PRIORITY[a.audienceClass] - AUDIENCE_CLASS_PRIORITY[b.audienceClass];
|
|
1542
|
-
if (classDelta !== 0) {
|
|
1543
|
-
return classDelta;
|
|
1544
|
-
}
|
|
1545
|
-
return a.audienceKey.localeCompare(b.audienceKey);
|
|
1546
|
-
});
|
|
1547
|
-
return normalized;
|
|
1548
|
-
}
|
|
1549
|
-
async function queryRegistryRows(ctx, args) {
|
|
1550
|
-
if (!args.tenantId) {
|
|
1551
|
-
return [...DEFAULT_AUDIENCES];
|
|
1552
|
-
}
|
|
1553
|
-
const rows = await ctx.db.query("platformAudiences").withIndex("by_tenantId", (q) => q.eq("tenantId", args.tenantId)).collect();
|
|
1554
|
-
const workspaceIdString = args.workspaceId ? String(args.workspaceId) : null;
|
|
1555
|
-
const tenantScoped = rows.filter((row) => row.status === "active");
|
|
1556
|
-
const applicable = tenantScoped.filter((row) => {
|
|
1557
|
-
if (!row.workspaceId) {
|
|
1558
|
-
return true;
|
|
1559
|
-
}
|
|
1560
|
-
if (!workspaceIdString) {
|
|
1561
|
-
return false;
|
|
1562
|
-
}
|
|
1563
|
-
return String(row.workspaceId) === workspaceIdString;
|
|
1564
|
-
});
|
|
1565
|
-
return dedupeRegistryRows([
|
|
1566
|
-
...DEFAULT_AUDIENCES,
|
|
1567
|
-
...applicable.map(
|
|
1568
|
-
(row) => normalizeRegistryRow({
|
|
1569
|
-
audienceKey: row.audienceKey,
|
|
1570
|
-
audienceLabel: row.audienceLabel,
|
|
1571
|
-
audienceClass: row.audienceClass,
|
|
1572
|
-
workspaceId: row.workspaceId
|
|
1573
|
-
})
|
|
1574
|
-
)
|
|
1575
|
-
]);
|
|
1576
|
-
}
|
|
1577
|
-
async function listAudienceRegistryRows(ctx, args) {
|
|
1578
|
-
return queryRegistryRows(ctx, args);
|
|
1579
|
-
}
|
|
1580
|
-
|
|
1581
|
-
// ../../packages/contracts/src/schema-helpers/enumValidation.ts
|
|
1582
|
-
var BUILTIN_ENUM_FALLBACK = {
|
|
1583
|
-
topic_type: /* @__PURE__ */ new Set([
|
|
1584
|
-
"domain",
|
|
1585
|
-
"theme",
|
|
1586
|
-
"deal",
|
|
1587
|
-
"strategy",
|
|
1588
|
-
"constitution",
|
|
1589
|
-
"project",
|
|
1590
|
-
"portfolio",
|
|
1591
|
-
"architecture",
|
|
1592
|
-
"capability",
|
|
1593
|
-
"runtime",
|
|
1594
|
-
"interface",
|
|
1595
|
-
"governance",
|
|
1596
|
-
"operations",
|
|
1597
|
-
"security",
|
|
1598
|
-
"data"
|
|
1599
|
-
]),
|
|
1600
|
-
branch_schema: /* @__PURE__ */ new Set(["pillar", "track", "dimension", "axis", "phase"]),
|
|
1601
|
-
lens_perspective_type: /* @__PURE__ */ new Set([
|
|
1602
|
-
"investigation",
|
|
1603
|
-
"monitoring",
|
|
1604
|
-
"analysis",
|
|
1605
|
-
"comparison",
|
|
1606
|
-
"taxonomy"
|
|
1607
|
-
]),
|
|
1608
|
-
belief_type: /* @__PURE__ */ new Set([
|
|
1609
|
-
"belief",
|
|
1610
|
-
"hypothesis",
|
|
1611
|
-
"principle",
|
|
1612
|
-
"invariant",
|
|
1613
|
-
"assumption",
|
|
1614
|
-
"tenet",
|
|
1615
|
-
"prior",
|
|
1616
|
-
"preference",
|
|
1617
|
-
"goal",
|
|
1618
|
-
"forecast",
|
|
1619
|
-
"decision",
|
|
1620
|
-
"constraint",
|
|
1621
|
-
"tradeoff",
|
|
1622
|
-
"policy",
|
|
1623
|
-
"implementation_choice",
|
|
1624
|
-
"implementation_decision",
|
|
1625
|
-
"interface_contract",
|
|
1626
|
-
"migration_state",
|
|
1627
|
-
"code_pattern",
|
|
1628
|
-
"deprecation_notice"
|
|
1629
|
-
]),
|
|
1630
|
-
edge_type: /* @__PURE__ */ new Set([
|
|
1631
|
-
"supports",
|
|
1632
|
-
"informs",
|
|
1633
|
-
"depends_on",
|
|
1634
|
-
"derived_from",
|
|
1635
|
-
"contains",
|
|
1636
|
-
"tests",
|
|
1637
|
-
"supersedes",
|
|
1638
|
-
"responds_to",
|
|
1639
|
-
"belongs_to",
|
|
1640
|
-
"relates_to_thesis",
|
|
1641
|
-
"works_at",
|
|
1642
|
-
"invested_in",
|
|
1643
|
-
"competes_with",
|
|
1644
|
-
"participates_in",
|
|
1645
|
-
"founded_by",
|
|
1646
|
-
"evaluates",
|
|
1647
|
-
"performs",
|
|
1648
|
-
"function_in",
|
|
1649
|
-
"impacts",
|
|
1650
|
-
"raised_from",
|
|
1651
|
-
"mentioned_in",
|
|
1652
|
-
"perspective_on",
|
|
1653
|
-
"plays_theme"
|
|
1654
|
-
]),
|
|
1655
|
-
worktree_type: /* @__PURE__ */ new Set([
|
|
1656
|
-
"belief_test",
|
|
1657
|
-
"existential",
|
|
1658
|
-
"contradiction",
|
|
1659
|
-
"refinement",
|
|
1660
|
-
"coverage",
|
|
1661
|
-
"discovery",
|
|
1662
|
-
"clarification",
|
|
1663
|
-
"confirmation"
|
|
1664
|
-
]),
|
|
1665
|
-
worktree_phase: /* @__PURE__ */ new Set([
|
|
1666
|
-
"cluster_mapping",
|
|
1667
|
-
"hypothesis_formation",
|
|
1668
|
-
"question_generation",
|
|
1669
|
-
"evidence_collection",
|
|
1670
|
-
"synthesis",
|
|
1671
|
-
"decision",
|
|
1672
|
-
"retrospective"
|
|
1673
|
-
]),
|
|
1674
|
-
activity_type: /* @__PURE__ */ new Set([
|
|
1675
|
-
"create",
|
|
1676
|
-
"update",
|
|
1677
|
-
"review",
|
|
1678
|
-
"merge",
|
|
1679
|
-
"archive",
|
|
1680
|
-
"comment",
|
|
1681
|
-
"status_change",
|
|
1682
|
-
"evidence_added",
|
|
1683
|
-
"question_added"
|
|
1684
|
-
])
|
|
1685
|
-
};
|
|
1686
|
-
function normalizeEnumValue(value) {
|
|
1687
|
-
return value.trim().toLowerCase();
|
|
1688
|
-
}
|
|
1689
|
-
async function validateSchemaEnumValue(_ctx, args) {
|
|
1690
|
-
const normalized = normalizeEnumValue(args.value);
|
|
1691
|
-
if (!normalized) {
|
|
1692
|
-
return { valid: false, source: "none" };
|
|
1693
|
-
}
|
|
1694
|
-
if (BUILTIN_ENUM_FALLBACK[args.category].has(normalized)) {
|
|
1695
|
-
return { valid: true, source: "builtin" };
|
|
1696
|
-
}
|
|
1697
|
-
return { valid: false, source: "none" };
|
|
1698
|
-
}
|
|
1699
|
-
async function assertSchemaEnumValue(ctx, args) {
|
|
1700
|
-
if (typeof args.value !== "string") {
|
|
1701
|
-
return;
|
|
1702
|
-
}
|
|
1703
|
-
const normalized = normalizeEnumValue(args.value);
|
|
1704
|
-
if (!normalized) {
|
|
1705
|
-
return;
|
|
1706
|
-
}
|
|
1707
|
-
const validation = await validateSchemaEnumValue(ctx, {
|
|
1708
|
-
category: args.category,
|
|
1709
|
-
value: normalized,
|
|
1710
|
-
tenantId: args.tenantId
|
|
1711
|
-
});
|
|
1712
|
-
if (!validation.valid) {
|
|
1713
|
-
const tenantHint = args.tenantId ? ` for tenant ${args.tenantId}` : "";
|
|
1714
|
-
throw new Error(
|
|
1715
|
-
`[${args.context}] Invalid value "${normalized}" for category "${args.category}"${tenantHint}. Add it to the contracts schema enum manifest before use.`
|
|
1716
|
-
);
|
|
1717
|
-
}
|
|
1718
|
-
return normalized;
|
|
1719
|
-
}
|
|
15
|
+
var internal = anyApi;
|
|
16
|
+
var internalMutation = internalMutationGeneric;
|
|
17
|
+
var internalQuery = internalQueryGeneric;
|
|
18
|
+
var mutation = mutationGeneric;
|
|
19
|
+
var query = queryGeneric;
|
|
1720
20
|
|
|
1721
21
|
// src/beliefLifecycle.ts
|
|
1722
22
|
var BELIEF_STATUS_VALUES = [
|
|
@@ -1820,8 +120,6 @@ var containsPropagationSpec = {
|
|
|
1820
120
|
operator: () => null,
|
|
1821
121
|
description: "Structural containment only. Traversed for explicit semantics, but it never propagates opinions."
|
|
1822
122
|
};
|
|
1823
|
-
|
|
1824
|
-
// src/edges/utils.ts
|
|
1825
123
|
function readEdgeMetadata(edge) {
|
|
1826
124
|
return {
|
|
1827
125
|
constraint: edge.constraint ?? void 0,
|
|
@@ -1871,9 +169,7 @@ function propagateNegativeSupportWithMetadata(sourceOpinion, targetOpinion, edge
|
|
|
1871
169
|
readEdgeMetadata(edge)
|
|
1872
170
|
);
|
|
1873
171
|
}
|
|
1874
|
-
|
|
1875
|
-
return applyNegativeEvidence(sourceOpinion, targetOpinion, edgeWeight);
|
|
1876
|
-
}
|
|
172
|
+
var propagateNegativeInform = applyNegativeEvidence;
|
|
1877
173
|
|
|
1878
174
|
// src/edges/contradicts.ts
|
|
1879
175
|
var contradictsPropagationSpec = {
|
|
@@ -1898,8 +194,6 @@ var contradictsPropagationSpec = {
|
|
|
1898
194
|
},
|
|
1899
195
|
description: "Legacy contradiction edges move negative pressure in either direction, but never beyond one hop."
|
|
1900
196
|
};
|
|
1901
|
-
|
|
1902
|
-
// src/edges/dependsOn.ts
|
|
1903
197
|
var dependsOnPropagationSpec = {
|
|
1904
198
|
edgeType: "depends_on",
|
|
1905
199
|
direction: "incoming",
|
|
@@ -1915,8 +209,18 @@ var dependsOnPropagationSpec = {
|
|
|
1915
209
|
if (metadata.conditionalA && metadata.conditionalNotA) {
|
|
1916
210
|
const deducedOpinion = conditionalDeduction(
|
|
1917
211
|
dampedSource,
|
|
1918
|
-
|
|
1919
|
-
|
|
212
|
+
mkOpinion(
|
|
213
|
+
metadata.conditionalA.b,
|
|
214
|
+
metadata.conditionalA.d,
|
|
215
|
+
metadata.conditionalA.u,
|
|
216
|
+
metadata.conditionalA.a
|
|
217
|
+
),
|
|
218
|
+
mkOpinion(
|
|
219
|
+
metadata.conditionalNotA.b,
|
|
220
|
+
metadata.conditionalNotA.d,
|
|
221
|
+
metadata.conditionalNotA.u,
|
|
222
|
+
metadata.conditionalNotA.a
|
|
223
|
+
),
|
|
1920
224
|
targetOpinion.a
|
|
1921
225
|
);
|
|
1922
226
|
return annotateRationale(
|
|
@@ -2089,69 +393,30 @@ function getTraversalDirections(direction) {
|
|
|
2089
393
|
return ["outgoing", "incoming"];
|
|
2090
394
|
}
|
|
2091
395
|
|
|
2092
|
-
//
|
|
2093
|
-
|
|
2094
|
-
|
|
2095
|
-
"
|
|
2096
|
-
|
|
2097
|
-
|
|
2098
|
-
|
|
2099
|
-
|
|
2100
|
-
"claim",
|
|
2101
|
-
"evidence",
|
|
2102
|
-
"synthesis",
|
|
2103
|
-
"answer",
|
|
2104
|
-
"atomic_fact",
|
|
2105
|
-
"excerpt",
|
|
2106
|
-
"source",
|
|
2107
|
-
"company",
|
|
2108
|
-
"person",
|
|
2109
|
-
"investor",
|
|
2110
|
-
"function",
|
|
2111
|
-
"value_chain"
|
|
2112
|
-
];
|
|
2113
|
-
function isNodeType(value) {
|
|
2114
|
-
return NODE_TYPES.includes(value);
|
|
2115
|
-
}
|
|
2116
|
-
function getLayerForNodeType(type) {
|
|
2117
|
-
switch (type) {
|
|
2118
|
-
case "decision":
|
|
2119
|
-
return "L4";
|
|
2120
|
-
case "belief":
|
|
2121
|
-
case "question":
|
|
2122
|
-
case "theme":
|
|
2123
|
-
case "deal":
|
|
2124
|
-
return "L3";
|
|
2125
|
-
case "claim":
|
|
2126
|
-
case "evidence":
|
|
2127
|
-
case "synthesis":
|
|
2128
|
-
case "answer":
|
|
2129
|
-
return "L2";
|
|
2130
|
-
case "atomic_fact":
|
|
2131
|
-
case "excerpt":
|
|
2132
|
-
case "source":
|
|
2133
|
-
return "L1";
|
|
2134
|
-
case "topic":
|
|
2135
|
-
return "organizational";
|
|
2136
|
-
case "company":
|
|
2137
|
-
case "person":
|
|
2138
|
-
case "investor":
|
|
2139
|
-
case "function":
|
|
2140
|
-
case "value_chain":
|
|
2141
|
-
return "ontological";
|
|
396
|
+
// src/debug.ts
|
|
397
|
+
function isGraphPrimitiveDebugEnabled() {
|
|
398
|
+
const env = globalThis.process?.env;
|
|
399
|
+
return env?.LUCERN_COMPAT_FALLBACK_DEBUG === "1" || env?.LUCERN_GRAPH_DEBUG === "1";
|
|
400
|
+
}
|
|
401
|
+
function debugGraphPrimitiveFallback(message, context) {
|
|
402
|
+
if (!isGraphPrimitiveDebugEnabled()) {
|
|
403
|
+
return;
|
|
2142
404
|
}
|
|
405
|
+
console.debug(message, context ?? {});
|
|
2143
406
|
}
|
|
2144
|
-
|
|
407
|
+
|
|
408
|
+
// src/topicScope.ts
|
|
409
|
+
var LEGACY_SCOPE_FIELD = "graphScopeProjectId";
|
|
2145
410
|
function asMappedProjectId(topic) {
|
|
2146
411
|
if (!topic) {
|
|
2147
412
|
return;
|
|
2148
413
|
}
|
|
2149
|
-
const directLegacyProjectId = normalizeScopeValue(topic[
|
|
414
|
+
const directLegacyProjectId = normalizeScopeValue(topic[LEGACY_SCOPE_FIELD]);
|
|
2150
415
|
if (directLegacyProjectId) {
|
|
2151
416
|
return directLegacyProjectId;
|
|
2152
417
|
}
|
|
2153
418
|
const metadata = topic.metadata || {};
|
|
2154
|
-
const candidate = metadata[
|
|
419
|
+
const candidate = metadata[LEGACY_SCOPE_FIELD] || metadata.legacyProjectId || metadata.projectId || metadata.scopeProjectId;
|
|
2155
420
|
return candidate ? candidate : void 0;
|
|
2156
421
|
}
|
|
2157
422
|
function normalizeScopeValue(value) {
|
|
@@ -2180,9 +445,16 @@ async function findTopicsByScopeAlias(ctx, scopeId) {
|
|
|
2180
445
|
try {
|
|
2181
446
|
return await ctx.db.query("topics").withIndex(
|
|
2182
447
|
"by_graph_scope_project",
|
|
2183
|
-
(q) => q.eq(
|
|
448
|
+
(q) => q.eq(LEGACY_SCOPE_FIELD, scopeId)
|
|
2184
449
|
).collect();
|
|
2185
|
-
} catch {
|
|
450
|
+
} catch (error) {
|
|
451
|
+
debugGraphPrimitiveFallback(
|
|
452
|
+
"[topicScope] Failed to resolve scope alias via index",
|
|
453
|
+
{
|
|
454
|
+
error,
|
|
455
|
+
scopeId
|
|
456
|
+
}
|
|
457
|
+
);
|
|
2186
458
|
const topics = await ctx.db.query("topics").collect();
|
|
2187
459
|
return topics.filter((topic) => {
|
|
2188
460
|
const normalizedGlobalId = normalizeScopeValue(topic.globalId);
|
|
@@ -2196,10 +468,17 @@ async function tryResolveHostTopicById(ctx, topicId) {
|
|
|
2196
468
|
return null;
|
|
2197
469
|
}
|
|
2198
470
|
try {
|
|
2199
|
-
return await ctx.runQuery(
|
|
471
|
+
return await ctx.runQuery(api.topics.get, {
|
|
2200
472
|
id: topicId
|
|
2201
473
|
}) ?? null;
|
|
2202
|
-
} catch {
|
|
474
|
+
} catch (error) {
|
|
475
|
+
debugGraphPrimitiveFallback(
|
|
476
|
+
"[topicScope] Failed to resolve topic by host query",
|
|
477
|
+
{
|
|
478
|
+
error,
|
|
479
|
+
topicId
|
|
480
|
+
}
|
|
481
|
+
);
|
|
2203
482
|
return null;
|
|
2204
483
|
}
|
|
2205
484
|
}
|
|
@@ -2208,10 +487,17 @@ async function tryResolveHostTopicByLegacyScope(ctx, legacyScopeId) {
|
|
|
2208
487
|
return null;
|
|
2209
488
|
}
|
|
2210
489
|
try {
|
|
2211
|
-
return await ctx.runQuery(
|
|
490
|
+
return await ctx.runQuery(api.topics.getByLegacyScopeId, {
|
|
2212
491
|
projectId: legacyScopeId
|
|
2213
492
|
}) ?? null;
|
|
2214
|
-
} catch {
|
|
493
|
+
} catch (error) {
|
|
494
|
+
debugGraphPrimitiveFallback(
|
|
495
|
+
"[topicScope] Failed to resolve topic by legacy scope",
|
|
496
|
+
{
|
|
497
|
+
error,
|
|
498
|
+
legacyScopeId
|
|
499
|
+
}
|
|
500
|
+
);
|
|
2215
501
|
return null;
|
|
2216
502
|
}
|
|
2217
503
|
}
|
|
@@ -2240,8 +526,17 @@ async function resolveTopicProjectScope(ctx, args) {
|
|
|
2240
526
|
if (args.topicId) {
|
|
2241
527
|
let topic = null;
|
|
2242
528
|
try {
|
|
2243
|
-
topic = await ctx.db.get(
|
|
2244
|
-
|
|
529
|
+
topic = await ctx.db.get(
|
|
530
|
+
args.topicId
|
|
531
|
+
);
|
|
532
|
+
} catch (error) {
|
|
533
|
+
debugGraphPrimitiveFallback(
|
|
534
|
+
"[topicScope] Failed to load topic by direct id",
|
|
535
|
+
{
|
|
536
|
+
error,
|
|
537
|
+
topicId: args.topicId
|
|
538
|
+
}
|
|
539
|
+
);
|
|
2245
540
|
}
|
|
2246
541
|
if (!topic) {
|
|
2247
542
|
topic = await tryResolveHostTopicById(ctx, String(args.topicId));
|
|
@@ -2278,7 +573,14 @@ async function resolveTopicProjectScope(ctx, args) {
|
|
|
2278
573
|
directTopic = await ctx.db.get(
|
|
2279
574
|
args.projectId
|
|
2280
575
|
);
|
|
2281
|
-
} catch {
|
|
576
|
+
} catch (error) {
|
|
577
|
+
debugGraphPrimitiveFallback(
|
|
578
|
+
"[topicScope] Failed to load direct project topic",
|
|
579
|
+
{
|
|
580
|
+
error,
|
|
581
|
+
projectId: args.projectId
|
|
582
|
+
}
|
|
583
|
+
);
|
|
2282
584
|
}
|
|
2283
585
|
if (directTopic) {
|
|
2284
586
|
const inherited = await resolveInheritedWorkspaceScope(ctx, directTopic);
|
|
@@ -2485,16 +787,17 @@ function resolveTraversalTargetNodeId(edge, direction) {
|
|
|
2485
787
|
}
|
|
2486
788
|
function readNodeOpinion(node) {
|
|
2487
789
|
const metadata = node.metadata ?? {};
|
|
2488
|
-
|
|
2489
|
-
{
|
|
790
|
+
try {
|
|
791
|
+
return readOpinionFromRecord({
|
|
2490
792
|
...metadata,
|
|
2491
793
|
opinion_b: node.opinion_b,
|
|
2492
794
|
opinion_d: node.opinion_d,
|
|
2493
795
|
opinion_u: node.opinion_u,
|
|
2494
796
|
opinion_a: node.opinion_a
|
|
2495
|
-
}
|
|
2496
|
-
|
|
2497
|
-
|
|
797
|
+
});
|
|
798
|
+
} catch {
|
|
799
|
+
return mkOpinion(0, 0, 1, 0.5);
|
|
800
|
+
}
|
|
2498
801
|
}
|
|
2499
802
|
async function collectConfidencePropagationDispatches(args) {
|
|
2500
803
|
const dispatchesByTargetId = /* @__PURE__ */ new Map();
|
|
@@ -2563,14 +866,20 @@ async function collectConfidencePropagationDispatches(args) {
|
|
|
2563
866
|
if (!result || !hasProjectedOpinionChanged(targetOpinion, result.opinion)) {
|
|
2564
867
|
continue;
|
|
2565
868
|
}
|
|
2566
|
-
|
|
869
|
+
const projectedOpinion = mkOpinion(
|
|
870
|
+
result.opinion.b,
|
|
871
|
+
result.opinion.d,
|
|
872
|
+
result.opinion.u,
|
|
873
|
+
result.opinion.a
|
|
874
|
+
);
|
|
875
|
+
opinionCache.set(cacheKey, projectedOpinion);
|
|
2567
876
|
const existingDispatch = dispatchesByTargetId.get(cacheKey);
|
|
2568
877
|
dispatchesByTargetId.set(cacheKey, {
|
|
2569
878
|
targetNodeId,
|
|
2570
879
|
edgeType: spec.edgeType,
|
|
2571
880
|
traversedDirection: direction,
|
|
2572
881
|
weight: edge.weight ?? 1,
|
|
2573
|
-
opinion:
|
|
882
|
+
opinion: projectedOpinion,
|
|
2574
883
|
operator: result.operator,
|
|
2575
884
|
rationale: existingDispatch ? `${existingDispatch.rationale}; ${result.rationale}` : result.rationale,
|
|
2576
885
|
hop: nextHop
|
|
@@ -2578,7 +887,7 @@ async function collectConfidencePropagationDispatches(args) {
|
|
|
2578
887
|
if (canContinueTransitively(spec, nextHop)) {
|
|
2579
888
|
queue.push({
|
|
2580
889
|
nodeId: targetNodeId,
|
|
2581
|
-
opinion:
|
|
890
|
+
opinion: projectedOpinion,
|
|
2582
891
|
hop: nextHop,
|
|
2583
892
|
visitedNodeIds: /* @__PURE__ */ new Set([
|
|
2584
893
|
...state.visitedNodeIds,
|
|
@@ -2615,7 +924,15 @@ async function scheduleEmbeddingGeneration(args) {
|
|
|
2615
924
|
confidence: args.confidence
|
|
2616
925
|
}
|
|
2617
926
|
);
|
|
2618
|
-
} catch {
|
|
927
|
+
} catch (error) {
|
|
928
|
+
debugGraphPrimitiveFallback(
|
|
929
|
+
"[embeddingTrigger] Failed to schedule embedding generation",
|
|
930
|
+
{
|
|
931
|
+
error,
|
|
932
|
+
nodeId: String(args.nodeId),
|
|
933
|
+
nodeType: args.nodeType
|
|
934
|
+
}
|
|
935
|
+
);
|
|
2619
936
|
}
|
|
2620
937
|
}
|
|
2621
938
|
|
|
@@ -2693,96 +1010,125 @@ async function getQuestionsAnsweredByEvidence(ctx, evidenceId) {
|
|
|
2693
1010
|
}
|
|
2694
1011
|
|
|
2695
1012
|
// src/topicProjectOverlay.ts
|
|
2696
|
-
var
|
|
2697
|
-
function
|
|
1013
|
+
var LEGACY_SCOPE_FIELD2 = "graphScopeProjectId";
|
|
1014
|
+
function readNonEmptyString(value) {
|
|
2698
1015
|
if (typeof value !== "string") {
|
|
2699
1016
|
return;
|
|
2700
1017
|
}
|
|
2701
1018
|
const normalized = value.trim();
|
|
2702
1019
|
return normalized.length > 0 ? normalized : void 0;
|
|
2703
1020
|
}
|
|
2704
|
-
function
|
|
1021
|
+
function readStringArray(value) {
|
|
2705
1022
|
if (!Array.isArray(value)) {
|
|
2706
1023
|
return [];
|
|
2707
1024
|
}
|
|
2708
|
-
return value.map((entry) =>
|
|
1025
|
+
return value.map((entry) => readNonEmptyString(entry)).filter((entry) => Boolean(entry));
|
|
2709
1026
|
}
|
|
2710
|
-
function
|
|
1027
|
+
function readMetadata(topic) {
|
|
2711
1028
|
return topic.metadata && typeof topic.metadata === "object" ? topic.metadata : {};
|
|
2712
1029
|
}
|
|
2713
|
-
function
|
|
1030
|
+
function readLegacyProjectId(value) {
|
|
2714
1031
|
if (!value) {
|
|
2715
1032
|
return;
|
|
2716
1033
|
}
|
|
2717
|
-
return
|
|
1034
|
+
return readNonEmptyString(value[LEGACY_SCOPE_FIELD2]);
|
|
2718
1035
|
}
|
|
2719
|
-
function
|
|
1036
|
+
function coerceVisibility(value) {
|
|
2720
1037
|
return value === "private" || value === "team" || value === "firm" || value === "external" || value === "public" ? value : void 0;
|
|
2721
1038
|
}
|
|
2722
|
-
function
|
|
1039
|
+
function coerceStatus(value) {
|
|
2723
1040
|
return value === "active" || value === "archived" || value === "watching" ? value : void 0;
|
|
2724
1041
|
}
|
|
2725
|
-
function
|
|
2726
|
-
const explicit =
|
|
1042
|
+
function mapProjectType(topic, metadata) {
|
|
1043
|
+
const explicit = readNonEmptyString(metadata.projectType);
|
|
2727
1044
|
if (explicit) {
|
|
2728
1045
|
return explicit;
|
|
2729
1046
|
}
|
|
2730
1047
|
if (topic.type === "theme") {
|
|
2731
1048
|
return "thematic";
|
|
2732
1049
|
}
|
|
2733
|
-
return
|
|
1050
|
+
return readNonEmptyString(topic.type) || "general";
|
|
2734
1051
|
}
|
|
2735
|
-
function
|
|
2736
|
-
const metadata =
|
|
2737
|
-
return topic.type === "theme" || topic.type === "thematic" || topic.type === "deal" || topic.type === "monitoring" ||
|
|
1052
|
+
function isProjectLikeTopic(topic) {
|
|
1053
|
+
const metadata = readMetadata(topic);
|
|
1054
|
+
return topic.type === "theme" || topic.type === "thematic" || topic.type === "deal" || topic.type === "monitoring" || readLegacyProjectId(topic) !== void 0 || readNonEmptyString(metadata.projectType) !== void 0;
|
|
2738
1055
|
}
|
|
2739
1056
|
function isMissingLucernChildComponentError(error) {
|
|
2740
|
-
const message =
|
|
1057
|
+
const message = getErrorMessage(error);
|
|
2741
1058
|
return message.includes(
|
|
2742
1059
|
'Child component ComponentName(Identifier("lucern")) not found'
|
|
2743
1060
|
) || message.includes("Child component") && message.includes("lucern") && message.includes("not found");
|
|
2744
1061
|
}
|
|
2745
|
-
|
|
1062
|
+
function getErrorMessage(error) {
|
|
1063
|
+
if (error instanceof Error) {
|
|
1064
|
+
return error.message;
|
|
1065
|
+
}
|
|
1066
|
+
if (typeof error === "object" && error !== null && "message" in error && typeof error.message === "string") {
|
|
1067
|
+
return error.message;
|
|
1068
|
+
}
|
|
1069
|
+
return "unknown error";
|
|
1070
|
+
}
|
|
1071
|
+
async function resolveTopicDoc(ctx, scopeId) {
|
|
2746
1072
|
if (ctx?.db && typeof ctx.db.get === "function") {
|
|
2747
1073
|
try {
|
|
2748
|
-
const directTopic = await ctx.db.get(
|
|
1074
|
+
const directTopic = await ctx.db.get(
|
|
1075
|
+
scopeId
|
|
1076
|
+
);
|
|
2749
1077
|
if (directTopic) {
|
|
2750
1078
|
return directTopic;
|
|
2751
1079
|
}
|
|
2752
|
-
} catch {
|
|
1080
|
+
} catch (error) {
|
|
1081
|
+
debugGraphPrimitiveFallback(
|
|
1082
|
+
"[topicProjectOverlay] Failed to resolve topic by direct ID",
|
|
1083
|
+
{
|
|
1084
|
+
error,
|
|
1085
|
+
scopeId
|
|
1086
|
+
}
|
|
1087
|
+
);
|
|
2753
1088
|
}
|
|
2754
1089
|
}
|
|
2755
1090
|
if (typeof ctx.runQuery !== "function") {
|
|
2756
1091
|
return null;
|
|
2757
1092
|
}
|
|
2758
1093
|
try {
|
|
2759
|
-
const topic = await ctx.runQuery(
|
|
1094
|
+
const topic = await ctx.runQuery(api.topics.get, {
|
|
2760
1095
|
id: String(scopeId)
|
|
2761
1096
|
});
|
|
2762
1097
|
if (topic?.name !== void 0 && topic?.type !== void 0) {
|
|
2763
1098
|
return topic;
|
|
2764
1099
|
}
|
|
2765
|
-
} catch {
|
|
1100
|
+
} catch (error) {
|
|
1101
|
+
debugGraphPrimitiveFallback(
|
|
1102
|
+
"[topicProjectOverlay] Failed to resolve topic by ID query",
|
|
1103
|
+
{
|
|
1104
|
+
error,
|
|
1105
|
+
scopeId
|
|
1106
|
+
}
|
|
1107
|
+
);
|
|
2766
1108
|
}
|
|
2767
1109
|
try {
|
|
2768
|
-
const topic = await ctx.runQuery(
|
|
1110
|
+
const topic = await ctx.runQuery(api.topics.getByLegacyScopeId, {
|
|
2769
1111
|
projectId: String(scopeId)
|
|
2770
1112
|
});
|
|
2771
1113
|
if (topic?.name !== void 0 && topic?.type !== void 0) {
|
|
2772
1114
|
return topic;
|
|
2773
1115
|
}
|
|
2774
|
-
} catch {
|
|
1116
|
+
} catch (error) {
|
|
1117
|
+
debugGraphPrimitiveFallback(
|
|
1118
|
+
"[topicProjectOverlay] Failed to resolve topic by legacy scope ID",
|
|
1119
|
+
{ error, scopeId }
|
|
1120
|
+
);
|
|
2775
1121
|
}
|
|
2776
1122
|
return null;
|
|
2777
1123
|
}
|
|
2778
|
-
function
|
|
2779
|
-
const metadata =
|
|
1124
|
+
function materializeTopicProjectOverlay(topic, idMode = "legacy") {
|
|
1125
|
+
const metadata = readMetadata(topic);
|
|
2780
1126
|
const topicId = String(topic._id);
|
|
2781
|
-
const legacyProjectId =
|
|
1127
|
+
const legacyProjectId = readLegacyProjectId(topic) || readLegacyProjectId(metadata) || readNonEmptyString(metadata.legacyProjectId);
|
|
2782
1128
|
const storageProjectId = legacyProjectId || topicId;
|
|
2783
1129
|
const outwardId = idMode === "topic" ? topicId : storageProjectId;
|
|
2784
|
-
const visibility =
|
|
2785
|
-
const status =
|
|
1130
|
+
const visibility = coerceVisibility(topic.visibility) || coerceVisibility(metadata.visibility) || "private";
|
|
1131
|
+
const status = coerceStatus(topic.status) || coerceStatus(metadata.status) || "active";
|
|
2786
1132
|
const createdAt = typeof topic.createdAt === "number" ? topic.createdAt : typeof topic._creationTime === "number" ? topic._creationTime : 0;
|
|
2787
1133
|
const updatedAt = typeof topic.updatedAt === "number" ? topic.updatedAt : typeof metadata.updatedAt === "number" ? metadata.updatedAt : createdAt;
|
|
2788
1134
|
return {
|
|
@@ -2792,16 +1138,16 @@ function materializeTopicProjectOverlay2(topic, idMode = "legacy") {
|
|
|
2792
1138
|
topicId,
|
|
2793
1139
|
storageProjectId,
|
|
2794
1140
|
legacyProjectId,
|
|
2795
|
-
name:
|
|
2796
|
-
type:
|
|
2797
|
-
description:
|
|
2798
|
-
ownerId:
|
|
2799
|
-
sharedWith:
|
|
1141
|
+
name: readNonEmptyString(topic.name) || "Untitled Theme",
|
|
1142
|
+
type: mapProjectType(topic, metadata),
|
|
1143
|
+
description: readNonEmptyString(topic.description),
|
|
1144
|
+
ownerId: readNonEmptyString(metadata.ownerId) || readNonEmptyString(topic.createdBy) || "system",
|
|
1145
|
+
sharedWith: readStringArray(metadata.sharedWith),
|
|
2800
1146
|
visibility,
|
|
2801
|
-
tenantId:
|
|
2802
|
-
workspaceId:
|
|
1147
|
+
tenantId: readNonEmptyString(topic.tenantId) || readNonEmptyString(metadata.tenantId),
|
|
1148
|
+
workspaceId: readNonEmptyString(topic.workspaceId) || readNonEmptyString(metadata.workspaceId),
|
|
2803
1149
|
status,
|
|
2804
|
-
tags:
|
|
1150
|
+
tags: readStringArray(metadata.tags),
|
|
2805
1151
|
chatCount: typeof metadata.chatCount === "number" ? metadata.chatCount : 0,
|
|
2806
1152
|
artifactCount: typeof metadata.artifactCount === "number" ? metadata.artifactCount : 0,
|
|
2807
1153
|
lastActivityAt: typeof metadata.lastActivityAt === "number" ? metadata.lastActivityAt : updatedAt,
|
|
@@ -2810,38 +1156,42 @@ function materializeTopicProjectOverlay2(topic, idMode = "legacy") {
|
|
|
2810
1156
|
updatedAt
|
|
2811
1157
|
};
|
|
2812
1158
|
}
|
|
2813
|
-
async function
|
|
2814
|
-
const topic = await
|
|
1159
|
+
async function resolveTopicProjectOverlay(ctx, scopeId, options = {}) {
|
|
1160
|
+
const topic = await resolveTopicDoc(ctx, scopeId);
|
|
2815
1161
|
if (!topic) {
|
|
2816
1162
|
return null;
|
|
2817
1163
|
}
|
|
2818
|
-
if (options.projectLikeOnly !== false && !
|
|
1164
|
+
if (options.projectLikeOnly !== false && !isProjectLikeTopic(topic)) {
|
|
2819
1165
|
return null;
|
|
2820
1166
|
}
|
|
2821
|
-
return
|
|
1167
|
+
return materializeTopicProjectOverlay(topic, options.idMode);
|
|
2822
1168
|
}
|
|
2823
|
-
async function
|
|
1169
|
+
async function listTopicProjectOverlays(ctx, options = {}) {
|
|
2824
1170
|
let allTopics = [];
|
|
2825
1171
|
if (ctx?.db?.query && typeof ctx.db.query === "function") {
|
|
2826
1172
|
try {
|
|
2827
1173
|
allTopics = await ctx.db.query("topics").collect();
|
|
2828
|
-
} catch {
|
|
1174
|
+
} catch (error) {
|
|
1175
|
+
debugGraphPrimitiveFallback(
|
|
1176
|
+
"[topicProjectOverlay] Failed to read topics table; falling back to API",
|
|
1177
|
+
{ error }
|
|
1178
|
+
);
|
|
2829
1179
|
allTopics = [];
|
|
2830
1180
|
}
|
|
2831
1181
|
}
|
|
2832
1182
|
if (allTopics.length === 0 && typeof ctx.runQuery === "function") {
|
|
2833
|
-
allTopics = (await ctx.runQuery(
|
|
1183
|
+
allTopics = (await ctx.runQuery(api.topics.list, {}) ?? []) || [];
|
|
2834
1184
|
}
|
|
2835
1185
|
return allTopics.filter(
|
|
2836
|
-
(topic) => options.projectLikeOnly === false ||
|
|
2837
|
-
).map((topic) =>
|
|
1186
|
+
(topic) => options.projectLikeOnly === false || isProjectLikeTopic(topic)
|
|
1187
|
+
).map((topic) => materializeTopicProjectOverlay(topic, options.idMode));
|
|
2838
1188
|
}
|
|
2839
1189
|
async function patchTopicProjectOverlay(ctx, scopeId, value) {
|
|
2840
|
-
const topic = await
|
|
1190
|
+
const topic = await resolveTopicDoc(ctx, scopeId);
|
|
2841
1191
|
if (!topic) {
|
|
2842
1192
|
return null;
|
|
2843
1193
|
}
|
|
2844
|
-
const nextMetadata = { ...
|
|
1194
|
+
const nextMetadata = { ...readMetadata(topic) };
|
|
2845
1195
|
const patch = {};
|
|
2846
1196
|
const topicUpdateArgs = {
|
|
2847
1197
|
id: String(topic._id)
|
|
@@ -2866,7 +1216,7 @@ async function patchTopicProjectOverlay(ctx, scopeId, value) {
|
|
|
2866
1216
|
`patchTopicProjectOverlay cannot mutate ${key} via component-owned topics`
|
|
2867
1217
|
);
|
|
2868
1218
|
case "status": {
|
|
2869
|
-
const status =
|
|
1219
|
+
const status = coerceStatus(rawValue);
|
|
2870
1220
|
if (status) {
|
|
2871
1221
|
patch.status = status;
|
|
2872
1222
|
topicUpdateArgs.status = status;
|
|
@@ -2874,7 +1224,7 @@ async function patchTopicProjectOverlay(ctx, scopeId, value) {
|
|
|
2874
1224
|
break;
|
|
2875
1225
|
}
|
|
2876
1226
|
case "visibility": {
|
|
2877
|
-
const visibility =
|
|
1227
|
+
const visibility = coerceVisibility(rawValue);
|
|
2878
1228
|
if (visibility) {
|
|
2879
1229
|
patch.visibility = visibility;
|
|
2880
1230
|
topicUpdateArgs.visibility = visibility;
|
|
@@ -2882,7 +1232,7 @@ async function patchTopicProjectOverlay(ctx, scopeId, value) {
|
|
|
2882
1232
|
break;
|
|
2883
1233
|
}
|
|
2884
1234
|
case "type": {
|
|
2885
|
-
const projectType =
|
|
1235
|
+
const projectType = readNonEmptyString(rawValue);
|
|
2886
1236
|
if (projectType) {
|
|
2887
1237
|
nextMetadata.projectType = projectType;
|
|
2888
1238
|
} else {
|
|
@@ -2906,7 +1256,7 @@ async function patchTopicProjectOverlay(ctx, scopeId, value) {
|
|
|
2906
1256
|
topicUpdateArgs.metadata = nextMetadata;
|
|
2907
1257
|
if (typeof ctx.runMutation === "function") {
|
|
2908
1258
|
try {
|
|
2909
|
-
await ctx.runMutation(
|
|
1259
|
+
await ctx.runMutation(api.topics.update, topicUpdateArgs);
|
|
2910
1260
|
} catch (error) {
|
|
2911
1261
|
if (!isMissingLucernChildComponentError(error) || !ctx?.db || typeof ctx.db.patch !== "function") {
|
|
2912
1262
|
throw error;
|
|
@@ -2920,19 +1270,28 @@ async function patchTopicProjectOverlay(ctx, scopeId, value) {
|
|
|
2920
1270
|
"Cannot patch topic without component adapter (ctx.runMutation unavailable)"
|
|
2921
1271
|
);
|
|
2922
1272
|
}
|
|
2923
|
-
return
|
|
2924
|
-
|
|
2925
|
-
|
|
2926
|
-
|
|
2927
|
-
|
|
2928
|
-
}
|
|
2929
|
-
);
|
|
1273
|
+
return materializeTopicProjectOverlay({
|
|
1274
|
+
...topic,
|
|
1275
|
+
...patch,
|
|
1276
|
+
metadata: nextMetadata
|
|
1277
|
+
});
|
|
2930
1278
|
}
|
|
2931
1279
|
|
|
2932
1280
|
// src/resolvers.ts
|
|
2933
1281
|
function isMissingLucernChildComponentError2(error) {
|
|
2934
|
-
const message =
|
|
2935
|
-
return message.includes(
|
|
1282
|
+
const message = getErrorMessage2(error);
|
|
1283
|
+
return message.includes(
|
|
1284
|
+
'Child component ComponentName(Identifier("lucern")) not found'
|
|
1285
|
+
) || message.includes("Child component") && message.includes("lucern") && message.includes("not found");
|
|
1286
|
+
}
|
|
1287
|
+
function getErrorMessage2(error) {
|
|
1288
|
+
if (error instanceof Error) {
|
|
1289
|
+
return error.message;
|
|
1290
|
+
}
|
|
1291
|
+
if (typeof error === "object" && error !== null && "message" in error && typeof error.message === "string") {
|
|
1292
|
+
return error.message;
|
|
1293
|
+
}
|
|
1294
|
+
return "unknown error";
|
|
2936
1295
|
}
|
|
2937
1296
|
function isAdvisoryTopicPatch(value) {
|
|
2938
1297
|
const advisoryKeys = /* @__PURE__ */ new Set(["lastActivityAt", "updatedAt"]);
|
|
@@ -2946,39 +1305,34 @@ async function patchProjectWithTolerance(ctx, projectId, value) {
|
|
|
2946
1305
|
if (!isAdvisoryTopicPatch(value) || !isMissingLucernChildComponentError2(error)) {
|
|
2947
1306
|
throw error;
|
|
2948
1307
|
}
|
|
2949
|
-
console.warn(
|
|
2950
|
-
|
|
2951
|
-
|
|
2952
|
-
|
|
2953
|
-
|
|
1308
|
+
console.warn(
|
|
1309
|
+
"[lucern graph-primitives] Non-fatal advisory topic patch failure",
|
|
1310
|
+
{
|
|
1311
|
+
projectId,
|
|
1312
|
+
keys: Object.keys(value),
|
|
1313
|
+
error: getErrorMessage2(error)
|
|
1314
|
+
}
|
|
1315
|
+
);
|
|
2954
1316
|
}
|
|
2955
1317
|
}
|
|
2956
|
-
function
|
|
1318
|
+
function defaultResolvers() {
|
|
2957
1319
|
return {
|
|
2958
|
-
|
|
2959
|
-
|
|
2960
|
-
|
|
2961
|
-
|
|
2962
|
-
|
|
2963
|
-
|
|
2964
|
-
|
|
2965
|
-
|
|
2966
|
-
|
|
2967
|
-
async listTopics(ctx) {
|
|
2968
|
-
return await listTopicProjectOverlays2(ctx, {
|
|
2969
|
-
idMode: "legacy"
|
|
2970
|
-
});
|
|
2971
|
-
},
|
|
2972
|
-
async getFinalArtifact(ctx, artifactId) {
|
|
2973
|
-
return await ctx.db.get(artifactId);
|
|
2974
|
-
}
|
|
1320
|
+
getProject: (ctx, projectId) => resolveTopicProjectOverlay(ctx, projectId, {
|
|
1321
|
+
idMode: "legacy",
|
|
1322
|
+
projectLikeOnly: false
|
|
1323
|
+
}),
|
|
1324
|
+
patchProject: (ctx, projectId, value) => patchProjectWithTolerance(ctx, projectId, value),
|
|
1325
|
+
listTopics: (ctx) => listTopicProjectOverlays(ctx, {
|
|
1326
|
+
idMode: "legacy"
|
|
1327
|
+
}),
|
|
1328
|
+
getFinalArtifact: (ctx, artifactId) => ctx.db.get(artifactId)
|
|
2975
1329
|
};
|
|
2976
1330
|
}
|
|
2977
|
-
var
|
|
1331
|
+
var resolverOverrides = {};
|
|
2978
1332
|
function resolveGraphPrimitivesAppResolvers(_ctx) {
|
|
2979
1333
|
return {
|
|
2980
|
-
...
|
|
2981
|
-
...
|
|
1334
|
+
...defaultResolvers(),
|
|
1335
|
+
...resolverOverrides
|
|
2982
1336
|
};
|
|
2983
1337
|
}
|
|
2984
1338
|
|
|
@@ -3003,7 +1357,7 @@ function throwStructuredMutationError(args) {
|
|
|
3003
1357
|
function readFiniteNumber(value) {
|
|
3004
1358
|
return typeof value === "number" && Number.isFinite(value) ? value : void 0;
|
|
3005
1359
|
}
|
|
3006
|
-
function
|
|
1360
|
+
function clamp01(value) {
|
|
3007
1361
|
return Math.max(0, Math.min(1, value));
|
|
3008
1362
|
}
|
|
3009
1363
|
function assertBaseRateInRange(baseRate, field = "baseRate") {
|
|
@@ -3047,26 +1401,35 @@ function buildBeliefConfidenceRow(args) {
|
|
|
3047
1401
|
...args.triggeringWorktreeId ? { triggeringWorktreeId: args.triggeringWorktreeId } : {}
|
|
3048
1402
|
};
|
|
3049
1403
|
}
|
|
1404
|
+
function buildBeliefStatusSuccessResult() {
|
|
1405
|
+
return { success: true };
|
|
1406
|
+
}
|
|
1407
|
+
function buildBeliefEvidenceNotFoundResult() {
|
|
1408
|
+
const result = {};
|
|
1409
|
+
result.success = false;
|
|
1410
|
+
result.message = "Evidence node not found";
|
|
1411
|
+
return result;
|
|
1412
|
+
}
|
|
3050
1413
|
function deriveSyntheticBackfillOpinion(source) {
|
|
3051
1414
|
const belief = readFiniteNumber(source.opinion_b) ?? readFiniteNumber(source.belief);
|
|
3052
1415
|
const disbelief = readFiniteNumber(source.opinion_d) ?? readFiniteNumber(source.disbelief);
|
|
3053
1416
|
const uncertainty = readFiniteNumber(source.opinion_u) ?? readFiniteNumber(source.uncertainty);
|
|
3054
1417
|
const baseRate = readFiniteNumber(source.opinion_a) ?? readFiniteNumber(source.baseRate);
|
|
3055
1418
|
if (belief !== void 0 || disbelief !== void 0 || uncertainty !== void 0 || baseRate !== void 0) {
|
|
3056
|
-
|
|
3057
|
-
|
|
3058
|
-
|
|
3059
|
-
|
|
3060
|
-
|
|
3061
|
-
|
|
1419
|
+
try {
|
|
1420
|
+
return readOpinionFromRecord(source);
|
|
1421
|
+
} catch (error) {
|
|
1422
|
+
debugGraphPrimitiveFallback(
|
|
1423
|
+
"[epistemicBeliefs] Failed to decode legacy belief opinion",
|
|
1424
|
+
{
|
|
1425
|
+
error
|
|
1426
|
+
}
|
|
1427
|
+
);
|
|
1428
|
+
return mkOpinion(0, 0, 1, 0.5);
|
|
1429
|
+
}
|
|
3062
1430
|
}
|
|
3063
|
-
const confidence =
|
|
3064
|
-
return
|
|
3065
|
-
b: confidence,
|
|
3066
|
-
d: 1 - confidence,
|
|
3067
|
-
u: 0,
|
|
3068
|
-
a: 0.5
|
|
3069
|
-
};
|
|
1431
|
+
const confidence = clamp01(readFiniteNumber(source.confidence) ?? 0);
|
|
1432
|
+
return mkOpinion(confidence, 1 - confidence, 0, 0.5);
|
|
3070
1433
|
}
|
|
3071
1434
|
function clampBeliefLimit(limit, fallback = DEFAULT_PROJECT_BELIEF_LIMIT) {
|
|
3072
1435
|
if (!Number.isFinite(limit)) {
|
|
@@ -3081,16 +1444,24 @@ function readTupleContradictedFlag(value) {
|
|
|
3081
1444
|
return typeof value === "boolean" ? value : void 0;
|
|
3082
1445
|
}
|
|
3083
1446
|
function readBeliefOpinionSnapshot(node, metadata) {
|
|
3084
|
-
|
|
3085
|
-
{
|
|
1447
|
+
try {
|
|
1448
|
+
return readOpinionFromRecord({
|
|
3086
1449
|
...metadata,
|
|
3087
1450
|
opinion_b: node.opinion_b,
|
|
3088
1451
|
opinion_d: node.opinion_d,
|
|
3089
1452
|
opinion_u: node.opinion_u,
|
|
3090
1453
|
opinion_a: node.opinion_a
|
|
3091
|
-
}
|
|
3092
|
-
|
|
3093
|
-
|
|
1454
|
+
});
|
|
1455
|
+
} catch (error) {
|
|
1456
|
+
debugGraphPrimitiveFallback(
|
|
1457
|
+
"[epistemicBeliefs] Failed to read belief opinion snapshot",
|
|
1458
|
+
{
|
|
1459
|
+
error,
|
|
1460
|
+
beliefId: node._id
|
|
1461
|
+
}
|
|
1462
|
+
);
|
|
1463
|
+
return mkOpinion(0, 0, 1, 0.5);
|
|
1464
|
+
}
|
|
3094
1465
|
}
|
|
3095
1466
|
function deriveTupleContradictionSeverity(node) {
|
|
3096
1467
|
const metadata = node.metadata || {};
|
|
@@ -3181,7 +1552,15 @@ async function resolveBeliefScopeOrNull(ctx, args) {
|
|
|
3181
1552
|
projectId: args.projectId ?? void 0,
|
|
3182
1553
|
topicId: args.topicId ?? void 0
|
|
3183
1554
|
});
|
|
3184
|
-
} catch {
|
|
1555
|
+
} catch (error) {
|
|
1556
|
+
debugGraphPrimitiveFallback(
|
|
1557
|
+
"[epistemicBeliefs] Failed to resolve belief scope",
|
|
1558
|
+
{
|
|
1559
|
+
error,
|
|
1560
|
+
projectId: args.projectId,
|
|
1561
|
+
topicId: args.topicId
|
|
1562
|
+
}
|
|
1563
|
+
);
|
|
3185
1564
|
return null;
|
|
3186
1565
|
}
|
|
3187
1566
|
}
|
|
@@ -3281,7 +1660,13 @@ async function getActiveConfidencePolicy(ctx) {
|
|
|
3281
1660
|
activeConfig?.tupleContradictionPolicy
|
|
3282
1661
|
)
|
|
3283
1662
|
};
|
|
3284
|
-
} catch {
|
|
1663
|
+
} catch (error) {
|
|
1664
|
+
debugGraphPrimitiveFallback(
|
|
1665
|
+
"[epistemicBeliefs] Failed to load active confidence policy",
|
|
1666
|
+
{
|
|
1667
|
+
error
|
|
1668
|
+
}
|
|
1669
|
+
);
|
|
3285
1670
|
return DEFAULT_CONFIDENCE_POLICY;
|
|
3286
1671
|
}
|
|
3287
1672
|
}
|
|
@@ -3318,7 +1703,11 @@ async function applyBeliefConfidenceChange(ctx, args) {
|
|
|
3318
1703
|
details: { nodeId: args.nodeId }
|
|
3319
1704
|
});
|
|
3320
1705
|
}
|
|
3321
|
-
await requireProjectWriteAccess(
|
|
1706
|
+
await requireProjectWriteAccess(
|
|
1707
|
+
ctx,
|
|
1708
|
+
node.projectId,
|
|
1709
|
+
args.authenticatedUserId
|
|
1710
|
+
);
|
|
3322
1711
|
const existingMetadata = node.metadata || {};
|
|
3323
1712
|
const currentBeliefStatus = resolveBeliefStatus(node, existingMetadata);
|
|
3324
1713
|
const confidencePolicy = await getActiveConfidencePolicy(ctx);
|
|
@@ -3362,29 +1751,29 @@ async function applyBeliefConfidenceChange(ctx, args) {
|
|
|
3362
1751
|
opinion: nextOpinion,
|
|
3363
1752
|
policy: tupleTransition.policy
|
|
3364
1753
|
});
|
|
3365
|
-
const newBeliefStatus = promoteBeliefStatusAfterScoring(
|
|
3366
|
-
|
|
3367
|
-
|
|
3368
|
-
|
|
3369
|
-
|
|
3370
|
-
metadata: existingMetadata
|
|
3371
|
-
}
|
|
3372
|
-
);
|
|
1754
|
+
const newBeliefStatus = promoteBeliefStatusAfterScoring(currentBeliefStatus, {
|
|
1755
|
+
confidence: derivedConfidence,
|
|
1756
|
+
predictionMeta,
|
|
1757
|
+
metadata: existingMetadata
|
|
1758
|
+
});
|
|
3373
1759
|
let tupleContradictionId;
|
|
3374
1760
|
if (tupleTransition.crossedIntoTupleContradiction) {
|
|
3375
|
-
tupleContradictionId = await ctx.runMutation(
|
|
3376
|
-
|
|
3377
|
-
|
|
3378
|
-
|
|
3379
|
-
|
|
3380
|
-
|
|
3381
|
-
|
|
3382
|
-
|
|
3383
|
-
|
|
3384
|
-
|
|
3385
|
-
|
|
3386
|
-
|
|
3387
|
-
|
|
1761
|
+
tupleContradictionId = await ctx.runMutation(
|
|
1762
|
+
"contradictions:create",
|
|
1763
|
+
{
|
|
1764
|
+
projectId: node.projectId,
|
|
1765
|
+
topicId: node.topicId,
|
|
1766
|
+
beliefId: args.nodeId,
|
|
1767
|
+
beliefBId: args.nodeId,
|
|
1768
|
+
supportingInsightIds: [],
|
|
1769
|
+
contradictingInsightIds: [],
|
|
1770
|
+
severity: deriveTupleContradictionSeverity(node),
|
|
1771
|
+
source: "tuple_space",
|
|
1772
|
+
detectionMethod: "agent",
|
|
1773
|
+
description: tupleContradictionDescription,
|
|
1774
|
+
createdBy: args.authenticatedUserId
|
|
1775
|
+
}
|
|
1776
|
+
);
|
|
3388
1777
|
}
|
|
3389
1778
|
await ctx.db.patch(args.nodeId, {
|
|
3390
1779
|
confidence: derivedConfidence,
|
|
@@ -3408,7 +1797,10 @@ async function applyBeliefConfidenceChange(ctx, args) {
|
|
|
3408
1797
|
});
|
|
3409
1798
|
if (isFirstScoring) {
|
|
3410
1799
|
const nodeTopicId = node.topicId;
|
|
3411
|
-
const themeNodes = await ctx.db.query("epistemicNodes").withIndex(
|
|
1800
|
+
const themeNodes = await ctx.db.query("epistemicNodes").withIndex(
|
|
1801
|
+
"by_topic",
|
|
1802
|
+
(q) => q.eq("topicId", nodeTopicId || node.projectId)
|
|
1803
|
+
).filter((q) => q.eq(q.field("nodeType"), "theme")).collect();
|
|
3412
1804
|
for (const theme of themeNodes) {
|
|
3413
1805
|
if (theme.globalId && node.globalId) {
|
|
3414
1806
|
await ctx.scheduler.runAfter(0, internal.neo4jEdgeAPI.createEdge, {
|
|
@@ -3538,12 +1930,12 @@ internalMutation({
|
|
|
3538
1930
|
},
|
|
3539
1931
|
returns: permissiveReturn,
|
|
3540
1932
|
handler: async (ctx, args) => {
|
|
3541
|
-
const sourceOpinion =
|
|
3542
|
-
|
|
3543
|
-
|
|
3544
|
-
|
|
3545
|
-
|
|
3546
|
-
|
|
1933
|
+
const sourceOpinion = mkOpinion(
|
|
1934
|
+
args.opinion_b,
|
|
1935
|
+
args.opinion_d,
|
|
1936
|
+
args.opinion_u,
|
|
1937
|
+
args.opinion_a
|
|
1938
|
+
);
|
|
3547
1939
|
const sourceNode = await ctx.db.get(args.nodeId);
|
|
3548
1940
|
const sourceScope = await resolveNodeScopeForWorkspaceIsolation(
|
|
3549
1941
|
ctx,
|
|
@@ -3650,7 +2042,7 @@ mutation({
|
|
|
3650
2042
|
expectedBy: v.optional(v.number())
|
|
3651
2043
|
})
|
|
3652
2044
|
),
|
|
3653
|
-
baseRate: v.number(),
|
|
2045
|
+
baseRate: v.optional(v.number()),
|
|
3654
2046
|
metadata: v.optional(v.any())
|
|
3655
2047
|
// Additional metadata including isConditional
|
|
3656
2048
|
},
|
|
@@ -3681,7 +2073,7 @@ mutation({
|
|
|
3681
2073
|
);
|
|
3682
2074
|
}
|
|
3683
2075
|
const now = Date.now();
|
|
3684
|
-
const baseRate = assertBaseRateInRange(args.baseRate);
|
|
2076
|
+
const baseRate = assertBaseRateInRange(args.baseRate ?? 0.5);
|
|
3685
2077
|
const initialBeliefStatus = args.worktreeId ? "hypothesis" : "assumption";
|
|
3686
2078
|
const initialEpistemicStatus = args.worktreeId ? "hypothesis" : "assumption";
|
|
3687
2079
|
const seedOpinion = {
|
|
@@ -4006,7 +2398,15 @@ query({
|
|
|
4006
2398
|
projectId: args.projectId,
|
|
4007
2399
|
topicId: args.topicId
|
|
4008
2400
|
});
|
|
4009
|
-
} catch {
|
|
2401
|
+
} catch (error) {
|
|
2402
|
+
debugGraphPrimitiveFallback(
|
|
2403
|
+
"[epistemicBeliefs] Failed to resolve project scope",
|
|
2404
|
+
{
|
|
2405
|
+
error,
|
|
2406
|
+
projectId: args.projectId,
|
|
2407
|
+
topicId: args.topicId
|
|
2408
|
+
}
|
|
2409
|
+
);
|
|
4010
2410
|
return [];
|
|
4011
2411
|
}
|
|
4012
2412
|
if (args.userId) {
|
|
@@ -4050,7 +2450,9 @@ query({
|
|
|
4050
2450
|
handler: async (ctx, args) => {
|
|
4051
2451
|
const pageSize = clampBeliefLimit(args.limit);
|
|
4052
2452
|
const scanLimit = Math.min(pageSize * 3, MAX_PROJECT_BELIEF_LIMIT);
|
|
4053
|
-
const scope = await resolveTopicProjectScope(ctx, {
|
|
2453
|
+
const scope = await resolveTopicProjectScope(ctx, {
|
|
2454
|
+
topicId: args.topicId
|
|
2455
|
+
});
|
|
4054
2456
|
const query2 = ctx.db.query("epistemicNodes").withIndex(
|
|
4055
2457
|
"by_topic_type",
|
|
4056
2458
|
(q) => q.eq("topicId", args.topicId).eq("nodeType", "belief")
|
|
@@ -4255,8 +2657,8 @@ mutation({
|
|
|
4255
2657
|
// d: evidence AGAINST [0,1]
|
|
4256
2658
|
uncertainty: v.number(),
|
|
4257
2659
|
// u: lack of evidence [0,1]
|
|
4258
|
-
baseRate: v.
|
|
4259
|
-
// a: prior probability [0,1]
|
|
2660
|
+
baseRate: v.number(),
|
|
2661
|
+
// a: prior probability [0,1]
|
|
4260
2662
|
trigger: v.union(
|
|
4261
2663
|
v.literal("evidence_added"),
|
|
4262
2664
|
v.literal("evidence_removed"),
|
|
@@ -4276,16 +2678,18 @@ mutation({
|
|
|
4276
2678
|
rationale: v.optional(v.string()),
|
|
4277
2679
|
userId: v.string(),
|
|
4278
2680
|
// SL operator provenance (optional — defaults to manual_assessment)
|
|
4279
|
-
slOperator: v.optional(
|
|
4280
|
-
v.
|
|
4281
|
-
|
|
4282
|
-
|
|
4283
|
-
|
|
4284
|
-
|
|
4285
|
-
|
|
4286
|
-
|
|
4287
|
-
|
|
4288
|
-
|
|
2681
|
+
slOperator: v.optional(
|
|
2682
|
+
v.union(
|
|
2683
|
+
v.literal("cumulative_fusion"),
|
|
2684
|
+
v.literal("averaging_fusion"),
|
|
2685
|
+
v.literal("trust_discount"),
|
|
2686
|
+
v.literal("conditional_deduction"),
|
|
2687
|
+
v.literal("dependency_cascade"),
|
|
2688
|
+
v.literal("negation"),
|
|
2689
|
+
v.literal("constraint_fusion"),
|
|
2690
|
+
v.literal("manual_assessment")
|
|
2691
|
+
)
|
|
2692
|
+
),
|
|
4289
2693
|
triggeringEvidenceId: v.optional(v.id("epistemicNodes")),
|
|
4290
2694
|
triggeringWorktreeId: v.optional(v.string())
|
|
4291
2695
|
},
|
|
@@ -4516,10 +2920,7 @@ query({
|
|
|
4516
2920
|
if (!node || node.nodeType !== "belief") {
|
|
4517
2921
|
return [];
|
|
4518
2922
|
}
|
|
4519
|
-
return await ctx.db.query("beliefConfidence").withIndex(
|
|
4520
|
-
"by_beliefId_time",
|
|
4521
|
-
(q) => q.eq("beliefId", args.nodeId)
|
|
4522
|
-
).order("asc").collect();
|
|
2923
|
+
return await ctx.db.query("beliefConfidence").withIndex("by_beliefId_time", (q) => q.eq("beliefId", args.nodeId)).order("asc").collect();
|
|
4523
2924
|
}
|
|
4524
2925
|
});
|
|
4525
2926
|
query({
|
|
@@ -4823,7 +3224,7 @@ mutation({
|
|
|
4823
3224
|
const now = Date.now();
|
|
4824
3225
|
const evidenceNode = await ctx.db.get(args.insightId);
|
|
4825
3226
|
if (!evidenceNode || evidenceNode.nodeType !== "evidence") {
|
|
4826
|
-
return
|
|
3227
|
+
return buildBeliefEvidenceNotFoundResult();
|
|
4827
3228
|
}
|
|
4828
3229
|
if (!evidenceNode.projectId) {
|
|
4829
3230
|
throw new Error("Evidence has no project scope");
|
|
@@ -4864,7 +3265,7 @@ mutation({
|
|
|
4864
3265
|
projectId: evidenceNode.projectId
|
|
4865
3266
|
});
|
|
4866
3267
|
}
|
|
4867
|
-
return
|
|
3268
|
+
return buildBeliefStatusSuccessResult();
|
|
4868
3269
|
}
|
|
4869
3270
|
});
|
|
4870
3271
|
query({
|
|
@@ -4976,7 +3377,7 @@ mutation({
|
|
|
4976
3377
|
changedAt: now,
|
|
4977
3378
|
projectId: edge.projectId
|
|
4978
3379
|
});
|
|
4979
|
-
return
|
|
3380
|
+
return buildBeliefStatusSuccessResult();
|
|
4980
3381
|
}
|
|
4981
3382
|
});
|
|
4982
3383
|
mutation({
|
|
@@ -5105,13 +3506,23 @@ mutation({
|
|
|
5105
3506
|
changedAt: now,
|
|
5106
3507
|
projectId: node.projectId
|
|
5107
3508
|
});
|
|
5108
|
-
|
|
5109
|
-
|
|
5110
|
-
|
|
5111
|
-
|
|
5112
|
-
|
|
5113
|
-
} catch {
|
|
5114
|
-
|
|
3509
|
+
const result = {};
|
|
3510
|
+
result.beliefId = update.beliefId;
|
|
3511
|
+
result.nodeId = node._id;
|
|
3512
|
+
result.success = true;
|
|
3513
|
+
results.push(result);
|
|
3514
|
+
} catch (error) {
|
|
3515
|
+
debugGraphPrimitiveFallback(
|
|
3516
|
+
"[epistemicBeliefs] Failed to update belief criticality",
|
|
3517
|
+
{
|
|
3518
|
+
error,
|
|
3519
|
+
beliefId: update.beliefId
|
|
3520
|
+
}
|
|
3521
|
+
);
|
|
3522
|
+
const result = {};
|
|
3523
|
+
result.beliefId = update.beliefId;
|
|
3524
|
+
result.success = false;
|
|
3525
|
+
results.push(result);
|
|
5115
3526
|
}
|
|
5116
3527
|
}
|
|
5117
3528
|
return { results, updatedCount: results.filter((r) => r.success).length };
|
|
@@ -5226,7 +3637,15 @@ query({
|
|
|
5226
3637
|
projectId: args.projectId,
|
|
5227
3638
|
topicId: args.topicId
|
|
5228
3639
|
});
|
|
5229
|
-
} catch {
|
|
3640
|
+
} catch (error) {
|
|
3641
|
+
debugGraphPrimitiveFallback(
|
|
3642
|
+
"[epistemicBeliefs] Failed to resolve criticality scope",
|
|
3643
|
+
{
|
|
3644
|
+
error,
|
|
3645
|
+
projectId: args.projectId,
|
|
3646
|
+
topicId: args.topicId
|
|
3647
|
+
}
|
|
3648
|
+
);
|
|
5230
3649
|
return [];
|
|
5231
3650
|
}
|
|
5232
3651
|
const nodes = await ctx.db.query("epistemicNodes").withIndex(
|
|
@@ -5347,7 +3766,9 @@ internalQuery({
|
|
|
5347
3766
|
const pageSize = clampBeliefLimit(args.limit, 500);
|
|
5348
3767
|
const scanLimit = Math.min(pageSize * 3, MAX_PROJECT_BELIEF_LIMIT);
|
|
5349
3768
|
const audienceMode = args.audienceMode ?? "internal";
|
|
5350
|
-
const scope = await resolveTopicProjectScope(ctx, {
|
|
3769
|
+
const scope = await resolveTopicProjectScope(ctx, {
|
|
3770
|
+
topicId: args.topicId
|
|
3771
|
+
});
|
|
5351
3772
|
const registryRows = await listAudienceRegistryRows(ctx, {
|
|
5352
3773
|
tenantId: scope.tenantId,
|
|
5353
3774
|
workspaceId: scope.workspaceId
|
|
@@ -5407,7 +3828,7 @@ internalMutation({
|
|
|
5407
3828
|
args: {
|
|
5408
3829
|
...optionalBeliefScopeArgs,
|
|
5409
3830
|
formulation: v.string(),
|
|
5410
|
-
baseRate: v.number(),
|
|
3831
|
+
baseRate: v.optional(v.number()),
|
|
5411
3832
|
confidence: v.optional(
|
|
5412
3833
|
v.union(v.literal("high"), v.literal("medium"), v.literal("low"))
|
|
5413
3834
|
),
|
|
@@ -5443,7 +3864,7 @@ internalMutation({
|
|
|
5443
3864
|
returns: permissiveReturn,
|
|
5444
3865
|
handler: async (ctx, args) => {
|
|
5445
3866
|
const now = Date.now();
|
|
5446
|
-
const baseRate = assertBaseRateInRange(args.baseRate);
|
|
3867
|
+
const baseRate = assertBaseRateInRange(args.baseRate ?? 0.5);
|
|
5447
3868
|
const scope = await resolveTopicProjectScope(ctx, {
|
|
5448
3869
|
topicId: args.topicId,
|
|
5449
3870
|
projectId: args.projectId
|
|
@@ -5692,15 +4113,17 @@ internalMutation({
|
|
|
5692
4113
|
skippedHasHistory++;
|
|
5693
4114
|
continue;
|
|
5694
4115
|
}
|
|
5695
|
-
const
|
|
4116
|
+
const opinion = deriveSyntheticBackfillOpinion(
|
|
4117
|
+
node
|
|
4118
|
+
);
|
|
5696
4119
|
await ctx.db.insert(
|
|
5697
4120
|
"beliefConfidence",
|
|
5698
4121
|
buildBeliefConfidenceRow({
|
|
5699
4122
|
beliefId: node._id,
|
|
5700
|
-
belief:
|
|
5701
|
-
disbelief:
|
|
5702
|
-
uncertainty:
|
|
5703
|
-
baseRate:
|
|
4123
|
+
belief: opinion.b,
|
|
4124
|
+
disbelief: opinion.d,
|
|
4125
|
+
uncertainty: opinion.u,
|
|
4126
|
+
baseRate: opinion.a,
|
|
5704
4127
|
trigger: "backfill_synthetic",
|
|
5705
4128
|
rationale: "LK-6 backfill: synthesized t0 from node-level opinion fields (no prior beliefConfidence row found).",
|
|
5706
4129
|
assessedAt: readFiniteNumber(node.createdAt) ?? readFiniteNumber(node.updatedAt) ?? Date.now(),
|
|
@@ -5998,7 +4421,13 @@ query({
|
|
|
5998
4421
|
return {
|
|
5999
4422
|
positions: {},
|
|
6000
4423
|
hasClusters: false,
|
|
6001
|
-
counts: {
|
|
4424
|
+
counts: {
|
|
4425
|
+
raw: 0,
|
|
4426
|
+
hypothesis: 0,
|
|
4427
|
+
conditional: 0,
|
|
4428
|
+
unprocessed: 0,
|
|
4429
|
+
total: 0
|
|
4430
|
+
}
|
|
6002
4431
|
};
|
|
6003
4432
|
}
|
|
6004
4433
|
const allBeliefNodes = await getBeliefNodesForScope(ctx, scope);
|
|
@@ -6060,14 +4489,8 @@ mutation({
|
|
|
6060
4489
|
let connectedReassigned = 0;
|
|
6061
4490
|
const alreadyReassigned = /* @__PURE__ */ new Set();
|
|
6062
4491
|
for (const beliefId of movedBeliefIds) {
|
|
6063
|
-
const outbound = await ctx.db.query("epistemicEdges").withIndex(
|
|
6064
|
-
|
|
6065
|
-
(q) => q.eq("fromNodeId", beliefId)
|
|
6066
|
-
).collect();
|
|
6067
|
-
const inbound = await ctx.db.query("epistemicEdges").withIndex(
|
|
6068
|
-
"by_to",
|
|
6069
|
-
(q) => q.eq("toNodeId", beliefId)
|
|
6070
|
-
).collect();
|
|
4492
|
+
const outbound = await ctx.db.query("epistemicEdges").withIndex("by_from", (q) => q.eq("fromNodeId", beliefId)).collect();
|
|
4493
|
+
const inbound = await ctx.db.query("epistemicEdges").withIndex("by_to", (q) => q.eq("toNodeId", beliefId)).collect();
|
|
6071
4494
|
const connectedNodeIds = /* @__PURE__ */ new Set();
|
|
6072
4495
|
for (const edge of outbound) {
|
|
6073
4496
|
if (edge.toNodeId) connectedNodeIds.add(String(edge.toNodeId));
|
|
@@ -6090,7 +4513,14 @@ mutation({
|
|
|
6090
4513
|
alreadyReassigned.add(connectedId);
|
|
6091
4514
|
connectedReassigned++;
|
|
6092
4515
|
}
|
|
6093
|
-
} catch {
|
|
4516
|
+
} catch (error) {
|
|
4517
|
+
debugGraphPrimitiveFallback(
|
|
4518
|
+
"[epistemicBeliefs] Failed to reassign connected node",
|
|
4519
|
+
{
|
|
4520
|
+
error,
|
|
4521
|
+
connectedId
|
|
4522
|
+
}
|
|
4523
|
+
);
|
|
6094
4524
|
}
|
|
6095
4525
|
}
|
|
6096
4526
|
}
|
|
@@ -6169,7 +4599,14 @@ function parseJsonCandidate(value) {
|
|
|
6169
4599
|
}
|
|
6170
4600
|
try {
|
|
6171
4601
|
return JSON.parse(value);
|
|
6172
|
-
} catch {
|
|
4602
|
+
} catch (error) {
|
|
4603
|
+
debugGraphPrimitiveFallback(
|
|
4604
|
+
"[evaluators/shared] Failed to parse JSON candidate",
|
|
4605
|
+
{
|
|
4606
|
+
error,
|
|
4607
|
+
valueType: typeof value
|
|
4608
|
+
}
|
|
4609
|
+
);
|
|
6173
4610
|
return null;
|
|
6174
4611
|
}
|
|
6175
4612
|
}
|