Disclosure up front: I build the tool this article walks through. It is free and Apache-2.0, so my interest is "people find this useful" rather than "people pay me."
Give one agent session a goal and it will read your repository, decide what the work is, do the work, and then report that it went well. That is four jobs in one context window, and one of them is grading its own output. A better prompt does not fix the shape. A boundary between deciding and doing does, as long as you can inspect the boundary.
This post is about where I put that boundary and why, based on what I ran into while building Ordewell, a terminal tool that turns a goal into a plan and then runs each task through the coding agent CLI you already have.
Planning and executing are different workloads
Planning is research. You do not know the repository yet, so the work is to read enough of it to find the seams, decide the order, and spot which steps do not depend on each other. Most of what you read turns out to be irrelevant, which is what makes the job speculative.
Executing is the opposite shape. The context is one change in one place, and every token in it is meant to modify something. Adding a field to a config loader and rewriting an auth check do not want the same model, the same effort or the same mode.
Verification is a third job, and a single session cannot honestly do it for itself. A session that wrote the code has every reason to read its own diff the way it intended it rather than the way it landed.
So the line I drew is this. The planner decides and does not touch. The executor touches and does not decide. Verification reads evidence produced by someone else and neither of the other two gets a vote.
What the planner is, concretely
The planner is a separate process whose output is a plan, and whose job ends the moment that plan is committed. It has a shell, because a planner that cannot run git log or a test command is guessing rather than reading, but every command is classified before anything runs.
Commands that only inspect run silently. Commands that change state are refused, and refusal is not an approval prompt with a persuasive label on it. The message names the binary, says the planner is read only, and points the change at the plan instead:
planner wants to run:
npm install
refused, before any approval prompt exists:
"npm install" modifies state. You are a read-only planner.
Describe the change as a task instead, and the runner
executing the plan will make it.
Compound commands are the interesting case: the classifier reads the string the way a shell would, consuming quotes and backslashes and splitting on pipes and chaining, so a write hiding behind && is still judged as a write.
The planner does not have to be a separate vendor either. A coding agent you already have installed can serve as the planner, spawned in that agent's own read only mode. The plan contract does not move, only the thing producing the text does.
What the executor is handed, and what it is not
This is the half people skip. Each task runs in its own session, and that session does not inherit the planning conversation. It gets its own prompt, a numbered map of the plan with each task's state, the review note and the last few hundred characters of output from the tasks it directly depends on, and the instruction for emitting its completion marker.
It does not get the research, the questions the planner asked you, or the reasoning behind the order. Two consequences follow. A task's prompt has to stand on its own, because it is written for someone who was not in the room, which makes "as we discussed above" a bug rather than a shorthand. And a long running session cannot quietly redefine the job, because its scope arrived as text before it started drifting.
The plan is where the two roles meet
Both sides of the seam read the same object. The planner writes it, the executor reads one task out of it, and you sit in between with data rather than a transcript, so changing your mind is cheap. A task carries its own id, order, title, dependencies, type, prompt, runner, model, thinking effort and mode.
Nothing is executed until that object is committed. The moment to disagree with the decomposition is before the first file is touched, not after a failing test tells you the plan was wrong.
Honest limits
- The seam has a price, and it is a parsing contract. A typed plan means depending on a model to emit valid JSON, and models wrap it in prose, put it in a fence, or truncate mid stream. That boundary is defended rather than trusted: fences are stripped, the outermost balanced braces are scanned for while ignoring braces inside strings, a failure raises a typed parse error carrying the raw text, and an invalid plan re-prompts the same model to resend strict JSON. The review path fails safe, so a flaky reply fails a task rather than passing it silently.
- Read only is policy, not a kernel. The classifier is a denylist over a real shell, so a gap it did not anticipate is a real bug, tracked like any other. Only the Codex path gets an OS level sandbox, and it refuses to start without one.
- The planner plans what it read. Exploration is bounded, so a plan can be confidently wrong about code that was never opened. Explicit dependencies help, because a wrong edge shows up in the plan instead of staying buried in a session.
- For a small job, the seam is overhead. If one agent would have finished in a single context, splitting the work buys structure you did not need. Whether the split pays depends on the work.
- A separate planner is not a moat. Platforms are absorbing goal to plan to run as a native feature, and any of them can replicate this pattern. What holds up is the artifact and the evidence, not the trick.
- A CLI agent used as a planner inherits that CLI's quirks. One of the three replays your own message into its event stream with no role on the frame to filter by, which would have let a goal quoting JSON be parsed as the plan.
The loop, end to end
Plan first, read it, correct it, then run. The planner asks what it needs to know before it commits anything.
npm install -g ordewell
# reads the repository read only, then hands back the task list
ordewell plan --goal "Add rate limiting to the public API"
# nothing has run yet. this is where you disagree with the plan.
ordewell run
Source and longer design notes: github.com/ordewell/ordewell, including why a separate planner.
Top comments (0)