The Bash Command /rewind Can't See
An engineer is four hours into a Claude Code session, refactoring a data pipeline. No commits yet - just autonomous iteration, with /rewind already used twice to back out of two wrong turns. Claude proposes a cleanup step before regenerating the build output: clear the stale build/ directory, then reorganize a couple of config files that got duplicated during the refactor. The engineer approves. Claude runs rm -rf build/ and mv config/settings.old.json config/settings.json through the Bash tool. The second command overwrites a settings file the engineer had hand-edited an hour earlier and never meant for Claude to touch.
The engineer opens /rewind, expecting the same safety net that already saved the session twice. Restore code and conversation. Nothing happens to the settings file. Claude Code's own documentation is explicit about why: checkpointing "does not track files modified by bash commands," and rm and mv are two of the three worked examples Anthropic's own docs use to explain the boundary. The file is gone. There is no redo. There is also no git commit to fall back to, because the engineer had been trusting /rewind to cover exactly this kind of moment.
This is not an edge case. It is the literal, documented shape of what checkpointing does not do, and it is happening to teams that treat Claude Code's autosave-like convenience as if it were version control.
Checkpointing vs. Git Commits: Two Recovery Tiers, Not One
Does Claude Code's checkpointing replace git? No - and the specific way it falls short is the entire subject of this article. Checkpointing and git commits are not the same safety net at two different granularities. They protect against two different failure classes, and the moment you treat one as a substitute for the other, you are carrying a gap you cannot see until you fall into it.
A /rewind checkpoint protects you against Claude's own bad edit, made through Claude's own file-editing tools, inside a session record that has not yet been pruned. A git commit protects you against everything else: Bash-tool side effects, manual edits made outside the session, a second concurrent session touching the same files, a session that never gets resumed, a session that outlives its retention window, and the thing nobody thinks about until they need it - the ability to cheaply bisect which change actually broke production.
In every fast-moving Claude Code rollout I've watched, teams drift toward the same unstated belief: checkpointing has made disciplined git hygiene optional during a session, because the tool already has you covered until you commit "at the end." That belief is wrong, and it is wrong in a way that is specifically dangerous because checkpointing exists at all. A tool that quietly and automatically does 70% of the job is more dangerous to your discipline than a tool that does none of it - the remaining 30% doesn't announce itself. You find it once, by falling through it, usually hours into a session with nothing durable behind you.
Why the Gap Widens as Sessions Get More Autonomous
This isn't a theoretical corner case that shrinks as the tooling matures. It grows with exactly the trends the rest of this series has been documenting.
Autonomous sessions run longer and touch the shell more. Part 6 of this series described the Spec-Plan-Auto-Verify Loop - the pattern of handing Claude a spec and letting it run in auto mode for extended stretches. Auto mode means more file-editing-tool calls, but it also means more Bash-tool calls: installs, test runners, database migrations, build steps. Every one of those is invisible to checkpointing by design. As sessions get more autonomous, the proportion of session activity that checkpointing can see goes down, not up.
Parallel sessions multiply the blind spot in a way that's easy to miss even if you already know checkpointing is session-scoped. Four Habits from the Creator of Claude Code covers running multiple Claude Code instances against separate git worktrees at once - and worktrees share one .git directory by design. A destructive Bash command in worktree A can touch objects, refs, or the index that worktree B depends on, and neither session's checkpoint history has any visibility into what the other is doing. Git is the only record that spans all of them, precisely because it's the one thing the worktrees were built to share.
There's a security dimension too, not just a convenience one. A Bash command that deletes or moves files runs with the same trust as everything else in an autonomous session - no separate confirmation gate, no audit trail beyond the shell history. For anything tracked in the repository, checkpointing gives you nothing to inspect after the fact; git gives you a diff. That's the same blind spot, just with a worse payload - but it stops at the repository boundary. An .env file or a mounted secret is almost always gitignored, which means neither tier helps: it was never checkpointed, and git status --porcelain won't show it either, so the commit-boundary hook in the next section will happily let a destructive command through against it. Protecting ignored paths needs a different control - a path-aware denylist, or the coarser Bash gate this series calls the Write Funnel - not either recovery tier.
And the failure mode is asymmetric in cost. A bad edit that checkpointing does catch costs you a /rewind and a re-prompt - seconds. A Bash-tool side effect that checkpointing doesn't catch costs you the actual work: hours of refactoring, a hand-edited config, a file nobody thought to back up because the safety net was supposedly already running. Hooks: The Enforcement Layer That Turns Agent Policy Into Agent Fact already established the relevant threshold for this series: if violating a rule produces a consequence you cannot recover from in production, it belongs behind an enforced gate, not a habit. An unrecoverable file loss mid-session clears that bar. This article is about building the gate.
The Wrong Way: Relying on /rewind Instead of Git Commits
The default failure pattern looks disciplined from the outside. The engineer has a plan. They open a session, describe the task, and let Claude iterate - editing files, running tests, occasionally backing out a wrong turn with /rewind. Nothing looks risky, because nothing is risky, right up until it is.
09:14 Session starts. No commit. Working tree already has uncommitted changes from yesterday.09:20 Claude edits three files implementing a rate limiter. [checkpoint 1]09:35 Tests fail. /rewind to checkpoint 1, re-prompt with a fix. [checkpoint 2]09:48 Claude edits the rate limiter again, tests pass. [checkpoint 3]10:10 Claude runs `npm install express-rate-limit` via Bash. [not checkpointed]10:22 Claude runs `mv src/limiter/ src/rate-limiter/` to rename the module. [not checkpointed]10:40 Claude edits the API route to use the new limiter. [checkpoint 4]11:05 Claude runs `rm -rf build/ && mv config/settings.old.json config/settings.json` to "clean up." [not checkpointed]11:06 Engineer notices the settings file is wrong. Opens /rewind.11:07 /rewind restores checkpoint 4's code and conversation. The settings file stays overwritten - it was never in scope.The engineer used /rewind correctly, for exactly what it's good at - backing out of a bad code direction mid-conversation. What made the session fail wasn't recklessness, it was structural: there was no git commit anywhere in the two hours (plus a dirty tree inherited from the day before), so when a Bash-tool side effect landed outside the checkpoint's coverage, there was no durable point to fall back to. The safety net that had worked twice already created the confidence that let the third, uncovered failure go unguarded.
The Right Way: Git Commit Discipline for Claude Code Sessions
Disciplined commit practice on top of Claude Code has one organizing rule: commit at decision boundaries, not at task completion. A decision boundary is any point where the state of the working tree is something you'd want back even if everything after it goes wrong.
Commit before the session starts. This establishes a rollback point that exists independently of the session entirely - not tied to a conversation record, not subject to any retention window.
git add -A && git commit -m "chore: baseline before rate-limiter session"Commit at completed sub-units of work, not when the whole task is done. "The rate limiter logic passes its unit tests" is a sub-unit. "The whole feature is shipped" is not - by the time you get there, you may have passed through several states worth protecting individually.
Use /rewind for what it's good at, and stop there. In-session iteration, backing out of a direction Claude took that didn't pan out, cheap re-prompting - that's the checkpoint's job, and it does it well. It is not a substitute for a commit, because it cannot see anything the Bash tool touches and it does not outlive a pruned or unresumed session.
Treat any Bash-tool step as a mandatory commit boundary. This is the one rule worth enforcing rather than remembering, because it's precisely the boundary checkpointing cannot see. The pattern below follows the same shape as the hooks in Skills vs Hooks in Claude Code: a consequence you can't recover from belongs in a gate, not a reminder.
#!/usr/bin/env bash# .claude/hooks/require-commit-before-destructive-bash.sh# PreToolUse hook: block destructive Bash commands if the working tree# has uncommitted changes. Checkpointing never sees these commands land -# git is the only thing that can undo them.input=$(cat)tool_name=$(echo "$input" | jq -r '.tool_name')command=$(echo "$input" | jq -r '.tool_input.command // ""')if [[ "$tool_name" != "Bash" ]]; then exit 0fiif ! git rev-parse --git-dir >/dev/null 2>&1; then exit 0 # not a git repo - this gate has nothing to check againstfidestructive_pattern='rm -rf|rm -r |rm [^-]|mv [^&]*|git reset --hard|git clean -fd|git checkout -- 'if ! echo "$command" | grep -qE "$destructive_pattern"; then exit 0fiif [[ -n "$(git status --porcelain)" ]]; then echo "Uncommitted changes exist and the next command is destructive: $command" >&2 echo "Commit first: git add -A && git commit -m 'checkpoint before destructive step'" >&2 exit 2fiexit 0Registered as a PreToolUse hook, this doesn't stop Claude from running rm or mv - both are legitimate, common operations. It stops Claude from running them while the only recovery path is a checkpoint that is blind to them. Exit code 2 blocks the tool call and returns the stderr message to Claude, which will typically commit and retry on its own. The same session from above, with the hook in place:
09:14 Session starts. git commit -m "chore: baseline before rate-limiter session". [git: baseline]09:20 Claude edits three files implementing a rate limiter. [checkpoint 1]09:35 Tests fail. /rewind to checkpoint 1, re-prompt with a fix. [checkpoint 2]09:48 Claude edits the rate limiter again, tests pass. [checkpoint 3]10:09 git commit -m "feat: rate limiter passes unit tests". [git: sub-unit 1]10:10 Claude runs `npm install express-rate-limit` via Bash. [not checkpointed, not destructive - hook allows]10:22 Hook blocks `mv src/limiter/ src/rate-limiter/` (matches destructive pattern, dirty tree). Claude commits, then re-runs it. [git: sub-unit 2]10:40 Claude edits the API route to use the new limiter. [checkpoint 4]11:05 Hook blocks `rm -rf build/ && mv config/...`. Claude commits first, then the cleanup runs against a committed tree. [git: sub-unit 3]11:06 Cleanup runs. Settings file changes, but it's in a commit - `git diff HEAD~1` shows exactly what moved.The checkpoint timeline and the git log are two independent tracks now, each doing the job it's actually good at, and the moment a Bash command could have caused unrecoverable loss, the hook forced a durable point to exist first.
Checkpoint Complacency: Naming the Erosion
Call this Checkpoint Complacency: the tendency to reduce deliberate recovery practice - committing to git - because an automatic, session-scoped safety net creates a false sense of comprehensive coverage.
The mechanism is specific, not just "people get lazy." Checkpointing is automatic and invisible; it happens on every prompt without asking anything of you. Git commits are deliberate and visible; they require you to stop and decide. Anything automatic that appears to do part of a job will get credit in your head for the whole job, especially when the part it doesn't cover never announces itself until the moment it costs you. You don't feel the gap while you're safe inside it. You feel it exactly once, at the worst possible time.
This is not a claim that checkpointing is poorly designed. Anthropic's own documentation draws the line correctly and explicitly: "Checkpoints are designed for quick, session-level recovery... Checkpoints complement but don't replace proper version control... Think of checkpoints as 'local undo' and Git as 'permanent history.'" The tool vendor is not confused about the boundary. The complacency is a property of how teams use a good tool, not a flaw in the tool itself. Name it precisely and it becomes a predictable behavioral failure mode you can catch in review, not a bug report you file after the fact.
The naming isn't from nowhere, either. Human-factors research on aviation and cockpit automation has documented "automation-induced complacency" for decades: operators trust a highly reliable automated system enough to reduce their own monitoring, and the failure surfaces only when the automation hits a case it wasn't built to catch. Checkpoint Complacency is that same mechanism, specific to the boundary Claude Code's checkpointing actually has - the aviation research names the general human tendency; this article names the specific, documented edge that trips it in an agentic coding session.
Tiering recovery by durability isn't a new idea in systems engineering - distributed training infrastructure has used two-level checkpoint recovery for fault tolerance for years, pairing fast local checkpoints with periodic transfer to durable remote storage. What's specific to agentic coding sessions is the behavioral failure this article names: teams under-committing because the fast, automatic tier already feels like coverage for the durable one, right up until a Bash command proves otherwise.
To be precise about what's genuinely new here versus what practitioners have already worked out: the checkpoint-as-local-undo, git-as-permanent-history split is not a novel observation - Anthropic states it directly in its own docs, and independent write-ups have converged on the same framing. What hasn't been established is the mechanism (an automation-complacency effect, not "people being careless"), a name precise enough to catch in code review ("that's Checkpoint Complacency" is a sentence a reviewer can actually say), and - in the next two sections - a concrete enforcement pattern that makes the boundary a property of the harness instead of a property of the engineer's memory. The taxonomy was already known. The behavioral failure mode it produces, and the hook that narrows it, were not.
What Claude Code Checkpointing Actually Covers (and Its 30-Day Retention Window)
The precise boundary matters more than the general intuition, because "checkpointing covers most things" is exactly the belief that produces Checkpoint Complacency in the first place. Anthropic shipped checkpointing and /rewind in Claude Code v2.0.0 (September 29, 2025), describing it as one of the most-requested features going into that release - which is worth sitting with, because it means the tool causing the complacency is also a tool teams specifically asked for and are genuinely satisfied with. That combination, not carelessness, is what makes the failure mode durable. Here is what each tier actually covers, as of Claude Code's documented behavior:
What /rewind tracks. Claude Code creates a checkpoint on every user prompt, and it tracks changes made through Claude's file-editing tools - Edit, Write, and NotebookEdit. That's the entire surface. If Claude touched a file through one of those three tools, /rewind can put it back.
What /rewind explicitly does not track. The documentation is unambiguous, and gives the same two examples this article opened with: commands run through the Bash tool - rm file.txt, mv old.txt new.txt, cp source.txt dest.txt - are invisible to checkpointing. "These file modifications cannot be undone through rewind." The same applies to any manual edit you make outside Claude Code, and to edits from a second concurrent session touching the same files - checkpointing "only tracks files that have been edited within the current session."
Where the checkpoint itself lives, and how long it lasts. Checkpoints are not purely a live-memory feature that vanishes the instant you close a terminal - Anthropic's own docs state they persist to disk and remain accessible if you resume the same conversation with --continue or --resume, and that they're "automatically cleaned up along with sessions after 30 days (configurable)." The setting name behind that window, cleanupPeriodDays, along with the detail that cleanup runs at startup and is irreversible - raising the window afterward doesn't resurrect what was already pruned - comes from independent testing of the tool rather than the official docs' own wording, so treat that specific mechanism as well-corroborated, not as an Anthropic-documented guarantee. Either way, the honest description of checkpoint durability isn't "gone the moment you close the terminal" - it's tied to a session record that can be pruned, or simply never resumed, and that record was never going to help you with a Bash side effect regardless.
What git covers, independent of any of that. A commit doesn't care which tool made the change, whether the session that made it still exists, whether you ever resume that conversation again, or whether 30 days have passed. It is the one artifact in this entire stack that is not scoped to a Claude Code session at all - which is exactly why it's the only thing that can recover a rm -rf run at 11:05 on a session nobody will reopen after 11:06.
This reframes the earlier engineering habits established elsewhere in this series rather than replacing them. Part 6 correctly describes /rewind as a context-hygiene habit - "resetting the session state to a checkpoint where Claude was still on the right track, and giving it a different direction from that point." That's still the right way to think about it. This article's contribution is the other half: the recoverability threshold from Hooks: The Enforcement Layer That Turns Agent Policy Into Agent Fact and the Enforceability Axis from Skills vs Hooks in Claude Code both point at the same underlying question this article answers concretely for git: does this failure cost more than the friction of preventing it? For an unrecoverable Bash-tool side effect, the answer is yes, and the fix belongs in a hook, not a habit.
Decision Guide: Which Layer Actually Recovers This
This mirrors the same diagnostic instinct as Which Claude Code Layer Solves Your Problem? (Part 5) - narrowed to one specific question: given a failure, which recovery mechanism was ever positioned to catch it.
flowchart TD
A[Claude takes an action] --> B{Which tool executed it?}
B -->|Edit, Write, or NotebookEdit| C[Tracked by the checkpoint]
B -->|Bash: rm, mv, npm install, migrations| D[Never tracked by any checkpoint]
B -->|Manual edit outside the session, or a second concurrent session| D
C --> E{Is the session record still alive?}
E -->|Resumed within the retention window, same conversation| F[/rewind restores it/]
E -->|Pruned, never resumed, or past cleanupPeriodDays| G[Checkpoint history is gone too]
D --> H[Only git covers this: log, reflog, checkout]
G --> H
F --> I[Session-scoped recovery worked]
H --> J[Durable recovery worked]
style A fill:#95A5A6,color:#2C2C2A
style B fill:#7B68EE,color:#FFFFFF
style C fill:#6BCF7F,color:#2C2C2A
style D fill:#E74C3C,color:#FFFFFF
style E fill:#7B68EE,color:#FFFFFF
style F fill:#6BCF7F,color:#2C2C2A
style G fill:#E74C3C,color:#FFFFFF
style H fill:#4A90E2,color:#FFFFFF
style I fill:#6BCF7F,color:#2C2C2A
style J fill:#4A90E2,color:#FFFFFF
Three of the four branches through this diagram end at "only git covers this" - the Bash branch, the manual/concurrent-session branch, and the checkpoint-but-pruned branch. Only one branch reaches a working /rewind. That ratio is the entire argument in one picture: checkpointing covers a real but narrow slice, and it is the slice that already fails safe (a bad edit, re-prompted). Everything outside it fails destructively, and only git was ever positioned to catch it.
What This Pattern Doesn't Solve
Commit discipline is a workflow practice, not a tooling guarantee. Overselling it just moves the false sense of safety from /rewind to a hook script - here's exactly where this pattern stops.
Over-committing has a real review cost. The rule is "commit at decision boundaries," not "commit every file save." A commit history that records every intermediate Claude edit is noisy enough that nobody, including future-you, will want to read it. Squash or rebase before merging if the granular history was only useful for session-level safety.
Checkpoint retention isn't the only expiring safety net - habits decay too. Even with the hook in place, require-commit-before-destructive-bash.sh only catches commands matching its destructive pattern. A comprehensive denylist is a losing game against shell syntax; the hook narrows the blast radius, it doesn't eliminate it. Pair it with a coarser Bash gate if your risk tolerance demands it, following the Write Funnel pattern from Skills vs Hooks in Claude Code - and verify the hook actually fires before you trust it, the same way How to Know Your Claude Code Setup Actually Works (Part 8) argues you should test any other layer of your setup.
Nothing here enforces commit quality. The hook forces a commit to exist before a destructive step; it says nothing about whether that commit has a useful message, a sensible scope, or code that actually builds. That remains a human and code-review problem, same as it always was.
Neither tier recovers state outside the working tree. A database migration, a call to an external API, a message published to a queue - none of that is a file, so neither /rewind nor a git commit touches it. Committing your migration script beforehand protects the script. It does nothing for the column it backfilled. If a Bash-tool step mutates state outside the filesystem, commit discipline narrows what you're exposed to; it doesn't close the exposure the way it does for a rm or an mv.
This is a workflow discipline layered on top of a tool, not a feature Anthropic ships. If you don't wire up the hook, nothing stops an engineer from going back to committing "at the end." The hook makes the boundary cheap to enforce; it doesn't make enforcement automatic without someone deciding to install it.
The Claude Code Commit Discipline Checklist
- Commit before you open the session. Not after the first prompt - before it. This is your rollback point that exists independently of anything that happens next.
- Commit at sub-units of work, not at task completion. A passing test, a working migration, a reviewed diff - each is a decision boundary worth a commit, well before the overall task is "done."
- Use
/rewindfor in-session iteration only. It's the right tool for backing out of a wrong direction mid-conversation. It is never the right tool for anything that happened through the Bash tool. - Gate destructive Bash commands on a clean commit, don't just remember to check. A
PreToolUsehook that blocksrm -rf,mv,git reset --hard, and similar patterns against a dirty working tree turns "I should have committed first" into "the session physically could not proceed until it did." - Treat checkpoint retention as a countdown, not a safety net. If a session matters enough to revisit in a month, it needs a commit today -
cleanupPeriodDayswill not wait for you to remember.
References
- Anthropic. (2025). Checkpointing - Claude Code Docs. code.claude.com/docs/en/checkpointing
- Anthropic. (2025, September 29). Enabling Claude Code to work more autonomously. anthropic.com/news/enabling-claude-code-to-work-more-autonomously
- Classmethod / DevelopersIO. (2026). Storage location and retention period (cleanupPeriodDays) of Claude Code conversation history. dev.classmethod.jp
- yurukusa. (2026). Claude Code recovery-first field guide [Gist]. gist.github.com/yurukusa
- GitButler. Agent-safe Git with GitButler. blog.gitbutler.com/agentic-safety
- InventiveHQ. How to Recover an Accidentally Closed Claude Code Session. inventivehq.com
- Guo, et al. Two-Level Incremental Checkpoint Recovery Scheme for Reducing System Total Overheads. PLOS ONE. ncbi.nlm.nih.gov/pmc/articles/PMC4128665
- Automation-Induced Complacency Potential: Development and Validation of a New Scale. NCBI/PMC. ncbi.nlm.nih.gov/pmc/articles/PMC6389673
- Part 4 of this series: Hooks: The Enforcement Layer That Turns Agent Policy Into Agent Fact
- Part 5 of this series: Which Claude Code Layer Solves Your Problem? A Diagnostic Guide for AI Engineers
- Part 6 of this series: Four Habits from the Creator of Claude Code That Will Change How You Ship
- Part 8 of this series: How to Know Your Claude Code Setup Actually Works
- Part 9 of this series: Skills vs Hooks in Claude Code: Enforceability Is the Design Variable
Related Articles
- You Can't Debug What You Can't See: Observability for Claude Code Sessions
- Subagents: How to Run Parallelism Inside a Single Agent Session Without Poisoning the Parent
- Agent Skills Are Not Prompts. They Are Production Knowledge Infrastructure.
- Which Claude Code Layer Solves Your Problem? A Diagnostic Guide for AI Engineers