Skip to content

Fix Black Bolt, Inhuman King - #7011

Open
JacobWoodson wants to merge 1 commit into
phase-rs:mainfrom
JacobWoodson:card/black-bolt-inhuman-king
Open

Fix Black Bolt, Inhuman King#7011
JacobWoodson wants to merge 1 commit into
phase-rs:mainfrom
JacobWoodson:card/black-bolt-inhuman-king

Conversation

@JacobWoodson

@JacobWoodson JacobWoodson commented Aug 4, 2026

Copy link
Copy Markdown
Contributor

Summary

Fixes a parse-fidelity defect on Black Bolt, Inhuman King.

Issue: Lethal Voice: destroy target's controller parsed as You instead of the triggering opponent; "that player controls" (the opponent controlling the spell/ability) anaphor defaulted to ControllerRef::You, so it targets your own nonland permanent rather than the opponent's.

Files changed

  • C:\Users\jacob\source\repos\phase-card-runs\crates\engine\src\parser\oracle_trigger.rs
  • C:\Users\jacob\source\repos\phase-card-runs\crates\engine\src\parser\oracle_trigger_tests.rs
  • C:\Users\jacob\source\repos\phase-card-runs\crates\engine\tests\integration\black_bolt_lethal_voice_destroys_triggering_opponents_permanent.rs
  • C:\Users\jacob\source\repos\phase-card-runs\crates\engine\tests\integration\main.rs
  • C:\Users\jacob\source\repos\phase-card-runs\crates\engine\src\parser\oracle_ir\snapshots\engine__parser__oracle_ir__snapshot_tests__bonecrusher_giant_ir.snap

CR references

  • CR 115.1
  • CR 603.2e
  • CR 109.4b

Track

Developer

LLM

Model: claude-opus-4-8
Thinking: high

Tier: Frontier

