Order By Multiple Meta Fields

One problem with the query is that the self-join with the ambiguous WHERE gives you a crossed dataset (which is masked by the DISTINCT), so it would be simpler to use wp_post as a base to attach the joins that precisely match the keys, eg

    SELECT      p.ID, key1.meta_value as prog_ongoing, key2.meta_value as prog_date_start
    FROM        $wpdb->posts p
    INNER JOIN  $wpdb->postmeta key1
                ON key1.post_id = p.ID
                AND key1.meta_key = %s
    INNER JOIN  $wpdb->postmeta key2
                ON key2.post_id = p.ID
                AND key2.meta_key = %s

which gives a linear dataset. Then you could add (or not) the WHERE clause to restrict the data:

    WHERE       key1.meta_value IS TRUE OR key2.meta_value >= %d

and for the ORDER BY use a CASE statement single field sort:

    ORDER BY    CASE
                    WHEN key2.meta_value >= %d THEN CONCAT('A', key2.meta_value)
                    WHEN key1.meta_value AND key2.meta_value THEN CONCAT('B', key2.meta_value)
                    WHEN key1.meta_value THEN 'C'
                    ELSE 'D'
                END ASC

or something similar, the above needing the prepare args to be:

    $meta_key1,
    $meta_key2,
    $start_date, $start_date

You could use the posts_orderby filter to do something similar using WP_Query (although it uses a method that produces crossed datasets, requiring it to have to use a GROUP BY, which can complicate things). For instance

$args = array(
    'posts_per_page' => -1,
    'post_type' => 'cpt_program',
    'meta_query' => array(
        'ongoing' => array(
            'key' => 'prog_ongoing',
        ),
        'start_date' => array(
            'key' => 'prog_date_start',
        )
    ),
);

add_filter( 'posts_orderby', $func = function ( $orderby, $query ) {
    $start_date = date('Ymd');
    global $wpdb;
    $orderby = $wpdb->prepare(
        "
        CASE
            WHEN mt1.meta_value >= %d THEN CONCAT('A', mt1.meta_value)
            WHEN {$wpdb->postmeta}.meta_value AND mt1.meta_value THEN CONCAT('B', mt1.meta_value)
            WHEN {$wpdb->postmeta}.meta_value THEN 'C'
            ELSE 'D'
        END ASC
        "
        , $start_date
    );
    return $orderby;
}, 10, 2 );
$query = new WP_Query( $args );
remove_filter( 'posts_orderby', $func, 10, 2 );