A large task usually starts with a request that sounds reasonable. "Build a locale catalog: a list, search over it, a country filter and a decent look." An hour later you have a page where the list is half built, search only works on some of the fields, the styling has changed twice already, and the conversation has grown so long you can no longer tell which result was actually checked and which was just a promise someone made along the way.

One possible cause is that nobody split the task into parts with a clear result. That doesn't rule out bugs in the code, but bugs are easier to check for when it's clear what should be working right now. Let's look at how to break a large piece of work into stages you can check, what information to give the agent for the current stage, and what to write down so you can come back to the task tomorrow - in the same conversation or a new one - without piecing everything together from memory.

Sample interface of the locale catalogA dark table with a search box and four rows: locale code, language, country, currency and domain.List of locale codesSearch by code or languageLOCALE CODELANGUAGECOUNTRYCURRENCYTLDen-USEnglishUnited StatesUSD.usde-DEGermanGermanyEUR.defr-CAFrenchCanadaCAD.caja-JPJapaneseJapanJPY.jp

That's what the catalog should turn into: a list with search, and in each row a locale code, language, country, currency and domain. Keep this mockup in mind - in the practice part it will come in handy as a style reference, not just as a picture.

A large task and a long conversation

A long task can stall before there's a single bug in the code: the process loses its shape. The context keeps growing, guesses get mixed up with decisions already made, and a restyling quietly creeps in alongside the search work. The context window is limited, sure. But the bigger problem is that it stops being clear what you're doing right now and what's already done.

TASK_SPEC pins down the goal, the boundaries and the check for the whole task. Then the work described there gets split into checkable stages - milestones. Each one has a result and boundaries, e.g. add search but keep the data and the styling as they are. That helps Claude stay on point, and helps you see where the task stands.

If the task is still vague, the first stage can be exploration: read two or three key files, name the possible approaches and the risks, then refine the task description and the stages that follow.

Finish every stage with checks and a short summary: what was done, how it was checked and what comes next. If the project is in Git, save the checked changes as a small commit. The summary keeps the decisions, the commit keeps the version of the code; together they help you check the result and pick the work up again after a break.

What counts as a stage

A milestone is a point where the task has an intermediate result and that result can be checked. From here on, the work leading up to such a point is called a stage.

A stage has three parts it can't do without:

  • the result - what exactly will start working;
  • the boundaries - what you leave alone this time;
  • the check - what you'll look at to be sure.

You can see the difference in how it's worded. "Work on the search" has no end point: you can keep working forever, and anyone can declare it "done". "Search finds a row by locale code, and an empty query returns the whole list" does have one, and you can see it in the browser in thirty seconds.

There's no universal limit on time or number of files. Size still matters, though. If several independent changes got mixed together inside a stage and you can't check the result in one clear pass, make the stage smaller. Aim for the next useful result you can get and check on its own.

Split the result into working parts

Take a sample locale catalog. Each record has the same fields as in the mockup above: locale code, language, country, currency and domain. To keep things simple, the ready-made array sits right in app.js, which is included in index.html. The page doesn't load the data with a separate request, so you can open it in the browser without a server. Here's the data set itself:

[
  { "code": "en-US", "language": "English", "country": "United States", "currency": "USD", "tld": ".us" },
  { "code": "de-DE", "language": "German", "country": "Germany", "currency": "EUR", "tld": ".de" },
  { "code": "fr-CA", "language": "French", "country": "Canada", "currency": "CAD", "tld": ".ca" },
  { "code": "ja-JP", "language": "Japanese", "country": "Japan", "currency": "JPY", "tld": ".jp" }
]

The whole request goes like this: a list, search and a country filter. The "decent look" from the intro doesn't get a stage of its own: the page gets its basic look together with the list in M1, and only new styling, meaning a change of theme, goes into the "deferred" column. Let's break the request into three stages.

A staircase up a mountain with signposts reading Milestone 1, Milestone 2 and Milestone 3
StageResultHow to checkDeferred
M1the page shows the ready-made list of localescompare the four rows and their fields against the array in app.jssearch, filter, new styling
M2search by locale code or languagede-DE and German each find one record, zzz finds none, an empty query returns all fourcountry filter
M3the country filter works together with searchCanada and French leave fr-CA; Canada and German give an empty list; clearing both conditions brings back four recordsnew features and styling
  • M1 list
  • M2 search
  • M3 filter

First you need a working list: it's a convenient base for checking how records get filtered. Search and the filter can be added in either order; here search goes first, and M3 checks that the two work together. An arrow between stages means the previous result has already been checked, and the green M3 is the stage after which the whole request is done.

Here the work is split by result. Each stage includes reading code, making edits and checking. A plan like "all the HTML first, then all the JS, check everything at the end" pushes checking the behavior to the very end. Finished markup can be a useful intermediate result too, but it doesn't yet show whether search works.

Keep in mind that the agent itself leans toward exactly that kind of plan, layer by layer: all the markup first, then all the logic. People who work with AI agents a lot say the same (Matt Pocock among them, in a workshop on his workflow): that plan shows up even when you've explicitly asked to split by result. So check the stages the agent proposes for one thing: at the end of each one, can you open something and see it working?

The first stage can be exploration

If it's unclear how the data is organized, don't rush to plan the code. The result of the first stage then reads like this: the file the records come from is found, the fields are named, the plan is refined. That's a checkable result too; you just check it by reading rather than in the browser.

And three stages here is an example, not a rule. sometimes it's two, sometimes six.

Give context for the current stage

Once a stage is picked, the agent needs information for that stage specifically, not the whole project. A request for M2 in the sample catalog might look like this:

We're adding search to the list of locales.
The list of four records already works. Keep its fields and styling,
don't change the contents of the source array. Add only what search needs.

Read @index.html and @app.js.

Requirements:
- a search field above the list;
- search by locale code and by language name;
- the queries de-DE and German find the de-DE record;
- the query zzz leaves no rows in the list;
- an empty query shows the whole list.

Don't add the country filter this time.
If you need another file, name it and read it.
First state the result and boundaries of this stage, don't change any files yet.

If the proposed boundaries work for you, ask it in your next message to carry out M2 and list the checks to run in the browser. While you're at it, ask it to separate what it checked itself from what's still left for you to check.

The paths in the example are placeholders; your files will be different. If the work needs a source the request doesn't mention, the agent should name it and read it: guessing a file's contents from its name is a bad idea, and you shouldn't ask for that either.

Styling is a separate kind of information here, and at this stage it's exactly what gets kept as is. Sometimes it's the other way round: the look is part of the stage's result, for instance when the page appears for the first time. You can't specify it in words - a request to "make it look nice" contains nothing you can check, and the decision stays with the agent. In that case attach a reference: a mockup, a screenshot or a small file with colors and class names.

It's also worth remembering what a line like that is not. "Don't add the country filter this time" is an instruction, not a technical barrier. It sets a direction but blocks nothing. So yes, you still look at the result yourself.

Ask about the result before any edits

A short summary of the result and boundaries helps you catch a mismatch with what you had in mind before any edits happen. It doesn't guarantee the implementation will be right: a finished stage still has to be checked.

When it's time to shrink a stage

Stages tend to grow while you're working on them. You started with search, and along the way it turned out it would be nice to change the data structure, refresh the styling while you're at it, and add navigation at the top. Sounds familiar.

For each new wish, ask whether it's needed for the result you picked. A new color theme and navigation can wait, but a visible input field for search is a must. If the records lack the information search has to work on, clarify the boundaries first: you can add to the data in the current stage, or make preparing it a separate checkable stage. A "don't change the data" rule can't be quietly dropped halfway through.

The clarifying request is short:

Don't change the data, styling or navigation this time,
write them down as separate items for later.
We're keeping search by code and language: input field, matches, empty query.

A stage is finished when the agreed result is there and its check has been run. If the check failed, sort out the current stage first; moving on to the next one only adds uncertainty.

Sometimes a stage just doesn't go well: you correct the agent, it redoes the work, and it's still wrong. The failed attempts don't go anywhere. They stay in the conversation, and the next attempt builds on them. The Claude Code documentation gives a simple rule of thumb: if you've had to correct the same thing more than twice, start a clean conversation with a request that includes everything you've learned so far. For example: "the approach with a separate array for search didn't work, it drifts out of sync with the source data; search the main array directly". A request like that usually works better than a third correction in a row. And if even that doesn't help, the stage is most likely too big and worth splitting.

Save a point to resume from

Checking a stage: if the result isn't confirmed, go back to the edits; if it is, save the state and move onCurrent stage editsCheck the resultagainst agreed criteriaKeep workingon the same stageSave the stateresult and checksFailedPassedNext stage
The check decides whether you can move on

The left branch leads back to edits on the same stage: its result isn't confirmed yet. The right one leads to writing down the state and on to the next stage. In the note, save both the checks that passed and whatever was left outside them.

Checked the result? Write down the state. The note isn't there for reporting's sake, it's there so that a day later, or in another conversation, you don't have to reconstruct the history by digging through the chat.

Any file name will do (an existing working note works too). Let's call it PROGRESS.md. Below is a sample note after M1 was checked; in your own note, write down only what you actually did and observed:

# Locale catalog

## Done
M1: the page shows the list of locales from the array in app.js.

## Checked
Opened index.html in the browser: 4 records, all five fields in each row
match the source array in app.js.

## Not checked
What the page shows if the data set is empty.
This check wasn't part of the M1 criteria; behavior for an empty array is unknown so far.

## Decisions
Data is kept as an array in app.js, no separate data loading.
Record contents and styling stay unchanged when search is added.

## Rejected
Loading data from a separate JSON file: without a server the browser
can't read it, and the page has to open as a file.

## Files
index.html, app.js

## Remaining stages
M2: search by locale code or language name works.
Check: de-DE and German find the de-DE record;
zzz finds no records; an empty query shows all four.

M3: the country filter works together with search.
Check: Canada + French leave fr-CA;
Canada + German give an empty list;
clearing both conditions brings back four records.

## Next step
Do M2. Don't add the country filter yet.

The key section here is "Checked". A line saying "everything works" means nothing a week later, while "opened it in the browser, 4 records, fields match" does. The separate section for what wasn't checked exists precisely because by tomorrow you won't remember that difference.

The "Rejected" section is easy to dismiss as unnecessary, since it lists things that aren't in the project. But in a new conversation the agent knows nothing about past attempts and may, in all good faith, suggest the same path again. One line with the reason saves a whole round.

You can have the agent write the note, but check it against the current files and the check results. It may lean on the conversation history, where plans sit right next to the actions actually taken. For an observation in the browser, say who made it; don't credit the agent with a check it never ran.

Two limitations. The day-to-day progress of a task doesn't belong in CLAUDE.md - that file holds the project's permanent rules, not what you happen to be busy with today. And the note doesn't save a version of the code: it's text about the state, not a copy of the files.

Carry on with the right information

The boundary between stages is a convenient place to decide which conversation to keep working in. The question is simple: is the current conversation helping with the next step, or is it already getting in the way?

If it's helping, keep going in it, nothing special needed. There's no rule that says "clear the conversation after every stage", and no percentage threshold after which it's supposedly time to start over.

If you're not sure how much room the history has taken up, run /context and look at how much of the window is used. Compare that with where the work stands: which long outputs and discussions does the next stage still need? The command helps you gauge the volume. Which information is actually useful is still your call.

If unrelated material has piled up and you've decided to start a clean conversation, save the note first. The /clear command starts a new conversation: the previous history isn't fed into its working context, the source files stay on disk, and project instructions keep loading. It doesn't delete the old conversation for good: you can get back to a saved conversation with /resume. But to continue in a new session, it's better to hand over the decisions you need explicitly.

To continue you need both the PROGRESS.md note and the current files: read them, reconcile the state, and only then do M2PROGRESS.mdChecks and decisionsWhat's left to doCurrent filesindex.html + app.jsCurrent implementationRead and reconcile the stateResolve mismatches before new editsContinue with M2Once the state is clear
The note and the code complement each other

The two arrows meet before the work continues. The note carries the decisions and check results, and the files show the current code. The agent reads both sources and summarizes the state; you check its answer before handing it M2.

Starting the new conversation is pretty mundane:

Read @PROGRESS.md, @index.html and @app.js.
In your own words, tell me what's already done and checked, and what's left.
Don't change any files yet.

Read the answer carefully. If the agent calls something done that you never did, compare what it says with the note, the files and the check results. The cause could be an inaccurate note, outdated code or a mistake by the agent itself. Fix the mismatch before any new edits: a clean conversation by itself doesn't fix a wrong task description and doesn't guarantee that the agent understands it correctly.

Build the whole catalog yourself

That's the walkthrough done; next you build the same catalog yourself, from an empty folder to a checked M2 and a point to resume from. The four steps follow the order of the article. For each one, write the prompt yourself first and open our version only as a second opinion: the point is to decide what information the agent needs, not to copy a line.

Step 1. A plan without code

There's no project yet, and the first urge is to ask "build the catalog". Then files show up before any decisions about them. This step aims for the opposite: a plan, and not a single file. Before you type, run through four things in your head:

  • how to keep the agent from jumping straight into writing code;
  • what information to give instead of files that don't exist yet;
  • which technical constraints to name so that no unneeded tool sneaks in;
  • what to ask for as output when there's nothing to create yet.

Our version:

I want a practice locale catalog: a list of records, search over it
and a country filter. There's no project yet.

The page has to open in the browser as a file, with no server and no build step:
just index.html, styles.css and app.js. Each record has these fields: locale code (en-US),
language, country, currency and domain.

First make a plan:
- state the goal, the boundaries and how to check the whole task;
- split the work into three stages;
- for each stage, name the result and what I'll see when I check it;
- tell me what information you're missing from me;
- list the files you would create.

Don't create any files yet.

Compare the stages you get with the M1-M3 table above; a different order is fine. If the agent split the work into "all the markup, then all the JS", ask it to redo the plan so the page can do something after every stage. And if a stage has no observable behavior named, fix the wording now: before the first line of code, that costs you one message.

Step 2. Only M1, with a reference from the start

The scope is narrow: the page shows four records, and that's it. No search, no filter. The look, though, can't be put off - the page appears for the first time, so it'll have some look or other. So attach a reference: save mockup.html into the project folder, static markup with no JavaScript, just colors and class names.

<style>
  body   { background:#0f172a; color:#e2e8f0; font-family:system-ui, sans-serif; padding:24px }
  .search{ width:100%; margin-bottom:12px; padding:10px 14px; border-radius:10px;
           background:#1e293b; color:#e2e8f0; border:1px solid #334155 }
  table  { width:100%; border-collapse:collapse }
  th, td { padding:10px 12px; text-align:left; border-bottom:1px solid #334155 }
  th     { color:#8b98b1; font-size:13px }
  .code  { font-family:ui-monospace, monospace; font-weight:700; color:#818cf8 }
</style>

<input class="search" placeholder="Search by code or language">
<table>
  <tr><th>Code</th><th>Language</th><th>Country</th><th>Currency</th><th>TLD</th></tr>
  <!-- sample row; app.js renders the real rows -->
  <tr><td class="code">en-US</td><td>English</td><td>United States</td><td>USD</td><td>.us</td></tr>
</table>

The search field is already drawn in the reference, but in M1 it doesn't go on the page: an empty field that does nothing is hard to check. Its style lives in the .search class and will come in handy in M2.

One more detail: you have to give the agent the data explicitly. Without it, the agent will make up its own set, and the checks from the table - four rows, de-DE, Canada - will stop adding up. Now the prompt: think about how to keep the agent inside one stage, what to ask it to show before any edits, and how you'll check the result.

We're doing only M1: the page shows the list of locales
from the array in app.js. No search and no filter this time,
don't add the search field from the reference yet.

The data is this array of four records, don't change its contents:
[paste the array from the article]

Take the look from @mockup.html: use its colors and class names,
don't invent new ones.

First show me the file plan and tell me where you'll stop.
Don't create any files until I confirm the plan.
After the edits, tell me what to open and what to compare it against.

Stop after M1, don't move on to M2 without my go-ahead.

If the plan has grown, don't confirm it; first shrink the stage down to one checkable result. If the plan is fine, allow the edits, then open the page and compare the four rows with the array. From this point on the basic look is locked in: it's kept as is, and a full theme change stays on the deferred list.

Step 3. M2 and an honest note

The prompt for M2 was already covered in "Give context for the current stage" - go back to it and put in your own paths. The walkthrough has two files; after M1 you have three: styles.css has joined index.html and app.js, so list it too. And add one line: the look is now something to keep, not something to set.

The styling was already set in M1 from @mockup.html - keep it as is,
and style the search field with the .search class from there.

If the agent named the boundaries correctly, allow the edits. If it wandered into the filter or the data structure, clear that up before any work starts. Once the three search checks have passed, write the state into PROGRESS.md following the example above, only this time after M2 and with your own list of files.

Then put your note to the test. Imagine that instead of the "Checked" section it has the line "almost everything's done, just a bit left". A week later that means nothing. Rewrite it so someone could pick the work up from it: what exactly you opened and saw, what was left outside the checks, what the next step is. Something along the lines of "M2: the queries de-DE and German find de-DE, zzz finds nothing, an empty query returns four records", "didn't check behavior with an empty data set", "next step - M3, country filter".

Step 4. Continue from a clean conversation

The last step checks the note, not the code. Start a new conversation, ask the agent to summarize the state from PROGRESS.md and the current files - all three, styles.css included - and then compare the summary with what you actually did. If the agent calls something done that you didn't check, the note is most likely too vague. The fix is editing the note, not yet another new conversation. But compare against the files too: the cause can be in the code, or a mistake by the agent itself. If it all lines up, you have a checked M1, a checked M2 and a point where M3 will start.

Our prompt isn't the gold standard

If your wording covers the same essentials - a plan before code, one stage at a time, a named result and a check - it's no worse. And if all you have so far is an idea, ask the agent to ask you clarifying questions and suggest two or three options for the boundaries: there's no need to squeeze a precise task description out of a vague idea.

How to move on to the next stage

The final sequence is short:

  1. pick the next result that can be checked;
  2. give the agent the sources for this stage and name the boundaries;
  3. carry out the current stage; write new wishes down separately;
  4. run the agreed check (in our example, that's the page in the browser);
  5. write down the state: done, checked, not checked, what you rejected, next step;
  6. decide whether to keep going in this conversation or start a clean one.

One stage isn't one message. It might take three, or fifteen, and that's normal. The number of stages isn't the number of conversations either: a couple of neighboring stages can happily share one conversation, and a single heavy one sometimes takes two.

What you get in return is that at any step you can say what already works and how that was checked. For a large task, that's probably what matters most.