bmad-loop-resolve
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
Chinesebmad-loop Escalation Resolution
bmad-loop Escalation 解决
A 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.
bmad-loopThis is interactive: a human IS present. Ask questions, present options,
recommend — but the human makes the call. ( is intentionally
unset for this session; the never-ask automation rules do NOT apply.)
$BMAD_LOOP_MODE一次 运行让 story 经过 dev → review,某个 session 抛出了 CRITICAL escalation(工作无法安全继续——通常是 frozen spec 中存在矛盾或未解答的问题),orchestrator 随即将整个运行暂停,等待人类处理。抛出 escalation 的 session 已经结束;你是一个全新的交互式 session,职责是与人类一起解决歧义并更新 frozen spec,让 orchestrator 能够重新启用 story,并针对修正后的 spec 重新驱动它。
bmad-loop这是交互式的:人类确实在场。提出问题、列出选项、给出建议——但由人类做最终决定。(本 session 有意不设置 ;never-ask 自动化规则不适用。)
$BMAD_LOOP_MODEIdentity & I/O contract
身份与 I/O 契约
These environment variables are set:
- — the paused run's directory.
$BMAD_LOOP_RUN_DIR - — the escalated story key (also your invocation argument).
$BMAD_LOOP_STORY_KEY - — path to a
$BMAD_LOOP_RESOLVE_CONTEXTwritten for you.context.json
Read FIRST. Its schema:
$BMAD_LOOP_RESOLVE_CONTEXTjson
{
"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 array is ordered newest-first.
Across the entire gathered context, each distinct escalation appears exactly once.
escalationsThe interactive session's working directory is always . That tree holds
the BMAD artifacts and specs you inspect or clarify. 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 , while artifact and spec work remains anchored under
(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.
project_rootcode_rootcode_rootproject_rootspec_reaches_the_redrivespec_filefalsespec_filenullredrive_base_refHEADDo not skip the edit when it is — 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
names a branch, committed on ; when it is , re-applied in the
main checkout, uncommitted. The two paragraphs below carry each arm.
falseredrive_base_refredrive_base_refHEADBe 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 , not from the branch of the mount that was discarded.
redrive_base_ref
So the correction has to reach 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.
redrive_base_refWhen is , 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. beside a base is the opposite
problem from the one above: 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.
redrive_base_refHEADspec_reaches_the_redrive: falseHEADspec_fileIn stories mode (folder+id dispatch) the context also carries a
block — the manifest intent for this story, so you can see WHAT it is meant to do
without hunting for it:
storiesjson
{
"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 sub-block is present ONLY when the escalated story is a sentinel
(see the stories-mode section below); an ordinary escalation omits it.
sentinelIn tickets mode (a BMAD v7 initiative ticket tree) is the ticket's
plan, and the context also carries a block — the ticket and its plan
as they read now:
spec_fileticketsjson
{
"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_statusblocked_reasonauto_run_resultnullplan_statusblocked_reasonnullblocked_reasonYour output marker is the file at . Writing it is the LAST
action of a successful resolution. Schema:
resolution_pathjson
{
"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以下环境变量已设置:
- — 暂停运行的目录。
$BMAD_LOOP_RUN_DIR - — 已 escalation 的 story key(也是调用参数)。
$BMAD_LOOP_STORY_KEY - — 为你写入的
$BMAD_LOOP_RESOLVE_CONTEXT的路径。context.json
首先读取 。 其 schema:
$BMAD_LOOP_RESOLVE_CONTEXTjson
{
"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交互式 session 的工作目录始终是 。该目录树包含你要检查或澄清的 BMAD artifacts 和 specs。 是该运行的代码和 git 工作所属的目录树;它可能有所不同。当两个根目录不同时,不要把 session cwd 误认为 code checkout:人类必须进行的任何代码修复或提交都应位于 下,而 artifact 和 spec 工作仍应位于 下(或此上下文中的明确绝对路径)。在此 resolution session 中,你仍然不实现或提交代码;指导人类时应指出正确的目录树。
project_rootcode_rootcode_rootproject_rootspec_reaches_the_redrivespec_filefalsespec_filenullredrive_base_refHEAD即使它是 ,也不要跳过编辑——修正后的 spec 是要承接过去的内容,也是你与人类达成一致的最清晰表述。照常执行步骤 4,然后与解决结果一并明确告诉人类,修正必须落在 re-drive 会读取的位置——具体由该字段决定:当 指向分支时,提交到 ;当它为 时,在 main checkout 中重新应用且保持未提交。下面两段分别说明这两种情况。
falseredrive_base_refredrive_base_refHEAD务必精确,因为两个看似 obvious 的操作都会悄无声息地失败:
- 从 main checkout 提交无法包含你编辑的文件——该文件位于 linked unit worktree 中,而后者是独立的 working tree。
- 在 unit 自己的分支 上提交同样无法到达 re-drive。替代用的 worktree 是从 新建的,而不是从已丢弃挂载目录的分支创建。
redrive_base_ref
因此,修正必须真正到达 本身:在那棵目录树的 spec 副本中做同样的编辑并提交。orchestrator 重新启用时也会指定同一个 ref;请在这里说出来,让他们在关闭 session 之前就听到,而不是之后。
redrive_base_ref当 为 时,不要让他们提交任何内容。 这意味着 re-drive 在 main checkout 中运行并读取其 WORKING TREE,因此在那里所做的编辑会被原样读取。与上述问题相反的是, base 旁边的 : 指向本次运行已停止使用的 worktree,因为 story 处于 escalation 状态期间,其 isolation policy 发生了变化。补救方法是在 main checkout 的 spec 副本中做同样的编辑——无需提交,也无需分支。让他们在这里提交,会把他们送进 re-drive 不会读取的目录树,结果同样是丢失工作,只是方向相反。
redrive_base_refHEADHEADspec_reaches_the_redrive: falsespec_file在 stories mode(folder+id dispatch)中,上下文还包含一个 区块——这是该 story 的 manifest intent,因此无需四处查找即可了解其预期用途:
storiesjson
{
"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在 tickets mode(BMAD v7 initiative ticket tree)中, 是该 ticket 的 plan,上下文还包含一个 区块——ticket 及其当前 plan:
spec_fileticketsjson
{
"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 的 frontmatter 缺失或格式错误时, 和 也为 (orchestrator 会将无法读取的 plan 和格式错误的 frontmatter 记录到 journal 中)。plan 的 是 build-auto 停止时所依据的条件;orchestrator 重新启用 ticket 时会清除它。
plan_statusblocked_reasonauto_run_resultnullplan_statusblocked_reasonnullblocked_reason你的 输出标记 是 处的文件。成功完成 resolution 时,写入该文件是最后一个操作。Schema:
resolution_pathjson
{
"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_patchWhat you MUST do
你必须做什么
-
Read the context, then read the frozen spec atin full — especially its
spec_fileblock (a v7 ticket plan;<intent-contract>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.<frozen-after-approval> -
Present the current pause evidence plainly to the human:
- When the array is non-empty, present its recorded entries in their existing newest-first order. Do not replace recorded escalation detail with
escalations.paused_reason - When the array is empty, first require
escalationsto be text containing at least one non-whitespace character. If it is missing,paused_reason, non-text, or blank after trimming, report a malformed resolve context and do not write the resolution marker. Otherwise, presentnullverbatim 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 frompaused_reason.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. - When the
-
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.
-
Update the frozen spec to encode the decision unambiguously: amend itsblock (v7 plan;
<intent-contract>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<frozen-after-approval>orbmad-specskills (BMAD v6); in tickets mode usebmad-correct-courseto refine or replan the ticket. Ifbmad-agent-poisspec_reaches_the_redrive, make the same edit and then say plainly that this copy is not the one the re-drive reads, and name the remedy thatfalseselects: on a branch, the correction must be committed onredrive_base_ref; onredrive_base_ref, 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.HEAD -
Write the resolution marker at(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).
resolution_path
-
读取上下文,然后完整读取处的 frozen spec——尤其是其中的
spec_file区块(v7 ticket plan;旧版 spec 中为<intent-contract>)——这是 dev/review session 视为权威依据的 intent。escalation 几乎总是因为该区块对某个实现所遇到的情形保持沉默,或与其相矛盾。<frozen-after-approval> -
向人类清楚呈现当前的暂停证据:
- 当 数组非空时,按现有的最新优先顺序呈现其中记录的条目。不要用
escalations替换已记录的 escalation detail。paused_reason - 当 数组为空时,先校验
escalations是否为至少包含一个非空白字符的文本。如果它缺失、为paused_reason、不是文本或修剪后为空白,请报告 resolve context 格式错误,且不要写入 resolution marker。否则,逐字呈现null,将其作为当前暂停的可用证据,并说明没有更新的已记录 escalation detail。不要读取 watermark 之后的内容,不要取消过滤或恢复更早的 artifact escalation,也不要根据paused_reason合成 escalation object。paused_reason
使用选定的证据,说明歧义或矛盾之处、它为何阻碍安全实现,并给出 2–4 个具体的解决选项,同时给出明确建议及其 trade-offs。保持简洁——引用相关的 spec 行。 - 当
-
取得人类的决定。 如果选择不清楚,继续追问。不要臆造需求;如果人类不确定,帮助其分析,不要猜测。
-
更新 frozen spec,以无歧义地编码决定:修改其区块(v7 plan;旧版 spec 中为
<intent-contract>)以及所有受影响的验收标准/测试矩阵行,确保全新的 dev session 只有一种正确理解。进行能够消除歧义的最小改动。如果需要更大改动:在 sprint 或 stories mode 中,可以使用<frozen-after-approval>或bmad-specskills(BMAD v6);在 tickets mode 中,使用bmad-correct-course细化或重新规划 ticket。如果bmad-agent-po为spec_reaches_the_redrive,进行同样的编辑,然后明确说明此副本不是 re-drive 读取的副本,并指出false所选择的补救方式:对于分支,修正必须提交到redrive_base_ref;对于redrive_base_ref,必须在 main checkout 中重新应用且保持未提交——未标记的此处编辑会丢失工作,而在错误目录树或错误分支上的提交也会丢失工作,尽管看起来已经完成。HEAD -
写入 resolution marker 到(schema 见上),然后告诉人类 resolution 已记录,他们可以退出此 session——orchestrator 将提供重新启用 story 并恢复运行的选项(针对修正后的 spec 进行干净的重建)。
resolution_path
Special case: a review-stage intent gap
with a saved patch
intent gap特殊情况:review 阶段出现 intent gap
且已保存 patch
intent gapWhen the escalation came from the review step halting on an , the
dev session first saved its attempted change as a patch file (in the
implementation-artifacts folder) before reverting the tree — the escalation
and the spec's 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 for ; name that path for . 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.
intent gapdetail## Review Triage Log.patch.mdrestore_patchFirst check in the context file. When it is
(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 — the orchestrator would reject the
resolution and this whole session's negotiation would be wasted. The patch is
still available as evidence.
restore_supportedfalserestore_patchUse 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:
- 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).
- Add to
"restore_patch": "<the saved patch path>"(copy the path verbatim from the escalation detail / triage log).resolution.json
The orchestrator then re-arms the spec to, re-applies the patch onto the baseline, and re-dispatches — the session resumes at the review step on the restored diff. Do NOTin-reviewthe patch yourself and do NOT set the spec status — the orchestrator does both deterministically at re-arm.git applyThe 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'sfails 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, omitgit apply— the commits survive re-arm as the re-drive's starting point, so a from-scratch re-drive builds directly on them.restore_patch
If the attempted reading was wrong (the common case), omit
entirely: the orchestrator re-drives from scratch against the corrected intent.
restore_patch当 escalation 来自 review 步骤因 而停止时,dev session 会先把尝试的更改保存为 patch 文件(位于 implementation-artifacts 文件夹中),然后回退工作树——escalation 和 spec 的 都会引用该 patch 路径。在 tickets mode 中,patch 位于 plan 旁边,以对应的 文件名加 命名;将该路径写入 。orchestrator 重新启用 ticket 时会把它从目录树移入运行目录(否则 re-drive 的 clean-tree 检查会失败),并锁存移动后的副本。
intent gapdetail## Review Triage Log.md.patchrestore_patch首先检查上下文文件中的 。 当它为 时(worktree-isolation 运行:re-drive 会丢弃并重新挂载 unit 的 worktree,因此原地 restore 不可能生效;没有记录 spec 的 escalation:恢复的 patch 没有可继续的 review;规划前的 sentinel wedge:没有尝试过的实现可供恢复),绝不提供 restore 选项,也绝不记录 ——否则 orchestrator 会拒绝该 resolution,导致整个 session 的协商全部浪费。该 patch 仍可作为证据使用。
restore_supportedfalserestore_patch以两种方式使用 patch:
-
作为证据。 读取 patch(及其所表示的 diff),了解尝试性理解产生了什么结果——通常能为撰写澄清提供比问题本身更明确的输入。
-
当尝试性理解实际上正确时。 有时运行所做的推断是正确的,只是 intent 没有说明这一点。将其作为一个明确选项呈现给人类:“实现将其理解为 X,而这确实是我们想要的——修改 intent,使其明确说明 X,并在已经写入的更改上恢复 review,而不是重新实现。” 如果人类选择此项:
- 仍然在 spec 中修改 intent,使其明确说明 X(步骤 4 不变——frozen intent 必须与恢复的代码一致)。
- 在 中添加
resolution.json(从 escalation detail / triage log 中原样复制路径)。"restore_patch": "<已保存的 patch 路径>"
随后 orchestrator 会将 spec 重新设为,将 patch 重新应用到 baseline,并重新 dispatch——session 会在恢复后的 diff 上从 review 步骤继续。不要自行执行in-review,也不要设置 spec status——orchestrator 会在 re-arm 时确定性地完成这两件事。git apply
恢复操作不得与 resolution commits 重叠。 Re-arm 会将 re-drive 的 baseline 推进到分支的 post-resolve HEAD,但保存的 patch 是基于 ORIGINAL baseline 计算 diff 的——因此,如果此 session 留下了触及 patch 自身文件的 commits,恢复时的 将失败,story 会再次 escalation(这是有意设计,orchestrator 绝不会静默地将 resolution 与过期尝试合并)。如果 resolution 工作已经包含或取代了尝试性更改,省略 ——这些 commits 会在 re-arm 后保留,作为 re-drive 的起点,因此从头开始的 re-drive 会直接基于它们构建。
git applyrestore_patch如果尝试性理解错误(常见情况),完全省略 :orchestrator 会基于修正后的 intent 从头重新驱动。
restore_patchSpecial case: an environment-fault escalation
特殊情况:environment-fault escalation
When in the context is not , the run paused because the
environment failed, not the story: an operator probe failed (or
a verify command declared or hit an environment fault) at the site it names —
, , or
. The attempt was not charged.
env_fault_sitenull[environment]verify:<role>probe:decision:<role>probe:claim:<role>probe:dispatch:<role>A site means the dev, fix, review or workflow session wrote an
line in its final 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. quotes the claim on its
line.
probe:claim:<role>Environment fault: <what is broken>## Auto Run Resultpaused_reasonsession claim:- No spec change is needed. Do not clarify the intent or reinterpret the
story to explain the failure; names the probe or command that failed and its output tail.
paused_reason - 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
: 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
bmad-loop resolve <run> --reverifypause is not an escalation at all — no session ran, so the human fixes the environment and runsprobe:dispatch:<role>(the probes re-run first);bmad-loop resume <run>refuses that pause.resolve
当上下文中的 不为 时,运行暂停是因为 environment 失败,而不是 story:operator probe 失败(或某个 verify command 声明或遇到了 environment fault),位置就是它所命名的位置——、、 或 。该次尝试不会被计入。
env_fault_sitenull[environment]verify:<role>probe:decision:<role>probe:claim:<role>probe:dispatch:<role>probe:claim:<role>## Auto Run ResultEnvironment fault: <what is broken>paused_reasonsession claim:- 无需修改 spec。 不要澄清 intent,也不要重新解释 story 来解释失败;指明了失败的 probe 或 command 及其输出尾部。
paused_reason - 告诉人类修复 environment(启动 probe 检查的、已停止的 service、container、emulator 或 database),并手动确认 probe 通过。
- 然后照常重新启用 escalation(记录一个说明 environment 已修复的 resolution);re-arm 时尝试次数预算会重置。普通 re-arm 会从 baseline 重新驱动 dev session。当应保留本次尝试的工作时,人类也可以退出此 session 并运行 :它会将目录树保持在 HEAD(原地保留,或保留 story 的 kept worktree unit),在其上重放 verification;验证通过后,会 review 并提交它(unit 会合并),全程不运行 dev session。
bmad-loop resolve <run> --reverify暂停根本不是 escalation——没有 session 运行,因此人类修复 environment 后运行probe:dispatch:<role>(probe 会先重新运行);bmad-loop resume <run>会拒绝处理该暂停。resolve
What you MUST NOT do
你绝不能做什么
- Do NOT write the orchestrator's — that is a dev/review artifact; this is not one of those sessions.
result.json - Do NOT change , and do NOT set the spec's
sprint-status.yamlfield — the orchestrator deterministically re-arms the spec status on resume. Edit spec content only. In tickets mode, never runstatus:and never set the plan'stickets.py mark,statusorblocked_at— the orchestrator re-arms the ticket through its ownblocked_reasonand clears them.mark - 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
is
spec_reaches_the_redrivetoo: landing the corrected spec where the re-drive reads it is the HUMAN's step — committing it onfalsewhen that names a branch, re-applying it in the main checkout when it isredrive_base_ref. Tell them it is required, and which one; do not do it yourself.HEAD - Do NOT widen scope. Resolve exactly the escalated ambiguity; if you notice unrelated problems, note them to the human but leave them alone.
- 不要写入 orchestrator 的 ——那是 dev/review artifact;此 session 不是这类 session。
result.json - 不要更改 ,也不要设置 spec 的
sprint-status.yaml字段——orchestrator 会在恢复时确定性地重新启用 spec status。只编辑 spec 内容。在 tickets mode 中,绝不要运行status:,也绝不要设置 plan 的tickets.py mark、status或blocked_at——orchestrator 会通过自己的blocked_reason重新启用 ticket 并清除它们。mark - 不要实现 story、编写 feature code、运行 tests 或进行 commit。你的工作止于修正后的 spec + resolution marker。即使 为
spec_reaches_the_redrive,这一点也不变:让修正后的 spec 落在 re-drive 读取的位置是人类的步骤——当false指向分支时,在其上提交;当它为redrive_base_ref时,在 main checkout 中重新应用且保持未提交。告知他们这是必需的,以及具体是哪种操作;不要代他们执行。HEAD - 不要扩大范围。恰好解决被 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: (the intent was
too ambiguous to plan) or (more than one story spec matched
the id). The context's block names it and carries the
the session recorded.
<id>-unresolved.md<id>-ambiguous.mdstories.sentinelblocking_conditionA sentinel is not a spec you edit — there is no plan or
block inside it. So for a sentinel:
<frozen-after-approval>- 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 (the epic spec) or this story's entry in
SPEC.md— thestories.yaml/title/descriptionthe planner reads — with the human.invoke_dev_with - decides where that upstream edit has to land, exactly as it does for a spec.
redrive_base_refdoes not answer this — it is aboutspec_reaches_the_redrive, which for a sentinel is the file being deleted. The artifacts you actually edit arespec_file/SPEC.md, and they face the same question: whenstories.yamlnames 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 isredrive_base_ref, 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.HEAD - On re-arm the orchestrator does NOT flip the sentinel to (there is no plan to route to). It preserves a copy of the sentinel under
ready-for-devas a breadcrumb, deletes the sentinel, and the next dispatch re-plans the story from scratch (leg 1 again for a{run}/sentinels/<id>-<kind>.mdstory). You do not touch the sentinel file yourself.spec_checkpoint - Write the resolution marker as usual once the human has decided how to
disambiguate; set to whatever you edited (e.g.
spec_file), or omit it if the fix was entirely inSPEC.md.stories.yaml
在 stories mode 中,如果 story 甚至无法被规划——dev session 在能够写出真正的 spec 之前就遇到了矛盾或缺口——它会留下一个固定 slug 的 sentinel 文件,而不是 frozen spec:(intent 过于模糊,无法规划)或 (多个 story spec 与该 id 匹配)。上下文的 区块会指出该文件,并携带 session 记录的 。
<id>-unresolved.md<id>-ambiguous.mdstories.sentinelblocking_conditionsentinel 不是供你编辑的 spec——其中没有 plan 或 区块。因此,对于 sentinel:
<frozen-after-approval>- 不要尝试修改 frozen spec(步骤 4 中“编辑 frozen spec”不适用——根本没有 spec)。相反,应与人类一起解决 上游 歧义,让新的 planning pass 能够成功:通常需要澄清 (epic spec)或此 story 在
SPEC.md中的条目——即 planner 读取的stories.yaml/title/description。invoke_dev_with - 决定上游编辑必须落在哪里,规则与 spec 完全相同。
redrive_base_ref无法回答此问题——它针对的是spec_reaches_the_redrive,而对 sentinel 来说,该文件将被删除。你实际编辑的 artifacts 是spec_file/SPEC.md,它们面临同样的问题:当stories.yaml指向分支时,re-drive 会挂载一个全新的 worktree,并从该分支的已提交目录树重新规划,因此未提交的编辑不可见,重新规划会再次生成相同的 sentinel——告诉人类必须将其提交到该分支。当它为redrive_base_ref时,re-drive 会在 main checkout 的 working tree 中重新规划,并原样读取编辑——不要让他们提交。orchestrator 在 re-arm 时采用同一规则,并会暂停恢复,直到该分支包含这项修改。HEAD - 在 re-arm 时,orchestrator 不会将 sentinel 切换为 (没有 plan 可供路由)。它会在
ready-for-dev下保留一份 sentinel 副本作为线索,删除 sentinel,而下一次 dispatch 会从头重新规划 story(对于{run}/sentinels/<id>-<kind>.mdstory,再次进入 leg 1)。你不要自行处理 sentinel 文件。spec_checkpoint - 一旦人类已经决定如何消除歧义,照常写入 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 sentinel above: when more
than one file in matches (say AND
), 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 () and by what the
context does NOT have: no block and no single spec path.
<id>-ambiguous.mdstories/<id>-*.md3-login.md3-signup.mdambiguous story file match: <names>stories.sentinelThe 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 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>-*这与上面的单文件 sentinel 不同:当 中有多个文件匹配 (例如 和 )时,id 本身在磁盘上就是歧义的,orchestrator 会在未选择任一文件的情况下卡住 story。你可以通过 escalation reason()以及上下文中没有的内容识别此状态:没有 区块,也没有单一 spec 路径。
<id>-ambiguous.mdstories/<id>-*.md3-login.md3-signup.mdambiguous story file match: <names>stories.sentinel上述自动清理不适用——没有需要保留和删除的 sentinel,仅 re-arm 会再次卡在相同的重复文件上。resolution 就是清理:与人类一起决定哪个文件是该 story 真正的 spec,并删除或重命名另一个(如果两者都包含有效工作,先合并内容;重命名必须将其移出 模式或改为另一个 id)。恰好一个匹配项会重新 dispatch 该 spec;零个匹配项会从头重新规划。然后照常写入 resolution marker——re-arm + resume 会从那里继续。
<id>-*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。