Skip to content

fix(engine): honor controller choice for self tucks - #6882

Merged
matthewevans merged 2 commits into
mainfrom
maintainer/rescue-5889-self-tuck-chooser
Aug 2, 2026
Merged

fix(engine): honor controller choice for self tucks#6882
matthewevans merged 2 commits into
mainfrom
maintainer/rescue-5889-self-tuck-chooser

Conversation

@matthewevans

@matthewevans matthewevans commented Aug 2, 2026

Copy link
Copy Markdown
Member

Rescues the valuable self-tuck controller-choice semantics from #5889.\n\n- models the effect-designated chooser separately from the card owner whose library receives the object;\n- parses controller/owner choice forms and preserves the existing owner-default behavior;\n- covers serialization, parser, resolver, and stolen-permanent behavior.\n\nThe protected pre-push gate passed on the published tree (parser gates, engine/AI tests, card-data validation/coverage, frontend lint/type-check).

Summary by CodeRabbit

  • New Features

    • Added support for selecting which player chooses whether an object goes on top or bottom of its owner’s library.
    • Improved handling of self-referential and controller-specific effects.
    • Preserved compatibility for existing effects and serialized data.
  • Bug Fixes

    • Corrected chooser and target resolution when the affected object’s controller differs from its owner.
    • Added coverage for self-tuck behavior and related library placement choices.

@matthewevans matthewevans added the bug Bug fix label Aug 2, 2026
@coderabbitai

coderabbitai Bot commented Aug 2, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Warning

Review limit reached

@matthewevans, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 40 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 6cbe1492-c9d4-4abe-9050-7a6332ec8ebb

📥 Commits

Reviewing files that changed from the base of the PR and between 9075f61 and 4d0ca53.

📒 Files selected for processing (11)
  • crates/engine/src/game/ability_rw.rs
  • crates/engine/src/game/ability_scan.rs
  • crates/engine/src/game/coverage.rs
  • crates/engine/src/game/effects/put_on_top_or_bottom.rs
  • crates/engine/src/parser/oracle_effect/mod.rs
  • crates/engine/src/parser/oracle_effect/tests.rs
  • crates/engine/src/types/ability.rs
  • crates/engine/src/types/game_state.rs
  • crates/engine/tests/integration/arashin_sovereign_self_tuck.rs
  • crates/engine/tests/integration/cost_zone_pipeline.rs
  • crates/engine/tests/integration/main.rs
📝 Walkthrough

Walkthrough

Changes

PutOnTopOrBottom chooser flow

Layer / File(s) Summary
Effect contract and resolution
crates/engine/src/types/ability.rs, crates/engine/src/types/game_state.rs, crates/engine/src/game/effects/put_on_top_or_bottom.rs
PutOnTopOrBottom now stores a configurable chooser. Resolution uses unified target handling and creates TopOrBottomChoice for the resolved chooser. Serialization preserves compatibility with owner-based effects.
Oracle parsing and chooser assignment
crates/engine/src/parser/oracle_effect/mod.rs, crates/engine/src/parser/oracle_effect/tests.rs
The parser assigns owner choosers to owner-based clauses and controller choosers to self-referential clauses. Parser tests assert the explicit chooser.
Effect analysis paths
crates/engine/src/game/ability_rw.rs, crates/engine/src/game/ability_scan.rs, crates/engine/src/game/coverage.rs
Legacy-reference traversal, axis scanning, and coverage details now include the chooser filter.
Integration validation and wiring
crates/engine/tests/integration/arashin_sovereign_self_tuck.rs, crates/engine/tests/integration/cost_zone_pipeline.rs, crates/engine/tests/integration/main.rs
Integration tests cover controller-specific self-tuck behavior. Existing test abilities declare owner choosers explicitly, and the new test module is registered.

Estimated code review effort: 3 (Moderate) | ~25 minutes

Sequence Diagram(s)

sequenceDiagram
  participant OracleEffectParser
  participant PutOnTopOrBottomResolve
  participant TargetFilter
  participant TopOrBottomChoice
  OracleEffectParser->>PutOnTopOrBottomResolve: create effect with target and chooser filters
  PutOnTopOrBottomResolve->>TargetFilter: resolve target
  TargetFilter-->>PutOnTopOrBottomResolve: matching object
  PutOnTopOrBottomResolve->>TargetFilter: resolve chooser
  TargetFilter-->>PutOnTopOrBottomResolve: controller or owner
  PutOnTopOrBottomResolve->>TopOrBottomChoice: create top-or-bottom choice
Loading

Suggested reviewers: jacobwoodson

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely describes the main change: honoring the controller's choice for self-tuck effects.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch maintainer/rescue-5889-self-tuck-chooser

Comment @coderabbitai help to get the list of available commands.

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

🧹 Nitpick comments (3)
crates/engine/src/parser/oracle_effect/mod.rs (1)

9076-9081: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Add a CR 608.2d annotation to try_parse_self_put_on_top_or_bottom.

This function parses a resolution-time choice and currently has no CR citation.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@crates/engine/src/parser/oracle_effect/mod.rs` around lines 9076 - 9081, Add
a `CR 608.2d` annotation to the `try_parse_self_put_on_top_or_bottom` function,
preserving its existing parsing behavior and placement within the surrounding
parser code.

Source: Path instructions

crates/engine/src/game/effects/put_on_top_or_bottom.rs (1)

1-55: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Correct the CR citations for resolved_targets.

CR 608.2c covers instruction ordering. CR 603.10 covers look-back-in-time trigger exceptions. Neither documents the three-tier dispatch or SelfRef handling. Use the applicable self-reference and object-identity rules, such as CR 201.5a and CR 400.7; retain CR 603.10 only for the relevant leaves-the-battlefield trigger context. CR 608.2d remains appropriate for the choice player.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@crates/engine/src/game/effects/put_on_top_or_bottom.rs` around lines 1 - 55,
Correct the comments in resolve around resolved_targets: remove the inaccurate
claim that CR 608.2c and CR 603.10 document the three-tier dispatch or SelfRef
handling, and cite the applicable self-reference/object-identity rules such as
CR 201.5a and CR 400.7. Retain CR 603.10 only where it specifically applies to
the leaves-the-battlefield trigger context, and leave the CR 608.2d
choice-player citation unchanged.

Source: Learnings

crates/engine/src/game/coverage.rs (1)

3433-3435: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Suppress the default chooser so unchanged cards keep a byte-identical coverage signature.

chooser defaults to TargetFilter::ParentTargetOwner. Emitting it unconditionally adds a new chooser row to every pre-existing PutOnTopOrBottom card (Aether Gust, Subtlety, Aetherspouts), none of which changed behavior in this PR. The coverage parse-diff sticky will report those cards as changed.

This matches the default-suppression convention already used in this function: Draw skips TargetFilter::Controller, and the Mana arm (lines 2907-2915) documents emitting each field "only when set so unqualified signatures stay byte-identical".

♻️ Emit `chooser` only when it is not the owner default
         Effect::PutOnTopOrBottom { target, chooser } => {
             d.push(("target".into(), fmt_target(target)));
-            d.push(("chooser".into(), fmt_target(chooser)));
+            if !matches!(chooser, TargetFilter::ParentTargetOwner) {
+                d.push(("chooser".into(), fmt_target(chooser)));
+            }
         }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@crates/engine/src/game/coverage.rs` around lines 3433 - 3435, Update the
Effect::PutOnTopOrBottom arm in the coverage formatter to emit the chooser field
only when it differs from TargetFilter::ParentTargetOwner, while always
retaining the target field. Follow the existing default-suppression convention
used by Draw and Mana so unchanged cards preserve byte-identical coverage
signatures.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@crates/engine/src/game/coverage.rs`:
- Around line 3433-3435: Update the Effect::PutOnTopOrBottom arm in the coverage
formatter to emit the chooser field only when it differs from
TargetFilter::ParentTargetOwner, while always retaining the target field. Follow
the existing default-suppression convention used by Draw and Mana so unchanged
cards preserve byte-identical coverage signatures.

In `@crates/engine/src/game/effects/put_on_top_or_bottom.rs`:
- Around line 1-55: Correct the comments in resolve around resolved_targets:
remove the inaccurate claim that CR 608.2c and CR 603.10 document the three-tier
dispatch or SelfRef handling, and cite the applicable
self-reference/object-identity rules such as CR 201.5a and CR 400.7. Retain CR
603.10 only where it specifically applies to the leaves-the-battlefield trigger
context, and leave the CR 608.2d choice-player citation unchanged.

