diff --git a/resources/sql/autopatches/20210713.harborcommand.01.migrate.sql b/resources/sql/autopatches/20210713.harborcommand.01.migrate.sql
new file mode 100644
--- /dev/null
+++ b/resources/sql/autopatches/20210713.harborcommand.01.migrate.sql
@@ -0,0 +1,4 @@
+INSERT IGNORE INTO {$NAMESPACE}_harbormaster.harbormaster_buildmessage
+  (authorPHID, receiverPHID, type, isConsumed, dateCreated, dateModified)
+  SELECT authorPHID, targetPHID, command, 0, dateCreated, dateModified
+    FROM {$NAMESPACE}_harbormaster.harbormaster_buildcommand;
diff --git a/resources/sql/autopatches/20210713.harborcommand.02.drop.sql b/resources/sql/autopatches/20210713.harborcommand.02.drop.sql
new file mode 100644
--- /dev/null
+++ b/resources/sql/autopatches/20210713.harborcommand.02.drop.sql
@@ -0,0 +1 @@
+DROP TABLE IF EXISTS {$NAMESPACE}_harbormaster.harbormaster_buildcommand;
diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php
--- a/src/__phutil_library_map__.php
+++ b/src/__phutil_library_map__.php
@@ -7597,7 +7597,7 @@
     'HarbormasterBuildArtifactPHIDType' => 'PhabricatorPHIDType',
     'HarbormasterBuildArtifactQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
     'HarbormasterBuildAutoplan' => 'Phobject',
-    'HarbormasterBuildCommand' => 'HarbormasterDAO',
+    'HarbormasterBuildCommand' => 'Phobject',
     'HarbormasterBuildDependencyDatasource' => 'PhabricatorTypeaheadDatasource',
     'HarbormasterBuildEngine' => 'Phobject',
     'HarbormasterBuildFailureException' => 'Exception',
diff --git a/src/applications/harbormaster/editor/HarbormasterBuildTransactionEditor.php b/src/applications/harbormaster/editor/HarbormasterBuildTransactionEditor.php
--- a/src/applications/harbormaster/editor/HarbormasterBuildTransactionEditor.php
+++ b/src/applications/harbormaster/editor/HarbormasterBuildTransactionEditor.php
@@ -93,10 +93,10 @@
       return;
     }
 
-    id(new HarbormasterBuildCommand())
+    HarbormasterBuildMessage::initializeNewMessage($actor)
       ->setAuthorPHID($xaction->getAuthorPHID())
-      ->setTargetPHID($build->getPHID())
-      ->setCommand($command)
+      ->setReceiverPHID($build->getPHID())
+      ->setType($command)
       ->save();
 
     PhabricatorWorker::scheduleTask(
diff --git a/src/applications/harbormaster/query/HarbormasterBuildQuery.php b/src/applications/harbormaster/query/HarbormasterBuildQuery.php
--- a/src/applications/harbormaster/query/HarbormasterBuildQuery.php
+++ b/src/applications/harbormaster/query/HarbormasterBuildQuery.php
@@ -104,10 +104,10 @@
     }
 
     $build_phids = mpull($page, 'getPHID');
-    $messages = id(new HarbormasterBuildCommand())->loadAllWhere(
-      'targetPHID IN (%Ls) ORDER BY id ASC',
+    $messages = id(new HarbormasterBuildMessage())->loadAllWhere(
+      'receiverPHID IN (%Ls) AND isConsumed = 0 ORDER BY id ASC',
       $build_phids);
-    $messages = mgroup($messages, 'getTargetPHID');
+    $messages = mgroup($messages, 'getReceiverPHID');
     foreach ($page as $build) {
       $unprocessed_messages = idx($messages, $build->getPHID(), array());
       $build->attachUnprocessedMessages($unprocessed_messages);
diff --git a/src/applications/harbormaster/storage/HarbormasterBuildCommand.php b/src/applications/harbormaster/storage/HarbormasterBuildCommand.php
--- a/src/applications/harbormaster/storage/HarbormasterBuildCommand.php
+++ b/src/applications/harbormaster/storage/HarbormasterBuildCommand.php
@@ -1,27 +1,11 @@
 <?php
 
-final class HarbormasterBuildCommand extends HarbormasterDAO {
+final class HarbormasterBuildCommand
+  extends Phobject {
 
   const COMMAND_PAUSE = 'pause';
   const COMMAND_RESUME = 'resume';
   const COMMAND_RESTART = 'restart';
   const COMMAND_ABORT = 'abort';
 
-  protected $authorPHID;
-  protected $targetPHID;
-  protected $command;
-
-  protected function getConfiguration() {
-    return array(
-      self::CONFIG_COLUMN_SCHEMA => array(
-        'command' => 'text128',
-      ),
-      self::CONFIG_KEY_SCHEMA => array(
-        'key_target' => array(
-          'columns' => array('targetPHID'),
-        ),
-      ),
-    ) + parent::getConfiguration();
-  }
-
 }
diff --git a/src/applications/harbormaster/storage/build/HarbormasterBuild.php b/src/applications/harbormaster/storage/build/HarbormasterBuild.php
--- a/src/applications/harbormaster/storage/build/HarbormasterBuild.php
+++ b/src/applications/harbormaster/storage/build/HarbormasterBuild.php
@@ -230,6 +230,7 @@
   }
 
   public function attachUnprocessedMessages(array $messages) {
+    assert_instances_of($messages, 'HarbormasterBuildMessage');
     $this->unprocessedMessages = $messages;
     return $this;
   }
@@ -331,7 +332,7 @@
   public function isPausing() {
     $is_pausing = false;
     foreach ($this->getUnprocessedMessages() as $message_object) {
-      $message_type = $message_object->getCommand();
+      $message_type = $message_object->getType();
       switch ($message_type) {
         case HarbormasterBuildCommand::COMMAND_PAUSE:
           $is_pausing = true;
@@ -352,7 +353,7 @@
   public function isResuming() {
     $is_resuming = false;
     foreach ($this->getUnprocessedMessages() as $message_object) {
-      $message_type = $message_object->getCommand();
+      $message_type = $message_object->getType();
       switch ($message_type) {
         case HarbormasterBuildCommand::COMMAND_RESTART:
         case HarbormasterBuildCommand::COMMAND_RESUME:
@@ -373,7 +374,7 @@
   public function isRestarting() {
     $is_restarting = false;
     foreach ($this->getUnprocessedMessages() as $message_object) {
-      $message_type = $message_object->getCommand();
+      $message_type = $message_object->getType();
       switch ($message_type) {
         case HarbormasterBuildCommand::COMMAND_RESTART:
           $is_restarting = true;
@@ -387,7 +388,7 @@
   public function isAborting() {
     $is_aborting = false;
     foreach ($this->getUnprocessedMessages() as $message_object) {
-      $message_type = $message_object->getCommand();
+      $message_type = $message_object->getType();
       switch ($message_type) {
         case HarbormasterBuildCommand::COMMAND_ABORT:
           $is_aborting = true;
@@ -399,9 +400,13 @@
   }
 
   public function markUnprocessedMessagesAsProcessed() {
-    // TODO: See T13072. This is a placeholder until BuildCommand and
-    // BuildMessage merge.
-    return $this->deleteUnprocessedMessages();
+    foreach ($this->getUnprocessedMessages() as $key => $message_object) {
+      $message_object
+        ->setIsConsumed(1)
+        ->save();
+    }
+
+    return $this;
   }
 
   public function deleteUnprocessedMessages() {