Skip to content

Fix combat phase stops during auto-pass - #7308

Merged
matthewevans merged 3 commits into
phase-rs:mainfrom
traemyn:fix/combat-phase-stop-autopass
Aug 12, 2026
Merged

Fix combat phase stops during auto-pass#7308
matthewevans merged 3 commits into
phase-rs:mainfrom
traemyn:fix/combat-phase-stop-autopass

Conversation

@traemyn

@traemyn traemyn commented Aug 12, 2026

Copy link
Copy Markdown
Contributor

Summary

Fixes combat phase stops during auto-pass. Combat Damage and End Combat now expose their normal priority windows so configured stops can interrupt auto-pass, while the frontend keeps Declare Attackers and Declare Blockers controls visible when an auto-pass session is armed. End Combat state now remains live through its priority window and is torn down only when the step ends.

Files changed

  • client/src/components/board/ActionButton.tsx
  • client/src/components/board/tests/ActionButton.test.tsx
  • crates/engine/src/game/engine_phase_trigger_regression_tests.rs
  • crates/engine/src/game/engine_tests.rs
  • crates/engine/src/game/turns.rs
  • crates/engine/tests/integration/issue_3279_song_of_dryads.rs
  • crates/engine/tests/integration/last_night_together_restricted_combat.rs
  • crates/engine/tests/integration/loop_shortcut.rs

Track

Developer

LLM

Model: GPT-5.6 Luna (via GitHub Copilot; canonical id not exposed)
Tier: Frontier
Thinking: high

Implementation method (required)

Method: /engine-implementer

Note

Any change to crates/engine/ game logic — parser, effects, resolver, targeting, rules behavior — is expected to go through /engine-implementer.
This change follows that engine implementation pipeline, including an independent blocker review and fix round.

CR references

CR 117.3a, CR 508.8, CR 510.4, CR 511.1, CR 511.2, CR 511.3, CR 724.2d, CR 724.2e.

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.

  • cargo fmt --all — passed on the merged head.

  • cargo clippy --all-targets -- -D warnings — passed on the merged head.

  • cargo test -p phase-engine — 18,901 unit tests passed, 4,851 integration tests passed, 0 failed; auxiliary test binaries passed (21 and 9 tests); doc-test harness completed with 7 ignored tests and 0 failures.

  • ./scripts/gen-card-data.sh with MTGJSON_SKIP_REFRESH=1 — completed on the merged head using the complete local MTGJSON cache; 35,798 faces indexed, 31,794 cards supported, 992 existing parser warnings across 3 categories.

  • cargo coverage — 31,794 of 35,798 cards supported (88.815%).

  • cargo semantic-audit — completed for 32,753 cards with the existing baseline of 266 findings; no parser files or card parse behavior changed in this PR.

  • Merged-source frontend protocol/type-check — passed.

  • Merged-source ESLint — 0 errors and 30 existing warnings.

  • Merged-source Vitest — 297 test files passed, 3 skipped; 2,698 tests passed, 12 todo.

  • The initial PR CI failures were infrastructure-only: Rust shard 4 and Card data failed while downloading the sccache binary with socket hang up, before compilation or tests. No source failure was reported. CI is rerunning for the updated head.

Gate A

Gate G PASS (router/grant architecture: strict router vs permissive grant boundary intact)
Gate A PASS head=08340f1b64018993a7fce0c9f82e74fa4ae5b368 base=dae3de6c1e95c71c872270510d4f75016dcb8662

Anchored on

  • crates/engine/src/game/turns.rs:2825 — existing Upkeep turn-interpreter branch that returns the active-player priority window after step actions and triggers.
  • crates/engine/src/game/turns.rs:2866 — existing Draw turn-interpreter branch that follows the same priority-window pattern.
  • client/src/components/board/ActionButton.tsx:334 — existing Declare Blockers controls remain available while an auto-pass session is armed.

Final review-impl

Final review-impl PASS head=08340f1b64018993a7fce0c9f82e74fa4ae5b368

The maintainer blocker was independently validated and fixed: normal End Combat teardown is centralized in complete_end_combat_teardown and runs when advance_phase_once leaves End Combat, while explicit combat skips reuse the same helper. The regression test proves combat and the attacker restriction remain live during End Combat priority and clear after the pass loop.

Claimed parse impact

None.

Scope Expansion

None. The blocker fix remains within combat phase-stop and phase-transition behavior.

Validation Failures

None.

CI Failures

None unresolved. The prior failed CI jobs were external sccache download/bootstrap failures, not code or test failures; the updated head has been pushed for a fresh CI run.

Summary by CodeRabbit

  • Bug Fixes
    • Combat controls remain available while auto-pass is enabled, including “Attack with All” and “Attack with None.”
    • Empty-attacker combat now correctly pauses during combat-damage and end-of-combat priority windows.
    • End-of-combat triggers and cleanup now resolve before advancing to the next phase.
    • Fixed combat progression when inserted phases occur during end-of-combat processing.

@traemyn
traemyn requested a review from matthewevans as a code owner August 12, 2026 17:56
@coderabbitai

coderabbitai Bot commented Aug 12, 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: 1484197b-8aaf-4f3e-83d8-a4c7f08f2889

📥 Commits

Reviewing files that changed from the base of the PR and between 97616b1 and 08340f1.

📒 Files selected for processing (3)
  • crates/engine/src/game/engine_phase_trigger_regression_tests.rs
  • crates/engine/src/game/turns.rs
  • crates/engine/tests/integration/last_night_together_restricted_combat.rs
🚧 Files skipped from review as they are similar to previous changes (2)
  • crates/engine/src/game/engine_phase_trigger_regression_tests.rs
  • crates/engine/src/game/turns.rs

📝 Walkthrough

Walkthrough

The client keeps attacker controls active during turn-boundary auto-pass. The engine now exposes priority after combat damage and during EndCombat, including after empty attacker declarations. Tests cover phase stops, inserted phases, combat transitions, and updated cascade timing.

Changes

Combat priority and auto-pass

Layer / File(s) Summary
Attacker controls during auto-pass
client/src/components/board/ActionButton.tsx, client/src/components/board/__tests__/ActionButton.test.tsx
Attacker and blocker modes remain actionable during auto-pass. Attacker controls render during ending-turn auto-pass. Tests cover both attacker actions.
Combat phase priority flow
crates/engine/src/game/turns.rs
Empty attacker declarations enter EndCombat. Combat damage and EndCombat expose active-player priority before teardown and advancement.
Phase-stop regression coverage
crates/engine/src/game/engine_phase_trigger_regression_tests.rs, crates/engine/src/game/turns.rs
Regression tests cover auto-pass interruption in CombatDamage and EndCombat, preserved combat state, and inserted-phase advancement through EndCombat.
Integration expectations
crates/engine/src/game/engine_tests.rs, crates/engine/tests/integration/issue_3279_song_of_dryads.rs, crates/engine/tests/integration/last_night_together_restricted_combat.rs, crates/engine/tests/integration/loop_shortcut.rs
Tests pass the added EndCombat window and update bounded-offer expectations for cascade timing.

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

Mergeability Score: ⚪ Minimal · up to 08340

The change restores normal combat priority windows during auto-pass and keeps the relevant combat controls visible; no actionable merge-blocking risk remains after normal checks and review.

Sequence Diagram(s)

sequenceDiagram
  participant advance_after_empty_attackers
  participant EndCombat
  participant active_player
  participant PostCombatMain
  advance_after_empty_attackers->>EndCombat: mark EndCombat
  EndCombat->>active_player: expose priority
  active_player->>EndCombat: pass priority
  EndCombat->>PostCombatMain: tear down combat and advance
Loading

Possibly related PRs

  • phase-rs/phase#6845: Both changes modify combat attacker declarations and turn-boundary auto-pass behavior.
  • phase-rs/phase#7051: Both changes modify empty-attacker combat handling and auto-pass behavior.
  • phase-rs/phase#7080: Both changes modify combat phase progression and priority handling.

