Skip to main content

RunOutcome

Enum RunOutcome 

Source
pub enum RunOutcome {
    Success {
        steps: u32,
    },
    StepCapReached {
        steps: u32,
    },
    TimeBudgetExceeded {
        steps: u32,
    },
    CostBudgetExceeded {
        steps: u32,
    },
    Denied {
        steps: u32,
    },
    AwaitingApproval {
        request_id: i64,
        steps: u32,
    },
    Stalled {
        steps: u32,
    },
    Escalated {
        steps: u32,
        retryable: bool,
    },
    BudgetCeilingReached {
        steps: u32,
    },
    Refused {
        steps: u32,
    },
    Cancelled {
        steps: u32,
    },
    Finished {
        steps: u32,
    },
}
Expand description

Why a run stopped.

A run stopping is not a run failing — only one of these variants is success, and lumping the rest together as “it didn’t work” loses the difference between a run that needs more budget, one that needs a human, and one that needs a different task. The match is what a caller writes around every entry point:

use io_harness::RunOutcome;

match outcome {
    RunOutcome::Success { .. } => "verification passed; ship it",

    // Paused, not finished. The pending action is persisted under
    // `request_id` and this process may exit; whoever decides later calls
    // `resume_with_decision` with that id. Never retry the run from scratch.
    RunOutcome::AwaitingApproval { request_id: _, .. } => "ask a human, then resume",

    // Ceilings, and they mean different things. More steps or more time is a
    // knob; a token ceiling that keeps being hit is usually a task too big
    // for one contract.
    RunOutcome::StepCapReached { .. } | RunOutcome::TimeBudgetExceeded { .. } => "raise the bound and resume",
    RunOutcome::CostBudgetExceeded { .. } | RunOutcome::BudgetCeilingReached { .. } => "split the task",

    // The agent is going in circles and was already told once. Resuming
    // spends the rest of the budget proving it again — change the goal.
    RunOutcome::Stalled { .. } => "rewrite the contract",

    // Both are reported *after the fact*: the run that escalated or was
    // refused returned the `Err` itself, and a later `resume` reports this
    // instead of re-driving the loop.
    RunOutcome::Escalated { retryable: true, .. } => "transient provider failure; resume",
    RunOutcome::Escalated { .. } => "wrong key or bad request; fix it first",
    RunOutcome::Refused { .. } => "the provider's host was denied; widen the net policy",

    RunOutcome::Denied { .. } => "a human said no; the action never happened",
    // An observer returned `Flow::Cancel`. Finished cleanly, and still resumable.
    RunOutcome::Cancelled { .. } => "resume when you want it to continue",

    // Only a `Verification::None` run reaches this: it stopped because the
    // agent stopped, not because a ceiling did. Nothing checked the work —
    // read it, rather than shipping it the way a `Success` may be shipped.
    RunOutcome::Finished { .. } => "the agent is done; nothing verified it",
}

Every variant carries steps, which is how many steps completed — so a StepCapReached { steps: 12 } and a Success { steps: 12 } cost the same and only one of them produced anything. For what the run actually spent, use RunResult::summary.

Variants§

§

Success

Verification passed. steps is the step it passed on.

Fields

§steps: u32
§

StepCapReached

The step budget was reached before verification passed.

Fields

§steps: u32
§

TimeBudgetExceeded

The time budget was exceeded. steps is how many steps completed.

Fields

§steps: u32
§

CostBudgetExceeded

The cost (token) budget was exceeded. steps is how many steps completed.

Fields

§steps: u32
§

Denied

A human denied a deferred action on resume, so the run stopped without performing it. steps is how many steps completed.

Fields

§steps: u32
§

AwaitingApproval

An approver deferred a decision. The run is paused, not finished: the pending action is persisted under request_id and survives this process, so resume_with_decision can continue it once a human decides. steps is how many steps completed.

Fields

§request_id: i64
§steps: u32
§

Stalled

The agent stopped making progress: for StallPolicy::window consecutive steps it changed nothing in the workspace while repeating a tool call it had already made, and it had already been told once. The run stops here rather than spending the rest of its step budget proving it is stuck. steps is how many steps completed.

Fields

§steps: u32
§

Escalated

A provider failure exhausted its retries and the run was escalated to the caller. retryable is whether the failure was one another attempt could have survived — a rate limit or a 503 — as opposed to a wrong key or an unacceptable request. Reached through resume after the fact: the run that escalated returned the Err itself.

Fields

§steps: u32
§retryable: bool
§

BudgetCeilingReached

(sub-agent trees) The tree’s aggregate spend ceiling was crossed, so the whole tree halts — not this one agent hitting its own budget. steps is how many steps this agent completed before the tree-wide halt.

Fields

§steps: u32
§

Refused

The run never started, because reaching the provider needed network access the policy asked about and a human denied. The authorization happens before the run’s first step, so steps is normally 0.

Reached through resume after the fact: the run that was refused returned the Err itself, exactly as an escalation does. Added in 0.12.0 — "refused" was written to the store from 0.8.0 onward with no variant and no mapping, so resuming a refused run fell back into the loop and asked the human again.

Fields

§steps: u32
§

Cancelled

An Observer asked the run to stop, and it stopped — at the next step boundary rather than where the request landed, so no step was abandoned half-done. steps is how many steps completed before it stopped.

Added in 0.12.0 with Flow::Cancel, which is the first supported way to stop a run in flight: dropping the run’s future abandons it mid-step and leaves runs.status as running forever, which nothing can tell apart from a process that crashed. A cancelled run is finished rather than abandoned, and stays resumable — a resume reports this outcome instead of re-driving the loop.

Fields

§steps: u32
§

Finished

The agent finished. Only a Verification::None run reaches this: with no criterion to pass, an assistant turn that calls no tool is the run saying it is done, and the loop stops there.

Distinct from every ceiling on purpose. An unattended run that completed its work and one that ran out of steps both stop, and treating them alike is how a fleet operator ends up re-driving finished work — or worse, shipping the output of a run that never got there. steps is the step it finished on.

It is not a claim the work is correct. Nothing checked it; that is what choosing Verification::None means. A run with a criterion reports RunOutcome::Success, and that one is a claim — bounded by what the criterion checked and no wider.

Added in 0.17.0 with Verification::None.

Fields

§steps: u32

Trait Implementations§

Source§

impl Clone for RunOutcome

Source§

fn clone(&self) -> RunOutcome

Returns a duplicate of the value. Read more
1.0.0 (const: unstable) · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for RunOutcome

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Eq for RunOutcome

Source§

impl PartialEq for RunOutcome

Source§

fn eq(&self, other: &RunOutcome) -> bool

Equality operator ==. Read more
1.0.0 (const: unstable) · Source§

fn ne(&self, other: &Rhs) -> bool

Inequality operator !=. Read more
Source§

impl StructuralPartialEq for RunOutcome

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<ST, DT> CastableFrom<ST, Initialized, Initialized> for DT
where ST: ?Sized, DT: ?Sized,

Source§

impl<ST, DT> CastableFrom<ST, Uninit, Uninit> for DT
where ST: ?Sized, DT: ?Sized,

Source§

impl<T> CloneToUninit for T
where T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<Q, K> Equivalent<K> for Q
where Q: Eq + ?Sized, K: Borrow<Q> + ?Sized,

Source§

fn equivalent(&self, key: &K) -> bool

Checks if this value is equivalent to the given key. Read more
Source§

impl<Q, K> Equivalent<K> for Q
where Q: Eq + ?Sized, K: Borrow<Q> + ?Sized,

Source§

fn equivalent(&self, key: &K) -> bool

Compare self to key and return true if they are equal.
Source§

impl<Q, K> Equivalent<K> for Q
where Q: Eq + ?Sized, K: Borrow<Q> + ?Sized,

Source§

fn equivalent(&self, key: &K) -> bool

Checks if this value is equivalent to the given key. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T> Instrument for T

Source§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided Span, returning an Instrumented wrapper. Read more
Source§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> if into_left is true. Converts self into a Right variant of Either<Self, Self> otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> if into_left(&self) returns true. Converts self into a Right variant of Either<Self, Self> otherwise. Read more
Source§

impl<T> Pointable for T

Source§

const ALIGN: usize

The alignment of pointer.
Source§

type Init = T

The type for initializers.
Source§

unsafe fn init(init: <T as Pointable>::Init) -> usize

Initializes a with the given initializer. Read more
Source§

unsafe fn deref<'a>(ptr: usize) -> &'a T

Dereferences the given pointer. Read more
Source§

unsafe fn deref_mut<'a>(ptr: usize) -> &'a mut T

Mutably dereferences the given pointer. Read more
Source§

unsafe fn drop(ptr: usize)

Drops the object pointed to by the given pointer. Read more
Source§

impl<T> PolicyExt for T
where T: ?Sized,

Source§

fn and<P, B, E>(self, other: P) -> And<T, P>
where T: Sized + Policy<B, E>, P: Policy<B, E>,

Create a new Policy that returns Action::Follow only if self and other return Action::Follow. Read more
Source§

fn or<P, B, E>(self, other: P) -> Or<T, P>
where T: Sized + Policy<B, E>, P: Policy<B, E>,

Create a new Policy that returns Action::Follow if either self or other returns Action::Follow. Read more
Source§

impl<T> Read<Exclusive, BecauseExclusive> for T
where T: ?Sized,

Source§

impl<T> Same for T

Source§

type Output = T

Should always be Self
Source§

impl<T> Scalar for T
where T: 'static + Clone + PartialEq + Debug,

Source§

impl<SS, SP> SupersetOf<SS> for SP
where SS: SubsetOf<SP>,

Source§

fn to_subset(&self) -> Option<SS>

The inverse inclusion map: attempts to construct self from the equivalent element of its superset. Read more
Source§

fn is_in_subset(&self) -> bool

Checks if self is actually part of its subset T (and can be converted to it).
Source§

fn to_subset_unchecked(&self) -> SS

Use with care! Same as self.to_subset but without any property checks. Always succeeds.
Source§

fn from_subset(element: &SS) -> SP

The inclusion map: converts self to the equivalent element of its superset.
Source§

impl<T> ToOwned for T
where T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
Source§

impl<V, T> VZip<V> for T
where V: MultiLane<T>,

Source§

fn vzip(self) -> V

Source§

impl<T> WithSubscriber for T

Source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a WithDispatch wrapper. Read more
Source§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a WithDispatch wrapper. Read more