fix(client): defer companion close-interceptor re-open out of the app_changed emit (PR #183 review)

Tyre's finding: _on_hud_app_changed ran open_app synchronously from
INSIDE close_app's app_changed emit; close_app continues past the emit
and resets _active_app, clobbering the re-open — Atlas left visible
(z already raised) but is_app_active() false, so atlas_app's input
guard rejects every key: keyboard soft-lock after one M/Escape on the
reach screen. Fix: open_app.call_deferred, out of the signal frame.

Two regression tests pin the contract at the HudGroups level (no
server needed): the synchronous shape must keep getting clobbered and
the deferred shape must survive — if HudGroups emit semantics ever
change, both flag the contract shift for a deliberate look.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
2026-07-17 09:29:29 +02:00
co-authored by Claude Fable 5
parent 061eb642c9
commit 9b5c113961
2 changed files with 65 additions and 6 deletions
+7 -1
View File
@@ -105,7 +105,13 @@ func _exit_tree() -> void:
## else to show). atlas_app.gd is never modified.
func _on_hud_app_changed(app_path: String, mode: int) -> void:
if app_path == FRONT_APP and mode == HudGroups.Mode.GAMEPLAY:
HudGroups.open_app(FRONT_APP)
# Deferred, never synchronous: this handler runs INSIDE close_app()'s
# app_changed emit, and close_app() continues after the emit and resets
# _active_app to "" — a synchronous open_app() here gets clobbered,
# leaving the Atlas visible (open_app already raised its z) but
# is_app_active() false, which kills atlas_app's input guard: a
# keyboard soft-lock (PR #183 review, Tyre).
HudGroups.open_app.call_deferred(FRONT_APP)
func _boot() -> void: