diff --git a/crates/zesdex-cms/src/domain/settings.rs b/crates/zesdex-cms/src/domain/settings.rs index bbff95c..71add9e 100644 --- a/crates/zesdex-cms/src/domain/settings.rs +++ b/crates/zesdex-cms/src/domain/settings.rs @@ -44,6 +44,10 @@ impl Default for SettingsFlags { } } +fn default_hive_mind_node_timeout_ms() -> u64 { + 600_000 +} + /// Top-level application settings. /// /// Serialized to `settings.json` by the infrastructure layer. @@ -63,6 +67,7 @@ pub struct Settings { #[serde(flatten)] pub flags: SettingsFlags, pub lsp_languages: Vec, + #[serde(default = "default_hive_mind_node_timeout_ms")] pub hive_mind_node_timeout_ms: u64, } diff --git a/crates/zesdex-cms/src/infrastructure/persistence/settings_repo.rs b/crates/zesdex-cms/src/infrastructure/persistence/settings_repo.rs index 7a833b6..400931a 100644 --- a/crates/zesdex-cms/src/infrastructure/persistence/settings_repo.rs +++ b/crates/zesdex-cms/src/infrastructure/persistence/settings_repo.rs @@ -34,8 +34,16 @@ impl SettingsRepository for JsonSettingsRepository { fn load(&self, base_dir: &Path) -> Result { let path = base_dir.join("settings.json"); match std::fs::read_to_string(&path) { - Ok(s) => serde_json::from_str(&s) - .map_err(|e| anyhow::anyhow!("failed to parse settings.json: {e}")), + Ok(s) => match serde_json::from_str(&s) { + Ok(settings) => Ok(settings), + Err(e) => { + tracing::warn!( + "settings.json at '{}' failed to parse ({e}); falling back to defaults", + path.display() + ); + Ok(Settings::default()) + } + }, Err(e) if e.kind() == std::io::ErrorKind::NotFound => { tracing::info!("settings.json not found, using defaults"); Ok(Settings::default()) @@ -72,3 +80,25 @@ impl SettingsRepository for JsonSettingsRepository { Ok(()) } } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn load_defaults_hive_mind_timeout_when_field_missing_from_old_settings_json() { + let dir = std::env::temp_dir().join(format!("zesdex-cms-settings-test-{}", uuid::Uuid::new_v4())); + std::fs::create_dir_all(&dir).unwrap(); + // Simulate a settings.json written before `hive_mind_node_timeout_ms` existed. + std::fs::write( + dir.join("settings.json"), + r#"{"internet_mode":"Off","provider":"zen","model":"m","api_keys":{},"max_tokens":null,"temperature":null,"review_max_lessons_per_run":5,"adaptive_review_max_skip":3,"verify_command":null,"verify_timeout_ms":30000,"workflow_max_concurrency":5,"review_enabled":true,"session_archive_enabled":true,"lsp_auto_provision":true,"lsp_languages":[]}"#, + ).unwrap(); + + let repo = JsonSettingsRepository::new(); + let settings = repo.load(&dir).expect("load must not fail on a pre-existing settings.json missing the new field"); + assert_eq!(settings.hive_mind_node_timeout_ms, 600_000); + + let _ = std::fs::remove_dir_all(&dir); + } +}