Conversation
Winning the ante-8 boss triggers win_game(), which pauses the game and raises the win overlay AFTER play() had already returned won=true. The bot was left in a paused session where every subsequent endless-mode endpoint ran on wall-clock REAL time instead of turbo. The polling event never recovered from the pause because event.lua ignores `created_on_pause` in config (it only honours `pause_force`). Switch to pause_force=true, and dismiss the overlay inside the event once ROUND_EVAL is entered (G.round_eval already exists; the delayed win events guard against a nil G.OVERLAY_MENU). The won path now flows through the existing cash_out_button checkpoint before responding.
Drive the win cycle live instead of via save/load fixtures: `load` resets the run and discards the paused/overlay state, which masked the bug entirely. The play test asserts an endless play stays responsive (elapsed < 5s) rather than crawling on wall-clock time; the gamestate test asserts won=true persists across the cash_out/next_round/select cycle. Removes the now-unused state-SELECTING_HAND--won-true fixture that could not capture the paused/overlay state.
5s is enough for the server health check on the turbo profile; the previous 10s wait added needless delay to the quick-reference example.
Contributor
There was a problem hiding this comment.
Pull request overview
Fixes an endless-mode responsiveness regression caused by the ante-8 win overlay pausing the game and preventing the play endpoint’s polling event from completing, leaving the bot session effectively “stuck” running on wall-clock time.
Changes:
- Update
playendpoint polling to run during pause and dismiss the win overlay duringROUND_EVALso endless mode remains responsive. - Add two live-driven Lua API regression tests to validate endless-mode responsiveness and
won=truepersistence through the endless transition cycle. - Remove an unrepresentable save/load fixture for the paused/overlay win state and shorten the CLI skill doc startup wait.
Reviewed changes
Copilot reviewed 4 out of 5 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
src/lua/endpoints/play.lua |
Keeps the polling event running while paused and dismisses the win overlay before responding, so endless play doesn’t degrade. |
tests/lua/endpoints/test_play.py |
Adds a live regression test asserting endless-mode play remains fast after a win. |
tests/lua/endpoints/test_gamestate.py |
Adds a live regression test asserting won=true persists across the endless round-transition cycle. |
tests/fixtures/fixtures.json |
Removes a fixture that cannot represent the paused/overlay state via save/load. |
.agents/skills/balatrobot/SKILL.md |
Reduces the suggested startup sleep before calling the CLI health check. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Comment on lines
+102
to
+103
| # Drive to the ante-8 boss and win it. | ||
| api(client, "menu") |
| blocking = false, | ||
| blockable = false, | ||
| created_on_pause = true, | ||
| pause_force = true, |
Expose G.SETTINGS.paused as GameState.paused so callers can detect a session stuck behind a blocking overlay (win screen, pause menu, game over). Previously the only externally observable symptom of such a stuck state was wall-clock speed — a signal no clean assertion could rely on. Added to the gamestate extractor, type definition, OpenRPC schema, and API docs.
The endless-mode regression test asserted elapsed < 5s, a flaky wall-clock check that measures speed rather than game state. Rewrite it to assert paused=false on the endless play via the new GameState field. Verified red against the pre-fix play.lua (paused=true failure) and green with the fix restored.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
After winning the ante-8 boss blind,
play()returnedwon=truethe instant the win was detected — butwin_game()fires right after and raises the win overlay (G.OVERLAY_MENU) while pausing the game (G.SETTINGS.paused = true). The bot was left in a paused session where every subsequent endless-mode endpoint ran on wall-clock REAL time instead of turbo, leaving endless play permanently degraded (~9s per play vs ~1.6s).Root cause
Two compounding issues in
src/lua/endpoints/play.lua:The polling event was killed by the pause. The condition event was created with
created_on_pause = true, but Balatro'sevent.luaignores that config field — it only honourspause_force(self.created_on_pause = config.pause_force or G.SETTINGS.paused). The event was created while unpaused, so it gotpause_skip'd the momentwin_game()paused, and never recovered.The overlay was never dismissed. The won branch responded immediately and left the overlay up, keeping the game paused for all following requests.
Fix
In
src/lua/endpoints/play.lua:created_on_pause = true→pause_force = trueso it keeps running while paused.if G.GAME.won and G.OVERLAY_MENU then G.FUNCS.exit_overlay_menu() end. This is safe because the overlay only appears after ROUND_EVAL is entered (soG.round_evalexists), and the delayed win events (Jimbo speech, endless-round text) guard against a nilG.OVERLAY_MENU.has_blind1 and has_cash_out_buttoncheckpoint before responding, instead of short-circuiting early.Tests
Two new regression tests, both driven live rather than via save/load fixtures —
loadresets the run and discards the paused/overlay state, which masked the bug entirely:test_play_endless_mode_after_won— drives a full win, continues into endless mode, and asserts the next endless play stays responsive (elapsed < 5s). This is the test that fails without the fix (~9.5s) and passes with it (~1.6s).test_won_persists_through_endless_cycle— assertswon=truesurvives thecash_out → next_round → selectcycle.Removed the
state-SELECTING_HAND--won-truefixture fromfixtures.json: the save/load mechanism structurally cannot capture the paused/overlay state, so it could not represent this scenario.Verification
make allgreen against the documented environment (.envrc):The responsiveness test requires the turbo profile + headless render set in
.envrc:Under the slow default (
render=headfull), every game op is ~5x slower and theelapsed < 5sassertion becomes unreliable. There is currently no CI workflow in.github/setting these, so reviewers shoulddirenv allow(or source.envrc) before runningtests/lualocally.Commits
a621babfix(lua.endpoints): dismiss win overlay so endless mode stays responsive282244dtest(lua): add endless-mode win overlay regression tests81d2ac7docs(skill): shorten balatrobot serve startup wait