Skip to content

fix(parser): bare 'it's a <type>' replaces card types per CR 205.1a (Arixmethes, #5213)#5231

Merged
matthewevans merged 1 commit into
phase-rs:mainfrom
minion1227:minion_arixmethes
Jul 7, 2026
Merged

fix(parser): bare 'it's a <type>' replaces card types per CR 205.1a (Arixmethes, #5213)#5231
matthewevans merged 1 commit into
phase-rs:mainfrom
minion1227:minion_arixmethes

Conversation

@minion1227

@minion1227 minion1227 commented Jul 7, 2026

Copy link
Copy Markdown
Contributor

Closes #5213

Summary

Fixes Arixmethes, Slumbering Isle (issue #5213): while it has a slumber counter it should become just a Land, but the engine showed the impossible "Legendary Creature Land — Kraken" — the Creature type was never removed.

Its static reads As long as Arixmethes has a slumber counter on it, it's a land. (It's not a creature.). The parser emitted an additive AddType{Land}, so the Kraken stayed a creature. Per CR 205.1a, an effect that sets a card type replaces the object's existing card types unless it retains them (CR 205.1b: "in addition to its other types" / "still a"). A bare "it's a land" therefore replaces.

Fix

parse_pronoun_becomes_type_static now runs its computed modifications through maybe_replace_card_types, which converts them to a single replacing SetCardTypes only when all of:

  • every modification is an AddType (a pure card-type change — no P/T, subtype, keyword, color, or supertype),
  • none of the added types is Creature (creature forms retain via the CR 205.1b "artifact creature" rule and their P/T),
  • the body carries no CR 205.1b retention marker (in addition to / still a).

So it's a landSetCardTypes{[Land]} (replaces, removing Creature), while creature animations (Gideon Blackblade, Midnight Mangler, Circle of the Moon Druid) and in addition to its other types forms stay additive. No new runtime: the existing SetCardTypes layer application (game/layers.rs) sets the core types and drops the correlated Kraken creature subtype, leaving a Legendary Land.

Files changed

  • crates/engine/src/parser/oracle_static/type_change.rs
  • crates/engine/src/parser/oracle_static/tests.rs
  • crates/engine/src/game/layers.rs

Anchored on

  • crates/engine/src/parser/oracle_static/type_change.rs parse_attached_isnt_and_is_type / the Imprisoned parse_enchanted_becomes_type_with_ability — existing handlers that already model type REPLACEMENT via SetCardTypes / RemoveType; this reuses SetCardTypes for the self "it's a [type]" form.
  • crates/engine/src/game/layers.rs:4886 ContinuousModification::SetCardTypes — the existing replacement runtime that sets core types and retains only correlated subtypes.

CR references

  • CR 205.1a — an effect that sets an object's card type replaces its existing card types; correlated subtypes of a removed card type are also removed.
  • CR 205.1b — retention only when the effect says "in addition to its other types", "still a [type]", or is an "artifact creature" form.

Track

Developer

LLM

Model: claude-opus-4-8
Thinking: high

Verification

  • cargo fmt --all — clean
  • ./scripts/check-parser-combinators.sh — clean (exit 0)
  • ./scripts/check-engine-authorities.sh — clean (exit 0)
  • cargo test -p engine --lib parser::oracle_static — 1014 pass / 0 fail
  • Parser test arixmethes_bare_is_a_land_replaces_card_types (asserts SetCardTypes{[Land]} + the slumber-counter condition, and that in addition to / artifact creature stay additive).
  • Runtime test set_card_types_land_strips_creature_and_creature_subtype (a Legendary Creature Kraken under SetCardTypes([Land]) becomes a Legendary Land: Creature removed, Kraken subtype dropped, Legendary retained).

Scope Expansion

None.

Validation Failures

None.

CI Failures

None.

🤖 Generated with Claude Code

…Arixmethes)

Closes phase-rs#5213.

A self type-change 'it's a <type>' with no CR 205.1b retention marker ('in
addition to its other types' / 'still a') sets the object's card type, which
REPLACES its existing card types (CR 205.1a). The parser emitted an additive
AddType, so Arixmethes, Slumbering Isle stayed a 'Creature Land' while it had a
slumber counter instead of becoming just a Land.

parse_pronoun_becomes_type_static now converts a pure non-creature card-type
change (only AddType mods, no Creature, no retention marker) to a replacing
SetCardTypes via maybe_replace_card_types. Creature animations (Gideon
Blackblade / Midnight Mangler / Circle of the Moon Druid) and 'in addition to
its other types' forms keep the additive path. The existing SetCardTypes runtime
drops the correlated creature subtype (Kraken), leaving a Legendary Land.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@minion1227 minion1227 requested a review from matthewevans as a code owner July 7, 2026 06:58
@github-actions github-actions Bot added the needs-maintainer AI-contribution PR requires human triage (Non-dev track or unresolved gaps) label Jul 7, 2026

@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 card-type replacement logic for self type-change effects (CR 205.1a, e.g., Arixmethes, Slumbering Isle) by introducing maybe_replace_card_types and adding corresponding tests. Feedback highlights two style guide violations: a non-standard CR annotation format on line 1413 and the use of verbatim string scanning (scan_contains) instead of nom combinators on line 1433.

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.

Some(def)
}

/// CR 205.1a / CR 205.1b: Decide whether a self type-change is a REPLACEMENT or

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.

medium

[MEDIUM] Non-standard CR annotation format. Evidence: crates/engine/src/parser/oracle_static/type_change.rs:1413.

Why it matters: The CR annotation uses a combined slash format (CR 205.1a / CR 205.1b) which may fail automated regex validation requiring the strict CR <number>: <description> format.

Suggested fix: Split the CR annotations into separate, cleanly formatted lines.

Suggested change
/// CR 205.1a / CR 205.1b: Decide whether a self type-change is a REPLACEMENT or
/// CR 205.1a: An effect that sets an object's card type replaces its existing card types.
/// CR 205.1b: Some effects specify that the object retains one or more of its prior card types.
/// Decide whether a self type-change is a REPLACEMENT or
References
  1. Every rules-touching line of engine code must carry a comment of the form CR : (regex CR \d{3}(.\d+[a-z]?)?). (link)

