feat(parser): recover trailing subtype/quoted-ability after a for-each dynamic pump#5621
Conversation
…h dynamic pump
CR 205.1b + CR 604.1: A dynamic "gets +N/+M for each X" pump can carry a trailing
conjunct, but the for-each branch of `parse_continuous_gets_has` only recovered a
trailing keyword clause — a trailing type-addition or quoted-ability grant was
dropped:
- Reaper's Scythe — "Equipped creature gets +1/+1 for each soul counter on this
Equipment and is an Assassin in addition to its other types." dropped the
Assassin subtype.
- Rakdos Riteknife — "Equipped creature gets +1/+0 for each blood counter on this
Equipment and has \"{T}, Sacrifice a creature: ...\"" dropped the granted ability.
Fix: after the dynamic pump and trailing-keyword recovery, also run the existing
`parse_additive_type_clause_modifications` (the same additive-type authority used
elsewhere) and `parse_quoted_ability_modifications` over the description. Both
scanners no-op when their pattern is absent, so keyword-only for-each pumps (Claws
of Valakut, Ethereal Armor, Nyxborn Hydra, Thran Power Suit) are unchanged, and
cards whose dynamic pump falls back to a fixed pump elsewhere are untouched.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
Warning You have reached your daily quota limit. Please wait up to 24 hours and I will start processing your requests again! |
Parse changes introduced by this PR · 2 card(s), 2 signature(s) (baseline: main
|
matthewevans
left a comment
There was a problem hiding this comment.
Approving. This is exactly the shape a parser fix should have.
The claim matches the measurement. You said two cards; the coverage-parse-diff sticky says two cards, and they are the two you named:
- Reaper's Scythe ·
mods:add dynamic power, add dynamic toughness→…, add subtype Assassin - Rakdos Riteknife ·
PutCounter (counter=1 blood, kind=activated, target=granting object)— added
Zero collateral on the general pool, against a baseline (1c3eee9dd9cb) that is one commit off current main. That is the whole point of the "both scanners no-op when their pattern is absent" argument, and the pool proves it rather than asserting it — the keyword-only for-each pumps (Claws of Valakut, Ethereal Armor, Nyxborn Hydra, Thran Power Suit) are untouched, and so are the cards that fall back to the fixed-pump path.
CR verification (grep-verified against docs/MagicCompRules.txt):
CR 205.1b(line 1400) — "This rule applies to effects that use phrases such as 'in addition to its other types'." Precisely the Reaper's Scythe clause.CR 604.1(line 2663) — static abilities. Correct for the quoted grant.
Right seam. You extended the existing authorities (parse_additive_type_clause_modifications from #5548, parse_quoted_ability_modifications) rather than building a parallel recovery path in the for-each branch. Four lines, two existing building blocks, no new vocabulary. That's the composable-building-block rule working as intended.
The tests are discriminating, which is the part I care most about: both new tests assert the trailing modification is present, and on main it is dropped — so they fail if the fix is reverted. And dynamic_for_each_pump_trailing_keyword_unchanged guards the no-op claim from the other side, asserting the keyword-only path gains no spurious subtype or grant. A test that can only pass because the fix is there, plus a test that catches the fix over-reaching. That's the pair.
Nice work — this and #5612 are both clean.
CR 205.1b + CR 604.1
Bug
A dynamic "gets +N/+M for each X" pump can carry a trailing conjunct, but the
for eachbranch ofparse_continuous_gets_hasonly recovered a trailing keyword clause — a trailing type-addition or quoted-ability grant was dropped:AddSubtype(Assassin)(kept only the dynamic P/T).Fix
After the dynamic pump and the existing trailing-keyword recovery, also run the existing
parse_additive_type_clause_modifications(the same additive-type authority added in #5548) andparse_quoted_ability_modificationsover the description, extending the modification list. Both scanners no-op when their pattern is absent, so:Tests
dynamic_for_each_pump_recovers_trailing_subtype_addition— Reaper's Scythe →AddDynamicPower+AddSubtype("Assassin").dynamic_for_each_pump_recovers_trailing_quoted_ability— Rakdos Riteknife →AddDynamicPower+GrantAbility.dynamic_for_each_pump_trailing_keyword_unchanged— regression: keyword-only pump keepsAddDynamicPower+AddKeyword(FirstStrike)and gains no spurious subtype/grant.parser::oracle_static::tests(1053 passed, 0 failed).CR verification
docs/MagicCompRules.txt).AI-contributor notes
parse_additive_type_clause_modifications,parse_quoted_ability_modifications) rather than adding a parallel path; pattern-absent no-op keeps the blast radius to cards that actually carry the trailing conjunct.cargo fmt, CI-exactclippy -p engine --all-targets --features proptest -D warnings, and the full oracle_static module are green.🤖 Generated with Claude Code