@lucern/graph-primitives 1.0.16 → 1.0.18

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/beliefEvidenceLinks.js +144 -99
  2. package/dist/beliefEvidenceLinks.js.map +1 -1
  3. package/dist/beliefEvidenceLinks.operational.d.ts +29 -0
  4. package/dist/beliefEvidenceLinks.operational.js +157 -0
  5. package/dist/beliefEvidenceLinks.operational.js.map +1 -0
  6. package/dist/{beliefLifecycle-y8WLXqQj.d.ts → beliefLifecycle-CXwdDw5e.d.ts} +7 -4
  7. package/dist/beliefLifecycle.d.ts +1 -1
  8. package/dist/beliefLifecycle.js +75 -18
  9. package/dist/beliefLifecycle.js.map +1 -1
  10. package/dist/entityLifecycle.js +1 -12
  11. package/dist/entityLifecycle.js.map +1 -1
  12. package/dist/epistemicAnswers.js +1 -12
  13. package/dist/epistemicAnswers.js.map +1 -1
  14. package/dist/epistemicBeliefs.admin.js.map +1 -1
  15. package/dist/epistemicBeliefs.backfills.d.ts +1 -1
  16. package/dist/epistemicBeliefs.backfills.js +63 -35
  17. package/dist/epistemicBeliefs.backfills.js.map +1 -1
  18. package/dist/epistemicBeliefs.confidence.d.ts +1 -1
  19. package/dist/epistemicBeliefs.confidence.js +70 -41
  20. package/dist/epistemicBeliefs.confidence.js.map +1 -1
  21. package/dist/epistemicBeliefs.core.js +946 -566
  22. package/dist/epistemicBeliefs.core.js.map +1 -1
  23. package/dist/epistemicBeliefs.d.ts +2 -2
  24. package/dist/epistemicBeliefs.forkEvidence.d.ts +18 -0
  25. package/dist/epistemicBeliefs.forkEvidence.js +121 -0
  26. package/dist/epistemicBeliefs.forkEvidence.js.map +1 -0
  27. package/dist/epistemicBeliefs.helpers.d.ts +2 -2
  28. package/dist/epistemicBeliefs.helpers.js +60 -32
  29. package/dist/epistemicBeliefs.helpers.js.map +1 -1
  30. package/dist/epistemicBeliefs.internal.js +175 -51
  31. package/dist/epistemicBeliefs.internal.js.map +1 -1
  32. package/dist/epistemicBeliefs.js +437 -84
  33. package/dist/epistemicBeliefs.js.map +1 -1
  34. package/dist/epistemicBeliefs.lifecycle.d.ts +2 -2
  35. package/dist/epistemicBeliefs.lifecycle.js +75 -47
  36. package/dist/epistemicBeliefs.lifecycle.js.map +1 -1
  37. package/dist/epistemicBeliefs.links.js +47 -13
  38. package/dist/epistemicBeliefs.links.js.map +1 -1
  39. package/dist/epistemicBeliefs.topicAnchor.d.ts +29 -0
  40. package/dist/epistemicBeliefs.topicAnchor.js +105 -0
  41. package/dist/epistemicBeliefs.topicAnchor.js.map +1 -0
  42. package/dist/epistemicContracts.evaluators.js +71 -42
  43. package/dist/epistemicContracts.evaluators.js.map +1 -1
  44. package/dist/epistemicContracts.handlers.js +72 -54
  45. package/dist/epistemicContracts.handlers.js.map +1 -1
  46. package/dist/epistemicContracts.js +72 -54
  47. package/dist/epistemicContracts.js.map +1 -1
  48. package/dist/epistemicContracts.metrics.js +1 -1
  49. package/dist/epistemicContracts.metrics.js.map +1 -1
  50. package/dist/epistemicContracts.types.d.ts +1 -1
  51. package/dist/epistemicEdgeCreation.js +1 -12
  52. package/dist/epistemicEdgeCreation.js.map +1 -1
  53. package/dist/epistemicEdges.helpers.d.ts +1 -1
  54. package/dist/epistemicEvidence.js +173 -93
  55. package/dist/epistemicEvidence.js.map +1 -1
  56. package/dist/epistemicEvidenceMutations.js +173 -93
  57. package/dist/epistemicEvidenceMutations.js.map +1 -1
  58. package/dist/epistemicHelpers.js +1 -12
  59. package/dist/epistemicHelpers.js.map +1 -1
  60. package/dist/epistemicNodeCreation.js +1 -10
  61. package/dist/epistemicNodeCreation.js.map +1 -1
  62. package/dist/epistemicNodes.internal.js.map +1 -1
  63. package/dist/epistemicNodes.js +2 -2
  64. package/dist/epistemicNodes.js.map +1 -1
  65. package/dist/epistemicNodes.mutations.js +2 -2
  66. package/dist/epistemicNodes.mutations.js.map +1 -1
  67. package/dist/epistemicQuestions.create.js +1 -12
  68. package/dist/epistemicQuestions.create.js.map +1 -1
  69. package/dist/epistemicQuestions.evidence.js +1 -12
  70. package/dist/epistemicQuestions.evidence.js.map +1 -1
  71. package/dist/epistemicQuestions.js +1 -12
  72. package/dist/epistemicQuestions.js.map +1 -1
  73. package/dist/epistemicQuestions.tail.js +1 -12
  74. package/dist/epistemicQuestions.tail.js.map +1 -1
  75. package/dist/epistemicSources.js +1 -12
  76. package/dist/epistemicSources.js.map +1 -1
  77. package/dist/evaluators/index.js +1 -1
  78. package/dist/evaluators/index.js.map +1 -1
  79. package/dist/globalId-4y9SPpC_.d.ts +10 -0
  80. package/dist/globalId.d.ts +1 -1
  81. package/dist/globalId.js +1 -13
  82. package/dist/globalId.js.map +1 -1
  83. package/dist/helpers.js +1 -12
  84. package/dist/helpers.js.map +1 -1
  85. package/dist/index.d.ts +4 -3
  86. package/dist/index.js +771 -247
  87. package/dist/index.js.map +1 -1
  88. package/dist/invariantEnforcement.js +2 -2
  89. package/dist/invariantEnforcement.js.map +1 -1
  90. package/dist/proof-attestation.json +3 -3
  91. package/package.json +4 -4
  92. package/dist/globalId-DKh9d_uD.d.ts +0 -20
package/dist/index.js CHANGED
@@ -7,6 +7,7 @@ import { componentsGeneric, anyApi, internalMutationGeneric, mutationGeneric, qu
7
7
  import { isNodeType, getLayerForNodeType } from '@lucern/contracts/schema-helpers/spine/tables/epistemicNodes';
8
8
  import { permissiveReturn } from '@lucern/contracts/schema-helpers/validators';
9
9
  import { assertSchemaEnumValue } from '@lucern/contracts/schema-helpers/enumValidation';
10
+ import { generateGlobalId, isUuidV7, generateUuidV7 } from '@lucern/contracts/ids';
10
11
  import { listAudienceRegistryRows } from '@lucern/access-control/audienceRegistry';
11
12
  import { scoreEntityTypeMatch, scoreEntityConnection, rankEntityTypeMatches, rankEntityConnections } from '@lucern/contracts/v1/ontologies/v1';
12
13
  import { wordTokenize, wordOverlapScore, tokenizeSearchText, tokenOverlapScore, stemToken, scoreLexicalSignals, scoreLexicalSignal, rerankLexicalWindow, rankWindowScore, prepareLexicalQuery, jaccardSimilarity, bigramTokenize } from '@lucern/contracts/text-matching.contract';
@@ -689,6 +690,7 @@ __export(beliefLifecycle_exports, {
689
690
  isPreValidationBeliefStatus: () => isPreValidationBeliefStatus,
690
691
  isPropagationEligibleBeliefStatus: () => isPropagationEligibleBeliefStatus,
691
692
  isResolvedByConfidence: () => isResolvedByConfidence,
693
+ promoteBeliefStatusAfterEvidence: () => promoteBeliefStatusAfterEvidence,
692
694
  promoteBeliefStatusAfterScoring: () => promoteBeliefStatusAfterScoring,
693
695
  resolveBeliefLifecycleStatus: () => resolveBeliefLifecycleStatus,
694
696
  shouldTreatBeliefAsFact: () => shouldTreatBeliefAsFact
@@ -696,8 +698,10 @@ __export(beliefLifecycle_exports, {
696
698
  var BELIEF_STATUS_VALUES = [
697
699
  "assumption",
698
700
  "hypothesis",
699
- "belief",
700
- "fact"
701
+ "active",
702
+ "superseded",
703
+ "resolved_true",
704
+ "resolved_false"
701
705
  ];
702
706
  var RESOLVED_PREDICTION_OUTCOMES = [
703
707
  "confirmed",
@@ -708,6 +712,24 @@ var RESOLVED_PREDICTION_OUTCOMES = [
708
712
  function isBeliefLifecycleStatus(value) {
709
713
  return typeof value === "string" && BELIEF_STATUS_VALUES.includes(value);
710
714
  }
715
+ function normalizeLegacyBeliefStatus(value) {
716
+ if (isBeliefLifecycleStatus(value)) {
717
+ return value;
718
+ }
719
+ if (value === "belief" || value === "established" || value === "emerging") {
720
+ return "active";
721
+ }
722
+ if (value === "fact" || value === "confirmed") {
723
+ return "resolved_true";
724
+ }
725
+ if (value === "disconfirmed" || value === "expired") {
726
+ return "resolved_false";
727
+ }
728
+ if (value === "deprecated") {
729
+ return "superseded";
730
+ }
731
+ return null;
732
+ }
711
733
  function normalizeBeliefConfidence(confidence) {
712
734
  if (typeof confidence !== "number" || !Number.isFinite(confidence)) {
713
735
  return null;
@@ -749,25 +771,58 @@ function shouldTreatBeliefAsFact(opts) {
749
771
  }
750
772
  return false;
751
773
  }
774
+ function resolvedPredictionStatus(predictionMeta) {
775
+ if (!predictionMeta || typeof predictionMeta !== "object") {
776
+ return null;
777
+ }
778
+ const outcome = predictionMeta.outcome;
779
+ if (outcome === "confirmed") {
780
+ return "resolved_true";
781
+ }
782
+ if (outcome === "disconfirmed" || outcome === "expired") {
783
+ return "resolved_false";
784
+ }
785
+ return null;
786
+ }
787
+ function shouldTreatBeliefAsResolved(opts) {
788
+ if (isResolvedByConfidence(opts.confidence)) {
789
+ const normalized = normalizeBeliefConfidence(opts.confidence);
790
+ return normalized === 0 ? "resolved_false" : "resolved_true";
791
+ }
792
+ const directPredictionStatus = resolvedPredictionStatus(opts.predictionMeta);
793
+ if (directPredictionStatus) {
794
+ return directPredictionStatus;
795
+ }
796
+ const metadataPredictionStatus = resolvedPredictionStatus(
797
+ getPredictionMetaFromMetadata(opts.metadata)
798
+ );
799
+ if (metadataPredictionStatus) {
800
+ return metadataPredictionStatus;
801
+ }
802
+ return null;
803
+ }
752
804
  function resolveBeliefLifecycleStatus(opts) {
753
- if (shouldTreatBeliefAsFact(opts)) {
754
- return "fact";
805
+ const resolvedStatus = shouldTreatBeliefAsResolved(opts);
806
+ if (resolvedStatus) {
807
+ return resolvedStatus;
755
808
  }
756
809
  const direct = opts.beliefStatus;
757
- if (isBeliefLifecycleStatus(direct)) {
810
+ const normalizedDirect = normalizeLegacyBeliefStatus(direct);
811
+ if (normalizedDirect) {
758
812
  const normalized = normalizeBeliefConfidence(opts.confidence);
759
- if (normalized !== null && isPreValidationBeliefStatus(direct)) {
760
- return "belief";
813
+ if (normalized !== null && isPreValidationBeliefStatus(normalizedDirect)) {
814
+ return "active";
761
815
  }
762
- return direct;
816
+ return normalizedDirect;
763
817
  }
764
818
  const metaStatus = opts.metadata?.beliefStatus;
765
- if (isBeliefLifecycleStatus(metaStatus)) {
819
+ const normalizedMetaStatus = normalizeLegacyBeliefStatus(metaStatus);
820
+ if (normalizedMetaStatus) {
766
821
  const normalized = normalizeBeliefConfidence(opts.confidence);
767
- if (normalized !== null && isPreValidationBeliefStatus(metaStatus)) {
768
- return "belief";
822
+ if (normalized !== null && isPreValidationBeliefStatus(normalizedMetaStatus)) {
823
+ return "active";
769
824
  }
770
- return metaStatus;
825
+ return normalizedMetaStatus;
771
826
  }
772
827
  return "assumption";
773
828
  }
@@ -775,16 +830,20 @@ function isPreValidationBeliefStatus(status) {
775
830
  return status === "assumption" || status === "hypothesis";
776
831
  }
777
832
  function isPropagationEligibleBeliefStatus(status) {
778
- return status === "belief" || status === "fact";
833
+ return status === "active" || status === "resolved_true";
779
834
  }
780
835
  function promoteBeliefStatusAfterScoring(status, opts) {
781
- if (shouldTreatBeliefAsFact({ ...opts })) {
782
- return "fact";
836
+ const resolvedStatus = shouldTreatBeliefAsResolved({ ...opts });
837
+ if (resolvedStatus) {
838
+ return resolvedStatus;
783
839
  }
784
840
  if (isPreValidationBeliefStatus(status)) {
785
- return "belief";
841
+ return "active";
786
842
  }
787
- return status === "fact" ? "fact" : "belief";
843
+ return status;
844
+ }
845
+ function promoteBeliefStatusAfterEvidence(status) {
846
+ return isPreValidationBeliefStatus(status) ? "active" : status;
788
847
  }
789
848
  var api = anyApi;
790
849
  componentsGeneric();
@@ -1607,7 +1666,7 @@ function buildBeliefConfidenceRow(args) {
1607
1666
  disbelief: args.disbelief,
1608
1667
  uncertainty: args.uncertainty,
1609
1668
  baseRate: args.baseRate,
1610
- slOperator: args.slOperator ?? "manual_assessment",
1669
+ slOperator: args.slOperator ?? "prior_seed",
1611
1670
  trigger: args.trigger,
1612
1671
  ...args.rationale ? { rationale: args.rationale } : {},
1613
1672
  assessedBy: args.assessedBy,
@@ -2061,17 +2120,17 @@ async function applyBeliefConfidenceChange(ctx, args) {
2061
2120
  status: 404,
2062
2121
  code: "NOT_FOUND",
2063
2122
  invariantCode: "belief.exists",
2064
- suggestion: "Verify nodeId points to an existing node before modulating confidence.",
2123
+ suggestion: "Verify nodeId points to an existing node before appending SL scoring.",
2065
2124
  details: { nodeId: args.nodeId }
2066
2125
  });
2067
2126
  }
2068
2127
  if (node.nodeType !== "belief") {
2069
2128
  throwStructuredMutationError({
2070
- message: `modulateConfidence only applies to belief nodes. Received nodeType "${node.nodeType}". Entity nodes (company, person, investor, etc.) do not have confidence \u2014 use entityLifecycle.updateEntityAttributes for mutable entity data.`,
2129
+ message: `appendSlScoring only applies to belief nodes. Received nodeType "${node.nodeType}". Entity nodes (company, person, investor, etc.) do not have confidence \u2014 use entityLifecycle.updateEntityAttributes for mutable entity data.`,
2071
2130
  status: 400,
2072
2131
  code: "INVALID_ARGUMENT",
2073
2132
  invariantCode: "entity.no_confidence",
2074
- suggestion: "Use entityLifecycle.updateEntityAttributes for entity mutations. modulateConfidence is for belief nodes only.",
2133
+ suggestion: "Use entityLifecycle.updateEntityAttributes for entity mutations. appendSlScoring is for belief nodes only.",
2075
2134
  details: { nodeId: args.nodeId, nodeType: node.nodeType }
2076
2135
  });
2077
2136
  }
@@ -2081,7 +2140,7 @@ async function applyBeliefConfidenceChange(ctx, args) {
2081
2140
  status: 400,
2082
2141
  code: "MISSING_SCOPE",
2083
2142
  invariantCode: "belief.project_required",
2084
- suggestion: "Belief must have a projectId to modulate confidence.",
2143
+ suggestion: "Belief must have a projectId before SL scoring can be appended.",
2085
2144
  details: { nodeId: args.nodeId }
2086
2145
  });
2087
2146
  }
@@ -2104,7 +2163,7 @@ async function applyBeliefConfidenceChange(ctx, args) {
2104
2163
  status: 409,
2105
2164
  code: "CONFLICT",
2106
2165
  invariantCode: "belief.confidence_append_only",
2107
- suggestion: "Complete a worktree linked to this belief before recording confidence modulation.",
2166
+ suggestion: "Complete a worktree linked to this belief before recording SL scoring.",
2108
2167
  details: { nodeId: args.nodeId }
2109
2168
  });
2110
2169
  }
@@ -2359,6 +2418,199 @@ var propagateConfidenceChange = internalMutation({
2359
2418
  }
2360
2419
  });
2361
2420
 
2421
+ // src/epistemicBeliefs.forkEvidence.ts
2422
+ function normalizeForkTriggerRelation(value) {
2423
+ if (value === "supports" || value === "supporting") {
2424
+ return "supports";
2425
+ }
2426
+ if (value === "contradicts" || value === "contradicting") {
2427
+ return "contradicts";
2428
+ }
2429
+ return null;
2430
+ }
2431
+ async function resolveForkTriggerEvidence(ctx, args) {
2432
+ const evidence = await ctx.db.get(args.triggeringEvidenceId);
2433
+ if (!evidence || evidence.nodeType !== "evidence") {
2434
+ throwStructuredMutationError({
2435
+ message: "Fork requires an existing evidence node.",
2436
+ status: 400,
2437
+ code: "INVALID_ARGUMENT",
2438
+ invariantCode: "belief.fork_requires_evidence",
2439
+ suggestion: "Create or link evidence first, then fork with triggeringEvidenceId.",
2440
+ details: { triggeringEvidenceId: args.triggeringEvidenceId }
2441
+ });
2442
+ }
2443
+ if (evidence.topicId && evidence.topicId !== args.parent.topicId) {
2444
+ throwStructuredMutationError({
2445
+ message: "Fork evidence belongs to a different topic scope.",
2446
+ status: 400,
2447
+ code: "INVALID_ARGUMENT",
2448
+ invariantCode: "belief.fork_evidence_scope",
2449
+ suggestion: "Use evidence from the same topic/workspace scope as the parent belief.",
2450
+ details: {
2451
+ parentNodeId: args.parentNodeId,
2452
+ triggeringEvidenceId: args.triggeringEvidenceId
2453
+ }
2454
+ });
2455
+ }
2456
+ const evidenceMetadata = evidence.metadata && typeof evidence.metadata === "object" ? evidence.metadata : {};
2457
+ const parentRefs = new Set(
2458
+ [
2459
+ String(args.parentNodeId),
2460
+ String(args.parent.globalId ?? ""),
2461
+ String(args.parent._id)
2462
+ ].filter(Boolean)
2463
+ );
2464
+ const evidenceRefs = new Set(
2465
+ [
2466
+ String(args.triggeringEvidenceId),
2467
+ String(evidence.globalId ?? ""),
2468
+ String(evidence._id)
2469
+ ].filter(Boolean)
2470
+ );
2471
+ let relation = null;
2472
+ const linkedBeliefNodeId = String(
2473
+ evidenceMetadata.linkedBeliefNodeId ?? ""
2474
+ );
2475
+ if (linkedBeliefNodeId && parentRefs.has(linkedBeliefNodeId)) {
2476
+ relation = normalizeForkTriggerRelation(evidenceMetadata.evidenceRelation);
2477
+ }
2478
+ if (!relation) {
2479
+ for (const parentRef of parentRefs) {
2480
+ const links = await ctx.db.query("beliefEvidenceLinks").withIndex("by_beliefId", (q) => q.eq("beliefId", parentRef)).collect();
2481
+ const matched = links.find((link) => evidenceRefs.has(String(link.insightId)));
2482
+ if (matched) {
2483
+ relation = normalizeForkTriggerRelation(matched.relation);
2484
+ break;
2485
+ }
2486
+ }
2487
+ }
2488
+ if (!relation) {
2489
+ throwStructuredMutationError({
2490
+ message: "Fork evidence must already be attached to the parent belief through an SL evidence relation.",
2491
+ status: 409,
2492
+ code: "CONFLICT",
2493
+ invariantCode: "belief.fork_requires_attached_evidence",
2494
+ suggestion: "Attach the evidence to the parent belief as supports or contradicts before forking.",
2495
+ details: {
2496
+ parentNodeId: args.parentNodeId,
2497
+ triggeringEvidenceId: args.triggeringEvidenceId
2498
+ }
2499
+ });
2500
+ }
2501
+ if (args.forkMode === "supersede" && relation !== "contradicts") {
2502
+ throwStructuredMutationError({
2503
+ message: "Superseding fork requires contradicting evidence against the parent belief.",
2504
+ status: 409,
2505
+ code: "CONFLICT",
2506
+ invariantCode: "belief.supersede_requires_contradiction",
2507
+ suggestion: "Use forkMode='branch' for a non-replacing fork, or attach contradicting evidence before superseding.",
2508
+ details: {
2509
+ parentNodeId: args.parentNodeId,
2510
+ triggeringEvidenceId: args.triggeringEvidenceId,
2511
+ relation
2512
+ }
2513
+ });
2514
+ }
2515
+ return { evidenceNodeId: args.triggeringEvidenceId, relation };
2516
+ }
2517
+
2518
+ // src/epistemicBeliefs.topicAnchor.ts
2519
+ function cleanString(value) {
2520
+ return typeof value === "string" && value.trim().length > 0 ? value.trim() : void 0;
2521
+ }
2522
+ function topicNodeCandidates(topicRef) {
2523
+ const normalized = topicRef.trim();
2524
+ if (!normalized) {
2525
+ return [];
2526
+ }
2527
+ const candidates = [normalized];
2528
+ if (normalized.startsWith("top_")) {
2529
+ candidates.push(normalized.slice(4));
2530
+ }
2531
+ return [...new Set(candidates)];
2532
+ }
2533
+ function readTopicNodeRef(args) {
2534
+ return cleanString(args.topicGlobalId) ?? cleanString(args.topicNodeId) ?? cleanString(args.topicId);
2535
+ }
2536
+ async function resolveRequiredTopicAnchor(ctx, topicRef) {
2537
+ for (const candidate of topicNodeCandidates(topicRef)) {
2538
+ try {
2539
+ const direct = await ctx.db.get(candidate);
2540
+ if (direct?.nodeType === "topic" && cleanString(direct.globalId)) {
2541
+ return direct;
2542
+ }
2543
+ } catch (_) {
2544
+ }
2545
+ const byGlobalId = await ctx.db.query("epistemicNodes").withIndex("by_globalId", (q) => q.eq("globalId", candidate)).first();
2546
+ if (byGlobalId?.nodeType === "topic" && cleanString(byGlobalId.globalId)) {
2547
+ return byGlobalId;
2548
+ }
2549
+ }
2550
+ throw new Error(
2551
+ "Belief creation requires topicGlobalId or topicNodeId for a topic node in epistemicNodes. Legacy topics-table IDs are not valid belief anchors."
2552
+ );
2553
+ }
2554
+ function scopeFromTopicAnchor(topicNode) {
2555
+ return {
2556
+ topicId: topicNode.globalId,
2557
+ projectId: cleanString(topicNode.projectId),
2558
+ tenantId: cleanString(topicNode.tenantId),
2559
+ workspaceId: cleanString(topicNode.workspaceId),
2560
+ source: "topic"
2561
+ };
2562
+ }
2563
+ async function createRequiredBeliefTopicEdge(ctx, args) {
2564
+ const topicGlobalId = args.topicNode.globalId;
2565
+ const edgeGlobalId = `edge:${args.beliefGlobalId}:${topicGlobalId}:scoped_by`;
2566
+ const now = Date.now();
2567
+ const existing = await ctx.db.query("epistemicEdges").withIndex("by_globalId", (q) => q.eq("globalId", edgeGlobalId)).first();
2568
+ if (!existing) {
2569
+ await ctx.db.insert("epistemicEdges", {
2570
+ globalId: edgeGlobalId,
2571
+ fromNodeId: String(args.beliefNodeId),
2572
+ toNodeId: String(args.topicNode._id),
2573
+ sourceGlobalId: args.beliefGlobalId,
2574
+ targetGlobalId: topicGlobalId,
2575
+ edgeType: "scoped_by",
2576
+ weight: 1,
2577
+ confidence: 1,
2578
+ context: "Belief creation topic anchor invariant.",
2579
+ reasoningMethod: "implicit",
2580
+ derivationType: "topic_scope_invariant",
2581
+ metadata: { invariant: "belief.topic_edge_required" },
2582
+ createdBy: args.createdBy,
2583
+ createdAt: now,
2584
+ updatedAt: now,
2585
+ projectId: cleanString(args.topicNode.projectId),
2586
+ topicId: topicGlobalId,
2587
+ tenantId: cleanString(args.topicNode.tenantId),
2588
+ workspaceId: cleanString(args.topicNode.workspaceId),
2589
+ fromNodeType: "belief",
2590
+ toNodeType: "topic",
2591
+ fromLayer: "L3",
2592
+ toLayer: args.topicNode.epistemicLayer ?? "ontological"
2593
+ });
2594
+ }
2595
+ await ctx.scheduler.runAfter(0, internal.neo4jEdgeAPI.createEdge, {
2596
+ globalId: edgeGlobalId,
2597
+ fromGlobalId: args.beliefGlobalId,
2598
+ toGlobalId: topicGlobalId,
2599
+ edgeType: "scoped_by",
2600
+ weight: 1,
2601
+ confidence: 1,
2602
+ context: "Belief creation topic anchor invariant.",
2603
+ projectId: cleanString(args.topicNode.projectId),
2604
+ topicId: topicGlobalId,
2605
+ createdBy: args.createdBy,
2606
+ fromNodeType: "belief",
2607
+ toNodeType: "topic",
2608
+ fromLayer: "L3",
2609
+ toLayer: args.topicNode.epistemicLayer ?? "ontological",
2610
+ metadata: { invariant: "belief.topic_edge_required" }
2611
+ });
2612
+ }
2613
+
2362
2614
  // src/embeddingTrigger.ts
2363
2615
  async function scheduleEmbeddingGeneration(args) {
2364
2616
  try {
@@ -2391,23 +2643,17 @@ async function scheduleEmbeddingGeneration(args) {
2391
2643
  // src/globalId.ts
2392
2644
  var globalId_exports = {};
2393
2645
  __export(globalId_exports, {
2394
- generateGlobalId: () => generateGlobalId
2646
+ generateGlobalId: () => generateGlobalId,
2647
+ generateUuidV7: () => generateUuidV7,
2648
+ isUuidV7: () => isUuidV7
2395
2649
  });
2396
- function generateGlobalId() {
2397
- const bytes = new Uint8Array(16);
2398
- crypto.getRandomValues(bytes);
2399
- bytes[6] = bytes[6] & 15 | 64;
2400
- bytes[8] = bytes[8] & 63 | 128;
2401
- const hex = Array.from(bytes, (b) => b.toString(16).padStart(2, "0")).join(
2402
- ""
2403
- );
2404
- return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20)}`;
2405
- }
2406
2650
 
2407
2651
  // src/epistemicBeliefs.core.ts
2408
2652
  var create = mutation({
2409
2653
  args: {
2410
2654
  ...optionalBeliefScopeArgs,
2655
+ topicNodeId: v.optional(v.string()),
2656
+ topicGlobalId: v.optional(v.string()),
2411
2657
  formulation: v.string(),
2412
2658
  beliefType: v.optional(v.string()),
2413
2659
  rationale: v.optional(v.string()),
@@ -2452,20 +2698,32 @@ var create = mutation({
2452
2698
  returns: permissiveReturn,
2453
2699
  handler: async (ctx, args) => {
2454
2700
  const authenticatedUserId = await requireAuthenticatedUserId(ctx);
2455
- const scope = await resolveTopicProjectScope(ctx, {
2456
- topicId: args.topicId,
2457
- projectId: args.projectId
2458
- });
2701
+ const topicRef = readTopicNodeRef(args);
2702
+ if (!topicRef) {
2703
+ throwStructuredMutationError({
2704
+ message: "Belief creation requires an explicit topic epistemic node.",
2705
+ status: 400,
2706
+ code: "INVALID_ARGUMENT",
2707
+ invariantCode: "belief.topic_node_required",
2708
+ suggestion: "Pass topicGlobalId or topicNodeId for a topic in epistemicNodes before creating a belief.",
2709
+ details: {
2710
+ topicId: args.topicId,
2711
+ topicNodeId: args.topicNodeId,
2712
+ topicGlobalId: args.topicGlobalId
2713
+ }
2714
+ });
2715
+ }
2716
+ const topicNode = await resolveRequiredTopicAnchor(ctx, topicRef);
2717
+ const scope = scopeFromTopicAnchor(topicNode);
2459
2718
  assertWorkspaceScopedEpistemicNodeScope({
2460
2719
  scope,
2461
2720
  nodeType: "belief",
2462
2721
  mutationName: "epistemicBeliefs.create"
2463
2722
  });
2464
- const topic = await ctx.db.get(scope.topicId);
2465
2723
  const normalizedBeliefType = await assertSchemaEnumValue(ctx, {
2466
2724
  category: "belief_type",
2467
2725
  value: args.beliefType,
2468
- tenantId: topic?.tenantId,
2726
+ tenantId: scope.tenantId,
2469
2727
  context: "epistemicBeliefs.create"
2470
2728
  });
2471
2729
  if (scope.projectId) {
@@ -2498,7 +2756,7 @@ var create = mutation({
2498
2756
  title: args.formulation.slice(0, 100) + (args.formulation.length > 100 ? "..." : ""),
2499
2757
  metadata: {
2500
2758
  pillar,
2501
- // No confidenceLevel — only set after worktree completion via modulateConfidence()
2759
+ // No confidenceLevel — only set after evidence-backed SL scoring.
2502
2760
  status: "active",
2503
2761
  worktreeId: args.worktreeId,
2504
2762
  beliefStatus: initialBeliefStatus,
@@ -2542,9 +2800,15 @@ var create = mutation({
2542
2800
  rationale: "LKC-2 mandatory prior: seeded vacuous opinion at belief creation.",
2543
2801
  assessedBy: authenticatedUserId,
2544
2802
  assessedAt: now,
2545
- slOperator: "manual_assessment"
2803
+ slOperator: "prior_seed"
2546
2804
  })
2547
2805
  );
2806
+ await createRequiredBeliefTopicEdge(ctx, {
2807
+ beliefNodeId: nodeId,
2808
+ beliefGlobalId,
2809
+ topicNode,
2810
+ createdBy: authenticatedUserId
2811
+ });
2548
2812
  await ctx.scheduler.runAfter(0, internal.neo4jSync.syncNodeToNeo4j, {
2549
2813
  nodeId,
2550
2814
  operation: "upsert"
@@ -2881,9 +3145,10 @@ var forkBelief = mutation({
2881
3145
  v.literal("refinement"),
2882
3146
  v.literal("contradiction_response"),
2883
3147
  v.literal("scope_change"),
2884
- v.literal("confidence_collapse"),
2885
- v.literal("manual")
3148
+ v.literal("confidence_collapse")
2886
3149
  ),
3150
+ forkMode: v.optional(v.union(v.literal("supersede"), v.literal("branch"))),
3151
+ triggeringEvidenceId: v.id("epistemicNodes"),
2887
3152
  rationale: v.optional(v.string()),
2888
3153
  userId: v.string()
2889
3154
  },
@@ -2926,6 +3191,33 @@ var forkBelief = mutation({
2926
3191
  await requireProjectWriteAccess(ctx, parent.projectId, authenticatedUserId);
2927
3192
  const metadata = parent.metadata;
2928
3193
  const forkBeliefStatus = "hypothesis";
3194
+ const forkMode = args.forkMode ?? "supersede";
3195
+ const triggerEvidence = await resolveForkTriggerEvidence(ctx, {
3196
+ parentNodeId: args.parentNodeId,
3197
+ parent,
3198
+ triggeringEvidenceId: args.triggeringEvidenceId,
3199
+ forkMode
3200
+ });
3201
+ const parentLifecycleStatus = resolveBeliefLifecycleStatus({
3202
+ beliefStatus: parent.beliefStatus,
3203
+ confidence: parent.confidence,
3204
+ predictionMeta: parent.predictionMeta,
3205
+ metadata
3206
+ });
3207
+ if (forkMode === "supersede" && parentLifecycleStatus !== "active") {
3208
+ throwStructuredMutationError({
3209
+ message: "Superseding fork requires an active parent belief. Attach evidence first so the lifecycle can promote deterministically.",
3210
+ status: 409,
3211
+ code: "CONFLICT",
3212
+ invariantCode: "belief.supersede_requires_active_parent",
3213
+ suggestion: "Attach the contradicting evidence to the parent belief, let the evidence path promote it to active, then supersede.",
3214
+ details: {
3215
+ parentNodeId: args.parentNodeId,
3216
+ parentLifecycleStatus,
3217
+ triggeringEvidenceId: args.triggeringEvidenceId
3218
+ }
3219
+ });
3220
+ }
2929
3221
  const newBeliefGlobalId = generateGlobalId();
2930
3222
  const newNodeId = await ctx.db.insert("epistemicNodes", {
2931
3223
  globalId: newBeliefGlobalId,
@@ -2939,6 +3231,9 @@ var forkBelief = mutation({
2939
3231
  ...metadata,
2940
3232
  forkedFrom: args.parentNodeId,
2941
3233
  forkReason: args.forkReason,
3234
+ forkMode,
3235
+ triggeringEvidenceId: args.triggeringEvidenceId,
3236
+ triggeringEvidenceRelation: triggerEvidence.relation,
2942
3237
  forkTimestamp: now,
2943
3238
  forkedBy: authenticatedUserId,
2944
3239
  status: "active",
@@ -2957,6 +3252,27 @@ var forkBelief = mutation({
2957
3252
  createdAt: now,
2958
3253
  updatedAt: now
2959
3254
  });
3255
+ if (forkMode === "supersede") {
3256
+ await ctx.db.patch(args.parentNodeId, {
3257
+ status: "superseded",
3258
+ beliefStatus: "superseded",
3259
+ epistemicStatus: "superseded",
3260
+ supersededBy: newNodeId,
3261
+ updatedAt: now,
3262
+ metadata: {
3263
+ ...metadata ?? {},
3264
+ status: "superseded",
3265
+ beliefStatus: "superseded",
3266
+ epistemicStatus: "superseded",
3267
+ supersededBy: String(newNodeId),
3268
+ supersededByEvidenceId: String(triggerEvidence.evidenceNodeId)
3269
+ }
3270
+ });
3271
+ await ctx.scheduler.runAfter(0, internal.neo4jSync.syncNodeToNeo4j, {
3272
+ nodeId: args.parentNodeId,
3273
+ operation: "upsert"
3274
+ });
3275
+ }
2960
3276
  const inheritedContracts = await ctx.db.query("epistemicContracts").withIndex(
2961
3277
  "by_belief",
2962
3278
  (q) => q.eq("beliefNodeId", args.parentNodeId)
@@ -2983,12 +3299,17 @@ var forkBelief = mutation({
2983
3299
  globalId: generateGlobalId(),
2984
3300
  fromGlobalId: newBeliefGlobalId,
2985
3301
  toGlobalId: parent.globalId,
2986
- edgeType: "supersedes",
2987
- context: `Fork reason: ${args.forkReason}`,
3302
+ edgeType: forkMode === "supersede" ? "supersedes" : "derived_from",
3303
+ context: `Fork reason: ${args.forkReason}; triggering evidence: ${triggerEvidence.evidenceNodeId}`,
2988
3304
  createdBy: authenticatedUserId,
2989
3305
  topicId: parent.projectId ? String(parent.projectId) : void 0,
2990
3306
  fromNodeType: "belief",
2991
- toNodeType: "belief"
3307
+ toNodeType: "belief",
3308
+ metadata: {
3309
+ forkMode,
3310
+ triggeringEvidenceId: String(triggerEvidence.evidenceNodeId),
3311
+ triggeringEvidenceRelation: triggerEvidence.relation
3312
+ }
2992
3313
  });
2993
3314
  await scheduleEmbeddingGeneration({
2994
3315
  ctx,
@@ -3013,6 +3334,9 @@ var forkBelief = mutation({
3013
3334
  newState: {
3014
3335
  formulation: args.newFormulation,
3015
3336
  forkReason: args.forkReason,
3337
+ forkMode,
3338
+ triggeringEvidenceId: String(triggerEvidence.evidenceNodeId),
3339
+ triggeringEvidenceRelation: triggerEvidence.relation,
3016
3340
  tupleContradicted: false
3017
3341
  },
3018
3342
  projectId: parent.projectId,
@@ -3047,10 +3371,17 @@ var forkBelief = mutation({
3047
3371
  projectId: parent.projectId,
3048
3372
  topicId: parent.topicId
3049
3373
  });
3050
- return { newNodeId, parentNodeId: args.parentNodeId };
3374
+ return {
3375
+ newNodeId,
3376
+ parentNodeId: args.parentNodeId,
3377
+ forkMode,
3378
+ forkReason: args.forkReason,
3379
+ triggeringEvidenceId: triggerEvidence.evidenceNodeId,
3380
+ triggeringEvidenceRelation: triggerEvidence.relation
3381
+ };
3051
3382
  }
3052
3383
  });
3053
- var modulateConfidence = mutation({
3384
+ var appendSlScoring = mutation({
3054
3385
  args: {
3055
3386
  nodeId: v.id("epistemicNodes"),
3056
3387
  // SL opinion — the ONLY confidence input (EK-7)
@@ -3067,8 +3398,6 @@ var modulateConfidence = mutation({
3067
3398
  v.literal("evidence_removed"),
3068
3399
  v.literal("contradiction_detected"),
3069
3400
  v.literal("contradiction_resolved"),
3070
- v.literal("manual"),
3071
- v.literal("decay"),
3072
3401
  v.literal("agent_assessment"),
3073
3402
  v.literal("worktree_outcome"),
3074
3403
  v.literal("worktree_completed"),
@@ -3080,7 +3409,7 @@ var modulateConfidence = mutation({
3080
3409
  ),
3081
3410
  rationale: v.optional(v.string()),
3082
3411
  userId: v.string(),
3083
- // SL operator provenance (optional — defaults to manual_assessment)
3412
+ // SL operator provenance (optional — defaults inside the SL scorer)
3084
3413
  slOperator: v.optional(
3085
3414
  v.union(
3086
3415
  v.literal("cumulative_fusion"),
@@ -3090,7 +3419,8 @@ var modulateConfidence = mutation({
3090
3419
  v.literal("dependency_cascade"),
3091
3420
  v.literal("negation"),
3092
3421
  v.literal("constraint_fusion"),
3093
- v.literal("manual_assessment")
3422
+ v.literal("no_op"),
3423
+ v.literal("prior_seed")
3094
3424
  )
3095
3425
  ),
3096
3426
  triggeringEvidenceId: v.optional(v.id("epistemicNodes")),
@@ -3457,6 +3787,12 @@ async function getQuestionsAnsweredByEvidence(ctx, evidenceId) {
3457
3787
  }
3458
3788
 
3459
3789
  // src/epistemicBeliefs.links.ts
3790
+ function assertSignedImpactScore(value, context) {
3791
+ if (typeof value !== "number" || !Number.isFinite(value) || value === 0 || value < -1 || value > 1) {
3792
+ throw new Error(`${context} requires explicit nonzero weight in [-1, 1]`);
3793
+ }
3794
+ return value;
3795
+ }
3460
3796
  var updatePillar = mutation({
3461
3797
  args: {
3462
3798
  nodeId: v.id("epistemicNodes"),
@@ -3636,6 +3972,7 @@ var linkEvidence = mutation({
3636
3972
  beliefNodeId: v.id("epistemicNodes"),
3637
3973
  insightId: insightIdUnion,
3638
3974
  type: v.union(v.literal("supporting"), v.literal("contradicting")),
3975
+ weight: v.number(),
3639
3976
  rationale: v.optional(v.string()),
3640
3977
  userId: v.string()
3641
3978
  },
@@ -3659,16 +3996,54 @@ var linkEvidence = mutation({
3659
3996
  }
3660
3997
  const evidenceNodeId = insight._id;
3661
3998
  const evidenceGlobalId = insight.globalId;
3999
+ const weight = assertSignedImpactScore(args.weight, "Evidence link");
4000
+ if (args.type === "supporting" && weight < 0) {
4001
+ throw new Error("Supporting evidence links require positive weight");
4002
+ }
4003
+ if (args.type === "contradicting" && weight > 0) {
4004
+ throw new Error("Contradicting evidence links require negative weight");
4005
+ }
4006
+ const confidence = Math.abs(weight);
3662
4007
  const edgeType = "informs";
3663
- const weight = args.type === "supporting" ? 1 : -1;
3664
4008
  const logicalRole = evidenceNodeId ? await computeLogicalRole(ctx, evidenceNodeId, args.beliefNodeId) : "contributory";
3665
4009
  const edgeGlobalId = generateGlobalId();
4010
+ await ctx.db.insert("epistemicEdges", {
4011
+ globalId: edgeGlobalId,
4012
+ fromNodeId: evidenceNodeId,
4013
+ toNodeId: args.beliefNodeId,
4014
+ sourceGlobalId: evidenceGlobalId,
4015
+ targetGlobalId: belief.globalId,
4016
+ edgeType,
4017
+ weight,
4018
+ confidence,
4019
+ context: args.rationale || `${args.type} evidence`,
4020
+ reasoningMethod: "testimonial",
4021
+ logicalRole,
4022
+ temporalClass: "structural",
4023
+ derivationType: "evidence_sl_scoring",
4024
+ metadata: {
4025
+ relation: args.type,
4026
+ confidence,
4027
+ impactScore: weight,
4028
+ invariant: "evidence.belief_impact_required"
4029
+ },
4030
+ createdBy: authenticatedUserId,
4031
+ createdAt: Date.now(),
4032
+ updatedAt: Date.now(),
4033
+ topicId: belief.topicId ? String(belief.topicId) : void 0,
4034
+ projectId: belief.projectId ? String(belief.projectId) : void 0,
4035
+ fromNodeType: "evidence",
4036
+ toNodeType: "belief",
4037
+ fromLayer: "L2",
4038
+ toLayer: "L3"
4039
+ });
3666
4040
  await ctx.scheduler.runAfter(0, internal.neo4jEdgeAPI.createEdge, {
3667
4041
  globalId: edgeGlobalId,
3668
4042
  fromGlobalId: evidenceGlobalId,
3669
4043
  toGlobalId: belief.globalId,
3670
4044
  edgeType,
3671
4045
  weight,
4046
+ confidence,
3672
4047
  context: args.rationale || `${args.type} evidence`,
3673
4048
  createdBy: authenticatedUserId,
3674
4049
  topicId: belief.projectId ? String(belief.projectId) : void 0,
@@ -4299,6 +4674,8 @@ var internalGetById = internalQuery({
4299
4674
  var internalCreate = internalMutation({
4300
4675
  args: {
4301
4676
  ...optionalBeliefScopeArgs,
4677
+ topicNodeId: v.optional(v.string()),
4678
+ topicGlobalId: v.optional(v.string()),
4302
4679
  formulation: v.string(),
4303
4680
  baseRate: v.optional(v.number()),
4304
4681
  confidence: v.optional(
@@ -4337,10 +4714,14 @@ var internalCreate = internalMutation({
4337
4714
  handler: async (ctx, args) => {
4338
4715
  const now = Date.now();
4339
4716
  const baseRate = assertBaseRateInRange(args.baseRate ?? 0.5);
4340
- const scope = await resolveTopicProjectScope(ctx, {
4341
- topicId: args.topicId,
4342
- projectId: args.projectId
4343
- });
4717
+ const topicRef = readTopicNodeRef(args);
4718
+ if (!topicRef) {
4719
+ throw new Error(
4720
+ "Belief creation requires topicGlobalId or topicNodeId for a topic node in epistemicNodes."
4721
+ );
4722
+ }
4723
+ const topicNode = await resolveRequiredTopicAnchor(ctx, topicRef);
4724
+ const scope = scopeFromTopicAnchor(topicNode);
4344
4725
  assertWorkspaceScopedEpistemicNodeScope({
4345
4726
  scope,
4346
4727
  nodeType: "belief",
@@ -4356,11 +4737,10 @@ var internalCreate = internalMutation({
4356
4737
  },
4357
4738
  mutationName: "epistemicBeliefs.internalCreate"
4358
4739
  });
4359
- const topic = await ctx.db.get(scope.topicId);
4360
4740
  const normalizedBeliefType = await assertSchemaEnumValue(ctx, {
4361
4741
  category: "belief_type",
4362
4742
  value: args.beliefType,
4363
- tenantId: topic?.tenantId,
4743
+ tenantId: scope.tenantId,
4364
4744
  context: "epistemicBeliefs.internalCreate"
4365
4745
  });
4366
4746
  const globalId = generateGlobalId();
@@ -4424,9 +4804,15 @@ var internalCreate = internalMutation({
4424
4804
  rationale: "LKC-2 mandatory prior: seeded vacuous opinion at belief creation.",
4425
4805
  assessedBy: args.userId,
4426
4806
  assessedAt: now,
4427
- slOperator: "manual_assessment"
4807
+ slOperator: "prior_seed"
4428
4808
  })
4429
4809
  );
4810
+ await createRequiredBeliefTopicEdge(ctx, {
4811
+ beliefNodeId: nodeId,
4812
+ beliefGlobalId: globalId,
4813
+ topicNode,
4814
+ createdBy: args.userId
4815
+ });
4430
4816
  await ctx.scheduler.runAfter(0, internal.neo4jSync.syncNodeToNeo4j, {
4431
4817
  nodeId,
4432
4818
  operation: "upsert"
@@ -4600,7 +4986,7 @@ var backfillSyntheticOpinionHistory = internalMutation({
4600
4986
  rationale: "LK-6 backfill: synthesized t0 from node-level opinion fields (no prior beliefConfidence row found).",
4601
4987
  assessedAt: readFiniteNumber(node.createdAt) ?? readFiniteNumber(node.updatedAt) ?? Date.now(),
4602
4988
  assessedBy: "system:lk-6-backfill",
4603
- slOperator: "manual_assessment"
4989
+ slOperator: "prior_seed"
4604
4990
  })
4605
4991
  );
4606
4992
  beliefsWithHistory.add(String(node._id));
@@ -4706,7 +5092,7 @@ var backfillMandatoryPriors = internalMutation({
4706
5092
  rationale: "LKC-2 backfill: inserted missing initial vacuous opinion with neutral prior.",
4707
5093
  assessedAt: readFiniteNumber(node.createdAt) ?? readFiniteNumber(node.updatedAt) ?? Date.now(),
4708
5094
  assessedBy: "system:lkc-2-prior-backfill",
4709
- slOperator: "manual_assessment"
5095
+ slOperator: "prior_seed"
4710
5096
  })
4711
5097
  );
4712
5098
  insertedInitialRows++;
@@ -4814,7 +5200,7 @@ var backfillScoredBeliefEdges = internalMutation({
4814
5200
  const scoredBeliefs = allBeliefs.filter((belief) => {
4815
5201
  const metadata = belief.metadata || {};
4816
5202
  const lifecycle = resolveBeliefStatus(belief, metadata);
4817
- return lifecycle === "belief" || lifecycle === "fact";
5203
+ return lifecycle === "active" || lifecycle === "resolved_true";
4818
5204
  });
4819
5205
  const themeNodes = await ctx.db.query("epistemicNodes").withIndex(
4820
5206
  "by_topic",
@@ -6300,85 +6686,32 @@ async function resolveScopeSoft(ctx, args) {
6300
6686
  ...projectId ? { projectId } : {}
6301
6687
  };
6302
6688
  }
6303
- var beliefIdUnion = v.id("epistemicNodes");
6304
- var insightIdUnion2 = v.id("epistemicNodes");
6305
- var suggestionStatusValidator = v.union(
6306
- v.literal("suggested"),
6307
- v.literal("approved"),
6308
- v.literal("dismissed")
6309
- );
6310
- var matcherMetadataValidator = v.object({
6311
- surface: v.string(),
6312
- matcherFamily: v.optional(v.string()),
6313
- matcherKey: v.optional(v.string()),
6314
- matcherVersion: v.optional(v.string()),
6315
- reviewStatus: v.optional(
6316
- v.union(
6317
- v.literal("pending"),
6318
- v.literal("accepted"),
6319
- v.literal("rejected"),
6320
- v.literal("auto_accepted"),
6321
- v.literal("superseded")
6322
- )
6323
- ),
6324
- configSnapshot: v.optional(v.any()),
6325
- signalSnapshot: v.optional(v.any()),
6326
- outcomeMetadata: v.optional(v.any())
6327
- });
6328
- async function markProjectGraphDirty(ctx, projectId) {
6329
- if (!projectId) {
6330
- return;
6689
+
6690
+ // src/beliefEvidenceLinks.operational.ts
6691
+ function assertEvidenceImpact(args) {
6692
+ if (!Number.isFinite(args.weight) || args.weight === 0 || args.weight < -1 || args.weight > 1) {
6693
+ throw new Error("Belief evidence links require explicit nonzero weight in [-1, 1]");
6331
6694
  }
6332
- await ctx.scheduler.runAfter(
6333
- 0,
6334
- internal.graphAnalysisCache.markCacheStaleInternal,
6335
- {
6336
- projectId
6337
- }
6338
- );
6339
- await resolveGraphPrimitivesAppResolvers().patchProject(ctx, projectId, {
6340
- lastActivityAt: Date.now()
6341
- });
6342
- }
6343
- async function recordMatcherDecision(ctx, args) {
6344
- if (!args.matcherMetadata) {
6345
- return;
6695
+ if (args.relation === "supports" && args.weight < 0) {
6696
+ throw new Error("Supporting evidence links require positive weight");
6697
+ }
6698
+ if (args.relation === "contradicts" && args.weight > 0) {
6699
+ throw new Error("Contradicting evidence links require negative weight");
6346
6700
  }
6347
- await ctx.runMutation("matcherFeedback:recordDecision", {
6348
- projectId: args.beliefNode.projectId,
6349
- topicId: args.beliefNode.topicId,
6350
- surface: args.matcherMetadata.surface,
6351
- matcherFamily: args.matcherMetadata.matcherFamily,
6352
- matcherKey: args.matcherMetadata.matcherKey,
6353
- matcherVersion: args.matcherMetadata.matcherVersion,
6354
- sourceEntityId: String(args.insightId),
6355
- targetEntityId: String(args.beliefId),
6356
- suggestionTable: "beliefEvidenceLinks",
6357
- suggestionId: args.linkId,
6358
- reviewStatus: deriveMatcherReviewStatus({
6359
- explicitReviewStatus: args.matcherMetadata.reviewStatus,
6360
- linkStatus: args.linkStatus
6361
- }),
6362
- reviewedBy: args.reviewedBy,
6363
- decisionReason: args.decisionReason,
6364
- configSnapshot: args.matcherMetadata.configSnapshot,
6365
- signalSnapshot: args.matcherMetadata.signalSnapshot,
6366
- outcomeMetadata: args.matcherMetadata.outcomeMetadata
6367
- });
6368
6701
  }
6369
6702
  async function applyOperationalLinkEffects(ctx, args) {
6703
+ assertEvidenceImpact({ relation: args.relation, weight: args.weight });
6704
+ const confidence = Math.abs(args.weight);
6370
6705
  const currentSupporting = args.beliefNode.supportingInsightIds ?? [];
6371
6706
  const currentContradicting = args.beliefNode.contradictingInsightIds ?? [];
6372
- if (args.relation === "supports") {
6373
- if (!currentSupporting.includes(args.insightId)) {
6374
- await ctx.db.patch(args.beliefId, {
6375
- supportingInsightIds: [...currentSupporting, args.insightId],
6376
- contradictingInsightIds: currentContradicting.filter(
6377
- (id) => id !== args.insightId
6378
- )
6379
- });
6380
- }
6381
- } else if (!currentContradicting.includes(args.insightId)) {
6707
+ if (args.relation === "supports" && !currentSupporting.includes(args.insightId)) {
6708
+ await ctx.db.patch(args.beliefId, {
6709
+ supportingInsightIds: [...currentSupporting, args.insightId],
6710
+ contradictingInsightIds: currentContradicting.filter(
6711
+ (id) => id !== args.insightId
6712
+ )
6713
+ });
6714
+ } else if (args.relation === "contradicts" && !currentContradicting.includes(args.insightId)) {
6382
6715
  await ctx.db.patch(args.beliefId, {
6383
6716
  contradictingInsightIds: [...currentContradicting, args.insightId],
6384
6717
  supportingInsightIds: currentSupporting.filter(
@@ -6389,7 +6722,7 @@ async function applyOperationalLinkEffects(ctx, args) {
6389
6722
  try {
6390
6723
  const beliefSpineNode = await ctx.db.get(args.beliefId);
6391
6724
  const evidenceSpineNode = await ctx.db.get(args.insightId);
6392
- if (beliefSpineNode && beliefSpineNode.nodeType === "belief" && evidenceSpineNode && evidenceSpineNode.nodeType === "evidence") {
6725
+ if (beliefSpineNode?.nodeType === "belief" && evidenceSpineNode?.nodeType === "evidence") {
6393
6726
  const existingEdges = await ctx.db.query("epistemicEdges").withIndex(
6394
6727
  "by_from_to",
6395
6728
  (q) => q.eq("fromNodeId", evidenceSpineNode._id).eq("toNodeId", beliefSpineNode._id)
@@ -6402,16 +6735,44 @@ async function applyOperationalLinkEffects(ctx, args) {
6402
6735
  await ctx.db.delete(edge._id);
6403
6736
  }
6404
6737
  }
6405
- const weight = args.relation === "supports" ? args.confidence ?? 0.7 : -(args.confidence ?? 0.7);
6406
6738
  const globalId = `edge-${Date.now()}-${Math.random().toString(36).slice(2, 11)}`;
6739
+ const context = args.rationale || `Linked as ${args.relation}`;
6740
+ await ctx.db.insert("epistemicEdges", {
6741
+ globalId,
6742
+ fromNodeId: evidenceSpineNode._id,
6743
+ toNodeId: beliefSpineNode._id,
6744
+ sourceGlobalId: evidenceSpineNode.globalId,
6745
+ targetGlobalId: beliefSpineNode.globalId,
6746
+ edgeType: "informs",
6747
+ weight: args.weight,
6748
+ confidence,
6749
+ context,
6750
+ reasoningMethod: "testimonial",
6751
+ derivationType: "evidence_sl_scoring",
6752
+ metadata: {
6753
+ relation: args.relation,
6754
+ confidence,
6755
+ impactScore: args.weight,
6756
+ invariant: "evidence.belief_impact_required"
6757
+ },
6758
+ createdBy: args.createdBy,
6759
+ createdAt: Date.now(),
6760
+ updatedAt: Date.now(),
6761
+ projectId: args.beliefNode.projectId,
6762
+ topicId: args.beliefNode.topicId ? String(args.beliefNode.topicId) : void 0,
6763
+ fromNodeType: "evidence",
6764
+ toNodeType: "belief",
6765
+ fromLayer: "L2",
6766
+ toLayer: "L3"
6767
+ });
6407
6768
  await ctx.scheduler.runAfter(0, internal.neo4jEdgeAPI.createEdge, {
6408
6769
  globalId,
6409
6770
  fromGlobalId: evidenceSpineNode.globalId,
6410
6771
  toGlobalId: beliefSpineNode.globalId,
6411
6772
  edgeType: "informs",
6412
- weight,
6413
- confidence: args.confidence,
6414
- context: args.rationale || `Linked as ${args.relation}`,
6773
+ weight: args.weight,
6774
+ confidence,
6775
+ context,
6415
6776
  projectId: args.beliefNode.projectId ? String(args.beliefNode.projectId) : void 0,
6416
6777
  createdBy: args.createdBy,
6417
6778
  fromNodeType: "evidence",
@@ -6424,17 +6785,13 @@ async function applyOperationalLinkEffects(ctx, args) {
6424
6785
  console.error("[EpistemicSpine] Failed to create informs edge:", e);
6425
6786
  }
6426
6787
  if (args.beliefNode.projectId) {
6427
- await ctx.scheduler.runAfter(
6428
- 0,
6429
- "verificationActions:deepVerifyEvidence",
6430
- {
6431
- insightId: args.insightId,
6432
- targetType: "belief",
6433
- targetId: args.beliefId,
6434
- projectId: args.beliefNode.projectId,
6435
- userId: args.createdBy
6436
- }
6437
- );
6788
+ await ctx.scheduler.runAfter(0, "verificationActions:deepVerifyEvidence", {
6789
+ insightId: args.insightId,
6790
+ targetType: "belief",
6791
+ targetId: args.beliefId,
6792
+ projectId: args.beliefNode.projectId,
6793
+ userId: args.createdBy
6794
+ });
6438
6795
  }
6439
6796
  }
6440
6797
  async function removeOperationalLinkEffects(ctx, args) {
@@ -6459,7 +6816,7 @@ async function removeOperationalLinkEffects(ctx, args) {
6459
6816
  try {
6460
6817
  const beliefSpineNode = await ctx.db.get(args.beliefId);
6461
6818
  const evidenceSpineNode = await ctx.db.get(args.insightId);
6462
- if (beliefSpineNode && beliefSpineNode.nodeType === "belief" && evidenceSpineNode && evidenceSpineNode.nodeType === "evidence") {
6819
+ if (beliefSpineNode?.nodeType === "belief" && evidenceSpineNode?.nodeType === "evidence") {
6463
6820
  const edges2 = await ctx.db.query("epistemicEdges").withIndex(
6464
6821
  "by_from_to",
6465
6822
  (q) => q.eq("fromNodeId", evidenceSpineNode._id).eq("toNodeId", beliefSpineNode._id)
@@ -6477,11 +6834,80 @@ async function removeOperationalLinkEffects(ctx, args) {
6477
6834
  console.error("[EpistemicSpine] Failed to remove informs edge:", e);
6478
6835
  }
6479
6836
  }
6837
+
6838
+ // src/beliefEvidenceLinks.ts
6839
+ var beliefIdUnion = v.id("epistemicNodes");
6840
+ var insightIdUnion2 = v.id("epistemicNodes");
6841
+ var suggestionStatusValidator = v.union(
6842
+ v.literal("suggested"),
6843
+ v.literal("approved"),
6844
+ v.literal("dismissed")
6845
+ );
6846
+ var matcherMetadataValidator = v.object({
6847
+ surface: v.string(),
6848
+ matcherFamily: v.optional(v.string()),
6849
+ matcherKey: v.optional(v.string()),
6850
+ matcherVersion: v.optional(v.string()),
6851
+ reviewStatus: v.optional(
6852
+ v.union(
6853
+ v.literal("pending"),
6854
+ v.literal("accepted"),
6855
+ v.literal("rejected"),
6856
+ v.literal("auto_accepted"),
6857
+ v.literal("superseded")
6858
+ )
6859
+ ),
6860
+ configSnapshot: v.optional(v.any()),
6861
+ signalSnapshot: v.optional(v.any()),
6862
+ outcomeMetadata: v.optional(v.any())
6863
+ });
6864
+ async function markProjectGraphDirty(ctx, projectId) {
6865
+ if (!projectId) {
6866
+ return;
6867
+ }
6868
+ await ctx.scheduler.runAfter(
6869
+ 0,
6870
+ internal.graphAnalysisCache.markCacheStaleInternal,
6871
+ {
6872
+ projectId
6873
+ }
6874
+ );
6875
+ await resolveGraphPrimitivesAppResolvers().patchProject(ctx, projectId, {
6876
+ lastActivityAt: Date.now()
6877
+ });
6878
+ }
6879
+ async function recordMatcherDecision(ctx, args) {
6880
+ if (!args.matcherMetadata) {
6881
+ return;
6882
+ }
6883
+ await ctx.runMutation("matcherFeedback:recordDecision", {
6884
+ projectId: args.beliefNode.projectId,
6885
+ topicId: args.beliefNode.topicId,
6886
+ surface: args.matcherMetadata.surface,
6887
+ matcherFamily: args.matcherMetadata.matcherFamily,
6888
+ matcherKey: args.matcherMetadata.matcherKey,
6889
+ matcherVersion: args.matcherMetadata.matcherVersion,
6890
+ sourceEntityId: String(args.insightId),
6891
+ targetEntityId: String(args.beliefId),
6892
+ suggestionTable: "beliefEvidenceLinks",
6893
+ suggestionId: args.linkId,
6894
+ reviewStatus: deriveMatcherReviewStatus({
6895
+ explicitReviewStatus: args.matcherMetadata.reviewStatus,
6896
+ linkStatus: args.linkStatus
6897
+ }),
6898
+ reviewedBy: args.reviewedBy,
6899
+ decisionReason: args.decisionReason,
6900
+ configSnapshot: args.matcherMetadata.configSnapshot,
6901
+ signalSnapshot: args.matcherMetadata.signalSnapshot,
6902
+ outcomeMetadata: args.matcherMetadata.outcomeMetadata
6903
+ });
6904
+ }
6480
6905
  var create2 = mutation({
6481
6906
  args: {
6482
6907
  beliefId: beliefIdUnion,
6483
6908
  insightId: insightIdUnion2,
6484
6909
  relation: v.union(v.literal("supports"), v.literal("contradicts")),
6910
+ weight: v.number(),
6485
6911
  confidence: v.optional(v.number()),
6486
6912
  rationale: v.optional(v.string()),
6487
6913
  status: v.optional(suggestionStatusValidator),
@@ -6500,6 +6926,16 @@ var create2 = mutation({
6500
6926
  if (!insightNode) {
6501
6927
  throw new Error("Evidence node not found");
6502
6928
  }
6929
+ if (!Number.isFinite(args.weight) || args.weight === 0 || args.weight < -1 || args.weight > 1) {
6930
+ throw new Error("Belief evidence links require explicit nonzero weight in [-1, 1]");
6931
+ }
6932
+ if (args.relation === "supports" && args.weight < 0) {
6933
+ throw new Error("Supporting evidence links require positive weight");
6934
+ }
6935
+ if (args.relation === "contradicts" && args.weight > 0) {
6936
+ throw new Error("Contradicting evidence links require negative weight");
6937
+ }
6938
+ const relationConfidence = Math.abs(args.weight);
6503
6939
  if (beliefNode.projectId) {
6504
6940
  const hasAccess = await checkProjectAccess(
6505
6941
  ctx,
@@ -6524,7 +6960,7 @@ var create2 = mutation({
6524
6960
  );
6525
6961
  await ctx.db.patch(duplicate._id, {
6526
6962
  relation: args.relation,
6527
- confidence: args.confidence,
6963
+ confidence: relationConfidence,
6528
6964
  rationale: args.rationale,
6529
6965
  status: nextStatus
6530
6966
  });
@@ -6536,7 +6972,7 @@ var create2 = mutation({
6536
6972
  beliefId: resolvedBeliefId,
6537
6973
  insightId: resolvedInsightId,
6538
6974
  relation: args.relation,
6539
- confidence: args.confidence,
6975
+ weight: args.weight,
6540
6976
  rationale: args.rationale,
6541
6977
  createdBy: args.createdBy
6542
6978
  });
@@ -6552,15 +6988,12 @@ var create2 = mutation({
6552
6988
  await markProjectGraphDirty(ctx, beliefNode.projectId);
6553
6989
  return duplicate._id;
6554
6990
  }
6555
- if (args.confidence !== void 0 && (args.confidence < 0 || args.confidence > 1)) {
6556
- throw new Error("Confidence must be between 0 and 1");
6557
- }
6558
6991
  const now = Date.now();
6559
6992
  const linkId = await ctx.db.insert("beliefEvidenceLinks", {
6560
6993
  beliefId: args.beliefId,
6561
6994
  insightId: args.insightId,
6562
6995
  relation: args.relation,
6563
- confidence: args.confidence,
6996
+ confidence: relationConfidence,
6564
6997
  rationale: args.rationale,
6565
6998
  status: args.status,
6566
6999
  createdBy: args.createdBy,
@@ -6580,7 +7013,7 @@ var create2 = mutation({
6580
7013
  beliefId: resolvedBeliefId,
6581
7014
  insightId: resolvedInsightId,
6582
7015
  relation: args.relation,
6583
- confidence: args.confidence,
7016
+ weight: args.weight,
6584
7017
  rationale: args.rationale,
6585
7018
  createdBy: args.createdBy
6586
7019
  });
@@ -6688,7 +7121,7 @@ var reviewSuggestion = mutation({
6688
7121
  beliefId: link.beliefId,
6689
7122
  insightId: link.insightId,
6690
7123
  relation: link.relation,
6691
- confidence: link.confidence,
7124
+ weight: link.relation === "supports" ? Math.abs(link.confidence ?? 0) : -Math.abs(link.confidence ?? 0),
6692
7125
  rationale: link.rationale,
6693
7126
  createdBy: args.userId
6694
7127
  });
@@ -8937,7 +9370,7 @@ async function evaluateBuiltInEvidentialContract(args) {
8937
9370
  observedValue: snapshot.value,
8938
9371
  operator: config.operator,
8939
9372
  threshold: config.threshold,
8940
- action: config.action ?? "modulate_confidence",
9373
+ action: config.action ?? "append_sl_scoring",
8941
9374
  actionParams: config.actionParams
8942
9375
  }
8943
9376
  };
@@ -11707,6 +12140,91 @@ function formatEvidenceNode(n) {
11707
12140
  updatedAt: n.updatedAt
11708
12141
  };
11709
12142
  }
12143
+ function assertSignedImpactScore2(value, context) {
12144
+ if (typeof value !== "number" || !Number.isFinite(value) || value === 0 || value < -1 || value > 1) {
12145
+ throw new Error(`${context} requires explicit nonzero weight in [-1, 1]`);
12146
+ }
12147
+ return value;
12148
+ }
12149
+ function normalizeEvidenceRelation(relation, weight, context) {
12150
+ if (relation === "supports" || relation === "contradicts") {
12151
+ if (relation === "supports" && weight < 0) {
12152
+ throw new Error(`${context} supports relation requires positive weight`);
12153
+ }
12154
+ if (relation === "contradicts" && weight > 0) {
12155
+ throw new Error(`${context} contradicts relation requires negative weight`);
12156
+ }
12157
+ return relation;
12158
+ }
12159
+ return weight < 0 ? "contradicts" : "supports";
12160
+ }
12161
+ async function createEvidenceBeliefEdge(ctx, args) {
12162
+ const edgeGlobalId = crypto.randomUUID();
12163
+ const confidence = Math.abs(args.weight);
12164
+ const existingEdges = await ctx.db.query("epistemicEdges").withIndex(
12165
+ "by_from_to",
12166
+ (q) => q.eq("fromNodeId", args.evidenceNodeId).eq("toNodeId", args.beliefNodeId)
12167
+ ).collect();
12168
+ const existing = existingEdges.find((edge) => edge.edgeType === "informs");
12169
+ const edgeDoc = {
12170
+ globalId: edgeGlobalId,
12171
+ fromNodeId: args.evidenceNodeId,
12172
+ toNodeId: args.beliefNodeId,
12173
+ sourceGlobalId: args.evidenceGlobalId,
12174
+ targetGlobalId: args.beliefGlobalId,
12175
+ edgeType: "informs",
12176
+ weight: args.weight,
12177
+ confidence,
12178
+ context: args.rationale,
12179
+ reasoningMethod: "testimonial",
12180
+ derivationType: "evidence_sl_scoring",
12181
+ metadata: {
12182
+ relation: args.relation,
12183
+ confidence,
12184
+ impactScore: args.weight,
12185
+ invariant: "evidence.belief_impact_required"
12186
+ },
12187
+ createdBy: args.userId,
12188
+ createdAt: Date.now(),
12189
+ updatedAt: Date.now(),
12190
+ topicId: args.topicId,
12191
+ projectId: args.projectId,
12192
+ fromNodeType: "evidence",
12193
+ toNodeType: "belief",
12194
+ fromLayer: "L2",
12195
+ toLayer: "L3"
12196
+ };
12197
+ if (existing) {
12198
+ await ctx.db.patch(existing._id, {
12199
+ ...edgeDoc,
12200
+ globalId: existing.globalId,
12201
+ createdAt: existing.createdAt ?? edgeDoc.createdAt
12202
+ });
12203
+ } else {
12204
+ await ctx.db.insert("epistemicEdges", edgeDoc);
12205
+ }
12206
+ await ctx.scheduler.runAfter(5e3, internal.neo4jEdgeAPI.createEdge, {
12207
+ globalId: existing?.globalId ?? edgeGlobalId,
12208
+ fromGlobalId: args.evidenceGlobalId,
12209
+ toGlobalId: args.beliefGlobalId,
12210
+ edgeType: "informs",
12211
+ weight: args.weight,
12212
+ confidence,
12213
+ createdBy: args.userId,
12214
+ topicId: args.projectId ?? args.topicId,
12215
+ fromNodeType: "evidence",
12216
+ toNodeType: "belief",
12217
+ fromLayer: "L2",
12218
+ toLayer: "L3",
12219
+ metadata: {
12220
+ relation: args.relation,
12221
+ confidence,
12222
+ impactScore: args.weight,
12223
+ invariant: "evidence.belief_impact_required"
12224
+ }
12225
+ });
12226
+ return existing?.globalId ?? edgeGlobalId;
12227
+ }
11710
12228
  var create6 = mutation({
11711
12229
  args: {
11712
12230
  ...optionalEvidenceScopeArgs,
@@ -11727,11 +12245,12 @@ var create6 = mutation({
11727
12245
  informationAsymmetry: v.optional(v.string()),
11728
12246
  sourceDescription: v.optional(v.string()),
11729
12247
  metadata: v.optional(v.any()),
11730
- // Optional linking to beliefs
11731
- linkedBeliefNodeId: v.optional(v.id("epistemicNodes")),
12248
+ // Required belief impact link.
12249
+ linkedBeliefNodeId: v.id("epistemicNodes"),
11732
12250
  evidenceRelation: v.optional(
11733
12251
  v.union(v.literal("supports"), v.literal("contradicts"))
11734
12252
  ),
12253
+ weight: v.number(),
11735
12254
  confidence: v.optional(v.number())
11736
12255
  },
11737
12256
  returns: permissiveReturn,
@@ -11753,6 +12272,16 @@ var create6 = mutation({
11753
12272
  const contentHash = generateContentHash5(args.text);
11754
12273
  const kind = normalizeKind(args.kind);
11755
12274
  const sourceType = normalizeSourceType(args.sourceType);
12275
+ const weight = assertSignedImpactScore2(args.weight, "Evidence creation");
12276
+ const evidenceRelation = normalizeEvidenceRelation(
12277
+ args.evidenceRelation,
12278
+ weight,
12279
+ "Evidence creation"
12280
+ );
12281
+ const linkedBeliefNode = await ctx.db.get(args.linkedBeliefNodeId);
12282
+ if (!linkedBeliefNode || linkedBeliefNode.nodeType !== "belief") {
12283
+ throw new Error("Evidence creation requires a linked belief node");
12284
+ }
11756
12285
  const additionalMetadata = args.metadata && typeof args.metadata === "object" ? args.metadata : {};
11757
12286
  const nodeId = await ctx.db.insert("epistemicNodes", {
11758
12287
  globalId,
@@ -11780,8 +12309,10 @@ var create6 = mutation({
11780
12309
  sourceQuestionId: args.sourceQuestionId,
11781
12310
  rationale: args.rationale,
11782
12311
  linkedBeliefNodeId: args.linkedBeliefNodeId,
11783
- evidenceRelation: args.evidenceRelation,
11784
- confidence: args.confidence,
12312
+ evidenceRelation,
12313
+ confidence: Math.abs(weight),
12314
+ weight,
12315
+ impactScore: weight,
11785
12316
  methodology: args.methodology,
11786
12317
  informationAsymmetry: args.informationAsymmetry,
11787
12318
  sourceDescription: args.sourceDescription,
@@ -11801,29 +12332,18 @@ var create6 = mutation({
11801
12332
  nodeType: "evidence",
11802
12333
  text: args.text
11803
12334
  });
11804
- if (args.linkedBeliefNodeId && args.evidenceRelation) {
11805
- const beliefNode = await ctx.db.get(args.linkedBeliefNodeId);
11806
- if (beliefNode) {
11807
- const weight = args.evidenceRelation === "supports" ? 1 : -1;
11808
- await ctx.scheduler.runAfter(5e3, internal.neo4jEdgeAPI.createEdge, {
11809
- globalId: crypto.randomUUID(),
11810
- fromGlobalId: globalId,
11811
- toGlobalId: beliefNode.globalId,
11812
- edgeType: "informs",
11813
- weight: weight * (args.confidence || 0.7),
11814
- createdBy: args.userId,
11815
- topicId: scope.projectId ? String(scope.projectId) : void 0,
11816
- fromNodeType: "evidence",
11817
- toNodeType: "belief",
11818
- fromLayer: "L2",
11819
- toLayer: "L3",
11820
- metadata: {
11821
- relation: args.evidenceRelation,
11822
- confidence: args.confidence
11823
- }
11824
- });
11825
- }
11826
- }
12335
+ await createEvidenceBeliefEdge(ctx, {
12336
+ evidenceNodeId: nodeId,
12337
+ evidenceGlobalId: globalId,
12338
+ beliefNodeId: args.linkedBeliefNodeId,
12339
+ beliefGlobalId: linkedBeliefNode.globalId,
12340
+ relation: evidenceRelation,
12341
+ weight,
12342
+ userId: args.userId,
12343
+ topicId: scope.topicId ? String(scope.topicId) : void 0,
12344
+ projectId: scope.projectId ? String(scope.projectId) : void 0,
12345
+ rationale: args.rationale
12346
+ });
11827
12347
  await ctx.db.insert("epistemicAudit", {
11828
12348
  entityType: "evidence",
11829
12349
  entityId: nodeId,
@@ -11838,7 +12358,8 @@ var create6 = mutation({
11838
12358
  kind,
11839
12359
  sourceType,
11840
12360
  linkedBeliefNodeId: args.linkedBeliefNodeId,
11841
- evidenceRelation: args.evidenceRelation
12361
+ evidenceRelation,
12362
+ weight
11842
12363
  }
11843
12364
  });
11844
12365
  if (scope.projectId || scope.topicId) {
@@ -11881,6 +12402,7 @@ var createAndLink = mutation({
11881
12402
  userId: v.string(),
11882
12403
  beliefNodeId: v.id("epistemicNodes"),
11883
12404
  relation: v.union(v.literal("supports"), v.literal("contradicts")),
12405
+ weight: v.number(),
11884
12406
  confidence: v.optional(v.number())
11885
12407
  },
11886
12408
  returns: permissiveReturn,
@@ -11895,7 +12417,17 @@ var createAndLink = mutation({
11895
12417
  const contentHash = generateContentHash5(args.text);
11896
12418
  const kind = normalizeKind(args.kind);
11897
12419
  const sourceType = normalizeSourceType(args.sourceType);
11898
- const confidence = args.confidence ?? 0.7;
12420
+ const weight = assertSignedImpactScore2(args.weight, "Evidence createAndLink");
12421
+ const relation = normalizeEvidenceRelation(
12422
+ args.relation,
12423
+ weight,
12424
+ "Evidence createAndLink"
12425
+ );
12426
+ const confidence = Math.abs(weight);
12427
+ const beliefNode = await ctx.db.get(args.beliefNodeId);
12428
+ if (!beliefNode || beliefNode.nodeType !== "belief") {
12429
+ throw new Error("Belief node not found for edge creation");
12430
+ }
11899
12431
  const nodeId = await ctx.db.insert("epistemicNodes", {
11900
12432
  globalId,
11901
12433
  topicId: scope.topicId,
@@ -11915,36 +12447,26 @@ var createAndLink = mutation({
11915
12447
  kind,
11916
12448
  tags: args.tags || [],
11917
12449
  linkedBeliefNodeId: args.beliefNodeId,
11918
- evidenceRelation: args.relation,
11919
- confidence
12450
+ evidenceRelation: relation,
12451
+ confidence,
12452
+ weight,
12453
+ impactScore: weight
11920
12454
  }
11921
12455
  });
11922
12456
  await ctx.scheduler.runAfter(0, internal.neo4jSync.syncNodeToNeo4j, {
11923
12457
  nodeId,
11924
12458
  operation: "upsert"
11925
12459
  });
11926
- const beliefNode = await ctx.db.get(args.beliefNodeId);
11927
- if (!beliefNode) {
11928
- throw new Error("Belief node not found for edge creation");
11929
- }
11930
- const weight = args.relation === "supports" ? confidence : -confidence;
11931
- const edgeGlobalId = crypto.randomUUID();
11932
- await ctx.scheduler.runAfter(5e3, internal.neo4jEdgeAPI.createEdge, {
11933
- globalId: edgeGlobalId,
11934
- fromGlobalId: globalId,
11935
- toGlobalId: beliefNode.globalId,
11936
- edgeType: "informs",
12460
+ const edgeGlobalId = await createEvidenceBeliefEdge(ctx, {
12461
+ evidenceNodeId: nodeId,
12462
+ evidenceGlobalId: globalId,
12463
+ beliefNodeId: args.beliefNodeId,
12464
+ beliefGlobalId: beliefNode.globalId,
12465
+ relation,
11937
12466
  weight,
11938
- createdBy: args.userId,
11939
- topicId: scope.projectId,
11940
- fromNodeType: "evidence",
11941
- toNodeType: "belief",
11942
- fromLayer: "L2",
11943
- toLayer: "L3",
11944
- metadata: {
11945
- relation: args.relation,
11946
- confidence
11947
- }
12467
+ userId: args.userId,
12468
+ topicId: scope.topicId ? String(scope.topicId) : void 0,
12469
+ projectId: scope.projectId ? String(scope.projectId) : void 0
11948
12470
  });
11949
12471
  await markProjectGraphDirty2(ctx, scope.projectId, String(scope.topicId));
11950
12472
  return { nodeId, edgeGlobalId };
@@ -12005,8 +12527,9 @@ var internalCreate2 = internalMutation({
12005
12527
  sourceQuestionId: v.optional(v.string()),
12006
12528
  userId: v.string(),
12007
12529
  rationale: v.string(),
12008
- linkedBeliefNodeId: v.optional(v.id("epistemicNodes")),
12530
+ linkedBeliefNodeId: v.id("epistemicNodes"),
12009
12531
  evidenceRelation: v.optional(v.string()),
12532
+ weight: v.number(),
12010
12533
  confidence: v.optional(v.number()),
12011
12534
  metadata: v.optional(v.any()),
12012
12535
  runtimeToolName: v.optional(v.string()),
@@ -12041,6 +12564,16 @@ var internalCreate2 = internalMutation({
12041
12564
  const contentHash = generateContentHash5(args.text);
12042
12565
  const kind = normalizeKind(args.kind);
12043
12566
  const sourceType = normalizeSourceType(args.sourceType);
12567
+ const weight = assertSignedImpactScore2(args.weight, "Internal evidence creation");
12568
+ const evidenceRelation = normalizeEvidenceRelation(
12569
+ args.evidenceRelation,
12570
+ weight,
12571
+ "Internal evidence creation"
12572
+ );
12573
+ const linkedBeliefNode = await ctx.db.get(args.linkedBeliefNodeId);
12574
+ if (!linkedBeliefNode || linkedBeliefNode.nodeType !== "belief") {
12575
+ throw new Error("Internal evidence creation requires a linked belief node");
12576
+ }
12044
12577
  const additionalMetadata = args.metadata && typeof args.metadata === "object" ? args.metadata : {};
12045
12578
  const nodeId = await ctx.db.insert("epistemicNodes", {
12046
12579
  globalId,
@@ -12068,8 +12601,10 @@ var internalCreate2 = internalMutation({
12068
12601
  sourceQuestionId: args.sourceQuestionId,
12069
12602
  rationale: args.rationale,
12070
12603
  linkedBeliefNodeId: args.linkedBeliefNodeId,
12071
- evidenceRelation: args.evidenceRelation,
12072
- confidence: args.confidence,
12604
+ evidenceRelation,
12605
+ confidence: Math.abs(weight),
12606
+ weight,
12607
+ impactScore: weight,
12073
12608
  ...additionalMetadata
12074
12609
  }
12075
12610
  });
@@ -12089,8 +12624,9 @@ var internalCreate2 = internalMutation({
12089
12624
  externalSourceType: args.externalSourceType,
12090
12625
  sourceUrl: args.sourceUrl,
12091
12626
  linkedBeliefNodeId: args.linkedBeliefNodeId,
12092
- evidenceRelation: args.evidenceRelation,
12093
- confidence: args.confidence
12627
+ evidenceRelation,
12628
+ confidence: Math.abs(weight),
12629
+ weight
12094
12630
  },
12095
12631
  triggeringAction: "epistemicEvidence.internalCreate"
12096
12632
  });
@@ -12098,30 +12634,18 @@ var internalCreate2 = internalMutation({
12098
12634
  nodeId,
12099
12635
  operation: "upsert"
12100
12636
  });
12101
- if (args.linkedBeliefNodeId && args.evidenceRelation) {
12102
- const beliefNode = await ctx.db.get(args.linkedBeliefNodeId);
12103
- if (beliefNode) {
12104
- const confidence = args.confidence ?? 0.7;
12105
- const weight = args.evidenceRelation === "supports" ? confidence : -confidence;
12106
- await ctx.scheduler.runAfter(5e3, internal.neo4jEdgeAPI.createEdge, {
12107
- globalId: crypto.randomUUID(),
12108
- fromGlobalId: globalId,
12109
- toGlobalId: beliefNode.globalId,
12110
- edgeType: "informs",
12111
- weight,
12112
- createdBy: args.userId,
12113
- topicId: scope.projectId ? String(scope.projectId) : void 0,
12114
- fromNodeType: "evidence",
12115
- toNodeType: "belief",
12116
- fromLayer: "L2",
12117
- toLayer: "L3",
12118
- metadata: {
12119
- relation: args.evidenceRelation,
12120
- confidence
12121
- }
12122
- });
12123
- }
12124
- }
12637
+ await createEvidenceBeliefEdge(ctx, {
12638
+ evidenceNodeId: nodeId,
12639
+ evidenceGlobalId: globalId,
12640
+ beliefNodeId: args.linkedBeliefNodeId,
12641
+ beliefGlobalId: linkedBeliefNode.globalId,
12642
+ relation: evidenceRelation,
12643
+ weight,
12644
+ userId: args.userId,
12645
+ topicId: scope.topicId ? String(scope.topicId) : void 0,
12646
+ projectId: scope.projectId ? String(scope.projectId) : void 0,
12647
+ rationale: args.rationale
12648
+ });
12125
12649
  if (scope.projectId || scope.topicId) {
12126
12650
  await ctx.scheduler.runAfter(
12127
12651
  0,
@@ -13412,7 +13936,7 @@ function assertBeliefNodeGenericUpdateAllowed(args) {
13412
13936
  throwInvariantError({
13413
13937
  message: "Belief confidence is append-only. Generic node updates cannot set confidence directly.",
13414
13938
  invariantCode: "belief.confidence_append_only",
13415
- suggestion: "Use epistemicBeliefs.modulateConfidence() so the beliefConfidence ledger and audit trail are updated together.",
13939
+ suggestion: "Use epistemicBeliefs.appendSlScoring() so the beliefConfidence ledger and audit trail are updated together.",
13416
13940
  details: { mutationName: args.mutationName, nodeId: args.node._id }
13417
13941
  });
13418
13942
  }
@@ -13464,7 +13988,7 @@ function assertBeliefNodeVerifyAllowed(args) {
13464
13988
  throwInvariantError({
13465
13989
  message: "Belief verification cannot set confidence directly. Confidence changes must stay append-only.",
13466
13990
  invariantCode: "belief.confidence_append_only",
13467
- suggestion: "Call epistemicBeliefs.modulateConfidence() after verification so the confidence history is preserved.",
13991
+ suggestion: "Call epistemicBeliefs.appendSlScoring() after verification so the confidence history is preserved.",
13468
13992
  details: { mutationName: args.mutationName, nodeId: args.node._id }
13469
13993
  });
13470
13994
  }
@@ -18904,7 +19428,7 @@ var epistemicBeliefs = {
18904
19428
  internalGetByTopic,
18905
19429
  linkBeliefs,
18906
19430
  linkEvidence,
18907
- modulateConfidence,
19431
+ appendSlScoring,
18908
19432
  propagateConfidenceChange,
18909
19433
  reassignBeliefsTopic,
18910
19434
  refineBelief,