From 98066669b30e3ec42d086ca7bc52262508ef6ada Mon Sep 17 00:00:00 2001 From: Bryan Helmkamp Date: Sat, 7 Mar 2026 03:33:19 -0500 Subject: [PATCH] Fix turn/tool-call counts always showing 0 in non-TTY mode The active_stages map was only populated inside a TTY renderer guard, so Plain mode never tracked counts. Move counters to a separate stage_counts map that is always populated regardless of renderer. Co-Authored-By: Claude Opus 4.6 (1M context) --- crates/arc-workflows/src/cli/progress.rs | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/crates/arc-workflows/src/cli/progress.rs b/crates/arc-workflows/src/cli/progress.rs index 82517f672..007d6cca5 100644 --- a/crates/arc-workflows/src/cli/progress.rs +++ b/crates/arc-workflows/src/cli/progress.rs @@ -160,8 +160,6 @@ struct ActiveStage { has_model: bool, spinner: ProgressBar, tool_calls: VecDeque, - turn_count: u32, - tool_call_count: u32, } const MAX_TOOL_CALLS: usize = 5; @@ -183,6 +181,9 @@ pub struct ProgressUI { renderer: ProgressRenderer, verbose: bool, active_stages: HashMap, + /// Turn and tool-call counts per stage, tracked independently of the + /// renderer so that Plain (non-TTY) mode reports accurate stats. + stage_counts: HashMap, setup_command_count: usize, sandbox_bar: Option, setup_bar: Option, @@ -204,6 +205,7 @@ impl ProgressUI { renderer, verbose, active_stages: HashMap::new(), + stage_counts: HashMap::new(), setup_command_count: 0, sandbox_bar: None, setup_bar: None, @@ -285,9 +287,9 @@ impl ProgressUI { .map(|c| format!("{} ", format_cost(c))) .unwrap_or_default(); let stats_str = if self.verbose { - let stage = self.active_stages.get(node_id); - let turn_count = stage.map_or(0, |s| s.turn_count); - let tool_call_count = stage.map_or(0, |s| s.tool_call_count); + let counts = self.stage_counts.get(node_id); + let turn_count = counts.map_or(0, |c| c.0); + let tool_call_count = counts.map_or(0, |c| c.1); let total_tokens = usage .as_ref() .map(|u| u.input_tokens + u.output_tokens) @@ -623,6 +625,7 @@ impl ProgressUI { // ── Stages ────────────────────────────────────────────────────────── fn on_stage_started(&mut self, node_id: &str, name: &str, script: Option<&str>) { + self.stage_counts.insert(node_id.to_string(), (0, 0)); let display_name = match script { Some(s) => { let dim = Style::new().dim(); @@ -648,8 +651,6 @@ impl ProgressUI { has_model: false, spinner: bar, tool_calls: VecDeque::new(), - turn_count: 0, - tool_call_count: 0, }, ); } @@ -689,9 +690,11 @@ impl ProgressUI { fn on_agent_event(&mut self, stage_node_id: &str, event: &AgentEvent) { match event { AgentEvent::AssistantMessage { model, .. } => { + if let Some(counts) = self.stage_counts.get_mut(stage_node_id) { + counts.0 += 1; + } if let ProgressRenderer::Tty(_) = &self.renderer { if let Some(stage) = self.active_stages.get_mut(stage_node_id) { - stage.turn_count += 1; if !stage.has_model { stage.has_model = true; let dim = Style::new().dim(); @@ -714,8 +717,8 @@ impl ProgressUI { is_error, .. } => { - if let Some(stage) = self.active_stages.get_mut(stage_node_id) { - stage.tool_call_count += 1; + if let Some(counts) = self.stage_counts.get_mut(stage_node_id) { + counts.1 += 1; } self.on_tool_call_completed(stage_node_id, tool_call_id, *is_error); }