remem-ai 0.6.90

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

use super::*;
use crate::db::{record_captured_event, CaptureEventInput, ExtractionTaskKind};

#[test]
fn global_candidate_replacement_reuses_the_existing_cross_project_route() -> Result<()> {
    let conn = Connection::open_in_memory()?;
    crate::migrate::run_migrations(&conn)?;
    conn.execute(
        "INSERT INTO memories
         (project, topic_key, title, content, memory_type, created_at_epoch,
          updated_at_epoch, status, scope, source_project, owner_scope, owner_key,
          context_class, source_trust_class)
         VALUES ('project-a', 'shared-global-candidate', 'Old global decision',
                 'Old shared value.', 'decision', 1, 1, 'active', 'global',
                 'project-a', 'user', 'user:default', 'startup_core', 'user_prompt')",
        [],
    )?;
    let old_id = conn.last_insert_rowid();
    let captured = record_captured_event(
        &conn,
        &CaptureEventInput {
            host: "codex-cli",
            session_id: "global-route",
            project: "project-b",
            cwd: None,
            event_type: "user_prompt",
            role: Some("user"),
            tool_name: None,
            content: "Use the new shared global value.",
            task_kind: Some(ExtractionTaskKind::MemoryCandidate),
        },
    )?;
    let evidence_json = serde_json::to_string(&vec![captured.event_row_id])?;
    conn.execute(
        "INSERT INTO memory_candidates
         (id, scope, memory_type, topic_key, text, evidence_event_ids, confidence,
          risk_class, review_status, created_at_epoch, updated_at_epoch)
         VALUES (701, 'global', 'decision', 'shared-global-candidate',
                 'Use the new shared global value.', ?1, 0.95, 'low',
                 'approved', 2, 2)",
        [evidence_json.as_str()],
    )?;
    let candidate = ParsedMemoryCandidate {
        scope: "global".to_string(),
        memory_type: "decision".to_string(),
        topic_key: "shared-global-candidate".to_string(),
        title_override: Some("New global decision".to_string()),
        text: "Use the new shared global value.".to_string(),
        confidence: 0.95,
        risk_class: "low".to_string(),
        outcome: None,
        facts: Vec::new(),
    };
    let route = CandidateRoute {
        owner_scope: "user".to_string(),
        owner_key: "user:default".to_string(),
        target_project: None,
        topic_domain: Some("user-preference".to_string()),
        routing_confidence: 0.95,
        routing_reason: "global candidate route".to_string(),
        context_class: "startup_core".to_string(),
    };

    let outcome = promote_candidate_to_memory_with_route(
        &conn,
        Some("global-route"),
        "project-b",
        701,
        &candidate,
        &evidence_json,
        &route,
        SourceTrustClass::UserPrompt,
    )?;
    let new_id = outcome
        .memory_id
        .context("global candidate was not promoted")?;
    assert_ne!(new_id, old_id);
    assert_eq!(outcome.superseded_ids, vec![old_id]);
    assert_eq!(
        conn.query_row(
            "SELECT project, source_project FROM memories WHERE id = ?1",
            [new_id],
            |row| Ok((row.get::<_, String>(0)?, row.get::<_, String>(1)?)),
        )?,
        ("project-a".to_string(), "project-b".to_string())
    );
    assert_eq!(
        conn.query_row(
            "SELECT COUNT(*) FROM memories
             WHERE topic_key = 'shared-global-candidate' AND status = 'active'",
            [],
            |row| row.get::<_, i64>(0),
        )?,
        1
    );
    Ok(())
}