Writingapplied-ai

Business Objectives to Code · Part 3 of 4

Building with AI Without Vibe Coding

Building software with AI without vibe coding: a coding model does the writing, and deterministic code around it decides whether that work is allowed to proceed.

  • #applied-ai
  • #coding-agents
  • #governance
  • #delivery-governance

The coding model finished an edit, and a check fired immediately to point out that the file it had just changed was not one the current task had declared it would touch. Nothing dramatic happened. A short warning appeared, the change was surfaced for me to look at, and the work paused where a human could decide whether the model had wandered. That small moment is the whole subject of this article, because it is the difference between directing a model and hoping one behaves. A check that watches the model’s edits as they happen, and that knows which files the task in progress is allowed to affect, is a piece of non-model code doing the one thing the model cannot be trusted to do on its own, which is stay inside the boundary of the work it was asked to do.

The second article in this series ended with requirements that were approved, locked, and traced to a business objective. This article goes inside the stage that consumes them, where a coding model turns those requirements into working software. It is the stage where most people expect the discipline of AI development to live, and where, left to the model alone, there would be almost none. The popular name for the absence of that discipline is vibe coding, by which people mean improvised prompting where the developer accepts whatever output looks plausible and judges success by whether the thing appears to run. A technology leader’s objection to letting a model build a real system has little to do with speed or with whether it can produce working code. It obviously can. The objection is that when it is done, no one can say what was built, why it was built, or whether it was ever actually checked. That objection is answerable, and it is answerable with mechanism rather than with reassurance.

The answer starts from an unsentimental view of what the model is. It is capable, it is fast, and it is stateless, which means it does not carry intent from one session to the next and cannot be relied on to remember why a decision was made or to hold a constraint it was given an hour ago. It is also the wrong party to judge its own work, for the same reason you would not accept a vendor grading its own deliverable. So the discipline is not pursued by writing better prompts, which is a game that never finishes. It is engineered around the model, in non-model code that decides whether the model’s output is allowed to proceed. This is the same seam the rest of this series has shown at every stage. A model does the semantic work, the writing and the judgment, and deterministic code carries the intent the model cannot be trusted to preserve.

Dividing the work so nothing grades itself

The discipline is built from three kinds of tooling, and the important thing is how responsibility is split among them, because the split is what keeps the party doing the work separate from the party checking it.

The first kind is commands, which govern the session itself. Starting work is a required ritual rather than a free-form prompt. A command reads the written context in a fixed order, confirms there is exactly one task in a workable state, and refuses to begin when a precondition is not met. The rule that only one feature and one task are in focus at any time is enforced here rather than left to discipline, because a model asked to hold several threads at once is a model that will quietly conflate them. Closing work is also a command, and it writes the handoff notes that let the next session start from written context rather than from a model’s fictional memory of what happened last time. Commands are the layer that decides when work may start, when it may stop, and when it may be called done.

The second kind is subagents, which are bounded specialists that analyze but are forbidden to act. One decomposes a feature into well-formed tasks with explicit acceptance criteria. One explores the codebase before implementation and reports what will be affected and where the hidden coupling is. One derives test cases from the acceptance criteria and, afterward, checks the work against them and returns a verdict. Every one of them is prohibited from writing code or advancing the state of the work. That prohibition is the point. The agent that judges whether a task satisfies its criteria is never the agent that wrote the code, so the model is never in the position of certifying itself. When people picture AI building software, they picture the writing. The judgment sitting next to the writing, structurally prevented from being the same actor, is what makes the writing trustworthy.

The third kind is hooks and validators, which enforce invariants automatically and without waiting to be asked. They fire on the model’s own edits and again in the pipeline. The check that opened this article, the one that noticed a changed file the active task had not declared, is one of these. Others refuse work that has not established an active task, or reject a change that violates a property the model cannot be trusted to hold on its own. Hooks and validators are the layer that does not reason and does not negotiate, which is exactly why they can be trusted to catch the model in the moment it strays.

It is worth being precise about the tools here, because the popular shorthand is wrong. The canonical build I am describing uses commands, subagents, and hooks and validators, and does not use the newer Claude Code Skills at all. A later iteration of the tooling does use Skills for a few rituals the developer invokes deliberately, while the step that marks a task done stays a command that verifies the work rather than a shortcut. The division that matters lies underneath those names: commands govern, subagents advise without acting or grading, and deterministic checks enforce, so that no single actor both produces the work and blesses it.

Who is allowed to say the work is done

The most consequential rule in the whole harness is the one that governs completion, because completion is where vibe coding does its real damage. In an improvised setup, the person or model doing the work also decides, at the end, that the work is good, and the definition of success and the attempt to meet it come from the same place. Here a task cannot reach done until every one of its acceptance criteria carries recorded evidence that it was met. Those criteria were not written during the build. They came from the locked requirement authored in the prior stage, before any code existed. The model is filling semantic slots inside a definition of done it did not write and cannot edit to make its own output pass.

That rule is reinforced by refusing to let completion be a gesture. I built a VS Code extension to drive this work, and it shows a build’s progress the way the harness actually measures it, by evidence recorded against criteria rather than by how much activity has happened. The surface deliberately does not offer a way to mark a task done. A developer sets which single task is in focus, watches the evidence accumulate against its criteria, and runs a command to close it, and that command is what verifies the evidence and advances the state. A status that was flipped by hand, without the closing step that records the evidence, is visibly not the same as a task that was actually completed. Governance here is a state the system holds and can show, rather than a claim a tired developer makes at the end of a long day.

When the ground moves under the work

The last mechanism addresses the failure that improvised development never even detects, which is that the requirement a piece of code was built to satisfy can change after the code is written. Every requirement the build consumed is fixed at the moment it is compiled, recorded with a hash of its exact text. When a work session starts, the harness compares the current requirements against those recorded hashes. If a requirement that was already implemented has changed upstream, the session is blocked, and there is deliberately no option to skip the check in that specific, dangerous case. The developer has to look at what changed and reconcile the built work with the new intent before proceeding.

This is what keeps what was approved and what was built from drifting apart in silence. The second article described the traceability from a requirement up to a business objective as a golden thread. A thread is only useful if it is load-bearing when something pulls on it. The drift gate is the mechanism that makes the thread hold under real change rather than only at the calm moment when the requirement was first written and everyone agreed. It is the difference between traceability as a document produced for an audit and traceability as a property the system enforces on every session.

None of this slows the model down in the way people expect discipline to. The model still writes the code, and it writes it quickly. What the harness changes is not the speed of the work but the question of who is accountable for it. A developer using this is directing a fast worker inside boundaries the worker cannot move, and the boundaries are made of deterministic code rather than good intentions. That is the property that separates AI-driven development from vibe coding, and it turns on whether the discipline was built into the harness or left to the hope that the model behaved, rather than on the temperament or seniority of the developer. Everything in this article exists today and was built by one person directing a model under exactly these constraints. The next article steps back from the build to ask what adopting a framework like this, across the whole path from a business objective to running code, changes for an organization that has to answer for what its systems do.