fix(parser): Gonti-class look-and-exile-face-down exiles the dug card, not the source (#1146) - #3161
Conversation
…1134) Inspirit, Flagship Vessel's hexproof/indestructible grant is gated on HasCounters{charge >= 8}; assert it tears down when counters drop below 8. Station mechanic shipped in 67bf02e; this locks in the teardown behavior. CR 611.3a: continuous effects from static abilities are not locked in; they apply only while the condition holds. CR 122.1: counters on objects.
…, not the source (#1146) 'Look at top N, exile one of them face down, then play it' lowered to a keep_count:0 peek + sibling ChangeZone{ParentTarget}, which short-circuited the dig choice and exiled the trigger source instead of the chosen card. Fuse it into Dig{keep_count:1, destination:Exile} (Hideaway model) so the player-selected card is the one exiled. CR 701.20e: looking is private. CR 406.3/708.2: face-down exile. CR 702.75a: Hideaway structural analog.
There was a problem hiding this comment.
Code Review
This pull request implements support for the "exile one of them face down" continuation clause following a private Dig effect to resolve issue #1146, and adds regression tests for both Gonti's ETB behavior and the Station threshold teardown mechanic (issue #1134). The review feedback correctly identifies a compilation error in the 'append_conceal_sub_ability' helper due to a type mismatch when traversing the sub-ability chain, which must be resolved by calling '.as_mut()' on the boxed sub-ability.
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.
| while cursor.sub_ability.is_some() { | ||
| cursor = cursor | ||
| .sub_ability | ||
| .as_mut() | ||
| .expect("sub_ability checked above"); | ||
| } |
There was a problem hiding this comment.
The loop assignment will fail to compile because cursor is of type &mut AbilityDefinition, but .expect(...) returns &mut Box<AbilityDefinition>. To fix this type mismatch, you need to call .as_mut() on the boxed sub-ability to obtain a mutable reference to the inner AbilityDefinition, matching the pattern used elsewhere in this file.
| while cursor.sub_ability.is_some() { | |
| cursor = cursor | |
| .sub_ability | |
| .as_mut() | |
| .expect("sub_ability checked above"); | |
| } | |
| while cursor.sub_ability.is_some() { | |
| cursor = cursor | |
| .sub_ability | |
| .as_mut() | |
| .expect("sub_ability checked above") | |
| .as_mut(); | |
| } |
Closes #1146.
Problem
Gonti, Lord of Luxury's ETB — "look at the top four cards of an opponent's library, exile one of them face down, then you may … play that card" — incorrectly exiled Gonti himself instead of the chosen card.
Root cause: the parser lowered "exile one of them face down" as a
Dig { keep_count: 0 }peek + a siblingChangeZone { target: ParentTarget → Exile }. At runtime thekeep_count:0peek short-circuits (dig.rs:96) without ever surfacing aDigChoice, so no card is selected; the siblingChangeZone{ParentTarget}then resolves with an empty object-target set and falls back tosource_id(targeting.rs:509) — exiling Gonti.Fix
Fuse the clause into the Hideaway model:
Dig { keep_count: Some(1), destination: Exile }+ aHideawayConceal { ParentTarget }face-down step (mirrorsdatabase/hideaway.rs). The dug card is now player-selected and routed to exile by the Dig itself; the trailing "you may play that card" permission binds to it via the existing tracked-set publish.Gated on an
eof-anchored "exile one of them face down" recognizer so genuine pure-peek (Delver) and "exile target X card" siblings are untouched. Class fix (~17 cards: Thief of Sanity, Siphon Insight, …).Test
gonti_lord_of_luxury_exiles_dug_card.rsdrives the real cast pipeline:DigChoicenow surfaces (pre-fix it didn't); Gonti stays on the battlefield; the chosen library card is the exiled, face-down object.keep_count:0/ no DigChoice.Tilt
test-enginegreen:gonti_exiles_the_dug_card_not_himself ... PASS, full suite 12772 passed.CR
Known follow-up (out of scope)
"an opponent's library" currently lowers to
Controller(Gonti digs its own controller's library) — a separate parser gap, not this bug. Flagged for follow-up.🤖 Generated with Claude Code