remem-ai 0.6.11

Local-first coding agent memory for Claude Code and OpenAI Codex
Documentation
use anyhow::Result;
use rusqlite::{params, Connection};

use super::*;
use crate::memory::graph_contract::{
    insert_graph_edge, GraphEdgeInput, GraphEdgeProvenance, GraphEdgeType, GraphNodeRef,
};

struct Fixture {
    conn: Connection,
    event_id: i64,
    candidate_id: i64,
    operation_id: i64,
}

fn fixture() -> Result<Fixture> {
    let conn = Connection::open_in_memory()?;
    crate::migrate::run_migrations(&conn)?;
    let now = 1_700_000_000_i64;
    let host_id: i64 =
        conn.query_row("SELECT id FROM hosts WHERE name = 'codex-cli'", [], |row| {
            row.get(0)
        })?;
    conn.execute(
        "INSERT INTO workspaces(root_path, git_remote, git_branch, created_at_epoch, updated_at_epoch)
         VALUES ('/tmp/gh853', 'origin', 'main', ?1, ?1)",
        [now],
    )?;
    let workspace_id = conn.last_insert_rowid();
    conn.execute(
        "INSERT INTO projects(workspace_id, project_path, project_key, created_at_epoch, updated_at_epoch)
         VALUES (?1, '/tmp/gh853', 'gh853', ?2, ?2)",
        params![workspace_id, now],
    )?;
    let project_id = conn.last_insert_rowid();
    conn.execute(
        "INSERT INTO sessions(host_id, workspace_id, project_id, session_id, started_at_epoch,
                              last_seen_at_epoch, status)
         VALUES (?1, ?2, ?3, 'gh853-session', ?4, ?4, 'active')",
        params![host_id, workspace_id, project_id, now],
    )?;
    let session_row_id = conn.last_insert_rowid();
    conn.execute(
        "INSERT INTO captured_events(host_id, workspace_id, project_id, session_row_id,
                                     session_id, event_id, event_type, content_hash,
                                     retention_class, created_at_epoch, inserted_at_epoch)
         VALUES (?1, ?2, ?3, ?4, 'gh853-session', 'gh853-event', 'message',
                 'gh853-hash', 'default', ?5, ?5)",
        params![host_id, workspace_id, project_id, session_row_id, now],
    )?;
    let event_id = conn.last_insert_rowid();
    conn.execute(
        "INSERT INTO memory_candidates(project_id, scope, memory_type, topic_key, text,
                                       evidence_event_ids, confidence, risk_class,
                                       review_status, created_at_epoch, updated_at_epoch)
         VALUES (?1, 'project', 'decision', 'gh853', 'graph fixture',
                 ?2, 0.9, 'low', 'accepted', ?3, ?3)",
        params![project_id, format!("[{event_id}]"), now],
    )?;
    let candidate_id = conn.last_insert_rowid();
    conn.execute(
        "INSERT INTO memory_operation_log(operation, planner_version, actor, source,
                                         owner_scope, owner_key, memory_type, state_key,
                                         source_candidate_id, superseded_ids, conflicting_ids,
                                         confidence, reason, created_at_epoch)
         VALUES ('add', 'gh853-test', 'test', 'memory_candidate', 'project', '/repo',
                 'decision', 'gh853', ?1, '[]', '[]', 0.9, 'graph fixture', ?2)",
        params![candidate_id, now],
    )?;
    let operation_id = conn.last_insert_rowid();
    Ok(Fixture {
        conn,
        event_id,
        candidate_id,
        operation_id,
    })
}

fn add_memory(fixture: &Fixture, project: &str, topic: &str, status: &str) -> Result<i64> {
    let id = crate::memory::insert_memory_full(
        &fixture.conn,
        Some("gh853-session"),
        project,
        Some(topic),
        topic,
        &format!("content for {topic}"),
        "decision",
        None,
        Some("main"),
        "project",
        Some(1_700_000_000),
    )?;
    if status != "active" {
        fixture.conn.execute(
            "UPDATE memories SET status = ?1 WHERE id = ?2",
            params![status, id],
        )?;
    }
    Ok(id)
}

