How to Write a Task Spec an Agent Can Actually Execute
Key takeaway: A task spec an agent can execute has five parts: a clear goal, hard scope boundaries, machine-checkable acceptance criteria, constraints, and where the code lives. Name the test that must pass rather than describing the vibe, and keep the task small enough to finish in one pass. Tight scope is what keeps a run under ten minutes and the diff reviewable.
An autonomous coding agent is only as good as the task you hand it. Give it a vague wish and you get a vague diff. Give it a tight spec - a clear goal, hard boundaries, and a machine-checkable definition of done - and it runs the whole thing end to end and comes back with a verified result. Writing that spec is the skill that separates a frustrating agent from a reliable one.
The anatomy of a task spec
A task an agent can execute without stopping to ask questions has five parts. Each one closes a gap the agent would otherwise have to guess across, and every guess is a chance to build the wrong thing.
- The goal. One sentence naming the observable change: "Add a rate limit of 60 requests per minute to the public API." Not "improve the API." The goal is the single outcome the whole run is judged against.
- Scope boundaries. What the task must not touch. "Do not change the auth middleware. Do not refactor the router." Boundaries are how you stop a well-meaning agent from rewriting half the codebase on the way to a small fix.
- Acceptance criteria. How success is verified: which test proves it, what the new behavior looks like, what a passing run returns. This is the target the agent aims at and the bar it has to clear before it reports done.
- Constraints. The rules the solution has to respect - the library already in use, the code style, the API that must stay backward compatible, the pattern the rest of the codebase follows.
- Where the code lives. The files, modules, or entry points involved. Pointing the agent at the right neighborhood saves it from a slow, error-prone hunt and keeps the change where you expect it.
Notice what these have in common: they are all concrete. A spec made of adjectives ("clean", "robust", "better") gives an agent nothing to aim at. A spec made of nouns, files, and assertions gives it a job it can finish.
Why tight scope keeps a run under 10 minutes
The most common reason an agent run goes badly is not a weak model - it is a task that was too big. A sprawling spec forces the agent to touch many files, hold a large mental map, and run a slow, wide check at the end. Each extra file is another place for the change to go sideways, and the long run makes every failure expensive to diagnose.
A tight task does the opposite. When the goal is one behavior, the boundaries are two or three files, and the acceptance criterion is a specific test, the whole loop - plan, edit, run the checks, report - finishes in roughly ten minutes. That speed is not just convenience. A fast run means:
- Clear failures. When a small change breaks one test, the cause is obvious. When a large change breaks five tests, you are back to debugging.
- Cheap retries. If the first attempt misses, re-running a ten-minute task costs almost nothing. Re-running an hour-long one costs your afternoon.
- Real momentum. Several small verified tasks in a morning beat one big task that is still "almost done" at the end of the day.
The rule of thumb: if you cannot describe the scope in a sentence or two, it is not one task. Split it until each piece has a single goal and a single way to prove it worked.
Hand a well-specced task to an agent and walk away
Heygents takes a task with a clear goal, boundaries, and acceptance criteria and runs it end to end - an agent plans the change, edits the code, runs your tests and checks, and reports back a verified result. A tight spec goes in, a green run comes out, and you review a finished diff instead of babysitting the work.
Open Heygents →Acceptance criteria: name the test, not the vibe
Acceptance criteria are the heart of a spec, and they are where most specs go soft. "Make the export feature work" is a wish, not a criterion, because nobody - human or agent - can point at a moment where it is unambiguously satisfied.
Good acceptance criteria are observable and specific. They name the exact behavior and, ideally, the exact check:
- Behavioral. "A CSV export of an empty table returns a header row and no data rows, with a 200 status."
- Test-anchored. "Covered by a new test in
export.test.jsthat asserts the header and empty body." - Bounded. "No change to the existing JSON export path or its tests."
Written this way, the criteria double as the agent's target and your review checklist. The agent knows precisely what to build and what to assert; you know precisely what to look for in the diff. There is no interpretation left in the middle where a misunderstanding can hide.
Constraints and where the code lives
Constraints are the guardrails that keep a correct solution from being the wrong solution for your codebase. An agent can invent a perfectly working feature that uses a library you do not want, follows a pattern you abandoned, or breaks a contract another part of the app depends on. Constraints prevent that by making the invisible rules visible.
The constraints worth stating almost every time:
- Stack and libraries. "Use the existing
db.jshelpers, not a new query builder." Stops the agent from pulling in a dependency you would only have to remove. - Compatibility. "The public response shape must not change." Protects the callers you are not editing.
- Style and patterns. "Follow the module structure used by the neighboring routes." Keeps the change from looking like it was written by a stranger.
Then tell the agent where the code lives. You do not have to map every line, but naming the entry point - "the handler in routes/export.js, wired up in server.js" - saves the agent a search and keeps the edit in the file you meant. A spec that names the goal and the neighborhood turns an open-ended exploration into a focused change.
Define "done" as tests and checks green
The single most important line in a task spec is the definition of done, because it is the thing the agent uses to decide it can stop. If "done" is fuzzy, the agent either stops too early with a half-built change or wanders past the goal polishing things you never asked about.
The reliable definition is one a machine can confirm without you in the loop:
- The tests pass - including the new one that proves the goal.
- Lint is clean - no new warnings introduced by the change.
- The type or build check succeeds - the code still fits together.
- The specific behavior is asserted - not just "nothing broke", but "the new thing provably works".
This is what lets an agent report a verified result instead of a hopeful one. The difference matters: a hopeful "I think this works" puts the burden of verification back on you, while "all checks green, new test covers the behavior" hands you a change you can trust at a glance. When done is machine-checkable, the agent's finish line and your acceptance are the same line, and there is no gap between them for a regression to slip through.
Common spec mistakes to avoid
Most bad agent runs trace back to a small number of avoidable spec problems. Once you learn to spot them, your first draft gets a lot more executable.
- The kitchen-sink task. Bundling three unrelated changes into one spec. Split them - each gets its own goal, its own test, and its own fast run.
- Adjective-driven goals. "Make it faster / cleaner / more robust." Replace every adjective with an observable outcome or a check.
- No boundaries. Leaving scope open invites a refactor you did not ask for. Always say what must not change.
- Untestable done. A finish line no machine can confirm. If you cannot name the check, you cannot know the task is complete.
- Hidden assumptions. The convention "everyone knows" that the agent does not. Write down the constraint instead of trusting it to be inferred.
- No pointer to the code. Forcing the agent to guess where the change belongs, which is where edits land in the wrong file.
The through-line is the same as the whole guide: replace anything the agent would have to guess with something it can read. A spec with no guesses left in it is a spec an agent can execute.
Go deeper: Reviewing AI-Generated Diffs - once a well-specced task comes back green, how to read the resulting change quickly and confidently before you merge it.
Frequently asked questions
What makes a task spec good enough for an agent to execute?
A good spec answers five questions with no guessing: what the goal is, what is out of scope, how success is verified, what constraints apply, and where the relevant code lives. When those are explicit, an agent can plan, change code, and check its own work without stopping to ask you what you meant.
How small should a task be for an autonomous agent?
Small enough that one focused change plus its checks finishes in about ten minutes. A task that touches one behavior in a couple of files, verified by a specific test, runs fast and fails clearly. If a task needs a paragraph to describe the scope, it is really several tasks and should be split.
How should I define done in a task spec?
Define done as an observable, machine-checkable result: the relevant tests pass, lint is clean, the type or build check succeeds, and the specific new behavior is covered by an assertion. Avoid vague finish lines like "works better" - if a machine cannot confirm it, an agent cannot prove it is finished.
From a tight spec to a verified result
Stop hand-holding half-finished changes. Heygents runs a well-specced task end to end - goal in, an agent edits the code, runs your tests and checks, and reports back a verified, green result. A solo developer writes the spec once and reviews a finished diff instead of driving every step.
Try Heygents free →