fix(engine): evaluate 'attacks alone' relative to the attacker, not the ability source#5378
Conversation
…he ability source Closes phase-rs#5328. TriggerCondition::MinCoAttackers counted co-attackers by excluding the ability SOURCE (source_id) from the tally. 'Attacks alone' is encoded as Not(MinCoAttackers{minimum:1}); when a non-attacking observer (Exalted / Agent 13, Sharon Carter) stays back and a DIFFERENT creature attacks alone, source_id is not in combat, so excluding it is a no-op. The lone attacker stays counted, MinCoAttackers returns true, and Not(...) makes the 'attacks alone' ability silently fail to fire. Exclude the creature that fired the trigger instead: read the single attacker named by the per-attacker AttackersDeclared event (produced by matching_attack_events and re-checked at resolution, CR 603.4), falling back to source_id when the event names no single attacker (self-referential paths, unchanged). source_id still resolves the filter's source object so Training's power-relative co-attacker comparison (CR 702.149a) is unaffected. CR 506.5 (attacks alone = only creature declared) + CR 508.1a + CR 603.2c + CR 702.83b (Exalted). Adds tests/attacks_alone_observer_5328.rs pinning both the fire-when-another-attacks-alone case (was 0 clues, now 1) and the two-attackers negative control.
|
🚨 Contributor flagged. Click here for more info: Superagent Dashboard |
There was a problem hiding this comment.
Code Review
This pull request adds a new integration test file attacks_alone_observer_5328.rs to verify the "attacks alone" mechanic (Issue #5328). The tests ensure that the mechanic is evaluated relative to the attacking creature rather than the ability's source, verifying that the trigger fires correctly when a different creature attacks alone and does not fire when multiple creatures attack. There are no review comments, so I have no feedback to provide.
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.
Parse changes introduced by this PR✓ No card-parse changes detected. |
matthewevans
left a comment
There was a problem hiding this comment.
[MED] Move this regression into the shared integration test binary instead of adding a new top-level engine test file. Evidence: crates/engine/tests/attacks_alone_observer_5328.rs:1 adds a new file directly under crates/engine/tests/, while the repo's current engine-test rule requires new integration tests under crates/engine/tests/integration/ with a mod entry in crates/engine/tests/integration/main.rs (see the existing adjacent issue_5159_attacks_alone_investigate entry at crates/engine/tests/integration/main.rs:426). Why it matters: each top-level file becomes a separate engine test binary, increasing link/build cost and bypassing the shared integration harness this repo uses for new engine regressions. Suggested fix: move this test to crates/engine/tests/integration/issue_5328_attacks_alone_observer.rs, add mod issue_5328_attacks_alone_observer; to crates/engine/tests/integration/main.rs, and reuse the shared super::rules::run_combat/imports where practical.
I ignored the contributor-trust signal as requested. The runtime seam itself looks right to me: matching_attack_events expands non-batched Attacks triggers into singleton attacker events before MinCoAttackers is checked, and the filter still uses source_id for Training's source-relative power comparison. Separately, GitHub currently reports the branch as DIRTY/CONFLICTING, so it will also need to be brought current before enqueue.
matthewevans
left a comment
There was a problem hiding this comment.
Maintainer update resolved the main merge conflict and moved the new engine coverage into the existing integration test harness. The attacker-relative MinCoAttackers logic now preserves source-relative filter evaluation. CI is still pending on the updated head.
Tier: Frontier
Model: claude-opus-4-8
Thinking: high
Closes #5328.
Summary
The "attacks alone" mechanic silently failed to fire when the observing permanent stayed back and a different creature attacked alone.
TriggerCondition::MinCoAttackerstallied co-attackers by excluding the ability source (source_id). "Attacks alone" is encoded asNot(MinCoAttackers { minimum: 1 }). When a non-attacking observer (an Exalted-style card — the issue's Agent 13, Sharon Carter / Black Widow Double Agent) is the ability source but is not in combat, excludingsource_idfrom the tally is a no-op — so the lone attacker stays counted,MinCoAttackersreturnstrue, andNot(...)reports "not alone." The ability never fires.Fix: exclude the creature that actually fired the trigger, not the ability source. The per-attacker
AttackersDeclaredevent (produced bymatching_attack_events, and re-checked at resolution per CR 603.4) names exactly the triggering attacker; use it, falling back tosource_idwhen the event names no single attacker (self-referential trigger paths — behavior unchanged).source_idstill resolves thefilter's source object, so Training's power-relative co-attacker comparison (CR 702.149a) is unaffected.Reproduction (confirmed failing-before / passing-after)
A four-board probe isolates exactly one wrong quadrant on
main:mainThe new test
other_creature_attacks_alone_fires_even_when_observer_stays_backis the regression pin (0 before this change, 1 after);two_other_creatures_attacking_do_not_fire_attacks_aloneis the negative control.Anchored on
crates/engine/src/game/triggers.rs— theTriggerCondition::MinCoAttackersarm incheck_trigger_condition: the co-attacker filter previously reada.object_id != source_id, now excludes the event's triggering attacker.AttackersDeclared { attacker_ids, .. }event (frommatching_attack_events, re-passed at resolution-time re-check per CR 603.4) — the source of the single triggering-attacker id.Verification
cargo fmt --all— clean.cargo clippy -p engine --tests -- -D warnings— clean.cargo test -p engine --test attacks_alone_observer_5328— 2/2 pass (fail-on-revert).cargo test -p engine --lib -- game::triggers::— 353 passed; fullintegrationsuite — 1823 passed. Pre-existing related tests still green:issue_5159_attacks_alone_investigate,training_condition_counts_only_higher_power_coattackers,granted_ability_self_binding.CR verified against
docs/MagicCompRules.txt: 506.5, 508.1a, 603.2c, 603.4, 702.83b, 702.149a.Scope Expansion
None.