pub struct OutcomeContract {
pub description: String,
pub allow_credentials: bool,
pub checks: Vec<ContractCheck>,
}Expand description
The verifiable definition of done for a coding session.
Fields§
§description: StringHuman summary of what success means.
allow_credentials: boolPermit this contract’s runtime-owned checks to use credentials.
Default false. When true, check execution omits only the coder’s
DenyCredentialAccess inspector; all other built-in and operator policy
remains. This never changes the model’s shell policy.
checks: Vec<ContractCheck>Checks that must all pass. Evaluated through the same policy-gated
shell inspector chain as the agent’s own calls, so a project
deny_tool rule refuses a “check” exactly as it would a tool call.
The credential posture differs — see the module docs.
Implementations§
Source§impl OutcomeContract
impl OutcomeContract
Sourcepub fn validate(&self) -> Vec<String>
pub fn validate(&self) -> Vec<String>
Structural problems that make a contract unusable. Empty = valid.
Beyond pure structure (empty/duplicate names, assertion-less checks)
this also rejects two failure modes seen live from small local models
(issue #168 follow-up): the prompt’s literal placeholder name leaking
through verbatim, and “toolchain-only” no-op commands like
cargo --version that prove nothing about the change. Both pass the
structural checks but make a contract that gates nothing, so they’re
surfaced as validation issues to drive the repair loop rather than
silently becoming the trust boundary.
Sourcepub fn repair_cosmetic_names(&mut self)
pub fn repair_cosmetic_names(&mut self)
Repair cosmetic naming problems weak local models commonly produce —
the literal unique_snake_case_label placeholder leaking through, an
empty name, or a duplicate — by assigning deterministic fallback labels
(check_1, check_2, …, suffixed on collision).
Dogfooding finding (2026-07-12): a small local model repeatedly echoed
the schema placeholder as a check name, and derive_contract’s bounded
repair loop couldn’t coax a better one out of it in 3 attempts, so the
entire coder session aborted before any coding over a name. A check’s
NAME is cosmetic — the command is the trust boundary — so a naming slip
must not be a hard failure. Substance problems (empty/toolchain-only/
assertion-less commands) are deliberately left for validate to drive
the repair loop, since those DO make the contract gate nothing.
Sourcepub fn strip_absolute_cd_prefixes(&mut self)
pub fn strip_absolute_cd_prefixes(&mut self)
Strip a hallucinated absolute-path cd prefix from each check command.
Checks run at the worktree root — the runtime sets CWD. But the derivation
model, trained on Docker-based coding harnesses, sometimes prefixes a check
with cd /repo && … (or another absolute mount that does not exist here).
That command then dies on cd: /repo: No such file or directory for EVERY
check, so a correctly-solved task self-verifies as failed and the session
ends “failed” — a false negative (surfaced by the coder A/B on a bytes2human
fix the coder got right in one iteration). We drop only a leading
cd <absolute> && / cd <absolute> ; (the runtime owns CWD, so it is
redundant at best and wrong at worst); a relative cd subdir && … is a
legitimate intra-repo move and is left untouched.
Sourcepub fn strip_exit_masking_pipes(&mut self)
pub fn strip_exit_masking_pipes(&mut self)
Drop a trailing output-limiting pipe (… | tail -20, | head -n 50,
| cat) from each check command.
A shell pipeline exits with the status of its last command, so
pytest … | tail -20 exits 0 no matter how badly pytest failed. The
derivation model adds these to keep output short, and thereby makes the
check structurally incapable of failing: expect_exit_zero ends up
asserting that tail ran, which it always does. The coder then
self-verifies green on broken code, reports needs_approval, and prints a
merge command — which is the exact opposite of this runtime’s promise that
success means “a real command exited 0”.
Worse, it is self-concealing: a masked check can never report the failure
that would let the repair loop notice its command is wrong, so the session
converges instantly on a lie. Surfaced by the coder A/B, where a gpt-5.4
arm derived python -m pytest tests/ -x -q 2>&1 | tail -20, went green in
31s without flask even importable, and lost every task to the manifest’s
(unpiped) contract.
Only output filters are stripped — tail/head/cat exist purely to
truncate and always succeed. A pipe into grep is left alone: its exit
status is a real assertion (“output contains X”), which is a legitimate
check the model may intend.
Trait Implementations§
Source§impl Clone for OutcomeContract
impl Clone for OutcomeContract
Source§impl Debug for OutcomeContract
impl Debug for OutcomeContract
Source§impl<'de> Deserialize<'de> for OutcomeContract
impl<'de> Deserialize<'de> for OutcomeContract
Source§fn deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>where
__D: Deserializer<'de>,
fn deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>where
__D: Deserializer<'de>,
Source§impl PartialEq for OutcomeContract
impl PartialEq for OutcomeContract
Source§impl Serialize for OutcomeContract
impl Serialize for OutcomeContract
impl StructuralPartialEq for OutcomeContract
Auto Trait Implementations§
impl Freeze for OutcomeContract
impl RefUnwindSafe for OutcomeContract
impl Send for OutcomeContract
impl Sync for OutcomeContract
impl Unpin for OutcomeContract
impl UnsafeUnpin for OutcomeContract
impl UnwindSafe for OutcomeContract
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<T> DeserializeOwned for Twhere
T: for<'de> Deserialize<'de>,
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