The first time I drew the ReAct loop on a whiteboard, it looked like a serious diagram: observation, thought, action, repeat. There was probably an arrow involved. Maybe a critique step. It felt like a research paper.

Then I watched Pi run it for ten minutes. The real diagram was: read file, edit file, run test, read error, edit again. Thought was not the interesting part. Tools were.

The first post was about why a model without tools can only do theater. The argument in this one is closer to: "the agent" is mostly just the name we give to the loop the tools make possible. Take the tools away and the loop collapses back into autocomplete with a shell account.

Add a second failure

tiny-ledger already had the decimal-price bug from part one. Give it a second one. Tax rounding:

cat > fixtures/cafe-tax.txt <<'EOF'
Coffee 3.50
Bagel 4.25
Tax 1.01
Total 8.76
EOF

At this point, the parser doesn't know about Tax or Total lines. The fixture also includes a deliberate near-miss — 3.50 + 4.25 = 7.75, tax 1.01, total should be 8.76. The numbers line up but the parser has no concept of summary fields, so it'll either drop them, treat them as line items, or invent something.

The exercise is to get to:

{
  items: [
    { name: "Coffee", price: 3.5 },
    { name: "Bagel", price: 4.25 },
  ],
  tax: 1.01,
  total: 8.76,
}

And the way Pi gets there is the lesson.

The loop is stupidly concrete

Try this prompt twice. Once without forcing structure, once with:

Add support for Tax and Total lines. Keep regular items separate from summary fields. Add a fixture and a test.
Before editing, list the files you need to inspect. After editing, run npm test and quote the relevant result.

The first one might work. The second one will show its work, which is the part that matters when you're trying to teach the harness shape rather than just get a green test.

The trace shape that emerges is mundane on purpose:

  1. read the parser.
  2. read the fixture.
  3. read the existing test.
  4. edit the parser to handle summary lines.
  5. write a new fixture file.
  6. write a new test.
  7. bash to run npm test.
  8. read the failure output.
  9. edit to fix the off-by-one.
  10. bash to re-run.
  11. Summarize, citing the test output.

Call it ReAct, CodeAct, or "agentic software engineering." The labels are fine; they just don't add any operations the harness wasn't already doing. The loop is read, edit, run, inspect, repeat.

Circular loop diagram showing read file, edit file, run test, inspect error, and repeat as the practical shape of a coding agent.

Shell as a first-class observation

Pi makes a useful distinction between commands that should enter the model's context and commands that shouldn't. The single bang and the double bang:

!npm test -- tax
!!npm test -- --watch

The first form runs npm test -- tax and sends the stdout/stderr back into the model context as an observation. The second runs npm test -- --watch and keeps the output out of the context — useful when the command is for you, or when the output is going to be a wall of noise the model doesn't need to chew on.

This matters because context is part of the harness budget. Every token you spend pushing a watcher tick into the prompt is a token you can't spend on the next thought. Dumping every intermediate result into the model is a category of self-harm where the agent slowly transmits a more and more distorted version of the work back to itself.

Pick the bangs intentionally. The model doesn't need to see the noise. It needs to see the signal that decides the next step.

What the loop is doing that the prompt can't

It would be tempting to read this and conclude that better prompts would replace the loop. They don't. Prompts can encode strategy ("inspect first, then edit"). They can't encode observation. A prompt cannot tell you whether the test passed. Only running the test can.

The trap is mistaking prompt quality for harness quality. Prompts are upstream of the work. Tools are the work. A great prompt that has no way to verify itself produces confident hallucination. A boring prompt with verification produces real diffs.

The model proposes. Tools observe and act. A harness binds them in a loop the human can interrupt. None of those parts is "the agent" by itself — the agent is the loop.

Exercise

For tiny-ledger:

  1. Run the tax-fixture prompt with no structure constraints. Note how many tool calls Pi makes and which ones produced new evidence.
  2. Run it again with the "inspect first, run test after" version. Diff the traces.
  3. Compare the two final diffs. Were the tests different? Did one version add a fixture? Did either version run --watch unnecessarily and bloat the trace?

Watch where evidence entered the loop. The diff you keep is the one whose evidence you trust.

Next primitive

The next post is about AGENTS.md — the way the repo teaches the harness what local truth looks like, and why project context beats generic prompting every time the prompt fights the file.