name + descriptionA mental model for AI coding agents, notes from the AIHomeLab
Here's how I think about AI coding agents now, after spending weeks building my AIHomeLab in one. The category includes Claude Code, Cursor, Cline, Codex, Antigravity, and whatever you build with one of the agent SDKs. They all wrap a frontier model in a stateful loop with tools, memory, and procedures. My examples here are from Claude Code because that's the one I learned in, but the mental model travels. If you use any of these systems, the same five insights apply.
I wrote these notes for myself, to remember what stuck rather than transcribe everything I did.
If you have 2 minutes, here's what I learned compressed into five things.
I've worked hands-on with Claude Code building my AIHomeLab. The same architecture shows up across the whole AI coding agent category (Cursor, Cline, Codex, Antigravity, custom SDK builds): a frontier model wrapped by a harness. The model is the brain, stateless and forgetful between calls. The harness is the body, the program that gives the model a loop with tools, memory, and governance. Almost everything I could shape, and almost every frustration I hit, lived in the harness configuration layer, not in the model.
The five things I had to internalize:
What stuck most: the harness is a configurable system, whichever agent you're using. Most frustrations resolve once you know which knob to turn for which problem.
Document map (jump to whichever pulls you in):
People say "Claude" to mean four different things: the model, the API, the harness, and the SDK. I conflated them at first too. The lesson that stuck: when something goes wrong, you can't fix it until you know which layer is broken.
Bottom-up: model (stateless function) → API (HTTP contract) → harness (the agent loop). Above the harness sit two user-facing layers: your configuration (CLAUDE.md, skills, hooks, memory) and you (prompts, slash commands). The SDK is a related concept, the kit for building harnesses, shown as a callout, not a layer in the running stack.
Claude is the model. A neural network, a function from tokens to a next-token distribution. Stateless. Each call is independent; the model has no memory of yesterday's chat. "Sonnet 4.6," "Opus 4.7," "Haiku 4.5" are snapshots of model weights, different brains in the same family.
The Anthropic API is the contract. An HTTP endpoint in front of the model. You POST messages and tool definitions; you get back text and tool-call requests. The API does not execute tools. It does not remember the conversation. If you want an agent, you build the loop.
The harness is the agent loop. A program that wraps the API and gives the model a body: Claude Code, Cursor, Aider, Antigravity, or anything custom-built. It maintains history, assembles the system prompt, executes tool calls (when the model says "run ls," the harness actually runs it), manages context, enforces permissions, spawns subagents. The model is the brain; the harness is everything else.
The SDK is for building harnesses. Same primitives Anthropic uses, packaged as a library. If Claude Code is "the harness Anthropic ships," the SDK is "build your own."
The practical rule when something misbehaves: was it the model or the harness? "Claude didn't run my skill" is almost always a harness problem: discovery, permissions, or configuration. "Claude wrote a bug" is almost always a model problem. Different layers, different fixes.
| ClaudeAnthropic | GPTOpenAI | GeminiGoogle | Open-sourceLlama · Qwen · GPT-OSS | |
|---|---|---|---|---|
| Claude CodeAnthropic | ||||
| CodexOpenAI | ||||
| AntigravityGoogle | ||||
| Cursor | ||||
| Cline · Aider · Continue.dev | ||||
| CustomSDK-built |
Harness and model are independent dimensions. An AI coding tool is a (harness, model) pair.
The conflation worth killing: "Claude Code = Claude." It doesn't. The harness and the model are independent choices.
Anthropic ships Claude Code, OpenAI ships Codex, Google ships Antigravity, each defaults to its own model. Third-party harnesses are mostly model-agnostic: Cursor, Cline, Aider, Continue.dev let you point at Claude, GPT, Gemini, or open-source models. Build your own harness with the Agent SDK and pick whatever model you want.
So when you compare "Claude Code vs. Cursor," you're comparing two harnesses, not two models. The same Claude weights feel different across them because the harness shapes the experience: what system prompt the model sees, what tools it has, how context is managed, what gets cached, how permissions work. Model is raw capability. Harness is workflow.
CLAUDE.mdappended to base promptInside one harness. Vendor builds the sockets: loop, built-in tools, base prompt, permissions engine, extension points. You author what plugs in.
A single harness instance has two surfaces, and the boundary between them is where productivity is won or lost.
The vendor's surface is fixed. Anthropic decides what the agent loop does, which tools are built in (Read, Edit, Bash, Glob, Grep, WebFetch), how context is managed, how permissions work, what the base system prompt says. You don't change any of this, but the vendor also wires extension points: a skills runtime, a hooks runtime, an MCP client, a subagent spawner, a memory loader. The vendor builds the sockets.
Your surface is what plugs into them. CLAUDE.md gets appended to the base prompt. Skills get loaded on demand. Hooks fire on events. MCP servers add new tools. Memory persists facts across sessions. Custom subagents are scoped child Claudes you've defined. The permissions allowlist tells the engine what's pre-approved.
This is harness engineering. Your .claude/ directory and your settings files are the visible shape of it. When you ask yourself "should I write a skill or a hook or a slash command for this?", you're asking which socket to plug into.
A useful test: if you can change something by editing a file in your repo or your ~/.claude/ directory, you're in the user surface. If changing it would require Anthropic to ship a new release, you're hitting the vendor surface. Most frustrations live at that boundary, and most of them are solvable on the user side once you know which socket to use.
Three things to get straight before "context fills up too fast" can have concrete handles. First, what counts as a unit. Second, what's actually in the bucket. Third, what makes re-using the bucket cheap.
The model doesn't see characters and it doesn't see words. It sees tokens: chunks of text decided by the tokenizer that ships with the model.
The numbers matter because everything is metered in tokens: context window, prompt cache, API bill. When someone says "Sonnet has a 200K context window," they mean 200,000 tokens.
Every model has a maximum input size: the context window. Hard ceiling.
The window holds everything the model sees on a single API call: base system prompt + CLAUDE.md + loaded skills + memory + conversation history + tool results + this turn's message. When the sum approaches the limit, autocompact kicks in: the harness summarizes earlier turns to free space. You stay below the ceiling but you lose fidelity.
So "context fills up too fast" really means: the things you've put in the window are accumulating faster than you've stopped putting them in.
I learned the hard way: context doesn't fill because a conversation is long. It fills because some kinds of content are expensive.
Typical Claude Code turn. Tool results usually dominate, system overhead is fixed; Reads, Bash output, and WebFetch grow with what you ask for.
In a typical turn:
One careless cat huge-log.txt, one verbose npm install, one fetched page, half your window is gone.
Every API call, the harness concatenates many sources into a single system message.
CLAUDE.mdwalked up the directory treeEvery API call, the harness glues these sources together into one system message. CLAUDE.md changes apply on the next turn, not the next session.
Two consequences worth banking:
Without optimization, every turn would reprocess the entire system prompt + history from scratch. The prompt cache fixes this:
The cache works on prefixes. Each turn extends the prefix; only the new tail is full-price. Appending at the end keeps the cache warm; changing anything early invalidates everything after.
Two practical implications:
After running out of context too many times, these are the moves I learned:
/clear between phases. Memory and CLAUDE.md still load, so durable context survives.The model doesn't do anything. It only recommends actions. Tool calls are how those recommendations become real work.
A tool definition is a JSON schema with three parts: a name, a description, and an input schema:
{
"name": "Read",
"description": "Reads a file from the filesystem and returns its contents with line numbers, up to 2000 lines...",
"input_schema": {
"type": "object",
"properties": {
"file_path": { "type": "string", "description": "The absolute path..." }
},
"required": ["file_path"]
}
}
The harness sends a list of these to the API on every turn alongside the messages. The model sees only this: name, description, and parameter schema. It does not see the implementation. It doesn't know Read opens a file handle and slurps content. All it knows is what the description says it does.
The load-bearing insight: a tool's description is a prompt to the model, not documentation for humans. Vague description → the model can't tell when to reach for it. Specific, action-verb-led description → clear matching.
The model recommends; the harness executes. The model never touches your filesystem; it just emits structured intent the harness translates into action.
When you send a message, the model sees: system prompt + conversation history + tool definitions + your new message. Three things shape the choice:
Bash 10 times for similar tasks, the model is more likely to use Bash again. Patterns reinforce themselves."Picks the wrong tool" is almost always one of:
The fix in all three cases: rewrite the description.
Modern Claude can recommend multiple tool calls in a single response. The model decides which calls are independent and emits them as a batch. The harness executes concurrently and returns all results before the next inference step.
Serial costs 4 model calls and sequential tool execution. Parallel costs 2 model calls and concurrent execution. The round-trip and wall-clock win compounds with more tools.
The model has to recognize independence. "Read A, then based on it, read B" can't parallelize; there's a dependency. "Read A, B, and C" can. You can nudge by phrasing requests as "in parallel" or "independently"; the model picks up the cue.
A tool call can fail: file not found, command exits non-zero, network error. The harness packages the failure into the tool_result with an error indicator. The model gets that error on the next turn and decides what to do: retry, switch tools, or ask you.
The error message you write into a tool's failure path matters. A tool result of "error: failed" tells the model almost nothing. A tool result of "error: file not found at /path/X. Check with ls" gives the model concrete next steps.
This is why MCP servers and custom tools you build need good error surfaces. The harness can't help if the tool itself returns junk in its error message.
The connection across the agent stack: the model only knows what your descriptions tell it. This applies to:
Read, Edit, Bash, Glob, Grep, WebFetch.)The "I built a thing and the model never uses it" problem is almost always a description problem.
A subagent is the same Claude model running in a fresh sandbox (its own context window, its own system prompt, its own toolbelt), spawned by the parent to handle a delegated piece of work. The parent pays only for the summary the subagent returns, not the work the subagent did.
This is one of the highest-leverage productivity tools in Claude Code, and it's underused because the model rarely volunteers. You usually have to know when to ask.
Mechanically, a subagent is:
Explore has only read tools)Task tool callThe parent doesn't see the subagent's intermediate turns. It only sees the final summary. From the parent's perspective, a subagent call is just another tool call, one that happens to take longer and return prose instead of file contents.
When the parent decides to delegate:
Task tool_use block: { subagent_type, description, prompt }Task, starts the child Claude with the named subagent typeThe parent's context stays clean; each child's context absorbs its own work and evaporates after the call returns. Only the summary survives.
The moment subagents clicked for me: a child's context evaporates after the call returns. That asymmetry is where the leverage lives.
Without subagent: 50 file contents live in the parent's conversation forever. With subagent: only the summary survives; the child's context evaporates.
A worked example:
Explore: "Find all references to X." Explore reads the 50 files in its context, returns: "X is referenced in src/auth.ts:42, src/api.ts:118, and src/utils/jwt.ts:7. The auth.ts version is canonical." Parent's context grows by ~30 tokens.The savings compound when:
WebFetchesThis is also why "I ran out of context" is often solvable structurally, by delegating the biggest reads to subagents instead of pulling them into the main thread.
Two flavors that matter:
Explore): restricted to read tools. Safe to spawn liberally for research. Can't modify state. Pattern: "Go look at a bunch of stuff and tell me what you found."Most context-saving leverage comes from read-only subagents. Most autonomy leverage comes from general-purpose subagents.
Like skills, you can define your own. A subagent definition lives in .claude/agents/<name>.md and includes:
The parent sees the subagent's description and decides when to delegate. As with tools, the description is the discovery surface. Vague description means the parent never invokes it.
Subagents have overhead: spawning a child is a real round trip. They're the wrong tool for:
The leverage rule: delegate when the work is read-heavy and the answer is short. That's the sweet spot.
A skill is procedural knowledge the model can load into its context on demand. Unlike a tool (a callable function) or a subagent (a separate Claude process), a skill is a text artifact that gets injected into the parent's system prompt when its description matches. The model then reads the skill body and follows the instructions inline.
This is where the "picks the wrong skill" frustration lives, and almost always, the fix is the same as for tools and subagents: the description.
Mechanically, a skill is a markdown file at .claude/skills/<skill-name>/SKILL.md with YAML frontmatter:
---
name: my-skill
description: Use when [trigger phrases]. Does [what it does].
---
[Body content with instructions, examples, reference material]
That's the whole thing. Two fields in the frontmatter (name, description), plus a body. The body can be a paragraph or a full procedure with sub-headers, code snippets, references.
A skill can also bundle supplementary files alongside SKILL.md: scripts, templates, examples. These don't auto-load; they're referenced from the body and read on demand.
.claude/skills/<skill-name>/
SKILL.mdname + descriptionscripts.shreference/Skill anatomy. Frontmatter is always loaded (cheap); body loads on demand when the description matches. Bundled files are referenced from the body and read only when the procedure calls for them.
This is the most important thing to internalize about skills, and the source of most "why didn't my skill trigger?" confusion.
Skills use two-phase loading:
Phase 1 (always): For every installed skill, only the name + description are loaded into the system prompt. The body is NOT loaded. The harness puts a compact list, descriptions are typically 100–200 tokens each. Even 20 skills cost ~3K tokens.
Phase 2 (on demand): When the model decides a skill is relevant (based on the request and the descriptions it sees), it calls a tool to load that skill's full content into context. Only then does the body become available.
diagramsuse when creating, editing, reviewing diagrams…publication-safetyuse when preparing repo for public sharing…verifyverify changes by running the app…diagrams description → triggers body loaddiagrams body now loadedProgressive disclosure. Phase 1 is cheap: just descriptions. Phase 2 loads the full body only when the description matches what the user asked for. This is why descriptions matter more than skill content for triggering.
This is why descriptions matter so much. The model is doing pattern matching between what the user asked and what each skill's description says. If the description doesn't surface naturally from the user's phrasing, the skill won't load, even if its body would handle the request perfectly.
Almost always one of these:
Diagnostic: read the description like the model would. Does it specifically tell the model when to use it? Does it use the language users would use?
Good description structure:
description: Use when: [trigger scenarios]. The skill [what it actually does].
[Optional: distinguishing factors vs related skills]
A well-engineered example (the diagrams skill):
description: Use when: creating, editing, or reviewing technical diagrams;
the user mentions diagram, mermaid, visualize, topology, flow chart,
pipeline picture, or asks to draw a system, a data path, or a cold/warm path.
What makes this work:
diagram, mermaid, visualize, topology…)Skills can come with companion files in the same directory:
.claude/skills/aihomelab-lustre-cluster/
├── SKILL.md
├── lustre-bringup.sh
└── reference/
└── lustre-modules-checklist.md
The body of SKILL.md references these. The model reads SKILL.md and follows the procedure, reaching for the companion files only when the procedure calls for them.
This keeps the loaded body lean: only the procedure loads, not the supporting material.
Three scopes, each with implications for when a skill is available:
<project-root>/.claude/skills/. Load only when working in that project. Good for project-specific procedures.~/.claude/skills/. Load in every project on your machine. Good for cross-cutting procedures (diagrams, publication-safety).verify, simplify, init, etc.).Scope matters because triggering happens against currently loaded descriptions. A user skill is invisible when working from a machine that doesn't have it installed.
MCP (Model Context Protocol) is the standardized way to add new tools to any harness. MCP servers expose tools that the harness routes the model's calls to. This section goes a layer deeper: the protocol, the deferred-tools optimization, and when you'd actually build one.
MCP is an open protocol that defines:
For the harness, an MCP tool is indistinguishable from a built-in tool: same tool_use structure, same tool_result flow. The harness just routes the call to the right MCP server instead of executing locally.
For your code (the MCP server side), you implement a small program that:
That's the whole contract. It works the same way across Claude Code, Cursor, Cline, custom SDK harnesses, anything that implements an MCP client.
MCP architecture. From the model's perspective, built-in tools and MCP tools look identical: same call shape. The harness routes calls to the right server using stdio for local servers and HTTP for remote ones.
Before MCP, every harness had its own way of adding extension tools. Cursor had Cursor's way; Claude Code had Anthropic's way; custom SDK code had to roll its own. If you wrote a "talks to GitHub" integration for one harness, you'd have to rewrite it for another.
MCP standardizes the contract. Now:
This is the agent-tool equivalent of LSP (Language Server Protocol) for IDEs: same standardization story.
Two ways the harness connects to an MCP server:
You configure each MCP server in your settings: a command + args for stdio, a URL for HTTP.
Some MCP servers expose many tools. A computer-use server has 26 tools. If every MCP tool's full schema were loaded into every conversation, the system prompt would balloon.
The solution: deferred tools. The harness only loads the names of MCP tools into the always-available list, not the full schemas. When the model wants to use one, it first calls a ToolSearch to fetch the actual schema, then calls the tool.
mcp__computer-use__screenshotmcp__computer-use__clickmcp__computer-use__typeclick, calls ToolSearch("click")
Deferred tools: same progressive-disclosure pattern as skills. Names are cheap to keep around (~5 tokens each); full schemas load only on demand via ToolSearch.
This is why having a big MCP server like computer-use connected doesn't blow up your context budget.
The question to ask: does Claude need access to a capability the built-in tools can't provide?
Good MCP server candidates:
Not-MCP-server candidates (often there's an easier way):
Bash already does thisRead already does thisWebFetch for one page, or an existing search MCP serverThere's a growing ecosystem of community MCP servers: GitHub, Slack, Postgres, filesystem, browser automation, search, and many more. Most are open source. You install them like any other dependency and configure your harness to connect.
MCP is the extension mechanism for adding capabilities Claude doesn't otherwise have. It's a protocol, not a feature.
We'll formalize the full decision tree in the next section.
You now have four ways to shape what Claude can do:
They look similar from a distance. They're not. Each occupies a specific role, and choosing the wrong one is the source of most "I built this and it doesn't work" frustration.
| Tool | Skill | Subagent | MCP server | |
|---|---|---|---|---|
| What it is | Function the model calls | Text injected into parent's prompt | Fresh Claude with isolated context | Protocol for exposing tools |
| Where it lives | Harness (built-in) or via MCP | .claude/skills/ |
.claude/agents/ |
Separate process or service |
| What it returns | Data (tool_result) |
Nothing (influences parent inline) | A summary string | Data (tool_result) |
| Cost idle | Definition in tool list | ~150 tok per description | ~150 tok per description | ~5 tok per name (deferred) |
| Cost active | Same as idle | +500–3000 tok (body) | Just the summary | +~500 tok (schema) |
| Best for | Atomic capabilities | Procedures with user decisions | Read-heavy isolated work | Adding new capabilities |
Start at the top and answer each question:
Walk top-down. First question that returns YES wins. Most "I built the wrong primitive" mistakes happen when Q1 and Q2 get mixed up.
Skill where you needed a subagent. You wrote a skill: "Read every file under src/ and find references to function X." The skill loads, the parent runs Read 50 times in the main thread, context bloats. → Should have been a subagent with a small return.
Subagent where you needed a skill. You wrote a subagent that walks the user through a deployment checklist: "Did you check X? What's the staging URL? Should we tag the release?" Subagents can't easily interact with the user mid-flight. → This is a skill.
MCP server where you needed a skill. You built an MCP server with a single tool called publication-checklist that returns a multi-step procedure. → A procedure isn't a capability; it's a skill. MCP is for external capabilities, not for stored checklists.
Tool where you needed an MCP server. You're embedding "talk to our company's Jira" as ad-hoc Bash + curl in many sessions. Every session re-invents the wheel. → The structured, schema-typed version belongs in an MCP server.
Real workflows often use all four primitives composed together. Here's a hypothetical "publish a release" workflow:
release-checklist triggers; parent now has the procedure + user interactionEdit bumps version in package.jsonchangelog-auditor reads 200 commits, returns 5-line gap reportslack__post_message announces the releasedeploy__mark_released updates the dashboardComposition pattern. Each primitive plays its specific role. Skill orchestrates; subagent does isolated research; MCP provides external capabilities; built-ins handle local actions.
Each primitive plays its role:
They don't compete; they complement. Skills tell the parent when to use subagents and tools. Subagents do isolated work. MCP adds new capabilities. Tools are the actual function calls.
For each scenario, the right primitive:
| Scenario | Primitive |
|---|---|
| "When the user mentions security review, walk them through these 8 checks" | Skill |
| "Search all 500 files for instances of deprecated API X" | Subagent (read-heavy, small answer) |
| "Let Claude post to our team's status board" | MCP server (new capability) |
| "Read the contents of this file" | Tool (built-in Read) |
| "Audit 30 published artifacts for stale links, report which need updates" | Subagent |
| "Provide a hard-gated procedure for promoting drafts to public" | Skill |
| "Connect Claude to our Postgres for schema queries" | MCP server |
Across all four primitives, the same pattern shows up:
Descriptions are the universal discovery surface. When something you built doesn't get used, the first investigation is always the description, across all four primitives.
These are the governance layer: the four knobs that let you shape how Claude Code behaves across sessions, what it can do without asking, when it intervenes deterministically, and how it stays in "design" mode before acting. None of these add capabilities the way MCP or skills do. They shape what's allowed, what's remembered, and what triggers automatically.
This section is shorter than the primitives sections; these are conceptually simpler, but they're the difference between a harness that just works and one that's calibrated to you.
(Other tools have analogous concepts under different names: Cursor's rules system, Cline's custom instructions, similar patterns in agent SDKs. The soft/hard distinction below is what travels.)
The four governance knobs operate at different layers of the session, memory in system-prompt assembly, hooks at event boundaries, permissions at tool boundaries, plan mode at the session level.
Skills and CLAUDE.md cover within-session and within-project context. Memory covers cross-session, longer-lived facts the model should know on every interaction.
Two mechanisms in Claude Code:
CLAUDE.md files, project-level instructions loaded automatically when working in that directory. Walked up the directory tree (so src/CLAUDE.md + project root CLAUDE.md both apply when editing under src/). Best for project conventions, architecture notes, "always do X here" rules.~/.claude/projects/<project-slug>/memory/. Each entry is its own markdown file with name, description, content; MEMORY.md indexes them.Memory files get concatenated into the system prompt on every turn (same assembly mechanic from earlier). They cost tokens but don't fight for conversation budget.
When to add a memory entry vs CLAUDE.md vs skill:
CLAUDE.mdA hook is a shell command the harness runs at specific event moments. Not a model decision; the harness fires hooks deterministically when configured events happen.
The hook events I reach for most (the Claude Code docs have the full list):
PreToolUse: fires before a tool runs (can block the call)PostToolUse: fires after a tool returns (can inspect/modify the result)UserPromptSubmit: fires when you send a message (can inject context, redirect)Stop: fires when Claude finishes a turn (good for notifications)SessionStart: fires when a session begins (good for setup)Others worth knowing exist for subagent stops, pre/post-compaction, session end, and desktop notifications.
Hook event timeline. Each amber band is a deterministic firing point where the harness can run a shell command: block a tool, inject context, format output, notify, etc.
Hooks are configured in your settings as { event, matcher, command }. The harness runs the command, reads the exit code and stdout, and acts accordingly.
What hooks are good for:
~/secrets/ regardless of what the model decidesprettier after every Editgitleaks"Hook vs CLAUDE.md instruction: an instruction in CLAUDE.md is a suggestion the model may or may not follow. A hook is enforcement: the harness runs it regardless of model intent. Use hooks for safety/security; use CLAUDE.md for conventions you can live without 100% compliance on.
Every tool call the model wants to make goes through the permissions engine. Three states per tool/pattern:
You configure these per-tool, per-pattern. Examples:
Read → allow (reading files is safe)Bash(npm:*) → allow (npm commands pre-approved)Bash(rm -rf:*) → deny (never)Edit(~/secrets/*) → deny (never touch secrets)Bash(*) → ask (any other shell command requires approval)Permission patterns use the same matching mechanic as hooks. Specific patterns take precedence over general ones.
The permission engine is the load-bearing safety layer. Skills, hooks, and the model itself all sit behind permissions. Even if a skill says "now run rm -rf /," the permission engine intercepts before the tool runs.
The opposite extremes ("allowlist everything" vs "ask for every command") both have failure modes. Allowing everything trades safety for speed; asking everything makes work tedious. Most productive users converge on a middle ground: allow common safe operations, ask for everything that touches shared state or is hard to reverse.
Plan mode is a harness-level state where:
It's a "dry run" mode that scales to complex tasks. Useful when:
Enter via the /plan command or certain workflows. Exit with ExitPlanMode (the model calls this when the plan is ready). Your approval starts execution.
These four knobs operate at different layers of the session:
| Knob | Layer | Acts on |
|---|---|---|
| Memory | System prompt | What Claude knows across sessions |
| Hooks | Event boundary | What the harness does deterministically |
| Permissions | Tool boundary | What gets approved without prompting |
| Plan mode | Session mode | What the model can do (read-only design vs. full execution) |
They compose: memory shapes Claude's defaults; hooks enforce deterministic behavior; permissions gate every action; plan mode constrains the whole session. Together they let you dial in how Claude Code behaves to match your taste and your safety bar.
The final synthesis. Nothing new mechanically: just how to combine the primitives and governance knobs into a usable daily playbook.
The single biggest productivity gain comes from treating context as a budget, not an unlimited resource:
cat huge-log.txt or one verbose npm install can eat 50K tokens.Explore subagent. Subagent reads 50 files in its context, returns a 30-token summary, your main thread saves ~99% of the cost./clear between phases. Memory and CLAUDE.md still load; durable context survives./context when something feels heavy. It tells you exactly what's eating budget.When you build something (a skill, a custom subagent, an MCP tool), the description determines whether it's ever used. Workflow:
Use when:, the model treats this as a strong signalWhen a primitive doesn't fire as expected: edit the description first. Rewrite the body only after the description fix fails.
If you're tempted to build a custom subagent that needs user input mid-flight, you wanted a skill. If you're tempted to write a skill that reads 50 files autonomously, you wanted a subagent.
For each requirement, ask: "if the model occasionally ignored this, would it matter?"
| Stakes | Mechanism |
|---|---|
| Safety, security, reversibility-critical | Hard: permissions deny, hook block |
| Conventions, style, defaults | Soft: CLAUDE.md, skill |
| Cross-session personal preferences | Memory |
| One-time per-session constraint | Plan mode |
Default failure mode: putting safety-critical rules in CLAUDE.md (soft). They'll be violated. Move them to hard mechanisms.
/clear between unrelated phases. Each phase starts fresh; memory + CLAUDE.md persist.Knowing which layer to investigate saves more time than any single fix.
/context → find heavy tool result → subagent, /clear, or memorysettings.json hook matchersettings.jsonDiagnostic reference. Identify the symptom → know which layer to investigate → take the first action. The model vs. harness diagnostic, applied to common situations.
Small effort now, big payoff later. These compound:
Compounding investments. Each is small effort now; each saves cognitive load on every future session. Six months in, the difference between "I built nothing" and "I tuned these" is orders of magnitude in iteration speed.
Across everything covered:
The harness is a configurable system, whichever one you're using. Most "I'm fighting this tool" frustrations resolve once you know which knob to turn for which problem. The specifics differ across Claude Code, Cursor, Cline, Codex, Antigravity, and custom-built agents, but the mental model travels. Now you have it.