weavatrix-memory 0.3.2

Event-sourced bitemporal evidence memory and budgeted context retrieval for coding agents
Documentation
use super::{MemoryProjection, state::NodeRevision};
use crate::{
    domain::MemoryEvent,
    error::{MemoryError, Result},
    event::StoredEvent,
    projection::Projection,
};

impl Projection<MemoryEvent> for MemoryProjection {
    fn prepare_replay(&mut self, events: &[StoredEvent<MemoryEvent>]) {
        let mut nodes = 0;
        let mut facts = 0;
        for event in events {
            match event.payload {
                MemoryEvent::NodeUpserted { .. } => nodes += 1,
                MemoryEvent::FactRecorded { .. } => facts += 1,
                MemoryEvent::FactRetracted { .. } => {}
            }
        }
        self.nodes.reserve(nodes);
        self.facts.reserve(facts);
        self.node_lookup.reserve(nodes);
        self.fact_lookup.reserve(facts);
        self.incident_offsets.reserve(nodes);
    }

    fn apply(&mut self, event: &StoredEvent<MemoryEvent>) -> Result<()> {
        if event.metadata.event_type != event.payload.event_type() {
            return Err(MemoryError::InvalidValue {
                field: "event_type",
                reason: "must match the memory event payload",
            });
        }
        match &event.payload {
            MemoryEvent::NodeUpserted { node } => self.insert_node(NodeRevision {
                node: node.clone(),
                recorded_at: event.metadata.recorded_at,
                position: event.metadata.global_position,
            })?,
            MemoryEvent::FactRecorded { fact } => {
                if fact.recorded_at != event.metadata.recorded_at
                    || fact.agent_id != event.metadata.agent_id
                    || fact.session_id != event.metadata.session_id
                {
                    return Err(MemoryError::InvalidValue {
                        field: "fact.envelope",
                        reason: "fact provenance must match its event envelope",
                    });
                }
                self.insert_fact(fact.clone())?;
            }
            MemoryEvent::FactRetracted {
                fact_id,
                valid_until,
                evidence,
            } => {
                self.apply_retraction(event.metadata.recorded_at, fact_id, *valid_until, evidence)?;
            }
        }
        self.last_global_position = Some(event.metadata.global_position);
        Ok(())
    }

    fn apply_owned(&mut self, event: StoredEvent<MemoryEvent>) -> Result<()> {
        let StoredEvent { metadata, payload } = event;
        if metadata.event_type != payload.event_type() {
            return Err(MemoryError::InvalidValue {
                field: "event_type",
                reason: "must match the memory event payload",
            });
        }
        match payload {
            MemoryEvent::NodeUpserted { node } => self.insert_node(NodeRevision {
                node,
                recorded_at: metadata.recorded_at,
                position: metadata.global_position,
            })?,
            MemoryEvent::FactRecorded { fact } => {
                if fact.recorded_at != metadata.recorded_at
                    || fact.agent_id != metadata.agent_id
                    || fact.session_id != metadata.session_id
                {
                    return Err(MemoryError::InvalidValue {
                        field: "fact.envelope",
                        reason: "fact provenance must match its event envelope",
                    });
                }
                self.insert_fact(fact)?;
            }
            MemoryEvent::FactRetracted {
                fact_id,
                valid_until,
                evidence,
            } => {
                self.apply_retraction(metadata.recorded_at, &fact_id, valid_until, &evidence)?;
            }
        }
        self.last_global_position = Some(metadata.global_position);
        Ok(())
    }
}