remem-ai 0.6.10

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

pub(super) struct MarkdownOwnership<'a> {
    pub(super) source_project: &'a str,
    pub(super) target_project: Option<&'a str>,
    pub(super) owner_scope: &'a str,
    pub(super) owner_key: &'a str,
    pub(super) context_class: &'a str,
}

pub(super) fn markdown_ownership(doc: &MarkdownMemoryDocument) -> MarkdownOwnership<'_> {
    let fallback =
        crate::memory::store::default_ownership(&doc.metadata.project, &doc.metadata.scope);
    MarkdownOwnership {
        source_project: doc
            .metadata
            .source_project
            .as_deref()
            .unwrap_or(fallback.source_project),
        target_project: doc
            .metadata
            .target_project
            .as_deref()
            .or(fallback.target_project),
        owner_scope: doc
            .metadata
            .owner_scope
            .as_deref()
            .unwrap_or(fallback.owner_scope),
        owner_key: doc
            .metadata
            .owner_key
            .as_deref()
            .unwrap_or(fallback.owner_key),
        context_class: doc
            .metadata
            .context_class
            .as_deref()
            .unwrap_or(fallback.context_class),
    }
}

pub(super) fn update_optional_memory_provenance(
    conn: &Connection,
    memory_id: i64,
    _doc: &MarkdownMemoryDocument,
) -> Result<()> {
    if column_exists(conn, "memories", "evidence_event_ids")? {
        conn.execute(
            "UPDATE memories SET evidence_event_ids = ?1 WHERE id = ?2",
            rusqlite::params![Option::<String>::None, memory_id],
        )?;
    }
    if column_exists(conn, "memories", "source_candidate_id")? {
        conn.execute(
            "UPDATE memories SET source_candidate_id = ?1 WHERE id = ?2",
            rusqlite::params![Option::<i64>::None, memory_id],
        )?;
    }
    Ok(())
}

pub(super) fn upsert_markdown_lesson_metadata(
    conn: &Connection,
    memory_id: i64,
    doc: &MarkdownMemoryDocument,
    updated_at_epoch: i64,
) -> Result<()> {
    let fallback = default_markdown_lesson_metadata(updated_at_epoch);
    let lesson = doc.metadata.lesson.as_ref().unwrap_or(&fallback);
    conn.execute(
        "INSERT INTO memory_lessons
         (memory_id, confidence, reinforcement_count, source_evidence,
          last_reinforced_at_epoch, stale_after_epoch, outcome_kind,
          success_count, failure_count, recovery_count, correction_count, revert_count)
         VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8, ?9, ?10, ?11, ?12)
         ON CONFLICT(memory_id) DO UPDATE SET
           confidence = excluded.confidence,
           reinforcement_count = excluded.reinforcement_count,
           source_evidence = excluded.source_evidence,
           last_reinforced_at_epoch = excluded.last_reinforced_at_epoch,
           stale_after_epoch = excluded.stale_after_epoch,
           outcome_kind = excluded.outcome_kind,
           success_count = excluded.success_count,
           failure_count = excluded.failure_count,
           recovery_count = excluded.recovery_count,
           correction_count = excluded.correction_count,
           revert_count = excluded.revert_count",
        rusqlite::params![
            memory_id,
            lesson.confidence,
            lesson.reinforcement_count,
            lesson.source_evidence,
            lesson.last_reinforced_at_epoch,
            lesson.stale_after_epoch,
            lesson.outcome_kind,
            lesson.success_count,
            lesson.failure_count,
            lesson.recovery_count,
            lesson.correction_count,
            lesson.revert_count,
        ],
    )?;
    Ok(())
}

pub(super) fn default_markdown_lesson_metadata(updated_at_epoch: i64) -> MarkdownLessonMetadata {
    MarkdownLessonMetadata {
        confidence: 0.7,
        reinforcement_count: 1,
        source_evidence: Some("markdown_import".to_string()),
        last_reinforced_at_epoch: updated_at_epoch,
        stale_after_epoch: None,
        outcome_kind: "unknown".to_string(),
        success_count: 0,
        failure_count: 0,
        recovery_count: 0,
        correction_count: 0,
        revert_count: 0,
    }
}