Skip to content

fix(parser): "target <filter>'s controller/owner <verb>s it" declares its object target#4701

Open
minion1227 wants to merge 1 commit into
phase-rs:mainfrom
minion1227:minion_4678
Open

fix(parser): "target <filter>'s controller/owner <verb>s it" declares its object target#4701
minion1227 wants to merge 1 commit into
phase-rs:mainfrom
minion1227:minion_4678

Conversation

@minion1227

Copy link
Copy Markdown
Contributor

Closes #4678.

Bug: "{T}: Target artifact creature's controller sacrifices it. …" (Arcum Dagsson) compiled to Effect::Sacrifice { target: ParentTarget } with no selectable object-target slotbuild_target_slots produced zero slots, so the ability activated without targeting or sacrificing anything. Root cause: parse_subject_application's "target " branch parsed the Typed([Artifact,Creature]) filter but discarded the "'s controller" remainder, and the imperative "sacrifices it" lowered to a dangling ParentTarget anaphor. The whole "target 's controller/owner s it" class was broken (Mercy Killing, etc.).

Fix (two scoped changes, no new variant):

  • parse_subject_application (subject.rs): when the "target " subject's remainder is exactly "'s controller"/"'s owner" (nom all_consuming(alt(...))), record the possessive shift as affected = ParentTargetController/Owner while keeping <filter> in target. Anaphoric "its controller"/"that creature's controller" subjects leave target = None, so the (affected, target) pair uniquely identifies the new shift.
  • lower_subject_predicate_ast imperative arm (mod.rs): for that shift, wrap the imperative effect in Effect::TargetOnly { target: <filter> } — the established pattern already used for player-target ChangeZone / Explore — so build_target_slots requires the object target and the effect's ParentTarget ("it") plus a following ParentTargetController ("that player may …") resolve against it.

CR 608.2c + CR 109.4 + CR 115.1.

Runtime test (build_target_slots): Arcum now surfaces exactly one required target slot; the opponent's artifact creature is legal, a plain creature is not. cargo fmt + clippy + the parser combinator gate are clean; 2308 oracle_effect + 125 ability_utils + 78 targeting + 697 casting tests green.

🤖 Generated with Claude Code

… its object target

"{T}: Target artifact creature's controller sacrifices it. …" (Arcum Dagsson,
phase-rs#4678) compiled to `Effect::Sacrifice { target: ParentTarget }` with NO
selectable object-target slot: `parse_subject_application`'s "target " branch
parsed the `Typed([Artifact,Creature])` filter but discarded the "'s controller"
remainder, and the imperative "sacrifices it" lowered to a `ParentTarget` anaphor
disconnected from the subject — dangling for a top-level ability. `build_target_
slots` produced zero slots, so the ability activated without targeting or
sacrificing anything. The whole "target <filter>'s controller/owner <verb>s it"
class was broken (Mercy Killing, Basalt Golem's spell-form siblings, etc.).

Two scoped changes:
- `parse_subject_application` (oracle_effect/subject.rs): when the "target "
  subject's remainder is exactly "'s controller"/"'s owner" (nom
  `all_consuming(alt(...))`), record the possessive shift as
  `affected = ParentTargetController/Owner` while keeping `<filter>` in
  `target`. The anaphoric "its controller"/"that creature's controller" subjects
  leave `target = None`, so the (affected, target) pair uniquely identifies the
  new shift.
- `lower_subject_predicate_ast` imperative arm (oracle_effect/mod.rs): for that
  shift, wrap the imperative effect in `Effect::TargetOnly { target: <filter> }`
  (the established pattern already used for player-target ChangeZone / Explore).
  This declares the object target so `build_target_slots` requires it and the
  effect's `ParentTarget` ("it") plus a following `ParentTargetController`
  ("that player may …") resolve against it. No new engine variant.

CR 608.2c (anaphora) + CR 109.4 (controller/owner) + CR 115.1 (targets).

Runtime test (build_target_slots): Arcum now surfaces exactly one required
target slot; the opponent's artifact creature is legal, a plain creature is not.
fmt + clippy + parser combinator gate clean; 2308 oracle_effect + 125
ability_utils + 78 targeting + 697 casting tests green.

Closes phase-rs#4678

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@minion1227 minion1227 requested a review from matthewevans as a code owner July 1, 2026 12:07

@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 introduces support for parsing and resolving abilities where the target's controller or owner is the active subject of an effect (e.g., "Target 's controller/owner s it"), such as Arcum Dagsson. It updates the subject parser using nom combinators to recognize possessive suffixes and map them to the appropriate target filters, and adds a unit test to verify correct target slot behavior. I have no feedback to provide as the implementation is idiomatic and conforms to the repository's style guide.

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.

@github-actions

github-actions Bot commented Jul 1, 2026

Copy link
Copy Markdown

Parse changes introduced by this PR · 7 card(s), 11 signature(s) (baseline: main d937775eaae1)

2 card(s) · ability/Sacrifice · removed: Sacrifice (target=parent target)

Examples: Mercy Killing, Reweave

2 card(s) · ability/TargetOnly · added: TargetOnly (target=creature)

Examples: Friendly Fire, Mercy Killing

2 card(s) · ability/TargetOnly · added: TargetOnly (target=spell on stack)

Examples: Denied!, Ertai's Meddling

1 card(s) · ability/ChangeZone · removed: ChangeZone (target=parent target, to=exile)

Examples: Ertai's Meddling

1 card(s) · ability/RevealHand · removed: RevealHand (card filter=none, player=creature)

Examples: Friendly Fire

1 card(s) · ability/RevealHand · removed: RevealHand (card filter=none, player=spell on stack)

Examples: Denied!

1 card(s) · ability/Sacrifice · removed: Sacrifice (kind=activated, target=parent target)

Examples: Arcum Dagsson

1 card(s) · ability/Sacrifice · removed: Sacrifice (kind=activated, target=parent target, timing=sorcery speed)

Examples: Sarkhan the Mad

1 card(s) · ability/TargetOnly · added: TargetOnly (kind=activated, target=artifact creature)

Examples: Arcum Dagsson

1 card(s) · ability/TargetOnly · added: TargetOnly (kind=activated, target=creature, timing=sorcery speed)

Examples: Sarkhan the Mad

1 card(s) · ability/TargetOnly · added: TargetOnly (target=permanent)

Examples: Reweave

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

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.

[Card Bug] "Arcum Dagsson" doesn't require sacrifice in order to fetch another artifact

1 participant