One Task,
Three Passes

Getting useful work out of an AI agent

You’ve been told this changes everything.
It hasn’t worked yet.

Either
you haven’t really started. Opened it, typed something, got a shrug, closed it.
Or
you did start, and it produced something confident and wrong that took longer to clean up than to have written yourself.

Both are the correct reaction. The method is missing — nobody hands you the method, they hand you a text box.

This starts where the 101 stops

Assumed: you have run a task in plan mode and read the diff.

By the end you can:

  1. Explain why quality depends on context and specification, not model choice
  2. Locate yourself on the map and name your next step
  3. Judge what comes back instead of trusting how polished it looks
  4. Capture one thing that worked into a file your team can reuse

Fluency takes weeks of doing this on real work. A talk doesn’t buy it.

Most of this room is at 2 —
which is the normal place to be

1
No AI
2
Individual
ad-hoc use
3
Team-integrated
workflows
4
Semi-autonomous
+ guardrails
5
Fully
AI-native

Level 1 is behind you. Three passes over one task make Level 2 deliberate; the rest of the hour is 3, 4 and 5. A map is not a scorecard — it just tells you which direction is forward.

The thing that changed isn’t quality.
It’s the size of task it can finish.

~5 hours
of human work, on tasks a
frontier model finishes half the time
(interval: 2.8–12.1 hours)
Measured across 228 tasks.

That horizon doubled roughly every
7 months since 2019 — and every
3 months since 2024.

METR, Time Horizon 1.1, January 2026

The task: add retry with backoff

Our API client hammers a flaky upstream service.
Add retry with backoff.

Deliberately boring. Every one of you has done a version of it.

Hold in your head what a good answer looks like.

Pass 0: it’s good. Better than
most of us write by hand.

● Done — 18 tests pass. src/retry.js (new) — the policy, kept separate from transport so it's testable on its own: - Retries 408/429/5xx, request timeouts, and connection-level TypeErrors. 4xx returns straight to the caller. - Exponential backoff with full jitter — random(0, 200ms * 2^attempt), capped at 5s. Plain exponential backoff would have every caller that failed in the same blip retry in the same instant, which re-hammers an upstream that's already struggling. - Retry-After overrides the computed delay. If the server asks for longer than the cap, the response is returned instead of parking the request. - 3 attempts by default.
// Methods safe to replay: the upstream ends in the same state whether
// the request lands once or twice. POST is absent on purpose.
export const IDEMPOTENT_METHODS = new Set(['GET','HEAD','OPTIONS','PUT','DELETE']);

// Full jitter (random(0, backoff) rather than backoff itself) keeps
// concurrent callers from re-converging on the same retry instant.
export function backoffDelay(attempt, { baseDelay, maxDelay }, random = Math.random) {
  const ceiling = Math.min(maxDelay, baseDelay * 2 ** attempt);
  return Math.round(random() * ceiling);
}

// Retry-After is either delta-seconds or an HTTP date. Returns ms, or null.
export function retryAfterMs(response, now = Date.now()) { /* ... */ }

real output, one sentence of prompt — plus 18 passing tests it wrote unasked

So what decision did it make for you?

// The timeout is per attempt, so each retry gets a full budget.

You passed timeout: 10_000. Three attempts plus backoff is ~30 seconds. There is no total budget anywhere.

It didn’t get this wrong. It decided it, wrote a comment justifying it, and moved on.

Nobody flags that line in review. The comment makes it look considered.

Instructions are intentions —
every gap you leave, it fills

It’s probabilistic. When you write a rule, you are shifting a probability — not setting a flag.

Not a defect you can prompt your way out of. It’s the shape of the thing.

The loop, the window, and why it has never seen your code — all in the 101.

Pass 1 changes one thing:
write the intent down first

Same task. Same model. Same tool.

Before I open a session, I write down what I actually want — in a file.

Not a formal document. Eight lines.

Eight lines, four minutes — every one
a decision it made for me last time

# Retry with backoff — intent

- Retry only idempotent requests: GET, HEAD, PUT, DELETE. Never POST.
- Retry only on 429, 502, 503, 504, and connection/timeout errors. Never 4xx.
- Exponential backoff with full jitter. Base 100ms, cap 5s.
- Respect an overall deadline passed by the caller; never exceed it.
- Max 4 attempts total, including the first.
- Log every retry with attempt number and reason.

Same model, same tool, different output

Two things worth flagging: - request now throws typed errors (HttpError with .status, TimeoutError, DeadlineExceededError) instead of a bare Error. The message format for HTTP failures is unchanged, but any caller matching on error identity sees new classes. - The intent doesn't say what to do when the deadline cuts a retry sequence short, so the last real failure propagates (e.g. the 503), not a deadline error. DeadlineExceededError is thrown only when the budget was already spent before any attempt ran. Easy to invert if you'd rather deadline exhaustion always be distinguishable.
const RETRYABLE_STATUS = new Set([429, 502, 503, 504]);
const IDEMPOTENT = new Set(['GET', 'HEAD', 'PUT', 'DELETE']);

function backoffWithJitter(attempt) {
  const ceiling = Math.min(MAX_DELAY_MS, BASE_DELAY_MS * 2 ** attempt);
  return Math.random() * ceiling;            // full jitter
}

if (!IDEMPOTENT.has(method)) {
  return fetch(url, { method, ...options }); // never retry non-idempotent
}

excerpt — full source in the repo

What’s still missing from my spec?

What happens when retries are exhausted — throw, or degrade?

Does the deadline include the backoff sleeps?

Does any of this apply to streaming responses?

The specification is the bottleneck, not the model

The file outlives the session —
and context degrades before it fills

Across 18 frontier models, accuracy grows increasingly unreliable as input length grows.

And models do worse when the long context is logically coherent than when it’s shuffled.

More context is not free.

So you don’t hold state in a conversation.

You write it to disk and hand it over.

The file is the handoff.

Chroma Research, “Context Rot,” 2025

Pass 2: a second session that never
watched the first one work

When the work is done, open a fresh session and ask it to review the result.

With the spec. Without the conversation that produced the code.

Not the same session. The reviewer must not have watched the author work.

Both serious findings were in the one
requirement I’d put in bold

Eight findings. Six were minor — a missing log line, an unreachable constant, a case-sensitivity nit. Both of the serious ones were in the deadline logic.

HIGH 1 — the deadline check reserves no room for the next attempt, and destroys the last usable response. One of four attempts burns on a 12ms window that could never succeed.

HIGH 2 — only undefined is guarded. deadlineMs: null fails every request with zero fetch calls; NaN slips through and silently disables the deadline entirely.

attempt 1/4 failed (HTTP 503); retrying in 100ms
THREW Error: deadline of 130ms exceeded after attempt 2 (timeout after 12ms)

It works because it has your spec — not
because two agents are smarter than one

Revising its own reasoning with nothing external to check against doesn’t reliably improve — sometimes it gets worse.

Copies of a model debating each other produce consistency, not correctness.

Pass 2 works because the second session has something external to check against — your spec.

And it ran probes — every finding reproduced by executing code, not read off the page.

Huang et al., “LLMs Cannot Self-Correct Reasoning Yet,” ICLR 2024

You cannot tell how well this is going.
Neither can I.

Polish suppresses scrutiny. Across 9,830 conversations, the ones involving code were where people least questioned the reasoning (−3.1pp) or noticed missing context (−5.2pp). Anthropic AI Fluency Index, Feb 2026

The harness moves the number as much as the model. Swap the scaffold around a fixed model and benchmark scores move like a model generation — 21.8 points on one benchmark. Gorinova et al., June 2026

And this one happened to me. I asked an agent to check a 2026 result. It reported +18% — twice. The paper reports −18%, a CI crossing zero, and “very weak evidence.” METR, Feb 2026

The check has to be structural, not a feeling

Level 1 → 2 — done

You just did the whole thing by hand

1
No AI
2
Individual
ad-hoc use
3
Team-integrated
workflows
4
Semi-autonomous
+ guardrails
5
Fully
AI-native

Write the intent to a file before you start. Hand the file to a fresh session. Open a second session to check the result against the file.

That is Level 2, and it is genuinely most of the value. If you stop here you have not failed.

Level 2 → 3

Your practice lives in your head.
That doesn’t scale to a team.

1
No AI
2
Individual
ad-hoc use
3
Team-integrated
workflows
4
Semi-autonomous
+ guardrails
5
Fully
AI-native

You know to say “never retry a POST.” The next person doesn’t. Every session on your repo starts from zero.

The highest-leverage file in the repo
is the one the agent reads first

# AGENTS.md

## Things an agent would otherwise get wrong

- Never retry non-idempotent requests. POST is not safe to replay here;
  the orders endpoint is not keyed for deduplication.
- `pricing.internal` is behind a 200ms p99. Anything in the request path
  needs a deadline, not just a per-attempt timeout.
- Run `npm test` before claiming done. There is no CI on this repo yet.

Not documentation. The set of things that get got wrong.

Ours is one line long

@AGENTS.md

That is the entire CLAUDE.md. The tool-specific file is a pointer; the content lives in a tool-agnostic one.

In May, ours was a “comprehensive project reference.” We learned.

Most of these files make things worse

A February 2026 study ran four agents across 300 SWE-bench Lite tasks and a 138-task agent benchmark, with and without context files.

Auto-generated context files — the raw output of an init command — did not improve success rates at all, while raising cost by over 20%.

And when they stripped the README and docs out of the repo, context files started helping consistently — which tells you most of what people put in them was already discoverable.

ETH Zurich, February 2026

You have an instruction budget,
and it is smaller than you think

Instruction-following degrades as instruction count rises — benchmarks put the turning point somewhere around the low hundreds, and your file is not the first claim on that budget. The harness ships its own.

And adherence decays through a session: strong in the first couple of exchanges, measurably weaker by the tenth.

You’re not writing laws. You’re writing suggestions.

Every line you add is a line
that competes with the work

Bloated

“This is a Node.js project using ES modules. We value clean code and readability. Please write good tests. Follow SOLID principles…”

True, generic, and it displaces context that isn’t.

Sharp

“There is no CI on this repo. Run npm test before claiming done.”

Unguessable, specific, changes behaviour.

Test for every line: could a competent stranger have guessed it?

Context arrives in layers,
and you are budgeting, not tidying

Global — how you work, everywhere
Project — how this repo works (AGENTS.md)
Skills — loaded only when relevant
Tools / MCP — systems it can reach
Conversation — right now

The mistake is stuffing everything into the top layer.

A skill is context that loads itself
only when it’s needed

---
name: incident-response
description: Use when triaging a production alert, paging, or writing a
  postmortem. Covers severity levels, comms templates, and escalation.
---

# Incident response
(...900 lines the agent only reads when the description matches...)

The description is the whole game. It is the only part always loaded.

Precision in, precision out

BeforeAfter
Session cost$3.60$2.64
Wall time17m 10s9m 12s
New tokens~361k~169k
Lines the agent changed1,159 119

Same task, same repo. The only change was tightening skill descriptions and deferring bodies behind them. No rules were deleted.

Three ways to give it capability,
and they are not interchangeable

Command / SOP A repeated process “run our design phase”
Skill Domain knowledge “how we handle incidents”
MCP tool An action in another system “query the dashboard”

Most teams reach for the third when they needed the first.

MCP is the expensive one,
and it charges you up front

MCP tool definitions load eagerly — every tool, every session, whether you use it or not. One internal server measured at ~27k tokens, about 13.5% of the window, before any work started.

A skill costs you a name and one line until it fires.

So: MCP for what you need in most tasks. Skills for what you need sometimes.

Which one would you reach for?

“Every new service needs the same six setup steps”

“The agent should be able to read our on-call schedule”

“Nobody remembers our deprecation policy”

Command. MCP tool. Skill.

Level 2 → 3 — done

The team’s knowledge stops being
something people carry

What you were doing by hand: re-supplying the conventions, every session, forever.

What replaced you: AGENTS.md for the corrections that repeat, skills for knowledge that loads on demand, MCP only where the agent must reach another system.

Do this week: at the end of your next session, ask what you had to correct, and put those lines in AGENTS.md.

Level 3 → 4

You are still the first person
who reads everything

1
No AI
2
Individual
ad-hoc use
3
Team-integrated
workflows
4
Semi-autonomous
+ guardrails
5
Fully
AI-native

The work got faster. Your queue didn’t. Everything still routes through one pair of eyes — yours.

Things you ask for, and things
you actually enforce

Asked for

“Always run the tests before you say you’re done.”

A probability shift. Usually works. Not always.

Enforced

A hook that runs the tests on every edit, and blocks on failure.

A door that is locked.

Autonomy is only as safe as the things you enforce

Someone measured this properly:
five approaches, 600 runs each

Pass rateInput tokens
No instructions15.7%1,870
Simple instructions82.5%2,329
Full procedure in the prompt99.8%9,879
Graph / workflow orchestration80.8%3,116
Enforced by hooks 100.0% 3,346

Same tools, one task. Enforcement matched the best prompt at a third of the tokens — a hook fires at the decision, not ten turns earlier.

Clare Liguori, Strands Agents, March 2026 — one chatbot task, vendor-published

So put the reviewers before your eyes,
not after

Gate 1 — evaluate. Correctness, tests, security, style. Several reviewers, different jobs, in parallel.
Gate 2 — challenge. An adversary tries to refute gate 1’s findings. Are these real, or noise?
Gate 3 — safety. What did everyone else miss? Edge cases, error paths, concurrency.

By the time it reaches you, the question is “is this the right approach?” — not “is there a null check missing on line 47?”

The reviewers are told to forget
that you meant well

Reviewers run cold — the instruction to respect the author’s intent is suspended. They judge the code, not the plan.

Then a separate pass puts intent back and sorts every finding into real bug, over-flagged, or not a bug.

Two passes, because doing both jobs at once produces a reviewer that talks itself out of real findings.

Every loop needs a way to stop
that isn’t you noticing

A round cap. Three passes, then it stops and escalates rather than grinding.

A circuit breaker. If the same finding survives two attempted fixes, stop trying and hand it back.

A diminishing-returns breaker. If a round is producing less than the one before it, that is the signal to stop.

Without these, a loop either runs forever or quietly ships something it couldn’t fix.

The bill for getting this wrong
arrives quietly

Publicly reported: a runaway agent loop that burned tens of thousands of dollars in five hours. Another that ran eleven days before anyone noticed.

The mechanism is simple. Every retry re-reads the whole context. Iteration one costs a little. Iteration ten costs a lot. You pay for each failure again and again.

Agents don’t fail loudly. They retry, reformulate, and look busy the whole time.

If you can’t grep why it stopped,
you haven’t automated it — you’ve hidden it

DROP     gate=1 id=f-4a2 reason=refuted-by-adversary
DEMOTE   gate=2 id=f-7c1 reason=style-not-correctness
ESCALATE gate=3 id=f-9e0 reason=concurrency-merge-block
BREAKER  gate=1 id=f-4a2 reason=two-failed-revisions

Every decision the pipeline makes leaves a line. Including the decisions to not do something.

You can only let it run unattended
if the worst case is cheap

git worktree add ../task-3 -b task-3
cd ../task-3
# agent works here. If it destroys everything:
git worktree remove ../task-3 --force

Blast radius: exactly one task. Every mistake becomes a two-way door.

Isolated and unambiguous tasks
can run at the same time

step-1 ──┬──> step-3 ──> step-4
step-2 ──┘

The plan is a graph, validated by topological sort so nothing can depend on something that never runs. Code computes which tasks are ready, dispatches those, and recomputes when one finishes.

Note the order: specify, then isolate, then parallelise. Skip either of the first two and you get N confidently different answers.

Which of these could you safely
stop watching tomorrow?

“It always runs the tests”

“It never touches files outside the task”

“It writes good commit messages”

“It never force-pushes to main”

The ones you can enforce. Three of these are a hook or a worktree. One is a hope.

Level 3 → 4 — done

You stop being the first reader

What you were doing by hand: being the only quality gate, on everything, serially.

What replaced you: layered gates that run before you look, bounded so they stop, logged so you can audit, inside isolation so the worst case is recoverable.

Do this week: add one hook that enforces something you currently ask for.

Level 4 → 5

And here is where I stop
being able to sell you anything

1
No AI
2
Individual
ad-hoc use
3
Team-integrated
workflows
4
Semi-autonomous
+ guardrails
5
Fully
AI-native

Level 5 means agents owning operational loops end to end. We are not there, and I’d be lying if I implied otherwise.

What actually defines each level
is who verifies the work

Levels 1 to 3 all rely on a human saying “this is good.”

Levels 4 and 5 delegate verification itself to the system.

The prerequisite for more autonomy isn’t a smarter model.
It’s better proof.

The instruction that says
“rate this minor” does not hold

We wrote a reviewer that was supposed to emit a specific severity. Across repeated runs on fixed inputs, it emitted whatever it wanted.

The fix was not a better sentence. It was moving the rule out of the prompt and into a deterministic check.

Prose is a hint. Code is a guarantee.

We built guardrails so good
they made everything worse

Review depth was scaled by how dangerous a change looked. Sensible — until you notice that editing a prompt file counted as touching the pipeline, so the cheapest, most reversible changes got the heaviest review.

A one-line prose edit, fully undone by git revert, went through the full production gauntlet.

It ran like that for weeks before anyone noticed. Nothing failed. It just quietly made every small change feel like shipping to production.

And our “autonomous” mode
is less autonomous than it sounds

InteractiveAuto
Who invokes ityou type the commandyou type the command
Task sequencingone file at a timeone file at a time
Context resetyou clear ityou clear it
Quality gatesyou confirm it decides and logs why

Every row is identical except one. What we automated was judgment at the gates — not the sequencing, and not the human.

What none of this does

It does not decide what to build.

It does not know which of these two designs you will regret in a year.

It does not know that this service is the one that pages you at 3am.

It does not know when the honest answer is “we shouldn’t.”

Every parallel task is executing your design. Good execution amplifies a bad one.

How this got built

None of it was designed up front

You have just seen four levels in twenty-five minutes, in a tidy order.

That is not how it happened, and if you copy the tidy order you will build the wrong things first.

Three months, twenty-seven projects,
and it ends with a deletion

Started by adopting an open-source workflow. Not building one.

Then the parts that broke: the handoff contract between phases, because context was getting lost.

Then gates, because output was landing that shouldn’t have.

Then isolation and parallelism, because by then it was safe to run more than one.

Then measurement — what are these gates costing versus what are they catching?

Then deletion. The most recent project removed machinery, and the system got better.

One file’s life, in lines

324 lines in May → 1,022 at its peak in July → 777 the following week.

The cut removed no rules. It removed machinery.

We adopted the core.
We didn’t invent it.

Prompt-Driven Development — an open-source family of agent SOPs. strands-agents/agent-sop, Apache 2.0, published out of AWS’s open-source org, built by James Hood and a community of practitioners.

Summarise the codebase → hone the idea into a design → break the design into tasks → implement each task. The contracts between the phases are the valuable part — someone already worked out what a design document must contain for the next phase to consume it.

Our plugin wraps that with the gates and the graph. The wrapper is ours. The core isn’t, and you shouldn’t reinvent either.

Yours starts as one markdown file

Next time you finish a session where you got something genuinely useful done, open a fresh session and say: “Look at what I asked for in the previous session. Capture what worked into a reusable instruction set. Save it as workflow.md.”

Every layer you have seen today started exactly there and got refined each time it was reused.

Three rules that survived every level

1. Clear context is your best friend.

2. Deterministic work belongs to code, not agents.

3. Plan for humans, not agents — every phase leaves a file you can read, and a door back in.

So — where are you, and
what’s the one step?

1
No AI
2
Individual
ad-hoc use
3
Team-integrated
workflows
4
Semi-autonomous
+ guardrails
5
Fully
AI-native
Write the spec down before you start
Put the corrections in AGENTS.md
Enforce one thing you currently ask for
Measure what a gate costs you
Be honestly sceptical