A Deterministic Pipeline for AI Pair Programming That Works
An AI coder is powerful but drifts without gates. Here is the plan, build, verify, ship pipeline that wraps it, with state files and plan freeze.
Handing a coding task to an AI assistant with the instruction "just do everything" fails in a predictable way. The model is capable, often more capable than you expect on the narrow slice in front of it, but it has no sense of where the work ends. It loses context between steps. It wanders off the original goal to fix something it noticed. It writes code that looks right and ships it with no test between the writing and the deploy. When something breaks, you cannot tell which of the last forty edits caused it, and you cannot cleanly undo.
The fix is not a better prompt. It is a harness. You wrap the AI's work in a deterministic pipeline that a human designs and controls: review, implement, verify, document, deploy, each with an explicit gate that stops the run when it fails. The model supplies the creativity inside each stage. The pipeline supplies the discipline around it. This post describes how that harness is built, why every stage needs a gate, and the specific case where skipping the whole thing is the right call.
Why an unguided AI drifts
The failure modes are consistent enough to name. Understanding them is what tells you which gate to build.
Context loss comes first. An AI assistant works inside a finite window of recent conversation. On a long task that window fills, and the earliest decisions fall out of it. The model that started by agreeing to a narrow scope no longer remembers the scope by the time it is deep in the code. It is not lying to you when it contradicts an earlier decision. It genuinely cannot see it anymore.
Scope creep comes second. Ask for a bug fix and the model will often notice three adjacent things that could be improved, and improve them, because each one looks locally reasonable. Every individual change is defensible. The sum is a diff four times the size you asked for, touching files you never intended to open, with the actual fix buried inside it.
Missing verification comes third and hurts most. Left alone, an AI writes code and reports success based on the code looking correct, not on the code running correct. Looking correct and being correct diverge exactly where the interesting bugs live: an off-by-one, a wrong filter, an assumption that held locally and failed in the deployed environment. Without a gate that actually executes the code, the model's confidence is untethered from reality.
Poor reversibility ties the others together. When work arrives as one large undifferentiated change with no record of what was decided or why, rolling back means unpicking a knot. You cannot revert a decision you never wrote down.
None of these is a reason to distrust the model's raw ability. They are reasons to distrust unstructured delegation. The power is real. Direction and gates are what turn it into shipped work you can stand behind.
The pipeline in five stages
The harness is a fixed sequence of stages. The order is not decorative. Each stage produces the input the next one needs, and each ends in a gate that must pass before the run continues.
| Stage | The AI does | The gate checks | On failure |
|---|---|---|---|
| Review | Reads context, writes a plan, names the risks | Plan is concrete, scope is bounded, a human approves | Stop, revise plan |
| Implement | Writes code against the frozen plan only | Code compiles, types check | Stop, fix or roll back |
| Verify | Runs tests, checks invariants | Tests pass, invariants hold | Stop, do not proceed to deploy |
| Document | Records what changed and why | Change log matches the actual diff | Stop, reconcile |
| Deploy | Ships through the normal release path | Health checks pass post-deploy | Roll back |
Review is where the thinking happens and the cheapest place to catch a mistake. The model reads the relevant code, writes down what it intends to do, and lists what could go wrong. A human reads that plan and approves it or sends it back. Approving a plan is far cheaper than reviewing a finished diff, because a plan is short and a diff is long, and a wrong plan caught here costs minutes instead of a rollback.
Implement turns the approved plan into code, and nothing but the approved plan. Verify is the gate that separates this pipeline from unguided delegation: it executes the code and refuses to advance if the tests fail. Document leaves a durable record so the next person, or the next AI session, can reconstruct the reasoning. Deploy ships through the same release path a human would use, with the same post-deploy checks, and rolls back on failure rather than leaving a half-shipped state.
State lives in files, not in the model's memory
An AI assistant's working memory is volatile. Close the session, exceed the context window, or hit an error that forces a restart, and everything the model was holding is gone. If your pipeline's state lives only in that memory, a reset means starting over, and starting over on a task that was half deployed is dangerous.
So the pipeline writes its state to files. Current stage, decisions made, items completed, items remaining, all of it lives on disk, updated as the work proceeds. The model reads this state at the start of every step and writes to it at the end. The volatile context becomes a cache of the durable state, not the source of truth.
pipeline-state:
task: "add rate limiting to the public API"
stage: verify # review -> implement -> verify -> document -> deploy
plan_frozen: true
decisions:
- "token bucket, 100 req/min per key, chosen over sliding window for simplicity"
- "limit stored in existing cache layer, no new dependency"
progress:
- [x] middleware written
- [x] unit tests for bucket refill
- [ ] integration test under concurrent load
failures:
- none
The payoff shows up at the worst moment. A session dies mid-task. A new one starts with none of the prior context. It reads the state file and knows exactly where the work stood: the plan is frozen, implementation is done, one verification step remains. It resumes from there instead of guessing, or worse, redoing finished work and undoing something correct. State in files is what makes an AI-driven task survive the interruptions that AI-driven tasks are especially prone to.
Plan freeze stops the scope from creeping
After the review stage produces an approved plan, the pipeline freezes it. Freeze means the scope is locked: while implementing, the AI may not add items to the plan or drop items from it. It builds what was agreed, no more and no less.
This directly targets the scope creep failure. Without a freeze, the model's habit of noticing and fixing adjacent things runs unchecked, and the diff bloats. With a freeze, that same instinct hits a wall. If the model, mid-implementation, decides another change is needed, it does not simply make it. The change has to be recorded as an explicit amendment to the plan, which a human can see and approve or reject.
The rule is not that scope can never change. Real work uncovers real surprises, and refusing to adapt is its own failure. The rule is that scope changes are visible and deliberate instead of silent and accumulating. A frozen plan with a short list of recorded amendments is auditable. An unfrozen plan that grew by accident is just a large diff nobody chose.
plan (frozen at review):
1. add token-bucket middleware
2. wire it into the public router
3. unit + integration tests
amendment (recorded during implement, approved):
4. add a config flag to disable the limit per environment
reason: staging load tests need it off
Everything the pipeline touched traces back to either the frozen plan or a recorded amendment. Nothing appears in the diff that nobody agreed to.
Stop rules keep failures from compounding
An AI assistant that hits an error will try again. That is usually good. But an assistant that hits the same error and tries the same fix will loop, sometimes for a long time, burning effort and occasionally making things worse with each pass. The pipeline needs hard rules about when to stop.
The first rule is a repeat-failure limit. If the same step fails twice in a row, the pipeline halts and surfaces the problem to a human rather than attempting a third time. Two identical failures mean the model does not understand the problem, and more autonomous attempts will not manufacture that understanding. They will just churn. Stopping at two turns a possible infinite loop into a bounded one with a clear handoff.
The second rule is a separate gate for destructive actions. Deleting a database, dropping a table, rewriting history, force-pushing, anything that is hard or impossible to undo, does not pass on the same automatic approval as ordinary steps. It requires explicit, specific human confirmation for that exact action. The reasoning is asymmetry: a reversible mistake costs a rollback, an irreversible one can cost data you cannot get back, so the gate in front of it has to be heavier.
on step failure:
record failure in state
if failures_of_this_step >= 2:
HALT and surface to human # no third attempt
else:
retry with adjusted approach
before any destructive action:
require explicit human approval for THIS specific action
# never covered by a blanket "yes, proceed"
Together these rules bound the blast radius. The AI can act autonomously inside the safe, reversible middle of the work, and the pipeline forces a human into the loop precisely at the two points where autonomy is most dangerous: when it is stuck, and when it is about to do something it cannot take back.
Why the wrapper has to be deterministic
The AI at the center of this pipeline is non-deterministic by nature. Give it the same request twice and you can get two different answers, two different implementations, two different orderings of the same steps. That variability is not a defect. It is the flip side of the creativity that makes the model useful in the first place. You want it exploring the solution space.
The pipeline around it must be the opposite. The stages run in a fixed order. The gates apply the same checks every time. The state is recorded the same way in the same place. Given the same state file, the pipeline resumes at the same point and does the same thing. This is what makes the whole arrangement trustworthy despite the randomness at its core.
Determinism in the wrapper buys three specific things. Reproducibility: you can rerun the process and get the process to behave the same, even if the model's output inside a stage differs. Auditability: because every decision and every stage transition is written down, you can reconstruct after the fact exactly what happened and why. Trust: you can let the model act with real autonomy inside a stage precisely because a predictable gate is standing at the exit, checking its work before it counts.
The division of labor is the whole idea. The AI owns creativity, the part that benefits from variation and exploration. The pipeline owns discipline, the part that must never vary. Neither does the other's job. A creative pipeline would be chaos. A disciplined AI would not be worth using.
When to skip the pipeline and just ask
The harness is not free. It has real overhead: writing the plan, maintaining the state file, passing each gate, recording the documentation. For the right task that overhead is a bargain. For the wrong task it is bureaucracy strangling a two-minute job.
Skip the pipeline when the task is small, one-off, and easily reversible. Renaming a variable across a file. Drafting a throwaway script you will read and discard. Answering a question about how some code works. Reformatting a block. For any of these, spinning up a five-stage pipeline with a frozen plan and a state file is pure waste. Just ask the model and read the result. The cost of a mistake is a glance and an undo.
Use the full pipeline when the work is repeatable, consequential, and hard to reverse, especially when it ends in a deploy. Anything that ships to production. Anything that touches a database schema or migrates data. Anything a teammate will build on top of. Anything you will need to explain or audit later. Here the overhead is not overhead at all. It is the cheapest insurance available against the exact failures, context loss, scope creep, unverified deploys, irreversible changes, that unguided delegation invites.
The deciding question is simple. What does a mistake cost? If the answer is a glance and an undo, skip the harness and let the model run free. If the answer is a production incident, lost data, or an afternoon of forensic archaeology through an untraceable diff, wrap it. Match the weight of the process to the weight of the consequences, and let the AI be as fast and loose as the stakes allow.
Related posts
Use an AI Agent as Your Shell Front End, Not More Aliases
The graveyard of forgotten shell scripts has an alternative. Describe the task in plain words and let an AI assemble grep, awk, and jq for you.
Moving a Backend from Rust to Go, and Why Build Time Decided It
Rust gave us safety we rarely needed and a compile loop we fought every day. Here is the honest tradeoff, with numbers, that pushed a service to Go.
Write Once in English, Auto-Translate to Reach More Readers
Writing in public teaches you and helps others, but one language limits reach. Write one English source, let a pipeline translate and publish the rest.