资讯动态

Beads `bd dolt pull` 卡死修复全解:config 表上的“两全其美“碰撞与 `commitBeforePull` 方案

发布时间:2026/9/11 1:35:08 来源:尧图企业网站定制
Beadsbd dolt pull卡死修复全解config 表上的两全其美碰撞与commitBeforePull方案【免费下载链接】beadsBeads - A memory upgrade for your coding agent项目地址: https://gitcode.com/GitHub_Trending/beads1/beads导读bd dolt pull在存在任何持久记忆bd remember写入的kv.memory.*行后必然失败报Error 1105 (HY000): cannot merge with uncommitted changes而bd doctor则持续报告Dolt Locks: config: modified——这就是本仓库中编号为pull config wedge拉取配置卡死的经典故障。本文以 PROPOSAL-pull-config-wedge.md 为骨架结合仓库当前已落地的源码实现完整还原该 bug 的根因两个各自正确的 PR 在config表上发生碰撞、端到端实证过程、最终修复设计commitBeforePull与三档configCommitMode提交策略以及配套回归测试读者可据此理解 Beads 的 Dolt 提交语义边界并掌握同类merge refuses to start问题的排查与修复方法。说明该提案最初以 for review 状态提交Wyvern 库上实时复现2026-06-14其提出的修复方向在当前仓库代码中已经落地并进一步演进——从单纯的预拉取提交必须包含 config升级为带kv.*白名单过滤的三档提交模式。文中源码引用一律以当前仓库实际实现为准。症状bd dolt pull恒定失败且任何命令都无法自愈故障的直观表现非常稳定bd dolt pull总是失败报错与 Dolt 的合并前置校验直接相关Error 1105 (HY000): cannot merge with uncommitted changesbd doctor每次命令后都报告Dolt Locks: config: modified——即使刚刚执行过bd dolt commit即使开启了--dolt-auto-commit on也无法消除。该现象在内部被跟踪为 Wyvern wy-71t。push 完全不受影响只有 pull跨 clone 的 receive 路径被卡死。这说明问题不在网络、认证或协议层而在于合并发生前的工作集working set状态校验——Dolt 的DOLT_MERGE拒绝在有未提交变更的工作集上启动。值得强调的是这个每次命令后都重新变脏的症状具有很强的迷惑性它看起来像是某条命令每跑一次都会写入 config导致永远无法收敛。后文实证验证一节将证明真实机制恰恰相反。根因两个各自正确的 PR 在config表上碰撞提案给出的根因链条由三个事实环环相扣构成其中前两个事实在当前源码中都能找到明确注释佐证事实 1持久记忆存放在已同步的config表中bd remember写入的每一条持久记忆都是一行形如kv.memory.slug的记录落在synced参与 Dolt 同步的config表中。提案在 Wyvern 线上库通过工作集实测确认了这一点——当时唯一变脏的行恰好就是 5 条记忆SELECT to_key, diff_type FROM dolt_diff(main,WORKING,config); kv.memory.beads-jsonl-sync-procedure added kv.memory.bitbucket-altssh-dead added kv.memory.local-dev-ssl-posture added kv.memory.react-client-e2e-against-the-local-game-server added kv.memory.react-e2e-same-character-parallel-logins addedkv.memory.*作为记忆行的命名空间在仓库中也有成体系的约束与测试见 backend/conformance/memories_contract.go其中明确列出了记忆行kv.memory.*、记忆与同名配置项互相遮蔽shadow的陷阱类、以及针对kv.memory.前缀裁剪的 DELETE 类攻击等契约行为。事实 2Commit刻意排除config表GH#2455Beads 的常规提交入口 DoltStore.Commit 在注释中完整记录了这段历史GH#2455 之后Commit只暂存除config外的所有脏表。原因是最早的DOLT_COMMIT(-Am, …)会把并发操作遗留的半成品issue_prefix变更顺手扫进无关提交造成污染。因此Commit现在的语义是自动提交绝不触碰config只有明确意图修改 config 的调用方如CommitWithConfig才允许提交它。事实 3预拉取自动提交恰好使用了CommitGH#2474GH#2474 为了让合并能启动引入了拉取前自动提交工作集的保护逻辑两条 pull 路径默认 remote 的pullFromRemote、命名 peer 的PullFrom在 merge 前都会先调用s.Commit清理工作集。但正如事实 2 所述Commit跳过config——于是这条保护对config表形同虚设。净效应只要存在任何一条记忆kv.memory.*行就永远处于未提交状态没有任何代码会去提交config预拉取清理步骤执行完毕后config依然脏DOLT_MERGE拒绝启动。这正是abortMerge注释中早已点名的场景——a merge that REFUSED TO START on a dirty working set一个因工作集脏而拒绝启动的合并。GH#2474 的意图被 GH#2455 悄然击穿而只要执行过一次bd rememberconfig就必然脏——所以这个 wedge 是必然发生而非偶发。提案还特别澄清了一个容易混淆的独立故障同一数据库上出现的 v42→v51 schema 迁移分叉#4259与本问题无关迁移后强制发布force-publish可以修复分叉并恢复 push但 pull 仍会因 config 问题继续卡死。实证验证一个无需改源码的端到端证明提案在 Wyvern 线上库完成了完整的机制闭环验证未做任何源码修改唯一脏表就是config唯一脏行就是那 5 条kv.memory.*即上文dolt_diff查询结果。显式提交 config 后工作集立即变干净CALL DOLT_ADD(config); CALL DOLT_COMMIT(-m, …)随后dolt_diff(HEAD,WORKING,config)返回 0 行。后续普通命令bd list、bd show不再让它变脏仍为 0 行。这证明 Beads 每条命令的记忆再同步操作是幂等的一旦行内容与 HEAD 一致重复写入相同值不会产生 diff。每次命令后都重新变脏的表象唯一成因就是这些行从未被提交Commit跳过 config因此永远显示为未提交。把 config 提交一次即可打破循环。提交 config 后bd dolt pull可重复成功Pull complete.bd dolt push依然正常bd doctor也不再报告config: modified。结论非常干净干净的 config 工作集是合并唯一需要的前置条件因此合并前把 config 提交掉是一次性根治而不是逐命令的临时补丁。同时提案也预告了反例不打补丁的话下一次bd remember产生新的未提交kv.memory.*行wedge 立即复发。修复方案预拉取提交必须包含config提案的修复方向是复用已有的CommitWithConfig当时的store.go:1780现位于 store.go#L3226内部执行DOLT_COMMIT(-Am, …)把两条预拉取提交点从Commit换成 config 包含式提交。提案原文给出了两份 diff--- a/internal/storage/dolt/store.go b/internal/storage/dolt/store.go pullFromRemote (GH#2474 pre-pull auto-commit) - if err : s.Commit(ctx, auto-commit before pull); err ! nil { // Include config: persistent memories live in config as kv.memory.* rows, // and Commit() excludes config (GH#2455), so they never commit and leave // the working set permanently dirty — DOLT_MERGE then refuses to start. if err : s.CommitWithConfig(ctx, auto-commit before pull); err ! nil { if !isDoltNothingToCommit(err) { return fmt.Errorf(failed to commit pending changes before pull: %w, err) } }--- a/internal/storage/dolt/federation.go b/internal/storage/dolt/federation.go PullFrom (GH#2474 pre-pull auto-commit) - if err : s.Commit(ctx, auto-commit before pull); err ! nil { if err : s.CommitWithConfig(ctx, auto-commit before pull); err ! nil { if !isDoltNothingToCommit(err) { return nil, fmt.Errorf(failed to commit pending changes before pull: %w, err) } }当前仓库中的演进实现commitBeforePull与三档configCommitMode当前代码已把提案的方向落地并做了关键加固——没有直接照搬CommitWithConfig而是新增了专用入口 DoltStore.commitBeforePull并围绕config表定义了三种提交模式枚举store.go#L2962-L2975configExclude常规Commit使用的模式跳过configGH#2455 的原始语义configIncludeUserKVOnly预拉取自动提交专用——只暂存属于本 clone 自己的用户 KV 数据kv.*命名空间含kv.memory.*记忆行一旦检测到任何非kv.的内部键如issue_prefix变脏就拒绝提交并给出操作指引确保 pull 永远不会自动提交不安全的 configGH#2455 GH#2474 双约束configIncludeAll仅用于显式合并收尾bd federation sync --strategy/bd vc merge --strategy操作者已明确选定冲突解决策略因此所有被触及的 config 行包括issue_prefix都要如实提交不允许丢弃解决结果。该模式还负责干净工作集也要收尾的边界情况即--ours场景详见commitWorkingSet中 wy-36ilm 的注释。底层的 commitWorkingSet 先查询dolt_status枚举脏表再用反连接anti-join排除dolt_ignore表wisps、wisp_%、leases 属于常态脏表且不可暂存遇到config则按模式分流对configIncludeUserKVOnly会额外调用assertDirtyConfigUserKVOnly校验脏键范围。config 采用显式DOLT_ADD暂存而非依赖-Am因为该路径必须在暂存前筛查脏键、只放行用户kv.*数据源码注释同时记录了 GH#4412 的历史教训——旧版 server 模式下DOLT_COMMIT(-Am)曾出现不暂存 config 的现象对应提案中待验证的 wrinkle而CommitAll的 pinned-server 容器测试现已证明受支持路径上-Am能正确暂存 config。两条拉取链路都已切换到该入口默认 remote 拉取pullFromRemoteUncheckedPull/PullRemote的公共内部实现在 merge 前调用commitBeforePull(ctx, auto-commit before pull)命名 peer 拉取pullFromPeerPullFrom的内部实现同样先commitBeforePull且 Syncbd federation sync的预合并提交也使用同一入口。两条路径均以isDoltNothingToCommit容忍无事可提交其余错误包装为failed to commit pending changes before pull返回。为什么这不会重开 GH#2455提案专门论证了安全性GH#2455 防范的是-Am把并发写者的半成品issue_prefix变更扫进无关提交。而预拉取提交在性质上完全不同——它是用户显式发起的操作bd dolt pull本身就要求干净工作集且提交的是本 clone 自己的config 状态作为合并基准这与CommitPendingbd dolt commit做的事情完全一致。GH#2455 担心的竞态窗口另一个 bd 进程在同一瞬间改写issue_prefix并不会因为在这里提交 config 而有意义地扩大——反正用户下一次bd dolt commit也会提交它。当前实现的configIncludeUserKVOnly更进一步用kv.*白名单把这种安全性从论证变成了代码强制。可选加固自动解决收敛型config合并冲突主修复生效后config含kv.memory.*变成了已提交、已同步的表——副作用是两个 clone 编辑同一条记忆时会触发config合并冲突。仓库中的自动解决器 TryAutoResolveMergeConflicts 目前只处理metadata、dependencies、schema_migrations不含config因此这类冲突会中断 pull 等待操作者介入。提案建议的后续加固方向是让解决器对机器可收敛的kv.memory.*键采用--theirs策略自动解决与metadata表既有的收敛策略保持一致。当前代码已出现对应的收敛判定configConflictsAreMemoryConvergent见 store.go#L3005 附近注释与测试覆盖下文测试一节说明该加固方向已在落地中。提案明确划界这是 wedge 修复之外的增强主修复已能解决报告的 bug在那之前同记忆的分歧编辑属于罕见的操作者可见事件而非静默卡死。被否决的替代方案把记忆迁到dolt_ignore表提案考虑过把记忆挪进dolt_ignore忽略的表就像 wisps 那样并明确否决记忆的设计意图就是跨 clone 同步它们经由bd export | bd import往返属于共享状态的一部分而dolt_ignore会让它们变成机器本地数据、彻底停止经 Dolt 同步——这是行为变更不是修复。测试计划与当前回归测试提案给出的回归测试场景建议放在pull_conflict_test.go/federation_pull_settle_test.go旁两个 tiny remote-backed DB 的 cloneclone A 上执行bd remember --key t x以kv.memory.*行弄脏 config常规路径不提交任何东西clone B 上创建并 push 一个 issueclone A 执行Pull——必须成功修复前返回cannot merge with uncommitted changes且 clone A 最终同时拥有 B 的 issue 与自己的记忆。该计划在仓库中已形成完整的测试族集中存放于 internal/storage/dolt/pull_config_wedge_test.goTestCommitBeforePullIncludesConfig直接复现 wedge 前置条件——插入kv.memory.*行后普通Commit()确实不提交 config确认 GH#2455 排除逻辑仍在而commitBeforePull必须暂存 config 并让dolt_status干净TestFederationSyncCommitsConfigBeforeFetchbd federation sync的对应物——预合并提交必须包含 config即使 sync 随后因 remote 不存在而失败失败点已足够证明 config 已被暂存TestPullAutoResolveMemoryConfigConflicts验证仅涉及kv.memory.*行的合并冲突按theirs策略自动解决这正是上文可选加固的测试落点另有针对不安全 config 键的守卫测试kv.*白名单之外commitBeforePull必须拒绝、点名违规键、保持 config 未提交且 HEAD 不动防止 GH#2455 的陈旧 config 清扫问题以 pull 为入口复活还有覆盖通用kv.*行bd kv set场景的测试证明白名单外推后普通 KV 写入也不再 wedge pull/sync。测试辅助函数configDirty用SELECT COUNT(*) FROM dolt_status WHERE table_name config精确判定config 工作集脏这一 DOLT_MERGE 拒绝启动的确切条件断言风格与提案建议的预拉取提交后dolt_status为空完全一致。涉及文件清单PROPOSAL-pull-config-wedge.md——本提案原始文档症状、根因、实证、修复、测试计划internal/storage/dolt/store.go——CommitL2986排除 config、commitBeforePullL3017主修复入口、CommitMergeResolutionL3032、CommitWithConfigL3226、CommitAllL3266、commitWorkingSetL3045与configCommitMode三档枚举L2962、pullFromRemoteUncheckedL3979预拉取提交点internal/storage/dolt/federation.go——PullFrom/pullFromPeerL69/L79命名 peer 拉取的预拉取提交点与SyncL355共用commitBeforePullinternal/storage/dolt/pull_config_wedge_test.go——wedge 回归测试族internal/storage/versioncontrolops/mergesettle.go——TryAutoResolveMergeConflicts可选加固落点backend/conformance/memories_contract.go——kv.memory.*记忆行命名空间与遮蔽陷阱的契约测试结语pull config wedge是一起教科书级的子系统交互事故提交排除逻辑GH#2455与预拉取清理逻辑GH#2474各自单独看都正确却在记忆存于 synced config 表这一事实之上组合出了必然失败。它留下的方法论启示对 Beads 乃至任何基于 Dolt/类 Git 存储的 agent 状态库同样适用当清理工作集的提交路径与某张表永不参与自动提交的排除规则重叠时必须先确认被排除的表里是否有必须同步的用户数据再谈合并前置条件。当前仓库以commitBeforePullkv.*白名单 三档提交模式给出了兼顾修复与防回归的最终答案并以一整套回归测试固化了这条边界。【免费下载链接】beadsBeads - A memory upgrade for your coding agent项目地址: https://gitcode.com/GitHub_Trending/beads1/beads创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价