Skip to content

Add Lady Loki, Agent of Chaos - #6945

Merged
matthewevans merged 7 commits into
phase-rs:mainfrom
JacobWoodson:card/lady-loki-agent-of-chaos
Aug 4, 2026
Merged

Add Lady Loki, Agent of Chaos#6945
matthewevans merged 7 commits into
phase-rs:mainfrom
JacobWoodson:card/lady-loki-agent-of-chaos

Conversation

@JacobWoodson

@JacobWoodson JacobWoodson commented Aug 3, 2026

Copy link
Copy Markdown
Contributor

Summary

Adds engine support for Lady Loki, Agent of Chaos.

Files changed

  • C:\Users\jacob\source\repos\phase-card-runs\crates\engine\src\parser\oracle_nom\quantity.rs
  • C:\Users\jacob\source\repos\phase-card-runs\crates\engine\src\game\quantity.rs
  • C:\Users\jacob\source\repos\phase-card-runs\crates\engine\src\game\effects\deal_damage.rs
  • C:\Users\jacob\source\repos\phase-card-runs\crates\engine\src\game\effects\mod.rs
  • C:\Users\jacob\source\repos\phase-card-runs\crates\engine\tests\integration\lady_loki_agent_of_chaos.rs
  • C:\Users\jacob\source\repos\phase-card-runs\crates\engine\tests\integration\main.rs

CR references

  • CR 109.5
  • CR 120.3
  • CR 202.3
  • CR 202.3e
  • CR 400.7
  • CR 608.2c
  • CR 616.1e

Track

Developer

LLM

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

Tier: Frontier

Verification

  • cargo fmt --all — clean
  • ./scripts/check-parser-combinators.sh (Gate A) — clean (after env fix: hardcoded python3 resolved to the broken Windows App Store stub causing a false 'cross-product detector RED' failure; reran with msys64 python3 on PATH -> Gate G PASS + Gate A PASS)
  • cargo clippy-strict — incomplete - still compiling in background (cold build; foreground attempt timed out at 10m, background run still 'Checking phase-engine', no result yet)
  • cargo test -p phase-engine — not run (blocked on clippy-strict completing in the && chain)
  • ./scripts/gen-card-data.sh — not run (blocked on prior steps)
  • cargo coverage — not run
  • cargo semantic-audit — not run

Re-verified at chunk-1 checkpoint with freshly regenerated card-data: all listed cards supported:true gap:0, semantic-audit clean. The run-time 'partial' was a stale-card-data artifact, not a code defect.

Scope Expansion

None. (Scope narrowed vs plan: the R6 zones/LKI change for EventSource-across-exile proved unnecessary — move_to_zone reuses the object id on Stack->Exile, so the exiled spell's off-stack MV is read directly.)

Validation Failures

See review/cross-check notes.

CI Failures

  • Verification incomplete - forced to report before the build chain finished. cargo clippy-strict was still compiling (cold build; single foreground run timed out at 10m, background run had only reached 'Checking phase-engine' with no clean/error result).
  • cargo test -p phase-engine: NOT RUN (fallback path is clippy-strict && test-engine && gen-card-data; blocked because clippy had not yet returned clean).
  • ./scripts/gen-card-data.sh: NOT RUN.
  • cargo coverage: NOT RUN - 'Lady Loki, Agent of Chaos' supported/gap status UNCONFIRMED, so it is NOT in cardsSupported.
  • cargo semantic-audit: NOT RUN - 'Lady Loki, Agent of Chaos' finding count UNCONFIRMED, so semanticAuditClean is false.
  • Environment note (not a code failure): Tilt was down (tilt get uiresource clippy exit 1), so the direct-cargo fallback path was correctly selected. Also, python3/python on PATH resolve to the Windows App Store stub (Permission denied, exit 126); a working interpreter exists at C:/msys64/mingw64/bin/python3.exe (3.9.7). Gate A only passes when that msys64 python3 is prepended to PATH.

Summary by CodeRabbit

  • Bug Fixes

    • Improved damage effects that pause for replacements, preserving correct targets and per-player values.
    • Fixed mana-value calculations for targeted cards, including qualified references such as “that nonland card.”
    • Corrected target and scope handling in nested quantity calculations.
    • Improved Lady Loki, Agent of Chaos interactions, including mana-value damage, exile sequencing, and optional casting of exiled cards.
  • Tests

    • Added coverage for Lady Loki’s parsing and gameplay behavior.

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

coderabbitai Bot commented Aug 3, 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: 85d11278-1a7d-41df-9878-070266b3400b

📥 Commits

Reviewing files that changed from the base of the PR and between c869f45 and 70332cf.

📒 Files selected for processing (6)
  • crates/engine/src/game/effects/deal_damage.rs
  • crates/engine/src/game/effects/mod.rs
  • crates/engine/src/game/engine.rs
  • crates/engine/src/game/quantity.rs
  • crates/engine/src/parser/oracle_nom/quantity.rs
  • crates/engine/tests/integration/main.rs
🚧 Files skipped from review as they are similar to previous changes (5)
  • crates/engine/tests/integration/main.rs
  • crates/engine/src/game/effects/mod.rs
  • crates/engine/src/game/quantity.rs
  • crates/engine/src/parser/oracle_nom/quantity.rs
  • crates/engine/src/game/effects/deal_damage.rs

📝 Walkthrough

Walkthrough

The change adds target-aware quantity resolution, parses qualified target card references, and preserves parent object targets through paused damage continuations. New tests cover Lady Loki’s parser and runtime behavior.

Changes

Target-scoped damage resolution

Layer / File(s) Summary
Target-aware quantity parsing and resolution
crates/engine/src/parser/oracle_nom/quantity.rs, crates/engine/src/game/quantity.rs
Qualified card references now resolve with target scope. Scoped quantity expressions propagate explicit targets through nested expressions.
Target propagation through damage continuations
crates/engine/src/game/effects/mod.rs, crates/engine/src/game/effects/deal_damage.rs, crates/engine/src/game/engine.rs
Paused damage continuations preserve eligible parent object targets. DamageEachPlayer retains target context for current and remaining players.
Lady Loki integration coverage
crates/engine/tests/integration/lady_loki_agent_of_chaos.rs, crates/engine/tests/integration/main.rs
Integration tests cover mana-value damage, off-stack X values, exile sequencing, optional casting, parser structure, and module registration.

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

Sequence Diagram(s)

sequenceDiagram
  participant TriggeredSpell
  participant LadyLokiAbility
  participant ExileUntilEffect
  participant DamageEachPlayer
  participant QuantityResolver
  participant CastFromZone
  TriggeredSpell->>LadyLokiAbility: trigger on first matching spell
  LadyLokiAbility->>ExileUntilEffect: exile cards until a nonland card
  ExileUntilEffect->>DamageEachPlayer: resolve damage effect
  DamageEachPlayer->>QuantityResolver: resolve target-scoped mana values
  DamageEachPlayer->>CastFromZone: offer optional free cast of exiled card
Loading

Possibly related PRs

Suggested reviewers: matthewevans, lgray

🚥 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 primary change: adding engine support for Lady Loki, Agent of Chaos.
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 unit tests (beta)
  • Create PR with unit tests

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.

🧹 Nitpick comments (4)
crates/engine/tests/integration/lady_loki_agent_of_chaos.rs (1)

159-167: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Assert the hit's zone instead of discarding the binding.

let _ = hit; discards the id. The test then has no reach guard proving the dig actually exiled a nonland card with mana value 1. If the dig failed and the amount fell back to another path that also produced 2, the test would pass for the wrong reason.

💚 Replace the discard with a reach guard
-    let _ = hit;
-
     let mut runner = scenario.build();
     let outcome = runner.cast(spell).x(3).resolve();
+
+    // Reach guard: the dig exiled the nonland hit, so MV(hit) = 1 was read.
+    assert_eq!(
+        outcome.zone_of(hit),
+        engine::types::zones::Zone::Exile,
+        "the nonland hit must be exiled by the dig"
+    );

As per path instructions: "require a paired positive reach-guard proving the input actually reached the code under test".

🤖 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/tests/integration/lady_loki_agent_of_chaos.rs` around lines 159
- 167, Replace the discarded hit binding in the test setup with a positive reach
guard that asserts the card identified by hit is in the exile zone after the
dig. Keep the existing result assertion, ensuring the test proves the nonland
mana-value-1 card reached the code path under test.

Source: Path instructions

crates/engine/src/game/effects/deal_damage.rs (2)

3081-3088: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Tighten the chain assertion to an exact match.

The assertion uses any(...). A regression that re-stashes the paused first opponent (P1) alongside P2 would still pass. Assert the full summary instead, so the test pins both the amount and the exact remaining-player set.

💚 Exact-match assertion
-        let summary = collect_chain_summary(&cont.chain);
-        assert!(
-            summary.iter().any(
-                |(_, target, amount)| *target == TargetRef::Player(PlayerId(2)) && *amount == 3
-            ),
-            "remaining opponent must be stashed with |MV(spell) − MV(hit)| = 3 (a \
-             target-dropped pre-resolve gives 4): {summary:?}"
-        );
+        let summary = collect_chain_summary(&cont.chain);
+        assert_eq!(
+            summary,
+            vec![(source, TargetRef::Player(PlayerId(2)), 3)],
+            "only the remaining opponent must be stashed, with \
+             |MV(spell) − MV(hit)| = 3 (a target-dropped pre-resolve gives 4)"
+        );

As per path instructions: "Test adequacy is the highest-frequency contributor finding — scrutinize it."

🤖 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/game/effects/deal_damage.rs` around lines 3081 - 3088,
Update the assertion around collect_chain_summary to require an exact summary
rather than merely finding a matching entry: verify the remaining stashed player
set contains only PlayerId(2) and that its damage amount is 3, so an extra
PlayerId(1) entry fails the test.

Source: Path instructions


1028-1037: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Add a negative case that proves the guard blocks propagation.

cast_tail_with_parent_targets copies the parent's object targets into every stashed tail when should_propagate_parent_targets returns true. The new regression test at Line 2964 only covers the true branch. No test covers the false branch on a pause path. If the predicate ever diverges from resolve_ability_chain, a plain multi-target DealDamage tail would silently gain the parent's object referents and damage the wrong recipients after a replacement pause.

Add a paired case: a parent DealDamage with two object targets and a sub whose target_choice_timing is Resolution, then assert the stashed tail keeps targets.is_empty().

As per path instructions: "For every negative assertion … require a paired positive reach-guard proving the input actually reached the code under test".

🤖 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/game/effects/deal_damage.rs` around lines 1028 - 1037, Add
a paired regression test near the existing propagation test for
cast_tail_with_parent_targets: use a parent DealDamage with two object targets
and a sub whose target_choice_timing is Resolution, then assert the stashed
tail’s targets remain empty. Include a positive reach-guard confirming the
replacement pause reaches the stashed-tail path before the negative assertion,
and leave the true-branch coverage intact.

Source: Path instructions

crates/engine/src/parser/oracle_nom/quantity.rs (1)

4628-4639: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Compose the non prefix as its own axis instead of enumerating two literals.

parse_card_type_qualifier enumerates nonland and noncreature as separate literal arms, while delegating bare types to parse_core_type. The non prefix is an independent axis. Composing it over parse_core_type covers the whole class (nonartifact card's mana value, nonenchantment card's mana value, …) with the same node count.

♻️ Compose the prefix axis
 fn parse_card_type_qualifier(input: &str) -> OracleResult<'_, ()> {
     terminated(
-        alt((
-            value((), tag("nonland")),
-            value((), tag("noncreature")),
-            value((), tag("permanent")),
-            value((), parse_core_type),
-        )),
+        value(
+            (),
+            (
+                opt(tag("non")),
+                alt((value((), tag("permanent")), value((), parse_core_type))),
+            ),
+        ),
         tag(" "),
     )
     .parse(input)
 }

As per coding guidelines: "Compose alt() per axis; never enumerate the cartesian product as separate tag("full string") arms."

🤖 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_nom/quantity.rs` around lines 4628 - 4639,
Update parse_card_type_qualifier to parse the optional “non” prefix separately
and compose it with parse_core_type, while retaining the standalone “permanent”
qualifier and trailing-space requirement. Remove the separate nonland and
noncreature literal arms so all non-prefixed and non-prefixed core card types
are handled through the shared parser.

Source: Coding guidelines

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

Nitpick comments:
In `@crates/engine/src/game/effects/deal_damage.rs`:
- Around line 3081-3088: Update the assertion around collect_chain_summary to
require an exact summary rather than merely finding a matching entry: verify the
remaining stashed player set contains only PlayerId(2) and that its damage
amount is 3, so an extra PlayerId(1) entry fails the test.
- Around line 1028-1037: Add a paired regression test near the existing
propagation test for cast_tail_with_parent_targets: use a parent DealDamage with
two object targets and a sub whose target_choice_timing is Resolution, then
assert the stashed tail’s targets remain empty. Include a positive reach-guard
confirming the replacement pause reaches the stashed-tail path before the
negative assertion, and leave the true-branch coverage intact.

In `@crates/engine/src/parser/oracle_nom/quantity.rs`:
- Around line 4628-4639: Update parse_card_type_qualifier to parse the optional
“non” prefix separately and compose it with parse_core_type, while retaining the
standalone “permanent” qualifier and trailing-space requirement. Remove the
separate nonland and noncreature literal arms so all non-prefixed and
non-prefixed core card types are handled through the shared parser.

In `@crates/engine/tests/integration/lady_loki_agent_of_chaos.rs`:
- Around line 159-167: Replace the discarded hit binding in the test setup with
a positive reach guard that asserts the card identified by hit is in the exile
zone after the dig. Keep the existing result assertion, ensuring the test proves
the nonland mana-value-1 card reached the code path under test.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: d316f225-37bf-4bb9-a19b-238ca6be8e18

📥 Commits

Reviewing files that changed from the base of the PR and between 7fa1fc4 and 269497e.

📒 Files selected for processing (6)
  • crates/engine/src/game/effects/deal_damage.rs
  • crates/engine/src/game/effects/mod.rs
  • crates/engine/src/game/quantity.rs
  • crates/engine/src/parser/oracle_nom/quantity.rs
  • crates/engine/tests/integration/lady_loki_agent_of_chaos.rs
  • crates/engine/tests/integration/main.rs

@matthewevans matthewevans added the enhancement New feature or request label Aug 3, 2026
@matthewevans

matthewevans commented Aug 3, 2026

Copy link
Copy Markdown
Member

Generated for head 70332cfbe082743e64ce7fdce9be763b4c254692.

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

🟢 Added (1 signature)

  • 1 card · ➕ ability/DamageEachPlayer · added: DamageEachPlayer (amount=|event source's mana value - target's mana value|, players=each opponent)
    • Affected (first 3): Lady Loki, Agent of Chaos

🔴 Removed (1 signature)

  • 1 card · ➖ ability/deal · removed: deal
    • Affected (first 3): Lady Loki, Agent of Chaos

2 card(s) had Oracle-text changes (errata/reprint) — excluded as non-parser.

@matthewevans matthewevans removed their assignment Aug 3, 2026
@matthewevans matthewevans self-assigned this Aug 3, 2026
@matthewevans

Copy link
Copy Markdown
Member

Maintainer hold for current head 6d705c61c44a93d86223a23d68f5e64e8631eb55: implementation review and approval will resume once required Rust checks settle and CI publishes a parse-diff bound to this head. The available parse-diff evidence is stale (bound to 337f1f208221487168ea7d23f94b13337f21105d), so it cannot support a current-head parser review. No implementation findings in this hold.

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

Request changes — current head 6d705c61c44a93d86223a23d68f5e64e8631eb55 has an unaccounted parser blast radius.

The current parse-diff reports O-Kagachi Made Manifest changing from unsupported where_x_binding to supported Pump (+target's mana value/+0) alongside the intended Lady Loki change: #6945 (comment). The new generic that [type] card qualifier is the shared parser seam (crates/engine/src/parser/oracle_nom/quantity.rs:4617-4668), and O-Kagachi uses it after a player chooses a nonland card in a graveyard, returns that chosen card, then gets +X/+0 for its mana value (data/mtgish-cards.json:28199).

Either add a production-pipeline O-Kagachi scenario proving the real choose → return → mana-value pump chain (including a discriminating nonzero mana value), or narrow/justify this additional parse change so coverage stays honest. Lady Loki-only tests do not establish the selected-authority/target threading for O-Kagachi's distinct choice path.

I also checked current CodeRabbit feedback: its only review is against older head 269497e4... and there are no unresolved current-head threads, so no separate current CodeRabbit reach-guard request applies.

@matthewevans matthewevans removed their assignment Aug 3, 2026
@JacobWoodson JacobWoodson changed the title Partial: Lady Loki, Agent of Chaos Add Lady Loki, Agent of Chaos Aug 4, 2026
…ghten Lady Loki tests

Addresses the PR review parser blast-radius: the opt(parse_card_type_qualifier)
"that card" arms widened the seam so a BARE "that card" also lowered to
ObjectScope::Target. That flipped O-Kagachi Made Manifest's "...where X is the
mana value of that card" from an honest where_x_binding gap into a dishonest
Pump (+target's mana value/+0) -- but O-Kagachi's "that card" is a card the
defending player CHOSE from a graveyard, not a threaded target, so no target is
ever wired for the pump to read.

Narrow both the possessive and prepositional arms to REQUIRE the type qualifier
(parse_card_type_qualifier), keeping Lady Loki's type-qualified "that nonland
card's mana value" (bound to the ExileFromTopUntil producer) while reverting
O-Kagachi to its baseline where_x_binding -- verified end-to-end via
parse_oracle_text. Also compose the non prefix as its own opt(tag("non"))
axis over parse_core_type so every "non<type>" qualifier is covered by one node
set instead of enumerated literals.

Tests:
- rename/extend the positive scope test to cover the composed non-axis
- add bare_of_that_card_mana_value_is_not_target_scope honesty guard (O-Kagachi
  prepositional form), paired with the positive reach-guard
- Lady Loki X-spell test: replace `let _ = hit;` with an Exile reach guard
- exact-match the DamageEachPlayer pause chain summary
- add the false-branch pair proving a Resolution-timed sub does not inherit
  parent targets on the pause path

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

@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: 2

🤖 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_nom/quantity.rs`:
- Around line 4632-4636: Correct the documentation above the non qualifier
parser by removing the inaccurate CR 205.2b citation or replacing it with a
verified rule citation whose text explicitly defines the non qualifier behavior;
leave the parser implementation and canonical parse_core_type delegation
unchanged.
- Around line 4645-4647: Extend parse_core_type to recognize the vanguard card
type so parse_card_type_qualifier supports complete card-type coverage. Add
regression cases for both “that vanguard card’s mana value” and “mana value of
that vanguard card,” covering the possessive and prepositional forms while
preserving existing card-type parsing.
🪄 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: a5559522-fee0-4131-845e-35f82f1f7ef4

📥 Commits

Reviewing files that changed from the base of the PR and between 337f1f2 and c869f45.

📒 Files selected for processing (5)
  • crates/engine/src/game/effects/deal_damage.rs
  • crates/engine/src/game/quantity.rs
  • crates/engine/src/parser/oracle_nom/quantity.rs
  • crates/engine/tests/integration/lady_loki_agent_of_chaos.rs
  • crates/engine/tests/integration/main.rs
🚧 Files skipped from review as they are similar to previous changes (4)
  • crates/engine/tests/integration/main.rs
  • crates/engine/tests/integration/lady_loki_agent_of_chaos.rs
  • crates/engine/src/game/effects/deal_damage.rs
  • crates/engine/src/game/quantity.rs

Comment thread crates/engine/src/parser/oracle_nom/quantity.rs Outdated
Comment on lines +4645 to +4647
opt(tag("non")),
alt((value((), tag("permanent")), value((), parse_core_type))),
),

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.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Support the missing vanguard card type.

parse_card_type_qualifier claims complete card-type coverage but delegates to parse_core_type, which excludes vanguard. Therefore, that vanguard card's mana value and mana value of that vanguard card do not parse. Add vanguard to the shared parser and add possessive and prepositional regression cases. Vanguard is a card type under CR 205.2a and CR 300.1. (media.wizards.com)

As per path instructions, strict fidelity to the Comprehensive Rules is non-negotiable.

Also applies to: 10887-10896

🤖 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_nom/quantity.rs` around lines 4645 - 4647,
Extend parse_core_type to recognize the vanguard card type so
parse_card_type_qualifier supports complete card-type coverage. Add regression
cases for both “that vanguard card’s mana value” and “mana value of that
vanguard card,” covering the possessive and prepositional forms while preserving
existing card-type parsing.

Source: Path instructions

@JacobWoodson

Copy link
Copy Markdown
Contributor Author

@matthewevans thanks for the catch on the parse blast radius. Addressed in c869f45c by taking the narrow option.

O-Kagachi (the blast radius): the opt(parse_card_type_qualifier) "that card" arms were letting a bare "that card" lower to ObjectScope::Target. That's what flipped O-Kagachi Made Manifest's "…where X is the mana value of that card" from an honest where_x_binding gap into a Pump (+target's mana value/+0) — but O-Kagachi's "that card" is a card the defending player chose from a graveyard, not a threaded target, so nothing is ever wired for that pump to read.

Both the possessive and prepositional arms now require the type qualifier. Lady Loki's type-qualified "that nonland card's mana value" (bound to the ExileFromTopUntil producer) still binds to Target; a bare "that card" no longer does. Verified end-to-end through parse_oracle_text that O-Kagachi reverts to its baseline:

"effect": { "type": "Unimplemented", "name": "where_x_binding",
            "description": "where X is the mana value of that card" }

So the regenerated parse-diff should now show only Lady Loki. No separate O-Kagachi choose→return→pump scenario is added, since that path (a defending-player choice from a graveyard) isn't implemented and this keeps coverage honest rather than claiming it.

Also in this commit:

  • Composed the non prefix as its own opt(tag("non")) axis over parse_core_type (covers the whole non<type> class instead of enumerated literals).
  • The CodeRabbit nitpicks: exact-match the DamageEachPlayer pause chain summary; added the false-branch pair proving a Resolution-timed sub does not inherit parent targets on the pause path; replaced let _ = hit; with an Exile reach-guard; added a bare_of_that_card_mana_value_is_not_target_scope honesty guard paired with the positive reach-guard.

cargo fmt + clippy -D warnings clean; parser/pause unit tests and the four Lady Loki integration tests pass.

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

Current-head review for 5ec8afdd28365abe6fb7b716bf847349a799a948 — changes requested.

[MED] The parser coverage artifact is not bound to this review head. Evidence: the full <!-- coverage-parse-diff --> marker was updated at 2026-08-04T02:48:54Z, but its body says it was generated for c869f45cfab59ab48a3013749994e71cbefb616f, rather than 5ec8afdd28365abe6fb7b716bf847349a799a948; parser files changed in the intervening merge. Why it matters: the current card-level parse blast radius is therefore unverified. Suggested fix: regenerate the parse-diff artifact for the current head before coverage acceptance.

[MED] The new qualifier documentation has false rules provenance and an inaccurate completeness claim. Evidence: crates/engine/src/parser/oracle_nom/quantity.rs:4632-4637 attributes the non prefix to CR 205.2b and says the delegated parser covers the full core set. CR 205.2b instead concerns multi-card-type applicability; the canonical parse_core_type intentionally omits Vanguard because CoreType does not model the out-of-scope Vanguard avatars (crates/engine/src/parser/oracle_nom/primitives.rs:345-354). Why it matters: this repository requires every CR annotation to describe the code it annotates. Suggested fix: remove the CR 205.2b attribution and describe the qualifier as covering the supported CoreType vocabulary; do not add new Vanguard support in this PR.

… qualifier doc

The parse_card_type_qualifier doc attributed the `non` prefix to CR 205.2b and
claimed complete coverage of the card-type set. Both are inaccurate:

- CR 205.2b concerns multi-card-type applicability, not a `non` qualifier. Drop
  the citation; the `non` prefix is a plain grammatical axis with no dedicated CR.
- parse_core_type intentionally omits `vanguard` (CoreType models no Vanguard
  variant), so the qualifier does NOT cover the full CR 300.1 card-type set.
  Describe coverage as exactly the supported CoreType vocabulary and note vanguard
  is out of scope and not added by this PR.

Per-repo rule: every CR annotation must describe the code it annotates. No
behavior change; documentation only.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@matthewevans matthewevans self-assigned this Aug 4, 2026
Maintainer fixup for current-main line movement.

Co-authored-by: Jacob Woodson <38709105+JacobWoodson@users.noreply.github.com>
@matthewevans

Copy link
Copy Markdown
Member

Maintainer fixup pushed at 70332cfbe082743e64ce7fdce9be763b4c254692: rebased the CR 603.5 prompt-census pins against the current CI merge tree (game/effects/mod.rs 5996/6073/90485999/6076/9051). This is maintainer-side line movement, not a semantic change to the contribution. Holding approval and queue action until CI and the parse artifact finish for this new head.

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

Approved for current head 70332cfbe082743e64ce7fdce9be763b4c254692.

The current parse-diff is bound to this head and confines parser impact to Lady Loki (one card, two signatures). Current CI is green. The remaining open CodeRabbit Vanguard thread is refuted by the current code: parse_card_type_qualifier explicitly consumes only the supported CoreType vocabulary, and CoreType intentionally has no Vanguard variant; expanding that engine model is outside this PR's Lady Loki seam.

@matthewevans matthewevans removed the needs-maintainer AI-contribution PR requires human triage (Non-dev track or unresolved gaps) label Aug 4, 2026
@matthewevans
matthewevans added this pull request to the merge queue Aug 4, 2026
@matthewevans matthewevans removed their assignment Aug 4, 2026
Merged via the queue into phase-rs:main with commit cdb99ba Aug 4, 2026
13 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants