refactor(engine): ambiguity burn-down batch 4 — zero ambiguities, gate enforced
Test / test (pull_request) Failing after 17m5s
Test / test (pull_request) Failing after 17m5s
Clears the final 47 pairs and turns the ratchet into a hard gate (AMBIGUITY_BASELINE = 0, assert_eq): - HudButtons: the 14 HUD button/popover handlers run as one chain, before ui_focus::FocusKeys — Esc/keyboard consumption order is now defined (restore prompt → buttons/popovers → focus navigation → settings toggle) instead of scheduler-dependent. - HUD text updaters (update_hud, update_selection_hud, update_won_previously) chained, in UiTextFx, after the new AutoComplete set (update_hud reads AutoCompleteState). - restore_hud_on_modal → apply_hud_visibility chained before UpdateOnResize: HudVisibility writes, application, and the layout read happen in a fixed order. - New writer sets UndoRequestWriters / InfoToastWriters (same self-ambiguous pattern as NewGameRequestWriters). - Logic-before-paint: check_no_moves and the AutoComplete chain order before BoardVisuals; SettingsMutation after UpdateOnResize. - MarkerVisuals set wraps the table painter chain; chrome fx declare disjointness from it. - update_hud_typography after BoardVisuals; avatar/settings-toggle interaction handlers declared disjoint from HudButtons. 302 → 198 → 171 → 47 → 0 in four batches, one day. New systems now fail CI unless they declare their ordering or their disjointness. Closes #143 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -154,6 +154,13 @@ pub struct HudColumn;
|
||||
#[derive(Component, Debug)]
|
||||
pub struct HudActionBar;
|
||||
|
||||
/// Set wrapping the chained HUD button/popover interaction systems. Other
|
||||
/// keyboard consumers order themselves around it (e.g.
|
||||
/// [`crate::ui_focus::FocusKeys`] runs after) so input-consumption order is
|
||||
/// deterministic (#143).
|
||||
#[derive(bevy::ecs::schedule::SystemSet, Debug, Clone, PartialEq, Eq, Hash)]
|
||||
pub struct HudButtons;
|
||||
|
||||
/// Marker on the text node inside each touch-layout action-bar button.
|
||||
/// Used by `resize_action_bar_labels` to update font size on window resize.
|
||||
#[derive(Component, Debug)]
|
||||
@@ -468,31 +475,56 @@ impl Plugin for HudPlugin {
|
||||
// defensively so the HUD plugin works standalone in tests.
|
||||
.add_message::<WindowResized>()
|
||||
.add_systems(Startup, (spawn_hud_band, spawn_hud, spawn_action_buttons, spawn_hud_avatar))
|
||||
.add_systems(Update, update_hud.after(GameMutation))
|
||||
// HUD text updaters run as one deterministic chain (they write
|
||||
// disjoint Text nodes, but Bevy can't prove it); update_hud also
|
||||
// reads AutoCompleteState, so the chain sits after the
|
||||
// auto-complete detect/drive chain (#143).
|
||||
.add_systems(
|
||||
Update,
|
||||
apply_hud_visibility.before(LayoutSystem::UpdateOnResize),
|
||||
(
|
||||
update_hud,
|
||||
update_selection_hud.run_if(
|
||||
resource_exists_and_changed::<SelectionState>
|
||||
.or(resource_exists_and_changed::<GameStateResource>),
|
||||
),
|
||||
update_won_previously,
|
||||
)
|
||||
.chain()
|
||||
.after(GameMutation)
|
||||
.after(crate::auto_complete_plugin::AutoComplete)
|
||||
.in_set(UiTextFx)
|
||||
.ambiguous_with(UiTextFx),
|
||||
)
|
||||
// HUD chrome visibility: modal-restore writes HudVisibility, the
|
||||
// applier consumes it, and the layout recompute reads it — a
|
||||
// fixed chain instead of three racing systems (#143).
|
||||
.add_systems(
|
||||
Update,
|
||||
(restore_hud_on_modal, apply_hud_visibility)
|
||||
.chain()
|
||||
.before(LayoutSystem::UpdateOnResize),
|
||||
)
|
||||
.add_systems(Update, restore_hud_on_modal)
|
||||
.add_systems(
|
||||
Update,
|
||||
(
|
||||
update_hud_avatar.after(crate::settings_plugin::SettingsMutation),
|
||||
handle_avatar_button,
|
||||
handle_avatar_button.ambiguous_with(HudButtons),
|
||||
),
|
||||
)
|
||||
.add_systems(Update, update_won_previously.after(GameMutation))
|
||||
.add_systems(Update, announce_auto_complete.after(GameMutation))
|
||||
.add_systems(
|
||||
Update,
|
||||
update_selection_hud
|
||||
announce_auto_complete
|
||||
.after(GameMutation)
|
||||
.run_if(
|
||||
resource_exists_and_changed::<SelectionState>
|
||||
.or(resource_exists_and_changed::<GameStateResource>),
|
||||
),
|
||||
.after(crate::auto_complete_plugin::AutoComplete)
|
||||
.in_set(crate::game_plugin::InfoToastWriters)
|
||||
.ambiguous_with(crate::game_plugin::InfoToastWriters),
|
||||
)
|
||||
// Typography rescale touches HUD TextFont only, but orders after
|
||||
// the board painters that resize card/label text (#143).
|
||||
.add_systems(
|
||||
Update,
|
||||
update_hud_typography.after(crate::card_plugin::BoardVisuals),
|
||||
)
|
||||
.add_systems(Update, update_hud_typography)
|
||||
.add_systems(
|
||||
Update,
|
||||
(
|
||||
@@ -521,10 +553,16 @@ impl Plugin for HudPlugin {
|
||||
handle_new_game_button
|
||||
.in_set(NewGameRequestWriters)
|
||||
.ambiguous_with(NewGameRequestWriters),
|
||||
handle_undo_button,
|
||||
handle_undo_button
|
||||
.in_set(crate::game_plugin::UndoRequestWriters)
|
||||
.ambiguous_with(crate::game_plugin::UndoRequestWriters)
|
||||
.before(GameMutation),
|
||||
handle_pause_button,
|
||||
handle_help_button,
|
||||
handle_hint_button.after(GameMutation),
|
||||
handle_hint_button
|
||||
.after(GameMutation)
|
||||
.in_set(crate::game_plugin::InfoToastWriters)
|
||||
.ambiguous_with(crate::game_plugin::InfoToastWriters),
|
||||
handle_modes_button,
|
||||
handle_mode_option_click
|
||||
.in_set(NewGameRequestWriters)
|
||||
@@ -536,7 +574,10 @@ impl Plugin for HudPlugin {
|
||||
handle_menu_backdrop_click,
|
||||
close_menu_popover_on_escape,
|
||||
paint_action_buttons,
|
||||
),
|
||||
)
|
||||
.chain()
|
||||
.in_set(HudButtons)
|
||||
.before(crate::ui_focus::FocusKeys),
|
||||
)
|
||||
// Fade lives in `Last` so it always overrides whatever the
|
||||
// hover/paint pass set on `BackgroundColor` this frame.
|
||||
|
||||
Reference in New Issue
Block a user