← All packs

Writing task descriptions an agent can actually execute

A CLAUDE.md sets context once for every task that follows — conventions, architecture, what not to touch. A task description sets the goal for exactly one run, and it's a different writing problem: too little detail and the agent fills the gap with a reasonable guess that isn't yours; too much and the actually-important constraint drowns in boilerplate the agent has to re-derive priority from. Most tickets that produce "the agent didn't do what I meant" weren't ambiguous to a human reading casually — they were ambiguous exactly where it mattered, and a human filling in the gap would have used context the ticket never wrote down.

The test: would two competent engineers agree?

Before handing a task to an agent, ask whether two engineers reading only the written description — no access to what's in your head — could reasonably produce different implementations. If yes, the ticket is under-specified, and an agent will resolve the ambiguity the same way a new hire would: pick the interpretation that seems most reasonable given the text, which is not the same as picking the one you wanted. The fix isn't more words, it's naming the specific fork in the road: "use the existing retry helper in lib/http.py, don't add a new one" closes a fork that "make the request more resilient" leaves wide open.

What a task description needs that a CLAUDE.md doesn't

ElementWhy it's task-specific, not repo-wide
Observable done-condition"tests pass and the new endpoint returns 201" is checkable; "improve error handling" isn't — different tasks have different finish lines
Scope boundary for this changewhich files/modules are in play changes every task; a repo-wide CLAUDE.md can't name them in advance
One example of correct outputa sample request/response, a before/after diff snippet, or expected CLI output disambiguates faster than another paragraph of prose
Decisions to flag, not makewhich specific judgment calls in this task should pause for a human — see escalation design for the general policy; the ticket states the instance

State the done-condition as something checkable, not a direction

"Make the search faster" has no finish line an agent can verify against; it will stop when it judges the improvement sufficient, and that judgment may not match yours. "The search endpoint should return in under 200ms for the existing test fixture" has a finish line anyone — human or agent — can check without asking you. Where an exact number isn't available, a comparative or structural condition still beats a vibe: "no N+1 query in the new code path" or "existing test suite still passes plus one new test covering the empty-results case" both give the agent something to verify against before calling the task done, instead of something to guess at.

Name the boundary before the agent has to infer it

Every task touches a smaller surface than the whole repo, but the ticket rarely says so explicitly — it's assumed shared context between the person who wrote it and whoever they expected to read it. An agent doesn't have that shared history, so "fix the login bug" without naming the suspect file leads to either a correct-but-slow investigation (fine, just costs time) or a fix applied in the wrong layer because two files both plausibly touch login. One line — "the bug is in the session-refresh path, not the initial-auth path" — turns a search problem into an implementation problem, which is a better use of an agent's execution budget than its exploration budget.

One concrete example beats three more sentences of description

Prose descriptions of a desired transformation are lossy in ways that are hard to notice while writing them — "normalize the phone numbers to E.164" sounds precise until the agent hits a number with an extension, a number missing a country code, or a number already in the target format, none of which the sentence addressed. A single input/output pair pinned to one of those edge cases resolves more ambiguity than another paragraph, because it's concrete enough that there's no reasonable second reading. This is the same principle behind structured output validation at the schema level, applied one level up, to the task description itself.

Tasks that genuinely have no single right answer — "explore whether we could simplify this module" — should say so, and ask for options with tradeoffs instead of a diff. Writing an open-ended task as if it had a checkable done-condition just pushes the agent to invent a false one and present it with unearned confidence.