Skip to main content

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:

  1. PostToolUse Hook - Triggers after Claude Code tool executions
  2. Background Watcher Daemon - Independent polling (backup when hooks fail)

Key Characteristics

SettingValueDescription
MIN_CONTEXT_PERCENT75%Trigger when context reaches this threshold
MAX_CONTEXT_PERCENT95%Don't trigger above this (too late)
CHECK_INTERVAL_SECONDS20sCheck frequency for responsive detection
COOLDOWN_MINUTES10Wait 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/
  1. session-exporter.py - Exports current session to text file
  2. exports-pending/ - Staging directory for unprocessed exports
  3. /cx - Processes exports and indexes to SQLite database
  4. 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

FilePathPurpose
Hook~/.coditect/hooks/context-threshold-monitor.pyPostToolUse hook (primary)
Daemon~/.coditect/scripts/context-watcher-daemon.pyBackground watcher (backup)
LaunchAgent~/Library/LaunchAgents/com.coditect.context-watcher.plistAuto-start config
Exporter~/.coditect/scripts/session-exporter.pyExport session to text
State~/PROJECTS/.coditect-data/context-storage/auto-export-state.jsonPersist export state
Daemon Log~/PROJECTS/.coditect-data/context-storage/context-watcher.logDaemon 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:

StateDisplay
2 pending─[💾:2 pending → run /cx]─
Just exported─[💾:✓ just exported]─
Exported 3m ago─[💾:✓ exported 3m ago]─
No pending(section hidden)

Workflow

Automatic Flow

  1. Work normally in Claude Code session
  2. Context reaches 75% threshold
  3. Hook detects threshold, triggers export
  4. Export saved to exports-pending/
  5. Statusline shows pending export reminder
  6. User runs /cx to process exports
  7. 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

  • 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