if/else on custom query gives 200 OK when condition not met?

Headers are sent long before you try to alter them. Headers are sent by actions associated with get_header(), so by the time your code executes, it is too late to alter the headers. You can demonstrate this with a simple experiment. Try each of the following:

get_header(); 
status_header( 404 );

and

status_header( 404 );
get_header(); 

In a template file and watch the output with a tool like HttpFox or Wireshark (if you really want to have some fun 🙂 ).

To effectively change the headers you will need to run your logic before get_header().

I believe this will get the effect you want with fewer lines of code to boot:

/*
* (Example URL: example.com/comments/?original_post=ID)
*
* Check if HTTP variable (original_post) exists in queried URL,
* AND that it is set to some value (ID), AND that the value (ID) is a number.
*/

if( 
  array_key_exists( 'original_post', $_GET ) 
  && isset( $_GET['original_post'] ) 
  && ctype_digit( $_GET['original_post'] ) 
) {
  $query_original_post = new WP_Query( 
    array(
      'posts_per_page' => 1,
      'p'              => $_GET['original_post'],
      'post_type'      => 'any',
    ) 
  );
  if( !$query_original_post->have_posts() ) {
    global $wp_query;
    $wp_query->set_404();
    status_header( 404 );
    nocache_headers();
  } 
}

get_header();
if( !empty($query_original_post) && $query_original_post->have_posts() ) {
  while ( $query_original_post->have_posts() ) {
    $query_original_post->the_post();
    // [...]
  }
  wp_reset_postdata();
} else {
  get_template_part( '404', 'archive' );
}
get_footer();

And for the love of all that is holy please don’t use PHP opening and closing tags unless you actually need them.