remem-ai 0.6.13

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

use super::*;

#[tokio::test]
async fn memory_candidate_assigns_ttl_to_current_operational_state() -> Result<()> {
    let mut conn = setup_conn();
    let task = setup_task(&mut conn, "sess-candidate-ttl")?;
    let text = "Local dev server is currently running at localhost:3000 for remem.";
    insert_source_observation(&conn, &task, text)?;
    let before = chrono::Utc::now().timestamp();

    let result = process_with_generator(&mut conn, &task, |_prompt| async {
        Ok(format!(
            "<memory_candidate>\
                <scope>project</scope>\
                <type>decision</type>\
                <topic_key>repo:/tmp/remem:dev-server</topic_key>\
                <risk_class>low</risk_class>\
                <confidence>0.92</confidence>\
                <text>{text}</text>\
             </memory_candidate>"
        ))
    })
    .await?;

    assert_eq!(
        result,
        MemoryCandidateResult::Written {
            candidates: 1,
            promoted: 1,
            pending_review: 0,
            to_event_id: task.high_watermark_event_id.expect("task watermark")
        }
    );
    let (candidate_expires, memory_expires, memory_valid_from): (i64, i64, i64) = conn.query_row(
        "SELECT c.expires_at_epoch, m.expires_at_epoch, m.valid_from_epoch
         FROM memory_candidates c
         JOIN memories m ON m.source_candidate_id = c.id",
        [],
        |row| Ok((row.get(0)?, row.get(1)?, row.get(2)?)),
    )?;
    let min_expected = before + crate::memory::lifecycle::SHORT_CURRENT_TTL_SECONDS;
    let max_expected =
        chrono::Utc::now().timestamp() + crate::memory::lifecycle::SHORT_CURRENT_TTL_SECONDS;
    assert!((min_expected..=max_expected).contains(&candidate_expires));
    assert!((min_expected..=max_expected).contains(&memory_expires));
    assert!(memory_valid_from >= before);
    Ok(())
}

#[tokio::test]
async fn memory_candidate_refreshes_legacy_current_state_without_ttl() -> Result<()> {
    let mut conn = setup_conn();
    let task = setup_task(&mut conn, "sess-candidate-refresh-ttl")?;
    let text = "Local dev server is currently running at localhost:3000 for remem.";
    let topic_key = "repo-tmp-remem-dev-server";
    insert_source_observation(&conn, &task, text)?;
    let old_epoch = chrono::Utc::now().timestamp() - 3600;

    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,
          source_project, target_project, owner_scope, owner_key, context_class)
         VALUES (?1, 'project', 'decision', ?2, ?3, '[]',
                 0.92, 'low', 'auto_promoted', ?4, ?4,
                 ?5, ?5, 'repo', ?5, 'startup_core')",
        params![task.project_id, topic_key, text, old_epoch, task.project],
    )?;
    conn.execute(
        "INSERT INTO memories
         (session_id, project, topic_key, title, content, memory_type,
          created_at_epoch, updated_at_epoch, status, scope,
          source_project, target_project, owner_scope, owner_key, context_class)
         VALUES ('legacy-current', ?1, ?2, 'Dev server', ?3, 'decision',
                 ?4, ?4, 'active', 'project',
                 ?1, ?1, 'repo', ?1, 'startup_core')",
        params![task.project, topic_key, text, old_epoch],
    )?;

    let result = process_with_generator(&mut conn, &task, |_prompt| async {
        Ok(format!(
            "<memory_candidate>\
                <scope>project</scope>\
                <type>decision</type>\
                <topic_key>{topic_key}</topic_key>\
                <risk_class>low</risk_class>\
                <confidence>0.92</confidence>\
                <text>{text}</text>\
             </memory_candidate>"
        ))
    })
    .await?;

    assert_eq!(
        result,
        MemoryCandidateResult::Written {
            candidates: 1,
            promoted: 1,
            pending_review: 0,
            to_event_id: task.high_watermark_event_id.expect("task watermark")
        }
    );
    let rows = conn
        .prepare(
            "SELECT status, expires_at_epoch
             FROM memories
             WHERE topic_key = ?1
             ORDER BY id ASC",
        )?
        .query_map([topic_key], |row| {
            Ok((row.get::<_, String>(0)?, row.get::<_, Option<i64>>(1)?))
        })?
        .collect::<std::result::Result<Vec<_>, _>>()?;
    assert_eq!(rows.len(), 2);
    assert_eq!(rows[0].0, "stale");
    assert_eq!(rows[0].1, None);
    assert_eq!(rows[1].0, "active");
    assert!(rows[1].1.is_some());
    Ok(())
}