fix(smash): the selector gate stops guessing the lobby too - #1033
Merged
Conversation
andrewgazelka
force-pushed
the
fix/smash-e2e-threshold
branch
2 times, most recently
from
July 28, 2026 11:35
46ca254 to
4b0b21a
Compare
## Effect `smash-selector-e2e` gets the same treatment `smash-e2e` got in #1027. It had the identical defect, twice, and #1019 broke it the same way. ## The two copies `FULL_PLAYERS = 8`, under a comment naming `LobbyConfig::default` as its source, which is what made it a copy. #1019 moved that default to 4 and the number here stayed at 8, still claiming to be it. It is now `--full-clients`, declared by the gate off `SMASH_FULL_PLAYERS` so the two cannot disagree. And "Three, which is one short of `min_players`" above its own `connect(3)`. Three is now one *over* the minimum, so a countdown runs underneath the hub checks instead of after them. Three stays, because the checks need three named roles -- a holder, a watcher, and somebody to take a freed mob -- but it is no longer described as arithmetic on a threshold, because it never was. ## The guard, not the number `hub_only` fails the run if the lobby leaves `Waiting` while the hub checks still need it, called once after the clients join and again before the first claim. It reads the phase off the server's own broadcasts, so it is right whatever the thresholds become. `smash-selector-e2e` now also gets `serverEnv = smashGateLobby.env`, so it runs against the same declared 4/8 lobby as `smash-e2e`. ## One shared helper, and what it deliberately does not do The message both harnesses print is now `hub_lost` in `smash-match.py`, which `smash-selector.py`, `hotbar-check.py`, `hud-check.py` and `identity-check.py` all already load. One failure, one wording. It shares the consequence, not the number, and it cannot share the number. There is no single safe roster: the sweep wants 3, the selector wants 3 for unrelated reasons, and filling a lobby wants 8. The thing that was duplicated is `LobbyConfig::default`, and the right number of copies of that in Python is zero rather than one. A helper returning "the largest roster that will not start" would be the fourth copy wearing a hat, so `hub_lost` says in its own docstring that it will not answer that. ## Stale prose, three files `hotbar-check.py` claimed the lobby needs four players; it needs two, and that gate uses one client so only the reason was wrong. `smash-map-check.py` and `hud-check.py` both called 8 "`full_players`" rather than "at or above it". A comment asserting a false property is worse than no comment.
andrewgazelka
force-pushed
the
fix/smash-e2e-threshold
branch
from
July 28, 2026 11:50
4b0b21a to
c9b6903
Compare
Benchmark Results for generalComparing to 54de2ba |
Benchmark Results for generalComparing to 035a8ce |
Codecov Report✅ All modified and coverable lines are covered by tests. @@ Coverage Diff @@
## main #1033 +/- ##
==========================================
+ Coverage 53.84% 54.07% +0.23%
==========================================
Files 345 347 +2
Lines 32126 32497 +371
Branches 1215 1234 +19
==========================================
+ Hits 17297 17572 +275
- Misses 14556 14644 +88
- Partials 273 281 +8 see 3 files with indirect coverage changes 🚀 New features to boost your workflow:
|
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.
Effect
nix run .#smash-selector-e2egets the treatmentsmash-e2egot in #1027. It had the identical defect, twice, and #1019 broke it the same way. Filed as ENG-10889 while fixing #1027; fixing rather than leaving it filed.The two copies
FULL_PLAYERS = 8, under a comment namingLobbyConfig::defaultas its source, which is exactly what made it a copy. #1019 moved that default to 4 and the number here stayed at 8, still claiming to be it. It is now--full-clients, which the gate derives from its ownSMASH_FULL_PLAYERSso the two cannot disagree."Three, which is one short of
min_players" above its ownconnect(3). Three is now one over the minimum, so a countdown ran underneath the hub checks instead of after them -- the comment's own stated failure mode, arrived at by the comment going stale.Three stays. The checks need three named roles (a holder, a watcher, and somebody to take a freed mob), which is a fact about this gate and not arithmetic on a threshold. What changed is that it is no longer described as arithmetic on a threshold, because it never was.
The guard, not the number
hub_onlyfails the run if the lobby leavesWaitingwhile the hub checks still need it: once after the clients join, and again before the first claim, since the reads in between cost real seconds. It reads the phase off the server's own broadcasts, so it stays right whatever the thresholds become.smash-selector-e2ealso now getsserverEnv = smashGateLobby.env, so it runs against the same declared 4/8 lobby assmash-e2erather than inheriting the product's.One shared helper, and the thing it refuses to do
The message both harnesses print is now
hub_lostinsmash-match.py, whichsmash-selector.py,hotbar-check.py,hud-check.pyandidentity-check.pyall already load. One failure, one wording.It shares the consequence, not the number, and it could not share the number. There is no single safe roster to share: the ability sweep wants 3, the selector wants 3 for entirely unrelated reasons, and filling a lobby wants 8. The thing that was duplicated is
LobbyConfig::default, and the right number of copies of that in Python is zero rather than one. A helper returning "the largest roster that will not start" would be the fourth copy wearing a hat, sohub_lostsays in its own docstring that it will not answer that.No mechanical gate, and what a reviewer now has to catch
I considered a lint over
tools/*.py-- forbid an integer literal on a line mentioningmin_players/full_players, or a module constant named*_PLAYERS. I did not add one. It would have caughtFULL_PLAYERS = 8and would not have caughtconnect(3)under a false comment, which is the other half of this same bug. A regex over the spelling that happened to occur last time is the failure mode that lets the fourth instance through while reading as protection.The real protection is the runtime guard, which does not care how the number was arrived at. It is opt-in, so three things still need a human:
hub_only. Nothing forces adoption.SMASH_*export.--full-clientsand--sweep-clientsonly mean anything because their gate also sets the threshold; the flag and the env have to move together. Onlysmash-e2eandsmash-selector-e2eare wired this way.hud-check.pyandsmash-map-check.pystill pass 8 as "enough to fill" against a server they do not configure. They pass today only because 8 is above afull_playersof 4. If it ever goes above 8 they will quietly stop testing the full band instead of failing. I did not close this: those gates belong to other agents, and closing it means giving themserverEnvtoo. Named here rather than left implicit.Stale prose, three files
hotbar-check.pyclaimed the lobby needs four players. It needs two, and that gate uses one client, so only the reason was wrong.smash-map-check.pycalled 8 "full_players" rather than "at or above it".hud-check.pyhad the same in its docstring; its--clientshelp was already corrected on main by another agent and I took theirs over mine in the rebase.Checks
nix run .#smash-selector-e2e, all 15 claims:Guard broken deliberately, by pointing
smashGateLobby.envat production's 2/4:That run is also the confirmation ENG-10889 was missing: at 2/4 three clients start a countdown 0.72 seconds in, so
smash-selector-e2ereally was broken by #1019 and not merely suspicious. Restored to 4/8 afterwards.smash-e2emay be red on main, but I could not confirm it here, and this PR does not touch itReported honestly rather than overstated:
nix run .#smash-e2efailed on a Sky Squidshields_casterprobe in every run of mine that reached it (5a486fd twice, 54de2ba once), filed as ENG-10906. But I cannot confirm a real bug from this machine, and the ticket now says so.The box has sat at load average ~20 all night (22 users, seven agents). The shield probe hits the caster immediately after the press expecting the immunity tag already applied, which makes it the tightest-timing probe in the sweep and the first to flake under load. When I stacked a
cargocompile on top, the sweep took 377s instead of 121s and five unrelated timing probes failed (both shields, Storm Squid's launches,buffs_meleeon two kits) -- a load signature, not five bugs.The load-immune instrument disagrees with the wire:
events/smash/tests/abilities.rs::every_declared_effect_actually_happenspasses on 54de2ba, and it is not a blind test -- I madearm_shielda no-op and watched it fail in 1.2s with verbatim the same sentence. So the shield works in-process, and the leading explanation for the wire failure is the probe racing the shield's application, not a broken shield. #1032 already retired the old "hit 50ms after the press" probe for exactly this reason. The wire verdict needs CI'sFlakejob or an idle host; ENG-10906 is rescoped to that rather than closed or asserted.None of this is in scope for this PR, which touches only the harness wiring and prose. Its own gate,
smash-selector-e2e, is green.Python:
python3 -m py_compileon all five changed harnesses, andruff checkclean onsmash-selector.pybefore and after.