@lucern/graph-primitives 1.0.50 → 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 +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
@@ -5,16 +5,15 @@ import { getCurrentUserId } from '@lucern/access-control/auth';
5
5
  import { throwStructuredMutationError } from '@lucern/access-control/structuredMutationError';
6
6
  import { normalizeTupleContradictionPolicy, mkOpinion, conditionalDeduction, project, dampedDependencyCascade, deriveContractModulationPlan, deriveContractStatus, trustDiscount, applyNegativeSupport, cumulativeFusion, applyNegativeEvidence, confidenceFromSL, detectTupleContradiction, evaluateTupleContradictionTransition, readOpinionFromRecord, parseEvidentialEvaluatorConfig, compareMetricValue, resolveComparisonResult, buildEvidentialRationale, parseMetricCheckerConfig, getEvaluatorInputRecord, pickFiniteNumber, buildComparisonRationale, parseReferenceCheckCounterConfig, parseTemporalDeadlineConfig, parseMarketIndexComparatorConfig, hasProjectedOpinionChanged, createInheritedContractRecord } from '@lucern/confidence';
7
7
  import { v } from 'convex/values';
8
+ import { generateGlobalId, generateUuidV7, isUuidV7 } from '@lucern/contracts/ids';
8
9
  import '@lucern/contracts/schema-helpers/spine/tables/epistemicNodes';
9
10
  import { checkProjectAccess, requireScopeWriteAccess } from '@lucern/access-control/access';
10
- import { generateGlobalId, generateUuidV7 } from '@lucern/contracts/ids';
11
11
  import { permissiveReturn } from '@lucern/contracts/schema-helpers/validators';
12
12
 
13
13
  // src/convex.ts
14
14
  var unsafeApi = unsafeConvexAnyApi(
15
15
  "graph-primitives top-level module bundle lacks a committed Convex _generated/api surface"
16
16
  );
17
- var api = unsafeApi;
18
17
  componentsGeneric();
19
18
  var internal = unsafeApi;
20
19
  var internalMutation = internalMutationGeneric;
@@ -153,6 +152,8 @@ function promoteBeliefStatusAfterScoring(status, opts) {
153
152
  }
154
153
  return status;
155
154
  }
155
+
156
+ // src/topicScope.ts
156
157
  var LEGACY_SCOPE_FIELD = "graphScopeProjectId";
