Skip to content

fix(parser): parse Gilgamesh dig-from-among and active-voice reflexive attach#3743

Merged
matthewevans merged 4 commits into
phase-rs:mainfrom
andriypolanski:fix/3286-gilgamesh
Jun 18, 2026
Merged

fix(parser): parse Gilgamesh dig-from-among and active-voice reflexive attach#3743
matthewevans merged 4 commits into
phase-rs:mainfrom
andriypolanski:fix/3286-gilgamesh

Conversation

@andriypolanski

@andriypolanski andriypolanski commented Jun 18, 2026

Copy link
Copy Markdown
Contributor

Summary

Gilgamesh, Master-at-Arms was reported in Discord as fully parsed with zero coverage gaps, but the trigger AST dropped most of the Oracle text. The card only retained a bare Dig(6) with filter: Any and no reflexive attach step.

This change fixes two parser building blocks:

  1. DigFromAmong disambiguation — the bare "put N of them" arm in parse_dig_from_among incorrectly matched the substring " of them" inside "from among them", so "put any number of Equipment cards from among them" lowered to filter: Any instead of an Equipment filter with PutCount::AnyNumber.
  2. Active-voice reflexive gate — Gilgamesh uses "When you put one or more Equipment onto the battlefield this way" (CR 603.12 active voice), not the passive "If an Equipment is put onto the battlefield this way" pattern Armored Skyhunter uses. Added parse_you_put_onto_battlefield_this_way_clause and wired it through strip_if_you_do_conditional so the attach step gets a ZoneChangedThisWay { Equipment } condition and forward_result on the parent Dig.

Closes #3286

Root cause

Coverage marked the card supported because a typed Dig node existed for the trigger, but AST faithfulness checks were never run (classifier:pending). Comparing Oracle text to parse_details showed:

Oracle clause Expected AST Before fix
You may put any number of Equipment cards from among them onto the battlefield DigFromAmong { AnyNumber, Equipment, Battlefield } Missing — bare Dig, filter: Any
Put the rest on the bottom … in a random order rest_destination: Library Partially present
When you put one or more Equipment … this way, you may attach one of them to a Samurai you control Attach + ZoneChangedThisWay { Equipment }, optional Missing entirely

Files changed

  • crates/engine/src/parser/oracle_effect/sequence.rs — guard the "of them" early arm when "from among" is present; regression tests
  • crates/engine/src/parser/oracle_nom/condition.rs — new parse_you_put_onto_battlefield_this_way_clause combinator + unit test
  • crates/engine/src/parser/oracle_effect/conditions.rs — recognize "when you put … this way" as ZoneChangedThisWay
  • crates/engine/src/parser/oracle_effect/mod.rs — full-chain regression test (attach_just_moved_gilgamesh_any_number_equipment_reflexive_attach)

Expected AST after fix

Trigger EntersOrAttacks execute chain:

Dig(count=6, keep_count=MAX, up_to=true, filter=Equipment, destination=Battlefield, rest_destination=Library, forward_result=true)
  └─ Attach(attachment=ParentTarget, target=Samurai you control, optional=true)
       condition=ZoneChangedThisWay { Equipment }

Test plan

  • cargo fmt --all
  • cargo test -p engine attach_just_moved_gilgamesh_any_number_equipment_reflexive_attach
  • cargo test -p engine from_among_any_number_equipment_not_misrouted_as_of_them
  • cargo test -p engine strip_if_you_do_conditional_gilgamesh_you_put_equipment_this_way
  • cargo test -p engine test_you_put_onto_battlefield_this_way_equipment
  • cargo test -p engine put_two_of_them_into_hand_still_uses_of_them_arm
  • Regenerate card-data and confirm Gilgamesh parse_details shows Equipment filter + Attach child
  • Manual: cast Gilgamesh, trigger on enter, put Equipment from top 6, verify optional attach prompt targets Samurai you control

@andriypolanski andriypolanski changed the title fix(parser): parse Gilgamesh dig-from-among and active-voice reflexiv… fix(parser): parse Gilgamesh dig-from-among and active-voice reflexive attach Jun 18, 2026

@gemini-code-assist gemini-code-assist 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.

Code Review

This pull request introduces parsing support for active-voice reflexive trigger conditions (e.g., 'when you put one or more [type] onto the battlefield this way') and refactors the 'dig from among' parser to avoid misrouting. The review feedback points out two violations of the repository's hard architectural rules: using a substring scan (scan_contains) for parser dispatch in sequence.rs (violating R1), and using verbatim string comparison (prefix == "when ") alongside a missing CR annotation in conditions.rs (violating R6).

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.

