vtcode-memory 0.181.1

Unified per-session state store: append-only ThreadEvent log plus derived views, retention, and cross-session query.
Documentation
use super::*;

fn sample_ledger() -> ProgressLedger {
    let mut l = ProgressLedger::new("s1", "ship the feature");
    l.set_milestones(vec![
        Milestone {
            id: "1".into(),
            description: "design".into(),
            status: MilestoneStatus::Done,
        },
        Milestone {
            id: "2".into(),
            description: "implement".into(),
            status: MilestoneStatus::InProgress,
        },
        Milestone {
            id: "3".into(),
            description: "verify".into(),
            status: MilestoneStatus::Pending,
        },
    ]);
    l
}

#[test]
fn completion_ratio_reflects_terminal_milestones() {
    let l = sample_ledger();
    assert!((l.completion_ratio() - 1.0 / 3.0).abs() < f32::EPSILON);
    assert!(!l.is_complete());
}

#[test]
fn empty_ledger_is_complete() {
    let l = ProgressLedger::new("s", "goal");
    assert!(l.is_complete());
    assert!((l.completion_ratio() - 1.0).abs() < f32::EPSILON);
}

#[test]
fn advance_clears_stall_and_bumps_confidence() {
    let mut l = sample_ledger();
    l.note_stall();
    assert!(l.is_stalled());
    let before = l.confidence;
    l.note_advance();
    assert!(!l.is_stalled());
    assert!(l.confidence >= before);
}

#[test]
fn persistence_round_trips() {
    let tmp = std::env::temp_dir().join(format!("vtcode-prog-{}", std::process::id()));
    let ws = tmp.join("ws");
    std::fs::create_dir_all(&ws).unwrap();
    let mut l = sample_ledger();
    l.note_stall();
    save_progress(&ws, "s1", &l).unwrap();
    let loaded = load_progress(&ws, "s1").unwrap().expect("ledger present");
    assert_eq!(loaded, l);
    assert!(loaded.is_stalled());
    assert!(load_progress(&ws, "absent").unwrap().is_none());
    let _ = std::fs::remove_dir_all(&tmp);
}

#[test]
fn handoff_metadata_defaults_to_none() {
    let l = ProgressLedger::new("s1", "goal");
    assert!(l.previous_session_id.is_none());
    assert!(l.handoff_summary.is_none());
    assert!(l.known_issues.is_empty());
    assert!(l.git_checkpoint.is_none());
}

#[test]
fn set_handoff_records_metadata() {
    let mut l = ProgressLedger::new("s2", "goal");
    l.set_handoff("s1", "implemented login", Some("abc123".to_string()));
    assert_eq!(l.previous_session_id.as_deref(), Some("s1"));
    assert_eq!(l.handoff_summary.as_deref(), Some("implemented login"));
    assert_eq!(l.git_checkpoint.as_deref(), Some("abc123"));
}

#[test]
fn add_known_issue_accumulates() {
    let mut l = ProgressLedger::new("s3", "goal");
    l.add_known_issue("rate limiting missing");
    l.add_known_issue("no error handling for timeouts");
    assert_eq!(l.known_issues.len(), 2);
    assert_eq!(l.known_issues[0], "rate limiting missing");
}

#[test]
fn handoff_metadata_survives_persistence() {
    let tmp = std::env::temp_dir().join(format!("vtcode-prog-{}", std::process::id()));
    let ws = tmp.join("ws");
    std::fs::create_dir_all(&ws).unwrap();

    let mut l = sample_ledger();
    l.set_handoff("prev-session", "built auth", Some("def456".to_string()));
    l.add_known_issue("tests are flaky");

    save_progress(&ws, "s4", &l).unwrap();
    let loaded = load_progress(&ws, "s4").unwrap().expect("present");
    assert_eq!(loaded.previous_session_id.as_deref(), Some("prev-session"));
    assert_eq!(loaded.handoff_summary.as_deref(), Some("built auth"));
    assert_eq!(loaded.git_checkpoint.as_deref(), Some("def456"));
    assert_eq!(loaded.known_issues, vec!["tests are flaky"]);

    let _ = std::fs::remove_dir_all(&tmp);
}

#[test]
fn to_markdown_includes_handoff_metadata() {
    let mut l = ProgressLedger::new("s5", "build feature");
    l.set_handoff("s4", "implemented core", Some("abc123".to_string()));
    l.add_known_issue("missing error handling");

    let md = l.to_markdown();
    assert!(md.contains("Handed off from:** s4"));
    assert!(md.contains("Handoff summary:** implemented core"));
    assert!(md.contains("Git checkpoint:** `abc123`"));
    assert!(md.contains("- missing error handling"));
}

#[test]
fn to_markdown_omits_handoff_when_absent() {
    let l = ProgressLedger::new("s6", "goal");
    let md = l.to_markdown();
    assert!(!md.contains("Handed off from"));
    assert!(!md.contains("Handoff summary"));
    assert!(!md.contains("Git checkpoint"));
    assert!(!md.contains("Known Issues"));
}