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}