Sprint 20 client refactors — 4 tickets enforcing D-020 (Godot = pure renderer) and reducing structural debt.
#557: game_state.gd — stationary_ticks and zone_id now read from server snapshot with deprecated client-side fallbacks. Protocol extended with decode paths and TODO markers for server team.
#558: dialogue_box.gd — replaced 3 direct GameState.dialogue_active mutations and all AudioManager calls with signals (dialogue_state_changed, audio_dip_requested, audio_dip_cleared). Zero autoload references remain.
apply_snapshot() now reads stationary_ticks and zone_id directly from
the server snapshot when present (D-020 compliance). Client-side
accumulation and tile lookup retained as deprecated fallbacks until
the server populates these fields. Protocol.gd extended with decode
paths and TODO markers for the server team.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replace 3 direct GameState.dialogue_active mutations and all
AudioManager.apply_dip/clear_dip calls with signals:
dialogue_state_changed, audio_dip_requested, audio_dip_cleared.
dialogue_box.gd now has zero references to GameState or AudioManager.
main.gd wires coordinator handlers in _ready() (D-020).
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
New SnapshotEventRouter class (46 lines) provides callable-based
snapshot dispatch via register(), register_always(), and dispatch().
main.gd _process() now calls _router.dispatch(snapshot) instead of
15+ inline if-has blocks. Handlers registered in _ready().
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Extract shared YamlParser utility (client/scripts/util/yaml_parser.gd)
with parse() for nested typed dicts and parse_flat() for dotted-key
string format. UIStrings._parse_yaml() and ChecklistEvaluator's inline
parser both delegate to YamlParser, removing ~140 lines of duplication.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Summary
Sprint 20 client refactors — 4 tickets enforcing D-020 (Godot = pure renderer) and reducing structural debt.
game_state.gd—stationary_ticksandzone_idnow read from server snapshot with deprecated client-side fallbacks. Protocol extended with decode paths and TODO markers for server team.dialogue_box.gd— replaced 3 directGameState.dialogue_activemutations and allAudioManagercalls with signals (dialogue_state_changed,audio_dip_requested,audio_dip_cleared). Zero autoload references remain.main.gd— extractedSnapshotEventRouter(46 lines) with callable-basedregister()/dispatch()pattern. Replaces 15+ inline if-has dispatch blocks.YamlParserutility (parse()for nested typed dicts,parse_flat()for dotted-key UIStrings format). ~140 lines removed from consumers.Stats: 15 files changed, +1172 / -265 lines. 4 new test files with comprehensive coverage.
Test plan
test_game_state_sprint20.gd— server-authoritative paths and fallback degradation (10 tests)test_dialogue_sprint20.gd— signal emission, no-direct-mutation assertions (7 tests)test_snapshot_event_router.gd— keyed/always dispatch, ordering, edge cases (8 tests)test_yaml_parser.gd— parse/parse_flat, type inference, arrays, nesting, regression (39 tests)Review: client -> main (type: code)
Hoshe (Code Quality): APPROVE
Solid refactoring set with good test coverage. Four items worth noting, none blocking.
client/scripts/util/yaml_parser.gd:34-72client/scripts/util/yaml_parser.gd:130#) could truncate unquoted values containing that patternclient/ui/dialogue_box.gd:603-616_expire_entries()breaks early — pinned entry at index 0 can prevent pruning of expired entries beyond it, causing unbounded growth in long dialoguesclient/scripts/snapshot_event_router.gd:40-45is_valid()guard on handler callables — freed objects could halt dispatch loopclient/scripts/main.gd:321push_warningfor debuggingclient/tests/test_dialogue_sprint18.gdTyre (Architecture): APPROVE
All four commits architecturally sound. D-020 compliance correct, new abstractions well-bounded, no D-010/D-012 violations.
client/scripts/snapshot_event_router.gd_keyedDictionary untyped — runtime failure mode on wrong typesclient/scripts/snapshot_event_router.gd:39-45client/ui/dialogue_box.gd:147_load_theme()callsUIStrings._parse_yaml()(private) instead ofYamlParser.parse_flat()(public API from #560)client/scripts/util/yaml_parser.gd:64-72client/scripts/autoloads/game_state.gd:116-117_prev_player_positiondeprecated field — flag for two-part removal with fallbackclient/scripts/autoloads/game_state.gd:162-170tilesvsvisible_tilesdual-key accreting complexity — candidate for deprecated fallback treatmentVerdict: APPROVED
Pull request closed