CI / Tool tests and sync-check (push) Successful in 16s
CI / Verify references (all modules, Rust and TS) (push) Successful in 2m4s
CI / Verify Expansion Drop solutions (all modules, Rust and TS) (push) Successful in 2m1s
CI / Coupling audit on references (push) Successful in 5s
CI / Differential fuzzing of the Rust and TS references (300 seeds) (push) Successful in 53s
CI / Mutation check (tools/mutants.json against the highest TS reference) (push) Successful in 34s
CI / Verify the SpellGrid level proofs (solver) (push) Successful in 5m25s
CI / Site build (push) Successful in 15s
CI / Naive engine tests (Module 0) (push) Successful in 20s
I1: run-scenario and both SpellGrid CLIs check moves first and treat any other engine error as fatal (same text in both tracks); trigger blocks close on error (try/finally, catch_unwind in Rust); unknown summons rejected. I2, I3, M1-M7, T3, T9a-c, T10a-b. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Module 12 reference engine (Rust)
The course's own engine as of the end of Module 12: Module 11's engine (Part I's card engine, Part II's grid engine with conditions, and SpellGrid joining them), with combat and the end of a turn in Part I. Every test from Modules 1–11 still passes. Module 11's reference was the starting point, so a diff between the two shows what this module adds. course/11-conditions/reference/rust/README.md and the READMEs it points to describe everything else.
cards/andlevels/(at the crate root; not in this directory): staged bytools/verify-references.mjsas for Module 11, pluscourse/12-combat/cards/(Test Bell).- Part I (
src/engine/):combat.rs(new):attack_error(world, attacker, defender), the legality rules (a minion in play attacks an enemy character in play, with more than 0 Attack, and a Taunt minion if the defender's side has one), andTAUNT.card.rs:Card::keywords, compiled from keyword clauses.TriggerOn::EndOfTurnnow fires.event.rs:Event::TurnEnded { player }, which end of turn triggers answer. The course's log doesn't show it.queue.rs:Action::Damagecarrieshits, each a target, an amount and aPriority(hits(targets, amount)builds the usual one-amount list).reacttakes the priority of each event's entries and sorts its Queue by priority, highest first, keeping the order it was filled in within one priority.Action::Attackbuilds one Damage action: the attacker's Attack to the defender at priorityDEFENDER, and the defender's Attack (if more than 0) to the attacker atNORMAL.Action::EndTurnrecordsTurnEndedand resolves the triggers that answer it.
src/harness.rs:order_attack(attacker, defender) -> Result<(), String>,can_attack(attacker, defender) -> Option<String>andend_turn(player)onGame.- Part II, Part III,
src/cli.rsandsrc/bin/spellgrid.rs: unchanged from Module 11. SpellGrid doesn't attack.tools/diff-tracks.mjsuses this module's CLIs by default.
Run it with the module's tests: node tools/verify-references.mjs 12 from the course repository.