@lucern/graph-primitives 1.0.0 → 1.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +13 -12
- package/dist/beliefDecay.js +24 -17
- package/dist/beliefDecay.js.map +1 -1
- package/dist/beliefEvidenceLinks.js +32 -8
- package/dist/beliefEvidenceLinks.js.map +1 -1
- package/dist/confidencePropagationDispatch.js.map +1 -1
- package/dist/contradictions.js +32 -9
- package/dist/contradictions.js.map +1 -1
- package/dist/convex.d.ts +55 -12
- package/dist/convex.js.map +1 -1
- package/dist/edgeValidation.d.ts +25 -2
- package/dist/edges/index.d.ts +9 -2
- package/dist/edges/index.js.map +1 -1
- package/dist/edges/propagationTypes.d.ts +2 -3
- package/dist/edges/propagationTypes.js.map +1 -1
- package/dist/entityBridge.js +10 -3
- package/dist/entityBridge.js.map +1 -1
- package/dist/entityLifecycle.js +15 -3
- package/dist/entityLifecycle.js.map +1 -1
- package/dist/epistemicAnswers.js.map +1 -1
- package/dist/epistemicBeliefs.admin.d.ts +36 -0
- package/dist/epistemicBeliefs.admin.js +745 -0
- package/dist/epistemicBeliefs.admin.js.map +1 -0
- package/dist/epistemicBeliefs.backfills.d.ts +62 -0
- package/dist/epistemicBeliefs.backfills.js +1004 -0
- package/dist/epistemicBeliefs.backfills.js.map +1 -0
- package/dist/epistemicBeliefs.confidence.d.ts +45 -0
- package/dist/epistemicBeliefs.confidence.js +1285 -0
- package/dist/epistemicBeliefs.confidence.js.map +1 -0
- package/dist/epistemicBeliefs.core.d.ts +35 -0
- package/dist/epistemicBeliefs.core.js +1508 -0
- package/dist/epistemicBeliefs.core.js.map +1 -0
- package/dist/epistemicBeliefs.d.ts +12 -3
- package/dist/epistemicBeliefs.helpers.d.ts +168 -0
- package/dist/epistemicBeliefs.helpers.js +1060 -0
- package/dist/epistemicBeliefs.helpers.js.map +1 -0
- package/dist/epistemicBeliefs.internal.d.ts +30 -0
- package/dist/epistemicBeliefs.internal.js +1329 -0
- package/dist/epistemicBeliefs.internal.js.map +1 -0
- package/dist/epistemicBeliefs.js +1196 -1184
- package/dist/epistemicBeliefs.js.map +1 -1
- package/dist/epistemicBeliefs.lifecycle.d.ts +19 -0
- package/dist/epistemicBeliefs.lifecycle.js +1608 -0
- package/dist/epistemicBeliefs.lifecycle.js.map +1 -0
- package/dist/epistemicBeliefs.links.d.ts +30 -0
- package/dist/epistemicBeliefs.links.js +761 -0
- package/dist/epistemicBeliefs.links.js.map +1 -0
- package/dist/epistemicBeliefs.queries.d.ts +16 -0
- package/dist/epistemicBeliefs.queries.js +90 -0
- package/dist/epistemicBeliefs.queries.js.map +1 -0
- package/dist/epistemicContractHelpers.d.ts +1 -1
- package/dist/epistemicContractHelpers.js +1 -1
- package/dist/epistemicContracts.d.ts +5 -76
- package/dist/epistemicContracts.evaluators.d.ts +36 -0
- package/dist/epistemicContracts.evaluators.js +2506 -0
- package/dist/epistemicContracts.evaluators.js.map +1 -0
- package/dist/epistemicContracts.handlers.d.ts +40 -0
- package/dist/epistemicContracts.handlers.js +3029 -0
- package/dist/epistemicContracts.handlers.js.map +1 -0
- package/dist/epistemicContracts.js +2006 -5281
- package/dist/epistemicContracts.js.map +1 -1
- package/dist/epistemicContracts.metrics.d.ts +26 -0
- package/dist/epistemicContracts.metrics.js +427 -0
- package/dist/epistemicContracts.metrics.js.map +1 -0
- package/dist/epistemicContracts.types.d.ts +159 -0
- package/dist/epistemicContracts.types.js +3 -0
- package/dist/epistemicContracts.types.js.map +1 -0
- package/dist/epistemicEdgeCreation.d.ts +73 -0
- package/dist/epistemicEdgeCreation.js +450 -0
- package/dist/epistemicEdgeCreation.js.map +1 -0
- package/dist/epistemicEdges-BF-cn4i3.d.ts +43 -0
- package/dist/epistemicEdges.d.ts +8 -1
- package/dist/epistemicEdges.handlers.d.ts +20 -0
- package/dist/epistemicEdges.handlers.js +289 -0
- package/dist/epistemicEdges.handlers.js.map +1 -0
- package/dist/epistemicEdges.helpers.d.ts +27 -0
- package/dist/epistemicEdges.helpers.js +162 -0
- package/dist/epistemicEdges.helpers.js.map +1 -0
- package/dist/epistemicEdges.js +797 -875
- package/dist/epistemicEdges.js.map +1 -1
- package/dist/epistemicEdges.mutations.d.ts +39 -0
- package/dist/epistemicEdges.mutations.js +1365 -0
- package/dist/epistemicEdges.mutations.js.map +1 -0
- package/dist/epistemicEdges.queries.d.ts +95 -0
- package/dist/epistemicEdges.queries.js +851 -0
- package/dist/epistemicEdges.queries.js.map +1 -0
- package/dist/epistemicEdges.types.d.ts +32 -0
- package/dist/epistemicEdges.types.js +3 -0
- package/dist/epistemicEdges.types.js.map +1 -0
- package/dist/epistemicEvidence-DvfchNt7.d.ts +46 -0
- package/dist/epistemicEvidence.d.ts +5 -2
- package/dist/epistemicEvidence.js +801 -807
- package/dist/epistemicEvidence.js.map +1 -1
- package/dist/epistemicEvidenceHelpers.d.ts +71 -0
- package/dist/epistemicEvidenceHelpers.js +769 -0
- package/dist/epistemicEvidenceHelpers.js.map +1 -0
- package/dist/epistemicEvidenceMutations.d.ts +10 -0
- package/dist/epistemicEvidenceMutations.js +1421 -0
- package/dist/epistemicEvidenceMutations.js.map +1 -0
- package/dist/epistemicEvidenceQueries.d.ts +10 -0
- package/dist/epistemicEvidenceQueries.js +1049 -0
- package/dist/epistemicEvidenceQueries.js.map +1 -0
- package/dist/epistemicHelpers.d.ts +4 -2
- package/dist/epistemicHelpers.js +132 -127
- package/dist/epistemicHelpers.js.map +1 -1
- package/dist/epistemicLayerRules.d.ts +138 -0
- package/dist/epistemicLayerRules.js +481 -0
- package/dist/epistemicLayerRules.js.map +1 -0
- package/dist/epistemicLinking.js +1 -1
- package/dist/epistemicLinking.js.map +1 -1
- package/dist/epistemicNodeCreation.d.ts +101 -0
- package/dist/epistemicNodeCreation.js +709 -0
- package/dist/epistemicNodeCreation.js.map +1 -0
- package/dist/epistemicNodes-BCQxpYx_.d.ts +54 -0
- package/dist/epistemicNodes.d.ts +5 -1
- package/dist/epistemicNodes.helpers.d.ts +51 -0
- package/dist/epistemicNodes.helpers.js +73 -0
- package/dist/epistemicNodes.helpers.js.map +1 -0
- package/dist/epistemicNodes.internal.d.ts +34 -0
- package/dist/epistemicNodes.internal.js +658 -0
- package/dist/epistemicNodes.internal.js.map +1 -0
- package/dist/epistemicNodes.js +698 -693
- package/dist/epistemicNodes.js.map +1 -1
- package/dist/epistemicNodes.mutations.d.ts +34 -0
- package/dist/epistemicNodes.mutations.js +1153 -0
- package/dist/epistemicNodes.mutations.js.map +1 -0
- package/dist/epistemicNodes.queries.d.ts +36 -0
- package/dist/epistemicNodes.queries.js +619 -0
- package/dist/epistemicNodes.queries.js.map +1 -0
- package/dist/epistemicNodes.validators.d.ts +23 -0
- package/dist/epistemicNodes.validators.js +105 -0
- package/dist/epistemicNodes.validators.js.map +1 -0
- package/dist/epistemicQuestions-bwHd2FWE.d.ts +68 -0
- package/dist/epistemicQuestions.conviction.d.ts +52 -0
- package/dist/epistemicQuestions.conviction.js +1389 -0
- package/dist/epistemicQuestions.conviction.js.map +1 -0
- package/dist/epistemicQuestions.create.d.ts +29 -0
- package/dist/epistemicQuestions.create.js +1300 -0
- package/dist/epistemicQuestions.create.js.map +1 -0
- package/dist/epistemicQuestions.d.ts +10 -2
- package/dist/epistemicQuestions.evidence.d.ts +22 -0
- package/dist/epistemicQuestions.evidence.js +929 -0
- package/dist/epistemicQuestions.evidence.js.map +1 -0
- package/dist/epistemicQuestions.helpers.d.ts +69 -0
- package/dist/epistemicQuestions.helpers.js +824 -0
- package/dist/epistemicQuestions.helpers.js.map +1 -0
- package/dist/epistemicQuestions.js +2435 -2430
- package/dist/epistemicQuestions.js.map +1 -1
- package/dist/epistemicQuestions.lifecycle.d.ts +24 -0
- package/dist/epistemicQuestions.lifecycle.js +838 -0
- package/dist/epistemicQuestions.lifecycle.js.map +1 -0
- package/dist/epistemicQuestions.queries.d.ts +41 -0
- package/dist/epistemicQuestions.queries.js +1013 -0
- package/dist/epistemicQuestions.queries.js.map +1 -0
- package/dist/epistemicQuestions.sprint.d.ts +22 -0
- package/dist/epistemicQuestions.sprint.js +757 -0
- package/dist/epistemicQuestions.sprint.js.map +1 -0
- package/dist/epistemicQuestions.tail.d.ts +42 -0
- package/dist/epistemicQuestions.tail.js +1345 -0
- package/dist/epistemicQuestions.tail.js.map +1 -0
- package/dist/epistemicSources.js +6 -2
- package/dist/epistemicSources.js.map +1 -1
- package/dist/evaluators/index.d.ts +2 -2
- package/dist/evaluators/index.js +45 -5320
- package/dist/evaluators/index.js.map +1 -1
- package/dist/evaluators/lintCheckerEvaluator.d.ts +1 -1
- package/dist/evaluators/sentryCheckerEvaluator.d.ts +1 -1
- package/dist/evaluators/testRunnerEvaluator.d.ts +1 -1
- package/dist/evaluators/tscCheckerEvaluator.d.ts +1 -1
- package/dist/{graphTypes-CpgIuCdo.d.ts → graphTypes-B8VaIjnl.d.ts} +1 -1
- package/dist/graphTypes.d.ts +1 -1
- package/dist/{helpers-BYHIk5vU.d.ts → helpers-DNYfg6mo.d.ts} +2 -3
- package/dist/helpers.d.ts +2 -2
- package/dist/helpers.js.map +1 -1
- package/dist/{index-Dq-7R-gi.d.ts → index-C-Kyd7hD.d.ts} +1 -1
- package/dist/index.d.ts +160 -14
- package/dist/index.js +12291 -13001
- package/dist/index.js.map +1 -1
- package/dist/logicalRoleInference.js.map +1 -1
- package/dist/ontologyApproval.js +1 -1
- package/dist/ontologyApproval.js.map +1 -1
- package/dist/ontologyDefinitions.js +25 -7
- package/dist/ontologyDefinitions.js.map +1 -1
- package/dist/ontologyRegistry.js.map +1 -1
- package/dist/projectionReconciliation.js.map +1 -1
- package/dist/questionEvidenceLinks.js +28 -7
- package/dist/questionEvidenceLinks.js.map +1 -1
- package/dist/resolvers.js.map +1 -1
- package/dist/scopeResolverCompat.js.map +1 -1
- package/dist/topicProjectOverlay.js.map +1 -1
- package/dist/topicScope.js.map +1 -1
- package/dist/workflowBridge.js.map +1 -1
- package/dist/workspaceIsolation.js.map +1 -1
- package/package.json +4 -5
- package/dist/edgeValidation-CeI0wc0r.d.ts +0 -35
- package/dist/epistemicBeliefs-DzKjZAeC.d.ts +0 -377
- package/dist/epistemicEdges-CvlKnEyy.d.ts +0 -191
- package/dist/epistemicEvidence-xw6UUrwh.d.ts +0 -128
- package/dist/epistemicHelpers-DevrYgPN.d.ts +0 -329
- package/dist/epistemicNodes-DjSUfvyD.d.ts +0 -167
- package/dist/epistemicQuestions-B_nUclrH.d.ts +0 -214
- package/dist/index-Dct1T70K.d.ts +0 -25
|
@@ -0,0 +1,1004 @@
|
|
|
1
|
+
import { v } from 'convex/values';
|
|
2
|
+
import { normalizeTupleContradictionPolicy, confidenceFromSL, readOpinionFromRecord, mkOpinion } from '@lucern/confidence';
|
|
3
|
+
import { checkScopeAccess } from '@lucern/access-control/access';
|
|
4
|
+
import { permissiveReturn } from '@lucern/contracts/schema-helpers/validators';
|
|
5
|
+
import { componentsGeneric, anyApi, queryGeneric, internalMutationGeneric, mutationGeneric } from 'convex/server';
|
|
6
|
+
import '@lucern/access-control/audience';
|
|
7
|
+
import '@lucern/access-control/auth';
|
|
8
|
+
import '@lucern/contracts/schema-helpers/spine/tables/epistemicNodes';
|
|
9
|
+
|
|
10
|
+
// src/epistemicBeliefs.backfills.ts
|
|
11
|
+
var api = anyApi;
|
|
12
|
+
componentsGeneric();
|
|
13
|
+
var internal = anyApi;
|
|
14
|
+
var internalMutation = internalMutationGeneric;
|
|
15
|
+
var mutation = mutationGeneric;
|
|
16
|
+
var query = queryGeneric;
|
|
17
|
+
|
|
18
|
+
// src/debug.ts
|
|
19
|
+
function isGraphPrimitiveDebugEnabled() {
|
|
20
|
+
const env = globalThis.process?.env;
|
|
21
|
+
return env?.LUCERN_COMPAT_FALLBACK_DEBUG === "1" || env?.LUCERN_GRAPH_DEBUG === "1";
|
|
22
|
+
}
|
|
23
|
+
function debugGraphPrimitiveFallback(message, context) {
|
|
24
|
+
if (!isGraphPrimitiveDebugEnabled()) {
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
console.debug(message, context ?? {});
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// src/beliefLifecycle.ts
|
|
31
|
+
var BELIEF_STATUS_VALUES = [
|
|
32
|
+
"assumption",
|
|
33
|
+
"hypothesis",
|
|
34
|
+
"belief",
|
|
35
|
+
"fact"
|
|
36
|
+
];
|
|
37
|
+
var RESOLVED_PREDICTION_OUTCOMES = [
|
|
38
|
+
"confirmed",
|
|
39
|
+
"disconfirmed",
|
|
40
|
+
"partial",
|
|
41
|
+
"expired"
|
|
42
|
+
];
|
|
43
|
+
function isBeliefLifecycleStatus(value) {
|
|
44
|
+
return typeof value === "string" && BELIEF_STATUS_VALUES.includes(value);
|
|
45
|
+
}
|
|
46
|
+
function normalizeBeliefConfidence(confidence) {
|
|
47
|
+
if (typeof confidence !== "number" || !Number.isFinite(confidence)) {
|
|
48
|
+
return null;
|
|
49
|
+
}
|
|
50
|
+
if (confidence >= 0 && confidence <= 1) {
|
|
51
|
+
return confidence;
|
|
52
|
+
}
|
|
53
|
+
if (confidence > 1 && confidence <= 100) {
|
|
54
|
+
return confidence / 100;
|
|
55
|
+
}
|
|
56
|
+
return null;
|
|
57
|
+
}
|
|
58
|
+
function isResolvedByConfidence(confidence) {
|
|
59
|
+
const normalized = normalizeBeliefConfidence(confidence);
|
|
60
|
+
if (normalized === null) {
|
|
61
|
+
return false;
|
|
62
|
+
}
|
|
63
|
+
return normalized <= 0 || normalized >= 1;
|
|
64
|
+
}
|
|
65
|
+
function hasResolvedPredictionOutcome(predictionMeta) {
|
|
66
|
+
if (!predictionMeta || typeof predictionMeta !== "object") {
|
|
67
|
+
return false;
|
|
68
|
+
}
|
|
69
|
+
const outcome = predictionMeta.outcome;
|
|
70
|
+
return typeof outcome === "string" && RESOLVED_PREDICTION_OUTCOMES.includes(outcome);
|
|
71
|
+
}
|
|
72
|
+
function getPredictionMetaFromMetadata(metadata) {
|
|
73
|
+
return metadata?.predictionMeta;
|
|
74
|
+
}
|
|
75
|
+
function shouldTreatBeliefAsFact(opts) {
|
|
76
|
+
if (isResolvedByConfidence(opts.confidence)) {
|
|
77
|
+
return true;
|
|
78
|
+
}
|
|
79
|
+
if (hasResolvedPredictionOutcome(opts.predictionMeta)) {
|
|
80
|
+
return true;
|
|
81
|
+
}
|
|
82
|
+
if (hasResolvedPredictionOutcome(getPredictionMetaFromMetadata(opts.metadata))) {
|
|
83
|
+
return true;
|
|
84
|
+
}
|
|
85
|
+
return false;
|
|
86
|
+
}
|
|
87
|
+
function resolveBeliefLifecycleStatus(opts) {
|
|
88
|
+
if (shouldTreatBeliefAsFact(opts)) {
|
|
89
|
+
return "fact";
|
|
90
|
+
}
|
|
91
|
+
const direct = opts.beliefStatus;
|
|
92
|
+
if (isBeliefLifecycleStatus(direct)) {
|
|
93
|
+
const normalized = normalizeBeliefConfidence(opts.confidence);
|
|
94
|
+
if (normalized !== null && isPreValidationBeliefStatus(direct)) {
|
|
95
|
+
return "belief";
|
|
96
|
+
}
|
|
97
|
+
return direct;
|
|
98
|
+
}
|
|
99
|
+
const metaStatus = opts.metadata?.beliefStatus;
|
|
100
|
+
if (isBeliefLifecycleStatus(metaStatus)) {
|
|
101
|
+
const normalized = normalizeBeliefConfidence(opts.confidence);
|
|
102
|
+
if (normalized !== null && isPreValidationBeliefStatus(metaStatus)) {
|
|
103
|
+
return "belief";
|
|
104
|
+
}
|
|
105
|
+
return metaStatus;
|
|
106
|
+
}
|
|
107
|
+
return "assumption";
|
|
108
|
+
}
|
|
109
|
+
function isPreValidationBeliefStatus(status) {
|
|
110
|
+
return status === "assumption" || status === "hypothesis";
|
|
111
|
+
}
|
|
112
|
+
var LEGACY_SCOPE_FIELD = "graphScopeProjectId";
|
|
113
|
+
function asMappedProjectId(topic) {
|
|
114
|
+
if (!topic) {
|
|
115
|
+
return;
|
|
116
|
+
}
|
|
117
|
+
const directLegacyProjectId = normalizeScopeValue(topic[LEGACY_SCOPE_FIELD]);
|
|
118
|
+
if (directLegacyProjectId) {
|
|
119
|
+
return directLegacyProjectId;
|
|
120
|
+
}
|
|
121
|
+
const metadata = topic.metadata || {};
|
|
122
|
+
const candidate = metadata[LEGACY_SCOPE_FIELD] || metadata.legacyProjectId || metadata.projectId || metadata.scopeProjectId;
|
|
123
|
+
return candidate ? candidate : void 0;
|
|
124
|
+
}
|
|
125
|
+
function normalizeScopeValue(value) {
|
|
126
|
+
if (typeof value !== "string") {
|
|
127
|
+
return;
|
|
128
|
+
}
|
|
129
|
+
const normalized = value.trim();
|
|
130
|
+
return normalized.length > 0 ? normalized : void 0;
|
|
131
|
+
}
|
|
132
|
+
function pickPrimaryTopic(candidates) {
|
|
133
|
+
return [...candidates].sort((a, b) => {
|
|
134
|
+
const depthA = a.depth ?? 9999;
|
|
135
|
+
const depthB = b.depth ?? 9999;
|
|
136
|
+
if (depthA !== depthB) {
|
|
137
|
+
return depthA - depthB;
|
|
138
|
+
}
|
|
139
|
+
const createdA = a.createdAt ?? Number.MAX_SAFE_INTEGER;
|
|
140
|
+
const createdB = b.createdAt ?? Number.MAX_SAFE_INTEGER;
|
|
141
|
+
if (createdA !== createdB) {
|
|
142
|
+
return createdA - createdB;
|
|
143
|
+
}
|
|
144
|
+
return String(a.name || "").localeCompare(String(b.name || ""));
|
|
145
|
+
})[0];
|
|
146
|
+
}
|
|
147
|
+
async function findTopicsByScopeAlias(ctx, scopeId) {
|
|
148
|
+
try {
|
|
149
|
+
return await ctx.db.query("topics").withIndex(
|
|
150
|
+
"by_graph_scope_project",
|
|
151
|
+
(q) => q.eq(LEGACY_SCOPE_FIELD, scopeId)
|
|
152
|
+
).collect();
|
|
153
|
+
} catch (error) {
|
|
154
|
+
debugGraphPrimitiveFallback(
|
|
155
|
+
"[topicScope] Failed to resolve scope alias via index",
|
|
156
|
+
{
|
|
157
|
+
error,
|
|
158
|
+
scopeId
|
|
159
|
+
}
|
|
160
|
+
);
|
|
161
|
+
const topics = await ctx.db.query("topics").collect();
|
|
162
|
+
return topics.filter((topic) => {
|
|
163
|
+
const normalizedGlobalId = normalizeScopeValue(topic.globalId);
|
|
164
|
+
const mappedProjectId = asMappedProjectId(topic);
|
|
165
|
+
return String(topic._id) === scopeId || normalizedGlobalId === scopeId || mappedProjectId === scopeId;
|
|
166
|
+
});
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
async function tryResolveHostTopicById(ctx, topicId) {
|
|
170
|
+
if (typeof ctx.runQuery !== "function") {
|
|
171
|
+
return null;
|
|
172
|
+
}
|
|
173
|
+
try {
|
|
174
|
+
return await ctx.runQuery(api.topics.get, {
|
|
175
|
+
id: topicId
|
|
176
|
+
}) ?? null;
|
|
177
|
+
} catch (error) {
|
|
178
|
+
debugGraphPrimitiveFallback(
|
|
179
|
+
"[topicScope] Failed to resolve topic by host query",
|
|
180
|
+
{
|
|
181
|
+
error,
|
|
182
|
+
topicId
|
|
183
|
+
}
|
|
184
|
+
);
|
|
185
|
+
return null;
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
async function tryResolveHostTopicByLegacyScope(ctx, legacyScopeId) {
|
|
189
|
+
if (typeof ctx.runQuery !== "function") {
|
|
190
|
+
return null;
|
|
191
|
+
}
|
|
192
|
+
try {
|
|
193
|
+
return await ctx.runQuery(api.topics.getByLegacyScopeId, {
|
|
194
|
+
projectId: legacyScopeId
|
|
195
|
+
}) ?? null;
|
|
196
|
+
} catch (error) {
|
|
197
|
+
debugGraphPrimitiveFallback(
|
|
198
|
+
"[topicScope] Failed to resolve topic by legacy scope",
|
|
199
|
+
{
|
|
200
|
+
error,
|
|
201
|
+
legacyScopeId
|
|
202
|
+
}
|
|
203
|
+
);
|
|
204
|
+
return null;
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
async function resolveInheritedWorkspaceScope(ctx, topic) {
|
|
208
|
+
const MAX_DEPTH = 10;
|
|
209
|
+
let tenantId = normalizeScopeValue(topic.tenantId);
|
|
210
|
+
let workspaceId = normalizeScopeValue(topic.workspaceId);
|
|
211
|
+
if (tenantId && workspaceId) {
|
|
212
|
+
return { tenantId, workspaceId };
|
|
213
|
+
}
|
|
214
|
+
let current = topic;
|
|
215
|
+
for (let i = 0; i < MAX_DEPTH && current?.parentTopicId; i++) {
|
|
216
|
+
current = await ctx.db.get(current.parentTopicId);
|
|
217
|
+
if (!current) break;
|
|
218
|
+
if (!tenantId) {
|
|
219
|
+
tenantId = normalizeScopeValue(current.tenantId);
|
|
220
|
+
}
|
|
221
|
+
if (!workspaceId) {
|
|
222
|
+
workspaceId = normalizeScopeValue(current.workspaceId);
|
|
223
|
+
}
|
|
224
|
+
if (tenantId && workspaceId) break;
|
|
225
|
+
}
|
|
226
|
+
return { tenantId, workspaceId };
|
|
227
|
+
}
|
|
228
|
+
async function resolveTopicProjectScope(ctx, args) {
|
|
229
|
+
if (args.topicId) {
|
|
230
|
+
let topic = null;
|
|
231
|
+
try {
|
|
232
|
+
topic = await ctx.db.get(
|
|
233
|
+
args.topicId
|
|
234
|
+
);
|
|
235
|
+
} catch (error) {
|
|
236
|
+
debugGraphPrimitiveFallback(
|
|
237
|
+
"[topicScope] Failed to load topic by direct id",
|
|
238
|
+
{
|
|
239
|
+
error,
|
|
240
|
+
topicId: args.topicId
|
|
241
|
+
}
|
|
242
|
+
);
|
|
243
|
+
}
|
|
244
|
+
if (!topic) {
|
|
245
|
+
topic = await tryResolveHostTopicById(ctx, String(args.topicId));
|
|
246
|
+
}
|
|
247
|
+
if (!topic) {
|
|
248
|
+
topic = pickPrimaryTopic(
|
|
249
|
+
await findTopicsByScopeAlias(ctx, String(args.topicId))
|
|
250
|
+
) ?? null;
|
|
251
|
+
}
|
|
252
|
+
if (!topic) {
|
|
253
|
+
throw new Error(`Topic not found: ${String(args.topicId)}`);
|
|
254
|
+
}
|
|
255
|
+
const inherited = await resolveInheritedWorkspaceScope(ctx, topic);
|
|
256
|
+
const mapped = asMappedProjectId(topic);
|
|
257
|
+
if (mapped) {
|
|
258
|
+
return {
|
|
259
|
+
topicId: topic._id,
|
|
260
|
+
projectId: mapped,
|
|
261
|
+
tenantId: inherited.tenantId,
|
|
262
|
+
workspaceId: inherited.workspaceId,
|
|
263
|
+
source: "topic"
|
|
264
|
+
};
|
|
265
|
+
}
|
|
266
|
+
return {
|
|
267
|
+
topicId: topic._id,
|
|
268
|
+
tenantId: inherited.tenantId,
|
|
269
|
+
workspaceId: inherited.workspaceId,
|
|
270
|
+
source: "topic"
|
|
271
|
+
};
|
|
272
|
+
}
|
|
273
|
+
if (args.projectId) {
|
|
274
|
+
let directTopic = null;
|
|
275
|
+
try {
|
|
276
|
+
directTopic = await ctx.db.get(
|
|
277
|
+
args.projectId
|
|
278
|
+
);
|
|
279
|
+
} catch (error) {
|
|
280
|
+
debugGraphPrimitiveFallback(
|
|
281
|
+
"[topicScope] Failed to load direct project topic",
|
|
282
|
+
{
|
|
283
|
+
error,
|
|
284
|
+
projectId: args.projectId
|
|
285
|
+
}
|
|
286
|
+
);
|
|
287
|
+
}
|
|
288
|
+
if (directTopic) {
|
|
289
|
+
const inherited = await resolveInheritedWorkspaceScope(ctx, directTopic);
|
|
290
|
+
const mapped = asMappedProjectId(directTopic);
|
|
291
|
+
return {
|
|
292
|
+
topicId: directTopic._id,
|
|
293
|
+
projectId: mapped ?? args.projectId,
|
|
294
|
+
tenantId: inherited.tenantId,
|
|
295
|
+
workspaceId: inherited.workspaceId,
|
|
296
|
+
source: "topic_inferred"
|
|
297
|
+
};
|
|
298
|
+
}
|
|
299
|
+
directTopic = await tryResolveHostTopicByLegacyScope(ctx, args.projectId);
|
|
300
|
+
if (directTopic) {
|
|
301
|
+
const inherited = await resolveInheritedWorkspaceScope(ctx, directTopic);
|
|
302
|
+
const mapped = asMappedProjectId(directTopic);
|
|
303
|
+
return {
|
|
304
|
+
topicId: directTopic._id,
|
|
305
|
+
projectId: mapped ?? args.projectId,
|
|
306
|
+
tenantId: inherited.tenantId,
|
|
307
|
+
workspaceId: inherited.workspaceId,
|
|
308
|
+
source: "topic_inferred"
|
|
309
|
+
};
|
|
310
|
+
}
|
|
311
|
+
const topics = await findTopicsByScopeAlias(ctx, args.projectId);
|
|
312
|
+
const primary = pickPrimaryTopic(topics);
|
|
313
|
+
if (primary) {
|
|
314
|
+
const inherited = await resolveInheritedWorkspaceScope(ctx, primary);
|
|
315
|
+
return {
|
|
316
|
+
topicId: primary._id,
|
|
317
|
+
projectId: args.projectId,
|
|
318
|
+
tenantId: inherited.tenantId,
|
|
319
|
+
workspaceId: inherited.workspaceId,
|
|
320
|
+
source: "project_mapped_topic"
|
|
321
|
+
};
|
|
322
|
+
}
|
|
323
|
+
throw new Error(
|
|
324
|
+
`Legacy project scope ${String(args.projectId)} has no mapped topic.`
|
|
325
|
+
);
|
|
326
|
+
}
|
|
327
|
+
throw new Error(
|
|
328
|
+
"Missing scope: provide topicId (preferred) or legacy projectId alias."
|
|
329
|
+
);
|
|
330
|
+
}
|
|
331
|
+
var optionalScopeArgs = {
|
|
332
|
+
projectId: v.optional(v.string()),
|
|
333
|
+
topicId: v.optional(v.string())
|
|
334
|
+
};
|
|
335
|
+
function normalizeScopeValue2(value) {
|
|
336
|
+
if (typeof value !== "string") {
|
|
337
|
+
return;
|
|
338
|
+
}
|
|
339
|
+
const normalized = value.trim();
|
|
340
|
+
return normalized.length > 0 ? normalized : void 0;
|
|
341
|
+
}
|
|
342
|
+
function nodeMatchesWorkspaceReasoningScope(node, scope) {
|
|
343
|
+
if (!node) {
|
|
344
|
+
return false;
|
|
345
|
+
}
|
|
346
|
+
const scopeTenantId = normalizeScopeValue2(scope.tenantId);
|
|
347
|
+
const scopeWorkspaceId = normalizeScopeValue2(scope.workspaceId);
|
|
348
|
+
const nodeTenantId = normalizeScopeValue2(node.tenantId);
|
|
349
|
+
const nodeWorkspaceId = normalizeScopeValue2(node.workspaceId);
|
|
350
|
+
const epistemicLayer = typeof node.epistemicLayer === "string" ? node.epistemicLayer : void 0;
|
|
351
|
+
if (scopeTenantId && nodeTenantId && scopeTenantId !== nodeTenantId) {
|
|
352
|
+
return false;
|
|
353
|
+
}
|
|
354
|
+
if (epistemicLayer === "ontological" && nodeWorkspaceId === void 0) {
|
|
355
|
+
return true;
|
|
356
|
+
}
|
|
357
|
+
if (!scopeWorkspaceId && node.publicationStatus === "published") {
|
|
358
|
+
return true;
|
|
359
|
+
}
|
|
360
|
+
if (!scopeWorkspaceId) {
|
|
361
|
+
return nodeWorkspaceId === void 0;
|
|
362
|
+
}
|
|
363
|
+
return scopeWorkspaceId === nodeWorkspaceId;
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
// src/epistemicBeliefs.helpers.ts
|
|
367
|
+
v.id("epistemicNodes");
|
|
368
|
+
var optionalBeliefScopeArgs = optionalScopeArgs;
|
|
369
|
+
({
|
|
370
|
+
tupleContradiction: normalizeTupleContradictionPolicy()
|
|
371
|
+
});
|
|
372
|
+
function throwStructuredMutationError(args) {
|
|
373
|
+
const error = new Error(args.message);
|
|
374
|
+
error.status = args.status;
|
|
375
|
+
error.code = args.code;
|
|
376
|
+
error.invariantCode = args.invariantCode;
|
|
377
|
+
error.suggestion = args.suggestion;
|
|
378
|
+
error.details = args.details;
|
|
379
|
+
throw error;
|
|
380
|
+
}
|
|
381
|
+
function readFiniteNumber(value) {
|
|
382
|
+
return typeof value === "number" && Number.isFinite(value) ? value : void 0;
|
|
383
|
+
}
|
|
384
|
+
function clamp01(value) {
|
|
385
|
+
return Math.max(0, Math.min(1, value));
|
|
386
|
+
}
|
|
387
|
+
function assertBaseRateInRange(baseRate, field = "baseRate") {
|
|
388
|
+
if (baseRate < 0 || baseRate > 1) {
|
|
389
|
+
throwStructuredMutationError({
|
|
390
|
+
message: `${field} must be within [0, 1].`,
|
|
391
|
+
status: 400,
|
|
392
|
+
code: "INVALID_ARGUMENT",
|
|
393
|
+
invariantCode: "request.valid_shape",
|
|
394
|
+
suggestion: `Clamp ${field} into the inclusive [0, 1] interval.`,
|
|
395
|
+
details: { field, baseRate }
|
|
396
|
+
});
|
|
397
|
+
}
|
|
398
|
+
return baseRate;
|
|
399
|
+
}
|
|
400
|
+
function buildBeliefConfidenceRow(args) {
|
|
401
|
+
return {
|
|
402
|
+
beliefId: args.beliefId,
|
|
403
|
+
confidence: confidenceFromSL(
|
|
404
|
+
args.belief,
|
|
405
|
+
args.disbelief,
|
|
406
|
+
args.uncertainty,
|
|
407
|
+
args.baseRate
|
|
408
|
+
),
|
|
409
|
+
belief: args.belief,
|
|
410
|
+
disbelief: args.disbelief,
|
|
411
|
+
uncertainty: args.uncertainty,
|
|
412
|
+
baseRate: args.baseRate,
|
|
413
|
+
slOperator: args.slOperator ?? "manual_assessment",
|
|
414
|
+
trigger: args.trigger,
|
|
415
|
+
...args.rationale ? { rationale: args.rationale } : {},
|
|
416
|
+
assessedBy: args.assessedBy,
|
|
417
|
+
assessedAt: args.assessedAt,
|
|
418
|
+
...args.triggeringEvidenceId ? {
|
|
419
|
+
triggeringEvidenceId: args.triggeringEvidenceId,
|
|
420
|
+
triggeringEvidenceIds: [String(args.triggeringEvidenceId)]
|
|
421
|
+
} : {},
|
|
422
|
+
...args.triggeringContradictionId ? {
|
|
423
|
+
triggeringContradictionId: args.triggeringContradictionId
|
|
424
|
+
} : {},
|
|
425
|
+
...args.triggeringWorktreeId ? { triggeringWorktreeId: args.triggeringWorktreeId } : {}
|
|
426
|
+
};
|
|
427
|
+
}
|
|
428
|
+
function deriveSyntheticBackfillOpinion(source) {
|
|
429
|
+
const belief = readFiniteNumber(source.opinion_b) ?? readFiniteNumber(source.belief);
|
|
430
|
+
const disbelief = readFiniteNumber(source.opinion_d) ?? readFiniteNumber(source.disbelief);
|
|
431
|
+
const uncertainty = readFiniteNumber(source.opinion_u) ?? readFiniteNumber(source.uncertainty);
|
|
432
|
+
const baseRate = readFiniteNumber(source.opinion_a) ?? readFiniteNumber(source.baseRate);
|
|
433
|
+
if (belief !== void 0 || disbelief !== void 0 || uncertainty !== void 0 || baseRate !== void 0) {
|
|
434
|
+
try {
|
|
435
|
+
return readOpinionFromRecord(source);
|
|
436
|
+
} catch (error) {
|
|
437
|
+
debugGraphPrimitiveFallback(
|
|
438
|
+
"[epistemicBeliefs] Failed to decode legacy belief opinion",
|
|
439
|
+
{
|
|
440
|
+
error
|
|
441
|
+
}
|
|
442
|
+
);
|
|
443
|
+
return mkOpinion(0, 0, 1, 0.5);
|
|
444
|
+
}
|
|
445
|
+
}
|
|
446
|
+
const confidence = clamp01(readFiniteNumber(source.confidence) ?? 0);
|
|
447
|
+
return mkOpinion(confidence, 1 - confidence, 0, 0.5);
|
|
448
|
+
}
|
|
449
|
+
async function resolveBeliefScopeOrNull(ctx, args) {
|
|
450
|
+
if (!args.projectId && !args.topicId) {
|
|
451
|
+
return null;
|
|
452
|
+
}
|
|
453
|
+
try {
|
|
454
|
+
return await resolveTopicProjectScope(ctx, {
|
|
455
|
+
projectId: args.projectId ?? void 0,
|
|
456
|
+
topicId: args.topicId ?? void 0
|
|
457
|
+
});
|
|
458
|
+
} catch (error) {
|
|
459
|
+
debugGraphPrimitiveFallback(
|
|
460
|
+
"[epistemicBeliefs] Failed to resolve belief scope",
|
|
461
|
+
{
|
|
462
|
+
error,
|
|
463
|
+
projectId: args.projectId,
|
|
464
|
+
topicId: args.topicId
|
|
465
|
+
}
|
|
466
|
+
);
|
|
467
|
+
return null;
|
|
468
|
+
}
|
|
469
|
+
}
|
|
470
|
+
async function getBeliefNodesForScope(ctx, scope, args) {
|
|
471
|
+
const baseQuery = ctx.db.query("epistemicNodes").withIndex(
|
|
472
|
+
"by_topic_type",
|
|
473
|
+
(q) => q.eq("topicId", scope.topicId).eq("nodeType", "belief")
|
|
474
|
+
);
|
|
475
|
+
const nodes = await baseQuery.collect();
|
|
476
|
+
const scopedNodes = nodes.filter(
|
|
477
|
+
(node) => nodeMatchesWorkspaceReasoningScope(node, scope)
|
|
478
|
+
);
|
|
479
|
+
{
|
|
480
|
+
return scopedNodes;
|
|
481
|
+
}
|
|
482
|
+
}
|
|
483
|
+
function resolveBeliefStatus(node, metadata) {
|
|
484
|
+
return resolveBeliefLifecycleStatus({
|
|
485
|
+
beliefStatus: node.beliefStatus,
|
|
486
|
+
confidence: node.confidence,
|
|
487
|
+
predictionMeta: node.predictionMeta,
|
|
488
|
+
metadata
|
|
489
|
+
});
|
|
490
|
+
}
|
|
491
|
+
|
|
492
|
+
// src/epistemicBeliefs.backfills.ts
|
|
493
|
+
var getRecentConfidenceChanges = query({
|
|
494
|
+
args: {
|
|
495
|
+
...optionalBeliefScopeArgs,
|
|
496
|
+
userId: v.string(),
|
|
497
|
+
limit: v.optional(v.number())
|
|
498
|
+
},
|
|
499
|
+
returns: permissiveReturn,
|
|
500
|
+
handler: async (ctx, args) => {
|
|
501
|
+
const limit = args.limit ?? 10;
|
|
502
|
+
const sevenDaysAgo = Date.now() - 7 * 24 * 60 * 60 * 1e3;
|
|
503
|
+
const scope = await resolveBeliefScopeOrNull(ctx, args);
|
|
504
|
+
if (!scope) {
|
|
505
|
+
return [];
|
|
506
|
+
}
|
|
507
|
+
const scopeId = scope.topicId ? String(scope.topicId) : scope.projectId;
|
|
508
|
+
if (!scopeId || !await checkScopeAccess(ctx, scopeId, args.userId)) {
|
|
509
|
+
return [];
|
|
510
|
+
}
|
|
511
|
+
const projectBeliefs = await getBeliefNodesForScope(ctx, scope);
|
|
512
|
+
if (projectBeliefs.length === 0) {
|
|
513
|
+
return [];
|
|
514
|
+
}
|
|
515
|
+
const beliefMap = /* @__PURE__ */ new Map();
|
|
516
|
+
for (const belief of projectBeliefs) {
|
|
517
|
+
const info = {
|
|
518
|
+
id: belief._id,
|
|
519
|
+
title: belief.canonicalText || belief.title || "Untitled belief",
|
|
520
|
+
currentConfidence: belief.confidence ?? 0.5
|
|
521
|
+
};
|
|
522
|
+
beliefMap.set(belief._id, info);
|
|
523
|
+
}
|
|
524
|
+
const recentConfidence = await ctx.db.query("beliefConfidence").order("desc").filter((q) => q.gt(q.field("assessedAt"), sevenDaysAgo)).take(200);
|
|
525
|
+
const changes = [];
|
|
526
|
+
const seenBeliefs = /* @__PURE__ */ new Set();
|
|
527
|
+
for (const entry of recentConfidence) {
|
|
528
|
+
const beliefInfo = beliefMap.get(entry.beliefId);
|
|
529
|
+
if (!beliefInfo) {
|
|
530
|
+
continue;
|
|
531
|
+
}
|
|
532
|
+
if (seenBeliefs.has(beliefInfo.id)) {
|
|
533
|
+
continue;
|
|
534
|
+
}
|
|
535
|
+
seenBeliefs.add(beliefInfo.id);
|
|
536
|
+
const previousEntries = await ctx.db.query("beliefConfidence").withIndex("by_beliefId", (q) => q.eq("beliefId", entry.beliefId)).order("desc").filter((q) => q.lt(q.field("assessedAt"), entry.assessedAt)).take(1);
|
|
537
|
+
const previousConfidence = previousEntries[0]?.confidence ?? 0.5;
|
|
538
|
+
const delta = entry.confidence - previousConfidence;
|
|
539
|
+
if (Math.abs(delta) < 0.05) {
|
|
540
|
+
continue;
|
|
541
|
+
}
|
|
542
|
+
changes.push({
|
|
543
|
+
beliefId: beliefInfo.id,
|
|
544
|
+
beliefTitle: beliefInfo.title,
|
|
545
|
+
delta,
|
|
546
|
+
trigger: entry.trigger,
|
|
547
|
+
assessedBy: entry.assessedBy,
|
|
548
|
+
assessedAt: entry.assessedAt
|
|
549
|
+
});
|
|
550
|
+
if (changes.length >= limit) {
|
|
551
|
+
break;
|
|
552
|
+
}
|
|
553
|
+
}
|
|
554
|
+
return changes;
|
|
555
|
+
}
|
|
556
|
+
});
|
|
557
|
+
var backfillSyntheticOpinionHistory = internalMutation({
|
|
558
|
+
args: {
|
|
559
|
+
limit: v.optional(v.number())
|
|
560
|
+
},
|
|
561
|
+
returns: permissiveReturn,
|
|
562
|
+
handler: async (ctx, args) => {
|
|
563
|
+
const limit = Math.max(1, Math.min(args.limit ?? 100, 100));
|
|
564
|
+
const [beliefNodes, existingHistory] = await Promise.all([
|
|
565
|
+
ctx.db.query("epistemicNodes").withIndex("by_nodeType", (q) => q.eq("nodeType", "belief")).collect(),
|
|
566
|
+
ctx.db.query("beliefConfidence").collect()
|
|
567
|
+
]);
|
|
568
|
+
const beliefsWithHistory = new Set(
|
|
569
|
+
existingHistory.map((entry) => String(entry.beliefId))
|
|
570
|
+
);
|
|
571
|
+
let inserted = 0;
|
|
572
|
+
let skippedNoOpinion = 0;
|
|
573
|
+
let skippedHasHistory = 0;
|
|
574
|
+
for (const node of beliefNodes) {
|
|
575
|
+
if (inserted >= limit) {
|
|
576
|
+
break;
|
|
577
|
+
}
|
|
578
|
+
const hasOpinionSnapshot = readFiniteNumber(node.opinion_b) !== void 0 || readFiniteNumber(node.opinion_d) !== void 0 || readFiniteNumber(node.opinion_u) !== void 0 || readFiniteNumber(node.opinion_a) !== void 0 || readFiniteNumber(node.confidence) !== void 0;
|
|
579
|
+
if (!hasOpinionSnapshot) {
|
|
580
|
+
skippedNoOpinion++;
|
|
581
|
+
continue;
|
|
582
|
+
}
|
|
583
|
+
if (beliefsWithHistory.has(String(node._id))) {
|
|
584
|
+
skippedHasHistory++;
|
|
585
|
+
continue;
|
|
586
|
+
}
|
|
587
|
+
const opinion = deriveSyntheticBackfillOpinion(
|
|
588
|
+
node
|
|
589
|
+
);
|
|
590
|
+
await ctx.db.insert(
|
|
591
|
+
"beliefConfidence",
|
|
592
|
+
buildBeliefConfidenceRow({
|
|
593
|
+
beliefId: node._id,
|
|
594
|
+
belief: opinion.b,
|
|
595
|
+
disbelief: opinion.d,
|
|
596
|
+
uncertainty: opinion.u,
|
|
597
|
+
baseRate: opinion.a,
|
|
598
|
+
trigger: "backfill_synthetic",
|
|
599
|
+
rationale: "LK-6 backfill: synthesized t0 from node-level opinion fields (no prior beliefConfidence row found).",
|
|
600
|
+
assessedAt: readFiniteNumber(node.createdAt) ?? readFiniteNumber(node.updatedAt) ?? Date.now(),
|
|
601
|
+
assessedBy: "system:lk-6-backfill",
|
|
602
|
+
slOperator: "manual_assessment"
|
|
603
|
+
})
|
|
604
|
+
);
|
|
605
|
+
beliefsWithHistory.add(String(node._id));
|
|
606
|
+
inserted++;
|
|
607
|
+
}
|
|
608
|
+
const remainingCandidates = beliefNodes.filter((node) => {
|
|
609
|
+
const hasOpinionSnapshot = readFiniteNumber(node.opinion_b) !== void 0 || readFiniteNumber(node.opinion_d) !== void 0 || readFiniteNumber(node.opinion_u) !== void 0 || readFiniteNumber(node.opinion_a) !== void 0 || readFiniteNumber(node.confidence) !== void 0;
|
|
610
|
+
return hasOpinionSnapshot && !beliefsWithHistory.has(String(node._id));
|
|
611
|
+
}).length;
|
|
612
|
+
return {
|
|
613
|
+
scanned: beliefNodes.length,
|
|
614
|
+
inserted,
|
|
615
|
+
skippedNoOpinion,
|
|
616
|
+
skippedHasHistory,
|
|
617
|
+
remainingCandidates,
|
|
618
|
+
hasMore: remainingCandidates > 0
|
|
619
|
+
};
|
|
620
|
+
}
|
|
621
|
+
});
|
|
622
|
+
var backfillMandatoryPriors = internalMutation({
|
|
623
|
+
args: {
|
|
624
|
+
limit: v.optional(v.number()),
|
|
625
|
+
defaultBaseRate: v.optional(v.number())
|
|
626
|
+
},
|
|
627
|
+
returns: permissiveReturn,
|
|
628
|
+
handler: async (ctx, args) => {
|
|
629
|
+
const limit = Math.max(1, Math.min(args.limit ?? 100, 1e3));
|
|
630
|
+
const defaultBaseRate = assertBaseRateInRange(
|
|
631
|
+
args.defaultBaseRate ?? 0.5,
|
|
632
|
+
"defaultBaseRate"
|
|
633
|
+
);
|
|
634
|
+
const [beliefNodes, historyRows] = await Promise.all([
|
|
635
|
+
ctx.db.query("epistemicNodes").withIndex("by_nodeType", (q) => q.eq("nodeType", "belief")).collect(),
|
|
636
|
+
ctx.db.query("beliefConfidence").collect()
|
|
637
|
+
]);
|
|
638
|
+
const historyByBeliefId = /* @__PURE__ */ new Map();
|
|
639
|
+
for (const row of historyRows) {
|
|
640
|
+
const beliefId = String(row.beliefId);
|
|
641
|
+
const entries = historyByBeliefId.get(beliefId) ?? [];
|
|
642
|
+
entries.push(row);
|
|
643
|
+
historyByBeliefId.set(beliefId, entries);
|
|
644
|
+
}
|
|
645
|
+
let touched = 0;
|
|
646
|
+
let patchedNodes = 0;
|
|
647
|
+
let patchedHistoryRows = 0;
|
|
648
|
+
let insertedInitialRows = 0;
|
|
649
|
+
let auditEntries = 0;
|
|
650
|
+
for (const node of beliefNodes) {
|
|
651
|
+
if (touched >= limit) {
|
|
652
|
+
break;
|
|
653
|
+
}
|
|
654
|
+
const beliefId = String(node._id);
|
|
655
|
+
const history = historyByBeliefId.get(beliefId) ?? [];
|
|
656
|
+
const patchedOpinion = {
|
|
657
|
+
belief: readFiniteNumber(node.opinion_b) ?? 0,
|
|
658
|
+
disbelief: readFiniteNumber(node.opinion_d) ?? 0,
|
|
659
|
+
uncertainty: readFiniteNumber(node.opinion_u) ?? 1,
|
|
660
|
+
baseRate: readFiniteNumber(node.opinion_a) ?? defaultBaseRate
|
|
661
|
+
};
|
|
662
|
+
const needsNodePatch = readFiniteNumber(node.opinion_b) === void 0 || readFiniteNumber(node.opinion_d) === void 0 || readFiniteNumber(node.opinion_u) === void 0 || readFiniteNumber(node.opinion_a) === void 0;
|
|
663
|
+
const rowsMissingBaseRate = history.filter(
|
|
664
|
+
(row) => readFiniteNumber(row.baseRate) === void 0
|
|
665
|
+
);
|
|
666
|
+
const needsInitialRow = history.length === 0;
|
|
667
|
+
if (!needsNodePatch && rowsMissingBaseRate.length === 0 && !needsInitialRow) {
|
|
668
|
+
continue;
|
|
669
|
+
}
|
|
670
|
+
if (needsNodePatch) {
|
|
671
|
+
await ctx.db.patch(node._id, {
|
|
672
|
+
opinion_b: patchedOpinion.belief,
|
|
673
|
+
opinion_d: patchedOpinion.disbelief,
|
|
674
|
+
opinion_u: patchedOpinion.uncertainty,
|
|
675
|
+
opinion_a: patchedOpinion.baseRate,
|
|
676
|
+
updatedAt: Date.now()
|
|
677
|
+
});
|
|
678
|
+
patchedNodes++;
|
|
679
|
+
}
|
|
680
|
+
for (const row of rowsMissingBaseRate) {
|
|
681
|
+
const belief = readFiniteNumber(row.belief) ?? 0;
|
|
682
|
+
const disbelief = readFiniteNumber(row.disbelief) ?? 0;
|
|
683
|
+
const uncertainty = readFiniteNumber(row.uncertainty) ?? 1;
|
|
684
|
+
await ctx.db.patch(row._id, {
|
|
685
|
+
baseRate: defaultBaseRate,
|
|
686
|
+
confidence: confidenceFromSL(
|
|
687
|
+
belief,
|
|
688
|
+
disbelief,
|
|
689
|
+
uncertainty,
|
|
690
|
+
defaultBaseRate
|
|
691
|
+
)
|
|
692
|
+
});
|
|
693
|
+
patchedHistoryRows++;
|
|
694
|
+
}
|
|
695
|
+
if (needsInitialRow) {
|
|
696
|
+
await ctx.db.insert(
|
|
697
|
+
"beliefConfidence",
|
|
698
|
+
buildBeliefConfidenceRow({
|
|
699
|
+
beliefId: node._id,
|
|
700
|
+
belief: patchedOpinion.belief,
|
|
701
|
+
disbelief: patchedOpinion.disbelief,
|
|
702
|
+
uncertainty: patchedOpinion.uncertainty,
|
|
703
|
+
baseRate: patchedOpinion.baseRate,
|
|
704
|
+
trigger: "initial",
|
|
705
|
+
rationale: "LKC-2 backfill: inserted missing initial vacuous opinion with neutral prior.",
|
|
706
|
+
assessedAt: readFiniteNumber(node.createdAt) ?? readFiniteNumber(node.updatedAt) ?? Date.now(),
|
|
707
|
+
assessedBy: "system:lkc-2-prior-backfill",
|
|
708
|
+
slOperator: "manual_assessment"
|
|
709
|
+
})
|
|
710
|
+
);
|
|
711
|
+
insertedInitialRows++;
|
|
712
|
+
}
|
|
713
|
+
await ctx.db.insert("epistemicAudit", {
|
|
714
|
+
entityType: "belief",
|
|
715
|
+
entityId: beliefId,
|
|
716
|
+
changeType: "updated",
|
|
717
|
+
changedAt: Date.now(),
|
|
718
|
+
changedBy: "system:lkc-2-prior-backfill",
|
|
719
|
+
isAgent: false,
|
|
720
|
+
previousState: null,
|
|
721
|
+
newState: {
|
|
722
|
+
baseRateBackfilled: true,
|
|
723
|
+
baseRate: defaultBaseRate,
|
|
724
|
+
patchedNodeOpinion: needsNodePatch,
|
|
725
|
+
patchedHistoryRows: rowsMissingBaseRate.length,
|
|
726
|
+
insertedInitialOpinion: needsInitialRow
|
|
727
|
+
},
|
|
728
|
+
projectId: node.projectId,
|
|
729
|
+
topicId: typeof node.topicId === "string" ? node.topicId : void 0
|
|
730
|
+
});
|
|
731
|
+
auditEntries++;
|
|
732
|
+
touched++;
|
|
733
|
+
}
|
|
734
|
+
return {
|
|
735
|
+
scanned: beliefNodes.length,
|
|
736
|
+
patchedNodes,
|
|
737
|
+
patchedHistoryRows,
|
|
738
|
+
insertedInitialRows,
|
|
739
|
+
auditEntries,
|
|
740
|
+
hasMore: touched >= limit
|
|
741
|
+
};
|
|
742
|
+
}
|
|
743
|
+
});
|
|
744
|
+
var backfillTwoAxisConfidence = internalMutation({
|
|
745
|
+
args: {
|
|
746
|
+
dryRun: v.optional(v.boolean())
|
|
747
|
+
},
|
|
748
|
+
returns: permissiveReturn,
|
|
749
|
+
handler: async (ctx, args) => {
|
|
750
|
+
const dryRun = args.dryRun ?? false;
|
|
751
|
+
const allConfRecords = await ctx.db.query("beliefConfidence").collect();
|
|
752
|
+
let confUpdated = 0;
|
|
753
|
+
let confSkipped = 0;
|
|
754
|
+
for (const record of allConfRecords) {
|
|
755
|
+
if (record.valence !== void 0 && record.certainty !== void 0) {
|
|
756
|
+
confSkipped++;
|
|
757
|
+
continue;
|
|
758
|
+
}
|
|
759
|
+
const conf = record.confidence ?? 0.5;
|
|
760
|
+
const valence = Math.round((conf * 2 - 1) * 100) / 100;
|
|
761
|
+
const certainty = Math.round(Math.abs(conf - 0.5) * 2 * 100) / 100;
|
|
762
|
+
if (!dryRun) {
|
|
763
|
+
await ctx.db.patch(record._id, { valence, certainty });
|
|
764
|
+
}
|
|
765
|
+
confUpdated++;
|
|
766
|
+
}
|
|
767
|
+
const allBeliefNodes = await ctx.db.query("epistemicNodes").filter((q) => q.eq(q.field("nodeType"), "belief")).collect();
|
|
768
|
+
let nodesUpdated = 0;
|
|
769
|
+
let nodesSkipped = 0;
|
|
770
|
+
for (const node of allBeliefNodes) {
|
|
771
|
+
const meta = node.metadata || {};
|
|
772
|
+
if (meta.valence !== void 0 && meta.certainty !== void 0) {
|
|
773
|
+
nodesSkipped++;
|
|
774
|
+
continue;
|
|
775
|
+
}
|
|
776
|
+
const conf = node.confidence ?? 0.5;
|
|
777
|
+
const valence = Math.round((conf * 2 - 1) * 100) / 100;
|
|
778
|
+
const certainty = Math.round(Math.abs(conf - 0.5) * 2 * 100) / 100;
|
|
779
|
+
if (!dryRun) {
|
|
780
|
+
await ctx.db.patch(node._id, {
|
|
781
|
+
metadata: { ...meta, valence, certainty }
|
|
782
|
+
});
|
|
783
|
+
}
|
|
784
|
+
nodesUpdated++;
|
|
785
|
+
}
|
|
786
|
+
return {
|
|
787
|
+
dryRun,
|
|
788
|
+
beliefConfidence: {
|
|
789
|
+
updated: confUpdated,
|
|
790
|
+
skipped: confSkipped,
|
|
791
|
+
total: allConfRecords.length
|
|
792
|
+
},
|
|
793
|
+
epistemicNodes: {
|
|
794
|
+
updated: nodesUpdated,
|
|
795
|
+
skipped: nodesSkipped,
|
|
796
|
+
total: allBeliefNodes.length
|
|
797
|
+
}
|
|
798
|
+
};
|
|
799
|
+
}
|
|
800
|
+
});
|
|
801
|
+
var backfillScoredBeliefEdges = internalMutation({
|
|
802
|
+
args: {
|
|
803
|
+
...optionalBeliefScopeArgs,
|
|
804
|
+
dryRun: v.optional(v.boolean())
|
|
805
|
+
},
|
|
806
|
+
returns: permissiveReturn,
|
|
807
|
+
handler: async (ctx, args) => {
|
|
808
|
+
const dryRun = args.dryRun ?? false;
|
|
809
|
+
const allBeliefs = await ctx.db.query("epistemicNodes").withIndex(
|
|
810
|
+
"by_topic",
|
|
811
|
+
(q) => q.eq("topicId", args.topicId || args.projectId)
|
|
812
|
+
).filter((q) => q.eq(q.field("nodeType"), "belief")).collect();
|
|
813
|
+
const scoredBeliefs = allBeliefs.filter((belief) => {
|
|
814
|
+
const metadata = belief.metadata || {};
|
|
815
|
+
const lifecycle = resolveBeliefStatus(belief, metadata);
|
|
816
|
+
return lifecycle === "belief" || lifecycle === "fact";
|
|
817
|
+
});
|
|
818
|
+
const themeNodes = await ctx.db.query("epistemicNodes").withIndex(
|
|
819
|
+
"by_topic",
|
|
820
|
+
(q) => q.eq("topicId", args.topicId || args.projectId)
|
|
821
|
+
).filter((q) => q.eq(q.field("nodeType"), "theme")).collect();
|
|
822
|
+
let created = 0;
|
|
823
|
+
let skipped = 0;
|
|
824
|
+
for (const belief of scoredBeliefs) {
|
|
825
|
+
if (!belief.globalId) {
|
|
826
|
+
skipped++;
|
|
827
|
+
continue;
|
|
828
|
+
}
|
|
829
|
+
for (const theme of themeNodes) {
|
|
830
|
+
if (!theme.globalId) {
|
|
831
|
+
continue;
|
|
832
|
+
}
|
|
833
|
+
const edgeGlobalId = `edge-${belief.globalId}-relates_to_thesis-${theme.globalId}`;
|
|
834
|
+
const existing = await ctx.db.query("epistemicEdges").filter((q) => q.eq(q.field("globalId"), edgeGlobalId)).first();
|
|
835
|
+
if (existing) {
|
|
836
|
+
skipped++;
|
|
837
|
+
continue;
|
|
838
|
+
}
|
|
839
|
+
if (!dryRun) {
|
|
840
|
+
await ctx.scheduler.runAfter(0, internal.neo4jEdgeAPI.createEdge, {
|
|
841
|
+
globalId: edgeGlobalId,
|
|
842
|
+
fromGlobalId: belief.globalId,
|
|
843
|
+
toGlobalId: theme.globalId,
|
|
844
|
+
edgeType: "relates_to_thesis",
|
|
845
|
+
weight: belief.confidence ?? 0.5,
|
|
846
|
+
createdBy: "backfill:scored_belief_theme",
|
|
847
|
+
topicId: String(args.projectId),
|
|
848
|
+
fromNodeType: "belief",
|
|
849
|
+
toNodeType: "theme",
|
|
850
|
+
fromLayer: "L3",
|
|
851
|
+
toLayer: "L3"
|
|
852
|
+
});
|
|
853
|
+
}
|
|
854
|
+
created++;
|
|
855
|
+
}
|
|
856
|
+
}
|
|
857
|
+
return {
|
|
858
|
+
dryRun,
|
|
859
|
+
scoredBeliefs: scoredBeliefs.length,
|
|
860
|
+
themeNodes: themeNodes.length,
|
|
861
|
+
edgesCreated: created,
|
|
862
|
+
edgesSkipped: skipped
|
|
863
|
+
};
|
|
864
|
+
}
|
|
865
|
+
});
|
|
866
|
+
var backfillBeliefSprintIds = internalMutation({
|
|
867
|
+
args: {
|
|
868
|
+
...optionalBeliefScopeArgs,
|
|
869
|
+
dryRun: v.optional(v.boolean())
|
|
870
|
+
},
|
|
871
|
+
returns: permissiveReturn,
|
|
872
|
+
handler: async (_ctx, _args) => {
|
|
873
|
+
return {
|
|
874
|
+
dryRun: true,
|
|
875
|
+
message: "backfillBeliefSprintIds removed \u2014 sprints are D-tier legacy. Use worktrees.",
|
|
876
|
+
sprintsScanned: 0,
|
|
877
|
+
beliefsWithSprints: 0,
|
|
878
|
+
updated: 0,
|
|
879
|
+
skipped: 0
|
|
880
|
+
};
|
|
881
|
+
}
|
|
882
|
+
});
|
|
883
|
+
var getBeliefClusterPositions = query({
|
|
884
|
+
args: {
|
|
885
|
+
...optionalBeliefScopeArgs,
|
|
886
|
+
userId: v.optional(v.string())
|
|
887
|
+
},
|
|
888
|
+
returns: permissiveReturn,
|
|
889
|
+
handler: async (ctx, args) => {
|
|
890
|
+
const scope = await resolveBeliefScopeOrNull(ctx, args);
|
|
891
|
+
if (!scope || !scope.projectId) {
|
|
892
|
+
return {
|
|
893
|
+
positions: {},
|
|
894
|
+
hasClusters: false,
|
|
895
|
+
counts: {
|
|
896
|
+
raw: 0,
|
|
897
|
+
hypothesis: 0,
|
|
898
|
+
conditional: 0,
|
|
899
|
+
unprocessed: 0,
|
|
900
|
+
total: 0
|
|
901
|
+
}
|
|
902
|
+
};
|
|
903
|
+
}
|
|
904
|
+
const allBeliefNodes = await getBeliefNodesForScope(ctx, scope);
|
|
905
|
+
const clusteredBeliefIds = /* @__PURE__ */ new Set();
|
|
906
|
+
for (const node of allBeliefNodes) {
|
|
907
|
+
const links = await ctx.db.query("worktreeBeliefCluster").withIndex("by_belief", (q) => q.eq("beliefId", node._id)).first();
|
|
908
|
+
if (links) {
|
|
909
|
+
clusteredBeliefIds.add(String(node._id));
|
|
910
|
+
}
|
|
911
|
+
}
|
|
912
|
+
const positions = {};
|
|
913
|
+
for (const node of allBeliefNodes) {
|
|
914
|
+
const id = String(node._id);
|
|
915
|
+
positions[id] = clusteredBeliefIds.has(id) ? "hypothesis" : "unprocessed";
|
|
916
|
+
}
|
|
917
|
+
const unprocessedCount = allBeliefNodes.length - clusteredBeliefIds.size;
|
|
918
|
+
return {
|
|
919
|
+
positions,
|
|
920
|
+
hasClusters: clusteredBeliefIds.size > 0,
|
|
921
|
+
counts: {
|
|
922
|
+
raw: 0,
|
|
923
|
+
hypothesis: clusteredBeliefIds.size,
|
|
924
|
+
conditional: 0,
|
|
925
|
+
unprocessed: unprocessedCount,
|
|
926
|
+
total: allBeliefNodes.length
|
|
927
|
+
}
|
|
928
|
+
};
|
|
929
|
+
}
|
|
930
|
+
});
|
|
931
|
+
var reassignBeliefsTopic = mutation({
|
|
932
|
+
args: {
|
|
933
|
+
beliefNodeIds: v.array(v.id("epistemicNodes")),
|
|
934
|
+
targetTopicId: v.string(),
|
|
935
|
+
reason: v.optional(v.string())
|
|
936
|
+
},
|
|
937
|
+
returns: permissiveReturn,
|
|
938
|
+
handler: async (ctx, args) => {
|
|
939
|
+
const targetTopic = await ctx.db.get(args.targetTopicId);
|
|
940
|
+
if (!targetTopic) {
|
|
941
|
+
throw new Error(`Target topic not found: ${args.targetTopicId}`);
|
|
942
|
+
}
|
|
943
|
+
let reassigned = 0;
|
|
944
|
+
let skipped = 0;
|
|
945
|
+
const now = Date.now();
|
|
946
|
+
const movedBeliefIds = /* @__PURE__ */ new Set();
|
|
947
|
+
for (const beliefNodeId of args.beliefNodeIds) {
|
|
948
|
+
const node = await ctx.db.get(beliefNodeId);
|
|
949
|
+
if (!node || node.nodeType !== "belief") {
|
|
950
|
+
skipped++;
|
|
951
|
+
continue;
|
|
952
|
+
}
|
|
953
|
+
await ctx.db.patch(beliefNodeId, {
|
|
954
|
+
topicId: args.targetTopicId,
|
|
955
|
+
updatedAt: now
|
|
956
|
+
});
|
|
957
|
+
movedBeliefIds.add(String(beliefNodeId));
|
|
958
|
+
reassigned++;
|
|
959
|
+
}
|
|
960
|
+
let connectedReassigned = 0;
|
|
961
|
+
const alreadyReassigned = /* @__PURE__ */ new Set();
|
|
962
|
+
for (const beliefId of movedBeliefIds) {
|
|
963
|
+
const outbound = await ctx.db.query("epistemicEdges").withIndex("by_from", (q) => q.eq("fromNodeId", beliefId)).collect();
|
|
964
|
+
const inbound = await ctx.db.query("epistemicEdges").withIndex("by_to", (q) => q.eq("toNodeId", beliefId)).collect();
|
|
965
|
+
const connectedNodeIds = /* @__PURE__ */ new Set();
|
|
966
|
+
for (const edge of outbound) {
|
|
967
|
+
if (edge.toNodeId) connectedNodeIds.add(String(edge.toNodeId));
|
|
968
|
+
}
|
|
969
|
+
for (const edge of inbound) {
|
|
970
|
+
if (edge.fromNodeId) connectedNodeIds.add(String(edge.fromNodeId));
|
|
971
|
+
}
|
|
972
|
+
for (const connectedId of connectedNodeIds) {
|
|
973
|
+
if (movedBeliefIds.has(connectedId) || alreadyReassigned.has(connectedId)) {
|
|
974
|
+
continue;
|
|
975
|
+
}
|
|
976
|
+
try {
|
|
977
|
+
const connectedNode = await ctx.db.get(connectedId);
|
|
978
|
+
if (!connectedNode || connectedNode.status !== "active") continue;
|
|
979
|
+
if (connectedNode.topicId && String(connectedNode.topicId) !== String(args.targetTopicId)) {
|
|
980
|
+
await ctx.db.patch(connectedId, {
|
|
981
|
+
topicId: args.targetTopicId,
|
|
982
|
+
updatedAt: now
|
|
983
|
+
});
|
|
984
|
+
alreadyReassigned.add(connectedId);
|
|
985
|
+
connectedReassigned++;
|
|
986
|
+
}
|
|
987
|
+
} catch (error) {
|
|
988
|
+
debugGraphPrimitiveFallback(
|
|
989
|
+
"[epistemicBeliefs] Failed to reassign connected node",
|
|
990
|
+
{
|
|
991
|
+
error,
|
|
992
|
+
connectedId
|
|
993
|
+
}
|
|
994
|
+
);
|
|
995
|
+
}
|
|
996
|
+
}
|
|
997
|
+
}
|
|
998
|
+
return { reassigned, skipped, connectedReassigned };
|
|
999
|
+
}
|
|
1000
|
+
});
|
|
1001
|
+
|
|
1002
|
+
export { backfillBeliefSprintIds, backfillMandatoryPriors, backfillScoredBeliefEdges, backfillSyntheticOpinionHistory, backfillTwoAxisConfidence, getBeliefClusterPositions, getRecentConfidenceChanges, reassignBeliefsTopic };
|
|
1003
|
+
//# sourceMappingURL=epistemicBeliefs.backfills.js.map
|
|
1004
|
+
//# sourceMappingURL=epistemicBeliefs.backfills.js.map
|