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
@@ -536,6 +536,7 @@
     'DifferentialRevisionPackageHeraldField' => 'applications/differential/herald/DifferentialRevisionPackageHeraldField.php',
     'DifferentialRevisionPackageOwnerHeraldField' => 'applications/differential/herald/DifferentialRevisionPackageOwnerHeraldField.php',
     'DifferentialRevisionQuery' => 'applications/differential/query/DifferentialRevisionQuery.php',
+    'DifferentialRevisionRelationshipSource' => 'applications/search/relationship/DifferentialRevisionRelationshipSource.php',
     'DifferentialRevisionRepositoryHeraldField' => 'applications/differential/herald/DifferentialRevisionRepositoryHeraldField.php',
     'DifferentialRevisionRepositoryProjectsHeraldField' => 'applications/differential/herald/DifferentialRevisionRepositoryProjectsHeraldField.php',
     'DifferentialRevisionRequiredActionResultBucket' => 'applications/differential/query/DifferentialRevisionRequiredActionResultBucket.php',
@@ -614,6 +615,7 @@
     'DiffusionCommitParentsQueryConduitAPIMethod' => 'applications/diffusion/conduit/DiffusionCommitParentsQueryConduitAPIMethod.php',
     'DiffusionCommitQuery' => 'applications/diffusion/query/DiffusionCommitQuery.php',
     'DiffusionCommitRef' => 'applications/diffusion/data/DiffusionCommitRef.php',
+    'DiffusionCommitRelationshipSource' => 'applications/search/relationship/DiffusionCommitRelationshipSource.php',
     'DiffusionCommitRemarkupRule' => 'applications/diffusion/remarkup/DiffusionCommitRemarkupRule.php',
     'DiffusionCommitRemarkupRuleTestCase' => 'applications/diffusion/remarkup/__tests__/DiffusionCommitRemarkupRuleTestCase.php',
     'DiffusionCommitRepositoryHeraldField' => 'applications/diffusion/herald/DiffusionCommitRepositoryHeraldField.php',
@@ -1442,6 +1444,7 @@
     'ManiphestTaskPriorityHeraldField' => 'applications/maniphest/herald/ManiphestTaskPriorityHeraldField.php',
     'ManiphestTaskQuery' => 'applications/maniphest/query/ManiphestTaskQuery.php',
     'ManiphestTaskRelationship' => 'applications/maniphest/relationship/ManiphestTaskRelationship.php',
+    'ManiphestTaskRelationshipSource' => 'applications/search/relationship/ManiphestTaskRelationshipSource.php',
     'ManiphestTaskResultListView' => 'applications/maniphest/view/ManiphestTaskResultListView.php',
     'ManiphestTaskSearchEngine' => 'applications/maniphest/query/ManiphestTaskSearchEngine.php',
     'ManiphestTaskStatus' => 'applications/maniphest/constants/ManiphestTaskStatus.php',
@@ -2867,6 +2870,7 @@
     'PhabricatorObjectQuery' => 'applications/phid/query/PhabricatorObjectQuery.php',
     'PhabricatorObjectRelationship' => 'applications/search/relationship/PhabricatorObjectRelationship.php',
     'PhabricatorObjectRelationshipList' => 'applications/search/relationship/PhabricatorObjectRelationshipList.php',
+    'PhabricatorObjectRelationshipSource' => 'applications/search/relationship/PhabricatorObjectRelationshipSource.php',
     'PhabricatorObjectRemarkupRule' => 'infrastructure/markup/rule/PhabricatorObjectRemarkupRule.php',
     'PhabricatorObjectSelectorDialog' => 'view/control/PhabricatorObjectSelectorDialog.php',
     'PhabricatorOffsetPagedQuery' => 'infrastructure/query/PhabricatorOffsetPagedQuery.php',