Suggested labels: area:engine

Suggested reviewers: matthewevans

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
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.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely describes the primary change: fixing combat phase stops during auto-pass.
✨ 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.

@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 `@crates/engine/src/game/turns.rs`:
- Around line 3034-3039: The EndCombat transition currently clears combat state
and EndOfCombat effects before the CR 511.1 priority window. In
crates/engine/src/game/turns.rs:3034-3039, keep combat and EndOfCombat state
intact while returning AutoAdvanceStep::waiting for priority, then move combat
removal and EndOfCombat pruning to the transition out of Phase::EndCombat after
all players pass, with a verified CR citation. In
crates/engine/src/game/engine_phase_trigger_regression_tests.rs:170-192, add
production-path assertions that both remain active during EndCombat priority and
are cleared after transitioning to PostCombatMain.
🪄 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: e14ebeb8-a915-45bb-9f7e-d30748e9ae1a

📥 Commits

Reviewing files that changed from the base of the PR and between dc3a57a and 97616b1.

📒 Files selected for processing (7)
  • client/src/components/board/ActionButton.tsx
  • client/src/components/board/__tests__/ActionButton.test.tsx
  • crates/engine/src/game/engine_phase_trigger_regression_tests.rs
  • crates/engine/src/game/engine_tests.rs
  • crates/engine/src/game/turns.rs
  • crates/engine/tests/integration/issue_3279_song_of_dryads.rs
  • crates/engine/tests/integration/loop_shortcut.rs

Comment thread crates/engine/src/game/turns.rs
@matthewevans matthewevans self-assigned this Aug 12, 2026

@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.

Request changes — EndCombat teardown is occurring before the step's priority window.

🔴 Blocker

crates/engine/src/game/turns.rs:3013-3027 clears combat, the combat-attacker restriction, and every EndOfCombat expiry before turns.rs:3034-3039 returns WaitingFor::Priority. That makes the state visible during the EndCombat priority window inconsistent with the rules. Verified CR evidence:

  • CR 511.1: “Once it begins, the active player gets priority.”
  • CR 511.2: “Effects that last ‘until end of combat’ expire at the end of the combat phase.”
  • CR 511.3: “As soon as the end of combat step ends, all creatures, battles, and planeswalkers are removed from combat.”

Move the normal-combat teardown to the transition out of Phase::EndCombat (the phase-transition authority around advance_phase_once), after all players pass. Keep it centralized with the existing expiry cleanup so combat, attacker restrictions, continuous effects, replacement definitions, and pending damage replacements have one rules-correct boundary.

crates/engine/src/game/engine_phase_trigger_regression_tests.rs:170-192 currently proves only that combat is absent after reaching PostCombatMain. Add a production-path regression that proves combat and an EndOfCombat-expiring effect/restriction are still present while priority is offered in EndCombat, then both are gone after the pass loop transitions to PostCombatMain.

Recommendation: request changes; preserve the priority windows, but defer EndCombat teardown until the step actually ends.

@matthewevans matthewevans added the bug Bug fix label Aug 12, 2026
@matthewevans matthewevans removed their assignment Aug 12, 2026
@github-actions

Copy link
Copy Markdown

Generated for head 08340f1b64018993a7fce0c9f82e74fa4ae5b368.

Parse changes introduced by this PR

✓ No card-parse changes detected.

@traemyn
traemyn requested a review from matthewevans August 12, 2026 22:30
@matthewevans matthewevans self-assigned this Aug 12, 2026

@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 — the End Combat teardown now runs only when leaving the step, after its priority window. The current head preserves combat state through priority and clears it on the transition to postcombat main.

@matthewevans
matthewevans added this pull request to the merge queue Aug 12, 2026
@matthewevans matthewevans removed their assignment Aug 12, 2026
Merged via the queue into phase-rs:main with commit 0327b57 Aug 12, 2026
15 checks passed
@traemyn
traemyn deleted the fix/combat-phase-stop-autopass branch August 14, 2026 21:47
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.

2 participants