ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

Beads `bd init` 安全不变量(ADR 0002):从 `--force` 误用事故到可执行的安全扼流点设计

2026/9/12 16:14:50 拓冰建站 浏览量
Beads `bd init` 安全不变量(ADR 0002):从 `--force` 误用事故到可执行的安全扼流点设计 Beadsbd init安全不变量ADR 0002从--force误用事故到可执行的安全扼流点设计【免费下载链接】beadsBeads - A memory upgrade for your coding agent项目地址: https://gitcode.com/GitHub_Trending/beads1/beadsBeads 是一个为编码 Agent 提供记忆升级memory upgrade的仓库管理工具其bd init负责在工作区初始化.beads/数据仓库。本文围绕仓库中的架构决策记录 engdocs/adr/0002-init-safety-invariants.md 展开讲解一次真实数据事故bd-q83bd init --force静默跳过远程引导导致孤儿 Dolt 历史覆盖团队远端如何沉淀为五条硬性安全不变量以及它们在 cmd/bd/init_safety.go、cmd/bd/init.go 和 cmd/bd/init_safety_test.go 中的落地实现。读完本文你将掌握bd init的完整 flag 语义、稳定的退出码契约、--discard-remote与 destroy-token 的授权流程以及一套错误文本不回显破坏性命令的 Agent 防御设计思路。事故背景一次静默跳过的初始化如何摧毁 247 个 issue故障链条在修复前bd init --force在 origin 上已经存在refs/dolt/data引用时会静默跳过从远端引导bootstrap-from-remote路径旧实现位于cmd/bd/init.go:511随后仍然把 origin 接线为 Dolt 远程init.go:643-650。接下来第一次写入会自动 push 一份孤儿 Dolt 历史远端会以no common ancestor拒绝合并。此时用户能做的恢复手段全部是破坏性的要么 force-push 覆盖整个团队的远端要么rm -rf .beads/dolt丢弃本地数据。工具自身的警告文本甚至已经把这类失败识别为众所周知的陷阱well-known footgun。事故编号为 bd-q83来自本地 beads 跟踪器合并了 qa-engineer F1 与 historian F3 在council-2026-04-22-beads-resilience-audit中的发现完整决策日志记录在~/.claude/councils/2026-04-24-bd-q83-init-force-safety/log.mdADR 文档自述外部环境路径。历史模式八个提交各补一个洞却从未编码底层不变量ADR 通过git log对cmd/bd/init.go做历史模式分析列出至少八个提交各自修补了这类问题的一个表面却都没有把底层不变量固化下来提交修补的表面58f5989bf误导性错误消息导致用户丢失 247 个 issue本事故的前身af12d6f72server→embedded 迁移的导出顺序9db6b56f2v0.63→v1.0 schema 回填的多语句 ALTER63c7a2601shared-server 模式下project_id的采用401da9df7通过bd doctor --fix回填project_id3a4840169引入--force作为安全绕过的覆写开关12dc4e075引入--destroy-token因为--force--quiet绕过了确认83b0f099b移除--quiet对安全守卫的绕过每个提交都只为一个数据源本地 JSONL、本地 DB 文件、shared-server 的_project_id、远端refs/dolt/data加了守卫但--force标志活在全局作用域里——除非作者记得特判它否则每个未来的守卫都隐式继承了--force作为绕过开关。3a4840169引入的就是这个形状flag 的语义是绕过那个安全守卫单数但代码写出的形态却对每一个未来的守卫都生效。五项决策不变量ADR 的决策部分定义了五条不变量是全文的骨架。不变量 1单一来源身份解析每次bd init调用都只能从恰好一个被显式命名的来源解析project_id(a) 全新铸造mint fresh(b) 从远端收养adopt from remote——通过bd bootstrap或在 origin 存在refs/dolt/data时自动引导(c) 复用远端身份做本地重初始化reuse remote identity with local reinit。当存在两个互不相交的候选来源本地数据 远端 Dolt 历史且没有任何 flag 指名胜者时bd init拒绝执行。不变量 2作用域受限的--force/--reinit-local--force以及它的替代品--reinit-local只绕过本地数据安全守卫绝不授权对远端历史的静默分叉。当 origin 通告了refs/dolt/data时bd init --force必须拒绝除非同时传入--discard-remote。不变量 3中央扼流点可执行而非建议所有可能与远端历史交互的bd initflag 都必须经由 cmd/bd/init_safety.go 中的CheckRemoteSafety路由。新增一个 flag 就是扩展 cmd/bd/init_safety_test.go 中守卫矩阵测试的信号如果表格没有穷尽覆盖(dataSource × flagSet) → outcome这条 ADR 就存在缺口。源码中的注释说得更直白永远不要在调用点再加一个 !someFlag。不变量 4错误文本不回显error-text-no-echo任何bd运行时错误输出不得包含一条完整可复制的破坏性命令调用。允许出现 flag 标识符--discard-remote、--destroy-token和安全工具名bd bootstrap、bd doctor、bd help init-safety而 token 值DESTROY-prefix、哈希以及其他摩擦性参数只能出现在bd help init-safety、本 ADR 与docs/RECOVERY.md中。这条不变量直接封死58f5989bf事故类别当时一个 AI Agent 从工具自己的错误文本里复制粘贴了建议的一行命令bd init --force --destroy-tokenhash并执行毁掉了 247 个 issue。ADR 的定性非常犀利——Agent 的行为对它所读到的错误是理性的文本才是 bug。不变量 5竞态安全当--discard-remote被授权交互式确认或 destroy-token 匹配后bd init必须在提示/确认与执行之间重新验证 origin 上的refs/dolt/data。如果在确认窗口期内远端状态发生了变化比如另一个 Agent 刚 push 过bd init以ExitRemoteDivergenceRefused中止。竞态安全是内部不变量不是面向用户的仪式。决策落地从纯函数到 CLI 接线CheckRemoteSafety无副作用的决策函数源码将决策逻辑封装为一个纯函数无文件系统、无网络、无 git 访问。核心输入结构RemoteSafetyInput同时携带 flag 状态与远端观测状态type RemoteSafetyInput struct { Force bool // 与 ReinitLocal 等价但保留独立字段以便调用点发出弃用警告 ReinitLocal bool FromJSONL bool // 选择本地 JSONL 作为 init 来源 DiscardRemote bool // 授权丢弃远端 Dolt 历史 DestroyToken string // 命令行传入的 token ExpectedToken string // 调用方根据 issue 前缀算出的期望 token RemoteHasDoltData bool // 观测到的远端状态 IsInteractive bool // 是否附着 TTY决定是否可交互提示 }决策结果RemoteSafetyDecision返回Action、Reason、稳定的ExitCode以及供调用方原样打印的UserMessage。动作枚举RemoteSafetyAction定义了五个分支ActionNoRemoteData——远端无 Dolt 数据正常初始化ActionBootstrap——远端有 Dolt 数据应克隆收养而不是铸造新身份ActionRefuseDivergence——远端有数据且传了--force/--reinit-local但没有--discard-remote以ExitRemoteDivergenceRefused拒绝ActionRequireDestroyToken——传了--discard-remote但非交互模式下 token 校验失败ActionProceedWithDivergence——授权通过跳过引导并接线远端以便下次操作 force-push。函数体的决策树见 cmd/bd/init_safety.go依次判断远端无数据→放行有数据但用户无任何覆写意图→引导收养有覆写意图但无--discard-remote→拒绝分叉有--discard-remote且非交互但 token 缺失/错误→要求 token否则放行交互式调用方负责自行提示确认。拒绝文案What/Why/Next 三段式且不泄露破坏性命令refusalMessageDivergence()生成的拒绝文本刻意不回声完整破坏性命令结构是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. --force / --reinit-local bypasses only the LOCAL contenteditable="false">【免费下载链接】beadsBeads - A memory upgrade for your coding agent项目地址: https://gitcode.com/GitHub_Trending/beads1/beads创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考