Skip to content

fix(parser): support filtered card counts in the chosen player's zone (Haunting Apparition)#5426

Merged
matthewevans merged 1 commit into
phase-rs:mainfrom
galuis116:feat/next-parser-gap
Jul 9, 2026
Merged

fix(parser): support filtered card counts in the chosen player's zone (Haunting Apparition)#5426
matthewevans merged 1 commit into
phase-rs:mainfrom
galuis116:feat/next-parser-gap

Conversation

@galuis116

Copy link
Copy Markdown
Contributor

Closes #5425.

What

Makes Haunting Apparition work — "power is equal to 1 plus the number of green creature cards in the chosen player's graveyard." A card-count quantity that's filtered (by card type or color) and scoped to "the chosen player's" graveyard failed to parse. The unfiltered chosen-player form (Sewer Nemesis) and the filtered your/their/opponents' forms already worked — only the filter × chosen-player combination was missing.

How (routes to existing runtime, no engine changes)

Two count-parsing paths never learned the "the chosen player's <zone>" possessive they already recognize for your/their/opponents':

  1. parse_scoped_zone_ref (the ZoneCardCount scoped-zone path, type-filtered counts) gains a "the chosen player's <zone>" arm → CountScope::SourceChosenPlayer. This unifies the type-filtered and unfiltered cases, so the previously separate unfiltered-only parse_number_of_cards_in_chosen_player_zone special case is removed (single authority).
  2. parse_zone_qual (the general typed-phrase zone-suffix path, color/property filters that lower to ObjectCount) gains a ZoneQual::ChosenPlayer arm → ControllerRef::SourceChosenPlayer, mirroring how "your <zone>" sets ControllerRef::You. Ordered before the bare "the " article so it isn't mis-consumed.

Both resolve against the source's persisted chosen player (SourceChosenPlayer, CR 613.1) via the existing filter/count runtime.

Haunting Apparition now parses to SetDynamicPower(Offset { ObjectCount { Creature, controller: SourceChosenPlayer, [HasColor(Green), InZone(Graveyard)] }, offset: 1 }).

