Skip to content

fix(parser): recognize "transformed" as a filter quality (Mutagen Connoisseur)#5440

Merged
matthewevans merged 2 commits into
phase-rs:mainfrom
galuis116:feat/transformed-filter-quality
Jul 9, 2026
Merged

fix(parser): recognize "transformed" as a filter quality (Mutagen Connoisseur)#5440
matthewevans merged 2 commits into
phase-rs:mainfrom
galuis116:feat/transformed-filter-quality

Conversation

@galuis116

Copy link
Copy Markdown
Contributor

Closes #5439.

The bug

Mutagen Connoisseur"This creature gets +1/+0 for each transformed permanent you control." — parsed to a flat AddPower{1}/AddToughness{0}. The "for each transformed permanent you control" scaling was silently dropped, so the card always gets +1/+0 instead of +N/+0. The parser's own swallow-audit flags a DynamicQty SwallowedClause on the line.

Root cause

Isolation shows only the transformed quality is affected — tapped, face-down, artifact, creature all scale correctly:

Phrase Scales today
for each tapped permanent you control
for each artifact you control
for each transformed permanent you control ❌ (flat)

FilterProp has boolean battlefield-state qualities (Tapped, FaceDown, Suspected, IsSaddled, …) recognized as type-phrase adjective prefixes, but no Transformed variant — even though GameObject::transformed already exists.

Fix

Adds FilterProp::Transformed, the symmetric sibling of FilterProp::FaceDown — a per-object battlefield state read from GameObject::transformed:

  • recognized as an adjective prefix (transformed permanent/transformed creature) via parse_property_filter + the parse_combat_status_prefix whitelist;
  • matched at runtime in filter.rs (FilterProp::Transformed => obj.transformed);
  • threaded through the axis-classification lists / coverage display alongside FaceDown.

No runtime state change — the object flag was already tracked; this only lets the parser build the count/selector that reads it. Consistent with the existing individual-variant design for boolean state qualities (Suspected, Renowned, IsSaddled were all added the same way), so no parameterization refactor is implied.

Tests

  • test_parse_property_filter_transformed"transformed permanent"FilterProp::Transformed.
  • transformed_property_matches_only_transformed — the filter matches a transformed permanent and rejects a normal one.

Verification

cargo fmt --all ✓ · engine lib suite 15,904 pass / 0 fail + 2 new ✓ · clippy -p engine --all-targets -D warnings clean ✓

CR 711.4.

@galuis116
galuis116 requested a review from matthewevans as a code owner July 9, 2026 13:13

@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 the Transformed property filter across the engine and parser to support transformed permanents (with their back face up). The changes include updating FilterProp matching, adding parser support for the "transformed" adjective, and introducing corresponding unit tests. The review feedback correctly identifies multiple outdated Comprehensive Rules (CR) annotations referencing CR 711.4 instead of CR 701.28g, which violates the repository's strict rule-fidelity guidelines. These references should be updated to ensure accurate documentation.

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 thread crates/engine/src/game/filter.rs Outdated
Comment thread crates/engine/src/game/filter.rs Outdated
Comment thread crates/engine/src/parser/oracle_nom/filter.rs Outdated
Comment thread crates/engine/src/parser/oracle_nom/filter.rs Outdated
Comment thread crates/engine/src/parser/oracle_target.rs Outdated
@matthewevans matthewevans self-assigned this Jul 9, 2026
@github-actions

github-actions Bot commented Jul 9, 2026

Copy link
Copy Markdown

Parse changes introduced by this PR · 4 card(s), 5 signature(s) (baseline: main 500e30b15742)

1 card(s) · static/Continuous · added: Continuous (affects=transformed another you control permanent, mods=grant Trample, grant Ward)

Examples: Gargantuan Slabhorn

1 card(s) · static/Continuous · field conditional: unrecognizedtransformed in battlefield you control permanent is present

Examples: Oculus Whelp

1 card(s) · static/Continuous · field mods: power +1, toughness +0add dynamic power

Examples: Mutagen Connoisseur

1 card(s) · ability/TargetOnly · field target: anytransformed token you control Phyrexian

Examples: Incubation Triformer

1 card(s) · ability/static_structure · removed: static_structure

Examples: Gargantuan Slabhorn

@matthewevans matthewevans removed their assignment Jul 9, 2026
Mutagen Connoisseur — "This creature gets +1/+0 for each transformed
permanent you control." — parsed to a flat +1/+0: the "for each transformed
permanent you control" scaling was silently dropped (the swallow-audit flags
a DynamicQty SwallowedClause), because "transformed" was not recognized as a
filter quality. Every other qualifier — tapped, face-down, artifact,
creature — scales correctly; only "transformed" was missing.

Adds `FilterProp::Transformed`, the symmetric sibling of `FilterProp::FaceDown`:
a per-object battlefield state read from `GameObject::transformed` (which
already existed). It is recognized as a type-phrase adjective prefix
("transformed permanent"/"transformed creature") via `parse_property_filter`
and the `parse_combat_status_prefix` whitelist, and matched at runtime in
`filter.rs`. No runtime state change — the object flag was already tracked.

CR 701.27g.
@galuis116
galuis116 force-pushed the feat/transformed-filter-quality branch from 9b8c61d to 5d6eb90 Compare July 9, 2026 13:42
@galuis116

Copy link
Copy Markdown
Contributor Author

Good catch on the CR reference — fixed. CR 711 is indeed Leveler Cards, so CR 711.4 was wrong.

One correction to the suggestion: the bot proposed CR 701.28g, but CR 701.28 is Convert, not Transform. The definition of a "transformed permanent" is CR 701.27g:

701.27g Some spells and abilities refer to a "transformed permanent." This phrase refers to a double-faced permanent on the battlefield with its back face up. …

I've updated all six citations (code + tests + commit message) to CR 701.27g, which also matches the existing convention in the repo — FilterProp::SourceIsTransformed already cites CR 701.27g (types/ability.rs). Verified against docs/MagicCompRules.txt.

@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.

Implementation review remains clean at ac6cd6427541e0e65d3d1365d9534a81809f70e6. The change is at the shared FilterProp/typed-filter parser seam, uses the existing nom property combinator path, carries the verified CR 701.27g citation, and the refreshed parse-diff shows only the expected transformed-permanent class expansion.

@matthewevans
matthewevans added this pull request to the merge queue Jul 9, 2026
@matthewevans matthewevans removed their assignment Jul 9, 2026
Merged via the queue into phase-rs:main with commit e8244c7 Jul 9, 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.

Parser: "transformed" filter quality is dropped in count/type phrases — Mutagen Connoisseur gets flat +1/+0

2 participants