@@ -3397,6 +3401,7 @@
     'PhabricatorSearchOrderField' => 'applications/search/field/PhabricatorSearchOrderField.php',
     'PhabricatorSearchRelationship' => 'applications/search/constants/PhabricatorSearchRelationship.php',
     'PhabricatorSearchRelationshipController' => 'applications/search/controller/PhabricatorSearchRelationshipController.php',
+    'PhabricatorSearchRelationshipSourceController' => 'applications/search/controller/PhabricatorSearchRelationshipSourceController.php',
     'PhabricatorSearchResultBucket' => 'applications/search/buckets/PhabricatorSearchResultBucket.php',
     'PhabricatorSearchResultBucketGroup' => 'applications/search/buckets/PhabricatorSearchResultBucketGroup.php',
     'PhabricatorSearchResultView' => 'applications/search/view/PhabricatorSearchResultView.php',
@@ -3870,6 +3875,7 @@
     'PholioMockNameHeraldField' => 'applications/pholio/herald/PholioMockNameHeraldField.php',
     'PholioMockPHIDType' => 'applications/pholio/phid/PholioMockPHIDType.php',
     'PholioMockQuery' => 'applications/pholio/query/PholioMockQuery.php',
+    'PholioMockRelationshipSource' => 'applications/search/relationship/PholioMockRelationshipSource.php',
     'PholioMockSearchEngine' => 'applications/pholio/query/PholioMockSearchEngine.php',
     'PholioMockThumbGridView' => 'applications/pholio/view/PholioMockThumbGridView.php',
     'PholioMockViewController' => 'applications/pholio/controller/PholioMockViewController.php',
@@ -4887,6 +4893,7 @@
     'DifferentialRevisionPackageHeraldField' => 'DifferentialRevisionHeraldField',
     'DifferentialRevisionPackageOwnerHeraldField' => 'DifferentialRevisionHeraldField',
     'DifferentialRevisionQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
+    'DifferentialRevisionRelationshipSource' => 'PhabricatorObjectRelationshipSource',
     'DifferentialRevisionRepositoryHeraldField' => 'DifferentialRevisionHeraldField',
     'DifferentialRevisionRepositoryProjectsHeraldField' => 'DifferentialRevisionHeraldField',
     'DifferentialRevisionRequiredActionResultBucket' => 'DifferentialRevisionResultBucket',
@@ -4965,6 +4972,7 @@
     'DiffusionCommitParentsQueryConduitAPIMethod' => 'DiffusionQueryConduitAPIMethod',
     'DiffusionCommitQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
     'DiffusionCommitRef' => 'Phobject',
+    'DiffusionCommitRelationshipSource' => 'PhabricatorObjectRelationshipSource',
     'DiffusionCommitRemarkupRule' => 'PhabricatorObjectRemarkupRule',
     'DiffusionCommitRemarkupRuleTestCase' => 'PhabricatorTestCase',
     'DiffusionCommitRepositoryHeraldField' => 'DiffusionCommitHeraldField',
@@ -5935,6 +5943,7 @@
     'ManiphestTaskPriorityHeraldField' => 'ManiphestTaskHeraldField',
     'ManiphestTaskQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
     'ManiphestTaskRelationship' => 'PhabricatorObjectRelationship',
+    'ManiphestTaskRelationshipSource' => 'PhabricatorObjectRelationshipSource',
     'ManiphestTaskResultListView' => 'ManiphestView',
     'ManiphestTaskSearchEngine' => 'PhabricatorApplicationSearchEngine',
     'ManiphestTaskStatus' => 'ManiphestConstants',
@@ -7561,6 +7570,7 @@
     'PhabricatorObjectQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
     'PhabricatorObjectRelationship' => 'Phobject',
     'PhabricatorObjectRelationshipList' => 'Phobject',
+    'PhabricatorObjectRelationshipSource' => 'Phobject',
     'PhabricatorObjectRemarkupRule' => 'PhutilRemarkupRule',
     'PhabricatorObjectSelectorDialog' => 'Phobject',
     'PhabricatorOffsetPagedQuery' => 'PhabricatorQuery',
