fix(parser): UNSUPPORTED cluster: Land/permanent STATIC-STRUCTURE parsing — static_structure gap o#4694
Conversation
…sing — `static_structure` gap o
…-unsupported-cluster-land-permanent
|
Warning You have reached your daily quota limit. Please wait up to 24 hours and I will start processing your requests again! |
Parse changes introduced by this PR · 4 card(s), 7 signature(s) (baseline: main
|
matthewevans
left a comment
There was a problem hiding this comment.
[HIGH] The new NamedChoice field leaves a workspace crate fixture uncompilable. Evidence: crates/manabrew-compat/src/lib.rs:2338 and crates/engine/src/types/game_state.rs:3474. Why it matters: manabrew-compat is included by workspace members = ["crates/*"], so this initializer is now missing required field persist_player and the workspace will not compile. Suggested fix: add persist_player: None to that WaitingFor::NamedChoice test fixture.
Reviewed current head 357eead3076a765996d6ca84555a8838539c491b.
Summary
Fixes a parser misparse affecting 2 card(s) in the Doctor Who Commander precons.
Root cause: UNSUPPORTED cluster: Land/permanent STATIC-STRUCTURE parsing —
static_structuregap on lands/locations with continuous abilitiesCards corrected
Fix
Implemented the reviewed cluster-81 plan across all three workstreams on the worktree (upstream/main @ 8da56cc, much newer than the plan's base). I first re-verified both cards still misparse on current main (Singing Towers: static_structure Unimplemented; Two Streams: two static_structure Unimplemented + a "who" Unimplemented chaos trigger), then implemented the full plan.
W0 (shared command-zone static-source admission): added game::functioning_abilities::object_sources_static_from_command_zone (single admission authority for emblems, face-up conspiracies, and active planes/phenomena via active_zones.contains(Command)); threaded it through static_source_index.rs and both command-loop gates in layers.rs::for_each_static_effect_source; switched static_abilities::additional_land_drops from battlefield_active_statics to game_active_statics so command-zone plane land-drop statics are seen. synthesize_planechase already stamps [Command] (verify-only).
Workstream A (Two Streams — per-player persistent "last chose "): added Player.chosen_attributes (reuses ChosenAttribute::Label) + game::players::player_last_chose_label single authority; added WaitingFor::NamedChoice.persist_player routing (set from ability.scoped_player during player_scope fan-out), threaded through effects/choose.rs (resolve raise site, bind_named_choice dual-destination that writes to the player NOT the object, resolve_random_in_chain=None) and engine_resolution_choices.rs; added typed variants TargetFilter::PlayerWhoChoseLabel, FilterProp::ControllerChoseLabel, Effect::SwapChosenLabels + new game/effects/swap_chosen_labels.rs resolver; parser: player-scope land-drop subject + rule_static_affected_is_player_scope allow-list, "controlled by players who last chose " anthem subject, strip_each_player_subject "who last chose" bail + parse_swap_chosen_labels registered in parse_effect_clause_inner; static_filter_matches explicit arm before the fail-open catch-all; matches_filter_prop arm + all FilterProp/TargetFilter classifier sites.
Workstream B (Singing Towers — derived-cost off-zone keyword grant): parameterized ContinuousModification::AddKeywordWithDerivedCost { kind: CostBearingKeywordKind, derivation: CostDerivation } (NOT a foretell-only leaf) covering the plain-ManaCost CR-702 off-zone family (Foretell/Madness/Disturb/Mayhem/Dash/Unearth); added CostBearingKeywordKind (mirrors DynamicKeywordKind, with with_cost/from_name/matches_keyword), CostDerivation::ManaCostReducedBy + ManaCost::reduced_generic_by; off-zone applier arm (threads recipient object_id, per-recipient "without " dedup) + supports_off_zone_keyword_query; layer()/apply_continuous_effect_filtered/b_changes_abilities registration; routed foretell_cost through effective_off_zone_keywords across all three call sites (can_foretell_card, handle_foretell, grant_permission compute-before-mut-borrow); parser parse_hand_cards_have_derived_cost_keyword.
Both cards now parse with zero Unimplemented/Unknown (verified via fresh oracle-gen + gen-card-data jq gap check). Added building-block tests (admission helper, mana reduction, keyword family, bind_named_choice routing, swap resolver, parser tests) and TWO runtime card-tests in planechase_tests.rs proving the command-zone statics apply end-to-end (green-anchor land drop, red-waterfall +2/+0 haste anthem, derived-cost foretell {2}{U}{U}).
Verification (worktree not under Tilt, cargo run directly): cargo fmt clean; cargo clippy -p engine -p phase-ai --all-targets 0 warnings; cargo test -p engine 14410 passed 0 failed (all integration binaries green); cargo test -p phase-ai green; cargo check -p engine-wasm clean (tsify boundary); frontend pnpm type-check + lint green (0 errors); parser diff gate PASS (zero string-dispatch in added non-test parser lines).
Files changed
CR references
Verification
cargo fmt --all— pass (no files changed, nothing to commit)check-parser-combinators.sh (upstream/main merge-base 8e3e928c)— pass (exit 0)cargo clippy -p engine --all-targets -- -D warnings— pass (exit 0, no warnings; all 51 changed files clippy-clean)cargo test -p engine— pass (136 test-result-ok blocks, zero failures)oracle-gen data --filter 'singing towers of darillium|two streams facility'— pass (both cards fully parsed; no Unimplemented effects / Unknown trigger modes)Cards confirmed re-parsed correctly: singing towers of darillium, two streams facility
🤖 Generated with Claude Code