- Rename course/11-break-your-engine to 13-break-your-engine and update every
link and mention; start-module explains that Module 13 has no tests to start.
- tools/diff-tracks.mjs (npm run diff:tracks): stages both references of a
module, generates a seeded random SpellGrid level and script per seed, pipes
it to the Rust and TypeScript CLIs and diffs their output; crashes on one or
both sides fail. stagePlay takes a module number.
- register_assembled: the TypeScript track compared only the text of an
existing Assembled card, so a base re-registered with a new cost (or a
different split) silently rewrote the earlier card. It now compares every
field the engine reads, as Rust does (Modules 5-10).
New test_05_assemble_never_rewrites_an_earlier_card.
- SpellGrid CLIs: coordinates and chooser numbers are 1-9 plain digits in both
tracks (Rust accepted signs, TypeScript accepted "+2", "0x2", "1e0"); Rust
quotes card names verbatim instead of with Debug escapes; Rust joins the
minion list with "; " instead of rewriting ", ". Two new CLI tests per track.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A Deathrattle summon used the dying minion's board index from before the
Death Creation Step removed anything. That index is stale when a minion to
its left dies in the same step, so the token landed one place too far right.
Each Death Event now records `before`: the nearest minion to the right that
is not dying in the same step. The position handed to the effect context is
resolved when the trigger resolves: the current index of `before`, or the
right end. Applied to the Rust and TS references of Modules 3-10.
Module 3 gains two tests, documented in scenarios.md:
test_03_summon_position_after_simultaneous_deaths (fails before the fix) and
test_03_simultaneous_summons_keep_board_order.
Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
Adds IS NOT to the Part II parser and a resolution pass (negation, then
identity lock) that turns parsed rules into the rules in force; harness
gains activeRules() and hasProperty(). A leading NOT now breaks the
sentence instead of being ignored (carried from M6 review). Nouns say
which objects they name ("selects"), so the ALL Expansion Drop is one
line of data. Corrects lesson 06's NOT wording; harness.md M7 details.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>