diff --git a/automatic_updates.services.yml b/automatic_updates.services.yml
index e6d01a70d67324466c04c99b79e490ee717f5d78..4850a512fd85e29b4f4e1544ae5c66d5d4e3d6ed 100644
--- a/automatic_updates.services.yml
+++ b/automatic_updates.services.yml
@@ -6,6 +6,7 @@ services:
       - '@datetime.time'
       - '@package_manager.path_locator'
       - '@event_dispatcher'
+      - '@automatic_updates.updater'
       - 24
   automatic_updates.updater:
     class: Drupal\automatic_updates\Updater
diff --git a/src/BatchProcessor.php b/src/BatchProcessor.php
index d7f046b7e523c3ef4aada1ffdddc9990d62e7ed8..6e0ac4f158ff283516e67270189b9fa419103875 100644
--- a/src/BatchProcessor.php
+++ b/src/BatchProcessor.php
@@ -39,7 +39,7 @@ class BatchProcessor {
     ];
 
     if ($error instanceof UpdateException) {
-      foreach ($error->getValidationResults() as $result) {
+      foreach ($error->getResults() as $result) {
         $messages = $result->getMessages();
         if (count($messages) > 1) {
           array_unshift($messages, $result->getSummary());
diff --git a/src/Event/PackagesAwareTrait.php b/src/Event/PackagesAwareTrait.php
deleted file mode 100644
index 49ead03861c49f1f7c0877440f94511c6169919f..0000000000000000000000000000000000000000
--- a/src/Event/PackagesAwareTrait.php
+++ /dev/null
@@ -1,27 +0,0 @@
-<?php
-
-namespace Drupal\automatic_updates\Event;
-
-/**
- * Common functionality for events which can carry desired package versions.
- */
-trait PackagesAwareTrait {
-
-  /**
-   * The desired package versions to update to, keyed by package name.
-   *
-   * @var string[]
-   */
-  protected $packageVersions;
-
-  /**
-   * Returns the desired package versions to update to.
-   *
-   * @return string[]
-   *   The desired package versions to update to, keyed by package name.
-   */
-  public function getPackageVersions(): array {
-    return $this->packageVersions;
-  }
-
-}
diff --git a/src/Event/PreStartEvent.php b/src/Event/PreStartEvent.php
deleted file mode 100644
index 02450fe055429507bfce5e80270b78f070db92eb..0000000000000000000000000000000000000000
--- a/src/Event/PreStartEvent.php
+++ /dev/null
@@ -1,33 +0,0 @@
-<?php
-
-namespace Drupal\automatic_updates\Event;
-
-use Drupal\package_manager\ComposerUtility;
-use Drupal\package_manager\Event\ExcludedPathsTrait;
-
-/**
- * Event fired before an update begins.
- *
- * This event is fired before any files are staged. Validation results added
- * by subscribers are not cached.
- */
-class PreStartEvent extends UpdateEvent {
-
-  use ExcludedPathsTrait;
-  use PackagesAwareTrait;
-
-  /**
-   * Constructs a PreStartEvent object.
-   *
-   * @param \Drupal\package_manager\ComposerUtility $active_composer
-   *   A Composer utility object for the active directory.
-   * @param string[] $package_versions
-   *   (optional) The desired package versions to update to, keyed by package
-   *   name.
-   */
-  public function __construct(ComposerUtility $active_composer, array $package_versions = []) {
-    parent::__construct($active_composer);
-    $this->packageVersions = $package_versions;
-  }
-
-}
diff --git a/src/Event/ReadinessCheckEvent.php b/src/Event/ReadinessCheckEvent.php
index 74a9190c6fa223469c9953f487ac3c7306e81871..934a5170026437306a99301d3c07e24cd6acb93e 100644
--- a/src/Event/ReadinessCheckEvent.php
+++ b/src/Event/ReadinessCheckEvent.php
@@ -2,7 +2,8 @@
 
 namespace Drupal\automatic_updates\Event;
 
-use Drupal\package_manager\ComposerUtility;
+use Drupal\automatic_updates\Updater;
+use Drupal\package_manager\Event\StageEvent;
 
 /**
  * Event fired when checking if the site could perform an update.
@@ -16,22 +17,37 @@ use Drupal\package_manager\ComposerUtility;
  *
  * @see \Drupal\automatic_updates\Validation\ReadinessValidationManager
  */
-class ReadinessCheckEvent extends UpdateEvent {
+class ReadinessCheckEvent extends StageEvent {
 
-  use PackagesAwareTrait;
+  /**
+   * The desired package versions to update to, keyed by package name.
+   *
+   * @var string[]
+   */
+  protected $packageVersions;
 
   /**
    * Constructs a ReadinessCheckEvent object.
    *
-   * @param \Drupal\package_manager\ComposerUtility $active_composer
-   *   A Composer utility object for the active directory.
+   * @param \Drupal\automatic_updates\Updater $updater
+   *   The updater service.
    * @param string[] $package_versions
    *   (optional) The desired package versions to update to, keyed by package
    *   name.
    */
-  public function __construct(ComposerUtility $active_composer, array $package_versions = []) {
-    parent::__construct($active_composer);
+  public function __construct(Updater $updater, array $package_versions = []) {
+    parent::__construct($updater);
     $this->packageVersions = $package_versions;
   }
 
+  /**
+   * Returns the desired package versions to update to.
+   *
+   * @return string[]
+   *   The desired package versions to update to, keyed by package name.
+   */
+  public function getPackageVersions(): array {
+    return $this->packageVersions;
+  }
+
 }
diff --git a/src/Event/UpdateEvent.php b/src/Event/UpdateEvent.php
deleted file mode 100644
index aef519aabbbc10334adb618f9c9d8f2db36d0800..0000000000000000000000000000000000000000
--- a/src/Event/UpdateEvent.php
+++ /dev/null
@@ -1,82 +0,0 @@
-<?php
-
-namespace Drupal\automatic_updates\Event;
-
-use Drupal\Component\EventDispatcher\Event;
-use Drupal\package_manager\ComposerUtility;
-use Drupal\package_manager\ValidationResult;
-
-/**
- * Event fired when a site is updating.
- *
- * These events allow listeners to validate updates at various points in the
- * update process.  Listeners to these events should add validation results via
- * ::addValidationResult() if necessary. Only error level validation results
- * will stop an update from continuing.
- */
-abstract class UpdateEvent extends Event {
-
-  /**
-   * The validation results.
-   *
-   * @var \Drupal\package_manager\ValidationResult[]
-   */
-  protected $results = [];
-
-  /**
-   * The Composer utility object for the active directory.
-   *
-   * @var \Drupal\package_manager\ComposerUtility
-   */
-  protected $activeComposer;
-
-  /**
-   * Constructs a new UpdateEvent object.
-   *
-   * @param \Drupal\package_manager\ComposerUtility $active_composer
-   *   A Composer utility object for the active directory.
-   */
-  public function __construct(ComposerUtility $active_composer) {
-    $this->activeComposer = $active_composer;
-  }
-
-  /**
-   * Returns a Composer utility object for the active directory.
-   *
-   * @return \Drupal\package_manager\ComposerUtility
-   *   The Composer utility object for the active directory.
-   */
-  public function getActiveComposer(): ComposerUtility {
-    return $this->activeComposer;
-  }
-
-  /**
-   * Adds a validation result.
-   *
-   * @param \Drupal\package_manager\ValidationResult $validation_result
-   *   The validation result.
-   */
-  public function addValidationResult(ValidationResult $validation_result): void {
-    $this->results[] = $validation_result;
-  }
-
-  /**
-   * Gets the validation results.
-   *
-   * @param int|null $severity
-   *   (optional) The severity for the results to return. Should be one of the
-   *   SystemManager::REQUIREMENT_* constants.
-   *
-   * @return \Drupal\package_manager\ValidationResult[]
-   *   The validation results.
-   */
-  public function getResults(?int $severity = NULL): array {
-    if ($severity !== NULL) {
-      return array_filter($this->results, function ($result) use ($severity) {
-        return $result->getSeverity() === $severity;
-      });
-    }
-    return $this->results;
-  }
-
-}
diff --git a/src/Exception/UpdateException.php b/src/Exception/UpdateException.php
index b9b604ebcd5120e8d9f469c9b0b798e20e15bf2d..55b87639578ed46a4290d9bc941477e21c19cf13 100644
--- a/src/Exception/UpdateException.php
+++ b/src/Exception/UpdateException.php
@@ -2,39 +2,10 @@
 
 namespace Drupal\automatic_updates\Exception;
 
+use Drupal\package_manager\StageException;
+
 /**
  * Defines a custom exception for a failure during an update.
  */
-class UpdateException extends \RuntimeException {
-
-  /**
-   * The validation results for the exception.
-   *
-   * @var \Drupal\package_manager\ValidationResult[]
-   */
-  protected $validationResults;
-
-  /**
-   * Constructs an UpdateException object.
-   *
-   * @param \Drupal\package_manager\ValidationResult[] $validation_results
-   *   The validation results.
-   * @param string $message
-   *   The exception message.
-   */
-  public function __construct(array $validation_results, string $message) {
-    parent::__construct($message);
-    $this->validationResults = $validation_results;
-  }
-
-  /**
-   * Gets the validation results for the exception.
-   *
-   * @return \Drupal\package_manager\ValidationResult[]
-   *   The validation results.
-   */
-  public function getValidationResults(): array {
-    return $this->validationResults;
-  }
-
+class UpdateException extends StageException {
 }
diff --git a/src/Updater.php b/src/Updater.php
index 3a18139534d56e8f980d92adcf393f0526b6259f..4df1e6b946c4c0f5c2717fc24e8f4f0900d272fb 100644
--- a/src/Updater.php
+++ b/src/Updater.php
@@ -2,13 +2,12 @@
 
 namespace Drupal\automatic_updates;
 
-use Drupal\automatic_updates\Event\PreStartEvent;
-use Drupal\automatic_updates\Event\UpdateEvent;
 use Drupal\automatic_updates\Exception\UpdateException;
 use Drupal\Core\State\StateInterface;
 use Drupal\package_manager\ComposerUtility;
+use Drupal\package_manager\Event\StageEvent;
 use Drupal\package_manager\Stage;
-use Drupal\system\SystemManager;
+use Drupal\package_manager\StageException;
 
 /**
  * Defines a service to perform updates.
@@ -79,17 +78,33 @@ class Updater extends Stage {
       $packages[$package] = $project_versions['drupal'];
     }
     $stage_key = $this->createActiveStage($packages);
-    $this->dispatchUpdateEvent(new PreStartEvent($composer, $packages));
     $this->create();
     return $stage_key;
   }
 
+  /**
+   * Returns the package versions that will be required during the update.
+   *
+   * @return string[]
+   *   The package versions, as a set of Composer constraints where the keys are
+   *   the package names, and the values are the version constraints.
+   */
+  public function getPackageVersions(): array {
+    $metadata = $this->state->get(static::STATE_KEY, []);
+    return $metadata['package_versions'];
+  }
+
   /**
    * Stages the update.
    */
   public function stage(): void {
-    $current = $this->state->get(static::STATE_KEY);
-    $this->require($current['package_versions']);
+    $metadata = $this->state->get(static::STATE_KEY, []);
+
+    $requirements = [];
+    foreach ($metadata['package_versions'] as $package => $constraint) {
+      $requirements[] = "$package:$constraint";
+    }
+    $this->require($requirements);
   }
 
   /**
@@ -117,41 +132,25 @@ class Updater extends Stage {
    *   The active update ID.
    */
   private function createActiveStage(array $package_versions): string {
-    $requirements = [];
-    foreach ($package_versions as $package_name => $version) {
-      $requirements[] = "$package_name:$version";
-    }
-
     $value = static::STATE_KEY . microtime();
-    $this->state->set(
-      static::STATE_KEY,
-      [
-        'id' => $value,
-        'package_versions' => $requirements,
-      ]
-    );
+
+    $this->state->set(static::STATE_KEY, [
+      'id' => $value,
+      'package_versions' => $package_versions,
+    ]);
     return $value;
   }
 
   /**
-   * Dispatches an update event.
-   *
-   * @param \Drupal\automatic_updates\Event\UpdateEvent $event
-   *   The update event.
-   *
-   * @return \Drupal\automatic_updates\Event\UpdateEvent
-   *   The event object.
-   *
-   * @throws \Drupal\automatic_updates\Exception\UpdateException
-   *   If any of the event subscribers adds a validation error.
+   * {@inheritdoc}
    */
-  public function dispatchUpdateEvent(UpdateEvent $event): UpdateEvent {
-    $this->eventDispatcher->dispatch($event);
-    if ($checker_results = $event->getResults(SystemManager::REQUIREMENT_ERROR)) {
-      throw new UpdateException($checker_results,
-        "Unable to complete the update because of errors.");
+  protected function dispatch(StageEvent $event): void {
+    try {
+      parent::dispatch($event);
+    }
+    catch (StageException $e) {
+      throw new UpdateException($e->getResults(), $e->getMessage() ?: "Unable to complete the update because of errors.", $e->getCode(), $e);
     }
-    return $event;
   }
 
 }
diff --git a/src/Validation/ReadinessValidationManager.php b/src/Validation/ReadinessValidationManager.php
index 2e3fe7345234646e9a467ab16c666a4d93904875..4dac6438656c6a3dbdc094f846a4ef6b3ea11d84 100644
--- a/src/Validation/ReadinessValidationManager.php
+++ b/src/Validation/ReadinessValidationManager.php
@@ -3,6 +3,7 @@
 namespace Drupal\automatic_updates\Validation;
 
 use Drupal\automatic_updates\Event\ReadinessCheckEvent;
+use Drupal\automatic_updates\Updater;
 use Drupal\automatic_updates\UpdateRecommender;
 use Drupal\Component\Datetime\TimeInterface;
 use Drupal\Core\KeyValueStore\KeyValueExpirableFactoryInterface;
@@ -50,6 +51,13 @@ class ReadinessValidationManager {
    */
   protected $pathLocator;
 
+  /**
+   * The updater service.
+   *
+   * @var \Drupal\automatic_updates\Updater
+   */
+  protected $updater;
+
   /**
    * Constructs a ReadinessValidationManager.
    *
@@ -61,14 +69,17 @@ class ReadinessValidationManager {
    *   The path locator service.
    * @param \Symfony\Component\EventDispatcher\EventDispatcherInterface $dispatcher
    *   The event dispatcher service.
+   * @param \Drupal\automatic_updates\Updater $updater
+   *   The updater service.
    * @param int $results_time_to_live
    *   The number of hours to store results.
    */
-  public function __construct(KeyValueExpirableFactoryInterface $key_value_expirable_factory, TimeInterface $time, PathLocator $path_locator, EventDispatcherInterface $dispatcher, int $results_time_to_live) {
+  public function __construct(KeyValueExpirableFactoryInterface $key_value_expirable_factory, TimeInterface $time, PathLocator $path_locator, EventDispatcherInterface $dispatcher, Updater $updater, int $results_time_to_live) {
     $this->keyValueExpirable = $key_value_expirable_factory->get('automatic_updates');
     $this->time = $time;
     $this->pathLocator = $path_locator;
     $this->eventDispatcher = $dispatcher;
+    $this->updater = $updater;
     $this->resultsTimeToLive = $results_time_to_live;
   }
 
@@ -91,7 +102,7 @@ class ReadinessValidationManager {
     else {
       $package_versions = [];
     }
-    $event = new ReadinessCheckEvent($composer, $package_versions);
+    $event = new ReadinessCheckEvent($this->updater, $package_versions);
     $this->eventDispatcher->dispatch($event);
     $results = $event->getResults();
     $this->keyValueExpirable->setWithExpire(
diff --git a/src/Validator/ComposerExecutableValidator.php b/src/Validator/ComposerExecutableValidator.php
index 0a3c3a1a4d35a6bc7c6b4ae56ffa1c776ba56a80..3b2c337b91345bcb1bde257c92e129c4f89a938b 100644
--- a/src/Validator/ComposerExecutableValidator.php
+++ b/src/Validator/ComposerExecutableValidator.php
@@ -3,7 +3,6 @@
 namespace Drupal\automatic_updates\Validator;
 
 use Drupal\automatic_updates\Event\ReadinessCheckEvent;
-use Drupal\automatic_updates\Event\UpdateEvent;
 use Drupal\package_manager\ValidationResult;
 use Drupal\Core\Extension\ExtensionVersion;
 use Drupal\Core\StringTranslation\StringTranslationTrait;
@@ -50,10 +49,10 @@ class ComposerExecutableValidator implements EventSubscriberInterface, ProcessOu
   /**
    * Validates that the Composer executable can be found.
    *
-   * @param \Drupal\automatic_updates\Event\UpdateEvent $event
+   * @param \Drupal\automatic_updates\Event\ReadinessCheckEvent $event
    *   The event object.
    */
-  public function checkForComposerExecutable(UpdateEvent $event): void {
+  public function checkForComposerExecutable(ReadinessCheckEvent $event): void {
     try {
       $this->composer->run(['--version'], $this);
     }
diff --git a/src/Validator/CoreComposerValidator.php b/src/Validator/CoreComposerValidator.php
index 95b02b39b32bfe687d6d4f42593612cbb93aa556..26084650968cf2bcb78155c2ea42ce247d3510f2 100644
--- a/src/Validator/CoreComposerValidator.php
+++ b/src/Validator/CoreComposerValidator.php
@@ -25,7 +25,7 @@ class CoreComposerValidator implements EventSubscriberInterface {
     // If neither is, then core cannot be updated, which we consider an error
     // condition.
     $core_requirements = array_intersect(
-      $event->getActiveComposer()->getCorePackageNames(),
+      $event->getStage()->getActiveComposer()->getCorePackageNames(),
       ['drupal/core', 'drupal/core-recommended']
     );
     if (empty($core_requirements)) {
diff --git a/src/Validator/DiskSpaceValidator.php b/src/Validator/DiskSpaceValidator.php
index b782d571cf1940ec8b242f978644cb57fd9baf7a..2675a771bdfea788ed5df8685946652048bbc5d8 100644
--- a/src/Validator/DiskSpaceValidator.php
+++ b/src/Validator/DiskSpaceValidator.php
@@ -3,7 +3,6 @@
 namespace Drupal\automatic_updates\Validator;
 
 use Drupal\automatic_updates\Event\ReadinessCheckEvent;
-use Drupal\automatic_updates\Event\UpdateEvent;
 use Drupal\package_manager\ValidationResult;
 use Drupal\Component\FileSystem\FileSystem;
 use Drupal\Component\Utility\Bytes;
@@ -100,10 +99,10 @@ class DiskSpaceValidator implements EventSubscriberInterface {
   /**
    * Checks that there is enough free space to perform automatic updates.
    *
-   * @param \Drupal\automatic_updates\Event\UpdateEvent $event
-   *   The update event object.
+   * @param \Drupal\automatic_updates\Event\ReadinessCheckEvent $event
+   *   The event object.
    */
-  public function checkDiskSpace(UpdateEvent $event): void {
+  public function checkDiskSpace(ReadinessCheckEvent $event): void {
     $root_path = $this->pathLocator->getProjectRoot();
     $vendor_path = $this->pathLocator->getVendorDirectory();
     $messages = [];
diff --git a/src/Validator/PendingUpdatesValidator.php b/src/Validator/PendingUpdatesValidator.php
index bc891529be06549fe479665be14caf093107f02b..f82c2793b0766288028a32e1e4e137b4c5160032 100644
--- a/src/Validator/PendingUpdatesValidator.php
+++ b/src/Validator/PendingUpdatesValidator.php
@@ -2,9 +2,9 @@
 
 namespace Drupal\automatic_updates\Validator;
 
-use Drupal\automatic_updates\Event\PreStartEvent;
 use Drupal\automatic_updates\Event\ReadinessCheckEvent;
-use Drupal\automatic_updates\Event\UpdateEvent;
+use Drupal\package_manager\Event\PreCreateEvent;
+use Drupal\package_manager\Event\StageEvent;
 use Drupal\package_manager\ValidationResult;
 use Drupal\Core\StringTranslation\StringTranslationTrait;
 use Drupal\Core\StringTranslation\TranslationInterface;
@@ -52,10 +52,10 @@ class PendingUpdatesValidator implements EventSubscriberInterface {
   /**
    * Validates that there are no pending database updates.
    *
-   * @param \Drupal\automatic_updates\Event\UpdateEvent $event
-   *   The update event.
+   * @param \Drupal\package_manager\Event\StageEvent $event
+   *   The event object.
    */
-  public function checkPendingUpdates(UpdateEvent $event) {
+  public function checkPendingUpdates(StageEvent $event): void {
     require_once $this->appRoot . '/core/includes/install.inc';
     require_once $this->appRoot . '/core/includes/update.inc';
 
@@ -77,7 +77,7 @@ class PendingUpdatesValidator implements EventSubscriberInterface {
    */
   public static function getSubscribedEvents() {
     return [
-      PreStartEvent::class => 'checkPendingUpdates',
+      PreCreateEvent::class => 'checkPendingUpdates',
       ReadinessCheckEvent::class => 'checkPendingUpdates',
     ];
   }
diff --git a/src/Validator/UpdateVersionValidator.php b/src/Validator/UpdateVersionValidator.php
index a4b5eddba16bb2eca2046f33b498d755f6cba638..18941bc1bbe598c2704434df2627ea86f3f87c33 100644
--- a/src/Validator/UpdateVersionValidator.php
+++ b/src/Validator/UpdateVersionValidator.php
@@ -3,9 +3,10 @@
 namespace Drupal\automatic_updates\Validator;
 
 use Composer\Semver\Semver;
-use Drupal\automatic_updates\Event\PreStartEvent;
 use Drupal\automatic_updates\Event\ReadinessCheckEvent;
-use Drupal\automatic_updates\Event\UpdateEvent;
+use Drupal\automatic_updates\Updater;
+use Drupal\package_manager\Event\PreCreateEvent;
+use Drupal\package_manager\Event\StageEvent;
 use Drupal\package_manager\ValidationResult;
 use Drupal\Core\Extension\ExtensionVersion;
 use Drupal\Core\StringTranslation\StringTranslationTrait;
@@ -47,17 +48,37 @@ class UpdateVersionValidator implements EventSubscriberInterface {
   /**
    * Validates that core is not being updated to another minor or major version.
    *
-   * @param \Drupal\automatic_updates\Event\PreStartEvent|\Drupal\automatic_updates\Event\ReadinessCheckEvent $event
+   * @param \Drupal\package_manager\Event\StageEvent $event
    *   The event object.
    */
-  public function checkUpdateVersion(UpdateEvent $event): void {
+  public function checkUpdateVersion(StageEvent $event): void {
+    $stage = $event->getStage();
+    // We only want to do this check if the stage belongs to Automatic Updates.
+    if (!$stage instanceof Updater) {
+      return;
+    }
+
+    if ($event instanceof ReadinessCheckEvent) {
+      $package_versions = $event->getPackageVersions();
+      // During readiness checks, we might not know the desired package
+      // versions, which means there's nothing to validate.
+      if (empty($package_versions)) {
+        return;
+      }
+    }
+    else {
+      // If the stage has begun its life cycle, we expect it knows the desired
+      // package versions.
+      $package_versions = $stage->getPackageVersions();
+    }
+
     $from_version_string = $this->getCoreVersion();
     $from_version = ExtensionVersion::createFromVersionString($from_version_string);
-    $core_package_names = $event->getActiveComposer()->getCorePackageNames();
+    $core_package_names = $stage->getActiveComposer()->getCorePackageNames();
     // All the core packages will be updated to the same version, so it doesn't
     // matter which specific package we're looking at.
     $core_package_name = reset($core_package_names);
-    $to_version_string = $event->getPackageVersions()[$core_package_name];
+    $to_version_string = $package_versions[$core_package_name];
     $to_version = ExtensionVersion::createFromVersionString($to_version_string);
     if (Semver::satisfies($to_version_string, "< $from_version_string")) {
       $messages[] = $this->t('Update version @to_version is lower than @from_version, downgrading is not supported.', [
@@ -91,21 +112,6 @@ class UpdateVersionValidator implements EventSubscriberInterface {
       $error = ValidationResult::createError($messages);
       $event->addValidationResult($error);
     }
-
-  }
-
-  /**
-   * Validates readiness check event.
-   *
-   * @param \Drupal\automatic_updates\Event\ReadinessCheckEvent $event
-   *   The readiness check event object.
-   */
-  public function checkReadinessUpdateVersion(ReadinessCheckEvent $event): void {
-    // During readiness checks, we might not know the desired package versions,
-    // which means there's nothing to validate.
-    if ($event->getPackageVersions()) {
-      $this->checkUpdateVersion($event);
-    }
   }
 
   /**
@@ -113,8 +119,8 @@ class UpdateVersionValidator implements EventSubscriberInterface {
    */
   public static function getSubscribedEvents() {
     return [
-      PreStartEvent::class => 'checkUpdateVersion',
-      ReadinessCheckEvent::class => 'checkReadinessUpdateVersion',
+      PreCreateEvent::class => 'checkUpdateVersion',
+      ReadinessCheckEvent::class => 'checkUpdateVersion',
     ];
   }
 
diff --git a/src/Validator/WritableFileSystemValidator.php b/src/Validator/WritableFileSystemValidator.php
index 6c786dfa9a4fa632b82d4eda997d2d6058bbb8fb..7f2714d7e9583e946fbd36de1a6915f2b0198756 100644
--- a/src/Validator/WritableFileSystemValidator.php
+++ b/src/Validator/WritableFileSystemValidator.php
@@ -2,9 +2,9 @@
 
 namespace Drupal\automatic_updates\Validator;
 
-use Drupal\automatic_updates\Event\PreStartEvent;
 use Drupal\automatic_updates\Event\ReadinessCheckEvent;
-use Drupal\automatic_updates\Event\UpdateEvent;
+use Drupal\package_manager\Event\PreCreateEvent;
+use Drupal\package_manager\Event\StageEvent;
 use Drupal\package_manager\ValidationResult;
 use Drupal\Core\StringTranslation\StringTranslationTrait;
 use Drupal\Core\StringTranslation\TranslationInterface;
@@ -51,7 +51,7 @@ class WritableFileSystemValidator implements EventSubscriberInterface {
   /**
    * Checks that the file system is writable.
    *
-   * @param \Drupal\automatic_updates\Event\UpdateEvent $event
+   * @param \Drupal\package_manager\Event\StageEvent $event
    *   The event object.
    *
    * @todo It might make sense to use a more sophisticated method of testing
@@ -59,7 +59,7 @@ class WritableFileSystemValidator implements EventSubscriberInterface {
    *   false negatives/positives due to things like SELinux, exotic file
    *   systems, and so forth.
    */
-  public function checkPermissions(UpdateEvent $event): void {
+  public function checkPermissions(StageEvent $event): void {
     $messages = [];
 
     if (!is_writable($this->appRoot)) {
@@ -85,7 +85,7 @@ class WritableFileSystemValidator implements EventSubscriberInterface {
   public static function getSubscribedEvents() {
     return [
       ReadinessCheckEvent::class => 'checkPermissions',
-      PreStartEvent::class => 'checkPermissions',
+      PreCreateEvent::class => 'checkPermissions',
     ];
   }
 
diff --git a/tests/modules/automatic_updates_test/src/ReadinessChecker/TestChecker1.php b/tests/modules/automatic_updates_test/src/ReadinessChecker/TestChecker1.php
index 3a7efd8032b94b30c4f8d0ad894702de1272ed59..c04f373d6cff934ef2605edaa0be7d4960e0304a 100644
--- a/tests/modules/automatic_updates_test/src/ReadinessChecker/TestChecker1.php
+++ b/tests/modules/automatic_updates_test/src/ReadinessChecker/TestChecker1.php
@@ -2,11 +2,10 @@
 
 namespace Drupal\automatic_updates_test\ReadinessChecker;
 
-use Drupal\automatic_updates\Event\PreStartEvent;
 use Drupal\automatic_updates\Event\ReadinessCheckEvent;
-use Drupal\automatic_updates\Event\UpdateEvent;
 use Drupal\Core\State\StateInterface;
 use Drupal\package_manager\Event\PreApplyEvent;
+use Drupal\package_manager\Event\PreCreateEvent;
 use Symfony\Component\EventDispatcher\EventSubscriberInterface;
 
 /**
@@ -65,11 +64,9 @@ class TestChecker1 implements EventSubscriberInterface {
    *
    * @param object $event
    *   The update event.
-   * @param string $state_key
-   *   The state key.
    */
-  protected function addResults(object $event, string $state_key): void {
-    $results = $this->state->get($state_key, []);
+  public function addResults(object $event): void {
+    $results = $this->state->get(static::STATE_KEY . '.' . get_class($event), []);
     if ($results instanceof \Throwable) {
       throw $results;
     }
@@ -78,44 +75,14 @@ class TestChecker1 implements EventSubscriberInterface {
     }
   }
 
-  /**
-   * Adds test results for the readiness check event.
-   *
-   * @param \Drupal\automatic_updates\Event\UpdateEvent $event
-   *   The update event.
-   */
-  public function runPreChecks(UpdateEvent $event): void {
-    $this->addResults($event, static::STATE_KEY . "." . ReadinessCheckEvent::class);
-  }
-
-  /**
-   * Adds test results for the pre-apply event.
-   *
-   * @param \Drupal\package_manager\Event\PreApplyEvent $event
-   *   The update event.
-   */
-  public function runPreApplyChecks(PreApplyEvent $event): void {
-    $this->addResults($event, static::STATE_KEY . "." . PreApplyEvent::class);
-  }
-
-  /**
-   * Adds test results for the pre-start event.
-   *
-   * @param \Drupal\automatic_updates\Event\UpdateEvent $event
-   *   The update event.
-   */
-  public function runStartChecks(UpdateEvent $event): void {
-    $this->addResults($event, static::STATE_KEY . "." . PreStartEvent::class);
-  }
-
   /**
    * {@inheritdoc}
    */
   public static function getSubscribedEvents() {
     $priority = defined('AUTOMATIC_UPDATES_TEST_SET_PRIORITY') ? AUTOMATIC_UPDATES_TEST_SET_PRIORITY : 5;
-    $events[ReadinessCheckEvent::class][] = ['runPreChecks', $priority];
-    $events[PreStartEvent::class][] = ['runStartChecks', $priority];
-    $events[PreApplyEvent::class][] = ['runPreApplyChecks', $priority];
+    $events[ReadinessCheckEvent::class][] = ['addResults', $priority];
+    $events[PreCreateEvent::class][] = ['addResults', $priority];
+    $events[PreApplyEvent::class][] = ['addResults', $priority];
     return $events;
   }
 
diff --git a/tests/modules/automatic_updates_test/src/TestController.php b/tests/modules/automatic_updates_test/src/TestController.php
index 690edc978630c9c9631164ca9ec67d54a0c6b591..69308a467d37b439fdaaa251f3d7fb120e249417 100644
--- a/tests/modules/automatic_updates_test/src/TestController.php
+++ b/tests/modules/automatic_updates_test/src/TestController.php
@@ -42,7 +42,7 @@ class TestController extends ControllerBase {
     }
     catch (UpdateException $e) {
       $messages = [];
-      foreach ($e->getValidationResults() as $result) {
+      foreach ($e->getResults() as $result) {
         if ($summary = $result->getSummary()) {
           $messages[] = $summary;
         }
diff --git a/tests/modules/automatic_updates_test2/src/ReadinessChecker/TestChecker2.php b/tests/modules/automatic_updates_test2/src/ReadinessChecker/TestChecker2.php
index dfad950052b9b62f66a6fcd41c92723ee5899686..06b0bd14c0b5826ab60d6a773d45c513bd128fd7 100644
--- a/tests/modules/automatic_updates_test2/src/ReadinessChecker/TestChecker2.php
+++ b/tests/modules/automatic_updates_test2/src/ReadinessChecker/TestChecker2.php
@@ -2,9 +2,9 @@
 
 namespace Drupal\automatic_updates_test2\ReadinessChecker;
 
-use Drupal\automatic_updates\Event\PreStartEvent;
 use Drupal\automatic_updates\Event\ReadinessCheckEvent;
 use Drupal\automatic_updates_test\ReadinessChecker\TestChecker1;
+use Drupal\package_manager\Event\PreCreateEvent;
 
 /**
  * A test readiness checker.
@@ -14,8 +14,8 @@ class TestChecker2 extends TestChecker1 {
   protected const STATE_KEY = 'automatic_updates_test2.checker_results';
 
   public static function getSubscribedEvents() {
-    $events[ReadinessCheckEvent::class][] = ['runPreChecks', 4];
-    $events[PreStartEvent::class][] = ['runStartChecks', 4];
+    $events[ReadinessCheckEvent::class][] = ['addResults', 4];
+    $events[PreCreateEvent::class][] = ['addResults', 4];
 
     return $events;
   }
diff --git a/tests/src/Functional/UpdaterFormTest.php b/tests/src/Functional/UpdaterFormTest.php
index da3166bfe8f63c1dc87b658f8e195d48b62e4eb9..c2cdefcde60ee111f1082b43468ad3d20a974273 100644
--- a/tests/src/Functional/UpdaterFormTest.php
+++ b/tests/src/Functional/UpdaterFormTest.php
@@ -2,8 +2,8 @@
 
 namespace Drupal\Tests\automatic_updates\Functional;
 
-use Drupal\automatic_updates\Event\PreStartEvent;
 use Drupal\automatic_updates\Exception\UpdateException;
+use Drupal\package_manager\Event\PreCreateEvent;
 use Drupal\package_manager\ValidationResult;
 use Drupal\automatic_updates_test\ReadinessChecker\TestChecker1;
 use Drupal\Tests\automatic_updates\Traits\ValidationTestTrait;
@@ -177,7 +177,7 @@ class UpdaterFormTest extends AutomaticUpdatesFunctionalTestBase {
     // Repackage the validation error as an exception, so we can test what
     // happens if a validator throws once the update has started.
     $error = new UpdateException($expected_results, 'The update exploded.');
-    TestChecker1::setTestResult($error, PreStartEvent::class);
+    TestChecker1::setTestResult($error, PreCreateEvent::class);
     $session->reload();
     $assert_session->pageTextNotContains(static::$errorsExplanation);
     $assert_session->pageTextNotContains(static::$warningsExplanation);
@@ -193,7 +193,7 @@ class UpdaterFormTest extends AutomaticUpdatesFunctionalTestBase {
 
     // If a validator flags an error, but doesn't throw, the update should still
     // be halted.
-    TestChecker1::setTestResult($expected_results, PreStartEvent::class);
+    TestChecker1::setTestResult($expected_results, PreCreateEvent::class);
     $this->deleteStagedUpdate();
     $page->pressButton('Update');
     $this->checkForMetaRefresh();
@@ -206,7 +206,7 @@ class UpdaterFormTest extends AutomaticUpdatesFunctionalTestBase {
     // If a validator flags a warning, but doesn't throw, the update should
     // continue.
     $expected_results = $this->testResults['checker_1']['1 warning'];
-    TestChecker1::setTestResult($expected_results, PreStartEvent::class);
+    TestChecker1::setTestResult($expected_results, PreCreateEvent::class);
     $session->reload();
     $this->deleteStagedUpdate();
     $page->pressButton('Update');
diff --git a/tests/src/Kernel/CronUpdaterTest.php b/tests/src/Kernel/CronUpdaterTest.php
index 6de07f4bbed81ce28a48c2387ed3d8141ddd02d3..d65f5e21db4e5b9cb0b4afa6d5ef3a59e44ce8b9 100644
--- a/tests/src/Kernel/CronUpdaterTest.php
+++ b/tests/src/Kernel/CronUpdaterTest.php
@@ -4,6 +4,7 @@ namespace Drupal\Tests\automatic_updates\Kernel;
 
 use Drupal\automatic_updates\CronUpdater;
 use Drupal\Core\Form\FormState;
+use Drupal\package_manager\ComposerUtility;
 use Drupal\update\UpdateSettingsForm;
 
 /**
@@ -100,6 +101,10 @@ class CronUpdaterTest extends AutomaticUpdatesKernelTestBase {
     // depending on configuration.
     $will_update = (int) $will_update;
     $updater = $this->prophesize('\Drupal\automatic_updates\Updater');
+
+    $composer = ComposerUtility::createForDirectory(__DIR__ . '/../../fixtures/fake-site');
+    $updater->getActiveComposer()->willReturn($composer);
+
     $updater->begin(['drupal' => '9.8.1'])->shouldBeCalledTimes($will_update);
     $updater->stage()->shouldBeCalledTimes($will_update);
     $updater->commit()->shouldBeCalledTimes($will_update);