@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
package/dist/epistemicBeliefs.js
CHANGED
|
@@ -2,14 +2,14 @@ import { v } from 'convex/values';
|
|
|
2
2
|
import { normalizeTupleContradictionPolicy, mkOpinion, createInheritedContractRecord, confidenceFromSL, conditionalDeduction, project, dampedDependencyCascade, hasProjectedOpinionChanged, detectTupleContradiction, evaluateTupleContradictionTransition, readOpinionFromRecord, trustDiscount, applyNegativeSupport, cumulativeFusion, applyNegativeEvidence } from '@lucern/confidence';
|
|
3
3
|
import { checkScopeAccess, checkProjectAccess } from '@lucern/access-control/access';
|
|
4
4
|
import { canAudienceClassAccess, normalizeAudienceKey, classFromAudienceKey } from '@lucern/access-control/audience';
|
|
5
|
-
import { listAudienceRegistryRows } from '@lucern/access-control/audienceRegistry';
|
|
6
5
|
import { getCurrentUserId } from '@lucern/access-control/auth';
|
|
7
|
-
import { assertSchemaEnumValue } from '@lucern/contracts/schema-helpers/enumValidation';
|
|
8
|
-
import { permissiveReturn } from '@lucern/contracts/schema-helpers/validators';
|
|
9
6
|
import { componentsGeneric, anyApi, internalMutationGeneric, mutationGeneric, queryGeneric, internalQueryGeneric } from 'convex/server';
|
|
10
7
|
import { isNodeType, getLayerForNodeType } from '@lucern/contracts/schema-helpers/spine/tables/epistemicNodes';
|
|
8
|
+
import { permissiveReturn } from '@lucern/contracts/schema-helpers/validators';
|
|
9
|
+
import { assertSchemaEnumValue } from '@lucern/contracts/schema-helpers/enumValidation';
|
|
10
|
+
import { listAudienceRegistryRows } from '@lucern/access-control/audienceRegistry';
|
|
11
11
|
|
|
12
|
-
// src/epistemicBeliefs.ts
|
|
12
|
+
// src/epistemicBeliefs.helpers.ts
|
|
13
13
|
|
|
14
14
|
// src/beliefLifecycle.ts
|
|
15
15
|
var BELIEF_STATUS_VALUES = [
|
|
@@ -110,375 +110,417 @@ var internalQuery = internalQueryGeneric;
|
|
|
110
110
|
var mutation = mutationGeneric;
|
|
111
111
|
var query = queryGeneric;
|
|
112
112
|
|
|
113
|
-
// src/
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
transitivity: "none",
|
|
118
|
-
damping: 1,
|
|
119
|
-
maxHops: 1,
|
|
120
|
-
operator: () => null,
|
|
121
|
-
description: "Structural containment only. Traversed for explicit semantics, but it never propagates opinions."
|
|
122
|
-
};
|
|
123
|
-
function readEdgeMetadata(edge) {
|
|
124
|
-
return {
|
|
125
|
-
constraint: edge.constraint ?? void 0,
|
|
126
|
-
normalization: edge.normalization ?? void 0,
|
|
127
|
-
propagation: edge.propagation ?? void 0,
|
|
128
|
-
conditionalA: edge.conditionalA ?? void 0,
|
|
129
|
-
conditionalNotA: edge.conditionalNotA ?? void 0
|
|
130
|
-
};
|
|
113
|
+
// src/debug.ts
|
|
114
|
+
function isGraphPrimitiveDebugEnabled() {
|
|
115
|
+
const env = globalThis.process?.env;
|
|
116
|
+
return env?.LUCERN_COMPAT_FALLBACK_DEBUG === "1" || env?.LUCERN_GRAPH_DEBUG === "1";
|
|
131
117
|
}
|
|
132
|
-
function
|
|
133
|
-
if (
|
|
134
|
-
return
|
|
118
|
+
function debugGraphPrimitiveFallback(message, context) {
|
|
119
|
+
if (!isGraphPrimitiveDebugEnabled()) {
|
|
120
|
+
return;
|
|
135
121
|
}
|
|
136
|
-
|
|
122
|
+
console.debug(message, context ?? {});
|
|
137
123
|
}
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
}
|
|
124
|
+
|
|
125
|
+
// src/topicProjectOverlay.ts
|
|
126
|
+
var LEGACY_SCOPE_FIELD = "graphScopeProjectId";
|
|
127
|
+
function readNonEmptyString(value) {
|
|
128
|
+
if (typeof value !== "string") {
|
|
129
|
+
return;
|
|
130
|
+
}
|
|
131
|
+
const normalized = value.trim();
|
|
132
|
+
return normalized.length > 0 ? normalized : void 0;
|
|
145
133
|
}
|
|
146
|
-
function
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
rationale: `Supporting evidence (weight=${edgeWeight.toFixed(
|
|
152
|
-
2
|
|
153
|
-
)}) from source at ${project(sourceOpinion).toFixed(2)}`
|
|
154
|
-
};
|
|
134
|
+
function readStringArray(value) {
|
|
135
|
+
if (!Array.isArray(value)) {
|
|
136
|
+
return [];
|
|
137
|
+
}
|
|
138
|
+
return value.map((entry) => readNonEmptyString(entry)).filter((entry) => Boolean(entry));
|
|
155
139
|
}
|
|
156
|
-
function
|
|
157
|
-
|
|
158
|
-
return {
|
|
159
|
-
opinion: cumulativeFusion(targetOpinion, discounted),
|
|
160
|
-
operator: "cumulative_fusion",
|
|
161
|
-
rationale: `Supporting evidence (weight=${edgeWeight.toFixed(2)})`
|
|
162
|
-
};
|
|
140
|
+
function readMetadata(topic) {
|
|
141
|
+
return topic.metadata && typeof topic.metadata === "object" ? topic.metadata : {};
|
|
163
142
|
}
|
|
164
|
-
function
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
readEdgeMetadata(edge)
|
|
170
|
-
);
|
|
143
|
+
function readLegacyProjectId(value) {
|
|
144
|
+
if (!value) {
|
|
145
|
+
return;
|
|
146
|
+
}
|
|
147
|
+
return readNonEmptyString(value[LEGACY_SCOPE_FIELD]);
|
|
171
148
|
}
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
const
|
|
211
|
-
|
|
212
|
-
mkOpinion(
|
|
213
|
-
metadata.conditionalA.b,
|
|
214
|
-
metadata.conditionalA.d,
|
|
215
|
-
metadata.conditionalA.u,
|
|
216
|
-
metadata.conditionalA.a
|
|
217
|
-
),
|
|
218
|
-
mkOpinion(
|
|
219
|
-
metadata.conditionalNotA.b,
|
|
220
|
-
metadata.conditionalNotA.d,
|
|
221
|
-
metadata.conditionalNotA.u,
|
|
222
|
-
metadata.conditionalNotA.a
|
|
223
|
-
),
|
|
224
|
-
targetOpinion.a
|
|
149
|
+
function coerceVisibility(value) {
|
|
150
|
+
return value === "private" || value === "team" || value === "firm" || value === "external" || value === "public" ? value : void 0;
|
|
151
|
+
}
|
|
152
|
+
function coerceStatus(value) {
|
|
153
|
+
return value === "active" || value === "archived" || value === "watching" ? value : void 0;
|
|
154
|
+
}
|
|
155
|
+
function mapProjectType(topic, metadata) {
|
|
156
|
+
const explicit = readNonEmptyString(metadata.projectType);
|
|
157
|
+
if (explicit) {
|
|
158
|
+
return explicit;
|
|
159
|
+
}
|
|
160
|
+
if (topic.type === "theme") {
|
|
161
|
+
return "thematic";
|
|
162
|
+
}
|
|
163
|
+
return readNonEmptyString(topic.type) || "general";
|
|
164
|
+
}
|
|
165
|
+
function isProjectLikeTopic(topic) {
|
|
166
|
+
const metadata = readMetadata(topic);
|
|
167
|
+
return topic.type === "theme" || topic.type === "thematic" || topic.type === "deal" || topic.type === "monitoring" || readLegacyProjectId(topic) !== void 0 || readNonEmptyString(metadata.projectType) !== void 0;
|
|
168
|
+
}
|
|
169
|
+
function isMissingLucernChildComponentError(error) {
|
|
170
|
+
const message = getErrorMessage(error);
|
|
171
|
+
return message.includes(
|
|
172
|
+
'Child component ComponentName(Identifier("lucern")) not found'
|
|
173
|
+
) || message.includes("Child component") && message.includes("lucern") && message.includes("not found");
|
|
174
|
+
}
|
|
175
|
+
function getErrorMessage(error) {
|
|
176
|
+
if (error instanceof Error) {
|
|
177
|
+
return error.message;
|
|
178
|
+
}
|
|
179
|
+
if (typeof error === "object" && error !== null && "message" in error && typeof error.message === "string") {
|
|
180
|
+
return error.message;
|
|
181
|
+
}
|
|
182
|
+
return "unknown error";
|
|
183
|
+
}
|
|
184
|
+
async function resolveTopicDoc(ctx, scopeId) {
|
|
185
|
+
if (ctx?.db && typeof ctx.db.get === "function") {
|
|
186
|
+
try {
|
|
187
|
+
const directTopic = await ctx.db.get(
|
|
188
|
+
scopeId
|
|
225
189
|
);
|
|
226
|
-
|
|
190
|
+
if (directTopic) {
|
|
191
|
+
return directTopic;
|
|
192
|
+
}
|
|
193
|
+
} catch (error) {
|
|
194
|
+
debugGraphPrimitiveFallback(
|
|
195
|
+
"[topicProjectOverlay] Failed to resolve topic by direct ID",
|
|
227
196
|
{
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
dampedSource
|
|
232
|
-
).toFixed(2)}`
|
|
233
|
-
},
|
|
234
|
-
context.spec,
|
|
235
|
-
context.hop
|
|
197
|
+
error,
|
|
198
|
+
scopeId
|
|
199
|
+
}
|
|
236
200
|
);
|
|
237
201
|
}
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
description: "Provenance only. The traversal surface stays explicit, but confidence does not move across derived_from edges."
|
|
257
|
-
};
|
|
258
|
-
|
|
259
|
-
// src/edges/elaborates.ts
|
|
260
|
-
var elaboratesPropagationSpec = {
|
|
261
|
-
edgeType: "elaborates",
|
|
262
|
-
direction: "outgoing",
|
|
263
|
-
transitivity: "damped",
|
|
264
|
-
damping: 0.7,
|
|
265
|
-
maxHops: 2,
|
|
266
|
-
operator: (sourceOpinion, targetOpinion, edge, context) => {
|
|
267
|
-
const dampedSource = applyPerHopDamping(
|
|
268
|
-
sourceOpinion,
|
|
269
|
-
context.spec.damping
|
|
270
|
-
);
|
|
271
|
-
const contextualWeight = Math.min(Math.abs(edge.weight ?? 0.35), 0.35);
|
|
272
|
-
const result = propagatePositiveInform(
|
|
273
|
-
dampedSource,
|
|
274
|
-
targetOpinion,
|
|
275
|
-
contextualWeight
|
|
202
|
+
}
|
|
203
|
+
if (typeof ctx.runQuery !== "function") {
|
|
204
|
+
return null;
|
|
205
|
+
}
|
|
206
|
+
try {
|
|
207
|
+
const topic = await ctx.runQuery(api.topics.get, {
|
|
208
|
+
id: String(scopeId)
|
|
209
|
+
});
|
|
210
|
+
if (topic?.name !== void 0 && topic?.type !== void 0) {
|
|
211
|
+
return topic;
|
|
212
|
+
}
|
|
213
|
+
} catch (error) {
|
|
214
|
+
debugGraphPrimitiveFallback(
|
|
215
|
+
"[topicProjectOverlay] Failed to resolve topic by ID query",
|
|
216
|
+
{
|
|
217
|
+
error,
|
|
218
|
+
scopeId
|
|
219
|
+
}
|
|
276
220
|
);
|
|
277
|
-
return annotateRationale(result, context.spec, context.hop);
|
|
278
|
-
},
|
|
279
|
-
description: "Context-rich supporting detail. Elaborates carries a small positive effect with short, damped chaining."
|
|
280
|
-
};
|
|
281
|
-
|
|
282
|
-
// src/edges/informs.ts
|
|
283
|
-
var informsPropagationSpec = {
|
|
284
|
-
edgeType: "informs",
|
|
285
|
-
direction: "outgoing",
|
|
286
|
-
transitivity: "full",
|
|
287
|
-
damping: 0.92,
|
|
288
|
-
maxHops: "unbounded",
|
|
289
|
-
operator: (sourceOpinion, targetOpinion, edge, context) => {
|
|
290
|
-
const dampedSource = applyPerHopDamping(
|
|
291
|
-
sourceOpinion,
|
|
292
|
-
context.spec.damping
|
|
293
|
-
);
|
|
294
|
-
const weight = edge.weight ?? 1;
|
|
295
|
-
const result = weight < 0 ? propagateNegativeInform(dampedSource, targetOpinion, weight) : propagatePositiveInform(dampedSource, targetOpinion, weight);
|
|
296
|
-
return annotateRationale(result, context.spec, context.hop);
|
|
297
|
-
},
|
|
298
|
-
description: "Evidence-bearing influence. Informs can chain through the graph with light per-hop damping."
|
|
299
|
-
};
|
|
300
|
-
|
|
301
|
-
// src/edges/propagationTypes.ts
|
|
302
|
-
function isPropagationTraversalDirection(direction) {
|
|
303
|
-
return direction === "outgoing" || direction === "incoming";
|
|
304
|
-
}
|
|
305
|
-
function canTraverseHop(spec, nextHop) {
|
|
306
|
-
return spec.maxHops === "unbounded" || nextHop <= spec.maxHops;
|
|
307
|
-
}
|
|
308
|
-
function canContinueTransitively(spec, currentHop) {
|
|
309
|
-
if (spec.transitivity === "none") {
|
|
310
|
-
return false;
|
|
311
221
|
}
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
const dampedSource = applyPerHopDamping(
|
|
324
|
-
sourceOpinion,
|
|
325
|
-
context.spec.damping
|
|
326
|
-
);
|
|
327
|
-
const negativeWeight = -Math.abs(edge.weight ?? 1);
|
|
328
|
-
const result = propagateNegativeInform(
|
|
329
|
-
dampedSource,
|
|
330
|
-
targetOpinion,
|
|
331
|
-
negativeWeight
|
|
332
|
-
);
|
|
333
|
-
return annotateRationale(result, context.spec, context.hop);
|
|
334
|
-
},
|
|
335
|
-
description: "Explicit negative evidence semantics. Refutes is treated as strong one-hop counter-evidence."
|
|
336
|
-
};
|
|
337
|
-
|
|
338
|
-
// src/edges/supports.ts
|
|
339
|
-
var supportsPropagationSpec = {
|
|
340
|
-
edgeType: "supports",
|
|
341
|
-
direction: "outgoing",
|
|
342
|
-
transitivity: "full",
|
|
343
|
-
damping: 0.85,
|
|
344
|
-
maxHops: "unbounded",
|
|
345
|
-
operator: (sourceOpinion, targetOpinion, edge, context) => {
|
|
346
|
-
const dampedSource = applyPerHopDamping(
|
|
347
|
-
sourceOpinion,
|
|
348
|
-
context.spec.damping
|
|
222
|
+
try {
|
|
223
|
+
const topic = await ctx.runQuery(api.topics.getByLegacyScopeId, {
|
|
224
|
+
projectId: String(scopeId)
|
|
225
|
+
});
|
|
226
|
+
if (topic?.name !== void 0 && topic?.type !== void 0) {
|
|
227
|
+
return topic;
|
|
228
|
+
}
|
|
229
|
+
} catch (error) {
|
|
230
|
+
debugGraphPrimitiveFallback(
|
|
231
|
+
"[topicProjectOverlay] Failed to resolve topic by legacy scope ID",
|
|
232
|
+
{ error, scopeId }
|
|
349
233
|
);
|
|
350
|
-
const weight = edge.weight ?? 1;
|
|
351
|
-
const result = weight < 0 ? propagateNegativeSupportWithMetadata(
|
|
352
|
-
dampedSource,
|
|
353
|
-
targetOpinion,
|
|
354
|
-
weight,
|
|
355
|
-
edge
|
|
356
|
-
) : propagatePositiveSupport(dampedSource, targetOpinion, weight);
|
|
357
|
-
return annotateRationale(result, context.spec, context.hop);
|
|
358
|
-
},
|
|
359
|
-
description: "Belief-to-belief influence. Supports chains transitively with moderate per-hop damping."
|
|
360
|
-
};
|
|
361
|
-
|
|
362
|
-
// src/edges/tests.ts
|
|
363
|
-
var testsPropagationSpec = {
|
|
364
|
-
edgeType: "tests",
|
|
365
|
-
direction: "outgoing",
|
|
366
|
-
transitivity: "none",
|
|
367
|
-
damping: 1,
|
|
368
|
-
maxHops: 1,
|
|
369
|
-
operator: () => null,
|
|
370
|
-
description: "Interrogation linkage only. Tests edges do not directly move confidence."
|
|
371
|
-
};
|
|
372
|
-
|
|
373
|
-
// src/edges/index.ts
|
|
374
|
-
var EDGE_PROPAGATION_SPECS = [
|
|
375
|
-
supportsPropagationSpec,
|
|
376
|
-
informsPropagationSpec,
|
|
377
|
-
dependsOnPropagationSpec,
|
|
378
|
-
derivedFromPropagationSpec,
|
|
379
|
-
containsPropagationSpec,
|
|
380
|
-
testsPropagationSpec,
|
|
381
|
-
contradictsPropagationSpec,
|
|
382
|
-
refutesPropagationSpec,
|
|
383
|
-
elaboratesPropagationSpec
|
|
384
|
-
];
|
|
385
|
-
new Map(EDGE_PROPAGATION_SPECS.map((spec) => [spec.edgeType, spec]));
|
|
386
|
-
function getEdgePropagationSpecs() {
|
|
387
|
-
return EDGE_PROPAGATION_SPECS;
|
|
388
|
-
}
|
|
389
|
-
function getTraversalDirections(direction) {
|
|
390
|
-
if (isPropagationTraversalDirection(direction)) {
|
|
391
|
-
return [direction];
|
|
392
234
|
}
|
|
393
|
-
return
|
|
394
|
-
}
|
|
395
|
-
|
|
396
|
-
// src/debug.ts
|
|
397
|
-
function isGraphPrimitiveDebugEnabled() {
|
|
398
|
-
const env = globalThis.process?.env;
|
|
399
|
-
return env?.LUCERN_COMPAT_FALLBACK_DEBUG === "1" || env?.LUCERN_GRAPH_DEBUG === "1";
|
|
235
|
+
return null;
|
|
400
236
|
}
|
|
401
|
-
function
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
237
|
+
function materializeTopicProjectOverlay(topic, idMode = "legacy") {
|
|
238
|
+
const metadata = readMetadata(topic);
|
|
239
|
+
const topicId = String(topic._id);
|
|
240
|
+
const legacyProjectId = readLegacyProjectId(topic) || readLegacyProjectId(metadata) || readNonEmptyString(metadata.legacyProjectId);
|
|
241
|
+
const storageProjectId = legacyProjectId || topicId;
|
|
242
|
+
const outwardId = idMode === "topic" ? topicId : storageProjectId;
|
|
243
|
+
const visibility = coerceVisibility(topic.visibility) || coerceVisibility(metadata.visibility) || "private";
|
|
244
|
+
const status = coerceStatus(topic.status) || coerceStatus(metadata.status) || "active";
|
|
245
|
+
const createdAt = typeof topic.createdAt === "number" ? topic.createdAt : typeof topic._creationTime === "number" ? topic._creationTime : 0;
|
|
246
|
+
const updatedAt = typeof topic.updatedAt === "number" ? topic.updatedAt : typeof metadata.updatedAt === "number" ? metadata.updatedAt : createdAt;
|
|
247
|
+
return {
|
|
248
|
+
...metadata,
|
|
249
|
+
_id: outwardId,
|
|
250
|
+
projectId: outwardId,
|
|
251
|
+
topicId,
|
|
252
|
+
storageProjectId,
|
|
253
|
+
legacyProjectId,
|
|
254
|
+
name: readNonEmptyString(topic.name) || "Untitled Theme",
|
|
255
|
+
type: mapProjectType(topic, metadata),
|
|
256
|
+
description: readNonEmptyString(topic.description),
|
|
257
|
+
ownerId: readNonEmptyString(metadata.ownerId) || readNonEmptyString(topic.createdBy) || "system",
|
|
258
|
+
sharedWith: readStringArray(metadata.sharedWith),
|
|
259
|
+
visibility,
|
|
260
|
+
tenantId: readNonEmptyString(topic.tenantId) || readNonEmptyString(metadata.tenantId),
|
|
261
|
+
workspaceId: readNonEmptyString(topic.workspaceId) || readNonEmptyString(metadata.workspaceId),
|
|
262
|
+
status,
|
|
263
|
+
tags: readStringArray(metadata.tags),
|
|
264
|
+
chatCount: typeof metadata.chatCount === "number" ? metadata.chatCount : 0,
|
|
265
|
+
artifactCount: typeof metadata.artifactCount === "number" ? metadata.artifactCount : 0,
|
|
266
|
+
lastActivityAt: typeof metadata.lastActivityAt === "number" ? metadata.lastActivityAt : updatedAt,
|
|
267
|
+
_creationTime: typeof topic._creationTime === "number" ? topic._creationTime : createdAt,
|
|
268
|
+
createdAt,
|
|
269
|
+
updatedAt
|
|
270
|
+
};
|
|
406
271
|
}
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
var LEGACY_SCOPE_FIELD = "graphScopeProjectId";
|
|
410
|
-
function asMappedProjectId(topic) {
|
|
272
|
+
async function resolveTopicProjectOverlay(ctx, scopeId, options = {}) {
|
|
273
|
+
const topic = await resolveTopicDoc(ctx, scopeId);
|
|
411
274
|
if (!topic) {
|
|
412
|
-
return;
|
|
413
|
-
}
|
|
414
|
-
const directLegacyProjectId = normalizeScopeValue(topic[LEGACY_SCOPE_FIELD]);
|
|
415
|
-
if (directLegacyProjectId) {
|
|
416
|
-
return directLegacyProjectId;
|
|
275
|
+
return null;
|
|
417
276
|
}
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
return candidate ? candidate : void 0;
|
|
421
|
-
}
|
|
422
|
-
function normalizeScopeValue(value) {
|
|
423
|
-
if (typeof value !== "string") {
|
|
424
|
-
return;
|
|
277
|
+
if (options.projectLikeOnly !== false && !isProjectLikeTopic(topic)) {
|
|
278
|
+
return null;
|
|
425
279
|
}
|
|
426
|
-
|
|
427
|
-
return normalized.length > 0 ? normalized : void 0;
|
|
280
|
+
return materializeTopicProjectOverlay(topic, options.idMode);
|
|
428
281
|
}
|
|
429
|
-
function
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
282
|
+
async function listTopicProjectOverlays(ctx, options = {}) {
|
|
283
|
+
let allTopics = [];
|
|
284
|
+
if (ctx?.db?.query && typeof ctx.db.query === "function") {
|
|
285
|
+
try {
|
|
286
|
+
allTopics = await ctx.db.query("topics").collect();
|
|
287
|
+
} catch (error) {
|
|
288
|
+
debugGraphPrimitiveFallback(
|
|
289
|
+
"[topicProjectOverlay] Failed to read topics table; falling back to API",
|
|
290
|
+
{ error }
|
|
291
|
+
);
|
|
292
|
+
allTopics = [];
|
|
440
293
|
}
|
|
441
|
-
return String(a.name || "").localeCompare(String(b.name || ""));
|
|
442
|
-
})[0];
|
|
443
|
-
}
|
|
444
|
-
async function findTopicsByScopeAlias(ctx, scopeId) {
|
|
445
|
-
try {
|
|
446
|
-
return await ctx.db.query("topics").withIndex(
|
|
447
|
-
"by_graph_scope_project",
|
|
448
|
-
(q) => q.eq(LEGACY_SCOPE_FIELD, scopeId)
|
|
449
|
-
).collect();
|
|
450
|
-
} catch (error) {
|
|
451
|
-
debugGraphPrimitiveFallback(
|
|
452
|
-
"[topicScope] Failed to resolve scope alias via index",
|
|
453
|
-
{
|
|
454
|
-
error,
|
|
455
|
-
scopeId
|
|
456
|
-
}
|
|
457
|
-
);
|
|
458
|
-
const topics = await ctx.db.query("topics").collect();
|
|
459
|
-
return topics.filter((topic) => {
|
|
460
|
-
const normalizedGlobalId = normalizeScopeValue(topic.globalId);
|
|
461
|
-
const mappedProjectId = asMappedProjectId(topic);
|
|
462
|
-
return String(topic._id) === scopeId || normalizedGlobalId === scopeId || mappedProjectId === scopeId;
|
|
463
|
-
});
|
|
464
294
|
}
|
|
295
|
+
if (allTopics.length === 0 && typeof ctx.runQuery === "function") {
|
|
296
|
+
allTopics = (await ctx.runQuery(api.topics.list, {}) ?? []) || [];
|
|
297
|
+
}
|
|
298
|
+
return allTopics.filter(
|
|
299
|
+
(topic) => options.projectLikeOnly === false || isProjectLikeTopic(topic)
|
|
300
|
+
).map((topic) => materializeTopicProjectOverlay(topic, options.idMode));
|
|
465
301
|
}
|
|
466
|
-
async function
|
|
467
|
-
|
|
302
|
+
async function patchTopicProjectOverlay(ctx, scopeId, value) {
|
|
303
|
+
const topic = await resolveTopicDoc(ctx, scopeId);
|
|
304
|
+
if (!topic) {
|
|
468
305
|
return null;
|
|
469
306
|
}
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
}
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
307
|
+
const nextMetadata = { ...readMetadata(topic) };
|
|
308
|
+
const patch = {};
|
|
309
|
+
const topicUpdateArgs = {
|
|
310
|
+
id: String(topic._id)
|
|
311
|
+
};
|
|
312
|
+
for (const [key, rawValue] of Object.entries(value)) {
|
|
313
|
+
switch (key) {
|
|
314
|
+
case "_id":
|
|
315
|
+
case "projectId":
|
|
316
|
+
case "topicId":
|
|
317
|
+
case "legacyProjectId":
|
|
318
|
+
case "storageProjectId":
|
|
319
|
+
break;
|
|
320
|
+
case "name":
|
|
321
|
+
case "description":
|
|
322
|
+
patch[key] = rawValue;
|
|
323
|
+
topicUpdateArgs[key] = rawValue;
|
|
324
|
+
break;
|
|
325
|
+
case "tenantId":
|
|
326
|
+
case "workspaceId":
|
|
327
|
+
case "ownerId":
|
|
328
|
+
throw new Error(
|
|
329
|
+
`patchTopicProjectOverlay cannot mutate ${key} via component-owned topics`
|
|
330
|
+
);
|
|
331
|
+
case "status": {
|
|
332
|
+
const status = coerceStatus(rawValue);
|
|
333
|
+
if (status) {
|
|
334
|
+
patch.status = status;
|
|
335
|
+
topicUpdateArgs.status = status;
|
|
336
|
+
}
|
|
337
|
+
break;
|
|
338
|
+
}
|
|
339
|
+
case "visibility": {
|
|
340
|
+
const visibility = coerceVisibility(rawValue);
|
|
341
|
+
if (visibility) {
|
|
342
|
+
patch.visibility = visibility;
|
|
343
|
+
topicUpdateArgs.visibility = visibility;
|
|
344
|
+
}
|
|
345
|
+
break;
|
|
346
|
+
}
|
|
347
|
+
case "type": {
|
|
348
|
+
const projectType = readNonEmptyString(rawValue);
|
|
349
|
+
if (projectType) {
|
|
350
|
+
nextMetadata.projectType = projectType;
|
|
351
|
+
} else {
|
|
352
|
+
delete nextMetadata.projectType;
|
|
353
|
+
}
|
|
354
|
+
break;
|
|
355
|
+
}
|
|
356
|
+
case "updatedAt":
|
|
357
|
+
case "createdAt":
|
|
358
|
+
break;
|
|
359
|
+
default:
|
|
360
|
+
if (rawValue === void 0) {
|
|
361
|
+
delete nextMetadata[key];
|
|
362
|
+
} else {
|
|
363
|
+
nextMetadata[key] = rawValue;
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
patch.updatedAt = Date.now();
|
|
368
|
+
patch.metadata = nextMetadata;
|
|
369
|
+
topicUpdateArgs.metadata = nextMetadata;
|
|
370
|
+
if (typeof ctx.runMutation === "function") {
|
|
371
|
+
try {
|
|
372
|
+
await ctx.runMutation(api.topics.update, topicUpdateArgs);
|
|
373
|
+
} catch (error) {
|
|
374
|
+
if (!isMissingLucernChildComponentError(error) || !ctx?.db || typeof ctx.db.patch !== "function") {
|
|
375
|
+
throw error;
|
|
376
|
+
}
|
|
377
|
+
await ctx.db.patch(String(topic._id), patch);
|
|
378
|
+
}
|
|
379
|
+
} else if (ctx?.db && typeof ctx.db.patch === "function") {
|
|
380
|
+
await ctx.db.patch(String(topic._id), patch);
|
|
381
|
+
} else {
|
|
382
|
+
throw new Error(
|
|
383
|
+
"Cannot patch topic without component adapter (ctx.runMutation unavailable)"
|
|
384
|
+
);
|
|
385
|
+
}
|
|
386
|
+
return materializeTopicProjectOverlay({
|
|
387
|
+
...topic,
|
|
388
|
+
...patch,
|
|
389
|
+
metadata: nextMetadata
|
|
390
|
+
});
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
// src/resolvers.ts
|
|
394
|
+
function isMissingLucernChildComponentError2(error) {
|
|
395
|
+
const message = getErrorMessage2(error);
|
|
396
|
+
return message.includes(
|
|
397
|
+
'Child component ComponentName(Identifier("lucern")) not found'
|
|
398
|
+
) || message.includes("Child component") && message.includes("lucern") && message.includes("not found");
|
|
399
|
+
}
|
|
400
|
+
function getErrorMessage2(error) {
|
|
401
|
+
if (error instanceof Error) {
|
|
402
|
+
return error.message;
|
|
403
|
+
}
|
|
404
|
+
if (typeof error === "object" && error !== null && "message" in error && typeof error.message === "string") {
|
|
405
|
+
return error.message;
|
|
406
|
+
}
|
|
407
|
+
return "unknown error";
|
|
408
|
+
}
|
|
409
|
+
function isAdvisoryTopicPatch(value) {
|
|
410
|
+
const advisoryKeys = /* @__PURE__ */ new Set(["lastActivityAt", "updatedAt"]);
|
|
411
|
+
const keys = Object.keys(value);
|
|
412
|
+
return keys.length > 0 && keys.every((key) => advisoryKeys.has(key));
|
|
413
|
+
}
|
|
414
|
+
async function patchProjectWithTolerance(ctx, projectId, value) {
|
|
415
|
+
try {
|
|
416
|
+
await patchTopicProjectOverlay(ctx, projectId, value);
|
|
417
|
+
} catch (error) {
|
|
418
|
+
if (!isAdvisoryTopicPatch(value) || !isMissingLucernChildComponentError2(error)) {
|
|
419
|
+
throw error;
|
|
420
|
+
}
|
|
421
|
+
console.warn(
|
|
422
|
+
"[lucern graph-primitives] Non-fatal advisory topic patch failure",
|
|
423
|
+
{
|
|
424
|
+
projectId,
|
|
425
|
+
keys: Object.keys(value),
|
|
426
|
+
error: getErrorMessage2(error)
|
|
427
|
+
}
|
|
428
|
+
);
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
function defaultResolvers() {
|
|
432
|
+
return {
|
|
433
|
+
getProject: (ctx, projectId) => resolveTopicProjectOverlay(ctx, projectId, {
|
|
434
|
+
idMode: "legacy",
|
|
435
|
+
projectLikeOnly: false
|
|
436
|
+
}),
|
|
437
|
+
patchProject: (ctx, projectId, value) => patchProjectWithTolerance(ctx, projectId, value),
|
|
438
|
+
listTopics: (ctx) => listTopicProjectOverlays(ctx, {
|
|
439
|
+
idMode: "legacy"
|
|
440
|
+
}),
|
|
441
|
+
getFinalArtifact: (ctx, artifactId) => ctx.db.get(artifactId)
|
|
442
|
+
};
|
|
443
|
+
}
|
|
444
|
+
var resolverOverrides = {};
|
|
445
|
+
function resolveGraphPrimitivesAppResolvers(_ctx) {
|
|
446
|
+
return {
|
|
447
|
+
...defaultResolvers(),
|
|
448
|
+
...resolverOverrides
|
|
449
|
+
};
|
|
450
|
+
}
|
|
451
|
+
var LEGACY_SCOPE_FIELD2 = "graphScopeProjectId";
|
|
452
|
+
function asMappedProjectId(topic) {
|
|
453
|
+
if (!topic) {
|
|
454
|
+
return;
|
|
455
|
+
}
|
|
456
|
+
const directLegacyProjectId = normalizeScopeValue(topic[LEGACY_SCOPE_FIELD2]);
|
|
457
|
+
if (directLegacyProjectId) {
|
|
458
|
+
return directLegacyProjectId;
|
|
459
|
+
}
|
|
460
|
+
const metadata = topic.metadata || {};
|
|
461
|
+
const candidate = metadata[LEGACY_SCOPE_FIELD2] || metadata.legacyProjectId || metadata.projectId || metadata.scopeProjectId;
|
|
462
|
+
return candidate ? candidate : void 0;
|
|
463
|
+
}
|
|
464
|
+
function normalizeScopeValue(value) {
|
|
465
|
+
if (typeof value !== "string") {
|
|
466
|
+
return;
|
|
467
|
+
}
|
|
468
|
+
const normalized = value.trim();
|
|
469
|
+
return normalized.length > 0 ? normalized : void 0;
|
|
470
|
+
}
|
|
471
|
+
function pickPrimaryTopic(candidates) {
|
|
472
|
+
return [...candidates].sort((a, b) => {
|
|
473
|
+
const depthA = a.depth ?? 9999;
|
|
474
|
+
const depthB = b.depth ?? 9999;
|
|
475
|
+
if (depthA !== depthB) {
|
|
476
|
+
return depthA - depthB;
|
|
477
|
+
}
|
|
478
|
+
const createdA = a.createdAt ?? Number.MAX_SAFE_INTEGER;
|
|
479
|
+
const createdB = b.createdAt ?? Number.MAX_SAFE_INTEGER;
|
|
480
|
+
if (createdA !== createdB) {
|
|
481
|
+
return createdA - createdB;
|
|
482
|
+
}
|
|
483
|
+
return String(a.name || "").localeCompare(String(b.name || ""));
|
|
484
|
+
})[0];
|
|
485
|
+
}
|
|
486
|
+
async function findTopicsByScopeAlias(ctx, scopeId) {
|
|
487
|
+
try {
|
|
488
|
+
return await ctx.db.query("topics").withIndex(
|
|
489
|
+
"by_graph_scope_project",
|
|
490
|
+
(q) => q.eq(LEGACY_SCOPE_FIELD2, scopeId)
|
|
491
|
+
).collect();
|
|
492
|
+
} catch (error) {
|
|
493
|
+
debugGraphPrimitiveFallback(
|
|
494
|
+
"[topicScope] Failed to resolve scope alias via index",
|
|
495
|
+
{
|
|
496
|
+
error,
|
|
497
|
+
scopeId
|
|
498
|
+
}
|
|
499
|
+
);
|
|
500
|
+
const topics = await ctx.db.query("topics").collect();
|
|
501
|
+
return topics.filter((topic) => {
|
|
502
|
+
const normalizedGlobalId = normalizeScopeValue(topic.globalId);
|
|
503
|
+
const mappedProjectId = asMappedProjectId(topic);
|
|
504
|
+
return String(topic._id) === scopeId || normalizedGlobalId === scopeId || mappedProjectId === scopeId;
|
|
505
|
+
});
|
|
506
|
+
}
|
|
507
|
+
}
|
|
508
|
+
async function tryResolveHostTopicById(ctx, topicId) {
|
|
509
|
+
if (typeof ctx.runQuery !== "function") {
|
|
510
|
+
return null;
|
|
511
|
+
}
|
|
512
|
+
try {
|
|
513
|
+
return await ctx.runQuery(api.topics.get, {
|
|
514
|
+
id: topicId
|
|
515
|
+
}) ?? null;
|
|
516
|
+
} catch (error) {
|
|
517
|
+
debugGraphPrimitiveFallback(
|
|
518
|
+
"[topicScope] Failed to resolve topic by host query",
|
|
519
|
+
{
|
|
520
|
+
error,
|
|
521
|
+
topicId
|
|
522
|
+
}
|
|
523
|
+
);
|
|
482
524
|
return null;
|
|
483
525
|
}
|
|
484
526
|
}
|
|
@@ -629,8 +671,6 @@ var optionalScopeArgs = {
|
|
|
629
671
|
projectId: v.optional(v.string()),
|
|
630
672
|
topicId: v.optional(v.string())
|
|
631
673
|
};
|
|
632
|
-
|
|
633
|
-
// src/workspaceIsolation.ts
|
|
634
674
|
function normalizeScopeValue2(value) {
|
|
635
675
|
if (typeof value !== "string") {
|
|
636
676
|
return;
|
|
@@ -780,896 +820,786 @@ function assertTenantPackWorkspaceMutationAllowed(args) {
|
|
|
780
820
|
});
|
|
781
821
|
}
|
|
782
822
|
|
|
783
|
-
// src/
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
823
|
+
// src/epistemicBeliefs.helpers.ts
|
|
824
|
+
var insightIdUnion = v.id("epistemicNodes");
|
|
825
|
+
var DEFAULT_PROJECT_BELIEF_LIMIT = 250;
|
|
826
|
+
var MAX_PROJECT_BELIEF_LIMIT = 1e3;
|
|
827
|
+
var optionalBeliefScopeArgs = optionalScopeArgs;
|
|
828
|
+
var DEFAULT_CONFIDENCE_POLICY = {
|
|
829
|
+
scoringMode: "after_worktree",
|
|
830
|
+
tupleContradiction: normalizeTupleContradictionPolicy()
|
|
831
|
+
};
|
|
832
|
+
function throwStructuredMutationError(args) {
|
|
833
|
+
const error = new Error(args.message);
|
|
834
|
+
error.status = args.status;
|
|
835
|
+
error.code = args.code;
|
|
836
|
+
error.invariantCode = args.invariantCode;
|
|
837
|
+
error.suggestion = args.suggestion;
|
|
838
|
+
error.details = args.details;
|
|
839
|
+
throw error;
|
|
787
840
|
}
|
|
788
|
-
function
|
|
789
|
-
|
|
790
|
-
try {
|
|
791
|
-
return readOpinionFromRecord({
|
|
792
|
-
...metadata,
|
|
793
|
-
opinion_b: node.opinion_b,
|
|
794
|
-
opinion_d: node.opinion_d,
|
|
795
|
-
opinion_u: node.opinion_u,
|
|
796
|
-
opinion_a: node.opinion_a
|
|
797
|
-
});
|
|
798
|
-
} catch {
|
|
799
|
-
return mkOpinion(0, 0, 1, 0.5);
|
|
800
|
-
}
|
|
841
|
+
function readFiniteNumber(value) {
|
|
842
|
+
return typeof value === "number" && Number.isFinite(value) ? value : void 0;
|
|
801
843
|
}
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
844
|
+
function clamp01(value) {
|
|
845
|
+
return Math.max(0, Math.min(1, value));
|
|
846
|
+
}
|
|
847
|
+
function assertBaseRateInRange(baseRate, field = "baseRate") {
|
|
848
|
+
if (baseRate < 0 || baseRate > 1) {
|
|
849
|
+
throwStructuredMutationError({
|
|
850
|
+
message: `${field} must be within [0, 1].`,
|
|
851
|
+
status: 400,
|
|
852
|
+
code: "INVALID_ARGUMENT",
|
|
853
|
+
invariantCode: "request.valid_shape",
|
|
854
|
+
suggestion: `Clamp ${field} into the inclusive [0, 1] interval.`,
|
|
855
|
+
details: { field, baseRate }
|
|
856
|
+
});
|
|
857
|
+
}
|
|
858
|
+
return baseRate;
|
|
859
|
+
}
|
|
860
|
+
function buildBeliefConfidenceRow(args) {
|
|
861
|
+
return {
|
|
862
|
+
beliefId: args.beliefId,
|
|
863
|
+
confidence: confidenceFromSL(
|
|
864
|
+
args.belief,
|
|
865
|
+
args.disbelief,
|
|
866
|
+
args.uncertainty,
|
|
867
|
+
args.baseRate
|
|
868
|
+
),
|
|
869
|
+
belief: args.belief,
|
|
870
|
+
disbelief: args.disbelief,
|
|
871
|
+
uncertainty: args.uncertainty,
|
|
872
|
+
baseRate: args.baseRate,
|
|
873
|
+
slOperator: args.slOperator ?? "manual_assessment",
|
|
874
|
+
trigger: args.trigger,
|
|
875
|
+
...args.rationale ? { rationale: args.rationale } : {},
|
|
876
|
+
assessedBy: args.assessedBy,
|
|
877
|
+
assessedAt: args.assessedAt,
|
|
878
|
+
...args.triggeringEvidenceId ? {
|
|
879
|
+
triggeringEvidenceId: args.triggeringEvidenceId,
|
|
880
|
+
triggeringEvidenceIds: [String(args.triggeringEvidenceId)]
|
|
881
|
+
} : {},
|
|
882
|
+
...args.triggeringContradictionId ? {
|
|
883
|
+
triggeringContradictionId: args.triggeringContradictionId
|
|
884
|
+
} : {},
|
|
885
|
+
...args.triggeringWorktreeId ? { triggeringWorktreeId: args.triggeringWorktreeId } : {}
|
|
821
886
|
};
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
continue;
|
|
846
|
-
}
|
|
847
|
-
if (state.visitedNodeIds.has(String(targetNodeId))) {
|
|
848
|
-
continue;
|
|
849
|
-
}
|
|
850
|
-
const targetNode = await loadNode(targetNodeId);
|
|
851
|
-
if (!targetNode || targetNode.nodeType !== "belief") {
|
|
852
|
-
continue;
|
|
853
|
-
}
|
|
854
|
-
if (args.sourceScope && !nodeMatchesWorkspaceReasoningScope(targetNode, args.sourceScope)) {
|
|
855
|
-
continue;
|
|
856
|
-
}
|
|
857
|
-
const cacheKey = String(targetNodeId);
|
|
858
|
-
const targetOpinion = opinionCache.get(cacheKey) ?? readNodeOpinion(targetNode);
|
|
859
|
-
const result = spec.operator(state.opinion, targetOpinion, edge, {
|
|
860
|
-
hop: nextHop,
|
|
861
|
-
sourceNodeId: state.nodeId,
|
|
862
|
-
targetNodeId,
|
|
863
|
-
traversedDirection: direction,
|
|
864
|
-
spec
|
|
865
|
-
});
|
|
866
|
-
if (!result || !hasProjectedOpinionChanged(targetOpinion, result.opinion)) {
|
|
867
|
-
continue;
|
|
868
|
-
}
|
|
869
|
-
const projectedOpinion = mkOpinion(
|
|
870
|
-
result.opinion.b,
|
|
871
|
-
result.opinion.d,
|
|
872
|
-
result.opinion.u,
|
|
873
|
-
result.opinion.a
|
|
874
|
-
);
|
|
875
|
-
opinionCache.set(cacheKey, projectedOpinion);
|
|
876
|
-
const existingDispatch = dispatchesByTargetId.get(cacheKey);
|
|
877
|
-
dispatchesByTargetId.set(cacheKey, {
|
|
878
|
-
targetNodeId,
|
|
879
|
-
edgeType: spec.edgeType,
|
|
880
|
-
traversedDirection: direction,
|
|
881
|
-
weight: edge.weight ?? 1,
|
|
882
|
-
opinion: projectedOpinion,
|
|
883
|
-
operator: result.operator,
|
|
884
|
-
rationale: existingDispatch ? `${existingDispatch.rationale}; ${result.rationale}` : result.rationale,
|
|
885
|
-
hop: nextHop
|
|
886
|
-
});
|
|
887
|
-
if (canContinueTransitively(spec, nextHop)) {
|
|
888
|
-
queue.push({
|
|
889
|
-
nodeId: targetNodeId,
|
|
890
|
-
opinion: projectedOpinion,
|
|
891
|
-
hop: nextHop,
|
|
892
|
-
visitedNodeIds: /* @__PURE__ */ new Set([
|
|
893
|
-
...state.visitedNodeIds,
|
|
894
|
-
String(targetNodeId)
|
|
895
|
-
])
|
|
896
|
-
});
|
|
897
|
-
}
|
|
887
|
+
}
|
|
888
|
+
function buildBeliefStatusSuccessResult() {
|
|
889
|
+
return { success: true };
|
|
890
|
+
}
|
|
891
|
+
function buildBeliefEvidenceNotFoundResult() {
|
|
892
|
+
const result = {};
|
|
893
|
+
result.success = false;
|
|
894
|
+
result.message = "Evidence node not found";
|
|
895
|
+
return result;
|
|
896
|
+
}
|
|
897
|
+
function deriveSyntheticBackfillOpinion(source) {
|
|
898
|
+
const belief = readFiniteNumber(source.opinion_b) ?? readFiniteNumber(source.belief);
|
|
899
|
+
const disbelief = readFiniteNumber(source.opinion_d) ?? readFiniteNumber(source.disbelief);
|
|
900
|
+
const uncertainty = readFiniteNumber(source.opinion_u) ?? readFiniteNumber(source.uncertainty);
|
|
901
|
+
const baseRate = readFiniteNumber(source.opinion_a) ?? readFiniteNumber(source.baseRate);
|
|
902
|
+
if (belief !== void 0 || disbelief !== void 0 || uncertainty !== void 0 || baseRate !== void 0) {
|
|
903
|
+
try {
|
|
904
|
+
return readOpinionFromRecord(source);
|
|
905
|
+
} catch (error) {
|
|
906
|
+
debugGraphPrimitiveFallback(
|
|
907
|
+
"[epistemicBeliefs] Failed to decode legacy belief opinion",
|
|
908
|
+
{
|
|
909
|
+
error
|
|
898
910
|
}
|
|
899
|
-
|
|
911
|
+
);
|
|
912
|
+
return mkOpinion(0, 0, 1, 0.5);
|
|
900
913
|
}
|
|
901
914
|
}
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
return left.hop - right.hop;
|
|
905
|
-
}
|
|
906
|
-
return String(left.targetNodeId).localeCompare(String(right.targetNodeId));
|
|
907
|
-
});
|
|
915
|
+
const confidence = clamp01(readFiniteNumber(source.confidence) ?? 0);
|
|
916
|
+
return mkOpinion(confidence, 1 - confidence, 0, 0.5);
|
|
908
917
|
}
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
918
|
+
function clampBeliefLimit(limit, fallback = DEFAULT_PROJECT_BELIEF_LIMIT) {
|
|
919
|
+
if (!Number.isFinite(limit)) {
|
|
920
|
+
return fallback;
|
|
921
|
+
}
|
|
922
|
+
return Math.max(
|
|
923
|
+
1,
|
|
924
|
+
Math.min(Math.floor(limit), MAX_PROJECT_BELIEF_LIMIT)
|
|
925
|
+
);
|
|
926
|
+
}
|
|
927
|
+
function readTupleContradictedFlag(value) {
|
|
928
|
+
return typeof value === "boolean" ? value : void 0;
|
|
929
|
+
}
|
|
930
|
+
function readBeliefOpinionSnapshot(node, metadata) {
|
|
912
931
|
try {
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
createdBy: args.createdBy,
|
|
921
|
-
nodeType: args.nodeType,
|
|
922
|
-
text: args.text.slice(0, 2e4),
|
|
923
|
-
hasAnswer: args.hasAnswer,
|
|
924
|
-
confidence: args.confidence
|
|
925
|
-
}
|
|
926
|
-
);
|
|
932
|
+
return readOpinionFromRecord({
|
|
933
|
+
...metadata,
|
|
934
|
+
opinion_b: node.opinion_b,
|
|
935
|
+
opinion_d: node.opinion_d,
|
|
936
|
+
opinion_u: node.opinion_u,
|
|
937
|
+
opinion_a: node.opinion_a
|
|
938
|
+
});
|
|
927
939
|
} catch (error) {
|
|
928
940
|
debugGraphPrimitiveFallback(
|
|
929
|
-
"[
|
|
941
|
+
"[epistemicBeliefs] Failed to read belief opinion snapshot",
|
|
930
942
|
{
|
|
931
943
|
error,
|
|
932
|
-
|
|
933
|
-
nodeType: args.nodeType
|
|
944
|
+
beliefId: node._id
|
|
934
945
|
}
|
|
935
946
|
);
|
|
947
|
+
return mkOpinion(0, 0, 1, 0.5);
|
|
936
948
|
}
|
|
937
949
|
}
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
bytes[6] = bytes[6] & 15 | 64;
|
|
944
|
-
bytes[8] = bytes[8] & 63 | 128;
|
|
945
|
-
const hex = Array.from(bytes, (b) => b.toString(16).padStart(2, "0")).join(
|
|
946
|
-
""
|
|
947
|
-
);
|
|
948
|
-
return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20)}`;
|
|
949
|
-
}
|
|
950
|
-
|
|
951
|
-
// src/logicalRoleInference.ts
|
|
952
|
-
var PILLAR_IMPORTANCE = {
|
|
953
|
-
market: 1,
|
|
954
|
-
competition: 2,
|
|
955
|
-
product: 3,
|
|
956
|
-
team: 4,
|
|
957
|
-
financials: 5,
|
|
958
|
-
timing: 6,
|
|
959
|
-
other: 10
|
|
960
|
-
};
|
|
961
|
-
async function computeLogicalRole(ctx, evidenceId, beliefId) {
|
|
962
|
-
const belief = await ctx.db.get(beliefId);
|
|
963
|
-
if (!belief || belief.nodeType !== "belief") {
|
|
964
|
-
return "contributory";
|
|
950
|
+
function deriveTupleContradictionSeverity(node) {
|
|
951
|
+
const metadata = node.metadata || {};
|
|
952
|
+
const criticality = typeof metadata.criticality === "string" ? metadata.criticality : void 0;
|
|
953
|
+
if (criticality === "blocking") {
|
|
954
|
+
return "critical";
|
|
965
955
|
}
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
const pillarRank = PILLAR_IMPORTANCE[pillar] ?? 10;
|
|
969
|
-
const isSynthesized = await checkIfSynthesizedHypothesis(ctx, beliefId);
|
|
970
|
-
const testingQuestions = await getTestingQuestionsForBelief(ctx, beliefId);
|
|
971
|
-
const answeredQuestions = await getQuestionsAnsweredByEvidence(
|
|
972
|
-
ctx,
|
|
973
|
-
evidenceId
|
|
974
|
-
);
|
|
975
|
-
const directlyTests = testingQuestions.filter(
|
|
976
|
-
(questionId) => answeredQuestions.includes(questionId)
|
|
977
|
-
);
|
|
978
|
-
if (directlyTests.length === 0) {
|
|
979
|
-
return "contributory";
|
|
956
|
+
if (criticality === "supporting") {
|
|
957
|
+
return "minor";
|
|
980
958
|
}
|
|
981
|
-
|
|
982
|
-
return directlyTests.length > 1 ? "necessary_sufficient" : "necessary";
|
|
983
|
-
}
|
|
984
|
-
if (isSynthesized) {
|
|
985
|
-
return "necessary";
|
|
986
|
-
}
|
|
987
|
-
return directlyTests.length > 1 ? "necessary" : "contributory";
|
|
988
|
-
}
|
|
989
|
-
async function checkIfSynthesizedHypothesis(ctx, beliefId) {
|
|
990
|
-
const belief = await ctx.db.get(beliefId);
|
|
991
|
-
if (!belief) {
|
|
992
|
-
return false;
|
|
993
|
-
}
|
|
994
|
-
const metadata = belief.metadata;
|
|
995
|
-
return metadata?.isSynthesized === true || metadata?.beliefStatus === "hypothesis";
|
|
996
|
-
}
|
|
997
|
-
async function getTestingQuestionsForBelief(ctx, beliefId) {
|
|
998
|
-
const testEdges = await ctx.db.query("epistemicEdges").withIndex(
|
|
999
|
-
"by_to_type",
|
|
1000
|
-
(q) => q.eq("toNodeId", beliefId).eq("edgeType", "tests")
|
|
1001
|
-
).collect();
|
|
1002
|
-
return testEdges.map((edge) => edge.fromNodeId).filter((id) => id !== void 0);
|
|
959
|
+
return "significant";
|
|
1003
960
|
}
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
"by_from_type",
|
|
1007
|
-
(q) => q.eq("fromNodeId", evidenceId).eq("edgeType", "derived_from")
|
|
1008
|
-
).collect();
|
|
1009
|
-
return answerEdges.map((edge) => edge.toNodeId).filter((id) => id !== void 0);
|
|
961
|
+
function formatTupleContradictionDescription(args) {
|
|
962
|
+
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)}.`;
|
|
1010
963
|
}
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
964
|
+
function generateContentHash(text) {
|
|
965
|
+
const content = `belief:${text.trim().toLowerCase().replace(/\s+/g, " ")}`;
|
|
966
|
+
let hash = 5381;
|
|
967
|
+
for (let i = 0; i < content.length; i++) {
|
|
968
|
+
hash = (hash << 5) + hash + content.charCodeAt(i);
|
|
969
|
+
hash &= hash;
|
|
1017
970
|
}
|
|
1018
|
-
|
|
1019
|
-
return normalized.length > 0 ? normalized : void 0;
|
|
971
|
+
return Math.abs(hash).toString(16).padStart(8, "0");
|
|
1020
972
|
}
|
|
1021
|
-
function
|
|
1022
|
-
|
|
1023
|
-
|
|
973
|
+
function resolveBeliefWorktreeId(metadata) {
|
|
974
|
+
const worktreeId = metadata?.worktreeId;
|
|
975
|
+
if (typeof worktreeId === "string" && worktreeId.trim().length > 0) {
|
|
976
|
+
return worktreeId;
|
|
1024
977
|
}
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
function readMetadata(topic) {
|
|
1028
|
-
return topic.metadata && typeof topic.metadata === "object" ? topic.metadata : {};
|
|
978
|
+
const sprintId = metadata?.sprintId;
|
|
979
|
+
return typeof sprintId === "string" && sprintId.trim().length > 0 ? sprintId : void 0;
|
|
1029
980
|
}
|
|
1030
|
-
function
|
|
1031
|
-
if (!
|
|
1032
|
-
return;
|
|
981
|
+
function normalizePillar(pillar) {
|
|
982
|
+
if (!pillar) {
|
|
983
|
+
return "other";
|
|
1033
984
|
}
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
985
|
+
const lower = pillar.toLowerCase();
|
|
986
|
+
const validPillars = [
|
|
987
|
+
"market",
|
|
988
|
+
"competition",
|
|
989
|
+
"product",
|
|
990
|
+
"team",
|
|
991
|
+
"financials",
|
|
992
|
+
"regulatory",
|
|
993
|
+
"timing",
|
|
994
|
+
"customer",
|
|
995
|
+
"technology",
|
|
996
|
+
"distribution",
|
|
997
|
+
"deal",
|
|
998
|
+
"risks"
|
|
999
|
+
];
|
|
1000
|
+
return validPillars.find((p) => lower.includes(p)) || "other";
|
|
1041
1001
|
}
|
|
1042
|
-
function
|
|
1043
|
-
const
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
if (topic.type === "theme") {
|
|
1048
|
-
return "thematic";
|
|
1002
|
+
async function markBeliefGraphDirty(ctx, scope) {
|
|
1003
|
+
const projectId = typeof scope.projectId === "string" && scope.projectId.trim().length > 0 ? scope.projectId : void 0;
|
|
1004
|
+
const topicId = typeof scope.topicId === "string" && scope.topicId.trim().length > 0 ? scope.topicId : void 0;
|
|
1005
|
+
if (!projectId && !topicId) {
|
|
1006
|
+
return;
|
|
1049
1007
|
}
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
function isMissingLucernChildComponentError(error) {
|
|
1057
|
-
const message = getErrorMessage(error);
|
|
1058
|
-
return message.includes(
|
|
1059
|
-
'Child component ComponentName(Identifier("lucern")) not found'
|
|
1060
|
-
) || message.includes("Child component") && message.includes("lucern") && message.includes("not found");
|
|
1061
|
-
}
|
|
1062
|
-
function getErrorMessage(error) {
|
|
1063
|
-
if (error instanceof Error) {
|
|
1064
|
-
return error.message;
|
|
1008
|
+
if (projectId) {
|
|
1009
|
+
await ctx.scheduler.runAfter(
|
|
1010
|
+
0,
|
|
1011
|
+
internal.graphAnalysisCache.markCacheStaleInternal,
|
|
1012
|
+
{ projectId }
|
|
1013
|
+
);
|
|
1065
1014
|
}
|
|
1066
|
-
if (
|
|
1067
|
-
|
|
1015
|
+
if (topicId) {
|
|
1016
|
+
await ctx.scheduler.runAfter(
|
|
1017
|
+
0,
|
|
1018
|
+
internal.graphAnalysisCache.markCacheStaleByTopic,
|
|
1019
|
+
{ topicId }
|
|
1020
|
+
);
|
|
1068
1021
|
}
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
const directTopic = await ctx.db.get(
|
|
1075
|
-
scopeId
|
|
1076
|
-
);
|
|
1077
|
-
if (directTopic) {
|
|
1078
|
-
return directTopic;
|
|
1079
|
-
}
|
|
1080
|
-
} catch (error) {
|
|
1081
|
-
debugGraphPrimitiveFallback(
|
|
1082
|
-
"[topicProjectOverlay] Failed to resolve topic by direct ID",
|
|
1083
|
-
{
|
|
1084
|
-
error,
|
|
1085
|
-
scopeId
|
|
1086
|
-
}
|
|
1087
|
-
);
|
|
1022
|
+
await resolveGraphPrimitivesAppResolvers().patchProject(
|
|
1023
|
+
ctx,
|
|
1024
|
+
topicId ?? projectId,
|
|
1025
|
+
{
|
|
1026
|
+
lastActivityAt: Date.now()
|
|
1088
1027
|
}
|
|
1089
|
-
|
|
1090
|
-
|
|
1028
|
+
);
|
|
1029
|
+
}
|
|
1030
|
+
async function resolveBeliefScopeOrNull(ctx, args) {
|
|
1031
|
+
if (!args.projectId && !args.topicId) {
|
|
1091
1032
|
return null;
|
|
1092
1033
|
}
|
|
1093
1034
|
try {
|
|
1094
|
-
|
|
1095
|
-
|
|
1035
|
+
return await resolveTopicProjectScope(ctx, {
|
|
1036
|
+
projectId: args.projectId ?? void 0,
|
|
1037
|
+
topicId: args.topicId ?? void 0
|
|
1096
1038
|
});
|
|
1097
|
-
if (topic?.name !== void 0 && topic?.type !== void 0) {
|
|
1098
|
-
return topic;
|
|
1099
|
-
}
|
|
1100
1039
|
} catch (error) {
|
|
1101
1040
|
debugGraphPrimitiveFallback(
|
|
1102
|
-
"[
|
|
1041
|
+
"[epistemicBeliefs] Failed to resolve belief scope",
|
|
1103
1042
|
{
|
|
1104
1043
|
error,
|
|
1105
|
-
|
|
1044
|
+
projectId: args.projectId,
|
|
1045
|
+
topicId: args.topicId
|
|
1106
1046
|
}
|
|
1107
1047
|
);
|
|
1048
|
+
return null;
|
|
1108
1049
|
}
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1050
|
+
}
|
|
1051
|
+
async function getBeliefNodesForScope(ctx, scope, args) {
|
|
1052
|
+
const baseQuery = ctx.db.query("epistemicNodes").withIndex(
|
|
1053
|
+
"by_topic_type",
|
|
1054
|
+
(q) => q.eq("topicId", scope.topicId).eq("nodeType", "belief")
|
|
1055
|
+
);
|
|
1056
|
+
const nodes = typeof args?.scanLimit === "number" ? await baseQuery.order("desc").take(args.scanLimit) : await baseQuery.collect();
|
|
1057
|
+
const scopedNodes = nodes.filter(
|
|
1058
|
+
(node) => nodeMatchesWorkspaceReasoningScope(node, scope)
|
|
1059
|
+
);
|
|
1060
|
+
if (!args?.status) {
|
|
1061
|
+
return scopedNodes;
|
|
1121
1062
|
}
|
|
1122
|
-
return
|
|
1063
|
+
return scopedNodes.filter((node) => node.status === args.status);
|
|
1123
1064
|
}
|
|
1124
|
-
function
|
|
1125
|
-
const
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
projectId: outwardId,
|
|
1138
|
-
topicId,
|
|
1139
|
-
storageProjectId,
|
|
1140
|
-
legacyProjectId,
|
|
1141
|
-
name: readNonEmptyString(topic.name) || "Untitled Theme",
|
|
1142
|
-
type: mapProjectType(topic, metadata),
|
|
1143
|
-
description: readNonEmptyString(topic.description),
|
|
1144
|
-
ownerId: readNonEmptyString(metadata.ownerId) || readNonEmptyString(topic.createdBy) || "system",
|
|
1145
|
-
sharedWith: readStringArray(metadata.sharedWith),
|
|
1146
|
-
visibility,
|
|
1147
|
-
tenantId: readNonEmptyString(topic.tenantId) || readNonEmptyString(metadata.tenantId),
|
|
1148
|
-
workspaceId: readNonEmptyString(topic.workspaceId) || readNonEmptyString(metadata.workspaceId),
|
|
1149
|
-
status,
|
|
1150
|
-
tags: readStringArray(metadata.tags),
|
|
1151
|
-
chatCount: typeof metadata.chatCount === "number" ? metadata.chatCount : 0,
|
|
1152
|
-
artifactCount: typeof metadata.artifactCount === "number" ? metadata.artifactCount : 0,
|
|
1153
|
-
lastActivityAt: typeof metadata.lastActivityAt === "number" ? metadata.lastActivityAt : updatedAt,
|
|
1154
|
-
_creationTime: typeof topic._creationTime === "number" ? topic._creationTime : createdAt,
|
|
1155
|
-
createdAt,
|
|
1156
|
-
updatedAt
|
|
1065
|
+
function createBeliefAudienceResolver(registryRows) {
|
|
1066
|
+
const audienceClassByKey = new Map(
|
|
1067
|
+
registryRows.map((row) => [
|
|
1068
|
+
normalizeAudienceKey(row.audienceKey),
|
|
1069
|
+
row.audienceClass
|
|
1070
|
+
])
|
|
1071
|
+
);
|
|
1072
|
+
return (audienceKey, fallback) => {
|
|
1073
|
+
const key = normalizeAudienceKey(audienceKey);
|
|
1074
|
+
if (!key) {
|
|
1075
|
+
return fallback;
|
|
1076
|
+
}
|
|
1077
|
+
return audienceClassByKey.get(key) ?? classFromAudienceKey(key, fallback);
|
|
1157
1078
|
};
|
|
1158
1079
|
}
|
|
1159
|
-
|
|
1160
|
-
const
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1080
|
+
function flattenBeliefNode(node) {
|
|
1081
|
+
const meta = node.metadata || {};
|
|
1082
|
+
const worktreeId = resolveBeliefWorktreeId(meta);
|
|
1083
|
+
const tupleContradicted = readTupleContradictedFlag(
|
|
1084
|
+
node.tupleContradicted
|
|
1085
|
+
) ?? readTupleContradictedFlag(meta.tupleContradicted) ?? false;
|
|
1086
|
+
return {
|
|
1087
|
+
_id: node._id,
|
|
1088
|
+
_epistemicNodeId: node._id,
|
|
1089
|
+
_creationTime: node._creationTime,
|
|
1090
|
+
belief: node.canonicalText,
|
|
1091
|
+
formulation: node.canonicalText,
|
|
1092
|
+
projectId: node.projectId,
|
|
1093
|
+
topicId: node.topicId,
|
|
1094
|
+
userId: node.userId || node.createdBy || "",
|
|
1095
|
+
confidence: meta.confidence || "untested",
|
|
1096
|
+
status: node.status,
|
|
1097
|
+
beliefStatus: resolveBeliefStatus(node, meta),
|
|
1098
|
+
topic: meta.topic || meta.pillar || "other",
|
|
1099
|
+
pillar: meta.pillar || meta.topic || "",
|
|
1100
|
+
category: meta.category || "",
|
|
1101
|
+
subcategory: meta.subcategory || "",
|
|
1102
|
+
categoryIcon: meta.categoryIcon || "",
|
|
1103
|
+
supportingEvidence: meta.supportingEvidenceIds || [],
|
|
1104
|
+
contradictingEvidence: meta.contradictingEvidenceIds || [],
|
|
1105
|
+
testingQuestions: meta.testingQuestionIds || [],
|
|
1106
|
+
linkedInsights: meta.linkedInsightIds || [],
|
|
1107
|
+
createdAt: node.createdAt,
|
|
1108
|
+
updatedAt: node.updatedAt || node.createdAt,
|
|
1109
|
+
tupleContradicted,
|
|
1110
|
+
sprintId: meta.sprintId || void 0,
|
|
1111
|
+
worktreeId,
|
|
1112
|
+
sourceBeliefIds: meta.sourceBeliefIds || void 0,
|
|
1113
|
+
criticality: meta.criticality || "unanalyzed",
|
|
1114
|
+
rationale: meta.rationale || "",
|
|
1115
|
+
audienceLabel: node.audienceLabel,
|
|
1116
|
+
policyTags: node.policyTags,
|
|
1117
|
+
sensitivityTier: node.sensitivityTier,
|
|
1118
|
+
exportClass: node.exportClass,
|
|
1119
|
+
anonymizationClass: node.anonymizationClass
|
|
1198
1120
|
};
|
|
1199
|
-
for (const [key, rawValue] of Object.entries(value)) {
|
|
1200
|
-
switch (key) {
|
|
1201
|
-
case "_id":
|
|
1202
|
-
case "projectId":
|
|
1203
|
-
case "topicId":
|
|
1204
|
-
case "legacyProjectId":
|
|
1205
|
-
case "storageProjectId":
|
|
1206
|
-
break;
|
|
1207
|
-
case "name":
|
|
1208
|
-
case "description":
|
|
1209
|
-
patch[key] = rawValue;
|
|
1210
|
-
topicUpdateArgs[key] = rawValue;
|
|
1211
|
-
break;
|
|
1212
|
-
case "tenantId":
|
|
1213
|
-
case "workspaceId":
|
|
1214
|
-
case "ownerId":
|
|
1215
|
-
throw new Error(
|
|
1216
|
-
`patchTopicProjectOverlay cannot mutate ${key} via component-owned topics`
|
|
1217
|
-
);
|
|
1218
|
-
case "status": {
|
|
1219
|
-
const status = coerceStatus(rawValue);
|
|
1220
|
-
if (status) {
|
|
1221
|
-
patch.status = status;
|
|
1222
|
-
topicUpdateArgs.status = status;
|
|
1223
|
-
}
|
|
1224
|
-
break;
|
|
1225
|
-
}
|
|
1226
|
-
case "visibility": {
|
|
1227
|
-
const visibility = coerceVisibility(rawValue);
|
|
1228
|
-
if (visibility) {
|
|
1229
|
-
patch.visibility = visibility;
|
|
1230
|
-
topicUpdateArgs.visibility = visibility;
|
|
1231
|
-
}
|
|
1232
|
-
break;
|
|
1233
|
-
}
|
|
1234
|
-
case "type": {
|
|
1235
|
-
const projectType = readNonEmptyString(rawValue);
|
|
1236
|
-
if (projectType) {
|
|
1237
|
-
nextMetadata.projectType = projectType;
|
|
1238
|
-
} else {
|
|
1239
|
-
delete nextMetadata.projectType;
|
|
1240
|
-
}
|
|
1241
|
-
break;
|
|
1242
|
-
}
|
|
1243
|
-
case "updatedAt":
|
|
1244
|
-
case "createdAt":
|
|
1245
|
-
break;
|
|
1246
|
-
default:
|
|
1247
|
-
if (rawValue === void 0) {
|
|
1248
|
-
delete nextMetadata[key];
|
|
1249
|
-
} else {
|
|
1250
|
-
nextMetadata[key] = rawValue;
|
|
1251
|
-
}
|
|
1252
|
-
}
|
|
1253
|
-
}
|
|
1254
|
-
patch.updatedAt = Date.now();
|
|
1255
|
-
patch.metadata = nextMetadata;
|
|
1256
|
-
topicUpdateArgs.metadata = nextMetadata;
|
|
1257
|
-
if (typeof ctx.runMutation === "function") {
|
|
1258
|
-
try {
|
|
1259
|
-
await ctx.runMutation(api.topics.update, topicUpdateArgs);
|
|
1260
|
-
} catch (error) {
|
|
1261
|
-
if (!isMissingLucernChildComponentError(error) || !ctx?.db || typeof ctx.db.patch !== "function") {
|
|
1262
|
-
throw error;
|
|
1263
|
-
}
|
|
1264
|
-
await ctx.db.patch(String(topic._id), patch);
|
|
1265
|
-
}
|
|
1266
|
-
} else if (ctx?.db && typeof ctx.db.patch === "function") {
|
|
1267
|
-
await ctx.db.patch(String(topic._id), patch);
|
|
1268
|
-
} else {
|
|
1269
|
-
throw new Error(
|
|
1270
|
-
"Cannot patch topic without component adapter (ctx.runMutation unavailable)"
|
|
1271
|
-
);
|
|
1272
|
-
}
|
|
1273
|
-
return materializeTopicProjectOverlay({
|
|
1274
|
-
...topic,
|
|
1275
|
-
...patch,
|
|
1276
|
-
metadata: nextMetadata
|
|
1277
|
-
});
|
|
1278
1121
|
}
|
|
1279
|
-
|
|
1280
|
-
|
|
1281
|
-
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
)
|
|
1122
|
+
function resolveBeliefStatus(node, metadata) {
|
|
1123
|
+
return resolveBeliefLifecycleStatus({
|
|
1124
|
+
beliefStatus: node.beliefStatus,
|
|
1125
|
+
confidence: node.confidence,
|
|
1126
|
+
predictionMeta: node.predictionMeta,
|
|
1127
|
+
metadata
|
|
1128
|
+
});
|
|
1286
1129
|
}
|
|
1287
|
-
function
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
|
|
1292
|
-
|
|
1130
|
+
async function hasCompletedWorktreeForBelief(ctx, beliefNodeId) {
|
|
1131
|
+
const clusterMembership = await ctx.db.query("worktreeBeliefCluster").withIndex("by_belief", (q) => q.eq("beliefId", beliefNodeId)).collect();
|
|
1132
|
+
for (const membership of clusterMembership) {
|
|
1133
|
+
const worktree = await ctx.db.get(membership.worktreeId);
|
|
1134
|
+
if (worktree?.status === "completed" || worktree?.status === "merged") {
|
|
1135
|
+
return true;
|
|
1136
|
+
}
|
|
1293
1137
|
}
|
|
1294
|
-
return
|
|
1295
|
-
}
|
|
1296
|
-
function isAdvisoryTopicPatch(value) {
|
|
1297
|
-
const advisoryKeys = /* @__PURE__ */ new Set(["lastActivityAt", "updatedAt"]);
|
|
1298
|
-
const keys = Object.keys(value);
|
|
1299
|
-
return keys.length > 0 && keys.every((key) => advisoryKeys.has(key));
|
|
1138
|
+
return false;
|
|
1300
1139
|
}
|
|
1301
|
-
async function
|
|
1140
|
+
async function getActiveConfidencePolicy(ctx) {
|
|
1302
1141
|
try {
|
|
1303
|
-
await
|
|
1142
|
+
const activeConfig = await ctx.db.query("logicSprintScoring").withIndex("by_active", (q) => q.eq("isActive", true)).first();
|
|
1143
|
+
return {
|
|
1144
|
+
scoringMode: activeConfig?.confidencePolicy === "always" ? "always" : DEFAULT_CONFIDENCE_POLICY.scoringMode,
|
|
1145
|
+
tupleContradiction: normalizeTupleContradictionPolicy(
|
|
1146
|
+
activeConfig?.tupleContradictionPolicy
|
|
1147
|
+
)
|
|
1148
|
+
};
|
|
1304
1149
|
} catch (error) {
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
}
|
|
1308
|
-
console.warn(
|
|
1309
|
-
"[lucern graph-primitives] Non-fatal advisory topic patch failure",
|
|
1150
|
+
debugGraphPrimitiveFallback(
|
|
1151
|
+
"[epistemicBeliefs] Failed to load active confidence policy",
|
|
1310
1152
|
{
|
|
1311
|
-
|
|
1312
|
-
keys: Object.keys(value),
|
|
1313
|
-
error: getErrorMessage2(error)
|
|
1153
|
+
error
|
|
1314
1154
|
}
|
|
1315
1155
|
);
|
|
1156
|
+
return DEFAULT_CONFIDENCE_POLICY;
|
|
1316
1157
|
}
|
|
1317
1158
|
}
|
|
1318
|
-
function
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
|
-
|
|
1322
|
-
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
|
|
1328
|
-
|
|
1329
|
-
|
|
1159
|
+
async function requireAuthenticatedUserId(ctx) {
|
|
1160
|
+
const userId = await getCurrentUserId(
|
|
1161
|
+
ctx
|
|
1162
|
+
);
|
|
1163
|
+
if (!userId) {
|
|
1164
|
+
throwStructuredMutationError({
|
|
1165
|
+
message: "Authentication required.",
|
|
1166
|
+
status: 401,
|
|
1167
|
+
code: "AUTHENTICATION_REQUIRED",
|
|
1168
|
+
invariantCode: "auth.required",
|
|
1169
|
+
suggestion: "Provide a valid bearer token before invoking belief mutations."
|
|
1170
|
+
});
|
|
1171
|
+
}
|
|
1172
|
+
return userId;
|
|
1330
1173
|
}
|
|
1331
|
-
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
|
|
1174
|
+
async function requireProjectWriteAccess(ctx, projectId, userId) {
|
|
1175
|
+
const hasAccess = await checkProjectAccess(
|
|
1176
|
+
ctx,
|
|
1177
|
+
projectId,
|
|
1178
|
+
userId
|
|
1179
|
+
);
|
|
1180
|
+
if (!hasAccess) {
|
|
1181
|
+
throwStructuredMutationError({
|
|
1182
|
+
message: "Project access required.",
|
|
1183
|
+
status: 403,
|
|
1184
|
+
code: "FORBIDDEN",
|
|
1185
|
+
invariantCode: "policy.scope_required",
|
|
1186
|
+
suggestion: "Request write access for the project and retry.",
|
|
1187
|
+
details: { projectId, userId }
|
|
1188
|
+
});
|
|
1189
|
+
}
|
|
1337
1190
|
}
|
|
1338
1191
|
|
|
1339
|
-
// src/
|
|
1340
|
-
var
|
|
1341
|
-
|
|
1342
|
-
|
|
1343
|
-
|
|
1344
|
-
|
|
1345
|
-
|
|
1346
|
-
|
|
1347
|
-
|
|
1348
|
-
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
|
|
1353
|
-
|
|
1354
|
-
|
|
1355
|
-
|
|
1356
|
-
}
|
|
1357
|
-
function readFiniteNumber(value) {
|
|
1358
|
-
return typeof value === "number" && Number.isFinite(value) ? value : void 0;
|
|
1359
|
-
}
|
|
1360
|
-
function clamp01(value) {
|
|
1361
|
-
return Math.max(0, Math.min(1, value));
|
|
1192
|
+
// src/edges/contains.ts
|
|
1193
|
+
var containsPropagationSpec = {
|
|
1194
|
+
edgeType: "contains",
|
|
1195
|
+
direction: "outgoing",
|
|
1196
|
+
transitivity: "none",
|
|
1197
|
+
damping: 1,
|
|
1198
|
+
maxHops: 1,
|
|
1199
|
+
operator: () => null,
|
|
1200
|
+
description: "Structural containment only. Traversed for explicit semantics, but it never propagates opinions."
|
|
1201
|
+
};
|
|
1202
|
+
function readEdgeMetadata(edge) {
|
|
1203
|
+
return {
|
|
1204
|
+
constraint: edge.constraint ?? void 0,
|
|
1205
|
+
normalization: edge.normalization ?? void 0,
|
|
1206
|
+
propagation: edge.propagation ?? void 0,
|
|
1207
|
+
conditionalA: edge.conditionalA ?? void 0,
|
|
1208
|
+
conditionalNotA: edge.conditionalNotA ?? void 0
|
|
1209
|
+
};
|
|
1362
1210
|
}
|
|
1363
|
-
function
|
|
1364
|
-
if (
|
|
1365
|
-
|
|
1366
|
-
message: `${field} must be within [0, 1].`,
|
|
1367
|
-
status: 400,
|
|
1368
|
-
code: "INVALID_ARGUMENT",
|
|
1369
|
-
invariantCode: "request.valid_shape",
|
|
1370
|
-
suggestion: `Clamp ${field} into the inclusive [0, 1] interval.`,
|
|
1371
|
-
details: { field, baseRate }
|
|
1372
|
-
});
|
|
1211
|
+
function applyPerHopDamping(sourceOpinion, damping) {
|
|
1212
|
+
if (damping >= 1) {
|
|
1213
|
+
return sourceOpinion;
|
|
1373
1214
|
}
|
|
1374
|
-
return
|
|
1215
|
+
return trustDiscount(sourceOpinion, Math.max(0, damping));
|
|
1375
1216
|
}
|
|
1376
|
-
function
|
|
1217
|
+
function annotateRationale(result, spec, hop) {
|
|
1377
1218
|
return {
|
|
1378
|
-
|
|
1379
|
-
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
-
args.uncertainty,
|
|
1383
|
-
args.baseRate
|
|
1384
|
-
),
|
|
1385
|
-
belief: args.belief,
|
|
1386
|
-
disbelief: args.disbelief,
|
|
1387
|
-
uncertainty: args.uncertainty,
|
|
1388
|
-
baseRate: args.baseRate,
|
|
1389
|
-
slOperator: args.slOperator ?? "manual_assessment",
|
|
1390
|
-
trigger: args.trigger,
|
|
1391
|
-
...args.rationale ? { rationale: args.rationale } : {},
|
|
1392
|
-
assessedBy: args.assessedBy,
|
|
1393
|
-
assessedAt: args.assessedAt,
|
|
1394
|
-
...args.triggeringEvidenceId ? {
|
|
1395
|
-
triggeringEvidenceId: args.triggeringEvidenceId,
|
|
1396
|
-
triggeringEvidenceIds: [String(args.triggeringEvidenceId)]
|
|
1397
|
-
} : {},
|
|
1398
|
-
...args.triggeringContradictionId ? {
|
|
1399
|
-
triggeringContradictionId: args.triggeringContradictionId
|
|
1400
|
-
} : {},
|
|
1401
|
-
...args.triggeringWorktreeId ? { triggeringWorktreeId: args.triggeringWorktreeId } : {}
|
|
1219
|
+
...result,
|
|
1220
|
+
rationale: `hop=${hop} edge=${spec.edgeType} damping=${spec.damping.toFixed(
|
|
1221
|
+
2
|
|
1222
|
+
)} :: ${result.rationale}`
|
|
1402
1223
|
};
|
|
1403
1224
|
}
|
|
1404
|
-
function
|
|
1405
|
-
|
|
1406
|
-
|
|
1407
|
-
|
|
1408
|
-
|
|
1409
|
-
|
|
1410
|
-
|
|
1411
|
-
|
|
1225
|
+
function propagatePositiveSupport(sourceOpinion, targetOpinion, edgeWeight) {
|
|
1226
|
+
const discounted = trustDiscount(sourceOpinion, Math.abs(edgeWeight));
|
|
1227
|
+
return {
|
|
1228
|
+
opinion: cumulativeFusion(targetOpinion, discounted),
|
|
1229
|
+
operator: "cumulative_fusion",
|
|
1230
|
+
rationale: `Supporting evidence (weight=${edgeWeight.toFixed(
|
|
1231
|
+
2
|
|
1232
|
+
)}) from source at ${project(sourceOpinion).toFixed(2)}`
|
|
1233
|
+
};
|
|
1412
1234
|
}
|
|
1413
|
-
function
|
|
1414
|
-
const
|
|
1415
|
-
|
|
1416
|
-
|
|
1417
|
-
|
|
1418
|
-
|
|
1419
|
-
|
|
1420
|
-
return readOpinionFromRecord(source);
|
|
1421
|
-
} catch (error) {
|
|
1422
|
-
debugGraphPrimitiveFallback(
|
|
1423
|
-
"[epistemicBeliefs] Failed to decode legacy belief opinion",
|
|
1424
|
-
{
|
|
1425
|
-
error
|
|
1426
|
-
}
|
|
1427
|
-
);
|
|
1428
|
-
return mkOpinion(0, 0, 1, 0.5);
|
|
1429
|
-
}
|
|
1430
|
-
}
|
|
1431
|
-
const confidence = clamp01(readFiniteNumber(source.confidence) ?? 0);
|
|
1432
|
-
return mkOpinion(confidence, 1 - confidence, 0, 0.5);
|
|
1235
|
+
function propagatePositiveInform(sourceOpinion, targetOpinion, edgeWeight) {
|
|
1236
|
+
const discounted = trustDiscount(sourceOpinion, Math.abs(edgeWeight));
|
|
1237
|
+
return {
|
|
1238
|
+
opinion: cumulativeFusion(targetOpinion, discounted),
|
|
1239
|
+
operator: "cumulative_fusion",
|
|
1240
|
+
rationale: `Supporting evidence (weight=${edgeWeight.toFixed(2)})`
|
|
1241
|
+
};
|
|
1433
1242
|
}
|
|
1434
|
-
function
|
|
1435
|
-
|
|
1436
|
-
|
|
1437
|
-
|
|
1438
|
-
|
|
1439
|
-
|
|
1440
|
-
Math.min(Math.floor(limit), MAX_PROJECT_BELIEF_LIMIT)
|
|
1243
|
+
function propagateNegativeSupportWithMetadata(sourceOpinion, targetOpinion, edgeWeight, edge) {
|
|
1244
|
+
return applyNegativeSupport(
|
|
1245
|
+
sourceOpinion,
|
|
1246
|
+
targetOpinion,
|
|
1247
|
+
edgeWeight,
|
|
1248
|
+
readEdgeMetadata(edge)
|
|
1441
1249
|
);
|
|
1442
1250
|
}
|
|
1443
|
-
|
|
1444
|
-
|
|
1445
|
-
|
|
1446
|
-
|
|
1447
|
-
|
|
1448
|
-
|
|
1449
|
-
|
|
1450
|
-
|
|
1451
|
-
|
|
1452
|
-
|
|
1453
|
-
|
|
1454
|
-
|
|
1455
|
-
|
|
1456
|
-
debugGraphPrimitiveFallback(
|
|
1457
|
-
"[epistemicBeliefs] Failed to read belief opinion snapshot",
|
|
1458
|
-
{
|
|
1459
|
-
error,
|
|
1460
|
-
beliefId: node._id
|
|
1461
|
-
}
|
|
1251
|
+
var propagateNegativeInform = applyNegativeEvidence;
|
|
1252
|
+
|
|
1253
|
+
// src/edges/contradicts.ts
|
|
1254
|
+
var contradictsPropagationSpec = {
|
|
1255
|
+
edgeType: "contradicts",
|
|
1256
|
+
direction: "bidirectional",
|
|
1257
|
+
transitivity: "none",
|
|
1258
|
+
damping: 0.85,
|
|
1259
|
+
maxHops: 1,
|
|
1260
|
+
operator: (sourceOpinion, targetOpinion, edge, context) => {
|
|
1261
|
+
const dampedSource = applyPerHopDamping(
|
|
1262
|
+
sourceOpinion,
|
|
1263
|
+
context.spec.damping
|
|
1462
1264
|
);
|
|
1463
|
-
|
|
1464
|
-
|
|
1465
|
-
|
|
1466
|
-
|
|
1467
|
-
|
|
1468
|
-
|
|
1469
|
-
if (criticality === "blocking") {
|
|
1470
|
-
return "critical";
|
|
1471
|
-
}
|
|
1472
|
-
if (criticality === "supporting") {
|
|
1473
|
-
return "minor";
|
|
1474
|
-
}
|
|
1475
|
-
return "significant";
|
|
1476
|
-
}
|
|
1477
|
-
function formatTupleContradictionDescription(args) {
|
|
1478
|
-
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)}.`;
|
|
1479
|
-
}
|
|
1480
|
-
function generateContentHash(text) {
|
|
1481
|
-
const content = `belief:${text.trim().toLowerCase().replace(/\s+/g, " ")}`;
|
|
1482
|
-
let hash = 5381;
|
|
1483
|
-
for (let i = 0; i < content.length; i++) {
|
|
1484
|
-
hash = (hash << 5) + hash + content.charCodeAt(i);
|
|
1485
|
-
hash &= hash;
|
|
1486
|
-
}
|
|
1487
|
-
return Math.abs(hash).toString(16).padStart(8, "0");
|
|
1488
|
-
}
|
|
1489
|
-
function resolveBeliefWorktreeId(metadata) {
|
|
1490
|
-
const worktreeId = metadata?.worktreeId;
|
|
1491
|
-
if (typeof worktreeId === "string" && worktreeId.trim().length > 0) {
|
|
1492
|
-
return worktreeId;
|
|
1493
|
-
}
|
|
1494
|
-
const sprintId = metadata?.sprintId;
|
|
1495
|
-
return typeof sprintId === "string" && sprintId.trim().length > 0 ? sprintId : void 0;
|
|
1496
|
-
}
|
|
1497
|
-
function normalizePillar(pillar) {
|
|
1498
|
-
if (!pillar) {
|
|
1499
|
-
return "other";
|
|
1500
|
-
}
|
|
1501
|
-
const lower = pillar.toLowerCase();
|
|
1502
|
-
const validPillars = [
|
|
1503
|
-
"market",
|
|
1504
|
-
"competition",
|
|
1505
|
-
"product",
|
|
1506
|
-
"team",
|
|
1507
|
-
"financials",
|
|
1508
|
-
"regulatory",
|
|
1509
|
-
"timing",
|
|
1510
|
-
"customer",
|
|
1511
|
-
"technology",
|
|
1512
|
-
"distribution",
|
|
1513
|
-
"deal",
|
|
1514
|
-
"risks"
|
|
1515
|
-
];
|
|
1516
|
-
return validPillars.find((p) => lower.includes(p)) || "other";
|
|
1517
|
-
}
|
|
1518
|
-
async function markBeliefGraphDirty(ctx, scope) {
|
|
1519
|
-
const projectId = typeof scope.projectId === "string" && scope.projectId.trim().length > 0 ? scope.projectId : void 0;
|
|
1520
|
-
const topicId = typeof scope.topicId === "string" && scope.topicId.trim().length > 0 ? scope.topicId : void 0;
|
|
1521
|
-
if (!projectId && !topicId) {
|
|
1522
|
-
return;
|
|
1523
|
-
}
|
|
1524
|
-
if (projectId) {
|
|
1525
|
-
await ctx.scheduler.runAfter(
|
|
1526
|
-
0,
|
|
1527
|
-
internal.graphAnalysisCache.markCacheStaleInternal,
|
|
1528
|
-
{ projectId }
|
|
1265
|
+
const negativeWeight = -Math.abs(edge.weight ?? 1);
|
|
1266
|
+
const result = propagateNegativeSupportWithMetadata(
|
|
1267
|
+
dampedSource,
|
|
1268
|
+
targetOpinion,
|
|
1269
|
+
negativeWeight,
|
|
1270
|
+
edge
|
|
1529
1271
|
);
|
|
1530
|
-
|
|
1531
|
-
|
|
1532
|
-
|
|
1533
|
-
|
|
1534
|
-
|
|
1535
|
-
|
|
1272
|
+
return annotateRationale(result, context.spec, context.hop);
|
|
1273
|
+
},
|
|
1274
|
+
description: "Legacy contradiction edges move negative pressure in either direction, but never beyond one hop."
|
|
1275
|
+
};
|
|
1276
|
+
var dependsOnPropagationSpec = {
|
|
1277
|
+
edgeType: "depends_on",
|
|
1278
|
+
direction: "incoming",
|
|
1279
|
+
transitivity: "damped",
|
|
1280
|
+
damping: 0.8,
|
|
1281
|
+
maxHops: "unbounded",
|
|
1282
|
+
operator: (sourceOpinion, targetOpinion, edge, context) => {
|
|
1283
|
+
const dampedSource = applyPerHopDamping(
|
|
1284
|
+
sourceOpinion,
|
|
1285
|
+
context.spec.damping
|
|
1536
1286
|
);
|
|
1537
|
-
|
|
1538
|
-
|
|
1539
|
-
|
|
1540
|
-
|
|
1541
|
-
|
|
1542
|
-
|
|
1287
|
+
const metadata = readEdgeMetadata(edge);
|
|
1288
|
+
if (metadata.conditionalA && metadata.conditionalNotA) {
|
|
1289
|
+
const deducedOpinion = conditionalDeduction(
|
|
1290
|
+
dampedSource,
|
|
1291
|
+
mkOpinion(
|
|
1292
|
+
metadata.conditionalA.b,
|
|
1293
|
+
metadata.conditionalA.d,
|
|
1294
|
+
metadata.conditionalA.u,
|
|
1295
|
+
metadata.conditionalA.a
|
|
1296
|
+
),
|
|
1297
|
+
mkOpinion(
|
|
1298
|
+
metadata.conditionalNotA.b,
|
|
1299
|
+
metadata.conditionalNotA.d,
|
|
1300
|
+
metadata.conditionalNotA.u,
|
|
1301
|
+
metadata.conditionalNotA.a
|
|
1302
|
+
),
|
|
1303
|
+
targetOpinion.a
|
|
1304
|
+
);
|
|
1305
|
+
return annotateRationale(
|
|
1306
|
+
{
|
|
1307
|
+
opinion: deducedOpinion,
|
|
1308
|
+
operator: "conditional_deduction",
|
|
1309
|
+
rationale: `Conditional deduction: prerequisite at ${project(
|
|
1310
|
+
dampedSource
|
|
1311
|
+
).toFixed(2)}`
|
|
1312
|
+
},
|
|
1313
|
+
context.spec,
|
|
1314
|
+
context.hop
|
|
1315
|
+
);
|
|
1543
1316
|
}
|
|
1544
|
-
|
|
1545
|
-
|
|
1546
|
-
|
|
1547
|
-
|
|
1548
|
-
return null;
|
|
1549
|
-
}
|
|
1550
|
-
try {
|
|
1551
|
-
return await resolveTopicProjectScope(ctx, {
|
|
1552
|
-
projectId: args.projectId ?? void 0,
|
|
1553
|
-
topicId: args.topicId ?? void 0
|
|
1554
|
-
});
|
|
1555
|
-
} catch (error) {
|
|
1556
|
-
debugGraphPrimitiveFallback(
|
|
1557
|
-
"[epistemicBeliefs] Failed to resolve belief scope",
|
|
1558
|
-
{
|
|
1559
|
-
error,
|
|
1560
|
-
projectId: args.projectId,
|
|
1561
|
-
topicId: args.topicId
|
|
1562
|
-
}
|
|
1317
|
+
const result = dampedDependencyCascade(
|
|
1318
|
+
dampedSource,
|
|
1319
|
+
targetOpinion,
|
|
1320
|
+
metadata.propagation ?? "continuous"
|
|
1563
1321
|
);
|
|
1564
|
-
return
|
|
1565
|
-
}
|
|
1566
|
-
|
|
1567
|
-
|
|
1568
|
-
|
|
1569
|
-
|
|
1570
|
-
|
|
1571
|
-
|
|
1572
|
-
|
|
1573
|
-
|
|
1574
|
-
|
|
1575
|
-
|
|
1576
|
-
|
|
1577
|
-
|
|
1578
|
-
|
|
1579
|
-
|
|
1322
|
+
return annotateRationale(result, context.spec, context.hop);
|
|
1323
|
+
},
|
|
1324
|
+
description: "Structural gating. Textbook conditional deduction when edge conditionals exist, otherwise damped dependency cascade through downstream chains."
|
|
1325
|
+
};
|
|
1326
|
+
|
|
1327
|
+
// src/edges/derivedFrom.ts
|
|
1328
|
+
var derivedFromPropagationSpec = {
|
|
1329
|
+
edgeType: "derived_from",
|
|
1330
|
+
direction: "incoming",
|
|
1331
|
+
transitivity: "none",
|
|
1332
|
+
damping: 1,
|
|
1333
|
+
maxHops: 1,
|
|
1334
|
+
operator: () => null,
|
|
1335
|
+
description: "Provenance only. The traversal surface stays explicit, but confidence does not move across derived_from edges."
|
|
1336
|
+
};
|
|
1337
|
+
|
|
1338
|
+
// src/edges/elaborates.ts
|
|
1339
|
+
var elaboratesPropagationSpec = {
|
|
1340
|
+
edgeType: "elaborates",
|
|
1341
|
+
direction: "outgoing",
|
|
1342
|
+
transitivity: "damped",
|
|
1343
|
+
damping: 0.7,
|
|
1344
|
+
maxHops: 2,
|
|
1345
|
+
operator: (sourceOpinion, targetOpinion, edge, context) => {
|
|
1346
|
+
const dampedSource = applyPerHopDamping(
|
|
1347
|
+
sourceOpinion,
|
|
1348
|
+
context.spec.damping
|
|
1349
|
+
);
|
|
1350
|
+
const contextualWeight = Math.min(Math.abs(edge.weight ?? 0.35), 0.35);
|
|
1351
|
+
const result = propagatePositiveInform(
|
|
1352
|
+
dampedSource,
|
|
1353
|
+
targetOpinion,
|
|
1354
|
+
contextualWeight
|
|
1355
|
+
);
|
|
1356
|
+
return annotateRationale(result, context.spec, context.hop);
|
|
1357
|
+
},
|
|
1358
|
+
description: "Context-rich supporting detail. Elaborates carries a small positive effect with short, damped chaining."
|
|
1359
|
+
};
|
|
1360
|
+
|
|
1361
|
+
// src/edges/informs.ts
|
|
1362
|
+
var informsPropagationSpec = {
|
|
1363
|
+
edgeType: "informs",
|
|
1364
|
+
direction: "outgoing",
|
|
1365
|
+
transitivity: "full",
|
|
1366
|
+
damping: 0.92,
|
|
1367
|
+
maxHops: "unbounded",
|
|
1368
|
+
operator: (sourceOpinion, targetOpinion, edge, context) => {
|
|
1369
|
+
const dampedSource = applyPerHopDamping(
|
|
1370
|
+
sourceOpinion,
|
|
1371
|
+
context.spec.damping
|
|
1372
|
+
);
|
|
1373
|
+
const weight = edge.weight ?? 1;
|
|
1374
|
+
const result = weight < 0 ? propagateNegativeInform(dampedSource, targetOpinion, weight) : propagatePositiveInform(dampedSource, targetOpinion, weight);
|
|
1375
|
+
return annotateRationale(result, context.spec, context.hop);
|
|
1376
|
+
},
|
|
1377
|
+
description: "Evidence-bearing influence. Informs can chain through the graph with light per-hop damping."
|
|
1378
|
+
};
|
|
1379
|
+
|
|
1380
|
+
// src/edges/propagationTypes.ts
|
|
1381
|
+
function isPropagationTraversalDirection(direction) {
|
|
1382
|
+
return direction === "outgoing" || direction === "incoming";
|
|
1580
1383
|
}
|
|
1581
|
-
function
|
|
1582
|
-
|
|
1583
|
-
registryRows.map((row) => [
|
|
1584
|
-
normalizeAudienceKey(row.audienceKey),
|
|
1585
|
-
row.audienceClass
|
|
1586
|
-
])
|
|
1587
|
-
);
|
|
1588
|
-
return (audienceKey, fallback) => {
|
|
1589
|
-
const key = normalizeAudienceKey(audienceKey);
|
|
1590
|
-
if (!key) {
|
|
1591
|
-
return fallback;
|
|
1592
|
-
}
|
|
1593
|
-
return audienceClassByKey.get(key) ?? classFromAudienceKey(key, fallback);
|
|
1594
|
-
};
|
|
1384
|
+
function canTraverseHop(spec, nextHop) {
|
|
1385
|
+
return spec.maxHops === "unbounded" || nextHop <= spec.maxHops;
|
|
1595
1386
|
}
|
|
1596
|
-
function
|
|
1597
|
-
|
|
1598
|
-
|
|
1599
|
-
|
|
1600
|
-
return
|
|
1601
|
-
_id: node._id,
|
|
1602
|
-
_epistemicNodeId: node._id,
|
|
1603
|
-
_creationTime: node._creationTime,
|
|
1604
|
-
belief: node.canonicalText,
|
|
1605
|
-
formulation: node.canonicalText,
|
|
1606
|
-
projectId: node.projectId,
|
|
1607
|
-
topicId: node.topicId,
|
|
1608
|
-
userId: node.userId || node.createdBy || "",
|
|
1609
|
-
confidence: meta.confidence || "untested",
|
|
1610
|
-
status: node.status,
|
|
1611
|
-
beliefStatus: resolveBeliefStatus(node, meta),
|
|
1612
|
-
topic: meta.topic || meta.pillar || "other",
|
|
1613
|
-
pillar: meta.pillar || meta.topic || "",
|
|
1614
|
-
category: meta.category || "",
|
|
1615
|
-
subcategory: meta.subcategory || "",
|
|
1616
|
-
categoryIcon: meta.categoryIcon || "",
|
|
1617
|
-
supportingEvidence: meta.supportingEvidenceIds || [],
|
|
1618
|
-
contradictingEvidence: meta.contradictingEvidenceIds || [],
|
|
1619
|
-
testingQuestions: meta.testingQuestionIds || [],
|
|
1620
|
-
linkedInsights: meta.linkedInsightIds || [],
|
|
1621
|
-
createdAt: node.createdAt,
|
|
1622
|
-
updatedAt: node.updatedAt || node.createdAt,
|
|
1623
|
-
tupleContradicted,
|
|
1624
|
-
sprintId: meta.sprintId || void 0,
|
|
1625
|
-
worktreeId,
|
|
1626
|
-
sourceBeliefIds: meta.sourceBeliefIds || void 0,
|
|
1627
|
-
criticality: meta.criticality || "unanalyzed",
|
|
1628
|
-
rationale: meta.rationale || "",
|
|
1629
|
-
audienceLabel: node.audienceLabel,
|
|
1630
|
-
policyTags: node.policyTags,
|
|
1631
|
-
sensitivityTier: node.sensitivityTier,
|
|
1632
|
-
exportClass: node.exportClass,
|
|
1633
|
-
anonymizationClass: node.anonymizationClass
|
|
1634
|
-
};
|
|
1387
|
+
function canContinueTransitively(spec, currentHop) {
|
|
1388
|
+
if (spec.transitivity === "none") {
|
|
1389
|
+
return false;
|
|
1390
|
+
}
|
|
1391
|
+
return spec.maxHops === "unbounded" || currentHop < spec.maxHops;
|
|
1635
1392
|
}
|
|
1636
|
-
|
|
1637
|
-
|
|
1638
|
-
|
|
1639
|
-
|
|
1640
|
-
|
|
1641
|
-
|
|
1642
|
-
|
|
1393
|
+
|
|
1394
|
+
// src/edges/refutes.ts
|
|
1395
|
+
var refutesPropagationSpec = {
|
|
1396
|
+
edgeType: "refutes",
|
|
1397
|
+
direction: "outgoing",
|
|
1398
|
+
transitivity: "none",
|
|
1399
|
+
damping: 0.9,
|
|
1400
|
+
maxHops: 1,
|
|
1401
|
+
operator: (sourceOpinion, targetOpinion, edge, context) => {
|
|
1402
|
+
const dampedSource = applyPerHopDamping(
|
|
1403
|
+
sourceOpinion,
|
|
1404
|
+
context.spec.damping
|
|
1405
|
+
);
|
|
1406
|
+
const negativeWeight = -Math.abs(edge.weight ?? 1);
|
|
1407
|
+
const result = propagateNegativeInform(
|
|
1408
|
+
dampedSource,
|
|
1409
|
+
targetOpinion,
|
|
1410
|
+
negativeWeight
|
|
1411
|
+
);
|
|
1412
|
+
return annotateRationale(result, context.spec, context.hop);
|
|
1413
|
+
},
|
|
1414
|
+
description: "Explicit negative evidence semantics. Refutes is treated as strong one-hop counter-evidence."
|
|
1415
|
+
};
|
|
1416
|
+
|
|
1417
|
+
// src/edges/supports.ts
|
|
1418
|
+
var supportsPropagationSpec = {
|
|
1419
|
+
edgeType: "supports",
|
|
1420
|
+
direction: "outgoing",
|
|
1421
|
+
transitivity: "full",
|
|
1422
|
+
damping: 0.85,
|
|
1423
|
+
maxHops: "unbounded",
|
|
1424
|
+
operator: (sourceOpinion, targetOpinion, edge, context) => {
|
|
1425
|
+
const dampedSource = applyPerHopDamping(
|
|
1426
|
+
sourceOpinion,
|
|
1427
|
+
context.spec.damping
|
|
1428
|
+
);
|
|
1429
|
+
const weight = edge.weight ?? 1;
|
|
1430
|
+
const result = weight < 0 ? propagateNegativeSupportWithMetadata(
|
|
1431
|
+
dampedSource,
|
|
1432
|
+
targetOpinion,
|
|
1433
|
+
weight,
|
|
1434
|
+
edge
|
|
1435
|
+
) : propagatePositiveSupport(dampedSource, targetOpinion, weight);
|
|
1436
|
+
return annotateRationale(result, context.spec, context.hop);
|
|
1437
|
+
},
|
|
1438
|
+
description: "Belief-to-belief influence. Supports chains transitively with moderate per-hop damping."
|
|
1439
|
+
};
|
|
1440
|
+
|
|
1441
|
+
// src/edges/tests.ts
|
|
1442
|
+
var testsPropagationSpec = {
|
|
1443
|
+
edgeType: "tests",
|
|
1444
|
+
direction: "outgoing",
|
|
1445
|
+
transitivity: "none",
|
|
1446
|
+
damping: 1,
|
|
1447
|
+
maxHops: 1,
|
|
1448
|
+
operator: () => null,
|
|
1449
|
+
description: "Interrogation linkage only. Tests edges do not directly move confidence."
|
|
1450
|
+
};
|
|
1451
|
+
|
|
1452
|
+
// src/edges/index.ts
|
|
1453
|
+
var EDGE_PROPAGATION_SPECS = [
|
|
1454
|
+
supportsPropagationSpec,
|
|
1455
|
+
informsPropagationSpec,
|
|
1456
|
+
dependsOnPropagationSpec,
|
|
1457
|
+
derivedFromPropagationSpec,
|
|
1458
|
+
containsPropagationSpec,
|
|
1459
|
+
testsPropagationSpec,
|
|
1460
|
+
contradictsPropagationSpec,
|
|
1461
|
+
refutesPropagationSpec,
|
|
1462
|
+
elaboratesPropagationSpec
|
|
1463
|
+
];
|
|
1464
|
+
new Map(EDGE_PROPAGATION_SPECS.map((spec) => [spec.edgeType, spec]));
|
|
1465
|
+
function getEdgePropagationSpecs() {
|
|
1466
|
+
return EDGE_PROPAGATION_SPECS;
|
|
1643
1467
|
}
|
|
1644
|
-
|
|
1645
|
-
|
|
1646
|
-
|
|
1647
|
-
const worktree = await ctx.db.get(membership.worktreeId);
|
|
1648
|
-
if (worktree?.status === "completed" || worktree?.status === "merged") {
|
|
1649
|
-
return true;
|
|
1650
|
-
}
|
|
1468
|
+
function getTraversalDirections(direction) {
|
|
1469
|
+
if (isPropagationTraversalDirection(direction)) {
|
|
1470
|
+
return [direction];
|
|
1651
1471
|
}
|
|
1652
|
-
return
|
|
1472
|
+
return ["outgoing", "incoming"];
|
|
1473
|
+
}
|
|
1474
|
+
|
|
1475
|
+
// src/confidencePropagationDispatch.ts
|
|
1476
|
+
function resolveTraversalTargetNodeId(edge, direction) {
|
|
1477
|
+
const targetNodeId = direction === "outgoing" ? edge.toNodeId : edge.fromNodeId;
|
|
1478
|
+
return targetNodeId ?? void 0;
|
|
1653
1479
|
}
|
|
1654
|
-
|
|
1480
|
+
function readNodeOpinion(node) {
|
|
1481
|
+
const metadata = node.metadata ?? {};
|
|
1655
1482
|
try {
|
|
1656
|
-
|
|
1657
|
-
|
|
1658
|
-
|
|
1659
|
-
|
|
1660
|
-
|
|
1661
|
-
|
|
1662
|
-
};
|
|
1663
|
-
} catch
|
|
1664
|
-
|
|
1665
|
-
|
|
1666
|
-
|
|
1667
|
-
|
|
1483
|
+
return readOpinionFromRecord({
|
|
1484
|
+
...metadata,
|
|
1485
|
+
opinion_b: node.opinion_b,
|
|
1486
|
+
opinion_d: node.opinion_d,
|
|
1487
|
+
opinion_u: node.opinion_u,
|
|
1488
|
+
opinion_a: node.opinion_a
|
|
1489
|
+
});
|
|
1490
|
+
} catch {
|
|
1491
|
+
return mkOpinion(0, 0, 1, 0.5);
|
|
1492
|
+
}
|
|
1493
|
+
}
|
|
1494
|
+
async function collectConfidencePropagationDispatches(args) {
|
|
1495
|
+
const dispatchesByTargetId = /* @__PURE__ */ new Map();
|
|
1496
|
+
const opinionCache = /* @__PURE__ */ new Map();
|
|
1497
|
+
const nodeCache = /* @__PURE__ */ new Map();
|
|
1498
|
+
const traversalSpecs = args.traversalSpecs ?? getEdgePropagationSpecs();
|
|
1499
|
+
const queue = [
|
|
1500
|
+
{
|
|
1501
|
+
nodeId: args.sourceNodeId,
|
|
1502
|
+
opinion: args.sourceOpinion,
|
|
1503
|
+
hop: 0,
|
|
1504
|
+
visitedNodeIds: /* @__PURE__ */ new Set([String(args.sourceNodeId)])
|
|
1505
|
+
}
|
|
1506
|
+
];
|
|
1507
|
+
const loadNode = async (nodeId) => {
|
|
1508
|
+
const cacheKey = String(nodeId);
|
|
1509
|
+
if (!nodeCache.has(cacheKey)) {
|
|
1510
|
+
nodeCache.set(cacheKey, await args.getNode(nodeId));
|
|
1511
|
+
}
|
|
1512
|
+
return nodeCache.get(cacheKey) ?? null;
|
|
1513
|
+
};
|
|
1514
|
+
while (queue.length > 0) {
|
|
1515
|
+
const state = queue.shift();
|
|
1516
|
+
if (!state) {
|
|
1517
|
+
continue;
|
|
1518
|
+
}
|
|
1519
|
+
for (const spec of traversalSpecs) {
|
|
1520
|
+
const nextHop = state.hop + 1;
|
|
1521
|
+
if (!canTraverseHop(spec, nextHop)) {
|
|
1522
|
+
continue;
|
|
1668
1523
|
}
|
|
1669
|
-
|
|
1670
|
-
|
|
1524
|
+
for (const direction of getTraversalDirections(spec.direction)) {
|
|
1525
|
+
const edges = await args.queryEdges({
|
|
1526
|
+
nodeId: state.nodeId,
|
|
1527
|
+
spec,
|
|
1528
|
+
direction,
|
|
1529
|
+
hop: nextHop
|
|
1530
|
+
});
|
|
1531
|
+
for (const edge of edges) {
|
|
1532
|
+
if (args.sourceScope && !edgeMatchesWorkspaceReasoningScope(edge, args.sourceScope)) {
|
|
1533
|
+
continue;
|
|
1534
|
+
}
|
|
1535
|
+
const targetNodeId = resolveTraversalTargetNodeId(edge, direction);
|
|
1536
|
+
if (!targetNodeId) {
|
|
1537
|
+
continue;
|
|
1538
|
+
}
|
|
1539
|
+
if (state.visitedNodeIds.has(String(targetNodeId))) {
|
|
1540
|
+
continue;
|
|
1541
|
+
}
|
|
1542
|
+
const targetNode = await loadNode(targetNodeId);
|
|
1543
|
+
if (!targetNode || targetNode.nodeType !== "belief") {
|
|
1544
|
+
continue;
|
|
1545
|
+
}
|
|
1546
|
+
if (args.sourceScope && !nodeMatchesWorkspaceReasoningScope(targetNode, args.sourceScope)) {
|
|
1547
|
+
continue;
|
|
1548
|
+
}
|
|
1549
|
+
const cacheKey = String(targetNodeId);
|
|
1550
|
+
const targetOpinion = opinionCache.get(cacheKey) ?? readNodeOpinion(targetNode);
|
|
1551
|
+
const result = spec.operator(state.opinion, targetOpinion, edge, {
|
|
1552
|
+
hop: nextHop,
|
|
1553
|
+
sourceNodeId: state.nodeId,
|
|
1554
|
+
targetNodeId,
|
|
1555
|
+
traversedDirection: direction,
|
|
1556
|
+
spec
|
|
1557
|
+
});
|
|
1558
|
+
if (!result || !hasProjectedOpinionChanged(targetOpinion, result.opinion)) {
|
|
1559
|
+
continue;
|
|
1560
|
+
}
|
|
1561
|
+
const projectedOpinion = mkOpinion(
|
|
1562
|
+
result.opinion.b,
|
|
1563
|
+
result.opinion.d,
|
|
1564
|
+
result.opinion.u,
|
|
1565
|
+
result.opinion.a
|
|
1566
|
+
);
|
|
1567
|
+
opinionCache.set(cacheKey, projectedOpinion);
|
|
1568
|
+
const existingDispatch = dispatchesByTargetId.get(cacheKey);
|
|
1569
|
+
dispatchesByTargetId.set(cacheKey, {
|
|
1570
|
+
targetNodeId,
|
|
1571
|
+
edgeType: spec.edgeType,
|
|
1572
|
+
traversedDirection: direction,
|
|
1573
|
+
weight: edge.weight ?? 1,
|
|
1574
|
+
opinion: projectedOpinion,
|
|
1575
|
+
operator: result.operator,
|
|
1576
|
+
rationale: existingDispatch ? `${existingDispatch.rationale}; ${result.rationale}` : result.rationale,
|
|
1577
|
+
hop: nextHop
|
|
1578
|
+
});
|
|
1579
|
+
if (canContinueTransitively(spec, nextHop)) {
|
|
1580
|
+
queue.push({
|
|
1581
|
+
nodeId: targetNodeId,
|
|
1582
|
+
opinion: projectedOpinion,
|
|
1583
|
+
hop: nextHop,
|
|
1584
|
+
visitedNodeIds: /* @__PURE__ */ new Set([
|
|
1585
|
+
...state.visitedNodeIds,
|
|
1586
|
+
String(targetNodeId)
|
|
1587
|
+
])
|
|
1588
|
+
});
|
|
1589
|
+
}
|
|
1590
|
+
}
|
|
1591
|
+
}
|
|
1592
|
+
}
|
|
1671
1593
|
}
|
|
1594
|
+
return Array.from(dispatchesByTargetId.values()).sort((left, right) => {
|
|
1595
|
+
if (left.hop !== right.hop) {
|
|
1596
|
+
return left.hop - right.hop;
|
|
1597
|
+
}
|
|
1598
|
+
return String(left.targetNodeId).localeCompare(String(right.targetNodeId));
|
|
1599
|
+
});
|
|
1672
1600
|
}
|
|
1601
|
+
|
|
1602
|
+
// src/epistemicBeliefs.confidence.ts
|
|
1673
1603
|
async function applyBeliefConfidenceChange(ctx, args) {
|
|
1674
1604
|
const now = Date.now();
|
|
1675
1605
|
const node = await ctx.db.get(args.nodeId);
|
|
@@ -1976,32 +1906,49 @@ var propagateConfidenceChange = internalMutation({
|
|
|
1976
1906
|
};
|
|
1977
1907
|
}
|
|
1978
1908
|
});
|
|
1979
|
-
|
|
1980
|
-
|
|
1981
|
-
|
|
1982
|
-
|
|
1983
|
-
|
|
1984
|
-
|
|
1985
|
-
|
|
1986
|
-
|
|
1987
|
-
|
|
1988
|
-
|
|
1909
|
+
|
|
1910
|
+
// src/embeddingTrigger.ts
|
|
1911
|
+
async function scheduleEmbeddingGeneration(args) {
|
|
1912
|
+
try {
|
|
1913
|
+
await args.ctx.scheduler.runAfter(
|
|
1914
|
+
0,
|
|
1915
|
+
"embeddingActions:generateEpistemicNodeEmbedding",
|
|
1916
|
+
{
|
|
1917
|
+
nodeId: args.nodeId,
|
|
1918
|
+
projectId: args.projectId ? String(args.projectId) : void 0,
|
|
1919
|
+
topicId: args.topicId ? String(args.topicId) : void 0,
|
|
1920
|
+
createdBy: args.createdBy,
|
|
1921
|
+
nodeType: args.nodeType,
|
|
1922
|
+
text: args.text.slice(0, 2e4),
|
|
1923
|
+
hasAnswer: args.hasAnswer,
|
|
1924
|
+
confidence: args.confidence
|
|
1925
|
+
}
|
|
1926
|
+
);
|
|
1927
|
+
} catch (error) {
|
|
1928
|
+
debugGraphPrimitiveFallback(
|
|
1929
|
+
"[embeddingTrigger] Failed to schedule embedding generation",
|
|
1930
|
+
{
|
|
1931
|
+
error,
|
|
1932
|
+
nodeId: String(args.nodeId),
|
|
1933
|
+
nodeType: args.nodeType
|
|
1934
|
+
}
|
|
1935
|
+
);
|
|
1989
1936
|
}
|
|
1990
|
-
return userId;
|
|
1991
1937
|
}
|
|
1992
|
-
|
|
1993
|
-
|
|
1994
|
-
|
|
1995
|
-
|
|
1996
|
-
|
|
1997
|
-
|
|
1998
|
-
|
|
1999
|
-
|
|
2000
|
-
|
|
2001
|
-
|
|
2002
|
-
|
|
2003
|
-
}
|
|
1938
|
+
|
|
1939
|
+
// src/globalId.ts
|
|
1940
|
+
function generateGlobalId() {
|
|
1941
|
+
const bytes = new Uint8Array(16);
|
|
1942
|
+
crypto.getRandomValues(bytes);
|
|
1943
|
+
bytes[6] = bytes[6] & 15 | 64;
|
|
1944
|
+
bytes[8] = bytes[8] & 63 | 128;
|
|
1945
|
+
const hex = Array.from(bytes, (b) => b.toString(16).padStart(2, "0")).join(
|
|
1946
|
+
""
|
|
1947
|
+
);
|
|
1948
|
+
return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20)}`;
|
|
2004
1949
|
}
|
|
1950
|
+
|
|
1951
|
+
// src/epistemicBeliefs.core.ts
|
|
2005
1952
|
var create = mutation({
|
|
2006
1953
|
args: {
|
|
2007
1954
|
...optionalBeliefScopeArgs,
|
|
@@ -2419,11 +2366,11 @@ var getByProject = query({
|
|
|
2419
2366
|
return [];
|
|
2420
2367
|
}
|
|
2421
2368
|
}
|
|
2422
|
-
const
|
|
2369
|
+
const query6 = ctx.db.query("epistemicNodes").withIndex(
|
|
2423
2370
|
"by_topic_type",
|
|
2424
2371
|
(q) => q.eq("topicId", scope.topicId).eq("nodeType", "belief")
|
|
2425
2372
|
);
|
|
2426
|
-
const nodes = await
|
|
2373
|
+
const nodes = await query6.order("desc").take(scanLimit);
|
|
2427
2374
|
const scopedNodes = nodes.filter(
|
|
2428
2375
|
(node) => nodeMatchesWorkspaceReasoningScope(node, scope)
|
|
2429
2376
|
);
|
|
@@ -2453,11 +2400,11 @@ var getByTopic = query({
|
|
|
2453
2400
|
const scope = await resolveTopicProjectScope(ctx, {
|
|
2454
2401
|
topicId: args.topicId
|
|
2455
2402
|
});
|
|
2456
|
-
const
|
|
2403
|
+
const query6 = ctx.db.query("epistemicNodes").withIndex(
|
|
2457
2404
|
"by_topic_type",
|
|
2458
2405
|
(q) => q.eq("topicId", args.topicId).eq("nodeType", "belief")
|
|
2459
2406
|
);
|
|
2460
|
-
const nodes = await
|
|
2407
|
+
const nodes = await query6.order("desc").take(scanLimit);
|
|
2461
2408
|
const scopedNodes = nodes.filter(
|
|
2462
2409
|
(node) => nodeMatchesWorkspaceReasoningScope(node, {
|
|
2463
2410
|
tenantId: scope.tenantId,
|
|
@@ -2962,7 +2909,9 @@ var getByIds = query({
|
|
|
2962
2909
|
},
|
|
2963
2910
|
returns: permissiveReturn,
|
|
2964
2911
|
handler: async (ctx, args) => {
|
|
2965
|
-
const beliefs = await Promise.all(
|
|
2912
|
+
const beliefs = await Promise.all(
|
|
2913
|
+
args.nodeIds.map((id) => ctx.db.get(id))
|
|
2914
|
+
);
|
|
2966
2915
|
return beliefs.filter((b) => b !== null && b.nodeType === "belief");
|
|
2967
2916
|
}
|
|
2968
2917
|
});
|
|
@@ -2985,6 +2934,69 @@ var getByWorktree = query({
|
|
|
2985
2934
|
return beliefs;
|
|
2986
2935
|
}
|
|
2987
2936
|
});
|
|
2937
|
+
|
|
2938
|
+
// src/logicalRoleInference.ts
|
|
2939
|
+
var PILLAR_IMPORTANCE = {
|
|
2940
|
+
market: 1,
|
|
2941
|
+
competition: 2,
|
|
2942
|
+
product: 3,
|
|
2943
|
+
team: 4,
|
|
2944
|
+
financials: 5,
|
|
2945
|
+
timing: 6,
|
|
2946
|
+
other: 10
|
|
2947
|
+
};
|
|
2948
|
+
async function computeLogicalRole(ctx, evidenceId, beliefId) {
|
|
2949
|
+
const belief = await ctx.db.get(beliefId);
|
|
2950
|
+
if (!belief || belief.nodeType !== "belief") {
|
|
2951
|
+
return "contributory";
|
|
2952
|
+
}
|
|
2953
|
+
const beliefMetadata = belief.metadata;
|
|
2954
|
+
const pillar = beliefMetadata?.pillar || "other";
|
|
2955
|
+
const pillarRank = PILLAR_IMPORTANCE[pillar] ?? 10;
|
|
2956
|
+
const isSynthesized = await checkIfSynthesizedHypothesis(ctx, beliefId);
|
|
2957
|
+
const testingQuestions = await getTestingQuestionsForBelief(ctx, beliefId);
|
|
2958
|
+
const answeredQuestions = await getQuestionsAnsweredByEvidence(
|
|
2959
|
+
ctx,
|
|
2960
|
+
evidenceId
|
|
2961
|
+
);
|
|
2962
|
+
const directlyTests = testingQuestions.filter(
|
|
2963
|
+
(questionId) => answeredQuestions.includes(questionId)
|
|
2964
|
+
);
|
|
2965
|
+
if (directlyTests.length === 0) {
|
|
2966
|
+
return "contributory";
|
|
2967
|
+
}
|
|
2968
|
+
if (isSynthesized && pillarRank <= 2) {
|
|
2969
|
+
return directlyTests.length > 1 ? "necessary_sufficient" : "necessary";
|
|
2970
|
+
}
|
|
2971
|
+
if (isSynthesized) {
|
|
2972
|
+
return "necessary";
|
|
2973
|
+
}
|
|
2974
|
+
return directlyTests.length > 1 ? "necessary" : "contributory";
|
|
2975
|
+
}
|
|
2976
|
+
async function checkIfSynthesizedHypothesis(ctx, beliefId) {
|
|
2977
|
+
const belief = await ctx.db.get(beliefId);
|
|
2978
|
+
if (!belief) {
|
|
2979
|
+
return false;
|
|
2980
|
+
}
|
|
2981
|
+
const metadata = belief.metadata;
|
|
2982
|
+
return metadata?.isSynthesized === true || metadata?.beliefStatus === "hypothesis";
|
|
2983
|
+
}
|
|
2984
|
+
async function getTestingQuestionsForBelief(ctx, beliefId) {
|
|
2985
|
+
const testEdges = await ctx.db.query("epistemicEdges").withIndex(
|
|
2986
|
+
"by_to_type",
|
|
2987
|
+
(q) => q.eq("toNodeId", beliefId).eq("edgeType", "tests")
|
|
2988
|
+
).collect();
|
|
2989
|
+
return testEdges.map((edge) => edge.fromNodeId).filter((id) => id !== void 0);
|
|
2990
|
+
}
|
|
2991
|
+
async function getQuestionsAnsweredByEvidence(ctx, evidenceId) {
|
|
2992
|
+
const answerEdges = await ctx.db.query("epistemicEdges").withIndex(
|
|
2993
|
+
"by_from_type",
|
|
2994
|
+
(q) => q.eq("fromNodeId", evidenceId).eq("edgeType", "derived_from")
|
|
2995
|
+
).collect();
|
|
2996
|
+
return answerEdges.map((edge) => edge.toNodeId).filter((id) => id !== void 0);
|
|
2997
|
+
}
|
|
2998
|
+
|
|
2999
|
+
// src/epistemicBeliefs.links.ts
|
|
2988
3000
|
var updatePillar = mutation({
|
|
2989
3001
|
args: {
|
|
2990
3002
|
nodeId: v.id("epistemicNodes"),
|
|
@@ -3775,11 +3787,11 @@ var internalGetByTopic = internalQuery({
|
|
|
3775
3787
|
});
|
|
3776
3788
|
const resolveAudienceClass = createBeliefAudienceResolver(registryRows);
|
|
3777
3789
|
const viewerClass = resolveAudienceClass(audienceMode, "public");
|
|
3778
|
-
const
|
|
3790
|
+
const query6 = ctx.db.query("epistemicNodes").withIndex(
|
|
3779
3791
|
"by_topic_type",
|
|
3780
3792
|
(q) => q.eq("topicId", args.topicId).eq("nodeType", "belief")
|
|
3781
3793
|
);
|
|
3782
|
-
const nodes = await
|
|
3794
|
+
const nodes = await query6.order("desc").take(scanLimit);
|
|
3783
3795
|
let filtered = nodes.filter(
|
|
3784
3796
|
(node) => canAudienceClassAccess(
|
|
3785
3797
|
viewerClass,
|