Vibes are useful for starting.
They are terrible at deciding when software is done.
You throw a prompt into the machine. The machine thinks in a little theatrical silence. Then a diff appears. Files changed. Tests maybe added. Somewhere, a ticket starts drifting toward Done on vibes alone.
This is the dangerous moment.
Not because the code is necessarily bad. Sometimes it is good. Sometimes it is better than the first version you would have written before coffee. The danger is that generation feels like completion. It is not. A plausible diff is a hypothesis, not a verdict.
The missing piece is process.
I wrote earlier about why cheap code did not make cheap software. This is the operational follow-up: the loop that turns generated code into code worth keeping.
Not “prompt harder”. Not “believe in the vibes”. Not a magic prompt to become a 10x engineer.
Also not waterfall returning with a fake mustache. Waterfall tried to make uncertainty disappear before the work started. A loop admits uncertainty and keeps asking for proof.
The process is a loop: research the system, write a plan, implement inside a sandbox, run measurable gates, review the result, make the agent fix what the evidence finds, and repeat until the acceptance bar is actually met.
If the spec is vibes, the result is vibes with semicolons.
Start With Research, Not Incantation
The first step of an agentic coding task should not be typing “implement X” and hoping the model guesses your architecture, your edge cases, your taste, and the one adapter named probablyLegacyButActuallyLive.ts.
The first step is investigation.
Where does this behavior live? What tests already exist? What conventions does the project use? Which files are safe to touch? What does “done” mean? What should not change? What is the rollback path if the agent gets ambitious and decides your authentication module would look fresher as a distributed philosophy experiment?
This sounds boring because it is supposed to be boring. The point is to replace guessing with checks.
If Boris the Blade were a software engineer, he would say: boring is good, boring is reliable.
For an agent, a plan is not ceremony. It is a steering wheel. Without it, the model is optimizing for the nearest plausible completion. With it, the model has a path, constraints, and a way to notice when it has produced a beautiful wrong answer.
That flow is not glamorous. It will not get applause in a demo. It will, however, save you from the much less glamorous demo where the feature works perfectly in the recording and fails the first time it meets old data, weird permissions, or a user with imagination.
Working Is Not Evidence
“It works” is a useful sentence when you are assembling a chair.
It is a dangerous sentence when you are changing software.
Works for what input? In which environment? Against what existing behavior? For the happy path only, or also when the network lies, the token expired, the cache is stale, and the person who wrote the original module now answers Slack once per solstice?
Classical software engineering spent decades inventing names for all the ways “it works” can betray you: regressions, edge cases, race conditions, security vulnerabilities, performance cliffs, and code that is technically correct in the same way a tax form is technically literature.
AI does not make those problems disappear. It makes it cheaper to produce code before you have dealt with them. That can be wonderful. It can also be how you create far more surface area for bugs while calling it acceleration.
Stop asking whether the agent produced code. Ask what evidence proves the change is good.
The Goal Is an Acceptance Contract
Many coding agents now have some version of a goal, task, plan, or acceptance criteria field.
A weak goal says:
Implement password reset.This is how you get a technically implemented password reset that behaves like a haunted vending machine.
A better goal says:
Implement password reset inside the existing auth flow.
Acceptance gates:
- Existing login and signup tests still pass.
- Password reset succeeds for a valid, unused token.
- Expired, reused, invalid, and missing tokens are rejected.
- Unknown email submission does not reveal whether an account exists.
- New or changed tests cover the success path and the rejection paths.
- The project test command and build command pass.
- Coverage for the touched auth module does not decrease; if the project has a
numeric coverage gate, meet it.
- The final response lists changed files, test evidence, and remaining risks.Now the agent has a shape to work against. More importantly, you have something to reject.
The measurable part matters. Green tests, build pass, lint pass, coverage target, typecheck, bundle size limit, latency budget, migration dry run, security scan: these are gates a loop can actually evaluate. They are not perfect, but they give the agent something firmer than mood.
You can add qualitative criteria too. In fact, you should when the task is more than a tiny fix.
For example:
Before finishing, get an adversarial review from an independent reviewer.
The review should grade the change AAA+ for correctness, maintainability,
security, test quality, and fit with the existing codebase.
Every dimension must reach AAA+ with cited evidence.
Any lower grade, blocking finding, or major finding restarts the loop.The AAA+ grade is intentionally loud, but it still needs mechanics. A reviewer that says “AAA+, looks great” has contributed confetti. Define the scale before you use it:
A = no blocking correctness, security, or regression findings.
AA = realistic unhappy paths and named constraints are covered.
AAA = the change is maintainable, minimal, secure, tested, and fits the
existing codebase.
AAA+ = an adversarial reviewer found no blocking or major issues, and every
important quality claim is backed by cited evidence.Cap the chain there; further rounds mean the spec is underspecified, not that more reviewers will help. An undefined scale is itself a blocking finding.
A qualitative grade is still a judgment call. A model can cite evidence that looks rigorous and still be wrong. So the grade does not stand alone.
The cited evidence has to be checkable against the actual diff, tests, logs, or rendered behavior, and every blocking or major finding must be resolved or explicitly accepted with a reason. If the reviewer and the author-model agree too cleanly but the evidence cannot be checked, the gate has not passed. It has merely negotiated a nicer label.
Quantitative gates keep the loop honest. Qualitative gates keep it from becoming a green checkmark factory.
Tests Are the First Executable Gate
Tests are the most obvious gate because they have the decency to be executable.
A good test suite turns “please do good, bad no do” into something the machine can check. It says: this input should produce that output, this permission should be denied, this old behavior must survive, this migration must not eat user data, this error should be handled without setting the building on fire.
The happy path matters. It is also the path every demo walks down first.
The unhappy paths are where the system tells the truth.
If you are adding password reset, the first test should not be only “user clicks link, password changes, confetti.” You also want expired tokens, reused tokens, invalid tokens, unknown email addresses, rate limiting if the system has it, audit logging if that matters, and a check that existing login behavior did not regress.
The exact list depends on the feature. The principle does not.
Tests give the loop teeth. Without them, the agent can keep producing confident revisions that look reasonable and remain wrong. With them, the system argues back.
Generated tests need the same skepticism. They can be useful, but they can also be little applause machines: they assert mocks instead of outcomes, cover the path that proves the agent was right, and ignore the path where the system bites.
The failure mode is especially funny in a grim way: the model writes code, writes tests for the code, runs the tests, sees green, and reports success. The whole loop is now a courtroom where the defendant, judge, jury, and stenographer are the same autocomplete with different hats.
Ask what behavior each test protects. Ask what bug would make it fail. Ask whether the test would still pass if the implementation were wrong in a realistic way. Ask whether it covers a user-visible outcome or just the internal arrangement of furniture.
If the test suite only proves that the generated code resembles itself, you have not built a safety net. You have built a mirror.
Let the Agent Work
There is another trap: manually approving every tiny action.
Approve this file read. Approve that command. Approve this edit. Reject that import. Approve running tests. Approve reading the error. Approve trying the obvious fix. Congratulations, you have invented pair programming where one participant is a supercomputer and the other is a bored customs officer stamping forms at the speed of anxiety.
Approval is absolutely right for irreversible actions, external writes, credential access, production systems, destructive commands, and anything with unclear rollback. Those are boundary decisions. They deserve a human.
Approving every harmless read, local edit, and test rerun is different: involvement can feel like safety while mostly adding delay.
Human review works best at the boundaries: setting the goal, defining the sandbox, limiting permissions, reviewing the plan, inspecting the final diff, and deciding whether the evidence is good enough. It works badly as a keystroke approval queue.
Let agents work for longer stretches.
Give them hours, not keystrokes.
But only if they are locked down.
Trust them to work unattended, provided “unattended” means “inside a sandbox with the blast radius measured in millimeters.”
That sandbox can mean different things depending on the task: a restricted filesystem, no network by default, no secrets, no production credentials, a disposable branch, a test database, read-only access to external systems, explicit approval for destructive commands, and logs of what happened. The point is not paranoia. The point is engineering.
This is not abstract paranoia. Once an agent reads PR bodies, issues, web pages, or tool output, prompt injection turns untrusted text into attempted instructions. That is why destructive commands and external writes belong behind approval, not behind hope.
This is why I think in controlled systems, not naked models. The model is only one part of the system. Permissions, tools, context, and stopping rules shape what it can safely do.
A good sandbox lets the agent move fast where mistakes are cheap and stops it where mistakes become expensive. It turns autonomy from a leap of faith into a controlled experiment.
If you cannot safely let the agent run, the fix is not to click “approve” two hundred times. The fix is to improve the sandbox and the work contract until autonomy is safe enough to be useful.
Run Several Loops in Parallel
One loop does not have to monopolize the repository.
With git worktree, you can check out working trees of the same repository into separate directories, each on its own branch. One loop can explore the small fix, another can try the bolder refactor, and a third can write the tests first. They share history, but they do not share a working directory, so their edits do not trample each other.
Each worktree needs a distinct branch; Git will not check out the same branch twice. In practice, that means one terminal and one agent session per worktree.
git worktree add ../loop-small -b loop/small-fix
git worktree add ../loop-tests -b loop/tests-firstThat gives you parallel exploration without turning one checkout into a knife fight. When the loops finish, compare the evidence: tests, review notes, diff size, risk, and how much intent survived in the code. Keep the branch that earns trust. Delete the others without ceremony.
Parallel loops multiply token cost as fast as throughput. Match the number of concurrent loops to task risk, not to your enthusiasm.
Bring an Outside Reviewer
The agent that wrote the code should not be the only judge of whether the code is good.
This is not because models are uniquely vain. Humans do the same thing. We all read our own diff with the soft lighting turned on.
The four-eyes principle works with agents too. It just comes with a token bill.
A useful loop adds an outside reviewer: another model, another provider, another agent configuration, or a stricter review prompt. If one model wrote the code, ask a different reviewer to inspect it adversarially through the same portable criteria. If one model wrote the tests, ask another whether the tests would catch the bugs that matter.
If you only have one system available, make the review as independent as you can: fresh context, goal plus diff only, stricter prompt, no implementation transcript. Weaker than a different reviewer, still better than self-grading.
The review prompt should not be polite.
Review this change adversarially.
Look for regressions, missing edge cases, security bugs, overbroad edits,
weak tests, unclear intent, and places where the implementation only appears
to satisfy the goal.
Grade correctness, maintainability, security, test quality, and fit with the
existing codebase. Each dimension needs AAA+ with cited evidence.
Any grade below AAA+, any blocking finding, or any major finding restarts the loop.
Return blocking and major findings first. Do not praise the code.That last sentence is doing real work. Models love praise the way dashboards love green. Praise is cheap. Findings are useful.
Multiple reviewers can help when the change is risky. One reviewer focuses on security. Another focuses on maintainability. Another checks test quality. This is not magic, and it is not free. But it changes the shape of the failure: if two reviewers disagree, the loop has found the exact place where a human should look.
The loop is:
research -> plan -> implement -> gate -> review -> fix -> repeat until gates passThe loop is not:
generate -> looks fine -> ship -> learn from production, the most expensive teacherProduction does teach. Production also charges tuition in cash, sleep, and reputation.
A Small Workflow
Here is the version I would actually want for a medium-risk change like password reset:
Goal:
Add password reset to the existing auth system without changing login behavior.
Research:
Inspect current auth routes, token storage, mailer integration, tests,
project instructions, and security conventions. Write a short plan before
editing files.
Sandbox:
Work in a branch or disposable worktree. No production credentials.
No external writes without approval. Use a test database or local fixtures.
Implementation:
Add the smallest code path that fits the existing architecture.
Do not introduce a new auth framework.
Do not rewrite unrelated login/signup behavior.
Tests:
Cover valid reset, expired token, reused token, invalid token,
unknown email behavior, and unchanged login/signup behavior.
Quantitative gates:
The project test command passes.
The project build or typecheck command passes.
Coverage for touched auth code does not decrease, or meets the existing
project threshold if one exists.
Qualitative gate:
External adversarial review grades the change AAA+ for correctness,
maintainability, security, test quality, and fit with the existing codebase.
Every dimension needs cited evidence. Any lower grade, blocking finding, or
major finding restarts the loop.
Finish:
Report changed files, commands run, evidence, unresolved risks, and anything
intentionally left out of scope.This is not a tool-specific prompt. It is a portable work contract.
The difference matters. A prompt asks for output. A work contract shapes behavior, limits damage, and defines what counts as done. The exact gates change with the task. The need for evidence does not.
Loops Have a Cost
Loops burn tokens.
Tests take time. Reviews take time. Agents can chase their own tail with impressive stamina. Add three external reviewers, two self-critiques, and a coverage report to a toy app, and somewhere a small rainforest is gone.
So no, not every task needs the full ritual.
A loop that will not converge is signal, not noise. The spec, gates, or task scope may be wrong. Set a budget in iterations, tokens, or wall-clock time, and treat exhaustion as a finding instead of a failure of effort. My heuristic for a medium-risk change: two review/fix cycles or 30-60 minutes is often enough to learn whether the loop is converging. Past that, stop and inspect the goal instead of feeding the meter.
A typo fix does not need adversarial review from a committee of synthetic elders. A CSS tweak probably does not need three models debating ontology. A risky auth change or data migration does.
The loop should match the blast radius.
Small change, small loop: inspect, edit, run targeted test.
Medium change, medium loop: plan, tests for happy and unhappy paths, build, review.
Large or risky change, serious loop: sandboxed autonomy, measurable gates, external review, security thinking, migration/rollback plan, and enough written context that future-you can understand why this thing exists without spelunking through a chat transcript titled “quick fix”.
Do not maximize process. Process can become its own little productivity tax office. Buy evidence proportional to risk.
The Vibe Is Not the Process
I like starting with a vibe. The blank page is annoying, and having a machine throw a plausible first version over the wall is genuinely useful.
But vibes are inputs, not acceptance criteria. A plausible diff is not a completed engineering task. It is raw material.
The loop is where the work becomes real: research before action, a plan before edits, measurable gates before completion, tests that argue back, agents that can run without constant babysitting because they are safely boxed in, and reviewers that are allowed to be rude.
That is the part worth building.
Build the process.
The vibe can start the work. The process has to earn the merge.






