Skip to content

Fix Throne of Eldraine#6625

Open
invalidCards wants to merge 5 commits into
phase-rs:mainfrom
invalidCards:card/throne-of-eldraine
Open

Fix Throne of Eldraine#6625
invalidCards wants to merge 5 commits into
phase-rs:mainfrom
invalidCards:card/throne-of-eldraine

Conversation

@invalidCards

@invalidCards invalidCards commented Jul 25, 2026

Copy link
Copy Markdown
Contributor

Summary

Fixes engine support for Throne of Eldraine's chosen-color mana restrictions.

Files changed

  • crates/engine/src/ai_support/candidates.rs
  • crates/engine/src/ai_support/mod.rs
  • crates/engine/src/game/ability_rw.rs
  • crates/engine/src/game/ability_scan.rs
  • crates/engine/src/game/casting.rs
  • crates/engine/src/game/casting_costs.rs
  • crates/engine/src/game/casting_tests.rs
  • crates/engine/src/game/cost_payability.rs
  • crates/engine/src/game/costs.rs
  • crates/engine/src/game/effects/mana.rs
  • crates/engine/src/game/engine.rs
  • crates/engine/src/game/engine_tests.rs
  • crates/engine/src/game/keywords.rs
  • crates/engine/src/game/mana_abilities.rs
  • crates/engine/src/game/mana_payment.rs
  • crates/engine/src/game/mana_sources.rs
  • crates/engine/src/game/planeswalker.rs
  • crates/engine/src/game/replacement.rs
  • crates/engine/src/parser/oracle.rs
  • crates/engine/src/parser/oracle_effect/mana.rs
  • crates/engine/src/parser/oracle_effect/sequence.rs
  • crates/engine/src/parser/oracle_ir/ast.rs
  • crates/engine/src/parser/oracle_tests.rs
  • crates/engine/src/types/ability.rs
  • crates/engine/src/types/mana.rs
  • crates/engine/tests/integration/throne_of_eldraine_mana_riders.rs

CR references

  • CR 105.2a
  • CR 105.4
  • CR 106.6 and CR 106.6a
  • CR 400.7
  • CR 601.2f-h
  • CR 602.2b
  • CR 609.4b

Implementation method (required)

Method: /engine-implementer

Track

Developer

LLM

Model: gpt-5.6
Thinking: high

Tier: Frontier

Verification

  • Required checks ran clean, or exact unresolved local failures are 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 — PASS

  • git diff --check — PASS

  • CARGO_TARGET_DIR=/tmp/phase-throne-verify cargo test -p engine --test integration throne_of_eldraine_mana_riders — PASS (3 tests)

  • ./scripts/check-parser-combinators.sh — PASS

  • cargo clippy-strict, cargo test -p engine, ./scripts/gen-card-data.sh, cargo coverage, and cargo semantic-audit — not completed after two local attempts because the shared Cargo build directory remained locked; CI owns the remaining checks.

Gate A

Gate G PASS (router/grant architecture: strict router vs permissive grant boundary intact)
Gate A PASS head=3d772f6a671b78ee09de68c67bb9f021ca210aa7 base=6895ca39a2b1017ceec96c25fb8141d0ee6847a7

Anchored on

  • crates/engine/src/parser/oracle_effect/mana.rs:1644 — existing nom tag() dispatch for spend-restriction clauses.
  • crates/engine/src/types/mana.rs:995 — existing typed ManaRestriction spell-payment authority.

Final review-impl

Final review-impl PASS head=3d772f6a671b78ee09de68c67bb9f021ca210aa7

Claimed parse impact

  • Throne of Eldraine

Validation Failures

None.

CI Failures

Local Cargo commands that require the shared build directory were blocked by its persistent lock after two attempts. The focused integration regression suite passed in an isolated target directory; CI must run the remaining full suite and card-data audits.

Summary by CodeRabbit

  • New Features

    • Added support for abilities that restrict activation payments to mana of a source’s chosen color.
    • Added support for spells that require mana of a chosen color and are monocolored.
    • Improved parsing and enforcement of multiple mana-spend restrictions.
  • Bug Fixes

    • Corrected mana eligibility and automatic tapping for restricted activation costs.
    • Prevented invalid or unresolved restricted mana from being used.
    • Improved ability-specific cost validation across activation and loyalty payments.
  • Tests

    • Added end-to-end coverage for chosen-color mana production, casting, activation payments, and automatic mana selection.

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

coderabbitai Bot commented Jul 25, 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: 240b6d2c-b7a3-4a6d-94c2-fd24686bb773

📥 Commits

Reviewing files that changed from the base of the PR and between be8aab1 and 9bc52dc.

📒 Files selected for processing (37)
  • crates/engine/src/ai_support/candidates.rs
  • crates/engine/src/ai_support/mod.rs
  • crates/engine/src/game/ability_rw.rs
  • crates/engine/src/game/ability_scan.rs
  • crates/engine/src/game/casting.rs
  • crates/engine/src/game/casting_costs.rs
  • crates/engine/src/game/casting_tests.rs
  • crates/engine/src/game/cost_payability.rs
  • crates/engine/src/game/costs.rs
  • crates/engine/src/game/effects/collect_evidence.rs
  • crates/engine/src/game/effects/mana.rs
  • crates/engine/src/game/engine.rs
  • crates/engine/src/game/engine_tests.rs
  • crates/engine/src/game/mana_abilities.rs
  • crates/engine/src/game/mana_payment.rs
  • crates/engine/src/game/mana_sources.rs
  • crates/engine/src/game/planeswalker.rs
  • crates/engine/src/game/replacement.rs
  • crates/engine/src/game/visibility.rs
  • crates/engine/src/parser/oracle.rs
  • crates/engine/src/parser/oracle_effect/mana.rs
  • crates/engine/src/parser/oracle_effect/sequence.rs
  • crates/engine/src/parser/oracle_ir/ast.rs
  • crates/engine/src/parser/oracle_tests.rs
  • crates/engine/src/types/ability.rs
  • crates/engine/src/types/game_state.rs
  • crates/engine/src/types/mana.rs
  • crates/engine/tests/integration/companion_special_action.rs
  • crates/engine/tests/integration/issue_2862_teferi_loyalty.rs
  • crates/engine/tests/integration/issue_4220_agatha_soul_cauldron.rs
  • crates/engine/tests/integration/main.rs
  • crates/engine/tests/integration/restricted_mana_face_down_and_face_up.rs
  • crates/engine/tests/integration/restricted_mana_mv_or_x.rs
  • crates/engine/tests/integration/restricted_mana_x_cost_only.rs
  • crates/engine/tests/integration/throne_of_eldraine_mana_riders.rs
  • crates/phase-ai/src/policies/land_animation.rs
  • crates/server-core/src/game_action_payload_guard.rs
🚧 Files skipped from review as they are similar to previous changes (30)
  • crates/engine/tests/integration/restricted_mana_x_cost_only.rs
  • crates/engine/src/parser/oracle_ir/ast.rs
  • crates/engine/tests/integration/main.rs
  • crates/server-core/src/game_action_payload_guard.rs
  • crates/engine/tests/integration/companion_special_action.rs
  • crates/engine/tests/integration/issue_4220_agatha_soul_cauldron.rs
  • crates/engine/tests/integration/issue_2862_teferi_loyalty.rs
  • crates/engine/src/game/engine_tests.rs
  • crates/engine/src/game/ability_rw.rs
  • crates/engine/src/game/planeswalker.rs
  • crates/engine/tests/integration/restricted_mana_mv_or_x.rs
  • crates/engine/src/ai_support/mod.rs
  • crates/engine/tests/integration/restricted_mana_face_down_and_face_up.rs
  • crates/engine/src/game/replacement.rs
  • crates/phase-ai/src/policies/land_animation.rs
  • crates/engine/tests/integration/throne_of_eldraine_mana_riders.rs
  • crates/engine/src/game/effects/mana.rs
  • crates/engine/src/game/mana_sources.rs
  • crates/engine/src/game/ability_scan.rs
  • crates/engine/src/parser/oracle_tests.rs
  • crates/engine/src/game/cost_payability.rs
  • crates/engine/src/game/engine.rs
  • crates/engine/src/game/casting_tests.rs
  • crates/engine/src/parser/oracle.rs
  • crates/engine/src/game/casting.rs
  • crates/engine/src/types/ability.rs
  • crates/engine/src/parser/oracle_effect/sequence.rs
  • crates/engine/src/game/casting_costs.rs
  • crates/engine/src/parser/oracle_effect/mana.rs
  • crates/engine/src/types/mana.rs

📝 Walkthrough

Walkthrough

This PR adds chosen-color mana riders, carries activation ability indexes through payment evaluation, extends mana restriction parsing to multiple restrictions, and updates mana eligibility, auto-tap, replacement payment, AI, and integration tests.

Changes

Chosen-color restriction parsing and contracts

Layer / File(s) Summary
Restriction contracts and Oracle parsing
crates/engine/src/types/*, crates/engine/src/parser/*, crates/engine/src/game/ability_*.rs
Adds chosen-color restriction types, parses activation riders, supports multiple parsed mana restrictions, and wires serialization and AST handling.

Activation payment context and mana enforcement

Layer / File(s) Summary
Activation payment context and mana enforcement
crates/engine/src/game/casting*.rs, crates/engine/src/game/mana_*.rs, crates/engine/src/game/effects/mana.rs, crates/engine/src/game/engine.rs
Derives activation payment context from ability indexes, filters actual mana types, applies fail-closed restrictions, and updates auto-tap and payment flows.

Ability-index payment plumbing

Layer / File(s) Summary
Ability-index payment plumbing
crates/engine/src/game/mana_abilities.rs, crates/engine/src/game/cost*.rs, crates/phase-ai/..., crates/engine/src/game/replacement.rs
Threads optional ability indexes through mana-ability costs, activation payment, AI affordability, loyalty activation, and replacement may-cost handling.

Validation and integration

Layer / File(s) Summary
Regression and integration validation
crates/engine/**/*tests*.rs, crates/engine/tests/integration/*, crates/server-core/...
Updates payment-context fixtures and adds coverage for Throne of Eldraine chosen-color production, casting, manual payment, and auto-tap behavior.

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

Possibly related PRs

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 is concise and clearly matches the main change: fixing Throne of Eldraine 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 unit tests (beta)
  • Create PR with unit tests

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 ast-grep (0.44.1)
crates/engine/src/game/casting_tests.rs

ast-grep timed out on this file


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

@github-actions

github-actions Bot commented Jul 25, 2026

Copy link
Copy Markdown

Parse changes introduced by this PR · 1 card(s), 2 signature(s) (baseline: main 9e6cc761460f)

🔴 Removed (1 signature)

  • 1 card · ➖ ability/spend · removed: spend
    • Affected (first 3): Throne of Eldraine

🟡 Modified fields (1 signature)

  • 1 card · 🔄 ability/Mana · changed field restrictions: [SpellWithColorCount { comparator: EQ, count: 1 }, SpellOfSourceChosenColor]
    • Affected (first 3): Throne of Eldraine

1 card(s) had Oracle-text changes (errata/reprint) — excluded as non-parser.

@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 (3)
crates/engine/src/ai_support/candidates.rs (1)

2118-2141: 📐 Maintainability & Code Quality | 🟠 Major | 🏗️ Heavy lift

Silent fallback masks a should-never-happen state instead of failing loudly.

pending_cast.activation_ability_index.unwrap_or(usize::MAX) converts a missing index (which, for ActivationCostOneOfChoice, should be structurally impossible) into "no color restriction" rather than an assertion/error. If activation_ability_index is ever unexpectedly None here, a chosen-color-restricted activation would silently be treated as unrestricted instead of surfacing the bug. See the consolidated comment for the cross-file fix.

🤖 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/ai_support/candidates.rs` around lines 2118 - 2141, Replace
the silent unwrap_or fallback in the WaitingFor::ActivationCostOneOfChoice
candidate generation with an assertion or equivalent fail-fast handling that
requires pending_cast.activation_ability_index to be present. Pass the validated
index to casting::can_pay_ability_cost_now so a missing activation ability index
cannot be treated as unrestricted.
crates/engine/src/ai_support/mod.rs (1)

272-287: 📐 Maintainability & Code Quality | 🟠 Major | 🏗️ Heavy lift

Mirrors the candidates.rs ActivationCostOneOfChoice sentinel — same root cause.

Same .unwrap_or(usize::MAX) pattern gating the same WaitingFor::ActivationCostOneOfChoice legality check as ai_support/candidates.rs. See the consolidated comment.

🤖 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/ai_support/mod.rs` around lines 272 - 287, The
WaitingFor::ActivationCostOneOfChoice legality check incorrectly uses usize::MAX
as the fallback activation ability index. Update the
pending_cast.activation_ability_index handling in this match arm to use the same
valid fallback and behavior established by candidates.rs, while preserving the
existing cost lookup and can_pay_ability_cost_now validation.
crates/engine/src/game/keywords.rs (1)

776-786: 📐 Maintainability & Code Quality | 🟠 Major | 🏗️ Heavy lift

usize::MAX used as an undocumented "no ability index" sentinel.

Ninjutsu-family activation has no indexed ability, so usize::MAX stands in for "not applicable." This works today only because the lookup this feeds into (activation_ability_definition) gracefully falls back to Unrestricted on a miss — but nothing here documents that usize::MAX is a deliberate sentinel rather than a real index. See the consolidated comment for the cross-file recommendation (Option<usize> propagation).

🤖 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/keywords.rs` around lines 776 - 786, Update
pay_ability_cost_for_activation and its activation_ability_definition lookup
path to accept Option<usize> for the ability index, passing None from the
ninjutsu-family activation call instead of usize::MAX. Preserve the existing
Unrestricted fallback behavior when no indexed ability applies, and update all
affected callers and signatures consistently.
🤖 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/costs.rs`:
- Around line 201-208: The activation ability index contract uses a magic
usize::MAX sentinel instead of representing the absent case explicitly. In
crates/engine/src/game/costs.rs:201-208, change
PaymentScope::Activation.ability_index to Option<usize>; in
crates/engine/src/game/costs.rs:570-617, update pay_ability_cost_for_activation
and pay_ability_cost_for_activation_with_cost_move_replacement to accept and
propagate Option<usize>. Update all callers to pass None for unindexed
Ninjutsu-family activations and the actual Option value (or an explicit
invariant assertion) for stamped activation choices, including the affected
paths in game/keywords.rs:776-786, ai_support/candidates.rs:2118-2141 and
4033-4047, and ai_support/mod.rs:272-287.

In `@crates/engine/tests/integration/throne_of_eldraine_mana_riders.rs`:
- Around line 197-210: Update the manual-pin/resume test to reach the
mana-payment state through the production GameAction::ActivateAbility path in
manual payment mode, rather than constructing PendingCast and
activation_ability_index directly. Verify the resulting
pending_cast.activation_ability_index is Some(draw) before exercising pin
rejection, while preserving the existing tapped-state and failure-path
assertions.

---

Outside diff comments:
In `@crates/engine/src/ai_support/candidates.rs`:
- Around line 2118-2141: Replace the silent unwrap_or fallback in the
WaitingFor::ActivationCostOneOfChoice candidate generation with an assertion or
equivalent fail-fast handling that requires
pending_cast.activation_ability_index to be present. Pass the validated index to
casting::can_pay_ability_cost_now so a missing activation ability index cannot
be treated as unrestricted.

In `@crates/engine/src/ai_support/mod.rs`:
- Around line 272-287: The WaitingFor::ActivationCostOneOfChoice legality check
incorrectly uses usize::MAX as the fallback activation ability index. Update the
pending_cast.activation_ability_index handling in this match arm to use the same
valid fallback and behavior established by candidates.rs, while preserving the
existing cost lookup and can_pay_ability_cost_now validation.

In `@crates/engine/src/game/keywords.rs`:
- Around line 776-786: Update pay_ability_cost_for_activation and its
activation_ability_definition lookup path to accept Option<usize> for the
ability index, passing None from the ninjutsu-family activation call instead of
usize::MAX. Preserve the existing Unrestricted fallback behavior when no indexed
ability applies, and update all affected callers and signatures consistently.
🪄 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: 74ea3e82-3984-4e71-a310-cf1dbd2948ca

📥 Commits

Reviewing files that changed from the base of the PR and between 712fe27 and 3d772f6.

📒 Files selected for processing (35)
  • crates/engine/src/ai_support/candidates.rs
  • crates/engine/src/ai_support/mod.rs
  • crates/engine/src/game/ability_rw.rs
  • crates/engine/src/game/ability_scan.rs
  • crates/engine/src/game/casting.rs
  • crates/engine/src/game/casting_costs.rs
  • crates/engine/src/game/casting_tests.rs
  • crates/engine/src/game/cost_payability.rs
  • crates/engine/src/game/costs.rs
  • crates/engine/src/game/effects/mana.rs
  • crates/engine/src/game/engine.rs
  • crates/engine/src/game/engine_tests.rs
  • crates/engine/src/game/keywords.rs
  • crates/engine/src/game/mana_abilities.rs
  • crates/engine/src/game/mana_payment.rs
  • crates/engine/src/game/mana_sources.rs
  • crates/engine/src/game/planeswalker.rs
  • crates/engine/src/game/replacement.rs
  • crates/engine/src/parser/oracle.rs
  • crates/engine/src/parser/oracle_effect/mana.rs
  • crates/engine/src/parser/oracle_effect/sequence.rs
  • crates/engine/src/parser/oracle_ir/ast.rs
  • crates/engine/src/parser/oracle_tests.rs
  • crates/engine/src/types/ability.rs
  • crates/engine/src/types/mana.rs
  • crates/engine/tests/integration/companion_special_action.rs
  • crates/engine/tests/integration/issue_2862_teferi_loyalty.rs
  • crates/engine/tests/integration/issue_4220_agatha_soul_cauldron.rs
  • crates/engine/tests/integration/main.rs
  • crates/engine/tests/integration/restricted_mana_face_down_and_face_up.rs
  • crates/engine/tests/integration/restricted_mana_mv_or_x.rs
  • crates/engine/tests/integration/restricted_mana_x_cost_only.rs
  • crates/engine/tests/integration/throne_of_eldraine_mana_riders.rs
  • crates/phase-ai/src/policies/land_animation.rs
  • crates/server-core/src/game_action_payload_guard.rs

Comment thread crates/engine/src/game/costs.rs
Comment on lines +197 to +210
let mut pending = PendingCast::new(
throne,
CardId(0xED),
ResolvedAbility::new((*draw_ability.effect).clone(), Vec::new(), throne, P0),
ManaCost::generic(3),
);
pending.activation_ability_index = Some(draw);
state.pending_cast = Some(Box::new(pending));
state.objects.get_mut(&throne).unwrap().tapped = true;
state.waiting_for = WaitingFor::ManaPayment {
player: P0,
convoke_mode: None,
};
assert!(matches!(state.waiting_for, WaitingFor::ManaPayment { .. }));

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.

📐 Maintainability & Code Quality | 🟠 Major | 🏗️ Heavy lift

The manual-pin section hand-seeds the exact state the fix is supposed to produce.

PendingCast::new(...) + pending.activation_ability_index = Some(draw) + state.waiting_for = ManaPayment + tapped = true construct post-activation state directly. handle_spend_pool_mana derives its PaymentContext::Activation from pending.activation_ability_index, so if the production activation path ever stopped populating that field (or populated a wrong index), this assertion pair at Lines 212-220 would still pass — the pin rejection is proven only against a fixture-supplied index, not against what ActivateAbility actually writes.

The auto path above covers production wiring, so the rider isn't unguarded overall; the gap is specifically the manual pin/resume branch. Reaching WaitingFor::ManaPayment for an activation through a real GameAction::ActivateAbility (manual payment mode) would close it. Failing that, at minimum assert the production-populated value first — activate through the runner, then read back state.pending_cast.activation_ability_index == Some(draw) before pinning.

As per path instructions, "A test must exercise the FAILURE path the fix prevents and drive the engine through its production pipeline" and "Flag constructor shortcuts ... builders that bypass production wiring, setUp that pre-populates post-fix state) that can silently mask the very bug a regression test claims to catch."

🤖 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/tests/integration/throne_of_eldraine_mana_riders.rs` around
lines 197 - 210, Update the manual-pin/resume test to reach the mana-payment
state through the production GameAction::ActivateAbility path in manual payment
mode, rather than constructing PendingCast and activation_ability_index
directly. Verify the resulting pending_cast.activation_ability_index is
Some(draw) before exercising pin rejection, while preserving the existing
tapped-state and failure-path assertions.

Source: Path instructions

@matthewevans matthewevans added the enhancement New feature or request label Jul 25, 2026
@matthewevans matthewevans removed the needs-maintainer AI-contribution PR requires human triage (Non-dev track or unresolved gaps) label Jul 25, 2026
@github-actions github-actions Bot added the needs-maintainer AI-contribution PR requires human triage (Non-dev track or unresolved gaps) label Jul 25, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request 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.

2 participants