Skip to content

Partial: Extract Power - #7032

Merged
matthewevans merged 5 commits into
phase-rs:mainfrom
JacobWoodson:card/extract-power
Aug 9, 2026
Merged

Partial: Extract Power#7032
matthewevans merged 5 commits into
phase-rs:mainfrom
JacobWoodson:card/extract-power

Conversation

@JacobWoodson

@JacobWoodson JacobWoodson commented Aug 5, 2026

Copy link
Copy Markdown
Contributor

Summary

Fixes a parse-fidelity defect on Extract Power.

Issue: ExileTop scope is player: Controller (only controller's top card), but Oracle exiles the top card of EACH player's library.

Files changed

  • C:\Users\jacob\source\repos\phase-card-runs\crates\engine\src\parser\oracle_effect\imperative.rs
  • C:\Users\jacob\source\repos\phase-card-runs\crates\engine\src\parser\oracle_effect\sequence.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\tests\integration\extract_power_each_player_exile.rs

CR references

  • CR 401.1
  • CR 608.2c
  • CR 101.4
  • CR 406.3
  • CR 609.3

Track

Developer

LLM

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

Tier: Frontier

Verification

  • cargo fmt --all — passed (exit 0)
  • ./scripts/check-parser-combinators.sh (Gate A) — passed (Gate G PASS + Gate A PASS, exit 0; Family-D skipped — python3 Windows-Store stub, documented env limitation)
  • cargo clippy-strict — passed (exit 0, no warnings)
  • cargo test -p phase-engine — failed (exit 101, 2 failures) — both unrelated to Extract Power: Windows path-separator artifacts in committed bounded-offer infra (game/engine.rs); Extract Power's own 3 integration tests pass
  • cargo export-cards data --stats --sidecar-dir client/public && cp client/public/card-data.json data/card-data.json — passed (28154 cards regenerated; Extract Power present)
  • cargo coverage — passed (exit 0; Extract Power supported:true gap_count:0)
  • cargo semantic-audit — passed (exit 0; Extract Power 0 findings, 32615 cards audited)

Scope Expansion

None.

Validation Failures

See review/cross-check notes.

CI Failures

  • cargo test -p phase-engine: 2 lib-test failures UNRELATED to Extract Power and NOT fixable by me. (1) game::engine::stage2_injector_tests::the_cr_603_5_prompt_census_is_pinned_so_a_sixth_producer_is_a_counted_event (crates/engine/src/game/engine.rs:15307). (2) game::engine::bounded_offer_conjunct_tests::f2c_the_cr_603_5_conjunct_set_has_one_production_assembler (crates/engine/src/game/engine.rs:17848). Root cause: both tests hardcode Unix forward-slash path literals (e.g. "game/effects/mod.rs:6175") and compare them against file!()-derived coordinates, which emit backslashes on the Windows/MSVC toolchain. The left/right diffs are identical file:line pairs differing ONLY by separator (\ vs /) — a pure Windows-environment artifact, the same class as the whitelisted python3/Family-D limitation. These tests live in game/engine.rs (committed CR 603.5 bounded-offer prompt-census infra), which is NOT part of Extract Power's working-tree changes (imperative.rs, sequence.rs, extract_power_each_player_exile.rs). The code carries an explicit warning: 'Diagnose by rebuilding the merge tree... NOT by editing pins to match a local tree', so per CLAUDE.md multi-agent safety I did not modify it. Retry is futile (deterministic toolchain behavior). Extract Power itself is fully verified: supported:true gap:0, 0 semantic-audit findings, and its own 3 integration tests pass.

Summary by CodeRabbit

  • Bug Fixes

    • Corrected effects that look at the top card of each player’s library and exile those cards.
    • Cards are now exiled face down from every non-empty library, while remaining cards stay in place.
    • Empty libraries are handled without errors.
    • Follow-up permissions to play or cast the exiled cards continue to work correctly.
  • Tests

    • Added coverage for multi-player resolution, empty libraries, and related parsing behavior.

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

coderabbitai Bot commented Aug 5, 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: 24 minutes

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

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: fbbfbc90-a740-4334-adca-95226ffb724b

📥 Commits

Reviewing files that changed from the base of the PR and between e1b7c72 and feb5136.

📒 Files selected for processing (4)
  • crates/engine/src/parser/oracle_effect/imperative.rs
  • crates/engine/src/parser/oracle_effect/sequence.rs
  • crates/engine/tests/integration/extract_power_each_player_exile.rs
  • crates/engine/tests/integration/main.rs
📝 Walkthrough

Walkthrough

This change updates Oracle parsing for “of each player's library” so look-then-exile effects keep per-player scope during ExileTop materialization. It also adds integration coverage for Extract Power resolution, parser shape, empty-library handling, and an unchanged Lidless Gaze parsing path.

Changes

Each-player library exile

Layer / File(s) Summary
Detect each-player library scope
crates/engine/src/parser/oracle_effect/imperative.rs
The imperative parser now matches “of each player's library” and returns TargetFilter::ScopedPlayer instead of falling back to TargetFilter::Controller.
Materialize scoped top-card exile
crates/engine/src/parser/oracle_effect/sequence.rs
When a looked-at-card exile rewrites into Effect::ExileTop, the sequence parser now lifts the exile into the parsed all-player scope and keeps the play/cast continuation separate from that scope.
Validate parsing and resolution
crates/engine/tests/integration/extract_power_each_player_exile.rs, crates/engine/tests/integration/main.rs
New integration tests cover three-player resolution, empty-library resolution, Extract Power parser shape, preserved CastFromZone sub-ability, and the unchanged Lidless Gaze direct-exile path. The new test module is registered in the integration suite.

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

Possibly related PRs

  • phase-rs/phase#6564: Both PRs change player-scope propagation in oracle_effect/imperative.rs and oracle_effect/sequence.rs.
  • phase-rs/phase#6784: This PR preserves a CastFromZone sub-ability for “you may play them”, which overlaps with play-from-exile resolution and scoping changes there.
  • phase-rs/phase#6545: Both PRs touch parser handling for reveal/exile continuation flows in the same Oracle effect parsing area.

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 for Extract Power and accurately reflects the partial implementation.
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

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.

@matthewevans matthewevans self-assigned this Aug 5, 2026
@matthewevans matthewevans added the bug Bug fix label Aug 5, 2026
@matthewevans

Copy link
Copy Markdown
Member

Maintainer fixup pushed at 02b5555b7b: removed the inaccurate APNAP assertion from the Extract Power test documentation. CR 101.4 governs simultaneous choices/actions; this sequential per-player resolution makes none. CI restarted for the new head; no approval or enqueue action has been taken.

@matthewevans matthewevans removed their assignment Aug 5, 2026

@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_effect/imperative.rs`:
- Around line 217-224: Update the ScopedPlayer detection in the current parser
function to validate the possessive library phrase at the current noun-phrase
boundary instead of scanning all of rest_lower with preceded/take_until. Reuse
parse_library_player_suffix if it supports this grammar, and preserve
ScopedPlayer only when that helper confirms the current library owner.

In `@crates/engine/tests/integration/extract_power_each_player_exile.rs`:
- Around line 264-273: Extend the integration test around the Extract Power
resolution to exercise the retained play permission through the game pipeline,
not just inspect the sub-ability AST. After all-player exile resolves, have P0
cast an exiled P1 or P2 card and assert that the cast succeeds and the card
resolves, covering permission access across the union of exiled cards; retain
AST assertions only as supplemental checks.
🪄 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: 81275b44-6383-42a0-9a75-674358bf6276

📥 Commits

Reviewing files that changed from the base of the PR and between 562e7b5 and ba87fb2.

📒 Files selected for processing (4)
  • crates/engine/src/parser/oracle_effect/imperative.rs
  • crates/engine/src/parser/oracle_effect/sequence.rs
  • crates/engine/tests/integration/extract_power_each_player_exile.rs
  • crates/engine/tests/integration/main.rs

Comment thread crates/engine/src/parser/oracle_effect/imperative.rs Outdated
Comment thread crates/engine/tests/integration/extract_power_each_player_exile.rs
@github-actions

github-actions Bot commented Aug 5, 2026

Copy link
Copy Markdown

Generated for head feb51365c5b7fe7e090fabd6f8b0c269c85f65f6.

Parse changes introduced by this PR · 0 card(s), 0 signature(s) (baseline: main e1b7c72a2d9a)

11 card(s) had Oracle-text changes (errata/reprint) — excluded as non-parser.
New cards in head: 138.

@matthewevans matthewevans self-assigned this 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.

Changes requested — both current-head correctness gaps remain.

🔴 Blocker

[HIGH] The new each-player owner detection is still an unbounded remainder scan. Evidence: crates/engine/src/parser/oracle_effect/imperative.rs:217-224 applies take_until("of each player's library") to all of rest_lower, and the caller supplies the whole post-"look at the top " remainder at :2992-3006. The established phrase-to-owner authority, parse_library_player_suffix, instead matches the current card[s] of <owner>'s library noun phrase at :8233-8264. Why it matters: a later "of each player's library" in the same clause can incorrectly turn a dig of a different library into ScopedPlayer; the continuation then materializes an all-player ExileTop fan-out. Suggested fix: bind the owner at the current noun phrase using the shared suffix authority (thread ParseContext if needed), and add a later-phrase negative sibling proving the earlier library remains the selected owner.

[HIGH] The regression test does not execute the retained permission. Evidence: crates/engine/tests/integration/extract_power_each_player_exile.rs:69-141 proves the three cards moved to exile, while :263-272 only checks that the parsed sub-ability is CastFromZone; it never sends an exiled P1/P2 card through GameAction::CastSpell. Why it matters: split_player_scope_chain can preserve an AST node while failing to make its tracked union usable by the casting pipeline. Suggested fix: after Extract Power resolves, use a known castable P1 or P2 top card, cast it as P0 through GameAction::CastSpell, and assert acceptance plus resolution; retain the AST check only as supplemental coverage.

✅ Clean

The 02b5555b follow-up corrects the earlier CR-text assertion, but it changes only test prose and leaves both CodeRabbit findings against the implementation and runtime coverage unresolved.

Recommendation: request changes — bind the library owner at the current noun phrase and add the cross-player runtime cast test before re-review.

@matthewevans matthewevans removed their assignment Aug 5, 2026
@matthewevans matthewevans removed the needs-maintainer AI-contribution PR requires human triage (Non-dev track or unresolved gaps) label Aug 6, 2026
@matthewevans matthewevans self-assigned this Aug 9, 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.

Changes requested — current head feb51365c5b7fe7e090fabd6f8b0c269c85f65f6

[MED] The each-player dig owner matcher recognizes only the singular noun phrase and leaves the plural sibling controller-scoped. Evidence: crates/engine/src/parser/oracle_effect/imperative.rs:217-224 accepts only "card of each player's library", while its documented suffix authority parse_library_player_suffix supports both "card" and "cards" at :8254-8255. The caller passes the post-count noun phrase into this matcher at :2990-3003, so a multi-card form such as “look at the top two cards of each player's library, then exile those cards” falls through to TargetFilter::Controller; the later ExileTop materialization cannot restore the missing all-player scope.

Why it matters: this is the same each-player look-then-exile class the PR claims to fix, but it remains wrong for plural counts.

Suggested fix: bind the current noun phrase through parse_library_player_suffix (threading ParseContext), or extract and reuse its noun-boundary authority. Add a plural each-player regression and a negative case proving a later “of each player's library” phrase does not re-scope an earlier library.

[HIGH] Current-head parser-impact evidence is still missing. The only <!-- coverage-parse-diff --> sticky is bound to 02b5555b7b12be08255efc6b6e30724f8a0b2c28, not this parser-changing head. Publish a fresh artifact bound to feb51365c5b7fe7e090fabd6f8b0c269c85f65f6 and account for its surface before re-review.

@matthewevans

Copy link
Copy Markdown
Member

Correction/update — current head feb51365c5b7fe7e090fabd6f8b0c269c85f65f6

The previous finding that the current head lacked parser-impact evidence is withdrawn: the new <!-- coverage-parse-diff --> receipt is correctly bound to this exact head. The formal CHANGES_REQUESTED disposition remains in force for the following current-head blockers.

[MED] The each-player dig owner matcher still recognizes only the singular noun phrase. Evidence: crates/engine/src/parser/oracle_effect/imperative.rs:217-221 accepts card of each player's library, while the shared suffix authority accepts both card and cards at :8254-8255; the caller supplies the post-count noun phrase at :2989-3003. Why it matters: look at the top two cards of each player's library, then exile those cards remains controller-scoped and loses ScopedPlayer. Suggested fix: bind the noun phrase through the shared suffix authority (or extract its boundary-aware parser), and add plural plus later-phrase negative regressions.

[MED] The exact-head receipt reports 0 cards / 0 signatures, despite the PR claiming an Extract Power parser change. Evidence: current receipt https://github.com/phase-rs/phase/pull/7032#issuecomment-5192827721, bound to this head. Why it matters: either the claimed parser behavior is not represented in card data or the intended surface is not being measured; neither case substantiates the parser change. Suggested fix: reconcile the claim with the zero-delta artifact before re-review (identify the real card-data parse change or reduce/reframe the implementation and tests).

@matthewevans matthewevans removed their assignment Aug 9, 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.

Maintainer sign-off: current head fixes the library-owner boundary and proves the cross-player cast permission through the production pipeline.

@matthewevans
matthewevans added this pull request to the merge queue Aug 9, 2026
Merged via the queue into phase-rs:main with commit 731c070 Aug 9, 2026
20 of 22 checks passed
@JacobWoodson
JacobWoodson deleted the card/extract-power branch August 10, 2026 01:17
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Bug fix

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants