Skip to content

1 · Context Engineering — The Central Skill

The quality of an agent's output is capped by the quality of its context: an agent can only act on what it can see. Before any technique for driving an agent comes the skill of controlling what enters its view. That is the subject of this module, and every later module depends on it.

An agent's context window is its working memory, and it is finite. Everything the agent knows about the task at hand — the files it has read, your instructions, the last few things it tried — lives in that window, and nothing else does. When the files it needs are absent, it guesses. When the window is crowded with irrelevant material, the signal it needs is buried. In both cases the output degrades, and the cause stays hidden unless you are watching the context rather than the code. The operator's first job is therefore curation: put in what the task needs, and leave out what it does not.

The governing rule is that fresh context beats bloated context. A focused window of the right files and instructions outperforms a large dump of everything. When an agent begins to thrash, the fix is usually less context rather than more: start clean and reintroduce only what matters.

The widget below makes the tradeoff concrete. Fill the window and watch the readout change from focused to bloated as you add a repository dump and stale dead-ends.

Interactive: a fixed context window you fill with items — watch focused change to bloated as you add the repo dump and stale dead-ends. (Enable JavaScript to try it.)

Concept

Three levers control what is in the window. Two are file choices: the project instruction file and the files for the current task. The third is a session choice: whether the current thread is still fresh enough to keep using.

  1. Project instructions — the CLAUDE.md craft. The agent reads a project instruction file at the start of every session. In this course, that file is CLAUDE.md at the repository root. A good CLAUDE.md does not explain the whole repository. It tells the agent where to look and what is not obvious from the code: the tribal knowledge, the landmines, the conventions a newcomer would violate. Think of it as an onboarding document for a teammate who reads fast and forgets nothing, but starts each day knowing nothing about this repository.

    Use a simple placement rule when deciding what belongs there. Put only the always-needed map and landmines in CLAUDE.md. Leave raw implementation detail in the codebase, where the agent can search for it when the task reaches that deep. The file should point the agent to the right places without copying those places into the resident context.

    For example, CLAUDE.md might say where request handlers live and name the rule that every handler change needs a test. It should not copy the handler code, the full testing guide, or the framework reference. CLAUDE.md gives the agent the starting map; the task determines which files it should open next.

  2. The files the task actually touches. A single task usually changes only two or three files. Everything else in the repository is noise the agent must read past, and the risk runs both ways: if the files that matter are missing, the agent guesses at code it cannot see, and if too much else is in the window, the few lines that matter are buried. So the operator's job is to get the task's files into view and leave the rest out.

    Most of the time a good CLAUDE.md does this for you, because it records where each kind of change lives. A single line such as Change wording the model sees → app/llm/prompts.py is enough: when you ask the agent to reword the prompt, it reads that line and opens prompts.py directly instead of grepping the tree. The instructions file turns a vague request into the right file before the agent takes a step.

    When the file has no such line, or you simply want to be explicit, name the file in the prompt. "Edit the prompt in app/llm/prompts.py to add a confidence score" starts the agent in the right place; "fix the classifier" leaves it guessing and sends it hunting. If a change spans two files, name both, for example "update the response shape in app/models.py and the code that builds it in app/triage/classify.py."

    The reverse habit matters too: leave out files that only look related. For a prompt change the files are the prompt and the test that checks it; the application's entrypoint (app/main.py) is usually unrelated, and pulling it in just spends context for nothing.

  3. Freshness — start clean when the thread goes stale. A long session fills with dead ends, abandoned plans, and stale file contents, and that residue drags down the agent's later moves. The tool fights this for you by compacting — summarizing the older parts of the window to make room — but a summary loses detail. When a thread has clearly gone sideways, the stronger move is to reset: start a fresh session and restate the goal in a sentence or two.

In short: write the standing map, put each task's files in view, and start clean when the window goes stale.

Bad / Good / Great — a CLAUDE.md for the Practice Repo

The examples use Triage, the small support-ticket classifier used in the course labs. Here is the same repository with three instruction files. The distance between Good and Great is the lesson.

# CLAUDE.md

This is a Python project. It uses FastAPI. The app does ticket triage with AI.
Please write clean, well-tested code and follow best practices. Be careful and
make sure everything works. We value code quality. Always add tests. Use type
hints. Don't break anything. Thanks!

Why it fails: generic filler the agent already assumes. It offers no navigation, no tribal knowledge, and no way to verify work. "Follow best practices" tells it nothing it did not already know. This file leaves the agent no smarter about this repository.

# CLAUDE.md — Triage

A support-ticket assistant: classify an incoming ticket into a category.

## Layout
- `app/triage/classify.py` — core classification
- `app/llm/` — provider wrapper (`client.py`) and prompts (`prompts.py`)
- `app/models.py` — request/response shapes
- `eval/` — offline eval set + runner

## Run / test
- Serve: `uvicorn app.main:app --reload`
- Test: `pytest`

Why it is solid: a real map and the commands to run. A newcomer agent can find its way and verify a build. This is already most of the value.

# CLAUDE.md — Triage

A support-ticket assistant: classify a ticket into a category. Read this, then
the file it points you to — don't scan the tree.

## Where things live (start here, don't grep the repo)
- Change classification logic → `app/triage/classify.py`
- Change wording the model sees → `app/llm/prompts.py` (NOT inline in classify.py)
- Change models/providers → `app/llm/client.py` only

## Non-obvious rules (the landmines)
- Never hardcode a model id. It lives in settings, read once in `llm/client.py`.
  A model string anywhere else is a bug.
- `eval/` is the source of truth for any prompt change. Edit a prompt → run
  `python -m eval.run` and check the score didn't drop before you call it done.
- The LLM call is mocked in `tests/` (`conftest.py`). Don't hit the real
  provider in a unit test.

## Verify your work
- `pytest` must pass. A prompt change ALSO requires `python -m eval.run` to hold.

Why it is great: it is not longer for its own sake. It is navigational, saying where to look rather than describing the whole tree; it captures the tribal knowledge the code does not show (the model-id rule, the eval gate, the mocked LLM call); and it tells the agent how to verify itself. It makes the agent behave like someone who has worked here for a year.

In short, a great instructions file is not longer than a good one. It points to where things live, it records the tribal knowledge the code cannot show, and it tells the agent how to check its own work. Length is not what makes it great; aim is.

The through-line of this course is one question: how long can the agent run before it needs you? Context is the first lever on that runway. The sharper the instructions file, the longer the agent navigates and checks its own work without you.

Guided Lab

On the Practice Repo, you will run one cold task three times — with no CLAUDE.md, with a Good one, and with a Great one — and measure how the agent's wasted turns shrink each time.

  1. Clone Triage and start the agent in the repo root:

    git clone https://github.com/mrfelixwong/agentic-engineering-triage triage && cd triage
    claude
    

    Expected: the agent opens at the repo root and no CLAUDE.md is present (confirm with ls CLAUDE.mdNo such file or directory).

  2. Cold run, with no context. Give the agent this exact task:

    add a confidence score to the classification output

    Expected: it grep-scans the tree, opens several wrong files, and takes multiple turns to locate the two files that matter, app/triage/classify.py and app/llm/prompts.py. Note how many turns it spends before touching the right file. That number is your baseline.

  3. Write a Good CLAUDE.md. Create CLAUDE.md at the repo root with the layout and run/test content from the Good tab above (the ## Layout map and the ## Run / test commands).

    Expected: CLAUDE.md exists at the repo root (ls CLAUDE.md now succeeds).

  4. Fresh run. Start a clean session so the stale cold-run context is gone, then repeat the same task:

    /clear
    

    add a confidence score to the classification output

    Expected: the agent opens app/triage/classify.py early instead of scanning blindly, using fewer turns than your step-2 baseline.

  5. Push to Great. Edit CLAUDE.md to add the three things from the Great tab: the "where things live" map, one real landmine (the model-id rule, where a model string outside app/llm/client.py is a bug), and the verify step (pytest, plus python -m eval.run for any prompt change).

  6. Fresh run again. Run /clear, then repeat the same task.

    Expected: the agent edits the prompt in app/llm/prompts.py rather than inline in classify.py, does not hardcode a model id, and ends by running pytest and python -m eval.run on its own, because the file told it how to check itself. The turn count falling from step 2 to step 4 to step 6 is the runway getting longer.

Coached mode

A /lesson-1-cold-navigation instructor skill can coach this live from a second terminal. It watches your session, stops at each checkpoint, and asks a leading question before handing you the next move; it does not edit your repository for you. The skill ships in the Practice Repo; for now you can also follow the numbered steps above.

On your own codebase, repeat the exercise. Write the three non-obvious rules a new hire always trips on; those three lines are often worth more than the entire layout section.

Keep the artifact

The artifact is the CLAUDE.md you wrote: a "where things live" map, the tribal rules, and a verify step. It is not a worksheet to throw away. It ships in the repository and earns its keep on every future session, for you and for every teammate's agent, so commit it. The standing rule from here is simple: when you learn a non-obvious thing about a repository the hard way, add the line to CLAUDE.md so neither you nor the agent has to learn it twice. That file growing sharper over time is the first instance of compounding (Module 6).

Self-check

You did it right if, in a fresh session with your CLAUDE.md in place, the agent:

  • opens the correct file for the task without you naming the path, and
  • respects at least one rule you wrote that it would otherwise have broken — for example, it edits the prompt in prompts.py instead of inlining it, or it does not hardcode a model id.

If it still flails or breaks a rule you wrote, the file is describing the repository rather than navigating it. Tighten it toward the Great example.

Recall

Before moving on: name the three things that make a CLAUDE.md great rather than merely good, and why "add more detail" is not one of them.

Answer

Navigation — it points to where things live; tribal knowledge — the rules the code cannot show; self-verification — how the agent checks its own work. Length is not on the list; a great file is often shorter, just better aimed.