fn provenance(fixture: &Fixture) -> GraphEdgeProvenance<'_> {
    GraphEdgeProvenance {
        source_event_ids: std::slice::from_ref(&fixture.event_id),
        source_candidate_id: Some(fixture.candidate_id),
        source_operation_id: Some(fixture.operation_id),
        confidence: Some(0.9),
        reason: Some("gh853 traversal test"),
    }
}

fn request(seed_ids: &[i64]) -> GraphTraversalRequest<'_> {
    GraphTraversalRequest {
        seed_memory_ids: seed_ids,
        project: Some("/repo"),
        memory_type: Some("decision"),
        branch: Some("main"),
        include_inactive: false,
        reference_time_epoch: 1_700_000_100,
        limits: GraphTraversalLimits::default(),
    }
}

#[test]
fn reports_missing_empty_and_no_seed_states() -> Result<()> {
    let bare = Connection::open_in_memory()?;
    assert_eq!(
        traverse_trusted_graph(&bare, request(&[1]))?.status,
        GraphTraversalStatus::MissingTable
    );
    let fixture = fixture()?;
    assert_eq!(
        traverse_trusted_graph(&fixture.conn, request(&[]))?.status,
        GraphTraversalStatus::NoSeed
    );
    assert_eq!(
        traverse_trusted_graph(&fixture.conn, request(&[1]))?.status,
        GraphTraversalStatus::EmptyGraph
    );
    Ok(())
}

#[test]
fn trusted_mentions_two_hop_filters_scope_and_hints() -> Result<()> {
    let fixture = fixture()?;
    let seed = add_memory(&fixture, "/repo", "seed", "active")?;
    let target = add_memory(&fixture, "/repo", "target", "active")?;
    let other_project = add_memory(&fixture, "/other", "private", "active")?;
    fixture.conn.execute(
        "INSERT INTO entities(canonical_name, entity_type, mention_count, created_at_epoch)
         VALUES ('GH853 bridge', 'concept', 1, 1700000000)",
        [],
    )?;
    let entity = fixture.conn.last_insert_rowid();
    for memory_id in [seed, target, other_project] {
        insert_graph_edge(
            &fixture.conn,
            &GraphEdgeInput {
                edge_type: GraphEdgeType::Mentions,
                from_node: GraphNodeRef::memory(memory_id)?,
                to_node: GraphNodeRef::entity(entity)?,
                provenance: provenance(&fixture),
                valid_from_epoch: Some(1_699_999_999),
                valid_to_epoch: None,
            },
        )?;
    }
    insert_graph_edge(
        &fixture.conn,
        &GraphEdgeInput {
            edge_type: GraphEdgeType::SimilarTo,
            from_node: GraphNodeRef::memory(seed)?,
            to_node: GraphNodeRef::memory(other_project)?,
            provenance: GraphEdgeProvenance::default(),
            valid_from_epoch: None,
            valid_to_epoch: None,
        },
    )?;

    let outcome = traverse_trusted_graph(&fixture.conn, request(&[seed]))?;
    assert_eq!(outcome.status, GraphTraversalStatus::Ready);
    assert_eq!(
        outcome
            .hits
            .iter()
            .map(|hit| hit.memory_id)
            .collect::<Vec<_>>(),
        vec![target]
    );
    assert_eq!(outcome.hits[0].hop_count, 2);
    assert_eq!(outcome.hits[0].path_kind, GraphPathKind::Mentions);
    assert_eq!(outcome.diagnostics.diagnostic_hint_edges, 1);
    assert!(outcome.diagnostics.targets_filtered >= 1);
    Ok(())
}

#[test]
fn supersedes_only_walks_from_old_seed_to_current_memory() -> Result<()> {
    let fixture = fixture()?;
    let current = add_memory(&fixture, "/repo", "current", "active")?;
    let old = add_memory(&fixture, "/repo", "old", "stale")?;
    insert_graph_edge(
        &fixture.conn,
        &GraphEdgeInput {
            edge_type: GraphEdgeType::Supersedes,
            from_node: GraphNodeRef::memory(current)?,
            to_node: GraphNodeRef::memory(old)?,
            provenance: provenance(&fixture),
            valid_from_epoch: None,
            valid_to_epoch: None,
        },
    )?;

    let from_old = traverse_trusted_graph(&fixture.conn, request(&[old]))?;
    assert_eq!(from_old.hits[0].memory_id, current);
    assert_eq!(from_old.hits[0].hop_count, 1);
    assert_eq!(from_old.hits[0].path_kind, GraphPathKind::Supersedes);
    let from_current = traverse_trusted_graph(&fixture.conn, request(&[current]))?;
    assert_eq!(from_current.status, GraphTraversalStatus::NoExpansion);
    Ok(())
}

#[test]
fn degree_cap_fails_closed_without_partial_hits() -> Result<()> {
    let fixture = fixture()?;
    let seed = add_memory(&fixture, "/repo", "seed", "active")?;
    for index in 0..2 {
        let target = add_memory(&fixture, "/repo", &format!("target-{index}"), "active")?;
        insert_graph_edge(
            &fixture.conn,
            &GraphEdgeInput {
                edge_type: GraphEdgeType::Supersedes,
                from_node: GraphNodeRef::memory(target)?,
                to_node: GraphNodeRef::memory(seed)?,
                provenance: provenance(&fixture),
                valid_from_epoch: None,
                valid_to_epoch: None,
            },
        )?;
    }
    let seeds = [seed];
    let mut bounded = request(&seeds);
    bounded.limits.max_degree_per_node = 1;
    let error = traverse_trusted_graph(&fixture.conn, bounded).unwrap_err();
    assert!(error.to_string().contains("degree cap exceeded"));
    Ok(())
}

#[test]
fn expired_edges_are_not_traversed() -> Result<()> {
    let fixture = fixture()?;
    let current = add_memory(&fixture, "/repo", "current", "active")?;
    let old = add_memory(&fixture, "/repo", "old", "stale")?;
    insert_graph_edge(
        &fixture.conn,
        &GraphEdgeInput {
            edge_type: GraphEdgeType::Supersedes,
            from_node: GraphNodeRef::memory(current)?,
            to_node: GraphNodeRef::memory(old)?,
            provenance: provenance(&fixture),
            valid_from_epoch: None,
            valid_to_epoch: Some(1_700_000_050),
        },
    )?;

    let outcome = traverse_trusted_graph(&fixture.conn, request(&[old]))?;
    assert_eq!(outcome.status, GraphTraversalStatus::NoExpansion);
    assert!(outcome.hits.is_empty());
    Ok(())
}

#[test]
fn duplicate_paths_keep_the_deterministic_highest_confidence_hit() -> Result<()> {
    let fixture = fixture()?;
    let seed = add_memory(&fixture, "/repo", "seed", "active")?;
    let target = add_memory(&fixture, "/repo", "target", "active")?;
    for (index, confidence) in [0.55, 0.85].into_iter().enumerate() {
        fixture.conn.execute(
            "INSERT INTO entities(canonical_name, entity_type, mention_count, created_at_epoch)
             VALUES (?1, 'concept', 1, 1700000000)",
            [format!("GH853 bridge {index}")],
        )?;
        let entity = fixture.conn.last_insert_rowid();
        for memory_id in [seed, target] {
            let mut edge_provenance = provenance(&fixture);
            edge_provenance.confidence = Some(confidence);
            insert_graph_edge(
                &fixture.conn,
                &GraphEdgeInput {
                    edge_type: GraphEdgeType::Mentions,
                    from_node: GraphNodeRef::memory(memory_id)?,
                    to_node: GraphNodeRef::entity(entity)?,
                    provenance: edge_provenance,
                    valid_from_epoch: None,
                    valid_to_epoch: None,
                },
            )?;
        }
    }

    let outcome = traverse_trusted_graph(&fixture.conn, request(&[seed]))?;
    assert_eq!(outcome.status, GraphTraversalStatus::Ready);
    assert_eq!(outcome.hits.len(), 1);
    assert_eq!(outcome.hits[0].memory_id, target);
    assert_eq!(outcome.hits[0].path_kind, GraphPathKind::Mentions);
    assert_eq!(outcome.hits[0].min_confidence, 0.85);
    Ok(())
}