Update some (not all) post titles with custom field values before running the Loop

If you are querying all posts at once with 'posts_per_page'=> -1 it would be easiest to get all posts as an array, update title if needed, and sort them after.

The disadvantage of that approach is you would have to update your loop to use properties instead of functions i.e. instead of the_title(); you would use echo $article->post_title; Example below would work without the cchs_swap_title filter and cchs_archives_orderby action.

$args = array(
        'post_type'         => 'cchs_article',
        'posts_per_page'    => -1
    );
$the_query = new WP_Query( $args );
$articles = $the_query->get_posts(); // Get all the posts as an array

// Update post title based on custom field
foreach ( $articles as $article ) {
    $customTitle = get_post_meta( $article->ID, 'custom_title', true);
    if( $customTitle ) {
        $article->post_title = $customTitle;
    }
}

// Sort all posts by title
usort($articles, function($a, $b) {
    return strcmp( $a->post_title, $b->post_title );
} );

// The loop
if ( $the_query->have_posts() ) :
    foreach ( $articles as $article ) {
        get_template_part( 'template-parts/content', 'archive-cchs_article', [ 'article' => $article ] );
    }
else :
    get_template_part( 'template-parts/content', 'none' );
endif;

In the above example inside the template part file instead of the_title() you would need to use echo $args['article']->post_title;.

The $args['article'] is the article we pass as 3rd argument in get_template_part function.