Auto Session Preservation Architecture
Overview
CODITECT includes a dual-mechanism auto-preservation system that automatically exports sessions when context usage reaches 75%. This provides a safety net ensuring work is preserved before context is exhausted.
Two mechanisms for reliability:
- PostToolUse Hook - Triggers after Claude Code tool executions
- Background Watcher Daemon - Independent polling (backup when hooks fail)
Key Characteristics
| Setting | Value | Description |
|---|---|---|
MIN_CONTEXT_PERCENT | 75% | Trigger when context reaches this threshold |
MAX_CONTEXT_PERCENT | 95% | Don't trigger above this (too late) |
CHECK_INTERVAL_SECONDS | 20s | Check frequency for responsive detection |
COOLDOWN_MINUTES | 10 | Wait time between exports |
Architecture
Hook-Based Trigger
The system uses a PostToolUse hook that runs after each tool execution:
┌─────────────────────────────────────────────────────────────────────┐
│ PostToolUse Hook Execution │
│ │ │
│ ▼ │
│ ┌─────────────────────┐ │
│ │ Throttle check │ │
│ │ (20s since last?) │ │
│ └──────────┬──────────┘ │
│ │ │
│ EXPIRED ───────┴─────── ACTIVE │
│ │ │ │
│ ▼ ▼ │
│ ┌─────────────────────┐ ┌─────────────────┐ │
│ │ Parse session JSONL │ │ Skip (throttled)│ │
│ │ Calculate context % │ └─────────────────┘ │
│ └──────────┬──────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────────┐ │
│ │ Context 75-95%? │ │
│ └──────────┬──────────┘ │
│ │ │
│ YES ─────┴───── NO │
│ │ │ │
│ ▼ ▼ │
│ ┌──────────────┐ ┌─────────────────┐ │
│ │ Check cooldown│ │ Update state │ │
│ │ (10 min) │ │ Skip export │ │
│ └──────┬───────┘ └─────────────────┘ │
│ │ │
│ EXPIRED ─── ACTIVE │
│ │ │ │
│ ▼ ▼ │
│ ┌────────────┐ ┌─────────────────┐ │
│ │ 1. /export │ │ Skip (cooldown) │ │
│ │ 2. Track │ └─────────────────┘ │
│ │ pending │ │
│ │ 3. Update │ │
│ │ state │ │
│ └────────────┘ │
└─────────────────────────────────────────────────────────────────────┘
Export Pipeline
session-exporter.py → exports-pending/ → /cx → exports-archive/
- session-exporter.py - Exports current session to text file
- exports-pending/ - Staging directory for unprocessed exports
- /cx - Processes exports and indexes to SQLite database
- exports-archive/ - Archive of processed exports
Configuration
Hook Registration
File: ~/.claude/settings.json
{
"hooks": {
"PostToolUse": [
{
"matcher": "Bash|Read|Write|Edit|Grep|Glob",
"hooks": [{
"type": "command",
"command": "~/.coditect/hooks/context-threshold-monitor.py",
"timeout": 5000
}]
}
]
}
}
Hook Configuration
File: ~/.coditect/hooks/context-threshold-monitor.py
# Configuration
MIN_CONTEXT_PERCENT = 75 # Start exporting when context hits 75%
MAX_CONTEXT_PERCENT = 95 # Stop if above 95% (too late, might fail)
CONTEXT_LIMIT_TOKENS = 200000 # Claude's context limit (approximate)
COOLDOWN_MINUTES = 10 # Don't re-export within cooldown
CHECK_INTERVAL_SECONDS = 20 # Only check context every N seconds
State Tracking
File: ~/PROJECTS/.coditect-data/context-storage/auto-export-state.json
{
"last_export": "2026-01-11T07:30:00Z",
"context_percent": 75,
"trigger": "auto",
"last_check_time": 1704960600.123,
"last_context_percent": 75.5,
"last_tokens": 150000,
"last_check": "2026-01-11T07:48:13Z",
"session_file": "e99331dd-50b2-4892-8ad2-bb84e10c2cce.jsonl",
"pending_exports": [
{
"filename": "2026-01-11-abc123.txt",
"exported_at": "2026-01-11T07:30:00Z",
"context_percent": 75.5,
"tokens": 150000
}
]
}
Background Watcher Daemon
The watcher daemon provides a reliable backup when Claude Code hooks fail to fire.
Why a Daemon?
Claude Code's PostToolUse hooks are unreliable - they don't fire consistently. The daemon runs independently, polling every 10 seconds to ensure exports happen.
Daemon Architecture
┌────────────────────────────────────────────────────────────────┐
│ Context Watcher Daemon │
│ (Independent Process) │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────┐ │
│ │ Poll every 10 seconds │ │
│ │ Find most recent session JSONL │ │
│ └──────────────┬──────────────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────┐ │
│ │ Session modified <5 min ago? │ │
│ └──────────────┬──────────────────┘ │
│ YES ───┴─── NO │
│ │ │ │
│ ▼ ▼ │
│ ┌────────────────────┐ ┌────────────────┐ │
│ │ Parse last 100KB │ │ Skip (inactive)│ │
│ │ Calculate context %│ └────────────────┘ │
│ └─────────┬──────────┘ │
│ │ │
│ ▼ │
│ ┌────────────────────┐ │
│ │ Context 75-95%? │ │
│ │ Not in cooldown? │ │
│ └─────────┬──────────┘ │
│ YES ───┴─── NO │
│ │ │ │
│ ▼ ▼ │
│ ┌──────────────┐ ┌────────────────┐ │
│ │ Trigger │ │ Update state │ │
│ │ session- │ │ Continue poll │ │
│ │ exporter.py │ └────────────────┘ │
│ └──────────────┘ │
└────────────────────────────────────────────────────────────────┘
Daemon Configuration
File: ~/.coditect/scripts/context-watcher-daemon.py
# Configuration
MIN_CONTEXT_PERCENT = 75 # Trigger export at 75%
MAX_CONTEXT_PERCENT = 95 # Don't trigger above 95% (too late)
CONTEXT_LIMIT_TOKENS = 200000
COOLDOWN_MINUTES = 10 # Cooldown between exports
POLL_INTERVAL_SECONDS = 10 # Check every 10 seconds
Starting the Daemon
Manual start:
nohup python3 ~/.coditect/scripts/context-watcher-daemon.py > /tmp/context-watcher.log 2>&1 &
Auto-start on login (launchd):
# The plist is installed at:
# ~/Library/LaunchAgents/com.coditect.context-watcher.plist
# Load/start
launchctl load ~/Library/LaunchAgents/com.coditect.context-watcher.plist
# Unload/stop
launchctl unload ~/Library/LaunchAgents/com.coditect.context-watcher.plist
# Check status
launchctl list | grep coditect
Daemon Management
# Check if running
pgrep -f context-watcher-daemon
# View logs
tail -f ~/PROJECTS/.coditect-data/context-storage/context-watcher.log
# Stop daemon (if started manually)
pkill -f context-watcher-daemon
# Check PID file
cat ~/PROJECTS/.coditect-data/context-storage/context-watcher.pid
Files
| File | Path | Purpose |
|---|---|---|
| Hook | ~/.coditect/hooks/context-threshold-monitor.py | PostToolUse hook (primary) |
| Daemon | ~/.coditect/scripts/context-watcher-daemon.py | Background watcher (backup) |
| LaunchAgent | ~/Library/LaunchAgents/com.coditect.context-watcher.plist | Auto-start config |
| Exporter | ~/.coditect/scripts/session-exporter.py | Export session to text |
| State | ~/PROJECTS/.coditect-data/context-storage/auto-export-state.json | Persist export state |
| Daemon Log | ~/PROJECTS/.coditect-data/context-storage/context-watcher.log | Daemon activity log |
| Pending | ~/PROJECTS/.coditect-data/context-storage/exports-pending/ | Unprocessed exports |
| Archive | ~/PROJECTS/.coditect-data/context-storage/exports-archive/ | Processed exports |
Statusline Integration
Pending exports are displayed in the statusline:
| State | Display |
|---|---|
| 2 pending | ─[💾:2 pending → run /cx]─ |
| Just exported | ─[💾:✓ just exported]─ |
| Exported 3m ago | ─[💾:✓ exported 3m ago]─ |
| No pending | (section hidden) |
Workflow
Automatic Flow
- Work normally in Claude Code session
- Context reaches 75% threshold
- Hook detects threshold, triggers export
- Export saved to
exports-pending/ - Statusline shows pending export reminder
- User runs
/cxto process exports - Exports archived, messages indexed
Manual Flow
# End of session workflow
/export # Export current session
/cx # Process exports + index to database
/cxq "verify saved" # Query to confirm
Troubleshooting
Export Not Triggering
# Check state file
cat ~/PROJECTS/.coditect-data/context-storage/auto-export-state.json | jq .
# Check context percentage
tail -1 ~/.claude/projects/-*/$(ls -t ~/.claude/projects/-*/*.jsonl | head -1 | xargs basename) | \
python3 -c "import sys,json; d=json.loads(sys.stdin.read()); u=d.get('message',{}).get('usage',{}); \
total=u.get('cache_read_input_tokens',0)+u.get('cache_creation_input_tokens',0)+u.get('input_tokens',0)+u.get('output_tokens',0); \
print(f'{total/2000:.1f}% context')"
# Test hook manually
echo '{}' | python3 ~/.coditect/hooks/context-threshold-monitor.py
Exports Not Processing
# Check pending exports
ls -la ~/PROJECTS/.coditect-data/context-storage/exports-pending/
# Process manually
/cx
Disable Auto-Preservation
# Option 1: Remove hook from settings.json
jq 'del(.hooks.PostToolUse[] | select(.hooks[].command | contains("context-threshold-monitor")))' \
~/.claude/settings.json | sponge ~/.claude/settings.json
# Option 2: Set threshold above 100% (effectively disable)
sed -i 's/MIN_CONTEXT_PERCENT = 75/MIN_CONTEXT_PERCENT = 101/' \
~/.coditect/hooks/context-threshold-monitor.py
Troubleshooting Daemon
Daemon Not Starting
# Check if already running
pgrep -f context-watcher-daemon
# Check launchd status
launchctl list | grep coditect
# Check launchd error log
cat ~/PROJECTS/.coditect-data/context-storage/context-watcher-stderr.log
# Try manual start to see errors
python3 ~/.coditect/scripts/context-watcher-daemon.py
Daemon Not Exporting
# Check daemon log
tail -20 ~/PROJECTS/.coditect-data/context-storage/context-watcher.log
# Check state file
cat ~/PROJECTS/.coditect-data/context-storage/auto-export-state.json | jq .
# Verify session is active (modified recently)
ls -lt ~/.claude/projects/-*/*.jsonl | head -1
Duplicate Exports
Both hook and daemon may trigger exports. The 10-minute cooldown prevents duplicates. Check trigger field in state:
"trigger": "auto"- From PostToolUse hook"trigger": "watcher"- From daemon
Related ADRs
- ADR-062: Statusline Configuration System (includes auto-preservation details)
- ADR-057: CODITECT Core Initial Setup
Version: 2.1.0 Last Updated: 2026-01-11 Author: CODITECT Team