
Beads 编码代理内存库bd reopen命令完整指南重新打开已关闭 issue 的语义、参数与底层实现【免费下载链接】beadsBeads - A memory upgrade for your coding agent项目地址: https://gitcode.com/GitHub_Trending/beads1/beadsbd reopen是 Beads一个为编码代理提供记忆升级的 issue 跟踪工具CLI 中专门用于重新打开已关闭 issue的命令。本文以其官方 CLI 参考文档 docs/cli-reference/reopen.md 为核心结合命令入口、存储层事务与跨后端一致性契约的源码实现完整讲解它的语法、--reason参数语义、与bd update --status open的本质区别、底层状态迁移规则含自定义状态类别、事件记录机制与并发安全保证让你在代理驱动的回归、需求变更等工作流中正确、安全地使用它。命令概览语法与核心语义bd reopen的官方定义生成自bd help --doc reopen只有一句话却包含了三层关键信息Reopen closed issues by setting status to open and clearing the closed_at timestamp. This is more explicit than bd update --status open and emits a Reopened event.命令语法bd reopen [id...] [flags]其核心语义可拆解为三点状态迁移将 issue 的status置为open清除关闭记录清空closed_at时间戳从 internal/storage/issueops/reopen.go 的 UPDATE 语句看实际清除的是closed_at、close_reason、closed_by_session、defer_until四个字段——关闭产生的完整痕迹会被一次性抹掉显式事件发出一个reopened事件internal/types/types.go 中定义为EventReopened reopened这正是它与普通更新命令的本质差异。为什么比bd update --status open更显式bd update --status open是一个通用补丁操作只改状态字段而bd reopen是生命周期Lifecycle级别的语义操作它同时完成状态迁移、关闭痕迹清理、事件记录、租赁lease清理与阻塞状态重算。从 issueops/issueops.go 中Lifecycle.Reopen的契约看它被定义为受保护的 issue 变更之一与Create、Update、Close并列是生命周期闭环中与Close严格对称的另一半。参数详解位置参数[id...]命令要求至少一个 issue ID源码 cmd/bd/reopen.go 中Args: cobra.MinimumNArgs(1)支持一次重新打开多个 issue。ID 解析走的是前缀路由prefix routing源码 cmd/bd/reopen.go 调用resolveAndGetIssueForMutation解析每个 ID注释明确说明supports cross-rig reopens likebd reopen xe-5ls——即你只需要提供可唯一定位的前缀系统会解析到完整 ID并且支持跨rig不同数据库实例解析。解析失败如 ID 不存在会向 stderr 输出Error resolving id: ...并继续处理其余 ID。标志Flags标志简写类型默认值说明--reason-rstring空重新打开的原因--reason的底层去向值得注意issueops/issueops.go原因不写入 issue 的字段而是记录在 issue 的事件历史中——挂在本次迁移自己产生的reopened事件条目上。因此查询原因需要读取 issue 的事件流而不是bd show的字段。从存储层实现 internal/storage/issueops/reopen.go 看非空 reason 除了写入reopened事件外还会额外添加一条评论AddCommentEventInTx让原因同时出现在评论流中便于人读。未提供 reason 的 reopen 同样会记录事件条目只是不携带原因文本。实战用法从单个到批量以下示例基于技能文档 plugins/beads/skills/beads/commands/reopen.md 与 CLI 实现整理# 1. 重新打开单个 issue bd reopen bd-42 # 2. 一次重新打开多个 issue bd reopen bd-42 bd-43 bd-44 # 3. 携带原因重新打开 bd reopen bd-42 --reason Found regression # 4. 前缀路由跨 rig 解析 bd reopen xe-5ls常见的重新打开原因技能文档建议的典型场景Regression found回归缺陷被发现Requirements changed需求变更Incomplete implementation实现不完整New information discovered发现新信息命令输出解读非 JSON 模式下成功的 reopen 输出cmd/bd/reopen.go↻ Reopened bd-42: Found regression带原因时会在 ID 后追加: reason。命令在只读模式CheckReadonly(reopen)见 cmd/bd/reopen.go或数据库未初始化时会被拒绝执行。JSON 输出配合全局 JSON 输出开关时命令会输出重新打开后 issue 的操作后状态快照post-state snapshot而不是重新读取数据库——源码注释明确这是operations own post-state snapshot replaces the re-readcmd/bd/reopen.go并会丢弃依赖记录updated.Dependencies nil因为bd reopen从不打印依赖。幂等与 no-op 行为bd reopen对非关闭状态是安全的 no-op分为两种情况cmd/bd/reopen.go目标已是open输出id is already open目标是其他非 done 状态如进行中、自定义 active 状态输出id is not closed (status: X); nothing to do。这两种情况都不会写库、不会提交、不会触发 hook——契约 backend/conformance/lifecycle_close_reopen_contract.go 专门用RunLifecycleReopenLeavesNonDoneStatusesUnchanged钉死了这一规则非 done比已打开范围更宽wip 内建状态与自定义 active 状态同样原样保留且带 holder 的行如in_progress assignee整体前后比对防止实现顺手清掉 assignee。源码级拆解一次 reopen 事务里发生了什么从存储层核心函数reopenIssueInTxinternal/storage/issueops/reopen.go可以完整看到一次 reopen 在单个事务内的全部步骤平面路由IsActiveWispInTx判断目标是 durable issue 还是 ephemeral wisp再通过WispTableRouting选择对应的 issues/wisps 与事件表状态分类读取当前status用ReopenCategoryInTx解析其类别只有done类别的状态才会被重新打开字面量closed以及通过status.custom配置的自定义 done 状态影响集计算AffectedByStatusChangeInTx/AffectedByStatusChangeForWispInTx找出本次状态变更会影响到的所有行用于后续阻塞状态重算条件更新执行带WHERE id ? AND status ?的条件 UPDATE将status置为 open同时清空closed_at、close_reason、closed_by_session、defer_until刷新updated_at并重铸row_lock乐观并发令牌行数守卫与并发重试若RowsAffected() 0说明状态已被并发修改——重读最新状态再次分类若已是 done 类别则递归重试一次否则返回 no-op 结果重试仍失败则报status changed concurrently租赁清理DeleteLeaseInTx删除该行持有的 claim 租赁记录事件记录RecordEventInTable写入reopened事件携带 actor 与 reason若 reason 非空再AddCommentEventInTx追加一条评论阻塞状态重算RecomputeIsBlockedInTxWithResult重算受影响行的is_blocked列——因为 reopen 跨越 closed/pinned 边界必须满足BlockedStateInvariant每个生命周期动词都必须让受影响行的阻塞列在提交前保持正确历史记录RecordEventInTx(ctx, tx, EventUpdate, ...)将这次变更作为一次update记入版本控制历史reopen 本质是状态变更因此归类为 update。Holder 保留契约测试RunLifecycleCloseAndReopenKeepTheClaimHolderbackend/conformance/lifecycle_close_reopen_contract.go钉死了一个容易被忽略的行为——reopen 清除的是关闭记录close_reason、closed_by_session、closed_at而assignee 不被清除。也就是说代理 A 持有并关闭的 issue 被 reopen 后仍然归属 A工作可以交还给同一 holder。自定义状态类别configured done categoryBeads 支持通过status.custom配置自定义状态词汇格式如name:category。契约测试使用的示例值是lcrtriage:active,lcrarchived:donebackend/conformance/lifecycle_close_reopen_contract.go其中lcrarchived被归类为done。这对bd reopen的影响RunLifecycleCloseAndReopenSpanTheConfiguredDoneCategory同文件 [L806-L845]reopen 处理的是配置的 done 类别而非字面量 closed。一个处于自定义 done 状态的 issue 用bd reopen会真实迁移到openChanged true——若把它当作已最终完成而跳过会让行停留在没有任何内建查询匹配的状态上。并发安全ExpectedVersion 与检查顺序ReopenRequest支持ExpectedVersion乐观并发前置条件issueops/issueops.go规则如下令牌不透明、仅做相等比较每次生命周期写入都会重铸非递增因此无法回答新旧问题它守卫的是行的生命周期状态status、assignee、started_at 写入会重铸而非图的边nil表示禁用检查不匹配时返回ErrVersionMismatch且不写入任何内容。关键检查顺序契约测试RunLifecycleExpectedVersionIsCheckedBeforeTheNoOpsbackend/conformance/lifecycle_close_reopen_contract.go对本会是 no-op的 reopen目标非 doneExpectedVersion依然先于no-op 判定被检查——即携带过期令牌的重开请求会收到ErrVersionMismatch而非nothing to do的成功响应避免丢失更新前置条件被静默跳过但行解析先于版本检查不存在的 ID 永远返回ErrNotFound而不是ErrVersionMismatch——两者的含义不同重新读取并重试 vs 这个 ID 是错的契约同时断言了ErrNotFound不会同时匹配ErrVersionMismatch。HTTP API 面bd serve下的 reopen当bd以服务器模式运行bd serve时reopen 通过 HTTP API 暴露对应处理器handleReopeninternal/httpapi/reopen.go请求体成员白名单为actor、reason、expected_version未知成员会被拒绝响应包含Issue操作后快照、AlreadyOpen!Changed的线上名称幂等语义同 re-claim、re-close与Revision行重开后的并发令牌供reopen 后再 close的恢复流程组合下一次expected_version失败路径中ErrVersionMismatch映射为版本前置条件 4xx 响应其历史记录 provenance 固定为bd serve: reopen issue同文件 [L27]保证无论后端是 store-backed 还是 unit-of-work历史条目的拼写一致。常见使用场景与注意事项回归工作流代理验证发现已关闭的 issue 出现回归 →bd reopen bd-42 --reason Regression found→ issue 回到 open 且关闭痕迹被清除 → 可再次 claim 处理。此时bd reopen比bd update --status open更合适因为后者不会清理closed_at——一个开着却带着完成时间戳的行会污染周期时间cycle-time与燃尽图统计契约测试明确指出了这一点backend/conformance/lifecycle_close_reopen_contract.go。批量恢复误关闭多个 issue 时bd reopen bd-42 bd-43 bd-44一次恢复单个解析失败不影响其他项继续执行最终以非零退出码标记部分失败SilentExit。约束提醒只对closed及自定义 done 状态生效open/wip 等状态是安全的 no-op命令是写操作在只读模式下被拒reason 的归宿是事件历史与评论不在 issue 字段上如需取回读取 issue 的事件流reopen 会清理 claim 租赁lease但它不改变 assignee——holder 保持原状。验证与测试覆盖如果你想深入验证上述行为仓库提供了三层测试契约层backend/conformance/lifecycle_close_reopen_contract.go —— 在全部后端server-backed、embedded、unit-of-work上运行覆盖关闭痕迹清理、holder 保留、非 done no-op、自定义 done 类别、ExpectedVersion 顺序等语义CLI 层cmd/bd/reopen_test.go 与 cmd/bd/reopen_embedded_test.go —— 验证 reopen 后状态为 open、ClosedAt为 nil、原因正确落库存储层internal/storage/issueops/reopen_test.go —— 针对 SQL 分支含自定义状态的单元测试。这些测试文件既是行为规范也是你改造或扩展 reopen 行为时必须保持绿灯的基线。【免费下载链接】beadsBeads - A memory upgrade for your coding agent项目地址: https://gitcode.com/GitHub_Trending/beads1/beads创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考