Summary
Error type differs at every layer boundary: tool runner uses Result<String, String>, agent loop uses LibreFangError, LLM layer uses LlmError, kernel has a 2-variant KernelError pass-through, and the wider codebase mixes anyhow + thiserror within the same modules.
Evidence
crates/librefang-runtime/src/tool_runner.rs — 10+ Result<String, String> sites at lines 2253, 2264, 2365, 2377, 2402, 2436, 2457, 2493, 2548
crates/librefang-runtime/src/agent_loop.rs — LibreFangError with structured variants
crates/librefang-kernel/src/error.rs — KernelError has only LibreFang(#[from] LibreFangError) and BootFailed(String) — barely earning its keep
- 246
Box<dyn> / Arc<dyn> sites, 31 anyhow references, 7 thiserror derives
Why it matters
Error context is lost at every boundary crossing. Cannot pattern-match on "rate-limited LLM" vs "tool authorization failure" without string-sniffing. Retry/backoff in agent_loop.rs:3920 handle_retryable_llm_error already does exactly that. Operators get noisy logs with the same root cause appearing under different shapes.
Suggested direction
One structured error type per layer boundary, enforced: ToolError for tool-runner outputs (replace Result<String, String>), keep LlmError for drivers, LibreFangError as the application-level enum, drop KernelError (2-variant pass-through). Use anyhow only inside binaries / xtask, never in library crates. Add clippy::disallowed_types to ban anyhow::Result in librefang-kernel / librefang-runtime / librefang-api.
Reported by automated multi-agent code review (security / concurrency / architecture / performance / testing). Evidence is grep-verified file:line; please re-confirm before fixing.
Summary
Error type differs at every layer boundary: tool runner uses
Result<String, String>, agent loop usesLibreFangError, LLM layer usesLlmError, kernel has a 2-variantKernelErrorpass-through, and the wider codebase mixesanyhow+thiserrorwithin the same modules.Evidence
crates/librefang-runtime/src/tool_runner.rs— 10+Result<String, String>sites at lines 2253, 2264, 2365, 2377, 2402, 2436, 2457, 2493, 2548crates/librefang-runtime/src/agent_loop.rs—LibreFangErrorwith structured variantscrates/librefang-kernel/src/error.rs—KernelErrorhas onlyLibreFang(#[from] LibreFangError)andBootFailed(String)— barely earning its keepBox<dyn>/Arc<dyn>sites, 31anyhowreferences, 7thiserrorderivesWhy it matters
Error context is lost at every boundary crossing. Cannot pattern-match on "rate-limited LLM" vs "tool authorization failure" without string-sniffing. Retry/backoff in
agent_loop.rs:3920 handle_retryable_llm_erroralready does exactly that. Operators get noisy logs with the same root cause appearing under different shapes.Suggested direction
One structured error type per layer boundary, enforced:
ToolErrorfor tool-runner outputs (replaceResult<String, String>), keepLlmErrorfor drivers,LibreFangErroras the application-level enum, dropKernelError(2-variant pass-through). Useanyhowonly inside binaries /xtask, never in library crates. Addclippy::disallowed_typesto bananyhow::Resultinlibrefang-kernel/librefang-runtime/librefang-api.Reported by automated multi-agent code review (security / concurrency / architecture / performance / testing). Evidence is grep-verified file:line; please re-confirm before fixing.