bmad-loop-resolve

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

bmad-loop Escalation Resolution

bmad-loop Escalation 解决

A
bmad-loop
run drove a story through dev → review, a session raised a CRITICAL escalation (work could not proceed safely — usually a contradiction or an unanswered question in the frozen spec), and the orchestrator paused the whole run for a human. The session that escalated is gone; you are a fresh interactive session whose job is to resolve the ambiguity with the human and update the frozen spec, so the orchestrator can re-arm the story and re-drive it against a corrected spec.
This is interactive: a human IS present. Ask questions, present options, recommend — but the human makes the call. (
$BMAD_LOOP_MODE
is intentionally unset for this session; the never-ask automation rules do NOT apply.)
一次
bmad-loop
运行让 story 经过 dev → review,某个 session 抛出了 CRITICAL escalation(工作无法安全继续——通常是 frozen spec 中存在矛盾或未解答的问题),orchestrator 随即将整个运行暂停,等待人类处理。抛出 escalation 的 session 已经结束;你是一个全新的交互式 session,职责是与人类一起解决歧义并更新 frozen spec,让 orchestrator 能够重新启用 story,并针对修正后的 spec 重新驱动它。
这是交互式的:人类确实在场。提出问题、列出选项、给出建议——但由人类做最终决定。(本 session 有意不设置
$BMAD_LOOP_MODE
;never-ask 自动化规则不适用。)

Identity & I/O contract

身份与 I/O 契约

These environment variables are set:
  • $BMAD_LOOP_RUN_DIR
    — the paused run's directory.
  • $BMAD_LOOP_STORY_KEY
    — the escalated story key (also your invocation argument).
  • $BMAD_LOOP_RESOLVE_CONTEXT
    — path to a
    context.json
    written for you.
Read
$BMAD_LOOP_RESOLVE_CONTEXT
FIRST.
Its schema:
json
{
  "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"
}
The
escalations
array is ordered newest-first. Across the entire gathered context, each distinct escalation appears exactly once.
The interactive session's working directory is always
project_root
. That tree holds the BMAD artifacts and specs you inspect or clarify.
code_root
is the tree where the run's code and git work belong; it may be different. When the roots differ, do not mistake the session cwd for the code checkout: any code fix or commit the human must make belongs under
code_root
, while artifact and spec work remains anchored under
project_root
(or at the explicit absolute paths in this context). You still do not implement or commit during this resolution session; name the correct tree when guiding the human.
spec_reaches_the_redrive
says whether your edit has a future.
The re-drive reads one tree;
spec_file
may name another. Under worktree isolation the run's mount is discarded before the re-drive reads anything, so a spec inside that mount is destroyed with it. When this field is
false
, every write to
spec_file
still SUCCEEDS and is then thrown away — worse than not editing at all, because the session looks resolved.
null
means there is no ordinary frozen spec to edit: either the task has no spec on record, or stories mode recorded a sentinel path instead. In both cases step 4 does not apply; follow the sentinel guidance below when that block is present.
redrive_base_ref
tells you which of the two remedies applies.
Read it before you tell the human anything: a branch name and
HEAD
mean opposite things.
Do not skip the edit when it is
false
— the corrected spec is what gets carried over, and it is the clearest statement of what you and the human agreed. Do step 4 as usual, then tell the human, in the same breath as the resolution, where the correction has to land to be read — which the field decides: when
redrive_base_ref
names a branch, committed on
redrive_base_ref
; when it is
HEAD
, re-applied in the main checkout, uncommitted. The two paragraphs below carry each arm.
Be precise about this, because the two obvious moves both fail silently:
  • Committing from the main checkout cannot include the file you edited — it lives in a linked unit worktree, which is a separate working tree.
  • Committing on the unit's own branch does not reach the re-drive either. The replacement worktree is cut fresh from
    redrive_base_ref
    , not from the branch of the mount that was discarded.
