Skip to content

fix(RS): Fix stale MBT annotation on the initial state of --mbt traces - #2012

Open
bmastenbrook wants to merge 1 commit into
quint-co:mainfrom
bmastenbrook:fix-mbt-init-metadata
Open

fix(RS): Fix stale MBT annotation on the initial state of --mbt traces#2012
bmastenbrook wants to merge 1 commit into
quint-co:mainfrom
bmastenbrook:fix-mbt-init-metadata

Conversation

@bmastenbrook

Copy link
Copy Markdown

This PR fixes #2011. When simulating with --mbt, the Rust evaluator sometimes records the initial state of a trace with mbt::actionTaken set to the composite step action's name (e.g. "step") instead of "init", and with mbt::nondetPicks populated with values no transition in the trace used. See the issue for a minimal reproduction and full analysis.

The MBT registers action_taken and nondet_picks in Storage are cleared after a state is recorded (Env::shift) and before each candidate inside any — but never at the start of a sample. A sample that ends because step evaluated to false leaves the failed attempt's values in the registers, and since track_action is first-write-wins, the next sample's init cannot relabel them: shift records that sample's initial state with the previous sample's stale metadata.

Fix

  • Rust (evaluator/src/simulator.rs): clear the metadata registers at the start of every sample in simulate_with_env, so the sample boundary is a metadata boundary regardless of how the previous sample ended. This is a no-op when --mbt is not set, and matches the TypeScript simulator, which resets its VarStorage (including MBT metadata) at the top of every run.

A regression test was added (evaluator/tests/simulator_tests.rs, with fixture evaluator/fixtures/mbt_metadata.qnt — a spec whose every sample dead-ends): it fails before this change with initial state labeled "step" instead of "init" and passes after. Manually verified with the issue's reproduction: 200/200 corrupted initial states before, 0/200 after; a spec with intermittent dead-ends goes from 91/100 to 0/100; non-initial annotations are unchanged (machine-checked against the state payloads).

  • I have read and I understand the Note on AI-assisted contributions
  • Changes manually tested locally and confirmed to work as described
    (including screenshots is helpful)
  • Tests added for any new code
  • Documentation added for any new functionality (N/A)
  • Entries added to the respective CHANGELOG.md for any new functionality

AI disclosure: This PR was developed with AI assistance (Claude). The fix, tests, and manual verification were reviewed by a human.

A sample that ends because `step` evaluated to false leaves the failed
attempt's `action_taken` and `nondet_picks` in the storage. Since
`track_action` is first-write-wins, the next sample's `init` could not
relabel them, and its initial state was recorded with the previous
sample's metadata: `mbt::actionTaken` set to the composite action's
name (e.g. "step") and `mbt::nondetPicks` holding values no executed
transition used.

Clear the metadata registers at the start of every sample, making the
sample boundary a metadata boundary regardless of how the previous
sample ended. This matches the TypeScript simulator, which resets its
VarStorage at the top of every run.

The regression test uses a fixture that dead-ends on every sample, so
every trace after a thread's first exhibits the corruption without the
reset.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@bmastenbrook
bmastenbrook force-pushed the fix-mbt-init-metadata branch from 717d54e to 5b7a850 Compare August 13, 2026 13:17
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.

--mbt traces label the initial state with a stale actionTaken/nondetPicks from a previous sample's failed step attempt (Rust backend)

1 participant