Skip to content

fix: gate attacks-alone triggers on zero co-attackers (closes #5159)#5162

Open
RealDiligent wants to merge 1 commit into
phase-rs:mainfrom
RealDiligent:fix/issue-5159-attacks-alone-trigger-gate
Open

fix: gate attacks-alone triggers on zero co-attackers (closes #5159)#5162
RealDiligent wants to merge 1 commit into
phase-rs:mainfrom
RealDiligent:fix/issue-5159-attacks-alone-trigger-gate

Conversation

@RealDiligent

Copy link
Copy Markdown
Contributor

Summary

  • Parse "attacks alone" on attack triggers into TriggerCondition::MinCoAttackers { minimum: 0 } (CR 508.3a — zero co-attackers).
  • Wire the same condition through mtgish-import WhenACreatureAttacksAlone.
  • Add integration tests for Agent 13, Sharon Carter: solo attack investigates once; multi-creature attack does not.

Closes #5159.

Test plan

  • trigger_samurai_or_warrior_attacks_alone parser unit test
  • issue_5159_solo_attack_investigates_once
  • issue_5159_multi_attack_does_not_investigate
  • CI green

…rs#5159)

Parse "attacks alone" into MinCoAttackers { minimum: 0 } so Sharon Carter /
Exalted-class triggers fire only when a single creature attacks.

Co-authored-by: Cursor <[email protected]>

@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 "attacks alone" trigger qualifier by encoding it as a zero-co-attacker gate (TriggerCondition::MinCoAttackers { minimum: 0 }) in both the native parser and the mtgish-import converter, accompanied by integration tests. The review feedback identifies violations of R1 due to using standard string methods (strip_prefix) instead of nom combinators in the parser, as well as violations of R6 regarding missing or incorrect CR citations (citing CR 508.3a instead of CR 506.5).

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 +7995 to +8004
/// CR 508.3a: Strip a trailing " alone" qualifier from attack trigger event
/// text ("attacks alone", Exalted / Sharon Carter class). The co-attacker gate
/// is encoded separately as `TriggerCondition::MinCoAttackers { minimum: 0 }`.
fn strip_attack_alone_qualifier(after: &str) -> (bool, &str) {
if let Some(rest) = after.strip_prefix(" alone") {
(true, rest)
} else {
(false, after)
}
}

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

[MEDIUM] Use nom combinators and correct CR rule citation for "attacks alone". Evidence: crates/engine/src/parser/oracle_trigger.rs:7995.
Why it matters: R1 requires all new parser dispatch to use nom combinators rather than standard string methods like strip_prefix, and R6 requires verified CR annotations (CR 506.5 defines "attacks alone", not CR 508.3a). Suggested fix: Rewrite the helper using nom's tag combinator and update the CR citation.

Suggested change
/// CR 508.3a: Strip a trailing " alone" qualifier from attack trigger event
/// text ("attacks alone", Exalted / Sharon Carter class). The co-attacker gate
/// is encoded separately as `TriggerCondition::MinCoAttackers { minimum: 0 }`.
fn strip_attack_alone_qualifier(after: &str) -> (bool, &str) {
if let Some(rest) = after.strip_prefix(" alone") {
(true, rest)
} else {
(false, after)
}
}
/// CR 506.5: Strip a trailing " alone" qualifier from attack trigger event
/// text ("attacks alone", Exalted / Sharon Carter class). The co-attacker gate
/// is encoded separately as TriggerCondition::MinCoAttackers { minimum: 0 }.
fn strip_attack_alone_qualifier(after: &str) -> (bool, &str) {
if let Ok((rest, _)) = nom::bytes::complete::tag::<_, _, nom::error::Error<&str>>(" alone")(after) {
(true, rest)
} else {
(false, after)
}
}
References
  1. R1. Nom combinators on the first pass — no exceptions. Every new parser dispatch under crates/engine/src/parser/ must use nom 8.0 combinators or delegate to existing helpers. (link)
  2. R6. CR annotations are mandatory and verified. Every rules-touching line of engine code must carry a comment of the form CR : . (link)
  3. Avoid verbatim string equality for parsing Oracle phrases as it bypasses the robust nom-based parser and creates fragile matches. Instead, decompose compound phrases into modular, reusable parsers for constituent parts (e.g., subjects, conjunctions) and compose them using idiomatic combinator aggregates (like nested alt and tag sequences) to prevent combinatorial explosion and improve maintainability.

Comment on lines +8365 to +8370
if attacks_alone {
def.condition = Some(TriggerCondition::MinCoAttackers {
minimum: 0,
filter: None,
});
}

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

[MEDIUM] Missing mandatory CR annotation on rules-touching code. Evidence: crates/engine/src/parser/oracle_trigger.rs:8365.
Why it matters: R6 requires every rules-touching line of engine code to carry a comment of the form CR <number>: <description> to ensure compliance and traceability. Suggested fix: Add a comment citing CR 506.5 above the condition block.

Suggested change
if attacks_alone {
def.condition = Some(TriggerCondition::MinCoAttackers {
minimum: 0,
filter: None,
});
}
if attacks_alone {
// CR 506.5: A creature attacks alone if it's the only creature declared as an attacker.
def.condition = Some(TriggerCondition::MinCoAttackers {
minimum: 0,
filter: None,
});
}
References
  1. R6. CR annotations are mandatory and verified. Every rules-touching line of engine code must carry a comment of the form CR : . (link)

Comment on lines 575 to +577
// CR 508.3a: "Whenever [a creature] attacks alone" — same firing axis
// as a regular attack trigger; the "alone" qualifier (single-attacker
// batch) has no engine field today. Mirrors native parser at
// oracle_trigger.rs:8273 (collapses to `TriggerMode::Attacks`).
// as a regular attack trigger; the "alone" qualifier is a
// zero-co-attacker gate via `MinCoAttackers { minimum: 0 }`.

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

[MEDIUM] Incorrect CR rule citation for "attacks alone". Evidence: crates/mtgish-import/src/convert/trigger.rs:575.
Why it matters: CR 508.3a describes general attack triggers, whereas CR 506.5 is the specific rule that defines what it means for a creature to "attack alone". Suggested fix: Update the comment to reference CR 506.5.

Suggested change
// CR 508.3a: "Whenever [a creature] attacks alone" — same firing axis
// as a regular attack trigger; the "alone" qualifier (single-attacker
// batch) has no engine field today. Mirrors native parser at
// oracle_trigger.rs:8273 (collapses to `TriggerMode::Attacks`).
// as a regular attack trigger; the "alone" qualifier is a
// zero-co-attacker gate via `MinCoAttackers { minimum: 0 }`.
// CR 506.5: "Whenever [a creature] attacks alone" — same firing axis
// as a regular attack trigger; the "alone" qualifier is a
// zero-co-attacker gate via MinCoAttackers { minimum: 0 }.
References
  1. R6. CR annotations are mandatory and verified. Every rules-touching line of engine code must carry a comment of the form CR : . (link)

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

Thanks for taking this on, but the current encoding does not implement the attacks alone gate.

TriggerCondition::MinCoAttackers is an at-least condition: the runtime counts same-controller co-attackers and checks co_attacker_count >= minimum. Setting minimum: 0 therefore always passes, including when the source attacks with one or more other creatures. That means the new parser/converter shape still allows the Agent 13 trigger to fire during multi-creature attacks, which is the bug this PR is meant to fix.

Rules evidence: CR 506.5 defines a creature as attacking alone only when it is attacking and no other creatures are. Please model this as an exact/maximum zero co-attacker gate, or add an appropriate parameterized condition shape, and keep the multi-attacker regression covering that runtime behavior. Also note the PR is currently DIRTY against base and still needs the parse-diff evidence before it can be considered for approval.

@matthewevans matthewevans added the bug Bug fix label Jul 6, 2026
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.

[Card Bug] Agent 13, Sharon Carter triggering when attacking with multiple creates

2 participants