LEARN · PHASE 06 · LESSON 48

Skills Inside Subagents — the genuinely confusing case

Two fields that sound like the same thing do opposite jobs. Get this wrong and

Type
Learn
Prerequisites
Lessons 45–47; Phase 05 Lesson 42 (The Invocation Matrix), Lesson 44 (Author a Skill From Scratch)
Time
~35 minutes

Learning Objectives

  • Tell skills: on a subagent apart from context: fork on a skill, in one sentence each
  • Decide which direction you need before you write either field
  • Say what a preloaded skill costs and what it saves
  • Explain why a skill with disable-model-invocation: true can't be preloaded

The Problem

An agent needed to write API endpoints matching a team's conventions. Delegating to a subagent produced plausible endpoints that ignored every convention the team had written down — different error shape, different naming, no auth header. The conventions existed. They were in a skill. The subagent never saw them.

The fix was one line, skills: in the agent's frontmatter, which preloads the full content of named skills before work starts.

Then somebody tried the same thing the other way round. They set context: fork on the convention skill itself, expecting it to "apply to subagents too." It ran in a fresh subagent with the skill text as its only prompt, no history, and returned something useless — because the skill said "follow these conventions" and a subagent with no task to perform has nowhere to apply them.

Two fields. Opposite directions. One of them failed for a reason that looks like it should have worked.


The Concept

There are two arrows. They point opposite ways, and they do different things.

Direction one: knowledge into a subagent

The skills field on a subagent preloads the full content of named skills into that subagent's context at startup.

```markdown .claude/agents/api-developer.md


name: api-developer description: Implement API endpoints following team conventions skills:

  • api-conventions
  • error-handling-patterns

Implement API endpoints. Follow the conventions and patterns from the preloaded skills.


The subagent controls the system prompt. The skill is reference material sitting
next to it, loaded before work starts.

**What it does not do:** restrict anything. A subagent can still invoke *other*
project, user, and plugin skills through the `Skill` tool during the run. `skills:`
selects what's preloaded, not what's reachable.

To actually block skill invocation, remove `Skill` from `tools`, or add it to
`disallowedTools`. Those are enforcement. `skills:` is not.

Limits and rules:
- Up to **32 distinct names** are preloaded; the rest are ignored.
- Skills with `disable-model-invocation: true` **cannot** be preloaded — preloading
  draws from the set of skills Claude is allowed to invoke.
- A missing or disabled skill is skipped with a warning in the debug log. Not an
  error.
- Built-in agents don't preload skills at all.

### Direction two: a skill *into* a subagent

`context: fork` on a skill runs the skill's content in a fresh subagent.

name: deep-research description: Research a topic thoroughly context: fork agent: Explore


Research $ARGUMENTS thoroughly:

  1. Find relevant files using Glob and Grep
  2. Read and analyze the code
  3. Summarize findings with specific file references

The subagent is the **task**. It gets the rendered skill as its prompt, runs
`agent` (which sets model, tools, permissions), and returns its result.

flowchart TD subgraph A["skills: on a subagent"] A1["Subagent's own body<br/>= its system prompt"] --> A3["Named skills<br/>preloaded alongside"] A2["Claude's delegation message<br/>= its task"] --> A3 A3 --> A4["Subagent does the task,<br/>with the knowledge loaded"] end subgraph B["context: fork on a skill"] B1["Rendered SKILL.md<br/>= the subagent's PROMPT"] --> B2["The task IS the skill"] B2 --> B3["Subagent returns the result"] end


Same words, inverted roles. In one, the skill is *reference for* a task. In the
other, the skill *becomes* the task.

### The comparison table

| | `skills:` on a subagent | `context: fork` on a skill |
|---|---|---|
| System prompt | The subagent's markdown body | The agent type named in `agent` |
| Task | Claude's delegation message | The rendered SKILL.md |
| Also loads | Named skills + CLAUDE.md, per the subagent's startup | CLAUDE.md, per the agent type's startup |
| Starts with your history? | No | No |

### The trap in the name

`context: fork` does **not** fork your current conversation. It creates a new
isolated context. That's the exact opposite of the `fork` from Lesson 47, which
does inherit everything.

If your task depends on the conversation, `/subtask` (Lesson 47) is what you
want. `context: fork` on a skill is the wrong tool and will not be fixed by
turning it on.

### When `context: fork` is pointless

`context: fork` only makes sense for skills with **explicit instructions**.

A skill that says "use these API conventions" is a guideline. In a forked
subagent it becomes a prompt with no task — the subagent gets the guidelines, has
nothing to do, and returns without meaningful output.

Guidelines → put them in a subagent's `skills:` field, where a real task is
waiting next to them.

### Behaviour worth knowing before you use it

- Forked skills normally run in the **background**. You keep working; the result
  arrives when it finishes. Set `background: false` to wait.
- A backgrounded fork also runs with the **narrower background-subagent tool
  set**. If your skill's steps need a tool outside that set, `background: false`.
  Note the asymmetry with Lesson 47: a `/subtask` **conversation** fork is
  exempt from that filter and keeps your full tool pool. A `context: fork` skill
  is an ordinary subagent, so the exemption does not cover it.
- A backgrounded fork's edits fall **outside session checkpoints**. `/rewind`
  won't undo them. Use git.
- Claude Code waits anyway in non-interactive mode (`-p`, the Agent SDK), when
  another invocation of the same forked skill is running, when a scheduled task
  invokes it, and for some dynamic-workflow agent invocations.
- For side-effecting operations, model invocation on a forked skill is normally
  the safer default — Claude triggering your deploy should require a human to type
  `/deploy`.

> **The reframe:** `skills:` on a subagent loads knowledge *next to* a task the
> subagent already has, while `context: fork` on a skill *becomes* the task — and
> the one that runs in the background quietly gets a smaller tool set and no
> checkpoint safety net.

Skill frontmatter moves too. Verify against
[the skills page](https://code.claude.com/docs/en/skills) and
`https://code.claude.com/docs/llms.txt`.

---

## Build It

### 1. Build the failing case on purpose

Create `.claude/skills/api-conventions/SKILL.md`:

name: api-conventions description: Team conventions for API endpoints. Error shape, naming, auth headers.


Every endpoint returns {"error": {"code": "...", "message": "..."}} on failure. Never return a bare error string. Authenticated routes require an Authorization header. Handlers are named handle<Resource><Verb>.


Now create `.claude/agents/api-developer.md` **without** a `skills` field, same
body as the example above. Restart. Ask it to write an endpoint.

✅ You pass when the output ignores at least one convention and you can see the
convention was available in the repo the whole time.

### 2. Add the one line

Add to the agent's frontmatter:

skills:

  • api-conventions

Restart. Ask again.

✅ You pass when the output now follows the error shape and the handler naming,
and you can name the line responsible.

### 3. Check that it preloads rather than restricts

Remove `Skill` from nothing — the agent has no `tools` field, so it inherits the
pool including `Skill`. Add a second skill to the repo that this agent was never
told about, and ask it to use it.

It can. `skills:` is a preload list, not an access list.

✅ You pass when the agent can reach a skill you never named, and you can say
what would actually block it.

### 4. Try the other direction and watch it fail

Add `context: fork` to the conventions skill itself. Remove `skills:` from the
agent. Restart. Invoke the skill.

The subagent receives the guidelines and no task.

✅ You pass when it returns something empty or confused, and you can say which
half of the problem that was.

### 5. Fix it by giving the skill a task

Rewrite the skill body as instructions with a task, and point `agent:` at
`Explore`:

name: api-conventions description: Audit API handlers in a repo against team conventions context: fork agent: Explore


Audit the repository's API handlers against these conventions:

  • Errors return {"error": {"code": ..., "message": ...}}, never a bare string.
  • Authenticated routes carry an Authorization header.
  • Handlers are named handle<Resource><Verb>.

Report each violation as path:line — convention broken.


✅ You pass when it produces findings rather than nothing, and you can say what
changed: the skill went from guideline to instruction.

### 6. Feel the background cost

Set `background: false` on the forked skill. Run something in the main
conversation while it runs.

Then remove it and do the same.

✅ You pass when you can state what changes about *your* session — you can either
keep working or you can't — without rereading the docs.

---

## Use It

| Situation | Move |
|---|---|
| A subagent needs standing knowledge it can't discover | `skills:` in the subagent |
| You want to stop a subagent invoking skills at all | Remove `Skill` from `tools`, or `disallowedTools: Skill` |
| You want a skill to run isolated, in the background | `context: fork` on the skill |
| The task depends on your conversation history | `/subtask`, not `context: fork` |
| A skill is pure guideline with no task | Don't fork it. Preload it into a subagent that has a task. |
| A forked skill needs a tool the background set drops | `background: false` |
| A backgrounded fork edited files | Use git. `/rewind` won't reach it. |
| Something destructive, invoked by a model | `disable-model-invocation: true` |

---

## Exercises

1. Take a skill from your own repo. Decide whether it is a guideline or an
   instruction, and put it on the matching side of the arrow.
   ✅ You pass when the placement follows the task/no-task distinction rather than
   which one felt more isolated.

2. Author a subagent with a `skills:` list, then add a `tools:` field that omits
   `Skill`. Try to make it invoke an unlisted skill.
   ✅ You pass when it cannot, and you can say which of the two fields did the
   blocking.

3. Run the same task three ways: in the main conversation, as a fresh subagent,
   and as a fork. Note what each one knew at the start.
   ✅ You pass when you can name what the fork had that the fresh subagent
   didn't, and what it gave up to get it.

---

## Key Terms

| Term | What people say | What it actually means |
|---|---|---|
| `skills:` on a subagent | "give this agent skills" | Preloads full skill content into its window at startup. Up to 32 names. Not a restriction. |
| `context: fork` on a skill | "run this in a fork" | Starts a new isolated subagent whose prompt is the skill. Not a fork of your conversation. |
| preload vs access | "skills controls what it can use" | It controls what's *loaded*. Access is `tools` / `disallowedTools`. |
| `disable-model-invocation` | "manual-only skill" | Also makes the skill **unpreloadable** — preloading draws from the invocable set. |
| guideline vs instruction | "a skill" | Guidelines have no task and fork to nothing. Instructions fork usefully. |
| backgrounded fork | "running in parallel" | Narrower tool set, outside session checkpoints. `/rewind` doesn't reach its edits. |
| `agent:` on a forked skill | "which agent it uses" | Picks the subagent type — which decides model, tools, permissions. Defaults to `general-purpose`. |

