fix(parser): bind perpetual cost grants to prior referents#5098
Conversation
There was a problem hiding this comment.
Code Review
This pull request updates the parser misparse backlog documentation (docs/parser-misparse-backlog.md) to reflect the resolution of several cards. Specifically, it decrements the total count of implicated cards and updates the 'Perpetual (Alchemy) duration mis-mapped to UntilEndOfTurn' backlog item by removing Bloodsprout Talisman, Geistchanneler, and Nightclub Bouncer from the list, reducing its count from 71 to 68. There are no review comments, and I have no feedback to provide.
Important
The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.
Parse changes introduced by this PR · 3 card(s), 3 signature(s) (baseline: main
|
|
Holding review on this head because GitHub currently reports the branch as conflicting with |
matthewevans
left a comment
There was a problem hiding this comment.
Thanks for the update. I found one remaining behavior blocker, and the branch is also currently conflicting with main:
[MED] Empty ChooseFromZone paths can apply the perpetual cost grant to the source. Evidence: crates/engine/src/parser/oracle_effect/mod.rs:7216, crates/engine/src/game/effects/choose_from_zone.rs:79, and crates/engine/src/game/effects/perpetual.rs:104. Why it matters: Bloodsprout Talisman / Geistchanneler with no eligible card should do nothing, but the new ParentTarget sub-ability can resolve with no inherited targets and fall back to modifying the source. Suggested fix: make ApplyPerpetual { target: ParentTarget } no-op when no parent/choice/event object exists, and add a runtime empty-choice regression.
Also, current head is mergeable: CONFLICTING / mergeStateStatus: DIRTY; the conflict needs to be resolved before this can be considered for queueing.
Summary
It perpetually gains "This spell costs ..."clauses asApplyPerpetualcost modifiers when the effect chain already has a prior object referent.It perpetually gains ...honest-red instead of silently binding it to the source.Bloodsprout Talisman,Geistchanneler, andNightclub Bouncerfrom root cause chore: update coverage stats and badges #19 indocs/parser-misparse-backlog.md.Root Cause
These Alchemy clauses were falling through to generic continuous-modification parsing. That produced
GenericEffectentries withUntilEndOfTurnduration instead of persistentApplyPerpetualcost modifications. The missing piece was the bound-pronoun subject form after a priorChooseFromZoneor targeted clause.Card-Data Audit
Full raw
card-data.jsonbefore/after exports both contained 35,397 cards. The only raw card entries that changed were:bloodsprout talisman:GenericEffect/UntilEndOfTurn/Any->ApplyPerpetual/ParentTarget/ModifyCost Reduce {1}geistchanneler:GenericEffect/UntilEndOfTurn/Any->ApplyPerpetual/ParentTarget/ModifyCost Reduce {2}nightclub bouncer:GenericEffect/UntilEndOfTurn/ParentTarget->ApplyPerpetual/ParentTarget/ModifyCost Raise {2}Related perpetual-cost cards that still require separate root-cause fixes, such as
Chronicler of Worship,Garruk, Wrath of the Wilds,Lumbering Lightshield, andThought Rattle, were intentionally unchanged.coverage-parse-diffreports noparse_detailschanges because this PR changes the serialized effect shape rather than the coverage detail text.Verification
cargo fmt --all./scripts/check-parser-combinators.shRUSTC_WRAPPER= ZIG_GLOBAL_CACHE_DIR=/tmp/phase-zig-global-cache ZIG_LOCAL_CACHE_DIR=/tmp/phase-zig-local-cache cargo test -p engine --features cli --lib perpetual_modify_costRUSTC_WRAPPER= ZIG_GLOBAL_CACHE_DIR=/tmp/phase-zig-global-cache ZIG_LOCAL_CACHE_DIR=/tmp/phase-zig-local-cache cargo clippy -p engine --all-targets -- -D warningsorigin/main: changed exactlybloodsprout talisman,geistchanneler, andnightclub bouncerRUSTC_WRAPPER= ZIG_GLOBAL_CACHE_DIR=/tmp/phase-zig-global-cache ZIG_LOCAL_CACHE_DIR=/tmp/phase-zig-local-cache cargo run -p engine --features cli --bin card-data-validate -- /tmp/phase-root19-full/head/card-data.jsonRUSTC_WRAPPER= ZIG_GLOBAL_CACHE_DIR=/tmp/phase-zig-global-cache ZIG_LOCAL_CACHE_DIR=/tmp/phase-zig-local-cache cargo run -p engine --features cli --bin coverage-parse-diff -- /tmp/phase-root19-full/base/card-data.json /tmp/phase-root19-full/head/card-data.json --base-sha origin/main --markdown /tmp/phase-root19-full/coverage-parse-diff.md --json /tmp/phase-root19-full/coverage-parse-diff.json