Skip to content

fleet TUI: kill confirmation modal renders as an empty red box (Static children collapse an auto-width container) #449

Description

Summary

Pressing k (kill) or K (kill all) in the conductor fleet TUI shows an empty red box instead of the kill-confirmation dialog. The dialog is ConfirmKillModal (src/conductor/fleet/tui/actions.py), and it renders as nothing but its own border: thick $error — its content is laid out at zero size.

The keybindings still work (y confirms, n/esc cancels), so the kill itself functions. But the user is asked to confirm the destruction of a running workflow by a box that names nothing, shows no hint keys, and — for a foreground run — omits the checkpoint-recoverability warning that is the entire reason D1 made the TUI confirm unconditionally.

Present since the Fleet Manager PR (#431, d785a28). It has never rendered.

Reproduction

conductor fleet          # with at least one run listed
# press k

Reproduced deterministically in App.run_test() against textual 8.2.8:

dialog region: Region(x=47, y=13, width=6, height=4)
  #confirm-message size=Size(width=0, height=0)
  #confirm-hint    size=Size(width=0, height=0)

    █▀▀▀▀█
    █    █
    █    █
    █▄▄▄▄█

6×4 is exactly border (2) + padding: 1 2 (4 cols / 2 rows) around a 0×0 interior. $error is #ba3c5b in the default theme, hence "red box".

Root cause

src/conductor/fleet/tui/actions.py:229-238:

#confirm-dialog {
    width: auto;
    height: auto;
    border: thick $error;
    padding: 1 2;
}

#confirm-dialog is a Vertical whose two children are Static widgets. Static.DEFAULT_CSS sets only height: auto; its width falls back to Textual's base default of 1fr. An auto-width container whose children are all 1fr resolves to 0, so both Statics collapse to 0×0 — and the auto height then has zero-width content to measure, collapsing too.

Label does not have this problem because Label.DEFAULT_CSS sets width: auto.

Verification of the fix direction

Same harness, same CSS, only the child widget/width changed:

Variant Result
Static, as shipped Region(width=6, height=4), children 0×0
Label instead of Static Region(width=34, height=6), renders correctly
Static + #confirm-message, #confirm-hint { width: auto } Region(width=34, height=6), renders correctly

Correct render:

█▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀█
█                                █
█  Kill 1 run(s): plan?          █
█  [y] Confirm   [n/esc] Cancel  █
█                                █
█▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄█

Note the multi-line foreground-run message (build_kill_confirmation_message appends _foreground_stop_warning_lines) also needs to survive the fix — an auto-width Static sizes to its longest line, which is correct, but the fix should be checked against that case rather than only the one-line bg case.

Scope

  • ConfirmKillModal only. GateOptionsModal in the same file is unaffected: it uses explicit width: 90%; max-width: 120; height: 80%. Its CSS comment already documents that auto-sizing was found broken for that modal and fixed there — the same class of defect was never revisited for the kill modal.
  • The markup escape in the hint ("[bold]\\[y][/bold] ...") is fine and renders as [y] Confirm [n/esc] Cancel.

Why the tests miss it

tests/test_fleet/test_tui_actions.py drives the modal by pressing y/n and asserts on os.kill call counts/PIDs and on the message string passed to ConfirmKillModal.__init__ (test_kill_all_names_foreground_runs_in_confirmation). Nothing asserts that the modal has non-zero size or that its text reaches the screen, so a completely invisible confirmation dialog passes the entire suite.

A regression test should assert against rendered geometry/content, e.g. that #confirm-message has non-zero size and that the workflow name appears in the composited screen text — not merely that the string was constructed. The same assertion would be worth having for GateOptionsModal, since it is the one other ModalScreen in the codebase and was fixed for a related sizing failure without a test.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Labels

No labels
No labels

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions