feat(parser): reach graveyard cards with the chosen-creature-type static (Ashes of the Fallen)#5864
feat(parser): reach graveyard cards with the chosen-creature-type static (Ashes of the Fallen)#5864minion1227 wants to merge 1 commit into
Conversation
…tic (Ashes of the Fallen)
"Each creature card in your graveyard has the chosen creature type in
addition to its other types." (Ashes of the Fallen) strict-failed — the
line produced zero statics, so the card did nothing after its ETB choice.
This is the ZONE-axis sibling of Arcane Adaptation: the same creature
subject and the same additive AddChosenSubtype{CreatureType} grant, one
axis different — the cards live in the controller's graveyard rather than
on the battlefield. So it extends the existing scope axis
(ChosenCreatureTypeStaticScope) that parse_arcane_adaptation_chosen_type_
static already dispatches on, and the new scope's target_filter carries the
restriction with the identical FilterProp::InAnyZone fold that
parse_hand_cards_have_derived_cost_keyword applies for its hand-scoped
grant. The subject arm reads "has" (a single card) rather than "are", and
binds "its" as the retention pronoun.
No new engine primitive: AddChosenSubtype, InAnyZone and the additive/SET
application split are all pre-existing and already runtime-wired. The
battlefield forms are untouched — regression-tested that Arcane Adaptation
gains no zone restriction.
CR 400.1 (zones) + CR 205.1b (in-addition retention) + CR 607.2d
(chosen-value link) + CR 613.1d (Layer 4).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Code Review
This pull request introduces support for graveyard-scoped creature card type-change effects (such as Ashes of the Fallen) by adding the GraveyardCreatureCards variant to ChosenCreatureTypeStaticScope and implementing its target filtering, parsing, and application logic. The review feedback correctly identifies that the comments citing CR 400.1 should instead cite CR 109.2a to accurately reflect the distinction between cards in non-battlefield zones and permanents on the battlefield, in accordance with Rule R6.
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.
| /// CR 400.1 + CR 607.2d: the grant reaches creature CARDS in the controller's | ||
| /// graveyard rather than permanents on the battlefield (Ashes of the Fallen). | ||
| GraveyardCreatureCards, |
There was a problem hiding this comment.
[MED] Incorrect CR citation for zone-based card vs permanent distinction. Evidence:
crates/engine/src/parser/oracle_static/type_change.rs:70.
Why it matters: CR 400.1 merely defines what an object is, whereas CR 109.2a specifically explains why an object with a card type in a non-battlefield zone is a card rather than a permanent.
Suggested fix: ChangeCR 400.1toCR 109.2a.
| /// CR 400.1 + CR 607.2d: the grant reaches creature CARDS in the controller's | |
| /// graveyard rather than permanents on the battlefield (Ashes of the Fallen). | |
| GraveyardCreatureCards, | |
| /// CR 109.2a + CR 607.2d: the grant reaches creature CARDS in the controller's | |
| /// graveyard rather than permanents on the battlefield (Ashes of the Fallen). | |
| GraveyardCreatureCards, |
References
- Rule R6: Every rules-touching line of engine code must carry a comment of the form CR : . The number must be verified against docs/MagicCompRules.txt before writing. (link)
| // CR 400.1 + CR 607.2d: Ashes of the Fallen grants the chosen type to | ||
| // creature CARDS in the controller's graveyard. Same creature subject, | ||
| // one axis different — the zone — so the filter carries the restriction | ||
| // via the identical `InAnyZone` fold the hand-scoped derived-cost grant | ||
| // (`parse_hand_cards_have_derived_cost_keyword`) already applies. | ||
| Self::GraveyardCreatureCards => TargetFilter::Typed( |
There was a problem hiding this comment.
[MED] Incorrect CR citation for zone-based card vs permanent distinction. Evidence:
crates/engine/src/parser/oracle_static/type_change.rs:93.
Why it matters: CR 400.1 merely defines what an object is, whereas CR 109.2a specifically explains why an object with a card type in a non-battlefield zone is a card rather than a permanent.
Suggested fix: ChangeCR 400.1toCR 109.2a.
| // CR 400.1 + CR 607.2d: Ashes of the Fallen grants the chosen type to | |
| // creature CARDS in the controller's graveyard. Same creature subject, | |
| // one axis different — the zone — so the filter carries the restriction | |
| // via the identical `InAnyZone` fold the hand-scoped derived-cost grant | |
| // (`parse_hand_cards_have_derived_cost_keyword`) already applies. | |
| Self::GraveyardCreatureCards => TargetFilter::Typed( | |
| // CR 109.2a + CR 607.2d: Ashes of the Fallen grants the chosen type to | |
| // creature CARDS in the controller's graveyard. Same creature subject, | |
| // one axis different — the zone — so the filter carries the restriction | |
| // via the identical `InAnyZone` fold the hand-scoped derived-cost grant | |
| // (`parse_hand_cards_have_derived_cost_keyword`) already applies. | |
| Self::GraveyardCreatureCards => TargetFilter::Typed( |
References
- Rule R6: Every rules-touching line of engine code must carry a comment of the form CR : . The number must be verified against docs/MagicCompRules.txt before writing. (link)
Parse changes introduced by this PR · 1 card(s), 2 signature(s) (baseline: main
|
matthewevans
left a comment
There was a problem hiding this comment.
Request changes — correct both type-change CR annotations.
🟡 Finding
[MED] The new annotations cite unrelated CR 607.2d and CR 400.1. Evidence: crates/engine/src/parser/oracle_static/type_change.rs:70,93. Why it matters: the comments claim rule support they do not have. Suggested fix: use the verified relevant citations CR 109.2a + CR 613.1d / CR 205.1b with accurate descriptions.
Recommendation: request-changes.
Closes #5863
Summary
"Each creature card in your graveyard has the chosen creature type in addition to its other types." (Ashes of the Fallen) strict-failed — zero statics, so the card did nothing after its ETB choice. The battlefield sibling (Arcane Adaptation) parses fine, so this was purely a zone-scope gap.
parse_arcane_adaptation_chosen_type_staticdispatches onChosenCreatureTypeStaticScope, whose subject matcher had fixed arms only forcreatures you control are/each creature you control is/vehicle creatures you control are. The graveyard subject — which also reads "has" rather than "are" — had no arm, so the line fell through unparsed.Files changed
crates/engine/src/parser/oracle_static/type_change.rscrates/engine/src/parser/oracle_static/tests.rsCR references
Implementation method (required)
Method: not-applicable — authored directly against the
oracle-parserskill. Pure parser change; no card-data regeneration required (AST-shape fix).Track
Developer
LLM
Model: claude-opus-4-8
Thinking: high
Verification
a08d0c992).Results on head
a08d0c992:cargo test -p engine --lib parser::— ok. 8188 passed; 0 failed (2 new; all snapshot tests unaffected).cargo clippy -p engine --all-targets --features proptest -- -D warnings— Finished, 0 warnings (exit 0).cargo fmt --all— clean.Parse diff (Ashes of the Fallen)
Before:
After:
Anchored on
crates/engine/src/parser/oracle_static/type_change.rs(parse_arcane_adaptation_chosen_type_static+ChosenCreatureTypeStaticScope) — the existing scope axis this extends. Same creature subject, same additiveAddChosenSubtype{CreatureType}, same additive/SET application split; only the zone differs, so it becomes a fourth scope arm rather than a parallel handler.crates/engine/src/parser/oracle_static/shared.rs(parse_hand_cards_have_derived_cost_keyword) — the established zone-fold precedent: a card-in-a-zone static carries its scope asFilterProp::InAnyZone { zones: [Hand] }on the same typed filter. This reuses that fold verbatim with[Graveyard].Final review
Self-review against the review-impl lenses. No new engine primitive:
AddChosenSubtype,FilterProp::InAnyZone, and the additive/SET split are pre-existing and already runtime-wired, so this is a parser-recognition change only. The new arm matches the full literal subject+verb ("each creature card in your graveyard has"), so it cannot claim any other line, and it binds"its"as the retention pronoun to match the card's "in addition to its other types" (the pronoun feeds the shared additive-suffix check). The graveyard scope is grouped with the other creature-card subjects in the SET/replace arm for semantic correctness; no SET printing exists today (Ashes is additive), so that arm is unreachable in practice and documented as such. Battlefield forms are byte-for-byte unchanged — regression-tested that Arcane Adaptation gains no zone restriction (arcane_adaptation_stays_unzoned_after_graveyard_arm), plus 8188 parser tests incl. snapshots unaffected.Claimed parse impact
The CI
coverage-parse-diffsticky enumerates the full affected set for the current head.Validation Failures
None.
CI Failures
None.