Reconstructs session logs from Claude Code transcripts when a session crashes or is closed before /save. Two entry points: - /recover <uuid|latest> : manual, Claude-reviewed reconstruction - detect_orphaned_sessions.py : scheduled scan that auto-builds logs for substantive, unsaved, not-yet-recovered transcripts (banner-marked RECOVERED-UNVERIFIED), commits them, and posts a #bot-alerts FYI. recover_session.py is the shared engine: Python extracts the verbatim command/config/reference timeline; Ollama drafts prose-only narrative. Machine-local ledger (.claude/state/) prevents reprocessing. Reviewed: git add scoped to own files, ledger written only after successful push, per-uuid idempotency, --max cap for unattended runs. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
77 lines
4.6 KiB
Markdown
77 lines
4.6 KiB
Markdown
# Session Recovery
|
|
|
|
Never lose work again when a Claude Code session crashes or is closed before `/save`.
|
|
|
|
Claude Code writes every session live to a transcript JSONL. This toolset distills those transcripts back into normal session logs in the `.claude/commands/save.md` format.
|
|
|
|
---
|
|
|
|
## The three pieces
|
|
|
|
| Piece | File | Role |
|
|
|---|---|---|
|
|
| Engine | `.claude/scripts/recover_session.py` | Parses one transcript, classifies it, and reconstructs a full session log. CLI: `--uuid` / `--latest` / `--path` with `--print` (default), `--auto`, or `--json`. |
|
|
| Detector | `.claude/scripts/detect_orphaned_sessions.py` | Scans all idle transcripts, auto-recovers the orphans (substantive + unsaved), updates the ledger, commits + pushes, and posts an FYI to `#bot-alerts`. CLI: `--dry-run`, `--idle-min N`, `--no-commit`, `--no-alert`. |
|
|
| Command | `.claude/commands/recover.md` | `/recover <uuid>` / `/recover latest` / `/recover --list` — the **manual, reviewed** path where Claude edits the draft before writing. |
|
|
|
|
The scheduled-task registration script `.claude/scripts/register-orphan-detector.ps1` wires the detector into the Windows Task Scheduler (Windows only).
|
|
|
|
---
|
|
|
|
## Where things live
|
|
|
|
- **Transcripts:** `~/.claude/projects/<slug>/<uuid>.jsonl`, where `<slug>` is the claudetools repo root with `/`, `\`, and `:` each replaced by `-`. On a `D:\claudetools` machine the slug is `D--claudetools`, so `C:\Users\<you>\.claude\projects\D--claudetools\*.jsonl`. The slug is computed portably from `claudetools_root` in `.claude/identity.json`. Sibling `<uuid>/` dirs hold subagent transcripts and are ignored for the main narrative.
|
|
- **Ledger:** `.claude/state/recovered-sessions.json` (machine-local, gitignored). Records every processed uuid with its verdict (`recovered` / `skipped-saved` / `skipped-trivial` / `error`) so it is never re-scanned. Transcripts are per-machine, so the ledger is too.
|
|
|
|
---
|
|
|
|
## How to run
|
|
|
|
```bash
|
|
# See candidate orphans without writing anything:
|
|
py .claude/scripts/detect_orphaned_sessions.py --dry-run
|
|
|
|
# Inspect one transcript's verdict as JSON (writes nothing):
|
|
py .claude/scripts/recover_session.py --json --uuid <uuid>
|
|
|
|
# Print a reconstructed log to stdout (writes nothing):
|
|
py .claude/scripts/recover_session.py --uuid <uuid> --print
|
|
|
|
# Full unattended run (writes logs, updates ledger, commits, pushes, alerts):
|
|
py .claude/scripts/detect_orphaned_sessions.py
|
|
```
|
|
|
|
### Register the scheduled task (Windows)
|
|
|
|
```powershell
|
|
powershell -ExecutionPolicy Bypass -File D:\claudetools\.claude\scripts\register-orphan-detector.ps1
|
|
```
|
|
|
|
Registers `ClaudeTools - Orphaned Session Detector`: runs at logon and every 4 hours. The 4-hour cadence pairs with the detector's 90-minute idle gate so an active session is never grabbed mid-flight.
|
|
|
|
---
|
|
|
|
## Accuracy split: Ollama prose vs Python verbatim
|
|
|
|
This is the core design principle.
|
|
|
|
- **Ollama drafts prose only** — Session Summary, Key Decisions, Problems Encountered, Pending / Incomplete Tasks. It never sees and never emits commands, IPs, credentials, file paths, commit SHAs, or ticket IDs. If Ollama is unreachable the log is still produced with a placeholder note in the prose sections.
|
|
- **Python extracts the verbatim evidence** — Configuration Changes (Write/Edit/NotebookEdit targets), Commands & Outputs (mutating Bash/PowerShell with truncated results), Reference Information (regex-extracted SHAs, URLs, IPs, ticket numbers, coord message ids), and Infrastructure & Servers. This is the high-value, accuracy-critical part and it comes straight from the transcript.
|
|
|
|
Trust the verbatim sections for facts; treat the prose as a draft.
|
|
|
|
---
|
|
|
|
## Classification
|
|
|
|
- **substantive** — the session did real work: a Write/Edit/NotebookEdit, a mutating Bash/PowerShell command (git commit/push/add, ssh, schtasks, New-Item, Set-Content, Remove-Item, Out-File, a POST/PUT/DELETE/PATCH curl, an `/api/` call, `vault.sh`, a mutating Invoke-RestMethod), or a mutating Skill (syncro, rmm, remediation-tool, mailbox, forum-post, syncro-emergency-billing).
|
|
- **saved** — the session was already saved: a save/scc/checkpoint Skill, or a Write/Edit into a `session-logs/` path.
|
|
- **orphan** = substantive AND not saved. Only orphans are auto-recovered.
|
|
- **scope** — client / project / general, decided by Python from the transcript text, `cwd`, and `gitBranch` against the known client and project slugs. Conservative: ambiguous resolves to `general`.
|
|
|
|
---
|
|
|
|
## Banner discipline
|
|
|
|
Auto-recovered logs are written with a `[RECOVERED -- UNVERIFIED]` banner. **The banner stays until a human reviews the log** and removes it. The manual `/recover` path lets Claude review and correct the draft before writing, and drops the banner once verified.
|