Beads 故障恢复手册:从 Dolt 数据损坏到主键分叉的完整救援指南
【免费下载链接】beadsBeads - A memory upgrade for your coding agent项目地址: https://gitcode.com/GitHub_Trending/beads1/beads
导读:本文以 Beads 开源仓库的恢复运维文档(docs/RECOVERY.md 与 docs/recovery/ 下的全部 playbook)为核心骨架,系统梳理 Beads(
bdCLI)在 Dolt 存储层上可能遇到的各类故障——初始化安全拒绝、数据库损坏、合并冲突、循环依赖、同步失败、历史膨胀以及主键分叉(PK fork)等,并给出可直接复制的诊断命令、分步恢复流程与预防策略。读完本文,你将掌握bd doctor、bd bootstrap、bd export/import、dolt gc、bd admin reset等关键工具在真实故障场景下的正确用法,具备独立处理 Beads 仓库级事故的能力。
一、恢复手册的定位与整体结构
1.1 为什么存在这份手册
Beads 是一个为编码 Agent 提供"记忆升级"的开源项目,它将 issue、依赖关系等数据存储在本地 Dolt 数据库中(位于仓库下的.beads/目录),并通过 Dolt 的版本控制与远程同步能力实现团队协作。Dolt 作为类 Git 的 SQL 数据库,虽然提供了强大的分支、合并与历史能力,但也带来了 Git 模型特有的故障模式——合并冲突、历史膨胀、主键分叉等。
docs/RECOVERY.md是恢复 playbook 的入口桩文件。它的存在本身就有特殊的技术原因:
已发布的
bd二进制会把 URL 打印到这个文件——例如 v1.1.0 的 Dolt 合并拒绝指引(printAncestorPKMismatchGuidance,位于cmd/bd/dolt.go)会打印docs/RECOVERY.md#pk-fork-refused。这个桩文件确保这些打印出来的链接能落到指向真实 playbook 的页面上。
也就是说,当用户执行bd dolt pull或bd dolt push遇到主键分叉错误时,CLI 会在终端里直接打印指向本手册的恢复指引。从源码看,printAncestorPKMismatchGuidance在 cmd/bd/dolt.go 中定义,并在cmd/bd/dolt.go的多个 push/pull 错误处理路径(约 L566、L598、L680、L703)以及 cmd/bd/dolt_autopush.go、cmd/bd/sync.go 中被调用。
真正的 playbook 存放在 docs/recovery/ 目录下,共 9 个文件:
| 文件 | 主题 |
|---|---|
| index.md | 恢复总览与快速诊断 |
| init-safety.md | bd init/bd dolt push/pull拒绝(含主键分叉) |
| database-corruption.md | Dolt 数据库损坏 |
| merge-conflicts.md | 同步期间的合并冲突 |
| circular-dependencies.md | 依赖循环 |
| sync-failures.md | push/pull 同步失败 |
| history-squash.md | 历史膨胀(dolt gc 无法回收) |
| accidental-1-2-1-release.md | 意外发布的 v1.2.1 版本恢复 |
| uninstalling.md | 卸载与数据迁移 |
每个 runbook 都遵循统一的格式:症状(Symptoms)→ 诊断(Diagnosis)→ 解决方案(Solution,步骤不超过 5 步)→ 预防(Prevention),便于快速定位和执行。
1.2 快速诊断入口
在深入具体 runbook 之前,docs/recovery/index.md 给出了三条快速诊断命令:
# 检查 Beads 状态(大多数问题的第一站) bd status # 验证 Dolt 服务器是否在运行 bd doctor # 检查被阻塞的 issue bd blocked官方建议是:大多数问题都可以先用bd status诊断,然后才进入具体的 runbook。如果这些 runbook 未能解决问题,手册建议查阅 FAQ,或携带诊断输出在 issue 系统中提问。
二、bd init/bd dolt安全拒绝类故障
这是 Beads 中最具特色的故障类别。Beads 在bd init、bd dolt push、bd dolt pull中实现了命名式安全拒绝(named refusal),每种拒绝都有一个模式代码(如pk-fork-refused),错误信息会指向 docs/recovery/init-safety.md 中的对应锚点,并提供分步恢复指引。该文档的"新鲜度来源"标注为cmd/bd/init.go、cmd/bd/init_safety.go、cmd/bd/init_safety_test.go和cmd/bd/dolt.go,也就是说本文内容与源码错误路径一一对应。
init-safety 的底层设计依据可以参见 engdocs/adr/0002-init-safety-invariants.md(ADR 0002 ——bd init安全不变量)。本节覆盖四种命名拒绝。
2.1 init-force-refused —— 本地强制初始化被拒绝
退出码:10(ExitRemoteDivergenceRefused)
症状:
bd init refuses: remote 'origin' already has Dolt history (refs/dolt/data). Why: this init mode would create or reuse local history instead of adopting the remote. ...原因:bd init --force(或--reinit-local)让bd绕过本地数据安全守卫;bd init --from-jsonl则选择本地 JSONL 导出作为数据源。但此时远程仓库已经存在项目历史。如果继续执行,会创建一个与 origin 无共同祖先的孤儿本地 Dolt 分支。下一次bd dolt push要么失败(无共同祖先),更糟的情况下如果被强制推送(force-push),会摧毁团队数据。
恢复路径(按你的意图选择):
采纳远程历史(最常见):
bd bootstrap这条命令将远程 Dolt 数据库克隆到全新的本地
.beads/中,本地状态被忽略,团队历史成为你的历史。先诊断再决定:
bd doctor bd dolt statusbd doctor会遍历本地 + 远程状态并指出具体问题;bd dolt status展示 Dolt 层面视图。两者都只读,不会修改任何东西。故意覆盖远程历史(破坏性操作):这是影响所有协作者的跨边界操作。需要将本地源 init(
--reinit-local或--from-jsonl)与--discard-remote配合使用。交互模式下bd会提示确认;非交互模式下必须提供--destroy-token(格式见bd help init-safety)。执行bd init --reinit-local --discard-remote之后,下一次bd dolt push必须是替换历史的推送,务必先与团队协调。
2.2 init-token-missing —— 缺少销毁令牌
退出码:12(ExitDestroyTokenMissing)
症状:
bd init refuses: --discard-remote requires an explicit destroy-token in non-interactive mode.原因:你在非交互环境(CI、Agent、管道输入)中传入了--discard-remote。破坏性的跨边界操作不能被静默授权。
恢复路径:
改为交互式运行:在 TTY 中重新执行
bd init --reinit-local --discard-remote,确认时会提示你输入 destroy-token。显式提供令牌(CI/自动化场景):令牌格式为
DESTROY-<issue-prefix>。例如 issue 前缀为bd的项目:bd init --reinit-local --discard-remote --destroy-token=DESTROY-bd自动化脚本应从项目状态模板化生成令牌,不要从错误输出中解析。ADR 0002 的不变量 4 解释了为什么令牌永远不会出现在
bd的错误消息中——防止令牌泄露。
2.3 init-local-exists —— 本地数据已存在
退出码:11(ExitLocalExistsRefused)
症状:
Refusing to destroy N issues in non-interactive mode. See 'bd help init-safety' for the required --destroy-token format.或者交互模式下你拒绝了destroy N issues的确认输入。
原因:本地.beads/已有 issue 数据,bd init --reinit-local会永久销毁它们。
恢复路径:
先导出,再继续:
bd export > issue-export.jsonl bd init --reinit-localissue-export.jsonl允许你按需重新导入单个 issue。但它不是完整的数据库备份;当 Dolt 数据库足够健康、能创建可恢复备份时,应使用bd backup。先查原因:如果你并不期望在这里执行
bd init,先运行bd doctor——你可能遇到的是服务器配置问题,而 re-init 无法修复它。
2.4 pk-fork-refused —— 主键分叉(最重要)
症状:
$ bd dolt pull Error: ... cannot merge because table dependencies has different primary keys in its common ancestor(或没有in its common ancestor的变体)。bd会在错误后附带打印下方恢复配方的简版。
原因:被合并的两条历史对某张表的主键集合(primary key set)产生了分歧——而不是行内容分歧。Dolt 可以对行进行单元格级合并,但拒绝合并主键被两侧各自重塑(或共同祖先与两侧主键都不一致)的表。这种拒绝发生在任何行冲突显现之前,因此bd dolt pull的冲突自动解析器根本没有机会运行。重试永远不会有用:这两条历史已永久不可合并。
从源码看,CLI 打印的恢复指引与 playbook 完全一致:cmd/bd/dolt.go 中的printAncestorPKMismatchGuidance明确指出:
"This is a schema fork: two clones reshaped the table's primary key independently, usually by upgrading bd (and so running schema migrations) separately on each clone while un-synced changes existed on both sides. Retrying will not help — these histories can no longer be merged."
典型成因:两个克隆各自独立升级bd,跨越了会重塑主键的 schema 迁移版本,而两侧同时存在未同步的修改。具体案例是 #4259 事故——克隆跨越0041/0043/0050对dependencies表的主键重塑(v1.0.4 → v1.0.6),升级后第一次 pull 时恰好两侧都有未推送的依赖编辑,就触发了该错误。
需要说明的是,远程迁移预防门(v1.0.6+)的存在就是为了阻止这类问题产生:它拒绝自动迁移远程托管的数据库,并要求指定单一迁移者。本 playbook 针对的是分叉已经存在的情况。
恢复:从一个规范克隆引导(bootstrap)
分叉的历史无法合并,因此必须选择一侧作为规范(canonical),所有其他克隆从它重新克隆。issue数据通过 JSONL 导出/导入得以保留;被丢弃的只是非规范克隆上不可合并的 Dolt历史。
选择规范克隆:通常选择最完整/最近最活跃的克隆。在每个克隆上只读比较:
bd stats bd dolt status在规范克隆上:升级、迁移、强制推送:
bd version # 确认新的 bd 二进制 bd doctor # 发布前做健全性检查 bd dolt push --force # 让远程成为权威(
bd的迁移门可能会在这里拦截——这恰恰是门在询问的"指定迁移者"场景,请在规范克隆上遵循它打印的指引。)在其他每个克隆上:保存本地工作、重新克隆、重新应用:
bd export --all -o /tmp/beads-local.jsonl # 未同步工作的安全网 rm -rf .beads/dolt # 丢弃不可合并的历史 bd bootstrap # 从远程重新克隆 bd import /tmp/beads-local.jsonl # 重新应用本地工作bd import具有upsert 语义:只存在于本克隆的 issue 被重新创建,更新的本地编辑被应用,比远程已有数据更旧的行被跳过。完成后用bd stats抽查验证。
预防(跨主键重塑迁移的升级):
- 升级前先同步:在所有克隆仍运行旧版本时,先在每个克隆上执行
bd dolt push+bd dolt pull,然后停止编辑。新二进制安装后 push/pull 也会被门控,所以这一步必须先做。 - 指定单一迁移者:升级一台机器,让它完成迁移,然后
bd dolt push。 - 其他克隆"采纳"而不是"拉取":迁移者推送后,其他克隆各自升级二进制并运行
bd bootstrap采纳迁移后的数据库。克隆仍有待处理迁移时bd dolt pull会被拒绝,所以不要依赖它;上面"先同步"的步骤才是保留这些克隆工作的关键,因为bd bootstrap会替换本地数据库。
三、数据库损坏(Database Corruption)
当 Beads 遇到数据库层面的问题——命令报错、"database is locked" 错误持续存在、本应存在的 issue 缺失、数据库状态不一致——使用 docs/recovery/database-corruption.md 中的流程。
诊断:
# 检查数据库完整性 bd doctor # 检查 Dolt 服务器健康 bd dolt show解决方案(6 步):
# Step 1: 停止 Dolt 服务器 bd dolt stop # Step 2: 备份当前状态 cp -r .beads .beads.backup # Step 3: 预览 doctor 将要修复的内容(不实际修改) bd doctor --dry-run # Step 4: 重建数据库 bd doctor --fix # Step 5: 验证恢复 bd doctor bd list # Step 6: 重启 Dolt 服务器 dolt sql-server预防:
- 让 Dolt 服务器负责同步
- 系统关机前使用
bd dolt stop - 定期运行
bd doctor及早发现问题
bd doctor --dry-run是这里的关键设计:先预览再修复,避免--fix直接改动数据造成二次伤害。
四、合并冲突(Merge Conflicts)
当bd dolt pull以冲突错误失败、或克隆之间 issue 状态不一致时,使用 docs/recovery/merge-conflicts.md。
诊断:
bd doctor # 检查数据库健康 bd doctor --dry-run # 预览将要应用的修复解决方案(5 步):
# Step 1: 备份当前状态 cp -r .beads .beads.backup # Step 2: 检查冲突 bd doctor # Step 3: 修复以协调 bd doctor --fix # Step 4: 验证状态 bd list bd stats # Step 5: 推送已解决的状态 bd dolt push预防:
- 每次工作会话前后用
bd dolt pull/bd dolt push同步 - 避免在没有 Dolt 服务器运行的情况下从多个克隆并发修改
五、循环依赖(Circular Dependencies)
当出现 "circular dependency detected" 错误、bd blocked结果异常、本应就绪的 issue 显示被阻塞时,使用 docs/recovery/circular-dependencies.md。
诊断:
# 检查被阻塞的 issue bd blocked # 查看特定 issue 的依赖 bd show <issue-id> # 列出所有依赖 bd dep tree解决方案(5 步):
# Step 1: 识别循环 bd blocked --verbose # Step 2: 映射依赖链 bd show <issue-a> bd show <issue-b> # 沿着链条追踪直到回到 <issue-a> # Step 3: 决定移除哪个依赖 # 考虑:哪个依赖对工作流最不关键? # Step 4: 移除有问题的依赖 bd dep remove <dependent-issue> <blocking-issue> # Step 5: 验证循环已断开 bd blocked bd ready预防:
- 添加依赖时想的是 "X needs Y" 而不是 "X before Y"
- 添加依赖后用
bd blocked检查循环 - 尽可能保持依赖链浅层
循环依赖检测在底层由issueops/cycledetector.go与 backend/conformance/cycle_detector_contract.go 中的契约测试保障,bd dep remove正是打破环路的官方入口。
六、同步失败(Sync Failures)
当bd dolt push/bd dolt pull挂起或超时、出现网络相关错误、"failed to push/pull"、Dolt 服务器无响应时,使用 docs/recovery/sync-failures.md。
诊断:
# 检查 Dolt 服务器健康 bd doctor bd dolt show # 查看 Dolt 服务器日志(server 模式) tail -50 .beads/dolt-server.log解决方案(6 步):
# Step 1: 停止 Dolt 服务器 bd dolt stop # Step 2: 检查锁文件 ls -la .beads/*.lock # 确认 Dolt 服务器已完全停止后,移除陈旧锁 rm -f .beads/*.lock # Step 3: 备份并预览修复 cp -r .beads .beads.backup bd doctor --dry-run # Step 4: 需要时应用修复 bd doctor --fix # Step 5: 重启 Dolt 服务器 dolt sql-server # Step 6: 验证同步可用 bd dolt push bd doctor常见原因对照表:
| 原因 | 解决方案 |
|---|---|
| 网络超时 | 在更好的连接下重试 |
| 陈旧锁文件 | 停止 Dolt 服务器后移除锁 |
| 状态损坏 | 备份,然后bd doctor --fix |
| 合并冲突 | 参见合并冲突 playbook |
预防:
- 同步前确保网络稳定
- 让同步完成后再关闭终端
- 系统关机前使用
bd dolt stop
七、历史膨胀(History Bloat)与历史压缩
7.1 问题本质
这是 Beads 存储模型特有的问题。每一次 bead 写入都会产生一个 Dolt commit,而 Dolt 会保留可达 commit 引用的每一个字节——dolt gc只能回收没有任何引用的数据。一个积累了数月高频写入的工作区,其存储可能远超实际数据量(几千条 bead 占用数 GB 存储),而dolt gc却什么都回收不了,因为整条链仍被分支可达。runbook docs/recovery/history-squash.md 提供了把链条压缩为单个基线 commit 的完整方案。
重要警告:此流程会重写历史。数据库的每个其他克隆都将变得不可合并、必须重新克隆,远程和备份也必须重新指向。必须在围栏窗口(fenced window)内执行:所有同步该数据库的机器上的所有写入者全部停止,且先验证备份。
7.2 症状与诊断
症状:
- Dolt 数据目录(或其远程/备份)很大且持续增长,而
bd stats显示的 bead 数量并不多 dolt gc和dolt gc --full回收很少或什么都不回收- 克隆或拉取数据库的速度与内容量严重不成比例
诊断(在 Dolt 数据目录内执行——embedded 模式为.beads/embeddeddolt/<database>/,server 模式为.beads/dolt/<database>/):
# 存储有多大? du -sh . # 历史有多深?数千个 commit 加上很小的活数据集, # 意味着膨胀的是历史而非数据。 dolt log --oneline | wc -l # 确认 gc 没有不可达的东西可回收 dolt gc --full如果dolt gc --full释放了空间,那就结束了——不需要压缩。
7.3 解决方案(5 步)
Step 1:围栏并备份。停止所有写入者:Agent、后台服务,以及最容易遗漏的——每台同步该数据库的机器上定时推送/拉取的同步任务(cron、launchd、systemd)。逐一列出机器及其同步单元名称并确认已停止。一个仍在线的对端同步会在下一个 tick 撤销压缩:它拉取新基线、与旧链交叉合并、然后把整个旧历史推回新远程。server 模式下备份后还要停止服务器。备份是 Dolt 原生的、保留完整历史,因此它是你的回滚手段。
bd backup sync bd dolt stopStep 2:压缩为单个基线。从 Dolt 数据目录中,将当前树直接重新提交到根 commit 之上。保留根作为唯一祖先以维持合法链条——不要试图用孤儿分支进一步"简化":
root=$(dolt log --oneline | tail -1 | cut -d' ' -f1) dolt reset --soft "$root" dolt add -A dolt commit -m "history squash: baseline $(date +%F)"Step 3:丢弃其他 ref 并回收。任何仍指向旧链的东西都会让它存活——陈旧的本地分支和标签,以及远程跟踪 ref(每次 push/fetch 都会留下,长期同步的工作区必然有)。全部删除后再回收;Step 4 的 force-push 会在新链上重建远程跟踪 ref:
dolt branch # 删除陈旧分支: dolt branch -D <name> dolt branch -r # 删除远程跟踪 ref: dolt branch -rd <remote>/<branch> dolt tag # 删除陈旧标签: dolt tag -d <name> dolt gc --full du -sh . # 验证: 存储现在应仅为旧尺寸的零头如果尺寸几乎没变,说明仍有 ref 锚定旧链——重新检查dolt branch、dolt branch -r、dolt tag是否有幸存者并再次回收。(回收失败不会危及 Step 4——push 只发送新基线引用的内容——但本机在 gc 成功前会一直保留膨胀。)
Step 4:重新指向远程与备份。新历史与旧历史无关,因此首次发布必须替换:
bd dolt push --force bd backup remove && bd backup init <path> # 全新目标,然后: bd backup sync远程存储回收的细节(来自 runbook 的 Warning):Dolt 远程是单调累积 chunk 的——force-push 会把远程 ref 重新指向压缩后的链,但不删除任何东西,所以远程存储不会缩小。要同时回收发布侧,需要替换远程——push 前清空其存储(或选择全新路径/前缀):bd dolt remote remove <name>、bd dolt remote add <name> <fresh-url>,然后bd dolt push --force。压缩后每个克隆反正都要重新克隆,所以替换远程没有额外成本。
git 托管的远程(issue 数据搭载在代码远程的refs/dolt/data下)没有新路径可选——存储就是 git 仓库本身,其 manifest 把每个历史表文件都列为当前文件,所以 force-push 后完整旧存储仍可达。此时应就地替换数据平面:在 git 远程上删除 Dolt 数据 ref,然后 force-push 重建只包含活 chunk 的全新存储。代码分支不受影响。
git push origin :refs/dolt/data :refs/heads/__dolt_remote_info__ bd dolt push --forceStep 5:验证,然后其他地方重新克隆。本机执行:
bd doctor bd list -n 5该数据库的每个其他克隆都必须从压缩后的远程重新创建。旧克隆绝不能 pull:两条链仍共享根 commit,所以 pull 可能"成功"为一次重新锚定整个旧历史的交叉合并——之后的一次 push 就会在压缩后的远程上复活膨胀。
同步任务与写入者要作为独立的开关处理。每个机器的同步任务只有在该机器完成重新克隆(验证过,而非假设)后才重新启用,最后才解除写入者围栏;解除写入者绝不能隐式重启对端的同步任务。一个对端从旧克隆同步会把整个压缩前的存储重新上传到新远程。
超时陷阱:重新克隆后第一次 push 可能远大于常规同步。如果该机器的定时同步任务有超时限制,受限的 push 可能每个 tick 都中途死掉,在远程留下孤儿分片上传——重新克隆后立即手动执行一次bd dolt push(无超时),完成后再把控制权交还调度。
7.4 预防
高频协调状态存放在非版本化表中(claim 租约和 wisps),正是为了让常规 Agent 流量不产生历史——因此这种规模的膨胀通常意味着某个东西在紧凑循环中写入版本化表。找到并修复那个写入者,持续观察数据目录增长,并定期运行dolt gc,让不可达垃圾永远不会累积在可达历史之上。
八、意外版本发布的事故恢复(v1.2.1)
8.1 事故背景与影响判定
2026-08-11,v1.2.0 和 v1.2.1 未经发布测试被意外发布。v1.2.2 通过以更高版本号重新发布经过测试的 1.1 分支来取代它们——它是v1.1.2 的代码,因此所有安装渠道(Homebrew、npm、安装脚本、go install)都会前移到经过测试的代码上。1.2.x 专属特性(工作租约 work leases、事件日志 events journal、同步联邦、HTTP API 服务器、溯源事件)不在 v1.2.2 中,它们会在经过恰当测试的发布中回归。
关键陷阱:即使只运行过一次 v1.2.1 二进制(任何命令,包括bd list),也会把本地数据库 schema 从 v53 迁移到 v65。v1.2.2 二进制只认识 schema v53,因此在这样的数据库上会停止并报错:
schema version mismatch: database is at v65, binary knows up to v53 (12 migrations ahead)本 playbook(docs/recovery/accidental-1-2-1-release.md)用于修复此问题。v1.2.x 的 schema 变更严格是增量式的——1.1 分支读写的内容没有被删除、重命名或收窄——因此恢复是两分钟的元数据修复,而不是数据迁移。
是否受影响:只有同时满足"至少运行过一次 v1.2.1"且"用 v1.2.2(或任何 1.1.x 二进制)看到上述 schema 不匹配错误"。升级过但从未运行bd的用户,以及工作区配置了 Dolt 远程的用户(远程迁移门阻止了静默迁移),通常不受影响。
8.2 推荐修复:回滚 schema 游标
v1.2.x 的迁移被设计为游标回滚后可重放安全,因此本操作可逆,之后正常的、经过测试的 1.2.x 升级会正常工作。
先把每台机器和每个克隆都升级到 v1.2.2。残留的 v1.2.1 二进制一旦碰数据库就会静默重新迁移。
停止所有使用数据库的东西:关闭正在运行的
bd进程;server 模式下还要执行bd dolt stop。备份工作区数据库副本:
cp -a .beads .beads.backup-pre-recovery用 Dolt CLI 回滚游标(任意较新的
dolt版本即可;无需dolt config配置——命令自带作者信息)。数据库目录为.beads/embeddeddolt/<db>(embedded 模式,默认)或.beads/dolt/<db>(server 模式):cd .beads/embeddeddolt/<db> dolt sql -q "DELETE FROM schema_migrations WHERE version > 53; CALL DOLT_ADD('schema_migrations'); CALL DOLT_COMMIT('-m', 'recovery: roll schema cursor back to v53 (accidental v1.2.1)', '--author', 'bd recovery <recovery@beads.invalid>')"(如果报告没有可提交的内容,说明这步已完成——可以安全继续。)
在工作区运行任意
bd命令。应当无警告、无需BD_IGNORE_SCHEMA_SKEW正常工作。
这对 v1.2.1创建的数据库同样有效(不仅仅是升级的):v65 schema 是 1.1 分支所需一切的超集。
如果与队友共享 push/pull issue 数据,注意迁移后的游标会复制:要么恢复每个克隆,要么恢复一个后推送,让其他人拉取。
可选:恢复审计事件版本化。一个 v1.2.x 迁移把events审计表移出了 Dolt 的版本化平面,游标回滚后 1.1 分支会继续写审计事件但不做版本化/同步(其他一切正常同步)。如果你依赖版本化审计轨迹,在相同数据库目录下重新跟踪该表:
dolt sql -q "DELETE FROM dolt_ignore WHERE pattern = 'events'; CALL DOLT_ADD('-f', 'events'); CALL DOLT_COMMIT('-m', 'recovery: re-track events table', '--author', 'bd recovery <recovery@beads.invalid>')"8.3 应急通道与遗留物
恢复前的应急通道:如果立刻要用bd,schema 偏差守卫(skew guard)有逃生口:
BD_IGNORE_SCHEMA_SKEW=1 bd <command>该方案已针对 v53 二进制 / v65 数据库的精确组合验证:读操作一致、写操作正常,因为 v1.2.x 的 schema 增量对 1.1 分支不可见。审计事件版本化会暂停(见上文),直到完成游标回滚,所以这只是应急而非终点。
恢复后遗留的数据:意外版本写入 1.2.x 专属结构的数据会留在数据库中但被 1.1 分支闲置:工作租约状态(临时性,5 分钟窗口)、事件日志行(默认关闭的功能)、溯源行(仅由显式新命令写入)、storage_class标记。它们都不会阻塞未来的 1.2.x 升级,升级后会被继续使用。
备选方案:通过 Dolt 历史完整回滚:若想恢复数据库历史本身到迁移前状态(游标回滚会保留迁移 commit 在历史中),v1.2.1 迁移器为每个迁移做了一枚带标签的 Dolt commit(schema: apply migration 0054_...到0065_...),迁移前 commit 很容易找到。安全序列:用 v1.2.1 二进制导出(bd export --all -o backup.jsonl),停止一切并复制.beads到一边,在数据库目录执行dolt reset --hard <pre-migration-commit>,安装 v1.2.2,然后bd import backup.jsonl。注意事项:升级后删除的 issue 会回来(import 无法重新删除),v1.2.1 期间记录的审计事件会丢失。大多数用户应优先选择游标回滚。
九、卸载 Beads(Uninstalling)
uninstalling.md 覆盖两种场景:从仓库移除 Beads、从机器移除bd二进制。它回答了"如何干净地撤销 Beads 的安装痕迹"这一常见需求。
9.1 删除数据前的备份
删除.beads/会永久删除本地 Dolt 数据库。如果 issue 历史重要,先做 Dolt 原生备份:
bd backup init /path/to/beads-backup bd backup sync如需审查、迁移或互操作,也可以导出 issue 表:
bd export -o ~/beads-issues-$(date +%Y%m%d).jsonl注意:bd export不是可完整恢复的数据库备份,它不保留 Dolt 分支、commit 历史、工作集状态或非 issue 表。
9.2 仓库重置
在仓库根目录使用bd admin reset。默认先预览将要删除的内容:
bd admin reset预览无误后执行:
bd admin reset --force它会删除 Beads 管理的仓库数据,包括:.beads/目录、Beads 完整安装的 git hooks、.git/beads-worktrees/下的旧版同步 worktree。
关键细节:重置作用于整个 hook 文件而非段落。你自己的 hook 如果被 Beads 注入了段落,会被保留并报告——因为删除文件会连你的内容一起带走。这类 hook 要用bd hooks uninstall移除注入段落。
9.3 仅移除 hooks
想保留 issue 数据但移除 git hooks:
bd hooks uninstall这优于手动删除 hook 文件,因为 Beads 会保留其托管标记之外、与它无关的用户 hook 内容。
9.4 手动清理(兜底)
仅当bd admin reset不可用或无法在仓库内运行时才使用手动清理。先停止本地 Dolt 服务器(如果有):
bd dolt stop 2>/dev/null || trueHooks:先看再删。这一步故意没有批处理命令。pre-commit、prepare-commit-msg、post-merge、pre-push、post-checkout是标准 git hook 名,不是 Beads 保留名,任何一个都可能是你自己写的 hook。列出哪些存在以及 Beads 在里面留下了什么:
grep -l -e 'bd-hooks-version:' -e 'bd-shim' -e 'bd (beads)' -e 'BEGIN BEADS INTEGRATION' \ .git/hooks/pre-commit .git/hooks/prepare-commit-msg .git/hooks/post-merge \ .git/hooks/pre-push .git/hooks/post-checkout 2>/dev/null打开每个匹配的文件,根据内容决定:
- 整个内容都是 Beads 生成的文件带有
# bd-hooks-version:、# bd-shim或# bd (beads)行,除 shebang 外没有其他内容——删除它:rm -f .git/hooks/<name>。 - 你自己的文件带有
# --- BEGIN BEADS INTEGRATION ... ---块——编辑它:删除从BEGIN标记到END标记的行,保留其余部分。你围绕该块的注释也算数——"头部注释 + Beads 块"的 hook 仍是你自己的文件,应编辑而非删除。
命令未列出的 hooks 无论它们提到什么都归你所有。在注释中提及 Beads,或在你自编的 hook 中调用bd,都不会让文件变成 Beads 的。
其余清理:
# 移除本地 beads 数据库与配置。 rm -rf .beads # 移除旧版 beads 的同步分支 worktree。 rm -rf .git/beads-worktrees git worktree prune如果.gitattributes只包含 beads 的合并驱动配置,移除它;如果还有其他项目条目,只编辑掉 beads 那一行。如果残留 beads 专属 git 配置,移除:
git config --unset beads.role 2>/dev/null || true git config --unset core.hooksPath 2>/dev/null || true git config --unset merge.beads.driver 2>/dev/null || true git config --unset merge.beads.name 2>/dev/null || true不要跳过core.hooksPath:如果留着它,git 会继续寻找已不存在的 hooks 目录,Beads 的 post-checkout 导入可能会在旧前缀下重建.beads/工作区。但先检查值——core.hooksPath不是 Beads 专属。如果git config --get core.hooksPath报告的是另一个 hook 管理器的目录(例如 husky 的.husky/_)而不是.beads/hooks或.beads-hooks,就保持原样;取消设置会同时禁用那个工具的功能。bd doctor也应用相同规则,不会触碰它未设置的 hooks 路径。
9.5 移除bd二进制与验证
CLI 是独立二进制,按安装方式移除:
# Homebrew brew uninstall beads # Go install rm -f "$(which bd)" # 手动安装位置 rm -f /usr/local/bin/bd如果单独安装了 MCP 包,用当初的安装工具移除它。
验证移除:
which bd test ! -e .beads bd hooks list 2>/dev/null || true git config --get merge.beads.driver日后重装:
bd init十、恢复故障的共性方法论
纵览全部 9 个 playbook,可以提炼出 Beads 恢复操作的一致模式,这对读者建立事故处理直觉很有价值:
- 先只读诊断,后写操作:
bd status→bd doctor→bd dolt status/bd dolt show,全程只读。 - 预览优先:
bd doctor --dry-run展示将要修复的内容,确认后再bd doctor --fix。 - 先备份再动手:数据库类操作前
cp -r .beads .beads.backup;需要 Dolt 原生备份时用bd backup init+bd backup sync;需要跨机器迁移数据时用bd export+bd import(upsert 语义)。 - 停止所有写入者:尤其是历史压缩这类重写历史的操作,围栏必须覆盖每一台同步该数据库的机器,包括定时同步任务。
- 破坏性操作双因子确认:跨边界操作(覆盖远程、销毁本地数据)要求交互确认或
--destroy-token,非交互环境绝不能静默授权。 - 预防优先于救援:每个 playbook 都附有预防章节——定期
bd doctor、同步前后 push/pull、升级前指定单一迁移者、定期dolt gc。
这套方法论与 docs/recovery/index.md 中"每个 runbook 遵循统一格式:症状 → 诊断 → 解决方案(最多 5 步)→ 预防"的设计完全吻合,也是bd doctor在 cmd/bd/doctor.go、bd bootstrap在 cmd/bd/bootstrap.go、bd backup在 cmd/bd/backup.go 中各自承担职责的映射。
延伸阅读
- docs/recovery/index.md —— 恢复总览、快速诊断与常见问题索引
- docs/recovery/init-safety.md —— 初始化安全拒绝与主键分叉的完整 playbook
- docs/recovery/history-squash.md —— 历史压缩的完整操作细节与警告
- docs/recovery/accidental-1-2-1-release.md —— 意外版本发布的 schema 游标回滚
- docs/recovery/uninstalling.md —— 仓库与二进制的完整卸载流程
- engdocs/adr/0002-init-safety-invariants.md ——
bd init安全不变量的设计决策(ADR 0002) - 源码实现:cmd/bd/dolt.go(
printAncestorPKMismatchGuidance)、cmd/bd/doctor.go、cmd/bd/init.go、cmd/bd/init_safety.go、cmd/bd/init_safety_test.go - 底层能力:issue 操作角色与契约位于 issueops/ 与 backend/conformance/,如 issueops/cycledetector.go、backend/conformance/cycle_detector_contract.go
【免费下载链接】beadsBeads - A memory upgrade for your coding agent项目地址: https://gitcode.com/GitHub_Trending/beads1/beads
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考