Files
analysis_claude_code/s05_todo_write/README.md
2026-07-31 03:15:58 +08:00

5.2 KiB

s05: TodoWrite — An Agent Without a Plan Drifts Off Course

English · 中文 · 日本語

s01 → s02 → s03 → s04 → s05s06 → s07 → ... → s20 → s21

"An agent without a plan goes wherever the wind blows" — List the steps first, then execute. Complex tasks are less likely to miss steps.

Harness Layer: Planning — Let the Agent think before it acts.


The Problem

Give the Agent a complex task: "Rename all Python files to snake_case, run tests, and fix failures."

The Agent starts working, renames 3 files, runs a test, finds 2 failures, starts fixing. While fixing, it forgets the original goal was "rename to snake_case", the test failures have consumed all its attention.

The longer the conversation, the worse it gets: tool results keep filling the context, diluting the system prompt's influence. A 10-step refactoring: after steps 1-3, the Agent starts improvising because steps 4-10 have been pushed out of its attention.


The Solution

Todo Overview

The minimal hook structure from the previous chapter is preserved, focusing on the new todo_write tool and reminder mechanism. todo_write does no actual work, can't read files or run commands, it simply lets the Agent organize its thoughts before diving in.

The dispatch mechanism is unchanged; the new tool is still routed through TOOL_HANDLERS[block.name]. However, to demonstrate the todo reminder, a counter was added to the loop: after 3 consecutive rounds without calling todo_write, a reminder is injected.


How It Works

The todo_write tool accepts a list with statuses, keeps it in the current process memory, and displays progress in the terminal:

CURRENT_TODOS: list[dict] = []

def run_todo_write(todos: list) -> str:
    global CURRENT_TODOS
    CURRENT_TODOS = todos

    lines = ["\n## Current Tasks"]
    for t in CURRENT_TODOS:
        icon = {"pending": " ", "in_progress": "▸", "completed": "✓"}[t["status"]]
        lines.append(f"  [{icon}] {t['content']}")
    print("\n".join(lines))
    return f"Updated {len(CURRENT_TODOS)} tasks"

The tool definition joins the other 5 in the dispatch map:

TOOLS = [
    {"name": "bash",       ...},
    {"name": "read_file",  ...},
    {"name": "write_file", ...},
    {"name": "edit_file",  ...},
    {"name": "glob",       ...},
    # s05: new entry
    {"name": "todo_write", "description": "Create and manage a task list ...",
     "input_schema": {
         "type": "object",
         "properties": {
             "todos": {
                 "type": "array",
                 "items": {
                     "type": "object",
                     "properties": {
                         "content": {"type": "string"},
                         "status": {"type": "string", "enum": ["pending", "in_progress", "completed"]},
                     },
                 },
             },
         },
     },
    },
]

TOOL_HANDLERS["todo_write"] = run_todo_write

Nag reminder: when the model has not called todo_write for 3 consecutive rounds, a reminder is automatically injected:

if rounds_since_todo >= 3 and messages:
    messages.append({
        "role": "user",
        "content": "<reminder>Update your todos.</reminder>",
    })
    rounds_since_todo = 0

Typical flow when the Agent receives a task: first call todo_write to list all steps (all pending) → pick one step, set it to in_progress → complete it, set to completed → look at the next pending → continue. After 3 rounds without todo_write, the loop appends a reminder before the next LLM call.

Key insight: todo_write doesn't give the Agent any additional execution capability. What it adds is planning capability.


Changes from s04

Component Before (s04) After (s05)
Tool count 5 (bash, read, write, edit, glob) 6 (+todo_write)
Planning None Stateful TODO list + nag reminder
SYSTEM prompt Generic prompt Added "plan before executing" guidance
Loop Unchanged Dispatch unchanged, added rounds_since_todo counter and reminder injection

Try It

cd learn-claude-code
python s05_todo_write/code.py

Try these prompts:

  1. Refactor s05_todo_write/example/hello.py: add type hints, docstrings, and a main guard (should list 3 steps first, then execute)
  2. Create a Python package under s05_todo_write/example/demo_pkg with __init__.py, utils.py, and tests/test_utils.py
  3. Review Python files under s05_todo_write/example and fix any style issues

What to watch for: Was the first tool call todo_write? How many TODO steps were listed? Did statuses move from pending to in_progress / completed during execution?


What's Next

The Agent can plan now. But if a task is too large, say "refactor the entire auth module", a TODO list alone isn't enough. That task is itself a collection of dozens of subtasks that would drown in a single conversation's context.

→ s06 Subagent: Break large tasks into subtasks, each handled by an independent Agent with its own clean context, no cross-contamination.