"We may compare a man in the process of computing a real number to a machine which is only capable of a finite number of conditions." — Alan Turing, "On Computable Numbers" (1936)
TURING is a Claude Code plugin that preserves session state across context compaction events, enabling cognitive continuity for long-running AI-assisted development sessions.
Version 1.1 — Priority-based selective restore, token budget tracking, auto decision extraction, state archiving, and open threads.
- Conceptual Foundation
- How It Works
- Features
- Architecture
- Tech Stack
- Hook System
- Session Discovery Algorithm
- File Formats
- Installation
- Usage
- Gotchas & Limitations
- Troubleshooting
- Development
TURING draws from Alan Turing's 1936 paper "On Computable Numbers, with an Application to the Entscheidungsproblem" — the foundational work that introduced:
| Turing Concept | TURING Plugin Equivalent |
|---|---|
| m-configurations | Session states (ACTIVE, COMPACTING, RESTORED, HALTED) |
| Infinite tape | Conversation context divided into sessions |
| Scanning head | Claude's current focus in the codebase |
| Standard Description (S.D.) | .claude/sessions/{session_id}/state.md |
| Description Number (D.N.) | ADR entries (ADR-0001, ADR-0002, etc.) |
Claude Code has finite context. When context fills up, it compacts — summarizing the conversation and losing detailed state. For long-running sessions, this means:
- Loss of architectural decisions and their rationale
- Forgotten file changes and their purposes
- Broken continuity in multi-step implementations
- Repeated work due to lost context
TURING automatically captures session state before compaction and restores it after, creating a persistent memory layer that survives context boundaries.
┌─────────────────────────────────────────────────────────────────┐
│ NORMAL OPERATION │
│ │
│ Claude Code Session (ACTIVE) │
│ ├── User requests │
│ ├── File modifications │
│ ├── Architectural decisions │
│ └── Context accumulates... │
└─────────────────────────────────────────────────────────────────┘
│
▼ Context limit approached
┌─────────────────────────────────────────────────────────────────┐
│ PreCompact HOOK FIRES │
│ │
│ capture-context.sh executes: │
│ ├── Reads session_id from hook input │
│ ├── Captures git state (branch, commits, changes) │
│ ├── Captures project identification │
│ ├── Saves transcript excerpt │
│ ├── Writes state.md (Standard Description) │
│ ├── Writes metadata.json (TTY, timestamps) │
│ ├── Updates index.json (session registry) │
│ └── Outputs state for Claude to see before compaction │
└─────────────────────────────────────────────────────────────────┘
│
▼ Context compacts
┌─────────────────────────────────────────────────────────────────┐
│ SessionStart HOOK FIRES │
│ (source: "compact") │
│ │
│ restore-context.sh executes: │
│ ├── Reads session_id from hook input │
│ ├── Loads state.md for current session │
│ ├── Displays restored state to Claude │
│ ├── Shows ADR summaries (TL;DR) │
│ └── Shows current git state for orientation │
└─────────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────┐
│ SESSION CONTINUES (RESTORED) │
│ │
│ Claude has context from: │
│ ├── Compaction summary (built-in) │
│ ├── TURING state.md (detailed state) │
│ ├── ADR history (architectural decisions) │
│ └── Git state (current codebase position) │
└─────────────────────────────────────────────────────────────────┘
Key capabilities for context efficiency and reliability.
State sections are tagged with priority levels to optimize token usage:
| Priority | Content | Included On |
|---|---|---|
| CRITICAL | Active focus, blockers | All restores |
| HIGH | Key decisions, next steps | All restores |
| MEDIUM | Modified files, project context | compact/resume |
| LOW | Session history, project info | Explicit request |
| ARCHIVE | Compressed previous states | Never auto-included |
Behavior by Source:
startup(new session): CRITICAL + HIGH only (~minimal tokens)compact/resume(continuity): CRITICAL + HIGH + MEDIUM (~moderate tokens)/turing-fullcommand: ALL priorities (~full context)
Every capture tracks token usage:
**Tokens**: ~2000 (state: 300, template: 1700)
What tokens represent: These are context window consumption estimates — how many tokens the restored TURING state will use in Claude's context when a session is restored. They are NOT API billing tokens.
┌─────────────────────────────────────────────┐
│ Claude Context Window │
├─────────────────────────────────────────────┤
│ TURING restored state (~300 tokens) │ ← Tracked here
│ System prompts & instructions │
│ Conversation history │
│ Code being discussed │
│ Available space for new work │
└─────────────────────────────────────────────┘
- Estimates tokens using ~4 chars/token heuristic
- Warns when context exceeds 2500 tokens (state getting bloated)
- Tracks token history across compactions in
metadata.json - Helps you understand how much context space TURING consumes on restore
Automatically extracts decisions from the conversation transcript:
### Auto-Extracted
- implemented TTY-based session discovery
- going with SQLite + sqlite-vec for storage
- decided to use priority levels for state filteringPatterns recognized:
- "decided to [action]"
- "going with [choice]"
- "will use [tool] because"
- "chose [option] over [alternative]"
- "implemented [feature]"
Track open work items across sessions via .claude/sessions/context.md:
## Threads
- [ ] Add rate limiting (2026-01-19)
- [ ] Review caching strategy (2026-01-19)- Max 5 open threads displayed on restore (~100 tokens)
- Completed
[x]threads auto-pruned - Session journal logged but never restored (write-only)
Previous states are automatically archived before overwrite:
.claude/sessions/{session_id}/
├── state.md # Current state
├── metadata.json # Session metadata
└── archive/
├── state-1702858200.md # Previous state 1
└── state-1702861800.md # Previous state 2
Key info from previous states is summarized in the ARCHIVE section.
State files now include machine-parseable YAML frontmatter:
---
version: 1.1
session_id: abc123-def456
tty: /dev/ttys001
captured_at: 2025-12-17T15:30:00Z
compaction_count: 3
trigger: auto
project: my-project
checksum: a1b2c3d4e5f6...
token_estimate: 850
---- MD5 checksum generated for state file integrity
- Validation status tracked (success, warning:small, error:missing)
- Checksum stored both in frontmatter and
.state-checksumfile
metadata.json now tracks:
{
"session_id": "abc123",
"created_at": "2025-12-17T10:00:00Z",
"last_compacted_at": "2025-12-17T15:30:00Z",
"tty": "/dev/ttys001",
"project_dir": "/path/to/project",
"compaction_count": 3,
"validation": {
"status": "success",
"state_bytes": 3400,
"state_lines": 85,
"checksum": "a1b2c3d4..."
},
"tokens": {
"state": 300,
"template": 1700,
"total": 2000,
"status": "ok"
},
"token_history": [
{"timestamp": "...", "tokens": 250, "compaction": 1},
{"timestamp": "...", "tokens": 280, "compaction": 2},
{"timestamp": "...", "tokens": 300, "compaction": 3}
],
"auto_decisions_extracted": 5
}project-root/
└── .claude/
└── sessions/
├── index.json # Session registry for fast lookup
├── context.md # Open threads + session journal
├── .latest # Backwards-compat marker
├── {session_id_1}/
│ ├── state.md # Standard Description (S.D.)
│ ├── adrs.md # Architecture Decision Records
│ ├── metadata.json # Session metadata
│ ├── .state-checksum # MD5 checksum for validation
│ └── archive/ # Previous state versions
│ ├── state-1702858200.md
│ └── state-1702861800.md
└── {session_id_2}/
├── state.md
├── adrs.md
├── metadata.json
└── .state-checksum
claude-code/plugins/turing/
├── .claude-plugin/
│ └── plugin.json # Plugin manifest
├── hooks/
│ └── hooks.json # Hook definitions
├── scripts/
│ ├── capture-context.sh # PreCompact handler
│ └── restore-context.sh # SessionStart handler
├── commands/
│ └── turing-save.md # Manual save command
└── templates/
└── turing-precompact.md # Protocol template
| Component | Purpose | Why This Choice |
|---|---|---|
| Bash | Script execution | Universal on macOS/Linux, no installation needed |
| Python 3 | JSON parsing | Pre-installed on macOS/Linux, more robust than jq |
| Git | State capture | Already present in development environments |
TURING deliberately avoids external dependencies:
- No jq — Python's
jsonmodule is more universal - No Node.js — Slower startup, not always present
- No compiled binaries — Maximum portability
| Operation | Typical Time | Notes |
|---|---|---|
| capture-context.sh | ~100-200ms | Dominated by git commands |
| restore-context.sh | ~50-100ms | Mostly file reads |
| Python JSON parsing | ~10-20ms | Negligible overhead |
TURING uses two Claude Code hook types:
Fires before context compaction occurs.
{
"PreCompact": [
{
"matcher": "auto",
"hooks": [{ "type": "command", "command": "..." }]
},
{
"matcher": "manual",
"hooks": [{ "type": "command", "command": "..." }]
}
]
}Matchers:
auto— Automatic compaction (context limit reached)manual— User-triggered via/compactcommand
Input (stdin JSON):
{
"session_id": "abc123-def456",
"transcript_path": "/path/to/transcript.jsonl",
"cwd": "/path/to/project",
"trigger": "auto"
}Fires when a session starts or resumes.
{
"SessionStart": [
{ "matcher": "startup", "hooks": [...] },
{ "matcher": "resume", "hooks": [...] },
{ "matcher": "compact", "hooks": [...] }
]
}Matchers:
startup— Freshclaudeinvocation (new session_id)resume—claude --resumeorclaude --continue(same session_id)compact— After context compaction (same session_id)
Input (stdin JSON):
{
"session_id": "abc123-def456",
"cwd": "/path/to/project",
"source": "compact"
}Hooks output to stdout. Everything printed becomes part of the initial context Claude sees.
echo "# [TURING] State Restored"
echo ""
cat "$STATE_FILE"Claude Code supports two hook types:
"type": "command"— Runs a shell command"type": "prompt"— Injects a prompt
However, prompt-type hooks only work for Stop and SubagentStop hooks, NOT for PreCompact. This is why TURING uses command hooks exclusively and outputs the protocol template via bash.
When a fresh session starts (source: "startup"), TURING must find the most relevant previous session. This is non-trivial with multiple concurrent terminals.
Terminal A (session abc123): Working on feature X
Terminal B (session def456): Working on bugfix Y
↓
Terminal A: /compact → .latest = abc123
Terminal B: /compact → .latest = def456 (overwrites!)
↓
Terminal A: exits, starts fresh session
→ reads .latest → gets def456's state ❌ WRONG!
Each session records its TTY (terminal device path):
{
"session_id": "abc123",
"tty": "/dev/ttys001",
"last_compacted_at": "2025-12-17T12:00:00Z"
}Discovery priority:
- Same TTY, most recent — If the current terminal matches a previous session's TTY, use that session
- Single recent session — If only one session exists within 24 hours, use it
- Most recent overall — Fall back to the most recently compacted session
.latestmarker — Backwards compatibility if index.json doesn't exist
# Priority 1: Same TTY, most recent
tty_matches = [s for s in sessions if s['tty'] == current_tty]
if tty_matches:
return max(tty_matches, key=lambda s: s['last_compacted_at'])
# Priority 2: Single recent session (within 24h)
recent = [s for s in sessions if is_within_24h(s['last_compacted_at'])]
if len(recent) == 1:
return recent[0]
# Priority 3: Most recent overall
return max(sessions, key=lambda s: s['last_compacted_at'])| Scenario | TTY Value |
|---|---|
| Interactive terminal | /dev/ttys001, /dev/pts/0, etc. |
| Piped input (CI/CD) | pipe |
| SSH session | /dev/pts/N |
| VS Code terminal | /dev/ttys00N |
Format with YAML frontmatter and priority tags:
---
version: 1.1
session_id: abc123-def456
tty: /dev/ttys001
captured_at: 2025-12-17T15:30:00Z
compaction_count: 2
trigger: auto
project: my-project
checksum: a1b2c3d4e5f6...
token_estimate: 850
---
<!-- PRIORITY: CRITICAL -->
## Active Focus
Previous focus: Implementing session-aware architecture
Previous decisions: Use TTY-based discovery; Add priority levels
_Focus will be set by Claude during compaction. Use /turing-save to manually set._
<!-- PRIORITY: HIGH -->
## Key Decisions (This Session)
### Auto-Extracted
- implemented TTY-based session discovery
- going with priority levels for state filtering
- decided to use YAML frontmatter
### Recorded ADRs (2)
ADR-0001: Use TypeScript strict mode
TL;DR: Enable strict mode to catch type errors at compile time
ADR-0002: Use React Query for data fetching
TL;DR: Simplify data fetching with caching and background updates
<!-- PRIORITY: MEDIUM -->
## Modified Files
- **Branch**: `<your-current-branch>`
- **Uncommitted**: N files
M path/to/modified-file.ext A path/to/staged-file.ext ?? path/to/untracked-file.ext
### Recent Commits
<!-- PRIORITY: LOW -->
## Project Context
- **Directory**: /Users/dev/project
- **Name**: my-project
- **Package**: @scope/my-project
- **CLAUDE.md**: Present
<!-- PRIORITY: ARCHIVE -->
## Session History
- **Compaction Count**: 2
- **Session Started**: 2025-12-17T10:00:00Z
- **Archived States**: 1
---
**M-configuration**: COMPACTING → HALTED
{
"session_id": "abc123-def456",
"created_at": "2025-12-17T10:00:00.000000Z",
"last_compacted_at": "2025-12-17T15:30:00.000000Z",
"tty": "/dev/ttys001",
"project_dir": "/Users/dev/project"
}{
"sessions": {
"abc123-def456": {
"last_compacted_at": "2025-12-17T15:30:00.000000Z",
"tty": "/dev/ttys001"
},
"xyz789-uvw012": {
"last_compacted_at": "2025-12-17T14:00:00.000000Z",
"tty": "/dev/ttys002"
}
},
"last_updated": "2025-12-17T15:30:00.000000Z"
}# Architecture Decision Records
# Session: abc123-def456
# Project: my-project
# Initialized: 12172025 10:00:00
================================================================================
ADR-0001: Use TypeScript strict mode
================================================================================
Date: 12172025 10:30:00
Status: Accepted
Session: abc123-def456
Confidence: High
TL;DR: Enable strict mode to catch type errors at compile time
## Context
The project had several runtime type errors...
## Decision
Enable `strict: true` in tsconfig.json...
## Alternatives Rejected
1. Keep loose mode — Rejected because: Too many runtime errors
## Consequences
### Positive
- Catch errors at compile time
### Trade-offs
- More verbose type annotations required# TURING Context
## Threads
- [ ] Add rate limiting (2026-01-19)
- [x] Implement auth flow (2026-01-18)
## Journal
| Date | Session | Files | Summary |
|------|---------|-------|---------|
| 2026-01-19 16:45 | abc123 | 3 | Added unit tests |- Threads: Restored on session start (max 5 open)
- Journal: Write-only historical log (never restored)
# Add the marketplace
/plugin marketplace add agenisea/turing
# Install the TURING plugin
/plugin install turing@agenisea-ai# Clone the repository
git clone https://github.com/agenisea/turing.git
# Install from local directory
/plugin marketplace add ./turing
/plugin install turing@agenisea-aiTURING works automatically once installed:
- Work normally — Make changes, discuss architecture, etc.
- Context compacts — TURING captures state automatically
- Continue working — State is restored seamlessly
Use /turing-save to manually preserve state and record ADRs:
/turing-save
Claude will:
- Analyze the current session
- Create/update
state.md - Record any architecturally significant decisions to
adrs.md - Output confirmation
Use /turing-status to see TURING's cognitive memory:
/turing-status # Project-level: sessions, tokens, ADRs
/turing-status --global # Workstation-level: all projects with TURING memoryProject-level output:
# TURING Memory Status
## Project: my-project
### Sessions Overview
| Session ID | TTY | Last Compacted | Compactions | Tokens |
|------------|-----|----------------|-------------|--------|
| `abc123...` | /dev/ttys001 | 2h ago | 3 | ~850 |
## Latest Session: abc123...
- **Token Usage**: state=300, template=1700, total=2000
- **Compaction Count**: 3
Workstation-level output:
# TURING Workstation Memory Status
## Projects with TURING Memory
| Project | Sessions | Tokens | Path |
|---------|----------|--------|------|
| my-project | 2 | ~5098 | `/path/to/my-project` |
## Workstation Summary
- **Total Projects**: 1
- **Total Sessions**: 2
- **Total Tokens**: ~5098
cat .claude/sessions/$(cat .claude/sessions/.latest)/state.mdcat .claude/sessions/$(cat .claude/sessions/.latest)/adrs.mdClaude Code caches plugins at session startup. If you modify the plugin:
# Changes won't take effect until you restart Claude Code
claude # Fresh session picks up changesWhen running Claude Code in a pipe (CI/CD, scripts), TTY is detected as pipe. All piped sessions share the same TTY identifier, which may cause session confusion.
Workaround: Use explicit session IDs in automated scenarios.
The ${CLAUDE_PLUGIN_ROOT} variable expands only in "type": "command" hooks, not in "type": "prompt" hooks.
// ✅ Works
{ "type": "command", "command": "${CLAUDE_PLUGIN_ROOT}/scripts/foo.sh" }
// ❌ Does NOT work
{ "type": "prompt", "prompt": "Read ${CLAUDE_PLUGIN_ROOT}/templates/bar.md" }Session IDs are generated by Claude Code and are opaque strings. Don't rely on their format — they may change between versions.
The transcript_path in hook input may be empty or point to a non-existent file in some scenarios. TURING handles this gracefully.
State capture includes git information. In non-git directories, git sections are omitted but the plugin still functions.
TURING requires Python 3 for JSON parsing. It's pre-installed on macOS and most Linux distributions. If missing:
# macOS
brew install python3
# Ubuntu/Debian
sudo apt install python3
# The script will output an error if Python 3 is not foundTURING uses bash scripts and Unix-specific commands (tty, etc.). Windows support would require PowerShell equivalents.
ADRs are only recorded when:
- You run
/turing-savemanually - Claude follows the TURING protocol during PreCompact (not guaranteed)
For critical decisions, explicitly use /turing-save.
Old sessions accumulate in .claude/sessions/. Periodically clean up:
# Remove sessions older than 7 days
find .claude/sessions -type d -mtime +7 -exec rm -rf {} +-
Check plugin is installed:
claude /plugins
-
Check hooks are registered:
cat ~/.claude/plugins/cache/*/hooks.json | grep -i turing
-
Test capture script manually:
echo '{"session_id":"test","trigger":"auto"}' | \ bash ./claude-code/plugins/turing/scripts/capture-context.sh
-
Check session directory exists:
ls -la .claude/sessions/
-
Check index.json:
cat .claude/sessions/index.json
-
Test restore script manually:
echo '{"session_id":"test","source":"startup"}' | \ bash ./claude-code/plugins/turing/scripts/restore-context.sh
Check TTY matching:
# Current TTY
tty
# TTYs in index
cat .claude/sessions/index.json | python3 -c "
import json, sys
data = json.load(sys.stdin)
for sid, info in data['sessions'].items():
print(f'{sid}: {info.get(\"tty\", \"unknown\")}')"Enable debug logging in the scripts:
# In capture-context.sh or restore-context.sh, uncomment:
# echo "$INPUT" > /tmp/turing-debug.jsoncd /path/to/turing
# Test capture
echo '{"session_id":"test-123","trigger":"auto","transcript_path":""}' | \
bash ./claude-code/plugins/turing/scripts/capture-context.sh
# Test restore (startup)
echo '{"session_id":"new-session","source":"startup"}' | \
bash ./claude-code/plugins/turing/scripts/restore-context.sh
# Test restore (compact)
echo '{"session_id":"test-123","source":"compact"}' | \
bash ./claude-code/plugins/turing/scripts/restore-context.shAdd to script:
# At the top of capture-context.sh or restore-context.sh
INPUT=$(cat)
echo "$INPUT" > /tmp/turing-hook-input.jsonThen check /tmp/turing-hook-input.json after a hook fires.
- Make changes to files in
claude-code/plugins/turing/ - Reinstall the plugin or clear the cache:
rm -rf ~/.claude/plugins/cache/turing* claude /install ./path/to/plugin
- Start a fresh Claude Code session
MIT License — see LICENSE
- Concept & Implementation: Patrick Peña
- Inspiration: Alan Turing's "On Computable Numbers" (1936)
- Platform: Claude Code by Anthropic
