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:

  1. Claude reads the beads prompt (injected via hooks)
  2. The daemon tries to start
  3. Fails with a mismatch error
  4. Claude tries bd sync
  5. Fails with a remote error
  6. You tell it “ignore that”
  7. 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:

  • TaskCreate with descriptions, activeForm for spinners, and metadata
  • TaskUpdate with dependencies (addBlocks, addBlockedBy)
  • TaskList and TaskGet for 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:

1
2
3
4
5
6
7
8
# Attempt 1: bash with string interpolation
# Result: broken JSON from parentheses and arrows

# Attempt 2: Python with urllib
# Result: 401 because op read doesn't evaluate inside Python

# Attempt 3: cry a little
# Result: cathartic but unproductive

And then I discovered linear CLI:

1
2
brew install schpet/tap/linear
linear auth login -k "$(op read 'op://FRR DEV/Linear/api-key')"

And creating an issue becomes:

1
2
3
4
5
6
linear issue create --team RST --no-interactive \
  -t "Port: agent/loop" \
  --project "Phase 1: Core Loop" \
  --priority 1 \
  -l port \
  -d "Port agent loop (476 LOC). Heart of the agent."

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:

NeedBeforeNow
Strategic visionLinear (MCP/API)Linear (linear CLI)
Tactical workBeadsLinear (linear CLI)
Session executionTasksTasks

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

1
2
rm -rf .beads/
git add -A && git commit -m "chore: remove beads"

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: