@lucern/graph-primitives 1.0.29 → 1.0.31
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{beliefDecay-DZ6tkLYq.d.ts → beliefDecay-BmkEk5OJ.d.ts} +3 -3
- package/dist/beliefDecay.d.ts +1 -1
- package/dist/beliefDecay.js +448 -314
- package/dist/beliefDecay.js.map +1 -1
- package/dist/{beliefEvidenceLinks-CWOXxxJg.d.ts → beliefEvidenceLinks-BzfjON_6.d.ts} +13 -13
- package/dist/beliefEvidenceLinks.d.ts +1 -1
- package/dist/beliefEvidenceLinks.js +843 -624
- package/dist/beliefEvidenceLinks.js.map +1 -1
- package/dist/beliefEvidenceLinks.operational.d.ts +7 -5
- package/dist/beliefEvidenceLinks.operational.js +91 -18
- package/dist/beliefEvidenceLinks.operational.js.map +1 -1
- package/dist/beliefLifecycle.js.map +1 -1
- package/dist/confidencePropagationDispatch.d.ts +28 -27
- package/dist/confidencePropagationDispatch.js +157 -99
- package/dist/confidencePropagationDispatch.js.map +1 -1
- package/dist/{contradictions-51VLsESq.d.ts → contradictions-BATPuZTL.d.ts} +10 -10
- package/dist/contradictions.d.ts +1 -1
- package/dist/contradictions.js +395 -225
- package/dist/contradictions.js.map +1 -1
- package/dist/convex.d.ts +65 -30
- package/dist/convex.js +7 -3
- package/dist/convex.js.map +1 -1
- package/dist/debug.js.map +1 -1
- package/dist/edgeValidation.js +293 -85
- package/dist/edgeValidation.js.map +1 -1
- package/dist/edges/contains.d.ts +1 -1
- package/dist/edges/contains.js.map +1 -1
- package/dist/edges/contradicts.d.ts +1 -1
- package/dist/edges/contradicts.js.map +1 -1
- package/dist/edges/{dependsOn.d.ts → depends-on.d.ts} +1 -1
- package/dist/edges/{dependsOn.js → depends-on.js} +4 -4
- package/dist/edges/depends-on.js.map +1 -0
- package/dist/edges/{derivedFrom.d.ts → derived-from.d.ts} +1 -1
- package/dist/edges/{derivedFrom.js → derived-from.js} +3 -3
- package/dist/edges/derived-from.js.map +1 -0
- package/dist/edges/elaborates.d.ts +1 -1
- package/dist/edges/elaborates.js.map +1 -1
- package/dist/edges/index.d.ts +7 -3
- package/dist/edges/index.js +7 -4
- package/dist/edges/index.js.map +1 -1
- package/dist/edges/informs.d.ts +1 -1
- package/dist/edges/informs.js.map +1 -1
- package/dist/edges/{propagationTypes.d.ts → propagation-types.d.ts} +14 -14
- package/dist/edges/{propagationTypes.js → propagation-types.js} +3 -3
- package/dist/edges/propagation-types.js.map +1 -0
- package/dist/edges/refutes.d.ts +1 -1
- package/dist/edges/refutes.js.map +1 -1
- package/dist/edges/supports.d.ts +1 -1
- package/dist/edges/supports.js.map +1 -1
- package/dist/edges/tests.d.ts +1 -1
- package/dist/edges/tests.js.map +1 -1
- package/dist/edges/utils.d.ts +1 -1
- package/dist/edges/utils.js.map +1 -1
- package/dist/embeddingTrigger.d.ts +14 -6
- package/dist/embeddingTrigger.js +11 -14
- package/dist/embeddingTrigger.js.map +1 -1
- package/dist/{entityBridge-DMaKooYn.d.ts → entityBridge-BhVDM3pc.d.ts} +5 -5
- package/dist/entityBridge.d.ts +1 -1
- package/dist/entityBridge.js +602 -225
- package/dist/entityBridge.js.map +1 -1
- package/dist/entityCanonicalMatch.d.ts +14 -12
- package/dist/entityCanonicalMatch.js.map +1 -1
- package/dist/{entityLifecycle-CvgSK5FV.d.ts → entityLifecycle-BsfCz9pS.d.ts} +5 -9
- package/dist/entityLifecycle.d.ts +1 -1
- package/dist/entityLifecycle.js +854 -480
- package/dist/entityLifecycle.js.map +1 -1
- package/dist/{entityValidation-KLZ_Xl2D.d.ts → entityValidation-B1yNEHJx.d.ts} +7 -6
- package/dist/entityValidation.d.ts +3 -1
- package/dist/entityValidation.js +60 -8
- package/dist/entityValidation.js.map +1 -1
- package/dist/{epistemicAnswers-C5ib4z6_.d.ts → epistemicAnswers-f47YMu9U.d.ts} +6 -6
- package/dist/epistemicAnswers.d.ts +1 -1
- package/dist/epistemicAnswers.js +587 -545
- package/dist/epistemicAnswers.js.map +1 -1
- package/dist/epistemicBeliefs.admin.d.ts +8 -8
- package/dist/epistemicBeliefs.admin.js +365 -166
- package/dist/epistemicBeliefs.admin.js.map +1 -1
- package/dist/epistemicBeliefs.backfills.d.ts +8 -8
- package/dist/epistemicBeliefs.backfills.js +655 -289
- package/dist/epistemicBeliefs.backfills.js.map +1 -1
- package/dist/epistemicBeliefs.confidence.d.ts +19 -15
- package/dist/epistemicBeliefs.confidence.js +633 -386
- package/dist/epistemicBeliefs.confidence.js.map +1 -1
- package/dist/epistemicBeliefs.core.d.ts +6 -6
- package/dist/epistemicBeliefs.core.js +717 -371
- package/dist/epistemicBeliefs.core.js.map +1 -1
- package/dist/epistemicBeliefs.d.ts +11 -9
- package/dist/epistemicBeliefs.forkEvidence.d.ts +2 -0
- package/dist/epistemicBeliefs.forkEvidence.js +8 -8
- package/dist/epistemicBeliefs.forkEvidence.js.map +1 -1
- package/dist/epistemicBeliefs.helpers.d.ts +68 -49
- package/dist/epistemicBeliefs.helpers.js +358 -211
- package/dist/epistemicBeliefs.helpers.js.map +1 -1
- package/dist/epistemicBeliefs.internal.d.ts +5 -5
- package/dist/epistemicBeliefs.internal.js +1248 -1026
- package/dist/epistemicBeliefs.internal.js.map +1 -1
- package/dist/epistemicBeliefs.js +4942 -3590
- package/dist/epistemicBeliefs.js.map +1 -1
- package/dist/epistemicBeliefs.lifecycle.d.ts +5 -5
- package/dist/epistemicBeliefs.lifecycle.js +1138 -781
- package/dist/epistemicBeliefs.lifecycle.js.map +1 -1
- package/dist/epistemicBeliefs.links.d.ts +7 -7
- package/dist/epistemicBeliefs.links.js +404 -267
- package/dist/epistemicBeliefs.links.js.map +1 -1
- package/dist/epistemicBeliefs.queries.d.ts +4 -4
- package/dist/epistemicBeliefs.queries.js +175 -20
- package/dist/epistemicBeliefs.queries.js.map +1 -1
- package/dist/epistemicBeliefs.topicAnchor.d.ts +6 -4
- package/dist/epistemicBeliefs.topicAnchor.js +12 -5
- package/dist/epistemicBeliefs.topicAnchor.js.map +1 -1
- package/dist/epistemicContracts.d.ts +28 -3
- package/dist/epistemicContracts.evaluators.d.ts +2 -0
- package/dist/epistemicContracts.evaluators.js +1062 -576
- package/dist/epistemicContracts.evaluators.js.map +1 -1
- package/dist/epistemicContracts.handlers.d.ts +15 -32
- package/dist/epistemicContracts.handlers.js +1829 -1351
- package/dist/epistemicContracts.handlers.js.map +1 -1
- package/dist/epistemicContracts.js +1131 -636
- package/dist/epistemicContracts.js.map +1 -1
- package/dist/epistemicContracts.metrics.d.ts +2 -0
- package/dist/epistemicContracts.metrics.js +375 -158
- package/dist/epistemicContracts.metrics.js.map +1 -1
- package/dist/epistemicContracts.types.d.ts +87 -81
- package/dist/epistemicEdgeCreation.d.ts +2 -0
- package/dist/epistemicEdgeCreation.js +87 -16
- package/dist/epistemicEdgeCreation.js.map +1 -1
- package/dist/{epistemicEdges-BF-cn4i3.d.ts → epistemicEdges-BGBh0QSP.d.ts} +4 -7
- package/dist/epistemicEdges.d.ts +6 -5
- package/dist/epistemicEdges.handlers.d.ts +3 -3
- package/dist/epistemicEdges.handlers.js +129 -24
- package/dist/epistemicEdges.handlers.js.map +1 -1
- package/dist/epistemicEdges.helpers.d.ts +6 -4
- package/dist/epistemicEdges.helpers.js +37 -2
- package/dist/epistemicEdges.helpers.js.map +1 -1
- package/dist/epistemicEdges.js +1966 -1202
- package/dist/epistemicEdges.js.map +1 -1
- package/dist/epistemicEdges.mutations.d.ts +7 -7
- package/dist/epistemicEdges.mutations.js +956 -579
- package/dist/epistemicEdges.mutations.js.map +1 -1
- package/dist/epistemicEdges.queries.d.ts +16 -16
- package/dist/epistemicEdges.queries.js +639 -367
- package/dist/epistemicEdges.queries.js.map +1 -1
- package/dist/epistemicEdges.types.d.ts +10 -8
- package/dist/epistemicEvidence.d.ts +4 -1
- package/dist/epistemicEvidence.js +933 -532
- package/dist/epistemicEvidence.js.map +1 -1
- package/dist/epistemicEvidenceHelpers.d.ts +26 -10
- package/dist/epistemicEvidenceHelpers.js +239 -200
- package/dist/epistemicEvidenceHelpers.js.map +1 -1
- package/dist/epistemicEvidenceMutations.d.ts +8 -8
- package/dist/epistemicEvidenceMutations.js +840 -692
- package/dist/epistemicEvidenceMutations.js.map +1 -1
- package/dist/epistemicEvidenceQueries.d.ts +8 -8
- package/dist/epistemicEvidenceQueries.js +514 -238
- package/dist/epistemicEvidenceQueries.js.map +1 -1
- package/dist/epistemicHelpers.d.ts +4 -2
- package/dist/epistemicHelpers.js +308 -134
- package/dist/epistemicHelpers.js.map +1 -1
- package/dist/epistemicInsert.d.ts +16 -4
- package/dist/epistemicInsert.js +6 -3
- package/dist/epistemicInsert.js.map +1 -1
- package/dist/epistemicLayerRules.d.ts +10 -8
- package/dist/epistemicLayerRules.js +1 -5
- package/dist/epistemicLayerRules.js.map +1 -1
- package/dist/{epistemicLinking-CfE00tHJ.d.ts → epistemicLinking-CsCDv2cN.d.ts} +3 -3
- package/dist/epistemicLinking.d.ts +1 -1
- package/dist/epistemicLinking.js +177 -100
- package/dist/epistemicLinking.js.map +1 -1
- package/dist/epistemicNodeCreation.d.ts +2 -0
- package/dist/epistemicNodeCreation.js +203 -40
- package/dist/epistemicNodeCreation.js.map +1 -1
- package/dist/{epistemicNodes-BCQxpYx_.d.ts → epistemicNodes-CokAgBHg.d.ts} +3 -3
- package/dist/epistemicNodes.d.ts +3 -3
- package/dist/epistemicNodes.helpers.d.ts +24 -15
- package/dist/epistemicNodes.helpers.js.map +1 -1
- package/dist/epistemicNodes.internal.d.ts +6 -6
- package/dist/epistemicNodes.internal.js +389 -319
- package/dist/epistemicNodes.internal.js.map +1 -1
- package/dist/epistemicNodes.js +700 -504
- package/dist/epistemicNodes.js.map +1 -1
- package/dist/epistemicNodes.mutations.d.ts +6 -6
- package/dist/epistemicNodes.mutations.js +560 -463
- package/dist/epistemicNodes.mutations.js.map +1 -1
- package/dist/epistemicNodes.queries.d.ts +8 -8
- package/dist/epistemicNodes.queries.js +311 -314
- package/dist/epistemicNodes.queries.js.map +1 -1
- package/dist/epistemicNodes.validators.d.ts +2 -2
- package/dist/epistemicNodes.validators.js.map +1 -1
- package/dist/epistemicQuestions.conviction.d.ts +8 -8
- package/dist/epistemicQuestions.conviction.js +665 -484
- package/dist/epistemicQuestions.conviction.js.map +1 -1
- package/dist/epistemicQuestions.create.d.ts +4 -4
- package/dist/epistemicQuestions.create.js +640 -612
- package/dist/epistemicQuestions.create.js.map +1 -1
- package/dist/epistemicQuestions.d.ts +8 -5
- package/dist/epistemicQuestions.evidence.d.ts +2 -2
- package/dist/epistemicQuestions.evidence.js +475 -383
- package/dist/epistemicQuestions.evidence.js.map +1 -1
- package/dist/epistemicQuestions.helpers.d.ts +125 -24
- package/dist/epistemicQuestions.helpers.js +240 -209
- package/dist/epistemicQuestions.helpers.js.map +1 -1
- package/dist/epistemicQuestions.js +3474 -2823
- package/dist/epistemicQuestions.js.map +1 -1
- package/dist/epistemicQuestions.lifecycle.d.ts +2 -2
- package/dist/epistemicQuestions.lifecycle.js +607 -546
- package/dist/epistemicQuestions.lifecycle.js.map +1 -1
- package/dist/epistemicQuestions.queries.d.ts +12 -7
- package/dist/epistemicQuestions.queries.js +305 -244
- package/dist/epistemicQuestions.queries.js.map +1 -1
- package/dist/epistemicQuestions.sprint.d.ts +2 -2
- package/dist/epistemicQuestions.sprint.js +600 -394
- package/dist/epistemicQuestions.sprint.js.map +1 -1
- package/dist/epistemicQuestions.tail.d.ts +6 -6
- package/dist/epistemicQuestions.tail.js +572 -433
- package/dist/epistemicQuestions.tail.js.map +1 -1
- package/dist/{epistemicSources-dlKj58Jp.d.ts → epistemicSources-DQtaEkWs.d.ts} +4 -4
- package/dist/epistemicSources.d.ts +1 -1
- package/dist/epistemicSources.js +351 -311
- package/dist/epistemicSources.js.map +1 -1
- package/dist/evaluators/index.d.ts +8 -6
- package/dist/evaluators/index.js +399 -167
- package/dist/evaluators/index.js.map +1 -1
- package/dist/evaluators/lint-checker-evaluator.d.ts +16 -0
- package/dist/evaluators/{lintCheckerEvaluator.js → lint-checker-evaluator.js} +10 -5
- package/dist/evaluators/lint-checker-evaluator.js.map +1 -0
- package/dist/evaluators/{sentryCheckerEvaluator.d.ts → sentry-checker-evaluator.d.ts} +7 -2
- package/dist/evaluators/{sentryCheckerEvaluator.js → sentry-checker-evaluator.js} +3 -3
- package/dist/evaluators/sentry-checker-evaluator.js.map +1 -0
- package/dist/evaluators/shared.d.ts +2 -2
- package/dist/evaluators/shared.js +3 -1
- package/dist/evaluators/shared.js.map +1 -1
- package/dist/evaluators/{testRunnerEvaluator.d.ts → test-runner-evaluator.d.ts} +6 -1
- package/dist/evaluators/{testRunnerEvaluator.js → test-runner-evaluator.js} +6 -4
- package/dist/evaluators/test-runner-evaluator.js.map +1 -0
- package/dist/evaluators/tsc-checker-evaluator.d.ts +16 -0
- package/dist/evaluators/{tscCheckerEvaluator.js → tsc-checker-evaluator.js} +10 -5
- package/dist/evaluators/tsc-checker-evaluator.js.map +1 -0
- package/dist/graphTypes.js +6 -2
- package/dist/graphTypes.js.map +1 -1
- package/dist/helpers.d.ts +2 -0
- package/dist/helpers.js +313 -93
- package/dist/helpers.js.map +1 -1
- package/dist/{index-C-Kyd7hD.d.ts → index-DZxyC9Pb.d.ts} +7 -6
- package/dist/index.d.ts +86 -83
- package/dist/index.js +16914 -11760
- package/dist/index.js.map +1 -1
- package/dist/invariantEnforcement.d.ts +3 -3
- package/dist/invariantEnforcement.js.map +1 -1
- package/dist/logicalRoleInference.d.ts +2 -0
- package/dist/logicalRoleInference.js +1 -1
- package/dist/logicalRoleInference.js.map +1 -1
- package/dist/matcherFeedbackUtils.d.ts +2 -2
- package/dist/matcherFeedbackUtils.js.map +1 -1
- package/dist/{ontology-matching-C6rrz2VP.d.ts → ontology-matching-C-mYFrir.d.ts} +16 -16
- package/dist/ontology-matching.d.ts +1 -1
- package/dist/{ontologyApproval-CFYmqKmk.d.ts → ontologyApproval-BVt0feJi.d.ts} +10 -10
- package/dist/ontologyApproval.d.ts +1 -1
- package/dist/ontologyApproval.js +7 -1
- package/dist/ontologyApproval.js.map +1 -1
- package/dist/ontologyDefinitions.d.ts +14 -24
- package/dist/ontologyDefinitions.js +269 -34
- package/dist/ontologyDefinitions.js.map +1 -1
- package/dist/ontologyHelpers.d.ts +13 -13
- package/dist/ontologyHelpers.js.map +1 -1
- package/dist/{ontologyRegistry-B67rPJ16.d.ts → ontologyRegistry-CljS-ENv.d.ts} +2 -2
- package/dist/ontologyRegistry.d.ts +1 -1
- package/dist/ontologyRegistry.js +34 -6
- package/dist/ontologyRegistry.js.map +1 -1
- package/dist/{projectionReconciliation-jww2fBI0.d.ts → projectionReconciliation-DnrSgHSQ.d.ts} +4 -4
- package/dist/projectionReconciliation.d.ts +1 -1
- package/dist/projectionReconciliation.js +57 -10
- package/dist/projectionReconciliation.js.map +1 -1
- package/dist/{projectionStaleness-CmdbpjVK.d.ts → projectionStaleness-C8ImQ2zP.d.ts} +17 -17
- package/dist/projectionStaleness.d.ts +1 -1
- package/dist/projectionStaleness.js +8 -2
- package/dist/projectionStaleness.js.map +1 -1
- package/dist/proof-attestation.json +1 -1
- package/dist/{questionEvidenceLinks-DFlyPpAj.d.ts → questionEvidenceLinks-_nPRa-LY.d.ts} +10 -10
- package/dist/questionEvidenceLinks.d.ts +1 -1
- package/dist/questionEvidenceLinks.js +564 -347
- package/dist/questionEvidenceLinks.js.map +1 -1
- package/dist/{resolverTypes-CC8Ea2E2.d.ts → resolverTypes-BOXPxLET.d.ts} +8 -7
- package/dist/resolverTypes.d.ts +4 -2
- package/dist/{resolvers-Br1a6eLV.d.ts → resolvers-B1TIBmRO.d.ts} +3 -1
- package/dist/resolvers.d.ts +5 -3
- package/dist/resolvers.js +121 -77
- package/dist/resolvers.js.map +1 -1
- package/dist/scopeResolverCompat.d.ts +10 -7
- package/dist/scopeResolverCompat.js +106 -123
- package/dist/scopeResolverCompat.js.map +1 -1
- package/dist/{text-matching-DNg4M5Wd.d.ts → text-matching-DzFooju6.d.ts} +7 -7
- package/dist/text-matching.d.ts +1 -1
- package/dist/topicOntologyResolver.d.ts +22 -21
- package/dist/topicOntologyResolver.js +54 -32
- package/dist/topicOntologyResolver.js.map +1 -1
- package/dist/topicProjectOverlay.d.ts +30 -20
- package/dist/topicProjectOverlay.js +120 -76
- package/dist/topicProjectOverlay.js.map +1 -1
- package/dist/{topicScope-7zhyeGl7.d.ts → topicScope-DJVa0mLa.d.ts} +22 -7
- package/dist/topicScope.d.ts +3 -1
- package/dist/topicScope.js +104 -119
- package/dist/topicScope.js.map +1 -1
- package/dist/workflowBridge.d.ts +26 -15
- package/dist/workflowBridge.js +140 -144
- package/dist/workflowBridge.js.map +1 -1
- package/dist/workspaceIsolation.d.ts +14 -12
- package/dist/workspaceIsolation.js +108 -122
- package/dist/workspaceIsolation.js.map +1 -1
- package/package.json +4 -4
- package/dist/edges/dependsOn.js.map +0 -1
- package/dist/edges/derivedFrom.js.map +0 -1
- package/dist/edges/propagationTypes.js.map +0 -1
- package/dist/evaluators/lintCheckerEvaluator.d.ts +0 -11
- package/dist/evaluators/lintCheckerEvaluator.js.map +0 -1
- package/dist/evaluators/sentryCheckerEvaluator.js.map +0 -1
- package/dist/evaluators/testRunnerEvaluator.js.map +0 -1
- package/dist/evaluators/tscCheckerEvaluator.d.ts +0 -11
- package/dist/evaluators/tscCheckerEvaluator.js.map +0 -1
- package/dist/{epistemicQuestions-bwHd2FWE.d.ts → epistemicQuestions-Do1fhYm5.d.ts} +4 -4
|
@@ -1,15 +1,19 @@
|
|
|
1
|
-
import { v } from 'convex/values';
|
|
2
1
|
import { checkScopeAccess } from '@lucern/access-control/access';
|
|
3
2
|
import { permissiveReturn } from '@lucern/contracts/schema-helpers/validators';
|
|
4
|
-
import {
|
|
3
|
+
import { v } from 'convex/values';
|
|
4
|
+
import { unsafeConvexAnyApi } from '@lucern/contracts/convex/unsafeAnyApi';
|
|
5
|
+
import { componentsGeneric, queryGeneric, mutationGeneric } from 'convex/server';
|
|
6
|
+
import '@lucern/contracts';
|
|
5
7
|
import { generateGlobalId, assertUuidV7Identity } from '@lucern/contracts/ids';
|
|
6
8
|
import '@lucern/contracts/schema-helpers/spine/tables/epistemicNodes';
|
|
7
|
-
import '@lucern/contracts';
|
|
8
9
|
|
|
9
10
|
// src/epistemicQuestions.tail.ts
|
|
10
|
-
var
|
|
11
|
+
var unsafeApi = unsafeConvexAnyApi(
|
|
12
|
+
"graph-primitives top-level module bundle lacks a committed Convex _generated/api surface"
|
|
13
|
+
);
|
|
14
|
+
var api = unsafeApi;
|
|
11
15
|
componentsGeneric();
|
|
12
|
-
var internal =
|
|
16
|
+
var internal = unsafeApi;
|
|
13
17
|
var mutation = mutationGeneric;
|
|
14
18
|
var query = queryGeneric;
|
|
15
19
|
|
|
@@ -43,6 +47,10 @@ function debugGraphPrimitiveFallback(message, context) {
|
|
|
43
47
|
}
|
|
44
48
|
console.debug(message, context ?? {});
|
|
45
49
|
}
|
|
50
|
+
function insertEpistemicNode(ctx, doc) {
|
|
51
|
+
assertUuidV7Identity("epistemicNodes", doc.globalId);
|
|
52
|
+
return ctx.db.insert("epistemicNodes", doc);
|
|
53
|
+
}
|
|
46
54
|
|
|
47
55
|
// src/topicProjectOverlay.ts
|
|
48
56
|
var LEGACY_SCOPE_FIELD = "graphScopeProjectId";
|
|
@@ -62,6 +70,10 @@ function readStringArray(value) {
|
|
|
62
70
|
function readMetadata(topic) {
|
|
63
71
|
return topic.metadata && typeof topic.metadata === "object" ? topic.metadata : {};
|
|
64
72
|
}
|
|
73
|
+
function omitMetadataKey(metadata, key) {
|
|
74
|
+
const { [key]: _omitted, ...rest } = metadata;
|
|
75
|
+
return rest;
|
|
76
|
+
}
|
|
65
77
|
function readLegacyProjectId(value) {
|
|
66
78
|
if (!value) {
|
|
67
79
|
return;
|
|
@@ -142,9 +154,12 @@ async function resolveTopicDoc(ctx, scopeId) {
|
|
|
142
154
|
);
|
|
143
155
|
}
|
|
144
156
|
try {
|
|
145
|
-
const topic = await ctx.runQuery(
|
|
146
|
-
|
|
147
|
-
|
|
157
|
+
const topic = await ctx.runQuery(
|
|
158
|
+
api.topics.getByLegacyScopeId,
|
|
159
|
+
{
|
|
160
|
+
projectId: String(scopeId)
|
|
161
|
+
}
|
|
162
|
+
);
|
|
148
163
|
if (topic?.name !== void 0 && topic?.type !== void 0) {
|
|
149
164
|
return topic;
|
|
150
165
|
}
|
|
@@ -164,8 +179,18 @@ function materializeTopicProjectOverlay(topic, idMode = "legacy") {
|
|
|
164
179
|
const outwardId = idMode === "topic" ? topicId : storageProjectId;
|
|
165
180
|
const visibility = coerceVisibility(topic.visibility) || coerceVisibility(metadata.visibility) || "private";
|
|
166
181
|
const status = coerceStatus(topic.status) || coerceStatus(metadata.status) || "active";
|
|
167
|
-
|
|
168
|
-
|
|
182
|
+
let createdAt = 0;
|
|
183
|
+
if (typeof topic.createdAt === "number") {
|
|
184
|
+
createdAt = topic.createdAt;
|
|
185
|
+
} else if (typeof topic._creationTime === "number") {
|
|
186
|
+
createdAt = topic._creationTime;
|
|
187
|
+
}
|
|
188
|
+
let updatedAt = createdAt;
|
|
189
|
+
if (typeof topic.updatedAt === "number") {
|
|
190
|
+
updatedAt = topic.updatedAt;
|
|
191
|
+
} else if (typeof metadata.updatedAt === "number") {
|
|
192
|
+
updatedAt = metadata.updatedAt;
|
|
193
|
+
}
|
|
169
194
|
return {
|
|
170
195
|
...metadata,
|
|
171
196
|
_id: outwardId,
|
|
@@ -234,90 +259,113 @@ async function patchTopicProjectOverlay(ctx, scopeId, value) {
|
|
|
234
259
|
if (!topic) {
|
|
235
260
|
return null;
|
|
236
261
|
}
|
|
237
|
-
const
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
262
|
+
const plan = buildTopicProjectOverlayPatchPlan(topic, value);
|
|
263
|
+
await applyTopicProjectOverlayPatch(ctx, topic, plan);
|
|
264
|
+
return materializeTopicProjectOverlay({
|
|
265
|
+
...topic,
|
|
266
|
+
...plan.patch,
|
|
267
|
+
metadata: plan.nextMetadata
|
|
268
|
+
});
|
|
269
|
+
}
|
|
270
|
+
function buildTopicProjectOverlayPatchPlan(topic, value) {
|
|
271
|
+
const plan = {
|
|
272
|
+
nextMetadata: { ...readMetadata(topic) },
|
|
273
|
+
patch: {},
|
|
274
|
+
topicUpdateArgs: {
|
|
275
|
+
id: String(topic._id)
|
|
276
|
+
}
|
|
241
277
|
};
|
|
242
278
|
for (const [key, rawValue] of Object.entries(value)) {
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
default:
|
|
290
|
-
if (rawValue === void 0) {
|
|
291
|
-
delete nextMetadata[key];
|
|
292
|
-
} else {
|
|
293
|
-
nextMetadata[key] = rawValue;
|
|
294
|
-
}
|
|
295
|
-
}
|
|
279
|
+
applyTopicProjectOverlayPatchEntry(plan, key, rawValue);
|
|
280
|
+
}
|
|
281
|
+
plan.patch.updatedAt = Date.now();
|
|
282
|
+
plan.patch.metadata = plan.nextMetadata;
|
|
283
|
+
plan.topicUpdateArgs.metadata = plan.nextMetadata;
|
|
284
|
+
return plan;
|
|
285
|
+
}
|
|
286
|
+
function applyTopicProjectOverlayPatchEntry(plan, key, rawValue) {
|
|
287
|
+
switch (key) {
|
|
288
|
+
case "_id":
|
|
289
|
+
case "projectId":
|
|
290
|
+
case "topicId":
|
|
291
|
+
case "legacyProjectId":
|
|
292
|
+
case "storageProjectId":
|
|
293
|
+
case "updatedAt":
|
|
294
|
+
case "createdAt":
|
|
295
|
+
return;
|
|
296
|
+
case "name":
|
|
297
|
+
case "description":
|
|
298
|
+
plan.patch[key] = rawValue;
|
|
299
|
+
plan.topicUpdateArgs[key] = rawValue;
|
|
300
|
+
return;
|
|
301
|
+
case "tenantId":
|
|
302
|
+
case "workspaceId":
|
|
303
|
+
case "ownerId":
|
|
304
|
+
throw new Error(
|
|
305
|
+
`patchTopicProjectOverlay cannot mutate ${key} via component-owned topics`
|
|
306
|
+
);
|
|
307
|
+
case "status":
|
|
308
|
+
applyTopicStatusPatch(plan, rawValue);
|
|
309
|
+
return;
|
|
310
|
+
case "visibility":
|
|
311
|
+
applyTopicVisibilityPatch(plan, rawValue);
|
|
312
|
+
return;
|
|
313
|
+
case "type":
|
|
314
|
+
applyTopicProjectTypePatch(plan, rawValue);
|
|
315
|
+
return;
|
|
316
|
+
default:
|
|
317
|
+
applyTopicMetadataPatch(plan, key, rawValue);
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
function applyTopicStatusPatch(plan, rawValue) {
|
|
321
|
+
const status = coerceStatus(rawValue);
|
|
322
|
+
if (status) {
|
|
323
|
+
plan.patch.status = status;
|
|
324
|
+
plan.topicUpdateArgs.status = status;
|
|
296
325
|
}
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
326
|
+
}
|
|
327
|
+
function applyTopicVisibilityPatch(plan, rawValue) {
|
|
328
|
+
const visibility = coerceVisibility(rawValue);
|
|
329
|
+
if (visibility) {
|
|
330
|
+
plan.patch.visibility = visibility;
|
|
331
|
+
plan.topicUpdateArgs.visibility = visibility;
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
function applyTopicProjectTypePatch(plan, rawValue) {
|
|
335
|
+
const projectType = readNonEmptyString(rawValue);
|
|
336
|
+
if (projectType) {
|
|
337
|
+
plan.nextMetadata.projectType = projectType;
|
|
338
|
+
return;
|
|
339
|
+
}
|
|
340
|
+
plan.nextMetadata = omitMetadataKey(plan.nextMetadata, "projectType");
|
|
341
|
+
}
|
|
342
|
+
function applyTopicMetadataPatch(plan, key, rawValue) {
|
|
343
|
+
if (rawValue === void 0) {
|
|
344
|
+
plan.nextMetadata = omitMetadataKey(plan.nextMetadata, key);
|
|
345
|
+
return;
|
|
346
|
+
}
|
|
347
|
+
plan.nextMetadata[key] = rawValue;
|
|
348
|
+
}
|
|
349
|
+
async function applyTopicProjectOverlayPatch(ctx, topic, plan) {
|
|
300
350
|
if (typeof ctx.runMutation === "function") {
|
|
301
351
|
try {
|
|
302
|
-
await ctx.runMutation(api.topics.update, topicUpdateArgs);
|
|
352
|
+
await ctx.runMutation(api.topics.update, plan.topicUpdateArgs);
|
|
303
353
|
} catch (error) {
|
|
304
|
-
if (!
|
|
354
|
+
if (!canPatchTopicViaLocalDb(ctx, error)) {
|
|
305
355
|
throw error;
|
|
306
356
|
}
|
|
307
|
-
await ctx.db.patch(
|
|
357
|
+
await ctx.db.patch(topic._id, plan.patch);
|
|
308
358
|
}
|
|
309
359
|
} else if (ctx?.db && typeof ctx.db.patch === "function") {
|
|
310
|
-
await ctx.db.patch(
|
|
360
|
+
await ctx.db.patch(topic._id, plan.patch);
|
|
311
361
|
} else {
|
|
312
362
|
throw new Error(
|
|
313
363
|
"Cannot patch topic without component adapter (ctx.runMutation unavailable)"
|
|
314
364
|
);
|
|
315
365
|
}
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
metadata: nextMetadata
|
|
320
|
-
});
|
|
366
|
+
}
|
|
367
|
+
function canPatchTopicViaLocalDb(ctx, error) {
|
|
368
|
+
return isMissingLucernChildComponentError(error) && Boolean(ctx?.db) && typeof ctx.db?.patch === "function";
|
|
321
369
|
}
|
|
322
370
|
|
|
323
371
|
// src/resolvers.ts
|
|
@@ -345,7 +393,7 @@ async function patchProjectWithTolerance(ctx, projectId, value) {
|
|
|
345
393
|
try {
|
|
346
394
|
await patchTopicProjectOverlay(ctx, projectId, value);
|
|
347
395
|
} catch (error) {
|
|
348
|
-
if (!isAdvisoryTopicPatch(value)
|
|
396
|
+
if (!(isAdvisoryTopicPatch(value) && isMissingLucernChildComponentError2(error))) {
|
|
349
397
|
throw error;
|
|
350
398
|
}
|
|
351
399
|
console.warn(
|
|
@@ -412,13 +460,15 @@ function asMappedProjectId(topic) {
|
|
|
412
460
|
if (!topic) {
|
|
413
461
|
return;
|
|
414
462
|
}
|
|
415
|
-
const directLegacyProjectId = normalizeScopeValue(
|
|
463
|
+
const directLegacyProjectId = normalizeScopeValue(
|
|
464
|
+
topic[LEGACY_SCOPE_FIELD2]
|
|
465
|
+
);
|
|
416
466
|
if (directLegacyProjectId) {
|
|
417
467
|
return directLegacyProjectId;
|
|
418
468
|
}
|
|
419
469
|
const metadata = topic.metadata || {};
|
|
420
470
|
const candidate = metadata[LEGACY_SCOPE_FIELD2] || metadata.legacyProjectId || metadata.projectId || metadata.scopeProjectId;
|
|
421
|
-
return candidate ? candidate : void 0;
|
|
471
|
+
return typeof candidate === "string" ? normalizeScopeValue(candidate) : void 0;
|
|
422
472
|
}
|
|
423
473
|
function normalizeScopeValue(value) {
|
|
424
474
|
if (typeof value !== "string") {
|
|
@@ -443,8 +493,9 @@ function pickPrimaryTopic(candidates) {
|
|
|
443
493
|
})[0];
|
|
444
494
|
}
|
|
445
495
|
async function findTopicsByScopeAlias(ctx, scopeId) {
|
|
496
|
+
const query2 = ctx.db.query("topics");
|
|
446
497
|
try {
|
|
447
|
-
return await
|
|
498
|
+
return await query2.withIndex(
|
|
448
499
|
"by_graph_scope_project",
|
|
449
500
|
(q) => q.eq(LEGACY_SCOPE_FIELD2, scopeId)
|
|
450
501
|
).collect();
|
|
@@ -456,7 +507,7 @@ async function findTopicsByScopeAlias(ctx, scopeId) {
|
|
|
456
507
|
scopeId
|
|
457
508
|
}
|
|
458
509
|
);
|
|
459
|
-
const topics = await
|
|
510
|
+
const topics = await query2.collect();
|
|
460
511
|
return topics.filter((topic) => {
|
|
461
512
|
const normalizedGlobalId = normalizeScopeValue(topic.globalId);
|
|
462
513
|
const mappedProjectId = asMappedProjectId(topic);
|
|
@@ -512,244 +563,140 @@ async function resolveInheritedWorkspaceScope(ctx, topic) {
|
|
|
512
563
|
let current = topic;
|
|
513
564
|
for (let i = 0; i < MAX_DEPTH && current?.parentTopicId; i++) {
|
|
514
565
|
current = await ctx.db.get(current.parentTopicId);
|
|
515
|
-
if (!current)
|
|
566
|
+
if (!current) {
|
|
567
|
+
break;
|
|
568
|
+
}
|
|
516
569
|
if (!tenantId) {
|
|
517
570
|
tenantId = normalizeScopeValue(current.tenantId);
|
|
518
571
|
}
|
|
519
572
|
if (!workspaceId) {
|
|
520
573
|
workspaceId = normalizeScopeValue(current.workspaceId);
|
|
521
574
|
}
|
|
522
|
-
if (tenantId && workspaceId)
|
|
575
|
+
if (tenantId && workspaceId) {
|
|
576
|
+
break;
|
|
577
|
+
}
|
|
523
578
|
}
|
|
524
579
|
return { tenantId, workspaceId };
|
|
525
580
|
}
|
|
526
581
|
async function resolveTopicProjectScope(ctx, args) {
|
|
527
582
|
if (args.topicId) {
|
|
528
|
-
|
|
529
|
-
try {
|
|
530
|
-
topic = await ctx.db.get(
|
|
531
|
-
args.topicId
|
|
532
|
-
);
|
|
533
|
-
} catch (error) {
|
|
534
|
-
debugGraphPrimitiveFallback(
|
|
535
|
-
"[topicScope] Failed to load topic by direct id",
|
|
536
|
-
{
|
|
537
|
-
error,
|
|
538
|
-
topicId: args.topicId
|
|
539
|
-
}
|
|
540
|
-
);
|
|
541
|
-
}
|
|
542
|
-
if (!topic) {
|
|
543
|
-
topic = await tryResolveHostTopicById(ctx, String(args.topicId));
|
|
544
|
-
}
|
|
545
|
-
if (!topic) {
|
|
546
|
-
topic = pickPrimaryTopic(
|
|
547
|
-
await findTopicsByScopeAlias(ctx, String(args.topicId))
|
|
548
|
-
) ?? null;
|
|
549
|
-
}
|
|
550
|
-
if (!topic) {
|
|
551
|
-
const nodeScope = await resolveTopicNodeScopeOrNull(
|
|
552
|
-
ctx,
|
|
553
|
-
String(args.topicId)
|
|
554
|
-
);
|
|
555
|
-
if (nodeScope) {
|
|
556
|
-
return nodeScope;
|
|
557
|
-
}
|
|
558
|
-
throw new Error(`Topic not found: ${String(args.topicId)}`);
|
|
559
|
-
}
|
|
560
|
-
const inherited = await resolveInheritedWorkspaceScope(ctx, topic);
|
|
561
|
-
const mapped = asMappedProjectId(topic);
|
|
562
|
-
if (mapped) {
|
|
563
|
-
return {
|
|
564
|
-
topicId: topic._id,
|
|
565
|
-
projectId: mapped,
|
|
566
|
-
tenantId: inherited.tenantId,
|
|
567
|
-
workspaceId: inherited.workspaceId,
|
|
568
|
-
source: "topic"
|
|
569
|
-
};
|
|
570
|
-
}
|
|
571
|
-
return {
|
|
572
|
-
topicId: topic._id,
|
|
573
|
-
tenantId: inherited.tenantId,
|
|
574
|
-
workspaceId: inherited.workspaceId,
|
|
575
|
-
source: "topic"
|
|
576
|
-
};
|
|
583
|
+
return await resolveScopeFromTopicId(ctx, args.topicId);
|
|
577
584
|
}
|
|
578
585
|
if (args.projectId) {
|
|
579
|
-
|
|
580
|
-
try {
|
|
581
|
-
directTopic = await ctx.db.get(
|
|
582
|
-
args.projectId
|
|
583
|
-
);
|
|
584
|
-
} catch (error) {
|
|
585
|
-
debugGraphPrimitiveFallback(
|
|
586
|
-
"[topicScope] Failed to load direct project topic",
|
|
587
|
-
{
|
|
588
|
-
error,
|
|
589
|
-
projectId: args.projectId
|
|
590
|
-
}
|
|
591
|
-
);
|
|
592
|
-
}
|
|
593
|
-
if (directTopic) {
|
|
594
|
-
const inherited = await resolveInheritedWorkspaceScope(ctx, directTopic);
|
|
595
|
-
const mapped = asMappedProjectId(directTopic);
|
|
596
|
-
return {
|
|
597
|
-
topicId: directTopic._id,
|
|
598
|
-
projectId: mapped ?? args.projectId,
|
|
599
|
-
tenantId: inherited.tenantId,
|
|
600
|
-
workspaceId: inherited.workspaceId,
|
|
601
|
-
source: "topic_inferred"
|
|
602
|
-
};
|
|
603
|
-
}
|
|
604
|
-
directTopic = await tryResolveHostTopicByLegacyScope(ctx, args.projectId);
|
|
605
|
-
if (directTopic) {
|
|
606
|
-
const inherited = await resolveInheritedWorkspaceScope(ctx, directTopic);
|
|
607
|
-
const mapped = asMappedProjectId(directTopic);
|
|
608
|
-
return {
|
|
609
|
-
topicId: directTopic._id,
|
|
610
|
-
projectId: mapped ?? args.projectId,
|
|
611
|
-
tenantId: inherited.tenantId,
|
|
612
|
-
workspaceId: inherited.workspaceId,
|
|
613
|
-
source: "topic_inferred"
|
|
614
|
-
};
|
|
615
|
-
}
|
|
616
|
-
const topics = await findTopicsByScopeAlias(ctx, args.projectId);
|
|
617
|
-
const primary = pickPrimaryTopic(topics);
|
|
618
|
-
if (primary) {
|
|
619
|
-
const inherited = await resolveInheritedWorkspaceScope(ctx, primary);
|
|
620
|
-
return {
|
|
621
|
-
topicId: primary._id,
|
|
622
|
-
projectId: args.projectId,
|
|
623
|
-
tenantId: inherited.tenantId,
|
|
624
|
-
workspaceId: inherited.workspaceId,
|
|
625
|
-
source: "project_mapped_topic"
|
|
626
|
-
};
|
|
627
|
-
}
|
|
628
|
-
const nodeScope = await resolveTopicNodeScopeOrNull(
|
|
629
|
-
ctx,
|
|
630
|
-
String(args.projectId)
|
|
631
|
-
);
|
|
632
|
-
if (nodeScope) {
|
|
633
|
-
return {
|
|
634
|
-
...nodeScope,
|
|
635
|
-
projectId: nodeScope.projectId ?? String(args.projectId)
|
|
636
|
-
};
|
|
637
|
-
}
|
|
638
|
-
throw new Error(
|
|
639
|
-
`Legacy project scope ${String(args.projectId)} has no mapped topic.`
|
|
640
|
-
);
|
|
586
|
+
return await resolveScopeFromLegacyProjectId(ctx, args.projectId);
|
|
641
587
|
}
|
|
642
588
|
throw new Error(
|
|
643
589
|
"Missing scope: provide topicId (preferred) or legacy projectId alias."
|
|
644
590
|
);
|
|
645
591
|
}
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
function normalizeScopeValue2(value) {
|
|
651
|
-
if (typeof value !== "string") {
|
|
652
|
-
return;
|
|
592
|
+
async function resolveScopeFromTopicId(ctx, topicId) {
|
|
593
|
+
const topic = await resolveTopicDocFromTopicId(ctx, topicId);
|
|
594
|
+
if (topic) {
|
|
595
|
+
return await buildTopicScope(ctx, topic, "topic");
|
|
653
596
|
}
|
|
654
|
-
const
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
function throwWorkspaceIsolationError(args) {
|
|
658
|
-
const error = new Error(args.message);
|
|
659
|
-
error.status = 409;
|
|
660
|
-
error.code = "INVARIANT_VIOLATION";
|
|
661
|
-
error.invariantCode = args.invariantCode;
|
|
662
|
-
error.suggestion = args.suggestion;
|
|
663
|
-
error.details = args.details;
|
|
664
|
-
throw error;
|
|
665
|
-
}
|
|
666
|
-
async function resolveNodeScopeForWorkspaceIsolation(ctx, node) {
|
|
667
|
-
const epistemicLayer = typeof node?.epistemicLayer === "string" ? node.epistemicLayer : void 0;
|
|
668
|
-
const resolved = {
|
|
669
|
-
tenantId: normalizeScopeValue2(node?.tenantId),
|
|
670
|
-
workspaceId: normalizeScopeValue2(node?.workspaceId),
|
|
671
|
-
epistemicLayer,
|
|
672
|
-
nodeType: typeof node?.nodeType === "string" ? node.nodeType : void 0
|
|
673
|
-
};
|
|
674
|
-
if (!node) {
|
|
675
|
-
return resolved;
|
|
597
|
+
const nodeScope = await resolveTopicNodeScopeOrNull(ctx, String(topicId));
|
|
598
|
+
if (nodeScope) {
|
|
599
|
+
return nodeScope;
|
|
676
600
|
}
|
|
677
|
-
|
|
678
|
-
|
|
601
|
+
throw new Error(`Topic not found: ${String(topicId)}`);
|
|
602
|
+
}
|
|
603
|
+
async function resolveTopicDocFromTopicId(ctx, topicId) {
|
|
604
|
+
const direct = await tryReadTopicDoc(ctx, topicId, {
|
|
605
|
+
failureLog: "[topicScope] Failed to load topic by direct id",
|
|
606
|
+
idLogKey: "topicId"
|
|
607
|
+
});
|
|
608
|
+
if (direct) {
|
|
609
|
+
return direct;
|
|
679
610
|
}
|
|
680
|
-
|
|
681
|
-
|
|
611
|
+
const hostTopic = await tryResolveHostTopicById(ctx, String(topicId));
|
|
612
|
+
if (hostTopic) {
|
|
613
|
+
return hostTopic;
|
|
682
614
|
}
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
615
|
+
return pickPrimaryTopic(await findTopicsByScopeAlias(ctx, String(topicId))) ?? null;
|
|
616
|
+
}
|
|
617
|
+
async function resolveScopeFromLegacyProjectId(ctx, legacyProjectId) {
|
|
618
|
+
const directTopic = await resolveDirectLegacyProjectTopic(
|
|
619
|
+
ctx,
|
|
620
|
+
legacyProjectId
|
|
621
|
+
);
|
|
622
|
+
if (directTopic) {
|
|
623
|
+
return await buildTopicScope(ctx, directTopic, "topic_inferred", {
|
|
624
|
+
fallbackProjectId: legacyProjectId
|
|
686
625
|
});
|
|
687
|
-
return {
|
|
688
|
-
...resolved,
|
|
689
|
-
tenantId: topicScope.tenantId,
|
|
690
|
-
workspaceId: topicScope.workspaceId
|
|
691
|
-
};
|
|
692
626
|
}
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
627
|
+
const primary = pickPrimaryTopic(
|
|
628
|
+
await findTopicsByScopeAlias(ctx, legacyProjectId)
|
|
629
|
+
);
|
|
630
|
+
if (primary) {
|
|
631
|
+
return await buildTopicScope(ctx, primary, "project_mapped_topic", {
|
|
632
|
+
fallbackProjectId: legacyProjectId
|
|
696
633
|
});
|
|
634
|
+
}
|
|
635
|
+
const nodeScope = await resolveTopicNodeScopeOrNull(ctx, legacyProjectId);
|
|
636
|
+
if (nodeScope) {
|
|
697
637
|
return {
|
|
698
|
-
...
|
|
699
|
-
|
|
700
|
-
workspaceId: topicScope.workspaceId
|
|
638
|
+
...nodeScope,
|
|
639
|
+
projectId: nodeScope.projectId ?? legacyProjectId
|
|
701
640
|
};
|
|
702
641
|
}
|
|
703
|
-
|
|
642
|
+
throw new Error(
|
|
643
|
+
`Legacy project scope ${legacyProjectId} has no mapped topic.`
|
|
644
|
+
);
|
|
704
645
|
}
|
|
705
|
-
function
|
|
706
|
-
|
|
707
|
-
|
|
646
|
+
async function resolveDirectLegacyProjectTopic(ctx, legacyProjectId) {
|
|
647
|
+
const directTopic = await tryReadTopicDoc(ctx, legacyProjectId, {
|
|
648
|
+
failureLog: "[topicScope] Failed to load direct project topic",
|
|
649
|
+
idLogKey: "projectId"
|
|
650
|
+
});
|
|
651
|
+
return directTopic ?? tryResolveHostTopicByLegacyScope(ctx, legacyProjectId);
|
|
652
|
+
}
|
|
653
|
+
async function tryReadTopicDoc(ctx, id, log) {
|
|
654
|
+
try {
|
|
655
|
+
return await ctx.db.get(id);
|
|
656
|
+
} catch (error) {
|
|
657
|
+
debugGraphPrimitiveFallback(log.failureLog, {
|
|
658
|
+
error,
|
|
659
|
+
[log.idLogKey]: id
|
|
660
|
+
});
|
|
661
|
+
return null;
|
|
708
662
|
}
|
|
663
|
+
}
|
|
664
|
+
async function buildTopicScope(ctx, topic, source, options = {}) {
|
|
665
|
+
const inherited = await resolveInheritedWorkspaceScope(ctx, topic);
|
|
666
|
+
const mapped = asMappedProjectId(topic);
|
|
709
667
|
return {
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
668
|
+
topicId: topic._id,
|
|
669
|
+
...mapped || options.fallbackProjectId ? { projectId: mapped ?? options.fallbackProjectId } : {},
|
|
670
|
+
tenantId: inherited.tenantId,
|
|
671
|
+
workspaceId: inherited.workspaceId,
|
|
672
|
+
source
|
|
713
673
|
};
|
|
714
674
|
}
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
const targetWorkspaceId = normalizeScopeValue2(args.target.workspaceId);
|
|
720
|
-
const targetLayer = typeof args.target.epistemicLayer === "string" ? args.target.epistemicLayer : void 0;
|
|
721
|
-
if (!targetWorkspaceId || targetLayer === "ontological") {
|
|
722
|
-
return;
|
|
723
|
-
}
|
|
724
|
-
throwWorkspaceIsolationError({
|
|
725
|
-
message: `Tenant-scoped pack "${args.runtime.packKey}" cannot mutate workspace-scoped reasoning state.`,
|
|
726
|
-
invariantCode: "workspace.tenant_pack_reasoning_write_forbidden",
|
|
727
|
-
suggestion: "Use a workspace-scoped pack for workspace-local graph mutations, or route the change through tenant-global canonical entity flows.",
|
|
728
|
-
details: {
|
|
729
|
-
mutationName: args.mutationName,
|
|
730
|
-
toolName: args.runtime.toolName,
|
|
731
|
-
packKey: args.runtime.packKey,
|
|
732
|
-
targetWorkspaceId,
|
|
733
|
-
targetNodeType: args.target.nodeType,
|
|
734
|
-
targetLayer
|
|
735
|
-
}
|
|
736
|
-
});
|
|
737
|
-
}
|
|
675
|
+
var optionalScopeArgs = {
|
|
676
|
+
projectId: v.optional(v.string()),
|
|
677
|
+
topicId: v.optional(v.string())
|
|
678
|
+
};
|
|
738
679
|
|
|
739
680
|
// src/epistemicQuestions.helpers.ts
|
|
740
681
|
function generateContentHash(text) {
|
|
741
682
|
const content = `question:${text.trim().toLowerCase().replace(/\s+/g, " ")}`;
|
|
742
683
|
let hash = 5381;
|
|
684
|
+
const maxSigned32Bit = 2147483647;
|
|
685
|
+
const uint32Size = 4294967296;
|
|
743
686
|
for (let i = 0; i < content.length; i++) {
|
|
744
|
-
hash = (hash
|
|
745
|
-
hash
|
|
687
|
+
hash = Math.imul(hash, 33) + content.charCodeAt(i);
|
|
688
|
+
hash %= uint32Size;
|
|
689
|
+
if (hash > maxSigned32Bit) {
|
|
690
|
+
hash -= uint32Size;
|
|
691
|
+
}
|
|
746
692
|
}
|
|
747
693
|
return Math.abs(hash).toString(16).padStart(8, "0");
|
|
748
694
|
}
|
|
749
695
|
async function markProjectGraphDirty(ctx, projectId, topicId) {
|
|
696
|
+
const markCacheStaleByTopic = internal.graphAnalysisCache.markCacheStaleByTopic;
|
|
750
697
|
const normalizedProjectId = typeof projectId === "string" && projectId.trim().length > 0 ? projectId : void 0;
|
|
751
698
|
const normalizedTopicId = typeof topicId === "string" && topicId.trim().length > 0 ? topicId : void 0;
|
|
752
|
-
if (!normalizedProjectId
|
|
699
|
+
if (!(normalizedProjectId || normalizedTopicId)) {
|
|
753
700
|
return;
|
|
754
701
|
}
|
|
755
702
|
if (normalizedProjectId) {
|
|
@@ -762,17 +709,17 @@ async function markProjectGraphDirty(ctx, projectId, topicId) {
|
|
|
762
709
|
);
|
|
763
710
|
}
|
|
764
711
|
if (normalizedTopicId) {
|
|
765
|
-
await ctx.scheduler.runAfter(
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
712
|
+
await ctx.scheduler.runAfter(0, markCacheStaleByTopic, {
|
|
713
|
+
topicId: normalizedTopicId
|
|
714
|
+
});
|
|
715
|
+
}
|
|
716
|
+
const resolvedProjectId = normalizedTopicId ?? normalizedProjectId;
|
|
717
|
+
if (!resolvedProjectId) {
|
|
718
|
+
return;
|
|
772
719
|
}
|
|
773
720
|
await resolveGraphPrimitivesAppResolvers().patchProject(
|
|
774
721
|
ctx,
|
|
775
|
-
|
|
722
|
+
resolvedProjectId,
|
|
776
723
|
{
|
|
777
724
|
lastActivityAt: Date.now()
|
|
778
725
|
}
|
|
@@ -798,7 +745,7 @@ function resolveQuestionScopeId(scope) {
|
|
|
798
745
|
return normalizeQuestionTopicId(scope.topicId) ?? scope.projectId ?? void 0;
|
|
799
746
|
}
|
|
800
747
|
async function resolveQuestionScopeOrNull(ctx, args) {
|
|
801
|
-
if (!args.projectId
|
|
748
|
+
if (!(args.projectId || args.topicId)) {
|
|
802
749
|
return null;
|
|
803
750
|
}
|
|
804
751
|
try {
|
|
@@ -854,7 +801,7 @@ function flattenQuestionNode(n) {
|
|
|
854
801
|
linkedBeliefId: meta.linkedBeliefNodeId || null,
|
|
855
802
|
testType: meta.testType || "validates",
|
|
856
803
|
importance: meta.importance || 5,
|
|
857
|
-
isKeyQuestion: meta.isKeyQuestion
|
|
804
|
+
isKeyQuestion: Boolean(meta.isKeyQuestion),
|
|
858
805
|
answer: meta.answer || null,
|
|
859
806
|
convictionStage: meta.convictionStage || null,
|
|
860
807
|
conviction: meta.conviction ?? null,
|
|
@@ -889,7 +836,7 @@ function getQuestionStatusCandidates(node) {
|
|
|
889
836
|
);
|
|
890
837
|
}
|
|
891
838
|
function isActiveQuestionNode(node) {
|
|
892
|
-
if (INACTIVE_NODE_STATUSES.has(node.status)) {
|
|
839
|
+
if (typeof node.status === "string" && INACTIVE_NODE_STATUSES.has(node.status)) {
|
|
893
840
|
return false;
|
|
894
841
|
}
|
|
895
842
|
return !getQuestionStatusCandidates(node).some(
|
|
@@ -902,12 +849,288 @@ function matchesRequestedQuestionStatus(node, requestedStatus) {
|
|
|
902
849
|
(status) => status.toLowerCase() === normalizedStatus
|
|
903
850
|
);
|
|
904
851
|
}
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
852
|
+
function normalizeScopeValue2(value) {
|
|
853
|
+
if (typeof value !== "string") {
|
|
854
|
+
return;
|
|
855
|
+
}
|
|
856
|
+
const normalized = value.trim();
|
|
857
|
+
return normalized.length > 0 ? normalized : void 0;
|
|
858
|
+
}
|
|
859
|
+
function throwWorkspaceIsolationError(args) {
|
|
860
|
+
const error = new Error(args.message);
|
|
861
|
+
error.status = 409;
|
|
862
|
+
error.code = "INVARIANT_VIOLATION";
|
|
863
|
+
error.invariantCode = args.invariantCode;
|
|
864
|
+
error.suggestion = args.suggestion;
|
|
865
|
+
error.details = args.details;
|
|
866
|
+
throw error;
|
|
867
|
+
}
|
|
868
|
+
async function resolveNodeScopeForWorkspaceIsolation(ctx, node) {
|
|
869
|
+
const epistemicLayer = typeof node?.epistemicLayer === "string" ? node.epistemicLayer : void 0;
|
|
870
|
+
const resolved = {
|
|
871
|
+
tenantId: normalizeScopeValue2(node?.tenantId),
|
|
872
|
+
workspaceId: normalizeScopeValue2(node?.workspaceId),
|
|
873
|
+
epistemicLayer,
|
|
874
|
+
nodeType: typeof node?.nodeType === "string" ? node.nodeType : void 0
|
|
875
|
+
};
|
|
876
|
+
if (!node) {
|
|
877
|
+
return resolved;
|
|
878
|
+
}
|
|
879
|
+
if (resolved.epistemicLayer === "ontological") {
|
|
880
|
+
return resolved;
|
|
881
|
+
}
|
|
882
|
+
if (resolved.tenantId || resolved.workspaceId) {
|
|
883
|
+
return resolved;
|
|
884
|
+
}
|
|
885
|
+
const topicId = normalizeScopeValue2(node.topicId);
|
|
886
|
+
if (topicId) {
|
|
887
|
+
const topicScope = await resolveTopicProjectScope(ctx, {
|
|
888
|
+
topicId
|
|
889
|
+
});
|
|
890
|
+
return {
|
|
891
|
+
...resolved,
|
|
892
|
+
tenantId: topicScope.tenantId,
|
|
893
|
+
workspaceId: topicScope.workspaceId
|
|
894
|
+
};
|
|
895
|
+
}
|
|
896
|
+
if (node.projectId) {
|
|
897
|
+
const topicScope = await resolveTopicProjectScope(ctx, {
|
|
898
|
+
projectId: String(node.projectId)
|
|
899
|
+
});
|
|
900
|
+
return {
|
|
901
|
+
...resolved,
|
|
902
|
+
tenantId: topicScope.tenantId,
|
|
903
|
+
workspaceId: topicScope.workspaceId
|
|
904
|
+
};
|
|
905
|
+
}
|
|
906
|
+
return resolved;
|
|
907
|
+
}
|
|
908
|
+
function resolveRuntimePackMutationContext(args) {
|
|
909
|
+
if (!(args.runtimeToolName || args.runtimePackKey || args.runtimePackInstallScope)) {
|
|
910
|
+
return;
|
|
911
|
+
}
|
|
912
|
+
return {
|
|
913
|
+
toolName: args.runtimeToolName,
|
|
914
|
+
packKey: args.runtimePackKey,
|
|
915
|
+
packInstallScope: args.runtimePackInstallScope
|
|
916
|
+
};
|
|
917
|
+
}
|
|
918
|
+
function assertTenantPackWorkspaceMutationAllowed(args) {
|
|
919
|
+
if (!args.runtime?.packKey || args.runtime.packInstallScope !== "tenant") {
|
|
920
|
+
return;
|
|
921
|
+
}
|
|
922
|
+
const targetWorkspaceId = normalizeScopeValue2(args.target.workspaceId);
|
|
923
|
+
const targetLayer = typeof args.target.epistemicLayer === "string" ? args.target.epistemicLayer : void 0;
|
|
924
|
+
if (!targetWorkspaceId || targetLayer === "ontological") {
|
|
925
|
+
return;
|
|
926
|
+
}
|
|
927
|
+
throwWorkspaceIsolationError({
|
|
928
|
+
message: `Tenant-scoped pack "${args.runtime.packKey}" cannot mutate workspace-scoped reasoning state.`,
|
|
929
|
+
invariantCode: "workspace.tenant_pack_reasoning_write_forbidden",
|
|
930
|
+
suggestion: "Use a workspace-scoped pack for workspace-local graph mutations, or route the change through tenant-global canonical entity flows.",
|
|
931
|
+
details: {
|
|
932
|
+
mutationName: args.mutationName,
|
|
933
|
+
toolName: args.runtime.toolName,
|
|
934
|
+
packKey: args.runtime.packKey,
|
|
935
|
+
targetWorkspaceId,
|
|
936
|
+
targetNodeType: args.target.nodeType,
|
|
937
|
+
targetLayer
|
|
938
|
+
}
|
|
939
|
+
});
|
|
908
940
|
}
|
|
909
941
|
|
|
910
942
|
// src/epistemicQuestions.tail.ts
|
|
943
|
+
var QUESTION_PRIORITY_ORDER = {
|
|
944
|
+
high: 0,
|
|
945
|
+
low: 2,
|
|
946
|
+
medium: 1
|
|
947
|
+
};
|
|
948
|
+
function readQuestionType(question, metadata) {
|
|
949
|
+
if (typeof question.questionType === "string") {
|
|
950
|
+
return question.questionType;
|
|
951
|
+
}
|
|
952
|
+
if (typeof metadata.questionType === "string") {
|
|
953
|
+
return metadata.questionType;
|
|
954
|
+
}
|
|
955
|
+
return;
|
|
956
|
+
}
|
|
957
|
+
function toRecord(value) {
|
|
958
|
+
if (typeof value === "object" && value !== null && !Array.isArray(value)) {
|
|
959
|
+
return value;
|
|
960
|
+
}
|
|
961
|
+
return {};
|
|
962
|
+
}
|
|
963
|
+
function readOptionalString(value) {
|
|
964
|
+
return typeof value === "string" && value.trim().length > 0 ? value : void 0;
|
|
965
|
+
}
|
|
966
|
+
function readStringValue(source, key) {
|
|
967
|
+
return readOptionalString(source[key]);
|
|
968
|
+
}
|
|
969
|
+
function readConvexId(value) {
|
|
970
|
+
const normalized = readOptionalString(value);
|
|
971
|
+
return normalized ? normalized : null;
|
|
972
|
+
}
|
|
973
|
+
function readStringArray2(value) {
|
|
974
|
+
return Array.isArray(value) ? value.filter((item) => typeof item === "string") : [];
|
|
975
|
+
}
|
|
976
|
+
function isEpistemicNodeDoc(value) {
|
|
977
|
+
return readConvexId(toRecord(value)._id) !== null;
|
|
978
|
+
}
|
|
979
|
+
function isQuestionNodeDoc(value) {
|
|
980
|
+
return isEpistemicNodeDoc(value) && readStringValue(toRecord(value), "nodeType") === "question";
|
|
981
|
+
}
|
|
982
|
+
function isBeliefNodeDoc(value) {
|
|
983
|
+
return isEpistemicNodeDoc(value) && readStringValue(toRecord(value), "nodeType") === "belief";
|
|
984
|
+
}
|
|
985
|
+
function readFinalArtifactRow(value) {
|
|
986
|
+
const record = toRecord(value);
|
|
987
|
+
const id = readConvexId(record._id);
|
|
988
|
+
if (!id) {
|
|
989
|
+
return null;
|
|
990
|
+
}
|
|
991
|
+
const row = { _id: id };
|
|
992
|
+
const projectId = readOptionalString(record.projectId);
|
|
993
|
+
if (projectId !== void 0) {
|
|
994
|
+
row.projectId = projectId;
|
|
995
|
+
}
|
|
996
|
+
const topicId = readOptionalString(record.topicId);
|
|
997
|
+
if (topicId !== void 0) {
|
|
998
|
+
row.topicId = topicId;
|
|
999
|
+
}
|
|
1000
|
+
return row;
|
|
1001
|
+
}
|
|
1002
|
+
function readQuestionPriority(metadata) {
|
|
1003
|
+
const value = metadata.priority;
|
|
1004
|
+
return value === "high" || value === "medium" || value === "low" ? value : "medium";
|
|
1005
|
+
}
|
|
1006
|
+
function loadQuestionNodes(ctx, questionIds) {
|
|
1007
|
+
return Promise.all(
|
|
1008
|
+
questionIds.map(async (questionId) => {
|
|
1009
|
+
try {
|
|
1010
|
+
const nodeId = readConvexId(questionId);
|
|
1011
|
+
return nodeId ? await ctx.db.get(nodeId) : null;
|
|
1012
|
+
} catch (error) {
|
|
1013
|
+
debugGraphPrimitiveFallback(
|
|
1014
|
+
"[epistemicQuestions] Failed to load original question",
|
|
1015
|
+
{
|
|
1016
|
+
error: formatGraphPrimitiveError(error),
|
|
1017
|
+
questionId
|
|
1018
|
+
}
|
|
1019
|
+
);
|
|
1020
|
+
return null;
|
|
1021
|
+
}
|
|
1022
|
+
})
|
|
1023
|
+
);
|
|
1024
|
+
}
|
|
1025
|
+
function questionBelongsToScope(question, scope) {
|
|
1026
|
+
if (scope.topicId) {
|
|
1027
|
+
return String(question.topicId) === String(scope.topicId) || !question.topicId && scope.projectId !== void 0 && String(question.projectId) === String(scope.projectId);
|
|
1028
|
+
}
|
|
1029
|
+
return String(question.projectId) === String(scope.projectId);
|
|
1030
|
+
}
|
|
1031
|
+
function consolidatedQuestionNodes(questions, scope, expectedCount) {
|
|
1032
|
+
const validQuestions = questions.filter(
|
|
1033
|
+
(question) => isQuestionNodeDoc(question) && questionBelongsToScope(question, scope)
|
|
1034
|
+
);
|
|
1035
|
+
if (validQuestions.length !== expectedCount) {
|
|
1036
|
+
throw new Error(
|
|
1037
|
+
"Some questions not found or do not belong to this topic scope"
|
|
1038
|
+
);
|
|
1039
|
+
}
|
|
1040
|
+
return validQuestions;
|
|
1041
|
+
}
|
|
1042
|
+
function chooseConsolidatedPriority(explicitPriority, questions) {
|
|
1043
|
+
if (explicitPriority) {
|
|
1044
|
+
return explicitPriority;
|
|
1045
|
+
}
|
|
1046
|
+
let bestPriority = "medium";
|
|
1047
|
+
let bestScore = QUESTION_PRIORITY_ORDER.low;
|
|
1048
|
+
for (const question of questions) {
|
|
1049
|
+
const priority = readQuestionPriority(toRecord(question.metadata));
|
|
1050
|
+
const score = QUESTION_PRIORITY_ORDER[priority];
|
|
1051
|
+
if (score < bestScore) {
|
|
1052
|
+
bestPriority = priority;
|
|
1053
|
+
bestScore = score;
|
|
1054
|
+
}
|
|
1055
|
+
}
|
|
1056
|
+
return bestPriority;
|
|
1057
|
+
}
|
|
1058
|
+
function collectLinkedBeliefIds(questions, explicitBeliefIds) {
|
|
1059
|
+
const linkedBeliefSet = /* @__PURE__ */ new Set();
|
|
1060
|
+
if (explicitBeliefIds) {
|
|
1061
|
+
for (const beliefId of explicitBeliefIds) {
|
|
1062
|
+
linkedBeliefSet.add(String(beliefId));
|
|
1063
|
+
}
|
|
1064
|
+
return linkedBeliefSet;
|
|
1065
|
+
}
|
|
1066
|
+
for (const question of questions) {
|
|
1067
|
+
const metadata = toRecord(question.metadata);
|
|
1068
|
+
const linkedBeliefNodeId = metadata.linkedBeliefNodeId;
|
|
1069
|
+
const linkedBeliefId = metadata.linkedBeliefId;
|
|
1070
|
+
if (linkedBeliefNodeId) {
|
|
1071
|
+
linkedBeliefSet.add(String(linkedBeliefNodeId));
|
|
1072
|
+
}
|
|
1073
|
+
if (linkedBeliefId) {
|
|
1074
|
+
linkedBeliefSet.add(String(linkedBeliefId));
|
|
1075
|
+
}
|
|
1076
|
+
}
|
|
1077
|
+
return linkedBeliefSet;
|
|
1078
|
+
}
|
|
1079
|
+
async function readConsolidatedQuestionCategory(ctx, linkedBeliefSet) {
|
|
1080
|
+
const firstBeliefId = Array.from(linkedBeliefSet)[0];
|
|
1081
|
+
if (!firstBeliefId) {
|
|
1082
|
+
return "other";
|
|
1083
|
+
}
|
|
1084
|
+
try {
|
|
1085
|
+
const beliefId = readConvexId(firstBeliefId);
|
|
1086
|
+
const belief = beliefId ? await ctx.db.get(beliefId) : null;
|
|
1087
|
+
const metadata = toRecord(belief?.metadata);
|
|
1088
|
+
return readStringValue(metadata, "category") ?? readStringValue(metadata, "pillar") ?? "other";
|
|
1089
|
+
} catch (error) {
|
|
1090
|
+
debugGraphPrimitiveFallback(
|
|
1091
|
+
"[epistemicQuestions] Failed to read first linked belief metadata",
|
|
1092
|
+
{
|
|
1093
|
+
beliefId: firstBeliefId,
|
|
1094
|
+
error: formatGraphPrimitiveError(error)
|
|
1095
|
+
}
|
|
1096
|
+
);
|
|
1097
|
+
return "other";
|
|
1098
|
+
}
|
|
1099
|
+
}
|
|
1100
|
+
async function archiveConsolidatedQuestions(ctx, questionIds, newNodeId, now) {
|
|
1101
|
+
for (const questionId of questionIds) {
|
|
1102
|
+
try {
|
|
1103
|
+
const nodeId = readConvexId(questionId);
|
|
1104
|
+
if (!nodeId) {
|
|
1105
|
+
continue;
|
|
1106
|
+
}
|
|
1107
|
+
const node = await ctx.db.get(nodeId);
|
|
1108
|
+
if (!node) {
|
|
1109
|
+
continue;
|
|
1110
|
+
}
|
|
1111
|
+
const metadata = toRecord(node.metadata);
|
|
1112
|
+
await ctx.db.patch(nodeId, {
|
|
1113
|
+
metadata: {
|
|
1114
|
+
...metadata,
|
|
1115
|
+
archivedAt: now,
|
|
1116
|
+
archivedReason: `Consolidated into question ${newNodeId}`,
|
|
1117
|
+
consolidatedIntoId: newNodeId,
|
|
1118
|
+
questionStatus: "archived"
|
|
1119
|
+
},
|
|
1120
|
+
status: "archived",
|
|
1121
|
+
updatedAt: now
|
|
1122
|
+
});
|
|
1123
|
+
} catch (error) {
|
|
1124
|
+
debugGraphPrimitiveFallback(
|
|
1125
|
+
"[epistemicQuestions] Failed to archive linked question",
|
|
1126
|
+
{
|
|
1127
|
+
error: formatGraphPrimitiveError(error),
|
|
1128
|
+
questionId
|
|
1129
|
+
}
|
|
1130
|
+
);
|
|
1131
|
+
}
|
|
1132
|
+
}
|
|
1133
|
+
}
|
|
911
1134
|
var list = query({
|
|
912
1135
|
args: {
|
|
913
1136
|
...optionalScopeArgs,
|
|
@@ -929,8 +1152,9 @@ var list = query({
|
|
|
929
1152
|
return [];
|
|
930
1153
|
}
|
|
931
1154
|
const nodes = await getQuestionNodesForScope(ctx, scope);
|
|
932
|
-
const
|
|
933
|
-
|
|
1155
|
+
const requestedStatus = args.status;
|
|
1156
|
+
const filtered = requestedStatus ? nodes.filter(
|
|
1157
|
+
(node) => matchesRequestedQuestionStatus(node, requestedStatus)
|
|
934
1158
|
) : nodes.filter(isActiveQuestionNode);
|
|
935
1159
|
return filtered.map(flattenInternalQuestionNode);
|
|
936
1160
|
}
|
|
@@ -961,7 +1185,10 @@ var getByPillar = query({
|
|
|
961
1185
|
(q) => q.eq("topicId", scope.topicId).eq("nodeType", "belief")
|
|
962
1186
|
).collect();
|
|
963
1187
|
const beliefs = allBeliefNodes.filter((n) => {
|
|
964
|
-
|
|
1188
|
+
if (!isBeliefNodeDoc(n)) {
|
|
1189
|
+
return false;
|
|
1190
|
+
}
|
|
1191
|
+
const meta = toRecord(n.metadata);
|
|
965
1192
|
return (meta.category === args.pillar || meta.topic === args.pillar || meta.pillar === args.pillar) && n.status !== "archived";
|
|
966
1193
|
});
|
|
967
1194
|
if (beliefs.length === 0) {
|
|
@@ -973,37 +1200,34 @@ var getByPillar = query({
|
|
|
973
1200
|
);
|
|
974
1201
|
const questionNodes = await getQuestionNodesForScope(ctx, scope);
|
|
975
1202
|
const pillarQuestions = questionNodes.filter((q) => {
|
|
976
|
-
const meta = q.metadata
|
|
977
|
-
const linkedId =
|
|
978
|
-
meta.linkedBeliefNodeId || meta.linkedBeliefId || meta.beliefId || ""
|
|
979
|
-
);
|
|
1203
|
+
const meta = toRecord(q.metadata);
|
|
1204
|
+
const linkedId = readOptionalString(meta.linkedBeliefNodeId) ?? readOptionalString(meta.linkedBeliefId) ?? readOptionalString(meta.beliefId);
|
|
980
1205
|
if (linkedId && beliefIds.has(linkedId)) {
|
|
981
1206
|
return true;
|
|
982
1207
|
}
|
|
983
|
-
|
|
984
|
-
|
|
1208
|
+
return readStringArray2(meta.relatedBeliefIds).some(
|
|
1209
|
+
(id) => beliefIds.has(id)
|
|
1210
|
+
);
|
|
985
1211
|
});
|
|
986
1212
|
const includeAnswered = args.includeAnswered ?? true;
|
|
987
1213
|
const filteredQuestions = includeAnswered ? pillarQuestions : pillarQuestions.filter((q) => {
|
|
988
|
-
const meta = q.metadata
|
|
1214
|
+
const meta = toRecord(q.metadata);
|
|
989
1215
|
return meta.questionStatus !== "answered";
|
|
990
1216
|
});
|
|
991
1217
|
const enrichedQuestions = filteredQuestions.map((q) => {
|
|
992
|
-
const meta = q.metadata
|
|
993
|
-
const linkedId =
|
|
994
|
-
meta.linkedBeliefNodeId || meta.linkedBeliefId || meta.beliefId || ""
|
|
995
|
-
);
|
|
1218
|
+
const meta = toRecord(q.metadata);
|
|
1219
|
+
const linkedId = readOptionalString(meta.linkedBeliefNodeId) ?? readOptionalString(meta.linkedBeliefId) ?? readOptionalString(meta.beliefId);
|
|
996
1220
|
return {
|
|
997
1221
|
_id: q._id,
|
|
998
1222
|
_creationTime: q._creationTime,
|
|
999
1223
|
projectId: q.projectId,
|
|
1000
1224
|
question: q.canonicalText,
|
|
1001
1225
|
canonicalText: q.canonicalText,
|
|
1002
|
-
category: meta.category
|
|
1003
|
-
priority: meta
|
|
1004
|
-
status: meta.questionStatus
|
|
1005
|
-
questionType: q.questionType
|
|
1006
|
-
beliefId: linkedId
|
|
1226
|
+
category: readOptionalString(meta.category) ?? "other",
|
|
1227
|
+
priority: readQuestionPriority(meta),
|
|
1228
|
+
status: readOptionalString(meta.questionStatus) ?? q.status ?? "open",
|
|
1229
|
+
questionType: q.questionType ?? readOptionalString(meta.questionType) ?? "general",
|
|
1230
|
+
beliefId: linkedId,
|
|
1007
1231
|
beliefText: linkedId ? beliefMap.get(linkedId) : void 0,
|
|
1008
1232
|
createdAt: q.createdAt,
|
|
1009
1233
|
createdBy: q.createdBy,
|
|
@@ -1021,15 +1245,15 @@ var getByPillar = query({
|
|
|
1021
1245
|
if (aPriority !== bPriority) {
|
|
1022
1246
|
return aPriority - bPriority;
|
|
1023
1247
|
}
|
|
1024
|
-
return (b.createdAt
|
|
1248
|
+
return (b.createdAt ?? 0) - (a.createdAt ?? 0);
|
|
1025
1249
|
});
|
|
1026
1250
|
return {
|
|
1027
1251
|
questions: enrichedQuestions,
|
|
1028
1252
|
beliefs: beliefs.map((b) => ({
|
|
1029
1253
|
_id: b._id,
|
|
1030
1254
|
belief: b.canonicalText,
|
|
1031
|
-
pillar: b.metadata
|
|
1032
|
-
confidence: b.metadata
|
|
1255
|
+
pillar: readStringValue(toRecord(b.metadata), "category") || readStringValue(toRecord(b.metadata), "pillar"),
|
|
1256
|
+
confidence: toRecord(b.metadata).confidence
|
|
1033
1257
|
}))
|
|
1034
1258
|
};
|
|
1035
1259
|
}
|
|
@@ -1065,88 +1289,23 @@ var consolidate = mutation({
|
|
|
1065
1289
|
if (args.questionIds.length < 2) {
|
|
1066
1290
|
throw new Error("At least 2 questions required for consolidation");
|
|
1067
1291
|
}
|
|
1068
|
-
const
|
|
1069
|
-
args.questionIds
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1292
|
+
const validQuestions = consolidatedQuestionNodes(
|
|
1293
|
+
await loadQuestionNodes(ctx, args.questionIds),
|
|
1294
|
+
scope,
|
|
1295
|
+
args.questionIds.length
|
|
1296
|
+
);
|
|
1297
|
+
const bestPriority = chooseConsolidatedPriority(
|
|
1298
|
+
args.priority,
|
|
1299
|
+
validQuestions
|
|
1300
|
+
);
|
|
1301
|
+
const linkedBeliefSet = collectLinkedBeliefIds(
|
|
1302
|
+
validQuestions,
|
|
1303
|
+
args.linkedBeliefIds
|
|
1304
|
+
);
|
|
1305
|
+
const category = await readConsolidatedQuestionCategory(
|
|
1306
|
+
ctx,
|
|
1307
|
+
linkedBeliefSet
|
|
1083
1308
|
);
|
|
1084
|
-
const validQuestions = originalQuestions.filter((q) => {
|
|
1085
|
-
if (!q || q.nodeType !== "question") {
|
|
1086
|
-
return false;
|
|
1087
|
-
}
|
|
1088
|
-
if (scope.topicId) {
|
|
1089
|
-
return String(q.topicId) === String(scope.topicId) || !q.topicId && scope.projectId !== void 0 && String(q.projectId) === String(scope.projectId);
|
|
1090
|
-
}
|
|
1091
|
-
return String(q.projectId) === String(scope.projectId);
|
|
1092
|
-
});
|
|
1093
|
-
if (validQuestions.length !== args.questionIds.length) {
|
|
1094
|
-
throw new Error(
|
|
1095
|
-
"Some questions not found or do not belong to this topic scope"
|
|
1096
|
-
);
|
|
1097
|
-
}
|
|
1098
|
-
const priorityOrder = {
|
|
1099
|
-
high: 0,
|
|
1100
|
-
medium: 1,
|
|
1101
|
-
low: 2
|
|
1102
|
-
};
|
|
1103
|
-
let bestPriority = args.priority;
|
|
1104
|
-
if (!bestPriority) {
|
|
1105
|
-
let bestScore = 2;
|
|
1106
|
-
for (const q of validQuestions) {
|
|
1107
|
-
const meta = q?.metadata || {};
|
|
1108
|
-
const score = priorityOrder[meta.priority || "medium"] ?? 1;
|
|
1109
|
-
if (score < bestScore) {
|
|
1110
|
-
bestScore = score;
|
|
1111
|
-
bestPriority = meta.priority || "medium";
|
|
1112
|
-
}
|
|
1113
|
-
}
|
|
1114
|
-
}
|
|
1115
|
-
const linkedBeliefSet = /* @__PURE__ */ new Set();
|
|
1116
|
-
if (args.linkedBeliefIds) {
|
|
1117
|
-
for (const bid of args.linkedBeliefIds) {
|
|
1118
|
-
linkedBeliefSet.add(String(bid));
|
|
1119
|
-
}
|
|
1120
|
-
} else {
|
|
1121
|
-
for (const q of validQuestions) {
|
|
1122
|
-
const meta = q?.metadata || {};
|
|
1123
|
-
if (meta.linkedBeliefNodeId) {
|
|
1124
|
-
linkedBeliefSet.add(String(meta.linkedBeliefNodeId));
|
|
1125
|
-
}
|
|
1126
|
-
if (meta.linkedBeliefId) {
|
|
1127
|
-
linkedBeliefSet.add(String(meta.linkedBeliefId));
|
|
1128
|
-
}
|
|
1129
|
-
}
|
|
1130
|
-
}
|
|
1131
|
-
let category = "other";
|
|
1132
|
-
if (linkedBeliefSet.size > 0) {
|
|
1133
|
-
const firstBeliefId = Array.from(linkedBeliefSet)[0];
|
|
1134
|
-
try {
|
|
1135
|
-
const belief = await ctx.db.get(firstBeliefId);
|
|
1136
|
-
if (belief) {
|
|
1137
|
-
const bMeta = belief.metadata || {};
|
|
1138
|
-
category = bMeta.category || bMeta.pillar || "other";
|
|
1139
|
-
}
|
|
1140
|
-
} catch (error) {
|
|
1141
|
-
debugGraphPrimitiveFallback(
|
|
1142
|
-
"[epistemicQuestions] Failed to read first linked belief metadata",
|
|
1143
|
-
{
|
|
1144
|
-
error: formatGraphPrimitiveError(error),
|
|
1145
|
-
beliefId: firstBeliefId
|
|
1146
|
-
}
|
|
1147
|
-
);
|
|
1148
|
-
}
|
|
1149
|
-
}
|
|
1150
1309
|
const now = Date.now();
|
|
1151
1310
|
const globalId = generateGlobalId();
|
|
1152
1311
|
const contentHash = generateContentHash(args.consolidatedQuestion);
|
|
@@ -1165,7 +1324,7 @@ var consolidate = mutation({
|
|
|
1165
1324
|
createdBy: args.consolidatedBy,
|
|
1166
1325
|
metadata: {
|
|
1167
1326
|
category,
|
|
1168
|
-
priority: bestPriority
|
|
1327
|
+
priority: bestPriority,
|
|
1169
1328
|
source: "consolidated",
|
|
1170
1329
|
questionStatus: "open",
|
|
1171
1330
|
linkedBeliefNodeId: linkedBeliefSet.size > 0 ? Array.from(linkedBeliefSet)[0] : void 0,
|
|
@@ -1177,33 +1336,7 @@ var consolidate = mutation({
|
|
|
1177
1336
|
}
|
|
1178
1337
|
}
|
|
1179
1338
|
});
|
|
1180
|
-
|
|
1181
|
-
try {
|
|
1182
|
-
const node = await ctx.db.get(qid);
|
|
1183
|
-
if (node) {
|
|
1184
|
-
const meta = node.metadata || {};
|
|
1185
|
-
await ctx.db.patch(qid, {
|
|
1186
|
-
status: "archived",
|
|
1187
|
-
updatedAt: now,
|
|
1188
|
-
metadata: {
|
|
1189
|
-
...meta,
|
|
1190
|
-
questionStatus: "archived",
|
|
1191
|
-
archivedAt: now,
|
|
1192
|
-
archivedReason: `Consolidated into question ${newNodeId}`,
|
|
1193
|
-
consolidatedIntoId: newNodeId
|
|
1194
|
-
}
|
|
1195
|
-
});
|
|
1196
|
-
}
|
|
1197
|
-
} catch (error) {
|
|
1198
|
-
debugGraphPrimitiveFallback(
|
|
1199
|
-
"[epistemicQuestions] Failed to archive linked question",
|
|
1200
|
-
{
|
|
1201
|
-
error: formatGraphPrimitiveError(error),
|
|
1202
|
-
questionId: qid
|
|
1203
|
-
}
|
|
1204
|
-
);
|
|
1205
|
-
}
|
|
1206
|
-
}
|
|
1339
|
+
await archiveConsolidatedQuestions(ctx, args.questionIds, newNodeId, now);
|
|
1207
1340
|
await ctx.scheduler.runAfter(0, internal.neo4jSync.syncNodeToNeo4j, {
|
|
1208
1341
|
nodeId: newNodeId,
|
|
1209
1342
|
operation: "upsert"
|
|
@@ -1233,7 +1366,7 @@ var deleteQuestion = mutation({
|
|
|
1233
1366
|
returns: permissiveReturn,
|
|
1234
1367
|
handler: async (ctx, args) => {
|
|
1235
1368
|
const node = await ctx.db.get(args.questionId);
|
|
1236
|
-
if (!node
|
|
1369
|
+
if (!isQuestionNodeDoc(node)) {
|
|
1237
1370
|
throw new Error("Question not found");
|
|
1238
1371
|
}
|
|
1239
1372
|
assertTenantPackWorkspaceMutationAllowed({
|
|
@@ -1242,7 +1375,7 @@ var deleteQuestion = mutation({
|
|
|
1242
1375
|
mutationName: "epistemicQuestions.deleteQuestion"
|
|
1243
1376
|
});
|
|
1244
1377
|
const now = Date.now();
|
|
1245
|
-
const meta = node.metadata
|
|
1378
|
+
const meta = toRecord(node.metadata);
|
|
1246
1379
|
await ctx.db.patch(args.questionId, {
|
|
1247
1380
|
status: "archived",
|
|
1248
1381
|
updatedAt: now,
|
|
@@ -1283,16 +1416,22 @@ var markAnsweredWithArtifact = mutation({
|
|
|
1283
1416
|
returns: permissiveReturn,
|
|
1284
1417
|
handler: async (ctx, args) => {
|
|
1285
1418
|
const node = await ctx.db.get(args.questionId);
|
|
1286
|
-
if (!node
|
|
1419
|
+
if (!isQuestionNodeDoc(node)) {
|
|
1287
1420
|
throw new Error("Question not found");
|
|
1288
1421
|
}
|
|
1289
|
-
const artifact =
|
|
1422
|
+
const artifact = readFinalArtifactRow(
|
|
1423
|
+
await resolveGraphPrimitivesAppResolvers().getFinalArtifact(
|
|
1424
|
+
ctx,
|
|
1425
|
+
args.artifactId
|
|
1426
|
+
)
|
|
1427
|
+
);
|
|
1290
1428
|
if (!artifact) {
|
|
1291
1429
|
throw new Error("Artifact not found");
|
|
1292
1430
|
}
|
|
1431
|
+
const nodeTopicId = normalizeQuestionTopicId(node.topicId);
|
|
1293
1432
|
const scopeId = resolveQuestionScopeId({
|
|
1294
|
-
projectId: node.projectId,
|
|
1295
|
-
topicId:
|
|
1433
|
+
...node.projectId ? { projectId: node.projectId } : {},
|
|
1434
|
+
...nodeTopicId ? { topicId: nodeTopicId } : {}
|
|
1296
1435
|
});
|
|
1297
1436
|
if (scopeId) {
|
|
1298
1437
|
const hasAccess = await checkScopeAccess(ctx, scopeId, args.userId);
|
|
@@ -1301,7 +1440,7 @@ var markAnsweredWithArtifact = mutation({
|
|
|
1301
1440
|
}
|
|
1302
1441
|
}
|
|
1303
1442
|
const now = Date.now();
|
|
1304
|
-
const meta = node.metadata
|
|
1443
|
+
const meta = toRecord(node.metadata);
|
|
1305
1444
|
await ctx.db.patch(args.questionId, {
|
|
1306
1445
|
updatedAt: now,
|
|
1307
1446
|
metadata: {
|
|
@@ -1352,10 +1491,10 @@ var getQuestionClusterPositions = query({
|
|
|
1352
1491
|
}
|
|
1353
1492
|
};
|
|
1354
1493
|
}
|
|
1355
|
-
const questionNodes = await ctx.db.query("epistemicNodes").withIndex(
|
|
1494
|
+
const questionNodes = (await ctx.db.query("epistemicNodes").withIndex(
|
|
1356
1495
|
"by_topic_type",
|
|
1357
1496
|
(q) => q.eq("topicId", scope.topicId).eq("nodeType", "question")
|
|
1358
|
-
).collect();
|
|
1497
|
+
).collect()).filter(isQuestionNodeDoc);
|
|
1359
1498
|
const activeQuestionNodes = questionNodes.filter(isActiveQuestionNode);
|
|
1360
1499
|
const positions = {};
|
|
1361
1500
|
let clusterCount = 0;
|
|
@@ -1363,8 +1502,8 @@ var getQuestionClusterPositions = query({
|
|
|
1363
1502
|
let baselineCount = 0;
|
|
1364
1503
|
for (const question of activeQuestionNodes) {
|
|
1365
1504
|
const id = question._id.toString();
|
|
1366
|
-
const meta = question.metadata
|
|
1367
|
-
const questionType =
|
|
1505
|
+
const meta = toRecord(question.metadata);
|
|
1506
|
+
const questionType = readQuestionType(question, meta);
|
|
1368
1507
|
if (questionType === "belief_test" || meta.testType) {
|
|
1369
1508
|
positions[id] = "cluster";
|
|
1370
1509
|
clusterCount++;
|