A Scoped 'Test-Writer' Agent: Custom Subagents in Claude Code
▶ Watch on YouTube & subscribe to The Stack Underflow
You need Claude to write tests for a pull request. Just tests — not refactors, not a dependency upgrade, not a “while I’m in here” rewrite of the module it’s testing. You could type a longer, sterner prompt every time and hope it holds. Or you could build a specialist that structurally cannot do anything else, because it was never handed the tools to do anything else. That second option is a subagent, and it’s the mechanism behind Domain 3, Question 10 of the Claude Certified Architect practice set.
The one-sentence version: A subagent is a named, file-defined specialist with its own system prompt and its own tool allowlist — you scope it once in a config file, and every future delegation to it inherits that scope automatically.
The question
You want a specialized “test-writer” agent restricted to a few tools. How do you set it up?
- A) Ask nicely in the prompt
- B) A subagent definition in
.claude/agents/*.mdwith tools + model in frontmatter - C) A new CLAUDE.md
- D) A slash command
Pause here and pick an answer before you scroll further.
The answer
The correct option is B — a subagent definition in .claude/agents/*.md, with the tool allowlist and model choice set in the file’s YAML frontmatter.
The mechanism is a delegation, not a suggestion. The main Claude Code session reads the agent definitions available to it, and when a task matches a subagent’s description, it delegates via the Task tool. The subagent that spins up is a separate context with its own system prompt and — critically — its own restricted tool list. It cannot reach for Bash to run rm -rf or for Edit to touch application code if those tools were never listed in its frontmatter. There’s nothing to “ask nicely” about; the tool simply isn’t in the process.
.claude/agents/test-writer.md
┌──────────────────────────────────────────────┐
│ --- │
│ name: test-writer │
│ description: Writes unit and integration │
│ tests for changed files. Use when a PR │
│ needs test coverage. │
│ tools: [Read, Write, Bash] │
│ model: sonnet │
│ --- │
│ │
│ You are a test-writing specialist. Given a │
│ diff, write tests that cover the new │
│ behavior. Do not modify application code. │
│ Run the test suite to confirm it passes. │
└──────────────────────────────────────────────┘
│
│ main agent recognizes a matching
│ task, delegates via Task tool
▼
┌──────────────────────────────────────────────┐
│ SUBAGENT SESSION: test-writer │
│ own system prompt: "test-writing specialist" │
│ own tool allowlist: Read, Write, Bash only │
│ (no Edit on app code, no git push, no MCP │
│ tools it wasn't granted) │
│ runs → returns result → main agent continues │
└──────────────────────────────────────────────┘
Two fields in the frontmatter do the actual scoping work. tools is the allowlist — anything not named there is unreachable inside that subagent’s session, no matter how the prompt is worded. model lets you pair the task with an appropriately sized model — a mechanical task like test scaffolding doesn’t need your most expensive model, while a subagent doing architectural review might. The description field is what the main agent reads to decide when to delegate, so it needs to say plainly what kind of task belongs here.
This is a config-file mechanism, not a runtime negotiation. You write the file once, and every future task that matches gets the same guarantees — the same restricted toolset, the same model, the same system prompt — without you re-specifying any of it in the conversation.
Why the other options fail
- A) Ask nicely in the prompt. A prompt instruction is advisory. The model reads it, and in the common case follows it — but nothing in the runtime enforces it. If the full toolset is available in the session, a sufficiently ambiguous request, a long conversation, or an edge case can still result in the agent reaching for a tool you asked it not to use. Scoping needs to happen at the tool-availability layer, not the wording layer. This is the same distinction the series covers in Instructions or a Hook — advisory text versus enforced mechanism.
- C) A new CLAUDE.md. CLAUDE.md is memory — always-on context the model reads at the start of a session, as covered in Where Does a Team Convention Live?. It’s excellent for standing conventions (“we use pytest,” “never commit generated files”) but it does not restrict which tools are callable. A CLAUDE.md note describing a test-writer role is still just a note in a session that has the full tool surface available.
- D) A slash command. A slash command, defined in
.claude/commands/*.md, is a reusable prompt template you invoke explicitly with/command-name— see Where Does a /review Shortcut Live?. It expands into a prompt in the current session; it doesn’t spin up a separate context with its own tool allowlist. You could put “write tests, don’t touch other code” into a slash command, but you’d be back to option A’s advisory problem — the full toolset is still sitting in the same session.
The concept behind it
The general principle is scope by construction, not by instruction. Anywhere Claude Code lets you restrict what an agent can reach — rather than merely what it’s told to do — that mechanism produces a guarantee. Anywhere the restriction only lives in prose, it produces a preference.
Subagents apply this principle at the tool-access layer. The .claude/agents/*.md file format gives you three levers per specialist:
| Frontmatter field | What it controls | Why it matters |
|---|---|---|
name | The identifier the main agent references when delegating | Lets you address a specific specialist deliberately |
description | When the main agent should delegate here | Poor descriptions cause under- or over-triggering |
tools | The allowlist of tools available inside this subagent’s session | The actual security/scope boundary — see Too Many Tools, Wrong Picks for the same idea applied to a whole agent’s toolset |
model | Which model runs this subagent | Cost/capability tradeoff, independent of the main session’s model |
This pattern generalizes past “test-writer.” A code-review subagent gets Read and a linting tool but not Write or Bash. A documentation subagent gets Read and Write scoped to a docs/ path convention in its prompt, but no Bash. A release-notes subagent gets Read and Bash(git log:*) but nothing that touches the working tree. Each one is a separate file, a separate allowlist, and a separate, auditable boundary — you can read the frontmatter and know exactly what that specialist could possibly do, without reading its entire prompt.
It’s worth being precise about where subagents sit relative to two other Claude Code primitives, since exam questions (and real debugging) tend to probe the boundary:
- Subagent vs Skill. An Agent Skill (
SKILL.md) packages files and instructions the main agent reaches for when relevant — it doesn’t spin up a separate context or restrict tools; see Slash Command or Skill? for the fuller comparison. A subagent spins up a distinct session with its own tool allowlist. - Subagent vs permission rules. Permission rules in
settings.jsongovern what’s allowed, asked, or denied across a session, regardless of which agent is running — see Allow npm test, Gate Destructive Git. A subagent’stoolslist is a different, narrower scoping layer: it decides which tools even exist in that context, before permission rules ever get evaluated.
Both mechanisms are enforcement layers, not suggestions — which is the throughline of Domain 3. Whenever you’re deciding how to constrain agent behavior, ask where the constraint actually lives: in a file the runtime reads and enforces, or in text the model merely reads.
FAQ
Where exactly does a subagent definition file live?
Project-level subagents live in .claude/agents/*.md inside the repo, so the whole team gets the same specialists — one Markdown file per agent, frontmatter plus a system prompt. Personal subagents you want across every project typically live under your user-level Claude Code directory, mirroring the same project-vs-user split covered in A Rule for You, Not Your Team. Verify the exact current path in Anthropic’s Claude Code docs, since Domain 3 covers live product surface that moves fast.
Can a subagent call other subagents? The mental model to hold is a main agent that delegates to scoped specialists via the Task tool, each running in its own context. Whether nesting is supported and how deep it goes is exactly the kind of implementation detail that shifts between Claude Code releases — treat the “own prompt, own tool allowlist, delegated via Task” mechanism as the durable fact, and check current docs for delegation depth.
Isn’t this the same as just prompting the main agent to “act like a test-writer for this task”?
No — that’s option A’s failure mode. Prompting changes tone and priorities; it doesn’t remove tools from the session. A subagent’s tools frontmatter is enforced by the runtime before the model ever gets a turn to decide whether to use something outside its lane.
Does giving a subagent a smaller tool list also make it faster or cheaper?
Often, yes, as a side effect — fewer tool definitions in context means less prompt overhead per turn, and pairing a narrow-scope subagent with a smaller model value compounds the savings. But the primary reason to scope is correctness and safety, not cost; the cost benefit is a bonus, not the design driver.
Where this fits in the CCA series
This is Domain 3, Configuration & Workflows — question 10 of 10 in this practice set, and it closes out the domain’s core toolkit: memory hierarchy, hooks, slash commands, Skills, headless mode, permissions, settings scope, precedence, and now subagents. If you haven’t already, the CCA Exam Guide lays out all five domains on one diagram so you can see where D3’s 20% sits against the rest of the exam.
For the closest siblings to this question, see Slash Command or Skill? for the model-invoked-vs-you-invoked distinction that subagents sit next to, Instructions or a Hook for the advisory-vs-enforced theme this question shares, and Allow npm test, Gate Destructive Git for the other tool-scoping layer in Claude Code.
Not affiliated with, authorized, or endorsed by Anthropic. “Claude” and “Claude Certified Architect” are trademarks of Anthropic. These are original practice questions for study, not real exam content — verify current exam details on Anthropic’s official pages.
Browse all tutorials for the rest of the CCA prep series.
Found this useful? The deep version lives on YouTube — new breakdowns of how AI dev tools actually work, weekly.
Subscribe on YouTube →