remem-ai 0.6.93

Local-first coding agent memory for Claude Code and OpenAI Codex
Documentation
use super::*;
use anyhow::Context;

fn verified_candidates(conn: &Connection) -> Result<(ProcedureCandidate, ProcedureCandidate)> {
    let now = chrono::Utc::now().timestamp();
    let command = "cargo test";
    let workflow_key = crate::memory::slugify_for_topic(command, 64);
    let files = vec!["src/lib.rs".to_string()];
    let files_json = serde_json::to_string(&files)?;
    let mut traces = Vec::new();
    for seq in 1_i64..=3 {
        let captured = crate::db::record_captured_event(
            conn,
            &crate::db::CaptureEventInput {
                host: "codex-cli",
                session_id: "procedure-activation-receipt",
                project: "/tmp/remem",
                cwd: None,
                event_type: "tool_result",
                role: None,
                tool_name: Some("Bash"),
                content: &serde_json::json!({
                    "seq": seq,
                    "event_type": "bash",
                    "exit_code": 0,
                    "tool_input": { "command": command },
                    "files": files,
                    "git_branch": "main"
                })
                .to_string(),
                task_kind: None,
            },
        )?;
        let (host_id, project_id, session_row_id, verified_at_epoch): (i64, i64, i64, i64) = conn
            .query_row(
            "SELECT host_id, project_id, session_row_id, created_at_epoch
             FROM captured_events WHERE id = ?1",
            [captured.event_row_id],
            |row| Ok((row.get(0)?, row.get(1)?, row.get(2)?, row.get(3)?)),
        )?;
        conn.execute(
            "INSERT INTO procedure_verifications
             (host_id, project_id, session_row_id, branch, workflow_key, command,
              files_touched, source_event_id, verified_at_epoch,
              created_at_epoch, updated_at_epoch)
             VALUES (?1, ?2, ?3, 'main', ?4, ?5, ?6, ?7, ?8, ?9, ?9)",
            params![
                host_id,
                project_id,
                session_row_id,
                workflow_key,
                command,
                files_json,
                captured.event_row_id,
                verified_at_epoch,
                now
            ],
        )?;
        traces.push(ProcedureTrace {
            project: "/tmp/remem".to_string(),
            branch: Some("main".to_string()),
            workflow_key: workflow_key.clone(),
            command: command.to_string(),
            files_touched: files.clone(),
            succeeded: true,
            verified_at_epoch,
            source_event_id: Some(captured.event_row_id),
        });
    }
    let initial =
        build_procedure_candidate(&traces[..2], now, &ProcedurePromotionPolicy::default())
            .context("two verified traces should build a procedure candidate")?;
    let expanded = build_procedure_candidate(&traces, now, &ProcedurePromotionPolicy::default())
        .context("three verified traces should build an expanded procedure candidate")?;
    Ok((initial, expanded))
}

#[test]
fn procedure_promotion_binds_verified_evidence_before_activation_receipt() -> Result<()> {
    let conn = Connection::open_in_memory()?;
    conn.execute_batch("PRAGMA journal_mode=WAL; PRAGMA foreign_keys=ON;")?;
    crate::migrate::run_migrations(&conn)?;
    let (candidate, expanded) = verified_candidates(&conn)?;

    let mut forged = candidate.clone();
    forged.source_event_ids.push(i64::MAX);
    let error = promote_procedure_memory(&conn, &forged)
        .expect_err("unverified procedure evidence must fail before activation");
    assert!(
        error.to_string().contains("verified evidence")
            || error.to_string().contains("evidence ids")
    );
    assert_eq!(
        conn.query_row("SELECT COUNT(*) FROM memories", [], |row| row
            .get::<_, i64>(0))?,
        0
    );
    assert_eq!(
        conn.query_row(
            "SELECT COUNT(*) FROM memory_activation_requests",
            [],
            |row| { row.get::<_, i64>(0) }
        )?,
        0
    );

    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 (901, 'project', 'procedure', ?1, ?2, '[]', 0.8,
                 'low', 'approved', 1, 1)",
        params![candidate.topic_key, candidate.content],
    )?;
    conn.execute(
        "INSERT INTO memories
         (project, topic_key, title, content, memory_type, files,
          created_at_epoch, updated_at_epoch, reference_time_epoch, status, branch, scope,
          source_project, target_project, owner_scope, owner_key, context_class,
          source_trust_class, source_candidate_id)
         VALUES (?1, ?2, ?3, ?4, 'procedure', ?5, 1, 1, 1, 'active', 'main', 'project',
                 ?1, ?1, 'repo', ?1, 'startup_core', 'external_content', 901)",
        params![
            candidate.project,
            candidate.topic_key,
            candidate.title,
            candidate.content,
            serde_json::to_string(&candidate.files)?
        ],
    )?;

    let memory_id = promote_procedure_memory(&conn, &candidate)?;
    let replayed_id = promote_procedure_memory(&conn, &candidate)?;
    assert_eq!(replayed_id, memory_id);
    let (route_kind, result_sha256, receipt_count): (String, String, i64) = conn.query_row(
        "SELECT route_kind, result_sha256,
                (SELECT COUNT(*) FROM memory_activation_requests
                 WHERE result_memory_id = ?1)
         FROM memory_activation_requests
         WHERE result_memory_id = ?1",
        [memory_id],
        |row| Ok((row.get(0)?, row.get(1)?, row.get(2)?)),
    )?;
    assert_eq!(route_kind, "candidate_promotion");
    assert_eq!(receipt_count, 1);
    let actual = crate::memory::activation::ExpectedActiveMemory::from_existing(&conn, memory_id)?;
    assert_eq!(actual.source_candidate_id, Some(901));
    assert_eq!(
        serde_json::from_str::<Vec<i64>>(
            actual
                .evidence_event_ids
                .as_deref()
                .context("procedure memory must retain evidence ids")?
        )?,
        candidate.source_event_ids
    );
    assert_eq!(result_sha256, actual.sha256());
    let retained_trust: String = conn.query_row(
        "SELECT source_trust_class FROM memories WHERE id = ?1",
        [memory_id],
        |row| row.get(0),
    )?;
    assert_eq!(retained_trust, "external_content");

    let markdown_content = format!("{}\nMarkdown clarification.", candidate.content);
    let route = crate::memory::activation::load_existing_route(&conn, memory_id)?;
    let markdown_request = crate::memory::activation::ActiveMemoryWriteRequest {
        activation_id: "markdown:procedure-provenance-update".to_string(),
        route_kind: crate::memory::activation::ActivationRouteKind::BackupImport,
        actor_kind: crate::memory::activation::ActivationActorKind::Operator,
        source_operation: "markdown_update".to_string(),
        source_trust: crate::memory::poisoning::SourceTrustClass::RepoFile,
        result_source_trust: crate::memory::poisoning::SourceTrustClass::RepoFile,
        source_project: route.source_project,
        route: route.route,
        provenance_kind: crate::memory::activation::ActivationProvenanceKind::Backup,
        provenance_ref: "operator:markdown:procedure-provenance".to_string(),
        payload_sha256: crate::memory::activation::payload_sha256(&[
            "procedure-provenance-update",
            &markdown_content,
        ]),
        expected_memory: crate::memory::activation::ExpectedActiveMemory::new(
            &candidate.title,
            &markdown_content,
            "procedure",
        )
        .with_topic_key(Some(&candidate.topic_key))
        .with_files(Some(&serde_json::to_string(&candidate.files)?)),
        poisoning_verdict: crate::memory::activation::ActivationPoisoningVerdict::Clean,
        superseded_ids: Vec::new(),
    };
    crate::memory::activation::execute_one(&conn, &markdown_request, |_permit| {
        conn.execute(
            "UPDATE memories
             SET content = ?1, source_trust_class = 'repo_file',
                 source_candidate_id = NULL, evidence_event_ids = NULL
             WHERE id = ?2",
            params![markdown_content, memory_id],
        )?;
        Ok(memory_id)
    })?;
    conn.execute(
        "UPDATE memory_candidates
         SET topic_key = 'retagged-procedure', memory_type = 'discovery'
         WHERE id = 901",
        [],
    )?;
    assert_eq!(promote_procedure_memory(&conn, &candidate)?, memory_id);

    let replacement_id = promote_procedure_memory(&conn, &expanded)?;
    assert_ne!(replacement_id, memory_id);
    assert_eq!(
        conn.query_row(
            "SELECT status FROM memories WHERE id = ?1",
            [memory_id],
            |row| { row.get::<_, String>(0) }
        )?,
        "stale"
    );
    assert_eq!(
        conn.query_row(
            "SELECT COUNT(*) FROM memory_activation_requests",
            [],
            |row| row.get::<_, i64>(0),
        )?,
        3
    );
    for id in [memory_id, replacement_id] {
        let stored_sha: String = conn.query_row(
            "SELECT result_sha256 FROM memory_activation_requests
             WHERE result_memory_id = ?1 ORDER BY rowid DESC LIMIT 1",
            [id],
            |row| row.get(0),
        )?;
        let row = crate::memory::activation::ExpectedActiveMemory::from_existing(&conn, id)?;
        assert_eq!(stored_sha, row.sha256());
    }
    assert_eq!(promote_procedure_memory(&conn, &candidate)?, memory_id);
    let replacement_trust: String = conn.query_row(
        "SELECT source_trust_class FROM memories WHERE id = ?1",
        [replacement_id],
        |row| row.get(0),
    )?;
    assert_eq!(replacement_trust, "repo_file");
    conn.execute(
        "UPDATE procedure_verifications SET verified_at_epoch = 1",
        [],
    )?;
    assert_eq!(promote_procedure_memory(&conn, &candidate)?, memory_id);
    Ok(())
}

#[test]
fn procedure_evidence_must_match_the_captured_success_event() -> Result<()> {
    let conn = Connection::open_in_memory()?;
    conn.execute_batch("PRAGMA journal_mode=WAL; PRAGMA foreign_keys=ON;")?;
    crate::migrate::run_migrations(&conn)?;
    let (candidate, _) = verified_candidates(&conn)?;
    conn.execute("UPDATE captured_events SET tool_name = 'Read'", [])?;

    assert!(super::evidence::load_verified_procedure_evidence(
        &conn,
        &candidate.source_event_ids,
        &candidate.project,
        &ProcedurePromotionPolicy::default(),
    )?
    .is_none());
    let error = promote_procedure_memory(&conn, &candidate)
        .expect_err("non-Bash captured events may not authenticate procedure evidence");
    assert!(error.to_string().contains("verified evidence"));
    assert_eq!(
        conn.query_row("SELECT COUNT(*) FROM memories", [], |row| row
            .get::<_, i64>(0))?,
        0
    );
    Ok(())
}

#[test]
fn procedure_replay_uses_original_receipt_trust_after_agent_update() -> Result<()> {
    let conn = Connection::open_in_memory()?;
    conn.execute_batch("PRAGMA journal_mode=WAL; PRAGMA foreign_keys=ON;")?;
    crate::migrate::run_migrations(&conn)?;
    let (candidate, _) = verified_candidates(&conn)?;

    let memory_id = promote_procedure_memory(&conn, &candidate)?;
    let updated = crate::memory::service::save_memory_from_with_reference_time(
        &conn,
        &crate::memory::service::SaveMemoryRequest {
            text: format!("{}\nAgent clarification.", candidate.content),
            title: Some(candidate.title.clone()),
            project: Some(candidate.project.clone()),
            topic_key: Some(candidate.topic_key.clone()),
            memory_type: Some("procedure".to_string()),
            branch: candidate.branch.clone(),
            scope: Some("project".to_string()),
            local_copy_enabled: Some(false),
            claim_enabled: Some(false),
            idempotency_key: Some("procedure-agent-update".to_string()),
            ..crate::memory::service::SaveMemoryRequest::default()
        },
        None,
        crate::memory::service::SaveMemoryCaller::McpAgent,
    )?;
    assert_eq!(updated.id, memory_id);
    assert_eq!(
        conn.query_row(
            "SELECT source_trust_class FROM memories WHERE id = ?1",
            [memory_id],
            |row| row.get::<_, String>(0),
        )?,
        "external_content"
    );

    assert_eq!(promote_procedure_memory(&conn, &candidate)?, memory_id);
    assert_eq!(
        conn.query_row(
            "SELECT COUNT(*) FROM memory_activation_requests WHERE result_memory_id = ?1",
            [memory_id],
            |row| row.get::<_, i64>(0),
        )?,
        2
    );
    Ok(())
}