Skip to main content

zeph_config/memory/
store.rs

1// SPDX-FileCopyrightText: 2026 Andrei G <bug-ops>
2// SPDX-License-Identifier: MIT OR Apache-2.0
3
4//! Cross-thread key-value store configuration (spec-080, #6363).
5//!
6//! `LangGraph` `Store` parity — see [`crate::memory::root::MemoryConfig::store`] and
7//! `crates/zeph-memory/src/store/cross_thread.rs`.
8
9use serde::{Deserialize, Serialize};
10
11fn default_max_value_bytes() -> usize {
12    65536
13}
14
15/// Configuration for the generic namespaced cross-thread key-value store, nested under
16/// `[memory.store]` in TOML (spec-080, #6363).
17///
18/// Disabled by default (FR-A-001): when `enabled = false`, no store read/write path is
19/// exposed to orchestration or the CLI — zero behavior change.
20///
21/// # Example (TOML)
22///
23/// ```toml
24/// [memory.store]
25/// enabled = false
26/// max_value_bytes = 65536
27/// # search_provider = "fast"   # reserved for future semantic search
28/// ```
29#[derive(Debug, Clone, Deserialize, Serialize)]
30#[serde(default)]
31pub struct CrossThreadStoreConfig {
32    /// Master switch. Default: `false` (FR-A-001).
33    pub enabled: bool,
34    /// Maximum `value` size in bytes accepted by `store_put`; larger writes are rejected
35    /// with a descriptive error rather than truncated (FR-A-005). Default: `65536`.
36    #[serde(default = "default_max_value_bytes")]
37    pub max_value_bytes: usize,
38    /// Reserved for a future semantic-search extension over store values (`[memory.store]
39    /// search_provider`, declare-once `*_provider` naming per `CLAUDE.md` §Multi-Model
40    /// Design). Unused in v1 — MVP `store_search` is namespace-prefix + keyword match only
41    /// (spec-080 §1 Out of Scope).
42    #[serde(default)]
43    pub search_provider: Option<String>,
44}
45
46impl Default for CrossThreadStoreConfig {
47    fn default() -> Self {
48        Self {
49            enabled: false,
50            max_value_bytes: default_max_value_bytes(),
51            search_provider: None,
52        }
53    }
54}
55
56#[cfg(test)]
57mod tests {
58    use super::*;
59
60    #[test]
61    fn defaults_match_spec() {
62        let cfg = CrossThreadStoreConfig::default();
63        assert!(!cfg.enabled);
64        assert_eq!(cfg.max_value_bytes, 65536);
65        assert!(cfg.search_provider.is_none());
66    }
67
68    #[test]
69    fn deserializes_from_empty_table() {
70        let cfg: CrossThreadStoreConfig = toml::from_str("").unwrap();
71        assert!(!cfg.enabled);
72        assert_eq!(cfg.max_value_bytes, 65536);
73    }
74
75    #[test]
76    fn deserializes_explicit_values() {
77        let cfg: CrossThreadStoreConfig =
78            toml::from_str("enabled = true\nmax_value_bytes = 1024\n").unwrap();
79        assert!(cfg.enabled);
80        assert_eq!(cfg.max_value_bytes, 1024);
81    }
82}