WP_Query in functions.php overrides global $post object, even with wp_reset_query()

The problem is that wp_reset_postdata attempts to restore $post from the main $wp_query, but on post.php in admin, $post isn’t populated from $wp_query, so wp_reset_postdata fails to restore it. The solution is to use get_posts and a foreach instead of WP_Query and the loop.