How Task Metadata Saved My Git History: Keeping Claude Code in the Right Git Worktree

4 min read

One of the biggest boosts to my AI assisted development productivity has got to be the introduction of using git worktrees. With these it’s possible to run multiple Claude instances to work on multiple features at the same time whilst isolating their changes from each other until features are ready to be merged.

In my setup I use a custom Claude skill to manage this. The details of the skill aren’t important for this post, but essentially before any significant work, Claude creates a git worktree in a .worktrees directory in the root of the solution. When a feature is complete, it then manages the process of merging into dev and deleting the worktree and its folder.

Umbraco.AI
 └── .worktrees
       ├── feature-a
       └── feature-b

For the most part this setup has been rock solid — right up until it wasn’t.

Recently on a number of occasions Claude has lost track of the fact it was working in a feature branch / worktree and has mistakenly made updates directly to the root solution and committed them to dev.

Not ideal.

The Problem: Context Compression Amnesia

I noticed this happened mostly on long running implementations, particularly ones that would fill up the context window requiring context compression. When Claude compresses the context, it effectively summarizes what it deems to be important information, but it would appear that knowing which directory it was working in wasn’t something it felt worth remembering.

At that point it became clear this wasn’t a git issue — it was a memory model issue.

The question then became: how do we ensure this information survives compression? We couldn’t encode it in our CLAUDE.md file as the branch / worktree names change for every feature. We could write it to a file on disk, but this felt hacky and brittle.

The solution came in Claude Code 2.1.16 with the introduction of the new Tasks API which updated the old Todo API to one that persisted its tasks to disk, and key for me, allowed the addition of arbitrary metadata to be assigned to tasks.

The Fix: Phase 0 and Task Metadata

I incorporated this with a few choice instructions in my CLAUDE.local.md.

The first part was to instruct Claude that during any planning session for any significant feature it should always include a Phase 0 task in which it should utilize the git-worktree skill to initialize a git worktree. Importantly, when it creates the Task list item for this phase, I instruct it to embed the feature name and worktree path into the task list as metadata.

## Phase 0: Git Worktree Setup (MANDATORY)

For non-trivial work, **always** start with Phase 0 in your plan:

1. Call `/git-worktree` to create a worktree with a `feature/<name>` branch (e.g., `feature/add-streaming-support`)
2. Record the worktree in task metadata: `TaskCreate with metadata: {"worktree": "feature-add-streaming", "branch": "feature/add-streaming"}`
3. Do all implementation work in that worktree
4. Commit frequently using conventional commit format

Skip worktree setup only for tiny changes the user explicitly requests in the dev branch.

**Example Phase 0:**
```
Phase 0: Setup Development Environment
- Create worktree: /git-worktree → feature/add-chat-streaming
- Create task with worktree metadata
- All subsequent phases execute from this worktree
```

The second part is a set of working directory verification steps

### Working Directory Verification (CRITICAL — Run Before ANY Code Change)

**BEFORE every Read/Edit/Write/Bash on code (especially after compression):**

1. **Check the plan/task list** — if Phase 0 specifies a worktree/branch, you MUST use it.
2. **Verify location:**
```bash
   pwd && git branch --show-current
```
3. **If wrong directory**, navigate to the correct worktree:
```bash
   cd .worktrees/<feature-name>   # exact name from Phase 0
   pwd && git branch --show-current
```

Why This Works

The shift here wasn’t about trying to “remind Claude better” or adding more warnings in a markdown file. It was about moving something operationally critical out of conversational context and into durable state.

The active branch and worktree path aren’t just helpful bits of information — they’re foundational to the safety of the whole setup. If those get dropped during a compression cycle, you don’t just lose context, you lose isolation. And that’s when changes start quietly landing in dev.

By persisting the branch and worktree into task metadata, that information stops being something Claude has to remember and becomes something it can look up. It survives compression. It survives long-running sessions. It survives the natural entropy of large implementations.

Once that state is durable, the worktree name stops being an assumption and becomes data. Claude isn’t guessing where it should be operating — it checks the plan, reads the metadata, verifies the directory, and corrects itself if needed. It becomes procedural rather than hopeful.

Wrapping Up

The broader lesson for me is this: if something absolutely must not be forgotten, it cannot live only in context.

AI agents are designed to compress. They summarize aggressively. They optimize for what seems relevant in the moment. And it turns out that “current working directory” doesn’t always make the cut.

So when you start building real workflows around AI — especially ones that mutate source control — you need to promote critical operational details into durable, inspectable state. In my case, the Tasks API just happened to be the right primitive for that.

Git worktrees unlocked parallel feature development for me, but Task metadata made it safe.

Until next time 👋