tickets: an injected plan_halt result for a built plan escalates with stopped-attempt wording
Los mantenedores suelen responder en 1 día
Nadie ha tomado este issue todavía.
Evaluación
- Dificultad
- 4/5
- Tiempo estimado
- 3-5 días
- Aptitud para principiantes
- 36/100
Línea de trabajo
Start with the plan-halt flow in src/bmad_loop/tickets_engine.py around _drive_story and the result handling at lines 1418-1437; then compare the expected status check in src/bmad_loop/verify.py with result synthesis in src/bmad_loop/devcontract.py. Reproduce with the supplied MockAdapter script and compare the injected plan_halt case with overrun-synth. Done means a supported adapter or reproducible race is identified, or the inconsistent result gets a clearer diagnostic without weakening verification.
Escrito por el modelo de indexación a partir del texto del issue.
Descripción
Status (needs confirmation)
- Observed (3/3, engine seam): for an unrefined
plan_checkpointticket, a leg-1 session (aMockAdapterresult injected by the script) that builds and commits pastHalt after planning.behaves as follows:- without
plan_haltin its result, the run pausesplan-checkpointwith "plan review owed for 1.1: the skill implemented before the plan checkpoint" (the dedicated path); - with
"plan_halt": truein its result, it pausesescalationwith "ACTION REQUIRED — manual recovery needed (committed work present) … attempt was stopped with auto-rollback OFF". Thedev-decisionisretrywith "spec status is 'built', expected 'ready-for-dev'".
- without
- Observed control: a leg 1 that honours the halt (plan at
ready-for-dev,plan_halt: true, nothing committed) pausesplan-checkpoint"plan checkpoint for 1.1: review the spec, then resume". - Measured additional control: production
devcontract.synthesize_result(..., plan_halt=True, success_statuses=("built",))omitsplan_haltfor the same built plan, and the engine then pauses with "plan review owed". It setsplan_haltforready-for-dev. A live build-auto/GenericDevAdapter run was not performed, and no operator path producing the contradictory marker has been demonstrated. - Still needed: demonstrate a supported adapter or a reproducible result/plan race that supplies
plan_halt: truewhile the bound plan isbuilt. A skill merely ignoring the halt does not establish that precondition. If you have seen such a result from a real session, please confirm here with the result JSON and the pause stage and message.
Summary
With an injected MockAdapter result containing plan_halt: true and a committed built plan, verification rejects the plan's status and the run pauses for manual recovery. The same built plan processed by production result synthesis reaches the dedicated "plan review owed" pause. No confirmed normal build-auto failure is shown. The guide's promise holds in every row (no bmad-loop commit happens and the run pauses); what differs is the pause: with the flag set, the overrun becomes a failed attempt and an escalation that says the attempt "was stopped" and asks for manual recovery of committed work, instead of the "plan review owed" pause that TicketsEngine._drive_story has for an overrun.
Environment
bmad-loop a5a8f097, Windows 11 10.0.26200, PowerShell 7.6.6 (SHELL unset), Python 3.13.2; TicketsEngine with MockAdapter as an engine seam (not a live generic-adapter validation; no coding CLI); Linux: not run, platform parity unmeasured.
Steps to reproduce
From a bmad-loop checkout at a5a8f097 after uv sync --all-extras, in PowerShell 7 with SHELL removed. Save the script outside the checkout as $env:TEMP\overrun-repro\repro_overrun.py, then run from the checkout root (the script imports the test helpers from tests). Each run creates its sandbox under $env:TEMP.
Remove-Item Env:SHELL -ErrorAction SilentlyContinue
$env:PYTHONUTF8 = '1'
$s = "$env:TEMP\overrun-repro\repro_overrun.py"
uv run python $s overrun-flag # trigger: injected plan_halt on a built plan
uv run python $s overrun # control: no plan_halt
uv run python $s halt # control: the halt honoured
uv run python $s overrun-synth # control: bmad-loop's own result synthesis for the built plan
import json, os, subprocess, sys, tempfile
from pathlib import Path
mode = sys.argv[1]
root = Path(tempfile.mkdtemp(prefix=f"overrun-{mode}-"))
os.environ["BMAD_LOOP_STATE_DIR"] = str(root.parent / (root.name + "-state"))
sys.path.insert(0, "tests")
import conftest as C
from bmad_loop import verify
from bmad_loop.adapters.base import SessionResult
from bmad_loop.adapters.mock import MockAdapter
from bmad_loop.bmadconfig import load_paths
from bmad_loop.journal import Journal, load_state
from bmad_loop.model import RunState
from bmad_loop.policy import GatesPolicy, NotifyPolicy, Policy, ReviewPolicy
from bmad_loop.tickets_engine import TicketsEngine
git = lambda cwd, *a: subprocess.run(["git", "-C", str(cwd), *a], capture_output=True, text=True, check=True).stdout.strip()
lay = C.install_bmad_v7(root)
(root / "src.txt").write_text("original\n", encoding="utf-8")
(root / ".gitignore").write_text(C.V7_GITIGNORE, encoding="utf-8")
C.write_ticket_tree(lay, (C.EpicSpec(1, "epic-cart", "Cart", (C.EntrySpec(1, "Cart model", plan_checkpoint=True),)),))
C._commit_template(root)
plan = Path(C.run_tickets(lay, "find", "1.1")["plan"])
def leg1(spec):
cwd, base = Path(spec.cwd), verify.rev_parse_head(Path(spec.cwd))
if mode.startswith("overrun"): # ignores "Halt after planning.", builds and commits its own work
(cwd / "src.txt").write_bytes(b"cart model\n")
plan.write_bytes(f"---\nticket: 1\nstatus: built\nbaseline_revision: '{base}'\n---\n\n# plan\n".encode())
git(cwd, "add", "-A"); git(cwd, "commit", "-q", "-m", "build 1.1")
status = "built"
else: # honours the halt
plan.write_bytes(f"---\nticket: 1\nstatus: ready-for-dev\nbaseline_revision: '{base}'\n---\n\n# plan\n\nsteps\n".encode())
status = "ready-for-dev"
result = {"workflow": "auto-dev", "spec_file": str(plan), "status": status}
if mode in ("halt", "overrun-flag"):
result["plan_halt"] = True
if mode == "overrun-synth": # control: bmad-loop's own result synthesis for the same built plan
from bmad_loop.devcontract import synthesize_result
result = synthesize_result(plan, story_key="1.1", plan_halt=True, success_statuses=("built",)).result_json
print(" synthesized:", {"status": result.get("status"), "plan_halt": result.get("plan_halt")})
return SessionResult(status="completed", result_json=result)
run_dir = root / ".bmad-loop" / "runs" / "r1"
state = RunState(run_id="r1", project=str(root), started_at="now", initiative=lay.initiative, initiative_folder=str(lay.initiative_folder))
TicketsEngine(paths=load_paths(root, want_tickets=True),
policy=Policy(gates=GatesPolicy(mode="none"), notify=NotifyPolicy(desktop=False, file=True), review=ReviewPolicy(enabled=False)),
adapter=MockAdapter([leg1] * 3), run_dir=run_dir, journal=Journal(run_dir), state=state).run()
st = load_state(run_dir)
print(f"{mode}: paused_stage={st.paused_stage}\n reason: {(st.paused_reason or '').splitlines()[0][:160]}")
print(" journal:", [json.loads(l)["kind"] for l in (run_dir / "journal.jsonl").read_text(encoding="utf-8").splitlines()])
Observed (Windows; trigger and the first two controls 3/3, the synthesis control in its own runs):
overrun-flag: paused_stage=escalation
reason: **ACTION REQUIRED — manual recovery needed (committed work present)**
journal: ['story-start', 'plan-halt', 'session-start', 'session-end', 'dev-decision', 'rollback-manual-required', 'run-paused']
overrun: paused_stage=plan-checkpoint
reason: plan review owed for 1.1: the skill implemented before the plan checkpoint — review the spec, then resume
journal: ['story-start', 'plan-halt', 'session-start', 'session-end', 'dev-decision', 'checkpoint-pause', 'run-paused']
halt: paused_stage=plan-checkpoint
reason: plan checkpoint for 1.1: review the spec, then resume
journal: ['story-start', 'plan-halt', 'session-start', 'session-end', 'plan-halt-proof-of-work-skipped', 'dev-decision', 'checkpoint-pause', 'run-paused']
synthesized: {'status': 'built', 'plan_halt': None}
overrun-synth: paused_stage=plan-checkpoint
reason: plan review owed for 1.1: the skill implemented before the plan checkpoint — review the spec, then resume
journal: ['story-start', 'plan-halt', 'session-start', 'session-end', 'dev-decision', 'checkpoint-pause', 'run-paused']
Source
At a5a8f097:
src/bmad_loop/tickets_engine.py:1002-1034(_drive_story): the comment says "a session that overran the halt still owes the review"; a false return from_dev_phaseexits before either checkpoint pause (_pause_plan_checkpoint,_pause_plan_review_owed).src/bmad_loop/tickets_engine.py:1418-1437: takesbool(result["plan_halt"]), setstask.plan_checkpoint_pending, and passes that flag toverify_dev_tickets.src/bmad_loop/verify.py:9445-9456: selectsPLAN_HALT_STATUS(ready-for-dev) for a marked result andPLAN_BUILTotherwise, so a markedbuiltplan fails (observed as thedev-decisionretry reason).src/bmad_loop/devcontract.py:163-164: "the seam is gated onplan_halt, never on the status alone",PLAN_HALT_STATUS = "ready-for-dev".src/bmad_loop/devcontract.py:635-639: result synthesis marks only a cleanready-for-devresult (if plan_halt and status == PLAN_HALT_STATUS and not escalations: result["plan_halt"] = True);src/bmad_loop/adapters/generic.py:3013-3031calls that synthesizer with the dispatched plan-halt flag.src/bmad_loop/recovery_flow.py:2604-2625: the "stopped" and committed-work recovery wording observed.
Suggested fix
Not validated: consider clearer diagnostic wording for inconsistent adapter results. Do not relax the expected-status verification or route contradictory results to plan approval without a demonstrated supported user path and an explicit contract decision.
- Lenguaje dominante
- Python
- Estrellas
- 160
- Forks
- 68
- Merge medio
- 1 d 1 h
- PR fusionados (30 d)
- 84
Preparar el entorno
- Sin Dockerfile ni archivo de Docker Compose
- Tiene una plantilla de pull request
- Leer la guía de contribución
Primeros pasos
- Lee el issue completo y luego la guía de contribución del proyecto.
- Comenta en el issue que vas a ocuparte — evita que dos personas hagan lo mismo.
- Haz un fork del repositorio y trabaja en una rama.
- Abre un pull request que haga referencia al número del issue.
Más de bmad-code-org/bmad-loop
-
Dificultad 2/5 1-3 horas Aptitud para principiantes 69/100
bmad-code-org/bmad-loop#973 ·
Los mantenedores suelen responder en 1 día
-
Dificultad 2/5 1-3 horas Aptitud para principiantes 78/100
bmad-code-org/bmad-loop#965 ·
Los mantenedores suelen responder en 1 día
-
Dificultad 2/5 1-3 horas Aptitud para principiantes 75/100
bmad-code-org/bmad-loop#835 ·
Los mantenedores suelen responder en 1 día
-
area:adapters area:psmux bug P4
Dificultad 2/5 1-3 horas Aptitud para principiantes 76/100
bmad-code-org/bmad-loop#673 · 8 comentarios · 1 reacción ·
Los mantenedores suelen responder en 1 día
-
Dificultad 4/5 1-2 días Aptitud para principiantes 49/100
bmad-code-org/bmad-loop#971 ·
Los mantenedores suelen responder en 1 día
Todos los issues de bmad-code-org/bmad-loop
Issues similares
-
Dificultad 2/5 1-3 horas Aptitud para principiantes 62/100
foxglove/mcap#1907 · 1 comentario ·
Los mantenedores suelen responder en 1 día
-
namespace operations
Dificultad 1/5 Menos de una hora Aptitud para principiantes 72/100
EclipseFdn/open-vsx.org#14623 ·
Los mantenedores suelen responder en 1 día
-
Dificultad 2/5 1-3 horas Aptitud para principiantes 76/100
developmentseed/titiler-cmr#261 ·
Los mantenedores suelen responder en 1 día
-
Dificultad 2/5 1-3 horas Aptitud para principiantes 78/100
andrewyng/openworker#774 ·
Los mantenedores suelen responder en 1 día
-
aggfuncAbierto
Dificultad 2/5 1-3 horas Aptitud para principiantes 68/100
worldbank/blackmarblepy#160 ·