diff --git a/lib/crates/fabro-cli/tests/it/workflow/real_cli.rs b/lib/crates/fabro-cli/tests/it/workflow/real_cli.rs index b247f14ae..2db5868a7 100644 --- a/lib/crates/fabro-cli/tests/it/workflow/real_cli.rs +++ b/lib/crates/fabro-cli/tests/it/workflow/real_cli.rs @@ -25,8 +25,6 @@ async fn run_real_cli_test(provider: Provider, model: &str) { let context = Context::new(); let emitter = Arc::new(EventEmitter::default()); - let dir = tempfile::tempdir().unwrap(); - let result = backend .run( &node, @@ -34,7 +32,7 @@ async fn run_real_cli_test(provider: Provider, model: &str) { &context, None, &emitter, - dir.path(), + workspace.path(), &env, None, ) @@ -56,29 +54,6 @@ async fn run_real_cli_test(provider: Provider, model: &str) { } CodergenResult::Full(_) => panic!("expected Text result from {provider}/{model}"), } - - // Verify log files were written - let provider_path = dir.path().join("provider_used.json"); - assert!( - provider_path.exists(), - "{provider}/{model}: provider_used.json should exist" - ); - let provider_json: serde_json::Value = - serde_json::from_str(&std::fs::read_to_string(&provider_path).unwrap()).unwrap(); - assert_eq!(provider_json["mode"], "cli"); - assert_eq!(provider_json["provider"], provider.as_str()); - - // Verify CLI output was streamed to stage_dir during poll - let stdout_log = dir.path().join("cli_stdout.log"); - assert!( - stdout_log.exists(), - "{provider}/{model}: cli_stdout.log should be written during poll" - ); - let stdout_content = std::fs::read_to_string(&stdout_log).unwrap(); - assert!( - !stdout_content.is_empty(), - "{provider}/{model}: cli_stdout.log should not be empty" - ); } #[fabro_macros::e2e_test(live("ANTHROPIC_API_KEY"))] diff --git a/lib/crates/fabro-workflow/src/handler/llm/api.rs b/lib/crates/fabro-workflow/src/handler/llm/api.rs index 8156720d6..56449fc56 100644 --- a/lib/crates/fabro-workflow/src/handler/llm/api.rs +++ b/lib/crates/fabro-workflow/src/handler/llm/api.rs @@ -13,7 +13,6 @@ use fabro_llm::types::{Message, Request, Usage}; use fabro_mcp::config::McpServerSettings; use fabro_model::FallbackTarget; use fabro_model::Provider; -use tokio::fs; use tokio::sync::Mutex as TokioMutex; use super::super::agent::{CodergenBackend, CodergenResult}; @@ -269,7 +268,7 @@ impl CodergenBackend for AgentApiBackend { node: &Node, prompt: &str, system_prompt: Option<&str>, - stage_dir: &std::path::Path, + _stage_dir: &std::path::Path, ) -> Result { let client = Client::from_env() .await @@ -310,11 +309,6 @@ impl CodergenBackend for AgentApiBackend { provider_options: None, }; - let _ = fs::create_dir_all(stage_dir).await; - if let Ok(json) = serde_json::to_string_pretty(&request) { - let _ = fs::write(stage_dir.join("api_request.json"), json).await; - } - // Build per-request fallback chain: if the node overrides the provider, // no failover is available; otherwise use the backend's. let fallback_chain: &[FallbackTarget] = if node.provider().is_some() { @@ -391,10 +385,6 @@ impl CodergenBackend for AgentApiBackend { Err(sdk_err) => return Err(FabroError::Llm(sdk_err)), }; - if let Ok(json) = serde_json::to_string_pretty(&response) { - let _ = fs::write(stage_dir.join("api_response.json"), json).await; - } - let mut stage_usage = StageUsage { model: actual_model, input_tokens: response.usage.input_tokens, diff --git a/lib/crates/fabro-workflow/src/handler/llm/cli.rs b/lib/crates/fabro-workflow/src/handler/llm/cli.rs index 00408f9ba..7c9a0e845 100644 --- a/lib/crates/fabro-workflow/src/handler/llm/cli.rs +++ b/lib/crates/fabro-workflow/src/handler/llm/cli.rs @@ -6,7 +6,6 @@ use async_trait::async_trait; use fabro_agent::Sandbox; use fabro_agent::sandbox::ExecResult; use fabro_model::Provider; -use tokio::fs; use tokio::time::sleep; use super::super::agent::{CodergenBackend, CodergenResult}; @@ -466,7 +465,7 @@ impl CodergenBackend for AgentCliBackend { _context: &Context, _thread_id: Option<&str>, emitter: &Arc, - stage_dir: &Path, + _stage_dir: &Path, sandbox: &Arc, _tool_hooks: Option>, ) -> Result { @@ -508,8 +507,6 @@ impl CodergenBackend for AgentCliBackend { command: command.clone(), }); - let _ = fs::create_dir_all(stage_dir).await; - // Forward provider API key and custom env vars so the CLI tool can authenticate. // Build a HashMap to pass via exec_command's env_vars parameter — this // prepends `export` statements directly into the base64-encoded command, @@ -602,21 +599,6 @@ impl CodergenBackend for AgentCliBackend { .map_err(|e| FabroError::handler(format!("Failed to poll CLI command: {e}")))?; let status = poll_result.stdout.trim(); - // Sync CLI output to stage_dir for visibility (best-effort) - for (remote, local) in [ - (&stdout_path, "cli_stdout.log"), - (&stderr_path, "cli_stderr.log"), - ] { - if let Ok(r) = sandbox - .exec_command(&format!("cat {remote}"), 30_000, None, None, None) - .await - { - if !r.stdout.is_empty() { - let _ = fs::write(stage_dir.join(local), &r.stdout).await; - } - } - } - if status != "running" { break status.parse::().unwrap_or(-1); } @@ -653,19 +635,7 @@ impl CodergenBackend for AgentCliBackend { .exec_command(&format!("rm -f {tmp_prefix}_*"), 30_000, None, None, None) .await; - if let Ok(json) = serde_json::to_string_pretty(&serde_json::json!({ - "exit_code": result.exit_code, - "stdout_len": result.stdout.len(), - "stderr_len": result.stderr.len(), - "duration_ms": result.duration_ms, - })) { - let _ = fs::write(stage_dir.join("cli_result_meta.json"), json).await; - } - if result.exit_code != 0 { - let _ = fs::write(stage_dir.join("cli_stdout.log"), &result.stdout).await; - let _ = fs::write(stage_dir.join("cli_stderr.log"), &result.stderr).await; - let tail = |s: &str, n: usize| -> String { s.chars() .rev() diff --git a/lib/crates/fabro-workflow/src/node_handler.rs b/lib/crates/fabro-workflow/src/node_handler.rs index c49fdd91a..31e908899 100644 --- a/lib/crates/fabro-workflow/src/node_handler.rs +++ b/lib/crates/fabro-workflow/src/node_handler.rs @@ -17,7 +17,6 @@ use crate::graph::WorkflowNode; use crate::handler::{EngineServices, dispatch_handler, format_panic_message}; use crate::outcome::{Outcome, StageStatus}; use crate::retry::build_retry_policy; -use crate::run_dir; use fabro_graphviz::graph::types::Graph as GvGraph; use tokio::time::timeout; @@ -100,10 +99,6 @@ impl NodeHandler for WorkflowNodeHandler { } Err(panic_payload) => { let msg = format_panic_message(&panic_payload); - let visit = context.node_visit_count().max(1); - let panic_dir = run_dir::node_dir(&self.run_dir, &gv_node.id, visit); - let _ = std::fs::create_dir_all(&panic_dir); - let _ = std::fs::write(panic_dir.join("panic.txt"), &msg); Err(CoreError::handler(HandlerErrorDetail { message: msg, retryable: false, diff --git a/lib/crates/fabro-workflow/src/pipeline/execute/tests.rs b/lib/crates/fabro-workflow/src/pipeline/execute/tests.rs index 8b5d1c054..85918d0cd 100644 --- a/lib/crates/fabro-workflow/src/pipeline/execute/tests.rs +++ b/lib/crates/fabro-workflow/src/pipeline/execute/tests.rs @@ -768,7 +768,7 @@ async fn max_node_visits_errors_on_cycle() { } #[tokio::test] -async fn panic_handler_writes_panic_txt() { +async fn panic_handler_returns_panic_message() { let dir = tempfile::tempdir().unwrap(); let mut g = Graph::new("panic_test"); let mut start = Node::new("start"); @@ -790,7 +790,7 @@ async fn panic_handler_writes_panic_txt() { let mut registry = make_registry(); registry.register("panicker", Box::new(PanickingHandler)); - let _ = run_graph( + let result = run_graph( registry, test_emitter_arc("test-run"), local_env(), @@ -799,10 +799,10 @@ async fn panic_handler_writes_panic_txt() { ) .await; + let outcome = result.expect("runner should convert panic into a failed outcome"); + assert_eq!(outcome.status, StageStatus::Fail); let panic_path = dir.path().join("nodes").join("boom").join("panic.txt"); - assert!(panic_path.exists()); - let content = std::fs::read_to_string(&panic_path).unwrap(); - assert!(content.contains("test panic message")); + assert!(!panic_path.exists(), "panic.txt should not be written"); } #[tokio::test] diff --git a/lib/crates/fabro-workflow/tests/it/daytona_integration.rs b/lib/crates/fabro-workflow/tests/it/daytona_integration.rs index 493611107..4e0e19589 100644 --- a/lib/crates/fabro-workflow/tests/it/daytona_integration.rs +++ b/lib/crates/fabro-workflow/tests/it/daytona_integration.rs @@ -982,16 +982,6 @@ async fn run_daytona_cli_test(provider: Provider, model: &str, install_command: Err(e) => panic!("{provider}/{model} on Daytona failed: {e}"), } - // Verify log files - let provider_path = dir.path().join("provider_used.json"); - assert!( - provider_path.exists(), - "{provider}/{model}: provider_used.json should exist" - ); - let provider_json: serde_json::Value = - serde_json::from_str(&std::fs::read_to_string(&provider_path).unwrap()).unwrap(); - assert_eq!(provider_json["mode"], "cli"); - env.cleanup().await.unwrap(); } diff --git a/lib/crates/fabro-workflow/tests/it/integration.rs b/lib/crates/fabro-workflow/tests/it/integration.rs index a896505ba..fad936176 100644 --- a/lib/crates/fabro-workflow/tests/it/integration.rs +++ b/lib/crates/fabro-workflow/tests/it/integration.rs @@ -9475,7 +9475,7 @@ async fn cli_backend_run_uses_node_provider_override() { } #[tokio::test] -async fn cli_backend_run_writes_provider_used_json() { +async fn cli_backend_run_returns_text_and_usage() { let claude_output = r#"{"type":"result","result":"done","usage":{"input_tokens":10,"output_tokens":5}}"#; let env: Arc = Arc::new(CliTestEnv::new(claude_output)); @@ -9487,7 +9487,7 @@ async fn cli_backend_run_writes_provider_used_json() { let emitter = Arc::new(EventEmitter::default()); let dir = tempfile::tempdir().unwrap(); - backend + let result = backend .run( &node, "test", @@ -9501,19 +9501,16 @@ async fn cli_backend_run_writes_provider_used_json() { .await .expect("should succeed"); - let provider_path = dir.path().join("provider_used.json"); - assert!(provider_path.exists(), "should write provider_used.json"); - let provider_json: serde_json::Value = - serde_json::from_str(&std::fs::read_to_string(&provider_path).unwrap()).unwrap(); - assert_eq!(provider_json["mode"], "cli"); - assert_eq!(provider_json["provider"], "anthropic"); - assert_eq!(provider_json["model"], "claude-opus-4-6"); - assert!( - provider_json["command"] - .as_str() - .unwrap() - .contains("claude") - ); + match result { + CodergenResult::Text { text, usage, .. } => { + assert_eq!(text, "done"); + let usage = usage.expect("CLI backend should report usage"); + assert_eq!(usage.input_tokens, 10); + assert_eq!(usage.output_tokens, 5); + assert_eq!(usage.model, "claude-opus-4-6"); + } + CodergenResult::Full(_) => panic!("expected Text result"), + } } // -- BackendRouter e2e: delegates to correct backend --