Source: AI Engineering with Claude (Udacity ND7426), plus current Claude Code docs: CLAUDE.md and memory, skills, and The new rules of context engineering
In Part 1, I treated Claude Code as a bounded loop: perceive, reason, act, then feed the evidence back in. The reasoning engine is only as good as the working set it sees. A precise goal still fails if the window is full of directory trees, stale chat, and a vague tool contract.
This is Part 2 of 6 in my Agentic Coding with Claude Code series. The working model I am keeping is simple: context is assembled, not typed, and it is a budget, not a bucket.
CLAUDE.md, path-scoped rules, skills, memory, tool results, and the conversation all compete for the same tokens. Unfiltered tool output is paid for on every later turn. The job is to load the right slice at the right time.
Later posts will cover the harness and Agent SDK, MCP, evals, and guardrails. This one stays on the context layer that shapes every decision in the loop.
Context Is Assembled, Not Just the Prompt
A Claude Code session does not start from your latest sentence. The product already packed a window before you typed.
At launch, Claude typically receives the system prompt, auto memory, environment and git hints, skill names and descriptions, and every CLAUDE.md that applies to the working directory, including @ imports. Your prompt is one more block in that stack. After you start working, file reads, path-scoped rules, skill bodies, and tool output join the same window.
flowchart TB
subgraph Demand["Loaded on demand"]
direction LR
R[Path-scoped rules] --> S[Skill body] --> T[Tool results]
end
subgraph Launch["Loaded at launch"]
direction LR
C[CLAUDE.md] --> I["@ imports"] --> M[Auto memory] --> D[Skill names]
end
Prompt[User prompt] --> Window[Working context]
Launch --> Window
Demand --> Window
Window --> Loop[Perceive / Reason / Act]
classDef blueClass fill:#4A90E2,stroke:#333,stroke-width:2px,color:#fff
classDef orangeClass fill:#F39C12,stroke:#333,stroke-width:2px,color:#fff
classDef greenClass fill:#27AE60,stroke:#333,stroke-width:2px,color:#fff
classDef purpleClass fill:#9B59B6,stroke:#333,stroke-width:2px,color:#fff
classDef tealClass fill:#16A085,stroke:#333,stroke-width:2px,color:#fff
classDef redClass fill:#E74C3C,stroke:#333,stroke-width:2px,color:#fff
class Prompt blueClass
class C,I orangeClass
class M,D greenClass
class R,S purpleClass
class T tealClass
class Window,Loop redClass
Two consequences follow from Part 1:
- Memory is not one file. Working context, project instructions, and durable notes are different layers. Current source and fresh tool output remain the authority.
- Tokens are a budget. File reads dominate cost. A research-heavy turn that dumps six files, or a tool that returns an unfiltered log, taxes every later decision. You pay again on the next perceive step, not only on the turn that fetched the text.
Anthropic’s current guidance matches that framing. Context engineering is the work of assembling general guidance that still leaves room for the user’s actual request. Newer Claude models need fewer hardcoded “never do X” constraints. They need cleaner interfaces and progressive disclosure.
CLAUDE.md is guidance, not a lock. Claude treats it as context. If an action must be blocked no matter what the model decides, that belongs in permissions or a hook. Guardrails are a later post. Here the question is: what should occupy the window at all?
Progressive Disclosure
The failure mode I keep seeing in my own repos is a CLAUDE.md that tries to be a handbook. Build commands, folder maps, review checklists, deploy steps, and “be careful with X” all sit in one file that loads on every turn.
Official size guidance is blunt: target under 200 lines per CLAUDE.md. Longer files cost tokens and reduce adherence. /doctor can propose trims for a checked-in file: cut what Claude can derive from the tree, keep pitfalls and conventions that differ from defaults.
My working split:
Keep in CLAUDE.md |
Move out |
|---|---|
| What this repo is for, in a few lines | Directory tours Claude can ls |
| Commands Claude cannot guess | Multi-step procedures |
| Gotchas and non-obvious invariants | Surface-specific style that only matters in one tree |
| Always / never rules that apply to every session | Side-effect workflows you want to invoke on purpose |
Procedures become skills. A skill is lazily loaded: frontmatter and a short description stay in the catalog so Claude knows the skill exists; the body loads only when you type /name or Claude decides it is relevant. Path-specific conventions become rules in .claude/rules/ with paths frontmatter, so they load when Claude reads a matching file, not at launch.
A skill encodes expertise. An agent is what acts with it. SKILL.md is the playbook. Claude Code (or a later SDK loop) is the worker that reads the playbook, calls tools, and updates the window. Mixing those two jobs is how a CLAUDE.md turns into a handbook: you stuffed an agent’s procedure into always-on memory.
That is progressive disclosure: the model always knows that a capability exists. It pays for the full text only when the task needs it.
Imports vs Rules vs Skills
CLAUDE.md scales by staying modular: a small root file, @ imports for shared always-on snippets, and path-scoped rules for the file surface in play. @ imports help humans navigate. They do not help the context window. Imported files still expand at launch. Use them to organize, not to hide cost.
| Mechanism | When it loads | Use it for | Do not use it for |
|---|---|---|---|
CLAUDE.md |
Every session, in full | Facts, gotchas, always / never, pointers to skills | Tutorials, dump of the file tree, long playbooks |
@ imports |
At launch, with the file that references them | Shared snippets you want in every session, or an AGENTS.md bridge |
Large reference docs you only need sometimes |
Rules without paths |
At launch, same priority as project CLAUDE.md |
Topic files that still apply everywhere | Anything you were trying to lazy-load |
| Path-scoped rules | When Claude reads a matching file | Conventions tied to a glob: apps/web/**, **/*.test.ts |
Procedures you invoke by name |
| Skills | Description at launch; body on invoke | Repeatable workflows, checklists, domain playbooks | One-line facts that belong in every session |
A useful test: if I would paste the text into chat only for some tasks, it is a skill. If it is true only when a certain path is open, it is a path-scoped rule. If I would re-explain it in every session, it stays in CLAUDE.md.
Skills also have a second context trick. Set context: fork when the workflow is noisy: a deploy check, a wide review, a script that prints a lot. The skill runs in a subagent. The main session keeps the summary, not the raw trace. Custom commands in .claude/commands/ still work and still become /name. New work should be a skill so you can attach supporting files and control who may invoke it.
Long Conversations Need a Strategy
Course 1 trains this on a long-running support copilot. I am not reprinting that exercise. The pattern I kept is a harness rule, not a product-specific trick.
A long session dies in a predictable way. Tool output is verbose. Early turns are resolved but still occupy tokens. The few facts that must stay true (order id, last error, what the user already confirmed) get buried in the middle of the window. The model then re-asks questions or “fixes” the wrong object.
The working model:
flowchart LR
Raw[Verbose tool output] --> Trim[Trim at the tool]
Trim --> Facts[Persistent facts block]
Chat[Resolved turns] --> Compress[Tiered compress]
Facts --> Place[Place facts at head or tail]
Compress --> Place
Place --> Next[Next perceive / reason / act]
classDef orangeClass fill:#F39C12,stroke:#333,stroke-width:2px,color:#fff
classDef blueClass fill:#4A90E2,stroke:#333,stroke-width:2px,color:#fff
classDef greenClass fill:#27AE60,stroke:#333,stroke-width:2px,color:#fff
classDef purpleClass fill:#9B59B6,stroke:#333,stroke-width:2px,color:#fff
classDef tealClass fill:#16A085,stroke:#333,stroke-width:2px,color:#fff
class Raw,Chat orangeClass
class Trim,Compress blueClass
class Facts greenClass
class Place purpleClass
class Next tealClass
- Trim at the tool boundary first. A deterministic cap (
--quiet, a structured result, a line limit) is cheaper than asking the model to ignore a dump it already ingested. Claude Code already caps bash output and, past that, returns a file path plus a short preview. That is a safety net, not a strategy. If I own the tool, I shrink the result before it enters the window. - Keep a persistent facts block. Case id, environment, constraints, and confirmed decisions must not be compressed away. Write it as short bullets, not a transcript.
- Compress in tiers, under a stated budget.
/compactis Claude Code’s version of this. When I assemble a window myself later, I will set the budget in tokens, not in “looks short enough.” - Place critical facts, do not only include them. Models read the head and tail of a long window more reliably than the middle. A facts block buried between tool traces is as good as missing. After
/compact, project-rootCLAUDE.mdis re-read and re-injected. NestedCLAUDE.mdfiles and path-scoped rules are not. Skill descriptions are not re-injected either; only skills you already invoked are carried forward, and only within a token budget. Put durable facts in a slot that is re-attached at the edge of the working set: rootCLAUDE.md, or a facts block your harness writes back to the head or tail.
The squeeze rule I will reuse:
| Layer | Squeeze | Keep |
|---|---|---|
| Resolved turns | Hard | Outcome and the evidence that proved it |
| Active turns | Light | Enough to continue the current step |
| Facts block | Never | Case id, constraints, confirmed decisions |
The context-engineering post also dropped an older habit: repeat the same rule at the end of the window so the model cannot miss it. Newer models need less of that repetition. They still need the one copy to sit where it survives compact and where it is not lost in the middle of a log.
When I build my own loop with the Agent SDK (the next post), I will have to implement trim / facts / tiered compress / placement myself. In Claude Code, I get a head start: output caps, /compact, auto memory, forked skills, and Explore.
Team Config for a Multi-Surface Repo
The other Course 1 lesson I keep coming back to is a version-controlled Claude Code setup for a team that shares one monorepo. Again, my notes, not their files.
A single root CLAUDE.md that lists every package’s quirks will fight itself. Frontend “use the design tokens” and API “do not invent status codes” are both true, and both are noise when you are in the other tree.
The setup I want in a multi-surface repo:
| Piece | Role |
|---|---|
Root CLAUDE.md |
Repo purpose, shared commands, always / never, links to skills |
| Path-scoped rules | One file per surface: apps/web/**, apps/api/**, **/*.test.ts |
| Skills | Review, deploy-check, release notes: procedures with a /name |
| Forked skills | Checks whose logs should not land in the main session |
| Slash entry points | /review as a read-only skill; /deploy-check only when a human invokes it |
disable-model-invocation: true is the right default for anything with side effects. Claude should not decide that the branch looks ready and run deploy. You type /deploy-check when you mean it.
The real config work is a decision framework, not a pile of files. Official docs already separate the modes:
| Mode | What Claude can do | When I use it |
|---|---|---|
| Plan mode | Read and propose. No source edits until I approve. | Scope is unclear, several files will change, or I am new to the area |
| Direct execution | Edit and run in the main session | I can describe the diff in one sentence |
| Explore | Search in a separate window; only the findings return | I need a map, not a patch. Explore also skips CLAUDE.md to stay cheap |
Best-practice wording I agree with: explore, then plan, then code. Skip the ceremony for a typo. Pay for it when the wrong edit is expensive.
One monorepo detail that surprised me: Explore and Plan skip your CLAUDE.md on purpose. The parent session still has those instructions when it reads the summary. If a rule must reach the researcher (“ignore vendor/“), put it in the delegation prompt.
Tool Descriptions Are Context Too
Part 1 said a vague tool contract wastes the loop. I now treat the description as part of the prompt, because it is.
The new context-engineering rules dropped a habit I used to follow: pile examples into the tool text so the model cannot miss the shape. Newer models explore less when you over-specify the happy path. They do better when the interface is expressive: clear name, when to call it, parameters that encode the real choices, and a result the next turn can use.
A working checklist for any tool I add later (CLI, MCP, or SDK):
1 | Name: what this tool is for, in the verb the model should think |
“Search the system” is a weak contract. “Find an invoice by id or by customer email; return id, status, and outstanding cents; return not_found instead of guessing” is a contract the loop can verify. The return shape is the first trim: a small structured result beats a raw dump the model is then asked to filter. If two tools overlap, say so in both descriptions. Silent overlap is how the agent calls the wrong one and then reasons over junk.
I will go deeper on MCP tool design in a later post. The takeaway for this one: if the description is vague, the perception step is already compromised.
How I Will Set This Up
A checklist I can reuse on the next repo:
- Run
/init, then delete anything Claude can see by reading the tree. - Keep root
CLAUDE.mdunder 200 lines: purpose, commands, gotchas, always / never. Scale with@imports and path-scoped rules, not a longer root file. - Treat skills as expertise, not as extra agents. Point at them instead of inlining playbooks.
@import only what must load every session. - Add
.claude/rules/withpathsfor each surface. Leave unscoped rules for true globals. - Put review and deploy-check in skills. Fork the noisy ones. Set
disable-model-invocation: trueon side effects. - Write down the mode rule: plan for unfamiliar or cross-cutting work, Explore for research, direct for small diffs.
- For any tool I add, return a small structured result. Do not dump a log and ask the model to filter it.
- Run
/contextand/doctor. If a line would not change a decision, cut it. - After a long session, ask: which facts should have sat in a persistent block at the head or tail, and which tool dumps should have been trimmed before they entered the window?
Before / After: a Small CLAUDE.md
Invented example, not a course file. Imagine invoice-desk: a TypeScript API, a React dashboard, and shared types.
Before (loads every session, teaches almost nothing Claude cannot discover):
1 | # invoice-desk |
After (facts and gotchas; procedures and surfaces live elsewhere):
1 | # invoice-desk |
The path-scoped API rule can be a short file:
1 | --- |
The /review skill body stays out of the default window until someone invokes it. That is the whole point.
Putting the Concepts into Practice
Pick one repo you already use with Claude Code and write this brief before you add more files:
1 | Repo purpose (2 lines): |
Then inspect a real session:
- Run
/context. What loaded that you did not need? - Which
CLAUDE.mdlines are a procedure in disguise? - Did a path-scoped rule fire when you opened the matching files?
- Did a forked skill or Explore keep a noisy trace out of the main window?
- After
/compact, which instruction disappeared, and should it live in rootCLAUDE.mdor a facts block at the edge of the window?
If you cannot answer those, the config is still a document, not a working set.
Key Terms
- Context engineering: Assembling the instructions, evidence, and tool contracts the model needs for the next decision, while keeping the rest out of the window
- Context budget: Every token stays in the working set and is paid for on later turns; treat the window as a scarce allocation, not a dump
- Progressive disclosure: Keep a small index in context; load the full text only when the task needs it
CLAUDE.md: Project, user, or managed instructions loaded at session start; guidance, not enforcement@import: A file expanded intoCLAUDE.mdat launch; useful for structure, not for lazy-loading- Path-scoped rule: A
.claude/rules/file withpathsfrontmatter that loads when Claude reads a matching file - Skill: Encoded expertise whose description is listed up front and whose body loads on invoke; not an agent
- Agent: The loop that perceives, reasons, and acts, using skills and tools
- Forked skill: A skill with
context: forkthat runs in a subagent so verbose output stays out of the main session - Auto memory: Notes Claude writes for itself; the
MEMORY.mdindex loads every session, topic files load on demand - Facts block: A short, durable set of case or project facts you re-attach so they survive prune and compact
- Tiered compression: Squeeze resolved turns hardest, keep active turns fuller, never drop the facts block, all under a stated token budget
- Plan mode: Read-and-propose workflow with edits blocked until you approve
- Explore: Built-in research subagent that returns findings without filling the parent window
Final Thoughts
The loop from Part 1 does not get cheaper when I paste more instructions. It gets cheaper when each instruction has a load rule and each tool result has a size. Root CLAUDE.md holds facts. Rules hold surface law. Skills hold expertise the agent can load. Tool descriptions hold contracts. Long sessions keep a facts block at the edge of the window and throw the rest away.
That is the setup I will carry into the rest of this series. The next post moves from Claude Code configuration to the harness: the Agent SDK, the stop-reason loop, and the context strategy you have to implement when you own the runtime.
This is Part 2 of 6 in the Agentic Coding with Claude Code series.
Comments