diff --git a/src/applications/differential/xaction/DifferentialRevisionUpdateTransaction.php b/src/applications/differential/xaction/DifferentialRevisionUpdateTransaction.php
index 2b74cbfab5..189ed2f2ba 100644
--- a/src/applications/differential/xaction/DifferentialRevisionUpdateTransaction.php
+++ b/src/applications/differential/xaction/DifferentialRevisionUpdateTransaction.php
@@ -1,224 +1,226 @@
 <?php
 
 final class DifferentialRevisionUpdateTransaction
   extends DifferentialRevisionTransactionType {
 
   const TRANSACTIONTYPE = 'differential:update';
   const EDITKEY = 'update';
 
   public function generateOldValue($object) {
     return $object->getActiveDiffPHID();
   }
 
   public function applyInternalEffects($object, $value) {
     $should_review = $this->shouldRequestReviewAfterUpdate($object);
     if ($should_review) {
       $object->setModernRevisionStatus(
         DifferentialRevisionStatus::NEEDS_REVIEW);
     }
 
     $editor = $this->getEditor();
     $diff = $editor->requireDiff($value);
 
     $this->updateRevisionLineCounts($object, $diff);
 
     $object->setRepositoryPHID($diff->getRepositoryPHID());
     $object->setActiveDiffPHID($diff->getPHID());
     $object->attachActiveDiff($diff);
   }
 
   private function shouldRequestReviewAfterUpdate($object) {
     if ($this->isCommitUpdate()) {
       return false;
     }
 
     $should_update =
       $object->isNeedsRevision() ||
       $object->isChangePlanned() ||
       $object->isAbandoned();
     if ($should_update) {
       return true;
     }
 
     return false;
   }
 
   public function applyExternalEffects($object, $value) {
     $editor = $this->getEditor();
     $diff = $editor->requireDiff($value);
 
     // TODO: This can race with diff updates, particularly those from
     // Harbormaster. See discussion in T8650.
     $diff->setRevisionID($object->getID());
     $diff->save();
 
     // If there are any outstanding buildables for this diff, tell
     // Harbormaster that their containers need to be updated. This is
     // common, because `arc` creates buildables so it can upload lint
     // and unit results.
 
     $buildables = id(new HarbormasterBuildableQuery())
       ->setViewer(PhabricatorUser::getOmnipotentUser())
       ->withManualBuildables(false)
       ->withBuildablePHIDs(array($diff->getPHID()))
       ->execute();
     foreach ($buildables as $buildable) {
       $buildable->sendMessage(
         $this->getActor(),
         HarbormasterMessageType::BUILDABLE_CONTAINER,
         true);
     }
   }
 
   public function getColor() {
     return 'sky';
   }
 
   public function getIcon() {
     return 'fa-refresh';
   }
 
   public function getActionName() {
     if ($this->isCreateTransaction()) {
       return pht('Request');
     } else {
       return pht('Updated');
     }
   }
 
   public function getActionStrength() {
     return 2;
   }
 
   public function getTitle() {
     $old = $this->getOldValue();
     $new = $this->getNewValue();
 
     if ($this->isCommitUpdate()) {
       return pht(
         'This revision was automatically updated to reflect the '.
         'committed changes.');
     }
 
     // NOTE: Very, very old update transactions did not have a new value or
     // did not use a diff PHID as a new value. This was changed years ago,
     // but wasn't migrated. We might consider migrating if this causes issues.
 
     return pht(
       '%s updated this revision to %s.',
       $this->renderAuthor(),
       $this->renderNewHandle());
   }
 
   public function getTitleForFeed() {
     return pht(
       '%s updated the diff for %s.',
       $this->renderAuthor(),
       $this->renderObject());
   }
 
   public function validateTransactions($object, array $xactions) {
     $errors = array();
 
     $diff_phid = null;
     foreach ($xactions as $xaction) {
       $diff_phid = $xaction->getNewValue();
 
       $diff = id(new DifferentialDiffQuery())
         ->withPHIDs(array($diff_phid))
         ->setViewer($this->getActor())
         ->executeOne();
       if (!$diff) {
         $errors[] = $this->newInvalidError(
           pht(
             'Specified diff ("%s") does not exist.',
             $diff_phid),
           $xaction);
         continue;
       }
 
-      $is_attached = ($diff->getRevisionID() == $object->getID());
+      $is_attached =
+        ($diff->getRevisionID()) &&
+        ($diff->getRevisionID() == $object->getID());
       if ($is_attached) {
         $is_active = ($diff_phid == $object->getActiveDiffPHID());
       } else {
         $is_active = false;
       }
 
       if ($is_attached) {
         if ($is_active) {
           // This is a no-op: we're reattaching the current active diff to the
           // revision it is already attached to. This is valid and will just
           // be dropped later on in the process.
         } else {
           // At least for now, there's no support for "undoing" a diff and
           // reverting to an older proposed change without just creating a
           // new diff from whole cloth.
           $errors[] = $this->newInvalidError(
             pht(
               'You can not update this revision with the specified diff '.
               '("%s") because this diff is already attached to the revision '.
               'as an older version of the change.',
               $diff_phid),
             $xaction);
           continue;
         }
       } else if ($diff->getRevisionID()) {
         $errors[] = $this->newInvalidError(
           pht(
             'You can not update this revision with the specified diff ("%s") '.
             'because the diff is already attached to another revision.',
             $diff_phid),
           $xaction);
         continue;
       }
     }
 
     if (!$diff_phid && !$object->getActiveDiffPHID()) {
       $errors[] = $this->newInvalidError(
         pht(
           'You must specify an initial diff when creating a revision.'));
     }
 
     return $errors;
   }
 
   public function isCommitUpdate() {
     return (bool)$this->getMetadataValue('isCommitUpdate');
   }
 
   private function updateRevisionLineCounts(
     DifferentialRevision $revision,
     DifferentialDiff $diff) {
 
     $revision->setLineCount($diff->getLineCount());
 
     $conn = $revision->establishConnection('r');
 
     $row = queryfx_one(
       $conn,
       'SELECT SUM(addLines) A, SUM(delLines) D FROM %T
         WHERE diffID = %d',
       id(new DifferentialChangeset())->getTableName(),
       $diff->getID());
 
     if ($row) {
       $revision->setAddedLineCount((int)$row['A']);
       $revision->setRemovedLineCount((int)$row['D']);
     }
   }
 
   public function getTransactionTypeForConduit($xaction) {
     return 'update';
   }
 
   public function getFieldValuesForConduit($object, $data) {
     $commit_phids = $object->getMetadataValue('commitPHIDs', array());
 
     return array(
       'old' => $object->getOldValue(),
       'new' => $object->getNewValue(),
       'commitPHIDs' => $commit_phids,
     );
   }
 
 }
diff --git a/src/applications/owners/storage/PhabricatorOwnersPackage.php b/src/applications/owners/storage/PhabricatorOwnersPackage.php
index 56dd51a3c6..c76864702c 100644
--- a/src/applications/owners/storage/PhabricatorOwnersPackage.php
+++ b/src/applications/owners/storage/PhabricatorOwnersPackage.php
@@ -1,712 +1,715 @@
 <?php
 
 final class PhabricatorOwnersPackage
   extends PhabricatorOwnersDAO
   implements
     PhabricatorPolicyInterface,
     PhabricatorApplicationTransactionInterface,
     PhabricatorCustomFieldInterface,
     PhabricatorDestructibleInterface,
     PhabricatorConduitResultInterface,
     PhabricatorFulltextInterface,
     PhabricatorFerretInterface,
     PhabricatorNgramsInterface {
 
   protected $name;
   protected $auditingEnabled;
   protected $autoReview;
   protected $description;
   protected $mailKey;
   protected $status;
   protected $viewPolicy;
   protected $editPolicy;
   protected $dominion;
 
   private $paths = self::ATTACHABLE;
   private $owners = self::ATTACHABLE;
   private $customFields = self::ATTACHABLE;
   private $pathRepositoryMap = array();
 
   const STATUS_ACTIVE = 'active';
   const STATUS_ARCHIVED = 'archived';
 
   const AUTOREVIEW_NONE = 'none';
   const AUTOREVIEW_SUBSCRIBE = 'subscribe';
   const AUTOREVIEW_SUBSCRIBE_ALWAYS = 'subscribe-always';
   const AUTOREVIEW_REVIEW = 'review';
   const AUTOREVIEW_REVIEW_ALWAYS = 'review-always';
   const AUTOREVIEW_BLOCK = 'block';
   const AUTOREVIEW_BLOCK_ALWAYS = 'block-always';
 
   const DOMINION_STRONG = 'strong';
   const DOMINION_WEAK = 'weak';
 
   public static function initializeNewPackage(PhabricatorUser $actor) {
     $app = id(new PhabricatorApplicationQuery())
       ->setViewer($actor)
       ->withClasses(array('PhabricatorOwnersApplication'))
       ->executeOne();
 
     $view_policy = $app->getPolicy(
       PhabricatorOwnersDefaultViewCapability::CAPABILITY);
     $edit_policy = $app->getPolicy(
       PhabricatorOwnersDefaultEditCapability::CAPABILITY);
 
     return id(new PhabricatorOwnersPackage())
       ->setAuditingEnabled(0)
       ->setAutoReview(self::AUTOREVIEW_NONE)
       ->setDominion(self::DOMINION_STRONG)
       ->setViewPolicy($view_policy)
       ->setEditPolicy($edit_policy)
       ->attachPaths(array())
       ->setStatus(self::STATUS_ACTIVE)
       ->attachOwners(array())
       ->setDescription('');
   }
 
   public static function getStatusNameMap() {
     return array(
       self::STATUS_ACTIVE => pht('Active'),
       self::STATUS_ARCHIVED => pht('Archived'),
     );
   }
 
   public static function getAutoreviewOptionsMap() {
     return array(
       self::AUTOREVIEW_NONE => array(
         'name' => pht('No Autoreview'),
       ),
       self::AUTOREVIEW_REVIEW => array(
         'name' => pht('Review Changes With Non-Owner Author'),
         'authority' => true,
       ),
       self::AUTOREVIEW_BLOCK => array(
         'name' => pht('Review Changes With Non-Owner Author (Blocking)'),
         'authority' => true,
       ),
       self::AUTOREVIEW_SUBSCRIBE => array(
         'name' => pht('Subscribe to Changes With Non-Owner Author'),
         'authority' => true,
       ),
       self::AUTOREVIEW_REVIEW_ALWAYS => array(
         'name' => pht('Review All Changes'),
       ),
       self::AUTOREVIEW_BLOCK_ALWAYS => array(
         'name' => pht('Review All Changes (Blocking)'),
       ),
       self::AUTOREVIEW_SUBSCRIBE_ALWAYS => array(
         'name' => pht('Subscribe to All Changes'),
       ),
     );
   }
 
   public static function getDominionOptionsMap() {
     return array(
       self::DOMINION_STRONG => array(
         'name' => pht('Strong (Control All Paths)'),
         'short' => pht('Strong'),
       ),
       self::DOMINION_WEAK => array(
         'name' => pht('Weak (Control Unowned Paths)'),
         'short' => pht('Weak'),
       ),
     );
   }
 
   protected function getConfiguration() {
     return array(
       // This information is better available from the history table.
       self::CONFIG_TIMESTAMPS => false,
       self::CONFIG_AUX_PHID => true,
       self::CONFIG_COLUMN_SCHEMA => array(
         'name' => 'sort',
         'description' => 'text',
         'auditingEnabled' => 'bool',
         'mailKey' => 'bytes20',
         'status' => 'text32',
         'autoReview' => 'text32',
         'dominion' => 'text32',
       ),
     ) + parent::getConfiguration();
   }
 
   public function generatePHID() {
     return PhabricatorPHID::generateNewPHID(
       PhabricatorOwnersPackagePHIDType::TYPECONST);
   }
 
   public function save() {
     if (!$this->getMailKey()) {
       $this->setMailKey(Filesystem::readRandomCharacters(20));
     }
 
     return parent::save();
   }
 
   public function isArchived() {
     return ($this->getStatus() == self::STATUS_ARCHIVED);
   }
 
   public function setName($name) {
     $this->name = $name;
     return $this;
   }
 
   public function loadOwners() {
     if (!$this->getID()) {
       return array();
     }
     return id(new PhabricatorOwnersOwner())->loadAllWhere(
       'packageID = %d',
       $this->getID());
   }
 
   public function loadPaths() {
     if (!$this->getID()) {
       return array();
     }
     return id(new PhabricatorOwnersPath())->loadAllWhere(
       'packageID = %d',
       $this->getID());
   }
 
   public static function loadAffectedPackages(
     PhabricatorRepository $repository,
     array $paths) {
 
     if (!$paths) {
       return array();
     }
 
     return self::loadPackagesForPaths($repository, $paths);
   }
 
   public static function loadOwningPackages($repository, $path) {
     if (empty($path)) {
       return array();
     }
 
     return self::loadPackagesForPaths($repository, array($path), 1);
   }
 
   private static function loadPackagesForPaths(
     PhabricatorRepository $repository,
     array $paths,
     $limit = 0) {
 
     $fragments = array();
     foreach ($paths as $path) {
       foreach (self::splitPath($path) as $fragment) {
         $fragments[$fragment][$path] = true;
       }
     }
 
     $package = new PhabricatorOwnersPackage();
     $path = new PhabricatorOwnersPath();
     $conn = $package->establishConnection('r');
 
     $repository_clause = qsprintf(
       $conn,
       'AND p.repositoryPHID = %s',
       $repository->getPHID());
 
     // NOTE: The list of $paths may be very large if we're coming from
     // the OwnersWorker and processing, e.g., an SVN commit which created a new
     // branch. Break it apart so that it will fit within 'max_allowed_packet',
     // and then merge results in PHP.
 
     $rows = array();
     foreach (array_chunk(array_keys($fragments), 1024) as $chunk) {
       $indexes = array();
       foreach ($chunk as $fragment) {
         $indexes[] = PhabricatorHash::digestForIndex($fragment);
       }
 
       $rows[] = queryfx_all(
         $conn,
         'SELECT pkg.id, pkg.dominion, p.excluded, p.path
           FROM %T pkg JOIN %T p ON p.packageID = pkg.id
           WHERE p.pathIndex IN (%Ls) AND pkg.status IN (%Ls) %Q',
         $package->getTableName(),
         $path->getTableName(),
         $indexes,
         array(
           self::STATUS_ACTIVE,
         ),
         $repository_clause);
     }
     $rows = array_mergev($rows);
 
     $ids = self::findLongestPathsPerPackage($rows, $fragments);
 
     if (!$ids) {
       return array();
     }
 
     arsort($ids);
     if ($limit) {
       $ids = array_slice($ids, 0, $limit, $preserve_keys = true);
     }
     $ids = array_keys($ids);
 
     $packages = $package->loadAllWhere('id in (%Ld)', $ids);
     $packages = array_select_keys($packages, $ids);
 
     return $packages;
   }
 
   public static function loadPackagesForRepository($repository) {
     $package = new PhabricatorOwnersPackage();
     $ids = ipull(
       queryfx_all(
         $package->establishConnection('r'),
         'SELECT DISTINCT packageID FROM %T WHERE repositoryPHID = %s',
         id(new PhabricatorOwnersPath())->getTableName(),
         $repository->getPHID()),
       'packageID');
 
     return $package->loadAllWhere('id in (%Ld)', $ids);
   }
 
   public static function findLongestPathsPerPackage(array $rows, array $paths) {
 
     // Build a map from each path to all the package paths which match it.
     $path_hits = array();
     $weak = array();
     foreach ($rows as $row) {
       $id = $row['id'];
       $path = $row['path'];
       $length = strlen($path);
       $excluded = $row['excluded'];
 
       if ($row['dominion'] === self::DOMINION_WEAK) {
         $weak[$id] = true;
       }
 
       $matches = $paths[$path];
       foreach ($matches as $match => $ignored) {
         $path_hits[$match][] = array(
           'id' => $id,
           'excluded' => $excluded,
           'length' => $length,
         );
       }
     }
 
     // For each path, process the matching package paths to figure out which
     // packages actually own it.
     $path_packages = array();
     foreach ($path_hits as $match => $hits) {
       $hits = isort($hits, 'length');
 
       $packages = array();
       foreach ($hits as $hit) {
         $package_id = $hit['id'];
         if ($hit['excluded']) {
           unset($packages[$package_id]);
         } else {
           $packages[$package_id] = $hit;
         }
       }
 
       $path_packages[$match] = $packages;
     }
 
     // Remove packages with weak dominion rules that should cede control to
     // a more specific package.
     if ($weak) {
       foreach ($path_packages as $match => $packages) {
 
         // Group packages by length.
         $length_map = array();
         foreach ($packages as $package_id => $package) {
           $length_map[$package['length']][$package_id] = $package;
         }
 
         // For each path length, remove all weak packages if there are any
         // strong packages of the same length. This makes sure that if there
         // are one or more strong claims on a particular path, only those
         // claims stand.
         foreach ($length_map as $package_list) {
           $any_strong = false;
           foreach ($package_list as $package_id => $package) {
             if (!isset($weak[$package_id])) {
               $any_strong = true;
               break;
             }
           }
 
           if ($any_strong) {
             foreach ($package_list as $package_id => $package) {
               if (isset($weak[$package_id])) {
                 unset($packages[$package_id]);
               }
             }
           }
         }
 
         $packages = isort($packages, 'length');
         $packages = array_reverse($packages, true);
 
         $best_length = null;
         foreach ($packages as $package_id => $package) {
           // If this is the first package we've encountered, note its length.
           // We're iterating over the packages from longest to shortest match,
           // so packages of this length always have the best claim on the path.
           if ($best_length === null) {
             $best_length = $package['length'];
           }
 
           // If this package has the same length as the best length, its claim
           // stands.
           if ($package['length'] === $best_length) {
             continue;
           }
 
           // If this is a weak package and does not have the best length,
           // cede its claim to the stronger package.
           if (isset($weak[$package_id])) {
             unset($packages[$package_id]);
           }
         }
 
         $path_packages[$match] = $packages;
       }
     }
 
     // For each package that owns at least one path, identify the longest
     // path it owns.
     $package_lengths = array();
     foreach ($path_packages as $match => $hits) {
       foreach ($hits as $hit) {
         $length = $hit['length'];
         $id = $hit['id'];
         if (empty($package_lengths[$id])) {
           $package_lengths[$id] = $length;
         } else {
           $package_lengths[$id] = max($package_lengths[$id], $length);
         }
       }
     }
 
     return $package_lengths;
   }
 
   public static function splitPath($path) {
-    $trailing_slash = preg_match('@/$@', $path) ? '/' : '';
-    $path = trim($path, '/');
+    $result = array(
+      '/',
+    );
+
     $parts = explode('/', $path);
+    $buffer = '/';
+    foreach ($parts as $part) {
+      if (!strlen($part)) {
+        continue;
+      }
 
-    $result = array();
-    while (count($parts)) {
-      $result[] = '/'.implode('/', $parts).$trailing_slash;
-      $trailing_slash = '/';
-      array_pop($parts);
+      $buffer = $buffer.$part.'/';
+      $result[] = $buffer;
     }
-    $result[] = '/';
 
-    return array_reverse($result);
+    return $result;
   }
 
   public function attachPaths(array $paths) {
     assert_instances_of($paths, 'PhabricatorOwnersPath');
     $this->paths = $paths;
 
     // Drop this cache if we're attaching new paths.
     $this->pathRepositoryMap = array();
 
     return $this;
   }
 
   public function getPaths() {
     return $this->assertAttached($this->paths);
   }
 
   public function getPathsForRepository($repository_phid) {
     if (isset($this->pathRepositoryMap[$repository_phid])) {
       return $this->pathRepositoryMap[$repository_phid];
     }
 
     $map = array();
     foreach ($this->getPaths() as $path) {
       if ($path->getRepositoryPHID() == $repository_phid) {
         $map[] = $path;
       }
     }
 
     $this->pathRepositoryMap[$repository_phid] = $map;
 
     return $this->pathRepositoryMap[$repository_phid];
   }
 
   public function attachOwners(array $owners) {
     assert_instances_of($owners, 'PhabricatorOwnersOwner');
     $this->owners = $owners;
     return $this;
   }
 
   public function getOwners() {
     return $this->assertAttached($this->owners);
   }
 
   public function getOwnerPHIDs() {
     return mpull($this->getOwners(), 'getUserPHID');
   }
 
   public function isOwnerPHID($phid) {
     if (!$phid) {
       return false;
     }
 
     $owner_phids = $this->getOwnerPHIDs();
     $owner_phids = array_fuse($owner_phids);
 
     return isset($owner_phids[$phid]);
   }
 
   public function getMonogram() {
     return 'O'.$this->getID();
   }
 
   public function getURI() {
     // TODO: Move these to "/O123" for consistency.
     return '/owners/package/'.$this->getID().'/';
   }
 
 /* -(  PhabricatorPolicyInterface  )----------------------------------------- */
 
 
   public function getCapabilities() {
     return array(
       PhabricatorPolicyCapability::CAN_VIEW,
       PhabricatorPolicyCapability::CAN_EDIT,
     );
   }
 
   public function getPolicy($capability) {
     switch ($capability) {
       case PhabricatorPolicyCapability::CAN_VIEW:
         return $this->getViewPolicy();
       case PhabricatorPolicyCapability::CAN_EDIT:
         return $this->getEditPolicy();
     }
   }
 
   public function hasAutomaticCapability($capability, PhabricatorUser $viewer) {
     switch ($capability) {
       case PhabricatorPolicyCapability::CAN_VIEW:
         if ($this->isOwnerPHID($viewer->getPHID())) {
           return true;
         }
         break;
     }
 
     return false;
   }
 
   public function describeAutomaticCapability($capability) {
     return pht('Owners of a package may always view it.');
   }
 
 
 /* -(  PhabricatorApplicationTransactionInterface  )------------------------- */
 
 
   public function getApplicationTransactionEditor() {
     return new PhabricatorOwnersPackageTransactionEditor();
   }
 
   public function getApplicationTransactionObject() {
     return $this;
   }
 
   public function getApplicationTransactionTemplate() {
     return new PhabricatorOwnersPackageTransaction();
   }
 
   public function willRenderTimeline(
     PhabricatorApplicationTransactionView $timeline,
     AphrontRequest $request) {
     return $timeline;
   }
 
 
 /* -(  PhabricatorCustomFieldInterface  )------------------------------------ */
 
 
   public function getCustomFieldSpecificationForRole($role) {
     return PhabricatorEnv::getEnvConfig('owners.fields');
   }
 
   public function getCustomFieldBaseClass() {
     return 'PhabricatorOwnersCustomField';
   }
 
   public function getCustomFields() {
     return $this->assertAttached($this->customFields);
   }
 
   public function attachCustomFields(PhabricatorCustomFieldAttachment $fields) {
     $this->customFields = $fields;
     return $this;
   }
 
 
 /* -(  PhabricatorDestructibleInterface  )----------------------------------- */
 
 
   public function destroyObjectPermanently(
     PhabricatorDestructionEngine $engine) {
 
     $this->openTransaction();
       $conn_w = $this->establishConnection('w');
 
       queryfx(
         $conn_w,
         'DELETE FROM %T WHERE packageID = %d',
         id(new PhabricatorOwnersPath())->getTableName(),
         $this->getID());
 
       queryfx(
         $conn_w,
         'DELETE FROM %T WHERE packageID = %d',
         id(new PhabricatorOwnersOwner())->getTableName(),
         $this->getID());
 
       $this->delete();
     $this->saveTransaction();
   }
 
 
 /* -(  PhabricatorConduitResultInterface  )---------------------------------- */
 
 
   public function getFieldSpecificationsForConduit() {
     return array(
       id(new PhabricatorConduitSearchFieldSpecification())
         ->setKey('name')
         ->setType('string')
         ->setDescription(pht('The name of the package.')),
       id(new PhabricatorConduitSearchFieldSpecification())
         ->setKey('description')
         ->setType('string')
         ->setDescription(pht('The package description.')),
       id(new PhabricatorConduitSearchFieldSpecification())
         ->setKey('status')
         ->setType('string')
         ->setDescription(pht('Active or archived status of the package.')),
       id(new PhabricatorConduitSearchFieldSpecification())
         ->setKey('owners')
         ->setType('list<map<string, wild>>')
         ->setDescription(pht('List of package owners.')),
       id(new PhabricatorConduitSearchFieldSpecification())
         ->setKey('review')
         ->setType('map<string, wild>')
         ->setDescription(pht('Auto review information.')),
       id(new PhabricatorConduitSearchFieldSpecification())
         ->setKey('audit')
         ->setType('map<string, wild>')
         ->setDescription(pht('Auto audit information.')),
       id(new PhabricatorConduitSearchFieldSpecification())
         ->setKey('dominion')
         ->setType('map<string, wild>')
         ->setDescription(pht('Dominion setting information.')),
     );
   }
 
   public function getFieldValuesForConduit() {
     $owner_list = array();
     foreach ($this->getOwners() as $owner) {
       $owner_list[] = array(
         'ownerPHID' => $owner->getUserPHID(),
       );
     }
 
     $review_map = self::getAutoreviewOptionsMap();
     $review_value = $this->getAutoReview();
     if (isset($review_map[$review_value])) {
       $review_label = $review_map[$review_value]['name'];
     } else {
       $review_label = pht('Unknown ("%s")', $review_value);
     }
 
     $review = array(
       'value' => $review_value,
       'label' => $review_label,
     );
 
     if ($this->getAuditingEnabled()) {
       $audit_value = 'audit';
       $audit_label = pht('Auditing Enabled');
     } else {
       $audit_value = 'none';
       $audit_label = pht('No Auditing');
     }
 
     $audit = array(
       'value' => $audit_value,
       'label' => $audit_label,
     );
 
     $dominion_value = $this->getDominion();
     $dominion_map = self::getDominionOptionsMap();
     if (isset($dominion_map[$dominion_value])) {
       $dominion_label = $dominion_map[$dominion_value]['name'];
       $dominion_short = $dominion_map[$dominion_value]['short'];
     } else {
       $dominion_label = pht('Unknown ("%s")', $dominion_value);
       $dominion_short = pht('Unknown ("%s")', $dominion_value);
     }
 
     $dominion = array(
       'value' => $dominion_value,
       'label' => $dominion_label,
       'short' => $dominion_short,
     );
 
     return array(
       'name' => $this->getName(),
       'description' => $this->getDescription(),
       'status' => $this->getStatus(),
       'owners' => $owner_list,
       'review' => $review,
       'audit' => $audit,
       'dominion' => $dominion,
     );
   }
 
   public function getConduitSearchAttachments() {
     return array(
       id(new PhabricatorOwnersPathsSearchEngineAttachment())
         ->setAttachmentKey('paths'),
     );
   }
 
 
 /* -(  PhabricatorFulltextInterface  )--------------------------------------- */
 
 
   public function newFulltextEngine() {
     return new PhabricatorOwnersPackageFulltextEngine();
   }
 
 
 /* -(  PhabricatorFerretInterface  )----------------------------------------- */
 
 
   public function newFerretEngine() {
     return new PhabricatorOwnersPackageFerretEngine();
   }
 
 
 /* -(  PhabricatorNgramsInterface  )----------------------------------------- */
 
 
   public function newNgrams() {
     return array(
       id(new PhabricatorOwnersPackageNameNgrams())
         ->setValue($this->getName()),
     );
   }
 
 }