fix(engine): don't force a MustAttack carrier to attack itself (#5952) - #6296
Conversation
|
Caution The consumer version of Gemini Code Assist on GitHub has been sunset. All code review activity has officially ceased. |
|
🚨 Contributor flagged. Click here for more info: Superagent Dashboard |
Parse changes introduced by this PR✓ No card-parse changes detected. |
matthewevans
left a comment
There was a problem hiding this comment.
Request changes — the runtime predicate is corrected, but the declaration authority still reintroduces the same unscoped local-static test.
🔴 Blocker
crates/engine/src/game/combat.rs:3341 still treats any MustAttack carried by the candidate as a requirement via active_static_definitions(...).any(|sd| sd.mode == StaticMode::MustAttack), without applying sd.affected. The PR correctly removes that predicate from creature_must_attack_with_attackable_players_gated, but AttackDeclarationConstraints::build feeds both validate_attack_declaration (combat.rs:3877-3925) and AI completion, so an empty declaration in Fumiko's controller's combat still has required == 1 and is rejected. The current regression only calls creature_must_attack; it does not exercise this production declaration path.
Evidence: Fumiko's Oracle text is “Creatures your opponents control attack each combat if able.” The scoped affected filter must therefore be applied before adding MustAttackGeneric; the unfiltered local arm is exactly the old carrier-vs-beneficiary bug at a second enforcement seam.
Please replace the active_static_definitions local arm in AttackDeclarationConstraints::build with the same check_static_ability(..., static_target_ctx(cid)) applicability query used by this patch, and add a registered runtime regression through validate_attack_declaration/the declare-attackers action showing that Fumiko and her controller's other creature can be omitted during P0's combat while P1's creature remains required during P1's combat.
✅ Clean
The changed creature_must_attack_with_attackable_players_gated and source collector do use check_static_ability / check_static_ability_sources, and the parse-diff artifact for this head reports no card-parse changes.
Recommendation: request changes; update the remaining declaration/AI constraint seam and cover the real validator before re-review.
|
@coderabbitai review |
✅ Action performedReview finished.
|
|
Warning Review limit reached
Next review available in: 32 seconds Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available. How can I continue?After more reviews become available, a review can be triggered using the 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 configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (1)
📝 WalkthroughWalkthroughCombat must-attack source reporting and creature eligibility now share gated ChangesMust-Attack Gating
Estimated code review effort: 3 (Moderate) | ~20 minutes Suggested reviewers: 🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Comment |
Co-authored-by: andriypolanski <andriypolanski@users.noreply.github.com>
matthewevans
left a comment
There was a problem hiding this comment.
Approved after current-head review. The MustAttack applicability check now consistently uses the scoped static-ability authority, and the registered regression covers both predicate and declaration enforcement.
Closes #5952
Summary
Discord report: Fumiko the Lowblood appears to "goad" herself — her static "Creatures your opponents control attack each combat if able." was forcing Fumiko (a creature you control) to attack.
The parse is correct: the static is a
MustAttackwithaffected: Typed { controller: Opponent }. The bug is in runtime enforcement, not the AST.Root cause: the must-attack logic had a
has_local_must_attackfast-path that reported a requirement for any creature merely carrying aMustAttackstatic — checking onlysd.mode == StaticMode::MustAttackand ignoring the static'saffectedfilter. Fumiko carries an opponent-scopedMustAttack, so she satisfied "local must-attack" against herself (CR 508.1d + CR 109.5).Fix:
check_static_ability/check_static_ability_sourcesare already the single authority — they run the full applicability predicate (affectedfilter, condition, per-player gates) against each carrier and return the carrier id, correctly handling both an intrinsic self static (Juggernaut'sSelfRef) and a remote scope (Fumiko'sOpponent). Remove the redundant, buggyhas_local_must_attackduplicate and route both call sites through that authority, dropping the now-unusedobjparameter frommust_attack_sources_gated.Changes
crates/engine/src/game/combat.rs— deletehas_local_must_attack; the enforcement bool (creature_must_attack_with_attackable_players_gated) and the source collector (must_attack_sources_gated) now rely solely oncheck_static_ability/check_static_ability_sources, which honor the static'saffectedfilter. Drop the unusedobjparameter frommust_attack_sources_gated.crates/engine/tests/integration/fumiko_the_lowblood_5952.rs(+main.rsmod) — runtime regression through the real parse → layer → combat-enforcement pipeline: Fumiko and her controller's other creatures are NOT forced during their own combat, while a creature an opponent of Fumiko's controller controls IS forced (positive control).Test Plan
cargo fmt --all -- --checkcargo clippy -p engine --all-targets --features engine/proptest -- -D warningscargo test -p engine(full suite: 21,174 pass)cargo test -p engine --test integration fumikocargo test -p engine --test integration curse_of_the_nightly_hunt_must_attack(sibling cross-permanent MustAttack, still green)Out of scope
The classifier also noted local card data collapses "Bushido X" to
Bushido 1. That is a separate parser issue explicitly scoped out of this report and is not addressed here.Summary by CodeRabbit
Bug Fixes
Tests