//! Storage service (Phase 2, FR-STORE-*, FR-REL-*). SQLite index + on-disk //! audio/transcript/notes files (ADR-0006). Async via sqlx. //! //! Invariant: audio is the source of truth. Derived artifacts (transcript/notes/ //! summary) are regenerable; retention never touches an in-progress meeting. use crate::models::{MeetingId, MeetingListItem}; use async_trait::async_trait; #[derive(Debug, thiserror::Error)] pub enum StoreError { #[error("db error: {0}")] Db(String), #[error("io error: {0}")] Io(#[from] std::io::Error), #[error("not found: {0}")] NotFound(String), } pub struct NewMeeting { pub title: String, pub calendar_event_id: Option, } pub struct Retention { pub max_age_days: Option, pub max_size_gb: Option, } #[async_trait] pub trait Store: Send + Sync { async fn create_meeting(&self, m: NewMeeting) -> Result; async fn finalize_meeting(&self, id: &MeetingId) -> Result<(), StoreError>; async fn list_meetings( &self, query: Option, ) -> Result, StoreError>; async fn delete_meeting(&self, id: &MeetingId) -> Result<(), StoreError>; async fn update_notes(&self, id: &MeetingId, markdown: &str) -> Result<(), StoreError>; /// Full-text search across transcripts + notes (Phase 8, FR-SEARCH-1). async fn search(&self, query: &str) -> Result, StoreError>; /// Startup reconcile: meetings with audio but no finalized transcript (FR-REL-1). async fn recover_scan(&self) -> Result, StoreError>; /// Enforce retention; returns count removed. Skips in-progress meetings. async fn enforce_retention(&self, policy: Retention) -> Result; } /// SQLite-backed store. Migrations live in `migrations/` (sqlx::migrate!). pub struct SqliteStore; #[async_trait] impl Store for SqliteStore { async fn create_meeting(&self, _m: NewMeeting) -> Result { todo!("Phase 2 — create meeting row + folder") } async fn finalize_meeting(&self, _id: &MeetingId) -> Result<(), StoreError> { todo!("Phase 2 — persist transcript.json + metadata") } async fn list_meetings( &self, _query: Option, ) -> Result, StoreError> { todo!("Phase 2 — list meetings") } async fn delete_meeting(&self, _id: &MeetingId) -> Result<(), StoreError> { todo!("Phase 2 — delete meeting + folder") } async fn update_notes(&self, _id: &MeetingId, _markdown: &str) -> Result<(), StoreError> { todo!("Phase 2 — write notes.md + FTS") } async fn search(&self, _query: &str) -> Result, StoreError> { todo!("Phase 8 — FTS5 search") } async fn recover_scan(&self) -> Result, StoreError> { todo!("Phase 2 — recovery scan") } async fn enforce_retention(&self, _policy: Retention) -> Result { todo!("Phase 2 — retention enforcement") } }