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
@@ -4056,6 +4056,8 @@
     'PhabricatorProfileMenuItemConfigurationTransactionQuery' => 'applications/search/query/PhabricatorProfileMenuItemConfigurationTransactionQuery.php',
     'PhabricatorProfileMenuItemIconSet' => 'applications/search/menuitem/PhabricatorProfileMenuItemIconSet.php',
     'PhabricatorProfileMenuItemPHIDType' => 'applications/search/phidtype/PhabricatorProfileMenuItemPHIDType.php',
+    'PhabricatorProfileMenuItemView' => 'applications/search/engine/PhabricatorProfileMenuItemView.php',
+    'PhabricatorProfileMenuItemViewList' => 'applications/search/engine/PhabricatorProfileMenuItemViewList.php',
     'PhabricatorProject' => 'applications/project/storage/PhabricatorProject.php',
     'PhabricatorProjectAddHeraldAction' => 'applications/project/herald/PhabricatorProjectAddHeraldAction.php',
     'PhabricatorProjectApplication' => 'applications/project/application/PhabricatorProjectApplication.php',
@@ -10184,6 +10186,8 @@
     'PhabricatorProfileMenuItemConfigurationTransactionQuery' => 'PhabricatorApplicationTransactionQuery',
     'PhabricatorProfileMenuItemIconSet' => 'PhabricatorIconSet',
     'PhabricatorProfileMenuItemPHIDType' => 'PhabricatorPHIDType',
+    'PhabricatorProfileMenuItemView' => 'Phobject',
+    'PhabricatorProfileMenuItemViewList' => 'Phobject',
     'PhabricatorProject' => array(
       'PhabricatorProjectDAO',
       'PhabricatorApplicationTransactionInterface',
diff --git a/src/applications/search/editor/PhabricatorProfileMenuEditEngine.php b/src/applications/search/editor/PhabricatorProfileMenuEditEngine.php
--- a/src/applications/search/editor/PhabricatorProfileMenuEditEngine.php
+++ b/src/applications/search/editor/PhabricatorProfileMenuEditEngine.php
@@ -109,7 +109,7 @@
   }
 
   protected function getObjectEditTitleText($object) {
-    $object->willBuildNavigationItems(array($object));
+    $object->willGetMenuItemViewList(array($object));
     return pht('Edit Menu Item: %s', $object->getDisplayName());
   }
 
diff --git a/src/applications/search/engine/PhabricatorProfileMenuEngine.php b/src/applications/search/engine/PhabricatorProfileMenuEngine.php
--- a/src/applications/search/engine/PhabricatorProfileMenuEngine.php
+++ b/src/applications/search/engine/PhabricatorProfileMenuEngine.php
@@ -71,16 +71,6 @@
     return $this->controller;
   }
 
-  private function setDefaultItem(
-    PhabricatorProfileMenuItemConfiguration $default_item) {
-    $this->defaultItem = $default_item;
-    return $this;
-  }
-
-  public function getDefaultItem() {
-    return $this->pickDefaultItem($this->getItems());
-  }
-
   public function setShowNavigation($show) {
     $this->showNavigation = $show;
     return $this;
@@ -150,10 +140,10 @@
       $item_id = $request->getURIData('id');
     }
 
-    $item_list = $this->getItems();
+    $view_list = $this->newProfileMenuItemViewList();
 
-    $selected_item = $this->pickSelectedItem(
-      $item_list,
+    $selected_item = $this->selectItem(
+      $view_list,
       $item_id,
       $is_view);
 
@@ -183,8 +173,7 @@
         break;
     }
 
-    $navigation = $this->buildNavigation($selected_item);
-
+    $navigation = $view_list->newNavigationView();
     $crumbs = $controller->buildApplicationCrumbsForEditEngine();
 
     if (!$is_view) {
@@ -288,9 +277,7 @@
         if (!$this->isMenuEnginePinnable()) {
           return new Aphront404Response();
         }
-        $content = $this->buildItemDefaultContent(
-          $selected_item,
-          $item_list);
+        $content = $this->buildItemDefaultContent($selected_item);
         break;
       case 'edit':
         $content = $this->buildItemEditContent();
@@ -333,80 +320,7 @@
     return $page;
   }
 
-  public function buildNavigation(
-    PhabricatorProfileMenuItemConfiguration $selected_item = null) {
 
-    if ($this->navigation) {
-      return $this->navigation;
-    }
-
-    $nav = id(new AphrontSideNavFilterView())
-      ->setIsProfileMenu(true)
-      ->setBaseURI(new PhutilURI($this->getItemURI('')));
-
-    $menu_items = $this->getItems();
-
-    $filtered_items = array();
-    foreach ($menu_items as $menu_item) {
-      if ($menu_item->isDisabled()) {
-        continue;
-      }
-      $filtered_items[] = $menu_item;
-    }
-    $filtered_groups = mgroup($filtered_items, 'getMenuItemKey');
-    foreach ($filtered_groups as $group) {
-      $first_item = head($group);
-      $first_item->willBuildNavigationItems($group);
-    }
-
-    $has_items = false;
-    foreach ($menu_items as $menu_item) {
-      if ($menu_item->isDisabled()) {
-        continue;
-      }
-
-      $items = $menu_item->buildNavigationMenuItems();
-      foreach ($items as $item) {
-        $this->validateNavigationMenuItem($item);
-      }
-
-      // If the item produced only a single item which does not otherwise
-      // have a key, try to automatically assign it a reasonable key. This
-      // makes selecting the correct item simpler.
-
-      if (count($items) == 1) {
-        $item = head($items);
-        if ($item->getKey() === null) {
-          $default_key = $menu_item->getDefaultMenuItemKey();
-          $item->setKey($default_key);
-        }
-      }
-
-      foreach ($items as $item) {
-        $nav->addMenuItem($item);
-        $has_items = true;
-      }
-    }
-
-    if (!$has_items) {
-      // If the navigation menu has no items, add an empty label item to
-      // force it to render something.
-      $empty_item = id(new PHUIListItemView())
-        ->setType(PHUIListItemView::TYPE_LABEL);
-      $nav->addMenuItem($empty_item);
-    }
-
-    $nav->selectFilter(null);
-
-    $navigation_items = $nav->getMenu()->getItems();
-    $select_key = $this->pickHighlightedMenuItem(
-      $navigation_items,
-      $selected_item);
-    $nav->selectFilter($select_key);
-
-    $this->navigation = $nav;
-    return $this->navigation;
-  }
 
   private function getItems() {
     if ($this->items === null) {
@@ -715,7 +629,7 @@
    *
    * @return bool True if items may be pinned as default items.
    */
-  protected function isMenuEnginePinnable() {
+  public function isMenuEnginePinnable() {
     return !$this->isMenuEnginePersonalizable();
   }
 
@@ -779,7 +693,7 @@
     $filtered_groups = mgroup($items, 'getMenuItemKey');
     foreach ($filtered_groups as $group) {
       $first_item = head($group);
-      $first_item->willBuildNavigationItems($group);
+      $first_item->willGetMenuItemViewList($group);
     }
 
     // Users only need to be able to edit the object which this menu appears
@@ -1153,8 +1067,7 @@
   }
 
   private function buildItemDefaultContent(
-    PhabricatorProfileMenuItemConfiguration $configuration,
-    array $items) {
+    PhabricatorProfileMenuItemConfiguration $configuration) {
 
     $controller = $this->getController();
     $request = $controller->getRequest();
@@ -1220,6 +1133,17 @@
       ->setIsTailItem(true);
   }
 
+  public function getDefaultMenuItemConfiguration() {
+    $configs = $this->getItems();
+    foreach ($configs as $config) {
+      if ($config->isDefault()) {
+        return $config;
+      }
+    }
+
+    return null;
+  }
+
   public function adjustDefault($key) {
     $controller = $this->getController();
     $request = $controller->getRequest();
@@ -1340,157 +1264,74 @@
       pht('There are no menu items.'));
   }
 
-  private function pickDefaultItem(array $items) {
-    // Remove all the items which can not be the default item.
-    foreach ($items as $key => $item) {
-      if (!$item->canMakeDefault()) {
-        unset($items[$key]);
-        continue;
-      }
 
+  final public function newProfileMenuItemViewList() {
+    $items = $this->getItems();
+
+    // Throw away disabled items: they are not allowed to build any views for
+    // the menu.
+    foreach ($items as $key => $item) {
       if ($item->isDisabled()) {
         unset($items[$key]);
         continue;
       }
     }
 
-    // If this engine supports pinning items and a valid item is pinned,
-    // pick that item as the default.
-    if ($this->isMenuEnginePinnable()) {
-      foreach ($items as $key => $item) {
-        if ($item->isDefault()) {
-          return $item;
-        }
-      }
+    // Give each item group a callback so it can load data it needs to render
+    // views.
+    $groups = mgroup($items, 'getMenuItemKey');
+    foreach ($groups as $group) {
+      $item = head($group);
+      $item->willGetMenuItemViewList($group);
     }
 
-    // If we have some other valid items, pick the first one as the default.
-    if ($items) {
-      return head($items);
-    }
-
-    return null;
-  }
+    $view_list = id(new PhabricatorProfileMenuItemViewList())
+      ->setProfileMenuEngine($this);
 
-  private function pickSelectedItem(array $items, $item_id, $is_view) {
-    if (strlen($item_id)) {
-      $item_id_int = (int)$item_id;
-      foreach ($items as $item) {
-        if ($item_id_int) {
-          if ((int)$item->getID() === $item_id_int) {
-            return $item;
-          }
-        }
-
-        $builtin_key = $item->getBuiltinKey();
-        if ($builtin_key === (string)$item_id) {
-          return $item;
-        }
+    foreach ($items as $item) {
+      $views = $item->getMenuItemViewList();
+      foreach ($views as $view) {
+        $view_list->addItemView($view);
       }
-
-      // Nothing matches the selected item ID, so we don't have a valid
-      // selection.
-      return null;
-    }
-
-    if ($is_view) {
-      return $this->pickDefaultItem($items);
     }
 
-    return null;
+    return $view_list;
   }
 
-  private function pickHighlightedMenuItem(
-    array $items,
-    PhabricatorProfileMenuItemConfiguration $selected_item = null) {
+  private function selectItem(
+    PhabricatorProfileMenuItemViewList $view_list,
+    $item_id,
+    $want_default) {
 
-    assert_instances_of($items, 'PHUIListItemView');
-
-    $default_key = null;
-    if ($selected_item) {
-      $default_key = $selected_item->getDefaultMenuItemKey();
-    }
-
-    $controller = $this->getController();
-
-    // In some rare cases, when like building the "Favorites" menu on a
-    // 404 page, we may not have a controller. Just accept whatever default
-    // behavior we'd otherwise end up with.
-    if (!$controller) {
-      return $default_key;
-    }
+    // Figure out which view's content we're going to render. In most cases,
+    // the URI tells us. If we don't have an identifier in the URI, we'll
+    // render the default view instead if this is a workflow that falls back
+    // to default rendering.
 
-    $request = $controller->getRequest();
-
-    // See T12949. If one of the menu items is a link to the same URI that
-    // the page was accessed with, we want to highlight that item. For example,
-    // this allows you to add links to a menu that apply filters to a
-    // workboard.
-
-    $matches = array();
-    foreach ($items as $item) {
-      $href = $item->getHref();
-      if ($this->isMatchForRequestURI($request, $href)) {
-        $matches[] = $item;
+    $selected_view = null;
+    if (strlen($item_id)) {
+      $item_views = $view_list->getViewsWithItemIdentifier($item_id);
+      if ($item_views) {
+        $selected_view = head($item_views);
       }
-    }
-
-    foreach ($matches as $match) {
-      if ($match->getKey() === $default_key) {
-        return $default_key;
+    } else {
+      if ($want_default) {
+        $default_views = $view_list->getDefaultViews();
+        if ($default_views) {
+          $selected_view = head($default_views);
+        }
       }
     }
 
-    if ($matches) {
-      return head($matches)->getKey();
+    if ($selected_view) {
+      $view_list->setSelectedView($selected_view);
+      $selected_item = $selected_view->getMenuItemConfiguration();
+    } else {
+      $selected_item = null;
     }
 
-    return $default_key;
+    return $selected_item;
   }
 
-  private function isMatchForRequestURI(AphrontRequest $request, $item_uri) {
-    $request_uri = $request->getAbsoluteRequestURI();
-    $item_uri = new PhutilURI($item_uri);
-
-    // If the request URI and item URI don't have matching paths, they
-    // do not match.
-    if ($request_uri->getPath() !== $item_uri->getPath()) {
-      return false;
-    }
-
-    // If the request URI and item URI don't have matching parameters, they
-    // also do not match. We're specifically trying to let "?filter=X" work
-    // on Workboards, among other use cases, so this is important.
-    $request_params = $request_uri->getQueryParamsAsPairList();
-    $item_params = $item_uri->getQueryParamsAsPairList();
-    if ($request_params !== $item_params) {
-      return false;
-    }
-
-    // If the paths and parameters match, the item domain must be: empty; or
-    // match the request domain; or match the production domain.
-
-    $request_domain = $request_uri->getDomain();
-
-    $production_uri = PhabricatorEnv::getProductionURI('/');
-    $production_domain = id(new PhutilURI($production_uri))
-      ->getDomain();
-
-    $allowed_domains = array(
-      '',
-      $request_domain,
-      $production_domain,
-    );
-    $allowed_domains = array_fuse($allowed_domains);
-
-    $item_domain = $item_uri->getDomain();
-    $item_domain = (string)$item_domain;
-
-    if (isset($allowed_domains[$item_domain])) {
-      return true;
-    }
-
-    return false;
-  }
 
 }
diff --git a/src/applications/search/engine/PhabricatorProfileMenuItemView.php b/src/applications/search/engine/PhabricatorProfileMenuItemView.php
new file mode 100644
--- /dev/null
+++ b/src/applications/search/engine/PhabricatorProfileMenuItemView.php
@@ -0,0 +1,212 @@
+<?php
+
+final class PhabricatorProfileMenuItemView
+  extends Phobject {
+
+  private $config;
+  private $uri;
+  private $name;
+  private $icon;
+  private $disabled;
+  private $tooltip;
+  private $actions = array();
+  private $counts = array();
+  private $images = array();
+  private $progressBars = array();
+  private $isExternalLink;
+  private $specialType;
+
+  public function setMenuItemConfiguration(
+    PhabricatorProfileMenuItemConfiguration $config) {
+    $this->config = $config;
+    return $this;
+  }
+
+  public function getMenuItemConfiguration() {
+    return $this->config;
+  }
+
+  public function setURI($uri) {
+    $this->uri = $uri;
+    return $this;
+  }
+
+  public function getURI() {
+    return $this->uri;
+  }
+
+  public function setName($name) {
+    $this->name = $name;
+    return $this;
+  }
+
+  public function getName() {
+    return $this->name;
+  }
+
+  public function setIcon($icon) {
+    $this->icon = $icon;
+    return $this;
+  }
+
+  public function getIcon() {
+    return $this->icon;
+  }
+
+  public function setDisabled($disabled) {
+    $this->disabled = $disabled;
+    return $this;
+  }
+
+  public function getDisabled() {
+    return $this->disabled;
+  }
+
+  public function setTooltip($tooltip) {
+    $this->tooltip = $tooltip;
+    return $this;
+  }
+
+  public function getTooltip() {
+    return $this->tooltip;
+  }
+
+  public function newAction($uri) {
+    $this->actions[] = $uri;
+    return null;
+  }
+
+  public function newCount($count) {
+    $this->counts[] = $count;
+    return null;
+  }
+
+  public function newProfileImage($src) {
+    $this->images[] = $src;
+    return null;
+  }
+
+  public function newProgressBar($bar) {
+    $this->progressBars[] = $bar;
+    return null;
+  }
+
+  public function setIsExternalLink($is_external) {
+    $this->isExternalLink = $is_external;
+    return $this;
+  }
+
+  public function getIsExternalLink() {
+    return $this->isExternalLink;
+  }
+
+  public function setIsLabel($is_label) {
+    return $this->setSpecialType('label');
+  }
+
+  public function getIsLabel() {
+    return $this->isSpecialType('label');
+  }
+
+  public function setIsDivider($is_divider) {
+    return $this->setSpecialType('divider');
+  }
+
+  public function getIsDivider() {
+    return $this->isSpecialType('divider');
+  }
+
+  private function setSpecialType($type) {
+    $this->specialType = $type;
+    return $this;
+  }
+
+  private function isSpecialType($type) {
+    return ($this->specialType === $type);
+  }
+
+  public function newListItemView() {
+    $view = id(new PHUIListItemView())
+      ->setName($this->getName());
+
+    $uri = $this->getURI();
+    if (strlen($uri)) {
+      if ($this->getIsExternalLink()) {
+        if (!PhabricatorEnv::isValidURIForLink($uri)) {
+          $uri = '#';
+        }
+        $view->setRel('noreferrer');
+      }
+
+      $view->setHref($uri);
+    }
+
+    $icon = $this->getIcon();
+    if ($icon) {
+      $view->setIcon($icon);
+    }
+
+    if ($this->getDisabled()) {
+      $view->setDisabled(true);
+    }
+
+    if ($this->getIsLabel()) {
+      $view->setType(PHUIListItemView::TYPE_LABEL);
+    }
+
+    if ($this->getIsDivider()) {
+      $view
+        ->setType(PHUIListItemView::TYPE_DIVIDER)
+        ->addClass('phui-divider');
+    }
+
+    if ($this->images) {
+      require_celerity_resource('people-picture-menu-item-css');
+      foreach ($this->images as $image_src) {
+        $classes = array();
+        $classes[] = 'people-menu-image';
+
+        if ($this->getDisabled()) {
+          $classes[] = 'phui-image-disabled';
+        }
+
+        $image = phutil_tag(
+          'img',
+          array(
+            'src' => $image_src,
+            'class' => implode(' ', $classes),
+          ));
+
+        $image = phutil_tag(
+          'div',
+          array(
+            'class' => 'people-menu-image-container',
+          ),
+          $image);
+
+        $view->appendChild($image);
+      }
+    }
+
+    foreach ($this->counts as $count) {
+      $view->appendChild(
+        phutil_tag(
+          'span',
+          array(
+            'class' => 'phui-list-item-count',
+          ),
+          $count));
+    }
+
+    foreach ($this->actions as $action) {
+      $view->setActionIcon('fa-pencil', $action);
+    }
+
+    foreach ($this->progressBars as $bar) {
+      $view->appendChild($bar);
+    }
+
+    return $view;
+  }
+
+}
diff --git a/src/applications/search/engine/PhabricatorProfileMenuItemViewList.php b/src/applications/search/engine/PhabricatorProfileMenuItemViewList.php
new file mode 100644
--- /dev/null
+++ b/src/applications/search/engine/PhabricatorProfileMenuItemViewList.php
@@ -0,0 +1,278 @@
+<?php
+
+final class PhabricatorProfileMenuItemViewList
+  extends Phobject {
+
+  private $engine;
+  private $views = array();
+  private $selectedView;
+
+  public function setProfileMenuEngine(PhabricatorProfileMenuEngine $engine) {
+    $this->engine = $engine;
+    return $this;
+  }
+
+  public function getProfileMenuEngine() {
+    return $this->engine;
+  }
+
+  public function addItemView(PhabricatorProfileMenuItemView $view) {
+    $this->views[] = $view;
+    return $this;
+  }
+
+  public function getItemViews() {
+    return $this->views;
+  }
+
+  public function setSelectedView(PhabricatorProfileMenuItemView $view) {
+    $found = false;
+    foreach ($this->getItemViews() as $item_view) {
+      if ($view === $item_view) {
+        $found = true;
+        break;
+      }
+    }
+
+    if (!$found) {
+      throw new Exception(
+        pht(
+          'Provided view is not one of the views in the list: you can only '.
+          'select a view which appears in the list.'));
+    }
+
+    $this->selectedView = $view;
+
+    return $this;
+  }
+
+  public function setSelectedViewWithItemIdentifier($identifier) {
+    $views = $this->getViewsWithItemIdentifier($identifier);
+
+    if (!$views) {
+      throw new Exception(
+        pht(
+          'No views match identifier "%s"!',
+          $identifier));
+    }
+
+    return $this->setSelectedView(head($views));
+  }
+
+  public function getViewsWithItemIdentifier($identifier) {
+    $views = $this->getItemViews();
+
+    if (!strlen($identifier)) {
+      return array();
+    }
+
+    if (ctype_digit($identifier)) {
+      $identifier_int = (int)$identifier;
+    } else {
+      $identifier_int = null;
+    }
+
+    $identifier_str = (string)$identifier;
+
+    $results = array();
+    foreach ($views as $view) {
+      $config = $view->getMenuItemConfiguration();
+
+      if ($identifier_int !== null) {
+        $config_id = (int)$config->getID();
+        if ($config_id === $identifier_int) {
+          $results[] = $view;
+          continue;
+        }
+      }
+
+      if ($config->getBuiltinKey() === $identifier_str) {
+        $results[] = $view;
+        continue;
+      }
+    }
+
+    return $results;
+  }
+
+  public function getDefaultViews() {
+    $engine = $this->getProfileMenuEngine();
+    $can_pin = $engine->isMenuEnginePinnable();
+
+    $views = $this->getItemViews();
+
+    // Remove all the views which were built by an item that can not be the
+    // default item.
+    foreach ($views as $key => $view) {
+      $config = $view->getMenuItemConfiguration();
+
+      if (!$config->canMakeDefault()) {
+        unset($views[$key]);
+        continue;
+      }
+    }
+
+    // If this engine supports pinning items and we have candidate views from a
+    // valid pinned item, they are the default views.
+    if ($can_pin) {
+      $pinned = array();
+
+      foreach ($views as $key => $view) {
+        $config = $view->getMenuItemConfiguration();
+
+        if ($config->isDefault()) {
+          $pinned[] = $view;
+          continue;
+        }
+      }
+
+      if ($pinned) {
+        return $pinned;
+      }
+    }
+
+    // Return whatever remains that's still valid.
+    return $views;
+  }
+
+  public function newNavigationView() {
+    $engine = $this->getProfileMenuEngine();
+
+    $base_uri = $engine->getItemURI('');
+    $base_uri = new PhutilURI($base_uri);
+
+    $navigation = id(new AphrontSideNavFilterView())
+      ->setIsProfileMenu(true)
+      ->setBaseURI($base_uri);
+
+    $views = $this->getItemViews();
+    $selected_item = null;
+    $item_key = 0;
+    $items = array();
+    foreach ($views as $view) {
+      $list_item = $view->newListItemView();
+
+      // Assign unique keys to the list items. These keys are purely internal.
+      $list_item->setKey(sprintf('item(%d)', $item_key++));
+
+      if ($this->selectedView) {
+        if ($this->selectedView === $view) {
+          $selected_item = $list_item;
+        }
+      }
+
+      $navigation->addMenuItem($list_item);
+      $items[] = $list_item;
+    }
+
+    if (!$views) {
+      // If the navigation menu has no items, add an empty label item to
+      // force it to render something.
+      $empty_item = id(new PHUIListItemView())
+        ->setType(PHUIListItemView::TYPE_LABEL);
+      $navigation->addMenuItem($empty_item);
+    }
+
+    $highlight_key = $this->getHighlightedItemKey(
+      $items,
+      $selected_item);
+    $navigation->selectFilter($highlight_key);
+
+    return $navigation;
+  }
+
+  private function getHighlightedItemKey(
+    array $items,
+    PHUIListItemView $selected_item = null) {
+
+    assert_instances_of($items, 'PHUIListItemView');
+
+    $default_key = null;
+    if ($selected_item) {
+      $default_key = $selected_item->getKey();
+    }
+
+    $engine = $this->getProfileMenuEngine();
+    $controller = $engine->getController();
+
+    // In some rare cases, when like building the "Favorites" menu on a
+    // 404 page, we may not have a controller. Just accept whatever default
+    // behavior we'd otherwise end up with.
+    if (!$controller) {
+      return $default_key;
+    }
+
+    $request = $controller->getRequest();
+
+    // See T12949. If one of the menu items is a link to the same URI that
+    // the page was accessed with, we want to highlight that item. For example,
+    // this allows you to add links to a menu that apply filters to a
+    // workboard.
+
+    $matches = array();
+    foreach ($items as $item) {
+      $href = $item->getHref();
+      if ($this->isMatchForRequestURI($request, $href)) {
+        $matches[] = $item;
+      }
+    }
+
+    foreach ($matches as $match) {
+      if ($match->getKey() === $default_key) {
+        return $default_key;
+      }
+    }
+
+    if ($matches) {
+      return head($matches)->getKey();
+    }
+
+    return $default_key;
+  }
+
+  private function isMatchForRequestURI(AphrontRequest $request, $item_uri) {
+    $request_uri = $request->getAbsoluteRequestURI();
+    $item_uri = new PhutilURI($item_uri);
+
+    // If the request URI and item URI don't have matching paths, they
+    // do not match.
+    if ($request_uri->getPath() !== $item_uri->getPath()) {
+      return false;
+    }
+
+    // If the request URI and item URI don't have matching parameters, they
+    // also do not match. We're specifically trying to let "?filter=X" work
+    // on Workboards, among other use cases, so this is important.
+    $request_params = $request_uri->getQueryParamsAsPairList();
+    $item_params = $item_uri->getQueryParamsAsPairList();
+    if ($request_params !== $item_params) {
+      return false;
+    }
+
+    // If the paths and parameters match, the item domain must be: empty; or
+    // match the request domain; or match the production domain.
+
+    $request_domain = $request_uri->getDomain();
+
+    $production_uri = PhabricatorEnv::getProductionURI('/');
+    $production_domain = id(new PhutilURI($production_uri))
+      ->getDomain();
+
+    $allowed_domains = array(
+      '',
+      $request_domain,
+      $production_domain,
+    );
+    $allowed_domains = array_fuse($allowed_domains);
+
+    $item_domain = $item_uri->getDomain();
+    $item_domain = (string)$item_domain;
+
+    if (isset($allowed_domains[$item_domain])) {
+      return true;
+    }
+
+    return false;
+  }
+
+}
diff --git a/src/applications/search/storage/PhabricatorProfileMenuItemConfiguration.php b/src/applications/search/storage/PhabricatorProfileMenuItemConfiguration.php
--- a/src/applications/search/storage/PhabricatorProfileMenuItemConfiguration.php
+++ b/src/applications/search/storage/PhabricatorProfileMenuItemConfiguration.php
@@ -100,10 +100,6 @@
     return idx($this->menuItemProperties, $key, $default);
   }
 
-  public function buildNavigationMenuItems() {
-    return $this->getMenuItem()->buildNavigationMenuItems($this);
-  }
-
   public function getMenuItemTypeName() {
     return $this->getMenuItem()->getMenuItemTypeName();
   }
@@ -124,8 +120,12 @@
     return $this->getMenuItem()->shouldEnableForObject($object);
   }
 
-  public function willBuildNavigationItems(array $items) {
-    return $this->getMenuItem()->willBuildNavigationItems($items);
+  public function willGetMenuItemViewList(array $items) {
+    return $this->getMenuItem()->willGetMenuItemViewList($items);
+  }
+
+  public function getMenuItemViewList() {
+    return $this->getMenuItem()->getMenuItemViewList($this);
   }
 
   public function validateTransactions(array $map) {