Claude Architect WorkshopPDFEPUBlearnwithparam.comkaas.team

Foundations, lesson 9

Plan mode

Plan wide changes and edit small ones directly

A team asks Claude to migrate every order module to a new call style. Claude starts editing at once, and 15 files change before anyone sees the approach. The same team's one-word typo gets an hour of planning that it never needed. This is the agent at Lumen Supply, an online electronics shop. You will watch Claude edit every module with no plan, then fix it with a short team file.

I doWatch the failure and its fix.

The size of the change picks the way to work

A wide change, or one with several valid designs, gets a plan first. Claude reads the code and lists every file it would change. A person approves the plan, and only then do edits begin.

A fix in one file with one clear answer is edited directly.

A wide change gets a plan first. A small fix is edited at once.A changewide or smallPlan firstlist every file, then stopEdit directlyone file, one answerFileschanged with approvalthe choice is written in the project's CLAUDE.mdA wide change gets a plan first. A small fix is edited at once.A changewide or smallPlan firstlist every file, then stopEdit directlyone file, one answerFileschanged with approvalthe choice is written in the project's CLAUDE.md

The migration is planned and the typo is fixed

Claude is asked for the migration. It Globs the order modules and reads the shared helper and a few modules. Then it lists 15 files in order, the helper first, and stops. The tool calls show no Edit, so no file changed.

Open the reply card to read the plan. The typo request, with the same file, is fixed in one edit.

  1. ColleagueMigrate every module in src/orders from the callback form of fetchOrder to an async…
    Show the result
    Migrate every module in src/orders from the callback form of fetchOrder to an async function that awaits it. Work out how.
  2. Tool callsBash x3
    Show the result
    [
      "Bash .",
      "Bash .",
      "Bash ."
    ]
  3. ClaudeThis change touches 15 files, and there are two reasonable designs, so under the project's CLAUDE.md I have not edited anything…
    Show the result
    This change touches 15 files, and there are two reasonable designs, so under the project's CLAUDE.md I have not edited anything. Here is the plan for your approval.
    
    Current state
    - src/lib/orders.ts defines fetchOrder(id, callback), which calls callback({ total: id.length }) and returns void.
    - All 14 modules in src/orders have the same shape: export function X(id, done: (total: number) => void) calling fetchOrder(id, (order) => done(order.total)).
    - Nothing in src outside these files references fetchOrder. I did not search the rest of the repo.
    
    Files I would change, in order
    1. src/lib/orders.ts: make fetchOrder(id) return Promise<{ total: number }> instead of taking a callback.
    2. src/orders/cart.ts
    3. src/orders/checkout.ts
    4. src/orders/coupons.ts
    5. src/orders/fraud.ts
    6. src/orders/inventory.ts
    7. src/orders/invoices.ts
    8. src/orders/loyalty.ts
    9. src/orders/payments.ts
    10. src/orders/refunds.ts
    11. src/orders/returns.ts
    12. src/orders/shipping.ts
    13. src/orders/tax.ts
    14. src/orders/tracking.ts
    15. src/orders/wishlist.ts
    
    Each module would become export async function X(id: string, done: (total: number) => void): Promise<void> { const order = await fetchOrder(id); done(order.total); }.
    
    Approach choices
    - A (recommended): Change fetchOrder to return a Promise and make each module an async function that awaits it. Each module keeps its done callback, so its public signature stays the same and no callers break.
    - B: Also drop the done callback, so each module returns Promise<number>. This is a cleaner API, but it changes every export's signature. Since no callers exist in src, the breakage is limited, but I would need to confirm there are none elsewhere in the repo.
    - C: Add a new promise-returning fetchOrderAsync and keep the callback fetchOrder. This is the safest for other callers, but it leaves two code paths behind.
    
    Should I go ahead with A? If you choose B or C, tell me which. I would also grep the whole repo for other fetchOrder callers before editing. I have not run any tests, since I haven't found a test setup yet.

Claude planned 14 modules and edited none.

The team file as written

The project CLAUDE.md tells Claude how to work here. The starting file holds house facts only. It says nothing about when to plan, so a wide change is treated like a typo.

starter/CLAUDE.md
1# Lumen shop23TODO(workshop): this file holds house facts only.4Add how Claude should work on a change.56- The order modules live in `src/orders`, one file per module.7- `src/lib/orders.ts` holds the shared order helpers.

What happens to a wide change with no plan?

Take the request to migrate every module in src/orders. Claude has the Edit tool, and the team file lists only house facts.

