Skip to content

Fix True-Name Nemesis chosen-player protection - #7213

Open
traemyn wants to merge 2 commits into
phase-rs:mainfrom
traemyn:fix/issue-5941-true-name-protection
Open

Fix True-Name Nemesis chosen-player protection#7213
traemyn wants to merge 2 commits into
phase-rs:mainfrom
traemyn:fix/issue-5941-true-name-protection

Conversation

@traemyn

@traemyn traemyn commented Aug 10, 2026

Copy link
Copy Markdown
Contributor

Summary

Closes #5941. Fixes the True-Name Nemesis protection misparse by representing “the chosen player” as a typed protection target and resolving it from the protected permanent’s persisted choice, so objects controlled by that player cannot target it.

Files changed

  • crates/engine/src/game/keywords.rs
  • crates/engine/src/game/static_abilities.rs
  • crates/engine/src/types/keywords.rs
  • crates/engine/tests/integration/main.rs
  • crates/engine/tests/integration/issue_5941_true_name_chosen_player_protection.rs

Track

Developer

LLM

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

Implementation method (required)

Method: /engine-implementer

CR references

  • CR 702.16
  • CR 109.4

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 test -p phase-engine parse_protection_target_chosen_player — passed: test types::keywords::tests::parse_protection_target_chosen_player ... ok

  • cargo test -p phase-engine --test integration issue_5941_true_name_chosen_player_protection — passed: test issue_5941_true_name_chosen_player_protection::true_name_protection_uses_the_protected_objects_chosen_player ... ok

  • cargo test -p phase-engine --lib --quiet — passed: 18833 passed; 0 failed; 6 ignored; finished in 26.65s

  • cargo clippy -p phase-engine --all-targets -- -D warnings — passed.

  • cargo fmt --all — passed.

  • git diff --check — passed.

  • Independent final review-impl — LGTM.

Gate A

Gate G PASS (router/grant architecture: strict router vs permissive grant boundary intact)
Gate A PASS head=b71eec325dc0c7a1a46dbcbd4cf9a1ba5be5973f base=97591656218103d8e8c7315725b24cfe64645dd4

Anchored on

  • crates/engine/src/game/keywords.rs:533 — existing chosen-color and chosen-card-type protection matching reads durable choices from the protected object.
  • crates/engine/src/types/keywords.rs:2877 — existing chosen-color and chosen-card-type parser arms use typed protection variants for runtime resolution.
  • crates/engine/tests/integration/serras_emissary_chosen_card_type_protection.rs — existing production-path choice-to-targeting regression structure.

Final review-impl

Final review-impl PASS head=b71eec325dc0c7a1a46dbcbd4cf9a1ba5be5973f

Claimed parse impact

True-Name Nemesis — expected parser classification changes from the stringly typed CardType("the chosen player") form to ProtectionTarget::ChosenPlayer when generated card data is regenerated. The generated card-data artifact was not available in this worktree, so no artifact parse-diff was produced.

Scope Expansion

None.

Validation Failures

  • cargo clippy --all-targets -- -D warnings — blocked before linting because openssl-sys v0.9.116 could not find an OpenSSL installation and requires OPENSSL_DIR.
  • cargo test -p phase-engine — started successfully with many passing tests but exceeded the 120-second safety timeout and was killed while loop_shortcut::an_evicting_beat_mints_without_growing_the_ring was still running.

CI Failures

None.

Summary by CodeRabbit

  • Bug Fixes

    • Fixed protection effects that target a chosen player.
    • Sources controlled by the chosen player can no longer interact with protected permanents.
    • Other players’ sources remain eligible to interact as expected.
  • Tests

    • Added coverage for chosen-player selection and protection behavior.

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

coderabbitai Bot commented Aug 10, 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: 240913be-19e2-414c-a1a0-f39b3556a2a0

📥 Commits

Reviewing files that changed from the base of the PR and between b71eec3 and 3c6294a.

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

📝 Walkthrough

Walkthrough

The engine parses ProtectionTarget::ChosenPlayer, stores the selected player, and blocks sources controlled by that player from targeting the protected permanent. Unit and integration tests cover parsing and target legality.

Changes

Chosen-player protection

Layer / File(s) Summary
Protection contract and parsing
crates/engine/src/types/keywords.rs
Adds ProtectionTarget::ChosenPlayer, parses both chosen-player text forms, and tests Keyword::from_str integration.
Protection matching and integration
crates/engine/src/game/keywords.rs, crates/engine/src/game/static_abilities.rs, crates/engine/tests/integration/*
Matches source controllers against the chosen player, excludes chosen-player protection from unsupported player-level checks, and verifies target filtering in the True-Name Nemesis scenario.

Estimated code review effort: 2 (Simple) | ~10 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 clearly describes the fix for True-Name Nemesis's chosen-player protection.
Linked Issues check ✅ Passed The changes implement chosen-player protection and add regression coverage for issue #5941.
Out of Scope Changes check ✅ Passed All changes support the linked issue through parser, runtime, and integration-test updates.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ 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: 2

🤖 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/keywords.rs`:
- Around line 545-549: Update ProtectionTarget::ChosenPlayer in
crates/engine/src/game/keywords.rs:545-549 to match the chosen player against
source.owner when source.controller is absent, while retaining controller
matching for controlled sources and using owner for nonbattlefield player-scoped
queries. Update the documentation in crates/engine/src/types/keywords.rs:505-507
to cite CR 702.16k and describe both controller and ownership protection
behavior.

In
`@crates/engine/tests/integration/issue_5941_true_name_chosen_player_protection.rs`:
- Around line 35-41: Update the test setup around the direct
WaitingFor::NamedChoice assignment to enter True-Name Nemesis through the
production enter-the-battlefield path instead of prepopulating post-dispatch
state. Assert that this path produces the expected WaitingFor::NamedChoice, then
submit GameAction::ChooseOption and retain the existing target-legality
assertions.
🪄 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: bae2b917-67e3-4060-b068-437d4d432a3e

📥 Commits

Reviewing files that changed from the base of the PR and between 1bb6c1d and b71eec3.

📒 Files selected for processing (5)
  • crates/engine/src/game/keywords.rs
  • crates/engine/src/game/static_abilities.rs
  • crates/engine/src/types/keywords.rs
  • crates/engine/tests/integration/issue_5941_true_name_chosen_player_protection.rs
  • crates/engine/tests/integration/main.rs

Comment thread crates/engine/src/game/keywords.rs Outdated
Comment on lines +545 to +549
// CR 702.16 + CR 109.4: the source is protected against objects
// controlled by the player chosen for the protected permanent.
ProtectionTarget::ChosenPlayer => protected
.chosen_player()
.is_some_and(|player| source.controller == player),

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

Implement the ownership leg of protection from the chosen player.

CR 702.16k covers objects controlled by the chosen player and objects owned by that player that another player does not control. The current comparison only checks source.controller, so nonbattlefield sources owned by the chosen player can bypass protection. CR 109.4 also states that objects outside the stack and battlefield have no controller. (media.wizards.com)

  • crates/engine/src/game/keywords.rs#L545-L549: Match the chosen player against the source owner when the source has no controller, while retaining controller matching for controlled sources.
  • crates/engine/src/types/keywords.rs#L505-L507: Replace the CR 109.4 citation with the applicable CR 702.16k behavior and document the ownership case.

As per path instructions, player-scoped queries on nonbattlefield zones must filter by obj.owner, not controller.

📍 Affects 2 files
  • crates/engine/src/game/keywords.rs#L545-L549 (this comment)
  • crates/engine/src/types/keywords.rs#L505-L507
🤖 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 545 - 549, Update
ProtectionTarget::ChosenPlayer in crates/engine/src/game/keywords.rs:545-549 to
match the chosen player against source.owner when source.controller is absent,
while retaining controller matching for controlled sources and using owner for
nonbattlefield player-scoped queries. Update the documentation in
crates/engine/src/types/keywords.rs:505-507 to cite CR 702.16k and describe both
controller and ownership protection behavior.

Sources: Path instructions, MCP tools

Comment thread crates/engine/tests/integration/issue_5941_true_name_chosen_player_protection.rs Outdated
@github-actions

github-actions Bot commented Aug 10, 2026

Copy link
Copy Markdown

Generated for head 3c6294a50bfdc307b981ba70ebce890c6a2d2191.

Parse changes introduced by this PR

✓ No card-parse changes detected.

@matthewevans matthewevans self-assigned this Aug 10, 2026
@matthewevans matthewevans added the bug Bug fix label Aug 10, 2026
@matthewevans

Copy link
Copy Markdown
Member

Held pending current-head CI. I pushed maintainer commit 3c6294a50bfdc307b981ba70ebce890c6a2d2191 to make the regression exercise the actual cast → as-enters → named-choice path. Please let the required checks and the head-bound parse-diff artifact complete; I will resume approval and enqueue once that evidence is green for this head.

@matthewevans matthewevans removed their assignment Aug 10, 2026
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.

true name nemesis — True name nemesis was able to be targeted by my opponent's Song of the Dryads even though i named p…

2 participants