How do I set a post slug automatically based on taxonomy?

I tried hooking in to rest_post_dispatch, and that seems to work for edits in the block editor. Only problem here is that the slug does not update in the editor, so the URL is wrong if the author clicks the link to view it after saving. A separate hook is needed to cover quick edit and bulk edit. Current working code:

/**
 * Default event recap slug
 * If has parent and is in category Recap, set slug to "recap".
 * Can't use `wp_insert_post_data` because taxonomy data isn't included, frustratingly.
 */
// Catch changes in block editor
add_filter( 'rest_post_dispatch', 'recap_slug_rest', 10, 3 );
function recap_slug_rest( $result, $server, $request ) {
    if ( 'PUT' == $request->get_method() ) {
        $post_id = $request->get_param( 'id' );
        $post = get_post( $post_id );
        if ( 'event' == $post->post_type && $post->post_parent ) {
            $terms = get_the_terms( $post_id, 'event_category' );
            foreach ( $terms as $term ) {
                if ( in_array( $term->slug, [ 'recap', 'recaps' ] ) ) {
                    wp_update_post( [ 'ID' => $post_id, 'post_name' => 'recap' ] );
                    break;
                }
            }
        }
    }
    return $result;
}
// Catch quick/bulk edit
add_action( 'save_post_event', 'default_recap_slug_save_post', 10, 3 );
function default_recap_slug_save_post( $post_ID, $post, $update ) {
    if ( defined( 'REST_REQUEST' ) ) return;
    if ( $post->post_parent ) {
        $terms = get_the_terms( $post, 'event_category' );
        foreach ( $terms as $term ) {
            if ( in_array( $term->slug, [ 'recap', 'recaps' ] ) ) {
                remove_action( 'save_post_event', 'default_recap_slug_save_post' );
                wp_update_post( [ 'ID' => $post->ID, 'post_name' => 'recap' ] );
                add_action( 'save_post_event', 'default_recap_slug_save_post', 10, 3 );
                break;
            }
        }
    }
}