How to handle / cleanup obsolete aggregated css / js?
This is actually by design so that cached pages with older versions of files don't get broken. See this closed issue.

TL;DR: They will be deleted automatically 30 days (or whatever your drupal_stale_file_threshold variable is set to) after they were created via drupal_clear_css_cache() and drupal_clear_js_cache(). So the solution is to modify the drupal_stale_file_threshold value to something lower than the default 30 days.


When are old files deleted


  Old cache files are not deleted immediately when the lookup variable is emptied, but are deleted after a set period by drupal_delete_file_if_stale(). This ensures that files referenced by a cached page will still be available.
  
  drupal_delete_file_if_stale() defaults to 30 days - so if a) Cron is running properly and b) you see aggregated files older than 30 days, you have a different problem.


variable_get('drupal_stale_file_threshold', 2592000) is the 30 day check.
variable_set('drupal_stale_file_threshold', 172800) would change the timeout to two days. On a site where cache handling is strictly controlled, the time could be even shorter.

Source: http://api.drupal.org/api/drupal/includes!common.inc/function/drupal_build_css_cache/7
See drupal_delete_file_if_stale() for more information.


Is there some reason both .css and .css.gz files are kept?


  If CSS gzip compression is enabled, clean URLs are enabled (which means that rewrite rules are working) and the zlib extension is available then create a gzipped version of this file. This file is served conditionally to browsers that accept gzip using .htaccess rules.
  
  Source: http://api.drupal.org/api/drupal/includes!common.inc/function/drupal_build_css_cache/7 (In the function comments)


Also see drupal_build_js_cache() which is nearly identical to drupal_build_css_cache().