ADR-210: Background Agent Manifest for Session Recovery
Status
Accepted — 2026-02-18
Context
When Claude Code sessions approach their context limit, the system compacts prior messages to free space. Background agents launched via Task(run_in_background=true) survive compaction — their processes continue running and their output files remain on disk — but the context about them (agent IDs, target file paths, what they were asked to do) is lost.
This creates two failure modes:
- Duplicate work: The next session re-launches agents that are already running, wasting tokens and producing conflicting output files.
- Orphaned agents: Completed background agents go unnoticed because no one checks their output files.
Root Cause
Background agent launches are ephemeral conversation events. Once compacted, the only surviving reference is in system-reminder tags (which preserve agent IDs but not intent or output paths).
Observed Incident
2026-02-18: Three MoE judge agents (G.6.5) were launched in parallel. Session compacted before completion was logged. The next session had no record of their existence, requiring manual recovery by grepping session logs and checking output files.
Decision
Implement a PostToolUse:Task hook (background-agent-manifest.py) that intercepts every Task tool call and, when run_in_background=true, writes a structured JSONL manifest entry containing:
| Field | Source | Purpose |
|---|---|---|
timestamp | UTC ISO 8601 | When the agent was launched |
agent_id | tool_result parsing | Unique agent identifier |
subagent_type | tool_input.subagent_type | Agent type (e.g., synthesis-writer) |
description | tool_input.description | Short task description |
prompt_preview | First 200 chars of prompt | What the agent was asked to do |
output_file | tool_result parsing | Where to find agent output |
session_id | CLAUDE_SESSION_ID env | Which session launched it |
cwd | CLAUDE_PROJECT_DIR env | Working directory context |
status | Always "launched" | Initial state |
Manifest Locations
-
Global manifest:
~/.coditect-data/agent-manifests/background-agents-YYYY-MM-DD.jsonl- One file per day, append-only
- Survives across all sessions and projects
-
Project manifest (best-effort):
~/.coditect-data/session-logs/projects/{project-id}/{machine-uuid}/.background-agents.jsonl- Project-scoped for targeted recovery
- Uses existing project detection from
scripts.core.paths.discover_project()
Recovery Workflow
A recovering session can:
# Check today's background agents
cat ~/.coditect-data/agent-manifests/background-agents-$(date -u +%Y-%m-%d).jsonl | python3 -m json.tool
# Check specific agent output
tail -20 /path/from/manifest/output_file
# Check project-level agents
cat ~/.coditect-data/session-logs/projects/PILOT/{machine}/.background-agents.jsonl
Consequences
Positive
- Zero-cost recovery: Any session can discover agents launched by prior sessions without re-scanning conversation history
- No workflow change: The hook is transparent — it fires automatically on every background Task call
- Append-only safety: JSONL format means concurrent writers don't corrupt each other
- Project-scoped: Project manifest enables targeted recovery within project context
Negative
- Parse dependency: Agent ID and output file extraction relies on parsing
tool_resultstring format, which could change across Claude Code versions - No completion tracking: The manifest records launches but not completions — a future hook could update status
- Disk accumulation: JSONL files grow indefinitely (mitigated by daily rotation)
Operational Discipline (Complementary)
This hook is the automated enforcement. The complementary operational discipline is:
Always log agent IDs and target file paths to the session log immediately after launch, before doing anything else.
Even without the hook, this pattern ensures session log entries survive compaction and are grep-able by future sessions.
Alternatives Considered
- Session log-only approach: Rely on manual session log entries. Rejected: too easy to forget under time pressure, and the session log itself can compact.
- Database approach: Write to
sessions.db. Rejected: over-engineered for append-only manifest data; JSONL is simpler and more portable. - Environment variable approach: Set env vars with agent IDs. Rejected: doesn't survive process boundaries.
Implementation
- Hook:
hooks/background-agent-manifest.py - Registration:
.claude/settings.local.jsonunderH.P.005-HOOKS.PostToolUsewith matcherTask - Dependencies: Python 3, stdlib only (json, pathlib, datetime)
Related
- ADR-155: Project-Scoped Session Logs (provides project detection)
- ADR-173: Structured Inter-Session Message Schema (bus-based coordination)
- Hook:
hooks/background-agent-manifest.py - Track: H (Framework)