[Behavioral] Skill-list system-reminder is re-injected on tool-call boundaries without content-diff check, causing repeated 5.5k-token redundancy in stable sessions
Behavioral Observation: Skill-list system-reminder is re-injected on tool-call boundaries without content-diff check, causing repeated 5.5k-token redundancy in stable sessions
Category: Behavioral Pattern (not a bug or feature request)
Model: Claude Opus 4.6 (1M context) via Claude Code 2.1.119 (Claude Code)
Reproducibility: likely_systematic
Severity: medium
Observed Behavior
During a 4-hour skill-intensive session (S212, building a new global skill, editing several SKILL.md / MEMORY.md files, and using Edit/Write/Bash tool calls in skill paths), the Claude Code harness re-injected the full system-reminder block listing all available skills approximately 6-8 times. Each injection contained ~80 entries (globals + plugin-skills) at ~5,500 tokens, with no diff against previous injections — same content, same order, repeated verbatim. Cumulative redundant token cost in this single session: ~33,000 tokens of pure list overhead, beyond the actual conversation work. The trigger appears to correlate with tool calls that touch skill-related paths (Edit / Write / Bash on ~/.claude/skills/* or project skill directories), but the trigger fires regardless of whether the skill set has changed since the previous injection.
Expected Behavior
The skill-list system-reminder should be cached after its first injection in a session. Re-injection should only occur when (a) the underlying skill set has actually changed (new file in skills/, deletion, frontmatter edit), or (b) an explicit refresh trigger fires (e.g., a user-issued command like 'reload skills'). Tool calls that read or write skill files should not invalidate the cache by default — they should compare the resulting skill set against the previously injected one and only re-inject on real change.
Claude's Self-Analysis
The re-injection trigger is tool-type-based (Edit / Write / Bash on skill paths), not content-based. There is no apparent diff or hash check before re-injection — identical 5,500-token blocks are emitted in succession. Several existing OPEN issues on anthropics/claude-code share this root cause but describe different symptoms: #14882 (full skill bodies loaded at startup, not progressive disclosure), #44045 (skill_listing block repositioned on --resume, causing partial cache miss), #51008 (skills triplicated in slash-command autocomplete), #52942 (system-injected tools/schemas consume tokens without user control), #50998 (repeated context injection via JSONL attachments). Our specific symptom — stable-content re-injection at every tool-use boundary in a session that didn't modify the skill set — appears to be undocumented as its own issue. Local mitigation options are limited: settings.json has no disabledSkills / enabledSkills / skillScope fields. The only available lever is per-skill 'disable-model-invocation: true' in SKILL.md frontmatter, which is reported to hide skills from the slash-command menu but its effect on the system-reminder injection is unverified and likely marginal (28 of 88 skills already have the flag, and the system-reminder list still contains ~80 entries).
User Impact
Per 4-hour skill-intensive session: ~33,000 tokens of redundant list content, equivalent to roughly 16% of a 200k-context window. Direct effects: (1) elevated input-token API cost — at Sonnet/Opus rates, this is non-trivial when accumulated across daily sessions; (2) prompt-cache miss risk — if the re-injection lands in a position that displaces the previous cache cut, the next turn drops out of cache (5x cost penalty within the 5-minute TTL); (3) earlier auto-compaction trigger because of context-window pressure, which itself is an expensive re-read of the entire session; (4) attention-budget drift — the model receives the full ~80-skill description block repeatedly, which competes with the actual user request for attention and may degrade response quality over long sessions. For a heavy user editing skills daily, the cumulative monthly cost is likely substantial.
Local Mitigation
Investigated and found largely ineffective at the user level. (1) settings.json offers no skill-list filter fields. (2) 'disable-model-invocation: true' in skill frontmatter exists but its effect on the system-reminder injection is unverified — empirically the system-reminder list still appears to contain ~80 entries even though 28/88 skills have the flag set. Setting this flag on more skills risks breaking self-healing autonomous-orchestration loops (one skill calling another). (3) Plugin-marketplace toggles only affect plugin skills, not core or project skills. (4) Hooks cannot intercept system-reminder generation — that is handled by the harness before the model receives input. Conclusion: full mitigation requires harness-side changes; user-side workarounds at best reduce list size by single-digit percent at the cost of breaking autonomy patterns.
Suggestion for Model Improvement
Cache the most recently injected skill-list state per session. Compute a hash of the relevant skill set (file paths + frontmatter mtimes, or equivalent) once at session start and again only when a tool call modifies a skill file. Re-inject the system-reminder block only when the hash changes or when an explicit refresh is requested. Tool calls that read skill files (without modification) should never trigger re-injection. Optionally: expose a settings.json field 'skillListInjectionMode: cached | always | minimal' so power users can opt into stricter behavior. Related fixes #14882 and #44045 already imply that the harness-side logic for skill-list management needs structural changes — this observation provides one more concrete symptom of the same root cause.
---
This observation was documented during a Claude Code session and reviewed by the user before submission.
No claims in this issue are estimated — all details are from actual session transcripts.
This issue has 3 comments on GitHub. Read the full discussion on GitHub ↗