Skip to content

fix(coverage): traverse direct effect payloads - #7137

Merged
matthewevans merged 2 commits into
mainfrom
ship/fixcoverage-traverse-direct-effect-payloads
Aug 10, 2026
Merged

fix(coverage): traverse direct effect payloads#7137
matthewevans merged 2 commits into
mainfrom
ship/fixcoverage-traverse-direct-effect-payloads

Conversation

@matthewevans

@matthewevans matthewevans commented Aug 10, 2026

Copy link
Copy Markdown
Member

Summary by CodeRabbit

  • Bug Fixes
    • Improved handling of abilities embedded directly within game effects, including voting, pile separation, reveals, delayed triggers, dice, coin flips, and choice branches.
    • Improved support detection and validation for nested ability effects.
    • Corrected traversal and reporting for unsupported content and nested gaps.
    • Expanded coverage for replacement executions and complex effect combinations, improving consistency across parsing, modifications, and semantic checks.

@matthewevans
matthewevans enabled auto-merge August 10, 2026 00:30
@coderabbitai

coderabbitai Bot commented Aug 10, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

Coverage traversal now discovers executable abilities embedded directly in vote, pile, reveal, delayed-trigger, die, coin-flip, and ChooseOneOf effects. A shared visitor supports parsing, validation, extraction, modification traversal, and semantic audits. Tests cover all payload edges and nested cases.

Changes

Direct effect payload coverage

Layer / File(s) Summary
Direct payload visitor and parse details
crates/engine/src/game/coverage.rs
Adds shared traversal for executable abilities in direct effect payloads. Parse details now include nested abilities.
Coverage consumer integration
crates/engine/src/game/coverage.rs
Updates modification traversal, unsupported detection, gap collection, resolver checks, feature extraction, and semantic-audit traversal to inspect direct payload abilities.
Direct payload regression coverage
crates/engine/src/game/coverage.rs
Adds fixtures and tests for payload edges, nested gaps, unsupported effects, delayed triggers, replacement execution, and traversal behavior.

Estimated code review effort: 4 (Complex) | ~45 minutes

Possibly related PRs

  • phase-rs/phase#6323: Both changes update coverage feature and support analysis in coverage.rs.
  • phase-rs/phase#6686: Both changes update coverage and detail traversal for effect payloads.
  • phase-rs/phase#6978: Both changes update semantic-audit traversal for nested delayed-trigger payloads.

Suggested labels: bug

Suggested reviewers: jacobwoodson, andriypolanski, mike-thedude

🚥 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 identifies the coverage fix and the traversal of direct effect payloads, which matches the main change.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
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 ship/fixcoverage-traverse-direct-effect-payloads

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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.

Actionable comments posted: 1

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

Inline comments:
In `@crates/engine/src/game/coverage.rs`:
- Around line 6256-6339: Replace the wildcard arm in
visit_direct_effect_ability_payloads with an exhaustive match arm listing every
current Effect variant that has no direct AbilityDefinition payload, grouping
them with |. Keep the existing payload-visiting arms unchanged, so adding a new
Effect variant forces explicit classification at compile time.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 52e525ee-4def-4970-bdb9-e40a5f4c08e3

📥 Commits

Reviewing files that changed from the base of the PR and between 9759165 and d3971d9.

📒 Files selected for processing (1)
  • crates/engine/src/game/coverage.rs

Comment thread crates/engine/src/game/coverage.rs
@matthewevans
matthewevans force-pushed the ship/fixcoverage-traverse-direct-effect-payloads branch from d3971d9 to 50f6918 Compare August 10, 2026 00:41
@github-actions

github-actions Bot commented Aug 10, 2026

Copy link
Copy Markdown

Generated for head 50f69185ce4e3ab991fe2c999fb25e04304f270d.

Parse changes introduced by this PR · 1153 card(s), 471 signature(s) (baseline: main 5417a0573714)