@@ -8219,6 +8229,7 @@
     'PhabricatorSearchOrderField' => 'PhabricatorSearchField',
     'PhabricatorSearchRelationship' => 'Phobject',
     'PhabricatorSearchRelationshipController' => 'PhabricatorSearchBaseController',
+    'PhabricatorSearchRelationshipSourceController' => 'PhabricatorSearchBaseController',
     'PhabricatorSearchResultBucket' => 'Phobject',
     'PhabricatorSearchResultBucketGroup' => 'Phobject',
     'PhabricatorSearchResultView' => 'AphrontView',
@@ -8790,6 +8801,7 @@
     'PholioMockNameHeraldField' => 'PholioMockHeraldField',
     'PholioMockPHIDType' => 'PhabricatorPHIDType',
     'PholioMockQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
+    'PholioMockRelationshipSource' => 'PhabricatorObjectRelationshipSource',
     'PholioMockSearchEngine' => 'PhabricatorApplicationSearchEngine',
     'PholioMockThumbGridView' => 'AphrontView',
     'PholioMockViewController' => 'PholioController',
diff --git a/src/applications/maniphest/relationship/ManiphestTaskHasCommitRelationship.php b/src/applications/maniphest/relationship/ManiphestTaskHasCommitRelationship.php
--- a/src/applications/maniphest/relationship/ManiphestTaskHasCommitRelationship.php
+++ b/src/applications/maniphest/relationship/ManiphestTaskHasCommitRelationship.php
@@ -17,13 +17,6 @@
     return 'fa-code';
   }
 
-  public function shouldAppearInActionMenu() {
-    // TODO: For now, the default search for commits is not very good, so
-    // it is hard to find objects to link to. Until that works better, just
-    // hide this item.
-    return false;
-  }
-
   public function canRelateObjects($src, $dst) {
     return ($dst instanceof PhabricatorRepositoryCommit);
   }
@@ -40,4 +33,8 @@
     return pht('Save Related Commits');
   }
 
+  protected function newRelationshipSource() {
+    return new DiffusionCommitRelationshipSource();
+  }
+
 }
diff --git a/src/applications/maniphest/relationship/ManiphestTaskHasMockRelationship.php b/src/applications/maniphest/relationship/ManiphestTaskHasMockRelationship.php
--- a/src/applications/maniphest/relationship/ManiphestTaskHasMockRelationship.php
+++ b/src/applications/maniphest/relationship/ManiphestTaskHasMockRelationship.php
@@ -33,4 +33,8 @@
     return pht('Save Related Mocks');
   }
 
+  protected function newRelationshipSource() {
+    return new PholioMockRelationshipSource();
+  }
+
 }
diff --git a/src/applications/maniphest/relationship/ManiphestTaskHasParentRelationship.php b/src/applications/maniphest/relationship/ManiphestTaskHasParentRelationship.php
--- a/src/applications/maniphest/relationship/ManiphestTaskHasParentRelationship.php
+++ b/src/applications/maniphest/relationship/ManiphestTaskHasParentRelationship.php
@@ -37,4 +37,8 @@
     return pht('Save Parent Tasks');
   }
 
+  protected function newRelationshipSource() {
+    return new ManiphestTaskRelationshipSource();
+  }
+
 }
diff --git a/src/applications/maniphest/relationship/ManiphestTaskHasRevisionRelationship.php b/src/applications/maniphest/relationship/ManiphestTaskHasRevisionRelationship.php
--- a/src/applications/maniphest/relationship/ManiphestTaskHasRevisionRelationship.php
+++ b/src/applications/maniphest/relationship/ManiphestTaskHasRevisionRelationship.php
@@ -33,4 +33,8 @@
     return pht('Save Related Revisions');
   }
 
