Skip to content

chain_tracked_set_id carries CR 104.4b loop-equality residue β€” uncleared by normalize_for_loop (partner of the #7484 mode-latch fix)Β #7514

Description

@lgray

πŸ€– AI text below πŸ€–

Summary

GameState::chain_tracked_set_id carries transient mid-chain residue into CR 104.4b loop-equality comparison: normalize_for_loop() clears its lockstep partner resolving_modal_instruction (fixed in #7484, commit 976eb923d β†’ merged 83f61d8d1) but deliberately does not clear chain_tracked_set_id. Two otherwise-identical loop states that differ only in this transient field then never compare equal, so the loop detector cannot confirm a repeated position β€” a false negative for mandatory-loop draw detection (CR 104.4b) in any loop whose sampled states sit mid-chain.

In-tree pin (merged to main)

crates/engine/src/types/game_state.rs, inside normalize_for_loop() (~line 22143 at f7c44692f):

// NOTE: its lockstep partner `chain_tracked_set_id` carries the same
// residue and is deliberately NOT cleared here β€” that is pre-existing
// behavior with its own follow-up, not something this line may widen.
clone.resolving_modal_instruction = None;

The field's own doc (game_state.rs ~15655) states it is "Cleared at the top-level chain entry (depth == 0) in resolve_ability_chain" β€” i.e. it is Some(..) precisely while a chain is mid-resolution, which is exactly when a paused/sampled loop state can be captured.

Why it was split out rather than fixed in #7484

The resolving_modal_instruction clear was a review fix scoped to the mode latch; widening it to the partner field mid-review would have changed loop-equality behavior for a field #7484 otherwise never touched. The two fields are documented as a cleared-in-the-same-prelude pair, so the residue classes are the same; the fix is the symmetric one-line clear plus a discriminating test showing a mid-chain loop state pair that compares equal only with the clear (and a negative control showing depth-0 states are unaffected, since depth-0 already resets the field).

Provenance

Found during the #6857/#7484 lane while fixing the partner field. Pre-existing; #7484 neither caused nor widened it.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions