contenox

How to write good tasks for coding agents, with a template

Updated

WORKING WITH AGENTS Write good agent tasks

To write good tasks for coding agents, say three things: what is wrong or wanted, how to check that it’s done, and what the agent must not change. Name the files or the symptom if you know them, and leave the method to the agent. Write it the way you would write an issue for a capable colleague who has never seen your codebase.

Before and after

Weak:

fix the login bug

Better:

Users get logged out when their session times out instead of the token refreshing. Look at the token refresh in src/auth/. Write a failing test that reproduces it, then fix the code so it passes. Done when npm test -- auth passes. Don’t change the public API of refreshSession.

The first version makes the agent guess which bug, where, and when to stop. The second gives it the symptom, a place to start, a test it can run on its own, and a boundary.

What should a task include?

Four parts, a sentence or two each:

  1. Goal: what should be true afterwards.
  2. Context: where to look, the error message, how to reproduce it.
  3. Done when: a command that passes, an output to match, a behavior to try.
  4. Don’t: what not to touch, which pattern to follow, what is out of scope.

A template to copy:

Goal: <what should be true afterwards>
Context: <files, error message, how to reproduce>
Done when: <command passes / behavior you can see>
Don't: <files not to touch, no new dependencies, keep API X unchanged>

If you already have the thing the agent needs, paste it instead of describing it: the full error, the log lines, a screenshot of the broken page.

Should I describe the steps or the outcome?

The outcome. When you dictate steps, the agent follows your plan literally, mistakes included.

Weak:

Open cart.ts, find the total function, and add a check for discounts.

Better:

Orders with a discount code show the full price on the checkout page, but we charge the discounted price. Make the displayed total match the charge.

The second leaves room for the agent to find that the bug is in a different file.

The exception is a preference only you know. If there are two reasonable ways and you care which one it picks, say so: “Use the existing Money type, not floats.”

How does the agent know when it’s done?

Give it a check that passes or fails. With one, the agent can make a change, run the check, read the failure and try again on its own. Without one, it stops when the code looks right, and you become the test suite.

  • “Done when npm test -- checkout passes.”
  • “Done when curl localhost:3000/health returns 200.”
  • “go run ./cmd/import testdata/bad.csv panics. Done when it prints a validation error instead.”

If no test covers the behavior, ask for one first: “Write a failing test that reproduces this, then fix it.”

Ask for evidence too: “Show me the test command you ran and its output.” A summary that says “all tests pass” is a claim, not proof.

How much detail is too much?

Detail that removes guessing helps: names, paths, the exact error, what done means. Detail that dictates the method usually hurts.

Agents can also err small. Claude Code’s docs note that Claude tends to build the smallest thing that satisfies a request. If you want empty states, error handling and keyboard support, list them.

Rules that apply to every task, such as the test command or “no new dependencies”, belong in your repository’s AGENTS.md, not in every prompt. See AGENTS.md explained.

How big should one task be?

Small enough that you can review the diff in one sitting. A task is too big if:

  • You can’t say what “done” means in one or two sentences.
  • It touches several unrelated parts of the code.
  • A person doing it would open more than one pull request.

Split it and review between steps:

  1. “Read the auth module and explain how sessions are created and expired. Don’t change anything.”
  2. “Add a failing test for the expiry bug.”
  3. “Fix the code so the test passes.”
  4. “Remove the now unused legacyExpiry helper.”

Starting with a read-only question is a cheap way to see whether the agent understood the code before it edits anything.

A task can also be too small. Renaming one variable is faster to do yourself than to describe.

What if I don’t know exactly what I want yet?

Let the agent ask you. Claude Code’s docs suggest a prompt like this:

I want to add rate limiting to the public API but haven't thought it through yet. Interview me until you could write the spec, then write it to SPEC.md.

Then start a fresh session that builds from SPEC.md. The spec becomes your definition of done, and the file you review against later.

More examples

Weak:

Add dark mode.

Better:

Add a dark theme to the settings page only. Add dark values to the existing color tokens in styles/tokens.css instead of hardcoding colors. Follow the system setting with prefers-color-scheme; no toggle yet. Done when the settings page looks right in both modes and npm run lint passes. Don’t change other pages.

Weak:

The tests are failing, fix them.

Better:

pnpm test fails in date-utils.test.ts since yesterday’s dependency update. Find out why. If the library changed behavior, update our wrapper in date-utils.ts to keep the old output. Don’t change the test expectations.

The last line matters. Without it, the quickest way to green tests is to edit the tests.

How do I write a task for script mode?

In a script or CI job (claude -p, codex exec), nobody is there to answer a question or approve a step. A task that works fine interactively can stall or fail silently. Three changes:

  • Tell it what to do when stuck. Without this, the agent guesses. Add a line like “If something is unclear or blocked, stop and explain what you need instead of guessing.”
  • Grant exactly the tools it needs, up front. In script mode an action that would normally ask is simply refused. With Claude Code, list what may run without asking. With Codex, codex exec is read-only until you allow edits. With OpenCode, opencode run --auto approves everything your opencode.json doesn’t deny, so write the deny rules first.
  • Say what the output should look like, because a script reads it. “End with one line: PASS or FAIL, then a list of changed files.”
claude -p "Fix the failing test in src/dates/parse.test.ts. Don't change the test. Done when npm test -- dates passes. If blocked, stop and explain what you need. End with PASS or FAIL and the files you changed." --max-turns 30 --allowedTools "Read" "Edit" "Write" "Bash(npm test *)"
codex exec --sandbox workspace-write "Fix the failing test in src/dates/parse.test.ts. Don't change the test. Done when npm test -- dates passes. If blocked, stop and explain what you need. End with PASS or FAIL and the files you changed."
opencode run --auto "Fix the failing test in src/dates/parse.test.ts. Don't change the test. Done when npm test -- dates passes. If blocked, stop and explain what you need. End with PASS or FAIL and the files you changed."

--max-turns caps how long Claude Code may work, so a confused run can’t loop for an hour.

The argument-order trap: --allowedTools takes every following argument as a tool name. Put the prompt right after -p, before it, or Claude Code reads your task as one more tool.

What do I do when the agent goes off track?

  • Stop it early. If the first few steps look wrong, interrupt. A wrong direction only gets bigger. In Claude Code, Esc stops it and keeps the work so far.
  • Correct the misunderstanding, not the output. “The bug is in how the total is displayed, not in how it’s charged.”
  • Start over after two failed corrections. A session full of wrong turns keeps re-reading them. A new session with a better task usually wins. In Claude Code, /clear starts fresh.

Related

Esc to close