TAI BUI
Back to curriculum
BuildPython

The Minimal Agent Workbench

The smallest useful workbench is three files: a root router, a state file, and a task board. Everything else layers on top. If a repo can't carry these three, no model will save it.

TIME: ~45 minutesPREREQUISITES: Phase 14 · 31 (Why Capable Models Still Fail)

Learning Objectives

  • Define the three files that form the minimum viable workbench.
  • Explain why a short root router beats a long monolithic `AGENTS.md`.
  • Build a state file the agent can read at every turn and write at the end.
  • Build a task board that survives multi-session work without chat history.

The Problem

Most teams reach for a workbench by writing a 3000-line AGENTS.md and calling it done. The model loads it, ignores the parts it cannot summarize, and still fails on the same surfaces it failed on before.

The fix is the opposite shape. A tiny root file routes the agent into deeper files only when they're relevant. Durable state gets read before acting and written after. A task board says what is in flight, what is blocked, and what is up next.

That's three files, each with one job, and each machine-readable enough to grow into a real system later.

Warm-up0/3
1. A batch job that processes 1,000 files crashes at file 600 and is restarted. What lets it resume at file 601 instead of starting over?Warm-up
2. You hand a new teammate a 3000-line onboarding document on day one. What usually happens?Warm-up
3. When a long chat is trimmed to fit a model's context window, what happens to early details?Warm-up

The Concept

Mermaid diagram

A minimal agent workbench is three files in a repo: a short AGENTS.md that routes the agent to everything else, an agent_state.json that records where the work stands, and a task_board.json that queues what is left. Each one is covered below.

AGENTS.md is a router, not a manual

A router is a short root file that points the agent at what it needs and nothing more. A good AGENTS.md points at:

  • The state file (where you are).
  • The task board (what is left).
  • The deeper rules (under docs/agent-rules.md).
  • The verification command (how to know it works).

Anything longer goes in deeper docs, loaded only when needed. Long manuals get ignored, while short routers get followed.

agent_state.json is the system of record

The system of record is the file the workbench treats as authoritative once chat is gone. Here that file carries the active task id, the touched files, the assumptions made, the blockers, and the next action. The agent reads it at every turn, and the next session reads it instead of replaying chat.

State lives in a file because chat history is unreliable. Sessions die and conversations get trimmed. The file doesn't.

task_board.json is the queue

The task board holds every task with a status of todo | in_progress | done | blocked. The agent pulls from it when state is empty, and you read it when you want to know whether the agent is on track.

A task has an id, a goal, an owner (builder, reviewer, or human), and acceptance criteria. The board is small on purpose: when it grows past a screen, you have a planning problem, not a board problem.

Three files is the floor, not the ceiling

Later lessons add scope contracts, feedback runners, verification gates, reviewer checklists, and handoff packets. All of them assume these three files exist.

Check: The Concept0/13
1. What does a workbench's agent_state.json carry?Define
2. Which set of fields does a task on task_board.json carry?Define
3. What is a router in an agent workbench?Define
4. What does it mean for a file to be the system of record in a workbench?Define
5. Which three files make up the minimum viable agent workbench?Define
6. Why does the lesson keep state in a file instead of in chat history?Explain
7. Why does a short root router get followed when a long monolithic AGENTS.md does not?Explain
8. The state file and the task board both live in the repo. How do their jobs differ?Explain
9. The task board is small on purpose. What does the lesson say when it grows past a screen?Explain
10. How does the lesson frame the three-file minimum against later additions?Explain
11. A new session starts. agent_state.json shows task t-3 active and blocked on missing credentials, and the board lists t-4 as todo. What should the session do first?Apply
12. Your root AGENTS.md has grown to 400 lines and the agent keeps missing rules. What should you do first?Analyze
13. A builder agent stops mid-project and a reviewer agent must continue in a separate session with no shared chat. Which setup fits?Design

Build It

code/main.py writes the minimal workbench into an empty repo and runs a single agent turn that:

  1. Reads agent_state.json.
  2. Pulls the next task from task_board.json if state is empty.
  3. Marks it in_progress and sets the next action.
  4. Writes back updated state.

Run it:

python3 code/main.py

The script creates workdir/ next to itself, lays down the three files, runs one turn, and prints the diff. Run it again and the second turn picks up where the first left off.

Check: Build It0/6
1. main.py runs once in an empty workdir. After the turn, what do the active task and board statuses show?Apply
2. You run main.py a second time without changing any files. What next action does it print after the turn?Apply
3. Starting from an empty workdir, how many runs of main.py does it take before T-001 is marked done?Apply
4. agent_state.json names active task T-009, but the board holds only T-001 and T-002. What does one turn do?Apply
5. Every task on the board is done and active_task_id is null. What does a turn do?Apply
6. A turn moves T-001 to in_progress in memory, but the script crashes before the save calls. What does the next session find?Analyze

Use It

