You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Follow-up to #205 / PR #209, which was deliberately scoped to the CLI: the TUI stayed on the text path.
Current state
Pressing v runs action_validate → launch.run_captured(["validate", "--project", ...]), which captures the CLI's raw text and hands it to TextOutputModal (src/bmad_loop/tui/screens/modals.py) — a scrollable verbatim blob rendered via Text.from_ansi, with the pass/fail verdict inferred from the exit code.
Proposal
Invoke validate --json instead, parse the schema-v1 document from #205, and build the modal from the findings array — one styled row per Finding rather than one text blob.
What that buys:
Severity-styled rows — color-coded ok / warning / problem, including cleaning up the ok: warning: ... double-prefix rendering the frozen CLI text is stuck with. A structural renderer has no byte-compat constraint.
Verdict from the document's ok, not the exit code — separates "the checks failed" from "the command broke". If stdout is empty or unparseable, that is the command-broke case: fall back to showing raw output.
Detail on demand — mux.backends-detected carries the full MuxBackendInfo rows (all six fields) where the text collapses to tmux*, psmux (unavailable); skills.base-incomplete carries missing_markers as a list. Findings with a detail payload can be expandable, or the mux inventory shown as a small table.
Unblocks the mux.external-backend severity promotion noted in PR feat(cli): validate --json emits the check findings as a document (#205) #209's traps: it reads like a warning but stays severity ok today only because promoting it would insert the warning: infix into shipped text the TUI displays verbatim. Once the TUI renders from the document, the text-freeze argument no longer pins it. (The CLI text still changes on promotion — decide separately whether that's acceptable then.)
Contract notes for the implementer
Pin schema_version == 1 and degrade to the raw-text modal on mismatch rather than guessing at fields.
Absence is not a pass: the gates are chained, so an early failure (e.g. policy load) leaves later checks contributing no finding at all. Render what's present; don't synthesize green rows for missing check ids.
message is not contracted — match on check ids only, never on wording.
Tests
tests/test_tui_app.py stubs run_captured returning (1, "FAIL: no policy\n") — those stubs need to return a document (there's a machine_json conftest helper from #206 on the CLI side worth reusing/extending), plus a case for the empty/unparseable-stdout fallback.
Follow-up to #205 / PR #209, which was deliberately scoped to the CLI: the TUI stayed on the text path.
Current state
Pressing
vrunsaction_validate→launch.run_captured(["validate", "--project", ...]), which captures the CLI's raw text and hands it toTextOutputModal(src/bmad_loop/tui/screens/modals.py) — a scrollable verbatim blob rendered viaText.from_ansi, with the pass/fail verdict inferred from the exit code.Proposal
Invoke
validate --jsoninstead, parse the schema-v1 document from #205, and build the modal from thefindingsarray — one styled row per Finding rather than one text blob.What that buys:
ok: warning: ...double-prefix rendering the frozen CLI text is stuck with. A structural renderer has no byte-compat constraint.ok, not the exit code — separates "the checks failed" from "the command broke". If stdout is empty or unparseable, that is the command-broke case: fall back to showing raw output.mux.backends-detectedcarries the fullMuxBackendInforows (all six fields) where the text collapses totmux*, psmux (unavailable);skills.base-incompletecarriesmissing_markersas a list. Findings with adetailpayload can be expandable, or the mux inventory shown as a small table.mux.external-backendseverity promotion noted in PR feat(cli): validate --json emits the check findings as a document (#205) #209's traps: it reads like a warning but stays severityoktoday only because promoting it would insert thewarning:infix into shipped text the TUI displays verbatim. Once the TUI renders from the document, the text-freeze argument no longer pins it. (The CLI text still changes on promotion — decide separately whether that's acceptable then.)Contract notes for the implementer
schema_version == 1and degrade to the raw-text modal on mismatch rather than guessing at fields.messageis not contracted — match oncheckids only, never on wording.Tests
tests/test_tui_app.pystubsrun_capturedreturning(1, "FAIL: no policy\n")— those stubs need to return a document (there's amachine_jsonconftest helper from #206 on the CLI side worth reusing/extending), plus a case for the empty/unparseable-stdout fallback.