Classify sandbox state-change rejections as transient infra

A Daytona "Sandbox state change in progress" rejection surfacing
through the pipeline lifecycle path ("Pipeline lifecycle operation
failed") matched no transient-infra hint, so the run failure was
categorized deterministic. The condition is a provider lifecycle
transition that finishes on its own — the definition of transient
infrastructure — and the deterministic label misinforms retry
machinery and anyone reading the failure.

Add two transient-infra hints: the provider rejection ("state change
in progress") and the bounded-wait timeout an activation reports when
a stop transition outlives its budget ("sandbox stop still in
progress").

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Bryan Helmkamp 2026-08-20 17:39:19 -04:00
parent 03c3412e51
commit f88df59163
No known key found for this signature in database

View file

@ -84,6 +84,8 @@ const TRANSIENT_INFRA_HINTS: &[&str] = &[
"cross-device link",
"invalid cross-device link",
"os error 18",
"state change in progress",
"sandbox stop still in progress",
];
const BUDGET_EXHAUSTED_HINTS: &[&str] = &[
@ -807,6 +809,18 @@ mod tests {
assert_eq!(err.failure_category(), FailureCategory::TransientInfra);
}
#[test]
fn engine_error_with_sandbox_state_change_cause_classifies_transient() {
let source = TestOuterError {
message: "Failed to start Daytona sandbox",
source: TestCause("Sandbox state change in progress"),
};
let err = Error::engine_with_source("Pipeline lifecycle operation failed", source);
assert_eq!(err.failure_category(), FailureCategory::TransientInfra);
assert!(err.is_retryable());
}
#[test]
fn handler_error_display() {
let err = Error::handler("LLM call failed");
@ -1281,7 +1295,7 @@ mod tests {
#[test]
fn transient_infra_hints_count() {
assert_eq!(TRANSIENT_INFRA_HINTS.len(), 38);
assert_eq!(TRANSIENT_INFRA_HINTS.len(), 40);
}
#[test]
@ -1450,6 +1464,25 @@ mod tests {
);
}
#[test]
fn classify_reason_sandbox_state_change_in_progress() {
assert_eq!(
classify_failure_reason(
"Pipeline lifecycle operation failed: failed to activate sandbox after node \
attempt survey: Failed to start Daytona sandbox: Sandbox state change in progress"
),
FailureCategory::TransientInfra
);
}
#[test]
fn classify_reason_sandbox_stop_still_in_progress() {
assert_eq!(
classify_failure_reason("Daytona sandbox stop still in progress after 120s"),
FailureCategory::TransientInfra
);
}
#[test]
fn classify_reason_500() {
assert_eq!(