Skip to content

fix: wire P2P draft server backups to the shared draft-code contract - #6818

Closed
RealDiligent wants to merge 3 commits into
phase-rs:mainfrom
RealDiligent:fix/critical-issue-p2p-backup-upload-path
Closed

fix: wire P2P draft server backups to the shared draft-code contract#6818
RealDiligent wants to merge 3 commits into
phase-rs:mainfrom
RealDiligent:fix/critical-issue-p2p-backup-upload-path

Conversation

@RealDiligent

@RealDiligent RealDiligent commented Jul 30, 2026

Copy link
Copy Markdown
Contributor

Summary

Fixes the inert D-08 P2P draft server-backup path (#6728): production now supplies a backupEndpoint, client draft codes match the shared 6-char uppercase contract used by phase-server, and non-2xx upload/cleanup responses are logged instead of treated as success. Also seeds the first backup upload on draft start so the host claims the row immediately.

Closes #6728

Related: completes the production wiring gap called out on #5370.

Files changed

  • client/src/adapter/draftPodHostAdapter.ts
  • client/src/adapter/p2p-draft-host.ts
  • client/src/adapter/__tests__/p2pDraftHostBackup.test.ts
  • client/src/config/multiplayerServer.ts
  • crates/server-core/src/draft_session.rs
  • crates/server-core/src/lib.rs
  • crates/phase-server/src/admin.rs
  • crates/phase-server/src/main.rs

Track

Non-developer

LLM

Model: Composer
Tier: Standard
Thinking: high

Implementation method (required)

Method: not-applicable — multiplayer P2P backup HTTP contract / client wiring (#6728), not crates/engine parser or rules work

CR references

None.

Verification

  • Required checks ran clean, or the exact CI-owned alternative is stated below.

  • Gate A output below is for the current committed head.

  • Final review-impl below is clean for the current committed head.

  • Both anchors cite existing analogous code at the same seam.

  • npx vitest run src/adapter/__tests__/p2pDraftHostBackup.test.ts — 4 passed

  • npx tsc --noEmit -p tsconfig.json — clean

  • GitHub Actions CI — pending on this PR (Rust unit/integration tests for server-core + phase-server backup handlers; no local cargo on this Windows agent)

Gate A

Gate A N/A — no crates/engine parser changes; combinator script not applicable to this multiplayer backup contract fix.
head=103f4373c1b3fddefa375c68141ffe27183c47c4 base=daf5c1df7b2f372042b1a093558312c1dd150bb4

Anchored on

  • crates/server-core/src/draft_session.rs:674 — existing generate_draft_code() 6-char uppercase alphabet (shared contract source)
  • crates/phase-server/src/main.rs:8203 — existing p2p_backup_delete_tests HTTP harness for GET/DELETE ownership checks (extended for POST round-trip + legacy-code rejection)
  • client/src/adapter/p2p-draft-host.ts:174 — existing backupEndpoint upload gate / D-08 persist path (wired from production constructor)

Final review-impl

Final review-impl PASS head=103f4373c1b3fddefa375c68141ffe27183c47c4
Self-review: production constructor now passes endpoint; draft code generation matches is_valid_draft_code; HTTP status checked; server validator centralized; regression tests cover client shape + server POST reject of draft-xxxxxxxx.

Claimed parse impact

None.

Scope Expansion

None beyond #6728 acceptance criteria (endpoint wiring, shared ID contract, non-2xx diagnostics, HTTP integration coverage). Immediate first-upload seed is included so a fixed path actually claims the backup row at draft start.

Validation Failures

None.

CI Failures

None.

Summary by CodeRabbit

  • New Features

    • Enabled best-effort automatic P2P draft backups to the configured server.
    • Draft codes are now concise six-character uppercase alphanumeric strings.
    • Backup endpoints are derived automatically from multiplayer WebSocket settings.
  • Bug Fixes

    • Improved handling and reporting for failed backup HTTP requests.
    • Rejected legacy or incorrectly formatted draft codes.
    • Ensured sensitive seat token data is excluded from stored backups.
  • Tests

    • Added/expanded coverage for backup upload, endpoint conversion, draft code validation, and full backup lifecycle (POST/GET/DELETE).

@github-actions github-actions Bot added the needs-maintainer AI-contribution PR requires human triage (Non-dev track or unresolved gaps) label Jul 30, 2026
@coderabbitai

coderabbitai Bot commented Jul 30, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

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: 1c7e6735-18d4-4d03-83d5-6a6dbfb54e2a

📥 Commits

Reviewing files that changed from the base of the PR and between 103f437 and 020dc48.

📒 Files selected for processing (2)
  • crates/phase-server/src/main.rs
  • crates/server-core/src/lib.rs
🚧 Files skipped from review as they are similar to previous changes (1)
  • crates/phase-server/src/main.rs

📝 Walkthrough

Walkthrough

The client now generates server-compatible P2P draft codes, resolves and wires the production backup endpoint, enables immediate backup eligibility, reports non-OK responses, and cleans up backups. Server validation is shared and integration-tested through POST, GET, and DELETE flows.

Changes

P2P draft backup

Layer / File(s) Summary
Canonical draft-code contract
client/src/adapter/p2p-draft-host.ts, crates/server-core/src/draft_session.rs, crates/server-core/src/lib.rs
Client and server use six-character uppercase alphanumeric draft codes, with shared validation and tests for legacy formats.
Backup endpoint resolution and wiring
client/src/config/multiplayerServer.ts, client/src/adapter/draftPodHostAdapter.ts, client/src/adapter/__tests__/p2pDraftHostBackup.test.ts
WebSocket URLs resolve to HTTP backup origins, which are supplied to production P2PDraftHost instances and covered by contract tests.
Backup upload and cleanup behavior
client/src/adapter/p2p-draft-host.ts, client/src/adapter/__tests__/p2pDraftHostBackup.test.ts
Draft startup enables immediate backup eligibility, while upload and cleanup warn on non-OK HTTP responses and preserve best-effort handling.
Server validation and round-trip coverage
crates/phase-server/src/admin.rs, crates/phase-server/src/main.rs
The server imports shared draft-code validation and tests legacy-code rejection plus POST–GET–DELETE persistence with sensitive seat tokens removed.

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

Sequence Diagram(s)

sequenceDiagram
  participant DraftPodHostAdapter
  participant P2PDraftHost
  participant PhaseServer
  DraftPodHostAdapter->>P2PDraftHost: initialize with resolved backup endpoint
  P2PDraftHost->>P2PDraftHost: startDraft generates six-character draft code
  P2PDraftHost->>PhaseServer: POST /p2p-draft-backup with snapshot
  PhaseServer-->>P2PDraftHost: HTTP response status
  P2PDraftHost->>PhaseServer: DELETE backup on cleanup
Loading

Suggested labels: bug

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 describes wiring P2P server backups to the shared draft-code contract.
Linked Issues check ✅ Passed The PR addresses both blockers and adds HTTP/SQLite backup lifecycle coverage for #6728.
Out of Scope Changes check ✅ Passed The changes stay focused on P2P backup wiring, code validation, and related tests.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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

Production omitted backupEndpoint and sent draft-xxxxxxxx IDs that phase-server rejects, so D-08 backups never landed. Align client generation with is_valid_draft_code, pass the lobby HTTP origin, and surface non-2xx upload failures.

Closes phase-rs#6728

Co-authored-by: Cursor <cursoragent@cursor.com>

@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: 1

🤖 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/config/multiplayerServer.ts`:
- Around line 35-36: Update the WebSocket URL predicate in the multiplayer
server URL converter to recognize both `/ws` and `/ws/` suffixes, while
preserving the existing pathname normalization. Extend the converter tests to
cover a URL ending in `/ws/` and verify it produces the root backup path.
🪄 Autofix (Beta)

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: 7e2af08c-605e-45fc-8105-d37824640aeb

📥 Commits

Reviewing files that changed from the base of the PR and between daf5c1d and 103f437.

📒 Files selected for processing (8)
  • client/src/adapter/__tests__/p2pDraftHostBackup.test.ts
  • client/src/adapter/draftPodHostAdapter.ts
  • client/src/adapter/p2p-draft-host.ts
  • client/src/config/multiplayerServer.ts
  • crates/phase-server/src/admin.rs
  • crates/phase-server/src/main.rs
  • crates/server-core/src/draft_session.rs
  • crates/server-core/src/lib.rs

Comment on lines +35 to +36
if (url.pathname === "/ws" || url.pathname.endsWith("/ws")) {
url.pathname = url.pathname.replace(/\/ws\/?$/, "") || "/";

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

Handle WebSocket URLs ending in /ws/.

For wss://host/ws/, pathname is /ws/, so the condition is false and backups are sent to /ws/p2p-draft-backup instead of /p2p-draft-backup. Match the optional trailing slash in the predicate and add that case to the converter test.

Proposed fix
-    if (url.pathname === "/ws" || url.pathname.endsWith("/ws")) {
+    if (/\/ws\/?$/.test(url.pathname)) {
       url.pathname = url.pathname.replace(/\/ws\/?$/, "") || "/";
     }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if (url.pathname === "/ws" || url.pathname.endsWith("/ws")) {
url.pathname = url.pathname.replace(/\/ws\/?$/, "") || "/";
if (/\/ws\/?$/.test(url.pathname)) {
url.pathname = url.pathname.replace(/\/ws\/?$/, "") || "/";
🤖 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/config/multiplayerServer.ts` around lines 35 - 36, Update the
WebSocket URL predicate in the multiplayer server URL converter to recognize
both `/ws` and `/ws/` suffixes, while preserving the existing pathname
normalization. Extend the converter tests to cover a URL ending in `/ws/` and
verify it produces the root backup path.

Source: Path instructions

jak-glitch and others added 2 commits July 31, 2026 00:06
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
@matthewevans

Copy link
Copy Markdown
Member

Closed without implementation-diff review.

This PR was opened on 2026-07-30, after the 2026-07-24 applicability cutoff in docs/AI-CONTRIBUTOR.md §0.1.1. Its canonical PR-body declaration is Model: Composer and Tier: Standard.

The version of that policy live before this PR was opened (commit b2628643, 2026-07-23) requires Frontier-tier models only and explicitly lists every composer-* model as not accepted. Section §0.1.4 also requires the canonical declaration Tier: Frontier; a non-Frontier declaration for a post-cutoff PR is closed as out of policy without an implementation review.

This is a policy gate, not a technical assessment of the change or of the contributor. Please open a fresh PR produced with an accepted Frontier-tier model and the required contribution evidence.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

needs-maintainer AI-contribution PR requires human triage (Non-dev track or unresolved gaps)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

P2P server backups are never uploaded and use a server-invalid draft code

3 participants