`Cooldowns::add` appended a record for every tracked insert and never dropped one. Only `prune` removed fully elapsed records, and it ran on the player's list alone. So the pet's list kept a record for every pet GCD and every pet `SMSG_SPELL_GO` recovery for the whole session, and each pet-bar and pet-book read walked all of them.
`add` now prunes fully elapsed, not-on-hold records before it pushes. The list is bounded by its live records plus the one being added. This holds for the pet's list and the player's alike, without a new system or any schedule change.
The prune keeps its contract:
- it moves `expiry_epoch`, never `generation`, so the pet list's flush fires once for the insert and nothing for the prune;
- reads skip elapsed records already, so no reader sees a difference;
- the player's per-frame prune in `ui_action/state.rs` stays, for an expiry that arrives with no insert.
Tests:
- `an_insert_bounds_the_list_by_its_live_records_plus_itself` checks the bound after each of 30 inserts on a synthetic clock.
- `an_insert_keeps_a_running_and_a_parked_record_and_changes_no_read`.
- In the pet rig, after 40 elapsed pet presses, one more press leaves only the live records. That insert fires its one flush, and the bar's reads of the records that stay are unchanged.
Fixes#656🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>