Partial: Doom's Time Platform - #7022
Conversation
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (1)
🚧 Files skipped from review as they are similar to previous changes (1)
📝 WalkthroughWalkthroughExile parsing now separates trailing counter clauses from descriptive targets in counterless zones. The parser applies those counters when the card enters exile and preserves battlefield counter filters. Parser and integration tests cover both behaviors. ChangesExile counter parsing
Estimated code review effort: 3 (Moderate) | ~20 minutes Sequence Diagram(s)sequenceDiagram
participant Combat
participant Doom's Time Platform
participant Graveyard
participant Exile zone
Combat->>Doom's Time Platform: resolve attack trigger
Doom's Time Platform->>Graveyard: target a nonland card
Doom's Time Platform->>Exile zone: exile the card with two time counters
Possibly related PRs
Suggested labels: Suggested reviewers: 🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
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. Comment |
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 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_effect/tests.rs`:
- Around line 35589-35664: Complete the regression matrix around the existing
exile counter tests, covering graveyard, hand, library, battlefield, and a
non-counter “with …” clause. In each origin-sensitive case, assert the parsed
target preserves the expected origin via origin or FilterProp::InZone, while
confirming only graveyard/hand/library lift counters to enter_with_counters and
battlefield retains FilterProp::Counters. Use the existing parse_effect_chain
and target pattern in the named tests.
🪄 Autofix
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: f4dbb2a0-05a7-42fd-beb8-f57e4fa53cb4
📒 Files selected for processing (5)
crates/engine/src/parser/oracle_effect/imperative.rscrates/engine/src/parser/oracle_effect/mod.rscrates/engine/src/parser/oracle_effect/tests.rscrates/engine/tests/integration/doom_s_time_platform_exile_with_time_counters.rscrates/engine/tests/integration/main.rs
| /// CR 122.2 + CR 702.62a: the counterless-origin lift is class-level, not a | ||
| /// Doom's Time Platform special case — a LIBRARY origin ("exile target card | ||
| /// from your library with a +1/+1 counter on it") is equally counterless, so | ||
| /// the clause is an enter-with-counters rider rather than a filter. Guards the | ||
| /// whole "exile <descriptive target> from your {graveyard,hand,library} with N | ||
| /// <type> counter(s) on it" class. | ||
| #[test] | ||
| fn exile_library_descriptive_target_with_counters_lifts_to_enter_with_counters() { | ||
| let def = parse_effect_chain( | ||
| "Exile target card from your library with a +1/+1 counter on it.", | ||
| AbilityKind::Spell, | ||
| ); | ||
| let Effect::ChangeZone { | ||
| destination: Zone::Exile, | ||
| target: TargetFilter::Typed(typed), | ||
| enter_with_counters, | ||
| .. | ||
| } = &*def.effect | ||
| else { | ||
| panic!( | ||
| "expected ChangeZone->Exile(Typed) from library, got: {:?}", | ||
| def.effect | ||
| ); | ||
| }; | ||
| assert_eq!( | ||
| enter_with_counters.as_slice(), | ||
| &[(CounterType::Plus1Plus1, QuantityExpr::Fixed { value: 1 })], | ||
| "expected (+1/+1, 1) enter_with_counters, got: {enter_with_counters:?}" | ||
| ); | ||
| assert!( | ||
| !typed | ||
| .properties | ||
| .iter() | ||
| .any(|p| matches!(p, FilterProp::Counters { .. })), | ||
| "counter clause must not remain a target filter: {:?}", | ||
| typed.properties | ||
| ); | ||
| } | ||
|
|
||
| /// Negative reach-guard for the counterless-origin lift: a BATTLEFIELD target | ||
| /// ("exile target creature with two +1/+1 counters on it") CAN legitimately | ||
| /// bear counters (CR 122.1), so the origin gate must NOT fire — the clause stays | ||
| /// a `FilterProp::Counters` target filter and `enter_with_counters` stays empty. | ||
| /// Pairs with the positive tests to prove the split is origin-scoped, not a | ||
| /// blanket rewrite of every "exile … with N counters" clause. | ||
| #[test] | ||
| fn exile_battlefield_target_with_counters_stays_a_target_filter() { | ||
| let def = parse_effect_chain( | ||
| "Exile target creature with two +1/+1 counters on it.", | ||
| AbilityKind::Spell, | ||
| ); | ||
| let Effect::ChangeZone { | ||
| destination: Zone::Exile, | ||
| target: TargetFilter::Typed(typed), | ||
| enter_with_counters, | ||
| .. | ||
| } = &*def.effect | ||
| else { | ||
| panic!("expected ChangeZone->Exile(Typed), got: {:?}", def.effect); | ||
| }; | ||
| assert!( | ||
| enter_with_counters.is_empty(), | ||
| "a battlefield target's counter clause must NOT be lifted: {enter_with_counters:?}" | ||
| ); | ||
| assert!( | ||
| typed.properties.iter().any(|p| matches!( | ||
| p, | ||
| FilterProp::Counters { | ||
| comparator: Comparator::GE, | ||
| .. | ||
| } | ||
| )), | ||
| "battlefield counter clause must remain a target filter: {:?}", | ||
| typed.properties | ||
| ); | ||
| } |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟠 Major | ⚡ Quick win
Complete the parser regression matrix.
The library test does not assert origin: Some(Zone::Library) or FilterProp::InZone { Zone::Library }. An origin-loss regression can still pass while applying counters to the wrong target class.
Add parser cases for Zone::Hand and a non-counter with … clause. The helper has explicit behavior for both cases.
As per path instructions, add parser tests for graveyard, hand, library, battlefield, and non-counter variants.
🤖 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_effect/tests.rs` around lines 35589 - 35664,
Complete the regression matrix around the existing exile counter tests, covering
graveyard, hand, library, battlefield, and a non-counter “with …” clause. In
each origin-sensitive case, assert the parsed target preserves the expected
origin via origin or FilterProp::InZone, while confirming only
graveyard/hand/library lift counters to enter_with_counters and battlefield
retains FilterProp::Counters. Use the existing parse_effect_chain and target
pattern in the named tests.
Source: Path instructions
|
Generated for head Parse changes introduced by this PR · 3 card(s), 5 signature(s) (baseline: main
|
matthewevans
left a comment
There was a problem hiding this comment.
Request changes — the implementation is a three-card parser-class change, but the public scope and regression evidence still describe only Doom's Time Platform.
🟡 Required before merge
-
Scope/impact disclosure.
crates/engine/src/parser/oracle_effect/mod.rs:34513-34527lifts the rider for every inferredGraveyard | Hand | Libraryorigin, not only Doom's Time Platform. The current-head parse-diff artifact forb4d57fe4ec336db44b8fc89fb720174bb8441c0fmeasures five modified signatures across Doom's Time Platform, Altaïr Ibn-La'Ahad, and The Animus. Please update the PR body to state this exact three-card impact/class (or constrain the implementation to the intended scope) so review and future regressions are honest. -
Complete the origin regression matrix.
crates/engine/src/parser/oracle_effect/tests.rs:35589-35664exercises library lifting but does not assert itsorigin/InZonepreservation; it has no Hand case and no non-counter-rider case. Add parser regressions for graveyard, hand, library, battlefield, and a non-counterwith …clause. Each origin-sensitive case should assert the retained origin semantics; only graveyard/hand/library should lift counters, while battlefield and non-counter clauses must remain their respective filter/unparsed forms. This confirms the parser's new class-level gate rather than just the Doom fixture.
CodeRabbit independently raised the test-matrix gap on this head's predecessor; it remains present at the current head: #7022 (comment).
Recommendation: update the stated impact and add the complete regression matrix, then request re-review on the new head.
Summary
Fixes a parse-fidelity defect on Doom's Time Platform.
Issue: "with two time counters on it" should place two time counters on the exiled card (enter_with_counters), but is parsed as a target-filter requiring the card to already have >=2 time counters, dropping the counter placement.
Files changed
CR references
Track
Developer
LLM
Model: claude-opus-4-8
Thinking: high
Tier: Frontier
Verification
cargo fmt --all— pass (exit 0, clean)./scripts/check-parser-combinators.sh (Gate A)— pass (Gate G PASS + Gate A PASS, exit 0; ran with real python3 at /c/msys64/mingw64/bin/python3 by removing the WindowsApps stub from PATH, so Family-D genuinely ran rather than skipped)cargo clippy-strict— pass (exit 0, clean)cargo test -p phase-engine— fail (exit 101; 18516 passed, 1 failed): game::engine::stage2_injector_tests::the_cr_603_5_prompt_census_is_pinned_so_a_sixth_producer_is_a_counted_event at engine.rs:15178 - deterministic Windows-only path-separator artifact, unrelated to the card, in a shared file not part of card work; not modified per multi-agent safetycargo export-cards data --output client/public/card-data.json --stats --sidecar-dir client/public && cp -> data/card-data.json— pass (exit 0; corrected recipe in-loop by adding --output since the literal recipe dumps to stdout and would copy a stale file; fresh 98142340-byte file regenerated from this branch's engine)cargo coverage— pass (exit 0; Doom's Time Platform supported:true gap_count:0)cargo semantic-audit data— pass (exit 0; Doom's Time Platform present in audited data, 0 findings)Scope Expansion
None.
Validation Failures
See review/cross-check notes.
CI Failures
Summary by CodeRabbit
Bug Fixes
Tests