The same three roles show up inside production agent products under different names. This mapping is illustrative, not a full account of each product:

  • Claude Code: CLAUDE.md (or a symlink to AGENTS.md) is the router. For state and the board, keep agent_state.json and task_board.json in the repo and point the router at them.
  • Codex / Cursor: workspace rules or AGENTS.md are the router. State and board are again files you add and reference from it. Each product also has its own session memory or task list, which fill similar roles but live outside your repo.
  • Custom Python agent: the same files you just wrote.

The names change. The shape doesn't.

Check: Use It0/1
1. In Codex or Cursor, workspace rules or AGENTS.md act as the router. Where do the state and board live?Explain

Production patterns in the wild

The minimum workbench holds up in real monorepos once three patterns are layered on top. They're independent, so pick the ones your repo needs.

Nested AGENTS.md with nearest-wins precedence. OpenAI ships 88 AGENTS.md files across its main repo, one per subcomponent. Tools that read AGENTS.md natively, such as Codex and Cursor, walk from the working file toward the repo root and concatenate every AGENTS.md they find on the way, so sub-directory files extend the root file. Claude Code reads CLAUDE.md instead, which is why the symlink below exists. Codex adds AGENTS.override.md to replace rather than extend. That mechanism is Codex-specific, so avoid it for cross-tool work. Augment Code's measurement is the line that matters: the best AGENTS.md files give a quality jump equivalent to upgrading from Haiku to Opus, and the worst ones make output worse than no file at all.

Anti-patterns to refuse, even when they look like coverage.

  • Conflicting instructions silently drop the agent from interactive to greedy mode (ICLR 2026 AMBIG-SWE: 48.8% → 28% resolve rate). Give the rules ranked numbers so the agent knows which one wins when, say, "ask before editing" collides with "don't interrupt me", instead of stacking them flat.
  • Unverifiable style rules ("follow the Google Python Style Guide") with no enforcement command let the agent invent compliance. Pair every style rule with the exact lint command.
  • Leading with style instead of commands buries the verification path. Put commands first and style last.
  • Writing for humans instead of agents wastes context budget. Terseness is a feature.

Cross-tool symlinks. One root file plus symlinks (ln -s AGENTS.md CLAUDE.md, ln -s AGENTS.md .github/copilot-instructions.md, ln -s AGENTS.md .cursorrules) keeps every coding agent on the same source of truth. Nx's nx ai-setup automates this across Claude Code, Cursor, Copilot, Gemini, Codex, and OpenCode from a single config.

Check: Production patterns in the wild0/9
1. What does the lesson say about Codex's AGENTS.override.md?Define
2. How do tools that read AGENTS.md natively handle nested files?Define
3. A root AGENTS.md says "always ask before editing" in one place and "fix issues without interrupting me" in another. What does the lesson say tends to happen?Explain
4. Which AGENTS.md habit does the lesson flag as letting the agent invent compliance?Explain
5. Why does the lesson suggest symlinks such as ln -s AGENTS.md CLAUDE.md?Explain
6. An AGENTS.md opens with two pages of style rules and ends with the test command. The agent often ships code that fails the tests. Which anti-pattern fits?Analyze
7. Teams keep CLAUDE.md, .cursorrules and AGENTS.md as three hand-edited copies. Which failure do symlinks to one root file avoid?Analyze
8. A monorepo has twelve packages with different test commands, and the team uses both Codex and Claude Code. Which design fits?Design
9. Your agent resolves fewer tasks because AGENTS.md says "ask before editing" and also "don't interrupt me". Which fix follows the lesson?Design

Ship It

outputs/skill-minimal-workbench.md generates the three-file workbench for any new repo: an AGENTS.md router tuned to the project, an agent_state.json with the right keys, and a task_board.json seeded with the current backlog.

Author's Note

I treat the router as the file to protect. The failure this lesson opens with is a 3000-line AGENTS.md that the model loads and then ignores the parts it cannot summarize. The fix it gives is a short file that points elsewhere. So when a new rule wants to live in the root, I'd put it in docs/agent-rules.md and leave a pointer behind. My bet is that a root file short enough to lint (exercise 4 caps it at 80 lines) stays a router, and once it starts reading like a manual it has stopped doing the job.

Exercises

  1. Add a last_run timestamp to agent_state.json. Refuse to run if the file is older than 24 hours unless an operator confirms.
  2. Add a priority field to the task board and change the puller to always pick the highest priority todo.
  3. Migrate task_board.json to JSON Lines so each task is a line and diffs are clean in version control.
  4. Write a lint_workbench.py that fails if AGENTS.md is over 80 lines or references a file that does not exist.
  5. Decide which one of the three files would hurt the most to lose. Defend it.

Key Terms

TermWhat people sayWhat it actually means
Router"The instructions file"Short root file that points the agent at deeper docs and files
State file"The notes"Machine-readable record of where the agent is, written every turn
Task board"The backlog"JSON queue of work with status, owner, acceptance
System of record"Source of truth"The file the workbench treats as authoritative when chat is gone

Further Reading

Code

Ships With