Hacktoberfest 2026: những issue maintainer đã đánh dấu cho tháng Mười, đang mở và phù hợp người mới. Xem issue Hacktoberfest

tickets: an injected plan_halt result for a built plan escalates with stopped-attempt wording

Đang mở
#972 0 bình luận 0 reaction 0 người được giao Xem trên GitHub

Maintainer thường phản hồi trong vòng 1 ngày

Chưa có ai nhận issue này.

Đánh giá

Độ khó
4/5
Thời gian dự kiến
3-5 ngày
Mức phù hợp với người mới
36/100
Loại issue
Lỗi
Độ rõ ràng
Khá rõ ràng
Mức độ hoạt động
Sôi nổi
Công nghệ
python
Lĩnh vực
backend, testing

Hướng nghiên cứu

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.

Do mô hình lập chỉ mục viết ra từ nội dung của issue.

Mô tả

Status (needs confirmation)

  • Observed (3/3, engine seam): for an unrefined plan_checkpoint ticket, a leg-1 session (a MockAdapter result injected by the script) that builds and commits past Halt after planning. behaves as follows:
    • without plan_halt in its result, the run pauses plan-checkpoint with "plan review owed for 1.1: the skill implemented before the plan checkpoint" (the dedicated path);
    • with "plan_halt": true in its result, it pauses escalation with "ACTION REQUIRED — manual recovery needed (committed work present) … attempt was stopped with auto-rollback OFF". The dev-decision is retry with "spec status is 'built', expected 'ready-for-dev'".
  • Observed control: a leg 1 that honours the halt (plan at ready-for-dev, plan_halt: true, nothing committed) pauses plan-checkpoint "plan checkpoint for 1.1: review the spec, then resume".
  • Measured additional control: production devcontract.synthesize_result(..., plan_halt=True, success_statuses=("built",)) omits plan_halt for the same built plan, and the engine then pauses with "plan review owed". It sets plan_halt for ready-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: true while the bound plan is built. 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_phase exits before either checkpoint pause (_pause_plan_checkpoint, _pause_plan_review_owed).
  • src/bmad_loop/tickets_engine.py:1418-1437: takes bool(result["plan_halt"]), sets task.plan_checkpoint_pending, and passes that flag to verify_dev_tickets.
  • src/bmad_loop/verify.py:9445-9456: selects PLAN_HALT_STATUS (ready-for-dev) for a marked result and PLAN_BUILT otherwise, so a marked built plan fails (observed as the dev-decision retry reason).
  • src/bmad_loop/devcontract.py:163-164: "the seam is gated on plan_halt, never on the status alone", PLAN_HALT_STATUS = "ready-for-dev".
  • src/bmad_loop/devcontract.py:635-639: result synthesis marks only a clean ready-for-dev result (if plan_halt and status == PLAN_HALT_STATUS and not escalations: result["plan_halt"] = True); src/bmad_loop/adapters/generic.py:3013-3031 calls 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.

Ngôn ngữ chính
Python
Star
160
Fork
68
Merge trung bình
1 ngày 6 giờ
Pull request đã merge (30 ngày)
90

Chuẩn bị môi trường

Bắt đầu từ đâu

  1. Đọc hết issue, rồi đọc hướng dẫn đóng góp của dự án.
  2. Bình luận trên issue rằng bạn sẽ nhận — tránh hai người làm cùng một việc.
  3. Fork repository và làm thay đổi trên một nhánh.
  4. Mở pull request có tham chiếu số hiệu của issue.

Issue khác của bmad-code-org/bmad-loop

Tất cả issue của bmad-code-org/bmad-loop

Issue tương tự

Thêm issue về Python

Nhận issue mới trong hộp thư của bạn

Bản tóm tắt ngắn những issue GitHub phù hợp với người mới.