use crate::agentloop::stop::TerminalStatus;
pub const EXIT_CODES: &str = "1.0";
pub const SUCCESS: i32 = 0;
pub const GENERIC: i32 = 1;
pub const USAGE: i32 = 2;
pub const PARTIAL: i32 = 3;
pub const INTEL_UNAVAILABLE: i32 = 4;
pub const REFUSED: i32 = 5;
pub const MCP_REQUIRED_DOWN: i32 = 6;
pub const BUDGET: i32 = 7;
pub const DEADLINE: i32 = 124;
pub fn once_exit(status: TerminalStatus, partial: bool) -> i32 {
use TerminalStatus::*;
match status {
Completed => {
if partial {
PARTIAL
} else {
SUCCESS
}
}
Refused => REFUSED,
ExhaustedSteps | ExhaustedTokens | Deadline => BUDGET,
Stalled | LoopDetected => PARTIAL,
Cancelled => GENERIC,
Crashed => GENERIC,
}
}
pub const SIGKILL_EXIT: i32 = 137; pub const SIGTERM_EXIT: i32 = 143;
pub fn pod_failure_intent(code: i32) -> &'static str {
match code {
SUCCESS => "complete",
USAGE | REFUSED => "terminal",
PARTIAL | BUDGET | DEADLINE => "policy",
GENERIC | INTEL_UNAVAILABLE | MCP_REQUIRED_DOWN => "retriable",
SIGKILL_EXIT | SIGTERM_EXIT => "infra",
_ => "retriable",
}
}
pub fn apply_budget_remap(code: i32, budget_exit_code: Option<i32>) -> i32 {
match (code, budget_exit_code) {
(PARTIAL | BUDGET, Some(remapped)) => remapped,
_ => code,
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::agentloop::stop::TerminalStatus::*;
#[test]
fn budget_remap_touches_only_partial_and_budget() {
assert_eq!(apply_budget_remap(PARTIAL, Some(0)), 0);
assert_eq!(apply_budget_remap(BUDGET, Some(0)), 0);
assert_eq!(apply_budget_remap(BUDGET, Some(1)), 1);
for code in [
SUCCESS,
GENERIC,
USAGE,
INTEL_UNAVAILABLE,
REFUSED,
MCP_REQUIRED_DOWN,
DEADLINE,
] {
assert_eq!(
apply_budget_remap(code, Some(0)),
code,
"code {code} must never be remapped by --budget-exit-code"
);
}
assert_eq!(apply_budget_remap(PARTIAL, None), PARTIAL);
assert_eq!(apply_budget_remap(BUDGET, None), BUDGET);
}
#[test]
fn mapping_matches_table() {
assert_eq!(once_exit(Completed, false), SUCCESS);
assert_eq!(once_exit(Completed, true), PARTIAL);
assert_eq!(once_exit(Refused, false), REFUSED);
assert_eq!(once_exit(ExhaustedSteps, false), BUDGET);
assert_eq!(once_exit(ExhaustedTokens, false), BUDGET);
assert_eq!(once_exit(Deadline, false), BUDGET);
assert_eq!(once_exit(Stalled, false), PARTIAL);
assert_eq!(once_exit(LoopDetected, false), PARTIAL);
assert_eq!(once_exit(Cancelled, false), GENERIC);
assert_eq!(once_exit(Crashed, false), GENERIC);
}
#[test]
fn codes_are_distinct_and_in_documented_bands() {
let table = [
SUCCESS,
GENERIC,
USAGE,
PARTIAL,
INTEL_UNAVAILABLE,
REFUSED,
MCP_REQUIRED_DOWN,
BUDGET,
DEADLINE,
];
for (i, a) in table.iter().enumerate() {
for b in &table[i + 1..] {
assert_ne!(a, b, "exit codes must be distinct");
}
}
assert!(table.iter().all(|&c| (0..=124).contains(&c)));
}
#[test]
fn pod_failure_intent_matches_the_contract_table() {
assert_eq!(pod_failure_intent(SUCCESS), "complete");
assert_eq!(pod_failure_intent(GENERIC), "retriable");
assert_eq!(pod_failure_intent(USAGE), "terminal");
assert_eq!(pod_failure_intent(PARTIAL), "policy");
assert_eq!(pod_failure_intent(INTEL_UNAVAILABLE), "retriable");
assert_eq!(pod_failure_intent(REFUSED), "terminal");
assert_eq!(pod_failure_intent(MCP_REQUIRED_DOWN), "retriable");
assert_eq!(pod_failure_intent(BUDGET), "policy");
assert_eq!(pod_failure_intent(DEADLINE), "policy");
assert_eq!(pod_failure_intent(SIGKILL_EXIT), "infra");
assert_eq!(pod_failure_intent(SIGTERM_EXIT), "infra");
}
#[test]
fn pod_failure_intent_is_total_over_the_contract_and_defaults_safely() {
let intents = ["complete", "terminal", "retriable", "policy", "infra"];
for code in [
SUCCESS,
GENERIC,
USAGE,
PARTIAL,
INTEL_UNAVAILABLE,
REFUSED,
MCP_REQUIRED_DOWN,
BUDGET,
DEADLINE,
SIGKILL_EXIT,
SIGTERM_EXIT,
] {
assert!(
intents.contains(&pod_failure_intent(code)),
"code {code} mapped outside the documented intent set"
);
}
assert_eq!(pod_failure_intent(99), "retriable");
assert_eq!(pod_failure_intent(-1), "retriable");
}
#[test]
fn intent_never_authors_a_retry_rule_for_a_terminal_or_infra_code() {
for code in [USAGE, REFUSED, SIGKILL_EXIT, SIGTERM_EXIT] {
assert_ne!(
pod_failure_intent(code),
"retriable",
"code {code} must not be authored as a retry rule"
);
}
}
#[test]
fn exit_codes_contract_version_is_frozen_at_one_zero() {
assert_eq!(EXIT_CODES, "1.0");
}
#[test]
fn once_exit_never_returns_success_for_a_non_completed_status() {
for s in [
Refused,
ExhaustedSteps,
ExhaustedTokens,
Deadline,
Stalled,
LoopDetected,
Cancelled,
Crashed,
] {
assert_ne!(
once_exit(s, false),
SUCCESS,
"{s:?} must not look like success"
);
}
}
}