pub struct LoopOutcome {
pub passed: bool,
pub iterations: u32,
pub last_results: Vec<CheckResult>,
pub error: Option<String>,
pub failure: Option<LoopFailure>,
pub nomination: Option<NoChangeNomination>,
pub cost_usd: Option<f64>,
}Expand description
How a loop run ended.
Build these with LoopOutcome::green and LoopOutcome::lost rather than
a struct literal. The failure-is-None-exactly-when-passed invariant is
documented on the field and held at every site today, but nothing enforced
it — a struct literal lets the next site quietly state that a run both passed
and failed. The constructors make that unrepresentable.
Fields§
§passed: boolEvery contract check passed.
iterations: u32Iterations actually executed.
last_results: Vec<CheckResult>Check results from the final evaluation.
error: Option<String>Human-readable terminal error. None on a pass, and on a loss the loop
attributes to the work rather than the machinery — an exhausted budget
whose last run was clean leaves this empty and lets rpc render
“contract not satisfied after N iteration(s)”.
It is NOT empty merely because the budget ran out: an exhausted
LoopFailure::Infrastructure still fills this in, deliberately (see
the scraping note below).
Human-readable, and load-bearing beyond humans: car-cli’s coder-A/B
scrapes this text across a process boundary to split infra failures out
of the scored denominator (coder_ab::INFRA_MARKERS). Keep the
external agent '<id>' / foreman adapter '<id>' prefixes and the bare
cancelled spelling stable, or that split breaks silently.
failure: Option<LoopFailure>Typed reason the run is not green; None exactly when passed.
Exists so callers branch on a value instead of matching on error’s
prose — rpc’s engine fallback used to be an e != "cancelled" string
compare.
Descriptive, not prescriptive. It records what went wrong, not what
to do about it. Retry policy belongs to whichever loop produced the
value and knows its own budgets: external_loop retries an
Infrastructure failure it has budget for, while the same variant from
native_loop means the loop already retried internally and gave up. Do
not write if failure == Infrastructure { retry } at an outer level.
nomination: Option<NoChangeNomination>A no-change finding the loop nominated and the runtime accepted or
parked. Some means the run ended WITHOUT a diff and without that being
a failure — see super::no_change.
This is the one case where passed is false and failure is also
None, which relaxes the invariant stated above: failure is None
exactly when passed or nomination.is_some(). A nomination is
neither a green contract nor a loss, and forcing it into either is the
whole defect #1070 describes.
cost_usd: Option<f64>Metered inference spend for this run, when anything reported it.
None means unknown, not free — the native loop does not meter, and
conflating the two is how ab::ArmOutcome.cost_usd came to publish a
structural zero: cost_per_pass was computed as sum / passes over a
field nothing ever filled, so every external arm reported a cost of
exactly $0.00 as though it were measured.
Implementations§
Source§impl LoopOutcome
impl LoopOutcome
Sourcepub fn with_cost(self, usd: Option<f64>) -> Self
pub fn with_cost(self, usd: Option<f64>) -> Self
Attach metered spend. Separate from the constructors so adding cost accounting did not churn all 24 construction sites, and so a caller with nothing to report simply never calls it.
Sourcepub fn green(iterations: u32, last_results: Vec<CheckResult>) -> Self
pub fn green(iterations: u32, last_results: Vec<CheckResult>) -> Self
Every check passed. failure is None by construction.
Sourcepub fn reported(
finding: NoChangeNomination,
iterations: u32,
last_results: Vec<CheckResult>,
) -> Self
pub fn reported( finding: NoChangeNomination, iterations: u32, last_results: Vec<CheckResult>, ) -> Self
The run ended without a diff, and correctly so — the runtime accepted or
parked a no-change nomination. Neither green nor lost; see the
nomination field for why that third shape has to exist.
Sourcepub fn lost(
failure: LoopFailure,
error: Option<String>,
iterations: u32,
last_results: Vec<CheckResult>,
) -> Self
pub fn lost( failure: LoopFailure, error: Option<String>, iterations: u32, last_results: Vec<CheckResult>, ) -> Self
The run ended not-green, for failure.
error is the human-readable text, and stays None when the loss needs
no explanation beyond the checks themselves — rpc then renders
“contract not satisfied after N iteration(s)”. It is NOT decoration:
car-cli’s A/B scrapes it across a process boundary, so the
external agent '<id>' prefix and the bare cancelled spelling are load-
bearing where they appear.
Trait Implementations§
Source§impl Clone for LoopOutcome
impl Clone for LoopOutcome
Auto Trait Implementations§
impl Freeze for LoopOutcome
impl RefUnwindSafe for LoopOutcome
impl Send for LoopOutcome
impl Sync for LoopOutcome
impl Unpin for LoopOutcome
impl UnsafeUnpin for LoopOutcome
impl UnwindSafe for LoopOutcome
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
impl<ST, DT> CastableFrom<ST, Initialized, Initialized> for DT
impl<ST, DT> CastableFrom<ST, Uninit, Uninit> for DT
Source§impl<T> CloneToUninit for Twhere
T: Clone,
impl<T> CloneToUninit for Twhere
T: Clone,
impl<S, T> Duplex<S> for Twhere
T: FromSample<S> + ToSample<S>,
impl<T> ErasedDestructor for Twhere
T: 'static,
Source§impl<S> FromSample<S> for S
impl<S> FromSample<S> for S
fn from_sample_(s: S) -> S
Source§impl<T> Instrument for T
impl<T> Instrument for T
Source§fn instrument(self, span: Span) -> Instrumented<Self> ⓘ
fn instrument(self, span: Span) -> Instrumented<Self> ⓘ
Source§fn in_current_span(self) -> Instrumented<Self> ⓘ
fn in_current_span(self) -> Instrumented<Self> ⓘ
Source§impl<T> IntoEither for T
impl<T> IntoEither for T
Source§fn into_either(self, into_left: bool) -> Either<Self, Self> ⓘ
fn into_either(self, into_left: bool) -> Either<Self, Self> ⓘ
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 moreSource§fn into_either_with<F>(self, into_left: F) -> Either<Self, Self> ⓘ
fn into_either_with<F>(self, into_left: F) -> Either<Self, Self> ⓘ
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