---

## What this lesson skipped

**Auto-compaction inside a subagent.** `autoCompactWindow` exists, and the window
fills exactly as it does in the main conversation — Phase 04 Lesson 30 owns that.

**Prompt caching.** A subagent's first request is cold unless it shares a
parent's — a fork's does. This changes what a fork actually costs, and Phase 03
Lesson 22 is where caching makes sense as a budget.

**Agent teams and cross-session messaging.** Subagents that can message each
other, and roster injection at startup. Phase 11.

**How skills get packaged and shared.** A skill in a plugin is still a skill, but
scoped names and the discovery path change. Phase 09.

Check yourself

5 questions. Answer before you reveal — recognising the right option is not the same as being able to produce it.

  1. Your API subagent ignores a team conventions skill. You want that skill's content present before it starts writing code. Which change does it?

    1. Add skills: [api-conventions] to the subagent's frontmatter
    2. Add context: fork to the conventions skill's frontmatter
    3. Move the conventions into CLAUDE.md
    4. Add the skill name to the delegation message each time you ask
    Show answer

    A. Add skills: [api-conventions] to the subagent's frontmatter

    The skills field preloads full skill content into the subagent's context at startup, so the knowledge is there before work begins. Option B forks the skill into a fresh subagent where those guidelines become a prompt with no task — the classic empty result. Option C puts it in the subagent's window but nowhere near the endpoint code being written. Option D works but re-pays every run and depends on you remembering.

  2. A subagent lists skills: [deploy-conventions] in its frontmatter. You want it to be unable to invoke any skill at all, including ones you didn't list. What actually enforces that?

    1. skills: — listing one skill already makes it the only reachable one
    2. Remove Skill from the subagent's tools, or add it to disallowedTools
    3. set disable-model-invocation: true on every other skill in the repo
    4. Nothing — subagents can't invoke skills, only preload them
    Show answer

    B. Remove Skill from the subagent's tools, or add it to disallowedTools

    skills selects what's preloaded, not what's reachable — a subagent can still invoke project, user, and plugin skills through the Skill tool during the run. Removing Skill from the allowlist or denying it is the enforcement layer. Option C is both global and backwards: it would break your own invocations, and it's the wrong tool for scoping one agent. Option D is false — subagents can invoke skills.

  3. A skill whose body is only "use these API conventions" is set to context: fork. Running it returns something vague or empty. Why?

    1. The skills field was missing so the content never loaded
    2. forked skills need a model field or they default to refusing work
    3. context: fork makes the skill the subagent's prompt, and a guideline with no task gives it nothing to do
    4. Explore and Plan can't run skills and the default agent was Plan
    Show answer

    C. context: fork makes the skill the subagent's prompt, and a guideline with no task gives it nothing to do

    context: fork turns the rendered SKILL.md into the subagent's prompt, so a skill needs explicit instructions with a task — a bare guideline leaves the subagent with principles and no work, and it returns without meaningful output. Option A confuses the two directions; the skills field belongs on the subagent, not the skill. Option D misreads which agent type applies — the default is general-purpose, and none of the listed agents are the cause.

  4. A forked skill edits three files in the background. You run /rewind expecting the edits to be undone and they aren't. Why?

    1. /rewind only handles the main conversation, which is exactly this case
    2. The skill's context: fork field wasn't committed to version control
    3. A backgrounded fork runs outside the session's checkpoints, so /rewind never reaches its edits
    4. Edits from any subagent are permanent by design
    Show answer

    C. A backgrounded fork runs outside the session's checkpoints, so /rewind never reaches its edits

    Backgrounded forks specifically fall outside session checkpoints — that's why the guidance is to revert them with git rather than /rewind. Option A inverts it: /rewind handles the main conversation, and this edit isn't in it. Option C's scope is wrong too — foreground subagent edits do fall under checkpoints; it's the background-fork case that's outside them.

  5. You want a subagent that has your whole conversation, plus the option to edit files. Which combination is right?

    1. A skill with context: fork and an agent field pointing at general-purpose
    2. A fork — /subtask — with isolation: worktree if the edits must not touch your checkout
    3. A custom subagent with skills: preloaded, since preloading carries conversation history
    4. A fresh subagent with omitClaudeMd: false, which inherits the parent conversation
    Show answer

    B. A fork — /subtask — with isolation: worktree if the edits must not touch your checkout

    Only a fork inherits the full parent conversation; /subtask starts one, and isolation: worktree redirects its file edits into a separate worktree so your checkout stays clean. Option A gets the name backwards — context: fork creates a fresh isolated context and does not carry your history. Option B is wrong because preloading skills injects reference text, not conversation. Option D is also wrong: omitting CLAUDE.md controls instruction files, not history.