@lucern/graph-primitives 1.0.28 → 1.0.30
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 +398 -228
- 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 +857 -515
- 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 +366 -203
- package/dist/epistemicBeliefs.admin.js.map +1 -1
- package/dist/epistemicBeliefs.backfills.d.ts +8 -8
- package/dist/epistemicBeliefs.backfills.js +655 -308
- package/dist/epistemicBeliefs.backfills.js.map +1 -1
- package/dist/epistemicBeliefs.confidence.d.ts +19 -14
- package/dist/epistemicBeliefs.confidence.js +634 -423
- package/dist/epistemicBeliefs.confidence.js.map +1 -1
- package/dist/epistemicBeliefs.core.d.ts +6 -6
- package/dist/epistemicBeliefs.core.js +719 -411
- package/dist/epistemicBeliefs.core.js.map +1 -1
- package/dist/epistemicBeliefs.d.ts +11 -8
- package/dist/epistemicBeliefs.forkEvidence.d.ts +2 -0
- package/dist/epistemicBeliefs.forkEvidence.js +8 -28
- package/dist/epistemicBeliefs.forkEvidence.js.map +1 -1
- package/dist/epistemicBeliefs.helpers.d.ts +69 -74
- package/dist/epistemicBeliefs.helpers.js +359 -248
- package/dist/epistemicBeliefs.helpers.js.map +1 -1
- package/dist/epistemicBeliefs.internal.d.ts +5 -5
- package/dist/epistemicBeliefs.internal.js +1246 -1044
- package/dist/epistemicBeliefs.internal.js.map +1 -1
- package/dist/epistemicBeliefs.js +4922 -3608
- package/dist/epistemicBeliefs.js.map +1 -1
- package/dist/epistemicBeliefs.lifecycle.d.ts +5 -5
- package/dist/epistemicBeliefs.lifecycle.js +1137 -818
- package/dist/epistemicBeliefs.lifecycle.js.map +1 -1
- package/dist/epistemicBeliefs.links.d.ts +7 -7
- package/dist/epistemicBeliefs.links.js +408 -307
- 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 +1063 -613
- package/dist/epistemicContracts.evaluators.js.map +1 -1
- package/dist/epistemicContracts.handlers.d.ts +15 -32
- package/dist/epistemicContracts.handlers.js +2086 -1644
- package/dist/epistemicContracts.handlers.js.map +1 -1
- package/dist/epistemicContracts.js +1131 -672
- 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 +1969 -1205
- package/dist/epistemicEdges.js.map +1 -1
- package/dist/epistemicEdges.mutations.d.ts +7 -7
- package/dist/epistemicEdges.mutations.js +960 -583
- 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 +937 -536
- 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 +844 -696
- 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 +704 -508
- package/dist/epistemicNodes.js.map +1 -1
- package/dist/epistemicNodes.mutations.d.ts +6 -6
- package/dist/epistemicNodes.mutations.js +564 -467
- 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 +352 -312
- 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 +87 -83
- package/dist/index.js +15677 -10594
- 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
|
@@ -1,16 +1,20 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import { requireScopeWriteAccess } from '@lucern/access-control/access';
|
|
2
|
+
import { EVIDENCE_SOURCE_QUALITY_VALUES, EVIDENCE_METHODOLOGY_VALUES, EVIDENCE_INFORMATION_ASYMMETRY_VALUES, assertEdgePolicyAllowed, edgePolicyManifest } from '@lucern/contracts';
|
|
3
3
|
import { permissiveReturn } from '@lucern/contracts/schema-helpers/validators';
|
|
4
|
-
import {
|
|
4
|
+
import { v } from 'convex/values';
|
|
5
|
+
import { unsafeConvexAnyApi } from '@lucern/contracts/convex/unsafeAnyApi';
|
|
6
|
+
import { componentsGeneric, mutationGeneric, internalMutationGeneric } from 'convex/server';
|
|
7
|
+
import '@lucern/access-control/audience';
|
|
5
8
|
import { generateGlobalId, assertUuidV7Identity, assertStorageEdgeVocabulary, assertUuidShapedEdgeEndpoint } from '@lucern/contracts/ids';
|
|
6
9
|
import { isNodeType, getLayerForNodeType } from '@lucern/contracts/schema-helpers/spine/tables/epistemicNodes';
|
|
7
|
-
import '@lucern/access-control/audience';
|
|
8
|
-
import { assertEdgePolicyAllowed, edgePolicyManifest } from '@lucern/contracts';
|
|
9
10
|
|
|
10
11
|
// src/epistemicEvidenceMutations.ts
|
|
11
|
-
var
|
|
12
|
+
var unsafeApi = unsafeConvexAnyApi(
|
|
13
|
+
"graph-primitives top-level module bundle lacks a committed Convex _generated/api surface"
|
|
14
|
+
);
|
|
15
|
+
var api = unsafeApi;
|
|
12
16
|
componentsGeneric();
|
|
13
|
-
var internal =
|
|
17
|
+
var internal = unsafeApi;
|
|
14
18
|
var internalMutation = internalMutationGeneric;
|
|
15
19
|
var mutation = mutationGeneric;
|
|
16
20
|
|
|
@@ -27,22 +31,19 @@ function debugGraphPrimitiveFallback(message, context) {
|
|
|
27
31
|
}
|
|
28
32
|
|
|
29
33
|
// src/embeddingTrigger.ts
|
|
34
|
+
var embeddingActionRef = "embeddingActions:generateEpistemicNodeEmbedding";
|
|
30
35
|
async function scheduleEmbeddingGeneration(args) {
|
|
31
36
|
try {
|
|
32
|
-
await args.ctx.scheduler.runAfter(
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
hasAnswer: args.hasAnswer,
|
|
43
|
-
confidence: args.confidence
|
|
44
|
-
}
|
|
45
|
-
);
|
|
37
|
+
await args.ctx.scheduler.runAfter(0, embeddingActionRef, {
|
|
38
|
+
nodeId: args.nodeId,
|
|
39
|
+
projectId: args.projectId ? String(args.projectId) : void 0,
|
|
40
|
+
topicId: args.topicId ? String(args.topicId) : void 0,
|
|
41
|
+
createdBy: args.createdBy,
|
|
42
|
+
nodeType: args.nodeType,
|
|
43
|
+
text: args.text.slice(0, 2e4),
|
|
44
|
+
hasAnswer: args.hasAnswer,
|
|
45
|
+
confidence: args.confidence
|
|
46
|
+
});
|
|
46
47
|
} catch (error) {
|
|
47
48
|
debugGraphPrimitiveFallback(
|
|
48
49
|
"[embeddingTrigger] Failed to schedule embedding generation",
|
|
@@ -54,350 +55,9 @@ async function scheduleEmbeddingGeneration(args) {
|
|
|
54
55
|
);
|
|
55
56
|
}
|
|
56
57
|
}
|
|
57
|
-
var LEGACY_SCOPE_FIELD = "graphScopeProjectId";
|
|
58
|
-
async function resolveTopicNodeScopeOrNull(ctx, ref) {
|
|
59
|
-
if (!ctx?.db || typeof ctx.db.query !== "function") {
|
|
60
|
-
return null;
|
|
61
|
-
}
|
|
62
|
-
let node = null;
|
|
63
|
-
try {
|
|
64
|
-
const byGlobalId = await ctx.db.query("epistemicNodes").withIndex("by_globalId", (q) => q.eq("globalId", ref)).first();
|
|
65
|
-
if (byGlobalId && byGlobalId.nodeType === "topic") {
|
|
66
|
-
node = byGlobalId;
|
|
67
|
-
}
|
|
68
|
-
} catch (error) {
|
|
69
|
-
debugGraphPrimitiveFallback(
|
|
70
|
-
"[topicScope] topic-node scope lookup by globalId failed",
|
|
71
|
-
{ error, ref }
|
|
72
|
-
);
|
|
73
|
-
}
|
|
74
|
-
if (!node) {
|
|
75
|
-
return null;
|
|
76
|
-
}
|
|
77
|
-
const scopeKey = normalizeScopeValue(node.topicId) ?? normalizeScopeValue(node.globalId);
|
|
78
|
-
if (!scopeKey) {
|
|
79
|
-
return null;
|
|
80
|
-
}
|
|
81
|
-
return {
|
|
82
|
-
topicId: scopeKey,
|
|
83
|
-
projectId: asMappedProjectId(node),
|
|
84
|
-
source: "topic_node"
|
|
85
|
-
};
|
|
86
|
-
}
|
|
87
|
-
function asMappedProjectId(topic) {
|
|
88
|
-
if (!topic) {
|
|
89
|
-
return;
|
|
90
|
-
}
|
|
91
|
-
const directLegacyProjectId = normalizeScopeValue(topic[LEGACY_SCOPE_FIELD]);
|
|
92
|
-
if (directLegacyProjectId) {
|
|
93
|
-
return directLegacyProjectId;
|
|
94
|
-
}
|
|
95
|
-
const metadata = topic.metadata || {};
|
|
96
|
-
const candidate = metadata[LEGACY_SCOPE_FIELD] || metadata.legacyProjectId || metadata.projectId || metadata.scopeProjectId;
|
|
97
|
-
return candidate ? candidate : void 0;
|
|
98
|
-
}
|
|
99
|
-
function normalizeScopeValue(value) {
|
|
100
|
-
if (typeof value !== "string") {
|
|
101
|
-
return;
|
|
102
|
-
}
|
|
103
|
-
const normalized = value.trim();
|
|
104
|
-
return normalized.length > 0 ? normalized : void 0;
|
|
105
|
-
}
|
|
106
|
-
function pickPrimaryTopic(candidates) {
|
|
107
|
-
return [...candidates].sort((a, b) => {
|
|
108
|
-
const depthA = a.depth ?? 9999;
|
|
109
|
-
const depthB = b.depth ?? 9999;
|
|
110
|
-
if (depthA !== depthB) {
|
|
111
|
-
return depthA - depthB;
|
|
112
|
-
}
|
|
113
|
-
const createdA = a.createdAt ?? Number.MAX_SAFE_INTEGER;
|
|
114
|
-
const createdB = b.createdAt ?? Number.MAX_SAFE_INTEGER;
|
|
115
|
-
if (createdA !== createdB) {
|
|
116
|
-
return createdA - createdB;
|
|
117
|
-
}
|
|
118
|
-
return String(a.name || "").localeCompare(String(b.name || ""));
|
|
119
|
-
})[0];
|
|
120
|
-
}
|
|
121
|
-
async function findTopicsByScopeAlias(ctx, scopeId) {
|
|
122
|
-
try {
|
|
123
|
-
return await ctx.db.query("topics").withIndex(
|
|
124
|
-
"by_graph_scope_project",
|
|
125
|
-
(q) => q.eq(LEGACY_SCOPE_FIELD, scopeId)
|
|
126
|
-
).collect();
|
|
127
|
-
} catch (error) {
|
|
128
|
-
debugGraphPrimitiveFallback(
|
|
129
|
-
"[topicScope] Failed to resolve scope alias via index",
|
|
130
|
-
{
|
|
131
|
-
error,
|
|
132
|
-
scopeId
|
|
133
|
-
}
|
|
134
|
-
);
|
|
135
|
-
const topics = await ctx.db.query("topics").collect();
|
|
136
|
-
return topics.filter((topic) => {
|
|
137
|
-
const normalizedGlobalId = normalizeScopeValue(topic.globalId);
|
|
138
|
-
const mappedProjectId = asMappedProjectId(topic);
|
|
139
|
-
return String(topic._id) === scopeId || normalizedGlobalId === scopeId || mappedProjectId === scopeId;
|
|
140
|
-
});
|
|
141
|
-
}
|
|
142
|
-
}
|
|
143
|
-
async function tryResolveHostTopicById(ctx, topicId) {
|
|
144
|
-
if (typeof ctx.runQuery !== "function") {
|
|
145
|
-
return null;
|
|
146
|
-
}
|
|
147
|
-
try {
|
|
148
|
-
return await ctx.runQuery(api.topics.get, {
|
|
149
|
-
id: topicId
|
|
150
|
-
}) ?? null;
|
|
151
|
-
} catch (error) {
|
|
152
|
-
debugGraphPrimitiveFallback(
|
|
153
|
-
"[topicScope] Failed to resolve topic by host query",
|
|
154
|
-
{
|
|
155
|
-
error,
|
|
156
|
-
topicId
|
|
157
|
-
}
|
|
158
|
-
);
|
|
159
|
-
return null;
|
|
160
|
-
}
|
|
161
|
-
}
|
|
162
|
-
async function tryResolveHostTopicByLegacyScope(ctx, legacyScopeId) {
|
|
163
|
-
if (typeof ctx.runQuery !== "function") {
|
|
164
|
-
return null;
|
|
165
|
-
}
|
|
166
|
-
try {
|
|
167
|
-
return await ctx.runQuery(api.topics.getByLegacyScopeId, {
|
|
168
|
-
projectId: legacyScopeId
|
|
169
|
-
}) ?? null;
|
|
170
|
-
} catch (error) {
|
|
171
|
-
debugGraphPrimitiveFallback(
|
|
172
|
-
"[topicScope] Failed to resolve topic by legacy scope",
|
|
173
|
-
{
|
|
174
|
-
error,
|
|
175
|
-
legacyScopeId
|
|
176
|
-
}
|
|
177
|
-
);
|
|
178
|
-
return null;
|
|
179
|
-
}
|
|
180
|
-
}
|
|
181
|
-
async function resolveInheritedWorkspaceScope(ctx, topic) {
|
|
182
|
-
const MAX_DEPTH = 10;
|
|
183
|
-
let tenantId = normalizeScopeValue(topic.tenantId);
|
|
184
|
-
let workspaceId = normalizeScopeValue(topic.workspaceId);
|
|
185
|
-
if (tenantId && workspaceId) {
|
|
186
|
-
return { tenantId, workspaceId };
|
|
187
|
-
}
|
|
188
|
-
let current = topic;
|
|
189
|
-
for (let i = 0; i < MAX_DEPTH && current?.parentTopicId; i++) {
|
|
190
|
-
current = await ctx.db.get(current.parentTopicId);
|
|
191
|
-
if (!current) break;
|
|
192
|
-
if (!tenantId) {
|
|
193
|
-
tenantId = normalizeScopeValue(current.tenantId);
|
|
194
|
-
}
|
|
195
|
-
if (!workspaceId) {
|
|
196
|
-
workspaceId = normalizeScopeValue(current.workspaceId);
|
|
197
|
-
}
|
|
198
|
-
if (tenantId && workspaceId) break;
|
|
199
|
-
}
|
|
200
|
-
return { tenantId, workspaceId };
|
|
201
|
-
}
|
|
202
|
-
async function resolveTopicProjectScope(ctx, args) {
|
|
203
|
-
if (args.topicId) {
|
|
204
|
-
let topic = null;
|
|
205
|
-
try {
|
|
206
|
-
topic = await ctx.db.get(
|
|
207
|
-
args.topicId
|
|
208
|
-
);
|
|
209
|
-
} catch (error) {
|
|
210
|
-
debugGraphPrimitiveFallback(
|
|
211
|
-
"[topicScope] Failed to load topic by direct id",
|
|
212
|
-
{
|
|
213
|
-
error,
|
|
214
|
-
topicId: args.topicId
|
|
215
|
-
}
|
|
216
|
-
);
|
|
217
|
-
}
|
|
218
|
-
if (!topic) {
|
|
219
|
-
topic = await tryResolveHostTopicById(ctx, String(args.topicId));
|
|
220
|
-
}
|
|
221
|
-
if (!topic) {
|
|
222
|
-
topic = pickPrimaryTopic(
|
|
223
|
-
await findTopicsByScopeAlias(ctx, String(args.topicId))
|
|
224
|
-
) ?? null;
|
|
225
|
-
}
|
|
226
|
-
if (!topic) {
|
|
227
|
-
const nodeScope = await resolveTopicNodeScopeOrNull(
|
|
228
|
-
ctx,
|
|
229
|
-
String(args.topicId)
|
|
230
|
-
);
|
|
231
|
-
if (nodeScope) {
|
|
232
|
-
return nodeScope;
|
|
233
|
-
}
|
|
234
|
-
throw new Error(`Topic not found: ${String(args.topicId)}`);
|
|
235
|
-
}
|
|
236
|
-
const inherited = await resolveInheritedWorkspaceScope(ctx, topic);
|
|
237
|
-
const mapped = asMappedProjectId(topic);
|
|
238
|
-
if (mapped) {
|
|
239
|
-
return {
|
|
240
|
-
topicId: topic._id,
|
|
241
|
-
projectId: mapped,
|
|
242
|
-
tenantId: inherited.tenantId,
|
|
243
|
-
workspaceId: inherited.workspaceId,
|
|
244
|
-
source: "topic"
|
|
245
|
-
};
|
|
246
|
-
}
|
|
247
|
-
return {
|
|
248
|
-
topicId: topic._id,
|
|
249
|
-
tenantId: inherited.tenantId,
|
|
250
|
-
workspaceId: inherited.workspaceId,
|
|
251
|
-
source: "topic"
|
|
252
|
-
};
|
|
253
|
-
}
|
|
254
|
-
if (args.projectId) {
|
|
255
|
-
let directTopic = null;
|
|
256
|
-
try {
|
|
257
|
-
directTopic = await ctx.db.get(
|
|
258
|
-
args.projectId
|
|
259
|
-
);
|
|
260
|
-
} catch (error) {
|
|
261
|
-
debugGraphPrimitiveFallback(
|
|
262
|
-
"[topicScope] Failed to load direct project topic",
|
|
263
|
-
{
|
|
264
|
-
error,
|
|
265
|
-
projectId: args.projectId
|
|
266
|
-
}
|
|
267
|
-
);
|
|
268
|
-
}
|
|
269
|
-
if (directTopic) {
|
|
270
|
-
const inherited = await resolveInheritedWorkspaceScope(ctx, directTopic);
|
|
271
|
-
const mapped = asMappedProjectId(directTopic);
|
|
272
|
-
return {
|
|
273
|
-
topicId: directTopic._id,
|
|
274
|
-
projectId: mapped ?? args.projectId,
|
|
275
|
-
tenantId: inherited.tenantId,
|
|
276
|
-
workspaceId: inherited.workspaceId,
|
|
277
|
-
source: "topic_inferred"
|
|
278
|
-
};
|
|
279
|
-
}
|
|
280
|
-
directTopic = await tryResolveHostTopicByLegacyScope(ctx, args.projectId);
|
|
281
|
-
if (directTopic) {
|
|
282
|
-
const inherited = await resolveInheritedWorkspaceScope(ctx, directTopic);
|
|
283
|
-
const mapped = asMappedProjectId(directTopic);
|
|
284
|
-
return {
|
|
285
|
-
topicId: directTopic._id,
|
|
286
|
-
projectId: mapped ?? args.projectId,
|
|
287
|
-
tenantId: inherited.tenantId,
|
|
288
|
-
workspaceId: inherited.workspaceId,
|
|
289
|
-
source: "topic_inferred"
|
|
290
|
-
};
|
|
291
|
-
}
|
|
292
|
-
const topics = await findTopicsByScopeAlias(ctx, args.projectId);
|
|
293
|
-
const primary = pickPrimaryTopic(topics);
|
|
294
|
-
if (primary) {
|
|
295
|
-
const inherited = await resolveInheritedWorkspaceScope(ctx, primary);
|
|
296
|
-
return {
|
|
297
|
-
topicId: primary._id,
|
|
298
|
-
projectId: args.projectId,
|
|
299
|
-
tenantId: inherited.tenantId,
|
|
300
|
-
workspaceId: inherited.workspaceId,
|
|
301
|
-
source: "project_mapped_topic"
|
|
302
|
-
};
|
|
303
|
-
}
|
|
304
|
-
const nodeScope = await resolveTopicNodeScopeOrNull(
|
|
305
|
-
ctx,
|
|
306
|
-
String(args.projectId)
|
|
307
|
-
);
|
|
308
|
-
if (nodeScope) {
|
|
309
|
-
return {
|
|
310
|
-
...nodeScope,
|
|
311
|
-
projectId: nodeScope.projectId ?? String(args.projectId)
|
|
312
|
-
};
|
|
313
|
-
}
|
|
314
|
-
throw new Error(
|
|
315
|
-
`Legacy project scope ${String(args.projectId)} has no mapped topic.`
|
|
316
|
-
);
|
|
317
|
-
}
|
|
318
|
-
throw new Error(
|
|
319
|
-
"Missing scope: provide topicId (preferred) or legacy projectId alias."
|
|
320
|
-
);
|
|
321
|
-
}
|
|
322
|
-
var optionalScopeArgs = {
|
|
323
|
-
projectId: v.optional(v.string()),
|
|
324
|
-
topicId: v.optional(v.string())
|
|
325
|
-
};
|
|
326
|
-
|
|
327
|
-
// src/workspaceIsolation.ts
|
|
328
|
-
function normalizeScopeValue2(value) {
|
|
329
|
-
if (typeof value !== "string") {
|
|
330
|
-
return;
|
|
331
|
-
}
|
|
332
|
-
const normalized = value.trim();
|
|
333
|
-
return normalized.length > 0 ? normalized : void 0;
|
|
334
|
-
}
|
|
335
|
-
function throwWorkspaceIsolationError(args) {
|
|
336
|
-
const error = new Error(args.message);
|
|
337
|
-
error.status = 409;
|
|
338
|
-
error.code = "INVARIANT_VIOLATION";
|
|
339
|
-
error.invariantCode = args.invariantCode;
|
|
340
|
-
error.suggestion = args.suggestion;
|
|
341
|
-
error.details = args.details;
|
|
342
|
-
throw error;
|
|
343
|
-
}
|
|
344
|
-
function assertWorkspaceScopedEpistemicNodeScope(args) {
|
|
345
|
-
const layer = isNodeType(args.nodeType) ? getLayerForNodeType(args.nodeType) : void 0;
|
|
346
|
-
if (layer === "ontological") {
|
|
347
|
-
return;
|
|
348
|
-
}
|
|
349
|
-
const workspaceId = normalizeScopeValue2(args.scope.workspaceId);
|
|
350
|
-
if (workspaceId) {
|
|
351
|
-
return;
|
|
352
|
-
}
|
|
353
|
-
throwWorkspaceIsolationError({
|
|
354
|
-
message: "Workspace-scoped reasoning isolation requires workspaceId on non-ontological node creation.",
|
|
355
|
-
invariantCode: "workspace.scope_required_for_epistemic_nodes",
|
|
356
|
-
suggestion: "Resolve the topic/project scope through a workspace-bound topic before creating epistemic nodes.",
|
|
357
|
-
details: {
|
|
358
|
-
mutationName: args.mutationName,
|
|
359
|
-
nodeType: args.nodeType,
|
|
360
|
-
topicId: args.scope.topicId,
|
|
361
|
-
projectId: args.scope.projectId
|
|
362
|
-
}
|
|
363
|
-
});
|
|
364
|
-
}
|
|
365
|
-
function resolveRuntimePackMutationContext(args) {
|
|
366
|
-
if (!args.runtimeToolName && !args.runtimePackKey && !args.runtimePackInstallScope) {
|
|
367
|
-
return;
|
|
368
|
-
}
|
|
369
|
-
return {
|
|
370
|
-
toolName: args.runtimeToolName,
|
|
371
|
-
packKey: args.runtimePackKey,
|
|
372
|
-
packInstallScope: args.runtimePackInstallScope
|
|
373
|
-
};
|
|
374
|
-
}
|
|
375
|
-
function assertTenantPackWorkspaceMutationAllowed(args) {
|
|
376
|
-
if (!args.runtime?.packKey || args.runtime.packInstallScope !== "tenant") {
|
|
377
|
-
return;
|
|
378
|
-
}
|
|
379
|
-
const targetWorkspaceId = normalizeScopeValue2(args.target.workspaceId);
|
|
380
|
-
const targetLayer = typeof args.target.epistemicLayer === "string" ? args.target.epistemicLayer : void 0;
|
|
381
|
-
if (!targetWorkspaceId || targetLayer === "ontological") {
|
|
382
|
-
return;
|
|
383
|
-
}
|
|
384
|
-
throwWorkspaceIsolationError({
|
|
385
|
-
message: `Tenant-scoped pack "${args.runtime.packKey}" cannot mutate workspace-scoped reasoning state.`,
|
|
386
|
-
invariantCode: "workspace.tenant_pack_reasoning_write_forbidden",
|
|
387
|
-
suggestion: "Use a workspace-scoped pack for workspace-local graph mutations, or route the change through tenant-global canonical entity flows.",
|
|
388
|
-
details: {
|
|
389
|
-
mutationName: args.mutationName,
|
|
390
|
-
toolName: args.runtime.toolName,
|
|
391
|
-
packKey: args.runtime.packKey,
|
|
392
|
-
targetWorkspaceId,
|
|
393
|
-
targetNodeType: args.target.nodeType,
|
|
394
|
-
targetLayer
|
|
395
|
-
}
|
|
396
|
-
});
|
|
397
|
-
}
|
|
398
58
|
|
|
399
59
|
// src/topicProjectOverlay.ts
|
|
400
|
-
var
|
|
60
|
+
var LEGACY_SCOPE_FIELD = "graphScopeProjectId";
|
|
401
61
|
function readNonEmptyString(value) {
|
|
402
62
|
if (typeof value !== "string") {
|
|
403
63
|
return;
|
|
@@ -414,11 +74,15 @@ function readStringArray(value) {
|
|
|
414
74
|
function readMetadata(topic) {
|
|
415
75
|
return topic.metadata && typeof topic.metadata === "object" ? topic.metadata : {};
|
|
416
76
|
}
|
|
77
|
+
function omitMetadataKey(metadata, key) {
|
|
78
|
+
const { [key]: _omitted, ...rest } = metadata;
|
|
79
|
+
return rest;
|
|
80
|
+
}
|
|
417
81
|
function readLegacyProjectId(value) {
|
|
418
82
|
if (!value) {
|
|
419
83
|
return;
|
|
420
84
|
}
|
|
421
|
-
return readNonEmptyString(value[
|
|
85
|
+
return readNonEmptyString(value[LEGACY_SCOPE_FIELD]);
|
|
422
86
|
}
|
|
423
87
|
function coerceVisibility(value) {
|
|
424
88
|
return value === "private" || value === "team" || value === "firm" || value === "external" || value === "public" ? value : void 0;
|
|
@@ -494,9 +158,12 @@ async function resolveTopicDoc(ctx, scopeId) {
|
|
|
494
158
|
);
|
|
495
159
|
}
|
|
496
160
|
try {
|
|
497
|
-
const topic = await ctx.runQuery(
|
|
498
|
-
|
|
499
|
-
|
|
161
|
+
const topic = await ctx.runQuery(
|
|
162
|
+
api.topics.getByLegacyScopeId,
|
|
163
|
+
{
|
|
164
|
+
projectId: String(scopeId)
|
|
165
|
+
}
|
|
166
|
+
);
|
|
500
167
|
if (topic?.name !== void 0 && topic?.type !== void 0) {
|
|
501
168
|
return topic;
|
|
502
169
|
}
|
|
@@ -516,8 +183,18 @@ function materializeTopicProjectOverlay(topic, idMode = "legacy") {
|
|
|
516
183
|
const outwardId = idMode === "topic" ? topicId : storageProjectId;
|
|
517
184
|
const visibility = coerceVisibility(topic.visibility) || coerceVisibility(metadata.visibility) || "private";
|
|
518
185
|
const status = coerceStatus(topic.status) || coerceStatus(metadata.status) || "active";
|
|
519
|
-
|
|
520
|
-
|
|
186
|
+
let createdAt = 0;
|
|
187
|
+
if (typeof topic.createdAt === "number") {
|
|
188
|
+
createdAt = topic.createdAt;
|
|
189
|
+
} else if (typeof topic._creationTime === "number") {
|
|
190
|
+
createdAt = topic._creationTime;
|
|
191
|
+
}
|
|
192
|
+
let updatedAt = createdAt;
|
|
193
|
+
if (typeof topic.updatedAt === "number") {
|
|
194
|
+
updatedAt = topic.updatedAt;
|
|
195
|
+
} else if (typeof metadata.updatedAt === "number") {
|
|
196
|
+
updatedAt = metadata.updatedAt;
|
|
197
|
+
}
|
|
521
198
|
return {
|
|
522
199
|
...metadata,
|
|
523
200
|
_id: outwardId,
|
|
@@ -586,90 +263,113 @@ async function patchTopicProjectOverlay(ctx, scopeId, value) {
|
|
|
586
263
|
if (!topic) {
|
|
587
264
|
return null;
|
|
588
265
|
}
|
|
589
|
-
const
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
266
|
+
const plan = buildTopicProjectOverlayPatchPlan(topic, value);
|
|
267
|
+
await applyTopicProjectOverlayPatch(ctx, topic, plan);
|
|
268
|
+
return materializeTopicProjectOverlay({
|
|
269
|
+
...topic,
|
|
270
|
+
...plan.patch,
|
|
271
|
+
metadata: plan.nextMetadata
|
|
272
|
+
});
|
|
273
|
+
}
|
|
274
|
+
function buildTopicProjectOverlayPatchPlan(topic, value) {
|
|
275
|
+
const plan = {
|
|
276
|
+
nextMetadata: { ...readMetadata(topic) },
|
|
277
|
+
patch: {},
|
|
278
|
+
topicUpdateArgs: {
|
|
279
|
+
id: String(topic._id)
|
|
280
|
+
}
|
|
593
281
|
};
|
|
594
282
|
for (const [key, rawValue] of Object.entries(value)) {
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
default:
|
|
642
|
-
if (rawValue === void 0) {
|
|
643
|
-
delete nextMetadata[key];
|
|
644
|
-
} else {
|
|
645
|
-
nextMetadata[key] = rawValue;
|
|
646
|
-
}
|
|
647
|
-
}
|
|
283
|
+
applyTopicProjectOverlayPatchEntry(plan, key, rawValue);
|
|
284
|
+
}
|
|
285
|
+
plan.patch.updatedAt = Date.now();
|
|
286
|
+
plan.patch.metadata = plan.nextMetadata;
|
|
287
|
+
plan.topicUpdateArgs.metadata = plan.nextMetadata;
|
|
288
|
+
return plan;
|
|
289
|
+
}
|
|
290
|
+
function applyTopicProjectOverlayPatchEntry(plan, key, rawValue) {
|
|
291
|
+
switch (key) {
|
|
292
|
+
case "_id":
|
|
293
|
+
case "projectId":
|
|
294
|
+
case "topicId":
|
|
295
|
+
case "legacyProjectId":
|
|
296
|
+
case "storageProjectId":
|
|
297
|
+
case "updatedAt":
|
|
298
|
+
case "createdAt":
|
|
299
|
+
return;
|
|
300
|
+
case "name":
|
|
301
|
+
case "description":
|
|
302
|
+
plan.patch[key] = rawValue;
|
|
303
|
+
plan.topicUpdateArgs[key] = rawValue;
|
|
304
|
+
return;
|
|
305
|
+
case "tenantId":
|
|
306
|
+
case "workspaceId":
|
|
307
|
+
case "ownerId":
|
|
308
|
+
throw new Error(
|
|
309
|
+
`patchTopicProjectOverlay cannot mutate ${key} via component-owned topics`
|
|
310
|
+
);
|
|
311
|
+
case "status":
|
|
312
|
+
applyTopicStatusPatch(plan, rawValue);
|
|
313
|
+
return;
|
|
314
|
+
case "visibility":
|
|
315
|
+
applyTopicVisibilityPatch(plan, rawValue);
|
|
316
|
+
return;
|
|
317
|
+
case "type":
|
|
318
|
+
applyTopicProjectTypePatch(plan, rawValue);
|
|
319
|
+
return;
|
|
320
|
+
default:
|
|
321
|
+
applyTopicMetadataPatch(plan, key, rawValue);
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
function applyTopicStatusPatch(plan, rawValue) {
|
|
325
|
+
const status = coerceStatus(rawValue);
|
|
326
|
+
if (status) {
|
|
327
|
+
plan.patch.status = status;
|
|
328
|
+
plan.topicUpdateArgs.status = status;
|
|
648
329
|
}
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
330
|
+
}
|
|
331
|
+
function applyTopicVisibilityPatch(plan, rawValue) {
|
|
332
|
+
const visibility = coerceVisibility(rawValue);
|
|
333
|
+
if (visibility) {
|
|
334
|
+
plan.patch.visibility = visibility;
|
|
335
|
+
plan.topicUpdateArgs.visibility = visibility;
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
function applyTopicProjectTypePatch(plan, rawValue) {
|
|
339
|
+
const projectType = readNonEmptyString(rawValue);
|
|
340
|
+
if (projectType) {
|
|
341
|
+
plan.nextMetadata.projectType = projectType;
|
|
342
|
+
return;
|
|
343
|
+
}
|
|
344
|
+
plan.nextMetadata = omitMetadataKey(plan.nextMetadata, "projectType");
|
|
345
|
+
}
|
|
346
|
+
function applyTopicMetadataPatch(plan, key, rawValue) {
|
|
347
|
+
if (rawValue === void 0) {
|
|
348
|
+
plan.nextMetadata = omitMetadataKey(plan.nextMetadata, key);
|
|
349
|
+
return;
|
|
350
|
+
}
|
|
351
|
+
plan.nextMetadata[key] = rawValue;
|
|
352
|
+
}
|
|
353
|
+
async function applyTopicProjectOverlayPatch(ctx, topic, plan) {
|
|
652
354
|
if (typeof ctx.runMutation === "function") {
|
|
653
355
|
try {
|
|
654
|
-
await ctx.runMutation(api.topics.update, topicUpdateArgs);
|
|
356
|
+
await ctx.runMutation(api.topics.update, plan.topicUpdateArgs);
|
|
655
357
|
} catch (error) {
|
|
656
|
-
if (!
|
|
358
|
+
if (!canPatchTopicViaLocalDb(ctx, error)) {
|
|
657
359
|
throw error;
|
|
658
360
|
}
|
|
659
|
-
await ctx.db.patch(
|
|
361
|
+
await ctx.db.patch(topic._id, plan.patch);
|
|
660
362
|
}
|
|
661
363
|
} else if (ctx?.db && typeof ctx.db.patch === "function") {
|
|
662
|
-
await ctx.db.patch(
|
|
364
|
+
await ctx.db.patch(topic._id, plan.patch);
|
|
663
365
|
} else {
|
|
664
366
|
throw new Error(
|
|
665
367
|
"Cannot patch topic without component adapter (ctx.runMutation unavailable)"
|
|
666
368
|
);
|
|
667
369
|
}
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
metadata: nextMetadata
|
|
672
|
-
});
|
|
370
|
+
}
|
|
371
|
+
function canPatchTopicViaLocalDb(ctx, error) {
|
|
372
|
+
return isMissingLucernChildComponentError(error) && Boolean(ctx?.db) && typeof ctx.db?.patch === "function";
|
|
673
373
|
}
|
|
674
374
|
|
|
675
375
|
// src/resolvers.ts
|
|
@@ -697,7 +397,7 @@ async function patchProjectWithTolerance(ctx, projectId, value) {
|
|
|
697
397
|
try {
|
|
698
398
|
await patchTopicProjectOverlay(ctx, projectId, value);
|
|
699
399
|
} catch (error) {
|
|
700
|
-
if (!isAdvisoryTopicPatch(value)
|
|
400
|
+
if (!(isAdvisoryTopicPatch(value) && isMissingLucernChildComponentError2(error))) {
|
|
701
401
|
throw error;
|
|
702
402
|
}
|
|
703
403
|
console.warn(
|
|
@@ -710,26 +410,276 @@ async function patchProjectWithTolerance(ctx, projectId, value) {
|
|
|
710
410
|
);
|
|
711
411
|
}
|
|
712
412
|
}
|
|
713
|
-
function defaultResolvers() {
|
|
714
|
-
return {
|
|
715
|
-
getProject: (ctx, projectId) => resolveTopicProjectOverlay(ctx, projectId, {
|
|
716
|
-
idMode: "legacy",
|
|
717
|
-
projectLikeOnly: false
|
|
718
|
-
}),
|
|
719
|
-
patchProject: (ctx, projectId, value) => patchProjectWithTolerance(ctx, projectId, value),
|
|
720
|
-
listTopics: (ctx) => listTopicProjectOverlays(ctx, {
|
|
721
|
-
idMode: "legacy"
|
|
722
|
-
}),
|
|
723
|
-
getFinalArtifact: (ctx, artifactId) => ctx.db.get(artifactId)
|
|
724
|
-
};
|
|
413
|
+
function defaultResolvers() {
|
|
414
|
+
return {
|
|
415
|
+
getProject: (ctx, projectId) => resolveTopicProjectOverlay(ctx, projectId, {
|
|
416
|
+
idMode: "legacy",
|
|
417
|
+
projectLikeOnly: false
|
|
418
|
+
}),
|
|
419
|
+
patchProject: (ctx, projectId, value) => patchProjectWithTolerance(ctx, projectId, value),
|
|
420
|
+
listTopics: (ctx) => listTopicProjectOverlays(ctx, {
|
|
421
|
+
idMode: "legacy"
|
|
422
|
+
}),
|
|
423
|
+
getFinalArtifact: (ctx, artifactId) => ctx.db.get(artifactId)
|
|
424
|
+
};
|
|
425
|
+
}
|
|
426
|
+
var resolverOverrides = {};
|
|
427
|
+
function resolveGraphPrimitivesAppResolvers(_ctx) {
|
|
428
|
+
return {
|
|
429
|
+
...defaultResolvers(),
|
|
430
|
+
...resolverOverrides
|
|
431
|
+
};
|
|
432
|
+
}
|
|
433
|
+
var LEGACY_SCOPE_FIELD2 = "graphScopeProjectId";
|
|
434
|
+
async function resolveTopicNodeScopeOrNull(ctx, ref) {
|
|
435
|
+
if (!ctx?.db || typeof ctx.db.query !== "function") {
|
|
436
|
+
return null;
|
|
437
|
+
}
|
|
438
|
+
let node = null;
|
|
439
|
+
try {
|
|
440
|
+
const byGlobalId = await ctx.db.query("epistemicNodes").withIndex("by_globalId", (q) => q.eq("globalId", ref)).first();
|
|
441
|
+
if (byGlobalId && byGlobalId.nodeType === "topic") {
|
|
442
|
+
node = byGlobalId;
|
|
443
|
+
}
|
|
444
|
+
} catch (error) {
|
|
445
|
+
debugGraphPrimitiveFallback(
|
|
446
|
+
"[topicScope] topic-node scope lookup by globalId failed",
|
|
447
|
+
{ error, ref }
|
|
448
|
+
);
|
|
449
|
+
}
|
|
450
|
+
if (!node) {
|
|
451
|
+
return null;
|
|
452
|
+
}
|
|
453
|
+
const scopeKey = normalizeScopeValue(node.topicId) ?? normalizeScopeValue(node.globalId);
|
|
454
|
+
if (!scopeKey) {
|
|
455
|
+
return null;
|
|
456
|
+
}
|
|
457
|
+
return {
|
|
458
|
+
topicId: scopeKey,
|
|
459
|
+
projectId: asMappedProjectId(node),
|
|
460
|
+
source: "topic_node"
|
|
461
|
+
};
|
|
462
|
+
}
|
|
463
|
+
function asMappedProjectId(topic) {
|
|
464
|
+
if (!topic) {
|
|
465
|
+
return;
|
|
466
|
+
}
|
|
467
|
+
const directLegacyProjectId = normalizeScopeValue(
|
|
468
|
+
topic[LEGACY_SCOPE_FIELD2]
|
|
469
|
+
);
|
|
470
|
+
if (directLegacyProjectId) {
|
|
471
|
+
return directLegacyProjectId;
|
|
472
|
+
}
|
|
473
|
+
const metadata = topic.metadata || {};
|
|
474
|
+
const candidate = metadata[LEGACY_SCOPE_FIELD2] || metadata.legacyProjectId || metadata.projectId || metadata.scopeProjectId;
|
|
475
|
+
return typeof candidate === "string" ? normalizeScopeValue(candidate) : void 0;
|
|
476
|
+
}
|
|
477
|
+
function normalizeScopeValue(value) {
|
|
478
|
+
if (typeof value !== "string") {
|
|
479
|
+
return;
|
|
480
|
+
}
|
|
481
|
+
const normalized = value.trim();
|
|
482
|
+
return normalized.length > 0 ? normalized : void 0;
|
|
483
|
+
}
|
|
484
|
+
function pickPrimaryTopic(candidates) {
|
|
485
|
+
return [...candidates].sort((a, b) => {
|
|
486
|
+
const depthA = a.depth ?? 9999;
|
|
487
|
+
const depthB = b.depth ?? 9999;
|
|
488
|
+
if (depthA !== depthB) {
|
|
489
|
+
return depthA - depthB;
|
|
490
|
+
}
|
|
491
|
+
const createdA = a.createdAt ?? Number.MAX_SAFE_INTEGER;
|
|
492
|
+
const createdB = b.createdAt ?? Number.MAX_SAFE_INTEGER;
|
|
493
|
+
if (createdA !== createdB) {
|
|
494
|
+
return createdA - createdB;
|
|
495
|
+
}
|
|
496
|
+
return String(a.name || "").localeCompare(String(b.name || ""));
|
|
497
|
+
})[0];
|
|
498
|
+
}
|
|
499
|
+
async function findTopicsByScopeAlias(ctx, scopeId) {
|
|
500
|
+
const query = ctx.db.query("topics");
|
|
501
|
+
try {
|
|
502
|
+
return await query.withIndex(
|
|
503
|
+
"by_graph_scope_project",
|
|
504
|
+
(q) => q.eq(LEGACY_SCOPE_FIELD2, scopeId)
|
|
505
|
+
).collect();
|
|
506
|
+
} catch (error) {
|
|
507
|
+
debugGraphPrimitiveFallback(
|
|
508
|
+
"[topicScope] Failed to resolve scope alias via index",
|
|
509
|
+
{
|
|
510
|
+
error,
|
|
511
|
+
scopeId
|
|
512
|
+
}
|
|
513
|
+
);
|
|
514
|
+
const topics = await query.collect();
|
|
515
|
+
return topics.filter((topic) => {
|
|
516
|
+
const normalizedGlobalId = normalizeScopeValue(topic.globalId);
|
|
517
|
+
const mappedProjectId = asMappedProjectId(topic);
|
|
518
|
+
return String(topic._id) === scopeId || normalizedGlobalId === scopeId || mappedProjectId === scopeId;
|
|
519
|
+
});
|
|
520
|
+
}
|
|
521
|
+
}
|
|
522
|
+
async function tryResolveHostTopicById(ctx, topicId) {
|
|
523
|
+
if (typeof ctx.runQuery !== "function") {
|
|
524
|
+
return null;
|
|
525
|
+
}
|
|
526
|
+
try {
|
|
527
|
+
return await ctx.runQuery(api.topics.get, {
|
|
528
|
+
id: topicId
|
|
529
|
+
}) ?? null;
|
|
530
|
+
} catch (error) {
|
|
531
|
+
debugGraphPrimitiveFallback(
|
|
532
|
+
"[topicScope] Failed to resolve topic by host query",
|
|
533
|
+
{
|
|
534
|
+
error,
|
|
535
|
+
topicId
|
|
536
|
+
}
|
|
537
|
+
);
|
|
538
|
+
return null;
|
|
539
|
+
}
|
|
540
|
+
}
|
|
541
|
+
async function tryResolveHostTopicByLegacyScope(ctx, legacyScopeId) {
|
|
542
|
+
if (typeof ctx.runQuery !== "function") {
|
|
543
|
+
return null;
|
|
544
|
+
}
|
|
545
|
+
try {
|
|
546
|
+
return await ctx.runQuery(api.topics.getByLegacyScopeId, {
|
|
547
|
+
projectId: legacyScopeId
|
|
548
|
+
}) ?? null;
|
|
549
|
+
} catch (error) {
|
|
550
|
+
debugGraphPrimitiveFallback(
|
|
551
|
+
"[topicScope] Failed to resolve topic by legacy scope",
|
|
552
|
+
{
|
|
553
|
+
error,
|
|
554
|
+
legacyScopeId
|
|
555
|
+
}
|
|
556
|
+
);
|
|
557
|
+
return null;
|
|
558
|
+
}
|
|
559
|
+
}
|
|
560
|
+
async function resolveInheritedWorkspaceScope(ctx, topic) {
|
|
561
|
+
const MAX_DEPTH = 10;
|
|
562
|
+
let tenantId = normalizeScopeValue(topic.tenantId);
|
|
563
|
+
let workspaceId = normalizeScopeValue(topic.workspaceId);
|
|
564
|
+
if (tenantId && workspaceId) {
|
|
565
|
+
return { tenantId, workspaceId };
|
|
566
|
+
}
|
|
567
|
+
let current = topic;
|
|
568
|
+
for (let i = 0; i < MAX_DEPTH && current?.parentTopicId; i++) {
|
|
569
|
+
current = await ctx.db.get(current.parentTopicId);
|
|
570
|
+
if (!current) {
|
|
571
|
+
break;
|
|
572
|
+
}
|
|
573
|
+
if (!tenantId) {
|
|
574
|
+
tenantId = normalizeScopeValue(current.tenantId);
|
|
575
|
+
}
|
|
576
|
+
if (!workspaceId) {
|
|
577
|
+
workspaceId = normalizeScopeValue(current.workspaceId);
|
|
578
|
+
}
|
|
579
|
+
if (tenantId && workspaceId) {
|
|
580
|
+
break;
|
|
581
|
+
}
|
|
582
|
+
}
|
|
583
|
+
return { tenantId, workspaceId };
|
|
584
|
+
}
|
|
585
|
+
async function resolveTopicProjectScope(ctx, args) {
|
|
586
|
+
if (args.topicId) {
|
|
587
|
+
return await resolveScopeFromTopicId(ctx, args.topicId);
|
|
588
|
+
}
|
|
589
|
+
if (args.projectId) {
|
|
590
|
+
return await resolveScopeFromLegacyProjectId(ctx, args.projectId);
|
|
591
|
+
}
|
|
592
|
+
throw new Error(
|
|
593
|
+
"Missing scope: provide topicId (preferred) or legacy projectId alias."
|
|
594
|
+
);
|
|
595
|
+
}
|
|
596
|
+
async function resolveScopeFromTopicId(ctx, topicId) {
|
|
597
|
+
const topic = await resolveTopicDocFromTopicId(ctx, topicId);
|
|
598
|
+
if (topic) {
|
|
599
|
+
return await buildTopicScope(ctx, topic, "topic");
|
|
600
|
+
}
|
|
601
|
+
const nodeScope = await resolveTopicNodeScopeOrNull(ctx, String(topicId));
|
|
602
|
+
if (nodeScope) {
|
|
603
|
+
return nodeScope;
|
|
604
|
+
}
|
|
605
|
+
throw new Error(`Topic not found: ${String(topicId)}`);
|
|
606
|
+
}
|
|
607
|
+
async function resolveTopicDocFromTopicId(ctx, topicId) {
|
|
608
|
+
const direct = await tryReadTopicDoc(ctx, topicId, {
|
|
609
|
+
failureLog: "[topicScope] Failed to load topic by direct id",
|
|
610
|
+
idLogKey: "topicId"
|
|
611
|
+
});
|
|
612
|
+
if (direct) {
|
|
613
|
+
return direct;
|
|
614
|
+
}
|
|
615
|
+
const hostTopic = await tryResolveHostTopicById(ctx, String(topicId));
|
|
616
|
+
if (hostTopic) {
|
|
617
|
+
return hostTopic;
|
|
618
|
+
}
|
|
619
|
+
return pickPrimaryTopic(await findTopicsByScopeAlias(ctx, String(topicId))) ?? null;
|
|
620
|
+
}
|
|
621
|
+
async function resolveScopeFromLegacyProjectId(ctx, legacyProjectId) {
|
|
622
|
+
const directTopic = await resolveDirectLegacyProjectTopic(
|
|
623
|
+
ctx,
|
|
624
|
+
legacyProjectId
|
|
625
|
+
);
|
|
626
|
+
if (directTopic) {
|
|
627
|
+
return await buildTopicScope(ctx, directTopic, "topic_inferred", {
|
|
628
|
+
fallbackProjectId: legacyProjectId
|
|
629
|
+
});
|
|
630
|
+
}
|
|
631
|
+
const primary = pickPrimaryTopic(
|
|
632
|
+
await findTopicsByScopeAlias(ctx, legacyProjectId)
|
|
633
|
+
);
|
|
634
|
+
if (primary) {
|
|
635
|
+
return await buildTopicScope(ctx, primary, "project_mapped_topic", {
|
|
636
|
+
fallbackProjectId: legacyProjectId
|
|
637
|
+
});
|
|
638
|
+
}
|
|
639
|
+
const nodeScope = await resolveTopicNodeScopeOrNull(ctx, legacyProjectId);
|
|
640
|
+
if (nodeScope) {
|
|
641
|
+
return {
|
|
642
|
+
...nodeScope,
|
|
643
|
+
projectId: nodeScope.projectId ?? legacyProjectId
|
|
644
|
+
};
|
|
645
|
+
}
|
|
646
|
+
throw new Error(
|
|
647
|
+
`Legacy project scope ${legacyProjectId} has no mapped topic.`
|
|
648
|
+
);
|
|
649
|
+
}
|
|
650
|
+
async function resolveDirectLegacyProjectTopic(ctx, legacyProjectId) {
|
|
651
|
+
const directTopic = await tryReadTopicDoc(ctx, legacyProjectId, {
|
|
652
|
+
failureLog: "[topicScope] Failed to load direct project topic",
|
|
653
|
+
idLogKey: "projectId"
|
|
654
|
+
});
|
|
655
|
+
return directTopic ?? tryResolveHostTopicByLegacyScope(ctx, legacyProjectId);
|
|
725
656
|
}
|
|
726
|
-
|
|
727
|
-
|
|
657
|
+
async function tryReadTopicDoc(ctx, id, log) {
|
|
658
|
+
try {
|
|
659
|
+
return await ctx.db.get(id);
|
|
660
|
+
} catch (error) {
|
|
661
|
+
debugGraphPrimitiveFallback(log.failureLog, {
|
|
662
|
+
error,
|
|
663
|
+
[log.idLogKey]: id
|
|
664
|
+
});
|
|
665
|
+
return null;
|
|
666
|
+
}
|
|
667
|
+
}
|
|
668
|
+
async function buildTopicScope(ctx, topic, source, options = {}) {
|
|
669
|
+
const inherited = await resolveInheritedWorkspaceScope(ctx, topic);
|
|
670
|
+
const mapped = asMappedProjectId(topic);
|
|
728
671
|
return {
|
|
729
|
-
|
|
730
|
-
...
|
|
672
|
+
topicId: topic._id,
|
|
673
|
+
...mapped || options.fallbackProjectId ? { projectId: mapped ?? options.fallbackProjectId } : {},
|
|
674
|
+
tenantId: inherited.tenantId,
|
|
675
|
+
workspaceId: inherited.workspaceId,
|
|
676
|
+
source
|
|
731
677
|
};
|
|
732
678
|
}
|
|
679
|
+
var optionalScopeArgs = {
|
|
680
|
+
projectId: v.optional(v.string()),
|
|
681
|
+
topicId: v.optional(v.string())
|
|
682
|
+
};
|
|
733
683
|
|
|
734
684
|
// src/epistemicEvidenceHelpers.ts
|
|
735
685
|
var optionalEvidenceScopeArgs = optionalScopeArgs;
|
|
@@ -765,7 +715,7 @@ function normalizeSourceType(sourceType) {
|
|
|
765
715
|
async function markProjectGraphDirty(ctx, projectId, topicId) {
|
|
766
716
|
const normalizedProjectId = typeof projectId === "string" && projectId.trim().length > 0 ? projectId : void 0;
|
|
767
717
|
const normalizedTopicId = typeof topicId === "string" && topicId.trim().length > 0 ? topicId : void 0;
|
|
768
|
-
if (!normalizedProjectId
|
|
718
|
+
if (!(normalizedProjectId || normalizedTopicId)) {
|
|
769
719
|
return;
|
|
770
720
|
}
|
|
771
721
|
if (normalizedProjectId) {
|
|
@@ -786,16 +736,22 @@ async function markProjectGraphDirty(ctx, projectId, topicId) {
|
|
|
786
736
|
}
|
|
787
737
|
);
|
|
788
738
|
}
|
|
739
|
+
const activityScopeId = normalizedTopicId ?? normalizedProjectId;
|
|
740
|
+
if (!activityScopeId) {
|
|
741
|
+
throw new Error(
|
|
742
|
+
"Expected evidence graph dirty scope to include a topic or project id."
|
|
743
|
+
);
|
|
744
|
+
}
|
|
789
745
|
await resolveGraphPrimitivesAppResolvers().patchProject(
|
|
790
746
|
ctx,
|
|
791
|
-
|
|
747
|
+
activityScopeId,
|
|
792
748
|
{
|
|
793
749
|
lastActivityAt: Date.now()
|
|
794
750
|
}
|
|
795
751
|
);
|
|
796
752
|
}
|
|
797
753
|
async function resolveEvidenceScopeOrNull(ctx, args) {
|
|
798
|
-
if (!args.projectId
|
|
754
|
+
if (!(args.projectId || args.topicId)) {
|
|
799
755
|
return null;
|
|
800
756
|
}
|
|
801
757
|
try {
|
|
@@ -815,13 +771,16 @@ async function resolveEvidenceScopeOrNull(ctx, args) {
|
|
|
815
771
|
return null;
|
|
816
772
|
}
|
|
817
773
|
}
|
|
818
|
-
|
|
774
|
+
function insertEpistemicNode(ctx, doc) {
|
|
819
775
|
assertUuidV7Identity("epistemicNodes", doc.globalId);
|
|
820
776
|
return ctx.db.insert("epistemicNodes", doc);
|
|
821
777
|
}
|
|
822
778
|
async function assertExistingNodeEndpoint(ctx, endpointRole, endpoint) {
|
|
823
779
|
assertUuidShapedEdgeEndpoint(endpointRole, endpoint);
|
|
824
|
-
const node = await ctx.db.query("epistemicNodes").withIndex(
|
|
780
|
+
const node = await ctx.db.query("epistemicNodes").withIndex(
|
|
781
|
+
"by_globalId",
|
|
782
|
+
(q) => q.eq("globalId", endpoint)
|
|
783
|
+
).first();
|
|
825
784
|
if (!node) {
|
|
826
785
|
throw new Error(
|
|
827
786
|
`edge_endpoint_not_canonical: epistemicEdges insert requires ${endpointRole} to be the globalId of an existing epistemicNodes row, received ${endpoint} (no node with that globalId)`
|
|
@@ -861,8 +820,188 @@ async function insertEpistemicEdge(ctx, doc) {
|
|
|
861
820
|
}
|
|
862
821
|
return ctx.db.insert("epistemicEdges", doc);
|
|
863
822
|
}
|
|
823
|
+
function normalizeScopeValue2(value) {
|
|
824
|
+
if (typeof value !== "string") {
|
|
825
|
+
return;
|
|
826
|
+
}
|
|
827
|
+
const normalized = value.trim();
|
|
828
|
+
return normalized.length > 0 ? normalized : void 0;
|
|
829
|
+
}
|
|
830
|
+
function throwWorkspaceIsolationError(args) {
|
|
831
|
+
const error = new Error(args.message);
|
|
832
|
+
error.status = 409;
|
|
833
|
+
error.code = "INVARIANT_VIOLATION";
|
|
834
|
+
error.invariantCode = args.invariantCode;
|
|
835
|
+
error.suggestion = args.suggestion;
|
|
836
|
+
error.details = args.details;
|
|
837
|
+
throw error;
|
|
838
|
+
}
|
|
839
|
+
function assertWorkspaceScopedEpistemicNodeScope(args) {
|
|
840
|
+
const layer = isNodeType(args.nodeType) ? getLayerForNodeType(args.nodeType) : void 0;
|
|
841
|
+
if (layer === "ontological") {
|
|
842
|
+
return;
|
|
843
|
+
}
|
|
844
|
+
const workspaceId = normalizeScopeValue2(args.scope.workspaceId);
|
|
845
|
+
if (workspaceId) {
|
|
846
|
+
return;
|
|
847
|
+
}
|
|
848
|
+
throwWorkspaceIsolationError({
|
|
849
|
+
message: "Workspace-scoped reasoning isolation requires workspaceId on non-ontological node creation.",
|
|
850
|
+
invariantCode: "workspace.scope_required_for_epistemic_nodes",
|
|
851
|
+
suggestion: "Resolve the topic/project scope through a workspace-bound topic before creating epistemic nodes.",
|
|
852
|
+
details: {
|
|
853
|
+
mutationName: args.mutationName,
|
|
854
|
+
nodeType: args.nodeType,
|
|
855
|
+
topicId: args.scope.topicId,
|
|
856
|
+
projectId: args.scope.projectId
|
|
857
|
+
}
|
|
858
|
+
});
|
|
859
|
+
}
|
|
860
|
+
function resolveRuntimePackMutationContext(args) {
|
|
861
|
+
if (!(args.runtimeToolName || args.runtimePackKey || args.runtimePackInstallScope)) {
|
|
862
|
+
return;
|
|
863
|
+
}
|
|
864
|
+
return {
|
|
865
|
+
toolName: args.runtimeToolName,
|
|
866
|
+
packKey: args.runtimePackKey,
|
|
867
|
+
packInstallScope: args.runtimePackInstallScope
|
|
868
|
+
};
|
|
869
|
+
}
|
|
870
|
+
function assertTenantPackWorkspaceMutationAllowed(args) {
|
|
871
|
+
if (!args.runtime?.packKey || args.runtime.packInstallScope !== "tenant") {
|
|
872
|
+
return;
|
|
873
|
+
}
|
|
874
|
+
const targetWorkspaceId = normalizeScopeValue2(args.target.workspaceId);
|
|
875
|
+
const targetLayer = typeof args.target.epistemicLayer === "string" ? args.target.epistemicLayer : void 0;
|
|
876
|
+
if (!targetWorkspaceId || targetLayer === "ontological") {
|
|
877
|
+
return;
|
|
878
|
+
}
|
|
879
|
+
throwWorkspaceIsolationError({
|
|
880
|
+
message: `Tenant-scoped pack "${args.runtime.packKey}" cannot mutate workspace-scoped reasoning state.`,
|
|
881
|
+
invariantCode: "workspace.tenant_pack_reasoning_write_forbidden",
|
|
882
|
+
suggestion: "Use a workspace-scoped pack for workspace-local graph mutations, or route the change through tenant-global canonical entity flows.",
|
|
883
|
+
details: {
|
|
884
|
+
mutationName: args.mutationName,
|
|
885
|
+
toolName: args.runtime.toolName,
|
|
886
|
+
packKey: args.runtime.packKey,
|
|
887
|
+
targetWorkspaceId,
|
|
888
|
+
targetNodeType: args.target.nodeType,
|
|
889
|
+
targetLayer
|
|
890
|
+
}
|
|
891
|
+
});
|
|
892
|
+
}
|
|
864
893
|
|
|
865
894
|
// src/epistemicEvidenceMutations.ts
|
|
895
|
+
var EMBEDDING_GENERATION_ACTION = "embeddingActions:generateEpistemicNodeEmbedding";
|
|
896
|
+
function isRecord(value) {
|
|
897
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
898
|
+
}
|
|
899
|
+
function metadataRecord(value) {
|
|
900
|
+
return isRecord(value) ? value : {};
|
|
901
|
+
}
|
|
902
|
+
function readOptionalString(value) {
|
|
903
|
+
if (typeof value !== "string") {
|
|
904
|
+
return;
|
|
905
|
+
}
|
|
906
|
+
const trimmed = value.trim();
|
|
907
|
+
return trimmed.length > 0 ? trimmed : void 0;
|
|
908
|
+
}
|
|
909
|
+
function readConvexId(value) {
|
|
910
|
+
const normalized = readOptionalString(value);
|
|
911
|
+
return normalized;
|
|
912
|
+
}
|
|
913
|
+
function assertBeliefNode(node, errorMessage) {
|
|
914
|
+
if (!isRecord(node) || node.nodeType !== "belief" || typeof node.globalId !== "string") {
|
|
915
|
+
throw new Error(errorMessage);
|
|
916
|
+
}
|
|
917
|
+
return node;
|
|
918
|
+
}
|
|
919
|
+
function assertEvidenceNode(node, errorMessage) {
|
|
920
|
+
if (!isRecord(node)) {
|
|
921
|
+
throw new Error(errorMessage);
|
|
922
|
+
}
|
|
923
|
+
const id = readConvexId(node._id);
|
|
924
|
+
if (!(id && node.nodeType === "evidence")) {
|
|
925
|
+
throw new Error(errorMessage);
|
|
926
|
+
}
|
|
927
|
+
const evidence = { _id: id, nodeType: "evidence" };
|
|
928
|
+
const canonicalText = readOptionalString(node.canonicalText);
|
|
929
|
+
if (canonicalText !== void 0) {
|
|
930
|
+
evidence.canonicalText = canonicalText;
|
|
931
|
+
}
|
|
932
|
+
const createdBy = readOptionalString(node.createdBy);
|
|
933
|
+
if (createdBy !== void 0) {
|
|
934
|
+
evidence.createdBy = createdBy;
|
|
935
|
+
}
|
|
936
|
+
if ("metadata" in node) {
|
|
937
|
+
evidence.metadata = node.metadata;
|
|
938
|
+
}
|
|
939
|
+
const projectId = readOptionalString(node.projectId);
|
|
940
|
+
if (projectId !== void 0) {
|
|
941
|
+
evidence.projectId = projectId;
|
|
942
|
+
}
|
|
943
|
+
const status = readOptionalString(node.status);
|
|
944
|
+
if (status !== void 0) {
|
|
945
|
+
evidence.status = status;
|
|
946
|
+
}
|
|
947
|
+
const topicId = readOptionalString(node.topicId);
|
|
948
|
+
if (topicId !== void 0) {
|
|
949
|
+
evidence.topicId = topicId;
|
|
950
|
+
}
|
|
951
|
+
return evidence;
|
|
952
|
+
}
|
|
953
|
+
function readEvidenceNodeOrNull(node) {
|
|
954
|
+
if (!isRecord(node) || node.nodeType !== "evidence") {
|
|
955
|
+
return null;
|
|
956
|
+
}
|
|
957
|
+
try {
|
|
958
|
+
return assertEvidenceNode(node, "Evidence node not found");
|
|
959
|
+
} catch {
|
|
960
|
+
return null;
|
|
961
|
+
}
|
|
962
|
+
}
|
|
963
|
+
function optionalTrimmedString(value) {
|
|
964
|
+
if (typeof value !== "string") {
|
|
965
|
+
return;
|
|
966
|
+
}
|
|
967
|
+
const trimmed = value.trim();
|
|
968
|
+
return trimmed.length > 0 ? trimmed : void 0;
|
|
969
|
+
}
|
|
970
|
+
async function resolveEvidenceNodeId(ctx, args) {
|
|
971
|
+
if (args.nodeId) {
|
|
972
|
+
return args.nodeId;
|
|
973
|
+
}
|
|
974
|
+
const insightId = readOptionalString(args.insightId);
|
|
975
|
+
if (!insightId) {
|
|
976
|
+
throw new Error("Either nodeId or insightId is required");
|
|
977
|
+
}
|
|
978
|
+
const normalizedId = ctx.db.normalizeId?.("epistemicNodes", insightId) ?? readConvexId(insightId);
|
|
979
|
+
if (normalizedId) {
|
|
980
|
+
try {
|
|
981
|
+
const direct = readEvidenceNodeOrNull(await ctx.db.get(normalizedId));
|
|
982
|
+
if (direct) {
|
|
983
|
+
return direct._id;
|
|
984
|
+
}
|
|
985
|
+
} catch {
|
|
986
|
+
}
|
|
987
|
+
}
|
|
988
|
+
const byGlobalId = readEvidenceNodeOrNull(
|
|
989
|
+
await ctx.db.query("epistemicNodes").withIndex("by_globalId", (q) => q.eq("globalId", insightId)).first()
|
|
990
|
+
);
|
|
991
|
+
if (!byGlobalId) {
|
|
992
|
+
throw new Error("Evidence node not found");
|
|
993
|
+
}
|
|
994
|
+
return byGlobalId._id;
|
|
995
|
+
}
|
|
996
|
+
function optionalLiteral(values, value, fieldName) {
|
|
997
|
+
if (typeof value !== "string" || value.length === 0) {
|
|
998
|
+
return;
|
|
999
|
+
}
|
|
1000
|
+
if (values.includes(value)) {
|
|
1001
|
+
return value;
|
|
1002
|
+
}
|
|
1003
|
+
throw new Error(`Evidence ${fieldName} must be one of: ${values.join(", ")}`);
|
|
1004
|
+
}
|
|
866
1005
|
function assertSignedImpactScore(value, context) {
|
|
867
1006
|
if (typeof value !== "number" || !Number.isFinite(value) || value === 0 || value < -1 || value > 1) {
|
|
868
1007
|
throw new Error(`${context} requires explicit nonzero weight in [-1, 1]`);
|
|
@@ -875,12 +1014,121 @@ function normalizeEvidenceRelation(relation, weight, context) {
|
|
|
875
1014
|
throw new Error(`${context} supports relation requires positive weight`);
|
|
876
1015
|
}
|
|
877
1016
|
if (relation === "contradicts" && weight > 0) {
|
|
878
|
-
throw new Error(
|
|
1017
|
+
throw new Error(
|
|
1018
|
+
`${context} contradicts relation requires negative weight`
|
|
1019
|
+
);
|
|
879
1020
|
}
|
|
880
1021
|
return relation;
|
|
881
1022
|
}
|
|
882
1023
|
return weight < 0 ? "contradicts" : "supports";
|
|
883
1024
|
}
|
|
1025
|
+
function planEvidenceImpact(args, context) {
|
|
1026
|
+
const weight = assertSignedImpactScore(args.weight, context);
|
|
1027
|
+
const signedImpact = args.signedImpact === void 0 ? weight : assertSignedImpactScore(args.signedImpact, `${context} signedImpact`);
|
|
1028
|
+
return {
|
|
1029
|
+
confidence: Math.abs(weight),
|
|
1030
|
+
evidenceRelation: normalizeEvidenceRelation(
|
|
1031
|
+
args.evidenceRelation,
|
|
1032
|
+
weight,
|
|
1033
|
+
context
|
|
1034
|
+
),
|
|
1035
|
+
signedImpact,
|
|
1036
|
+
weight
|
|
1037
|
+
};
|
|
1038
|
+
}
|
|
1039
|
+
function evidenceTaxonomyProjection(args) {
|
|
1040
|
+
const sourceRef = optionalTrimmedString(args.sourceRef);
|
|
1041
|
+
const sourceQuality = optionalLiteral(
|
|
1042
|
+
EVIDENCE_SOURCE_QUALITY_VALUES,
|
|
1043
|
+
args.sourceQuality,
|
|
1044
|
+
"sourceQuality"
|
|
1045
|
+
);
|
|
1046
|
+
const methodology = optionalLiteral(
|
|
1047
|
+
EVIDENCE_METHODOLOGY_VALUES,
|
|
1048
|
+
args.methodology,
|
|
1049
|
+
"methodology"
|
|
1050
|
+
);
|
|
1051
|
+
const informationAsymmetry = optionalLiteral(
|
|
1052
|
+
EVIDENCE_INFORMATION_ASYMMETRY_VALUES,
|
|
1053
|
+
args.informationAsymmetry,
|
|
1054
|
+
"informationAsymmetry"
|
|
1055
|
+
);
|
|
1056
|
+
const sourceDescription = optionalTrimmedString(args.sourceDescription);
|
|
1057
|
+
return {
|
|
1058
|
+
...sourceRef ? { sourceRef } : {},
|
|
1059
|
+
...sourceQuality ? { sourceQuality } : {},
|
|
1060
|
+
...methodology ? { methodology } : {},
|
|
1061
|
+
...informationAsymmetry ? { informationAsymmetry } : {},
|
|
1062
|
+
...sourceDescription ? { sourceDescription } : {}
|
|
1063
|
+
};
|
|
1064
|
+
}
|
|
1065
|
+
function evidenceTextProjection(args) {
|
|
1066
|
+
const title = optionalTrimmedString(args.title);
|
|
1067
|
+
const contentType = optionalTrimmedString(args.contentType);
|
|
1068
|
+
return {
|
|
1069
|
+
...title ? { title } : {},
|
|
1070
|
+
...typeof args.content === "string" && args.content.length > 0 ? { content: args.content } : {},
|
|
1071
|
+
...contentType ? { contentType } : {}
|
|
1072
|
+
};
|
|
1073
|
+
}
|
|
1074
|
+
function evidenceMetadata(args, impact, kind, profile) {
|
|
1075
|
+
const base = {
|
|
1076
|
+
kind,
|
|
1077
|
+
tags: args.tags || [],
|
|
1078
|
+
linkedBeliefNodeId: args.linkedBeliefNodeId,
|
|
1079
|
+
evidenceRelation: impact.evidenceRelation,
|
|
1080
|
+
confidence: impact.confidence,
|
|
1081
|
+
weight: impact.weight,
|
|
1082
|
+
impactScore: impact.weight
|
|
1083
|
+
};
|
|
1084
|
+
const sourceContext = profile === "createAndLink" ? {} : {
|
|
1085
|
+
externalSourceType: args.externalSourceType,
|
|
1086
|
+
sourceUrl: args.sourceUrl,
|
|
1087
|
+
sourceQuestionId: args.sourceQuestionId,
|
|
1088
|
+
rationale: args.rationale
|
|
1089
|
+
};
|
|
1090
|
+
const publicCreateTaxonomy = profile === "create" ? {
|
|
1091
|
+
methodology: args.methodology,
|
|
1092
|
+
informationAsymmetry: args.informationAsymmetry,
|
|
1093
|
+
sourceDescription: args.sourceDescription
|
|
1094
|
+
} : {};
|
|
1095
|
+
return {
|
|
1096
|
+
...base,
|
|
1097
|
+
...sourceContext,
|
|
1098
|
+
...publicCreateTaxonomy,
|
|
1099
|
+
...metadataRecord(args.metadata)
|
|
1100
|
+
};
|
|
1101
|
+
}
|
|
1102
|
+
function buildEvidenceNodeInsert(args) {
|
|
1103
|
+
return {
|
|
1104
|
+
globalId: args.globalId,
|
|
1105
|
+
topicId: args.scope.topicId,
|
|
1106
|
+
projectId: args.scope.projectId,
|
|
1107
|
+
tenantId: args.scope.tenantId,
|
|
1108
|
+
workspaceId: args.scope.workspaceId,
|
|
1109
|
+
nodeType: "evidence",
|
|
1110
|
+
canonicalText: args.write.text,
|
|
1111
|
+
contentHash: generateContentHash(args.write.text),
|
|
1112
|
+
...evidenceTextProjection(args.write),
|
|
1113
|
+
status: "active",
|
|
1114
|
+
epistemicLayer: "L2",
|
|
1115
|
+
sourceType: args.sourceType,
|
|
1116
|
+
// RC.1 taxonomy fields — top-level projection
|
|
1117
|
+
evidenceRelation: args.impact.evidenceRelation,
|
|
1118
|
+
signedImpact: args.impact.signedImpact,
|
|
1119
|
+
targetBeliefId: String(args.linkedBeliefNodeId),
|
|
1120
|
+
...evidenceTaxonomyProjection(args.write),
|
|
1121
|
+
createdAt: args.now,
|
|
1122
|
+
updatedAt: args.now,
|
|
1123
|
+
createdBy: args.write.userId,
|
|
1124
|
+
metadata: evidenceMetadata(
|
|
1125
|
+
args.write,
|
|
1126
|
+
args.impact,
|
|
1127
|
+
args.kind,
|
|
1128
|
+
args.metadataProfile
|
|
1129
|
+
)
|
|
1130
|
+
};
|
|
1131
|
+
}
|
|
884
1132
|
async function createEvidenceBeliefEdge(ctx, args) {
|
|
885
1133
|
const edgeGlobalId = generateGlobalId();
|
|
886
1134
|
const confidence = Math.abs(args.weight);
|
|
@@ -1001,70 +1249,31 @@ var create = mutation({
|
|
|
1001
1249
|
mutationName: "epistemicEvidence.create"
|
|
1002
1250
|
});
|
|
1003
1251
|
if (scope.projectId) {
|
|
1004
|
-
await
|
|
1252
|
+
await requireScopeWriteAccess(ctx, scope.projectId, args.userId);
|
|
1005
1253
|
}
|
|
1006
1254
|
const now = Date.now();
|
|
1007
1255
|
const globalId = generateGlobalId();
|
|
1008
|
-
const contentHash = generateContentHash(args.text);
|
|
1009
1256
|
const kind = normalizeKind(args.kind);
|
|
1010
1257
|
const sourceType = normalizeSourceType(args.sourceType);
|
|
1011
|
-
const
|
|
1012
|
-
const
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
weight,
|
|
1016
|
-
"Evidence creation"
|
|
1258
|
+
const impact = planEvidenceImpact(args, "Evidence creation");
|
|
1259
|
+
const linkedBeliefNode = assertBeliefNode(
|
|
1260
|
+
await ctx.db.get(args.linkedBeliefNodeId),
|
|
1261
|
+
"Evidence creation requires a linked belief node"
|
|
1017
1262
|
);
|
|
1018
|
-
const
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
const nodeId = await insertEpistemicNode(ctx, {
|
|
1024
|
-
globalId,
|
|
1025
|
-
topicId: scope.topicId,
|
|
1026
|
-
projectId: scope.projectId,
|
|
1027
|
-
tenantId: scope.tenantId,
|
|
1028
|
-
workspaceId: scope.workspaceId,
|
|
1029
|
-
nodeType: "evidence",
|
|
1030
|
-
canonicalText: args.text,
|
|
1031
|
-
contentHash,
|
|
1032
|
-
...typeof args.title === "string" && args.title.trim().length > 0 ? { title: args.title.trim() } : {},
|
|
1033
|
-
...typeof args.content === "string" && args.content.length > 0 ? { content: args.content } : {},
|
|
1034
|
-
...typeof args.contentType === "string" && args.contentType.trim().length > 0 ? { contentType: args.contentType.trim() } : {},
|
|
1035
|
-
status: "active",
|
|
1036
|
-
epistemicLayer: "L2",
|
|
1037
|
-
sourceType,
|
|
1038
|
-
// RC.1 taxonomy fields — top-level projection
|
|
1039
|
-
evidenceRelation,
|
|
1040
|
-
signedImpact,
|
|
1041
|
-
targetBeliefId: String(args.linkedBeliefNodeId),
|
|
1042
|
-
...typeof args.sourceRef === "string" && args.sourceRef.trim().length > 0 ? { sourceRef: args.sourceRef.trim() } : {},
|
|
1043
|
-
...typeof args.sourceQuality === "string" && args.sourceQuality.length > 0 ? { sourceQuality: args.sourceQuality } : {},
|
|
1044
|
-
...typeof args.methodology === "string" && args.methodology.length > 0 ? { methodology: args.methodology } : {},
|
|
1045
|
-
...typeof args.informationAsymmetry === "string" && args.informationAsymmetry.length > 0 ? { informationAsymmetry: args.informationAsymmetry } : {},
|
|
1046
|
-
...typeof args.sourceDescription === "string" && args.sourceDescription.trim().length > 0 ? { sourceDescription: args.sourceDescription.trim() } : {},
|
|
1047
|
-
createdAt: now,
|
|
1048
|
-
updatedAt: now,
|
|
1049
|
-
createdBy: args.userId,
|
|
1050
|
-
metadata: {
|
|
1263
|
+
const nodeId = await insertEpistemicNode(
|
|
1264
|
+
ctx,
|
|
1265
|
+
buildEvidenceNodeInsert({
|
|
1266
|
+
globalId,
|
|
1267
|
+
impact,
|
|
1051
1268
|
kind,
|
|
1052
|
-
tags: args.tags || [],
|
|
1053
|
-
externalSourceType: args.externalSourceType,
|
|
1054
|
-
sourceUrl: args.sourceUrl,
|
|
1055
|
-
sourceQuestionId: args.sourceQuestionId,
|
|
1056
|
-
rationale: args.rationale,
|
|
1057
1269
|
linkedBeliefNodeId: args.linkedBeliefNodeId,
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
...additionalMetadata
|
|
1066
|
-
}
|
|
1067
|
-
});
|
|
1270
|
+
metadataProfile: "create",
|
|
1271
|
+
now,
|
|
1272
|
+
scope,
|
|
1273
|
+
sourceType,
|
|
1274
|
+
write: args
|
|
1275
|
+
})
|
|
1276
|
+
);
|
|
1068
1277
|
await ctx.scheduler.runAfter(0, internal.neo4jSync.syncNodeToNeo4j, {
|
|
1069
1278
|
nodeId,
|
|
1070
1279
|
operation: "upsert"
|
|
@@ -1082,8 +1291,8 @@ var create = mutation({
|
|
|
1082
1291
|
evidenceGlobalId: globalId,
|
|
1083
1292
|
beliefNodeId: args.linkedBeliefNodeId,
|
|
1084
1293
|
beliefGlobalId: linkedBeliefNode.globalId,
|
|
1085
|
-
relation: evidenceRelation,
|
|
1086
|
-
weight,
|
|
1294
|
+
relation: impact.evidenceRelation,
|
|
1295
|
+
weight: impact.weight,
|
|
1087
1296
|
userId: args.userId,
|
|
1088
1297
|
topicId: scope.topicId ? String(scope.topicId) : void 0,
|
|
1089
1298
|
projectId: scope.projectId ? String(scope.projectId) : void 0,
|
|
@@ -1103,23 +1312,19 @@ var create = mutation({
|
|
|
1103
1312
|
kind,
|
|
1104
1313
|
sourceType,
|
|
1105
1314
|
linkedBeliefNodeId: args.linkedBeliefNodeId,
|
|
1106
|
-
evidenceRelation,
|
|
1107
|
-
weight
|
|
1315
|
+
evidenceRelation: impact.evidenceRelation,
|
|
1316
|
+
weight: impact.weight
|
|
1108
1317
|
}
|
|
1109
1318
|
});
|
|
1110
1319
|
if (scope.projectId || scope.topicId) {
|
|
1111
|
-
await ctx.scheduler.runAfter(
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
nodeType: "evidence",
|
|
1120
|
-
text: args.text
|
|
1121
|
-
}
|
|
1122
|
-
);
|
|
1320
|
+
await ctx.scheduler.runAfter(0, EMBEDDING_GENERATION_ACTION, {
|
|
1321
|
+
nodeId,
|
|
1322
|
+
projectId: scope.projectId,
|
|
1323
|
+
topicId: scope.topicId ? String(scope.topicId) : void 0,
|
|
1324
|
+
createdBy: args.userId,
|
|
1325
|
+
nodeType: "evidence",
|
|
1326
|
+
text: args.text
|
|
1327
|
+
});
|
|
1123
1328
|
}
|
|
1124
1329
|
if (scope.projectId || scope.topicId) {
|
|
1125
1330
|
await ctx.scheduler.runAfter(
|
|
@@ -1133,7 +1338,11 @@ var create = mutation({
|
|
|
1133
1338
|
}
|
|
1134
1339
|
);
|
|
1135
1340
|
}
|
|
1136
|
-
await markProjectGraphDirty(
|
|
1341
|
+
await markProjectGraphDirty(
|
|
1342
|
+
ctx,
|
|
1343
|
+
scope.projectId,
|
|
1344
|
+
scope.topicId ? String(scope.topicId) : void 0
|
|
1345
|
+
);
|
|
1137
1346
|
return { nodeId };
|
|
1138
1347
|
}
|
|
1139
1348
|
});
|
|
@@ -1171,58 +1380,39 @@ var createAndLink = mutation({
|
|
|
1171
1380
|
if (!scope) {
|
|
1172
1381
|
throw new Error("Invalid scope: projectId or topicId is required");
|
|
1173
1382
|
}
|
|
1174
|
-
|
|
1383
|
+
const accessScopeId = scope.projectId ?? (scope.topicId ? String(scope.topicId) : void 0);
|
|
1384
|
+
if (!accessScopeId) {
|
|
1385
|
+
throw new Error("Resolved evidence scope has no access-control id");
|
|
1386
|
+
}
|
|
1387
|
+
await requireScopeWriteAccess(ctx, accessScopeId, args.userId);
|
|
1175
1388
|
const now = Date.now();
|
|
1176
1389
|
const globalId = generateGlobalId();
|
|
1177
|
-
const contentHash = generateContentHash(args.text);
|
|
1178
1390
|
const kind = normalizeKind(args.kind);
|
|
1179
1391
|
const sourceType = normalizeSourceType(args.sourceType);
|
|
1180
|
-
const
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
args.
|
|
1184
|
-
|
|
1185
|
-
|
|
1392
|
+
const write = {
|
|
1393
|
+
...args,
|
|
1394
|
+
evidenceRelation: args.relation,
|
|
1395
|
+
linkedBeliefNodeId: args.beliefNodeId
|
|
1396
|
+
};
|
|
1397
|
+
const impact = planEvidenceImpact(write, "Evidence createAndLink");
|
|
1398
|
+
const beliefNode = assertBeliefNode(
|
|
1399
|
+
await ctx.db.get(args.beliefNodeId),
|
|
1400
|
+
"Belief node not found for edge creation"
|
|
1186
1401
|
);
|
|
1187
|
-
const
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
const nodeId = await insertEpistemicNode(ctx, {
|
|
1193
|
-
globalId,
|
|
1194
|
-
topicId: scope.topicId,
|
|
1195
|
-
projectId: scope.projectId,
|
|
1196
|
-
tenantId: scope.tenantId,
|
|
1197
|
-
workspaceId: scope.workspaceId,
|
|
1198
|
-
nodeType: "evidence",
|
|
1199
|
-
canonicalText: args.text,
|
|
1200
|
-
contentHash,
|
|
1201
|
-
status: "active",
|
|
1202
|
-
epistemicLayer: "L2",
|
|
1203
|
-
sourceType,
|
|
1204
|
-
// RC.1 taxonomy fields — top-level projection
|
|
1205
|
-
evidenceRelation: relation,
|
|
1206
|
-
signedImpact,
|
|
1207
|
-
targetBeliefId: String(args.beliefNodeId),
|
|
1208
|
-
...typeof args.sourceRef === "string" && args.sourceRef.trim().length > 0 ? { sourceRef: args.sourceRef.trim() } : {},
|
|
1209
|
-
...typeof args.sourceQuality === "string" && args.sourceQuality.length > 0 ? { sourceQuality: args.sourceQuality } : {},
|
|
1210
|
-
...typeof args.methodology === "string" && args.methodology.length > 0 ? { methodology: args.methodology } : {},
|
|
1211
|
-
...typeof args.informationAsymmetry === "string" && args.informationAsymmetry.length > 0 ? { informationAsymmetry: args.informationAsymmetry } : {},
|
|
1212
|
-
...typeof args.sourceDescription === "string" && args.sourceDescription.trim().length > 0 ? { sourceDescription: args.sourceDescription.trim() } : {},
|
|
1213
|
-
createdAt: now,
|
|
1214
|
-
updatedAt: now,
|
|
1215
|
-
createdBy: args.userId,
|
|
1216
|
-
metadata: {
|
|
1402
|
+
const nodeId = await insertEpistemicNode(
|
|
1403
|
+
ctx,
|
|
1404
|
+
buildEvidenceNodeInsert({
|
|
1405
|
+
globalId,
|
|
1406
|
+
impact,
|
|
1217
1407
|
kind,
|
|
1218
|
-
tags: args.tags || [],
|
|
1219
1408
|
linkedBeliefNodeId: args.beliefNodeId,
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
|
|
1409
|
+
metadataProfile: "createAndLink",
|
|
1410
|
+
now,
|
|
1411
|
+
scope,
|
|
1412
|
+
sourceType,
|
|
1413
|
+
write
|
|
1414
|
+
})
|
|
1415
|
+
);
|
|
1226
1416
|
await ctx.scheduler.runAfter(0, internal.neo4jSync.syncNodeToNeo4j, {
|
|
1227
1417
|
nodeId,
|
|
1228
1418
|
operation: "upsert"
|
|
@@ -1231,13 +1421,17 @@ var createAndLink = mutation({
|
|
|
1231
1421
|
evidenceGlobalId: globalId,
|
|
1232
1422
|
beliefNodeId: args.beliefNodeId,
|
|
1233
1423
|
beliefGlobalId: beliefNode.globalId,
|
|
1234
|
-
relation,
|
|
1235
|
-
weight,
|
|
1424
|
+
relation: impact.evidenceRelation,
|
|
1425
|
+
weight: impact.weight,
|
|
1236
1426
|
userId: args.userId,
|
|
1237
1427
|
topicId: scope.topicId ? String(scope.topicId) : void 0,
|
|
1238
1428
|
projectId: scope.projectId ? String(scope.projectId) : void 0
|
|
1239
1429
|
});
|
|
1240
|
-
await markProjectGraphDirty(
|
|
1430
|
+
await markProjectGraphDirty(
|
|
1431
|
+
ctx,
|
|
1432
|
+
scope.projectId,
|
|
1433
|
+
scope.topicId ? String(scope.topicId) : void 0
|
|
1434
|
+
);
|
|
1241
1435
|
return { nodeId, edgeGlobalId };
|
|
1242
1436
|
}
|
|
1243
1437
|
});
|
|
@@ -1253,10 +1447,10 @@ var updateStatus = mutation({
|
|
|
1253
1447
|
},
|
|
1254
1448
|
returns: permissiveReturn,
|
|
1255
1449
|
handler: async (ctx, args) => {
|
|
1256
|
-
const node =
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
1450
|
+
const node = assertEvidenceNode(
|
|
1451
|
+
await ctx.db.get(args.nodeId),
|
|
1452
|
+
"Evidence not found"
|
|
1453
|
+
);
|
|
1260
1454
|
const now = Date.now();
|
|
1261
1455
|
await ctx.db.patch(args.nodeId, {
|
|
1262
1456
|
status: args.status,
|
|
@@ -1345,63 +1539,27 @@ var internalCreate = internalMutation({
|
|
|
1345
1539
|
mutationName: "epistemicEvidence.internalCreate"
|
|
1346
1540
|
});
|
|
1347
1541
|
const globalId = generateGlobalId();
|
|
1348
|
-
const contentHash = generateContentHash(args.text);
|
|
1349
1542
|
const kind = normalizeKind(args.kind);
|
|
1350
1543
|
const sourceType = normalizeSourceType(args.sourceType);
|
|
1351
|
-
const
|
|
1352
|
-
const
|
|
1353
|
-
|
|
1354
|
-
|
|
1355
|
-
weight,
|
|
1356
|
-
"Internal evidence creation"
|
|
1544
|
+
const impact = planEvidenceImpact(args, "Internal evidence creation");
|
|
1545
|
+
const linkedBeliefNode = assertBeliefNode(
|
|
1546
|
+
await ctx.db.get(args.linkedBeliefNodeId),
|
|
1547
|
+
"Internal evidence creation requires a linked belief node"
|
|
1357
1548
|
);
|
|
1358
|
-
const
|
|
1359
|
-
|
|
1360
|
-
|
|
1361
|
-
|
|
1362
|
-
|
|
1363
|
-
const nodeId = await insertEpistemicNode(ctx, {
|
|
1364
|
-
globalId,
|
|
1365
|
-
topicId: scope.topicId,
|
|
1366
|
-
projectId: scope.projectId,
|
|
1367
|
-
tenantId: scope.tenantId,
|
|
1368
|
-
workspaceId: scope.workspaceId,
|
|
1369
|
-
nodeType: "evidence",
|
|
1370
|
-
canonicalText: args.text,
|
|
1371
|
-
contentHash,
|
|
1372
|
-
...typeof args.title === "string" && args.title.trim().length > 0 ? { title: args.title.trim() } : {},
|
|
1373
|
-
...typeof args.content === "string" && args.content.length > 0 ? { content: args.content } : {},
|
|
1374
|
-
...typeof args.contentType === "string" && args.contentType.trim().length > 0 ? { contentType: args.contentType.trim() } : {},
|
|
1375
|
-
status: "active",
|
|
1376
|
-
epistemicLayer: "L2",
|
|
1377
|
-
sourceType,
|
|
1378
|
-
// RC.1 taxonomy fields — top-level projection
|
|
1379
|
-
evidenceRelation,
|
|
1380
|
-
signedImpact,
|
|
1381
|
-
targetBeliefId: String(args.linkedBeliefNodeId),
|
|
1382
|
-
...typeof args.sourceRef === "string" && args.sourceRef.trim().length > 0 ? { sourceRef: args.sourceRef.trim() } : {},
|
|
1383
|
-
...typeof args.sourceQuality === "string" && args.sourceQuality.length > 0 ? { sourceQuality: args.sourceQuality } : {},
|
|
1384
|
-
...typeof args.methodology === "string" && args.methodology.length > 0 ? { methodology: args.methodology } : {},
|
|
1385
|
-
...typeof args.informationAsymmetry === "string" && args.informationAsymmetry.length > 0 ? { informationAsymmetry: args.informationAsymmetry } : {},
|
|
1386
|
-
...typeof args.sourceDescription === "string" && args.sourceDescription.trim().length > 0 ? { sourceDescription: args.sourceDescription.trim() } : {},
|
|
1387
|
-
createdAt: now,
|
|
1388
|
-
updatedAt: now,
|
|
1389
|
-
createdBy: args.userId,
|
|
1390
|
-
metadata: {
|
|
1549
|
+
const nodeId = await insertEpistemicNode(
|
|
1550
|
+
ctx,
|
|
1551
|
+
buildEvidenceNodeInsert({
|
|
1552
|
+
globalId,
|
|
1553
|
+
impact,
|
|
1391
1554
|
kind,
|
|
1392
|
-
tags: args.tags || [],
|
|
1393
|
-
externalSourceType: args.externalSourceType,
|
|
1394
|
-
sourceUrl: args.sourceUrl,
|
|
1395
|
-
sourceQuestionId: args.sourceQuestionId,
|
|
1396
|
-
rationale: args.rationale,
|
|
1397
1555
|
linkedBeliefNodeId: args.linkedBeliefNodeId,
|
|
1398
|
-
|
|
1399
|
-
|
|
1400
|
-
|
|
1401
|
-
|
|
1402
|
-
|
|
1403
|
-
}
|
|
1404
|
-
|
|
1556
|
+
metadataProfile: "internalCreate",
|
|
1557
|
+
now,
|
|
1558
|
+
scope,
|
|
1559
|
+
sourceType,
|
|
1560
|
+
write: args
|
|
1561
|
+
})
|
|
1562
|
+
);
|
|
1405
1563
|
await ctx.db.insert("epistemicAudit", {
|
|
1406
1564
|
entityType: "evidence",
|
|
1407
1565
|
entityId: String(nodeId),
|
|
@@ -1418,9 +1576,9 @@ var internalCreate = internalMutation({
|
|
|
1418
1576
|
externalSourceType: args.externalSourceType,
|
|
1419
1577
|
sourceUrl: args.sourceUrl,
|
|
1420
1578
|
linkedBeliefNodeId: args.linkedBeliefNodeId,
|
|
1421
|
-
evidenceRelation,
|
|
1422
|
-
confidence:
|
|
1423
|
-
weight
|
|
1579
|
+
evidenceRelation: impact.evidenceRelation,
|
|
1580
|
+
confidence: impact.confidence,
|
|
1581
|
+
weight: impact.weight
|
|
1424
1582
|
},
|
|
1425
1583
|
triggeringAction: "epistemicEvidence.internalCreate"
|
|
1426
1584
|
});
|
|
@@ -1432,28 +1590,28 @@ var internalCreate = internalMutation({
|
|
|
1432
1590
|
evidenceGlobalId: globalId,
|
|
1433
1591
|
beliefNodeId: args.linkedBeliefNodeId,
|
|
1434
1592
|
beliefGlobalId: linkedBeliefNode.globalId,
|
|
1435
|
-
relation: evidenceRelation,
|
|
1436
|
-
weight,
|
|
1593
|
+
relation: impact.evidenceRelation,
|
|
1594
|
+
weight: impact.weight,
|
|
1437
1595
|
userId: args.userId,
|
|
1438
1596
|
topicId: scope.topicId ? String(scope.topicId) : void 0,
|
|
1439
1597
|
projectId: scope.projectId ? String(scope.projectId) : void 0,
|
|
1440
1598
|
rationale: args.rationale
|
|
1441
1599
|
});
|
|
1442
1600
|
if (scope.projectId || scope.topicId) {
|
|
1443
|
-
await ctx.scheduler.runAfter(
|
|
1444
|
-
|
|
1445
|
-
|
|
1446
|
-
|
|
1447
|
-
|
|
1448
|
-
|
|
1449
|
-
|
|
1450
|
-
|
|
1451
|
-
nodeType: "evidence",
|
|
1452
|
-
text: args.text
|
|
1453
|
-
}
|
|
1454
|
-
);
|
|
1601
|
+
await ctx.scheduler.runAfter(0, EMBEDDING_GENERATION_ACTION, {
|
|
1602
|
+
nodeId,
|
|
1603
|
+
projectId: scope.projectId,
|
|
1604
|
+
topicId: scope.topicId ? String(scope.topicId) : void 0,
|
|
1605
|
+
createdBy: args.userId,
|
|
1606
|
+
nodeType: "evidence",
|
|
1607
|
+
text: args.text
|
|
1608
|
+
});
|
|
1455
1609
|
}
|
|
1456
|
-
await markProjectGraphDirty(
|
|
1610
|
+
await markProjectGraphDirty(
|
|
1611
|
+
ctx,
|
|
1612
|
+
scope.projectId,
|
|
1613
|
+
scope.topicId ? String(scope.topicId) : void 0
|
|
1614
|
+
);
|
|
1457
1615
|
return { nodeId };
|
|
1458
1616
|
}
|
|
1459
1617
|
});
|
|
@@ -1466,11 +1624,11 @@ var updateVerificationStatus = mutation({
|
|
|
1466
1624
|
},
|
|
1467
1625
|
returns: permissiveReturn,
|
|
1468
1626
|
handler: async (ctx, args) => {
|
|
1469
|
-
const node =
|
|
1470
|
-
|
|
1471
|
-
|
|
1472
|
-
|
|
1473
|
-
const metadata = node.metadata
|
|
1627
|
+
const node = assertEvidenceNode(
|
|
1628
|
+
await ctx.db.get(args.nodeId),
|
|
1629
|
+
"Evidence node not found"
|
|
1630
|
+
);
|
|
1631
|
+
const metadata = metadataRecord(node.metadata);
|
|
1474
1632
|
await ctx.db.patch(args.nodeId, {
|
|
1475
1633
|
metadata: {
|
|
1476
1634
|
...metadata,
|
|
@@ -1496,20 +1654,17 @@ var update = mutation({
|
|
|
1496
1654
|
},
|
|
1497
1655
|
returns: permissiveReturn,
|
|
1498
1656
|
handler: async (ctx, args) => {
|
|
1499
|
-
const resolvedId =
|
|
1500
|
-
|
|
1501
|
-
|
|
1502
|
-
|
|
1503
|
-
|
|
1504
|
-
if (!node || node.nodeType !== "evidence") {
|
|
1505
|
-
throw new Error("Evidence node not found");
|
|
1506
|
-
}
|
|
1657
|
+
const resolvedId = await resolveEvidenceNodeId(ctx, args);
|
|
1658
|
+
const node = assertEvidenceNode(
|
|
1659
|
+
await ctx.db.get(resolvedId),
|
|
1660
|
+
"Evidence node not found"
|
|
1661
|
+
);
|
|
1507
1662
|
if (!node.projectId) {
|
|
1508
1663
|
throw new Error("Evidence has no project scope");
|
|
1509
1664
|
}
|
|
1510
|
-
await
|
|
1665
|
+
await requireScopeWriteAccess(ctx, node.projectId, args.userId);
|
|
1511
1666
|
const now = Date.now();
|
|
1512
|
-
const existingMeta = node.metadata
|
|
1667
|
+
const existingMeta = metadataRecord(node.metadata);
|
|
1513
1668
|
const metaUpdates = { ...existingMeta };
|
|
1514
1669
|
if (args.kind !== void 0) {
|
|
1515
1670
|
metaUpdates.kind = args.kind;
|
|
@@ -1541,17 +1696,13 @@ var update = mutation({
|
|
|
1541
1696
|
newState: { text: (args.text ?? node.canonicalText)?.slice(0, 200) }
|
|
1542
1697
|
});
|
|
1543
1698
|
if (args.text !== void 0) {
|
|
1544
|
-
await ctx.scheduler.runAfter(
|
|
1545
|
-
|
|
1546
|
-
|
|
1547
|
-
|
|
1548
|
-
|
|
1549
|
-
|
|
1550
|
-
|
|
1551
|
-
nodeType: "evidence",
|
|
1552
|
-
text: args.text
|
|
1553
|
-
}
|
|
1554
|
-
);
|
|
1699
|
+
await ctx.scheduler.runAfter(0, EMBEDDING_GENERATION_ACTION, {
|
|
1700
|
+
nodeId: resolvedId,
|
|
1701
|
+
topicId: node.projectId,
|
|
1702
|
+
createdBy: node.createdBy ?? args.userId,
|
|
1703
|
+
nodeType: "evidence",
|
|
1704
|
+
text: args.text
|
|
1705
|
+
});
|
|
1555
1706
|
}
|
|
1556
1707
|
await markProjectGraphDirty(ctx, node.projectId, node.topicId);
|
|
1557
1708
|
return { nodeId: resolvedId };
|
|
@@ -1567,15 +1718,15 @@ var flagAsIncorrect = mutation({
|
|
|
1567
1718
|
returns: permissiveReturn,
|
|
1568
1719
|
handler: async (ctx, args) => {
|
|
1569
1720
|
const now = Date.now();
|
|
1570
|
-
const node =
|
|
1571
|
-
|
|
1572
|
-
|
|
1573
|
-
|
|
1721
|
+
const node = assertEvidenceNode(
|
|
1722
|
+
await ctx.db.get(args.insightId),
|
|
1723
|
+
"Evidence not found in epistemic spine"
|
|
1724
|
+
);
|
|
1574
1725
|
if (!node.projectId) {
|
|
1575
1726
|
throw new Error("Evidence has no project scope");
|
|
1576
1727
|
}
|
|
1577
|
-
await
|
|
1578
|
-
const existingMeta = node.metadata
|
|
1728
|
+
await requireScopeWriteAccess(ctx, node.projectId, args.userId);
|
|
1729
|
+
const existingMeta = metadataRecord(node.metadata);
|
|
1579
1730
|
await ctx.db.patch(node._id, {
|
|
1580
1731
|
verificationStatus: "contradicted",
|
|
1581
1732
|
metadata: {
|
|
@@ -1631,14 +1782,11 @@ var remove = mutation({
|
|
|
1631
1782
|
},
|
|
1632
1783
|
returns: permissiveReturn,
|
|
1633
1784
|
handler: async (ctx, args) => {
|
|
1634
|
-
const resolvedId =
|
|
1635
|
-
|
|
1636
|
-
|
|
1637
|
-
|
|
1638
|
-
|
|
1639
|
-
if (!node || node.nodeType !== "evidence") {
|
|
1640
|
-
throw new Error("Evidence node not found");
|
|
1641
|
-
}
|
|
1785
|
+
const resolvedId = await resolveEvidenceNodeId(ctx, args);
|
|
1786
|
+
const node = assertEvidenceNode(
|
|
1787
|
+
await ctx.db.get(resolvedId),
|
|
1788
|
+
"Evidence node not found"
|
|
1789
|
+
);
|
|
1642
1790
|
if (node.createdBy !== args.userId) {
|
|
1643
1791
|
throw new Error("Only the creator can archive this evidence");
|
|
1644
1792
|
}
|