From 46b6e7d5e5c0092ca8c5190123fa4f1c78ed37d0 Mon Sep 17 00:00:00 2001 From: Artem Trofymenko <99894081+artemtrofymenko@users.noreply.github.com> Date: Thu, 3 Sep 2026 17:58:31 +0200 Subject: [PATCH 1/2] fix(desktop): stop overriding an operator's global RUST_LOG level MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit child_rust_log_filter appends `buzz_acp=info` to any filter that does not already mention `buzz_acp`. A bare level is such a filter, and a target directive outranks the global one, so the append reverses the operator's intent in both directions: `RUST_LOG=off` still logs this crate at info, and `RUST_LOG=trace` is narrowed back to info for the one crate they were trying to debug. Forward a filter that sets a global level unchanged. Named and numeric spellings both count — `EnvFilter` accepts `0`..`5` as levels, and `0` is the case that matters most, since widening "log nothing" is the opposite of the request. Target-only filters still gain the harness default, an explicit `buzz_acp` filter is still passed through, and unset still defaults, so the change is confined to the case that was wrong. Reported independently by @cristiansotogarciaxatech on #6035 while reviewing the log-target defect; this is the part of that discussion which survives #3309 landing, since it lives in the desktop rather than in the crate's targets. Signed-off-by: Artem Trofymenko <99894081+artemtrofymenko@users.noreply.github.com> --- .../src/managed_agents/runtime/metadata.rs | 77 +++++++++++++++++-- 1 file changed, 71 insertions(+), 6 deletions(-) diff --git a/desktop/src-tauri/src/managed_agents/runtime/metadata.rs b/desktop/src-tauri/src/managed_agents/runtime/metadata.rs index 769e20cedf6..7517b17acfc 100644 --- a/desktop/src-tauri/src/managed_agents/runtime/metadata.rs +++ b/desktop/src-tauri/src/managed_agents/runtime/metadata.rs @@ -108,20 +108,50 @@ pub(crate) fn resolve_session_title(display_name: Option<&str>, name: &str) -> O .find(|value| !value.is_empty()) } +/// Level names that, standing alone, set `EnvFilter`'s global default rather +/// than a target. The parser accepts the numeric forms too. +const GLOBAL_LEVELS: [&str; 12] = [ + "off", "error", "warn", "info", "debug", "trace", "0", "1", "2", "3", "4", "5", +]; + +/// Whether any directive in `filter` is a bare level, i.e. the operator set a +/// global default instead of naming targets. +fn sets_global_level(filter: &str) -> bool { + filter.split(',').any(|directive| { + let directive = directive.trim(); + GLOBAL_LEVELS.iter().any(|level| directive.eq_ignore_ascii_case(level)) + }) +} + /// Build the `RUST_LOG` value forwarded to the agent child: keep an existing -/// filter that already mentions `buzz_acp`, append `buzz_acp=info` to any other -/// non-empty filter, and default to `buzz_acp=info` when unset. +/// filter that already mentions `buzz_acp` or sets a global level, append +/// `buzz_acp=info` to any other non-empty filter, and default to +/// `buzz_acp=info` when unset. pub(crate) fn child_rust_log_filter() -> String { - match std::env::var("RUST_LOG") { - Ok(existing) if existing.contains("buzz_acp") => existing, - Ok(existing) if !existing.trim().is_empty() => format!("{existing},buzz_acp=info"), + child_rust_log_filter_from(std::env::var("RUST_LOG").ok()) +} + +/// Taking the ambient value as an argument keeps the policy testable without a +/// test mutating process environment. +fn child_rust_log_filter_from(existing: Option) -> String { + match existing { + Some(existing) if existing.contains("buzz_acp") => existing, + // A bare level is the global default, and a target directive outranks + // it. Appending ours would override the operator in both directions: + // `off` would still log this crate at info, and `trace` would be + // narrowed to info for the one crate the operator was debugging. + Some(existing) if sets_global_level(&existing) => existing, + Some(existing) if !existing.trim().is_empty() => format!("{existing},buzz_acp=info"), _ => "buzz_acp=info".to_string(), } } #[cfg(test)] mod tests { - use super::{apply_replay_floor_env, resolve_session_title, REPLAY_FLOOR_ENV_VAR}; + use super::{ + apply_replay_floor_env, child_rust_log_filter_from, resolve_session_title, + REPLAY_FLOOR_ENV_VAR, + }; fn replay_floor_of(cmd: &std::process::Command) -> Option { cmd.get_envs() @@ -254,4 +284,39 @@ mod tests { fn resolve_session_title_returns_none_when_both_candidates_are_control_chars_only() { assert_eq!(resolve_session_title(Some("\u{0}"), "\u{0}"), None); } + + #[test] + fn bare_global_level_is_forwarded_untouched() { + // `off` must stay silence and `trace` must stay trace: appending a + // target directive would outrank the level the operator chose. + for value in ["off", "TRACE", " debug ", "0", "5"] { + assert_eq!( + child_rust_log_filter_from(Some(value.to_string())), + value, + "global level must reach the child unchanged" + ); + } + } + + #[test] + fn global_level_alongside_targets_is_still_respected() { + let filter = child_rust_log_filter_from(Some("debug,hyper=warn".to_string())); + assert_eq!(filter, "debug,hyper=warn"); + } + + #[test] + fn target_only_filter_still_gains_the_harness_default() { + let filter = child_rust_log_filter_from(Some("hyper=warn".to_string())); + assert_eq!(filter, "hyper=warn,buzz_acp=info"); + } + + #[test] + fn explicit_buzz_acp_filter_and_unset_are_unchanged() { + assert_eq!( + child_rust_log_filter_from(Some("buzz_acp=debug".to_string())), + "buzz_acp=debug" + ); + assert_eq!(child_rust_log_filter_from(None), "buzz_acp=info"); + assert_eq!(child_rust_log_filter_from(Some(" ".to_string())), "buzz_acp=info"); + } } From 80dfff46383cd45bfd68d9efb6ac98ba1395e2da Mon Sep 17 00:00:00 2001 From: Artem Trofymenko <99894081+artemtrofymenko@users.noreply.github.com> Date: Thu, 3 Sep 2026 18:20:55 +0200 Subject: [PATCH 2/2] style: rustfmt the global-level helper and its test Signed-off-by: Artem Trofymenko Signed-off-by: Artem Trofymenko <99894081+artemtrofymenko@users.noreply.github.com> --- desktop/src-tauri/src/managed_agents/runtime/metadata.rs | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/desktop/src-tauri/src/managed_agents/runtime/metadata.rs b/desktop/src-tauri/src/managed_agents/runtime/metadata.rs index 7517b17acfc..0c5a143672d 100644 --- a/desktop/src-tauri/src/managed_agents/runtime/metadata.rs +++ b/desktop/src-tauri/src/managed_agents/runtime/metadata.rs @@ -119,7 +119,9 @@ const GLOBAL_LEVELS: [&str; 12] = [ fn sets_global_level(filter: &str) -> bool { filter.split(',').any(|directive| { let directive = directive.trim(); - GLOBAL_LEVELS.iter().any(|level| directive.eq_ignore_ascii_case(level)) + GLOBAL_LEVELS + .iter() + .any(|level| directive.eq_ignore_ascii_case(level)) }) } @@ -317,6 +319,9 @@ mod tests { "buzz_acp=debug" ); assert_eq!(child_rust_log_filter_from(None), "buzz_acp=info"); - assert_eq!(child_rust_log_filter_from(Some(" ".to_string())), "buzz_acp=info"); + assert_eq!( + child_rust_log_filter_from(Some(" ".to_string())), + "buzz_acp=info" + ); } }