The producing side, step by step
- Turn end. Your loop (or a framework adapter) calls
on_turn_endwith the session id and turn payload. This returns immediately — nothing on this path blocks your agent. - Admission gates. Cheap producer-side controls run first: a hard per-process eval budget, per-session turn sampling, and a per-session rate limit. A newer turn supersedes the session’s in-flight evaluation.
- Trace discovery. A background task lists the session’s completed traces (newest-first from the platform, replayed oldest-first) and keeps a per-session seen-set, so each turn scores only what is new.
- Tiered evaluation. Tier 1 scores every new trace in one batched run and folds each into the trajectory gate; a gate breach escalates to Tier 2 on the last trace only; a confirmed Tier-2 breach may pull in Tier 3. See Evaluation & detection.
- Outcome verification. If you wired a verifier, it runs alongside the tier ladder (it depends only on the turn payload, so stacking its latency after the ladder would be wasteful) and contributes a synthetic
outcome_correctscore. - Validation cadence. Every handled report — healthy or not — feeds the candidate-rule validation engine: forward trials get their denominator, and a single-flight task evaluates open candidates. See Rule validation.
- Notice gate. Identical conditions are de-duplicated per session, recoveries are journaled, and a circuit breaker escalates notice storms to a single
needs_human. - Persistence. A surviving condition becomes a
DiagnosticNoticeinmailbox/pending/, a verbose dump undertraces/, and a journal event — and, when the severity isbreachand capture is enabled, a replayable eval case.
The repair side
Inside the samesettle() call, the repair agent picks up what the producing side posted:
- Episode assembly. Related notices from the same session and turn — those whose trace or signature evidence overlaps — are coalesced into one episode. One underlying failure gets one diagnosis, not three.
- Bounded diagnosis. A package-owned tool loop reads the notice, inspects a trace that notice named, and searches existing rules for prior coverage. It runs on its own model, its own session, and its own trace, so repair activity is never scored as task activity.
- One resolution. The episode ends with at most one candidate rule (
harness_rule_add, including which scope it belongs in) and exactly one acknowledgement. Timeout or failure acknowledges nothing and leaves the notice recoverable.
The consuming side
harness.system_context(session_id) returns one stable sentence to prepend to your system prompt: learned rules exist, and four read-only tools can reach them. That is all — no rule bodies, no scope index, no notice count. Building it reads nothing from the workspace, so no eval-derived text can reach a task prompt automatically.
Your agent acts, if it chooses to, through the task toolset: four read-only operations, delivered natively in Anthropic, LangChain, or OpenAI tool format.
The workspace on disk
Everything the harness knows lives under one directory — inspectable, greppable, and durable across process restarts:rules.md and everything under rules/ are derived views with no authority: they are regenerated from rules.jsonl on every rule mutation and at startup. Delete rules.md and it comes back; hand-edit it and the next rule write overwrites your edit. Read them freely; change rules through the store.
Journal event types
The journal is the harness’s cross-run memory. Every notable event is one JSON line:One healing cycle, end to end
- Turns 1–5 make no progress on the task:
task_completionsits flat and the trajectory gate closes its window →stall:task_completion. Tier 2 comes back clean, so this is an advisorytrendnotice — recorded, but nothing is captured to train on. - Turn 6 repeats an identical payment call. The gate fires again, Tier 2 escalates, and
tool_correctnessscores0.20→ abreachnotice withtrace_idandtier: 2. The session is captured as a replayable failure case. - Still inside that turn’s barrier, the repair agent takes the episode. It reads the notice, inspects the flagged trace, searches for prior coverage, finds none, and writes “Never call the payment tool twice without verifying the transaction status” — filing it under
payments, because the evidence is specific to that workflow. The rule enters as a candidate. - Validation replays the captured failure with the candidate discoverable and the rest of the provisional set hidden, so the delta is attributable.
task_completionimproves past the margin → promoted (rule_promote, validatorreplay). - Subsequent turns climb instead of stalling (recovery). Your task agent can now read that rule via
harness_rules_readwhenever it judges payment work relevant — and a regression run confirms the old failure isimprovedand the protected winunchanged.
examples/misc/closed_loop_self_heal.py prints a version of this you can run offline, with no credentials and no repair-model spend.
