diff --git a/src/Controller/Component/ConflictListBuilder.php b/src/Controller/Component/ConflictListBuilder.php
new file mode 100644
index 0000000..04afa26
--- /dev/null
+++ b/src/Controller/Component/ConflictListBuilder.php
@@ -0,0 +1,218 @@
+<?php
+
+namespace Drupal\workspace\Controller\Component;
+
+use Drupal\Core\Datetime\DateFormatterInterface;
+use Drupal\Core\DependencyInjection\DependencySerializationTrait;
+use Drupal\Core\Entity\EntityInterface;
+use Drupal\Core\Entity\EntityTypeManagerInterface;
+use Drupal\Core\StringTranslation\StringTranslationTrait;
+use Drupal\multiversion\Entity\Index\EntityIndexInterface;
+use Drupal\multiversion\Entity\Workspace;
+use Drupal\multiversion\Workspace\ConflictTrackerInterface;
+use Drupal\node\Entity\Node;
+use Symfony\Component\DependencyInjection\ContainerInterface;
+
+/**
+ * A list builder for entity revision conflicts.
+ *
+ * Note: this class does not implement EntityListBuilderInterface because we
+ * don't need the getStorage. We aren't showing just one entity type here, so we
+ * need the storage for each revision.
+ */
+class ConflictListBuilder {
+  use StringTranslationTrait;
+  use DependencySerializationTrait;
+
+  /**
+   * Inject services needed to build the list.
+   *
+   * @param \Drupal\multiversion\Workspace\ConflictTrackerInterface $conflict_tracker
+   *   The conflict tracking service.
+   * @param \Drupal\multiversion\Entity\Index\EntityIndexInterface $entity_index
+   *   The entity index service.
+   * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager
+   *   The entity type manager.
+   * @param \Drupal\Core\Datetime\DateFormatterInterface $date_formatter
+   *   A date formatter to show pretty dates.
+   */
+  public function __construct(
+    ConflictTrackerInterface $conflict_tracker,
+    EntityIndexInterface $entity_index,
+    EntityTypeManagerInterface $entity_type_manager,
+    DateFormatterInterface $date_formatter
+  ) {
+    $this->conflictTracker = $conflict_tracker;
+    $this->entityIndex = $entity_index;
+    $this->entityTypeManager = $entity_type_manager;
+    $this->dateFormatter = $date_formatter;
+  }
+
+  /**
+   * Instantiates a new instance of this list builder.
+   *
+   * Because we don't have a single entity type, we cannot use
+   * EntityHandlerInterface::createInstance.
+   *
+   * @param \Symfony\Component\DependencyInjection\ContainerInterface $container
+   *   The service container this object should use.
+   *
+   * @return static
+   *   A new instance of this list builder.
+   */
+  public static function createInstance(ContainerInterface $container) {
+    return new self(
+      $container->get('workspace.conflict_tracker'),
+      $container->get('multiversion.entity_index.rev'),
+      $container->get('entity_type.manager'),
+      $container->get('date.formatter')
+    );
+  }
+
+  /**
+   * Build the table header.
+   *
+   * @return array
+   *   The header array used by table render arrays.
+   */
+  public function buildHeader() {
+    $header = array(
+      'title' => $this->t('Title'),
+      'type' => array(
+        'data' => $this->t('Content type'),
+        'class' => array(RESPONSIVE_PRIORITY_MEDIUM),
+      ),
+      'author' => array(
+        'data' => $this->t('Author'),
+        'class' => array(RESPONSIVE_PRIORITY_LOW),
+      ),
+      'status' => $this->t('Status'),
+      'changed' => array(
+        'data' => $this->t('Updated'),
+        'class' => array(RESPONSIVE_PRIORITY_LOW),
+      ),
+    );
+    return $header;
+  }
+
+  /**
+   * Build a row for the given entity.
+   *
+   * @todo Handle translations.
+   *
+   * @see \Drupal\node\NodeListBuilder::buildRow
+   *
+   * @param \Drupal\Core\Entity\EntityInterface $entity
+   *   The entity to build the render array row for.
+   *
+   * @return array
+   *   A row render array used by a table render array.
+   */
+  public function buildRow(EntityInterface $entity) {
+    $row['title'] = $entity->label();
+    // @todo Is there a way to get the human readable name for the bundle?
+    $row['type'] = $entity->bundle();
+    if ($entity instanceof Node) {
+      $row['author']['data'] = [
+        '#theme' => 'username',
+        '#account' => $entity->getOwner(),
+      ];
+    }
+    else {
+      // @todo Figure out how to handle other entities that have a "uid" entity key.
+      $row['author'] = $this->t('None');
+    }
+    if ($entity instanceof Node) {
+      $row['status'] = $entity->isPublished() ? $this->t('published') : $this->t('not published');
+    }
+    else {
+      // @todo Figure out how to handle other entities that have a "status" entity key.
+      $row['status'] = $this->t('published');
+    }
+    $row['changed'] = $this->dateFormatter->format($entity->getChangedTime(), 'short');
+    return $row;
+  }
+
+  /**
+   * Get the title of the entire table.
+   *
+   * @return string
+   *   The title to use for the whole table.
+   */
+  public function getTitle() {
+    return '';
+  }
+
+  /**
+   * Load the entities needed for the table.
+   *
+   * @see workspace_preprocess_workspace_rev
+   *
+   * @return \Drupal\Core\Entity\EntityInterface[]
+   *   An array of entities.
+   */
+  public function load($workspace_id) {
+    /* \Drupal\multiversion\Entity\Workspace $workspace */
+    $workspace = Workspace::load($workspace_id);
+
+    $conflicts = $this->conflictTracker
+      ->useWorkspace($workspace)
+      ->getAll();
+
+    $entity_revisions = [];
+    foreach ($conflicts as $uuid => $conflict) {
+      // @todo figure out why this is an array and what to do if there is more than 1
+      // @todo what happens when the conflict value is not "available"? what does this mean?
+      $rev = reset(array_keys($conflict));
+      $rev_info = $this->entityIndex
+        ->useWorkspace($workspace->id())
+        ->get("$uuid:$rev");
+
+      if (!empty($rev_info['revision_id'])) {
+        $entity_revisions[] = $this->entityTypeManager
+          ->getStorage($rev_info['entity_type_id'])
+          ->useWorkspace($workspace->id())
+          ->loadRevision($rev_info['revision_id']);
+      }
+    }
+
+    return $entity_revisions;
+  }
+
+  /**
+   * Build the render array to display on the page.
+   *
+   * @param string $workspace_id
+   *   The workspace ID to build the conflict list for.
+   *
+   * @return array
+   *   A table render array to show on the page.
+   */
+  public function buildList($workspace_id) {
+    $build['table'] = array(
+      '#type' => 'table',
+      '#header' => $this->buildHeader(),
+      '#title' => $this->getTitle(),
+      '#rows' => array(),
+      '#empty' => 'There are no conflicts.',
+    );
+    $entities = $this->load($workspace_id);
+    foreach ($entities as $entity) {
+      $row = $this->buildRow($entity);
+
+      if ($row = $this->buildRow($entity)) {
+        $build['table']['#rows'][$entity->id()] = $row;
+      }
+    }
+
+    // Only add the pager if a limit is specified.
+    if ($this->limit) {
+      $build['pager'] = array(
+        '#type' => 'pager',
+      );
+    }
+
+    return $build;
+  }
+
+}
diff --git a/src/Controller/WorkspaceController.php b/src/Controller/WorkspaceController.php
index fc74889..9a54748 100644
--- a/src/Controller/WorkspaceController.php
+++ b/src/Controller/WorkspaceController.php
@@ -7,6 +7,7 @@ use Drupal\Core\Url;
 use Drupal\multiversion\Entity\Workspace;
 use Drupal\multiversion\Entity\WorkspaceType;
 use Drupal\multiversion\Entity\WorkspaceTypeInterface;
+use Drupal\workspace\Controller\Component\ConflictListBuilder;
 
 class WorkspaceController extends ControllerBase {
 
@@ -38,4 +39,30 @@ class WorkspaceController extends ControllerBase {
   public function getAddFormTitle(WorkspaceTypeInterface $workspace_type) {
     return $this->t('Add %type workspace', array('%type' => $workspace_type->label()));
   }
+
+  /**
+   * View a list of conflicts for a workspace.
+   *
+   * @param string $workspace
+   *   The workspace ID to get conflicts for.
+   *
+   * @return array
+   *   The render array to display for the page.
+   */
+  public function viewConflicts($workspace) {
+    $container = \Drupal::getContainer();
+    $builder = ConflictListBuilder::createInstance($container);
+    return $builder->buildList($workspace);
+  }
+
+  /**
+   * Get the page title for the list of conflicts page.
+   *
+   * @return string
+   *   The page title.
+   */
+  public function getViewConflictsTitle() {
+    return 'Workspace Conflicts';
+  }
+
 }
diff --git a/src/Entity/Form/WorkspaceForm.php b/src/Entity/Form/WorkspaceForm.php
index 929a487..3a57f78 100644
--- a/src/Entity/Form/WorkspaceForm.php
+++ b/src/Entity/Form/WorkspaceForm.php
@@ -4,7 +4,12 @@ namespace Drupal\workspace\Entity\Form;
 
 use Drupal\Core\Entity\ContentEntityForm;
 use Drupal\Core\Entity\EntityConstraintViolationListInterface;
+use Drupal\Core\Entity\EntityManagerInterface;
 use Drupal\Core\Form\FormStateInterface;
+use Drupal\Core\Render\Markup;
+use Drupal\Core\Url;
+use Drupal\multiversion\Workspace\ConflictTrackerInterface;
+use Symfony\Component\DependencyInjection\ContainerInterface;
 
 /**
  * Form controller for the workspace edit forms.
@@ -12,6 +17,13 @@ use Drupal\Core\Form\FormStateInterface;
 class WorkspaceForm extends ContentEntityForm {
 
   /**
+   * The injected service to track conflicts during replication.
+   *
+   * @var ConflictTrackerInterface
+   */
+  protected $conflictTracker;
+
+  /**
    * The workspace content entity.
    *
    * @var \Drupal\multiversion\Entity\WorkspaceInterface
@@ -19,14 +31,65 @@ class WorkspaceForm extends ContentEntityForm {
   protected $entity;
 
   /**
+   * Constructs a ContentEntityForm object.
+   *
+   * @param \Drupal\Core\Entity\EntityManagerInterface $entity_manager
+   *   The entity manager.
+   * @param ConflictTrackerInterface $conflict_tracker
+   *   The confict tracking service.
+   */
+  public function __construct(EntityManagerInterface $entity_manager, ConflictTrackerInterface $conflict_tracker) {
+    $this->entityManager = $entity_manager;
+    $this->conflictTracker = $conflict_tracker;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function create(ContainerInterface $container) {
+    return new static(
+      $container->get('entity.manager'),
+      $container->get('workspace.conflict_tracker')
+    );
+  }
+
+  /**
    * {@inheritdoc}
    */
   public function form(array $form, FormStateInterface $form_state) {
     $workspace = $this->entity;
 
     if ($this->operation == 'edit') {
+      // Allow the user to not abort on conflicts.
+      $this->conflictTracker->useWorkspace($workspace);
+      $conflicts = $this->conflictTracker->getAll();
+      if ($conflicts) {
+        $form['message'] = $this->generateMessageRenderArray('error', $this->t(
+          'There are @count conflicts. The replication logic has arbitrarily chosen the entity revision that should be used and it may not have been the one you desired. Proceeding with replication will push these conflicts upstream, which may cause a loss of data. See the full list of conflicts <a href=":link">here</a>.',
+          [
+            '@count' => count($conflicts),
+            ':link' => \Drupal::url('entity.workspace.conflicts', ['workspace' => $workspace->id()]),
+          ]
+        ));
+        $form['is_aborted_on_conflict'] = [
+          '#type' => 'radios',
+          '#title' => $this->t('Abort if there are conflicts?'),
+          '#default_value' => 'true',
+          '#options' => [
+            'true' => $this->t('Yes, if conflicts are found do not replicate to upstream.'),
+            'false' => $this->t('No, go ahead and push any conflicts to the upstream.'),
+          ],
+          '#weight' => 0,
+        ];
+      }
+      else {
+        $form['message'] = $this->generateMessageRenderArray('status', 'There are no conflicts.');
+      }
+
+      // Set the form title based on workspace.
       $form['#title'] = $this->t('Edit workspace %label', array('%label' => $workspace->label()));
     }
+
     $form['label'] = array(
       '#type' => 'textfield',
       '#title' => $this->t('Label'),
@@ -69,7 +132,7 @@ class WorkspaceForm extends ContentEntityForm {
     // contained in the display.
     $field_names = array(
       'label',
-      'machine_name'
+      'machine_name',
     );
     foreach ($violations->getByFields($field_names) as $violation) {
       list($field_name) = explode('.', $violation->getPropertyPath(), 2);
@@ -82,32 +145,102 @@ class WorkspaceForm extends ContentEntityForm {
    * {@inheritdoc}
    */
   public function save(array $form, FormStateInterface $form_state) {
+    // Pass the abort flag to the ReplicationManager using runtime-only state,
+    // i.e. a static.
+    $is_aborted_on_conflict = !$form_state->hasValue('is_aborted_on_conflict') || $form_state->getValue('is_aborted_on_conflict') === 'true';
+    drupal_static('workspace_is_aborted_on_conflict', $is_aborted_on_conflict);
+
     $workspace = $this->entity;
-    $insert = $workspace->isNew();
+    $is_new = $workspace->isNew();
     $workspace->save();
+
     $info = ['%info' => $workspace->label()];
     $context = array('@type' => $workspace->bundle(), '%info' => $workspace->label());
     $logger = $this->logger('workspace');
 
-    if ($insert) {
-      $logger->notice('@type: added %info.', $context);
-      drupal_set_message($this->t('Workspace %info has been created.', $info));
-    }
-    else {
+    // If Workbench Moderation is enabled, a publish of the Workspace should
+    // trigger a replication. We pass back the status of that replication using
+    // a static variable. If replication happened, we want to handle the case
+    // of failed replication, as well as modify the wording of the saved
+    // message.
+    // @see \Drupal\workspace\EventSubscriber\WorkbenchModerationSubscriber
+    // @todo Avoid using statics.
+    $replication_status = drupal_static('publish_workspace_replication_status', NULL);
+    if ($replication_status !== NULL) {
       $logger->notice('@type: updated %info.', $context);
-      drupal_set_message($this->t('Workspace %info has been updated.', $info));
-    }
 
-    if ($workspace->id()) {
-      $form_state->setValue('id', $workspace->id());
-      $form_state->set('id', $workspace->id());
-      $redirect = $this->currentUser()->hasPermission('administer workspaces') ? $workspace->toUrl('collection') : $workspace->toUrl('canonical');
-      $form_state->setRedirectUrl($redirect);
+      if ($replication_status == TRUE) {
+        // The replication succeeded, in addition to saving the workspace.
+        drupal_set_message($this->t('Workspace :source has been updated and changes were pushed to :target.', [
+          ':source' => $workspace->label(),
+          ':target' => $workspace->get('upstream')->entity->label(),
+        ]), 'status');
+
+        $form_state->setValue('id', $workspace->id());
+        $form_state->set('id', $workspace->id());
+        $redirect = $this->currentUser()->hasPermission('administer workspaces') ? $workspace->toUrl('collection') : $workspace->toUrl('canonical');
+        $form_state->setRedirectUrl($redirect);
+      }
+      else {
+        // The replication failed, even though the Workspace was updated.
+        $previous_workflow_state = drupal_static('publish_workspace_previous_state', NULL);
+
+        // This variable should always be set, else there is an issue with
+        // the trigger logic.
+        if ($previous_workflow_state == NULL) {
+          throw new \Exception('The publish_workspace_replication_status should be set.');
+        }
+
+        // Revert the workspace back to its previous moderation state.
+        $workspace->moderation_state->target_id = $previous_workflow_state;
+        $workspace->save();
+
+        // Show the form again.
+        $form_state->setRebuild();
+      }
     }
     else {
-      drupal_set_message($this->t('The workspace could not be saved.'), 'error');
-      $form_state->setRebuild();
+      // Assume a replication did not happen OR that Workbench Moderation is not
+      // installed.
+      if ($is_new) {
+        $logger->notice('@type: added %info.', $context);
+        drupal_set_message($this->t('Workspace %info has been created.', $info));
+      }
+      else {
+        $logger->notice('@type: updated %info.', $context);
+        drupal_set_message($this->t('Workspace %info has been updated.', $info));
+      }
+
+      if ($workspace->id()) {
+        $form_state->setValue('id', $workspace->id());
+        $form_state->set('id', $workspace->id());
+        $redirect = $this->currentUser()->hasPermission('administer workspaces') ? $workspace->toUrl('collection') : $workspace->toUrl('canonical');
+        $form_state->setRedirectUrl($redirect);
+      }
+      else {
+        drupal_set_message($this->t('The workspace could not be saved.'), 'error');
+        $form_state->setRebuild();
+      }
     }
   }
 
+  /**
+   * Generate a message render array with the given text.
+   *
+   * @param string $type
+   *   The type of message: status, warning, or error.
+   * @param string $message
+   *   The message to create with.
+   *
+   * @see \Drupal\Core\Render\Element\StatusMessages
+   */
+  protected function generateMessageRenderArray($type, $message) {
+    return [
+      '#theme' => 'status_messages',
+      '#message_list' => [
+        $type => [Markup::create($message)],
+      ],
+    ];
+  }
+
 }
diff --git a/src/EntityTypeInfo.php b/src/EntityTypeInfo.php
index 0f38520..d303dfb 100644
--- a/src/EntityTypeInfo.php
+++ b/src/EntityTypeInfo.php
@@ -108,6 +108,7 @@ class EntityTypeInfo {
     $workspace->setLinkTemplate('canonical', '/admin/structure/workspace/{workspace}');
     $workspace->setLinkTemplate('edit-form', '/admin/structure/workspace/{workspace}/edit');
     $workspace->setLinkTemplate('activate-form', '/admin/structure/workspace/{workspace}/activate');
+    $workspace->setLinkTemplate('conflicts', '/admin/structure/workspace/{workspace}/conflicts');
     $workspace->set('field_ui_base_route', 'entity.workspace_type.edit_form');
 
     return $workspace;
diff --git a/src/EventSubscriber/WorkbenchModerationSubscriber.php b/src/EventSubscriber/WorkbenchModerationSubscriber.php
index 603b44a..9475220 100644
--- a/src/EventSubscriber/WorkbenchModerationSubscriber.php
+++ b/src/EventSubscriber/WorkbenchModerationSubscriber.php
@@ -48,11 +48,35 @@ class WorkbenchModerationSubscriber implements EventSubscriberInterface {
    *   The transition event that just fired.
    */
   public function onTransition(WorkbenchModerationTransitionEvent $event) {
+    /* @var WorkspaceInterface $entity */
     $entity = $event->getEntity();
 
     if ($entity->getEntityTypeId() == 'workspace' && $this->wasDefaultRevision($event)) {
-      /** @var WorkspaceInterface $entity */
-      $this->mergeWorkspaceToParent($entity);
+
+      // If there is no upstream to replicate to, abort.
+      if (!$entity->get('upstream')->entity) {
+        drupal_set_message(t('The :source workspace does not have an upstream to replicate to!', [
+          ':source' => $entity->label(),
+        ]), 'error');
+
+        // @todo Should we revert the workspace to its previous state?
+
+        return;
+      }
+
+      $log = $this->mergeWorkspaceToParent($entity);
+
+      // Pass the replication status to the logic that triggered the state
+      // change. This allows, for example, the caller to revert back the
+      // Workspace's workflow state.
+      // @see \Drupal\workspace\Entity\Form\WorkspaceForm
+      drupal_static('publish_workspace_replication_status', (bool) $log->get('ok')->value);
+
+      // Set the previous workflow state in case a revert needs to happen.
+      // Note: we would not be able to revert back the Workspace's moderation
+      // state here since the event is triggered within a presave hook.
+      // @todo Find a way to share the replication pass/fail status besides a static.
+      drupal_static('publish_workspace_previous_state', $event->getStateBefore());
     }
   }
 
@@ -77,22 +101,21 @@ class WorkbenchModerationSubscriber implements EventSubscriberInterface {
    *
    * @param \Drupal\multiversion\Entity\WorkspaceInterface $workspace
    *   The workspace entity to merge.
+   *
+   * @return \Drupal\replication\Entity\ReplicationLog
+   *   The replication log entry.
    */
   protected function mergeWorkspaceToParent(WorkspaceInterface $workspace) {
-    // This may be insufficient for handling a missing parent.
-    /** @var \Drupal\workspace\WorkspacePointerInterface $parent_workspace */
+    /* @var \Drupal\workspace\WorkspacePointerInterface $parent_workspace */
     $parent_workspace_pointer = $workspace->get('upstream')->entity;
-    if (!$parent_workspace_pointer) {
-      // @todo Should we silently ignore this, or throw an error, or...?
-      return;
-    }
 
+    /* @var \Drupal\workspace\WorkspacePointerInterface $source_pointer */
     $source_pointer = $this->getPointerToWorkspace($workspace);
 
     // Derive a replication task from the Workspace we are acting on.
     $task = $this->replicatorManager->getTask($workspace, 'push_replication_settings');
 
-    $this->replicatorManager->replicate($source_pointer, $parent_workspace_pointer, $task);
+    return $this->replicatorManager->replicate($source_pointer, $parent_workspace_pointer, $task);
   }
 
   /**
diff --git a/src/Form/UpdateForm.php b/src/Form/UpdateForm.php
index df7035d..7e2afc5 100644
--- a/src/Form/UpdateForm.php
+++ b/src/Form/UpdateForm.php
@@ -163,7 +163,7 @@ class UpdateForm extends ConfirmFormBase {
 
       $response = $this->replicatorManager->update($upstream, $active, $task);
 
-      if (($response instanceof ReplicationLogInterface) && $response->get('ok')) {
+      if (($response instanceof ReplicationLogInterface) && ($response->get('ok')->value === TRUE)) {
         drupal_set_message($this->t('%workspace has been updated with content from %upstream.', ['%upstream' => $upstream->label(), '%workspace' => $active->label()]));
       }
       else {
diff --git a/src/InternalReplicator.php b/src/InternalReplicator.php
index 65d8c93..4a8c9b7 100644
--- a/src/InternalReplicator.php
+++ b/src/InternalReplicator.php
@@ -93,6 +93,8 @@ class InternalReplicator implements ReplicatorInterface {
     $source_workspace = $source->getWorkspace();
     $target_workspace = $target->getWorkspace();
     // Set active workspace to source.
+    // @todo Avoid modifying the user's active workspace.
+    $current_active = $this->workspaceManager->getActiveWorkspace();
     try {
       $this->workspaceManager->setActiveWorkspace($source_workspace);
     }
@@ -177,6 +179,10 @@ class InternalReplicator implements ReplicatorInterface {
     $replication_log->setSourceLastSeq($source_workspace->getUpdateSeq());
     $replication_log->setHistory($history);
     $replication_log->save();
+
+    // Switch back to the workspace that was originally active.
+    $this->workspaceManager->setActiveWorkspace($current_active);
+
     return $replication_log;
   }
 
diff --git a/src/Plugin/RulesAction/ReplicateContent.php b/src/Plugin/RulesAction/ReplicateContent.php
index 8431b49..c72a72e 100644
--- a/src/Plugin/RulesAction/ReplicateContent.php
+++ b/src/Plugin/RulesAction/ReplicateContent.php
@@ -99,7 +99,7 @@ class ReplicateContent extends RulesActionBase implements ContainerFactoryPlugin
     /** @var \Drupal\replication\Entity\ReplicationLogInterface $result */
     $result = $this->replicatorManager->replicate($source, $upstream, $task);
 
-    if ($result->get('ok') == TRUE) {
+    if ($result->get('ok')->value === TRUE) {
       drupal_set_message($this->t('Content replicated from workspace @source to workspace @upstream.',
         ['@source' => $workspace->label(), '@upstream' => $upstream->label()]));
     }
diff --git a/src/ReplicatorManager.php b/src/ReplicatorManager.php
index 15f5c1b..c53779a 100644
--- a/src/ReplicatorManager.php
+++ b/src/ReplicatorManager.php
@@ -60,8 +60,18 @@ class ReplicatorManager implements ReplicatorInterface {
    * {@inheritdoc}
    */
   public function replicate(WorkspacePointerInterface $source, WorkspacePointerInterface $target, ReplicationTaskInterface $task = NULL) {
-    // @todo use $initial_conflicts in a conflict management workflow
-    $initial_conflicts = $this->conflictTracker->getAll();
+    // It is assumed a caller of replicate will set this static variable to
+    // FALSE if they wish to proceed with replicating content upstream even in
+    // the presence of conflicts.
+    // @todo Use a sequence index instead of boolean? This will allow the
+    // caller to know there haven't been additional conflicts.
+    $is_aborted_on_conflict = drupal_static('workspace_is_aborted_on_conflict', TRUE);
+
+    // Abort updating the Workspace if there are conflicts.
+    $initial_conflicts = $this->conflictTracker->useWorkspace($source->getWorkspace())->getAll();
+    if ($is_aborted_on_conflict && $initial_conflicts) {
+      return $this->failedReplicationLog($source, $target, $task);
+    }
 
     // Derive a pull replication task from the Workspace we are acting on.
     $pull_task = $this->getTask($source->getWorkspace(), 'pull_replication_settings');
@@ -69,8 +79,11 @@ class ReplicatorManager implements ReplicatorInterface {
     // Pull in changes from $target to $source to ensure a merge will complete.
     $this->update($target, $source, $pull_task);
 
-    // @todo use $post_conflicts in a conflict management workflow
-    $post_conflicts = $this->conflictTracker->getAll();
+    // Abort replicating to target Workspace if there are conflicts.
+    $post_conflicts = $this->conflictTracker->useWorkspace($source->getWorkspace())->getAll();
+    if ($is_aborted_on_conflict && $post_conflicts) {
+      return $this->failedReplicationLog($source, $target, $task);
+    }
 
     // Automatically derive settings from the workspace if no task sent.
     // @todo Refactor to eliminate obscurity of having an optional parameter
diff --git a/src/WorkspaceListBuilder.php b/src/WorkspaceListBuilder.php
index 31a5a31..dac4c32 100644
--- a/src/WorkspaceListBuilder.php
+++ b/src/WorkspaceListBuilder.php
@@ -47,6 +47,7 @@ class WorkspaceListBuilder extends EntityListBuilder {
    * {@inheritdoc}
    */
   public function buildHeader() {
+    // @todo should we show conflicted state in this list?
     $header['label'] = t('Workspace');
     $header['uid'] = t('Owner');
     $header['type'] = t('Type');
@@ -89,7 +90,13 @@ class WorkspaceListBuilder extends EntityListBuilder {
       );
     }
 
+    $operations['conflicts'] = [
+      'title' => $this->t('View Conflicts'),
+      'weight' => 21,
+      'url' => $entity->urlInfo('conflicts', ['workspace' => $entity->id()]),
+    ];
+
     return $operations;
   }
 
-}
\ No newline at end of file
+}
diff --git a/templates/workspace-conflict-list.html.twig b/templates/workspace-conflict-list.html.twig
new file mode 100644
index 0000000..a86ac0d
--- /dev/null
+++ b/templates/workspace-conflict-list.html.twig
@@ -0,0 +1,22 @@
+{#
+/**
+ * @file
+ * Default theme implementation to present a list of a Workspace's conflicted entities.
+ *
+ * Available variables:
+ * - revisions: A collection of all revisions for this workspace.
+ *   Each revision contains the following:
+ *   - link: A link to the conflicted revision.
+ *
+ * @see template_preprocess_block_content_add_list()
+ *
+ * @ingroup themeable
+ */
+#}
+<ul class="admin-list">
+    {% for revision in revisions %}
+        <li class="clearfix">
+          {{ revision.link }}
+        </li>
+    {% endfor %}
+</ul>
diff --git a/workspace.routing.yml b/workspace.routing.yml
index 6577bac..2c68bdb 100644
--- a/workspace.routing.yml
+++ b/workspace.routing.yml
@@ -37,6 +37,16 @@ entity.workspace.activate_form:
   requirements:
     _workspace_view: 'TRUE'
 
+entity.workspace.conflicts:
+  path: '/admin/structure/workspace/{workspace}/conflicts'
+  defaults:
+    _controller: '\Drupal\workspace\Controller\WorkspaceController::viewConflicts'
+    _title_callback: '\Drupal\workspace\Controller\WorkspaceController::getViewConflictsTitle'
+  options:
+    _admin_route: TRUE
+  requirements:
+    _permission: 'view_any_workspace'
+
 # WorkspaceType routing definition
 entity.workspace_type.collection:
   path: '/admin/structure/workspace/types'
