diff --git a/src/applications/releeph/controller/product/ReleephProductActionController.php b/src/applications/releeph/controller/product/ReleephProductActionController.php
index cca9b0bf0a..da82e2d6ad 100644
--- a/src/applications/releeph/controller/product/ReleephProductActionController.php
+++ b/src/applications/releeph/controller/product/ReleephProductActionController.php
@@ -1,93 +1,85 @@
 <?php
 
 final class ReleephProductActionController extends ReleephProductController {
 
-  private $id;
-  private $action;
-
-  public function willProcessRequest(array $data) {
-    $this->id = $data['projectID'];
-    $this->action = $data['action'];
-  }
-
-  public function processRequest() {
-    $request = $this->getRequest();
-    $viewer = $request->getUser();
+  public function handleRequest(AphrontRequest $request) {
+    $viewer = $request->getViewer();
+    $id = $request->getURIData('projectID');
+    $action = $request->getURIData('action');
 
     $product = id(new ReleephProductQuery())
-      ->withIDs(array($this->id))
+      ->withIDs(array($id))
       ->requireCapabilities(
         array(
           PhabricatorPolicyCapability::CAN_VIEW,
           PhabricatorPolicyCapability::CAN_EDIT,
         ))
       ->setViewer($viewer)
       ->executeOne();
     if (!$product) {
       return new Aphront404Response();
     }
 
     $this->setProduct($product);
 
     $product_id = $product->getID();
     $product_uri = $this->getProductViewURI($product);
 
-    $action = $this->action;
     switch ($action) {
       case 'deactivate':
       case 'activate':
         break;
       default:
         throw new Aphront404Response();
     }
 
     if ($request->isFormPost()) {
       $type_active = ReleephProductTransaction::TYPE_ACTIVE;
 
       $xactions = array();
       if ($action == 'activate') {
         $xactions[] = id(new ReleephProductTransaction())
           ->setTransactionType($type_active)
           ->setNewValue(1);
       } else {
         $xactions[] = id(new ReleephProductTransaction())
           ->setTransactionType($type_active)
           ->setNewValue(0);
       }
 
       $editor = id(new ReleephProductEditor())
         ->setActor($viewer)
         ->setContentSourceFromRequest($request)
         ->setContinueOnNoEffect(true)
         ->setContinueOnMissingFields(true);
 
       $editor->applyTransactions($product, $xactions);
 
       return id(new AphrontRedirectResponse())->setURI($product_uri);
     }
 
     if ($action == 'activate') {
       $title = pht('Activate Product?');
       $body = pht(
         'Reactivate the product %s?',
         phutil_tag('strong', array(), $product->getName()));
       $submit = pht('Reactivate Product');
       $short = pht('Deactivate');
     } else {
       $title = pht('Really Deactivate Product?');
       $body = pht(
         'Really deactivate the product %s?',
         phutil_tag('strong', array(), $product->getName()));
       $submit = pht('Deactivate Product');
       $short = pht('Activate');
     }
 
     return $this->newDialog()
       ->setTitle($title)
       ->setShortTitle($short)
       ->appendParagraph($body)
       ->addSubmitButton($submit)
       ->addCancelButton($product_uri);
   }
 
 }
diff --git a/src/applications/releeph/controller/product/ReleephProductCreateController.php b/src/applications/releeph/controller/product/ReleephProductCreateController.php
index 6d5f29ff37..9772b293fe 100644
--- a/src/applications/releeph/controller/product/ReleephProductCreateController.php
+++ b/src/applications/releeph/controller/product/ReleephProductCreateController.php
@@ -1,133 +1,132 @@
 <?php
 
 final class ReleephProductCreateController extends ReleephProductController {
 
-  public function processRequest() {
-    $request = $this->getRequest();
+  public function handleRequest(AphrontRequest $request) {
     $name = trim($request->getStr('name'));
     $trunk_branch = trim($request->getStr('trunkBranch'));
     $repository_phid = $request->getStr('repositoryPHID');
 
     $e_name = true;
     $e_trunk_branch = true;
     $errors = array();
 
     if ($request->isFormPost()) {
       if (!$name) {
         $e_name = pht('Required');
         $errors[] = pht(
           'Your product should have a simple, descriptive name.');
       }
 
       if (!$trunk_branch) {
         $e_trunk_branch = pht('Required');
         $errors[] = pht(
           'You must specify which branch you will be picking from.');
       }
 
       $pr_repository = id(new PhabricatorRepositoryQuery())
         ->setViewer($request->getUser())
         ->withPHIDs(array($repository_phid))
         ->executeOne();
 
 
       if (!$errors) {
         $releeph_product = id(new ReleephProject())
           ->setName($name)
           ->setTrunkBranch($trunk_branch)
           ->setRepositoryPHID($pr_repository->getPHID())
           ->setCreatedByUserPHID($request->getUser()->getPHID())
           ->setIsActive(1);
 
         try {
           $releeph_product->save();
 
           return id(new AphrontRedirectResponse())
             ->setURI($releeph_product->getURI());
         } catch (AphrontDuplicateKeyQueryException $ex) {
           $e_name = pht('Not Unique');
           $errors[] = pht('Another product already uses this name.');
         }
       }
     }
 
     $repo_options = $this->getRepositorySelectOptions();
 
     $product_name_input = id(new AphrontFormTextControl())
       ->setLabel(pht('Name'))
       ->setDisableAutocomplete(true)
       ->setName('name')
       ->setValue($name)
       ->setError($e_name)
       ->setCaption(pht('A name like "Thrift" but not "Thrift releases".'));
 
     $repository_input = id(new AphrontFormSelectControl())
       ->setLabel(pht('Repository'))
       ->setName('repositoryPHID')
       ->setValue($repository_phid)
       ->setOptions($repo_options);
 
     $branch_name_preview = id(new ReleephBranchPreviewView())
       ->setLabel(pht('Example Branch'))
       ->addControl('projectName', $product_name_input)
       ->addControl('repositoryPHID', $repository_input)
       ->addStatic('template', '')
       ->addStatic('isSymbolic', false);
 
     $form = id(new AphrontFormView())
       ->setUser($request->getUser())
       ->appendChild($product_name_input)
       ->appendChild($repository_input)
       ->appendChild(
         id(new AphrontFormTextControl())
           ->setLabel(pht('Trunk'))
           ->setName('trunkBranch')
           ->setValue($trunk_branch)
           ->setError($e_trunk_branch)
           ->setCaption(pht(
             'The development branch, from which requests will be picked.')))
       ->appendChild($branch_name_preview)
       ->appendChild(
         id(new AphrontFormSubmitControl())
           ->addCancelButton('/releeph/project/')
           ->setValue(pht('Create Release Product')));
 
     $form_box = id(new PHUIObjectBoxView())
       ->setHeaderText(pht('Create New Product'))
       ->setFormErrors($errors)
       ->setForm($form);
 
     $crumbs = $this->buildApplicationCrumbs();
     $crumbs->addTextCrumb(pht('New Product'));
 
     return $this->buildApplicationPage(
       array(
         $crumbs,
         $form_box,
       ),
       array(
         'title' => pht('Create New Product'),
       ));
   }
 
   private function getRepositorySelectOptions() {
     $repos = id(new PhabricatorRepositoryQuery())
       ->setViewer($this->getRequest()->getUser())
       ->execute();
 
     $repos = msort($repos, 'getName');
     $repos = mpull($repos, null, 'getID');
 
     $choices = array();
 
     foreach ($repos as $repo_id => $repo) {
       $repo_name = $repo->getName();
       $callsign = $repo->getCallsign();
       $choices[$repo->getPHID()] = "r{$callsign} ({$repo_name})";
     }
 
     ksort($choices);
     return $choices;
   }
 
 }
diff --git a/src/applications/releeph/controller/product/ReleephProductEditController.php b/src/applications/releeph/controller/product/ReleephProductEditController.php
index d0769c7b28..7fd8e81563 100644
--- a/src/applications/releeph/controller/product/ReleephProductEditController.php
+++ b/src/applications/releeph/controller/product/ReleephProductEditController.php
@@ -1,273 +1,267 @@
 <?php
 
 final class ReleephProductEditController extends ReleephProductController {
 
-  private $productID;
-
-  public function willProcessRequest(array $data) {
-    $this->productID = $data['projectID'];
-  }
-
-  public function processRequest() {
-    $request = $this->getRequest();
-    $viewer = $request->getUser();
+  public function handleRequest(AphrontRequest $request) {
+    $viewer = $request->getViewer();
+    $id = $request->getURIData('projectID');
 
     $product = id(new ReleephProductQuery())
       ->setViewer($viewer)
-      ->withIDs(array($this->productID))
+      ->withIDs(array($id))
       ->requireCapabilities(
         array(
           PhabricatorPolicyCapability::CAN_VIEW,
           PhabricatorPolicyCapability::CAN_EDIT,
         ))
       ->executeOne();
     if (!$product) {
       return new Aphront404Response();
     }
     $this->setProduct($product);
 
     $e_name = true;
     $e_trunk_branch = true;
     $e_branch_template = false;
     $errors = array();
 
     $product_name = $request->getStr('name', $product->getName());
 
     $trunk_branch = $request->getStr('trunkBranch', $product->getTrunkBranch());
     $branch_template = $request->getStr('branchTemplate');
     if ($branch_template === null) {
       $branch_template = $product->getDetail('branchTemplate');
     }
     $pick_failure_instructions = $request->getStr('pickFailureInstructions',
       $product->getDetail('pick_failure_instructions'));
     $test_paths = $request->getStr('testPaths');
     if ($test_paths !== null) {
       $test_paths = array_filter(explode("\n", $test_paths));
     } else {
       $test_paths = $product->getDetail('testPaths', array());
     }
 
     $repository_phid = $product->getRepositoryPHID();
 
     if ($request->isFormPost()) {
       $pusher_phids = $request->getArr('pushers');
 
       if (!$product_name) {
         $e_name = pht('Required');
         $errors[] =
           pht('Your releeph product should have a simple descriptive name.');
       }
 
       if (!$trunk_branch) {
         $e_trunk_branch = pht('Required');
         $errors[] =
           pht('You must specify which branch you will be picking from.');
       }
 
       $other_releeph_products = id(new ReleephProject())
         ->loadAllWhere('id != %d', $product->getID());
       $other_releeph_product_names = mpull($other_releeph_products,
         'getName', 'getID');
 
       if (in_array($product_name, $other_releeph_product_names)) {
         $errors[] = pht('Releeph product name %s is already taken',
           $product_name);
       }
 
       foreach ($test_paths as $test_path) {
         $result = @preg_match($test_path, '');
         $is_a_valid_regexp = $result !== false;
         if (!$is_a_valid_regexp) {
           $errors[] = pht('Please provide a valid regular expression: '.
             '%s is not valid', $test_path);
         }
       }
 
       $product
         ->setName($product_name)
         ->setTrunkBranch($trunk_branch)
         ->setDetail('pushers', $pusher_phids)
         ->setDetail('pick_failure_instructions', $pick_failure_instructions)
         ->setDetail('branchTemplate', $branch_template)
         ->setDetail('testPaths', $test_paths);
 
       $fake_commit_handle = ReleephBranchTemplate::getFakeCommitHandleFor(
         $repository_phid,
         $viewer);
 
       if ($branch_template) {
         list($branch_name, $template_errors) = id(new ReleephBranchTemplate())
           ->setCommitHandle($fake_commit_handle)
           ->setReleephProjectName($product_name)
           ->interpolate($branch_template);
 
         if ($template_errors) {
           $e_branch_template = pht('Whoopsies!');
           foreach ($template_errors as $template_error) {
             $errors[] = pht('Template error: %s', $template_error);
           }
         }
       }
 
       if (!$errors) {
         $product->save();
 
         return id(new AphrontRedirectResponse())->setURI($product->getURI());
       }
     }
 
     $pusher_phids = $request->getArr(
       'pushers',
       $product->getDetail('pushers', array()));
 
     $form = id(new AphrontFormView())
       ->setUser($request->getUser())
       ->appendChild(
         id(new AphrontFormTextControl())
           ->setLabel(pht('Name'))
           ->setName('name')
           ->setValue($product_name)
           ->setError($e_name)
           ->setCaption(pht('A name like "Thrift" but not "Thrift releases".')))
       ->appendChild(
         id(new AphrontFormStaticControl())
           ->setLabel(pht('Repository'))
           ->setValue(
             $product->getRepository()->getName()))
       ->appendChild(
         id(new AphrontFormStaticControl())
           ->setLabel(pht('Repository'))
           ->setValue(
             $product->getRepository()->getName()))
       ->appendChild(
         id(new AphrontFormStaticControl())
           ->setLabel(pht('Releeph Project PHID'))
           ->setValue(
             $product->getPHID()))
       ->appendChild(
         id(new AphrontFormTextControl())
           ->setLabel(pht('Trunk'))
           ->setValue($trunk_branch)
           ->setName('trunkBranch')
           ->setError($e_trunk_branch))
       ->appendChild(
         id(new AphrontFormTextAreaControl())
           ->setLabel(pht('Pick Instructions'))
           ->setValue($pick_failure_instructions)
           ->setName('pickFailureInstructions')
           ->setCaption(
             pht('Instructions for pick failures, which will be used '.
             'in emails generated by failed picks')))
       ->appendChild(
         id(new AphrontFormTextAreaControl())
           ->setLabel(pht('Tests paths'))
           ->setValue(implode("\n", $test_paths))
           ->setName('testPaths')
           ->setCaption(
             pht('List of strings that all test files contain in their path '.
             'in this project. One string per line. '.
             'Examples: \'__tests__\', \'/javatests/\'...')));
 
     $branch_template_input = id(new AphrontFormTextControl())
       ->setName('branchTemplate')
       ->setValue($branch_template)
       ->setLabel(pht('Branch Template'))
       ->setError($e_branch_template)
       ->setCaption(
         pht("Leave this blank to use your installation's default."));
 
     $branch_template_preview = id(new ReleephBranchPreviewView())
       ->setLabel(pht('Preview'))
       ->addControl('template', $branch_template_input)
       ->addStatic('repositoryPHID', $repository_phid)
       ->addStatic('isSymbolic', false)
       ->addStatic('projectName', $product->getName());
 
     $form
       ->appendControl(
         id(new AphrontFormTokenizerControl())
           ->setLabel(pht('Pushers'))
           ->setName('pushers')
           ->setDatasource(new PhabricatorPeopleDatasource())
           ->setValue($pusher_phids))
       ->appendChild($branch_template_input)
       ->appendChild($branch_template_preview)
       ->appendRemarkupInstructions($this->getBranchHelpText());
 
     $form
       ->appendChild(
         id(new AphrontFormSubmitControl())
           ->addCancelButton('/releeph/product/')
           ->setValue(pht('Save')));
 
     $box = id(new PHUIObjectBoxView())
       ->setHeaderText(pht('Edit Releeph Product'))
       ->setFormErrors($errors)
       ->appendChild($form);
 
     $crumbs = $this->buildApplicationCrumbs();
     $crumbs->addTextCrumb(pht('Edit Product'));
 
     return $this->buildStandardPageResponse(
       array(
         $crumbs,
         $box,
       ),
       array(
         'title' => pht('Edit Releeph Product'),
         'device' => true,
       ));
   }
 
   private function getBranchHelpText() {
     return <<<EOTEXT
 
 ==== Interpolations ====
 
 | Code  | Meaning
 | ----- | -------
 | `%P`  | The name of your product, with spaces changed to "-".
 | `%p`  | Like %P, but all lowercase.
 | `%Y`  | The four digit year associated with the branch date.
 | `%m`  | The two digit month.
 | `%d`  | The two digit day.
 | `%v`  | The handle of the commit where the branch was cut ("rXYZa4b3c2d1").
 | `%V`  | The abbreviated commit id where the branch was cut ("a4b3c2d1").
 | `%..` | Any other sequence interpreted by `strftime()`.
 | `%%`  | A literal percent sign.
 
 
 ==== Tips for Branch Templates ====
 
 Use a directory to separate your release branches from other branches:
 
   lang=none
   releases/%Y-%M-%d-%v
   => releases/2012-30-16-rHERGE32cd512a52b7
 
 Include a second hierarchy if you share your repository with other products:
 
   lang=none
   releases/%P/%p-release-%Y%m%d-%V
   => releases/Tintin/tintin-release-20121116-32cd512a52b7
 
 Keep your branch names simple, avoiding strange punctuation, most of which is
 forbidden or escaped anyway:
 
   lang=none, counterexample
   releases//..clown-releases..//`date --iso=seconds`-$(sudo halt)
 
 Include the date early in your template, in an order which sorts properly:
 
   lang=none
   releases/%Y%m%d-%v
   => releases/20121116-rHERGE32cd512a52b7 (good!)
 
   releases/%V-%m.%d.%Y
   => releases/32cd512a52b7-11.16.2012 (awful!)
 
 
 EOTEXT;
   }
 
 }
diff --git a/src/applications/releeph/controller/product/ReleephProductHistoryController.php b/src/applications/releeph/controller/product/ReleephProductHistoryController.php
index 15d7139854..ebe9f15725 100644
--- a/src/applications/releeph/controller/product/ReleephProductHistoryController.php
+++ b/src/applications/releeph/controller/product/ReleephProductHistoryController.php
@@ -1,46 +1,41 @@
 <?php
 
 final class ReleephProductHistoryController extends ReleephProductController {
 
-  private $id;
-
   public function shouldAllowPublic() {
     return true;
   }
 
-  public function willProcessRequest(array $data) {
-    $this->id = $data['projectID'];
-  }
-
-  public function processRequest() {
-    $request = $this->getRequest();
-    $viewer = $request->getUser();
+  public function handleRequest(AphrontRequest $request) {
+    $viewer = $request->getViewer();
+    $id = $request->getURIData('projectID');
 
     $product = id(new ReleephProductQuery())
       ->setViewer($viewer)
-      ->withIDs(array($this->id))
+      ->withIDs(array($id))
       ->executeOne();
     if (!$product) {
       return new Aphront404Response();
     }
     $this->setProduct($product);
 
     $timeline = $this->buildTransactionTimeline(
       $product,
       new ReleephProductTransactionQuery());
     $timeline->setShouldTerminate(true);
 
     $crumbs = $this->buildApplicationCrumbs();
     $crumbs->addTextCrumb(pht('History'));
+    $crumbs->setBorder(true);
 
     return $this->buildApplicationPage(
       array(
         $crumbs,
         $timeline,
       ),
       array(
         'title' => pht('Product History'),
       ));
   }
 
 }
diff --git a/src/applications/releeph/controller/product/ReleephProductListController.php b/src/applications/releeph/controller/product/ReleephProductListController.php
index 73d2b27416..14cc964e03 100644
--- a/src/applications/releeph/controller/product/ReleephProductListController.php
+++ b/src/applications/releeph/controller/product/ReleephProductListController.php
@@ -1,36 +1,31 @@
 <?php
 
 final class ReleephProductListController extends ReleephController {
 
-  private $queryKey;
-
   public function shouldAllowPublic() {
     return true;
   }
 
-  public function willProcessRequest(array $data) {
-    $this->queryKey = idx($data, 'queryKey');
-  }
-
-  public function processRequest() {
+  public function handleRequest(AphrontRequest $request) {
+    $query_key = $request->getURIData('queryKey');
     $controller = id(new PhabricatorApplicationSearchController())
-      ->setQueryKey($this->queryKey)
+      ->setQueryKey($query_key)
       ->setSearchEngine(new ReleephProductSearchEngine())
       ->setNavigation($this->buildSideNavView());
 
     return $this->delegateToController($controller);
   }
 
   protected function buildApplicationCrumbs() {
     $crumbs = parent::buildApplicationCrumbs();
 
     $crumbs->addAction(
       id(new PHUIListItemView())
         ->setName(pht('Create Product'))
         ->setHref($this->getApplicationURI('product/create/'))
         ->setIcon('fa-plus-square'));
 
     return $crumbs;
   }
 
 }
diff --git a/src/applications/releeph/controller/product/ReleephProductViewController.php b/src/applications/releeph/controller/product/ReleephProductViewController.php
index 3bccae0ea0..e35497eec7 100644
--- a/src/applications/releeph/controller/product/ReleephProductViewController.php
+++ b/src/applications/releeph/controller/product/ReleephProductViewController.php
@@ -1,161 +1,154 @@
 <?php
 
 final class ReleephProductViewController extends ReleephProductController {
 
-  private $productID;
-  private $queryKey;
-
   public function shouldAllowPublic() {
     return true;
   }
 
-  public function willProcessRequest(array $data) {
-    $this->productID = idx($data, 'projectID');
-    $this->queryKey = idx($data, 'queryKey');
-  }
-
-  public function processRequest() {
-    $request = $this->getRequest();
-    $viewer = $request->getUser();
+  public function handleRequest(AphrontRequest $request) {
+    $id = $request->getURIData('projectID');
+    $query_key = $request->getURIData('queryKey');
+    $viewer = $request->getViewer();
 
     $product = id(new ReleephProductQuery())
       ->setViewer($viewer)
-      ->withIDs(array($this->productID))
+      ->withIDs(array($id))
       ->executeOne();
     if (!$product) {
       return new Aphront404Response();
     }
     $this->setProduct($product);
 
     $controller = id(new PhabricatorApplicationSearchController())
-      ->setQueryKey($this->queryKey)
+      ->setQueryKey($query_key)
       ->setPreface($this->renderPreface())
       ->setSearchEngine(
         id(new ReleephBranchSearchEngine())
           ->setProduct($product))
       ->setNavigation($this->buildSideNavView());
 
     return $this->delegateToController($controller);
   }
 
   public function buildSideNavView($for_app = false) {
     $viewer = $this->getRequest()->getUser();
     $product = $this->getProduct();
 
     $nav = new AphrontSideNavFilterView();
     $nav->setBaseURI(new PhutilURI($this->getApplicationURI()));
 
     if ($for_app) {
       $nav->addFilter('product/create/', pht('Create Product'));
     }
 
     id(new ReleephBranchSearchEngine())
       ->setProduct($product)
       ->setViewer($viewer)
       ->addNavigationItems($nav->getMenu());
 
     $nav->selectFilter(null);
 
     return $nav;
   }
 
   protected function buildApplicationCrumbs() {
     $crumbs = parent::buildApplicationCrumbs();
 
     $product = $this->getProduct();
     if ($product) {
       $crumbs->addAction(
         id(new PHUIListItemView())
           ->setHref($product->getURI('cutbranch/'))
           ->setName(pht('Cut New Branch'))
           ->setIcon('fa-plus'));
     }
 
     return $crumbs;
   }
 
   private function renderPreface() {
     $viewer = $this->getRequest()->getUser();
     $product = $this->getProduct();
 
     $id = $product->getID();
 
     $header = id(new PHUIHeaderView())
       ->setHeader($product->getName())
       ->setUser($viewer)
       ->setPolicyObject($product);
 
     if ($product->getIsActive()) {
       $header->setStatus('fa-check', 'bluegrey', pht('Active'));
     } else {
       $header->setStatus('fa-ban', 'dark', pht('Inactive'));
     }
 
     $actions = id(new PhabricatorActionListView())
       ->setUser($viewer)
       ->setObject($product)
       ->setObjectURI($this->getRequest()->getRequestURI());
 
     $can_edit = PhabricatorPolicyFilter::hasCapability(
       $viewer,
       $product,
       PhabricatorPolicyCapability::CAN_EDIT);
 
     $edit_uri = $this->getApplicationURI("product/{$id}/edit/");
     $history_uri = $this->getApplicationURI("product/{$id}/history/");
 
     $actions->addAction(
       id(new PhabricatorActionView())
         ->setName(pht('Edit Product'))
         ->setHref($edit_uri)
         ->setIcon('fa-pencil')
         ->setDisabled(!$can_edit)
         ->setWorkflow(!$can_edit));
 
     if ($product->getIsActive()) {
       $status_name = pht('Deactivate Product');
       $status_href = "product/{$id}/action/deactivate/";
       $status_icon = 'fa-times';
     } else {
       $status_name = pht('Reactivate Product');
       $status_href = "product/{$id}/action/activate/";
       $status_icon = 'fa-plus-circle-o';
     }
 
     $actions->addAction(
       id(new PhabricatorActionView())
         ->setName($status_name)
         ->setHref($this->getApplicationURI($status_href))
         ->setIcon($status_icon)
         ->setDisabled(!$can_edit)
         ->setWorkflow(true));
 
     $actions->addAction(
       id(new PhabricatorActionView())
         ->setName(pht('View History'))
         ->setHref($history_uri)
         ->setIcon('fa-list'));
 
     $properties = id(new PHUIPropertyListView())
       ->setUser($viewer)
       ->setObject($product);
 
     $properties->addProperty(
       pht('Repository'),
       $product->getRepository()->getName());
 
     $properties->setActionList($actions);
 
     $pushers = $product->getPushers();
     if ($pushers) {
       $properties->addProperty(
         pht('Pushers'),
         $viewer->renderHandleList($pushers));
     }
 
     return id(new PHUIObjectBoxView())
       ->setHeader($header)
       ->addPropertyList($properties);
   }
 
 }
diff --git a/src/applications/releeph/controller/request/ReleephRequestActionController.php b/src/applications/releeph/controller/request/ReleephRequestActionController.php
index bbcc1df2bb..1a53b08b8c 100644
--- a/src/applications/releeph/controller/request/ReleephRequestActionController.php
+++ b/src/applications/releeph/controller/request/ReleephRequestActionController.php
@@ -1,132 +1,122 @@
 <?php
 
 final class ReleephRequestActionController
   extends ReleephRequestController {
 
-  private $action;
-  private $requestID;
-
-  public function willProcessRequest(array $data) {
-    $this->action = $data['action'];
-    $this->requestID = $data['requestID'];
-  }
-
-  public function processRequest() {
-    $request = $this->getRequest();
-    $viewer = $request->getUser();
+  public function handleRequest(AphrontRequest $request) {
+    $action = $request->getURIData('action');
+    $id = $request->getURIData('requestID');
+    $viewer = $request->getViewer();
 
     $request->validateCSRF();
 
     $pull = id(new ReleephRequestQuery())
       ->setViewer($viewer)
-      ->withIDs(array($this->requestID))
+      ->withIDs(array($id))
       ->executeOne();
     if (!$pull) {
       return new Aphront404Response();
     }
 
     $branch = $pull->getBranch();
     $product = $branch->getProduct();
-
-    $action = $this->action;
-
     $origin_uri = '/'.$pull->getMonogram();
 
     $editor = id(new ReleephRequestTransactionalEditor())
       ->setActor($viewer)
       ->setContinueOnNoEffect(true)
       ->setContentSourceFromRequest($request);
 
     $xactions = array();
 
     switch ($action) {
       case 'want':
       case 'pass':
         static $action_map = array(
           'want' => ReleephRequest::INTENT_WANT,
           'pass' => ReleephRequest::INTENT_PASS,
         );
         $intent = $action_map[$action];
         $xactions[] = id(new ReleephRequestTransaction())
           ->setTransactionType(ReleephRequestTransaction::TYPE_USER_INTENT)
           ->setMetadataValue(
             'isAuthoritative',
             $product->isAuthoritative($viewer))
           ->setNewValue($intent);
         break;
 
       case 'mark-manually-picked':
       case 'mark-manually-reverted':
         if (
           $pull->getRequestUserPHID() === $viewer->getPHID() ||
           $product->isAuthoritative($viewer)) {
 
           // We're all good!
         } else {
           throw new Exception(
             pht(
               "Bug! Only pushers or the requestor can manually change a ".
               "request's in-branch status!"));
         }
 
         if ($action === 'mark-manually-picked') {
           $in_branch = 1;
           $intent = ReleephRequest::INTENT_WANT;
         } else {
           $in_branch = 0;
           $intent = ReleephRequest::INTENT_PASS;
         }
 
         $xactions[] = id(new ReleephRequestTransaction())
           ->setTransactionType(ReleephRequestTransaction::TYPE_USER_INTENT)
           ->setMetadataValue('isManual', true)
           ->setMetadataValue('isAuthoritative', true)
           ->setNewValue($intent);
 
         $xactions[] = id(new ReleephRequestTransaction())
           ->setTransactionType(ReleephRequestTransaction::TYPE_MANUAL_IN_BRANCH)
           ->setNewValue($in_branch);
 
         break;
 
       default:
         throw new Exception(
           pht('Unknown or unimplemented action %s.', $action));
     }
 
     $editor->applyTransactions($pull, $xactions);
 
     if ($request->getBool('render')) {
       $field_list = PhabricatorCustomField::getObjectFields(
         $pull,
         PhabricatorCustomField::ROLE_VIEW);
 
       $field_list
         ->setViewer($viewer)
         ->readFieldsFromStorage($pull);
 
       // TODO: This should be more modern and general.
       $engine = id(new PhabricatorMarkupEngine())
         ->setViewer($viewer);
       foreach ($field_list->getFields() as $field) {
         if ($field->shouldMarkup()) {
           $field->setMarkupEngine($engine);
         }
       }
       $engine->process();
 
       $pull_box = id(new ReleephRequestView())
         ->setUser($viewer)
         ->setCustomFields($field_list)
         ->setPullRequest($pull)
         ->setIsListView(true);
 
       return id(new AphrontAjaxResponse())->setContent(
         array(
           'markup' => hsprintf('%s', $pull_box),
         ));
     }
 
     return id(new AphrontRedirectResponse())->setURI($origin_uri);
   }
 }
diff --git a/src/applications/releeph/controller/request/ReleephRequestCommentController.php b/src/applications/releeph/controller/request/ReleephRequestCommentController.php
index 4c728341af..0a31261a13 100644
--- a/src/applications/releeph/controller/request/ReleephRequestCommentController.php
+++ b/src/applications/releeph/controller/request/ReleephRequestCommentController.php
@@ -1,69 +1,63 @@
 <?php
 
 final class ReleephRequestCommentController
   extends ReleephRequestController {
 
-  private $requestID;
-
-  public function willProcessRequest(array $data) {
-    $this->requestID = $data['requestID'];
-  }
-
-  public function processRequest() {
-    $request = $this->getRequest();
-    $viewer = $request->getUser();
+  public function handleRequest(AphrontRequest $request) {
+    $id = $request->getURIData('requestID');
+    $viewer = $request->getViewer();
 
     if (!$request->isFormPost()) {
       return new Aphront400Response();
     }
 
     $pull = id(new ReleephRequestQuery())
       ->setViewer($viewer)
-      ->withIDs(array($this->requestID))
+      ->withIDs(array($id))
       ->executeOne();
     if (!$pull) {
       return new Aphront404Response();
     }
 
     $is_preview = $request->isPreviewRequest();
     $draft = PhabricatorDraft::buildFromRequest($request);
 
     $view_uri = $this->getApplicationURI('/'.$pull->getMonogram());
 
     $xactions = array();
     $xactions[] = id(new ReleephRequestTransaction())
       ->setTransactionType(PhabricatorTransactions::TYPE_COMMENT)
       ->attachComment(
         id(new ReleephRequestTransactionComment())
           ->setContent($request->getStr('comment')));
 
     $editor = id(new ReleephRequestTransactionalEditor())
       ->setActor($viewer)
       ->setContinueOnNoEffect($request->isContinueRequest())
       ->setContentSourceFromRequest($request)
       ->setIsPreview($is_preview);
 
     try {
       $xactions = $editor->applyTransactions($pull, $xactions);
     } catch (PhabricatorApplicationTransactionNoEffectException $ex) {
       return id(new PhabricatorApplicationTransactionNoEffectResponse())
         ->setCancelURI($view_uri)
         ->setException($ex);
     }
 
     if ($draft) {
       $draft->replaceOrDelete();
     }
 
     if ($request->isAjax() && $is_preview) {
       return id(new PhabricatorApplicationTransactionResponse())
         ->setViewer($viewer)
         ->setTransactions($xactions)
         ->setIsPreview($is_preview);
     } else {
       return id(new AphrontRedirectResponse())
         ->setURI($view_uri);
     }
   }
 
 }
diff --git a/src/applications/releeph/controller/request/ReleephRequestDifferentialCreateController.php b/src/applications/releeph/controller/request/ReleephRequestDifferentialCreateController.php
index 3c2dc3d735..ffd6388284 100644
--- a/src/applications/releeph/controller/request/ReleephRequestDifferentialCreateController.php
+++ b/src/applications/releeph/controller/request/ReleephRequestDifferentialCreateController.php
@@ -1,107 +1,102 @@
 <?php
 
 // TODO: After T2222, this is likely unreachable?
 
 final class ReleephRequestDifferentialCreateController
   extends ReleephController {
 
-  private $revisionID;
   private $revision;
 
-  public function willProcessRequest(array $data) {
-    $this->revisionID = $data['diffRevID'];
-  }
-
-  public function processRequest() {
-    $request = $this->getRequest();
-    $user = $request->getUser();
+  public function handleRequest(AphrontRequest $request) {
+    $revision_id = $request->getURIData('diffRevID');
+    $viewer = $request->getViewer();
 
     $diff_rev = id(new DifferentialRevisionQuery())
-      ->setViewer($user)
-      ->withIDs(array($this->revisionID))
+      ->setViewer($viewer)
+      ->withIDs(array($revision_id))
       ->executeOne();
     if (!$diff_rev) {
       return new Aphront404Response();
     }
     $this->revision = $diff_rev;
 
     $repository = $this->revision->getRepository();
 
     $projects = id(new ReleephProject())->loadAllWhere(
       'repositoryPHID = %s AND isActive = 1',
       $repository->getPHID());
     if (!$projects) {
       throw new Exception(
         pht(
           "%s belongs to the '%s' repository, ".
           "which is not part of any Releeph project!",
           'D'.$this->revision->getID(),
           $repository->getMonogram()));
     }
 
     $branches = id(new ReleephBranch())->loadAllWhere(
       'releephProjectID IN (%Ld) AND isActive = 1',
       mpull($projects, 'getID'));
     if (!$branches) {
       throw new Exception(pht(
         '%s could be in the Releeph project(s) %s, '.
         'but this project / none of these projects have open branches.',
         'D'.$this->revision->getID(),
         implode(', ', mpull($projects, 'getName'))));
     }
 
     if (count($branches) === 1) {
       return id(new AphrontRedirectResponse())
         ->setURI($this->buildReleephRequestURI(head($branches)));
     }
 
     $projects = msort(
       mpull($projects, null, 'getID'),
       'getName');
 
     $branch_groups = mgroup($branches, 'getReleephProjectID');
 
     require_celerity_resource('releeph-request-differential-create-dialog');
     $dialog = id(new AphrontDialogView())
-      ->setUser($user)
+      ->setUser($viewer)
       ->setTitle(pht('Choose Releeph Branch'))
       ->setClass('releeph-request-differential-create-dialog')
       ->addCancelButton('/D'.$request->getStr('D'));
 
     $dialog->appendChild(
       pht(
         'This differential revision changes code that is associated '.
         'with multiple Releeph branches. Please select the branch '.
         'where you would like this code to be picked.'));
 
     foreach ($branch_groups as $project_id => $branches) {
       $project = idx($projects, $project_id);
       $dialog->appendChild(
         phutil_tag(
           'h1',
           array(),
           $project->getName()));
       $branches = msort($branches, 'getBasename');
       foreach ($branches as $branch) {
         $uri = $this->buildReleephRequestURI($branch);
         $dialog->appendChild(
           phutil_tag(
             'a',
             array(
               'href' => $uri,
             ),
             $branch->getDisplayNameWithDetail()));
       }
     }
 
     return id(new AphrontDialogResponse())
       ->setDialog($dialog);
   }
 
   private function buildReleephRequestURI(ReleephBranch $branch) {
     $uri = $branch->getURI('request/');
     return id(new PhutilURI($uri))
       ->setQueryParam('D', $this->revision->getID());
   }
 
 }
diff --git a/src/applications/releeph/controller/request/ReleephRequestEditController.php b/src/applications/releeph/controller/request/ReleephRequestEditController.php
index 279fee4bd6..d5f5187349 100644
--- a/src/applications/releeph/controller/request/ReleephRequestEditController.php
+++ b/src/applications/releeph/controller/request/ReleephRequestEditController.php
@@ -1,314 +1,308 @@
 <?php
 
 final class ReleephRequestEditController extends ReleephBranchController {
 
-  private $requestID;
-  private $branchID;
+  public function handleRequest(AphrontRequest $request) {
+    $action = $request->getURIData('action');
+    $request_id = $request->getURIData('requestID');
+    $branch_id = $request->getURIData('branchID');
+    $viewer = $request->getViewer();
 
-  public function willProcessRequest(array $data) {
-    $this->requestID = idx($data, 'requestID');
-    $this->branchID = idx($data, 'branchID');
-  }
-
-  public function processRequest() {
-    $request = $this->getRequest();
-    $viewer = $request->getUser();
-
-    if ($this->requestID) {
+    if ($request_id) {
       $pull = id(new ReleephRequestQuery())
         ->setViewer($viewer)
-        ->withIDs(array($this->requestID))
+        ->withIDs(array($request_id))
         ->requireCapabilities(
           array(
             PhabricatorPolicyCapability::CAN_VIEW,
             PhabricatorPolicyCapability::CAN_EDIT,
           ))
         ->executeOne();
       if (!$pull) {
         return new Aphront404Response();
       }
 
       $branch = $pull->getBranch();
 
       $is_edit = true;
     } else {
       $branch = id(new ReleephBranchQuery())
         ->setViewer($viewer)
-        ->withIDs(array($this->branchID))
+        ->withIDs(array($branch_id))
         ->executeOne();
       if (!$branch) {
         return new Aphront404Response();
       }
 
       $pull = id(new ReleephRequest())
         ->setRequestUserPHID($viewer->getPHID())
         ->setBranchID($branch->getID())
         ->setInBranch(0)
         ->attachBranch($branch);
 
       $is_edit = false;
     }
     $this->setBranch($branch);
 
     $product = $branch->getProduct();
 
     $request_identifier = $request->getStr('requestIdentifierRaw');
     $e_request_identifier = true;
 
     // Load all the ReleephFieldSpecifications
     $selector = $branch->getProduct()->getReleephFieldSelector();
     $fields = $selector->getFieldSpecifications();
     foreach ($fields as $field) {
       $field
         ->setReleephProject($product)
         ->setReleephBranch($branch)
         ->setReleephRequest($pull);
     }
 
     $field_list = PhabricatorCustomField::getObjectFields(
       $pull,
       PhabricatorCustomField::ROLE_EDIT);
     foreach ($field_list->getFields() as $field) {
       $field
         ->setReleephProject($product)
         ->setReleephBranch($branch)
         ->setReleephRequest($pull);
     }
     $field_list->readFieldsFromStorage($pull);
 
 
-    if ($this->branchID) {
-      $cancel_uri = $this->getApplicationURI('branch/'.$this->branchID.'/');
+    if ($branch_id) {
+      $cancel_uri = $this->getApplicationURI('branch/'.$branch_id.'/');
     } else {
       $cancel_uri = '/'.$pull->getMonogram();
     }
 
     // Make edits
     $errors = array();
     if ($request->isFormPost()) {
       $xactions = array();
 
       // The commit-identifier being requested...
       if (!$is_edit) {
         if ($request_identifier ===
           ReleephRequestTypeaheadControl::PLACEHOLDER) {
 
           $errors[] = pht('No commit ID was provided.');
           $e_request_identifier = pht('Required');
         } else {
           $pr_commit = null;
           $finder = id(new ReleephCommitFinder())
             ->setUser($viewer)
             ->setReleephProject($product);
           try {
             $pr_commit = $finder->fromPartial($request_identifier);
           } catch (Exception $e) {
             $e_request_identifier = pht('Invalid');
             $errors[] = pht(
               'Request %s is probably not a valid commit.',
               $request_identifier);
             $errors[] = $e->getMessage();
           }
 
           if (!$errors) {
             $object_phid = $finder->getRequestedObjectPHID();
             if (!$object_phid) {
               $object_phid = $pr_commit->getPHID();
             }
 
             $pull->setRequestedObjectPHID($object_phid);
           }
         }
 
         if (!$errors) {
           $existing = id(new ReleephRequest())
             ->loadOneWhere('requestCommitPHID = %s AND branchID = %d',
                 $pr_commit->getPHID(), $branch->getID());
           if ($existing) {
             return id(new AphrontRedirectResponse())
               ->setURI('/releeph/request/edit/'.$existing->getID().
                 '?existing=1');
           }
 
           $xactions[] = id(new ReleephRequestTransaction())
             ->setTransactionType(ReleephRequestTransaction::TYPE_REQUEST)
             ->setNewValue($pr_commit->getPHID());
 
           $xactions[] = id(new ReleephRequestTransaction())
             ->setTransactionType(ReleephRequestTransaction::TYPE_USER_INTENT)
             // To help hide these implicit intents...
             ->setMetadataValue('isRQCreate', true)
             ->setMetadataValue('userPHID', $viewer->getPHID())
             ->setMetadataValue(
               'isAuthoritative',
               $product->isAuthoritative($viewer))
             ->setNewValue(ReleephRequest::INTENT_WANT);
         }
       }
 
       // TODO: This should happen implicitly while building transactions
       // instead.
       foreach ($field_list->getFields() as $field) {
         $field->readValueFromRequest($request);
       }
 
       if (!$errors) {
         foreach ($fields as $field) {
           if ($field->isEditable()) {
             try {
               $data = $request->getRequestData();
               $value = idx($data, $field->getRequiredStorageKey());
               $field->validate($value);
               $xactions[] = id(new ReleephRequestTransaction())
                 ->setTransactionType(ReleephRequestTransaction::TYPE_EDIT_FIELD)
                 ->setMetadataValue('fieldClass', get_class($field))
                 ->setNewValue($value);
             } catch (ReleephFieldParseException $ex) {
               $errors[] = $ex->getMessage();
             }
           }
         }
       }
 
       if (!$errors) {
         $editor = id(new ReleephRequestTransactionalEditor())
           ->setActor($viewer)
           ->setContinueOnNoEffect(true)
           ->setContentSourceFromRequest($request);
         $editor->applyTransactions($pull, $xactions);
         return id(new AphrontRedirectResponse())->setURI($cancel_uri);
       }
     }
 
     $handle_phids = array(
       $pull->getRequestUserPHID(),
       $pull->getRequestCommitPHID(),
     );
     $handle_phids = array_filter($handle_phids);
     if ($handle_phids) {
       $handles = id(new PhabricatorHandleQuery())
         ->setViewer($viewer)
         ->withPHIDs($handle_phids)
         ->execute();
     } else {
       $handles = array();
     }
 
     $age_string = '';
     if ($is_edit) {
       $age_string = phutil_format_relative_time(
         time() - $pull->getDateCreated()).' ago';
     }
 
     // Warn the user if we've been redirected here because we tried to
     // re-request something.
     $notice_view = null;
     if ($request->getInt('existing')) {
       $notice_messages = array(
         pht('You are editing an existing pick request!'),
         pht(
           'Requested %s by %s',
           $age_string,
           $handles[$pull->getRequestUserPHID()]->renderLink()),
       );
       $notice_view = id(new PHUIInfoView())
         ->setSeverity(PHUIInfoView::SEVERITY_NOTICE)
         ->setErrors($notice_messages);
     }
 
     $form = id(new AphrontFormView())
       ->setUser($viewer);
 
     if ($is_edit) {
       $form
         ->appendChild(
           id(new AphrontFormMarkupControl())
             ->setLabel(pht('Original Commit'))
             ->setValue(
               $handles[$pull->getRequestCommitPHID()]->renderLink()))
         ->appendChild(
           id(new AphrontFormMarkupControl())
             ->setLabel(pht('Requestor'))
             ->setValue(hsprintf(
               '%s %s',
               $handles[$pull->getRequestUserPHID()]->renderLink(),
               $age_string)));
     } else {
       $origin = null;
       $diff_rev_id = $request->getStr('D');
       if ($diff_rev_id) {
         $diff_rev = id(new DifferentialRevisionQuery())
           ->setViewer($viewer)
           ->withIDs(array($diff_rev_id))
           ->executeOne();
         $origin = '/D'.$diff_rev->getID();
         $title = sprintf(
           'D%d: %s',
           $diff_rev_id,
           $diff_rev->getTitle());
         $form
           ->addHiddenInput('requestIdentifierRaw', 'D'.$diff_rev_id)
           ->appendChild(
             id(new AphrontFormStaticControl())
               ->setLabel(pht('Diff'))
               ->setValue($title));
       } else {
         $origin = $branch->getURI();
         $repo = $product->getRepository();
         $branch_cut_point = id(new PhabricatorRepositoryCommit())
           ->loadOneWhere(
               'phid = %s',
               $branch->getCutPointCommitPHID());
         $form->appendChild(
           id(new ReleephRequestTypeaheadControl())
             ->setName('requestIdentifierRaw')
             ->setLabel(pht('Commit ID'))
             ->setRepo($repo)
             ->setValue($request_identifier)
             ->setError($e_request_identifier)
             ->setStartTime($branch_cut_point->getEpoch())
             ->setCaption(
               pht(
                 'Start typing to autocomplete on commit title, '.
                 'or give a Phabricator commit identifier like rFOO1234.')));
       }
     }
 
     $field_list->appendFieldsToForm($form);
 
     $crumbs = $this->buildApplicationCrumbs();
 
     if ($is_edit) {
       $title = pht('Edit Pull Request');
       $submit_name = pht('Save');
 
       $crumbs->addTextCrumb($pull->getMonogram(), '/'.$pull->getMonogram());
       $crumbs->addTextCrumb(pht('Edit'));
     } else {
       $title = pht('Create Pull Request');
       $submit_name = pht('Create Pull Request');
 
       $crumbs->addTextCrumb(pht('New Pull Request'));
     }
 
     $form->appendChild(
       id(new AphrontFormSubmitControl())
         ->addCancelButton($cancel_uri, pht('Cancel'))
         ->setValue($submit_name));
 
     $box = id(new PHUIObjectBoxView())
       ->setHeaderText($title)
       ->setFormErrors($errors)
       ->appendChild($form);
 
     return $this->buildApplicationPage(
       array(
         $crumbs,
         $notice_view,
         $box,
       ),
       array(
         'title' => $title,
       ));
   }
 }
diff --git a/src/applications/releeph/controller/request/ReleephRequestTypeaheadController.php b/src/applications/releeph/controller/request/ReleephRequestTypeaheadController.php
index ca49a52858..3a93de7453 100644
--- a/src/applications/releeph/controller/request/ReleephRequestTypeaheadController.php
+++ b/src/applications/releeph/controller/request/ReleephRequestTypeaheadController.php
@@ -1,92 +1,90 @@
 <?php
 
 final class ReleephRequestTypeaheadController
   extends PhabricatorTypeaheadDatasourceController {
 
-  public function processRequest() {
-    $request = $this->getRequest();
-
+  public function handleRequest(AphrontRequest $request) {
     $query    = $request->getStr('q');
     $repo_id  = $request->getInt('repo');
     $since    = $request->getInt('since');
     $limit    = $request->getInt('limit');
 
     $now = time();
     $data = array();
 
     // Dummy instances used for getting connections, table names, etc.
     $pr_commit = new PhabricatorRepositoryCommit();
     $pr_commit_data = new PhabricatorRepositoryCommitData();
 
     $conn = $pr_commit->establishConnection('r');
 
     $rows = queryfx_all(
       $conn,
       'SELECT
         rc.phid as commitPHID,
         rc.authorPHID,
         rcd.authorName,
         SUBSTRING(rcd.commitMessage, 1, 100) AS shortMessage,
         rc.commitIdentifier,
         rc.epoch
         FROM %T rc
         INNER JOIN %T rcd ON rcd.commitID = rc.id
         WHERE repositoryID = %d
         AND rc.epoch >= %d
         AND (
           rcd.commitMessage LIKE %~
           OR
           rc.commitIdentifier LIKE %~
         )
         ORDER BY rc.epoch DESC
         LIMIT %d',
       $pr_commit->getTableName(),
       $pr_commit_data->getTableName(),
       $repo_id,
       $since,
       $query,
       $query,
       $limit);
 
     foreach ($rows as $row) {
       $full_commit_id = $row['commitIdentifier'];
       $short_commit_id = substr($full_commit_id, 0, 12);
       $first_line = $this->getFirstLine($row['shortMessage']);
       $data[] = array(
         $full_commit_id,
         $short_commit_id,
         $row['authorName'],
         phutil_format_relative_time($now - $row['epoch']),
         $first_line,
       );
     }
 
     return id(new AphrontAjaxResponse())
       ->setContent($data);
   }
 
   /**
    * Split either at the first new line, or a bunch of dashes.
    *
    * Really just a legacy from old Releeph Daemon commit messages where I used
    * to say:
    *
    *   Commit of FOO for BAR
    *   ------------
    *   This does X Y Z
    *
    */
   private function getFirstLine($commit_message_fragment) {
     static $separators = array('-------', "\n");
     $string = ltrim($commit_message_fragment);
     $first_line = $string;
     foreach ($separators as $separator) {
       if ($pos = strpos($string, $separator)) {
         $first_line = substr($string, 0, $pos);
         break;
       }
     }
     return $first_line;
   }
 
 }
diff --git a/src/applications/releeph/controller/request/ReleephRequestViewController.php b/src/applications/releeph/controller/request/ReleephRequestViewController.php
index 04f91b5fc6..694505cd20 100644
--- a/src/applications/releeph/controller/request/ReleephRequestViewController.php
+++ b/src/applications/releeph/controller/request/ReleephRequestViewController.php
@@ -1,100 +1,93 @@
 <?php
 
 final class ReleephRequestViewController
   extends ReleephBranchController {
 
-  private $requestID;
-
-  public function willProcessRequest(array $data) {
-    $this->requestID = $data['requestID'];
-  }
-
-  public function processRequest() {
-    $request = $this->getRequest();
-    $viewer = $request->getUser();
+  public function handleRequest(AphrontRequest $request) {
+    $id = $request->getURIData('requestID');
+    $viewer = $request->getViewer();
 
     $pull = id(new ReleephRequestQuery())
       ->setViewer($viewer)
-      ->withIDs(array($this->requestID))
+      ->withIDs(array($id))
       ->executeOne();
     if (!$pull) {
       return new Aphront404Response();
     }
     $this->setBranch($pull->getBranch());
 
     // Redirect older URIs to new "Y" URIs.
     // TODO: Get rid of this eventually.
     $actual_path = $request->getRequestURI()->getPath();
     $expect_path = '/'.$pull->getMonogram();
     if ($actual_path != $expect_path) {
       return id(new AphrontRedirectResponse())->setURI($expect_path);
     }
 
     // TODO: Break this 1:1 stuff?
     $branch = $pull->getBranch();
 
     $field_list = PhabricatorCustomField::getObjectFields(
       $pull,
       PhabricatorCustomField::ROLE_VIEW);
 
     $field_list
       ->setViewer($viewer)
       ->readFieldsFromStorage($pull);
 
     // TODO: This should be more modern and general.
     $engine = id(new PhabricatorMarkupEngine())
       ->setViewer($viewer);
     foreach ($field_list->getFields() as $field) {
       if ($field->shouldMarkup()) {
         $field->setMarkupEngine($engine);
       }
     }
     $engine->process();
 
     $pull_box = id(new ReleephRequestView())
       ->setUser($viewer)
       ->setCustomFields($field_list)
       ->setPullRequest($pull);
 
     $timeline = $this->buildTransactionTimeline(
       $pull,
       new ReleephRequestTransactionQuery());
 
     $add_comment_header = pht('Plea or Yield');
 
     $draft = PhabricatorDraft::newFromUserAndKey(
       $viewer,
       $pull->getPHID());
 
     $title = hsprintf(
       '%s %s',
       $pull->getMonogram(),
       $pull->getSummaryForDisplay());
 
     $add_comment_form = id(new PhabricatorApplicationTransactionCommentView())
       ->setUser($viewer)
       ->setObjectPHID($pull->getPHID())
       ->setDraft($draft)
       ->setHeaderText($add_comment_header)
       ->setAction($this->getApplicationURI(
         '/request/comment/'.$pull->getID().'/'))
       ->setSubmitButtonName(pht('Comment'));
 
     $crumbs = $this->buildApplicationCrumbs();
     $crumbs->addTextCrumb($pull->getMonogram(), '/'.$pull->getMonogram());
 
     return $this->buildStandardPageResponse(
       array(
         $crumbs,
         $pull_box,
         $timeline,
         $add_comment_form,
       ),
       array(
         'title' => $title,
-        'device' => true,
       ));
   }
 
 
 }