The console was a flat form: every control on the root window, 48 operations
as undifferentiated rows, a giant output textarea, privilege as a buried
sentence. It now has a shell: a fixed header band (product, target, device
state, privilege) plus a page body, and in detailed mode a sidebar with five
operation categories and an Activity page.
What changed (presentation layer only; _execute, gates, run_plan, audit and
redaction untouched):
- simple mode is an Overview: four state cards (target/device/privilege/
inventory) plus Install/Status/Exit, instead of the raw form;
- the target is one segmented control (Device/Wave/Entire fleet) with a single
scope source of truth; the dealer text can no longer silently override the
wave, and the scope stays visible across pages;
- operations are browsed, not dumped: icon + name + explanation + scope/risk
badges, with a detail panel (meta grid, warnings, options form, backend-built
command preview on demand);
- destructive confirmations are dedicated dialogs showing OPERATION/TARGET/
SCOPE/EFFECT with a fleet/production strip and focus on Cancel; the token
dialog is styled; secrets never enter either;
- the output textarea is an execution drawer (IDLE/RUNNING with mm:ss /
DONE-FAILED with duration and exit code) streaming the same backend output;
- write operations stay visible but disabled with their reason when
unprivileged, in both entry points;
- new module bfos/ui_widgets.py (~490 lines, stdlib + tkinter only, no colour
literals): canvas line icons, segmented control, collapsible panel, meta
grid, timer, the two safety dialogs.
Tests: test_bfos_gui_static.py 95 OK, test_bfos.py 155 OK, run-all.sh 12/12.
The colour-literal test now also scans ui_widgets.py (proven by mutation:
a #ff0000 probe goes red, green after removal). Live QA: zero geometry errors
over 712 widgets, all 48 operations listed, 13 write ops disabled with reasons
without root, TUI and --list smoke-tested.
Known limits: no dark theme (tokens ready); the detail page's target line does
not refresh when the target changes mid-form (the confirmation dialog always
shows the current one); visual QA was geometry + OCR, not a human eye.