@lucern/graph-primitives 1.0.23 → 1.0.24

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 (92) hide show
  1. package/dist/beliefDecay.js +3 -0
  2. package/dist/beliefDecay.js.map +1 -1
  3. package/dist/beliefEvidenceLinks.js +15 -3
  4. package/dist/beliefEvidenceLinks.js.map +1 -1
  5. package/dist/beliefEvidenceLinks.operational.js +12 -3
  6. package/dist/beliefEvidenceLinks.operational.js.map +1 -1
  7. package/dist/entityCanonicalMatch.d.ts +40 -0
  8. package/dist/entityCanonicalMatch.js +33 -0
  9. package/dist/entityCanonicalMatch.js.map +1 -0
  10. package/dist/entityLifecycle.js +62 -37
  11. package/dist/entityLifecycle.js.map +1 -1
  12. package/dist/epistemicAnswers.js.map +1 -1
  13. package/dist/epistemicBeliefs.admin.js +17 -6
  14. package/dist/epistemicBeliefs.admin.js.map +1 -1
  15. package/dist/epistemicBeliefs.backfills.js +13 -2
  16. package/dist/epistemicBeliefs.backfills.js.map +1 -1
  17. package/dist/epistemicBeliefs.confidence.js +17 -6
  18. package/dist/epistemicBeliefs.confidence.js.map +1 -1
  19. package/dist/epistemicBeliefs.core.js +32 -9
  20. package/dist/epistemicBeliefs.core.js.map +1 -1
  21. package/dist/epistemicBeliefs.forkEvidence.js +13 -2
  22. package/dist/epistemicBeliefs.forkEvidence.js.map +1 -1
  23. package/dist/epistemicBeliefs.helpers.d.ts +16 -1
  24. package/dist/epistemicBeliefs.helpers.js +20 -6
  25. package/dist/epistemicBeliefs.helpers.js.map +1 -1
  26. package/dist/epistemicBeliefs.internal.js +28 -5
  27. package/dist/epistemicBeliefs.internal.js.map +1 -1
  28. package/dist/epistemicBeliefs.js +32 -9
  29. package/dist/epistemicBeliefs.js.map +1 -1
  30. package/dist/epistemicBeliefs.lifecycle.js +17 -6
  31. package/dist/epistemicBeliefs.lifecycle.js.map +1 -1
  32. package/dist/epistemicBeliefs.links.js +29 -9
  33. package/dist/epistemicBeliefs.links.js.map +1 -1
  34. package/dist/epistemicBeliefs.topicAnchor.js +12 -3
  35. package/dist/epistemicBeliefs.topicAnchor.js.map +1 -1
  36. package/dist/epistemicContracts.evaluators.js +17 -6
  37. package/dist/epistemicContracts.evaluators.js.map +1 -1
  38. package/dist/epistemicContracts.handlers.js +17 -6
  39. package/dist/epistemicContracts.handlers.js.map +1 -1
  40. package/dist/epistemicContracts.js +17 -6
  41. package/dist/epistemicContracts.js.map +1 -1
  42. package/dist/epistemicEdges.handlers.js +12 -3
  43. package/dist/epistemicEdges.handlers.js.map +1 -1
  44. package/dist/epistemicEdges.helpers.d.ts +2 -2
  45. package/dist/epistemicEdges.js +15 -3
  46. package/dist/epistemicEdges.js.map +1 -1
  47. package/dist/epistemicEdges.mutations.js +3 -0
  48. package/dist/epistemicEdges.mutations.js.map +1 -1
  49. package/dist/epistemicEvidence.js +15 -3
  50. package/dist/epistemicEvidence.js.map +1 -1
  51. package/dist/epistemicEvidenceHelpers.js +3 -0
  52. package/dist/epistemicEvidenceHelpers.js.map +1 -1
  53. package/dist/epistemicEvidenceMutations.js +15 -3
  54. package/dist/epistemicEvidenceMutations.js.map +1 -1
  55. package/dist/epistemicEvidenceQueries.js +3 -0
  56. package/dist/epistemicEvidenceQueries.js.map +1 -1
  57. package/dist/epistemicHelpers.js.map +1 -1
  58. package/dist/epistemicInsert.js +12 -3
  59. package/dist/epistemicInsert.js.map +1 -1
  60. package/dist/epistemicNodeCreation.js.map +1 -1
  61. package/dist/epistemicNodes.internal.js.map +1 -1
  62. package/dist/epistemicNodes.js.map +1 -1
  63. package/dist/epistemicNodes.mutations.js.map +1 -1
  64. package/dist/epistemicNodes.validators.d.ts +2 -2
  65. package/dist/epistemicQuestions.conviction.js +3 -0
  66. package/dist/epistemicQuestions.conviction.js.map +1 -1
  67. package/dist/epistemicQuestions.create.js +3 -0
  68. package/dist/epistemicQuestions.create.js.map +1 -1
  69. package/dist/epistemicQuestions.evidence.js +3 -0
  70. package/dist/epistemicQuestions.evidence.js.map +1 -1
  71. package/dist/epistemicQuestions.helpers.js +3 -0
  72. package/dist/epistemicQuestions.helpers.js.map +1 -1
  73. package/dist/epistemicQuestions.js +3 -0
  74. package/dist/epistemicQuestions.js.map +1 -1
  75. package/dist/epistemicQuestions.lifecycle.js +3 -0
  76. package/dist/epistemicQuestions.lifecycle.js.map +1 -1
  77. package/dist/epistemicQuestions.queries.js +3 -0
  78. package/dist/epistemicQuestions.queries.js.map +1 -1
  79. package/dist/epistemicQuestions.tail.js +3 -0
  80. package/dist/epistemicQuestions.tail.js.map +1 -1
  81. package/dist/epistemicSources.js.map +1 -1
  82. package/dist/index.js +78 -42
  83. package/dist/index.js.map +1 -1
  84. package/dist/proof-attestation.json +1 -1
  85. package/dist/questionEvidenceLinks.js +3 -0
  86. package/dist/questionEvidenceLinks.js.map +1 -1
  87. package/dist/resolvers.js +3 -0
  88. package/dist/resolvers.js.map +1 -1
  89. package/dist/topicProjectOverlay.d.ts +4 -0
  90. package/dist/topicProjectOverlay.js +3 -0
  91. package/dist/topicProjectOverlay.js.map +1 -1
  92. package/package.json +4 -4
@@ -1,10 +1,19 @@
1
1
  import { componentsGeneric, anyApi } from 'convex/server';
2
- import { generateGlobalId, assertUuidV7Identity, assertStorageEdgeVocabulary, assertCanonicalEdgeEndpoint } from '@lucern/contracts/ids';
2
+ import { generateGlobalId, assertUuidV7Identity, assertStorageEdgeVocabulary, assertUuidShapedEdgeEndpoint } from '@lucern/contracts/ids';
3
3
  import { assertEdgePolicyAllowed, edgePolicyManifest } from '@lucern/contracts';
4
4
 
5
5
  // src/convex.ts
6
6
  componentsGeneric();
7
7
  var internal = anyApi;
8
+ async function assertExistingNodeEndpoint(ctx, endpointRole, endpoint) {
9
+ assertUuidShapedEdgeEndpoint(endpointRole, endpoint);
10
+ const node = await ctx.db.query("epistemicNodes").withIndex("by_globalId", (q) => q.eq("globalId", endpoint)).first();
11
+ if (!node) {
12
+ throw new Error(
13
+ `edge_endpoint_not_canonical: epistemicEdges insert requires ${endpointRole} to be the globalId of an existing epistemicNodes row, received ${endpoint} (no node with that globalId)`
14
+ );
15
+ }
16
+ }
8
17
  async function insertEpistemicEdge(ctx, doc) {
9
18
  assertUuidV7Identity("epistemicEdges", doc.globalId);
10
19
  assertStorageEdgeVocabulary(doc.edgeType);
@@ -18,8 +27,8 @@ async function insertEpistemicEdge(ctx, doc) {
18
27
  "edge_endpoint_missing: epistemicEdges insert requires a non-empty toNodeId"
19
28
  );
20
29
  }
21
- assertCanonicalEdgeEndpoint("fromNodeId", doc.fromNodeId);
22
- assertCanonicalEdgeEndpoint("toNodeId", doc.toNodeId);
30
+ await assertExistingNodeEndpoint(ctx, "fromNodeId", doc.fromNodeId);
31
+ await assertExistingNodeEndpoint(ctx, "toNodeId", doc.toNodeId);
23
32
  if (doc.fromNodeType && doc.toNodeType && doc.edgeType !== "extracted_from") {
24
33
  assertEdgePolicyAllowed(
25
34
  edgePolicyManifest,
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/convex.ts","../src/epistemicInsert.ts","../src/beliefEvidenceLinks.operational.ts"],"names":[],"mappings":"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","file":"beliefEvidenceLinks.operational.js","sourcesContent":["import {\n actionGeneric,\n anyApi,\n componentsGeneric,\n httpActionGeneric,\n internalActionGeneric,\n internalMutationGeneric,\n internalQueryGeneric,\n mutationGeneric,\n queryGeneric,\n} from \"convex/server\";\nimport type { GenericId } from \"convex/values\";\n\nexport const api = anyApi as any;\nexport const components = componentsGeneric() as any;\nexport const internal = anyApi as any;\n\nexport type TableNames = string;\nexport type Id<TableName extends TableNames = string> = GenericId<TableName>;\nexport type Doc<TableName extends TableNames = string> = any;\nexport type DataModel = any;\ntype IndexRangeBuilder = {\n field(fieldName: string): string;\n eq(fieldName: string, value: unknown): IndexRangeBuilder;\n gt(fieldName: string, value: unknown): IndexRangeBuilder;\n gte(fieldName: string, value: unknown): IndexRangeBuilder;\n lt(fieldName: string, value: unknown): IndexRangeBuilder;\n lte(fieldName: string, value: unknown): IndexRangeBuilder;\n};\ntype FilterBuilder = {\n eq(left: unknown, right: unknown): unknown;\n neq(left: unknown, right: unknown): unknown;\n gt(left: unknown, right: unknown): unknown;\n gte(left: unknown, right: unknown): unknown;\n lt(left: unknown, right: unknown): unknown;\n lte(left: unknown, right: unknown): unknown;\n and(...clauses: unknown[]): unknown;\n or(...clauses: unknown[]): unknown;\n field(fieldName: string): unknown;\n};\ntype QueryInitializer<TableName extends TableNames> = {\n withIndex(\n indexName: string,\n range?: (q: any) => unknown\n ): QueryInitializer<TableName>;\n filter(predicate: (q: any) => unknown): QueryInitializer<TableName>;\n order(direction: \"asc\" | \"desc\"): QueryInitializer<TableName>;\n collect(): Promise<Doc<TableName>[]>;\n take(limit: number): Promise<Doc<TableName>[]>;\n first(): Promise<Doc<TableName> | null>;\n unique(): Promise<Doc<TableName> | null>;\n};\nexport type DatabaseReader = {\n get<TableName extends TableNames>(\n id: Id<TableName>\n ): Promise<Doc<TableName> | null>;\n query<TableName extends TableNames>(\n tableName: TableName\n ): QueryInitializer<TableName>;\n normalizeId?<TableName extends TableNames>(\n tableName: TableName,\n id: string\n ): Id<TableName> | null;\n};\nexport type DatabaseWriter = DatabaseReader & {\n insert<TableName extends TableNames>(\n tableName: TableName,\n value: Record<string, unknown>\n ): Promise<Id<TableName>>;\n patch<TableName extends TableNames>(\n id: Id<TableName>,\n value: Record<string, unknown>\n ): Promise<void>;\n replace<TableName extends TableNames>(\n id: Id<TableName>,\n value: Record<string, unknown>\n ): Promise<void>;\n delete<TableName extends TableNames>(id: Id<TableName>): Promise<void>;\n};\ntype Scheduler = {\n runAfter(delayMs: number, functionReference: unknown, args?: unknown): Promise<void>;\n};\ntype AuthReader = {\n getUserIdentity(): Promise<unknown>;\n};\ntype RuntimeInvoker = {\n runQuery(functionReference: unknown, args?: unknown): Promise<any>;\n runMutation(functionReference: unknown, args?: unknown): Promise<any>;\n runAction(functionReference: unknown, args?: unknown): Promise<any>;\n};\nexport type QueryCtx = RuntimeInvoker & {\n auth: AuthReader;\n db: DatabaseReader;\n scheduler: Scheduler;\n};\nexport type MutationCtx = RuntimeInvoker & {\n auth: AuthReader;\n db: DatabaseWriter;\n scheduler: Scheduler;\n};\nexport type ActionCtx = RuntimeInvoker & {\n auth: AuthReader;\n scheduler: Scheduler;\n};\n\ntype ConvexFunctionBuilder<Ctx> = <\n Definition extends { handler?: (ctx: Ctx, args: any) => any },\n>(\n definition: Definition\n) => any;\n\nexport const action = actionGeneric as unknown as ConvexFunctionBuilder<ActionCtx>;\nexport const httpAction =\n httpActionGeneric as unknown as ConvexFunctionBuilder<ActionCtx>;\nexport const internalAction =\n internalActionGeneric as unknown as ConvexFunctionBuilder<ActionCtx>;\nexport const internalMutation =\n internalMutationGeneric as unknown as ConvexFunctionBuilder<MutationCtx>;\nexport const internalQuery =\n internalQueryGeneric as unknown as ConvexFunctionBuilder<QueryCtx>;\nexport const mutation =\n mutationGeneric as unknown as ConvexFunctionBuilder<MutationCtx>;\nexport const query = queryGeneric as unknown as ConvexFunctionBuilder<QueryCtx>;\n","import type { WithoutSystemFields } from \"convex/server\";\nimport {\n assertUuidV7Identity,\n assertStorageEdgeVocabulary,\n assertCanonicalEdgeEndpoint,\n} from \"@lucern/contracts/ids\";\nimport {\n assertEdgePolicyAllowed,\n edgePolicyManifest,\n type EpistemicNodeType,\n} from \"@lucern/contracts\";\nimport type { Doc, Id, MutationCtx } from \"./convex\";\n\nexport async function insertEpistemicNode(\n ctx: MutationCtx,\n doc: WithoutSystemFields<Doc<\"epistemicNodes\">>,\n): Promise<Id<\"epistemicNodes\">> {\n assertUuidV7Identity(\"epistemicNodes\", doc.globalId);\n return ctx.db.insert(\"epistemicNodes\", doc);\n}\n\nexport async function insertEpistemicEdge(\n ctx: MutationCtx,\n doc: WithoutSystemFields<Doc<\"epistemicEdges\">>,\n): Promise<Id<\"epistemicEdges\">> {\n assertUuidV7Identity(\"epistemicEdges\", doc.globalId);\n\n // R1.1a — STORAGE-VOCABULARY MEMBERSHIP\n assertStorageEdgeVocabulary(doc.edgeType);\n\n // R1.1b — ENDPOINT PRESENCE\n if (!doc.fromNodeId || typeof doc.fromNodeId !== \"string\") {\n throw new Error(\n \"edge_endpoint_missing: epistemicEdges insert requires a non-empty fromNodeId\"\n );\n }\n if (!doc.toNodeId || typeof doc.toNodeId !== \"string\") {\n throw new Error(\n \"edge_endpoint_missing: epistemicEdges insert requires a non-empty toNodeId\"\n );\n }\n\n // C2-RR.4 Defect E — ENDPOINT CANONICAL IDENTITY. Edge endpoints must be the\n // UUIDv7 globalIds of the connected nodes, never Convex doc ids or legacy\n // topic ids. This refuses the mixed _id/globalId endpoint writes at the floor.\n assertCanonicalEdgeEndpoint(\"fromNodeId\", doc.fromNodeId);\n assertCanonicalEdgeEndpoint(\"toNodeId\", doc.toNodeId);\n\n // R1.1c — FULL POLICY ASSERT (when nodeTypes are present and edgeType is in public manifest)\n if (doc.fromNodeType && doc.toNodeType && doc.edgeType !== \"extracted_from\") {\n assertEdgePolicyAllowed(\n edgePolicyManifest,\n doc.edgeType,\n {\n kind: \"epistemic_node\",\n nodeId: doc.fromNodeId,\n nodeType: doc.fromNodeType as EpistemicNodeType,\n },\n {\n kind: \"epistemic_node\",\n nodeId: doc.toNodeId,\n nodeType: doc.toNodeType as EpistemicNodeType,\n }\n );\n }\n\n return ctx.db.insert(\"epistemicEdges\", doc);\n}\n","import type { Id, MutationCtx } from \"./convex\";\nimport { internal } from \"./convex\";\nimport { generateGlobalId } from \"./globalId\";\nimport { insertEpistemicEdge } from \"./epistemicInsert.js\";\n\ntype BeliefEvidenceRelation = \"supports\" | \"contradicts\";\ntype IndexRangeBuilder = {\n eq(fieldName: string, value: unknown): IndexRangeBuilder;\n};\ntype EpistemicNode = {\n _id: Id<\"epistemicNodes\">;\n nodeType?: string;\n globalId?: string;\n projectId?: string;\n topicId?: string | Id<\"topics\">;\n supportingInsightIds?: Id<\"epistemicNodes\">[];\n contradictingInsightIds?: Id<\"epistemicNodes\">[];\n};\n\nfunction assertEvidenceImpact(args: {\n relation: BeliefEvidenceRelation;\n weight: number;\n}) {\n if (\n !Number.isFinite(args.weight) ||\n args.weight === 0 ||\n args.weight < -1 ||\n args.weight > 1\n ) {\n throw new Error(\"Belief evidence links require explicit nonzero weight in [-1, 1]\");\n }\n if (args.relation === \"supports\" && args.weight < 0) {\n throw new Error(\"Supporting evidence links require positive weight\");\n }\n if (args.relation === \"contradicts\" && args.weight > 0) {\n throw new Error(\"Contradicting evidence links require negative weight\");\n }\n}\n\nexport async function applyOperationalLinkEffects(\n ctx: MutationCtx,\n args: {\n beliefNode: EpistemicNode;\n beliefId: Id<\"epistemicNodes\">;\n insightId: Id<\"epistemicNodes\">;\n relation: BeliefEvidenceRelation;\n weight: number;\n rationale?: string;\n createdBy: string;\n },\n): Promise<void> {\n assertEvidenceImpact({ relation: args.relation, weight: args.weight });\n const confidence = Math.abs(args.weight);\n const currentSupporting = args.beliefNode.supportingInsightIds ?? [];\n const currentContradicting = args.beliefNode.contradictingInsightIds ?? [];\n\n if (args.relation === \"supports\" && !currentSupporting.includes(args.insightId)) {\n await ctx.db.patch(args.beliefId, {\n supportingInsightIds: [...currentSupporting, args.insightId],\n contradictingInsightIds: currentContradicting.filter(\n (id: Id<\"epistemicNodes\">) => id !== args.insightId,\n ),\n });\n } else if (\n args.relation === \"contradicts\" &&\n !currentContradicting.includes(args.insightId)\n ) {\n await ctx.db.patch(args.beliefId, {\n contradictingInsightIds: [...currentContradicting, args.insightId],\n supportingInsightIds: currentSupporting.filter(\n (id: Id<\"epistemicNodes\">) => id !== args.insightId,\n ),\n });\n }\n\n try {\n const beliefSpineNode = await ctx.db.get(args.beliefId);\n const evidenceSpineNode = await ctx.db.get(args.insightId);\n\n if (\n beliefSpineNode?.nodeType === \"belief\" &&\n evidenceSpineNode?.nodeType === \"evidence\"\n ) {\n const existingEdges = await ctx.db\n .query(\"epistemicEdges\")\n .withIndex(\"by_from_to\", (q: IndexRangeBuilder) =>\n q\n .eq(\"fromNodeId\", evidenceSpineNode.globalId)\n .eq(\"toNodeId\", beliefSpineNode.globalId),\n )\n .collect();\n\n for (const edge of existingEdges) {\n if (edge.edgeType === \"informs\") {\n await ctx.scheduler.runAfter(0, internal.neo4jEdgeAPI.deleteEdge, {\n globalId: edge.globalId,\n });\n await ctx.db.delete(edge._id);\n }\n }\n\n const globalId = generateGlobalId();\n const context = args.rationale || `Linked as ${args.relation}`;\n await insertEpistemicEdge(ctx, {\n globalId,\n // C2-RR.4 Defect E — canonical UUIDv7 endpoints, not Convex doc ids.\n fromNodeId: evidenceSpineNode.globalId,\n toNodeId: beliefSpineNode.globalId,\n sourceGlobalId: evidenceSpineNode.globalId,\n targetGlobalId: beliefSpineNode.globalId,\n edgeType: \"informs\",\n weight: args.weight,\n confidence,\n context,\n reasoningMethod: \"testimonial\",\n derivationType: \"evidence_sl_scoring\",\n metadata: {\n relation: args.relation,\n confidence,\n impactScore: args.weight,\n invariant: \"evidence.belief_impact_required\",\n },\n createdBy: args.createdBy,\n createdAt: Date.now(),\n updatedAt: Date.now(),\n projectId: args.beliefNode.projectId,\n topicId: args.beliefNode.topicId ? String(args.beliefNode.topicId) : undefined,\n fromNodeType: \"evidence\",\n toNodeType: \"belief\",\n fromLayer: \"L2\",\n toLayer: \"L3\",\n });\n\n await ctx.scheduler.runAfter(0, internal.neo4jEdgeAPI.createEdge, {\n globalId,\n fromGlobalId: evidenceSpineNode.globalId,\n toGlobalId: beliefSpineNode.globalId,\n edgeType: \"informs\",\n weight: args.weight,\n confidence,\n context,\n projectId: args.beliefNode.projectId ? String(args.beliefNode.projectId) : undefined,\n createdBy: args.createdBy,\n fromNodeType: \"evidence\",\n toNodeType: \"belief\",\n fromLayer: \"L2\",\n toLayer: \"L3\",\n });\n }\n } catch (e) {\n console.error(\"[EpistemicSpine] Failed to create informs edge:\", e);\n }\n\n if (args.beliefNode.projectId) {\n await ctx.scheduler.runAfter(0, \"verificationActions:deepVerifyEvidence\", {\n insightId: args.insightId,\n targetType: \"belief\",\n targetId: args.beliefId,\n projectId: args.beliefNode.projectId,\n userId: args.createdBy,\n });\n }\n}\n\nexport async function removeOperationalLinkEffects(\n ctx: MutationCtx,\n args: {\n beliefId: Id<\"epistemicNodes\">;\n insightId: Id<\"epistemicNodes\">;\n relation: BeliefEvidenceRelation;\n },\n): Promise<void> {\n const belief = await ctx.db.get(args.beliefId);\n if (belief) {\n const currentSupporting = belief.supportingInsightIds ?? [];\n const currentContradicting = belief.contradictingInsightIds ?? [];\n\n if (args.relation === \"supports\") {\n await ctx.db.patch(args.beliefId, {\n supportingInsightIds: currentSupporting.filter(\n (id: Id<\"epistemicNodes\">) => id !== args.insightId,\n ),\n });\n } else {\n await ctx.db.patch(args.beliefId, {\n contradictingInsightIds: currentContradicting.filter(\n (id: Id<\"epistemicNodes\">) => id !== args.insightId,\n ),\n });\n }\n }\n\n try {\n const beliefSpineNode = await ctx.db.get(args.beliefId);\n const evidenceSpineNode = await ctx.db.get(args.insightId);\n\n if (\n beliefSpineNode?.nodeType === \"belief\" &&\n evidenceSpineNode?.nodeType === \"evidence\"\n ) {\n const edges = await ctx.db\n .query(\"epistemicEdges\")\n .withIndex(\"by_from_to\", (q: IndexRangeBuilder) =>\n q.eq(\"fromNodeId\", evidenceSpineNode._id).eq(\"toNodeId\", beliefSpineNode._id),\n )\n .collect();\n\n for (const edge of edges) {\n if (edge.edgeType === \"informs\") {\n await ctx.scheduler.runAfter(0, internal.neo4jEdgeAPI.deleteEdge, {\n globalId: edge.globalId,\n });\n await ctx.db.delete(edge._id);\n }\n }\n }\n } catch (e) {\n console.error(\"[EpistemicSpine] Failed to remove informs edge:\", e);\n }\n}\n"]}
1
+ {"version":3,"sources":["../src/convex.ts","../src/epistemicInsert.ts","../src/beliefEvidenceLinks.operational.ts"],"names":[],"mappings":"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","file":"beliefEvidenceLinks.operational.js","sourcesContent":["import {\n actionGeneric,\n anyApi,\n componentsGeneric,\n httpActionGeneric,\n internalActionGeneric,\n internalMutationGeneric,\n internalQueryGeneric,\n mutationGeneric,\n queryGeneric,\n} from \"convex/server\";\nimport type { GenericId } from \"convex/values\";\n\nexport const api = anyApi as any;\nexport const components = componentsGeneric() as any;\nexport const internal = anyApi as any;\n\nexport type TableNames = string;\nexport type Id<TableName extends TableNames = string> = GenericId<TableName>;\nexport type Doc<TableName extends TableNames = string> = any;\nexport type DataModel = any;\ntype IndexRangeBuilder = {\n field(fieldName: string): string;\n eq(fieldName: string, value: unknown): IndexRangeBuilder;\n gt(fieldName: string, value: unknown): IndexRangeBuilder;\n gte(fieldName: string, value: unknown): IndexRangeBuilder;\n lt(fieldName: string, value: unknown): IndexRangeBuilder;\n lte(fieldName: string, value: unknown): IndexRangeBuilder;\n};\ntype FilterBuilder = {\n eq(left: unknown, right: unknown): unknown;\n neq(left: unknown, right: unknown): unknown;\n gt(left: unknown, right: unknown): unknown;\n gte(left: unknown, right: unknown): unknown;\n lt(left: unknown, right: unknown): unknown;\n lte(left: unknown, right: unknown): unknown;\n and(...clauses: unknown[]): unknown;\n or(...clauses: unknown[]): unknown;\n field(fieldName: string): unknown;\n};\ntype QueryInitializer<TableName extends TableNames> = {\n withIndex(\n indexName: string,\n range?: (q: any) => unknown\n ): QueryInitializer<TableName>;\n filter(predicate: (q: any) => unknown): QueryInitializer<TableName>;\n order(direction: \"asc\" | \"desc\"): QueryInitializer<TableName>;\n collect(): Promise<Doc<TableName>[]>;\n take(limit: number): Promise<Doc<TableName>[]>;\n first(): Promise<Doc<TableName> | null>;\n unique(): Promise<Doc<TableName> | null>;\n};\nexport type DatabaseReader = {\n get<TableName extends TableNames>(\n id: Id<TableName>\n ): Promise<Doc<TableName> | null>;\n query<TableName extends TableNames>(\n tableName: TableName\n ): QueryInitializer<TableName>;\n normalizeId?<TableName extends TableNames>(\n tableName: TableName,\n id: string\n ): Id<TableName> | null;\n};\nexport type DatabaseWriter = DatabaseReader & {\n insert<TableName extends TableNames>(\n tableName: TableName,\n value: Record<string, unknown>\n ): Promise<Id<TableName>>;\n patch<TableName extends TableNames>(\n id: Id<TableName>,\n value: Record<string, unknown>\n ): Promise<void>;\n replace<TableName extends TableNames>(\n id: Id<TableName>,\n value: Record<string, unknown>\n ): Promise<void>;\n delete<TableName extends TableNames>(id: Id<TableName>): Promise<void>;\n};\ntype Scheduler = {\n runAfter(delayMs: number, functionReference: unknown, args?: unknown): Promise<void>;\n};\ntype AuthReader = {\n getUserIdentity(): Promise<unknown>;\n};\ntype RuntimeInvoker = {\n runQuery(functionReference: unknown, args?: unknown): Promise<any>;\n runMutation(functionReference: unknown, args?: unknown): Promise<any>;\n runAction(functionReference: unknown, args?: unknown): Promise<any>;\n};\nexport type QueryCtx = RuntimeInvoker & {\n auth: AuthReader;\n db: DatabaseReader;\n scheduler: Scheduler;\n};\nexport type MutationCtx = RuntimeInvoker & {\n auth: AuthReader;\n db: DatabaseWriter;\n scheduler: Scheduler;\n};\nexport type ActionCtx = RuntimeInvoker & {\n auth: AuthReader;\n scheduler: Scheduler;\n};\n\ntype ConvexFunctionBuilder<Ctx> = <\n Definition extends { handler?: (ctx: Ctx, args: any) => any },\n>(\n definition: Definition\n) => any;\n\nexport const action = actionGeneric as unknown as ConvexFunctionBuilder<ActionCtx>;\nexport const httpAction =\n httpActionGeneric as unknown as ConvexFunctionBuilder<ActionCtx>;\nexport const internalAction =\n internalActionGeneric as unknown as ConvexFunctionBuilder<ActionCtx>;\nexport const internalMutation =\n internalMutationGeneric as unknown as ConvexFunctionBuilder<MutationCtx>;\nexport const internalQuery =\n internalQueryGeneric as unknown as ConvexFunctionBuilder<QueryCtx>;\nexport const mutation =\n mutationGeneric as unknown as ConvexFunctionBuilder<MutationCtx>;\nexport const query = queryGeneric as unknown as ConvexFunctionBuilder<QueryCtx>;\n","import type { WithoutSystemFields } from \"convex/server\";\nimport {\n assertUuidV7Identity,\n assertStorageEdgeVocabulary,\n assertUuidShapedEdgeEndpoint,\n} from \"@lucern/contracts/ids\";\nimport {\n assertEdgePolicyAllowed,\n edgePolicyManifest,\n type EpistemicNodeType,\n} from \"@lucern/contracts\";\nimport type { Doc, Id, MutationCtx } from \"./convex\";\n\nexport async function insertEpistemicNode(\n ctx: MutationCtx,\n doc: WithoutSystemFields<Doc<\"epistemicNodes\">>,\n): Promise<Id<\"epistemicNodes\">> {\n assertUuidV7Identity(\"epistemicNodes\", doc.globalId);\n return ctx.db.insert(\"epistemicNodes\", doc);\n}\n\n/**\n * C2-RR.4b — REFERENTIAL edge-endpoint canonicality (amends RR.4's FORMAT guard).\n *\n * Proves an endpoint is the `globalId` of an EXISTING `epistemicNodes` row, not\n * that it has a v7 shape. Refuses doc-ids and fabricated/unknown endpoints while\n * keeping legacy v4-`globalId` nodes (the established prod corpus) linkable.\n * SHAPE pre-filter accepts both v4 and v7; the indexed `by_globalId` lookup is\n * the referential authority (no full scan). v7-format stays enforced only at new\n * identity minting (`assertUuidV7Identity`). See c2-rr4b-endpoint-referential.\n */\nasync function assertExistingNodeEndpoint(\n ctx: MutationCtx,\n endpointRole: \"fromNodeId\" | \"toNodeId\",\n endpoint: string,\n): Promise<void> {\n assertUuidShapedEdgeEndpoint(endpointRole, endpoint);\n const node = await ctx.db\n .query(\"epistemicNodes\")\n .withIndex(\"by_globalId\", (q: any) => q.eq(\"globalId\", endpoint))\n .first();\n if (!node) {\n throw new Error(\n `edge_endpoint_not_canonical: epistemicEdges insert requires ${endpointRole} to be the globalId of an existing epistemicNodes row, received ${endpoint} (no node with that globalId)`,\n );\n }\n}\n\nexport async function insertEpistemicEdge(\n ctx: MutationCtx,\n doc: WithoutSystemFields<Doc<\"epistemicEdges\">>,\n): Promise<Id<\"epistemicEdges\">> {\n assertUuidV7Identity(\"epistemicEdges\", doc.globalId);\n\n // R1.1a — STORAGE-VOCABULARY MEMBERSHIP\n assertStorageEdgeVocabulary(doc.edgeType);\n\n // R1.1b — ENDPOINT PRESENCE\n if (!doc.fromNodeId || typeof doc.fromNodeId !== \"string\") {\n throw new Error(\n \"edge_endpoint_missing: epistemicEdges insert requires a non-empty fromNodeId\"\n );\n }\n if (!doc.toNodeId || typeof doc.toNodeId !== \"string\") {\n throw new Error(\n \"edge_endpoint_missing: epistemicEdges insert requires a non-empty toNodeId\"\n );\n }\n\n // C2-RR.4b Defect E (REFERENTIAL) — ENDPOINT CANONICAL IDENTITY. Edge\n // endpoints must each be the globalId of an EXISTING epistemicNodes row,\n // never a Convex doc id, a legacy topic id, or a fabricated/unknown uuid.\n // This refuses the mixed _id/globalId endpoint writes at the floor while\n // keeping legacy v4-globalId nodes linkable (RR.4b prod-incident cure).\n await assertExistingNodeEndpoint(ctx, \"fromNodeId\", doc.fromNodeId);\n await assertExistingNodeEndpoint(ctx, \"toNodeId\", doc.toNodeId);\n\n // R1.1c — FULL POLICY ASSERT (when nodeTypes are present and edgeType is in public manifest)\n if (doc.fromNodeType && doc.toNodeType && doc.edgeType !== \"extracted_from\") {\n assertEdgePolicyAllowed(\n edgePolicyManifest,\n doc.edgeType,\n {\n kind: \"epistemic_node\",\n nodeId: doc.fromNodeId,\n nodeType: doc.fromNodeType as EpistemicNodeType,\n },\n {\n kind: \"epistemic_node\",\n nodeId: doc.toNodeId,\n nodeType: doc.toNodeType as EpistemicNodeType,\n }\n );\n }\n\n return ctx.db.insert(\"epistemicEdges\", doc);\n}\n","import type { Id, MutationCtx } from \"./convex\";\nimport { internal } from \"./convex\";\nimport { generateGlobalId } from \"./globalId\";\nimport { insertEpistemicEdge } from \"./epistemicInsert.js\";\n\ntype BeliefEvidenceRelation = \"supports\" | \"contradicts\";\ntype IndexRangeBuilder = {\n eq(fieldName: string, value: unknown): IndexRangeBuilder;\n};\ntype EpistemicNode = {\n _id: Id<\"epistemicNodes\">;\n nodeType?: string;\n globalId?: string;\n projectId?: string;\n topicId?: string | Id<\"topics\">;\n supportingInsightIds?: Id<\"epistemicNodes\">[];\n contradictingInsightIds?: Id<\"epistemicNodes\">[];\n};\n\nfunction assertEvidenceImpact(args: {\n relation: BeliefEvidenceRelation;\n weight: number;\n}) {\n if (\n !Number.isFinite(args.weight) ||\n args.weight === 0 ||\n args.weight < -1 ||\n args.weight > 1\n ) {\n throw new Error(\"Belief evidence links require explicit nonzero weight in [-1, 1]\");\n }\n if (args.relation === \"supports\" && args.weight < 0) {\n throw new Error(\"Supporting evidence links require positive weight\");\n }\n if (args.relation === \"contradicts\" && args.weight > 0) {\n throw new Error(\"Contradicting evidence links require negative weight\");\n }\n}\n\nexport async function applyOperationalLinkEffects(\n ctx: MutationCtx,\n args: {\n beliefNode: EpistemicNode;\n beliefId: Id<\"epistemicNodes\">;\n insightId: Id<\"epistemicNodes\">;\n relation: BeliefEvidenceRelation;\n weight: number;\n rationale?: string;\n createdBy: string;\n },\n): Promise<void> {\n assertEvidenceImpact({ relation: args.relation, weight: args.weight });\n const confidence = Math.abs(args.weight);\n const currentSupporting = args.beliefNode.supportingInsightIds ?? [];\n const currentContradicting = args.beliefNode.contradictingInsightIds ?? [];\n\n if (args.relation === \"supports\" && !currentSupporting.includes(args.insightId)) {\n await ctx.db.patch(args.beliefId, {\n supportingInsightIds: [...currentSupporting, args.insightId],\n contradictingInsightIds: currentContradicting.filter(\n (id: Id<\"epistemicNodes\">) => id !== args.insightId,\n ),\n });\n } else if (\n args.relation === \"contradicts\" &&\n !currentContradicting.includes(args.insightId)\n ) {\n await ctx.db.patch(args.beliefId, {\n contradictingInsightIds: [...currentContradicting, args.insightId],\n supportingInsightIds: currentSupporting.filter(\n (id: Id<\"epistemicNodes\">) => id !== args.insightId,\n ),\n });\n }\n\n try {\n const beliefSpineNode = await ctx.db.get(args.beliefId);\n const evidenceSpineNode = await ctx.db.get(args.insightId);\n\n if (\n beliefSpineNode?.nodeType === \"belief\" &&\n evidenceSpineNode?.nodeType === \"evidence\"\n ) {\n const existingEdges = await ctx.db\n .query(\"epistemicEdges\")\n .withIndex(\"by_from_to\", (q: IndexRangeBuilder) =>\n q\n .eq(\"fromNodeId\", evidenceSpineNode.globalId)\n .eq(\"toNodeId\", beliefSpineNode.globalId),\n )\n .collect();\n\n for (const edge of existingEdges) {\n if (edge.edgeType === \"informs\") {\n await ctx.scheduler.runAfter(0, internal.neo4jEdgeAPI.deleteEdge, {\n globalId: edge.globalId,\n });\n await ctx.db.delete(edge._id);\n }\n }\n\n const globalId = generateGlobalId();\n const context = args.rationale || `Linked as ${args.relation}`;\n await insertEpistemicEdge(ctx, {\n globalId,\n // C2-RR.4 Defect E — canonical UUIDv7 endpoints, not Convex doc ids.\n fromNodeId: evidenceSpineNode.globalId,\n toNodeId: beliefSpineNode.globalId,\n sourceGlobalId: evidenceSpineNode.globalId,\n targetGlobalId: beliefSpineNode.globalId,\n edgeType: \"informs\",\n weight: args.weight,\n confidence,\n context,\n reasoningMethod: \"testimonial\",\n derivationType: \"evidence_sl_scoring\",\n metadata: {\n relation: args.relation,\n confidence,\n impactScore: args.weight,\n invariant: \"evidence.belief_impact_required\",\n },\n createdBy: args.createdBy,\n createdAt: Date.now(),\n updatedAt: Date.now(),\n projectId: args.beliefNode.projectId,\n topicId: args.beliefNode.topicId ? String(args.beliefNode.topicId) : undefined,\n fromNodeType: \"evidence\",\n toNodeType: \"belief\",\n fromLayer: \"L2\",\n toLayer: \"L3\",\n });\n\n await ctx.scheduler.runAfter(0, internal.neo4jEdgeAPI.createEdge, {\n globalId,\n fromGlobalId: evidenceSpineNode.globalId,\n toGlobalId: beliefSpineNode.globalId,\n edgeType: \"informs\",\n weight: args.weight,\n confidence,\n context,\n projectId: args.beliefNode.projectId ? String(args.beliefNode.projectId) : undefined,\n createdBy: args.createdBy,\n fromNodeType: \"evidence\",\n toNodeType: \"belief\",\n fromLayer: \"L2\",\n toLayer: \"L3\",\n });\n }\n } catch (e) {\n console.error(\"[EpistemicSpine] Failed to create informs edge:\", e);\n }\n\n if (args.beliefNode.projectId) {\n await ctx.scheduler.runAfter(0, \"verificationActions:deepVerifyEvidence\", {\n insightId: args.insightId,\n targetType: \"belief\",\n targetId: args.beliefId,\n projectId: args.beliefNode.projectId,\n userId: args.createdBy,\n });\n }\n}\n\nexport async function removeOperationalLinkEffects(\n ctx: MutationCtx,\n args: {\n beliefId: Id<\"epistemicNodes\">;\n insightId: Id<\"epistemicNodes\">;\n relation: BeliefEvidenceRelation;\n },\n): Promise<void> {\n const belief = await ctx.db.get(args.beliefId);\n if (belief) {\n const currentSupporting = belief.supportingInsightIds ?? [];\n const currentContradicting = belief.contradictingInsightIds ?? [];\n\n if (args.relation === \"supports\") {\n await ctx.db.patch(args.beliefId, {\n supportingInsightIds: currentSupporting.filter(\n (id: Id<\"epistemicNodes\">) => id !== args.insightId,\n ),\n });\n } else {\n await ctx.db.patch(args.beliefId, {\n contradictingInsightIds: currentContradicting.filter(\n (id: Id<\"epistemicNodes\">) => id !== args.insightId,\n ),\n });\n }\n }\n\n try {\n const beliefSpineNode = await ctx.db.get(args.beliefId);\n const evidenceSpineNode = await ctx.db.get(args.insightId);\n\n if (\n beliefSpineNode?.nodeType === \"belief\" &&\n evidenceSpineNode?.nodeType === \"evidence\"\n ) {\n const edges = await ctx.db\n .query(\"epistemicEdges\")\n .withIndex(\"by_from_to\", (q: IndexRangeBuilder) =>\n q.eq(\"fromNodeId\", evidenceSpineNode._id).eq(\"toNodeId\", beliefSpineNode._id),\n )\n .collect();\n\n for (const edge of edges) {\n if (edge.edgeType === \"informs\") {\n await ctx.scheduler.runAfter(0, internal.neo4jEdgeAPI.deleteEdge, {\n globalId: edge.globalId,\n });\n await ctx.db.delete(edge._id);\n }\n }\n }\n } catch (e) {\n console.error(\"[EpistemicSpine] Failed to remove informs edge:\", e);\n }\n}\n"]}
@@ -0,0 +1,40 @@
1
+ import { Id } from './convex.js';
2
+ import 'convex/values';
3
+
4
+ /**
5
+ * Canonical-entity match helpers — pure text-normalization, title-derivation,
6
+ * and record-matching predicates behind the canonical-entity dedup/lookup path
7
+ * in entityLifecycle.ts. Extracted from entityLifecycle.ts (the mutations/queries
8
+ * registration stays there) so the module stays under the long-file hard gate;
9
+ * pure code motion, no logic delta.
10
+ */
11
+
12
+ type CanonicalEntityRecord = {
13
+ _id: Id<"epistemicNodes">;
14
+ title?: string;
15
+ canonicalText?: string;
16
+ status?: string;
17
+ nodeType?: string;
18
+ epistemicLayer?: string;
19
+ tenantId?: string;
20
+ workspaceId?: string;
21
+ contentHash?: string;
22
+ projectId?: string;
23
+ globalId?: string;
24
+ subtype?: string;
25
+ domain?: string;
26
+ tags?: string[];
27
+ verificationStatus?: string;
28
+ externalIds?: Record<string, unknown>;
29
+ metadata?: Record<string, unknown>;
30
+ };
31
+ declare function normalizeCanonicalEntityText(value: string): string;
32
+ declare function buildEntityTitle(canonicalText: string): string;
33
+ declare function matchesCanonicalEntityRecord(node: CanonicalEntityRecord, args: {
34
+ nodeType?: string;
35
+ tenantId?: string;
36
+ contentHash?: string;
37
+ includeArchived?: boolean;
38
+ }): boolean;
39
+
40
+ export { type CanonicalEntityRecord, buildEntityTitle, matchesCanonicalEntityRecord, normalizeCanonicalEntityText };
@@ -0,0 +1,33 @@
1
+ // src/entityCanonicalMatch.ts
2
+ function normalizeCanonicalEntityText(value) {
3
+ return value.trim().toLowerCase().replace(/\s+/g, " ");
4
+ }
5
+ function buildEntityTitle(canonicalText) {
6
+ return canonicalText.slice(0, 100) + (canonicalText.length > 100 ? "..." : "");
7
+ }
8
+ function matchesCanonicalEntityRecord(node, args) {
9
+ if (node.epistemicLayer !== "ontological") {
10
+ return false;
11
+ }
12
+ if (node.workspaceId !== void 0) {
13
+ return false;
14
+ }
15
+ if (args.nodeType && node.nodeType !== args.nodeType) {
16
+ return false;
17
+ }
18
+ if (args.contentHash && node.contentHash !== args.contentHash) {
19
+ return false;
20
+ }
21
+ const rowTenantId = typeof node.tenantId === "string" && node.tenantId.trim().length > 0 ? node.tenantId.trim() : void 0;
22
+ if (rowTenantId !== args.tenantId) {
23
+ return false;
24
+ }
25
+ if (args.includeArchived) {
26
+ return node.status !== "deleted";
27
+ }
28
+ return node.status === "active";
29
+ }
30
+
31
+ export { buildEntityTitle, matchesCanonicalEntityRecord, normalizeCanonicalEntityText };
32
+ //# sourceMappingURL=entityCanonicalMatch.js.map
33
+ //# sourceMappingURL=entityCanonicalMatch.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/entityCanonicalMatch.ts"],"names":[],"mappings":";AA6BO,SAAS,6BAA6B,KAAA,EAAuB;AAClE,EAAA,OAAO,MAAM,IAAA,EAAK,CAAE,aAAY,CAAE,OAAA,CAAQ,QAAQ,GAAG,CAAA;AACvD;AAEO,SAAS,iBAAiB,aAAA,EAA+B;AAC9D,EAAA,OAAO,aAAA,CAAc,MAAM,CAAA,EAAG,GAAG,KAAK,aAAA,CAAc,MAAA,GAAS,MAAM,KAAA,GAAQ,EAAA,CAAA;AAC7E;AAEO,SAAS,4BAAA,CACd,MACA,IAAA,EAMS;AACT,EAAA,IAAI,IAAA,CAAK,mBAAmB,aAAA,EAAe;AACzC,IAAA,OAAO,KAAA;AAAA,EACT;AACA,EAAA,IAAI,IAAA,CAAK,gBAAgB,MAAA,EAAW;AAClC,IAAA,OAAO,KAAA;AAAA,EACT;AACA,EAAA,IAAI,IAAA,CAAK,QAAA,IAAY,IAAA,CAAK,QAAA,KAAa,KAAK,QAAA,EAAU;AACpD,IAAA,OAAO,KAAA;AAAA,EACT;AACA,EAAA,IAAI,IAAA,CAAK,WAAA,IAAe,IAAA,CAAK,WAAA,KAAgB,KAAK,WAAA,EAAa;AAC7D,IAAA,OAAO,KAAA;AAAA,EACT;AACA,EAAA,MAAM,WAAA,GACJ,OAAO,IAAA,CAAK,QAAA,KAAa,YAAY,IAAA,CAAK,QAAA,CAAS,IAAA,EAAK,CAAE,MAAA,GAAS,CAAA,GAC/D,IAAA,CAAK,QAAA,CAAS,MAAK,GACnB,MAAA;AACN,EAAA,IAAI,WAAA,KAAgB,KAAK,QAAA,EAAU;AACjC,IAAA,OAAO,KAAA;AAAA,EACT;AACA,EAAA,IAAI,KAAK,eAAA,EAAiB;AACxB,IAAA,OAAO,KAAK,MAAA,KAAW,SAAA;AAAA,EACzB;AACA,EAAA,OAAO,KAAK,MAAA,KAAW,QAAA;AACzB","file":"entityCanonicalMatch.js","sourcesContent":["/**\n * Canonical-entity match helpers — pure text-normalization, title-derivation,\n * and record-matching predicates behind the canonical-entity dedup/lookup path\n * in entityLifecycle.ts. Extracted from entityLifecycle.ts (the mutations/queries\n * registration stays there) so the module stays under the long-file hard gate;\n * pure code motion, no logic delta.\n */\nimport type { Id } from \"./convex\";\n\nexport type CanonicalEntityRecord = {\n _id: Id<\"epistemicNodes\">;\n title?: string;\n canonicalText?: string;\n status?: string;\n nodeType?: string;\n epistemicLayer?: string;\n tenantId?: string;\n workspaceId?: string;\n contentHash?: string;\n projectId?: string;\n globalId?: string;\n subtype?: string;\n domain?: string;\n tags?: string[];\n verificationStatus?: string;\n externalIds?: Record<string, unknown>;\n metadata?: Record<string, unknown>;\n};\n\nexport function normalizeCanonicalEntityText(value: string): string {\n return value.trim().toLowerCase().replace(/\\s+/g, \" \");\n}\n\nexport function buildEntityTitle(canonicalText: string): string {\n return canonicalText.slice(0, 100) + (canonicalText.length > 100 ? \"...\" : \"\");\n}\n\nexport function matchesCanonicalEntityRecord(\n node: CanonicalEntityRecord,\n args: {\n nodeType?: string;\n tenantId?: string;\n contentHash?: string;\n includeArchived?: boolean;\n }\n): boolean {\n if (node.epistemicLayer !== \"ontological\") {\n return false;\n }\n if (node.workspaceId !== undefined) {\n return false;\n }\n if (args.nodeType && node.nodeType !== args.nodeType) {\n return false;\n }\n if (args.contentHash && node.contentHash !== args.contentHash) {\n return false;\n }\n const rowTenantId =\n typeof node.tenantId === \"string\" && node.tenantId.trim().length > 0\n ? node.tenantId.trim()\n : undefined;\n if (rowTenantId !== args.tenantId) {\n return false;\n }\n if (args.includeArchived) {\n return node.status !== \"deleted\";\n }\n return node.status === \"active\";\n}\n"]}
@@ -1,9 +1,9 @@
1
- import { v } from 'convex/values';
1
+ import { v, ConvexError } from 'convex/values';
2
2
  import { checkProjectAccess } from '@lucern/access-control/access';
