Skip to content

feat(queue): captain mode#639

Draft
garrappachc wants to merge 18 commits into
masterfrom
feat/captain-mode
Draft

feat(queue): captain mode#639
garrappachc wants to merge 18 commits into
masterfrom
feat/captain-mode

Conversation

@garrappachc

Copy link
Copy Markdown
Member

Summary

  • Add src/queue-captain/ module with full captain mode queue logic: join/leave/ready-up/pick/ban-map/kick/reset operations
  • State machine: waiting → ready → draft → launching; captains selected from opted-in eligible players; ABBA pick order; 3-map pool with one ban per captain before game launch
  • New shared src/queue/plugins/handle-mode-change.ts that atomically clears both queue collections on mode switch and emits queue/mode:changed
  • All captain plugins are mode-aware via isActive flag — zero overhead when running in auto mode
  • New config entries: queue.captain_min_games, queue.captain_pick_timeout; new QueueState.draft; new DB collections queue.players and queue.draft
  • games/create.ts extended with overload for captain draft → game slot conversion

Test plan

  • Build passes (pnpm build)
  • Lint passes (pnpm lint)
  • Switch to captain mode via admin config: verify both queue collections are cleared and all players kicked
  • Switch back to auto mode: verify captain state is cleaned up
  • Captain join/leave/ready-up flow
  • Captain selection and ABBA pick sequence
  • Map ban sequence (BLU bans first, RED second, remaining map selected)
  • Pick timeout: timed-out captain is kicked; draft resets; queue re-evaluates
  • canFormTeams bipartite matching: verify queue enters ready-up only when team can actually be filled from offered classes

🤖 Generated with Claude Code

@github-actions

github-actions Bot commented May 29, 2026

Copy link
Copy Markdown
Contributor

Playwright test results

passed  206 passed
flaky  1 flaky

Details

stats  207 tests across 33 suites
duration  8 minutes, 16 seconds
commit  d091481

Flaky tests

chromium › 50-captain-queue/03-draft-flow.spec.ts › full captain draft: picks, map bans, game launch @6v6

@garrappachc garrappachc force-pushed the feat/captain-mode branch 2 times, most recently from ebb8355 to a9e39b8 Compare May 29, 2026 21:19
garrappachc and others added 8 commits May 30, 2026 13:28
Add captain mode as an alternative to auto queue mode. Captains are
selected from opted-in eligible players, then pick teams sequentially
using ABBA order with map banning before game launch.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Add full client-facing UI for captain mode: class columns with join/leave
buttons, wants-captain toggle, draft board with team columns/pick order
tracker/player pool, map ban panel, and real-time WebSocket sync.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Add CSS for class columns, player cards, wants-captain toggle, draft
board (team columns, pick-order dots, player pool, pick buttons),
map ban panel, and selected map display.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Add Playwright e2e tests covering: join/leave class columns, multi-class
offers, wants-captain toggle + crown icon, and full draft flow (ABBA picks,
map bans, game launch). Includes a new /admin/queue settings page for
switching queue mode and configuring captain min-games and pick timeout.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Add `as 'safe'` casts and `!!` boolean coercion to suppress false-positive
XSS scan errors for enum-derived strings and async component array rendering.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- emit queue/players:updated from setWantsCaptain so crown icon
  re-renders on player cards after toggling wants-captain
- always render #draft-board in DOM so HTMX can OOB swap into it
  when state transitions from ready to draft
- clear #captain-queue when broadcasting draft board on state change

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Unchecked HTML checkboxes are excluded from form serialization, so
untoggling the captain wish never sent the wantsCaptain value via
ws-send. Switch to hx-vals so the value is always included.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
garrappachc and others added 8 commits May 30, 2026 14:21
The ready-up dialog was never shown to captain mode players because
queue-auto/sync-clients only finds players in queueSlots (empty in
captain mode). Add a queue/state:updated handler for QueueState.ready
in captain sync-clients that shows the dialog to unready queuePlayers.
Also close the dialog after a successful ready-up in the captain
gateway listener.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
queueState.last is only set by queue-auto/join, so in captain mode it
is always undefined. Previously setState(QueueState.ready) would throw
before emitting queue/state:updated, silently preventing the ready-up
transition from completing. Make the pre-ready block conditional on
last being present.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The ready-up dialog (modal) is shown as soon as all 12 slots fill.
Having the first two captain candidates join and opt in before the
remaining 10 join ensures the toggle is checked while state is still
'waiting' and the modal hasn't appeared yet.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The remainingSlots loop was checking `picked < total` for every slot
in allSlots, pushing the full total count instead of total-picked.
This made the pool appear too small and rejected valid picks.

Also replaced the captain-slot subtraction heuristic (which assumed
captains fill the last 2 classes) with wildcard captain entries.
In create.ts captains fill the first unfilled class regardless of
their offeredClasses, so they are effectively class-agnostic in the
bipartite matching.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
After all picks are made, reset currentTurn to blu so the map-ban UI
shows the correct captain. Also persist currentTurn after each ban so
the next banning captain is reflected in the draft board.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
After the last ban, the view transitions to the selected-map panel so
.map-ban-card.banned disappears. The selectedMap assertion that follows
the loop is sufficient to confirm the ban was registered.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
When the server was already in captain mode from a previous test run,
the fixture skipped setQueueMode and left captainMinGames at its
persisted value, making test players ineligible. Now always call
setQueueMode so captainMinGames=0 is enforced regardless of current
queue mode.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
After a captain-mode game is created the draft board was never cleared
from connected clients' pages. Three changes:

- Add auto-reset plugin: listens for game:created and calls reset(),
  which deletes captainDraft and queuePlayers (mirroring queue-auto's
  auto-reset for the captain-specific collections).
- Add CaptainQueueSection component: renders the captain queue form so
  it can be sent over WebSocket when restoring the queue view.
- Handle QueueState.waiting in sync-clients: clears <div id="draft-board">
  and restores the captain queue form for all clients on the queue page.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@garrappachc garrappachc changed the title feat(queue): implement captain mode queue backend feat(queue): captain mode Jun 1, 2026
garrappachc and others added 2 commits June 1, 2026 18:03
The feasibility check in pick() was global (across both teams), so a
captain could pick more than cls.count players of a class for their team
as long as the global total was still valid. This produced invalid lineups
(e.g. 3 soldiers on one team, 1 on the other).

- Server: reject a pick if the picking team already has cls.count players
  for that class.
- UI: hide pick buttons for classes the picking team has already filled,
  so captains can only click valid options.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Adds an e2e test verifying that when the BLU captain fails to pick
within the timeout, the queue reverts to the waiting state and the
draft board is cleared.

Also extends AdminPage.setQueueMode to accept captainPickTimeout, and
makes the captain-mode fixture always reset the timeout to 60 s so
tests don't interfere with each other.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@garrappachc garrappachc marked this pull request as draft June 12, 2026 13:23
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

1 participant