Apparently I needed to ask to see the answer: return a RedirectResponse from the finish callback:
public static function finished($success, $results, $operations) { if ($success) { // Here we do something meaningful with the results. $message = t("@count tasks were done.", array( '@count' => count($results), )); drupal_set_message($message); } return new RedirectResponse('/job/results'); } As far as I can tell this is an undocumented behavior.
From the helpful comment below, you can also batch_rediect on the batch array that's either a string or Url object:
$batch = [ 'title' => $this->t('job running'), 'init_message' => $this->t('starting job'), 'operations' => $ops, 'finished' => '\Drupal\my_module\MyClass::finished', 'batch_rediect' => '/job/results', ];