@lucern/graph-primitives 1.0.15 → 1.0.17

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 (65) 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/epistemicBeliefs.admin.js.map +1 -1
  11. package/dist/epistemicBeliefs.backfills.d.ts +1 -1
  12. package/dist/epistemicBeliefs.backfills.js +63 -35
  13. package/dist/epistemicBeliefs.backfills.js.map +1 -1
  14. package/dist/epistemicBeliefs.confidence.d.ts +1 -1
  15. package/dist/epistemicBeliefs.confidence.js +70 -41
  16. package/dist/epistemicBeliefs.confidence.js.map +1 -1
  17. package/dist/epistemicBeliefs.core.js +957 -566
  18. package/dist/epistemicBeliefs.core.js.map +1 -1
  19. package/dist/epistemicBeliefs.d.ts +2 -2
  20. package/dist/epistemicBeliefs.forkEvidence.d.ts +18 -0
  21. package/dist/epistemicBeliefs.forkEvidence.js +121 -0
  22. package/dist/epistemicBeliefs.forkEvidence.js.map +1 -0
  23. package/dist/epistemicBeliefs.helpers.d.ts +2 -2
  24. package/dist/epistemicBeliefs.helpers.js +60 -32
  25. package/dist/epistemicBeliefs.helpers.js.map +1 -1
  26. package/dist/epistemicBeliefs.internal.js +174 -39
  27. package/dist/epistemicBeliefs.internal.js.map +1 -1
  28. package/dist/epistemicBeliefs.js +436 -72
  29. package/dist/epistemicBeliefs.js.map +1 -1
  30. package/dist/epistemicBeliefs.lifecycle.d.ts +2 -2
  31. package/dist/epistemicBeliefs.lifecycle.js +75 -47
  32. package/dist/epistemicBeliefs.lifecycle.js.map +1 -1
  33. package/dist/epistemicBeliefs.links.js +46 -1
  34. package/dist/epistemicBeliefs.links.js.map +1 -1
  35. package/dist/epistemicBeliefs.topicAnchor.d.ts +29 -0
  36. package/dist/epistemicBeliefs.topicAnchor.js +105 -0
  37. package/dist/epistemicBeliefs.topicAnchor.js.map +1 -0
  38. package/dist/epistemicContracts.evaluators.js +71 -42
  39. package/dist/epistemicContracts.evaluators.js.map +1 -1
  40. package/dist/epistemicContracts.handlers.js +71 -42
  41. package/dist/epistemicContracts.handlers.js.map +1 -1
  42. package/dist/epistemicContracts.js +71 -42
  43. package/dist/epistemicContracts.js.map +1 -1
  44. package/dist/epistemicContracts.metrics.js +1 -1
  45. package/dist/epistemicContracts.metrics.js.map +1 -1
  46. package/dist/epistemicContracts.types.d.ts +1 -1
  47. package/dist/epistemicEdges.helpers.d.ts +1 -1
  48. package/dist/epistemicEvidence.js +172 -81
  49. package/dist/epistemicEvidence.js.map +1 -1
  50. package/dist/epistemicEvidenceMutations.js +172 -81
  51. package/dist/epistemicEvidenceMutations.js.map +1 -1
  52. package/dist/epistemicNodes.internal.js.map +1 -1
  53. package/dist/epistemicNodes.js +2 -2
  54. package/dist/epistemicNodes.js.map +1 -1
  55. package/dist/epistemicNodes.mutations.js +2 -2
  56. package/dist/epistemicNodes.mutations.js.map +1 -1
  57. package/dist/evaluators/index.js +1 -1
  58. package/dist/evaluators/index.js.map +1 -1
  59. package/dist/index.d.ts +2 -2
  60. package/dist/index.js +767 -236
  61. package/dist/index.js.map +1 -1
  62. package/dist/invariantEnforcement.js +2 -2
  63. package/dist/invariantEnforcement.js.map +1 -1
  64. package/dist/proof-attestation.json +3 -3
  65. package/package.json +4 -4