🟢 Added (25 signatures)

  • 72 cards · ➕ ability/Draw · added: Draw
    • Affected (first 3): A Good Day to Die, Aleatory, Arcane Denial (+69 more)
  • 57 cards · ➕ ability/ChangeZone · added: ChangeZone (from=battlefield, target=self, to=exile)
    • Affected (first 3): Anathemancer, Archfiend of Sorrows, Artificer's Dragon (+54 more)
  • 55 cards · ➕ ability/Sacrifice · added: Sacrifice (target=last created)
    • Affected (first 3): A-Dorothea's Retribution, Ashling, the Limitless, Avenger of the Fallen (+52 more)
  • 48 cards · ➕ ability/Untap · added: Untap (target=parent target)
    • Affected (first 3): Bounding Krasis, Captain of the Mists, Chain Stasis (+45 more)
  • 47 cards · ➕ ability/Tap · added: Tap (target=parent target)
    • Affected (first 3): Bounding Krasis, Captain of the Mists, Chain Stasis (+44 more)
  • 40 cards · ➕ ability/ChangeZone · added: ChangeZone (from=exile, target=parent target, to=battlefield)
    • Affected (first 3): Astral Drift, Astral Slide, Bad Wolf Bay (+37 more)
  • 39 cards · ➕ ability/Sacrifice · added: Sacrifice (target=self)
    • Affected (first 3): A-Dorothea, Vengeful Victim, Acidic Dagger, Armor of Thorns (+36 more)
  • 37 cards · ➕ ability/ChangeZone · added: ChangeZone (from=battlefield, target=last created, to=exile)
    • Affected (first 3): Akoum Stonewaker, Angelic Favor, Daring Piracy (+34 more)
  • 35 cards · ➕ ability/Bounce · added: Bounce (target=parent target)
    • Affected (first 3): Aberrant Mind Sorcerer, Aether Membrane, Alora, Cheerful Assassin (+32 more)
  • 35 cards · ➕ ability/Destroy · added: Destroy (target=parent target)
    • Affected (first 3): Abomination, Acidic Dagger, Battering Ram (+32 more)
  • 34 cards · ➕ ability/ChangeZone · added: ChangeZone (enter_tapped=Tapped, enters_under=You, target=triggering source, to=battlefield)
    • Affected (first 3): Aang, Destined Savior, Avatar Kyoshi, Earthbender, Ba Sing Se (+31 more)
  • 34 cards · ➕ ability/Sacrifice · added: Sacrifice (target=parent target)
    • Affected (first 3): Animate Dead, Attack-in-the-Box, Balduvian Atrocity (+31 more)
  • 23 cards · ➕ ability/CopySpell · added: CopySpell (target=triggering source)
    • Affected (first 3): Breeches, the Blastmaker, Chandra, the Firebrand, Complete the Circuit (+20 more)
  • 22 cards · ➕ ability/ChangeZone · added: ChangeZone (target=parent target, to=exile)
    • Affected (first 3): Ayara, Furnace Queen, Coalstoke Gearhulk, Conqueror's Galleon (+19 more)
  • 19 cards · ➕ ability/Tap · added: Tap (target=self)
    • Affected (first 3): Ancient Amphitheater, Auntie's Hovel, Choked Estuary (+16 more)
  • 18 cards · ➕ ability/PutCounter · added: PutCounter (counter=1 P1P1, target=self)
    • Affected (first 3): Accomplished Automaton, Ambitious Aetherborn, Basri, Devoted Paladin (+15 more)
  • 18 cards · ➕ ability/Sacrifice · added: Sacrifice (kind=activated, target=parent target)
    • Affected (first 3): Angrath, the Flame-Chained, Apprentice Necromancer, Arms Race (+15 more)
  • 15 cards · ➕ ability/ChangeZone · added: ChangeZone (from=exile, kind=activated, target=parent target, to=battlefield)
    • Affected (first 3): Abuelo, Ancestral Echo, Aetherling, Angel of Condemnation (+12 more)
  • 14 cards · ➕ ability/CopySpell · added: CopySpell (target=parent target)
    • Affected (first 3): Adaptive Training Post, Bonus Round, Howl of the Horde (+11 more)
  • 14 cards · ➕ ability/PayCost · added: PayCost
    • Affected (first 3): Asgardian Inspiration, Endless Ranks of HYDRA, Intervention Pact (+11 more)
  • 12 cards · ➕ ability/Sacrifice · added: Sacrifice (kind=activated, target=last created)
    • Affected (first 3): Balduvian Dead, Chandra, Acolyte of Flame, Chrome Dome (+9 more)
  • 10 cards · ➕ ability/ChangeZone · added: ChangeZone (from=battlefield, kind=activated, target=last created, to=exile)
    • Affected (first 3): Chandra, Flamecaller, Cogwork Assembler, Echo Chamber (+7 more)
  • 10 cards · ➕ ability/Token · added: Token (token=+1/+1 Servo (Artifact Creature Servo))
    • Affected (first 3): Accomplished Automaton, Ambitious Aetherborn, Cayth, Famed Mechanist (+7 more)
  • 9 cards · ➕ ability/Discard · added: Discard (count=1, target=controller)
    • Affected (first 3): Chandra, Spark Hunter, Contract Hero, Crypt Lurker (+6 more)
  • 9 cards · ➕ ability/grant Lifelink · added: grant Lifelink (affects=parent target, duration=until end of turn, grants=grant Lifelink)
    • Affected (first 3): Alchemist's Gift, Atraxa's Skitterfang, Gideon Blackblade (+6 more)
