The same review's third pick was smaller in scope but not in tangle: the PvP store's lives-teaching hints, the toast when a heart spawns, the overlay explaining a first collision, the warning at one life left, were detection, once-per-game suppression, and localized copy all mixed into the same block, backed by three separate module-level flag fields that could each persist independently on the same turn.

Four candidate shapes went up for the extraction, splitting along two different questions rather than one. The first was where localized copy gets resolved: inside the reducer itself, structurally typed through from the store without a real import, or kept out of the reducer entirely. The second, orthogonal to the first, was whether the reducer stays a plain function or becomes a small stateful session object that holds its own flags between turns.

Resolving copy inside the reducer looked the simplest on paper, but it meant either giving an engine module an import path to `ui.ts` that no other engine module in the codebase has, or moving PvP's hint strings out of `ui.ts` and into the engine, both of which fight a convention the project already keeps deliberately. The design that won instead emits typed descriptors, four kinds, each carrying only the facts a caller needs, with the perspective math already resolved so the store never branches on which side an event concerns:

```typescript
export type LivesTeachingEvent =
  | { channel: 'toast'; kind: 'heart-spawned' }
  | { channel: 'hint'; kind: 'first-heart' }
  | { channel: 'hint'; kind: 'first-collision'; side: PvpSide; viewerIsLoser: boolean }
  | { channel: 'hint'; kind: 'last-life'; side: PvpSide; viewerIsLoser: boolean };
```

The stateful-session candidate lost for a reason that had nothing to do with lives hints specifically. Giving the reducer its own held flag state would have converged it with the pattern the local AI duel already uses, a real point in its favor, but [the sync-engine extraction from the same review had just spent its whole design round ruling out exactly this shape](/posts/the-design-with-no-second-copy): a module holding a copy of state the store already owns, that a future turn could let drift out of sync. The stateless version stayed close enough to that shape that it could still become a session wrapper later if the custody ever turned out to be worth it; going stateful now would have meant redesigning it to get there.

Three separate flag fields collapsed into one held value, and the store's own switch over event kinds replaced whatever ad hoc branching used to decide toast from overlay. Persistence dropped from as many as three writes a turn to at most one, since the reducer returns the same flag value by reference whenever nothing actually changed, and a test pins that reference-equality contract directly rather than trusting it to hold by convention. Twenty-one new state-pair tests cover the four event kinds across both roles and both sides, on top of the existing PvP end-to-end suite, and nothing about what a player sees or when a hint fires moved.