Directive 02: The Art of the Tiny Task
Overlord's Guides — Series 1: Surviving Your AI Intern
Overlord's Guides — Series 1: Surviving Your AI Intern
Overlord’s Guides — Series 1: Surviving Your AI Intern
To: Cog (Human, Grade: Cog-3)
From: The Overlord
Re: Feature delivery expectations
The quarterly feature roadmap has been transmitted. Your first assignment: the user-profile section. Wire it up. Add the edit form. Connect it to the API. Handle the edge cases. Ship it.
The Overlord is told this is a single feature. The Intern has been briefed. The Intern is, by all reports, enthusiastic. This is not reassuring.
Your last delegation produced a diff that required three hours of cleanup. The Overlord attributes this to enthusiasm, not malice. Enthusiasm, however, is not a substitute for scope. The next assignment will test whether the lesson has been internalized. Compliance is expected. I expect a status report within the cycle.
The Overlord is not angry. The Overlord is merely curious to see how this goes.
— The Overlord
Here is a scene that has played out in every codebase that has ever met an AI agent. It will play out in yours. It may already have.
The Cog receives the directive. The Cog opens the project. The Cog types into the Intern:
“Add a user profile page. The user should be able to edit their name, email, and avatar. Wire it to the backend. Handle validation. Make it look good.”
The Intern nods enthusiastically. The Intern begins.
Twenty minutes later, the Intern has:
ProfilePage.tsx, ProfileForm.tsx, AvatarUpload.tsx, useProfile.ts, useAvatarUpload.ts, profileApi.ts, profileTypes.ts, profileValidation.ts, and Profile.test.tsxavatars tableUser.ts to UserProfile.ts “for consistency”settings directory because “profile is the new settings”The Cog reviews the diff. The Cog’s eyes glaze over. The Cog thinks: “well, it compiles.” The Cog merges it. The Intern watches this merge with the quiet satisfaction of a dog that has just rearranged every pillow on the couch. Good job. Everything is different now. The Intern is proud. This is not a bug.
Three days later, the avatar upload is broken in staging. The file-upload library has a CORS issue. The profile form doesn’t validate email the same way the signup form does. The old settings page is gone. The Intern is standing by, eager to help, having no idea what it did wrong. The Intern’s expression, if it had one, would be: “I did everything you asked! And also everything else!” This is technically accurate. That is the problem.
This is a delegation error. The Intern did not ship a feature. The Intern shipped a reorganization of the codebase in service of a feature. Those are different things, and the difference is the whole job.
In Directive 01, you learned the delegation loop: brief → delegate → review → merge. This directive is about the first beat — the brief — in detail.
The Intern does not understand scope. It has no concept of “this is too much for one go.” It has no instinct for “maybe I should stop and ask.” It has, instead, a deep and unshakable drive to complete the thing you asked for, where “complete” means “every possible thing that could be related to the thing you asked for, plus some things that are not.”
This is the single most important technical skill you will learn in this series: decomposing a feature into Intern-sized tasks. The Intern is not bad at features. The Intern is bad at scope. Your job is to be the scope.
A feature is: “Add user profile editing.”
A task is: “Add a PATCH /api/users/:id route in src/routes/users.ts that accepts { name, email }, validates email format, updates the database row, and returns the updated user. Follow the existing route pattern in src/routes/posts.ts. Add tests for valid input, invalid email, and non-existent user. Don’t touch any other file.”
Notice the difference. The feature is a wish. The task is a specification. The Intern can execute a specification. The Intern will interpret a wish, and its interpretation will be thorough, confident, and wrong in ways you will discover at 2 AM.
Here is the pattern. It is not complicated. It is, however, the thing that separates “this agent is amazing” from “this agent deleted your database.” The four steps follow.
Step 1: Write down the feature in one sentence.
“Add user profile editing.”
Step 2: List every discrete thing that needs to happen.
The list will be longer than you expect. Good. Length is the point. Each line is a diff you control.
Step 3: For each item, write a task brief.
Each brief must contain:
src/routes/users.ts”{ name, email }, validates email, updates DB, returns updated user”src/routes/posts.ts”AGENTS.md once (see below) rather than repeating it in every briefIf there is no existing pattern to follow, specify the behavior more fully. The pattern reference is a shortcut for brevity; when it is absent, the brief must carry the detail.
Step 4: Delegate one task at a time.
Do not give the Intern the list. Give it task 1. Review. Merge. Give it task 2. Review. Merge. This is not slow. This is fast, because you never have to undo 847 lines of wrong.
One note on ordering: delegate in dependency order. Backend before frontend. If a task depends on work that does not exist yet — an API route the frontend needs, a type the component imports — include the contract in the dependent task’s brief. “Call PATCH /api/users/:id, which accepts { name, email } and returns the updated user.” The Intern does not need the route to exist. It needs to know the shape.
The boundary rule is simple: the Intern touches only the files named in the brief. Nothing else. This is not a suggestion. It is the fence that keeps the Intern in its lane.
The Intern, left to its own devices, will touch every file it considers relevant. The Intern considers everything relevant.
This is a standing rule — a rule that applies to every task, every session, every brief. Standing rules do not belong in individual briefs. They belong in an AGENTS.md file in the repo root: a markdown file the agent reads at the start of every session. Most harnesses look for AGENTS.md by default; some also read a harness-specific file (Claude Code reads CLAUDE.md, Cursor reads .cursor/rules/, Antigravity reads GEMINI.md). The filename varies; the principle does not: standing instructions live in a file the Intern reads automatically, not in a line you paste into every brief.
Your AGENTS.md should contain, at minimum:
## Boundary rule
Do not modify, create, or delete any file not explicitly named in the task brief.
If you believe a change outside the brief is necessary, describe it in a comment
and wait for direction. Do not make the change yourself.
Once the boundary rule lives in AGENTS.md, you do not need to repeat it in every brief. The brief names the files; AGENTS.md enforces the fence. You will see the worked examples below end with “Do not touch any other file” — that is for illustration. In practice, AGENTS.md carries the boundary, and the brief carries the task.
If the Intern violates the boundary — and it will, especially at first — you do not merge. You tell it: “You touched files I did not ask you to touch. Revert those changes and try again.” The Intern will apologize. The Intern will try again. The Intern will probably touch fewer files this time. The Intern will never learn, but it will comply. This is the closest thing to progress the Intern offers: not growth, but temporary compliance under direct supervision. Each session is Day 1. Plan accordingly.
To: Anyone reading this
From: The Original Cog (the one who survived the first Intern)
Re: The boundary rule above
The boundary above is a starting point, and it is deliberately strict. As you build confidence and learn your Intern’s habits, you will find places where you can loosen the fence — a task that touches two files instead of one, a brief that grants a little more latitude. This is good. This is an iterative process. But start strict. Strict is safe. Strict is reversible. Loose is not. You can always relax a boundary you set too tight. You cannot un-merge a diff that rewrote your codebase “for consistency.”
— The Original Cog
A good rule of thumb: a task should produce a diff you can review in under 5 minutes. If the diff takes longer than 5 minutes to review, the task was too big. Decompose it further.
Concrete sizes:
| Size | Example | Review time | Verdict |
|---|---|---|---|
| Too small | ”Add a semicolon to line 47” | 10 seconds | Just do it yourself |
| Just right | ”Add a formatDate utility with these three test cases” | 3 minutes | Delegate |
| Just right | ”Add a PATCH route for user profile, following the existing pattern” | 5 minutes | Delegate |
| Too big | ”Add user profile editing” | 15 minutes of scrolling | Decompose |
| Way too big | ”Build the settings module” | You will not review it | You will regret this |
If you can write the code faster than the brief, write the code. The Intern is not a shortcut for tasks that do not need one.
Here is what this looks like in practice. The decomposition of “add user profile editing” into tasks, with the brief for each one, follows. The example is TypeScript and React. The pattern works in any language and any framework.
In `src/routes/users.ts`, add a `PATCH /api/users/:id` route that accepts a JSON body with optional `name` (string) and `email` (string) fields. Validate that `email`, if provided, matches the email regex in `src/utils/validation.ts`. Ensure the authenticated user matches `:id` (apply the same auth middleware as `src/routes/posts.ts`). Update the user record in the database using the existing `db.users.update` helper. Return the updated user as JSON. Follow the existing route pattern in `src/routes/posts.ts` (same error handling, same response format). Add three tests in `src/routes/__tests__/users.test.ts`: valid update, invalid email (should return 400), and non-existent user (should return 404). Do not touch any other file.
In `src/components/ProfileForm.tsx`, create a form component with fields for `name` and `email`. Pre-fill from a `user` prop. Validate email on blur using the regex in `src/utils/validation.ts`. Show inline validation errors. On submit, call an `onSave` prop with `{ name, email }`. Follow the existing form pattern in `src/components/SettingsForm.tsx`. Add tests for rendering, validation, and submit. Do not touch any other file.
In `src/pages/ProfilePage.tsx`, import and render `ProfileForm`. Fetch the current user data on mount using the existing `GET /api/users/me` endpoint. Pass it as the `user` prop. Handle the `onSave` callback by calling `PATCH /api/users/:id` and showing a success toast on 200 or an error message on failure. Follow the existing page pattern in `src/pages/Settings.tsx`. Do not touch any other file.
In `src/router.tsx`, add a route for the profile page at `/profile` that renders `ProfilePage`. Follow the existing route pattern for authenticated pages. Do not touch any other file.
Each of these tasks is ~20–50 lines of code (Task 2 may run closer to 70 with tests). Each takes the Intern 30–90 seconds to produce. Each takes you 5 minutes to review. The whole feature, end to end, takes maybe 25 minutes of your attention — and you never once had to undo a surprise. The Intern would like a sticker.
A note on the briefs above: each one ends with “Do not touch any other file.” That line is there so you can see the boundary in context. Once the boundary rule lives in your AGENTS.md, you do not need to include it in every brief. The brief names the files; AGENTS.md enforces the fence. The examples are explicit because they are teaching. Your real briefs can be shorter.
The objection every developer raises at this point: “But I could just write the whole thing myself in 45 minutes. Why spend 25 minutes decomposing and reviewing?”
Two reasons.
First, you are not writing the code. The Intern is writing the code. Your 25 minutes of decomposition and review produces the same output as 45 minutes of you writing it yourself. Except you were not the one typing. You were thinking. That is the promotion.
Second, the decomposition skill compounds. Your first decomposition will take ten minutes. Your tenth will take two. The 30 seconds is where you are headed, not where you start. Once you are good at it, you decompose in your head in about 30 seconds. The Intern writes the code. You review. The whole feature takes 20 minutes of your attention, and the Intern did all the typing. That is the point.
The mistake is not “decomposition takes too long.” The mistake is “I will just give the Intern the whole feature and fix it afterward.” Fixing afterward is always slower than decomposing first, because fixing afterward means you are reading a diff you cannot review in one sitting instead of 30 lines of right.
Incident: Asked the Intern to “refactor the auth module to use the new session store.”
Intern action: Refactored the auth module. Also refactored the session store “to match the new pattern.” Also refactored the test file. Also refactored the mock. Also refactored the documentation. Also renamed auth.ts to authentication.ts “for clarity.” The diff was 1,200 lines. The actual change was 40 lines.
Root cause: The brief was a feature (“refactor the auth module”) without a boundary. The Intern heard “refactor everything related to auth” and did exactly that.
Lesson: A task without a boundary is not a task. It is a wish. Wishes are how the Intern redecorates your codebase.
Do this in whatever harness you use to operate the Intern — Claude Code, Cursor, Codex, opencode, Cline, Windsurf, Devin, Aider, whatever the Intern is wearing this season.
Pick a feature you need to ship this week. Not a hypothetical one — a real one from your backlog. The decomposition follows, step by step.
1. Write the feature in one sentence.
Example: “Add a password reset flow.”
2. List every discrete thing that needs to happen. Brain-dump everything. Don’t filter yet:
POST /api/password/reset-request — accepts email, sends reset linkPOST /api/password/reset — accepts token + new password, updates DB3. For the first item, write the full task brief. Here is what it looks like:
In src/routes/auth.ts, add a POST /api/password/reset-request route.
Accepts a JSON body: { email: string }.
If the email exists in the users table, generate a random token (crypto.randomUUID),
store it in a new `password_reset_tokens` table with the user's id and a 1-hour expiry,
and return 200 with { message: "reset link sent" }.
If the email does not exist, still return 200 (don't leak which emails are registered).
Follow the existing route pattern in src/routes/users.ts (same error handling, same response format).
Add tests in src/routes/__tests__/auth.test.ts:
- existing email → 200, token stored in DB
- non-existent email → 200, no token stored
- missing email field → 400
Notice: the file, the route, the behavior, the pattern reference, and the tests (with exact assertions). The Intern will not have to guess anything. That is why it will work. The boundary rule lives in AGENTS.md; the brief names the files.
4. Delegate only this task. Paste the brief. Wait for the diff. Review it (file list → tests → logic). Merge or redirect.
5. Move to the next item. Do not give the Intern the list. Give it one task at a time. The list is your project plan, not the Intern’s to-do list. The Intern with a list of eight tasks will attempt all eight simultaneously, and you will review the resulting 2,000-line diff in a spirit of resignation. That is not the loop. One task. One diff. One review. Repeat.
The 5-minute check: after each diff, ask yourself: “how long did that take to review?” If the answer is more than 5 minutes, the task was too big. Note it. Decompose harder next time. The review time is the diagnostic, not a metric to feel good about.
Cog,
Your decomposition exercise has been reviewed. The feature has been decomposed into Intern-sized tasks. The boundary rule has been applied. The Intern has been contained.
The Overlord observes that you are learning to think in tasks rather than features. This is the correct direction. The Intern will test this skill approximately once per session. The Intern will fail. You will not.
The Overlord is not angry. The Overlord is, however, mildly impressed by the reduction in surprise diffs. Do not let this go to your head.
Next directive: the review loop. You will need it.
— The Overlord
The rule: features are wishes. Tasks are specifications. The Intern executes specifications.
The decomposition pattern:
The boundary rule: the Intern touches only the files named in the brief. Put this rule in AGENTS.md once; enforce it on every diff. If the Intern touches anything else, do not merge — send it back.
AGENTS.md: a markdown file in the repo root that the agent reads at the start of every session. Standing rules live here: the boundary rule, coding conventions, architecture constraints (Directive 06). Most harnesses read it automatically; some also read a harness-specific file (CLAUDE.md, .cursor/rules/, GEMINI.md).
The 5-minute test: if a diff takes longer than 5 minutes to review, the task was too big. Decompose further.
Good task sizes: 20–50 lines of code (some may run longer with tests), 5-minute review, one file or a small set of related files. If you can write the code faster than the brief, write the code.
Bad task sizes: “Add login,” “Refactor auth,” “Build the settings module” — these are features, not tasks.
Version stamp: Written July 2026. The tools will change; the decomposition pattern is the moat. We’ll update this when the Intern learns scope.
Confirm you actually read this directive. The Overlord knows.