use basis::RunReport;
pub(crate) const EXIT_OK: u8 = 0;
pub(crate) const EXIT_FAILED: u8 = 1;
pub(crate) const EXIT_USAGE: u8 = 2;
pub(crate) const EXIT_BOUNDED: u8 = 3;
pub(crate) fn exit_code<S>(report: &RunReport<S>) -> u8 {
match report.stopped_by {
Some(_) => EXIT_BOUNDED,
None if report.succeeded() => EXIT_OK,
None => EXIT_FAILED,
}
}
#[cfg(test)]
mod tests {
use super::*;
use basis::RunOutcome;
fn report(outcome: RunOutcome, stopped_by: Option<basis::Bound>) -> RunReport<()> {
RunReport {
session_id: "s1".to_string(),
model: "gpt-5".to_string(),
provider: "openai".to_string(),
final_message: None,
outcome,
stopped_by,
usage: basis::RunUsage::default(),
sink: (),
}
}
#[test]
fn a_finished_run_exits_zero() {
assert_eq!(exit_code(&report(RunOutcome::Ok, None)), EXIT_OK);
}
#[test]
fn a_tripped_bound_is_told_apart_from_a_failure_by_the_exit_code() {
let failed = report(
RunOutcome::Error {
message: "provider refused the request".to_string(),
},
None,
);
let bounded = report(
RunOutcome::Error {
message: "deadline exceeded".to_string(),
},
Some(basis::Bound::Deadline),
);
assert_eq!(exit_code(&failed), EXIT_FAILED);
assert_eq!(exit_code(&bounded), EXIT_BOUNDED);
assert_ne!(
exit_code(&failed),
exit_code(&bounded),
"a shell script must be able to tell the two apart"
);
}
#[test]
fn a_run_that_answered_on_a_spent_token_budget_still_exits_bounded() {
let answered = report(RunOutcome::Ok, Some(basis::Bound::TokenBudget));
let unanswered = report(
RunOutcome::Error {
message: "run completed without a final assistant message".to_string(),
},
Some(basis::Bound::TokenBudget),
);
assert_eq!(exit_code(&answered), EXIT_BOUNDED);
assert_eq!(
exit_code(&unanswered),
EXIT_BOUNDED,
"the same bound earns the same code whether or not prose came back"
);
}
}