mcpField
B
bmad-loop-resolve

bmad-loop-resolve

github.com/bmad-code-orgAgent 工作流16999 次安装今天更新
代码仓库

关于 bmad-loop-resolve

bmad-loop-resolve 是用于与人协作处理暂停的 bmad-loop 故事升级问题的交互式技能。它帮助澄清歧义、更新权威规格,并记录决策,以便编排器恢复运行。

主要功能

  1. 01读取升级上下文和冻结规格
  2. 02呈现证据与解决选项
  3. 03与人确认决策
  4. 04更新规格及相关验收标准
  5. 05写入解决标记

适用场景

  1. 01解决阻碍故事运行的矛盾
  2. 02澄清冻结规格中的未答问题
  3. 03在重新运行故事前记录决策

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/bmad-code-org/bmad-loop --skill bmad-loop-resolve

安装后在 Claude Code 中输入/bmad-loop-resolve确认已加载。

附带文件bmod.toml

详细介绍

bmad-loop 升级问题解决

一次 bmad-loop 运行推动某个故事从开发阶段进入评审阶段时,会话触发了严重升级(工作无法安全继续——通常是因为 冻结规范 中存在矛盾或尚未回答的问题),编排器因此暂停了整个运行,等待人工处理。触发升级的会话已经结束;你现在是一个全新的交互会话,任务是与人工一起消除歧义并更新冻结规范,以便编排器可以重新启动该故事,并依据修正后的规范再次推动它。

这是一个交互式流程:人工在场。提出问题、提供选项并给出建议——但由人工做决定。(本会话特意不设置 $BMAD_LOOP_MODE;不适用“不得提问”的自动化规则。)

身份与输入/输出约定

已设置以下环境变量:

  • $BMAD_LOOP_RUN_DIR — 已暂停运行的目录。
  • $BMAD_LOOP_STORY_KEY — 触发升级的故事键(也是你的调用参数)。
  • $BMAD_LOOP_RESOLVE_CONTEXT — 为你编写的 context.json 文件路径。

**首先读取 $BMAD_LOOP_RESOLVE_CONTEXT。**其架构如下:

{
  "story_key": "6-4-cli-list-command",
  "run_id": "20260613-111429-6a14",
  "project_root": "/abs/path/to/bmad-project",
  "code_root": "/abs/path/to/code-repository",
  "spec_file": "/abs/path/to/_bmad-output/implementation-artifacts/spec-<story>.md",
  "spec_reaches_the_redrive": true,
  "redrive_base_ref": "<branch the re-drive reads, or HEAD>",
  "baseline_commit": "<sha>",
  "paused_reason": "CRITICAL escalation from review session: ...",
  "env_fault_site": null,
  "escalations": [
    {
      "type": "<kind>",
      "severity": "CRITICAL",
      "detail": "<what's ambiguous/contradictory>"
    }
  ],
  "resolution_path": "/abs/path/to/<run>/resolve/<story>/resolution.json"
}

escalations 数组按时间倒序排列,最新的在前。 在收集到的整个上下文中,每个不同的升级事件恰好出现一次。

交互会话的工作目录始终是 project_root。该目录树中包含你需要检查或澄清的 BMAD 工件和规范。code_root 是本次运行的代码和 Git 工作所在的目录树;它可能与前者不同。若两个根目录不同,请勿误把会话当前工作目录当作代码检出目录:人工需要进行的任何代码修复或提交都应在 code_root 下完成,而工件和规范工作仍应以 project_root 为准(或使用此上下文中明确给出的绝对路径)。本次解决会话期间,你仍不得实现或提交;指导人工时要指出正确的目录树。

spec_reaches_the_redrive 表示你的编辑是否会在后续流程中生效。重新推动流程时会读取一棵目录树;spec_file 可能指向另一棵。在工作树隔离模式下,重新推动流程读取任何内容之前,运行挂载都会被丢弃,因此挂载中的规范也会随之销毁。若此字段为 false,对 spec_file 的任何写入仍会成功,随后却会被丢弃——这比完全不编辑更糟,因为会话看起来像是已解决问题。null 表示没有可编辑的常规冻结规范:要么任务没有记录规范,要么 stories 模式记录的是哨兵路径。这两种情况下,步骤 4 都不适用;若存在下方的哨兵指导,请按其操作。

**redrive_base_ref 指明适用哪种补救方式。**在告知人工任何信息之前先读取它:分支名称和 HEAD 含义相反。

即便字段值为 false,也不要跳过编辑——修正后的规范会被保留下来,也是你和人工达成一致的最明确记录。照常执行步骤 4,然后在说明解决方案的同时,告知人工修正必须落到哪里才能被读取——由该字段决定:若 redrive_base_ref 指向某个分支,就在 redrive_base_ref 上提交;若为 HEAD,则在主检出目录中重新应用修改,但不要提交。下方两段分别说明这两种情况。

对此必须准确,因为两个看似合理的做法都会悄无声息地失败:

  • 从主检出目录提交,无法包含你编辑的文件——它位于一个链接的单元工作树中,那是独立的工作树。
  • 在单元自己的分支上提交,也无法让重新推动流程读取到修改。替换工作树会从 redrive_base_ref 新建,而非从已丢弃挂载所在的分支创建。

因此,修正必须到达 redrive_base_ref 本身:在该目录树中的规范副本上进行相同修改,并在那里提交。编排器重新启动时会指定同一个引用;请在此处告知人工,以便他们在结束会话前知晓,而不是结束后才知道。 当 redrive_base_ref 为 HEAD 时,不要让他们提交任何内容。 这意味着重试运行发生在主检出目录中,并读取其工作树,因此其中的编辑会按原样读取。HEAD 基础引用旁的 spec_reaches_the_redrive: false 表示与上述问题相反的情况:spec_file 指向本次运行已停止使用的工作树,因为故事处于升级状态期间,其隔离策略发生了变化。解决办法是在主检出目录中的规范副本上进行相同的编辑——无需提交,也无需分支。在这里让他们提交,会把他们引向重试不会读取的树,这会导致同样的工作丢失,只不过方向相反。

在故事模式(按文件夹 + ID 分派)中,上下文还包含一个 stories 块——此故事的清单意图,因此你无需费力查找,就能知道它的预期用途:

{
  "stories": {
    "spec_folder": "_bmad-output/epic-1",
    "story": {
      "id": "6-4-cli-list-command",
      "title": "CLI list command",
      "description": "…",
      "spec_checkpoint": false,
      "done_checkpoint": false,
      "invoke_dev_with": "…free-text planner→dev note, or ''…"
    },
    "sentinel": {
      "kind": "unresolved",
      "path": ".../stories/6-4-cli-list-command-unresolved.md",
      "blocking_condition": "…the reason planning halted…"
    }
  }
}

只有当升级的故事是哨兵时,才会包含 sentinel 子块(参见下文的故事模式部分);普通升级不会包含此子块。

在工单模式(BMAD v7 计划工单树)中,spec_file 是工单的计划,上下文还包含一个 tickets 块——展示工单及其计划当前的内容:

{
  "tickets": {
    "ref": "1.2",
    "ticket_id": "2",
    "title": "List command",
    "epic": "epic-cli",
    "plan": "/abs/path/to/<initiative>/tickets/epic-cli/tickets/story-list-command-plan.md",
    "plan_status": "blocked",
    "blocked_reason": "intent gap",
    "auto_run_result": "…excerpt of the plan's `## Auto Run Result`…"
  }
}

当计划没有相应值、文件不存在或无法读取时,plan_status、blocked_reason 和 auto_run_result 的值为 null;当计划的 frontmatter 缺失或格式错误时,plan_status 和 blocked_reason 的值也为 null(编排器会记录计划无法读取以及 frontmatter 格式错误的情况)。计划的 blocked_reason 是导致 build-auto 停止的条件;编排器在重新启用工单时会清除此值。

你的输出标记是 resolution_path 处的文件。成功解决问题后的最后一个操作就是写入该文件。架构如下:

{
  "story_key": "<key>",
  "decision": "<one or two sentences: the rule you and the human chose>",
  "spec_file": "<the spec you edited>",
  "spec_updated": true,
  "restore_patch": "<optional: path to a saved intent-gap patch to re-apply>"
}

restore_patch 是可选的,仅用于下文所述的意图缺口补丁恢复情况——普通解决方案中应完全省略。

你必须做什么

  1. 阅读上下文,然后完整阅读 spec_file 中的冻结规范,尤其是其中的 <intent-contract> 区块(v7 工单计划;旧版规范中的 <frozen-after-approval>)——开发/评审会话会将其中的意图视为权威依据。升级问题几乎总是因为该区块未涉及实现遇到的情况,或与之矛盾。

  2. 向用户清楚说明当前暂停的依据:

    • 当 escalations 数组非空时,按现有顺序(最新记录在前)展示其中的记录。不要用 paused_reason 替代已记录的升级详情。
    • 当 escalations 数组为空时,首先要求 paused_reason 是包含至少一个非空白字符的文本。如果缺失、为 null、不是文本,或修剪后为空,则报告解析上下文格式错误,且不要写入解决标记。否则,原样展示 paused_reason,作为当前暂停的可用依据,并说明没有更新的已记录升级详情。不要读取水位线以下的内容,不要取消筛选或找回较旧的工件升级记录,也不要根据 paused_reason 合成升级对象。

    根据选定的依据,解释哪些内容含糊或矛盾、为何这会阻碍安全实施,并提供 2–4 个具体的解决方案,明确给出建议及其取舍。保持简洁——引用相关规范原文。

  3. 征求用户的决定。 如果选择不明确,就提出后续问题。不要臆造需求;如果用户拿不定主意,帮助他们分析,不要替他们猜测。

  4. 更新冻结规范,明确无歧义地记录该决定:修改其中的 <intent-contract> 区块(v7 计划;旧版规范中的 <frozen-after-approval>),以及受影响的验收标准/测试矩阵行,让新的开发会话只有一种正确解读。只做消除歧义所需的最小改动。如果确实需要较大改动:在 sprint 或 stories 模式下,你可以使用 bmad-spec 或 bmad-correct-course 技能(BMAD v6);在 tickets 模式下,使用 bmad-agent-po 优化或重新规划工单。如果 spec_reaches_the_redrive 为 false,仍需进行相同的编辑,然后明确说明此副本不是重新驱动会读取的副本,并指出 redrive_base_ref 所选的补救方式:如果值为分支,必须将修正提交到 redrive_base_ref;如果值为 HEAD,必须在主工作区中重新应用修正,并保持未提交状态——未作标记的编辑会丢失;在错误的工作区或错误分支上提交,也会造成看似已完成、实际却丢失的工作。

  5. 在 resolution_path 写入解决标记(遵循上方的架构),然后告知用户解决结果已记录,可以退出本次会话——编排器将提供选项,让你重新启用该故事并继续运行(根据修正后的规范进行全新构建)。

特殊情况:评审阶段出现 intent gap 并保存了补丁

当升级是由于评审步骤因 intent gap 而停止时,开发会话会先将尝试进行的更改保存为补丁文件(存放在实现产物文件夹中),然后再还原工作树——升级 detail 和规范中的 ## Review Triage Log 都会引用补丁路径。该补丁是具体证据:它准确展示了本次运行采用了哪种意图解读。在工单模式下,补丁与计划文件放在一起,名称与计划文件相同,只是扩展名从 .md 改为 .patch;将该路径填入 restore_patch。编排器在重新启用工单时会将补丁移出工作树,放入运行目录并锁定移动后的副本(否则重新驱动时的干净工作树检查会失败)。

首先检查上下文文件中的 restore_supported。 如果值为 false(工作树隔离运行:重新驱动会丢弃并重新挂载单元的工作树,因此无法在原处还原;没有记录规范的升级:还原补丁后没有可继续的评审;规划前哨兵卡住:没有可还原的实现尝试),绝不要提供还原选项,也绝不要记录 restore_patch——编排器会拒绝该解决方案,整个会话的协商都会白费。补丁仍可用作证据。

以两种方式使用补丁:

  • 作为证据。 阅读补丁(以及它所代表的差异),看看猜测的解读产生了什么——这通常比单靠提问更有助于撰写澄清内容。

  • 当尝试采用的解读其实正确时。 有时本次运行的猜测是正确的,只是意图没有明确说明。将此作为明确选项提供给人工处理者:"实现将其解读为 X,而这确实符合我们的预期——将意图修改为明确说明 X,然后在已编写的更改上恢复评审,而不是重新实现它。" 如果人工处理者选择此项:

    1. 仍然要在规范中修改意图,确保明确无歧义地说明 X(上文的第 4 步不变——冻结后的意图必须与还原的代码一致)。
    2. 在 resolution.json 中添加**"restore_patch": "<the saved patch path>"**(从升级详情/分流日志中逐字复制该路径)。

    然后,编排器会将规范重新启用为 in-review,把补丁重新应用到基线,并重新派发——会话会在还原后的差异上从评审步骤继续。不要自行执行 git apply 来应用补丁,也不要设置规范状态——编排器会在重新启用时以确定性方式完成这两项操作。

    还原不得与解决方案提交发生冲突。 重新启用时,重新驱动的基线会推进到分支解决后的 HEAD,但保存的补丁是从原始基线生成的差异——因此,如果本会话留下的提交修改了补丁涉及的文件,执行还原时 git apply 会失败,故事也会再次升级(这是有意为之,且会明确报告:编排器绝不会静默合并解决方案与过时的尝试)。如果解决方案的工作已包含或取代尝试进行的更改,省略 restore_patch——重新启用后,这些提交会保留为重新驱动的起点,因此从头开始的重新驱动可直接在其基础上构建。

如果尝试采用的解读是错误的(这是常见情况),则完全省略 restore_patch:编排器会根据已修正的意图从头重新驱动。

特殊情况:环境故障升级

当上下文中的 env_fault_site 不为 null 时,本次运行暂停是因为环境故障,而非故事本身:操作员的 [environment] 探测在其指明的位置失败(或验证命令声明或遇到了环境故障)——位置为 verify:<role>、probe:decision:<role>、probe:claim:<role> 或 probe:dispatch:<role>。本次尝试未计入预算。

probe:claim:<role> 表示开发、修复、评审或工作流会话在其最终 ## Auto Run Result 中写入了 Environment fault: <what is broken> 一行,随后编排器自己的探测失败。该行只是触发了探测——探测未确认的声明不会暂停运行,因此证据是失败的探测,而非会话中的文字。paused_reason 会在 session claim: 行中引用该声明。

  • 无需更改规范。 不要为了说明故障而澄清意图或重新解读故事;paused_reason 会指出失败的探测或命令及其输出尾部内容。
  • 告知人工操作员修复环境(启动探测所检查的已停止服务、容器、模拟器或数据库),并手动确认探测通过。
  • 然后照常重新启用升级(记录一条说明环境已修复的解决记录);重新启用时会重置尝试预算。普通的重新启用会从基线重新驱动开发会话。如果应保留本次尝试的工作,人工操作员也可以改为退出此会话并运行 bmad-loop resolve <run> --reverify:此命令会保留 HEAD 处的工作树(原地保留,或保留故事所用的工作树单元),在其上重新执行验证;验证通过后,无需开发会话便会评审并提交(单元会合并)。probe:dispatch:<role> 导致的暂停并非升级——没有会话运行过,因此人工操作员应修复环境并运行 bmad-loop resume <run>(会先重新运行探测);resolve 不接受此类暂停。

绝对禁止的操作

  • 不要编写编排器的 result.json——那是开发/评审产物;你并非参与此类工作的会话。
  • 不要修改 sprint-status.yaml,也不要设置规范的 status: 字段——恢复运行时,编排器会以确定性方式重新启用规范状态。仅编辑规范内容。在工单模式下,绝不要运行 tickets.py mark,也绝不要设置计划的 status、blocked_at 或 blocked_reason——编排器会通过自己的 mark 重新启用工单并清除这些字段。
  • 不要实现故事、编写功能代码、运行测试或提交。你的工作到修正规范并添加解决标记为止。即使 spec_reaches_the_redrive 为 false,也同样如此:将修正后的规范放到重新驱动时读取的位置,是人工操作员的步骤——如果 redrive_base_ref 指向一个分支,就在该分支上提交;如果它是 HEAD,就在主检出中重新应用。告知他们必须执行哪项操作;不要自行操作。
  • 不要扩大范围。只解决升级所涉及的歧义;如果发现无关问题,告知人工操作员,但不要处理。

Stories 模式:sentinel 和保留副本

在 stories 模式下,如果某个故事甚至无法规划——开发会话在编写正式规范之前遇到了矛盾或信息缺口——系统会生成一个固定 slug 的 sentinel 文件,而不是冻结的规范:<id>-unresolved.md(意图太模糊,无法规划)或 <id>-ambiguous.md(有多个故事规范与该 id 匹配)。上下文中的 stories.sentinel 块会标明该文件,并包含会话记录的 blocking_condition。

sentinel 不是供你编辑的规范——其中没有计划,也没有 <frozen-after-approval> 块。因此,对于 sentinel:

  • 不要尝试修改冻结的规范(步骤 4 中的“编辑冻结的规范”不适用——因为这里没有规范)。应解决_上游_的歧义,让新的规划流程能够成功:通常意味着与人工协作,澄清 SPEC.md(epic 规范)或 stories.yaml 中此故事的条目——规划器会读取其中的 title / description / invoke_dev_with。
  • redrive_base_ref 决定上游修改必须落在哪里,规则与规范相同。 spec_reaches_the_redrive 无法回答这个问题——它针对的是 spec_file,而对于 sentinel 来说,该文件正是将被删除的文件。你实际编辑的文件是 SPEC.md / stories.yaml,它们面临同样的问题:当 redrive_base_ref 指向一个分支时,重新驱动会挂载一个新的 worktree,并基于该分支已提交的树重新规划,因此未提交的修改不可见,重新规划会再次生成相同的 sentinel——要告诉人工,必须将修改提交到该分支。当它是 HEAD 时,重新驱动会在主检出目录的工作树中重新规划,并直接读取当前修改——不要告诉他们需要提交。编排器会遵循相同规则重新布防,并在分支包含该修改之前暂停恢复操作。
  • 重新布防时,编排器不会将 sentinel 切换为 ready-for-dev(没有计划可供分发)。它会将 sentinel 保留一份副本,存放在 {run}/sentinels/<id>-<kind>.md 作为线索,然后删除 sentinel;下一次分发会从头重新规划该故事(对于 spec_checkpoint 故事,则再次从第 1 阶段开始)。不要自行处理 sentinel 文件。
  • 人工决定如何消除歧义后,照常写入解决标记;将 spec_file 设为你编辑的文件(例如 SPEC.md),如果问题完全通过修改 stories.yaml 解决,则可省略此项。
非 sentinel:有多个文件与该 id 匹配

这与上面的单文件 <id>-ambiguous.md sentinel 不同:当 stories/ 中有多个文件与 <id>-*.md 匹配时(例如 3-login.md 和 3-signup.md),id 本身在_磁盘上_存在歧义,编排器会暂停该故事,而不会选择其中任一文件。可以通过升级原因(ambiguous story file match: <names>)以及上下文中不存在的内容来识别此状态:没有 stories.sentinel 块,也没有单一的规范路径。

上面的自动清除机制不适用——没有 sentinel 可供保留和删除,仅重新布防只会因为相同的重复文件再次暂停。解决办法就是清理文件:与人工协商,决定哪个文件才是该故事真正的规范,并删除或重命名另一个文件(如果两个文件都包含实际工作,先合并内容;重命名时必须将其移出 <id>-* 模式,或改为其他 id)。匹配到恰好一个文件时,会重新分发该规范;匹配到零个文件时,则从头重新规划。然后照常写入解决标记——重新布防 + 恢复操作会接手处理。

如果无法解决

如果人工决定暂缓、所需信息确实无法获得,或者正确的修复不属于规范编辑范围(例如需要修改 PRD/架构),请明确说明,不要写入解决标记。不写入标记就退出,会让故事继续处于升级状态,并让运行保持暂停——这是安全的默认行为。对于没有记录解决方案的故事,编排器不会重新布防。

评论

支持 Markdown