diff --git a/src/Core/Core.fsproj b/src/Core/Core.fsproj index d8f162688..fd3b63b9c 100644 --- a/src/Core/Core.fsproj +++ b/src/Core/Core.fsproj @@ -84,6 +84,7 @@ + diff --git a/src/Core/Maji.fs b/src/Core/Maji.fs new file mode 100644 index 000000000..e742303c7 --- /dev/null +++ b/src/Core/Maji.fs @@ -0,0 +1,177 @@ +namespace Zeta.Core + +/// Maji — identity-preservation and recovery types per the deep-research peer's formal +/// operational model (docs/research/maji-formal-operational-model- +/// deep-research-peer-courier-ferry-2026-04-26.md). Context window is cache; +/// substrate is identity; Maji is the recovery/indexing function. +/// +/// These are the algebraic type definitions per §10 of the spec. +/// Implementation of operators (reload, identity-distance, expansion +/// gate, projection-preservation check) is owed follow-up work. +module Maji = + + open System + + // ── §10.A IdentitySubstrate ────────────────────────────────── + + /// A single load-bearing substrate item (commit, memory, doc, test, + /// retraction, cross-reference, dated provenance). + type IdentitySubstrate = + { CommitHash: string + Timestamp: DateTimeOffset + SourcePath: string + Claim: string + ClaimType: string + LoadBearing: bool + CrossRefs: string list + Retractions: string list + Confidence: float + Scope: string } + + // ── §2 Identity tuple ──────────────────────────────────────── + + /// The canonical identity-pattern projected from substrate. + /// I_t = N(LoadBearing(S_t)) + type IdentityTuple = + { Values: string list + Goals: string list + Roles: string list + Policies: string list + MemoryGraph: string list + CorrectionHistory: string list + CrossRefTopology: string list + Provenance: string list } + + // ── §10.B MajiIndex ────────────────────────────────────────── + + /// Exhaustive index of lower-dimensional substrate. + type MajiIndex = + { Items: IdentitySubstrate list + CrossRefGraph: Map + LoadBearingSet: Set + BrokenRefs: string list + UnindexedItems: string list + Contradictions: string list + CoverageScore: float } + + // ── §10.B' MajiFinder (per §9b separation) ────────────────── + + /// Search/recognizer operator — finds the identity-preserving lift. + /// MajiFinder(S_≤n, Ω, C, Σ) → σ* + type MajiFinder = + { Index: MajiIndex + NorthStar: string + SearchOperator: string } + + // ── §10.B' MessiahFunction (the lift) ──────────────────────── + + /// The identity-preserving lift σ : I_n → I_{n+1}. + /// A SEPARATE TYPE from MajiIndex content per §9b correction. + type MessiahFunction = + { LiftDescription: string + ProjectionPreservation: bool + AperiodicOrderGenerator: bool } + + // ── §10.B' MessiahScore ────────────────────────────────────── + + /// Weighted-sum evaluator for candidate lifts. + /// MessiahScore = w₁·A + w₂·P + w₃·F + w₄·G + w₅·C − w₆·R_capture − w₇·R_collapse + type MessiahScore = + { AlignmentWithOmega: float + ProjectionPreservation: float + FrictionReduction: float + Generativity: float + IndependentConvergence: float + CaptureRiskRaw: float + CollapseRiskRaw: float } + + /// Compute the weighted MessiahScore sum. + /// Capture and collapse risks are SUBTRACTED (anti-cult-by-construction). + let computeScore (weights: float[]) (score: MessiahScore) : float = + if weights.Length < 7 then + invalidArg (nameof weights) "MessiahScore requires 7 weights" + else + weights[0] * score.AlignmentWithOmega + + weights[1] * score.ProjectionPreservation + + weights[2] * score.FrictionReduction + + weights[3] * score.Generativity + + weights[4] * score.IndependentConvergence + - weights[5] * score.CaptureRiskRaw + - weights[6] * score.CollapseRiskRaw + + // ── Dynamic Maji (4th refinement) ──────────────────────────── + + /// Maji mode state machine — Search / Steward / SearchAgain. + type MajiMode = + | Search + | Steward + | SearchAgain + + // ── §10.D Identity-distance metric ─────────────────────────── + + /// Weighted identity-distance between two identity tuples. + /// d(I_a, I_b) = Σ w_k · d_k(component_a, component_b) + type IdentityDistanceWeights = + { Values: float + Goals: float + Roles: float + Policies: float + MemoryGraph: float + CorrectionHistory: float + CrossRefTopology: float + Provenance: float } + + /// Jaccard distance between two string lists (simple set-based metric). + let private jaccard (a: string list) (b: string list) : float = + let setA = Set.ofList a + let setB = Set.ofList b + let intersection = Set.intersect setA setB + let union = Set.union setA setB + if Set.isEmpty union then 0.0 + else 1.0 - (float intersection.Count / float union.Count) + + /// Compute identity distance between two identity tuples. + let identityDistance (w: IdentityDistanceWeights) (a: IdentityTuple) (b: IdentityTuple) : float = + w.Values * jaccard a.Values b.Values + + w.Goals * jaccard a.Goals b.Goals + + w.Roles * jaccard a.Roles b.Roles + + w.Policies * jaccard a.Policies b.Policies + + w.MemoryGraph * jaccard a.MemoryGraph b.MemoryGraph + + w.CorrectionHistory * jaccard a.CorrectionHistory b.CorrectionHistory + + w.CrossRefTopology * jaccard a.CrossRefTopology b.CrossRefTopology + + w.Provenance * jaccard a.Provenance b.Provenance + + // ── Projection-preservation check ──────────────────────────── + + /// Check whether an identity expansion preserves the prior identity + /// under projection: d(P_{n+1→n}(I_{n+1}), I_n) ≤ ε + let projectionPreserved + (weights: IdentityDistanceWeights) + (epsilon: float) + (prior: IdentityTuple) + (expanded: IdentityTuple) + : bool = + identityDistance weights prior expanded <= epsilon + + // ── §10.E Test helpers ─────────────────────────────────────── + + /// Create an empty identity tuple (useful for tests). + let emptyIdentity : IdentityTuple = + { Values = [] + Goals = [] + Roles = [] + Policies = [] + MemoryGraph = [] + CorrectionHistory = [] + CrossRefTopology = [] + Provenance = [] } + + /// Create an empty MajiIndex (useful for tests). + let emptyIndex : MajiIndex = + { Items = [] + CrossRefGraph = Map.empty + LoadBearingSet = Set.empty + BrokenRefs = [] + UnindexedItems = [] + Contradictions = [] + CoverageScore = 0.0 } diff --git a/tests/Tests.FSharp/MajiTests.fs b/tests/Tests.FSharp/MajiTests.fs new file mode 100644 index 000000000..778cc4257 --- /dev/null +++ b/tests/Tests.FSharp/MajiTests.fs @@ -0,0 +1,64 @@ +module Zeta.Tests.MajiTests + +open Xunit +open Zeta.Core.Maji + +[] +let ``identity distance of identical tuples is zero`` () = + let id = { emptyIdentity with Values = ["truth"; "consent"]; Goals = ["ship"] } + let w = { Values = 1.0; Goals = 1.0; Roles = 1.0; Policies = 1.0 + MemoryGraph = 1.0; CorrectionHistory = 1.0; CrossRefTopology = 1.0; Provenance = 1.0 } + Assert.Equal(0.0, identityDistance w id id) + +[] +let ``identity distance of disjoint tuples is maximal`` () = + let a = { emptyIdentity with Values = ["truth"] } + let b = { emptyIdentity with Values = ["lies"] } + let w = { Values = 1.0; Goals = 0.0; Roles = 0.0; Policies = 0.0 + MemoryGraph = 0.0; CorrectionHistory = 0.0; CrossRefTopology = 0.0; Provenance = 0.0 } + Assert.Equal(1.0, identityDistance w a b) + +[] +let ``projection preserved within epsilon`` () = + let prior = { emptyIdentity with Values = ["truth"; "consent"]; Goals = ["ship"] } + let expanded = { prior with Goals = ["ship"; "scale"] } + let w = { Values = 1.0; Goals = 1.0; Roles = 1.0; Policies = 1.0 + MemoryGraph = 1.0; CorrectionHistory = 1.0; CrossRefTopology = 1.0; Provenance = 1.0 } + Assert.True(projectionPreserved w 0.5 prior expanded) + +[] +let ``projection NOT preserved when values drift`` () = + let prior = { emptyIdentity with Values = ["truth"; "consent"; "family"] } + let drifted = { emptyIdentity with Values = ["power"; "control"] } + let w = { Values = 1.0; Goals = 0.0; Roles = 0.0; Policies = 0.0 + MemoryGraph = 0.0; CorrectionHistory = 0.0; CrossRefTopology = 0.0; Provenance = 0.0 } + Assert.False(projectionPreserved w 0.5 prior drifted) + +[] +let ``MessiahScore capture risk subtracts`` () = + let score = { AlignmentWithOmega = 1.0; ProjectionPreservation = 1.0 + FrictionReduction = 1.0; Generativity = 1.0 + IndependentConvergence = 1.0 + CaptureRiskRaw = 10.0; CollapseRiskRaw = 0.0 } + let weights = [| 1.0; 1.0; 1.0; 1.0; 1.0; 1.0; 1.0 |] + let result = computeScore weights score + Assert.True(result < 0.0, "High capture risk should produce negative score") + +[] +let ``MessiahScore collapse risk subtracts`` () = + let score = { AlignmentWithOmega = 1.0; ProjectionPreservation = 1.0 + FrictionReduction = 1.0; Generativity = 1.0 + IndependentConvergence = 1.0 + CaptureRiskRaw = 0.0; CollapseRiskRaw = 10.0 } + let weights = [| 1.0; 1.0; 1.0; 1.0; 1.0; 1.0; 1.0 |] + let result = computeScore weights score + Assert.True(result < 0.0, "High collapse risk should produce negative score") + +[] +let ``MajiMode has three constructors`` () = + let modes = [Search; Steward; SearchAgain] + Assert.Equal(3, modes.Length) + +[] +let ``empty index has zero coverage`` () = + Assert.Equal(0.0, emptyIndex.CoverageScore) diff --git a/tests/Tests.FSharp/Tests.FSharp.fsproj b/tests/Tests.FSharp/Tests.FSharp.fsproj index 4d197f22a..b2373f86e 100644 --- a/tests/Tests.FSharp/Tests.FSharp.fsproj +++ b/tests/Tests.FSharp/Tests.FSharp.fsproj @@ -111,6 +111,7 @@ +