import InfoBox from '../components/InfoBox.astro';
import PullQuote from '../components/PullQuote.astro';

Nine pull requests merged in two days, and none of them was meant to change the game. The goal was to make one rule have one home, give the stores less knowledge, and put the risky parts of the engine behind interfaces that could be tested directly. The earlier [seam-carving pass](/posts/carving-seams#naming-the-seams) had shown that naming shared logic could expose real bugs. This pass turned that lesson into a method.

## One seam per pull request

Each issue began with the same question: if this module disappeared, would its complexity disappear as well, or would it scatter back into its callers? The author used that deletion test to settle the boundary, often after a grilling session over two or three plausible interfaces. The agent then implemented one boundary in one pull request, with a focused test suite and the full project checks behind it. Combo knowledge stopped being reconstructed across several files, while the daily and duel stores stopped carrying separate copies of the submission sequence. About 150 duplicated lines became one pipeline, but the opponent turn stayed outside because it skips validation, notices, and hints. Similar code was not enough reason to erase a real behavioral difference.

<InfoBox title="The verification line kept moving">
  The first pull request completed with 715 tests passing. The ninth completed with 756. Every seam
  also passed type checking and a production build before merge, so the refactor accumulated direct
  coverage without spending the existing regression suite.
</InfoBox>

## Put the rule where the decision is made

The strongest extractions moved knowledge inward rather than moving lines sideways. Retroactive combo tags now come from the engine that detects the combo, instead of being reconstructed later by both stores. The [word preview](/posts/word-preview#walking-the-snake-before-it-moves) now delegates movement to the same snake logic as the real turn, removing a second implementation of tail movement and collision. The same principle governs the fruit lifecycle and apple queue: callers provide facts and receive results, while the ordering and fallback rules stay behind one interface.

<PullQuote>A seam is useful when the caller can stop knowing why the rule works.</PullQuote>

## Stop where the boundary is real

The review did not turn every large file into a folder. The daily store's public interface was split into game, submission, and UI surfaces while all three kept sharing one closure in the same file. The duel store was the harder case: network fetching, opponent timing, engine application, and two animation queues were intertwined, and a full rewrite would have made the seam harder to trust. After review, only the opponent-turn flow moved into a pure engine with injected fetch and clock adapters. Five direct tests cover success, timing, slow responses, network failure, and rejected moves, while animation sequencing remains where it already belongs.

## A sprint, not a permanent activity

By the end of the second day, the suite had 41 more tests and the stores had fewer reasons to know how the engine reaches its answers. That is enough for this pass. The point is not to keep extracting until every function has a module name, but to return to feature work with clearer places to make the next change, and to run the deletion test again when a rule starts appearing in two places.