Skip to content

fix(engine): resolve recent hang and crash reports - #6633

Merged
matthewevans merged 9 commits into
mainfrom
ship/fix-hang-crash-reports
Jul 25, 2026
Merged

fix(engine): resolve recent hang and crash reports#6633
matthewevans merged 9 commits into
mainfrom
ship/fix-hang-crash-reports

Conversation

@matthewevans

@matthewevans matthewevans commented Jul 25, 2026

Copy link
Copy Markdown
Member
  • fix(engine): finalize zero-target divided triggers
  • fix(engine): respect sibling links after deferred effects
  • fix(ai): use legal spell target fallback
  • feat(engine): resolve face-down exile piles atomically

Summary by CodeRabbit

  • New Features

    • Added support for an effect that exiles an explicit object and the top cards of a player’s library as a face-down pile.
    • After resolution, face-down pile cards return to the library in the correct order (including the shuffling/replacement flow).
  • Bug Fixes

    • Preserves “face-down in exile” behavior across paused and resumed zone-move deliveries.
    • Fixed deferred target traversal so stack targets don’t incorrectly resolve across conditional boundaries.
    • Resolved divided-trigger 0/1-target edge cases to avoid hanging.
    • Improved spell target fallback behavior for stale vs current legal targets.

@matthewevans
matthewevans enabled auto-merge July 25, 2026 17:25
@coderabbitai

coderabbitai Bot commented Jul 25, 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: 24 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

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: cdfbd8a4-bbb6-471e-bdf6-31d837ba3604

📥 Commits

Reviewing files that changed from the base of the PR and between 301be6d and a235fb3.

📒 Files selected for processing (2)
  • crates/engine/src/game/ability_utils.rs
  • crates/phase-ai/src/search.rs
📝 Walkthrough

Walkthrough

The PR adds the ExileFaceDownPile effect across parsing, typing, profiling, resolution, zone delivery, and batch completion. It also centralizes deferred target traversal, handles empty divided-trigger distributions, and improves AI fallback behavior for target selection.

Changes

Face-down pile exile

Layer / File(s) Summary
Effect contract and parser integration
crates/engine/src/types/ability.rs, crates/engine/src/parser/oracle_effect/*, crates/engine/src/parser/oracle_ir/*
Defines and parses ExileFaceDownPile, including parser coverage for the Triumph of Saint Katherine text.
Face-down delivery and batch completion
crates/engine/src/game/effects/*, crates/engine/src/game/zone_pipeline.rs, crates/engine/src/types/game_state.rs, crates/engine/src/game/engine_resolution_choices.rs
Exiles pile members face-down, preserves concealment across pauses, and returns fully delivered members to the library.
Effect scanning and profiling
crates/engine/src/game/ability_*.rs, crates/engine/src/game/coverage.rs, crates/engine/src/game/trigger_index.rs, crates/engine/src/analysis/*, crates/phase-ai/src/policies/*
Adds scanner, profiler, coverage, census, choice-freedom, randomness, projection, trigger, and policy handling for the new effect.

Deferred target traversal

Layer / File(s) Summary
Stack-target tail traversal
crates/engine/src/game/ability_utils.rs
Centralizes deferred-chain traversal for target collection, assignment, sink detection, and minimum-target calculation.

Divided trigger distribution

Layer / File(s) Summary
Distribution completion branches
crates/engine/src/game/engine_stack.rs, crates/engine/src/game/triggers.rs
Handles zero, one, and multiple assigned targets without leaving empty-target triggers waiting for distribution.

AI target-selection fallback

Layer / File(s) Summary
Current target fallback
crates/phase-ai/src/search.rs
Uses current legal target actions, supports optional empty selections, and cancels required empty selections.

Estimated code review effort: 5 (Critical) | ~120 minutes

Suggested labels: bug

Sequence Diagram(s)

sequenceDiagram
  participant OracleParser
  participant EffectResolver
  participant ZonePipeline
  participant GameState
  OracleParser->>EffectResolver: produce ExileFaceDownPile
  EffectResolver->>ZonePipeline: submit pile exile moves
  ZonePipeline->>GameState: settle and conceal exiled members
  ZonePipeline-->>EffectResolver: deliver batch completion
  EffectResolver->>ZonePipeline: schedule library-top return
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 accurately reflects the PR’s main goal of fixing engine hangs and crashes, even though it is broad.
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 unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch ship/fix-hang-crash-reports

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: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
crates/engine/src/game/coverage.rs (1)

3581-3646: 🎯 Functional Correctness | 🔴 Critical | ⚡ Quick win

Duplicate match arm: Effect::ExileFaceDownPile is already handled at Line 2304, causing an unreachable pattern here.

Effect::ExileFaceDownPile { .. } was added to this catch-all "no interesting parameters" group, but the variant is already exhaustively bound and handled by the dedicated arm at Line 2304-2312 earlier in the same match. This makes the Line 3604 pattern unreachable — confirmed by the static analysis hints, which show this exact line failing both "Card data (generate, validate, coverage)" and "WASM compile check" with unreachable pattern.

🐛 Proposed fix: remove the duplicate arm
         | Effect::Cloak { .. }
-        | Effect::ExileFaceDownPile { .. }
         | Effect::RuntimeHandled { .. }
🤖 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/engine/src/game/coverage.rs` around lines 3581 - 3646, Remove the
duplicate Effect::ExileFaceDownPile { .. } pattern from the catch-all arm in the
coverage match. Keep its existing dedicated handling near the earlier match arm
unchanged, and preserve the remaining no-parameter variants.

Source: Linters/SAST tools

🤖 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/types/ability.rs`:
- Around line 11406-11421: Update the exhaustive matches in Effect::count_expr
and Effect::count_expr_mut to include ExileFaceDownPile, returning or mutably
exposing its count QuantityExpr alongside the existing ExileTop handling. Leave
target_filter and the object field behavior unchanged.

In `@crates/phase-ai/src/search.rs`:
- Around line 874-879: Add a `WaitingFor::TargetSelection { .. }` arm to the
later `match &state.waiting_for` near the existing `TriggerTargetSelection` arm,
marking it unreachable because the earlier return handles that state. Keep the
current trigger-target behavior unchanged and ensure the match remains
exhaustive for future compiler checks.

---

Outside diff comments:
In `@crates/engine/src/game/coverage.rs`:
- Around line 3581-3646: Remove the duplicate Effect::ExileFaceDownPile { .. }
pattern from the catch-all arm in the coverage match. Keep its existing
dedicated handling near the earlier match arm unchanged, and preserve the
remaining no-parameter variants.
🪄 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: 42355e85-1d46-479a-8464-5e184ba9e2f7

📥 Commits

Reviewing files that changed from the base of the PR and between 0446b31 and f5774b8.

📒 Files selected for processing (18)
  • crates/engine/src/game/ability_rw.rs
  • crates/engine/src/game/ability_scan.rs
  • crates/engine/src/game/ability_utils.rs
  • crates/engine/src/game/coverage.rs
  • crates/engine/src/game/effects/exile_face_down_pile.rs
  • crates/engine/src/game/effects/mod.rs
  • crates/engine/src/game/elimination.rs
  • crates/engine/src/game/engine_resolution_choices.rs
  • crates/engine/src/game/engine_stack.rs
  • crates/engine/src/game/layers.rs
  • crates/engine/src/game/trigger_index.rs
  • crates/engine/src/game/triggers.rs
  • crates/engine/src/game/zone_pipeline.rs
  • crates/engine/src/parser/oracle_effect/mod.rs
  • crates/engine/src/parser/oracle_effect/tests.rs
  • crates/engine/src/types/ability.rs
  • crates/engine/src/types/game_state.rs
  • crates/phase-ai/src/search.rs

Comment thread crates/engine/src/types/ability.rs
Comment thread crates/phase-ai/src/search.rs

@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/parser/oracle_effect/sequence.rs`:
- Around line 6116-6117: Annotate the Effect::ExileFaceDownPile branch in the
effect grouping with the CR 406.3 + CR 608.2c + CR 701.24a basis, preserving its
existing dig-lookback-transparent behavior and keeping the adjacent
Effect::ExileTop branch unchanged.
🪄 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: 9dc97c42-7b26-43f2-bb53-47411abb6a5a

📥 Commits

Reviewing files that changed from the base of the PR and between f5774b8 and 3ef8c41.

📒 Files selected for processing (11)
  • crates/engine/src/analysis/ability_graph.rs
  • crates/engine/src/game/coverage.rs
  • crates/engine/src/game/effects/exile_face_down_pile.rs
  • crates/engine/src/game/printed_cards.rs
  • crates/engine/src/parser/oracle_effect/mod.rs
  • crates/engine/src/parser/oracle_effect/sequence.rs
  • crates/engine/src/parser/oracle_ir/doc.rs
  • crates/engine/src/types/ability.rs
  • crates/phase-ai/src/policies/effect_classify.rs
  • crates/phase-ai/src/policies/redundancy_avoidance.rs
  • crates/phase-ai/src/search.rs
💤 Files with no reviewable changes (1)
  • crates/engine/src/game/coverage.rs
🚧 Files skipped from review as they are similar to previous changes (3)
  • crates/engine/src/types/ability.rs
  • crates/engine/src/parser/oracle_effect/mod.rs
  • crates/engine/src/game/effects/exile_face_down_pile.rs

Comment on lines 6116 to +6117
| Effect::ExileTop { .. }
| Effect::ExileFaceDownPile { .. }

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 | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
rg -n -i -C 3 'dig|exil(e|ed).*face.?down|face.?down.*exil' docs/MagicCompRules.txt

Repository: phase-rs/phase

Length of output: 216


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf 'Repo root: '; pwd
echo
fd -a 'MagicCompRules.txt' .
echo
fd -a 'sequence.rs' crates/engine/src/parser/oracle_effect
echo
ast-grep outline crates/engine/src/parser/oracle_effect/sequence.rs --view expanded | sed -n '1,220p'

Repository: phase-rs/phase

Length of output: 15854


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Show the relevant area around the changed arm.
sed -n '6070,6145p' crates/engine/src/parser/oracle_effect/sequence.rs

echo
# Find the effect definition and any existing comments around it.
rg -n -C 3 'ExileFaceDownPile|clause_is_dig_lookback_transparent|Dig' crates/engine/src/parser/oracle_effect/sequence.rs crates/engine/src -g '!**/target/**'

Repository: phase-rs/phase

Length of output: 50371


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Locate the repo's comprehensive rules text and the relevant parser arm.
fd -a 'MagicCompRules.txt' .
echo '---'
sed -n '6070,6145p' crates/engine/src/parser/oracle_effect/sequence.rs
echo '---'
rg -n -C 4 'Effect::ExileFaceDownPile|clause_is_dig_lookback_transparent|Dig' crates/engine/src/parser/oracle_effect/sequence.rs crates/engine/src -g '!**/target/**'

Repository: phase-rs/phase

Length of output: 50371


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo 'MagicCompRules candidates:'
fd -a 'MagicCompRules.txt' . || true
echo
echo 'Parser sequence context:'
sed -n '6098,6135p' crates/engine/src/parser/oracle_effect/sequence.rs
echo
echo 'Nearby definitions/usages:'
rg -n -C 3 'ExileFaceDownPile|clause_is_dig_lookback_transparent|Dig' crates/engine/src/parser/oracle_effect/sequence.rs crates/engine/src -g '!**/target/**' || true

Repository: phase-rs/phase

Length of output: 50371


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Look for how this repository annotates parser or effect-classification code.
rg -n -C 2 'CR [0-9]+|clause_is_.*transparent|lookback_transparent' crates/engine/src -g '!**/target/**' || true

Repository: phase-rs/phase

Length of output: 50371


🏁 Script executed:

#!/bin/bash
set -euo pipefail

sed -n '6000,6038p' crates/engine/src/parser/oracle_effect/sequence.rs
echo '---'
rg -n -C 4 'CR 406\.3|CR 701\.20e|ExileFaceDownPile|clause_is_dig_lookback_transparent' crates/engine/src/parser/oracle_effect/sequence.rs crates/engine/src/parser/oracle_effect/mod.rs -g '!**/target/**'
echo '---'
# Find the repository's Comprehensive Rules text file if present under a different path.
fd -a 'MagicCompRules.txt|CompRules|ComprehensiveRules|comprehensive rules' .

Repository: phase-rs/phase

Length of output: 39246


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Show the exact classification function with line numbers.
cat -n crates/engine/src/parser/oracle_effect/sequence.rs | sed -n '6000,6038p'

echo '---'
# Narrow search for the nearby CR-backed rationale and the new arm.
rg -n -C 3 'CR 406\.3|CR 701\.20e|ExileFaceDownPile|clause_is_dig_lookback_transparent' \
  crates/engine/src/parser/oracle_effect/sequence.rs \
  crates/engine/src/parser/oracle_effect/mod.rs \
  -g '!**/target/**' || true

echo '---'
# Locate the comprehensive rules text file.
fd -a 'MagicCompRules.txt' . || true
fd -a 'CompRules*' . || true
fd -a 'Comprehensive*' . || true

Repository: phase-rs/phase

Length of output: 32243


🏁 Script executed:

#!/bin/bash
set -euo pipefail

cat -n crates/engine/src/parser/oracle_effect/sequence.rs | sed -n '6000,6038p'
echo '---'
rg -n -C 3 'CR 406\.3|CR 701\.20e|ExileFaceDownPile|clause_is_dig_lookback_transparent' \
  crates/engine/src/parser/oracle_effect/sequence.rs \
  crates/engine/src/parser/oracle_effect/mod.rs \
  -g '!**/target/**' || true

Repository: phase-rs/phase

Length of output: 32239


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Show only the dig lookback transparency function.
sed -n '6008,6068p' crates/engine/src/parser/oracle_effect/sequence.rs | cat -n

echo '---'
# Show the call site that relies on this predicate.
sed -n '30308,30328p' crates/engine/src/parser/oracle_effect/mod.rs | cat -n

Repository: phase-rs/phase

Length of output: 4616


Annotate the ExileFaceDownPile branch with its CR basis. crates/engine/src/parser/oracle_effect/sequence.rs:6116-6117 treats this effect as dig-lookback-transparent; add the matching CR 406.3 + CR 608.2c + CR 701.24a explanation so the grouping stays intentional.

🤖 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/engine/src/parser/oracle_effect/sequence.rs` around lines 6116 - 6117,
Annotate the Effect::ExileFaceDownPile branch in the effect grouping with the CR
406.3 + CR 608.2c + CR 701.24a basis, preserving its existing
dig-lookback-transparent behavior and keeping the adjacent Effect::ExileTop
branch unchanged.

Source: Path instructions

@github-actions

Copy link
Copy Markdown

Parse changes introduced by this PR

✓ No card-parse changes detected.

@matthewevans
matthewevans added this pull request to the merge queue Jul 25, 2026
Merged via the queue into main with commit 28920af Jul 25, 2026
18 checks passed
@matthewevans
matthewevans deleted the ship/fix-hang-crash-reports branch July 25, 2026 20:17
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