Skip to content

fix(parser): Gonti-class look-and-exile-face-down exiles the dug card, not the source (#1146) - #3161

Merged
matthewevans merged 2 commits into
mainfrom
fix/issue-1146-gonti-exile
Jun 13, 2026
Merged

fix(parser): Gonti-class look-and-exile-face-down exiles the dug card, not the source (#1146)#3161
matthewevans merged 2 commits into
mainfrom
fix/issue-1146-gonti-exile

Conversation

@matthewevans

Copy link
Copy Markdown
Member

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 sibling ChangeZone { target: ParentTarget → Exile }. At runtime the keep_count:0 peek short-circuits (dig.rs:96) without ever surfacing a DigChoice, so no card is selected; the sibling ChangeZone{ParentTarget} then resolves with an empty object-target set and falls back to source_id (targeting.rs:509) — exiling Gonti.

Fix

Fuse the clause into the Hideaway model: Dig { keep_count: Some(1), destination: Exile } + a HideawayConceal { ParentTarget } face-down step (mirrors database/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.rs drives the real cast pipeline:

  • Discriminators: a DigChoice now surfaces (pre-fix it didn't); Gonti stays on the battlefield; the chosen library card is the exiled, face-down object.
  • Regression guard: Delver-class pure-peek still lowers to keep_count:0 / no DigChoice.

Tilt test-engine green: gonti_exiles_the_dug_card_not_himself ... PASS, full suite 12772 passed.

CR

  • CR 701.20e: looking is private. CR 406.3 / 708.2: face-down exile. CR 702.75a: Hideaway structural analog. CR 608.2c: continuation ordering.

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

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

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

Comment on lines +2738 to +2743
while cursor.sub_ability.is_some() {
cursor = cursor
.sub_ability
.as_mut()
.expect("sub_ability checked above");
}

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

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.

Suggested change
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();
}

@matthewevans
matthewevans enabled auto-merge June 13, 2026 19:24
@matthewevans
matthewevans added this pull request to the merge queue Jun 13, 2026
Merged via the queue into main with commit 02641d5 Jun 13, 2026
10 checks passed
@matthewevans
matthewevans deleted the fix/issue-1146-gonti-exile branch June 13, 2026 19:38
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Gonti, Lord of Luxury — When Gonti is played from the Command Zone, the game automatically puts him into Exile with no…

1 participant