Tech-debt sweep, scoped and measured in docs/PAUSE_SEMANTICS_SWEEP.md.
Requested by Pip on 2026-08-30 after #1341 was found on a shipped build.
Due by Friday 2026-09-04, for the league patch. Declared in the calendar:
COMMITMENT: 2026-09-04 -- Sweep the pause-semantics family ... -- owner: pip -- kind: deadline
The one-sentence problem
Nothing in this repository states what "paused" means, so every site that
could observe SceneTree.paused decided for itself -- usually by inheriting an
engine default that says to ignore it.
#1341 is one member: SceneTree.create_timer() defaults process_always to
true, so the month-playback loop ticked straight through the pause menu. Six
day-ticks and a month boundary ran while Pip had the menu open to change the
volume. That is fixed, narrowly, at the two gameplay-advancing sites. This is the
part that is not narrow.
Measured surface, 2026-08-30
Timers that ignore pause -- 9 remaining sites.
grep -rn "create_timer(" godot/scripts/ --include=*.gd | grep -v ", *false" | grep -v ", *true"
None advances game state, which is why the #1341 fix stops at two. But "does not
advance state" is not "is correct" -- plan_screen.gd:58 lets a 4s error toast
expire while you are reading the pause menu, and conference_vignette.gd paces a
mid-run sequence you can press ESC during. Nobody has decided which of these
should stop.
Tweens -- 18 sites, none of which say anything about pause.
grep -rc "create_tween()" godot/scripts/ --include=*.gd -> 18
grep -rc "set_pause_mode|TWEEN_PAUSE" godot/scripts/ -> 0
Every tween inherits pause behaviour from its creating node and no site has
examined that. This family already has a known latent member: #1034, which
records that the fade transition awaits a pausable tween and "would deadlock ALL
navigation if called while paused". It has never fired only because nothing has
yet called it from a paused state.
Input capture by things you cannot see -- 47 mouse_filter sites.
The second half of what Pip hit: the pause menu was open, drawn under the Month
Review dialog, and still eating clicks. Fixing the timer removes the common path
into that state; it does not answer which overlay owns input when two are up.
Related: #603 (events leak under the fanfare popup), #1028 (ESC/pause disables
all diagnostic surfaces -- root cause is process_mode).
Definition of done
- A written statement of what pausing means, in one place, covering the
simulation, presentation beats, audio, input and navigation. Likely an ADR --
the answer is a design decision, not a bug fix.
- Each site above conformed to it, or explicitly excepted with the reason
next to the code.
- A guard so the next one cannot land silently. Cheapest honest version: flag
get_tree().create_timer( with no explicit second argument under
godot/scripts/, forcing the author to state intent either way. Greppable,
gateable, same family as check_scene_nav.py. Should ship with a self-test and
be wired into guards.yml, per the doctrine already established there.
Why sweep rather than wait for the next report
This class does not announce itself. Every member is a default nobody chose, and
each stays invisible until a player does something ordinary -- opens a menu,
changes the volume, presses ESC at the wrong moment. #1341 sat in a shipped build
and reached players. #1034 is sitting in this one now, waiting for the first
navigation call from a paused state.
The fix per site is usually one argument. The expensive part is knowing which
sites, and that is what this sweep buys.
Tech-debt sweep, scoped and measured in
docs/PAUSE_SEMANTICS_SWEEP.md.Requested by Pip on 2026-08-30 after #1341 was found on a shipped build.
Due by Friday 2026-09-04, for the league patch. Declared in the calendar:
The one-sentence problem
Nothing in this repository states what "paused" means, so every site that
could observe
SceneTree.pauseddecided for itself -- usually by inheriting anengine default that says to ignore it.
#1341 is one member:
SceneTree.create_timer()defaultsprocess_alwaystotrue, so the month-playback loop ticked straight through the pause menu. Six
day-ticks and a month boundary ran while Pip had the menu open to change the
volume. That is fixed, narrowly, at the two gameplay-advancing sites. This is the
part that is not narrow.
Measured surface, 2026-08-30
Timers that ignore pause -- 9 remaining sites.
None advances game state, which is why the #1341 fix stops at two. But "does not
advance state" is not "is correct" --
plan_screen.gd:58lets a 4s error toastexpire while you are reading the pause menu, and
conference_vignette.gdpaces amid-run sequence you can press ESC during. Nobody has decided which of these
should stop.
Tweens -- 18 sites, none of which say anything about pause.
Every tween inherits pause behaviour from its creating node and no site has
examined that. This family already has a known latent member: #1034, which
records that the fade transition awaits a pausable tween and "would deadlock ALL
navigation if called while paused". It has never fired only because nothing has
yet called it from a paused state.
Input capture by things you cannot see -- 47
mouse_filtersites.The second half of what Pip hit: the pause menu was open, drawn under the Month
Review dialog, and still eating clicks. Fixing the timer removes the common path
into that state; it does not answer which overlay owns input when two are up.
Related: #603 (events leak under the fanfare popup), #1028 (ESC/pause disables
all diagnostic surfaces -- root cause is
process_mode).Definition of done
simulation, presentation beats, audio, input and navigation. Likely an ADR --
the answer is a design decision, not a bug fix.
next to the code.
get_tree().create_timer(with no explicit second argument undergodot/scripts/, forcing the author to state intent either way. Greppable,gateable, same family as
check_scene_nav.py. Should ship with a self-test andbe wired into
guards.yml, per the doctrine already established there.Why sweep rather than wait for the next report
This class does not announce itself. Every member is a default nobody chose, and
each stays invisible until a player does something ordinary -- opens a menu,
changes the volume, presses ESC at the wrong moment. #1341 sat in a shipped build
and reached players. #1034 is sitting in this one now, waiting for the first
navigation call from a paused state.
The fix per site is usually one argument. The expensive part is knowing which
sites, and that is what this sweep buys.