fix(acp): reserve stdout for protocol frames

Co-authored-by: Larry <larry@users.noreply.github.com>
Signed-off-by: Logan Johnson <loganj@squareup.com>
This commit is contained in:
Logan Johnson
2026-09-17 23:35:47 -04:00
parent bf209308d0
commit 5d12a01e5f
2 changed files with 38 additions and 0 deletions

View File

@@ -2488,7 +2488,10 @@ async fn tokio_main() -> Result<()> {
return run_authenticate(args).await;
}
// Stdout is the ACP transport when buzz-acp is launched as an agent command.
// Keep every harness diagnostic on stderr so logging can never corrupt NDJSON.
tracing_subscriber::fmt()
.with_writer(std::io::stderr)
.with_env_filter(
EnvFilter::try_from_default_env().unwrap_or_else(|_| EnvFilter::new("buzz_acp=info")),
)

View File

@@ -0,0 +1,35 @@
use std::process::Command;
#[test]
fn startup_and_failure_diagnostics_never_enter_acp_stdout() {
let output = Command::new(env!("CARGO_BIN_EXE_buzz-acp"))
.env("BUZZ_RELAY_URL", "ws://127.0.0.1:1")
.env(
"BUZZ_PRIVATE_KEY",
"0000000000000000000000000000000000000000000000000000000000000001",
)
.env("BUZZ_AGENT_COMMAND", "/definitely/missing/buzz-agent")
.env("RUST_LOG", "buzz_acp=info")
.output()
.expect("buzz-acp process should start");
assert!(
!output.status.success(),
"unreachable relay must fail startup"
);
assert!(
output.stdout.is_empty(),
"ACP stdout contained diagnostics: {:?}",
String::from_utf8_lossy(&output.stdout)
);
let stderr = String::from_utf8_lossy(&output.stderr);
assert!(
stderr.contains("buzz-acp starting:"),
"missing startup INFO: {stderr}"
);
assert!(
stderr.contains("Error:") || stderr.contains("error"),
"missing failure diagnostic: {stderr}"
);
}