@lucern/graph-primitives 1.0.50 → 1.0.53

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.
Files changed (67) hide show
  1. package/dist/beliefDecay.js +34 -186
  2. package/dist/beliefEvidenceLinks.js +32 -187
  3. package/dist/contradictions.js +34 -187
  4. package/dist/entityLifecycle.js +88 -205
  5. package/dist/epistemicAnswers.js +35 -187
  6. package/dist/epistemicBeliefs.admin.js +34 -187
  7. package/dist/epistemicBeliefs.backfills.js +34 -188
  8. package/dist/epistemicBeliefs.confidence.d.ts +1 -1
  9. package/dist/epistemicBeliefs.confidence.js +32 -188
  10. package/dist/epistemicBeliefs.core.js +37 -187
  11. package/dist/epistemicBeliefs.d.ts +1 -1
  12. package/dist/epistemicBeliefs.helpers.d.ts +1 -1
  13. package/dist/epistemicBeliefs.helpers.js +34 -186
  14. package/dist/epistemicBeliefs.internal.js +37 -187
  15. package/dist/epistemicBeliefs.js +37 -187
  16. package/dist/epistemicBeliefs.lifecycle.js +32 -188
  17. package/dist/epistemicBeliefs.links.js +34 -188
  18. package/dist/epistemicContracts.evaluators.js +34 -188
  19. package/dist/epistemicContracts.handlers.js +34 -188
  20. package/dist/epistemicContracts.js +34 -188
  21. package/dist/epistemicEdges.d.ts +1 -1
  22. package/dist/epistemicEdges.helpers.d.ts +1 -1
  23. package/dist/epistemicEdges.js +32 -187
  24. package/dist/epistemicEdges.mutations.js +34 -186
  25. package/dist/epistemicEdges.queries.js +35 -188
  26. package/dist/epistemicEdges.types.d.ts +1 -1
  27. package/dist/epistemicEvidence.d.ts +1 -1
  28. package/dist/epistemicEvidence.js +37 -187
  29. package/dist/epistemicEvidenceHelpers.d.ts +1 -1
  30. package/dist/epistemicEvidenceHelpers.js +34 -186
  31. package/dist/epistemicEvidenceMutations.js +37 -187
  32. package/dist/epistemicEvidenceQueries.js +34 -186
  33. package/dist/epistemicHelpers.js +4 -1
  34. package/dist/epistemicInsert.js +4 -1
  35. package/dist/epistemicNodeCreation.js +4 -1
  36. package/dist/epistemicNodes.helpers.d.ts +1 -1
  37. package/dist/epistemicNodes.internal.js +35 -188
  38. package/dist/epistemicNodes.js +37 -188
  39. package/dist/epistemicNodes.mutations.js +35 -188
  40. package/dist/epistemicNodes.queries.js +35 -188
  41. package/dist/epistemicQuestions.conviction.js +34 -186
  42. package/dist/epistemicQuestions.create.js +37 -187
  43. package/dist/epistemicQuestions.d.ts +1 -1
  44. package/dist/epistemicQuestions.evidence.js +37 -187
  45. package/dist/epistemicQuestions.helpers.d.ts +1 -1
  46. package/dist/epistemicQuestions.helpers.js +34 -186
  47. package/dist/epistemicQuestions.js +37 -187
  48. package/dist/epistemicQuestions.lifecycle.js +34 -186
  49. package/dist/epistemicQuestions.queries.js +34 -186
  50. package/dist/epistemicQuestions.sprint.js +35 -188
  51. package/dist/epistemicQuestions.tail.js +37 -187
  52. package/dist/epistemicSources.js +35 -188
  53. package/dist/index.d.ts +1 -1
  54. package/dist/index.js +98 -213
  55. package/dist/proof-attestation.json +1 -1
  56. package/dist/questionEvidenceLinks.js +34 -187
  57. package/dist/scopeResolverCompat.d.ts +1 -1
  58. package/dist/scopeResolverCompat.js +32 -193
  59. package/dist/topicOntologyResolver.d.ts +3 -3
  60. package/dist/topicOntologyResolver.js +57 -18
  61. package/dist/{topicScope-DJVa0mLa.d.ts → topicScope-CL1IVOmv.d.ts} +2 -2
  62. package/dist/topicScope.d.ts +1 -1
  63. package/dist/topicScope.js +32 -193
  64. package/dist/workflowBridge.js +32 -193
  65. package/dist/workspaceIsolation.d.ts +1 -1
  66. package/dist/workspaceIsolation.js +32 -193
  67. package/package.json +4 -4
@@ -8,8 +8,8 @@ import { componentsGeneric, internalQueryGeneric, internalMutationGeneric } from
8
8
  import '@lucern/access-control/auth';
9
9
  import { throwStructuredMutationError } from '@lucern/access-control/structuredMutationError';
10
10
  import { normalizeTupleContradictionPolicy, confidenceFromSL } from '@lucern/confidence';
11
+ import { generateGlobalId, assertUuidV7Identity, assertUuidV7Reference, generateUuidV7, assertStorageEdgeVocabulary, isUuidV7, assertUuidShapedEdgeEndpoint } from '@lucern/contracts/ids';
11
12
  import { isNodeType, getLayerForNodeType } from '@lucern/contracts/schema-helpers/spine/tables/epistemicNodes';
12
- import { generateGlobalId, assertUuidV7Identity, generateUuidV7, assertStorageEdgeVocabulary, isUuidV7, assertUuidShapedEdgeEndpoint } from '@lucern/contracts/ids';
13
13
  import { assertEdgePolicyAllowed } from '@lucern/contracts/manifests/edge-policy-manifest';
14
14
  import { edgePolicyManifest } from '@lucern/contracts/manifests/edge-policy-manifest.data';
15
15
 
@@ -520,6 +520,8 @@ function resolveGraphPrimitivesAppResolvers(_ctx) {
520
520
  ...resolverOverrides
521
521
  };
522
522
  }
523
+
524
+ // src/topicScope.ts
523
525
  var LEGACY_SCOPE_FIELD2 = "graphScopeProjectId";
524
526
  async function resolveTopicNodeScopeOrNull(ctx, ref) {
525
527
  if (!ctx?.db || typeof ctx.db.query !== "function") {
@@ -540,16 +542,36 @@ async function resolveTopicNodeScopeOrNull(ctx, ref) {
540
542
  if (!node) {
541
543
  return null;
542
544
  }
543
- const scopeKey = normalizeScopeValue(node.topicId) ?? normalizeScopeValue(node.globalId);
545
+ const scopeKey = canonicalTopicGlobalId(node);
544
546
  if (!scopeKey) {
545
- return null;
547
+ throw new Error(
548
+ `topic.uuidv7_identity_required: topic node ${ref} is missing a UUIDv7 globalId.`
549
+ );
546
550
  }
551
+ const metadata = node.metadata ?? {};
547
552
  return {
548
553
  topicId: scopeKey,
549
554
  projectId: asMappedProjectId(node),
550
- source: "topic_node"
555
+ source: "topic_node",
556
+ tenantId: normalizeScopeValue(node.tenantId) ?? normalizeScopeValue(metadata.tenantId),
557
+ workspaceId: normalizeScopeValue(node.workspaceId) ?? normalizeScopeValue(metadata.workspaceId)
551
558
  };
552
559
  }
560
+ function canonicalTopicGlobalId(node) {
561
+ const globalId = normalizeScopeValue(node.globalId);
562
+ if (globalId && isUuidV7(globalId)) {
563
+ return globalId;
564
+ }
565
+ const topicId = normalizeScopeValue(node.topicId);
566
+ return topicId && isUuidV7(topicId) ? topicId : null;
567
+ }
568
+ function requireUuidV7TopicScope(field, value) {
569
+ if (!isUuidV7(value)) {
570
+ throw new Error(
571
+ `topic.uuidv7_required: ${field} must be a UUIDv7 public topic identifier.`
572
+ );
573
+ }
574
+ }
553
575
  function asMappedProjectId(topic) {
554
576
  if (!topic) {
555
577
  return;
@@ -571,200 +593,25 @@ function normalizeScopeValue(value) {
571
593
  const normalized = value.trim();
572
594
  return normalized.length > 0 ? normalized : void 0;
573
595
  }
574
- function pickPrimaryTopic(candidates) {
575
- return [...candidates].sort((a, b) => {
576
- const depthA = a.depth ?? 9999;
577
- const depthB = b.depth ?? 9999;
578
- if (depthA !== depthB) {
579
- return depthA - depthB;
580
- }
581
- const createdA = a.createdAt ?? Number.MAX_SAFE_INTEGER;
582
- const createdB = b.createdAt ?? Number.MAX_SAFE_INTEGER;
583
- if (createdA !== createdB) {
584
- return createdA - createdB;
585
- }
586
- return String(a.name || "").localeCompare(String(b.name || ""));
587
- })[0];
588
- }
589
- async function findTopicsByScopeAlias(ctx, scopeId) {
590
- const query = ctx.db.query("topics");
591
- try {
592
- return await query.withIndex(
593
- "by_graph_scope_project",
594
- (q) => q.eq(LEGACY_SCOPE_FIELD2, scopeId)
595
- ).collect();
596
- } catch (error) {
597
- debugGraphPrimitiveFallback(
598
- "[topicScope] Failed to resolve scope alias via index",
599
- {
600
- error,
601
- scopeId
602
- }
603
- );
604
- const topics = await query.collect();
605
- return topics.filter((topic) => {
606
- const normalizedGlobalId = normalizeScopeValue(topic.globalId);
607
- const mappedProjectId = asMappedProjectId(topic);
608
- return String(topic._id) === scopeId || normalizedGlobalId === scopeId || mappedProjectId === scopeId;
609
- });
610
- }
611
- }
612
- async function tryResolveHostTopicById(ctx, topicId) {
613
- if (typeof ctx.runQuery !== "function") {
614
- return null;
615
- }
616
- try {
617
- return await ctx.runQuery(api.topics.get, {
618
- id: topicId
619
- }) ?? null;
620
- } catch (error) {
621
- debugGraphPrimitiveFallback(
622
- "[topicScope] Failed to resolve topic by host query",
623
- {
624
- error,
625
- topicId
626
- }
627
- );
628
- return null;
629
- }
630
- }
631
- async function tryResolveHostTopicByLegacyScope(ctx, legacyScopeId) {
632
- if (typeof ctx.runQuery !== "function") {
633
- return null;
634
- }
635
- try {
636
- return await ctx.runQuery(api.topics.getByLegacyScopeId, {
637
- projectId: legacyScopeId
638
- }) ?? null;
639
- } catch (error) {
640
- debugGraphPrimitiveFallback(
641
- "[topicScope] Failed to resolve topic by legacy scope",
642
- {
643
- error,
644
- legacyScopeId
645
- }
646
- );
647
- return null;
648
- }
649
- }
650
- async function resolveInheritedWorkspaceScope(ctx, topic) {
651
- const MAX_DEPTH = 10;
652
- let tenantId = normalizeScopeValue(topic.tenantId);
653
- let workspaceId = normalizeScopeValue(topic.workspaceId);
654
- if (tenantId && workspaceId) {
655
- return { tenantId, workspaceId };
656
- }
657
- let current = topic;
658
- for (let i = 0; i < MAX_DEPTH && current?.parentTopicId; i++) {
659
- current = await ctx.db.get(current.parentTopicId);
660
- if (!current) {
661
- break;
662
- }
663
- if (!tenantId) {
664
- tenantId = normalizeScopeValue(current.tenantId);
665
- }
666
- if (!workspaceId) {
667
- workspaceId = normalizeScopeValue(current.workspaceId);
668
- }
669
- if (tenantId && workspaceId) {
670
- break;
671
- }
672
- }
673
- return { tenantId, workspaceId };
674
- }
675
596
  async function resolveTopicProjectScope(ctx, args) {
676
597
  if (args.topicId) {
677
598
  return await resolveScopeFromTopicId(ctx, args.topicId);
678
599
  }
679
600
  if (args.projectId) {
680
- return await resolveScopeFromLegacyProjectId(ctx, args.projectId);
601
+ throw new Error(
602
+ "topic.uuidv7_required: projectId scope aliases are retired; pass topicId as the UUIDv7 topic globalId."
603
+ );
681
604
  }
682
- throw new Error(
683
- "Missing scope: provide topicId (preferred) or legacy projectId alias."
684
- );
605
+ throw new Error("topic.uuidv7_required: Missing scope: provide topicId.");
685
606
  }
686
607
  async function resolveScopeFromTopicId(ctx, topicId) {
687
- const topic = await resolveTopicDocFromTopicId(ctx, topicId);
688
- if (topic) {
689
- return await buildTopicScope(ctx, topic, "topic");
690
- }
691
- const nodeScope = await resolveTopicNodeScopeOrNull(ctx, String(topicId));
608
+ const topicGlobalId = String(topicId);
609
+ requireUuidV7TopicScope("topicId", topicGlobalId);
610
+ const nodeScope = await resolveTopicNodeScopeOrNull(ctx, topicGlobalId);
692
611
  if (nodeScope) {
693
612
  return nodeScope;
694
613
  }
695
- throw new Error(`Topic not found: ${String(topicId)}`);
696
- }
697
- async function resolveTopicDocFromTopicId(ctx, topicId) {
698
- const direct = await tryReadTopicDoc(ctx, topicId, {
699
- failureLog: "[topicScope] Failed to load topic by direct id",
700
- idLogKey: "topicId"
701
- });
702
- if (direct) {
703
- return direct;
704
- }
705
- const hostTopic = await tryResolveHostTopicById(ctx, String(topicId));
706
- if (hostTopic) {
707
- return hostTopic;
708
- }
709
- return pickPrimaryTopic(await findTopicsByScopeAlias(ctx, String(topicId))) ?? null;
710
- }
711
- async function resolveScopeFromLegacyProjectId(ctx, legacyProjectId) {
712
- const directTopic = await resolveDirectLegacyProjectTopic(
713
- ctx,
714
- legacyProjectId
715
- );
716
- if (directTopic) {
717
- return await buildTopicScope(ctx, directTopic, "topic_inferred", {
718
- fallbackProjectId: legacyProjectId
719
- });
720
- }
721
- const primary = pickPrimaryTopic(
722
- await findTopicsByScopeAlias(ctx, legacyProjectId)
723
- );
724
- if (primary) {
725
- return await buildTopicScope(ctx, primary, "project_mapped_topic", {
726
- fallbackProjectId: legacyProjectId
727
- });
728
- }
729
- const nodeScope = await resolveTopicNodeScopeOrNull(ctx, legacyProjectId);
730
- if (nodeScope) {
731
- return {
732
- ...nodeScope,
733
- projectId: nodeScope.projectId ?? legacyProjectId
734
- };
735
- }
736
- throw new Error(
737
- `Legacy project scope ${legacyProjectId} has no mapped topic.`
738
- );
739
- }
740
- async function resolveDirectLegacyProjectTopic(ctx, legacyProjectId) {
741
- const directTopic = await tryReadTopicDoc(ctx, legacyProjectId, {
742
- failureLog: "[topicScope] Failed to load direct project topic",
743
- idLogKey: "projectId"
744
- });
745
- return directTopic ?? tryResolveHostTopicByLegacyScope(ctx, legacyProjectId);
746
- }
747
- async function tryReadTopicDoc(ctx, id, log) {
748
- try {
749
- return await ctx.db.get(id);
750
- } catch (error) {
751
- debugGraphPrimitiveFallback(log.failureLog, {
752
- error,
753
- [log.idLogKey]: id
754
- });
755
- return null;
756
- }
757
- }
758
- async function buildTopicScope(ctx, topic, source, options = {}) {
759
- const inherited = await resolveInheritedWorkspaceScope(ctx, topic);
760
- const mapped = asMappedProjectId(topic);
761
- return {
762
- topicId: topic._id,
763
- ...mapped || options.fallbackProjectId ? { projectId: mapped ?? options.fallbackProjectId } : {},
764
- tenantId: inherited.tenantId,
765
- workspaceId: inherited.workspaceId,
766
- source
767
- };
614
+ throw new Error(`Topic not found: ${topicGlobalId}`);
768
615
  }
769
616
  var optionalScopeArgs = {
770
617
  projectId: v.optional(v.string()),
@@ -1212,6 +1059,9 @@ function resolveBeliefStatus(node, metadata) {
1212
1059
  }
1213
1060
  function insertEpistemicNode(ctx, doc) {
1214
1061
  assertUuidV7Identity("epistemicNodes", doc.globalId);
1062
+ if (doc.topicId !== void 0 && doc.topicId !== null) {
1063
+ assertUuidV7Reference("epistemicNodes.topicId", doc.topicId);
1064
+ }
1215
1065
  return ctx.db.insert("epistemicNodes", doc);
1216
1066
  }
1217
1067
  async function assertExistingNodeEndpoint(ctx, endpointRole, endpoint) {
@@ -7,8 +7,8 @@ import { canAudienceClassAccess, normalizeAudienceKey, classFromAudienceKey } fr
7
7
  import { getCurrentUserId } from '@lucern/access-control/auth';
8
8
  import { throwStructuredMutationError } from '@lucern/access-control/structuredMutationError';
9
9
  import { normalizeTupleContradictionPolicy, mkOpinion, createInheritedContractRecord, readOpinionFromRecord, confidenceFromSL, conditionalDeduction, project, dampedDependencyCascade, trustDiscount, applyNegativeSupport, cumulativeFusion, applyNegativeEvidence, detectTupleContradiction, evaluateTupleContradictionTransition, hasProjectedOpinionChanged } from '@lucern/confidence';
10
+ import { generateUuidV7, generateGlobalId, assertUuidV7Identity, assertUuidV7Reference, assertStorageEdgeVocabulary, isUuidV7, assertUuidShapedEdgeEndpoint } from '@lucern/contracts/ids';
10
11
  import { isNodeType, getLayerForNodeType } from '@lucern/contracts/schema-helpers/spine/tables/epistemicNodes';
11
- import { generateUuidV7, generateGlobalId, assertUuidV7Identity, assertStorageEdgeVocabulary, isUuidV7, assertUuidShapedEdgeEndpoint } from '@lucern/contracts/ids';
12
12
  import { assertSchemaEnumValue } from '@lucern/contracts/schema-helpers/enumValidation';
13
13
  import { assertEdgePolicyAllowed } from '@lucern/contracts/manifests/edge-policy-manifest';
14
14
  import { edgePolicyManifest } from '@lucern/contracts/manifests/edge-policy-manifest.data';
@@ -533,6 +533,8 @@ function resolveGraphPrimitivesAppResolvers(_ctx) {
533
533
  ...resolverOverrides
534
534
  };
535
535
  }
536
+
537
+ // src/topicScope.ts
536
538
  var LEGACY_SCOPE_FIELD2 = "graphScopeProjectId";
537
539
  async function resolveTopicNodeScopeOrNull(ctx, ref) {
538
540
  if (!ctx?.db || typeof ctx.db.query !== "function") {
@@ -553,16 +555,36 @@ async function resolveTopicNodeScopeOrNull(ctx, ref) {
553
555
  if (!node) {
554
556
  return null;
555
557
  }
556
- const scopeKey = normalizeScopeValue(node.topicId) ?? normalizeScopeValue(node.globalId);
558
+ const scopeKey = canonicalTopicGlobalId(node);
557
559
  if (!scopeKey) {
558
- return null;
560
+ throw new Error(
561
+ `topic.uuidv7_identity_required: topic node ${ref} is missing a UUIDv7 globalId.`
562
+ );
559
563
  }
564
+ const metadata = node.metadata ?? {};
560
565
  return {
561
566
  topicId: scopeKey,
562
567
  projectId: asMappedProjectId(node),
563
- source: "topic_node"
568
+ source: "topic_node",
569
+ tenantId: normalizeScopeValue(node.tenantId) ?? normalizeScopeValue(metadata.tenantId),
570
+ workspaceId: normalizeScopeValue(node.workspaceId) ?? normalizeScopeValue(metadata.workspaceId)
564
571
  };
565
572
  }
573
+ function canonicalTopicGlobalId(node) {
574
+ const globalId = normalizeScopeValue(node.globalId);
575
+ if (globalId && isUuidV7(globalId)) {
576
+ return globalId;
577
+ }
578
+ const topicId2 = normalizeScopeValue(node.topicId);
579
+ return topicId2 && isUuidV7(topicId2) ? topicId2 : null;
580
+ }
581
+ function requireUuidV7TopicScope(field, value) {
582
+ if (!isUuidV7(value)) {
583
+ throw new Error(
584
+ `topic.uuidv7_required: ${field} must be a UUIDv7 public topic identifier.`
585
+ );
586
+ }
587
+ }
566
588
  function asMappedProjectId(topic) {
567
589
  if (!topic) {
568
590
  return;
@@ -584,200 +606,25 @@ function normalizeScopeValue(value) {
584
606
  const normalized = value.trim();
585
607
  return normalized.length > 0 ? normalized : void 0;
586
608
  }
587
- function pickPrimaryTopic(candidates) {
588
- return [...candidates].sort((a, b) => {
589
- const depthA = a.depth ?? 9999;
590
- const depthB = b.depth ?? 9999;
591
- if (depthA !== depthB) {
592
- return depthA - depthB;
593
- }
594
- const createdA = a.createdAt ?? Number.MAX_SAFE_INTEGER;
595
- const createdB = b.createdAt ?? Number.MAX_SAFE_INTEGER;
596
- if (createdA !== createdB) {
597
- return createdA - createdB;
598
- }
599
- return String(a.name || "").localeCompare(String(b.name || ""));
600
- })[0];
601
- }
602
- async function findTopicsByScopeAlias(ctx, scopeId) {
603
- const query2 = ctx.db.query("topics");
604
- try {
605
- return await query2.withIndex(
606
- "by_graph_scope_project",
607
- (q) => q.eq(LEGACY_SCOPE_FIELD2, scopeId)
608
- ).collect();
609
- } catch (error) {
610
- debugGraphPrimitiveFallback(
611
- "[topicScope] Failed to resolve scope alias via index",
612
- {
613
- error,
614
- scopeId
615
- }
616
- );
617
- const topics = await query2.collect();
618
- return topics.filter((topic) => {
619
- const normalizedGlobalId = normalizeScopeValue(topic.globalId);
620
- const mappedProjectId = asMappedProjectId(topic);
621
- return String(topic._id) === scopeId || normalizedGlobalId === scopeId || mappedProjectId === scopeId;
622
- });
623
- }
624
- }
625
- async function tryResolveHostTopicById(ctx, topicId2) {
626
- if (typeof ctx.runQuery !== "function") {
627
- return null;
628
- }
629
- try {
630
- return await ctx.runQuery(api.topics.get, {
631
- id: topicId2
632
- }) ?? null;
633
- } catch (error) {
634
- debugGraphPrimitiveFallback(
635
- "[topicScope] Failed to resolve topic by host query",
636
- {
637
- error,
638
- topicId: topicId2
639
- }
640
- );
641
- return null;
642
- }
643
- }
644
- async function tryResolveHostTopicByLegacyScope(ctx, legacyScopeId) {
645
- if (typeof ctx.runQuery !== "function") {
646
- return null;
647
- }
648
- try {
649
- return await ctx.runQuery(api.topics.getByLegacyScopeId, {
650
- projectId: legacyScopeId
651
- }) ?? null;
652
- } catch (error) {
653
- debugGraphPrimitiveFallback(
654
- "[topicScope] Failed to resolve topic by legacy scope",
655
- {
656
- error,
657
- legacyScopeId
658
- }
659
- );
660
- return null;
661
- }
662
- }
663
- async function resolveInheritedWorkspaceScope(ctx, topic) {
664
- const MAX_DEPTH = 10;
665
- let tenantId = normalizeScopeValue(topic.tenantId);
666
- let workspaceId = normalizeScopeValue(topic.workspaceId);
667
- if (tenantId && workspaceId) {
668
- return { tenantId, workspaceId };
669
- }
670
- let current = topic;
671
- for (let i = 0; i < MAX_DEPTH && current?.parentTopicId; i++) {
672
- current = await ctx.db.get(current.parentTopicId);
673
- if (!current) {
674
- break;
675
- }
676
- if (!tenantId) {
677
- tenantId = normalizeScopeValue(current.tenantId);
678
- }
679
- if (!workspaceId) {
680
- workspaceId = normalizeScopeValue(current.workspaceId);
681
- }
682
- if (tenantId && workspaceId) {
683
- break;
684
- }
685
- }
686
- return { tenantId, workspaceId };
687
- }
688
609
  async function resolveTopicProjectScope(ctx, args) {
689
610
  if (args.topicId) {
690
611
  return await resolveScopeFromTopicId(ctx, args.topicId);
691
612
  }
692
613
  if (args.projectId) {
693
- return await resolveScopeFromLegacyProjectId(ctx, args.projectId);
614
+ throw new Error(
615
+ "topic.uuidv7_required: projectId scope aliases are retired; pass topicId as the UUIDv7 topic globalId."
616
+ );
694
617
  }
695
- throw new Error(
696
- "Missing scope: provide topicId (preferred) or legacy projectId alias."
697
- );
618
+ throw new Error("topic.uuidv7_required: Missing scope: provide topicId.");
698
619
  }
699
620
  async function resolveScopeFromTopicId(ctx, topicId2) {
700
- const topic = await resolveTopicDocFromTopicId(ctx, topicId2);
701
- if (topic) {
702
- return await buildTopicScope(ctx, topic, "topic");
703
- }
704
- const nodeScope = await resolveTopicNodeScopeOrNull(ctx, String(topicId2));
621
+ const topicGlobalId = String(topicId2);
622
+ requireUuidV7TopicScope("topicId", topicGlobalId);
623
+ const nodeScope = await resolveTopicNodeScopeOrNull(ctx, topicGlobalId);
705
624
  if (nodeScope) {
706
625
  return nodeScope;
707
626
  }
708
- throw new Error(`Topic not found: ${String(topicId2)}`);
709
- }
710
- async function resolveTopicDocFromTopicId(ctx, topicId2) {
711
- const direct = await tryReadTopicDoc(ctx, topicId2, {
712
- failureLog: "[topicScope] Failed to load topic by direct id",
713
- idLogKey: "topicId"
714
- });
715
- if (direct) {
716
- return direct;
717
- }
718
- const hostTopic = await tryResolveHostTopicById(ctx, String(topicId2));
719
- if (hostTopic) {
720
- return hostTopic;
721
- }
722
- return pickPrimaryTopic(await findTopicsByScopeAlias(ctx, String(topicId2))) ?? null;
723
- }
724
- async function resolveScopeFromLegacyProjectId(ctx, legacyProjectId) {
725
- const directTopic = await resolveDirectLegacyProjectTopic(
726
- ctx,
727
- legacyProjectId
728
- );
729
- if (directTopic) {
730
- return await buildTopicScope(ctx, directTopic, "topic_inferred", {
731
- fallbackProjectId: legacyProjectId
732
- });
733
- }
734
- const primary = pickPrimaryTopic(
735
- await findTopicsByScopeAlias(ctx, legacyProjectId)
736
- );
737
- if (primary) {
738
- return await buildTopicScope(ctx, primary, "project_mapped_topic", {
739
- fallbackProjectId: legacyProjectId
740
- });
741
- }
742
- const nodeScope = await resolveTopicNodeScopeOrNull(ctx, legacyProjectId);
743
- if (nodeScope) {
744
- return {
745
- ...nodeScope,
746
- projectId: nodeScope.projectId ?? legacyProjectId
747
- };
748
- }
749
- throw new Error(
750
- `Legacy project scope ${legacyProjectId} has no mapped topic.`
751
- );
752
- }
753
- async function resolveDirectLegacyProjectTopic(ctx, legacyProjectId) {
754
- const directTopic = await tryReadTopicDoc(ctx, legacyProjectId, {
755
- failureLog: "[topicScope] Failed to load direct project topic",
756
- idLogKey: "projectId"
757
- });
758
- return directTopic ?? tryResolveHostTopicByLegacyScope(ctx, legacyProjectId);
759
- }
760
- async function tryReadTopicDoc(ctx, id, log) {
761
- try {
762
- return await ctx.db.get(id);
763
- } catch (error) {
764
- debugGraphPrimitiveFallback(log.failureLog, {
765
- error,
766
- [log.idLogKey]: id
767
- });
768
- return null;
769
- }
770
- }
771
- async function buildTopicScope(ctx, topic, source, options = {}) {
772
- const inherited = await resolveInheritedWorkspaceScope(ctx, topic);
773
- const mapped = asMappedProjectId(topic);
774
- return {
775
- topicId: topic._id,
776
- ...mapped || options.fallbackProjectId ? { projectId: mapped ?? options.fallbackProjectId } : {},
777
- tenantId: inherited.tenantId,
778
- workspaceId: inherited.workspaceId,
779
- source
780
- };
627
+ throw new Error(`Topic not found: ${topicGlobalId}`);
781
628
  }
782
629
  var optionalScopeArgs = {
783
630
  projectId: v.optional(v.string()),
@@ -3738,6 +3585,9 @@ async function resolveForkTriggerEvidence(ctx, args) {
3738
3585
  }
3739
3586
  function insertEpistemicNode(ctx, doc) {
3740
3587
  assertUuidV7Identity("epistemicNodes", doc.globalId);
3588
+ if (doc.topicId !== void 0 && doc.topicId !== null) {
3589
+ assertUuidV7Reference("epistemicNodes.topicId", doc.topicId);
3590
+ }
3741
3591
  return ctx.db.insert("epistemicNodes", doc);
3742
3592
  }
3743
3593
  async function assertExistingNodeEndpoint(ctx, endpointRole, endpoint) {