Comment on lines +1433 to +1435
if nom_primitives::scan_contains(body_lower, "in addition to")
|| nom_primitives::scan_contains(body_lower, "still a")
{

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.

medium

[MEDIUM] Avoid verbatim string scanning for parsing Oracle phrases. Evidence: crates/engine/src/parser/oracle_static/type_change.rs:1433.

Why it matters: Using scan_contains for phrases like 'still a' or 'in addition to' bypasses the robust nom-based parser and creates fragile matches. Instead, decompose compound phrases into modular, reusable parsers for constituent parts and compose them using idiomatic combinator aggregates (like nested alt and tag sequences) to prevent combinatorial explosion and improve maintainability.

Suggested change
if nom_primitives::scan_contains(body_lower, "in addition to")
|| nom_primitives::scan_contains(body_lower, "still a")
{
let mut parser = alt((
tag("in addition to"),
tag("still a"),
tag("still an"),
));
References
  1. Avoid verbatim string equality for parsing Oracle phrases as it bypasses the robust nom-based parser and creates fragile matches. Instead, decompose compound phrases into modular, reusable parsers for constituent parts and compose them using idiomatic combinator aggregates.

@github-actions

github-actions Bot commented Jul 7, 2026

Copy link
Copy Markdown

Parse changes introduced by this PR · 1 card(s), 1 signature(s) (baseline: main 27d836864f61)

1 card(s) · static/Continuous · field mods: add type landset card types land

Examples: Arixmethes, Slumbering Isle

1 card(s) had Oracle-text changes (errata/reprint) — excluded as non-parser.

@matthewevans matthewevans self-assigned this Jul 7, 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 review: right seam, CR 205.1a/205.1b checked, parse-diff is scoped to Arixmethes, and tests cover both parser shape and layer behavior.

@matthewevans matthewevans added the bug Bug fix label Jul 7, 2026
@matthewevans matthewevans added this pull request to the merge queue Jul 7, 2026
@matthewevans matthewevans removed their assignment Jul 7, 2026
Merged via the queue into phase-rs:main with commit 7db13c5 Jul 7, 2026
12 checks passed
real-venus added a commit to real-venus/phase that referenced this pull request Jul 7, 2026
…-land coverage

phase-rs#5231 (merged) already covers Arixmethes land replacement, the slumber
condition, additive retention, and artifact-creature retention. Swap the
duplicate tests for a distinct untested sibling: the "it's a <type>"
replacement and its "in addition to" retention exception are not
land-specific — "~ is an artifact" REPLACES via SetCardTypes while
"~ is an artifact in addition to its other types" stays additive.
andriypolanski pushed a commit to andriypolanski/phase that referenced this pull request Jul 7, 2026
…phase-rs#5218)

* fix(parser): self-ref "it's a <type>" replaces card types (CR 205.1a)

A self-referential type-change static of the form "it's a <core type>"
(Arixmethes, Slumbering Isle: "As long as ~ has a slumber counter on it,
it's a land.") went through the pronoun-animation path, which is
unconditionally additive: it emitted `AddType(Land)` and left the
permanent a "Legendary Creature Land — Kraken" — an impossible land
creature. Per CR 205.1a, "is a <type>" WITHOUT "in addition to its other
types" REPLACES the permanent's card types, so it must become a Land
only (Creature removed → "Legendary Land — Kraken").

In `parse_pronoun_becomes_type_static`, when the body is a pure bare
core-type set (no retained P/T, keywords, color, or subtype change) and
carries no "in addition"/"still a <type>" retention clause, collapse the
additive `AddType` list into one replacing `SetCardTypes` — mirroring the
aura type-change path's `needs_set_card_types`, and reusing the existing
`SetCardTypes` layer handler (which retains supertypes and creature
subtypes, yielding "Legendary Land — Kraken"). The additive animations
(Gideon Blackblade, manlands) are excluded because they carry a P/T,
keywords, or an explicit retention clause.

Closes phase-rs#5213

* Update crates/engine/src/parser/oracle_static/type_change.rs

Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>

* Update crates/engine/src/parser/oracle_static/type_change.rs

Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>

* fix(parser): use scan_contains in type-set guard; robust Arixmethes test

CI fixes for the self-ref "it's a <type>" replacement:
- The retention-clause guard used raw `str::contains`, which the parser
  combinator gate bans. Route through the approved
  `nom_primitives::scan_contains` helper (mirrors the existing
  "in addition to" scan in this module).
- Make the Arixmethes regression assert `contains(SetCardTypes[Land])` +
  no `AddType` rather than an exact `==` on the modification vector, so
  it is not brittle to incidental additional modifications, and
  fully-qualify `CoreType` to drop the local import.

* fix(parser): preserve CR 205.1b artifact-creature retention in type-set flip

The self-ref "it's a <type>" → SetCardTypes replacement wrongly collapsed
the artifact-creature animation class (Grond, the Gatebreaker; Midnight
Mangler; Phoenix Fleet Airship) from additive `AddType artifact/creature`
to a replacing `SetCardTypes`, which removes prior card types. Per CR
205.1b, animating a permanent INTO a creature ("~ is an artifact
creature") GAINS the creature type while retaining its other card types
— it must stay additive.

Restrict the replacement to type-sets that do NOT add Creature: only a
non-creature type-set like Arixmethes' "it's a land" (CR 205.1a / CR
305.7) replaces the card types. Add a regression asserting
"During your turn / During turns other than yours, ~ is an artifact
creature" stays `AddType` (no `SetCardTypes`).

* docs(parser): drop incorrect CR 305.7 from it's-a-land type-replacement comment

CR 305.7 governs setting a land's SUBTYPE (basic land types) and
explicitly does not add or remove card types; card-type replacement for
"it's a land" is CR 205.1a alone.

* test(parser): replace duplicate phase-rs#5231 tests with distinct non-land coverage

phase-rs#5231 (merged) already covers Arixmethes land replacement, the slumber
condition, additive retention, and artifact-creature retention. Swap the
duplicate tests for a distinct untested sibling: the "it's a <type>"
replacement and its "in addition to" retention exception are not
land-specific — "~ is an artifact" REPLACES via SetCardTypes while
"~ is an artifact in addition to its other types" stays additive.

---------

Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
Co-authored-by: matthewevans <matthewevans@users.noreply.github.com>
@minion1227 minion1227 deleted the minion_arixmethes branch July 9, 2026 17:49
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Bug fix needs-maintainer AI-contribution PR requires human triage (Non-dev track or unresolved gaps)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Card Bug] Arixmethes' type changing effect doesn't remove "Creature" type.

2 participants