Skip to content

fix(client): bind a whole-match concede for pod bot matches - #7936

Queued
cuinhellcat wants to merge 5 commits into
phase-rs:mainfrom
cuinhellcat:fix/7920-bot-match-concede
Queued

fix(client): bind a whole-match concede for pod bot matches#7936
cuinhellcat wants to merge 5 commits into
phase-rs:mainfrom
cuinhellcat:fix/7920-bot-match-concede

Conversation

@cuinhellcat

@cuinhellcat cuinhellcat commented Aug 26, 2026

Copy link
Copy Markdown
Contributor

Fixes #7920.

What: In a pod draft, conceding a human-vs-bot match did nothing — the menu's Concede is (correctly) gated on a transport-installed whole-match capability, and the bot-match branch of startMatch installs a bare WasmAdapter with no such binding (console: [useConcedeHandler] refused unbound draft pod match concession).

How: WasmAdapter gains a conditional, pod-installed MatchConcedeCapability, mirroring the P2P adapters' bound-capability pattern — plain AI games never bind it and keep their engine-dispatch concede path. The bot-match branch of startMatch binds it to a plain game-level Concede for the local seat (game player 0, the DRAFT_BOT_AI_SEAT authority): CR 104.3a ends the game as a loss, the game-over screen's existing pod effect settles the match through the same path as a played-out game, and "Back to pod" returns to the standings with any next round intact. useConcedeHandler is untouched; its existing bound/unbound tests still hold.

