Skip to content

Unparsed "one-time boon" wrapper yields a counter type that is an entire sentence #7495

Description

@JacobWoodson

Summary

You get a one-time boon with "<ability>" is not recognized anywhere in the parser, so the entire boon sentence — quote characters and all — is fed to the counter-clause grammar. On two cards the result is a CounterType::Generic whose name is a complete English sentence.

grep -rn "one-time boon\|boon" crates/engine/src/parser/ returns nothing; there is no boon handling at all.

Reproduction

Parse each card's full Oracle text through parse_oracle_text:

CARD March Toward Perfection: abilities=1 triggers=0 replacements=1
   repl[0] counter_type = "\"when you cast a phyrexian creature spell,
                            that creature enters with an additional +1/+1"

CARD Arcane Archery:         abilities=1 triggers=0 replacements=1   (same shape)

CARD Tenacious Pup:          abilities=0 triggers=1 replacements=0

Affected cards (the full printed class, from a corpus sweep over every enters/enter with … counter line):

  • March Toward PerfectionYou get a one-time boon with "When you cast a Phyrexian creature spell, that creature enters with an additional +1/+1 counter and deathtouch counter on it."
  • Arcane Archery… "When you cast a creature spell, that creature enters with an additional +1/+1 counter, reach counter, and trample counter on it."
  • Tenacious Pup… "When you cast a creature spell, that creature enters the battlefield with an additional +1/+1 counter, trample counter, and vigilance counter on it."

Mechanism

  1. The boon line's trigger prefix is inside a quoted grant, so has_trigger_prefix(&lower) is false at the head of the line. The Priority 5-pre interceptor (crates/engine/src/parser/oracle.rs:5226) never fires, and parse_whenever_you_cast_enters_with_trigger is never reached — hence triggers=0.
  2. The line falls through to the generic object-hosted replacement path, parse_enters_with_counters.
  3. strip_after(work_text, "with ") matches the boon's own boon with , not the counter clause's with . The counter grammar receives text beginning with a " character.
  4. The conjoined-list reader correctly rejects that, and the single-counter fallback's unbounded take_until(" counter") consumes everything up to the first counter as the counter type, producing the Generic above.

Impact

A counter type that is an English sentence is meaningless at runtime — it matches no counter the engine can place, remove, or query, and it is not the counter the card prints. This is worse than a dropped counter: the ability appears "supported" while placing a phantom counter type. It also means enters with counter riders on all three cards are entirely non-functional.

Note that Tenacious Pup fails differently (routed to a trigger, no replacement at all), so a fix needs to cover both shapes.

Suggested fix

Strip the You get a one-time boon with "<ability>" wrapper and parse the quoted text as a granted ability, the way other quoted-ability grants are handled — rather than widening the counter grammar, which is downstream of the real problem. Loch Larent prints the same wrapper (crates/engine/src/parser/oracle_tests.rs:10023) and is worth checking as part of the same class.

Not a regression

Pre-existing on main. Confirmed by the CI parse diff on #7490, which reports ✓ No card-parse changes detected — these cards parse identically with and without that PR's counter-grammar changes. Found while investigating #7490; that PR's elided-count grammar is correct but unreachable for these cards until this wrapper is handled.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions