[BUG] Frontmatter-preloaded agent skills re-injected in full on every SendMessage resume (linear duplicate context growth)

Open 💬 1 comment Opened Jul 10, 2026 by whimzyLive

[BUG] Frontmatter-preloaded agent skills are re-injected in full on every SendMessage resume (linear duplicate context growth)

Summary

When a custom agent (.claude/agents/*.md or plugin agent) declares skills: in its frontmatter, the full text of each listed skill is injected into the subagent's context at spawn — as expected. However, every subsequent SendMessage resume of that subagent re-injects the entire preload set again, verbatim. Copies grow linearly: N resumes → N+1 identical copies of every preloaded skill in the agent's context.

For an agent with 4 preloaded skills (~19k chars / ~5k tokens per set), a 10-resume conversation carries ~50k tokens of byte-identical duplicate skill text.

Environment

  • Claude Code: 2.1.206
  • Platform: macOS 26.5.1 (darwin), CLI
  • Agent type: plugin-defined subagent with frontmatter:

```yaml
skills:

  • executing-plans
  • conventional-commit
  • test-driven-development
  • verification-before-completion

```

Reproduction

  1. Define an agent with 2+ skills in its frontmatter skills: list.
  2. Spawn it via the Agent tool with any prompt.
  3. Resume it twice via SendMessage with a trivial message each time (e.g. "Reply with the single word ack. No tools, no skills.").
  4. Count skill injections in the subagent transcript (<session-dir>/tasks/<agentId>.output):
grep -o '<command-name>[a-z-]*</command-name>' tasks/<agentId>.output | sort | uniq -c

Observed results (actual transcript data)

Injection counts per preloaded skill:

| State | Copies of EACH preloaded skill |
|---|---|
| Fresh spawn (0 resumes) | 1 |
| After resume 1 | 2 |
| After resume 2 | 3 |

Injection timestamps from the transcript show one full batch of 4 skills per event — spawn and each resume:

2026-07-10T08:58:22Z  executing-plans, conventional-commit, test-driven-development, verification-before-completion   (spawn)
2026-07-10T09:12:50Z  executing-plans, conventional-commit, test-driven-development, verification-before-completion   (resume 1)
2026-07-10T09:13:21Z  executing-plans, conventional-commit, test-driven-development, verification-before-completion   (resume 2)

The duplicates are byte-identical: all 3 copies of the test-driven-development block are 9,892 chars with the same sha256 (729e0fa6…). One full preload set is ~19k chars (~5k tokens).

Each injection arrives as an isMeta: true user message with the standard skill wrapper:

<command-message>test-driven-development</command-message>
<command-name>test-driven-development</command-name>
<skill-format>true</skill-format>
Base directory for this skill: …/skills/test-driven-development
# Test-Driven Development (TDD)
…full body…

Cumulative subagent token usage corroborates: 35,659 → 43,247 → 51,387 tokens across the two resumes — ~8k tokens added per resume even though the agent's entire output each time was the single word "ack" with zero tool calls.

Expected

Preloaded skills are injected once at spawn. A SendMessage resume continues from the existing transcript (which already contains the skill text) without re-injecting it — same as how the rest of the transcript is not duplicated on resume.

Impact

  • Linear context bloat per resume for any agent using frontmatter skill preloads — directly multiplies token cost and eats the context window of long-lived agents.
  • Punishes exactly the recommended pattern: the harness suggests continuing agents via SendMessage to preserve context, but each continuation re-pays the full preload.
  • Compounds with larger skills: lists — cost scales as (skills total size) × (resume count).

Workarounds

  • Keep frontmatter skills: lists minimal.
  • Prefer fresh short-lived agent dispatches over long SendMessage chains.

Possibly related (different mechanisms, for triage cross-ref)

  • #25994 — skills doubled after compaction (main session; closed)
  • #73280 — compaction summarizes skill content instead of clearing it
  • #67345 / #75054 / #67794 — other state lost/mishandled on SendMessage resume (model override), suggesting resume path rebuilds spawn-time state instead of restoring it

View original on GitHub ↗

This issue has 1 comment on GitHub. Read the full discussion on GitHub ↗