a library relocates the bug
updated May 29, 2026
Mahjong scoring is the genuinely hard part of a mahjong app (dozens of yaku, fu edge cases, exact point tables), so riichi doesn’t compute it. Scoring goes to a Rust/WASM library validated against millions of real hands. Obviously the right call. But it didn’t delete my scoring bugs; it moved all of them to the calling boundary.
Ron didn’t fire for weeks because I passed 14 tiles where the library wanted 13 (the winning tile goes in its own field), and a broad catch swallowed the exception, so it looked like the hand just didn’t win. A closed tsumo lost its pinfu because the library reads the last tile as the drawn one and I wasn’t building hands that way. Ura-dora came out mislabelled because the library has one dora bucket and I folded both kinds into it. Three bugs, none of them in the library, all in the thin layer where my types meet its API.
So that’s where the engineering went. Tiles are encoded in the library’s own 1–34 ordering, so there’s no translation layer to mis-map an honour tile. And the golden tests run the real WASM against real hands — no mock, because mocking the scoring engine would hide exactly the bugs that actually happen. The bug was never the math. It was the handshake.
the hub · warm terminal