Skip to content

Fix Lictor#7

Open
keloide wants to merge 13 commits into
mainfrom
claude/phase-developer-track-erj3c7
Open

Fix Lictor#7
keloide wants to merge 13 commits into
mainfrom
claude/phase-developer-track-erj3c7

Conversation

@keloide

@keloide keloide commented Jul 23, 2026

Copy link
Copy Markdown
Owner

Summary

Fixes the dropped intervening-"if" misparse for Lictor (Warhammer 40,000
Commander). Its Pheromone Trail — "When this creature enters, if a creature
entered the battlefield under an opponent's control this turn
, create a 3/3
green Tyranid Warrior creature token with trample" — parsed with condition: null, so the ETB trigger fired unconditionally and Lictor made a token every
time it entered, even with no opponent entry that turn.

The "under your control" surface of this class was already supported (13
cards); this adds the opponent-scoped, past-tense mirror. A new
parse_entered_this_turn_under_opponent_control combinator reuses the shared
parse_or_more_entered_count / parse_entered_this_turn_subject helpers and
carries the scope via PlayerScope::Opponent { aggregate: Max } — the
existential "an opponent" reading already documented on
parse_opponent_had_entered_this_turn — over the CR 608.2i
BattlefieldEntriesThisTurn snapshot. No new engine variant.

