@lucern/graph-primitives 0.1.0-alpha.4 → 0.3.0-alpha.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (78) hide show
  1. package/dist/beliefDecay.js +229 -1115
  2. package/dist/beliefDecay.js.map +1 -1
  3. package/dist/beliefEvidenceLinks.js +53 -834
  4. package/dist/beliefEvidenceLinks.js.map +1 -1
  5. package/dist/confidencePropagationDispatch.d.ts +3 -3
  6. package/dist/confidencePropagationDispatch.js +30 -308
  7. package/dist/confidencePropagationDispatch.js.map +1 -1
  8. package/dist/contradictions.js +5 -797
  9. package/dist/contradictions.js.map +1 -1
  10. package/dist/edges/contradicts.js +1 -122
  11. package/dist/edges/contradicts.js.map +1 -1
  12. package/dist/edges/dependsOn.js +14 -172
  13. package/dist/edges/dependsOn.js.map +1 -1
  14. package/dist/edges/elaborates.js +1 -49
  15. package/dist/edges/elaborates.js.map +1 -1
  16. package/dist/edges/index.js +14 -277
  17. package/dist/edges/index.js.map +1 -1
  18. package/dist/edges/informs.js +1 -62
  19. package/dist/edges/informs.js.map +1 -1
  20. package/dist/edges/propagationTypes.d.ts +2 -2
  21. package/dist/edges/propagationTypes.js.map +1 -1
  22. package/dist/edges/refutes.js +1 -62
  23. package/dist/edges/refutes.js.map +1 -1
  24. package/dist/edges/supports.js +1 -122
  25. package/dist/edges/supports.js.map +1 -1
  26. package/dist/edges/utils.d.ts +6 -6
  27. package/dist/edges/utils.js +1 -130
  28. package/dist/edges/utils.js.map +1 -1
  29. package/dist/entityBridge.js +2 -17
  30. package/dist/entityBridge.js.map +1 -1
  31. package/dist/entityLifecycle.js +62 -848
  32. package/dist/entityLifecycle.js.map +1 -1
  33. package/dist/epistemicAnswers.js +27 -838
  34. package/dist/epistemicAnswers.js.map +1 -1
  35. package/dist/epistemicBeliefs.js +186 -2214
  36. package/dist/epistemicBeliefs.js.map +1 -1
  37. package/dist/epistemicContractHelpers.js +1 -318
  38. package/dist/epistemicContractHelpers.js.map +1 -1
  39. package/dist/epistemicContracts.js +163 -2467
  40. package/dist/epistemicContracts.js.map +1 -1
  41. package/dist/epistemicEdges.js +60 -863
  42. package/dist/epistemicEdges.js.map +1 -1
  43. package/dist/epistemicEvidence.js +116 -1647
  44. package/dist/epistemicEvidence.js.map +1 -1
  45. package/dist/epistemicHelpers.js +3 -2
  46. package/dist/epistemicHelpers.js.map +1 -1
  47. package/dist/epistemicLinking.js +2 -785
  48. package/dist/epistemicLinking.js.map +1 -1
  49. package/dist/epistemicNodes.js +34 -1427
  50. package/dist/epistemicNodes.js.map +1 -1
  51. package/dist/epistemicQuestions.js +88 -1637
  52. package/dist/epistemicQuestions.js.map +1 -1
  53. package/dist/epistemicSources.js +28 -1421
  54. package/dist/epistemicSources.js.map +1 -1
  55. package/dist/evaluators/index.js +163 -2467
  56. package/dist/evaluators/index.js.map +1 -1
  57. package/dist/index.js +486 -3649
  58. package/dist/index.js.map +1 -1
  59. package/dist/ontology-matching.js +1 -344
  60. package/dist/ontology-matching.js.map +1 -1
  61. package/dist/ontologyApproval.js +1 -13
  62. package/dist/ontologyApproval.js.map +1 -1
  63. package/dist/ontologyDefinitions.js +2 -17
  64. package/dist/ontologyDefinitions.js.map +1 -1
  65. package/dist/ontologyRegistry.js +2 -17
  66. package/dist/ontologyRegistry.js.map +1 -1
  67. package/dist/projectionReconciliation.js +2 -17
  68. package/dist/projectionReconciliation.js.map +1 -1
  69. package/dist/questionEvidenceLinks.js +242 -837
  70. package/dist/questionEvidenceLinks.js.map +1 -1
  71. package/dist/text-matching.js +1 -244
  72. package/dist/text-matching.js.map +1 -1
  73. package/dist/workflowBridge.d.ts +27 -0
  74. package/dist/workflowBridge.js +303 -0
  75. package/dist/workflowBridge.js.map +1 -0
  76. package/dist/workspaceIsolation.js +8 -609
  77. package/dist/workspaceIsolation.js.map +1 -1
  78. package/package.json +6 -6
@@ -1,1749 +1,22 @@
1
1
  import { v } from 'convex/values';
2
- import { componentsGeneric, defineTable, internalMutationGeneric, mutationGeneric, anyApi, queryGeneric, internalQueryGeneric } from 'convex/server';
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, internalMutationGeneric, mutationGeneric, anyApi, 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
- // ../access-control/src/topicProjectOverlay.ts
9
- var LEGACY_SCOPE_FIELD = "graphScopeProjectId";
10
- function readNonEmptyString(value) {
11
- if (typeof value !== "string") {
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
- componentsGeneric();
1581
- var internal2 = anyApi;
1582
-
1583
- // ../schema-management/src/enumValidation.ts
1584
- var BUILTIN_ENUM_FALLBACK = {
1585
- topic_type: /* @__PURE__ */ new Set([
1586
- "domain",
1587
- "theme",
1588
- "deal",
1589
- "strategy",
1590
- "constitution",
1591
- "project",
1592
- "portfolio",
1593
- "architecture",
1594
- "capability",
1595
- "runtime",
1596
- "interface",
1597
- "governance",
1598
- "operations",
1599
- "security",
1600
- "data"
1601
- ]),
1602
- branch_schema: /* @__PURE__ */ new Set(["pillar", "track", "dimension", "axis", "phase"]),
1603
- lens_perspective_type: /* @__PURE__ */ new Set([
1604
- "investigation",
1605
- "monitoring",
1606
- "analysis",
1607
- "comparison",
1608
- "taxonomy"
1609
- ]),
1610
- belief_type: /* @__PURE__ */ new Set([
1611
- "belief",
1612
- "hypothesis",
1613
- "principle",
1614
- "invariant",
1615
- "assumption",
1616
- "tenet",
1617
- "prior",
1618
- "preference",
1619
- "goal",
1620
- "forecast",
1621
- "decision",
1622
- "constraint",
1623
- "tradeoff",
1624
- "policy",
1625
- "implementation_choice",
1626
- // Coding intelligence domain
1627
- "implementation_decision",
1628
- "interface_contract",
1629
- "migration_state",
1630
- "code_pattern",
1631
- "deprecation_notice"
1632
- ]),
1633
- edge_type: /* @__PURE__ */ new Set([
1634
- // === 6 CANONICAL EPISTEMIC TYPES ===
1635
- "supports",
1636
- // L3↔L3: belief bears on belief (weight -1 to +1)
1637
- "informs",
1638
- // L2→L3: evidence bears on belief
1639
- "depends_on",
1640
- // L3→L3, Q→Q: structural gate
1641
- "derived_from",
1642
- // Any→Any: provenance chain
1643
- "contains",
1644
- // Any→Any: hierarchy, scoping, membership
1645
- "tests",
1646
- // Q→L3: question interrogates belief
1647
- // === STRUCTURAL / LIFECYCLE ===
1648
- "supersedes",
1649
- "responds_to",
1650
- "belongs_to",
1651
- "relates_to_thesis",
1652
- // === ONTOLOGICAL (tenant-extensible) ===
1653
- "works_at",
1654
- "invested_in",
1655
- "competes_with",
1656
- "participates_in",
1657
- "founded_by",
1658
- "evaluates",
1659
- "performs",
1660
- "function_in",
1661
- "impacts",
1662
- "raised_from",
1663
- "mentioned_in",
1664
- "perspective_on",
1665
- "plays_theme"
1666
- ]),
1667
- worktree_type: /* @__PURE__ */ new Set([
1668
- "belief_test",
1669
- "existential",
1670
- "contradiction",
1671
- "refinement",
1672
- "coverage",
1673
- "discovery",
1674
- "clarification",
1675
- "confirmation"
1676
- ]),
1677
- worktree_phase: /* @__PURE__ */ new Set([
1678
- "cluster_mapping",
1679
- "hypothesis_formation",
1680
- "question_generation",
1681
- "evidence_collection",
1682
- "synthesis",
1683
- "decision",
1684
- "retrospective"
1685
- ]),
1686
- activity_type: /* @__PURE__ */ new Set([
1687
- "create",
1688
- "update",
1689
- "review",
1690
- "merge",
1691
- "archive",
1692
- "comment",
1693
- "status_change",
1694
- "evidence_added",
1695
- "question_added"
1696
- ])
1697
- };
1698
- function normalizeEnumValue(value) {
1699
- return value.trim().toLowerCase();
1700
- }
1701
- async function validateSchemaEnumValue(ctx, args) {
1702
- const normalized = normalizeEnumValue(args.value);
1703
- if (!normalized) {
1704
- return { valid: false, source: "none" };
1705
- }
1706
- try {
1707
- const validFromSchema = await ctx.runQuery(
1708
- internal2.schemaConfig.internalValidate,
1709
- {
1710
- category: args.category,
1711
- value: normalized,
1712
- tenantId: args.tenantId
1713
- }
1714
- );
1715
- if (validFromSchema) {
1716
- return { valid: true, source: "schema" };
1717
- }
1718
- } catch {
1719
- }
1720
- const fallback = BUILTIN_ENUM_FALLBACK[args.category];
1721
- if (fallback.has(normalized)) {
1722
- return { valid: true, source: "builtin" };
1723
- }
1724
- return { valid: false, source: "none" };
1725
- }
1726
- async function assertSchemaEnumValue(ctx, args) {
1727
- if (typeof args.value !== "string") {
1728
- return;
1729
- }
1730
- const normalized = normalizeEnumValue(args.value);
1731
- if (!normalized) {
1732
- return;
1733
- }
1734
- const validation = await validateSchemaEnumValue(ctx, {
1735
- category: args.category,
1736
- value: normalized,
1737
- tenantId: args.tenantId
1738
- });
1739
- if (!validation.valid) {
1740
- const tenantHint = args.tenantId ? ` for tenant ${args.tenantId}` : "";
1741
- throw new Error(
1742
- `[${args.context}] Invalid value "${normalized}" for category "${args.category}"${tenantHint}. Add it to schemaEnumConfig before use.`
1743
- );
1744
- }
1745
- return normalized;
1746
- }
15
+ var internal = anyApi;
16
+ var internalMutation = internalMutationGeneric;
17
+ var internalQuery = internalQueryGeneric;
18
+ var mutation = mutationGeneric;
19
+ var query = queryGeneric;
1747
20
 
1748
21
  // src/beliefLifecycle.ts
1749
22
  var BELIEF_STATUS_VALUES = [
@@ -1761,30 +34,30 @@ var RESOLVED_PREDICTION_OUTCOMES = [
1761
34
  function isBeliefLifecycleStatus(value) {
1762
35
  return typeof value === "string" && BELIEF_STATUS_VALUES.includes(value);
1763
36
  }
1764
- function normalizeBeliefConfidence(confidence2) {
1765
- if (typeof confidence2 !== "number" || !Number.isFinite(confidence2)) {
37
+ function normalizeBeliefConfidence(confidence) {
38
+ if (typeof confidence !== "number" || !Number.isFinite(confidence)) {
1766
39
  return null;
1767
40
  }
1768
- if (confidence2 >= 0 && confidence2 <= 1) {
1769
- return confidence2;
41
+ if (confidence >= 0 && confidence <= 1) {
42
+ return confidence;
1770
43
  }
1771
- if (confidence2 > 1 && confidence2 <= 100) {
1772
- return confidence2 / 100;
44
+ if (confidence > 1 && confidence <= 100) {
45
+ return confidence / 100;
1773
46
  }
1774
47
  return null;
1775
48
  }
1776
- function isResolvedByConfidence(confidence2) {
1777
- const normalized = normalizeBeliefConfidence(confidence2);
49
+ function isResolvedByConfidence(confidence) {
50
+ const normalized = normalizeBeliefConfidence(confidence);
1778
51
  if (normalized === null) {
1779
52
  return false;
1780
53
  }
1781
54
  return normalized <= 0 || normalized >= 1;
1782
55
  }
1783
- function hasResolvedPredictionOutcome(predictionMeta2) {
1784
- if (!predictionMeta2 || typeof predictionMeta2 !== "object") {
56
+ function hasResolvedPredictionOutcome(predictionMeta) {
57
+ if (!predictionMeta || typeof predictionMeta !== "object") {
1785
58
  return false;
1786
59
  }
1787
- const outcome = predictionMeta2.outcome;
60
+ const outcome = predictionMeta.outcome;
1788
61
  return typeof outcome === "string" && RESOLVED_PREDICTION_OUTCOMES.includes(outcome);
1789
62
  }
1790
63
  function getPredictionMetaFromMetadata(metadata) {
@@ -1847,8 +120,6 @@ var containsPropagationSpec = {
1847
120
  operator: () => null,
1848
121
  description: "Structural containment only. Traversed for explicit semantics, but it never propagates opinions."
1849
122
  };
1850
-
1851
- // src/edges/utils.ts
1852
123
  function readEdgeMetadata(edge) {
1853
124
  return {
1854
125
  constraint: edge.constraint ?? void 0,
@@ -1925,8 +196,6 @@ var contradictsPropagationSpec = {
1925
196
  },
1926
197
  description: "Legacy contradiction edges move negative pressure in either direction, but never beyond one hop."
1927
198
  };
1928
-
1929
- // src/edges/dependsOn.ts
1930
199
  var dependsOnPropagationSpec = {
1931
200
  edgeType: "depends_on",
1932
201
  direction: "incoming",
@@ -1942,8 +211,18 @@ var dependsOnPropagationSpec = {
1942
211
  if (metadata.conditionalA && metadata.conditionalNotA) {
1943
212
  const deducedOpinion = conditionalDeduction(
1944
213
  dampedSource,
1945
- metadata.conditionalA,
1946
- metadata.conditionalNotA,
214
+ mkOpinion(
215
+ metadata.conditionalA.b,
216
+ metadata.conditionalA.d,
217
+ metadata.conditionalA.u,
218
+ metadata.conditionalA.a
219
+ ),
220
+ mkOpinion(
221
+ metadata.conditionalNotA.b,
222
+ metadata.conditionalNotA.d,
223
+ metadata.conditionalNotA.u,
224
+ metadata.conditionalNotA.a
225
+ ),
1947
226
  targetOpinion.a
1948
227
  );
1949
228
  return annotateRationale(
@@ -2115,606 +394,17 @@ function getTraversalDirections(direction) {
2115
394
  }
2116
395
  return ["outgoing", "incoming"];
2117
396
  }
2118
- v.number();
2119
- v.union(
2120
- v.literal("very_high"),
2121
- // 0.9+
2122
- v.literal("high"),
2123
- // 0.7-0.9
2124
- v.literal("medium"),
2125
- // 0.4-0.7
2126
- v.literal("low"),
2127
- // 0.2-0.4
2128
- v.literal("very_low")
2129
- // 0-0.2
2130
- );
2131
- v.union(
2132
- v.literal(1),
2133
- // Critical
2134
- v.literal(2),
2135
- // High
2136
- v.literal(3),
2137
- // Medium
2138
- v.literal(4),
2139
- // Low
2140
- v.literal(5)
2141
- // Backlog
2142
- );
2143
- v.union(
2144
- v.literal("critical"),
2145
- v.literal("high"),
2146
- v.literal("medium"),
2147
- v.literal("low"),
2148
- v.literal("backlog")
2149
- );
2150
- v.union(
2151
- v.literal("active"),
2152
- v.literal("paused"),
2153
- v.literal("completed"),
2154
- v.literal("archived")
2155
- );
2156
- v.union(
2157
- v.literal("pending"),
2158
- v.literal("processing"),
2159
- v.literal("completed"),
2160
- v.literal("failed")
2161
- );
2162
- v.object({
2163
- crunchbaseId: v.optional(v.string()),
2164
- linkedinUrl: v.optional(v.string()),
2165
- pitchbookId: v.optional(v.string()),
2166
- twitterUrl: v.optional(v.string()),
2167
- domain: v.optional(v.string())
2168
- });
2169
- var sourceType = v.union(
2170
- v.literal("proprietary"),
2171
- // Internal Stack research
2172
- v.literal("primary"),
2173
- // Direct interviews, calls
2174
- v.literal("secondary"),
2175
- // Published sources
2176
- v.literal("ai_generated"),
2177
- // AI-synthesized
2178
- v.literal("user_input"),
2179
- // Manual user entry
2180
- v.literal("inferred")
2181
- // System inference
2182
- );
2183
- v.object({
2184
- sourceType: v.optional(sourceType),
2185
- sourceId: v.optional(v.string()),
2186
- // Reference to source entity
2187
- sourceUrl: v.optional(v.string()),
2188
- sourceDate: v.optional(v.number()),
2189
- sourceName: v.optional(v.string())
2190
- });
2191
- v.object({
2192
- cursor: v.optional(v.string()),
2193
- limit: v.optional(v.number())
2194
- });
2195
- v.object({
2196
- hasMore: v.boolean(),
2197
- nextCursor: v.optional(v.string()),
2198
- totalCount: v.optional(v.number())
2199
- });
2200
- var richTextContent = v.object({
2201
- type: v.literal("doc"),
2202
- content: looseJsonArray
2203
- });
2204
- v.union(v.string(), richTextContent);
2205
- v.object({
2206
- promptTokens: v.optional(v.number()),
2207
- completionTokens: v.optional(v.number()),
2208
- totalTokens: v.optional(v.number())
2209
- });
2210
- v.object({
2211
- fileName: v.optional(v.string()),
2212
- fileSize: v.optional(v.number()),
2213
- mimeType: v.optional(v.string()),
2214
- storageId: v.optional(v.id("_storage")),
2215
- externalUrl: v.optional(v.string())
2216
- });
2217
-
2218
- // ../schema-management/src/spine/tables/epistemicNodes.ts
2219
- var nodeType = v.union(
2220
- // --- L4: Audit Targets (decisions, outcomes) ---
2221
- v.literal("decision"),
2222
- // Investment decision with knowledge horizon snapshot
2223
- // --- L3: Traversal Anchors (epistemic structure) ---
2224
- v.literal("belief"),
2225
- // Structured conviction (immutable formulation)
2226
- v.literal("question"),
2227
- // Unit of uncertainty
2228
- v.literal("theme"),
2229
- // Investment thesis / conviction cluster
2230
- v.literal("deal"),
2231
- // Investment evaluation process
2232
- v.literal("topic"),
2233
- // Hierarchical knowledge container
2234
- // --- L2: Compression Boundary (minimum reasoning unit) ---
2235
- v.literal("claim"),
2236
- // Atomic assertion that can be true/false
2237
- v.literal("evidence"),
2238
- // Interpreted signal linked to beliefs
2239
- v.literal("synthesis"),
2240
- // Primers, deep research
2241
- v.literal("answer"),
2242
- // Immutable answer snapshot for a question
2243
- // --- L1: Terminal Leaves (non-traversable, grounding) ---
2244
- v.literal("atomic_fact"),
2245
- // Raw fact from source (not interpreted)
2246
- v.literal("excerpt"),
2247
- // Direct quote from source document
2248
- v.literal("source"),
2249
- // News, documents, transcripts
2250
- // --- Ontological Entities (things in the world) ---
2251
- v.literal("company"),
2252
- // Organization (subtype: private, corporate, portfolio)
2253
- v.literal("person"),
2254
- // Individual (founder, expert, LP, contact)
2255
- v.literal("investor"),
2256
- // Investment entity (subtype: vc, lp, cvc, pe, family_office, angel)
2257
- v.literal("function"),
2258
- // What a company does (from classifier)
2259
- v.literal("value_chain")
2260
- // Market structure / value flow
2261
- );
2262
- var epistemicLayer = v.union(
2263
- v.literal("L4"),
2264
- // Decisions, outcomes - audit targets
2265
- v.literal("L3"),
2266
- // Beliefs, questions, themes - traversal anchors
2267
- v.literal("L2"),
2268
- // Claims, evidence, synthesis - compression boundary
2269
- v.literal("L1"),
2270
- // Atomic facts, excerpts, sources - terminal leaves
2271
- v.literal("ontological"),
2272
- // Companies, people, etc - not epistemic
2273
- v.literal("organizational")
2274
- // Topics, lenses, worktrees — structural containers
2275
- );
2276
- var nodeStatus = v.union(
2277
- v.literal("active"),
2278
- v.literal("superseded"),
2279
- // Replaced by newer version
2280
- v.literal("archived"),
2281
- v.literal("deleted")
2282
- );
2283
- var sourceType2 = v.union(
2284
- v.literal("human"),
2285
- // User created directly
2286
- v.literal("ai_extracted"),
2287
- // LLM extracted from a source
2288
- v.literal("ai_generated"),
2289
- // LLM synthesized/created
2290
- v.literal("imported"),
2291
- // External system import
2292
- v.literal("system"),
2293
- // System-generated (migrations, classifiers)
2294
- v.literal("verified"),
2295
- // Human-verified source
2296
- v.literal("proprietary")
2297
- // Proprietary/internal data
2298
- );
2299
- var verificationStatus = v.union(
2300
- v.literal("unverified"),
2301
- v.literal("human_verified"),
2302
- v.literal("ai_verified"),
2303
- v.literal("contradicted"),
2304
- v.literal("outdated")
2305
- );
2306
- var syncStatus = v.union(
2307
- v.literal("synced"),
2308
- // Node and edges fully synced to Neo4j
2309
- v.literal("pending_edges"),
2310
- // Node created, edges being created
2311
- v.literal("edge_creation_failed")
2312
- // Edge creation failed, needs retry
2313
- );
2314
- var audienceLabel = v.string();
2315
- var sensitivityTier = v.union(
2316
- v.literal("low"),
2317
- v.literal("medium"),
2318
- v.literal("high"),
2319
- v.literal("restricted")
2320
- );
2321
- var exportClass = v.union(
2322
- v.literal("internal_only"),
2323
- v.literal("client_safe"),
2324
- v.literal("public_safe"),
2325
- v.literal("restricted")
2326
- );
2327
- var anonymizationClass = v.union(
2328
- v.literal("none"),
2329
- v.literal("standard"),
2330
- v.literal("strict")
2331
- );
2332
- var epistemicStatus = v.union(
2333
- v.literal("hypothesis"),
2334
- // Initial conjecture, low evidence
2335
- v.literal("emerging"),
2336
- // Building evidence, gaining traction
2337
- v.literal("established"),
2338
- // Well-evidenced, core to thesis
2339
- v.literal("challenged"),
2340
- // Contradicting evidence appeared
2341
- v.literal("assumption"),
2342
- // Taken as given, not actively tested
2343
- v.literal("deprecated")
2344
- // Superseded or abandoned
2345
- );
2346
- var beliefStatus = v.union(
2347
- v.literal("assumption"),
2348
- v.literal("hypothesis"),
2349
- v.literal("belief"),
2350
- v.literal("fact")
2351
- );
2352
- var reversibility = v.union(
2353
- v.literal("irreversible"),
2354
- // One-way door decision
2355
- v.literal("hard_to_reverse"),
2356
- // Significant cost to undo
2357
- v.literal("reversible"),
2358
- // Can change course with moderate effort
2359
- v.literal("trivial")
2360
- // Easy to adjust
2361
- );
2362
- var predictionOutcome = v.union(
2363
- v.literal("pending"),
2364
- v.literal("confirmed"),
2365
- v.literal("disconfirmed"),
2366
- v.literal("partial"),
2367
- v.literal("expired")
2368
- );
2369
- var predictionMeta = v.object({
2370
- isPrediction: v.boolean(),
2371
- registeredAt: v.number(),
2372
- // When prediction was made
2373
- expectedBy: v.optional(v.number()),
2374
- // When we expect resolution
2375
- outcome: v.optional(predictionOutcome),
2376
- outcomeRecordedAt: v.optional(v.number()),
2377
- outcomeEvidenceId: v.optional(v.string()),
2378
- // globalId of confirming evidence
2379
- confidenceAtPrediction: v.optional(v.number()),
2380
- // 0-1
2381
- actualVsPredicted: v.optional(v.string())
2382
- // Notes on how outcome compared
2383
- });
2384
- var methodology = v.union(
2385
- // Primary Research (high value)
2386
- v.literal("primary_research"),
2387
- // Direct investigation
2388
- v.literal("expert_interview"),
2389
- // Expert call/interview
2390
- v.literal("customer_interview"),
2391
- // Customer research
2392
- v.literal("field_observation"),
2393
- // On-site observation
2394
- v.literal("proprietary_data"),
2395
- // Internal data analysis
2396
- // Secondary Research
2397
- v.literal("desk_research"),
2398
- // Public sources
2399
- v.literal("regulatory_filing"),
2400
- // SEC, regulatory docs
2401
- v.literal("news_article"),
2402
- // News/press
2403
- v.literal("academic_paper"),
2404
- // Academic research
2405
- // AI-Assisted
2406
- v.literal("ai_synthesis"),
2407
- // AI-generated synthesis
2408
- v.literal("ai_extraction")
2409
- // AI-extracted from source
2410
- );
2411
- var informationAsymmetry = v.union(
2412
- v.literal("proprietary"),
2413
- // Only we have this
2414
- v.literal("early"),
2415
- // We're early but others will get it
2416
- v.literal("common")
2417
- // Everyone has access
2418
- );
2419
- var temporalNature = v.union(
2420
- v.literal("factual"),
2421
- // Resolved outcome. Grounded in reality.
2422
- v.literal("forecast"),
2423
- // Prediction. Will resolve. Discounted weight.
2424
- v.literal("unknown")
2425
- // Not yet classified.
2426
- );
2427
- var questionType = v.union(
2428
- v.literal("validation"),
2429
- // Does evidence support this belief?
2430
- v.literal("falsification"),
2431
- // What would prove this belief wrong?
2432
- v.literal("assumption_probe"),
2433
- // Is this unstated assumption true?
2434
- v.literal("prediction_test"),
2435
- // Will this predicted outcome occur?
2436
- v.literal("counterfactual"),
2437
- // What would we expect if X were false?
2438
- v.literal("discovery"),
2439
- // What don't we know yet?
2440
- v.literal("clarification"),
2441
- // What does X actually mean?
2442
- v.literal("comparison"),
2443
- // How does X compare to Y?
2444
- v.literal("causal"),
2445
- // What caused X?
2446
- v.literal("mechanism"),
2447
- // How does X work?
2448
- v.literal("general")
2449
- // Unclassified
2450
- );
2451
- var questionPriority = v.union(
2452
- v.literal("critical"),
2453
- // Blocks decision-making
2454
- v.literal("high"),
2455
- // Important for thesis
2456
- v.literal("medium"),
2457
- // Would be nice to know
2458
- v.literal("low")
2459
- // Background/curiosity
2460
- );
2461
- var answerQuality = v.union(
2462
- v.literal("definitive"),
2463
- // Clear, well-supported
2464
- v.literal("strong"),
2465
- // Good evidence, high confidence
2466
- v.literal("moderate"),
2467
- // Some evidence
2468
- v.literal("weak"),
2469
- // Limited evidence
2470
- v.literal("speculative"),
2471
- // Mostly conjecture
2472
- v.literal("unanswered")
2473
- // No answer yet
2474
- );
2475
- var consensusView = v.union(
2476
- v.literal("aligned"),
2477
- // We agree with market consensus
2478
- v.literal("ahead_of"),
2479
- // We see this before consensus does
2480
- v.literal("contrarian"),
2481
- // We actively disagree with consensus
2482
- v.literal("orthogonal"),
2483
- // We're looking at something consensus isn't discussing
2484
- v.literal("unknown")
2485
- // We don't know what consensus thinks
2486
- );
2487
- var themeConviction = v.union(
2488
- v.literal("high"),
2489
- // Strong conviction, actively deploying
2490
- v.literal("medium"),
2491
- // Building conviction
2492
- v.literal("low"),
2493
- // Exploring, not convicted
2494
- v.literal("negative")
2495
- // Actively avoiding
2496
- );
2497
- var decisionType = v.union(
2498
- v.literal("invest"),
2499
- v.literal("pass"),
2500
- v.literal("follow_on"),
2501
- v.literal("exit"),
2502
- v.literal("deep_dive"),
2503
- v.literal("monitor"),
2504
- v.literal("deprioritize"),
2505
- v.literal("thesis_adopt"),
2506
- v.literal("thesis_revise"),
2507
- v.literal("thesis_abandon")
2508
- );
2509
- var decisionOutcome = v.union(
2510
- v.literal("pending"),
2511
- v.literal("successful"),
2512
- v.literal("unsuccessful"),
2513
- v.literal("mixed"),
2514
- v.literal("unknown")
2515
- );
2516
- var externalIds2 = v.object({
2517
- crunchbase: v.optional(v.string()),
2518
- linkedin: v.optional(v.string()),
2519
- pitchbook: v.optional(v.string()),
2520
- twitter: v.optional(v.string()),
2521
- website: v.optional(v.string())
2522
- });
2523
- defineTable({
2524
- // === IDENTITY ===
2525
- globalId: v.string(),
2526
- // UUID - survives migration to Neo4j
2527
- // === TYPE ===
2528
- nodeType,
2529
- // === EPISTEMIC LAYER ===
2530
- epistemicLayer: v.optional(epistemicLayer),
2531
- // === SUBTYPE (for typed entities) ===
2532
- subtype: v.optional(v.string()),
2533
- // company: private|corporate|portfolio, investor: vc|lp|cvc|pe|family_office|angel
2534
- // === CONTENT ===
2535
- canonicalText: v.string(),
2536
- // The core content (belief statement, company name, etc.)
2537
- contentHash: v.string(),
2538
- // SHA256(nodeType + canonicalText) for deduplication
2539
- // Extended content (for sources/syntheses)
2540
- content: v.optional(v.string()),
2541
- // Full text for documents/articles
2542
- contentType: v.optional(v.string()),
2543
- // "markdown", "html", "pdf", "text"
2544
- // === METADATA ===
2545
- title: v.optional(v.string()),
2546
- // Display title
2547
- tags: v.optional(v.array(v.string())),
2548
- domain: v.optional(v.string()),
2549
- // For companies: website domain
2550
- // Type-specific metadata (flexible object - LEGACY)
2551
- // New code should use the typed fields below when available
2552
- metadata: v.optional(looseJsonObject),
2553
- // === POLICY / ENTITLEMENT ===
2554
- tenantId: v.optional(v.string()),
2555
- workspaceId: v.optional(v.string()),
2556
- ownerPrincipalId: v.optional(v.string()),
2557
- audienceLabel: v.optional(audienceLabel),
2558
- policyTags: v.optional(v.array(v.string())),
2559
- sensitivityTier: v.optional(sensitivityTier),
2560
- exportClass: v.optional(exportClass),
2561
- anonymizationClass: v.optional(anonymizationClass),
2562
- // === PUBLICATION (visibility-based, not copy-based) ===
2563
- // Publication expands who can see a workspace-local node — the node stays
2564
- // in its workspace, like a microservice exposing part of its API surface.
2565
- // Rules-based: pack/tenant-level publicationRules auto-evaluate on
2566
- // confidence changes and node creation. No manual click-by-click.
2567
- publicationStatus: v.optional(
2568
- v.union(
2569
- v.literal("unpublished"),
2570
- // Default: workspace-local only
2571
- v.literal("published"),
2572
- // Visible at tenant scope (rules matched)
2573
- v.literal("suppressed")
2574
- // Manually blocked even if rules match
2575
- )
2576
- ),
2577
- publishedAt: v.optional(v.number()),
2578
- // When publication status last changed to published
2579
- publishedBy: v.optional(v.string()),
2580
- // userId or "system:publication_rules" for auto-publish
2581
- // === TYPED METADATA FIELDS ===
2582
- // --- Belief ---
2583
- // Belief type — validated against schemaEnumConfig category "belief_type"
2584
- // Platform core: hypothesis, belief, principle, invariant, assumption,
2585
- // tenet, prior, preference, goal, forecast
2586
- beliefType: v.optional(v.string()),
2587
- beliefStatus: v.optional(beliefStatus),
2588
- epistemicStatus: v.optional(epistemicStatus),
2589
- reversibility: v.optional(reversibility),
2590
- predictionMeta: v.optional(predictionMeta),
2591
- // Consensus tracking (for non-consensus detection)
2592
- consensusView: v.optional(consensusView),
2593
- consensusConfidence: v.optional(v.number()),
2594
- // 0-1: What we think consensus confidence is
2595
- consensusSource: v.optional(v.string()),
2596
- // Where we got the consensus view (twitter, reports, etc.)
2597
- // --- Evidence ---
2598
- methodology: v.optional(methodology),
2599
- informationAsymmetry: v.optional(informationAsymmetry),
2600
- temporalNature: v.optional(temporalNature),
2601
- // --- Question ---
2602
- questionType: v.optional(questionType),
2603
- questionPriority: v.optional(questionPriority),
2604
- answerQuality: v.optional(answerQuality),
2605
- // --- Theme ---
2606
- themeConviction: v.optional(themeConviction),
2607
- // Market timing (for "early on theme" detection)
2608
- marketAwarenessDate: v.optional(v.number()),
2609
- // When this theme became broadly discussed
2610
- marketAwarenessSource: v.optional(v.string()),
2611
- // How we know (first major report, twitter volume spike, etc.)
2612
- earlySignalIds: v.optional(v.array(v.string())),
2613
- // globalIds of evidence we had before market awareness
2614
- // --- Decision ---
2615
- decisionType: v.optional(decisionType),
2616
- decisionOutcome: v.optional(decisionOutcome),
2617
- // === EXTERNAL IDS (for ontological entities) ===
2618
- externalIds: v.optional(externalIds2),
2619
- // === PROVENANCE ===
2620
- sourceType: sourceType2,
2621
- aiProvider: v.optional(v.string()),
2622
- // "claude", "gemini", "gpt-4", etc.
2623
- extractedFromNodeId: v.optional(v.id("epistemicNodes")),
2624
- // Quick reference to source
2625
- // === EXTRACTION CONTEXT ===
2626
- extractionModel: v.optional(v.string()),
2627
- // "claude-sonnet-4-20250514"
2628
- extractionPromptName: v.optional(v.string()),
2629
- // "lucern/extract-evidence"
2630
- extractionPromptVersion: v.optional(v.number()),
2631
- extractionTemperature: v.optional(v.number()),
2632
- extractionLangfuseTraceId: v.optional(v.string()),
2633
- // === GROUNDING VERIFICATION ===
2634
- groundingVerified: v.optional(v.boolean()),
2635
- groundingConfidence: v.optional(v.number()),
2636
- // 0-1 match quality
2637
- groundingMatchedText: v.optional(v.string()),
2638
- // Actual text from source
2639
- groundingStartOffset: v.optional(v.number()),
2640
- groundingEndOffset: v.optional(v.number()),
2641
- groundingRejectionReason: v.optional(v.string()),
2642
- // === CONFIDENCE & VERIFICATION ===
2643
- confidence: v.optional(v.number()),
2644
- // 0-1 projected probability P(x) = b + a*u
2645
- verificationStatus: v.optional(verificationStatus),
2646
- // === SL OPINION (Subjective Logic — Kernel v2) ===
2647
- // Replaces scalar confidence with rich epistemic state.
2648
- // b + d + u = 1. P(x) = b + a*u is stored in `confidence` for backward compat.
2649
- opinion_b: v.optional(v.number()),
2650
- // Belief: evidence FOR (0-1)
2651
- opinion_d: v.optional(v.number()),
2652
- // Disbelief: evidence AGAINST (0-1)
2653
- opinion_u: v.optional(v.number()),
2654
- // Uncertainty: absence of evidence (0-1)
2655
- opinion_a: v.optional(v.number()),
2656
- // Base rate / prior probability (0-1)
2657
- tupleContradicted: v.optional(v.boolean()),
2658
- // Single-belief tuple-space contradiction flag
2659
- // === LIFECYCLE ===
2660
- status: nodeStatus,
2661
- supersededBy: v.optional(v.id("epistemicNodes")),
2662
- // === OWNERSHIP ===
2663
- topicId: v.optional(v.string()),
2664
- // Canonical scope container (topic-first model)
2665
- projectId: v.optional(v.string()),
2666
- // DEPRECATED: Use belongs_to edges
2667
- createdBy: v.string(),
2668
- // Clerk user ID
2669
- createdAt: v.number(),
2670
- updatedAt: v.number(),
2671
- // === NEO4J SYNC STATUS ===
2672
- syncStatus: v.optional(syncStatus),
2673
- syncError: v.optional(v.string())
2674
- // Error message if sync failed
2675
- }).index("by_globalId", ["globalId"]).index("by_contentHash", ["contentHash"]).index("by_nodeType", ["nodeType"]).index("by_subtype", ["nodeType", "subtype"]).index("by_domain", ["domain"]).index("by_project", ["projectId"]).index("by_project_type", ["projectId", "nodeType"]).index("by_topic", ["topicId"]).index("by_topic_type", ["topicId", "nodeType"]).index("by_tenantId", ["tenantId"]).index("by_workspaceId", ["workspaceId"]).index("by_tenant_workspace", ["tenantId", "workspaceId"]).index("by_audienceLabel", ["audienceLabel"]).index("by_sensitivityTier", ["sensitivityTier"]).index("by_exportClass", ["exportClass"]).index("by_status", ["status"]).index("by_sourceType", ["sourceType"]).index("by_verification", ["verificationStatus"]).index("by_layer", ["epistemicLayer"]).index("by_layer_type", ["epistemicLayer", "nodeType"]).index("by_syncStatus", ["syncStatus"]).index("by_publicationStatus", ["publicationStatus"]).index("by_tenant_publicationStatus", ["tenantId", "publicationStatus"]).index("by_belief_status", ["nodeType", "beliefStatus"]).index("by_epistemic_status", ["nodeType", "epistemicStatus"]).index("by_temporal_nature", ["nodeType", "temporalNature"]).index("by_methodology", ["nodeType", "methodology"]).index("by_reversibility", ["nodeType", "reversibility"]).index("by_questionType", ["nodeType", "questionType"]).index("by_questionPriority", ["nodeType", "questionPriority"]).searchIndex("search_canonicalText", {
2676
- searchField: "canonicalText",
2677
- filterFields: ["nodeType", "projectId", "topicId", "status"]
2678
- });
2679
- function getLayerForNodeType(type) {
2680
- switch (type) {
2681
- case "decision":
2682
- return "L4";
2683
- case "belief":
2684
- case "question":
2685
- case "theme":
2686
- case "deal":
2687
- return "L3";
2688
- case "claim":
2689
- case "evidence":
2690
- case "synthesis":
2691
- case "answer":
2692
- return "L2";
2693
- case "atomic_fact":
2694
- case "excerpt":
2695
- case "source":
2696
- return "L1";
2697
- case "topic":
2698
- return "organizational";
2699
- case "company":
2700
- case "person":
2701
- case "investor":
2702
- case "function":
2703
- case "value_chain":
2704
- return "ontological";
2705
- }
2706
- }
2707
- var LEGACY_SCOPE_FIELD2 = "graphScopeProjectId";
397
+ var LEGACY_SCOPE_FIELD = "graphScopeProjectId";
2708
398
  function asMappedProjectId(topic) {
2709
399
  if (!topic) {
2710
400
  return;
2711
401
  }
2712
- const directLegacyProjectId = normalizeScopeValue(topic[LEGACY_SCOPE_FIELD2]);
402
+ const directLegacyProjectId = normalizeScopeValue(topic[LEGACY_SCOPE_FIELD]);
2713
403
  if (directLegacyProjectId) {
2714
404
  return directLegacyProjectId;
2715
405
  }
2716
406
  const metadata = topic.metadata || {};
2717
- const candidate = metadata[LEGACY_SCOPE_FIELD2] || metadata.legacyProjectId || metadata.projectId || metadata.scopeProjectId;
407
+ const candidate = metadata[LEGACY_SCOPE_FIELD] || metadata.legacyProjectId || metadata.projectId || metadata.scopeProjectId;
2718
408
  return candidate ? candidate : void 0;
2719
409
  }
2720
410
  function normalizeScopeValue(value) {
@@ -2743,7 +433,7 @@ async function findTopicsByScopeAlias(ctx, scopeId) {
2743
433
  try {
2744
434
  return await ctx.db.query("topics").withIndex(
2745
435
  "by_graph_scope_project",
2746
- (q) => q.eq(LEGACY_SCOPE_FIELD2, scopeId)
436
+ (q) => q.eq(LEGACY_SCOPE_FIELD, scopeId)
2747
437
  ).collect();
2748
438
  } catch {
2749
439
  const topics = await ctx.db.query("topics").collect();
@@ -2759,7 +449,7 @@ async function tryResolveHostTopicById(ctx, topicId) {
2759
449
  return null;
2760
450
  }
2761
451
  try {
2762
- return await ctx.runQuery(api2.topics.get, {
452
+ return await ctx.runQuery(api.topics.get, {
2763
453
  id: topicId
2764
454
  }) ?? null;
2765
455
  } catch {
@@ -2771,7 +461,7 @@ async function tryResolveHostTopicByLegacyScope(ctx, legacyScopeId) {
2771
461
  return null;
2772
462
  }
2773
463
  try {
2774
- return await ctx.runQuery(api2.topics.getByLegacyScopeId, {
464
+ return await ctx.runQuery(api.topics.getByLegacyScopeId, {
2775
465
  projectId: legacyScopeId
2776
466
  }) ?? null;
2777
467
  } catch {
@@ -2909,7 +599,7 @@ function throwWorkspaceIsolationError(args) {
2909
599
  throw error;
2910
600
  }
2911
601
  function assertWorkspaceScopedEpistemicNodeScope(args) {
2912
- const layer = getLayerForNodeType(args.nodeType);
602
+ const layer = isNodeType(args.nodeType) ? getLayerForNodeType(args.nodeType) : void 0;
2913
603
  if (layer === "ontological") {
2914
604
  return;
2915
605
  }
@@ -2937,11 +627,11 @@ function nodeMatchesWorkspaceReasoningScope(node, scope) {
2937
627
  const scopeWorkspaceId = normalizeScopeValue2(scope.workspaceId);
2938
628
  const nodeTenantId = normalizeScopeValue2(node.tenantId);
2939
629
  const nodeWorkspaceId = normalizeScopeValue2(node.workspaceId);
2940
- const epistemicLayer2 = typeof node.epistemicLayer === "string" ? node.epistemicLayer : void 0;
630
+ const epistemicLayer = typeof node.epistemicLayer === "string" ? node.epistemicLayer : void 0;
2941
631
  if (scopeTenantId && nodeTenantId && scopeTenantId !== nodeTenantId) {
2942
632
  return false;
2943
633
  }
2944
- if (epistemicLayer2 === "ontological" && nodeWorkspaceId === void 0) {
634
+ if (epistemicLayer === "ontological" && nodeWorkspaceId === void 0) {
2945
635
  return true;
2946
636
  }
2947
637
  if (!scopeWorkspaceId && node.publicationStatus === "published") {
@@ -2969,11 +659,11 @@ function edgeMatchesWorkspaceReasoningScope(edge, scope) {
2969
659
  return scopeWorkspaceId === edgeWorkspaceId;
2970
660
  }
2971
661
  async function resolveNodeScopeForWorkspaceIsolation(ctx, node) {
2972
- const epistemicLayer2 = typeof node?.epistemicLayer === "string" ? node.epistemicLayer : void 0;
662
+ const epistemicLayer = typeof node?.epistemicLayer === "string" ? node.epistemicLayer : void 0;
2973
663
  const resolved = {
2974
664
  tenantId: normalizeScopeValue2(node?.tenantId),
2975
665
  workspaceId: normalizeScopeValue2(node?.workspaceId),
2976
- epistemicLayer: epistemicLayer2,
666
+ epistemicLayer,
2977
667
  nodeType: typeof node?.nodeType === "string" ? node.nodeType : void 0
2978
668
  };
2979
669
  if (!node) {
@@ -3048,16 +738,17 @@ function resolveTraversalTargetNodeId(edge, direction) {
3048
738
  }
3049
739
  function readNodeOpinion(node) {
3050
740
  const metadata = node.metadata ?? {};
3051
- return readOpinionFromRecord(
3052
- {
741
+ try {
742
+ return readOpinionFromRecord({
3053
743
  ...metadata,
3054
744
  opinion_b: node.opinion_b,
3055
745
  opinion_d: node.opinion_d,
3056
746
  opinion_u: node.opinion_u,
3057
747
  opinion_a: node.opinion_a
3058
- },
3059
- { b: 0, d: 0, u: 1, a: 0.5 }
3060
- );
748
+ });
749
+ } catch {
750
+ return mkOpinion(0, 0, 1, 0.5);
751
+ }
3061
752
  }
3062
753
  async function collectConfidencePropagationDispatches(args) {
3063
754
  const dispatchesByTargetId = /* @__PURE__ */ new Map();
@@ -3126,14 +817,20 @@ async function collectConfidencePropagationDispatches(args) {
3126
817
  if (!result || !hasProjectedOpinionChanged(targetOpinion, result.opinion)) {
3127
818
  continue;
3128
819
  }
3129
- opinionCache.set(cacheKey, result.opinion);
820
+ const projectedOpinion = mkOpinion(
821
+ result.opinion.b,
822
+ result.opinion.d,
823
+ result.opinion.u,
824
+ result.opinion.a
825
+ );
826
+ opinionCache.set(cacheKey, projectedOpinion);
3130
827
  const existingDispatch = dispatchesByTargetId.get(cacheKey);
3131
828
  dispatchesByTargetId.set(cacheKey, {
3132
829
  targetNodeId,
3133
830
  edgeType: spec.edgeType,
3134
831
  traversedDirection: direction,
3135
832
  weight: edge.weight ?? 1,
3136
- opinion: result.opinion,
833
+ opinion: projectedOpinion,
3137
834
  operator: result.operator,
3138
835
  rationale: existingDispatch ? `${existingDispatch.rationale}; ${result.rationale}` : result.rationale,
3139
836
  hop: nextHop
@@ -3141,7 +838,7 @@ async function collectConfidencePropagationDispatches(args) {
3141
838
  if (canContinueTransitively(spec, nextHop)) {
3142
839
  queue.push({
3143
840
  nodeId: targetNodeId,
3144
- opinion: result.opinion,
841
+ opinion: projectedOpinion,
3145
842
  hop: nextHop,
3146
843
  visitedNodeIds: /* @__PURE__ */ new Set([
3147
844
  ...state.visitedNodeIds,
@@ -3256,48 +953,48 @@ async function getQuestionsAnsweredByEvidence(ctx, evidenceId) {
3256
953
  }
3257
954
 
3258
955
  // src/topicProjectOverlay.ts
3259
- var LEGACY_SCOPE_FIELD3 = "graphScopeProjectId";
3260
- function readNonEmptyString2(value) {
956
+ var LEGACY_SCOPE_FIELD2 = "graphScopeProjectId";
957
+ function readNonEmptyString(value) {
3261
958
  if (typeof value !== "string") {
3262
959
  return;
3263
960
  }
3264
961
  const normalized = value.trim();
3265
962
  return normalized.length > 0 ? normalized : void 0;
3266
963
  }
3267
- function readStringArray2(value) {
964
+ function readStringArray(value) {
3268
965
  if (!Array.isArray(value)) {
3269
966
  return [];
3270
967
  }
3271
- return value.map((entry) => readNonEmptyString2(entry)).filter((entry) => Boolean(entry));
968
+ return value.map((entry) => readNonEmptyString(entry)).filter((entry) => Boolean(entry));
3272
969
  }
3273
- function readMetadata2(topic) {
970
+ function readMetadata(topic) {
3274
971
  return topic.metadata && typeof topic.metadata === "object" ? topic.metadata : {};
3275
972
  }
3276
- function readLegacyProjectId2(value) {
973
+ function readLegacyProjectId(value) {
3277
974
  if (!value) {
3278
975
  return;
3279
976
  }
3280
- return readNonEmptyString2(value[LEGACY_SCOPE_FIELD3]);
977
+ return readNonEmptyString(value[LEGACY_SCOPE_FIELD2]);
3281
978
  }
3282
- function coerceVisibility2(value) {
979
+ function coerceVisibility(value) {
3283
980
  return value === "private" || value === "team" || value === "firm" || value === "external" || value === "public" ? value : void 0;
3284
981
  }
3285
- function coerceStatus2(value) {
982
+ function coerceStatus(value) {
3286
983
  return value === "active" || value === "archived" || value === "watching" ? value : void 0;
3287
984
  }
3288
- function mapProjectType2(topic, metadata) {
3289
- const explicit = readNonEmptyString2(metadata.projectType);
985
+ function mapProjectType(topic, metadata) {
986
+ const explicit = readNonEmptyString(metadata.projectType);
3290
987
  if (explicit) {
3291
988
  return explicit;
3292
989
  }
3293
990
  if (topic.type === "theme") {
3294
991
  return "thematic";
3295
992
  }
3296
- return readNonEmptyString2(topic.type) || "general";
993
+ return readNonEmptyString(topic.type) || "general";
3297
994
  }
3298
- function isProjectLikeTopic2(topic) {
3299
- const metadata = readMetadata2(topic);
3300
- return topic.type === "theme" || topic.type === "thematic" || topic.type === "deal" || topic.type === "monitoring" || readLegacyProjectId2(topic) !== void 0 || readNonEmptyString2(metadata.projectType) !== void 0;
995
+ function isProjectLikeTopic(topic) {
996
+ const metadata = readMetadata(topic);
997
+ return topic.type === "theme" || topic.type === "thematic" || topic.type === "deal" || topic.type === "monitoring" || readLegacyProjectId(topic) !== void 0 || readNonEmptyString(metadata.projectType) !== void 0;
3301
998
  }
3302
999
  function isMissingLucernChildComponentError(error) {
3303
1000
  const message = error instanceof Error ? error.message : String(error);
@@ -3305,7 +1002,7 @@ function isMissingLucernChildComponentError(error) {
3305
1002
  'Child component ComponentName(Identifier("lucern")) not found'
3306
1003
  ) || message.includes("Child component") && message.includes("lucern") && message.includes("not found");
3307
1004
  }
3308
- async function resolveTopicDoc2(ctx, scopeId) {
1005
+ async function resolveTopicDoc(ctx, scopeId) {
3309
1006
  if (ctx?.db && typeof ctx.db.get === "function") {
3310
1007
  try {
3311
1008
  const directTopic = await ctx.db.get(scopeId);
@@ -3319,7 +1016,7 @@ async function resolveTopicDoc2(ctx, scopeId) {
3319
1016
  return null;
3320
1017
  }
3321
1018
  try {
3322
- const topic = await ctx.runQuery(api2.topics.get, {
1019
+ const topic = await ctx.runQuery(api.topics.get, {
3323
1020
  id: String(scopeId)
3324
1021
  });
3325
1022
  if (topic?.name !== void 0 && topic?.type !== void 0) {
@@ -3328,7 +1025,7 @@ async function resolveTopicDoc2(ctx, scopeId) {
3328
1025
  } catch {
3329
1026
  }
3330
1027
  try {
3331
- const topic = await ctx.runQuery(api2.topics.getByLegacyScopeId, {
1028
+ const topic = await ctx.runQuery(api.topics.getByLegacyScopeId, {
3332
1029
  projectId: String(scopeId)
3333
1030
  });
3334
1031
  if (topic?.name !== void 0 && topic?.type !== void 0) {
@@ -3338,14 +1035,14 @@ async function resolveTopicDoc2(ctx, scopeId) {
3338
1035
  }
3339
1036
  return null;
3340
1037
  }
3341
- function materializeTopicProjectOverlay2(topic, idMode = "legacy") {
3342
- const metadata = readMetadata2(topic);
1038
+ function materializeTopicProjectOverlay(topic, idMode = "legacy") {
1039
+ const metadata = readMetadata(topic);
3343
1040
  const topicId = String(topic._id);
3344
- const legacyProjectId = readLegacyProjectId2(topic) || readLegacyProjectId2(metadata) || readNonEmptyString2(metadata.legacyProjectId);
1041
+ const legacyProjectId = readLegacyProjectId(topic) || readLegacyProjectId(metadata) || readNonEmptyString(metadata.legacyProjectId);
3345
1042
  const storageProjectId = legacyProjectId || topicId;
3346
1043
  const outwardId = idMode === "topic" ? topicId : storageProjectId;
3347
- const visibility = coerceVisibility2(topic.visibility) || coerceVisibility2(metadata.visibility) || "private";
3348
- const status = coerceStatus2(topic.status) || coerceStatus2(metadata.status) || "active";
1044
+ const visibility = coerceVisibility(topic.visibility) || coerceVisibility(metadata.visibility) || "private";
1045
+ const status = coerceStatus(topic.status) || coerceStatus(metadata.status) || "active";
3349
1046
  const createdAt = typeof topic.createdAt === "number" ? topic.createdAt : typeof topic._creationTime === "number" ? topic._creationTime : 0;
3350
1047
  const updatedAt = typeof topic.updatedAt === "number" ? topic.updatedAt : typeof metadata.updatedAt === "number" ? metadata.updatedAt : createdAt;
3351
1048
  return {
@@ -3355,16 +1052,16 @@ function materializeTopicProjectOverlay2(topic, idMode = "legacy") {
3355
1052
  topicId,
3356
1053
  storageProjectId,
3357
1054
  legacyProjectId,
3358
- name: readNonEmptyString2(topic.name) || "Untitled Theme",
3359
- type: mapProjectType2(topic, metadata),
3360
- description: readNonEmptyString2(topic.description),
3361
- ownerId: readNonEmptyString2(metadata.ownerId) || readNonEmptyString2(topic.createdBy) || "system",
3362
- sharedWith: readStringArray2(metadata.sharedWith),
1055
+ name: readNonEmptyString(topic.name) || "Untitled Theme",
1056
+ type: mapProjectType(topic, metadata),
1057
+ description: readNonEmptyString(topic.description),
1058
+ ownerId: readNonEmptyString(metadata.ownerId) || readNonEmptyString(topic.createdBy) || "system",
1059
+ sharedWith: readStringArray(metadata.sharedWith),
3363
1060
  visibility,
3364
- tenantId: readNonEmptyString2(topic.tenantId) || readNonEmptyString2(metadata.tenantId),
3365
- workspaceId: readNonEmptyString2(topic.workspaceId) || readNonEmptyString2(metadata.workspaceId),
1061
+ tenantId: readNonEmptyString(topic.tenantId) || readNonEmptyString(metadata.tenantId),
1062
+ workspaceId: readNonEmptyString(topic.workspaceId) || readNonEmptyString(metadata.workspaceId),
3366
1063
  status,
3367
- tags: readStringArray2(metadata.tags),
1064
+ tags: readStringArray(metadata.tags),
3368
1065
  chatCount: typeof metadata.chatCount === "number" ? metadata.chatCount : 0,
3369
1066
  artifactCount: typeof metadata.artifactCount === "number" ? metadata.artifactCount : 0,
3370
1067
  lastActivityAt: typeof metadata.lastActivityAt === "number" ? metadata.lastActivityAt : updatedAt,
@@ -3373,17 +1070,17 @@ function materializeTopicProjectOverlay2(topic, idMode = "legacy") {
3373
1070
  updatedAt
3374
1071
  };
3375
1072
  }
3376
- async function resolveTopicProjectOverlay2(ctx, scopeId, options = {}) {
3377
- const topic = await resolveTopicDoc2(ctx, scopeId);
1073
+ async function resolveTopicProjectOverlay(ctx, scopeId, options = {}) {
1074
+ const topic = await resolveTopicDoc(ctx, scopeId);
3378
1075
  if (!topic) {
3379
1076
  return null;
3380
1077
  }
3381
- if (options.projectLikeOnly !== false && !isProjectLikeTopic2(topic)) {
1078
+ if (options.projectLikeOnly !== false && !isProjectLikeTopic(topic)) {
3382
1079
  return null;
3383
1080
  }
3384
- return materializeTopicProjectOverlay2(topic, options.idMode);
1081
+ return materializeTopicProjectOverlay(topic, options.idMode);
3385
1082
  }
3386
- async function listTopicProjectOverlays2(ctx, options = {}) {
1083
+ async function listTopicProjectOverlays(ctx, options = {}) {
3387
1084
  let allTopics = [];
3388
1085
  if (ctx?.db?.query && typeof ctx.db.query === "function") {
3389
1086
  try {
@@ -3393,18 +1090,18 @@ async function listTopicProjectOverlays2(ctx, options = {}) {
3393
1090
  }
3394
1091
  }
3395
1092
  if (allTopics.length === 0 && typeof ctx.runQuery === "function") {
3396
- allTopics = (await ctx.runQuery(api2.topics.list, {}) ?? []) || [];
1093
+ allTopics = (await ctx.runQuery(api.topics.list, {}) ?? []) || [];
3397
1094
  }
3398
1095
  return allTopics.filter(
3399
- (topic) => options.projectLikeOnly === false || isProjectLikeTopic2(topic)
3400
- ).map((topic) => materializeTopicProjectOverlay2(topic, options.idMode));
1096
+ (topic) => options.projectLikeOnly === false || isProjectLikeTopic(topic)
1097
+ ).map((topic) => materializeTopicProjectOverlay(topic, options.idMode));
3401
1098
  }
3402
1099
  async function patchTopicProjectOverlay(ctx, scopeId, value) {
3403
- const topic = await resolveTopicDoc2(ctx, scopeId);
1100
+ const topic = await resolveTopicDoc(ctx, scopeId);
3404
1101
  if (!topic) {
3405
1102
  return null;
3406
1103
  }
3407
- const nextMetadata = { ...readMetadata2(topic) };
1104
+ const nextMetadata = { ...readMetadata(topic) };
3408
1105
  const patch = {};
3409
1106
  const topicUpdateArgs = {
3410
1107
  id: String(topic._id)
@@ -3429,7 +1126,7 @@ async function patchTopicProjectOverlay(ctx, scopeId, value) {
3429
1126
  `patchTopicProjectOverlay cannot mutate ${key} via component-owned topics`
3430
1127
  );
3431
1128
  case "status": {
3432
- const status = coerceStatus2(rawValue);
1129
+ const status = coerceStatus(rawValue);
3433
1130
  if (status) {
3434
1131
  patch.status = status;
3435
1132
  topicUpdateArgs.status = status;
@@ -3437,7 +1134,7 @@ async function patchTopicProjectOverlay(ctx, scopeId, value) {
3437
1134
  break;
3438
1135
  }
3439
1136
  case "visibility": {
3440
- const visibility = coerceVisibility2(rawValue);
1137
+ const visibility = coerceVisibility(rawValue);
3441
1138
  if (visibility) {
3442
1139
  patch.visibility = visibility;
3443
1140
  topicUpdateArgs.visibility = visibility;
@@ -3445,7 +1142,7 @@ async function patchTopicProjectOverlay(ctx, scopeId, value) {
3445
1142
  break;
3446
1143
  }
3447
1144
  case "type": {
3448
- const projectType = readNonEmptyString2(rawValue);
1145
+ const projectType = readNonEmptyString(rawValue);
3449
1146
  if (projectType) {
3450
1147
  nextMetadata.projectType = projectType;
3451
1148
  } else {
@@ -3469,7 +1166,7 @@ async function patchTopicProjectOverlay(ctx, scopeId, value) {
3469
1166
  topicUpdateArgs.metadata = nextMetadata;
3470
1167
  if (typeof ctx.runMutation === "function") {
3471
1168
  try {
3472
- await ctx.runMutation(api2.topics.update, topicUpdateArgs);
1169
+ await ctx.runMutation(api.topics.update, topicUpdateArgs);
3473
1170
  } catch (error) {
3474
1171
  if (!isMissingLucernChildComponentError(error) || !ctx?.db || typeof ctx.db.patch !== "function") {
3475
1172
  throw error;
@@ -3483,7 +1180,7 @@ async function patchTopicProjectOverlay(ctx, scopeId, value) {
3483
1180
  "Cannot patch topic without component adapter (ctx.runMutation unavailable)"
3484
1181
  );
3485
1182
  }
3486
- return materializeTopicProjectOverlay2(
1183
+ return materializeTopicProjectOverlay(
3487
1184
  {
3488
1185
  ...topic,
3489
1186
  ...patch,
@@ -3516,10 +1213,10 @@ async function patchProjectWithTolerance(ctx, projectId, value) {
3516
1213
  });
3517
1214
  }
3518
1215
  }
3519
- function defaultResolvers2() {
1216
+ function defaultResolvers() {
3520
1217
  return {
3521
1218
  async getProject(ctx, projectId) {
3522
- return await resolveTopicProjectOverlay2(ctx, projectId, {
1219
+ return await resolveTopicProjectOverlay(ctx, projectId, {
3523
1220
  idMode: "legacy",
3524
1221
  projectLikeOnly: false
3525
1222
  });
@@ -3528,7 +1225,7 @@ function defaultResolvers2() {
3528
1225
  await patchProjectWithTolerance(ctx, projectId, value);
3529
1226
  },
3530
1227
  async listTopics(ctx) {
3531
- return await listTopicProjectOverlays2(ctx, {
1228
+ return await listTopicProjectOverlays(ctx, {
3532
1229
  idMode: "legacy"
3533
1230
  });
3534
1231
  },
@@ -3537,11 +1234,11 @@ function defaultResolvers2() {
3537
1234
  }
3538
1235
  };
3539
1236
  }
3540
- var resolverOverrides2 = {};
1237
+ var resolverOverrides = {};
3541
1238
  function resolveGraphPrimitivesAppResolvers(_ctx) {
3542
1239
  return {
3543
- ...defaultResolvers2(),
3544
- ...resolverOverrides2
1240
+ ...defaultResolvers(),
1241
+ ...resolverOverrides
3545
1242
  };
3546
1243
  }
3547
1244
 
@@ -3566,7 +1263,7 @@ function throwStructuredMutationError(args) {
3566
1263
  function readFiniteNumber(value) {
3567
1264
  return typeof value === "number" && Number.isFinite(value) ? value : void 0;
3568
1265
  }
3569
- function clamp012(value) {
1266
+ function clamp01(value) {
3570
1267
  return Math.max(0, Math.min(1, value));
3571
1268
  }
3572
1269
  function assertBaseRateInRange(baseRate, field = "baseRate") {
@@ -3616,20 +1313,14 @@ function deriveSyntheticBackfillOpinion(source) {
3616
1313
  const uncertainty = readFiniteNumber(source.opinion_u) ?? readFiniteNumber(source.uncertainty);
3617
1314
  const baseRate = readFiniteNumber(source.opinion_a) ?? readFiniteNumber(source.baseRate);
3618
1315
  if (belief !== void 0 || disbelief !== void 0 || uncertainty !== void 0 || baseRate !== void 0) {
3619
- return readOpinionFromRecord(source, {
3620
- b: 0,
3621
- d: 0,
3622
- u: 1,
3623
- a: 0.5
3624
- });
1316
+ try {
1317
+ return readOpinionFromRecord(source);
1318
+ } catch {
1319
+ return mkOpinion(0, 0, 1, 0.5);
1320
+ }
3625
1321
  }
3626
- const confidence2 = clamp012(readFiniteNumber(source.confidence) ?? 0);
3627
- return {
3628
- b: confidence2,
3629
- d: 1 - confidence2,
3630
- u: 0,
3631
- a: 0.5
3632
- };
1322
+ const confidence = clamp01(readFiniteNumber(source.confidence) ?? 0);
1323
+ return mkOpinion(confidence, 1 - confidence, 0, 0.5);
3633
1324
  }
3634
1325
  function clampBeliefLimit(limit, fallback = DEFAULT_PROJECT_BELIEF_LIMIT) {
3635
1326
  if (!Number.isFinite(limit)) {
@@ -3644,16 +1335,17 @@ function readTupleContradictedFlag(value) {
3644
1335
  return typeof value === "boolean" ? value : void 0;
3645
1336
  }
3646
1337
  function readBeliefOpinionSnapshot(node, metadata) {
3647
- return readOpinionFromRecord(
3648
- {
1338
+ try {
1339
+ return readOpinionFromRecord({
3649
1340
  ...metadata,
3650
1341
  opinion_b: node.opinion_b,
3651
1342
  opinion_d: node.opinion_d,
3652
1343
  opinion_u: node.opinion_u,
3653
1344
  opinion_a: node.opinion_a
3654
- },
3655
- { b: 0, d: 0, u: 1, a: 0.5 }
3656
- );
1345
+ });
1346
+ } catch {
1347
+ return mkOpinion(0, 0, 1, 0.5);
1348
+ }
3657
1349
  }
3658
1350
  function deriveTupleContradictionSeverity(node) {
3659
1351
  const metadata = node.metadata || {};
@@ -3670,10 +1362,10 @@ function formatTupleContradictionDescription(args) {
3670
1362
  return `Tuple-space contradiction detected: b=${args.opinion.b.toFixed(2)} > ${args.policy.beliefThreshold.toFixed(2)} and d=${args.opinion.d.toFixed(2)} > ${args.policy.disbeliefThreshold.toFixed(2)}.`;
3671
1363
  }
3672
1364
  function generateContentHash(text) {
3673
- const content2 = `belief:${text.trim().toLowerCase().replace(/\s+/g, " ")}`;
1365
+ const content = `belief:${text.trim().toLowerCase().replace(/\s+/g, " ")}`;
3674
1366
  let hash = 5381;
3675
- for (let i = 0; i < content2.length; i++) {
3676
- hash = (hash << 5) + hash + content2.charCodeAt(i);
1367
+ for (let i = 0; i < content.length; i++) {
1368
+ hash = (hash << 5) + hash + content.charCodeAt(i);
3677
1369
  hash &= hash;
3678
1370
  }
3679
1371
  return Math.abs(hash).toString(16).padStart(8, "0");
@@ -3750,8 +1442,8 @@ async function resolveBeliefScopeOrNull(ctx, args) {
3750
1442
  }
3751
1443
  async function getBeliefNodesForScope(ctx, scope, args) {
3752
1444
  const baseQuery = ctx.db.query("epistemicNodes").withIndex(
3753
- scope.topicId ? "by_topic_type" : "by_project_type",
3754
- (q) => scope.topicId ? q.eq("topicId", scope.topicId).eq("nodeType", "belief") : q.eq("projectId", scope.projectId).eq("nodeType", "belief")
1445
+ "by_topic_type",
1446
+ (q) => q.eq("topicId", scope.topicId).eq("nodeType", "belief")
3755
1447
  );
3756
1448
  const nodes = typeof args?.scanLimit === "number" ? await baseQuery.order("desc").take(args.scanLimit) : await baseQuery.collect();
3757
1449
  const scopedNodes = nodes.filter(
@@ -3902,7 +1594,7 @@ async function applyBeliefConfidenceChange(ctx, args) {
3902
1594
  }
3903
1595
  }
3904
1596
  const previousConfidence = node.confidence || 0.5;
3905
- const predictionMeta2 = node.predictionMeta || existingMetadata.predictionMeta;
1597
+ const predictionMeta = node.predictionMeta || existingMetadata.predictionMeta;
3906
1598
  const previousOpinion = readBeliefOpinionSnapshot(node, existingMetadata);
3907
1599
  const slB = args.belief;
3908
1600
  const slD = args.disbelief;
@@ -3929,7 +1621,7 @@ async function applyBeliefConfidenceChange(ctx, args) {
3929
1621
  currentBeliefStatus,
3930
1622
  {
3931
1623
  confidence: derivedConfidence,
3932
- predictionMeta: predictionMeta2,
1624
+ predictionMeta,
3933
1625
  metadata: existingMetadata
3934
1626
  }
3935
1627
  );
@@ -4101,12 +1793,12 @@ internalMutation({
4101
1793
  },
4102
1794
  returns: permissiveReturn,
4103
1795
  handler: async (ctx, args) => {
4104
- const sourceOpinion = {
4105
- b: args.opinion_b,
4106
- d: args.opinion_d,
4107
- u: args.opinion_u,
4108
- a: args.opinion_a
4109
- };
1796
+ const sourceOpinion = mkOpinion(
1797
+ args.opinion_b,
1798
+ args.opinion_d,
1799
+ args.opinion_u,
1800
+ args.opinion_a
1801
+ );
4110
1802
  const sourceNode = await ctx.db.get(args.nodeId);
4111
1803
  const sourceScope = await resolveNodeScopeForWorkspaceIsolation(
4112
1804
  ctx,
@@ -4213,7 +1905,7 @@ mutation({
4213
1905
  expectedBy: v.optional(v.number())
4214
1906
  })
4215
1907
  ),
4216
- baseRate: v.number(),
1908
+ baseRate: v.optional(v.number()),
4217
1909
  metadata: v.optional(v.any())
4218
1910
  // Additional metadata including isConditional
4219
1911
  },
@@ -4244,7 +1936,7 @@ mutation({
4244
1936
  );
4245
1937
  }
4246
1938
  const now = Date.now();
4247
- const baseRate = assertBaseRateInRange(args.baseRate);
1939
+ const baseRate = assertBaseRateInRange(args.baseRate ?? 0.5);
4248
1940
  const initialBeliefStatus = args.worktreeId ? "hypothesis" : "assumption";
4249
1941
  const initialEpistemicStatus = args.worktreeId ? "hypothesis" : "assumption";
4250
1942
  const seedOpinion = {
@@ -4583,8 +2275,8 @@ query({
4583
2275
  }
4584
2276
  }
4585
2277
  const query2 = ctx.db.query("epistemicNodes").withIndex(
4586
- scope.topicId ? "by_topic_type" : "by_project_type",
4587
- (q) => scope.topicId ? q.eq("topicId", scope.topicId).eq("nodeType", "belief") : q.eq("projectId", scope.projectId).eq("nodeType", "belief")
2278
+ "by_topic_type",
2279
+ (q) => q.eq("topicId", scope.topicId).eq("nodeType", "belief")
4588
2280
  );
4589
2281
  const nodes = await query2.order("desc").take(scanLimit);
4590
2282
  const scopedNodes = nodes.filter(
@@ -4818,8 +2510,8 @@ mutation({
4818
2510
  // d: evidence AGAINST [0,1]
4819
2511
  uncertainty: v.number(),
4820
2512
  // u: lack of evidence [0,1]
4821
- baseRate: v.optional(v.number()),
4822
- // a: prior probability [0,1], defaults to 0.5
2513
+ baseRate: v.number(),
2514
+ // a: prior probability [0,1]
4823
2515
  trigger: v.union(
4824
2516
  v.literal("evidence_added"),
4825
2517
  v.literal("evidence_removed"),
@@ -5222,9 +2914,13 @@ query({
5222
2914
  },
5223
2915
  returns: permissiveReturn,
5224
2916
  handler: async (ctx, args) => {
2917
+ const scope = await resolveBeliefScopeOrNull(ctx, args);
2918
+ if (!scope) {
2919
+ return { active: 0, superseded: 0, archived: 0, total: 0 };
2920
+ }
5225
2921
  const nodes = await ctx.db.query("epistemicNodes").withIndex(
5226
- args.topicId ? "by_topic_type" : "by_project_type",
5227
- (q) => args.topicId ? q.eq("topicId", args.topicId).eq("nodeType", "belief") : q.eq("projectId", args.projectId).eq("nodeType", "belief")
2922
+ "by_topic_type",
2923
+ (q) => q.eq("topicId", scope.topicId).eq("nodeType", "belief")
5228
2924
  ).collect();
5229
2925
  const counts = {
5230
2926
  active: 0,
@@ -5789,8 +3485,8 @@ query({
5789
3485
  return [];
5790
3486
  }
5791
3487
  const nodes = await ctx.db.query("epistemicNodes").withIndex(
5792
- scope.topicId ? "by_topic_type" : "by_project_type",
5793
- (q) => scope.topicId ? q.eq("topicId", scope.topicId).eq("nodeType", "belief") : q.eq("projectId", scope.projectId).eq("nodeType", "belief")
3488
+ "by_topic_type",
3489
+ (q) => q.eq("topicId", scope.topicId).eq("nodeType", "belief")
5794
3490
  ).collect();
5795
3491
  return nodes.filter((n) => {
5796
3492
  const metadata = n.metadata;
@@ -5966,7 +3662,7 @@ internalMutation({
5966
3662
  args: {
5967
3663
  ...optionalBeliefScopeArgs,
5968
3664
  formulation: v.string(),
5969
- baseRate: v.number(),
3665
+ baseRate: v.optional(v.number()),
5970
3666
  confidence: v.optional(
5971
3667
  v.union(v.literal("high"), v.literal("medium"), v.literal("low"))
5972
3668
  ),
@@ -6002,7 +3698,7 @@ internalMutation({
6002
3698
  returns: permissiveReturn,
6003
3699
  handler: async (ctx, args) => {
6004
3700
  const now = Date.now();
6005
- const baseRate = assertBaseRateInRange(args.baseRate);
3701
+ const baseRate = assertBaseRateInRange(args.baseRate ?? 0.5);
6006
3702
  const scope = await resolveTopicProjectScope(ctx, {
6007
3703
  topicId: args.topicId,
6008
3704
  projectId: args.projectId
@@ -6251,15 +3947,15 @@ internalMutation({
6251
3947
  skippedHasHistory++;
6252
3948
  continue;
6253
3949
  }
6254
- const opinion2 = deriveSyntheticBackfillOpinion(node);
3950
+ const opinion = deriveSyntheticBackfillOpinion(node);
6255
3951
  await ctx.db.insert(
6256
3952
  "beliefConfidence",
6257
3953
  buildBeliefConfidenceRow({
6258
3954
  beliefId: node._id,
6259
- belief: opinion2.b,
6260
- disbelief: opinion2.d,
6261
- uncertainty: opinion2.u,
6262
- baseRate: opinion2.a,
3955
+ belief: opinion.b,
3956
+ disbelief: opinion.d,
3957
+ uncertainty: opinion.u,
3958
+ baseRate: opinion.a,
6263
3959
  trigger: "backfill_synthetic",
6264
3960
  rationale: "LK-6 backfill: synthesized t0 from node-level opinion fields (no prior beliefConfidence row found).",
6265
3961
  assessedAt: readFiniteNumber(node.createdAt) ?? readFiniteNumber(node.updatedAt) ?? Date.now(),