48e412177c
Previously when a click-handler in Update spawned a modal, attach_focusable_to_modal_buttons and auto_focus_on_modal_open ran in the same Update — but with no ordering edge to the click handler the deferred Commands wouldn't materialise in time, so attach saw no entities, FocusedButton stayed empty, and the very next Tab/Enter press wasted itself moving focus from None to the primary instead of activating it. Moves attach_focusable_to_modal_buttons + auto_focus_on_modal_open from Update to PostUpdate. The schedule boundary itself supplies the sync point: every modal spawned anywhere in Update is materialised before PostUpdate runs, attach can find the new ModalButtons, and FocusedButton is populated before app.update() returns. handle_focus_keys stays in Update so it observes input on the frame it occurs, reading FocusedButton written by the previous tick's PostUpdate. Two new tests pin the contract: - primary_button_is_focused_on_modal_spawn_same_frame uses a production-shaped spawner system (no chain edge to UiFocusPlugin) and asserts FocusedButton.0 is Some after a single update — fails without the fix, passes with it. - first_tab_after_modal_open_advances_to_secondary guards against a regression where focus arrives but the very first Tab moves from None to primary instead of from primary to secondary. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>