car-ffi-common 0.32.1

Shared logic for FFI bindings (NAPI, PyO3) — JSON wrappers for verify, multi-agent, scheduler
//! JSON wrapper for the agent-native memory diagnostic (arXiv 2606.24775).
//!
//! Stateless helper, binding-only — see
//! `docs/proposals/agent-native-memory-diagnostic.md`. Scores a memory system
//! along the paper's four dimensions (representation fidelity, retrieval
//! precision, update correctness, long-horizon stability) and names its
//! bottleneck module, from aggregate counters the caller folds off the graph.

use car_memgine::memsys::{diagnose, MemoryStats};

/// Diagnose a memory system. `stats_json` is a [`MemoryStats`] as JSON
/// (`{ total_facts, structured_facts, total_edges, total_retrievals,
/// helpful_retrievals, conflicts_resolved, outstanding_outdated, facts_created,
/// facts_superseded }`; any field omitted defaults to 0). Returns the
/// [`car_memgine::memsys::MemorySystemReport`] as JSON — the four `[0,1]`
/// dimension scores, an `overall` mean, the `bottleneck` (lowest-scoring module
/// with evidence), a `recommendation`, and whether the store was `evaluated`.
pub fn report(stats_json: &str) -> Result<String, String> {
    let stats: MemoryStats = crate::from_json("stats", stats_json)?;
    let report = diagnose(&stats);
    crate::to_json(&report)
}

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

    #[test]
    fn retrieval_bottleneck_round_trips() {
        let stats = r#"{
            "total_facts": 100, "structured_facts": 90, "total_edges": 300,
            "total_retrievals": 100, "helpful_retrievals": 20,
            "conflicts_resolved": 50, "outstanding_outdated": 1,
            "facts_created": 100, "facts_superseded": 3
        }"#;
        let out = report(stats).unwrap();
        let v: Value = serde_json::from_str(&out).unwrap();
        assert_eq!(v["bottleneck"], "retrieval");
        assert_eq!(v["evaluated"], true);
    }

    #[test]
    fn empty_stats_default_to_cold() {
        let out = report("{}").unwrap();
        let v: Value = serde_json::from_str(&out).unwrap();
        assert_eq!(v["bottleneck"], "none");
        assert_eq!(v["evaluated"], false);
    }

    #[test]
    fn invalid_json_errors() {
        assert!(report("not json").is_err());
    }
}