Verification

  • cargo fmt --all — clean (exit 0)
  • ./scripts/check-parser-combinators.sh — clean - Gate G PASS + Gate A PASS. WindowsApps python3 stub falsely hard-fails D0 pre-check; re-ran with working msys64 python3 on PATH so Family D actually ran and passed (10/10 detector tests). Not merely skipped.
  • cargo clippy-strict — clean (exit 0, -D warnings); re-verified after the census-test edit
  • cargo test -p phase-engine — clean (exit 0) after in-loop fix: lib 18483 passed/0 failed, integration 4489 passed/0 failed. Fixed a Windows path-separator artifact in the unrelated CR-603.5 census pin test (test-only .replace('\','/') in game/engine.rs; no-op on Linux CI, pins untouched).
  • cargo export-cards data --stats --output client/public/card-data.json --sidecar-dir client/public + cp to data/card-data.json — clean (exit 0). Used --output because the literal recipe (no --output) streams main export to stdout and --sidecar-dir writes only localized card-data..json; the cp would have copied the stale sidecar. Fresh card-data.json regenerated against this branch's engine.
  • cargo coverage — clean (exit 0); Black Bolt, Inhuman King supported:true gap_count:0
  • cargo semantic-audit — clean (exit 0); Black Bolt, Inhuman King absent from all 295 flagged_cards -> 0 findings

Scope Expansion

Also updated the Bonecrusher Giant IR snapshot (metadata-only relative_player_scope, another becomes-target trigger; effect unchanged); card-data.json not hand-regenerated (gitignored, Tilt-owned).

Validation Failures

None.

CI Failures

None.

Summary by CodeRabbit

  • Bug Fixes

    • Improved handling of effects that trigger when a permanent becomes the target of a spell or ability.
    • “That player” references now correctly resolve to the player who controlled the triggering spell or ability.
    • Improved platform-independent path matching.
  • Tests

    • Added coverage for targeting triggers, life-loss effects, attack and damage triggers, and Black Bolt’s Lethal Voice ability.

@coderabbitai

coderabbitai Bot commented Aug 4, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: f153b8d4-1f2c-47a2-8a4a-7fdceab1da85

📥 Commits

Reviewing files that changed from the base of the PR and between 644c713 and 2cfa6c6.

⛔ Files ignored due to path filters (1)
  • crates/engine/src/parser/oracle_ir/snapshots/engine__parser__oracle_ir__snapshot_tests__bonecrusher_giant_ir.snap is excluded by !**/*.snap, !**/snapshots/**
📒 Files selected for processing (5)
  • crates/engine/src/game/engine.rs
  • crates/engine/src/parser/oracle_trigger.rs
  • crates/engine/src/parser/oracle_trigger_tests.rs
  • crates/engine/tests/integration/black_bolt_lethal_voice_destroys_triggering_opponents_permanent.rs
  • crates/engine/tests/integration/main.rs

📝 Walkthrough

Walkthrough

The PR normalizes relative census paths to forward slashes. It also parses becomes-target triggers as references to the triggering player and adds parser and integration regression tests for related target selection and resolution.

Changes

Census path normalization

Layer / File(s) Summary
Normalize relative census paths
crates/engine/src/game/engine.rs
Relative source paths replace backslash separators with forward slashes after string conversion.

Becomes-target trigger resolution

Layer / File(s) Summary
Parse triggering-player references
crates/engine/src/parser/oracle_trigger.rs, crates/engine/src/parser/oracle_trigger_tests.rs
The parser detects singular and plural becomes-target conditions and maps their source controller to ControllerRef::TriggeringPlayer. Tests cover destroy, attack, damage, and life-loss conditions.
Validate Black Bolt resolution
crates/engine/tests/integration/black_bolt_lethal_voice_destroys_triggering_opponents_permanent.rs, crates/engine/tests/integration/main.rs
The integration test verifies that Black Bolt’s triggered ability selects and destroys only the triggering opponent’s nonland permanent.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Possibly related PRs

Suggested labels: bug

Suggested reviewers: matthewevans

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly identifies the main change: fixing Black Bolt, Inhuman King behavior.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@github-actions github-actions Bot added the needs-maintainer AI-contribution PR requires human triage (Non-dev track or unresolved gaps) label Aug 4, 2026
@github-actions

github-actions Bot commented Aug 4, 2026

Copy link
Copy Markdown

Generated for head 2cfa6c6a44894ca925b6a9fc2246f9a50412ab54.

Parse changes introduced by this PR · 2 card(s), 1 signature(s) (baseline: main 644c7139a711)

🟡 Modified fields (1 signature)

  • 2 cards · 🔄 ability/Destroy · changed field target: you control permanent non-landtriggering player controls permanent non-land
    • Affected (first 3): Black Bolt, Inhuman King, Scalelord Reckoner

@matthewevans matthewevans added the bug Bug fix label Aug 5, 2026

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

[MED] Snapshot the targeting spell or ability's controller in GameEvent::BecomesTarget. Evidence: crates/engine/src/parser/oracle_trigger.rs:1045-1055,1219-1227 maps the targeter to ControllerRef::TriggeringPlayer, while crates/engine/src/game/targeting.rs:1662-1665 later derives that player from the current state.objects[source_id].controller; the event emitted at crates/engine/src/game/casting.rs:621-636 carries only source_id despite already receiving controller. Why it matters: if an activated ability targets Black Bolt and its source changes controllers before Lethal Voice resolves, the trigger can select the new controller's permanent instead of the player who activated the targeting ability. CR 602.2a and CR 115.1c (verified in docs/MagicCompRules.txt) establish that the activated ability's controller and targets are set during activation. Suggested fix: capture the controller in GameEvent::BecomesTarget at emit_targeting_events, resolve this anaphor from that snapshot, and add a runtime regression test for an activated ability followed by source control change; the existing test covers only a spell without control change.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Bug fix needs-maintainer AI-contribution PR requires human triage (Non-dev track or unresolved gaps)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants