diff --git a/resources/sql/autopatches/20140212.dx.1.armageddon.php b/resources/sql/autopatches/20140212.dx.1.armageddon.php
new file mode 100644
--- /dev/null
+++ b/resources/sql/autopatches/20140212.dx.1.armageddon.php
@@ -0,0 +1,222 @@
+<?php
+
+$conn_w = id(new DifferentialRevision())->establishConnection('w');
+$rows = new LiskRawMigrationIterator($conn_w, 'differential_comment');
+
+$content_source = PhabricatorContentSource::newForSource(
+  PhabricatorContentSource::SOURCE_LEGACY,
+  array())->serialize();
+
+echo "Migrating Differential comments to modern storage...\n";
+foreach ($rows as $row) {
+  $id = $row['id'];
+  echo "Migrating comment {$id}...\n";
+
+  $revision = id(new DifferentialRevision())->load($row['revisionID']);
+  if (!$revision) {
+    echo "No revision, continuing.\n";
+    continue;
+  }
+
+  $revision_phid = $revision->getPHID();
+
+  $comments = queryfx_all(
+    $conn_w,
+    'SELECT * FROM %T WHERE legacyCommentID = %d',
+    'differential_transaction_comment',
+    $id);
+
+  $main_comments = array();
+  $inline_comments = array();
+
+  foreach ($comments as $comment) {
+    if ($comment['changesetID']) {
+      $inline_comments[] = $comment;
+    } else {
+      $main_comments[] = $comment;
+    }
+  }
+
+  $metadata = json_decode($row['metadata'], true);
+  if (!is_array($metadata)) {
+    $metadata = array();
+  }
+
+  $key_cc = DifferentialComment::METADATA_ADDED_CCS;
+  $key_add_rev = DifferentialComment::METADATA_ADDED_REVIEWERS;
+  $key_rem_rev = DifferentialComment::METADATA_REMOVED_REVIEWERS;
+  $key_diff_id = DifferentialComment::METADATA_DIFF_ID;
+
+  $xactions = array();
+
+  // Build the main action transaction.
+  switch ($row['action']) {
+    case DifferentialAction::ACTION_COMMENT:
+    case DifferentialAction::ACTION_ADDREVIEWERS:
+    case DifferentialAction::ACTION_ADDCCS:
+    case DifferentialAction::ACTION_UPDATE:
+    case DifferentialTransaction::TYPE_INLINE:
+      // These actions will have their transactions created by other rules.
+      break;
+    default:
+      // Otherwise, this is a normal action (like an accept or reject).
+      $xactions[] = array(
+        'type' => DifferentialTransaction::TYPE_ACTION,
+        'old' => null,
+        'new' => $row['action'],
+      );
+      break;
+  }
+
+  // Build the diff update transaction, if one exists.
+  $diff_id = idx($metadata, $key_diff_id);
+  if (!is_scalar($diff_id)) {
+    $diff_id = null;
+  }
+
+  if ($diff_id || $row['action'] == DifferentialAction::ACTION_UPDATE) {
+    $xactions[] = array(
+      'type' => DifferentialTransaction::TYPE_UPDATE,
+      'old' => null,
+      'new' => $diff_id,
+    );
+  }
+
+  // Build the add/remove reviewers transaction, if one exists.
+  $add_rev = idx($metadata, $key_add_rev, array());
+  if (!is_array($add_rev)) {
+    $add_rev = array();
+  }
+  $rem_rev = idx($metadata, $key_rem_rev, array());
+  if (!is_array($rem_rev)) {
+    $rem_rev = array();
+  }
+
+  if ($add_rev || $rem_rev) {
+    $old = array();
+    foreach ($rem_rev as $phid) {
+      if (!is_scalar($phid)) {
+        continue;
+      }
+      $old[$phid] = array(
+        'src' => $revision_phid,
+        'type' => PhabricatorEdgeConfig::TYPE_DREV_HAS_REVIEWER,
+        'dst' => $phid,
+      );
+    }
+
+    $new = array();
+    foreach ($add_rev as $phid) {
+      if (!is_scalar($phid)) {
+        continue;
+      }
+      $new[$phid] = array(
+        'src' => $revision_phid,
+        'type' => PhabricatorEdgeConfig::TYPE_DREV_HAS_REVIEWER,
+        'dst' => $phid,
+      );
+    }
+
+    $xactions[] = array(
+      'type' => PhabricatorTransactions::TYPE_EDGE,
+      'old' => $old,
+      'new' => $new,
+      'meta' => array(
+        'edge:type' => PhabricatorEdgeConfig::TYPE_DREV_HAS_REVIEWER,
+      ),
+    );
+  }
+
+  // Build the CC transaction, if one exists.
+  $add_cc = idx($metadata, $key_cc, array());
+  if (!is_array($add_cc)) {
+    $add_cc = array();
+  }
+
+  if ($add_cc) {
+    $xactions[] = array(
+      'type' => PhabricatorTransactions::TYPE_SUBSCRIBERS,
+      'old' => array(),
+      'new' => array_fuse($add_cc),
+    );
+  }
+
+
+  // Build the main comment transaction.
+  foreach ($main_comments as $main) {
+    $xactions[] = array(
+      'type' => PhabricatorTransactions::TYPE_COMMENT,
+      'old' => null,
+      'new' => null,
+      'phid' => $main['transactionPHID'],
+      'comment' => $main,
+    );
+  }
+
+  // Build inline comment transactions.
+  foreach ($inline_comments as $inline) {
+    $xactions[] = array(
+      'type' => DifferentialTransaction::TYPE_INLINE,
+      'old' => null,
+      'new' => null,
+      'phid' => $inline['transactionPHID'],
+      'comment' => $inline,
+    );
+  }
+
+  foreach ($xactions as $xaction) {
+    // Generate a new PHID, if we don't already have one from the comment
+    // table. We pregenerated into the comment table to make this a little
+    // easier, so we only need to write to one table.
+    $xaction_phid = idx($xaction, 'phid');
+    if (!$xaction_phid) {
+      $xaction_phid = PhabricatorPHID::generateNewPHID(
+        PhabricatorApplicationTransactionPHIDTypeTransaction::TYPECONST,
+        DifferentialPHIDTypeRevision::TYPECONST);
+    }
+    unset($xaction['phid']);
+
+    $comment_phid = null;
+    $comment_version = 0;
+    if (idx($xaction, 'comment')) {
+      $comment_phid = $xaction['comment']['phid'];
+      $comment_version = 1;
+    }
+
+    $old = idx($xaction, 'old');
+    $new = idx($xaction, 'new');
+    $meta = idx($xaction, 'meta', array());
+
+    queryfx(
+      $conn_w,
+      'INSERT INTO %T (phid, authorPHID, objectPHID, viewPolicy, editPolicy,
+          commentPHID, commentVersion, transactionType, oldValue, newValue,
+          contentSource, metadata, dateCreated, dateModified)
+        VALUES (%s, %s, %s, %s, %s, %ns, %d, %s, %ns, %ns, %s, %s, %d, %d)',
+      'differential_transaction',
+
+      // PHID, authorPHID, objectPHID
+      $xaction_phid,
+      (string)$row['authorPHID'],
+      $revision_phid,
+
+      // viewPolicy, editPolicy, commentPHID, commentVersion
+      'public',
+      (string)$row['authorPHID'],
+      $comment_phid,
+      $comment_version,
+
+      // transactionType, oldValue, newValue, contentSource, metadata
+      $xaction['type'],
+      json_encode($old),
+      json_encode($new),
+      $content_source,
+      json_encode($meta),
+
+      // dates
+      $row['dateCreated'],
+      $row['dateModified']);
+  }
+
+}
+echo "Done.\n";
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
@@ -464,6 +464,7 @@
     'DifferentialTitleFieldSpecification' => 'applications/differential/field/specification/DifferentialTitleFieldSpecification.php',
     'DifferentialTransaction' => 'applications/differential/storage/DifferentialTransaction.php',
     'DifferentialTransactionComment' => 'applications/differential/storage/DifferentialTransactionComment.php',
+    'DifferentialTransactionQuery' => 'applications/differential/query/DifferentialTransactionQuery.php',
     'DifferentialUnitFieldSpecification' => 'applications/differential/field/specification/DifferentialUnitFieldSpecification.php',
     'DifferentialUnitStatus' => 'applications/differential/constants/DifferentialUnitStatus.php',
     'DifferentialUnitTestResult' => 'applications/differential/constants/DifferentialUnitTestResult.php',
@@ -2856,11 +2857,7 @@
     'DifferentialChangesetTwoUpRenderer' => 'DifferentialChangesetHTMLRenderer',
     'DifferentialChangesetTwoUpTestRenderer' => 'DifferentialChangesetTestRenderer',
     'DifferentialChangesetViewController' => 'DifferentialController',
-    'DifferentialComment' =>
-    array(
-      0 => 'DifferentialDAO',
-      1 => 'PhabricatorMarkupInterface',
-    ),
+    'DifferentialComment' => 'PhabricatorMarkupInterface',
     'DifferentialCommentEditor' => 'PhabricatorEditor',
     'DifferentialCommentMail' => 'DifferentialMail',
     'DifferentialCommentPreviewController' => 'DifferentialController',
@@ -2981,6 +2978,7 @@
     'DifferentialTitleFieldSpecification' => 'DifferentialFreeformFieldSpecification',
     'DifferentialTransaction' => 'PhabricatorApplicationTransaction',
     'DifferentialTransactionComment' => 'PhabricatorApplicationTransactionComment',
+    'DifferentialTransactionQuery' => 'PhabricatorApplicationTransactionQuery',
     'DifferentialUnitFieldSpecification' => 'DifferentialFieldSpecification',
     'DifferentialViewPolicyFieldSpecification' => 'DifferentialFieldSpecification',
     'DiffusionBranchTableController' => 'DiffusionController',
diff --git a/src/applications/differential/conduit/ConduitAPI_differential_getrevisioncomments_Method.php b/src/applications/differential/conduit/ConduitAPI_differential_getrevisioncomments_Method.php
--- a/src/applications/differential/conduit/ConduitAPI_differential_getrevisioncomments_Method.php
+++ b/src/applications/differential/conduit/ConduitAPI_differential_getrevisioncomments_Method.php
@@ -1,11 +1,16 @@
 <?php
 
-/**
- * @group conduit
- */
 final class ConduitAPI_differential_getrevisioncomments_Method
   extends ConduitAPI_differential_Method {
 
+  public function getMethodStatus() {
+    return self::METHOD_STATUS_DEPRECATED;
+  }
+
+  public function getMethodStatusDescription() {
+    return pht('Obsolete and doomed, see T2222.');
+  }
+
   public function getMethodDescription() {
     return "Retrieve Differential Revision Comments.";
   }
@@ -13,7 +18,7 @@
   public function defineParamTypes() {
     return array(
       'ids' => 'required list<int>',
-      'inlines' => 'optional bool',
+      'inlines' => 'optional bool (deprecated)',
     );
   }
 
@@ -34,47 +39,36 @@
       return $results;
     }
 
-    $comments = id(new DifferentialCommentQuery())
-      ->withRevisionIDs($revision_ids)
+    $revisions = id(new DifferentialRevisionQuery())
+      ->setViewer($request->getUser())
+      ->withIDs($revision_ids)
       ->execute();
 
-    $with_inlines = $request->getValue('inlines');
-    if ($with_inlines) {
-      $inlines = id(new DifferentialInlineCommentQuery())
-        ->withRevisionIDs($revision_ids)
-        ->execute();
-      $changesets = array();
-      if ($inlines) {
-        $changesets = id(new DifferentialChangeset())->loadAllWhere(
-          'id IN (%Ld)',
-          array_unique(mpull($inlines, 'getChangesetID')));
-        $inlines = mgroup($inlines, 'getCommentID');
-      }
+    if (!$revisions) {
+      return $results;
     }
 
+    $comments = id(new DifferentialCommentQuery())
+      ->withRevisionPHIDs(mpull($revisions, 'getPHID'))
+      ->execute();
+
+    $revisions = mpull($revisions, null, 'getPHID');
+
     foreach ($comments as $comment) {
-      // TODO: Sort this out in the ID -> PHID change.
-      $revision_id = $comment->getRevisionID();
+      $revision = idx($revisions, $comment->getRevisionPHID());
+      if (!$revision) {
+        continue;
+      }
+
       $result = array(
-        'revisionID'  => $revision_id,
+        'revisionID'  => $revision->getID(),
         'action'      => $comment->getAction(),
         'authorPHID'  => $comment->getAuthorPHID(),
         'dateCreated' => $comment->getDateCreated(),
         'content'     => $comment->getContent(),
       );
 
-      if ($with_inlines) {
-        $result['inlines'] = array();
-        foreach (idx($inlines, $comment->getID(), array()) as $inline) {
-          $changeset = idx($changesets, $inline->getChangesetID());
-          $result['inlines'][] = $this->buildInlineInfoDictionary(
-            $inline,
-            $changeset);
-        }
-        // TODO: Put synthetic inlines without an attached comment somewhere.
-      }
-
-      $results[$revision_id][] = $result;
+      $results[$revision->getID()][] = $result;
     }
 
     return $results;
diff --git a/src/applications/differential/constants/DifferentialAction.php b/src/applications/differential/constants/DifferentialAction.php
--- a/src/applications/differential/constants/DifferentialAction.php
+++ b/src/applications/differential/constants/DifferentialAction.php
@@ -90,6 +90,11 @@
           $title = pht('%s reopened this revision.',
             $author_name);
         break;
+        case DifferentialTransaction::TYPE_INLINE:
+          $title = pht(
+            '%s added an inline comment.',
+            $author_name);
+          break;
         default:
           $title = pht('Ghosts happened to this revision.');
         break;
diff --git a/src/applications/differential/editor/DifferentialCommentEditor.php b/src/applications/differential/editor/DifferentialCommentEditor.php
--- a/src/applications/differential/editor/DifferentialCommentEditor.php
+++ b/src/applications/differential/editor/DifferentialCommentEditor.php
@@ -624,7 +624,7 @@
 
     // If this edit has comments or inline comments, save a transaction for
     // the comment content.
-    if (strlen($this->message) || $inline_comments) {
+    if (strlen($this->message)) {
       $comments[] = id(clone $template)
         ->setAction(DifferentialAction::ACTION_COMMENT)
         ->setContent((string)$this->message);
@@ -634,8 +634,6 @@
       $comment->save();
     }
 
-    $last_comment = last($comments);
-
     $changesets = array();
     if ($inline_comments) {
       $load_ids = mpull($inline_comments, 'getChangesetID');
@@ -646,12 +644,13 @@
           $load_ids);
       }
       foreach ($inline_comments as $inline) {
-        // For now, attach inlines to the last comment. We'll eventually give
-        // them their own transactions, but this would be fairly gross during
-        // the storage transition and we'll have to do special thing with these
-        // during migration anyway.
-        $inline->setCommentID($last_comment->getID());
-        $inline->save();
+        $inline_xaction_comment = $inline->getTransactionCommentForSave();
+        $inline_xaction_comment->setRevisionPHID($revision->getPHID());
+
+        $comments[] = id(clone $template)
+          ->setAction(DifferentialTransaction::TYPE_INLINE)
+          ->setProxyComment($inline_xaction_comment)
+          ->save();
       }
     }
 
