Skip to main content

Module contract

Module contract 

Source
Expand description

Outcome contracts — the verifiable definition of “done” for a coder session.

A contract is a set of shell commands that must pass inside the worktree. It is derived from the user’s intent by a model (with a bounded repair loop mirroring car-builder: generation is an injected closure, so tests run without inference) and then becomes the trust boundary for the whole session: whatever engine did the work — the native loop or an external CLI — the runtime re-runs the checks itself before asking for merge approval.

§What a contract can and cannot assert

The point-in-time assertion vocabulary is ContractCheck::expect_exit_zero and ContractCheck::output_contains. The latter normally checks a substring; its reserved $json:<pointer>=<json> form performs runtime-owned JSON value equality. That is not the boundary, though: the command is graded on its exit code, so a threshold against a live system ([ "$(…)" -lt 100000 ]) is a legal check today, and contract checks are deliberately not de-credentialed, which is why they run through WorktreeExecutor::run_check_shell rather than the model’s own run_shell.

Before/after claims are expressible too (Parslee-ai/car#1067). A check marked ContractCheck::baseline is a capture: it runs once, at session start, inside evaluate_contract_baseline’s pass over the unmodified worktree, and its output is kept (BaselineCaptures, built by collect_baseline_captures). A check carrying a ContractCheck::differential runs at every later evaluation and its output is compared against the named capture by the RUNTIME — exhaustively one of DifferentialExpect::Changed, DifferentialExpect::Unchanged (the control-group claim), or DifferentialExpect::DeltaWithin (numeric delta bounds). The command is arbitrary — a row count, a file digest, a curl body — so the external subject falls out of the command. Checks keep the model’s policy chain by default; an explicit contract opt-in omits only DenyCredentialAccess (car#1066). What this adds is the before/after structure. Both executions are runtime-owned and both results are stamped into the session’s events (the capture in contract_baseline, the comparison in check_completed): model claims count for nothing.

What is still missing is an evaluation point past delivery. Every evaluation is on this side of it: evaluate_contract_baseline against the unmodified worktree, then evaluate_contract once per repair round inside the loop, then evaluate_contract_within as the gate that admits delivery. Only car code-task runs that gate as a separate pass after the loop, holding the loop’s own verdict advisory; a daemon session finalizes straight off the loop’s last evaluation, which is the same call one layer down. Nothing runs after either way. So a claim about what a DEPLOY changed (“the row count fell after the deploy”) still belongs to the orchestrator wrapping the session, which owns the deploy and both sides of that window — the differential machinery here measures across the session’s work, not across a deploy the session never performs. See docs/car-code-task.md.

Structs§

CheckResult
Result of evaluating one ContractCheck.
ContractCheck
One acceptance check: a shell command run at the worktree root.
ContractDraftRequest
What one derivation attempt asks of the injected generator: the prompt, plus whether this attempt must be routed to a DIFFERENT model than the last one.
DifferentialCheck
A before/after claim: compare this check’s output against a named baseline capture.
OutcomeContract
The verifiable definition of done for a coding session.

Enums§

DifferentialExpect
The differential claims the runtime can decide. Matching is exhaustive everywhere — a new variant must be handled at every site or the build fails, which is the point.

Functions§

baseline_cannot_run
Checks whose COMMAND could not run at all, by name.
baseline_gates_nothing
Whether a baseline run means the contract gates nothing at all.
clamp_check_timeout
The timeout one check may take: its own, never more than the session has left on the wall clock.
collect_baseline_captures
Extract the baseline captures from a session-start baseline pass: the results of every check the contract marks ContractCheck::baseline.
derive_contract
Derive a contract from intent via the injected generate closure, with a bounded validate→repair loop.
evaluate_contract
Run every check through the worktree shell tool and report results.
evaluate_contract_baseline
Evaluate the contract against the unmodified worktree, before the first edit — the red-green baseline.
evaluate_contract_baseline_within
evaluate_contract_baseline, bounded by a session deadline. See evaluate_contract_within for why the baseline needs one at all.
evaluate_contract_with_baselines
evaluate_contract, with the session-start baseline captures that differential checks compare against. Callers that hold captures (the coder loops) use this; the capture-less signatures delegate here with an empty map, under which a differential check FAILS with a “never captured” message rather than silently passing — fail closed, never open.
evaluate_contract_within
evaluate_contract, bounded by a session deadline.
evaluate_contract_within_baselines
evaluate_contract_within, with baseline captures.
intent_targets_tests
Whether the intent is a “the tests fail, make them pass” task — the case where the contract must be grounded in the actually failing tests rather than guessed. Deliberately narrow: the observe-then-derive path runs the test suite, so it only fires when the intent clearly asks for it.
parse_test_failures
Parse pytest’s short-summary FAILED lines into node ids (tests/test_x.py::test_name). Best-effort and format-tolerant: the line is FAILED <node id> - <reason>, so the second whitespace token is the id. Deduplicated, order-preserving. Anything unrecognized yields nothing — the caller treats an empty result as “learned nothing”, not “no failures”.
summary_with_failures
Fold observed failing tests into the repo summary handed to derivation, so the model’s contract is grounded in what actually fails instead of guessed. Empty input returns the summary unchanged.

Type Aliases§

BaselineCaptures
Baseline captures by capturing check name — the before-values differential checks compare against. Built from the session-start baseline pass by collect_baseline_captures; the capture is the check’s CheckResult, whose output_tail (the 4 KiB tail) is the compared value, so keep a capture command’s output small and deterministic (a count, a digest, a status line — not a full dump).