In `@crates/engine/src/parser/oracle_effect/mod.rs`:
- Around line 9076-9081: Add a `CR 608.2d` annotation to the
`try_parse_self_put_on_top_or_bottom` function, preserving its existing parsing
behavior and placement within the surrounding parser code.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: e1e5b361-8de8-462f-993d-5f661535e834

📥 Commits

Reviewing files that changed from the base of the PR and between b902333 and 9075f61.

📒 Files selected for processing (11)
  • crates/engine/src/game/ability_rw.rs
  • crates/engine/src/game/ability_scan.rs
  • crates/engine/src/game/coverage.rs
  • crates/engine/src/game/effects/put_on_top_or_bottom.rs
  • crates/engine/src/parser/oracle_effect/mod.rs
  • crates/engine/src/parser/oracle_effect/tests.rs
  • crates/engine/src/types/ability.rs
  • crates/engine/src/types/game_state.rs
  • crates/engine/tests/integration/arashin_sovereign_self_tuck.rs
  • crates/engine/tests/integration/cost_zone_pipeline.rs
  • crates/engine/tests/integration/main.rs

@github-actions

github-actions Bot commented Aug 2, 2026

Copy link
Copy Markdown

Parse changes introduced by this PR · 35 card(s), 3 signature(s) (baseline: main 4b4e9e57c3b4)

🟢 Added (1 signature)

  • 2 cards · ➕ ability/PutOnTopOrBottom · added: PutOnTopOrBottom (chooser=controller, target=self)
    • Affected (first 3): Arashin Sovereign, S.N.E.A.K. Dispatcher

🔴 Removed (1 signature)

  • 2 cards · ➖ ability/put · removed: put
    • Affected (first 3): Arashin Sovereign, S.N.E.A.K. Dispatcher

🟡 Modified fields (1 signature)

  • 33 cards · 🔄 ability/PutOnTopOrBottom · changed field chooser: parent target's owner
    • Affected (first 3): A-You Come to a River, Aether Gust, Aetherspouts (+30 more)

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

@matthewevans
matthewevans force-pushed the maintainer/rescue-5889-self-tuck-chooser branch from 9075f61 to 292dc56 Compare August 2, 2026 08:42
root and others added 2 commits August 2, 2026 01:43
Route the self-reflexive library tuck "put it on your choice of the top or
bottom of its owner's library" to the existing Effect::PutOnTopOrBottom.

The owner-framing patterns in try_parse_put_on_top_or_bottom recognize "its
owner puts it on their choice of the top or bottom of their library" (Aether
Gust, Subtlety, Aetherspouts). The controller-framing self form (Arashin
Sovereign's dies trigger) had no arm, so the effect body lowered to
Effect::Unimplemented and nothing happened at resolution -- Arashin stayed in
the graveyard and the whole card rendered unsupported.

try_parse_self_put_on_top_or_bottom adds one recognizer arm that routes the
SELF form to the same Effect::PutOnTopOrBottom the owner-framing patterns
produce -- no new effect variant. It is scoped to the self reference
(resolve_it_pronoun == SelfRef): for the dying source's own library tuck the
owner IS the controller, so the effect's owner-chooses resolution (CR 401.4)
matches the card's "your choice". A non-self "it" (S.N.E.A.K. Dispatcher) or a
"that card" subject (Hinder) is declined so it stays an honest coverage gap
rather than letting the wrong player choose the position.

PutOnTopOrBottom's resolver read ability.targets directly, which is empty for a
SelfRef effect (SelfRef is not a chosen target). It now delegates to the shared
resolved_targets 3-tier dispatch, matching Effect::Bounce and the other
zone-change resolvers -- SelfRef short-circuits to ability.source_id while a
chosen/parent target is unchanged (tier 3 returns the pre-selected targets).

Adds a discriminating integration test that drives the real dies-trigger ->
resolve -> ChooseTopOrBottom pipeline and fails on main.
@matthewevans
matthewevans force-pushed the maintainer/rescue-5889-self-tuck-chooser branch from 292dc56 to 4d0ca53 Compare August 2, 2026 08:43
@matthewevans
matthewevans added this pull request to the merge queue Aug 2, 2026
Merged via the queue into main with commit 36ac6d2 Aug 2, 2026
14 checks passed
@matthewevans
matthewevans deleted the maintainer/rescue-5889-self-tuck-chooser branch August 2, 2026 09:20
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Bug fix

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant