Fix commander mana value parsing for multi-zone patterns#3370
Fix commander mana value parsing for multi-zone patterns#3370Kelvinchen03 wants to merge 10 commits into
Conversation
There was a problem hiding this comment.
Code Review
This pull request implements parsing for the commander mana value pattern (e.g., for Stinging Study) by adding parse_commander_mana_value_ref and associated tests. Feedback highlights that the implementation violates several style guide rules: it lacks a mandatory CR annotation, fails to cover alternative phrase variants like 'converted mana cost' and 'they own', and uses fragile verbatim string matching instead of modular nom combinators.
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.
matthewevans
left a comment
There was a problem hiding this comment.
Thanks for tightening the parser around this commander-mana-value phrase. I can't approve this head yet because the implementation conflates two different Oracle patterns:
- Stinging Study says "the mana value of a commander you own on the battlefield or in the command zone." That is an indefinite "a commander" choice made while resolving the effect (CR 608.2d), not necessarily the greatest qualifying commander. The local card-data model also reflects that as choose-a-commander first, then use that chosen commander's mana value. Mapping this phrase to
AggregateFunction::Maxmeans a player with partner commanders of MV 2 and 6 would always draw/lose 6, even if the effect should let them choose the commander. - The Background flashback-style wording that says "the greatest mana value of a commander..." can use the aggregate-max shape, but it should be parsed as that specific wording, not shared with the non-
greatestwording.
There are two parser-shape issues to fix at the same time:
- The new branch still consumes
mana value of a commander you ownandon the battlefield or in the command zoneas full phrase tags. Please decompose the axes with nom combinators: optionalgreatest,mana value/converted mana costif in scope, owner phrase (you own/ per-playerthey ownfor cards like the discard/draw effect), and the battlefield/command-zone disjunction. - Add the CR annotation after verifying it against
docs/MagicCompRules.txt: CR 903.3d for commander references by zone and CR 202.3 for mana value. CR 608.2d is the relevant rule for the non-greatestStinging Study choice.
The current integration test only proves the card can be constructed; it would pass even if the runtime amount is wrong. Please add discriminating runtime coverage for the real Stinging Study path with multiple owned commanders of different mana values, and separate parser coverage for the greatest aggregate wording.
matthewevans
left a comment
There was a problem hiding this comment.
Thanks for the update. I re-reviewed head 8f746b1cbff1245f9242bff9552a316e3155b323. The parser now distinguishes the greatest wording from the non-greatest wording, but this still cannot merge as a fix for #3316.
[HIGH] ChosenObject is marked supported without any runtime prompt/storage path, so Stinging Study still resolves to 0 unless some unrelated code pre-populates the source. Evidence: crates/engine/src/game/quantity.rs:2116-2143 reads ChosenAttribute::Object from the source and falls back to ObjectId(0)/0 when absent; crates/engine/tests/integration/issue_3316_stinging_study.rs:6-14 explicitly says the actual prompt/storage is follow-up work; crates/engine/src/game/coverage.rs:5665-5668 marks ChosenObject as Handled anyway. Why it matters: coverage/swallow now report the card as supported while the real Stinging Study path still draws/loses 0 instead of asking the player which commander to use. Suggested fix: either implement the end-to-end choice path in this PR (effect-chain prompt + ChosenAttribute::Object storage + discriminating runtime test with two commanders of different mana values) or keep the non-greatest phrase unsupported/diagnosed until that runtime architecture lands.
[MED] The per-player ownership phrase is still scoped incorrectly. Evidence: crates/engine/src/parser/oracle_nom/quantity.rs:635-637 maps "they own " to ControllerRef::Opponent; in an each-player/per-player quantity context, “they” should bind to the iterated/scoped player, not every opponent of the source controller. Why it matters: sibling wording like “each player … a commander they own …” will select the wrong commander set in multiplayer. Suggested fix: parse this axis to ControllerRef::ScopedPlayer (or the existing scoped-player ownership representation) and add parser coverage that asserts the emitted filter is scoped-player-owned, not opponent-owned.
The current parse-only integration tests don’t discriminate the bug: they pass even though the comment says runtime is not implemented. The previous requested runtime test is still needed.
matthewevans
left a comment
There was a problem hiding this comment.
Thanks for the update. I re-reviewed head a6df010decf2b33077561514eaf2437c34f99335. This fixes the previous coverage-honesty part by marking QuantityRef::ChosenObject as Unhandled, and the they own parser branch now maps to ControllerRef::ScopedPlayer, but I still can't approve this head.
[HIGH] The new runtime test is a no-op while the PR still adds an unfinished object-choice runtime API. Evidence: crates/engine/tests/integration/issue_3316_stinging_study.rs defines stinging_study_runtime_without_choice_path_resolves_to_zero, but it contains only comments and no setup/assertions; rg "ChoiceType::Object|ChosenAttribute::Object|ChosenObject" crates/engine/src crates/engine/tests shows the new object-choice path is only represented by the new enum/value plumbing, compute_options returning an empty list, and QuantityRef::ChosenObject reading ChosenAttribute::Object if something else pre-populated it. There is still no effect-chain prompt, no filter-carrying object choice waiting state, and no storage path from a player action to ChosenAttribute::Object.
Why it matters: this lands a public-looking runtime shape that cannot actually be driven, plus a passing test whose name says it verifies the runtime bug but verifies nothing. That makes future reviews and coverage harder, not safer. Please either implement the full object-choice path with a discriminating runtime test (two owned commanders with different mana values, choose one, assert draw/life equals the chosen mana value), or keep this PR parser/coverage-only: remove the no-op runtime test and avoid adding runtime choice stubs that are not wired.
The ChosenObject coverage status being Unhandled is the right direction for not claiming Stinging Study is supported yet. The remaining blocker is making the code and tests accurately reflect that boundary.
Summary
Fix parser support for "the mana value of a commander you own on the battlefield or in the command zone" pattern, which was causing Stinging Study and similar cards to treat X as 0.
Implementation method (required)
/engine-implementerpipeline (plan → review-plan → implement → review-impl → commit)/engine-implementer— explain why belowThis is a focused parser fix adding a single new pattern-matching function. The change is too small to warrant the full
/engine-implementerpipeline - it follows the existing pattern for similar aggregate quantity parsers (e.g., parse_linked_exile_mana_value_ref) and doesn't involve new engine mechanics, rules behavior, or complex resolver logic. The fix simply adds the missing parser pattern to recognize text that the engine already knows how to handle at runtime.CR references
None - this is a parser fix, not a rules change. The CR rules for mana value and commander zones are already correctly implemented in the engine; this PR only adds the missing parser pattern to recognize the Oracle text.
Verification
cargo test -p engine --test integration issue_3316_stinging_study- passed