enact-core 0.0.2

Core agent runtime for Enact - Graph-Native AI agents
Documentation
//! Persistence Layer - Storage Abstractions
//!
//! This module defines the core storage traits for the Enact execution engine.
//! The kernel interacts only with these traits; concrete implementations live
//! in the `enact-persistence` crate.
//!
//! ## Store Roles
//!
//! | Store | Role | Mutable? | Authoritative? |
//! |-------|------|----------|----------------|
//! | EventStore | Source of truth | Append-only | Yes |
//! | StateStore | Cache/snapshot | Yes | No |
//! | VectorStore | Semantic recall | Yes | No |
//!
//! ## Key Invariants
//!
//! 1. Kernel NEVER imports concrete implementations
//! 2. All stores implement `StorageBackend` for mode validation
//! 3. EventStore is append-only and authoritative
//! 4. StateStore/VectorStore are non-authoritative caches
//!
//! @see docs/TECHNICAL/14-PERSISTENCE-LAYER.md

mod event_store;
mod message_store;
mod state_store;
mod vector_store;

pub use event_store::{EventStore, ExecutionEventData, StoredEvent};
pub use message_store::{
    CostInfo, ExecutionStats, FinishReason, InMemoryMessageStore, Message, MessageMetadata,
    MessagePart, MessageRole, MessageStore, Thread, TokenUsage,
};
pub use state_store::{ExecutionSnapshot, StateStore, StateStoreJsonExt};
pub use vector_store::{
    CollectionInfo, DistanceMetric, VectorDocument, VectorFilter, VectorSearchResult, VectorStore,
};

use async_trait::async_trait;

/// Base trait for all storage backends
///
/// This trait provides metadata about the backend's capabilities,
/// enabling runtime mode validation (e.g., air-gapped mode rejection
/// of network-requiring backends).
#[async_trait]
pub trait StorageBackend: Send + Sync {
    /// Human-readable name of this backend (e.g., "sqlite", "postgres", "qdrant")
    fn name(&self) -> &str;

    /// Does this backend require network access?
    ///
    /// Returns `true` for backends like Postgres, Redis, remote Qdrant.
    /// Returns `false` for SQLite, filesystem, embedded Qdrant.
    fn requires_network(&self) -> bool;

    /// Can this backend operate in air-gapped mode?
    ///
    /// Default implementation returns `!requires_network()`.
    fn supports_air_gapped(&self) -> bool {
        !self.requires_network()
    }

    /// Health check - verify the backend is operational
    ///
    /// Should return `Ok(())` if the backend is ready to accept operations.
    /// Should return an error if the backend is unavailable or misconfigured.
    async fn health_check(&self) -> anyhow::Result<()>;

    /// Graceful shutdown
    ///
    /// Called when the runtime is shutting down. Implementations should
    /// flush any pending writes and close connections.
    async fn shutdown(&self) -> anyhow::Result<()> {
        Ok(())
    }
}

/// Store guarantees - documented contracts for each store type
#[allow(clippy::mixed_attributes_style)]
pub mod guarantees {
    //! # Store Guarantees
    //!
    //! These guarantees are contractual and must be tested.
    //!
    //! ## EventStore
    //! - **Durability**: Event is durable before `append()` returns
    //! - **Ordering**: Events are strictly ordered per execution
    //! - **Immutability**: Events are append-only, never modified
    //! - **Authority**: EventStore is the source of truth
    //!
    //! ## StateStore
    //! - **Durability**: Best-effort - may be lost on failure
    //! - **Freshness**: May be stale - always verify with EventStore
    //! - **Authority**: Non-authoritative - cache only
    //! - **Availability**: Designed for fast reads, not durability
    //!
    //! ## VectorStore
    //! - **Consistency**: Eventually consistent
    //! - **Authority**: Non-authoritative - for recall only
    //! - **Availability**: Search may return stale results
    //! - **Durability**: Documents are durable after `upsert()` returns
}