157
158
  async function resolveTopicNodeScopeOrNull(ctx, ref) {
158
159
  if (!ctx?.db || typeof ctx.db.query !== "function") {
@@ -173,16 +174,36 @@ async function resolveTopicNodeScopeOrNull(ctx, ref) {
173
174
  if (!node) {
174
175
  return null;
175
176
  }
176
- const scopeKey = normalizeScopeValue(node.topicId) ?? normalizeScopeValue(node.globalId);
177
+ const scopeKey = canonicalTopicGlobalId(node);
177
178
  if (!scopeKey) {
178
- return null;
179
+ throw new Error(
180
+ `topic.uuidv7_identity_required: topic node ${ref} is missing a UUIDv7 globalId.`
181
+ );
179
182
  }
183
+ const metadata = node.metadata ?? {};
180
184
  return {
181
185
  topicId: scopeKey,
182
186
  projectId: asMappedProjectId(node),
183
- source: "topic_node"
187
+ source: "topic_node",
188
+ tenantId: normalizeScopeValue(node.tenantId) ?? normalizeScopeValue(metadata.tenantId),
189
+ workspaceId: normalizeScopeValue(node.workspaceId) ?? normalizeScopeValue(metadata.workspaceId)
184
190
  };
185
191
  }
192
+ function canonicalTopicGlobalId(node) {
193
+ const globalId = normalizeScopeValue(node.globalId);
194
+ if (globalId && isUuidV7(globalId)) {
195
+ return globalId;
196
+ }
197
+ const topicId = normalizeScopeValue(node.topicId);
198
+ return topicId && isUuidV7(topicId) ? topicId : null;
199
+ }
200
+ function requireUuidV7TopicScope(field, value) {
201
+ if (!isUuidV7(value)) {
202
+ throw new Error(
203
+ `topic.uuidv7_required: ${field} must be a UUIDv7 public topic identifier.`
204
+ );
205
+ }
206
+ }
186
207
  function asMappedProjectId(topic) {
187
208
  if (!topic) {
188
209
  return;
@@ -204,200 +225,25 @@ function normalizeScopeValue(value) {
204
225
  const normalized = value.trim();
205
226
  return normalized.length > 0 ? normalized : void 0;
206
227
  }
207
- function pickPrimaryTopic(candidates) {
208
- return [...candidates].sort((a, b) => {
209
- const depthA = a.depth ?? 9999;
210
- const depthB = b.depth ?? 9999;
211
- if (depthA !== depthB) {
212
- return depthA - depthB;
213
- }
214
- const createdA = a.createdAt ?? Number.MAX_SAFE_INTEGER;
215
- const createdB = b.createdAt ?? Number.MAX_SAFE_INTEGER;
216
- if (createdA !== createdB) {
217
- return createdA - createdB;
218
- }
219
- return String(a.name || "").localeCompare(String(b.name || ""));
220
- })[0];
221
- }
222
- async function findTopicsByScopeAlias(ctx, scopeId) {
223
- const query2 = ctx.db.query("topics");
224
- try {
225
- return await query2.withIndex(
226
- "by_graph_scope_project",
227
- (q) => q.eq(LEGACY_SCOPE_FIELD, scopeId)
228
- ).collect();
229
- } catch (error) {
230
- debugGraphPrimitiveFallback(
231
- "[topicScope] Failed to resolve scope alias via index",
232
- {
233
- error,
234
- scopeId
235
- }
236
- );
237
- const topics = await query2.collect();
238
- return topics.filter((topic) => {
239
- const normalizedGlobalId = normalizeScopeValue(topic.globalId);
240
- const mappedProjectId = asMappedProjectId(topic);
241
- return String(topic._id) === scopeId || normalizedGlobalId === scopeId || mappedProjectId === scopeId;
242
- });
243
- }
244
- }
245
- async function tryResolveHostTopicById(ctx, topicId) {
246
- if (typeof ctx.runQuery !== "function") {
247
- return null;
248
- }
249
- try {
250
- return await ctx.runQuery(api.topics.get, {
251
- id: topicId
252
- }) ?? null;
253
- } catch (error) {
254
- debugGraphPrimitiveFallback(
255
- "[topicScope] Failed to resolve topic by host query",
256
- {
257
- error,
258
- topicId
259
- }
260
- );
261
- return null;
262
- }
263
- }
264
- async function tryResolveHostTopicByLegacyScope(ctx, legacyScopeId) {
265
- if (typeof ctx.runQuery !== "function") {
266
- return null;
267
- }
268
- try {
269
- return await ctx.runQuery(api.topics.getByLegacyScopeId, {
270
- projectId: legacyScopeId
271
- }) ?? null;
272
- } catch (error) {
273
- debugGraphPrimitiveFallback(
274
- "[topicScope] Failed to resolve topic by legacy scope",
275
- {
276
- error,
277
- legacyScopeId
278
- }
279
- );
280
- return null;
281
- }
282
- }
283
- async function resolveInheritedWorkspaceScope(ctx, topic) {
284
- const MAX_DEPTH = 10;
285
- let tenantId = normalizeScopeValue(topic.tenantId);
286
- let workspaceId = normalizeScopeValue(topic.workspaceId);
287
- if (tenantId && workspaceId) {
288
- return { tenantId, workspaceId };
289
- }
290
- let current = topic;
291
- for (let i = 0; i < MAX_DEPTH && current?.parentTopicId; i++) {
292
- current = await ctx.db.get(current.parentTopicId);
293
- if (!current) {
294
- break;
295
- }
296
- if (!tenantId) {
297
- tenantId = normalizeScopeValue(current.tenantId);
298
- }
299
- if (!workspaceId) {
300
- workspaceId = normalizeScopeValue(current.workspaceId);
301
- }
302
- if (tenantId && workspaceId) {
303
- break;
304
- }
305
- }
306
- return { tenantId, workspaceId };
307
- }
308
228
  async function resolveTopicProjectScope(ctx, args) {
309
229
  if (args.topicId) {
310
230
  return await resolveScopeFromTopicId(ctx, args.topicId);
311
231
  }
312
232
  if (args.projectId) {
313
- return await resolveScopeFromLegacyProjectId(ctx, args.projectId);
233
+ throw new Error(
234
+ "topic.uuidv7_required: projectId scope aliases are retired; pass topicId as the UUIDv7 topic globalId."
235
+ );
314
236
  }
315
- throw new Error(
316
- "Missing scope: provide topicId (preferred) or legacy projectId alias."
317
- );
237
+ throw new Error("topic.uuidv7_required: Missing scope: provide topicId.");
318
238
  }
319
239
  async function resolveScopeFromTopicId(ctx, topicId) {
320
- const topic = await resolveTopicDocFromTopicId(ctx, topicId);
321
- if (topic) {
322
- return await buildTopicScope(ctx, topic, "topic");
323
- }
324
- const nodeScope = await resolveTopicNodeScopeOrNull(ctx, String(topicId));
240
+ const topicGlobalId = String(topicId);
241
+ requireUuidV7TopicScope("topicId", topicGlobalId);
242
+ const nodeScope = await resolveTopicNodeScopeOrNull(ctx, topicGlobalId);
325
243
  if (nodeScope) {
326
244
  return nodeScope;
327
245
  }
328
- throw new Error(`Topic not found: ${String(topicId)}`);
329
- }
330
- async function resolveTopicDocFromTopicId(ctx, topicId) {
331
- const direct = await tryReadTopicDoc(ctx, topicId, {
332
- failureLog: "[topicScope] Failed to load topic by direct id",
333
- idLogKey: "topicId"
334
- });
335
- if (direct) {
336
- return direct;
337
- }
338
- const hostTopic = await tryResolveHostTopicById(ctx, String(topicId));
339
- if (hostTopic) {
340
- return hostTopic;
341
- }
342
- return pickPrimaryTopic(await findTopicsByScopeAlias(ctx, String(topicId))) ?? null;
343
- }
344
- async function resolveScopeFromLegacyProjectId(ctx, legacyProjectId) {
345
- const directTopic = await resolveDirectLegacyProjectTopic(
346
- ctx,
347
- legacyProjectId
348
- );
349
- if (directTopic) {
350
- return await buildTopicScope(ctx, directTopic, "topic_inferred", {
351
- fallbackProjectId: legacyProjectId
352
- });
353
- }
354
- const primary = pickPrimaryTopic(
355
- await findTopicsByScopeAlias(ctx, legacyProjectId)
356
- );
357
- if (primary) {
358
- return await buildTopicScope(ctx, primary, "project_mapped_topic", {
359
- fallbackProjectId: legacyProjectId
360
- });
361
- }
362
- const nodeScope = await resolveTopicNodeScopeOrNull(ctx, legacyProjectId);
363
- if (nodeScope) {
364
- return {
365
- ...nodeScope,
366
- projectId: nodeScope.projectId ?? legacyProjectId
367
- };
368
- }
369
- throw new Error(
370
- `Legacy project scope ${legacyProjectId} has no mapped topic.`
371
- );
372
- }
373
- async function resolveDirectLegacyProjectTopic(ctx, legacyProjectId) {
374
- const directTopic = await tryReadTopicDoc(ctx, legacyProjectId, {
375
- failureLog: "[topicScope] Failed to load direct project topic",
376
- idLogKey: "projectId"
377
- });
378
- return directTopic ?? tryResolveHostTopicByLegacyScope(ctx, legacyProjectId);
379
- }
380
- async function tryReadTopicDoc(ctx, id, log) {
381
- try {
382
- return await ctx.db.get(id);
383
- } catch (error) {
384
- debugGraphPrimitiveFallback(log.failureLog, {
385
- error,
386
- [log.idLogKey]: id
387
- });
388
- return null;
389
- }
390
- }
391
- async function buildTopicScope(ctx, topic, source, options = {}) {
392
- const inherited = await resolveInheritedWorkspaceScope(ctx, topic);
393
- const mapped = asMappedProjectId(topic);
394
- return {
395
- topicId: topic._id,
396
- ...mapped || options.fallbackProjectId ? { projectId: mapped ?? options.fallbackProjectId } : {},
397
- tenantId: inherited.tenantId,
398
- workspaceId: inherited.workspaceId,
399
- source
400
- };
246
+ throw new Error(`Topic not found: ${topicGlobalId}`);
401
247
  }
402
248
  ({
403
249
  projectId: v.optional(v.string()),
@@ -7,4 +7,4 @@ import './convex.js';
7
7
  import '@lucern/access-control/convex';
8
8
  import '@lucern/contracts/convex/unsafeAnyApi';
9
9
  import './epistemicEdges.types.js';
10
- import './topicScope-DJVa0mLa.js';
10
+ import './topicScope-CL1IVOmv.js';
@@ -3,7 +3,7 @@ import { Id, QueryCtx } from './convex.js';
3
3
  import { EdgeStatusResult, WorkspaceEdgeScope, WorkspaceScopedEdge } from './epistemicEdges.types.js';
4
4
  import '@lucern/access-control/convex';
5
5
  import '@lucern/contracts/convex/unsafeAnyApi';
6
- import './topicScope-DJVa0mLa.js';
6
+ import './topicScope-CL1IVOmv.js';
7
7
 
8
8
  declare const epistemicLayerValidator: convex_values.VUnion<"L4" | "L3" | "L2" | "L1" | "ontological" | "organizational", [convex_values.VLiteral<"L4", "required">, convex_values.VLiteral<"L3", "required">, convex_values.VLiteral<"L2", "required">, convex_values.VLiteral<"L1", "required">, convex_values.VLiteral<"ontological", "required">, convex_values.VLiteral<"organizational", "required">], "required", never>;
9
9
  declare const subjectiveOpinionValidator: convex_values.VObject<{
@@ -3,7 +3,7 @@ import { REASONING_METHODS } from '@lucern/contracts/types/reasoning-method';
3
3
  import { v } from 'convex/values';
4
4
  import { unsafeConvexAnyApi } from '@lucern/contracts/convex/unsafeAnyApi';
5
5
  import { componentsGeneric, internalMutationGeneric, mutationGeneric, queryGeneric } from 'convex/server';
6
- import { assertUuidV7Identity, assertStorageEdgeVocabulary, assertUuidShapedEdgeEndpoint } from '@lucern/contracts/ids';
6
+ import { assertUuidV7Identity, assertStorageEdgeVocabulary, assertUuidShapedEdgeEndpoint, isUuidV7 } from '@lucern/contracts/ids';
7
7
  import { assertEdgePolicyAllowed } from '@lucern/contracts/manifests/edge-policy-manifest';
8
8
  import { edgePolicyManifest } from '@lucern/contracts/manifests/edge-policy-manifest.data';
9
9
  import { requireScopeWriteAccess, checkScopeAccess } from '@lucern/access-control/access';
@@ -287,16 +287,36 @@ async function resolveTopicNodeScopeOrNull(ctx, ref) {
287
287
  if (!node) {
288
288
  return null;
289
289
  }
290
- const scopeKey = normalizeScopeValue(node.topicId) ?? normalizeScopeValue(node.globalId);
290
+ const scopeKey = canonicalTopicGlobalId(node);
291
291
  if (!scopeKey) {
292
- return null;
292
+ throw new Error(
293
+ `topic.uuidv7_identity_required: topic node ${ref} is missing a UUIDv7 globalId.`
294
+ );
293
295
  }
296
+ const metadata = node.metadata ?? {};
294
297
  return {
295
298
  topicId: scopeKey,
296
299
  projectId: asMappedProjectId(node),
297
- source: "topic_node"
300
+ source: "topic_node",
301
+ tenantId: normalizeScopeValue(node.tenantId) ?? normalizeScopeValue(metadata.tenantId),
302
+ workspaceId: normalizeScopeValue(node.workspaceId) ?? normalizeScopeValue(metadata.workspaceId)
298
303
  };
299
304
  }
305
+ function canonicalTopicGlobalId(node) {
306
+ const globalId = normalizeScopeValue(node.globalId);
307
+ if (globalId && isUuidV7(globalId)) {
308
+ return globalId;
309
+ }
310
+ const topicId = normalizeScopeValue(node.topicId);
311
+ return topicId && isUuidV7(topicId) ? topicId : null;
312
+ }
313
+ function requireUuidV7TopicScope(field, value) {
314
+ if (!isUuidV7(value)) {
315
+ throw new Error(
316
+ `topic.uuidv7_required: ${field} must be a UUIDv7 public topic identifier.`
317
+ );
318
+ }
319
+ }
300
320
  function asMappedProjectId(topic) {
301
321
  if (!topic) {
302
322
  return;
@@ -318,200 +338,25 @@ function normalizeScopeValue(value) {
318
338
  const normalized = value.trim();
319
339
  return normalized.length > 0 ? normalized : void 0;
320
340
  }
321
- function pickPrimaryTopic(candidates) {
322
- return [...candidates].sort((a, b) => {
323
- const depthA = a.depth ?? 9999;
324
- const depthB = b.depth ?? 9999;
325
- if (depthA !== depthB) {
326
- return depthA - depthB;
327
- }
328
- const createdA = a.createdAt ?? Number.MAX_SAFE_INTEGER;
329
- const createdB = b.createdAt ?? Number.MAX_SAFE_INTEGER;
330
- if (createdA !== createdB) {
331
- return createdA - createdB;
332
- }
333
- return String(a.name || "").localeCompare(String(b.name || ""));
334
- })[0];
335
- }
336
- async function findTopicsByScopeAlias(ctx, scopeId) {
337
- const query2 = ctx.db.query("topics");
338
- try {
339
- return await query2.withIndex(
340
- "by_graph_scope_project",
341
- (q) => q.eq(LEGACY_SCOPE_FIELD, scopeId)
342
- ).collect();
343
- } catch (error) {
344
- debugGraphPrimitiveFallback(
345
- "[topicScope] Failed to resolve scope alias via index",
346
- {
347
- error,
348
- scopeId
349
- }
350
- );
351
- const topics = await query2.collect();
352
- return topics.filter((topic) => {
353
- const normalizedGlobalId = normalizeScopeValue(topic.globalId);
354
- const mappedProjectId = asMappedProjectId(topic);
355
- return String(topic._id) === scopeId || normalizedGlobalId === scopeId || mappedProjectId === scopeId;
356
- });
357
- }
358
- }
359
- async function tryResolveHostTopicById(ctx, topicId) {
360
- if (typeof ctx.runQuery !== "function") {
361
- return null;
362
- }
363
- try {
364
- return await ctx.runQuery(api.topics.get, {
365
- id: topicId
366
- }) ?? null;
367
- } catch (error) {
368
- debugGraphPrimitiveFallback(
369
- "[topicScope] Failed to resolve topic by host query",
370
- {
371
- error,
372
- topicId
373
- }
374
- );
375
- return null;
376
- }
377
- }
378
- async function tryResolveHostTopicByLegacyScope(ctx, legacyScopeId) {
379
- if (typeof ctx.runQuery !== "function") {
380
- return null;
381
- }
382
- try {
383
- return await ctx.runQuery(api.topics.getByLegacyScopeId, {
384
- projectId: legacyScopeId
385
- }) ?? null;
386
- } catch (error) {
387
- debugGraphPrimitiveFallback(
388
- "[topicScope] Failed to resolve topic by legacy scope",
389
- {
390
- error,
391
- legacyScopeId
392
- }
393
- );
394
- return null;
395
- }
396
- }
397
- async function resolveInheritedWorkspaceScope(ctx, topic) {
398
- const MAX_DEPTH = 10;
399
- let tenantId = normalizeScopeValue(topic.tenantId);
400
- let workspaceId = normalizeScopeValue(topic.workspaceId);
401
- if (tenantId && workspaceId) {
402
- return { tenantId, workspaceId };
403
- }
404
- let current = topic;
405
- for (let i = 0; i < MAX_DEPTH && current?.parentTopicId; i++) {
406
- current = await ctx.db.get(current.parentTopicId);
407
- if (!current) {
408
- break;
409
- }
410
- if (!tenantId) {
411
- tenantId = normalizeScopeValue(current.tenantId);
412
- }
413
- if (!workspaceId) {
414
- workspaceId = normalizeScopeValue(current.workspaceId);
415
- }
416
- if (tenantId && workspaceId) {
417
- break;
418
- }
419
- }
420
- return { tenantId, workspaceId };
421
- }
422
341
  async function resolveTopicProjectScope(ctx, args) {
423
342
  if (args.topicId) {
424
343
  return await resolveScopeFromTopicId(ctx, args.topicId);
425
344
  }
426
345
  if (args.projectId) {
427
- return await resolveScopeFromLegacyProjectId(ctx, args.projectId);
346
+ throw new Error(
347
+ "topic.uuidv7_required: projectId scope aliases are retired; pass topicId as the UUIDv7 topic globalId."
348
+ );
428
349
  }
429
- throw new Error(
430
- "Missing scope: provide topicId (preferred) or legacy projectId alias."
431
- );
350
+ throw new Error("topic.uuidv7_required: Missing scope: provide topicId.");
432
351
  }
433
352
  async function resolveScopeFromTopicId(ctx, topicId) {
434
- const topic = await resolveTopicDocFromTopicId(ctx, topicId);
435
- if (topic) {
436
- return await buildTopicScope(ctx, topic, "topic");
437
- }
438
- const nodeScope = await resolveTopicNodeScopeOrNull(ctx, String(topicId));
353
+ const topicGlobalId = String(topicId);
354
+ requireUuidV7TopicScope("topicId", topicGlobalId);
355
+ const nodeScope = await resolveTopicNodeScopeOrNull(ctx, topicGlobalId);
439
356
  if (nodeScope) {
440
357
  return nodeScope;
441
358
  }
442
- throw new Error(`Topic not found: ${String(topicId)}`);
443
- }
444
- async function resolveTopicDocFromTopicId(ctx, topicId) {
445
- const direct = await tryReadTopicDoc(ctx, topicId, {
446
- failureLog: "[topicScope] Failed to load topic by direct id",
447
- idLogKey: "topicId"
448
- });
449
- if (direct) {
450
- return direct;
451
- }
452
- const hostTopic = await tryResolveHostTopicById(ctx, String(topicId));
453
- if (hostTopic) {
454
- return hostTopic;
455
- }
456
- return pickPrimaryTopic(await findTopicsByScopeAlias(ctx, String(topicId))) ?? null;
457
- }
458
- async function resolveScopeFromLegacyProjectId(ctx, legacyProjectId) {
459
- const directTopic = await resolveDirectLegacyProjectTopic(
460
- ctx,
461
- legacyProjectId
462
- );
463
- if (directTopic) {
464
- return await buildTopicScope(ctx, directTopic, "topic_inferred", {
465
- fallbackProjectId: legacyProjectId
466
- });
467
- }
468
- const primary = pickPrimaryTopic(
469
- await findTopicsByScopeAlias(ctx, legacyProjectId)
470
- );
471
- if (primary) {
472
- return await buildTopicScope(ctx, primary, "project_mapped_topic", {
473
- fallbackProjectId: legacyProjectId
474
- });
475
- }
476
- const nodeScope = await resolveTopicNodeScopeOrNull(ctx, legacyProjectId);
477
- if (nodeScope) {
478
- return {
479
- ...nodeScope,
480
- projectId: nodeScope.projectId ?? legacyProjectId
481
- };
482
- }
483
- throw new Error(
484
- `Legacy project scope ${legacyProjectId} has no mapped topic.`
485
- );
486
- }
487
- async function resolveDirectLegacyProjectTopic(ctx, legacyProjectId) {
488
- const directTopic = await tryReadTopicDoc(ctx, legacyProjectId, {
489
- failureLog: "[topicScope] Failed to load direct project topic",
490
- idLogKey: "projectId"
491
- });
492
- return directTopic ?? tryResolveHostTopicByLegacyScope(ctx, legacyProjectId);
493
- }
494
- async function tryReadTopicDoc(ctx, id, log) {
495
- try {
496
- return await ctx.db.get(id);
497
- } catch (error) {
498
- debugGraphPrimitiveFallback(log.failureLog, {
499
- error,
500
- [log.idLogKey]: id
501
- });
502
- return null;
503
- }
504
- }
505
- async function buildTopicScope(ctx, topic, source, options = {}) {
506
- const inherited = await resolveInheritedWorkspaceScope(ctx, topic);
507
- const mapped = asMappedProjectId(topic);
508
- return {
509
- topicId: topic._id,
510
- ...mapped || options.fallbackProjectId ? { projectId: mapped ?? options.fallbackProjectId } : {},
511
- tenantId: inherited.tenantId,
512
- workspaceId: inherited.workspaceId,
513
- source
514
- };
359
+ throw new Error(`Topic not found: ${topicGlobalId}`);
515
360
  }
516
361
  var optionalScopeArgs = {
517
362
  projectId: v.optional(v.string()),