@@ -702,7 +701,7 @@
 
       // NOTE: Don't use this, it will be removed after ApplicationTransactions.
       // For now, it powers inline comment rendering over the Asana brdige.
-      'temporaryCommentID'   => $last_comment->getID(),
+      'temporaryTransactionPHIDs' => mpull($comments, 'getPHID'),
     );
 
     id(new PhabricatorFeedStoryPublisher())
diff --git a/src/applications/differential/query/DifferentialCommentQuery.php b/src/applications/differential/query/DifferentialCommentQuery.php
--- a/src/applications/differential/query/DifferentialCommentQuery.php
+++ b/src/applications/differential/query/DifferentialCommentQuery.php
@@ -6,67 +6,29 @@
 final class DifferentialCommentQuery
   extends PhabricatorOffsetPagedQuery {
 
-  private $revisionIDs;
+  private $revisionPHIDs;
 
-  public function withRevisionIDs(array $ids) {
-    $this->revisionIDs = $ids;
+  public function withRevisionPHIDs(array $phids) {
+    $this->revisionPHIDs = $phids;
     return $this;
   }
 
   public function execute() {
-    $table = new DifferentialComment();
-    $conn_r = $table->establishConnection('r');
-
-    $data = queryfx_all(
-      $conn_r,
-      'SELECT * FROM %T %Q %Q',
-      $table->getTableName(),
-      $this->buildWhereClause($conn_r),
-      $this->buildLimitClause($conn_r));
-
-    $comments = $table->loadAllFromArray($data);
-
-    // We've moved the actual text storage into DifferentialTransactionComment,
-    // so load the relevant pieces of text we need.
-    if ($comments) {
-      $this->loadCommentText($comments);
-    }
-
-    return $comments;
-  }
-
-  private function buildWhereClause(AphrontDatabaseConnection $conn_r) {
-    $where = array();
-
-    if ($this->revisionIDs) {
-      $where[] = qsprintf(
-        $conn_r,
-        'revisionID IN (%Ld)',
-        $this->revisionIDs);
+    // TODO: We're getting rid of this, it is the bads.
+    $viewer = PhabricatorUser::getOmnipotentUser();
+
+    $xactions = id(new DifferentialTransactionQuery())
+      ->setViewer($viewer)
+      ->withObjectPHIDs($this->revisionPHIDs)
+      ->needComments(true)
+      ->execute();
+
+    $results = array();
+    foreach ($xactions as $xaction) {
+      $results[] = DifferentialComment::newFromModernTransaction($xaction);
     }
 
-    return $this->formatWhereClause($where);
+    return $results;
   }
 
-  private function loadCommentText(array $comments) {
-    $table = new DifferentialTransactionComment();
-    $conn_r = $table->establishConnection('r');
-
-    $data = queryfx_all(
-      $conn_r,
-      'SELECT * FROM %T WHERE legacyCommentID IN (%Ld) AND changesetID IS NULL',
-      $table->getTableName(),
-      mpull($comments, 'getID'));
-    $texts = $table->loadAllFromArray($data);
-    $texts = mpull($texts, null, 'getLegacyCommentID');
-
-    foreach ($comments as $comment) {
-      $text = idx($texts, $comment->getID());
-      if ($text) {
-        $comment->setProxyComment($text);
-      }
-    }
-  }
-
-
 }
diff --git a/src/applications/differential/query/DifferentialTransactionQuery.php b/src/applications/differential/query/DifferentialTransactionQuery.php
new file mode 100644
--- /dev/null
+++ b/src/applications/differential/query/DifferentialTransactionQuery.php
@@ -0,0 +1,10 @@
+<?php
+
+final class DifferentialTransactionQuery
+  extends PhabricatorApplicationTransactionQuery {
+
+  public function getTemplateApplicationTransaction() {
+    return new DifferentialTransaction();
+  }
+
+}
diff --git a/src/applications/differential/search/DifferentialSearchIndexer.php b/src/applications/differential/search/DifferentialSearchIndexer.php
--- a/src/applications/differential/search/DifferentialSearchIndexer.php
+++ b/src/applications/differential/search/DifferentialSearchIndexer.php
@@ -53,7 +53,7 @@
       time());
 
     $comments = id(new DifferentialCommentQuery())
-      ->withRevisionIDs(array($rev->getID()))
+      ->withRevisionPHIDs(array($rev->getPHID()))
       ->execute();
 
     $inlines = id(new DifferentialInlineCommentQuery())
diff --git a/src/applications/differential/storage/DifferentialComment.php b/src/applications/differential/storage/DifferentialComment.php
--- a/src/applications/differential/storage/DifferentialComment.php
+++ b/src/applications/differential/storage/DifferentialComment.php
@@ -1,6 +1,9 @@
 <?php
 
-final class DifferentialComment extends DifferentialDAO
+/**
+ * Temporary proxy shell around ApplicationTransactions.
+ */
+final class DifferentialComment
   implements PhabricatorMarkupInterface {
 
   const METADATA_ADDED_REVIEWERS   = 'added-reviewers';
@@ -10,36 +13,244 @@
 
   const MARKUP_FIELD_BODY          = 'markup:body';
 
-  protected $authorPHID;
-  protected $revisionID;
-  protected $action;
-  protected $content = '';
-  protected $cache;
-  protected $metadata = array();
-  protected $contentSource;
-
   private $arbitraryDiffForFacebook;
   private $proxyComment;
+  private $proxy;
+
+  public function __construct() {
+    $this->proxy = new DifferentialTransaction();
+  }
 
   public function __clone() {
+    $this->proxy = clone $this->proxy;
     if ($this->proxyComment) {
       $this->proxyComment = clone $this->proxyComment;
     }
   }
 
+  public static function newFromModernTransaction(
+    DifferentialTransaction $xaction) {
+
+    $obj = new DifferentialComment();
+    $obj->proxy = $xaction;
+
+    if ($xaction->hasComment()) {
+      $obj->proxyComment = $xaction->getComment();
+    }
+
+    return $obj;
+  }
+
+  public function getPHID() {
+    return $this->proxy->getPHID();
+  }
+
   public function getContent() {
     return $this->getProxyComment()->getContent();
   }
 
   public function setContent($content) {
-    // NOTE: We no longer read this field, but there's no cost to continuing
-    // to write it in case something goes horribly wrong, since it makes it
-    // far easier to back out of this.
-    $this->content = $content;
     $this->getProxyComment()->setContent($content);
     return $this;
   }
 
+  public function getAuthorPHID() {
+    return $this->proxy->getAuthorPHID();
+  }
+
+  public function setAuthorPHID($author_phid) {
+    $this->proxy->setAuthorPHID($author_phid);
+    return $this;
+  }
+
+  public function setContentSource($content_source) {
+    $this->proxy->setContentSource($content_source);
+    $this->proxyComment->setContentSource($content_source);
+    return $this;
+  }
+
+  public function setAction($action) {
+    $meta = array();
+    switch ($action) {
+      case DifferentialAction::ACTION_COMMENT:
+        $type = PhabricatorTransactions::TYPE_COMMENT;
+        $old = null;
+        $new = null;
+        break;
+      case DifferentialAction::ACTION_ADDREVIEWERS:
+        $type = PhabricatorTransactions::TYPE_EDGE;
+        $old = array();
+        $new = array();
+        $meta = array(
+          'edge:type' => PhabricatorEdgeConfig::TYPE_DREV_HAS_REVIEWER,
+        );
+        break;
+      case DifferentialAction::ACTION_ADDCCS:
+        $type = PhabricatorTransactions::TYPE_SUBSCRIBERS;
+        $old = array();
+        $new = array();
+        break;
+      case DifferentialAction::ACTION_UPDATE:
+        $type = DifferentialTransaction::TYPE_UPDATE;
+        $old = null;
+        $new = null;
+        break;
+      case DifferentialTransaction::TYPE_INLINE:
+        $type = $action;
+        $old = null;
+        $new = null;
+        break;
+      default:
+        $type = DifferentialTransaction::TYPE_ACTION;
+        $old = null;
+        $new = $action;
+        break;
+    }
+
+    $xaction = $this->proxy;
+
+    $xaction
+      ->setTransactionType($type)
+      ->setOldValue($old)
+      ->setNewValue($new);
+
+    if ($meta) {
+      foreach ($meta as $key => $value) {
+        $xaction->setMetadataValue($key, $value);
+      }
+    }
+
+    return $this;
+  }
+
+  public function getAction() {
+    switch ($this->proxy->getTransactionType()) {
+      case PhabricatorTransactions::TYPE_SUBSCRIBERS:
+        return DifferentialAction::ACTION_ADDCCS;
+      case DifferentialTransaction::TYPE_UPDATE:
+        return DifferentialAction::ACTION_UPDATE;
+      case PhabricatorTransactions::TYPE_EDGE:
+        return DifferentialAction::ACTION_ADDREVIEWERS;
+      case PhabricatorTransactions::TYPE_COMMENT:
+        return DifferentialAction::ACTION_COMMENT;
+      case DifferentialTransaction::TYPE_INLINE:
+        return DifferentialTransaction::TYPE_INLINE;
+      default:
+        return $this->proxy->getNewValue();
+    }
+  }
+
+  public function setMetadata(array $metadata) {
+    if (!$this->proxy->getTransactionType()) {
+      throw new Exception(pht('Call setAction() before setMetadata()!'));
+    }
+
+    $key_cc = self::METADATA_ADDED_CCS;
+    $key_add_rev = self::METADATA_ADDED_REVIEWERS;
+    $key_rem_rev = self::METADATA_REMOVED_REVIEWERS;
+    $key_diff_id = self::METADATA_DIFF_ID;
+
+    switch ($this->proxy->getTransactionType()) {
+      case DifferentialTransaction::TYPE_UPDATE:
+        $id = idx($metadata, $key_diff_id);
+        $this->proxy->setNewValue($id);
+        break;
+      case PhabricatorTransactions::TYPE_EDGE:
+        $rem = idx($metadata, $key_rem_rev, array());
+        $old = array();
+        foreach ($rem as $phid) {
+          $old[$phid] = array(
+            'src' => $this->proxy->getObjectPHID(),
+            'type' => PhabricatorEdgeConfig::TYPE_DREV_HAS_REVIEWER,
+            'dst' => $phid,
+          );
+        }
+        $this->proxy->setOldValue($old);
+
+        $add = idx($metadata, $key_add_rev, array());
+        $new = array();
+        foreach ($add as $phid) {
+          $new[$phid] = array(
+            'src' => $this->proxy->getObjectPHID(),
+            'type' => PhabricatorEdgeConfig::TYPE_DREV_HAS_REVIEWER,
+            'dst' => $phid,
+          );
+        }
+        $this->proxy->setNewValue($new);
+        break;
+      case PhabricatorTransactions::TYPE_SUBSCRIBERS:
+        $phids = idx($metadata, $key_cc, array());
+        $new = array();
+        foreach ($phids as $phid) {
+          $new[$phid] = $phid;
+        }
+        $this->proxy->setNewValue($new);
+        break;
+      default:
+        break;
+    }
+
+    return $this;
+  }
+
+  public function getMetadata() {
+    if (!$this->proxy->getTransactionType()) {
+      throw new Exception(pht('Call setAction() before getMetadata()!'));
+    }
+
+    $key_cc = self::METADATA_ADDED_CCS;
+    $key_add_rev = self::METADATA_ADDED_REVIEWERS;
+    $key_rem_rev = self::METADATA_REMOVED_REVIEWERS;
+    $key_diff_id = self::METADATA_DIFF_ID;
+
+    $type = $this->proxy->getTransactionType();
+
+    switch ($type) {
+      case PhabricatorTransactions::TYPE_SUBSCRIBERS:
+        $value = $this->proxy->getNewValue();
+        if (!$value) {
+          $value = array();
+        }
+        return array(
+          $key_cc => $value,
+        );
+      case DifferentialTransaction::TYPE_UPDATE:
+        return array(
+          $key_diff_id => $this->proxy->getNewValue(),
+        );
+      case PhabricatorTransactions::TYPE_EDGE:
+      case PhabricatorTransactions::TYPE_SUBSCRIBERS:
+        $old = $this->proxy->getOldValue();
+        if (!$old) {
+          $old = array();
+        }
+        $new = $this->proxy->getNewValue();
+        if (!$new) {
+          $new = array();
+        }
+
+        $rem = array_diff_key($old, $new);
+        $add = array_diff_key($new, $old);
+
+        if ($type == PhabricatorTransactions::TYPE_EDGE) {
+          return array(
+            $key_add_rev => array_keys($add),
+            $key_rem_rev => array_keys($rem),
+          );
+        } else {
+          return array(
+            $key_cc => array_keys($add),
+          );
+        }
+      default:
+        return array();
+    }
+  }
+
+  public function getContentSource() {
+    return $this->proxy->getContentSource();
+  }
+
   private function getProxyComment() {
     if (!$this->proxyComment) {
       $this->proxyComment = new DifferentialTransactionComment();
@@ -48,16 +259,14 @@
   }
 
   public function setProxyComment(DifferentialTransactionComment $proxy) {
-    if ($this->proxyComment) {
-      throw new Exception(pht('You can not overwrite a proxy comment.'));
-    }
     $this->proxyComment = $proxy;
     return $this;
   }
 
   public function setRevision(DifferentialRevision $revision) {
     $this->getProxyComment()->setRevisionPHID($revision->getPHID());
-    return $this->setRevisionID($revision->getID());
+    $this->proxy->setObjectPHID($revision->getPHID());
+    return $this;
   }
 
   public function giveFacebookSomeArbitraryDiff(DifferentialDiff $diff) {
@@ -66,48 +275,19 @@
   }
 
   public function getRequiredHandlePHIDs() {
-    $phids = array();
-
-    $metadata = $this->getMetadata();
-    $added_reviewers = idx(
-      $metadata,
-      self::METADATA_ADDED_REVIEWERS);
-    if ($added_reviewers) {
-      foreach ($added_reviewers as $phid) {
-        $phids[] = $phid;
-      }
-    }
-    $added_ccs = idx(
-      $metadata,
-      self::METADATA_ADDED_CCS);
-    if ($added_ccs) {
-      foreach ($added_ccs as $phid) {
-        $phids[] = $phid;
-      }
+    switch ($this->proxy->getTransactionType()) {
+      case PhabricatorTransactions::TYPE_SUBSCRIBERS:
+      case PhabricatorTransactions::TYPE_EDGE:
+        return array_merge(
+          array_keys($this->proxy->getOldValue()),
+          array_keys($this->proxy->getNewValue()));
     }
 
-    return $phids;
-  }
-
-  public function getConfiguration() {
-    return array(
-      self::CONFIG_SERIALIZATION => array(
-        'metadata' => self::SERIALIZATION_JSON,
-      ),
-    ) + parent::getConfiguration();
-  }
-
-  public function setContentSource(PhabricatorContentSource $content_source) {
-    $this->contentSource = $content_source->serialize();
-    return $this;
-  }
-
-  public function getContentSource() {
-    return PhabricatorContentSource::newFromSerialized($this->contentSource);
+    return array();
   }
 
   public function getMarkupFieldKey($field) {
-    return 'DC:'.$this->getID();
+    return 'DC:'.$this->getPHID();
   }
 
   public function newMarkupEngine($field) {
@@ -126,37 +306,49 @@
   }
 
   public function shouldUseMarkupCache($field) {
-    return (bool)$this->getID();
+    return (bool)$this->getPHID();
   }
 
-  public function save() {
-    $this->openTransaction();
-      $result = parent::save();
+  public function getDateCreated() {
+    return $this->proxy->getDateCreated();
+  }
 
-      if ($this->getContent() !== null) {
-        $content_source = PhabricatorContentSource::newForSource(
-          PhabricatorContentSource::SOURCE_LEGACY,
-          array());
+  public function getRevisionPHID() {
+    return $this->proxy->getObjectPHID();
+  }
 
-        $xaction_phid = PhabricatorPHID::generateNewPHID(
-          PhabricatorApplicationTransactionPHIDTypeTransaction::TYPECONST,
-          DifferentialPHIDTypeRevision::TYPECONST);
+  public function save() {
+    $this->proxy->openTransaction();
+      $this->proxy
+        ->setViewPolicy('public')
+        ->setEditPolicy($this->getAuthorPHID())
+        ->save();
+
+      if ($this->getContent() !== null ||
+          $this->getProxyComment()->getChangesetID()) {
 
-        $proxy = $this->getProxyComment();
-        $proxy
+        $this->getProxyComment()
           ->setAuthorPHID($this->getAuthorPHID())
           ->setViewPolicy('public')
           ->setEditPolicy($this->getAuthorPHID())
-          ->setContentSource($content_source)
           ->setCommentVersion(1)
-          ->setLegacyCommentID($this->getID())
-          ->setTransactionPHID($xaction_phid)
+          ->setTransactionPHID($this->proxy->getPHID())
+          ->save();
+
+        $this->proxy
+          ->setCommentVersion(1)
+          ->setCommentPHID($this->getProxyComment()->getPHID())
           ->save();
       }
 
-    $this->saveTransaction();
+    $this->proxy->saveTransaction();
 
-    return $result;
+    return $this;
+  }
+
+  public function delete() {
+    $this->proxy->delete();
+    return $this;
   }
 
 }
diff --git a/src/applications/differential/storage/DifferentialInlineComment.php b/src/applications/differential/storage/DifferentialInlineComment.php
--- a/src/applications/differential/storage/DifferentialInlineComment.php
+++ b/src/applications/differential/storage/DifferentialInlineComment.php
@@ -14,7 +14,7 @@
     $this->proxy = clone $this->proxy;
   }
 
-  public function save() {
+  public function getTransactionCommentForSave() {
     $content_source = PhabricatorContentSource::newForSource(
       PhabricatorContentSource::SOURCE_LEGACY,
       array());
@@ -23,8 +23,14 @@
       ->setViewPolicy('public')
       ->setEditPolicy($this->getAuthorPHID())
       ->setContentSource($content_source)
-      ->setCommentVersion(1)
-      ->save();
+      ->setCommentVersion(1);
+
+    return $this->proxy;
+  }
+
+
+  public function save() {
+    $this->getTransactionCommentForSave()->save();
 
     return $this;
   }
@@ -74,7 +80,7 @@
   }
 
   public function isDraft() {
-    return !$this->getCommentID();
+    return !$this->proxy->getTransactionPHID();
   }
 
   public function setChangesetID($id) {
diff --git a/src/applications/differential/storage/DifferentialRevision.php b/src/applications/differential/storage/DifferentialRevision.php
--- a/src/applications/differential/storage/DifferentialRevision.php
+++ b/src/applications/differential/storage/DifferentialRevision.php
@@ -158,7 +158,7 @@
       return array();
     }
     return id(new DifferentialCommentQuery())
-      ->withRevisionIDs(array($this->getID()))
+      ->withRevisionPHIDs(array($this->getPHID()))
       ->execute();
   }
 
@@ -194,7 +194,7 @@
         $this->getID());
 
       $comments = id(new DifferentialCommentQuery())
-        ->withRevisionIDs(array($this->getID()))
+        ->withRevisionPHIDs(array($this->getPHID()))
         ->execute();
       foreach ($comments as $comment) {
         $comment->delete();
diff --git a/src/applications/differential/storage/DifferentialTransaction.php b/src/applications/differential/storage/DifferentialTransaction.php
--- a/src/applications/differential/storage/DifferentialTransaction.php
+++ b/src/applications/differential/storage/DifferentialTransaction.php
@@ -2,6 +2,10 @@
 
 final class DifferentialTransaction extends PhabricatorApplicationTransaction {
 
+  const TYPE_INLINE = 'differential:inline';
+  const TYPE_UPDATE = 'differential:update';
+  const TYPE_ACTION = 'differential:action';
+
   public function getApplicationName() {
     return 'differential';
   }
diff --git a/src/applications/differential/view/DifferentialRevisionCommentListView.php b/src/applications/differential/view/DifferentialRevisionCommentListView.php
--- a/src/applications/differential/view/DifferentialRevisionCommentListView.php
+++ b/src/applications/differential/view/DifferentialRevisionCommentListView.php
@@ -93,7 +93,7 @@
       $view->setUser($this->user);
       $view->setHandles($this->handles);
       $view->setMarkupEngine($engine);
-      $view->setInlineComments(idx($inlines, $comment->getID(), array()));
+//      $view->setInlineComments(idx($inlines, $comment->getID(), array()));
       $view->setChangesets($this->changesets);
       $view->setTargetDiff($this->target);
       $view->setRevision($this->getRevision());
@@ -132,7 +132,7 @@
     $hidden = array();
     if ($last_comment !== null) {
       foreach ($objs as $position => $comment) {
-        if (!$comment->getID()) {
+        if (!$comment->getPHID()) {
           // These are synthetic comments with summary/test plan information.
           $header[] = $html[$position];
           unset($html[$position]);
diff --git a/src/applications/releeph/field/specification/ReleephDiffChurnFieldSpecification.php b/src/applications/releeph/field/specification/ReleephDiffChurnFieldSpecification.php
--- a/src/applications/releeph/field/specification/ReleephDiffChurnFieldSpecification.php
+++ b/src/applications/releeph/field/specification/ReleephDiffChurnFieldSpecification.php
@@ -24,7 +24,7 @@
 
     $diff_rev = $this->getReleephRequest()->loadDifferentialRevision();
     $comments = id(new DifferentialCommentQuery())
-      ->withRevisionIDs(array($diff_rev->getID()))
+      ->withRevisionPHIDs(array($diff_rev->getPHID()))
       ->execute();
 
     $counts = array();