car-memgine 0.48.0

Memgine — graph-based memory engine for Common Agent Runtime
//! Localized-vs-global maintenance decision (arXiv 2606.24775, *Are We Ready For
//! An Agent-Native Memory System?*).
//!
//! Applies the paper's most actionable finding — **localized maintenance is more
//! cost-efficient than global reorganization** — as a pure decision core; see
//! `docs/proposals/agent-native-memory-diagnostic.md`. When some regions of the
//! store are "dirty" (conflicting/stale facts needing reconciliation), there are
//! two strategies:
//!
//! - **Localized**: patch only the dirty regions. Cost scales with the *dirty*
//!   set; resolves exactly those regions.
//! - **Global**: a full reorganization (CAR's `consolidate()`) — re-cluster /
//!   re-summarize the *whole* store. Cost scales with the *total* set; resolves
//!   the dirty regions **and** yields a store-wide structural gain (dedup,
//!   re-clustering).
//!
//! Both fully resolve the dirty regions, so that benefit cancels in the
//! comparison. Global is worth its extra cost only when its store-wide
//! structural gain (valued) clears the *additional* cost of touching everything:
//!
//! ```text
//! prefer Global  iff  structural_gain · gain_value  >  (global_cost − localized_cost)
//! ```
//!
//! Because a global reorg touches every region (`global_cost` scales with
//! `total_regions`) while localized touches only the dirty ones, the bar is high
//! for large stores with a small dirty set — exactly the regime where the paper
//! finds localized wins. Pure and deterministic, like [`crate::memsys`].

use serde::{Deserialize, Serialize};

/// Inputs to the maintenance decision: how much is dirty, how big the store is,
/// the per-region costs of each strategy, and how much a global reorg's
/// store-wide structural gain is worth.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct MaintenanceInput {
    /// Regions (facts/clusters) that are dirty and need reconciliation.
    #[serde(default)]
    pub dirty_regions: u64,
    /// Total regions in the store (a global reorg touches all of them).
    #[serde(default)]
    pub total_regions: u64,
    /// Cost to locally patch one dirty region.
    #[serde(default)]
    pub localized_cost_per_region: f64,
    /// Cost per region of a full reorganization (paid across *all* regions).
    #[serde(default)]
    pub global_cost_per_region: f64,
    /// Store-wide structural quality gain a global reorg yields, in `[0,1]`
    /// (dedup, re-clustering, tighter topology) — over and above resolving the
    /// dirty regions, which localized maintenance also achieves.
    #[serde(default)]
    pub global_structural_gain: f64,
    /// Value of a full unit of structural gain, in the same units as cost. Lets
    /// the caller price store-wide quality against maintenance spend.
    #[serde(default)]
    pub gain_value: f64,
}

/// The chosen maintenance strategy.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum MaintenanceStrategy {
    /// Nothing is dirty — do nothing.
    NoOp,
    /// Patch only the dirty regions (the cost-efficient default).
    Localized,
    /// Full reorganization — worth its store-wide cost here.
    Global,
}

/// The decision plus the costs and the net-value gap that drove it.
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct MaintenanceDecision {
    pub strategy: MaintenanceStrategy,
    pub localized_cost: f64,
    pub global_cost: f64,
    /// The valued store-wide structural gain a global reorg would add
    /// (`global_structural_gain · gain_value`).
    pub global_extra_value: f64,
    /// `global_extra_value − (global_cost − localized_cost)`: positive means
    /// global clears its extra-cost bar; negative means localized is cheaper net.
    pub global_net_advantage: f64,
    pub rationale: String,
}

/// Decide localized vs. global maintenance. Returns [`MaintenanceStrategy::NoOp`]
/// when nothing is dirty; otherwise picks Global only when its store-wide
/// structural gain (valued) outweighs the extra cost of reorganizing everything,
/// and Localized otherwise. Deterministic.
pub fn decide_maintenance(input: &MaintenanceInput) -> MaintenanceDecision {
    let localized_cost = input.dirty_regions as f64 * input.localized_cost_per_region.max(0.0);
    let global_cost = input.total_regions as f64 * input.global_cost_per_region.max(0.0);
    let global_extra_value =
        input.global_structural_gain.clamp(0.0, 1.0) * input.gain_value.max(0.0);
    // Both strategies resolve the dirty regions (that benefit cancels). Global's
    // only edge is its structural gain; its only penalty is the extra cost of
    // touching the whole store.
    let global_net_advantage = global_extra_value - (global_cost - localized_cost);

    let (strategy, rationale) = if input.dirty_regions == 0 {
        (
            MaintenanceStrategy::NoOp,
            "No dirty regions — maintenance is unnecessary.".to_string(),
        )
    } else if global_net_advantage > 0.0 {
        (
            MaintenanceStrategy::Global,
            format!(
                "Global reorganization: its store-wide structural gain (value {global_extra_value:.3}) \
                 clears the extra cost of touching all {} regions (net +{global_net_advantage:.3}).",
                input.total_regions
            ),
        )
    } else {
        (
            MaintenanceStrategy::Localized,
            format!(
                "Localized maintenance: patching {} dirty region(s) is more cost-efficient than a \
                 global reorg (global net {global_net_advantage:.3} ≤ 0).",
                input.dirty_regions
            ),
        )
    };

    MaintenanceDecision {
        strategy,
        localized_cost,
        global_cost,
        global_extra_value,
        global_net_advantage,
        rationale,
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    fn base() -> MaintenanceInput {
        MaintenanceInput {
            dirty_regions: 5,
            total_regions: 1000,
            localized_cost_per_region: 1.0,
            global_cost_per_region: 1.0,
            global_structural_gain: 0.1,
            gain_value: 10.0,
        }
    }

    #[test]
    fn no_dirty_is_noop() {
        let d = decide_maintenance(&MaintenanceInput {
            dirty_regions: 0,
            ..base()
        });
        assert_eq!(d.strategy, MaintenanceStrategy::NoOp);
    }

    #[test]
    fn large_store_small_dirty_prefers_localized() {
        // localized = 5, global = 1000, gain value = 0.1*10 = 1. Global net
        // advantage = 1 - (1000 - 5) = -994 ≪ 0 → localized. This is the paper's
        // headline regime.
        let d = decide_maintenance(&base());
        assert_eq!(d.strategy, MaintenanceStrategy::Localized);
        assert_eq!(d.localized_cost, 5.0);
        assert_eq!(d.global_cost, 1000.0);
        assert!(d.global_net_advantage < 0.0);
    }

    #[test]
    fn high_structural_value_justifies_global() {
        // Crank the structural gain's value so it clears the extra global cost.
        let d = decide_maintenance(&MaintenanceInput {
            dirty_regions: 5,
            total_regions: 100,
            localized_cost_per_region: 1.0,
            global_cost_per_region: 1.0,
            global_structural_gain: 1.0,
            gain_value: 1000.0, // extra value 1000 > extra cost (100 - 5) = 95
        });
        assert_eq!(d.strategy, MaintenanceStrategy::Global);
        assert!(d.global_net_advantage > 0.0);
    }

    #[test]
    fn cheap_global_dominates() {
        // If a global reorg is cheaper than localized (tiny store, cheap per
        // region) and brings any gain, global wins outright.
        let d = decide_maintenance(&MaintenanceInput {
            dirty_regions: 10,
            total_regions: 5,
            localized_cost_per_region: 5.0, // localized = 50
            global_cost_per_region: 1.0,    // global = 5
            global_structural_gain: 0.5,
            gain_value: 2.0, // extra value 1; extra cost 5 - 50 = -45 → net +46
        });
        assert_eq!(d.strategy, MaintenanceStrategy::Global);
    }

    #[test]
    fn no_structural_gain_keeps_localized() {
        // With zero structural gain, global can never beat localized when it
        // costs more.
        let d = decide_maintenance(&MaintenanceInput {
            global_structural_gain: 0.0,
            gain_value: 1000.0,
            ..base()
        });
        assert_eq!(d.strategy, MaintenanceStrategy::Localized);
        assert_eq!(d.global_extra_value, 0.0);
    }
}