fix(parser): parse "play up to <n> additional lands this turn" (Summer Bloom #5979) - #6206
Conversation
There was a problem hiding this comment.
Code Review
This pull request updates the parse_additional_land_head parser in crates/engine/src/parser/clause_shell.rs to support parsing the phrase "play up to additional lands" by making the "up to " segment optional using the opt combinator. It also adds a comment referencing CR 305.2 to document this rule. There are no review comments, and I have no feedback to provide.
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.
Parse changes introduced by this PR · 2 card(s), 3 signature(s) (baseline: main
|
matthewevans
left a comment
There was a problem hiding this comment.
Blocked: the regression does not cover the second affected parser context.
🔴 Blocker
[MED] The new regression only parses Summer Bloom's standalone sentence. Evidence: crates/engine/src/parser/oracle_effect/tests.rs:22147; the fresh coverage artifact also identifies Journey of Discovery, whose Choose one modal bullet reaches this grammar through a distinct full-card parsing context. Why it matters: both cards changed from CastFromZone to AdditionalLandDrop, so a standalone sentence assertion cannot keep the modal-card path from regressing. Suggested fix: add a registered regression that parses Journey of Discovery's full Oracle text and asserts its second mode produces AdditionalLandDrop { count: 2 } through the production card parser.
✅ Clean
- The optional
up tosegment is modeled with the existingnom::combinator::optparser at both grammar seams; no new engine surface or one-off string dispatch was introduced. - Required CI is green and the current coverage artifact supports the intended two-card grammar class.
…overy) Addresses review feedback on phase-rs#6206: the "up to <n> additional lands" grammar is also reached as a modal mode bullet, not only as a standalone sentence. Journey of Discovery ("Choose one — ... • You may play up to two additional lands this turn. Entwine {2}{G}") exercises that distinct full-card parsing context. Adds a registered regression through the production card parser asserting its second mode produces AdditionalLandDrop { count: 2 } (Entwine registers as a keyword, not a third mode). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
Thanks @matthewevans — addressed in 176194f. Added a registered regression through the production card parser (
This exercises the distinct modal-bullet parsing context alongside the existing standalone-sentence regression, so neither the Summer Bloom path nor the Journey of Discovery path can silently regress.
|
matthewevans
left a comment
There was a problem hiding this comment.
Blocked: the modal parser regression fixes the requested context gap, but the supported card path still lacks a runtime assertion.
🔴 Blocker
[MED] journey_of_discovery_modal_up_to_two_additional_lands stops after inspecting the parsed StaticMode at crates/engine/src/parser/oracle_tests.rs:3777-3812. It never casts the spell or observes the transient land-drop effect that the production resolver must register. Why it matters: this parser change changes coverage from CastFromZone to supported AdditionalLandDrop; an AST shape assertion cannot prove the parser-produced GenericEffect reaches additional_land_drops through apply()/stack resolution. Suggested fix: add a registered integration regression using GameScenario + GameRunner::cast(...).resolve() with Summer Bloom's exact Oracle text and assert additional_land_drops(outcome.state(), P0) == 3; follow crates/engine/tests/integration/explore_spell_3315.rs:38-68 for the existing runtime pattern.
✅ Clean
- The new Journey of Discovery full-card regression covers the previously missing modal context and would fail on the pre-fix parser.
- The only new commit since the prior head changes that regression file; the existing two-card parse-diff remains explained by the already-reviewed grammar change.
Recommendation: add the single runtime regression, then request re-review. The contributor test gate is documented in docs/AI-CONTRIBUTOR.md.
…nt (phase-rs#5979) Addresses second-round review feedback on phase-rs#6206: an AST-shape assertion cannot prove the parser-produced GenericEffect reaches additional_land_drops through cast + stack resolution. Adds a registered integration regression (tests/integration/summer_bloom_5979.rs) that casts Summer Bloom's exact Oracle text via GameScenario + GameRunner::cast(...).resolve() and asserts additional_land_drops(state, P0) == 3, following the explore_spell_3315.rs runtime pattern. Registered in tests/integration/main.rs. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
Thanks @matthewevans — runtime assertion added in bffd99d. New registered integration regression let spell = scenario
.add_spell_to_hand_from_oracle(P0, "Summer Bloom", false,
"You may play up to three additional lands this turn.")
.with_mana_cost(ManaCost::zero())
.id();
let outcome = scenario.build().cast(spell).resolve();
assert_eq!(additional_land_drops(outcome.state(), P0), 3);Follows the Coverage now: parser structure (Summer Bloom standalone + Journey of Discovery modal context) and runtime resolution. Note on local verification: my sandbox has no C linker + no root, so I bootstrapped a |
|
Local verification update: I got a memory-fitting harness working (standalone binary linked against the prebuilt So the |
…r Bloom phase-rs#5979) The turn-scoped additional-land grant parser recognized "play an additional land" and "play <n> additional lands" but not the "up to" hedge form ("You may play up to three additional lands this turn." — Summer Bloom). Because `parse_additional_land_head` did not match the "up to" variant, `is_specialized_duration_carrier` returned false and the trailing " this turn" duration was stripped before `try_parse_additional_land_this_turn` ran, whose required ` this turn` tag then failed — so the clause never became an additional-land grant. CR 305.2: "up to <n>" is functionally identical to a bare "<n>" here — playing lands is already optional, so the "up to" hedge grants the same +n land-play allowance. Both parse sites now accept an optional `up to ` before the count, mapping to `AdditionalLandDrop { count: n }`. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…overy) Addresses review feedback on phase-rs#6206: the "up to <n> additional lands" grammar is also reached as a modal mode bullet, not only as a standalone sentence. Journey of Discovery ("Choose one — ... • You may play up to two additional lands this turn. Entwine {2}{G}") exercises that distinct full-card parsing context. Adds a registered regression through the production card parser asserting its second mode produces AdditionalLandDrop { count: 2 } (Entwine registers as a keyword, not a third mode). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…nt (phase-rs#5979) Addresses second-round review feedback on phase-rs#6206: an AST-shape assertion cannot prove the parser-produced GenericEffect reaches additional_land_drops through cast + stack resolution. Adds a registered integration regression (tests/integration/summer_bloom_5979.rs) that casts Summer Bloom's exact Oracle text via GameScenario + GameRunner::cast(...).resolve() and asserts additional_land_drops(state, P0) == 3, following the explore_spell_3315.rs runtime pattern. Registered in tests/integration/main.rs. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
bffd99d to
211ade1
Compare
matthewevans
left a comment
There was a problem hiding this comment.
Reviewed current head 211ade160ae54d37446cffdb403b59358a477175: the parser change uses the existing nom seams, the two-card parse delta is explained, and the registered Summer Bloom scenario proves the resolver grants three additional land drops at runtime.
Fixes #5979.
Summary
Summer Bloom (
{1}{G}sorcery — "You may play up to three additional lands this turn.") never granted its extra land plays. The turn-scoped additional-land grant parser recognizedplay an additional landandplay <n> additional landsbut not the "up to" hedge form.Root cause is a two-site interaction:
parse_additional_land_head(used byis_specialized_duration_carrier) did not matchplay up to <n> additional lands, so the shell treated the clause as a non-duration-carrier and stripped the trailingthis turninto the clause duration.try_parse_additional_land_this_turnthen required a literalthis turntag that was already gone, so the clause fell through instead of becoming anAdditionalLandDropgrant.Both parse sites now accept an optional
up tobefore the count.Rules
CR 305.2: a "play up to
<n>additional lands this turn" grant is functionally identical to a bare "<n>additional lands" grant — playing lands is already optional, so the "up to" hedge grants the same+nland-play allowance. It therefore maps toAdditionalLandDrop { count: n }, matching the existing count-nbranch.Class, not card
The fix is grammar-level: any "play up to
<n>additional lands this turn" card is now covered, not just Summer Bloom. Theopt(tag("up to "))composes into the existing countalt()— no new AST shape, no per-card branch.Anchored on
alt()count branch ((parse_number, tag(" additional lands"))) that I extended withopt(tag("up to ")).parse_additional_land_headalt()(("play ", parse_number, " additional lands")) extended the same way, keeping the duration-carrier detector in sync with the effect parser.Same nom combinator family (
alt/tag/opt/value), same naming, same modules.Test
Added
summer_bloom_up_to_three_additional_lands_parses_countinoracle_effect/tests.rs, mirroring the existingplay_two_additional_lands_this_turn_parses_count: asserts the clause parses to aGenericEffectcarryingAdditionalLandDrop { count: 3 }affecting the controller,UntilEndOfTurn, non-optional.Verification
alt/tag/opt/value/map).cc/ldabsent) and no root to install one, so a localcargo/clippy/testbuild is not possible here. CI will run the full suite. The change is a two-line grammar extension using combinators already in scope, and the added test is a near-exact copy of the existing, passingplay_two_additional_lands_this_turn_parses_count(same asserts, count 3 instead of 2,up toin the input).cargo fmt --allrun clean (no diff).Gate A
Model: claude-opus-4-8
Thinking: High
Tier: Frontier