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.
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.
| Element | Why 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 change | which files/modules are in play changes every task; a repo-wide CLAUDE.md can't name them in advance |
| One example of correct output | a sample request/response, a before/after diff snippet, or expected CLI output disambiguates faster than another paragraph of prose |
| Decisions to flag, not make | which specific judgment calls in this task should pause for a human — see escalation design for the general policy; the ticket states the instance |
"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.
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.
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.