package/dist/index.js CHANGED
@@ -689,6 +689,7 @@ __export(beliefLifecycle_exports, {
689
689
  isPreValidationBeliefStatus: () => isPreValidationBeliefStatus,
690
690
  isPropagationEligibleBeliefStatus: () => isPropagationEligibleBeliefStatus,
691
691
  isResolvedByConfidence: () => isResolvedByConfidence,
692
+ promoteBeliefStatusAfterEvidence: () => promoteBeliefStatusAfterEvidence,
692
693
  promoteBeliefStatusAfterScoring: () => promoteBeliefStatusAfterScoring,
693
694
  resolveBeliefLifecycleStatus: () => resolveBeliefLifecycleStatus,
694
695
  shouldTreatBeliefAsFact: () => shouldTreatBeliefAsFact
@@ -696,8 +697,10 @@ __export(beliefLifecycle_exports, {
696
697
  var BELIEF_STATUS_VALUES = [
697
698
  "assumption",
698
699
  "hypothesis",
699
- "belief",
700
- "fact"
700
+ "active",
701
+ "superseded",
702
+ "resolved_true",
703
+ "resolved_false"
701
704
  ];
702
705
  var RESOLVED_PREDICTION_OUTCOMES = [
703
706
  "confirmed",
@@ -708,6 +711,24 @@ var RESOLVED_PREDICTION_OUTCOMES = [
708
711
  function isBeliefLifecycleStatus(value) {
709
712
  return typeof value === "string" && BELIEF_STATUS_VALUES.includes(value);
710
713
  }
714
+ function normalizeLegacyBeliefStatus(value) {
715
+ if (isBeliefLifecycleStatus(value)) {
716
+ return value;
717
+ }
718
+ if (value === "belief" || value === "established" || value === "emerging") {
719
+ return "active";
720
+ }
721
+ if (value === "fact" || value === "confirmed") {
722
+ return "resolved_true";
723
+ }
724
+ if (value === "disconfirmed" || value === "expired") {
725
+ return "resolved_false";
726
+ }
727
+ if (value === "deprecated") {
728
+ return "superseded";
729
+ }
730
+ return null;
731
+ }
711
732
  function normalizeBeliefConfidence(confidence) {
712
733
  if (typeof confidence !== "number" || !Number.isFinite(confidence)) {
713
734
  return null;
@@ -749,25 +770,58 @@ function shouldTreatBeliefAsFact(opts) {
749
770
  }
750
771
  return false;
751
772
  }
773
+ function resolvedPredictionStatus(predictionMeta) {
774
+ if (!predictionMeta || typeof predictionMeta !== "object") {
775
+ return null;
776
+ }
777
+ const outcome = predictionMeta.outcome;
778
+ if (outcome === "confirmed") {
779
+ return "resolved_true";
780
+ }
781
+ if (outcome === "disconfirmed" || outcome === "expired") {
782
+ return "resolved_false";
783
+ }
784
+ return null;
785
+ }
786
+ function shouldTreatBeliefAsResolved(opts) {
787
+ if (isResolvedByConfidence(opts.confidence)) {
788
+ const normalized = normalizeBeliefConfidence(opts.confidence);
789
+ return normalized === 0 ? "resolved_false" : "resolved_true";
790
+ }
791
+ const directPredictionStatus = resolvedPredictionStatus(opts.predictionMeta);
792
+ if (directPredictionStatus) {
793
+ return directPredictionStatus;
794
+ }
795
+ const metadataPredictionStatus = resolvedPredictionStatus(
796
+ getPredictionMetaFromMetadata(opts.metadata)
797
+ );
798
+ if (metadataPredictionStatus) {
799
+ return metadataPredictionStatus;
800
+ }
801
+ return null;
802
+ }
752
803
  function resolveBeliefLifecycleStatus(opts) {
753
- if (shouldTreatBeliefAsFact(opts)) {
754
- return "fact";
804
+ const resolvedStatus = shouldTreatBeliefAsResolved(opts);
805
+ if (resolvedStatus) {
806
+ return resolvedStatus;
755
807
  }
756
808
  const direct = opts.beliefStatus;
757
- if (isBeliefLifecycleStatus(direct)) {
809
+ const normalizedDirect = normalizeLegacyBeliefStatus(direct);
810
+ if (normalizedDirect) {
758
811
  const normalized = normalizeBeliefConfidence(opts.confidence);
759
- if (normalized !== null && isPreValidationBeliefStatus(direct)) {
760
- return "belief";
812
+ if (normalized !== null && isPreValidationBeliefStatus(normalizedDirect)) {
813
+ return "active";
761
814
  }
762
- return direct;
815
+ return normalizedDirect;
763
816
  }
764
817
  const metaStatus = opts.metadata?.beliefStatus;
765
- if (isBeliefLifecycleStatus(metaStatus)) {
818
+ const normalizedMetaStatus = normalizeLegacyBeliefStatus(metaStatus);
819
+ if (normalizedMetaStatus) {
766
820
  const normalized = normalizeBeliefConfidence(opts.confidence);
767
- if (normalized !== null && isPreValidationBeliefStatus(metaStatus)) {
768
- return "belief";
821
+ if (normalized !== null && isPreValidationBeliefStatus(normalizedMetaStatus)) {
822
+ return "active";
769
823
  }
770
- return metaStatus;
824
+ return normalizedMetaStatus;
771
825
  }
772
826
  return "assumption";
773
827
  }
@@ -775,16 +829,20 @@ function isPreValidationBeliefStatus(status) {
775
829
  return status === "assumption" || status === "hypothesis";
776
830
  }
777
831
  function isPropagationEligibleBeliefStatus(status) {
778
- return status === "belief" || status === "fact";
832
+ return status === "active" || status === "resolved_true";
779
833
  }
780
834
  function promoteBeliefStatusAfterScoring(status, opts) {
781
- if (shouldTreatBeliefAsFact({ ...opts })) {
782
- return "fact";
835
+ const resolvedStatus = shouldTreatBeliefAsResolved({ ...opts });
836
+ if (resolvedStatus) {
837
+ return resolvedStatus;
783
838
  }
784
839
  if (isPreValidationBeliefStatus(status)) {
785
- return "belief";
840
+ return "active";
786
841
  }
787
- return status === "fact" ? "fact" : "belief";
842
+ return status;
843
+ }
844
+ function promoteBeliefStatusAfterEvidence(status) {
845
+ return isPreValidationBeliefStatus(status) ? "active" : status;
788
846
  }
789
847
  var api = anyApi;
790
848
  componentsGeneric();
@@ -1607,7 +1665,7 @@ function buildBeliefConfidenceRow(args) {
1607
1665
  disbelief: args.disbelief,
1608
1666
  uncertainty: args.uncertainty,
1609
1667
  baseRate: args.baseRate,
1610
- slOperator: args.slOperator ?? "manual_assessment",
1668
+ slOperator: args.slOperator ?? "prior_seed",
1611
1669
  trigger: args.trigger,
1612
1670
  ...args.rationale ? { rationale: args.rationale } : {},
1613
1671
  assessedBy: args.assessedBy,
@@ -2061,17 +2119,17 @@ async function applyBeliefConfidenceChange(ctx, args) {
2061
2119
  status: 404,
2062
2120
  code: "NOT_FOUND",
2063
2121
  invariantCode: "belief.exists",
2064
- suggestion: "Verify nodeId points to an existing node before modulating confidence.",
2122
+ suggestion: "Verify nodeId points to an existing node before appending SL scoring.",
2065
2123
  details: { nodeId: args.nodeId }
2066
2124
  });
2067
2125
  }
2068
2126
  if (node.nodeType !== "belief") {
2069
2127
  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.`,
2128
+ 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
2129
  status: 400,
2072
2130
  code: "INVALID_ARGUMENT",
2073
2131
  invariantCode: "entity.no_confidence",
2074
- suggestion: "Use entityLifecycle.updateEntityAttributes for entity mutations. modulateConfidence is for belief nodes only.",
2132
+ suggestion: "Use entityLifecycle.updateEntityAttributes for entity mutations. appendSlScoring is for belief nodes only.",
2075
2133
  details: { nodeId: args.nodeId, nodeType: node.nodeType }
2076
2134
  });
2077
2135
  }
@@ -2081,7 +2139,7 @@ async function applyBeliefConfidenceChange(ctx, args) {
2081
2139
  status: 400,
2082
2140
  code: "MISSING_SCOPE",
2083
2141
  invariantCode: "belief.project_required",
2084
- suggestion: "Belief must have a projectId to modulate confidence.",
2142
+ suggestion: "Belief must have a projectId before SL scoring can be appended.",
2085
2143
  details: { nodeId: args.nodeId }
2086
2144
  });
2087
2145
  }
@@ -2104,7 +2162,7 @@ async function applyBeliefConfidenceChange(ctx, args) {
2104
2162
  status: 409,
2105
2163
  code: "CONFLICT",
2106
2164
  invariantCode: "belief.confidence_append_only",
2107
- suggestion: "Complete a worktree linked to this belief before recording confidence modulation.",
2165
+ suggestion: "Complete a worktree linked to this belief before recording SL scoring.",
2108
2166
  details: { nodeId: args.nodeId }
2109
2167
  });
2110
2168
  }
@@ -2359,6 +2417,199 @@ var propagateConfidenceChange = internalMutation({
2359
2417
  }
2360
2418
  });
2361
2419
 
2420
+ // src/epistemicBeliefs.forkEvidence.ts
2421
+ function normalizeForkTriggerRelation(value) {
2422
+ if (value === "supports" || value === "supporting") {
2423
+ return "supports";
2424
+ }
2425
+ if (value === "contradicts" || value === "contradicting") {
2426
+ return "contradicts";
2427
+ }
2428
+ return null;
2429
+ }
2430
+ async function resolveForkTriggerEvidence(ctx, args) {
2431
+ const evidence = await ctx.db.get(args.triggeringEvidenceId);
2432
+ if (!evidence || evidence.nodeType !== "evidence") {
2433
+ throwStructuredMutationError({
2434
+ message: "Fork requires an existing evidence node.",
2435
+ status: 400,
2436
+ code: "INVALID_ARGUMENT",
2437
+ invariantCode: "belief.fork_requires_evidence",
2438
+ suggestion: "Create or link evidence first, then fork with triggeringEvidenceId.",
2439
+ details: { triggeringEvidenceId: args.triggeringEvidenceId }
2440
+ });
2441
+ }
2442
+ if (evidence.topicId && evidence.topicId !== args.parent.topicId) {
2443
+ throwStructuredMutationError({
2444
+ message: "Fork evidence belongs to a different topic scope.",
2445
+ status: 400,
2446
+ code: "INVALID_ARGUMENT",
2447
+ invariantCode: "belief.fork_evidence_scope",
2448
+ suggestion: "Use evidence from the same topic/workspace scope as the parent belief.",
2449
+ details: {
2450
+ parentNodeId: args.parentNodeId,
2451
+ triggeringEvidenceId: args.triggeringEvidenceId
2452
+ }
2453
+ });
2454
+ }
2455
+ const evidenceMetadata = evidence.metadata && typeof evidence.metadata === "object" ? evidence.metadata : {};
2456
+ const parentRefs = new Set(
2457
+ [
2458
+ String(args.parentNodeId),
2459
+ String(args.parent.globalId ?? ""),
2460
+ String(args.parent._id)
2461
+ ].filter(Boolean)
2462
+ );
2463
+ const evidenceRefs = new Set(
2464
+ [
2465
+ String(args.triggeringEvidenceId),
2466
+ String(evidence.globalId ?? ""),
2467
+ String(evidence._id)
2468
+ ].filter(Boolean)
2469
+ );
2470
+ let relation = null;
2471
+ const linkedBeliefNodeId = String(
2472
+ evidenceMetadata.linkedBeliefNodeId ?? ""
2473
+ );
2474
+ if (linkedBeliefNodeId && parentRefs.has(linkedBeliefNodeId)) {
2475
+ relation = normalizeForkTriggerRelation(evidenceMetadata.evidenceRelation);
2476
+ }
2477
+ if (!relation) {
2478
+ for (const parentRef of parentRefs) {
2479
+ const links = await ctx.db.query("beliefEvidenceLinks").withIndex("by_beliefId", (q) => q.eq("beliefId", parentRef)).collect();
2480
+ const matched = links.find((link) => evidenceRefs.has(String(link.insightId)));
2481
+ if (matched) {
2482
+ relation = normalizeForkTriggerRelation(matched.relation);
2483
+ break;
2484
+ }
2485
+ }
2486
+ }
2487
+ if (!relation) {
2488
+ throwStructuredMutationError({
2489
+ message: "Fork evidence must already be attached to the parent belief through an SL evidence relation.",
2490
+ status: 409,
2491
+ code: "CONFLICT",
2492
+ invariantCode: "belief.fork_requires_attached_evidence",
2493
+ suggestion: "Attach the evidence to the parent belief as supports or contradicts before forking.",
2494
+ details: {
2495
+ parentNodeId: args.parentNodeId,
2496
+ triggeringEvidenceId: args.triggeringEvidenceId
2497
+ }
2498
+ });
2499
+ }
2500
+ if (args.forkMode === "supersede" && relation !== "contradicts") {
2501
+ throwStructuredMutationError({
2502
+ message: "Superseding fork requires contradicting evidence against the parent belief.",
2503
+ status: 409,
2504
+ code: "CONFLICT",
2505
+ invariantCode: "belief.supersede_requires_contradiction",
2506
+ suggestion: "Use forkMode='branch' for a non-replacing fork, or attach contradicting evidence before superseding.",
2507
+ details: {
2508
+ parentNodeId: args.parentNodeId,
2509
+ triggeringEvidenceId: args.triggeringEvidenceId,
2510
+ relation
2511
+ }
2512
+ });
2513
+ }
2514
+ return { evidenceNodeId: args.triggeringEvidenceId, relation };
2515
+ }
2516
+
2517
+ // src/epistemicBeliefs.topicAnchor.ts
2518
+ function cleanString(value) {
2519
+ return typeof value === "string" && value.trim().length > 0 ? value.trim() : void 0;
2520
+ }
2521
+ function topicNodeCandidates(topicRef) {
2522
+ const normalized = topicRef.trim();
2523
+ if (!normalized) {
2524
+ return [];
2525
+ }
2526
+ const candidates = [normalized];
2527
+ if (normalized.startsWith("top_")) {
2528
+ candidates.push(normalized.slice(4));
2529
+ }
2530
+ return [...new Set(candidates)];
2531
+ }
2532
+ function readTopicNodeRef(args) {
2533
+ return cleanString(args.topicGlobalId) ?? cleanString(args.topicNodeId) ?? cleanString(args.topicId);
2534
+ }
2535
+ async function resolveRequiredTopicAnchor(ctx, topicRef) {
2536
+ for (const candidate of topicNodeCandidates(topicRef)) {
2537
+ try {
2538
+ const direct = await ctx.db.get(candidate);
2539
+ if (direct?.nodeType === "topic" && cleanString(direct.globalId)) {
2540
+ return direct;
2541
+ }
2542
+ } catch (_) {
2543
+ }
2544
+ const byGlobalId = await ctx.db.query("epistemicNodes").withIndex("by_globalId", (q) => q.eq("globalId", candidate)).first();
2545
+ if (byGlobalId?.nodeType === "topic" && cleanString(byGlobalId.globalId)) {
2546
+ return byGlobalId;
2547
+ }
2548
+ }
2549
+ throw new Error(
2550
+ "Belief creation requires topicGlobalId or topicNodeId for a topic node in epistemicNodes. Legacy topics-table IDs are not valid belief anchors."
2551
+ );
2552
+ }
2553
+ function scopeFromTopicAnchor(topicNode) {
2554
+ return {
2555
+ topicId: topicNode.globalId,
2556
+ projectId: cleanString(topicNode.projectId),
2557
+ tenantId: cleanString(topicNode.tenantId),
2558
+ workspaceId: cleanString(topicNode.workspaceId),
2559
+ source: "topic"
2560
+ };
2561
+ }
2562
+ async function createRequiredBeliefTopicEdge(ctx, args) {
2563
+ const topicGlobalId = args.topicNode.globalId;
2564
+ const edgeGlobalId = `edge:${args.beliefGlobalId}:${topicGlobalId}:scoped_by`;
2565
+ const now = Date.now();
2566
+ const existing = await ctx.db.query("epistemicEdges").withIndex("by_globalId", (q) => q.eq("globalId", edgeGlobalId)).first();
2567
+ if (!existing) {
2568
+ await ctx.db.insert("epistemicEdges", {
2569
+ globalId: edgeGlobalId,
2570
+ fromNodeId: String(args.beliefNodeId),
2571
+ toNodeId: String(args.topicNode._id),
2572
+ sourceGlobalId: args.beliefGlobalId,
2573
+ targetGlobalId: topicGlobalId,
2574
+ edgeType: "scoped_by",
2575
+ weight: 1,
2576
+ confidence: 1,
2577
+ context: "Belief creation topic anchor invariant.",
2578
+ reasoningMethod: "implicit",
2579
+ derivationType: "topic_scope_invariant",
2580
+ metadata: { invariant: "belief.topic_edge_required" },
2581
+ createdBy: args.createdBy,
2582
+ createdAt: now,
2583
+ updatedAt: now,
2584
+ projectId: cleanString(args.topicNode.projectId),
2585
+ topicId: topicGlobalId,
2586
+ tenantId: cleanString(args.topicNode.tenantId),
2587
+ workspaceId: cleanString(args.topicNode.workspaceId),
2588
+ fromNodeType: "belief",
2589
+ toNodeType: "topic",
2590
+ fromLayer: "L3",
2591
+ toLayer: args.topicNode.epistemicLayer ?? "ontological"
2592
+ });
2593
+ }
2594
+ await ctx.scheduler.runAfter(0, internal.neo4jEdgeAPI.createEdge, {
2595
+ globalId: edgeGlobalId,
2596
+ fromGlobalId: args.beliefGlobalId,
2597
+ toGlobalId: topicGlobalId,
2598
+ edgeType: "scoped_by",
2599
+ weight: 1,
2600
+ confidence: 1,
2601
+ context: "Belief creation topic anchor invariant.",
2602
+ projectId: cleanString(args.topicNode.projectId),
2603
+ topicId: topicGlobalId,
2604
+ createdBy: args.createdBy,
2605
+ fromNodeType: "belief",
2606
+ toNodeType: "topic",
2607
+ fromLayer: "L3",
2608
+ toLayer: args.topicNode.epistemicLayer ?? "ontological",
2609
+ metadata: { invariant: "belief.topic_edge_required" }
2610
+ });
2611
+ }
2612
+
2362
2613
  // src/embeddingTrigger.ts
2363
2614
  async function scheduleEmbeddingGeneration(args) {
2364
2615
  try {
@@ -2408,6 +2659,8 @@ function generateGlobalId() {
2408
2659
  var create = mutation({
2409
2660
  args: {
2410
2661
  ...optionalBeliefScopeArgs,
2662
+ topicNodeId: v.optional(v.string()),
2663
+ topicGlobalId: v.optional(v.string()),
2411
2664
  formulation: v.string(),
2412
2665
  beliefType: v.optional(v.string()),
2413
2666
  rationale: v.optional(v.string()),
@@ -2452,20 +2705,32 @@ var create = mutation({
2452
2705
  returns: permissiveReturn,
2453
2706
  handler: async (ctx, args) => {
2454
2707
  const authenticatedUserId = await requireAuthenticatedUserId(ctx);
2455
- const scope = await resolveTopicProjectScope(ctx, {
2456
- topicId: args.topicId,
2457
- projectId: args.projectId
2458
- });
2708
+ const topicRef = readTopicNodeRef(args);
2709
+ if (!topicRef) {
2710
+ throwStructuredMutationError({
2711
+ message: "Belief creation requires an explicit topic epistemic node.",
2712
+ status: 400,
2713
+ code: "INVALID_ARGUMENT",
2714
+ invariantCode: "belief.topic_node_required",
2715
+ suggestion: "Pass topicGlobalId or topicNodeId for a topic in epistemicNodes before creating a belief.",
2716
+ details: {
2717
+ topicId: args.topicId,
2718
+ topicNodeId: args.topicNodeId,
2719
+ topicGlobalId: args.topicGlobalId
2720
+ }
2721
+ });
2722
+ }
2723
+ const topicNode = await resolveRequiredTopicAnchor(ctx, topicRef);
2724
+ const scope = scopeFromTopicAnchor(topicNode);
2459
2725
  assertWorkspaceScopedEpistemicNodeScope({
2460
2726
  scope,
2461
2727
  nodeType: "belief",
2462
2728
  mutationName: "epistemicBeliefs.create"
2463
2729
  });
2464
- const topic = await ctx.db.get(scope.topicId);
2465
2730
  const normalizedBeliefType = await assertSchemaEnumValue(ctx, {
2466
2731
  category: "belief_type",
2467
2732
  value: args.beliefType,
2468
- tenantId: topic?.tenantId,
2733
+ tenantId: scope.tenantId,
2469
2734
  context: "epistemicBeliefs.create"
2470
2735
  });
2471
2736
  if (scope.projectId) {
@@ -2498,7 +2763,7 @@ var create = mutation({
2498
2763
  title: args.formulation.slice(0, 100) + (args.formulation.length > 100 ? "..." : ""),
2499
2764
  metadata: {
2500
2765
  pillar,
2501
- // No confidenceLevel — only set after worktree completion via modulateConfidence()
2766
+ // No confidenceLevel — only set after evidence-backed SL scoring.
2502
2767
  status: "active",
2503
2768
  worktreeId: args.worktreeId,
2504
2769
  beliefStatus: initialBeliefStatus,
@@ -2542,9 +2807,15 @@ var create = mutation({
2542
2807
  rationale: "LKC-2 mandatory prior: seeded vacuous opinion at belief creation.",
2543
2808
  assessedBy: authenticatedUserId,
2544
2809
  assessedAt: now,
2545
- slOperator: "manual_assessment"
2810
+ slOperator: "prior_seed"
2546
2811
  })
2547
2812
  );
2813
+ await createRequiredBeliefTopicEdge(ctx, {
2814
+ beliefNodeId: nodeId,
2815
+ beliefGlobalId,
2816
+ topicNode,
2817
+ createdBy: authenticatedUserId
2818
+ });
2548
2819
  await ctx.scheduler.runAfter(0, internal.neo4jSync.syncNodeToNeo4j, {
2549
2820
  nodeId,
2550
2821
  operation: "upsert"
@@ -2881,9 +3152,10 @@ var forkBelief = mutation({
2881
3152
  v.literal("refinement"),
2882
3153
  v.literal("contradiction_response"),
2883
3154
  v.literal("scope_change"),
2884
- v.literal("confidence_collapse"),
2885
- v.literal("manual")
3155
+ v.literal("confidence_collapse")
2886
3156
  ),
3157
+ forkMode: v.optional(v.union(v.literal("supersede"), v.literal("branch"))),
3158
+ triggeringEvidenceId: v.id("epistemicNodes"),
2887
3159
  rationale: v.optional(v.string()),
2888
3160
  userId: v.string()
2889
3161
  },
@@ -2926,6 +3198,33 @@ var forkBelief = mutation({
2926
3198
  await requireProjectWriteAccess(ctx, parent.projectId, authenticatedUserId);
2927
3199
  const metadata = parent.metadata;
2928
3200
  const forkBeliefStatus = "hypothesis";
3201
+ const forkMode = args.forkMode ?? "supersede";
3202
+ const triggerEvidence = await resolveForkTriggerEvidence(ctx, {
3203
+ parentNodeId: args.parentNodeId,
3204
+ parent,
3205
+ triggeringEvidenceId: args.triggeringEvidenceId,
3206
+ forkMode
3207
+ });
3208
+ const parentLifecycleStatus = resolveBeliefLifecycleStatus({
3209
+ beliefStatus: parent.beliefStatus,
3210
+ confidence: parent.confidence,
3211
+ predictionMeta: parent.predictionMeta,
3212
+ metadata
3213
+ });
3214
+ if (forkMode === "supersede" && parentLifecycleStatus !== "active") {
3215
+ throwStructuredMutationError({
3216
+ message: "Superseding fork requires an active parent belief. Attach evidence first so the lifecycle can promote deterministically.",
3217
+ status: 409,
3218
+ code: "CONFLICT",
3219
+ invariantCode: "belief.supersede_requires_active_parent",
3220
+ suggestion: "Attach the contradicting evidence to the parent belief, let the evidence path promote it to active, then supersede.",
3221
+ details: {
3222
+ parentNodeId: args.parentNodeId,
3223
+ parentLifecycleStatus,
3224
+ triggeringEvidenceId: args.triggeringEvidenceId
3225
+ }
3226
+ });
3227
+ }
2929
3228
  const newBeliefGlobalId = generateGlobalId();
2930
3229
  const newNodeId = await ctx.db.insert("epistemicNodes", {
2931
3230
  globalId: newBeliefGlobalId,
@@ -2939,6 +3238,9 @@ var forkBelief = mutation({
2939
3238
  ...metadata,
2940
3239
  forkedFrom: args.parentNodeId,
2941
3240
  forkReason: args.forkReason,
3241
+ forkMode,
3242
+ triggeringEvidenceId: args.triggeringEvidenceId,
3243
+ triggeringEvidenceRelation: triggerEvidence.relation,
2942
3244
  forkTimestamp: now,
2943
3245
  forkedBy: authenticatedUserId,
2944
3246
  status: "active",
@@ -2957,6 +3259,27 @@ var forkBelief = mutation({
2957
3259
  createdAt: now,
2958
3260
  updatedAt: now
2959
3261
  });
3262
+ if (forkMode === "supersede") {
3263
+ await ctx.db.patch(args.parentNodeId, {
3264
+ status: "superseded",
3265
+ beliefStatus: "superseded",
3266
+ epistemicStatus: "superseded",
3267
+ supersededBy: newNodeId,
3268
+ updatedAt: now,
3269
+ metadata: {
3270
+ ...metadata ?? {},
3271
+ status: "superseded",
3272
+ beliefStatus: "superseded",
3273
+ epistemicStatus: "superseded",
3274
+ supersededBy: String(newNodeId),
3275
+ supersededByEvidenceId: String(triggerEvidence.evidenceNodeId)
3276
+ }
3277
+ });
3278
+ await ctx.scheduler.runAfter(0, internal.neo4jSync.syncNodeToNeo4j, {
3279
+ nodeId: args.parentNodeId,
3280
+ operation: "upsert"
3281
+ });
3282
+ }
2960
3283
  const inheritedContracts = await ctx.db.query("epistemicContracts").withIndex(
2961
3284
  "by_belief",
2962
3285
  (q) => q.eq("beliefNodeId", args.parentNodeId)
@@ -2983,12 +3306,17 @@ var forkBelief = mutation({
2983
3306
  globalId: generateGlobalId(),
2984
3307
  fromGlobalId: newBeliefGlobalId,
2985
3308
  toGlobalId: parent.globalId,
2986
- edgeType: "supersedes",
2987
- context: `Fork reason: ${args.forkReason}`,
3309
+ edgeType: forkMode === "supersede" ? "supersedes" : "derived_from",
3310
+ context: `Fork reason: ${args.forkReason}; triggering evidence: ${triggerEvidence.evidenceNodeId}`,
2988
3311
  createdBy: authenticatedUserId,
2989
3312
  topicId: parent.projectId ? String(parent.projectId) : void 0,
2990
3313
  fromNodeType: "belief",
2991
- toNodeType: "belief"
3314
+ toNodeType: "belief",
3315
+ metadata: {
3316
+ forkMode,
3317
+ triggeringEvidenceId: String(triggerEvidence.evidenceNodeId),
3318
+ triggeringEvidenceRelation: triggerEvidence.relation
3319
+ }
2992
3320
  });
2993
3321
  await scheduleEmbeddingGeneration({
2994
3322
  ctx,
@@ -3013,6 +3341,9 @@ var forkBelief = mutation({
3013
3341
  newState: {
3014
3342
  formulation: args.newFormulation,
3015
3343
  forkReason: args.forkReason,
3344
+ forkMode,
3345
+ triggeringEvidenceId: String(triggerEvidence.evidenceNodeId),
3346
+ triggeringEvidenceRelation: triggerEvidence.relation,
3016
3347
  tupleContradicted: false
3017
3348
  },
3018
3349
  projectId: parent.projectId,
@@ -3047,10 +3378,17 @@ var forkBelief = mutation({
3047
3378
  projectId: parent.projectId,
3048
3379
  topicId: parent.topicId
3049
3380
  });
3050
- return { newNodeId, parentNodeId: args.parentNodeId };
3381
+ return {
3382
+ newNodeId,
3383
+ parentNodeId: args.parentNodeId,
3384
+ forkMode,
3385
+ forkReason: args.forkReason,
3386
+ triggeringEvidenceId: triggerEvidence.evidenceNodeId,
3387
+ triggeringEvidenceRelation: triggerEvidence.relation
3388
+ };
3051
3389
  }
3052
3390
  });
3053
- var modulateConfidence = mutation({
3391
+ var appendSlScoring = mutation({
3054
3392
  args: {
3055
3393
  nodeId: v.id("epistemicNodes"),
3056
3394
  // SL opinion — the ONLY confidence input (EK-7)
@@ -3067,8 +3405,6 @@ var modulateConfidence = mutation({
3067
3405
  v.literal("evidence_removed"),
3068
3406
  v.literal("contradiction_detected"),
3069
3407
  v.literal("contradiction_resolved"),
3070
- v.literal("manual"),
3071
- v.literal("decay"),
3072
3408
  v.literal("agent_assessment"),
3073
3409
  v.literal("worktree_outcome"),
3074
3410
  v.literal("worktree_completed"),
@@ -3080,7 +3416,7 @@ var modulateConfidence = mutation({
3080
3416
  ),
3081
3417
  rationale: v.optional(v.string()),
3082
3418
  userId: v.string(),
3083
- // SL operator provenance (optional — defaults to manual_assessment)
3419
+ // SL operator provenance (optional — defaults inside the SL scorer)
3084
3420
  slOperator: v.optional(
3085
3421
  v.union(
3086
3422
  v.literal("cumulative_fusion"),
@@ -3090,7 +3426,8 @@ var modulateConfidence = mutation({
3090
3426
  v.literal("dependency_cascade"),
3091
3427
  v.literal("negation"),
3092
3428
  v.literal("constraint_fusion"),
3093
- v.literal("manual_assessment")
3429
+ v.literal("no_op"),
3430
+ v.literal("prior_seed")
3094
3431
  )
3095
3432
  ),
3096
3433
  triggeringEvidenceId: v.optional(v.id("epistemicNodes")),
@@ -3457,6 +3794,12 @@ async function getQuestionsAnsweredByEvidence(ctx, evidenceId) {
3457
3794
  }
3458
3795
 
3459
3796
  // src/epistemicBeliefs.links.ts
3797
+ function assertSignedImpactScore(value, context) {
3798
+ if (typeof value !== "number" || !Number.isFinite(value) || value === 0 || value < -1 || value > 1) {
3799
+ throw new Error(`${context} requires explicit nonzero weight in [-1, 1]`);
3800
+ }
3801
+ return value;
3802
+ }
3460
3803
  var updatePillar = mutation({
3461
3804
  args: {
3462
3805
  nodeId: v.id("epistemicNodes"),
@@ -3636,6 +3979,7 @@ var linkEvidence = mutation({
3636
3979
  beliefNodeId: v.id("epistemicNodes"),
3637
3980
  insightId: insightIdUnion,
3638
3981
  type: v.union(v.literal("supporting"), v.literal("contradicting")),
3982
+ weight: v.number(),
3639
3983
  rationale: v.optional(v.string()),
3640
3984
  userId: v.string()
3641
3985
  },
@@ -3659,16 +4003,54 @@ var linkEvidence = mutation({
3659
4003
  }
3660
4004
  const evidenceNodeId = insight._id;
3661
4005
  const evidenceGlobalId = insight.globalId;
4006
+ const weight = assertSignedImpactScore(args.weight, "Evidence link");
4007
+ if (args.type === "supporting" && weight < 0) {
4008
+ throw new Error("Supporting evidence links require positive weight");
4009
+ }
4010
+ if (args.type === "contradicting" && weight > 0) {
4011
+ throw new Error("Contradicting evidence links require negative weight");
4012
+ }
4013
+ const confidence = Math.abs(weight);
3662
4014
  const edgeType = "informs";
3663
- const weight = args.type === "supporting" ? 1 : -1;
3664
4015
  const logicalRole = evidenceNodeId ? await computeLogicalRole(ctx, evidenceNodeId, args.beliefNodeId) : "contributory";
3665
4016
  const edgeGlobalId = generateGlobalId();
4017
+ await ctx.db.insert("epistemicEdges", {
4018
+ globalId: edgeGlobalId,
4019
+ fromNodeId: evidenceNodeId,
4020
+ toNodeId: args.beliefNodeId,
4021
+ sourceGlobalId: evidenceGlobalId,
4022
+ targetGlobalId: belief.globalId,
4023
+ edgeType,
4024
+ weight,
4025
+ confidence,
4026
+ context: args.rationale || `${args.type} evidence`,
4027
+ reasoningMethod: "testimonial",
4028
+ logicalRole,
4029
+ temporalClass: "structural",
4030
+ derivationType: "evidence_sl_scoring",
4031
+ metadata: {
4032
+ relation: args.type,
4033
+ confidence,
4034
+ impactScore: weight,
4035
+ invariant: "evidence.belief_impact_required"
4036
+ },
4037
+ createdBy: authenticatedUserId,
4038
+ createdAt: Date.now(),
4039
+ updatedAt: Date.now(),
4040
+ topicId: belief.topicId ? String(belief.topicId) : void 0,
4041
+ projectId: belief.projectId ? String(belief.projectId) : void 0,
4042
+ fromNodeType: "evidence",
4043
+ toNodeType: "belief",
4044
+ fromLayer: "L2",
4045
+ toLayer: "L3"
4046
+ });
3666
4047
  await ctx.scheduler.runAfter(0, internal.neo4jEdgeAPI.createEdge, {
3667
4048
  globalId: edgeGlobalId,
3668
4049
  fromGlobalId: evidenceGlobalId,
3669
4050
  toGlobalId: belief.globalId,
3670
4051
  edgeType,
3671
4052
  weight,
4053
+ confidence,
3672
4054
  context: args.rationale || `${args.type} evidence`,
3673
4055
  createdBy: authenticatedUserId,
3674
4056
  topicId: belief.projectId ? String(belief.projectId) : void 0,
@@ -4299,6 +4681,8 @@ var internalGetById = internalQuery({
4299
4681
  var internalCreate = internalMutation({
4300
4682
  args: {
4301
4683
  ...optionalBeliefScopeArgs,
4684
+ topicNodeId: v.optional(v.string()),
4685
+ topicGlobalId: v.optional(v.string()),
4302
4686
  formulation: v.string(),
4303
4687
  baseRate: v.optional(v.number()),
4304
4688
  confidence: v.optional(
@@ -4337,10 +4721,14 @@ var internalCreate = internalMutation({
4337
4721
  handler: async (ctx, args) => {
4338
4722
  const now = Date.now();
4339
4723
  const baseRate = assertBaseRateInRange(args.baseRate ?? 0.5);
4340
- const scope = await resolveTopicProjectScope(ctx, {
4341
- topicId: args.topicId,
4342
- projectId: args.projectId
4343
- });
4724
+ const topicRef = readTopicNodeRef(args);
4725
+ if (!topicRef) {
4726
+ throw new Error(
4727
+ "Belief creation requires topicGlobalId or topicNodeId for a topic node in epistemicNodes."
4728
+ );
4729
+ }
4730
+ const topicNode = await resolveRequiredTopicAnchor(ctx, topicRef);
4731
+ const scope = scopeFromTopicAnchor(topicNode);
4344
4732
  assertWorkspaceScopedEpistemicNodeScope({
4345
4733
  scope,
4346
4734
  nodeType: "belief",
@@ -4356,11 +4744,10 @@ var internalCreate = internalMutation({
4356
4744
  },
4357
4745
  mutationName: "epistemicBeliefs.internalCreate"
4358
4746
  });
4359
- const topic = await ctx.db.get(scope.topicId);
4360
4747
  const normalizedBeliefType = await assertSchemaEnumValue(ctx, {
4361
4748
  category: "belief_type",
4362
4749
  value: args.beliefType,
4363
- tenantId: topic?.tenantId,
4750
+ tenantId: scope.tenantId,
4364
4751
  context: "epistemicBeliefs.internalCreate"
4365
4752
  });
4366
4753
  const globalId = generateGlobalId();
@@ -4424,9 +4811,15 @@ var internalCreate = internalMutation({
4424
4811
  rationale: "LKC-2 mandatory prior: seeded vacuous opinion at belief creation.",
4425
4812
  assessedBy: args.userId,
4426
4813
  assessedAt: now,
4427
- slOperator: "manual_assessment"
4814
+ slOperator: "prior_seed"
4428
4815
  })
4429
4816
  );
4817
+ await createRequiredBeliefTopicEdge(ctx, {
4818
+ beliefNodeId: nodeId,
4819
+ beliefGlobalId: globalId,
4820
+ topicNode,
4821
+ createdBy: args.userId
4822
+ });
4430
4823
  await ctx.scheduler.runAfter(0, internal.neo4jSync.syncNodeToNeo4j, {
4431
4824
  nodeId,
4432
4825
  operation: "upsert"
@@ -4600,7 +4993,7 @@ var backfillSyntheticOpinionHistory = internalMutation({
4600
4993
  rationale: "LK-6 backfill: synthesized t0 from node-level opinion fields (no prior beliefConfidence row found).",
4601
4994
  assessedAt: readFiniteNumber(node.createdAt) ?? readFiniteNumber(node.updatedAt) ?? Date.now(),
4602
4995
  assessedBy: "system:lk-6-backfill",
4603
- slOperator: "manual_assessment"
4996
+ slOperator: "prior_seed"
4604
4997
  })
4605
4998
  );
4606
4999
  beliefsWithHistory.add(String(node._id));
@@ -4706,7 +5099,7 @@ var backfillMandatoryPriors = internalMutation({
4706
5099
  rationale: "LKC-2 backfill: inserted missing initial vacuous opinion with neutral prior.",
4707
5100
  assessedAt: readFiniteNumber(node.createdAt) ?? readFiniteNumber(node.updatedAt) ?? Date.now(),
4708
5101
  assessedBy: "system:lkc-2-prior-backfill",
4709
- slOperator: "manual_assessment"
5102
+ slOperator: "prior_seed"
4710
5103
  })
4711
5104
  );
4712
5105
  insertedInitialRows++;
@@ -4814,7 +5207,7 @@ var backfillScoredBeliefEdges = internalMutation({
4814
5207
  const scoredBeliefs = allBeliefs.filter((belief) => {
4815
5208
  const metadata = belief.metadata || {};
4816
5209
  const lifecycle = resolveBeliefStatus(belief, metadata);
4817
- return lifecycle === "belief" || lifecycle === "fact";
5210
+ return lifecycle === "active" || lifecycle === "resolved_true";
4818
5211
  });
4819
5212
  const themeNodes = await ctx.db.query("epistemicNodes").withIndex(
4820
5213
  "by_topic",
@@ -6300,85 +6693,32 @@ async function resolveScopeSoft(ctx, args) {
6300
6693
  ...projectId ? { projectId } : {}
6301
6694
  };
6302
6695
  }
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;
6696
+
6697
+ // src/beliefEvidenceLinks.operational.ts
6698
+ function assertEvidenceImpact(args) {
6699
+ if (!Number.isFinite(args.weight) || args.weight === 0 || args.weight < -1 || args.weight > 1) {
6700
+ throw new Error("Belief evidence links require explicit nonzero weight in [-1, 1]");
6331
6701
  }
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;
6702
+ if (args.relation === "supports" && args.weight < 0) {
6703
+ throw new Error("Supporting evidence links require positive weight");
6704
+ }
6705
+ if (args.relation === "contradicts" && args.weight > 0) {
6706
+ throw new Error("Contradicting evidence links require negative weight");
6346
6707
  }
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
6708
  }
6369
6709
  async function applyOperationalLinkEffects(ctx, args) {
6710
+ assertEvidenceImpact({ relation: args.relation, weight: args.weight });
6711
+ const confidence = Math.abs(args.weight);
6370
6712
  const currentSupporting = args.beliefNode.supportingInsightIds ?? [];
6371
6713
  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)) {
6714
+ if (args.relation === "supports" && !currentSupporting.includes(args.insightId)) {
6715
+ await ctx.db.patch(args.beliefId, {
6716
+ supportingInsightIds: [...currentSupporting, args.insightId],
6717
+ contradictingInsightIds: currentContradicting.filter(
6718
+ (id) => id !== args.insightId
6719
+ )
6720
+ });
6721
+ } else if (args.relation === "contradicts" && !currentContradicting.includes(args.insightId)) {
6382
6722
  await ctx.db.patch(args.beliefId, {
6383
6723
  contradictingInsightIds: [...currentContradicting, args.insightId],
6384
6724
  supportingInsightIds: currentSupporting.filter(
@@ -6389,7 +6729,7 @@ async function applyOperationalLinkEffects(ctx, args) {
6389
6729
  try {
6390
6730
  const beliefSpineNode = await ctx.db.get(args.beliefId);
6391
6731
  const evidenceSpineNode = await ctx.db.get(args.insightId);
6392
- if (beliefSpineNode && beliefSpineNode.nodeType === "belief" && evidenceSpineNode && evidenceSpineNode.nodeType === "evidence") {
6732
+ if (beliefSpineNode?.nodeType === "belief" && evidenceSpineNode?.nodeType === "evidence") {
6393
6733
  const existingEdges = await ctx.db.query("epistemicEdges").withIndex(
6394
6734
  "by_from_to",
6395
6735
  (q) => q.eq("fromNodeId", evidenceSpineNode._id).eq("toNodeId", beliefSpineNode._id)
@@ -6402,16 +6742,44 @@ async function applyOperationalLinkEffects(ctx, args) {
6402
6742
  await ctx.db.delete(edge._id);
6403
6743
  }
6404
6744
  }
6405
- const weight = args.relation === "supports" ? args.confidence ?? 0.7 : -(args.confidence ?? 0.7);
6406
6745
  const globalId = `edge-${Date.now()}-${Math.random().toString(36).slice(2, 11)}`;
6746
+ const context = args.rationale || `Linked as ${args.relation}`;
6747
+ await ctx.db.insert("epistemicEdges", {
6748
+ globalId,
6749
+ fromNodeId: evidenceSpineNode._id,
6750
+ toNodeId: beliefSpineNode._id,
6751
+ sourceGlobalId: evidenceSpineNode.globalId,
6752
+ targetGlobalId: beliefSpineNode.globalId,
6753
+ edgeType: "informs",
6754
+ weight: args.weight,
6755
+ confidence,
6756
+ context,
6757
+ reasoningMethod: "testimonial",
6758
+ derivationType: "evidence_sl_scoring",
6759
+ metadata: {
6760
+ relation: args.relation,
6761
+ confidence,
6762
+ impactScore: args.weight,
6763
+ invariant: "evidence.belief_impact_required"
6764
+ },
6765
+ createdBy: args.createdBy,
6766
+ createdAt: Date.now(),
6767
+ updatedAt: Date.now(),
6768
+ projectId: args.beliefNode.projectId,
6769
+ topicId: args.beliefNode.topicId ? String(args.beliefNode.topicId) : void 0,
6770
+ fromNodeType: "evidence",
6771
+ toNodeType: "belief",
6772
+ fromLayer: "L2",
6773
+ toLayer: "L3"
6774
+ });
6407
6775
  await ctx.scheduler.runAfter(0, internal.neo4jEdgeAPI.createEdge, {
6408
6776
  globalId,
6409
6777
  fromGlobalId: evidenceSpineNode.globalId,
6410
6778
  toGlobalId: beliefSpineNode.globalId,
6411
6779
  edgeType: "informs",
6412
- weight,
6413
- confidence: args.confidence,
6414
- context: args.rationale || `Linked as ${args.relation}`,
6780
+ weight: args.weight,
6781
+ confidence,
6782
+ context,
6415
6783
  projectId: args.beliefNode.projectId ? String(args.beliefNode.projectId) : void 0,
6416
6784
  createdBy: args.createdBy,
6417
6785
  fromNodeType: "evidence",
@@ -6424,17 +6792,13 @@ async function applyOperationalLinkEffects(ctx, args) {
6424
6792
  console.error("[EpistemicSpine] Failed to create informs edge:", e);
6425
6793
  }
6426
6794
  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
- );
6795
+ await ctx.scheduler.runAfter(0, "verificationActions:deepVerifyEvidence", {
6796
+ insightId: args.insightId,
6797
+ targetType: "belief",
6798
+ targetId: args.beliefId,
6799
+ projectId: args.beliefNode.projectId,
6800
+ userId: args.createdBy
6801
+ });
6438
6802
  }
6439
6803
  }
6440
6804
  async function removeOperationalLinkEffects(ctx, args) {
@@ -6459,7 +6823,7 @@ async function removeOperationalLinkEffects(ctx, args) {
6459
6823
  try {
6460
6824
  const beliefSpineNode = await ctx.db.get(args.beliefId);
6461
6825
  const evidenceSpineNode = await ctx.db.get(args.insightId);
6462
- if (beliefSpineNode && beliefSpineNode.nodeType === "belief" && evidenceSpineNode && evidenceSpineNode.nodeType === "evidence") {
6826
+ if (beliefSpineNode?.nodeType === "belief" && evidenceSpineNode?.nodeType === "evidence") {
6463
6827
  const edges2 = await ctx.db.query("epistemicEdges").withIndex(
6464
6828
  "by_from_to",
6465
6829
  (q) => q.eq("fromNodeId", evidenceSpineNode._id).eq("toNodeId", beliefSpineNode._id)
@@ -6477,11 +6841,80 @@ async function removeOperationalLinkEffects(ctx, args) {
6477
6841
  console.error("[EpistemicSpine] Failed to remove informs edge:", e);
6478
6842
  }
6479
6843
  }
6844
+
6845
+ // src/beliefEvidenceLinks.ts
6846
+ var beliefIdUnion = v.id("epistemicNodes");
6847
+ var insightIdUnion2 = v.id("epistemicNodes");
6848
+ var suggestionStatusValidator = v.union(
6849
+ v.literal("suggested"),
6850
+ v.literal("approved"),
6851
+ v.literal("dismissed")
6852
+ );
6853
+ var matcherMetadataValidator = v.object({
6854
+ surface: v.string(),
6855
+ matcherFamily: v.optional(v.string()),
6856
+ matcherKey: v.optional(v.string()),
6857
+ matcherVersion: v.optional(v.string()),
6858
+ reviewStatus: v.optional(
6859
+ v.union(
6860
+ v.literal("pending"),
6861
+ v.literal("accepted"),
6862
+ v.literal("rejected"),
6863
+ v.literal("auto_accepted"),
6864
+ v.literal("superseded")
6865
+ )
6866
+ ),
6867
+ configSnapshot: v.optional(v.any()),
6868
+ signalSnapshot: v.optional(v.any()),
6869
+ outcomeMetadata: v.optional(v.any())
6870
+ });
6871
+ async function markProjectGraphDirty(ctx, projectId) {
6872
+ if (!projectId) {
6873
+ return;
6874
+ }
6875
+ await ctx.scheduler.runAfter(
6876
+ 0,
6877
+ internal.graphAnalysisCache.markCacheStaleInternal,
6878
+ {
6879
+ projectId
6880
+ }
6881
+ );
6882
+ await resolveGraphPrimitivesAppResolvers().patchProject(ctx, projectId, {
6883
+ lastActivityAt: Date.now()
6884
+ });
6885
+ }
6886
+ async function recordMatcherDecision(ctx, args) {
6887
+ if (!args.matcherMetadata) {
6888
+ return;
6889
+ }
6890
+ await ctx.runMutation("matcherFeedback:recordDecision", {
6891
+ projectId: args.beliefNode.projectId,
6892
+ topicId: args.beliefNode.topicId,
6893
+ surface: args.matcherMetadata.surface,
6894
+ matcherFamily: args.matcherMetadata.matcherFamily,
6895
+ matcherKey: args.matcherMetadata.matcherKey,
6896
+ matcherVersion: args.matcherMetadata.matcherVersion,
6897
+ sourceEntityId: String(args.insightId),
6898
+ targetEntityId: String(args.beliefId),
6899
+ suggestionTable: "beliefEvidenceLinks",
6900
+ suggestionId: args.linkId,
6901
+ reviewStatus: deriveMatcherReviewStatus({
6902
+ explicitReviewStatus: args.matcherMetadata.reviewStatus,
6903
+ linkStatus: args.linkStatus
6904
+ }),
6905
+ reviewedBy: args.reviewedBy,
6906
+ decisionReason: args.decisionReason,
6907
+ configSnapshot: args.matcherMetadata.configSnapshot,
6908
+ signalSnapshot: args.matcherMetadata.signalSnapshot,
6909
+ outcomeMetadata: args.matcherMetadata.outcomeMetadata
6910
+ });
6911
+ }
6480
6912
  var create2 = mutation({
6481
6913
  args: {
6482
6914
  beliefId: beliefIdUnion,
6483
6915
  insightId: insightIdUnion2,
6484
6916
  relation: v.union(v.literal("supports"), v.literal("contradicts")),
6917
+ weight: v.number(),
6485
6918
  confidence: v.optional(v.number()),
6486
6919
  rationale: v.optional(v.string()),
6487
6920
  status: v.optional(suggestionStatusValidator),
@@ -6500,6 +6933,16 @@ var create2 = mutation({
6500
6933
  if (!insightNode) {
6501
6934
  throw new Error("Evidence node not found");
6502
6935
  }
6936
+ if (!Number.isFinite(args.weight) || args.weight === 0 || args.weight < -1 || args.weight > 1) {
6937
+ throw new Error("Belief evidence links require explicit nonzero weight in [-1, 1]");
6938
+ }
6939
+ if (args.relation === "supports" && args.weight < 0) {
6940
+ throw new Error("Supporting evidence links require positive weight");
6941
+ }
6942
+ if (args.relation === "contradicts" && args.weight > 0) {
6943
+ throw new Error("Contradicting evidence links require negative weight");
6944
+ }
6945
+ const relationConfidence = Math.abs(args.weight);
6503
6946
  if (beliefNode.projectId) {
6504
6947
  const hasAccess = await checkProjectAccess(
6505
6948
  ctx,
@@ -6524,7 +6967,7 @@ var create2 = mutation({
6524
6967
  );
6525
6968
  await ctx.db.patch(duplicate._id, {
6526
6969
  relation: args.relation,
6527
- confidence: args.confidence,
6970
+ confidence: relationConfidence,
6528
6971
  rationale: args.rationale,
6529
6972
  status: nextStatus
6530
6973
  });
@@ -6536,7 +6979,7 @@ var create2 = mutation({
6536
6979
  beliefId: resolvedBeliefId,
6537
6980
  insightId: resolvedInsightId,
6538
6981
  relation: args.relation,
6539
- confidence: args.confidence,
6982
+ weight: args.weight,
6540
6983
  rationale: args.rationale,
6541
6984
  createdBy: args.createdBy
6542
6985
  });
@@ -6552,15 +6995,12 @@ var create2 = mutation({
6552
6995
  await markProjectGraphDirty(ctx, beliefNode.projectId);
6553
6996
  return duplicate._id;
6554
6997
  }
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
6998
  const now = Date.now();
6559
6999
  const linkId = await ctx.db.insert("beliefEvidenceLinks", {
6560
7000
  beliefId: args.beliefId,
6561
7001
  insightId: args.insightId,
6562
7002
  relation: args.relation,
6563
- confidence: args.confidence,
7003
+ confidence: relationConfidence,
6564
7004
  rationale: args.rationale,
6565
7005
  status: args.status,
6566
7006
  createdBy: args.createdBy,
@@ -6580,7 +7020,7 @@ var create2 = mutation({
6580
7020
  beliefId: resolvedBeliefId,
6581
7021
  insightId: resolvedInsightId,
6582
7022
  relation: args.relation,
6583
- confidence: args.confidence,
7023
+ weight: args.weight,
6584
7024
  rationale: args.rationale,
6585
7025
  createdBy: args.createdBy
6586
7026
  });
@@ -6688,7 +7128,7 @@ var reviewSuggestion = mutation({
6688
7128
  beliefId: link.beliefId,
6689
7129
  insightId: link.insightId,
6690
7130
  relation: link.relation,
6691
- confidence: link.confidence,
7131
+ weight: link.relation === "supports" ? Math.abs(link.confidence ?? 0) : -Math.abs(link.confidence ?? 0),
6692
7132
  rationale: link.rationale,
6693
7133
  createdBy: args.userId
6694
7134
  });
@@ -8937,7 +9377,7 @@ async function evaluateBuiltInEvidentialContract(args) {
8937
9377
  observedValue: snapshot.value,
8938
9378
  operator: config.operator,
8939
9379
  threshold: config.threshold,
8940
- action: config.action ?? "modulate_confidence",
9380
+ action: config.action ?? "append_sl_scoring",
8941
9381
  actionParams: config.actionParams
8942
9382
  }
8943
9383
  };
@@ -11707,6 +12147,91 @@ function formatEvidenceNode(n) {
11707
12147
  updatedAt: n.updatedAt
11708
12148
  };
11709
12149
  }
12150
+ function assertSignedImpactScore2(value, context) {
12151
+ if (typeof value !== "number" || !Number.isFinite(value) || value === 0 || value < -1 || value > 1) {
12152
+ throw new Error(`${context} requires explicit nonzero weight in [-1, 1]`);
12153
+ }
12154
+ return value;
12155
+ }
12156
+ function normalizeEvidenceRelation(relation, weight, context) {
12157
+ if (relation === "supports" || relation === "contradicts") {
12158
+ if (relation === "supports" && weight < 0) {
12159
+ throw new Error(`${context} supports relation requires positive weight`);
12160
+ }
12161
+ if (relation === "contradicts" && weight > 0) {
12162
+ throw new Error(`${context} contradicts relation requires negative weight`);
12163
+ }
12164
+ return relation;
12165
+ }
12166
+ return weight < 0 ? "contradicts" : "supports";
12167
+ }
12168
+ async function createEvidenceBeliefEdge(ctx, args) {
12169
+ const edgeGlobalId = crypto.randomUUID();
12170
+ const confidence = Math.abs(args.weight);
12171
+ const existingEdges = await ctx.db.query("epistemicEdges").withIndex(
12172
+ "by_from_to",
12173
+ (q) => q.eq("fromNodeId", args.evidenceNodeId).eq("toNodeId", args.beliefNodeId)
12174
+ ).collect();
12175
+ const existing = existingEdges.find((edge) => edge.edgeType === "informs");
12176
+ const edgeDoc = {
12177
+ globalId: edgeGlobalId,
12178
+ fromNodeId: args.evidenceNodeId,
12179
+ toNodeId: args.beliefNodeId,
12180
+ sourceGlobalId: args.evidenceGlobalId,
12181
+ targetGlobalId: args.beliefGlobalId,
12182
+ edgeType: "informs",
12183
+ weight: args.weight,
12184
+ confidence,
12185
+ context: args.rationale,
12186
+ reasoningMethod: "testimonial",
12187
+ derivationType: "evidence_sl_scoring",
12188
+ metadata: {
12189
+ relation: args.relation,
12190
+ confidence,
12191
+ impactScore: args.weight,
12192
+ invariant: "evidence.belief_impact_required"
12193
+ },
12194
+ createdBy: args.userId,
12195
+ createdAt: Date.now(),
12196
+ updatedAt: Date.now(),
12197
+ topicId: args.topicId,
12198
+ projectId: args.projectId,
12199
+ fromNodeType: "evidence",
12200
+ toNodeType: "belief",
12201
+ fromLayer: "L2",
12202
+ toLayer: "L3"
12203
+ };
12204
+ if (existing) {
12205
+ await ctx.db.patch(existing._id, {
12206
+ ...edgeDoc,
12207
+ globalId: existing.globalId,
12208
+ createdAt: existing.createdAt ?? edgeDoc.createdAt
12209
+ });
12210
+ } else {
12211
+ await ctx.db.insert("epistemicEdges", edgeDoc);
12212
+ }
12213
+ await ctx.scheduler.runAfter(5e3, internal.neo4jEdgeAPI.createEdge, {
12214
+ globalId: existing?.globalId ?? edgeGlobalId,
12215
+ fromGlobalId: args.evidenceGlobalId,
12216
+ toGlobalId: args.beliefGlobalId,
12217
+ edgeType: "informs",
12218
+ weight: args.weight,
12219
+ confidence,
12220
+ createdBy: args.userId,
12221
+ topicId: args.projectId ?? args.topicId,
12222
+ fromNodeType: "evidence",
12223
+ toNodeType: "belief",
12224
+ fromLayer: "L2",
12225
+ toLayer: "L3",
12226
+ metadata: {
12227
+ relation: args.relation,
12228
+ confidence,
12229
+ impactScore: args.weight,
12230
+ invariant: "evidence.belief_impact_required"
12231
+ }
12232
+ });
12233
+ return existing?.globalId ?? edgeGlobalId;
12234
+ }
11710
12235
  var create6 = mutation({
11711
12236
  args: {
11712
12237
  ...optionalEvidenceScopeArgs,
@@ -11727,11 +12252,12 @@ var create6 = mutation({
11727
12252
  informationAsymmetry: v.optional(v.string()),
11728
12253
  sourceDescription: v.optional(v.string()),
11729
12254
  metadata: v.optional(v.any()),
11730
- // Optional linking to beliefs
11731
- linkedBeliefNodeId: v.optional(v.id("epistemicNodes")),
12255
+ // Required belief impact link.
12256
+ linkedBeliefNodeId: v.id("epistemicNodes"),
11732
12257
  evidenceRelation: v.optional(
11733
12258
  v.union(v.literal("supports"), v.literal("contradicts"))
11734
12259
  ),
12260
+ weight: v.number(),
11735
12261
  confidence: v.optional(v.number())
11736
12262
  },
11737
12263
  returns: permissiveReturn,
@@ -11753,6 +12279,16 @@ var create6 = mutation({
11753
12279
  const contentHash = generateContentHash5(args.text);
11754
12280
  const kind = normalizeKind(args.kind);
11755
12281
  const sourceType = normalizeSourceType(args.sourceType);
12282
+ const weight = assertSignedImpactScore2(args.weight, "Evidence creation");
12283
+ const evidenceRelation = normalizeEvidenceRelation(
12284
+ args.evidenceRelation,
12285
+ weight,
12286
+ "Evidence creation"
12287
+ );
12288
+ const linkedBeliefNode = await ctx.db.get(args.linkedBeliefNodeId);
12289
+ if (!linkedBeliefNode || linkedBeliefNode.nodeType !== "belief") {
12290
+ throw new Error("Evidence creation requires a linked belief node");
12291
+ }
11756
12292
  const additionalMetadata = args.metadata && typeof args.metadata === "object" ? args.metadata : {};
11757
12293
  const nodeId = await ctx.db.insert("epistemicNodes", {
11758
12294
  globalId,
@@ -11780,8 +12316,10 @@ var create6 = mutation({
11780
12316
  sourceQuestionId: args.sourceQuestionId,
11781
12317
  rationale: args.rationale,
11782
12318
  linkedBeliefNodeId: args.linkedBeliefNodeId,
11783
- evidenceRelation: args.evidenceRelation,
11784
- confidence: args.confidence,
12319
+ evidenceRelation,
12320
+ confidence: Math.abs(weight),
12321
+ weight,
12322
+ impactScore: weight,
11785
12323
  methodology: args.methodology,
11786
12324
  informationAsymmetry: args.informationAsymmetry,
11787
12325
  sourceDescription: args.sourceDescription,
@@ -11801,29 +12339,18 @@ var create6 = mutation({
11801
12339
  nodeType: "evidence",
11802
12340
  text: args.text
11803
12341
  });
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
- }
12342
+ await createEvidenceBeliefEdge(ctx, {
12343
+ evidenceNodeId: nodeId,
12344
+ evidenceGlobalId: globalId,
12345
+ beliefNodeId: args.linkedBeliefNodeId,
12346
+ beliefGlobalId: linkedBeliefNode.globalId,
12347
+ relation: evidenceRelation,
12348
+ weight,
12349
+ userId: args.userId,
12350
+ topicId: scope.topicId ? String(scope.topicId) : void 0,
12351
+ projectId: scope.projectId ? String(scope.projectId) : void 0,
12352
+ rationale: args.rationale
12353
+ });
11827
12354
  await ctx.db.insert("epistemicAudit", {
11828
12355
  entityType: "evidence",
11829
12356
  entityId: nodeId,
@@ -11838,7 +12365,8 @@ var create6 = mutation({
11838
12365
  kind,
11839
12366
  sourceType,
11840
12367
  linkedBeliefNodeId: args.linkedBeliefNodeId,
11841
- evidenceRelation: args.evidenceRelation
12368
+ evidenceRelation,
12369
+ weight
11842
12370
  }
11843
12371
  });
11844
12372
  if (scope.projectId || scope.topicId) {
@@ -11881,6 +12409,7 @@ var createAndLink = mutation({
11881
12409
  userId: v.string(),
11882
12410
  beliefNodeId: v.id("epistemicNodes"),
11883
12411
  relation: v.union(v.literal("supports"), v.literal("contradicts")),
12412
+ weight: v.number(),
11884
12413
  confidence: v.optional(v.number())
11885
12414
  },
11886
12415
  returns: permissiveReturn,
@@ -11895,7 +12424,17 @@ var createAndLink = mutation({
11895
12424
  const contentHash = generateContentHash5(args.text);
11896
12425
  const kind = normalizeKind(args.kind);
11897
12426
  const sourceType = normalizeSourceType(args.sourceType);
11898
- const confidence = args.confidence ?? 0.7;
12427
+ const weight = assertSignedImpactScore2(args.weight, "Evidence createAndLink");
12428
+ const relation = normalizeEvidenceRelation(
12429
+ args.relation,
12430
+ weight,
12431
+ "Evidence createAndLink"
12432
+ );
12433
+ const confidence = Math.abs(weight);
12434
+ const beliefNode = await ctx.db.get(args.beliefNodeId);
12435
+ if (!beliefNode || beliefNode.nodeType !== "belief") {
12436
+ throw new Error("Belief node not found for edge creation");
12437
+ }
11899
12438
  const nodeId = await ctx.db.insert("epistemicNodes", {
11900
12439
  globalId,
11901
12440
  topicId: scope.topicId,
@@ -11915,36 +12454,26 @@ var createAndLink = mutation({
11915
12454
  kind,
11916
12455
  tags: args.tags || [],
11917
12456
  linkedBeliefNodeId: args.beliefNodeId,
11918
- evidenceRelation: args.relation,
11919
- confidence
12457
+ evidenceRelation: relation,
12458
+ confidence,
12459
+ weight,
12460
+ impactScore: weight
11920
12461
  }
11921
12462
  });
11922
12463
  await ctx.scheduler.runAfter(0, internal.neo4jSync.syncNodeToNeo4j, {
11923
12464
  nodeId,
11924
12465
  operation: "upsert"
11925
12466
  });
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",
12467
+ const edgeGlobalId = await createEvidenceBeliefEdge(ctx, {
12468
+ evidenceNodeId: nodeId,
12469
+ evidenceGlobalId: globalId,
12470
+ beliefNodeId: args.beliefNodeId,
12471
+ beliefGlobalId: beliefNode.globalId,
12472
+ relation,
11937
12473
  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
- }
12474
+ userId: args.userId,
12475
+ topicId: scope.topicId ? String(scope.topicId) : void 0,
12476
+ projectId: scope.projectId ? String(scope.projectId) : void 0
11948
12477
  });
11949
12478
  await markProjectGraphDirty2(ctx, scope.projectId, String(scope.topicId));
11950
12479
  return { nodeId, edgeGlobalId };
@@ -12005,8 +12534,9 @@ var internalCreate2 = internalMutation({
12005
12534
  sourceQuestionId: v.optional(v.string()),
12006
12535
  userId: v.string(),
12007
12536
  rationale: v.string(),
12008
- linkedBeliefNodeId: v.optional(v.id("epistemicNodes")),
12537
+ linkedBeliefNodeId: v.id("epistemicNodes"),
12009
12538
  evidenceRelation: v.optional(v.string()),
12539
+ weight: v.number(),
12010
12540
  confidence: v.optional(v.number()),
12011
12541
  metadata: v.optional(v.any()),
12012
12542
  runtimeToolName: v.optional(v.string()),
@@ -12041,6 +12571,16 @@ var internalCreate2 = internalMutation({
12041
12571
  const contentHash = generateContentHash5(args.text);
12042
12572
  const kind = normalizeKind(args.kind);
12043
12573
  const sourceType = normalizeSourceType(args.sourceType);
12574
+ const weight = assertSignedImpactScore2(args.weight, "Internal evidence creation");
12575
+ const evidenceRelation = normalizeEvidenceRelation(
12576
+ args.evidenceRelation,
12577
+ weight,
12578
+ "Internal evidence creation"
12579
+ );
12580
+ const linkedBeliefNode = await ctx.db.get(args.linkedBeliefNodeId);
12581
+ if (!linkedBeliefNode || linkedBeliefNode.nodeType !== "belief") {
12582
+ throw new Error("Internal evidence creation requires a linked belief node");
12583
+ }
12044
12584
  const additionalMetadata = args.metadata && typeof args.metadata === "object" ? args.metadata : {};
12045
12585
  const nodeId = await ctx.db.insert("epistemicNodes", {
12046
12586
  globalId,
@@ -12068,8 +12608,10 @@ var internalCreate2 = internalMutation({
12068
12608
  sourceQuestionId: args.sourceQuestionId,
12069
12609
  rationale: args.rationale,
12070
12610
  linkedBeliefNodeId: args.linkedBeliefNodeId,
12071
- evidenceRelation: args.evidenceRelation,
12072
- confidence: args.confidence,
12611
+ evidenceRelation,
12612
+ confidence: Math.abs(weight),
12613
+ weight,
12614
+ impactScore: weight,
12073
12615
  ...additionalMetadata
12074
12616
  }
12075
12617
  });
@@ -12089,8 +12631,9 @@ var internalCreate2 = internalMutation({
12089
12631
  externalSourceType: args.externalSourceType,
12090
12632
  sourceUrl: args.sourceUrl,
12091
12633
  linkedBeliefNodeId: args.linkedBeliefNodeId,
12092
- evidenceRelation: args.evidenceRelation,
12093
- confidence: args.confidence
12634
+ evidenceRelation,
12635
+ confidence: Math.abs(weight),
12636
+ weight
12094
12637
  },
12095
12638
  triggeringAction: "epistemicEvidence.internalCreate"
12096
12639
  });
@@ -12098,30 +12641,18 @@ var internalCreate2 = internalMutation({
12098
12641
  nodeId,
12099
12642
  operation: "upsert"
12100
12643
  });
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
- }
12644
+ await createEvidenceBeliefEdge(ctx, {
12645
+ evidenceNodeId: nodeId,
12646
+ evidenceGlobalId: globalId,
12647
+ beliefNodeId: args.linkedBeliefNodeId,
12648
+ beliefGlobalId: linkedBeliefNode.globalId,
12649
+ relation: evidenceRelation,
12650
+ weight,
12651
+ userId: args.userId,
12652
+ topicId: scope.topicId ? String(scope.topicId) : void 0,
12653
+ projectId: scope.projectId ? String(scope.projectId) : void 0,
12654
+ rationale: args.rationale
12655
+ });
12125
12656
  if (scope.projectId || scope.topicId) {
12126
12657
  await ctx.scheduler.runAfter(
12127
12658
  0,
@@ -13412,7 +13943,7 @@ function assertBeliefNodeGenericUpdateAllowed(args) {
13412
13943
  throwInvariantError({
13413
13944
  message: "Belief confidence is append-only. Generic node updates cannot set confidence directly.",
13414
13945
  invariantCode: "belief.confidence_append_only",
13415
- suggestion: "Use epistemicBeliefs.modulateConfidence() so the beliefConfidence ledger and audit trail are updated together.",
13946
+ suggestion: "Use epistemicBeliefs.appendSlScoring() so the beliefConfidence ledger and audit trail are updated together.",
13416
13947
  details: { mutationName: args.mutationName, nodeId: args.node._id }
13417
13948
  });
13418
13949
  }
@@ -13464,7 +13995,7 @@ function assertBeliefNodeVerifyAllowed(args) {
13464
13995
  throwInvariantError({
13465
13996
  message: "Belief verification cannot set confidence directly. Confidence changes must stay append-only.",
13466
13997
  invariantCode: "belief.confidence_append_only",
13467
- suggestion: "Call epistemicBeliefs.modulateConfidence() after verification so the confidence history is preserved.",
13998
+ suggestion: "Call epistemicBeliefs.appendSlScoring() after verification so the confidence history is preserved.",
13468
13999
  details: { mutationName: args.mutationName, nodeId: args.node._id }
13469
14000
  });
13470
14001
  }
@@ -18904,7 +19435,7 @@ var epistemicBeliefs = {
18904
19435
  internalGetByTopic,
18905
19436
  linkBeliefs,
18906
19437
  linkEvidence,
18907
- modulateConfidence,
19438
+ appendSlScoring,
18908
19439
  propagateConfidenceChange,
18909
19440
  reassignBeliefsTopic,
18910
19441
  refineBelief,