Give the agent local truth
A coding agent cannot execute "make onboarding better" safely because the phrase hides too much: which app surface, which user state, which existing code, which tests, which design constraints, which analytics, and which work is intentionally out of scope. The first job of an AI-executable spec is to supply local truth before the agent starts editing.
Local truth includes the target repository, relevant files, existing patterns, business rules, build commands, test commands, and known traps. If there is a canonical component, migration pattern, authorization helper, logging convention, or API style, point to it. Agents are strongest when they can imitate a known pattern and weakest when the spec forces them to infer hidden project norms.
"reads your codebase, edits files, runs commands"
Official agent docs converge on the same principle. OpenAI Codex documents project instructions through AGENTS.md. Anthropic documents project memory and persistent instruction files. Cursor documents persistent rules. GitHub Copilot documents repository custom instructions. A spec should assume those project instructions exist, but it should still name the task-specific constraints for this change.
Fence the scope hard
Scope fences are the most important difference between a human-readable spec and an AI-executable one. Humans often infer that "do not redesign the whole thing" is implied. Agents benefit from explicit boundaries because they are optimized to continue helpful work unless told where to stop.
Good fences are written as concrete prohibitions and permissions. "Touch only the checkout route, the order summary component, and the checkout tests." "Do not change pricing calculation." "Do not introduce new dependencies." "Do not alter copy outside the error message states listed below." These are not bureaucratic details; they are blast-radius controls.
Weak fence
Keep changes small and avoid overengineering.
Executable fence
Do not create new shared abstractions. Do not change the payment model. Limit edits to CheckoutForm, CheckoutSummary, and their tests unless verification reveals a directly blocking dependency.
Use examples as ambiguity breakers
Examples are not decoration. They are executable intent. If the behavior can be expressed as inputs and outputs, include examples before implementation starts. For UI work, examples can be states: empty, loading, valid, invalid, permission denied, partial failure, retry, success. For APIs, examples should include request payloads, response payloads, status codes, and idempotency expectations.
Gherkin-style examples are useful because they force a state-action-result shape. You do not need a BDD test suite to borrow the discipline. The point is to make the hidden assumptions visible.
Acceptance example
Given an invited user opens the workspace invite link
And the invite has expired
When the user submits the signup form
Then the account is not created
And the page shows an expired-invite recovery path
And the event "workspace_invite_expired_signup_blocked" is recorded once Define done in commands and observations
"Done" must be observable. For an agent, that usually means a short list of required commands and checks. Include the narrow tests to run during iteration and the broader gate to run before handoff. If a command is slow, say when to run it. If a browser flow matters, describe the viewport and expected state. If logs or analytics matter, name the event or metric.
Verification should also include negative checks: no unrelated formatting churn, no new dependency, no schema drift, no public API break, no console errors, no layout overlap, no missing empty state. These checks protect against plausible-looking but incomplete implementations.
Done criteria
- Unit tests for invite expiration pass.
- Existing signup tests still pass.
- Manual browser check covers valid invite, expired invite, already-used invite.
- No pricing, billing, or workspace role behavior changes.
- Implementation summary lists files changed and verification output. Reserve MUST for real obligations
Requirement keywords are useful when the team treats them consistently. RFC 8174 clarifies the uppercase convention around requirement keywords. For product specs, the lesson is practical: uppercase requirement words should mean something. If every preference is a MUST, none of them are.
Use MUST for behavior that blocks acceptance, SHOULD for the default expectation when exceptions are possible, and MAY for optional behavior. Then make the acceptance criteria match the language. A MUST without a test is only emphasis.
"clarity and consistency"
Specify the handoff report
The spec should tell the agent what to report when implementation is done. Ask for the changed files, verification commands, notable decisions, deviations from the spec, and any remaining risk. This prevents the final answer from becoming a vague success note and gives the human reviewer a map of what to inspect.
For larger work, include a rule for when to stop and ask instead of continuing: repeated failing tests, missing credentials, ambiguous product decision, destructive migration risk, or conflict with an existing contract. Agents are more useful when the spec distinguishes implementation autonomy from decision authority.
Before sending a draft to an agent, run the Ambiguity Checker and then review it against the Spec Review Checklist. The checker catches obvious vague language; the checklist catches ownership and risk gaps.
Agent implementation brief template
# Agent Implementation Brief
Goal:
[Observable outcome]
Repository / working directory:
[Path or repo]
Relevant context:
- [Files, modules, routes, docs, existing examples]
- [Business rules]
- [Current behavior]
Scope:
- [Allowed edits]
- [Required behavior]
Non-goals:
- [Explicitly out-of-scope work]
- [Do not refactor/change]
Examples:
- Given [state], when [action], then [result].
- Input: [payload]
Output: [response]
Implementation constraints:
- [No new dependency / preserve API / follow local pattern]
- [Accessibility, performance, privacy, migration notes]
Done criteria:
- [Tests]
- [Manual checks]
- [Build/lint/typecheck]
Report back:
- Files changed
- Verification output
- Deviations
- Remaining risks The common mistake: prompting instead of specifying
A prompt is an instruction for a conversation. A spec is an artifact that survives the conversation. Prompts can be informal when the user is actively supervising each turn. Specs need to be durable because they will be read by implementers, reviewers, future maintainers, and possibly different agents.
The difference matters most when the work crosses files, changes data, or affects user-visible behavior. The more expensive a wrong interpretation would be, the more the task needs a spec rather than a loose prompt.