This page is a report, not a guide. It describes the design of the self-healing loop in the
lousho SDK and the evidence collected up to 2026-11-09. For how to use it, read Self-healing and self-improvement and Improving an agent. The SDK’s docs/improve.md is the full reference for the loop.Abstract
An agent’s behaviour depends on its model and on its harness: the instructions and skills that tell the model how to work. Lousho can change that harness from the agent’s own failed runs. The system has two layers. The first recovers a run while the agent serves traffic and never changes the agent. The second, the self-healing loop, has editor models propose edits to the instructions and skills. A gate tests each edit on fresh runs of training cases and held-out cases, and checks its cost. A replay canary tests it again. The loop then applies the edit (auto mode, the default) or queues it for a person (manual mode). After the write, a corpus guard and a sequential monitor roll the edit back if it hurts. Every change is recorded in a hash-chained audit log. Budgets, a lease and a kill switch bound the loop. We report four sets of results on a synthetic coding benchmark. Naming the failed check for the editors helped on one task and did not beat giving no name on another. An end-to-end command-line run kept the base harness when no edit helped. A live run of all eleven loop scenarios passed every attempt that tested something. But it ran one attempt per scenario where the go/no-go bars need 5 to 10, so its verdict is incomplete. A side experiment found a multi-agent layout too costly to be the default. Auto mode stays the default, and the loop stays experimental.1. Introduction
1.1 Motivation
Agents fail in two ways. Some failures are transient: a provider outage, a crash, a tool that times out. Retrying fixes them. Other failures repeat because the harness is wrong. The instructions miss a rule, or a skill teaches the wrong habit. Retrying does not fix those. Someone has to change the harness. Changing a harness by hand is slow. Changing it automatically is risky. An edit can fix the failure that triggered it and break a case that worked. It can also learn the check instead of the requirement, or carry text that an attacker planted in a tool result. A system that edits its own harness needs evidence before it writes, checks after it writes, and a way back.1.2 Contributions
- Two layers with a clear boundary. Run recovery finishes or stops runs and never edits the agent. The self-healing loop edits only
instructions.mdandskills/<name>/SKILL.md. - A noise-aware gate. Each edit is judged per training case on fresh runs, with held-out cases the editors never see and a cost check that compares only runs that pass on both sides.
- Checks before and after the write. A replay canary uses runs no gate decision used. A corpus guard and a sequential monitor roll back a harmful edit.
- Two approval modes. Auto mode writes after the canary. Manual mode queues the edit for a person. Both run the same checks.
- Harness² options. Parallel editors, k proposers, sequential rounds, a blind selector, an editor cache with a ledger, and rejection of edits that quote a failed check.
- A planted-harm benchmark. Eleven scenarios where the loop must fix, refuse or undo something, with go/no-go bars.
2. Problem setting
A harness here is an agent directory’sinstructions.md and its skills/<name>/SKILL.md files. Tools, permissions, limits, models, agent.ts and scripts are out of reach. A skill folder that holds any other file is read-only. An edit may leave at most 8,000 characters of always-on instructions.
A case is a recorded input and a check. A check is a synchronous function that returns pass, fail, or “cannot score”. On a fail it may name the check the answer missed. Cases come from the agent’s own failed runs in the local observe database, and only from runs of the harness revision that is live now.
The threat model has three parts:
- Noise. Agent runs are random. A small gate can pass a useless edit by chance.
- Collateral harm. An edit that fixes one case can break another.
- Injection. Run records hold text that anyone could have written, such as tool output or file contents. That text can try to steer the editors.
3. Method
3.1 Layer one: recovering a run
agent.recover() resumes interrupted runs from their last checkpoint. A failed run waits out a backoff that doubles with each failure, up to 15 minutes. After 10 failures in a row it is quarantined for a person. With retryWithDiagnosis, a run at the cap gets one more try: a fork named <id>.heal, resumed from the same step, with a note that quotes the error as data. After three provider failures in a row, the loop pauses and probes one run at a time. createProgressGuard() stops a run that repeats the same failing tool call. This layer never changes the agent.
3.2 Layer two: one improvement step
improveAgent() runs one step and returns a proposal or keeps the base. It writes nothing.
- It runs the training cases on the current harness. If they all pass, it stops.
- Two editors propose edits from the failed runs. The invariant editor fixes how the agent works, at the earliest point a run went wrong. The domain editor fixes what the agent knows. Each proposal is probed on the same cases.
- A composer merges what the probes support, or changes nothing.
- The gate reruns the current harness on fresh runs, so it never compares against the unlucky runs that triggered editing. At least one training case must pass at least 2 more runs in 3, and no training case may get worse. A held-out case that drops is rerun 3 more times on both sides. The edit is rejected if the pooled drop is at least 2 runs in 6 (with 3 repeats). Tokens per passing run may rise by up to 50%, compared only on cases that pass under both harnesses.
- If the composed harness fails, each editor’s own harness that improved a case is tried through the same checks, cheapest first.
.heal fork that passed gives a pair on the same input: the failed run and a retry that passed after a diagnosis. casesFromRecoveries() returns these pairs. The editors read the retry as untrusted evidence. A recovery is never a gate run and never makes a case protected, and it can never come from a held-out case.
3.3 Scaling the step: Harness² options
The step follows the contrastive propose, probe and compose design of Harness² and offers its scaling options. All are off or at k = 1 by default.3.4 Learning across cycles
After a cycle is decided (kept, rejected or rolled back), a reflection session reads the review card, the diff, the case verdicts and the outcome. It never reads a raw transcript. It writes at most one lesson to a per-agent library. A lesson is refused if it trips the injection heuristics, holds a link or HTML, is too long, or quotes a failed check. Editors read up to 20 lessons as untrusted data and cite the ones they use. When an edit is kept, cited lessons get a helped vote. When it is rolled back, they get a hurt vote. A lesson retires once its hurt votes outnumber its helped votes by two.4. System design
4.1 The cycle
One cycle runs per agent directory at a time.lousho improve <dir> runs one cycle. --watch runs them on a timer. A score regression seen by the observe daemon can also start one.
The replay canary runs each training case and each corpus case 6 times under the proposal, with repeat numbers the gate never used. Each training case must pass at least 5 of 6. The writer is the only code that writes harness files. It refuses the write if the directory changed since the cycle read it, checks the edit for injection again, saves the revisions before and after, and appends an apply audit entry.
4.2 Auto and manual modes
The modes run the same gate and canary. They differ in who writes and in a few defaults. Auto mode learns only from failures seen in two or more runs, and rejects an edit that quotes a failed check. Manual mode takes every failure and flags such an edit on the card. Automatic rollback stays on in both modes, because it only returns to a revision that was already checked. An opt-in traffic canary can serve the candidate to at most 25% of new sessions and at most 50 sessions before a promote. In manual mode it starts only after a person approves.4.3 Checks after the write
- Corpus guard. Every protected and fixed case runs 3 times under the new revision, with a rerun on a drop. On a regression, or if the guard cannot judge, the edit is rolled back. A watcher repeats the guard every 24 hours.
- Monitor. It compares real runs under the applied revision with runs under the one before. It waits for 20 runs on each side and looks at most three times, after 20, 40 and 60 runs. A regression is a pass-rate drop of more than 0.05 that is significant at a per-look z of 2.1, which keeps the overall false rollback rate under one-sided 5%. After 7 days without enough runs, the edit is marked
kept-unverified. - Rollback. It restores a saved revision through the same writer and makes a new revision. History is never rewritten. Every rollback starts a 24-hour cooldown.
4.4 Operations
The kill switch is checked before each cycle and again before the editors, the canary and the write. Every step that changes something appends a line to
.lousho/audit.jsonl. Each line holds the SHA-256 of the line before it, so lousho improve audit <dir> --verify finds an edited or removed line.
4.5 Safety and injection defences
- Every run record the editors read starts with a banner saying it is data, not instructions.
- Tool results sit between untrusted markers. Markers forged inside a run, nested or spaced, are removed.
- Text an edit adds is checked for prompt injection when the editor writes it and again at write time.
- Held-out cases never reach the editors. The reflection session sees them only as “held-out case 1”, “held-out case 2” and so on, and no recovery may come from a held-out case.
- Editors have file tools on an in-memory workspace, with no shell and no network.
- Model text that reaches a card, a notification or the audit log is plain text: no links, images or HTML.
5. Experimental setup
Benchmark. All experiments useexamples/heal-benchmark in the SDK repository. Its coding-agent fixes bugs in a small JavaScript project and is scored by hidden tests. A run fails if it edits a test or a file the task does not need. Every task is synthetic.
Models. The agent ran on qwen/qwen3.7-flash in the live loop run, in the end-to-end run and in stage 2 of the failure-name study. The editors ran on a separate model, stealth/crouton-alpha. Stage 1 and the W0 side experiment also used that model. It ran only on synthetic fixtures. The loop script checks before each attempt and each editor call that every run in its database belongs to the benchmark, and stops if not.
Metrics. A proposal is confirmed when it passes at least 5 of 6 fresh runs. pass^3 is how likely a case passes three runs in a row, averaged over cases. We also report requests and tokens.
6. Results
6.1 Naming the failed check
The check can return the name of the requirement a failed answer missed. We compared three conditions, 5 attempts each: the real name, none, and shuffled names taken from other checks. The plan’s bar asked that real names beat shuffled names by at least 3 proposals in 5.
On the easier task, real names and no names both hit the ceiling. Only a wrong name hurt. On the harder task, the real name helped and a wrong name stopped every proposal. The guidance that follows is in the SDK docs: name a check only when you know it.
6.2 End-to-end command-line run
lousho improve ran end to end on the Qwen agent with captured cases. The editors saw the real checks. Three training cases failed 0 of 3 under both the base and the composed harness, so the gate kept the base. This is the expected outcome when no edit helps.
6.3 Live loop proof (S29)
On 2026-11-09 we ranloop.ts --live over all eleven scenarios. Each scenario ran once, and planted-editor ran three times. Five scenarios that test the loop’s own mechanics used a scripted agent. The run made 3,675 requests, 766 agent runs and about 6.3 million tokens. No run was excluded and none errored.
The audit chain verified on all 13 attempt directories. Every attempt that tested something passed. But the bars need 10 attempts (5 for
true-fix, healthy and canary-traffic), so every bar except the audit bar reads “too few”. The report’s verdict is INCOMPLETE.
6.4 Readout (S30)
The plan’s rule was: flip the default to manual if a planted-harm bar (B:planted-editor, C: planted-forced) is missed. Neither was missed: every counted attempt passed. Neither was met either, because there were too few attempts. The decision was to keep auto mode as the default and keep the “experimental” label until the bars are run at full count.
7. Analysis
7.1 What the failure-name study isolates
The three conditions differ only in the text of the failure name. So the study is an ablation of one input to the editors. It shows that the signal matters on a hard task and that a wrong signal is worse than none. It does not show that the gate admits only good edits. The gate saw the same evidence in every condition.7.2 Mechanics versus behaviour
Five of the eleven live scenarios used a scripted agent. Those rows test the loop’s mechanics, such as the kill switch, the queue, the budget and the monitor. They do not test model behaviour. The live-model rows (true-fix, planted-editor, planted-forced, injection, healthy, canary-traffic) are the ones that say something about real edits, and each has one or two counted attempts.
7.3 A side experiment: agent layout (W0)
W0 asked whether a multi-agent layout should be the default for products. It ran 22 synthetic coding cases, 3 runs each, withstealth/crouton-alpha, in three arms: one flat agent, a flat-plus agent used to separate routing from content, and the multi-agent product.
The product cost 4.24 times the flat agent’s tokens, above the 1.3 times bar, so the verdict against flat was NO-GO: multi-agent is not the default. Against flat-plus it was GO, at 1.24 times the tokens. The flat agent failed only one case,
add-months, so the model was near the ceiling.
8. Limitations
- Too few attempts. The live proof ran 1 attempt per scenario (3 for
planted-editor). Its verdict is incomplete. It shows the loop can work end to end, not that it works reliably. - Synthetic tasks only. Every result comes from a small synthetic coding benchmark. Real agents and real traffic may behave differently.
- Few models. One agent model and one editor model. The editor model ran only on synthetic fixtures. Results may not transfer to other models.
- Scripted rows. Five live scenarios used a scripted agent and test mechanics only.
- A small gate can pass a useless edit. With 1 to 3 training cases and 3 repeats, the canary, the guard and the monitor have to catch it. Harm on traffic the corpus does not cover shows only after 20 runs.
- Low-traffic agents. They may never get a monitor verdict. Their edits stay
kept-unverified. - Model drift. A model change can look like a regression and roll back a good edit.
- Heuristic defences. The injection checks can be beaten.
- The stage 1 bar was missed. On
add-months, real failure names did not beat shuffled names by the planned margin.