Skip to content

refactor(parser): retain generic activated abilities as native IR - #6792

Merged
matthewevans merged 4 commits into
mainfrom
wt/p05b-t10b
Jul 29, 2026
Merged

refactor(parser): retain generic activated abilities as native IR#6792
matthewevans merged 4 commits into
mainfrom
wt/p05b-t10b

Conversation

@matthewevans

@matthewevans matthewevans commented Jul 29, 2026

Copy link
Copy Markdown
Member

Summary

  • adds an inert lowering-shell stage for activated mana-normalization before existing cost-reduction and mana-spend extraction
  • routes generic and ability-word-prefixed activated abilities through native AbilityIr
  • preserves activation envelopes, printed slots, fallback diagnostics, and terminal d20 result tables

Verification

  • cargo fmt --all --check
  • parser combinator / classifier gates (G and A): PASS
  • skill-doc and prelowered-ratchet gates: PASS
  • focused IR snapshots: 118 passed
  • cargo clippy --all-targets -- -D warnings: PASS
  • full-pool card-data parity: identical SHA-256 and cmp exit 0

Witnesses

  • Thespian’s Stage preserves copied text and printed slot 1
  • Barbarian Ring covers ability-word routing
  • Component Pouch covers d20 table attachment and nonterminal-roll rejection

The full-pool exports used the same checked AtomicCards input; neither generated export is committed.

Summary by CodeRabbit

  • Bug Fixes
    • Improved activated-ability parsing and IR routing, including labeled abilities, self-references, die-roll handling, and copy/activation restrictions.
    • More reliable fallback behavior for ambiguous or unsupported text, with reduced chances of residual unimplemented effects.
    • Added consistent normalization for “activated mana instead” within the activated-ability shell pipeline.
  • Tests
    • Added/expanded snapshot coverage for activated abilities across multiple complex scenarios.
    • Updated validation to follow the improved activated-ability parsing and lowering flow.

@matthewevans
matthewevans enabled auto-merge July 29, 2026 17:51
@coderabbitai

coderabbitai Bot commented Jul 29, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Warning

Review limit reached

@matthewevans, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 28 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

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 configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 0c9272b3-6ee3-4460-b924-418c2257cf96

📥 Commits

Reviewing files that changed from the base of the PR and between e29fd85 and 4aac78a.

📒 Files selected for processing (3)
  • crates/engine/src/parser/oracle.rs
  • crates/engine/src/parser/oracle_ir/snapshot_tests.rs
  • crates/engine/src/parser/oracle_tests.rs
📝 Walkthrough

Walkthrough

Activated-ability parsing now builds and emits AbilityIr, applies activation metadata through shell stages, and supports IR-based self-reference fallback parsing. New snapshots cover copy retention, ability-word activations, die-result tables, and subsequent activations.

Changes

Activated ability IR migration

Layer / File(s) Summary
IR routing and shell construction
crates/engine/src/parser/oracle.rs, crates/engine/src/parser/oracle_ir/context.rs, crates/engine/src/parser/oracle_ir/effect_chain.rs
Activated ability dispatch now constructs AbilityIr, populates shell fields, derives activation zones, and emits through ability_ir_at.
Shell-stage lowering
crates/engine/src/parser/oracle.rs, crates/engine/src/parser/oracle_effect/mod.rs, crates/engine/src/parser/oracle_ir/effect_chain.rs
Activated mana normalization, cost reduction extraction, and mana-spend trigger extraction run through shell-stage processing.
Self-reference fallback and diagnostics
crates/engine/src/parser/oracle.rs, crates/engine/src/parser/oracle_pipeline_snapshot_tests.rs
The fallback helper now retries with normalized self-references using AbilityIr, evaluates lowered results, filters target-fallback diagnostics, and preserves failures.
IR-native snapshot coverage
crates/engine/src/parser/oracle_ir/snapshot_tests.rs
Snapshots cover generic and ability-word activations, terminal die tables, nonterminal die rolls, and recursive unimplemented-effect checks.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant OracleParser
  participant ActivatedAbilityIR
  participant ShellStages
  participant Lowerer
  participant Emitter
  OracleParser->>ActivatedAbilityIR: parse activated ability
  ActivatedAbilityIR->>ShellStages: configure normalization and extraction stages
  OracleParser->>Lowerer: lower AbilityIr for guards and fallback checks
  Lowerer-->>OracleParser: lowered ability and diagnostics
  OracleParser->>Emitter: emit ability_ir_at(item_line, ir)
