# Building a Reproducible Self-Improving Agent Harness

## What Inspired Me

I kept running into the same problem with self-improving-agent demos: a slick transcript showing an agent “rewriting itself,” but no way to reproduce the result or verify that anything actually improved.

The recursive self-improvement literature—including Lilian Weng’s writing on RSI via harnesses, STOP, and meta-harness research—pointed toward something more interesting than a flashy autonomous loop:

> **The leverage lives in the harness, not the model.**

If the machinery surrounding the model determines whether a change is safe, useful, learned, and retained, then that machinery is what deserves to be built carefully—and proven.

The guiding principle became **method over spectacle**.

Instead of creating one large autonomous loop that I could not trust, I built the system incrementally. Each stage had to earn the next by delivering:

- Tested, production-quality software
- A clear interface
- A standalone experiment demonstrating the new capability
- Offline and deterministic execution
- Reproducibility from a clean checkout in seconds
- No API key requirement

---

## How I Built It

The system is divided into stages. Each stage contains real code behind a defined interface, along with a standalone `proof_*.py` script that exercises the capability end to end using a deterministic `FakeProvider` or `FakeEmbedder`.

That design choice makes all **277 tests**, as well as every proof, reproducible without a live model. The underlying plumbing is exercised for real; only token generation is scripted.

### 1. Substrate — Stage 0

The foundation is a kernel loop that:

1. Assembles context
2. Calls a provider
3. Dispatches tool calls through a permission-gated registry
4. Executes those calls inside a sandbox
5. Traces every step

Because every later stage depends on this substrate, it received the most interface attention.

Key design choices include:

- Pointer-first tools
- A reject-invalid-edit format
- A `GraphStore` with interchangeable in-memory and SQLite backends behind a shared contract

### 2. Self-Modification — Stage 1

The agent never edits the live source tree.

Instead, it modifies an isolated copy of the harness. A gate then reruns the full test suite and the gate-task battery in fresh subprocesses against that copy.

A modification is accepted only if it causes no regression:

$$
\operatorname{accept}(m)
\iff
\operatorname{tests}(H_m)\text{ are green}
\land
p(H_m)\ge p(H_0)
$$

where:

$$
p(H)=\frac{1}{N}\sum_{i=1}^{N}
\mathbb{1}\left[\operatorname{task}_i\text{ passes}\right]
$$

Here:

- $H_0$ is the original harness
- $H_m$ is the harness after modification $m$
- $p(H)$ is the gate-task pass rate

A rejected modification leaves no trace. An accepted modification is appended to the graph as a modification node.

### 3. Promotion and Isolation — Stage 2

Once a change has been validated, it can be promoted into the running source tree.

The promotion process:

1. Snapshots `src/`
2. Performs an atomic `os.replace` swap
3. Runs a fresh-process smoke test
4. Rolls back automatically if the smoke test fails

Promotion is allowed only behind an isolator that watches for sandbox-escape attempts. If an escape attempt is detected, promotion is refused.

### 4. Cross-Session Learning — Stage 3

Each session records successful and unsuccessful approaches as embedded graph nodes:

- `memory` for strategies that worked
- `dead_end` for strategies that failed

A later session retrieves relevant nodes using cosine similarity:

$$
\operatorname{sim}(q,\ell)
=
\frac{q\cdot e_\ell}
{\lVert q\rVert\lVert e_\ell\rVert}
$$

The recalled information is injected into the agent loop on each iteration, allowing later sessions to benefit from earlier experience.

### 5. Graph-Coordinated Communication — Stage 4

Multiple agent processes coordinate through a minimal JSON-RPC 2.0 protocol over standard input and output.

The graph acts as the durable coordination substrate, providing:

- An agent registry
- An append-only peer-message log
- Threaded communication between agents

---

## What I Learned

### A Proof Is Worthless If It Cannot Fail

The lesson that reshaped the entire project was simple:

> **A proof is worthless if it cannot fail.**

Every proof therefore includes a negative control—a run that is expected to fail and demonstrably does.

The cross-session learning proof is the clearest example.

#### Session A

- Starts with an empty store
- Chooses the wrong approach
- Fails
- Records the approach as a `dead_end`

#### Session B

- Reopens the same store
- Runs with recall enabled
- Retrieves the relevant `dead_end`
- Avoids the failed approach
- Passes

The pass rate moves from:

$$
0.0 \rightarrow 1.0
$$

By itself, however, that proves very little. The second run could have succeeded through luck or simply because it was the second attempt.

#### Negative Control

A third session runs with recall disabled.

It still fails.

That control ties the improvement specifically to recall rather than to repetition or the mere existence of the store.

### Detection Is Not Prevention

Another important lesson was to state security boundaries honestly.

Stage 2’s `LocalIsolator` is a **tripwire**. It detects a write that reaches outside the jail, but it does not prevent the write from occurring.

Similarly, the sandbox limits execution time and output size, but it is explicitly **not a security boundary**.

Naming those limitations plainly in the code and documentation mattered more than presenting the system as more secure than it is. The moment a tripwire is described as a jail, someone may run an adversarial agent against it and get hurt.

---

## The Challenges I Faced

### Making Self-Modification Safe Without Making It Useless

A gate strict enough to be trustworthy tends to reject almost everything.

Defining non-regression correctly required the most iteration:

- The full test suite must remain green
- The battery pass rate must remain at or above baseline
- Evaluation must run in fresh subprocesses
- Evaluation must target an isolated copy
- A broken edit must never be able to corrupt the runner judging it

The result is that a gate or proof run cannot mutate the live working tree.

### Proving Causality Instead of Correlation

It is easy to demonstrate that an agent improved. It is much harder to demonstrate why it improved.

Every claim that “the agent got better” needed a control capable of ruling out the boring explanations. Designing controls that were meaningful rather than vacuous required substantial work.

### Keeping Parallel Work From Colliding

I organized deferred workstreams as file-disjoint units.

Each stage owns its package and communicates through defined interfaces. File disjointness became the integration contract that allowed independent components to land without overwriting or destabilizing one another.

### Resisting the Live Loop

The project’s most important open limitation is also deliberate:

> Every proof is currently offline and scripted.

Live, model-driven loops remain a deferred integration seam. They require a capable base model, but the harness should not depend on one for correctness or reproducibility.

I chose to:

- Keep deterministic offline proofs in continuous integration
- Place live runs behind feature flags
- Prove the surrounding machinery before connecting a live model

That was the central bet of the project:

> **Prove the machinery first. Plug in the model second.**

Built With

Share this project:

Updates