From e0b2d716680cc05e8a6b64bfab407f5d8ace1838 Mon Sep 17 00:00:00 2001 From: Bryan Helmkamp Date: Sun, 19 Apr 2026 20:56:38 -0400 Subject: [PATCH] refactor(unwrap): clear non-test workspace warnings --- lib/crates/fabro-agent/src/agent_profile.rs | 6 ++-- lib/crates/fabro-agent/src/subagent.rs | 9 ++++-- lib/crates/fabro-agent/src/tool_execution.rs | 2 +- lib/crates/fabro-agent/src/tools.rs | 32 +++++++++++-------- lib/crates/fabro-api/build.rs | 13 +++----- lib/crates/fabro-cli/src/commands/model.rs | 7 +++- .../fabro-cli/src/commands/run/output.rs | 8 ++++- .../fabro-cli/src/commands/run/rewind.rs | 8 +++-- lib/crates/fabro-cli/src/commands/run/wait.rs | 9 +++--- .../fabro-cli/src/commands/runs/list.rs | 4 +-- .../fabro-cli/src/commands/system/prune.rs | 6 ++-- .../fabro-cli/src/commands/workflow/list.rs | 8 ++++- lib/crates/fabro-cli/src/main.rs | 2 +- .../fabro-config/src/effective_settings.rs | 4 +-- lib/crates/fabro-devcontainer/src/lib.rs | 2 +- lib/crates/fabro-graphviz/src/render.rs | 7 ++-- lib/crates/fabro-hooks/src/executor.rs | 8 +++-- .../fabro-llm/src/providers/anthropic.rs | 2 +- lib/crates/fabro-sandbox/src/daytona/mod.rs | 20 +++++++----- lib/crates/fabro-sandbox/src/docker.rs | 5 ++- lib/crates/fabro-server/src/demo/mod.rs | 2 +- lib/crates/fabro-server/src/run_files.rs | 6 ++-- lib/crates/fabro-tracker/src/linear.rs | 2 +- lib/crates/fabro-util/build.rs | 2 +- lib/crates/fabro-workflow/src/event.rs | 2 +- lib/crates/fabro-workflow/src/graph.rs | 2 +- .../fabro-workflow/src/handler/command.rs | 7 ++-- .../fabro-workflow/src/handler/parallel.rs | 5 +-- .../src/operations/rebuild_meta.rs | 5 ++- .../fabro-workflow/src/operations/rewind.rs | 5 ++- .../fabro-workflow/src/operations/start.rs | 4 +-- .../fabro-workflow/src/pipeline/retro.rs | 2 +- 32 files changed, 123 insertions(+), 83 deletions(-) diff --git a/lib/crates/fabro-agent/src/agent_profile.rs b/lib/crates/fabro-agent/src/agent_profile.rs index cc9c1e1d4..69d8418de 100644 --- a/lib/crates/fabro-agent/src/agent_profile.rs +++ b/lib/crates/fabro-agent/src/agent_profile.rs @@ -38,9 +38,9 @@ pub trait AgentProfile: Send + Sync { } fn context_window_size(&self) -> usize { - Catalog::builtin() - .get(self.model()) - .map_or(200_000, |m| usize::try_from(m.context_window()).unwrap()) + Catalog::builtin().get(self.model()).map_or(200_000, |m| { + usize::try_from(m.context_window()).unwrap_or(usize::MAX) + }) } fn register_subagent_tools( diff --git a/lib/crates/fabro-agent/src/subagent.rs b/lib/crates/fabro-agent/src/subagent.rs index a27cde76b..0db683603 100644 --- a/lib/crates/fabro-agent/src/subagent.rs +++ b/lib/crates/fabro-agent/src/subagent.rs @@ -195,7 +195,7 @@ impl SubAgentManager { let join_handle = self .agents .get_mut(agent_id) - .unwrap() + .expect("agent should still exist after status check") .task .take() .ok_or_else(|| Error::InvalidState(format!("Agent {agent_id} has no running task")))?; @@ -226,7 +226,10 @@ impl SubAgentManager { } // Phase 5: Store result in status and return clone - let agent = self.agents.get_mut(agent_id).unwrap(); + let agent = self + .agents + .get_mut(agent_id) + .expect("agent should still exist when storing task result"); agent.status = SubAgentStatus::Finished(task_result); match &agent.status { @@ -338,7 +341,7 @@ pub fn make_spawn_agent_tool( let max_turns = args .get("max_turns") .and_then(serde_json::Value::as_u64) - .map(|v| usize::try_from(v).unwrap()); + .map(|v| usize::try_from(v).unwrap_or(usize::MAX)); // Note: working_dir and model require session factory changes to wire through let mut session = session_factory(); diff --git a/lib/crates/fabro-agent/src/tool_execution.rs b/lib/crates/fabro-agent/src/tool_execution.rs index 9b9b133ee..d4ac99489 100644 --- a/lib/crates/fabro-agent/src/tool_execution.rs +++ b/lib/crates/fabro-agent/src/tool_execution.rs @@ -206,7 +206,7 @@ async fn execute_and_emit_one_tool_with_lookup( debug!(tool = %tc.name, hook_event = "pre_tool_use", "Calling tool hook"); let start = std::time::Instant::now(); let decision = hooks.pre_tool_use(&tc.name, &tc.arguments).await; - let elapsed = u64::try_from(start.elapsed().as_millis()).unwrap(); + let elapsed = u64::try_from(start.elapsed().as_millis()).unwrap_or(u64::MAX); debug!(tool = %tc.name, hook_event = "pre_tool_use", ?decision, duration_ms = elapsed, "Tool hook complete"); if let ToolHookDecision::Block { reason } = decision { diff --git a/lib/crates/fabro-agent/src/tools.rs b/lib/crates/fabro-agent/src/tools.rs index c3b1a8bc5..285f4c4ca 100644 --- a/lib/crates/fabro-agent/src/tools.rs +++ b/lib/crates/fabro-agent/src/tools.rs @@ -68,6 +68,15 @@ pub(crate) fn required_str<'a>(args: &'a serde_json::Value, key: &str) -> Result .ok_or_else(|| format!("Missing required parameter: {key}")) } +fn optional_usize_arg(args: &serde_json::Value, key: &str) -> Result, String> { + args.get(key) + .and_then(serde_json::Value::as_u64) + .map(|value| { + usize::try_from(value).map_err(|_| format!("Parameter {key} is too large: {value}")) + }) + .transpose() +} + #[must_use] pub fn make_read_file_tool() -> RegisteredTool { RegisteredTool { @@ -87,11 +96,8 @@ pub fn make_read_file_tool() -> RegisteredTool { executor: Arc::new(|args, ctx| { Box::pin(async move { let file_path = required_str(&args, "file_path")?; - let offset = args.get("offset").and_then(serde_json::Value::as_u64); - let limit = args.get("limit").and_then(serde_json::Value::as_u64); - - let offset_usize = offset.map(|v| usize::try_from(v).unwrap()); - let limit_usize = limit.map(|v| usize::try_from(v).unwrap()); + let offset_usize = optional_usize_arg(&args, "offset")?; + let limit_usize = optional_usize_arg(&args, "limit")?; let content = ctx .env @@ -281,10 +287,13 @@ pub fn make_grep_tool() -> RegisteredTool { .and_then(serde_json::Value::as_str) .unwrap_or("."); - let max_results = args - .get("max_results") - .and_then(serde_json::Value::as_u64) - .map(|v| usize::try_from(v).unwrap()); + let max_results = args.get("max_results").and_then(serde_json::Value::as_u64); + let max_results = max_results + .map(|value| { + usize::try_from(value) + .map_err(|_| format!("Parameter max_results is too large: {value}")) + }) + .transpose()?; let options = GrepOptions { glob_filter: args .get("glob_filter") @@ -402,10 +411,7 @@ pub(crate) fn make_list_dir_tool() -> RegisteredTool { executor: Arc::new(|args, ctx| { Box::pin(async move { let path = required_str(&args, "path")?; - let depth = args - .get("depth") - .and_then(serde_json::Value::as_u64) - .map(|v| usize::try_from(v).unwrap()); + let depth = optional_usize_arg(&args, "depth")?; let entries = ctx.env.list_directory(path, depth).await?; let lines: Vec = entries diff --git a/lib/crates/fabro-api/build.rs b/lib/crates/fabro-api/build.rs index c2c9166f6..df667b1d5 100644 --- a/lib/crates/fabro-api/build.rs +++ b/lib/crates/fabro-api/build.rs @@ -31,7 +31,7 @@ fn patch_nullable(value: &mut serde_json::Value) { let mut inner = variants.remove(0); inner .as_object_mut() - .unwrap() + .expect("oneOf collapse should leave an object schema") .insert("nullable".to_string(), serde_json::Value::Bool(true)); patch_nullable(&mut inner); *value = inner; @@ -129,12 +129,9 @@ fn patch_codegen_request_body_media_types(value: &mut serde_json::Value) { fn spec_path_from_manifest_dir(manifest_dir: &Path) -> PathBuf { manifest_dir - .parent() - .unwrap() - .parent() - .unwrap() - .parent() - .unwrap() + .ancestors() + .nth(3) + .expect("fabro-api manifest dir should be nested under /lib/crates/fabro-api") .join("docs/api-reference/fabro-api.yaml") } @@ -171,7 +168,7 @@ fn main() { let syntax_tree = syn::parse2::(tokens).expect("failed to parse generated tokens"); let formatted = prettyplease::unparse(&syntax_tree); - let out_dir = env::var("OUT_DIR").unwrap(); + let out_dir = env::var("OUT_DIR").expect("OUT_DIR should be set for build scripts"); let out_path = Path::new(&out_dir).join("codegen.rs"); fs::write(&out_path, formatted).expect("failed to write generated code"); } diff --git a/lib/crates/fabro-cli/src/commands/model.rs b/lib/crates/fabro-cli/src/commands/model.rs index c55b8da4e..f50bf43ed 100644 --- a/lib/crates/fabro-cli/src/commands/model.rs +++ b/lib/crates/fabro-cli/src/commands/model.rs @@ -158,7 +158,12 @@ fn print_models_table(models: &[Model], styles: &Styles) { .color_choice(color_choice(use_color)) .border(Border::builder().build()) .separator(Separator::builder().build()); - println!("{}", table.display().unwrap()); + println!( + "{}", + table + .display() + .expect("rendering the models table should succeed") + ); } fn model_test_row_from_status(model: &Model, status: &str, result_color: Color) -> ModelTestRow { diff --git a/lib/crates/fabro-cli/src/commands/run/output.rs b/lib/crates/fabro-cli/src/commands/run/output.rs index 8531b0e4b..95383e294 100644 --- a/lib/crates/fabro-cli/src/commands/run/output.rs +++ b/lib/crates/fabro-cli/src/commands/run/output.rs @@ -398,7 +398,13 @@ async fn print_assets_with_client( .separator(Separator::builder().build()); fabro_util::printerr!(printer, "\n{}", styles.bold.apply_to("=== Artifacts ===")); - fabro_util::printerr!(printer, "{}", table.display().unwrap()); + fabro_util::printerr!( + printer, + "{}", + table + .display() + .expect("rendering the artifacts table should succeed") + ); fabro_util::printerr!( printer, "{}", diff --git a/lib/crates/fabro-cli/src/commands/run/rewind.rs b/lib/crates/fabro-cli/src/commands/run/rewind.rs index dc7a1d989..b0423f949 100644 --- a/lib/crates/fabro-cli/src/commands/run/rewind.rs +++ b/lib/crates/fabro-cli/src/commands/run/rewind.rs @@ -64,7 +64,11 @@ pub(crate) async fn run( return Ok(()); } - let target = args.target.as_deref().unwrap().parse::()?; + let target_arg = args + .target + .as_deref() + .expect("rewind target should be present unless listing"); + let target = target_arg.parse::()?; rewind(&store, &RewindInput { run_id, @@ -80,7 +84,7 @@ pub(crate) async fn run( if cli.output.format == OutputFormat::Json { print_json_pretty(&serde_json::json!({ "run_id": run_id_string, - "target": args.target.as_deref().unwrap(), + "target": target_arg, }))?; } else { fabro_util::printerr!( diff --git a/lib/crates/fabro-cli/src/commands/run/wait.rs b/lib/crates/fabro-cli/src/commands/run/wait.rs index ffe6a34b2..49e8557a7 100644 --- a/lib/crates/fabro-cli/src/commands/run/wait.rs +++ b/lib/crates/fabro-cli/src/commands/run/wait.rs @@ -50,11 +50,10 @@ pub(crate) async fn run( if let Some(dl) = deadline { let now = std::time::Instant::now(); if now >= dl { - bail!( - "Timed out after {}s waiting for run '{}'", - args.timeout.unwrap(), - run_id - ); + let timeout_secs = args + .timeout + .expect("deadline should only exist when timeout is set"); + bail!("Timed out after {timeout_secs}s waiting for run '{run_id}'"); } time::sleep(interval.min(dl - now)).await; } else { diff --git a/lib/crates/fabro-cli/src/commands/runs/list.rs b/lib/crates/fabro-cli/src/commands/runs/list.rs index e33769198..5864f4da0 100644 --- a/lib/crates/fabro-cli/src/commands/runs/list.rs +++ b/lib/crates/fabro-cli/src/commands/runs/list.rs @@ -99,9 +99,7 @@ pub(crate) async fn list_command( None => match run.start_time_dt() { Some(start) => { let elapsed = now.signed_duration_since(start); - format_duration_ms( - u64::try_from(elapsed.num_milliseconds().max(0)).unwrap(), - ) + format_duration_ms(elapsed.num_milliseconds().max(0).cast_unsigned()) } None => "-".to_string(), }, diff --git a/lib/crates/fabro-cli/src/commands/system/prune.rs b/lib/crates/fabro-cli/src/commands/system/prune.rs index cb85235b6..0489198f1 100644 --- a/lib/crates/fabro-cli/src/commands/system/prune.rs +++ b/lib/crates/fabro-cli/src/commands/system/prune.rs @@ -44,12 +44,12 @@ pub(crate) fn parse_duration(s: &str) -> Result { bail!("empty duration string"); } let (num_str, unit) = s.split_at(s.len() - 1); - let num: u64 = num_str + let num: i64 = num_str .parse() .with_context(|| format!("invalid duration: {s}"))?; match unit { - "h" => Ok(chrono::Duration::hours(i64::try_from(num).unwrap())), - "d" => Ok(chrono::Duration::days(i64::try_from(num).unwrap())), + "h" => Ok(chrono::Duration::hours(num)), + "d" => Ok(chrono::Duration::days(num)), _ => bail!("invalid duration unit '{unit}' in '{s}' (expected 'h' or 'd')"), } } diff --git a/lib/crates/fabro-cli/src/commands/workflow/list.rs b/lib/crates/fabro-cli/src/commands/workflow/list.rs index 5f79e8205..44eee6f46 100644 --- a/lib/crates/fabro-cli/src/commands/workflow/list.rs +++ b/lib/crates/fabro-cli/src/commands/workflow/list.rs @@ -129,7 +129,13 @@ fn print_section( .color_choice(color_choice) .border(Border::builder().build()) .separator(Separator::builder().build()); - fabro_util::printerr!(printer, "{}", table.display().unwrap()); + fabro_util::printerr!( + printer, + "{}", + table + .display() + .expect("rendering the workflow table should succeed") + ); } fn truncate_str(s: &str, max: usize) -> String { diff --git a/lib/crates/fabro-cli/src/main.rs b/lib/crates/fabro-cli/src/main.rs index a8b8db3c9..8e31fee27 100644 --- a/lib/crates/fabro-cli/src/main.rs +++ b/lib/crates/fabro-cli/src/main.rs @@ -77,7 +77,7 @@ async fn main() { let start = std::time::Instant::now(); let (command_name, result) = Box::pin(main_inner()).await; - let duration_ms = u64::try_from(start.elapsed().as_millis()).unwrap(); + let duration_ms = u64::try_from(start.elapsed().as_millis()).unwrap_or(u64::MAX); let is_error = result.is_err(); // An empty command_name means no subcommand was invoked (landing was shown); diff --git a/lib/crates/fabro-config/src/effective_settings.rs b/lib/crates/fabro-config/src/effective_settings.rs index b16f2065b..8fb12a8f2 100644 --- a/lib/crates/fabro-config/src/effective_settings.rs +++ b/lib/crates/fabro-config/src/effective_settings.rs @@ -158,11 +158,9 @@ fn apply_local_daemon_overrides( } // Ensure a run.execution table exists so downstream consumers that check // for explicit dry-run defaults see a well-formed layer. - settings.run.get_or_insert_with(RunLayer::default); settings .run - .as_mut() - .unwrap() + .get_or_insert_with(RunLayer::default) .execution .get_or_insert_with(RunExecutionLayer::default); settings diff --git a/lib/crates/fabro-devcontainer/src/lib.rs b/lib/crates/fabro-devcontainer/src/lib.rs index 5411a4087..e1926152e 100644 --- a/lib/crates/fabro-devcontainer/src/lib.rs +++ b/lib/crates/fabro-devcontainer/src/lib.rs @@ -578,7 +578,7 @@ impl DevcontainerResolver { ports .iter() .filter_map(|p| match p { - serde_json::Value::Number(n) => n.as_u64().map(|n| u16::try_from(n).unwrap()), + serde_json::Value::Number(n) => n.as_u64().and_then(|n| u16::try_from(n).ok()), serde_json::Value::String(s) => { let s = s.split('/').next().unwrap_or(s); // strip protocol if let Some((_host, container)) = s.split_once(':') { diff --git a/lib/crates/fabro-graphviz/src/render.rs b/lib/crates/fabro-graphviz/src/render.rs index 9f7bb518f..2b1f485c6 100644 --- a/lib/crates/fabro-graphviz/src/render.rs +++ b/lib/crates/fabro-graphviz/src/render.rs @@ -20,13 +20,14 @@ const DOT_STYLE_DEFAULTS: &str = r##" edge [color="#666666", fontname="Helvetica", fontsize=10, fontcolor="#666666"] "##; -static RANKDIR_RE: LazyLock = - LazyLock::new(|| regex::Regex::new(r"rankdir\s*=\s*\w+").unwrap()); +static RANKDIR_RE: LazyLock = LazyLock::new(|| { + regex::Regex::new(r"rankdir\s*=\s*\w+").expect("hardcoded regex should compile") +}); static WHITE_BG_POLYGON_RE: LazyLock = LazyLock::new(|| { regex::Regex::new( r#"]*fill="white"[^>]*stroke="none"[^>]*/>|]*stroke="none"[^>]*fill="white"[^>]*/>"#, ) - .unwrap() + .expect("hardcoded regex should compile") }); /// Rewrite `rankdir=...` in DOT source. diff --git a/lib/crates/fabro-hooks/src/executor.rs b/lib/crates/fabro-hooks/src/executor.rs index c8405d651..e69ad09ae 100644 --- a/lib/crates/fabro-hooks/src/executor.rs +++ b/lib/crates/fabro-hooks/src/executor.rs @@ -35,6 +35,10 @@ static HOOK_RESPONSE_SCHEMA: LazyLock = LazyLock::new(|| { }) }); +fn duration_ms(duration: std::time::Duration) -> u64 { + u64::try_from(duration.as_millis()).unwrap_or(u64::MAX) +} + /// Trait for executing hooks via different transports. #[async_trait] pub trait HookExecutor: Send + Sync { @@ -149,7 +153,7 @@ impl HookExecutorImpl { } }; let context_json = serde_json::to_string(context).unwrap_or_default(); - let timeout_ms = u64::try_from(definition.timeout().as_millis()).unwrap(); + let timeout_ms = duration_ms(definition.timeout()); let mut env_vars = HashMap::new(); env_vars.insert("FABRO_EVENT".to_string(), context.event.to_string()); @@ -679,7 +683,7 @@ impl HookExecutor for HookExecutorImpl { }, }; - let duration_ms = u64::try_from(start.elapsed().as_millis()).unwrap(); + let duration_ms = duration_ms(start.elapsed()); HookResult { hook_name: definition.name.clone(), decision, diff --git a/lib/crates/fabro-llm/src/providers/anthropic.rs b/lib/crates/fabro-llm/src/providers/anthropic.rs index ecf6948c6..948ba9432 100644 --- a/lib/crates/fabro-llm/src/providers/anthropic.rs +++ b/lib/crates/fabro-llm/src/providers/anthropic.rs @@ -190,7 +190,7 @@ fn estimate_reasoning_tokens(content_parts: &[ContentPart]) -> Option { }) .sum(); if total_chars > 0 { - Some(i64::try_from((total_chars / 4).max(1)).unwrap()) + Some(i64::try_from((total_chars / 4).max(1)).unwrap_or(i64::MAX)) } else { None } diff --git a/lib/crates/fabro-sandbox/src/daytona/mod.rs b/lib/crates/fabro-sandbox/src/daytona/mod.rs index f4dffeb00..b86094323 100644 --- a/lib/crates/fabro-sandbox/src/daytona/mod.rs +++ b/lib/crates/fabro-sandbox/src/daytona/mod.rs @@ -39,6 +39,10 @@ async fn build_daytona_client( daytona_sdk::Client::new_with_config(sdk_config).await } +fn elapsed_ms(start: &Instant) -> u64 { + u64::try_from(start.elapsed().as_millis()).unwrap_or(u64::MAX) +} + /// Sandbox that runs all operations inside a Daytona cloud sandbox. pub struct DaytonaSandbox { config: DaytonaConfig, @@ -952,7 +956,7 @@ impl Sandbox for DaytonaSandbox { name: f.name, is_dir: f.is_dir, size: if f.size > 0 { - Some(u64::try_from(f.size).unwrap()) + u64::try_from(f.size).ok() } else { None }, @@ -982,7 +986,7 @@ impl Sandbox for DaytonaSandbox { .map_err(|e| format!("Failed to get process service: {e}"))?; tracing::info!( - elapsed_ms = u64::try_from(start.elapsed().as_millis()).unwrap(), + elapsed_ms = elapsed_ms(&start), "exec_command: process service acquired, starting select" ); @@ -1021,7 +1025,7 @@ impl Sandbox for DaytonaSandbox { let result = tokio::select! { res = exec_future => { tracing::info!( - elapsed_ms = u64::try_from(start.elapsed().as_millis()).unwrap(), + elapsed_ms = elapsed_ms(&start), ok = res.is_ok(), "exec_command: HTTP response received" ); @@ -1029,7 +1033,7 @@ impl Sandbox for DaytonaSandbox { } () = time::sleep(timeout_duration) => { tracing::info!( - elapsed_ms = u64::try_from(start.elapsed().as_millis()).unwrap(), + elapsed_ms = elapsed_ms(&start), timeout_ms, "exec_command: client-side timeout fired" ); @@ -1038,12 +1042,12 @@ impl Sandbox for DaytonaSandbox { stderr: "Command timed out locally".to_string(), exit_code: -1, timed_out: true, - duration_ms: u64::try_from(start.elapsed().as_millis()).unwrap(), + duration_ms: elapsed_ms(&start), }); } () = token.cancelled() => { tracing::info!( - elapsed_ms = u64::try_from(start.elapsed().as_millis()).unwrap(), + elapsed_ms = elapsed_ms(&start), "exec_command: cancelled via token" ); return Ok(ExecResult { @@ -1051,12 +1055,12 @@ impl Sandbox for DaytonaSandbox { stderr: "Command cancelled".to_string(), exit_code: -1, timed_out: true, - duration_ms: u64::try_from(start.elapsed().as_millis()).unwrap(), + duration_ms: elapsed_ms(&start), }); } }; - let duration_ms = u64::try_from(start.elapsed().as_millis()).unwrap(); + let duration_ms = elapsed_ms(&start); // The Daytona SDK returns combined output in `result` field. // Separate stderr isn't available in the simple execute_command API. diff --git a/lib/crates/fabro-sandbox/src/docker.rs b/lib/crates/fabro-sandbox/src/docker.rs index c61730ec8..33c0d6e1e 100644 --- a/lib/crates/fabro-sandbox/src/docker.rs +++ b/lib/crates/fabro-sandbox/src/docker.rs @@ -192,7 +192,10 @@ impl DockerSandbox { .await .map_err(|e| format!("Failed to inspect exec: {e}"))?; - let exit_code = i32::try_from(inspect.exit_code.unwrap_or(-1)).unwrap(); + let exit_code = inspect + .exit_code + .and_then(|code| i32::try_from(code).ok()) + .unwrap_or(-1); Ok((stdout, stderr, exit_code)) } diff --git a/lib/crates/fabro-server/src/demo/mod.rs b/lib/crates/fabro-server/src/demo/mod.rs index 4606c2271..7ca48a246 100644 --- a/lib/crates/fabro-server/src/demo/mod.rs +++ b/lib/crates/fabro-server/src/demo/mod.rs @@ -762,7 +762,7 @@ pub(crate) async fn get_aggregate_billing( use chrono::{DateTime, Utc}; fn ts(s: &str) -> DateTime { - s.parse().unwrap() + s.parse().expect("hardcoded demo timestamp should parse") } mod runs { diff --git a/lib/crates/fabro-server/src/run_files.rs b/lib/crates/fabro-server/src/run_files.rs index ab2a0e55d..7e4486a17 100644 --- a/lib/crates/fabro-server/src/run_files.rs +++ b/lib/crates/fabro-server/src/run_files.rs @@ -551,8 +551,10 @@ fn to_sha_wrapper(sha: &str) -> RunFilesMetaToSha { // constraint. Values we produce (via `git rev-parse HEAD`) always match. // `try_from` is expected to succeed; fall back to an empty wrapper on // the impossible failure rather than panicking. - RunFilesMetaToSha::try_from(sha.to_string()) - .unwrap_or_else(|_| RunFilesMetaToSha::try_from(String::from("0000000")).unwrap()) + RunFilesMetaToSha::try_from(sha.to_string()).unwrap_or_else(|_| { + RunFilesMetaToSha::try_from(String::from("0000000")) + .expect("hardcoded fallback sha should satisfy schema") + }) } /// Load the run projection from the store, returning a 404 for the IDOR-safe diff --git a/lib/crates/fabro-tracker/src/linear.rs b/lib/crates/fabro-tracker/src/linear.rs index cbe6fa0dc..9754f6ac1 100644 --- a/lib/crates/fabro-tracker/src/linear.rs +++ b/lib/crates/fabro-tracker/src/linear.rs @@ -57,7 +57,7 @@ fn normalize_issue(node: &Value) -> Result { .map(std::string::ToString::to_string); let priority = match node["priority"].as_i64() { Some(0) | None => None, - Some(n) => Some(i32::try_from(n).unwrap()), + Some(n) => Some(i32::try_from(n).map_err(|_| format!("Priority out of range: {n}"))?), }; let state = node["state"]["name"] .as_str() diff --git a/lib/crates/fabro-util/build.rs b/lib/crates/fabro-util/build.rs index c290e61f6..cdf150135 100644 --- a/lib/crates/fabro-util/build.rs +++ b/lib/crates/fabro-util/build.rs @@ -68,7 +68,7 @@ fn main() { let toml_content = fs::read_to_string(toml_path).expect("failed to read gitleaks.toml"); let config: Config = toml::from_str(&toml_content).expect("failed to parse gitleaks.toml"); - let out_dir = env::var("OUT_DIR").unwrap(); + let out_dir = env::var("OUT_DIR").expect("OUT_DIR should be set for build scripts"); let out_path = Path::new(&out_dir).join("rules_generated.rs"); let mut code = String::new(); diff --git a/lib/crates/fabro-workflow/src/event.rs b/lib/crates/fabro-workflow/src/event.rs index 2473463db..31116566c 100644 --- a/lib/crates/fabro-workflow/src/event.rs +++ b/lib/crates/fabro-workflow/src/event.rs @@ -2823,7 +2823,7 @@ fn epoch_millis() -> i64 { .duration_since(std::time::UNIX_EPOCH) .unwrap_or_default() .as_millis(); - i64::try_from(millis).unwrap() + i64::try_from(millis).unwrap_or(i64::MAX) } /// Listener callback type for workflow run events. diff --git a/lib/crates/fabro-workflow/src/graph.rs b/lib/crates/fabro-workflow/src/graph.rs index b4eb1390f..c38491028 100644 --- a/lib/crates/fabro-workflow/src/graph.rs +++ b/lib/crates/fabro-workflow/src/graph.rs @@ -33,7 +33,7 @@ impl NodeSpec for WorkflowNode { fn max_visits(&self) -> Option { self.0 .max_visits() - .map(|v| usize::try_from(v.max(0)).unwrap()) + .map(|v| usize::try_from(v.max(0)).unwrap_or(usize::MAX)) } } diff --git a/lib/crates/fabro-workflow/src/handler/command.rs b/lib/crates/fabro-workflow/src/handler/command.rs index 11f2cd3ec..be550ec27 100644 --- a/lib/crates/fabro-workflow/src/handler/command.rs +++ b/lib/crates/fabro-workflow/src/handler/command.rs @@ -10,8 +10,7 @@ use crate::event::{Event, StageScope}; use crate::outcome::{Outcome, OutcomeExt}; fn timeout_ms(node: &Node) -> Option { - node.timeout() - .map(|d| u64::try_from(d.as_millis()).unwrap()) + node.timeout().map(crate::millis_u64) } /// Shell-escape a string using `shlex::try_quote` (POSIX-safe). @@ -101,9 +100,7 @@ impl Handler for CommandHandler { &stage_scope, ); - let timeout_ms = node - .timeout() - .map_or(600_000, |d| u64::try_from(d.as_millis()).unwrap()); + let timeout_ms = node.timeout().map_or(600_000, crate::millis_u64); let env_vars = if services.env.is_empty() { None } else { diff --git a/lib/crates/fabro-workflow/src/handler/parallel.rs b/lib/crates/fabro-workflow/src/handler/parallel.rs index 8b1fec8c0..6927da366 100644 --- a/lib/crates/fabro-workflow/src/handler/parallel.rs +++ b/lib/crates/fabro-workflow/src/handler/parallel.rs @@ -501,8 +501,9 @@ impl Handler for ParallelHandler { .collect(); successful.sort_by(|a, b| a.id.cmp(&b.id)); if let Some(winner) = successful.first() { - let sha = winner.head_sha.as_ref().unwrap(); - git_merge_ff_only(&*services.sandbox, sha).await; + if let Some(sha) = winner.head_sha.as_ref() { + git_merge_ff_only(&*services.sandbox, sha).await; + } } } diff --git a/lib/crates/fabro-workflow/src/operations/rebuild_meta.rs b/lib/crates/fabro-workflow/src/operations/rebuild_meta.rs index 4b38c9e04..0ce2a8fe1 100644 --- a/lib/crates/fabro-workflow/src/operations/rebuild_meta.rs +++ b/lib/crates/fabro-workflow/src/operations/rebuild_meta.rs @@ -344,7 +344,10 @@ fn canonical_repo_root_for_path(path: &str) -> Result { fn resolve_prefix_matches(prefix: &str, matches: Vec) -> Result { match matches.len() { 0 => bail!("no run found matching '{prefix}'"), - 1 => Ok(matches.into_iter().next().unwrap()), + 1 => Ok(matches + .into_iter() + .next() + .expect("exactly one run should match when len is 1")), _ => { let mut msg = format!("ambiguous run ID prefix '{prefix}', matches:\n"); for run_id in &matches { diff --git a/lib/crates/fabro-workflow/src/operations/rewind.rs b/lib/crates/fabro-workflow/src/operations/rewind.rs index 158f7c4b9..844c6d8c1 100644 --- a/lib/crates/fabro-workflow/src/operations/rewind.rs +++ b/lib/crates/fabro-workflow/src/operations/rewind.rs @@ -339,7 +339,10 @@ pub fn find_run_id_by_prefix(repo: &Repository, prefix: &str) -> Result { match matches.len() { 0 => bail!("no run found matching '{prefix}'"), - 1 => Ok(matches.into_iter().next().unwrap()), + 1 => Ok(matches + .into_iter() + .next() + .expect("exactly one run should match when len is 1")), _ => { let mut msg = format!("ambiguous run ID prefix '{prefix}', matches:\n"); for m in &matches { diff --git a/lib/crates/fabro-workflow/src/operations/start.rs b/lib/crates/fabro-workflow/src/operations/start.rs index 1e736d309..5494a9ad1 100644 --- a/lib/crates/fabro-workflow/src/operations/start.rs +++ b/lib/crates/fabro-workflow/src/operations/start.rs @@ -258,13 +258,13 @@ async fn persist_terminal_engine_failure( run_store, final_status, failure_reason, - u64::try_from(duration.as_millis()).unwrap(), + crate::millis_u64(duration), None, ) .await; if let Err(err) = append_event_to_sink(event_sink, &run_id, &Event::WorkflowRunFailed { error: error.clone(), - duration_ms: u64::try_from(duration.as_millis()).unwrap(), + duration_ms: crate::millis_u64(duration), reason: status_reason, git_commit_sha: None, final_patch: None, diff --git a/lib/crates/fabro-workflow/src/pipeline/retro.rs b/lib/crates/fabro-workflow/src/pipeline/retro.rs index c61afe749..a6def63d4 100644 --- a/lib/crates/fabro-workflow/src/pipeline/retro.rs +++ b/lib/crates/fabro-workflow/src/pipeline/retro.rs @@ -109,7 +109,7 @@ pub async fn run_retro(options: &RetroOptions, dry_run: bool) -> Option { Err(anyhow::anyhow!("No LLM client available")) }; - let duration_ms = u64::try_from(retro_start.elapsed().as_millis()).unwrap(); + let duration_ms = crate::millis_u64(retro_start.elapsed()); match retro_result { Ok((narrative, response)) => { retro.apply_narrative(narrative);