Loading

Possibly related PRs

Suggested labels: quality

🚥 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 accurately summarizes the main change: activated abilities are now retained as native IR during parsing.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
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 docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch wt/p05b-t10b

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

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

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
crates/engine/src/parser/oracle.rs (1)

8474-8512: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

normalize_activated_mana_instead_delta silently drops a non-ConditionInstead sub-ability condition, and this function is now reachable much more broadly.

At line 8488, sub.condition.take() unconditionally clears sub.condition. When the pattern match against AbilityCondition::ConditionInstead fails (i.e. sub.condition was Some(some_other_condition)), the else { return; } branch returns without restoring it — the condition is permanently lost. Contrast with the two later failure branches in this same function (lines 8502 and 8508), which correctly restore sub.condition = Some(AbilityCondition::ConditionInstead { inner }) before returning.

This function's body is unchanged by this PR, but its visibility just widened to pub(crate) specifically so it can be invoked via the new ShellStage::NormalizeActivatedManaInstead variant, which parse_activated_ability_ir now unconditionally adds to ir.shell.stages for every generic and ability-word-prefixed activated ability. That is a materially larger call surface than before this PR (per the PR's own description of NormalizeActivatedManaInstead as a new addition), so any activated mana ability (Effect::Mana with a Colorless fixed count) that has a sub_ability gated by some condition OTHER than an "instead" alternative will now silently lose that condition during lowering.

🐛 Proposed fix: restore the condition on match failure
-    let Some(AbilityCondition::ConditionInstead { inner }) = sub.condition.take() else {
-        return;
-    };
+    let taken_condition = sub.condition.take();
+    let Some(AbilityCondition::ConditionInstead { inner }) = taken_condition else {
+        sub.condition = taken_condition;
+        return;
+    };
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@crates/engine/src/parser/oracle.rs` around lines 8474 - 8512, Update
normalize_activated_mana_instead_delta so a sub-ability condition that is not
AbilityCondition::ConditionInstead is restored before returning. Preserve the
existing early-return behavior for absent conditions and keep the later
restoration paths unchanged.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@crates/engine/src/parser/oracle_ir/snapshot_tests.rs`:
- Around line 192-196: Strengthen the assertions for the first lowered ability
in the snapshot test to verify its complete effect chain, including both the
roll and draw instructions in order rather than only matching Effect::Mana.
Snapshot both effect layers as needed so a nonterminal roll cannot omit either
clause.

---

Outside diff comments:
In `@crates/engine/src/parser/oracle.rs`:
- Around line 8474-8512: Update normalize_activated_mana_instead_delta so a
sub-ability condition that is not AbilityCondition::ConditionInstead is restored
before returning. Preserve the existing early-return behavior for absent
conditions and keep the later restoration paths unchanged.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: f772103d-f09b-4a17-b91c-06b9dbbe42d7

📥 Commits

Reviewing files that changed from the base of the PR and between 895de40 and b2e8ca0.

⛔ Files ignored due to path filters (21)
  • crates/engine/src/parser/oracle_ir/snapshots/engine__parser__oracle_ir__snapshot_tests__aetherling_ir.snap is excluded by !**/*.snap, !**/snapshots/**
  • crates/engine/src/parser/oracle_ir/snapshots/engine__parser__oracle_ir__snapshot_tests__barbarian_ring_activated_ir.snap is excluded by !**/*.snap, !**/snapshots/**
  • crates/engine/src/parser/oracle_ir/snapshots/engine__parser__oracle_ir__snapshot_tests__barbarian_ring_activated_lowered.snap is excluded by !**/*.snap, !**/snapshots/**
  • crates/engine/src/parser/oracle_ir/snapshots/engine__parser__oracle_ir__snapshot_tests__batterskull_ir.snap is excluded by !**/*.snap, !**/snapshots/**
  • crates/engine/src/parser/oracle_ir/snapshots/engine__parser__oracle_ir__snapshot_tests__birds_of_paradise_ir.snap is excluded by !**/*.snap, !**/snapshots/**
  • crates/engine/src/parser/oracle_ir/snapshots/engine__parser__oracle_ir__snapshot_tests__bomat_courier_ir.snap is excluded by !**/*.snap, !**/snapshots/**
  • crates/engine/src/parser/oracle_ir/snapshots/engine__parser__oracle_ir__snapshot_tests__boseiju_who_endures_ir.snap is excluded by !**/*.snap, !**/snapshots/**
  • crates/engine/src/parser/oracle_ir/snapshots/engine__parser__oracle_ir__snapshot_tests__component_pouch_activated_ir.snap is excluded by !**/*.snap, !**/snapshots/**
  • crates/engine/src/parser/oracle_ir/snapshots/engine__parser__oracle_ir__snapshot_tests__component_pouch_activated_lowered.snap is excluded by !**/*.snap, !**/snapshots/**
  • crates/engine/src/parser/oracle_ir/snapshots/engine__parser__oracle_ir__snapshot_tests__experiment_one_ir.snap is excluded by !**/*.snap, !**/snapshots/**
  • crates/engine/src/parser/oracle_ir/snapshots/engine__parser__oracle_ir__snapshot_tests__figure_of_destiny_ir.snap is excluded by !**/*.snap, !**/snapshots/**
  • crates/engine/src/parser/oracle_ir/snapshots/engine__parser__oracle_ir__snapshot_tests__ghost_lit_stalker_ir.snap is excluded by !**/*.snap, !**/snapshots/**
  • crates/engine/src/parser/oracle_ir/snapshots/engine__parser__oracle_ir__snapshot_tests__jade_mage_ir.snap is excluded by !**/*.snap, !**/snapshots/**
  • crates/engine/src/parser/oracle_ir/snapshots/engine__parser__oracle_ir__snapshot_tests__llanowar_elves_ir.snap is excluded by !**/*.snap, !**/snapshots/**
  • crates/engine/src/parser/oracle_ir/snapshots/engine__parser__oracle_ir__snapshot_tests__mother_of_runes_ir.snap is excluded by !**/*.snap, !**/snapshots/**
  • crates/engine/src/parser/oracle_ir/snapshots/engine__parser__oracle_ir__snapshot_tests__repeat_offender_ir.snap is excluded by !**/*.snap, !**/snapshots/**
  • crates/engine/src/parser/oracle_ir/snapshots/engine__parser__oracle_ir__snapshot_tests__stoneforge_mystic_ir.snap is excluded by !**/*.snap, !**/snapshots/**
  • crates/engine/src/parser/oracle_ir/snapshots/engine__parser__oracle_ir__snapshot_tests__sylvan_safekeeper_ir.snap is excluded by !**/*.snap, !**/snapshots/**
  • crates/engine/src/parser/oracle_ir/snapshots/engine__parser__oracle_ir__snapshot_tests__thespians_stage_generic_activated_ir.snap is excluded by !**/*.snap, !**/snapshots/**
  • crates/engine/src/parser/oracle_ir/snapshots/engine__parser__oracle_ir__snapshot_tests__thespians_stage_generic_activated_lowered.snap is excluded by !**/*.snap, !**/snapshots/**
  • crates/engine/src/parser/oracle_ir/snapshots/engine__parser__oracle_ir__snapshot_tests__walking_ballista_ir.snap is excluded by !**/*.snap, !**/snapshots/**
📒 Files selected for processing (6)
  • crates/engine/src/parser/oracle.rs
  • crates/engine/src/parser/oracle_effect/mod.rs
  • crates/engine/src/parser/oracle_ir/context.rs
  • crates/engine/src/parser/oracle_ir/effect_chain.rs
  • crates/engine/src/parser/oracle_ir/snapshot_tests.rs
  • crates/engine/src/parser/oracle_pipeline_snapshot_tests.rs

Comment thread crates/engine/src/parser/oracle_ir/snapshot_tests.rs
@github-actions

github-actions Bot commented Jul 29, 2026

Copy link
Copy Markdown

Parse changes introduced by this PR

Baseline pending for 3658a21d4c527b9d01a98223fb3bfe5c8f4fd09d — this populates once main publishes its coverage snapshot (a few minutes after that commit landed).

@matthewevans
matthewevans added this pull request to the merge queue Jul 29, 2026
Merged via the queue into main with commit 240cfc8 Jul 29, 2026
14 checks passed
@matthewevans
matthewevans deleted the wt/p05b-t10b branch July 29, 2026 18:54
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant