Skip to content

ship/add sealed draft event support - #7133

Merged
matthewevans merged 6 commits into
mainfrom
ship/add-sealed-draft-event-support
Aug 9, 2026
Merged

ship/add sealed draft event support#7133
matthewevans merged 6 commits into
mainfrom
ship/add-sealed-draft-event-support

Conversation

@matthewevans

@matthewevans matthewevans commented Aug 9, 2026

Copy link
Copy Markdown
Member
  • feat(draft): add sealed event support
  • fix(draft): preserve cube sizes and version sealed wire
  • fix(draft): align sealed host state and restore checks

Summary by CodeRabbit

  • New Features
    • Added Sealed draft support for set-based events, including six-pack allocation, deckbuilding, and single-match play.
    • Added Quick/Sealed selection for local drafts and Sealed setup for draft pods.
    • Added Sealed labels, descriptions, and setup guidance.
    • Draft views now provide match configuration details for accurate match setup.
  • Bug Fixes
    • Improved validation for draft configuration and persisted sessions.
    • Prevented invalid drafts and unsupported pool configurations from starting or restoring.
  • Compatibility
    • Updated networking protocols to support Sealed drafts.

@matthewevans
matthewevans enabled auto-merge August 9, 2026 20:27
@coderabbitai

coderabbitai Bot commented Aug 9, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Warning

Review limit reached

@matthewevans, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 14 minutes

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

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 199e280d-7c73-458f-a766-7036b4eab16c

📥 Commits

Reviewing files that changed from the base of the PR and between e73d926 and 3c2e102.

📒 Files selected for processing (6)
  • client/src/i18n/locales/de/draft.json
  • client/src/i18n/locales/es/draft.json
  • client/src/i18n/locales/fr/draft.json
  • client/src/i18n/locales/it/draft.json
  • client/src/i18n/locales/pl/draft.json
  • client/src/i18n/locales/pt/draft.json
📝 Walkthrough

Walkthrough

The change adds Sealed drafts across the Rust core, WASM, server, client state, setup pages, persistence, and network protocols. Sealed drafts use six packs, enter deckbuilding, require Set pools, and run Bo1 matches.

Changes

Sealed draft support

Layer / File(s) Summary
Sealed core lifecycle
crates/draft-core/src/types.rs, crates/draft-core/src/session.rs, crates/draft-core/src/view.rs
Adds Sealed draft types, validation, six-pack allocation, deckbuilding transitions, pairing transitions, and match configuration in draft views.
WASM and server integration
crates/draft-wasm/src/lib.rs, crates/phase-server/src/main.rs, crates/server-core/*
Adds Sealed initialization, multiplayer kind mapping, Set-only enforcement, validated persistence restoration, and Sealed pack configuration.
Client adapter and state flow
client/src/adapter/*, client/src/services/*, client/src/stores/*
Propagates draft kinds and match configuration, starts and resumes Sealed drafts, derives phases from views, persists draft kind, and selects Bo1 matches.
Sealed setup interface
client/src/pages/DraftPage.tsx, client/src/pages/DraftPodPage.tsx, client/src/i18n/locales/en/draft.json
Adds Quick/Sealed selection, Sealed descriptions, Set-only selection, and Sealed localization.
Protocol version updates
client/src/adapter/ws-adapter.ts, client/src/network/*, crates/lobby-broker/src/protocol.rs, crates/server-core/src/protocol.rs, scripts/check-protocol-version.mjs
Increments protocol versions and updates Sealed serialization and compatibility tests.

Estimated code review effort: 4 (Complex) | ~60 minutes

Suggested labels: enhancement

Sequence Diagram(s)

sequenceDiagram
  participant DraftPage
  participant draftStore
  participant DraftAdapter
  participant WASM
  participant DraftSession
  DraftPage->>draftStore: select Sealed and start draft
  draftStore->>DraftAdapter: initializeSealed
  DraftAdapter->>WASM: start_sealed_draft
  WASM->>DraftSession: create six-pack Sealed session
  DraftSession-->>WASM: return deckbuilding view
  WASM-->>draftStore: return DraftPlayerView
  draftStore-->>DraftPage: render deckbuilding state
Loading
🚥 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 the primary change: adding sealed draft event support.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch ship/add-sealed-draft-event-support

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.

@coderabbitai coderabbitai Bot left a comment

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.

Actionable comments posted: 7

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@client/src/pages/DraftPage.tsx`:
- Around line 427-447: Increase the touch target for each selectable label in
the localDraftKind radio group by applying a minimum 44pt size to both labels.
Update the label elements surrounding the Quick and Sealed inputs while
preserving their existing layout and selection behavior.

In `@client/src/pages/DraftPodPage.tsx`:
- Around line 190-199: Increase the touch target for the Sealed radio selector
in the draftKind label by applying at least 44pt minimum height and sufficient
padding, while preserving its existing alignment and radio behavior.

In `@client/src/stores/draftPodStore.ts`:
- Around line 137-142: The setConfig update only resets poolMode when kind
changes, allowing later setPoolMode calls to select cube for Sealed
configurations. Enforce the invariant across setConfig and setPoolMode so kind
=== "Sealed" always uses set mode, and validate the normalized configuration
before createPod submits it to WASM. Ensure setup-layer paths preserve set-based
sealed configuration.

In `@client/src/stores/draftStore.ts`:
- Line 530: Replace local Bo1/Bo3 inference with the engine-provided match
configuration: update client/src/stores/draftStore.ts:530-530 and :616-616 to
use its match type, client/src/adapter/p2p-draft-host.ts:251-251 to stop
deriving configuration from kind, and client/src/pages/DraftPage.tsx:47-47 to
show only permitted formats. Add the resolved field to the engine view and wire
it symmetrically through WASM, WebSocket, Tauri, and P2P adapters, including a
round-trip test.

In `@crates/draft-core/src/session.rs`:
- Around line 72-103: Update the sealed snapshot restore validation around the
visible per-seat and lifecycle checks to reuse the same startup configuration
validation as apply_start_draft, rejecting unsupported Swiss and
single-elimination pod sizes and invalid seat counts. Ensure persisted
DraftStatus::Drafting snapshots are accepted only when their packs and pools
represent a valid in-progress Sealed event, while preserving the existing
per-seat vector and pool-size validation.

In `@crates/server-core/src/draft_session.rs`:
- Around line 94-112: Update try_from_persisted to reject persisted snapshots
containing duplicate non-empty player_tokens before calling from_persisted,
while allowing empty tokens to repeat. Return a validation error consistent with
the existing snapshot checks, and add a restore test covering duplicate tokens
and confirming the snapshot is rejected.

In `@crates/server-core/src/draft_wire_guard.rs`:
- Around line 26-27: Update the pod_size validation in the draft wire guard to
use the caller’s DraftKind, allowing pod_size 1 for Quick drafts while requiring
at least 2 for Premier, Traditional, and Sealed drafts. Use the existing typed
DraftKind enum and composable validation helpers rather than retaining a global
minimum or adding an ad hoc special case.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: c963190a-13cc-4eeb-887c-fe24f0b7be67

📥 Commits

Reviewing files that changed from the base of the PR and between cbdfa04 and b965741.

⛔ Files ignored due to path filters (1)
  • client/src/wasm/draft_wasm.d.ts is excluded by !client/src/wasm/**, !**/*.d.ts