… 446 more signature(s) (737 card-changes) — showing first 200; see parse-diff.json

🟢 Added (200 signatures)

  • 8 cards · ➕ ability/PutCounter · added: PutCounter (counter=1 P1P1, target=parent target)
  • 8 cards · ➕ ability/Sacrifice · added: Sacrifice (target=tracked set #0)
  • 8 cards · ➕ ability/Token · added: Token (token=Treasure (Artifact Treasure))
  • 7 cards · ➕ ability/ChangeZone · added: ChangeZone (enters_under=You, target=parent target, to=battlefield)
  • 7 cards · ➕ ability/ChangeZone · added: ChangeZone (from=graveyard, target=self, to=hand)
  • 7 cards · ➕ ability/ChangeZone · added: ChangeZone (target=parent target, to=battlefield)
  • 7 cards · ➕ ability/Draw · added: Draw (count=2)
  • 7 cards · ➕ ability/Sacrifice · added: Sacrifice (kind=activated, target=self)
  • 7 cards · ➕ ability/grant Lifelink · added: grant Lifelink (affects=self, duration=until end of turn, grants=grant Lifelink)
  • 6 cards · ➕ ability/ChangeZone · added: ChangeZone (kind=activated, target=parent target, to=exile)
  • 6 cards · ➕ ability/ChangeZone · added: ChangeZone (target=self, to=battlefield)
  • 6 cards · ➕ ability/CopyTokenOf · added: CopyTokenOf (copies=parent target)
  • 6 cards · ➕ ability/Destroy · added: Destroy (kind=activated, target=parent target)
  • 6 cards · ➕ ability/PutCounter · added: PutCounter (counter=1 trample, target=parent target)
  • 6 cards · ➕ ability/grant Flying · added: grant Flying (affects=self, duration=until end of turn, grants=grant Flying)
  • 5 cards · ➕ ability/ChangeZone · added: ChangeZone (from=battlefield, target=tracked set #0, to=exile)
  • 5 cards · ➕ ability/ChangeZone · added: ChangeZone (target=parent target, to=graveyard)
  • 5 cards · ➕ ability/ChaosEnsues · added: ChaosEnsues
  • 5 cards · ➕ ability/Choose · added: Choose (choice=color, duration=until end of turn, persist=yes)
  • 5 cards · ➕ ability/Draw · added: Draw (kind=activated)
  • 5 cards · ➕ ability/LoseTheGame · added: LoseTheGame
  • 5 cards · ➕ ability/Planeswalk · added: Planeswalk
  • 5 cards · ➕ ability/PutCounter · added: PutCounter (counter=1 vigilance, target=parent target)
  • 5 cards · ➕ ability/PutCounter · added: PutCounter (counter=2 P1P1, target=self)
  • 5 cards · ➕ ability/Sacrifice · added: Sacrifice (target=you control artifact)
  • 5 cards · ➕ ability/Token · added: Token (token=2× +1/+1 Servo (Artifact Creature Servo))
  • 5 cards · ➕ ability/grant Haste · added: grant Haste (affects=self, duration=until end of turn, grants=grant Haste)
  • 4 cards · ➕ ability/AddPendingETBCounters · added: AddPendingETBCounters
  • 4 cards · ➕ ability/Bounce · added: Bounce (kind=activated, target=parent target)
  • 4 cards · ➕ ability/ChangeZone · added: ChangeZone (target=parent target, to=hand)
  • 4 cards · ➕ ability/ChangeZone · added: ChangeZone (target=tracked set #0, to=battlefield)
  • 4 cards · ➕ ability/ChangeZone · added: ChangeZone (target=triggering source, to=battlefield)
  • 4 cards · ➕ ability/DiscardCard · added: DiscardCard (target=parent target)
  • 4 cards · ➕ ability/GainLife · added: GainLife (amount=event amount)
  • 4 cards · ➕ ability/LoseLife · added: LoseLife (amount=1)
  • 4 cards · ➕ ability/LoseLife · added: LoseLife (amount=2)
  • 4 cards · ➕ ability/PutCounter · added: PutCounter (counter=1 menace, target=parent target)
  • 4 cards · ➕ ability/RemoveCounter · added: RemoveCounter (counter=1 P1P1, target=self)
  • 4 cards · ➕ ability/Sacrifice · added: Sacrifice (target=you control creature)
  • 4 cards · ➕ ability/Token · added: Token (token=Food (Artifact Food))
  • 4 cards · ➕ ability/grant Deathtouch · added: grant Deathtouch (affects=parent target, duration=until end of turn, grants=grant Deathtouch)
  • 4 cards · ➕ ability/grant Deathtouch · added: grant Deathtouch (affects=self, duration=until end of turn, grants=grant Deathtouch)
  • 4 cards · ➕ ability/grant FirstStrike · added: grant FirstStrike (affects=self, duration=until end of turn, grants=grant FirstStrike)
  • 4 cards · ➕ ability/grant Protection · added: grant Protection (affects=parent target, duration=until end of turn, grants=grant Protection)
  • 4 cards · ➕ ability/grant Vigilance · added: grant Vigilance (affects=parent target, duration=until end of turn, grants=grant Vigilance)
  • 4 cards · ➕ ability/grant Vigilance · added: grant Vigilance (affects=self, duration=until end of turn, grants=grant Vigilance)
  • 3 cards · ➕ ability/AddRestriction · added: AddRestriction
  • 3 cards · ➕ ability/Bounce · added: Bounce (target=triggering source)
  • 3 cards · ➕ ability/ChangeZone · added: ChangeZone (enter_with_counters=[(Plus1Plus1, Fixed { value: 1 })], from=exile, target=parent target, to=battlefield)
  • 3 cards · ➕ ability/ChangeZone · added: ChangeZone (from=battlefield, target=another you control creature, to=exile)
  • 3 cards · ➕ ability/ChangeZone · added: ChangeZone (target=tracked set #0, to=exile)
  • 3 cards · ➕ ability/CopySpell · added: CopySpell (target=cards exiled by source)
  • 3 cards · ➕ ability/GainLife · added: GainLife (amount=1)
  • 3 cards · ➕ ability/GainLife · added: GainLife (amount=2)
  • 3 cards · ➕ ability/Investigate · added: Investigate
  • 3 cards · ➕ ability/PhaseIn · added: PhaseIn (target=parent target)
  • 3 cards · ➕ ability/PutCounter · added: PutCounter (counter=1 P1P1, target=you control creature)
  • 3 cards · ➕ ability/PutCounter · added: PutCounter (counter=1 first strike, target=parent target)
  • 3 cards · ➕ ability/PutCounter · added: PutCounter (counter=1 flying, target=self)
  • 3 cards · ➕ ability/PutCounter · added: PutCounter (counter=1 lifelink, target=parent target)
  • 3 cards · ➕ ability/PutCounter · added: PutCounter (counter=1 reach, target=parent target)
  • 3 cards · ➕ ability/RemoveCounter · added: RemoveCounter (counter=1 P1P1, target=parent target)
  • 3 cards · ➕ ability/Transform · added: Transform (target=self)
  • 3 cards · ➕ ability/UntapAll · added: UntapAll (filter=attacked this turn creature)
  • 3 cards · ➕ ability/grant Flying · added: grant Flying (affects=parent target, duration=until end of turn, grants=grant Flying)
  • 3 cards · ➕ ability/grant Trample · added: grant Trample (affects=self, duration=until end of turn, grants=grant Trample)
  • 2 cards · ➕ ability/BecomeMonarch · added: BecomeMonarch
  • 2 cards · ➕ ability/Bounce · added: Bounce (kind=activated, target=self)
  • 2 cards · ➕ ability/Bounce · added: Bounce (target=self)
  • 2 cards · ➕ ability/Bounce · added: Bounce (target=tracked set #0)
  • 2 cards · ➕ ability/ChangeZone · added: ChangeZone (enter_tapped=Tapped, from=exile, kind=activated, target=parent target, to=battlefield)
  • 2 cards · ➕ ability/ChangeZone · added: ChangeZone (enter_tapped=Tapped, target=tracked set #0, to=battlefield)
  • 2 cards · ➕ ability/ChangeZone · added: ChangeZone (enter_transformed=true, enters_under=You, target=parent target, to=battlefield)
  • 2 cards · ➕ ability/ChangeZone · added: ChangeZone (enters_under=You, from=exile, kind=activated, target=parent target, to=battlefield)
  • 2 cards · ➕ ability/ChangeZone · added: ChangeZone (from=battlefield, target=another you control creature Elemental, to=exile)
  • 2 cards · ➕ ability/ChangeZone · added: ChangeZone (from=battlefield, target=any target, to=exile)
  • 2 cards · ➕ ability/ChangeZone · added: ChangeZone (from=exile, kind=activated, target=parent target, to=hand)
  • 2 cards · ➕ ability/ChangeZone · added: ChangeZone (from=graveyard, target=self, to=exile)
  • 2 cards · ➕ ability/ChangeZone · added: ChangeZone (kind=activated, target=parent target, to=battlefield)
  • 2 cards · ➕ ability/ChangeZone · added: ChangeZone (kind=activated, target=tracked set #0, to=battlefield)
  • 2 cards · ➕ ability/DamageAll · added: DamageAll (amount=3, filter=creature)
  • 2 cards · ➕ ability/DamageEachPlayer · added: DamageEachPlayer (amount=2, players=each opponent)
  • 2 cards · ➕ ability/DealDamage · added: DealDamage (amount=5, target=triggering source)
  • 2 cards · ➕ ability/Destroy · added: Destroy (kind=activated, target=self)
  • 2 cards · ➕ ability/Destroy · added: Destroy (target=self)
  • 2 cards · ➕ ability/Discard · added: Discard (count=1, target=scoped player)
  • 2 cards · ➕ ability/Discard · added: Discard (count=3, target=controller)
  • 2 cards · ➕ ability/Discard · added: Discard (count=cards in hand (target player), kind=activated, target=controller)
  • 2 cards · ➕ ability/Draw · added: Draw (count=3)
  • 2 cards · ➕ ability/ExileTop · added: ExileTop (count=1, player=controller)
  • 2 cards · ➕ ability/ExtraTurn · added: ExtraTurn (player=controller)
  • 2 cards · ➕ ability/GainLife · added: GainLife (amount=3)
  • 2 cards · ➕ ability/GivePlayerCounter · added: GivePlayerCounter (count=1, counter=Poison, target=parent target's controller)
  • 2 cards · ➕ ability/LoseLife · added: LoseLife (amount=3)
  • 2 cards · ➕ ability/LoseLife · added: LoseLife (amount=4)
  • 2 cards · ➕ ability/Mana · added: Mana (mana={C} xself mana value)
  • 2 cards · ➕ ability/Pump · added: Pump (p/t=-1/+0, target=self)
  • 2 cards · ➕ ability/Pump · added: Pump (p/t=-1/-1, target=opponent controls creature)
  • 2 cards · ➕ ability/PutAtLibraryPosition · added: PutAtLibraryPosition (count=Fixed { value: 1 }, position=Top, target=parent target)
  • 2 cards · ➕ ability/PutCounter · added: PutCounter (counter=1 P1P1, target=triggering source)
  • 2 cards · ➕ ability/PutCounter · added: PutCounter (counter=1 first strike, target=self)
  • 2 cards · ➕ ability/PutCounter · added: PutCounter (counter=1 vigilance, target=self)
  • 2 cards · ➕ ability/PutCounter · added: PutCounter (counter=2 P1P1, target=parent target)
  • 2 cards · ➕ ability/PutCounter · added: PutCounter (counter=2 P1P1, target=triggering source)
  • 2 cards · ➕ ability/PutCounter · added: PutCounter (counter=3 P1P1, target=self)
  • 2 cards · ➕ ability/PutCounter · added: PutCounter (counter=EventContextAmount +0/+1, target=parent target)
  • 2 cards · ➕ ability/PutCounterAll · added: PutCounterAll (counter=1 P1P1, target=you control creature)
  • 2 cards · ➕ ability/RemoveCounter · added: RemoveCounter (counter=1 time, target=self)
  • 2 cards · ➕ ability/Sacrifice · added: Sacrifice (target=scoped player controls creature)
  • 2 cards · ➕ ability/Sacrifice · added: Sacrifice (target=you control permanent)
  • 2 cards · ➕ ability/Scry · added: Scry (count=2)
  • 2 cards · ➕ ability/Scry · added: Scry (count=3)
  • 2 cards · ➕ ability/SearchLibrary · added: SearchLibrary (find=mv referenced object's mana value< creature)
  • 2 cards · ➕ ability/Token · added: Token (token=+1/+1 Red Goblin (Creature Goblin))
  • 2 cards · ➕ ability/Token · added: Token (token=Clue (Artifact Clue))
  • 2 cards · ➕ ability/UntapAll · added: UntapAll (filter=you control creature)
  • 2 cards · ➕ ability/create · added: create
  • 2 cards · ➕ ability/exchange · added: exchange (kind=activated)
  • 2 cards · ➕ ability/grant DoubleStrike · added: grant DoubleStrike (affects=parent target, duration=until end of turn, grants=grant DoubleStrike)
  • 2 cards · ➕ ability/grant Indestructible · added: grant Indestructible (affects=parent target, duration=until end of turn, grants=grant Indestructible)
  • 2 cards · ➕ ability/grant Menace · added: grant Menace (affects=parent target, duration=until end of turn, grants=grant Menace)
  • 2 cards · ➕ ability/grant Menace · added: grant Menace (affects=self, duration=until end of turn, grants=grant Menace)
  • 2 cards · ➕ ability/grant Protection · added: grant Protection (affects=self, duration=until end of turn, grants=grant Protection)
  • 2 cards · ➕ ability/grant Trample · added: grant Trample (affects=parent target, duration=until end of turn, grants=grant Trample)
  • 2 cards · ➕ ability/the · added: the
  • 1 card · ➕ ability/" · added: "
  • 1 card · ➕ ability/1—9 · added: 1—9
  • 1 card · ➕ ability/Animate · added: Animate (duration=permanent, p/t=0/0, target=you control land, types=Creature)
  • 1 card · ➕ ability/BecomePrepared · added: BecomePrepared (target=self)
  • 1 card · ➕ ability/Bounce · added: Bounce (kind=activated, target=tracked set #0)
  • 1 card · ➕ ability/Bounce · added: Bounce (target=you control creature)
  • 1 card · ➕ ability/BounceAll · added: BounceAll (filter=in graveyard zone changed this turn from Battlefield to Graveyard you control creature)
  • 1 card · ➕ ability/CastFromZone · added: CastFromZone (duration=for as long as condition, free cast=yes, target=cards exiled by source)
  • 1 card · ➕ ability/CastFromZone · added: CastFromZone (duration=for as long as condition, target=cards exiled by source)
  • 1 card · ➕ ability/CastFromZone · added: CastFromZone (free cast=yes, target=in hand you control card)
  • 1 card · ➕ ability/CastFromZone · added: CastFromZone (free cast=yes, target=parent target)
  • 1 card · ➕ ability/CastFromZone · added: CastFromZone (target=in hand card)
  • 1 card · ➕ ability/CastFromZone · added: CastFromZone (target=land)
  • 1 card · ➕ ability/ChangeTargets · added: ChangeTargets
  • 1 card · ➕ ability/ChangeZone · added: ChangeZone (conditional=# of in battlefield you control Island ≥ 1, from=graveyard, target=self, to=exile)
  • 1 card · ➕ ability/ChangeZone · added: ChangeZone (enter_tapped=Tapped, enters_under=You, from=battlefield, target=last created, to=battlefield)
  • 1 card · ➕ ability/ChangeZone · added: ChangeZone (enter_tapped=Tapped, enters_under=You, target=parent target, to=battlefield)
  • 1 card · ➕ ability/ChangeZone · added: ChangeZone (enter_tapped=Tapped, from=exile, target=parent target, to=battlefield)
  • 1 card · ➕ ability/ChangeZone · added: ChangeZone (enter_tapped=Tapped, from=graveyard, target=self, to=battlefield)
  • 1 card · ➕ ability/ChangeZone · added: ChangeZone (enter_tapped=Tapped, target=parent target, to=battlefield)
  • 1 card · ➕ ability/ChangeZone · added: ChangeZone (enter_tapped=Tapped, target=self, to=battlefield)
  • 1 card · ➕ ability/ChangeZone · added: ChangeZone (enter_tapped=Tapped, target=triggering source, to=battlefield)
  • 1 card · ➕ ability/ChangeZone · added: ChangeZone (enter_transformed=true, target=triggering source, to=battlefield)
  • 1 card · ➕ ability/ChangeZone · added: ChangeZone (enter_with_counters=[(Generic("revival"), ClampMin { inner: Offset { inner: Ref { qty: CountersOn { scope: Source, counter_type: Some(Generic("revi…
  • 1 card · ➕ ability/ChangeZone · added: ChangeZone (enter_with_counters=[(Keyword(Flying), Fixed { value: 1 })], target=triggering source, to=battlefield)
  • 1 card · ➕ ability/ChangeZone · added: ChangeZone (enter_with_counters=[(Keyword(Vigilance), Fixed { value: 1 })], from=battlefield, kind=activated, target=parent target, to=battlefield)
  • 1 card · ➕ ability/ChangeZone · added: ChangeZone (enter_with_counters=[(Plus1Plus1, Fixed { value: 1 })], enters_under=You, target=triggering source, to=battlefield)
  • 1 card · ➕ ability/ChangeZone · added: ChangeZone (enter_with_counters=[(Plus1Plus1, Fixed { value: 1 })], from=graveyard, target=self, to=battlefield)
  • 1 card · ➕ ability/ChangeZone · added: ChangeZone (enters_under=You, from=exile, target=parent target, to=battlefield)
  • 1 card · ➕ ability/ChangeZone · added: ChangeZone (enters_under=You, target=creature, to=battlefield)
  • 1 card · ➕ ability/ChangeZone · added: ChangeZone (from=battlefield, kind=activated, target=tracked set #0, to=exile)
  • 1 card · ➕ ability/ChangeZone · added: ChangeZone (from=battlefield, target=another you control creature Elf, to=exile)
  • 1 card · ➕ ability/ChangeZone · added: ChangeZone (from=battlefield, target=another you control creature Faerie, to=exile)
  • 1 card · ➕ ability/ChangeZone · added: ChangeZone (from=battlefield, target=another you control creature Goblin or shaman, to=exile)
  • 1 card · ➕ ability/ChangeZone · added: ChangeZone (from=battlefield, target=another you control creature Goblin, to=exile)
  • 1 card · ➕ ability/ChangeZone · added: ChangeZone (from=battlefield, target=another you control creature Kithkin, to=exile)
  • 1 card · ➕ ability/ChangeZone · added: ChangeZone (from=battlefield, target=another you control creature Merfolk, to=exile)
  • 1 card · ➕ ability/ChangeZone · added: ChangeZone (from=battlefield, target=another you control creature Treefolk or warrior, to=exile)
  • 1 card · ➕ ability/ChangeZone · added: ChangeZone (from=exile, target=self, to=battlefield)
  • 1 card · ➕ ability/ChangeZone · added: ChangeZone (from=graveyard, target=in graveyard creature, to=battlefield)
  • 1 card · ➕ ability/ChangeZone · added: ChangeZone (from=graveyard, target=in graveyard you control card, targets=7-7, to=exile)
  • 1 card · ➕ ability/ChangeZone · added: ChangeZone (from=graveyard, target=in graveyard you control card, to=hand)
  • 1 card · ➕ ability/ChangeZone · added: ChangeZone (from=graveyard, target=in graveyard you control enchantment, to=hand)
  • 1 card · ➕ ability/ChangeZone · added: ChangeZone (from=graveyard, target=mv referenced object's mana value- in graveyard you control permanent, targets=0-1, to=battlefield)
  • 1 card · ➕ ability/ChangeZone · added: ChangeZone (from=graveyard, target=parent target + in graveyard, to=graveyard)
  • 1 card · ➕ ability/ChangeZone · added: ChangeZone (from=graveyard, target=parent target, to=exile)
  • 1 card · ➕ ability/ChangeZone · added: ChangeZone (from=graveyard, target=scoped player controls in graveyard card, targets=0-2, to=hand)
  • 1 card · ➕ ability/ChangeZone · added: ChangeZone (from=graveyard, target=scoped player controls in graveyard creature, to=hand)
  • 1 card · ➕ ability/ChangeZone · added: ChangeZone (from=graveyard, target=tracked set #0 + in graveyard you control, to=battlefield)
  • 1 card · ➕ ability/ChangeZone · added: ChangeZone (from=graveyard, target=tracked set #0 + in graveyard you control, to=hand)
  • 1 card · ➕ ability/ChangeZone · added: ChangeZone (from=hand, target=in hand you control Construct or in hand you control Robot or in hand you control Vehicle, to=battlefield)
  • 1 card · ➕ ability/ChangeZone · added: ChangeZone (owner_library=true, target=another scoped player controls scoped player controls creature, to=library)
  • 1 card · ➕ ability/ChangeZone · added: ChangeZone (owner_library=true, target=self, to=library)
  • 1 card · ➕ ability/ChangeZone · added: ChangeZone (target=parent target, to=command zone)
  • 1 card · ➕ ability/ChangeZone · added: ChangeZone (target=self, to=library)
  • 1 card · ➕ ability/ChangeZoneAll · added: ChangeZoneAll (enter_tapped=Tapped, from=exile, target=cards exiled by source, to=battlefield)
  • 1 card · ➕ ability/ChangeZoneAll · added: ChangeZoneAll (from=exile, target=cards exiled by source, to=hand)
  • 1 card · ➕ ability/ChangeZoneAll · added: ChangeZoneAll (from=exile, target=permanent + cards exiled by source, to=battlefield)
  • 1 card · ➕ ability/ChangeZoneAll · added: ChangeZoneAll (from=graveyard, target=scoped player controls in graveyard creature, to=battlefield)
  • 1 card · ➕ ability/ChangeZoneAll · added: ChangeZoneAll (target=token, to=exile)
  • 1 card · ➕ ability/ChangeZoneAll · added: ChangeZoneAll (target=tracked set #0 matching land, to=battlefield)
  • 1 card · ➕ ability/Choose · added: Choose (choice=basic land type, kind=activated)
  • 1 card · ➕ ability/Choose · added: Choose (choice=opponent)
  • 1 card · ➕ ability/Choose · added: Choose (choice=opponent, kind=activated)
  • 1 card · ➕ ability/ChooseFromZone · added: ChooseFromZone (count=1, zone=battlefield)
  • 1 card · ➕ ability/Conjure · added: Conjure
  • 1 card · ➕ ability/CopyTokenOf · added: CopyTokenOf (attacking=yes, copies=parent target)
  • 1 card · ➕ ability/CopyTokenOf · added: CopyTokenOf (copies=token you control non-Saga)
  • 1 card · ➕ ability/CopyTokenOf · added: CopyTokenOf (copies=you control creature)
  • 1 card · ➕ ability/DamageAll · added: DamageAll (amount=1, filter=creature non-Wall)
  • 1 card · ➕ ability/DamageAll · added: DamageAll (amount=2, filter=attacking creature)
  • 1 card · ➕ ability/DamageAll · added: DamageAll (amount=2, filter=blocking creature)
  • 1 card · ➕ ability/DamageAll · added: DamageAll (amount=6, filter=opponent controls creature)
  • 1 card · ➕ ability/DamageAll · added: DamageAll (amount=event amount, filter=another creature, player_filter=All)
  • 1 card · ➕ ability/DamageEachPlayer · added: DamageEachPlayer (amount=# of Reveler, players=each player)

@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 (1)
crates/engine/src/game/coverage.rs (1)

6768-6774: 🚀 Performance & Scalability | 🔵 Trivial | 💤 Low value

Consider short-circuiting the payload scans.

visit_direct_effect_ability_payloads runs the closure for every payload. The accumulator patterns has_unimplemented_parts |= ..., supported &= ..., and found |= ... therefore recurse into every remaining payload subtree after the answer is already known. The result is correct. Only the work is wasted.

A shared early-exit helper would remove the duplication across the three sites and stop the traversal at the first match.

♻️ Proposed helper
/// Returns true as soon as a direct payload ability satisfies `pred`.
fn any_direct_effect_ability_payload(
    effect: &Effect,
    mut pred: impl FnMut(&AbilityDefinition) -> bool,
) -> bool {
    let mut found = false;
    visit_direct_effect_ability_payloads(effect, |_, payload| {
        found = found || pred(payload);
    });
    found
}
-        || {
-            let mut has_unimplemented_parts = false;
-            visit_direct_effect_ability_payloads(&def.effect, |_, payload| {
-                has_unimplemented_parts |= ability_definition_has_unimplemented_parts(payload);
-            });
-            has_unimplemented_parts
-        }
+        || any_direct_effect_ability_payload(&def.effect, ability_definition_has_unimplemented_parts)
-    let mut supported = true;
-    visit_direct_effect_ability_payloads(&def.effect, |_, payload| {
-        supported &= is_ability_supported(payload);
-    });
-    if !supported {
-        return false;
-    }
-    true
+    !any_direct_effect_ability_payload(&def.effect, |payload| !is_ability_supported(payload))

Also applies to: 7435-7441, 8261-8267

🤖 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 6768 - 6774, Add a shared
any_direct_effect_ability_payload helper that evaluates payloads with
short-circuiting and returns immediately once the predicate matches. Replace the
accumulator closures at the three affected sites around
ability_definition_has_unimplemented_parts and the supported/found checks with
this helper, preserving each site’s existing predicate and result semantics.
🤖 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 6768-6774: Add a shared any_direct_effect_ability_payload helper
that evaluates payloads with short-circuiting and returns immediately once the
predicate matches. Replace the accumulator closures at the three affected sites
around ability_definition_has_unimplemented_parts and the supported/found checks
with this helper, preserving each site’s existing predicate and result
semantics.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: bac2d1b4-e931-4b29-9ec3-bddfabcd5a6a

📥 Commits

Reviewing files that changed from the base of the PR and between d3971d9 and 50f6918.

📒 Files selected for processing (1)
  • crates/engine/src/game/coverage.rs

@matthewevans
matthewevans added this pull request to the merge queue Aug 10, 2026
Merged via the queue into main with commit 907d219 Aug 10, 2026
13 checks passed
@matthewevans
matthewevans deleted the ship/fixcoverage-traverse-direct-effect-payloads branch August 10, 2026 01:25
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.

1 participant