@lucern/graph-primitives 1.0.29 → 1.0.31
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{beliefDecay-DZ6tkLYq.d.ts → beliefDecay-BmkEk5OJ.d.ts} +3 -3
- package/dist/beliefDecay.d.ts +1 -1
- package/dist/beliefDecay.js +448 -314
- package/dist/beliefDecay.js.map +1 -1
- package/dist/{beliefEvidenceLinks-CWOXxxJg.d.ts → beliefEvidenceLinks-BzfjON_6.d.ts} +13 -13
- package/dist/beliefEvidenceLinks.d.ts +1 -1
- package/dist/beliefEvidenceLinks.js +843 -624
- package/dist/beliefEvidenceLinks.js.map +1 -1
- package/dist/beliefEvidenceLinks.operational.d.ts +7 -5
- package/dist/beliefEvidenceLinks.operational.js +91 -18
- package/dist/beliefEvidenceLinks.operational.js.map +1 -1
- package/dist/beliefLifecycle.js.map +1 -1
- package/dist/confidencePropagationDispatch.d.ts +28 -27
- package/dist/confidencePropagationDispatch.js +157 -99
- package/dist/confidencePropagationDispatch.js.map +1 -1
- package/dist/{contradictions-51VLsESq.d.ts → contradictions-BATPuZTL.d.ts} +10 -10
- package/dist/contradictions.d.ts +1 -1
- package/dist/contradictions.js +395 -225
- package/dist/contradictions.js.map +1 -1
- package/dist/convex.d.ts +65 -30
- package/dist/convex.js +7 -3
- package/dist/convex.js.map +1 -1
- package/dist/debug.js.map +1 -1
- package/dist/edgeValidation.js +293 -85
- package/dist/edgeValidation.js.map +1 -1
- package/dist/edges/contains.d.ts +1 -1
- package/dist/edges/contains.js.map +1 -1
- package/dist/edges/contradicts.d.ts +1 -1
- package/dist/edges/contradicts.js.map +1 -1
- package/dist/edges/{dependsOn.d.ts → depends-on.d.ts} +1 -1
- package/dist/edges/{dependsOn.js → depends-on.js} +4 -4
- package/dist/edges/depends-on.js.map +1 -0
- package/dist/edges/{derivedFrom.d.ts → derived-from.d.ts} +1 -1
- package/dist/edges/{derivedFrom.js → derived-from.js} +3 -3
- package/dist/edges/derived-from.js.map +1 -0
- package/dist/edges/elaborates.d.ts +1 -1
- package/dist/edges/elaborates.js.map +1 -1
- package/dist/edges/index.d.ts +7 -3
- package/dist/edges/index.js +7 -4
- package/dist/edges/index.js.map +1 -1
- package/dist/edges/informs.d.ts +1 -1
- package/dist/edges/informs.js.map +1 -1
- package/dist/edges/{propagationTypes.d.ts → propagation-types.d.ts} +14 -14
- package/dist/edges/{propagationTypes.js → propagation-types.js} +3 -3
- package/dist/edges/propagation-types.js.map +1 -0
- package/dist/edges/refutes.d.ts +1 -1
- package/dist/edges/refutes.js.map +1 -1
- package/dist/edges/supports.d.ts +1 -1
- package/dist/edges/supports.js.map +1 -1
- package/dist/edges/tests.d.ts +1 -1
- package/dist/edges/tests.js.map +1 -1
- package/dist/edges/utils.d.ts +1 -1
- package/dist/edges/utils.js.map +1 -1
- package/dist/embeddingTrigger.d.ts +14 -6
- package/dist/embeddingTrigger.js +11 -14
- package/dist/embeddingTrigger.js.map +1 -1
- package/dist/{entityBridge-DMaKooYn.d.ts → entityBridge-BhVDM3pc.d.ts} +5 -5
- package/dist/entityBridge.d.ts +1 -1
- package/dist/entityBridge.js +602 -225
- package/dist/entityBridge.js.map +1 -1
- package/dist/entityCanonicalMatch.d.ts +14 -12
- package/dist/entityCanonicalMatch.js.map +1 -1
- package/dist/{entityLifecycle-CvgSK5FV.d.ts → entityLifecycle-BsfCz9pS.d.ts} +5 -9
- package/dist/entityLifecycle.d.ts +1 -1
- package/dist/entityLifecycle.js +854 -480
- package/dist/entityLifecycle.js.map +1 -1
- package/dist/{entityValidation-KLZ_Xl2D.d.ts → entityValidation-B1yNEHJx.d.ts} +7 -6
- package/dist/entityValidation.d.ts +3 -1
- package/dist/entityValidation.js +60 -8
- package/dist/entityValidation.js.map +1 -1
- package/dist/{epistemicAnswers-C5ib4z6_.d.ts → epistemicAnswers-f47YMu9U.d.ts} +6 -6
- package/dist/epistemicAnswers.d.ts +1 -1
- package/dist/epistemicAnswers.js +587 -545
- package/dist/epistemicAnswers.js.map +1 -1
- package/dist/epistemicBeliefs.admin.d.ts +8 -8
- package/dist/epistemicBeliefs.admin.js +365 -166
- package/dist/epistemicBeliefs.admin.js.map +1 -1
- package/dist/epistemicBeliefs.backfills.d.ts +8 -8
- package/dist/epistemicBeliefs.backfills.js +655 -289
- package/dist/epistemicBeliefs.backfills.js.map +1 -1
- package/dist/epistemicBeliefs.confidence.d.ts +19 -15
- package/dist/epistemicBeliefs.confidence.js +633 -386
- package/dist/epistemicBeliefs.confidence.js.map +1 -1
- package/dist/epistemicBeliefs.core.d.ts +6 -6
- package/dist/epistemicBeliefs.core.js +717 -371
- package/dist/epistemicBeliefs.core.js.map +1 -1
- package/dist/epistemicBeliefs.d.ts +11 -9
- package/dist/epistemicBeliefs.forkEvidence.d.ts +2 -0
- package/dist/epistemicBeliefs.forkEvidence.js +8 -8
- package/dist/epistemicBeliefs.forkEvidence.js.map +1 -1
- package/dist/epistemicBeliefs.helpers.d.ts +68 -49
- package/dist/epistemicBeliefs.helpers.js +358 -211
- package/dist/epistemicBeliefs.helpers.js.map +1 -1
- package/dist/epistemicBeliefs.internal.d.ts +5 -5
- package/dist/epistemicBeliefs.internal.js +1248 -1026
- package/dist/epistemicBeliefs.internal.js.map +1 -1
- package/dist/epistemicBeliefs.js +4942 -3590
- package/dist/epistemicBeliefs.js.map +1 -1
- package/dist/epistemicBeliefs.lifecycle.d.ts +5 -5
- package/dist/epistemicBeliefs.lifecycle.js +1138 -781
- package/dist/epistemicBeliefs.lifecycle.js.map +1 -1
- package/dist/epistemicBeliefs.links.d.ts +7 -7
- package/dist/epistemicBeliefs.links.js +404 -267
- package/dist/epistemicBeliefs.links.js.map +1 -1
- package/dist/epistemicBeliefs.queries.d.ts +4 -4
- package/dist/epistemicBeliefs.queries.js +175 -20
- package/dist/epistemicBeliefs.queries.js.map +1 -1
- package/dist/epistemicBeliefs.topicAnchor.d.ts +6 -4
- package/dist/epistemicBeliefs.topicAnchor.js +12 -5
- package/dist/epistemicBeliefs.topicAnchor.js.map +1 -1
- package/dist/epistemicContracts.d.ts +28 -3
- package/dist/epistemicContracts.evaluators.d.ts +2 -0
- package/dist/epistemicContracts.evaluators.js +1062 -576
- package/dist/epistemicContracts.evaluators.js.map +1 -1
- package/dist/epistemicContracts.handlers.d.ts +15 -32
- package/dist/epistemicContracts.handlers.js +1829 -1351
- package/dist/epistemicContracts.handlers.js.map +1 -1
- package/dist/epistemicContracts.js +1131 -636
- package/dist/epistemicContracts.js.map +1 -1
- package/dist/epistemicContracts.metrics.d.ts +2 -0
- package/dist/epistemicContracts.metrics.js +375 -158
- package/dist/epistemicContracts.metrics.js.map +1 -1
- package/dist/epistemicContracts.types.d.ts +87 -81
- package/dist/epistemicEdgeCreation.d.ts +2 -0
- package/dist/epistemicEdgeCreation.js +87 -16
- package/dist/epistemicEdgeCreation.js.map +1 -1
- package/dist/{epistemicEdges-BF-cn4i3.d.ts → epistemicEdges-BGBh0QSP.d.ts} +4 -7
- package/dist/epistemicEdges.d.ts +6 -5
- package/dist/epistemicEdges.handlers.d.ts +3 -3
- package/dist/epistemicEdges.handlers.js +129 -24
- package/dist/epistemicEdges.handlers.js.map +1 -1
- package/dist/epistemicEdges.helpers.d.ts +6 -4
- package/dist/epistemicEdges.helpers.js +37 -2
- package/dist/epistemicEdges.helpers.js.map +1 -1
- package/dist/epistemicEdges.js +1966 -1202
- package/dist/epistemicEdges.js.map +1 -1
- package/dist/epistemicEdges.mutations.d.ts +7 -7
- package/dist/epistemicEdges.mutations.js +956 -579
- package/dist/epistemicEdges.mutations.js.map +1 -1
- package/dist/epistemicEdges.queries.d.ts +16 -16
- package/dist/epistemicEdges.queries.js +639 -367
- package/dist/epistemicEdges.queries.js.map +1 -1
- package/dist/epistemicEdges.types.d.ts +10 -8
- package/dist/epistemicEvidence.d.ts +4 -1
- package/dist/epistemicEvidence.js +933 -532
- package/dist/epistemicEvidence.js.map +1 -1
- package/dist/epistemicEvidenceHelpers.d.ts +26 -10
- package/dist/epistemicEvidenceHelpers.js +239 -200
- package/dist/epistemicEvidenceHelpers.js.map +1 -1
- package/dist/epistemicEvidenceMutations.d.ts +8 -8
- package/dist/epistemicEvidenceMutations.js +840 -692
- package/dist/epistemicEvidenceMutations.js.map +1 -1
- package/dist/epistemicEvidenceQueries.d.ts +8 -8
- package/dist/epistemicEvidenceQueries.js +514 -238
- package/dist/epistemicEvidenceQueries.js.map +1 -1
- package/dist/epistemicHelpers.d.ts +4 -2
- package/dist/epistemicHelpers.js +308 -134
- package/dist/epistemicHelpers.js.map +1 -1
- package/dist/epistemicInsert.d.ts +16 -4
- package/dist/epistemicInsert.js +6 -3
- package/dist/epistemicInsert.js.map +1 -1
- package/dist/epistemicLayerRules.d.ts +10 -8
- package/dist/epistemicLayerRules.js +1 -5
- package/dist/epistemicLayerRules.js.map +1 -1
- package/dist/{epistemicLinking-CfE00tHJ.d.ts → epistemicLinking-CsCDv2cN.d.ts} +3 -3
- package/dist/epistemicLinking.d.ts +1 -1
- package/dist/epistemicLinking.js +177 -100
- package/dist/epistemicLinking.js.map +1 -1
- package/dist/epistemicNodeCreation.d.ts +2 -0
- package/dist/epistemicNodeCreation.js +203 -40
- package/dist/epistemicNodeCreation.js.map +1 -1
- package/dist/{epistemicNodes-BCQxpYx_.d.ts → epistemicNodes-CokAgBHg.d.ts} +3 -3
- package/dist/epistemicNodes.d.ts +3 -3
- package/dist/epistemicNodes.helpers.d.ts +24 -15
- package/dist/epistemicNodes.helpers.js.map +1 -1
- package/dist/epistemicNodes.internal.d.ts +6 -6
- package/dist/epistemicNodes.internal.js +389 -319
- package/dist/epistemicNodes.internal.js.map +1 -1
- package/dist/epistemicNodes.js +700 -504
- package/dist/epistemicNodes.js.map +1 -1
- package/dist/epistemicNodes.mutations.d.ts +6 -6
- package/dist/epistemicNodes.mutations.js +560 -463
- package/dist/epistemicNodes.mutations.js.map +1 -1
- package/dist/epistemicNodes.queries.d.ts +8 -8
- package/dist/epistemicNodes.queries.js +311 -314
- package/dist/epistemicNodes.queries.js.map +1 -1
- package/dist/epistemicNodes.validators.d.ts +2 -2
- package/dist/epistemicNodes.validators.js.map +1 -1
- package/dist/epistemicQuestions.conviction.d.ts +8 -8
- package/dist/epistemicQuestions.conviction.js +665 -484
- package/dist/epistemicQuestions.conviction.js.map +1 -1
- package/dist/epistemicQuestions.create.d.ts +4 -4
- package/dist/epistemicQuestions.create.js +640 -612
- package/dist/epistemicQuestions.create.js.map +1 -1
- package/dist/epistemicQuestions.d.ts +8 -5
- package/dist/epistemicQuestions.evidence.d.ts +2 -2
- package/dist/epistemicQuestions.evidence.js +475 -383
- package/dist/epistemicQuestions.evidence.js.map +1 -1
- package/dist/epistemicQuestions.helpers.d.ts +125 -24
- package/dist/epistemicQuestions.helpers.js +240 -209
- package/dist/epistemicQuestions.helpers.js.map +1 -1
- package/dist/epistemicQuestions.js +3474 -2823
- package/dist/epistemicQuestions.js.map +1 -1
- package/dist/epistemicQuestions.lifecycle.d.ts +2 -2
- package/dist/epistemicQuestions.lifecycle.js +607 -546
- package/dist/epistemicQuestions.lifecycle.js.map +1 -1
- package/dist/epistemicQuestions.queries.d.ts +12 -7
- package/dist/epistemicQuestions.queries.js +305 -244
- package/dist/epistemicQuestions.queries.js.map +1 -1
- package/dist/epistemicQuestions.sprint.d.ts +2 -2
- package/dist/epistemicQuestions.sprint.js +600 -394
- package/dist/epistemicQuestions.sprint.js.map +1 -1
- package/dist/epistemicQuestions.tail.d.ts +6 -6
- package/dist/epistemicQuestions.tail.js +572 -433
- package/dist/epistemicQuestions.tail.js.map +1 -1
- package/dist/{epistemicSources-dlKj58Jp.d.ts → epistemicSources-DQtaEkWs.d.ts} +4 -4
- package/dist/epistemicSources.d.ts +1 -1
- package/dist/epistemicSources.js +351 -311
- package/dist/epistemicSources.js.map +1 -1
- package/dist/evaluators/index.d.ts +8 -6
- package/dist/evaluators/index.js +399 -167
- package/dist/evaluators/index.js.map +1 -1
- package/dist/evaluators/lint-checker-evaluator.d.ts +16 -0
- package/dist/evaluators/{lintCheckerEvaluator.js → lint-checker-evaluator.js} +10 -5
- package/dist/evaluators/lint-checker-evaluator.js.map +1 -0
- package/dist/evaluators/{sentryCheckerEvaluator.d.ts → sentry-checker-evaluator.d.ts} +7 -2
- package/dist/evaluators/{sentryCheckerEvaluator.js → sentry-checker-evaluator.js} +3 -3
- package/dist/evaluators/sentry-checker-evaluator.js.map +1 -0
- package/dist/evaluators/shared.d.ts +2 -2
- package/dist/evaluators/shared.js +3 -1
- package/dist/evaluators/shared.js.map +1 -1
- package/dist/evaluators/{testRunnerEvaluator.d.ts → test-runner-evaluator.d.ts} +6 -1
- package/dist/evaluators/{testRunnerEvaluator.js → test-runner-evaluator.js} +6 -4
- package/dist/evaluators/test-runner-evaluator.js.map +1 -0
- package/dist/evaluators/tsc-checker-evaluator.d.ts +16 -0
- package/dist/evaluators/{tscCheckerEvaluator.js → tsc-checker-evaluator.js} +10 -5
- package/dist/evaluators/tsc-checker-evaluator.js.map +1 -0
- package/dist/graphTypes.js +6 -2
- package/dist/graphTypes.js.map +1 -1
- package/dist/helpers.d.ts +2 -0
- package/dist/helpers.js +313 -93
- package/dist/helpers.js.map +1 -1
- package/dist/{index-C-Kyd7hD.d.ts → index-DZxyC9Pb.d.ts} +7 -6
- package/dist/index.d.ts +86 -83
- package/dist/index.js +16914 -11760
- package/dist/index.js.map +1 -1
- package/dist/invariantEnforcement.d.ts +3 -3
- package/dist/invariantEnforcement.js.map +1 -1
- package/dist/logicalRoleInference.d.ts +2 -0
- package/dist/logicalRoleInference.js +1 -1
- package/dist/logicalRoleInference.js.map +1 -1
- package/dist/matcherFeedbackUtils.d.ts +2 -2
- package/dist/matcherFeedbackUtils.js.map +1 -1
- package/dist/{ontology-matching-C6rrz2VP.d.ts → ontology-matching-C-mYFrir.d.ts} +16 -16
- package/dist/ontology-matching.d.ts +1 -1
- package/dist/{ontologyApproval-CFYmqKmk.d.ts → ontologyApproval-BVt0feJi.d.ts} +10 -10
- package/dist/ontologyApproval.d.ts +1 -1
- package/dist/ontologyApproval.js +7 -1
- package/dist/ontologyApproval.js.map +1 -1
- package/dist/ontologyDefinitions.d.ts +14 -24
- package/dist/ontologyDefinitions.js +269 -34
- package/dist/ontologyDefinitions.js.map +1 -1
- package/dist/ontologyHelpers.d.ts +13 -13
- package/dist/ontologyHelpers.js.map +1 -1
- package/dist/{ontologyRegistry-B67rPJ16.d.ts → ontologyRegistry-CljS-ENv.d.ts} +2 -2
- package/dist/ontologyRegistry.d.ts +1 -1
- package/dist/ontologyRegistry.js +34 -6
- package/dist/ontologyRegistry.js.map +1 -1
- package/dist/{projectionReconciliation-jww2fBI0.d.ts → projectionReconciliation-DnrSgHSQ.d.ts} +4 -4
- package/dist/projectionReconciliation.d.ts +1 -1
- package/dist/projectionReconciliation.js +57 -10
- package/dist/projectionReconciliation.js.map +1 -1
- package/dist/{projectionStaleness-CmdbpjVK.d.ts → projectionStaleness-C8ImQ2zP.d.ts} +17 -17
- package/dist/projectionStaleness.d.ts +1 -1
- package/dist/projectionStaleness.js +8 -2
- package/dist/projectionStaleness.js.map +1 -1
- package/dist/proof-attestation.json +1 -1
- package/dist/{questionEvidenceLinks-DFlyPpAj.d.ts → questionEvidenceLinks-_nPRa-LY.d.ts} +10 -10
- package/dist/questionEvidenceLinks.d.ts +1 -1
- package/dist/questionEvidenceLinks.js +564 -347
- package/dist/questionEvidenceLinks.js.map +1 -1
- package/dist/{resolverTypes-CC8Ea2E2.d.ts → resolverTypes-BOXPxLET.d.ts} +8 -7
- package/dist/resolverTypes.d.ts +4 -2
- package/dist/{resolvers-Br1a6eLV.d.ts → resolvers-B1TIBmRO.d.ts} +3 -1
- package/dist/resolvers.d.ts +5 -3
- package/dist/resolvers.js +121 -77
- package/dist/resolvers.js.map +1 -1
- package/dist/scopeResolverCompat.d.ts +10 -7
- package/dist/scopeResolverCompat.js +106 -123
- package/dist/scopeResolverCompat.js.map +1 -1
- package/dist/{text-matching-DNg4M5Wd.d.ts → text-matching-DzFooju6.d.ts} +7 -7
- package/dist/text-matching.d.ts +1 -1
- package/dist/topicOntologyResolver.d.ts +22 -21
- package/dist/topicOntologyResolver.js +54 -32
- package/dist/topicOntologyResolver.js.map +1 -1
- package/dist/topicProjectOverlay.d.ts +30 -20
- package/dist/topicProjectOverlay.js +120 -76
- package/dist/topicProjectOverlay.js.map +1 -1
- package/dist/{topicScope-7zhyeGl7.d.ts → topicScope-DJVa0mLa.d.ts} +22 -7
- package/dist/topicScope.d.ts +3 -1
- package/dist/topicScope.js +104 -119
- package/dist/topicScope.js.map +1 -1
- package/dist/workflowBridge.d.ts +26 -15
- package/dist/workflowBridge.js +140 -144
- package/dist/workflowBridge.js.map +1 -1
- package/dist/workspaceIsolation.d.ts +14 -12
- package/dist/workspaceIsolation.js +108 -122
- package/dist/workspaceIsolation.js.map +1 -1
- package/package.json +4 -4
- package/dist/edges/dependsOn.js.map +0 -1
- package/dist/edges/derivedFrom.js.map +0 -1
- package/dist/edges/propagationTypes.js.map +0 -1
- package/dist/evaluators/lintCheckerEvaluator.d.ts +0 -11
- package/dist/evaluators/lintCheckerEvaluator.js.map +0 -1
- package/dist/evaluators/sentryCheckerEvaluator.js.map +0 -1
- package/dist/evaluators/testRunnerEvaluator.js.map +0 -1
- package/dist/evaluators/tscCheckerEvaluator.d.ts +0 -11
- package/dist/evaluators/tscCheckerEvaluator.js.map +0 -1
- package/dist/{epistemicQuestions-bwHd2FWE.d.ts → epistemicQuestions-Do1fhYm5.d.ts} +4 -4
package/dist/epistemicEdges.js
CHANGED
|
@@ -1,29 +1,52 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { checkScopeAccess, requireScopeWriteAccess } from '@lucern/access-control/access';
|
|
1
|
+
import { REASONING_METHODS, assertEdgePolicyAllowed, edgePolicyManifest } from '@lucern/contracts';
|
|
3
2
|
import { permissiveReturn } from '@lucern/contracts/schema-helpers/validators';
|
|
4
|
-
import {
|
|
3
|
+
import { v } from 'convex/values';
|
|
4
|
+
import { unsafeConvexAnyApi } from '@lucern/contracts/convex/unsafeAnyApi';
|
|
5
|
+
import { componentsGeneric, internalMutationGeneric, mutationGeneric, queryGeneric } from 'convex/server';
|
|
5
6
|
import { assertUuidV7Identity, assertStorageEdgeVocabulary, assertUuidShapedEdgeEndpoint } from '@lucern/contracts/ids';
|
|
6
|
-
import {
|
|
7
|
+
import { requireScopeWriteAccess, checkScopeAccess } from '@lucern/access-control/access';
|
|
7
8
|
|
|
8
|
-
// src/epistemicEdges.
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
return env?.LUCERN_COMPAT_FALLBACK_DEBUG === "1" || env?.LUCERN_GRAPH_DEBUG === "1";
|
|
14
|
-
}
|
|
15
|
-
function debugGraphPrimitiveFallback(message, context) {
|
|
16
|
-
if (!isGraphPrimitiveDebugEnabled()) {
|
|
17
|
-
return;
|
|
18
|
-
}
|
|
19
|
-
console.debug(message, context ?? {});
|
|
20
|
-
}
|
|
21
|
-
var api = anyApi;
|
|
9
|
+
// src/epistemicEdges.handlers.ts
|
|
10
|
+
var unsafeApi = unsafeConvexAnyApi(
|
|
11
|
+
"graph-primitives top-level module bundle lacks a committed Convex _generated/api surface"
|
|
12
|
+
);
|
|
13
|
+
var api = unsafeApi;
|
|
22
14
|
componentsGeneric();
|
|
23
|
-
var internal =
|
|
15
|
+
var internal = unsafeApi;
|
|
24
16
|
var internalMutation = internalMutationGeneric;
|
|
25
17
|
var mutation = mutationGeneric;
|
|
26
18
|
var query = queryGeneric;
|
|
19
|
+
function readOptionalString(value) {
|
|
20
|
+
return typeof value === "string" && value.trim().length > 0 ? value : void 0;
|
|
21
|
+
}
|
|
22
|
+
function readConvexId(value) {
|
|
23
|
+
const normalized = readOptionalString(value);
|
|
24
|
+
return normalized;
|
|
25
|
+
}
|
|
26
|
+
function readRecord(value) {
|
|
27
|
+
return value && typeof value === "object" && !Array.isArray(value) ? value : null;
|
|
28
|
+
}
|
|
29
|
+
function readWorkspaceScopedEdge(value) {
|
|
30
|
+
const record = readRecord(value);
|
|
31
|
+
if (!record) {
|
|
32
|
+
return null;
|
|
33
|
+
}
|
|
34
|
+
const id = readConvexId(record._id);
|
|
35
|
+
const edgeType = readOptionalString(record.edgeType);
|
|
36
|
+
if (!(id && edgeType)) {
|
|
37
|
+
return null;
|
|
38
|
+
}
|
|
39
|
+
const edge = { _id: id, edgeType };
|
|
40
|
+
const projectId = readOptionalString(record.projectId);
|
|
41
|
+
if (projectId !== void 0) {
|
|
42
|
+
edge.projectId = projectId;
|
|
43
|
+
}
|
|
44
|
+
const topicId = readOptionalString(record.topicId);
|
|
45
|
+
if (topicId !== void 0) {
|
|
46
|
+
edge.topicId = topicId;
|
|
47
|
+
}
|
|
48
|
+
return edge;
|
|
49
|
+
}
|
|
27
50
|
var epistemicLayerValidator = v.union(
|
|
28
51
|
v.literal("L4"),
|
|
29
52
|
v.literal("L3"),
|
|
@@ -146,7 +169,7 @@ function buildEdgeMirrorWriteResult(edgeId, existed) {
|
|
|
146
169
|
};
|
|
147
170
|
}
|
|
148
171
|
function edgeMatchesWorkspaceReasoningScope(edge, scope) {
|
|
149
|
-
return scope.topicId !== void 0 && edge.topicId === scope.topicId || scope.projectId !== void 0 && edge.projectId === scope.projectId;
|
|
172
|
+
return scope.topicId !== void 0 && edge.topicId === String(scope.topicId) || scope.projectId !== void 0 && (edge.projectId === scope.projectId || edge.topicId === scope.projectId);
|
|
150
173
|
}
|
|
151
174
|
async function collectScopedEdges(ctx, scope, scanLimit) {
|
|
152
175
|
const queries = [];
|
|
@@ -169,7 +192,11 @@ async function collectScopedEdges(ctx, scope, scanLimit) {
|
|
|
169
192
|
const seen = /* @__PURE__ */ new Set();
|
|
170
193
|
const deduped = [];
|
|
171
194
|
const flattened = (await Promise.all(queries)).flat();
|
|
172
|
-
for (const
|
|
195
|
+
for (const value of flattened) {
|
|
196
|
+
const edge = readWorkspaceScopedEdge(value);
|
|
197
|
+
if (!edge) {
|
|
198
|
+
continue;
|
|
199
|
+
}
|
|
173
200
|
const key = String(edge._id);
|
|
174
201
|
if (seen.has(key)) {
|
|
175
202
|
continue;
|
|
@@ -179,457 +206,115 @@ async function collectScopedEdges(ctx, scope, scanLimit) {
|
|
|
179
206
|
}
|
|
180
207
|
return deduped;
|
|
181
208
|
}
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
derived_from: "DERIVED_FROM",
|
|
193
|
-
// Any→Any: provenance chain (fork, synthesis, extraction, answer)
|
|
194
|
-
contains: "CONTAINS",
|
|
195
|
-
// Any→Any: hierarchy, scoping, membership
|
|
196
|
-
tests: "TESTS",
|
|
197
|
-
// Q→L3: question interrogates belief
|
|
198
|
-
// === L4 DECISION EDGES (derived_from with derivationType=decision) ===
|
|
199
|
-
// Kept as separate Neo4j relationship types for backward compat with L4 queries.
|
|
200
|
-
// New code should use derived_from + metadata.
|
|
201
|
-
based_on_belief: "BASED_ON_BELIEF",
|
|
202
|
-
based_on_question: "BASED_ON_QUESTION",
|
|
203
|
-
blocked_by_contradiction: "BLOCKED_BY_CONTRADICTION",
|
|
204
|
-
informed_by_theme: "INFORMED_BY_THEME",
|
|
205
|
-
// === ONTOLOGICAL EDGES (tenant-extensible, managed by ontology system) ===
|
|
206
|
-
works_at: "WORKS_AT",
|
|
207
|
-
invested_in: "INVESTED_IN",
|
|
208
|
-
competes_with: "COMPETES_WITH",
|
|
209
|
-
participates_in: "PARTICIPATES_IN",
|
|
210
|
-
founded_by: "FOUNDED_BY",
|
|
211
|
-
evaluates: "EVALUATES",
|
|
212
|
-
performs: "PERFORMS",
|
|
213
|
-
function_in: "FUNCTION_IN",
|
|
214
|
-
impacts: "IMPACTS",
|
|
215
|
-
raised_from: "RAISED_FROM",
|
|
216
|
-
mentioned_in: "MENTIONED_IN",
|
|
217
|
-
perspective_on: "PERSPECTIVE_ON",
|
|
218
|
-
about_entity: "ABOUT_ENTITY",
|
|
219
|
-
entity_referenced_in: "ENTITY_REFERENCED_IN"
|
|
220
|
-
};
|
|
221
|
-
function getNodeLayer(nodeType) {
|
|
222
|
-
const L4_TYPES = ["decision"];
|
|
223
|
-
const L3_TYPES = ["belief", "question", "theme", "deal"];
|
|
224
|
-
const L2_TYPES = ["claim", "evidence", "synthesis", "answer"];
|
|
225
|
-
const L1_TYPES = ["atomic_fact", "excerpt", "source"];
|
|
226
|
-
const ONTOLOGICAL_TYPES = [
|
|
227
|
-
"company",
|
|
228
|
-
"person",
|
|
229
|
-
"investor",
|
|
230
|
-
"function",
|
|
231
|
-
"value_chain"
|
|
232
|
-
];
|
|
233
|
-
const ORGANIZATIONAL_TYPES = ["topic"];
|
|
234
|
-
if (L4_TYPES.includes(nodeType)) {
|
|
235
|
-
return "L4";
|
|
236
|
-
}
|
|
237
|
-
if (L3_TYPES.includes(nodeType)) {
|
|
238
|
-
return "L3";
|
|
239
|
-
}
|
|
240
|
-
if (L2_TYPES.includes(nodeType)) {
|
|
241
|
-
return "L2";
|
|
209
|
+
async function assertExistingNodeEndpoint(ctx, endpointRole, endpoint) {
|
|
210
|
+
assertUuidShapedEdgeEndpoint(endpointRole, endpoint);
|
|
211
|
+
const node = await ctx.db.query("epistemicNodes").withIndex(
|
|
212
|
+
"by_globalId",
|
|
213
|
+
(q) => q.eq("globalId", endpoint)
|
|
214
|
+
).first();
|
|
215
|
+
if (!node) {
|
|
216
|
+
throw new Error(
|
|
217
|
+
`edge_endpoint_not_canonical: epistemicEdges insert requires ${endpointRole} to be the globalId of an existing epistemicNodes row, received ${endpoint} (no node with that globalId)`
|
|
218
|
+
);
|
|
242
219
|
}
|
|
243
|
-
|
|
244
|
-
|
|
220
|
+
}
|
|
221
|
+
async function insertEpistemicEdge(ctx, doc) {
|
|
222
|
+
assertUuidV7Identity("epistemicEdges", doc.globalId);
|
|
223
|
+
assertStorageEdgeVocabulary(doc.edgeType);
|
|
224
|
+
if (!doc.fromNodeId || typeof doc.fromNodeId !== "string") {
|
|
225
|
+
throw new Error(
|
|
226
|
+
"edge_endpoint_missing: epistemicEdges insert requires a non-empty fromNodeId"
|
|
227
|
+
);
|
|
245
228
|
}
|
|
246
|
-
if (
|
|
247
|
-
|
|
229
|
+
if (!doc.toNodeId || typeof doc.toNodeId !== "string") {
|
|
230
|
+
throw new Error(
|
|
231
|
+
"edge_endpoint_missing: epistemicEdges insert requires a non-empty toNodeId"
|
|
232
|
+
);
|
|
248
233
|
}
|
|
249
|
-
|
|
250
|
-
|
|
234
|
+
await assertExistingNodeEndpoint(ctx, "fromNodeId", doc.fromNodeId);
|
|
235
|
+
await assertExistingNodeEndpoint(ctx, "toNodeId", doc.toNodeId);
|
|
236
|
+
if (doc.fromNodeType && doc.toNodeType && doc.edgeType !== "extracted_from") {
|
|
237
|
+
assertEdgePolicyAllowed(
|
|
238
|
+
edgePolicyManifest,
|
|
239
|
+
doc.edgeType,
|
|
240
|
+
{
|
|
241
|
+
kind: "epistemic_node",
|
|
242
|
+
nodeId: doc.fromNodeId,
|
|
243
|
+
nodeType: doc.fromNodeType
|
|
244
|
+
},
|
|
245
|
+
{
|
|
246
|
+
kind: "epistemic_node",
|
|
247
|
+
nodeId: doc.toNodeId,
|
|
248
|
+
nodeType: doc.toNodeType
|
|
249
|
+
}
|
|
250
|
+
);
|
|
251
251
|
}
|
|
252
|
-
|
|
253
|
-
return "L2";
|
|
254
|
-
}
|
|
255
|
-
var CANONICAL_EPISTEMIC_TYPES = /* @__PURE__ */ new Set([
|
|
256
|
-
"supports",
|
|
257
|
-
"informs",
|
|
258
|
-
"depends_on",
|
|
259
|
-
"derived_from",
|
|
260
|
-
"contains",
|
|
261
|
-
"tests"
|
|
262
|
-
]);
|
|
263
|
-
function isDeprecatedEdgeType(edgeType) {
|
|
264
|
-
if (CANONICAL_EPISTEMIC_TYPES.has(edgeType)) return false;
|
|
265
|
-
if (edgeType in EDGE_TYPE_TO_REL) return false;
|
|
266
|
-
return true;
|
|
252
|
+
return ctx.db.insert("epistemicEdges", doc);
|
|
267
253
|
}
|
|
268
254
|
|
|
269
|
-
// src/
|
|
270
|
-
function
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
return 4;
|
|
274
|
-
case "L3":
|
|
275
|
-
return 3;
|
|
276
|
-
case "L2":
|
|
277
|
-
return 2;
|
|
278
|
-
case "L1":
|
|
279
|
-
return 1;
|
|
280
|
-
case "ontological":
|
|
281
|
-
return 0;
|
|
282
|
-
default:
|
|
283
|
-
return -1;
|
|
284
|
-
}
|
|
255
|
+
// src/debug.ts
|
|
256
|
+
function isGraphPrimitiveDebugEnabled() {
|
|
257
|
+
const env = globalThis.process?.env;
|
|
258
|
+
return env?.LUCERN_COMPAT_FALLBACK_DEBUG === "1" || env?.LUCERN_GRAPH_DEBUG === "1";
|
|
285
259
|
}
|
|
286
|
-
function
|
|
287
|
-
if (
|
|
288
|
-
return
|
|
289
|
-
}
|
|
290
|
-
if (fromLayer === "ontological" && toLayer === "ontological") {
|
|
291
|
-
return true;
|
|
292
|
-
}
|
|
293
|
-
if (fromLayer === "ontological" && (toLayer === "L3" || toLayer === "L2" || toLayer === "L1")) {
|
|
294
|
-
return true;
|
|
295
|
-
}
|
|
296
|
-
if ((fromLayer === "L3" || fromLayer === "L2" || fromLayer === "L1") && toLayer === "ontological") {
|
|
297
|
-
return true;
|
|
298
|
-
}
|
|
299
|
-
if (fromLayer === "L2" && toLayer === "L1") {
|
|
300
|
-
return true;
|
|
301
|
-
}
|
|
302
|
-
if (fromLayer === "L2" && toLayer === "L3") {
|
|
303
|
-
return true;
|
|
304
|
-
}
|
|
305
|
-
if (fromLayer === "L3" && toLayer === "L2") {
|
|
306
|
-
return true;
|
|
307
|
-
}
|
|
308
|
-
if (fromLayer === "L3" && toLayer === "L4") {
|
|
309
|
-
return true;
|
|
260
|
+
function debugGraphPrimitiveFallback(message, context) {
|
|
261
|
+
if (!isGraphPrimitiveDebugEnabled()) {
|
|
262
|
+
return;
|
|
310
263
|
}
|
|
311
|
-
|
|
312
|
-
|
|
264
|
+
console.debug(message, context ?? {});
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
// src/topicScope.ts
|
|
268
|
+
var LEGACY_SCOPE_FIELD = "graphScopeProjectId";
|
|
269
|
+
async function resolveTopicNodeScopeOrNull(ctx, ref) {
|
|
270
|
+
if (!ctx?.db || typeof ctx.db.query !== "function") {
|
|
271
|
+
return null;
|
|
313
272
|
}
|
|
314
|
-
|
|
315
|
-
|
|
273
|
+
let node = null;
|
|
274
|
+
try {
|
|
275
|
+
const byGlobalId = await ctx.db.query("epistemicNodes").withIndex("by_globalId", (q) => q.eq("globalId", ref)).first();
|
|
276
|
+
if (byGlobalId && byGlobalId.nodeType === "topic") {
|
|
277
|
+
node = byGlobalId;
|
|
278
|
+
}
|
|
279
|
+
} catch (error) {
|
|
280
|
+
debugGraphPrimitiveFallback(
|
|
281
|
+
"[topicScope] topic-node scope lookup by globalId failed",
|
|
282
|
+
{ error, ref }
|
|
283
|
+
);
|
|
316
284
|
}
|
|
317
|
-
if (
|
|
318
|
-
return
|
|
285
|
+
if (!node) {
|
|
286
|
+
return null;
|
|
319
287
|
}
|
|
320
|
-
const
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
return false;
|
|
288
|
+
const scopeKey = normalizeScopeValue(node.topicId) ?? normalizeScopeValue(node.globalId);
|
|
289
|
+
if (!scopeKey) {
|
|
290
|
+
return null;
|
|
324
291
|
}
|
|
325
|
-
return
|
|
292
|
+
return {
|
|
293
|
+
topicId: scopeKey,
|
|
294
|
+
projectId: asMappedProjectId(node),
|
|
295
|
+
source: "topic_node"
|
|
296
|
+
};
|
|
326
297
|
}
|
|
327
|
-
function
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
if (options.minLayer !== void 0 && targetDepth < options.minLayer) {
|
|
331
|
-
return false;
|
|
298
|
+
function asMappedProjectId(topic) {
|
|
299
|
+
if (!topic) {
|
|
300
|
+
return;
|
|
332
301
|
}
|
|
333
|
-
|
|
334
|
-
|
|
302
|
+
const directLegacyProjectId = normalizeScopeValue(
|
|
303
|
+
topic[LEGACY_SCOPE_FIELD]
|
|
304
|
+
);
|
|
305
|
+
if (directLegacyProjectId) {
|
|
306
|
+
return directLegacyProjectId;
|
|
335
307
|
}
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
case "decision_trace":
|
|
344
|
-
return currentDepth === 4 ? targetDepth === 3 : targetDepth === currentDepth;
|
|
345
|
-
default:
|
|
346
|
-
return true;
|
|
308
|
+
const metadata = topic.metadata || {};
|
|
309
|
+
const candidate = metadata[LEGACY_SCOPE_FIELD] || metadata.legacyProjectId || metadata.projectId || metadata.scopeProjectId;
|
|
310
|
+
return typeof candidate === "string" ? normalizeScopeValue(candidate) : void 0;
|
|
311
|
+
}
|
|
312
|
+
function normalizeScopeValue(value) {
|
|
313
|
+
if (typeof value !== "string") {
|
|
314
|
+
return;
|
|
347
315
|
}
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
switch (mode) {
|
|
351
|
-
case "anchor_down":
|
|
352
|
-
return 1;
|
|
353
|
-
case "anchor_up":
|
|
354
|
-
return 2;
|
|
355
|
-
case "same_layer":
|
|
356
|
-
return 1;
|
|
357
|
-
case "decision_trace":
|
|
358
|
-
return 3;
|
|
359
|
-
default:
|
|
360
|
-
return 1;
|
|
361
|
-
}
|
|
362
|
-
}
|
|
363
|
-
var EDGE_LAYER_RULES = {
|
|
364
|
-
// === 6 Canonical Epistemic Types ===
|
|
365
|
-
supports: {
|
|
366
|
-
from: ["L3"],
|
|
367
|
-
to: ["L3"],
|
|
368
|
-
description: "Belief bears on Belief (weight: +1 supports, -1 contradicts)"
|
|
369
|
-
},
|
|
370
|
-
informs: {
|
|
371
|
-
from: ["L2"],
|
|
372
|
-
to: ["L3"],
|
|
373
|
-
description: "Evidence -> Belief (L2 -> L3)"
|
|
374
|
-
},
|
|
375
|
-
depends_on: {
|
|
376
|
-
from: ["L3"],
|
|
377
|
-
to: ["L3"],
|
|
378
|
-
description: "Belief B requires Belief A (structural gate)"
|
|
379
|
-
},
|
|
380
|
-
derived_from: {
|
|
381
|
-
from: ["L2", "L3", "L4"],
|
|
382
|
-
to: ["L1", "L2", "L3"],
|
|
383
|
-
description: "A was produced from B (provenance chain)"
|
|
384
|
-
},
|
|
385
|
-
contains: {
|
|
386
|
-
from: ["L3", "L4", "ontological"],
|
|
387
|
-
to: ["L2", "L3", "ontological"],
|
|
388
|
-
description: "A scopes/aggregates B (hierarchy)"
|
|
389
|
-
},
|
|
390
|
-
tests: {
|
|
391
|
-
from: ["L3"],
|
|
392
|
-
to: ["L3"],
|
|
393
|
-
description: "Question -> Belief (L3 -> L3)"
|
|
394
|
-
},
|
|
395
|
-
// === Structural / Lifecycle ===
|
|
396
|
-
supersedes: {
|
|
397
|
-
from: ["L3"],
|
|
398
|
-
to: ["L3"],
|
|
399
|
-
description: "NewNode -> OldNode (fork lineage)"
|
|
400
|
-
},
|
|
401
|
-
responds_to: {
|
|
402
|
-
from: ["L2", "L3"],
|
|
403
|
-
to: ["L3"],
|
|
404
|
-
description: "Answer -> Question (L2/L3 -> L3)"
|
|
405
|
-
},
|
|
406
|
-
belongs_to: {
|
|
407
|
-
from: ["L2", "L3", "ontological"],
|
|
408
|
-
to: ["L3"],
|
|
409
|
-
description: "Membership (migrating to contains)"
|
|
410
|
-
},
|
|
411
|
-
relates_to_thesis: {
|
|
412
|
-
from: ["L3"],
|
|
413
|
-
to: ["L3"],
|
|
414
|
-
description: "Belief -> Theme (L3 -> L3)"
|
|
415
|
-
},
|
|
416
|
-
// === Ontological (entity-entity or entity-epistemic bridge) ===
|
|
417
|
-
works_at: {
|
|
418
|
-
from: ["ontological"],
|
|
419
|
-
to: ["ontological"],
|
|
420
|
-
description: "Person -> Company"
|
|
421
|
-
},
|
|
422
|
-
invested_in: {
|
|
423
|
-
from: ["ontological"],
|
|
424
|
-
to: ["ontological"],
|
|
425
|
-
description: "Investor -> Company"
|
|
426
|
-
},
|
|
427
|
-
competes_with: {
|
|
428
|
-
from: ["ontological"],
|
|
429
|
-
to: ["ontological"],
|
|
430
|
-
description: "Company -> Company"
|
|
431
|
-
},
|
|
432
|
-
participates_in: {
|
|
433
|
-
from: ["ontological"],
|
|
434
|
-
to: ["ontological"],
|
|
435
|
-
description: "Company -> ValueChain"
|
|
436
|
-
},
|
|
437
|
-
founded_by: {
|
|
438
|
-
from: ["ontological"],
|
|
439
|
-
to: ["ontological"],
|
|
440
|
-
description: "Company -> Person"
|
|
441
|
-
},
|
|
442
|
-
evaluates: {
|
|
443
|
-
from: ["ontological"],
|
|
444
|
-
to: ["ontological"],
|
|
445
|
-
description: "Deal -> Company"
|
|
446
|
-
},
|
|
447
|
-
performs: {
|
|
448
|
-
from: ["ontological"],
|
|
449
|
-
to: ["ontological"],
|
|
450
|
-
description: "Company -> Function"
|
|
451
|
-
},
|
|
452
|
-
function_in: {
|
|
453
|
-
from: ["ontological"],
|
|
454
|
-
to: ["ontological"],
|
|
455
|
-
description: "Function -> ValueChain"
|
|
456
|
-
},
|
|
457
|
-
impacts: {
|
|
458
|
-
from: ["ontological", "L3"],
|
|
459
|
-
to: ["ontological"],
|
|
460
|
-
description: "Theme/Entity -> ValueChain"
|
|
461
|
-
},
|
|
462
|
-
raised_from: {
|
|
463
|
-
from: ["ontological"],
|
|
464
|
-
to: ["ontological"],
|
|
465
|
-
description: "Company -> Investor"
|
|
466
|
-
},
|
|
467
|
-
mentioned_in: {
|
|
468
|
-
from: ["ontological"],
|
|
469
|
-
to: ["L1", "L2"],
|
|
470
|
-
description: "Entity -> Source/Evidence"
|
|
471
|
-
},
|
|
472
|
-
perspective_on: {
|
|
473
|
-
from: ["ontological"],
|
|
474
|
-
to: ["L3"],
|
|
475
|
-
description: "Person -> Belief/Theme"
|
|
476
|
-
},
|
|
477
|
-
plays_theme: {
|
|
478
|
-
from: ["ontological"],
|
|
479
|
-
to: ["L3"],
|
|
480
|
-
description: "Deal -> Theme"
|
|
481
|
-
},
|
|
482
|
-
// C2-RR.4 — storage migration alias + the remaining 55 reachable edge types.
|
|
483
|
-
// Mirrors the kernel rule table
|
|
484
|
-
// (packages/reasoning-kernel/src/adapters/lib/edgeValidation.ts) so this
|
|
485
|
-
// graph-primitives write path admits the same public edge vocabulary. See the
|
|
486
|
-
// kernel file for the per-group rationale.
|
|
487
|
-
extracted_from: {
|
|
488
|
-
from: ["L2", "L3", "L4"],
|
|
489
|
-
to: ["L1", "L2", "L3"],
|
|
490
|
-
description: "Legacy storage migration alias for derived_from; public writes use derived_from"
|
|
491
|
-
},
|
|
492
|
-
qualifies: { from: ["L2", "L3"], to: ["L3"], description: "Evidence/Belief qualifies a Belief" },
|
|
493
|
-
contradicts: { from: ["L2", "L3"], to: ["L3"], description: "Evidence/Belief contradicts a Belief" },
|
|
494
|
-
reinforces: { from: ["L2", "L3"], to: ["L3"], description: "Evidence/Belief reinforces a Belief" },
|
|
495
|
-
corroborates: { from: ["L2", "L3"], to: ["L3"], description: "Independent evidence/belief corroborates a Belief" },
|
|
496
|
-
strengthened_by: { from: ["L3"], to: ["L2", "L3"], description: "Belief is strengthened by Evidence/Belief" },
|
|
497
|
-
weakened_by: { from: ["L3"], to: ["L2", "L3"], description: "Belief is weakened by Evidence/Belief" },
|
|
498
|
-
validated_by: { from: ["L3"], to: ["L2", "L3"], description: "Belief is validated by Evidence/Belief" },
|
|
499
|
-
falsified_by: { from: ["L3"], to: ["L2", "L3"], description: "Belief is falsified by Evidence/Belief" },
|
|
500
|
-
amplifies: { from: ["L3"], to: ["L3"], description: "Belief amplifies another Belief" },
|
|
501
|
-
precondition_for: { from: ["L3"], to: ["L3"], description: "Belief is a precondition for another Belief" },
|
|
502
|
-
prerequisite_for: { from: ["L3"], to: ["L3"], description: "Belief is a prerequisite for another Belief" },
|
|
503
|
-
required_for: { from: ["L3"], to: ["L3"], description: "Belief is required for another Belief" },
|
|
504
|
-
in_tension_with: { from: ["L3"], to: ["L3"], description: "Belief is in tension with another Belief" },
|
|
505
|
-
mutually_exclusive: { from: ["L3"], to: ["L3"], description: "Beliefs cannot both hold" },
|
|
506
|
-
exclusive_with: { from: ["L3"], to: ["L3"], description: "Belief is exclusive with another Belief" },
|
|
507
|
-
alternative_to: { from: ["L3"], to: ["L3"], description: "Belief is an alternative to another Belief" },
|
|
508
|
-
subsumes: { from: ["L3"], to: ["L3"], description: "Belief subsumes a narrower Belief" },
|
|
509
|
-
extends: { from: ["L3"], to: ["L3"], description: "Belief extends another Belief" },
|
|
510
|
-
refines: { from: ["L3"], to: ["L3"], description: "Belief refines another Belief" },
|
|
511
|
-
implements: { from: ["L3"], to: ["L3"], description: "Belief implements an abstract Belief" },
|
|
512
|
-
violates: { from: ["L3"], to: ["L3"], description: "Belief violates a constraint Belief" },
|
|
513
|
-
assumes: { from: ["L3"], to: ["L3"], description: "Belief assumes another Belief" },
|
|
514
|
-
would_predict: { from: ["L3"], to: ["L3"], description: "Belief would predict another Belief" },
|
|
515
|
-
analogous_to: { from: ["L3"], to: ["L3"], description: "Belief is analogous to another Belief" },
|
|
516
|
-
independent_of: { from: ["L3"], to: ["L3"], description: "Belief is independent of another Belief" },
|
|
517
|
-
correlates_with: { from: ["L3"], to: ["L3"], description: "Belief correlates with another Belief" },
|
|
518
|
-
co_changes_with: { from: ["L3"], to: ["L3"], description: "Belief co-changes with another Belief" },
|
|
519
|
-
counterfactual_of: { from: ["L3"], to: ["L3"], description: "Belief is a counterfactual of another Belief" },
|
|
520
|
-
parallel_to: { from: ["L3"], to: ["L3"], description: "Belief runs parallel to another Belief" },
|
|
521
|
-
cascade_from: { from: ["L3"], to: ["L3"], description: "Cascade from an upstream Belief" },
|
|
522
|
-
cascade_to: { from: ["L3"], to: ["L3"], description: "Cascade to a downstream Belief" },
|
|
523
|
-
collapses_if: { from: ["L3"], to: ["L3"], description: "Belief collapses if another fails" },
|
|
524
|
-
branches_from: { from: ["L3"], to: ["L3"], description: "Belief branches from an ancestor Belief" },
|
|
525
|
-
same_as: { from: ["L2", "L3", "ontological"], to: ["L2", "L3", "ontological"], description: "Two nodes are the same entity" },
|
|
526
|
-
answers: { from: ["L2", "L3"], to: ["L3"], description: "Answer/Belief answers a Question" },
|
|
527
|
-
partially_answers: { from: ["L2", "L3"], to: ["L3"], description: "Answer/Belief partially answers a Question" },
|
|
528
|
-
explores: { from: ["L3"], to: ["L3"], description: "Question explores a Belief/Theme" },
|
|
529
|
-
informed_by_theme: { from: ["L3"], to: ["L3"], description: "Belief is informed by a Theme" },
|
|
530
|
-
same_theme_as: { from: ["L3"], to: ["L3"], description: "Two Beliefs share a Theme" },
|
|
531
|
-
based_on: { from: ["L4"], to: ["L3"], description: "Decision is based on a Belief/Question" },
|
|
532
|
-
based_on_belief: { from: ["L4"], to: ["L3"], description: "Decision is based on a Belief" },
|
|
533
|
-
based_on_question: { from: ["L4"], to: ["L3"], description: "Decision is based on a Question" },
|
|
534
|
-
blocked_by_contradiction: { from: ["L4"], to: ["L3"], description: "Decision is blocked by a Contradiction" },
|
|
535
|
-
parent_of: { from: ["L3", "L4", "ontological", "organizational"], to: ["L2", "L3", "ontological", "organizational"], description: "A is the parent of B" },
|
|
536
|
-
child_of: { from: ["L2", "L3", "ontological", "organizational"], to: ["L3", "L4", "ontological", "organizational"], description: "A is the child of B" },
|
|
537
|
-
scoped_by: { from: ["L2", "L3"], to: ["L3", "organizational"], description: "Object is scoped by a Topic/Theme" },
|
|
538
|
-
cites: { from: ["L2", "L3"], to: ["L1", "L2"], description: "Evidence/Belief cites a Source/Excerpt" },
|
|
539
|
-
summarizes: { from: ["L2", "L3"], to: ["L1", "L2"], description: "Synthesis/Evidence summarizes a Source/Evidence" },
|
|
540
|
-
same_source_as: { from: ["L1", "L2"], to: ["L1", "L2"], description: "Two nodes derive from the same Source" },
|
|
541
|
-
migrating_from: { from: ["L2", "L3", "L4"], to: ["L1", "L2", "L3"], description: "Migration lineage: from an ancestor" },
|
|
542
|
-
migrating_to: { from: ["L1", "L2", "L3"], to: ["L2", "L3", "L4"], description: "Migration lineage: to a successor" },
|
|
543
|
-
about_entity: { from: ["L2", "L3"], to: ["ontological"], description: "Belief/Evidence is about an Entity" },
|
|
544
|
-
entity_referenced_in: { from: ["ontological"], to: ["L1", "L2"], description: "Entity is referenced in a Source/Evidence" },
|
|
545
|
-
related_to: { from: ["L2", "L3", "ontological", "organizational"], to: ["L2", "L3", "ontological", "organizational"], description: "Lateral adjacency; no confidence pressure" },
|
|
546
|
-
blocks: { from: ["L3", "L4"], to: ["L3", "L4"], description: "A blocks B (structural gate)" }
|
|
547
|
-
};
|
|
548
|
-
function validateEdgeLayers(edgeType, fromLayer, toLayer) {
|
|
549
|
-
const rules = EDGE_LAYER_RULES[edgeType];
|
|
550
|
-
if (!rules) {
|
|
551
|
-
console.warn(
|
|
552
|
-
`[EdgeValidation] Unknown edge type: ${edgeType}, allowing by default`
|
|
553
|
-
);
|
|
554
|
-
return { valid: true };
|
|
555
|
-
}
|
|
556
|
-
if (edgeType === "supersedes") {
|
|
557
|
-
if (fromLayer !== toLayer) {
|
|
558
|
-
return {
|
|
559
|
-
valid: false,
|
|
560
|
-
reason: `${edgeType} edges must be between nodes of the same layer. Got ${fromLayer} -> ${toLayer}`
|
|
561
|
-
};
|
|
562
|
-
}
|
|
563
|
-
return { valid: true };
|
|
564
|
-
}
|
|
565
|
-
if (!rules.from.includes(fromLayer)) {
|
|
566
|
-
return {
|
|
567
|
-
valid: false,
|
|
568
|
-
reason: `Edge type '${edgeType}' does not allow source layer ${fromLayer}. Allowed: ${rules.from.join(", ")}`
|
|
569
|
-
};
|
|
570
|
-
}
|
|
571
|
-
if (!rules.to.includes(toLayer)) {
|
|
572
|
-
return {
|
|
573
|
-
valid: false,
|
|
574
|
-
reason: `Edge type '${edgeType}' does not allow target layer ${toLayer}. Allowed: ${rules.to.join(", ")}`
|
|
575
|
-
};
|
|
576
|
-
}
|
|
577
|
-
if (!isValidLayerConnection(fromLayer, toLayer)) {
|
|
578
|
-
return {
|
|
579
|
-
valid: false,
|
|
580
|
-
reason: `Layer transition ${fromLayer} -> ${toLayer} is not permitted`
|
|
581
|
-
};
|
|
582
|
-
}
|
|
583
|
-
return { valid: true };
|
|
584
|
-
}
|
|
585
|
-
var LEGACY_SCOPE_FIELD = "graphScopeProjectId";
|
|
586
|
-
async function resolveTopicNodeScopeOrNull(ctx, ref) {
|
|
587
|
-
if (!ctx?.db || typeof ctx.db.query !== "function") {
|
|
588
|
-
return null;
|
|
589
|
-
}
|
|
590
|
-
let node = null;
|
|
591
|
-
try {
|
|
592
|
-
const byGlobalId = await ctx.db.query("epistemicNodes").withIndex("by_globalId", (q) => q.eq("globalId", ref)).first();
|
|
593
|
-
if (byGlobalId && byGlobalId.nodeType === "topic") {
|
|
594
|
-
node = byGlobalId;
|
|
595
|
-
}
|
|
596
|
-
} catch (error) {
|
|
597
|
-
debugGraphPrimitiveFallback(
|
|
598
|
-
"[topicScope] topic-node scope lookup by globalId failed",
|
|
599
|
-
{ error, ref }
|
|
600
|
-
);
|
|
601
|
-
}
|
|
602
|
-
if (!node) {
|
|
603
|
-
return null;
|
|
604
|
-
}
|
|
605
|
-
const scopeKey = normalizeScopeValue(node.topicId) ?? normalizeScopeValue(node.globalId);
|
|
606
|
-
if (!scopeKey) {
|
|
607
|
-
return null;
|
|
608
|
-
}
|
|
609
|
-
return {
|
|
610
|
-
topicId: scopeKey,
|
|
611
|
-
projectId: asMappedProjectId(node),
|
|
612
|
-
source: "topic_node"
|
|
613
|
-
};
|
|
614
|
-
}
|
|
615
|
-
function asMappedProjectId(topic) {
|
|
616
|
-
if (!topic) {
|
|
617
|
-
return;
|
|
618
|
-
}
|
|
619
|
-
const directLegacyProjectId = normalizeScopeValue(topic[LEGACY_SCOPE_FIELD]);
|
|
620
|
-
if (directLegacyProjectId) {
|
|
621
|
-
return directLegacyProjectId;
|
|
622
|
-
}
|
|
623
|
-
const metadata = topic.metadata || {};
|
|
624
|
-
const candidate = metadata[LEGACY_SCOPE_FIELD] || metadata.legacyProjectId || metadata.projectId || metadata.scopeProjectId;
|
|
625
|
-
return candidate ? candidate : void 0;
|
|
626
|
-
}
|
|
627
|
-
function normalizeScopeValue(value) {
|
|
628
|
-
if (typeof value !== "string") {
|
|
629
|
-
return;
|
|
630
|
-
}
|
|
631
|
-
const normalized = value.trim();
|
|
632
|
-
return normalized.length > 0 ? normalized : void 0;
|
|
316
|
+
const normalized = value.trim();
|
|
317
|
+
return normalized.length > 0 ? normalized : void 0;
|
|
633
318
|
}
|
|
634
319
|
function pickPrimaryTopic(candidates) {
|
|
635
320
|
return [...candidates].sort((a, b) => {
|
|
@@ -647,8 +332,9 @@ function pickPrimaryTopic(candidates) {
|
|
|
647
332
|
})[0];
|
|
648
333
|
}
|
|
649
334
|
async function findTopicsByScopeAlias(ctx, scopeId) {
|
|
335
|
+
const query2 = ctx.db.query("topics");
|
|
650
336
|
try {
|
|
651
|
-
return await
|
|
337
|
+
return await query2.withIndex(
|
|
652
338
|
"by_graph_scope_project",
|
|
653
339
|
(q) => q.eq(LEGACY_SCOPE_FIELD, scopeId)
|
|
654
340
|
).collect();
|
|
@@ -660,7 +346,7 @@ async function findTopicsByScopeAlias(ctx, scopeId) {
|
|
|
660
346
|
scopeId
|
|
661
347
|
}
|
|
662
348
|
);
|
|
663
|
-
const topics = await
|
|
349
|
+
const topics = await query2.collect();
|
|
664
350
|
return topics.filter((topic) => {
|
|
665
351
|
const normalizedGlobalId = normalizeScopeValue(topic.globalId);
|
|
666
352
|
const mappedProjectId = asMappedProjectId(topic);
|
|
@@ -716,527 +402,997 @@ async function resolveInheritedWorkspaceScope(ctx, topic) {
|
|
|
716
402
|
let current = topic;
|
|
717
403
|
for (let i = 0; i < MAX_DEPTH && current?.parentTopicId; i++) {
|
|
718
404
|
current = await ctx.db.get(current.parentTopicId);
|
|
719
|
-
if (!current)
|
|
405
|
+
if (!current) {
|
|
406
|
+
break;
|
|
407
|
+
}
|
|
720
408
|
if (!tenantId) {
|
|
721
409
|
tenantId = normalizeScopeValue(current.tenantId);
|
|
722
410
|
}
|
|
723
411
|
if (!workspaceId) {
|
|
724
412
|
workspaceId = normalizeScopeValue(current.workspaceId);
|
|
725
413
|
}
|
|
726
|
-
if (tenantId && workspaceId)
|
|
414
|
+
if (tenantId && workspaceId) {
|
|
415
|
+
break;
|
|
416
|
+
}
|
|
727
417
|
}
|
|
728
418
|
return { tenantId, workspaceId };
|
|
729
419
|
}
|
|
730
420
|
async function resolveTopicProjectScope(ctx, args) {
|
|
731
421
|
if (args.topicId) {
|
|
732
|
-
|
|
733
|
-
try {
|
|
734
|
-
topic = await ctx.db.get(
|
|
735
|
-
args.topicId
|
|
736
|
-
);
|
|
737
|
-
} catch (error) {
|
|
738
|
-
debugGraphPrimitiveFallback(
|
|
739
|
-
"[topicScope] Failed to load topic by direct id",
|
|
740
|
-
{
|
|
741
|
-
error,
|
|
742
|
-
topicId: args.topicId
|
|
743
|
-
}
|
|
744
|
-
);
|
|
745
|
-
}
|
|
746
|
-
if (!topic) {
|
|
747
|
-
topic = await tryResolveHostTopicById(ctx, String(args.topicId));
|
|
748
|
-
}
|
|
749
|
-
if (!topic) {
|
|
750
|
-
topic = pickPrimaryTopic(
|
|
751
|
-
await findTopicsByScopeAlias(ctx, String(args.topicId))
|
|
752
|
-
) ?? null;
|
|
753
|
-
}
|
|
754
|
-
if (!topic) {
|
|
755
|
-
const nodeScope = await resolveTopicNodeScopeOrNull(
|
|
756
|
-
ctx,
|
|
757
|
-
String(args.topicId)
|
|
758
|
-
);
|
|
759
|
-
if (nodeScope) {
|
|
760
|
-
return nodeScope;
|
|
761
|
-
}
|
|
762
|
-
throw new Error(`Topic not found: ${String(args.topicId)}`);
|
|
763
|
-
}
|
|
764
|
-
const inherited = await resolveInheritedWorkspaceScope(ctx, topic);
|
|
765
|
-
const mapped = asMappedProjectId(topic);
|
|
766
|
-
if (mapped) {
|
|
767
|
-
return {
|
|
768
|
-
topicId: topic._id,
|
|
769
|
-
projectId: mapped,
|
|
770
|
-
tenantId: inherited.tenantId,
|
|
771
|
-
workspaceId: inherited.workspaceId,
|
|
772
|
-
source: "topic"
|
|
773
|
-
};
|
|
774
|
-
}
|
|
775
|
-
return {
|
|
776
|
-
topicId: topic._id,
|
|
777
|
-
tenantId: inherited.tenantId,
|
|
778
|
-
workspaceId: inherited.workspaceId,
|
|
779
|
-
source: "topic"
|
|
780
|
-
};
|
|
422
|
+
return await resolveScopeFromTopicId(ctx, args.topicId);
|
|
781
423
|
}
|
|
782
424
|
if (args.projectId) {
|
|
783
|
-
|
|
784
|
-
try {
|
|
785
|
-
directTopic = await ctx.db.get(
|
|
786
|
-
args.projectId
|
|
787
|
-
);
|
|
788
|
-
} catch (error) {
|
|
789
|
-
debugGraphPrimitiveFallback(
|
|
790
|
-
"[topicScope] Failed to load direct project topic",
|
|
791
|
-
{
|
|
792
|
-
error,
|
|
793
|
-
projectId: args.projectId
|
|
794
|
-
}
|
|
795
|
-
);
|
|
796
|
-
}
|
|
797
|
-
if (directTopic) {
|
|
798
|
-
const inherited = await resolveInheritedWorkspaceScope(ctx, directTopic);
|
|
799
|
-
const mapped = asMappedProjectId(directTopic);
|
|
800
|
-
return {
|
|
801
|
-
topicId: directTopic._id,
|
|
802
|
-
projectId: mapped ?? args.projectId,
|
|
803
|
-
tenantId: inherited.tenantId,
|
|
804
|
-
workspaceId: inherited.workspaceId,
|
|
805
|
-
source: "topic_inferred"
|
|
806
|
-
};
|
|
807
|
-
}
|
|
808
|
-
directTopic = await tryResolveHostTopicByLegacyScope(ctx, args.projectId);
|
|
809
|
-
if (directTopic) {
|
|
810
|
-
const inherited = await resolveInheritedWorkspaceScope(ctx, directTopic);
|
|
811
|
-
const mapped = asMappedProjectId(directTopic);
|
|
812
|
-
return {
|
|
813
|
-
topicId: directTopic._id,
|
|
814
|
-
projectId: mapped ?? args.projectId,
|
|
815
|
-
tenantId: inherited.tenantId,
|
|
816
|
-
workspaceId: inherited.workspaceId,
|
|
817
|
-
source: "topic_inferred"
|
|
818
|
-
};
|
|
819
|
-
}
|
|
820
|
-
const topics = await findTopicsByScopeAlias(ctx, args.projectId);
|
|
821
|
-
const primary = pickPrimaryTopic(topics);
|
|
822
|
-
if (primary) {
|
|
823
|
-
const inherited = await resolveInheritedWorkspaceScope(ctx, primary);
|
|
824
|
-
return {
|
|
825
|
-
topicId: primary._id,
|
|
826
|
-
projectId: args.projectId,
|
|
827
|
-
tenantId: inherited.tenantId,
|
|
828
|
-
workspaceId: inherited.workspaceId,
|
|
829
|
-
source: "project_mapped_topic"
|
|
830
|
-
};
|
|
831
|
-
}
|
|
832
|
-
const nodeScope = await resolveTopicNodeScopeOrNull(
|
|
833
|
-
ctx,
|
|
834
|
-
String(args.projectId)
|
|
835
|
-
);
|
|
836
|
-
if (nodeScope) {
|
|
837
|
-
return {
|
|
838
|
-
...nodeScope,
|
|
839
|
-
projectId: nodeScope.projectId ?? String(args.projectId)
|
|
840
|
-
};
|
|
841
|
-
}
|
|
842
|
-
throw new Error(
|
|
843
|
-
`Legacy project scope ${String(args.projectId)} has no mapped topic.`
|
|
844
|
-
);
|
|
425
|
+
return await resolveScopeFromLegacyProjectId(ctx, args.projectId);
|
|
845
426
|
}
|
|
846
427
|
throw new Error(
|
|
847
428
|
"Missing scope: provide topicId (preferred) or legacy projectId alias."
|
|
848
429
|
);
|
|
849
430
|
}
|
|
431
|
+
async function resolveScopeFromTopicId(ctx, topicId) {
|
|
432
|
+
const topic = await resolveTopicDocFromTopicId(ctx, topicId);
|
|
433
|
+
if (topic) {
|
|
434
|
+
return await buildTopicScope(ctx, topic, "topic");
|
|
435
|
+
}
|
|
436
|
+
const nodeScope = await resolveTopicNodeScopeOrNull(ctx, String(topicId));
|
|
437
|
+
if (nodeScope) {
|
|
438
|
+
return nodeScope;
|
|
439
|
+
}
|
|
440
|
+
throw new Error(`Topic not found: ${String(topicId)}`);
|
|
441
|
+
}
|
|
442
|
+
async function resolveTopicDocFromTopicId(ctx, topicId) {
|
|
443
|
+
const direct = await tryReadTopicDoc(ctx, topicId, {
|
|
444
|
+
failureLog: "[topicScope] Failed to load topic by direct id",
|
|
445
|
+
idLogKey: "topicId"
|
|
446
|
+
});
|
|
447
|
+
if (direct) {
|
|
448
|
+
return direct;
|
|
449
|
+
}
|
|
450
|
+
const hostTopic = await tryResolveHostTopicById(ctx, String(topicId));
|
|
451
|
+
if (hostTopic) {
|
|
452
|
+
return hostTopic;
|
|
453
|
+
}
|
|
454
|
+
return pickPrimaryTopic(await findTopicsByScopeAlias(ctx, String(topicId))) ?? null;
|
|
455
|
+
}
|
|
456
|
+
async function resolveScopeFromLegacyProjectId(ctx, legacyProjectId) {
|
|
457
|
+
const directTopic = await resolveDirectLegacyProjectTopic(
|
|
458
|
+
ctx,
|
|
459
|
+
legacyProjectId
|
|
460
|
+
);
|
|
461
|
+
if (directTopic) {
|
|
462
|
+
return await buildTopicScope(ctx, directTopic, "topic_inferred", {
|
|
463
|
+
fallbackProjectId: legacyProjectId
|
|
464
|
+
});
|
|
465
|
+
}
|
|
466
|
+
const primary = pickPrimaryTopic(
|
|
467
|
+
await findTopicsByScopeAlias(ctx, legacyProjectId)
|
|
468
|
+
);
|
|
469
|
+
if (primary) {
|
|
470
|
+
return await buildTopicScope(ctx, primary, "project_mapped_topic", {
|
|
471
|
+
fallbackProjectId: legacyProjectId
|
|
472
|
+
});
|
|
473
|
+
}
|
|
474
|
+
const nodeScope = await resolveTopicNodeScopeOrNull(ctx, legacyProjectId);
|
|
475
|
+
if (nodeScope) {
|
|
476
|
+
return {
|
|
477
|
+
...nodeScope,
|
|
478
|
+
projectId: nodeScope.projectId ?? legacyProjectId
|
|
479
|
+
};
|
|
480
|
+
}
|
|
481
|
+
throw new Error(
|
|
482
|
+
`Legacy project scope ${legacyProjectId} has no mapped topic.`
|
|
483
|
+
);
|
|
484
|
+
}
|
|
485
|
+
async function resolveDirectLegacyProjectTopic(ctx, legacyProjectId) {
|
|
486
|
+
const directTopic = await tryReadTopicDoc(ctx, legacyProjectId, {
|
|
487
|
+
failureLog: "[topicScope] Failed to load direct project topic",
|
|
488
|
+
idLogKey: "projectId"
|
|
489
|
+
});
|
|
490
|
+
return directTopic ?? tryResolveHostTopicByLegacyScope(ctx, legacyProjectId);
|
|
491
|
+
}
|
|
492
|
+
async function tryReadTopicDoc(ctx, id, log) {
|
|
493
|
+
try {
|
|
494
|
+
return await ctx.db.get(id);
|
|
495
|
+
} catch (error) {
|
|
496
|
+
debugGraphPrimitiveFallback(log.failureLog, {
|
|
497
|
+
error,
|
|
498
|
+
[log.idLogKey]: id
|
|
499
|
+
});
|
|
500
|
+
return null;
|
|
501
|
+
}
|
|
502
|
+
}
|
|
503
|
+
async function buildTopicScope(ctx, topic, source, options = {}) {
|
|
504
|
+
const inherited = await resolveInheritedWorkspaceScope(ctx, topic);
|
|
505
|
+
const mapped = asMappedProjectId(topic);
|
|
506
|
+
return {
|
|
507
|
+
topicId: topic._id,
|
|
508
|
+
...mapped || options.fallbackProjectId ? { projectId: mapped ?? options.fallbackProjectId } : {},
|
|
509
|
+
tenantId: inherited.tenantId,
|
|
510
|
+
workspaceId: inherited.workspaceId,
|
|
511
|
+
source
|
|
512
|
+
};
|
|
513
|
+
}
|
|
850
514
|
var optionalScopeArgs = {
|
|
851
515
|
projectId: v.optional(v.string()),
|
|
852
516
|
topicId: v.optional(v.string())
|
|
853
517
|
};
|
|
854
518
|
|
|
855
|
-
// src/epistemicEdges.
|
|
856
|
-
var
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
519
|
+
// src/epistemicEdges.handlers.ts
|
|
520
|
+
var EDGE_CONSTRAINTS = /* @__PURE__ */ new Set([
|
|
521
|
+
"alternative",
|
|
522
|
+
"counterfactual",
|
|
523
|
+
"xor"
|
|
524
|
+
]);
|
|
525
|
+
var EDGE_PROPAGATION_MODES = /* @__PURE__ */ new Set([
|
|
526
|
+
"continuous",
|
|
527
|
+
"threshold"
|
|
528
|
+
]);
|
|
529
|
+
var EDGE_LOGICAL_ROLES = /* @__PURE__ */ new Set([
|
|
530
|
+
"contributory",
|
|
531
|
+
"corroborative",
|
|
532
|
+
"necessary",
|
|
533
|
+
"necessary_sufficient",
|
|
534
|
+
"sufficient"
|
|
535
|
+
]);
|
|
536
|
+
var EDGE_TEMPORAL_CLASSES = /* @__PURE__ */ new Set([
|
|
537
|
+
"cyclical",
|
|
538
|
+
"point_in_time",
|
|
539
|
+
"structural",
|
|
540
|
+
"transient"
|
|
541
|
+
]);
|
|
542
|
+
var EPISTEMIC_LAYERS = /* @__PURE__ */ new Set([
|
|
543
|
+
"L4",
|
|
544
|
+
"L3",
|
|
545
|
+
"L2",
|
|
546
|
+
"L1",
|
|
547
|
+
"ontological",
|
|
548
|
+
"organizational"
|
|
549
|
+
]);
|
|
550
|
+
var REASONING_METHOD_SET = new Set(REASONING_METHODS);
|
|
551
|
+
function readOptionalString2(value) {
|
|
552
|
+
return typeof value === "string" && value.trim().length > 0 ? value : void 0;
|
|
553
|
+
}
|
|
554
|
+
function readConvexId2(value) {
|
|
555
|
+
const normalized = readOptionalString2(value);
|
|
556
|
+
return normalized ? normalized : null;
|
|
557
|
+
}
|
|
558
|
+
function readRecord2(value) {
|
|
559
|
+
return value && typeof value === "object" && !Array.isArray(value) ? value : null;
|
|
560
|
+
}
|
|
561
|
+
function readEdgeMirrorNodeRow(value) {
|
|
562
|
+
const record = readRecord2(value);
|
|
563
|
+
if (!record) {
|
|
564
|
+
return null;
|
|
861
565
|
}
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
566
|
+
const globalId = readOptionalString2(record.globalId);
|
|
567
|
+
return globalId ? { globalId } : null;
|
|
568
|
+
}
|
|
569
|
+
function requireEdgeId(value, context) {
|
|
570
|
+
const id = readConvexId2(value);
|
|
571
|
+
if (!id) {
|
|
572
|
+
throw new Error(`${context} requires a stored epistemic edge id.`);
|
|
868
573
|
}
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
},
|
|
875
|
-
returns: permissiveReturn,
|
|
876
|
-
handler: async (ctx, args) => {
|
|
877
|
-
const { edgeType } = args;
|
|
878
|
-
if (edgeType) {
|
|
879
|
-
return await ctx.db.query("epistemicEdges").withIndex(
|
|
880
|
-
"by_from_type",
|
|
881
|
-
(q) => q.eq("fromNodeId", args.fromNodeId).eq("edgeType", edgeType)
|
|
882
|
-
).collect();
|
|
883
|
-
}
|
|
884
|
-
return await ctx.db.query("epistemicEdges").withIndex("by_from", (q) => q.eq("fromNodeId", args.fromNodeId)).collect();
|
|
574
|
+
return id;
|
|
575
|
+
}
|
|
576
|
+
function readOptionalEnum(value, allowed, fieldName) {
|
|
577
|
+
if (value === void 0) {
|
|
578
|
+
return;
|
|
885
579
|
}
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
args: {
|
|
889
|
-
toNodeId: v.id("epistemicNodes"),
|
|
890
|
-
edgeType: v.optional(edgeTypeValidator)
|
|
891
|
-
},
|
|
892
|
-
returns: permissiveReturn,
|
|
893
|
-
handler: async (ctx, args) => {
|
|
894
|
-
const { edgeType } = args;
|
|
895
|
-
if (edgeType) {
|
|
896
|
-
return await ctx.db.query("epistemicEdges").withIndex(
|
|
897
|
-
"by_to_type",
|
|
898
|
-
(q) => q.eq("toNodeId", args.toNodeId).eq("edgeType", edgeType)
|
|
899
|
-
).collect();
|
|
900
|
-
}
|
|
901
|
-
return await ctx.db.query("epistemicEdges").withIndex("by_to", (q) => q.eq("toNodeId", args.toNodeId)).collect();
|
|
580
|
+
if (allowed.has(value)) {
|
|
581
|
+
return value;
|
|
902
582
|
}
|
|
903
|
-
});
|
|
904
|
-
|
|
583
|
+
throw new Error(`${fieldName} received unsupported value "${value}".`);
|
|
584
|
+
}
|
|
585
|
+
var mirrorEdgeToConvex = internalMutation({
|
|
905
586
|
args: {
|
|
906
|
-
|
|
907
|
-
|
|
587
|
+
globalId: v.string(),
|
|
588
|
+
fromGlobalId: v.string(),
|
|
589
|
+
toGlobalId: v.string(),
|
|
590
|
+
edgeType: v.string(),
|
|
591
|
+
weight: v.optional(v.number()),
|
|
592
|
+
confidence: v.optional(v.number()),
|
|
593
|
+
context: v.optional(v.string()),
|
|
594
|
+
derivationType: v.optional(v.string()),
|
|
595
|
+
createdBy: v.string(),
|
|
596
|
+
...optionalScopeArgs,
|
|
597
|
+
fromLayer: v.optional(v.string()),
|
|
598
|
+
toLayer: v.optional(v.string()),
|
|
599
|
+
fromNodeType: v.optional(v.string()),
|
|
600
|
+
toNodeType: v.optional(v.string()),
|
|
601
|
+
reasoningMethod: v.optional(v.string()),
|
|
602
|
+
logicalRole: v.optional(v.string()),
|
|
603
|
+
temporalClass: v.optional(v.string()),
|
|
604
|
+
validFrom: v.optional(v.number()),
|
|
605
|
+
validUntil: v.optional(v.number()),
|
|
606
|
+
constraint: v.optional(v.string()),
|
|
607
|
+
propagation: v.optional(v.string()),
|
|
608
|
+
blocking: v.optional(v.boolean()),
|
|
609
|
+
implicit: v.optional(v.boolean()),
|
|
610
|
+
conditionalA: v.optional(subjectiveOpinionValidator),
|
|
611
|
+
conditionalNotA: v.optional(subjectiveOpinionValidator)
|
|
908
612
|
},
|
|
909
613
|
returns: permissiveReturn,
|
|
910
614
|
handler: async (ctx, args) => {
|
|
911
|
-
const
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
"by_from",
|
|
918
|
-
(q) => q.eq("fromNodeId", args.sourceNodeId)
|
|
615
|
+
const fromNode = readEdgeMirrorNodeRow(
|
|
616
|
+
await ctx.db.query("epistemicNodes").withIndex("by_globalId", (q) => q.eq("globalId", args.fromGlobalId)).first()
|
|
617
|
+
);
|
|
618
|
+
if (!fromNode) {
|
|
619
|
+
console.log(
|
|
620
|
+
`[Dual-Write] Skipping mirror - source node not in Convex: ${args.fromGlobalId}`
|
|
919
621
|
);
|
|
920
|
-
return
|
|
921
|
-
}
|
|
922
|
-
const byGlobalId = await ctx.db.query("epistemicEdges").withIndex(
|
|
923
|
-
"by_source_global_id",
|
|
924
|
-
(q) => q.eq("sourceGlobalId", args.sourceNodeId)
|
|
925
|
-
).collect();
|
|
926
|
-
if (args.edgeType) {
|
|
927
|
-
return byGlobalId.filter((edge) => edge.edgeType === args.edgeType);
|
|
622
|
+
return buildEdgeMirrorSkippedResult();
|
|
928
623
|
}
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
handler: async (ctx, args) => {
|
|
939
|
-
const isConvexId = args.targetNodeId.length === 32 || args.targetNodeId.includes("epistemicNodes");
|
|
940
|
-
if (isConvexId) {
|
|
941
|
-
const query2 = args.edgeType ? ctx.db.query("epistemicEdges").withIndex(
|
|
942
|
-
"by_to_type",
|
|
943
|
-
(q) => q.eq("toNodeId", args.targetNodeId).eq("edgeType", args.edgeType)
|
|
944
|
-
) : ctx.db.query("epistemicEdges").withIndex(
|
|
945
|
-
"by_to",
|
|
946
|
-
(q) => q.eq("toNodeId", args.targetNodeId)
|
|
624
|
+
const toNode = readEdgeMirrorNodeRow(
|
|
625
|
+
await ctx.db.query("epistemicNodes").withIndex("by_globalId", (q) => q.eq("globalId", args.toGlobalId)).first()
|
|
626
|
+
);
|
|
627
|
+
const existing = await ctx.db.query("epistemicEdges").withIndex("by_globalId", (q) => q.eq("globalId", args.globalId)).first();
|
|
628
|
+
if (existing) {
|
|
629
|
+
console.log(`[Dual-Write] Edge already exists: ${args.globalId}`);
|
|
630
|
+
return buildEdgeMirrorWriteResult(
|
|
631
|
+
requireEdgeId(existing._id, "Edge mirror write result"),
|
|
632
|
+
true
|
|
947
633
|
);
|
|
948
|
-
return await query2.collect();
|
|
949
634
|
}
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
return byGlobalId.filter((edge) => edge.edgeType === args.edgeType);
|
|
635
|
+
if (!toNode) {
|
|
636
|
+
console.log(
|
|
637
|
+
`[Dual-Write] Skipping mirror - to-node not in Convex: ${args.toGlobalId} (edge ${args.globalId} type ${args.edgeType})`
|
|
638
|
+
);
|
|
639
|
+
return buildEdgeMirrorSkippedResult();
|
|
956
640
|
}
|
|
957
|
-
|
|
641
|
+
const now = Date.now();
|
|
642
|
+
const edgeId = await insertEpistemicEdge(ctx, {
|
|
643
|
+
globalId: args.globalId,
|
|
644
|
+
// C2-RR.4 Defect E — endpoints are canonical node globalIds, not Convex
|
|
645
|
+
// doc ids. fromNode/toNode are resolved by_globalId above, so their
|
|
646
|
+
// stored globalId equals args.from/toGlobalId; persisting the doc _id here
|
|
647
|
+
// was the "mixed _id/globalId" defect.
|
|
648
|
+
fromNodeId: fromNode.globalId,
|
|
649
|
+
toNodeId: toNode.globalId,
|
|
650
|
+
sourceGlobalId: args.fromGlobalId,
|
|
651
|
+
targetGlobalId: args.toGlobalId,
|
|
652
|
+
edgeType: args.edgeType,
|
|
653
|
+
weight: args.weight,
|
|
654
|
+
confidence: args.confidence,
|
|
655
|
+
context: args.context,
|
|
656
|
+
derivationType: args.derivationType,
|
|
657
|
+
constraint: readOptionalEnum(
|
|
658
|
+
args.constraint,
|
|
659
|
+
EDGE_CONSTRAINTS,
|
|
660
|
+
"constraint"
|
|
661
|
+
),
|
|
662
|
+
propagation: readOptionalEnum(
|
|
663
|
+
args.propagation,
|
|
664
|
+
EDGE_PROPAGATION_MODES,
|
|
665
|
+
"propagation"
|
|
666
|
+
),
|
|
667
|
+
blocking: args.blocking,
|
|
668
|
+
implicit: args.implicit,
|
|
669
|
+
conditionalA: args.conditionalA,
|
|
670
|
+
conditionalNotA: args.conditionalNotA,
|
|
671
|
+
createdBy: args.createdBy,
|
|
672
|
+
createdAt: now,
|
|
673
|
+
updatedAt: now,
|
|
674
|
+
topicId: args.projectId,
|
|
675
|
+
fromLayer: readOptionalEnum(
|
|
676
|
+
args.fromLayer,
|
|
677
|
+
EPISTEMIC_LAYERS,
|
|
678
|
+
"fromLayer"
|
|
679
|
+
),
|
|
680
|
+
toLayer: readOptionalEnum(args.toLayer, EPISTEMIC_LAYERS, "toLayer"),
|
|
681
|
+
fromNodeType: args.fromNodeType,
|
|
682
|
+
toNodeType: args.toNodeType,
|
|
683
|
+
reasoningMethod: readOptionalEnum(
|
|
684
|
+
args.reasoningMethod,
|
|
685
|
+
REASONING_METHOD_SET,
|
|
686
|
+
"reasoningMethod"
|
|
687
|
+
),
|
|
688
|
+
logicalRole: readOptionalEnum(
|
|
689
|
+
args.logicalRole,
|
|
690
|
+
EDGE_LOGICAL_ROLES,
|
|
691
|
+
"logicalRole"
|
|
692
|
+
),
|
|
693
|
+
temporalClass: readOptionalEnum(
|
|
694
|
+
args.temporalClass,
|
|
695
|
+
EDGE_TEMPORAL_CLASSES,
|
|
696
|
+
"temporalClass"
|
|
697
|
+
),
|
|
698
|
+
validFrom: args.validFrom,
|
|
699
|
+
validUntil: args.validUntil
|
|
700
|
+
});
|
|
701
|
+
console.log(
|
|
702
|
+
`[Dual-Write] Mirrored edge to Convex: ${args.globalId} (${args.edgeType})`
|
|
703
|
+
);
|
|
704
|
+
return buildEdgeMirrorWriteResult(edgeId, false);
|
|
958
705
|
}
|
|
959
706
|
});
|
|
960
|
-
var
|
|
707
|
+
var deleteEdgeFromConvex = internalMutation({
|
|
961
708
|
args: {
|
|
962
|
-
|
|
963
|
-
toNodeId: v.id("epistemicNodes"),
|
|
964
|
-
edgeType: v.optional(edgeTypeValidator)
|
|
709
|
+
globalId: v.string()
|
|
965
710
|
},
|
|
966
711
|
returns: permissiveReturn,
|
|
967
712
|
handler: async (ctx, args) => {
|
|
968
|
-
const
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
).collect();
|
|
972
|
-
if (args.edgeType) {
|
|
973
|
-
return edges.filter((e) => e.edgeType === args.edgeType);
|
|
713
|
+
const existing = await ctx.db.query("epistemicEdges").withIndex("by_globalId", (q) => q.eq("globalId", args.globalId)).first();
|
|
714
|
+
if (!existing) {
|
|
715
|
+
return buildEdgeMirrorMissingResult();
|
|
974
716
|
}
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
fromNodeId: v.id("epistemicNodes"),
|
|
981
|
-
toNodeId: v.id("epistemicNodes")
|
|
982
|
-
},
|
|
983
|
-
returns: permissiveReturn,
|
|
984
|
-
handler: async (ctx, args) => {
|
|
985
|
-
return await ctx.db.query("epistemicEdges").withIndex(
|
|
986
|
-
"by_from_to",
|
|
987
|
-
(q) => q.eq("fromNodeId", args.fromNodeId).eq("toNodeId", args.toNodeId)
|
|
988
|
-
).first();
|
|
717
|
+
await ctx.db.delete(
|
|
718
|
+
requireEdgeId(existing._id, "Delete mirrored edge from Convex")
|
|
719
|
+
);
|
|
720
|
+
console.log(`[Dual-Write] Deleted edge from Convex: ${args.globalId}`);
|
|
721
|
+
return buildEdgeStatusSuccessResult();
|
|
989
722
|
}
|
|
990
723
|
});
|
|
991
|
-
var
|
|
724
|
+
var updateEdgeInConvex = internalMutation({
|
|
992
725
|
args: {
|
|
993
|
-
|
|
994
|
-
|
|
726
|
+
globalId: v.string(),
|
|
727
|
+
weight: v.optional(v.number()),
|
|
728
|
+
confidence: v.optional(v.number()),
|
|
729
|
+
context: v.optional(v.string()),
|
|
730
|
+
derivationType: v.optional(v.string()),
|
|
731
|
+
constraint: v.optional(v.string()),
|
|
732
|
+
propagation: v.optional(v.string()),
|
|
733
|
+
blocking: v.optional(v.boolean()),
|
|
734
|
+
implicit: v.optional(v.boolean()),
|
|
735
|
+
conditionalA: v.optional(subjectiveOpinionValidator),
|
|
736
|
+
conditionalNotA: v.optional(subjectiveOpinionValidator)
|
|
995
737
|
},
|
|
996
738
|
returns: permissiveReturn,
|
|
997
739
|
handler: async (ctx, args) => {
|
|
998
|
-
|
|
999
|
-
|
|
740
|
+
const existing = await ctx.db.query("epistemicEdges").withIndex("by_globalId", (q) => q.eq("globalId", args.globalId)).first();
|
|
741
|
+
if (!existing) {
|
|
742
|
+
return buildEdgeMirrorMissingResult();
|
|
1000
743
|
}
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
});
|
|
1007
|
-
} catch (error) {
|
|
1008
|
-
debugGraphPrimitiveFallback(
|
|
1009
|
-
"[epistemicEdges] Failed to resolve getAll scope",
|
|
1010
|
-
{
|
|
1011
|
-
error,
|
|
1012
|
-
projectId: args.projectId,
|
|
1013
|
-
topicId: args.topicId
|
|
1014
|
-
}
|
|
1015
|
-
);
|
|
1016
|
-
return [];
|
|
744
|
+
const updates = {
|
|
745
|
+
updatedAt: Date.now()
|
|
746
|
+
};
|
|
747
|
+
if (args.weight !== void 0) {
|
|
748
|
+
updates.weight = args.weight;
|
|
1017
749
|
}
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
(e) => e.edgeType === args.edgeType && edgeMatchesWorkspaceReasoningScope(e, scope)
|
|
1021
|
-
).slice(0, 5e3);
|
|
1022
|
-
}
|
|
1023
|
-
});
|
|
1024
|
-
var getByProject = query({
|
|
1025
|
-
args: {
|
|
1026
|
-
...optionalScopeArgs,
|
|
1027
|
-
userId: v.optional(v.string()),
|
|
1028
|
-
limit: v.optional(v.number())
|
|
1029
|
-
},
|
|
1030
|
-
returns: permissiveReturn,
|
|
1031
|
-
handler: async (ctx, args) => {
|
|
1032
|
-
if (!args.projectId && !args.topicId) {
|
|
1033
|
-
return [];
|
|
750
|
+
if (args.confidence !== void 0) {
|
|
751
|
+
updates.confidence = args.confidence;
|
|
1034
752
|
}
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
scope = await resolveTopicProjectScope(ctx, {
|
|
1038
|
-
projectId: args.projectId,
|
|
1039
|
-
topicId: args.topicId
|
|
1040
|
-
});
|
|
1041
|
-
} catch (error) {
|
|
1042
|
-
debugGraphPrimitiveFallback(
|
|
1043
|
-
"[epistemicEdges] Failed to resolve getByProject scope",
|
|
1044
|
-
{
|
|
1045
|
-
error,
|
|
1046
|
-
projectId: args.projectId,
|
|
1047
|
-
topicId: args.topicId
|
|
1048
|
-
}
|
|
1049
|
-
);
|
|
1050
|
-
return [];
|
|
753
|
+
if (args.context !== void 0) {
|
|
754
|
+
updates.context = args.context;
|
|
1051
755
|
}
|
|
1052
|
-
if (args.
|
|
1053
|
-
|
|
1054
|
-
ctx,
|
|
1055
|
-
String(scope.topicId ?? scope.projectId),
|
|
1056
|
-
args.userId
|
|
1057
|
-
);
|
|
1058
|
-
if (!hasAccess) {
|
|
1059
|
-
return [];
|
|
1060
|
-
}
|
|
756
|
+
if (args.derivationType !== void 0) {
|
|
757
|
+
updates.derivationType = args.derivationType;
|
|
1061
758
|
}
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
759
|
+
if (args.constraint !== void 0) {
|
|
760
|
+
updates.constraint = args.constraint;
|
|
761
|
+
}
|
|
762
|
+
if (args.propagation !== void 0) {
|
|
763
|
+
updates.propagation = args.propagation;
|
|
764
|
+
}
|
|
765
|
+
if (args.blocking !== void 0) {
|
|
766
|
+
updates.blocking = args.blocking;
|
|
767
|
+
}
|
|
768
|
+
if (args.implicit !== void 0) {
|
|
769
|
+
updates.implicit = args.implicit;
|
|
770
|
+
}
|
|
771
|
+
if (args.conditionalA !== void 0) {
|
|
772
|
+
updates.conditionalA = args.conditionalA;
|
|
773
|
+
}
|
|
774
|
+
if (args.conditionalNotA !== void 0) {
|
|
775
|
+
updates.conditionalNotA = args.conditionalNotA;
|
|
776
|
+
}
|
|
777
|
+
await ctx.db.patch(
|
|
778
|
+
requireEdgeId(existing._id, "Update mirrored edge in Convex"),
|
|
779
|
+
updates
|
|
1067
780
|
);
|
|
1068
|
-
|
|
781
|
+
console.log(`[Dual-Write] Updated edge in Convex: ${args.globalId}`);
|
|
782
|
+
return buildEdgeStatusSuccessResult();
|
|
1069
783
|
}
|
|
1070
784
|
});
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
785
|
+
|
|
786
|
+
// src/graphTypes.ts
|
|
787
|
+
var EDGE_TYPE_TO_REL = {
|
|
788
|
+
// === THE SIX CANONICAL EPISTEMIC EDGE TYPES ===
|
|
789
|
+
supports: "SUPPORTS",
|
|
790
|
+
// L3↔L3: belief bears on belief (weight -1 to +1)
|
|
791
|
+
informs: "INFORMS",
|
|
792
|
+
// L2→L3: evidence bears on belief
|
|
793
|
+
depends_on: "DEPENDS_ON",
|
|
794
|
+
// L3→L3, Q→Q: structural gate
|
|
795
|
+
derived_from: "DERIVED_FROM",
|
|
796
|
+
// Any→Any: provenance chain (fork, synthesis, extraction, answer)
|
|
797
|
+
contains: "CONTAINS",
|
|
798
|
+
// Any→Any: hierarchy, scoping, membership
|
|
799
|
+
tests: "TESTS",
|
|
800
|
+
// Q→L3: question interrogates belief
|
|
801
|
+
// === L4 DECISION EDGES (derived_from with derivationType=decision) ===
|
|
802
|
+
// Kept as separate Neo4j relationship types for backward compat with L4 queries.
|
|
803
|
+
// New code should use derived_from + metadata.
|
|
804
|
+
based_on_belief: "BASED_ON_BELIEF",
|
|
805
|
+
based_on_question: "BASED_ON_QUESTION",
|
|
806
|
+
blocked_by_contradiction: "BLOCKED_BY_CONTRADICTION",
|
|
807
|
+
informed_by_theme: "INFORMED_BY_THEME",
|
|
808
|
+
// === ONTOLOGICAL EDGES (tenant-extensible, managed by ontology system) ===
|
|
809
|
+
works_at: "WORKS_AT",
|
|
810
|
+
invested_in: "INVESTED_IN",
|
|
811
|
+
competes_with: "COMPETES_WITH",
|
|
812
|
+
participates_in: "PARTICIPATES_IN",
|
|
813
|
+
founded_by: "FOUNDED_BY",
|
|
814
|
+
evaluates: "EVALUATES",
|
|
815
|
+
performs: "PERFORMS",
|
|
816
|
+
function_in: "FUNCTION_IN",
|
|
817
|
+
impacts: "IMPACTS",
|
|
818
|
+
raised_from: "RAISED_FROM",
|
|
819
|
+
mentioned_in: "MENTIONED_IN",
|
|
820
|
+
perspective_on: "PERSPECTIVE_ON",
|
|
821
|
+
about_entity: "ABOUT_ENTITY",
|
|
822
|
+
entity_referenced_in: "ENTITY_REFERENCED_IN"
|
|
823
|
+
};
|
|
824
|
+
function getNodeLayer(nodeType) {
|
|
825
|
+
const L4_TYPES = ["decision"];
|
|
826
|
+
const L3_TYPES = ["belief", "question", "theme", "deal"];
|
|
827
|
+
const L2_TYPES = ["claim", "evidence", "synthesis", "answer"];
|
|
828
|
+
const L1_TYPES = ["atomic_fact", "excerpt", "source"];
|
|
829
|
+
const ONTOLOGICAL_TYPES = [
|
|
830
|
+
"company",
|
|
831
|
+
"person",
|
|
832
|
+
"investor",
|
|
833
|
+
"function",
|
|
834
|
+
"value_chain"
|
|
835
|
+
];
|
|
836
|
+
const ORGANIZATIONAL_TYPES = ["topic"];
|
|
837
|
+
if (L4_TYPES.includes(nodeType)) {
|
|
838
|
+
return "L4";
|
|
839
|
+
}
|
|
840
|
+
if (L3_TYPES.includes(nodeType)) {
|
|
841
|
+
return "L3";
|
|
842
|
+
}
|
|
843
|
+
if (L2_TYPES.includes(nodeType)) {
|
|
844
|
+
return "L2";
|
|
845
|
+
}
|
|
846
|
+
if (L1_TYPES.includes(nodeType)) {
|
|
847
|
+
return "L1";
|
|
848
|
+
}
|
|
849
|
+
if (ONTOLOGICAL_TYPES.includes(nodeType)) {
|
|
850
|
+
return "ontological";
|
|
851
|
+
}
|
|
852
|
+
if (ORGANIZATIONAL_TYPES.includes(nodeType)) {
|
|
853
|
+
return "organizational";
|
|
854
|
+
}
|
|
855
|
+
console.warn(`[GraphTypes] Unknown nodeType "${nodeType}", defaulting to L2`);
|
|
856
|
+
return "L2";
|
|
857
|
+
}
|
|
858
|
+
var CANONICAL_EPISTEMIC_TYPES = /* @__PURE__ */ new Set([
|
|
859
|
+
"supports",
|
|
860
|
+
"informs",
|
|
861
|
+
"depends_on",
|
|
862
|
+
"derived_from",
|
|
863
|
+
"contains",
|
|
864
|
+
"tests"
|
|
865
|
+
]);
|
|
866
|
+
function isDeprecatedEdgeType(edgeType) {
|
|
867
|
+
if (CANONICAL_EPISTEMIC_TYPES.has(edgeType)) {
|
|
868
|
+
return false;
|
|
869
|
+
}
|
|
870
|
+
if (edgeType in EDGE_TYPE_TO_REL) {
|
|
871
|
+
return false;
|
|
872
|
+
}
|
|
873
|
+
return true;
|
|
874
|
+
}
|
|
875
|
+
|
|
876
|
+
// src/edgeValidation.ts
|
|
877
|
+
var VALID_LAYER_CONNECTIONS = {
|
|
878
|
+
ontological: /* @__PURE__ */ new Set(["ontological", "L1", "L2", "L3"]),
|
|
879
|
+
L1: /* @__PURE__ */ new Set(["L1", "L2"]),
|
|
880
|
+
L2: /* @__PURE__ */ new Set(["L1", "L2", "L3", "ontological"]),
|
|
881
|
+
L3: /* @__PURE__ */ new Set(["L2", "L3", "L4", "ontological", "organizational"]),
|
|
882
|
+
L4: /* @__PURE__ */ new Set(["L3", "L4"]),
|
|
883
|
+
organizational: /* @__PURE__ */ new Set(["organizational", "L3"])
|
|
884
|
+
};
|
|
885
|
+
function getLayerDepth(layer) {
|
|
886
|
+
switch (layer) {
|
|
887
|
+
case "L4":
|
|
888
|
+
return 4;
|
|
889
|
+
case "L3":
|
|
890
|
+
return 3;
|
|
891
|
+
case "L2":
|
|
892
|
+
return 2;
|
|
893
|
+
case "L1":
|
|
894
|
+
return 1;
|
|
895
|
+
case "ontological":
|
|
896
|
+
return 0;
|
|
897
|
+
default:
|
|
898
|
+
return -1;
|
|
899
|
+
}
|
|
900
|
+
}
|
|
901
|
+
function isValidLayerConnection(fromLayer, toLayer) {
|
|
902
|
+
if (!VALID_LAYER_CONNECTIONS[fromLayer].has(toLayer)) {
|
|
903
|
+
return false;
|
|
904
|
+
}
|
|
905
|
+
if (fromLayer === "ontological" || toLayer === "ontological") {
|
|
906
|
+
return true;
|
|
907
|
+
}
|
|
908
|
+
if (fromLayer === "organizational" || toLayer === "organizational") {
|
|
909
|
+
return true;
|
|
910
|
+
}
|
|
911
|
+
const fromDepth = getLayerDepth(fromLayer);
|
|
912
|
+
const toDepth = getLayerDepth(toLayer);
|
|
913
|
+
if (Math.abs(fromDepth - toDepth) > 1) {
|
|
914
|
+
return false;
|
|
915
|
+
}
|
|
916
|
+
return true;
|
|
917
|
+
}
|
|
918
|
+
function shouldContinueTraversal(currentLayer, targetLayer, options) {
|
|
919
|
+
const currentDepth = getLayerDepth(currentLayer);
|
|
920
|
+
const targetDepth = getLayerDepth(targetLayer);
|
|
921
|
+
if (options.minLayer !== void 0 && targetDepth < options.minLayer) {
|
|
922
|
+
return false;
|
|
923
|
+
}
|
|
924
|
+
if (options.maxLayer !== void 0 && targetDepth > options.maxLayer) {
|
|
925
|
+
return false;
|
|
926
|
+
}
|
|
927
|
+
switch (options.mode) {
|
|
928
|
+
case "anchor_down":
|
|
929
|
+
return targetDepth <= currentDepth;
|
|
930
|
+
case "anchor_up":
|
|
931
|
+
return targetDepth >= currentDepth;
|
|
932
|
+
case "same_layer":
|
|
933
|
+
return targetDepth === currentDepth;
|
|
934
|
+
case "decision_trace":
|
|
935
|
+
return currentDepth === 4 ? targetDepth === 3 : targetDepth === currentDepth;
|
|
936
|
+
default:
|
|
937
|
+
return true;
|
|
938
|
+
}
|
|
939
|
+
}
|
|
940
|
+
function getDefaultMinLayer(mode) {
|
|
941
|
+
switch (mode) {
|
|
942
|
+
case "anchor_down":
|
|
943
|
+
return 1;
|
|
944
|
+
case "anchor_up":
|
|
945
|
+
return 2;
|
|
946
|
+
case "same_layer":
|
|
947
|
+
return 1;
|
|
948
|
+
case "decision_trace":
|
|
949
|
+
return 3;
|
|
950
|
+
default:
|
|
951
|
+
return 1;
|
|
952
|
+
}
|
|
953
|
+
}
|
|
954
|
+
var EDGE_LAYER_RULES = {
|
|
955
|
+
// === 6 Canonical Epistemic Types ===
|
|
956
|
+
supports: {
|
|
957
|
+
from: ["L3"],
|
|
958
|
+
to: ["L3"],
|
|
959
|
+
description: "Belief bears on Belief (weight: +1 supports, -1 contradicts)"
|
|
960
|
+
},
|
|
961
|
+
informs: {
|
|
962
|
+
from: ["L2"],
|
|
963
|
+
to: ["L3"],
|
|
964
|
+
description: "Evidence -> Belief (L2 -> L3)"
|
|
965
|
+
},
|
|
966
|
+
depends_on: {
|
|
967
|
+
from: ["L3"],
|
|
968
|
+
to: ["L3"],
|
|
969
|
+
description: "Belief B requires Belief A (structural gate)"
|
|
970
|
+
},
|
|
971
|
+
derived_from: {
|
|
972
|
+
from: ["L2", "L3", "L4"],
|
|
973
|
+
to: ["L1", "L2", "L3"],
|
|
974
|
+
description: "A was produced from B (provenance chain)"
|
|
975
|
+
},
|
|
976
|
+
contains: {
|
|
977
|
+
from: ["L3", "L4", "ontological"],
|
|
978
|
+
to: ["L2", "L3", "ontological"],
|
|
979
|
+
description: "A scopes/aggregates B (hierarchy)"
|
|
980
|
+
},
|
|
981
|
+
tests: {
|
|
982
|
+
from: ["L3"],
|
|
983
|
+
to: ["L3"],
|
|
984
|
+
description: "Question -> Belief (L3 -> L3)"
|
|
985
|
+
},
|
|
986
|
+
// === Structural / Lifecycle ===
|
|
987
|
+
supersedes: {
|
|
988
|
+
from: ["L3"],
|
|
989
|
+
to: ["L3"],
|
|
990
|
+
description: "NewNode -> OldNode (fork lineage)"
|
|
991
|
+
},
|
|
992
|
+
responds_to: {
|
|
993
|
+
from: ["L2", "L3"],
|
|
994
|
+
to: ["L3"],
|
|
995
|
+
description: "Answer -> Question (L2/L3 -> L3)"
|
|
996
|
+
},
|
|
997
|
+
belongs_to: {
|
|
998
|
+
from: ["L2", "L3", "ontological"],
|
|
999
|
+
to: ["L3"],
|
|
1000
|
+
description: "Membership (migrating to contains)"
|
|
1001
|
+
},
|
|
1002
|
+
relates_to_thesis: {
|
|
1003
|
+
from: ["L3"],
|
|
1004
|
+
to: ["L3"],
|
|
1005
|
+
description: "Belief -> Theme (L3 -> L3)"
|
|
1006
|
+
},
|
|
1007
|
+
// === Ontological (entity-entity or entity-epistemic bridge) ===
|
|
1008
|
+
works_at: {
|
|
1009
|
+
from: ["ontological"],
|
|
1010
|
+
to: ["ontological"],
|
|
1011
|
+
description: "Person -> Company"
|
|
1012
|
+
},
|
|
1013
|
+
invested_in: {
|
|
1014
|
+
from: ["ontological"],
|
|
1015
|
+
to: ["ontological"],
|
|
1016
|
+
description: "Investor -> Company"
|
|
1017
|
+
},
|
|
1018
|
+
competes_with: {
|
|
1019
|
+
from: ["ontological"],
|
|
1020
|
+
to: ["ontological"],
|
|
1021
|
+
description: "Company -> Company"
|
|
1022
|
+
},
|
|
1023
|
+
participates_in: {
|
|
1024
|
+
from: ["ontological"],
|
|
1025
|
+
to: ["ontological"],
|
|
1026
|
+
description: "Company -> ValueChain"
|
|
1027
|
+
},
|
|
1028
|
+
founded_by: {
|
|
1029
|
+
from: ["ontological"],
|
|
1030
|
+
to: ["ontological"],
|
|
1031
|
+
description: "Company -> Person"
|
|
1032
|
+
},
|
|
1033
|
+
evaluates: {
|
|
1034
|
+
from: ["ontological"],
|
|
1035
|
+
to: ["ontological"],
|
|
1036
|
+
description: "Deal -> Company"
|
|
1037
|
+
},
|
|
1038
|
+
performs: {
|
|
1039
|
+
from: ["ontological"],
|
|
1040
|
+
to: ["ontological"],
|
|
1041
|
+
description: "Company -> Function"
|
|
1042
|
+
},
|
|
1043
|
+
function_in: {
|
|
1044
|
+
from: ["ontological"],
|
|
1045
|
+
to: ["ontological"],
|
|
1046
|
+
description: "Function -> ValueChain"
|
|
1047
|
+
},
|
|
1048
|
+
impacts: {
|
|
1049
|
+
from: ["ontological", "L3"],
|
|
1050
|
+
to: ["ontological"],
|
|
1051
|
+
description: "Theme/Entity -> ValueChain"
|
|
1052
|
+
},
|
|
1053
|
+
raised_from: {
|
|
1054
|
+
from: ["ontological"],
|
|
1055
|
+
to: ["ontological"],
|
|
1056
|
+
description: "Company -> Investor"
|
|
1057
|
+
},
|
|
1058
|
+
mentioned_in: {
|
|
1059
|
+
from: ["ontological"],
|
|
1060
|
+
to: ["L1", "L2"],
|
|
1061
|
+
description: "Entity -> Source/Evidence"
|
|
1062
|
+
},
|
|
1063
|
+
perspective_on: {
|
|
1064
|
+
from: ["ontological"],
|
|
1065
|
+
to: ["L3"],
|
|
1066
|
+
description: "Person -> Belief/Theme"
|
|
1067
|
+
},
|
|
1068
|
+
plays_theme: {
|
|
1069
|
+
from: ["ontological"],
|
|
1070
|
+
to: ["L3"],
|
|
1071
|
+
description: "Deal -> Theme"
|
|
1072
|
+
},
|
|
1073
|
+
// C2-RR.4 — storage migration alias + the remaining 55 reachable edge types.
|
|
1074
|
+
// Mirrors the kernel rule table
|
|
1075
|
+
// (packages/reasoning-kernel/src/adapters/lib/edgeValidation.ts) so this
|
|
1076
|
+
// graph-primitives write path admits the same public edge vocabulary. See the
|
|
1077
|
+
// kernel file for the per-group rationale.
|
|
1078
|
+
extracted_from: {
|
|
1079
|
+
from: ["L2", "L3", "L4"],
|
|
1080
|
+
to: ["L1", "L2", "L3"],
|
|
1081
|
+
description: "Legacy storage migration alias for derived_from; public writes use derived_from"
|
|
1082
|
+
},
|
|
1083
|
+
qualifies: {
|
|
1084
|
+
from: ["L2", "L3"],
|
|
1085
|
+
to: ["L3"],
|
|
1086
|
+
description: "Evidence/Belief qualifies a Belief"
|
|
1087
|
+
},
|
|
1088
|
+
contradicts: {
|
|
1089
|
+
from: ["L2", "L3"],
|
|
1090
|
+
to: ["L3"],
|
|
1091
|
+
description: "Evidence/Belief contradicts a Belief"
|
|
1092
|
+
},
|
|
1093
|
+
reinforces: {
|
|
1094
|
+
from: ["L2", "L3"],
|
|
1095
|
+
to: ["L3"],
|
|
1096
|
+
description: "Evidence/Belief reinforces a Belief"
|
|
1097
|
+
},
|
|
1098
|
+
corroborates: {
|
|
1099
|
+
from: ["L2", "L3"],
|
|
1100
|
+
to: ["L3"],
|
|
1101
|
+
description: "Independent evidence/belief corroborates a Belief"
|
|
1102
|
+
},
|
|
1103
|
+
strengthened_by: {
|
|
1104
|
+
from: ["L3"],
|
|
1105
|
+
to: ["L2", "L3"],
|
|
1106
|
+
description: "Belief is strengthened by Evidence/Belief"
|
|
1107
|
+
},
|
|
1108
|
+
weakened_by: {
|
|
1109
|
+
from: ["L3"],
|
|
1110
|
+
to: ["L2", "L3"],
|
|
1111
|
+
description: "Belief is weakened by Evidence/Belief"
|
|
1112
|
+
},
|
|
1113
|
+
validated_by: {
|
|
1114
|
+
from: ["L3"],
|
|
1115
|
+
to: ["L2", "L3"],
|
|
1116
|
+
description: "Belief is validated by Evidence/Belief"
|
|
1117
|
+
},
|
|
1118
|
+
falsified_by: {
|
|
1119
|
+
from: ["L3"],
|
|
1120
|
+
to: ["L2", "L3"],
|
|
1121
|
+
description: "Belief is falsified by Evidence/Belief"
|
|
1122
|
+
},
|
|
1123
|
+
amplifies: {
|
|
1124
|
+
from: ["L3"],
|
|
1125
|
+
to: ["L3"],
|
|
1126
|
+
description: "Belief amplifies another Belief"
|
|
1127
|
+
},
|
|
1128
|
+
precondition_for: {
|
|
1129
|
+
from: ["L3"],
|
|
1130
|
+
to: ["L3"],
|
|
1131
|
+
description: "Belief is a precondition for another Belief"
|
|
1132
|
+
},
|
|
1133
|
+
prerequisite_for: {
|
|
1134
|
+
from: ["L3"],
|
|
1135
|
+
to: ["L3"],
|
|
1136
|
+
description: "Belief is a prerequisite for another Belief"
|
|
1137
|
+
},
|
|
1138
|
+
required_for: {
|
|
1139
|
+
from: ["L3"],
|
|
1140
|
+
to: ["L3"],
|
|
1141
|
+
description: "Belief is required for another Belief"
|
|
1142
|
+
},
|
|
1143
|
+
in_tension_with: {
|
|
1144
|
+
from: ["L3"],
|
|
1145
|
+
to: ["L3"],
|
|
1146
|
+
description: "Belief is in tension with another Belief"
|
|
1147
|
+
},
|
|
1148
|
+
mutually_exclusive: {
|
|
1149
|
+
from: ["L3"],
|
|
1150
|
+
to: ["L3"],
|
|
1151
|
+
description: "Beliefs cannot both hold"
|
|
1152
|
+
},
|
|
1153
|
+
exclusive_with: {
|
|
1154
|
+
from: ["L3"],
|
|
1155
|
+
to: ["L3"],
|
|
1156
|
+
description: "Belief is exclusive with another Belief"
|
|
1157
|
+
},
|
|
1158
|
+
alternative_to: {
|
|
1159
|
+
from: ["L3"],
|
|
1160
|
+
to: ["L3"],
|
|
1161
|
+
description: "Belief is an alternative to another Belief"
|
|
1162
|
+
},
|
|
1163
|
+
subsumes: {
|
|
1164
|
+
from: ["L3"],
|
|
1165
|
+
to: ["L3"],
|
|
1166
|
+
description: "Belief subsumes a narrower Belief"
|
|
1167
|
+
},
|
|
1168
|
+
extends: {
|
|
1169
|
+
from: ["L3"],
|
|
1170
|
+
to: ["L3"],
|
|
1171
|
+
description: "Belief extends another Belief"
|
|
1172
|
+
},
|
|
1173
|
+
refines: {
|
|
1174
|
+
from: ["L3"],
|
|
1175
|
+
to: ["L3"],
|
|
1176
|
+
description: "Belief refines another Belief"
|
|
1177
|
+
},
|
|
1178
|
+
implements: {
|
|
1179
|
+
from: ["L3"],
|
|
1180
|
+
to: ["L3"],
|
|
1181
|
+
description: "Belief implements an abstract Belief"
|
|
1182
|
+
},
|
|
1183
|
+
violates: {
|
|
1184
|
+
from: ["L3"],
|
|
1185
|
+
to: ["L3"],
|
|
1186
|
+
description: "Belief violates a constraint Belief"
|
|
1187
|
+
},
|
|
1188
|
+
assumes: {
|
|
1189
|
+
from: ["L3"],
|
|
1190
|
+
to: ["L3"],
|
|
1191
|
+
description: "Belief assumes another Belief"
|
|
1192
|
+
},
|
|
1193
|
+
would_predict: {
|
|
1194
|
+
from: ["L3"],
|
|
1195
|
+
to: ["L3"],
|
|
1196
|
+
description: "Belief would predict another Belief"
|
|
1197
|
+
},
|
|
1198
|
+
analogous_to: {
|
|
1199
|
+
from: ["L3"],
|
|
1200
|
+
to: ["L3"],
|
|
1201
|
+
description: "Belief is analogous to another Belief"
|
|
1202
|
+
},
|
|
1203
|
+
independent_of: {
|
|
1204
|
+
from: ["L3"],
|
|
1205
|
+
to: ["L3"],
|
|
1206
|
+
description: "Belief is independent of another Belief"
|
|
1207
|
+
},
|
|
1208
|
+
correlates_with: {
|
|
1209
|
+
from: ["L3"],
|
|
1210
|
+
to: ["L3"],
|
|
1211
|
+
description: "Belief correlates with another Belief"
|
|
1212
|
+
},
|
|
1213
|
+
co_changes_with: {
|
|
1214
|
+
from: ["L3"],
|
|
1215
|
+
to: ["L3"],
|
|
1216
|
+
description: "Belief co-changes with another Belief"
|
|
1217
|
+
},
|
|
1218
|
+
counterfactual_of: {
|
|
1219
|
+
from: ["L3"],
|
|
1220
|
+
to: ["L3"],
|
|
1221
|
+
description: "Belief is a counterfactual of another Belief"
|
|
1222
|
+
},
|
|
1223
|
+
parallel_to: {
|
|
1224
|
+
from: ["L3"],
|
|
1225
|
+
to: ["L3"],
|
|
1226
|
+
description: "Belief runs parallel to another Belief"
|
|
1227
|
+
},
|
|
1228
|
+
cascade_from: {
|
|
1229
|
+
from: ["L3"],
|
|
1230
|
+
to: ["L3"],
|
|
1231
|
+
description: "Cascade from an upstream Belief"
|
|
1232
|
+
},
|
|
1233
|
+
cascade_to: {
|
|
1234
|
+
from: ["L3"],
|
|
1235
|
+
to: ["L3"],
|
|
1236
|
+
description: "Cascade to a downstream Belief"
|
|
1237
|
+
},
|
|
1238
|
+
collapses_if: {
|
|
1239
|
+
from: ["L3"],
|
|
1240
|
+
to: ["L3"],
|
|
1241
|
+
description: "Belief collapses if another fails"
|
|
1242
|
+
},
|
|
1243
|
+
branches_from: {
|
|
1244
|
+
from: ["L3"],
|
|
1245
|
+
to: ["L3"],
|
|
1246
|
+
description: "Belief branches from an ancestor Belief"
|
|
1247
|
+
},
|
|
1248
|
+
same_as: {
|
|
1249
|
+
from: ["L2", "L3", "ontological"],
|
|
1250
|
+
to: ["L2", "L3", "ontological"],
|
|
1251
|
+
description: "Two nodes are the same entity"
|
|
1074
1252
|
},
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
Math.min(Math.floor(args.limit ?? 5e3), 1e4)
|
|
1080
|
-
);
|
|
1081
|
-
return await ctx.db.query("epistemicEdges").order("desc").take(pageSize);
|
|
1082
|
-
}
|
|
1083
|
-
});
|
|
1084
|
-
var findContradictions = query({
|
|
1085
|
-
args: {
|
|
1086
|
-
nodeId: v.id("epistemicNodes")
|
|
1253
|
+
answers: {
|
|
1254
|
+
from: ["L2", "L3"],
|
|
1255
|
+
to: ["L3"],
|
|
1256
|
+
description: "Answer/Belief answers a Question"
|
|
1087
1257
|
},
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
(q) => q.eq("toNodeId", args.nodeId).eq("edgeType", "informs")
|
|
1093
|
-
).collect();
|
|
1094
|
-
return edges.filter((e) => (e.weight ?? 0) < 0);
|
|
1095
|
-
}
|
|
1096
|
-
});
|
|
1097
|
-
var findSupport = query({
|
|
1098
|
-
args: {
|
|
1099
|
-
nodeId: v.id("epistemicNodes")
|
|
1258
|
+
partially_answers: {
|
|
1259
|
+
from: ["L2", "L3"],
|
|
1260
|
+
to: ["L3"],
|
|
1261
|
+
description: "Answer/Belief partially answers a Question"
|
|
1100
1262
|
},
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
(q) => q.eq("toNodeId", args.nodeId).eq("edgeType", "informs")
|
|
1106
|
-
).collect();
|
|
1107
|
-
return edges.filter((e) => (e.weight ?? 0) >= 0);
|
|
1108
|
-
}
|
|
1109
|
-
});
|
|
1110
|
-
var getLineage = query({
|
|
1111
|
-
args: {
|
|
1112
|
-
nodeId: v.id("epistemicNodes"),
|
|
1113
|
-
maxDepth: v.optional(v.number()),
|
|
1114
|
-
// Phase 2D: Layer-aware traversal options
|
|
1115
|
-
minLayer: v.optional(v.number()),
|
|
1116
|
-
// 1=L1, 2=L2, 3=L3, 4=L4
|
|
1117
|
-
maxLayer: v.optional(v.number()),
|
|
1118
|
-
mode: v.optional(
|
|
1119
|
-
v.union(
|
|
1120
|
-
v.literal("anchor_down"),
|
|
1121
|
-
v.literal("anchor_up"),
|
|
1122
|
-
v.literal("same_layer"),
|
|
1123
|
-
v.literal("decision_trace")
|
|
1124
|
-
)
|
|
1125
|
-
)
|
|
1263
|
+
explores: {
|
|
1264
|
+
from: ["L3"],
|
|
1265
|
+
to: ["L3"],
|
|
1266
|
+
description: "Question explores a Belief/Theme"
|
|
1126
1267
|
},
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
const minLayer = args.minLayer ?? getDefaultMinLayer(mode);
|
|
1132
|
-
const maxLayer = args.maxLayer ?? 4;
|
|
1133
|
-
const lineage = [];
|
|
1134
|
-
const visited = /* @__PURE__ */ new Set();
|
|
1135
|
-
const startNode = await ctx.db.get(args.nodeId);
|
|
1136
|
-
if (!startNode) {
|
|
1137
|
-
return lineage;
|
|
1138
|
-
}
|
|
1139
|
-
const startLayer = startNode.epistemicLayer || getNodeLayer(startNode.nodeType);
|
|
1140
|
-
const queue = [{ nodeId: args.nodeId, depth: 0, currentLayer: startLayer }];
|
|
1141
|
-
while (queue.length > 0) {
|
|
1142
|
-
const current = queue.shift();
|
|
1143
|
-
if (!current || current.depth >= maxDepth) {
|
|
1144
|
-
continue;
|
|
1145
|
-
}
|
|
1146
|
-
const nodeIdStr = current.nodeId.toString();
|
|
1147
|
-
if (visited.has(nodeIdStr)) {
|
|
1148
|
-
continue;
|
|
1149
|
-
}
|
|
1150
|
-
visited.add(nodeIdStr);
|
|
1151
|
-
const edges = await ctx.db.query("epistemicEdges").withIndex("by_from", (q) => q.eq("fromNodeId", current.nodeId)).collect();
|
|
1152
|
-
const lineageEdges = edges.filter((e) => e.edgeType === "derived_from");
|
|
1153
|
-
for (const edge of lineageEdges) {
|
|
1154
|
-
if (!edge.toNodeId) {
|
|
1155
|
-
continue;
|
|
1156
|
-
}
|
|
1157
|
-
const targetNode = await ctx.db.get(edge.toNodeId);
|
|
1158
|
-
if (!targetNode || visited.has(edge.toNodeId.toString())) {
|
|
1159
|
-
continue;
|
|
1160
|
-
}
|
|
1161
|
-
const targetLayer = edge.toLayer || targetNode.epistemicLayer || getNodeLayer(targetNode.nodeType);
|
|
1162
|
-
const shouldContinue = shouldContinueTraversal(
|
|
1163
|
-
current.currentLayer,
|
|
1164
|
-
targetLayer,
|
|
1165
|
-
{ mode, minLayer, maxLayer }
|
|
1166
|
-
);
|
|
1167
|
-
if (!shouldContinue) {
|
|
1168
|
-
continue;
|
|
1169
|
-
}
|
|
1170
|
-
lineage.push({
|
|
1171
|
-
node: targetNode,
|
|
1172
|
-
depth: current.depth + 1,
|
|
1173
|
-
edgeType: edge.edgeType,
|
|
1174
|
-
layer: targetLayer
|
|
1175
|
-
});
|
|
1176
|
-
queue.push({
|
|
1177
|
-
nodeId: edge.toNodeId,
|
|
1178
|
-
depth: current.depth + 1,
|
|
1179
|
-
currentLayer: targetLayer
|
|
1180
|
-
});
|
|
1181
|
-
}
|
|
1182
|
-
}
|
|
1183
|
-
return lineage;
|
|
1184
|
-
}
|
|
1185
|
-
});
|
|
1186
|
-
var getEvidenceForBelief = query({
|
|
1187
|
-
args: {
|
|
1188
|
-
beliefNodeId: v.id("epistemicNodes")
|
|
1268
|
+
informed_by_theme: {
|
|
1269
|
+
from: ["L3"],
|
|
1270
|
+
to: ["L3"],
|
|
1271
|
+
description: "Belief is informed by a Theme"
|
|
1189
1272
|
},
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
|
|
1273
|
+
same_theme_as: {
|
|
1274
|
+
from: ["L3"],
|
|
1275
|
+
to: ["L3"],
|
|
1276
|
+
description: "Two Beliefs share a Theme"
|
|
1277
|
+
},
|
|
1278
|
+
based_on: {
|
|
1279
|
+
from: ["L4"],
|
|
1280
|
+
to: ["L3"],
|
|
1281
|
+
description: "Decision is based on a Belief/Question"
|
|
1282
|
+
},
|
|
1283
|
+
based_on_belief: {
|
|
1284
|
+
from: ["L4"],
|
|
1285
|
+
to: ["L3"],
|
|
1286
|
+
description: "Decision is based on a Belief"
|
|
1287
|
+
},
|
|
1288
|
+
based_on_question: {
|
|
1289
|
+
from: ["L4"],
|
|
1290
|
+
to: ["L3"],
|
|
1291
|
+
description: "Decision is based on a Question"
|
|
1292
|
+
},
|
|
1293
|
+
blocked_by_contradiction: {
|
|
1294
|
+
from: ["L4"],
|
|
1295
|
+
to: ["L3"],
|
|
1296
|
+
description: "Decision is blocked by a Contradiction"
|
|
1297
|
+
},
|
|
1298
|
+
parent_of: {
|
|
1299
|
+
from: ["L3", "L4", "ontological", "organizational"],
|
|
1300
|
+
to: ["L2", "L3", "ontological", "organizational"],
|
|
1301
|
+
description: "A is the parent of B"
|
|
1302
|
+
},
|
|
1303
|
+
child_of: {
|
|
1304
|
+
from: ["L2", "L3", "ontological", "organizational"],
|
|
1305
|
+
to: ["L3", "L4", "ontological", "organizational"],
|
|
1306
|
+
description: "A is the child of B"
|
|
1307
|
+
},
|
|
1308
|
+
scoped_by: {
|
|
1309
|
+
from: ["L2", "L3"],
|
|
1310
|
+
to: ["L3", "organizational"],
|
|
1311
|
+
description: "Object is scoped by a Topic/Theme"
|
|
1312
|
+
},
|
|
1313
|
+
cites: {
|
|
1314
|
+
from: ["L2", "L3"],
|
|
1315
|
+
to: ["L1", "L2"],
|
|
1316
|
+
description: "Evidence/Belief cites a Source/Excerpt"
|
|
1317
|
+
},
|
|
1318
|
+
summarizes: {
|
|
1319
|
+
from: ["L2", "L3"],
|
|
1320
|
+
to: ["L1", "L2"],
|
|
1321
|
+
description: "Synthesis/Evidence summarizes a Source/Evidence"
|
|
1322
|
+
},
|
|
1323
|
+
same_source_as: {
|
|
1324
|
+
from: ["L1", "L2"],
|
|
1325
|
+
to: ["L1", "L2"],
|
|
1326
|
+
description: "Two nodes derive from the same Source"
|
|
1327
|
+
},
|
|
1328
|
+
migrating_from: {
|
|
1329
|
+
from: ["L2", "L3", "L4"],
|
|
1330
|
+
to: ["L1", "L2", "L3"],
|
|
1331
|
+
description: "Migration lineage: from an ancestor"
|
|
1332
|
+
},
|
|
1333
|
+
migrating_to: {
|
|
1334
|
+
from: ["L1", "L2", "L3"],
|
|
1335
|
+
to: ["L2", "L3", "L4"],
|
|
1336
|
+
description: "Migration lineage: to a successor"
|
|
1337
|
+
},
|
|
1338
|
+
about_entity: {
|
|
1339
|
+
from: ["L2", "L3"],
|
|
1340
|
+
to: ["ontological"],
|
|
1341
|
+
description: "Belief/Evidence is about an Entity"
|
|
1342
|
+
},
|
|
1343
|
+
entity_referenced_in: {
|
|
1344
|
+
from: ["ontological"],
|
|
1345
|
+
to: ["L1", "L2"],
|
|
1346
|
+
description: "Entity is referenced in a Source/Evidence"
|
|
1347
|
+
},
|
|
1348
|
+
related_to: {
|
|
1349
|
+
from: ["L2", "L3", "ontological", "organizational"],
|
|
1350
|
+
to: ["L2", "L3", "ontological", "organizational"],
|
|
1351
|
+
description: "Lateral adjacency; no confidence pressure"
|
|
1352
|
+
},
|
|
1353
|
+
blocks: {
|
|
1354
|
+
from: ["L3", "L4"],
|
|
1355
|
+
to: ["L3", "L4"],
|
|
1356
|
+
description: "A blocks B (structural gate)"
|
|
1357
|
+
}
|
|
1358
|
+
};
|
|
1359
|
+
function validateEdgeLayers(edgeType, fromLayer, toLayer) {
|
|
1360
|
+
const rules = EDGE_LAYER_RULES[edgeType];
|
|
1361
|
+
if (!rules) {
|
|
1362
|
+
console.warn(
|
|
1363
|
+
`[EdgeValidation] Unknown edge type: ${edgeType}, allowing by default`
|
|
1213
1364
|
);
|
|
1365
|
+
return { valid: true };
|
|
1366
|
+
}
|
|
1367
|
+
if (edgeType === "supersedes") {
|
|
1368
|
+
if (fromLayer !== toLayer) {
|
|
1369
|
+
return {
|
|
1370
|
+
valid: false,
|
|
1371
|
+
reason: `${edgeType} edges must be between nodes of the same layer. Got ${fromLayer} -> ${toLayer}`
|
|
1372
|
+
};
|
|
1373
|
+
}
|
|
1374
|
+
return { valid: true };
|
|
1375
|
+
}
|
|
1376
|
+
if (!rules.from.includes(fromLayer)) {
|
|
1214
1377
|
return {
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
supportCount: filteredSupporting.length,
|
|
1218
|
-
contradictCount: filteredContradicting.length
|
|
1378
|
+
valid: false,
|
|
1379
|
+
reason: `Edge type '${edgeType}' does not allow source layer ${fromLayer}. Allowed: ${rules.from.join(", ")}`
|
|
1219
1380
|
};
|
|
1220
1381
|
}
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
returns: permissiveReturn,
|
|
1227
|
-
handler: async (ctx, args) => {
|
|
1228
|
-
if (args.beliefIds.length === 0) {
|
|
1229
|
-
return [];
|
|
1230
|
-
}
|
|
1231
|
-
const beliefIdSet = new Set(args.beliefIds);
|
|
1232
|
-
const allEdges = await ctx.db.query("epistemicEdges").collect();
|
|
1233
|
-
return allEdges.filter((edge) => {
|
|
1234
|
-
const fromInCluster = beliefIdSet.has(String(edge.fromNodeId)) || edge.sourceGlobalId && beliefIdSet.has(edge.sourceGlobalId);
|
|
1235
|
-
const toInCluster = beliefIdSet.has(String(edge.toNodeId)) || edge.targetGlobalId && beliefIdSet.has(edge.targetGlobalId);
|
|
1236
|
-
return fromInCluster && toInCluster;
|
|
1237
|
-
});
|
|
1382
|
+
if (!rules.to.includes(toLayer)) {
|
|
1383
|
+
return {
|
|
1384
|
+
valid: false,
|
|
1385
|
+
reason: `Edge type '${edgeType}' does not allow target layer ${toLayer}. Allowed: ${rules.to.join(", ")}`
|
|
1386
|
+
};
|
|
1238
1387
|
}
|
|
1239
|
-
|
|
1388
|
+
if (!isValidLayerConnection(fromLayer, toLayer)) {
|
|
1389
|
+
return {
|
|
1390
|
+
valid: false,
|
|
1391
|
+
reason: `Layer transition ${fromLayer} -> ${toLayer} is not permitted`
|
|
1392
|
+
};
|
|
1393
|
+
}
|
|
1394
|
+
return { valid: true };
|
|
1395
|
+
}
|
|
1240
1396
|
|
|
1241
1397
|
// src/topicProjectOverlay.ts
|
|
1242
1398
|
var LEGACY_SCOPE_FIELD2 = "graphScopeProjectId";
|
|
@@ -1256,6 +1412,10 @@ function readStringArray(value) {
|
|
|
1256
1412
|
function readMetadata(topic) {
|
|
1257
1413
|
return topic.metadata && typeof topic.metadata === "object" ? topic.metadata : {};
|
|
1258
1414
|
}
|
|
1415
|
+
function omitMetadataKey(metadata, key) {
|
|
1416
|
+
const { [key]: _omitted, ...rest } = metadata;
|
|
1417
|
+
return rest;
|
|
1418
|
+
}
|
|
1259
1419
|
function readLegacyProjectId(value) {
|
|
1260
1420
|
if (!value) {
|
|
1261
1421
|
return;
|
|
@@ -1336,9 +1496,12 @@ async function resolveTopicDoc(ctx, scopeId) {
|
|
|
1336
1496
|
);
|
|
1337
1497
|
}
|
|
1338
1498
|
try {
|
|
1339
|
-
const topic = await ctx.runQuery(
|
|
1340
|
-
|
|
1341
|
-
|
|
1499
|
+
const topic = await ctx.runQuery(
|
|
1500
|
+
api.topics.getByLegacyScopeId,
|
|
1501
|
+
{
|
|
1502
|
+
projectId: String(scopeId)
|
|
1503
|
+
}
|
|
1504
|
+
);
|
|
1342
1505
|
if (topic?.name !== void 0 && topic?.type !== void 0) {
|
|
1343
1506
|
return topic;
|
|
1344
1507
|
}
|
|
@@ -1358,8 +1521,18 @@ function materializeTopicProjectOverlay(topic, idMode = "legacy") {
|
|
|
1358
1521
|
const outwardId = idMode === "topic" ? topicId : storageProjectId;
|
|
1359
1522
|
const visibility = coerceVisibility(topic.visibility) || coerceVisibility(metadata.visibility) || "private";
|
|
1360
1523
|
const status = coerceStatus(topic.status) || coerceStatus(metadata.status) || "active";
|
|
1361
|
-
|
|
1362
|
-
|
|
1524
|
+
let createdAt = 0;
|
|
1525
|
+
if (typeof topic.createdAt === "number") {
|
|
1526
|
+
createdAt = topic.createdAt;
|
|
1527
|
+
} else if (typeof topic._creationTime === "number") {
|
|
1528
|
+
createdAt = topic._creationTime;
|
|
1529
|
+
}
|
|
1530
|
+
let updatedAt = createdAt;
|
|
1531
|
+
if (typeof topic.updatedAt === "number") {
|
|
1532
|
+
updatedAt = topic.updatedAt;
|
|
1533
|
+
} else if (typeof metadata.updatedAt === "number") {
|
|
1534
|
+
updatedAt = metadata.updatedAt;
|
|
1535
|
+
}
|
|
1363
1536
|
return {
|
|
1364
1537
|
...metadata,
|
|
1365
1538
|
_id: outwardId,
|
|
@@ -1428,90 +1601,113 @@ async function patchTopicProjectOverlay(ctx, scopeId, value) {
|
|
|
1428
1601
|
if (!topic) {
|
|
1429
1602
|
return null;
|
|
1430
1603
|
}
|
|
1431
|
-
const
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
|
|
1604
|
+
const plan = buildTopicProjectOverlayPatchPlan(topic, value);
|
|
1605
|
+
await applyTopicProjectOverlayPatch(ctx, topic, plan);
|
|
1606
|
+
return materializeTopicProjectOverlay({
|
|
1607
|
+
...topic,
|
|
1608
|
+
...plan.patch,
|
|
1609
|
+
metadata: plan.nextMetadata
|
|
1610
|
+
});
|
|
1611
|
+
}
|
|
1612
|
+
function buildTopicProjectOverlayPatchPlan(topic, value) {
|
|
1613
|
+
const plan = {
|
|
1614
|
+
nextMetadata: { ...readMetadata(topic) },
|
|
1615
|
+
patch: {},
|
|
1616
|
+
topicUpdateArgs: {
|
|
1617
|
+
id: String(topic._id)
|
|
1618
|
+
}
|
|
1435
1619
|
};
|
|
1436
1620
|
for (const [key, rawValue] of Object.entries(value)) {
|
|
1437
|
-
|
|
1438
|
-
|
|
1439
|
-
|
|
1440
|
-
|
|
1441
|
-
|
|
1442
|
-
|
|
1443
|
-
|
|
1444
|
-
|
|
1445
|
-
|
|
1446
|
-
|
|
1447
|
-
|
|
1448
|
-
|
|
1449
|
-
|
|
1450
|
-
|
|
1451
|
-
|
|
1452
|
-
|
|
1453
|
-
|
|
1454
|
-
|
|
1455
|
-
|
|
1456
|
-
|
|
1457
|
-
|
|
1458
|
-
|
|
1459
|
-
|
|
1460
|
-
|
|
1461
|
-
|
|
1462
|
-
|
|
1463
|
-
|
|
1464
|
-
|
|
1465
|
-
|
|
1466
|
-
|
|
1467
|
-
|
|
1468
|
-
|
|
1469
|
-
|
|
1470
|
-
|
|
1471
|
-
|
|
1472
|
-
|
|
1473
|
-
|
|
1474
|
-
|
|
1475
|
-
|
|
1476
|
-
|
|
1477
|
-
|
|
1478
|
-
|
|
1479
|
-
|
|
1480
|
-
|
|
1481
|
-
|
|
1482
|
-
|
|
1483
|
-
|
|
1484
|
-
|
|
1485
|
-
|
|
1486
|
-
|
|
1487
|
-
|
|
1488
|
-
|
|
1489
|
-
|
|
1621
|
+
applyTopicProjectOverlayPatchEntry(plan, key, rawValue);
|
|
1622
|
+
}
|
|
1623
|
+
plan.patch.updatedAt = Date.now();
|
|
1624
|
+
plan.patch.metadata = plan.nextMetadata;
|
|
1625
|
+
plan.topicUpdateArgs.metadata = plan.nextMetadata;
|
|
1626
|
+
return plan;
|
|
1627
|
+
}
|
|
1628
|
+
function applyTopicProjectOverlayPatchEntry(plan, key, rawValue) {
|
|
1629
|
+
switch (key) {
|
|
1630
|
+
case "_id":
|
|
1631
|
+
case "projectId":
|
|
1632
|
+
case "topicId":
|
|
1633
|
+
case "legacyProjectId":
|
|
1634
|
+
case "storageProjectId":
|
|
1635
|
+
case "updatedAt":
|
|
1636
|
+
case "createdAt":
|
|
1637
|
+
return;
|
|
1638
|
+
case "name":
|
|
1639
|
+
case "description":
|
|
1640
|
+
plan.patch[key] = rawValue;
|
|
1641
|
+
plan.topicUpdateArgs[key] = rawValue;
|
|
1642
|
+
return;
|
|
1643
|
+
case "tenantId":
|
|
1644
|
+
case "workspaceId":
|
|
1645
|
+
case "ownerId":
|
|
1646
|
+
throw new Error(
|
|
1647
|
+
`patchTopicProjectOverlay cannot mutate ${key} via component-owned topics`
|
|
1648
|
+
);
|
|
1649
|
+
case "status":
|
|
1650
|
+
applyTopicStatusPatch(plan, rawValue);
|
|
1651
|
+
return;
|
|
1652
|
+
case "visibility":
|
|
1653
|
+
applyTopicVisibilityPatch(plan, rawValue);
|
|
1654
|
+
return;
|
|
1655
|
+
case "type":
|
|
1656
|
+
applyTopicProjectTypePatch(plan, rawValue);
|
|
1657
|
+
return;
|
|
1658
|
+
default:
|
|
1659
|
+
applyTopicMetadataPatch(plan, key, rawValue);
|
|
1660
|
+
}
|
|
1661
|
+
}
|
|
1662
|
+
function applyTopicStatusPatch(plan, rawValue) {
|
|
1663
|
+
const status = coerceStatus(rawValue);
|
|
1664
|
+
if (status) {
|
|
1665
|
+
plan.patch.status = status;
|
|
1666
|
+
plan.topicUpdateArgs.status = status;
|
|
1667
|
+
}
|
|
1668
|
+
}
|
|
1669
|
+
function applyTopicVisibilityPatch(plan, rawValue) {
|
|
1670
|
+
const visibility = coerceVisibility(rawValue);
|
|
1671
|
+
if (visibility) {
|
|
1672
|
+
plan.patch.visibility = visibility;
|
|
1673
|
+
plan.topicUpdateArgs.visibility = visibility;
|
|
1674
|
+
}
|
|
1675
|
+
}
|
|
1676
|
+
function applyTopicProjectTypePatch(plan, rawValue) {
|
|
1677
|
+
const projectType = readNonEmptyString(rawValue);
|
|
1678
|
+
if (projectType) {
|
|
1679
|
+
plan.nextMetadata.projectType = projectType;
|
|
1680
|
+
return;
|
|
1490
1681
|
}
|
|
1491
|
-
|
|
1492
|
-
|
|
1493
|
-
|
|
1682
|
+
plan.nextMetadata = omitMetadataKey(plan.nextMetadata, "projectType");
|
|
1683
|
+
}
|
|
1684
|
+
function applyTopicMetadataPatch(plan, key, rawValue) {
|
|
1685
|
+
if (rawValue === void 0) {
|
|
1686
|
+
plan.nextMetadata = omitMetadataKey(plan.nextMetadata, key);
|
|
1687
|
+
return;
|
|
1688
|
+
}
|
|
1689
|
+
plan.nextMetadata[key] = rawValue;
|
|
1690
|
+
}
|
|
1691
|
+
async function applyTopicProjectOverlayPatch(ctx, topic, plan) {
|
|
1494
1692
|
if (typeof ctx.runMutation === "function") {
|
|
1495
1693
|
try {
|
|
1496
|
-
await ctx.runMutation(api.topics.update, topicUpdateArgs);
|
|
1694
|
+
await ctx.runMutation(api.topics.update, plan.topicUpdateArgs);
|
|
1497
1695
|
} catch (error) {
|
|
1498
|
-
if (!
|
|
1696
|
+
if (!canPatchTopicViaLocalDb(ctx, error)) {
|
|
1499
1697
|
throw error;
|
|
1500
1698
|
}
|
|
1501
|
-
await ctx.db.patch(
|
|
1699
|
+
await ctx.db.patch(topic._id, plan.patch);
|
|
1502
1700
|
}
|
|
1503
1701
|
} else if (ctx?.db && typeof ctx.db.patch === "function") {
|
|
1504
|
-
await ctx.db.patch(
|
|
1702
|
+
await ctx.db.patch(topic._id, plan.patch);
|
|
1505
1703
|
} else {
|
|
1506
1704
|
throw new Error(
|
|
1507
1705
|
"Cannot patch topic without component adapter (ctx.runMutation unavailable)"
|
|
1508
1706
|
);
|
|
1509
1707
|
}
|
|
1510
|
-
|
|
1511
|
-
|
|
1512
|
-
|
|
1513
|
-
metadata: nextMetadata
|
|
1514
|
-
});
|
|
1708
|
+
}
|
|
1709
|
+
function canPatchTopicViaLocalDb(ctx, error) {
|
|
1710
|
+
return isMissingLucernChildComponentError(error) && Boolean(ctx?.db) && typeof ctx.db?.patch === "function";
|
|
1515
1711
|
}
|
|
1516
1712
|
|
|
1517
1713
|
// src/resolvers.ts
|
|
@@ -1539,7 +1735,7 @@ async function patchProjectWithTolerance(ctx, projectId, value) {
|
|
|
1539
1735
|
try {
|
|
1540
1736
|
await patchTopicProjectOverlay(ctx, projectId, value);
|
|
1541
1737
|
} catch (error) {
|
|
1542
|
-
if (!isAdvisoryTopicPatch(value)
|
|
1738
|
+
if (!(isAdvisoryTopicPatch(value) && isMissingLucernChildComponentError2(error))) {
|
|
1543
1739
|
throw error;
|
|
1544
1740
|
}
|
|
1545
1741
|
console.warn(
|
|
@@ -1572,8 +1768,140 @@ function resolveGraphPrimitivesAppResolvers(_ctx) {
|
|
|
1572
1768
|
...resolverOverrides
|
|
1573
1769
|
};
|
|
1574
1770
|
}
|
|
1575
|
-
|
|
1576
|
-
// src/epistemicEdges.mutations.ts
|
|
1771
|
+
|
|
1772
|
+
// src/epistemicEdges.mutations.ts
|
|
1773
|
+
var EPISTEMIC_LAYERS2 = /* @__PURE__ */ new Set([
|
|
1774
|
+
"L4",
|
|
1775
|
+
"L3",
|
|
1776
|
+
"L2",
|
|
1777
|
+
"L1",
|
|
1778
|
+
"ontological",
|
|
1779
|
+
"organizational"
|
|
1780
|
+
]);
|
|
1781
|
+
function readOptionalString3(value) {
|
|
1782
|
+
return typeof value === "string" && value.trim().length > 0 ? value : void 0;
|
|
1783
|
+
}
|
|
1784
|
+
function readOptionalNumber(value) {
|
|
1785
|
+
return typeof value === "number" && Number.isFinite(value) ? value : void 0;
|
|
1786
|
+
}
|
|
1787
|
+
function readConvexId3(value) {
|
|
1788
|
+
const normalized = readOptionalString3(value);
|
|
1789
|
+
return normalized;
|
|
1790
|
+
}
|
|
1791
|
+
function readRecord3(value) {
|
|
1792
|
+
return value && typeof value === "object" && !Array.isArray(value) ? value : null;
|
|
1793
|
+
}
|
|
1794
|
+
function readEpistemicLayer(value) {
|
|
1795
|
+
const layer = readOptionalString3(value);
|
|
1796
|
+
return layer && EPISTEMIC_LAYERS2.has(layer) ? layer : void 0;
|
|
1797
|
+
}
|
|
1798
|
+
function readEdgeNodeRow(value) {
|
|
1799
|
+
const record = readRecord3(value);
|
|
1800
|
+
if (!record) {
|
|
1801
|
+
return null;
|
|
1802
|
+
}
|
|
1803
|
+
const id = readConvexId3(record._id);
|
|
1804
|
+
const globalId = readOptionalString3(record.globalId);
|
|
1805
|
+
const nodeType = readOptionalString3(record.nodeType);
|
|
1806
|
+
if (!(id && globalId && nodeType)) {
|
|
1807
|
+
return null;
|
|
1808
|
+
}
|
|
1809
|
+
const node = { _id: id, globalId, nodeType };
|
|
1810
|
+
const epistemicLayer = readEpistemicLayer(record.epistemicLayer);
|
|
1811
|
+
if (epistemicLayer !== void 0) {
|
|
1812
|
+
node.epistemicLayer = epistemicLayer;
|
|
1813
|
+
}
|
|
1814
|
+
const projectId = readOptionalString3(record.projectId);
|
|
1815
|
+
if (projectId !== void 0) {
|
|
1816
|
+
node.projectId = projectId;
|
|
1817
|
+
}
|
|
1818
|
+
return node;
|
|
1819
|
+
}
|
|
1820
|
+
function requireEdgeNodeRow(value, context) {
|
|
1821
|
+
const node = readEdgeNodeRow(value);
|
|
1822
|
+
if (!node) {
|
|
1823
|
+
throw new Error(`${context} requires a canonical epistemic node row.`);
|
|
1824
|
+
}
|
|
1825
|
+
return node;
|
|
1826
|
+
}
|
|
1827
|
+
function readEdgeRow(value) {
|
|
1828
|
+
const record = readRecord3(value);
|
|
1829
|
+
if (!record) {
|
|
1830
|
+
return null;
|
|
1831
|
+
}
|
|
1832
|
+
const id = readConvexId3(record._id);
|
|
1833
|
+
const globalId = readOptionalString3(record.globalId);
|
|
1834
|
+
const edgeType = readOptionalString3(record.edgeType);
|
|
1835
|
+
if (!(id && globalId && edgeType)) {
|
|
1836
|
+
return null;
|
|
1837
|
+
}
|
|
1838
|
+
const edge = { _id: id, globalId, edgeType };
|
|
1839
|
+
const createdBy = readOptionalString3(record.createdBy);
|
|
1840
|
+
if (createdBy !== void 0) {
|
|
1841
|
+
edge.createdBy = createdBy;
|
|
1842
|
+
}
|
|
1843
|
+
const fromNodeId = readOptionalString3(record.fromNodeId);
|
|
1844
|
+
if (fromNodeId !== void 0) {
|
|
1845
|
+
edge.fromNodeId = fromNodeId;
|
|
1846
|
+
}
|
|
1847
|
+
const projectId = readOptionalString3(record.projectId);
|
|
1848
|
+
if (projectId !== void 0) {
|
|
1849
|
+
edge.projectId = projectId;
|
|
1850
|
+
}
|
|
1851
|
+
const toNodeId = readOptionalString3(record.toNodeId);
|
|
1852
|
+
if (toNodeId !== void 0) {
|
|
1853
|
+
edge.toNodeId = toNodeId;
|
|
1854
|
+
}
|
|
1855
|
+
const confidence = readOptionalNumber(record.confidence);
|
|
1856
|
+
if (confidence !== void 0) {
|
|
1857
|
+
edge.confidence = confidence;
|
|
1858
|
+
}
|
|
1859
|
+
const weight = readOptionalNumber(record.weight);
|
|
1860
|
+
if (weight !== void 0) {
|
|
1861
|
+
edge.weight = weight;
|
|
1862
|
+
}
|
|
1863
|
+
if ("context" in record) {
|
|
1864
|
+
edge.context = record.context;
|
|
1865
|
+
}
|
|
1866
|
+
if ("description" in record) {
|
|
1867
|
+
edge.description = record.description;
|
|
1868
|
+
}
|
|
1869
|
+
return edge;
|
|
1870
|
+
}
|
|
1871
|
+
function readEdgeRows(values) {
|
|
1872
|
+
return values.flatMap((value) => {
|
|
1873
|
+
const edge = readEdgeRow(value);
|
|
1874
|
+
return edge ? [edge] : [];
|
|
1875
|
+
});
|
|
1876
|
+
}
|
|
1877
|
+
function nodeEndpointRefs(nodeId, node) {
|
|
1878
|
+
const refs = /* @__PURE__ */ new Set([String(nodeId)]);
|
|
1879
|
+
if (node?.globalId) {
|
|
1880
|
+
refs.add(node.globalId);
|
|
1881
|
+
}
|
|
1882
|
+
return [...refs];
|
|
1883
|
+
}
|
|
1884
|
+
async function collectEdgesBetween(ctx, fromRefs, toRefs) {
|
|
1885
|
+
const seen = /* @__PURE__ */ new Set();
|
|
1886
|
+
const edges = [];
|
|
1887
|
+
for (const fromRef of fromRefs) {
|
|
1888
|
+
for (const toRef of toRefs) {
|
|
1889
|
+
const rows = await ctx.db.query("epistemicEdges").withIndex(
|
|
1890
|
+
"by_from_to",
|
|
1891
|
+
(q) => q.eq("fromNodeId", fromRef).eq("toNodeId", toRef)
|
|
1892
|
+
).collect();
|
|
1893
|
+
for (const edge of readEdgeRows(rows)) {
|
|
1894
|
+
const key = String(edge._id);
|
|
1895
|
+
if (seen.has(key)) {
|
|
1896
|
+
continue;
|
|
1897
|
+
}
|
|
1898
|
+
seen.add(key);
|
|
1899
|
+
edges.push(edge);
|
|
1900
|
+
}
|
|
1901
|
+
}
|
|
1902
|
+
}
|
|
1903
|
+
return edges;
|
|
1904
|
+
}
|
|
1577
1905
|
var create = mutation({
|
|
1578
1906
|
args: {
|
|
1579
1907
|
globalId: v.string(),
|
|
@@ -1590,11 +1918,14 @@ var create = mutation({
|
|
|
1590
1918
|
},
|
|
1591
1919
|
returns: permissiveReturn,
|
|
1592
1920
|
handler: async (ctx, args) => {
|
|
1593
|
-
const fromNode =
|
|
1594
|
-
|
|
1595
|
-
|
|
1596
|
-
|
|
1597
|
-
|
|
1921
|
+
const fromNode = requireEdgeNodeRow(
|
|
1922
|
+
await ctx.db.get(args.fromNodeId),
|
|
1923
|
+
"Create edge source"
|
|
1924
|
+
);
|
|
1925
|
+
const toNode = requireEdgeNodeRow(
|
|
1926
|
+
await ctx.db.get(args.toNodeId),
|
|
1927
|
+
"Create edge target"
|
|
1928
|
+
);
|
|
1598
1929
|
const resolvedScope = args.topicId || args.projectId ? await resolveTopicProjectScope(ctx, {
|
|
1599
1930
|
topicId: args.topicId,
|
|
1600
1931
|
projectId: args.projectId
|
|
@@ -1603,8 +1934,8 @@ var create = mutation({
|
|
|
1603
1934
|
if (resolvedProjectId) {
|
|
1604
1935
|
await requireScopeWriteAccess(ctx, resolvedProjectId, args.createdBy);
|
|
1605
1936
|
}
|
|
1606
|
-
const fromLayer = fromNode.epistemicLayer
|
|
1607
|
-
const toLayer = toNode.epistemicLayer
|
|
1937
|
+
const fromLayer = fromNode.epistemicLayer ?? getNodeLayer(fromNode.nodeType);
|
|
1938
|
+
const toLayer = toNode.epistemicLayer ?? getNodeLayer(toNode.nodeType);
|
|
1608
1939
|
if (!args.skipLayerValidation) {
|
|
1609
1940
|
const validation = validateEdgeLayers(args.edgeType, fromLayer, toLayer);
|
|
1610
1941
|
if (!validation.valid) {
|
|
@@ -1618,10 +1949,11 @@ var create = mutation({
|
|
|
1618
1949
|
`FORBIDDEN: Edge type '${args.edgeType}' has been removed from StackOS schema.`
|
|
1619
1950
|
);
|
|
1620
1951
|
}
|
|
1621
|
-
const existing = await
|
|
1622
|
-
|
|
1623
|
-
(
|
|
1624
|
-
|
|
1952
|
+
const existing = await collectEdgesBetween(
|
|
1953
|
+
ctx,
|
|
1954
|
+
nodeEndpointRefs(args.fromNodeId, fromNode),
|
|
1955
|
+
nodeEndpointRefs(args.toNodeId, toNode)
|
|
1956
|
+
);
|
|
1625
1957
|
const duplicateEdge = existing.find((e) => e.edgeType === args.edgeType);
|
|
1626
1958
|
if (duplicateEdge) {
|
|
1627
1959
|
return { edgeId: duplicateEdge._id, isDuplicate: true };
|
|
@@ -1698,7 +2030,7 @@ var update = mutation({
|
|
|
1698
2030
|
returns: permissiveReturn,
|
|
1699
2031
|
handler: async (ctx, args) => {
|
|
1700
2032
|
const { edgeId, userId, ...updates } = args;
|
|
1701
|
-
const edge = await ctx.db.get(edgeId);
|
|
2033
|
+
const edge = readEdgeRow(await ctx.db.get(edgeId));
|
|
1702
2034
|
if (!edge) {
|
|
1703
2035
|
throw new Error("Edge not found");
|
|
1704
2036
|
}
|
|
@@ -1739,7 +2071,7 @@ var remove = mutation({
|
|
|
1739
2071
|
},
|
|
1740
2072
|
returns: permissiveReturn,
|
|
1741
2073
|
handler: async (ctx, args) => {
|
|
1742
|
-
const edge = await ctx.db.get(args.edgeId);
|
|
2074
|
+
const edge = readEdgeRow(await ctx.db.get(args.edgeId));
|
|
1743
2075
|
if (!edge) {
|
|
1744
2076
|
return buildEdgeNotFoundResult();
|
|
1745
2077
|
}
|
|
@@ -1785,10 +2117,13 @@ var removeBetween = mutation({
|
|
|
1785
2117
|
},
|
|
1786
2118
|
returns: permissiveReturn,
|
|
1787
2119
|
handler: async (ctx, args) => {
|
|
1788
|
-
const
|
|
1789
|
-
|
|
1790
|
-
|
|
1791
|
-
|
|
2120
|
+
const fromNode = readEdgeNodeRow(await ctx.db.get(args.fromNodeId));
|
|
2121
|
+
const toNode = readEdgeNodeRow(await ctx.db.get(args.toNodeId));
|
|
2122
|
+
const edges = await collectEdgesBetween(
|
|
2123
|
+
ctx,
|
|
2124
|
+
nodeEndpointRefs(args.fromNodeId, fromNode),
|
|
2125
|
+
nodeEndpointRefs(args.toNodeId, toNode)
|
|
2126
|
+
);
|
|
1792
2127
|
let deleted = 0;
|
|
1793
2128
|
for (const edge of edges) {
|
|
1794
2129
|
if (!args.edgeType || edge.edgeType === args.edgeType) {
|
|
@@ -1825,19 +2160,23 @@ var batchCreate = mutation({
|
|
|
1825
2160
|
const results = [];
|
|
1826
2161
|
const errors = [];
|
|
1827
2162
|
for (const edge of args.edges) {
|
|
1828
|
-
const fromNode = await ctx.db.get(edge.fromNodeId);
|
|
1829
|
-
const toNode = await ctx.db.get(edge.toNodeId);
|
|
1830
|
-
if (!fromNode
|
|
2163
|
+
const fromNode = readEdgeNodeRow(await ctx.db.get(edge.fromNodeId));
|
|
2164
|
+
const toNode = readEdgeNodeRow(await ctx.db.get(edge.toNodeId));
|
|
2165
|
+
if (!(fromNode && toNode)) {
|
|
1831
2166
|
errors.push({
|
|
1832
2167
|
globalId: edge.globalId,
|
|
1833
2168
|
error: "One or both nodes not found"
|
|
1834
2169
|
});
|
|
1835
2170
|
continue;
|
|
1836
2171
|
}
|
|
1837
|
-
const fromLayer = fromNode.epistemicLayer
|
|
1838
|
-
const toLayer = toNode.epistemicLayer
|
|
2172
|
+
const fromLayer = fromNode.epistemicLayer ?? getNodeLayer(fromNode.nodeType);
|
|
2173
|
+
const toLayer = toNode.epistemicLayer ?? getNodeLayer(toNode.nodeType);
|
|
1839
2174
|
if (!args.skipLayerValidation) {
|
|
1840
|
-
const validation = validateEdgeLayers(
|
|
2175
|
+
const validation = validateEdgeLayers(
|
|
2176
|
+
edge.edgeType,
|
|
2177
|
+
fromLayer,
|
|
2178
|
+
toLayer
|
|
2179
|
+
);
|
|
1841
2180
|
if (!validation.valid) {
|
|
1842
2181
|
errors.push({
|
|
1843
2182
|
globalId: edge.globalId,
|
|
@@ -1872,9 +2211,7 @@ var batchCreate = mutation({
|
|
|
1872
2211
|
results.push({ globalId: edge.globalId, edgeGlobalId: edge.globalId });
|
|
1873
2212
|
}
|
|
1874
2213
|
const projectIds = new Set(
|
|
1875
|
-
args.edges.flatMap(
|
|
1876
|
-
(edge) => typeof edge.projectId === "string" ? [edge.projectId] : []
|
|
1877
|
-
)
|
|
2214
|
+
args.edges.flatMap((edge) => edge.projectId ? [edge.projectId] : [])
|
|
1878
2215
|
);
|
|
1879
2216
|
for (const pid of projectIds) {
|
|
1880
2217
|
if (pid) {
|
|
@@ -1895,8 +2232,11 @@ var cleanupDeprecatedEdges = mutation({
|
|
|
1895
2232
|
handler: async (ctx, args) => {
|
|
1896
2233
|
const DEPRECATED_TYPES = ["contradicts"];
|
|
1897
2234
|
const scopeId = args.topicId || args.projectId;
|
|
1898
|
-
const allEdges = scopeId ? await ctx.db.query("epistemicEdges").withIndex(
|
|
1899
|
-
|
|
2235
|
+
const allEdges = scopeId ? await ctx.db.query("epistemicEdges").withIndex(
|
|
2236
|
+
"by_topic",
|
|
2237
|
+
(q) => q.eq("topicId", scopeId)
|
|
2238
|
+
).collect() : [];
|
|
2239
|
+
const deprecatedEdges = readEdgeRows(allEdges).filter(
|
|
1900
2240
|
(edge) => DEPRECATED_TYPES.includes(edge.edgeType)
|
|
1901
2241
|
);
|
|
1902
2242
|
if (args.dryRun) {
|
|
@@ -1939,15 +2279,15 @@ var deleteEdges = mutation({
|
|
|
1939
2279
|
const deleted = [];
|
|
1940
2280
|
const notFound = [];
|
|
1941
2281
|
for (const edgeId of args.edgeIds) {
|
|
1942
|
-
const edge = await ctx.db.get(edgeId);
|
|
2282
|
+
const edge = readEdgeRow(await ctx.db.get(edgeId));
|
|
1943
2283
|
if (edge) {
|
|
1944
2284
|
await ctx.scheduler.runAfter(0, internal.neo4jEdgeAPI.deleteEdge, {
|
|
1945
2285
|
globalId: edge.globalId
|
|
1946
2286
|
});
|
|
1947
2287
|
await ctx.db.delete(edgeId);
|
|
1948
|
-
deleted.push(edgeId);
|
|
2288
|
+
deleted.push(String(edgeId));
|
|
1949
2289
|
} else {
|
|
1950
|
-
notFound.push(edgeId);
|
|
2290
|
+
notFound.push(String(edgeId));
|
|
1951
2291
|
}
|
|
1952
2292
|
}
|
|
1953
2293
|
return {
|
|
@@ -1957,219 +2297,643 @@ var deleteEdges = mutation({
|
|
|
1957
2297
|
};
|
|
1958
2298
|
}
|
|
1959
2299
|
});
|
|
1960
|
-
|
|
1961
|
-
|
|
1962
|
-
|
|
1963
|
-
|
|
1964
|
-
|
|
1965
|
-
|
|
2300
|
+
var DERIVED_FROM_EDGE_TYPE = "derived_from";
|
|
2301
|
+
var EPISTEMIC_LAYERS3 = /* @__PURE__ */ new Set([
|
|
2302
|
+
"L4",
|
|
2303
|
+
"L3",
|
|
2304
|
+
"L2",
|
|
2305
|
+
"L1",
|
|
2306
|
+
"ontological",
|
|
2307
|
+
"organizational"
|
|
2308
|
+
]);
|
|
2309
|
+
function readOptionalString4(value) {
|
|
2310
|
+
return typeof value === "string" && value.trim().length > 0 ? value : void 0;
|
|
2311
|
+
}
|
|
2312
|
+
function readOptionalNumber2(value) {
|
|
2313
|
+
return typeof value === "number" && Number.isFinite(value) ? value : void 0;
|
|
2314
|
+
}
|
|
2315
|
+
function readConvexId4(value) {
|
|
2316
|
+
const normalized = readOptionalString4(value);
|
|
2317
|
+
return normalized;
|
|
2318
|
+
}
|
|
2319
|
+
function readRecord4(value) {
|
|
2320
|
+
return value && typeof value === "object" && !Array.isArray(value) ? value : null;
|
|
2321
|
+
}
|
|
2322
|
+
function readEpistemicLayer2(value) {
|
|
2323
|
+
const layer = readOptionalString4(value);
|
|
2324
|
+
return layer && EPISTEMIC_LAYERS3.has(layer) ? layer : void 0;
|
|
2325
|
+
}
|
|
2326
|
+
function readEdgeNodeRow2(value) {
|
|
2327
|
+
const record = readRecord4(value);
|
|
2328
|
+
if (!record) {
|
|
2329
|
+
return null;
|
|
2330
|
+
}
|
|
2331
|
+
const id = readConvexId4(record._id);
|
|
2332
|
+
const nodeType = readOptionalString4(record.nodeType);
|
|
2333
|
+
if (!(id && nodeType)) {
|
|
2334
|
+
return null;
|
|
2335
|
+
}
|
|
2336
|
+
const node = { ...record, _id: id, nodeType };
|
|
2337
|
+
const globalId = readOptionalString4(record.globalId);
|
|
2338
|
+
if (globalId !== void 0) {
|
|
2339
|
+
node.globalId = globalId;
|
|
2340
|
+
}
|
|
2341
|
+
const epistemicLayer = readEpistemicLayer2(record.epistemicLayer);
|
|
2342
|
+
if (epistemicLayer !== void 0) {
|
|
2343
|
+
node.epistemicLayer = epistemicLayer;
|
|
2344
|
+
}
|
|
2345
|
+
return node;
|
|
2346
|
+
}
|
|
2347
|
+
function readEdgeRow2(value) {
|
|
2348
|
+
const record = readRecord4(value);
|
|
2349
|
+
if (!record) {
|
|
2350
|
+
return null;
|
|
2351
|
+
}
|
|
2352
|
+
const id = readConvexId4(record._id);
|
|
2353
|
+
const edgeType = readOptionalString4(record.edgeType);
|
|
2354
|
+
if (!(id && edgeType)) {
|
|
2355
|
+
return null;
|
|
2356
|
+
}
|
|
2357
|
+
const edge = { ...record, _id: id, edgeType };
|
|
2358
|
+
const globalId = readOptionalString4(record.globalId);
|
|
2359
|
+
if (globalId !== void 0) {
|
|
2360
|
+
edge.globalId = globalId;
|
|
2361
|
+
}
|
|
2362
|
+
const fromNodeId = readOptionalString4(record.fromNodeId);
|
|
2363
|
+
if (fromNodeId !== void 0) {
|
|
2364
|
+
edge.fromNodeId = fromNodeId;
|
|
2365
|
+
}
|
|
2366
|
+
const toNodeId = readOptionalString4(record.toNodeId);
|
|
2367
|
+
if (toNodeId !== void 0) {
|
|
2368
|
+
edge.toNodeId = toNodeId;
|
|
2369
|
+
}
|
|
2370
|
+
const sourceGlobalId = readOptionalString4(record.sourceGlobalId);
|
|
2371
|
+
if (sourceGlobalId !== void 0) {
|
|
2372
|
+
edge.sourceGlobalId = sourceGlobalId;
|
|
2373
|
+
}
|
|
2374
|
+
const targetGlobalId = readOptionalString4(record.targetGlobalId);
|
|
2375
|
+
if (targetGlobalId !== void 0) {
|
|
2376
|
+
edge.targetGlobalId = targetGlobalId;
|
|
2377
|
+
}
|
|
2378
|
+
const fromGlobalId = readOptionalString4(record.fromGlobalId);
|
|
2379
|
+
if (fromGlobalId !== void 0) {
|
|
2380
|
+
edge.fromGlobalId = fromGlobalId;
|
|
2381
|
+
}
|
|
2382
|
+
const toGlobalId = readOptionalString4(record.toGlobalId);
|
|
2383
|
+
if (toGlobalId !== void 0) {
|
|
2384
|
+
edge.toGlobalId = toGlobalId;
|
|
2385
|
+
}
|
|
2386
|
+
const toLayer = readEpistemicLayer2(record.toLayer);
|
|
2387
|
+
if (toLayer !== void 0) {
|
|
2388
|
+
edge.toLayer = toLayer;
|
|
2389
|
+
}
|
|
2390
|
+
const weight = readOptionalNumber2(record.weight);
|
|
2391
|
+
if (weight !== void 0) {
|
|
2392
|
+
edge.weight = weight;
|
|
2393
|
+
}
|
|
2394
|
+
return edge;
|
|
2395
|
+
}
|
|
2396
|
+
function readEdgeRows2(values) {
|
|
2397
|
+
return values.flatMap((value) => {
|
|
2398
|
+
const edge = readEdgeRow2(value);
|
|
2399
|
+
return edge ? [edge] : [];
|
|
2400
|
+
});
|
|
2401
|
+
}
|
|
2402
|
+
function dedupeEdges(edges) {
|
|
2403
|
+
const seen = /* @__PURE__ */ new Set();
|
|
2404
|
+
const deduped = [];
|
|
2405
|
+
for (const edge of edges) {
|
|
2406
|
+
const key = String(edge._id);
|
|
2407
|
+
if (seen.has(key)) {
|
|
2408
|
+
continue;
|
|
2409
|
+
}
|
|
2410
|
+
seen.add(key);
|
|
2411
|
+
deduped.push(edge);
|
|
2412
|
+
}
|
|
2413
|
+
return deduped;
|
|
2414
|
+
}
|
|
2415
|
+
function filterEdgesByType(edges, edgeType) {
|
|
2416
|
+
return edgeType ? edges.filter((edge) => edge.edgeType === edgeType) : [...edges];
|
|
2417
|
+
}
|
|
2418
|
+
function nodeEndpointRefs2(node) {
|
|
2419
|
+
const refs = /* @__PURE__ */ new Set([String(node._id)]);
|
|
2420
|
+
if (node.globalId) {
|
|
2421
|
+
refs.add(node.globalId);
|
|
2422
|
+
}
|
|
2423
|
+
return [...refs];
|
|
2424
|
+
}
|
|
2425
|
+
function edgeSourceRefs(edge) {
|
|
2426
|
+
return [edge.fromNodeId, edge.sourceGlobalId, edge.fromGlobalId].flatMap(
|
|
2427
|
+
(value) => value ? [value] : []
|
|
2428
|
+
);
|
|
2429
|
+
}
|
|
2430
|
+
function edgeTargetRefs(edge) {
|
|
2431
|
+
return [edge.toNodeId, edge.targetGlobalId, edge.toGlobalId].flatMap(
|
|
2432
|
+
(value) => value ? [value] : []
|
|
2433
|
+
);
|
|
2434
|
+
}
|
|
2435
|
+
function primaryEdgeSourceRef(edge) {
|
|
2436
|
+
return edge.sourceGlobalId ?? edge.fromGlobalId ?? edge.fromNodeId;
|
|
2437
|
+
}
|
|
2438
|
+
function primaryEdgeTargetRef(edge) {
|
|
2439
|
+
return edge.targetGlobalId ?? edge.toGlobalId ?? edge.toNodeId;
|
|
2440
|
+
}
|
|
2441
|
+
function hasVisitedNode(node, visited) {
|
|
2442
|
+
return nodeEndpointRefs2(node).some((ref) => visited.has(ref));
|
|
2443
|
+
}
|
|
2444
|
+
function markVisitedNode(node, visited) {
|
|
2445
|
+
for (const ref of nodeEndpointRefs2(node)) {
|
|
2446
|
+
visited.add(ref);
|
|
2447
|
+
}
|
|
2448
|
+
}
|
|
2449
|
+
async function resolveNodeRefSoft(ctx, nodeRef) {
|
|
2450
|
+
const normalizedId = ctx.db.normalizeId?.("epistemicNodes", nodeRef) ?? readConvexId4(nodeRef);
|
|
2451
|
+
if (normalizedId) {
|
|
2452
|
+
try {
|
|
2453
|
+
const direct = readEdgeNodeRow2(await ctx.db.get(normalizedId));
|
|
2454
|
+
if (direct) {
|
|
2455
|
+
return direct;
|
|
2456
|
+
}
|
|
2457
|
+
} catch (error) {
|
|
2458
|
+
debugGraphPrimitiveFallback(
|
|
2459
|
+
"[epistemicEdges] Direct node-ref lookup failed",
|
|
2460
|
+
{ error, nodeRef }
|
|
2461
|
+
);
|
|
2462
|
+
}
|
|
2463
|
+
}
|
|
2464
|
+
return readEdgeNodeRow2(
|
|
2465
|
+
await ctx.db.query("epistemicNodes").withIndex("by_globalId", (q) => q.eq("globalId", nodeRef)).first()
|
|
2466
|
+
);
|
|
2467
|
+
}
|
|
2468
|
+
async function collectNodeReferenceIds(ctx, nodeRef) {
|
|
2469
|
+
const refs = /* @__PURE__ */ new Set([nodeRef]);
|
|
2470
|
+
const node = await resolveNodeRefSoft(ctx, nodeRef);
|
|
2471
|
+
if (node) {
|
|
2472
|
+
for (const ref of nodeEndpointRefs2(node)) {
|
|
2473
|
+
refs.add(ref);
|
|
2474
|
+
}
|
|
2475
|
+
}
|
|
2476
|
+
return [...refs];
|
|
2477
|
+
}
|
|
2478
|
+
async function collectEdgesFromRefs(ctx, refs, edgeType) {
|
|
2479
|
+
const reads = [];
|
|
2480
|
+
for (const ref of refs) {
|
|
2481
|
+
reads.push(
|
|
2482
|
+
edgeType ? ctx.db.query("epistemicEdges").withIndex(
|
|
2483
|
+
"by_from_type",
|
|
2484
|
+
(q) => q.eq("fromNodeId", ref).eq("edgeType", edgeType)
|
|
2485
|
+
).collect() : ctx.db.query("epistemicEdges").withIndex("by_from", (q) => q.eq("fromNodeId", ref)).collect(),
|
|
2486
|
+
ctx.db.query("epistemicEdges").withIndex("by_source_global_id", (q) => q.eq("sourceGlobalId", ref)).collect()
|
|
2487
|
+
);
|
|
2488
|
+
}
|
|
2489
|
+
const edges = readEdgeRows2((await Promise.all(reads)).flat());
|
|
2490
|
+
return dedupeEdges(filterEdgesByType(edges, edgeType));
|
|
2491
|
+
}
|
|
2492
|
+
async function collectEdgesToRefs(ctx, refs, edgeType) {
|
|
2493
|
+
const reads = [];
|
|
2494
|
+
for (const ref of refs) {
|
|
2495
|
+
reads.push(
|
|
2496
|
+
edgeType ? ctx.db.query("epistemicEdges").withIndex(
|
|
2497
|
+
"by_to_type",
|
|
2498
|
+
(q) => q.eq("toNodeId", ref).eq("edgeType", edgeType)
|
|
2499
|
+
).collect() : ctx.db.query("epistemicEdges").withIndex("by_to", (q) => q.eq("toNodeId", ref)).collect(),
|
|
2500
|
+
ctx.db.query("epistemicEdges").withIndex("by_target_global_id", (q) => q.eq("targetGlobalId", ref)).collect()
|
|
2501
|
+
);
|
|
2502
|
+
}
|
|
2503
|
+
const edges = readEdgeRows2((await Promise.all(reads)).flat());
|
|
2504
|
+
return dedupeEdges(filterEdgesByType(edges, edgeType));
|
|
2505
|
+
}
|
|
2506
|
+
async function collectEdgesFromNodeRef(ctx, nodeRef, edgeType) {
|
|
2507
|
+
return collectEdgesFromRefs(
|
|
2508
|
+
ctx,
|
|
2509
|
+
await collectNodeReferenceIds(ctx, nodeRef),
|
|
2510
|
+
edgeType
|
|
2511
|
+
);
|
|
2512
|
+
}
|
|
2513
|
+
async function collectEdgesToNodeRef(ctx, nodeRef, edgeType) {
|
|
2514
|
+
return collectEdgesToRefs(
|
|
2515
|
+
ctx,
|
|
2516
|
+
await collectNodeReferenceIds(ctx, nodeRef),
|
|
2517
|
+
edgeType
|
|
2518
|
+
);
|
|
2519
|
+
}
|
|
2520
|
+
async function collectEdgesBetweenRefs(ctx, fromRefs, toRefs) {
|
|
2521
|
+
const reads = [];
|
|
2522
|
+
for (const fromRef of fromRefs) {
|
|
2523
|
+
for (const toRef of toRefs) {
|
|
2524
|
+
reads.push(
|
|
2525
|
+
ctx.db.query("epistemicEdges").withIndex(
|
|
2526
|
+
"by_from_to",
|
|
2527
|
+
(q) => q.eq("fromNodeId", fromRef).eq("toNodeId", toRef)
|
|
2528
|
+
).collect()
|
|
2529
|
+
);
|
|
2530
|
+
}
|
|
2531
|
+
reads.push(
|
|
2532
|
+
ctx.db.query("epistemicEdges").withIndex(
|
|
2533
|
+
"by_source_global_id",
|
|
2534
|
+
(q) => q.eq("sourceGlobalId", fromRef)
|
|
2535
|
+
).collect()
|
|
2536
|
+
);
|
|
2537
|
+
}
|
|
2538
|
+
for (const toRef of toRefs) {
|
|
2539
|
+
reads.push(
|
|
2540
|
+
ctx.db.query("epistemicEdges").withIndex("by_target_global_id", (q) => q.eq("targetGlobalId", toRef)).collect()
|
|
2541
|
+
);
|
|
2542
|
+
}
|
|
2543
|
+
const fromRefSet = new Set(fromRefs);
|
|
2544
|
+
const toRefSet = new Set(toRefs);
|
|
2545
|
+
const edges = readEdgeRows2((await Promise.all(reads)).flat()).filter(
|
|
2546
|
+
(edge) => edgeSourceRefs(edge).some((ref) => fromRefSet.has(ref)) && edgeTargetRefs(edge).some((ref) => toRefSet.has(ref))
|
|
2547
|
+
);
|
|
2548
|
+
return dedupeEdges(edges);
|
|
2549
|
+
}
|
|
2550
|
+
async function collectEdgesBetweenNodeRefs(ctx, fromNodeRef, toNodeRef) {
|
|
2551
|
+
const [fromRefs, toRefs] = await Promise.all([
|
|
2552
|
+
collectNodeReferenceIds(ctx, fromNodeRef),
|
|
2553
|
+
collectNodeReferenceIds(ctx, toNodeRef)
|
|
2554
|
+
]);
|
|
2555
|
+
return collectEdgesBetweenRefs(ctx, fromRefs, toRefs);
|
|
2556
|
+
}
|
|
2557
|
+
function buildLineageTraversalConfig(args) {
|
|
2558
|
+
const mode = args.mode ?? "anchor_down";
|
|
2559
|
+
return {
|
|
2560
|
+
maxDepth: args.maxDepth ?? 10,
|
|
2561
|
+
mode,
|
|
2562
|
+
minLayer: args.minLayer ?? getDefaultMinLayer(mode),
|
|
2563
|
+
maxLayer: args.maxLayer ?? 4
|
|
2564
|
+
};
|
|
2565
|
+
}
|
|
2566
|
+
function buildLineageQueueSeed(node) {
|
|
2567
|
+
return {
|
|
2568
|
+
node,
|
|
2569
|
+
depth: 0,
|
|
2570
|
+
currentLayer: node.epistemicLayer ?? getNodeLayer(node.nodeType)
|
|
2571
|
+
};
|
|
2572
|
+
}
|
|
2573
|
+
function shouldSkipLineageNode(item, state, visited) {
|
|
2574
|
+
if (item.depth >= state.maxDepth) {
|
|
2575
|
+
return true;
|
|
2576
|
+
}
|
|
2577
|
+
return hasVisitedNode(item.node, visited);
|
|
2578
|
+
}
|
|
2579
|
+
function resolveLineageTargetLayer(edge, targetNode) {
|
|
2580
|
+
return edge.toLayer ?? targetNode.epistemicLayer ?? getNodeLayer(targetNode.nodeType);
|
|
2581
|
+
}
|
|
2582
|
+
function collectDerivedFromEdges(ctx, node) {
|
|
2583
|
+
return collectEdgesFromRefs(
|
|
2584
|
+
ctx,
|
|
2585
|
+
nodeEndpointRefs2(node),
|
|
2586
|
+
DERIVED_FROM_EDGE_TYPE
|
|
2587
|
+
);
|
|
2588
|
+
}
|
|
2589
|
+
function shouldAdvanceLineage(currentLayer, targetLayer, state) {
|
|
2590
|
+
return shouldContinueTraversal(currentLayer, targetLayer, {
|
|
2591
|
+
mode: state.mode,
|
|
2592
|
+
minLayer: state.minLayer,
|
|
2593
|
+
maxLayer: state.maxLayer
|
|
2594
|
+
});
|
|
2595
|
+
}
|
|
2596
|
+
async function appendLineageEdges(ctx, current, state, lineage, queue, visited) {
|
|
2597
|
+
const lineageEdges = await collectDerivedFromEdges(ctx, current.node);
|
|
2598
|
+
for (const edge of lineageEdges) {
|
|
2599
|
+
const targetRef = primaryEdgeTargetRef(edge);
|
|
2600
|
+
if (!targetRef) {
|
|
2601
|
+
continue;
|
|
2602
|
+
}
|
|
2603
|
+
const targetNode = await resolveNodeRefSoft(ctx, targetRef);
|
|
2604
|
+
if (!targetNode || hasVisitedNode(targetNode, visited)) {
|
|
2605
|
+
continue;
|
|
2606
|
+
}
|
|
2607
|
+
const targetLayer = resolveLineageTargetLayer(edge, targetNode);
|
|
2608
|
+
if (!shouldAdvanceLineage(current.currentLayer, targetLayer, state)) {
|
|
2609
|
+
continue;
|
|
2610
|
+
}
|
|
2611
|
+
const nextDepth = current.depth + 1;
|
|
2612
|
+
lineage.push({
|
|
2613
|
+
node: targetNode,
|
|
2614
|
+
depth: nextDepth,
|
|
2615
|
+
edgeType: edge.edgeType,
|
|
2616
|
+
layer: targetLayer
|
|
2617
|
+
});
|
|
2618
|
+
queue.push({
|
|
2619
|
+
node: targetNode,
|
|
2620
|
+
depth: nextDepth,
|
|
2621
|
+
currentLayer: targetLayer
|
|
2622
|
+
});
|
|
2623
|
+
}
|
|
2624
|
+
}
|
|
2625
|
+
var get = query({
|
|
2626
|
+
args: { edgeId: v.id("epistemicEdges") },
|
|
2627
|
+
returns: permissiveReturn,
|
|
2628
|
+
handler: async (ctx, args) => await ctx.db.get(args.edgeId)
|
|
2629
|
+
});
|
|
2630
|
+
var getByGlobalId = query({
|
|
2631
|
+
args: { globalId: v.string() },
|
|
2632
|
+
returns: permissiveReturn,
|
|
2633
|
+
handler: async (ctx, args) => await ctx.db.query("epistemicEdges").withIndex("by_globalId", (q) => q.eq("globalId", args.globalId)).first()
|
|
2634
|
+
});
|
|
2635
|
+
var getFromNode = query({
|
|
2636
|
+
args: {
|
|
2637
|
+
fromNodeId: v.id("epistemicNodes"),
|
|
2638
|
+
edgeType: v.optional(edgeTypeValidator)
|
|
2639
|
+
},
|
|
2640
|
+
returns: permissiveReturn,
|
|
2641
|
+
handler: async (ctx, args) => await collectEdgesFromNodeRef(ctx, String(args.fromNodeId), args.edgeType)
|
|
2642
|
+
});
|
|
2643
|
+
var getToNode = query({
|
|
2644
|
+
args: {
|
|
2645
|
+
toNodeId: v.id("epistemicNodes"),
|
|
2646
|
+
edgeType: v.optional(edgeTypeValidator)
|
|
2647
|
+
},
|
|
2648
|
+
returns: permissiveReturn,
|
|
2649
|
+
handler: async (ctx, args) => await collectEdgesToNodeRef(ctx, String(args.toNodeId), args.edgeType)
|
|
2650
|
+
});
|
|
2651
|
+
var getBySourceNode = query({
|
|
2652
|
+
args: {
|
|
2653
|
+
sourceNodeId: v.string(),
|
|
2654
|
+
edgeType: v.optional(v.string())
|
|
2655
|
+
},
|
|
2656
|
+
returns: permissiveReturn,
|
|
2657
|
+
handler: async (ctx, args) => await collectEdgesFromNodeRef(ctx, args.sourceNodeId, args.edgeType)
|
|
2658
|
+
});
|
|
2659
|
+
var getByTargetNode = query({
|
|
2660
|
+
args: {
|
|
2661
|
+
targetNodeId: v.string(),
|
|
2662
|
+
edgeType: v.optional(v.string())
|
|
2663
|
+
},
|
|
2664
|
+
returns: permissiveReturn,
|
|
2665
|
+
handler: async (ctx, args) => await collectEdgesToNodeRef(ctx, args.targetNodeId, args.edgeType)
|
|
2666
|
+
});
|
|
2667
|
+
var getBetween = query({
|
|
2668
|
+
args: {
|
|
2669
|
+
fromNodeId: v.id("epistemicNodes"),
|
|
2670
|
+
toNodeId: v.id("epistemicNodes"),
|
|
2671
|
+
edgeType: v.optional(edgeTypeValidator)
|
|
2672
|
+
},
|
|
2673
|
+
returns: permissiveReturn,
|
|
2674
|
+
handler: async (ctx, args) => filterEdgesByType(
|
|
2675
|
+
await collectEdgesBetweenNodeRefs(
|
|
2676
|
+
ctx,
|
|
2677
|
+
String(args.fromNodeId),
|
|
2678
|
+
String(args.toNodeId)
|
|
2679
|
+
),
|
|
2680
|
+
args.edgeType
|
|
2681
|
+
)
|
|
2682
|
+
});
|
|
2683
|
+
var getByNodes = query({
|
|
2684
|
+
args: {
|
|
2685
|
+
fromNodeId: v.id("epistemicNodes"),
|
|
2686
|
+
toNodeId: v.id("epistemicNodes")
|
|
2687
|
+
},
|
|
2688
|
+
returns: permissiveReturn,
|
|
2689
|
+
handler: async (ctx, args) => (await collectEdgesBetweenNodeRefs(
|
|
2690
|
+
ctx,
|
|
2691
|
+
String(args.fromNodeId),
|
|
2692
|
+
String(args.toNodeId)
|
|
2693
|
+
))[0] ?? null
|
|
2694
|
+
});
|
|
2695
|
+
var getByProjectAndType = query({
|
|
2696
|
+
args: {
|
|
2697
|
+
...optionalScopeArgs,
|
|
2698
|
+
edgeType: edgeTypeValidator
|
|
2699
|
+
},
|
|
2700
|
+
returns: permissiveReturn,
|
|
2701
|
+
handler: async (ctx, args) => {
|
|
2702
|
+
if (!(args.projectId || args.topicId)) {
|
|
2703
|
+
return [];
|
|
2704
|
+
}
|
|
2705
|
+
let scope;
|
|
2706
|
+
try {
|
|
2707
|
+
scope = await resolveTopicProjectScope(ctx, {
|
|
2708
|
+
projectId: args.projectId,
|
|
2709
|
+
topicId: args.topicId
|
|
2710
|
+
});
|
|
2711
|
+
} catch (error) {
|
|
2712
|
+
debugGraphPrimitiveFallback(
|
|
2713
|
+
"[epistemicEdges] Failed to resolve getAll scope",
|
|
2714
|
+
{
|
|
2715
|
+
error,
|
|
2716
|
+
projectId: args.projectId,
|
|
2717
|
+
topicId: args.topicId
|
|
2718
|
+
}
|
|
2719
|
+
);
|
|
2720
|
+
return [];
|
|
2721
|
+
}
|
|
2722
|
+
const projectEdges = await collectScopedEdges(ctx, scope, 5e3);
|
|
2723
|
+
return projectEdges.filter(
|
|
2724
|
+
(e) => e.edgeType === args.edgeType && edgeMatchesWorkspaceReasoningScope(e, scope)
|
|
2725
|
+
).slice(0, 5e3);
|
|
2726
|
+
}
|
|
2727
|
+
});
|
|
2728
|
+
var getByProject = query({
|
|
2729
|
+
args: {
|
|
2730
|
+
...optionalScopeArgs,
|
|
2731
|
+
userId: v.optional(v.string()),
|
|
2732
|
+
limit: v.optional(v.number())
|
|
2733
|
+
},
|
|
2734
|
+
returns: permissiveReturn,
|
|
2735
|
+
handler: async (ctx, args) => {
|
|
2736
|
+
if (!(args.projectId || args.topicId)) {
|
|
2737
|
+
return [];
|
|
2738
|
+
}
|
|
2739
|
+
let scope;
|
|
2740
|
+
try {
|
|
2741
|
+
scope = await resolveTopicProjectScope(ctx, {
|
|
2742
|
+
projectId: args.projectId,
|
|
2743
|
+
topicId: args.topicId
|
|
2744
|
+
});
|
|
2745
|
+
} catch (error) {
|
|
2746
|
+
debugGraphPrimitiveFallback(
|
|
2747
|
+
"[epistemicEdges] Failed to resolve getByProject scope",
|
|
2748
|
+
{
|
|
2749
|
+
error,
|
|
2750
|
+
projectId: args.projectId,
|
|
2751
|
+
topicId: args.topicId
|
|
2752
|
+
}
|
|
2753
|
+
);
|
|
2754
|
+
return [];
|
|
2755
|
+
}
|
|
2756
|
+
if (args.userId) {
|
|
2757
|
+
const hasAccess = await checkScopeAccess(
|
|
2758
|
+
ctx,
|
|
2759
|
+
String(scope.topicId ?? scope.projectId),
|
|
2760
|
+
args.userId
|
|
2761
|
+
);
|
|
2762
|
+
if (!hasAccess) {
|
|
2763
|
+
return [];
|
|
2764
|
+
}
|
|
2765
|
+
}
|
|
2766
|
+
const pageSize = Math.max(1, Math.min(Math.floor(args.limit ?? 500), 2e3));
|
|
2767
|
+
const edges = await collectScopedEdges(
|
|
2768
|
+
ctx,
|
|
2769
|
+
scope,
|
|
2770
|
+
Math.min(pageSize * 3, 6e3)
|
|
1966
2771
|
);
|
|
2772
|
+
return edges.filter((edge) => edgeMatchesWorkspaceReasoningScope(edge, scope)).slice(0, pageSize);
|
|
1967
2773
|
}
|
|
1968
|
-
}
|
|
1969
|
-
|
|
1970
|
-
|
|
1971
|
-
|
|
1972
|
-
|
|
1973
|
-
|
|
1974
|
-
|
|
2774
|
+
});
|
|
2775
|
+
var listAll = query({
|
|
2776
|
+
args: {
|
|
2777
|
+
limit: v.optional(v.number())
|
|
2778
|
+
},
|
|
2779
|
+
returns: permissiveReturn,
|
|
2780
|
+
handler: async (ctx, args) => {
|
|
2781
|
+
const pageSize = Math.max(
|
|
2782
|
+
1,
|
|
2783
|
+
Math.min(Math.floor(args.limit ?? 5e3), 1e4)
|
|
1975
2784
|
);
|
|
2785
|
+
return await ctx.db.query("epistemicEdges").order("desc").take(pageSize);
|
|
1976
2786
|
}
|
|
1977
|
-
|
|
1978
|
-
|
|
1979
|
-
|
|
2787
|
+
});
|
|
2788
|
+
var findContradictions = query({
|
|
2789
|
+
args: {
|
|
2790
|
+
nodeId: v.id("epistemicNodes")
|
|
2791
|
+
},
|
|
2792
|
+
returns: permissiveReturn,
|
|
2793
|
+
handler: async (ctx, args) => {
|
|
2794
|
+
const edges = await collectEdgesToNodeRef(
|
|
2795
|
+
ctx,
|
|
2796
|
+
String(args.nodeId),
|
|
2797
|
+
"informs"
|
|
1980
2798
|
);
|
|
2799
|
+
return edges.filter((e) => (e.weight ?? 0) < 0);
|
|
1981
2800
|
}
|
|
1982
|
-
|
|
1983
|
-
|
|
1984
|
-
|
|
1985
|
-
|
|
1986
|
-
|
|
1987
|
-
|
|
1988
|
-
|
|
1989
|
-
|
|
1990
|
-
|
|
1991
|
-
|
|
1992
|
-
|
|
1993
|
-
{
|
|
1994
|
-
kind: "epistemic_node",
|
|
1995
|
-
nodeId: doc.toNodeId,
|
|
1996
|
-
nodeType: doc.toNodeType
|
|
1997
|
-
}
|
|
2801
|
+
});
|
|
2802
|
+
var findSupport = query({
|
|
2803
|
+
args: {
|
|
2804
|
+
nodeId: v.id("epistemicNodes")
|
|
2805
|
+
},
|
|
2806
|
+
returns: permissiveReturn,
|
|
2807
|
+
handler: async (ctx, args) => {
|
|
2808
|
+
const edges = await collectEdgesToNodeRef(
|
|
2809
|
+
ctx,
|
|
2810
|
+
String(args.nodeId),
|
|
2811
|
+
"informs"
|
|
1998
2812
|
);
|
|
2813
|
+
return edges.filter((e) => (e.weight ?? 0) >= 0);
|
|
1999
2814
|
}
|
|
2000
|
-
|
|
2001
|
-
|
|
2002
|
-
|
|
2003
|
-
// src/epistemicEdges.handlers.ts
|
|
2004
|
-
var mirrorEdgeToConvex = internalMutation({
|
|
2815
|
+
});
|
|
2816
|
+
var getLineage = query({
|
|
2005
2817
|
args: {
|
|
2006
|
-
|
|
2007
|
-
|
|
2008
|
-
|
|
2009
|
-
|
|
2010
|
-
|
|
2011
|
-
|
|
2012
|
-
|
|
2013
|
-
|
|
2014
|
-
|
|
2015
|
-
|
|
2016
|
-
|
|
2017
|
-
|
|
2018
|
-
|
|
2019
|
-
|
|
2020
|
-
reasoningMethod: v.optional(v.string()),
|
|
2021
|
-
logicalRole: v.optional(v.string()),
|
|
2022
|
-
temporalClass: v.optional(v.string()),
|
|
2023
|
-
validFrom: v.optional(v.number()),
|
|
2024
|
-
validUntil: v.optional(v.number()),
|
|
2025
|
-
constraint: v.optional(v.string()),
|
|
2026
|
-
propagation: v.optional(v.string()),
|
|
2027
|
-
blocking: v.optional(v.boolean()),
|
|
2028
|
-
implicit: v.optional(v.boolean()),
|
|
2029
|
-
conditionalA: v.optional(subjectiveOpinionValidator),
|
|
2030
|
-
conditionalNotA: v.optional(subjectiveOpinionValidator)
|
|
2818
|
+
nodeId: v.id("epistemicNodes"),
|
|
2819
|
+
maxDepth: v.optional(v.number()),
|
|
2820
|
+
// Phase 2D: Layer-aware traversal options
|
|
2821
|
+
minLayer: v.optional(v.number()),
|
|
2822
|
+
// 1=L1, 2=L2, 3=L3, 4=L4
|
|
2823
|
+
maxLayer: v.optional(v.number()),
|
|
2824
|
+
mode: v.optional(
|
|
2825
|
+
v.union(
|
|
2826
|
+
v.literal("anchor_down"),
|
|
2827
|
+
v.literal("anchor_up"),
|
|
2828
|
+
v.literal("same_layer"),
|
|
2829
|
+
v.literal("decision_trace")
|
|
2830
|
+
)
|
|
2831
|
+
)
|
|
2031
2832
|
},
|
|
2032
2833
|
returns: permissiveReturn,
|
|
2033
2834
|
handler: async (ctx, args) => {
|
|
2034
|
-
const
|
|
2035
|
-
|
|
2036
|
-
|
|
2037
|
-
|
|
2038
|
-
|
|
2039
|
-
|
|
2040
|
-
|
|
2041
|
-
const toNode = await ctx.db.query("epistemicNodes").withIndex("by_globalId", (q) => q.eq("globalId", args.toGlobalId)).first();
|
|
2042
|
-
const existing = await ctx.db.query("epistemicEdges").withIndex("by_globalId", (q) => q.eq("globalId", args.globalId)).first();
|
|
2043
|
-
if (existing) {
|
|
2044
|
-
console.log(`[Dual-Write] Edge already exists: ${args.globalId}`);
|
|
2045
|
-
return buildEdgeMirrorWriteResult(existing._id, true);
|
|
2835
|
+
const traversalState = buildLineageTraversalConfig(args);
|
|
2836
|
+
const lineage = [];
|
|
2837
|
+
const queue = [];
|
|
2838
|
+
const visited = /* @__PURE__ */ new Set();
|
|
2839
|
+
const startNode = readEdgeNodeRow2(await ctx.db.get(args.nodeId));
|
|
2840
|
+
if (!startNode) {
|
|
2841
|
+
return lineage;
|
|
2046
2842
|
}
|
|
2047
|
-
|
|
2048
|
-
|
|
2049
|
-
|
|
2843
|
+
queue.push(buildLineageQueueSeed(startNode));
|
|
2844
|
+
while (queue.length > 0) {
|
|
2845
|
+
const current = queue.shift();
|
|
2846
|
+
if (!current || shouldSkipLineageNode(current, traversalState, visited)) {
|
|
2847
|
+
continue;
|
|
2848
|
+
}
|
|
2849
|
+
markVisitedNode(current.node, visited);
|
|
2850
|
+
await appendLineageEdges(
|
|
2851
|
+
ctx,
|
|
2852
|
+
current,
|
|
2853
|
+
traversalState,
|
|
2854
|
+
lineage,
|
|
2855
|
+
queue,
|
|
2856
|
+
visited
|
|
2050
2857
|
);
|
|
2051
|
-
return buildEdgeMirrorSkippedResult();
|
|
2052
2858
|
}
|
|
2053
|
-
|
|
2054
|
-
const edgeId = await insertEpistemicEdge(ctx, {
|
|
2055
|
-
globalId: args.globalId,
|
|
2056
|
-
// C2-RR.4 Defect E — endpoints are canonical node globalIds, not Convex
|
|
2057
|
-
// doc ids. fromNode/toNode are resolved by_globalId above, so their
|
|
2058
|
-
// stored globalId equals args.from/toGlobalId; persisting the doc _id here
|
|
2059
|
-
// was the "mixed _id/globalId" defect.
|
|
2060
|
-
fromNodeId: fromNode.globalId,
|
|
2061
|
-
toNodeId: toNode.globalId,
|
|
2062
|
-
sourceGlobalId: args.fromGlobalId,
|
|
2063
|
-
targetGlobalId: args.toGlobalId,
|
|
2064
|
-
// Preserve the canonical string value even when the local schema lags.
|
|
2065
|
-
edgeType: args.edgeType,
|
|
2066
|
-
weight: args.weight,
|
|
2067
|
-
confidence: args.confidence,
|
|
2068
|
-
context: args.context,
|
|
2069
|
-
derivationType: args.derivationType,
|
|
2070
|
-
constraint: args.constraint,
|
|
2071
|
-
propagation: args.propagation,
|
|
2072
|
-
blocking: args.blocking,
|
|
2073
|
-
implicit: args.implicit,
|
|
2074
|
-
conditionalA: args.conditionalA,
|
|
2075
|
-
conditionalNotA: args.conditionalNotA,
|
|
2076
|
-
createdBy: args.createdBy,
|
|
2077
|
-
createdAt: now,
|
|
2078
|
-
updatedAt: now,
|
|
2079
|
-
topicId: args.projectId,
|
|
2080
|
-
fromLayer: args.fromLayer,
|
|
2081
|
-
toLayer: args.toLayer,
|
|
2082
|
-
fromNodeType: args.fromNodeType,
|
|
2083
|
-
toNodeType: args.toNodeType,
|
|
2084
|
-
reasoningMethod: args.reasoningMethod,
|
|
2085
|
-
logicalRole: args.logicalRole,
|
|
2086
|
-
temporalClass: args.temporalClass,
|
|
2087
|
-
validFrom: args.validFrom,
|
|
2088
|
-
validUntil: args.validUntil
|
|
2089
|
-
});
|
|
2090
|
-
console.log(
|
|
2091
|
-
`[Dual-Write] Mirrored edge to Convex: ${args.globalId} (${args.edgeType})`
|
|
2092
|
-
);
|
|
2093
|
-
return buildEdgeMirrorWriteResult(edgeId, false);
|
|
2859
|
+
return lineage;
|
|
2094
2860
|
}
|
|
2095
2861
|
});
|
|
2096
|
-
var
|
|
2862
|
+
var getEvidenceForBelief = query({
|
|
2097
2863
|
args: {
|
|
2098
|
-
|
|
2864
|
+
beliefNodeId: v.id("epistemicNodes")
|
|
2099
2865
|
},
|
|
2100
2866
|
returns: permissiveReturn,
|
|
2101
2867
|
handler: async (ctx, args) => {
|
|
2102
|
-
const
|
|
2103
|
-
|
|
2104
|
-
|
|
2105
|
-
|
|
2106
|
-
|
|
2107
|
-
|
|
2108
|
-
|
|
2868
|
+
const informsEdges = await collectEdgesToNodeRef(
|
|
2869
|
+
ctx,
|
|
2870
|
+
String(args.beliefNodeId),
|
|
2871
|
+
"informs"
|
|
2872
|
+
);
|
|
2873
|
+
const supportEdges = informsEdges.filter((e) => (e.weight ?? 0) >= 0);
|
|
2874
|
+
const contradictEdges = informsEdges.filter((e) => (e.weight ?? 0) < 0);
|
|
2875
|
+
const supportingEvidence = await Promise.all(
|
|
2876
|
+
supportEdges.map(async (edge) => {
|
|
2877
|
+
const sourceRef = primaryEdgeSourceRef(edge);
|
|
2878
|
+
return {
|
|
2879
|
+
edge,
|
|
2880
|
+
node: sourceRef ? await resolveNodeRefSoft(ctx, sourceRef) : null
|
|
2881
|
+
};
|
|
2882
|
+
})
|
|
2883
|
+
);
|
|
2884
|
+
const contradictingEvidence = await Promise.all(
|
|
2885
|
+
contradictEdges.map(async (edge) => {
|
|
2886
|
+
const sourceRef = primaryEdgeSourceRef(edge);
|
|
2887
|
+
return {
|
|
2888
|
+
edge,
|
|
2889
|
+
node: sourceRef ? await resolveNodeRefSoft(ctx, sourceRef) : null
|
|
2890
|
+
};
|
|
2891
|
+
})
|
|
2892
|
+
);
|
|
2893
|
+
const filteredSupporting = supportingEvidence.filter(
|
|
2894
|
+
(e) => e.node !== null
|
|
2895
|
+
);
|
|
2896
|
+
const filteredContradicting = contradictingEvidence.filter(
|
|
2897
|
+
(e) => e.node !== null
|
|
2898
|
+
);
|
|
2899
|
+
return {
|
|
2900
|
+
supporting: filteredSupporting,
|
|
2901
|
+
contradicting: filteredContradicting,
|
|
2902
|
+
supportCount: filteredSupporting.length,
|
|
2903
|
+
contradictCount: filteredContradicting.length
|
|
2904
|
+
};
|
|
2109
2905
|
}
|
|
2110
2906
|
});
|
|
2111
|
-
var
|
|
2907
|
+
var getClusterEdges = query({
|
|
2112
2908
|
args: {
|
|
2113
|
-
|
|
2114
|
-
weight: v.optional(v.number()),
|
|
2115
|
-
confidence: v.optional(v.number()),
|
|
2116
|
-
context: v.optional(v.string()),
|
|
2117
|
-
derivationType: v.optional(v.string()),
|
|
2118
|
-
constraint: v.optional(v.string()),
|
|
2119
|
-
propagation: v.optional(v.string()),
|
|
2120
|
-
blocking: v.optional(v.boolean()),
|
|
2121
|
-
implicit: v.optional(v.boolean()),
|
|
2122
|
-
conditionalA: v.optional(subjectiveOpinionValidator),
|
|
2123
|
-
conditionalNotA: v.optional(subjectiveOpinionValidator)
|
|
2909
|
+
beliefIds: v.array(v.string())
|
|
2124
2910
|
},
|
|
2125
2911
|
returns: permissiveReturn,
|
|
2126
2912
|
handler: async (ctx, args) => {
|
|
2127
|
-
|
|
2128
|
-
|
|
2129
|
-
return buildEdgeMirrorMissingResult();
|
|
2130
|
-
}
|
|
2131
|
-
const updates = {
|
|
2132
|
-
updatedAt: Date.now()
|
|
2133
|
-
};
|
|
2134
|
-
if (args.weight !== void 0) {
|
|
2135
|
-
updates.weight = args.weight;
|
|
2136
|
-
}
|
|
2137
|
-
if (args.confidence !== void 0) {
|
|
2138
|
-
updates.confidence = args.confidence;
|
|
2139
|
-
}
|
|
2140
|
-
if (args.context !== void 0) {
|
|
2141
|
-
updates.context = args.context;
|
|
2142
|
-
}
|
|
2143
|
-
if (args.derivationType !== void 0) {
|
|
2144
|
-
updates.derivationType = args.derivationType;
|
|
2145
|
-
}
|
|
2146
|
-
if (args.constraint !== void 0) {
|
|
2147
|
-
updates.constraint = args.constraint;
|
|
2148
|
-
}
|
|
2149
|
-
if (args.propagation !== void 0) {
|
|
2150
|
-
updates.propagation = args.propagation;
|
|
2151
|
-
}
|
|
2152
|
-
if (args.blocking !== void 0) {
|
|
2153
|
-
updates.blocking = args.blocking;
|
|
2154
|
-
}
|
|
2155
|
-
if (args.implicit !== void 0) {
|
|
2156
|
-
updates.implicit = args.implicit;
|
|
2157
|
-
}
|
|
2158
|
-
if (args.conditionalA !== void 0) {
|
|
2159
|
-
updates.conditionalA = args.conditionalA;
|
|
2913
|
+
if (args.beliefIds.length === 0) {
|
|
2914
|
+
return [];
|
|
2160
2915
|
}
|
|
2161
|
-
|
|
2162
|
-
|
|
2916
|
+
const beliefIdSet = /* @__PURE__ */ new Set();
|
|
2917
|
+
for (const beliefId of args.beliefIds) {
|
|
2918
|
+
for (const ref of await collectNodeReferenceIds(ctx, beliefId)) {
|
|
2919
|
+
beliefIdSet.add(ref);
|
|
2920
|
+
}
|
|
2163
2921
|
}
|
|
2164
|
-
|
|
2165
|
-
|
|
2166
|
-
|
|
2922
|
+
const allEdges = readEdgeRows2(
|
|
2923
|
+
await ctx.db.query("epistemicEdges").collect()
|
|
2924
|
+
);
|
|
2925
|
+
return allEdges.filter((edge) => {
|
|
2926
|
+
const fromInCluster = edgeSourceRefs(edge).some(
|
|
2927
|
+
(ref) => beliefIdSet.has(ref)
|
|
2928
|
+
);
|
|
2929
|
+
const toInCluster = edgeTargetRefs(edge).some(
|
|
2930
|
+
(ref) => beliefIdSet.has(ref)
|
|
2931
|
+
);
|
|
2932
|
+
return fromInCluster && toInCluster;
|
|
2933
|
+
});
|
|
2167
2934
|
}
|
|
2168
2935
|
});
|
|
2169
2936
|
|
|
2170
|
-
|
|
2171
|
-
var getByTopic = getByProject;
|
|
2172
|
-
|
|
2173
|
-
export { batchCreate, cleanupDeprecatedEdges, create, deleteEdgeFromConvex, deleteEdges, edgeTypeValidator, epistemicLayerValidator, findContradictions, findSupport, get, getBetween, getByGlobalId, getByNodes, getByProject, getByProjectAndType, getBySourceNode, getByTargetNode, getByTopic, getClusterEdges, getEvidenceForBelief, getFromNode, getLineage, getToNode, listAll, mirrorEdgeToConvex, remove, removeBetween, update, updateEdgeInConvex };
|
|
2937
|
+
export { batchCreate, cleanupDeprecatedEdges, create, deleteEdgeFromConvex, deleteEdges, edgeTypeValidator, epistemicLayerValidator, findContradictions, findSupport, get, getBetween, getByGlobalId, getByNodes, getByProject, getByProjectAndType, getBySourceNode, getByTargetNode, getByProject as getByTopic, getClusterEdges, getEvidenceForBelief, getFromNode, getLineage, getToNode, listAll, mirrorEdgeToConvex, remove, removeBetween, update, updateEdgeInConvex };
|
|
2174
2938
|
//# sourceMappingURL=epistemicEdges.js.map
|
|
2175
2939
|
//# sourceMappingURL=epistemicEdges.js.map
|