From cb2aa9a73163ce392d7c3f1dd81888b039312a67 Mon Sep 17 00:00:00 2001 From: soryu Date: Sat, 24 Jan 2026 00:52:28 +0000 Subject: feat: Add maximum iterations limit for autonomous loop mode Adds configurable iteration limits to prevent runaway autonomous loops and provide predictable behavior, inspired by Ralph's design patterns. Changes: - Add AutonomousLoopConfig to daemon config with: - default_max_iterations: 10 (default for new tasks) - hard_limit: 50 (absolute maximum that cannot be exceeded) - no_change_threshold: 3 (consecutive runs without progress) - same_error_threshold: 5 (consecutive runs with same error) - Add max_iterations and iteration_count fields to Task model - Add iteration_limit_reached status to TaskStatus enum - Pass max_iterations through DaemonCommand::SpawnTask - Apply limits in CircuitBreaker during autonomous loop execution When a task hits the iteration limit: - Task status is set to "iteration_limit_reached" (not "failed") - Clear message is logged about hitting the limit - Task can be resumed with a higher limit if needed Co-Authored-By: Claude Opus 4.5 --- makima/src/db/models.rs | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) (limited to 'makima/src/db') diff --git a/makima/src/db/models.rs b/makima/src/db/models.rs index 58f4da1..c71dec5 100644 --- a/makima/src/db/models.rs +++ b/makima/src/db/models.rs @@ -351,6 +351,9 @@ pub enum TaskStatus { Done, Failed, Merged, + /// Task stopped due to reaching maximum iteration limit in autonomous loop mode. + /// Task can be resumed with a higher limit if needed. + IterationLimitReached, } impl std::fmt::Display for TaskStatus { @@ -363,6 +366,7 @@ impl std::fmt::Display for TaskStatus { TaskStatus::Done => write!(f, "done"), TaskStatus::Failed => write!(f, "failed"), TaskStatus::Merged => write!(f, "merged"), + TaskStatus::IterationLimitReached => write!(f, "iteration_limit_reached"), } } } @@ -379,6 +383,7 @@ impl std::str::FromStr for TaskStatus { "done" => Ok(TaskStatus::Done), "failed" => Ok(TaskStatus::Failed), "merged" => Ok(TaskStatus::Merged), + "iteration_limit_reached" => Ok(TaskStatus::IterationLimitReached), _ => Err(format!("Unknown task status: {}", s)), } } @@ -531,6 +536,15 @@ pub struct Task { /// Standalone completed tasks can be dismissed by the user. #[serde(default)] pub hidden: bool, + + // Autonomous loop iteration tracking + /// Maximum iterations for autonomous loop mode (None = use daemon default). + /// Task stops with "iteration_limit_reached" status when limit is hit. + #[serde(skip_serializing_if = "Option::is_none")] + pub max_iterations: Option, + /// Current iteration count in autonomous loop mode. + #[serde(default)] + pub iteration_count: i32, } impl Task { @@ -653,6 +667,9 @@ pub struct CreateTaskRequest { pub branched_from_task_id: Option, /// Conversation history to initialize the task with (JSON array of messages) pub conversation_history: Option, + /// Maximum iterations for autonomous loop mode (None = use daemon default). + /// Task stops with "iteration_limit_reached" status when limit is hit. + pub max_iterations: Option, } /// Request payload for updating a task @@ -684,6 +701,8 @@ pub struct UpdateTaskRequest { pub hidden: Option, /// Version for optimistic locking pub version: Option, + /// Update iteration count (for autonomous loop tracking) + pub iteration_count: Option, } /// Task with its subtasks for detail view -- cgit v1.2.3