lean-ctx-engine 3.12.2

LeanCTX | OSS library: the open-source runtime for the Context Gateway for AI agents behind the lean-ctx CLI, MCP server and proxy integrations.
Documentation
// SPDX-License-Identifier: Apache-2.0
use super::*;
use crate::core::knowledge::FactOrigin;
use crate::core::memory_policy::MemoryPolicy;

fn setup() -> (
    crate::core::data_dir::IsolatedDataDir,
    tempfile::TempDir,
    ProjectKnowledge,
) {
    let data = crate::core::data_dir::isolated_data_dir();
    let root = tempfile::tempdir().unwrap();
    std::fs::create_dir(root.path().join(".lean-ctx")).unwrap();
    std::fs::write(
        root.path().join(".lean-ctx/policy.toml"),
        "name='origin'\nversion='1.0.0'\ndescription='fixture'\n",
    )
    .unwrap();
    let mut knowledge = ProjectKnowledge::new(root.path().to_str().unwrap());
    knowledge.remember_with_origin(
        "finding",
        "remote",
        "withheld issue title",
        "provider-ingest-old",
        0.9,
        &MemoryPolicy::default(),
        FactOrigin::Unverified,
    );
    (data, root, knowledge)
}

#[test]
fn source_view_keeps_hidden_records_on_unrelated_atomic_write() {
    let (_data, root, knowledge) = setup();
    knowledge.save().unwrap();
    let view = ProjectKnowledge::load(root.path().to_str().unwrap()).unwrap();
    assert!(view.facts.is_empty());
    assert!(
        !serde_json::to_string(&view)
            .unwrap()
            .contains("withheld issue title")
    );
    ProjectKnowledge::mutate_locked(root.path().to_str().unwrap(), |current| {
        current.remember(
            "decision",
            "local",
            "use a bounded parser",
            "local-session",
            0.9,
            &MemoryPolicy::default(),
        );
    })
    .unwrap();
    let raw = ProjectKnowledge::load_for_checked_capture(root.path().to_str().unwrap()).unwrap();
    assert_eq!(raw.facts.len(), 2);
    assert!(
        raw.facts
            .iter()
            .any(|f| same_record(f, &knowledge.facts[0]))
    );
    let view = ProjectKnowledge::load(root.path().to_str().unwrap()).unwrap();
    assert_eq!(view.facts.len(), 1);
    assert_eq!(view.facts[0].key, "local");
}

#[test]
fn stale_view_cannot_resurrect_an_operator_removed_hidden_record() {
    let (_data, root, knowledge) = setup();
    knowledge.save().unwrap();
    let view = ProjectKnowledge::load(root.path().to_str().unwrap()).unwrap();
    let mut raw =
        ProjectKnowledge::load_for_checked_capture(root.path().to_str().unwrap()).unwrap();
    raw.facts.clear();
    raw.save().unwrap();
    assert!(view.save().unwrap_err().contains("stale"));
    assert!(
        ProjectKnowledge::load_for_checked_capture(root.path().to_str().unwrap())
            .unwrap()
            .facts
            .is_empty()
    );
}

#[test]
fn authored_note_does_not_adopt_unknown_fact_on_confirmation() {
    let (_data, _root, mut knowledge) = setup();
    knowledge.remember(
        "finding",
        "remote",
        "withheld issue title",
        "local-session",
        0.9,
        &MemoryPolicy::default(),
    );
    assert_eq!(knowledge.facts.len(), 2);
    assert_eq!(knowledge.facts[0].origin, FactOrigin::Unverified);
    assert_eq!(knowledge.facts[0].source_session, "provider-ingest-old");
    assert_eq!(knowledge.facts[1].origin, FactOrigin::Local);
}

#[test]
fn hidden_counter_updates_survive_a_stale_view_save() {
    let (_data, root, knowledge) = setup();
    knowledge.save().unwrap();
    let view = ProjectKnowledge::load(root.path().to_str().unwrap()).unwrap();
    let mut latest =
        ProjectKnowledge::load_for_checked_capture(root.path().to_str().unwrap()).unwrap();
    latest.facts[0].confirmation_count += 1;
    latest.facts[0].valid_until = Some(chrono::Utc::now());
    latest.save().unwrap();
    view.save().unwrap();
    let saved = ProjectKnowledge::load_for_checked_capture(root.path().to_str().unwrap()).unwrap();
    assert_eq!(
        serde_json::to_value(&saved.facts).unwrap(),
        serde_json::to_value(&latest.facts).unwrap()
    );
}

#[test]
fn hidden_content_changes_require_reload_without_overwriting() {
    let (_data, root, knowledge) = setup();
    knowledge.save().unwrap();
    let view = ProjectKnowledge::load(root.path().to_str().unwrap()).unwrap();
    let mut latest =
        ProjectKnowledge::load_for_checked_capture(root.path().to_str().unwrap()).unwrap();
    latest.facts[0].value = "updated remote finding".into();
    latest.save().unwrap();
    assert!(view.save().unwrap_err().contains("stale"));
    let saved = ProjectKnowledge::load_for_checked_capture(root.path().to_str().unwrap()).unwrap();
    assert_eq!(saved.facts[0].value, "updated remote finding");
}

#[test]
fn derived_origin_is_not_downgraded_when_policy_is_absent() {
    let (_data, root, mut knowledge) = setup();
    std::fs::remove_file(root.path().join(".lean-ctx/policy.toml")).unwrap();
    knowledge.facts[0].origin = FactOrigin::Derived(vec![FactOrigin::Unverified]);
    let view = knowledge.admit_sources();
    assert!(view.facts.is_empty());
    assert_eq!(view.withheld.len(), 1);
}

#[test]
fn missing_or_imported_local_origin_is_not_a_source_authorization() {
    let (_data, _root, knowledge) = setup();
    let mut value = serde_json::to_value(&knowledge.facts[0]).unwrap();
    value.as_object_mut().unwrap().remove("origin");
    let old: KnowledgeFact = serde_json::from_value(value).unwrap();
    assert_eq!(old.origin, FactOrigin::Unverified);
    assert_eq!(imported_origin(&FactOrigin::Local), FactOrigin::Unverified);
}

#[test]
fn source_view_cannot_admit_foreign_local_notes_in_a_bound_protected_request() {
    let (_data, root, _) = setup();
    let foreign = tempfile::tempdir().unwrap();
    let mut knowledge = ProjectKnowledge::new(foreign.path().to_str().unwrap());
    knowledge.remember(
        "decision",
        "foreign",
        "private other project note",
        "local",
        0.9,
        &MemoryPolicy::default(),
    );
    knowledge
        .patterns
        .push(crate::core::knowledge::ProjectPattern {
            pattern_type: "workflow".into(),
            description: "private foreign pattern".into(),
            examples: vec!["example".into()],
            source_session: "local".into(),
            created_at: chrono::Utc::now(),
        });
    knowledge
        .history
        .push(crate::core::knowledge::ConsolidatedInsight {
            summary: "private foreign history".into(),
            from_sessions: vec!["session".into()],
            timestamp: chrono::Utc::now(),
        });
    knowledge
        .judged_pairs
        .push(crate::core::knowledge::JudgedPair {
            key_a: "private-a".into(),
            key_b: "private-b".into(),
            verdict: "private verdict".into(),
            judged_at: chrono::Utc::now(),
        });
    crate::core::policy::runtime::REQUEST_PROJECT.sync_scope(
        std::cell::RefCell::new(Some(root.path().to_owned())),
        || {
            let view = knowledge.admit_sources();
            assert!(view.facts.is_empty());
            assert!(view.patterns.is_empty());
            assert!(view.history.is_empty());
            assert!(view.judged_pairs.is_empty());
            assert_eq!(view.withheld.len(), 4);
            let debug = format!("{view:?}");
            for hidden in [
                "private other project note",
                "private foreign pattern",
                "private foreign history",
                "private verdict",
                "private-a",
                "private-b",
            ] {
                assert!(
                    !debug.contains(hidden),
                    "withheld record reached Debug output"
                );
                assert!(!format!("{:?}", view.withheld).contains(hidden));
            }
        },
    );
}