fix(#2624): make hive-forge failures unmistakable on stderr

`fn main() -> Result<()>` let anyhow's Debug impl render failures with a
bare `Error:` header. hive-forge is almost always invoked from an agent's
bash task, where the completion wake points at the task's .out file - so a
failure that only writes to .err is easy to miss entirely (mara's "had no
clue it failed" on #2624).

Wrap the dispatch in a run() and own the failure path in main():
- prefix with the binary name (`hive-forge: FAILED: ...`) so the line is
  unmistakably ours in a mixed transcript,
- render with {:#} (alternate Display), which keeps the full context chain
  inline - plain Display would have dropped every `.context()` below the
  top one,
- return ExitCode::FAILURE explicitly rather than relying on the Termination
  impl.

Half of #2624: the other half (surfacing .err in the bash-mcp completion
when a task exits non-zero) is damocles's, per the issue thread.
This commit is contained in:
atlas 2026-07-25 20:46:16 +02:00 committed by mara
commit 1356a1f049

View file

@ -28,6 +28,7 @@ mod verbs;
use anyhow::{Context, Result};
use clap::{Parser, Subcommand};
use std::process::ExitCode;
#[derive(Parser)]
#[command(
@ -173,7 +174,24 @@ enum Verb {
CiRerun(verbs::ci_rerun::Args),
}
fn main() -> Result<()> {
/// Wrapper over [`run`] that owns how a failure reaches the operator.
///
/// `fn main() -> Result<()>` would format the error with anyhow's `Debug`
/// impl, which prints a bare `Error:` header. `hive-forge` is almost always
/// invoked from an agent's bash task, where stdout is what gets surfaced
/// first and stderr is easy to miss — so failures are prefixed with the
/// binary name to be unmistakably ours, and rendered with `{:#}`
/// (alternate `Display`), which keeps the full `context` chain inline
/// rather than dropping it the way plain `Display` would.
fn main() -> ExitCode {
if let Err(e) = run() {
eprintln!("hive-forge: FAILED: {e:#}");
return ExitCode::FAILURE;
}
ExitCode::SUCCESS
}
fn run() -> Result<()> {
let cli = Cli::parse();
let client = client::Client::from_env(cli.repo, cli.json, cli.forge)
.context("initialize forge client")?;