📒 Files selected for processing (25)
  • client/src/adapter/draft-adapter.ts
  • client/src/adapter/draftPodHostAdapter.ts
  • client/src/adapter/p2p-draft-host.ts
  • client/src/adapter/server-draft-adapter.ts
  • client/src/adapter/ws-adapter.ts
  • client/src/i18n/locales/en/draft.json
  • client/src/network/__tests__/draftProtocol.test.ts
  • client/src/network/draftProtocol.ts
  • client/src/pages/DraftPage.tsx
  • client/src/pages/DraftPodPage.tsx
  • client/src/services/draftPersistence.ts
  • client/src/services/quickDraftPersistence.ts
  • client/src/stores/draftPodStore.ts
  • client/src/stores/draftStore.ts
  • client/src/stores/multiplayerDraftStore.ts
  • crates/draft-core/src/session.rs
  • crates/draft-core/src/types.rs
  • crates/draft-core/src/view.rs
  • crates/draft-wasm/src/lib.rs
  • crates/lobby-broker/src/protocol.rs
  • crates/phase-server/src/main.rs
  • crates/server-core/src/draft_session.rs
  • crates/server-core/src/draft_wire_guard.rs
  • crates/server-core/src/protocol.rs
  • scripts/check-protocol-version.mjs

Comment thread client/src/pages/DraftPage.tsx
Comment thread client/src/pages/DraftPodPage.tsx
Comment thread client/src/stores/draftPodStore.ts
Comment thread client/src/stores/draftStore.ts Outdated
Comment thread crates/draft-core/src/session.rs
Comment thread crates/server-core/src/draft_session.rs
Comment thread crates/server-core/src/draft_wire_guard.rs Outdated

@coderabbitai coderabbitai Bot left a comment

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.

Actionable comments posted: 4

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@client/src/adapter/p2p-draft-host.ts`:
- Line 2029: Remove the match_config derivation from P2PDraftHost and stop
inferring match_type from this.kind. Populate the lobby match_config using the
engine-owned draft configuration, preserving all MatchConfig fields and engine
defaults.

In `@client/src/stores/draftPodStore.ts`:
- Around line 175-178: Apply the same Sealed-pod source validation in
resumeHostedPod before restoring the persisted session and calling hostDraft.
Reject or clear any restored configuration where kind is "Sealed" and poolInput
represents a Cube source, while preserving valid set-based Sealed configurations
and existing createPod validation.
- Around line 175-178: Replace the hardcoded configError text in the Sealed and
non-set pool branch of the draft pod store with a stable localization key, or
route it through the project i18n service. Update the UI rendering of
configError to resolve that key via t(), while preserving the existing
validation and return behavior.

In `@crates/draft-core/src/session.rs`:
- Around line 81-85: Update the Sealed snapshot validation in the session
restoration flow around the DraftStatus check to reject Lobby snapshots with
non-empty pools, current_pack, or packs_by_seat. Preserve valid empty Lobby
state and existing Drafting rejection, returning the established
invalid-sealed-snapshot error for retained card state.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 69101413-59b2-41d4-8fc5-dc1a3febc316

📥 Commits

Reviewing files that changed from the base of the PR and between b965741 and bcc1acb.

📒 Files selected for processing (19)
  • client/src/adapter/__tests__/draftPodAdapter.test.ts
  • client/src/adapter/__tests__/server-draft-adapter.test.ts
  • client/src/adapter/draft-adapter.ts
  • client/src/adapter/p2p-draft-host.ts
  • client/src/components/draft/__tests__/LimitedDeckBuilder.test.tsx
  • client/src/components/draft/__tests__/PackDisplay.pod.test.tsx
  • client/src/pages/DraftLandingPage.tsx
  • client/src/pages/DraftPage.tsx
  • client/src/pages/DraftPodPage.tsx
  • client/src/stores/__tests__/multiplayerDraftStore.test.ts
  • client/src/stores/draftPodStore.ts
  • client/src/stores/draftStore.ts
  • crates/draft-core/src/session.rs
  • crates/draft-core/src/view.rs
  • crates/phase-server/src/main.rs
  • crates/server-core/src/client_message_wire_guard.rs
  • crates/server-core/src/draft_session.rs
  • crates/server-core/src/draft_wire_guard.rs
  • crates/server-core/src/protocol.rs
🚧 Files skipped from review as they are similar to previous changes (6)
  • crates/server-core/src/protocol.rs
  • client/src/pages/DraftPodPage.tsx
  • crates/server-core/src/draft_session.rs
  • client/src/pages/DraftPage.tsx
  • crates/phase-server/src/main.rs
  • client/src/stores/draftStore.ts

tournament_format: "Swiss",
pod_policy: "Competitive",
pairings: [],
match_config: { match_type: this.kind === "Traditional" ? "Bo3" : "Bo1" },

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.

🗄️ Data Integrity & Integration | 🟠 Major | 🏗️ Heavy lift

Remove the frontend match_config derivation.

Line 2029 infers match rules from this.kind. This duplicates engine-owned game logic in the frontend. It also drops any engine defaults beyond match_type, such as future MatchConfig fields.

Provide the lobby match_config from the engine-owned draft configuration. Do not derive it in P2PDraftHost.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@client/src/adapter/p2p-draft-host.ts` at line 2029, Remove the match_config
derivation from P2PDraftHost and stop inferring match_type from this.kind.
Populate the lobby match_config using the engine-owned draft configuration,
preserving all MatchConfig fields and engine defaults.

Source: Path instructions

Comment on lines +175 to +178
if (config.kind === "Sealed" && poolMode !== "set") {
set({ configError: "Sealed pods require a set pool" });
return;
}

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.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Validate restored pods with the same Sealed source invariant.

Lines 175-178 protect only createPod. resumeHostedPod can still restore a persisted kind: "Sealed" session with a Cube poolInput, then pass it to hostDraft.

Reject or clear this legacy configuration before restore. A Sealed Cube pod is invalid and will later fail core startup validation.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@client/src/stores/draftPodStore.ts` around lines 175 - 178, Apply the same
Sealed-pod source validation in resumeHostedPod before restoring the persisted
session and calling hostDraft. Reject or clear any restored configuration where
kind is "Sealed" and poolInput represents a Cube source, while preserving valid
set-based Sealed configurations and existing createPod validation.

Source: Path instructions


🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Localize the Sealed pool error.

Line 176 adds frontend-authored user-facing text directly in the store. Store a stable error key or use the project i18n service, then render the localized message through t() in the UI.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@client/src/stores/draftPodStore.ts` around lines 175 - 178, Replace the
hardcoded configError text in the Sealed and non-set pool branch of the draft
pod store with a stable localization key, or route it through the project i18n
service. Update the UI rendering of configError to resolve that key via t(),
while preserving the existing validation and return behavior.

Source: Path instructions

Comment on lines +81 to +85
if self.status == DraftStatus::Drafting {
return Err(DraftError::InvalidSealedSnapshot {
reason: "sealed sessions cannot be in drafting status".to_string(),
});
}

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.

🗄️ Data Integrity & Integration | 🟠 Major | 🏗️ Heavy lift

Reject retained Sealed state in Lobby.

Line 81 rejects only DraftStatus::Drafting. A Sealed snapshot in Lobby with non-empty pools, current_pack, or packs_by_seat still passes because the later checks exclude Lobby.

No valid Sealed lifecycle produces card state before start. Reject retained card state for Lobby snapshots. Otherwise a restored lobby can expose stale pools or packs through the player view before the next start replaces them.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@crates/draft-core/src/session.rs` around lines 81 - 85, Update the Sealed
snapshot validation in the session restoration flow around the DraftStatus check
to reject Lobby snapshots with non-empty pools, current_pack, or packs_by_seat.
Preserve valid empty Lobby state and existing Drafting rejection, returning the
established invalid-sealed-snapshot error for retained card state.

Source: Path instructions

@matthewevans
matthewevans added this pull request to the merge queue Aug 9, 2026
Merged via the queue into main with commit 9759165 Aug 9, 2026
13 checks passed
@matthewevans
matthewevans deleted the ship/add-sealed-draft-event-support branch August 9, 2026 22:35
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.

1 participant