The opponent surface in the quantity path stays honestly Unimplemented
(no printed card uses it there — locked by bbfu10_opponent_entry_surface_stays_unimplemented);
only the condition path, which Lictor uses, is added. Removes Lictor from
docs/parser-misparse-backlog.md (root cause #2) and decrements its counts.

Misparse fix (§3.1): "Fixes the dropped intervening-if for Lictor"; backlog
entry removed (root cause #2 count 591 → 590).

Files changed

  • crates/engine/src/parser/oracle_nom/condition.rs
  • crates/engine/tests/integration/lictor_opponent_entered_this_turn.rs
  • crates/engine/tests/integration/main.rs
  • docs/parser-misparse-backlog.md

Implementation method (required)

Method: not-applicable — implemented and reviewed inline by the driving
Frontier-tier LLM (plan → nom-combinator implementation → /review-impl
[clean] → verify → commit), per the oracle-parser / add-engine-variant
skill checklists. The change is a self-contained additive parser arm reusing
existing helpers with no new engine variant, so the /engine-implementer
plan→review→implement→review→commit contract was executed directly rather than
via spawned subagents.

CR references

  • CR 603.4 — intervening-"if" clause (the authorizing rule; condition checked at
    trigger time and again on resolution)
  • CR 608.2h — information determined once, from current/last-known info
  • CR 608.2i — look-back exception (a permanent that has since left still counts)
  • CR 102.2 / CR 102.3 — opponent definition (two-player / multiplayer)

Track

Developer

LLM

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

Tier: Frontier

Verification

  • Required checks ran clean, or the exact CI-owned alternative is stated below.

  • Gate A output below is for the current committed head.

  • Final review-impl below is clean for the current committed head.

  • Both anchors cite existing analogous code at the same seam.

  • cargo fmt --all — clean (exit 0)

  • cargo clippy-strict (clippy --all-targets -- -D warnings) — Finished, 0 warnings (exit 0)

  • cargo test -p engine --lib entered_this_turn_under_opponent — 2 passed (new parser unit tests: singular + count)

  • cargo test -p engine --test integration lictor_opponent_entered — 3 passed (shape-lock + positive + negative runtime)

  • cargo test -p engine --test integration bbfu10 — 17 passed (incl. bbfu10_opponent_entry_surface_stays_unimplemented — the quantity-path opponent lock stays intact)

  • cargo test -p engine --lib parser::oracle_nom::condition — 412 passed (no regression in the touched module)

  • Revert-probe — disabling the dispatch arm makes lictor_makes_no_token_without_opponent_entry FAIL (1 token), proving the runtime test drives the real pipeline and is discriminating

  • cargo semantic-audit — Lictor no longer flagged; cards-with-findings 285 → 284; DroppedCondition 65 → 64; no new findings introduced

  • cargo coverage — exit 0; supported_cards: 31528; Lictor remains supported with its intervening-if now populated (it was already supported: true, so the count is unchanged)

Gate A

Gate A PASS head=3d7672511ca1c7a8ff566677759d2ec2e8c4a663 base=c754a087a28ce6cabb46ea5c8fa6bfa095e79008

Anchored on

  • crates/engine/src/parser/oracle_nom/condition.rs:7804 — parse_opponent_had_entered_this_turn: the opponent-scoped sibling that carries PlayerScope::Opponent { aggregate: Max } over the same parse_or_more_entered_count / parse_entered_this_turn_subject helpers (same combinator family, same scope idiom).
  • crates/engine/src/parser/oracle_nom/condition.rs:7645 — parse_entered_this_turn: the "under your control" sibling that delegates the count/subject surfaces to the identical shared helpers (the class this fix mirrors for the opponent scope).

Final review-impl

Final review-impl PASS head=3d7672511ca1c7a8ff566677759d2ec2e8c4a663

Claimed parse impact

  • Lictor

Generated by Claude Code

matthewevans and others added 4 commits July 23, 2026 15:40
* fix(desktop): show native engine provisioning status

* fix(release): recover main bundle and clarify desktop downloads

---------

Co-authored-by: matthewevans <matthewevans@users.noreply.github.com>
Co-authored-by: matthewevans <matthewevans@users.noreply.github.com>
Lictor's Pheromone Trail — "When this creature enters, if a creature
entered the battlefield under an opponent's control this turn, create a
3/3 green Tyranid Warrior creature token with trample" — dropped its
intervening-"if" condition (parsed to None), so the ETB trigger fired
unconditionally and the token was created every time Lictor entered.

The "under your control" surface of this class was already supported;
this adds the opponent-scoped, past-tense mirror. A new
`parse_entered_this_turn_under_opponent_control` combinator reuses the
shared `parse_or_more_entered_count` / `parse_entered_this_turn_subject`
helpers and carries the scope via `PlayerScope::Opponent { Max }` — the
existential "an opponent" reading already documented on
`parse_opponent_had_entered_this_turn` — over the CR 608.2i
`BattlefieldEntriesThisTurn` snapshot. No new engine variant.

The opponent surface in the QUANTITY path stays honestly Unimplemented
(no printed card); only the condition path, which Lictor uses, is added.

Adds parser unit tests (singular + count forms) and a discriminating
runtime test that casts Lictor and asserts the token is gated on an
opponent's entry (fails on revert). Removes Lictor from the parser
misparse backlog.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Gw63XPowSxhpJaS9xM8Xyj
@github-actions

github-actions Bot commented Jul 23, 2026

Copy link
Copy Markdown

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

🟡 Modified fields (1 signature)

  • 1 card · 🔄 trigger/ChangesZone · changed field condition: battlefield entries this turn (creature, max of opponents) ≥ 1
    • Affected (first 3): Lictor

10 card(s) had Oracle-text changes (errata/reprint) — excluded as non-parser.
New cards in head: 4.
Cards only in baseline: 2.

matthewevans and others added 9 commits July 23, 2026 10:41
* fix(ui): surface roll outcomes and turn order

* fix(ui): address roll overlay review feedback

* test(ui): isolate draft match provider state

* fix(tauri): synchronize release lockfile version

* fix(ci): attach migration bridge before release publish

---------

Co-authored-by: matthewevans <matthewevans@users.noreply.github.com>
Co-authored-by: matthewevans <matthewevans@users.noreply.github.com>
* fix(client): respect legal Adventure cast faces

* fix(client): preserve waiting factory variant types

* fix(client): retain concrete waiting factories

---------

Co-authored-by: matthewevans <matthewevans@users.noreply.github.com>
Co-authored-by: matthewevans <matthewevans@users.noreply.github.com>
* fix(ci): scope preview server secrets

* fix(desktop): replay native engine progress

---------

Co-authored-by: matthewevans <matthewevans@users.noreply.github.com>
phase-rs#6558)

Co-authored-by: matthewevans <matthewevans@users.noreply.github.com>
…hange hub) (phase-rs#6560)

Co-authored-by: matthewevans <matthewevans@users.noreply.github.com>
The three shape assertions matched `PlayerScope::Opponent { .. }`, which
also admits `AggregateFunction::Sum` — precisely the cross-opponent
summation the combinator's doc comment identifies as wrong and that is
only observable at three or more seats. Every test the PR adds would
still have passed if the parser were later changed to emit `Sum`.

Tighten all three sites to `{ aggregate: AggregateFunction::Max }` so the
existential "an opponent" reading is actually pinned by the test suite.

Co-authored-by: keloide <75585494+keloide@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants