Skip to content

Escalation reason truncated at 2000 chars drops the blocker and the operator instructions #734

Description

@tronghieu

Observed on bmad-loop 0.11.0, run 20260826-114252-3da1.

Summary

A CRITICAL escalation's reason is truncated at 2000 characters with no marker. Because a dev session's report leads with what it built and closes with why it stopped, the truncation reliably keeps the narrative and drops the blocker and the operator instructions — the only two things the paused run actually needs a human to read.

Mechanism

  • devcontract.py:395escalations.append({"type": "blocked", "severity": "CRITICAL", "detail": detail[:2000]})
  • escalation.py:129return Decision(Action.PAUSE, f"CRITICAL escalation from dev session: {details}")

That single string then becomes:

  • reason on the dev-decision, story-escalated and run-paused journal entries — all three carry the same truncated copy, so the run directory holds no untruncated version;
  • the notice appended to ATTENTION;
  • the body of bmad-loop status <run-id>;
  • the escalation pane in bmad-loop tui.

Measured on this run: len(reason) == 2038 on all three entries — the 38-character prefix plus exactly 2000 characters of detail.

What the operator sees

The notice opens with Status: done and goes on for two thousand characters describing a completed, reviewed implementation. It ends mid-word:

[2026-08-26 17:13:09] CRITICAL escalation: 9-2-define-negotiated-variant-prices: CRITICAL escalation from dev session: Status: done
...
a database outage rendered as "this group has no negotiated prices" on the one screen whose purpose
is the negotiated-price record; and picking a catalog varia

There is no ellipsis, no (truncated), no length, and no pointer to the full text. Nothing in the notice signals that it is partial.

What was dropped

Everything after character 2000, which in this case was the entire reason for the pause:

### Blocking condition

`protected file change requires human approval (pnpm-lock.yaml)`

The implementation is complete and every verification command is green, but the
finalizing commit cannot be made by an agent. ... The pre-commit hook refuses it
and says so in the same words as review-process.md: agents and automated
sessions must not bypass it, and must escalate to a human.

Everything is staged and nothing else blocks. A maintainer completes the run
with the documented local escape hatch:

    LEFTHOOK_EXCLUDE=protected-files git commit

Reading ATTENTION and journal.jsonl alone, an operator cannot tell why the run paused. Both say Status: done and describe finished work. The blocking condition and the one command that clears it survived only in the terminal log capture, which is a TUI redraw dump and not something an operator can reliably read.

The full text is already on disk

arr.detail comes from parse_auto_run_result(spec_path) (devcontract.py:373), so the untruncated blocking condition is sitting in the story spec's result section the whole time — here, _bmad-output/implementation-artifacts/spec-9-2-define-negotiated-variant-prices.md. The escalation record already knows that path; it just never hands it to the reader.

Suggested fixes

In rough order of value:

  1. Mark the truncation and point at the source. When detail is cut, append something like \n\n[truncated at 2000 chars — full detail in <spec_file>]. Cheap, and it alone would have made this run self-explanatory.
  2. Do not truncate in the journal. journal.jsonl is line-delimited JSON on disk with no size pressure; the 2000-character cap belongs on display surfaces (status, tui, ATTENTION), not on the durable record.
  3. Truncate from the right end, or prefer the blocking section. If a cap must apply to a single field, the tail of an escalation report carries the actionable content. Better still, when the report has an explicit ### Blocking condition section, use that as the detail and treat the rest as context.

Metadata

Metadata

Assignees

No one assigned

    Labels

    P2Real defect - workaround exists or blast radius is narrowarea:engineOrchestrator engine and run lifecyclebugSomething isn't working

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions