Getting posts from some categories plus some individual posts

The problem here is that WP_Query requires the posts returned to be both in your lists of post IDs and in one of your categories. The SQL query generated by your code looks something like this:

SELECT SQL_CALC_FOUND_ROWS wp_posts.ID FROM wp_posts INNER JOIN wp_term_relationships ON (wp_posts.ID = wp_term_relationships.object_id) WHERE 1=1 AND wp_posts.ID IN (8200,3581,38,1562,7613) AND ( wp_term_relationships.term_taxonomy_id IN (28,34) ) AND wp_posts.post_type="post" AND (wp_posts.post_status="publish") GROUP BY wp_posts.ID ORDER BY wp_posts.post_date DESC LIMIT 0, 10

While what you would need in order to run your query is this:

SELECT SQL_CALC_FOUND_ROWS wp_posts.ID FROM wp_posts INNER JOIN wp_term_relationships ON (wp_posts.ID = wp_term_relationships.object_id) WHERE 1=1 AND (( wp_term_relationships.term_taxonomy_id IN (28,34) ) OR wp_posts.ID IN (8200,3581,38,1562,7613)) AND wp_posts.post_type="post" AND (wp_posts.post_status="publish") GROUP BY wp_posts.ID ORDER BY wp_posts.post_date DESC LIMIT 0, 10

In my opinion the most elegant solution would be to remove the posts__in keyword from your query, and inject the proper SQL (that is the wrapped OR distinction between your category and post queries), so that your final SQL statement matches the one above, like so:

add_filter( 'posts_where' , 'my_posts_where' );
function my_posts_where( $where ) {
    $where = ... // code to inject the OR statement and wrap 
    return $where;
}
query_posts( array ( 'category__in' => array( 28, 34 )) ); 
remove_filter( 'posts_where' , 'my_posts_where' );

That probably involves a fair bit of string mangling, so an easier solution might be to simply query $wpdb directly like this:

global $wpdb;
$sql = $wpdb->prepare("SELECT SQL_CALC_FOUND_ROWS $wpdb->posts.ID FROM $wpdb->posts INNER JOIN $wpdb->term_relationships ON ($wpdb->posts.ID = $wpdb->term_relationships.object_id) WHERE 1=1 AND (( $wpdb->term_relationships.term_taxonomy_id IN (28,34) ) OR $wpdb->posts.ID IN (8200,3581,38,1562,7613)) AND $wpdb->posts.post_type="post" AND ($wpdb->posts.post_status="publish") GROUP BY $wpdb->posts.ID ORDER BY $wpdb->posts.post_date DESC LIMIT 0, 10");
$posts = $wpdb->query($sql);

Off course, this solution will return a posts list, and not an iterable WP_Query object. If you need to use this in a template query the posts_where filter function described above is probably the best solution.