diff --git a/resources/celerity/map.php b/resources/celerity/map.php
--- a/resources/celerity/map.php
+++ b/resources/celerity/map.php
@@ -427,7 +427,7 @@
     'rsrc/js/application/repository/repository-crossreference.js' => 'e5339c43',
     'rsrc/js/application/search/behavior-reorder-queries.js' => 'e9581f08',
     'rsrc/js/application/slowvote/behavior-slowvote-embed.js' => '887ad43f',
-    'rsrc/js/application/transactions/behavior-comment-actions.js' => '6de53e91',
+    'rsrc/js/application/transactions/behavior-comment-actions.js' => 'bb0d2d0c',
     'rsrc/js/application/transactions/behavior-reorder-configs.js' => 'd7a74243',
     'rsrc/js/application/transactions/behavior-reorder-fields.js' => 'b59e1e96',
     'rsrc/js/application/transactions/behavior-show-older-transactions.js' => 'dbbf48b6',
@@ -572,7 +572,7 @@
     'javelin-behavior-audit-preview' => 'd835b03a',
     'javelin-behavior-bulk-job-reload' => 'edf8a145',
     'javelin-behavior-choose-control' => '6153c708',
-    'javelin-behavior-comment-actions' => '6de53e91',
+    'javelin-behavior-comment-actions' => 'bb0d2d0c',
     'javelin-behavior-config-reorder-fields' => 'b6993408',
     'javelin-behavior-conpherence-drag-and-drop-photo' => 'cf86d16a',
     'javelin-behavior-conpherence-menu' => '1d45c74d',
@@ -1337,14 +1337,6 @@
       'phabricator-drag-and-drop-file-upload',
       'phabricator-textareautils',
     ),
-    '6de53e91' => array(
-      'javelin-behavior',
-      'javelin-stratcom',
-      'javelin-workflow',
-      'javelin-dom',
-      'phuix-form-control-view',
-      'phuix-icon-view',
-    ),
     '6eff08aa' => array(
       'javelin-install',
       'javelin-util',
@@ -1741,6 +1733,14 @@
       'javelin-workflow',
       'phabricator-draggable-list',
     ),
+    'bb0d2d0c' => array(
+      'javelin-behavior',
+      'javelin-stratcom',
+      'javelin-workflow',
+      'javelin-dom',
+      'phuix-form-control-view',
+      'phuix-icon-view',
+    ),
     'bd4c8dca' => array(
       'javelin-install',
       'javelin-util',
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
@@ -2150,6 +2150,7 @@
     'PhabricatorEdgeTypeTestCase' => 'infrastructure/edges/type/__tests__/PhabricatorEdgeTypeTestCase.php',
     'PhabricatorEditEngine' => 'applications/transactions/editengine/PhabricatorEditEngine.php',
     'PhabricatorEditEngineAPIMethod' => 'applications/transactions/editengine/PhabricatorEditEngineAPIMethod.php',
+    'PhabricatorEditEngineCommentAction' => 'applications/transactions/commentaction/PhabricatorEditEngineCommentAction.php',
     'PhabricatorEditEngineConfiguration' => 'applications/transactions/storage/PhabricatorEditEngineConfiguration.php',
     'PhabricatorEditEngineConfigurationDefaultCreateController' => 'applications/transactions/controller/PhabricatorEditEngineConfigurationDefaultCreateController.php',
     'PhabricatorEditEngineConfigurationDefaultsController' => 'applications/transactions/controller/PhabricatorEditEngineConfigurationDefaultsController.php',
@@ -2175,6 +2176,8 @@
     'PhabricatorEditEngineListController' => 'applications/transactions/controller/PhabricatorEditEngineListController.php',
     'PhabricatorEditEngineQuery' => 'applications/transactions/query/PhabricatorEditEngineQuery.php',
     'PhabricatorEditEngineSearchEngine' => 'applications/transactions/query/PhabricatorEditEngineSearchEngine.php',
+    'PhabricatorEditEngineSelectCommentAction' => 'applications/transactions/commentaction/PhabricatorEditEngineSelectCommentAction.php',
+    'PhabricatorEditEngineTokenizerCommentAction' => 'applications/transactions/commentaction/PhabricatorEditEngineTokenizerCommentAction.php',
     'PhabricatorEditField' => 'applications/transactions/editfield/PhabricatorEditField.php',
     'PhabricatorEditType' => 'applications/transactions/edittype/PhabricatorEditType.php',
     'PhabricatorEditor' => 'infrastructure/PhabricatorEditor.php',
@@ -6308,6 +6311,7 @@
       'PhabricatorPolicyInterface',
     ),
     'PhabricatorEditEngineAPIMethod' => 'ConduitAPIMethod',
+    'PhabricatorEditEngineCommentAction' => 'Phobject',
     'PhabricatorEditEngineConfiguration' => array(
       'PhabricatorSearchDAO',
       'PhabricatorApplicationTransactionInterface',
@@ -6337,6 +6341,8 @@
     'PhabricatorEditEngineListController' => 'PhabricatorEditEngineController',
     'PhabricatorEditEngineQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
     'PhabricatorEditEngineSearchEngine' => 'PhabricatorApplicationSearchEngine',
+    'PhabricatorEditEngineSelectCommentAction' => 'PhabricatorEditEngineCommentAction',
+    'PhabricatorEditEngineTokenizerCommentAction' => 'PhabricatorEditEngineCommentAction',
     'PhabricatorEditField' => 'Phobject',
     'PhabricatorEditType' => 'Phobject',
     'PhabricatorEditor' => 'Phobject',
diff --git a/src/applications/maniphest/editor/ManiphestEditEngine.php b/src/applications/maniphest/editor/ManiphestEditEngine.php
--- a/src/applications/maniphest/editor/ManiphestEditEngine.php
+++ b/src/applications/maniphest/editor/ManiphestEditEngine.php
@@ -110,7 +110,7 @@
         ->setIsCopyable(true)
         ->setSingleValue($object->getOwnerPHID())
         ->setCommentActionLabel(pht('Assign / Claim'))
-        ->setCommentActionDefaultValue($owner_value),
+        ->setCommentActionValue($owner_value),
       id(new PhabricatorSelectEditField())
         ->setKey('status')
         ->setLabel(pht('Status'))
@@ -120,7 +120,7 @@
         ->setValue($object->getStatus())
         ->setOptions($status_map)
         ->setCommentActionLabel(pht('Change Status'))
-        ->setCommentActionDefaultValue($default_status),
+        ->setCommentActionValue($default_status),
       id(new PhabricatorSelectEditField())
         ->setKey('priority')
         ->setLabel(pht('Priority'))
diff --git a/src/applications/project/engineextension/PhabricatorProjectsEditEngineExtension.php b/src/applications/project/engineextension/PhabricatorProjectsEditEngineExtension.php
--- a/src/applications/project/engineextension/PhabricatorProjectsEditEngineExtension.php
+++ b/src/applications/project/engineextension/PhabricatorProjectsEditEngineExtension.php
@@ -54,7 +54,7 @@
         pht('Add projects.'),
         pht('Remove projects.'),
         pht('Set associated projects, overwriting current value.'))
-      ->setCommentActionLabel(pht('Add Projects'))
+      ->setCommentActionLabel(pht('Change Projects'))
       ->setTransactionType($edge_type)
       ->setMetadataValue('edge:type', $project_edge_type)
       ->setValue($project_phids);
diff --git a/src/applications/subscriptions/engineextension/PhabricatorSubscriptionsEditEngineExtension.php b/src/applications/subscriptions/engineextension/PhabricatorSubscriptionsEditEngineExtension.php
--- a/src/applications/subscriptions/engineextension/PhabricatorSubscriptionsEditEngineExtension.php
+++ b/src/applications/subscriptions/engineextension/PhabricatorSubscriptionsEditEngineExtension.php
@@ -49,7 +49,7 @@
         pht('Add subscribers.'),
         pht('Remove subscribers.'),
         pht('Set subscribers, overwriting current value.'))
-      ->setCommentActionLabel(pht('Add Subscribers'))
+      ->setCommentActionLabel(pht('Change Subscribers'))
       ->setTransactionType($subscribers_type)
       ->setValue($sub_phids);
 
diff --git a/src/applications/transactions/commentaction/PhabricatorEditEngineCommentAction.php b/src/applications/transactions/commentaction/PhabricatorEditEngineCommentAction.php
new file mode 100644
--- /dev/null
+++ b/src/applications/transactions/commentaction/PhabricatorEditEngineCommentAction.php
@@ -0,0 +1,49 @@
+<?php
+
+abstract class PhabricatorEditEngineCommentAction extends Phobject {
+
+  private $key;
+  private $label;
+  private $value;
+  private $initialValue;
+
+  abstract public function getPHUIXControlType();
+  abstract public function getPHUIXControlSpecification();
+
+  public function setKey($key) {
+    $this->key = $key;
+    return $this;
+  }
+
+  public function getKey() {
+    return $this->key;
+  }
+
+  public function setLabel($label) {
+    $this->label = $label;
+    return $this;
+  }
+
+  public function getLabel() {
+    return $this->label;
+  }
+
+  public function setValue($value) {
+    $this->value = $value;
+    return $this;
+  }
+
+  public function getValue() {
+    return $this->value;
+  }
+
+  public function setInitialValue($initial_value) {
+    $this->initialValue = $initial_value;
+    return $this;
+  }
+
+  public function getInitialValue() {
+    return $this->initialValue;
+  }
+
+}
diff --git a/src/applications/transactions/commentaction/PhabricatorEditEngineSelectCommentAction.php b/src/applications/transactions/commentaction/PhabricatorEditEngineSelectCommentAction.php
new file mode 100644
--- /dev/null
+++ b/src/applications/transactions/commentaction/PhabricatorEditEngineSelectCommentAction.php
@@ -0,0 +1,29 @@
+<?php
+
+final class PhabricatorEditEngineSelectCommentAction
+  extends PhabricatorEditEngineCommentAction {
+
+  private $options;
+
+  public function setOptions(array $options) {
+    $this->options = $options;
+    return $this;
+  }
+
+  public function getOptions() {
+    return $this->options;
+  }
+
+  public function getPHUIXControlType() {
+    return 'select';
+  }
+
+  public function getPHUIXControlSpecification() {
+    return array(
+      'options' => $this->getOptions(),
+      'order' => array_keys($this->getOptions()),
+      'value' => $this->getValue(),
+    );
+  }
+
+}
diff --git a/src/applications/transactions/commentaction/PhabricatorEditEngineTokenizerCommentAction.php b/src/applications/transactions/commentaction/PhabricatorEditEngineTokenizerCommentAction.php
new file mode 100644
--- /dev/null
+++ b/src/applications/transactions/commentaction/PhabricatorEditEngineTokenizerCommentAction.php
@@ -0,0 +1,55 @@
+<?php
+
+final class PhabricatorEditEngineTokenizerCommentAction
+  extends PhabricatorEditEngineCommentAction {
+
+  private $datasource;
+  private $limit;
+
+  public function setDatasource(PhabricatorTypeaheadDatasource $datasource) {
+    $this->datasource = $datasource;
+    return $this;
+  }
+
+  public function getDatasource() {
+    return $this->datasource;
+  }
+
+  public function setLimit($limit) {
+    $this->limit = $limit;
+    return $this;
+  }
+
+  public function getLimit() {
+    return $this->limit;
+  }
+
+  public function getPHUIXControlType() {
+    return 'tokenizer';
+  }
+
+  public function getPHUIXControlSpecification() {
+    $template = new AphrontTokenizerTemplateView();
+
+    $datasource = $this->getDatasource();
+    $limit = $this->getLimit();
+
+    $value = $this->getValue();
+    if (!$value) {
+      $value = array();
+    }
+    $value = $datasource->getWireTokens($value);
+
+    return array(
+      'markup' => $template->render(),
+      'config' => array(
+        'src' => $datasource->getDatasourceURI(),
+        'browseURI' => $datasource->getBrowseURI(),
+        'placeholder' => $datasource->getPlaceholderText(),
+        'limit' => $limit,
+      ),
+      'value' => $value,
+    );
+  }
+
+}
diff --git a/src/applications/transactions/editengine/PhabricatorEditEngine.php b/src/applications/transactions/editengine/PhabricatorEditEngine.php
--- a/src/applications/transactions/editengine/PhabricatorEditEngine.php
+++ b/src/applications/transactions/editengine/PhabricatorEditEngine.php
@@ -792,10 +792,17 @@
 
     $validation_exception = null;
     if ($request->isFormPost()) {
-      foreach ($fields as $field) {
+      $submit_fields = $fields;
+
+      foreach ($submit_fields as $key => $field) {
+        if (!$field->shouldGenerateTransactionsFromSubmit()) {
+          unset($submit_fields[$key]);
+          continue;
+        }
+
         $field->setIsSubmittedForm(true);
 
-        if ($field->getIsLocked() || $field->getIsHidden()) {
+        if (!$field->shouldReadValueFromSubmit()) {
           continue;
         }
 
@@ -803,22 +810,15 @@
       }
 
       $xactions = array();
-      foreach ($fields as $field) {
-        $types = $field->getWebEditTypes();
-        foreach ($types as $type) {
-          $type_xactions = $type->generateTransactions(
-            clone $template,
-            array(
-              'value' => $field->getValueForTransaction(),
-            ));
-
-          if (!$type_xactions) {
-            continue;
-          }
+      foreach ($submit_fields as $field) {
+        $type_xactions = $field->generateTransactions(
+          clone $template,
+          array(
+            'value' => $field->getValueForTransaction(),
+          ));
 
-          foreach ($type_xactions as $type_xaction) {
-            $xactions[] = $type_xaction;
-          }
+        foreach ($type_xactions as $type_xaction) {
+          $xactions[] = $type_xaction;
         }
       }
 
@@ -879,7 +879,7 @@
         }
 
         foreach ($fields as $field) {
-          if ($field->getIsLocked() || $field->getIsHidden()) {
+          if (!$field->shouldReadValueFromRequest()) {
             continue;
           }
 
@@ -1171,19 +1171,25 @@
 
     $fields = $this->buildEditFields($object);
 
-    $all_types = array();
+    $comment_actions = array();
     foreach ($fields as $field) {
-      if (!$this->isCommentField($field)) {
+      if (!$field->shouldGenerateTransactionsFromComment()) {
         continue;
       }
 
-      $types = $field->getCommentEditTypes();
-      foreach ($types as $type) {
-        $all_types[] = $type;
+      $comment_action = $field->getCommentAction();
+      if (!$comment_action) {
+        continue;
       }
+
+      $key = $comment_action->getKey();
+
+      // TODO: Validate these better.
+
+      $comment_actions[$key] = $comment_action;
     }
 
-    $view->setEditTypes($all_types);
+    $view->setCommentActions($comment_actions);
 
     return $view;
   }
@@ -1378,39 +1384,35 @@
     $xactions = array();
 
     if ($actions) {
-      $type_map = array();
-      foreach ($fields as $field) {
-        if (!$this->isCommentField($field)) {
+      $action_map = array();
+      foreach ($actions as $action) {
+        $type = idx($action, 'type');
+        if (!$type) {
           continue;
         }
 
-        $types = $field->getCommentEditTypes();
-        foreach ($types as $type) {
-          $type_map[$type->getEditType()] = array(
-            'type' => $type,
-            'field' => $field,
-          );
+        if (empty($fields[$type])) {
+          continue;
         }
+
+        $action_map[$type] = $action;
       }
 
-      foreach ($actions as $action) {
-        $type = idx($action, 'type');
-        if (!$type) {
-          continue;
-        }
+      foreach ($action_map as $type => $action) {
+        $field = $fields[$type];
 
-        $spec = idx($type_map, $type);
-        if (!$spec) {
+        if (!$field->shouldGenerateTransactionsFromComment()) {
           continue;
         }
 
-        $edit_type = $spec['type'];
-        $field = $spec['field'];
+        if (array_key_exists('initialValue', $action)) {
+          $field->setInitialValue($action['initialValue']);
+        }
 
-        $field->readValueFromComment($action);
+        $field->readValueFromComment(idx($action, 'value'));
 
-        $type_xactions = $edit_type->generateTransactions(
-          $template,
+        $type_xactions = $field->generateTransactions(
+          clone $template,
           array(
             'value' => $field->getValueForTransaction(),
           ));
@@ -1509,6 +1511,10 @@
       ->setContentSourceFromConduitRequest($request)
       ->setContinueOnNoEffect(true);
 
+    if (!$this->getIsCreate()) {
+      $editor->setContinueOnMissingFields(true);
+    }
+
     $xactions = $editor->applyTransactions($object, $xactions);
 
     $xactions_struct = array();
@@ -1729,23 +1735,6 @@
       PhabricatorPolicyCapability::CAN_EDIT);
   }
 
-  private function isCommentField(PhabricatorEditField $field) {
-    // TODO: This is a little bit hacky.
-    if ($field->getKey() == 'comment') {
-      return true;
-    }
-
-    if ($field->getIsLocked()) {
-      return false;
-    }
-
-    if ($field->getIsHidden()) {
-      return false;
-    }
-
-    return true;
-  }
-
 
 /* -(  PhabricatorPolicyInterface  )----------------------------------------- */
 
diff --git a/src/applications/transactions/editfield/PhabricatorCommentEditField.php b/src/applications/transactions/editfield/PhabricatorCommentEditField.php
--- a/src/applications/transactions/editfield/PhabricatorCommentEditField.php
+++ b/src/applications/transactions/editfield/PhabricatorCommentEditField.php
@@ -11,4 +11,8 @@
     return new PhabricatorCommentEditType();
   }
 
+  public function shouldGenerateTransactionsFromComment() {
+    return true;
+  }
+
 }
diff --git a/src/applications/transactions/editfield/PhabricatorEditField.php b/src/applications/transactions/editfield/PhabricatorEditField.php
--- a/src/applications/transactions/editfield/PhabricatorEditField.php
+++ b/src/applications/transactions/editfield/PhabricatorEditField.php
@@ -15,7 +15,10 @@
   private $description;
   private $editTypeKey;
   private $isRequired;
+
   private $commentActionLabel;
+  private $commentActionValue;
+  private $hasCommentActionValue;
 
   private $isLocked;
   private $isHidden;
@@ -202,6 +205,16 @@
     return $this->commentActionLabel;
   }
 
+  public function setCommentActionValue($comment_action_value) {
+    $this->hasCommentActionValue = true;
+    $this->commentActionValue = $comment_action_value;
+    return $this;
+  }
+
+  public function getCommentActionValue() {
+    return $this->commentActionValue;
+  }
+
   protected function newControl() {
     throw new PhutilMethodNotImplementedException();
   }
@@ -345,8 +358,8 @@
     return $this;
   }
 
-  public function readValueFromComment($action) {
-    $this->value = $this->getValueFromComment(idx($action, 'value'));
+  public function readValueFromComment($value) {
+    $this->value = $this->getValueFromComment($value);
     return $this;
   }
 
@@ -424,6 +437,11 @@
     return $this->initialValue;
   }
 
+  public function setInitialValue($initial_value) {
+    $this->initialValue = $initial_value;
+    return $this;
+  }
+
   public function readValueFromSubmit(AphrontRequest $request) {
     $key = $this->getKey();
     if ($this->getValueExistsInSubmit($request, $key)) {
@@ -548,22 +566,115 @@
     return array($edit_type);
   }
 
-  public function getWebEditTypes() {
+  public function getCommentAction() {
+    $label = $this->getCommentActionLabel();
+    if ($label === null) {
+      return null;
+    }
+
+    $action = $this->newCommentAction();
+    if ($action === null) {
+      return null;
+    }
+
+    if ($this->hasCommentActionValue) {
+      $value = $this->getCommentActionValue();
+    } else {
+      $value = $this->getValue();
+    }
+
+    $action
+      ->setKey($this->getKey())
+      ->setLabel($label)
+      ->setValue($this->getValueForCommentAction($value));
+
+    return $action;
+  }
+
+  protected function newCommentAction() {
+    return null;
+  }
+
+  protected function getValueForCommentAction($value) {
+    return $value;
+  }
+
+  public function shouldGenerateTransactionsFromSubmit() {
     if ($this->getIsConduitOnly()) {
-      return array();
+      return false;
     }
 
     $edit_type = $this->getEditType();
+    if (!$edit_type) {
+      return false;
+    }
 
-    if ($edit_type === null) {
-      return array();
+    return true;
+  }
+
+  public function shouldReadValueFromRequest() {
+    if ($this->getIsConduitOnly()) {
+      return false;
     }
 
-    return array($edit_type);
+    if ($this->getIsLocked()) {
+      return false;
+    }
+
+    if ($this->getIsHidden()) {
+      return false;
+    }
+
+    return true;
   }
 
-  public function getCommentEditTypes() {
-    return array();
+  public function shouldReadValueFromSubmit() {
+    if ($this->getIsConduitOnly()) {
+      return false;
+    }
+
+    if ($this->getIsLocked()) {
+      return false;
+    }
+
+    if ($this->getIsHidden()) {
+      return false;
+    }
+
+    return true;
+  }
+
+  public function shouldGenerateTransactionsFromComment() {
+    if ($this->getIsConduitOnly()) {
+      return false;
+    }
+
+    if ($this->getIsLocked()) {
+      return false;
+    }
+
+    if ($this->getIsHidden()) {
+      return false;
+    }
+
+    return true;
+  }
+
+  public function generateTransactions(
+    PhabricatorApplicationTransaction $template,
+    array $spec) {
+
+    $edit_type = $this->getEditType();
+    if (!$edit_type) {
+      throw new Exception(
+        pht(
+          'EditField (with key "%s", of class "%s") is generating '.
+          'transactions, but has no EditType.',
+          $this->getKey(),
+          get_class($this)));
+    }
+
+    return $edit_type->generateTransactions($template, $spec);
   }
 
 }
diff --git a/src/applications/transactions/editfield/PhabricatorPHIDListEditField.php b/src/applications/transactions/editfield/PhabricatorPHIDListEditField.php
--- a/src/applications/transactions/editfield/PhabricatorPHIDListEditField.php
+++ b/src/applications/transactions/editfield/PhabricatorPHIDListEditField.php
@@ -44,14 +44,6 @@
     return new AphrontPHIDListHTTPParameterType();
   }
 
-  public function readValueFromComment($value) {
-    // TODO: This is really hacky -- make sure we pass a plain PHID list to
-    // the edit type. This method probably needs to move down to EditType, and
-    // maybe more additional logic does too.
-    $this->setUseEdgeTransactions(false);
-    return parent::readValueFromComment($value);
-  }
-
   protected function getValueFromRequest(AphrontRequest $request, $key) {
     $value = parent::getValueFromRequest($request, $key);
     if ($this->getIsSingleValue()) {
diff --git a/src/applications/transactions/editfield/PhabricatorSelectEditField.php b/src/applications/transactions/editfield/PhabricatorSelectEditField.php
--- a/src/applications/transactions/editfield/PhabricatorSelectEditField.php
+++ b/src/applications/transactions/editfield/PhabricatorSelectEditField.php
@@ -4,7 +4,6 @@
   extends PhabricatorEditField {
 
   private $options;
-  private $commentActionDefaultValue;
 
   public function setOptions(array $options) {
     $this->options = $options;
@@ -18,15 +17,6 @@
     return $this->options;
   }
 
-  public function setCommentActionDefaultValue($default) {
-    $this->commentActionDefaultValue = $default;
-    return $this;
-  }
-
-  public function getCommentActionDefaultValue() {
-    return $this->commentActionDefaultValue;
-  }
-
   protected function newControl() {
     return id(new AphrontFormSelectControl())
       ->setOptions($this->getOptions());
@@ -36,28 +26,9 @@
     return new AphrontSelectHTTPParameterType();
   }
 
-  public function getCommentEditTypes() {
-    $label = $this->getCommentActionLabel();
-    if ($label === null) {
-      return array();
-    }
-
-    $default_value = $this->getCommentActionDefaultValue();
-    if ($default_value === null) {
-      $default_value = $this->getValue();
-    }
-
-    $edit = $this->getEditType()
-      ->setLabel($label)
-      ->setPHUIXControlType('select')
-      ->setPHUIXControlSpecification(
-        array(
-          'options' => $this->getOptions(),
-          'order' => array_keys($this->getOptions()),
-          'value' => $default_value,
-        ));
-
-    return array($edit);
+  protected function newCommentAction() {
+    return id(new PhabricatorEditEngineSelectCommentAction())
+      ->setOptions($this->getOptions());
   }
 
 }
diff --git a/src/applications/transactions/editfield/PhabricatorTokenizerEditField.php b/src/applications/transactions/editfield/PhabricatorTokenizerEditField.php
--- a/src/applications/transactions/editfield/PhabricatorTokenizerEditField.php
+++ b/src/applications/transactions/editfield/PhabricatorTokenizerEditField.php
@@ -3,26 +3,15 @@
 abstract class PhabricatorTokenizerEditField
   extends PhabricatorPHIDListEditField {
 
-  private $commentActionDefaultValue;
-
   abstract protected function newDatasource();
 
-  public function setCommentActionDefaultValue(array $default) {
-    $this->commentActionDefaultValue = $default;
-    return $this;
-  }
-
-  public function getCommentActionDefaultValue() {
-    return $this->commentActionDefaultValue;
-  }
-
   protected function newControl() {
     $control = id(new AphrontFormTokenizerControl())
       ->setDatasource($this->newDatasource());
 
     $initial_value = $this->getInitialValue();
     if ($initial_value !== null) {
-      $control->setOriginalValue($initial_value);
+      $control->setInitialValue($initial_value);
     }
 
     if ($this->getIsSingleValue()) {
@@ -33,7 +22,7 @@
   }
 
   protected function getInitialValueFromSubmit(AphrontRequest $request, $key) {
-    return $request->getArr($key.'.original');
+    return $request->getArr($key.'.initial');
   }
 
   protected function newEditType() {
@@ -46,38 +35,25 @@
     return $type;
   }
 
-  public function getCommentEditTypes() {
-    $label = $this->getCommentActionLabel();
-    if ($label === null) {
-      return array();
-    }
+  protected function newCommentAction() {
+    $viewer = $this->getViewer();
 
-    $transaction_type = $this->getTransactionType();
-    if ($transaction_type === null) {
-      return array();
-    }
-
-    if ($this->getUseEdgeTransactions()) {
-      $type_key = $this->getEditTypeKey();
-      $base = $this->getEditType();
+    $datasource = $this->newDatasource()
+      ->setViewer($viewer);
 
-      $add = id(clone $base)
-        ->setEditType($type_key.'.add')
-        ->setEdgeOperation('+')
-        ->setLabel($label);
+    $action = id(new PhabricatorEditEngineTokenizerCommentAction())
+      ->setDatasource($datasource);
 
-      return array($add);
+    if ($this->getIsSingleValue()) {
+      $action->setLimit(1);
     }
 
-    $edit = $this->getEditType()
-      ->setLabel($label);
-
-    $default = $this->getCommentActionDefaultValue();
-    if ($default) {
-      $edit->setDefaultValue($default);
+    $initial_value = $this->getInitialValue();
+    if ($initial_value !== null) {
+      $action->setInitialValue($initial_value);
     }
 
-    return array($edit);
+    return $action;
   }
 
 }
diff --git a/src/applications/transactions/edittype/PhabricatorEditType.php b/src/applications/transactions/edittype/PhabricatorEditType.php
--- a/src/applications/transactions/edittype/PhabricatorEditType.php
+++ b/src/applications/transactions/edittype/PhabricatorEditType.php
@@ -96,16 +96,4 @@
     return $xaction;
   }
 
-  public function getPHUIXControlType() {
-    return null;
-  }
-
-  public function getPHUIXControlSpecification() {
-    return null;
-  }
-
-  public function getCommentActionValueFromDraftValue($value) {
-    return $value;
-  }
-
 }
diff --git a/src/applications/transactions/edittype/PhabricatorPHIDListEditType.php b/src/applications/transactions/edittype/PhabricatorPHIDListEditType.php
--- a/src/applications/transactions/edittype/PhabricatorPHIDListEditType.php
+++ b/src/applications/transactions/edittype/PhabricatorPHIDListEditType.php
@@ -42,63 +42,4 @@
     }
   }
 
-  public function getPHUIXControlType() {
-    $datasource = $this->getDatasource();
-
-    if (!$datasource) {
-      return null;
-    }
-
-    return 'tokenizer';
-  }
-
-  public function getPHUIXControlSpecification() {
-    $datasource = $this->getDatasource();
-
-    if (!$datasource) {
-      return null;
-    }
-
-    $template = new AphrontTokenizerTemplateView();
-
-    if ($this->getIsSingleValue()) {
-      $limit = 1;
-    } else {
-      $limit = null;
-    }
-
-    $default = $this->getDefaultValue();
-    if ($default) {
-      $value = $datasource->getWireTokens($default);
-    } else {
-      $value = array();
-    }
-
-    return array(
-      'markup' => $template->render(),
-      'config' => array(
-        'src' => $datasource->getDatasourceURI(),
-        'browseURI' => $datasource->getBrowseURI(),
-        'placeholder' => $datasource->getPlaceholderText(),
-        'limit' => $limit,
-      ),
-      'value' => $value,
-    );
-  }
-
-  public function getCommentActionValueFromDraftValue($value) {
-    $datasource = $this->getDatasource();
-
-    if (!$datasource) {
-      return array();
-    }
-
-    if (!is_array($value)) {
-      return array();
-    }
-
-    return $datasource->getWireTokens($value);
-  }
-
-
 }
diff --git a/src/applications/transactions/edittype/PhabricatorSimpleEditType.php b/src/applications/transactions/edittype/PhabricatorSimpleEditType.php
--- a/src/applications/transactions/edittype/PhabricatorSimpleEditType.php
+++ b/src/applications/transactions/edittype/PhabricatorSimpleEditType.php
@@ -35,22 +35,4 @@
     return $this->valueDescription;
   }
 
-  public function setPHUIXControlType($type) {
-    $this->phuixControlType = $type;
-    return $this;
-  }
-
-  public function getPHUIXControlType() {
-    return $this->phuixControlType;
-  }
-
-  public function setPHUIXControlSpecification(array $spec) {
-    $this->phuixControlSpecification = $spec;
-    return $this;
-  }
-
-  public function getPHUIXControlSpecification() {
-    return $this->phuixControlSpecification;
-  }
-
 }
diff --git a/src/applications/transactions/view/PhabricatorApplicationTransactionCommentView.php b/src/applications/transactions/view/PhabricatorApplicationTransactionCommentView.php
--- a/src/applications/transactions/view/PhabricatorApplicationTransactionCommentView.php
+++ b/src/applications/transactions/view/PhabricatorApplicationTransactionCommentView.php
@@ -20,11 +20,9 @@
   private $headerText;
   private $noPermission;
 
-
-
   private $currentVersion;
   private $versionedDraft;
-  private $editTypes;
+  private $commentActions;
   private $transactionTimeline;
 
   public function setObjectPHID($object_phid) {
@@ -104,13 +102,14 @@
     return $this;
   }
 
-  public function setEditTypes($edit_types) {
-    $this->editTypes = $edit_types;
+  public function setCommentActions(array $comment_actions) {
+    assert_instances_of($comment_actions, 'PhabricatorEditEngineCommentAction');
+    $this->commentActions = $comment_actions;
     return $this;
   }
 
-  public function getEditTypes() {
-    return $this->editTypes;
+  public function getCommentActions() {
+    return $this->commentActions;
   }
 
   public function setNoPermission($no_permission) {
@@ -166,7 +165,7 @@
       $preview = null;
     }
 
-    if (!$this->getEditTypes()) {
+    if (!$this->getCommentActions()) {
       Javelin::initBehavior(
         'phabricator-transaction-comment-form',
         array(
@@ -219,21 +218,47 @@
       ->addHiddenInput('__draft__', $draft_key)
       ->addHiddenInput($version_key, $version_value);
 
-    $edit_types = $this->getEditTypes();
-    if ($edit_types) {
-
+    $comment_actions = $this->getCommentActions();
+    if ($comment_actions) {
       $action_map = array();
       $type_map = array();
-      foreach ($edit_types as $edit_type) {
-        $key = $edit_type->getEditType();
+
+      $comment_actions = mpull($comment_actions, null, 'getKey');
+
+      $draft_actions = array();
+      $draft_keys = array();
+      if ($versioned_draft) {
+        $draft_actions = $versioned_draft->getProperty('actions', array());
+
+        if (!is_array($draft_actions)) {
+          $draft_actions = array();
+        }
+
+        foreach ($draft_actions as $action) {
+          $type = idx($action, 'type');
+          $comment_action = idx($comment_actions, $type);
+          if (!$comment_action) {
+            continue;
+          }
+
+          $value = idx($action, 'value');
+          $comment_action->setValue($value);
+
+          $draft_keys[] = $type;
+        }
+      }
+
+      foreach ($comment_actions as $key => $comment_action) {
+        $key = $comment_action->getKey();
         $action_map[$key] = array(
           'key' => $key,
-          'label' => $edit_type->getLabel(),
-          'type' => $edit_type->getPHUIXControlType(),
-          'spec' => $edit_type->getPHUIXControlSpecification(),
+          'label' => $comment_action->getLabel(),
+          'type' => $comment_action->getPHUIXControlType(),
+          'spec' => $comment_action->getPHUIXControlSpecification(),
+          'initialValue' => $comment_action->getInitialValue(),
         );
 
-        $type_map[$key] = $edit_type;
+        $type_map[$key] = $comment_action;
       }
 
       $options = array();
@@ -270,28 +295,6 @@
             'id' => $place_id,
           )));
 
-      $draft_actions = array();
-      if ($versioned_draft) {
-        $draft_actions = $versioned_draft->getProperty('actions', array());
-        foreach ($draft_actions as $key => $action) {
-          $type = idx($action, 'type');
-          if (!$type) {
-            unset($draft_actions[$key]);
-            continue;
-          }
-
-          $edit_type = idx($type_map, $type);
-          if (!$edit_type) {
-            unset($draft_actions[$key]);
-            continue;
-          }
-
-          $value = idx($action, 'value');
-          $value = $edit_type->getCommentActionValueFromDraftValue($value);
-          $draft_actions[$key]['value'] = $value;
-        }
-      }
-
       Javelin::initBehavior(
         'comment-actions',
         array(
@@ -304,7 +307,7 @@
           'actions' => $action_map,
           'showPreview' => $this->getShowPreview(),
           'actionURI' => $this->getAction(),
-          'drafts' => $draft_actions,
+          'drafts' => $draft_keys,
         ));
     }
 
diff --git a/src/view/control/AphrontTokenizerTemplateView.php b/src/view/control/AphrontTokenizerTemplateView.php
--- a/src/view/control/AphrontTokenizerTemplateView.php
+++ b/src/view/control/AphrontTokenizerTemplateView.php
@@ -6,7 +6,7 @@
   private $name;
   private $id;
   private $browseURI;
-  private $originalValue;
+  private $initialValue;
 
   public function setBrowseURI($browse_uri) {
     $this->browseURI = $browse_uri;
@@ -37,13 +37,13 @@
     return $this->name;
   }
 
-  public function setOriginalValue(array $original_value) {
-    $this->originalValue = $original_value;
+  public function setInitialValue(array $initial_value) {
+    $this->initialValue = $initial_value;
     return $this;
   }
 
-  public function getOriginalValue() {
-    return $this->originalValue;
+  public function getInitialValue() {
+    return $this->initialValue;
   }
 
   public function render() {
@@ -95,15 +95,15 @@
       $classes[] = 'has-browse';
     }
 
-    $original = array();
-    $original_value = $this->getOriginalValue();
-    if ($original_value) {
-      foreach ($this->getOriginalValue() as $value) {
-        $original[] = phutil_tag(
+    $initial = array();
+    $initial_value = $this->getInitialValue();
+    if ($initial_value) {
+      foreach ($this->getInitialValue() as $value) {
+        $initial[] = phutil_tag(
           'input',
           array(
             'type' => 'hidden',
-            'name' => $name.'.original[]',
+            'name' => $name.'.initial[]',
             'value' => $value,
           ));
       }
@@ -118,7 +118,7 @@
       array(
         $container,
         $browse,
-        $original,
+        $initial,
       ));
 
     return $frame;
diff --git a/src/view/form/control/AphrontFormTokenizerControl.php b/src/view/form/control/AphrontFormTokenizerControl.php
--- a/src/view/form/control/AphrontFormTokenizerControl.php
+++ b/src/view/form/control/AphrontFormTokenizerControl.php
@@ -7,7 +7,7 @@
   private $limit;
   private $placeholder;
   private $handles;
-  private $originalValue;
+  private $initialValue;
 
   public function setDatasource(PhabricatorTypeaheadDatasource $datasource) {
     $this->datasource = $datasource;
@@ -33,13 +33,13 @@
     return $this;
   }
 
-  public function setOriginalValue(array $original_value) {
-    $this->originalValue = $original_value;
+  public function setInitialValue(array $initial_value) {
+    $this->initialValue = $initial_value;
     return $this;
   }
 
-  public function getOriginalValue() {
-    return $this->originalValue;
+  public function getInitialValue() {
+    return $this->initialValue;
   }
 
   public function willRender() {
@@ -84,9 +84,9 @@
       ->setID($id)
       ->setValue($tokens);
 
-    $original_value = $this->getOriginalValue();
-    if ($original_value !== null) {
-      $template->setOriginalValue($original_value);
+    $initial_value = $this->getInitialValue();
+    if ($initial_value !== null) {
+      $template->setInitialValue($initial_value);
     }
 
     $username = null;
diff --git a/webroot/rsrc/js/application/transactions/behavior-comment-actions.js b/webroot/rsrc/js/application/transactions/behavior-comment-actions.js
--- a/webroot/rsrc/js/application/transactions/behavior-comment-actions.js
+++ b/webroot/rsrc/js/application/transactions/behavior-comment-actions.js
@@ -80,7 +80,8 @@
     for (var k in rows) {
       data.push({
         type: k,
-        value: rows[k].getValue()
+        value: rows[k].getValue(),
+        initialValue: action_map[k].initialValue || null
       });
     }
 
@@ -104,13 +105,12 @@
     for (var ii = 0; ii < drafts.length; ii++) {
       draft = drafts[ii];
 
-      option = find_option(draft.type);
+      option = find_option(draft);
       if (!option) {
         continue;
       }
 
       control = add_row(option);
-      control.setValue(draft.value);
     }
   }
 
@@ -133,6 +133,7 @@
     input_node.value = serialize_actions();
   });
 
+
   if (config.showPreview) {
     var request = new JX.PhabricatorShapedRequest(
       config.actionURI,
@@ -154,5 +155,4 @@
   }
 
   restore_draft_actions(config.drafts || []);
-
 });