diff --git a/src/model/settings.rs b/src/model/settings.rs index 22d7540..41122f7 100644 --- a/src/model/settings.rs +++ b/src/model/settings.rs @@ -19,7 +19,10 @@ pub enum InternetMode { Full, } - +/// Default per-node timeout for hive-mind nodes: 10 minutes. +fn default_hive_mind_node_timeout_ms() -> u64 { + 600_000 +} /// Top-level application settings, serialized to `settings.json` in the store dir. /// @@ -42,6 +45,11 @@ pub struct Settings { pub session_archive_enabled: bool, pub lsp_auto_provision: bool, pub lsp_languages: Vec, + /// Wall-clock deadline for a single hive-mind processing node (cycle + /// node or synthesis node). Prevents one stuck node from hanging an + /// entire hive-mind convergence forever. + #[serde(default = "default_hive_mind_node_timeout_ms")] + pub hive_mind_node_timeout_ms: u64, } impl Default for Settings { @@ -62,6 +70,7 @@ impl Default for Settings { session_archive_enabled: true, lsp_auto_provision: true, lsp_languages: Vec::new(), + hive_mind_node_timeout_ms: default_hive_mind_node_timeout_ms(), } } } @@ -103,3 +112,42 @@ impl Settings { Ok(()) } } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn default_hive_mind_node_timeout_is_ten_minutes() { + let settings = Settings::default(); + assert_eq!(settings.hive_mind_node_timeout_ms, 600_000); + } + + #[test] + fn missing_hive_mind_node_timeout_field_falls_back_to_default() { + // Simulates loading a settings.json written before this field + // existed — #[serde(default = ...)] must fill it in rather than + // failing the whole parse (which would silently reset every + // other saved setting to default too). + let old_json = r#"{ + "internet_mode": "Off", + "provider": "zen", + "model": "deepseek-v4-flash-free", + "api_keys": {}, + "max_tokens": null, + "temperature": null, + "review_enabled": true, + "review_max_lessons_per_run": 5, + "adaptive_review_max_skip": 3, + "verify_command": null, + "verify_timeout_ms": 30000, + "workflow_max_concurrency": 5, + "session_archive_enabled": true, + "lsp_auto_provision": true, + "lsp_languages": [] + }"#; + let parsed: Settings = serde_json::from_str(old_json) + .expect("must parse even without the new field present"); + assert_eq!(parsed.hive_mind_node_timeout_ms, 600_000); + } +}