Evidence

  • New capability test: a bare WasmAdapter fails the supportsMatchConcede guard (revert discriminator: an unconditional supportsMatchConcede = true on the class turns it red — and would wrongly reroute every plain AI game's concede); a bound one runs exactly the bound concession.
  • Full client suite: 3277 vitest pass; tsc clean; eslint clean on the touched files.
  • Manual 4-seat bot pod test: Concede settles the match as a loss, returns to the pod page, and the next round remains playable.

Known gap: no store-level unit test drives the startMatch bot branch end to end; that seam is covered by the manual pod test above.

Summary by CodeRabbit

  • New Features
    • Added support for conceding bot matches directly through the match interface.
    • Bot-match concessions now trigger standard game-over handling and match settlement.
  • Bug Fixes
    • Concessions are correctly attributed to the local player and dispatched reliably.
    • Preserved the existing concession flow for standard AI games.

Fixes phase-rs#7920. A pod bot match installs a bare WasmAdapter, so the menu's
Concede was refused as an unbound pod-match concession. The bot-match
branch of startMatch now binds the duck-typed MatchConcedeCapability to
a plain game-level Concede for the local seat (game player 0, the
DRAFT_BOT_AI_SEAT authority): CR 104.3a ends the game as a loss, the
game-over screen's existing pod effect settles the match, and "Back to
pod" returns to the standings with any next round intact.

Not covered: no store-level unit test drives the startMatch bot branch
end to end — verified live in a 4-seat bot pod instead (concede settles
the match, the next round stays playable).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Aug 26, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Warning

Review limit reached

Next included review available in 24 minutes.

View limit details

Limit details: You’ve used all 2 included reviews currently available.

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

Learn how review limits work.

Review configuration:

⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 689d746d-e7db-4f9a-b748-bad906d706f6

📥 Commits

Reviewing files that changed from the base of the PR and between 941393b and e188a90.

📒 Files selected for processing (1)
  • client/src/stores/__tests__/multiplayerDraftStore.test.ts

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 4d3fd4e2-4da8-4f4d-adcb-7198e09b8363

📥 Commits

Reviewing files that changed from the base of the PR and between 42a5a23 and 941393b.

📒 Files selected for processing (1)
  • client/src/stores/__tests__/multiplayerDraftStore.test.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • client/src/stores/tests/multiplayerDraftStore.test.ts

Included review availability: Your plan provides up to 2 included reviews per hour; 0 remain after this review.


📝 Walkthrough

Walkthrough

The change adds conditional whole-match concession support to WasmAdapter. Bot matches bind this capability to dispatch a local-player Concede event. Adapter and multiplayer store tests cover the binding and dispatch behavior.

Changes

Bot Match Concede

Layer / File(s) Summary
WasmAdapter concede capability
client/src/adapter/wasm-adapter.ts, client/src/adapter/__tests__/wasm-match-concede.test.ts
WasmAdapter gains optional concede members and a binding method. Tests verify the unbound and bound states and callback execution.
Bot-match concession binding
client/src/stores/multiplayerDraftStore.ts, client/src/stores/__tests__/multiplayerDraftStore.test.ts
Bot matches bind concession to dispatch { type: "Concede", data: { player_id: 0 } }. Store tests mock the adapter and game loop, then verify the dispatch.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Merge Risk: ⚪ Minimal · up to 94139

This localized change binds concede behavior only for pod bot matches while preserving plain AI-game behavior, with automated and manual validation provided; no actionable merge-blocking risk remains.

Sequence Diagram(s)

sequenceDiagram
  participant GameMenu
  participant WasmAdapter
  participant MultiplayerDraftStore
  participant GameStore
  GameMenu->>WasmAdapter: sendMatchConcede()
  WasmAdapter->>MultiplayerDraftStore: invoke bound concession
  MultiplayerDraftStore->>GameStore: dispatch Concede for player 0
Loading

Suggested reviewers: matthewevans

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely describes binding whole-match concession support for pod bot matches, which is the primary change.
Linked Issues check ✅ Passed The changes address issue #7920 by binding the bot-match WasmAdapter to dispatch a game-level concession for the local seat. The added tests verify capability binding and concession dispatch, while pr…
Out of Scope Changes check ✅ Passed The implementation and tests are directly related to the bot-match concession defect in issue #7920. No unrelated code changes are identified.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 1 functions across 4 files.
Full details: Linked Issues check

Explanation

The changes address issue #7920 by binding the bot-match WasmAdapter to dispatch a game-level concession for the local seat. The added tests verify capability binding and concession dispatch, while preserving the existing plain AI behavior.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@matthewevans

Copy link
Copy Markdown
Member

Deferred by maintainer intake policy — not ignored.

This current head (96266dcf2db412912630c5cb80cc4fa0cd3380df) was triaged as a frontend-only change (client/src/adapter/__tests__/wasm-match-concede.test.ts, client/src/adapter/wasm-adapter.ts, client/src/stores/multiplayerDraftStore.ts) by cuinhellcat. The local frontend-review allowlist does not include this author, so this route does not perform an implementation-diff review or approve the PR.

A maintainer must explicitly take this PR or add a local frontend-review exception before it can receive substantive review. The defer label is a routing marker only, not a verdict on the change.

@matthewevans matthewevans added the defer-fe Frontend/client/UI PR deferred to Matt's direct review label Aug 26, 2026
@matthewevans

Copy link
Copy Markdown
Member

Maintainer note: approve for review.

@matthewevans matthewevans added defer-fe Frontend/client/UI PR deferred to Matt's direct review and removed defer-fe Frontend/client/UI PR deferred to Matt's direct review labels Aug 27, 2026
@matthewevans matthewevans self-assigned this Aug 27, 2026
@matthewevans matthewevans added the bug Bug fix label Aug 27, 2026
@matthewevans

Copy link
Copy Markdown
Member

Maintainer review is complete for current head 42a5a23739e5e71c813f892bdeff51a8d2cf4c53, including the maintainer regression test for the real bot-match launch path. Required Frontend (lint, type-check, test) and Rust (fmt, clippy, test, coverage-gate) checks are still pending; approval and merge-queue enrollment will resume once they settle.

@matthewevans

Copy link
Copy Markdown
Member

The earlier current-head CI hold is superseded. Frontend type-check on 42a5a23739e5e71c813f892bdeff51a8d2cf4c53 identified only the maintainer regression fixture's return type; that local fix is now on current head 941393b669bd7e70ae5b321ff0cb7e61cdbc4a83. Current-head checks are initializing. Approval and merge-queue enrollment will resume after the required Frontend and Rust checks settle.

@matthewevans

Copy link
Copy Markdown
Member

The failed Frontend check on prior head 941393b669bd7e70ae5b321ff0cb7e61cdbc4a83 was a maintainer test-fixture defect, not a production failure: WasmAdapter was mocked with a non-constructible arrow implementation while startMatch constructs it. Maintainer fix e188a9020ee282fc7e91a10f27bdf64edb019da9 makes that mock constructible. The current-head CI run is 33124999494; approval and merge-queue enrollment will resume after its required Frontend and Rust checks settle.

@matthewevans matthewevans left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Approved. Current head e188a9020ee282fc7e91a10f27bdf64edb019da9 binds the existing conditional whole-match-concede capability only for pod bot matches; the real launch path dispatches the local-seat game concession, and current required checks are green.

@matthewevans
matthewevans added this pull request to the merge queue Aug 28, 2026
@matthewevans matthewevans removed their assignment Aug 28, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Bug fix

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Pod draft: conceding a bot match does nothing (no whole-match concede capability on the bot-match adapter)

2 participants