The Instruction Spec: Defining an AI-Assisted Software Change
Six fields written down before an agent touches the code, and a risk category that decides which approvals the change needs.
An agent is told to add role-based access control to the dashboard. It does. Along the way it also touches the billing routes, because they share a middleware file, and nobody said not to.
Nothing here was a model failure. The constraint existed in somebody's head and never made it into the request.
Add role-based access control to the dashboard
It does
It also touches the billing routes
They share a middleware file.
Nobody said not to
The constraint was in somebody's head, not in the request.
Six fields
An instruction specification states the objective, the constraints, what would count as acceptance, the risk category, the points requiring approval, and how to undo the change.
objective: Add role-based access control for dashboard
constraints:
- do_not_modify_billing_routes
- preserve_existing_session_model
acceptance_criteria:
- unauthorized_users_receive_403
- role_persisted_in_users_table
risk:
category: auth-change
level: high
approval_gates:
- before_db_migration
- before_middleware_deploy
rollback:
- revert_migration
- revert_middleware
The format is unimportant. What matters is that each field is a decision that would otherwise be made silently.
Objective is what is being asked for. Constraints are the things that must not change, and they are the easiest field to leave out, because the thing you do not want touched is not the thing on your mind when you are describing what you want. Acceptance criteria are specific enough to disagree with before the work starts. Risk decides the rest. Approval gates name the moments a person has to say yes. Rollback is the undo path, written now rather than reconstructed later under pressure.
Objective
What is being asked for
Constraints
The things that must not change
Acceptance criteria
Specific enough to disagree with before the work starts
Risk
Decides the rest
Approval gates
The moments a person has to say yes
Rollback
The undo path, written now rather than reconstructed later under pressure
Risk decides the gates
Which approvals a change needs should follow from what the change touches rather than from how large the resulting diff is. A three-line change to authentication is a different proposition from a three-hundred-line change to a stylesheet.
Categorising by what is touched gives you something to attach gates to:
| Category | Examples | Where a gate belongs | Minimum check |
|---|---|---|---|
| auth-change | role model, middleware, token or session flow | before migration, before middleware deploy | access matrix tests, regression on protected routes |
| billing-change | webhook handling, entitlement checks, pricing logic | before webhook deploy, before entitlement enforcement | signature verification, replay protection |
| db-migration | schema change, destructive update | before applying in staging and in production | dry run, rollback rehearsal |
| external-api | new provider call, change in what data leaves | before secret registration, before live traffic | contract tests, timeout and retry behaviour |
These categories are an example of the shape, not a list to adopt. The useful property is that the category is decidable at specification time, before anyone knows how big the change will be.
The billing case shows why the category has to come first. A webhook handler that verifies signatures incorrectly is a small diff, passes its tests, and is a security defect. Nothing about its size would have triggered a gate.
Gate by diff size
- A three-line auth change looks minor
- A small webhook handler that verifies signatures wrongly passes its tests
Gate by what it touches
- auth-change, billing-change, db-migration, external-api
- Decided at specification time
Nothing about its size would have triggered a gate.
What this buys, and what it does not
A specification makes disagreement cheap. If the constraint about billing routes is written down, it gets argued about in a sentence rather than discovered in a diff.
It does not enforce anything. A line reading do_not_modify_billing_routes is a request to the agent, and if the only thing standing between the agent and those routes is that sentence, there is no check. Turning an approval gate into something that actually holds is a runtime question rather than a documentation one.
What a specification buys
- Disagreement in a sentence, not discovered in a diff
What it does not
- 'do_not_modify_billing_routes' is a request to the agent
- If that sentence is all that stands in the way, there is no check
Write the constraints before the objective.
What this changes
Write the constraints before the objective. The objective is the part everyone can already state; the constraints are the part that exists only in someone's head, and they are what the review was going to be about anyway.
Where this helps, and where it stops
- What it is
- A note on the short specification agreed before an AI agent changes code, and how the risk of a change decides its approvals.
- What it does not guarantee
- A development methodology, a toolchain, a definition of prompts or instructions, or an account of how approval is enforced at runtime.
- When the distinction matters
- An agent is about to change code that touches authentication, billing, data or an external service.