不到一个月前,我写了一篇完整的文章来解释如何在 Claude Code 中使用三层记忆系统:Linear 负责战略,Beads 负责战术,Tasks 负责执行。一个漂亮而优雅的金字塔。

然而现实打脸了。

今天我正式退役 Beads。不是心血来潮,而是因为现实已经充分证明:一个给你带来的麻烦比它解决的还多的工具,不叫工具,叫累赘。

Beads 带来了什么

对于没读过原文的人来说,Beads 是一个基于 git 的 issue 追踪器。它是 Claude Code 的插件,把 issues 以 JSONL 文件的形式存储在你的仓库里。这个想法在纸面上很漂亮:

  • Git 持久化:issues 保存在 .beads/ 目录中,和你的代码一起提交。
  • 依赖关系:一个 issue 可以阻塞另一个。
  • 离线可用:不需要网络连接。
  • LLM 直接可见:不需要 API,不需要配置。代理读取文件就行了。

它的承诺是:在"这周想做什么"(Linear)和"现在正在做什么"(Tasks)之间提供一个中间层。战术层面的粘合剂。

哪里出了问题

一切都很好,直到不再好了。而且不好的方式还挺有创意。

来自地狱的守护进程

Beads 在后台运行一个守护进程来管理 SQLite 数据库并与 git 同步。听起来很合理。但实际上:

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!

每次启动会话都会看到这条消息。守护进程崩溃,同步崩溃,然后你就陷入了一种量子态:issues 存在于你的本地 SQLite 中但不在 git 里,或者反过来。你的 issues 同时存在又不存在。

幽灵同步

bd sync 是用来将 issues 与 git 远程仓库同步的命令。除了它不工作的时候:

→ 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

原来 beads 会从 git 获取远程仓库地址,但如果你有多个 remote(很常见),它可能会选错。如果那个 remote 指向一个不存在或已改名的仓库,守护进程就会在每次操作时吐出错误。悄无声息地,你的 issues 停止同步,等你打开下一个会话发现一切都消失了才意识到出了问题。

认知成本

每次 Claude Code 会话都是这样开始的:

  1. Claude 读取 beads 的提示词(通过 hooks 注入)
  2. 守护进程尝试启动
  3. 因为 mismatch 错误而失败
  4. Claude 尝试 bd sync
  5. 因为 remote 错误而失败
  6. 你告诉它"忽略这个"
  7. 现在才能开始干活

六步摩擦,才能做任何有用的事情。六步消耗上下文、时间和耐心。

发生了什么变化

两件事让 Beads 从"有 bug 但有用的工具"变成了"不必要的开销":

1. Tasks 已经成熟

当我写那篇三层系统的文章时,Tasks 还很基础。现在它有了:

  • TaskCreate:支持描述、activeForm spinner 和元数据
  • TaskUpdate:支持依赖关系(addBlocksaddBlockedBy
  • TaskListTaskGet:用于查看检查
  • 通过 CLAUDE_CODE_TASK_LIST_ID 实现跨会话持久化

说人话:Tasks 现在能做 Beads 在会话内工作中做的一切。而且不需要守护进程,不需要 SQLite,不需要 git 同步,也没有幽灵错误。

2. Linear CLI 来了

Linear 的 MCP 说好听点就是一坨。时断时续,速度慢,还特别喜欢在你最需要它的时候掉链子。

替代方案是直接用 GraphQL 调 API。能用,是的,直到你试图在 issue 描述里放特殊字符:

1
2
3
4
5
6
7
8
# 尝试 1:bash 字符串插值
# 结果:括号和箭头把 JSON 搞坏了

# 尝试 2:Python urllib
# 结果:401,因为 op read 在 Python 里面不会被执行

# 尝试 3:哭一会儿
# 结果:有宣泄效果但无生产力

然后我发现了 linear CLI

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

创建一个 issue 变成了:

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."

不需要 GraphQL 转义。没有守护进程。没有损坏的同步。我用一个 bash 脚本在不到一分钟内创建了 49 个 issues。用 Linear MCP 或 API 的话,光是和字符转义斗争就得一个半小时。

新的金字塔(已经不是金字塔了)

三层模型很好看。但现实是两层就够了:

需求之前现在
战略视野Linear(MCP/API)Linear(linear CLI)
战术工作BeadsLinear(linear CLI)
会话内执行TasksTasks

Linear + 它的 CLI 覆盖了战略战术。Tasks 覆盖执行。Beads 没有覆盖任何其他两个做不到更好的东西。

之前:
Linear(周)→ Beads(天)→ Tasks(小时)

现在:
Linear(周/天)→ Tasks(小时)

更少的层次,更少的摩擦,更少可能出问题的东西。

经验教训

这让我想起一句常说的话:不必要的复杂性是无声的敌人。Beads 解决了一个真实的问题(LLM 在会话之间的失忆症),但它通过增加一层基础设施来实现,而这层基础设施长期来看制造的问题比它解决的还多。

工程领域永远是这个老故事:正确的解决方案有时不是添加东西,而是意识到你已有的东西已经改进到不再需要它了。

Linear MCP 是垃圾 –> CLI 来了。 Tasks 很基础 –> 它成熟了。 Beads 被夹在中间,无处安放。

终极 rm -rf

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

两行命令。这就是退役一个工具的方式。没有仪式,没有戏剧。

Beads 在有用的时候是有用的。现在不再有用了。没关系。


TL;DR:从我的 Claude Code 工作流中退役 Beads。Linear CLI(schpet/linear-cli)解决了 issue 管理的问题,不需要忍受 MCP 的痛苦或 GraphQL API 的折磨。Tasks 已经足够成熟来覆盖会话内的跟踪。两层就够了:Linear 负责战略和战术,Tasks 负责执行。

相关的早期文章