Tests

  • cda_type_filtered_cards_in_chosen_players_graveyardZoneCardCount { scope: SourceChosenPlayer, card_types: [Creature] }.
  • cda_color_filtered_cards_in_chosen_players_graveyardObjectCount with controller: SourceChosenPlayer + HasColor(Green) + InZone(Graveyard).
  • Verified no regressions across the goyf class (your / all / opponents' / their graveyards, unfiltered chosen-player).

Verification

cargo fmt --all ✓ · engine lib suite 15,871 pass / 0 fail ✓ · clippy -p engine --all-targets -D warnings clean ✓

…phase-rs#5425)

Haunting Apparition — "power is equal to 1 plus the number of green
creature cards in the chosen player's graveyard" — failed to parse. The
unfiltered form ("cards in the chosen player's graveyard", Sewer Nemesis)
and filtered forms scoped to your/their/opponents' graveyards already
worked; only a type/color filter combined with the chosen-player scope was
unsupported, because two count-parsing paths never learned the
"the chosen player's <zone>" possessive:

- `parse_scoped_zone_ref` (the `ZoneCardCount` scoped-zone path) gains a
  "the chosen player's <zone>" arm → `CountScope::SourceChosenPlayer`,
  alongside your/opponents'/all. This unifies the type-filtered and
  unfiltered cases, so the previously separate unfiltered-only
  `parse_number_of_cards_in_chosen_player_zone` special case is removed.
- `parse_zone_qual` (the general typed-phrase zone-suffix path, used for
  color/property filters that lower to `ObjectCount`) gains a
  `ZoneQual::ChosenPlayer` arm → `ControllerRef::SourceChosenPlayer`,
  mirroring how "your <zone>" sets `ControllerRef::You`. Ordered before the
  bare "the " article so it isn't mis-consumed.

Both resolve against the source's persisted chosen player, matching the
existing your/their/opponents' scopes. No runtime changes.

CR 613.1 + CR 404.2 + CR 108.3.
@galuis116 galuis116 requested a review from matthewevans as a code owner July 9, 2026 07:31

@gemini-code-assist gemini-code-assist 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.

Code Review

This pull request refactors the parsing of "the chosen player's " phrases by integrating them into the shared scoped-zone path and introducing the ZoneQual::ChosenPlayer qualifier. This change allows card-type and color filters to compose uniformly. Reviewer feedback correctly identifies that several code comments and test annotations incorrectly reference CR 613.1 (continuous effect layers) instead of CR 607.2d (linked abilities referring to a chosen player), violating the repository's strict rule on accurate CR annotations.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Comment on lines +2108 to +2112
// CR 613.1: "the chosen player's <zone>" — the player persisted on the
// source via an earlier "choose a player" (Haunting Apparition:
// "green creature cards in the chosen player's graveyard"). Placed on the
// shared scoped-zone path so card-type/color filters compose uniformly,
// rather than a separate unfiltered-only arm.

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.

medium

[MED] Incorrect CR annotation for chosen player. Evidence: crates/engine/src/parser/oracle_nom/quantity.rs:2108. Why it matters: CR 613.1 describes continuous effect layers, whereas CR 607.2d governs linked abilities referring to a chosen player. Suggested fix: Change CR 613.1 to CR 607.2d.

Suggested change
// CR 613.1: "the chosen player's <zone>" — the player persisted on the
// source via an earlier "choose a player" (Haunting Apparition:
// "green creature cards in the chosen player's graveyard"). Placed on the
// shared scoped-zone path so card-type/color filters compose uniformly,
// rather than a separate unfiltered-only arm.
// CR 607.2d: "the chosen player's <zone>" — the player persisted on the
// source via an earlier "choose a player" (Haunting Apparition:
// "green creature cards in the chosen player's graveyard"). Placed on the
// shared scoped-zone path so card-type/color filters compose uniformly,
// rather than a separate unfiltered-only arm.
References
  1. Every rules-touching line of engine code must carry a comment of the form CR : verified against docs/MagicCompRules.txt. (link)

Comment on lines +3337 to +3340
// CR 613.1: "creature cards in the chosen player's graveyard" — a type-
// filtered card count scoped to the source's persisted chosen player.
// Recognizing "the chosen player's <zone>" on the shared scoped-zone path
// lets the type filter compose, mirroring "your"/"opponents'" graveyards.

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.

medium

[MED] Incorrect CR annotation for chosen player. Evidence: crates/engine/src/parser/oracle_quantity.rs:3337. Why it matters: CR 613.1 describes continuous effect layers, whereas CR 607.2d governs linked abilities referring to a chosen player. Suggested fix: Change CR 613.1 to CR 607.2d.

Suggested change
// CR 613.1: "creature cards in the chosen player's graveyard" — a type-
// filtered card count scoped to the source's persisted chosen player.
// Recognizing "the chosen player's <zone>" on the shared scoped-zone path
// lets the type filter compose, mirroring "your"/"opponents'" graveyards.
// CR 607.2d: "creature cards in the chosen player's graveyard" — a type-
// filtered card count scoped to the source's persisted chosen player.
// Recognizing "the chosen player's <zone>" on the shared scoped-zone path
// lets the type filter compose, mirroring "your"/"opponents'" graveyards.
References
  1. Every rules-touching line of engine code must carry a comment of the form CR : verified against docs/MagicCompRules.txt. (link)

Comment on lines +3359 to +3363
// CR 613.1: Haunting Apparition — "green creature cards in the chosen
// player's graveyard". A color (property) filter routes through the
// general typed-phrase path as an `ObjectCount`; the "the chosen player's"
// zone qualifier must set `ControllerRef::SourceChosenPlayer`, mirroring
// how "your graveyard" sets `ControllerRef::You`.

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.

medium

[MED] Incorrect CR annotation for chosen player. Evidence: crates/engine/src/parser/oracle_quantity.rs:3359. Why it matters: CR 613.1 describes continuous effect layers, whereas CR 607.2d governs linked abilities referring to a chosen player. Suggested fix: Change CR 613.1 to CR 607.2d.

Suggested change
// CR 613.1: Haunting Apparition — "green creature cards in the chosen
// player's graveyard". A color (property) filter routes through the
// general typed-phrase path as an `ObjectCount`; the "the chosen player's"
// zone qualifier must set `ControllerRef::SourceChosenPlayer`, mirroring
// how "your graveyard" sets `ControllerRef::You`.
// CR 607.2d: Haunting Apparition — "green creature cards in the chosen
// player's graveyard". A color (property) filter routes through the
// general typed-phrase path as an ObjectCount; the "the chosen player's"
// zone qualifier must set ControllerRef::SourceChosenPlayer, mirroring
// how "your graveyard" sets ControllerRef::You.
References
  1. Every rules-touching line of engine code must carry a comment of the form CR : verified against docs/MagicCompRules.txt. (link)

Comment on lines +6892 to +6896
/// "the chosen player's " — the player persisted on the source via an earlier
/// "choose a player" (Haunting Apparition: "green creature cards in the chosen
/// player's graveyard"). Sets `ControllerRef::SourceChosenPlayer`, mirroring
/// how `You` sets `ControllerRef::You`; CR 613.1 resolves it against the
/// source's persisted choice.

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.

medium

[MED] Incorrect CR annotation for chosen player. Evidence: crates/engine/src/parser/oracle_target.rs:6892. Why it matters: CR 613.1 describes continuous effect layers, whereas CR 607.2d governs linked abilities referring to a chosen player. Suggested fix: Change CR 613.1 to CR 607.2d.

Suggested change
/// "the chosen player's " — the player persisted on the source via an earlier
/// "choose a player" (Haunting Apparition: "green creature cards in the chosen
/// player's graveyard"). Sets `ControllerRef::SourceChosenPlayer`, mirroring
/// how `You` sets `ControllerRef::You`; CR 613.1 resolves it against the
/// source's persisted choice.
/// "the chosen player's " — the player persisted on the source via an earlier
/// "choose a player" (Haunting Apparition: "green creature cards in the chosen
/// player's graveyard"). Sets ControllerRef::SourceChosenPlayer, mirroring
/// how You sets ControllerRef::You; CR 607.2d resolves it against the
/// source's persisted choice.
References
  1. Every rules-touching line of engine code must carry a comment of the form CR : verified against docs/MagicCompRules.txt. (link)

Comment on lines +7141 to +7142
// CR 613.1: must precede the `Plain` "the " arm so "the chosen player's "
// isn't consumed as a bare "the " article.

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.

medium

[MED] Incorrect CR annotation for chosen player. Evidence: crates/engine/src/parser/oracle_target.rs:7141. Why it matters: CR 613.1 describes continuous effect layers, whereas CR 607.2d governs linked abilities referring to a chosen player. Suggested fix: Change CR 613.1 to CR 607.2d.

Suggested change
// CR 613.1: must precede the `Plain` "the " arm so "the chosen player's "
// isn't consumed as a bare "the " article.
// CR 607.2d: must precede the Plain "the " arm so "the chosen player's "
// isn't consumed as a bare "the " article.
References
  1. Every rules-touching line of engine code must carry a comment of the form CR : verified against docs/MagicCompRules.txt. (link)

@github-actions

github-actions Bot commented Jul 9, 2026

Copy link
Copy Markdown

Parse changes introduced by this PR · 2 card(s), 3 signature(s) (baseline: main 4d5db0a673fa)

1 card(s) · static/Continuous · added: Continuous (CDA=yes, affects=self, mods=dynamic power)

Examples: Haunting Apparition

1 card(s) · static/Continuous · field mods: power -1, toughness -1add dynamic power, add dynamic toughness

Examples: Nyxathid

1 card(s) · ability/static_structure · removed: static_structure

Examples: Haunting Apparition

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

@matthewevans matthewevans self-assigned this Jul 9, 2026
@matthewevans matthewevans added bug Bug fix area:parser Oracle text parser labels Jul 9, 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.

Reviewed the parser change at 099a4a7e22ad33958ce213b03881da8c670b1265.

Evidence checked:

  • The diff is limited to parser quantity/target handling plus focused parser tests.
  • the chosen player's <zone> now composes through the existing scoped-zone building block instead of an unfiltered one-off arm.
  • The parse-diff sticky is narrow: Haunting Apparition plus the related Nyxathid dynamic-count shape.
  • GitHub CI is green, including Rust lint/tests, card-data, WASM, and frontend checks.

No blocking issues found.

@matthewevans matthewevans added this pull request to the merge queue Jul 9, 2026
@matthewevans matthewevans removed their assignment Jul 9, 2026
Merged via the queue into phase-rs:main with commit 895f450 Jul 9, 2026
13 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area:parser Oracle text parser bug Bug fix

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Parser: card counts filtered by type/color in "the chosen player's graveyard" are unsupported (Haunting Apparition)

2 participants