+  protected function newRelationshipSource() {
+    return new DifferentialRevisionRelationshipSource();
+  }
+
 }
diff --git a/src/applications/maniphest/relationship/ManiphestTaskHasSubtaskRelationship.php b/src/applications/maniphest/relationship/ManiphestTaskHasSubtaskRelationship.php
--- a/src/applications/maniphest/relationship/ManiphestTaskHasSubtaskRelationship.php
+++ b/src/applications/maniphest/relationship/ManiphestTaskHasSubtaskRelationship.php
@@ -37,4 +37,8 @@
     return pht('Save Subtasks');
   }
 
+  protected function newRelationshipSource() {
+    return new ManiphestTaskRelationshipSource();
+  }
+
 }
diff --git a/src/applications/phame/search/PhamePostFulltextEngine.php b/src/applications/phame/search/PhamePostFulltextEngine.php
--- a/src/applications/phame/search/PhamePostFulltextEngine.php
+++ b/src/applications/phame/search/PhamePostFulltextEngine.php
@@ -28,7 +28,6 @@
       $post->getPHID(),
       PhabricatorPhamePostPHIDType::TYPECONST,
       PhabricatorTime::getNow());
-
   }
 
 }
diff --git a/src/applications/pholio/search/PholioMockFulltextEngine.php b/src/applications/pholio/search/PholioMockFulltextEngine.php
--- a/src/applications/pholio/search/PholioMockFulltextEngine.php
+++ b/src/applications/pholio/search/PholioMockFulltextEngine.php
@@ -20,6 +20,14 @@
       $mock->getAuthorPHID(),
       PhabricatorPeopleUserPHIDType::TYPECONST,
       $mock->getDateCreated());
+
+    $document->addRelationship(
+      $mock->isClosed()
+        ? PhabricatorSearchRelationship::RELATIONSHIP_CLOSED
+        : PhabricatorSearchRelationship::RELATIONSHIP_OPEN,
+      $mock->getPHID(),
+      PholioMockPHIDType::TYPECONST,
+      PhabricatorTime::getNow());
   }
 
 }
diff --git a/src/applications/repository/search/DiffusionCommitFulltextEngine.php b/src/applications/repository/search/DiffusionCommitFulltextEngine.php
--- a/src/applications/repository/search/DiffusionCommitFulltextEngine.php
+++ b/src/applications/repository/search/DiffusionCommitFulltextEngine.php
@@ -47,5 +47,13 @@
       $repository->getPHID(),
       PhabricatorRepositoryRepositoryPHIDType::TYPECONST,
       $date_created);
+
+    $document->addRelationship(
+      $commit->isUnreachable()
+        ? PhabricatorSearchRelationship::RELATIONSHIP_CLOSED
+        : PhabricatorSearchRelationship::RELATIONSHIP_OPEN,
+      $commit->getPHID(),
+      PhabricatorRepositoryCommitPHIDType::TYPECONST,
+      PhabricatorTime::getNow());
   }
 }
diff --git a/src/applications/search/application/PhabricatorSearchApplication.php b/src/applications/search/application/PhabricatorSearchApplication.php
--- a/src/applications/search/application/PhabricatorSearchApplication.php
+++ b/src/applications/search/application/PhabricatorSearchApplication.php
@@ -43,6 +43,8 @@
         'order/(?P<engine>[^/]+)/' => 'PhabricatorSearchOrderController',
         'rel/(?P<relationshipKey>[^/]+)/(?P<sourcePHID>[^/]+)/'
           => 'PhabricatorSearchRelationshipController',
+        'source/(?P<relationshipKey>[^/]+)/(?P<sourcePHID>[^/]+)/'
+          => 'PhabricatorSearchRelationshipSourceController',
       ),
     );
   }
diff --git a/src/applications/search/controller/PhabricatorSearchBaseController.php b/src/applications/search/controller/PhabricatorSearchBaseController.php
--- a/src/applications/search/controller/PhabricatorSearchBaseController.php
+++ b/src/applications/search/controller/PhabricatorSearchBaseController.php
@@ -2,10 +2,41 @@
 
 abstract class PhabricatorSearchBaseController extends PhabricatorController {
 
+
   const ACTION_ATTACH       = 'attach';
   const ACTION_MERGE        = 'merge';
   const ACTION_DEPENDENCIES = 'dependencies';
   const ACTION_BLOCKS       = 'blocks';
   const ACTION_EDGE         = 'edge';
 
+  protected function loadRelationshipObject() {
+    $request = $this->getRequest();
+    $viewer = $this->getViewer();
+
+    $phid = $request->getURIData('sourcePHID');
+
+    return id(new PhabricatorObjectQuery())
+      ->setViewer($viewer)
+      ->withPHIDs(array($phid))
+      ->requireCapabilities(
+        array(
+          PhabricatorPolicyCapability::CAN_VIEW,
+          PhabricatorPolicyCapability::CAN_EDIT,
+        ))
+      ->executeOne();
+  }
+
+  protected function loadRelationship($object) {
+    $request = $this->getRequest();
+    $viewer = $this->getViewer();
+
+    $relationship_key = $request->getURIData('relationshipKey');
+
+    $list = PhabricatorObjectRelationshipList::newForObject(
+      $viewer,
+      $object);
+
+    return $list->getRelationship($relationship_key);
+  }
+
 }
diff --git a/src/applications/search/controller/PhabricatorSearchRelationshipController.php b/src/applications/search/controller/PhabricatorSearchRelationshipController.php
--- a/src/applications/search/controller/PhabricatorSearchRelationshipController.php
+++ b/src/applications/search/controller/PhabricatorSearchRelationshipController.php
@@ -6,26 +6,12 @@
   public function handleRequest(AphrontRequest $request) {
     $viewer = $this->getViewer();
 
-    $phid = $request->getURIData('sourcePHID');
-    $object = id(new PhabricatorObjectQuery())
-      ->setViewer($viewer)
-      ->withPHIDs(array($phid))
-      ->requireCapabilities(
-        array(
-          PhabricatorPolicyCapability::CAN_VIEW,
-          PhabricatorPolicyCapability::CAN_EDIT,
-        ))
-      ->executeOne();
+    $object = $this->loadRelationshipObject();
     if (!$object) {
       return new Aphront404Response();
     }
 
-    $list = PhabricatorObjectRelationshipList::newForObject(
-      $viewer,
-      $object);
-
-    $relationship_key = $request->getURIData('relationshipKey');
-    $relationship = $list->getRelationship($relationship_key);
+    $relationship = $this->loadRelationship($object);
     if (!$relationship) {
       return new Aphront404Response();
     }
@@ -135,21 +121,7 @@
     $dialog_button = $relationship->getDialogButtonText();
     $dialog_instructions = $relationship->getDialogInstructionsText();
 
-    // TODO: Remove this, this is just legacy support.
-    $legacy_kinds = array(
-      ManiphestTaskHasCommitEdgeType::EDGECONST => 'CMIT',
-      ManiphestTaskHasMockEdgeType::EDGECONST => 'MOCK',
-      ManiphestTaskHasRevisionEdgeType::EDGECONST => 'DREV',
-      ManiphestTaskDependsOnTaskEdgeType::EDGECONST => 'TASK',
-      ManiphestTaskDependedOnByTaskEdgeType::EDGECONST => 'TASK',
-    );
-
-    $edge_type = $relationship->getEdgeConstant();
-    $legacy_kind = idx($legacy_kinds, $edge_type);
-    if (!$legacy_kind) {
-      throw new Exception(
-        pht('Only specific legacy relationships are supported!'));
-    }
+    $source_uri = $relationship->getSourceURI($object);
 
     return id(new PhabricatorObjectSelectorDialog())
       ->setUser($viewer)
@@ -157,9 +129,9 @@
       ->setHandles($handles)
       ->setFilters($filters)
       ->setSelectedFilter('created')
-      ->setExcluded($phid)
+      ->setExcluded($src_phid)
       ->setCancelURI($done_uri)
-      ->setSearchURI("/search/select/{$legacy_kind}/edge/")
+      ->setSearchURI($source_uri)
       ->setTitle($dialog_title)
       ->setHeader($dialog_header)
       ->setButtonText($dialog_button)
diff --git a/src/applications/search/controller/PhabricatorSearchRelationshipSourceController.php b/src/applications/search/controller/PhabricatorSearchRelationshipSourceController.php
new file mode 100644
--- /dev/null
+++ b/src/applications/search/controller/PhabricatorSearchRelationshipSourceController.php
@@ -0,0 +1,89 @@
+<?php
+
+final class PhabricatorSearchRelationshipSourceController
+  extends PhabricatorSearchBaseController {
+
+  public function handleRequest(AphrontRequest $request) {
+    $viewer = $request->getViewer();
+
+    $object = $this->loadRelationshipObject();
+    if (!$object) {
+      return new Aphront404Response();
+    }
+
+    $relationship = $this->loadRelationship($object);
+    if (!$relationship) {
+      return new Aphront404Response();
+    }
+
+    $source = $relationship->newSource();
+    $query = new PhabricatorSavedQuery();
+
+    $action = $request->getURIData('action');
+    $query_str = $request->getStr('query');
+    $filter = $request->getStr('filter');
+
+    $query->setEngineClassName('PhabricatorSearchApplicationSearchEngine');
+    $query->setParameter('query', $query_str);
+
+    $types = $source->getResultPHIDTypes();
+    $query->setParameter('types', $types);
+
+    $status_open = PhabricatorSearchRelationship::RELATIONSHIP_OPEN;
+
+    switch ($filter) {
+      case 'assigned':
+        $query->setParameter('ownerPHIDs', array($viewer->getPHID()));
+        $query->setParameter('statuses', array($status_open));
+        break;
+      case 'created';
+        $query->setParameter('authorPHIDs', array($viewer->getPHID()));
+        $query->setParameter('statuses', array($status_open));
+        break;
+      case 'open':
+        $query->setParameter('statuses', array($status_open));
+        break;
+    }
+
+    $query->setParameter('excludePHIDs', array($request->getStr('exclude')));
+
+    $capabilities = $relationship->getRequiredRelationshipCapabilities();
+
+    $results = id(new PhabricatorSearchDocumentQuery())
+      ->setViewer($viewer)
+      ->requireObjectCapabilities($capabilities)
+      ->withSavedQuery($query)
+      ->setOffset(0)
+      ->setLimit(100)
+      ->execute();
+
+    $phids = array_fill_keys(mpull($results, 'getPHID'), true);
+    $phids += $this->queryObjectNames($query_str, $capabilities);
+
+    $phids = array_keys($phids);
+    $handles = $viewer->loadHandles($phids);
+
+    $data = array();
+    foreach ($handles as $handle) {
+      $view = new PhabricatorHandleObjectSelectorDataView($handle);
+      $data[] = $view->renderData();
+    }
+
+    return id(new AphrontAjaxResponse())->setContent($data);
+  }
+
+  private function queryObjectNames($query, $capabilities) {
+    $request = $this->getRequest();
+    $viewer = $request->getUser();
+
+    $objects = id(new PhabricatorObjectQuery())
+      ->setViewer($viewer)
+      ->requireCapabilities($capabilities)
+      ->withTypes(array($request->getURIData('type')))
+      ->withNames(array($query))
+      ->execute();
+
+    return mpull($objects, 'getPHID');
+  }
+
+}
diff --git a/src/applications/search/relationship/DifferentialRevisionRelationshipSource.php b/src/applications/search/relationship/DifferentialRevisionRelationshipSource.php
new file mode 100644
--- /dev/null
+++ b/src/applications/search/relationship/DifferentialRevisionRelationshipSource.php
@@ -0,0 +1,12 @@
+<?php
+
+final class DifferentialRevisionRelationshipSource
+  extends PhabricatorObjectRelationshipSource {
+
+  public function getResultPHIDTypes() {
+    return array(
+      DifferentialRevisionPHIDType::TYPECONST,
+    );
+  }
+
+}
diff --git a/src/applications/search/relationship/DiffusionCommitRelationshipSource.php b/src/applications/search/relationship/DiffusionCommitRelationshipSource.php
new file mode 100644
--- /dev/null
+++ b/src/applications/search/relationship/DiffusionCommitRelationshipSource.php
@@ -0,0 +1,12 @@
+<?php
+
+final class DiffusionCommitRelationshipSource
+  extends PhabricatorObjectRelationshipSource {
+
+  public function getResultPHIDTypes() {
+    return array(
+      PhabricatorRepositoryCommitPHIDType::TYPECONST,
+    );
+  }
+
+}
diff --git a/src/applications/search/relationship/ManiphestTaskRelationshipSource.php b/src/applications/search/relationship/ManiphestTaskRelationshipSource.php
new file mode 100644
--- /dev/null
+++ b/src/applications/search/relationship/ManiphestTaskRelationshipSource.php
@@ -0,0 +1,12 @@
+<?php
+
+final class ManiphestTaskRelationshipSource
+  extends PhabricatorObjectRelationshipSource {
+
+  public function getResultPHIDTypes() {
+    return array(
+      ManiphestTaskPHIDType::TYPECONST,
+    );
+  }
+
+}
diff --git a/src/applications/search/relationship/PhabricatorObjectRelationship.php b/src/applications/search/relationship/PhabricatorObjectRelationship.php
--- a/src/applications/search/relationship/PhabricatorObjectRelationship.php
+++ b/src/applications/search/relationship/PhabricatorObjectRelationship.php
@@ -47,6 +47,25 @@
       PhabricatorPolicyCapability::CAN_EDIT);
   }
 
+  public function getRequiredRelationshipCapabilities() {
+    return array(
+      PhabricatorPolicyCapability::CAN_VIEW,
+    );
+  }
+
+  final public function newSource() {
+    return $this->newRelationshipSource();
+  }
+
+  abstract protected function newRelationshipSource();
+
+  final public function getSourceURI($object) {
+    $relationship_key = $this->getRelationshipConstant();
+    $object_phid = $object->getPHID();
+
+    return "/search/source/{$relationship_key}/{$object_phid}/";
+  }
+
   final public function newAction($object) {
     $is_enabled = $this->isActionEnabled($object);
     $action_uri = $this->getActionURI($object);
diff --git a/src/applications/search/relationship/PhabricatorObjectRelationshipSource.php b/src/applications/search/relationship/PhabricatorObjectRelationshipSource.php
new file mode 100644
--- /dev/null
+++ b/src/applications/search/relationship/PhabricatorObjectRelationshipSource.php
@@ -0,0 +1,7 @@
+<?php
+
+abstract class PhabricatorObjectRelationshipSource extends Phobject {
+
+  abstract public function getResultPHIDTypes();
+
+}
diff --git a/src/applications/search/relationship/PholioMockRelationshipSource.php b/src/applications/search/relationship/PholioMockRelationshipSource.php
new file mode 100644
--- /dev/null
+++ b/src/applications/search/relationship/PholioMockRelationshipSource.php
@@ -0,0 +1,12 @@
+<?php
+
+final class PholioMockRelationshipSource
+  extends PhabricatorObjectRelationshipSource {
+
+  public function getResultPHIDTypes() {
+    return array(
+      PholioMockPHIDType::TYPECONST,
+    );
+  }
+
+}