0e04e54fbf
A pet-bar spell press armed no global cooldown on the pet's list. Claw or Firebolt sent `CMSG_PET_ACTION`, and no slot on the pet bar swept, so the bar looked ready while the pet could not cast. Only a spell with its own recovery timer, such as Bite, showed a sweep. In the reference, the pet bar's spell arm (`0x4bd240` in `0x4bd1d0`) first handles two cases that skip the cooldown: - A spell that is a live aura on the pet sends the cancel and returns. - A possessed unit, or a spell with `AttributesEx4 & 0x20` (`0x4bd355`, `0x4bd35e`), takes the generic cast entry `0x6e4b60`. Every other press calls `StartGlobalCooldown 0x6e2de0(spellId, 1)` (`0x4bd367`-`0x4bd36e`) before the `0x175` send. That call applies the spell's own `StartRecoveryCategory`/`StartRecoveryTime` pair, under spell-mod op 21, into the pet's list (`0xcecaec + 0x18`). This press is the only place the pet's GCD starts, because the `SMSG_SPELL_GO` pet leg inserts none (`0x6e85f7`). Changes: - `SpellDisplay` reads `AttributesEx4` (column 10). - `PetPress::press` arms the pet list's GCD through the existing `Cooldowns::start_gcd`, with the player's modifier tables. It skips the arm for the cases above and for an unresolved record or pet object (`0x4bd2fe`, `0x4bd34f`). - The feed's cooldown diff then fires `PET_BAR_UPDATE_COOLDOWN`. In the shipped `Spell.dbc` the pair is 133/1500 for most pet spells (later Claw ranks, Bite, Lash of Pain, Torment, Suffering) and 133/1000 for Firebolt and Claw rank 1. A real-data test pins both values. Tests: - `ui_pet/press_tests.rs` presses through `PetPress::press_slot` and covers: - the GCD across the slots sharing the category, read back through `GetPetActionCooldown`; - no pair; - possessed; - `AttributesEx4 & 0x20`; - the aura cancel; - no pet object; - op 21. - `real_pet_spells_carry_the_gcd_pair_and_ex4_reads_column_10` reads the real `Spell.dbc`. Fixes #589 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>