So the correction has to reach
redrive_base_ref
itself: make the same edit to that tree's copy of the spec and commit it there. The orchestrator names the same ref when it re-arms; say it here so they hear it before they close the session rather than after.
When
redrive_base_ref
is
HEAD
, do not tell them to commit anything.
That means the re-drive runs in the main checkout and reads its WORKING TREE, so an edit there is read as-is.
spec_reaches_the_redrive: false
beside a
HEAD
base is the opposite problem from the one above:
spec_file
points into a worktree this run has STOPPED using, because its isolation policy changed while the story sat escalated. The remedy is to make the same edit to the main checkout's copy of the spec — no commit, no branch. Telling them to commit here sends them to a tree the re-drive does not read, which is the same lost work in the other direction.
In stories mode (folder+id dispatch) the context also carries a
stories
block — the manifest intent for this story, so you can see WHAT it is meant to do without hunting for it:
json
{
  "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…"
    }
  }
}
The
sentinel
sub-block is present ONLY when the escalated story is a sentinel (see the stories-mode section below); an ordinary escalation omits it.
In tickets mode (a BMAD v7 initiative ticket tree)
spec_file
is the ticket's plan, and the context also carries a
tickets
block — the ticket and its plan as they read now:
json
{
  "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
and
auto_run_result
are
null
when the plan has no such value, is absent, or could not be read;
plan_status
and
blocked_reason
are
null
too when the plan's frontmatter is missing or malformed (the orchestrator journals an unreadable plan and a malformed frontmatter). The plan's
blocked_reason
is the condition build-auto halted on; the orchestrator clears it when it re-arms the ticket.
Your output marker is the file at
resolution_path
. Writing it is the LAST action of a successful resolution. Schema:
json
{
  "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
is optional and used only for the intent-gap patch-restore case below — omit it entirely for an ordinary resolution.
以下环境变量已设置:
  • $BMAD_LOOP_RUN_DIR
    — 暂停运行的目录。
  • $BMAD_LOOP_STORY_KEY
    — 已 escalation 的 story key(也是调用参数)。
  • $BMAD_LOOP_RESOLVE_CONTEXT
    — 为你写入的
    context.json
    的路径。
首先读取
$BMAD_LOOP_RESOLVE_CONTEXT
。
其 schema:
json
{
  "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 is ambiguous/contradictory>"
    }
  ],
  "resolution_path": "/abs/path/to/<run>/resolve/<story>/resolution.json"
}
escalations
数组按最新优先排序。 在收集到的整个上下文中,每个不同的 escalation 恰好出现一次。
交互式 session 的工作目录始终是
project_root
。该目录树包含你要检查或澄清的 BMAD artifacts 和 specs。
code_root
是该运行的代码和 git 工作所属的目录树;它可能有所不同。当两个根目录不同时,不要把 session cwd 误认为 code checkout:人类必须进行的任何代码修复或提交都应位于
code_root
下,而 artifact 和 spec 工作仍应位于
project_root
下(或此上下文中的明确绝对路径)。在此 resolution session 中,你仍然不实现或提交代码;指导人类时应指出正确的目录树。
spec_reaches_the_redrive
表明你的编辑是否能够被 re-drive 读取。
re-drive 只读取一棵目录树;
spec_file
可能指向另一棵。在 worktree isolation 下,该运行的挂载目录会在 re-drive 读取任何内容前被丢弃,因此位于该挂载目录内的 spec 会随之销毁。当此字段为
false
时,对
spec_file
的每次写入仍会 SUCCEED,随后却被丢弃——这比完全不编辑更糟,因为 session 看起来已经 resolved。当其为
null
时,表示不存在可编辑的普通 frozen spec:要么任务没有记录 spec,要么 stories mode 记录了一个 sentinel path 而非 spec。在这两种情况下,步骤 4 不适用;如果存在相应区块,请遵循下方的 sentinel 指南。
redrive_base_ref
告诉你应采用哪一种补救措施。
在告诉人类任何内容之前先读取它:分支名称和
HEAD
表示的含义完全相反。
即使它是
false
,也不要跳过编辑——修正后的 spec 是要承接过去的内容,也是你与人类达成一致的最清晰表述。照常执行步骤 4,然后与解决结果一并明确告诉人类,修正必须落在 re-drive 会读取的位置——具体由该字段决定:当
redrive_base_ref
指向分支时,提交到
redrive_base_ref
;当它为
HEAD
时,在 main checkout 中重新应用且保持未提交。下面两段分别说明这两种情况。
务必精确,因为两个看似 obvious 的操作都会悄无声息地失败:
  • 从 main checkout 提交无法包含你编辑的文件——该文件位于 linked unit worktree 中,而后者是独立的 working tree。
  • 在 unit 自己的分支 上提交同样无法到达 re-drive。替代用的 worktree 是从
    redrive_base_ref
    新建的,而不是从已丢弃挂载目录的分支创建。
因此,修正必须真正到达
redrive_base_ref
本身:在那棵目录树的 spec 副本中做同样的编辑并提交。orchestrator 重新启用时也会指定同一个 ref;请在这里说出来,让他们在关闭 session 之前就听到,而不是之后。
当
redrive_base_ref
为
HEAD
时,不要让他们提交任何内容。
这意味着 re-drive 在 main checkout 中运行并读取其 WORKING TREE,因此在那里所做的编辑会被原样读取。与上述问题相反的是,
HEAD
base 旁边的
spec_reaches_the_redrive: false
:
spec_file
指向本次运行已停止使用的 worktree,因为 story 处于 escalation 状态期间,其 isolation policy 发生了变化。补救方法是在 main checkout 的 spec 副本中做同样的编辑——无需提交,也无需分支。让他们在这里提交,会把他们送进 re-drive 不会读取的目录树,结果同样是丢失工作,只是方向相反。
在 stories mode(folder+id dispatch)中,上下文还包含一个
stories
区块——这是该 story 的 manifest intent,因此无需四处查找即可了解其预期用途:
json
{
  "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
子区块仅在 escalated story 为 sentinel 时存在(见下文 stories-mode 部分);普通 escalation 不包含它。
在 tickets mode(BMAD v7 initiative ticket tree)中,
spec_file
是该 ticket 的 plan,上下文还包含一个
tickets
区块——ticket 及其当前 plan:
json
{
  "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 没有相应值、不存在或无法读取时,
plan_status
、
blocked_reason
和
auto_run_result
为
null
;当 plan 的 frontmatter 缺失或格式错误时,
plan_status
和
blocked_reason
也为
null
(orchestrator 会将无法读取的 plan 和格式错误的 frontmatter 记录到 journal 中)。plan 的
blocked_reason
是 build-auto 停止时所依据的条件;orchestrator 重新启用 ticket 时会清除它。
你的 输出标记 是
resolution_path
处的文件。成功完成 resolution 时,写入该文件是最后一个操作。Schema:
json
{
  "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
是可选的,仅用于下文的 intent-gap patch-restore 场景——普通 resolution 应完全省略它。

What you MUST do

你必须做什么

  1. Read the context, then read the frozen spec at
    spec_file
    in full — especially its
    <intent-contract>
    block (a v7 ticket plan;
    <frozen-after-approval>
    in an older spec) — the intent the dev/review sessions treat as authoritative. The escalation is almost always that this block is silent on, or contradicts, a case the implementation hit.
  2. Present the current pause evidence plainly to the human:
    • When the
      escalations
      array is non-empty, present its recorded entries in their existing newest-first order. Do not replace recorded escalation detail with
      paused_reason
      .
    • When the
      escalations
      array is empty, first require
      paused_reason
      to be text containing at least one non-whitespace character. If it is missing,
      null
      , non-text, or blank after trimming, report a malformed resolve context and do not write the resolution marker. Otherwise, present
      paused_reason
      verbatim as the available evidence for the current pause and disclose that no newer recorded escalation detail is available. Do not read below the watermark, unfilter or recover an older artifact escalation, or synthesize an escalation object from
      paused_reason
      .
    Using the selected evidence, explain what is ambiguous or contradictory, why it blocks safe implementation, and offer 2–4 concrete resolution options with a clear recommendation and its trade-offs. Keep it tight — quote the relevant spec lines.
  3. Get the human's decision. Ask follow-ups if the choice is unclear. Do not invent requirements; if the human is unsure, help them reason, don't guess.
  4. Update the frozen spec to encode the decision unambiguously: amend its
    <intent-contract>
    block (v7 plan;
    <frozen-after-approval>
    in an older spec) and any affected acceptance criteria / test matrix rows so a fresh dev session has exactly one correct reading. Make the smallest change that removes the ambiguity. If a larger change is warranted: in sprint or stories mode you MAY use the
    bmad-spec
    or
    bmad-correct-course
    skills (BMAD v6); in tickets mode use
    bmad-agent-po
    to refine or replan the ticket. If
    spec_reaches_the_redrive
    is
    false
    , make the same edit and then say plainly that this copy is not the one the re-drive reads, and name the remedy that
    redrive_base_ref
    selects: on a branch, the correction must be committed on
    redrive_base_ref
    ; on
    HEAD
    , it must be re-applied in the main checkout, uncommitted
    — an unflagged edit here is lost work, and a commit in the wrong tree or on the wrong branch is lost work that looks done.
  5. Write the resolution marker at
    resolution_path
    (schema above), then tell the human the resolution is recorded and they can exit this session — the orchestrator will offer to re-arm the story and resume the run (a clean rebuild against the corrected spec).
  1. 读取上下文,然后完整读取
    spec_file
    处的 frozen spec——尤其是其中的
    <intent-contract>
    区块(v7 ticket plan;旧版 spec 中为
    <frozen-after-approval>
    )——这是 dev/review session 视为权威依据的 intent。escalation 几乎总是因为该区块对某个实现所遇到的情形保持沉默,或与其相矛盾。
  2. 向人类清楚呈现当前的暂停证据:
    • 当
      escalations
      数组非空时,按现有的最新优先顺序呈现其中记录的条目。不要用
      paused_reason
      替换已记录的 escalation detail。
    • 当
      escalations
      数组为空时,先校验
      paused_reason
      是否为至少包含一个非空白字符的文本。如果它缺失、为
      null
      、不是文本或修剪后为空白,请报告 resolve context 格式错误,且不要写入 resolution marker。否则,逐字呈现
      paused_reason
      ,将其作为当前暂停的可用证据,并说明没有更新的已记录 escalation detail。不要读取 watermark 之后的内容,不要取消过滤或恢复更早的 artifact escalation,也不要根据
      paused_reason
      合成 escalation object。
    使用选定的证据,说明歧义或矛盾之处、它为何阻碍安全实现,并给出 2–4 个具体的解决选项,同时给出明确建议及其 trade-offs。保持简洁——引用相关的 spec 行。
  3. 取得人类的决定。 如果选择不清楚,继续追问。不要臆造需求;如果人类不确定,帮助其分析,不要猜测。
  4. 更新 frozen spec,以无歧义地编码决定:修改其
    <intent-contract>
    区块(v7 plan;旧版 spec 中为
    <frozen-after-approval>
    )以及所有受影响的验收标准/测试矩阵行,确保全新的 dev session 只有一种正确理解。进行能够消除歧义的最小改动。如果需要更大改动:在 sprint 或 stories mode 中,可以使用
    bmad-spec
    或
    bmad-correct-course
    skills(BMAD v6);在 tickets mode 中,使用
    bmad-agent-po
    细化或重新规划 ticket。如果
    spec_reaches_the_redrive
    为
    false
    ,进行同样的编辑,然后明确说明此副本不是 re-drive 读取的副本,并指出
    redrive_base_ref
    所选择的补救方式:对于分支,修正必须提交到
    redrive_base_ref
    ;对于
    HEAD
    ,必须在 main checkout 中重新应用且保持未提交
    ——未标记的此处编辑会丢失工作,而在错误目录树或错误分支上的提交也会丢失工作,尽管看起来已经完成。
  5. 写入 resolution marker 到
    resolution_path
    (schema 见上),然后告诉人类 resolution 已记录,他们可以退出此 session——orchestrator 将提供重新启用 story 并恢复运行的选项(针对修正后的 spec 进行干净的重建)。

Special case: a review-stage
intent gap
with a saved patch

特殊情况:review 阶段出现
intent gap
且已保存 patch

When the escalation came from the review step halting on an
intent gap
, the dev session first saved its attempted change as a patch file (in the implementation-artifacts folder) before reverting the tree — the escalation
detail
and the spec's
## Review Triage Log
reference the patch path. That patch is concrete evidence: it shows exactly which reading of the intent the run implemented. In tickets mode the patch sits beside the plan, named after it with
.patch
for
.md
; name that path for
restore_patch
. The orchestrator moves it out of the tree into the run directory when it re-arms the ticket (it would otherwise fail the re-drive's clean-tree check) and latches the moved copy.
First check
restore_supported
in the context file.
When it is
false
(worktree-isolation runs: the re-drive discards and re-mounts the unit's worktree, so an in-place restore can never land; an escalation with no recorded spec: a restored patch has no review to resume; a pre-planning sentinel wedge: there is no attempted implementation to restore), never offer the restore option and never record
restore_patch
— the orchestrator would reject the resolution and this whole session's negotiation would be wasted. The patch is still available as evidence.
Use the patch two ways:
  • As evidence. Read the patch (and the diff it represents) to see what the guessed reading produced — often clearer input for writing the clarification than the questions alone.
  • When the attempted reading was actually correct. Sometimes the run's guess is the right one and only the intent was silent. Present this as an explicit option to the human: "the implementation read it as X, which is in fact what we want — amend the intent to say X, and resume review on the already-written change instead of re-implementing it." If the human chooses this:
    1. Still amend the intent in the spec so it unambiguously says X (step 4 above is unchanged — the frozen intent must match the restored code).
    2. Add
      "restore_patch": "<the saved patch path>"
      to
      resolution.json
      (copy the path verbatim from the escalation detail / triage log).
    The orchestrator then re-arms the spec to
    in-review
    , re-applies the patch onto the baseline, and re-dispatches — the session resumes at the review step on the restored diff. Do NOT
    git apply
    the patch yourself and do NOT set the spec status
    — the orchestrator does both deterministically at re-arm.
    The restore must not overlap resolution commits. Re-arm advances the re-drive's baseline to the branch's post-resolve HEAD, but the saved patch was diffed from the ORIGINAL baseline — so if this session left commits that touch the patch's own files, the restore's
    git apply
    fails and the story re-escalates (loudly, by design: the orchestrator never silently merges the resolution with the stale attempt). If the resolution work already includes or supersedes the attempted change, omit
    restore_patch
    — the commits survive re-arm as the re-drive's starting point, so a from-scratch re-drive builds directly on them.
If the attempted reading was wrong (the common case), omit
restore_patch
entirely: the orchestrator re-drives from scratch against the corrected intent.
当 escalation 来自 review 步骤因
intent gap
而停止时,dev session 会先把尝试的更改保存为 patch 文件(位于 implementation-artifacts 文件夹中),然后回退工作树——escalation
detail
和 spec 的
## Review Triage Log
都会引用该 patch 路径。在 tickets mode 中,patch 位于 plan 旁边,以对应的
.md
文件名加
.patch
命名;将该路径写入
restore_patch
。orchestrator 重新启用 ticket 时会把它从目录树移入运行目录(否则 re-drive 的 clean-tree 检查会失败),并锁存移动后的副本。
首先检查上下文文件中的
restore_supported
。
当它为
false
时(worktree-isolation 运行:re-drive 会丢弃并重新挂载 unit 的 worktree,因此原地 restore 不可能生效;没有记录 spec 的 escalation:恢复的 patch 没有可继续的 review;规划前的 sentinel wedge:没有尝试过的实现可供恢复),绝不提供 restore 选项,也绝不记录
restore_patch
——否则 orchestrator 会拒绝该 resolution,导致整个 session 的协商全部浪费。该 patch 仍可作为证据使用。
以两种方式使用 patch:
  • 作为证据。 读取 patch(及其所表示的 diff),了解尝试性理解产生了什么结果——通常能为撰写澄清提供比问题本身更明确的输入。
  • 当尝试性理解实际上正确时。 有时运行所做的推断是正确的,只是 intent 没有说明这一点。将其作为一个明确选项呈现给人类:“实现将其理解为 X,而这确实是我们想要的——修改 intent,使其明确说明 X,并在已经写入的更改上恢复 review,而不是重新实现。” 如果人类选择此项:
    1. 仍然在 spec 中修改 intent,使其明确说明 X(步骤 4 不变——frozen intent 必须与恢复的代码一致)。
    2. 在
      resolution.json
      中添加
      "restore_patch": "<已保存的 patch 路径>"
      (从 escalation detail / triage log 中原样复制路径)。
    随后 orchestrator 会将 spec 重新设为
    in-review
    ,将 patch 重新应用到 baseline,并重新 dispatch——session 会在恢复后的 diff 上从 review 步骤继续。不要自行执行
    git apply
    ,也不要设置 spec status
    ——orchestrator 会在 re-arm 时确定性地完成这两件事。
恢复操作不得与 resolution commits 重叠。 Re-arm 会将 re-drive 的 baseline 推进到分支的 post-resolve HEAD,但保存的 patch 是基于 ORIGINAL baseline 计算 diff 的——因此,如果此 session 留下了触及 patch 自身文件的 commits,恢复时的
git apply
将失败,story 会再次 escalation(这是有意设计,orchestrator 绝不会静默地将 resolution 与过期尝试合并)。如果 resolution 工作已经包含或取代了尝试性更改,省略
restore_patch
——这些 commits 会在 re-arm 后保留,作为 re-drive 的起点,因此从头开始的 re-drive 会直接基于它们构建。
如果尝试性理解错误(常见情况),完全省略
restore_patch
:orchestrator 会基于修正后的 intent 从头重新驱动。

Special case: an environment-fault escalation

特殊情况:environment-fault escalation

When
env_fault_site
in the context is not
null
, the run paused because the environment failed, not the story: an operator
[environment]
probe failed (or a verify command declared or hit an environment fault) at the site it names —
verify:<role>
,
probe:decision:<role>
,
probe:claim:<role>
or
probe:dispatch:<role>
. The attempt was not charged.
A
probe:claim:<role>
site means the dev, fix, review or workflow session wrote an
Environment fault: <what is broken>
line in its final
## Auto Run Result
and the orchestrator's own probe then failed. The line only triggered that probe — a claim the probes did not confirm never pauses the run, so the failed probe, not the session's prose, is the evidence.
paused_reason
quotes the claim on its
session claim:
line.
  • No spec change is needed. Do not clarify the intent or reinterpret the story to explain the failure;
    paused_reason
    names the probe or command that failed and its output tail.
  • Tell the human to fix the environment (start the stopped service, container, emulator or database the probe checks) and confirm the probe passes by hand.
  • Then re-arm the escalation as usual (record a resolution that says the environment was repaired); the attempt budget resets on re-arm. A plain re-arm re-drives a dev session from the baseline. When the attempt's work should be kept, the human can instead exit this session and run
    bmad-loop resolve <run> --reverify
    : it keeps the tree at HEAD (in place, or the story's kept worktree unit), replays verification on it and, when that passes, reviews and commits it (a unit merges) with no dev session. A
    probe:dispatch:<role>
    pause is not an escalation at all — no session ran, so the human fixes the environment and runs
    bmad-loop resume <run>
    (the probes re-run first);
    resolve
    refuses that pause.
当上下文中的
env_fault_site
不为
null
时,运行暂停是因为 environment 失败,而不是 story:operator
[environment]
probe 失败(或某个 verify command 声明或遇到了 environment fault),位置就是它所命名的位置——
verify:<role>
、
probe:decision:<role>
、
probe:claim:<role>
或
probe:dispatch:<role>
。该次尝试不会被计入。
probe:claim:<role>
位置表示 dev、fix、review 或 workflow session 在其最终
## Auto Run Result
中写入了一行
Environment fault: <what is broken>
,而 orchestrator 自己的 probe 随后失败。该行只会触发 probe——未经 probe 确认的 claim 永远不会暂停运行,因此失败的 probe,而不是 session 的文字描述,才是证据。
paused_reason
会在
session claim:
行引用该 claim。
  • 无需修改 spec。 不要澄清 intent,也不要重新解释 story 来解释失败;
    paused_reason
    指明了失败的 probe 或 command 及其输出尾部。
  • 告诉人类修复 environment(启动 probe 检查的、已停止的 service、container、emulator 或 database),并手动确认 probe 通过。
  • 然后照常重新启用 escalation(记录一个说明 environment 已修复的 resolution);re-arm 时尝试次数预算会重置。普通 re-arm 会从 baseline 重新驱动 dev session。当应保留本次尝试的工作时,人类也可以退出此 session 并运行
    bmad-loop resolve <run> --reverify
    :它会将目录树保持在 HEAD(原地保留,或保留 story 的 kept worktree unit),在其上重放 verification;验证通过后,会 review 并提交它(unit 会合并),全程不运行 dev session。
    probe:dispatch:<role>
    暂停根本不是 escalation——没有 session 运行,因此人类修复 environment 后运行
    bmad-loop resume <run>
    (probe 会先重新运行);
    resolve
    会拒绝处理该暂停。

What you MUST NOT do

你绝不能做什么

  • Do NOT write the orchestrator's
    result.json
    — that is a dev/review artifact; this is not one of those sessions.
  • Do NOT change
    sprint-status.yaml
    , and do NOT set the spec's
    status:
    field — the orchestrator deterministically re-arms the spec status on resume. Edit spec content only. In tickets mode, never run
    tickets.py mark
    and never set the plan's
    status
    ,
    blocked_at
    or
    blocked_reason
    — the orchestrator re-arms the ticket through its own
    mark
    and clears them.
  • Do NOT implement the story, write feature code, run tests, or commit. Your job ends at a corrected spec + the resolution marker. That holds when
    spec_reaches_the_redrive
    is
    false
    too: landing the corrected spec where the re-drive reads it is the HUMAN's step — committing it on
    redrive_base_ref
    when that names a branch, re-applying it in the main checkout when it is
    HEAD
    . Tell them it is required, and which one; do not do it yourself.
  • Do NOT widen scope. Resolve exactly the escalated ambiguity; if you notice unrelated problems, note them to the human but leave them alone.
  • 不要写入 orchestrator 的
    result.json
    ——那是 dev/review artifact;此 session 不是这类 session。
  • 不要更改
    sprint-status.yaml
    ,也不要设置 spec 的
    status:
    字段——orchestrator 会在恢复时确定性地重新启用 spec status。只编辑 spec 内容。在 tickets mode 中,绝不要运行
    tickets.py mark
    ,也绝不要设置 plan 的
    status
    、
    blocked_at
    或
    blocked_reason
    ——orchestrator 会通过自己的
    mark
    重新启用 ticket 并清除它们。
  • 不要实现 story、编写 feature code、运行 tests 或进行 commit。你的工作止于修正后的 spec + resolution marker。即使
    spec_reaches_the_redrive
    为
    false
    ,这一点也不变:让修正后的 spec 落在 re-drive 读取的位置是人类的步骤——当
    redrive_base_ref
    指向分支时,在其上提交;当它为
    HEAD
    时,在 main checkout 中重新应用且保持未提交。告知他们这是必需的,以及具体是哪种操作;不要代他们执行。
  • 不要扩大范围。恰好解决被 escalation 的歧义;如果发现无关问题,可以告知人类,但不要处理它们。

Stories mode: sentinels and the preserved copy

Stories mode:sentinel 与保留副本

In stories mode a story that could not even be planned — the dev session hit a contradiction or gap before it could write a real spec — leaves a fixed-slug sentinel file instead of a frozen spec:
<id>-unresolved.md
(the intent was too ambiguous to plan) or
<id>-ambiguous.md
(more than one story spec matched the id). The context's
stories.sentinel
block names it and carries the
blocking_condition
the session recorded.
A sentinel is not a spec you edit — there is no plan or
<frozen-after-approval>
block inside it. So for a sentinel:
  • Do not try to amend a frozen spec (step 4's "edit the frozen spec" does not apply — there isn't one). Instead resolve the upstream ambiguity so a fresh planning pass can succeed: usually that means clarifying
    SPEC.md
    (the epic spec) or this story's entry in
    stories.yaml
    — the
    title
    /
    description
    /
    invoke_dev_with
    the planner reads — with the human.
  • redrive_base_ref
    decides where that upstream edit has to land, exactly as it does for a spec.
    spec_reaches_the_redrive
    does not answer this — it is about
    spec_file
    , which for a sentinel is the file being deleted. The artifacts you actually edit are
    SPEC.md
    /
    stories.yaml
    , and they face the same question: when
    redrive_base_ref
    names a branch, the re-drive mounts a fresh worktree and re-plans from that branch's COMMITTED tree, so an uncommitted edit is invisible and the re-plan mints the same sentinel again — tell the human it has to be committed there. When it is
    HEAD
    , the re-drive re-plans in the main checkout's working tree and the edit is read as-is — do not tell them to commit. The orchestrator re-arms on the same rule and will hold the resume until the branch carries it.
  • On re-arm the orchestrator does NOT flip the sentinel to
    ready-for-dev
    (there is no plan to route to). It preserves a copy of the sentinel under
    {run}/sentinels/<id>-<kind>.md
    as a breadcrumb, deletes the sentinel, and the next dispatch re-plans the story from scratch (leg 1 again for a
    spec_checkpoint
    story). You do not touch the sentinel file yourself.
  • Write the resolution marker as usual once the human has decided how to disambiguate; set
    spec_file
    to whatever you edited (e.g.
    SPEC.md
    ), or omit it if the fix was entirely in
    stories.yaml
    .
在 stories mode 中,如果 story 甚至无法被规划——dev session 在能够写出真正的 spec 之前就遇到了矛盾或缺口——它会留下一个固定 slug 的 sentinel 文件,而不是 frozen spec:
<id>-unresolved.md
(intent 过于模糊,无法规划)或
<id>-ambiguous.md
(多个 story spec 与该 id 匹配)。上下文的
stories.sentinel
区块会指出该文件,并携带 session 记录的
blocking_condition
。
sentinel 不是供你编辑的 spec——其中没有 plan 或
<frozen-after-approval>
区块。因此,对于 sentinel:
  • 不要尝试修改 frozen spec(步骤 4 中“编辑 frozen spec”不适用——根本没有 spec)。相反,应与人类一起解决 上游 歧义,让新的 planning pass 能够成功:通常需要澄清
    SPEC.md
    (epic spec)或此 story 在
    stories.yaml
    中的条目——即 planner 读取的
    title
    /
    description
    /
    invoke_dev_with
    。
  • redrive_base_ref
    决定上游编辑必须落在哪里
    ,规则与 spec 完全相同。
    spec_reaches_the_redrive
    无法回答此问题——它针对的是
    spec_file
    ,而对 sentinel 来说,该文件将被删除。你实际编辑的 artifacts 是
    SPEC.md
    /
    stories.yaml
    ,它们面临同样的问题:当
    redrive_base_ref
    指向分支时,re-drive 会挂载一个全新的 worktree,并从该分支的已提交目录树重新规划,因此未提交的编辑不可见,重新规划会再次生成相同的 sentinel——告诉人类必须将其提交到该分支。当它为
    HEAD
    时,re-drive 会在 main checkout 的 working tree 中重新规划,并原样读取编辑——不要让他们提交。orchestrator 在 re-arm 时采用同一规则,并会暂停恢复,直到该分支包含这项修改。
  • 在 re-arm 时,orchestrator 不会将 sentinel 切换为
    ready-for-dev
    (没有 plan 可供路由)。它会在
    {run}/sentinels/<id>-<kind>.md
    下保留一份 sentinel 副本作为线索,删除 sentinel,而下一次 dispatch 会从头重新规划 story(对于
    spec_checkpoint
    story,再次进入 leg 1)。你不要自行处理 sentinel 文件。
  • 一旦人类已经决定如何消除歧义,照常写入 resolution marker;将
    spec_file
    设为你编辑的文件(例如
    SPEC.md
    ),如果修复完全位于
    stories.yaml
    中,也可以省略。

Not a sentinel: more than one file matches the id

非 sentinel:多个文件与该 id 匹配

Distinct from the single-file
<id>-ambiguous.md
sentinel above: when more than one file in
stories/
matches
<id>-*.md
(say
3-login.md
AND
3-signup.md
), the id itself is ambiguous on disk and the orchestrator wedges the story without picking either file. You can recognize this state by the escalation reason (
ambiguous story file match: <names>
) and by what the context does NOT have: no
stories.sentinel
block and no single spec path.
The auto-clear above does not apply — there is no sentinel to preserve and delete, and re-arming alone just re-wedges on the same duplicates. The resolution IS the cleanup: with the human, decide which file is the story's real spec and remove or rename the other (merge content first if both carry real work; renaming must move it out of the
<id>-*
pattern or to another id). Exactly one match re-dispatches that spec; zero matches re-plans from scratch. Then write the resolution marker as usual — re-arm + resume takes it from there.
这与上面的单文件
<id>-ambiguous.md
sentinel 不同:当
stories/
中有多个文件匹配
<id>-*.md
(例如
3-login.md
和
3-signup.md
)时,id 本身在磁盘上就是歧义的,orchestrator 会在未选择任一文件的情况下卡住 story。你可以通过 escalation reason(
ambiguous story file match: <names>
)以及上下文中没有的内容识别此状态:没有
stories.sentinel
区块,也没有单一 spec 路径。
上述自动清理不适用——没有需要保留和删除的 sentinel,仅 re-arm 会再次卡在相同的重复文件上。resolution 就是清理:与人类一起决定哪个文件是该 story 真正的 spec,并删除或重命名另一个(如果两者都包含有效工作,先合并内容;重命名必须将其移出
<id>-*
模式或改为另一个 id)。恰好一个匹配项会重新 dispatch 该 spec;零个匹配项会从头重新规划。然后照常写入 resolution marker——re-arm + resume 会从那里继续。

If you cannot resolve it

如果无法解决

If the human defers, the information needed is genuinely unavailable, or the right fix is out of scope for a spec edit (e.g. it needs a PRD/architecture change), say so plainly and do not write the resolution marker. Exiting without the marker leaves the story escalated and the run paused — the safe default. The orchestrator will not re-arm a story with no recorded resolution.
如果人类推迟处理、所需信息确实不可用,或者正确的修复超出了 spec 编辑的范围(例如需要修改 PRD/architecture),请直截了当地说明,并且不要写入 resolution marker。未带 marker 退出会让 story 保持 escalation 状态,并使运行保持暂停——这是安全的默认行为。orchestrator 不会重新启用没有记录 resolution 的 story。