Elanous

Blog · 2026.10.06

From one line to a pull request

The path of a single harness run — writing the goal · isolation · building · gates · review · landing, and what happens when it falls

Hand Elanous a single line — something like “put the cause in this error message too” — and a little later a pull request appears in the repository; when the conditions are met, it goes all the way to merge without a human hand. What carries the work in between is the “harness.” The internal documents that described the harness had swollen over a few months to hundreds of kilobytes, and nobody had read them to the end. So this time we redrew it as a one-page map. This post follows that map and walks outside developers through the path a one-line request travels — both the path when things go well and where it goes when it falls.

한 줄이 PR 이 되기까지 — 하니스 한 런의 길.

Beta — until the general release, Elanous is provided as is (Apache-2.0). Get news of the general release at elanous.ai.

The whole path on one page

First, the whole picture. Each box is unpacked in turn below.

The path of one harness run A one-line request from the terminal or TUI becomes a written goal, with authoring questions answered by the system itself. The run then goes either to an isolated local working tree or to a remote container (leased, image synced, credentials renewed ahead). The implementation child builds, the gate checks the changed-file scope, an unattended review reads the diff, and the supervisor gives its verdict. On a pass a pull request opens. If landing is open it merges and the working tree is reclaimed; if landing is frozen it is held and resumes after the freeze lifts. One-line request terminal · TUI Write the goal answers its own authoring questions Where does it run? local remote Isolated working tree Container lease · image · credentials renewed ahead Implementation child Gate changed-file scope Unattended review Supervisor verdict pass Open a pull request Landing frozen? open frozen Merge → reclaim the working tree Hold → resume after the freeze lifts
The whole path of one harness run, from a one-line request to a merge — or to a hold while landing is frozen.

1. The door — it comes in as one line

There are two doors in. One is for telling it what to do in a single line at the terminal; the other is for someone who has already written a goal document and hands that document over. Inside the TUI, a slash command or a fixed opening phrase that starts with “implement with the harness…” leads onto the same path. An ordinary conversation that does not use the opening phrase does not go to the harness. That split is intentional — there is no need to put even a light edit through isolation and review every time.

There used to be more doors. We have now closed two old ones; calling a closed door starts nothing and only returns a note saying “please come in through this door.” The reason for cutting the doors down is simple. Each door had slightly different default capabilities, so the same request produced different results depending on which door it came through.

2. Writing the goal — turning one line into “a document that can be judged”

The one line is not passed to implementation as it is. First it is turned into a “goal document.” A goal document holds what is being fixed, which files are the target, what signals “done,” and the boundaries that must not be touched.

There is one thing we learned from experience. If you give a child agent only “don’t do this” without “do this instead,” the child circles at a dead end. So in a goal we write a “path” and “a signal that can be disproved” rather than prohibitions.

While the goal is being written, the authoring step sometimes raises a question. It used to stop and wait for a person to answer it. Now, at all three doors, the authoring step reads the context, answers the question itself and carries on. It leaves only the questions a person has to answer.

3. Isolation — it does not touch anyone else’s working tree

Once the goal stands, a place to work is chosen. There are two cells.

  • Local — a separate working tree is created on the machine that made the request, and changes are made only inside it. The tree a person was using never becomes the child’s write area.
  • Remote container — a slot is leased from a pool made up of several machines. Each machine’s image is kept in line, and just before launch the coding model’s credentials are renewed ahead of time. That renewal was added to stop runs dying for the wrong reason because credentials expired mid-run.

The principle we kept here is “context is decided once and passed down only as arguments.” The moment a lower step starts reading “which folder am I in right now?” for itself, the isolation the parent set up is quietly broken.

4. Build · gate · review — a few laps around the same place

In the isolated place, the implementation child changes the code. When it finishes, the run goes straight to the gate.

The gate does not run the whole repository’s tests. It runs only the tests and type checks for the scope of the changed files, and what it asks is not “are there failures?” but “did this change cause a new failure?” Repositories whose full test suite is always green are rare. If failures that were already red get blamed on the child, the child spends all its rounds fixing things that have nothing to do with it. So every failure is labelled as one of “caused by this change · already there · unknown · precondition not met.”

Once the gate passes, an unattended review reads the diff. If the review raises a “must fix,” the run goes back to implementation. That is a rework round. A lap feeds back two kinds of things. Coming back from the gate, the failing tests and type errors go into the next round’s instructions; coming back from review, the list of things to fix does.

The review is not always right. So the child can “rebut” a must-fix, and a re-judgement that receives the rebuttal looks at it again. If every must-fix is dismissed, the run leaves the loop. Rounds have a cap (by default, two rework rounds).

The build, gate and review loop Implement leads to the gate. A new failure sends the run to a rework round; a pass goes to review. A must-fix from review goes to the child's rebuttal and a re-judgement: if all are dismissed the run goes to the supervisor verdict, if some remain it goes to a rework round. A rework round within the cap returns to implement; when the cap is used up or there is no progress it goes to the supervisor. A clean review also goes to the supervisor. The supervisor sends a pass to a pull request, no progress to splitting the scope and going again, a run stopped with work left to a harvest branch, a used-up budget or a person's call to the human gate with a decision card, and an exit reason not in the table to a proposal only. Implement Gate new failure Rework round within the cap pass Review must-fix Child rebuts → re-judged some remain clean all dismissed cap used up · no progress Supervisor verdict Pull request when: pass Split the scope and go again when: no progress Keep it as a harvest branch when: stopped with work left Human gate (decision card) when: budget used up · something a person must decide Leave it as a proposal only when: an exit reason that is not in the table
Laps around the same place: the gate and review send work back for rework, the child can rebut a must-fix, and the supervisor decides where every stop goes next.

5. The supervisor — it names “why did it stop?” in a word

Outside the rounds sits the supervisor. Every time a run stops, the supervisor records the reason as one word from a fixed set: things like “converged,” “no progress,” “round budget used up,” “model usage exhausted” and “review not observed.” There are fifteen of these words today, and the next cell each word goes to is fixed in a table as one of five: harvest, split the scope, review again on its own, human gate, or proposal.

The line in this table we care about most is the last one. When a new word that is not in the table appears, it is not read as a “pass”; it is left only as a “proposal.” That closes off the path by which something unknown gets quietly approved.

6. Landing — it is stopped in one place only, just before merge

A run that passes opens a pull request. Even with automatic merge switched on, the pull request is not merged right away. The gate does not look only at “the review passed.” It also checks whether the review actually ran, whether the reviewer read the diff to the end, and whether the evidence the goal asked for by name has been filled in. If any one of them is missing, the pull request stays open and is handed to a person.

There are times landing has to stop, such as while a release is being cut. It used to be checked separately on each landing path, which left gaps. Now every landing path checks for a freeze in one place, just before merge. A merge that arrives during a freeze is not thrown away; it goes into a hold queue and is merged in turn once the freeze lifts.

A run whose merge succeeds clears away the working tree and branch it made. If any one of the checks before clearing trips, nothing is deleted and both are left in place. That is so “something we could not confirm” is never read as “nothing there.”

7. When it falls — nothing is thrown away, and the reason is stated

Not every run ends in a merge. There are three paths on the failure side.

Rework and splitting the scope. When the gate or review catches something, the run first laps the same place again. If the rounds are used up with no progress, the supervisor does not fire the same goal again; its verdict is to split the scope and go again.

Harvest. The output of a run that stopped half-done is kept as a “harvest branch.” The next run, or a person, can pick it up from there. If it failed in a remote container, the result line is interpreted to show “why it actually died.” Remote failures used to be left as a single lumped-together line, so finding the cause meant digging through the logs by hand. Failure events are passed on to classification on the self-healing side, and that classification starts only on the host, not inside the remote container. That is so the same failure is not being fixed in several places at once.

Human gate — decision cards. When a run hits a wall it cannot get over on its own — the round budget used up, model usage exhausted, a step timing out — it goes to the human gate. A decision card with the options and the evidence goes up to a person. The rule we are aiming for is “a human gate only for payment · publication · credentials · irreversible decisions.” Today, budget and time-outs going to the human gate is a value we have left as an exception to that rule, and it is pinned in tests so that any change to it shows.

We are also building a “task worker” that makes this failure handling one step more automatic. It is split into a decision part, which takes the supervisor’s verdict and picks “the next move,” and an execution part, which carries that move out. If the same move fails twice it moves on to a different one, and the last move is a decision card. The execution part runs only as a shadow, without writing its results outside, and the decision part is not wired in yet.

What works today / what does not yet

ItemStatus
One-line request → writing the goal → isolated build → gate → unattended review → pull request → conditional automatic merge✅ Works today
Running in two cells, a local working tree or a remote container (remote: lease · image sync · credentials renewed ahead)✅ Works today
Answering authoring questions itself, at all three doors✅ Works today
Changed-file-scope gate · only “new failures” are put on the child✅ Works today
Supervisor exit words → next-cell table · unknown words left only as proposals✅ Works today
Freeze check just before merge on every landing path · resume after a hold✅ Works today
Reclaiming the working tree and branch after merge✅ Works today
Showing the real reason for a remote failure · keeping a harvest branch✅ Works today
Queueing requests that come in through the doors⚠️ Built in, but off by default
A lightweight authoring tier⚠️ Local only — for remote containers: Planned — not yet
Task worker execution part⚠️ Runs only as a shadow (does not write results outside)
Wiring in the task worker decision part (picking the next move itself after a failure)🔲 Planned — not yet
Narrowing the human gate to payment · publication · credentials · irreversible decisions🔲 Planned — not yet

Why we built it this way

There are not many cells on this path where a person really has to stand. Instead, if the child receives “a goal that can be judged,” the gate asks only about “failures it newly caused,” and the supervisor states in a word why a run stopped, then a person only needs to be at the cell where decision cards come up — that is our hypothesis. Whether it holds, we measure by whether the share of runs that go to the human gate goes down.

We cut the map down to one page, and decided that any change to the harness will update that map within the same change. The next time this path changes, we will write about it again, along with the cells that changed.

Source: github.com/ElanvitalAI/elanous · Docs: docs.elanous.ai

Beta — provided as is (Apache-2.0). Get update news at elanous.ai. Read also Redrawing the loop map with three cores.