3
3
  import { getCurrentUserId } from '@lucern/access-control/auth';
4
4
  import { permissiveReturn } from '@lucern/contracts/schema-helpers/validators';
5
5
  import { componentsGeneric, anyApi, queryGeneric, mutationGeneric } from 'convex/server';
6
- import { generateGlobalId, assertUuidV7Identity, assertStorageEdgeVocabulary, assertCanonicalEdgeEndpoint } from '@lucern/contracts/ids';
6
+ import { generateGlobalId, assertUuidV7Identity, assertStorageEdgeVocabulary, assertUuidShapedEdgeEndpoint } from '@lucern/contracts/ids';
7
7
  import { assertEdgePolicyAllowed, edgePolicyManifest } from '@lucern/contracts';
8
8
 
9
9
  // src/entityLifecycle.ts
@@ -77,6 +77,36 @@ async function validateEntityMetadata(ctx, nodeType, metadata, tenantId) {
77
77
  };
78
78
  }
79
79
 
80
+ // src/entityCanonicalMatch.ts
81
+ function normalizeCanonicalEntityText(value) {
82
+ return value.trim().toLowerCase().replace(/\s+/g, " ");
83
+ }
84
+ function buildEntityTitle(canonicalText) {
85
+ return canonicalText.slice(0, 100) + (canonicalText.length > 100 ? "..." : "");
86
+ }
87
+ function matchesCanonicalEntityRecord(node, args) {
88
+ if (node.epistemicLayer !== "ontological") {
89
+ return false;
90
+ }
91
+ if (node.workspaceId !== void 0) {
92
+ return false;
93
+ }
94
+ if (args.nodeType && node.nodeType !== args.nodeType) {
95
+ return false;
96
+ }
97
+ if (args.contentHash && node.contentHash !== args.contentHash) {
98
+ return false;
99
+ }
100
+ const rowTenantId = typeof node.tenantId === "string" && node.tenantId.trim().length > 0 ? node.tenantId.trim() : void 0;
101
+ if (rowTenantId !== args.tenantId) {
102
+ return false;
103
+ }
104
+ if (args.includeArchived) {
105
+ return node.status !== "deleted";
106
+ }
107
+ return node.status === "active";
108
+ }
109
+
80
110
  // src/topicOntologyResolver.ts
81
111
  var MAX_RESOLUTION_DEPTH = 10;
82
112
  async function resolveTopicOntologyInternal(ctx, topicId) {
@@ -286,6 +316,9 @@ function materializeTopicProjectOverlay(topic, idMode = "legacy") {
286
316
  type: mapProjectType(topic, metadata),
287
317
  description: readNonEmptyString(topic.description),
288
318
  ownerId: readNonEmptyString(metadata.ownerId) || readNonEmptyString(topic.createdBy) || "system",
319
+ // FR.7 creator-grant: surface the principal-shaped owner field (column-first,
320
+ // metadata fallback for legacy rows that recorded it in metadata).
321
+ ownerPrincipalId: readNonEmptyString(topic.ownerPrincipalId) || readNonEmptyString(metadata.ownerPrincipalId),
289
322
  sharedWith: readStringArray(metadata.sharedWith),
290
323
  visibility,
291
324
  tenantId: readNonEmptyString(topic.tenantId) || readNonEmptyString(metadata.tenantId),
@@ -752,6 +785,15 @@ async function insertEpistemicNode(ctx, doc) {
752
785
  assertUuidV7Identity("epistemicNodes", doc.globalId);
753
786
  return ctx.db.insert("epistemicNodes", doc);
754
787
  }
788
+ async function assertExistingNodeEndpoint(ctx, endpointRole, endpoint) {
789
+ assertUuidShapedEdgeEndpoint(endpointRole, endpoint);
790
+ const node = await ctx.db.query("epistemicNodes").withIndex("by_globalId", (q) => q.eq("globalId", endpoint)).first();
791
+ if (!node) {
792
+ throw new Error(
793
+ `edge_endpoint_not_canonical: epistemicEdges insert requires ${endpointRole} to be the globalId of an existing epistemicNodes row, received ${endpoint} (no node with that globalId)`
794
+ );
795
+ }
796
+ }
755
797
  async function insertEpistemicEdge(ctx, doc) {
756
798
  assertUuidV7Identity("epistemicEdges", doc.globalId);
757
799
  assertStorageEdgeVocabulary(doc.edgeType);
@@ -765,8 +807,8 @@ async function insertEpistemicEdge(ctx, doc) {
765
807
  "edge_endpoint_missing: epistemicEdges insert requires a non-empty toNodeId"
766
808
  );
767
809
  }
768
- assertCanonicalEdgeEndpoint("fromNodeId", doc.fromNodeId);
769
- assertCanonicalEdgeEndpoint("toNodeId", doc.toNodeId);
810
+ await assertExistingNodeEndpoint(ctx, "fromNodeId", doc.fromNodeId);
811
+ await assertExistingNodeEndpoint(ctx, "toNodeId", doc.toNodeId);
770
812
  if (doc.fromNodeType && doc.toNodeType && doc.edgeType !== "extracted_from") {
771
813
  assertEdgePolicyAllowed(
772
814
  edgePolicyManifest,
@@ -788,7 +830,18 @@ async function insertEpistemicEdge(ctx, doc) {
788
830
 
789
831
  // src/entityLifecycle.ts
790
832
  function throwStructuredMutationError(args) {
791
- const error = new Error(args.message);
833
+ const data = {
834
+ structuredMutationError: true,
835
+ message: args.message,
836
+ status: args.status,
837
+ code: args.code,
838
+ invariantCode: args.invariantCode,
839
+ suggestion: args.suggestion,
840
+ details: args.details
841
+ };
842
+ const error = new ConvexError(
843
+ data
844
+ );
792
845
  error.status = args.status;
793
846
  error.code = args.code;
794
847
  error.invariantCode = args.invariantCode;
@@ -813,12 +866,12 @@ async function requireProjectWriteAccess(ctx, projectId, userId) {
813
866
  const hasAccess = await checkProjectAccess(ctx, projectId, userId);
814
867
  if (!hasAccess) {
815
868
  throwStructuredMutationError({
816
- message: "Project access required.",
869
+ message: `Project write access denied for topic ${projectId}.`,
817
870
  status: 403,
818
- code: "FORBIDDEN",
871
+ code: "PROJECT_ACCESS_DENIED",
819
872
  invariantCode: "policy.scope_required",
820
- suggestion: "Request write access for the project and retry.",
821
- details: { projectId, userId }
873
+ suggestion: "The acting principal lacks project-write access to this topic. Request a topic grant (or, if the principal created this topic, run the creator-grant backfill) and retry.",
874
+ details: { topicId: projectId, principalId: userId }
822
875
  });
823
876
  }
824
877
  }
@@ -842,12 +895,6 @@ var ONTOLOGICAL_NODE_TYPE_SET = new Set(ONTOLOGICAL_NODE_TYPES);
842
895
  var isOntologicalNodeType = ONTOLOGICAL_NODE_TYPE_SET.has.bind(
843
896
  ONTOLOGICAL_NODE_TYPE_SET
844
897
  );
845
- function normalizeCanonicalEntityText(value) {
846
- return value.trim().toLowerCase().replace(/\s+/g, " ");
847
- }
848
- function buildEntityTitle(canonicalText) {
849
- return canonicalText.slice(0, 100) + (canonicalText.length > 100 ? "..." : "");
850
- }
851
898
  async function resolveCanonicalEntityScope(ctx, args) {
852
899
  const scope = await resolveTopicProjectScope(ctx, args);
853
900
  const topic = scope.topicId ? await ctx.db.get(scope.topicId) ?? null : null;
@@ -863,28 +910,6 @@ async function resolveCanonicalEntityScope(ctx, args) {
863
910
  project
864
911
  };
865
912
  }
866
- function matchesCanonicalEntityRecord(node, args) {
867
- if (node.epistemicLayer !== "ontological") {
868
- return false;
869
- }
870
- if (node.workspaceId !== void 0) {
871
- return false;
872
- }
873
- if (args.nodeType && node.nodeType !== args.nodeType) {
874
- return false;
875
- }
876
- if (args.contentHash && node.contentHash !== args.contentHash) {
877
- return false;
878
- }
879
- const rowTenantId = typeof node.tenantId === "string" && node.tenantId.trim().length > 0 ? node.tenantId.trim() : void 0;
880
- if (rowTenantId !== args.tenantId) {
881
- return false;
882
- }
883
- if (args.includeArchived) {
884
- return node.status !== "deleted";
885
- }
886
- return node.status === "active";
887
- }
888
913
  async function findExistingCanonicalEntity(ctx, args) {
889
914
  const contentHash = generateContentHash(args.nodeType, args.canonicalText);
890
915
  const candidates = await ctx.db.query("epistemicNodes").withIndex(