@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,550 +1,143 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { requireScopeWriteAccess } from '@lucern/access-control/access';
|
|
2
2
|
import { permissiveReturn } from '@lucern/contracts/schema-helpers/validators';
|
|
3
|
-
import {
|
|
3
|
+
import { v } from 'convex/values';
|
|
4
|
+
import { unsafeConvexAnyApi } from '@lucern/contracts/convex/unsafeAnyApi';
|
|
5
|
+
import { componentsGeneric, internalMutationGeneric, mutationGeneric } from 'convex/server';
|
|
6
|
+
import { normalizeTupleContradictionPolicy, mkOpinion, conditionalDeduction, project, dampedDependencyCascade, trustDiscount, applyNegativeSupport, cumulativeFusion, applyNegativeEvidence, confidenceFromSL, detectTupleContradiction, evaluateTupleContradictionTransition, readOpinionFromRecord, hasProjectedOpinionChanged } from '@lucern/confidence';
|
|
4
7
|
import '@lucern/contracts/schema-helpers/spine/tables/epistemicNodes';
|
|
5
|
-
import { normalizeTupleContradictionPolicy, mkOpinion, conditionalDeduction, project, dampedDependencyCascade, hasProjectedOpinionChanged, confidenceFromSL, detectTupleContradiction, evaluateTupleContradictionTransition, trustDiscount, applyNegativeSupport, cumulativeFusion, applyNegativeEvidence, readOpinionFromRecord } from '@lucern/confidence';
|
|
6
|
-
import { checkProjectAccess } from '@lucern/access-control/access';
|
|
7
8
|
import '@lucern/access-control/audience';
|
|
8
9
|
import { getCurrentUserId } from '@lucern/access-control/auth';
|
|
10
|
+
import { throwStructuredMutationError } from '@lucern/access-control/structuredMutationError';
|
|
9
11
|
|
|
10
12
|
// src/epistemicBeliefs.lifecycle.ts
|
|
11
|
-
var
|
|
13
|
+
var unsafeApi = unsafeConvexAnyApi(
|
|
14
|
+
"graph-primitives top-level module bundle lacks a committed Convex _generated/api surface"
|
|
15
|
+
);
|
|
16
|
+
var api = unsafeApi;
|
|
12
17
|
componentsGeneric();
|
|
13
|
-
var internal =
|
|
18
|
+
var internal = unsafeApi;
|
|
14
19
|
var internalMutation = internalMutationGeneric;
|
|
15
20
|
var mutation = mutationGeneric;
|
|
16
21
|
|
|
17
|
-
// src/
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
22
|
+
// src/beliefLifecycle.ts
|
|
23
|
+
var BELIEF_STATUS_VALUES = [
|
|
24
|
+
"assumption",
|
|
25
|
+
"hypothesis",
|
|
26
|
+
"active",
|
|
27
|
+
"superseded",
|
|
28
|
+
"resolved_true",
|
|
29
|
+
"resolved_false"
|
|
30
|
+
];
|
|
31
|
+
function isBeliefLifecycleStatus(value) {
|
|
32
|
+
return typeof value === "string" && BELIEF_STATUS_VALUES.includes(value);
|
|
21
33
|
}
|
|
22
|
-
function
|
|
23
|
-
if (
|
|
24
|
-
return;
|
|
34
|
+
function normalizeLegacyBeliefStatus(value) {
|
|
35
|
+
if (isBeliefLifecycleStatus(value)) {
|
|
36
|
+
return value;
|
|
25
37
|
}
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
// src/topicScope.ts
|
|
30
|
-
var LEGACY_SCOPE_FIELD = "graphScopeProjectId";
|
|
31
|
-
async function resolveTopicNodeScopeOrNull(ctx, ref) {
|
|
32
|
-
if (!ctx?.db || typeof ctx.db.query !== "function") {
|
|
33
|
-
return null;
|
|
38
|
+
if (value === "belief" || value === "established" || value === "emerging") {
|
|
39
|
+
return "active";
|
|
34
40
|
}
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
const byGlobalId = await ctx.db.query("epistemicNodes").withIndex("by_globalId", (q) => q.eq("globalId", ref)).first();
|
|
38
|
-
if (byGlobalId && byGlobalId.nodeType === "topic") {
|
|
39
|
-
node = byGlobalId;
|
|
40
|
-
}
|
|
41
|
-
} catch (error) {
|
|
42
|
-
debugGraphPrimitiveFallback(
|
|
43
|
-
"[topicScope] topic-node scope lookup by globalId failed",
|
|
44
|
-
{ error, ref }
|
|
45
|
-
);
|
|
41
|
+
if (value === "fact" || value === "confirmed") {
|
|
42
|
+
return "resolved_true";
|
|
46
43
|
}
|
|
47
|
-
if (
|
|
48
|
-
return
|
|
44
|
+
if (value === "disconfirmed" || value === "expired") {
|
|
45
|
+
return "resolved_false";
|
|
49
46
|
}
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
return null;
|
|
47
|
+
if (value === "deprecated") {
|
|
48
|
+
return "superseded";
|
|
53
49
|
}
|
|
54
|
-
return
|
|
55
|
-
topicId: scopeKey,
|
|
56
|
-
projectId: asMappedProjectId(node),
|
|
57
|
-
source: "topic_node"
|
|
58
|
-
};
|
|
50
|
+
return null;
|
|
59
51
|
}
|
|
60
|
-
function
|
|
61
|
-
if (!
|
|
62
|
-
return;
|
|
52
|
+
function normalizeBeliefConfidence(confidence) {
|
|
53
|
+
if (typeof confidence !== "number" || !Number.isFinite(confidence)) {
|
|
54
|
+
return null;
|
|
63
55
|
}
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
return directLegacyProjectId;
|
|
56
|
+
if (confidence >= 0 && confidence <= 1) {
|
|
57
|
+
return confidence;
|
|
67
58
|
}
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
return candidate ? candidate : void 0;
|
|
71
|
-
}
|
|
72
|
-
function normalizeScopeValue(value) {
|
|
73
|
-
if (typeof value !== "string") {
|
|
74
|
-
return;
|
|
59
|
+
if (confidence > 1 && confidence <= 100) {
|
|
60
|
+
return confidence / 100;
|
|
75
61
|
}
|
|
76
|
-
|
|
77
|
-
return normalized.length > 0 ? normalized : void 0;
|
|
78
|
-
}
|
|
79
|
-
function pickPrimaryTopic(candidates) {
|
|
80
|
-
return [...candidates].sort((a, b) => {
|
|
81
|
-
const depthA = a.depth ?? 9999;
|
|
82
|
-
const depthB = b.depth ?? 9999;
|
|
83
|
-
if (depthA !== depthB) {
|
|
84
|
-
return depthA - depthB;
|
|
85
|
-
}
|
|
86
|
-
const createdA = a.createdAt ?? Number.MAX_SAFE_INTEGER;
|
|
87
|
-
const createdB = b.createdAt ?? Number.MAX_SAFE_INTEGER;
|
|
88
|
-
if (createdA !== createdB) {
|
|
89
|
-
return createdA - createdB;
|
|
90
|
-
}
|
|
91
|
-
return String(a.name || "").localeCompare(String(b.name || ""));
|
|
92
|
-
})[0];
|
|
62
|
+
return null;
|
|
93
63
|
}
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
(q) => q.eq(LEGACY_SCOPE_FIELD, scopeId)
|
|
99
|
-
).collect();
|
|
100
|
-
} catch (error) {
|
|
101
|
-
debugGraphPrimitiveFallback(
|
|
102
|
-
"[topicScope] Failed to resolve scope alias via index",
|
|
103
|
-
{
|
|
104
|
-
error,
|
|
105
|
-
scopeId
|
|
106
|
-
}
|
|
107
|
-
);
|
|
108
|
-
const topics = await ctx.db.query("topics").collect();
|
|
109
|
-
return topics.filter((topic) => {
|
|
110
|
-
const normalizedGlobalId = normalizeScopeValue(topic.globalId);
|
|
111
|
-
const mappedProjectId = asMappedProjectId(topic);
|
|
112
|
-
return String(topic._id) === scopeId || normalizedGlobalId === scopeId || mappedProjectId === scopeId;
|
|
113
|
-
});
|
|
64
|
+
function isResolvedByConfidence(confidence) {
|
|
65
|
+
const normalized = normalizeBeliefConfidence(confidence);
|
|
66
|
+
if (normalized === null) {
|
|
67
|
+
return false;
|
|
114
68
|
}
|
|
69
|
+
return normalized <= 0 || normalized >= 1;
|
|
115
70
|
}
|
|
116
|
-
|
|
117
|
-
|
|
71
|
+
function getPredictionMetaFromMetadata(metadata) {
|
|
72
|
+
return metadata?.predictionMeta;
|
|
73
|
+
}
|
|
74
|
+
function resolvedPredictionStatus(predictionMeta) {
|
|
75
|
+
if (!predictionMeta || typeof predictionMeta !== "object") {
|
|
118
76
|
return null;
|
|
119
77
|
}
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
"[topicScope] Failed to resolve topic by host query",
|
|
127
|
-
{
|
|
128
|
-
error,
|
|
129
|
-
topicId
|
|
130
|
-
}
|
|
131
|
-
);
|
|
132
|
-
return null;
|
|
78
|
+
const outcome = predictionMeta.outcome;
|
|
79
|
+
if (outcome === "confirmed") {
|
|
80
|
+
return "resolved_true";
|
|
81
|
+
}
|
|
82
|
+
if (outcome === "disconfirmed" || outcome === "expired") {
|
|
83
|
+
return "resolved_false";
|
|
133
84
|
}
|
|
85
|
+
return null;
|
|
134
86
|
}
|
|
135
|
-
|
|
136
|
-
if (
|
|
137
|
-
|
|
87
|
+
function shouldTreatBeliefAsResolved(opts) {
|
|
88
|
+
if (isResolvedByConfidence(opts.confidence)) {
|
|
89
|
+
const normalized = normalizeBeliefConfidence(opts.confidence);
|
|
90
|
+
return normalized === 0 ? "resolved_false" : "resolved_true";
|
|
138
91
|
}
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
}) ?? null;
|
|
143
|
-
} catch (error) {
|
|
144
|
-
debugGraphPrimitiveFallback(
|
|
145
|
-
"[topicScope] Failed to resolve topic by legacy scope",
|
|
146
|
-
{
|
|
147
|
-
error,
|
|
148
|
-
legacyScopeId
|
|
149
|
-
}
|
|
150
|
-
);
|
|
151
|
-
return null;
|
|
92
|
+
const directPredictionStatus = resolvedPredictionStatus(opts.predictionMeta);
|
|
93
|
+
if (directPredictionStatus) {
|
|
94
|
+
return directPredictionStatus;
|
|
152
95
|
}
|
|
96
|
+
const metadataPredictionStatus = resolvedPredictionStatus(
|
|
97
|
+
getPredictionMetaFromMetadata(opts.metadata)
|
|
98
|
+
);
|
|
99
|
+
if (metadataPredictionStatus) {
|
|
100
|
+
return metadataPredictionStatus;
|
|
101
|
+
}
|
|
102
|
+
return null;
|
|
153
103
|
}
|
|
154
|
-
|
|
155
|
-
const
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
if (tenantId && workspaceId) {
|
|
159
|
-
return { tenantId, workspaceId };
|
|
104
|
+
function resolveBeliefLifecycleStatus(opts) {
|
|
105
|
+
const resolvedStatus = shouldTreatBeliefAsResolved(opts);
|
|
106
|
+
if (resolvedStatus) {
|
|
107
|
+
return resolvedStatus;
|
|
160
108
|
}
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
if (
|
|
166
|
-
|
|
109
|
+
const direct = opts.beliefStatus;
|
|
110
|
+
const normalizedDirect = normalizeLegacyBeliefStatus(direct);
|
|
111
|
+
if (normalizedDirect) {
|
|
112
|
+
const normalized = normalizeBeliefConfidence(opts.confidence);
|
|
113
|
+
if (normalized !== null && isPreValidationBeliefStatus(normalizedDirect)) {
|
|
114
|
+
return "active";
|
|
167
115
|
}
|
|
168
|
-
|
|
169
|
-
|
|
116
|
+
return normalizedDirect;
|
|
117
|
+
}
|
|
118
|
+
const metaStatus = opts.metadata?.beliefStatus;
|
|
119
|
+
const normalizedMetaStatus = normalizeLegacyBeliefStatus(metaStatus);
|
|
120
|
+
if (normalizedMetaStatus) {
|
|
121
|
+
const normalized = normalizeBeliefConfidence(opts.confidence);
|
|
122
|
+
if (normalized !== null && isPreValidationBeliefStatus(normalizedMetaStatus)) {
|
|
123
|
+
return "active";
|
|
170
124
|
}
|
|
171
|
-
|
|
125
|
+
return normalizedMetaStatus;
|
|
172
126
|
}
|
|
173
|
-
return
|
|
127
|
+
return "assumption";
|
|
174
128
|
}
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
} catch (error) {
|
|
183
|
-
debugGraphPrimitiveFallback(
|
|
184
|
-
"[topicScope] Failed to load topic by direct id",
|
|
185
|
-
{
|
|
186
|
-
error,
|
|
187
|
-
topicId: args.topicId
|
|
188
|
-
}
|
|
189
|
-
);
|
|
190
|
-
}
|
|
191
|
-
if (!topic) {
|
|
192
|
-
topic = await tryResolveHostTopicById(ctx, String(args.topicId));
|
|
193
|
-
}
|
|
194
|
-
if (!topic) {
|
|
195
|
-
topic = pickPrimaryTopic(
|
|
196
|
-
await findTopicsByScopeAlias(ctx, String(args.topicId))
|
|
197
|
-
) ?? null;
|
|
198
|
-
}
|
|
199
|
-
if (!topic) {
|
|
200
|
-
const nodeScope = await resolveTopicNodeScopeOrNull(
|
|
201
|
-
ctx,
|
|
202
|
-
String(args.topicId)
|
|
203
|
-
);
|
|
204
|
-
if (nodeScope) {
|
|
205
|
-
return nodeScope;
|
|
206
|
-
}
|
|
207
|
-
throw new Error(`Topic not found: ${String(args.topicId)}`);
|
|
208
|
-
}
|
|
209
|
-
const inherited = await resolveInheritedWorkspaceScope(ctx, topic);
|
|
210
|
-
const mapped = asMappedProjectId(topic);
|
|
211
|
-
if (mapped) {
|
|
212
|
-
return {
|
|
213
|
-
topicId: topic._id,
|
|
214
|
-
projectId: mapped,
|
|
215
|
-
tenantId: inherited.tenantId,
|
|
216
|
-
workspaceId: inherited.workspaceId,
|
|
217
|
-
source: "topic"
|
|
218
|
-
};
|
|
219
|
-
}
|
|
220
|
-
return {
|
|
221
|
-
topicId: topic._id,
|
|
222
|
-
tenantId: inherited.tenantId,
|
|
223
|
-
workspaceId: inherited.workspaceId,
|
|
224
|
-
source: "topic"
|
|
225
|
-
};
|
|
129
|
+
function isPreValidationBeliefStatus(status) {
|
|
130
|
+
return status === "assumption" || status === "hypothesis";
|
|
131
|
+
}
|
|
132
|
+
function promoteBeliefStatusAfterScoring(status, opts) {
|
|
133
|
+
const resolvedStatus = shouldTreatBeliefAsResolved({ ...opts });
|
|
134
|
+
if (resolvedStatus) {
|
|
135
|
+
return resolvedStatus;
|
|
226
136
|
}
|
|
227
|
-
if (
|
|
228
|
-
|
|
229
|
-
try {
|
|
230
|
-
directTopic = await ctx.db.get(
|
|
231
|
-
args.projectId
|
|
232
|
-
);
|
|
233
|
-
} catch (error) {
|
|
234
|
-
debugGraphPrimitiveFallback(
|
|
235
|
-
"[topicScope] Failed to load direct project topic",
|
|
236
|
-
{
|
|
237
|
-
error,
|
|
238
|
-
projectId: args.projectId
|
|
239
|
-
}
|
|
240
|
-
);
|
|
241
|
-
}
|
|
242
|
-
if (directTopic) {
|
|
243
|
-
const inherited = await resolveInheritedWorkspaceScope(ctx, directTopic);
|
|
244
|
-
const mapped = asMappedProjectId(directTopic);
|
|
245
|
-
return {
|
|
246
|
-
topicId: directTopic._id,
|
|
247
|
-
projectId: mapped ?? args.projectId,
|
|
248
|
-
tenantId: inherited.tenantId,
|
|
249
|
-
workspaceId: inherited.workspaceId,
|
|
250
|
-
source: "topic_inferred"
|
|
251
|
-
};
|
|
252
|
-
}
|
|
253
|
-
directTopic = await tryResolveHostTopicByLegacyScope(ctx, args.projectId);
|
|
254
|
-
if (directTopic) {
|
|
255
|
-
const inherited = await resolveInheritedWorkspaceScope(ctx, directTopic);
|
|
256
|
-
const mapped = asMappedProjectId(directTopic);
|
|
257
|
-
return {
|
|
258
|
-
topicId: directTopic._id,
|
|
259
|
-
projectId: mapped ?? args.projectId,
|
|
260
|
-
tenantId: inherited.tenantId,
|
|
261
|
-
workspaceId: inherited.workspaceId,
|
|
262
|
-
source: "topic_inferred"
|
|
263
|
-
};
|
|
264
|
-
}
|
|
265
|
-
const topics = await findTopicsByScopeAlias(ctx, args.projectId);
|
|
266
|
-
const primary = pickPrimaryTopic(topics);
|
|
267
|
-
if (primary) {
|
|
268
|
-
const inherited = await resolveInheritedWorkspaceScope(ctx, primary);
|
|
269
|
-
return {
|
|
270
|
-
topicId: primary._id,
|
|
271
|
-
projectId: args.projectId,
|
|
272
|
-
tenantId: inherited.tenantId,
|
|
273
|
-
workspaceId: inherited.workspaceId,
|
|
274
|
-
source: "project_mapped_topic"
|
|
275
|
-
};
|
|
276
|
-
}
|
|
277
|
-
const nodeScope = await resolveTopicNodeScopeOrNull(
|
|
278
|
-
ctx,
|
|
279
|
-
String(args.projectId)
|
|
280
|
-
);
|
|
281
|
-
if (nodeScope) {
|
|
282
|
-
return {
|
|
283
|
-
...nodeScope,
|
|
284
|
-
projectId: nodeScope.projectId ?? String(args.projectId)
|
|
285
|
-
};
|
|
286
|
-
}
|
|
287
|
-
throw new Error(
|
|
288
|
-
`Legacy project scope ${String(args.projectId)} has no mapped topic.`
|
|
289
|
-
);
|
|
137
|
+
if (isPreValidationBeliefStatus(status)) {
|
|
138
|
+
return "active";
|
|
290
139
|
}
|
|
291
|
-
|
|
292
|
-
"Missing scope: provide topicId (preferred) or legacy projectId alias."
|
|
293
|
-
);
|
|
294
|
-
}
|
|
295
|
-
({
|
|
296
|
-
projectId: v.optional(v.string()),
|
|
297
|
-
topicId: v.optional(v.string())
|
|
298
|
-
});
|
|
299
|
-
|
|
300
|
-
// src/workspaceIsolation.ts
|
|
301
|
-
function normalizeScopeValue2(value) {
|
|
302
|
-
if (typeof value !== "string") {
|
|
303
|
-
return;
|
|
304
|
-
}
|
|
305
|
-
const normalized = value.trim();
|
|
306
|
-
return normalized.length > 0 ? normalized : void 0;
|
|
307
|
-
}
|
|
308
|
-
function throwWorkspaceIsolationError(args) {
|
|
309
|
-
const error = new Error(args.message);
|
|
310
|
-
error.status = 409;
|
|
311
|
-
error.code = "INVARIANT_VIOLATION";
|
|
312
|
-
error.invariantCode = args.invariantCode;
|
|
313
|
-
error.suggestion = args.suggestion;
|
|
314
|
-
error.details = args.details;
|
|
315
|
-
throw error;
|
|
316
|
-
}
|
|
317
|
-
function nodeMatchesWorkspaceReasoningScope(node, scope) {
|
|
318
|
-
if (!node) {
|
|
319
|
-
return false;
|
|
320
|
-
}
|
|
321
|
-
const scopeTenantId = normalizeScopeValue2(scope.tenantId);
|
|
322
|
-
const scopeWorkspaceId = normalizeScopeValue2(scope.workspaceId);
|
|
323
|
-
const nodeTenantId = normalizeScopeValue2(node.tenantId);
|
|
324
|
-
const nodeWorkspaceId = normalizeScopeValue2(node.workspaceId);
|
|
325
|
-
const epistemicLayer = typeof node.epistemicLayer === "string" ? node.epistemicLayer : void 0;
|
|
326
|
-
if (scopeTenantId && nodeTenantId && scopeTenantId !== nodeTenantId) {
|
|
327
|
-
return false;
|
|
328
|
-
}
|
|
329
|
-
if (epistemicLayer === "ontological" && nodeWorkspaceId === void 0) {
|
|
330
|
-
return true;
|
|
331
|
-
}
|
|
332
|
-
if (!scopeWorkspaceId && node.publicationStatus === "published") {
|
|
333
|
-
return true;
|
|
334
|
-
}
|
|
335
|
-
if (!scopeWorkspaceId) {
|
|
336
|
-
return nodeWorkspaceId === void 0;
|
|
337
|
-
}
|
|
338
|
-
return scopeWorkspaceId === nodeWorkspaceId;
|
|
339
|
-
}
|
|
340
|
-
function edgeMatchesWorkspaceReasoningScope(edge, scope) {
|
|
341
|
-
if (!edge) {
|
|
342
|
-
return false;
|
|
343
|
-
}
|
|
344
|
-
const scopeTenantId = normalizeScopeValue2(scope.tenantId);
|
|
345
|
-
const scopeWorkspaceId = normalizeScopeValue2(scope.workspaceId);
|
|
346
|
-
const edgeTenantId = normalizeScopeValue2(edge.tenantId);
|
|
347
|
-
const edgeWorkspaceId = normalizeScopeValue2(edge.workspaceId);
|
|
348
|
-
if (scopeTenantId && edgeTenantId && scopeTenantId !== edgeTenantId) {
|
|
349
|
-
return false;
|
|
350
|
-
}
|
|
351
|
-
if (!scopeWorkspaceId) {
|
|
352
|
-
return edgeWorkspaceId === void 0;
|
|
353
|
-
}
|
|
354
|
-
return scopeWorkspaceId === edgeWorkspaceId;
|
|
355
|
-
}
|
|
356
|
-
async function resolveNodeScopeForWorkspaceIsolation(ctx, node) {
|
|
357
|
-
const epistemicLayer = typeof node?.epistemicLayer === "string" ? node.epistemicLayer : void 0;
|
|
358
|
-
const resolved = {
|
|
359
|
-
tenantId: normalizeScopeValue2(node?.tenantId),
|
|
360
|
-
workspaceId: normalizeScopeValue2(node?.workspaceId),
|
|
361
|
-
epistemicLayer,
|
|
362
|
-
nodeType: typeof node?.nodeType === "string" ? node.nodeType : void 0
|
|
363
|
-
};
|
|
364
|
-
if (!node) {
|
|
365
|
-
return resolved;
|
|
366
|
-
}
|
|
367
|
-
if (resolved.epistemicLayer === "ontological") {
|
|
368
|
-
return resolved;
|
|
369
|
-
}
|
|
370
|
-
if (resolved.tenantId || resolved.workspaceId) {
|
|
371
|
-
return resolved;
|
|
372
|
-
}
|
|
373
|
-
if (node.topicId) {
|
|
374
|
-
const topicScope = await resolveTopicProjectScope(ctx, {
|
|
375
|
-
topicId: node.topicId
|
|
376
|
-
});
|
|
377
|
-
return {
|
|
378
|
-
...resolved,
|
|
379
|
-
tenantId: topicScope.tenantId,
|
|
380
|
-
workspaceId: topicScope.workspaceId
|
|
381
|
-
};
|
|
382
|
-
}
|
|
383
|
-
if (node.projectId) {
|
|
384
|
-
const topicScope = await resolveTopicProjectScope(ctx, {
|
|
385
|
-
projectId: String(node.projectId)
|
|
386
|
-
});
|
|
387
|
-
return {
|
|
388
|
-
...resolved,
|
|
389
|
-
tenantId: topicScope.tenantId,
|
|
390
|
-
workspaceId: topicScope.workspaceId
|
|
391
|
-
};
|
|
392
|
-
}
|
|
393
|
-
return resolved;
|
|
394
|
-
}
|
|
395
|
-
function resolveRuntimePackMutationContext(args) {
|
|
396
|
-
if (!args.runtimeToolName && !args.runtimePackKey && !args.runtimePackInstallScope) {
|
|
397
|
-
return;
|
|
398
|
-
}
|
|
399
|
-
return {
|
|
400
|
-
toolName: args.runtimeToolName,
|
|
401
|
-
packKey: args.runtimePackKey,
|
|
402
|
-
packInstallScope: args.runtimePackInstallScope
|
|
403
|
-
};
|
|
404
|
-
}
|
|
405
|
-
function assertTenantPackWorkspaceMutationAllowed(args) {
|
|
406
|
-
if (!args.runtime?.packKey || args.runtime.packInstallScope !== "tenant") {
|
|
407
|
-
return;
|
|
408
|
-
}
|
|
409
|
-
const targetWorkspaceId = normalizeScopeValue2(args.target.workspaceId);
|
|
410
|
-
const targetLayer = typeof args.target.epistemicLayer === "string" ? args.target.epistemicLayer : void 0;
|
|
411
|
-
if (!targetWorkspaceId || targetLayer === "ontological") {
|
|
412
|
-
return;
|
|
413
|
-
}
|
|
414
|
-
throwWorkspaceIsolationError({
|
|
415
|
-
message: `Tenant-scoped pack "${args.runtime.packKey}" cannot mutate workspace-scoped reasoning state.`,
|
|
416
|
-
invariantCode: "workspace.tenant_pack_reasoning_write_forbidden",
|
|
417
|
-
suggestion: "Use a workspace-scoped pack for workspace-local graph mutations, or route the change through tenant-global canonical entity flows.",
|
|
418
|
-
details: {
|
|
419
|
-
mutationName: args.mutationName,
|
|
420
|
-
toolName: args.runtime.toolName,
|
|
421
|
-
packKey: args.runtime.packKey,
|
|
422
|
-
targetWorkspaceId,
|
|
423
|
-
targetNodeType: args.target.nodeType,
|
|
424
|
-
targetLayer
|
|
425
|
-
}
|
|
426
|
-
});
|
|
427
|
-
}
|
|
428
|
-
|
|
429
|
-
// src/beliefLifecycle.ts
|
|
430
|
-
var BELIEF_STATUS_VALUES = [
|
|
431
|
-
"assumption",
|
|
432
|
-
"hypothesis",
|
|
433
|
-
"active",
|
|
434
|
-
"superseded",
|
|
435
|
-
"resolved_true",
|
|
436
|
-
"resolved_false"
|
|
437
|
-
];
|
|
438
|
-
function isBeliefLifecycleStatus(value) {
|
|
439
|
-
return typeof value === "string" && BELIEF_STATUS_VALUES.includes(value);
|
|
440
|
-
}
|
|
441
|
-
function normalizeLegacyBeliefStatus(value) {
|
|
442
|
-
if (isBeliefLifecycleStatus(value)) {
|
|
443
|
-
return value;
|
|
444
|
-
}
|
|
445
|
-
if (value === "belief" || value === "established" || value === "emerging") {
|
|
446
|
-
return "active";
|
|
447
|
-
}
|
|
448
|
-
if (value === "fact" || value === "confirmed") {
|
|
449
|
-
return "resolved_true";
|
|
450
|
-
}
|
|
451
|
-
if (value === "disconfirmed" || value === "expired") {
|
|
452
|
-
return "resolved_false";
|
|
453
|
-
}
|
|
454
|
-
if (value === "deprecated") {
|
|
455
|
-
return "superseded";
|
|
456
|
-
}
|
|
457
|
-
return null;
|
|
458
|
-
}
|
|
459
|
-
function normalizeBeliefConfidence(confidence) {
|
|
460
|
-
if (typeof confidence !== "number" || !Number.isFinite(confidence)) {
|
|
461
|
-
return null;
|
|
462
|
-
}
|
|
463
|
-
if (confidence >= 0 && confidence <= 1) {
|
|
464
|
-
return confidence;
|
|
465
|
-
}
|
|
466
|
-
if (confidence > 1 && confidence <= 100) {
|
|
467
|
-
return confidence / 100;
|
|
468
|
-
}
|
|
469
|
-
return null;
|
|
470
|
-
}
|
|
471
|
-
function isResolvedByConfidence(confidence) {
|
|
472
|
-
const normalized = normalizeBeliefConfidence(confidence);
|
|
473
|
-
if (normalized === null) {
|
|
474
|
-
return false;
|
|
475
|
-
}
|
|
476
|
-
return normalized <= 0 || normalized >= 1;
|
|
477
|
-
}
|
|
478
|
-
function getPredictionMetaFromMetadata(metadata) {
|
|
479
|
-
return metadata?.predictionMeta;
|
|
480
|
-
}
|
|
481
|
-
function resolvedPredictionStatus(predictionMeta) {
|
|
482
|
-
if (!predictionMeta || typeof predictionMeta !== "object") {
|
|
483
|
-
return null;
|
|
484
|
-
}
|
|
485
|
-
const outcome = predictionMeta.outcome;
|
|
486
|
-
if (outcome === "confirmed") {
|
|
487
|
-
return "resolved_true";
|
|
488
|
-
}
|
|
489
|
-
if (outcome === "disconfirmed" || outcome === "expired") {
|
|
490
|
-
return "resolved_false";
|
|
491
|
-
}
|
|
492
|
-
return null;
|
|
493
|
-
}
|
|
494
|
-
function shouldTreatBeliefAsResolved(opts) {
|
|
495
|
-
if (isResolvedByConfidence(opts.confidence)) {
|
|
496
|
-
const normalized = normalizeBeliefConfidence(opts.confidence);
|
|
497
|
-
return normalized === 0 ? "resolved_false" : "resolved_true";
|
|
498
|
-
}
|
|
499
|
-
const directPredictionStatus = resolvedPredictionStatus(opts.predictionMeta);
|
|
500
|
-
if (directPredictionStatus) {
|
|
501
|
-
return directPredictionStatus;
|
|
502
|
-
}
|
|
503
|
-
const metadataPredictionStatus = resolvedPredictionStatus(
|
|
504
|
-
getPredictionMetaFromMetadata(opts.metadata)
|
|
505
|
-
);
|
|
506
|
-
if (metadataPredictionStatus) {
|
|
507
|
-
return metadataPredictionStatus;
|
|
508
|
-
}
|
|
509
|
-
return null;
|
|
510
|
-
}
|
|
511
|
-
function resolveBeliefLifecycleStatus(opts) {
|
|
512
|
-
const resolvedStatus = shouldTreatBeliefAsResolved(opts);
|
|
513
|
-
if (resolvedStatus) {
|
|
514
|
-
return resolvedStatus;
|
|
515
|
-
}
|
|
516
|
-
const direct = opts.beliefStatus;
|
|
517
|
-
const normalizedDirect = normalizeLegacyBeliefStatus(direct);
|
|
518
|
-
if (normalizedDirect) {
|
|
519
|
-
const normalized = normalizeBeliefConfidence(opts.confidence);
|
|
520
|
-
if (normalized !== null && isPreValidationBeliefStatus(normalizedDirect)) {
|
|
521
|
-
return "active";
|
|
522
|
-
}
|
|
523
|
-
return normalizedDirect;
|
|
524
|
-
}
|
|
525
|
-
const metaStatus = opts.metadata?.beliefStatus;
|
|
526
|
-
const normalizedMetaStatus = normalizeLegacyBeliefStatus(metaStatus);
|
|
527
|
-
if (normalizedMetaStatus) {
|
|
528
|
-
const normalized = normalizeBeliefConfidence(opts.confidence);
|
|
529
|
-
if (normalized !== null && isPreValidationBeliefStatus(normalizedMetaStatus)) {
|
|
530
|
-
return "active";
|
|
531
|
-
}
|
|
532
|
-
return normalizedMetaStatus;
|
|
533
|
-
}
|
|
534
|
-
return "assumption";
|
|
535
|
-
}
|
|
536
|
-
function isPreValidationBeliefStatus(status) {
|
|
537
|
-
return status === "assumption" || status === "hypothesis";
|
|
538
|
-
}
|
|
539
|
-
function promoteBeliefStatusAfterScoring(status, opts) {
|
|
540
|
-
const resolvedStatus = shouldTreatBeliefAsResolved({ ...opts });
|
|
541
|
-
if (resolvedStatus) {
|
|
542
|
-
return resolvedStatus;
|
|
543
|
-
}
|
|
544
|
-
if (isPreValidationBeliefStatus(status)) {
|
|
545
|
-
return "active";
|
|
546
|
-
}
|
|
547
|
-
return status;
|
|
140
|
+
return status;
|
|
548
141
|
}
|
|
549
142
|
|
|
550
143
|
// src/edges/contains.ts
|
|
@@ -682,7 +275,7 @@ var dependsOnPropagationSpec = {
|
|
|
682
275
|
description: "Structural gating. Textbook conditional deduction when edge conditionals exist, otherwise damped dependency cascade through downstream chains."
|
|
683
276
|
};
|
|
684
277
|
|
|
685
|
-
// src/edges/
|
|
278
|
+
// src/edges/derived-from.ts
|
|
686
279
|
var derivedFromPropagationSpec = {
|
|
687
280
|
edgeType: "derived_from",
|
|
688
281
|
direction: "incoming",
|
|
@@ -735,7 +328,7 @@ var informsPropagationSpec = {
|
|
|
735
328
|
description: "Evidence-bearing influence. Informs can chain through the graph with light per-hop damping."
|
|
736
329
|
};
|
|
737
330
|
|
|
738
|
-
// src/edges/
|
|
331
|
+
// src/edges/propagation-types.ts
|
|
739
332
|
function isPropagationTraversalDirection(direction) {
|
|
740
333
|
return direction === "outgoing" || direction === "incoming";
|
|
741
334
|
}
|
|
@@ -808,6 +401,9 @@ var testsPropagationSpec = {
|
|
|
808
401
|
};
|
|
809
402
|
|
|
810
403
|
// src/edges/index.ts
|
|
404
|
+
var canContinueTransitively2 = canContinueTransitively;
|
|
405
|
+
var canTraverseHop2 = canTraverseHop;
|
|
406
|
+
var isPropagationTraversalDirection2 = isPropagationTraversalDirection;
|
|
811
407
|
var EDGE_PROPAGATION_SPECS = [
|
|
812
408
|
supportsPropagationSpec,
|
|
813
409
|
informsPropagationSpec,
|
|
@@ -824,16 +420,409 @@ function getEdgePropagationSpecs() {
|
|
|
824
420
|
return EDGE_PROPAGATION_SPECS;
|
|
825
421
|
}
|
|
826
422
|
function getTraversalDirections(direction) {
|
|
827
|
-
if (
|
|
423
|
+
if (isPropagationTraversalDirection2(direction)) {
|
|
828
424
|
return [direction];
|
|
829
425
|
}
|
|
830
|
-
return ["outgoing", "incoming"];
|
|
426
|
+
return ["outgoing", "incoming"];
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
// src/debug.ts
|
|
430
|
+
function isGraphPrimitiveDebugEnabled() {
|
|
431
|
+
const env = globalThis.process?.env;
|
|
432
|
+
return env?.LUCERN_COMPAT_FALLBACK_DEBUG === "1" || env?.LUCERN_GRAPH_DEBUG === "1";
|
|
433
|
+
}
|
|
434
|
+
function debugGraphPrimitiveFallback(message, context) {
|
|
435
|
+
if (!isGraphPrimitiveDebugEnabled()) {
|
|
436
|
+
return;
|
|
437
|
+
}
|
|
438
|
+
console.debug(message, context ?? {});
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
// src/topicScope.ts
|
|
442
|
+
var LEGACY_SCOPE_FIELD = "graphScopeProjectId";
|
|
443
|
+
async function resolveTopicNodeScopeOrNull(ctx, ref) {
|
|
444
|
+
if (!ctx?.db || typeof ctx.db.query !== "function") {
|
|
445
|
+
return null;
|
|
446
|
+
}
|
|
447
|
+
let node = null;
|
|
448
|
+
try {
|
|
449
|
+
const byGlobalId = await ctx.db.query("epistemicNodes").withIndex("by_globalId", (q) => q.eq("globalId", ref)).first();
|
|
450
|
+
if (byGlobalId && byGlobalId.nodeType === "topic") {
|
|
451
|
+
node = byGlobalId;
|
|
452
|
+
}
|
|
453
|
+
} catch (error) {
|
|
454
|
+
debugGraphPrimitiveFallback(
|
|
455
|
+
"[topicScope] topic-node scope lookup by globalId failed",
|
|
456
|
+
{ error, ref }
|
|
457
|
+
);
|
|
458
|
+
}
|
|
459
|
+
if (!node) {
|
|
460
|
+
return null;
|
|
461
|
+
}
|
|
462
|
+
const scopeKey = normalizeScopeValue(node.topicId) ?? normalizeScopeValue(node.globalId);
|
|
463
|
+
if (!scopeKey) {
|
|
464
|
+
return null;
|
|
465
|
+
}
|
|
466
|
+
return {
|
|
467
|
+
topicId: scopeKey,
|
|
468
|
+
projectId: asMappedProjectId(node),
|
|
469
|
+
source: "topic_node"
|
|
470
|
+
};
|
|
471
|
+
}
|
|
472
|
+
function asMappedProjectId(topic) {
|
|
473
|
+
if (!topic) {
|
|
474
|
+
return;
|
|
475
|
+
}
|
|
476
|
+
const directLegacyProjectId = normalizeScopeValue(
|
|
477
|
+
topic[LEGACY_SCOPE_FIELD]
|
|
478
|
+
);
|
|
479
|
+
if (directLegacyProjectId) {
|
|
480
|
+
return directLegacyProjectId;
|
|
481
|
+
}
|
|
482
|
+
const metadata = topic.metadata || {};
|
|
483
|
+
const candidate = metadata[LEGACY_SCOPE_FIELD] || metadata.legacyProjectId || metadata.projectId || metadata.scopeProjectId;
|
|
484
|
+
return typeof candidate === "string" ? normalizeScopeValue(candidate) : void 0;
|
|
485
|
+
}
|
|
486
|
+
function normalizeScopeValue(value) {
|
|
487
|
+
if (typeof value !== "string") {
|
|
488
|
+
return;
|
|
489
|
+
}
|
|
490
|
+
const normalized = value.trim();
|
|
491
|
+
return normalized.length > 0 ? normalized : void 0;
|
|
492
|
+
}
|
|
493
|
+
function pickPrimaryTopic(candidates) {
|
|
494
|
+
return [...candidates].sort((a, b) => {
|
|
495
|
+
const depthA = a.depth ?? 9999;
|
|
496
|
+
const depthB = b.depth ?? 9999;
|
|
497
|
+
if (depthA !== depthB) {
|
|
498
|
+
return depthA - depthB;
|
|
499
|
+
}
|
|
500
|
+
const createdA = a.createdAt ?? Number.MAX_SAFE_INTEGER;
|
|
501
|
+
const createdB = b.createdAt ?? Number.MAX_SAFE_INTEGER;
|
|
502
|
+
if (createdA !== createdB) {
|
|
503
|
+
return createdA - createdB;
|
|
504
|
+
}
|
|
505
|
+
return String(a.name || "").localeCompare(String(b.name || ""));
|
|
506
|
+
})[0];
|
|
507
|
+
}
|
|
508
|
+
async function findTopicsByScopeAlias(ctx, scopeId) {
|
|
509
|
+
const query = ctx.db.query("topics");
|
|
510
|
+
try {
|
|
511
|
+
return await query.withIndex(
|
|
512
|
+
"by_graph_scope_project",
|
|
513
|
+
(q) => q.eq(LEGACY_SCOPE_FIELD, scopeId)
|
|
514
|
+
).collect();
|
|
515
|
+
} catch (error) {
|
|
516
|
+
debugGraphPrimitiveFallback(
|
|
517
|
+
"[topicScope] Failed to resolve scope alias via index",
|
|
518
|
+
{
|
|
519
|
+
error,
|
|
520
|
+
scopeId
|
|
521
|
+
}
|
|
522
|
+
);
|
|
523
|
+
const topics = await query.collect();
|
|
524
|
+
return topics.filter((topic) => {
|
|
525
|
+
const normalizedGlobalId = normalizeScopeValue(topic.globalId);
|
|
526
|
+
const mappedProjectId = asMappedProjectId(topic);
|
|
527
|
+
return String(topic._id) === scopeId || normalizedGlobalId === scopeId || mappedProjectId === scopeId;
|
|
528
|
+
});
|
|
529
|
+
}
|
|
530
|
+
}
|
|
531
|
+
async function tryResolveHostTopicById(ctx, topicId) {
|
|
532
|
+
if (typeof ctx.runQuery !== "function") {
|
|
533
|
+
return null;
|
|
534
|
+
}
|
|
535
|
+
try {
|
|
536
|
+
return await ctx.runQuery(api.topics.get, {
|
|
537
|
+
id: topicId
|
|
538
|
+
}) ?? null;
|
|
539
|
+
} catch (error) {
|
|
540
|
+
debugGraphPrimitiveFallback(
|
|
541
|
+
"[topicScope] Failed to resolve topic by host query",
|
|
542
|
+
{
|
|
543
|
+
error,
|
|
544
|
+
topicId
|
|
545
|
+
}
|
|
546
|
+
);
|
|
547
|
+
return null;
|
|
548
|
+
}
|
|
549
|
+
}
|
|
550
|
+
async function tryResolveHostTopicByLegacyScope(ctx, legacyScopeId) {
|
|
551
|
+
if (typeof ctx.runQuery !== "function") {
|
|
552
|
+
return null;
|
|
553
|
+
}
|
|
554
|
+
try {
|
|
555
|
+
return await ctx.runQuery(api.topics.getByLegacyScopeId, {
|
|
556
|
+
projectId: legacyScopeId
|
|
557
|
+
}) ?? null;
|
|
558
|
+
} catch (error) {
|
|
559
|
+
debugGraphPrimitiveFallback(
|
|
560
|
+
"[topicScope] Failed to resolve topic by legacy scope",
|
|
561
|
+
{
|
|
562
|
+
error,
|
|
563
|
+
legacyScopeId
|
|
564
|
+
}
|
|
565
|
+
);
|
|
566
|
+
return null;
|
|
567
|
+
}
|
|
568
|
+
}
|
|
569
|
+
async function resolveInheritedWorkspaceScope(ctx, topic) {
|
|
570
|
+
const MAX_DEPTH = 10;
|
|
571
|
+
let tenantId = normalizeScopeValue(topic.tenantId);
|
|
572
|
+
let workspaceId = normalizeScopeValue(topic.workspaceId);
|
|
573
|
+
if (tenantId && workspaceId) {
|
|
574
|
+
return { tenantId, workspaceId };
|
|
575
|
+
}
|
|
576
|
+
let current = topic;
|
|
577
|
+
for (let i = 0; i < MAX_DEPTH && current?.parentTopicId; i++) {
|
|
578
|
+
current = await ctx.db.get(current.parentTopicId);
|
|
579
|
+
if (!current) {
|
|
580
|
+
break;
|
|
581
|
+
}
|
|
582
|
+
if (!tenantId) {
|
|
583
|
+
tenantId = normalizeScopeValue(current.tenantId);
|
|
584
|
+
}
|
|
585
|
+
if (!workspaceId) {
|
|
586
|
+
workspaceId = normalizeScopeValue(current.workspaceId);
|
|
587
|
+
}
|
|
588
|
+
if (tenantId && workspaceId) {
|
|
589
|
+
break;
|
|
590
|
+
}
|
|
591
|
+
}
|
|
592
|
+
return { tenantId, workspaceId };
|
|
593
|
+
}
|
|
594
|
+
async function resolveTopicProjectScope(ctx, args) {
|
|
595
|
+
if (args.topicId) {
|
|
596
|
+
return await resolveScopeFromTopicId(ctx, args.topicId);
|
|
597
|
+
}
|
|
598
|
+
if (args.projectId) {
|
|
599
|
+
return await resolveScopeFromLegacyProjectId(ctx, args.projectId);
|
|
600
|
+
}
|
|
601
|
+
throw new Error(
|
|
602
|
+
"Missing scope: provide topicId (preferred) or legacy projectId alias."
|
|
603
|
+
);
|
|
604
|
+
}
|
|
605
|
+
async function resolveScopeFromTopicId(ctx, topicId) {
|
|
606
|
+
const topic = await resolveTopicDocFromTopicId(ctx, topicId);
|
|
607
|
+
if (topic) {
|
|
608
|
+
return await buildTopicScope(ctx, topic, "topic");
|
|
609
|
+
}
|
|
610
|
+
const nodeScope = await resolveTopicNodeScopeOrNull(ctx, String(topicId));
|
|
611
|
+
if (nodeScope) {
|
|
612
|
+
return nodeScope;
|
|
613
|
+
}
|
|
614
|
+
throw new Error(`Topic not found: ${String(topicId)}`);
|
|
615
|
+
}
|
|
616
|
+
async function resolveTopicDocFromTopicId(ctx, topicId) {
|
|
617
|
+
const direct = await tryReadTopicDoc(ctx, topicId, {
|
|
618
|
+
failureLog: "[topicScope] Failed to load topic by direct id",
|
|
619
|
+
idLogKey: "topicId"
|
|
620
|
+
});
|
|
621
|
+
if (direct) {
|
|
622
|
+
return direct;
|
|
623
|
+
}
|
|
624
|
+
const hostTopic = await tryResolveHostTopicById(ctx, String(topicId));
|
|
625
|
+
if (hostTopic) {
|
|
626
|
+
return hostTopic;
|
|
627
|
+
}
|
|
628
|
+
return pickPrimaryTopic(await findTopicsByScopeAlias(ctx, String(topicId))) ?? null;
|
|
629
|
+
}
|
|
630
|
+
async function resolveScopeFromLegacyProjectId(ctx, legacyProjectId) {
|
|
631
|
+
const directTopic = await resolveDirectLegacyProjectTopic(
|
|
632
|
+
ctx,
|
|
633
|
+
legacyProjectId
|
|
634
|
+
);
|
|
635
|
+
if (directTopic) {
|
|
636
|
+
return await buildTopicScope(ctx, directTopic, "topic_inferred", {
|
|
637
|
+
fallbackProjectId: legacyProjectId
|
|
638
|
+
});
|
|
639
|
+
}
|
|
640
|
+
const primary = pickPrimaryTopic(
|
|
641
|
+
await findTopicsByScopeAlias(ctx, legacyProjectId)
|
|
642
|
+
);
|
|
643
|
+
if (primary) {
|
|
644
|
+
return await buildTopicScope(ctx, primary, "project_mapped_topic", {
|
|
645
|
+
fallbackProjectId: legacyProjectId
|
|
646
|
+
});
|
|
647
|
+
}
|
|
648
|
+
const nodeScope = await resolveTopicNodeScopeOrNull(ctx, legacyProjectId);
|
|
649
|
+
if (nodeScope) {
|
|
650
|
+
return {
|
|
651
|
+
...nodeScope,
|
|
652
|
+
projectId: nodeScope.projectId ?? legacyProjectId
|
|
653
|
+
};
|
|
654
|
+
}
|
|
655
|
+
throw new Error(
|
|
656
|
+
`Legacy project scope ${legacyProjectId} has no mapped topic.`
|
|
657
|
+
);
|
|
658
|
+
}
|
|
659
|
+
async function resolveDirectLegacyProjectTopic(ctx, legacyProjectId) {
|
|
660
|
+
const directTopic = await tryReadTopicDoc(ctx, legacyProjectId, {
|
|
661
|
+
failureLog: "[topicScope] Failed to load direct project topic",
|
|
662
|
+
idLogKey: "projectId"
|
|
663
|
+
});
|
|
664
|
+
return directTopic ?? tryResolveHostTopicByLegacyScope(ctx, legacyProjectId);
|
|
665
|
+
}
|
|
666
|
+
async function tryReadTopicDoc(ctx, id, log) {
|
|
667
|
+
try {
|
|
668
|
+
return await ctx.db.get(id);
|
|
669
|
+
} catch (error) {
|
|
670
|
+
debugGraphPrimitiveFallback(log.failureLog, {
|
|
671
|
+
error,
|
|
672
|
+
[log.idLogKey]: id
|
|
673
|
+
});
|
|
674
|
+
return null;
|
|
675
|
+
}
|
|
676
|
+
}
|
|
677
|
+
async function buildTopicScope(ctx, topic, source, options = {}) {
|
|
678
|
+
const inherited = await resolveInheritedWorkspaceScope(ctx, topic);
|
|
679
|
+
const mapped = asMappedProjectId(topic);
|
|
680
|
+
return {
|
|
681
|
+
topicId: topic._id,
|
|
682
|
+
...mapped || options.fallbackProjectId ? { projectId: mapped ?? options.fallbackProjectId } : {},
|
|
683
|
+
tenantId: inherited.tenantId,
|
|
684
|
+
workspaceId: inherited.workspaceId,
|
|
685
|
+
source
|
|
686
|
+
};
|
|
687
|
+
}
|
|
688
|
+
({
|
|
689
|
+
projectId: v.optional(v.string()),
|
|
690
|
+
topicId: v.optional(v.string())
|
|
691
|
+
});
|
|
692
|
+
|
|
693
|
+
// src/workspaceIsolation.ts
|
|
694
|
+
function normalizeScopeValue2(value) {
|
|
695
|
+
if (typeof value !== "string") {
|
|
696
|
+
return;
|
|
697
|
+
}
|
|
698
|
+
const normalized = value.trim();
|
|
699
|
+
return normalized.length > 0 ? normalized : void 0;
|
|
700
|
+
}
|
|
701
|
+
function throwWorkspaceIsolationError(args) {
|
|
702
|
+
const error = new Error(args.message);
|
|
703
|
+
error.status = 409;
|
|
704
|
+
error.code = "INVARIANT_VIOLATION";
|
|
705
|
+
error.invariantCode = args.invariantCode;
|
|
706
|
+
error.suggestion = args.suggestion;
|
|
707
|
+
error.details = args.details;
|
|
708
|
+
throw error;
|
|
709
|
+
}
|
|
710
|
+
function nodeMatchesWorkspaceReasoningScope(node, scope) {
|
|
711
|
+
if (!node) {
|
|
712
|
+
return false;
|
|
713
|
+
}
|
|
714
|
+
const scopeTenantId = normalizeScopeValue2(scope.tenantId);
|
|
715
|
+
const scopeWorkspaceId = normalizeScopeValue2(scope.workspaceId);
|
|
716
|
+
const nodeTenantId = normalizeScopeValue2(node.tenantId);
|
|
717
|
+
const nodeWorkspaceId = normalizeScopeValue2(node.workspaceId);
|
|
718
|
+
const epistemicLayer = typeof node.epistemicLayer === "string" ? node.epistemicLayer : void 0;
|
|
719
|
+
if (scopeTenantId && nodeTenantId && scopeTenantId !== nodeTenantId) {
|
|
720
|
+
return false;
|
|
721
|
+
}
|
|
722
|
+
if (epistemicLayer === "ontological" && nodeWorkspaceId === void 0) {
|
|
723
|
+
return true;
|
|
724
|
+
}
|
|
725
|
+
if (!scopeWorkspaceId && node.publicationStatus === "published") {
|
|
726
|
+
return true;
|
|
727
|
+
}
|
|
728
|
+
if (!scopeWorkspaceId) {
|
|
729
|
+
return nodeWorkspaceId === void 0;
|
|
730
|
+
}
|
|
731
|
+
return scopeWorkspaceId === nodeWorkspaceId;
|
|
732
|
+
}
|
|
733
|
+
function edgeMatchesWorkspaceReasoningScope(edge, scope) {
|
|
734
|
+
if (!edge) {
|
|
735
|
+
return false;
|
|
736
|
+
}
|
|
737
|
+
const scopeTenantId = normalizeScopeValue2(scope.tenantId);
|
|
738
|
+
const scopeWorkspaceId = normalizeScopeValue2(scope.workspaceId);
|
|
739
|
+
const edgeTenantId = normalizeScopeValue2(edge.tenantId);
|
|
740
|
+
const edgeWorkspaceId = normalizeScopeValue2(edge.workspaceId);
|
|
741
|
+
if (scopeTenantId && edgeTenantId && scopeTenantId !== edgeTenantId) {
|
|
742
|
+
return false;
|
|
743
|
+
}
|
|
744
|
+
if (!scopeWorkspaceId) {
|
|
745
|
+
return edgeWorkspaceId === void 0;
|
|
746
|
+
}
|
|
747
|
+
return scopeWorkspaceId === edgeWorkspaceId;
|
|
748
|
+
}
|
|
749
|
+
async function resolveNodeScopeForWorkspaceIsolation(ctx, node) {
|
|
750
|
+
const epistemicLayer = typeof node?.epistemicLayer === "string" ? node.epistemicLayer : void 0;
|
|
751
|
+
const resolved = {
|
|
752
|
+
tenantId: normalizeScopeValue2(node?.tenantId),
|
|
753
|
+
workspaceId: normalizeScopeValue2(node?.workspaceId),
|
|
754
|
+
epistemicLayer,
|
|
755
|
+
nodeType: typeof node?.nodeType === "string" ? node.nodeType : void 0
|
|
756
|
+
};
|
|
757
|
+
if (!node) {
|
|
758
|
+
return resolved;
|
|
759
|
+
}
|
|
760
|
+
if (resolved.epistemicLayer === "ontological") {
|
|
761
|
+
return resolved;
|
|
762
|
+
}
|
|
763
|
+
if (resolved.tenantId || resolved.workspaceId) {
|
|
764
|
+
return resolved;
|
|
765
|
+
}
|
|
766
|
+
const topicId = normalizeScopeValue2(node.topicId);
|
|
767
|
+
if (topicId) {
|
|
768
|
+
const topicScope = await resolveTopicProjectScope(ctx, {
|
|
769
|
+
topicId
|
|
770
|
+
});
|
|
771
|
+
return {
|
|
772
|
+
...resolved,
|
|
773
|
+
tenantId: topicScope.tenantId,
|
|
774
|
+
workspaceId: topicScope.workspaceId
|
|
775
|
+
};
|
|
776
|
+
}
|
|
777
|
+
if (node.projectId) {
|
|
778
|
+
const topicScope = await resolveTopicProjectScope(ctx, {
|
|
779
|
+
projectId: String(node.projectId)
|
|
780
|
+
});
|
|
781
|
+
return {
|
|
782
|
+
...resolved,
|
|
783
|
+
tenantId: topicScope.tenantId,
|
|
784
|
+
workspaceId: topicScope.workspaceId
|
|
785
|
+
};
|
|
786
|
+
}
|
|
787
|
+
return resolved;
|
|
788
|
+
}
|
|
789
|
+
function resolveRuntimePackMutationContext(args) {
|
|
790
|
+
if (!(args.runtimeToolName || args.runtimePackKey || args.runtimePackInstallScope)) {
|
|
791
|
+
return;
|
|
792
|
+
}
|
|
793
|
+
return {
|
|
794
|
+
toolName: args.runtimeToolName,
|
|
795
|
+
packKey: args.runtimePackKey,
|
|
796
|
+
packInstallScope: args.runtimePackInstallScope
|
|
797
|
+
};
|
|
798
|
+
}
|
|
799
|
+
function assertTenantPackWorkspaceMutationAllowed(args) {
|
|
800
|
+
if (!args.runtime?.packKey || args.runtime.packInstallScope !== "tenant") {
|
|
801
|
+
return;
|
|
802
|
+
}
|
|
803
|
+
const targetWorkspaceId = normalizeScopeValue2(args.target.workspaceId);
|
|
804
|
+
const targetLayer = typeof args.target.epistemicLayer === "string" ? args.target.epistemicLayer : void 0;
|
|
805
|
+
if (!targetWorkspaceId || targetLayer === "ontological") {
|
|
806
|
+
return;
|
|
807
|
+
}
|
|
808
|
+
throwWorkspaceIsolationError({
|
|
809
|
+
message: `Tenant-scoped pack "${args.runtime.packKey}" cannot mutate workspace-scoped reasoning state.`,
|
|
810
|
+
invariantCode: "workspace.tenant_pack_reasoning_write_forbidden",
|
|
811
|
+
suggestion: "Use a workspace-scoped pack for workspace-local graph mutations, or route the change through tenant-global canonical entity flows.",
|
|
812
|
+
details: {
|
|
813
|
+
mutationName: args.mutationName,
|
|
814
|
+
toolName: args.runtime.toolName,
|
|
815
|
+
packKey: args.runtime.packKey,
|
|
816
|
+
targetWorkspaceId,
|
|
817
|
+
targetNodeType: args.target.nodeType,
|
|
818
|
+
targetLayer
|
|
819
|
+
}
|
|
820
|
+
});
|
|
831
821
|
}
|
|
832
822
|
|
|
833
823
|
// src/confidencePropagationDispatch.ts
|
|
834
|
-
function
|
|
835
|
-
|
|
836
|
-
return targetNodeId ?? void 0;
|
|
824
|
+
function nodeIdToCacheKey(nodeId) {
|
|
825
|
+
return String(nodeId);
|
|
837
826
|
}
|
|
838
827
|
function readNodeOpinion(node) {
|
|
839
828
|
const metadata = node.metadata ?? {};
|
|
@@ -849,137 +838,281 @@ function readNodeOpinion(node) {
|
|
|
849
838
|
return mkOpinion(0, 0, 1, 0.5);
|
|
850
839
|
}
|
|
851
840
|
}
|
|
841
|
+
function resolveTraversalTargetNodeId(edge, direction) {
|
|
842
|
+
const targetNodeId = direction === "outgoing" ? edge.toNodeId : edge.fromNodeId;
|
|
843
|
+
return targetNodeId ?? void 0;
|
|
844
|
+
}
|
|
845
|
+
function buildInitialState(sourceNodeId, sourceOpinion) {
|
|
846
|
+
return {
|
|
847
|
+
nodeId: sourceNodeId,
|
|
848
|
+
opinion: sourceOpinion,
|
|
849
|
+
hop: 0,
|
|
850
|
+
visitedNodeIds: /* @__PURE__ */ new Set([nodeIdToCacheKey(sourceNodeId)])
|
|
851
|
+
};
|
|
852
|
+
}
|
|
853
|
+
function extendVisited(currentVisited, targetNodeId) {
|
|
854
|
+
return /* @__PURE__ */ new Set([...currentVisited, nodeIdToCacheKey(targetNodeId)]);
|
|
855
|
+
}
|
|
856
|
+
function makeTransitiveState(current, targetNodeId, projectedOpinion, nextHop) {
|
|
857
|
+
return {
|
|
858
|
+
nodeId: targetNodeId,
|
|
859
|
+
opinion: projectedOpinion,
|
|
860
|
+
hop: nextHop,
|
|
861
|
+
visitedNodeIds: extendVisited(current.visitedNodeIds, targetNodeId)
|
|
862
|
+
};
|
|
863
|
+
}
|
|
864
|
+
function makeDispatchRecord(targetNodeId, spec, direction, edge, projectedOpinion, nextHop, result, existingDispatch) {
|
|
865
|
+
return {
|
|
866
|
+
targetNodeId,
|
|
867
|
+
edgeType: spec.edgeType,
|
|
868
|
+
traversedDirection: direction,
|
|
869
|
+
weight: edge.weight ?? 1,
|
|
870
|
+
opinion: projectedOpinion,
|
|
871
|
+
operator: result.operator,
|
|
872
|
+
rationale: existingDispatch ? `${existingDispatch.rationale}; ${result.rationale}` : result.rationale,
|
|
873
|
+
hop: nextHop
|
|
874
|
+
};
|
|
875
|
+
}
|
|
876
|
+
async function loadCachedNode(scope, nodeId) {
|
|
877
|
+
const cacheKey = nodeIdToCacheKey(nodeId);
|
|
878
|
+
if (!scope.nodeCache.has(cacheKey)) {
|
|
879
|
+
scope.nodeCache.set(cacheKey, await scope.args.getNode(nodeId));
|
|
880
|
+
}
|
|
881
|
+
return scope.nodeCache.get(cacheKey) ?? null;
|
|
882
|
+
}
|
|
883
|
+
async function collectTargetDispatch(state, nextHop, spec, direction, edge, queue, scope) {
|
|
884
|
+
const sourceScope = scope.args.sourceScope;
|
|
885
|
+
if (sourceScope && !edgeMatchesWorkspaceReasoningScope(edge, sourceScope)) {
|
|
886
|
+
return;
|
|
887
|
+
}
|
|
888
|
+
const targetNodeId = resolveTraversalTargetNodeId(edge, direction);
|
|
889
|
+
if (!targetNodeId) {
|
|
890
|
+
return;
|
|
891
|
+
}
|
|
892
|
+
const targetNodeIdKey = nodeIdToCacheKey(targetNodeId);
|
|
893
|
+
if (state.visitedNodeIds.has(targetNodeIdKey)) {
|
|
894
|
+
return;
|
|
895
|
+
}
|
|
896
|
+
const targetNode = await loadCachedNode(scope, targetNodeId);
|
|
897
|
+
if (targetNode?.nodeType !== "belief") {
|
|
898
|
+
return;
|
|
899
|
+
}
|
|
900
|
+
if (sourceScope && !nodeMatchesWorkspaceReasoningScope(targetNode, sourceScope)) {
|
|
901
|
+
return;
|
|
902
|
+
}
|
|
903
|
+
const targetOpinion = scope.opinionCache.get(targetNodeIdKey) ?? readNodeOpinion(targetNode);
|
|
904
|
+
const result = spec.operator(state.opinion, targetOpinion, edge, {
|
|
905
|
+
hop: nextHop,
|
|
906
|
+
sourceNodeId: state.nodeId,
|
|
907
|
+
targetNodeId,
|
|
908
|
+
traversedDirection: direction,
|
|
909
|
+
spec
|
|
910
|
+
});
|
|
911
|
+
if (!(result && hasProjectedOpinionChanged(targetOpinion, result.opinion))) {
|
|
912
|
+
return;
|
|
913
|
+
}
|
|
914
|
+
const projectedOpinion = mkOpinion(
|
|
915
|
+
result.opinion.b,
|
|
916
|
+
result.opinion.d,
|
|
917
|
+
result.opinion.u,
|
|
918
|
+
result.opinion.a
|
|
919
|
+
);
|
|
920
|
+
scope.opinionCache.set(targetNodeIdKey, projectedOpinion);
|
|
921
|
+
const existingDispatch = scope.dispatchesByTargetId.get(targetNodeIdKey);
|
|
922
|
+
scope.dispatchesByTargetId.set(
|
|
923
|
+
targetNodeIdKey,
|
|
924
|
+
makeDispatchRecord(
|
|
925
|
+
targetNodeId,
|
|
926
|
+
spec,
|
|
927
|
+
direction,
|
|
928
|
+
edge,
|
|
929
|
+
projectedOpinion,
|
|
930
|
+
nextHop,
|
|
931
|
+
result,
|
|
932
|
+
existingDispatch
|
|
933
|
+
)
|
|
934
|
+
);
|
|
935
|
+
if (canContinueTransitively2(spec, nextHop)) {
|
|
936
|
+
queue.push(
|
|
937
|
+
makeTransitiveState(state, targetNodeId, projectedOpinion, nextHop)
|
|
938
|
+
);
|
|
939
|
+
}
|
|
940
|
+
}
|
|
941
|
+
async function processTraversalSpec(state, nextHop, spec, queue, scope) {
|
|
942
|
+
const sourceNodeId = state.nodeId;
|
|
943
|
+
for (const direction of getTraversalDirections(spec.direction)) {
|
|
944
|
+
const edges = await scope.args.queryEdges({
|
|
945
|
+
nodeId: sourceNodeId,
|
|
946
|
+
spec,
|
|
947
|
+
direction,
|
|
948
|
+
hop: nextHop
|
|
949
|
+
});
|
|
950
|
+
for (const edge of edges) {
|
|
951
|
+
await collectTargetDispatch(
|
|
952
|
+
state,
|
|
953
|
+
nextHop,
|
|
954
|
+
spec,
|
|
955
|
+
direction,
|
|
956
|
+
edge,
|
|
957
|
+
queue,
|
|
958
|
+
scope
|
|
959
|
+
);
|
|
960
|
+
}
|
|
961
|
+
}
|
|
962
|
+
}
|
|
963
|
+
async function processQueuedState(state, queue, scope) {
|
|
964
|
+
const nextHop = state.hop + 1;
|
|
965
|
+
for (const spec of scope.traversalSpecs) {
|
|
966
|
+
if (!canTraverseHop2(spec, nextHop)) {
|
|
967
|
+
continue;
|
|
968
|
+
}
|
|
969
|
+
await processTraversalSpec(state, nextHop, spec, queue, scope);
|
|
970
|
+
}
|
|
971
|
+
}
|
|
972
|
+
function sortDispatches(dispatches) {
|
|
973
|
+
return Array.from(dispatches).sort((left, right) => {
|
|
974
|
+
if (left.hop !== right.hop) {
|
|
975
|
+
return left.hop - right.hop;
|
|
976
|
+
}
|
|
977
|
+
return String(left.targetNodeId).localeCompare(String(right.targetNodeId));
|
|
978
|
+
});
|
|
979
|
+
}
|
|
852
980
|
async function collectConfidencePropagationDispatches(args) {
|
|
853
981
|
const dispatchesByTargetId = /* @__PURE__ */ new Map();
|
|
854
982
|
const opinionCache = /* @__PURE__ */ new Map();
|
|
855
983
|
const nodeCache = /* @__PURE__ */ new Map();
|
|
856
984
|
const traversalSpecs = args.traversalSpecs ?? getEdgePropagationSpecs();
|
|
985
|
+
const scope = {
|
|
986
|
+
args,
|
|
987
|
+
dispatchesByTargetId,
|
|
988
|
+
opinionCache,
|
|
989
|
+
nodeCache,
|
|
990
|
+
traversalSpecs
|
|
991
|
+
};
|
|
857
992
|
const queue = [
|
|
858
|
-
|
|
859
|
-
nodeId: args.sourceNodeId,
|
|
860
|
-
opinion: args.sourceOpinion,
|
|
861
|
-
hop: 0,
|
|
862
|
-
visitedNodeIds: /* @__PURE__ */ new Set([String(args.sourceNodeId)])
|
|
863
|
-
}
|
|
993
|
+
buildInitialState(args.sourceNodeId, args.sourceOpinion)
|
|
864
994
|
];
|
|
865
|
-
const loadNode = async (nodeId) => {
|
|
866
|
-
const cacheKey = String(nodeId);
|
|
867
|
-
if (!nodeCache.has(cacheKey)) {
|
|
868
|
-
nodeCache.set(cacheKey, await args.getNode(nodeId));
|
|
869
|
-
}
|
|
870
|
-
return nodeCache.get(cacheKey) ?? null;
|
|
871
|
-
};
|
|
872
995
|
while (queue.length > 0) {
|
|
873
996
|
const state = queue.shift();
|
|
874
997
|
if (!state) {
|
|
875
998
|
continue;
|
|
876
999
|
}
|
|
877
|
-
|
|
878
|
-
const nextHop = state.hop + 1;
|
|
879
|
-
if (!canTraverseHop(spec, nextHop)) {
|
|
880
|
-
continue;
|
|
881
|
-
}
|
|
882
|
-
for (const direction of getTraversalDirections(spec.direction)) {
|
|
883
|
-
const edges = await args.queryEdges({
|
|
884
|
-
nodeId: state.nodeId,
|
|
885
|
-
spec,
|
|
886
|
-
direction,
|
|
887
|
-
hop: nextHop
|
|
888
|
-
});
|
|
889
|
-
for (const edge of edges) {
|
|
890
|
-
if (args.sourceScope && !edgeMatchesWorkspaceReasoningScope(edge, args.sourceScope)) {
|
|
891
|
-
continue;
|
|
892
|
-
}
|
|
893
|
-
const targetNodeId = resolveTraversalTargetNodeId(edge, direction);
|
|
894
|
-
if (!targetNodeId) {
|
|
895
|
-
continue;
|
|
896
|
-
}
|
|
897
|
-
if (state.visitedNodeIds.has(String(targetNodeId))) {
|
|
898
|
-
continue;
|
|
899
|
-
}
|
|
900
|
-
const targetNode = await loadNode(targetNodeId);
|
|
901
|
-
if (!targetNode || targetNode.nodeType !== "belief") {
|
|
902
|
-
continue;
|
|
903
|
-
}
|
|
904
|
-
if (args.sourceScope && !nodeMatchesWorkspaceReasoningScope(targetNode, args.sourceScope)) {
|
|
905
|
-
continue;
|
|
906
|
-
}
|
|
907
|
-
const cacheKey = String(targetNodeId);
|
|
908
|
-
const targetOpinion = opinionCache.get(cacheKey) ?? readNodeOpinion(targetNode);
|
|
909
|
-
const result = spec.operator(state.opinion, targetOpinion, edge, {
|
|
910
|
-
hop: nextHop,
|
|
911
|
-
sourceNodeId: state.nodeId,
|
|
912
|
-
targetNodeId,
|
|
913
|
-
traversedDirection: direction,
|
|
914
|
-
spec
|
|
915
|
-
});
|
|
916
|
-
if (!result || !hasProjectedOpinionChanged(targetOpinion, result.opinion)) {
|
|
917
|
-
continue;
|
|
918
|
-
}
|
|
919
|
-
const projectedOpinion = mkOpinion(
|
|
920
|
-
result.opinion.b,
|
|
921
|
-
result.opinion.d,
|
|
922
|
-
result.opinion.u,
|
|
923
|
-
result.opinion.a
|
|
924
|
-
);
|
|
925
|
-
opinionCache.set(cacheKey, projectedOpinion);
|
|
926
|
-
const existingDispatch = dispatchesByTargetId.get(cacheKey);
|
|
927
|
-
dispatchesByTargetId.set(cacheKey, {
|
|
928
|
-
targetNodeId,
|
|
929
|
-
edgeType: spec.edgeType,
|
|
930
|
-
traversedDirection: direction,
|
|
931
|
-
weight: edge.weight ?? 1,
|
|
932
|
-
opinion: projectedOpinion,
|
|
933
|
-
operator: result.operator,
|
|
934
|
-
rationale: existingDispatch ? `${existingDispatch.rationale}; ${result.rationale}` : result.rationale,
|
|
935
|
-
hop: nextHop
|
|
936
|
-
});
|
|
937
|
-
if (canContinueTransitively(spec, nextHop)) {
|
|
938
|
-
queue.push({
|
|
939
|
-
nodeId: targetNodeId,
|
|
940
|
-
opinion: projectedOpinion,
|
|
941
|
-
hop: nextHop,
|
|
942
|
-
visitedNodeIds: /* @__PURE__ */ new Set([
|
|
943
|
-
...state.visitedNodeIds,
|
|
944
|
-
String(targetNodeId)
|
|
945
|
-
])
|
|
946
|
-
});
|
|
947
|
-
}
|
|
948
|
-
}
|
|
949
|
-
}
|
|
950
|
-
}
|
|
1000
|
+
await processQueuedState(state, queue, scope);
|
|
951
1001
|
}
|
|
952
|
-
return
|
|
953
|
-
if (left.hop !== right.hop) {
|
|
954
|
-
return left.hop - right.hop;
|
|
955
|
-
}
|
|
956
|
-
return String(left.targetNodeId).localeCompare(String(right.targetNodeId));
|
|
957
|
-
});
|
|
1002
|
+
return sortDispatches(dispatchesByTargetId.values());
|
|
958
1003
|
}
|
|
959
1004
|
v.id("epistemicNodes");
|
|
960
1005
|
var DEFAULT_CONFIDENCE_POLICY = {
|
|
961
1006
|
scoringMode: "after_worktree",
|
|
962
1007
|
tupleContradiction: normalizeTupleContradictionPolicy()
|
|
963
1008
|
};
|
|
964
|
-
function
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
1009
|
+
function readFiniteNumber(value) {
|
|
1010
|
+
return typeof value === "number" && Number.isFinite(value) ? value : void 0;
|
|
1011
|
+
}
|
|
1012
|
+
function isRecord(value) {
|
|
1013
|
+
return Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
1014
|
+
}
|
|
1015
|
+
function readOptionalString(value) {
|
|
1016
|
+
return typeof value === "string" && value.trim().length > 0 ? value : void 0;
|
|
1017
|
+
}
|
|
1018
|
+
function readStringArray(value) {
|
|
1019
|
+
if (!Array.isArray(value)) {
|
|
1020
|
+
return;
|
|
1021
|
+
}
|
|
1022
|
+
const strings = value.filter(
|
|
1023
|
+
(item) => typeof item === "string" && item.length > 0
|
|
976
1024
|
);
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
1025
|
+
return strings.length === value.length ? strings : void 0;
|
|
1026
|
+
}
|
|
1027
|
+
function readRecord(value) {
|
|
1028
|
+
return isRecord(value) ? value : void 0;
|
|
1029
|
+
}
|
|
1030
|
+
function readBeliefNodeView(value) {
|
|
1031
|
+
if (!isRecord(value)) {
|
|
1032
|
+
return null;
|
|
1033
|
+
}
|
|
1034
|
+
const id = readOptionalString(value._id);
|
|
1035
|
+
const nodeType = readOptionalString(value.nodeType);
|
|
1036
|
+
if (!(id && nodeType === "belief")) {
|
|
1037
|
+
return null;
|
|
1038
|
+
}
|
|
1039
|
+
const node = {
|
|
1040
|
+
_id: id,
|
|
1041
|
+
nodeType
|
|
1042
|
+
};
|
|
1043
|
+
const creationTime = readFiniteNumber(value._creationTime);
|
|
1044
|
+
if (creationTime !== void 0) {
|
|
1045
|
+
node._creationTime = creationTime;
|
|
1046
|
+
}
|
|
1047
|
+
const metadata = readRecord(value.metadata);
|
|
1048
|
+
if (metadata !== void 0) {
|
|
1049
|
+
node.metadata = metadata;
|
|
1050
|
+
}
|
|
1051
|
+
const opinionA = readFiniteNumber(value.opinion_a);
|
|
1052
|
+
if (opinionA !== void 0) {
|
|
1053
|
+
node.opinion_a = opinionA;
|
|
1054
|
+
}
|
|
1055
|
+
const opinionB = readFiniteNumber(value.opinion_b);
|
|
1056
|
+
if (opinionB !== void 0) {
|
|
1057
|
+
node.opinion_b = opinionB;
|
|
1058
|
+
}
|
|
1059
|
+
const opinionD = readFiniteNumber(value.opinion_d);
|
|
1060
|
+
if (opinionD !== void 0) {
|
|
1061
|
+
node.opinion_d = opinionD;
|
|
1062
|
+
}
|
|
1063
|
+
const opinionU = readFiniteNumber(value.opinion_u);
|
|
1064
|
+
if (opinionU !== void 0) {
|
|
1065
|
+
node.opinion_u = opinionU;
|
|
1066
|
+
}
|
|
1067
|
+
const tupleContradicted = typeof value.tupleContradicted === "boolean" ? value.tupleContradicted : void 0;
|
|
1068
|
+
if (tupleContradicted !== void 0) {
|
|
1069
|
+
node.tupleContradicted = tupleContradicted;
|
|
1070
|
+
}
|
|
1071
|
+
const stringFields = {
|
|
1072
|
+
anonymizationClass: value.anonymizationClass,
|
|
1073
|
+
audienceLabel: value.audienceLabel,
|
|
1074
|
+
canonicalText: value.canonicalText,
|
|
1075
|
+
createdBy: value.createdBy,
|
|
1076
|
+
epistemicLayer: value.epistemicLayer,
|
|
1077
|
+
exportClass: value.exportClass,
|
|
1078
|
+
globalId: value.globalId,
|
|
1079
|
+
projectId: value.projectId,
|
|
1080
|
+
publicationStatus: value.publicationStatus,
|
|
1081
|
+
sensitivityTier: value.sensitivityTier,
|
|
1082
|
+
status: value.status,
|
|
1083
|
+
tenantId: value.tenantId,
|
|
1084
|
+
topicId: value.topicId,
|
|
1085
|
+
userId: value.userId,
|
|
1086
|
+
workspaceId: value.workspaceId
|
|
1087
|
+
};
|
|
1088
|
+
for (const [field, fieldValue] of Object.entries(stringFields)) {
|
|
1089
|
+
const normalized = readOptionalString(fieldValue);
|
|
1090
|
+
if (normalized !== void 0) {
|
|
1091
|
+
node[field] = normalized;
|
|
1092
|
+
}
|
|
1093
|
+
}
|
|
1094
|
+
const createdAt = readFiniteNumber(value.createdAt);
|
|
1095
|
+
if (createdAt !== void 0) {
|
|
1096
|
+
node.createdAt = createdAt;
|
|
1097
|
+
}
|
|
1098
|
+
const updatedAt = readFiniteNumber(value.updatedAt);
|
|
1099
|
+
if (updatedAt !== void 0) {
|
|
1100
|
+
node.updatedAt = updatedAt;
|
|
1101
|
+
}
|
|
1102
|
+
if (value.beliefStatus !== void 0) {
|
|
1103
|
+
node.beliefStatus = value.beliefStatus;
|
|
1104
|
+
}
|
|
1105
|
+
if (value.confidence !== void 0) {
|
|
1106
|
+
node.confidence = value.confidence;
|
|
1107
|
+
}
|
|
1108
|
+
if (value.predictionMeta !== void 0) {
|
|
1109
|
+
node.predictionMeta = value.predictionMeta;
|
|
1110
|
+
}
|
|
1111
|
+
const policyTags = readStringArray(value.policyTags);
|
|
1112
|
+
if (policyTags !== void 0) {
|
|
1113
|
+
node.policyTags = policyTags;
|
|
1114
|
+
}
|
|
1115
|
+
return node;
|
|
983
1116
|
}
|
|
984
1117
|
function buildBeliefConfidenceRow(args) {
|
|
985
1118
|
return {
|
|
@@ -1055,7 +1188,10 @@ function resolveBeliefStatus(node, metadata) {
|
|
|
1055
1188
|
});
|
|
1056
1189
|
}
|
|
1057
1190
|
async function hasCompletedWorktreeForBelief(ctx, beliefNodeId) {
|
|
1058
|
-
const clusterMembership = await ctx.db.query("worktreeBeliefCluster").withIndex(
|
|
1191
|
+
const clusterMembership = await ctx.db.query("worktreeBeliefCluster").withIndex(
|
|
1192
|
+
"by_belief",
|
|
1193
|
+
(q) => q.eq("beliefId", beliefNodeId)
|
|
1194
|
+
).collect();
|
|
1059
1195
|
for (const membership of clusterMembership) {
|
|
1060
1196
|
const worktree = await ctx.db.get(membership.worktreeId);
|
|
1061
1197
|
if (worktree?.status === "completed" || worktree?.status === "merged") {
|
|
@@ -1066,7 +1202,10 @@ async function hasCompletedWorktreeForBelief(ctx, beliefNodeId) {
|
|
|
1066
1202
|
}
|
|
1067
1203
|
async function getActiveConfidencePolicy(ctx) {
|
|
1068
1204
|
try {
|
|
1069
|
-
const activeConfig = await ctx.db.query("logicSprintScoring").withIndex(
|
|
1205
|
+
const activeConfig = await ctx.db.query("logicSprintScoring").withIndex(
|
|
1206
|
+
"by_active",
|
|
1207
|
+
(q) => q.eq("isActive", true)
|
|
1208
|
+
).first();
|
|
1070
1209
|
return {
|
|
1071
1210
|
scoringMode: activeConfig?.confidencePolicy === "always" ? "always" : DEFAULT_CONFIDENCE_POLICY.scoringMode,
|
|
1072
1211
|
tupleContradiction: normalizeTupleContradictionPolicy(
|
|
@@ -1098,28 +1237,191 @@ async function requireAuthenticatedUserId(ctx) {
|
|
|
1098
1237
|
}
|
|
1099
1238
|
return userId;
|
|
1100
1239
|
}
|
|
1101
|
-
async function requireProjectWriteAccess(ctx, projectId, userId) {
|
|
1102
|
-
const hasAccess = await checkProjectAccess(
|
|
1103
|
-
ctx,
|
|
1104
|
-
projectId,
|
|
1105
|
-
userId
|
|
1106
|
-
);
|
|
1107
|
-
if (!hasAccess) {
|
|
1108
|
-
throwStructuredMutationError({
|
|
1109
|
-
message: `Project write access denied for topic ${projectId}.`,
|
|
1110
|
-
status: 403,
|
|
1111
|
-
code: "PROJECT_ACCESS_DENIED",
|
|
1112
|
-
invariantCode: "policy.scope_required",
|
|
1113
|
-
suggestion: "The acting principal lacks project-write access to this topic. Request a topic grant (or, if the principal created this topic, run the creator-grant backfill) and retry.",
|
|
1114
|
-
details: { topicId: projectId, principalId: userId }
|
|
1115
|
-
});
|
|
1116
|
-
}
|
|
1117
|
-
}
|
|
1118
1240
|
|
|
1119
1241
|
// src/epistemicBeliefs.confidence.ts
|
|
1242
|
+
function isRecord2(value) {
|
|
1243
|
+
return Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
1244
|
+
}
|
|
1245
|
+
function readConvexId(value) {
|
|
1246
|
+
return typeof value === "string" && value.trim().length > 0 ? value : void 0;
|
|
1247
|
+
}
|
|
1248
|
+
function readOptionalBoolean(value) {
|
|
1249
|
+
return typeof value === "boolean" ? value : void 0;
|
|
1250
|
+
}
|
|
1251
|
+
function readOptionalNumber(value) {
|
|
1252
|
+
return typeof value === "number" && Number.isFinite(value) ? value : void 0;
|
|
1253
|
+
}
|
|
1254
|
+
function readOptionalString2(value) {
|
|
1255
|
+
return typeof value === "string" && value.trim().length > 0 ? value : void 0;
|
|
1256
|
+
}
|
|
1257
|
+
function readRecord2(value) {
|
|
1258
|
+
return isRecord2(value) ? value : void 0;
|
|
1259
|
+
}
|
|
1260
|
+
function readConfidenceBeliefNode(value) {
|
|
1261
|
+
if (!isRecord2(value)) {
|
|
1262
|
+
return null;
|
|
1263
|
+
}
|
|
1264
|
+
const id = readConvexId(value._id);
|
|
1265
|
+
const nodeType = readOptionalString2(value.nodeType);
|
|
1266
|
+
const projectId = readOptionalString2(value.projectId);
|
|
1267
|
+
if (!(id && nodeType === "belief" && projectId)) {
|
|
1268
|
+
return null;
|
|
1269
|
+
}
|
|
1270
|
+
const node = {
|
|
1271
|
+
_id: id,
|
|
1272
|
+
nodeType,
|
|
1273
|
+
projectId
|
|
1274
|
+
};
|
|
1275
|
+
const confidence = readOptionalNumber(value.confidence);
|
|
1276
|
+
const epistemicLayer = readOptionalString2(value.epistemicLayer);
|
|
1277
|
+
const globalId = readOptionalString2(value.globalId);
|
|
1278
|
+
const metadata = readRecord2(value.metadata);
|
|
1279
|
+
const opinionA = readOptionalNumber(value.opinion_a);
|
|
1280
|
+
const opinionB = readOptionalNumber(value.opinion_b);
|
|
1281
|
+
const opinionD = readOptionalNumber(value.opinion_d);
|
|
1282
|
+
const opinionU = readOptionalNumber(value.opinion_u);
|
|
1283
|
+
const predictionMeta = readRecord2(value.predictionMeta);
|
|
1284
|
+
const publicationStatus = readOptionalString2(value.publicationStatus);
|
|
1285
|
+
const status = readOptionalString2(value.status);
|
|
1286
|
+
const tenantId = readOptionalString2(value.tenantId);
|
|
1287
|
+
const topicId = readOptionalString2(value.topicId);
|
|
1288
|
+
const tupleContradicted = readOptionalBoolean(value.tupleContradicted);
|
|
1289
|
+
const workspaceId = readOptionalString2(value.workspaceId);
|
|
1290
|
+
if (confidence !== void 0) {
|
|
1291
|
+
node.confidence = confidence;
|
|
1292
|
+
}
|
|
1293
|
+
if (epistemicLayer !== void 0) {
|
|
1294
|
+
node.epistemicLayer = epistemicLayer;
|
|
1295
|
+
}
|
|
1296
|
+
if (globalId !== void 0) {
|
|
1297
|
+
node.globalId = globalId;
|
|
1298
|
+
}
|
|
1299
|
+
if (metadata !== void 0) {
|
|
1300
|
+
node.metadata = metadata;
|
|
1301
|
+
}
|
|
1302
|
+
if (opinionA !== void 0) {
|
|
1303
|
+
node.opinion_a = opinionA;
|
|
1304
|
+
}
|
|
1305
|
+
if (opinionB !== void 0) {
|
|
1306
|
+
node.opinion_b = opinionB;
|
|
1307
|
+
}
|
|
1308
|
+
if (opinionD !== void 0) {
|
|
1309
|
+
node.opinion_d = opinionD;
|
|
1310
|
+
}
|
|
1311
|
+
if (opinionU !== void 0) {
|
|
1312
|
+
node.opinion_u = opinionU;
|
|
1313
|
+
}
|
|
1314
|
+
if (predictionMeta !== void 0) {
|
|
1315
|
+
node.predictionMeta = predictionMeta;
|
|
1316
|
+
}
|
|
1317
|
+
if (publicationStatus !== void 0) {
|
|
1318
|
+
node.publicationStatus = publicationStatus;
|
|
1319
|
+
}
|
|
1320
|
+
if (status !== void 0) {
|
|
1321
|
+
node.status = status;
|
|
1322
|
+
}
|
|
1323
|
+
if (tenantId !== void 0) {
|
|
1324
|
+
node.tenantId = tenantId;
|
|
1325
|
+
}
|
|
1326
|
+
if (topicId !== void 0) {
|
|
1327
|
+
node.topicId = topicId;
|
|
1328
|
+
}
|
|
1329
|
+
if (tupleContradicted !== void 0) {
|
|
1330
|
+
node.tupleContradicted = tupleContradicted;
|
|
1331
|
+
}
|
|
1332
|
+
if (workspaceId !== void 0) {
|
|
1333
|
+
node.workspaceId = workspaceId;
|
|
1334
|
+
}
|
|
1335
|
+
return node;
|
|
1336
|
+
}
|
|
1337
|
+
function readPropagationEdge(value) {
|
|
1338
|
+
if (!isRecord2(value)) {
|
|
1339
|
+
return null;
|
|
1340
|
+
}
|
|
1341
|
+
const edgeType = readOptionalString2(value.edgeType);
|
|
1342
|
+
if (!edgeType) {
|
|
1343
|
+
return null;
|
|
1344
|
+
}
|
|
1345
|
+
const edge = { edgeType };
|
|
1346
|
+
const fromNodeId = readConvexId(value.fromNodeId);
|
|
1347
|
+
const tenantId = readOptionalString2(value.tenantId);
|
|
1348
|
+
const toNodeId = readConvexId(value.toNodeId);
|
|
1349
|
+
const weight = readOptionalNumber(value.weight);
|
|
1350
|
+
const workspaceId = readOptionalString2(value.workspaceId);
|
|
1351
|
+
if (fromNodeId !== void 0) {
|
|
1352
|
+
edge.fromNodeId = fromNodeId;
|
|
1353
|
+
}
|
|
1354
|
+
if (tenantId !== void 0) {
|
|
1355
|
+
edge.tenantId = tenantId;
|
|
1356
|
+
}
|
|
1357
|
+
if (toNodeId !== void 0) {
|
|
1358
|
+
edge.toNodeId = toNodeId;
|
|
1359
|
+
}
|
|
1360
|
+
if (weight !== void 0) {
|
|
1361
|
+
edge.weight = weight;
|
|
1362
|
+
}
|
|
1363
|
+
if (workspaceId !== void 0) {
|
|
1364
|
+
edge.workspaceId = workspaceId;
|
|
1365
|
+
}
|
|
1366
|
+
return edge;
|
|
1367
|
+
}
|
|
1368
|
+
function readRowList(values, reader) {
|
|
1369
|
+
return values.flatMap((value) => {
|
|
1370
|
+
const row = reader(value);
|
|
1371
|
+
return row ? [row] : [];
|
|
1372
|
+
});
|
|
1373
|
+
}
|
|
1120
1374
|
async function applyBeliefConfidenceChange(ctx, args) {
|
|
1121
1375
|
const now = Date.now();
|
|
1122
|
-
const node = await ctx
|
|
1376
|
+
const node = await requireConfidenceBeliefNode(ctx, args);
|
|
1377
|
+
const state = await buildConfidenceChangeState(ctx, node, args);
|
|
1378
|
+
await assertConfidenceScoringPolicySatisfied(ctx, args, state);
|
|
1379
|
+
const tupleContradictionId = await createTupleContradictionIfNeeded(
|
|
1380
|
+
ctx,
|
|
1381
|
+
args,
|
|
1382
|
+
node,
|
|
1383
|
+
state
|
|
1384
|
+
);
|
|
1385
|
+
await patchBeliefConfidenceState(ctx, args, state);
|
|
1386
|
+
await scheduleFirstScoringThemeEdges(ctx, args, node, state);
|
|
1387
|
+
const beliefConfidenceId = await insertBeliefConfidenceRecord(
|
|
1388
|
+
ctx,
|
|
1389
|
+
args,
|
|
1390
|
+
state,
|
|
1391
|
+
tupleContradictionId,
|
|
1392
|
+
now
|
|
1393
|
+
);
|
|
1394
|
+
await ctx.scheduler.runAfter(0, internal.neo4jSync.syncNodeToNeo4j, {
|
|
1395
|
+
nodeId: args.nodeId,
|
|
1396
|
+
operation: "upsert"
|
|
1397
|
+
});
|
|
1398
|
+
await insertConfidenceAudit(
|
|
1399
|
+
ctx,
|
|
1400
|
+
args,
|
|
1401
|
+
node,
|
|
1402
|
+
state,
|
|
1403
|
+
tupleContradictionId,
|
|
1404
|
+
now
|
|
1405
|
+
);
|
|
1406
|
+
await insertTupleTransitionAuditIfNeeded(
|
|
1407
|
+
ctx,
|
|
1408
|
+
args,
|
|
1409
|
+
node,
|
|
1410
|
+
state,
|
|
1411
|
+
tupleContradictionId,
|
|
1412
|
+
now
|
|
1413
|
+
);
|
|
1414
|
+
await scheduleConfidenceFollowups(ctx, args, node, state);
|
|
1415
|
+
return {
|
|
1416
|
+
nodeId: args.nodeId,
|
|
1417
|
+
previousConfidence: state.previousConfidence,
|
|
1418
|
+
newConfidence: state.derivedConfidence,
|
|
1419
|
+
opinion: state.nextOpinion,
|
|
1420
|
+
beliefConfidenceId
|
|
1421
|
+
};
|
|
1422
|
+
}
|
|
1423
|
+
async function requireConfidenceBeliefNode(ctx, args) {
|
|
1424
|
+
const node = readConfidenceBeliefNode(await ctx.db.get(args.nodeId));
|
|
1123
1425
|
if (!node) {
|
|
1124
1426
|
throwStructuredMutationError({
|
|
1125
1427
|
message: "Node not found.",
|
|
@@ -1130,59 +1432,28 @@ async function applyBeliefConfidenceChange(ctx, args) {
|
|
|
1130
1432
|
details: { nodeId: args.nodeId }
|
|
1131
1433
|
});
|
|
1132
1434
|
}
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
invariantCode: "entity.no_confidence",
|
|
1139
|
-
suggestion: "Use entityLifecycle.updateEntityAttributes for entity mutations. appendSlScoring is for belief nodes only.",
|
|
1140
|
-
details: { nodeId: args.nodeId, nodeType: node.nodeType }
|
|
1141
|
-
});
|
|
1142
|
-
}
|
|
1143
|
-
if (!node.projectId) {
|
|
1144
|
-
throwStructuredMutationError({
|
|
1145
|
-
message: "Belief has no project scope.",
|
|
1146
|
-
status: 400,
|
|
1147
|
-
code: "MISSING_SCOPE",
|
|
1148
|
-
invariantCode: "belief.project_required",
|
|
1149
|
-
suggestion: "Belief must have a projectId before SL scoring can be appended.",
|
|
1150
|
-
details: { nodeId: args.nodeId }
|
|
1151
|
-
});
|
|
1152
|
-
}
|
|
1153
|
-
await requireProjectWriteAccess(
|
|
1154
|
-
ctx,
|
|
1155
|
-
node.projectId,
|
|
1156
|
-
args.authenticatedUserId
|
|
1157
|
-
);
|
|
1158
|
-
const existingMetadata = node.metadata || {};
|
|
1435
|
+
await requireScopeWriteAccess(ctx, node.projectId, args.authenticatedUserId);
|
|
1436
|
+
return node;
|
|
1437
|
+
}
|
|
1438
|
+
async function buildConfidenceChangeState(ctx, node, args) {
|
|
1439
|
+
const existingMetadata = readNodeMetadata(node);
|
|
1159
1440
|
const currentBeliefStatus = resolveBeliefStatus(node, existingMetadata);
|
|
1160
1441
|
const confidencePolicy = await getActiveConfidencePolicy(ctx);
|
|
1161
|
-
if (confidencePolicy.scoringMode === "after_worktree" && isPreValidationBeliefStatus(currentBeliefStatus)) {
|
|
1162
|
-
const hasCompletedWorktree = await hasCompletedWorktreeForBelief(
|
|
1163
|
-
ctx,
|
|
1164
|
-
args.nodeId
|
|
1165
|
-
);
|
|
1166
|
-
if (!hasCompletedWorktree) {
|
|
1167
|
-
throwStructuredMutationError({
|
|
1168
|
-
message: "Cannot score belief before worktree completion. Complete a worktree that tests this belief first.",
|
|
1169
|
-
status: 409,
|
|
1170
|
-
code: "CONFLICT",
|
|
1171
|
-
invariantCode: "belief.confidence_append_only",
|
|
1172
|
-
suggestion: "Complete a worktree linked to this belief before recording SL scoring.",
|
|
1173
|
-
details: { nodeId: args.nodeId }
|
|
1174
|
-
});
|
|
1175
|
-
}
|
|
1176
|
-
}
|
|
1177
1442
|
const previousConfidence = node.confidence || 0.5;
|
|
1178
1443
|
const predictionMeta = node.predictionMeta || existingMetadata.predictionMeta;
|
|
1179
1444
|
const previousOpinion = readBeliefOpinionSnapshot(node, existingMetadata);
|
|
1180
|
-
const
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1445
|
+
const nextOpinion = {
|
|
1446
|
+
b: args.belief,
|
|
1447
|
+
d: args.disbelief,
|
|
1448
|
+
u: args.uncertainty,
|
|
1449
|
+
a: args.baseRate ?? 0.5
|
|
1450
|
+
};
|
|
1451
|
+
const derivedConfidence = confidenceFromSL(
|
|
1452
|
+
nextOpinion.b,
|
|
1453
|
+
nextOpinion.d,
|
|
1454
|
+
nextOpinion.u,
|
|
1455
|
+
nextOpinion.a
|
|
1456
|
+
);
|
|
1186
1457
|
const isFirstScoring = typeof node.confidence !== "number" || !Number.isFinite(node.confidence);
|
|
1187
1458
|
const previousTupleContradicted = readTupleContradictedFlag(node.tupleContradicted) ?? readTupleContradictedFlag(existingMetadata.tupleContradicted) ?? detectTupleContradiction(
|
|
1188
1459
|
previousOpinion,
|
|
@@ -1203,79 +1474,121 @@ async function applyBeliefConfidenceChange(ctx, args) {
|
|
|
1203
1474
|
predictionMeta,
|
|
1204
1475
|
metadata: existingMetadata
|
|
1205
1476
|
});
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
|
|
1477
|
+
const storedRationale = args.rationale ?? `Confidence changed from ${previousConfidence.toFixed(2)} (nodeId: ${args.nodeId})`;
|
|
1478
|
+
return {
|
|
1479
|
+
confidencePolicy,
|
|
1480
|
+
currentBeliefStatus,
|
|
1481
|
+
derivedConfidence,
|
|
1482
|
+
existingMetadata,
|
|
1483
|
+
isFirstScoring,
|
|
1484
|
+
newBeliefStatus,
|
|
1485
|
+
nextOpinion,
|
|
1486
|
+
previousConfidence,
|
|
1487
|
+
previousTupleContradicted,
|
|
1488
|
+
storedRationale,
|
|
1489
|
+
tupleContradictionDescription,
|
|
1490
|
+
tupleTransition
|
|
1491
|
+
};
|
|
1492
|
+
}
|
|
1493
|
+
function readNodeMetadata(node) {
|
|
1494
|
+
return node.metadata ?? {};
|
|
1495
|
+
}
|
|
1496
|
+
async function assertConfidenceScoringPolicySatisfied(ctx, args, state) {
|
|
1497
|
+
if (state.confidencePolicy.scoringMode !== "after_worktree" || !isPreValidationBeliefStatus(state.currentBeliefStatus)) {
|
|
1498
|
+
return;
|
|
1499
|
+
}
|
|
1500
|
+
const hasCompletedWorktree = await hasCompletedWorktreeForBelief(
|
|
1501
|
+
ctx,
|
|
1502
|
+
args.nodeId
|
|
1503
|
+
);
|
|
1504
|
+
if (hasCompletedWorktree) {
|
|
1505
|
+
return;
|
|
1506
|
+
}
|
|
1507
|
+
throwStructuredMutationError({
|
|
1508
|
+
message: "Cannot score belief before worktree completion. Complete a worktree that tests this belief first.",
|
|
1509
|
+
status: 409,
|
|
1510
|
+
code: "CONFLICT",
|
|
1511
|
+
invariantCode: "belief.confidence_append_only",
|
|
1512
|
+
suggestion: "Complete a worktree linked to this belief before recording SL scoring.",
|
|
1513
|
+
details: { nodeId: args.nodeId }
|
|
1514
|
+
});
|
|
1515
|
+
}
|
|
1516
|
+
async function createTupleContradictionIfNeeded(ctx, args, node, state) {
|
|
1517
|
+
if (!state.tupleTransition.crossedIntoTupleContradiction) {
|
|
1518
|
+
return;
|
|
1224
1519
|
}
|
|
1520
|
+
return await ctx.runMutation("contradictions:create", {
|
|
1521
|
+
projectId: node.projectId,
|
|
1522
|
+
topicId: node.topicId,
|
|
1523
|
+
beliefId: args.nodeId,
|
|
1524
|
+
beliefBId: args.nodeId,
|
|
1525
|
+
supportingInsightIds: [],
|
|
1526
|
+
contradictingInsightIds: [],
|
|
1527
|
+
severity: deriveTupleContradictionSeverity(node),
|
|
1528
|
+
source: "tuple_space",
|
|
1529
|
+
detectionMethod: "agent",
|
|
1530
|
+
description: state.tupleContradictionDescription,
|
|
1531
|
+
createdBy: args.authenticatedUserId
|
|
1532
|
+
});
|
|
1533
|
+
}
|
|
1534
|
+
async function patchBeliefConfidenceState(ctx, args, state) {
|
|
1225
1535
|
await ctx.db.patch(args.nodeId, {
|
|
1226
|
-
confidence: derivedConfidence,
|
|
1227
|
-
beliefStatus: newBeliefStatus,
|
|
1228
|
-
tupleContradicted: tupleTransition.tupleContradicted,
|
|
1229
|
-
updatedAt: now,
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
opinion_a: slA,
|
|
1536
|
+
confidence: state.derivedConfidence,
|
|
1537
|
+
beliefStatus: state.newBeliefStatus,
|
|
1538
|
+
tupleContradicted: state.tupleTransition.tupleContradicted,
|
|
1539
|
+
updatedAt: Date.now(),
|
|
1540
|
+
opinion_b: state.nextOpinion.b,
|
|
1541
|
+
opinion_d: state.nextOpinion.d,
|
|
1542
|
+
opinion_u: state.nextOpinion.u,
|
|
1543
|
+
opinion_a: state.nextOpinion.a,
|
|
1235
1544
|
metadata: {
|
|
1236
|
-
...existingMetadata,
|
|
1237
|
-
beliefStatus: newBeliefStatus,
|
|
1238
|
-
slBelief:
|
|
1239
|
-
slDisbelief:
|
|
1240
|
-
slUncertainty:
|
|
1241
|
-
slBaseRate:
|
|
1242
|
-
tupleContradicted: tupleTransition.tupleContradicted
|
|
1545
|
+
...state.existingMetadata,
|
|
1546
|
+
beliefStatus: state.newBeliefStatus,
|
|
1547
|
+
slBelief: state.nextOpinion.b,
|
|
1548
|
+
slDisbelief: state.nextOpinion.d,
|
|
1549
|
+
slUncertainty: state.nextOpinion.u,
|
|
1550
|
+
slBaseRate: state.nextOpinion.a,
|
|
1551
|
+
tupleContradicted: state.tupleTransition.tupleContradicted
|
|
1243
1552
|
}
|
|
1244
1553
|
});
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
edgeType: "relates_to_thesis",
|
|
1258
|
-
weight: derivedConfidence,
|
|
1259
|
-
createdBy: args.authenticatedUserId,
|
|
1260
|
-
topicId: String(node.projectId),
|
|
1261
|
-
fromNodeType: "belief",
|
|
1262
|
-
toNodeType: "theme",
|
|
1263
|
-
fromLayer: "L3",
|
|
1264
|
-
toLayer: "L3"
|
|
1265
|
-
});
|
|
1266
|
-
}
|
|
1554
|
+
}
|
|
1555
|
+
async function scheduleFirstScoringThemeEdges(ctx, args, node, state) {
|
|
1556
|
+
if (!state.isFirstScoring) {
|
|
1557
|
+
return;
|
|
1558
|
+
}
|
|
1559
|
+
const themeNodes = await ctx.db.query("epistemicNodes").withIndex(
|
|
1560
|
+
"by_topic",
|
|
1561
|
+
(q) => q.eq("topicId", node.topicId || node.projectId)
|
|
1562
|
+
).filter((q) => q.eq(q.field("nodeType"), "theme")).collect();
|
|
1563
|
+
for (const theme of themeNodes) {
|
|
1564
|
+
if (!(theme.globalId && node.globalId)) {
|
|
1565
|
+
continue;
|
|
1267
1566
|
}
|
|
1567
|
+
await ctx.scheduler.runAfter(0, internal.neo4jEdgeAPI.createEdge, {
|
|
1568
|
+
globalId: `edge-${node.globalId}-relates_to_thesis-${theme.globalId}`,
|
|
1569
|
+
fromGlobalId: node.globalId,
|
|
1570
|
+
toGlobalId: theme.globalId,
|
|
1571
|
+
edgeType: "relates_to_thesis",
|
|
1572
|
+
weight: state.derivedConfidence,
|
|
1573
|
+
createdBy: args.authenticatedUserId,
|
|
1574
|
+
topicId: String(node.projectId),
|
|
1575
|
+
fromNodeType: "belief",
|
|
1576
|
+
toNodeType: "theme",
|
|
1577
|
+
fromLayer: "L3",
|
|
1578
|
+
toLayer: "L3"
|
|
1579
|
+
});
|
|
1268
1580
|
}
|
|
1269
|
-
|
|
1270
|
-
|
|
1581
|
+
}
|
|
1582
|
+
async function insertBeliefConfidenceRecord(ctx, args, state, tupleContradictionId, now) {
|
|
1583
|
+
return await ctx.db.insert("beliefConfidence", {
|
|
1271
1584
|
...buildBeliefConfidenceRow({
|
|
1272
1585
|
beliefId: args.nodeId,
|
|
1273
|
-
belief:
|
|
1274
|
-
disbelief:
|
|
1275
|
-
uncertainty:
|
|
1276
|
-
baseRate:
|
|
1586
|
+
belief: state.nextOpinion.b,
|
|
1587
|
+
disbelief: state.nextOpinion.d,
|
|
1588
|
+
uncertainty: state.nextOpinion.u,
|
|
1589
|
+
baseRate: state.nextOpinion.a,
|
|
1277
1590
|
trigger: args.trigger,
|
|
1278
|
-
rationale: storedRationale,
|
|
1591
|
+
rationale: state.storedRationale,
|
|
1279
1592
|
assessedBy: args.authenticatedUserId,
|
|
1280
1593
|
assessedAt: now,
|
|
1281
1594
|
slOperator: args.slOperator,
|
|
@@ -1284,25 +1597,23 @@ async function applyBeliefConfidenceChange(ctx, args) {
|
|
|
1284
1597
|
triggeringWorktreeId: args.triggeringWorktreeId
|
|
1285
1598
|
})
|
|
1286
1599
|
});
|
|
1287
|
-
|
|
1288
|
-
|
|
1289
|
-
operation: "upsert"
|
|
1290
|
-
});
|
|
1600
|
+
}
|
|
1601
|
+
async function insertConfidenceAudit(ctx, args, node, state, tupleContradictionId, now) {
|
|
1291
1602
|
await ctx.db.insert("epistemicAudit", {
|
|
1292
1603
|
entityType: "belief",
|
|
1293
1604
|
entityId: args.nodeId,
|
|
1294
1605
|
changeType: "confidence_changed",
|
|
1295
1606
|
previousState: {
|
|
1296
|
-
confidence: previousConfidence,
|
|
1297
|
-
tupleContradicted: previousTupleContradicted
|
|
1607
|
+
confidence: state.previousConfidence,
|
|
1608
|
+
tupleContradicted: state.previousTupleContradicted
|
|
1298
1609
|
},
|
|
1299
1610
|
newState: {
|
|
1300
|
-
opinion: nextOpinion,
|
|
1301
|
-
confidence: derivedConfidence,
|
|
1611
|
+
opinion: state.nextOpinion,
|
|
1612
|
+
confidence: state.derivedConfidence,
|
|
1302
1613
|
trigger: args.trigger,
|
|
1303
|
-
rationale: storedRationale,
|
|
1304
|
-
tupleContradicted: tupleTransition.tupleContradicted,
|
|
1305
|
-
tupleContradictionPolicy: tupleTransition.policy,
|
|
1614
|
+
rationale: state.storedRationale,
|
|
1615
|
+
tupleContradicted: state.tupleTransition.tupleContradicted,
|
|
1616
|
+
tupleContradictionPolicy: state.tupleTransition.policy,
|
|
1306
1617
|
...tupleContradictionId ? { tupleContradictionId: String(tupleContradictionId) } : {}
|
|
1307
1618
|
},
|
|
1308
1619
|
changedBy: args.authenticatedUserId,
|
|
@@ -1311,28 +1622,39 @@ async function applyBeliefConfidenceChange(ctx, args) {
|
|
|
1311
1622
|
projectId: node.projectId,
|
|
1312
1623
|
topicId: node.topicId
|
|
1313
1624
|
});
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
|
-
|
|
1322
|
-
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
|
|
1328
|
-
|
|
1329
|
-
|
|
1330
|
-
|
|
1331
|
-
|
|
1332
|
-
|
|
1333
|
-
|
|
1625
|
+
}
|
|
1626
|
+
async function insertTupleTransitionAuditIfNeeded(ctx, args, node, state, tupleContradictionId, now) {
|
|
1627
|
+
if (!(state.tupleTransition.crossedIntoTupleContradiction || state.tupleTransition.crossedOutOfTupleContradiction)) {
|
|
1628
|
+
return;
|
|
1629
|
+
}
|
|
1630
|
+
await ctx.db.insert("epistemicAudit", {
|
|
1631
|
+
entityType: "belief",
|
|
1632
|
+
entityId: args.nodeId,
|
|
1633
|
+
changeType: "updated",
|
|
1634
|
+
previousState: { tupleContradicted: state.previousTupleContradicted },
|
|
1635
|
+
newState: {
|
|
1636
|
+
tupleContradicted: state.tupleTransition.tupleContradicted,
|
|
1637
|
+
action: state.tupleTransition.crossedIntoTupleContradiction ? "tuple_contradiction_detected" : "tuple_contradiction_cleared",
|
|
1638
|
+
opinion: state.nextOpinion,
|
|
1639
|
+
tupleContradictionPolicy: state.tupleTransition.policy,
|
|
1640
|
+
...tupleContradictionId ? { tupleContradictionId: String(tupleContradictionId) } : {}
|
|
1641
|
+
},
|
|
1642
|
+
rationale: tupleAuditRationale(state),
|
|
1643
|
+
changedBy: args.authenticatedUserId,
|
|
1644
|
+
isAgent: false,
|
|
1645
|
+
changedAt: now,
|
|
1646
|
+
projectId: node.projectId,
|
|
1647
|
+
topicId: node.topicId
|
|
1648
|
+
});
|
|
1649
|
+
}
|
|
1650
|
+
function tupleAuditRationale(state) {
|
|
1651
|
+
if (state.tupleTransition.crossedIntoTupleContradiction) {
|
|
1652
|
+
return state.tupleContradictionDescription;
|
|
1334
1653
|
}
|
|
1335
|
-
|
|
1654
|
+
return `Tuple-space contradiction cleared: b=${state.nextOpinion.b.toFixed(2)}, d=${state.nextOpinion.d.toFixed(2)} no longer exceed the configured policy thresholds.`;
|
|
1655
|
+
}
|
|
1656
|
+
async function scheduleConfidenceFollowups(ctx, args, node, state) {
|
|
1657
|
+
if (Math.abs(state.derivedConfidence - state.previousConfidence) >= 0.15) {
|
|
1336
1658
|
await ctx.scheduler.runAfter(
|
|
1337
1659
|
5e3,
|
|
1338
1660
|
internal.bi.contradictionSemanticDetector.scanAffectedBeliefs,
|
|
@@ -1349,13 +1671,6 @@ async function applyBeliefConfidenceChange(ctx, args) {
|
|
|
1349
1671
|
{ nodeId: args.nodeId }
|
|
1350
1672
|
);
|
|
1351
1673
|
}
|
|
1352
|
-
return {
|
|
1353
|
-
nodeId: args.nodeId,
|
|
1354
|
-
previousConfidence,
|
|
1355
|
-
newConfidence: derivedConfidence,
|
|
1356
|
-
opinion: { b: slB, d: slD, u: slU, a: slA },
|
|
1357
|
-
beliefConfidenceId
|
|
1358
|
-
};
|
|
1359
1674
|
}
|
|
1360
1675
|
function propagationPressureLabel(edgeType, weight) {
|
|
1361
1676
|
if (edgeType === "contradicts" || edgeType === "refutes") {
|
|
@@ -1383,7 +1698,7 @@ internalMutation({
|
|
|
1383
1698
|
args.opinion_u,
|
|
1384
1699
|
args.opinion_a
|
|
1385
1700
|
);
|
|
1386
|
-
const sourceNode = await ctx.db.get(args.nodeId);
|
|
1701
|
+
const sourceNode = readConfidenceBeliefNode(await ctx.db.get(args.nodeId));
|
|
1387
1702
|
const sourceScope = await resolveNodeScopeForWorkspaceIsolation(
|
|
1388
1703
|
ctx,
|
|
1389
1704
|
sourceNode
|
|
@@ -1392,16 +1707,20 @@ internalMutation({
|
|
|
1392
1707
|
sourceNodeId: args.nodeId,
|
|
1393
1708
|
sourceOpinion,
|
|
1394
1709
|
sourceScope,
|
|
1395
|
-
queryEdges: async ({ nodeId, spec, direction }) =>
|
|
1396
|
-
|
|
1710
|
+
queryEdges: async ({ nodeId, spec, direction }) => readRowList(
|
|
1711
|
+
await ctx.db.query("epistemicEdges").withIndex(
|
|
1397
1712
|
direction === "outgoing" ? "by_from_type" : "by_to_type",
|
|
1398
1713
|
(q) => direction === "outgoing" ? q.eq("fromNodeId", nodeId).eq("edgeType", spec.edgeType) : q.eq("toNodeId", nodeId).eq("edgeType", spec.edgeType)
|
|
1399
|
-
).collect()
|
|
1400
|
-
|
|
1401
|
-
|
|
1714
|
+
).collect(),
|
|
1715
|
+
readPropagationEdge
|
|
1716
|
+
),
|
|
1717
|
+
getNode: async (nodeId) => readConfidenceBeliefNode(await ctx.db.get(nodeId))
|
|
1402
1718
|
});
|
|
1403
1719
|
for (const dispatch of dispatches) {
|
|
1404
|
-
const pressureLabel = propagationPressureLabel(
|
|
1720
|
+
const pressureLabel = propagationPressureLabel(
|
|
1721
|
+
dispatch.edgeType,
|
|
1722
|
+
dispatch.weight
|
|
1723
|
+
);
|
|
1405
1724
|
await applyBeliefConfidenceChange(ctx, {
|
|
1406
1725
|
nodeId: dispatch.targetNodeId,
|
|
1407
1726
|
belief: dispatch.opinion.b,
|
|
@@ -1524,16 +1843,16 @@ var updateStatus = mutation({
|
|
|
1524
1843
|
handler: async (ctx, args) => {
|
|
1525
1844
|
const authenticatedUserId = await requireAuthenticatedUserId(ctx);
|
|
1526
1845
|
const now = Date.now();
|
|
1527
|
-
const node = await ctx.db.get(args.nodeId);
|
|
1528
|
-
if (!node
|
|
1846
|
+
const node = readBeliefNodeView(await ctx.db.get(args.nodeId));
|
|
1847
|
+
if (!node) {
|
|
1529
1848
|
throw new Error("Belief not found");
|
|
1530
1849
|
}
|
|
1531
1850
|
if (!node.projectId) {
|
|
1532
1851
|
throw new Error("Belief has no project scope");
|
|
1533
1852
|
}
|
|
1534
|
-
await
|
|
1853
|
+
await requireScopeWriteAccess(ctx, node.projectId, authenticatedUserId);
|
|
1535
1854
|
const previousStatus = node.status;
|
|
1536
|
-
const metadata = node.metadata
|
|
1855
|
+
const metadata = node.metadata ?? {};
|
|
1537
1856
|
await ctx.db.patch(args.nodeId, {
|
|
1538
1857
|
status: args.status,
|
|
1539
1858
|
updatedAt: now,
|
|
@@ -1569,14 +1888,14 @@ var archive = mutation({
|
|
|
1569
1888
|
returns: permissiveReturn,
|
|
1570
1889
|
handler: async (ctx, args) => {
|
|
1571
1890
|
const authenticatedUserId = await requireAuthenticatedUserId(ctx);
|
|
1572
|
-
const node = await ctx.db.get(args.nodeId);
|
|
1573
|
-
if (!node
|
|
1891
|
+
const node = readBeliefNodeView(await ctx.db.get(args.nodeId));
|
|
1892
|
+
if (!node) {
|
|
1574
1893
|
throw new Error("Belief not found");
|
|
1575
1894
|
}
|
|
1576
1895
|
if (!node.projectId) {
|
|
1577
1896
|
throw new Error("Belief has no project scope");
|
|
1578
1897
|
}
|
|
1579
|
-
await
|
|
1898
|
+
await requireScopeWriteAccess(ctx, node.projectId, authenticatedUserId);
|
|
1580
1899
|
return await ctx.runMutation(
|
|
1581
1900
|
// Use updateStatus internally
|
|
1582
1901
|
internal.epistemicBeliefs.updateStatusInternal,
|
|
@@ -1599,15 +1918,15 @@ var updateRationale = mutation({
|
|
|
1599
1918
|
handler: async (ctx, args) => {
|
|
1600
1919
|
const authenticatedUserId = await requireAuthenticatedUserId(ctx);
|
|
1601
1920
|
const now = Date.now();
|
|
1602
|
-
const node = await ctx.db.get(args.nodeId);
|
|
1603
|
-
if (!node
|
|
1921
|
+
const node = readBeliefNodeView(await ctx.db.get(args.nodeId));
|
|
1922
|
+
if (!node) {
|
|
1604
1923
|
throw new Error("Belief not found");
|
|
1605
1924
|
}
|
|
1606
1925
|
if (!node.projectId) {
|
|
1607
1926
|
throw new Error("Belief has no project scope");
|
|
1608
1927
|
}
|
|
1609
|
-
await
|
|
1610
|
-
const metadata = node.metadata
|
|
1928
|
+
await requireScopeWriteAccess(ctx, node.projectId, authenticatedUserId);
|
|
1929
|
+
const metadata = node.metadata ?? {};
|
|
1611
1930
|
const previousRationale = typeof metadata.rationale === "string" ? metadata.rationale : void 0;
|
|
1612
1931
|
const nextRationale = args.rationale?.trim();
|
|
1613
1932
|
await ctx.db.patch(args.nodeId, {
|
|
@@ -1660,8 +1979,8 @@ var updateStatusInternal = internalMutation({
|
|
|
1660
1979
|
returns: permissiveReturn,
|
|
1661
1980
|
handler: async (ctx, args) => {
|
|
1662
1981
|
const now = Date.now();
|
|
1663
|
-
const node = await ctx.db.get(args.nodeId);
|
|
1664
|
-
if (!node
|
|
1982
|
+
const node = readBeliefNodeView(await ctx.db.get(args.nodeId));
|
|
1983
|
+
if (!node) {
|
|
1665
1984
|
throw new Error("Belief not found");
|
|
1666
1985
|
}
|
|
1667
1986
|
assertTenantPackWorkspaceMutationAllowed({
|