diff --git a/resources/celerity/map.php b/resources/celerity/map.php
--- a/resources/celerity/map.php
+++ b/resources/celerity/map.php
@@ -13,7 +13,7 @@
     'core.pkg.js' => '1475bd91',
     'darkconsole.pkg.js' => '1f9a31bc',
     'differential.pkg.css' => '1ccbf3a9',
-    'differential.pkg.js' => 'b2c4cbfa',
+    'differential.pkg.js' => '889ab0ab',
     'diffusion.pkg.css' => 'b93d9b8c',
     'diffusion.pkg.js' => '84c8f8fd',
     'favicon.ico' => '30672e08',
@@ -393,8 +393,8 @@
     'rsrc/js/application/dashboard/behavior-dashboard-move-panels.js' => '408bf173',
     'rsrc/js/application/dashboard/behavior-dashboard-query-panel-select.js' => '453c5375',
     'rsrc/js/application/dashboard/behavior-dashboard-tab-panel.js' => 'd4eecc63',
-    'rsrc/js/application/diff/DiffChangeset.js' => 'ee50a0ec',
-    'rsrc/js/application/diff/DiffChangesetList.js' => 'b4cf2217',
+    'rsrc/js/application/diff/DiffChangeset.js' => 'd498bddb',
+    'rsrc/js/application/diff/DiffChangesetList.js' => '0db8cdca',
     'rsrc/js/application/diff/DiffInline.js' => '1d17130f',
     'rsrc/js/application/diff/behavior-preview-link.js' => '051c7832',
     'rsrc/js/application/differential/behavior-comment-preview.js' => '51c5ad07',
@@ -772,8 +772,8 @@
     'phabricator-darklog' => 'c8e1ffe3',
     'phabricator-darkmessage' => 'c48cccdd',
     'phabricator-dashboard-css' => 'fe5b1869',
-    'phabricator-diff-changeset' => 'ee50a0ec',
-    'phabricator-diff-changeset-list' => 'b4cf2217',
+    'phabricator-diff-changeset' => 'd498bddb',
+    'phabricator-diff-changeset-list' => '0db8cdca',
     'phabricator-diff-inline' => '1d17130f',
     'phabricator-drag-and-drop-file-upload' => '58dea2fa',
     'phabricator-draggable-list' => 'bea6e7f4',
@@ -960,6 +960,10 @@
       'javelin-dom',
       'javelin-router',
     ),
+    '0db8cdca' => array(
+      'javelin-install',
+      'phuix-button-view',
+    ),
     '0f764c35' => array(
       'javelin-install',
       'javelin-util',
@@ -1771,10 +1775,6 @@
     'b3e7d692' => array(
       'javelin-install',
     ),
-    'b4cf2217' => array(
-      'javelin-install',
-      'phuix-button-view',
-    ),
     'b59e1e96' => array(
       'javelin-behavior',
       'javelin-stratcom',
@@ -1978,6 +1978,17 @@
       'javelin-uri',
       'javelin-util',
     ),
+    'd498bddb' => array(
+      'javelin-dom',
+      'javelin-util',
+      'javelin-stratcom',
+      'javelin-install',
+      'javelin-workflow',
+      'javelin-router',
+      'javelin-behavior-device',
+      'javelin-vector',
+      'phabricator-diff-inline',
+    ),
     'd4eecc63' => array(
       'javelin-behavior',
       'javelin-dom',
@@ -2092,17 +2103,6 @@
       'javelin-behavior',
       'javelin-uri',
     ),
-    'ee50a0ec' => array(
-      'javelin-dom',
-      'javelin-util',
-      'javelin-stratcom',
-      'javelin-install',
-      'javelin-workflow',
-      'javelin-router',
-      'javelin-behavior-device',
-      'javelin-vector',
-      'phabricator-diff-inline',
-    ),
     'efe49472' => array(
       'javelin-install',
       'javelin-util',
diff --git a/webroot/rsrc/js/application/diff/DiffChangeset.js b/webroot/rsrc/js/application/diff/DiffChangeset.js
--- a/webroot/rsrc/js/application/diff/DiffChangeset.js
+++ b/webroot/rsrc/js/application/diff/DiffChangeset.js
@@ -440,6 +440,8 @@
               last_inline = inline;
             }
 
+            var is_saved = (!inline.isDraft() && !inline.isEditing());
+
             last_inline_item = {
               type: block.type,
               changeset: this,
@@ -452,7 +454,9 @@
               },
               attributes: {
                 unsaved: inline.isEditing(),
-                unsubmitted: inline.isDraft()
+                unsubmitted: inline.isDraft(),
+                undone: (is_saved && !inline.isDone()),
+                done: (is_saved && inline.isDone())
               }
             };
 
diff --git a/webroot/rsrc/js/application/diff/DiffChangesetList.js b/webroot/rsrc/js/application/diff/DiffChangesetList.js
--- a/webroot/rsrc/js/application/diff/DiffChangesetList.js
+++ b/webroot/rsrc/js/application/diff/DiffChangesetList.js
@@ -114,6 +114,8 @@
     _bannerNode: null,
     _unsavedButton: null,
     _unsubmittedButton: null,
+    _doneButton: null,
+    _doneMode: null,
 
     sleep: function() {
       this._asleep = true;
@@ -531,7 +533,7 @@
         break;
       }
 
-      this._setSelectionState(items[cursor]);
+      this._setSelectionState(items[cursor], true);
     },
 
     _getSelectionState: function() {
@@ -554,9 +556,9 @@
       };
     },
 
-    _setSelectionState: function(item) {
+    _setSelectionState: function(item, scroll) {
       this._cursorItem = item;
-      this._redrawSelection(true);
+      this._redrawSelection(scroll);
 
       return this;
     },
@@ -598,7 +600,7 @@
 
       var state = this._getSelectionState();
       if (state.cursor !== null) {
-        this._setSelectionState(state.items[state.cursor]);
+        this._setSelectionState(state.items[state.cursor], false);
       }
     },
 
@@ -910,7 +912,7 @@
       if (selection.cursor !== null) {
         item = selection.items[selection.cursor];
         if (item.target === inline) {
-          this._setSelectionState(null);
+          this._setSelectionState(null, false);
           return;
         }
       }
@@ -922,7 +924,7 @@
       for (var ii = 0; ii < items.length; ii++) {
         item = items[ii];
         if (item.target === inline) {
-          this._setSelectionState(item);
+          this._setSelectionState(item, false);
         }
       }
     },
@@ -1339,7 +1341,7 @@
       var unsaved = [];
       var unsubmitted = [];
       var undone = [];
-      var all = [];
+      var done = [];
 
       for (var ii = 0; ii < changesets.length; ii++) {
         var inlines = changesets[ii].getInlines();
@@ -1350,14 +1352,14 @@
             continue;
           }
 
-          all.push(inline);
-
           if (inline.isEditing()) {
             unsaved.push(inline);
           } else if (inline.isDraft()) {
             unsubmitted.push(inline);
           } else if (!inline.isDone()) {
             undone.push(inline);
+          } else {
+            done.push(inline);
           }
         }
       }
@@ -1375,6 +1377,7 @@
       var pht = this.getTranslations();
       var unsaved_button = this._getUnsavedButton();
       var unsubmitted_button = this._getUnsubmittedButton();
+      var done_button = this._getDoneButton();
 
       if (unsaved.length) {
         unsaved_button.setText(unsaved.length + ' ' + pht('Unsaved'));
@@ -1391,6 +1394,30 @@
         JX.DOM.hide(unsubmitted_button.getNode());
       }
 
+      if (done.length || undone.length) {
+        done_button.setText([
+          done.length,
+          ' / ',
+          (done.length + undone.length),
+          ' ',
+          pht('Comments')
+        ]);
+
+        JX.DOM.show(done_button.getNode());
+
+        // If any comments are not marked "Done", this cycles through the
+        // missing comments. Otherwise, it cycles through all the saved
+        // comments.
+        if (undone.length) {
+          this._doneMode = 'undone';
+        } else {
+          this._doneMode = 'done';
+        }
+
+      } else {
+        JX.DOM.hide(done_button.getNode());
+      }
+
       var path_view = [icon, ' ', changeset.getDisplayPath()];
 
       var buttons_attrs = {
@@ -1399,7 +1426,8 @@
 
       var buttons_list = [
         unsaved_button.getNode(),
-        unsubmitted_button.getNode()
+        unsubmitted_button.getNode(),
+        done_button.getNode()
       ];
 
       var buttons_view = JX.$N('div', buttons_attrs, buttons_list);
@@ -1446,6 +1474,22 @@
       return this._unsubmittedButton;
     },
 
+    _getDoneButton: function() {
+      if (!this._doneButton) {
+        var button = new JX.PHUIXButtonView()
+          .setIcon('fa-comment')
+          .setButtonType(JX.PHUIXButtonView.BUTTONTYPE_SIMPLE);
+
+        var node = button.getNode();
+
+        var ondone = JX.bind(this, this._ondoneclick);
+        JX.DOM.listen(node, 'click', null, ondone);
+
+        this._doneButton = button;
+      }
+
+      return this._doneButton;
+    },
     _onunsavedclick: function(e) {
       e.kill();
 
@@ -1470,6 +1514,18 @@
       this._onjumpkey(1, options);
     },
 
+    _ondoneclick: function(e) {
+      e.kill();
+
+      var options = {
+        filter: 'comment',
+        wrap: true,
+        attribute: this._doneMode
+      };
+
+      this._onjumpkey(1, options);
+    },
+
     _getBannerNode: function() {
       if (!this._bannerNode) {
         var attributes = {