feat(engine): register CantBeBlockedUnlessAllBlock in static registry#5645
Conversation
Wire the nullary StaticMode::CantBeBlockedUnlessAllBlock into build_static_registry() as a handle_rule_mod entry (mirrors Goaded, CantCrew, and other passive combat-restriction markers). Add the text-anchor arm in coverage.rs so the coverage classifier recognises the variant as supported. Add a regression test that asserts zero coverage gaps for a face carrying this static with the Tromokratis oracle text. The runtime enforcement (combat.rs declare-blockers validation) and parser (oracle_static/evasion.rs) already exist; the integration tests in tromokratis.rs (already registered in main.rs) exercise the end-to-end path. This commit closes the last remaining coverage gap for Tromokratis. CR: 509.1b
|
Warning You have reached your daily quota limit. Please wait up to 24 hours and I will start processing your requests again! |
Parse changes introduced by this PR · 1 card(s), 1 signature(s) (baseline: main
|
|
Clean — approve on settled green. This is a coverage false-negative fix, and the diagnosis is right. ✅ Clean
🟡 Non-blocking
Recommendation: approve and enqueue once CI settles green. Required checks ( |
matthewevans
left a comment
There was a problem hiding this comment.
Approved — coverage false-negative fix, verified runtime-inert.
✅ Clean
- Right seam.
handle_rule_modis the established building block for nullary registry-keyed statics (~20 siblings register through it). No new machinery, no new enum surface. - Runtime-inert by construction — no double-apply risk.
static_registryis read only incoverage.rs. Combat enforcement of this static runs throughstatic_kind_present(combat.rs:755) and a directdef.modescan (combat.rs:1591), neither of which consults the registry.StaticEffect::RuleModificationis constructed only instatic_abilities.rsand consumed nowhere — it is a pure marker. Registering the handler therefore cannot change combat behavior; it only corrects the coverage classification. - CR 509.1b grep-verified and governing (
docs/MagicCompRules.txt:2345): "The defending player checks each creature they control to see whether it's affected by any restrictions (effects that say a creature can't block, or that it can't block unless some condition is met)." Tromokratis is precisely the unless-condition case. - Discriminating test.
cant_be_blocked_unless_all_block_has_no_coverage_gapfails onmain, where the registry lacks the key andcard_face_gapsreports a gap. - Parse-diff tight and fresh. 1 card (Tromokratis),
static/CantBeBlockedUnlessAllBlocksupportfalse → true, baselinemain 9c8de549a93a, zero collateral. Diff is purely additive (+33/-0).
Labeled bug rather than enhancement despite the feat(engine): title: nothing new becomes possible at runtime — Tromokratis already worked end-to-end while coverage reported it an unsupported gap. This fixes the false negative. Labels classify behavior, not author intent.
Summary
Closes the last remaining coverage gap for Tromokratis by registering
StaticMode::CantBeBlockedUnlessAllBlockin the static-abilities registry and wiring the coverage classifier to recognise it as a supported variant.Card
Tromokratis — {5}{U}{U} Legendary Creature — Kraken (8/8 )
What already existed
oracle_static/evasion.rs:1769—parse_block_unless_all_block_nomcombat.rs:1574–1655— declare-blockers validationtromokratis.rs(6 tests) inmain.rs:906What this PR adds
static_abilities.rsCantBeBlockedUnlessAllBlockinbuild_static_registry()as ahandle_rule_modentry (nullary marker, same pattern as Goaded/CantCrew)coverage.rsStaticMode::CantBeBlockedUnlessAllBlock => effective_lower.contains("can't be blocked")coverage.rscant_be_blocked_unless_all_block_has_no_coverage_gap()asserting zero gaps for a face carrying this static with the Tromokratis oracle textDesign rationale
CantBeBlockedUnlessAllBlockis a nullary (no payload)StaticModevariant. Its runtime semantics are entirely handled bycombat.rsdeclare-blockers validation — the registry entry only needs to behandle_rule_mod(the no-op handler that marks the variant as "known and supported" for the layer system). This mirrors the pattern used forGoaded,CantCrew,MustAttack, and other passive combat-restriction markers.The coverage classifier uses the
"can't be blocked"text anchor (same asCantBeBlocked,CantBeBlockedExceptBy,CantBeBlockedBy) since all four share the same oracle-text prefix. The "unless all creatures" clause specificity is validated by the parser unit tests.Testing
cant_be_blocked_unless_all_block_has_no_coverage_gap— constructs a synthetic face with the Tromokratis oracle text and assertscard_face_gaps()returns emptytromokratis.rsexercising the full combat pathChecklist
cargo check --lib -p engine— zero errors/warningsrustfmt --check— cleanscripts/check-parser-combinators.sh— Gate A PASSunwrap()/expect()in non-test codetodo!()/unimplemented!()in non-test codeCR: 509.1b