Skip to content

fix(coverage): expose GrantAll{Activated,Triggered}AbilitiesOf source in the parse-diff signature - #5585

Merged
matthewevans merged 1 commit into
phase-rs:mainfrom
e11734937-beep:cov-grant-all-source
Jul 11, 2026
Merged

fix(coverage): expose GrantAll{Activated,Triggered}AbilitiesOf source in the parse-diff signature#5585
matthewevans merged 1 commit into
phase-rs:mainfrom
e11734937-beep:cov-grant-all-source

Conversation

@e11734937-beep

Copy link
Copy Markdown
Contributor

fmt_modification rendered GrantAllActivatedAbilitiesOf / GrantAllTriggeredAbilitiesOf as the bare labels grant all activated abilities of / grant all triggered abilities of, swallowing their source filter (and cap) with ...

So a parser change to which permanents' abilities are granted — the source filter — produces a removal with no compensating addition in the coverage-parse-diff sticky: the false signature of a regression, when it's really a half-rendered modification. This is the same class as #5492 (PreventDamage), #5495 (ChangeZone), #5501 (ChangeZoneAll), and #5507 (Mana grants).

Fix

Expose the source filter via the existing fmt_target, and the cap only when set so unqualified signatures stay byte-identical (mirroring #5511). Fully destructured (no ..) so a new GrantAll* field is a compile error, not another silent omission.

Test

grant_all_abilities_signature_exposes_source — asserts the source filter appears in both signatures and that two different source filters produce distinct signatures (rather than one shared bare label).

Full engine lib: 16167 passed, 0 failed; fmt / parser-combinator gate / clippy -D warnings all clean.

@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 updates fmt_modification in crates/engine/src/game/coverage.rs to expose the source filter (and cap when present) for GrantAllActivatedAbilitiesOf and GrantAllTriggeredAbilitiesOf modifications, ensuring distinct signatures are generated. A corresponding unit test was added to verify this behavior. The reviewer suggests expanding the test coverage to explicitly assert that the cap field is also correctly exposed when present.

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 +10495 to +10521
use crate::types::ability::{ContinuousModification, TargetFilter};

let act = |source: TargetFilter| {
fmt_modification(&ContinuousModification::GrantAllActivatedAbilitiesOf {
source,
cap: None,
})
};
let trg = |source: TargetFilter| {
fmt_modification(&ContinuousModification::GrantAllTriggeredAbilitiesOf { source })
};

// The source filter must appear in each signature ...
assert!(
act(TargetFilter::Controller).contains(&fmt_target(&TargetFilter::Controller)),
"activated-grant signature must expose its source filter",
);
assert!(
trg(TargetFilter::SelfRef).contains(&fmt_target(&TargetFilter::SelfRef)),
"triggered-grant signature must expose its source filter",
);
// ... so different source filters produce distinct signatures, not one bare label.
assert_ne!(
act(TargetFilter::Controller),
act(TargetFilter::SelfRef),
"different source filters must produce different activated-grant signatures",
);

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

[MED] Incomplete test coverage for the cap field of GrantAllActivatedAbilitiesOf. Evidence: crates/engine/src/game/coverage.rs:10495.
Why it matters: The test asserts that the source filter is exposed in the signature, but does not verify that the cap field is also correctly exposed when present, leaving a potential blind spot for future modifications.
Suggested fix: Update the test to assert that the cap field is exposed in the signature when present.

        use crate::types::ability::{ActivationRestriction, ContinuousModification, TargetFilter};

        let act = |source: TargetFilter, cap: Option<ActivationRestriction>| {
            fmt_modification(&ContinuousModification::GrantAllActivatedAbilitiesOf {
                source,
                cap,
            })
        };
        let trg = |source: TargetFilter| {
            fmt_modification(&ContinuousModification::GrantAllTriggeredAbilitiesOf { source })
        };

        // The source filter must appear in each signature ...
        assert!(
            act(TargetFilter::Controller, None).contains(&fmt_target(&TargetFilter::Controller)),
            "activated-grant signature must expose its source filter",
        );
        assert!(
            trg(TargetFilter::SelfRef).contains(&fmt_target(&TargetFilter::SelfRef)),
            "triggered-grant signature must expose its source filter",
        );
        // ... so different source filters produce distinct signatures, not one bare label.
        assert_ne!(
            act(TargetFilter::Controller, None),
            act(TargetFilter::SelfRef, None),
            "different source filters must produce different activated-grant signatures",
        );
        // ... and the cap must be exposed when present.
        assert!(
            act(TargetFilter::Controller, Some(ActivationRestriction::OnlyOnceEachTurn))
                .contains("OnlyOnceEachTurn"),
            "activated-grant signature must expose its cap when present",
        );

@github-actions

Copy link
Copy Markdown

Parse changes introduced by this PR · 16 card(s), 13 signature(s) (baseline: main 400a18223a92)

4 card(s) · static/Continuous · field mods: grant all activated abilities ofgrant all activated abilities of creature + cards exiled by source

Examples: Agatha's Soul Cauldron, Dark Impostor, Hazel's Brewmaster (+1 more)

2 card(s) · static/Continuous · field mods: grant all activated abilities ofgrant all activated abilities of cards exiled by source

Examples: Myr Welder, Territory Forge

1 card(s) · static/Continuous · field mods: grant all activated abilities ofgrant all activated abilities of 1+ P1P1 counters creature + not self

Examples: Experiment Kraj

1 card(s) · static/Continuous · field mods: grant all activated abilities ofgrant all activated abilities of cards exiled by source (cap OnlyOnceEachTurn)

Examples: Locus of Enlightenment

1 card(s) · static/Continuous · field mods: grant all activated abilities ofgrant all activated abilities of in battlefield land

Examples: Manascape Refractor

1 card(s) · static/Continuous · field mods: grant all activated abilities ofgrant all activated abilities of in graveyard creature

Examples: Necrotic Ooze

1 card(s) · static/Continuous · field mods: grant all activated abilities ofgrant all activated abilities of in graveyard land

Examples: Mirran Safehouse

1 card(s) · static/Continuous · field mods: grant all activated abilities ofgrant all activated abilities of legendary in battlefield you control creature

Examples: Robaran Mercenaries

1 card(s) · static/Continuous · field mods: grant all activated abilities ofgrant all activated abilities of not same name you control creature

Examples: Marvin, Murderous Mimic

1 card(s) · static/Continuous · field mods: grant all activated abilities ofgrant all activated abilities of you control in graveyard artifact

Examples: Trazyn the Infinite

1 card(s) · static/Continuous · field mods: grant all activated abilities of, grant all triggered abilities ofgrant all activated abilities of last chosen card, grant all triggered abilities of last chosen card

Examples: Koh, the Face Stealer

1 card(s) · ability/grant all activated abilities of · removed: grant all activated abilities of (affects=self, grants=grant all activated abilities of)

Examples: Drana and Linvala

1 card(s) · ability/grant all activated abilities of in battlefield opponent controls creature · added: grant all activated abilities of in battlefield opponent controls creature (affects=self, grants=grant all activated abilities of in battlefield opponent contr…

Examples: Drana and Linvala

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

Approve — right seam, minimal blast radius, and the test actually discriminates. Fifth in the established fmt_modification blind-spot series (#5492 PreventDamage, #5495 ChangeZone, #5501 ChangeZoneAll, #5507 Mana, #5511 cap-when-set), and it follows the pattern faithfully.

✅ Clean

  • The fix is at the seam, not around it. GrantAllActivatedAbilitiesOf / GrantAllTriggeredAbilitiesOf rendered bare labels, swallowing source with ... A parser change to which permanents' abilities are granted therefore produced a removal with no compensating addition in the sticky — the signature of a regression, when it was really a half-rendered modification. Exposing source through the existing fmt_target reuses the building block instead of hand-rolling a formatter.
  • Fully destructured — { source, cap }, no ... This is the load-bearing detail: a future field added to GrantAll* is now a compile error rather than another silent omission. That's what stops this bug class from recurring a sixth time, and it's why the fix is worth more than the four lines it changes.
  • cap rendered only when Some, so unqualified signatures stay byte-identical and this doesn't churn the coverage baseline for cards that never set a cap.
  • grant_all_abilities_signature_exposes_source is discriminating. The assert_ne! on two different source filters would fail on the pre-fix code — both rendered the identical bare label. It tests the building block's behavior across its input range rather than replaying one card.
  • All 13 checks green; 1 file, +48/−4.

Approving and enqueueing as bug.

@matthewevans matthewevans added the bug Bug fix label Jul 11, 2026
@matthewevans
matthewevans added this pull request to the merge queue Jul 11, 2026
Merged via the queue into phase-rs:main with commit 6cee23e Jul 11, 2026
13 checks passed
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.

2 participants