Less than a month ago I wrote an entire post explaining how to use three memory layers with Claude Code: Linear for strategy, Beads for tactics, and Tasks for execution. A nice, elegant pyramid.
Yeah, no.
Today I’m retiring Beads. Not on a whim, but because reality has made it abundantly clear that a tool causing more problems than it solves isn’t a tool. It’s dead weight.
What Beads Brought to the Table
For those who didn’t read the original post, Beads was a git-backed issue tracker. A Claude Code plugin that stored issues in JSONL files inside your repo. Brilliant idea on paper:
- Git persistence: issues lived in
.beads/and got committed with your code. - Dependencies: one issue could block another.
- Offline: worked without an internet connection.
- The LLM could see it directly: no APIs, no configuration. The agent read the files and that was it.
The promise: an intermediate layer between “what I want to do this week” (Linear) and “what I’m doing right now” (Tasks). The tactical glue.
What Went Wrong
Everything was fine until it wasn’t. And it stopped being fine in creative ways.
The Daemon From Hell
Beads runs a background daemon to manage the SQLite database and sync with git. Sounds reasonable. In practice:
DATABASE MISMATCH DETECTED!
This database belongs to a different repository:
Database repo ID: d1f9ca0c
Current repo ID: 01eac8ea
⚠️ CRITICAL: This mismatch can cause beads to incorrectly
delete issues during sync!
That message greets you when starting any session. The daemon fails, syncing fails, and you’re left with issues that exist in your local SQLite but not in git, or vice versa. A quantum state of bugs: your issues both exist and don’t exist at the same time.
The Ghost Sync
bd sync is the command to synchronize your issues with the git remote. Except when it doesn’t work:
→ Pulling from remote...
Error: pulling: git pull failed: exit status 1
remote: Repository not found
fatal: repository 'https://git.frr.dev/frr/wuwei.git/' not found
Turns out beads grabs the remote from git, but if you have multiple remotes (common enough), it might pick the wrong one. And if that remote points to a repo that doesn’t exist or has been renamed, the daemon spits errors on every operation. Silently, your issues stop syncing and you don’t realize it until you open another session and everything has vanished.
The Cognitive Cost
Every Claude Code session started like this:
- Claude reads the beads prompt (injected via hooks)
- The daemon tries to start
- Fails with a mismatch error
- Claude tries
bd sync - Fails with a remote error
- You tell it “ignore that”
- Now we can actually work
Six steps of friction before doing anything productive. Six steps consuming context, time, and patience.
What Changed
Two things turned Beads from “useful tool with bugs” into “unnecessary overhead”:
1. Tasks Has Matured
When I wrote the three-layer post, Tasks was basic. Now it has:
TaskCreatewith descriptions, activeForm for spinners, and metadataTaskUpdatewith dependencies (addBlocks,addBlockedBy)TaskListandTaskGetfor inspection- Optional persistence between sessions with
CLAUDE_CODE_TASK_LIST_ID
In plain English: Tasks now does everything Beads did for intra-session work. And it does it without a daemon, without SQLite, without git sync, and without ghost errors.
2. The Linear CLI Has Arrived
The Linear MCP was, being generous, crap. Intermittent, slow, and with a special talent for failing right when you needed it most.
The alternative was hitting the API raw with GraphQL. Which works, sure, until you try to put special characters in issue descriptions:
| |
And then I discovered linear CLI:
| |
And creating an issue becomes:
| |
No GraphQL escaping. No daemon. No broken sync. I created 49 issues in under a minute with a bash script. With the Linear MCP or the API, it would have taken an hour and a half of fighting character escaping.
The New Pyramid (That’s No Longer a Pyramid)
The three-layer model was pretty. But reality is that two layers are enough:
| Need | Before | Now |
|---|---|---|
| Strategic vision | Linear (MCP/API) | Linear (linear CLI) |
| Tactical work | Beads | Linear (linear CLI) |
| Session execution | Tasks | Tasks |
Linear + its CLI covers strategy and tactics. Tasks covers execution. Beads doesn’t cover anything that the other two don’t do better.
Before:
Linear (weeks) → Beads (days) → Tasks (hours)
Now:
Linear (weeks/days) → Tasks (hours)
Fewer layers, less friction, fewer things that can break.
Lesson Learned
This reminds me of something I always say: unnecessary complexity is the silent enemy. Beads solved a real problem (LLM amnesia between sessions), but it did so by adding an infrastructure layer that, over time, generated more problems than it solved.
It’s the same old story in engineering: the right solution sometimes isn’t adding something, but realizing that what you already have has improved enough that you no longer need it.
The Linear MCP was crap –> the CLI arrived. Tasks was basic –> it matured. Beads was left in the middle, with no place.
The Final rm -rf
| |
Two lines. That’s how you retire a tool. No ceremony, no drama.
Beads was useful while it lasted. Now it’s not. And that’s fine.
TL;DR: I’m retiring Beads from my Claude Code workflow. The Linear CLI (schpet/linear-cli) handles issue management without the pain of the MCP or the GraphQL API. Tasks has matured enough to cover intra-session tracking. Two layers are enough: Linear for strategy and tactics, Tasks for execution.
Previous posts on this topic: