Agent Harness #1 — From a Research Question to a Paper: Designing the Pipeline

Agent Harness, part 1. Series index

Systems that take a research question and return a paper already exist. Several of them have been published, benchmarked, and argued about. Building another one was never the point.

The question I actually wanted to answer was narrower. By the time I started, three open-source frameworks had become the default way people wrote multi-agent systems — CrewAI, AutoGen, LangGraph. Everybody picked one. Nobody seemed to have built the same non-trivial pipeline in all three and reported what the choice actually bought you. That was the contribution I was after: one long-horizon research pipeline, implemented three times, compared.

I built the CrewAI one first, because I had to have something working before there was anything to compare.

It took months, and by the time it ran end to end the ground had moved. All three frameworks had shipped breaking changes. A comparison of the versions I had started against would have described a state of the world that no longer existed, and would have kept decaying while I finished the other two ports. So I stopped.

What I had left was a single, working, unusually well-documented attempt: give it a research goal and a short config, and it plans an experiment, writes the code, runs it, analyses the result, and produces a paper — with a human approving at five points along the way. One person, one system, taken from an idea to something that actually ran.

That record seemed worth keeping, so I took it apart stage by stage. This series is that teardown: how it was designed, what those design decisions did and did not cover, and — by the end — what happened when I pointed the system’s own verification standard at my own write-up of it.

This first post is the architecture.

What automating research actually demands

There is a large and growing set of agent systems that complete tasks. Booking a flight, refactoring a module, filling a form. Research is a different shape, in three ways that turned out to drive every subsequent decision.

A task has a right answer; research does not. You can check a booking against the itinerary. You cannot check “is this the right experiment” against anything, which means the system cannot validate its own plan and something else has to.

A task takes minutes; research takes hours. A training run is not a request-response cycle. The harness has to stay alive across it, survive the machine going to sleep, and still know afterwards what happened.

A task that fails stops; research that fails produces output anyway. This is the one I did not see coming. If a flight booking fails you get an error. If an experiment fails, the next stage still has a result file to read, still has numbers in it, and still writes the paper. Failure does not interrupt the pipeline — it propagates through it, wearing the same clothes as success.

That third property is the seed of everything later in this series.

Seven stages

The pipeline I settled on:

question → plan → design → code → execute → analyze → write

In the implementation these collapse into five phase modules plus an exit gate. What matters more than the stage list is that each boundary forced a decision I did not anticipate having to make:

Stage The question it forced
plan / design If the system cannot judge its own plan, where does the human go, and what can they actually do — reject, or edit?
code A model is writing code that will be executed. What may it write, and what must it never be allowed to author?
execute A long run needs supervision. How do you tell “still working” from “hung” from “quietly dead”?
analyze When a stage fails, do you retry, escalate, or stop? What does the next stage get told?
write The paper stage reads the analysis. What stops it from writing up a number that was never measured?

Each of those is a post in this series. The last one is where it comes apart.

Phase modules, their boundaries, and the five human gates phase0_workspace phase1_planning phase2_coding phase3_execution phase4_writing exit_gate question intake plan · design code execute · analyze write Preflight clarification Plan approval Coding guidance Execution guidance Context injection boundary boundary boundary boundary boundary ● the five human gates - - - where state crosses, and where every later failure in this series lives

The five human gates are not evenly spread, and that is the point: two of them sit inside the execution phase, because that is where a long run can go wrong in ways nobody planned for. The dashed lines are the boundaries. Everything that broke later broke on one of them.

What was built, and what was not

The completed implementation is in CrewAI, and it works end to end. Some shape:

Module Lines
phase3_execution.py 1,342
phase2_coding.py 1,209
phase4_writing.py 657
phase1_planning.py 322
phase0_workspace.py 94
exit_gate.py 131

Around 14,600 lines across the CrewAI implementation, a React streaming UI on a FastAPI backend, five human-approval gates, and fourteen architecture decision records written as I went.

The honest other half of that table:

Component State
CrewAI pipeline, phases 0–4 Complete. Ran end to end; produced papers and metric files from a natural-language topic
React streaming UI Complete against the CrewAI backend
Human-in-the-loop gates (5) Complete
Fourteen ADRs Complete
Pre-registered benchmark protocol Written and committed. Never executed
AutoGen port Partial. Never ran end to end
LangGraph port Partial. Never ran end to end
Cross-framework benchmark results None

I am putting that second table in the first post deliberately, because it contains the thing I got wrong.

Stopping because the frameworks had moved was a judgement call I still think was right. But when I went back through the code much later, I found two further reasons the comparison could not have worked even if I had finished it — one in the implementation, one in the measurement design. Neither was visible to me at the time. Those are the next post, and the repository has carried the withdrawal since it was archived.

One thing I found while writing this

Writing this post meant counting lines I had not counted before, and one of them contradicts my own record. The repository’s evidence map says the largest phase does not import the framework, citing phase2_coding.py at 1,209 lines. phase2_coding.py does not import crewai — that part holds, and it is the striking part. But it is the second-largest phase. phase3_execution.py is 1,342 lines.

Small, and it does not change the finding. I am mentioning it because it is the first thing this series turned up, on the first pass, in a record I wrote myself and considered finished. It will not be the last.

Where this goes

The next few posts are design: what the framework was actually doing, how state crosses the boundaries between stages, what a model may and may not author, where the human sits, and what it takes to keep a multi-hour run observable. Then: what “success” even means to a harness, the failures that report success anyway, and finally the same standard applied back to my own numbers.

The transferable point of this one is smaller than the architecture diagram suggests: the number of stages is not the design. The contract between them is. Every failure later in this series lives on a boundary, not inside a stage.


The system described here is archived at MARS; the analysis is in agent-harness-anatomy. The archived runs were driven by OpenAI gpt-5.2.