Context injection is the practice of strategically delivering the right information to Claude at the right time — keeping its context window focused on what matters while avoiding bloat from irrelevant material.
Not all context is equal. Prioritize by retention value:
ALWAYS IN CONTEXT (loaded every session)
├── CLAUDE.md ← Project rules and stack
├── .claude/rules/*.md ← Detailed rules (auto-loaded)
└── .claude/memory/MEMORY.md ← User preferences and project state
LOADED ON DEMAND (when relevant)
├── Skill files (.claude/skills/*) ← Domain patterns
├── Plan files (docs/plans/*.md) ← Current feature plan
└── Specific code files ← Only what's needed
NEVER IN CONTEXT (read-only artifacts)
├── blackbox/session-log.md ← Audit trail
└── docs/diagrams/*.md ← Reference only
The "never in context" category is important. Large append-only logs consume context without providing proportional value.
The cleanest injection pattern: use UserPromptSubmit hooks to automatically add context to every prompt.
#!/usr/bin/env bash
# inject-context.sh — adds git state + sprint context to every prompt
BRANCH=$(git rev-parse --abbrev-ref HEAD 2>/dev/null || echo "no-git")
COMMIT=$(git log --oneline -1 2>/dev/null || echo "no commits")
CHANGED=$(git diff --name-only HEAD 2>/dev/null | head -5 | sed 's/^/ - /')
cat << EOF
## Session Context
- Branch: ${BRANCH}
- Latest commit: ${COMMIT}
$(if [ -n "$CHANGED" ]; then echo "- Uncommitted changes:"; echo "$CHANGED"; fi)
EOFThis runs automatically before every prompt. Claude always knows the current branch and state without you having to say it.
Subagents start fresh — they need explicit context injection. Template:
## INJECTED PROJECT CONTEXT
Project: [Name] — [one sentence description]
Stack:
- [Language + version]
- [Framework + version]
- [Database]
Critical constraints (these are non-negotiable):
1. [Most important constraint]
2. [Second constraint]
3. [Third constraint]
Relevant files for this task:
- [path]: [one-line purpose]
- [path]: [one-line purpose]
DO NOT:
- [Most important thing to avoid]
- [Second thing to avoid]
## TASK
[The actual task]
## DONE WHEN
- [ ] [Verifiable criterion 1]
- [ ] [Verifiable criterion 2]
- [ ] Tests pass: [exact command]
This template ensures every subagent has the constraints it needs, regardless of what's in CLAUDE.md.
Before complex implementations, ask Claude to surface its assumptions. This is context injection in reverse — you're asking Claude to tell you what context it's using:
"Before implementing, surface your assumptions:
ASSUMPTIONS I'M MAKING:
1. [assumption about tech/architecture]
2. [assumption about scope]
3. [assumption about edge cases]
→ Correct me now or I'll proceed with these."
This pattern catches wrong context before it becomes wrong code.
For multi-session features, plan files serve as persistent context injection:
# docs/plans/2026-04-06-rbac.md
## Status: IN PROGRESS (started 2026-04-06)
## Approved by: [human] on 2026-04-06
## Feature
Role-based access control for payments API
## Decisions Made
- Using permissions (not roles) to allow future ABAC upgrade
- JWT includes permissions array in payload
- 15-minute access token, 7-day refresh token
## Current Progress
- [x] Phase 1: permissions.py created
- [x] Phase 2: dependency added to routes
- [ ] Phase 3: tests (NEXT)
## Key Files
- src/auth/permissions.py — permission definitions
- src/payments/routes.py — permission checks added
- tests/auth/test_permissions.py — IN PROGRESSNew session:
"Load docs/plans/2026-04-06-rbac.md and continue from Phase 3."
→ Claude reads the plan
→ Has full context from Phase 1 + 2
→ Continues from exactly where you left off
Before /compact or at 70% context capacity, save a structured summary:
## Session Intent
Adding RBAC to the payments API
## Files Modified
- src/auth/permissions.py — created, has PERMISSIONS dict and require_permission()
- src/payments/routes.py — all 4 endpoints now have permission checks
## Decisions Made
- Using `permissions: list[str]` not `roles` — future ABAC compatibility
- Tokens include permissions in JWT payload
- 401 for missing/invalid token, 403 for valid token but missing permission
## Current State
Tests: 0 passing (haven't written them yet)
Implementation: complete, linting clean
## Next Steps
1. Write tests in tests/auth/test_permissions.py
2. Write 5 test cases: read-ok, write-ok, write-rejected, no-token, invalid-token
3. Run tests, verify all passAfter /compact, this summary is what Claude retains about the session so far.
Some information actively hurts context quality:
Don't inject:
- Long error messages that have been resolved
- Full file contents when only a function is relevant
- Git history for files you're not touching
- Exploratory reads that didn't lead anywhere
- Session logs (blackbox/session-log.md)
Why: Context distraction is real. Irrelevant material in the context window reduces recall of relevant material by 10–40%. Trim aggressively.
General principle: inject context as late as possible, as precisely as possible.
# Bad: inject everything upfront
"Here's our entire codebase structure: [500 lines]
Here's our entire architecture: [300 lines]
Now, fix the login bug."
# Good: inject only what's relevant
"Fix the login bug. The login handler is at src/auth/routes.py:login_user().
The JWT issuer is at src/auth/tokens.py:issue_jwt().
The test is at tests/auth/test_login.py:test_login_invalid_password() — it's failing."
The second prompt is more useful despite being much shorter, because all the context is precisely relevant to the task.
- Context Management — Managing the context window across long sessions
- Subagents — Context injection for subagent dispatch
- Hooks — Automating context injection via UserPromptSubmit hooks