Skip to content

feat(parser): compound-quantified dual-subject become effect (CR 611.3)#5380

Merged
matthewevans merged 11 commits into
phase-rs:mainfrom
claytonlin1110:clayton/compound-all-subjects-become-effect
Jul 8, 2026
Merged

feat(parser): compound-quantified dual-subject become effect (CR 611.3)#5380
matthewevans merged 11 commits into
phase-rs:mainfrom
claytonlin1110:clayton/compound-all-subjects-become-effect

Conversation

@claytonlin1110

Copy link
Copy Markdown
Contributor

Summary

Mirrors the structural pattern from #5219 (compound "all <X> and all <Y>" subject distribution) on the effect layer for become-verb compounds:

  • Factors peel_compound_all_quantified_conjuncts into static_helpers.rs as the shared " and all " seam authority
  • Refactors parse_compound_all_subjects_filter / parse_compound_all_subjects_land_filter in type_change.rs to reuse the peeler
  • Adds try_parse_compound_all_subjects_become_clause dispatched before try_parse_subject_become_clause

Fixes Nightcreep misparsing: all creatures become black and all lands become Swamps now emits two static_abilities (Creature → SetColor(Black), Land → SetBasicLandType(Swamp)) instead of dropping the land conjunct into description.

Closes #5377

Test plan

  • cargo fmt --all — clean
  • cargo check -p engine — clean
  • cargo test -p engine --lib compound_all_subjects_become — blocked locally by Windows link.exe; CI should verify

claytonlin1110 and others added 7 commits July 8, 2026 08:26
…11 / CR 702.18 / CR 611.3a)

Generalize parse_compound_subject_keyword_static beyond Protection-only hard-coded subjects so You-and-X grants split into object Continuous + player Hexproof/Shroud/PlayerProtection defs — fixing Sigarda-class empty-typed Or misparses.

Closes phase-rs#5366

Co-authored-by: Cursor <cursoragent@cursor.com>
…2.11c / CR 702.18a)

Parser support for Sigarda-class compound hexproof was false-green: player halves emitted StaticMode::Hexproof/Shroud but find_legal_targets only checked player_protection_from. Add player_has_hexproof/shroud + player_cannot_be_targeted_by as the single player-target authority and wire every player-candidate path through it.

Co-authored-by: Cursor <cursoragent@cursor.com>
… (CR 702.11c / CR 102.3)

Player hexproof used ctrl != player_id, which blocked teammates in team formats. Route through players::is_opponent and add 2HG regressions for authority + find_legal_targets.

Co-authored-by: Cursor <cursoragent@cursor.com>
Mirror the phase-rs#5219 compound-subject structural pattern on the effect layer: factor the and-all conjunct peeler into static_helpers, refactor type_change compound filters to share it, and add try_parse_compound_all_subjects_become_clause so Nightcreep-class lines emit one static per conjunct instead of dropping the second predicate into description text.

Closes phase-rs#5377

Co-authored-by: Cursor <cursoragent@cursor.com>

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request implements robust support for player-scope hexproof and shroud (CR 702.11c, CR 702.18a), introducing a single-authority targeting check that correctly handles Two-Headed Giant teammates. It also refactors compound-subject keyword-grant parsing to decompose player-applicable keywords into separate object-half and player-half static definitions, and adds a shared helper to parse compound-quantified dual-subject become effects. Feedback on the changes highlights a high-severity bug in the parser where matching "become " using tag fails when the verb is "becomes ", which can be resolved by using alt to match both forms.

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.

Comment on lines +634 to +636
tag::<_, _, OracleError<'_>>("become ")
.parse(predicate_lower.as_str())
.ok()?;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

[HIGH] tag("become ") will fail to match when the verb is becomes because "becomes " does not start with "become ". Additionally, avoid swallowing parsing errors with .ok()? as it masks critical bugs; instead, propagate the parser error directly.

Evidence: crates/engine/src/parser/oracle_effect/subject.rs:634-636.

Why it matters: This breaks the parser's explicit support for the singular "becomes" verb form, causing any compound clause containing "becomes" to fail parsing. Swallowing errors with .ok()? makes debugging parser failures extremely difficult.

