architecture-refactor
The store that had never been tested
pvpGameState.svelte.ts had never had a single unit test of its own. Turning it into an injectable factory meant answering, before touching it, whether Svelte 5 runes would even tolerate the shape.
Eight strings, one function
A five-level nested ternary picked the caption for how a PvP game ended. The only way to exercise all eight of its outcomes was rendering the whole page.
Load-bearing in one mode
Both duel pages held their own copy of the same reveal-timing math. One input to the shared version looks redundant for AI duel and is anything but for PvP.
The comment that confessed
A second architecture review found duplication a module's own doc comment had already admitted to, describing itself as a mirror of logic living somewhere else.
The plan had three slots
screenshot.ts had grown to 626 untested lines drawing both share images and the board underneath them. The obvious three-way split needed a fourth module once someone actually drew the boundary.
Verbatim, not cleaned up
GameEngine.ts held two unrelated engines in one 621-line file. The split needed no design round at all, just a careful move and one very literal constraint.
The deletion test
GameRecap and DuelRecap still carried the same state machine twice. Four candidate shells, judged by one question: does deleting it reduplicate the machine back into both callers?
Descriptors, not strings
The PvP lives-teaching hints get their own pure reducer. Four candidate designs, decided on two separate axes, one of them a rule the sync-engine round had already settled.
The design with no second copy
Four candidate shapes for pulling PvP sync logic out of an 898-line Svelte store. Two of them carried a duplicate copy of the game state; the winner needed none.
Illegal states, unrepresentable
An architecture review found both duel pages still carrying the same ninety-line derivation chain by hand. The fix collapses sixteen flat props into one discriminated model.