Tools: Claude Code Worktree Context Loss Is Real — Here Are 3 Workflows That Actually Help

Tools: Claude Code Worktree Context Loss Is Real — Here Are 3 Workflows That Actually Help

Claude Code Worktree Context Loss Is Real — Here Are 3 Workflows That Actually Help ## Why Worktrees Break Context Recovery ## Workflow 1: The Git Anchor Method ## Workflow 2: Worktree-Specific CLAUDE.md Files ## Workflow 3: Session Snapshot Scripts ## The Bigger Problem: Skill Discovery ## What Actually Matters If you've been following the Claude Code GitHub issues lately, you've probably seen #28769 pop up: --resume flag loses context when --worktree option was used. It has a bunch of upvotes and a long thread of people saying "yep, same here." I've been there. You spin up a worktree for a feature branch, do some solid work with Claude, then try to resume your session later — and Claude has no idea what you were doing. All that context about your architecture decisions, the edge cases you discussed, the specific approach you agreed on — gone. Here are three workflows I've settled on that actually help. Before getting into the workflows, it helps to understand what's happening. Claude Code sessions are tied to a working directory. When you use --worktree, you're operating in a different directory than your main checkout. The session state (compaction summaries, conversation history) lives relative to that path. When you try to --resume from your main directory, or switch between worktrees, Claude can't find the right session to pick up. This is a known issue — the GitHub thread on #28769 shows the core problem is that session identifiers don't account for the worktree path. Until there's a native fix, you have to work around it. The most reliable thing I've found is treating git commits as explicit context checkpoints — not just for code, but for session state. Before ending any meaningful Claude session, I run: The commit message carries the context. When I resume — in any worktree, on any machine — I read the last few commits and feed that to Claude: It's manual, but it's reliable. The commit log is your persistent memory layer. Claude Code reads CLAUDE.md files for project context. Most people have one at the repo root. The trick is: you can have one per worktree. Wait — that path won't work directly. Claude Code reads CLAUDE.md from your working directory. So the pattern that actually works is putting the file in the worktree root: Now every time Claude starts in that worktree, it reads your context file. You're rebuilding the session state automatically. This one takes a bit of setup but pays off fast if you jump between multiple worktrees. I have a small shell script called claude-snapshot: And a companion claude-restore that prints the latest snapshot for a given worktree: Run claude-snapshot before you stop work. Run claude-restore and paste the output to Claude when you resume. Takes about 30 seconds. There's another issue in the Claude Code tracker right now — #28782 about making Skills discoverable via a UI dropdown. This one is interesting because it points to a pattern: as these tools get more powerful, the tooling around them (session management, skill organization, context persistence) hasn't kept up. That's actually what drew me to Mantra. It's a tool built around exactly this gap — it handles the git anchor system automatically (Replay), gives you a unified console for managing Claude Code and other AI CLI tools (Control), and has session-aware context tracking. I use the manual workflows above when I'm on a machine without it, but Mantra handles a lot of this overhead when I'm in my normal setup. Worth a look if you're spending more time fighting session loss than writing code. The three workflows above — git anchor commits, worktree CLAUDE.md files, and session snapshots — address the same root problem: AI coding tools don't have persistent memory across sessions by default. You have to build that persistence yourself. Until the --resume + --worktree bug gets fixed natively, these are the practical options. The git anchor method is the one I'd start with — it has zero dependencies and your context is version-controlled alongside your code. If you've found other approaches that work, drop them in the comments. This is a problem enough people are hitting that I'd love to see more solutions floating around. Templates let you quickly answer FAQs or store snippets for re-use. Are you sure you want to ? It will become hidden in your post, but will still be visible via the comment's permalink. as well , this person and/or COMMAND_BLOCK: # Commit your actual work git add -A git commit -m "feat: implement user auth flow Session context: - Decided on JWT over sessions for stateless scaling - Using refresh token rotation (see auth/tokens.ts) - Left TODO: rate limiting on /api/auth/login - Next: add middleware to protected routes" COMMAND_BLOCK: # Commit your actual work git add -A git commit -m "feat: implement user auth flow Session context: - Decided on JWT over sessions for stateless scaling - Using refresh token rotation (see auth/tokens.ts) - Left TODO: rate limiting on /api/auth/login - Next: add middleware to protected routes" COMMAND_BLOCK: # Commit your actual work git add -A git commit -m "feat: implement user auth flow Session context: - Decided on JWT over sessions for stateless scaling - Using refresh token rotation (see auth/tokens.ts) - Left TODO: rate limiting on /api/auth/login - Next: add middleware to protected routes" COMMAND_BLOCK: # When resuming git log --oneline -5 # Then tell Claude: "Here's where we left off..." and paste the relevant commit messages COMMAND_BLOCK: # When resuming git log --oneline -5 # Then tell Claude: "Here's where we left off..." and paste the relevant commit messages COMMAND_BLOCK: # When resuming git log --oneline -5 # Then tell Claude: "Here's where we left off..." and paste the relevant commit messages COMMAND_BLOCK: # Create a worktree-specific context file cat > .git/worktrees/feature-auth/CLAUDE.md << 'EOF' # Feature: User Authentication (Branch: feature/auth) ## Current State Working on JWT implementation. Core logic done in auth/tokens.ts. ## Decisions Made - Chose HS256 for internal services (RS256 overkill for our scale) - Refresh tokens stored in httpOnly cookies - Access tokens: 15min expiry ## In Progress - [ ] Rate limiting middleware - [ ] Token revocation list - [ ] Integration tests for refresh flow ## Known Issues - Edge case with concurrent refresh requests not handled yet EOF COMMAND_BLOCK: # Create a worktree-specific context file cat > .git/worktrees/feature-auth/CLAUDE.md << 'EOF' # Feature: User Authentication (Branch: feature/auth) ## Current State Working on JWT implementation. Core logic done in auth/tokens.ts. ## Decisions Made - Chose HS256 for internal services (RS256 overkill for our scale) - Refresh tokens stored in httpOnly cookies - Access tokens: 15min expiry ## In Progress - [ ] Rate limiting middleware - [ ] Token revocation list - [ ] Integration tests for refresh flow ## Known Issues - Edge case with concurrent refresh requests not handled yet EOF COMMAND_BLOCK: # Create a worktree-specific context file cat > .git/worktrees/feature-auth/CLAUDE.md << 'EOF' # Feature: User Authentication (Branch: feature/auth) ## Current State Working on JWT implementation. Core logic done in auth/tokens.ts. ## Decisions Made - Chose HS256 for internal services (RS256 overkill for our scale) - Refresh tokens stored in httpOnly cookies - Access tokens: 15min expiry ## In Progress - [ ] Rate limiting middleware - [ ] Token revocation list - [ ] Integration tests for refresh flow ## Known Issues - Edge case with concurrent refresh requests not handled yet EOF COMMAND_BLOCK: # In your worktree directory echo "# Worktree Context: feature/auth ...your session notes..." > CLAUDE.md # Add to .gitignore so it doesn't pollute the branch echo "CLAUDE.md" >> .gitignore COMMAND_BLOCK: # In your worktree directory echo "# Worktree Context: feature/auth ...your session notes..." > CLAUDE.md # Add to .gitignore so it doesn't pollute the branch echo "CLAUDE.md" >> .gitignore COMMAND_BLOCK: # In your worktree directory echo "# Worktree Context: feature/auth ...your session notes..." > CLAUDE.md # Add to .gitignore so it doesn't pollute the branch echo "CLAUDE.md" >> .gitignore COMMAND_BLOCK: #!/bin/bash # claude-snapshot: Save session context before switching worktrees SNAPSHOT_DIR="$HOME/.claude-snapshots" WORKTREE_NAME=$(basename $(pwd)) SNAPSHOT_FILE="$SNAPSHOT_DIR/$WORKTREE_NAME-$(date +%Y%m%d-%H%M).md" mkdir -p "$SNAPSHOT_DIR" cat > "$SNAPSHOT_FILE" << EOF # Session Snapshot: $WORKTREE_NAME Date: $(date) Branch: $(git branch --show-current) Last commit: $(git log --oneline -1) ## Recent Changes $(git diff --stat HEAD~3 HEAD 2>/dev/null || echo "N/A") ## Session Notes EOF # Open editor for manual notes ${EDITOR:-nano} "$SNAPSHOT_FILE" echo "Snapshot saved to: $SNAPSHOT_FILE" COMMAND_BLOCK: #!/bin/bash # claude-snapshot: Save session context before switching worktrees SNAPSHOT_DIR="$HOME/.claude-snapshots" WORKTREE_NAME=$(basename $(pwd)) SNAPSHOT_FILE="$SNAPSHOT_DIR/$WORKTREE_NAME-$(date +%Y%m%d-%H%M).md" mkdir -p "$SNAPSHOT_DIR" cat > "$SNAPSHOT_FILE" << EOF # Session Snapshot: $WORKTREE_NAME Date: $(date) Branch: $(git branch --show-current) Last commit: $(git log --oneline -1) ## Recent Changes $(git diff --stat HEAD~3 HEAD 2>/dev/null || echo "N/A") ## Session Notes EOF # Open editor for manual notes ${EDITOR:-nano} "$SNAPSHOT_FILE" echo "Snapshot saved to: $SNAPSHOT_FILE" COMMAND_BLOCK: #!/bin/bash # claude-snapshot: Save session context before switching worktrees SNAPSHOT_DIR="$HOME/.claude-snapshots" WORKTREE_NAME=$(basename $(pwd)) SNAPSHOT_FILE="$SNAPSHOT_DIR/$WORKTREE_NAME-$(date +%Y%m%d-%H%M).md" mkdir -p "$SNAPSHOT_DIR" cat > "$SNAPSHOT_FILE" << EOF # Session Snapshot: $WORKTREE_NAME Date: $(date) Branch: $(git branch --show-current) Last commit: $(git log --oneline -1) ## Recent Changes $(git diff --stat HEAD~3 HEAD 2>/dev/null || echo "N/A") ## Session Notes EOF # Open editor for manual notes ${EDITOR:-nano} "$SNAPSHOT_FILE" echo "Snapshot saved to: $SNAPSHOT_FILE" COMMAND_BLOCK: #!/bin/bash # claude-restore: Print latest snapshot for current worktree SNAPSHOT_DIR="$HOME/.claude-snapshots" WORKTREE_NAME=$(basename $(pwd)) LATEST=$(ls -t "$SNAPSHOT_DIR/$WORKTREE_NAME"*.md 2>/dev/null | head -1) if [ -z "$LATEST" ]; then echo "No snapshot found for $WORKTREE_NAME" exit 1 fi echo "=== Latest snapshot: $LATEST ===" cat "$LATEST" COMMAND_BLOCK: #!/bin/bash # claude-restore: Print latest snapshot for current worktree SNAPSHOT_DIR="$HOME/.claude-snapshots" WORKTREE_NAME=$(basename $(pwd)) LATEST=$(ls -t "$SNAPSHOT_DIR/$WORKTREE_NAME"*.md 2>/dev/null | head -1) if [ -z "$LATEST" ]; then echo "No snapshot found for $WORKTREE_NAME" exit 1 fi echo "=== Latest snapshot: $LATEST ===" cat "$LATEST" COMMAND_BLOCK: #!/bin/bash # claude-restore: Print latest snapshot for current worktree SNAPSHOT_DIR="$HOME/.claude-snapshots" WORKTREE_NAME=$(basename $(pwd)) LATEST=$(ls -t "$SNAPSHOT_DIR/$WORKTREE_NAME"*.md 2>/dev/null | head -1) if [ -z "$LATEST" ]; then echo "No snapshot found for $WORKTREE_NAME" exit 1 fi echo "=== Latest snapshot: $LATEST ===" cat "$LATEST"