// Must be checked BEFORE the "from among" path for bare "of them" forms, but MUST NOT
// fire when "from among them/those cards" is present — that substring also contains
// " of them" and would mis-route (Gilgamesh, Collected Company).
if !nom_primitives::scan_contains(lower, "from among") {

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.

high

Violation of R1: Substring scan used for parser dispatch. Using scan_contains for parser dispatch violates the hard architectural rule R1, which mandates using nom combinators on the first pass. Additionally, compound phrases like 'from among' should be decomposed into modular, reusable parsers for constituent parts rather than matched as a single verbatim string.

Suggested fix: Use a composed nom parser to check for 'from' followed by 'among' in a robust way.

    let mut parser = tuple((tag("from"), space1, tag("among")));
References
  1. R1. Nom combinators on the first pass — no exceptions. Every new parser dispatch under crates/engine/src/parser/ must use nom 8.0 combinators or delegate to existing helpers. Findings: any new .contains("...") used for parsing dispatch in non-test parser code. (link)
  2. Avoid verbatim string equality for parsing Oracle phrases as it bypasses the robust nom-based parser and creates fragile matches. Instead, decompose compound phrases into modular, reusable parsers for constituent parts (e.g., subjects, conjunctions) and compose them using idiomatic combinator aggregates (like nested alt and tag sequences) to prevent combinatorial explosion and improve maintainability.

Comment on lines +587 to +588
if prefix == "when " {
if let Ok((after_clause, (filter, _negated))) =

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.

medium

Violation of R6: Missing CR annotation. Every rules-touching parser implementation must carry a verified CR annotation. Additionally, avoid verbatim string equality like prefix == "when " for parsing Oracle phrases as it bypasses the robust nom-based parser. Instead, use modular, reusable parsers for constituent parts.

Suggested fix: Replace the verbatim string check with a nom-based parser (e.g., using tag) and add the CR 603.12 annotation.

        // CR 603.12: Parse active-voice reflexive trigger condition ("when you put ... this way")
        let (after_clause, (filter, _negated)) = tag("when")(input)?
References
  1. R6. CR annotations are mandatory and verified. Every rules-touching line of engine code must carry a comment of the form CR : . (link)
  2. Avoid verbatim string equality for parsing Oracle phrases as it bypasses the robust nom-based parser and creates fragile matches. Instead, decompose compound phrases into modular, reusable parsers for constituent parts (e.g., subjects, conjunctions) and compose them using idiomatic combinator aggregates (like nested alt and tag sequences) to prevent combinatorial explosion and improve maintainability.

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

Reviewed current head 194a554e03df98815d31add3ef14b5aff5fad5b9.

Gemini's parser-gate concern is confirmed on the current head, and there is also a behavioral failure in the new Gilgamesh regression.

Findings:

[MED] The new Gilgamesh full-chain parse still does not produce the advertised attach target. Evidence: cargo test -p engine gilgamesh -- --nocapture fails parser::oracle_effect::tests::attach_just_moved_gilgamesh_any_number_equipment_reflexive_attach at crates/engine/src/parser/oracle_effect/mod.rs:49883, with expected Samurai you control attach target, got ParentTarget. Why it matters: Gilgamesh needs attachment = ParentTarget for the Equipment moved this way and target = Samurai you control; as parsed now, the attach step does not target the Samurai class the Oracle text requires. Suggested fix: fix the attach-clause split for attach one of them to a Samurai you control so only the attachment side binds to ParentTarget, and keep the full-chain regression strict.

[MED] The new parser branch fails the repository's nom-combinator gate. Evidence: scripts/check-parser-combinators.sh fails on added lines in crates/engine/src/parser/oracle_effect/conditions.rs:593-596 for unannotated strip_prefix chaining in the active-voice condition branch. Why it matters: parser dispatch in this repo must be expressed with nom combinators or a justified structural escape hatch, and this branch is part of the newly-added Oracle parser surface. Suggested fix: consume the separator after this way with a small nom combinator, or add a valid allow-noncombinator annotation only if this is purely structural punctuation cleanup after the clause has already parsed.

I also want the parse_dig_from_among change kept out of substring-dispatch territory: the new !scan_contains(lower, "from among") guard at crates/engine/src/parser/oracle_effect/sequence.rs:3267 is controlling which grammar arm runs. Prefer structuring the of them arm and the from among arm as explicit nom alternatives/prefix splits so the Gilgamesh fix does not depend on a broad clause-level scan.

@matthewevans matthewevans added the bug Bug fix label Jun 18, 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.

Thanks for the quick follow-up. The parser-combinator gate issue looks addressed on this head, but the substantive Gilgamesh parser failure is still present.

I reran the targeted engine test locally at 370ef438f28159f853cf11536e9ee3eb43dfe1cb:

cargo test -p engine gilgamesh -- --nocapture

It still fails in parser::oracle_effect::tests::attach_just_moved_gilgamesh_any_number_equipment_reflexive_attach with:

expected Samurai you control attach target, got ParentTarget

So the full active-voice reflexive attach chain is still not producing the Samurai target required by the Gilgamesh wording. Please fix that parser path and keep the discriminating test strict.

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

Approved after re-review of the current head: the Gilgamesh full-chain parser regression now passes, the parser-combinator gate is clean, GitHub checks are green, and the required bug label is present.

@matthewevans matthewevans added this pull request to the merge queue Jun 18, 2026
@matthewevans matthewevans removed their assignment Jun 18, 2026
Merged via the queue into phase-rs:main with commit 562ef35 Jun 18, 2026
10 checks passed
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.

Gilgamesh Master At Arms — Showing fully parsed.

2 participants