Suggested fix: Use nom::branch::alt to match either "become " or "becomes ", and propagate the error instead of converting it to an Option with .ok().

        nom::branch::alt::<_, _, _, OracleError<'_>>((tag("become "), tag("becomes ")))
            .parse(predicate_lower.as_str())?;
References
  1. Avoid swallowing all errors (e.g., using .ok()) when only specific errors are expected to be handled or ignored. Propagate unexpected errors (such as invariant violations or invalid actions) to prevent masking critical bugs.

@github-actions

github-actions Bot commented Jul 8, 2026

Copy link
Copy Markdown

Parse changes introduced by this PR

Baseline pending for 236a05639a6f3d7da2c2e0cdd4580f3a51eaf0d8 — this populates once main publishes its coverage snapshot (a few minutes after that commit landed).

@matthewevans matthewevans left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[MED] The new compound-become parser accepts becomes in the split but then rejects that same verb before building the clause. Evidence: crates/engine/src/parser/oracle_effect/subject.rs:626 accepts " becomes ", but crates/engine/src/parser/oracle_effect/subject.rs:634 immediately requires tag("become "); the added coverage at crates/engine/src/parser/oracle_effect/tests.rs:2236 only exercises plural "become". Why it matters: any singular/third-person conjunct that reaches this handler falls back instead of producing the intended per-conjunct static, so the new building block is narrower than its own grammar claims. Suggested fix: accept both become and becomes at the validation/build boundary, and add a test with a becomes conjunct.

… handler

The split path already peeled on becomes, but the validation gate only matched become. Accept both conjugations and add a mixed-conjunct regression test.

Co-authored-by: Cursor <cursoragent@cursor.com>

@matthewevans matthewevans left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[MED] Player hexproof uses the source object's stored controller instead of the targeting call's controller. Evidence: crates/engine/src/game/targeting.rs:16 carries source_controller, but the player-target branch calls player_cannot_be_targeted_by(state, player.id, source_id) without it at crates/engine/src/game/targeting.rs:204, and crates/engine/src/game/static_abilities.rs:1436 recomputes the controller from state.objects[source_id]; the repo already has an explicit override path at crates/engine/src/game/targeting.rs:57. Why it matters: when the ability/spell controller differs from the source object record, player hexproof can evaluate the wrong opponent relationship and allow an illegal player target. Suggested fix: pass the authoritative source_controller into player_cannot_be_targeted_by from all player-target enumeration sites and use that for the CR 702.11c opponent check.

…5.7)

build_become_clause was sending basic land type names to parse_animation_spec, producing AddSubtype(Swamps) instead of SetBasicLandType(Swamp). Intercept before animation spec so Nightcreep-class land conjuncts parse correctly.

Co-authored-by: Cursor <cursoragent@cursor.com>

@matthewevans matthewevans left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[MED] Player hexproof uses the source object's stored controller instead of the targeting call's controller. Evidence: crates/engine/src/game/targeting.rs:16 carries source_controller, but the player-target branch calls player_cannot_be_targeted_by(state, player.id, source_id) without it at crates/engine/src/game/targeting.rs:204, and crates/engine/src/game/static_abilities.rs:1436 recomputes the controller from state.objects[source_id]; the repo already has an explicit override path at crates/engine/src/game/targeting.rs:57. Why it matters: when the ability/spell controller differs from the source object record, player hexproof can evaluate the wrong opponent relationship and allow an illegal player target. Suggested fix: pass the authoritative source_controller into player_cannot_be_targeted_by from all player-target enumeration sites and use that for the CR 702.11c opponent check.

@matthewevans matthewevans self-assigned this Jul 8, 2026
# Conflicts:
#	crates/engine/src/game/static_abilities.rs
#	crates/engine/src/game/targeting.rs
@matthewevans matthewevans added the enhancement New feature or request label Jul 8, 2026

@matthewevans matthewevans left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maintainer sign-off: current head addresses the become/becomes and source-controller targeting blockers; parser capability is at the right seam with discriminating coverage.

@matthewevans matthewevans added this pull request to the merge queue Jul 8, 2026
Merged via the queue into phase-rs:main with commit c8a4c81 Jul 8, 2026
13 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat(parser): compound-quantified dual-subject become effect (Nightcreep class) (CR 611.3)

2 participants