Skip to content

refactor: expose public current_time() and trigger_early_reveal_if_complete() on GameState (closes #452)#460

Merged
mholzi merged 1 commit intomainfrom
fix/issue-452
Mar 31, 2026
Merged

refactor: expose public current_time() and trigger_early_reveal_if_complete() on GameState (closes #452)#460
mholzi merged 1 commit intomainfrom
fix/issue-452

Conversation

@mholzi
Copy link
Copy Markdown
Owner

@mholzi mholzi commented Mar 31, 2026

Fixes #452 β€” adds public wrappers on GameState to stop websocket.py accessing private _now() and _trigger_early_reveal() directly.

…mplete() on GameState (closes #452)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Copy link
Copy Markdown
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request refactors the GameState class by introducing a public current_time method and encapsulating the early reveal logic into trigger_early_reveal_if_complete. The websocket handlers were updated to utilize these new methods. Review feedback suggests that the refactoring resulted in a loss of observability; it is recommended to move the debug logging into the encapsulated method to ensure consistent tracking of the game state across all guess types and to remove the now-redundant logging in the websocket handlers.

Comment on lines +1003 to +1006
async def trigger_early_reveal_if_complete(self) -> None:
"""Trigger early reveal if the round is playing and all guesses are in."""
if self.phase == GamePhase.PLAYING and self.check_all_guesses_complete():
await self._trigger_early_reveal()
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The refactored trigger_early_reveal_if_complete method loses the observability previously present in websocket.py. Specifically, the all_complete status is no longer logged, making it harder to debug why an early reveal might not have triggered. Moving the debug logging into this method ensures all call sites (submit, artist guess, movie guess) benefit from consistent observability without duplicating logic.

Suggested change
async def trigger_early_reveal_if_complete(self) -> None:
"""Trigger early reveal if the round is playing and all guesses are in."""
if self.phase == GamePhase.PLAYING and self.check_all_guesses_complete():
await self._trigger_early_reveal()
async def trigger_early_reveal_if_complete(self) -> None:
"""Trigger early reveal if the round is playing and all guesses are in."""
if self.phase != GamePhase.PLAYING:
return
all_complete = self.check_all_guesses_complete()
_LOGGER.debug(
"Early reveal check: phase=%s, all_complete=%s",
self.phase.value,
all_complete,
)
if all_complete:
await self._trigger_early_reveal()

Comment on lines 813 to +818
_LOGGER.debug(
"Early reveal check: phase=%s, all_complete=%s, artist_challenge=%s",
"Early reveal check: phase=%s, artist_challenge=%s",
game_state.phase.value,
all_complete,
game_state.artist_challenge_enabled,
)
if game_state.phase == GamePhase.PLAYING and all_complete:
await game_state._trigger_early_reveal()
await game_state.trigger_early_reveal_if_complete()
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

This debug log is now redundant and incomplete compared to the original implementation, as it no longer includes the all_complete status. Since the logic has been encapsulated into GameState.trigger_early_reveal_if_complete(), the logging should also be moved there to provide consistent observability across all guess types while keeping the websocket handler clean.

        await game_state.trigger_early_reveal_if_complete()

Copy link
Copy Markdown
Owner Author

@mholzi mholzi left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review: LGTM βœ…

Clean refactor that eliminates all private-member access from websocket.py.

Checklist

# Check Result
1 All 6 call sites updated β€” 3Γ— _now() β†’ current_time(), 3Γ— _trigger_early_reveal() β†’ trigger_early_reveal_if_complete() βœ… Confirmed, zero game_state._now( / game_state._trigger_early_reveal( remain in websocket.py
2 Phase check encapsulated correctly β€” trigger_early_reveal_if_complete() checks phase == PLAYING and check_all_guesses_complete() before delegating to _trigger_early_reveal() βœ… Matches the exact guard that was previously duplicated at each call site
3 No logic moved incorrectly β€” the inner _trigger_early_reveal() still re-checks phase under _score_lock (line 987), so the outer check in the new method is a correct fast-path / short-circuit, not the sole race-condition guard βœ…
4 API surface clean β€” current_time() is a simple delegate to the injected clock; trigger_early_reveal_if_complete() is a well-named, self-documenting intent method. Both placed logically in the class βœ…

Net result: +9 / βˆ’0 in state.py, +13 / βˆ’24 in websocket.py β€” less code, better encapsulation, no behavioral change. Good to merge.

@mholzi mholzi merged commit 80c57d4 into main Mar 31, 2026
5 checks passed
@mholzi mholzi deleted the fix/issue-452 branch March 31, 2026 09:18
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Code Review: code-quality] External callers access private game_state._now() and _trigger_early_reveal()

1 participant