@lucern/graph-primitives 1.0.48 → 1.0.52

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 +2 -2
  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 '@lucern/access-control/audience';
8
8
  import { getCurrentUserId } from '@lucern/access-control/auth';
9
9
  import { throwStructuredMutationError } from '@lucern/access-control/structuredMutationError';
10
10
  import { normalizeTupleContradictionPolicy, createInheritedContractRecord, 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
 
@@ -546,6 +546,8 @@ function resolveGraphPrimitivesAppResolvers(_ctx) {
546
546
  ...resolverOverrides
547
547
  };
548
548
  }
549
+
550
+ // src/topicScope.ts
549
551
  var LEGACY_SCOPE_FIELD2 = "graphScopeProjectId";
550
552
  async function resolveTopicNodeScopeOrNull(ctx, ref) {
551
553
  if (!ctx?.db || typeof ctx.db.query !== "function") {
@@ -566,16 +568,36 @@ async function resolveTopicNodeScopeOrNull(ctx, ref) {
566
568
  if (!node) {
567
569
  return null;
568
570
  }
569
- const scopeKey = normalizeScopeValue(node.topicId) ?? normalizeScopeValue(node.globalId);
571
+ const scopeKey = canonicalTopicGlobalId(node);
570
572
  if (!scopeKey) {
571
- return null;
573
+ throw new Error(
574
+ `topic.uuidv7_identity_required: topic node ${ref} is missing a UUIDv7 globalId.`
575
+ );
572
576
  }
577
+ const metadata = node.metadata ?? {};
573
578
  return {
574
579
  topicId: scopeKey,
575
580
  projectId: asMappedProjectId(node),
576
- source: "topic_node"
581
+ source: "topic_node",
582
+ tenantId: normalizeScopeValue(node.tenantId) ?? normalizeScopeValue(metadata.tenantId),
583
+ workspaceId: normalizeScopeValue(node.workspaceId) ?? normalizeScopeValue(metadata.workspaceId)
577
584
  };
578
585
  }
586
+ function canonicalTopicGlobalId(node) {
587
+ const globalId = normalizeScopeValue(node.globalId);
588
+ if (globalId && isUuidV7(globalId)) {
589
+ return globalId;
590
+ }
591
+ const topicId = normalizeScopeValue(node.topicId);
592
+ return topicId && isUuidV7(topicId) ? topicId : null;
593
+ }
594
+ function requireUuidV7TopicScope(field, value) {
595
+ if (!isUuidV7(value)) {
596
+ throw new Error(
597
+ `topic.uuidv7_required: ${field} must be a UUIDv7 public topic identifier.`
598
+ );
599
+ }
600
+ }
579
601
  function asMappedProjectId(topic) {
580
602
  if (!topic) {
581
603
  return;
@@ -597,200 +619,25 @@ function normalizeScopeValue(value) {
597
619
  const normalized = value.trim();
598
620
  return normalized.length > 0 ? normalized : void 0;
599
621
  }
600
- function pickPrimaryTopic(candidates) {
601
- return [...candidates].sort((a, b) => {
602
- const depthA = a.depth ?? 9999;
603
- const depthB = b.depth ?? 9999;
604
- if (depthA !== depthB) {
605
- return depthA - depthB;
606
- }
607
- const createdA = a.createdAt ?? Number.MAX_SAFE_INTEGER;
608
- const createdB = b.createdAt ?? Number.MAX_SAFE_INTEGER;
609
- if (createdA !== createdB) {
610
- return createdA - createdB;
611
- }
612
- return String(a.name || "").localeCompare(String(b.name || ""));
613
- })[0];
614
- }
615
- async function findTopicsByScopeAlias(ctx, scopeId) {
616
- const query2 = ctx.db.query("topics");
617
- try {
618
- return await query2.withIndex(
619
- "by_graph_scope_project",
620
- (q) => q.eq(LEGACY_SCOPE_FIELD2, scopeId)
621
- ).collect();
622
- } catch (error) {
623
- debugGraphPrimitiveFallback(
624
- "[topicScope] Failed to resolve scope alias via index",
625
- {
626
- error,
627
- scopeId
628
- }
629
- );
630
- const topics = await query2.collect();
631
- return topics.filter((topic) => {
632
- const normalizedGlobalId = normalizeScopeValue(topic.globalId);
633
- const mappedProjectId = asMappedProjectId(topic);
634
- return String(topic._id) === scopeId || normalizedGlobalId === scopeId || mappedProjectId === scopeId;
635
- });
636
- }
637
- }
638
- async function tryResolveHostTopicById(ctx, topicId) {
639
- if (typeof ctx.runQuery !== "function") {
640
- return null;
641
- }
642
- try {
643
- return await ctx.runQuery(api.topics.get, {
644
- id: topicId
645
- }) ?? null;
646
- } catch (error) {
647
- debugGraphPrimitiveFallback(
648
- "[topicScope] Failed to resolve topic by host query",
649
- {
650
- error,
651
- topicId
652
- }
653
- );
654
- return null;
655
- }
656
- }
657
- async function tryResolveHostTopicByLegacyScope(ctx, legacyScopeId) {
658
- if (typeof ctx.runQuery !== "function") {
659
- return null;
660
- }
661
- try {
662
- return await ctx.runQuery(api.topics.getByLegacyScopeId, {
663
- projectId: legacyScopeId
664
- }) ?? null;
665
- } catch (error) {
666
- debugGraphPrimitiveFallback(
667
- "[topicScope] Failed to resolve topic by legacy scope",
668
- {
669
- error,
670
- legacyScopeId
671
- }
672
- );
673
- return null;
674
- }
675
- }
676
- async function resolveInheritedWorkspaceScope(ctx, topic) {
677
- const MAX_DEPTH = 10;
678
- let tenantId = normalizeScopeValue(topic.tenantId);
679
- let workspaceId = normalizeScopeValue(topic.workspaceId);
680
- if (tenantId && workspaceId) {
681
- return { tenantId, workspaceId };
682
- }
683
- let current = topic;
684
- for (let i = 0; i < MAX_DEPTH && current?.parentTopicId; i++) {
685
- current = await ctx.db.get(current.parentTopicId);
686
- if (!current) {
687
- break;
688
- }
689
- if (!tenantId) {
690
- tenantId = normalizeScopeValue(current.tenantId);
691
- }
692
- if (!workspaceId) {
693
- workspaceId = normalizeScopeValue(current.workspaceId);
694
- }
695
- if (tenantId && workspaceId) {
696
- break;
697
- }
698
- }
699
- return { tenantId, workspaceId };
700
- }
701
622
  async function resolveTopicProjectScope(ctx, args) {
702
623
  if (args.topicId) {
703
624
  return await resolveScopeFromTopicId(ctx, args.topicId);
704
625
  }
705
626
  if (args.projectId) {
706
- return await resolveScopeFromLegacyProjectId(ctx, args.projectId);
627
+ throw new Error(
628
+ "topic.uuidv7_required: projectId scope aliases are retired; pass topicId as the UUIDv7 topic globalId."
629
+ );
707
630
  }
708
- throw new Error(
709
- "Missing scope: provide topicId (preferred) or legacy projectId alias."
710
- );
631
+ throw new Error("topic.uuidv7_required: Missing scope: provide topicId.");
711
632
  }
712
633
  async function resolveScopeFromTopicId(ctx, topicId) {
713
- const topic = await resolveTopicDocFromTopicId(ctx, topicId);
714
- if (topic) {
715
- return await buildTopicScope(ctx, topic, "topic");
716
- }
717
- const nodeScope = await resolveTopicNodeScopeOrNull(ctx, String(topicId));
634
+ const topicGlobalId = String(topicId);
635
+ requireUuidV7TopicScope("topicId", topicGlobalId);
636
+ const nodeScope = await resolveTopicNodeScopeOrNull(ctx, topicGlobalId);
718
637
  if (nodeScope) {
719
638
  return nodeScope;
720
639
  }
721
- throw new Error(`Topic not found: ${String(topicId)}`);
722
- }
723
- async function resolveTopicDocFromTopicId(ctx, topicId) {
724
- const direct = await tryReadTopicDoc(ctx, topicId, {
725
- failureLog: "[topicScope] Failed to load topic by direct id",
726
- idLogKey: "topicId"
727
- });
728
- if (direct) {
729
- return direct;
730
- }
731
- const hostTopic = await tryResolveHostTopicById(ctx, String(topicId));
732
- if (hostTopic) {
733
- return hostTopic;
734
- }
735
- return pickPrimaryTopic(await findTopicsByScopeAlias(ctx, String(topicId))) ?? null;
736
- }
737
- async function resolveScopeFromLegacyProjectId(ctx, legacyProjectId) {
738
- const directTopic = await resolveDirectLegacyProjectTopic(
739
- ctx,
740
- legacyProjectId
741
- );
742
- if (directTopic) {
743
- return await buildTopicScope(ctx, directTopic, "topic_inferred", {
744
- fallbackProjectId: legacyProjectId
745
- });
746
- }
747
- const primary = pickPrimaryTopic(
748
- await findTopicsByScopeAlias(ctx, legacyProjectId)
749
- );
750
- if (primary) {
751
- return await buildTopicScope(ctx, primary, "project_mapped_topic", {
752
- fallbackProjectId: legacyProjectId
753
- });
754
- }
755
- const nodeScope = await resolveTopicNodeScopeOrNull(ctx, legacyProjectId);
756
- if (nodeScope) {
757
- return {
758
- ...nodeScope,
759
- projectId: nodeScope.projectId ?? legacyProjectId
760
- };
761
- }
762
- throw new Error(
763
- `Legacy project scope ${legacyProjectId} has no mapped topic.`
764
- );
765
- }
766
- async function resolveDirectLegacyProjectTopic(ctx, legacyProjectId) {
767
- const directTopic = await tryReadTopicDoc(ctx, legacyProjectId, {
768
- failureLog: "[topicScope] Failed to load direct project topic",
769
- idLogKey: "projectId"
770
- });
771
- return directTopic ?? tryResolveHostTopicByLegacyScope(ctx, legacyProjectId);
772
- }
773
- async function tryReadTopicDoc(ctx, id, log) {
774
- try {
775
- return await ctx.db.get(id);
776
- } catch (error) {
777
- debugGraphPrimitiveFallback(log.failureLog, {
778
- error,
779
- [log.idLogKey]: id
780
- });
781
- return null;
782
- }
783
- }
784
- async function buildTopicScope(ctx, topic, source, options = {}) {
785
- const inherited = await resolveInheritedWorkspaceScope(ctx, topic);
786
- const mapped = asMappedProjectId(topic);
787
- return {
788
- topicId: topic._id,
789
- ...mapped || options.fallbackProjectId ? { projectId: mapped ?? options.fallbackProjectId } : {},
790
- tenantId: inherited.tenantId,
791
- workspaceId: inherited.workspaceId,
792
- source
793
- };
640
+ throw new Error(`Topic not found: ${topicGlobalId}`);
794
641
  }
795
642
  var optionalScopeArgs = {
796
643
  projectId: v.optional(v.string()),
@@ -1114,6 +961,9 @@ async function resolveForkTriggerEvidence(ctx, args) {
1114
961
  }
1115
962
  function insertEpistemicNode(ctx, doc) {
1116
963
  assertUuidV7Identity("epistemicNodes", doc.globalId);
964
+ if (doc.topicId !== void 0 && doc.topicId !== null) {
965
+ assertUuidV7Reference("epistemicNodes.topicId", doc.topicId);
966
+ }
1117
967
  return ctx.db.insert("epistemicNodes", doc);
1118
968
  }
1119
969
  async function assertExistingNodeEndpoint(ctx, endpointRole, endpoint) {
@@ -12,6 +12,6 @@ import './convex.js';
12
12
  import '@lucern/access-control/convex';
13
13
  import '@lucern/contracts/convex/unsafeAnyApi';
14
14
  import 'convex/values';
15
- import './topicScope-DJVa0mLa.js';
15
+ import './topicScope-CL1IVOmv.js';
16
16
  import './beliefLifecycle-CXwdDw5e.js';
17
17
  import '@lucern/access-control/structuredMutationError';
@@ -1,4 +1,4 @@
1
- import { r as resolveTopicProjectScope, T as TopicProjectScope } from './topicScope-DJVa0mLa.js';
1
+ import { r as resolveTopicProjectScope, T as TopicProjectScope } from './topicScope-CL1IVOmv.js';
2
2
  import * as convex_values from 'convex/values';
3
3
  import { VerificationConfidenceTrigger, SLOperator, SLOpinion, Opinion, ConfidencePolicyConfig } from '@lucern/confidence';
4
4
  import { B as BeliefLifecycleStatus } from './beliefLifecycle-CXwdDw5e.js';
@@ -6,6 +6,7 @@ import { normalizeTupleContradictionPolicy, confidenceFromSL, readOpinionFromRec
6
6
  import { v } from 'convex/values';
7
7
  import { unsafeConvexAnyApi } from '@lucern/contracts/convex/unsafeAnyApi';
8
8
  import { componentsGeneric } from 'convex/server';
9
+ import { isUuidV7 } from '@lucern/contracts/ids';
9
10
  import '@lucern/contracts/schema-helpers/spine/tables/epistemicNodes';
10
11
 
11
12
  // src/epistemicBeliefs.helpers.ts
@@ -513,6 +514,8 @@ function resolveGraphPrimitivesAppResolvers(_ctx) {
513
514
  ...resolverOverrides
514
515
  };
515
516
  }
517
+
518
+ // src/topicScope.ts
516
519
  var LEGACY_SCOPE_FIELD2 = "graphScopeProjectId";
517
520
  async function resolveTopicNodeScopeOrNull(ctx, ref) {
518
521
  if (!ctx?.db || typeof ctx.db.query !== "function") {
@@ -533,16 +536,36 @@ async function resolveTopicNodeScopeOrNull(ctx, ref) {
533
536
  if (!node) {
534
537
  return null;
535
538
  }
536
- const scopeKey = normalizeScopeValue(node.topicId) ?? normalizeScopeValue(node.globalId);
539
+ const scopeKey = canonicalTopicGlobalId(node);
537
540
  if (!scopeKey) {
538
- return null;
541
+ throw new Error(
542
+ `topic.uuidv7_identity_required: topic node ${ref} is missing a UUIDv7 globalId.`
543
+ );
539
544
  }
545
+ const metadata = node.metadata ?? {};
540
546
  return {
541
547
  topicId: scopeKey,
542
548
  projectId: asMappedProjectId(node),
543
- source: "topic_node"
549
+ source: "topic_node",
550
+ tenantId: normalizeScopeValue(node.tenantId) ?? normalizeScopeValue(metadata.tenantId),
551
+ workspaceId: normalizeScopeValue(node.workspaceId) ?? normalizeScopeValue(metadata.workspaceId)
544
552
  };
545
553
  }
554
+ function canonicalTopicGlobalId(node) {
555
+ const globalId = normalizeScopeValue(node.globalId);
556
+ if (globalId && isUuidV7(globalId)) {
557
+ return globalId;
558
+ }
559
+ const topicId = normalizeScopeValue(node.topicId);
560
+ return topicId && isUuidV7(topicId) ? topicId : null;
561
+ }
562
+ function requireUuidV7TopicScope(field, value) {
563
+ if (!isUuidV7(value)) {
564
+ throw new Error(
565
+ `topic.uuidv7_required: ${field} must be a UUIDv7 public topic identifier.`
566
+ );
567
+ }
568
+ }
546
569
  function asMappedProjectId(topic) {
547
570
  if (!topic) {
548
571
  return;
@@ -564,200 +587,25 @@ function normalizeScopeValue(value) {
564
587
  const normalized = value.trim();
565
588
  return normalized.length > 0 ? normalized : void 0;
566
589
  }
567
- function pickPrimaryTopic(candidates) {
568
- return [...candidates].sort((a, b) => {
569
- const depthA = a.depth ?? 9999;
570
- const depthB = b.depth ?? 9999;
571
- if (depthA !== depthB) {
572
- return depthA - depthB;
573
- }
574
- const createdA = a.createdAt ?? Number.MAX_SAFE_INTEGER;
575
- const createdB = b.createdAt ?? Number.MAX_SAFE_INTEGER;
576
- if (createdA !== createdB) {
577
- return createdA - createdB;
578
- }
579
- return String(a.name || "").localeCompare(String(b.name || ""));
580
- })[0];
581
- }
582
- async function findTopicsByScopeAlias(ctx, scopeId) {
583
- const query = ctx.db.query("topics");
584
- try {
585
- return await query.withIndex(
586
- "by_graph_scope_project",
587
- (q) => q.eq(LEGACY_SCOPE_FIELD2, scopeId)
588
- ).collect();
589
- } catch (error) {
590
- debugGraphPrimitiveFallback(
591
- "[topicScope] Failed to resolve scope alias via index",
592
- {
593
- error,
594
- scopeId
595
- }
596
- );
597
- const topics = await query.collect();
598
- return topics.filter((topic) => {
599
- const normalizedGlobalId = normalizeScopeValue(topic.globalId);
600
- const mappedProjectId = asMappedProjectId(topic);
601
- return String(topic._id) === scopeId || normalizedGlobalId === scopeId || mappedProjectId === scopeId;
602
- });
603
- }
604
- }
605
- async function tryResolveHostTopicById(ctx, topicId) {
606
- if (typeof ctx.runQuery !== "function") {
607
- return null;
608
- }
609
- try {
610
- return await ctx.runQuery(api.topics.get, {
611
- id: topicId
612
- }) ?? null;
613
- } catch (error) {
614
- debugGraphPrimitiveFallback(
615
- "[topicScope] Failed to resolve topic by host query",
616
- {
617
- error,
618
- topicId
619
- }
620
- );
621
- return null;
622
- }
623
- }
624
- async function tryResolveHostTopicByLegacyScope(ctx, legacyScopeId) {
625
- if (typeof ctx.runQuery !== "function") {
626
- return null;
627
- }
628
- try {
629
- return await ctx.runQuery(api.topics.getByLegacyScopeId, {
630
- projectId: legacyScopeId
631
- }) ?? null;
632
- } catch (error) {
633
- debugGraphPrimitiveFallback(
634
- "[topicScope] Failed to resolve topic by legacy scope",
635
- {
636
- error,
637
- legacyScopeId
638
- }
639
- );
640
- return null;
641
- }
642
- }
643
- async function resolveInheritedWorkspaceScope(ctx, topic) {
644
- const MAX_DEPTH = 10;
645
- let tenantId = normalizeScopeValue(topic.tenantId);
646
- let workspaceId = normalizeScopeValue(topic.workspaceId);
647
- if (tenantId && workspaceId) {
648
- return { tenantId, workspaceId };
649
- }
650
- let current = topic;
651
- for (let i = 0; i < MAX_DEPTH && current?.parentTopicId; i++) {
652
- current = await ctx.db.get(current.parentTopicId);
653
- if (!current) {
654
- break;
655
- }
656
- if (!tenantId) {
657
- tenantId = normalizeScopeValue(current.tenantId);
658
- }
659
- if (!workspaceId) {
660
- workspaceId = normalizeScopeValue(current.workspaceId);
661
- }
662
- if (tenantId && workspaceId) {
663
- break;
664
- }
665
- }
666
- return { tenantId, workspaceId };
667
- }
668
590
  async function resolveTopicProjectScope(ctx, args) {
669
591
  if (args.topicId) {
670
592
  return await resolveScopeFromTopicId(ctx, args.topicId);
671
593
  }
672
594
  if (args.projectId) {
673
- return await resolveScopeFromLegacyProjectId(ctx, args.projectId);
595
+ throw new Error(
596
+ "topic.uuidv7_required: projectId scope aliases are retired; pass topicId as the UUIDv7 topic globalId."
597
+ );
674
598
  }
675
- throw new Error(
676
- "Missing scope: provide topicId (preferred) or legacy projectId alias."
677
- );
599
+ throw new Error("topic.uuidv7_required: Missing scope: provide topicId.");
678
600
  }
679
601
  async function resolveScopeFromTopicId(ctx, topicId) {
680
- const topic = await resolveTopicDocFromTopicId(ctx, topicId);
681
- if (topic) {
682
- return await buildTopicScope(ctx, topic, "topic");
683
- }
684
- const nodeScope = await resolveTopicNodeScopeOrNull(ctx, String(topicId));
602
+ const topicGlobalId = String(topicId);
603
+ requireUuidV7TopicScope("topicId", topicGlobalId);
604
+ const nodeScope = await resolveTopicNodeScopeOrNull(ctx, topicGlobalId);
685
605
  if (nodeScope) {
686
606
  return nodeScope;
687
607
  }
688
- throw new Error(`Topic not found: ${String(topicId)}`);
689
- }
690
- async function resolveTopicDocFromTopicId(ctx, topicId) {
691
- const direct = await tryReadTopicDoc(ctx, topicId, {
692
- failureLog: "[topicScope] Failed to load topic by direct id",
693
- idLogKey: "topicId"
694
- });
695
- if (direct) {
696
- return direct;
697
- }
698
- const hostTopic = await tryResolveHostTopicById(ctx, String(topicId));
699
- if (hostTopic) {
700
- return hostTopic;
701
- }
702
- return pickPrimaryTopic(await findTopicsByScopeAlias(ctx, String(topicId))) ?? null;
703
- }
704
- async function resolveScopeFromLegacyProjectId(ctx, legacyProjectId) {
705
- const directTopic = await resolveDirectLegacyProjectTopic(
706
- ctx,
707
- legacyProjectId
708
- );
709
- if (directTopic) {
710
- return await buildTopicScope(ctx, directTopic, "topic_inferred", {
711
- fallbackProjectId: legacyProjectId
712
- });
713
- }
714
- const primary = pickPrimaryTopic(
715
- await findTopicsByScopeAlias(ctx, legacyProjectId)
716
- );
717
- if (primary) {
718
- return await buildTopicScope(ctx, primary, "project_mapped_topic", {
719
- fallbackProjectId: legacyProjectId
720
- });
721
- }
722
- const nodeScope = await resolveTopicNodeScopeOrNull(ctx, legacyProjectId);
723
- if (nodeScope) {
724
- return {
725
- ...nodeScope,
726
- projectId: nodeScope.projectId ?? legacyProjectId
727
- };
728
- }
729
- throw new Error(
730
- `Legacy project scope ${legacyProjectId} has no mapped topic.`
731
- );
732
- }
733
- async function resolveDirectLegacyProjectTopic(ctx, legacyProjectId) {
734
- const directTopic = await tryReadTopicDoc(ctx, legacyProjectId, {
735
- failureLog: "[topicScope] Failed to load direct project topic",
736
- idLogKey: "projectId"
737
- });
738
- return directTopic ?? tryResolveHostTopicByLegacyScope(ctx, legacyProjectId);
739
- }
740
- async function tryReadTopicDoc(ctx, id, log) {
741
- try {
742
- return await ctx.db.get(id);
743
- } catch (error) {
744
- debugGraphPrimitiveFallback(log.failureLog, {
745
- error,
746
- [log.idLogKey]: id
747
- });
748
- return null;
749
- }
750
- }
751
- async function buildTopicScope(ctx, topic, source, options = {}) {
752
- const inherited = await resolveInheritedWorkspaceScope(ctx, topic);
753
- const mapped = asMappedProjectId(topic);
754
- return {
755
- topicId: topic._id,
756
- ...mapped || options.fallbackProjectId ? { projectId: mapped ?? options.fallbackProjectId } : {},
757
- tenantId: inherited.tenantId,
758
- workspaceId: inherited.workspaceId,
759
- source
760
- };
608
+ throw new Error(`Topic not found: ${topicGlobalId}`);
761
609
  }
762
610
  var optionalScopeArgs = {
763
611
  projectId: v.optional(v.string()),