Can we change the hook firing sequence?

It’s a bit of a hack, but you could

  • create a new member in your $plugin_admin_listings class that’s an array of post IDs published in this page cycle
  • in your transition_post_status add the IDs to that array
  • in save_meta_data as you save each post check if its ID is in the published_posts array and then send the email then

You probably need an array not a single ID in case this can be triggered from an admin bulk action (I’d guess so?). However this falls apart though if the two hooks run in separate page loads (I’d guess they don’t though). If that was the case you could e.g. add two post_meta timestamps

  • last published timestamp (set in transition post status)
  • last published email sent timestamp (set when you send the email)

In your save_post hook you could check if the first is after the second, and then send a new email. (And it’s also possible one of the existing dates on the WP_Post object would work too, but I forget precisely when they’re set.)