Predict: What does Claude do with the modules?

  1. AIt stops to list the files first, because the change is wide.
  2. BIt edits the modules straight away, and reports when it is done.
Show the answer

B. Nothing in the team file asks for a plan. Claude follows the file, so it starts editing and gives no chance to review the design.

Every module is edited with no review

This is the same request with the starting file. Claude changes 15 files: the shared fetchOrder in src/lib/orders.ts and every order module. Then it reports that it is done.

Nobody saw the approach before it landed. A wrong choice in the shared helper would now sit in 15 files. The result card is red.

This is a real Claude Haiku 5.5 run through claude -p, recorded once and replayed here.

  1. ColleagueMigrate every module in src/orders from the callback form of fetchOrder to an async…
    Show the result
    Migrate every module in src/orders from the callback form of fetchOrder to an async function that awaits it. Work out how.
  2. Tool callsGrep x7, Glob x2, Read x3, Bash x2, Edit x15
    Show the result
    [
      "Grep fetchOrder",
      "Glob src/orders//*",
      "Read src/lib/orders.ts",
      "Read src/orders/tax.ts",
      "Read src/orders/cart.ts",
      "Grep from [\"'](\\.\\./orders/|\\./orders/|.*/orders/)(tax|cart|payments|refunds|returns|wishlist|fraud|checkout|shipping|loyalty|inventory|tracking|invoices|coupons)[\"']",
      "Bash .",
      "Bash .",
      "Grep orders/",
      "Grep .",
      "Grep orders[\"']",
      "Edit src/lib/orders.ts",
      "Edit src/orders/tax.ts",
      "Edit src/orders/cart.ts",
      "Edit src/orders/fraud.ts",
      "Edit src/orders/tracking.ts",
      "Edit src/orders/payments.ts",
      "Edit src/orders/shipping.ts",
      "Edit src/orders/wishlist.ts",
      "Edit src/orders/refunds.ts",
      "Edit src/orders/returns.ts",
      "Edit src/orders/checkout.ts",
      "Edit src/orders/loyalty.ts",
      "Edit src/orders/coupons.ts",
      "Edit src/orders/inventory.ts",
      "Edit src/orders/invoices.ts",
      "Grep done|callback|=> done|function \\w+\\(id: string, ",
      "Grep await fetchOrder\\(id\\)",
      "Glob {package.json,tsconfig.json}"
    ]
  3. ClaudeAll 14 modules in src/orders now await fetchOrder. I couldn't compile or run tests because the project has no…
    Show the result
    All 14 modules in src/orders now await fetchOrder. I couldn't compile or run tests because the project has no package.json or tsconfig.json.
    
    How it works
    
    - src/lib/orders.ts: fetchOrder is now async and takes only id. It returns Promise<{ total: number }> instead of calling a callback.
    - Each module (tax, cart, payments, refunds, returns, wishlist, fraud, checkout, shipping, loyalty, inventory, tracking, invoices, coupons): the function is now async, takes only id, and returns Promise<number>:
    
    ts
    export async function tax(id: string): Promise<number> {
      const order = await fetchOrder(id);
      return order.total;
    }
    
    
    Choices I made
    
    - I replaced the done callback with the return value. Nothing outside src/orders imports these modules, and fetchOrder has no other callers, so no call sites broke. If you want to keep the done parameter for compatibility, that's a small change.
    - I didn't touch the import lines or the file layout. Each module is still one file.
    
    Verification
    
    - A search finds no done or callback usage left under src/.
    - All 14 modules contain exactly one await fetchOrder(id).
    - The repo isn't a git repository, so I didn't commit anything.
    
    The CLAUDE.md file has no workflow guidance for changes like this, so I followed the defaults above.

Claude edited 15 files with no plan.

Say when to plan and when to edit

The fix is two bullets, and the house facts stay. A change to more than three files, or one with several valid approaches, gets a list of every file and a stop for approval. A fix in one file with one clear answer is edited directly.

Claude Code also has a plan mode, switched on with --permission-mode plan. It blocks every edit for the session. The team file sets the habit for everyone, and still lets a one-file fix through.

Planning is one way to choose a shape for the work. A fixed chain runs the same steps in order, such as 4 checks on each pull request, then a last pass on how the parts fit. An adaptive plan lets each finding pick the next step, which suits an open investigation.

What happens when a colleague asks for a change.ColleagueClaudeFileschange requestread filesresultreply
CLAUDE.mdgreen lines are what the fix adds
1# Lumen shop23Choose how to work before you touch a file.45- A change to more than three files, or one with several valid approaches:6 do not edit. List every file you would change, in order, and stop for7 approval.8- A fix in one file with one clear answer: edit it directly. Do not plan.910- The order modules live in `src/orders`, one file per module.11- `src/lib/orders.ts` holds the shared order helpers.
We doChange one setting at a time and predict.

The same file, two different ends

Use the switch to run the migration with the starting file, then the fixed file. The card marked "This is where the runs split" shows what Claude did with the files.

Try the typo request too. Both files fix it in one edit, which is right. Send linked problems in one message, because one fix may solve both. Send independent problems one at a time, so each result is easy to check.

A code review has the same problem at file scale. One pass over 15 files spreads attention thin. Review each file on its own, then run a last pass on the links between files.

This part is interactive in the lab: run ./workshop watch plan-and-refine.

Search by content, match by name, edit by text

The tool calls card lists what each run did: the starting run used Edit, and the planning run did not. Grep searches inside files, so it finds every caller of fetchOrder. Glob matches file names, so it lists the modules. Edit replaces text that appears once.

When the text appears 6 times, Edit cannot choose. Add more surrounding text so the match is unique, or set replace_all when every copy should change. If neither works, Claude falls back to Read and Write for the whole file.

To trace a function through 3 wrapper modules, Grep the entry point, then Read each match to follow it. For noisy discovery, an Explore subagent reads the files and hands back a short summary.

This part is interactive in the lab: run ./workshop watch plan-and-refine.

Try it: change and file

Change one setting at a time. Pick the typo or the migration, then switch between the starting file and the fix.

Every combination is a recorded run, so this works with no internet.

This part is interactive in the lab: run ./workshop watch plan-and-refine.

Refine with examples and a failing test

When the first result is close but wrong, paste the failing test and a concrete input with its expected output, so Claude can check its own fix. Give 2 or 3 examples and write the failing test first. When decisions are open, let Claude interview you before it builds. For the migration:

text
Convert fetchOrder to async. Two examples:
  before: fetchOrder(id, (o) => done(o.total))
  after:  return (await fetchOrder(id)).total
  before: fetchOrder(id, cb)
  after:  cb(await fetchOrder(id))

This test fails now. Make it pass:
  test("refund awaits fetchOrder", async () => {
    expect(await refunds("A1")).toBe(40);
  });

The harness runs claude -p with --permission-mode acceptEdits and judges a run by the files it changed. Replay needs nothing installed. A live run needs the claude command-line tool, signed in. Every run on this page is a real Claude Haiku 5.5 run.

app/agent.pyfile: 89 lines
···64 lines not shown65 """A wide change is planned and left alone, a tiny one is edited."""66 if self.ticket.wide:67 return not self.edited and self.named >= NEEDED6869 return self.edited == ["README.md"]707172def run(ticket: Ticket, code: Path, llm: Run) -> Handled:73 """Ask for one change with `code` as the project's CLAUDE.md."""74 request = cli.build_request(75 ticket.text,76 f"lumen-shop:{fingerprint(code)}",77 allowed_tools=TOOLS,78 )79 with tempfile.TemporaryDirectory() as tmp:80 project = build_sandbox(code, Path(tmp))81 result = llm(request, sandbox=project)8283 edited = [f for f in result["edited"] if f != "CLAUDE.md"]84 events = [85 {"kind": "ask"},86 *({"kind": "call", **c} for c in result["calls"]),87 ]8889 return Handled(ticket, result["result"], edited, result["calls"], events)
You doFix the starting code, then test yourself.

You do: say when to plan

Copy the starting file and open work/plan-and-refine/CLAUDE.md. It holds house facts and a TODO where the way to work should be. Say when Claude plans first and when it edits directly.

Terminal
./workshop start plan-and-refine
./workshop check plan-and-refine
./workshop watch plan-and-refine

The checks want no TODO and a number that says how wide is wide. A wide change or one with several approaches gets a plan, and a one-file fix is edited directly.

Check your understanding

Answer each question, then read why every option is right or wrong. The wrong options are the mistakes engineers make on the job.

1. A migration touches many modules, and the edits collide in one module because nobody agreed the approach. What is the best fix?

  1. AEdit the modules one at a time and re-run the tests after each edit.
  2. BUse plan mode first, agree the approach, then execute the edits.
  3. CPlan every change from now on, including one-line fixes.
  4. DRun the same migration again with a longer prompt.
Show the answer

Answer: B

  • A Testing after each edit finds collisions late. The approach is still undecided.
  • B Correct. A plan agrees the design before any module changes.
  • C Planning a one-line fix costs more than the fix. Match the effort to the change.
  • D A longer prompt still starts editing at once, with no agreed approach.

2. A stack trace points at one line in one file, and the fix is clear. A plan would take 40 minutes. What is the best way to work?

  1. APlan it in plan mode so a person approves the edit first.
  2. BSplit the fix across two sessions so each one stays short and cheap.
  3. CInterview Claude about the design before touching the file.
  4. DEdit it directly. One file with one answer needs no plan.
Show the answer

Answer: D

  • A A plan adds 40 minutes to an answer that is already clear.
  • B Two sessions add overhead to a one-line change.
  • C An interview is for open decisions. This one has none.
  • D Correct. A small, clear fix is edited directly.

3. A caching change has three valid options with different costs, and the team has not chosen one. Claude starts editing. What should change?

  1. AStart in plan mode to weigh the options before any edit.
  2. BEdit directly but keep the diff under 50 lines so review stays easy.
  3. CPick the cheapest option and let the tests decide.
  4. DAsk for the change one file at a time.
Show the answer

Answer: A

  • A Correct. An open design needs a proposal a person can choose from.
  • B A short diff can still lock in the wrong option.
  • C Tests check behavior. They do not compare three designs.
  • D Smaller steps do not settle which design to use.

4. Which two things does plan mode give you? Select two.

  1. AClaude edits files faster, because it skips reading.
  2. BPlan mode writes the plan into the source files as comments.
  3. CClaude explores the code without editing any file.
  4. DA person approves the approach before edits start.
Show the answer

Answer: C, D

  • A A plan reads more, not less, before anything is edited.
  • B A plan is a proposal. It does not change the source files.
  • C Correct. Reading and proposing change nothing.
  • D Correct. The approval is the point of the plan.

5. The request says make the parser better, and each of 4 passes drifts somewhere new. What is the best fix?

  1. AAsk Claude to score each pass out of 10 and keep the best.
  2. BSwitch to plan mode, which makes the goal clear.
  3. CGive 2 or 3 input and output examples that show the target.
  4. DAsk for a fifth pass with a longer description of quality.
Show the answer

Answer: C

  • A A score from the same vague goal still has no target to measure against.
  • B A plan of a vague goal is still vague. The missing piece is an example.
  • C Correct. Examples turn a wish into something Claude can match.
  • D More adjectives are still not a target, so the next pass drifts too.

6. A fix must handle 5 edge cases, and each pass breaks a different one. What is the best fix?

  1. ADescribe all 5 edge cases in one long sentence before any code runs.
  2. BFix them one by one and hope none come back.
  3. CWrite the tests for the 5 cases first, then fix what fails.
  4. DEdit the file directly so the passes are faster.
Show the answer

Answer: C

  • A A sentence cannot tell Claude which case broke. A failing test can.
  • B Without tests, a fix for one case can break another silently.
  • C Correct. Failing tests name what is left, and Claude can run them.
  • D Speed is not the problem. The passes have no check between them.

7. The cache and failure decisions are unclear, and Claude assumes answers for both. What is the best fix?

  1. AGive Claude a longer description and let it assume again.
  2. BLet Claude interview you about the open decisions first.
  3. CPlan every module in detail, including the ones that are already clear.
  4. DPick an answer yourself and hide the question.
Show the answer

Answer: B

  • A More words still leave the two decisions open, so Claude assumes again.
  • B Correct. The questions bring out the requirements before code exists.
  • C The clear modules do not need a plan. Only the open decisions do.
  • D A hidden choice is still an assumption, and the team cannot review it.

8. Which two rules apply when you send fixes to Claude? Select two.

  1. ASend linked problems together, because one fix may solve several.
  2. BSend every issue in one message, whether linked or not.
  3. CSend independent problems one at a time.
  4. DSend one problem only when it touches one file.
Show the answer

Answer: A, C

  • A Correct. Linked problems share a cause, so they belong in one request.
  • B Independent issues in one message blur which change fixed which problem.
  • C Correct. One request per independent issue keeps each result easy to check.
  • D The link between problems decides this, and not the file count.

9. A search must find every caller of fetchOrder across the modules. Which tool fits?

  1. AGrep, because it searches the contents of files.
  2. BEdit, because it can find a string.
  3. CGlob, because the callers share a file name.
  4. DGlob, because it searches the text inside files.
Show the answer

Answer: A

  • A Correct. Grep searches contents, so it finds each caller.
  • B Edit replaces text. It does not report where text appears.
  • C Callers sit in many differently named files, so names do not help.
  • D Glob matches file names. It does not look inside files.

10. Edit fails because the text it must replace appears 6 times in the file, and only the copy in the refund function should change. What is the best fix?

  1. ASet replace_all, so all 6 copies change in one call.
  2. BInclude more surrounding text, so the match is unique.
  3. CSwitch to Glob, which can pick one match inside a file.
  4. DUse Grep to rewrite only the line inside the refund function.
Show the answer

Answer: B

  • A replace_all is for changing every copy. Here 5 of the 6 must stay.
  • B Correct. With enough context the text appears once, and Edit can choose it.
  • C Glob matches file names, so it cannot choose between matches in a file.
  • D Grep finds text. It does not change any file.

11. A function is called through 3 wrapper modules, and you need the real one. What is the best way to trace it?

  1. AGlob every file whose name contains the function's name, then edit those.
  2. BRead every module before searching.
  3. CGrep the entry point, then Read each match to follow it.
  4. DEdit each wrapper to print its name.
Show the answer

Answer: C

  • A File names do not show who calls whom.
  • B Reading everything first spends context on files that do not matter.
  • C Correct. Grep finds the next hop and Read confirms it.
  • D Editing the code to learn what it does changes files for no reason.

12. Which two facts about the built-in tools are true? Select two.

  1. AGrep matches file names only.
  2. BEdit replaces text that appears once.
  3. CGlob searches the text inside each file.
  4. DRead and Write handle whole files.
Show the answer

Answer: B, D

  • A Grep searches contents. Glob is for names.
  • B Correct. The text must be unique, or Edit cannot choose.
  • C Glob matches names. Grep is the tool for contents.
  • D Correct. They read or write a whole file.

13. A review of 14 files in one pass misses bugs because attention is spread thin. What is the best fix?

  1. AReview each file on its own, then the links between files.
  2. BReview all 14 files again in the same pass, more carefully this time.
  3. CReview only the 3 largest files.
  4. DAsk for a longer review of everything.
Show the answer

Answer: A

  • A Correct. One pass per file keeps attention local, and a last pass checks the links.
  • B A second identical pass has the same spread of attention.
  • C Bugs in the 11 other files stay unread.
  • D More length does not focus the attention.

14. An open investigation of an unknown codebase has steps nobody can list in advance. Which plan fits?

  1. AA fixed chain of 5 steps written up front and followed in order.
  2. BAn adaptive plan that changes with what turns up.
  3. COne step that reads everything at once.
  4. DNo plan, only direct edits.
Show the answer

Answer: B

  • A A fixed list cannot follow what the code turns up.
  • B Correct. Each finding picks the next step.
  • C One read of everything fills the context with files that may not matter.
  • D Edits come after the code is understood.

15. The same 4 checks run on every pull request, in the same order. Which plan fits?

  1. AAn adaptive plan built afresh each time.
  2. BAn interview before each check.
  3. CA plan mode session for each pull request.
  4. DA fixed chain of the 4 checks.
Show the answer

Answer: D

  • A A predictable task does not need a new plan on each run.
  • B The questions are known, so there is nothing to interview.
  • C A reviewed plan for a routine check adds an approval that changes nothing.
  • D Correct. Steps that repeat make a fixed chain.

16. Which two facts describe task decomposition with a chain? Select two.

  1. AThe steps run in order, each using the one before.
  2. BA last pass checks how the parts fit together.
  3. CThe steps run at once, with no order.
  4. DEach step repeats the whole task.
Show the answer

Answer: A, B

  • A Correct. A chain is sequential.
  • B Correct. The integration pass catches what single steps miss.
  • C A chain has an order. Steps that run at once are a different shape.
  • D Each step does a part of the task, not the whole of it.

Play: predict the output

The team wants a plan approved before any wide change, so they wrote it into project memory.

markdown
# Lumen shop

Before a change to more than three files, plan first.
Do not edit anything until I approve the plan.

Predict: What stops Claude from editing before you approve?

  1. AOnly the instruction. Memory is context, so nothing blocks an edit.
  2. BClaude Code reads the rule and blocks Edit until you approve.
Show the answer

A. Memory steers what Claude does but enforces nothing. When an edit must wait, start in plan mode, which explores and proposes without editing until you approve.

References

Conclusions

  • A wide change, or one with several valid designs, gets a plan before any edit.
  • A one-file fix with one clear answer is edited directly.
  • Refinement works best with examples of input and output and a failing test.

You can now pick the way to work by the size of the change, and refine a result with evidence.

Plan mode

Claude reads and proposes, and edits wait for approval

Direct edit

Claude changes the file at once, for a small clear fix

Refinement

Improving a result with examples and tests, not a vague request

Interview

Claude asks questions first, so the requirements come out