diff --git a/resources/celerity/map.php b/resources/celerity/map.php
index cd0efd5db..3e622a32b 100644
--- a/resources/celerity/map.php
+++ b/resources/celerity/map.php
@@ -1,2370 +1,2383 @@
 <?php
 
 /**
  * This file is automatically generated. Use 'bin/celerity map' to rebuild it.
  *
  * @generated
  */
 return array(
   'names' => array(
-    'core.pkg.css' => 'b729f9f5',
+    'core.pkg.css' => 'b7b8d101',
     'core.pkg.js' => '6972d365',
     'darkconsole.pkg.js' => 'e7393ebb',
     'differential.pkg.css' => '7ba78475',
     'differential.pkg.js' => 'd0cd0df6',
-    'diffusion.pkg.css' => 'dc8e0cc2',
+    'diffusion.pkg.css' => '91c5d3a6',
     'diffusion.pkg.js' => '3a9a8bfa',
     'maniphest.pkg.css' => '4845691a',
     'maniphest.pkg.js' => '949a7498',
     'rsrc/css/aphront/aphront-bars.css' => '231ac33c',
     'rsrc/css/aphront/dark-console.css' => 'f54bf286',
     'rsrc/css/aphront/dialog-view.css' => 'b4334e08',
     'rsrc/css/aphront/lightbox-attachment.css' => '7acac05d',
     'rsrc/css/aphront/list-filter-view.css' => '5d6f0526',
     'rsrc/css/aphront/multi-column.css' => 'fd18389d',
     'rsrc/css/aphront/notification.css' => '3f6c89c9',
     'rsrc/css/aphront/panel-view.css' => '8427b78d',
     'rsrc/css/aphront/phabricator-nav-view.css' => 'ac79a758',
     'rsrc/css/aphront/table-view.css' => '9258e19f',
     'rsrc/css/aphront/tokenizer.css' => '056da01b',
     'rsrc/css/aphront/tooltip.css' => '1a07aea8',
     'rsrc/css/aphront/typeahead-browse.css' => 'd8581d2c',
     'rsrc/css/aphront/typeahead.css' => 'd4f16145',
     'rsrc/css/application/almanac/almanac.css' => 'dbb9b3af',
     'rsrc/css/application/auth/auth.css' => '0877ed6e',
     'rsrc/css/application/base/main-menu-view.css' => 'd00a795a',
     'rsrc/css/application/base/notification-menu.css' => 'f31c0bde',
     'rsrc/css/application/base/phabricator-application-launch-view.css' => '95351601',
     'rsrc/css/application/base/phui-theme.css' => '027ba77e',
     'rsrc/css/application/base/standard-page-view.css' => 'e709f6d0',
     'rsrc/css/application/chatlog/chatlog.css' => 'd295b020',
     'rsrc/css/application/conduit/conduit-api.css' => '7bc725c4',
     'rsrc/css/application/config/config-options.css' => '0ede4c9b',
     'rsrc/css/application/config/config-template.css' => '8e6c6fcd',
     'rsrc/css/application/config/config-welcome.css' => '6abd79be',
     'rsrc/css/application/config/setup-issue.css' => 'db7e9c40',
     'rsrc/css/application/config/unhandled-exception.css' => '4c96257a',
     'rsrc/css/application/conpherence/durable-column.css' => '86396117',
     'rsrc/css/application/conpherence/menu.css' => 'f99fee4c',
     'rsrc/css/application/conpherence/message-pane.css' => '5897d3ac',
     'rsrc/css/application/conpherence/notification.css' => '6cdcc253',
     'rsrc/css/application/conpherence/transaction.css' => '85d0974c',
     'rsrc/css/application/conpherence/update.css' => 'faf6be09',
     'rsrc/css/application/conpherence/widget-pane.css' => '775eaaba',
     'rsrc/css/application/contentsource/content-source-view.css' => '4b8b05d4',
-    'rsrc/css/application/countdown/timer.css' => '96696f21',
+    'rsrc/css/application/countdown/timer.css' => '16c52f5c',
     'rsrc/css/application/daemon/bulk-job.css' => 'df9c1d4a',
     'rsrc/css/application/dashboard/dashboard.css' => 'bc6f2127',
     'rsrc/css/application/diff/inline-comment-summary.css' => '51efda3a',
     'rsrc/css/application/differential/add-comment.css' => 'c47f8c40',
     'rsrc/css/application/differential/changeset-view.css' => '3e3b0b76',
     'rsrc/css/application/differential/core.css' => '5b7b8ff4',
     'rsrc/css/application/differential/phui-inline-comment.css' => '5953c28e',
     'rsrc/css/application/differential/revision-comment.css' => '14b8565a',
     'rsrc/css/application/differential/revision-history.css' => '0e8eb855',
     'rsrc/css/application/differential/revision-list.css' => 'f3c47d33',
     'rsrc/css/application/differential/table-of-contents.css' => 'ae4b7a55',
-    'rsrc/css/application/diffusion/diffusion-icons.css' => '3311444d',
+    'rsrc/css/application/diffusion/diffusion-icons.css' => 'd678600a',
     'rsrc/css/application/diffusion/diffusion-readme.css' => '297373eb',
     'rsrc/css/application/diffusion/diffusion-source.css' => '68b30fd3',
     'rsrc/css/application/feed/feed.css' => 'ecd4ec57',
     'rsrc/css/application/files/global-drag-and-drop.css' => '5c1b47c2',
     'rsrc/css/application/flag/flag.css' => '5337623f',
     'rsrc/css/application/harbormaster/harbormaster.css' => 'f491c9f4',
     'rsrc/css/application/herald/herald-test.css' => 'a52e323e',
     'rsrc/css/application/herald/herald.css' => 'dc31f6e9',
     'rsrc/css/application/maniphest/batch-editor.css' => 'b0f0b6d5',
     'rsrc/css/application/maniphest/report.css' => '9b9580b7',
     'rsrc/css/application/maniphest/task-edit.css' => 'fda62a9b',
     'rsrc/css/application/maniphest/task-summary.css' => '11cc5344',
     'rsrc/css/application/objectselector/object-selector.css' => '85ee8ce6',
     'rsrc/css/application/owners/owners-path-editor.css' => '2f00933b',
     'rsrc/css/application/paste/paste.css' => '1898e534',
     'rsrc/css/application/people/people-profile.css' => '2473d929',
     'rsrc/css/application/phame/phame.css' => '737792d6',
     'rsrc/css/application/pholio/pholio-edit.css' => '3ad9d1ee',
     'rsrc/css/application/pholio/pholio-inline-comments.css' => '8e545e49',
     'rsrc/css/application/pholio/pholio.css' => 'ca89d380',
     'rsrc/css/application/phortune/phortune-credit-card-form.css' => '8391eb02',
     'rsrc/css/application/phortune/phortune.css' => '9149f103',
     'rsrc/css/application/phrequent/phrequent.css' => 'ffc185ad',
     'rsrc/css/application/phriction/phriction-document-css.css' => '4282e4ad',
     'rsrc/css/application/policy/policy-edit.css' => '815c66f7',
     'rsrc/css/application/policy/policy-transaction-detail.css' => '82100a43',
     'rsrc/css/application/policy/policy.css' => '957ea14c',
     'rsrc/css/application/ponder/ponder-view.css' => 'fbd45f96',
     'rsrc/css/application/project/project-card-view.css' => '9418c97d',
     'rsrc/css/application/project/project-view.css' => 'cbaa10a1',
     'rsrc/css/application/releeph/releeph-core.css' => '9b3c5733',
     'rsrc/css/application/releeph/releeph-preview-branch.css' => 'b7a6f4a5',
     'rsrc/css/application/releeph/releeph-request-differential-create-dialog.css' => '8d8b92cd',
     'rsrc/css/application/releeph/releeph-request-typeahead.css' => '667a48ae',
     'rsrc/css/application/search/search-results.css' => '7dea472c',
     'rsrc/css/application/slowvote/slowvote.css' => 'a94b7230',
     'rsrc/css/application/tokens/tokens.css' => '3d0f239e',
     'rsrc/css/application/uiexample/example.css' => '528b19de',
     'rsrc/css/core/core.css' => 'd0801452',
-    'rsrc/css/core/remarkup.css' => '6aae5360',
-    'rsrc/css/core/syntax.css' => '9fd11da8',
+    'rsrc/css/core/remarkup.css' => '787105d6',
+    'rsrc/css/core/syntax.css' => '5101175d',
     'rsrc/css/core/z-index.css' => '5b6fcf3f',
     'rsrc/css/diviner/diviner-shared.css' => 'aa3656aa',
     'rsrc/css/font/font-aleo.css' => '8bdb2835',
     'rsrc/css/font/font-awesome.css' => '2b7ebbcc',
     'rsrc/css/font/font-lato.css' => 'c7ccd872',
     'rsrc/css/font/phui-font-icon-base.css' => '6449bce8',
     'rsrc/css/layout/phabricator-filetree-view.css' => 'fccf9f82',
-    'rsrc/css/layout/phabricator-side-menu-view.css' => '3a3d9f41',
+    'rsrc/css/layout/phabricator-side-menu-view.css' => 'dd849797',
     'rsrc/css/layout/phabricator-source-code-view.css' => 'cbeef983',
     'rsrc/css/phui/calendar/phui-calendar-day.css' => 'd1cf6f93',
     'rsrc/css/phui/calendar/phui-calendar-list.css' => 'c1c7f338',
     'rsrc/css/phui/calendar/phui-calendar-month.css' => '476be7e0',
     'rsrc/css/phui/calendar/phui-calendar.css' => 'ccabe893',
     'rsrc/css/phui/phui-action-list.css' => 'c5eba19d',
     'rsrc/css/phui/phui-action-panel.css' => '91c7b835',
     'rsrc/css/phui/phui-badge.css' => '3baef8db',
     'rsrc/css/phui/phui-big-info-view.css' => 'bd903741',
-    'rsrc/css/phui/phui-box.css' => 'd909ea3d',
+    'rsrc/css/phui/phui-box.css' => '5c8387cf',
     'rsrc/css/phui/phui-button.css' => 'a64a8de6',
     'rsrc/css/phui/phui-chart.css' => '6bf6f78e',
     'rsrc/css/phui/phui-crumbs-view.css' => '1a1265d4',
     'rsrc/css/phui/phui-curtain-view.css' => '7148ae25',
-    'rsrc/css/phui/phui-document-pro.css' => '73e45fd2',
+    'rsrc/css/phui/phui-document-pro.css' => '8419560b',
     'rsrc/css/phui/phui-document-summary.css' => '9ca48bdf',
-    'rsrc/css/phui/phui-document.css' => '9c71d2bf',
-    'rsrc/css/phui/phui-feed-story.css' => 'd8440402',
+    'rsrc/css/phui/phui-document.css' => '715aedfb',
+    'rsrc/css/phui/phui-feed-story.css' => 'aa49845d',
     'rsrc/css/phui/phui-fontkit.css' => '9cda225e',
     'rsrc/css/phui/phui-form-view.css' => '6a51768e',
     'rsrc/css/phui/phui-form.css' => 'aac1d51d',
     'rsrc/css/phui/phui-head-thing.css' => 'fd311e5f',
     'rsrc/css/phui/phui-header-view.css' => '4c7dd8f5',
     'rsrc/css/phui/phui-hovercard.css' => 'de1a2119',
     'rsrc/css/phui/phui-icon-set-selector.css' => '1ab67aad',
     'rsrc/css/phui/phui-icon.css' => '3f33ab57',
     'rsrc/css/phui/phui-image-mask.css' => 'a8498f9c',
     'rsrc/css/phui/phui-info-panel.css' => '27ea50a1',
     'rsrc/css/phui/phui-info-view.css' => '28efab79',
     'rsrc/css/phui/phui-list.css' => '9da2aa00',
     'rsrc/css/phui/phui-object-box.css' => '6b487c57',
     'rsrc/css/phui/phui-object-item-list-view.css' => '8d99e42b',
     'rsrc/css/phui/phui-pager.css' => 'bea33d23',
     'rsrc/css/phui/phui-pinboard-view.css' => '2495140e',
     'rsrc/css/phui/phui-profile-menu.css' => 'c8557f33',
     'rsrc/css/phui/phui-property-list-view.css' => '1d42ee7c',
     'rsrc/css/phui/phui-remarkup-preview.css' => '1a8f2591',
     'rsrc/css/phui/phui-segment-bar-view.css' => '46342871',
     'rsrc/css/phui/phui-spacing.css' => '042804d6',
-    'rsrc/css/phui/phui-status.css' => '37309046',
+    'rsrc/css/phui/phui-status.css' => 'd5263e49',
     'rsrc/css/phui/phui-tag-view.css' => '6bbd83e2',
     'rsrc/css/phui/phui-timeline-view.css' => '6e342216',
     'rsrc/css/phui/phui-two-column-view.css' => 'b9538af1',
     'rsrc/css/phui/workboards/phui-workboard-color.css' => 'ac6fe6a7',
     'rsrc/css/phui/workboards/phui-workboard.css' => 'e6d89647',
     'rsrc/css/phui/workboards/phui-workcard.css' => '3646fb96',
     'rsrc/css/phui/workboards/phui-workpanel.css' => '92197373',
     'rsrc/css/sprite-login.css' => '60e8560e',
     'rsrc/css/sprite-menu.css' => '9dd65b92',
     'rsrc/css/sprite-tokens.css' => '4f399012',
+    'rsrc/css/syntax/syntax-default.css' => '9923583c',
     'rsrc/externals/d3/d3.min.js' => 'a11a5ff2',
     'rsrc/externals/font/aleo/aleo-bold.eot' => 'd3d3bed7',
     'rsrc/externals/font/aleo/aleo-bold.svg' => '45899c8e',
     'rsrc/externals/font/aleo/aleo-bold.ttf' => '4b08bef0',
     'rsrc/externals/font/aleo/aleo-bold.woff' => '93b513a1',
     'rsrc/externals/font/aleo/aleo-bold.woff2' => '75fbf322',
     'rsrc/externals/font/aleo/aleo-regular.eot' => 'a4e29e2f',
     'rsrc/externals/font/aleo/aleo-regular.svg' => '42a86f7a',
     'rsrc/externals/font/aleo/aleo-regular.ttf' => '751e7479',
     'rsrc/externals/font/aleo/aleo-regular.woff' => 'c3744be9',
     'rsrc/externals/font/aleo/aleo-regular.woff2' => '851aa0ee',
     'rsrc/externals/font/fontawesome/fontawesome-webfont.eot' => '59b3076c',
     'rsrc/externals/font/fontawesome/fontawesome-webfont.ttf' => '45ad7e57',
     'rsrc/externals/font/fontawesome/fontawesome-webfont.woff' => 'f861e2a8',
     'rsrc/externals/font/fontawesome/fontawesome-webfont.woff2' => '0ee0f078',
     'rsrc/externals/font/lato/lato-bold.eot' => '99fbcf8c',
     'rsrc/externals/font/lato/lato-bold.svg' => '2aa83045',
     'rsrc/externals/font/lato/lato-bold.ttf' => '0a7141f7',
     'rsrc/externals/font/lato/lato-bold.woff' => 'f5db2061',
     'rsrc/externals/font/lato/lato-bold.woff2' => '37a94ecd',
     'rsrc/externals/font/lato/lato-bolditalic.eot' => 'b93389d0',
     'rsrc/externals/font/lato/lato-bolditalic.svg' => '5442e1ef',
     'rsrc/externals/font/lato/lato-bolditalic.ttf' => 'dad31252',
     'rsrc/externals/font/lato/lato-bolditalic.woff' => 'e53bcf47',
     'rsrc/externals/font/lato/lato-bolditalic.woff2' => 'd035007f',
     'rsrc/externals/font/lato/lato-italic.eot' => '6a903f5d',
     'rsrc/externals/font/lato/lato-italic.svg' => '0dc7cf2f',
     'rsrc/externals/font/lato/lato-italic.ttf' => '629f64f0',
     'rsrc/externals/font/lato/lato-italic.woff' => '678dc4bb',
     'rsrc/externals/font/lato/lato-italic.woff2' => '7c8dd650',
     'rsrc/externals/font/lato/lato-regular.eot' => '848dfb1e',
     'rsrc/externals/font/lato/lato-regular.svg' => 'cbd5fd6b',
     'rsrc/externals/font/lato/lato-regular.ttf' => 'e270165b',
     'rsrc/externals/font/lato/lato-regular.woff' => '13d39fe2',
     'rsrc/externals/font/lato/lato-regular.woff2' => '57a9f742',
     'rsrc/externals/javelin/core/Event.js' => '2ee659ce',
     'rsrc/externals/javelin/core/Stratcom.js' => '6ad39b6f',
     'rsrc/externals/javelin/core/__tests__/event-stop-and-kill.js' => '717554e4',
     'rsrc/externals/javelin/core/__tests__/install.js' => 'c432ee85',
     'rsrc/externals/javelin/core/__tests__/stratcom.js' => '88bf7313',
     'rsrc/externals/javelin/core/__tests__/util.js' => 'e251703d',
     'rsrc/externals/javelin/core/init.js' => '3010e992',
     'rsrc/externals/javelin/core/init_node.js' => 'c234aded',
     'rsrc/externals/javelin/core/install.js' => '05270951',
     'rsrc/externals/javelin/core/util.js' => '93cc50d6',
     'rsrc/externals/javelin/docs/Base.js' => '74676256',
     'rsrc/externals/javelin/docs/onload.js' => 'e819c479',
     'rsrc/externals/javelin/ext/fx/Color.js' => '7e41274a',
     'rsrc/externals/javelin/ext/fx/FX.js' => '54b612ba',
     'rsrc/externals/javelin/ext/reactor/core/DynVal.js' => 'f6555212',
     'rsrc/externals/javelin/ext/reactor/core/Reactor.js' => '2b8de964',
     'rsrc/externals/javelin/ext/reactor/core/ReactorNode.js' => '1ad0a787',
     'rsrc/externals/javelin/ext/reactor/core/ReactorNodeCalmer.js' => '76f4ebed',
     'rsrc/externals/javelin/ext/reactor/dom/RDOM.js' => 'c90a04fc',
     'rsrc/externals/javelin/ext/view/HTMLView.js' => 'fe287620',
     'rsrc/externals/javelin/ext/view/View.js' => '0f764c35',
     'rsrc/externals/javelin/ext/view/ViewInterpreter.js' => 'f829edb3',
     'rsrc/externals/javelin/ext/view/ViewPlaceholder.js' => '47830651',
     'rsrc/externals/javelin/ext/view/ViewRenderer.js' => '6c2b09a2',
     'rsrc/externals/javelin/ext/view/ViewVisitor.js' => 'efe49472',
     'rsrc/externals/javelin/ext/view/__tests__/HTMLView.js' => 'f92d7bcb',
     'rsrc/externals/javelin/ext/view/__tests__/View.js' => '6450b38b',
     'rsrc/externals/javelin/ext/view/__tests__/ViewInterpreter.js' => '7a94d6a5',
     'rsrc/externals/javelin/ext/view/__tests__/ViewRenderer.js' => '6ea96ac9',
     'rsrc/externals/javelin/lib/Cookie.js' => '62dfea03',
     'rsrc/externals/javelin/lib/DOM.js' => '805b806a',
     'rsrc/externals/javelin/lib/History.js' => 'd4505101',
     'rsrc/externals/javelin/lib/JSON.js' => '69adf288',
     'rsrc/externals/javelin/lib/Leader.js' => 'fea0eb47',
     'rsrc/externals/javelin/lib/Mask.js' => '8a41885b',
     'rsrc/externals/javelin/lib/Quicksand.js' => '6b8ef10b',
     'rsrc/externals/javelin/lib/Request.js' => '94b750d2',
     'rsrc/externals/javelin/lib/Resource.js' => '44959b73',
     'rsrc/externals/javelin/lib/Routable.js' => 'b3e7d692',
     'rsrc/externals/javelin/lib/Router.js' => '29274e2b',
     'rsrc/externals/javelin/lib/Scrollbar.js' => '087e919c',
     'rsrc/externals/javelin/lib/Sound.js' => '949c0fe5',
     'rsrc/externals/javelin/lib/URI.js' => 'c989ade3',
     'rsrc/externals/javelin/lib/Vector.js' => '2caa8fb8',
     'rsrc/externals/javelin/lib/WebSocket.js' => 'e292eaf4',
     'rsrc/externals/javelin/lib/Workflow.js' => '28cfbdd0',
     'rsrc/externals/javelin/lib/__tests__/Cookie.js' => '5ed109e8',
     'rsrc/externals/javelin/lib/__tests__/DOM.js' => 'c984504b',
     'rsrc/externals/javelin/lib/__tests__/JSON.js' => '837a7d68',
     'rsrc/externals/javelin/lib/__tests__/URI.js' => '1e45fda9',
     'rsrc/externals/javelin/lib/__tests__/behavior.js' => '1ea62783',
     'rsrc/externals/javelin/lib/behavior.js' => '61cbc29a',
     'rsrc/externals/javelin/lib/control/tokenizer/Tokenizer.js' => '8d3bc1b2',
     'rsrc/externals/javelin/lib/control/typeahead/Typeahead.js' => '70baed2f',
     'rsrc/externals/javelin/lib/control/typeahead/normalizer/TypeaheadNormalizer.js' => 'e6e25838',
     'rsrc/externals/javelin/lib/control/typeahead/source/TypeaheadCompositeSource.js' => '503e17fd',
     'rsrc/externals/javelin/lib/control/typeahead/source/TypeaheadOnDemandSource.js' => '013ffff9',
     'rsrc/externals/javelin/lib/control/typeahead/source/TypeaheadPreloadedSource.js' => '54f314a0',
     'rsrc/externals/javelin/lib/control/typeahead/source/TypeaheadSource.js' => '1bc11c4a',
     'rsrc/externals/javelin/lib/control/typeahead/source/TypeaheadStaticSource.js' => '6c0e62fa',
     'rsrc/favicons/apple-touch-icon-120x120.png' => '43742962',
     'rsrc/favicons/apple-touch-icon-152x152.png' => '669eaec3',
     'rsrc/favicons/apple-touch-icon-76x76.png' => 'ecdef672',
     'rsrc/favicons/favicon-128.png' => '47cdff03',
     'rsrc/favicons/favicon-16x16.png' => 'ee2523ac',
     'rsrc/favicons/favicon-32x32.png' => 'b6a8150e',
     'rsrc/favicons/favicon-96x96.png' => '8f7ea177',
     'rsrc/favicons/mask-icon.svg' => '0460cb1f',
     'rsrc/image/BFCFDA.png' => 'd5ec91f4',
     'rsrc/image/actions/edit.png' => '2fc41442',
     'rsrc/image/avatar.png' => 'e132bb6a',
     'rsrc/image/checker_dark.png' => 'd8e65881',
     'rsrc/image/checker_light.png' => 'a0155918',
     'rsrc/image/checker_lighter.png' => 'd5da91b6',
     'rsrc/image/d5d8e1.png' => '0c2a1497',
     'rsrc/image/darkload.gif' => '1ffd3ec6',
     'rsrc/image/divot.png' => '94dded62',
     'rsrc/image/examples/hero.png' => '979a86ae',
     'rsrc/image/grippy_texture.png' => 'aca81e2f',
     'rsrc/image/icon/fatcow/arrow_branch.png' => '2537c01c',
     'rsrc/image/icon/fatcow/arrow_merge.png' => '21b660e0',
     'rsrc/image/icon/fatcow/bullet_black.png' => 'ff190031',
     'rsrc/image/icon/fatcow/bullet_orange.png' => 'e273e5bb',
     'rsrc/image/icon/fatcow/bullet_red.png' => 'c0b75434',
     'rsrc/image/icon/fatcow/calendar_edit.png' => '24632275',
     'rsrc/image/icon/fatcow/document_black.png' => '45fe1c60',
     'rsrc/image/icon/fatcow/flag_blue.png' => 'a01abb1d',
     'rsrc/image/icon/fatcow/flag_finish.png' => '67825cee',
     'rsrc/image/icon/fatcow/flag_ghost.png' => '20ca8783',
     'rsrc/image/icon/fatcow/flag_green.png' => '7e0eaa7a',
     'rsrc/image/icon/fatcow/flag_orange.png' => '9e73df66',
     'rsrc/image/icon/fatcow/flag_pink.png' => '7e92f3b2',
     'rsrc/image/icon/fatcow/flag_purple.png' => 'cc517522',
     'rsrc/image/icon/fatcow/flag_red.png' => '04ec726f',
     'rsrc/image/icon/fatcow/flag_yellow.png' => '73946fd4',
     'rsrc/image/icon/fatcow/key_question.png' => '52a0c26a',
     'rsrc/image/icon/fatcow/link.png' => '7afd4d5e',
     'rsrc/image/icon/fatcow/page_white_edit.png' => '39a2eed8',
     'rsrc/image/icon/fatcow/page_white_put.png' => '08c95a0c',
     'rsrc/image/icon/fatcow/source/conduit.png' => '4ea01d2f',
     'rsrc/image/icon/fatcow/source/email.png' => '9bab3239',
     'rsrc/image/icon/fatcow/source/fax.png' => '04195e68',
     'rsrc/image/icon/fatcow/source/mobile.png' => 'f1321264',
     'rsrc/image/icon/fatcow/source/tablet.png' => '49396799',
     'rsrc/image/icon/fatcow/source/web.png' => '136ccb5d',
     'rsrc/image/icon/lightbox/close-2.png' => 'cc40e7c8',
     'rsrc/image/icon/lightbox/close-hover-2.png' => 'fb5d6d9e',
     'rsrc/image/icon/lightbox/left-arrow-2.png' => '8426133b',
     'rsrc/image/icon/lightbox/left-arrow-hover-2.png' => '701e5ee3',
     'rsrc/image/icon/lightbox/right-arrow-2.png' => '6d5519a0',
     'rsrc/image/icon/lightbox/right-arrow-hover-2.png' => '3a04aa21',
     'rsrc/image/icon/subscribe.png' => 'd03ed5a5',
     'rsrc/image/icon/tango/attachment.png' => 'ecc8022e',
     'rsrc/image/icon/tango/edit.png' => '929a1363',
     'rsrc/image/icon/tango/go-down.png' => '96d95e43',
     'rsrc/image/icon/tango/log.png' => 'b08cc63a',
     'rsrc/image/icon/tango/upload.png' => '7bbb7984',
     'rsrc/image/icon/unsubscribe.png' => '25725013',
     'rsrc/image/lightblue-header.png' => '5c168b6d',
     'rsrc/image/main_texture.png' => '29a2c5ad',
     'rsrc/image/menu_texture.png' => '5a17580d',
     'rsrc/image/people/harding.png' => '45aa614e',
     'rsrc/image/people/jefferson.png' => 'afca0e53',
     'rsrc/image/people/lincoln.png' => '9369126d',
     'rsrc/image/people/mckinley.png' => 'fb8f16ce',
     'rsrc/image/people/taft.png' => 'd7bc402c',
     'rsrc/image/people/user0.png' => '03dacaea',
     'rsrc/image/people/user1.png' => '4a4e7702',
     'rsrc/image/people/user2.png' => '47a0ee40',
     'rsrc/image/people/user3.png' => '835ff627',
     'rsrc/image/people/user4.png' => 'b0e830f1',
     'rsrc/image/people/user5.png' => '9c95b369',
     'rsrc/image/people/user6.png' => 'ba3fbfb0',
     'rsrc/image/people/user7.png' => 'da613924',
     'rsrc/image/people/user8.png' => 'f1035edf',
     'rsrc/image/people/user9.png' => '66730be3',
     'rsrc/image/people/washington.png' => '40dd301c',
     'rsrc/image/phrequent_active.png' => 'a466a8ed',
     'rsrc/image/phrequent_inactive.png' => 'bfc15a69',
     'rsrc/image/sprite-login-X2.png' => 'e3991e37',
     'rsrc/image/sprite-login.png' => '03d5af29',
     'rsrc/image/sprite-menu-X2.png' => 'cfd8fca5',
     'rsrc/image/sprite-menu.png' => 'd7a99faa',
     'rsrc/image/sprite-tokens-X2.png' => '348f1745',
     'rsrc/image/sprite-tokens.png' => 'ce0b62be',
     'rsrc/image/texture/card-gradient.png' => '815f26e8',
     'rsrc/image/texture/dark-menu-hover.png' => '5fa7ece8',
     'rsrc/image/texture/dark-menu.png' => '7e22296e',
     'rsrc/image/texture/grip.png' => '719404f3',
     'rsrc/image/texture/panel-header-gradient.png' => 'e3b8dcfe',
     'rsrc/image/texture/phlnx-bg.png' => '8d819209',
     'rsrc/image/texture/pholio-background.gif' => 'ba29239c',
     'rsrc/image/texture/table_header.png' => '5c433037',
     'rsrc/image/texture/table_header_hover.png' => '038ec3b9',
     'rsrc/image/texture/table_header_tall.png' => 'd56b434f',
     'rsrc/js/application/aphlict/Aphlict.js' => '5359e785',
     'rsrc/js/application/aphlict/behavior-aphlict-dropdown.js' => '031cee25',
     'rsrc/js/application/aphlict/behavior-aphlict-listen.js' => 'fb20ac8d',
     'rsrc/js/application/aphlict/behavior-aphlict-status.js' => 'ea681761',
     'rsrc/js/application/aphlict/behavior-desktop-notifications-control.js' => 'edd1ba66',
     'rsrc/js/application/auth/behavior-persona-login.js' => '9414ff18',
     'rsrc/js/application/calendar/behavior-day-view.js' => '5c46cff2',
     'rsrc/js/application/calendar/behavior-event-all-day.js' => '38dcf3c8',
     'rsrc/js/application/calendar/behavior-recurring-edit.js' => '5f1c4d5f',
     'rsrc/js/application/config/behavior-reorder-fields.js' => 'b6993408',
     'rsrc/js/application/conpherence/ConpherenceThreadManager.js' => '01774ab2',
     'rsrc/js/application/conpherence/behavior-drag-and-drop-photo.js' => 'cf86d16a',
     'rsrc/js/application/conpherence/behavior-durable-column.js' => 'c72aa091',
     'rsrc/js/application/conpherence/behavior-menu.js' => '1d45c74d',
     'rsrc/js/application/conpherence/behavior-pontificate.js' => '21ba5861',
     'rsrc/js/application/conpherence/behavior-quicksand-blacklist.js' => '7927a7d3',
     'rsrc/js/application/conpherence/behavior-widget-pane.js' => 'a8458711',
     'rsrc/js/application/countdown/timer.js' => 'e4cc26b3',
     'rsrc/js/application/daemon/behavior-bulk-job-reload.js' => 'edf8a145',
     'rsrc/js/application/dashboard/behavior-dashboard-async-panel.js' => '469c0d9e',
     'rsrc/js/application/dashboard/behavior-dashboard-move-panels.js' => '019f36c4',
     'rsrc/js/application/dashboard/behavior-dashboard-query-panel-select.js' => '453c5375',
     'rsrc/js/application/dashboard/behavior-dashboard-tab-panel.js' => 'd4eecc63',
     'rsrc/js/application/differential/ChangesetViewManager.js' => 'a2828756',
     'rsrc/js/application/differential/DifferentialInlineCommentEditor.js' => '64a5550f',
     'rsrc/js/application/differential/behavior-add-reviewers-and-ccs.js' => 'e10f8e18',
     'rsrc/js/application/differential/behavior-comment-jump.js' => '4fdb476d',
     'rsrc/js/application/differential/behavior-comment-preview.js' => 'b064af76',
     'rsrc/js/application/differential/behavior-diff-radios.js' => 'e1ff79b1',
     'rsrc/js/application/differential/behavior-dropdown-menus.js' => '9a6b9324',
     'rsrc/js/application/differential/behavior-edit-inline-comments.js' => '4fbbc3e9',
     'rsrc/js/application/differential/behavior-keyboard-nav.js' => '2c426492',
     'rsrc/js/application/differential/behavior-populate.js' => '8694b1df',
     'rsrc/js/application/differential/behavior-toggle-files.js' => 'ca3f91eb',
     'rsrc/js/application/differential/behavior-user-select.js' => 'a8d8459d',
     'rsrc/js/application/diffusion/DiffusionLocateFileSource.js' => 'b42eddc7',
     'rsrc/js/application/diffusion/behavior-audit-preview.js' => 'd835b03a',
     'rsrc/js/application/diffusion/behavior-commit-branches.js' => 'bdaf4d04',
     'rsrc/js/application/diffusion/behavior-commit-graph.js' => '5a0b1a64',
     'rsrc/js/application/diffusion/behavior-diffusion-browse-file.js' => '054a0f0b',
     'rsrc/js/application/diffusion/behavior-jump-to.js' => '73d09eef',
     'rsrc/js/application/diffusion/behavior-load-blame.js' => '42126667',
     'rsrc/js/application/diffusion/behavior-locate-file.js' => '6d3e1947',
     'rsrc/js/application/diffusion/behavior-pull-lastmodified.js' => 'f01586dc',
     'rsrc/js/application/doorkeeper/behavior-doorkeeper-tag.js' => 'e5822781',
     'rsrc/js/application/drydock/drydock-live-operation-status.js' => '901935ef',
     'rsrc/js/application/files/behavior-icon-composer.js' => '8499b6ab',
     'rsrc/js/application/files/behavior-launch-icon-composer.js' => '48086888',
     'rsrc/js/application/herald/HeraldRuleEditor.js' => 'd6a7e717',
     'rsrc/js/application/herald/PathTypeahead.js' => 'f7fc67ec',
     'rsrc/js/application/herald/herald-rule-editor.js' => '7ebaeed3',
     'rsrc/js/application/maniphest/behavior-batch-editor.js' => '782ab6e7',
     'rsrc/js/application/maniphest/behavior-batch-selector.js' => '7b98d7c5',
     'rsrc/js/application/maniphest/behavior-line-chart.js' => 'e4232876',
     'rsrc/js/application/maniphest/behavior-list-edit.js' => 'a9f88de2',
     'rsrc/js/application/maniphest/behavior-subpriorityeditor.js' => '71237763',
     'rsrc/js/application/owners/OwnersPathEditor.js' => 'aa1733d0',
     'rsrc/js/application/owners/owners-path-editor.js' => '7a68dda3',
     'rsrc/js/application/passphrase/passphrase-credential-control.js' => '3cb0b2fc',
     'rsrc/js/application/pholio/behavior-pholio-mock-edit.js' => '246dc085',
     'rsrc/js/application/pholio/behavior-pholio-mock-view.js' => 'fbe497e7',
     'rsrc/js/application/phortune/behavior-stripe-payment-form.js' => '3f5d6dbf',
     'rsrc/js/application/phortune/behavior-test-payment-form.js' => 'fc91ab6c',
     'rsrc/js/application/phortune/phortune-credit-card-form.js' => '2290aeef',
     'rsrc/js/application/policy/behavior-policy-control.js' => 'd0c516d5',
     'rsrc/js/application/policy/behavior-policy-rule-editor.js' => '5e9f347c',
     'rsrc/js/application/projects/WorkboardBoard.js' => '52291776',
     'rsrc/js/application/projects/WorkboardCard.js' => 'c587b80f',
     'rsrc/js/application/projects/WorkboardColumn.js' => 'bae58312',
     'rsrc/js/application/projects/WorkboardController.js' => '55baf5ed',
     'rsrc/js/application/projects/behavior-project-boards.js' => '14a1faae',
     'rsrc/js/application/projects/behavior-project-create.js' => '065227cc',
     'rsrc/js/application/projects/behavior-reorder-columns.js' => 'e1d25dfb',
     'rsrc/js/application/releeph/releeph-preview-branch.js' => 'b2b4fbaf',
     'rsrc/js/application/releeph/releeph-request-state-change.js' => 'a0b57eb8',
     'rsrc/js/application/releeph/releeph-request-typeahead.js' => 'de2e896f',
     'rsrc/js/application/repository/repository-crossreference.js' => 'e5339c43',
     'rsrc/js/application/search/behavior-reorder-profile-menu-items.js' => 'e2e0a072',
     'rsrc/js/application/search/behavior-reorder-queries.js' => 'e9581f08',
     'rsrc/js/application/slowvote/behavior-slowvote-embed.js' => '887ad43f',
     'rsrc/js/application/transactions/behavior-comment-actions.js' => '06460e71',
     'rsrc/js/application/transactions/behavior-reorder-configs.js' => 'd7a74243',
     'rsrc/js/application/transactions/behavior-reorder-fields.js' => 'b59e1e96',
     'rsrc/js/application/transactions/behavior-show-older-transactions.js' => 'dbbf48b6',
     'rsrc/js/application/transactions/behavior-transaction-comment-form.js' => 'b23b49e6',
     'rsrc/js/application/transactions/behavior-transaction-list.js' => '13c739ea',
     'rsrc/js/application/typeahead/behavior-typeahead-browse.js' => '635de1ec',
     'rsrc/js/application/typeahead/behavior-typeahead-search.js' => '93d0c9e3',
     'rsrc/js/application/uiexample/JavelinViewExample.js' => 'd4a14807',
     'rsrc/js/application/uiexample/ReactorButtonExample.js' => 'd19198c8',
     'rsrc/js/application/uiexample/ReactorCheckboxExample.js' => '519705ea',
     'rsrc/js/application/uiexample/ReactorFocusExample.js' => '40a6a403',
     'rsrc/js/application/uiexample/ReactorInputExample.js' => '886fd850',
     'rsrc/js/application/uiexample/ReactorMouseoverExample.js' => '47c794d8',
     'rsrc/js/application/uiexample/ReactorRadioExample.js' => '988040b4',
     'rsrc/js/application/uiexample/ReactorSelectExample.js' => 'a155550f',
     'rsrc/js/application/uiexample/ReactorSendClassExample.js' => '1def2711',
     'rsrc/js/application/uiexample/ReactorSendPropertiesExample.js' => 'b1f0ccee',
     'rsrc/js/application/uiexample/busy-example.js' => '60479091',
     'rsrc/js/application/uiexample/gesture-example.js' => '558829c2',
     'rsrc/js/application/uiexample/notification-example.js' => '8ce821c5',
     'rsrc/js/core/Busy.js' => '59a7976a',
     'rsrc/js/core/DragAndDropFileUpload.js' => '81f182b5',
     'rsrc/js/core/DraggableList.js' => '5a13c79f',
     'rsrc/js/core/FileUpload.js' => '680ea2c8',
     'rsrc/js/core/Hovercard.js' => '1bd28176',
     'rsrc/js/core/KeyboardShortcut.js' => '1ae869f2',
     'rsrc/js/core/KeyboardShortcutManager.js' => 'c1700f6f',
     'rsrc/js/core/MultirowRowManager.js' => 'b5d57730',
     'rsrc/js/core/Notification.js' => 'ccf1cbf8',
     'rsrc/js/core/Prefab.js' => 'e67df814',
     'rsrc/js/core/ShapedRequest.js' => '7cbe244b',
     'rsrc/js/core/TextAreaUtils.js' => '5813016a',
     'rsrc/js/core/Title.js' => 'df5e11d2',
     'rsrc/js/core/ToolTip.js' => '6323f942',
     'rsrc/js/core/behavior-active-nav.js' => 'e379b58e',
     'rsrc/js/core/behavior-audio-source.js' => '59b251eb',
     'rsrc/js/core/behavior-autofocus.js' => '7319e029',
     'rsrc/js/core/behavior-badge-view.js' => '8ff5e24c',
     'rsrc/js/core/behavior-choose-control.js' => '327a00d1',
     'rsrc/js/core/behavior-crop.js' => 'fa0f4fc2',
     'rsrc/js/core/behavior-dark-console.js' => 'f411b6ae',
     'rsrc/js/core/behavior-device.js' => 'b5b36110',
     'rsrc/js/core/behavior-drag-and-drop-textarea.js' => '4f6a4b4e',
     'rsrc/js/core/behavior-error-log.js' => '6882e80a',
     'rsrc/js/core/behavior-fancy-datepicker.js' => '568931f3',
     'rsrc/js/core/behavior-file-tree.js' => '88236f00',
     'rsrc/js/core/behavior-form.js' => '5c54cbf3',
     'rsrc/js/core/behavior-gesture.js' => '3ab51e2c',
     'rsrc/js/core/behavior-global-drag-and-drop.js' => 'c8e57404',
     'rsrc/js/core/behavior-high-security-warning.js' => 'a464fe03',
     'rsrc/js/core/behavior-history-install.js' => '7ee2b591',
     'rsrc/js/core/behavior-hovercard.js' => 'bcaccd64',
     'rsrc/js/core/behavior-keyboard-pager.js' => 'a8da01f0',
     'rsrc/js/core/behavior-keyboard-shortcuts.js' => 'd75709e6',
     'rsrc/js/core/behavior-lightbox-attachments.js' => 'f8ba29d7',
     'rsrc/js/core/behavior-line-linker.js' => '1499a8cb',
     'rsrc/js/core/behavior-more.js' => 'a80d0378',
     'rsrc/js/core/behavior-object-selector.js' => '49b73b36',
     'rsrc/js/core/behavior-oncopy.js' => '2926fff2',
     'rsrc/js/core/behavior-phabricator-nav.js' => '56a1ca03',
     'rsrc/js/core/behavior-phabricator-remarkup-assist.js' => '340c8eff',
     'rsrc/js/core/behavior-read-only-warning.js' => 'ba158207',
     'rsrc/js/core/behavior-refresh-csrf.js' => 'ab2f381b',
     'rsrc/js/core/behavior-remarkup-preview.js' => '4b700e9e',
     'rsrc/js/core/behavior-reorder-applications.js' => '76b9fc3e',
     'rsrc/js/core/behavior-reveal-content.js' => '60821bc7',
     'rsrc/js/core/behavior-scrollbar.js' => '834a1173',
     'rsrc/js/core/behavior-search-typeahead.js' => '06c32383',
+    'rsrc/js/core/behavior-select-content.js' => 'bf5374ef',
     'rsrc/js/core/behavior-select-on-click.js' => '4e3e79a6',
     'rsrc/js/core/behavior-time-typeahead.js' => '522431f7',
     'rsrc/js/core/behavior-toggle-class.js' => '92b9ec77',
     'rsrc/js/core/behavior-tokenizer.js' => 'b3a4b884',
     'rsrc/js/core/behavior-tooltip.js' => '42fcb747',
     'rsrc/js/core/behavior-watch-anchor.js' => '9f36c42d',
     'rsrc/js/core/behavior-workflow.js' => '0a3f3021',
     'rsrc/js/core/phtize.js' => 'd254d646',
     'rsrc/js/phui/behavior-phui-dropdown-menu.js' => '54733475',
     'rsrc/js/phui/behavior-phui-object-box-tabs.js' => '2bfa2836',
     'rsrc/js/phui/behavior-phui-profile-menu.js' => '12884df9',
     'rsrc/js/phuix/PHUIXActionListView.js' => 'b5c256b8',
     'rsrc/js/phuix/PHUIXActionView.js' => '8cf6d262',
     'rsrc/js/phuix/PHUIXAutocomplete.js' => '9196fb06',
     'rsrc/js/phuix/PHUIXDropdownMenu.js' => 'bd4c8dca',
     'rsrc/js/phuix/PHUIXFormControl.js' => 'e15869a8',
     'rsrc/js/phuix/PHUIXIconView.js' => 'bff6884b',
   ),
   'symbols' => array(
     'almanac-css' => 'dbb9b3af',
     'aphront-bars' => '231ac33c',
     'aphront-dark-console-css' => 'f54bf286',
     'aphront-dialog-view-css' => 'b4334e08',
     'aphront-list-filter-view-css' => '5d6f0526',
     'aphront-multi-column-view-css' => 'fd18389d',
     'aphront-panel-view-css' => '8427b78d',
     'aphront-table-view-css' => '9258e19f',
     'aphront-tokenizer-control-css' => '056da01b',
     'aphront-tooltip-css' => '1a07aea8',
     'aphront-typeahead-control-css' => 'd4f16145',
     'auth-css' => '0877ed6e',
     'bulk-job-css' => 'df9c1d4a',
     'changeset-view-manager' => 'a2828756',
     'conduit-api-css' => '7bc725c4',
     'config-options-css' => '0ede4c9b',
     'config-welcome-css' => '6abd79be',
     'conpherence-durable-column-view' => '86396117',
     'conpherence-menu-css' => 'f99fee4c',
     'conpherence-message-pane-css' => '5897d3ac',
     'conpherence-notification-css' => '6cdcc253',
     'conpherence-thread-manager' => '01774ab2',
     'conpherence-transaction-css' => '85d0974c',
     'conpherence-update-css' => 'faf6be09',
     'conpherence-widget-pane-css' => '775eaaba',
     'd3' => 'a11a5ff2',
     'differential-changeset-view-css' => '3e3b0b76',
     'differential-core-view-css' => '5b7b8ff4',
     'differential-inline-comment-editor' => '64a5550f',
     'differential-revision-add-comment-css' => 'c47f8c40',
     'differential-revision-comment-css' => '14b8565a',
     'differential-revision-history-css' => '0e8eb855',
     'differential-revision-list-css' => 'f3c47d33',
     'differential-table-of-contents-css' => 'ae4b7a55',
-    'diffusion-icons-css' => '3311444d',
+    'diffusion-icons-css' => 'd678600a',
     'diffusion-readme-css' => '297373eb',
     'diffusion-source-css' => '68b30fd3',
     'diviner-shared-css' => 'aa3656aa',
     'font-aleo' => '8bdb2835',
     'font-fontawesome' => '2b7ebbcc',
     'font-lato' => 'c7ccd872',
     'global-drag-and-drop-css' => '5c1b47c2',
     'harbormaster-css' => 'f491c9f4',
     'herald-css' => 'dc31f6e9',
     'herald-rule-editor' => 'd6a7e717',
     'herald-test-css' => 'a52e323e',
     'inline-comment-summary-css' => '51efda3a',
     'javelin-aphlict' => '5359e785',
     'javelin-behavior' => '61cbc29a',
     'javelin-behavior-aphlict-dropdown' => '031cee25',
     'javelin-behavior-aphlict-listen' => 'fb20ac8d',
     'javelin-behavior-aphlict-status' => 'ea681761',
     'javelin-behavior-aphront-basic-tokenizer' => 'b3a4b884',
     'javelin-behavior-aphront-crop' => 'fa0f4fc2',
     'javelin-behavior-aphront-drag-and-drop-textarea' => '4f6a4b4e',
     'javelin-behavior-aphront-form-disable-on-submit' => '5c54cbf3',
     'javelin-behavior-aphront-more' => 'a80d0378',
     'javelin-behavior-audio-source' => '59b251eb',
     'javelin-behavior-audit-preview' => 'd835b03a',
     'javelin-behavior-badge-view' => '8ff5e24c',
     'javelin-behavior-bulk-job-reload' => 'edf8a145',
     'javelin-behavior-choose-control' => '327a00d1',
     'javelin-behavior-comment-actions' => '06460e71',
     'javelin-behavior-config-reorder-fields' => 'b6993408',
     'javelin-behavior-conpherence-drag-and-drop-photo' => 'cf86d16a',
     'javelin-behavior-conpherence-menu' => '1d45c74d',
     'javelin-behavior-conpherence-pontificate' => '21ba5861',
     'javelin-behavior-conpherence-widget-pane' => 'a8458711',
     'javelin-behavior-countdown-timer' => 'e4cc26b3',
     'javelin-behavior-dark-console' => 'f411b6ae',
     'javelin-behavior-dashboard-async-panel' => '469c0d9e',
     'javelin-behavior-dashboard-move-panels' => '019f36c4',
     'javelin-behavior-dashboard-query-panel-select' => '453c5375',
     'javelin-behavior-dashboard-tab-panel' => 'd4eecc63',
     'javelin-behavior-day-view' => '5c46cff2',
     'javelin-behavior-desktop-notifications-control' => 'edd1ba66',
     'javelin-behavior-device' => 'b5b36110',
     'javelin-behavior-differential-add-reviewers-and-ccs' => 'e10f8e18',
     'javelin-behavior-differential-comment-jump' => '4fdb476d',
     'javelin-behavior-differential-diff-radios' => 'e1ff79b1',
     'javelin-behavior-differential-dropdown-menus' => '9a6b9324',
     'javelin-behavior-differential-edit-inline-comments' => '4fbbc3e9',
     'javelin-behavior-differential-feedback-preview' => 'b064af76',
     'javelin-behavior-differential-keyboard-navigation' => '2c426492',
     'javelin-behavior-differential-populate' => '8694b1df',
     'javelin-behavior-differential-toggle-files' => 'ca3f91eb',
     'javelin-behavior-differential-user-select' => 'a8d8459d',
     'javelin-behavior-diffusion-browse-file' => '054a0f0b',
     'javelin-behavior-diffusion-commit-branches' => 'bdaf4d04',
     'javelin-behavior-diffusion-commit-graph' => '5a0b1a64',
     'javelin-behavior-diffusion-jump-to' => '73d09eef',
     'javelin-behavior-diffusion-locate-file' => '6d3e1947',
     'javelin-behavior-diffusion-pull-lastmodified' => 'f01586dc',
     'javelin-behavior-doorkeeper-tag' => 'e5822781',
     'javelin-behavior-drydock-live-operation-status' => '901935ef',
     'javelin-behavior-durable-column' => 'c72aa091',
     'javelin-behavior-editengine-reorder-configs' => 'd7a74243',
     'javelin-behavior-editengine-reorder-fields' => 'b59e1e96',
     'javelin-behavior-error-log' => '6882e80a',
     'javelin-behavior-event-all-day' => '38dcf3c8',
     'javelin-behavior-fancy-datepicker' => '568931f3',
     'javelin-behavior-global-drag-and-drop' => 'c8e57404',
     'javelin-behavior-herald-rule-editor' => '7ebaeed3',
     'javelin-behavior-high-security-warning' => 'a464fe03',
     'javelin-behavior-history-install' => '7ee2b591',
     'javelin-behavior-icon-composer' => '8499b6ab',
     'javelin-behavior-launch-icon-composer' => '48086888',
     'javelin-behavior-lightbox-attachments' => 'f8ba29d7',
     'javelin-behavior-line-chart' => 'e4232876',
     'javelin-behavior-load-blame' => '42126667',
     'javelin-behavior-maniphest-batch-editor' => '782ab6e7',
     'javelin-behavior-maniphest-batch-selector' => '7b98d7c5',
     'javelin-behavior-maniphest-list-editor' => 'a9f88de2',
     'javelin-behavior-maniphest-subpriority-editor' => '71237763',
     'javelin-behavior-owners-path-editor' => '7a68dda3',
     'javelin-behavior-passphrase-credential-control' => '3cb0b2fc',
     'javelin-behavior-persona-login' => '9414ff18',
     'javelin-behavior-phabricator-active-nav' => 'e379b58e',
     'javelin-behavior-phabricator-autofocus' => '7319e029',
     'javelin-behavior-phabricator-busy-example' => '60479091',
     'javelin-behavior-phabricator-file-tree' => '88236f00',
     'javelin-behavior-phabricator-gesture' => '3ab51e2c',
     'javelin-behavior-phabricator-gesture-example' => '558829c2',
     'javelin-behavior-phabricator-keyboard-pager' => 'a8da01f0',
     'javelin-behavior-phabricator-keyboard-shortcuts' => 'd75709e6',
     'javelin-behavior-phabricator-line-linker' => '1499a8cb',
     'javelin-behavior-phabricator-nav' => '56a1ca03',
     'javelin-behavior-phabricator-notification-example' => '8ce821c5',
     'javelin-behavior-phabricator-object-selector' => '49b73b36',
     'javelin-behavior-phabricator-oncopy' => '2926fff2',
     'javelin-behavior-phabricator-remarkup-assist' => '340c8eff',
     'javelin-behavior-phabricator-reveal-content' => '60821bc7',
     'javelin-behavior-phabricator-search-typeahead' => '06c32383',
     'javelin-behavior-phabricator-show-older-transactions' => 'dbbf48b6',
     'javelin-behavior-phabricator-tooltips' => '42fcb747',
     'javelin-behavior-phabricator-transaction-comment-form' => 'b23b49e6',
     'javelin-behavior-phabricator-transaction-list' => '13c739ea',
     'javelin-behavior-phabricator-watch-anchor' => '9f36c42d',
     'javelin-behavior-pholio-mock-edit' => '246dc085',
     'javelin-behavior-pholio-mock-view' => 'fbe497e7',
     'javelin-behavior-phui-dropdown-menu' => '54733475',
     'javelin-behavior-phui-hovercards' => 'bcaccd64',
     'javelin-behavior-phui-object-box-tabs' => '2bfa2836',
     'javelin-behavior-phui-profile-menu' => '12884df9',
     'javelin-behavior-policy-control' => 'd0c516d5',
     'javelin-behavior-policy-rule-editor' => '5e9f347c',
     'javelin-behavior-project-boards' => '14a1faae',
     'javelin-behavior-project-create' => '065227cc',
     'javelin-behavior-quicksand-blacklist' => '7927a7d3',
     'javelin-behavior-read-only-warning' => 'ba158207',
     'javelin-behavior-recurring-edit' => '5f1c4d5f',
     'javelin-behavior-refresh-csrf' => 'ab2f381b',
     'javelin-behavior-releeph-preview-branch' => 'b2b4fbaf',
     'javelin-behavior-releeph-request-state-change' => 'a0b57eb8',
     'javelin-behavior-releeph-request-typeahead' => 'de2e896f',
     'javelin-behavior-remarkup-preview' => '4b700e9e',
     'javelin-behavior-reorder-applications' => '76b9fc3e',
     'javelin-behavior-reorder-columns' => 'e1d25dfb',
     'javelin-behavior-reorder-profile-menu-items' => 'e2e0a072',
     'javelin-behavior-repository-crossreference' => 'e5339c43',
     'javelin-behavior-scrollbar' => '834a1173',
     'javelin-behavior-search-reorder-queries' => 'e9581f08',
+    'javelin-behavior-select-content' => 'bf5374ef',
     'javelin-behavior-select-on-click' => '4e3e79a6',
     'javelin-behavior-slowvote-embed' => '887ad43f',
     'javelin-behavior-stripe-payment-form' => '3f5d6dbf',
     'javelin-behavior-test-payment-form' => 'fc91ab6c',
     'javelin-behavior-time-typeahead' => '522431f7',
     'javelin-behavior-toggle-class' => '92b9ec77',
     'javelin-behavior-typeahead-browse' => '635de1ec',
     'javelin-behavior-typeahead-search' => '93d0c9e3',
     'javelin-behavior-view-placeholder' => '47830651',
     'javelin-behavior-workflow' => '0a3f3021',
     'javelin-color' => '7e41274a',
     'javelin-cookie' => '62dfea03',
     'javelin-diffusion-locate-file-source' => 'b42eddc7',
     'javelin-dom' => '805b806a',
     'javelin-dynval' => 'f6555212',
     'javelin-event' => '2ee659ce',
     'javelin-fx' => '54b612ba',
     'javelin-history' => 'd4505101',
     'javelin-install' => '05270951',
     'javelin-json' => '69adf288',
     'javelin-leader' => 'fea0eb47',
     'javelin-magical-init' => '3010e992',
     'javelin-mask' => '8a41885b',
     'javelin-quicksand' => '6b8ef10b',
     'javelin-reactor' => '2b8de964',
     'javelin-reactor-dom' => 'c90a04fc',
     'javelin-reactor-node-calmer' => '76f4ebed',
     'javelin-reactornode' => '1ad0a787',
     'javelin-request' => '94b750d2',
     'javelin-resource' => '44959b73',
     'javelin-routable' => 'b3e7d692',
     'javelin-router' => '29274e2b',
     'javelin-scrollbar' => '087e919c',
     'javelin-sound' => '949c0fe5',
     'javelin-stratcom' => '6ad39b6f',
     'javelin-tokenizer' => '8d3bc1b2',
     'javelin-typeahead' => '70baed2f',
     'javelin-typeahead-composite-source' => '503e17fd',
     'javelin-typeahead-normalizer' => 'e6e25838',
     'javelin-typeahead-ondemand-source' => '013ffff9',
     'javelin-typeahead-preloaded-source' => '54f314a0',
     'javelin-typeahead-source' => '1bc11c4a',
     'javelin-typeahead-static-source' => '6c0e62fa',
     'javelin-uri' => 'c989ade3',
     'javelin-util' => '93cc50d6',
     'javelin-vector' => '2caa8fb8',
     'javelin-view' => '0f764c35',
     'javelin-view-html' => 'fe287620',
     'javelin-view-interpreter' => 'f829edb3',
     'javelin-view-renderer' => '6c2b09a2',
     'javelin-view-visitor' => 'efe49472',
     'javelin-websocket' => 'e292eaf4',
     'javelin-workboard-board' => '52291776',
     'javelin-workboard-card' => 'c587b80f',
     'javelin-workboard-column' => 'bae58312',
     'javelin-workboard-controller' => '55baf5ed',
     'javelin-workflow' => '28cfbdd0',
     'lightbox-attachment-css' => '7acac05d',
     'maniphest-batch-editor' => 'b0f0b6d5',
     'maniphest-report-css' => '9b9580b7',
     'maniphest-task-edit-css' => 'fda62a9b',
     'maniphest-task-summary-css' => '11cc5344',
     'multirow-row-manager' => 'b5d57730',
     'owners-path-editor' => 'aa1733d0',
     'owners-path-editor-css' => '2f00933b',
     'paste-css' => '1898e534',
     'path-typeahead' => 'f7fc67ec',
     'people-profile-css' => '2473d929',
     'phabricator-action-list-view-css' => 'c5eba19d',
     'phabricator-application-launch-view-css' => '95351601',
     'phabricator-busy' => '59a7976a',
     'phabricator-chatlog-css' => 'd295b020',
     'phabricator-content-source-view-css' => '4b8b05d4',
     'phabricator-core-css' => 'd0801452',
-    'phabricator-countdown-css' => '96696f21',
+    'phabricator-countdown-css' => '16c52f5c',
     'phabricator-dashboard-css' => 'bc6f2127',
     'phabricator-drag-and-drop-file-upload' => '81f182b5',
     'phabricator-draggable-list' => '5a13c79f',
     'phabricator-fatal-config-template-css' => '8e6c6fcd',
     'phabricator-feed-css' => 'ecd4ec57',
     'phabricator-file-upload' => '680ea2c8',
     'phabricator-filetree-view-css' => 'fccf9f82',
     'phabricator-flag-css' => '5337623f',
     'phabricator-keyboard-shortcut' => '1ae869f2',
     'phabricator-keyboard-shortcut-manager' => 'c1700f6f',
     'phabricator-main-menu-view' => 'd00a795a',
     'phabricator-nav-view-css' => 'ac79a758',
     'phabricator-notification' => 'ccf1cbf8',
     'phabricator-notification-css' => '3f6c89c9',
     'phabricator-notification-menu-css' => 'f31c0bde',
     'phabricator-object-selector-css' => '85ee8ce6',
     'phabricator-phtize' => 'd254d646',
     'phabricator-prefab' => 'e67df814',
-    'phabricator-remarkup-css' => '6aae5360',
+    'phabricator-remarkup-css' => '787105d6',
     'phabricator-search-results-css' => '7dea472c',
     'phabricator-shaped-request' => '7cbe244b',
-    'phabricator-side-menu-view-css' => '3a3d9f41',
+    'phabricator-side-menu-view-css' => 'dd849797',
     'phabricator-slowvote-css' => 'a94b7230',
     'phabricator-source-code-view-css' => 'cbeef983',
     'phabricator-standard-page-view' => 'e709f6d0',
     'phabricator-textareautils' => '5813016a',
     'phabricator-title' => 'df5e11d2',
     'phabricator-tooltip' => '6323f942',
     'phabricator-ui-example-css' => '528b19de',
     'phabricator-uiexample-javelin-view' => 'd4a14807',
     'phabricator-uiexample-reactor-button' => 'd19198c8',
     'phabricator-uiexample-reactor-checkbox' => '519705ea',
     'phabricator-uiexample-reactor-focus' => '40a6a403',
     'phabricator-uiexample-reactor-input' => '886fd850',
     'phabricator-uiexample-reactor-mouseover' => '47c794d8',
     'phabricator-uiexample-reactor-radio' => '988040b4',
     'phabricator-uiexample-reactor-select' => 'a155550f',
     'phabricator-uiexample-reactor-sendclass' => '1def2711',
     'phabricator-uiexample-reactor-sendproperties' => 'b1f0ccee',
     'phabricator-zindex-css' => '5b6fcf3f',
     'phame-css' => '737792d6',
     'pholio-css' => 'ca89d380',
     'pholio-edit-css' => '3ad9d1ee',
     'pholio-inline-comments-css' => '8e545e49',
     'phortune-credit-card-form' => '2290aeef',
     'phortune-credit-card-form-css' => '8391eb02',
     'phortune-css' => '9149f103',
     'phrequent-css' => 'ffc185ad',
     'phriction-document-css' => '4282e4ad',
     'phui-action-panel-css' => '91c7b835',
     'phui-badge-view-css' => '3baef8db',
     'phui-big-info-view-css' => 'bd903741',
-    'phui-box-css' => 'd909ea3d',
+    'phui-box-css' => '5c8387cf',
     'phui-button-css' => 'a64a8de6',
     'phui-calendar-css' => 'ccabe893',
     'phui-calendar-day-css' => 'd1cf6f93',
     'phui-calendar-list-css' => 'c1c7f338',
     'phui-calendar-month-css' => '476be7e0',
     'phui-chart-css' => '6bf6f78e',
     'phui-crumbs-view-css' => '1a1265d4',
     'phui-curtain-view-css' => '7148ae25',
     'phui-document-summary-view-css' => '9ca48bdf',
-    'phui-document-view-css' => '9c71d2bf',
-    'phui-document-view-pro-css' => '73e45fd2',
-    'phui-feed-story-css' => 'd8440402',
+    'phui-document-view-css' => '715aedfb',
+    'phui-document-view-pro-css' => '8419560b',
+    'phui-feed-story-css' => 'aa49845d',
     'phui-font-icon-base-css' => '6449bce8',
     'phui-fontkit-css' => '9cda225e',
     'phui-form-css' => 'aac1d51d',
     'phui-form-view-css' => '6a51768e',
     'phui-head-thing-view-css' => 'fd311e5f',
     'phui-header-view-css' => '4c7dd8f5',
     'phui-hovercard' => '1bd28176',
     'phui-hovercard-view-css' => 'de1a2119',
     'phui-icon-set-selector-css' => '1ab67aad',
     'phui-icon-view-css' => '3f33ab57',
     'phui-image-mask-css' => 'a8498f9c',
     'phui-info-panel-css' => '27ea50a1',
     'phui-info-view-css' => '28efab79',
     'phui-inline-comment-view-css' => '5953c28e',
     'phui-list-view-css' => '9da2aa00',
     'phui-object-box-css' => '6b487c57',
     'phui-object-item-list-view-css' => '8d99e42b',
     'phui-pager-css' => 'bea33d23',
     'phui-pinboard-view-css' => '2495140e',
     'phui-profile-menu-css' => 'c8557f33',
     'phui-property-list-view-css' => '1d42ee7c',
     'phui-remarkup-preview-css' => '1a8f2591',
     'phui-segment-bar-view-css' => '46342871',
     'phui-spacing-css' => '042804d6',
-    'phui-status-list-view-css' => '37309046',
+    'phui-status-list-view-css' => 'd5263e49',
     'phui-tag-view-css' => '6bbd83e2',
     'phui-theme-css' => '027ba77e',
     'phui-timeline-view-css' => '6e342216',
     'phui-two-column-view-css' => 'b9538af1',
     'phui-workboard-color-css' => 'ac6fe6a7',
     'phui-workboard-view-css' => 'e6d89647',
     'phui-workcard-view-css' => '3646fb96',
     'phui-workpanel-view-css' => '92197373',
     'phuix-action-list-view' => 'b5c256b8',
     'phuix-action-view' => '8cf6d262',
     'phuix-autocomplete' => '9196fb06',
     'phuix-dropdown-menu' => 'bd4c8dca',
     'phuix-form-control-view' => 'e15869a8',
     'phuix-icon-view' => 'bff6884b',
     'policy-css' => '957ea14c',
     'policy-edit-css' => '815c66f7',
     'policy-transaction-detail-css' => '82100a43',
     'ponder-view-css' => 'fbd45f96',
     'project-card-view-css' => '9418c97d',
     'project-view-css' => 'cbaa10a1',
     'releeph-core' => '9b3c5733',
     'releeph-preview-branch' => 'b7a6f4a5',
     'releeph-request-differential-create-dialog' => '8d8b92cd',
     'releeph-request-typeahead-css' => '667a48ae',
     'setup-issue-css' => 'db7e9c40',
     'sprite-login-css' => '60e8560e',
     'sprite-menu-css' => '9dd65b92',
     'sprite-tokens-css' => '4f399012',
-    'syntax-highlighting-css' => '9fd11da8',
+    'syntax-default-css' => '9923583c',
+    'syntax-highlighting-css' => '5101175d',
     'tokens-css' => '3d0f239e',
     'typeahead-browse-css' => 'd8581d2c',
     'unhandled-exception-css' => '4c96257a',
   ),
   'requires' => array(
     '013ffff9' => array(
       'javelin-install',
       'javelin-util',
       'javelin-request',
       'javelin-typeahead-source',
     ),
     '01774ab2' => array(
       'javelin-dom',
       'javelin-util',
       'javelin-stratcom',
       'javelin-install',
       'javelin-aphlict',
       'javelin-workflow',
       'javelin-router',
       'javelin-behavior-device',
       'javelin-vector',
     ),
     '019f36c4' => array(
       'javelin-behavior',
       'javelin-dom',
       'javelin-util',
       'javelin-stratcom',
       'javelin-workflow',
       'phabricator-draggable-list',
     ),
     '031cee25' => array(
       'javelin-behavior',
       'javelin-request',
       'javelin-stratcom',
       'javelin-vector',
       'javelin-dom',
       'javelin-uri',
       'javelin-behavior-device',
       'phabricator-title',
     ),
     '05270951' => array(
       'javelin-util',
       'javelin-magical-init',
     ),
     '054a0f0b' => array(
       'javelin-behavior',
       'javelin-dom',
       'javelin-util',
       'phabricator-tooltip',
     ),
     '056da01b' => array(
       'aphront-typeahead-control-css',
       'phui-tag-view-css',
     ),
     '06460e71' => array(
       'javelin-behavior',
       'javelin-stratcom',
       'javelin-workflow',
       'javelin-dom',
       'phuix-form-control-view',
       'phuix-icon-view',
       'javelin-behavior-phabricator-gesture',
     ),
     '065227cc' => array(
       'javelin-behavior',
       'javelin-dom',
       'javelin-stratcom',
       'javelin-workflow',
     ),
     '06c32383' => array(
       'javelin-behavior',
       'javelin-typeahead-ondemand-source',
       'javelin-typeahead',
       'javelin-dom',
       'javelin-uri',
       'javelin-util',
       'javelin-stratcom',
       'phabricator-prefab',
       'phuix-icon-view',
     ),
     '087e919c' => array(
       'javelin-install',
       'javelin-dom',
       'javelin-stratcom',
       'javelin-vector',
     ),
     '0a3f3021' => array(
       'javelin-behavior',
       'javelin-stratcom',
       'javelin-workflow',
       'javelin-dom',
       'javelin-router',
     ),
     '0f764c35' => array(
       'javelin-install',
       'javelin-util',
     ),
     '12884df9' => array(
       'javelin-behavior',
       'javelin-stratcom',
       'javelin-dom',
     ),
     '13c739ea' => array(
       'javelin-behavior',
       'javelin-stratcom',
       'javelin-workflow',
       'javelin-dom',
       'javelin-uri',
       'phabricator-textareautils',
     ),
     '1499a8cb' => array(
       'javelin-behavior',
       'javelin-stratcom',
       'javelin-dom',
       'javelin-history',
     ),
     '14a1faae' => array(
       'javelin-behavior',
       'javelin-dom',
       'javelin-util',
       'javelin-vector',
       'javelin-stratcom',
       'javelin-workflow',
       'javelin-workboard-controller',
     ),
     '1ad0a787' => array(
       'javelin-install',
       'javelin-reactor',
       'javelin-util',
       'javelin-reactor-node-calmer',
     ),
     '1ae869f2' => array(
       'javelin-install',
       'javelin-util',
       'phabricator-keyboard-shortcut-manager',
     ),
     '1bc11c4a' => array(
       'javelin-install',
       'javelin-util',
       'javelin-dom',
       'javelin-typeahead-normalizer',
     ),
     '1bd28176' => array(
       'javelin-install',
       'javelin-dom',
       'javelin-vector',
       'javelin-request',
       'javelin-uri',
     ),
     '1d45c74d' => array(
       'javelin-behavior',
       'javelin-dom',
       'javelin-util',
       'javelin-stratcom',
       'javelin-workflow',
       'javelin-behavior-device',
       'javelin-history',
       'javelin-vector',
       'javelin-scrollbar',
       'phabricator-title',
       'phabricator-shaped-request',
       'conpherence-thread-manager',
     ),
     '1def2711' => array(
       'javelin-install',
       'javelin-dom',
       'javelin-reactor-dom',
     ),
     '21ba5861' => array(
       'javelin-behavior',
       'javelin-dom',
       'javelin-util',
       'javelin-workflow',
       'javelin-stratcom',
       'conpherence-thread-manager',
     ),
     '2290aeef' => array(
       'javelin-install',
       'javelin-dom',
       'javelin-json',
       'javelin-workflow',
       'javelin-util',
     ),
     '246dc085' => array(
       'javelin-behavior',
       'javelin-stratcom',
       'javelin-dom',
       'javelin-workflow',
       'javelin-quicksand',
       'phabricator-phtize',
       'phabricator-drag-and-drop-file-upload',
       'phabricator-draggable-list',
     ),
     '28cfbdd0' => array(
       'javelin-stratcom',
       'javelin-request',
       'javelin-dom',
       'javelin-vector',
       'javelin-install',
       'javelin-util',
       'javelin-mask',
       'javelin-uri',
       'javelin-routable',
     ),
     '2926fff2' => array(
       'javelin-behavior',
       'javelin-dom',
     ),
     '29274e2b' => array(
       'javelin-install',
       'javelin-util',
     ),
     '2b8de964' => array(
       'javelin-install',
       'javelin-util',
     ),
     '2bfa2836' => array(
       'javelin-behavior',
       'javelin-stratcom',
       'javelin-dom',
     ),
     '2c426492' => array(
       'javelin-behavior',
       'javelin-dom',
       'javelin-stratcom',
       'phabricator-keyboard-shortcut',
     ),
     '2caa8fb8' => array(
       'javelin-install',
       'javelin-event',
     ),
     '2ee659ce' => array(
       'javelin-install',
     ),
     '327a00d1' => array(
       'javelin-behavior',
       'javelin-stratcom',
       'javelin-dom',
       'javelin-workflow',
     ),
     '340c8eff' => array(
       'javelin-behavior',
       'javelin-stratcom',
       'javelin-dom',
       'phabricator-phtize',
       'phabricator-textareautils',
       'javelin-workflow',
       'javelin-vector',
       'phuix-autocomplete',
     ),
     '3ab51e2c' => array(
       'javelin-behavior',
       'javelin-behavior-device',
       'javelin-stratcom',
       'javelin-vector',
       'javelin-dom',
       'javelin-magical-init',
     ),
     '3cb0b2fc' => array(
       'javelin-behavior',
       'javelin-dom',
       'javelin-stratcom',
       'javelin-workflow',
       'javelin-util',
       'javelin-uri',
     ),
     '3e3b0b76' => array(
       'phui-inline-comment-view-css',
     ),
     '3f5d6dbf' => array(
       'javelin-behavior',
       'javelin-dom',
       'phortune-credit-card-form',
     ),
     '40a6a403' => array(
       'javelin-install',
       'javelin-dom',
       'javelin-reactor-dom',
     ),
     42126667 => array(
       'javelin-behavior',
       'javelin-dom',
       'javelin-request',
     ),
     '42fcb747' => array(
       'javelin-behavior',
       'javelin-behavior-device',
       'javelin-stratcom',
       'phabricator-tooltip',
     ),
     '44959b73' => array(
       'javelin-util',
       'javelin-uri',
       'javelin-install',
     ),
     '453c5375' => array(
       'javelin-behavior',
       'javelin-dom',
     ),
     '469c0d9e' => array(
       'javelin-behavior',
       'javelin-dom',
       'javelin-workflow',
     ),
     47830651 => array(
       'javelin-behavior',
       'javelin-dom',
       'javelin-view-renderer',
       'javelin-install',
     ),
     '47c794d8' => array(
       'javelin-install',
       'javelin-dom',
       'javelin-reactor-dom',
     ),
     48086888 => array(
       'javelin-behavior',
       'javelin-dom',
       'javelin-workflow',
     ),
     '49b73b36' => array(
       'javelin-behavior',
       'javelin-dom',
       'javelin-request',
       'javelin-util',
     ),
     '4b700e9e' => array(
       'javelin-behavior',
       'javelin-dom',
       'javelin-util',
       'phabricator-shaped-request',
     ),
     '4e3e79a6' => array(
       'javelin-behavior',
       'javelin-stratcom',
       'javelin-dom',
     ),
     '4f6a4b4e' => array(
       'javelin-behavior',
       'javelin-dom',
       'phabricator-drag-and-drop-file-upload',
       'phabricator-textareautils',
     ),
     '4fbbc3e9' => array(
       'javelin-behavior',
       'javelin-stratcom',
       'javelin-dom',
       'javelin-util',
       'javelin-vector',
       'differential-inline-comment-editor',
     ),
     '4fdb476d' => array(
       'javelin-behavior',
       'javelin-stratcom',
       'javelin-dom',
     ),
     '503e17fd' => array(
       'javelin-install',
       'javelin-typeahead-source',
       'javelin-util',
     ),
+    '5101175d' => array(
+      'syntax-default-css',
+    ),
     '519705ea' => array(
       'javelin-install',
       'javelin-dom',
       'javelin-reactor-dom',
     ),
     '522431f7' => array(
       'javelin-behavior',
       'javelin-util',
       'javelin-dom',
       'javelin-stratcom',
       'javelin-vector',
       'javelin-typeahead-static-source',
     ),
     52291776 => array(
       'javelin-install',
       'javelin-dom',
       'javelin-util',
       'javelin-stratcom',
       'javelin-workflow',
       'phabricator-draggable-list',
       'javelin-workboard-column',
     ),
     '5359e785' => array(
       'javelin-install',
       'javelin-util',
       'javelin-websocket',
       'javelin-leader',
       'javelin-json',
     ),
     54733475 => array(
       'javelin-behavior',
       'javelin-stratcom',
       'javelin-dom',
       'phuix-dropdown-menu',
     ),
     '54b612ba' => array(
       'javelin-color',
       'javelin-install',
       'javelin-util',
     ),
     '54f314a0' => array(
       'javelin-install',
       'javelin-util',
       'javelin-request',
       'javelin-typeahead-source',
     ),
     '558829c2' => array(
       'javelin-stratcom',
       'javelin-behavior',
       'javelin-vector',
       'javelin-dom',
     ),
     '55baf5ed' => array(
       'javelin-install',
       'javelin-dom',
       'javelin-util',
       'javelin-vector',
       'javelin-stratcom',
       'javelin-workflow',
       'phabricator-drag-and-drop-file-upload',
       'javelin-workboard-board',
     ),
     '568931f3' => array(
       'javelin-behavior',
       'javelin-util',
       'javelin-dom',
       'javelin-stratcom',
       'javelin-vector',
     ),
     '56a1ca03' => array(
       'javelin-behavior',
       'javelin-behavior-device',
       'javelin-stratcom',
       'javelin-dom',
       'javelin-magical-init',
       'javelin-vector',
       'javelin-request',
       'javelin-util',
     ),
     '5813016a' => array(
       'javelin-install',
       'javelin-dom',
       'javelin-vector',
     ),
     '59a7976a' => array(
       'javelin-install',
       'javelin-dom',
       'javelin-fx',
     ),
     '59b251eb' => array(
       'javelin-behavior',
       'javelin-stratcom',
       'javelin-vector',
       'javelin-dom',
     ),
     '5a0b1a64' => array(
       'javelin-behavior',
       'javelin-dom',
       'javelin-stratcom',
     ),
     '5a13c79f' => array(
       'javelin-install',
       'javelin-dom',
       'javelin-stratcom',
       'javelin-util',
       'javelin-vector',
       'javelin-magical-init',
     ),
     '5c54cbf3' => array(
       'javelin-behavior',
       'javelin-stratcom',
       'javelin-dom',
     ),
     '5e9f347c' => array(
       'javelin-behavior',
       'multirow-row-manager',
       'javelin-dom',
       'javelin-util',
       'phabricator-prefab',
       'javelin-json',
     ),
     60479091 => array(
       'phabricator-busy',
       'javelin-behavior',
     ),
     '60821bc7' => array(
       'javelin-behavior',
       'javelin-stratcom',
       'javelin-dom',
     ),
     '61cbc29a' => array(
       'javelin-magical-init',
       'javelin-util',
     ),
     '62dfea03' => array(
       'javelin-install',
       'javelin-util',
     ),
     '6323f942' => array(
       'javelin-install',
       'javelin-util',
       'javelin-dom',
       'javelin-vector',
     ),
     '635de1ec' => array(
       'javelin-behavior',
       'javelin-stratcom',
       'javelin-workflow',
       'javelin-dom',
     ),
     '64a5550f' => array(
       'javelin-dom',
       'javelin-util',
       'javelin-stratcom',
       'javelin-install',
       'javelin-request',
       'javelin-workflow',
     ),
     '680ea2c8' => array(
       'javelin-install',
       'javelin-dom',
       'phabricator-notification',
     ),
     '6882e80a' => array(
       'javelin-dom',
     ),
     '69adf288' => array(
       'javelin-install',
     ),
     '6ad39b6f' => array(
       'javelin-install',
       'javelin-event',
       'javelin-util',
       'javelin-magical-init',
     ),
     '6b8ef10b' => array(
       'javelin-install',
     ),
     '6c0e62fa' => array(
       'javelin-install',
       'javelin-typeahead-source',
     ),
     '6c2b09a2' => array(
       'javelin-install',
       'javelin-util',
     ),
     '6d3e1947' => array(
       'javelin-behavior',
       'javelin-diffusion-locate-file-source',
       'javelin-dom',
       'javelin-typeahead',
       'javelin-uri',
     ),
     '70baed2f' => array(
       'javelin-install',
       'javelin-dom',
       'javelin-vector',
       'javelin-util',
     ),
     71237763 => array(
       'javelin-behavior',
       'javelin-dom',
       'javelin-stratcom',
       'javelin-workflow',
       'phabricator-draggable-list',
     ),
     '7319e029' => array(
       'javelin-behavior',
       'javelin-dom',
     ),
     '73d09eef' => array(
       'javelin-behavior',
       'javelin-vector',
       'javelin-dom',
     ),
     '76b9fc3e' => array(
       'javelin-behavior',
       'javelin-stratcom',
       'javelin-workflow',
       'javelin-dom',
       'phabricator-draggable-list',
     ),
     '76f4ebed' => array(
       'javelin-install',
       'javelin-reactor',
       'javelin-util',
     ),
     '782ab6e7' => array(
       'javelin-behavior',
       'javelin-dom',
       'javelin-util',
       'phabricator-prefab',
       'multirow-row-manager',
       'javelin-json',
     ),
     '7927a7d3' => array(
       'javelin-behavior',
       'javelin-quicksand',
     ),
     '7a68dda3' => array(
       'owners-path-editor',
       'javelin-behavior',
     ),
     '7b98d7c5' => array(
       'javelin-behavior',
       'javelin-dom',
       'javelin-stratcom',
       'javelin-util',
     ),
     '7cbe244b' => array(
       'javelin-install',
       'javelin-util',
       'javelin-request',
       'javelin-router',
     ),
     '7e41274a' => array(
       'javelin-install',
     ),
     '7ebaeed3' => array(
       'herald-rule-editor',
       'javelin-behavior',
     ),
     '7ee2b591' => array(
       'javelin-behavior',
       'javelin-history',
     ),
     '805b806a' => array(
       'javelin-magical-init',
       'javelin-install',
       'javelin-util',
       'javelin-vector',
       'javelin-stratcom',
     ),
     '81f182b5' => array(
       'javelin-install',
       'javelin-util',
       'javelin-request',
       'javelin-dom',
       'javelin-uri',
       'phabricator-file-upload',
     ),
     '834a1173' => array(
       'javelin-behavior',
       'javelin-scrollbar',
     ),
     '8499b6ab' => array(
       'javelin-behavior',
       'javelin-dom',
       'javelin-stratcom',
     ),
     '85ee8ce6' => array(
       'aphront-dialog-view-css',
     ),
     '8694b1df' => array(
       'javelin-behavior',
       'javelin-dom',
       'javelin-stratcom',
       'phabricator-tooltip',
       'changeset-view-manager',
     ),
     '88236f00' => array(
       'javelin-behavior',
       'phabricator-keyboard-shortcut',
       'javelin-stratcom',
     ),
     '886fd850' => array(
       'javelin-install',
       'javelin-reactor-dom',
       'javelin-view-html',
       'javelin-view-interpreter',
       'javelin-view-renderer',
     ),
     '887ad43f' => array(
       'javelin-behavior',
       'javelin-request',
       'javelin-stratcom',
       'javelin-dom',
     ),
     '8a41885b' => array(
       'javelin-install',
       'javelin-dom',
     ),
     '8bdb2835' => array(
       'phui-fontkit-css',
     ),
     '8ce821c5' => array(
       'phabricator-notification',
       'javelin-stratcom',
       'javelin-behavior',
     ),
     '8cf6d262' => array(
       'javelin-install',
       'javelin-dom',
       'javelin-util',
     ),
     '8d3bc1b2' => array(
       'javelin-dom',
       'javelin-util',
       'javelin-stratcom',
       'javelin-install',
     ),
     '8ff5e24c' => array(
       'javelin-behavior',
       'javelin-stratcom',
       'javelin-dom',
     ),
     '901935ef' => array(
       'javelin-behavior',
       'javelin-dom',
       'javelin-request',
     ),
     '9196fb06' => array(
       'javelin-install',
       'javelin-dom',
       'phuix-icon-view',
       'phabricator-prefab',
     ),
     92197373 => array(
       'phui-workcard-view-css',
     ),
     '92b9ec77' => array(
       'javelin-behavior',
       'javelin-stratcom',
       'javelin-dom',
     ),
     '93d0c9e3' => array(
       'javelin-behavior',
       'javelin-stratcom',
       'javelin-workflow',
       'javelin-dom',
     ),
     '9414ff18' => array(
       'javelin-behavior',
       'javelin-resource',
       'javelin-stratcom',
       'javelin-workflow',
       'javelin-util',
     ),
     '949c0fe5' => array(
       'javelin-install',
     ),
     '94b750d2' => array(
       'javelin-install',
       'javelin-stratcom',
       'javelin-util',
       'javelin-behavior',
       'javelin-json',
       'javelin-dom',
       'javelin-resource',
       'javelin-routable',
     ),
     '988040b4' => array(
       'javelin-install',
       'javelin-dom',
       'javelin-reactor-dom',
     ),
     '9a6b9324' => array(
       'javelin-behavior',
       'javelin-dom',
       'javelin-util',
       'javelin-stratcom',
       'javelin-workflow',
       'phuix-dropdown-menu',
       'phuix-action-list-view',
       'phuix-action-view',
       'phabricator-phtize',
       'changeset-view-manager',
     ),
     '9f36c42d' => array(
       'javelin-behavior',
       'javelin-stratcom',
       'javelin-dom',
       'javelin-vector',
     ),
     'a0b57eb8' => array(
       'javelin-behavior',
       'javelin-dom',
       'javelin-stratcom',
       'javelin-workflow',
       'javelin-util',
       'phabricator-keyboard-shortcut',
     ),
     'a155550f' => array(
       'javelin-install',
       'javelin-dom',
       'javelin-reactor-dom',
     ),
     'a2828756' => array(
       'javelin-dom',
       'javelin-util',
       'javelin-stratcom',
       'javelin-install',
       'javelin-workflow',
       'javelin-router',
       'javelin-behavior-device',
       'javelin-vector',
     ),
     'a464fe03' => array(
       'javelin-behavior',
       'javelin-uri',
       'phabricator-notification',
     ),
     'a80d0378' => array(
       'javelin-behavior',
       'javelin-stratcom',
       'javelin-dom',
     ),
     'a8458711' => array(
       'javelin-behavior',
       'javelin-dom',
       'javelin-stratcom',
       'javelin-workflow',
       'javelin-util',
       'phabricator-notification',
       'javelin-behavior-device',
       'phuix-dropdown-menu',
       'phuix-action-list-view',
       'phuix-action-view',
       'conpherence-thread-manager',
     ),
     'a8d8459d' => array(
       'javelin-behavior',
       'javelin-dom',
       'javelin-stratcom',
     ),
     'a8da01f0' => array(
       'javelin-behavior',
       'javelin-uri',
       'phabricator-keyboard-shortcut',
     ),
     'a9f88de2' => array(
       'javelin-behavior',
       'javelin-dom',
       'javelin-stratcom',
       'javelin-workflow',
       'javelin-fx',
       'javelin-util',
     ),
     'aa1733d0' => array(
       'multirow-row-manager',
       'javelin-install',
       'path-typeahead',
       'javelin-dom',
       'javelin-util',
       'phabricator-prefab',
     ),
     'ab2f381b' => array(
       'javelin-request',
       'javelin-behavior',
       'javelin-dom',
       'javelin-router',
       'javelin-util',
       'phabricator-busy',
     ),
     'b064af76' => array(
       'javelin-behavior',
       'javelin-stratcom',
       'javelin-dom',
       'javelin-request',
       'javelin-util',
       'phabricator-shaped-request',
     ),
     'b1f0ccee' => array(
       'javelin-install',
       'javelin-dom',
       'javelin-reactor-dom',
     ),
     'b23b49e6' => array(
       'javelin-behavior',
       'javelin-dom',
       'javelin-util',
       'javelin-request',
       'phabricator-shaped-request',
     ),
     'b2b4fbaf' => array(
       'javelin-behavior',
       'javelin-dom',
       'javelin-uri',
       'javelin-request',
     ),
     'b3a4b884' => array(
       'javelin-behavior',
       'phabricator-prefab',
     ),
     'b3e7d692' => array(
       'javelin-install',
     ),
     'b42eddc7' => array(
       'javelin-install',
       'javelin-dom',
       'javelin-typeahead-preloaded-source',
       'javelin-util',
     ),
     'b59e1e96' => array(
       'javelin-behavior',
       'javelin-stratcom',
       'javelin-workflow',
       'javelin-dom',
       'phabricator-draggable-list',
     ),
     'b5b36110' => array(
       'javelin-behavior',
       'javelin-stratcom',
       'javelin-dom',
       'javelin-vector',
       'javelin-install',
     ),
     'b5c256b8' => array(
       'javelin-install',
       'javelin-dom',
     ),
     'b5d57730' => array(
       'javelin-install',
       'javelin-stratcom',
       'javelin-dom',
       'javelin-util',
     ),
     'b6993408' => array(
       'javelin-behavior',
       'javelin-stratcom',
       'javelin-dom',
       'javelin-json',
       'phabricator-draggable-list',
     ),
     'ba158207' => array(
       'javelin-behavior',
       'javelin-uri',
       'phabricator-notification',
     ),
     'bae58312' => array(
       'javelin-install',
       'javelin-workboard-card',
     ),
     'bcaccd64' => array(
       'javelin-behavior',
       'javelin-behavior-device',
       'javelin-stratcom',
       'javelin-vector',
       'phui-hovercard',
     ),
     'bd4c8dca' => array(
       'javelin-install',
       'javelin-util',
       'javelin-dom',
       'javelin-vector',
       'javelin-stratcom',
     ),
     'bdaf4d04' => array(
       'javelin-behavior',
       'javelin-dom',
       'javelin-util',
       'javelin-request',
     ),
+    'bf5374ef' => array(
+      'javelin-behavior',
+      'javelin-stratcom',
+      'javelin-dom',
+    ),
     'bff6884b' => array(
       'javelin-install',
       'javelin-dom',
     ),
     'c1700f6f' => array(
       'javelin-install',
       'javelin-util',
       'javelin-stratcom',
       'javelin-dom',
       'javelin-vector',
     ),
     'c587b80f' => array(
       'javelin-install',
     ),
     'c72aa091' => array(
       'javelin-behavior',
       'javelin-dom',
       'javelin-stratcom',
       'javelin-behavior-device',
       'javelin-scrollbar',
       'javelin-quicksand',
       'phabricator-keyboard-shortcut',
       'conpherence-thread-manager',
     ),
     'c7ccd872' => array(
       'phui-fontkit-css',
     ),
     'c8e57404' => array(
       'javelin-behavior',
       'javelin-dom',
       'javelin-uri',
       'javelin-mask',
       'phabricator-drag-and-drop-file-upload',
     ),
     'c90a04fc' => array(
       'javelin-dom',
       'javelin-dynval',
       'javelin-reactor',
       'javelin-reactornode',
       'javelin-install',
       'javelin-util',
     ),
     'c989ade3' => array(
       'javelin-install',
       'javelin-util',
       'javelin-stratcom',
     ),
     'ca3f91eb' => array(
       'javelin-behavior',
       'javelin-dom',
       'javelin-stratcom',
       'phabricator-phtize',
     ),
     'ccf1cbf8' => array(
       'javelin-install',
       'javelin-dom',
       'javelin-stratcom',
       'javelin-util',
       'phabricator-notification-css',
     ),
     'cf86d16a' => array(
       'javelin-behavior',
       'javelin-dom',
       'javelin-workflow',
       'phabricator-drag-and-drop-file-upload',
     ),
     'd00a795a' => array(
       'phui-theme-css',
     ),
     'd0c516d5' => array(
       'javelin-behavior',
       'javelin-dom',
       'javelin-util',
       'phuix-dropdown-menu',
       'phuix-action-list-view',
       'phuix-action-view',
       'javelin-workflow',
       'phuix-icon-view',
     ),
     'd19198c8' => array(
       'javelin-install',
       'javelin-dom',
       'javelin-util',
       'javelin-dynval',
       'javelin-reactor-dom',
     ),
     'd254d646' => array(
       'javelin-util',
     ),
     'd4505101' => array(
       'javelin-stratcom',
       'javelin-install',
       'javelin-uri',
       'javelin-util',
     ),
     'd4a14807' => array(
       'javelin-install',
       'javelin-dom',
       'javelin-view',
     ),
     'd4eecc63' => array(
       'javelin-behavior',
       'javelin-dom',
       'javelin-stratcom',
     ),
     'd6a7e717' => array(
       'multirow-row-manager',
       'javelin-install',
       'javelin-util',
       'javelin-dom',
       'javelin-stratcom',
       'javelin-json',
       'phabricator-prefab',
     ),
     'd75709e6' => array(
       'javelin-behavior',
       'javelin-workflow',
       'javelin-json',
       'javelin-dom',
       'phabricator-keyboard-shortcut',
     ),
     'd7a74243' => array(
       'javelin-behavior',
       'javelin-stratcom',
       'javelin-workflow',
       'javelin-dom',
       'phabricator-draggable-list',
     ),
     'd835b03a' => array(
       'javelin-behavior',
       'javelin-dom',
       'javelin-util',
       'phabricator-shaped-request',
     ),
     'dbbf48b6' => array(
       'javelin-behavior',
       'javelin-stratcom',
       'javelin-dom',
       'phabricator-busy',
     ),
     'de2e896f' => array(
       'javelin-behavior',
       'javelin-dom',
       'javelin-typeahead',
       'javelin-typeahead-ondemand-source',
       'javelin-dom',
     ),
     'df5e11d2' => array(
       'javelin-install',
     ),
     'e10f8e18' => array(
       'javelin-behavior',
       'javelin-dom',
       'phabricator-prefab',
     ),
     'e15869a8' => array(
       'javelin-install',
       'javelin-dom',
     ),
     'e1d25dfb' => array(
       'javelin-behavior',
       'javelin-stratcom',
       'javelin-workflow',
       'javelin-dom',
       'phabricator-draggable-list',
     ),
     'e1ff79b1' => array(
       'javelin-behavior',
       'javelin-stratcom',
       'javelin-dom',
     ),
     'e292eaf4' => array(
       'javelin-install',
     ),
     'e2e0a072' => array(
       'javelin-behavior',
       'javelin-stratcom',
       'javelin-workflow',
       'javelin-dom',
       'phabricator-draggable-list',
     ),
     'e379b58e' => array(
       'javelin-behavior',
       'javelin-stratcom',
       'javelin-vector',
       'javelin-dom',
       'javelin-uri',
     ),
     'e4232876' => array(
       'javelin-behavior',
       'javelin-dom',
       'javelin-vector',
       'phui-chart-css',
     ),
     'e4cc26b3' => array(
       'javelin-behavior',
       'javelin-dom',
     ),
     'e5339c43' => array(
       'javelin-behavior',
       'javelin-dom',
       'javelin-stratcom',
       'javelin-uri',
     ),
     'e5822781' => array(
       'javelin-behavior',
       'javelin-dom',
       'javelin-json',
       'javelin-workflow',
       'javelin-magical-init',
     ),
     'e67df814' => array(
       'javelin-install',
       'javelin-util',
       'javelin-dom',
       'javelin-typeahead',
       'javelin-tokenizer',
       'javelin-typeahead-preloaded-source',
       'javelin-typeahead-ondemand-source',
       'javelin-dom',
       'javelin-stratcom',
       'javelin-util',
     ),
     'e6e25838' => array(
       'javelin-install',
     ),
     'e9581f08' => array(
       'javelin-behavior',
       'javelin-stratcom',
       'javelin-workflow',
       'javelin-dom',
       'phabricator-draggable-list',
     ),
     'ea681761' => array(
       'javelin-behavior',
       'javelin-aphlict',
       'phabricator-phtize',
       'javelin-dom',
     ),
     'edd1ba66' => array(
       'javelin-behavior',
       'javelin-stratcom',
       'javelin-dom',
       'javelin-uri',
       'phabricator-notification',
     ),
     'edf8a145' => array(
       'javelin-behavior',
       'javelin-uri',
     ),
     'efe49472' => array(
       'javelin-install',
       'javelin-util',
     ),
     'f01586dc' => array(
       'javelin-behavior',
       'javelin-dom',
       'javelin-util',
       'javelin-workflow',
       'javelin-json',
     ),
     'f411b6ae' => array(
       'javelin-behavior',
       'javelin-stratcom',
       'javelin-util',
       'javelin-dom',
       'javelin-request',
       'phabricator-keyboard-shortcut',
     ),
     'f6555212' => array(
       'javelin-install',
       'javelin-reactornode',
       'javelin-util',
       'javelin-reactor',
     ),
     'f7fc67ec' => array(
       'javelin-install',
       'javelin-typeahead',
       'javelin-dom',
       'javelin-request',
       'javelin-typeahead-ondemand-source',
       'javelin-util',
     ),
     'f829edb3' => array(
       'javelin-view',
       'javelin-install',
       'javelin-dom',
     ),
     'f8ba29d7' => array(
       'javelin-behavior',
       'javelin-stratcom',
       'javelin-dom',
       'javelin-mask',
       'javelin-util',
       'phabricator-busy',
     ),
     'fa0f4fc2' => array(
       'javelin-behavior',
       'javelin-dom',
       'javelin-vector',
       'javelin-magical-init',
     ),
     'fb20ac8d' => array(
       'javelin-behavior',
       'javelin-aphlict',
       'javelin-stratcom',
       'javelin-request',
       'javelin-uri',
       'javelin-dom',
       'javelin-json',
       'javelin-router',
       'javelin-util',
       'javelin-leader',
       'javelin-sound',
       'phabricator-notification',
     ),
     'fbe497e7' => array(
       'javelin-behavior',
       'javelin-util',
       'javelin-stratcom',
       'javelin-dom',
       'javelin-vector',
       'javelin-magical-init',
       'javelin-request',
       'javelin-history',
       'javelin-workflow',
       'javelin-mask',
       'javelin-behavior-device',
       'phabricator-keyboard-shortcut',
     ),
     'fc91ab6c' => array(
       'javelin-behavior',
       'javelin-dom',
       'phortune-credit-card-form',
     ),
     'fe287620' => array(
       'javelin-install',
       'javelin-dom',
       'javelin-view-visitor',
       'javelin-util',
     ),
     'fea0eb47' => array(
       'javelin-install',
     ),
   ),
   'packages' => array(
     'core.pkg.css' => array(
       'phabricator-core-css',
       'phabricator-zindex-css',
       'phui-button-css',
       'phabricator-standard-page-view',
       'aphront-dialog-view-css',
       'phui-form-view-css',
       'aphront-panel-view-css',
       'aphront-table-view-css',
       'aphront-tokenizer-control-css',
       'aphront-typeahead-control-css',
       'aphront-list-filter-view-css',
       'phabricator-remarkup-css',
       'syntax-highlighting-css',
+      'syntax-default-css',
       'phui-pager-css',
       'aphront-tooltip-css',
       'phabricator-flag-css',
       'phui-info-view-css',
       'sprite-menu-css',
       'phabricator-main-menu-view',
       'phabricator-notification-css',
       'phabricator-notification-menu-css',
       'lightbox-attachment-css',
       'phui-header-view-css',
       'phabricator-filetree-view-css',
       'phabricator-nav-view-css',
       'phabricator-side-menu-view-css',
       'phui-crumbs-view-css',
       'phui-object-item-list-view-css',
       'global-drag-and-drop-css',
       'phui-spacing-css',
       'phui-form-css',
       'phui-icon-view-css',
       'phui-profile-menu-css',
       'phabricator-application-launch-view-css',
       'phabricator-action-list-view-css',
       'phui-property-list-view-css',
       'phui-tag-view-css',
       'phui-list-view-css',
       'font-fontawesome',
       'phui-font-icon-base-css',
       'phui-box-css',
       'phui-object-box-css',
       'phui-timeline-view-css',
       'sprite-tokens-css',
       'tokens-css',
       'phui-status-list-view-css',
       'phui-feed-story-css',
       'phabricator-feed-css',
       'phabricator-dashboard-css',
       'aphront-multi-column-view-css',
       'conpherence-durable-column-view',
     ),
     'core.pkg.js' => array(
       'javelin-util',
       'javelin-install',
       'javelin-event',
       'javelin-stratcom',
       'javelin-behavior',
       'javelin-resource',
       'javelin-request',
       'javelin-vector',
       'javelin-dom',
       'javelin-json',
       'javelin-uri',
       'javelin-workflow',
       'javelin-mask',
       'javelin-typeahead',
       'javelin-typeahead-normalizer',
       'javelin-typeahead-source',
       'javelin-typeahead-preloaded-source',
       'javelin-typeahead-ondemand-source',
       'javelin-tokenizer',
       'javelin-history',
       'javelin-router',
       'javelin-routable',
       'javelin-behavior-aphront-basic-tokenizer',
       'javelin-behavior-workflow',
       'javelin-behavior-aphront-form-disable-on-submit',
       'phabricator-keyboard-shortcut-manager',
       'phabricator-keyboard-shortcut',
       'javelin-behavior-phabricator-keyboard-shortcuts',
       'javelin-behavior-refresh-csrf',
       'javelin-behavior-phabricator-watch-anchor',
       'javelin-behavior-phabricator-autofocus',
       'phuix-dropdown-menu',
       'phuix-action-list-view',
       'phuix-action-view',
       'phabricator-phtize',
       'javelin-behavior-phabricator-oncopy',
       'phabricator-tooltip',
       'javelin-behavior-phabricator-tooltips',
       'phabricator-prefab',
       'javelin-behavior-device',
       'javelin-behavior-toggle-class',
       'javelin-behavior-lightbox-attachments',
       'phabricator-busy',
       'javelin-aphlict',
       'phabricator-notification',
       'javelin-behavior-aphlict-listen',
       'javelin-behavior-phabricator-search-typeahead',
       'javelin-behavior-aphlict-dropdown',
       'javelin-behavior-history-install',
       'javelin-behavior-phabricator-gesture',
       'javelin-behavior-phabricator-active-nav',
       'javelin-behavior-phabricator-nav',
       'javelin-behavior-phabricator-remarkup-assist',
       'phabricator-textareautils',
       'phabricator-file-upload',
       'javelin-behavior-global-drag-and-drop',
       'javelin-behavior-phabricator-reveal-content',
       'phui-hovercard',
       'javelin-behavior-phui-hovercards',
       'javelin-color',
       'javelin-fx',
       'phabricator-draggable-list',
       'javelin-behavior-phabricator-transaction-list',
       'javelin-behavior-phabricator-show-older-transactions',
       'javelin-behavior-phui-dropdown-menu',
       'javelin-behavior-doorkeeper-tag',
       'phabricator-title',
       'javelin-leader',
       'javelin-websocket',
       'javelin-behavior-dashboard-async-panel',
       'javelin-behavior-dashboard-tab-panel',
       'javelin-quicksand',
       'javelin-behavior-quicksand-blacklist',
       'javelin-behavior-high-security-warning',
       'javelin-behavior-read-only-warning',
       'javelin-scrollbar',
       'javelin-behavior-scrollbar',
       'javelin-behavior-durable-column',
       'conpherence-thread-manager',
     ),
     'darkconsole.pkg.js' => array(
       'javelin-behavior-dark-console',
       'javelin-behavior-error-log',
     ),
     'differential.pkg.css' => array(
       'differential-core-view-css',
       'differential-changeset-view-css',
       'differential-revision-history-css',
       'differential-revision-list-css',
       'differential-table-of-contents-css',
       'differential-revision-comment-css',
       'differential-revision-add-comment-css',
       'phabricator-object-selector-css',
       'phabricator-content-source-view-css',
       'inline-comment-summary-css',
       'phui-inline-comment-view-css',
     ),
     'differential.pkg.js' => array(
       'phabricator-drag-and-drop-file-upload',
       'phabricator-shaped-request',
       'javelin-behavior-differential-feedback-preview',
       'javelin-behavior-differential-edit-inline-comments',
       'javelin-behavior-differential-populate',
       'javelin-behavior-differential-diff-radios',
       'javelin-behavior-differential-comment-jump',
       'javelin-behavior-differential-add-reviewers-and-ccs',
       'javelin-behavior-differential-keyboard-navigation',
       'javelin-behavior-aphront-drag-and-drop-textarea',
       'javelin-behavior-phabricator-object-selector',
       'javelin-behavior-repository-crossreference',
       'javelin-behavior-load-blame',
       'differential-inline-comment-editor',
       'javelin-behavior-differential-dropdown-menus',
       'javelin-behavior-differential-toggle-files',
       'javelin-behavior-differential-user-select',
       'javelin-behavior-aphront-more',
       'changeset-view-manager',
     ),
     'diffusion.pkg.css' => array(
       'diffusion-icons-css',
     ),
     'diffusion.pkg.js' => array(
       'javelin-behavior-diffusion-pull-lastmodified',
       'javelin-behavior-diffusion-commit-graph',
       'javelin-behavior-audit-preview',
     ),
     'maniphest.pkg.css' => array(
       'maniphest-task-summary-css',
     ),
     'maniphest.pkg.js' => array(
       'javelin-behavior-maniphest-batch-selector',
       'javelin-behavior-maniphest-subpriority-editor',
       'javelin-behavior-maniphest-list-editor',
     ),
   ),
 );
diff --git a/resources/celerity/packages.php b/resources/celerity/packages.php
index df6c2ed72..fa19f5009 100644
--- a/resources/celerity/packages.php
+++ b/resources/celerity/packages.php
@@ -1,199 +1,200 @@
 <?php
 
 return array(
   'core.pkg.js' => array(
     'javelin-util',
     'javelin-install',
     'javelin-event',
     'javelin-stratcom',
     'javelin-behavior',
     'javelin-resource',
     'javelin-request',
     'javelin-vector',
     'javelin-dom',
     'javelin-json',
     'javelin-uri',
     'javelin-workflow',
     'javelin-mask',
     'javelin-typeahead',
     'javelin-typeahead-normalizer',
     'javelin-typeahead-source',
     'javelin-typeahead-preloaded-source',
     'javelin-typeahead-ondemand-source',
     'javelin-tokenizer',
     'javelin-history',
     'javelin-router',
     'javelin-routable',
     'javelin-behavior-aphront-basic-tokenizer',
     'javelin-behavior-workflow',
     'javelin-behavior-aphront-form-disable-on-submit',
     'phabricator-keyboard-shortcut-manager',
     'phabricator-keyboard-shortcut',
     'javelin-behavior-phabricator-keyboard-shortcuts',
     'javelin-behavior-refresh-csrf',
     'javelin-behavior-phabricator-watch-anchor',
     'javelin-behavior-phabricator-autofocus',
     'phuix-dropdown-menu',
     'phuix-action-list-view',
     'phuix-action-view',
     'phabricator-phtize',
     'javelin-behavior-phabricator-oncopy',
     'phabricator-tooltip',
     'javelin-behavior-phabricator-tooltips',
     'phabricator-prefab',
     'javelin-behavior-device',
     'javelin-behavior-toggle-class',
     'javelin-behavior-lightbox-attachments',
     'phabricator-busy',
     'javelin-aphlict',
     'phabricator-notification',
     'javelin-behavior-aphlict-listen',
     'javelin-behavior-phabricator-search-typeahead',
     'javelin-behavior-aphlict-dropdown',
     'javelin-behavior-history-install',
     'javelin-behavior-phabricator-gesture',
     'javelin-behavior-phabricator-active-nav',
     'javelin-behavior-phabricator-nav',
     'javelin-behavior-phabricator-remarkup-assist',
     'phabricator-textareautils',
     'phabricator-file-upload',
     'javelin-behavior-global-drag-and-drop',
     'javelin-behavior-phabricator-reveal-content',
     'phui-hovercard',
     'javelin-behavior-phui-hovercards',
     'javelin-color',
     'javelin-fx',
     'phabricator-draggable-list',
     'javelin-behavior-phabricator-transaction-list',
     'javelin-behavior-phabricator-show-older-transactions',
     'javelin-behavior-phui-dropdown-menu',
     'javelin-behavior-doorkeeper-tag',
     'phabricator-title',
     'javelin-leader',
     'javelin-websocket',
     'javelin-behavior-dashboard-async-panel',
     'javelin-behavior-dashboard-tab-panel',
     'javelin-quicksand',
     'javelin-behavior-quicksand-blacklist',
     'javelin-behavior-high-security-warning',
     'javelin-behavior-read-only-warning',
     'javelin-scrollbar',
     'javelin-behavior-scrollbar',
     'javelin-behavior-durable-column',
     'conpherence-thread-manager',
   ),
   'core.pkg.css' => array(
     'phabricator-core-css',
     'phabricator-zindex-css',
     'phui-button-css',
     'phabricator-standard-page-view',
     'aphront-dialog-view-css',
     'phui-form-view-css',
     'aphront-panel-view-css',
     'aphront-table-view-css',
     'aphront-tokenizer-control-css',
     'aphront-typeahead-control-css',
     'aphront-list-filter-view-css',
 
     'phabricator-remarkup-css',
     'syntax-highlighting-css',
+    'syntax-default-css',
     'phui-pager-css',
     'aphront-tooltip-css',
     'phabricator-flag-css',
     'phui-info-view-css',
     'sprite-menu-css',
 
     'phabricator-main-menu-view',
     'phabricator-notification-css',
     'phabricator-notification-menu-css',
     'lightbox-attachment-css',
     'phui-header-view-css',
     'phabricator-filetree-view-css',
     'phabricator-nav-view-css',
     'phabricator-side-menu-view-css',
     'phui-crumbs-view-css',
     'phui-object-item-list-view-css',
     'global-drag-and-drop-css',
     'phui-spacing-css',
     'phui-form-css',
     'phui-icon-view-css',
     'phui-profile-menu-css',
 
     'phabricator-application-launch-view-css',
     'phabricator-action-list-view-css',
     'phui-property-list-view-css',
     'phui-tag-view-css',
     'phui-list-view-css',
 
     'font-fontawesome',
     'phui-font-icon-base-css',
     'phui-box-css',
     'phui-object-box-css',
     'phui-timeline-view-css',
     'sprite-tokens-css',
     'tokens-css',
     'phui-status-list-view-css',
 
     'phui-feed-story-css',
     'phabricator-feed-css',
     'phabricator-dashboard-css',
     'aphront-multi-column-view-css',
     'conpherence-durable-column-view',
   ),
   'differential.pkg.css' => array(
     'differential-core-view-css',
     'differential-changeset-view-css',
     'differential-revision-history-css',
     'differential-revision-list-css',
     'differential-table-of-contents-css',
     'differential-revision-comment-css',
     'differential-revision-add-comment-css',
     'phabricator-object-selector-css',
     'phabricator-content-source-view-css',
     'inline-comment-summary-css',
     'phui-inline-comment-view-css',
   ),
   'differential.pkg.js' => array(
     'phabricator-drag-and-drop-file-upload',
     'phabricator-shaped-request',
 
     'javelin-behavior-differential-feedback-preview',
     'javelin-behavior-differential-edit-inline-comments',
     'javelin-behavior-differential-populate',
     'javelin-behavior-differential-diff-radios',
     'javelin-behavior-differential-comment-jump',
     'javelin-behavior-differential-add-reviewers-and-ccs',
     'javelin-behavior-differential-keyboard-navigation',
     'javelin-behavior-aphront-drag-and-drop-textarea',
     'javelin-behavior-phabricator-object-selector',
     'javelin-behavior-repository-crossreference',
     'javelin-behavior-load-blame',
 
     'differential-inline-comment-editor',
     'javelin-behavior-differential-dropdown-menus',
     'javelin-behavior-differential-toggle-files',
     'javelin-behavior-differential-user-select',
     'javelin-behavior-aphront-more',
     'changeset-view-manager',
   ),
   'diffusion.pkg.css' => array(
     'diffusion-icons-css',
   ),
   'diffusion.pkg.js' => array(
     'javelin-behavior-diffusion-pull-lastmodified',
     'javelin-behavior-diffusion-commit-graph',
     'javelin-behavior-audit-preview',
   ),
   'maniphest.pkg.css' => array(
     'maniphest-task-summary-css',
   ),
   'maniphest.pkg.js' => array(
     'javelin-behavior-maniphest-batch-selector',
     'javelin-behavior-maniphest-subpriority-editor',
     'javelin-behavior-maniphest-list-editor',
   ),
   'darkconsole.pkg.js' => array(
     'javelin-behavior-dark-console',
     'javelin-behavior-error-log',
   ),
 );
diff --git a/resources/sql/autopatches/20160112.repo.02.uri.index.php b/resources/sql/autopatches/20160112.repo.02.uri.index.php
index 7a11be317..e9cd06136 100644
--- a/resources/sql/autopatches/20160112.repo.02.uri.index.php
+++ b/resources/sql/autopatches/20160112.repo.02.uri.index.php
@@ -1,7 +1,4 @@
 <?php
 
-$table = new PhabricatorRepository();
-
-foreach (new LiskMigrationIterator($table) as $repo) {
-  $repo->updateURIIndex();
-}
+// A later patch ("20160510.repo.01.uriindex.php") performs an identical
+// regeneration of the index, so we no longer need to do it here.
diff --git a/resources/sql/autopatches/20160503.repo.01.lpath.sql b/resources/sql/autopatches/20160503.repo.01.lpath.sql
new file mode 100644
index 000000000..437dfb131
--- /dev/null
+++ b/resources/sql/autopatches/20160503.repo.01.lpath.sql
@@ -0,0 +1,2 @@
+ALTER TABLE {$NAMESPACE}_repository.repository
+  ADD localPath VARCHAR(128) COLLATE {$COLLATE_TEXT};
diff --git a/resources/sql/autopatches/20160503.repo.02.lpathkey.sql b/resources/sql/autopatches/20160503.repo.02.lpathkey.sql
new file mode 100644
index 000000000..b630d87c2
--- /dev/null
+++ b/resources/sql/autopatches/20160503.repo.02.lpathkey.sql
@@ -0,0 +1,2 @@
+ALTER TABLE {$NAMESPACE}_repository.repository
+  ADD UNIQUE KEY `key_local` (localPath);
diff --git a/resources/sql/autopatches/20160503.repo.03.lpathmigrate.php b/resources/sql/autopatches/20160503.repo.03.lpathmigrate.php
new file mode 100644
index 000000000..d810b074c
--- /dev/null
+++ b/resources/sql/autopatches/20160503.repo.03.lpathmigrate.php
@@ -0,0 +1,57 @@
+<?php
+
+$table = new PhabricatorRepository();
+$conn_w = $table->establishConnection('w');
+
+$default_path = PhabricatorEnv::getEnvConfig('repository.default-local-path');
+$default_path = rtrim($default_path, '/');
+
+foreach (new LiskMigrationIterator($table) as $repository) {
+  $local_path = $repository->getLocalPath();
+  if (strlen($local_path)) {
+    // Repository already has a modern, unique local path.
+    continue;
+  }
+
+  $local_path = $repository->getDetail('local-path');
+  if (!strlen($local_path)) {
+    // Repository does not have a local path using the older format.
+    continue;
+  }
+
+  $random = Filesystem::readRandomCharacters(8);
+
+  // Try the configured path first, then a default path, then a path with some
+  // random noise.
+  $paths = array(
+    $local_path,
+    $default_path.'/'.$repository->getID().'/',
+    $default_path.'/'.$repository->getID().'-'.$random.'/',
+  );
+
+  foreach ($paths as $path) {
+    // Set, then get the path to normalize it.
+    $repository->setLocalPath($path);
+    $path = $repository->getLocalPath();
+
+    try {
+      queryfx(
+        $conn_w,
+        'UPDATE %T SET localPath = %s WHERE id = %d',
+        $table->getTableName(),
+        $path,
+        $repository->getID());
+
+      echo tsprintf(
+        "%s\n",
+        pht(
+          'Assigned repository "%s" to local path "%s".',
+          $repository->getDisplayName(),
+          $path));
+
+      break;
+    } catch (AphrontDuplicateKeyQueryException $ex) {
+      // Ignore, try the next one.
+    }
+  }
+}
diff --git a/resources/sql/autopatches/20160503.repo.04.mirrormigrate.php b/resources/sql/autopatches/20160503.repo.04.mirrormigrate.php
new file mode 100644
index 000000000..9613e4354
--- /dev/null
+++ b/resources/sql/autopatches/20160503.repo.04.mirrormigrate.php
@@ -0,0 +1,38 @@
+<?php
+
+$table = new PhabricatorRepository();
+$conn_w = $table->establishConnection('w');
+
+$mirrors = queryfx_all(
+  $conn_w,
+  'SELECT * FROM %T',
+  'repository_mirror');
+
+foreach ($mirrors as $mirror) {
+  $repository_phid = $mirror['repositoryPHID'];
+  $uri = $mirror['remoteURI'];
+
+  $already_exists = id(new PhabricatorRepositoryURI())->loadOneWhere(
+    'repositoryPHID = %s AND uri = %s',
+    $repository_phid,
+    $uri);
+  if ($already_exists) {
+    // Decline to migrate stuff that looks like it was already migrated.
+    continue;
+  }
+
+  $new_uri = PhabricatorRepositoryURI::initializeNewURI()
+    ->setIOType(PhabricatorRepositoryURI::IO_MIRROR)
+    ->setRepositoryPHID($repository_phid)
+    ->setURI($uri)
+    ->setCredentialPHID($mirror['credentialPHID'])
+    ->setDateCreated($mirror['dateCreated'])
+    ->setDateModified($mirror['dateModified'])
+    ->save();
+
+  echo tsprintf(
+    "%s\n",
+    pht(
+      'Migrated mirror "%s".',
+      $uri));
+}
diff --git a/resources/sql/autopatches/20160503.repo.05.urimigrate.php b/resources/sql/autopatches/20160503.repo.05.urimigrate.php
new file mode 100644
index 000000000..d48afd4f6
--- /dev/null
+++ b/resources/sql/autopatches/20160503.repo.05.urimigrate.php
@@ -0,0 +1,82 @@
+<?php
+
+$table = new PhabricatorRepository();
+$conn_w = $table->establishConnection('w');
+
+foreach (new LiskMigrationIterator($table) as $repository) {
+  $uris = array();
+
+  $serve_http = $repository->getDetail('serve-over-http');
+  $http_io = PhabricatorRepositoryURI::IO_DEFAULT;
+  $disable_http = false;
+  switch ($serve_http) {
+    case 'readwrite':
+      break;
+    case 'readonly':
+      $http_io = PhabricatorRepositoryURI::IO_READ;
+      break;
+    case 'off':
+    default:
+      $disable_http = true;
+      break;
+  }
+
+  $serve_ssh = $repository->getDetail('serve-over-ssh');
+  $ssh_io = PhabricatorRepositoryURI::IO_DEFAULT;
+  $disable_ssh = false;
+  switch ($serve_ssh) {
+    case 'readwrite':
+      break;
+    case 'readonly':
+      $ssh_io = PhabricatorRepositoryURI::IO_READ;
+      break;
+    case 'off':
+    default:
+      $disable_ssh = true;
+      break;
+  }
+
+  $uris = $repository->newBuiltinURIs();
+
+  foreach ($uris as $uri) {
+    $builtin_protocol = $uri->getBuiltinProtocol();
+    if ($builtin_protocol == PhabricatorRepositoryURI::BUILTIN_PROTOCOL_SSH) {
+      $uri->setIsDisabled((int)$disable_ssh);
+      $uri->setIoType($ssh_io);
+    } else {
+      $uri->setIsDisabled((int)$disable_http);
+      $uri->setIoType($http_io);
+    }
+  }
+
+  if (!$repository->isHosted()) {
+    $remote_uri = $repository->getDetail('remote-uri');
+    if (strlen($remote_uri)) {
+      $uris[] = PhabricatorRepositoryURI::initializeNewURI()
+        ->setRepositoryPHID($repository->getPHID())
+        ->attachRepository($repository)
+        ->setURI($remote_uri)
+        ->setCredentialPHID($repository->getCredentialPHID())
+        ->setIOType(PhabricatorRepositoryURI::IO_OBSERVE);
+    }
+  }
+
+  foreach ($uris as $uri) {
+    $already_exists = id(new PhabricatorRepositoryURI())->loadOneWhere(
+      'repositoryPHID = %s AND uri = %s LIMIT 1',
+      $repository->getPHID(),
+      $uri->getURI());
+    if ($already_exists) {
+      continue;
+    }
+
+    $uri->save();
+
+    echo tsprintf(
+      "%s\n",
+      pht(
+        'Migrated URI "%s" for repository "%s".',
+        $uri->getURI(),
+        $repository->getDisplayName()));
+  }
+}
diff --git a/resources/sql/autopatches/20160510.repo.01.uriindex.php b/resources/sql/autopatches/20160510.repo.01.uriindex.php
new file mode 100644
index 000000000..191985af4
--- /dev/null
+++ b/resources/sql/autopatches/20160510.repo.01.uriindex.php
@@ -0,0 +1,10 @@
+<?php
+
+$repos = id(new PhabricatorRepositoryQuery())
+  ->setViewer(PhabricatorUser::getOmnipotentUser())
+  ->needURIs(true)
+  ->execute();
+
+foreach ($repos as $repo) {
+  $repo->updateURIIndex();
+}
diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php
index b0ffd4eef..e11c20a33 100644
--- a/src/__phutil_library_map__.php
+++ b/src/__phutil_library_map__.php
@@ -1,9007 +1,8994 @@
 <?php
 
 /**
  * This file is automatically generated. Use 'arc liberate' to rebuild it.
  *
  * @generated
  * @phutil-library-version 2
  */
 phutil_register_library_map(array(
   '__library_version__' => 2,
   'class' => array(
     'AlmanacAddress' => 'applications/almanac/util/AlmanacAddress.php',
     'AlmanacBinding' => 'applications/almanac/storage/AlmanacBinding.php',
     'AlmanacBindingDisableController' => 'applications/almanac/controller/AlmanacBindingDisableController.php',
     'AlmanacBindingEditController' => 'applications/almanac/controller/AlmanacBindingEditController.php',
     'AlmanacBindingEditor' => 'applications/almanac/editor/AlmanacBindingEditor.php',
     'AlmanacBindingPHIDType' => 'applications/almanac/phid/AlmanacBindingPHIDType.php',
     'AlmanacBindingPropertyEditEngine' => 'applications/almanac/editor/AlmanacBindingPropertyEditEngine.php',
     'AlmanacBindingQuery' => 'applications/almanac/query/AlmanacBindingQuery.php',
     'AlmanacBindingTableView' => 'applications/almanac/view/AlmanacBindingTableView.php',
     'AlmanacBindingTransaction' => 'applications/almanac/storage/AlmanacBindingTransaction.php',
     'AlmanacBindingTransactionQuery' => 'applications/almanac/query/AlmanacBindingTransactionQuery.php',
     'AlmanacBindingViewController' => 'applications/almanac/controller/AlmanacBindingViewController.php',
     'AlmanacBindingsSearchEngineAttachment' => 'applications/almanac/engineextension/AlmanacBindingsSearchEngineAttachment.php',
     'AlmanacClusterDatabaseServiceType' => 'applications/almanac/servicetype/AlmanacClusterDatabaseServiceType.php',
     'AlmanacClusterRepositoryServiceType' => 'applications/almanac/servicetype/AlmanacClusterRepositoryServiceType.php',
     'AlmanacClusterServiceType' => 'applications/almanac/servicetype/AlmanacClusterServiceType.php',
     'AlmanacConsoleController' => 'applications/almanac/controller/AlmanacConsoleController.php',
     'AlmanacController' => 'applications/almanac/controller/AlmanacController.php',
     'AlmanacCreateDevicesCapability' => 'applications/almanac/capability/AlmanacCreateDevicesCapability.php',
     'AlmanacCreateNamespacesCapability' => 'applications/almanac/capability/AlmanacCreateNamespacesCapability.php',
     'AlmanacCreateNetworksCapability' => 'applications/almanac/capability/AlmanacCreateNetworksCapability.php',
     'AlmanacCreateServicesCapability' => 'applications/almanac/capability/AlmanacCreateServicesCapability.php',
     'AlmanacCustomServiceType' => 'applications/almanac/servicetype/AlmanacCustomServiceType.php',
     'AlmanacDAO' => 'applications/almanac/storage/AlmanacDAO.php',
     'AlmanacDevice' => 'applications/almanac/storage/AlmanacDevice.php',
     'AlmanacDeviceController' => 'applications/almanac/controller/AlmanacDeviceController.php',
     'AlmanacDeviceEditController' => 'applications/almanac/controller/AlmanacDeviceEditController.php',
     'AlmanacDeviceEditEngine' => 'applications/almanac/editor/AlmanacDeviceEditEngine.php',
     'AlmanacDeviceEditor' => 'applications/almanac/editor/AlmanacDeviceEditor.php',
     'AlmanacDeviceListController' => 'applications/almanac/controller/AlmanacDeviceListController.php',
     'AlmanacDeviceNameNgrams' => 'applications/almanac/storage/AlmanacDeviceNameNgrams.php',
     'AlmanacDevicePHIDType' => 'applications/almanac/phid/AlmanacDevicePHIDType.php',
     'AlmanacDevicePropertyEditEngine' => 'applications/almanac/editor/AlmanacDevicePropertyEditEngine.php',
     'AlmanacDeviceQuery' => 'applications/almanac/query/AlmanacDeviceQuery.php',
     'AlmanacDeviceSearchConduitAPIMethod' => 'applications/almanac/conduit/AlmanacDeviceSearchConduitAPIMethod.php',
     'AlmanacDeviceSearchEngine' => 'applications/almanac/query/AlmanacDeviceSearchEngine.php',
     'AlmanacDeviceTransaction' => 'applications/almanac/storage/AlmanacDeviceTransaction.php',
     'AlmanacDeviceTransactionQuery' => 'applications/almanac/query/AlmanacDeviceTransactionQuery.php',
     'AlmanacDeviceViewController' => 'applications/almanac/controller/AlmanacDeviceViewController.php',
     'AlmanacDrydockPoolServiceType' => 'applications/almanac/servicetype/AlmanacDrydockPoolServiceType.php',
     'AlmanacEditor' => 'applications/almanac/editor/AlmanacEditor.php',
     'AlmanacInterface' => 'applications/almanac/storage/AlmanacInterface.php',
     'AlmanacInterfaceDatasource' => 'applications/almanac/typeahead/AlmanacInterfaceDatasource.php',
     'AlmanacInterfaceDeleteController' => 'applications/almanac/controller/AlmanacInterfaceDeleteController.php',
     'AlmanacInterfaceEditController' => 'applications/almanac/controller/AlmanacInterfaceEditController.php',
     'AlmanacInterfacePHIDType' => 'applications/almanac/phid/AlmanacInterfacePHIDType.php',
     'AlmanacInterfaceQuery' => 'applications/almanac/query/AlmanacInterfaceQuery.php',
     'AlmanacInterfaceTableView' => 'applications/almanac/view/AlmanacInterfaceTableView.php',
     'AlmanacKeys' => 'applications/almanac/util/AlmanacKeys.php',
     'AlmanacManageClusterServicesCapability' => 'applications/almanac/capability/AlmanacManageClusterServicesCapability.php',
     'AlmanacManagementRegisterWorkflow' => 'applications/almanac/management/AlmanacManagementRegisterWorkflow.php',
     'AlmanacManagementTrustKeyWorkflow' => 'applications/almanac/management/AlmanacManagementTrustKeyWorkflow.php',
     'AlmanacManagementUntrustKeyWorkflow' => 'applications/almanac/management/AlmanacManagementUntrustKeyWorkflow.php',
     'AlmanacManagementWorkflow' => 'applications/almanac/management/AlmanacManagementWorkflow.php',
     'AlmanacNames' => 'applications/almanac/util/AlmanacNames.php',
     'AlmanacNamesTestCase' => 'applications/almanac/util/__tests__/AlmanacNamesTestCase.php',
     'AlmanacNamespace' => 'applications/almanac/storage/AlmanacNamespace.php',
     'AlmanacNamespaceController' => 'applications/almanac/controller/AlmanacNamespaceController.php',
     'AlmanacNamespaceEditController' => 'applications/almanac/controller/AlmanacNamespaceEditController.php',
     'AlmanacNamespaceEditEngine' => 'applications/almanac/editor/AlmanacNamespaceEditEngine.php',
     'AlmanacNamespaceEditor' => 'applications/almanac/editor/AlmanacNamespaceEditor.php',
     'AlmanacNamespaceListController' => 'applications/almanac/controller/AlmanacNamespaceListController.php',
     'AlmanacNamespaceNameNgrams' => 'applications/almanac/storage/AlmanacNamespaceNameNgrams.php',
     'AlmanacNamespacePHIDType' => 'applications/almanac/phid/AlmanacNamespacePHIDType.php',
     'AlmanacNamespaceQuery' => 'applications/almanac/query/AlmanacNamespaceQuery.php',
     'AlmanacNamespaceSearchEngine' => 'applications/almanac/query/AlmanacNamespaceSearchEngine.php',
     'AlmanacNamespaceTransaction' => 'applications/almanac/storage/AlmanacNamespaceTransaction.php',
     'AlmanacNamespaceTransactionQuery' => 'applications/almanac/query/AlmanacNamespaceTransactionQuery.php',
     'AlmanacNamespaceViewController' => 'applications/almanac/controller/AlmanacNamespaceViewController.php',
     'AlmanacNetwork' => 'applications/almanac/storage/AlmanacNetwork.php',
     'AlmanacNetworkController' => 'applications/almanac/controller/AlmanacNetworkController.php',
     'AlmanacNetworkEditController' => 'applications/almanac/controller/AlmanacNetworkEditController.php',
     'AlmanacNetworkEditEngine' => 'applications/almanac/editor/AlmanacNetworkEditEngine.php',
     'AlmanacNetworkEditor' => 'applications/almanac/editor/AlmanacNetworkEditor.php',
     'AlmanacNetworkListController' => 'applications/almanac/controller/AlmanacNetworkListController.php',
     'AlmanacNetworkNameNgrams' => 'applications/almanac/storage/AlmanacNetworkNameNgrams.php',
     'AlmanacNetworkPHIDType' => 'applications/almanac/phid/AlmanacNetworkPHIDType.php',
     'AlmanacNetworkQuery' => 'applications/almanac/query/AlmanacNetworkQuery.php',
     'AlmanacNetworkSearchEngine' => 'applications/almanac/query/AlmanacNetworkSearchEngine.php',
     'AlmanacNetworkTransaction' => 'applications/almanac/storage/AlmanacNetworkTransaction.php',
     'AlmanacNetworkTransactionQuery' => 'applications/almanac/query/AlmanacNetworkTransactionQuery.php',
     'AlmanacNetworkViewController' => 'applications/almanac/controller/AlmanacNetworkViewController.php',
     'AlmanacPropertiesDestructionEngineExtension' => 'applications/almanac/engineextension/AlmanacPropertiesDestructionEngineExtension.php',
     'AlmanacPropertiesSearchEngineAttachment' => 'applications/almanac/engineextension/AlmanacPropertiesSearchEngineAttachment.php',
     'AlmanacProperty' => 'applications/almanac/storage/AlmanacProperty.php',
     'AlmanacPropertyController' => 'applications/almanac/controller/AlmanacPropertyController.php',
     'AlmanacPropertyDeleteController' => 'applications/almanac/controller/AlmanacPropertyDeleteController.php',
     'AlmanacPropertyEditController' => 'applications/almanac/controller/AlmanacPropertyEditController.php',
     'AlmanacPropertyEditEngine' => 'applications/almanac/editor/AlmanacPropertyEditEngine.php',
     'AlmanacPropertyInterface' => 'applications/almanac/property/AlmanacPropertyInterface.php',
     'AlmanacPropertyQuery' => 'applications/almanac/query/AlmanacPropertyQuery.php',
     'AlmanacQuery' => 'applications/almanac/query/AlmanacQuery.php',
     'AlmanacSchemaSpec' => 'applications/almanac/storage/AlmanacSchemaSpec.php',
     'AlmanacSearchEngineAttachment' => 'applications/almanac/engineextension/AlmanacSearchEngineAttachment.php',
     'AlmanacService' => 'applications/almanac/storage/AlmanacService.php',
     'AlmanacServiceController' => 'applications/almanac/controller/AlmanacServiceController.php',
     'AlmanacServiceDatasource' => 'applications/almanac/typeahead/AlmanacServiceDatasource.php',
     'AlmanacServiceEditController' => 'applications/almanac/controller/AlmanacServiceEditController.php',
     'AlmanacServiceEditEngine' => 'applications/almanac/editor/AlmanacServiceEditEngine.php',
     'AlmanacServiceEditor' => 'applications/almanac/editor/AlmanacServiceEditor.php',
     'AlmanacServiceListController' => 'applications/almanac/controller/AlmanacServiceListController.php',
     'AlmanacServiceNameNgrams' => 'applications/almanac/storage/AlmanacServiceNameNgrams.php',
     'AlmanacServicePHIDType' => 'applications/almanac/phid/AlmanacServicePHIDType.php',
     'AlmanacServicePropertyEditEngine' => 'applications/almanac/editor/AlmanacServicePropertyEditEngine.php',
     'AlmanacServiceQuery' => 'applications/almanac/query/AlmanacServiceQuery.php',
     'AlmanacServiceSearchConduitAPIMethod' => 'applications/almanac/conduit/AlmanacServiceSearchConduitAPIMethod.php',
     'AlmanacServiceSearchEngine' => 'applications/almanac/query/AlmanacServiceSearchEngine.php',
     'AlmanacServiceTransaction' => 'applications/almanac/storage/AlmanacServiceTransaction.php',
     'AlmanacServiceTransactionQuery' => 'applications/almanac/query/AlmanacServiceTransactionQuery.php',
     'AlmanacServiceType' => 'applications/almanac/servicetype/AlmanacServiceType.php',
     'AlmanacServiceTypeDatasource' => 'applications/almanac/typeahead/AlmanacServiceTypeDatasource.php',
     'AlmanacServiceTypeTestCase' => 'applications/almanac/servicetype/__tests__/AlmanacServiceTypeTestCase.php',
     'AlmanacServiceViewController' => 'applications/almanac/controller/AlmanacServiceViewController.php',
     'AlmanacTransaction' => 'applications/almanac/storage/AlmanacTransaction.php',
     'AphlictDropdownDataQuery' => 'applications/aphlict/query/AphlictDropdownDataQuery.php',
     'Aphront304Response' => 'aphront/response/Aphront304Response.php',
     'Aphront400Response' => 'aphront/response/Aphront400Response.php',
     'Aphront403Response' => 'aphront/response/Aphront403Response.php',
     'Aphront404Response' => 'aphront/response/Aphront404Response.php',
     'AphrontAjaxResponse' => 'aphront/response/AphrontAjaxResponse.php',
     'AphrontApplicationConfiguration' => 'aphront/configuration/AphrontApplicationConfiguration.php',
     'AphrontBarView' => 'view/widget/bars/AphrontBarView.php',
     'AphrontBoolHTTPParameterType' => 'aphront/httpparametertype/AphrontBoolHTTPParameterType.php',
     'AphrontCSRFException' => 'aphront/exception/AphrontCSRFException.php',
     'AphrontCalendarEventView' => 'applications/calendar/view/AphrontCalendarEventView.php',
     'AphrontController' => 'aphront/AphrontController.php',
     'AphrontCursorPagerView' => 'view/control/AphrontCursorPagerView.php',
     'AphrontDefaultApplicationConfiguration' => 'aphront/configuration/AphrontDefaultApplicationConfiguration.php',
     'AphrontDialogResponse' => 'aphront/response/AphrontDialogResponse.php',
     'AphrontDialogView' => 'view/AphrontDialogView.php',
     'AphrontEpochHTTPParameterType' => 'aphront/httpparametertype/AphrontEpochHTTPParameterType.php',
     'AphrontException' => 'aphront/exception/AphrontException.php',
     'AphrontFileResponse' => 'aphront/response/AphrontFileResponse.php',
     'AphrontFormCheckboxControl' => 'view/form/control/AphrontFormCheckboxControl.php',
     'AphrontFormControl' => 'view/form/control/AphrontFormControl.php',
     'AphrontFormDateControl' => 'view/form/control/AphrontFormDateControl.php',
     'AphrontFormDateControlValue' => 'view/form/control/AphrontFormDateControlValue.php',
     'AphrontFormDividerControl' => 'view/form/control/AphrontFormDividerControl.php',
     'AphrontFormFileControl' => 'view/form/control/AphrontFormFileControl.php',
     'AphrontFormHandlesControl' => 'view/form/control/AphrontFormHandlesControl.php',
     'AphrontFormMarkupControl' => 'view/form/control/AphrontFormMarkupControl.php',
     'AphrontFormPasswordControl' => 'view/form/control/AphrontFormPasswordControl.php',
     'AphrontFormPolicyControl' => 'view/form/control/AphrontFormPolicyControl.php',
     'AphrontFormRadioButtonControl' => 'view/form/control/AphrontFormRadioButtonControl.php',
     'AphrontFormRecaptchaControl' => 'view/form/control/AphrontFormRecaptchaControl.php',
     'AphrontFormSelectControl' => 'view/form/control/AphrontFormSelectControl.php',
     'AphrontFormStaticControl' => 'view/form/control/AphrontFormStaticControl.php',
     'AphrontFormSubmitControl' => 'view/form/control/AphrontFormSubmitControl.php',
     'AphrontFormTextAreaControl' => 'view/form/control/AphrontFormTextAreaControl.php',
     'AphrontFormTextControl' => 'view/form/control/AphrontFormTextControl.php',
     'AphrontFormTextWithSubmitControl' => 'view/form/control/AphrontFormTextWithSubmitControl.php',
     'AphrontFormTokenizerControl' => 'view/form/control/AphrontFormTokenizerControl.php',
     'AphrontFormTypeaheadControl' => 'view/form/control/AphrontFormTypeaheadControl.php',
     'AphrontFormView' => 'view/form/AphrontFormView.php',
     'AphrontGlyphBarView' => 'view/widget/bars/AphrontGlyphBarView.php',
     'AphrontHTMLResponse' => 'aphront/response/AphrontHTMLResponse.php',
     'AphrontHTTPParameterType' => 'aphront/httpparametertype/AphrontHTTPParameterType.php',
     'AphrontHTTPProxyResponse' => 'aphront/response/AphrontHTTPProxyResponse.php',
     'AphrontHTTPSink' => 'aphront/sink/AphrontHTTPSink.php',
     'AphrontHTTPSinkTestCase' => 'aphront/sink/__tests__/AphrontHTTPSinkTestCase.php',
     'AphrontIntHTTPParameterType' => 'aphront/httpparametertype/AphrontIntHTTPParameterType.php',
     'AphrontIsolatedDatabaseConnectionTestCase' => 'infrastructure/storage/__tests__/AphrontIsolatedDatabaseConnectionTestCase.php',
     'AphrontIsolatedHTTPSink' => 'aphront/sink/AphrontIsolatedHTTPSink.php',
     'AphrontJSONResponse' => 'aphront/response/AphrontJSONResponse.php',
     'AphrontJavelinView' => 'view/AphrontJavelinView.php',
     'AphrontKeyboardShortcutsAvailableView' => 'view/widget/AphrontKeyboardShortcutsAvailableView.php',
     'AphrontListFilterView' => 'view/layout/AphrontListFilterView.php',
     'AphrontListHTTPParameterType' => 'aphront/httpparametertype/AphrontListHTTPParameterType.php',
     'AphrontMalformedRequestException' => 'aphront/exception/AphrontMalformedRequestException.php',
     'AphrontMoreView' => 'view/layout/AphrontMoreView.php',
     'AphrontMultiColumnView' => 'view/layout/AphrontMultiColumnView.php',
     'AphrontMySQLDatabaseConnectionTestCase' => 'infrastructure/storage/__tests__/AphrontMySQLDatabaseConnectionTestCase.php',
     'AphrontNullView' => 'view/AphrontNullView.php',
     'AphrontPHIDHTTPParameterType' => 'aphront/httpparametertype/AphrontPHIDHTTPParameterType.php',
     'AphrontPHIDListHTTPParameterType' => 'aphront/httpparametertype/AphrontPHIDListHTTPParameterType.php',
     'AphrontPHPHTTPSink' => 'aphront/sink/AphrontPHPHTTPSink.php',
     'AphrontPageView' => 'view/page/AphrontPageView.php',
     'AphrontPlainTextResponse' => 'aphront/response/AphrontPlainTextResponse.php',
     'AphrontProgressBarView' => 'view/widget/bars/AphrontProgressBarView.php',
     'AphrontProjectListHTTPParameterType' => 'aphront/httpparametertype/AphrontProjectListHTTPParameterType.php',
     'AphrontProxyResponse' => 'aphront/response/AphrontProxyResponse.php',
     'AphrontRedirectResponse' => 'aphront/response/AphrontRedirectResponse.php',
     'AphrontRedirectResponseTestCase' => 'aphront/response/__tests__/AphrontRedirectResponseTestCase.php',
     'AphrontReloadResponse' => 'aphront/response/AphrontReloadResponse.php',
     'AphrontRequest' => 'aphront/AphrontRequest.php',
     'AphrontRequestExceptionHandler' => 'aphront/handler/AphrontRequestExceptionHandler.php',
     'AphrontRequestTestCase' => 'aphront/__tests__/AphrontRequestTestCase.php',
     'AphrontResponse' => 'aphront/response/AphrontResponse.php',
     'AphrontResponseProducerInterface' => 'aphront/interface/AphrontResponseProducerInterface.php',
     'AphrontRoutingMap' => 'aphront/site/AphrontRoutingMap.php',
     'AphrontRoutingResult' => 'aphront/site/AphrontRoutingResult.php',
     'AphrontSelectHTTPParameterType' => 'aphront/httpparametertype/AphrontSelectHTTPParameterType.php',
     'AphrontSideNavFilterView' => 'view/layout/AphrontSideNavFilterView.php',
     'AphrontSite' => 'aphront/site/AphrontSite.php',
     'AphrontStackTraceView' => 'view/widget/AphrontStackTraceView.php',
     'AphrontStandaloneHTMLResponse' => 'aphront/response/AphrontStandaloneHTMLResponse.php',
     'AphrontStringHTTPParameterType' => 'aphront/httpparametertype/AphrontStringHTTPParameterType.php',
     'AphrontStringListHTTPParameterType' => 'aphront/httpparametertype/AphrontStringListHTTPParameterType.php',
     'AphrontTableView' => 'view/control/AphrontTableView.php',
     'AphrontTagView' => 'view/AphrontTagView.php',
     'AphrontTokenizerTemplateView' => 'view/control/AphrontTokenizerTemplateView.php',
     'AphrontTypeaheadTemplateView' => 'view/control/AphrontTypeaheadTemplateView.php',
     'AphrontUnhandledExceptionResponse' => 'aphront/response/AphrontUnhandledExceptionResponse.php',
     'AphrontUserListHTTPParameterType' => 'aphront/httpparametertype/AphrontUserListHTTPParameterType.php',
     'AphrontView' => 'view/AphrontView.php',
     'AphrontWebpageResponse' => 'aphront/response/AphrontWebpageResponse.php',
     'ArcanistConduitAPIMethod' => 'applications/arcanist/conduit/ArcanistConduitAPIMethod.php',
     'AuditConduitAPIMethod' => 'applications/audit/conduit/AuditConduitAPIMethod.php',
     'AuditQueryConduitAPIMethod' => 'applications/audit/conduit/AuditQueryConduitAPIMethod.php',
     'AuthManageProvidersCapability' => 'applications/auth/capability/AuthManageProvidersCapability.php',
     'CalendarTimeUtil' => 'applications/calendar/util/CalendarTimeUtil.php',
     'CalendarTimeUtilTestCase' => 'applications/calendar/__tests__/CalendarTimeUtilTestCase.php',
     'CelerityAPI' => 'applications/celerity/CelerityAPI.php',
     'CelerityDefaultPostprocessor' => 'applications/celerity/postprocessor/CelerityDefaultPostprocessor.php',
     'CelerityHighContrastPostprocessor' => 'applications/celerity/postprocessor/CelerityHighContrastPostprocessor.php',
     'CelerityLargeFontPostprocessor' => 'applications/celerity/postprocessor/CelerityLargeFontPostprocessor.php',
     'CelerityManagementMapWorkflow' => 'applications/celerity/management/CelerityManagementMapWorkflow.php',
+    'CelerityManagementSyntaxWorkflow' => 'applications/celerity/management/CelerityManagementSyntaxWorkflow.php',
     'CelerityManagementWorkflow' => 'applications/celerity/management/CelerityManagementWorkflow.php',
     'CelerityPhabricatorResourceController' => 'applications/celerity/controller/CelerityPhabricatorResourceController.php',
     'CelerityPhabricatorResources' => 'applications/celerity/resources/CelerityPhabricatorResources.php',
     'CelerityPhysicalResources' => 'applications/celerity/resources/CelerityPhysicalResources.php',
     'CelerityPhysicalResourcesTestCase' => 'applications/celerity/resources/__tests__/CelerityPhysicalResourcesTestCase.php',
     'CelerityPostprocessor' => 'applications/celerity/postprocessor/CelerityPostprocessor.php',
     'CelerityPostprocessorTestCase' => 'applications/celerity/__tests__/CelerityPostprocessorTestCase.php',
     'CelerityResourceController' => 'applications/celerity/controller/CelerityResourceController.php',
     'CelerityResourceGraph' => 'applications/celerity/CelerityResourceGraph.php',
     'CelerityResourceMap' => 'applications/celerity/CelerityResourceMap.php',
     'CelerityResourceMapGenerator' => 'applications/celerity/CelerityResourceMapGenerator.php',
     'CelerityResourceTransformer' => 'applications/celerity/CelerityResourceTransformer.php',
     'CelerityResourceTransformerTestCase' => 'applications/celerity/__tests__/CelerityResourceTransformerTestCase.php',
     'CelerityResources' => 'applications/celerity/resources/CelerityResources.php',
     'CelerityResourcesOnDisk' => 'applications/celerity/resources/CelerityResourcesOnDisk.php',
     'CeleritySpriteGenerator' => 'applications/celerity/CeleritySpriteGenerator.php',
     'CelerityStaticResourceResponse' => 'applications/celerity/CelerityStaticResourceResponse.php',
     'ChatLogConduitAPIMethod' => 'applications/chatlog/conduit/ChatLogConduitAPIMethod.php',
     'ChatLogQueryConduitAPIMethod' => 'applications/chatlog/conduit/ChatLogQueryConduitAPIMethod.php',
     'ChatLogRecordConduitAPIMethod' => 'applications/chatlog/conduit/ChatLogRecordConduitAPIMethod.php',
     'ConduitAPIMethod' => 'applications/conduit/method/ConduitAPIMethod.php',
     'ConduitAPIMethodTestCase' => 'applications/conduit/method/__tests__/ConduitAPIMethodTestCase.php',
     'ConduitAPIRequest' => 'applications/conduit/protocol/ConduitAPIRequest.php',
     'ConduitAPIResponse' => 'applications/conduit/protocol/ConduitAPIResponse.php',
     'ConduitApplicationNotInstalledException' => 'applications/conduit/protocol/exception/ConduitApplicationNotInstalledException.php',
     'ConduitBoolParameterType' => 'applications/conduit/parametertype/ConduitBoolParameterType.php',
     'ConduitCall' => 'applications/conduit/call/ConduitCall.php',
     'ConduitCallTestCase' => 'applications/conduit/call/__tests__/ConduitCallTestCase.php',
     'ConduitColumnsParameterType' => 'applications/conduit/parametertype/ConduitColumnsParameterType.php',
     'ConduitConnectConduitAPIMethod' => 'applications/conduit/method/ConduitConnectConduitAPIMethod.php',
     'ConduitEpochParameterType' => 'applications/conduit/parametertype/ConduitEpochParameterType.php',
     'ConduitException' => 'applications/conduit/protocol/exception/ConduitException.php',
     'ConduitGetCapabilitiesConduitAPIMethod' => 'applications/conduit/method/ConduitGetCapabilitiesConduitAPIMethod.php',
     'ConduitGetCertificateConduitAPIMethod' => 'applications/conduit/method/ConduitGetCertificateConduitAPIMethod.php',
     'ConduitIntListParameterType' => 'applications/conduit/parametertype/ConduitIntListParameterType.php',
     'ConduitIntParameterType' => 'applications/conduit/parametertype/ConduitIntParameterType.php',
     'ConduitListParameterType' => 'applications/conduit/parametertype/ConduitListParameterType.php',
     'ConduitLogGarbageCollector' => 'applications/conduit/garbagecollector/ConduitLogGarbageCollector.php',
     'ConduitMethodDoesNotExistException' => 'applications/conduit/protocol/exception/ConduitMethodDoesNotExistException.php',
     'ConduitMethodNotFoundException' => 'applications/conduit/protocol/exception/ConduitMethodNotFoundException.php',
     'ConduitPHIDListParameterType' => 'applications/conduit/parametertype/ConduitPHIDListParameterType.php',
     'ConduitPHIDParameterType' => 'applications/conduit/parametertype/ConduitPHIDParameterType.php',
     'ConduitParameterType' => 'applications/conduit/parametertype/ConduitParameterType.php',
     'ConduitPingConduitAPIMethod' => 'applications/conduit/method/ConduitPingConduitAPIMethod.php',
     'ConduitPointsParameterType' => 'applications/conduit/parametertype/ConduitPointsParameterType.php',
     'ConduitProjectListParameterType' => 'applications/conduit/parametertype/ConduitProjectListParameterType.php',
     'ConduitQueryConduitAPIMethod' => 'applications/conduit/method/ConduitQueryConduitAPIMethod.php',
     'ConduitResultSearchEngineExtension' => 'applications/conduit/query/ConduitResultSearchEngineExtension.php',
     'ConduitSSHWorkflow' => 'applications/conduit/ssh/ConduitSSHWorkflow.php',
     'ConduitStringListParameterType' => 'applications/conduit/parametertype/ConduitStringListParameterType.php',
     'ConduitStringParameterType' => 'applications/conduit/parametertype/ConduitStringParameterType.php',
     'ConduitTokenGarbageCollector' => 'applications/conduit/garbagecollector/ConduitTokenGarbageCollector.php',
     'ConduitUserListParameterType' => 'applications/conduit/parametertype/ConduitUserListParameterType.php',
     'ConduitUserParameterType' => 'applications/conduit/parametertype/ConduitUserParameterType.php',
     'ConduitWildParameterType' => 'applications/conduit/parametertype/ConduitWildParameterType.php',
     'ConpherenceColumnViewController' => 'applications/conpherence/controller/ConpherenceColumnViewController.php',
     'ConpherenceConduitAPIMethod' => 'applications/conpherence/conduit/ConpherenceConduitAPIMethod.php',
     'ConpherenceConfigOptions' => 'applications/conpherence/config/ConpherenceConfigOptions.php',
     'ConpherenceConstants' => 'applications/conpherence/constants/ConpherenceConstants.php',
     'ConpherenceController' => 'applications/conpherence/controller/ConpherenceController.php',
     'ConpherenceCreateThreadConduitAPIMethod' => 'applications/conpherence/conduit/ConpherenceCreateThreadConduitAPIMethod.php',
     'ConpherenceDAO' => 'applications/conpherence/storage/ConpherenceDAO.php',
     'ConpherenceDurableColumnView' => 'applications/conpherence/view/ConpherenceDurableColumnView.php',
     'ConpherenceEditor' => 'applications/conpherence/editor/ConpherenceEditor.php',
     'ConpherenceFormDragAndDropUploadControl' => 'applications/conpherence/view/ConpherenceFormDragAndDropUploadControl.php',
     'ConpherenceFulltextQuery' => 'applications/conpherence/query/ConpherenceFulltextQuery.php',
     'ConpherenceImageData' => 'applications/conpherence/constants/ConpherenceImageData.php',
     'ConpherenceIndex' => 'applications/conpherence/storage/ConpherenceIndex.php',
     'ConpherenceLayoutView' => 'applications/conpherence/view/ConpherenceLayoutView.php',
     'ConpherenceListController' => 'applications/conpherence/controller/ConpherenceListController.php',
     'ConpherenceMenuItemView' => 'applications/conpherence/view/ConpherenceMenuItemView.php',
     'ConpherenceNewRoomController' => 'applications/conpherence/controller/ConpherenceNewRoomController.php',
     'ConpherenceNotificationPanelController' => 'applications/conpherence/controller/ConpherenceNotificationPanelController.php',
     'ConpherenceParticipant' => 'applications/conpherence/storage/ConpherenceParticipant.php',
     'ConpherenceParticipantCountQuery' => 'applications/conpherence/query/ConpherenceParticipantCountQuery.php',
     'ConpherenceParticipantQuery' => 'applications/conpherence/query/ConpherenceParticipantQuery.php',
     'ConpherenceParticipationStatus' => 'applications/conpherence/constants/ConpherenceParticipationStatus.php',
     'ConpherencePeopleWidgetView' => 'applications/conpherence/view/ConpherencePeopleWidgetView.php',
     'ConpherencePicCropControl' => 'applications/conpherence/view/ConpherencePicCropControl.php',
     'ConpherenceQueryThreadConduitAPIMethod' => 'applications/conpherence/conduit/ConpherenceQueryThreadConduitAPIMethod.php',
     'ConpherenceQueryTransactionConduitAPIMethod' => 'applications/conpherence/conduit/ConpherenceQueryTransactionConduitAPIMethod.php',
     'ConpherenceReplyHandler' => 'applications/conpherence/mail/ConpherenceReplyHandler.php',
     'ConpherenceRoomListController' => 'applications/conpherence/controller/ConpherenceRoomListController.php',
     'ConpherenceRoomTestCase' => 'applications/conpherence/__tests__/ConpherenceRoomTestCase.php',
     'ConpherenceSchemaSpec' => 'applications/conpherence/storage/ConpherenceSchemaSpec.php',
     'ConpherenceSettings' => 'applications/conpherence/constants/ConpherenceSettings.php',
     'ConpherenceTestCase' => 'applications/conpherence/__tests__/ConpherenceTestCase.php',
     'ConpherenceThread' => 'applications/conpherence/storage/ConpherenceThread.php',
     'ConpherenceThreadIndexEngineExtension' => 'applications/conpherence/engineextension/ConpherenceThreadIndexEngineExtension.php',
     'ConpherenceThreadListView' => 'applications/conpherence/view/ConpherenceThreadListView.php',
     'ConpherenceThreadMailReceiver' => 'applications/conpherence/mail/ConpherenceThreadMailReceiver.php',
     'ConpherenceThreadMembersPolicyRule' => 'applications/conpherence/policyrule/ConpherenceThreadMembersPolicyRule.php',
     'ConpherenceThreadQuery' => 'applications/conpherence/query/ConpherenceThreadQuery.php',
     'ConpherenceThreadRemarkupRule' => 'applications/conpherence/remarkup/ConpherenceThreadRemarkupRule.php',
     'ConpherenceThreadSearchEngine' => 'applications/conpherence/query/ConpherenceThreadSearchEngine.php',
     'ConpherenceTransaction' => 'applications/conpherence/storage/ConpherenceTransaction.php',
     'ConpherenceTransactionComment' => 'applications/conpherence/storage/ConpherenceTransactionComment.php',
     'ConpherenceTransactionQuery' => 'applications/conpherence/query/ConpherenceTransactionQuery.php',
     'ConpherenceTransactionRenderer' => 'applications/conpherence/ConpherenceTransactionRenderer.php',
     'ConpherenceTransactionView' => 'applications/conpherence/view/ConpherenceTransactionView.php',
     'ConpherenceUpdateActions' => 'applications/conpherence/constants/ConpherenceUpdateActions.php',
     'ConpherenceUpdateController' => 'applications/conpherence/controller/ConpherenceUpdateController.php',
     'ConpherenceUpdateThreadConduitAPIMethod' => 'applications/conpherence/conduit/ConpherenceUpdateThreadConduitAPIMethod.php',
     'ConpherenceViewController' => 'applications/conpherence/controller/ConpherenceViewController.php',
     'ConpherenceWidgetConfigConstants' => 'applications/conpherence/constants/ConpherenceWidgetConfigConstants.php',
     'ConpherenceWidgetController' => 'applications/conpherence/controller/ConpherenceWidgetController.php',
     'ConpherenceWidgetView' => 'applications/conpherence/view/ConpherenceWidgetView.php',
     'DarkConsoleController' => 'applications/console/controller/DarkConsoleController.php',
     'DarkConsoleCore' => 'applications/console/core/DarkConsoleCore.php',
     'DarkConsoleDataController' => 'applications/console/controller/DarkConsoleDataController.php',
     'DarkConsoleErrorLogPlugin' => 'applications/console/plugin/DarkConsoleErrorLogPlugin.php',
     'DarkConsoleErrorLogPluginAPI' => 'applications/console/plugin/errorlog/DarkConsoleErrorLogPluginAPI.php',
     'DarkConsoleEventPlugin' => 'applications/console/plugin/DarkConsoleEventPlugin.php',
     'DarkConsoleEventPluginAPI' => 'applications/console/plugin/event/DarkConsoleEventPluginAPI.php',
     'DarkConsolePlugin' => 'applications/console/plugin/DarkConsolePlugin.php',
     'DarkConsoleRequestPlugin' => 'applications/console/plugin/DarkConsoleRequestPlugin.php',
     'DarkConsoleServicesPlugin' => 'applications/console/plugin/DarkConsoleServicesPlugin.php',
     'DarkConsoleStartupPlugin' => 'applications/console/plugin/DarkConsoleStartupPlugin.php',
     'DarkConsoleXHProfPlugin' => 'applications/console/plugin/DarkConsoleXHProfPlugin.php',
     'DarkConsoleXHProfPluginAPI' => 'applications/console/plugin/xhprof/DarkConsoleXHProfPluginAPI.php',
     'DatabaseConfigurationProvider' => 'infrastructure/storage/configuration/DatabaseConfigurationProvider.php',
     'DefaultDatabaseConfigurationProvider' => 'infrastructure/storage/configuration/DefaultDatabaseConfigurationProvider.php',
     'DifferentialAction' => 'applications/differential/constants/DifferentialAction.php',
     'DifferentialActionEmailCommand' => 'applications/differential/command/DifferentialActionEmailCommand.php',
     'DifferentialActionMenuEventListener' => 'applications/differential/event/DifferentialActionMenuEventListener.php',
     'DifferentialAddCommentView' => 'applications/differential/view/DifferentialAddCommentView.php',
     'DifferentialAdjustmentMapTestCase' => 'applications/differential/storage/__tests__/DifferentialAdjustmentMapTestCase.php',
     'DifferentialAffectedPath' => 'applications/differential/storage/DifferentialAffectedPath.php',
     'DifferentialApplyPatchField' => 'applications/differential/customfield/DifferentialApplyPatchField.php',
     'DifferentialAsanaRepresentationField' => 'applications/differential/customfield/DifferentialAsanaRepresentationField.php',
     'DifferentialAuditorsField' => 'applications/differential/customfield/DifferentialAuditorsField.php',
     'DifferentialAuthorField' => 'applications/differential/customfield/DifferentialAuthorField.php',
     'DifferentialBlameRevisionField' => 'applications/differential/customfield/DifferentialBlameRevisionField.php',
     'DifferentialBlockHeraldAction' => 'applications/differential/herald/DifferentialBlockHeraldAction.php',
     'DifferentialBranchField' => 'applications/differential/customfield/DifferentialBranchField.php',
+    'DifferentialChangeDetailMailView' => 'applications/differential/mail/DifferentialChangeDetailMailView.php',
     'DifferentialChangeHeraldFieldGroup' => 'applications/differential/herald/DifferentialChangeHeraldFieldGroup.php',
     'DifferentialChangeType' => 'applications/differential/constants/DifferentialChangeType.php',
     'DifferentialChangesSinceLastUpdateField' => 'applications/differential/customfield/DifferentialChangesSinceLastUpdateField.php',
     'DifferentialChangeset' => 'applications/differential/storage/DifferentialChangeset.php',
     'DifferentialChangesetDetailView' => 'applications/differential/view/DifferentialChangesetDetailView.php',
     'DifferentialChangesetFileTreeSideNavBuilder' => 'applications/differential/view/DifferentialChangesetFileTreeSideNavBuilder.php',
     'DifferentialChangesetHTMLRenderer' => 'applications/differential/render/DifferentialChangesetHTMLRenderer.php',
     'DifferentialChangesetListView' => 'applications/differential/view/DifferentialChangesetListView.php',
+    'DifferentialChangesetOneUpMailRenderer' => 'applications/differential/render/DifferentialChangesetOneUpMailRenderer.php',
     'DifferentialChangesetOneUpRenderer' => 'applications/differential/render/DifferentialChangesetOneUpRenderer.php',
     'DifferentialChangesetOneUpTestRenderer' => 'applications/differential/render/DifferentialChangesetOneUpTestRenderer.php',
     'DifferentialChangesetParser' => 'applications/differential/parser/DifferentialChangesetParser.php',
     'DifferentialChangesetParserTestCase' => 'applications/differential/parser/__tests__/DifferentialChangesetParserTestCase.php',
     'DifferentialChangesetQuery' => 'applications/differential/query/DifferentialChangesetQuery.php',
     'DifferentialChangesetRenderer' => 'applications/differential/render/DifferentialChangesetRenderer.php',
     'DifferentialChangesetTestRenderer' => 'applications/differential/render/DifferentialChangesetTestRenderer.php',
     'DifferentialChangesetTwoUpRenderer' => 'applications/differential/render/DifferentialChangesetTwoUpRenderer.php',
     'DifferentialChangesetTwoUpTestRenderer' => 'applications/differential/render/DifferentialChangesetTwoUpTestRenderer.php',
     'DifferentialChangesetViewController' => 'applications/differential/controller/DifferentialChangesetViewController.php',
     'DifferentialCloseConduitAPIMethod' => 'applications/differential/conduit/DifferentialCloseConduitAPIMethod.php',
     'DifferentialCommentPreviewController' => 'applications/differential/controller/DifferentialCommentPreviewController.php',
     'DifferentialCommentSaveController' => 'applications/differential/controller/DifferentialCommentSaveController.php',
     'DifferentialCommitMessageParser' => 'applications/differential/parser/DifferentialCommitMessageParser.php',
     'DifferentialCommitMessageParserTestCase' => 'applications/differential/parser/__tests__/DifferentialCommitMessageParserTestCase.php',
     'DifferentialCommitsField' => 'applications/differential/customfield/DifferentialCommitsField.php',
     'DifferentialConduitAPIMethod' => 'applications/differential/conduit/DifferentialConduitAPIMethod.php',
     'DifferentialConflictsField' => 'applications/differential/customfield/DifferentialConflictsField.php',
     'DifferentialController' => 'applications/differential/controller/DifferentialController.php',
     'DifferentialCoreCustomField' => 'applications/differential/customfield/DifferentialCoreCustomField.php',
     'DifferentialCreateCommentConduitAPIMethod' => 'applications/differential/conduit/DifferentialCreateCommentConduitAPIMethod.php',
     'DifferentialCreateDiffConduitAPIMethod' => 'applications/differential/conduit/DifferentialCreateDiffConduitAPIMethod.php',
     'DifferentialCreateInlineConduitAPIMethod' => 'applications/differential/conduit/DifferentialCreateInlineConduitAPIMethod.php',
     'DifferentialCreateMailReceiver' => 'applications/differential/mail/DifferentialCreateMailReceiver.php',
     'DifferentialCreateRawDiffConduitAPIMethod' => 'applications/differential/conduit/DifferentialCreateRawDiffConduitAPIMethod.php',
     'DifferentialCreateRevisionConduitAPIMethod' => 'applications/differential/conduit/DifferentialCreateRevisionConduitAPIMethod.php',
     'DifferentialCustomField' => 'applications/differential/customfield/DifferentialCustomField.php',
     'DifferentialCustomFieldDependsOnParser' => 'applications/differential/parser/DifferentialCustomFieldDependsOnParser.php',
     'DifferentialCustomFieldDependsOnParserTestCase' => 'applications/differential/parser/__tests__/DifferentialCustomFieldDependsOnParserTestCase.php',
     'DifferentialCustomFieldNumericIndex' => 'applications/differential/storage/DifferentialCustomFieldNumericIndex.php',
     'DifferentialCustomFieldRevertsParser' => 'applications/differential/parser/DifferentialCustomFieldRevertsParser.php',
     'DifferentialCustomFieldRevertsParserTestCase' => 'applications/differential/parser/__tests__/DifferentialCustomFieldRevertsParserTestCase.php',
     'DifferentialCustomFieldStorage' => 'applications/differential/storage/DifferentialCustomFieldStorage.php',
     'DifferentialCustomFieldStringIndex' => 'applications/differential/storage/DifferentialCustomFieldStringIndex.php',
     'DifferentialDAO' => 'applications/differential/storage/DifferentialDAO.php',
     'DifferentialDefaultViewCapability' => 'applications/differential/capability/DifferentialDefaultViewCapability.php',
     'DifferentialDependenciesField' => 'applications/differential/customfield/DifferentialDependenciesField.php',
     'DifferentialDependsOnField' => 'applications/differential/customfield/DifferentialDependsOnField.php',
     'DifferentialDiff' => 'applications/differential/storage/DifferentialDiff.php',
     'DifferentialDiffAffectedFilesHeraldField' => 'applications/differential/herald/DifferentialDiffAffectedFilesHeraldField.php',
     'DifferentialDiffAuthorHeraldField' => 'applications/differential/herald/DifferentialDiffAuthorHeraldField.php',
     'DifferentialDiffAuthorProjectsHeraldField' => 'applications/differential/herald/DifferentialDiffAuthorProjectsHeraldField.php',
     'DifferentialDiffContentAddedHeraldField' => 'applications/differential/herald/DifferentialDiffContentAddedHeraldField.php',
     'DifferentialDiffContentHeraldField' => 'applications/differential/herald/DifferentialDiffContentHeraldField.php',
     'DifferentialDiffContentRemovedHeraldField' => 'applications/differential/herald/DifferentialDiffContentRemovedHeraldField.php',
     'DifferentialDiffCreateController' => 'applications/differential/controller/DifferentialDiffCreateController.php',
     'DifferentialDiffEditor' => 'applications/differential/editor/DifferentialDiffEditor.php',
     'DifferentialDiffExtractionEngine' => 'applications/differential/engine/DifferentialDiffExtractionEngine.php',
     'DifferentialDiffHeraldField' => 'applications/differential/herald/DifferentialDiffHeraldField.php',
     'DifferentialDiffHeraldFieldGroup' => 'applications/differential/herald/DifferentialDiffHeraldFieldGroup.php',
     'DifferentialDiffInlineCommentQuery' => 'applications/differential/query/DifferentialDiffInlineCommentQuery.php',
     'DifferentialDiffPHIDType' => 'applications/differential/phid/DifferentialDiffPHIDType.php',
     'DifferentialDiffProperty' => 'applications/differential/storage/DifferentialDiffProperty.php',
     'DifferentialDiffQuery' => 'applications/differential/query/DifferentialDiffQuery.php',
     'DifferentialDiffRepositoryHeraldField' => 'applications/differential/herald/DifferentialDiffRepositoryHeraldField.php',
     'DifferentialDiffRepositoryProjectsHeraldField' => 'applications/differential/herald/DifferentialDiffRepositoryProjectsHeraldField.php',
     'DifferentialDiffTestCase' => 'applications/differential/storage/__tests__/DifferentialDiffTestCase.php',
     'DifferentialDiffTransaction' => 'applications/differential/storage/DifferentialDiffTransaction.php',
     'DifferentialDiffTransactionQuery' => 'applications/differential/query/DifferentialDiffTransactionQuery.php',
     'DifferentialDiffViewController' => 'applications/differential/controller/DifferentialDiffViewController.php',
     'DifferentialDoorkeeperRevisionFeedStoryPublisher' => 'applications/differential/doorkeeper/DifferentialDoorkeeperRevisionFeedStoryPublisher.php',
     'DifferentialDraft' => 'applications/differential/storage/DifferentialDraft.php',
     'DifferentialEditPolicyField' => 'applications/differential/customfield/DifferentialEditPolicyField.php',
     'DifferentialFieldParseException' => 'applications/differential/exception/DifferentialFieldParseException.php',
     'DifferentialFieldValidationException' => 'applications/differential/exception/DifferentialFieldValidationException.php',
     'DifferentialFindConduitAPIMethod' => 'applications/differential/conduit/DifferentialFindConduitAPIMethod.php',
     'DifferentialGetAllDiffsConduitAPIMethod' => 'applications/differential/conduit/DifferentialGetAllDiffsConduitAPIMethod.php',
     'DifferentialGetCommitMessageConduitAPIMethod' => 'applications/differential/conduit/DifferentialGetCommitMessageConduitAPIMethod.php',
     'DifferentialGetCommitPathsConduitAPIMethod' => 'applications/differential/conduit/DifferentialGetCommitPathsConduitAPIMethod.php',
     'DifferentialGetDiffConduitAPIMethod' => 'applications/differential/conduit/DifferentialGetDiffConduitAPIMethod.php',
     'DifferentialGetRawDiffConduitAPIMethod' => 'applications/differential/conduit/DifferentialGetRawDiffConduitAPIMethod.php',
     'DifferentialGetRevisionCommentsConduitAPIMethod' => 'applications/differential/conduit/DifferentialGetRevisionCommentsConduitAPIMethod.php',
     'DifferentialGetRevisionConduitAPIMethod' => 'applications/differential/conduit/DifferentialGetRevisionConduitAPIMethod.php',
     'DifferentialGetWorkingCopy' => 'applications/differential/DifferentialGetWorkingCopy.php',
     'DifferentialGitHubLandingStrategy' => 'applications/differential/landing/DifferentialGitHubLandingStrategy.php',
     'DifferentialGitSVNIDField' => 'applications/differential/customfield/DifferentialGitSVNIDField.php',
     'DifferentialHarbormasterField' => 'applications/differential/customfield/DifferentialHarbormasterField.php',
     'DifferentialHiddenComment' => 'applications/differential/storage/DifferentialHiddenComment.php',
     'DifferentialHostField' => 'applications/differential/customfield/DifferentialHostField.php',
     'DifferentialHostedGitLandingStrategy' => 'applications/differential/landing/DifferentialHostedGitLandingStrategy.php',
     'DifferentialHostedMercurialLandingStrategy' => 'applications/differential/landing/DifferentialHostedMercurialLandingStrategy.php',
     'DifferentialHovercardEngineExtension' => 'applications/differential/engineextension/DifferentialHovercardEngineExtension.php',
     'DifferentialHunk' => 'applications/differential/storage/DifferentialHunk.php',
     'DifferentialHunkParser' => 'applications/differential/parser/DifferentialHunkParser.php',
     'DifferentialHunkParserTestCase' => 'applications/differential/parser/__tests__/DifferentialHunkParserTestCase.php',
     'DifferentialHunkQuery' => 'applications/differential/query/DifferentialHunkQuery.php',
     'DifferentialHunkTestCase' => 'applications/differential/storage/__tests__/DifferentialHunkTestCase.php',
     'DifferentialInlineComment' => 'applications/differential/storage/DifferentialInlineComment.php',
     'DifferentialInlineCommentEditController' => 'applications/differential/controller/DifferentialInlineCommentEditController.php',
+    'DifferentialInlineCommentMailView' => 'applications/differential/mail/DifferentialInlineCommentMailView.php',
     'DifferentialInlineCommentPreviewController' => 'applications/differential/controller/DifferentialInlineCommentPreviewController.php',
     'DifferentialInlineCommentQuery' => 'applications/differential/query/DifferentialInlineCommentQuery.php',
     'DifferentialJIRAIssuesField' => 'applications/differential/customfield/DifferentialJIRAIssuesField.php',
     'DifferentialLandingActionMenuEventListener' => 'applications/differential/landing/DifferentialLandingActionMenuEventListener.php',
     'DifferentialLandingStrategy' => 'applications/differential/landing/DifferentialLandingStrategy.php',
     'DifferentialLegacyHunk' => 'applications/differential/storage/DifferentialLegacyHunk.php',
     'DifferentialLineAdjustmentMap' => 'applications/differential/parser/DifferentialLineAdjustmentMap.php',
     'DifferentialLintField' => 'applications/differential/customfield/DifferentialLintField.php',
     'DifferentialLintStatus' => 'applications/differential/constants/DifferentialLintStatus.php',
     'DifferentialLocalCommitsView' => 'applications/differential/view/DifferentialLocalCommitsView.php',
+    'DifferentialMailView' => 'applications/differential/mail/DifferentialMailView.php',
     'DifferentialManiphestTasksField' => 'applications/differential/customfield/DifferentialManiphestTasksField.php',
     'DifferentialModernHunk' => 'applications/differential/storage/DifferentialModernHunk.php',
     'DifferentialNextStepField' => 'applications/differential/customfield/DifferentialNextStepField.php',
     'DifferentialParseCacheGarbageCollector' => 'applications/differential/garbagecollector/DifferentialParseCacheGarbageCollector.php',
     'DifferentialParseCommitMessageConduitAPIMethod' => 'applications/differential/conduit/DifferentialParseCommitMessageConduitAPIMethod.php',
     'DifferentialParseRenderTestCase' => 'applications/differential/__tests__/DifferentialParseRenderTestCase.php',
     'DifferentialPathField' => 'applications/differential/customfield/DifferentialPathField.php',
     'DifferentialProjectReviewersField' => 'applications/differential/customfield/DifferentialProjectReviewersField.php',
     'DifferentialProjectsField' => 'applications/differential/customfield/DifferentialProjectsField.php',
     'DifferentialQueryConduitAPIMethod' => 'applications/differential/conduit/DifferentialQueryConduitAPIMethod.php',
     'DifferentialQueryDiffsConduitAPIMethod' => 'applications/differential/conduit/DifferentialQueryDiffsConduitAPIMethod.php',
     'DifferentialRawDiffRenderer' => 'applications/differential/render/DifferentialRawDiffRenderer.php',
     'DifferentialReleephRequestFieldSpecification' => 'applications/releeph/differential/DifferentialReleephRequestFieldSpecification.php',
     'DifferentialRemarkupRule' => 'applications/differential/remarkup/DifferentialRemarkupRule.php',
     'DifferentialReplyHandler' => 'applications/differential/mail/DifferentialReplyHandler.php',
     'DifferentialRepositoryField' => 'applications/differential/customfield/DifferentialRepositoryField.php',
     'DifferentialRepositoryLookup' => 'applications/differential/query/DifferentialRepositoryLookup.php',
     'DifferentialRequiredSignaturesField' => 'applications/differential/customfield/DifferentialRequiredSignaturesField.php',
     'DifferentialRevertPlanField' => 'applications/differential/customfield/DifferentialRevertPlanField.php',
     'DifferentialReviewedByField' => 'applications/differential/customfield/DifferentialReviewedByField.php',
     'DifferentialReviewer' => 'applications/differential/storage/DifferentialReviewer.php',
     'DifferentialReviewerForRevisionEdgeType' => 'applications/differential/edge/DifferentialReviewerForRevisionEdgeType.php',
     'DifferentialReviewerStatus' => 'applications/differential/constants/DifferentialReviewerStatus.php',
     'DifferentialReviewersAddBlockingReviewersHeraldAction' => 'applications/differential/herald/DifferentialReviewersAddBlockingReviewersHeraldAction.php',
     'DifferentialReviewersAddBlockingSelfHeraldAction' => 'applications/differential/herald/DifferentialReviewersAddBlockingSelfHeraldAction.php',
     'DifferentialReviewersAddReviewersHeraldAction' => 'applications/differential/herald/DifferentialReviewersAddReviewersHeraldAction.php',
     'DifferentialReviewersAddSelfHeraldAction' => 'applications/differential/herald/DifferentialReviewersAddSelfHeraldAction.php',
     'DifferentialReviewersField' => 'applications/differential/customfield/DifferentialReviewersField.php',
     'DifferentialReviewersHeraldAction' => 'applications/differential/herald/DifferentialReviewersHeraldAction.php',
     'DifferentialReviewersView' => 'applications/differential/view/DifferentialReviewersView.php',
     'DifferentialRevision' => 'applications/differential/storage/DifferentialRevision.php',
     'DifferentialRevisionAffectedFilesHeraldField' => 'applications/differential/herald/DifferentialRevisionAffectedFilesHeraldField.php',
     'DifferentialRevisionAuthorHeraldField' => 'applications/differential/herald/DifferentialRevisionAuthorHeraldField.php',
     'DifferentialRevisionAuthorProjectsHeraldField' => 'applications/differential/herald/DifferentialRevisionAuthorProjectsHeraldField.php',
     'DifferentialRevisionCloseDetailsController' => 'applications/differential/controller/DifferentialRevisionCloseDetailsController.php',
     'DifferentialRevisionContentAddedHeraldField' => 'applications/differential/herald/DifferentialRevisionContentAddedHeraldField.php',
     'DifferentialRevisionContentHeraldField' => 'applications/differential/herald/DifferentialRevisionContentHeraldField.php',
     'DifferentialRevisionContentRemovedHeraldField' => 'applications/differential/herald/DifferentialRevisionContentRemovedHeraldField.php',
     'DifferentialRevisionControlSystem' => 'applications/differential/constants/DifferentialRevisionControlSystem.php',
     'DifferentialRevisionDependedOnByRevisionEdgeType' => 'applications/differential/edge/DifferentialRevisionDependedOnByRevisionEdgeType.php',
     'DifferentialRevisionDependsOnRevisionEdgeType' => 'applications/differential/edge/DifferentialRevisionDependsOnRevisionEdgeType.php',
     'DifferentialRevisionEditController' => 'applications/differential/controller/DifferentialRevisionEditController.php',
     'DifferentialRevisionFulltextEngine' => 'applications/differential/search/DifferentialRevisionFulltextEngine.php',
     'DifferentialRevisionHasCommitEdgeType' => 'applications/differential/edge/DifferentialRevisionHasCommitEdgeType.php',
     'DifferentialRevisionHasReviewerEdgeType' => 'applications/differential/edge/DifferentialRevisionHasReviewerEdgeType.php',
     'DifferentialRevisionHasTaskEdgeType' => 'applications/differential/edge/DifferentialRevisionHasTaskEdgeType.php',
     'DifferentialRevisionHeraldField' => 'applications/differential/herald/DifferentialRevisionHeraldField.php',
     'DifferentialRevisionHeraldFieldGroup' => 'applications/differential/herald/DifferentialRevisionHeraldFieldGroup.php',
     'DifferentialRevisionIDField' => 'applications/differential/customfield/DifferentialRevisionIDField.php',
     'DifferentialRevisionLandController' => 'applications/differential/controller/DifferentialRevisionLandController.php',
     'DifferentialRevisionListController' => 'applications/differential/controller/DifferentialRevisionListController.php',
     'DifferentialRevisionListView' => 'applications/differential/view/DifferentialRevisionListView.php',
     'DifferentialRevisionMailReceiver' => 'applications/differential/mail/DifferentialRevisionMailReceiver.php',
     'DifferentialRevisionOperationController' => 'applications/differential/controller/DifferentialRevisionOperationController.php',
     'DifferentialRevisionPHIDType' => 'applications/differential/phid/DifferentialRevisionPHIDType.php',
     'DifferentialRevisionPackageHeraldField' => 'applications/differential/herald/DifferentialRevisionPackageHeraldField.php',
     'DifferentialRevisionPackageOwnerHeraldField' => 'applications/differential/herald/DifferentialRevisionPackageOwnerHeraldField.php',
     'DifferentialRevisionQuery' => 'applications/differential/query/DifferentialRevisionQuery.php',
     'DifferentialRevisionRepositoryHeraldField' => 'applications/differential/herald/DifferentialRevisionRepositoryHeraldField.php',
     'DifferentialRevisionRepositoryProjectsHeraldField' => 'applications/differential/herald/DifferentialRevisionRepositoryProjectsHeraldField.php',
     'DifferentialRevisionReviewersHeraldField' => 'applications/differential/herald/DifferentialRevisionReviewersHeraldField.php',
     'DifferentialRevisionSearchEngine' => 'applications/differential/query/DifferentialRevisionSearchEngine.php',
     'DifferentialRevisionStatus' => 'applications/differential/constants/DifferentialRevisionStatus.php',
     'DifferentialRevisionSummaryHeraldField' => 'applications/differential/herald/DifferentialRevisionSummaryHeraldField.php',
     'DifferentialRevisionTitleHeraldField' => 'applications/differential/herald/DifferentialRevisionTitleHeraldField.php',
     'DifferentialRevisionUpdateHistoryView' => 'applications/differential/view/DifferentialRevisionUpdateHistoryView.php',
     'DifferentialRevisionViewController' => 'applications/differential/controller/DifferentialRevisionViewController.php',
     'DifferentialSchemaSpec' => 'applications/differential/storage/DifferentialSchemaSpec.php',
     'DifferentialSetDiffPropertyConduitAPIMethod' => 'applications/differential/conduit/DifferentialSetDiffPropertyConduitAPIMethod.php',
     'DifferentialStoredCustomField' => 'applications/differential/customfield/DifferentialStoredCustomField.php',
     'DifferentialSubscribersField' => 'applications/differential/customfield/DifferentialSubscribersField.php',
     'DifferentialSummaryField' => 'applications/differential/customfield/DifferentialSummaryField.php',
     'DifferentialTestPlanField' => 'applications/differential/customfield/DifferentialTestPlanField.php',
     'DifferentialTitleField' => 'applications/differential/customfield/DifferentialTitleField.php',
     'DifferentialTransaction' => 'applications/differential/storage/DifferentialTransaction.php',
     'DifferentialTransactionComment' => 'applications/differential/storage/DifferentialTransactionComment.php',
     'DifferentialTransactionEditor' => 'applications/differential/editor/DifferentialTransactionEditor.php',
     'DifferentialTransactionQuery' => 'applications/differential/query/DifferentialTransactionQuery.php',
     'DifferentialTransactionView' => 'applications/differential/view/DifferentialTransactionView.php',
     'DifferentialUnitField' => 'applications/differential/customfield/DifferentialUnitField.php',
     'DifferentialUnitStatus' => 'applications/differential/constants/DifferentialUnitStatus.php',
     'DifferentialUnitTestResult' => 'applications/differential/constants/DifferentialUnitTestResult.php',
     'DifferentialUpdateRevisionConduitAPIMethod' => 'applications/differential/conduit/DifferentialUpdateRevisionConduitAPIMethod.php',
     'DifferentialViewPolicyField' => 'applications/differential/customfield/DifferentialViewPolicyField.php',
     'DiffusionAuditorDatasource' => 'applications/diffusion/typeahead/DiffusionAuditorDatasource.php',
     'DiffusionAuditorFunctionDatasource' => 'applications/diffusion/typeahead/DiffusionAuditorFunctionDatasource.php',
     'DiffusionAuditorsAddAuditorsHeraldAction' => 'applications/diffusion/herald/DiffusionAuditorsAddAuditorsHeraldAction.php',
     'DiffusionAuditorsAddSelfHeraldAction' => 'applications/diffusion/herald/DiffusionAuditorsAddSelfHeraldAction.php',
     'DiffusionAuditorsHeraldAction' => 'applications/diffusion/herald/DiffusionAuditorsHeraldAction.php',
     'DiffusionBlameConduitAPIMethod' => 'applications/diffusion/conduit/DiffusionBlameConduitAPIMethod.php',
     'DiffusionBlameQuery' => 'applications/diffusion/query/blame/DiffusionBlameQuery.php',
     'DiffusionBlockHeraldAction' => 'applications/diffusion/herald/DiffusionBlockHeraldAction.php',
     'DiffusionBranchQueryConduitAPIMethod' => 'applications/diffusion/conduit/DiffusionBranchQueryConduitAPIMethod.php',
     'DiffusionBranchTableController' => 'applications/diffusion/controller/DiffusionBranchTableController.php',
     'DiffusionBranchTableView' => 'applications/diffusion/view/DiffusionBranchTableView.php',
     'DiffusionBrowseController' => 'applications/diffusion/controller/DiffusionBrowseController.php',
     'DiffusionBrowseQueryConduitAPIMethod' => 'applications/diffusion/conduit/DiffusionBrowseQueryConduitAPIMethod.php',
     'DiffusionBrowseResultSet' => 'applications/diffusion/data/DiffusionBrowseResultSet.php',
     'DiffusionBrowseTableView' => 'applications/diffusion/view/DiffusionBrowseTableView.php',
     'DiffusionCachedResolveRefsQuery' => 'applications/diffusion/query/DiffusionCachedResolveRefsQuery.php',
     'DiffusionChangeController' => 'applications/diffusion/controller/DiffusionChangeController.php',
     'DiffusionChangeHeraldFieldGroup' => 'applications/diffusion/herald/DiffusionChangeHeraldFieldGroup.php',
+    'DiffusionCloneURIView' => 'applications/diffusion/view/DiffusionCloneURIView.php',
     'DiffusionCommandEngine' => 'applications/diffusion/protocol/DiffusionCommandEngine.php',
     'DiffusionCommandEngineTestCase' => 'applications/diffusion/protocol/__tests__/DiffusionCommandEngineTestCase.php',
     'DiffusionCommitAffectedFilesHeraldField' => 'applications/diffusion/herald/DiffusionCommitAffectedFilesHeraldField.php',
     'DiffusionCommitAuthorHeraldField' => 'applications/diffusion/herald/DiffusionCommitAuthorHeraldField.php',
     'DiffusionCommitAutocloseHeraldField' => 'applications/diffusion/herald/DiffusionCommitAutocloseHeraldField.php',
     'DiffusionCommitBranchesController' => 'applications/diffusion/controller/DiffusionCommitBranchesController.php',
     'DiffusionCommitBranchesHeraldField' => 'applications/diffusion/herald/DiffusionCommitBranchesHeraldField.php',
     'DiffusionCommitCommitterHeraldField' => 'applications/diffusion/herald/DiffusionCommitCommitterHeraldField.php',
     'DiffusionCommitController' => 'applications/diffusion/controller/DiffusionCommitController.php',
     'DiffusionCommitDiffContentAddedHeraldField' => 'applications/diffusion/herald/DiffusionCommitDiffContentAddedHeraldField.php',
     'DiffusionCommitDiffContentHeraldField' => 'applications/diffusion/herald/DiffusionCommitDiffContentHeraldField.php',
     'DiffusionCommitDiffContentRemovedHeraldField' => 'applications/diffusion/herald/DiffusionCommitDiffContentRemovedHeraldField.php',
     'DiffusionCommitDiffEnormousHeraldField' => 'applications/diffusion/herald/DiffusionCommitDiffEnormousHeraldField.php',
     'DiffusionCommitEditController' => 'applications/diffusion/controller/DiffusionCommitEditController.php',
     'DiffusionCommitFulltextEngine' => 'applications/repository/search/DiffusionCommitFulltextEngine.php',
     'DiffusionCommitHasRevisionEdgeType' => 'applications/diffusion/edge/DiffusionCommitHasRevisionEdgeType.php',
     'DiffusionCommitHasTaskEdgeType' => 'applications/diffusion/edge/DiffusionCommitHasTaskEdgeType.php',
     'DiffusionCommitHash' => 'applications/diffusion/data/DiffusionCommitHash.php',
     'DiffusionCommitHeraldField' => 'applications/diffusion/herald/DiffusionCommitHeraldField.php',
     'DiffusionCommitHeraldFieldGroup' => 'applications/diffusion/herald/DiffusionCommitHeraldFieldGroup.php',
     'DiffusionCommitHookEngine' => 'applications/diffusion/engine/DiffusionCommitHookEngine.php',
     'DiffusionCommitHookRejectException' => 'applications/diffusion/exception/DiffusionCommitHookRejectException.php',
     'DiffusionCommitMergeHeraldField' => 'applications/diffusion/herald/DiffusionCommitMergeHeraldField.php',
     'DiffusionCommitMessageHeraldField' => 'applications/diffusion/herald/DiffusionCommitMessageHeraldField.php',
     'DiffusionCommitPackageAuditHeraldField' => 'applications/diffusion/herald/DiffusionCommitPackageAuditHeraldField.php',
     'DiffusionCommitPackageHeraldField' => 'applications/diffusion/herald/DiffusionCommitPackageHeraldField.php',
     'DiffusionCommitPackageOwnerHeraldField' => 'applications/diffusion/herald/DiffusionCommitPackageOwnerHeraldField.php',
     'DiffusionCommitParentsQueryConduitAPIMethod' => 'applications/diffusion/conduit/DiffusionCommitParentsQueryConduitAPIMethod.php',
     'DiffusionCommitQuery' => 'applications/diffusion/query/DiffusionCommitQuery.php',
     'DiffusionCommitRef' => 'applications/diffusion/data/DiffusionCommitRef.php',
     'DiffusionCommitRemarkupRule' => 'applications/diffusion/remarkup/DiffusionCommitRemarkupRule.php',
     'DiffusionCommitRemarkupRuleTestCase' => 'applications/diffusion/remarkup/__tests__/DiffusionCommitRemarkupRuleTestCase.php',
     'DiffusionCommitRepositoryHeraldField' => 'applications/diffusion/herald/DiffusionCommitRepositoryHeraldField.php',
     'DiffusionCommitRepositoryProjectsHeraldField' => 'applications/diffusion/herald/DiffusionCommitRepositoryProjectsHeraldField.php',
     'DiffusionCommitRevertedByCommitEdgeType' => 'applications/diffusion/edge/DiffusionCommitRevertedByCommitEdgeType.php',
     'DiffusionCommitRevertsCommitEdgeType' => 'applications/diffusion/edge/DiffusionCommitRevertsCommitEdgeType.php',
     'DiffusionCommitReviewerHeraldField' => 'applications/diffusion/herald/DiffusionCommitReviewerHeraldField.php',
     'DiffusionCommitRevisionAcceptedHeraldField' => 'applications/diffusion/herald/DiffusionCommitRevisionAcceptedHeraldField.php',
     'DiffusionCommitRevisionHeraldField' => 'applications/diffusion/herald/DiffusionCommitRevisionHeraldField.php',
     'DiffusionCommitRevisionReviewersHeraldField' => 'applications/diffusion/herald/DiffusionCommitRevisionReviewersHeraldField.php',
     'DiffusionCommitRevisionSubscribersHeraldField' => 'applications/diffusion/herald/DiffusionCommitRevisionSubscribersHeraldField.php',
     'DiffusionCommitTagsController' => 'applications/diffusion/controller/DiffusionCommitTagsController.php',
     'DiffusionConduitAPIMethod' => 'applications/diffusion/conduit/DiffusionConduitAPIMethod.php',
     'DiffusionController' => 'applications/diffusion/controller/DiffusionController.php',
     'DiffusionCreateCommentConduitAPIMethod' => 'applications/diffusion/conduit/DiffusionCreateCommentConduitAPIMethod.php',
     'DiffusionCreateRepositoriesCapability' => 'applications/diffusion/capability/DiffusionCreateRepositoriesCapability.php',
+    'DiffusionDaemonLockException' => 'applications/diffusion/exception/DiffusionDaemonLockException.php',
     'DiffusionDefaultEditCapability' => 'applications/diffusion/capability/DiffusionDefaultEditCapability.php',
     'DiffusionDefaultPushCapability' => 'applications/diffusion/capability/DiffusionDefaultPushCapability.php',
     'DiffusionDefaultViewCapability' => 'applications/diffusion/capability/DiffusionDefaultViewCapability.php',
     'DiffusionDiffController' => 'applications/diffusion/controller/DiffusionDiffController.php',
     'DiffusionDiffInlineCommentQuery' => 'applications/diffusion/query/DiffusionDiffInlineCommentQuery.php',
     'DiffusionDiffQueryConduitAPIMethod' => 'applications/diffusion/conduit/DiffusionDiffQueryConduitAPIMethod.php',
     'DiffusionDoorkeeperCommitFeedStoryPublisher' => 'applications/diffusion/doorkeeper/DiffusionDoorkeeperCommitFeedStoryPublisher.php',
     'DiffusionEmptyResultView' => 'applications/diffusion/view/DiffusionEmptyResultView.php',
     'DiffusionExistsQueryConduitAPIMethod' => 'applications/diffusion/conduit/DiffusionExistsQueryConduitAPIMethod.php',
     'DiffusionExternalController' => 'applications/diffusion/controller/DiffusionExternalController.php',
     'DiffusionExternalSymbolQuery' => 'applications/diffusion/symbol/DiffusionExternalSymbolQuery.php',
     'DiffusionExternalSymbolsSource' => 'applications/diffusion/symbol/DiffusionExternalSymbolsSource.php',
     'DiffusionFileContentQuery' => 'applications/diffusion/query/filecontent/DiffusionFileContentQuery.php',
     'DiffusionFileContentQueryConduitAPIMethod' => 'applications/diffusion/conduit/DiffusionFileContentQueryConduitAPIMethod.php',
     'DiffusionFindSymbolsConduitAPIMethod' => 'applications/diffusion/conduit/DiffusionFindSymbolsConduitAPIMethod.php',
     'DiffusionGetLintMessagesConduitAPIMethod' => 'applications/diffusion/conduit/DiffusionGetLintMessagesConduitAPIMethod.php',
     'DiffusionGetRecentCommitsByPathConduitAPIMethod' => 'applications/diffusion/conduit/DiffusionGetRecentCommitsByPathConduitAPIMethod.php',
     'DiffusionGitBlameQuery' => 'applications/diffusion/query/blame/DiffusionGitBlameQuery.php',
     'DiffusionGitBranch' => 'applications/diffusion/data/DiffusionGitBranch.php',
     'DiffusionGitBranchTestCase' => 'applications/diffusion/data/__tests__/DiffusionGitBranchTestCase.php',
     'DiffusionGitCommandEngine' => 'applications/diffusion/protocol/DiffusionGitCommandEngine.php',
     'DiffusionGitFileContentQuery' => 'applications/diffusion/query/filecontent/DiffusionGitFileContentQuery.php',
     'DiffusionGitLFSAuthenticateWorkflow' => 'applications/diffusion/gitlfs/DiffusionGitLFSAuthenticateWorkflow.php',
     'DiffusionGitLFSResponse' => 'applications/diffusion/response/DiffusionGitLFSResponse.php',
     'DiffusionGitLFSTemporaryTokenType' => 'applications/diffusion/gitlfs/DiffusionGitLFSTemporaryTokenType.php',
     'DiffusionGitRawDiffQuery' => 'applications/diffusion/query/rawdiff/DiffusionGitRawDiffQuery.php',
     'DiffusionGitReceivePackSSHWorkflow' => 'applications/diffusion/ssh/DiffusionGitReceivePackSSHWorkflow.php',
     'DiffusionGitRequest' => 'applications/diffusion/request/DiffusionGitRequest.php',
     'DiffusionGitResponse' => 'applications/diffusion/response/DiffusionGitResponse.php',
     'DiffusionGitSSHWorkflow' => 'applications/diffusion/ssh/DiffusionGitSSHWorkflow.php',
     'DiffusionGitUploadPackSSHWorkflow' => 'applications/diffusion/ssh/DiffusionGitUploadPackSSHWorkflow.php',
     'DiffusionHistoryController' => 'applications/diffusion/controller/DiffusionHistoryController.php',
     'DiffusionHistoryQueryConduitAPIMethod' => 'applications/diffusion/conduit/DiffusionHistoryQueryConduitAPIMethod.php',
     'DiffusionHistoryTableView' => 'applications/diffusion/view/DiffusionHistoryTableView.php',
     'DiffusionHovercardEngineExtension' => 'applications/diffusion/engineextension/DiffusionHovercardEngineExtension.php',
     'DiffusionInlineCommentController' => 'applications/diffusion/controller/DiffusionInlineCommentController.php',
     'DiffusionInlineCommentPreviewController' => 'applications/diffusion/controller/DiffusionInlineCommentPreviewController.php',
     'DiffusionInternalGitRawDiffQueryConduitAPIMethod' => 'applications/diffusion/conduit/DiffusionInternalGitRawDiffQueryConduitAPIMethod.php',
     'DiffusionLastModifiedController' => 'applications/diffusion/controller/DiffusionLastModifiedController.php',
     'DiffusionLastModifiedQueryConduitAPIMethod' => 'applications/diffusion/conduit/DiffusionLastModifiedQueryConduitAPIMethod.php',
     'DiffusionLintController' => 'applications/diffusion/controller/DiffusionLintController.php',
     'DiffusionLintCountQuery' => 'applications/diffusion/query/DiffusionLintCountQuery.php',
     'DiffusionLintSaveRunner' => 'applications/diffusion/DiffusionLintSaveRunner.php',
+    'DiffusionLocalRepositoryFilter' => 'applications/diffusion/data/DiffusionLocalRepositoryFilter.php',
     'DiffusionLookSoonConduitAPIMethod' => 'applications/diffusion/conduit/DiffusionLookSoonConduitAPIMethod.php',
     'DiffusionLowLevelCommitFieldsQuery' => 'applications/diffusion/query/lowlevel/DiffusionLowLevelCommitFieldsQuery.php',
     'DiffusionLowLevelCommitQuery' => 'applications/diffusion/query/lowlevel/DiffusionLowLevelCommitQuery.php',
     'DiffusionLowLevelGitRefQuery' => 'applications/diffusion/query/lowlevel/DiffusionLowLevelGitRefQuery.php',
     'DiffusionLowLevelMercurialBranchesQuery' => 'applications/diffusion/query/lowlevel/DiffusionLowLevelMercurialBranchesQuery.php',
     'DiffusionLowLevelMercurialPathsQuery' => 'applications/diffusion/query/lowlevel/DiffusionLowLevelMercurialPathsQuery.php',
     'DiffusionLowLevelMercurialPathsQueryTests' => 'applications/diffusion/query/lowlevel/__tests__/DiffusionLowLevelMercurialPathsQueryTests.php',
     'DiffusionLowLevelParentsQuery' => 'applications/diffusion/query/lowlevel/DiffusionLowLevelParentsQuery.php',
     'DiffusionLowLevelQuery' => 'applications/diffusion/query/lowlevel/DiffusionLowLevelQuery.php',
     'DiffusionLowLevelResolveRefsQuery' => 'applications/diffusion/query/lowlevel/DiffusionLowLevelResolveRefsQuery.php',
     'DiffusionMercurialBlameQuery' => 'applications/diffusion/query/blame/DiffusionMercurialBlameQuery.php',
     'DiffusionMercurialCommandEngine' => 'applications/diffusion/protocol/DiffusionMercurialCommandEngine.php',
     'DiffusionMercurialFileContentQuery' => 'applications/diffusion/query/filecontent/DiffusionMercurialFileContentQuery.php',
     'DiffusionMercurialRawDiffQuery' => 'applications/diffusion/query/rawdiff/DiffusionMercurialRawDiffQuery.php',
     'DiffusionMercurialRequest' => 'applications/diffusion/request/DiffusionMercurialRequest.php',
     'DiffusionMercurialResponse' => 'applications/diffusion/response/DiffusionMercurialResponse.php',
     'DiffusionMercurialSSHWorkflow' => 'applications/diffusion/ssh/DiffusionMercurialSSHWorkflow.php',
     'DiffusionMercurialServeSSHWorkflow' => 'applications/diffusion/ssh/DiffusionMercurialServeSSHWorkflow.php',
     'DiffusionMercurialWireClientSSHProtocolChannel' => 'applications/diffusion/ssh/DiffusionMercurialWireClientSSHProtocolChannel.php',
     'DiffusionMercurialWireProtocol' => 'applications/diffusion/protocol/DiffusionMercurialWireProtocol.php',
     'DiffusionMercurialWireProtocolTests' => 'applications/diffusion/protocol/__tests__/DiffusionMercurialWireProtocolTests.php',
     'DiffusionMercurialWireSSHTestCase' => 'applications/diffusion/ssh/__tests__/DiffusionMercurialWireSSHTestCase.php',
     'DiffusionMergedCommitsQueryConduitAPIMethod' => 'applications/diffusion/conduit/DiffusionMergedCommitsQueryConduitAPIMethod.php',
-    'DiffusionMirrorDeleteController' => 'applications/diffusion/controller/DiffusionMirrorDeleteController.php',
-    'DiffusionMirrorEditController' => 'applications/diffusion/controller/DiffusionMirrorEditController.php',
     'DiffusionPathChange' => 'applications/diffusion/data/DiffusionPathChange.php',
     'DiffusionPathChangeQuery' => 'applications/diffusion/query/pathchange/DiffusionPathChangeQuery.php',
     'DiffusionPathCompleteController' => 'applications/diffusion/controller/DiffusionPathCompleteController.php',
     'DiffusionPathIDQuery' => 'applications/diffusion/query/pathid/DiffusionPathIDQuery.php',
     'DiffusionPathQuery' => 'applications/diffusion/query/DiffusionPathQuery.php',
     'DiffusionPathQueryTestCase' => 'applications/diffusion/query/pathid/__tests__/DiffusionPathQueryTestCase.php',
     'DiffusionPathTreeController' => 'applications/diffusion/controller/DiffusionPathTreeController.php',
     'DiffusionPathValidateController' => 'applications/diffusion/controller/DiffusionPathValidateController.php',
     'DiffusionPhpExternalSymbolsSource' => 'applications/diffusion/symbol/DiffusionPhpExternalSymbolsSource.php',
     'DiffusionPreCommitContentAffectedFilesHeraldField' => 'applications/diffusion/herald/DiffusionPreCommitContentAffectedFilesHeraldField.php',
     'DiffusionPreCommitContentAuthorHeraldField' => 'applications/diffusion/herald/DiffusionPreCommitContentAuthorHeraldField.php',
     'DiffusionPreCommitContentAuthorRawHeraldField' => 'applications/diffusion/herald/DiffusionPreCommitContentAuthorRawHeraldField.php',
     'DiffusionPreCommitContentBranchesHeraldField' => 'applications/diffusion/herald/DiffusionPreCommitContentBranchesHeraldField.php',
     'DiffusionPreCommitContentCommitterHeraldField' => 'applications/diffusion/herald/DiffusionPreCommitContentCommitterHeraldField.php',
     'DiffusionPreCommitContentCommitterRawHeraldField' => 'applications/diffusion/herald/DiffusionPreCommitContentCommitterRawHeraldField.php',
     'DiffusionPreCommitContentDiffContentAddedHeraldField' => 'applications/diffusion/herald/DiffusionPreCommitContentDiffContentAddedHeraldField.php',
     'DiffusionPreCommitContentDiffContentHeraldField' => 'applications/diffusion/herald/DiffusionPreCommitContentDiffContentHeraldField.php',
     'DiffusionPreCommitContentDiffContentRemovedHeraldField' => 'applications/diffusion/herald/DiffusionPreCommitContentDiffContentRemovedHeraldField.php',
     'DiffusionPreCommitContentDiffEnormousHeraldField' => 'applications/diffusion/herald/DiffusionPreCommitContentDiffEnormousHeraldField.php',
     'DiffusionPreCommitContentHeraldField' => 'applications/diffusion/herald/DiffusionPreCommitContentHeraldField.php',
     'DiffusionPreCommitContentMergeHeraldField' => 'applications/diffusion/herald/DiffusionPreCommitContentMergeHeraldField.php',
     'DiffusionPreCommitContentMessageHeraldField' => 'applications/diffusion/herald/DiffusionPreCommitContentMessageHeraldField.php',
     'DiffusionPreCommitContentPusherHeraldField' => 'applications/diffusion/herald/DiffusionPreCommitContentPusherHeraldField.php',
     'DiffusionPreCommitContentPusherIsCommitterHeraldField' => 'applications/diffusion/herald/DiffusionPreCommitContentPusherIsCommitterHeraldField.php',
     'DiffusionPreCommitContentPusherProjectsHeraldField' => 'applications/diffusion/herald/DiffusionPreCommitContentPusherProjectsHeraldField.php',
     'DiffusionPreCommitContentRepositoryHeraldField' => 'applications/diffusion/herald/DiffusionPreCommitContentRepositoryHeraldField.php',
     'DiffusionPreCommitContentRepositoryProjectsHeraldField' => 'applications/diffusion/herald/DiffusionPreCommitContentRepositoryProjectsHeraldField.php',
     'DiffusionPreCommitContentRevisionAcceptedHeraldField' => 'applications/diffusion/herald/DiffusionPreCommitContentRevisionAcceptedHeraldField.php',
     'DiffusionPreCommitContentRevisionHeraldField' => 'applications/diffusion/herald/DiffusionPreCommitContentRevisionHeraldField.php',
     'DiffusionPreCommitContentRevisionReviewersHeraldField' => 'applications/diffusion/herald/DiffusionPreCommitContentRevisionReviewersHeraldField.php',
     'DiffusionPreCommitContentRevisionSubscribersHeraldField' => 'applications/diffusion/herald/DiffusionPreCommitContentRevisionSubscribersHeraldField.php',
     'DiffusionPreCommitRefChangeHeraldField' => 'applications/diffusion/herald/DiffusionPreCommitRefChangeHeraldField.php',
     'DiffusionPreCommitRefHeraldField' => 'applications/diffusion/herald/DiffusionPreCommitRefHeraldField.php',
     'DiffusionPreCommitRefHeraldFieldGroup' => 'applications/diffusion/herald/DiffusionPreCommitRefHeraldFieldGroup.php',
     'DiffusionPreCommitRefNameHeraldField' => 'applications/diffusion/herald/DiffusionPreCommitRefNameHeraldField.php',
     'DiffusionPreCommitRefPusherHeraldField' => 'applications/diffusion/herald/DiffusionPreCommitRefPusherHeraldField.php',
     'DiffusionPreCommitRefPusherProjectsHeraldField' => 'applications/diffusion/herald/DiffusionPreCommitRefPusherProjectsHeraldField.php',
     'DiffusionPreCommitRefRepositoryHeraldField' => 'applications/diffusion/herald/DiffusionPreCommitRefRepositoryHeraldField.php',
     'DiffusionPreCommitRefRepositoryProjectsHeraldField' => 'applications/diffusion/herald/DiffusionPreCommitRefRepositoryProjectsHeraldField.php',
     'DiffusionPreCommitRefTypeHeraldField' => 'applications/diffusion/herald/DiffusionPreCommitRefTypeHeraldField.php',
     'DiffusionPullEventGarbageCollector' => 'applications/diffusion/garbagecollector/DiffusionPullEventGarbageCollector.php',
     'DiffusionPushCapability' => 'applications/diffusion/capability/DiffusionPushCapability.php',
     'DiffusionPushEventViewController' => 'applications/diffusion/controller/DiffusionPushEventViewController.php',
     'DiffusionPushLogController' => 'applications/diffusion/controller/DiffusionPushLogController.php',
     'DiffusionPushLogListController' => 'applications/diffusion/controller/DiffusionPushLogListController.php',
     'DiffusionPushLogListView' => 'applications/diffusion/view/DiffusionPushLogListView.php',
     'DiffusionPythonExternalSymbolsSource' => 'applications/diffusion/symbol/DiffusionPythonExternalSymbolsSource.php',
     'DiffusionQuery' => 'applications/diffusion/query/DiffusionQuery.php',
     'DiffusionQueryCommitsConduitAPIMethod' => 'applications/diffusion/conduit/DiffusionQueryCommitsConduitAPIMethod.php',
     'DiffusionQueryConduitAPIMethod' => 'applications/diffusion/conduit/DiffusionQueryConduitAPIMethod.php',
     'DiffusionQueryPathsConduitAPIMethod' => 'applications/diffusion/conduit/DiffusionQueryPathsConduitAPIMethod.php',
     'DiffusionRawDiffQuery' => 'applications/diffusion/query/rawdiff/DiffusionRawDiffQuery.php',
     'DiffusionRawDiffQueryConduitAPIMethod' => 'applications/diffusion/conduit/DiffusionRawDiffQueryConduitAPIMethod.php',
     'DiffusionReadmeView' => 'applications/diffusion/view/DiffusionReadmeView.php',
     'DiffusionRefDatasource' => 'applications/diffusion/typeahead/DiffusionRefDatasource.php',
     'DiffusionRefNotFoundException' => 'applications/diffusion/exception/DiffusionRefNotFoundException.php',
     'DiffusionRefTableController' => 'applications/diffusion/controller/DiffusionRefTableController.php',
     'DiffusionRefsQueryConduitAPIMethod' => 'applications/diffusion/conduit/DiffusionRefsQueryConduitAPIMethod.php',
     'DiffusionRenameHistoryQuery' => 'applications/diffusion/query/DiffusionRenameHistoryQuery.php',
     'DiffusionRepositoryActionsManagementPanel' => 'applications/diffusion/management/DiffusionRepositoryActionsManagementPanel.php',
     'DiffusionRepositoryAutomationManagementPanel' => 'applications/diffusion/management/DiffusionRepositoryAutomationManagementPanel.php',
     'DiffusionRepositoryBasicsManagementPanel' => 'applications/diffusion/management/DiffusionRepositoryBasicsManagementPanel.php',
     'DiffusionRepositoryBranchesManagementPanel' => 'applications/diffusion/management/DiffusionRepositoryBranchesManagementPanel.php',
     'DiffusionRepositoryByIDRemarkupRule' => 'applications/diffusion/remarkup/DiffusionRepositoryByIDRemarkupRule.php',
     'DiffusionRepositoryClusterEngine' => 'applications/diffusion/protocol/DiffusionRepositoryClusterEngine.php',
     'DiffusionRepositoryClusterEngineLogInterface' => 'applications/diffusion/protocol/DiffusionRepositoryClusterEngineLogInterface.php',
     'DiffusionRepositoryController' => 'applications/diffusion/controller/DiffusionRepositoryController.php',
-    'DiffusionRepositoryCreateController' => 'applications/diffusion/controller/DiffusionRepositoryCreateController.php',
     'DiffusionRepositoryDatasource' => 'applications/diffusion/typeahead/DiffusionRepositoryDatasource.php',
     'DiffusionRepositoryDefaultController' => 'applications/diffusion/controller/DiffusionRepositoryDefaultController.php',
-    'DiffusionRepositoryEditActionsController' => 'applications/diffusion/controller/DiffusionRepositoryEditActionsController.php',
+    'DiffusionRepositoryDocumentationManagementPanel' => 'applications/diffusion/management/DiffusionRepositoryDocumentationManagementPanel.php',
     'DiffusionRepositoryEditActivateController' => 'applications/diffusion/controller/DiffusionRepositoryEditActivateController.php',
-    'DiffusionRepositoryEditAutomationController' => 'applications/diffusion/controller/DiffusionRepositoryEditAutomationController.php',
-    'DiffusionRepositoryEditBasicController' => 'applications/diffusion/controller/DiffusionRepositoryEditBasicController.php',
-    'DiffusionRepositoryEditBranchesController' => 'applications/diffusion/controller/DiffusionRepositoryEditBranchesController.php',
     'DiffusionRepositoryEditConduitAPIMethod' => 'applications/diffusion/conduit/DiffusionRepositoryEditConduitAPIMethod.php',
     'DiffusionRepositoryEditController' => 'applications/diffusion/controller/DiffusionRepositoryEditController.php',
     'DiffusionRepositoryEditDangerousController' => 'applications/diffusion/controller/DiffusionRepositoryEditDangerousController.php',
     'DiffusionRepositoryEditDeleteController' => 'applications/diffusion/controller/DiffusionRepositoryEditDeleteController.php',
-    'DiffusionRepositoryEditEncodingController' => 'applications/diffusion/controller/DiffusionRepositoryEditEncodingController.php',
     'DiffusionRepositoryEditEngine' => 'applications/diffusion/editor/DiffusionRepositoryEditEngine.php',
-    'DiffusionRepositoryEditHostingController' => 'applications/diffusion/controller/DiffusionRepositoryEditHostingController.php',
-    'DiffusionRepositoryEditMainController' => 'applications/diffusion/controller/DiffusionRepositoryEditMainController.php',
-    'DiffusionRepositoryEditStagingController' => 'applications/diffusion/controller/DiffusionRepositoryEditStagingController.php',
-    'DiffusionRepositoryEditStorageController' => 'applications/diffusion/controller/DiffusionRepositoryEditStorageController.php',
-    'DiffusionRepositoryEditSubversionController' => 'applications/diffusion/controller/DiffusionRepositoryEditSubversionController.php',
     'DiffusionRepositoryEditUpdateController' => 'applications/diffusion/controller/DiffusionRepositoryEditUpdateController.php',
-    'DiffusionRepositoryEditproController' => 'applications/diffusion/controller/DiffusionRepositoryEditproController.php',
     'DiffusionRepositoryHistoryManagementPanel' => 'applications/diffusion/management/DiffusionRepositoryHistoryManagementPanel.php',
     'DiffusionRepositoryListController' => 'applications/diffusion/controller/DiffusionRepositoryListController.php',
     'DiffusionRepositoryManageController' => 'applications/diffusion/controller/DiffusionRepositoryManageController.php',
+    'DiffusionRepositoryManagePanelsController' => 'applications/diffusion/controller/DiffusionRepositoryManagePanelsController.php',
     'DiffusionRepositoryManagementPanel' => 'applications/diffusion/management/DiffusionRepositoryManagementPanel.php',
-    'DiffusionRepositoryNewController' => 'applications/diffusion/controller/DiffusionRepositoryNewController.php',
     'DiffusionRepositoryPath' => 'applications/diffusion/data/DiffusionRepositoryPath.php',
     'DiffusionRepositoryPoliciesManagementPanel' => 'applications/diffusion/management/DiffusionRepositoryPoliciesManagementPanel.php',
     'DiffusionRepositoryRef' => 'applications/diffusion/data/DiffusionRepositoryRef.php',
     'DiffusionRepositoryRemarkupRule' => 'applications/diffusion/remarkup/DiffusionRepositoryRemarkupRule.php',
     'DiffusionRepositorySearchConduitAPIMethod' => 'applications/diffusion/conduit/DiffusionRepositorySearchConduitAPIMethod.php',
     'DiffusionRepositoryStagingManagementPanel' => 'applications/diffusion/management/DiffusionRepositoryStagingManagementPanel.php',
     'DiffusionRepositoryStatusManagementPanel' => 'applications/diffusion/management/DiffusionRepositoryStatusManagementPanel.php',
     'DiffusionRepositoryStorageManagementPanel' => 'applications/diffusion/management/DiffusionRepositoryStorageManagementPanel.php',
-    'DiffusionRepositorySymbolsController' => 'applications/diffusion/controller/DiffusionRepositorySymbolsController.php',
+    'DiffusionRepositorySubversionManagementPanel' => 'applications/diffusion/management/DiffusionRepositorySubversionManagementPanel.php',
     'DiffusionRepositorySymbolsManagementPanel' => 'applications/diffusion/management/DiffusionRepositorySymbolsManagementPanel.php',
     'DiffusionRepositoryTag' => 'applications/diffusion/data/DiffusionRepositoryTag.php',
     'DiffusionRepositoryTestAutomationController' => 'applications/diffusion/controller/DiffusionRepositoryTestAutomationController.php',
+    'DiffusionRepositoryURICredentialController' => 'applications/diffusion/controller/DiffusionRepositoryURICredentialController.php',
     'DiffusionRepositoryURIDisableController' => 'applications/diffusion/controller/DiffusionRepositoryURIDisableController.php',
     'DiffusionRepositoryURIEditController' => 'applications/diffusion/controller/DiffusionRepositoryURIEditController.php',
     'DiffusionRepositoryURIViewController' => 'applications/diffusion/controller/DiffusionRepositoryURIViewController.php',
     'DiffusionRepositoryURIsIndexEngineExtension' => 'applications/diffusion/engineextension/DiffusionRepositoryURIsIndexEngineExtension.php',
     'DiffusionRepositoryURIsManagementPanel' => 'applications/diffusion/management/DiffusionRepositoryURIsManagementPanel.php',
     'DiffusionRepositoryURIsSearchEngineAttachment' => 'applications/diffusion/engineextension/DiffusionRepositoryURIsSearchEngineAttachment.php',
     'DiffusionRequest' => 'applications/diffusion/request/DiffusionRequest.php',
     'DiffusionResolveRefsConduitAPIMethod' => 'applications/diffusion/conduit/DiffusionResolveRefsConduitAPIMethod.php',
     'DiffusionResolveUserQuery' => 'applications/diffusion/query/DiffusionResolveUserQuery.php',
     'DiffusionSSHWorkflow' => 'applications/diffusion/ssh/DiffusionSSHWorkflow.php',
     'DiffusionSearchQueryConduitAPIMethod' => 'applications/diffusion/conduit/DiffusionSearchQueryConduitAPIMethod.php',
     'DiffusionServeController' => 'applications/diffusion/controller/DiffusionServeController.php',
     'DiffusionSetPasswordSettingsPanel' => 'applications/diffusion/panel/DiffusionSetPasswordSettingsPanel.php',
     'DiffusionSetupException' => 'applications/diffusion/exception/DiffusionSetupException.php',
     'DiffusionSubversionCommandEngine' => 'applications/diffusion/protocol/DiffusionSubversionCommandEngine.php',
     'DiffusionSubversionSSHWorkflow' => 'applications/diffusion/ssh/DiffusionSubversionSSHWorkflow.php',
     'DiffusionSubversionServeSSHWorkflow' => 'applications/diffusion/ssh/DiffusionSubversionServeSSHWorkflow.php',
     'DiffusionSubversionWireProtocol' => 'applications/diffusion/protocol/DiffusionSubversionWireProtocol.php',
     'DiffusionSubversionWireProtocolTestCase' => 'applications/diffusion/protocol/__tests__/DiffusionSubversionWireProtocolTestCase.php',
     'DiffusionSvnBlameQuery' => 'applications/diffusion/query/blame/DiffusionSvnBlameQuery.php',
     'DiffusionSvnFileContentQuery' => 'applications/diffusion/query/filecontent/DiffusionSvnFileContentQuery.php',
     'DiffusionSvnRawDiffQuery' => 'applications/diffusion/query/rawdiff/DiffusionSvnRawDiffQuery.php',
     'DiffusionSvnRequest' => 'applications/diffusion/request/DiffusionSvnRequest.php',
     'DiffusionSymbolController' => 'applications/diffusion/controller/DiffusionSymbolController.php',
     'DiffusionSymbolDatasource' => 'applications/diffusion/typeahead/DiffusionSymbolDatasource.php',
     'DiffusionSymbolQuery' => 'applications/diffusion/query/DiffusionSymbolQuery.php',
     'DiffusionTagListController' => 'applications/diffusion/controller/DiffusionTagListController.php',
     'DiffusionTagListView' => 'applications/diffusion/view/DiffusionTagListView.php',
     'DiffusionTagsQueryConduitAPIMethod' => 'applications/diffusion/conduit/DiffusionTagsQueryConduitAPIMethod.php',
     'DiffusionURIEditConduitAPIMethod' => 'applications/diffusion/conduit/DiffusionURIEditConduitAPIMethod.php',
     'DiffusionURIEditEngine' => 'applications/diffusion/editor/DiffusionURIEditEngine.php',
     'DiffusionURIEditor' => 'applications/diffusion/editor/DiffusionURIEditor.php',
     'DiffusionURITestCase' => 'applications/diffusion/request/__tests__/DiffusionURITestCase.php',
     'DiffusionUpdateCoverageConduitAPIMethod' => 'applications/diffusion/conduit/DiffusionUpdateCoverageConduitAPIMethod.php',
     'DiffusionView' => 'applications/diffusion/view/DiffusionView.php',
     'DivinerArticleAtomizer' => 'applications/diviner/atomizer/DivinerArticleAtomizer.php',
     'DivinerAtom' => 'applications/diviner/atom/DivinerAtom.php',
     'DivinerAtomCache' => 'applications/diviner/cache/DivinerAtomCache.php',
     'DivinerAtomController' => 'applications/diviner/controller/DivinerAtomController.php',
     'DivinerAtomListController' => 'applications/diviner/controller/DivinerAtomListController.php',
     'DivinerAtomPHIDType' => 'applications/diviner/phid/DivinerAtomPHIDType.php',
     'DivinerAtomQuery' => 'applications/diviner/query/DivinerAtomQuery.php',
     'DivinerAtomRef' => 'applications/diviner/atom/DivinerAtomRef.php',
     'DivinerAtomSearchEngine' => 'applications/diviner/query/DivinerAtomSearchEngine.php',
     'DivinerAtomizeWorkflow' => 'applications/diviner/workflow/DivinerAtomizeWorkflow.php',
     'DivinerAtomizer' => 'applications/diviner/atomizer/DivinerAtomizer.php',
     'DivinerBookController' => 'applications/diviner/controller/DivinerBookController.php',
     'DivinerBookDatasource' => 'applications/diviner/typeahead/DivinerBookDatasource.php',
     'DivinerBookEditController' => 'applications/diviner/controller/DivinerBookEditController.php',
     'DivinerBookItemView' => 'applications/diviner/view/DivinerBookItemView.php',
     'DivinerBookPHIDType' => 'applications/diviner/phid/DivinerBookPHIDType.php',
     'DivinerBookQuery' => 'applications/diviner/query/DivinerBookQuery.php',
     'DivinerController' => 'applications/diviner/controller/DivinerController.php',
     'DivinerDAO' => 'applications/diviner/storage/DivinerDAO.php',
     'DivinerDefaultEditCapability' => 'applications/diviner/capability/DivinerDefaultEditCapability.php',
     'DivinerDefaultRenderer' => 'applications/diviner/renderer/DivinerDefaultRenderer.php',
     'DivinerDefaultViewCapability' => 'applications/diviner/capability/DivinerDefaultViewCapability.php',
     'DivinerDiskCache' => 'applications/diviner/cache/DivinerDiskCache.php',
     'DivinerFileAtomizer' => 'applications/diviner/atomizer/DivinerFileAtomizer.php',
     'DivinerFindController' => 'applications/diviner/controller/DivinerFindController.php',
     'DivinerGenerateWorkflow' => 'applications/diviner/workflow/DivinerGenerateWorkflow.php',
     'DivinerLiveAtom' => 'applications/diviner/storage/DivinerLiveAtom.php',
     'DivinerLiveBook' => 'applications/diviner/storage/DivinerLiveBook.php',
     'DivinerLiveBookEditor' => 'applications/diviner/editor/DivinerLiveBookEditor.php',
     'DivinerLiveBookFulltextEngine' => 'applications/diviner/search/DivinerLiveBookFulltextEngine.php',
     'DivinerLiveBookTransaction' => 'applications/diviner/storage/DivinerLiveBookTransaction.php',
     'DivinerLiveBookTransactionQuery' => 'applications/diviner/query/DivinerLiveBookTransactionQuery.php',
     'DivinerLivePublisher' => 'applications/diviner/publisher/DivinerLivePublisher.php',
     'DivinerLiveSymbol' => 'applications/diviner/storage/DivinerLiveSymbol.php',
     'DivinerLiveSymbolFulltextEngine' => 'applications/diviner/search/DivinerLiveSymbolFulltextEngine.php',
     'DivinerMainController' => 'applications/diviner/controller/DivinerMainController.php',
     'DivinerPHPAtomizer' => 'applications/diviner/atomizer/DivinerPHPAtomizer.php',
     'DivinerParameterTableView' => 'applications/diviner/view/DivinerParameterTableView.php',
     'DivinerPublishCache' => 'applications/diviner/cache/DivinerPublishCache.php',
     'DivinerPublisher' => 'applications/diviner/publisher/DivinerPublisher.php',
     'DivinerRenderer' => 'applications/diviner/renderer/DivinerRenderer.php',
     'DivinerReturnTableView' => 'applications/diviner/view/DivinerReturnTableView.php',
     'DivinerSchemaSpec' => 'applications/diviner/storage/DivinerSchemaSpec.php',
     'DivinerSectionView' => 'applications/diviner/view/DivinerSectionView.php',
     'DivinerStaticPublisher' => 'applications/diviner/publisher/DivinerStaticPublisher.php',
     'DivinerSymbolRemarkupRule' => 'applications/diviner/markup/DivinerSymbolRemarkupRule.php',
     'DivinerWorkflow' => 'applications/diviner/workflow/DivinerWorkflow.php',
     'DoorkeeperAsanaFeedWorker' => 'applications/doorkeeper/worker/DoorkeeperAsanaFeedWorker.php',
     'DoorkeeperAsanaRemarkupRule' => 'applications/doorkeeper/remarkup/DoorkeeperAsanaRemarkupRule.php',
     'DoorkeeperBridge' => 'applications/doorkeeper/bridge/DoorkeeperBridge.php',
     'DoorkeeperBridgeAsana' => 'applications/doorkeeper/bridge/DoorkeeperBridgeAsana.php',
     'DoorkeeperBridgeGitHub' => 'applications/doorkeeper/bridge/DoorkeeperBridgeGitHub.php',
     'DoorkeeperBridgeGitHubIssue' => 'applications/doorkeeper/bridge/DoorkeeperBridgeGitHubIssue.php',
     'DoorkeeperBridgeGitHubUser' => 'applications/doorkeeper/bridge/DoorkeeperBridgeGitHubUser.php',
     'DoorkeeperBridgeJIRA' => 'applications/doorkeeper/bridge/DoorkeeperBridgeJIRA.php',
     'DoorkeeperBridgeJIRATestCase' => 'applications/doorkeeper/bridge/__tests__/DoorkeeperBridgeJIRATestCase.php',
     'DoorkeeperBridgedObjectCurtainExtension' => 'applications/doorkeeper/engineextension/DoorkeeperBridgedObjectCurtainExtension.php',
     'DoorkeeperBridgedObjectInterface' => 'applications/doorkeeper/bridge/DoorkeeperBridgedObjectInterface.php',
     'DoorkeeperDAO' => 'applications/doorkeeper/storage/DoorkeeperDAO.php',
     'DoorkeeperExternalObject' => 'applications/doorkeeper/storage/DoorkeeperExternalObject.php',
     'DoorkeeperExternalObjectPHIDType' => 'applications/doorkeeper/phid/DoorkeeperExternalObjectPHIDType.php',
     'DoorkeeperExternalObjectQuery' => 'applications/doorkeeper/query/DoorkeeperExternalObjectQuery.php',
     'DoorkeeperFeedStoryPublisher' => 'applications/doorkeeper/engine/DoorkeeperFeedStoryPublisher.php',
     'DoorkeeperFeedWorker' => 'applications/doorkeeper/worker/DoorkeeperFeedWorker.php',
     'DoorkeeperImportEngine' => 'applications/doorkeeper/engine/DoorkeeperImportEngine.php',
     'DoorkeeperJIRAFeedWorker' => 'applications/doorkeeper/worker/DoorkeeperJIRAFeedWorker.php',
     'DoorkeeperJIRARemarkupRule' => 'applications/doorkeeper/remarkup/DoorkeeperJIRARemarkupRule.php',
     'DoorkeeperMissingLinkException' => 'applications/doorkeeper/exception/DoorkeeperMissingLinkException.php',
     'DoorkeeperObjectRef' => 'applications/doorkeeper/engine/DoorkeeperObjectRef.php',
     'DoorkeeperRemarkupRule' => 'applications/doorkeeper/remarkup/DoorkeeperRemarkupRule.php',
     'DoorkeeperSchemaSpec' => 'applications/doorkeeper/storage/DoorkeeperSchemaSpec.php',
     'DoorkeeperTagView' => 'applications/doorkeeper/view/DoorkeeperTagView.php',
     'DoorkeeperTagsController' => 'applications/doorkeeper/controller/DoorkeeperTagsController.php',
     'DrydockAlmanacServiceHostBlueprintImplementation' => 'applications/drydock/blueprint/DrydockAlmanacServiceHostBlueprintImplementation.php',
     'DrydockApacheWebrootInterface' => 'applications/drydock/interface/webroot/DrydockApacheWebrootInterface.php',
     'DrydockAuthorization' => 'applications/drydock/storage/DrydockAuthorization.php',
     'DrydockAuthorizationAuthorizeController' => 'applications/drydock/controller/DrydockAuthorizationAuthorizeController.php',
     'DrydockAuthorizationListController' => 'applications/drydock/controller/DrydockAuthorizationListController.php',
     'DrydockAuthorizationListView' => 'applications/drydock/view/DrydockAuthorizationListView.php',
     'DrydockAuthorizationPHIDType' => 'applications/drydock/phid/DrydockAuthorizationPHIDType.php',
     'DrydockAuthorizationQuery' => 'applications/drydock/query/DrydockAuthorizationQuery.php',
     'DrydockAuthorizationSearchEngine' => 'applications/drydock/query/DrydockAuthorizationSearchEngine.php',
     'DrydockAuthorizationViewController' => 'applications/drydock/controller/DrydockAuthorizationViewController.php',
     'DrydockBlueprint' => 'applications/drydock/storage/DrydockBlueprint.php',
     'DrydockBlueprintController' => 'applications/drydock/controller/DrydockBlueprintController.php',
     'DrydockBlueprintCoreCustomField' => 'applications/drydock/customfield/DrydockBlueprintCoreCustomField.php',
     'DrydockBlueprintCustomField' => 'applications/drydock/customfield/DrydockBlueprintCustomField.php',
     'DrydockBlueprintDatasource' => 'applications/drydock/typeahead/DrydockBlueprintDatasource.php',
     'DrydockBlueprintDisableController' => 'applications/drydock/controller/DrydockBlueprintDisableController.php',
     'DrydockBlueprintEditController' => 'applications/drydock/controller/DrydockBlueprintEditController.php',
     'DrydockBlueprintEditEngine' => 'applications/drydock/editor/DrydockBlueprintEditEngine.php',
     'DrydockBlueprintEditor' => 'applications/drydock/editor/DrydockBlueprintEditor.php',
     'DrydockBlueprintImplementation' => 'applications/drydock/blueprint/DrydockBlueprintImplementation.php',
     'DrydockBlueprintImplementationTestCase' => 'applications/drydock/blueprint/__tests__/DrydockBlueprintImplementationTestCase.php',
     'DrydockBlueprintListController' => 'applications/drydock/controller/DrydockBlueprintListController.php',
     'DrydockBlueprintNameNgrams' => 'applications/drydock/storage/DrydockBlueprintNameNgrams.php',
     'DrydockBlueprintPHIDType' => 'applications/drydock/phid/DrydockBlueprintPHIDType.php',
     'DrydockBlueprintQuery' => 'applications/drydock/query/DrydockBlueprintQuery.php',
     'DrydockBlueprintSearchEngine' => 'applications/drydock/query/DrydockBlueprintSearchEngine.php',
     'DrydockBlueprintTransaction' => 'applications/drydock/storage/DrydockBlueprintTransaction.php',
     'DrydockBlueprintTransactionQuery' => 'applications/drydock/query/DrydockBlueprintTransactionQuery.php',
     'DrydockBlueprintViewController' => 'applications/drydock/controller/DrydockBlueprintViewController.php',
     'DrydockCommand' => 'applications/drydock/storage/DrydockCommand.php',
     'DrydockCommandError' => 'applications/drydock/exception/DrydockCommandError.php',
     'DrydockCommandInterface' => 'applications/drydock/interface/command/DrydockCommandInterface.php',
     'DrydockCommandQuery' => 'applications/drydock/query/DrydockCommandQuery.php',
     'DrydockConsoleController' => 'applications/drydock/controller/DrydockConsoleController.php',
     'DrydockConstants' => 'applications/drydock/constants/DrydockConstants.php',
     'DrydockController' => 'applications/drydock/controller/DrydockController.php',
     'DrydockCreateBlueprintsCapability' => 'applications/drydock/capability/DrydockCreateBlueprintsCapability.php',
     'DrydockDAO' => 'applications/drydock/storage/DrydockDAO.php',
     'DrydockDefaultEditCapability' => 'applications/drydock/capability/DrydockDefaultEditCapability.php',
     'DrydockDefaultViewCapability' => 'applications/drydock/capability/DrydockDefaultViewCapability.php',
     'DrydockFilesystemInterface' => 'applications/drydock/interface/filesystem/DrydockFilesystemInterface.php',
     'DrydockInterface' => 'applications/drydock/interface/DrydockInterface.php',
     'DrydockLandRepositoryOperation' => 'applications/drydock/operation/DrydockLandRepositoryOperation.php',
     'DrydockLease' => 'applications/drydock/storage/DrydockLease.php',
     'DrydockLeaseAcquiredLogType' => 'applications/drydock/logtype/DrydockLeaseAcquiredLogType.php',
     'DrydockLeaseActivatedLogType' => 'applications/drydock/logtype/DrydockLeaseActivatedLogType.php',
     'DrydockLeaseActivationFailureLogType' => 'applications/drydock/logtype/DrydockLeaseActivationFailureLogType.php',
     'DrydockLeaseActivationYieldLogType' => 'applications/drydock/logtype/DrydockLeaseActivationYieldLogType.php',
     'DrydockLeaseController' => 'applications/drydock/controller/DrydockLeaseController.php',
     'DrydockLeaseDatasource' => 'applications/drydock/typeahead/DrydockLeaseDatasource.php',
     'DrydockLeaseDestroyedLogType' => 'applications/drydock/logtype/DrydockLeaseDestroyedLogType.php',
     'DrydockLeaseListController' => 'applications/drydock/controller/DrydockLeaseListController.php',
     'DrydockLeaseListView' => 'applications/drydock/view/DrydockLeaseListView.php',
     'DrydockLeaseNoAuthorizationsLogType' => 'applications/drydock/logtype/DrydockLeaseNoAuthorizationsLogType.php',
     'DrydockLeaseNoBlueprintsLogType' => 'applications/drydock/logtype/DrydockLeaseNoBlueprintsLogType.php',
     'DrydockLeasePHIDType' => 'applications/drydock/phid/DrydockLeasePHIDType.php',
     'DrydockLeaseQuery' => 'applications/drydock/query/DrydockLeaseQuery.php',
     'DrydockLeaseQueuedLogType' => 'applications/drydock/logtype/DrydockLeaseQueuedLogType.php',
     'DrydockLeaseReclaimLogType' => 'applications/drydock/logtype/DrydockLeaseReclaimLogType.php',
     'DrydockLeaseReleaseController' => 'applications/drydock/controller/DrydockLeaseReleaseController.php',
     'DrydockLeaseReleasedLogType' => 'applications/drydock/logtype/DrydockLeaseReleasedLogType.php',
     'DrydockLeaseSearchEngine' => 'applications/drydock/query/DrydockLeaseSearchEngine.php',
     'DrydockLeaseStatus' => 'applications/drydock/constants/DrydockLeaseStatus.php',
     'DrydockLeaseUpdateWorker' => 'applications/drydock/worker/DrydockLeaseUpdateWorker.php',
     'DrydockLeaseViewController' => 'applications/drydock/controller/DrydockLeaseViewController.php',
     'DrydockLeaseWaitingForResourcesLogType' => 'applications/drydock/logtype/DrydockLeaseWaitingForResourcesLogType.php',
     'DrydockLog' => 'applications/drydock/storage/DrydockLog.php',
     'DrydockLogController' => 'applications/drydock/controller/DrydockLogController.php',
     'DrydockLogGarbageCollector' => 'applications/drydock/garbagecollector/DrydockLogGarbageCollector.php',
     'DrydockLogListController' => 'applications/drydock/controller/DrydockLogListController.php',
     'DrydockLogListView' => 'applications/drydock/view/DrydockLogListView.php',
     'DrydockLogQuery' => 'applications/drydock/query/DrydockLogQuery.php',
     'DrydockLogSearchEngine' => 'applications/drydock/query/DrydockLogSearchEngine.php',
     'DrydockLogType' => 'applications/drydock/logtype/DrydockLogType.php',
     'DrydockManagementCommandWorkflow' => 'applications/drydock/management/DrydockManagementCommandWorkflow.php',
     'DrydockManagementLeaseWorkflow' => 'applications/drydock/management/DrydockManagementLeaseWorkflow.php',
     'DrydockManagementReclaimWorkflow' => 'applications/drydock/management/DrydockManagementReclaimWorkflow.php',
     'DrydockManagementReleaseLeaseWorkflow' => 'applications/drydock/management/DrydockManagementReleaseLeaseWorkflow.php',
     'DrydockManagementReleaseResourceWorkflow' => 'applications/drydock/management/DrydockManagementReleaseResourceWorkflow.php',
     'DrydockManagementUpdateLeaseWorkflow' => 'applications/drydock/management/DrydockManagementUpdateLeaseWorkflow.php',
     'DrydockManagementUpdateResourceWorkflow' => 'applications/drydock/management/DrydockManagementUpdateResourceWorkflow.php',
     'DrydockManagementWorkflow' => 'applications/drydock/management/DrydockManagementWorkflow.php',
     'DrydockObjectAuthorizationView' => 'applications/drydock/view/DrydockObjectAuthorizationView.php',
     'DrydockQuery' => 'applications/drydock/query/DrydockQuery.php',
     'DrydockRepositoryOperation' => 'applications/drydock/storage/DrydockRepositoryOperation.php',
     'DrydockRepositoryOperationController' => 'applications/drydock/controller/DrydockRepositoryOperationController.php',
     'DrydockRepositoryOperationDismissController' => 'applications/drydock/controller/DrydockRepositoryOperationDismissController.php',
     'DrydockRepositoryOperationListController' => 'applications/drydock/controller/DrydockRepositoryOperationListController.php',
     'DrydockRepositoryOperationPHIDType' => 'applications/drydock/phid/DrydockRepositoryOperationPHIDType.php',
     'DrydockRepositoryOperationQuery' => 'applications/drydock/query/DrydockRepositoryOperationQuery.php',
     'DrydockRepositoryOperationSearchEngine' => 'applications/drydock/query/DrydockRepositoryOperationSearchEngine.php',
     'DrydockRepositoryOperationStatusController' => 'applications/drydock/controller/DrydockRepositoryOperationStatusController.php',
     'DrydockRepositoryOperationStatusView' => 'applications/drydock/view/DrydockRepositoryOperationStatusView.php',
     'DrydockRepositoryOperationType' => 'applications/drydock/operation/DrydockRepositoryOperationType.php',
     'DrydockRepositoryOperationUpdateWorker' => 'applications/drydock/worker/DrydockRepositoryOperationUpdateWorker.php',
     'DrydockRepositoryOperationViewController' => 'applications/drydock/controller/DrydockRepositoryOperationViewController.php',
     'DrydockResource' => 'applications/drydock/storage/DrydockResource.php',
     'DrydockResourceActivationFailureLogType' => 'applications/drydock/logtype/DrydockResourceActivationFailureLogType.php',
     'DrydockResourceActivationYieldLogType' => 'applications/drydock/logtype/DrydockResourceActivationYieldLogType.php',
     'DrydockResourceController' => 'applications/drydock/controller/DrydockResourceController.php',
     'DrydockResourceDatasource' => 'applications/drydock/typeahead/DrydockResourceDatasource.php',
     'DrydockResourceListController' => 'applications/drydock/controller/DrydockResourceListController.php',
     'DrydockResourceListView' => 'applications/drydock/view/DrydockResourceListView.php',
     'DrydockResourcePHIDType' => 'applications/drydock/phid/DrydockResourcePHIDType.php',
     'DrydockResourceQuery' => 'applications/drydock/query/DrydockResourceQuery.php',
     'DrydockResourceReclaimLogType' => 'applications/drydock/logtype/DrydockResourceReclaimLogType.php',
     'DrydockResourceReleaseController' => 'applications/drydock/controller/DrydockResourceReleaseController.php',
     'DrydockResourceSearchEngine' => 'applications/drydock/query/DrydockResourceSearchEngine.php',
     'DrydockResourceStatus' => 'applications/drydock/constants/DrydockResourceStatus.php',
     'DrydockResourceUpdateWorker' => 'applications/drydock/worker/DrydockResourceUpdateWorker.php',
     'DrydockResourceViewController' => 'applications/drydock/controller/DrydockResourceViewController.php',
     'DrydockSFTPFilesystemInterface' => 'applications/drydock/interface/filesystem/DrydockSFTPFilesystemInterface.php',
     'DrydockSSHCommandInterface' => 'applications/drydock/interface/command/DrydockSSHCommandInterface.php',
     'DrydockSchemaSpec' => 'applications/drydock/storage/DrydockSchemaSpec.php',
     'DrydockSlotLock' => 'applications/drydock/storage/DrydockSlotLock.php',
     'DrydockSlotLockException' => 'applications/drydock/exception/DrydockSlotLockException.php',
     'DrydockSlotLockFailureLogType' => 'applications/drydock/logtype/DrydockSlotLockFailureLogType.php',
     'DrydockTestRepositoryOperation' => 'applications/drydock/operation/DrydockTestRepositoryOperation.php',
     'DrydockWebrootInterface' => 'applications/drydock/interface/webroot/DrydockWebrootInterface.php',
     'DrydockWorker' => 'applications/drydock/worker/DrydockWorker.php',
     'DrydockWorkingCopyBlueprintImplementation' => 'applications/drydock/blueprint/DrydockWorkingCopyBlueprintImplementation.php',
     'FeedConduitAPIMethod' => 'applications/feed/conduit/FeedConduitAPIMethod.php',
     'FeedPublishConduitAPIMethod' => 'applications/feed/conduit/FeedPublishConduitAPIMethod.php',
     'FeedPublisherHTTPWorker' => 'applications/feed/worker/FeedPublisherHTTPWorker.php',
     'FeedPublisherWorker' => 'applications/feed/worker/FeedPublisherWorker.php',
     'FeedPushWorker' => 'applications/feed/worker/FeedPushWorker.php',
     'FeedQueryConduitAPIMethod' => 'applications/feed/conduit/FeedQueryConduitAPIMethod.php',
     'FeedStoryNotificationGarbageCollector' => 'applications/notification/garbagecollector/FeedStoryNotificationGarbageCollector.php',
     'FileAllocateConduitAPIMethod' => 'applications/files/conduit/FileAllocateConduitAPIMethod.php',
     'FileConduitAPIMethod' => 'applications/files/conduit/FileConduitAPIMethod.php',
     'FileCreateMailReceiver' => 'applications/files/mail/FileCreateMailReceiver.php',
     'FileDownloadConduitAPIMethod' => 'applications/files/conduit/FileDownloadConduitAPIMethod.php',
     'FileInfoConduitAPIMethod' => 'applications/files/conduit/FileInfoConduitAPIMethod.php',
     'FileMailReceiver' => 'applications/files/mail/FileMailReceiver.php',
     'FileQueryChunksConduitAPIMethod' => 'applications/files/conduit/FileQueryChunksConduitAPIMethod.php',
     'FileReplyHandler' => 'applications/files/mail/FileReplyHandler.php',
     'FileUploadChunkConduitAPIMethod' => 'applications/files/conduit/FileUploadChunkConduitAPIMethod.php',
     'FileUploadConduitAPIMethod' => 'applications/files/conduit/FileUploadConduitAPIMethod.php',
     'FileUploadHashConduitAPIMethod' => 'applications/files/conduit/FileUploadHashConduitAPIMethod.php',
     'FilesDefaultViewCapability' => 'applications/files/capability/FilesDefaultViewCapability.php',
     'FlagConduitAPIMethod' => 'applications/flag/conduit/FlagConduitAPIMethod.php',
     'FlagDeleteConduitAPIMethod' => 'applications/flag/conduit/FlagDeleteConduitAPIMethod.php',
     'FlagEditConduitAPIMethod' => 'applications/flag/conduit/FlagEditConduitAPIMethod.php',
     'FlagQueryConduitAPIMethod' => 'applications/flag/conduit/FlagQueryConduitAPIMethod.php',
     'FundBacker' => 'applications/fund/storage/FundBacker.php',
     'FundBackerCart' => 'applications/fund/phortune/FundBackerCart.php',
     'FundBackerEditor' => 'applications/fund/editor/FundBackerEditor.php',
     'FundBackerListController' => 'applications/fund/controller/FundBackerListController.php',
     'FundBackerPHIDType' => 'applications/fund/phid/FundBackerPHIDType.php',
     'FundBackerProduct' => 'applications/fund/phortune/FundBackerProduct.php',
     'FundBackerQuery' => 'applications/fund/query/FundBackerQuery.php',
     'FundBackerSearchEngine' => 'applications/fund/query/FundBackerSearchEngine.php',
     'FundBackerTransaction' => 'applications/fund/storage/FundBackerTransaction.php',
     'FundBackerTransactionQuery' => 'applications/fund/query/FundBackerTransactionQuery.php',
     'FundController' => 'applications/fund/controller/FundController.php',
     'FundCreateInitiativesCapability' => 'applications/fund/capability/FundCreateInitiativesCapability.php',
     'FundDAO' => 'applications/fund/storage/FundDAO.php',
     'FundDefaultViewCapability' => 'applications/fund/capability/FundDefaultViewCapability.php',
     'FundInitiative' => 'applications/fund/storage/FundInitiative.php',
     'FundInitiativeBackController' => 'applications/fund/controller/FundInitiativeBackController.php',
     'FundInitiativeCloseController' => 'applications/fund/controller/FundInitiativeCloseController.php',
     'FundInitiativeCommentController' => 'applications/fund/controller/FundInitiativeCommentController.php',
     'FundInitiativeEditController' => 'applications/fund/controller/FundInitiativeEditController.php',
     'FundInitiativeEditor' => 'applications/fund/editor/FundInitiativeEditor.php',
     'FundInitiativeFulltextEngine' => 'applications/fund/search/FundInitiativeFulltextEngine.php',
     'FundInitiativeListController' => 'applications/fund/controller/FundInitiativeListController.php',
     'FundInitiativePHIDType' => 'applications/fund/phid/FundInitiativePHIDType.php',
     'FundInitiativeQuery' => 'applications/fund/query/FundInitiativeQuery.php',
     'FundInitiativeRemarkupRule' => 'applications/fund/remarkup/FundInitiativeRemarkupRule.php',
     'FundInitiativeReplyHandler' => 'applications/fund/mail/FundInitiativeReplyHandler.php',
     'FundInitiativeSearchEngine' => 'applications/fund/query/FundInitiativeSearchEngine.php',
     'FundInitiativeTransaction' => 'applications/fund/storage/FundInitiativeTransaction.php',
     'FundInitiativeTransactionComment' => 'applications/fund/storage/FundInitiativeTransactionComment.php',
     'FundInitiativeTransactionQuery' => 'applications/fund/query/FundInitiativeTransactionQuery.php',
     'FundInitiativeViewController' => 'applications/fund/controller/FundInitiativeViewController.php',
     'FundSchemaSpec' => 'applications/fund/storage/FundSchemaSpec.php',
     'HarbormasterArcLintBuildStepImplementation' => 'applications/harbormaster/step/HarbormasterArcLintBuildStepImplementation.php',
     'HarbormasterArcUnitBuildStepImplementation' => 'applications/harbormaster/step/HarbormasterArcUnitBuildStepImplementation.php',
     'HarbormasterArtifact' => 'applications/harbormaster/artifact/HarbormasterArtifact.php',
     'HarbormasterAutotargetsTestCase' => 'applications/harbormaster/__tests__/HarbormasterAutotargetsTestCase.php',
     'HarbormasterBuild' => 'applications/harbormaster/storage/build/HarbormasterBuild.php',
     'HarbormasterBuildAbortedException' => 'applications/harbormaster/exception/HarbormasterBuildAbortedException.php',
     'HarbormasterBuildActionController' => 'applications/harbormaster/controller/HarbormasterBuildActionController.php',
     'HarbormasterBuildArcanistAutoplan' => 'applications/harbormaster/autoplan/HarbormasterBuildArcanistAutoplan.php',
     'HarbormasterBuildArtifact' => 'applications/harbormaster/storage/build/HarbormasterBuildArtifact.php',
     'HarbormasterBuildArtifactPHIDType' => 'applications/harbormaster/phid/HarbormasterBuildArtifactPHIDType.php',
     'HarbormasterBuildArtifactQuery' => 'applications/harbormaster/query/HarbormasterBuildArtifactQuery.php',
     'HarbormasterBuildAutoplan' => 'applications/harbormaster/autoplan/HarbormasterBuildAutoplan.php',
     'HarbormasterBuildCommand' => 'applications/harbormaster/storage/HarbormasterBuildCommand.php',
     'HarbormasterBuildDependencyDatasource' => 'applications/harbormaster/typeahead/HarbormasterBuildDependencyDatasource.php',
     'HarbormasterBuildEngine' => 'applications/harbormaster/engine/HarbormasterBuildEngine.php',
     'HarbormasterBuildFailureException' => 'applications/harbormaster/exception/HarbormasterBuildFailureException.php',
     'HarbormasterBuildGraph' => 'applications/harbormaster/engine/HarbormasterBuildGraph.php',
     'HarbormasterBuildLintMessage' => 'applications/harbormaster/storage/build/HarbormasterBuildLintMessage.php',
     'HarbormasterBuildLog' => 'applications/harbormaster/storage/build/HarbormasterBuildLog.php',
     'HarbormasterBuildLogChunk' => 'applications/harbormaster/storage/build/HarbormasterBuildLogChunk.php',
     'HarbormasterBuildLogChunkIterator' => 'applications/harbormaster/storage/build/HarbormasterBuildLogChunkIterator.php',
     'HarbormasterBuildLogPHIDType' => 'applications/harbormaster/phid/HarbormasterBuildLogPHIDType.php',
     'HarbormasterBuildLogQuery' => 'applications/harbormaster/query/HarbormasterBuildLogQuery.php',
     'HarbormasterBuildMessage' => 'applications/harbormaster/storage/HarbormasterBuildMessage.php',
     'HarbormasterBuildMessageQuery' => 'applications/harbormaster/query/HarbormasterBuildMessageQuery.php',
     'HarbormasterBuildPHIDType' => 'applications/harbormaster/phid/HarbormasterBuildPHIDType.php',
     'HarbormasterBuildPlan' => 'applications/harbormaster/storage/configuration/HarbormasterBuildPlan.php',
     'HarbormasterBuildPlanDatasource' => 'applications/harbormaster/typeahead/HarbormasterBuildPlanDatasource.php',
     'HarbormasterBuildPlanDefaultEditCapability' => 'applications/harbormaster/capability/HarbormasterBuildPlanDefaultEditCapability.php',
     'HarbormasterBuildPlanDefaultViewCapability' => 'applications/harbormaster/capability/HarbormasterBuildPlanDefaultViewCapability.php',
     'HarbormasterBuildPlanEditEngine' => 'applications/harbormaster/editor/HarbormasterBuildPlanEditEngine.php',
     'HarbormasterBuildPlanEditor' => 'applications/harbormaster/editor/HarbormasterBuildPlanEditor.php',
     'HarbormasterBuildPlanNameNgrams' => 'applications/harbormaster/storage/configuration/HarbormasterBuildPlanNameNgrams.php',
     'HarbormasterBuildPlanPHIDType' => 'applications/harbormaster/phid/HarbormasterBuildPlanPHIDType.php',
     'HarbormasterBuildPlanQuery' => 'applications/harbormaster/query/HarbormasterBuildPlanQuery.php',
     'HarbormasterBuildPlanSearchEngine' => 'applications/harbormaster/query/HarbormasterBuildPlanSearchEngine.php',
     'HarbormasterBuildPlanTransaction' => 'applications/harbormaster/storage/configuration/HarbormasterBuildPlanTransaction.php',
     'HarbormasterBuildPlanTransactionQuery' => 'applications/harbormaster/query/HarbormasterBuildPlanTransactionQuery.php',
     'HarbormasterBuildQuery' => 'applications/harbormaster/query/HarbormasterBuildQuery.php',
     'HarbormasterBuildRequest' => 'applications/harbormaster/engine/HarbormasterBuildRequest.php',
     'HarbormasterBuildStep' => 'applications/harbormaster/storage/configuration/HarbormasterBuildStep.php',
     'HarbormasterBuildStepCoreCustomField' => 'applications/harbormaster/customfield/HarbormasterBuildStepCoreCustomField.php',
     'HarbormasterBuildStepCustomField' => 'applications/harbormaster/customfield/HarbormasterBuildStepCustomField.php',
     'HarbormasterBuildStepEditor' => 'applications/harbormaster/editor/HarbormasterBuildStepEditor.php',
     'HarbormasterBuildStepGroup' => 'applications/harbormaster/stepgroup/HarbormasterBuildStepGroup.php',
     'HarbormasterBuildStepImplementation' => 'applications/harbormaster/step/HarbormasterBuildStepImplementation.php',
     'HarbormasterBuildStepImplementationTestCase' => 'applications/harbormaster/step/__tests__/HarbormasterBuildStepImplementationTestCase.php',
     'HarbormasterBuildStepPHIDType' => 'applications/harbormaster/phid/HarbormasterBuildStepPHIDType.php',
     'HarbormasterBuildStepQuery' => 'applications/harbormaster/query/HarbormasterBuildStepQuery.php',
     'HarbormasterBuildStepTransaction' => 'applications/harbormaster/storage/configuration/HarbormasterBuildStepTransaction.php',
     'HarbormasterBuildStepTransactionQuery' => 'applications/harbormaster/query/HarbormasterBuildStepTransactionQuery.php',
     'HarbormasterBuildTarget' => 'applications/harbormaster/storage/build/HarbormasterBuildTarget.php',
     'HarbormasterBuildTargetPHIDType' => 'applications/harbormaster/phid/HarbormasterBuildTargetPHIDType.php',
     'HarbormasterBuildTargetQuery' => 'applications/harbormaster/query/HarbormasterBuildTargetQuery.php',
     'HarbormasterBuildTransaction' => 'applications/harbormaster/storage/HarbormasterBuildTransaction.php',
     'HarbormasterBuildTransactionEditor' => 'applications/harbormaster/editor/HarbormasterBuildTransactionEditor.php',
     'HarbormasterBuildTransactionQuery' => 'applications/harbormaster/query/HarbormasterBuildTransactionQuery.php',
     'HarbormasterBuildUnitMessage' => 'applications/harbormaster/storage/build/HarbormasterBuildUnitMessage.php',
     'HarbormasterBuildViewController' => 'applications/harbormaster/controller/HarbormasterBuildViewController.php',
     'HarbormasterBuildWorker' => 'applications/harbormaster/worker/HarbormasterBuildWorker.php',
     'HarbormasterBuildable' => 'applications/harbormaster/storage/HarbormasterBuildable.php',
     'HarbormasterBuildableActionController' => 'applications/harbormaster/controller/HarbormasterBuildableActionController.php',
     'HarbormasterBuildableAdapterInterface' => 'applications/harbormaster/herald/HarbormasterBuildableAdapterInterface.php',
     'HarbormasterBuildableInterface' => 'applications/harbormaster/interface/HarbormasterBuildableInterface.php',
     'HarbormasterBuildableListController' => 'applications/harbormaster/controller/HarbormasterBuildableListController.php',
     'HarbormasterBuildablePHIDType' => 'applications/harbormaster/phid/HarbormasterBuildablePHIDType.php',
     'HarbormasterBuildableQuery' => 'applications/harbormaster/query/HarbormasterBuildableQuery.php',
     'HarbormasterBuildableSearchEngine' => 'applications/harbormaster/query/HarbormasterBuildableSearchEngine.php',
     'HarbormasterBuildableTransaction' => 'applications/harbormaster/storage/HarbormasterBuildableTransaction.php',
     'HarbormasterBuildableTransactionEditor' => 'applications/harbormaster/editor/HarbormasterBuildableTransactionEditor.php',
     'HarbormasterBuildableTransactionQuery' => 'applications/harbormaster/query/HarbormasterBuildableTransactionQuery.php',
     'HarbormasterBuildableViewController' => 'applications/harbormaster/controller/HarbormasterBuildableViewController.php',
     'HarbormasterBuiltinBuildStepGroup' => 'applications/harbormaster/stepgroup/HarbormasterBuiltinBuildStepGroup.php',
     'HarbormasterCircleCIBuildStepImplementation' => 'applications/harbormaster/step/HarbormasterCircleCIBuildStepImplementation.php',
     'HarbormasterCircleCIBuildableInterface' => 'applications/harbormaster/interface/HarbormasterCircleCIBuildableInterface.php',
     'HarbormasterCircleCIHookController' => 'applications/harbormaster/controller/HarbormasterCircleCIHookController.php',
     'HarbormasterConduitAPIMethod' => 'applications/harbormaster/conduit/HarbormasterConduitAPIMethod.php',
     'HarbormasterController' => 'applications/harbormaster/controller/HarbormasterController.php',
     'HarbormasterCreateArtifactConduitAPIMethod' => 'applications/harbormaster/conduit/HarbormasterCreateArtifactConduitAPIMethod.php',
     'HarbormasterCreatePlansCapability' => 'applications/harbormaster/capability/HarbormasterCreatePlansCapability.php',
     'HarbormasterDAO' => 'applications/harbormaster/storage/HarbormasterDAO.php',
     'HarbormasterDrydockBuildStepGroup' => 'applications/harbormaster/stepgroup/HarbormasterDrydockBuildStepGroup.php',
     'HarbormasterDrydockCommandBuildStepImplementation' => 'applications/harbormaster/step/HarbormasterDrydockCommandBuildStepImplementation.php',
     'HarbormasterDrydockLeaseArtifact' => 'applications/harbormaster/artifact/HarbormasterDrydockLeaseArtifact.php',
     'HarbormasterExecFuture' => 'applications/harbormaster/future/HarbormasterExecFuture.php',
     'HarbormasterExternalBuildStepGroup' => 'applications/harbormaster/stepgroup/HarbormasterExternalBuildStepGroup.php',
     'HarbormasterFileArtifact' => 'applications/harbormaster/artifact/HarbormasterFileArtifact.php',
     'HarbormasterHTTPRequestBuildStepImplementation' => 'applications/harbormaster/step/HarbormasterHTTPRequestBuildStepImplementation.php',
     'HarbormasterHostArtifact' => 'applications/harbormaster/artifact/HarbormasterHostArtifact.php',
     'HarbormasterLeaseWorkingCopyBuildStepImplementation' => 'applications/harbormaster/step/HarbormasterLeaseWorkingCopyBuildStepImplementation.php',
     'HarbormasterLintMessagesController' => 'applications/harbormaster/controller/HarbormasterLintMessagesController.php',
     'HarbormasterLintPropertyView' => 'applications/harbormaster/view/HarbormasterLintPropertyView.php',
     'HarbormasterManagementArchiveLogsWorkflow' => 'applications/harbormaster/management/HarbormasterManagementArchiveLogsWorkflow.php',
     'HarbormasterManagementBuildWorkflow' => 'applications/harbormaster/management/HarbormasterManagementBuildWorkflow.php',
     'HarbormasterManagementUpdateWorkflow' => 'applications/harbormaster/management/HarbormasterManagementUpdateWorkflow.php',
     'HarbormasterManagementWorkflow' => 'applications/harbormaster/management/HarbormasterManagementWorkflow.php',
     'HarbormasterMessageType' => 'applications/harbormaster/engine/HarbormasterMessageType.php',
     'HarbormasterObject' => 'applications/harbormaster/storage/HarbormasterObject.php',
     'HarbormasterOtherBuildStepGroup' => 'applications/harbormaster/stepgroup/HarbormasterOtherBuildStepGroup.php',
     'HarbormasterPlanController' => 'applications/harbormaster/controller/HarbormasterPlanController.php',
     'HarbormasterPlanDisableController' => 'applications/harbormaster/controller/HarbormasterPlanDisableController.php',
     'HarbormasterPlanEditController' => 'applications/harbormaster/controller/HarbormasterPlanEditController.php',
     'HarbormasterPlanListController' => 'applications/harbormaster/controller/HarbormasterPlanListController.php',
     'HarbormasterPlanRunController' => 'applications/harbormaster/controller/HarbormasterPlanRunController.php',
     'HarbormasterPlanViewController' => 'applications/harbormaster/controller/HarbormasterPlanViewController.php',
     'HarbormasterPrototypeBuildStepGroup' => 'applications/harbormaster/stepgroup/HarbormasterPrototypeBuildStepGroup.php',
     'HarbormasterPublishFragmentBuildStepImplementation' => 'applications/harbormaster/step/HarbormasterPublishFragmentBuildStepImplementation.php',
     'HarbormasterQueryAutotargetsConduitAPIMethod' => 'applications/harbormaster/conduit/HarbormasterQueryAutotargetsConduitAPIMethod.php',
     'HarbormasterQueryBuildablesConduitAPIMethod' => 'applications/harbormaster/conduit/HarbormasterQueryBuildablesConduitAPIMethod.php',
     'HarbormasterQueryBuildsConduitAPIMethod' => 'applications/harbormaster/conduit/HarbormasterQueryBuildsConduitAPIMethod.php',
     'HarbormasterRemarkupRule' => 'applications/harbormaster/remarkup/HarbormasterRemarkupRule.php',
     'HarbormasterRunBuildPlansHeraldAction' => 'applications/harbormaster/herald/HarbormasterRunBuildPlansHeraldAction.php',
     'HarbormasterSchemaSpec' => 'applications/harbormaster/storage/HarbormasterSchemaSpec.php',
     'HarbormasterScratchTable' => 'applications/harbormaster/storage/HarbormasterScratchTable.php',
     'HarbormasterSendMessageConduitAPIMethod' => 'applications/harbormaster/conduit/HarbormasterSendMessageConduitAPIMethod.php',
     'HarbormasterSleepBuildStepImplementation' => 'applications/harbormaster/step/HarbormasterSleepBuildStepImplementation.php',
     'HarbormasterStepAddController' => 'applications/harbormaster/controller/HarbormasterStepAddController.php',
     'HarbormasterStepDeleteController' => 'applications/harbormaster/controller/HarbormasterStepDeleteController.php',
     'HarbormasterStepEditController' => 'applications/harbormaster/controller/HarbormasterStepEditController.php',
     'HarbormasterStepViewController' => 'applications/harbormaster/controller/HarbormasterStepViewController.php',
     'HarbormasterTargetEngine' => 'applications/harbormaster/engine/HarbormasterTargetEngine.php',
     'HarbormasterTargetWorker' => 'applications/harbormaster/worker/HarbormasterTargetWorker.php',
     'HarbormasterTestBuildStepGroup' => 'applications/harbormaster/stepgroup/HarbormasterTestBuildStepGroup.php',
     'HarbormasterThrowExceptionBuildStep' => 'applications/harbormaster/step/HarbormasterThrowExceptionBuildStep.php',
     'HarbormasterUIEventListener' => 'applications/harbormaster/event/HarbormasterUIEventListener.php',
     'HarbormasterURIArtifact' => 'applications/harbormaster/artifact/HarbormasterURIArtifact.php',
     'HarbormasterUnitMessageListController' => 'applications/harbormaster/controller/HarbormasterUnitMessageListController.php',
     'HarbormasterUnitMessageViewController' => 'applications/harbormaster/controller/HarbormasterUnitMessageViewController.php',
     'HarbormasterUnitPropertyView' => 'applications/harbormaster/view/HarbormasterUnitPropertyView.php',
     'HarbormasterUnitStatus' => 'applications/harbormaster/constants/HarbormasterUnitStatus.php',
     'HarbormasterUnitSummaryView' => 'applications/harbormaster/view/HarbormasterUnitSummaryView.php',
     'HarbormasterUploadArtifactBuildStepImplementation' => 'applications/harbormaster/step/HarbormasterUploadArtifactBuildStepImplementation.php',
     'HarbormasterWaitForPreviousBuildStepImplementation' => 'applications/harbormaster/step/HarbormasterWaitForPreviousBuildStepImplementation.php',
     'HarbormasterWorker' => 'applications/harbormaster/worker/HarbormasterWorker.php',
     'HarbormasterWorkingCopyArtifact' => 'applications/harbormaster/artifact/HarbormasterWorkingCopyArtifact.php',
     'HeraldAction' => 'applications/herald/action/HeraldAction.php',
     'HeraldActionGroup' => 'applications/herald/action/HeraldActionGroup.php',
     'HeraldActionRecord' => 'applications/herald/storage/HeraldActionRecord.php',
     'HeraldAdapter' => 'applications/herald/adapter/HeraldAdapter.php',
     'HeraldAlwaysField' => 'applications/herald/field/HeraldAlwaysField.php',
     'HeraldAnotherRuleField' => 'applications/herald/field/HeraldAnotherRuleField.php',
     'HeraldApplicationActionGroup' => 'applications/herald/action/HeraldApplicationActionGroup.php',
     'HeraldApplyTranscript' => 'applications/herald/storage/transcript/HeraldApplyTranscript.php',
     'HeraldBasicFieldGroup' => 'applications/herald/field/HeraldBasicFieldGroup.php',
     'HeraldCommitAdapter' => 'applications/diffusion/herald/HeraldCommitAdapter.php',
     'HeraldCondition' => 'applications/herald/storage/HeraldCondition.php',
     'HeraldConditionTranscript' => 'applications/herald/storage/transcript/HeraldConditionTranscript.php',
     'HeraldContentSourceField' => 'applications/herald/field/HeraldContentSourceField.php',
     'HeraldController' => 'applications/herald/controller/HeraldController.php',
     'HeraldDAO' => 'applications/herald/storage/HeraldDAO.php',
     'HeraldDifferentialAdapter' => 'applications/differential/herald/HeraldDifferentialAdapter.php',
     'HeraldDifferentialDiffAdapter' => 'applications/differential/herald/HeraldDifferentialDiffAdapter.php',
     'HeraldDifferentialRevisionAdapter' => 'applications/differential/herald/HeraldDifferentialRevisionAdapter.php',
     'HeraldDisableController' => 'applications/herald/controller/HeraldDisableController.php',
     'HeraldDoNothingAction' => 'applications/herald/action/HeraldDoNothingAction.php',
     'HeraldEditFieldGroup' => 'applications/herald/field/HeraldEditFieldGroup.php',
     'HeraldEffect' => 'applications/herald/engine/HeraldEffect.php',
     'HeraldEmptyFieldValue' => 'applications/herald/value/HeraldEmptyFieldValue.php',
     'HeraldEngine' => 'applications/herald/engine/HeraldEngine.php',
     'HeraldExactProjectsField' => 'applications/project/herald/HeraldExactProjectsField.php',
     'HeraldField' => 'applications/herald/field/HeraldField.php',
     'HeraldFieldGroup' => 'applications/herald/field/HeraldFieldGroup.php',
     'HeraldFieldTestCase' => 'applications/herald/field/__tests__/HeraldFieldTestCase.php',
     'HeraldFieldValue' => 'applications/herald/value/HeraldFieldValue.php',
     'HeraldGroup' => 'applications/herald/group/HeraldGroup.php',
     'HeraldInvalidActionException' => 'applications/herald/engine/exception/HeraldInvalidActionException.php',
     'HeraldInvalidConditionException' => 'applications/herald/engine/exception/HeraldInvalidConditionException.php',
     'HeraldManageGlobalRulesCapability' => 'applications/herald/capability/HeraldManageGlobalRulesCapability.php',
     'HeraldManiphestTaskAdapter' => 'applications/maniphest/herald/HeraldManiphestTaskAdapter.php',
     'HeraldNewController' => 'applications/herald/controller/HeraldNewController.php',
     'HeraldNewObjectField' => 'applications/herald/field/HeraldNewObjectField.php',
     'HeraldNotifyActionGroup' => 'applications/herald/action/HeraldNotifyActionGroup.php',
     'HeraldObjectTranscript' => 'applications/herald/storage/transcript/HeraldObjectTranscript.php',
     'HeraldPhameBlogAdapter' => 'applications/phame/herald/HeraldPhameBlogAdapter.php',
     'HeraldPhamePostAdapter' => 'applications/phame/herald/HeraldPhamePostAdapter.php',
     'HeraldPholioMockAdapter' => 'applications/pholio/herald/HeraldPholioMockAdapter.php',
     'HeraldPonderQuestionAdapter' => 'applications/ponder/herald/HeraldPonderQuestionAdapter.php',
     'HeraldPreCommitAdapter' => 'applications/diffusion/herald/HeraldPreCommitAdapter.php',
     'HeraldPreCommitContentAdapter' => 'applications/diffusion/herald/HeraldPreCommitContentAdapter.php',
     'HeraldPreCommitRefAdapter' => 'applications/diffusion/herald/HeraldPreCommitRefAdapter.php',
     'HeraldPreventActionGroup' => 'applications/herald/action/HeraldPreventActionGroup.php',
     'HeraldProjectsField' => 'applications/project/herald/HeraldProjectsField.php',
     'HeraldRecursiveConditionsException' => 'applications/herald/engine/exception/HeraldRecursiveConditionsException.php',
     'HeraldRelatedFieldGroup' => 'applications/herald/field/HeraldRelatedFieldGroup.php',
     'HeraldRemarkupRule' => 'applications/herald/remarkup/HeraldRemarkupRule.php',
     'HeraldRepetitionPolicyConfig' => 'applications/herald/config/HeraldRepetitionPolicyConfig.php',
     'HeraldRule' => 'applications/herald/storage/HeraldRule.php',
     'HeraldRuleController' => 'applications/herald/controller/HeraldRuleController.php',
     'HeraldRuleEditor' => 'applications/herald/editor/HeraldRuleEditor.php',
     'HeraldRuleListController' => 'applications/herald/controller/HeraldRuleListController.php',
     'HeraldRulePHIDType' => 'applications/herald/phid/HeraldRulePHIDType.php',
     'HeraldRuleQuery' => 'applications/herald/query/HeraldRuleQuery.php',
     'HeraldRuleSearchEngine' => 'applications/herald/query/HeraldRuleSearchEngine.php',
     'HeraldRuleSerializer' => 'applications/herald/editor/HeraldRuleSerializer.php',
     'HeraldRuleTestCase' => 'applications/herald/storage/__tests__/HeraldRuleTestCase.php',
     'HeraldRuleTransaction' => 'applications/herald/storage/HeraldRuleTransaction.php',
     'HeraldRuleTransactionComment' => 'applications/herald/storage/HeraldRuleTransactionComment.php',
     'HeraldRuleTranscript' => 'applications/herald/storage/transcript/HeraldRuleTranscript.php',
     'HeraldRuleTypeConfig' => 'applications/herald/config/HeraldRuleTypeConfig.php',
     'HeraldRuleViewController' => 'applications/herald/controller/HeraldRuleViewController.php',
     'HeraldSchemaSpec' => 'applications/herald/storage/HeraldSchemaSpec.php',
     'HeraldSelectFieldValue' => 'applications/herald/value/HeraldSelectFieldValue.php',
     'HeraldSpaceField' => 'applications/spaces/herald/HeraldSpaceField.php',
     'HeraldSubscribersField' => 'applications/subscriptions/herald/HeraldSubscribersField.php',
     'HeraldSupportActionGroup' => 'applications/herald/action/HeraldSupportActionGroup.php',
     'HeraldSupportFieldGroup' => 'applications/herald/field/HeraldSupportFieldGroup.php',
     'HeraldTestConsoleController' => 'applications/herald/controller/HeraldTestConsoleController.php',
     'HeraldTextFieldValue' => 'applications/herald/value/HeraldTextFieldValue.php',
     'HeraldTokenizerFieldValue' => 'applications/herald/value/HeraldTokenizerFieldValue.php',
     'HeraldTransactionQuery' => 'applications/herald/query/HeraldTransactionQuery.php',
     'HeraldTranscript' => 'applications/herald/storage/transcript/HeraldTranscript.php',
     'HeraldTranscriptController' => 'applications/herald/controller/HeraldTranscriptController.php',
     'HeraldTranscriptDestructionEngineExtension' => 'applications/herald/engineextension/HeraldTranscriptDestructionEngineExtension.php',
     'HeraldTranscriptGarbageCollector' => 'applications/herald/garbagecollector/HeraldTranscriptGarbageCollector.php',
     'HeraldTranscriptListController' => 'applications/herald/controller/HeraldTranscriptListController.php',
     'HeraldTranscriptPHIDType' => 'applications/herald/phid/HeraldTranscriptPHIDType.php',
     'HeraldTranscriptQuery' => 'applications/herald/query/HeraldTranscriptQuery.php',
     'HeraldTranscriptSearchEngine' => 'applications/herald/query/HeraldTranscriptSearchEngine.php',
     'HeraldTranscriptTestCase' => 'applications/herald/storage/__tests__/HeraldTranscriptTestCase.php',
     'HeraldUtilityActionGroup' => 'applications/herald/action/HeraldUtilityActionGroup.php',
     'Javelin' => 'infrastructure/javelin/Javelin.php',
     'JavelinReactorUIExample' => 'applications/uiexample/examples/JavelinReactorUIExample.php',
     'JavelinUIExample' => 'applications/uiexample/examples/JavelinUIExample.php',
     'JavelinViewExampleServerView' => 'applications/uiexample/examples/JavelinViewExampleServerView.php',
     'JavelinViewUIExample' => 'applications/uiexample/examples/JavelinViewUIExample.php',
     'LegalpadController' => 'applications/legalpad/controller/LegalpadController.php',
     'LegalpadCreateDocumentsCapability' => 'applications/legalpad/capability/LegalpadCreateDocumentsCapability.php',
     'LegalpadDAO' => 'applications/legalpad/storage/LegalpadDAO.php',
     'LegalpadDefaultEditCapability' => 'applications/legalpad/capability/LegalpadDefaultEditCapability.php',
     'LegalpadDefaultViewCapability' => 'applications/legalpad/capability/LegalpadDefaultViewCapability.php',
     'LegalpadDocument' => 'applications/legalpad/storage/LegalpadDocument.php',
     'LegalpadDocumentBody' => 'applications/legalpad/storage/LegalpadDocumentBody.php',
     'LegalpadDocumentCommentController' => 'applications/legalpad/controller/LegalpadDocumentCommentController.php',
     'LegalpadDocumentDatasource' => 'applications/legalpad/typeahead/LegalpadDocumentDatasource.php',
     'LegalpadDocumentDoneController' => 'applications/legalpad/controller/LegalpadDocumentDoneController.php',
     'LegalpadDocumentEditController' => 'applications/legalpad/controller/LegalpadDocumentEditController.php',
     'LegalpadDocumentEditor' => 'applications/legalpad/editor/LegalpadDocumentEditor.php',
     'LegalpadDocumentListController' => 'applications/legalpad/controller/LegalpadDocumentListController.php',
     'LegalpadDocumentManageController' => 'applications/legalpad/controller/LegalpadDocumentManageController.php',
     'LegalpadDocumentQuery' => 'applications/legalpad/query/LegalpadDocumentQuery.php',
     'LegalpadDocumentRemarkupRule' => 'applications/legalpad/remarkup/LegalpadDocumentRemarkupRule.php',
     'LegalpadDocumentSearchEngine' => 'applications/legalpad/query/LegalpadDocumentSearchEngine.php',
     'LegalpadDocumentSignController' => 'applications/legalpad/controller/LegalpadDocumentSignController.php',
     'LegalpadDocumentSignature' => 'applications/legalpad/storage/LegalpadDocumentSignature.php',
     'LegalpadDocumentSignatureAddController' => 'applications/legalpad/controller/LegalpadDocumentSignatureAddController.php',
     'LegalpadDocumentSignatureListController' => 'applications/legalpad/controller/LegalpadDocumentSignatureListController.php',
     'LegalpadDocumentSignatureQuery' => 'applications/legalpad/query/LegalpadDocumentSignatureQuery.php',
     'LegalpadDocumentSignatureSearchEngine' => 'applications/legalpad/query/LegalpadDocumentSignatureSearchEngine.php',
     'LegalpadDocumentSignatureVerificationController' => 'applications/legalpad/controller/LegalpadDocumentSignatureVerificationController.php',
     'LegalpadDocumentSignatureViewController' => 'applications/legalpad/controller/LegalpadDocumentSignatureViewController.php',
     'LegalpadMailReceiver' => 'applications/legalpad/mail/LegalpadMailReceiver.php',
     'LegalpadObjectNeedsSignatureEdgeType' => 'applications/legalpad/edge/LegalpadObjectNeedsSignatureEdgeType.php',
     'LegalpadReplyHandler' => 'applications/legalpad/mail/LegalpadReplyHandler.php',
     'LegalpadRequireSignatureHeraldAction' => 'applications/legalpad/herald/LegalpadRequireSignatureHeraldAction.php',
     'LegalpadSchemaSpec' => 'applications/legalpad/storage/LegalpadSchemaSpec.php',
     'LegalpadSignatureNeededByObjectEdgeType' => 'applications/legalpad/edge/LegalpadSignatureNeededByObjectEdgeType.php',
     'LegalpadTransaction' => 'applications/legalpad/storage/LegalpadTransaction.php',
     'LegalpadTransactionComment' => 'applications/legalpad/storage/LegalpadTransactionComment.php',
     'LegalpadTransactionQuery' => 'applications/legalpad/query/LegalpadTransactionQuery.php',
     'LegalpadTransactionView' => 'applications/legalpad/view/LegalpadTransactionView.php',
     'LiskChunkTestCase' => 'infrastructure/storage/lisk/__tests__/LiskChunkTestCase.php',
     'LiskDAO' => 'infrastructure/storage/lisk/LiskDAO.php',
     'LiskDAOSet' => 'infrastructure/storage/lisk/LiskDAOSet.php',
     'LiskDAOTestCase' => 'infrastructure/storage/lisk/__tests__/LiskDAOTestCase.php',
     'LiskEphemeralObjectException' => 'infrastructure/storage/lisk/LiskEphemeralObjectException.php',
     'LiskFixtureTestCase' => 'infrastructure/storage/lisk/__tests__/LiskFixtureTestCase.php',
     'LiskIsolationTestCase' => 'infrastructure/storage/lisk/__tests__/LiskIsolationTestCase.php',
     'LiskIsolationTestDAO' => 'infrastructure/storage/lisk/__tests__/LiskIsolationTestDAO.php',
     'LiskIsolationTestDAOException' => 'infrastructure/storage/lisk/__tests__/LiskIsolationTestDAOException.php',
     'LiskMigrationIterator' => 'infrastructure/storage/lisk/LiskMigrationIterator.php',
     'LiskRawMigrationIterator' => 'infrastructure/storage/lisk/LiskRawMigrationIterator.php',
     'MacroConduitAPIMethod' => 'applications/macro/conduit/MacroConduitAPIMethod.php',
     'MacroCreateMemeConduitAPIMethod' => 'applications/macro/conduit/MacroCreateMemeConduitAPIMethod.php',
     'MacroQueryConduitAPIMethod' => 'applications/macro/conduit/MacroQueryConduitAPIMethod.php',
     'ManiphestAssignEmailCommand' => 'applications/maniphest/command/ManiphestAssignEmailCommand.php',
     'ManiphestAssigneeDatasource' => 'applications/maniphest/typeahead/ManiphestAssigneeDatasource.php',
     'ManiphestBatchEditController' => 'applications/maniphest/controller/ManiphestBatchEditController.php',
     'ManiphestBulkEditCapability' => 'applications/maniphest/capability/ManiphestBulkEditCapability.php',
     'ManiphestClaimEmailCommand' => 'applications/maniphest/command/ManiphestClaimEmailCommand.php',
     'ManiphestCloseEmailCommand' => 'applications/maniphest/command/ManiphestCloseEmailCommand.php',
     'ManiphestConduitAPIMethod' => 'applications/maniphest/conduit/ManiphestConduitAPIMethod.php',
     'ManiphestConfiguredCustomField' => 'applications/maniphest/field/ManiphestConfiguredCustomField.php',
     'ManiphestConstants' => 'applications/maniphest/constants/ManiphestConstants.php',
     'ManiphestController' => 'applications/maniphest/controller/ManiphestController.php',
     'ManiphestCreateMailReceiver' => 'applications/maniphest/mail/ManiphestCreateMailReceiver.php',
     'ManiphestCreateTaskConduitAPIMethod' => 'applications/maniphest/conduit/ManiphestCreateTaskConduitAPIMethod.php',
     'ManiphestCustomField' => 'applications/maniphest/field/ManiphestCustomField.php',
     'ManiphestCustomFieldNumericIndex' => 'applications/maniphest/storage/ManiphestCustomFieldNumericIndex.php',
     'ManiphestCustomFieldStatusParser' => 'applications/maniphest/field/parser/ManiphestCustomFieldStatusParser.php',
     'ManiphestCustomFieldStatusParserTestCase' => 'applications/maniphest/field/parser/__tests__/ManiphestCustomFieldStatusParserTestCase.php',
     'ManiphestCustomFieldStorage' => 'applications/maniphest/storage/ManiphestCustomFieldStorage.php',
     'ManiphestCustomFieldStringIndex' => 'applications/maniphest/storage/ManiphestCustomFieldStringIndex.php',
     'ManiphestDAO' => 'applications/maniphest/storage/ManiphestDAO.php',
     'ManiphestDefaultEditCapability' => 'applications/maniphest/capability/ManiphestDefaultEditCapability.php',
     'ManiphestDefaultViewCapability' => 'applications/maniphest/capability/ManiphestDefaultViewCapability.php',
     'ManiphestEditAssignCapability' => 'applications/maniphest/capability/ManiphestEditAssignCapability.php',
     'ManiphestEditConduitAPIMethod' => 'applications/maniphest/conduit/ManiphestEditConduitAPIMethod.php',
     'ManiphestEditEngine' => 'applications/maniphest/editor/ManiphestEditEngine.php',
     'ManiphestEditPoliciesCapability' => 'applications/maniphest/capability/ManiphestEditPoliciesCapability.php',
     'ManiphestEditPriorityCapability' => 'applications/maniphest/capability/ManiphestEditPriorityCapability.php',
     'ManiphestEditProjectsCapability' => 'applications/maniphest/capability/ManiphestEditProjectsCapability.php',
     'ManiphestEditStatusCapability' => 'applications/maniphest/capability/ManiphestEditStatusCapability.php',
     'ManiphestEmailCommand' => 'applications/maniphest/command/ManiphestEmailCommand.php',
     'ManiphestExcelDefaultFormat' => 'applications/maniphest/export/ManiphestExcelDefaultFormat.php',
     'ManiphestExcelFormat' => 'applications/maniphest/export/ManiphestExcelFormat.php',
     'ManiphestExcelFormatTestCase' => 'applications/maniphest/export/__tests__/ManiphestExcelFormatTestCase.php',
     'ManiphestExportController' => 'applications/maniphest/controller/ManiphestExportController.php',
     'ManiphestGetTaskTransactionsConduitAPIMethod' => 'applications/maniphest/conduit/ManiphestGetTaskTransactionsConduitAPIMethod.php',
     'ManiphestHovercardEngineExtension' => 'applications/maniphest/engineextension/ManiphestHovercardEngineExtension.php',
     'ManiphestInfoConduitAPIMethod' => 'applications/maniphest/conduit/ManiphestInfoConduitAPIMethod.php',
     'ManiphestNameIndex' => 'applications/maniphest/storage/ManiphestNameIndex.php',
     'ManiphestPointsConfigOptionType' => 'applications/maniphest/config/ManiphestPointsConfigOptionType.php',
     'ManiphestPriorityConfigOptionType' => 'applications/maniphest/config/ManiphestPriorityConfigOptionType.php',
     'ManiphestPriorityEmailCommand' => 'applications/maniphest/command/ManiphestPriorityEmailCommand.php',
     'ManiphestProjectNameFulltextEngineExtension' => 'applications/maniphest/engineextension/ManiphestProjectNameFulltextEngineExtension.php',
     'ManiphestQueryConduitAPIMethod' => 'applications/maniphest/conduit/ManiphestQueryConduitAPIMethod.php',
     'ManiphestQueryStatusesConduitAPIMethod' => 'applications/maniphest/conduit/ManiphestQueryStatusesConduitAPIMethod.php',
     'ManiphestRemarkupRule' => 'applications/maniphest/remarkup/ManiphestRemarkupRule.php',
     'ManiphestReplyHandler' => 'applications/maniphest/mail/ManiphestReplyHandler.php',
     'ManiphestReportController' => 'applications/maniphest/controller/ManiphestReportController.php',
     'ManiphestSchemaSpec' => 'applications/maniphest/storage/ManiphestSchemaSpec.php',
     'ManiphestSearchConduitAPIMethod' => 'applications/maniphest/conduit/ManiphestSearchConduitAPIMethod.php',
     'ManiphestStatusConfigOptionType' => 'applications/maniphest/config/ManiphestStatusConfigOptionType.php',
     'ManiphestStatusEmailCommand' => 'applications/maniphest/command/ManiphestStatusEmailCommand.php',
     'ManiphestSubpriorityController' => 'applications/maniphest/controller/ManiphestSubpriorityController.php',
     'ManiphestTask' => 'applications/maniphest/storage/ManiphestTask.php',
     'ManiphestTaskAssignHeraldAction' => 'applications/maniphest/herald/ManiphestTaskAssignHeraldAction.php',
     'ManiphestTaskAssignOtherHeraldAction' => 'applications/maniphest/herald/ManiphestTaskAssignOtherHeraldAction.php',
     'ManiphestTaskAssignSelfHeraldAction' => 'applications/maniphest/herald/ManiphestTaskAssignSelfHeraldAction.php',
     'ManiphestTaskAssigneeHeraldField' => 'applications/maniphest/herald/ManiphestTaskAssigneeHeraldField.php',
     'ManiphestTaskAuthorHeraldField' => 'applications/maniphest/herald/ManiphestTaskAuthorHeraldField.php',
     'ManiphestTaskAuthorPolicyRule' => 'applications/maniphest/policyrule/ManiphestTaskAuthorPolicyRule.php',
     'ManiphestTaskClosedStatusDatasource' => 'applications/maniphest/typeahead/ManiphestTaskClosedStatusDatasource.php',
     'ManiphestTaskDependedOnByTaskEdgeType' => 'applications/maniphest/edge/ManiphestTaskDependedOnByTaskEdgeType.php',
     'ManiphestTaskDependsOnTaskEdgeType' => 'applications/maniphest/edge/ManiphestTaskDependsOnTaskEdgeType.php',
     'ManiphestTaskDescriptionHeraldField' => 'applications/maniphest/herald/ManiphestTaskDescriptionHeraldField.php',
     'ManiphestTaskDetailController' => 'applications/maniphest/controller/ManiphestTaskDetailController.php',
     'ManiphestTaskEditBulkJobType' => 'applications/maniphest/bulk/ManiphestTaskEditBulkJobType.php',
     'ManiphestTaskEditController' => 'applications/maniphest/controller/ManiphestTaskEditController.php',
     'ManiphestTaskFulltextEngine' => 'applications/maniphest/search/ManiphestTaskFulltextEngine.php',
     'ManiphestTaskHasCommitEdgeType' => 'applications/maniphest/edge/ManiphestTaskHasCommitEdgeType.php',
     'ManiphestTaskHasMockEdgeType' => 'applications/maniphest/edge/ManiphestTaskHasMockEdgeType.php',
     'ManiphestTaskHasRevisionEdgeType' => 'applications/maniphest/edge/ManiphestTaskHasRevisionEdgeType.php',
     'ManiphestTaskHeraldField' => 'applications/maniphest/herald/ManiphestTaskHeraldField.php',
     'ManiphestTaskHeraldFieldGroup' => 'applications/maniphest/herald/ManiphestTaskHeraldFieldGroup.php',
     'ManiphestTaskListController' => 'applications/maniphest/controller/ManiphestTaskListController.php',
     'ManiphestTaskListHTTPParameterType' => 'applications/maniphest/httpparametertype/ManiphestTaskListHTTPParameterType.php',
     'ManiphestTaskListView' => 'applications/maniphest/view/ManiphestTaskListView.php',
     'ManiphestTaskMailReceiver' => 'applications/maniphest/mail/ManiphestTaskMailReceiver.php',
     'ManiphestTaskOpenStatusDatasource' => 'applications/maniphest/typeahead/ManiphestTaskOpenStatusDatasource.php',
     'ManiphestTaskPHIDResolver' => 'applications/maniphest/httpparametertype/ManiphestTaskPHIDResolver.php',
     'ManiphestTaskPHIDType' => 'applications/maniphest/phid/ManiphestTaskPHIDType.php',
     'ManiphestTaskPoints' => 'applications/maniphest/constants/ManiphestTaskPoints.php',
     'ManiphestTaskPriority' => 'applications/maniphest/constants/ManiphestTaskPriority.php',
     'ManiphestTaskPriorityDatasource' => 'applications/maniphest/typeahead/ManiphestTaskPriorityDatasource.php',
     'ManiphestTaskPriorityHeraldAction' => 'applications/maniphest/herald/ManiphestTaskPriorityHeraldAction.php',
     'ManiphestTaskPriorityHeraldField' => 'applications/maniphest/herald/ManiphestTaskPriorityHeraldField.php',
     'ManiphestTaskQuery' => 'applications/maniphest/query/ManiphestTaskQuery.php',
     'ManiphestTaskResultListView' => 'applications/maniphest/view/ManiphestTaskResultListView.php',
     'ManiphestTaskSearchEngine' => 'applications/maniphest/query/ManiphestTaskSearchEngine.php',
     'ManiphestTaskStatus' => 'applications/maniphest/constants/ManiphestTaskStatus.php',
     'ManiphestTaskStatusDatasource' => 'applications/maniphest/typeahead/ManiphestTaskStatusDatasource.php',
     'ManiphestTaskStatusFunctionDatasource' => 'applications/maniphest/typeahead/ManiphestTaskStatusFunctionDatasource.php',
     'ManiphestTaskStatusHeraldAction' => 'applications/maniphest/herald/ManiphestTaskStatusHeraldAction.php',
     'ManiphestTaskStatusHeraldField' => 'applications/maniphest/herald/ManiphestTaskStatusHeraldField.php',
     'ManiphestTaskStatusTestCase' => 'applications/maniphest/constants/__tests__/ManiphestTaskStatusTestCase.php',
     'ManiphestTaskTestCase' => 'applications/maniphest/__tests__/ManiphestTaskTestCase.php',
     'ManiphestTaskTitleHeraldField' => 'applications/maniphest/herald/ManiphestTaskTitleHeraldField.php',
     'ManiphestTransaction' => 'applications/maniphest/storage/ManiphestTransaction.php',
     'ManiphestTransactionComment' => 'applications/maniphest/storage/ManiphestTransactionComment.php',
     'ManiphestTransactionEditor' => 'applications/maniphest/editor/ManiphestTransactionEditor.php',
     'ManiphestTransactionQuery' => 'applications/maniphest/query/ManiphestTransactionQuery.php',
     'ManiphestUpdateConduitAPIMethod' => 'applications/maniphest/conduit/ManiphestUpdateConduitAPIMethod.php',
     'ManiphestView' => 'applications/maniphest/view/ManiphestView.php',
     'MetaMTAEmailTransactionCommand' => 'applications/metamta/command/MetaMTAEmailTransactionCommand.php',
     'MetaMTAEmailTransactionCommandTestCase' => 'applications/metamta/command/__tests__/MetaMTAEmailTransactionCommandTestCase.php',
     'MetaMTAMailReceivedGarbageCollector' => 'applications/metamta/garbagecollector/MetaMTAMailReceivedGarbageCollector.php',
     'MetaMTAMailSentGarbageCollector' => 'applications/metamta/garbagecollector/MetaMTAMailSentGarbageCollector.php',
     'MetaMTAReceivedMailStatus' => 'applications/metamta/constants/MetaMTAReceivedMailStatus.php',
     'MultimeterContext' => 'applications/multimeter/storage/MultimeterContext.php',
     'MultimeterControl' => 'applications/multimeter/data/MultimeterControl.php',
     'MultimeterController' => 'applications/multimeter/controller/MultimeterController.php',
     'MultimeterDAO' => 'applications/multimeter/storage/MultimeterDAO.php',
     'MultimeterDimension' => 'applications/multimeter/storage/MultimeterDimension.php',
     'MultimeterEvent' => 'applications/multimeter/storage/MultimeterEvent.php',
     'MultimeterEventGarbageCollector' => 'applications/multimeter/garbagecollector/MultimeterEventGarbageCollector.php',
     'MultimeterHost' => 'applications/multimeter/storage/MultimeterHost.php',
     'MultimeterLabel' => 'applications/multimeter/storage/MultimeterLabel.php',
     'MultimeterSampleController' => 'applications/multimeter/controller/MultimeterSampleController.php',
     'MultimeterViewer' => 'applications/multimeter/storage/MultimeterViewer.php',
     'NuanceConduitAPIMethod' => 'applications/nuance/conduit/NuanceConduitAPIMethod.php',
     'NuanceConsoleController' => 'applications/nuance/controller/NuanceConsoleController.php',
     'NuanceContentSource' => 'applications/nuance/contentsource/NuanceContentSource.php',
     'NuanceController' => 'applications/nuance/controller/NuanceController.php',
     'NuanceDAO' => 'applications/nuance/storage/NuanceDAO.php',
     'NuanceGitHubEventItemType' => 'applications/nuance/item/NuanceGitHubEventItemType.php',
     'NuanceGitHubImportCursor' => 'applications/nuance/cursor/NuanceGitHubImportCursor.php',
     'NuanceGitHubIssuesImportCursor' => 'applications/nuance/cursor/NuanceGitHubIssuesImportCursor.php',
     'NuanceGitHubRawEvent' => 'applications/nuance/github/NuanceGitHubRawEvent.php',
     'NuanceGitHubRawEventTestCase' => 'applications/nuance/github/__tests__/NuanceGitHubRawEventTestCase.php',
     'NuanceGitHubRepositoryImportCursor' => 'applications/nuance/cursor/NuanceGitHubRepositoryImportCursor.php',
     'NuanceGitHubRepositorySourceDefinition' => 'applications/nuance/source/NuanceGitHubRepositorySourceDefinition.php',
     'NuanceImportCursor' => 'applications/nuance/cursor/NuanceImportCursor.php',
     'NuanceImportCursorData' => 'applications/nuance/storage/NuanceImportCursorData.php',
     'NuanceImportCursorDataQuery' => 'applications/nuance/query/NuanceImportCursorDataQuery.php',
     'NuanceImportCursorPHIDType' => 'applications/nuance/phid/NuanceImportCursorPHIDType.php',
     'NuanceItem' => 'applications/nuance/storage/NuanceItem.php',
     'NuanceItemActionController' => 'applications/nuance/controller/NuanceItemActionController.php',
     'NuanceItemCommand' => 'applications/nuance/storage/NuanceItemCommand.php',
     'NuanceItemCommandQuery' => 'applications/nuance/query/NuanceItemCommandQuery.php',
     'NuanceItemController' => 'applications/nuance/controller/NuanceItemController.php',
     'NuanceItemEditor' => 'applications/nuance/editor/NuanceItemEditor.php',
     'NuanceItemListController' => 'applications/nuance/controller/NuanceItemListController.php',
     'NuanceItemManageController' => 'applications/nuance/controller/NuanceItemManageController.php',
     'NuanceItemPHIDType' => 'applications/nuance/phid/NuanceItemPHIDType.php',
     'NuanceItemQuery' => 'applications/nuance/query/NuanceItemQuery.php',
     'NuanceItemSearchEngine' => 'applications/nuance/query/NuanceItemSearchEngine.php',
     'NuanceItemTransaction' => 'applications/nuance/storage/NuanceItemTransaction.php',
     'NuanceItemTransactionComment' => 'applications/nuance/storage/NuanceItemTransactionComment.php',
     'NuanceItemTransactionQuery' => 'applications/nuance/query/NuanceItemTransactionQuery.php',
     'NuanceItemType' => 'applications/nuance/item/NuanceItemType.php',
     'NuanceItemUpdateWorker' => 'applications/nuance/worker/NuanceItemUpdateWorker.php',
     'NuanceItemViewController' => 'applications/nuance/controller/NuanceItemViewController.php',
     'NuanceManagementImportWorkflow' => 'applications/nuance/management/NuanceManagementImportWorkflow.php',
     'NuanceManagementUpdateWorkflow' => 'applications/nuance/management/NuanceManagementUpdateWorkflow.php',
     'NuanceManagementWorkflow' => 'applications/nuance/management/NuanceManagementWorkflow.php',
     'NuancePhabricatorFormSourceDefinition' => 'applications/nuance/source/NuancePhabricatorFormSourceDefinition.php',
     'NuanceQuery' => 'applications/nuance/query/NuanceQuery.php',
     'NuanceQueue' => 'applications/nuance/storage/NuanceQueue.php',
     'NuanceQueueController' => 'applications/nuance/controller/NuanceQueueController.php',
     'NuanceQueueDatasource' => 'applications/nuance/typeahead/NuanceQueueDatasource.php',
     'NuanceQueueEditController' => 'applications/nuance/controller/NuanceQueueEditController.php',
     'NuanceQueueEditEngine' => 'applications/nuance/editor/NuanceQueueEditEngine.php',
     'NuanceQueueEditor' => 'applications/nuance/editor/NuanceQueueEditor.php',
     'NuanceQueueListController' => 'applications/nuance/controller/NuanceQueueListController.php',
     'NuanceQueuePHIDType' => 'applications/nuance/phid/NuanceQueuePHIDType.php',
     'NuanceQueueQuery' => 'applications/nuance/query/NuanceQueueQuery.php',
     'NuanceQueueSearchEngine' => 'applications/nuance/query/NuanceQueueSearchEngine.php',
     'NuanceQueueTransaction' => 'applications/nuance/storage/NuanceQueueTransaction.php',
     'NuanceQueueTransactionComment' => 'applications/nuance/storage/NuanceQueueTransactionComment.php',
     'NuanceQueueTransactionQuery' => 'applications/nuance/query/NuanceQueueTransactionQuery.php',
     'NuanceQueueViewController' => 'applications/nuance/controller/NuanceQueueViewController.php',
     'NuanceSchemaSpec' => 'applications/nuance/storage/NuanceSchemaSpec.php',
     'NuanceSource' => 'applications/nuance/storage/NuanceSource.php',
     'NuanceSourceActionController' => 'applications/nuance/controller/NuanceSourceActionController.php',
     'NuanceSourceController' => 'applications/nuance/controller/NuanceSourceController.php',
     'NuanceSourceDefaultEditCapability' => 'applications/nuance/capability/NuanceSourceDefaultEditCapability.php',
     'NuanceSourceDefaultViewCapability' => 'applications/nuance/capability/NuanceSourceDefaultViewCapability.php',
     'NuanceSourceDefinition' => 'applications/nuance/source/NuanceSourceDefinition.php',
     'NuanceSourceDefinitionTestCase' => 'applications/nuance/source/__tests__/NuanceSourceDefinitionTestCase.php',
     'NuanceSourceEditController' => 'applications/nuance/controller/NuanceSourceEditController.php',
     'NuanceSourceEditEngine' => 'applications/nuance/editor/NuanceSourceEditEngine.php',
     'NuanceSourceEditor' => 'applications/nuance/editor/NuanceSourceEditor.php',
     'NuanceSourceListController' => 'applications/nuance/controller/NuanceSourceListController.php',
     'NuanceSourceManageCapability' => 'applications/nuance/capability/NuanceSourceManageCapability.php',
     'NuanceSourceNameNgrams' => 'applications/nuance/storage/NuanceSourceNameNgrams.php',
     'NuanceSourcePHIDType' => 'applications/nuance/phid/NuanceSourcePHIDType.php',
     'NuanceSourceQuery' => 'applications/nuance/query/NuanceSourceQuery.php',
     'NuanceSourceSearchEngine' => 'applications/nuance/query/NuanceSourceSearchEngine.php',
     'NuanceSourceTransaction' => 'applications/nuance/storage/NuanceSourceTransaction.php',
     'NuanceSourceTransactionComment' => 'applications/nuance/storage/NuanceSourceTransactionComment.php',
     'NuanceSourceTransactionQuery' => 'applications/nuance/query/NuanceSourceTransactionQuery.php',
     'NuanceSourceViewController' => 'applications/nuance/controller/NuanceSourceViewController.php',
     'NuanceTransaction' => 'applications/nuance/storage/NuanceTransaction.php',
     'NuanceWorker' => 'applications/nuance/worker/NuanceWorker.php',
     'OwnersConduitAPIMethod' => 'applications/owners/conduit/OwnersConduitAPIMethod.php',
     'OwnersEditConduitAPIMethod' => 'applications/owners/conduit/OwnersEditConduitAPIMethod.php',
     'OwnersPackageReplyHandler' => 'applications/owners/mail/OwnersPackageReplyHandler.php',
     'OwnersQueryConduitAPIMethod' => 'applications/owners/conduit/OwnersQueryConduitAPIMethod.php',
     'OwnersSearchConduitAPIMethod' => 'applications/owners/conduit/OwnersSearchConduitAPIMethod.php',
     'PHIDConduitAPIMethod' => 'applications/phid/conduit/PHIDConduitAPIMethod.php',
     'PHIDInfoConduitAPIMethod' => 'applications/phid/conduit/PHIDInfoConduitAPIMethod.php',
     'PHIDLookupConduitAPIMethod' => 'applications/phid/conduit/PHIDLookupConduitAPIMethod.php',
     'PHIDQueryConduitAPIMethod' => 'applications/phid/conduit/PHIDQueryConduitAPIMethod.php',
     'PHUI' => 'view/phui/PHUI.php',
     'PHUIActionPanelExample' => 'applications/uiexample/examples/PHUIActionPanelExample.php',
     'PHUIActionPanelView' => 'view/phui/PHUIActionPanelView.php',
     'PHUIApplicationMenuView' => 'view/layout/PHUIApplicationMenuView.php',
     'PHUIBadgeBoxView' => 'view/phui/PHUIBadgeBoxView.php',
     'PHUIBadgeExample' => 'applications/uiexample/examples/PHUIBadgeExample.php',
     'PHUIBadgeMiniView' => 'view/phui/PHUIBadgeMiniView.php',
     'PHUIBadgeView' => 'view/phui/PHUIBadgeView.php',
     'PHUIBigInfoView' => 'view/phui/PHUIBigInfoView.php',
     'PHUIBoxExample' => 'applications/uiexample/examples/PHUIBoxExample.php',
     'PHUIBoxView' => 'view/phui/PHUIBoxView.php',
     'PHUIButtonBarExample' => 'applications/uiexample/examples/PHUIButtonBarExample.php',
     'PHUIButtonBarView' => 'view/phui/PHUIButtonBarView.php',
     'PHUIButtonExample' => 'applications/uiexample/examples/PHUIButtonExample.php',
     'PHUIButtonView' => 'view/phui/PHUIButtonView.php',
     'PHUICalendarDayView' => 'view/phui/calendar/PHUICalendarDayView.php',
     'PHUICalendarListView' => 'view/phui/calendar/PHUICalendarListView.php',
     'PHUICalendarMonthView' => 'view/phui/calendar/PHUICalendarMonthView.php',
     'PHUICalendarWidgetView' => 'view/phui/calendar/PHUICalendarWidgetView.php',
     'PHUIColorPalletteExample' => 'applications/uiexample/examples/PHUIColorPalletteExample.php',
     'PHUICrumbView' => 'view/phui/PHUICrumbView.php',
     'PHUICrumbsView' => 'view/phui/PHUICrumbsView.php',
     'PHUICurtainExtension' => 'view/extension/PHUICurtainExtension.php',
     'PHUICurtainPanelView' => 'view/layout/PHUICurtainPanelView.php',
     'PHUICurtainView' => 'view/layout/PHUICurtainView.php',
     'PHUIDiffInlineCommentDetailView' => 'infrastructure/diff/view/PHUIDiffInlineCommentDetailView.php',
     'PHUIDiffInlineCommentEditView' => 'infrastructure/diff/view/PHUIDiffInlineCommentEditView.php',
     'PHUIDiffInlineCommentRowScaffold' => 'infrastructure/diff/view/PHUIDiffInlineCommentRowScaffold.php',
     'PHUIDiffInlineCommentTableScaffold' => 'infrastructure/diff/view/PHUIDiffInlineCommentTableScaffold.php',
     'PHUIDiffInlineCommentUndoView' => 'infrastructure/diff/view/PHUIDiffInlineCommentUndoView.php',
     'PHUIDiffInlineCommentView' => 'infrastructure/diff/view/PHUIDiffInlineCommentView.php',
     'PHUIDiffOneUpInlineCommentRowScaffold' => 'infrastructure/diff/view/PHUIDiffOneUpInlineCommentRowScaffold.php',
     'PHUIDiffRevealIconView' => 'infrastructure/diff/view/PHUIDiffRevealIconView.php',
     'PHUIDiffTableOfContentsItemView' => 'infrastructure/diff/view/PHUIDiffTableOfContentsItemView.php',
     'PHUIDiffTableOfContentsListView' => 'infrastructure/diff/view/PHUIDiffTableOfContentsListView.php',
     'PHUIDiffTwoUpInlineCommentRowScaffold' => 'infrastructure/diff/view/PHUIDiffTwoUpInlineCommentRowScaffold.php',
     'PHUIDocumentExample' => 'applications/uiexample/examples/PHUIDocumentExample.php',
     'PHUIDocumentSummaryView' => 'view/phui/PHUIDocumentSummaryView.php',
     'PHUIDocumentView' => 'view/phui/PHUIDocumentView.php',
     'PHUIDocumentViewPro' => 'view/phui/PHUIDocumentViewPro.php',
     'PHUIFeedStoryExample' => 'applications/uiexample/examples/PHUIFeedStoryExample.php',
     'PHUIFeedStoryView' => 'view/phui/PHUIFeedStoryView.php',
     'PHUIFormDividerControl' => 'view/form/control/PHUIFormDividerControl.php',
     'PHUIFormFreeformDateControl' => 'view/form/control/PHUIFormFreeformDateControl.php',
     'PHUIFormIconSetControl' => 'view/form/control/PHUIFormIconSetControl.php',
     'PHUIFormInsetView' => 'view/form/PHUIFormInsetView.php',
     'PHUIFormLayoutView' => 'view/form/PHUIFormLayoutView.php',
     'PHUIFormMultiSubmitControl' => 'view/form/control/PHUIFormMultiSubmitControl.php',
     'PHUIFormPageView' => 'view/form/PHUIFormPageView.php',
     'PHUIHandleListView' => 'applications/phid/view/PHUIHandleListView.php',
     'PHUIHandleTagListView' => 'applications/phid/view/PHUIHandleTagListView.php',
     'PHUIHandleView' => 'applications/phid/view/PHUIHandleView.php',
     'PHUIHeadThingView' => 'view/phui/PHUIHeadThingView.php',
     'PHUIHeaderView' => 'view/phui/PHUIHeaderView.php',
     'PHUIHovercardUIExample' => 'applications/uiexample/examples/PHUIHovercardUIExample.php',
     'PHUIHovercardView' => 'view/phui/PHUIHovercardView.php',
     'PHUIIconCircleView' => 'view/phui/PHUIIconCircleView.php',
     'PHUIIconExample' => 'applications/uiexample/examples/PHUIIconExample.php',
     'PHUIIconView' => 'view/phui/PHUIIconView.php',
     'PHUIImageMaskExample' => 'applications/uiexample/examples/PHUIImageMaskExample.php',
     'PHUIImageMaskView' => 'view/phui/PHUIImageMaskView.php',
     'PHUIInfoExample' => 'applications/uiexample/examples/PHUIInfoExample.php',
     'PHUIInfoPanelExample' => 'applications/uiexample/examples/PHUIInfoPanelExample.php',
     'PHUIInfoPanelView' => 'view/phui/PHUIInfoPanelView.php',
     'PHUIInfoView' => 'view/form/PHUIInfoView.php',
     'PHUIListExample' => 'applications/uiexample/examples/PHUIListExample.php',
     'PHUIListItemView' => 'view/phui/PHUIListItemView.php',
     'PHUIListView' => 'view/phui/PHUIListView.php',
     'PHUIListViewTestCase' => 'view/layout/__tests__/PHUIListViewTestCase.php',
     'PHUIMainMenuView' => 'view/phui/PHUIMainMenuView.php',
     'PHUIObjectBoxView' => 'view/phui/PHUIObjectBoxView.php',
     'PHUIObjectItemListExample' => 'applications/uiexample/examples/PHUIObjectItemListExample.php',
     'PHUIObjectItemListView' => 'view/phui/PHUIObjectItemListView.php',
     'PHUIObjectItemView' => 'view/phui/PHUIObjectItemView.php',
     'PHUIPagedFormView' => 'view/form/PHUIPagedFormView.php',
     'PHUIPagerView' => 'view/phui/PHUIPagerView.php',
     'PHUIPinboardItemView' => 'view/phui/PHUIPinboardItemView.php',
     'PHUIPinboardView' => 'view/phui/PHUIPinboardView.php',
     'PHUIPropertyGroupView' => 'view/phui/PHUIPropertyGroupView.php',
     'PHUIPropertyListExample' => 'applications/uiexample/examples/PHUIPropertyListExample.php',
     'PHUIPropertyListView' => 'view/phui/PHUIPropertyListView.php',
     'PHUIRemarkupPreviewPanel' => 'view/phui/PHUIRemarkupPreviewPanel.php',
     'PHUIRemarkupView' => 'infrastructure/markup/view/PHUIRemarkupView.php',
     'PHUISegmentBarSegmentView' => 'view/phui/PHUISegmentBarSegmentView.php',
     'PHUISegmentBarView' => 'view/phui/PHUISegmentBarView.php',
     'PHUISpacesNamespaceContextView' => 'applications/spaces/view/PHUISpacesNamespaceContextView.php',
     'PHUIStatusItemView' => 'view/phui/PHUIStatusItemView.php',
     'PHUIStatusListView' => 'view/phui/PHUIStatusListView.php',
     'PHUITagExample' => 'applications/uiexample/examples/PHUITagExample.php',
     'PHUITagView' => 'view/phui/PHUITagView.php',
     'PHUITimelineEventView' => 'view/phui/PHUITimelineEventView.php',
     'PHUITimelineExample' => 'applications/uiexample/examples/PHUITimelineExample.php',
     'PHUITimelineView' => 'view/phui/PHUITimelineView.php',
     'PHUITwoColumnView' => 'view/phui/PHUITwoColumnView.php',
     'PHUITypeaheadExample' => 'applications/uiexample/examples/PHUITypeaheadExample.php',
     'PHUIWorkboardView' => 'view/phui/PHUIWorkboardView.php',
     'PHUIWorkpanelView' => 'view/phui/PHUIWorkpanelView.php',
     'PassphraseAbstractKey' => 'applications/passphrase/keys/PassphraseAbstractKey.php',
     'PassphraseConduitAPIMethod' => 'applications/passphrase/conduit/PassphraseConduitAPIMethod.php',
     'PassphraseController' => 'applications/passphrase/controller/PassphraseController.php',
     'PassphraseCredential' => 'applications/passphrase/storage/PassphraseCredential.php',
     'PassphraseCredentialAuthorPolicyRule' => 'applications/passphrase/policyrule/PassphraseCredentialAuthorPolicyRule.php',
     'PassphraseCredentialConduitController' => 'applications/passphrase/controller/PassphraseCredentialConduitController.php',
     'PassphraseCredentialControl' => 'applications/passphrase/view/PassphraseCredentialControl.php',
     'PassphraseCredentialCreateController' => 'applications/passphrase/controller/PassphraseCredentialCreateController.php',
     'PassphraseCredentialDestroyController' => 'applications/passphrase/controller/PassphraseCredentialDestroyController.php',
     'PassphraseCredentialEditController' => 'applications/passphrase/controller/PassphraseCredentialEditController.php',
     'PassphraseCredentialFulltextEngine' => 'applications/passphrase/search/PassphraseCredentialFulltextEngine.php',
     'PassphraseCredentialListController' => 'applications/passphrase/controller/PassphraseCredentialListController.php',
     'PassphraseCredentialLockController' => 'applications/passphrase/controller/PassphraseCredentialLockController.php',
     'PassphraseCredentialPHIDType' => 'applications/passphrase/phid/PassphraseCredentialPHIDType.php',
     'PassphraseCredentialPublicController' => 'applications/passphrase/controller/PassphraseCredentialPublicController.php',
     'PassphraseCredentialQuery' => 'applications/passphrase/query/PassphraseCredentialQuery.php',
     'PassphraseCredentialRevealController' => 'applications/passphrase/controller/PassphraseCredentialRevealController.php',
     'PassphraseCredentialSearchEngine' => 'applications/passphrase/query/PassphraseCredentialSearchEngine.php',
     'PassphraseCredentialTransaction' => 'applications/passphrase/storage/PassphraseCredentialTransaction.php',
     'PassphraseCredentialTransactionEditor' => 'applications/passphrase/editor/PassphraseCredentialTransactionEditor.php',
     'PassphraseCredentialTransactionQuery' => 'applications/passphrase/query/PassphraseCredentialTransactionQuery.php',
     'PassphraseCredentialType' => 'applications/passphrase/credentialtype/PassphraseCredentialType.php',
     'PassphraseCredentialTypeTestCase' => 'applications/passphrase/credentialtype/__tests__/PassphraseCredentialTypeTestCase.php',
     'PassphraseCredentialViewController' => 'applications/passphrase/controller/PassphraseCredentialViewController.php',
     'PassphraseDAO' => 'applications/passphrase/storage/PassphraseDAO.php',
     'PassphraseDefaultEditCapability' => 'applications/passphrase/capability/PassphraseDefaultEditCapability.php',
     'PassphraseDefaultViewCapability' => 'applications/passphrase/capability/PassphraseDefaultViewCapability.php',
     'PassphraseNoteCredentialType' => 'applications/passphrase/credentialtype/PassphraseNoteCredentialType.php',
     'PassphrasePasswordCredentialType' => 'applications/passphrase/credentialtype/PassphrasePasswordCredentialType.php',
     'PassphrasePasswordKey' => 'applications/passphrase/keys/PassphrasePasswordKey.php',
     'PassphraseQueryConduitAPIMethod' => 'applications/passphrase/conduit/PassphraseQueryConduitAPIMethod.php',
     'PassphraseRemarkupRule' => 'applications/passphrase/remarkup/PassphraseRemarkupRule.php',
     'PassphraseSSHGeneratedKeyCredentialType' => 'applications/passphrase/credentialtype/PassphraseSSHGeneratedKeyCredentialType.php',
     'PassphraseSSHKey' => 'applications/passphrase/keys/PassphraseSSHKey.php',
     'PassphraseSSHPrivateKeyCredentialType' => 'applications/passphrase/credentialtype/PassphraseSSHPrivateKeyCredentialType.php',
     'PassphraseSSHPrivateKeyFileCredentialType' => 'applications/passphrase/credentialtype/PassphraseSSHPrivateKeyFileCredentialType.php',
     'PassphraseSSHPrivateKeyTextCredentialType' => 'applications/passphrase/credentialtype/PassphraseSSHPrivateKeyTextCredentialType.php',
     'PassphraseSchemaSpec' => 'applications/passphrase/storage/PassphraseSchemaSpec.php',
     'PassphraseSecret' => 'applications/passphrase/storage/PassphraseSecret.php',
     'PassphraseTokenCredentialType' => 'applications/passphrase/credentialtype/PassphraseTokenCredentialType.php',
     'PasteConduitAPIMethod' => 'applications/paste/conduit/PasteConduitAPIMethod.php',
     'PasteCreateConduitAPIMethod' => 'applications/paste/conduit/PasteCreateConduitAPIMethod.php',
     'PasteCreateMailReceiver' => 'applications/paste/mail/PasteCreateMailReceiver.php',
     'PasteDefaultEditCapability' => 'applications/paste/capability/PasteDefaultEditCapability.php',
     'PasteDefaultViewCapability' => 'applications/paste/capability/PasteDefaultViewCapability.php',
     'PasteEditConduitAPIMethod' => 'applications/paste/conduit/PasteEditConduitAPIMethod.php',
     'PasteEmbedView' => 'applications/paste/view/PasteEmbedView.php',
     'PasteInfoConduitAPIMethod' => 'applications/paste/conduit/PasteInfoConduitAPIMethod.php',
     'PasteMailReceiver' => 'applications/paste/mail/PasteMailReceiver.php',
     'PasteQueryConduitAPIMethod' => 'applications/paste/conduit/PasteQueryConduitAPIMethod.php',
     'PasteReplyHandler' => 'applications/paste/mail/PasteReplyHandler.php',
     'PasteSearchConduitAPIMethod' => 'applications/paste/conduit/PasteSearchConduitAPIMethod.php',
     'PeopleBrowseUserDirectoryCapability' => 'applications/people/capability/PeopleBrowseUserDirectoryCapability.php',
     'PeopleCreateUsersCapability' => 'applications/people/capability/PeopleCreateUsersCapability.php',
     'PeopleHovercardEngineExtension' => 'applications/people/engineextension/PeopleHovercardEngineExtension.php',
     'PeopleUserLogGarbageCollector' => 'applications/people/garbagecollector/PeopleUserLogGarbageCollector.php',
     'Phabricator404Controller' => 'applications/base/controller/Phabricator404Controller.php',
     'PhabricatorAWSConfigOptions' => 'applications/config/option/PhabricatorAWSConfigOptions.php',
     'PhabricatorAccessControlTestCase' => 'applications/base/controller/__tests__/PhabricatorAccessControlTestCase.php',
     'PhabricatorAccessLog' => 'infrastructure/log/PhabricatorAccessLog.php',
     'PhabricatorAccessLogConfigOptions' => 'applications/config/option/PhabricatorAccessLogConfigOptions.php',
     'PhabricatorAccountSettingsPanel' => 'applications/settings/panel/PhabricatorAccountSettingsPanel.php',
     'PhabricatorActionListView' => 'view/layout/PhabricatorActionListView.php',
     'PhabricatorActionView' => 'view/layout/PhabricatorActionView.php',
     'PhabricatorActivitySettingsPanel' => 'applications/settings/panel/PhabricatorActivitySettingsPanel.php',
     'PhabricatorAdministratorsPolicyRule' => 'applications/people/policyrule/PhabricatorAdministratorsPolicyRule.php',
     'PhabricatorAjaxRequestExceptionHandler' => 'aphront/handler/PhabricatorAjaxRequestExceptionHandler.php',
     'PhabricatorAlmanacApplication' => 'applications/almanac/application/PhabricatorAlmanacApplication.php',
     'PhabricatorAmazonAuthProvider' => 'applications/auth/provider/PhabricatorAmazonAuthProvider.php',
     'PhabricatorAnchorView' => 'view/layout/PhabricatorAnchorView.php',
     'PhabricatorAphlictManagementDebugWorkflow' => 'applications/aphlict/management/PhabricatorAphlictManagementDebugWorkflow.php',
     'PhabricatorAphlictManagementRestartWorkflow' => 'applications/aphlict/management/PhabricatorAphlictManagementRestartWorkflow.php',
     'PhabricatorAphlictManagementStartWorkflow' => 'applications/aphlict/management/PhabricatorAphlictManagementStartWorkflow.php',
     'PhabricatorAphlictManagementStatusWorkflow' => 'applications/aphlict/management/PhabricatorAphlictManagementStatusWorkflow.php',
     'PhabricatorAphlictManagementStopWorkflow' => 'applications/aphlict/management/PhabricatorAphlictManagementStopWorkflow.php',
     'PhabricatorAphlictManagementWorkflow' => 'applications/aphlict/management/PhabricatorAphlictManagementWorkflow.php',
     'PhabricatorAphlictSetupCheck' => 'applications/notification/setup/PhabricatorAphlictSetupCheck.php',
     'PhabricatorAphrontBarUIExample' => 'applications/uiexample/examples/PhabricatorAphrontBarUIExample.php',
     'PhabricatorAphrontViewTestCase' => 'view/__tests__/PhabricatorAphrontViewTestCase.php',
     'PhabricatorAppSearchEngine' => 'applications/meta/query/PhabricatorAppSearchEngine.php',
     'PhabricatorApplication' => 'applications/base/PhabricatorApplication.php',
     'PhabricatorApplicationApplicationPHIDType' => 'applications/meta/phid/PhabricatorApplicationApplicationPHIDType.php',
     'PhabricatorApplicationConfigOptions' => 'applications/config/option/PhabricatorApplicationConfigOptions.php',
     'PhabricatorApplicationConfigurationPanel' => 'applications/meta/panel/PhabricatorApplicationConfigurationPanel.php',
     'PhabricatorApplicationConfigurationPanelTestCase' => 'applications/meta/panel/__tests__/PhabricatorApplicationConfigurationPanelTestCase.php',
     'PhabricatorApplicationDatasource' => 'applications/meta/typeahead/PhabricatorApplicationDatasource.php',
     'PhabricatorApplicationDetailViewController' => 'applications/meta/controller/PhabricatorApplicationDetailViewController.php',
     'PhabricatorApplicationEditController' => 'applications/meta/controller/PhabricatorApplicationEditController.php',
     'PhabricatorApplicationEditHTTPParameterHelpView' => 'applications/transactions/view/PhabricatorApplicationEditHTTPParameterHelpView.php',
     'PhabricatorApplicationEmailCommandsController' => 'applications/meta/controller/PhabricatorApplicationEmailCommandsController.php',
     'PhabricatorApplicationLaunchView' => 'applications/meta/view/PhabricatorApplicationLaunchView.php',
     'PhabricatorApplicationPanelController' => 'applications/meta/controller/PhabricatorApplicationPanelController.php',
     'PhabricatorApplicationQuery' => 'applications/meta/query/PhabricatorApplicationQuery.php',
     'PhabricatorApplicationSearchController' => 'applications/search/controller/PhabricatorApplicationSearchController.php',
     'PhabricatorApplicationSearchEngine' => 'applications/search/engine/PhabricatorApplicationSearchEngine.php',
     'PhabricatorApplicationSearchEngineTestCase' => 'applications/search/engine/__tests__/PhabricatorApplicationSearchEngineTestCase.php',
     'PhabricatorApplicationSearchResultView' => 'applications/search/view/PhabricatorApplicationSearchResultView.php',
     'PhabricatorApplicationStatusView' => 'applications/meta/view/PhabricatorApplicationStatusView.php',
     'PhabricatorApplicationTestCase' => 'applications/base/__tests__/PhabricatorApplicationTestCase.php',
     'PhabricatorApplicationTransaction' => 'applications/transactions/storage/PhabricatorApplicationTransaction.php',
     'PhabricatorApplicationTransactionComment' => 'applications/transactions/storage/PhabricatorApplicationTransactionComment.php',
     'PhabricatorApplicationTransactionCommentEditController' => 'applications/transactions/controller/PhabricatorApplicationTransactionCommentEditController.php',
     'PhabricatorApplicationTransactionCommentEditor' => 'applications/transactions/editor/PhabricatorApplicationTransactionCommentEditor.php',
     'PhabricatorApplicationTransactionCommentHistoryController' => 'applications/transactions/controller/PhabricatorApplicationTransactionCommentHistoryController.php',
     'PhabricatorApplicationTransactionCommentQuery' => 'applications/transactions/query/PhabricatorApplicationTransactionCommentQuery.php',
     'PhabricatorApplicationTransactionCommentQuoteController' => 'applications/transactions/controller/PhabricatorApplicationTransactionCommentQuoteController.php',
     'PhabricatorApplicationTransactionCommentRawController' => 'applications/transactions/controller/PhabricatorApplicationTransactionCommentRawController.php',
     'PhabricatorApplicationTransactionCommentRemoveController' => 'applications/transactions/controller/PhabricatorApplicationTransactionCommentRemoveController.php',
     'PhabricatorApplicationTransactionCommentView' => 'applications/transactions/view/PhabricatorApplicationTransactionCommentView.php',
     'PhabricatorApplicationTransactionController' => 'applications/transactions/controller/PhabricatorApplicationTransactionController.php',
     'PhabricatorApplicationTransactionDetailController' => 'applications/transactions/controller/PhabricatorApplicationTransactionDetailController.php',
     'PhabricatorApplicationTransactionEditor' => 'applications/transactions/editor/PhabricatorApplicationTransactionEditor.php',
     'PhabricatorApplicationTransactionFeedStory' => 'applications/transactions/feed/PhabricatorApplicationTransactionFeedStory.php',
     'PhabricatorApplicationTransactionInterface' => 'applications/transactions/interface/PhabricatorApplicationTransactionInterface.php',
     'PhabricatorApplicationTransactionNoEffectException' => 'applications/transactions/exception/PhabricatorApplicationTransactionNoEffectException.php',
     'PhabricatorApplicationTransactionNoEffectResponse' => 'applications/transactions/response/PhabricatorApplicationTransactionNoEffectResponse.php',
     'PhabricatorApplicationTransactionPublishWorker' => 'applications/transactions/worker/PhabricatorApplicationTransactionPublishWorker.php',
     'PhabricatorApplicationTransactionQuery' => 'applications/transactions/query/PhabricatorApplicationTransactionQuery.php',
     'PhabricatorApplicationTransactionRemarkupPreviewController' => 'applications/transactions/controller/PhabricatorApplicationTransactionRemarkupPreviewController.php',
     'PhabricatorApplicationTransactionReplyHandler' => 'applications/transactions/replyhandler/PhabricatorApplicationTransactionReplyHandler.php',
     'PhabricatorApplicationTransactionResponse' => 'applications/transactions/response/PhabricatorApplicationTransactionResponse.php',
     'PhabricatorApplicationTransactionShowOlderController' => 'applications/transactions/controller/PhabricatorApplicationTransactionShowOlderController.php',
     'PhabricatorApplicationTransactionStructureException' => 'applications/transactions/exception/PhabricatorApplicationTransactionStructureException.php',
     'PhabricatorApplicationTransactionTemplatedCommentQuery' => 'applications/transactions/query/PhabricatorApplicationTransactionTemplatedCommentQuery.php',
     'PhabricatorApplicationTransactionTextDiffDetailView' => 'applications/transactions/view/PhabricatorApplicationTransactionTextDiffDetailView.php',
     'PhabricatorApplicationTransactionTransactionPHIDType' => 'applications/transactions/phid/PhabricatorApplicationTransactionTransactionPHIDType.php',
     'PhabricatorApplicationTransactionValidationError' => 'applications/transactions/error/PhabricatorApplicationTransactionValidationError.php',
     'PhabricatorApplicationTransactionValidationException' => 'applications/transactions/exception/PhabricatorApplicationTransactionValidationException.php',
     'PhabricatorApplicationTransactionValidationResponse' => 'applications/transactions/response/PhabricatorApplicationTransactionValidationResponse.php',
     'PhabricatorApplicationTransactionValueController' => 'applications/transactions/controller/PhabricatorApplicationTransactionValueController.php',
     'PhabricatorApplicationTransactionView' => 'applications/transactions/view/PhabricatorApplicationTransactionView.php',
     'PhabricatorApplicationUninstallController' => 'applications/meta/controller/PhabricatorApplicationUninstallController.php',
     'PhabricatorApplicationsApplication' => 'applications/meta/application/PhabricatorApplicationsApplication.php',
     'PhabricatorApplicationsController' => 'applications/meta/controller/PhabricatorApplicationsController.php',
     'PhabricatorApplicationsListController' => 'applications/meta/controller/PhabricatorApplicationsListController.php',
     'PhabricatorAsanaAuthProvider' => 'applications/auth/provider/PhabricatorAsanaAuthProvider.php',
     'PhabricatorAsanaConfigOptions' => 'applications/doorkeeper/option/PhabricatorAsanaConfigOptions.php',
     'PhabricatorAsanaSubtaskHasObjectEdgeType' => 'applications/doorkeeper/edge/PhabricatorAsanaSubtaskHasObjectEdgeType.php',
     'PhabricatorAsanaTaskHasObjectEdgeType' => 'applications/doorkeeper/edge/PhabricatorAsanaTaskHasObjectEdgeType.php',
     'PhabricatorAuditActionConstants' => 'applications/audit/constants/PhabricatorAuditActionConstants.php',
     'PhabricatorAuditAddCommentController' => 'applications/audit/controller/PhabricatorAuditAddCommentController.php',
     'PhabricatorAuditApplication' => 'applications/audit/application/PhabricatorAuditApplication.php',
     'PhabricatorAuditCommentEditor' => 'applications/audit/editor/PhabricatorAuditCommentEditor.php',
     'PhabricatorAuditCommitStatusConstants' => 'applications/audit/constants/PhabricatorAuditCommitStatusConstants.php',
     'PhabricatorAuditController' => 'applications/audit/controller/PhabricatorAuditController.php',
     'PhabricatorAuditEditor' => 'applications/audit/editor/PhabricatorAuditEditor.php',
     'PhabricatorAuditInlineComment' => 'applications/audit/storage/PhabricatorAuditInlineComment.php',
     'PhabricatorAuditListController' => 'applications/audit/controller/PhabricatorAuditListController.php',
     'PhabricatorAuditListView' => 'applications/audit/view/PhabricatorAuditListView.php',
     'PhabricatorAuditMailReceiver' => 'applications/audit/mail/PhabricatorAuditMailReceiver.php',
     'PhabricatorAuditManagementDeleteWorkflow' => 'applications/audit/management/PhabricatorAuditManagementDeleteWorkflow.php',
     'PhabricatorAuditManagementWorkflow' => 'applications/audit/management/PhabricatorAuditManagementWorkflow.php',
     'PhabricatorAuditPreviewController' => 'applications/audit/controller/PhabricatorAuditPreviewController.php',
     'PhabricatorAuditReplyHandler' => 'applications/audit/mail/PhabricatorAuditReplyHandler.php',
     'PhabricatorAuditStatusConstants' => 'applications/audit/constants/PhabricatorAuditStatusConstants.php',
     'PhabricatorAuditTransaction' => 'applications/audit/storage/PhabricatorAuditTransaction.php',
     'PhabricatorAuditTransactionComment' => 'applications/audit/storage/PhabricatorAuditTransactionComment.php',
     'PhabricatorAuditTransactionQuery' => 'applications/audit/query/PhabricatorAuditTransactionQuery.php',
     'PhabricatorAuditTransactionView' => 'applications/audit/view/PhabricatorAuditTransactionView.php',
     'PhabricatorAuthAccountView' => 'applications/auth/view/PhabricatorAuthAccountView.php',
     'PhabricatorAuthApplication' => 'applications/auth/application/PhabricatorAuthApplication.php',
     'PhabricatorAuthAuthFactorPHIDType' => 'applications/auth/phid/PhabricatorAuthAuthFactorPHIDType.php',
     'PhabricatorAuthAuthProviderPHIDType' => 'applications/auth/phid/PhabricatorAuthAuthProviderPHIDType.php',
     'PhabricatorAuthConduitAPIMethod' => 'applications/auth/conduit/PhabricatorAuthConduitAPIMethod.php',
     'PhabricatorAuthConfirmLinkController' => 'applications/auth/controller/PhabricatorAuthConfirmLinkController.php',
     'PhabricatorAuthController' => 'applications/auth/controller/PhabricatorAuthController.php',
     'PhabricatorAuthDAO' => 'applications/auth/storage/PhabricatorAuthDAO.php',
     'PhabricatorAuthDisableController' => 'applications/auth/controller/config/PhabricatorAuthDisableController.php',
     'PhabricatorAuthDowngradeSessionController' => 'applications/auth/controller/PhabricatorAuthDowngradeSessionController.php',
     'PhabricatorAuthEditController' => 'applications/auth/controller/config/PhabricatorAuthEditController.php',
     'PhabricatorAuthFactor' => 'applications/auth/factor/PhabricatorAuthFactor.php',
     'PhabricatorAuthFactorConfig' => 'applications/auth/storage/PhabricatorAuthFactorConfig.php',
     'PhabricatorAuthFactorTestCase' => 'applications/auth/factor/__tests__/PhabricatorAuthFactorTestCase.php',
     'PhabricatorAuthFinishController' => 'applications/auth/controller/PhabricatorAuthFinishController.php',
     'PhabricatorAuthHighSecurityRequiredException' => 'applications/auth/exception/PhabricatorAuthHighSecurityRequiredException.php',
     'PhabricatorAuthHighSecurityToken' => 'applications/auth/data/PhabricatorAuthHighSecurityToken.php',
     'PhabricatorAuthInvite' => 'applications/auth/storage/PhabricatorAuthInvite.php',
     'PhabricatorAuthInviteAccountException' => 'applications/auth/exception/PhabricatorAuthInviteAccountException.php',
     'PhabricatorAuthInviteAction' => 'applications/auth/data/PhabricatorAuthInviteAction.php',
     'PhabricatorAuthInviteActionTableView' => 'applications/auth/view/PhabricatorAuthInviteActionTableView.php',
     'PhabricatorAuthInviteController' => 'applications/auth/controller/PhabricatorAuthInviteController.php',
     'PhabricatorAuthInviteDialogException' => 'applications/auth/exception/PhabricatorAuthInviteDialogException.php',
     'PhabricatorAuthInviteEngine' => 'applications/auth/engine/PhabricatorAuthInviteEngine.php',
     'PhabricatorAuthInviteException' => 'applications/auth/exception/PhabricatorAuthInviteException.php',
     'PhabricatorAuthInviteInvalidException' => 'applications/auth/exception/PhabricatorAuthInviteInvalidException.php',
     'PhabricatorAuthInviteLoginException' => 'applications/auth/exception/PhabricatorAuthInviteLoginException.php',
     'PhabricatorAuthInvitePHIDType' => 'applications/auth/phid/PhabricatorAuthInvitePHIDType.php',
     'PhabricatorAuthInviteQuery' => 'applications/auth/query/PhabricatorAuthInviteQuery.php',
     'PhabricatorAuthInviteRegisteredException' => 'applications/auth/exception/PhabricatorAuthInviteRegisteredException.php',
     'PhabricatorAuthInviteSearchEngine' => 'applications/auth/query/PhabricatorAuthInviteSearchEngine.php',
     'PhabricatorAuthInviteTestCase' => 'applications/auth/factor/__tests__/PhabricatorAuthInviteTestCase.php',
     'PhabricatorAuthInviteVerifyException' => 'applications/auth/exception/PhabricatorAuthInviteVerifyException.php',
     'PhabricatorAuthInviteWorker' => 'applications/auth/worker/PhabricatorAuthInviteWorker.php',
     'PhabricatorAuthLinkController' => 'applications/auth/controller/PhabricatorAuthLinkController.php',
     'PhabricatorAuthListController' => 'applications/auth/controller/config/PhabricatorAuthListController.php',
     'PhabricatorAuthLoginController' => 'applications/auth/controller/PhabricatorAuthLoginController.php',
     'PhabricatorAuthLoginHandler' => 'applications/auth/handler/PhabricatorAuthLoginHandler.php',
     'PhabricatorAuthLogoutConduitAPIMethod' => 'applications/auth/conduit/PhabricatorAuthLogoutConduitAPIMethod.php',
     'PhabricatorAuthMainMenuBarExtension' => 'applications/auth/extension/PhabricatorAuthMainMenuBarExtension.php',
     'PhabricatorAuthManagementCachePKCS8Workflow' => 'applications/auth/management/PhabricatorAuthManagementCachePKCS8Workflow.php',
     'PhabricatorAuthManagementLDAPWorkflow' => 'applications/auth/management/PhabricatorAuthManagementLDAPWorkflow.php',
     'PhabricatorAuthManagementListFactorsWorkflow' => 'applications/auth/management/PhabricatorAuthManagementListFactorsWorkflow.php',
     'PhabricatorAuthManagementRecoverWorkflow' => 'applications/auth/management/PhabricatorAuthManagementRecoverWorkflow.php',
     'PhabricatorAuthManagementRefreshWorkflow' => 'applications/auth/management/PhabricatorAuthManagementRefreshWorkflow.php',
     'PhabricatorAuthManagementStripWorkflow' => 'applications/auth/management/PhabricatorAuthManagementStripWorkflow.php',
     'PhabricatorAuthManagementTrustOAuthClientWorkflow' => 'applications/auth/management/PhabricatorAuthManagementTrustOAuthClientWorkflow.php',
     'PhabricatorAuthManagementUnlimitWorkflow' => 'applications/auth/management/PhabricatorAuthManagementUnlimitWorkflow.php',
     'PhabricatorAuthManagementUntrustOAuthClientWorkflow' => 'applications/auth/management/PhabricatorAuthManagementUntrustOAuthClientWorkflow.php',
     'PhabricatorAuthManagementVerifyWorkflow' => 'applications/auth/management/PhabricatorAuthManagementVerifyWorkflow.php',
     'PhabricatorAuthManagementWorkflow' => 'applications/auth/management/PhabricatorAuthManagementWorkflow.php',
     'PhabricatorAuthNeedsApprovalController' => 'applications/auth/controller/PhabricatorAuthNeedsApprovalController.php',
     'PhabricatorAuthNeedsMultiFactorController' => 'applications/auth/controller/PhabricatorAuthNeedsMultiFactorController.php',
     'PhabricatorAuthNewController' => 'applications/auth/controller/config/PhabricatorAuthNewController.php',
     'PhabricatorAuthOldOAuthRedirectController' => 'applications/auth/controller/PhabricatorAuthOldOAuthRedirectController.php',
     'PhabricatorAuthOneTimeLoginController' => 'applications/auth/controller/PhabricatorAuthOneTimeLoginController.php',
     'PhabricatorAuthOneTimeLoginTemporaryTokenType' => 'applications/auth/tokentype/PhabricatorAuthOneTimeLoginTemporaryTokenType.php',
     'PhabricatorAuthPasswordResetTemporaryTokenType' => 'applications/auth/tokentype/PhabricatorAuthPasswordResetTemporaryTokenType.php',
     'PhabricatorAuthProvider' => 'applications/auth/provider/PhabricatorAuthProvider.php',
     'PhabricatorAuthProviderConfig' => 'applications/auth/storage/PhabricatorAuthProviderConfig.php',
     'PhabricatorAuthProviderConfigController' => 'applications/auth/controller/config/PhabricatorAuthProviderConfigController.php',
     'PhabricatorAuthProviderConfigEditor' => 'applications/auth/editor/PhabricatorAuthProviderConfigEditor.php',
     'PhabricatorAuthProviderConfigQuery' => 'applications/auth/query/PhabricatorAuthProviderConfigQuery.php',
     'PhabricatorAuthProviderConfigTransaction' => 'applications/auth/storage/PhabricatorAuthProviderConfigTransaction.php',
     'PhabricatorAuthProviderConfigTransactionQuery' => 'applications/auth/query/PhabricatorAuthProviderConfigTransactionQuery.php',
     'PhabricatorAuthQueryPublicKeysConduitAPIMethod' => 'applications/auth/conduit/PhabricatorAuthQueryPublicKeysConduitAPIMethod.php',
     'PhabricatorAuthRegisterController' => 'applications/auth/controller/PhabricatorAuthRegisterController.php',
     'PhabricatorAuthRevokeTokenController' => 'applications/auth/controller/PhabricatorAuthRevokeTokenController.php',
     'PhabricatorAuthSSHKey' => 'applications/auth/storage/PhabricatorAuthSSHKey.php',
     'PhabricatorAuthSSHKeyController' => 'applications/auth/controller/PhabricatorAuthSSHKeyController.php',
     'PhabricatorAuthSSHKeyDeleteController' => 'applications/auth/controller/PhabricatorAuthSSHKeyDeleteController.php',
     'PhabricatorAuthSSHKeyEditController' => 'applications/auth/controller/PhabricatorAuthSSHKeyEditController.php',
     'PhabricatorAuthSSHKeyGenerateController' => 'applications/auth/controller/PhabricatorAuthSSHKeyGenerateController.php',
     'PhabricatorAuthSSHKeyPHIDType' => 'applications/auth/phid/PhabricatorAuthSSHKeyPHIDType.php',
     'PhabricatorAuthSSHKeyQuery' => 'applications/auth/query/PhabricatorAuthSSHKeyQuery.php',
     'PhabricatorAuthSSHKeyTableView' => 'applications/auth/view/PhabricatorAuthSSHKeyTableView.php',
     'PhabricatorAuthSSHPublicKey' => 'applications/auth/sshkey/PhabricatorAuthSSHPublicKey.php',
     'PhabricatorAuthSession' => 'applications/auth/storage/PhabricatorAuthSession.php',
     'PhabricatorAuthSessionEngine' => 'applications/auth/engine/PhabricatorAuthSessionEngine.php',
     'PhabricatorAuthSessionEngineExtension' => 'applications/auth/engine/PhabricatorAuthSessionEngineExtension.php',
     'PhabricatorAuthSessionEngineExtensionModule' => 'applications/auth/engine/PhabricatorAuthSessionEngineExtensionModule.php',
     'PhabricatorAuthSessionGarbageCollector' => 'applications/auth/garbagecollector/PhabricatorAuthSessionGarbageCollector.php',
     'PhabricatorAuthSessionQuery' => 'applications/auth/query/PhabricatorAuthSessionQuery.php',
     'PhabricatorAuthSetupCheck' => 'applications/config/check/PhabricatorAuthSetupCheck.php',
     'PhabricatorAuthStartController' => 'applications/auth/controller/PhabricatorAuthStartController.php',
     'PhabricatorAuthTOTPKeyTemporaryTokenType' => 'applications/auth/factor/PhabricatorAuthTOTPKeyTemporaryTokenType.php',
     'PhabricatorAuthTemporaryToken' => 'applications/auth/storage/PhabricatorAuthTemporaryToken.php',
     'PhabricatorAuthTemporaryTokenGarbageCollector' => 'applications/auth/garbagecollector/PhabricatorAuthTemporaryTokenGarbageCollector.php',
     'PhabricatorAuthTemporaryTokenQuery' => 'applications/auth/query/PhabricatorAuthTemporaryTokenQuery.php',
     'PhabricatorAuthTemporaryTokenType' => 'applications/auth/tokentype/PhabricatorAuthTemporaryTokenType.php',
     'PhabricatorAuthTemporaryTokenTypeModule' => 'applications/auth/tokentype/PhabricatorAuthTemporaryTokenTypeModule.php',
     'PhabricatorAuthTerminateSessionController' => 'applications/auth/controller/PhabricatorAuthTerminateSessionController.php',
     'PhabricatorAuthTryFactorAction' => 'applications/auth/action/PhabricatorAuthTryFactorAction.php',
     'PhabricatorAuthUnlinkController' => 'applications/auth/controller/PhabricatorAuthUnlinkController.php',
     'PhabricatorAuthValidateController' => 'applications/auth/controller/PhabricatorAuthValidateController.php',
     'PhabricatorAuthenticationConfigOptions' => 'applications/config/option/PhabricatorAuthenticationConfigOptions.php',
     'PhabricatorAutoEventListener' => 'infrastructure/events/PhabricatorAutoEventListener.php',
     'PhabricatorBadgeHasRecipientEdgeType' => 'applications/badges/edge/PhabricatorBadgeHasRecipientEdgeType.php',
     'PhabricatorBadgesApplication' => 'applications/badges/application/PhabricatorBadgesApplication.php',
     'PhabricatorBadgesArchiveController' => 'applications/badges/controller/PhabricatorBadgesArchiveController.php',
     'PhabricatorBadgesAward' => 'applications/badges/storage/PhabricatorBadgesAward.php',
     'PhabricatorBadgesAwardController' => 'applications/badges/controller/PhabricatorBadgesAwardController.php',
     'PhabricatorBadgesAwardQuery' => 'applications/badges/query/PhabricatorBadgesAwardQuery.php',
     'PhabricatorBadgesBadge' => 'applications/badges/storage/PhabricatorBadgesBadge.php',
     'PhabricatorBadgesBadgeNameNgrams' => 'applications/badges/storage/PhabricatorBadgesBadgeNameNgrams.php',
     'PhabricatorBadgesCommentController' => 'applications/badges/controller/PhabricatorBadgesCommentController.php',
     'PhabricatorBadgesController' => 'applications/badges/controller/PhabricatorBadgesController.php',
     'PhabricatorBadgesCreateCapability' => 'applications/badges/capability/PhabricatorBadgesCreateCapability.php',
     'PhabricatorBadgesDAO' => 'applications/badges/storage/PhabricatorBadgesDAO.php',
     'PhabricatorBadgesDatasource' => 'applications/badges/typeahead/PhabricatorBadgesDatasource.php',
     'PhabricatorBadgesDefaultEditCapability' => 'applications/badges/capability/PhabricatorBadgesDefaultEditCapability.php',
     'PhabricatorBadgesEditConduitAPIMethod' => 'applications/badges/conduit/PhabricatorBadgesEditConduitAPIMethod.php',
     'PhabricatorBadgesEditController' => 'applications/badges/controller/PhabricatorBadgesEditController.php',
     'PhabricatorBadgesEditEngine' => 'applications/badges/editor/PhabricatorBadgesEditEngine.php',
     'PhabricatorBadgesEditRecipientsController' => 'applications/badges/controller/PhabricatorBadgesEditRecipientsController.php',
     'PhabricatorBadgesEditor' => 'applications/badges/editor/PhabricatorBadgesEditor.php',
     'PhabricatorBadgesIconSet' => 'applications/badges/icon/PhabricatorBadgesIconSet.php',
     'PhabricatorBadgesListController' => 'applications/badges/controller/PhabricatorBadgesListController.php',
     'PhabricatorBadgesMailReceiver' => 'applications/badges/mail/PhabricatorBadgesMailReceiver.php',
     'PhabricatorBadgesPHIDType' => 'applications/badges/phid/PhabricatorBadgesPHIDType.php',
     'PhabricatorBadgesQuality' => 'applications/badges/constants/PhabricatorBadgesQuality.php',
     'PhabricatorBadgesQuery' => 'applications/badges/query/PhabricatorBadgesQuery.php',
     'PhabricatorBadgesRecipientsListView' => 'applications/badges/view/PhabricatorBadgesRecipientsListView.php',
     'PhabricatorBadgesRemoveRecipientsController' => 'applications/badges/controller/PhabricatorBadgesRemoveRecipientsController.php',
     'PhabricatorBadgesReplyHandler' => 'applications/badges/mail/PhabricatorBadgesReplyHandler.php',
     'PhabricatorBadgesSchemaSpec' => 'applications/badges/storage/PhabricatorBadgesSchemaSpec.php',
     'PhabricatorBadgesSearchConduitAPIMethod' => 'applications/badges/conduit/PhabricatorBadgesSearchConduitAPIMethod.php',
     'PhabricatorBadgesSearchEngine' => 'applications/badges/query/PhabricatorBadgesSearchEngine.php',
     'PhabricatorBadgesTransaction' => 'applications/badges/storage/PhabricatorBadgesTransaction.php',
     'PhabricatorBadgesTransactionComment' => 'applications/badges/storage/PhabricatorBadgesTransactionComment.php',
     'PhabricatorBadgesTransactionQuery' => 'applications/badges/query/PhabricatorBadgesTransactionQuery.php',
     'PhabricatorBadgesViewController' => 'applications/badges/controller/PhabricatorBadgesViewController.php',
     'PhabricatorBarePageUIExample' => 'applications/uiexample/examples/PhabricatorBarePageUIExample.php',
     'PhabricatorBarePageView' => 'view/page/PhabricatorBarePageView.php',
     'PhabricatorBaseURISetupCheck' => 'applications/config/check/PhabricatorBaseURISetupCheck.php',
     'PhabricatorBcryptPasswordHasher' => 'infrastructure/util/password/PhabricatorBcryptPasswordHasher.php',
     'PhabricatorBinariesSetupCheck' => 'applications/config/check/PhabricatorBinariesSetupCheck.php',
     'PhabricatorBitbucketAuthProvider' => 'applications/auth/provider/PhabricatorBitbucketAuthProvider.php',
     'PhabricatorBoardLayoutEngine' => 'applications/project/engine/PhabricatorBoardLayoutEngine.php',
     'PhabricatorBoardRenderingEngine' => 'applications/project/engine/PhabricatorBoardRenderingEngine.php',
     'PhabricatorBoardResponseEngine' => 'applications/project/engine/PhabricatorBoardResponseEngine.php',
     'PhabricatorBoolEditField' => 'applications/transactions/editfield/PhabricatorBoolEditField.php',
     'PhabricatorBot' => 'infrastructure/daemon/bot/PhabricatorBot.php',
     'PhabricatorBotChannel' => 'infrastructure/daemon/bot/target/PhabricatorBotChannel.php',
     'PhabricatorBotDebugLogHandler' => 'infrastructure/daemon/bot/handler/PhabricatorBotDebugLogHandler.php',
     'PhabricatorBotFeedNotificationHandler' => 'infrastructure/daemon/bot/handler/PhabricatorBotFeedNotificationHandler.php',
     'PhabricatorBotFlowdockProtocolAdapter' => 'infrastructure/daemon/bot/adapter/PhabricatorBotFlowdockProtocolAdapter.php',
     'PhabricatorBotHandler' => 'infrastructure/daemon/bot/handler/PhabricatorBotHandler.php',
     'PhabricatorBotLogHandler' => 'infrastructure/daemon/bot/handler/PhabricatorBotLogHandler.php',
     'PhabricatorBotMacroHandler' => 'infrastructure/daemon/bot/handler/PhabricatorBotMacroHandler.php',
     'PhabricatorBotMessage' => 'infrastructure/daemon/bot/PhabricatorBotMessage.php',
     'PhabricatorBotObjectNameHandler' => 'infrastructure/daemon/bot/handler/PhabricatorBotObjectNameHandler.php',
     'PhabricatorBotSymbolHandler' => 'infrastructure/daemon/bot/handler/PhabricatorBotSymbolHandler.php',
     'PhabricatorBotTarget' => 'infrastructure/daemon/bot/target/PhabricatorBotTarget.php',
     'PhabricatorBotUser' => 'infrastructure/daemon/bot/target/PhabricatorBotUser.php',
     'PhabricatorBotWhatsNewHandler' => 'infrastructure/daemon/bot/handler/PhabricatorBotWhatsNewHandler.php',
     'PhabricatorBritishEnglishTranslation' => 'infrastructure/internationalization/translation/PhabricatorBritishEnglishTranslation.php',
     'PhabricatorBuiltinPatchList' => 'infrastructure/storage/patch/PhabricatorBuiltinPatchList.php',
     'PhabricatorBulkContentSource' => 'infrastructure/daemon/contentsource/PhabricatorBulkContentSource.php',
     'PhabricatorBusyUIExample' => 'applications/uiexample/examples/PhabricatorBusyUIExample.php',
     'PhabricatorCacheDAO' => 'applications/cache/storage/PhabricatorCacheDAO.php',
     'PhabricatorCacheGeneralGarbageCollector' => 'applications/cache/garbagecollector/PhabricatorCacheGeneralGarbageCollector.php',
     'PhabricatorCacheManagementPurgeWorkflow' => 'applications/cache/management/PhabricatorCacheManagementPurgeWorkflow.php',
     'PhabricatorCacheManagementWorkflow' => 'applications/cache/management/PhabricatorCacheManagementWorkflow.php',
     'PhabricatorCacheMarkupGarbageCollector' => 'applications/cache/garbagecollector/PhabricatorCacheMarkupGarbageCollector.php',
     'PhabricatorCacheSchemaSpec' => 'applications/cache/storage/PhabricatorCacheSchemaSpec.php',
     'PhabricatorCacheSetupCheck' => 'applications/config/check/PhabricatorCacheSetupCheck.php',
     'PhabricatorCacheSpec' => 'applications/cache/spec/PhabricatorCacheSpec.php',
     'PhabricatorCacheTTLGarbageCollector' => 'applications/cache/garbagecollector/PhabricatorCacheTTLGarbageCollector.php',
     'PhabricatorCaches' => 'applications/cache/PhabricatorCaches.php',
     'PhabricatorCachesTestCase' => 'applications/cache/__tests__/PhabricatorCachesTestCase.php',
     'PhabricatorCalendarApplication' => 'applications/calendar/application/PhabricatorCalendarApplication.php',
     'PhabricatorCalendarController' => 'applications/calendar/controller/PhabricatorCalendarController.php',
     'PhabricatorCalendarDAO' => 'applications/calendar/storage/PhabricatorCalendarDAO.php',
     'PhabricatorCalendarEvent' => 'applications/calendar/storage/PhabricatorCalendarEvent.php',
     'PhabricatorCalendarEventCancelController' => 'applications/calendar/controller/PhabricatorCalendarEventCancelController.php',
     'PhabricatorCalendarEventCommentController' => 'applications/calendar/controller/PhabricatorCalendarEventCommentController.php',
     'PhabricatorCalendarEventDragController' => 'applications/calendar/controller/PhabricatorCalendarEventDragController.php',
     'PhabricatorCalendarEventEditController' => 'applications/calendar/controller/PhabricatorCalendarEventEditController.php',
     'PhabricatorCalendarEventEditor' => 'applications/calendar/editor/PhabricatorCalendarEventEditor.php',
     'PhabricatorCalendarEventEmailCommand' => 'applications/calendar/command/PhabricatorCalendarEventEmailCommand.php',
     'PhabricatorCalendarEventFulltextEngine' => 'applications/calendar/search/PhabricatorCalendarEventFulltextEngine.php',
     'PhabricatorCalendarEventInvitee' => 'applications/calendar/storage/PhabricatorCalendarEventInvitee.php',
     'PhabricatorCalendarEventInviteeQuery' => 'applications/calendar/query/PhabricatorCalendarEventInviteeQuery.php',
     'PhabricatorCalendarEventJoinController' => 'applications/calendar/controller/PhabricatorCalendarEventJoinController.php',
     'PhabricatorCalendarEventListController' => 'applications/calendar/controller/PhabricatorCalendarEventListController.php',
     'PhabricatorCalendarEventMailReceiver' => 'applications/calendar/mail/PhabricatorCalendarEventMailReceiver.php',
     'PhabricatorCalendarEventPHIDType' => 'applications/calendar/phid/PhabricatorCalendarEventPHIDType.php',
     'PhabricatorCalendarEventQuery' => 'applications/calendar/query/PhabricatorCalendarEventQuery.php',
     'PhabricatorCalendarEventRSVPEmailCommand' => 'applications/calendar/command/PhabricatorCalendarEventRSVPEmailCommand.php',
     'PhabricatorCalendarEventSearchEngine' => 'applications/calendar/query/PhabricatorCalendarEventSearchEngine.php',
     'PhabricatorCalendarEventTransaction' => 'applications/calendar/storage/PhabricatorCalendarEventTransaction.php',
     'PhabricatorCalendarEventTransactionComment' => 'applications/calendar/storage/PhabricatorCalendarEventTransactionComment.php',
     'PhabricatorCalendarEventTransactionQuery' => 'applications/calendar/query/PhabricatorCalendarEventTransactionQuery.php',
     'PhabricatorCalendarEventViewController' => 'applications/calendar/controller/PhabricatorCalendarEventViewController.php',
     'PhabricatorCalendarHoliday' => 'applications/calendar/storage/PhabricatorCalendarHoliday.php',
     'PhabricatorCalendarHolidayTestCase' => 'applications/calendar/storage/__tests__/PhabricatorCalendarHolidayTestCase.php',
     'PhabricatorCalendarIconSet' => 'applications/calendar/icon/PhabricatorCalendarIconSet.php',
     'PhabricatorCalendarRemarkupRule' => 'applications/calendar/remarkup/PhabricatorCalendarRemarkupRule.php',
     'PhabricatorCalendarReplyHandler' => 'applications/calendar/mail/PhabricatorCalendarReplyHandler.php',
     'PhabricatorCalendarSchemaSpec' => 'applications/calendar/storage/PhabricatorCalendarSchemaSpec.php',
     'PhabricatorCampfireProtocolAdapter' => 'infrastructure/daemon/bot/adapter/PhabricatorCampfireProtocolAdapter.php',
     'PhabricatorCelerityApplication' => 'applications/celerity/application/PhabricatorCelerityApplication.php',
     'PhabricatorCelerityTestCase' => '__tests__/PhabricatorCelerityTestCase.php',
     'PhabricatorChangeParserTestCase' => 'applications/repository/worker/__tests__/PhabricatorChangeParserTestCase.php',
     'PhabricatorChangesetResponse' => 'infrastructure/diff/PhabricatorChangesetResponse.php',
     'PhabricatorChatLogApplication' => 'applications/chatlog/application/PhabricatorChatLogApplication.php',
     'PhabricatorChatLogChannel' => 'applications/chatlog/storage/PhabricatorChatLogChannel.php',
     'PhabricatorChatLogChannelListController' => 'applications/chatlog/controller/PhabricatorChatLogChannelListController.php',
     'PhabricatorChatLogChannelLogController' => 'applications/chatlog/controller/PhabricatorChatLogChannelLogController.php',
     'PhabricatorChatLogChannelQuery' => 'applications/chatlog/query/PhabricatorChatLogChannelQuery.php',
     'PhabricatorChatLogController' => 'applications/chatlog/controller/PhabricatorChatLogController.php',
     'PhabricatorChatLogDAO' => 'applications/chatlog/storage/PhabricatorChatLogDAO.php',
     'PhabricatorChatLogEvent' => 'applications/chatlog/storage/PhabricatorChatLogEvent.php',
     'PhabricatorChatLogQuery' => 'applications/chatlog/query/PhabricatorChatLogQuery.php',
     'PhabricatorChunkedFileStorageEngine' => 'applications/files/engine/PhabricatorChunkedFileStorageEngine.php',
     'PhabricatorClusterConfigOptions' => 'applications/config/option/PhabricatorClusterConfigOptions.php',
     'PhabricatorClusterDatabasesConfigOptionType' => 'infrastructure/cluster/PhabricatorClusterDatabasesConfigOptionType.php',
     'PhabricatorClusterException' => 'infrastructure/cluster/PhabricatorClusterException.php',
     'PhabricatorClusterExceptionHandler' => 'infrastructure/cluster/PhabricatorClusterExceptionHandler.php',
     'PhabricatorClusterImpossibleWriteException' => 'infrastructure/cluster/PhabricatorClusterImpossibleWriteException.php',
     'PhabricatorClusterImproperWriteException' => 'infrastructure/cluster/PhabricatorClusterImproperWriteException.php',
     'PhabricatorClusterStrandedException' => 'infrastructure/cluster/PhabricatorClusterStrandedException.php',
     'PhabricatorColumnProxyInterface' => 'applications/project/interface/PhabricatorColumnProxyInterface.php',
     'PhabricatorColumnsEditField' => 'applications/transactions/editfield/PhabricatorColumnsEditField.php',
     'PhabricatorCommentEditEngineExtension' => 'applications/transactions/engineextension/PhabricatorCommentEditEngineExtension.php',
     'PhabricatorCommentEditField' => 'applications/transactions/editfield/PhabricatorCommentEditField.php',
     'PhabricatorCommentEditType' => 'applications/transactions/edittype/PhabricatorCommentEditType.php',
     'PhabricatorCommitBranchesField' => 'applications/repository/customfield/PhabricatorCommitBranchesField.php',
     'PhabricatorCommitCustomField' => 'applications/repository/customfield/PhabricatorCommitCustomField.php',
     'PhabricatorCommitMergedCommitsField' => 'applications/repository/customfield/PhabricatorCommitMergedCommitsField.php',
     'PhabricatorCommitRepositoryField' => 'applications/repository/customfield/PhabricatorCommitRepositoryField.php',
     'PhabricatorCommitSearchEngine' => 'applications/audit/query/PhabricatorCommitSearchEngine.php',
     'PhabricatorCommitTagsField' => 'applications/repository/customfield/PhabricatorCommitTagsField.php',
     'PhabricatorCommonPasswords' => 'applications/auth/constants/PhabricatorCommonPasswords.php',
     'PhabricatorConduitAPIController' => 'applications/conduit/controller/PhabricatorConduitAPIController.php',
     'PhabricatorConduitApplication' => 'applications/conduit/application/PhabricatorConduitApplication.php',
     'PhabricatorConduitCertificateToken' => 'applications/conduit/storage/PhabricatorConduitCertificateToken.php',
     'PhabricatorConduitConnectionLog' => 'applications/conduit/storage/PhabricatorConduitConnectionLog.php',
     'PhabricatorConduitConsoleController' => 'applications/conduit/controller/PhabricatorConduitConsoleController.php',
     'PhabricatorConduitContentSource' => 'infrastructure/contentsource/PhabricatorConduitContentSource.php',
     'PhabricatorConduitController' => 'applications/conduit/controller/PhabricatorConduitController.php',
     'PhabricatorConduitDAO' => 'applications/conduit/storage/PhabricatorConduitDAO.php',
     'PhabricatorConduitEditField' => 'applications/transactions/editfield/PhabricatorConduitEditField.php',
     'PhabricatorConduitListController' => 'applications/conduit/controller/PhabricatorConduitListController.php',
     'PhabricatorConduitLogController' => 'applications/conduit/controller/PhabricatorConduitLogController.php',
     'PhabricatorConduitLogQuery' => 'applications/conduit/query/PhabricatorConduitLogQuery.php',
     'PhabricatorConduitLogSearchEngine' => 'applications/conduit/query/PhabricatorConduitLogSearchEngine.php',
     'PhabricatorConduitMethodCallLog' => 'applications/conduit/storage/PhabricatorConduitMethodCallLog.php',
     'PhabricatorConduitMethodQuery' => 'applications/conduit/query/PhabricatorConduitMethodQuery.php',
     'PhabricatorConduitRequestExceptionHandler' => 'aphront/handler/PhabricatorConduitRequestExceptionHandler.php',
     'PhabricatorConduitResultInterface' => 'applications/conduit/interface/PhabricatorConduitResultInterface.php',
     'PhabricatorConduitSearchEngine' => 'applications/conduit/query/PhabricatorConduitSearchEngine.php',
     'PhabricatorConduitSearchFieldSpecification' => 'applications/conduit/interface/PhabricatorConduitSearchFieldSpecification.php',
     'PhabricatorConduitTestCase' => '__tests__/PhabricatorConduitTestCase.php',
     'PhabricatorConduitToken' => 'applications/conduit/storage/PhabricatorConduitToken.php',
     'PhabricatorConduitTokenController' => 'applications/conduit/controller/PhabricatorConduitTokenController.php',
     'PhabricatorConduitTokenEditController' => 'applications/conduit/controller/PhabricatorConduitTokenEditController.php',
     'PhabricatorConduitTokenHandshakeController' => 'applications/conduit/controller/PhabricatorConduitTokenHandshakeController.php',
     'PhabricatorConduitTokenQuery' => 'applications/conduit/query/PhabricatorConduitTokenQuery.php',
     'PhabricatorConduitTokenTerminateController' => 'applications/conduit/controller/PhabricatorConduitTokenTerminateController.php',
     'PhabricatorConduitTokensSettingsPanel' => 'applications/conduit/settings/PhabricatorConduitTokensSettingsPanel.php',
     'PhabricatorConfigAllController' => 'applications/config/controller/PhabricatorConfigAllController.php',
     'PhabricatorConfigApplication' => 'applications/config/application/PhabricatorConfigApplication.php',
     'PhabricatorConfigCacheController' => 'applications/config/controller/PhabricatorConfigCacheController.php',
     'PhabricatorConfigClusterDatabasesController' => 'applications/config/controller/PhabricatorConfigClusterDatabasesController.php',
     'PhabricatorConfigClusterNotificationsController' => 'applications/config/controller/PhabricatorConfigClusterNotificationsController.php',
     'PhabricatorConfigClusterRepositoriesController' => 'applications/config/controller/PhabricatorConfigClusterRepositoriesController.php',
     'PhabricatorConfigCollectorsModule' => 'applications/config/module/PhabricatorConfigCollectorsModule.php',
     'PhabricatorConfigColumnSchema' => 'applications/config/schema/PhabricatorConfigColumnSchema.php',
     'PhabricatorConfigConfigPHIDType' => 'applications/config/phid/PhabricatorConfigConfigPHIDType.php',
     'PhabricatorConfigController' => 'applications/config/controller/PhabricatorConfigController.php',
     'PhabricatorConfigCoreSchemaSpec' => 'applications/config/schema/PhabricatorConfigCoreSchemaSpec.php',
     'PhabricatorConfigDatabaseController' => 'applications/config/controller/PhabricatorConfigDatabaseController.php',
     'PhabricatorConfigDatabaseIssueController' => 'applications/config/controller/PhabricatorConfigDatabaseIssueController.php',
     'PhabricatorConfigDatabaseSchema' => 'applications/config/schema/PhabricatorConfigDatabaseSchema.php',
     'PhabricatorConfigDatabaseSource' => 'infrastructure/env/PhabricatorConfigDatabaseSource.php',
     'PhabricatorConfigDatabaseStatusController' => 'applications/config/controller/PhabricatorConfigDatabaseStatusController.php',
     'PhabricatorConfigDefaultSource' => 'infrastructure/env/PhabricatorConfigDefaultSource.php',
     'PhabricatorConfigDictionarySource' => 'infrastructure/env/PhabricatorConfigDictionarySource.php',
     'PhabricatorConfigEdgeModule' => 'applications/config/module/PhabricatorConfigEdgeModule.php',
     'PhabricatorConfigEditController' => 'applications/config/controller/PhabricatorConfigEditController.php',
     'PhabricatorConfigEditor' => 'applications/config/editor/PhabricatorConfigEditor.php',
     'PhabricatorConfigEntry' => 'applications/config/storage/PhabricatorConfigEntry.php',
     'PhabricatorConfigEntryDAO' => 'applications/config/storage/PhabricatorConfigEntryDAO.php',
     'PhabricatorConfigEntryQuery' => 'applications/config/query/PhabricatorConfigEntryQuery.php',
     'PhabricatorConfigFileSource' => 'infrastructure/env/PhabricatorConfigFileSource.php',
     'PhabricatorConfigGroupController' => 'applications/config/controller/PhabricatorConfigGroupController.php',
     'PhabricatorConfigHTTPParameterTypesModule' => 'applications/config/module/PhabricatorConfigHTTPParameterTypesModule.php',
     'PhabricatorConfigHistoryController' => 'applications/config/controller/PhabricatorConfigHistoryController.php',
     'PhabricatorConfigIgnoreController' => 'applications/config/controller/PhabricatorConfigIgnoreController.php',
     'PhabricatorConfigIssueListController' => 'applications/config/controller/PhabricatorConfigIssueListController.php',
     'PhabricatorConfigIssueViewController' => 'applications/config/controller/PhabricatorConfigIssueViewController.php',
     'PhabricatorConfigJSON' => 'applications/config/json/PhabricatorConfigJSON.php',
     'PhabricatorConfigJSONOptionType' => 'applications/config/custom/PhabricatorConfigJSONOptionType.php',
     'PhabricatorConfigKeySchema' => 'applications/config/schema/PhabricatorConfigKeySchema.php',
     'PhabricatorConfigListController' => 'applications/config/controller/PhabricatorConfigListController.php',
     'PhabricatorConfigLocalSource' => 'infrastructure/env/PhabricatorConfigLocalSource.php',
     'PhabricatorConfigManagementDeleteWorkflow' => 'applications/config/management/PhabricatorConfigManagementDeleteWorkflow.php',
     'PhabricatorConfigManagementGetWorkflow' => 'applications/config/management/PhabricatorConfigManagementGetWorkflow.php',
     'PhabricatorConfigManagementListWorkflow' => 'applications/config/management/PhabricatorConfigManagementListWorkflow.php',
     'PhabricatorConfigManagementMigrateWorkflow' => 'applications/config/management/PhabricatorConfigManagementMigrateWorkflow.php',
     'PhabricatorConfigManagementSetWorkflow' => 'applications/config/management/PhabricatorConfigManagementSetWorkflow.php',
     'PhabricatorConfigManagementWorkflow' => 'applications/config/management/PhabricatorConfigManagementWorkflow.php',
     'PhabricatorConfigModule' => 'applications/config/module/PhabricatorConfigModule.php',
     'PhabricatorConfigModuleController' => 'applications/config/controller/PhabricatorConfigModuleController.php',
     'PhabricatorConfigOption' => 'applications/config/option/PhabricatorConfigOption.php',
     'PhabricatorConfigOptionType' => 'applications/config/custom/PhabricatorConfigOptionType.php',
     'PhabricatorConfigPHIDModule' => 'applications/config/module/PhabricatorConfigPHIDModule.php',
     'PhabricatorConfigProxySource' => 'infrastructure/env/PhabricatorConfigProxySource.php',
     'PhabricatorConfigPurgeCacheController' => 'applications/config/controller/PhabricatorConfigPurgeCacheController.php',
     'PhabricatorConfigRequestExceptionHandlerModule' => 'applications/config/module/PhabricatorConfigRequestExceptionHandlerModule.php',
     'PhabricatorConfigResponse' => 'applications/config/response/PhabricatorConfigResponse.php',
     'PhabricatorConfigSchemaQuery' => 'applications/config/schema/PhabricatorConfigSchemaQuery.php',
     'PhabricatorConfigSchemaSpec' => 'applications/config/schema/PhabricatorConfigSchemaSpec.php',
     'PhabricatorConfigServerSchema' => 'applications/config/schema/PhabricatorConfigServerSchema.php',
     'PhabricatorConfigSiteModule' => 'applications/config/module/PhabricatorConfigSiteModule.php',
     'PhabricatorConfigSiteSource' => 'infrastructure/env/PhabricatorConfigSiteSource.php',
     'PhabricatorConfigSource' => 'infrastructure/env/PhabricatorConfigSource.php',
     'PhabricatorConfigStackSource' => 'infrastructure/env/PhabricatorConfigStackSource.php',
     'PhabricatorConfigStorageSchema' => 'applications/config/schema/PhabricatorConfigStorageSchema.php',
     'PhabricatorConfigTableSchema' => 'applications/config/schema/PhabricatorConfigTableSchema.php',
     'PhabricatorConfigTransaction' => 'applications/config/storage/PhabricatorConfigTransaction.php',
     'PhabricatorConfigTransactionQuery' => 'applications/config/query/PhabricatorConfigTransactionQuery.php',
     'PhabricatorConfigValidationException' => 'applications/config/exception/PhabricatorConfigValidationException.php',
     'PhabricatorConfigVersionsModule' => 'applications/config/module/PhabricatorConfigVersionsModule.php',
     'PhabricatorConfigWelcomeController' => 'applications/config/controller/PhabricatorConfigWelcomeController.php',
     'PhabricatorConpherenceApplication' => 'applications/conpherence/application/PhabricatorConpherenceApplication.php',
     'PhabricatorConpherencePreferencesSettingsPanel' => 'applications/settings/panel/PhabricatorConpherencePreferencesSettingsPanel.php',
     'PhabricatorConpherenceThreadPHIDType' => 'applications/conpherence/phid/PhabricatorConpherenceThreadPHIDType.php',
     'PhabricatorConsoleApplication' => 'applications/console/application/PhabricatorConsoleApplication.php',
     'PhabricatorConsoleContentSource' => 'infrastructure/contentsource/PhabricatorConsoleContentSource.php',
     'PhabricatorContentSource' => 'infrastructure/contentsource/PhabricatorContentSource.php',
     'PhabricatorContentSourceModule' => 'infrastructure/contentsource/PhabricatorContentSourceModule.php',
     'PhabricatorContentSourceView' => 'infrastructure/contentsource/PhabricatorContentSourceView.php',
     'PhabricatorContributedToObjectEdgeType' => 'applications/transactions/edges/PhabricatorContributedToObjectEdgeType.php',
     'PhabricatorController' => 'applications/base/controller/PhabricatorController.php',
     'PhabricatorCookies' => 'applications/auth/constants/PhabricatorCookies.php',
     'PhabricatorCoreConfigOptions' => 'applications/config/option/PhabricatorCoreConfigOptions.php',
     'PhabricatorCountdown' => 'applications/countdown/storage/PhabricatorCountdown.php',
     'PhabricatorCountdownApplication' => 'applications/countdown/application/PhabricatorCountdownApplication.php',
     'PhabricatorCountdownController' => 'applications/countdown/controller/PhabricatorCountdownController.php',
     'PhabricatorCountdownCountdownPHIDType' => 'applications/countdown/phid/PhabricatorCountdownCountdownPHIDType.php',
     'PhabricatorCountdownDAO' => 'applications/countdown/storage/PhabricatorCountdownDAO.php',
     'PhabricatorCountdownDefaultEditCapability' => 'applications/countdown/capability/PhabricatorCountdownDefaultEditCapability.php',
     'PhabricatorCountdownDefaultViewCapability' => 'applications/countdown/capability/PhabricatorCountdownDefaultViewCapability.php',
     'PhabricatorCountdownDeleteController' => 'applications/countdown/controller/PhabricatorCountdownDeleteController.php',
     'PhabricatorCountdownEditController' => 'applications/countdown/controller/PhabricatorCountdownEditController.php',
     'PhabricatorCountdownEditEngine' => 'applications/countdown/editor/PhabricatorCountdownEditEngine.php',
     'PhabricatorCountdownEditor' => 'applications/countdown/editor/PhabricatorCountdownEditor.php',
     'PhabricatorCountdownListController' => 'applications/countdown/controller/PhabricatorCountdownListController.php',
     'PhabricatorCountdownMailReceiver' => 'applications/countdown/mail/PhabricatorCountdownMailReceiver.php',
     'PhabricatorCountdownQuery' => 'applications/countdown/query/PhabricatorCountdownQuery.php',
     'PhabricatorCountdownRemarkupRule' => 'applications/countdown/remarkup/PhabricatorCountdownRemarkupRule.php',
     'PhabricatorCountdownReplyHandler' => 'applications/countdown/mail/PhabricatorCountdownReplyHandler.php',
     'PhabricatorCountdownSchemaSpec' => 'applications/countdown/storage/PhabricatorCountdownSchemaSpec.php',
     'PhabricatorCountdownSearchEngine' => 'applications/countdown/query/PhabricatorCountdownSearchEngine.php',
     'PhabricatorCountdownTransaction' => 'applications/countdown/storage/PhabricatorCountdownTransaction.php',
     'PhabricatorCountdownTransactionComment' => 'applications/countdown/storage/PhabricatorCountdownTransactionComment.php',
     'PhabricatorCountdownTransactionQuery' => 'applications/countdown/query/PhabricatorCountdownTransactionQuery.php',
     'PhabricatorCountdownView' => 'applications/countdown/view/PhabricatorCountdownView.php',
     'PhabricatorCountdownViewController' => 'applications/countdown/controller/PhabricatorCountdownViewController.php',
     'PhabricatorCredentialsUsedByObjectEdgeType' => 'applications/passphrase/edge/PhabricatorCredentialsUsedByObjectEdgeType.php',
     'PhabricatorCursorPagedPolicyAwareQuery' => 'infrastructure/query/policy/PhabricatorCursorPagedPolicyAwareQuery.php',
     'PhabricatorCustomField' => 'infrastructure/customfield/field/PhabricatorCustomField.php',
     'PhabricatorCustomFieldAttachment' => 'infrastructure/customfield/field/PhabricatorCustomFieldAttachment.php',
     'PhabricatorCustomFieldConfigOptionType' => 'infrastructure/customfield/config/PhabricatorCustomFieldConfigOptionType.php',
     'PhabricatorCustomFieldDataNotAvailableException' => 'infrastructure/customfield/exception/PhabricatorCustomFieldDataNotAvailableException.php',
     'PhabricatorCustomFieldEditEngineExtension' => 'infrastructure/customfield/engineextension/PhabricatorCustomFieldEditEngineExtension.php',
     'PhabricatorCustomFieldEditField' => 'infrastructure/customfield/editor/PhabricatorCustomFieldEditField.php',
     'PhabricatorCustomFieldEditType' => 'infrastructure/customfield/editor/PhabricatorCustomFieldEditType.php',
     'PhabricatorCustomFieldFulltextEngineExtension' => 'infrastructure/customfield/engineextension/PhabricatorCustomFieldFulltextEngineExtension.php',
     'PhabricatorCustomFieldHeraldField' => 'infrastructure/customfield/herald/PhabricatorCustomFieldHeraldField.php',
     'PhabricatorCustomFieldHeraldFieldGroup' => 'infrastructure/customfield/herald/PhabricatorCustomFieldHeraldFieldGroup.php',
     'PhabricatorCustomFieldImplementationIncompleteException' => 'infrastructure/customfield/exception/PhabricatorCustomFieldImplementationIncompleteException.php',
     'PhabricatorCustomFieldIndexStorage' => 'infrastructure/customfield/storage/PhabricatorCustomFieldIndexStorage.php',
     'PhabricatorCustomFieldInterface' => 'infrastructure/customfield/interface/PhabricatorCustomFieldInterface.php',
     'PhabricatorCustomFieldList' => 'infrastructure/customfield/field/PhabricatorCustomFieldList.php',
     'PhabricatorCustomFieldMonogramParser' => 'infrastructure/customfield/parser/PhabricatorCustomFieldMonogramParser.php',
     'PhabricatorCustomFieldNotAttachedException' => 'infrastructure/customfield/exception/PhabricatorCustomFieldNotAttachedException.php',
     'PhabricatorCustomFieldNotProxyException' => 'infrastructure/customfield/exception/PhabricatorCustomFieldNotProxyException.php',
     'PhabricatorCustomFieldNumericIndexStorage' => 'infrastructure/customfield/storage/PhabricatorCustomFieldNumericIndexStorage.php',
     'PhabricatorCustomFieldSearchEngineExtension' => 'infrastructure/customfield/engineextension/PhabricatorCustomFieldSearchEngineExtension.php',
     'PhabricatorCustomFieldStorage' => 'infrastructure/customfield/storage/PhabricatorCustomFieldStorage.php',
     'PhabricatorCustomFieldStringIndexStorage' => 'infrastructure/customfield/storage/PhabricatorCustomFieldStringIndexStorage.php',
     'PhabricatorCustomHeaderConfigType' => 'applications/config/custom/PhabricatorCustomHeaderConfigType.php',
     'PhabricatorDaemon' => 'infrastructure/daemon/PhabricatorDaemon.php',
     'PhabricatorDaemonBulkJobController' => 'applications/daemon/controller/PhabricatorDaemonBulkJobController.php',
     'PhabricatorDaemonBulkJobListController' => 'applications/daemon/controller/PhabricatorDaemonBulkJobListController.php',
     'PhabricatorDaemonBulkJobMonitorController' => 'applications/daemon/controller/PhabricatorDaemonBulkJobMonitorController.php',
     'PhabricatorDaemonBulkJobViewController' => 'applications/daemon/controller/PhabricatorDaemonBulkJobViewController.php',
     'PhabricatorDaemonConsoleController' => 'applications/daemon/controller/PhabricatorDaemonConsoleController.php',
     'PhabricatorDaemonContentSource' => 'infrastructure/daemon/contentsource/PhabricatorDaemonContentSource.php',
     'PhabricatorDaemonController' => 'applications/daemon/controller/PhabricatorDaemonController.php',
     'PhabricatorDaemonDAO' => 'applications/daemon/storage/PhabricatorDaemonDAO.php',
     'PhabricatorDaemonEventListener' => 'applications/daemon/event/PhabricatorDaemonEventListener.php',
     'PhabricatorDaemonLog' => 'applications/daemon/storage/PhabricatorDaemonLog.php',
     'PhabricatorDaemonLogEvent' => 'applications/daemon/storage/PhabricatorDaemonLogEvent.php',
     'PhabricatorDaemonLogEventGarbageCollector' => 'applications/daemon/garbagecollector/PhabricatorDaemonLogEventGarbageCollector.php',
     'PhabricatorDaemonLogEventViewController' => 'applications/daemon/controller/PhabricatorDaemonLogEventViewController.php',
     'PhabricatorDaemonLogEventsView' => 'applications/daemon/view/PhabricatorDaemonLogEventsView.php',
     'PhabricatorDaemonLogGarbageCollector' => 'applications/daemon/garbagecollector/PhabricatorDaemonLogGarbageCollector.php',
     'PhabricatorDaemonLogListController' => 'applications/daemon/controller/PhabricatorDaemonLogListController.php',
     'PhabricatorDaemonLogListView' => 'applications/daemon/view/PhabricatorDaemonLogListView.php',
     'PhabricatorDaemonLogQuery' => 'applications/daemon/query/PhabricatorDaemonLogQuery.php',
     'PhabricatorDaemonLogViewController' => 'applications/daemon/controller/PhabricatorDaemonLogViewController.php',
     'PhabricatorDaemonManagementDebugWorkflow' => 'applications/daemon/management/PhabricatorDaemonManagementDebugWorkflow.php',
     'PhabricatorDaemonManagementLaunchWorkflow' => 'applications/daemon/management/PhabricatorDaemonManagementLaunchWorkflow.php',
     'PhabricatorDaemonManagementListWorkflow' => 'applications/daemon/management/PhabricatorDaemonManagementListWorkflow.php',
     'PhabricatorDaemonManagementLogWorkflow' => 'applications/daemon/management/PhabricatorDaemonManagementLogWorkflow.php',
     'PhabricatorDaemonManagementReloadWorkflow' => 'applications/daemon/management/PhabricatorDaemonManagementReloadWorkflow.php',
     'PhabricatorDaemonManagementRestartWorkflow' => 'applications/daemon/management/PhabricatorDaemonManagementRestartWorkflow.php',
     'PhabricatorDaemonManagementStartWorkflow' => 'applications/daemon/management/PhabricatorDaemonManagementStartWorkflow.php',
     'PhabricatorDaemonManagementStatusWorkflow' => 'applications/daemon/management/PhabricatorDaemonManagementStatusWorkflow.php',
     'PhabricatorDaemonManagementStopWorkflow' => 'applications/daemon/management/PhabricatorDaemonManagementStopWorkflow.php',
     'PhabricatorDaemonManagementWorkflow' => 'applications/daemon/management/PhabricatorDaemonManagementWorkflow.php',
     'PhabricatorDaemonOverseerModule' => 'infrastructure/daemon/overseer/PhabricatorDaemonOverseerModule.php',
     'PhabricatorDaemonReference' => 'infrastructure/daemon/control/PhabricatorDaemonReference.php',
     'PhabricatorDaemonTaskGarbageCollector' => 'applications/daemon/garbagecollector/PhabricatorDaemonTaskGarbageCollector.php',
     'PhabricatorDaemonTasksTableView' => 'applications/daemon/view/PhabricatorDaemonTasksTableView.php',
     'PhabricatorDaemonsApplication' => 'applications/daemon/application/PhabricatorDaemonsApplication.php',
     'PhabricatorDaemonsSetupCheck' => 'applications/config/check/PhabricatorDaemonsSetupCheck.php',
     'PhabricatorDailyRoutineTriggerClock' => 'infrastructure/daemon/workers/clock/PhabricatorDailyRoutineTriggerClock.php',
     'PhabricatorDashboard' => 'applications/dashboard/storage/PhabricatorDashboard.php',
     'PhabricatorDashboardAddPanelController' => 'applications/dashboard/controller/PhabricatorDashboardAddPanelController.php',
     'PhabricatorDashboardApplication' => 'applications/dashboard/application/PhabricatorDashboardApplication.php',
     'PhabricatorDashboardArchiveController' => 'applications/dashboard/controller/PhabricatorDashboardArchiveController.php',
     'PhabricatorDashboardController' => 'applications/dashboard/controller/PhabricatorDashboardController.php',
     'PhabricatorDashboardCopyController' => 'applications/dashboard/controller/PhabricatorDashboardCopyController.php',
     'PhabricatorDashboardDAO' => 'applications/dashboard/storage/PhabricatorDashboardDAO.php',
     'PhabricatorDashboardDashboardHasPanelEdgeType' => 'applications/dashboard/edge/PhabricatorDashboardDashboardHasPanelEdgeType.php',
     'PhabricatorDashboardDashboardPHIDType' => 'applications/dashboard/phid/PhabricatorDashboardDashboardPHIDType.php',
     'PhabricatorDashboardEditController' => 'applications/dashboard/controller/PhabricatorDashboardEditController.php',
     'PhabricatorDashboardInstall' => 'applications/dashboard/storage/PhabricatorDashboardInstall.php',
     'PhabricatorDashboardInstallController' => 'applications/dashboard/controller/PhabricatorDashboardInstallController.php',
     'PhabricatorDashboardLayoutConfig' => 'applications/dashboard/layoutconfig/PhabricatorDashboardLayoutConfig.php',
     'PhabricatorDashboardListController' => 'applications/dashboard/controller/PhabricatorDashboardListController.php',
     'PhabricatorDashboardManageController' => 'applications/dashboard/controller/PhabricatorDashboardManageController.php',
     'PhabricatorDashboardMovePanelController' => 'applications/dashboard/controller/PhabricatorDashboardMovePanelController.php',
     'PhabricatorDashboardPanel' => 'applications/dashboard/storage/PhabricatorDashboardPanel.php',
     'PhabricatorDashboardPanelArchiveController' => 'applications/dashboard/controller/PhabricatorDashboardPanelArchiveController.php',
     'PhabricatorDashboardPanelCoreCustomField' => 'applications/dashboard/customfield/PhabricatorDashboardPanelCoreCustomField.php',
     'PhabricatorDashboardPanelCustomField' => 'applications/dashboard/customfield/PhabricatorDashboardPanelCustomField.php',
     'PhabricatorDashboardPanelEditController' => 'applications/dashboard/controller/PhabricatorDashboardPanelEditController.php',
     'PhabricatorDashboardPanelHasDashboardEdgeType' => 'applications/dashboard/edge/PhabricatorDashboardPanelHasDashboardEdgeType.php',
     'PhabricatorDashboardPanelListController' => 'applications/dashboard/controller/PhabricatorDashboardPanelListController.php',
     'PhabricatorDashboardPanelPHIDType' => 'applications/dashboard/phid/PhabricatorDashboardPanelPHIDType.php',
     'PhabricatorDashboardPanelQuery' => 'applications/dashboard/query/PhabricatorDashboardPanelQuery.php',
     'PhabricatorDashboardPanelRenderController' => 'applications/dashboard/controller/PhabricatorDashboardPanelRenderController.php',
     'PhabricatorDashboardPanelRenderingEngine' => 'applications/dashboard/engine/PhabricatorDashboardPanelRenderingEngine.php',
     'PhabricatorDashboardPanelSearchApplicationCustomField' => 'applications/dashboard/customfield/PhabricatorDashboardPanelSearchApplicationCustomField.php',
     'PhabricatorDashboardPanelSearchEngine' => 'applications/dashboard/query/PhabricatorDashboardPanelSearchEngine.php',
     'PhabricatorDashboardPanelSearchQueryCustomField' => 'applications/dashboard/customfield/PhabricatorDashboardPanelSearchQueryCustomField.php',
     'PhabricatorDashboardPanelTabsCustomField' => 'applications/dashboard/customfield/PhabricatorDashboardPanelTabsCustomField.php',
     'PhabricatorDashboardPanelTransaction' => 'applications/dashboard/storage/PhabricatorDashboardPanelTransaction.php',
     'PhabricatorDashboardPanelTransactionEditor' => 'applications/dashboard/editor/PhabricatorDashboardPanelTransactionEditor.php',
     'PhabricatorDashboardPanelTransactionQuery' => 'applications/dashboard/query/PhabricatorDashboardPanelTransactionQuery.php',
     'PhabricatorDashboardPanelType' => 'applications/dashboard/paneltype/PhabricatorDashboardPanelType.php',
     'PhabricatorDashboardPanelViewController' => 'applications/dashboard/controller/PhabricatorDashboardPanelViewController.php',
     'PhabricatorDashboardQuery' => 'applications/dashboard/query/PhabricatorDashboardQuery.php',
     'PhabricatorDashboardQueryPanelType' => 'applications/dashboard/paneltype/PhabricatorDashboardQueryPanelType.php',
     'PhabricatorDashboardRemarkupRule' => 'applications/dashboard/remarkup/PhabricatorDashboardRemarkupRule.php',
     'PhabricatorDashboardRemovePanelController' => 'applications/dashboard/controller/PhabricatorDashboardRemovePanelController.php',
     'PhabricatorDashboardRenderingEngine' => 'applications/dashboard/engine/PhabricatorDashboardRenderingEngine.php',
     'PhabricatorDashboardSchemaSpec' => 'applications/dashboard/storage/PhabricatorDashboardSchemaSpec.php',
     'PhabricatorDashboardSearchEngine' => 'applications/dashboard/query/PhabricatorDashboardSearchEngine.php',
     'PhabricatorDashboardTabsPanelType' => 'applications/dashboard/paneltype/PhabricatorDashboardTabsPanelType.php',
     'PhabricatorDashboardTextPanelType' => 'applications/dashboard/paneltype/PhabricatorDashboardTextPanelType.php',
     'PhabricatorDashboardTransaction' => 'applications/dashboard/storage/PhabricatorDashboardTransaction.php',
     'PhabricatorDashboardTransactionEditor' => 'applications/dashboard/editor/PhabricatorDashboardTransactionEditor.php',
     'PhabricatorDashboardTransactionQuery' => 'applications/dashboard/query/PhabricatorDashboardTransactionQuery.php',
     'PhabricatorDashboardUninstallController' => 'applications/dashboard/controller/PhabricatorDashboardUninstallController.php',
     'PhabricatorDashboardViewController' => 'applications/dashboard/controller/PhabricatorDashboardViewController.php',
     'PhabricatorDataCacheSpec' => 'applications/cache/spec/PhabricatorDataCacheSpec.php',
     'PhabricatorDataNotAttachedException' => 'infrastructure/storage/lisk/PhabricatorDataNotAttachedException.php',
     'PhabricatorDatabaseHealthRecord' => 'infrastructure/cluster/PhabricatorDatabaseHealthRecord.php',
     'PhabricatorDatabaseRef' => 'infrastructure/cluster/PhabricatorDatabaseRef.php',
     'PhabricatorDatabaseSetupCheck' => 'applications/config/check/PhabricatorDatabaseSetupCheck.php',
     'PhabricatorDatasourceEditField' => 'applications/transactions/editfield/PhabricatorDatasourceEditField.php',
     'PhabricatorDatasourceEditType' => 'applications/transactions/edittype/PhabricatorDatasourceEditType.php',
     'PhabricatorDateTimeSettingsPanel' => 'applications/settings/panel/PhabricatorDateTimeSettingsPanel.php',
     'PhabricatorDebugController' => 'applications/system/controller/PhabricatorDebugController.php',
     'PhabricatorDefaultRequestExceptionHandler' => 'aphront/handler/PhabricatorDefaultRequestExceptionHandler.php',
+    'PhabricatorDefaultSyntaxStyle' => 'infrastructure/syntax/PhabricatorDefaultSyntaxStyle.php',
     'PhabricatorDesktopNotificationsSettingsPanel' => 'applications/settings/panel/PhabricatorDesktopNotificationsSettingsPanel.php',
     'PhabricatorDestructibleInterface' => 'applications/system/interface/PhabricatorDestructibleInterface.php',
     'PhabricatorDestructionEngine' => 'applications/system/engine/PhabricatorDestructionEngine.php',
     'PhabricatorDestructionEngineExtension' => 'applications/system/engine/PhabricatorDestructionEngineExtension.php',
     'PhabricatorDestructionEngineExtensionModule' => 'applications/system/engine/PhabricatorDestructionEngineExtensionModule.php',
     'PhabricatorDeveloperConfigOptions' => 'applications/config/option/PhabricatorDeveloperConfigOptions.php',
     'PhabricatorDeveloperPreferencesSettingsPanel' => 'applications/settings/panel/PhabricatorDeveloperPreferencesSettingsPanel.php',
     'PhabricatorDiffInlineCommentQuery' => 'infrastructure/diff/query/PhabricatorDiffInlineCommentQuery.php',
     'PhabricatorDiffPreferencesSettingsPanel' => 'applications/settings/panel/PhabricatorDiffPreferencesSettingsPanel.php',
     'PhabricatorDifferenceEngine' => 'infrastructure/diff/PhabricatorDifferenceEngine.php',
     'PhabricatorDifferentialApplication' => 'applications/differential/application/PhabricatorDifferentialApplication.php',
     'PhabricatorDifferentialAttachCommitWorkflow' => 'applications/differential/management/PhabricatorDifferentialAttachCommitWorkflow.php',
     'PhabricatorDifferentialConfigOptions' => 'applications/differential/config/PhabricatorDifferentialConfigOptions.php',
     'PhabricatorDifferentialExtractWorkflow' => 'applications/differential/management/PhabricatorDifferentialExtractWorkflow.php',
     'PhabricatorDifferentialManagementWorkflow' => 'applications/differential/management/PhabricatorDifferentialManagementWorkflow.php',
     'PhabricatorDifferentialRevisionTestDataGenerator' => 'applications/differential/lipsum/PhabricatorDifferentialRevisionTestDataGenerator.php',
     'PhabricatorDiffusionApplication' => 'applications/diffusion/application/PhabricatorDiffusionApplication.php',
     'PhabricatorDiffusionConfigOptions' => 'applications/diffusion/config/PhabricatorDiffusionConfigOptions.php',
     'PhabricatorDisabledUserController' => 'applications/auth/controller/PhabricatorDisabledUserController.php',
     'PhabricatorDisplayPreferencesSettingsPanel' => 'applications/settings/panel/PhabricatorDisplayPreferencesSettingsPanel.php',
     'PhabricatorDisqusAuthProvider' => 'applications/auth/provider/PhabricatorDisqusAuthProvider.php',
     'PhabricatorDividerProfilePanel' => 'applications/search/profilepanel/PhabricatorDividerProfilePanel.php',
     'PhabricatorDivinerApplication' => 'applications/diviner/application/PhabricatorDivinerApplication.php',
     'PhabricatorDoorkeeperApplication' => 'applications/doorkeeper/application/PhabricatorDoorkeeperApplication.php',
     'PhabricatorDraft' => 'applications/draft/storage/PhabricatorDraft.php',
     'PhabricatorDraftDAO' => 'applications/draft/storage/PhabricatorDraftDAO.php',
     'PhabricatorDrydockApplication' => 'applications/drydock/application/PhabricatorDrydockApplication.php',
     'PhabricatorEdgeConfig' => 'infrastructure/edges/constants/PhabricatorEdgeConfig.php',
     'PhabricatorEdgeConstants' => 'infrastructure/edges/constants/PhabricatorEdgeConstants.php',
     'PhabricatorEdgeCycleException' => 'infrastructure/edges/exception/PhabricatorEdgeCycleException.php',
     'PhabricatorEdgeEditType' => 'applications/transactions/edittype/PhabricatorEdgeEditType.php',
     'PhabricatorEdgeEditor' => 'infrastructure/edges/editor/PhabricatorEdgeEditor.php',
     'PhabricatorEdgeGraph' => 'infrastructure/edges/util/PhabricatorEdgeGraph.php',
     'PhabricatorEdgeQuery' => 'infrastructure/edges/query/PhabricatorEdgeQuery.php',
     'PhabricatorEdgeTestCase' => 'infrastructure/edges/__tests__/PhabricatorEdgeTestCase.php',
     'PhabricatorEdgeType' => 'infrastructure/edges/type/PhabricatorEdgeType.php',
     'PhabricatorEdgeTypeTestCase' => 'infrastructure/edges/type/__tests__/PhabricatorEdgeTypeTestCase.php',
     'PhabricatorEdgesDestructionEngineExtension' => 'infrastructure/edges/engineextension/PhabricatorEdgesDestructionEngineExtension.php',
     'PhabricatorEditEngine' => 'applications/transactions/editengine/PhabricatorEditEngine.php',
     'PhabricatorEditEngineAPIMethod' => 'applications/transactions/editengine/PhabricatorEditEngineAPIMethod.php',
     'PhabricatorEditEngineColumnsCommentAction' => 'applications/transactions/commentaction/PhabricatorEditEngineColumnsCommentAction.php',
     'PhabricatorEditEngineCommentAction' => 'applications/transactions/commentaction/PhabricatorEditEngineCommentAction.php',
     'PhabricatorEditEngineConfiguration' => 'applications/transactions/storage/PhabricatorEditEngineConfiguration.php',
     'PhabricatorEditEngineConfigurationDefaultCreateController' => 'applications/transactions/controller/PhabricatorEditEngineConfigurationDefaultCreateController.php',
     'PhabricatorEditEngineConfigurationDefaultsController' => 'applications/transactions/controller/PhabricatorEditEngineConfigurationDefaultsController.php',
     'PhabricatorEditEngineConfigurationDisableController' => 'applications/transactions/controller/PhabricatorEditEngineConfigurationDisableController.php',
     'PhabricatorEditEngineConfigurationEditController' => 'applications/transactions/controller/PhabricatorEditEngineConfigurationEditController.php',
     'PhabricatorEditEngineConfigurationEditEngine' => 'applications/transactions/editor/PhabricatorEditEngineConfigurationEditEngine.php',
     'PhabricatorEditEngineConfigurationEditor' => 'applications/transactions/editor/PhabricatorEditEngineConfigurationEditor.php',
     'PhabricatorEditEngineConfigurationIsEditController' => 'applications/transactions/controller/PhabricatorEditEngineConfigurationIsEditController.php',
     'PhabricatorEditEngineConfigurationListController' => 'applications/transactions/controller/PhabricatorEditEngineConfigurationListController.php',
     'PhabricatorEditEngineConfigurationLockController' => 'applications/transactions/controller/PhabricatorEditEngineConfigurationLockController.php',
     'PhabricatorEditEngineConfigurationPHIDType' => 'applications/transactions/phid/PhabricatorEditEngineConfigurationPHIDType.php',
     'PhabricatorEditEngineConfigurationQuery' => 'applications/transactions/query/PhabricatorEditEngineConfigurationQuery.php',
     'PhabricatorEditEngineConfigurationReorderController' => 'applications/transactions/controller/PhabricatorEditEngineConfigurationReorderController.php',
     'PhabricatorEditEngineConfigurationSaveController' => 'applications/transactions/controller/PhabricatorEditEngineConfigurationSaveController.php',
     'PhabricatorEditEngineConfigurationSearchEngine' => 'applications/transactions/query/PhabricatorEditEngineConfigurationSearchEngine.php',
     'PhabricatorEditEngineConfigurationSortController' => 'applications/transactions/controller/PhabricatorEditEngineConfigurationSortController.php',
     'PhabricatorEditEngineConfigurationTransaction' => 'applications/transactions/storage/PhabricatorEditEngineConfigurationTransaction.php',
     'PhabricatorEditEngineConfigurationTransactionQuery' => 'applications/transactions/query/PhabricatorEditEngineConfigurationTransactionQuery.php',
     'PhabricatorEditEngineConfigurationViewController' => 'applications/transactions/controller/PhabricatorEditEngineConfigurationViewController.php',
     'PhabricatorEditEngineController' => 'applications/transactions/controller/PhabricatorEditEngineController.php',
     'PhabricatorEditEngineExtension' => 'applications/transactions/engineextension/PhabricatorEditEngineExtension.php',
     'PhabricatorEditEngineExtensionModule' => 'applications/transactions/engineextension/PhabricatorEditEngineExtensionModule.php',
     'PhabricatorEditEngineListController' => 'applications/transactions/controller/PhabricatorEditEngineListController.php',
     'PhabricatorEditEnginePointsCommentAction' => 'applications/transactions/commentaction/PhabricatorEditEnginePointsCommentAction.php',
     'PhabricatorEditEngineQuery' => 'applications/transactions/query/PhabricatorEditEngineQuery.php',
     'PhabricatorEditEngineSearchEngine' => 'applications/transactions/query/PhabricatorEditEngineSearchEngine.php',
     'PhabricatorEditEngineSelectCommentAction' => 'applications/transactions/commentaction/PhabricatorEditEngineSelectCommentAction.php',
     'PhabricatorEditEngineTokenizerCommentAction' => 'applications/transactions/commentaction/PhabricatorEditEngineTokenizerCommentAction.php',
     'PhabricatorEditField' => 'applications/transactions/editfield/PhabricatorEditField.php',
+    'PhabricatorEditPage' => 'applications/transactions/editengine/PhabricatorEditPage.php',
     'PhabricatorEditType' => 'applications/transactions/edittype/PhabricatorEditType.php',
     'PhabricatorEditor' => 'infrastructure/PhabricatorEditor.php',
     'PhabricatorElasticFulltextStorageEngine' => 'applications/search/fulltextstorage/PhabricatorElasticFulltextStorageEngine.php',
     'PhabricatorElasticSearchSetupCheck' => 'applications/config/check/PhabricatorElasticSearchSetupCheck.php',
     'PhabricatorEmailAddressesSettingsPanel' => 'applications/settings/panel/PhabricatorEmailAddressesSettingsPanel.php',
     'PhabricatorEmailContentSource' => 'applications/metamta/contentsource/PhabricatorEmailContentSource.php',
     'PhabricatorEmailFormatSettingsPanel' => 'applications/settings/panel/PhabricatorEmailFormatSettingsPanel.php',
     'PhabricatorEmailLoginController' => 'applications/auth/controller/PhabricatorEmailLoginController.php',
     'PhabricatorEmailPreferencesSettingsPanel' => 'applications/settings/panel/PhabricatorEmailPreferencesSettingsPanel.php',
     'PhabricatorEmailVerificationController' => 'applications/auth/controller/PhabricatorEmailVerificationController.php',
     'PhabricatorEmbedFileRemarkupRule' => 'applications/files/markup/PhabricatorEmbedFileRemarkupRule.php',
     'PhabricatorEmojiRemarkupRule' => 'applications/macro/markup/PhabricatorEmojiRemarkupRule.php',
     'PhabricatorEmptyQueryException' => 'infrastructure/query/PhabricatorEmptyQueryException.php',
     'PhabricatorEnv' => 'infrastructure/env/PhabricatorEnv.php',
     'PhabricatorEnvTestCase' => 'infrastructure/env/__tests__/PhabricatorEnvTestCase.php',
     'PhabricatorEpochEditField' => 'applications/transactions/editfield/PhabricatorEpochEditField.php',
     'PhabricatorEvent' => 'infrastructure/events/PhabricatorEvent.php',
     'PhabricatorEventEngine' => 'infrastructure/events/PhabricatorEventEngine.php',
     'PhabricatorEventListener' => 'infrastructure/events/PhabricatorEventListener.php',
     'PhabricatorEventType' => 'infrastructure/events/constant/PhabricatorEventType.php',
     'PhabricatorExampleEventListener' => 'infrastructure/events/PhabricatorExampleEventListener.php',
     'PhabricatorExecFutureFileUploadSource' => 'applications/files/uploadsource/PhabricatorExecFutureFileUploadSource.php',
     'PhabricatorExtendedPolicyInterface' => 'applications/policy/interface/PhabricatorExtendedPolicyInterface.php',
     'PhabricatorExtendingPhabricatorConfigOptions' => 'applications/config/option/PhabricatorExtendingPhabricatorConfigOptions.php',
     'PhabricatorExtensionsSetupCheck' => 'applications/config/check/PhabricatorExtensionsSetupCheck.php',
     'PhabricatorExternalAccount' => 'applications/people/storage/PhabricatorExternalAccount.php',
     'PhabricatorExternalAccountQuery' => 'applications/auth/query/PhabricatorExternalAccountQuery.php',
     'PhabricatorExternalAccountsSettingsPanel' => 'applications/settings/panel/PhabricatorExternalAccountsSettingsPanel.php',
     'PhabricatorExtraConfigSetupCheck' => 'applications/config/check/PhabricatorExtraConfigSetupCheck.php',
     'PhabricatorFacebookAuthProvider' => 'applications/auth/provider/PhabricatorFacebookAuthProvider.php',
     'PhabricatorFactAggregate' => 'applications/fact/storage/PhabricatorFactAggregate.php',
     'PhabricatorFactApplication' => 'applications/fact/application/PhabricatorFactApplication.php',
     'PhabricatorFactChartController' => 'applications/fact/controller/PhabricatorFactChartController.php',
     'PhabricatorFactController' => 'applications/fact/controller/PhabricatorFactController.php',
     'PhabricatorFactCountEngine' => 'applications/fact/engine/PhabricatorFactCountEngine.php',
     'PhabricatorFactCursor' => 'applications/fact/storage/PhabricatorFactCursor.php',
     'PhabricatorFactDAO' => 'applications/fact/storage/PhabricatorFactDAO.php',
     'PhabricatorFactDaemon' => 'applications/fact/daemon/PhabricatorFactDaemon.php',
     'PhabricatorFactEngine' => 'applications/fact/engine/PhabricatorFactEngine.php',
     'PhabricatorFactEngineTestCase' => 'applications/fact/engine/__tests__/PhabricatorFactEngineTestCase.php',
     'PhabricatorFactHomeController' => 'applications/fact/controller/PhabricatorFactHomeController.php',
     'PhabricatorFactLastUpdatedEngine' => 'applications/fact/engine/PhabricatorFactLastUpdatedEngine.php',
     'PhabricatorFactManagementAnalyzeWorkflow' => 'applications/fact/management/PhabricatorFactManagementAnalyzeWorkflow.php',
     'PhabricatorFactManagementCursorsWorkflow' => 'applications/fact/management/PhabricatorFactManagementCursorsWorkflow.php',
     'PhabricatorFactManagementDestroyWorkflow' => 'applications/fact/management/PhabricatorFactManagementDestroyWorkflow.php',
     'PhabricatorFactManagementListWorkflow' => 'applications/fact/management/PhabricatorFactManagementListWorkflow.php',
     'PhabricatorFactManagementStatusWorkflow' => 'applications/fact/management/PhabricatorFactManagementStatusWorkflow.php',
     'PhabricatorFactManagementWorkflow' => 'applications/fact/management/PhabricatorFactManagementWorkflow.php',
     'PhabricatorFactRaw' => 'applications/fact/storage/PhabricatorFactRaw.php',
     'PhabricatorFactSimpleSpec' => 'applications/fact/spec/PhabricatorFactSimpleSpec.php',
     'PhabricatorFactSpec' => 'applications/fact/spec/PhabricatorFactSpec.php',
     'PhabricatorFactUpdateIterator' => 'applications/fact/extract/PhabricatorFactUpdateIterator.php',
     'PhabricatorFaxContentSource' => 'infrastructure/contentsource/PhabricatorFaxContentSource.php',
     'PhabricatorFeedApplication' => 'applications/feed/application/PhabricatorFeedApplication.php',
     'PhabricatorFeedBuilder' => 'applications/feed/builder/PhabricatorFeedBuilder.php',
     'PhabricatorFeedConfigOptions' => 'applications/feed/config/PhabricatorFeedConfigOptions.php',
     'PhabricatorFeedController' => 'applications/feed/controller/PhabricatorFeedController.php',
     'PhabricatorFeedDAO' => 'applications/feed/storage/PhabricatorFeedDAO.php',
     'PhabricatorFeedDetailController' => 'applications/feed/controller/PhabricatorFeedDetailController.php',
     'PhabricatorFeedListController' => 'applications/feed/controller/PhabricatorFeedListController.php',
     'PhabricatorFeedManagementRepublishWorkflow' => 'applications/feed/management/PhabricatorFeedManagementRepublishWorkflow.php',
     'PhabricatorFeedManagementWorkflow' => 'applications/feed/management/PhabricatorFeedManagementWorkflow.php',
     'PhabricatorFeedQuery' => 'applications/feed/query/PhabricatorFeedQuery.php',
     'PhabricatorFeedSearchEngine' => 'applications/feed/query/PhabricatorFeedSearchEngine.php',
     'PhabricatorFeedStory' => 'applications/feed/story/PhabricatorFeedStory.php',
     'PhabricatorFeedStoryData' => 'applications/feed/storage/PhabricatorFeedStoryData.php',
     'PhabricatorFeedStoryNotification' => 'applications/notification/storage/PhabricatorFeedStoryNotification.php',
     'PhabricatorFeedStoryPublisher' => 'applications/feed/PhabricatorFeedStoryPublisher.php',
     'PhabricatorFeedStoryReference' => 'applications/feed/storage/PhabricatorFeedStoryReference.php',
     'PhabricatorFile' => 'applications/files/storage/PhabricatorFile.php',
     'PhabricatorFileBundleLoader' => 'applications/files/query/PhabricatorFileBundleLoader.php',
     'PhabricatorFileChunk' => 'applications/files/storage/PhabricatorFileChunk.php',
     'PhabricatorFileChunkIterator' => 'applications/files/engine/PhabricatorFileChunkIterator.php',
     'PhabricatorFileChunkQuery' => 'applications/files/query/PhabricatorFileChunkQuery.php',
     'PhabricatorFileCommentController' => 'applications/files/controller/PhabricatorFileCommentController.php',
     'PhabricatorFileComposeController' => 'applications/files/controller/PhabricatorFileComposeController.php',
     'PhabricatorFileController' => 'applications/files/controller/PhabricatorFileController.php',
     'PhabricatorFileDAO' => 'applications/files/storage/PhabricatorFileDAO.php',
     'PhabricatorFileDataController' => 'applications/files/controller/PhabricatorFileDataController.php',
     'PhabricatorFileDeleteController' => 'applications/files/controller/PhabricatorFileDeleteController.php',
     'PhabricatorFileDropUploadController' => 'applications/files/controller/PhabricatorFileDropUploadController.php',
     'PhabricatorFileEditController' => 'applications/files/controller/PhabricatorFileEditController.php',
     'PhabricatorFileEditor' => 'applications/files/editor/PhabricatorFileEditor.php',
     'PhabricatorFileFilePHIDType' => 'applications/files/phid/PhabricatorFileFilePHIDType.php',
     'PhabricatorFileHasObjectEdgeType' => 'applications/files/edge/PhabricatorFileHasObjectEdgeType.php',
     'PhabricatorFileIconSetSelectController' => 'applications/files/controller/PhabricatorFileIconSetSelectController.php',
     'PhabricatorFileImageMacro' => 'applications/macro/storage/PhabricatorFileImageMacro.php',
     'PhabricatorFileImageTransform' => 'applications/files/transform/PhabricatorFileImageTransform.php',
     'PhabricatorFileInfoController' => 'applications/files/controller/PhabricatorFileInfoController.php',
     'PhabricatorFileLinkView' => 'view/layout/PhabricatorFileLinkView.php',
     'PhabricatorFileListController' => 'applications/files/controller/PhabricatorFileListController.php',
     'PhabricatorFileQuery' => 'applications/files/query/PhabricatorFileQuery.php',
     'PhabricatorFileSchemaSpec' => 'applications/files/storage/PhabricatorFileSchemaSpec.php',
     'PhabricatorFileSearchEngine' => 'applications/files/query/PhabricatorFileSearchEngine.php',
     'PhabricatorFileStorageBlob' => 'applications/files/storage/PhabricatorFileStorageBlob.php',
     'PhabricatorFileStorageConfigurationException' => 'applications/files/exception/PhabricatorFileStorageConfigurationException.php',
     'PhabricatorFileStorageEngine' => 'applications/files/engine/PhabricatorFileStorageEngine.php',
     'PhabricatorFileStorageEngineTestCase' => 'applications/files/engine/__tests__/PhabricatorFileStorageEngineTestCase.php',
     'PhabricatorFileTemporaryGarbageCollector' => 'applications/files/garbagecollector/PhabricatorFileTemporaryGarbageCollector.php',
     'PhabricatorFileTestCase' => 'applications/files/storage/__tests__/PhabricatorFileTestCase.php',
     'PhabricatorFileTestDataGenerator' => 'applications/files/lipsum/PhabricatorFileTestDataGenerator.php',
     'PhabricatorFileThumbnailTransform' => 'applications/files/transform/PhabricatorFileThumbnailTransform.php',
     'PhabricatorFileTransaction' => 'applications/files/storage/PhabricatorFileTransaction.php',
     'PhabricatorFileTransactionComment' => 'applications/files/storage/PhabricatorFileTransactionComment.php',
     'PhabricatorFileTransactionQuery' => 'applications/files/query/PhabricatorFileTransactionQuery.php',
     'PhabricatorFileTransform' => 'applications/files/transform/PhabricatorFileTransform.php',
     'PhabricatorFileTransformController' => 'applications/files/controller/PhabricatorFileTransformController.php',
     'PhabricatorFileTransformListController' => 'applications/files/controller/PhabricatorFileTransformListController.php',
     'PhabricatorFileTransformTestCase' => 'applications/files/transform/__tests__/PhabricatorFileTransformTestCase.php',
     'PhabricatorFileUploadController' => 'applications/files/controller/PhabricatorFileUploadController.php',
     'PhabricatorFileUploadDialogController' => 'applications/files/controller/PhabricatorFileUploadDialogController.php',
     'PhabricatorFileUploadException' => 'applications/files/exception/PhabricatorFileUploadException.php',
     'PhabricatorFileUploadSource' => 'applications/files/uploadsource/PhabricatorFileUploadSource.php',
     'PhabricatorFileinfoSetupCheck' => 'applications/config/check/PhabricatorFileinfoSetupCheck.php',
     'PhabricatorFilesApplication' => 'applications/files/application/PhabricatorFilesApplication.php',
     'PhabricatorFilesApplicationStorageEnginePanel' => 'applications/files/applicationpanel/PhabricatorFilesApplicationStorageEnginePanel.php',
     'PhabricatorFilesBuiltinFile' => 'applications/files/builtin/PhabricatorFilesBuiltinFile.php',
     'PhabricatorFilesComposeIconBuiltinFile' => 'applications/files/builtin/PhabricatorFilesComposeIconBuiltinFile.php',
     'PhabricatorFilesConfigOptions' => 'applications/files/config/PhabricatorFilesConfigOptions.php',
     'PhabricatorFilesManagementCatWorkflow' => 'applications/files/management/PhabricatorFilesManagementCatWorkflow.php',
     'PhabricatorFilesManagementCompactWorkflow' => 'applications/files/management/PhabricatorFilesManagementCompactWorkflow.php',
     'PhabricatorFilesManagementEnginesWorkflow' => 'applications/files/management/PhabricatorFilesManagementEnginesWorkflow.php',
     'PhabricatorFilesManagementMigrateWorkflow' => 'applications/files/management/PhabricatorFilesManagementMigrateWorkflow.php',
     'PhabricatorFilesManagementPurgeWorkflow' => 'applications/files/management/PhabricatorFilesManagementPurgeWorkflow.php',
     'PhabricatorFilesManagementRebuildWorkflow' => 'applications/files/management/PhabricatorFilesManagementRebuildWorkflow.php',
     'PhabricatorFilesManagementWorkflow' => 'applications/files/management/PhabricatorFilesManagementWorkflow.php',
     'PhabricatorFilesOnDiskBuiltinFile' => 'applications/files/builtin/PhabricatorFilesOnDiskBuiltinFile.php',
     'PhabricatorFilesOutboundRequestAction' => 'applications/files/action/PhabricatorFilesOutboundRequestAction.php',
     'PhabricatorFlag' => 'applications/flag/storage/PhabricatorFlag.php',
     'PhabricatorFlagAddFlagHeraldAction' => 'applications/flag/herald/PhabricatorFlagAddFlagHeraldAction.php',
     'PhabricatorFlagColor' => 'applications/flag/constants/PhabricatorFlagColor.php',
     'PhabricatorFlagConstants' => 'applications/flag/constants/PhabricatorFlagConstants.php',
     'PhabricatorFlagController' => 'applications/flag/controller/PhabricatorFlagController.php',
     'PhabricatorFlagDAO' => 'applications/flag/storage/PhabricatorFlagDAO.php',
     'PhabricatorFlagDeleteController' => 'applications/flag/controller/PhabricatorFlagDeleteController.php',
     'PhabricatorFlagDestructionEngineExtension' => 'applications/flag/engineextension/PhabricatorFlagDestructionEngineExtension.php',
     'PhabricatorFlagEditController' => 'applications/flag/controller/PhabricatorFlagEditController.php',
     'PhabricatorFlagListController' => 'applications/flag/controller/PhabricatorFlagListController.php',
     'PhabricatorFlagQuery' => 'applications/flag/query/PhabricatorFlagQuery.php',
     'PhabricatorFlagSearchEngine' => 'applications/flag/query/PhabricatorFlagSearchEngine.php',
     'PhabricatorFlagSelectControl' => 'applications/flag/view/PhabricatorFlagSelectControl.php',
     'PhabricatorFlaggableInterface' => 'applications/flag/interface/PhabricatorFlaggableInterface.php',
     'PhabricatorFlagsApplication' => 'applications/flag/application/PhabricatorFlagsApplication.php',
     'PhabricatorFlagsUIEventListener' => 'applications/flag/events/PhabricatorFlagsUIEventListener.php',
     'PhabricatorFulltextEngine' => 'applications/search/index/PhabricatorFulltextEngine.php',
     'PhabricatorFulltextEngineExtension' => 'applications/search/index/PhabricatorFulltextEngineExtension.php',
     'PhabricatorFulltextEngineExtensionModule' => 'applications/search/index/PhabricatorFulltextEngineExtensionModule.php',
     'PhabricatorFulltextIndexEngineExtension' => 'applications/search/engineextension/PhabricatorFulltextIndexEngineExtension.php',
     'PhabricatorFulltextInterface' => 'applications/search/interface/PhabricatorFulltextInterface.php',
     'PhabricatorFulltextStorageEngine' => 'applications/search/fulltextstorage/PhabricatorFulltextStorageEngine.php',
     'PhabricatorFundApplication' => 'applications/fund/application/PhabricatorFundApplication.php',
     'PhabricatorGDSetupCheck' => 'applications/config/check/PhabricatorGDSetupCheck.php',
     'PhabricatorGarbageCollector' => 'infrastructure/daemon/garbagecollector/PhabricatorGarbageCollector.php',
     'PhabricatorGarbageCollectorManagementCollectWorkflow' => 'infrastructure/daemon/garbagecollector/management/PhabricatorGarbageCollectorManagementCollectWorkflow.php',
     'PhabricatorGarbageCollectorManagementSetPolicyWorkflow' => 'infrastructure/daemon/garbagecollector/management/PhabricatorGarbageCollectorManagementSetPolicyWorkflow.php',
     'PhabricatorGarbageCollectorManagementWorkflow' => 'infrastructure/daemon/garbagecollector/management/PhabricatorGarbageCollectorManagementWorkflow.php',
     'PhabricatorGestureUIExample' => 'applications/uiexample/examples/PhabricatorGestureUIExample.php',
     'PhabricatorGitGraphStream' => 'applications/repository/daemon/PhabricatorGitGraphStream.php',
     'PhabricatorGitHubAuthProvider' => 'applications/auth/provider/PhabricatorGitHubAuthProvider.php',
     'PhabricatorGlobalLock' => 'infrastructure/util/PhabricatorGlobalLock.php',
     'PhabricatorGlobalUploadTargetView' => 'applications/files/view/PhabricatorGlobalUploadTargetView.php',
     'PhabricatorGoogleAuthProvider' => 'applications/auth/provider/PhabricatorGoogleAuthProvider.php',
     'PhabricatorHTTPParameterTypeTableView' => 'applications/config/view/PhabricatorHTTPParameterTypeTableView.php',
     'PhabricatorHandleList' => 'applications/phid/handle/pool/PhabricatorHandleList.php',
     'PhabricatorHandleObjectSelectorDataView' => 'applications/phid/handle/view/PhabricatorHandleObjectSelectorDataView.php',
     'PhabricatorHandlePool' => 'applications/phid/handle/pool/PhabricatorHandlePool.php',
     'PhabricatorHandlePoolTestCase' => 'applications/phid/handle/pool/__tests__/PhabricatorHandlePoolTestCase.php',
     'PhabricatorHandleQuery' => 'applications/phid/query/PhabricatorHandleQuery.php',
     'PhabricatorHandleRemarkupRule' => 'applications/phid/remarkup/PhabricatorHandleRemarkupRule.php',
     'PhabricatorHandlesEditField' => 'applications/transactions/editfield/PhabricatorHandlesEditField.php',
     'PhabricatorHarbormasterApplication' => 'applications/harbormaster/application/PhabricatorHarbormasterApplication.php',
     'PhabricatorHarbormasterConfigOptions' => 'applications/harbormaster/config/PhabricatorHarbormasterConfigOptions.php',
     'PhabricatorHash' => 'infrastructure/util/PhabricatorHash.php',
     'PhabricatorHashTestCase' => 'infrastructure/util/__tests__/PhabricatorHashTestCase.php',
     'PhabricatorHelpApplication' => 'applications/help/application/PhabricatorHelpApplication.php',
     'PhabricatorHelpController' => 'applications/help/controller/PhabricatorHelpController.php',
     'PhabricatorHelpDocumentationController' => 'applications/help/controller/PhabricatorHelpDocumentationController.php',
     'PhabricatorHelpEditorProtocolController' => 'applications/help/controller/PhabricatorHelpEditorProtocolController.php',
     'PhabricatorHelpKeyboardShortcutController' => 'applications/help/controller/PhabricatorHelpKeyboardShortcutController.php',
     'PhabricatorHelpMainMenuBarExtension' => 'applications/help/extension/PhabricatorHelpMainMenuBarExtension.php',
     'PhabricatorHeraldApplication' => 'applications/herald/application/PhabricatorHeraldApplication.php',
     'PhabricatorHeraldContentSource' => 'applications/herald/contentsource/PhabricatorHeraldContentSource.php',
     'PhabricatorHighSecurityRequestExceptionHandler' => 'aphront/handler/PhabricatorHighSecurityRequestExceptionHandler.php',
     'PhabricatorHomeApplication' => 'applications/home/application/PhabricatorHomeApplication.php',
     'PhabricatorHomeController' => 'applications/home/controller/PhabricatorHomeController.php',
     'PhabricatorHomeMainController' => 'applications/home/controller/PhabricatorHomeMainController.php',
     'PhabricatorHomePreferencesSettingsPanel' => 'applications/settings/panel/PhabricatorHomePreferencesSettingsPanel.php',
     'PhabricatorHomeQuickCreateController' => 'applications/home/controller/PhabricatorHomeQuickCreateController.php',
     'PhabricatorHovercardEngineExtension' => 'applications/search/engineextension/PhabricatorHovercardEngineExtension.php',
     'PhabricatorHovercardEngineExtensionModule' => 'applications/search/engineextension/PhabricatorHovercardEngineExtensionModule.php',
     'PhabricatorHunksManagementMigrateWorkflow' => 'applications/differential/management/PhabricatorHunksManagementMigrateWorkflow.php',
     'PhabricatorHunksManagementWorkflow' => 'applications/differential/management/PhabricatorHunksManagementWorkflow.php',
     'PhabricatorIDsSearchEngineExtension' => 'applications/search/engineextension/PhabricatorIDsSearchEngineExtension.php',
     'PhabricatorIDsSearchField' => 'applications/search/field/PhabricatorIDsSearchField.php',
     'PhabricatorIRCProtocolAdapter' => 'infrastructure/daemon/bot/adapter/PhabricatorIRCProtocolAdapter.php',
     'PhabricatorIconRemarkupRule' => 'applications/macro/markup/PhabricatorIconRemarkupRule.php',
     'PhabricatorIconSet' => 'applications/files/iconset/PhabricatorIconSet.php',
     'PhabricatorIconSetEditField' => 'applications/transactions/editfield/PhabricatorIconSetEditField.php',
     'PhabricatorIconSetIcon' => 'applications/files/iconset/PhabricatorIconSetIcon.php',
     'PhabricatorImageMacroRemarkupRule' => 'applications/macro/markup/PhabricatorImageMacroRemarkupRule.php',
     'PhabricatorImageTransformer' => 'applications/files/PhabricatorImageTransformer.php',
     'PhabricatorImagemagickSetupCheck' => 'applications/config/check/PhabricatorImagemagickSetupCheck.php',
     'PhabricatorIndexEngine' => 'applications/search/index/PhabricatorIndexEngine.php',
     'PhabricatorIndexEngineExtension' => 'applications/search/index/PhabricatorIndexEngineExtension.php',
     'PhabricatorIndexEngineExtensionModule' => 'applications/search/index/PhabricatorIndexEngineExtensionModule.php',
     'PhabricatorInfrastructureTestCase' => '__tests__/PhabricatorInfrastructureTestCase.php',
     'PhabricatorInlineCommentController' => 'infrastructure/diff/PhabricatorInlineCommentController.php',
     'PhabricatorInlineCommentInterface' => 'infrastructure/diff/interface/PhabricatorInlineCommentInterface.php',
     'PhabricatorInlineCommentPreviewController' => 'infrastructure/diff/PhabricatorInlineCommentPreviewController.php',
     'PhabricatorInlineSummaryView' => 'infrastructure/diff/view/PhabricatorInlineSummaryView.php',
     'PhabricatorInstructionsEditField' => 'applications/transactions/editfield/PhabricatorInstructionsEditField.php',
     'PhabricatorInternationalizationManagementExtractWorkflow' => 'infrastructure/internationalization/management/PhabricatorInternationalizationManagementExtractWorkflow.php',
     'PhabricatorInternationalizationManagementWorkflow' => 'infrastructure/internationalization/management/PhabricatorInternationalizationManagementWorkflow.php',
     'PhabricatorInvalidConfigSetupCheck' => 'applications/config/check/PhabricatorInvalidConfigSetupCheck.php',
     'PhabricatorIteratedMD5PasswordHasher' => 'infrastructure/util/password/PhabricatorIteratedMD5PasswordHasher.php',
     'PhabricatorIteratedMD5PasswordHasherTestCase' => 'infrastructure/util/password/__tests__/PhabricatorIteratedMD5PasswordHasherTestCase.php',
     'PhabricatorIteratorFileUploadSource' => 'applications/files/uploadsource/PhabricatorIteratorFileUploadSource.php',
     'PhabricatorJIRAAuthProvider' => 'applications/auth/provider/PhabricatorJIRAAuthProvider.php',
     'PhabricatorJavelinLinter' => 'infrastructure/lint/linter/PhabricatorJavelinLinter.php',
     'PhabricatorJiraIssueHasObjectEdgeType' => 'applications/doorkeeper/edge/PhabricatorJiraIssueHasObjectEdgeType.php',
     'PhabricatorJumpNavHandler' => 'applications/search/engine/PhabricatorJumpNavHandler.php',
     'PhabricatorKeyValueDatabaseCache' => 'applications/cache/PhabricatorKeyValueDatabaseCache.php',
     'PhabricatorLDAPAuthProvider' => 'applications/auth/provider/PhabricatorLDAPAuthProvider.php',
     'PhabricatorLegalpadApplication' => 'applications/legalpad/application/PhabricatorLegalpadApplication.php',
     'PhabricatorLegalpadConfigOptions' => 'applications/legalpad/config/PhabricatorLegalpadConfigOptions.php',
     'PhabricatorLegalpadDocumentPHIDType' => 'applications/legalpad/phid/PhabricatorLegalpadDocumentPHIDType.php',
     'PhabricatorLegalpadSignaturePolicyRule' => 'applications/legalpad/policyrule/PhabricatorLegalpadSignaturePolicyRule.php',
     'PhabricatorLibraryTestCase' => '__tests__/PhabricatorLibraryTestCase.php',
     'PhabricatorLinkProfilePanel' => 'applications/search/profilepanel/PhabricatorLinkProfilePanel.php',
     'PhabricatorLipsumArtist' => 'applications/lipsum/image/PhabricatorLipsumArtist.php',
     'PhabricatorLipsumContentSource' => 'infrastructure/contentsource/PhabricatorLipsumContentSource.php',
     'PhabricatorLipsumGenerateWorkflow' => 'applications/lipsum/management/PhabricatorLipsumGenerateWorkflow.php',
     'PhabricatorLipsumManagementWorkflow' => 'applications/lipsum/management/PhabricatorLipsumManagementWorkflow.php',
     'PhabricatorLipsumMondrianArtist' => 'applications/lipsum/image/PhabricatorLipsumMondrianArtist.php',
     'PhabricatorLiskDAO' => 'infrastructure/storage/lisk/PhabricatorLiskDAO.php',
     'PhabricatorLiskFulltextEngineExtension' => 'applications/search/engineextension/PhabricatorLiskFulltextEngineExtension.php',
     'PhabricatorLiskSearchEngineExtension' => 'applications/search/engineextension/PhabricatorLiskSearchEngineExtension.php',
     'PhabricatorLiskSerializer' => 'infrastructure/storage/lisk/PhabricatorLiskSerializer.php',
     'PhabricatorListFilterUIExample' => 'applications/uiexample/examples/PhabricatorListFilterUIExample.php',
     'PhabricatorLocalDiskFileStorageEngine' => 'applications/files/engine/PhabricatorLocalDiskFileStorageEngine.php',
     'PhabricatorLocalTimeTestCase' => 'view/__tests__/PhabricatorLocalTimeTestCase.php',
     'PhabricatorLocaleScopeGuard' => 'infrastructure/internationalization/scope/PhabricatorLocaleScopeGuard.php',
     'PhabricatorLocaleScopeGuardTestCase' => 'infrastructure/internationalization/scope/__tests__/PhabricatorLocaleScopeGuardTestCase.php',
     'PhabricatorLogTriggerAction' => 'infrastructure/daemon/workers/action/PhabricatorLogTriggerAction.php',
     'PhabricatorLogoutController' => 'applications/auth/controller/PhabricatorLogoutController.php',
     'PhabricatorLunarPhasePolicyRule' => 'applications/policy/rule/PhabricatorLunarPhasePolicyRule.php',
     'PhabricatorMacroApplication' => 'applications/macro/application/PhabricatorMacroApplication.php',
     'PhabricatorMacroAudioController' => 'applications/macro/controller/PhabricatorMacroAudioController.php',
     'PhabricatorMacroCommentController' => 'applications/macro/controller/PhabricatorMacroCommentController.php',
     'PhabricatorMacroConfigOptions' => 'applications/macro/config/PhabricatorMacroConfigOptions.php',
     'PhabricatorMacroController' => 'applications/macro/controller/PhabricatorMacroController.php',
     'PhabricatorMacroDatasource' => 'applications/macro/typeahead/PhabricatorMacroDatasource.php',
     'PhabricatorMacroDisableController' => 'applications/macro/controller/PhabricatorMacroDisableController.php',
     'PhabricatorMacroEditController' => 'applications/macro/controller/PhabricatorMacroEditController.php',
     'PhabricatorMacroEditor' => 'applications/macro/editor/PhabricatorMacroEditor.php',
     'PhabricatorMacroListController' => 'applications/macro/controller/PhabricatorMacroListController.php',
     'PhabricatorMacroMacroPHIDType' => 'applications/macro/phid/PhabricatorMacroMacroPHIDType.php',
     'PhabricatorMacroMailReceiver' => 'applications/macro/mail/PhabricatorMacroMailReceiver.php',
     'PhabricatorMacroManageCapability' => 'applications/macro/capability/PhabricatorMacroManageCapability.php',
     'PhabricatorMacroMemeController' => 'applications/macro/controller/PhabricatorMacroMemeController.php',
     'PhabricatorMacroMemeDialogController' => 'applications/macro/controller/PhabricatorMacroMemeDialogController.php',
     'PhabricatorMacroQuery' => 'applications/macro/query/PhabricatorMacroQuery.php',
     'PhabricatorMacroReplyHandler' => 'applications/macro/mail/PhabricatorMacroReplyHandler.php',
     'PhabricatorMacroSearchEngine' => 'applications/macro/query/PhabricatorMacroSearchEngine.php',
     'PhabricatorMacroTransaction' => 'applications/macro/storage/PhabricatorMacroTransaction.php',
     'PhabricatorMacroTransactionComment' => 'applications/macro/storage/PhabricatorMacroTransactionComment.php',
     'PhabricatorMacroTransactionQuery' => 'applications/macro/query/PhabricatorMacroTransactionQuery.php',
     'PhabricatorMacroViewController' => 'applications/macro/controller/PhabricatorMacroViewController.php',
     'PhabricatorMailEmailHeraldField' => 'applications/metamta/herald/PhabricatorMailEmailHeraldField.php',
     'PhabricatorMailEmailHeraldFieldGroup' => 'applications/metamta/herald/PhabricatorMailEmailHeraldFieldGroup.php',
     'PhabricatorMailEmailSubjectHeraldField' => 'applications/metamta/herald/PhabricatorMailEmailSubjectHeraldField.php',
     'PhabricatorMailImplementationAdapter' => 'applications/metamta/adapter/PhabricatorMailImplementationAdapter.php',
     'PhabricatorMailImplementationAmazonSESAdapter' => 'applications/metamta/adapter/PhabricatorMailImplementationAmazonSESAdapter.php',
     'PhabricatorMailImplementationMailgunAdapter' => 'applications/metamta/adapter/PhabricatorMailImplementationMailgunAdapter.php',
     'PhabricatorMailImplementationPHPMailerAdapter' => 'applications/metamta/adapter/PhabricatorMailImplementationPHPMailerAdapter.php',
     'PhabricatorMailImplementationPHPMailerLiteAdapter' => 'applications/metamta/adapter/PhabricatorMailImplementationPHPMailerLiteAdapter.php',
     'PhabricatorMailImplementationSendGridAdapter' => 'applications/metamta/adapter/PhabricatorMailImplementationSendGridAdapter.php',
     'PhabricatorMailImplementationTestAdapter' => 'applications/metamta/adapter/PhabricatorMailImplementationTestAdapter.php',
     'PhabricatorMailManagementListInboundWorkflow' => 'applications/metamta/management/PhabricatorMailManagementListInboundWorkflow.php',
     'PhabricatorMailManagementListOutboundWorkflow' => 'applications/metamta/management/PhabricatorMailManagementListOutboundWorkflow.php',
     'PhabricatorMailManagementReceiveTestWorkflow' => 'applications/metamta/management/PhabricatorMailManagementReceiveTestWorkflow.php',
     'PhabricatorMailManagementResendWorkflow' => 'applications/metamta/management/PhabricatorMailManagementResendWorkflow.php',
     'PhabricatorMailManagementSendTestWorkflow' => 'applications/metamta/management/PhabricatorMailManagementSendTestWorkflow.php',
     'PhabricatorMailManagementShowInboundWorkflow' => 'applications/metamta/management/PhabricatorMailManagementShowInboundWorkflow.php',
     'PhabricatorMailManagementShowOutboundWorkflow' => 'applications/metamta/management/PhabricatorMailManagementShowOutboundWorkflow.php',
     'PhabricatorMailManagementVolumeWorkflow' => 'applications/metamta/management/PhabricatorMailManagementVolumeWorkflow.php',
     'PhabricatorMailManagementWorkflow' => 'applications/metamta/management/PhabricatorMailManagementWorkflow.php',
     'PhabricatorMailOutboundMailHeraldAdapter' => 'applications/metamta/herald/PhabricatorMailOutboundMailHeraldAdapter.php',
     'PhabricatorMailOutboundRoutingHeraldAction' => 'applications/metamta/herald/PhabricatorMailOutboundRoutingHeraldAction.php',
     'PhabricatorMailOutboundRoutingSelfEmailHeraldAction' => 'applications/metamta/herald/PhabricatorMailOutboundRoutingSelfEmailHeraldAction.php',
     'PhabricatorMailOutboundRoutingSelfNotificationHeraldAction' => 'applications/metamta/herald/PhabricatorMailOutboundRoutingSelfNotificationHeraldAction.php',
     'PhabricatorMailOutboundStatus' => 'applications/metamta/constants/PhabricatorMailOutboundStatus.php',
     'PhabricatorMailReceiver' => 'applications/metamta/receiver/PhabricatorMailReceiver.php',
     'PhabricatorMailReceiverTestCase' => 'applications/metamta/receiver/__tests__/PhabricatorMailReceiverTestCase.php',
     'PhabricatorMailReplyHandler' => 'applications/metamta/replyhandler/PhabricatorMailReplyHandler.php',
     'PhabricatorMailRoutingRule' => 'applications/metamta/constants/PhabricatorMailRoutingRule.php',
     'PhabricatorMailSetupCheck' => 'applications/config/check/PhabricatorMailSetupCheck.php',
     'PhabricatorMailTarget' => 'applications/metamta/replyhandler/PhabricatorMailTarget.php',
     'PhabricatorMailgunConfigOptions' => 'applications/config/option/PhabricatorMailgunConfigOptions.php',
     'PhabricatorMainMenuBarExtension' => 'view/page/menu/PhabricatorMainMenuBarExtension.php',
     'PhabricatorMainMenuSearchView' => 'view/page/menu/PhabricatorMainMenuSearchView.php',
     'PhabricatorMainMenuView' => 'view/page/menu/PhabricatorMainMenuView.php',
     'PhabricatorManagementWorkflow' => 'infrastructure/management/PhabricatorManagementWorkflow.php',
     'PhabricatorManiphestApplication' => 'applications/maniphest/application/PhabricatorManiphestApplication.php',
     'PhabricatorManiphestConfigOptions' => 'applications/maniphest/config/PhabricatorManiphestConfigOptions.php',
     'PhabricatorManiphestTaskTestDataGenerator' => 'applications/maniphest/lipsum/PhabricatorManiphestTaskTestDataGenerator.php',
     'PhabricatorMarkupCache' => 'applications/cache/storage/PhabricatorMarkupCache.php',
     'PhabricatorMarkupEngine' => 'infrastructure/markup/PhabricatorMarkupEngine.php',
     'PhabricatorMarkupInterface' => 'infrastructure/markup/PhabricatorMarkupInterface.php',
     'PhabricatorMarkupOneOff' => 'infrastructure/markup/PhabricatorMarkupOneOff.php',
     'PhabricatorMarkupPreviewController' => 'infrastructure/markup/PhabricatorMarkupPreviewController.php',
     'PhabricatorMemeRemarkupRule' => 'applications/macro/markup/PhabricatorMemeRemarkupRule.php',
     'PhabricatorMentionRemarkupRule' => 'applications/people/markup/PhabricatorMentionRemarkupRule.php',
     'PhabricatorMentionableInterface' => 'applications/transactions/interface/PhabricatorMentionableInterface.php',
     'PhabricatorMercurialGraphStream' => 'applications/repository/daemon/PhabricatorMercurialGraphStream.php',
     'PhabricatorMetaMTAActor' => 'applications/metamta/query/PhabricatorMetaMTAActor.php',
     'PhabricatorMetaMTAActorQuery' => 'applications/metamta/query/PhabricatorMetaMTAActorQuery.php',
     'PhabricatorMetaMTAApplication' => 'applications/metamta/application/PhabricatorMetaMTAApplication.php',
     'PhabricatorMetaMTAApplicationEmail' => 'applications/metamta/storage/PhabricatorMetaMTAApplicationEmail.php',
     'PhabricatorMetaMTAApplicationEmailDatasource' => 'applications/metamta/typeahead/PhabricatorMetaMTAApplicationEmailDatasource.php',
     'PhabricatorMetaMTAApplicationEmailEditor' => 'applications/metamta/editor/PhabricatorMetaMTAApplicationEmailEditor.php',
     'PhabricatorMetaMTAApplicationEmailHeraldField' => 'applications/metamta/herald/PhabricatorMetaMTAApplicationEmailHeraldField.php',
     'PhabricatorMetaMTAApplicationEmailPHIDType' => 'applications/phid/PhabricatorMetaMTAApplicationEmailPHIDType.php',
     'PhabricatorMetaMTAApplicationEmailPanel' => 'applications/metamta/applicationpanel/PhabricatorMetaMTAApplicationEmailPanel.php',
     'PhabricatorMetaMTAApplicationEmailQuery' => 'applications/metamta/query/PhabricatorMetaMTAApplicationEmailQuery.php',
     'PhabricatorMetaMTAApplicationEmailTransaction' => 'applications/metamta/storage/PhabricatorMetaMTAApplicationEmailTransaction.php',
     'PhabricatorMetaMTAApplicationEmailTransactionQuery' => 'applications/metamta/query/PhabricatorMetaMTAApplicationEmailTransactionQuery.php',
     'PhabricatorMetaMTAAttachment' => 'applications/metamta/storage/PhabricatorMetaMTAAttachment.php',
     'PhabricatorMetaMTAConfigOptions' => 'applications/config/option/PhabricatorMetaMTAConfigOptions.php',
     'PhabricatorMetaMTAController' => 'applications/metamta/controller/PhabricatorMetaMTAController.php',
     'PhabricatorMetaMTADAO' => 'applications/metamta/storage/PhabricatorMetaMTADAO.php',
     'PhabricatorMetaMTAEmailBodyParser' => 'applications/metamta/parser/PhabricatorMetaMTAEmailBodyParser.php',
     'PhabricatorMetaMTAEmailBodyParserTestCase' => 'applications/metamta/parser/__tests__/PhabricatorMetaMTAEmailBodyParserTestCase.php',
     'PhabricatorMetaMTAEmailHeraldAction' => 'applications/metamta/herald/PhabricatorMetaMTAEmailHeraldAction.php',
     'PhabricatorMetaMTAEmailOthersHeraldAction' => 'applications/metamta/herald/PhabricatorMetaMTAEmailOthersHeraldAction.php',
     'PhabricatorMetaMTAEmailSelfHeraldAction' => 'applications/metamta/herald/PhabricatorMetaMTAEmailSelfHeraldAction.php',
     'PhabricatorMetaMTAErrorMailAction' => 'applications/metamta/action/PhabricatorMetaMTAErrorMailAction.php',
     'PhabricatorMetaMTAMail' => 'applications/metamta/storage/PhabricatorMetaMTAMail.php',
     'PhabricatorMetaMTAMailBody' => 'applications/metamta/view/PhabricatorMetaMTAMailBody.php',
     'PhabricatorMetaMTAMailBodyTestCase' => 'applications/metamta/view/__tests__/PhabricatorMetaMTAMailBodyTestCase.php',
     'PhabricatorMetaMTAMailHasRecipientEdgeType' => 'applications/metamta/edge/PhabricatorMetaMTAMailHasRecipientEdgeType.php',
     'PhabricatorMetaMTAMailListController' => 'applications/metamta/controller/PhabricatorMetaMTAMailListController.php',
     'PhabricatorMetaMTAMailPHIDType' => 'applications/metamta/phid/PhabricatorMetaMTAMailPHIDType.php',
     'PhabricatorMetaMTAMailQuery' => 'applications/metamta/query/PhabricatorMetaMTAMailQuery.php',
     'PhabricatorMetaMTAMailSearchEngine' => 'applications/metamta/query/PhabricatorMetaMTAMailSearchEngine.php',
     'PhabricatorMetaMTAMailSection' => 'applications/metamta/view/PhabricatorMetaMTAMailSection.php',
     'PhabricatorMetaMTAMailTestCase' => 'applications/metamta/storage/__tests__/PhabricatorMetaMTAMailTestCase.php',
     'PhabricatorMetaMTAMailViewController' => 'applications/metamta/controller/PhabricatorMetaMTAMailViewController.php',
     'PhabricatorMetaMTAMailableDatasource' => 'applications/metamta/typeahead/PhabricatorMetaMTAMailableDatasource.php',
     'PhabricatorMetaMTAMailableFunctionDatasource' => 'applications/metamta/typeahead/PhabricatorMetaMTAMailableFunctionDatasource.php',
     'PhabricatorMetaMTAMailgunReceiveController' => 'applications/metamta/controller/PhabricatorMetaMTAMailgunReceiveController.php',
     'PhabricatorMetaMTAMemberQuery' => 'applications/metamta/query/PhabricatorMetaMTAMemberQuery.php',
     'PhabricatorMetaMTAPermanentFailureException' => 'applications/metamta/exception/PhabricatorMetaMTAPermanentFailureException.php',
     'PhabricatorMetaMTAReceivedMail' => 'applications/metamta/storage/PhabricatorMetaMTAReceivedMail.php',
     'PhabricatorMetaMTAReceivedMailProcessingException' => 'applications/metamta/exception/PhabricatorMetaMTAReceivedMailProcessingException.php',
     'PhabricatorMetaMTAReceivedMailTestCase' => 'applications/metamta/storage/__tests__/PhabricatorMetaMTAReceivedMailTestCase.php',
     'PhabricatorMetaMTASchemaSpec' => 'applications/metamta/storage/PhabricatorMetaMTASchemaSpec.php',
     'PhabricatorMetaMTASendGridReceiveController' => 'applications/metamta/controller/PhabricatorMetaMTASendGridReceiveController.php',
     'PhabricatorMetaMTAWorker' => 'applications/metamta/PhabricatorMetaMTAWorker.php',
     'PhabricatorMetronomicTriggerClock' => 'infrastructure/daemon/workers/clock/PhabricatorMetronomicTriggerClock.php',
     'PhabricatorMotivatorProfilePanel' => 'applications/search/profilepanel/PhabricatorMotivatorProfilePanel.php',
     'PhabricatorMultiColumnUIExample' => 'applications/uiexample/examples/PhabricatorMultiColumnUIExample.php',
     'PhabricatorMultiFactorSettingsPanel' => 'applications/settings/panel/PhabricatorMultiFactorSettingsPanel.php',
     'PhabricatorMultimeterApplication' => 'applications/multimeter/application/PhabricatorMultimeterApplication.php',
     'PhabricatorMustVerifyEmailController' => 'applications/auth/controller/PhabricatorMustVerifyEmailController.php',
     'PhabricatorMySQLConfigOptions' => 'applications/config/option/PhabricatorMySQLConfigOptions.php',
     'PhabricatorMySQLFileStorageEngine' => 'applications/files/engine/PhabricatorMySQLFileStorageEngine.php',
     'PhabricatorMySQLFulltextStorageEngine' => 'applications/search/fulltextstorage/PhabricatorMySQLFulltextStorageEngine.php',
     'PhabricatorMySQLSetupCheck' => 'applications/config/check/PhabricatorMySQLSetupCheck.php',
     'PhabricatorNamedQuery' => 'applications/search/storage/PhabricatorNamedQuery.php',
     'PhabricatorNamedQueryQuery' => 'applications/search/query/PhabricatorNamedQueryQuery.php',
     'PhabricatorNavigationRemarkupRule' => 'infrastructure/markup/rule/PhabricatorNavigationRemarkupRule.php',
     'PhabricatorNeverTriggerClock' => 'infrastructure/daemon/workers/clock/PhabricatorNeverTriggerClock.php',
     'PhabricatorNgramsIndexEngineExtension' => 'applications/search/engineextension/PhabricatorNgramsIndexEngineExtension.php',
     'PhabricatorNgramsInterface' => 'applications/search/interface/PhabricatorNgramsInterface.php',
     'PhabricatorNotificationBuilder' => 'applications/notification/builder/PhabricatorNotificationBuilder.php',
     'PhabricatorNotificationClearController' => 'applications/notification/controller/PhabricatorNotificationClearController.php',
     'PhabricatorNotificationClient' => 'applications/notification/client/PhabricatorNotificationClient.php',
     'PhabricatorNotificationConfigOptions' => 'applications/config/option/PhabricatorNotificationConfigOptions.php',
     'PhabricatorNotificationController' => 'applications/notification/controller/PhabricatorNotificationController.php',
     'PhabricatorNotificationDestructionEngineExtension' => 'applications/notification/engineextension/PhabricatorNotificationDestructionEngineExtension.php',
     'PhabricatorNotificationIndividualController' => 'applications/notification/controller/PhabricatorNotificationIndividualController.php',
     'PhabricatorNotificationListController' => 'applications/notification/controller/PhabricatorNotificationListController.php',
     'PhabricatorNotificationPanelController' => 'applications/notification/controller/PhabricatorNotificationPanelController.php',
     'PhabricatorNotificationQuery' => 'applications/notification/query/PhabricatorNotificationQuery.php',
     'PhabricatorNotificationSearchEngine' => 'applications/notification/query/PhabricatorNotificationSearchEngine.php',
     'PhabricatorNotificationServerRef' => 'applications/notification/client/PhabricatorNotificationServerRef.php',
     'PhabricatorNotificationServersConfigOptionType' => 'applications/notification/config/PhabricatorNotificationServersConfigOptionType.php',
     'PhabricatorNotificationStatusView' => 'applications/notification/view/PhabricatorNotificationStatusView.php',
     'PhabricatorNotificationTestController' => 'applications/notification/controller/PhabricatorNotificationTestController.php',
     'PhabricatorNotificationTestFeedStory' => 'applications/notification/feed/PhabricatorNotificationTestFeedStory.php',
     'PhabricatorNotificationUIExample' => 'applications/uiexample/examples/PhabricatorNotificationUIExample.php',
     'PhabricatorNotificationsApplication' => 'applications/notification/application/PhabricatorNotificationsApplication.php',
     'PhabricatorNuanceApplication' => 'applications/nuance/application/PhabricatorNuanceApplication.php',
     'PhabricatorOAuth1AuthProvider' => 'applications/auth/provider/PhabricatorOAuth1AuthProvider.php',
     'PhabricatorOAuth1SecretTemporaryTokenType' => 'applications/auth/provider/PhabricatorOAuth1SecretTemporaryTokenType.php',
     'PhabricatorOAuth2AuthProvider' => 'applications/auth/provider/PhabricatorOAuth2AuthProvider.php',
     'PhabricatorOAuthAuthProvider' => 'applications/auth/provider/PhabricatorOAuthAuthProvider.php',
     'PhabricatorOAuthClientAuthorization' => 'applications/oauthserver/storage/PhabricatorOAuthClientAuthorization.php',
     'PhabricatorOAuthClientAuthorizationQuery' => 'applications/oauthserver/query/PhabricatorOAuthClientAuthorizationQuery.php',
     'PhabricatorOAuthClientController' => 'applications/oauthserver/controller/client/PhabricatorOAuthClientController.php',
     'PhabricatorOAuthClientDisableController' => 'applications/oauthserver/controller/client/PhabricatorOAuthClientDisableController.php',
     'PhabricatorOAuthClientEditController' => 'applications/oauthserver/controller/client/PhabricatorOAuthClientEditController.php',
     'PhabricatorOAuthClientListController' => 'applications/oauthserver/controller/client/PhabricatorOAuthClientListController.php',
     'PhabricatorOAuthClientSecretController' => 'applications/oauthserver/controller/client/PhabricatorOAuthClientSecretController.php',
     'PhabricatorOAuthClientTestController' => 'applications/oauthserver/controller/client/PhabricatorOAuthClientTestController.php',
     'PhabricatorOAuthClientViewController' => 'applications/oauthserver/controller/client/PhabricatorOAuthClientViewController.php',
     'PhabricatorOAuthResponse' => 'applications/oauthserver/PhabricatorOAuthResponse.php',
     'PhabricatorOAuthServer' => 'applications/oauthserver/PhabricatorOAuthServer.php',
     'PhabricatorOAuthServerAccessToken' => 'applications/oauthserver/storage/PhabricatorOAuthServerAccessToken.php',
     'PhabricatorOAuthServerApplication' => 'applications/oauthserver/application/PhabricatorOAuthServerApplication.php',
     'PhabricatorOAuthServerAuthController' => 'applications/oauthserver/controller/PhabricatorOAuthServerAuthController.php',
     'PhabricatorOAuthServerAuthorizationCode' => 'applications/oauthserver/storage/PhabricatorOAuthServerAuthorizationCode.php',
     'PhabricatorOAuthServerAuthorizationsSettingsPanel' => 'applications/oauthserver/panel/PhabricatorOAuthServerAuthorizationsSettingsPanel.php',
     'PhabricatorOAuthServerClient' => 'applications/oauthserver/storage/PhabricatorOAuthServerClient.php',
     'PhabricatorOAuthServerClientAuthorizationPHIDType' => 'applications/oauthserver/phid/PhabricatorOAuthServerClientAuthorizationPHIDType.php',
     'PhabricatorOAuthServerClientPHIDType' => 'applications/oauthserver/phid/PhabricatorOAuthServerClientPHIDType.php',
     'PhabricatorOAuthServerClientQuery' => 'applications/oauthserver/query/PhabricatorOAuthServerClientQuery.php',
     'PhabricatorOAuthServerClientSearchEngine' => 'applications/oauthserver/query/PhabricatorOAuthServerClientSearchEngine.php',
     'PhabricatorOAuthServerController' => 'applications/oauthserver/controller/PhabricatorOAuthServerController.php',
     'PhabricatorOAuthServerCreateClientsCapability' => 'applications/oauthserver/capability/PhabricatorOAuthServerCreateClientsCapability.php',
     'PhabricatorOAuthServerDAO' => 'applications/oauthserver/storage/PhabricatorOAuthServerDAO.php',
     'PhabricatorOAuthServerEditEngine' => 'applications/oauthserver/editor/PhabricatorOAuthServerEditEngine.php',
     'PhabricatorOAuthServerEditor' => 'applications/oauthserver/editor/PhabricatorOAuthServerEditor.php',
     'PhabricatorOAuthServerScope' => 'applications/oauthserver/PhabricatorOAuthServerScope.php',
     'PhabricatorOAuthServerTestCase' => 'applications/oauthserver/__tests__/PhabricatorOAuthServerTestCase.php',
     'PhabricatorOAuthServerTokenController' => 'applications/oauthserver/controller/PhabricatorOAuthServerTokenController.php',
     'PhabricatorOAuthServerTransaction' => 'applications/oauthserver/storage/PhabricatorOAuthServerTransaction.php',
     'PhabricatorOAuthServerTransactionQuery' => 'applications/oauthserver/query/PhabricatorOAuthServerTransactionQuery.php',
     'PhabricatorObjectHandle' => 'applications/phid/PhabricatorObjectHandle.php',
     'PhabricatorObjectHasAsanaSubtaskEdgeType' => 'applications/doorkeeper/edge/PhabricatorObjectHasAsanaSubtaskEdgeType.php',
     'PhabricatorObjectHasAsanaTaskEdgeType' => 'applications/doorkeeper/edge/PhabricatorObjectHasAsanaTaskEdgeType.php',
     'PhabricatorObjectHasContributorEdgeType' => 'applications/transactions/edges/PhabricatorObjectHasContributorEdgeType.php',
     'PhabricatorObjectHasFileEdgeType' => 'applications/transactions/edges/PhabricatorObjectHasFileEdgeType.php',
     'PhabricatorObjectHasJiraIssueEdgeType' => 'applications/doorkeeper/edge/PhabricatorObjectHasJiraIssueEdgeType.php',
     'PhabricatorObjectHasSubscriberEdgeType' => 'applications/transactions/edges/PhabricatorObjectHasSubscriberEdgeType.php',
     'PhabricatorObjectHasUnsubscriberEdgeType' => 'applications/transactions/edges/PhabricatorObjectHasUnsubscriberEdgeType.php',
     'PhabricatorObjectHasWatcherEdgeType' => 'applications/transactions/edges/PhabricatorObjectHasWatcherEdgeType.php',
     'PhabricatorObjectListQuery' => 'applications/phid/query/PhabricatorObjectListQuery.php',
     'PhabricatorObjectListQueryTestCase' => 'applications/phid/query/__tests__/PhabricatorObjectListQueryTestCase.php',
     'PhabricatorObjectMailReceiver' => 'applications/metamta/receiver/PhabricatorObjectMailReceiver.php',
     'PhabricatorObjectMailReceiverTestCase' => 'applications/metamta/receiver/__tests__/PhabricatorObjectMailReceiverTestCase.php',
     'PhabricatorObjectMentionedByObjectEdgeType' => 'applications/transactions/edges/PhabricatorObjectMentionedByObjectEdgeType.php',
     'PhabricatorObjectMentionsObjectEdgeType' => 'applications/transactions/edges/PhabricatorObjectMentionsObjectEdgeType.php',
     'PhabricatorObjectQuery' => 'applications/phid/query/PhabricatorObjectQuery.php',
     'PhabricatorObjectRemarkupRule' => 'infrastructure/markup/rule/PhabricatorObjectRemarkupRule.php',
     'PhabricatorObjectSelectorDialog' => 'view/control/PhabricatorObjectSelectorDialog.php',
     'PhabricatorObjectUsesCredentialsEdgeType' => 'applications/transactions/edges/PhabricatorObjectUsesCredentialsEdgeType.php',
     'PhabricatorOffsetPagedQuery' => 'infrastructure/query/PhabricatorOffsetPagedQuery.php',
     'PhabricatorOldWorldContentSource' => 'infrastructure/contentsource/PhabricatorOldWorldContentSource.php',
     'PhabricatorOneTimeTriggerClock' => 'infrastructure/daemon/workers/clock/PhabricatorOneTimeTriggerClock.php',
     'PhabricatorOpcodeCacheSpec' => 'applications/cache/spec/PhabricatorOpcodeCacheSpec.php',
     'PhabricatorOwnerPathQuery' => 'applications/owners/query/PhabricatorOwnerPathQuery.php',
     'PhabricatorOwnersApplication' => 'applications/owners/application/PhabricatorOwnersApplication.php',
     'PhabricatorOwnersArchiveController' => 'applications/owners/controller/PhabricatorOwnersArchiveController.php',
     'PhabricatorOwnersConfigOptions' => 'applications/owners/config/PhabricatorOwnersConfigOptions.php',
     'PhabricatorOwnersConfiguredCustomField' => 'applications/owners/customfield/PhabricatorOwnersConfiguredCustomField.php',
     'PhabricatorOwnersController' => 'applications/owners/controller/PhabricatorOwnersController.php',
     'PhabricatorOwnersCustomField' => 'applications/owners/customfield/PhabricatorOwnersCustomField.php',
     'PhabricatorOwnersCustomFieldNumericIndex' => 'applications/owners/storage/PhabricatorOwnersCustomFieldNumericIndex.php',
     'PhabricatorOwnersCustomFieldStorage' => 'applications/owners/storage/PhabricatorOwnersCustomFieldStorage.php',
     'PhabricatorOwnersCustomFieldStringIndex' => 'applications/owners/storage/PhabricatorOwnersCustomFieldStringIndex.php',
     'PhabricatorOwnersDAO' => 'applications/owners/storage/PhabricatorOwnersDAO.php',
     'PhabricatorOwnersDefaultEditCapability' => 'applications/owners/capability/PhabricatorOwnersDefaultEditCapability.php',
     'PhabricatorOwnersDefaultViewCapability' => 'applications/owners/capability/PhabricatorOwnersDefaultViewCapability.php',
     'PhabricatorOwnersDetailController' => 'applications/owners/controller/PhabricatorOwnersDetailController.php',
     'PhabricatorOwnersEditController' => 'applications/owners/controller/PhabricatorOwnersEditController.php',
     'PhabricatorOwnersListController' => 'applications/owners/controller/PhabricatorOwnersListController.php',
     'PhabricatorOwnersOwner' => 'applications/owners/storage/PhabricatorOwnersOwner.php',
     'PhabricatorOwnersPackage' => 'applications/owners/storage/PhabricatorOwnersPackage.php',
     'PhabricatorOwnersPackageDatasource' => 'applications/owners/typeahead/PhabricatorOwnersPackageDatasource.php',
     'PhabricatorOwnersPackageEditEngine' => 'applications/owners/editor/PhabricatorOwnersPackageEditEngine.php',
     'PhabricatorOwnersPackageFulltextEngine' => 'applications/owners/query/PhabricatorOwnersPackageFulltextEngine.php',
     'PhabricatorOwnersPackageFunctionDatasource' => 'applications/owners/typeahead/PhabricatorOwnersPackageFunctionDatasource.php',
     'PhabricatorOwnersPackageNameNgrams' => 'applications/owners/storage/PhabricatorOwnersPackageNameNgrams.php',
     'PhabricatorOwnersPackageOwnerDatasource' => 'applications/owners/typeahead/PhabricatorOwnersPackageOwnerDatasource.php',
     'PhabricatorOwnersPackagePHIDType' => 'applications/owners/phid/PhabricatorOwnersPackagePHIDType.php',
     'PhabricatorOwnersPackageQuery' => 'applications/owners/query/PhabricatorOwnersPackageQuery.php',
     'PhabricatorOwnersPackageSearchEngine' => 'applications/owners/query/PhabricatorOwnersPackageSearchEngine.php',
     'PhabricatorOwnersPackageTestCase' => 'applications/owners/storage/__tests__/PhabricatorOwnersPackageTestCase.php',
     'PhabricatorOwnersPackageTransaction' => 'applications/owners/storage/PhabricatorOwnersPackageTransaction.php',
     'PhabricatorOwnersPackageTransactionEditor' => 'applications/owners/editor/PhabricatorOwnersPackageTransactionEditor.php',
     'PhabricatorOwnersPackageTransactionQuery' => 'applications/owners/query/PhabricatorOwnersPackageTransactionQuery.php',
     'PhabricatorOwnersPath' => 'applications/owners/storage/PhabricatorOwnersPath.php',
     'PhabricatorOwnersPathsController' => 'applications/owners/controller/PhabricatorOwnersPathsController.php',
     'PhabricatorOwnersPathsSearchEngineAttachment' => 'applications/owners/engineextension/PhabricatorOwnersPathsSearchEngineAttachment.php',
     'PhabricatorOwnersSchemaSpec' => 'applications/owners/storage/PhabricatorOwnersSchemaSpec.php',
     'PhabricatorOwnersSearchField' => 'applications/owners/searchfield/PhabricatorOwnersSearchField.php',
     'PhabricatorPHDConfigOptions' => 'applications/config/option/PhabricatorPHDConfigOptions.php',
     'PhabricatorPHID' => 'applications/phid/storage/PhabricatorPHID.php',
     'PhabricatorPHIDConstants' => 'applications/phid/PhabricatorPHIDConstants.php',
     'PhabricatorPHIDInterface' => 'applications/phid/interface/PhabricatorPHIDInterface.php',
     'PhabricatorPHIDListEditField' => 'applications/transactions/editfield/PhabricatorPHIDListEditField.php',
     'PhabricatorPHIDListEditType' => 'applications/transactions/edittype/PhabricatorPHIDListEditType.php',
     'PhabricatorPHIDResolver' => 'applications/phid/resolver/PhabricatorPHIDResolver.php',
     'PhabricatorPHIDType' => 'applications/phid/type/PhabricatorPHIDType.php',
     'PhabricatorPHIDTypeTestCase' => 'applications/phid/type/__tests__/PhabricatorPHIDTypeTestCase.php',
     'PhabricatorPHIDsSearchField' => 'applications/search/field/PhabricatorPHIDsSearchField.php',
     'PhabricatorPHPASTApplication' => 'applications/phpast/application/PhabricatorPHPASTApplication.php',
     'PhabricatorPHPConfigSetupCheck' => 'applications/config/check/PhabricatorPHPConfigSetupCheck.php',
     'PhabricatorPHPMailerConfigOptions' => 'applications/config/option/PhabricatorPHPMailerConfigOptions.php',
     'PhabricatorPagedFormUIExample' => 'applications/uiexample/examples/PhabricatorPagedFormUIExample.php',
     'PhabricatorPagerUIExample' => 'applications/uiexample/examples/PhabricatorPagerUIExample.php',
     'PhabricatorPassphraseApplication' => 'applications/passphrase/application/PhabricatorPassphraseApplication.php',
     'PhabricatorPasswordAuthProvider' => 'applications/auth/provider/PhabricatorPasswordAuthProvider.php',
     'PhabricatorPasswordHasher' => 'infrastructure/util/password/PhabricatorPasswordHasher.php',
     'PhabricatorPasswordHasherTestCase' => 'infrastructure/util/password/__tests__/PhabricatorPasswordHasherTestCase.php',
     'PhabricatorPasswordHasherUnavailableException' => 'infrastructure/util/password/PhabricatorPasswordHasherUnavailableException.php',
     'PhabricatorPasswordSettingsPanel' => 'applications/settings/panel/PhabricatorPasswordSettingsPanel.php',
     'PhabricatorPaste' => 'applications/paste/storage/PhabricatorPaste.php',
     'PhabricatorPasteApplication' => 'applications/paste/application/PhabricatorPasteApplication.php',
     'PhabricatorPasteArchiveController' => 'applications/paste/controller/PhabricatorPasteArchiveController.php',
     'PhabricatorPasteConfigOptions' => 'applications/paste/config/PhabricatorPasteConfigOptions.php',
     'PhabricatorPasteContentSearchEngineAttachment' => 'applications/paste/engineextension/PhabricatorPasteContentSearchEngineAttachment.php',
     'PhabricatorPasteController' => 'applications/paste/controller/PhabricatorPasteController.php',
     'PhabricatorPasteDAO' => 'applications/paste/storage/PhabricatorPasteDAO.php',
     'PhabricatorPasteEditController' => 'applications/paste/controller/PhabricatorPasteEditController.php',
     'PhabricatorPasteEditEngine' => 'applications/paste/editor/PhabricatorPasteEditEngine.php',
     'PhabricatorPasteEditor' => 'applications/paste/editor/PhabricatorPasteEditor.php',
     'PhabricatorPasteFilenameContextFreeGrammar' => 'applications/paste/lipsum/PhabricatorPasteFilenameContextFreeGrammar.php',
     'PhabricatorPasteListController' => 'applications/paste/controller/PhabricatorPasteListController.php',
     'PhabricatorPastePastePHIDType' => 'applications/paste/phid/PhabricatorPastePastePHIDType.php',
     'PhabricatorPasteQuery' => 'applications/paste/query/PhabricatorPasteQuery.php',
     'PhabricatorPasteRawController' => 'applications/paste/controller/PhabricatorPasteRawController.php',
     'PhabricatorPasteRemarkupRule' => 'applications/paste/remarkup/PhabricatorPasteRemarkupRule.php',
     'PhabricatorPasteSchemaSpec' => 'applications/paste/storage/PhabricatorPasteSchemaSpec.php',
     'PhabricatorPasteSearchEngine' => 'applications/paste/query/PhabricatorPasteSearchEngine.php',
     'PhabricatorPasteSnippet' => 'applications/paste/snippet/PhabricatorPasteSnippet.php',
     'PhabricatorPasteTestDataGenerator' => 'applications/paste/lipsum/PhabricatorPasteTestDataGenerator.php',
     'PhabricatorPasteTransaction' => 'applications/paste/storage/PhabricatorPasteTransaction.php',
     'PhabricatorPasteTransactionComment' => 'applications/paste/storage/PhabricatorPasteTransactionComment.php',
     'PhabricatorPasteTransactionQuery' => 'applications/paste/query/PhabricatorPasteTransactionQuery.php',
     'PhabricatorPasteViewController' => 'applications/paste/controller/PhabricatorPasteViewController.php',
     'PhabricatorPathSetupCheck' => 'applications/config/check/PhabricatorPathSetupCheck.php',
     'PhabricatorPeopleAnyOwnerDatasource' => 'applications/people/typeahead/PhabricatorPeopleAnyOwnerDatasource.php',
     'PhabricatorPeopleApplication' => 'applications/people/application/PhabricatorPeopleApplication.php',
     'PhabricatorPeopleApproveController' => 'applications/people/controller/PhabricatorPeopleApproveController.php',
     'PhabricatorPeopleCalendarController' => 'applications/people/controller/PhabricatorPeopleCalendarController.php',
     'PhabricatorPeopleController' => 'applications/people/controller/PhabricatorPeopleController.php',
     'PhabricatorPeopleCreateController' => 'applications/people/controller/PhabricatorPeopleCreateController.php',
     'PhabricatorPeopleDatasource' => 'applications/people/typeahead/PhabricatorPeopleDatasource.php',
     'PhabricatorPeopleDeleteController' => 'applications/people/controller/PhabricatorPeopleDeleteController.php',
     'PhabricatorPeopleDetailsProfilePanel' => 'applications/people/profilepanel/PhabricatorPeopleDetailsProfilePanel.php',
     'PhabricatorPeopleDisableController' => 'applications/people/controller/PhabricatorPeopleDisableController.php',
     'PhabricatorPeopleEmpowerController' => 'applications/people/controller/PhabricatorPeopleEmpowerController.php',
     'PhabricatorPeopleExternalPHIDType' => 'applications/people/phid/PhabricatorPeopleExternalPHIDType.php',
     'PhabricatorPeopleIconSet' => 'applications/people/icon/PhabricatorPeopleIconSet.php',
     'PhabricatorPeopleInviteController' => 'applications/people/controller/PhabricatorPeopleInviteController.php',
     'PhabricatorPeopleInviteListController' => 'applications/people/controller/PhabricatorPeopleInviteListController.php',
     'PhabricatorPeopleInviteSendController' => 'applications/people/controller/PhabricatorPeopleInviteSendController.php',
     'PhabricatorPeopleLdapController' => 'applications/people/controller/PhabricatorPeopleLdapController.php',
     'PhabricatorPeopleListController' => 'applications/people/controller/PhabricatorPeopleListController.php',
     'PhabricatorPeopleLogQuery' => 'applications/people/query/PhabricatorPeopleLogQuery.php',
     'PhabricatorPeopleLogSearchEngine' => 'applications/people/query/PhabricatorPeopleLogSearchEngine.php',
     'PhabricatorPeopleLogsController' => 'applications/people/controller/PhabricatorPeopleLogsController.php',
     'PhabricatorPeopleMainMenuBarExtension' => 'applications/people/extension/PhabricatorPeopleMainMenuBarExtension.php',
     'PhabricatorPeopleManageProfilePanel' => 'applications/people/profilepanel/PhabricatorPeopleManageProfilePanel.php',
     'PhabricatorPeopleNewController' => 'applications/people/controller/PhabricatorPeopleNewController.php',
     'PhabricatorPeopleNoOwnerDatasource' => 'applications/people/typeahead/PhabricatorPeopleNoOwnerDatasource.php',
     'PhabricatorPeopleOwnerDatasource' => 'applications/people/typeahead/PhabricatorPeopleOwnerDatasource.php',
     'PhabricatorPeopleProfileController' => 'applications/people/controller/PhabricatorPeopleProfileController.php',
     'PhabricatorPeopleProfileEditController' => 'applications/people/controller/PhabricatorPeopleProfileEditController.php',
     'PhabricatorPeopleProfileManageController' => 'applications/people/controller/PhabricatorPeopleProfileManageController.php',
     'PhabricatorPeopleProfilePanelEngine' => 'applications/people/engine/PhabricatorPeopleProfilePanelEngine.php',
     'PhabricatorPeopleProfilePictureController' => 'applications/people/controller/PhabricatorPeopleProfilePictureController.php',
     'PhabricatorPeopleProfileViewController' => 'applications/people/controller/PhabricatorPeopleProfileViewController.php',
     'PhabricatorPeopleQuery' => 'applications/people/query/PhabricatorPeopleQuery.php',
     'PhabricatorPeopleRenameController' => 'applications/people/controller/PhabricatorPeopleRenameController.php',
     'PhabricatorPeopleSearchEngine' => 'applications/people/query/PhabricatorPeopleSearchEngine.php',
     'PhabricatorPeopleTestDataGenerator' => 'applications/people/lipsum/PhabricatorPeopleTestDataGenerator.php',
     'PhabricatorPeopleTransactionQuery' => 'applications/people/query/PhabricatorPeopleTransactionQuery.php',
     'PhabricatorPeopleUserFunctionDatasource' => 'applications/people/typeahead/PhabricatorPeopleUserFunctionDatasource.php',
     'PhabricatorPeopleUserPHIDType' => 'applications/people/phid/PhabricatorPeopleUserPHIDType.php',
     'PhabricatorPeopleWelcomeController' => 'applications/people/controller/PhabricatorPeopleWelcomeController.php',
     'PhabricatorPersonaAuthProvider' => 'applications/auth/provider/PhabricatorPersonaAuthProvider.php',
     'PhabricatorPhabricatorAuthProvider' => 'applications/auth/provider/PhabricatorPhabricatorAuthProvider.php',
     'PhabricatorPhameApplication' => 'applications/phame/application/PhabricatorPhameApplication.php',
     'PhabricatorPhameBlogPHIDType' => 'applications/phame/phid/PhabricatorPhameBlogPHIDType.php',
     'PhabricatorPhamePostPHIDType' => 'applications/phame/phid/PhabricatorPhamePostPHIDType.php',
     'PhabricatorPhluxApplication' => 'applications/phlux/application/PhabricatorPhluxApplication.php',
     'PhabricatorPholioApplication' => 'applications/pholio/application/PhabricatorPholioApplication.php',
     'PhabricatorPholioConfigOptions' => 'applications/pholio/config/PhabricatorPholioConfigOptions.php',
     'PhabricatorPholioMockTestDataGenerator' => 'applications/pholio/lipsum/PhabricatorPholioMockTestDataGenerator.php',
     'PhabricatorPhortuneApplication' => 'applications/phortune/application/PhabricatorPhortuneApplication.php',
     'PhabricatorPhortuneContentSource' => 'applications/phortune/contentsource/PhabricatorPhortuneContentSource.php',
     'PhabricatorPhortuneManagementInvoiceWorkflow' => 'applications/phortune/management/PhabricatorPhortuneManagementInvoiceWorkflow.php',
     'PhabricatorPhortuneManagementWorkflow' => 'applications/phortune/management/PhabricatorPhortuneManagementWorkflow.php',
     'PhabricatorPhragmentApplication' => 'applications/phragment/application/PhabricatorPhragmentApplication.php',
     'PhabricatorPhrequentApplication' => 'applications/phrequent/application/PhabricatorPhrequentApplication.php',
     'PhabricatorPhrequentConfigOptions' => 'applications/phrequent/config/PhabricatorPhrequentConfigOptions.php',
     'PhabricatorPhrictionApplication' => 'applications/phriction/application/PhabricatorPhrictionApplication.php',
     'PhabricatorPhrictionConfigOptions' => 'applications/phriction/config/PhabricatorPhrictionConfigOptions.php',
     'PhabricatorPhurlApplication' => 'applications/phurl/application/PhabricatorPhurlApplication.php',
     'PhabricatorPhurlConfigOptions' => 'applications/config/option/PhabricatorPhurlConfigOptions.php',
     'PhabricatorPhurlController' => 'applications/phurl/controller/PhabricatorPhurlController.php',
     'PhabricatorPhurlDAO' => 'applications/phurl/storage/PhabricatorPhurlDAO.php',
     'PhabricatorPhurlLinkRemarkupRule' => 'applications/phurl/remarkup/PhabricatorPhurlLinkRemarkupRule.php',
     'PhabricatorPhurlRemarkupRule' => 'applications/phurl/remarkup/PhabricatorPhurlRemarkupRule.php',
     'PhabricatorPhurlSchemaSpec' => 'applications/phurl/storage/PhabricatorPhurlSchemaSpec.php',
     'PhabricatorPhurlShortURLController' => 'applications/phurl/controller/PhabricatorPhurlShortURLController.php',
     'PhabricatorPhurlShortURLDefaultController' => 'applications/phurl/controller/PhabricatorPhurlShortURLDefaultController.php',
     'PhabricatorPhurlURL' => 'applications/phurl/storage/PhabricatorPhurlURL.php',
     'PhabricatorPhurlURLAccessController' => 'applications/phurl/controller/PhabricatorPhurlURLAccessController.php',
     'PhabricatorPhurlURLCommentController' => 'applications/phurl/controller/PhabricatorPhurlURLCommentController.php',
     'PhabricatorPhurlURLCreateCapability' => 'applications/phurl/capability/PhabricatorPhurlURLCreateCapability.php',
     'PhabricatorPhurlURLEditController' => 'applications/phurl/controller/PhabricatorPhurlURLEditController.php',
     'PhabricatorPhurlURLEditor' => 'applications/phurl/editor/PhabricatorPhurlURLEditor.php',
     'PhabricatorPhurlURLListController' => 'applications/phurl/controller/PhabricatorPhurlURLListController.php',
     'PhabricatorPhurlURLMailReceiver' => 'applications/phurl/mail/PhabricatorPhurlURLMailReceiver.php',
     'PhabricatorPhurlURLPHIDType' => 'applications/phurl/phid/PhabricatorPhurlURLPHIDType.php',
     'PhabricatorPhurlURLQuery' => 'applications/phurl/query/PhabricatorPhurlURLQuery.php',
     'PhabricatorPhurlURLReplyHandler' => 'applications/phurl/mail/PhabricatorPhurlURLReplyHandler.php',
     'PhabricatorPhurlURLSearchEngine' => 'applications/phurl/query/PhabricatorPhurlURLSearchEngine.php',
     'PhabricatorPhurlURLTransaction' => 'applications/phurl/storage/PhabricatorPhurlURLTransaction.php',
     'PhabricatorPhurlURLTransactionComment' => 'applications/phurl/storage/PhabricatorPhurlURLTransactionComment.php',
     'PhabricatorPhurlURLTransactionQuery' => 'applications/phurl/query/PhabricatorPhurlURLTransactionQuery.php',
     'PhabricatorPhurlURLViewController' => 'applications/phurl/controller/PhabricatorPhurlURLViewController.php',
     'PhabricatorPirateEnglishTranslation' => 'infrastructure/internationalization/translation/PhabricatorPirateEnglishTranslation.php',
     'PhabricatorPlatformSite' => 'aphront/site/PhabricatorPlatformSite.php',
     'PhabricatorPointsEditField' => 'applications/transactions/editfield/PhabricatorPointsEditField.php',
     'PhabricatorPolicies' => 'applications/policy/constants/PhabricatorPolicies.php',
     'PhabricatorPolicy' => 'applications/policy/storage/PhabricatorPolicy.php',
     'PhabricatorPolicyApplication' => 'applications/policy/application/PhabricatorPolicyApplication.php',
     'PhabricatorPolicyAwareQuery' => 'infrastructure/query/policy/PhabricatorPolicyAwareQuery.php',
     'PhabricatorPolicyAwareTestQuery' => 'applications/policy/__tests__/PhabricatorPolicyAwareTestQuery.php',
     'PhabricatorPolicyCanEditCapability' => 'applications/policy/capability/PhabricatorPolicyCanEditCapability.php',
     'PhabricatorPolicyCanJoinCapability' => 'applications/policy/capability/PhabricatorPolicyCanJoinCapability.php',
     'PhabricatorPolicyCanViewCapability' => 'applications/policy/capability/PhabricatorPolicyCanViewCapability.php',
     'PhabricatorPolicyCapability' => 'applications/policy/capability/PhabricatorPolicyCapability.php',
     'PhabricatorPolicyCapabilityTestCase' => 'applications/policy/capability/__tests__/PhabricatorPolicyCapabilityTestCase.php',
     'PhabricatorPolicyConfigOptions' => 'applications/policy/config/PhabricatorPolicyConfigOptions.php',
     'PhabricatorPolicyConstants' => 'applications/policy/constants/PhabricatorPolicyConstants.php',
     'PhabricatorPolicyController' => 'applications/policy/controller/PhabricatorPolicyController.php',
     'PhabricatorPolicyDAO' => 'applications/policy/storage/PhabricatorPolicyDAO.php',
     'PhabricatorPolicyDataTestCase' => 'applications/policy/__tests__/PhabricatorPolicyDataTestCase.php',
     'PhabricatorPolicyEditController' => 'applications/policy/controller/PhabricatorPolicyEditController.php',
     'PhabricatorPolicyEditEngineExtension' => 'applications/policy/editor/PhabricatorPolicyEditEngineExtension.php',
     'PhabricatorPolicyEditField' => 'applications/transactions/editfield/PhabricatorPolicyEditField.php',
     'PhabricatorPolicyException' => 'applications/policy/exception/PhabricatorPolicyException.php',
     'PhabricatorPolicyExplainController' => 'applications/policy/controller/PhabricatorPolicyExplainController.php',
     'PhabricatorPolicyFilter' => 'applications/policy/filter/PhabricatorPolicyFilter.php',
     'PhabricatorPolicyInterface' => 'applications/policy/interface/PhabricatorPolicyInterface.php',
     'PhabricatorPolicyManagementShowWorkflow' => 'applications/policy/management/PhabricatorPolicyManagementShowWorkflow.php',
     'PhabricatorPolicyManagementUnlockWorkflow' => 'applications/policy/management/PhabricatorPolicyManagementUnlockWorkflow.php',
     'PhabricatorPolicyManagementWorkflow' => 'applications/policy/management/PhabricatorPolicyManagementWorkflow.php',
     'PhabricatorPolicyPHIDTypePolicy' => 'applications/policy/phid/PhabricatorPolicyPHIDTypePolicy.php',
     'PhabricatorPolicyQuery' => 'applications/policy/query/PhabricatorPolicyQuery.php',
     'PhabricatorPolicyRequestExceptionHandler' => 'aphront/handler/PhabricatorPolicyRequestExceptionHandler.php',
     'PhabricatorPolicyRule' => 'applications/policy/rule/PhabricatorPolicyRule.php',
     'PhabricatorPolicySearchEngineExtension' => 'applications/policy/engineextension/PhabricatorPolicySearchEngineExtension.php',
     'PhabricatorPolicyTestCase' => 'applications/policy/__tests__/PhabricatorPolicyTestCase.php',
     'PhabricatorPolicyTestObject' => 'applications/policy/__tests__/PhabricatorPolicyTestObject.php',
     'PhabricatorPolicyType' => 'applications/policy/constants/PhabricatorPolicyType.php',
     'PhabricatorPonderApplication' => 'applications/ponder/application/PhabricatorPonderApplication.php',
     'PhabricatorProfilePanel' => 'applications/search/profilepanel/PhabricatorProfilePanel.php',
     'PhabricatorProfilePanelConfiguration' => 'applications/search/storage/PhabricatorProfilePanelConfiguration.php',
     'PhabricatorProfilePanelConfigurationQuery' => 'applications/search/query/PhabricatorProfilePanelConfigurationQuery.php',
     'PhabricatorProfilePanelConfigurationTransaction' => 'applications/search/storage/PhabricatorProfilePanelConfigurationTransaction.php',
     'PhabricatorProfilePanelConfigurationTransactionQuery' => 'applications/search/query/PhabricatorProfilePanelConfigurationTransactionQuery.php',
     'PhabricatorProfilePanelEditEngine' => 'applications/search/editor/PhabricatorProfilePanelEditEngine.php',
     'PhabricatorProfilePanelEditor' => 'applications/search/editor/PhabricatorProfilePanelEditor.php',
     'PhabricatorProfilePanelEngine' => 'applications/search/engine/PhabricatorProfilePanelEngine.php',
     'PhabricatorProfilePanelIconSet' => 'applications/search/profilepanel/PhabricatorProfilePanelIconSet.php',
     'PhabricatorProfilePanelPHIDType' => 'applications/search/phidtype/PhabricatorProfilePanelPHIDType.php',
     'PhabricatorProject' => 'applications/project/storage/PhabricatorProject.php',
     'PhabricatorProjectAddHeraldAction' => 'applications/project/herald/PhabricatorProjectAddHeraldAction.php',
     'PhabricatorProjectApplication' => 'applications/project/application/PhabricatorProjectApplication.php',
     'PhabricatorProjectArchiveController' => 'applications/project/controller/PhabricatorProjectArchiveController.php',
     'PhabricatorProjectBoardBackgroundController' => 'applications/project/controller/PhabricatorProjectBoardBackgroundController.php',
     'PhabricatorProjectBoardController' => 'applications/project/controller/PhabricatorProjectBoardController.php',
     'PhabricatorProjectBoardDisableController' => 'applications/project/controller/PhabricatorProjectBoardDisableController.php',
     'PhabricatorProjectBoardImportController' => 'applications/project/controller/PhabricatorProjectBoardImportController.php',
     'PhabricatorProjectBoardManageController' => 'applications/project/controller/PhabricatorProjectBoardManageController.php',
     'PhabricatorProjectBoardReorderController' => 'applications/project/controller/PhabricatorProjectBoardReorderController.php',
     'PhabricatorProjectBoardViewController' => 'applications/project/controller/PhabricatorProjectBoardViewController.php',
     'PhabricatorProjectCardView' => 'applications/project/view/PhabricatorProjectCardView.php',
     'PhabricatorProjectColorsConfigOptionType' => 'applications/project/config/PhabricatorProjectColorsConfigOptionType.php',
     'PhabricatorProjectColumn' => 'applications/project/storage/PhabricatorProjectColumn.php',
     'PhabricatorProjectColumnDetailController' => 'applications/project/controller/PhabricatorProjectColumnDetailController.php',
     'PhabricatorProjectColumnEditController' => 'applications/project/controller/PhabricatorProjectColumnEditController.php',
     'PhabricatorProjectColumnHideController' => 'applications/project/controller/PhabricatorProjectColumnHideController.php',
     'PhabricatorProjectColumnPHIDType' => 'applications/project/phid/PhabricatorProjectColumnPHIDType.php',
     'PhabricatorProjectColumnPosition' => 'applications/project/storage/PhabricatorProjectColumnPosition.php',
     'PhabricatorProjectColumnPositionQuery' => 'applications/project/query/PhabricatorProjectColumnPositionQuery.php',
     'PhabricatorProjectColumnQuery' => 'applications/project/query/PhabricatorProjectColumnQuery.php',
     'PhabricatorProjectColumnTransaction' => 'applications/project/storage/PhabricatorProjectColumnTransaction.php',
     'PhabricatorProjectColumnTransactionEditor' => 'applications/project/editor/PhabricatorProjectColumnTransactionEditor.php',
     'PhabricatorProjectColumnTransactionQuery' => 'applications/project/query/PhabricatorProjectColumnTransactionQuery.php',
     'PhabricatorProjectConfigOptions' => 'applications/project/config/PhabricatorProjectConfigOptions.php',
     'PhabricatorProjectConfiguredCustomField' => 'applications/project/customfield/PhabricatorProjectConfiguredCustomField.php',
     'PhabricatorProjectController' => 'applications/project/controller/PhabricatorProjectController.php',
     'PhabricatorProjectCoreTestCase' => 'applications/project/__tests__/PhabricatorProjectCoreTestCase.php',
     'PhabricatorProjectCoverController' => 'applications/project/controller/PhabricatorProjectCoverController.php',
     'PhabricatorProjectCustomField' => 'applications/project/customfield/PhabricatorProjectCustomField.php',
     'PhabricatorProjectCustomFieldNumericIndex' => 'applications/project/storage/PhabricatorProjectCustomFieldNumericIndex.php',
     'PhabricatorProjectCustomFieldStorage' => 'applications/project/storage/PhabricatorProjectCustomFieldStorage.php',
     'PhabricatorProjectCustomFieldStringIndex' => 'applications/project/storage/PhabricatorProjectCustomFieldStringIndex.php',
     'PhabricatorProjectDAO' => 'applications/project/storage/PhabricatorProjectDAO.php',
     'PhabricatorProjectDatasource' => 'applications/project/typeahead/PhabricatorProjectDatasource.php',
     'PhabricatorProjectDefaultController' => 'applications/project/controller/PhabricatorProjectDefaultController.php',
     'PhabricatorProjectDescriptionField' => 'applications/project/customfield/PhabricatorProjectDescriptionField.php',
     'PhabricatorProjectDetailsProfilePanel' => 'applications/project/profilepanel/PhabricatorProjectDetailsProfilePanel.php',
     'PhabricatorProjectEditController' => 'applications/project/controller/PhabricatorProjectEditController.php',
     'PhabricatorProjectEditEngine' => 'applications/project/engine/PhabricatorProjectEditEngine.php',
     'PhabricatorProjectEditPictureController' => 'applications/project/controller/PhabricatorProjectEditPictureController.php',
     'PhabricatorProjectFulltextEngine' => 'applications/project/search/PhabricatorProjectFulltextEngine.php',
     'PhabricatorProjectHeraldAction' => 'applications/project/herald/PhabricatorProjectHeraldAction.php',
     'PhabricatorProjectHeraldAdapter' => 'applications/project/herald/PhabricatorProjectHeraldAdapter.php',
     'PhabricatorProjectHeraldFieldGroup' => 'applications/project/herald/PhabricatorProjectHeraldFieldGroup.php',
     'PhabricatorProjectHovercardEngineExtension' => 'applications/project/engineextension/PhabricatorProjectHovercardEngineExtension.php',
     'PhabricatorProjectIconSet' => 'applications/project/icon/PhabricatorProjectIconSet.php',
     'PhabricatorProjectIconsConfigOptionType' => 'applications/project/config/PhabricatorProjectIconsConfigOptionType.php',
     'PhabricatorProjectInterface' => 'applications/project/interface/PhabricatorProjectInterface.php',
     'PhabricatorProjectListController' => 'applications/project/controller/PhabricatorProjectListController.php',
     'PhabricatorProjectListView' => 'applications/project/view/PhabricatorProjectListView.php',
     'PhabricatorProjectLockController' => 'applications/project/controller/PhabricatorProjectLockController.php',
     'PhabricatorProjectLogicalAncestorDatasource' => 'applications/project/typeahead/PhabricatorProjectLogicalAncestorDatasource.php',
     'PhabricatorProjectLogicalDatasource' => 'applications/project/typeahead/PhabricatorProjectLogicalDatasource.php',
     'PhabricatorProjectLogicalOrNotDatasource' => 'applications/project/typeahead/PhabricatorProjectLogicalOrNotDatasource.php',
     'PhabricatorProjectLogicalUserDatasource' => 'applications/project/typeahead/PhabricatorProjectLogicalUserDatasource.php',
     'PhabricatorProjectLogicalViewerDatasource' => 'applications/project/typeahead/PhabricatorProjectLogicalViewerDatasource.php',
     'PhabricatorProjectManageController' => 'applications/project/controller/PhabricatorProjectManageController.php',
     'PhabricatorProjectManageProfilePanel' => 'applications/project/profilepanel/PhabricatorProjectManageProfilePanel.php',
     'PhabricatorProjectMaterializedMemberEdgeType' => 'applications/project/edge/PhabricatorProjectMaterializedMemberEdgeType.php',
     'PhabricatorProjectMemberListView' => 'applications/project/view/PhabricatorProjectMemberListView.php',
     'PhabricatorProjectMemberOfProjectEdgeType' => 'applications/project/edge/PhabricatorProjectMemberOfProjectEdgeType.php',
     'PhabricatorProjectMembersAddController' => 'applications/project/controller/PhabricatorProjectMembersAddController.php',
     'PhabricatorProjectMembersDatasource' => 'applications/project/typeahead/PhabricatorProjectMembersDatasource.php',
     'PhabricatorProjectMembersPolicyRule' => 'applications/project/policyrule/PhabricatorProjectMembersPolicyRule.php',
     'PhabricatorProjectMembersProfilePanel' => 'applications/project/profilepanel/PhabricatorProjectMembersProfilePanel.php',
     'PhabricatorProjectMembersRemoveController' => 'applications/project/controller/PhabricatorProjectMembersRemoveController.php',
     'PhabricatorProjectMembersViewController' => 'applications/project/controller/PhabricatorProjectMembersViewController.php',
     'PhabricatorProjectMoveController' => 'applications/project/controller/PhabricatorProjectMoveController.php',
     'PhabricatorProjectNameContextFreeGrammar' => 'applications/project/lipsum/PhabricatorProjectNameContextFreeGrammar.php',
     'PhabricatorProjectNoProjectsDatasource' => 'applications/project/typeahead/PhabricatorProjectNoProjectsDatasource.php',
     'PhabricatorProjectObjectHasProjectEdgeType' => 'applications/project/edge/PhabricatorProjectObjectHasProjectEdgeType.php',
     'PhabricatorProjectOrUserDatasource' => 'applications/project/typeahead/PhabricatorProjectOrUserDatasource.php',
     'PhabricatorProjectOrUserFunctionDatasource' => 'applications/project/typeahead/PhabricatorProjectOrUserFunctionDatasource.php',
     'PhabricatorProjectPHIDResolver' => 'applications/phid/resolver/PhabricatorProjectPHIDResolver.php',
     'PhabricatorProjectPanelController' => 'applications/project/controller/PhabricatorProjectPanelController.php',
     'PhabricatorProjectPointsProfilePanel' => 'applications/project/profilepanel/PhabricatorProjectPointsProfilePanel.php',
     'PhabricatorProjectProfileController' => 'applications/project/controller/PhabricatorProjectProfileController.php',
     'PhabricatorProjectProfilePanelEngine' => 'applications/project/engine/PhabricatorProjectProfilePanelEngine.php',
     'PhabricatorProjectProjectHasMemberEdgeType' => 'applications/project/edge/PhabricatorProjectProjectHasMemberEdgeType.php',
     'PhabricatorProjectProjectHasObjectEdgeType' => 'applications/project/edge/PhabricatorProjectProjectHasObjectEdgeType.php',
     'PhabricatorProjectProjectPHIDType' => 'applications/project/phid/PhabricatorProjectProjectPHIDType.php',
     'PhabricatorProjectQuery' => 'applications/project/query/PhabricatorProjectQuery.php',
     'PhabricatorProjectRemoveHeraldAction' => 'applications/project/herald/PhabricatorProjectRemoveHeraldAction.php',
     'PhabricatorProjectSchemaSpec' => 'applications/project/storage/PhabricatorProjectSchemaSpec.php',
     'PhabricatorProjectSearchEngine' => 'applications/project/query/PhabricatorProjectSearchEngine.php',
     'PhabricatorProjectSearchField' => 'applications/project/searchfield/PhabricatorProjectSearchField.php',
     'PhabricatorProjectSilenceController' => 'applications/project/controller/PhabricatorProjectSilenceController.php',
     'PhabricatorProjectSilencedEdgeType' => 'applications/project/edge/PhabricatorProjectSilencedEdgeType.php',
     'PhabricatorProjectSlug' => 'applications/project/storage/PhabricatorProjectSlug.php',
     'PhabricatorProjectStandardCustomField' => 'applications/project/customfield/PhabricatorProjectStandardCustomField.php',
     'PhabricatorProjectStatus' => 'applications/project/constants/PhabricatorProjectStatus.php',
     'PhabricatorProjectSubprojectWarningController' => 'applications/project/controller/PhabricatorProjectSubprojectWarningController.php',
     'PhabricatorProjectSubprojectsController' => 'applications/project/controller/PhabricatorProjectSubprojectsController.php',
     'PhabricatorProjectSubprojectsProfilePanel' => 'applications/project/profilepanel/PhabricatorProjectSubprojectsProfilePanel.php',
     'PhabricatorProjectTestDataGenerator' => 'applications/project/lipsum/PhabricatorProjectTestDataGenerator.php',
     'PhabricatorProjectTransaction' => 'applications/project/storage/PhabricatorProjectTransaction.php',
     'PhabricatorProjectTransactionEditor' => 'applications/project/editor/PhabricatorProjectTransactionEditor.php',
     'PhabricatorProjectTransactionQuery' => 'applications/project/query/PhabricatorProjectTransactionQuery.php',
     'PhabricatorProjectUIEventListener' => 'applications/project/events/PhabricatorProjectUIEventListener.php',
     'PhabricatorProjectUpdateController' => 'applications/project/controller/PhabricatorProjectUpdateController.php',
     'PhabricatorProjectUserFunctionDatasource' => 'applications/project/typeahead/PhabricatorProjectUserFunctionDatasource.php',
     'PhabricatorProjectUserListView' => 'applications/project/view/PhabricatorProjectUserListView.php',
     'PhabricatorProjectViewController' => 'applications/project/controller/PhabricatorProjectViewController.php',
     'PhabricatorProjectWatchController' => 'applications/project/controller/PhabricatorProjectWatchController.php',
     'PhabricatorProjectWatcherListView' => 'applications/project/view/PhabricatorProjectWatcherListView.php',
     'PhabricatorProjectWorkboardBackgroundColor' => 'applications/project/constants/PhabricatorProjectWorkboardBackgroundColor.php',
     'PhabricatorProjectWorkboardProfilePanel' => 'applications/project/profilepanel/PhabricatorProjectWorkboardProfilePanel.php',
     'PhabricatorProjectsCurtainExtension' => 'applications/project/engineextension/PhabricatorProjectsCurtainExtension.php',
     'PhabricatorProjectsEditEngineExtension' => 'applications/project/engineextension/PhabricatorProjectsEditEngineExtension.php',
     'PhabricatorProjectsEditField' => 'applications/transactions/editfield/PhabricatorProjectsEditField.php',
     'PhabricatorProjectsFulltextEngineExtension' => 'applications/project/engineextension/PhabricatorProjectsFulltextEngineExtension.php',
     'PhabricatorProjectsMembersSearchEngineAttachment' => 'applications/project/engineextension/PhabricatorProjectsMembersSearchEngineAttachment.php',
     'PhabricatorProjectsMembershipIndexEngineExtension' => 'applications/project/engineextension/PhabricatorProjectsMembershipIndexEngineExtension.php',
     'PhabricatorProjectsPolicyRule' => 'applications/project/policyrule/PhabricatorProjectsPolicyRule.php',
     'PhabricatorProjectsSearchEngineAttachment' => 'applications/project/engineextension/PhabricatorProjectsSearchEngineAttachment.php',
     'PhabricatorProjectsSearchEngineExtension' => 'applications/project/engineextension/PhabricatorProjectsSearchEngineExtension.php',
     'PhabricatorProjectsWatchersSearchEngineAttachment' => 'applications/project/engineextension/PhabricatorProjectsWatchersSearchEngineAttachment.php',
     'PhabricatorProtocolAdapter' => 'infrastructure/daemon/bot/adapter/PhabricatorProtocolAdapter.php',
     'PhabricatorPygmentSetupCheck' => 'applications/config/check/PhabricatorPygmentSetupCheck.php',
     'PhabricatorQuery' => 'infrastructure/query/PhabricatorQuery.php',
     'PhabricatorQueryConstraint' => 'infrastructure/query/constraint/PhabricatorQueryConstraint.php',
     'PhabricatorQueryOrderItem' => 'infrastructure/query/order/PhabricatorQueryOrderItem.php',
     'PhabricatorQueryOrderTestCase' => 'infrastructure/query/order/__tests__/PhabricatorQueryOrderTestCase.php',
     'PhabricatorQueryOrderVector' => 'infrastructure/query/order/PhabricatorQueryOrderVector.php',
     'PhabricatorRateLimitRequestExceptionHandler' => 'aphront/handler/PhabricatorRateLimitRequestExceptionHandler.php',
     'PhabricatorRecaptchaConfigOptions' => 'applications/config/option/PhabricatorRecaptchaConfigOptions.php',
     'PhabricatorRecipientHasBadgeEdgeType' => 'applications/badges/edge/PhabricatorRecipientHasBadgeEdgeType.php',
     'PhabricatorRedirectController' => 'applications/base/controller/PhabricatorRedirectController.php',
     'PhabricatorRefreshCSRFController' => 'applications/auth/controller/PhabricatorRefreshCSRFController.php',
     'PhabricatorRegistrationProfile' => 'applications/people/storage/PhabricatorRegistrationProfile.php',
     'PhabricatorReleephApplication' => 'applications/releeph/application/PhabricatorReleephApplication.php',
     'PhabricatorReleephApplicationConfigOptions' => 'applications/releeph/config/PhabricatorReleephApplicationConfigOptions.php',
     'PhabricatorRemarkupControl' => 'view/form/control/PhabricatorRemarkupControl.php',
     'PhabricatorRemarkupCowsayBlockInterpreter' => 'infrastructure/markup/interpreter/PhabricatorRemarkupCowsayBlockInterpreter.php',
     'PhabricatorRemarkupCustomBlockRule' => 'infrastructure/markup/rule/PhabricatorRemarkupCustomBlockRule.php',
     'PhabricatorRemarkupCustomInlineRule' => 'infrastructure/markup/rule/PhabricatorRemarkupCustomInlineRule.php',
     'PhabricatorRemarkupEditField' => 'applications/transactions/editfield/PhabricatorRemarkupEditField.php',
     'PhabricatorRemarkupFigletBlockInterpreter' => 'infrastructure/markup/interpreter/PhabricatorRemarkupFigletBlockInterpreter.php',
     'PhabricatorRemarkupUIExample' => 'applications/uiexample/examples/PhabricatorRemarkupUIExample.php',
     'PhabricatorRepositoriesSetupCheck' => 'applications/config/check/PhabricatorRepositoriesSetupCheck.php',
     'PhabricatorRepository' => 'applications/repository/storage/PhabricatorRepository.php',
     'PhabricatorRepositoryAuditRequest' => 'applications/repository/storage/PhabricatorRepositoryAuditRequest.php',
     'PhabricatorRepositoryBranch' => 'applications/repository/storage/PhabricatorRepositoryBranch.php',
     'PhabricatorRepositoryCommit' => 'applications/repository/storage/PhabricatorRepositoryCommit.php',
     'PhabricatorRepositoryCommitChangeParserWorker' => 'applications/repository/worker/commitchangeparser/PhabricatorRepositoryCommitChangeParserWorker.php',
     'PhabricatorRepositoryCommitData' => 'applications/repository/storage/PhabricatorRepositoryCommitData.php',
     'PhabricatorRepositoryCommitHeraldWorker' => 'applications/repository/worker/PhabricatorRepositoryCommitHeraldWorker.php',
     'PhabricatorRepositoryCommitMessageParserWorker' => 'applications/repository/worker/commitmessageparser/PhabricatorRepositoryCommitMessageParserWorker.php',
     'PhabricatorRepositoryCommitOwnersWorker' => 'applications/repository/worker/PhabricatorRepositoryCommitOwnersWorker.php',
     'PhabricatorRepositoryCommitPHIDType' => 'applications/repository/phid/PhabricatorRepositoryCommitPHIDType.php',
     'PhabricatorRepositoryCommitParserWorker' => 'applications/repository/worker/PhabricatorRepositoryCommitParserWorker.php',
     'PhabricatorRepositoryCommitRef' => 'applications/repository/engine/PhabricatorRepositoryCommitRef.php',
     'PhabricatorRepositoryConfigOptions' => 'applications/repository/config/PhabricatorRepositoryConfigOptions.php',
     'PhabricatorRepositoryDAO' => 'applications/repository/storage/PhabricatorRepositoryDAO.php',
     'PhabricatorRepositoryDiscoveryEngine' => 'applications/repository/engine/PhabricatorRepositoryDiscoveryEngine.php',
     'PhabricatorRepositoryEditor' => 'applications/repository/editor/PhabricatorRepositoryEditor.php',
     'PhabricatorRepositoryEngine' => 'applications/repository/engine/PhabricatorRepositoryEngine.php',
     'PhabricatorRepositoryGitCommitChangeParserWorker' => 'applications/repository/worker/commitchangeparser/PhabricatorRepositoryGitCommitChangeParserWorker.php',
     'PhabricatorRepositoryGitCommitMessageParserWorker' => 'applications/repository/worker/commitmessageparser/PhabricatorRepositoryGitCommitMessageParserWorker.php',
     'PhabricatorRepositoryGitLFSRef' => 'applications/repository/storage/PhabricatorRepositoryGitLFSRef.php',
     'PhabricatorRepositoryGitLFSRefQuery' => 'applications/repository/query/PhabricatorRepositoryGitLFSRefQuery.php',
     'PhabricatorRepositoryGraphCache' => 'applications/repository/graphcache/PhabricatorRepositoryGraphCache.php',
     'PhabricatorRepositoryGraphStream' => 'applications/repository/daemon/PhabricatorRepositoryGraphStream.php',
     'PhabricatorRepositoryManagementCacheWorkflow' => 'applications/repository/management/PhabricatorRepositoryManagementCacheWorkflow.php',
     'PhabricatorRepositoryManagementClusterizeWorkflow' => 'applications/repository/management/PhabricatorRepositoryManagementClusterizeWorkflow.php',
     'PhabricatorRepositoryManagementDiscoverWorkflow' => 'applications/repository/management/PhabricatorRepositoryManagementDiscoverWorkflow.php',
-    'PhabricatorRepositoryManagementEditWorkflow' => 'applications/repository/management/PhabricatorRepositoryManagementEditWorkflow.php',
     'PhabricatorRepositoryManagementImportingWorkflow' => 'applications/repository/management/PhabricatorRepositoryManagementImportingWorkflow.php',
     'PhabricatorRepositoryManagementListPathsWorkflow' => 'applications/repository/management/PhabricatorRepositoryManagementListPathsWorkflow.php',
     'PhabricatorRepositoryManagementListWorkflow' => 'applications/repository/management/PhabricatorRepositoryManagementListWorkflow.php',
     'PhabricatorRepositoryManagementLookupUsersWorkflow' => 'applications/repository/management/PhabricatorRepositoryManagementLookupUsersWorkflow.php',
     'PhabricatorRepositoryManagementMarkImportedWorkflow' => 'applications/repository/management/PhabricatorRepositoryManagementMarkImportedWorkflow.php',
     'PhabricatorRepositoryManagementMirrorWorkflow' => 'applications/repository/management/PhabricatorRepositoryManagementMirrorWorkflow.php',
     'PhabricatorRepositoryManagementMovePathsWorkflow' => 'applications/repository/management/PhabricatorRepositoryManagementMovePathsWorkflow.php',
     'PhabricatorRepositoryManagementParentsWorkflow' => 'applications/repository/management/PhabricatorRepositoryManagementParentsWorkflow.php',
     'PhabricatorRepositoryManagementPullWorkflow' => 'applications/repository/management/PhabricatorRepositoryManagementPullWorkflow.php',
     'PhabricatorRepositoryManagementRefsWorkflow' => 'applications/repository/management/PhabricatorRepositoryManagementRefsWorkflow.php',
     'PhabricatorRepositoryManagementReparseWorkflow' => 'applications/repository/management/PhabricatorRepositoryManagementReparseWorkflow.php',
     'PhabricatorRepositoryManagementThawWorkflow' => 'applications/repository/management/PhabricatorRepositoryManagementThawWorkflow.php',
     'PhabricatorRepositoryManagementUpdateWorkflow' => 'applications/repository/management/PhabricatorRepositoryManagementUpdateWorkflow.php',
     'PhabricatorRepositoryManagementWorkflow' => 'applications/repository/management/PhabricatorRepositoryManagementWorkflow.php',
     'PhabricatorRepositoryMercurialCommitChangeParserWorker' => 'applications/repository/worker/commitchangeparser/PhabricatorRepositoryMercurialCommitChangeParserWorker.php',
     'PhabricatorRepositoryMercurialCommitMessageParserWorker' => 'applications/repository/worker/commitmessageparser/PhabricatorRepositoryMercurialCommitMessageParserWorker.php',
     'PhabricatorRepositoryMirror' => 'applications/repository/storage/PhabricatorRepositoryMirror.php',
     'PhabricatorRepositoryMirrorEngine' => 'applications/repository/engine/PhabricatorRepositoryMirrorEngine.php',
-    'PhabricatorRepositoryMirrorPHIDType' => 'applications/repository/phid/PhabricatorRepositoryMirrorPHIDType.php',
-    'PhabricatorRepositoryMirrorQuery' => 'applications/repository/query/PhabricatorRepositoryMirrorQuery.php',
     'PhabricatorRepositoryParsedChange' => 'applications/repository/data/PhabricatorRepositoryParsedChange.php',
     'PhabricatorRepositoryPullEngine' => 'applications/repository/engine/PhabricatorRepositoryPullEngine.php',
     'PhabricatorRepositoryPullEvent' => 'applications/repository/storage/PhabricatorRepositoryPullEvent.php',
     'PhabricatorRepositoryPullEventPHIDType' => 'applications/repository/phid/PhabricatorRepositoryPullEventPHIDType.php',
     'PhabricatorRepositoryPullEventQuery' => 'applications/repository/query/PhabricatorRepositoryPullEventQuery.php',
     'PhabricatorRepositoryPullLocalDaemon' => 'applications/repository/daemon/PhabricatorRepositoryPullLocalDaemon.php',
     'PhabricatorRepositoryPushEvent' => 'applications/repository/storage/PhabricatorRepositoryPushEvent.php',
     'PhabricatorRepositoryPushEventPHIDType' => 'applications/repository/phid/PhabricatorRepositoryPushEventPHIDType.php',
     'PhabricatorRepositoryPushEventQuery' => 'applications/repository/query/PhabricatorRepositoryPushEventQuery.php',
     'PhabricatorRepositoryPushLog' => 'applications/repository/storage/PhabricatorRepositoryPushLog.php',
     'PhabricatorRepositoryPushLogPHIDType' => 'applications/repository/phid/PhabricatorRepositoryPushLogPHIDType.php',
     'PhabricatorRepositoryPushLogQuery' => 'applications/repository/query/PhabricatorRepositoryPushLogQuery.php',
     'PhabricatorRepositoryPushLogSearchEngine' => 'applications/repository/query/PhabricatorRepositoryPushLogSearchEngine.php',
     'PhabricatorRepositoryPushMailWorker' => 'applications/repository/worker/PhabricatorRepositoryPushMailWorker.php',
     'PhabricatorRepositoryPushReplyHandler' => 'applications/repository/mail/PhabricatorRepositoryPushReplyHandler.php',
     'PhabricatorRepositoryQuery' => 'applications/repository/query/PhabricatorRepositoryQuery.php',
     'PhabricatorRepositoryRefCursor' => 'applications/repository/storage/PhabricatorRepositoryRefCursor.php',
     'PhabricatorRepositoryRefCursorPHIDType' => 'applications/repository/phid/PhabricatorRepositoryRefCursorPHIDType.php',
     'PhabricatorRepositoryRefCursorQuery' => 'applications/repository/query/PhabricatorRepositoryRefCursorQuery.php',
     'PhabricatorRepositoryRefEngine' => 'applications/repository/engine/PhabricatorRepositoryRefEngine.php',
     'PhabricatorRepositoryRepositoryPHIDType' => 'applications/repository/phid/PhabricatorRepositoryRepositoryPHIDType.php',
     'PhabricatorRepositorySchemaSpec' => 'applications/repository/storage/PhabricatorRepositorySchemaSpec.php',
     'PhabricatorRepositorySearchEngine' => 'applications/repository/query/PhabricatorRepositorySearchEngine.php',
     'PhabricatorRepositoryStatusMessage' => 'applications/repository/storage/PhabricatorRepositoryStatusMessage.php',
     'PhabricatorRepositorySvnCommitChangeParserWorker' => 'applications/repository/worker/commitchangeparser/PhabricatorRepositorySvnCommitChangeParserWorker.php',
     'PhabricatorRepositorySvnCommitMessageParserWorker' => 'applications/repository/worker/commitmessageparser/PhabricatorRepositorySvnCommitMessageParserWorker.php',
     'PhabricatorRepositorySymbol' => 'applications/repository/storage/PhabricatorRepositorySymbol.php',
     'PhabricatorRepositoryTestCase' => 'applications/repository/storage/__tests__/PhabricatorRepositoryTestCase.php',
     'PhabricatorRepositoryTransaction' => 'applications/repository/storage/PhabricatorRepositoryTransaction.php',
     'PhabricatorRepositoryTransactionQuery' => 'applications/repository/query/PhabricatorRepositoryTransactionQuery.php',
     'PhabricatorRepositoryType' => 'applications/repository/constants/PhabricatorRepositoryType.php',
     'PhabricatorRepositoryURI' => 'applications/repository/storage/PhabricatorRepositoryURI.php',
     'PhabricatorRepositoryURIIndex' => 'applications/repository/storage/PhabricatorRepositoryURIIndex.php',
     'PhabricatorRepositoryURINormalizer' => 'applications/repository/data/PhabricatorRepositoryURINormalizer.php',
     'PhabricatorRepositoryURINormalizerTestCase' => 'applications/repository/data/__tests__/PhabricatorRepositoryURINormalizerTestCase.php',
     'PhabricatorRepositoryURIPHIDType' => 'applications/repository/phid/PhabricatorRepositoryURIPHIDType.php',
     'PhabricatorRepositoryURIQuery' => 'applications/repository/query/PhabricatorRepositoryURIQuery.php',
     'PhabricatorRepositoryURITestCase' => 'applications/repository/storage/__tests__/PhabricatorRepositoryURITestCase.php',
     'PhabricatorRepositoryURITransaction' => 'applications/repository/storage/PhabricatorRepositoryURITransaction.php',
     'PhabricatorRepositoryURITransactionQuery' => 'applications/repository/query/PhabricatorRepositoryURITransactionQuery.php',
     'PhabricatorRepositoryVCSPassword' => 'applications/repository/storage/PhabricatorRepositoryVCSPassword.php',
     'PhabricatorRepositoryVersion' => 'applications/repository/constants/PhabricatorRepositoryVersion.php',
     'PhabricatorRepositoryWorkingCopyVersion' => 'applications/repository/storage/PhabricatorRepositoryWorkingCopyVersion.php',
     'PhabricatorRequestExceptionHandler' => 'aphront/handler/PhabricatorRequestExceptionHandler.php',
     'PhabricatorResourceSite' => 'aphront/site/PhabricatorResourceSite.php',
     'PhabricatorRobotsController' => 'applications/system/controller/PhabricatorRobotsController.php',
     'PhabricatorS3FileStorageEngine' => 'applications/files/engine/PhabricatorS3FileStorageEngine.php',
     'PhabricatorSMS' => 'infrastructure/sms/storage/PhabricatorSMS.php',
     'PhabricatorSMSConfigOptions' => 'applications/config/option/PhabricatorSMSConfigOptions.php',
     'PhabricatorSMSDAO' => 'infrastructure/sms/storage/PhabricatorSMSDAO.php',
     'PhabricatorSMSDemultiplexWorker' => 'infrastructure/sms/worker/PhabricatorSMSDemultiplexWorker.php',
     'PhabricatorSMSImplementationAdapter' => 'infrastructure/sms/adapter/PhabricatorSMSImplementationAdapter.php',
     'PhabricatorSMSImplementationTestBlackholeAdapter' => 'infrastructure/sms/adapter/PhabricatorSMSImplementationTestBlackholeAdapter.php',
     'PhabricatorSMSImplementationTwilioAdapter' => 'infrastructure/sms/adapter/PhabricatorSMSImplementationTwilioAdapter.php',
     'PhabricatorSMSManagementListOutboundWorkflow' => 'infrastructure/sms/management/PhabricatorSMSManagementListOutboundWorkflow.php',
     'PhabricatorSMSManagementSendTestWorkflow' => 'infrastructure/sms/management/PhabricatorSMSManagementSendTestWorkflow.php',
     'PhabricatorSMSManagementShowOutboundWorkflow' => 'infrastructure/sms/management/PhabricatorSMSManagementShowOutboundWorkflow.php',
     'PhabricatorSMSManagementWorkflow' => 'infrastructure/sms/management/PhabricatorSMSManagementWorkflow.php',
     'PhabricatorSMSSendWorker' => 'infrastructure/sms/worker/PhabricatorSMSSendWorker.php',
     'PhabricatorSMSWorker' => 'infrastructure/sms/worker/PhabricatorSMSWorker.php',
     'PhabricatorSQLPatchList' => 'infrastructure/storage/patch/PhabricatorSQLPatchList.php',
     'PhabricatorSSHKeyGenerator' => 'infrastructure/util/PhabricatorSSHKeyGenerator.php',
     'PhabricatorSSHKeysSettingsPanel' => 'applications/settings/panel/PhabricatorSSHKeysSettingsPanel.php',
     'PhabricatorSSHLog' => 'infrastructure/log/PhabricatorSSHLog.php',
     'PhabricatorSSHPassthruCommand' => 'infrastructure/ssh/PhabricatorSSHPassthruCommand.php',
     'PhabricatorSSHPublicKeyInterface' => 'applications/auth/sshkey/PhabricatorSSHPublicKeyInterface.php',
     'PhabricatorSSHWorkflow' => 'infrastructure/ssh/PhabricatorSSHWorkflow.php',
     'PhabricatorSavedQuery' => 'applications/search/storage/PhabricatorSavedQuery.php',
     'PhabricatorSavedQueryQuery' => 'applications/search/query/PhabricatorSavedQueryQuery.php',
     'PhabricatorScheduleTaskTriggerAction' => 'infrastructure/daemon/workers/action/PhabricatorScheduleTaskTriggerAction.php',
     'PhabricatorScopedEnv' => 'infrastructure/env/PhabricatorScopedEnv.php',
     'PhabricatorSearchAbstractDocument' => 'applications/search/index/PhabricatorSearchAbstractDocument.php',
     'PhabricatorSearchApplication' => 'applications/search/application/PhabricatorSearchApplication.php',
     'PhabricatorSearchApplicationSearchEngine' => 'applications/search/query/PhabricatorSearchApplicationSearchEngine.php',
     'PhabricatorSearchApplicationStorageEnginePanel' => 'applications/search/applicationpanel/PhabricatorSearchApplicationStorageEnginePanel.php',
     'PhabricatorSearchAttachController' => 'applications/search/controller/PhabricatorSearchAttachController.php',
     'PhabricatorSearchBaseController' => 'applications/search/controller/PhabricatorSearchBaseController.php',
     'PhabricatorSearchCheckboxesField' => 'applications/search/field/PhabricatorSearchCheckboxesField.php',
     'PhabricatorSearchConfigOptions' => 'applications/search/config/PhabricatorSearchConfigOptions.php',
     'PhabricatorSearchController' => 'applications/search/controller/PhabricatorSearchController.php',
     'PhabricatorSearchCustomFieldProxyField' => 'applications/search/field/PhabricatorSearchCustomFieldProxyField.php',
     'PhabricatorSearchDAO' => 'applications/search/storage/PhabricatorSearchDAO.php',
     'PhabricatorSearchDatasource' => 'applications/search/typeahead/PhabricatorSearchDatasource.php',
     'PhabricatorSearchDatasourceField' => 'applications/search/field/PhabricatorSearchDatasourceField.php',
     'PhabricatorSearchDateControlField' => 'applications/search/field/PhabricatorSearchDateControlField.php',
     'PhabricatorSearchDateField' => 'applications/search/field/PhabricatorSearchDateField.php',
     'PhabricatorSearchDeleteController' => 'applications/search/controller/PhabricatorSearchDeleteController.php',
     'PhabricatorSearchDocument' => 'applications/search/storage/document/PhabricatorSearchDocument.php',
     'PhabricatorSearchDocumentField' => 'applications/search/storage/document/PhabricatorSearchDocumentField.php',
     'PhabricatorSearchDocumentFieldType' => 'applications/search/constants/PhabricatorSearchDocumentFieldType.php',
     'PhabricatorSearchDocumentQuery' => 'applications/search/query/PhabricatorSearchDocumentQuery.php',
     'PhabricatorSearchDocumentRelationship' => 'applications/search/storage/document/PhabricatorSearchDocumentRelationship.php',
     'PhabricatorSearchDocumentTypeDatasource' => 'applications/search/typeahead/PhabricatorSearchDocumentTypeDatasource.php',
     'PhabricatorSearchEditController' => 'applications/search/controller/PhabricatorSearchEditController.php',
     'PhabricatorSearchEngineAPIMethod' => 'applications/search/engine/PhabricatorSearchEngineAPIMethod.php',
     'PhabricatorSearchEngineAttachment' => 'applications/search/engineextension/PhabricatorSearchEngineAttachment.php',
     'PhabricatorSearchEngineExtension' => 'applications/search/engineextension/PhabricatorSearchEngineExtension.php',
     'PhabricatorSearchEngineExtensionModule' => 'applications/search/engineextension/PhabricatorSearchEngineExtensionModule.php',
     'PhabricatorSearchEngineTestCase' => 'applications/search/engine/__tests__/PhabricatorSearchEngineTestCase.php',
     'PhabricatorSearchField' => 'applications/search/field/PhabricatorSearchField.php',
     'PhabricatorSearchHovercardController' => 'applications/search/controller/PhabricatorSearchHovercardController.php',
     'PhabricatorSearchIndexVersion' => 'applications/search/storage/PhabricatorSearchIndexVersion.php',
     'PhabricatorSearchIndexVersionDestructionEngineExtension' => 'applications/search/engineextension/PhabricatorSearchIndexVersionDestructionEngineExtension.php',
     'PhabricatorSearchManagementIndexWorkflow' => 'applications/search/management/PhabricatorSearchManagementIndexWorkflow.php',
     'PhabricatorSearchManagementInitWorkflow' => 'applications/search/management/PhabricatorSearchManagementInitWorkflow.php',
     'PhabricatorSearchManagementWorkflow' => 'applications/search/management/PhabricatorSearchManagementWorkflow.php',
     'PhabricatorSearchNgrams' => 'applications/search/ngrams/PhabricatorSearchNgrams.php',
     'PhabricatorSearchNgramsDestructionEngineExtension' => 'applications/search/engineextension/PhabricatorSearchNgramsDestructionEngineExtension.php',
     'PhabricatorSearchOrderController' => 'applications/search/controller/PhabricatorSearchOrderController.php',
     'PhabricatorSearchOrderField' => 'applications/search/field/PhabricatorSearchOrderField.php',
     'PhabricatorSearchPreferencesSettingsPanel' => 'applications/settings/panel/PhabricatorSearchPreferencesSettingsPanel.php',
     'PhabricatorSearchRelationship' => 'applications/search/constants/PhabricatorSearchRelationship.php',
     'PhabricatorSearchResultView' => 'applications/search/view/PhabricatorSearchResultView.php',
     'PhabricatorSearchSchemaSpec' => 'applications/search/storage/PhabricatorSearchSchemaSpec.php',
     'PhabricatorSearchSelectController' => 'applications/search/controller/PhabricatorSearchSelectController.php',
     'PhabricatorSearchSelectField' => 'applications/search/field/PhabricatorSearchSelectField.php',
     'PhabricatorSearchStringListField' => 'applications/search/field/PhabricatorSearchStringListField.php',
     'PhabricatorSearchSubscribersField' => 'applications/search/field/PhabricatorSearchSubscribersField.php',
     'PhabricatorSearchTextField' => 'applications/search/field/PhabricatorSearchTextField.php',
     'PhabricatorSearchThreeStateField' => 'applications/search/field/PhabricatorSearchThreeStateField.php',
     'PhabricatorSearchTokenizerField' => 'applications/search/field/PhabricatorSearchTokenizerField.php',
     'PhabricatorSearchWorker' => 'applications/search/worker/PhabricatorSearchWorker.php',
     'PhabricatorSecurityConfigOptions' => 'applications/config/option/PhabricatorSecurityConfigOptions.php',
     'PhabricatorSecuritySetupCheck' => 'applications/config/check/PhabricatorSecuritySetupCheck.php',
     'PhabricatorSelectEditField' => 'applications/transactions/editfield/PhabricatorSelectEditField.php',
     'PhabricatorSendGridConfigOptions' => 'applications/config/option/PhabricatorSendGridConfigOptions.php',
     'PhabricatorSessionsSettingsPanel' => 'applications/settings/panel/PhabricatorSessionsSettingsPanel.php',
     'PhabricatorSettingsAddEmailAction' => 'applications/settings/action/PhabricatorSettingsAddEmailAction.php',
     'PhabricatorSettingsAdjustController' => 'applications/settings/controller/PhabricatorSettingsAdjustController.php',
     'PhabricatorSettingsApplication' => 'applications/settings/application/PhabricatorSettingsApplication.php',
     'PhabricatorSettingsMainController' => 'applications/settings/controller/PhabricatorSettingsMainController.php',
     'PhabricatorSettingsMainMenuBarExtension' => 'applications/settings/extension/PhabricatorSettingsMainMenuBarExtension.php',
     'PhabricatorSettingsPanel' => 'applications/settings/panel/PhabricatorSettingsPanel.php',
     'PhabricatorSetupCheck' => 'applications/config/check/PhabricatorSetupCheck.php',
     'PhabricatorSetupCheckTestCase' => 'applications/config/check/__tests__/PhabricatorSetupCheckTestCase.php',
     'PhabricatorSetupIssue' => 'applications/config/issue/PhabricatorSetupIssue.php',
     'PhabricatorSetupIssueUIExample' => 'applications/uiexample/examples/PhabricatorSetupIssueUIExample.php',
     'PhabricatorSetupIssueView' => 'applications/config/view/PhabricatorSetupIssueView.php',
     'PhabricatorShortSite' => 'aphront/site/PhabricatorShortSite.php',
     'PhabricatorSimpleEditType' => 'applications/transactions/edittype/PhabricatorSimpleEditType.php',
     'PhabricatorSite' => 'aphront/site/PhabricatorSite.php',
     'PhabricatorSlowvoteApplication' => 'applications/slowvote/application/PhabricatorSlowvoteApplication.php',
     'PhabricatorSlowvoteChoice' => 'applications/slowvote/storage/PhabricatorSlowvoteChoice.php',
     'PhabricatorSlowvoteCloseController' => 'applications/slowvote/controller/PhabricatorSlowvoteCloseController.php',
     'PhabricatorSlowvoteCommentController' => 'applications/slowvote/controller/PhabricatorSlowvoteCommentController.php',
     'PhabricatorSlowvoteController' => 'applications/slowvote/controller/PhabricatorSlowvoteController.php',
     'PhabricatorSlowvoteDAO' => 'applications/slowvote/storage/PhabricatorSlowvoteDAO.php',
     'PhabricatorSlowvoteDefaultViewCapability' => 'applications/slowvote/capability/PhabricatorSlowvoteDefaultViewCapability.php',
     'PhabricatorSlowvoteEditController' => 'applications/slowvote/controller/PhabricatorSlowvoteEditController.php',
     'PhabricatorSlowvoteEditor' => 'applications/slowvote/editor/PhabricatorSlowvoteEditor.php',
     'PhabricatorSlowvoteListController' => 'applications/slowvote/controller/PhabricatorSlowvoteListController.php',
     'PhabricatorSlowvoteMailReceiver' => 'applications/slowvote/mail/PhabricatorSlowvoteMailReceiver.php',
     'PhabricatorSlowvoteOption' => 'applications/slowvote/storage/PhabricatorSlowvoteOption.php',
     'PhabricatorSlowvotePoll' => 'applications/slowvote/storage/PhabricatorSlowvotePoll.php',
     'PhabricatorSlowvotePollController' => 'applications/slowvote/controller/PhabricatorSlowvotePollController.php',
     'PhabricatorSlowvotePollPHIDType' => 'applications/slowvote/phid/PhabricatorSlowvotePollPHIDType.php',
     'PhabricatorSlowvoteQuery' => 'applications/slowvote/query/PhabricatorSlowvoteQuery.php',
     'PhabricatorSlowvoteReplyHandler' => 'applications/slowvote/mail/PhabricatorSlowvoteReplyHandler.php',
     'PhabricatorSlowvoteSchemaSpec' => 'applications/slowvote/storage/PhabricatorSlowvoteSchemaSpec.php',
     'PhabricatorSlowvoteSearchEngine' => 'applications/slowvote/query/PhabricatorSlowvoteSearchEngine.php',
     'PhabricatorSlowvoteTransaction' => 'applications/slowvote/storage/PhabricatorSlowvoteTransaction.php',
     'PhabricatorSlowvoteTransactionComment' => 'applications/slowvote/storage/PhabricatorSlowvoteTransactionComment.php',
     'PhabricatorSlowvoteTransactionQuery' => 'applications/slowvote/query/PhabricatorSlowvoteTransactionQuery.php',
     'PhabricatorSlowvoteVoteController' => 'applications/slowvote/controller/PhabricatorSlowvoteVoteController.php',
     'PhabricatorSlug' => 'infrastructure/util/PhabricatorSlug.php',
     'PhabricatorSlugTestCase' => 'infrastructure/util/__tests__/PhabricatorSlugTestCase.php',
     'PhabricatorSortTableUIExample' => 'applications/uiexample/examples/PhabricatorSortTableUIExample.php',
     'PhabricatorSourceCodeView' => 'view/layout/PhabricatorSourceCodeView.php',
     'PhabricatorSpaceEditField' => 'applications/transactions/editfield/PhabricatorSpaceEditField.php',
     'PhabricatorSpacesApplication' => 'applications/spaces/application/PhabricatorSpacesApplication.php',
     'PhabricatorSpacesArchiveController' => 'applications/spaces/controller/PhabricatorSpacesArchiveController.php',
     'PhabricatorSpacesCapabilityCreateSpaces' => 'applications/spaces/capability/PhabricatorSpacesCapabilityCreateSpaces.php',
     'PhabricatorSpacesCapabilityDefaultEdit' => 'applications/spaces/capability/PhabricatorSpacesCapabilityDefaultEdit.php',
     'PhabricatorSpacesCapabilityDefaultView' => 'applications/spaces/capability/PhabricatorSpacesCapabilityDefaultView.php',
     'PhabricatorSpacesController' => 'applications/spaces/controller/PhabricatorSpacesController.php',
     'PhabricatorSpacesDAO' => 'applications/spaces/storage/PhabricatorSpacesDAO.php',
     'PhabricatorSpacesEditController' => 'applications/spaces/controller/PhabricatorSpacesEditController.php',
     'PhabricatorSpacesInterface' => 'applications/spaces/interface/PhabricatorSpacesInterface.php',
     'PhabricatorSpacesListController' => 'applications/spaces/controller/PhabricatorSpacesListController.php',
     'PhabricatorSpacesNamespace' => 'applications/spaces/storage/PhabricatorSpacesNamespace.php',
     'PhabricatorSpacesNamespaceDatasource' => 'applications/spaces/typeahead/PhabricatorSpacesNamespaceDatasource.php',
     'PhabricatorSpacesNamespaceEditor' => 'applications/spaces/editor/PhabricatorSpacesNamespaceEditor.php',
     'PhabricatorSpacesNamespacePHIDType' => 'applications/spaces/phid/PhabricatorSpacesNamespacePHIDType.php',
     'PhabricatorSpacesNamespaceQuery' => 'applications/spaces/query/PhabricatorSpacesNamespaceQuery.php',
     'PhabricatorSpacesNamespaceSearchEngine' => 'applications/spaces/query/PhabricatorSpacesNamespaceSearchEngine.php',
     'PhabricatorSpacesNamespaceTransaction' => 'applications/spaces/storage/PhabricatorSpacesNamespaceTransaction.php',
     'PhabricatorSpacesNamespaceTransactionQuery' => 'applications/spaces/query/PhabricatorSpacesNamespaceTransactionQuery.php',
     'PhabricatorSpacesNoAccessController' => 'applications/spaces/controller/PhabricatorSpacesNoAccessController.php',
     'PhabricatorSpacesRemarkupRule' => 'applications/spaces/remarkup/PhabricatorSpacesRemarkupRule.php',
     'PhabricatorSpacesSchemaSpec' => 'applications/spaces/storage/PhabricatorSpacesSchemaSpec.php',
     'PhabricatorSpacesSearchEngineExtension' => 'applications/spaces/engineextension/PhabricatorSpacesSearchEngineExtension.php',
     'PhabricatorSpacesSearchField' => 'applications/spaces/searchfield/PhabricatorSpacesSearchField.php',
     'PhabricatorSpacesTestCase' => 'applications/spaces/__tests__/PhabricatorSpacesTestCase.php',
     'PhabricatorSpacesViewController' => 'applications/spaces/controller/PhabricatorSpacesViewController.php',
     'PhabricatorStandardCustomField' => 'infrastructure/customfield/standard/PhabricatorStandardCustomField.php',
     'PhabricatorStandardCustomFieldBlueprints' => 'infrastructure/customfield/standard/PhabricatorStandardCustomFieldBlueprints.php',
     'PhabricatorStandardCustomFieldBool' => 'infrastructure/customfield/standard/PhabricatorStandardCustomFieldBool.php',
     'PhabricatorStandardCustomFieldCredential' => 'infrastructure/customfield/standard/PhabricatorStandardCustomFieldCredential.php',
     'PhabricatorStandardCustomFieldDatasource' => 'infrastructure/customfield/standard/PhabricatorStandardCustomFieldDatasource.php',
     'PhabricatorStandardCustomFieldDate' => 'infrastructure/customfield/standard/PhabricatorStandardCustomFieldDate.php',
     'PhabricatorStandardCustomFieldHeader' => 'infrastructure/customfield/standard/PhabricatorStandardCustomFieldHeader.php',
     'PhabricatorStandardCustomFieldInt' => 'infrastructure/customfield/standard/PhabricatorStandardCustomFieldInt.php',
     'PhabricatorStandardCustomFieldInterface' => 'infrastructure/customfield/interface/PhabricatorStandardCustomFieldInterface.php',
     'PhabricatorStandardCustomFieldLink' => 'infrastructure/customfield/standard/PhabricatorStandardCustomFieldLink.php',
     'PhabricatorStandardCustomFieldPHIDs' => 'infrastructure/customfield/standard/PhabricatorStandardCustomFieldPHIDs.php',
     'PhabricatorStandardCustomFieldRemarkup' => 'infrastructure/customfield/standard/PhabricatorStandardCustomFieldRemarkup.php',
     'PhabricatorStandardCustomFieldSelect' => 'infrastructure/customfield/standard/PhabricatorStandardCustomFieldSelect.php',
     'PhabricatorStandardCustomFieldText' => 'infrastructure/customfield/standard/PhabricatorStandardCustomFieldText.php',
     'PhabricatorStandardCustomFieldTokenizer' => 'infrastructure/customfield/standard/PhabricatorStandardCustomFieldTokenizer.php',
     'PhabricatorStandardCustomFieldUsers' => 'infrastructure/customfield/standard/PhabricatorStandardCustomFieldUsers.php',
     'PhabricatorStandardPageView' => 'view/page/PhabricatorStandardPageView.php',
     'PhabricatorStandardSelectCustomFieldDatasource' => 'infrastructure/customfield/datasource/PhabricatorStandardSelectCustomFieldDatasource.php',
     'PhabricatorStaticEditField' => 'applications/transactions/editfield/PhabricatorStaticEditField.php',
     'PhabricatorStatusController' => 'applications/system/controller/PhabricatorStatusController.php',
     'PhabricatorStatusUIExample' => 'applications/uiexample/examples/PhabricatorStatusUIExample.php',
     'PhabricatorStorageFixtureScopeGuard' => 'infrastructure/testing/fixture/PhabricatorStorageFixtureScopeGuard.php',
     'PhabricatorStorageManagementAPI' => 'infrastructure/storage/management/PhabricatorStorageManagementAPI.php',
     'PhabricatorStorageManagementAdjustWorkflow' => 'infrastructure/storage/management/workflow/PhabricatorStorageManagementAdjustWorkflow.php',
     'PhabricatorStorageManagementDatabasesWorkflow' => 'infrastructure/storage/management/workflow/PhabricatorStorageManagementDatabasesWorkflow.php',
     'PhabricatorStorageManagementDestroyWorkflow' => 'infrastructure/storage/management/workflow/PhabricatorStorageManagementDestroyWorkflow.php',
     'PhabricatorStorageManagementDumpWorkflow' => 'infrastructure/storage/management/workflow/PhabricatorStorageManagementDumpWorkflow.php',
     'PhabricatorStorageManagementProbeWorkflow' => 'infrastructure/storage/management/workflow/PhabricatorStorageManagementProbeWorkflow.php',
     'PhabricatorStorageManagementQuickstartWorkflow' => 'infrastructure/storage/management/workflow/PhabricatorStorageManagementQuickstartWorkflow.php',
     'PhabricatorStorageManagementRenamespaceWorkflow' => 'infrastructure/storage/management/workflow/PhabricatorStorageManagementRenamespaceWorkflow.php',
     'PhabricatorStorageManagementShellWorkflow' => 'infrastructure/storage/management/workflow/PhabricatorStorageManagementShellWorkflow.php',
     'PhabricatorStorageManagementStatusWorkflow' => 'infrastructure/storage/management/workflow/PhabricatorStorageManagementStatusWorkflow.php',
     'PhabricatorStorageManagementUpgradeWorkflow' => 'infrastructure/storage/management/workflow/PhabricatorStorageManagementUpgradeWorkflow.php',
     'PhabricatorStorageManagementWorkflow' => 'infrastructure/storage/management/workflow/PhabricatorStorageManagementWorkflow.php',
     'PhabricatorStoragePatch' => 'infrastructure/storage/management/PhabricatorStoragePatch.php',
     'PhabricatorStorageSchemaSpec' => 'infrastructure/storage/schema/PhabricatorStorageSchemaSpec.php',
     'PhabricatorStorageSetupCheck' => 'applications/config/check/PhabricatorStorageSetupCheck.php',
     'PhabricatorStreamingProtocolAdapter' => 'infrastructure/daemon/bot/adapter/PhabricatorStreamingProtocolAdapter.php',
     'PhabricatorStringListEditField' => 'applications/transactions/editfield/PhabricatorStringListEditField.php',
     'PhabricatorSubscribableInterface' => 'applications/subscriptions/interface/PhabricatorSubscribableInterface.php',
     'PhabricatorSubscribedToObjectEdgeType' => 'applications/transactions/edges/PhabricatorSubscribedToObjectEdgeType.php',
     'PhabricatorSubscribersEditField' => 'applications/transactions/editfield/PhabricatorSubscribersEditField.php',
     'PhabricatorSubscribersQuery' => 'applications/subscriptions/query/PhabricatorSubscribersQuery.php',
     'PhabricatorSubscriptionTriggerClock' => 'infrastructure/daemon/workers/clock/PhabricatorSubscriptionTriggerClock.php',
     'PhabricatorSubscriptionsAddSelfHeraldAction' => 'applications/subscriptions/herald/PhabricatorSubscriptionsAddSelfHeraldAction.php',
     'PhabricatorSubscriptionsAddSubscribersHeraldAction' => 'applications/subscriptions/herald/PhabricatorSubscriptionsAddSubscribersHeraldAction.php',
     'PhabricatorSubscriptionsApplication' => 'applications/subscriptions/application/PhabricatorSubscriptionsApplication.php',
     'PhabricatorSubscriptionsCurtainExtension' => 'applications/subscriptions/engineextension/PhabricatorSubscriptionsCurtainExtension.php',
     'PhabricatorSubscriptionsEditController' => 'applications/subscriptions/controller/PhabricatorSubscriptionsEditController.php',
     'PhabricatorSubscriptionsEditEngineExtension' => 'applications/subscriptions/engineextension/PhabricatorSubscriptionsEditEngineExtension.php',
     'PhabricatorSubscriptionsEditor' => 'applications/subscriptions/editor/PhabricatorSubscriptionsEditor.php',
     'PhabricatorSubscriptionsFulltextEngineExtension' => 'applications/subscriptions/engineextension/PhabricatorSubscriptionsFulltextEngineExtension.php',
     'PhabricatorSubscriptionsHeraldAction' => 'applications/subscriptions/herald/PhabricatorSubscriptionsHeraldAction.php',
     'PhabricatorSubscriptionsListController' => 'applications/subscriptions/controller/PhabricatorSubscriptionsListController.php',
     'PhabricatorSubscriptionsRemoveSelfHeraldAction' => 'applications/subscriptions/herald/PhabricatorSubscriptionsRemoveSelfHeraldAction.php',
     'PhabricatorSubscriptionsRemoveSubscribersHeraldAction' => 'applications/subscriptions/herald/PhabricatorSubscriptionsRemoveSubscribersHeraldAction.php',
     'PhabricatorSubscriptionsSearchEngineAttachment' => 'applications/subscriptions/engineextension/PhabricatorSubscriptionsSearchEngineAttachment.php',
     'PhabricatorSubscriptionsSearchEngineExtension' => 'applications/subscriptions/engineextension/PhabricatorSubscriptionsSearchEngineExtension.php',
     'PhabricatorSubscriptionsSubscribeEmailCommand' => 'applications/subscriptions/command/PhabricatorSubscriptionsSubscribeEmailCommand.php',
     'PhabricatorSubscriptionsSubscribersPolicyRule' => 'applications/subscriptions/policyrule/PhabricatorSubscriptionsSubscribersPolicyRule.php',
     'PhabricatorSubscriptionsTransactionController' => 'applications/subscriptions/controller/PhabricatorSubscriptionsTransactionController.php',
     'PhabricatorSubscriptionsUIEventListener' => 'applications/subscriptions/events/PhabricatorSubscriptionsUIEventListener.php',
     'PhabricatorSubscriptionsUnsubscribeEmailCommand' => 'applications/subscriptions/command/PhabricatorSubscriptionsUnsubscribeEmailCommand.php',
     'PhabricatorSupportApplication' => 'applications/support/application/PhabricatorSupportApplication.php',
     'PhabricatorSyntaxHighlighter' => 'infrastructure/markup/PhabricatorSyntaxHighlighter.php',
     'PhabricatorSyntaxHighlightingConfigOptions' => 'applications/config/option/PhabricatorSyntaxHighlightingConfigOptions.php',
+    'PhabricatorSyntaxStyle' => 'infrastructure/syntax/PhabricatorSyntaxStyle.php',
     'PhabricatorSystemAction' => 'applications/system/action/PhabricatorSystemAction.php',
     'PhabricatorSystemActionEngine' => 'applications/system/engine/PhabricatorSystemActionEngine.php',
     'PhabricatorSystemActionGarbageCollector' => 'applications/system/garbagecollector/PhabricatorSystemActionGarbageCollector.php',
     'PhabricatorSystemActionLog' => 'applications/system/storage/PhabricatorSystemActionLog.php',
     'PhabricatorSystemActionRateLimitException' => 'applications/system/exception/PhabricatorSystemActionRateLimitException.php',
     'PhabricatorSystemApplication' => 'applications/system/application/PhabricatorSystemApplication.php',
     'PhabricatorSystemDAO' => 'applications/system/storage/PhabricatorSystemDAO.php',
     'PhabricatorSystemDestructionGarbageCollector' => 'applications/system/garbagecollector/PhabricatorSystemDestructionGarbageCollector.php',
     'PhabricatorSystemDestructionLog' => 'applications/system/storage/PhabricatorSystemDestructionLog.php',
     'PhabricatorSystemReadOnlyController' => 'applications/system/controller/PhabricatorSystemReadOnlyController.php',
     'PhabricatorSystemRemoveDestroyWorkflow' => 'applications/system/management/PhabricatorSystemRemoveDestroyWorkflow.php',
     'PhabricatorSystemRemoveLogWorkflow' => 'applications/system/management/PhabricatorSystemRemoveLogWorkflow.php',
     'PhabricatorSystemRemoveWorkflow' => 'applications/system/management/PhabricatorSystemRemoveWorkflow.php',
     'PhabricatorSystemSelectEncodingController' => 'applications/system/controller/PhabricatorSystemSelectEncodingController.php',
     'PhabricatorSystemSelectHighlightController' => 'applications/system/controller/PhabricatorSystemSelectHighlightController.php',
     'PhabricatorTOTPAuthFactor' => 'applications/auth/factor/PhabricatorTOTPAuthFactor.php',
     'PhabricatorTOTPAuthFactorTestCase' => 'applications/auth/factor/__tests__/PhabricatorTOTPAuthFactorTestCase.php',
     'PhabricatorTaskmasterDaemon' => 'infrastructure/daemon/workers/PhabricatorTaskmasterDaemon.php',
     'PhabricatorTestApplication' => 'applications/base/controller/__tests__/PhabricatorTestApplication.php',
     'PhabricatorTestCase' => 'infrastructure/testing/PhabricatorTestCase.php',
     'PhabricatorTestController' => 'applications/base/controller/__tests__/PhabricatorTestController.php',
     'PhabricatorTestDataGenerator' => 'applications/lipsum/generator/PhabricatorTestDataGenerator.php',
     'PhabricatorTestNoCycleEdgeType' => 'applications/transactions/edges/PhabricatorTestNoCycleEdgeType.php',
     'PhabricatorTestStorageEngine' => 'applications/files/engine/PhabricatorTestStorageEngine.php',
     'PhabricatorTestWorker' => 'infrastructure/daemon/workers/__tests__/PhabricatorTestWorker.php',
     'PhabricatorTextAreaEditField' => 'applications/transactions/editfield/PhabricatorTextAreaEditField.php',
     'PhabricatorTextEditField' => 'applications/transactions/editfield/PhabricatorTextEditField.php',
     'PhabricatorTime' => 'infrastructure/time/PhabricatorTime.php',
     'PhabricatorTimeGuard' => 'infrastructure/time/PhabricatorTimeGuard.php',
     'PhabricatorTimeTestCase' => 'infrastructure/time/__tests__/PhabricatorTimeTestCase.php',
     'PhabricatorTimezoneSetupCheck' => 'applications/config/check/PhabricatorTimezoneSetupCheck.php',
     'PhabricatorToken' => 'applications/tokens/storage/PhabricatorToken.php',
     'PhabricatorTokenController' => 'applications/tokens/controller/PhabricatorTokenController.php',
     'PhabricatorTokenCount' => 'applications/tokens/storage/PhabricatorTokenCount.php',
     'PhabricatorTokenCountQuery' => 'applications/tokens/query/PhabricatorTokenCountQuery.php',
     'PhabricatorTokenDAO' => 'applications/tokens/storage/PhabricatorTokenDAO.php',
     'PhabricatorTokenDestructionEngineExtension' => 'applications/tokens/engineextension/PhabricatorTokenDestructionEngineExtension.php',
     'PhabricatorTokenGiveController' => 'applications/tokens/controller/PhabricatorTokenGiveController.php',
     'PhabricatorTokenGiven' => 'applications/tokens/storage/PhabricatorTokenGiven.php',
     'PhabricatorTokenGivenController' => 'applications/tokens/controller/PhabricatorTokenGivenController.php',
     'PhabricatorTokenGivenEditor' => 'applications/tokens/editor/PhabricatorTokenGivenEditor.php',
     'PhabricatorTokenGivenFeedStory' => 'applications/tokens/feed/PhabricatorTokenGivenFeedStory.php',
     'PhabricatorTokenGivenQuery' => 'applications/tokens/query/PhabricatorTokenGivenQuery.php',
     'PhabricatorTokenLeaderController' => 'applications/tokens/controller/PhabricatorTokenLeaderController.php',
     'PhabricatorTokenQuery' => 'applications/tokens/query/PhabricatorTokenQuery.php',
     'PhabricatorTokenReceiverInterface' => 'applications/tokens/interface/PhabricatorTokenReceiverInterface.php',
     'PhabricatorTokenReceiverQuery' => 'applications/tokens/query/PhabricatorTokenReceiverQuery.php',
     'PhabricatorTokenTokenPHIDType' => 'applications/tokens/phid/PhabricatorTokenTokenPHIDType.php',
     'PhabricatorTokenUIEventListener' => 'applications/tokens/event/PhabricatorTokenUIEventListener.php',
     'PhabricatorTokenizerEditField' => 'applications/transactions/editfield/PhabricatorTokenizerEditField.php',
     'PhabricatorTokensApplication' => 'applications/tokens/application/PhabricatorTokensApplication.php',
     'PhabricatorTokensCurtainExtension' => 'applications/tokens/engineextension/PhabricatorTokensCurtainExtension.php',
     'PhabricatorTokensSettingsPanel' => 'applications/settings/panel/PhabricatorTokensSettingsPanel.php',
     'PhabricatorTooltipUIExample' => 'applications/uiexample/examples/PhabricatorTooltipUIExample.php',
     'PhabricatorTransactions' => 'applications/transactions/constants/PhabricatorTransactions.php',
     'PhabricatorTransactionsApplication' => 'applications/transactions/application/PhabricatorTransactionsApplication.php',
     'PhabricatorTransactionsDestructionEngineExtension' => 'applications/transactions/engineextension/PhabricatorTransactionsDestructionEngineExtension.php',
     'PhabricatorTransactionsFulltextEngineExtension' => 'applications/transactions/engineextension/PhabricatorTransactionsFulltextEngineExtension.php',
     'PhabricatorTransformedFile' => 'applications/files/storage/PhabricatorTransformedFile.php',
     'PhabricatorTranslationsConfigOptions' => 'applications/config/option/PhabricatorTranslationsConfigOptions.php',
     'PhabricatorTriggerAction' => 'infrastructure/daemon/workers/action/PhabricatorTriggerAction.php',
     'PhabricatorTriggerClock' => 'infrastructure/daemon/workers/clock/PhabricatorTriggerClock.php',
     'PhabricatorTriggerClockTestCase' => 'infrastructure/daemon/workers/clock/__tests__/PhabricatorTriggerClockTestCase.php',
     'PhabricatorTriggerDaemon' => 'infrastructure/daemon/workers/PhabricatorTriggerDaemon.php',
     'PhabricatorTrivialTestCase' => 'infrastructure/testing/__tests__/PhabricatorTrivialTestCase.php',
     'PhabricatorTwitchAuthProvider' => 'applications/auth/provider/PhabricatorTwitchAuthProvider.php',
     'PhabricatorTwitterAuthProvider' => 'applications/auth/provider/PhabricatorTwitterAuthProvider.php',
     'PhabricatorTypeaheadApplication' => 'applications/typeahead/application/PhabricatorTypeaheadApplication.php',
     'PhabricatorTypeaheadCompositeDatasource' => 'applications/typeahead/datasource/PhabricatorTypeaheadCompositeDatasource.php',
     'PhabricatorTypeaheadDatasource' => 'applications/typeahead/datasource/PhabricatorTypeaheadDatasource.php',
     'PhabricatorTypeaheadDatasourceController' => 'applications/typeahead/controller/PhabricatorTypeaheadDatasourceController.php',
     'PhabricatorTypeaheadFunctionHelpController' => 'applications/typeahead/controller/PhabricatorTypeaheadFunctionHelpController.php',
     'PhabricatorTypeaheadInvalidTokenException' => 'applications/typeahead/exception/PhabricatorTypeaheadInvalidTokenException.php',
     'PhabricatorTypeaheadModularDatasourceController' => 'applications/typeahead/controller/PhabricatorTypeaheadModularDatasourceController.php',
     'PhabricatorTypeaheadMonogramDatasource' => 'applications/typeahead/datasource/PhabricatorTypeaheadMonogramDatasource.php',
     'PhabricatorTypeaheadResult' => 'applications/typeahead/storage/PhabricatorTypeaheadResult.php',
     'PhabricatorTypeaheadRuntimeCompositeDatasource' => 'applications/typeahead/datasource/PhabricatorTypeaheadRuntimeCompositeDatasource.php',
     'PhabricatorTypeaheadTokenView' => 'applications/typeahead/view/PhabricatorTypeaheadTokenView.php',
     'PhabricatorUIConfigOptions' => 'applications/config/option/PhabricatorUIConfigOptions.php',
     'PhabricatorUIExample' => 'applications/uiexample/examples/PhabricatorUIExample.php',
     'PhabricatorUIExampleRenderController' => 'applications/uiexample/controller/PhabricatorUIExampleRenderController.php',
     'PhabricatorUIExamplesApplication' => 'applications/uiexample/application/PhabricatorUIExamplesApplication.php',
     'PhabricatorUSEnglishTranslation' => 'infrastructure/internationalization/translation/PhabricatorUSEnglishTranslation.php',
     'PhabricatorUnitTestContentSource' => 'infrastructure/contentsource/PhabricatorUnitTestContentSource.php',
     'PhabricatorUnitsTestCase' => 'view/__tests__/PhabricatorUnitsTestCase.php',
     'PhabricatorUnknownContentSource' => 'infrastructure/contentsource/PhabricatorUnknownContentSource.php',
     'PhabricatorUnsubscribedFromObjectEdgeType' => 'applications/transactions/edges/PhabricatorUnsubscribedFromObjectEdgeType.php',
     'PhabricatorUser' => 'applications/people/storage/PhabricatorUser.php',
     'PhabricatorUserBlurbField' => 'applications/people/customfield/PhabricatorUserBlurbField.php',
     'PhabricatorUserCardView' => 'applications/people/view/PhabricatorUserCardView.php',
     'PhabricatorUserConfigOptions' => 'applications/people/config/PhabricatorUserConfigOptions.php',
     'PhabricatorUserConfiguredCustomField' => 'applications/people/customfield/PhabricatorUserConfiguredCustomField.php',
     'PhabricatorUserConfiguredCustomFieldStorage' => 'applications/people/storage/PhabricatorUserConfiguredCustomFieldStorage.php',
     'PhabricatorUserCustomField' => 'applications/people/customfield/PhabricatorUserCustomField.php',
     'PhabricatorUserCustomFieldNumericIndex' => 'applications/people/storage/PhabricatorUserCustomFieldNumericIndex.php',
     'PhabricatorUserCustomFieldStringIndex' => 'applications/people/storage/PhabricatorUserCustomFieldStringIndex.php',
     'PhabricatorUserDAO' => 'applications/people/storage/PhabricatorUserDAO.php',
     'PhabricatorUserEditor' => 'applications/people/editor/PhabricatorUserEditor.php',
     'PhabricatorUserEditorTestCase' => 'applications/people/editor/__tests__/PhabricatorUserEditorTestCase.php',
     'PhabricatorUserEmail' => 'applications/people/storage/PhabricatorUserEmail.php',
     'PhabricatorUserEmailTestCase' => 'applications/people/storage/__tests__/PhabricatorUserEmailTestCase.php',
     'PhabricatorUserFulltextEngine' => 'applications/people/search/PhabricatorUserFulltextEngine.php',
     'PhabricatorUserIconField' => 'applications/people/customfield/PhabricatorUserIconField.php',
     'PhabricatorUserLog' => 'applications/people/storage/PhabricatorUserLog.php',
     'PhabricatorUserLogView' => 'applications/people/view/PhabricatorUserLogView.php',
     'PhabricatorUserPHIDResolver' => 'applications/phid/resolver/PhabricatorUserPHIDResolver.php',
     'PhabricatorUserPreferences' => 'applications/settings/storage/PhabricatorUserPreferences.php',
     'PhabricatorUserProfile' => 'applications/people/storage/PhabricatorUserProfile.php',
     'PhabricatorUserProfileEditor' => 'applications/people/editor/PhabricatorUserProfileEditor.php',
     'PhabricatorUserRealNameField' => 'applications/people/customfield/PhabricatorUserRealNameField.php',
     'PhabricatorUserRolesField' => 'applications/people/customfield/PhabricatorUserRolesField.php',
     'PhabricatorUserSchemaSpec' => 'applications/people/storage/PhabricatorUserSchemaSpec.php',
     'PhabricatorUserSinceField' => 'applications/people/customfield/PhabricatorUserSinceField.php',
     'PhabricatorUserStatusField' => 'applications/people/customfield/PhabricatorUserStatusField.php',
     'PhabricatorUserTestCase' => 'applications/people/storage/__tests__/PhabricatorUserTestCase.php',
     'PhabricatorUserTitleField' => 'applications/people/customfield/PhabricatorUserTitleField.php',
     'PhabricatorUserTransaction' => 'applications/people/storage/PhabricatorUserTransaction.php',
     'PhabricatorUsersEditField' => 'applications/transactions/editfield/PhabricatorUsersEditField.php',
     'PhabricatorUsersPolicyRule' => 'applications/people/policyrule/PhabricatorUsersPolicyRule.php',
     'PhabricatorUsersSearchField' => 'applications/people/searchfield/PhabricatorUsersSearchField.php',
     'PhabricatorVCSResponse' => 'applications/repository/response/PhabricatorVCSResponse.php',
     'PhabricatorVersionedDraft' => 'applications/draft/storage/PhabricatorVersionedDraft.php',
     'PhabricatorVeryWowEnglishTranslation' => 'infrastructure/internationalization/translation/PhabricatorVeryWowEnglishTranslation.php',
     'PhabricatorViewerDatasource' => 'applications/people/typeahead/PhabricatorViewerDatasource.php',
     'PhabricatorWatcherHasObjectEdgeType' => 'applications/transactions/edges/PhabricatorWatcherHasObjectEdgeType.php',
     'PhabricatorWebContentSource' => 'infrastructure/contentsource/PhabricatorWebContentSource.php',
     'PhabricatorWordPressAuthProvider' => 'applications/auth/provider/PhabricatorWordPressAuthProvider.php',
     'PhabricatorWorker' => 'infrastructure/daemon/workers/PhabricatorWorker.php',
     'PhabricatorWorkerActiveTask' => 'infrastructure/daemon/workers/storage/PhabricatorWorkerActiveTask.php',
     'PhabricatorWorkerArchiveTask' => 'infrastructure/daemon/workers/storage/PhabricatorWorkerArchiveTask.php',
     'PhabricatorWorkerArchiveTaskQuery' => 'infrastructure/daemon/workers/query/PhabricatorWorkerArchiveTaskQuery.php',
     'PhabricatorWorkerBulkJob' => 'infrastructure/daemon/workers/storage/PhabricatorWorkerBulkJob.php',
     'PhabricatorWorkerBulkJobCreateWorker' => 'infrastructure/daemon/workers/bulk/PhabricatorWorkerBulkJobCreateWorker.php',
     'PhabricatorWorkerBulkJobEditor' => 'infrastructure/daemon/workers/editor/PhabricatorWorkerBulkJobEditor.php',
     'PhabricatorWorkerBulkJobPHIDType' => 'infrastructure/daemon/workers/phid/PhabricatorWorkerBulkJobPHIDType.php',
     'PhabricatorWorkerBulkJobQuery' => 'infrastructure/daemon/workers/query/PhabricatorWorkerBulkJobQuery.php',
     'PhabricatorWorkerBulkJobSearchEngine' => 'infrastructure/daemon/workers/query/PhabricatorWorkerBulkJobSearchEngine.php',
     'PhabricatorWorkerBulkJobTaskWorker' => 'infrastructure/daemon/workers/bulk/PhabricatorWorkerBulkJobTaskWorker.php',
     'PhabricatorWorkerBulkJobTestCase' => 'infrastructure/daemon/workers/__tests__/PhabricatorWorkerBulkJobTestCase.php',
     'PhabricatorWorkerBulkJobTransaction' => 'infrastructure/daemon/workers/storage/PhabricatorWorkerBulkJobTransaction.php',
     'PhabricatorWorkerBulkJobTransactionQuery' => 'infrastructure/daemon/workers/query/PhabricatorWorkerBulkJobTransactionQuery.php',
     'PhabricatorWorkerBulkJobType' => 'infrastructure/daemon/workers/bulk/PhabricatorWorkerBulkJobType.php',
     'PhabricatorWorkerBulkJobWorker' => 'infrastructure/daemon/workers/bulk/PhabricatorWorkerBulkJobWorker.php',
     'PhabricatorWorkerBulkTask' => 'infrastructure/daemon/workers/storage/PhabricatorWorkerBulkTask.php',
     'PhabricatorWorkerDAO' => 'infrastructure/daemon/workers/storage/PhabricatorWorkerDAO.php',
     'PhabricatorWorkerDestructionEngineExtension' => 'infrastructure/daemon/workers/engineextension/PhabricatorWorkerDestructionEngineExtension.php',
     'PhabricatorWorkerLeaseQuery' => 'infrastructure/daemon/workers/query/PhabricatorWorkerLeaseQuery.php',
     'PhabricatorWorkerManagementCancelWorkflow' => 'infrastructure/daemon/workers/management/PhabricatorWorkerManagementCancelWorkflow.php',
     'PhabricatorWorkerManagementExecuteWorkflow' => 'infrastructure/daemon/workers/management/PhabricatorWorkerManagementExecuteWorkflow.php',
     'PhabricatorWorkerManagementFloodWorkflow' => 'infrastructure/daemon/workers/management/PhabricatorWorkerManagementFloodWorkflow.php',
     'PhabricatorWorkerManagementFreeWorkflow' => 'infrastructure/daemon/workers/management/PhabricatorWorkerManagementFreeWorkflow.php',
     'PhabricatorWorkerManagementRetryWorkflow' => 'infrastructure/daemon/workers/management/PhabricatorWorkerManagementRetryWorkflow.php',
     'PhabricatorWorkerManagementWorkflow' => 'infrastructure/daemon/workers/management/PhabricatorWorkerManagementWorkflow.php',
     'PhabricatorWorkerPermanentFailureException' => 'infrastructure/daemon/workers/exception/PhabricatorWorkerPermanentFailureException.php',
     'PhabricatorWorkerSchemaSpec' => 'infrastructure/daemon/workers/storage/PhabricatorWorkerSchemaSpec.php',
     'PhabricatorWorkerTask' => 'infrastructure/daemon/workers/storage/PhabricatorWorkerTask.php',
     'PhabricatorWorkerTaskData' => 'infrastructure/daemon/workers/storage/PhabricatorWorkerTaskData.php',
     'PhabricatorWorkerTaskDetailController' => 'applications/daemon/controller/PhabricatorWorkerTaskDetailController.php',
     'PhabricatorWorkerTestCase' => 'infrastructure/daemon/workers/__tests__/PhabricatorWorkerTestCase.php',
     'PhabricatorWorkerTrigger' => 'infrastructure/daemon/workers/storage/PhabricatorWorkerTrigger.php',
     'PhabricatorWorkerTriggerEvent' => 'infrastructure/daemon/workers/storage/PhabricatorWorkerTriggerEvent.php',
     'PhabricatorWorkerTriggerManagementFireWorkflow' => 'infrastructure/daemon/workers/management/PhabricatorWorkerTriggerManagementFireWorkflow.php',
     'PhabricatorWorkerTriggerManagementWorkflow' => 'infrastructure/daemon/workers/management/PhabricatorWorkerTriggerManagementWorkflow.php',
     'PhabricatorWorkerTriggerPHIDType' => 'infrastructure/daemon/workers/phid/PhabricatorWorkerTriggerPHIDType.php',
     'PhabricatorWorkerTriggerQuery' => 'infrastructure/daemon/workers/query/PhabricatorWorkerTriggerQuery.php',
     'PhabricatorWorkerYieldException' => 'infrastructure/daemon/workers/exception/PhabricatorWorkerYieldException.php',
     'PhabricatorWorkingCopyDiscoveryTestCase' => 'applications/repository/engine/__tests__/PhabricatorWorkingCopyDiscoveryTestCase.php',
     'PhabricatorWorkingCopyPullTestCase' => 'applications/repository/engine/__tests__/PhabricatorWorkingCopyPullTestCase.php',
     'PhabricatorWorkingCopyTestCase' => 'applications/repository/engine/__tests__/PhabricatorWorkingCopyTestCase.php',
     'PhabricatorXHPASTDAO' => 'applications/phpast/storage/PhabricatorXHPASTDAO.php',
     'PhabricatorXHPASTParseTree' => 'applications/phpast/storage/PhabricatorXHPASTParseTree.php',
     'PhabricatorXHPASTViewController' => 'applications/phpast/controller/PhabricatorXHPASTViewController.php',
     'PhabricatorXHPASTViewFrameController' => 'applications/phpast/controller/PhabricatorXHPASTViewFrameController.php',
     'PhabricatorXHPASTViewFramesetController' => 'applications/phpast/controller/PhabricatorXHPASTViewFramesetController.php',
     'PhabricatorXHPASTViewInputController' => 'applications/phpast/controller/PhabricatorXHPASTViewInputController.php',
     'PhabricatorXHPASTViewPanelController' => 'applications/phpast/controller/PhabricatorXHPASTViewPanelController.php',
     'PhabricatorXHPASTViewRunController' => 'applications/phpast/controller/PhabricatorXHPASTViewRunController.php',
     'PhabricatorXHPASTViewStreamController' => 'applications/phpast/controller/PhabricatorXHPASTViewStreamController.php',
     'PhabricatorXHPASTViewTreeController' => 'applications/phpast/controller/PhabricatorXHPASTViewTreeController.php',
     'PhabricatorXHProfApplication' => 'applications/xhprof/application/PhabricatorXHProfApplication.php',
     'PhabricatorXHProfController' => 'applications/xhprof/controller/PhabricatorXHProfController.php',
     'PhabricatorXHProfDAO' => 'applications/xhprof/storage/PhabricatorXHProfDAO.php',
     'PhabricatorXHProfProfileController' => 'applications/xhprof/controller/PhabricatorXHProfProfileController.php',
     'PhabricatorXHProfProfileSymbolView' => 'applications/xhprof/view/PhabricatorXHProfProfileSymbolView.php',
     'PhabricatorXHProfProfileTopLevelView' => 'applications/xhprof/view/PhabricatorXHProfProfileTopLevelView.php',
     'PhabricatorXHProfProfileView' => 'applications/xhprof/view/PhabricatorXHProfProfileView.php',
     'PhabricatorXHProfSample' => 'applications/xhprof/storage/PhabricatorXHProfSample.php',
     'PhabricatorXHProfSampleListController' => 'applications/xhprof/controller/PhabricatorXHProfSampleListController.php',
     'PhabricatorYoutubeRemarkupRule' => 'infrastructure/markup/rule/PhabricatorYoutubeRemarkupRule.php',
     'PhameBlog' => 'applications/phame/storage/PhameBlog.php',
     'PhameBlogArchiveController' => 'applications/phame/controller/blog/PhameBlogArchiveController.php',
     'PhameBlogController' => 'applications/phame/controller/blog/PhameBlogController.php',
     'PhameBlogCreateCapability' => 'applications/phame/capability/PhameBlogCreateCapability.php',
     'PhameBlogEditConduitAPIMethod' => 'applications/phame/conduit/PhameBlogEditConduitAPIMethod.php',
     'PhameBlogEditController' => 'applications/phame/controller/blog/PhameBlogEditController.php',
     'PhameBlogEditEngine' => 'applications/phame/editor/PhameBlogEditEngine.php',
     'PhameBlogEditor' => 'applications/phame/editor/PhameBlogEditor.php',
     'PhameBlogFeedController' => 'applications/phame/controller/blog/PhameBlogFeedController.php',
     'PhameBlogListController' => 'applications/phame/controller/blog/PhameBlogListController.php',
     'PhameBlogListView' => 'applications/phame/view/PhameBlogListView.php',
     'PhameBlogManageController' => 'applications/phame/controller/blog/PhameBlogManageController.php',
     'PhameBlogProfilePictureController' => 'applications/phame/controller/blog/PhameBlogProfilePictureController.php',
     'PhameBlogQuery' => 'applications/phame/query/PhameBlogQuery.php',
     'PhameBlogReplyHandler' => 'applications/phame/mail/PhameBlogReplyHandler.php',
     'PhameBlogSearchConduitAPIMethod' => 'applications/phame/conduit/PhameBlogSearchConduitAPIMethod.php',
     'PhameBlogSearchEngine' => 'applications/phame/query/PhameBlogSearchEngine.php',
     'PhameBlogSite' => 'applications/phame/site/PhameBlogSite.php',
     'PhameBlogTransaction' => 'applications/phame/storage/PhameBlogTransaction.php',
     'PhameBlogTransactionQuery' => 'applications/phame/query/PhameBlogTransactionQuery.php',
     'PhameBlogViewController' => 'applications/phame/controller/blog/PhameBlogViewController.php',
     'PhameConstants' => 'applications/phame/constants/PhameConstants.php',
     'PhameController' => 'applications/phame/controller/PhameController.php',
     'PhameDAO' => 'applications/phame/storage/PhameDAO.php',
     'PhameDescriptionView' => 'applications/phame/view/PhameDescriptionView.php',
     'PhameDraftListView' => 'applications/phame/view/PhameDraftListView.php',
     'PhameHomeController' => 'applications/phame/controller/PhameHomeController.php',
     'PhameLiveController' => 'applications/phame/controller/PhameLiveController.php',
     'PhameNextPostView' => 'applications/phame/view/PhameNextPostView.php',
     'PhamePost' => 'applications/phame/storage/PhamePost.php',
     'PhamePostCommentController' => 'applications/phame/controller/post/PhamePostCommentController.php',
     'PhamePostController' => 'applications/phame/controller/post/PhamePostController.php',
     'PhamePostEditConduitAPIMethod' => 'applications/phame/conduit/PhamePostEditConduitAPIMethod.php',
     'PhamePostEditController' => 'applications/phame/controller/post/PhamePostEditController.php',
     'PhamePostEditEngine' => 'applications/phame/editor/PhamePostEditEngine.php',
     'PhamePostEditor' => 'applications/phame/editor/PhamePostEditor.php',
     'PhamePostHistoryController' => 'applications/phame/controller/post/PhamePostHistoryController.php',
     'PhamePostListController' => 'applications/phame/controller/post/PhamePostListController.php',
     'PhamePostListView' => 'applications/phame/view/PhamePostListView.php',
     'PhamePostMailReceiver' => 'applications/phame/mail/PhamePostMailReceiver.php',
     'PhamePostMoveController' => 'applications/phame/controller/post/PhamePostMoveController.php',
     'PhamePostPublishController' => 'applications/phame/controller/post/PhamePostPublishController.php',
     'PhamePostQuery' => 'applications/phame/query/PhamePostQuery.php',
     'PhamePostReplyHandler' => 'applications/phame/mail/PhamePostReplyHandler.php',
     'PhamePostSearchConduitAPIMethod' => 'applications/phame/conduit/PhamePostSearchConduitAPIMethod.php',
     'PhamePostSearchEngine' => 'applications/phame/query/PhamePostSearchEngine.php',
     'PhamePostTransaction' => 'applications/phame/storage/PhamePostTransaction.php',
     'PhamePostTransactionComment' => 'applications/phame/storage/PhamePostTransactionComment.php',
     'PhamePostTransactionQuery' => 'applications/phame/query/PhamePostTransactionQuery.php',
     'PhamePostViewController' => 'applications/phame/controller/post/PhamePostViewController.php',
     'PhameSchemaSpec' => 'applications/phame/storage/PhameSchemaSpec.php',
     'PhameSite' => 'applications/phame/site/PhameSite.php',
     'PhluxController' => 'applications/phlux/controller/PhluxController.php',
     'PhluxDAO' => 'applications/phlux/storage/PhluxDAO.php',
     'PhluxEditController' => 'applications/phlux/controller/PhluxEditController.php',
     'PhluxListController' => 'applications/phlux/controller/PhluxListController.php',
     'PhluxTransaction' => 'applications/phlux/storage/PhluxTransaction.php',
     'PhluxTransactionQuery' => 'applications/phlux/query/PhluxTransactionQuery.php',
     'PhluxVariable' => 'applications/phlux/storage/PhluxVariable.php',
     'PhluxVariableEditor' => 'applications/phlux/editor/PhluxVariableEditor.php',
     'PhluxVariablePHIDType' => 'applications/phlux/phid/PhluxVariablePHIDType.php',
     'PhluxVariableQuery' => 'applications/phlux/query/PhluxVariableQuery.php',
     'PhluxViewController' => 'applications/phlux/controller/PhluxViewController.php',
     'PholioActionMenuEventListener' => 'applications/pholio/event/PholioActionMenuEventListener.php',
     'PholioController' => 'applications/pholio/controller/PholioController.php',
     'PholioDAO' => 'applications/pholio/storage/PholioDAO.php',
     'PholioDefaultEditCapability' => 'applications/pholio/capability/PholioDefaultEditCapability.php',
     'PholioDefaultViewCapability' => 'applications/pholio/capability/PholioDefaultViewCapability.php',
     'PholioImage' => 'applications/pholio/storage/PholioImage.php',
     'PholioImagePHIDType' => 'applications/pholio/phid/PholioImagePHIDType.php',
     'PholioImageQuery' => 'applications/pholio/query/PholioImageQuery.php',
     'PholioImageUploadController' => 'applications/pholio/controller/PholioImageUploadController.php',
     'PholioInlineController' => 'applications/pholio/controller/PholioInlineController.php',
     'PholioInlineListController' => 'applications/pholio/controller/PholioInlineListController.php',
     'PholioMock' => 'applications/pholio/storage/PholioMock.php',
     'PholioMockArchiveController' => 'applications/pholio/controller/PholioMockArchiveController.php',
     'PholioMockAuthorHeraldField' => 'applications/pholio/herald/PholioMockAuthorHeraldField.php',
     'PholioMockCommentController' => 'applications/pholio/controller/PholioMockCommentController.php',
     'PholioMockDescriptionHeraldField' => 'applications/pholio/herald/PholioMockDescriptionHeraldField.php',
     'PholioMockEditController' => 'applications/pholio/controller/PholioMockEditController.php',
     'PholioMockEditor' => 'applications/pholio/editor/PholioMockEditor.php',
     'PholioMockEmbedView' => 'applications/pholio/view/PholioMockEmbedView.php',
     'PholioMockFulltextEngine' => 'applications/pholio/search/PholioMockFulltextEngine.php',
     'PholioMockHasTaskEdgeType' => 'applications/pholio/edge/PholioMockHasTaskEdgeType.php',
     'PholioMockHeraldField' => 'applications/pholio/herald/PholioMockHeraldField.php',
     'PholioMockHeraldFieldGroup' => 'applications/pholio/herald/PholioMockHeraldFieldGroup.php',
     'PholioMockImagesView' => 'applications/pholio/view/PholioMockImagesView.php',
     'PholioMockListController' => 'applications/pholio/controller/PholioMockListController.php',
     'PholioMockMailReceiver' => 'applications/pholio/mail/PholioMockMailReceiver.php',
     'PholioMockNameHeraldField' => 'applications/pholio/herald/PholioMockNameHeraldField.php',
     'PholioMockPHIDType' => 'applications/pholio/phid/PholioMockPHIDType.php',
     'PholioMockQuery' => 'applications/pholio/query/PholioMockQuery.php',
     'PholioMockSearchEngine' => 'applications/pholio/query/PholioMockSearchEngine.php',
     'PholioMockThumbGridView' => 'applications/pholio/view/PholioMockThumbGridView.php',
     'PholioMockViewController' => 'applications/pholio/controller/PholioMockViewController.php',
     'PholioRemarkupRule' => 'applications/pholio/remarkup/PholioRemarkupRule.php',
     'PholioReplyHandler' => 'applications/pholio/mail/PholioReplyHandler.php',
     'PholioSchemaSpec' => 'applications/pholio/storage/PholioSchemaSpec.php',
     'PholioTransaction' => 'applications/pholio/storage/PholioTransaction.php',
     'PholioTransactionComment' => 'applications/pholio/storage/PholioTransactionComment.php',
     'PholioTransactionQuery' => 'applications/pholio/query/PholioTransactionQuery.php',
     'PholioTransactionView' => 'applications/pholio/view/PholioTransactionView.php',
     'PholioUploadedImageView' => 'applications/pholio/view/PholioUploadedImageView.php',
     'PhortuneAccount' => 'applications/phortune/storage/PhortuneAccount.php',
     'PhortuneAccountEditController' => 'applications/phortune/controller/PhortuneAccountEditController.php',
     'PhortuneAccountEditor' => 'applications/phortune/editor/PhortuneAccountEditor.php',
     'PhortuneAccountHasMemberEdgeType' => 'applications/phortune/edge/PhortuneAccountHasMemberEdgeType.php',
     'PhortuneAccountListController' => 'applications/phortune/controller/PhortuneAccountListController.php',
     'PhortuneAccountPHIDType' => 'applications/phortune/phid/PhortuneAccountPHIDType.php',
     'PhortuneAccountQuery' => 'applications/phortune/query/PhortuneAccountQuery.php',
     'PhortuneAccountTransaction' => 'applications/phortune/storage/PhortuneAccountTransaction.php',
     'PhortuneAccountTransactionQuery' => 'applications/phortune/query/PhortuneAccountTransactionQuery.php',
     'PhortuneAccountViewController' => 'applications/phortune/controller/PhortuneAccountViewController.php',
     'PhortuneAdHocCart' => 'applications/phortune/cart/PhortuneAdHocCart.php',
     'PhortuneAdHocProduct' => 'applications/phortune/product/PhortuneAdHocProduct.php',
     'PhortuneCart' => 'applications/phortune/storage/PhortuneCart.php',
     'PhortuneCartAcceptController' => 'applications/phortune/controller/PhortuneCartAcceptController.php',
     'PhortuneCartCancelController' => 'applications/phortune/controller/PhortuneCartCancelController.php',
     'PhortuneCartCheckoutController' => 'applications/phortune/controller/PhortuneCartCheckoutController.php',
     'PhortuneCartController' => 'applications/phortune/controller/PhortuneCartController.php',
     'PhortuneCartEditor' => 'applications/phortune/editor/PhortuneCartEditor.php',
     'PhortuneCartImplementation' => 'applications/phortune/cart/PhortuneCartImplementation.php',
     'PhortuneCartListController' => 'applications/phortune/controller/PhortuneCartListController.php',
     'PhortuneCartPHIDType' => 'applications/phortune/phid/PhortuneCartPHIDType.php',
     'PhortuneCartQuery' => 'applications/phortune/query/PhortuneCartQuery.php',
     'PhortuneCartReplyHandler' => 'applications/phortune/mail/PhortuneCartReplyHandler.php',
     'PhortuneCartSearchEngine' => 'applications/phortune/query/PhortuneCartSearchEngine.php',
     'PhortuneCartTransaction' => 'applications/phortune/storage/PhortuneCartTransaction.php',
     'PhortuneCartTransactionQuery' => 'applications/phortune/query/PhortuneCartTransactionQuery.php',
     'PhortuneCartUpdateController' => 'applications/phortune/controller/PhortuneCartUpdateController.php',
     'PhortuneCartViewController' => 'applications/phortune/controller/PhortuneCartViewController.php',
     'PhortuneCharge' => 'applications/phortune/storage/PhortuneCharge.php',
     'PhortuneChargeListController' => 'applications/phortune/controller/PhortuneChargeListController.php',
     'PhortuneChargePHIDType' => 'applications/phortune/phid/PhortuneChargePHIDType.php',
     'PhortuneChargeQuery' => 'applications/phortune/query/PhortuneChargeQuery.php',
     'PhortuneChargeSearchEngine' => 'applications/phortune/query/PhortuneChargeSearchEngine.php',
     'PhortuneChargeTableView' => 'applications/phortune/view/PhortuneChargeTableView.php',
     'PhortuneConstants' => 'applications/phortune/constants/PhortuneConstants.php',
     'PhortuneController' => 'applications/phortune/controller/PhortuneController.php',
     'PhortuneCreditCardForm' => 'applications/phortune/view/PhortuneCreditCardForm.php',
     'PhortuneCurrency' => 'applications/phortune/currency/PhortuneCurrency.php',
     'PhortuneCurrencySerializer' => 'applications/phortune/currency/PhortuneCurrencySerializer.php',
     'PhortuneCurrencyTestCase' => 'applications/phortune/currency/__tests__/PhortuneCurrencyTestCase.php',
     'PhortuneDAO' => 'applications/phortune/storage/PhortuneDAO.php',
     'PhortuneErrCode' => 'applications/phortune/constants/PhortuneErrCode.php',
     'PhortuneLandingController' => 'applications/phortune/controller/PhortuneLandingController.php',
     'PhortuneMemberHasAccountEdgeType' => 'applications/phortune/edge/PhortuneMemberHasAccountEdgeType.php',
     'PhortuneMemberHasMerchantEdgeType' => 'applications/phortune/edge/PhortuneMemberHasMerchantEdgeType.php',
     'PhortuneMerchant' => 'applications/phortune/storage/PhortuneMerchant.php',
     'PhortuneMerchantCapability' => 'applications/phortune/capability/PhortuneMerchantCapability.php',
     'PhortuneMerchantController' => 'applications/phortune/controller/PhortuneMerchantController.php',
     'PhortuneMerchantEditController' => 'applications/phortune/controller/PhortuneMerchantEditController.php',
     'PhortuneMerchantEditor' => 'applications/phortune/editor/PhortuneMerchantEditor.php',
     'PhortuneMerchantHasMemberEdgeType' => 'applications/phortune/edge/PhortuneMerchantHasMemberEdgeType.php',
     'PhortuneMerchantInvoiceCreateController' => 'applications/phortune/controller/PhortuneMerchantInvoiceCreateController.php',
     'PhortuneMerchantListController' => 'applications/phortune/controller/PhortuneMerchantListController.php',
     'PhortuneMerchantPHIDType' => 'applications/phortune/phid/PhortuneMerchantPHIDType.php',
     'PhortuneMerchantQuery' => 'applications/phortune/query/PhortuneMerchantQuery.php',
     'PhortuneMerchantSearchEngine' => 'applications/phortune/query/PhortuneMerchantSearchEngine.php',
     'PhortuneMerchantTransaction' => 'applications/phortune/storage/PhortuneMerchantTransaction.php',
     'PhortuneMerchantTransactionQuery' => 'applications/phortune/query/PhortuneMerchantTransactionQuery.php',
     'PhortuneMerchantViewController' => 'applications/phortune/controller/PhortuneMerchantViewController.php',
     'PhortuneMonthYearExpiryControl' => 'applications/phortune/control/PhortuneMonthYearExpiryControl.php',
     'PhortuneOrderTableView' => 'applications/phortune/view/PhortuneOrderTableView.php',
     'PhortunePayPalPaymentProvider' => 'applications/phortune/provider/PhortunePayPalPaymentProvider.php',
     'PhortunePaymentMethod' => 'applications/phortune/storage/PhortunePaymentMethod.php',
     'PhortunePaymentMethodCreateController' => 'applications/phortune/controller/PhortunePaymentMethodCreateController.php',
     'PhortunePaymentMethodDisableController' => 'applications/phortune/controller/PhortunePaymentMethodDisableController.php',
     'PhortunePaymentMethodEditController' => 'applications/phortune/controller/PhortunePaymentMethodEditController.php',
     'PhortunePaymentMethodPHIDType' => 'applications/phortune/phid/PhortunePaymentMethodPHIDType.php',
     'PhortunePaymentMethodQuery' => 'applications/phortune/query/PhortunePaymentMethodQuery.php',
     'PhortunePaymentProvider' => 'applications/phortune/provider/PhortunePaymentProvider.php',
     'PhortunePaymentProviderConfig' => 'applications/phortune/storage/PhortunePaymentProviderConfig.php',
     'PhortunePaymentProviderConfigEditor' => 'applications/phortune/editor/PhortunePaymentProviderConfigEditor.php',
     'PhortunePaymentProviderConfigQuery' => 'applications/phortune/query/PhortunePaymentProviderConfigQuery.php',
     'PhortunePaymentProviderConfigTransaction' => 'applications/phortune/storage/PhortunePaymentProviderConfigTransaction.php',
     'PhortunePaymentProviderConfigTransactionQuery' => 'applications/phortune/query/PhortunePaymentProviderConfigTransactionQuery.php',
     'PhortunePaymentProviderPHIDType' => 'applications/phortune/phid/PhortunePaymentProviderPHIDType.php',
     'PhortunePaymentProviderTestCase' => 'applications/phortune/provider/__tests__/PhortunePaymentProviderTestCase.php',
     'PhortuneProduct' => 'applications/phortune/storage/PhortuneProduct.php',
     'PhortuneProductImplementation' => 'applications/phortune/product/PhortuneProductImplementation.php',
     'PhortuneProductListController' => 'applications/phortune/controller/PhortuneProductListController.php',
     'PhortuneProductPHIDType' => 'applications/phortune/phid/PhortuneProductPHIDType.php',
     'PhortuneProductQuery' => 'applications/phortune/query/PhortuneProductQuery.php',
     'PhortuneProductViewController' => 'applications/phortune/controller/PhortuneProductViewController.php',
     'PhortuneProviderActionController' => 'applications/phortune/controller/PhortuneProviderActionController.php',
     'PhortuneProviderDisableController' => 'applications/phortune/controller/PhortuneProviderDisableController.php',
     'PhortuneProviderEditController' => 'applications/phortune/controller/PhortuneProviderEditController.php',
     'PhortunePurchase' => 'applications/phortune/storage/PhortunePurchase.php',
     'PhortunePurchasePHIDType' => 'applications/phortune/phid/PhortunePurchasePHIDType.php',
     'PhortunePurchaseQuery' => 'applications/phortune/query/PhortunePurchaseQuery.php',
     'PhortuneSchemaSpec' => 'applications/phortune/storage/PhortuneSchemaSpec.php',
     'PhortuneStripePaymentProvider' => 'applications/phortune/provider/PhortuneStripePaymentProvider.php',
     'PhortuneSubscription' => 'applications/phortune/storage/PhortuneSubscription.php',
     'PhortuneSubscriptionCart' => 'applications/phortune/cart/PhortuneSubscriptionCart.php',
     'PhortuneSubscriptionEditController' => 'applications/phortune/controller/PhortuneSubscriptionEditController.php',
     'PhortuneSubscriptionImplementation' => 'applications/phortune/subscription/PhortuneSubscriptionImplementation.php',
     'PhortuneSubscriptionListController' => 'applications/phortune/controller/PhortuneSubscriptionListController.php',
     'PhortuneSubscriptionPHIDType' => 'applications/phortune/phid/PhortuneSubscriptionPHIDType.php',
     'PhortuneSubscriptionProduct' => 'applications/phortune/product/PhortuneSubscriptionProduct.php',
     'PhortuneSubscriptionQuery' => 'applications/phortune/query/PhortuneSubscriptionQuery.php',
     'PhortuneSubscriptionSearchEngine' => 'applications/phortune/query/PhortuneSubscriptionSearchEngine.php',
     'PhortuneSubscriptionTableView' => 'applications/phortune/view/PhortuneSubscriptionTableView.php',
     'PhortuneSubscriptionViewController' => 'applications/phortune/controller/PhortuneSubscriptionViewController.php',
     'PhortuneSubscriptionWorker' => 'applications/phortune/worker/PhortuneSubscriptionWorker.php',
     'PhortuneTestPaymentProvider' => 'applications/phortune/provider/PhortuneTestPaymentProvider.php',
     'PhortuneWePayPaymentProvider' => 'applications/phortune/provider/PhortuneWePayPaymentProvider.php',
     'PhragmentBrowseController' => 'applications/phragment/controller/PhragmentBrowseController.php',
     'PhragmentCanCreateCapability' => 'applications/phragment/capability/PhragmentCanCreateCapability.php',
     'PhragmentConduitAPIMethod' => 'applications/phragment/conduit/PhragmentConduitAPIMethod.php',
     'PhragmentController' => 'applications/phragment/controller/PhragmentController.php',
     'PhragmentCreateController' => 'applications/phragment/controller/PhragmentCreateController.php',
     'PhragmentDAO' => 'applications/phragment/storage/PhragmentDAO.php',
     'PhragmentFragment' => 'applications/phragment/storage/PhragmentFragment.php',
     'PhragmentFragmentPHIDType' => 'applications/phragment/phid/PhragmentFragmentPHIDType.php',
     'PhragmentFragmentQuery' => 'applications/phragment/query/PhragmentFragmentQuery.php',
     'PhragmentFragmentVersion' => 'applications/phragment/storage/PhragmentFragmentVersion.php',
     'PhragmentFragmentVersionPHIDType' => 'applications/phragment/phid/PhragmentFragmentVersionPHIDType.php',
     'PhragmentFragmentVersionQuery' => 'applications/phragment/query/PhragmentFragmentVersionQuery.php',
     'PhragmentGetPatchConduitAPIMethod' => 'applications/phragment/conduit/PhragmentGetPatchConduitAPIMethod.php',
     'PhragmentHistoryController' => 'applications/phragment/controller/PhragmentHistoryController.php',
     'PhragmentPatchController' => 'applications/phragment/controller/PhragmentPatchController.php',
     'PhragmentPatchUtil' => 'applications/phragment/util/PhragmentPatchUtil.php',
     'PhragmentPolicyController' => 'applications/phragment/controller/PhragmentPolicyController.php',
     'PhragmentQueryFragmentsConduitAPIMethod' => 'applications/phragment/conduit/PhragmentQueryFragmentsConduitAPIMethod.php',
     'PhragmentRevertController' => 'applications/phragment/controller/PhragmentRevertController.php',
     'PhragmentSchemaSpec' => 'applications/phragment/storage/PhragmentSchemaSpec.php',
     'PhragmentSnapshot' => 'applications/phragment/storage/PhragmentSnapshot.php',
     'PhragmentSnapshotChild' => 'applications/phragment/storage/PhragmentSnapshotChild.php',
     'PhragmentSnapshotChildQuery' => 'applications/phragment/query/PhragmentSnapshotChildQuery.php',
     'PhragmentSnapshotCreateController' => 'applications/phragment/controller/PhragmentSnapshotCreateController.php',
     'PhragmentSnapshotDeleteController' => 'applications/phragment/controller/PhragmentSnapshotDeleteController.php',
     'PhragmentSnapshotPHIDType' => 'applications/phragment/phid/PhragmentSnapshotPHIDType.php',
     'PhragmentSnapshotPromoteController' => 'applications/phragment/controller/PhragmentSnapshotPromoteController.php',
     'PhragmentSnapshotQuery' => 'applications/phragment/query/PhragmentSnapshotQuery.php',
     'PhragmentSnapshotViewController' => 'applications/phragment/controller/PhragmentSnapshotViewController.php',
     'PhragmentUpdateController' => 'applications/phragment/controller/PhragmentUpdateController.php',
     'PhragmentVersionController' => 'applications/phragment/controller/PhragmentVersionController.php',
     'PhragmentZIPController' => 'applications/phragment/controller/PhragmentZIPController.php',
     'PhrequentConduitAPIMethod' => 'applications/phrequent/conduit/PhrequentConduitAPIMethod.php',
     'PhrequentController' => 'applications/phrequent/controller/PhrequentController.php',
     'PhrequentCurtainExtension' => 'applications/phrequent/engineextension/PhrequentCurtainExtension.php',
     'PhrequentDAO' => 'applications/phrequent/storage/PhrequentDAO.php',
     'PhrequentListController' => 'applications/phrequent/controller/PhrequentListController.php',
     'PhrequentPopConduitAPIMethod' => 'applications/phrequent/conduit/PhrequentPopConduitAPIMethod.php',
     'PhrequentPushConduitAPIMethod' => 'applications/phrequent/conduit/PhrequentPushConduitAPIMethod.php',
     'PhrequentSearchEngine' => 'applications/phrequent/query/PhrequentSearchEngine.php',
     'PhrequentTimeBlock' => 'applications/phrequent/storage/PhrequentTimeBlock.php',
     'PhrequentTimeBlockTestCase' => 'applications/phrequent/storage/__tests__/PhrequentTimeBlockTestCase.php',
     'PhrequentTimeSlices' => 'applications/phrequent/storage/PhrequentTimeSlices.php',
     'PhrequentTrackController' => 'applications/phrequent/controller/PhrequentTrackController.php',
     'PhrequentTrackableInterface' => 'applications/phrequent/interface/PhrequentTrackableInterface.php',
     'PhrequentTrackingConduitAPIMethod' => 'applications/phrequent/conduit/PhrequentTrackingConduitAPIMethod.php',
     'PhrequentTrackingEditor' => 'applications/phrequent/editor/PhrequentTrackingEditor.php',
     'PhrequentUIEventListener' => 'applications/phrequent/event/PhrequentUIEventListener.php',
     'PhrequentUserTime' => 'applications/phrequent/storage/PhrequentUserTime.php',
     'PhrequentUserTimeQuery' => 'applications/phrequent/query/PhrequentUserTimeQuery.php',
     'PhrictionChangeType' => 'applications/phriction/constants/PhrictionChangeType.php',
     'PhrictionConduitAPIMethod' => 'applications/phriction/conduit/PhrictionConduitAPIMethod.php',
     'PhrictionConstants' => 'applications/phriction/constants/PhrictionConstants.php',
     'PhrictionContent' => 'applications/phriction/storage/PhrictionContent.php',
     'PhrictionController' => 'applications/phriction/controller/PhrictionController.php',
     'PhrictionCreateConduitAPIMethod' => 'applications/phriction/conduit/PhrictionCreateConduitAPIMethod.php',
     'PhrictionDAO' => 'applications/phriction/storage/PhrictionDAO.php',
     'PhrictionDeleteController' => 'applications/phriction/controller/PhrictionDeleteController.php',
     'PhrictionDiffController' => 'applications/phriction/controller/PhrictionDiffController.php',
     'PhrictionDocument' => 'applications/phriction/storage/PhrictionDocument.php',
     'PhrictionDocumentAuthorHeraldField' => 'applications/phriction/herald/PhrictionDocumentAuthorHeraldField.php',
     'PhrictionDocumentContentHeraldField' => 'applications/phriction/herald/PhrictionDocumentContentHeraldField.php',
     'PhrictionDocumentController' => 'applications/phriction/controller/PhrictionDocumentController.php',
     'PhrictionDocumentFulltextEngine' => 'applications/phriction/search/PhrictionDocumentFulltextEngine.php',
     'PhrictionDocumentHeraldAdapter' => 'applications/phriction/herald/PhrictionDocumentHeraldAdapter.php',
     'PhrictionDocumentHeraldField' => 'applications/phriction/herald/PhrictionDocumentHeraldField.php',
     'PhrictionDocumentHeraldFieldGroup' => 'applications/phriction/herald/PhrictionDocumentHeraldFieldGroup.php',
     'PhrictionDocumentPHIDType' => 'applications/phriction/phid/PhrictionDocumentPHIDType.php',
     'PhrictionDocumentPathHeraldField' => 'applications/phriction/herald/PhrictionDocumentPathHeraldField.php',
     'PhrictionDocumentQuery' => 'applications/phriction/query/PhrictionDocumentQuery.php',
     'PhrictionDocumentStatus' => 'applications/phriction/constants/PhrictionDocumentStatus.php',
     'PhrictionDocumentTitleHeraldField' => 'applications/phriction/herald/PhrictionDocumentTitleHeraldField.php',
     'PhrictionEditConduitAPIMethod' => 'applications/phriction/conduit/PhrictionEditConduitAPIMethod.php',
     'PhrictionEditController' => 'applications/phriction/controller/PhrictionEditController.php',
     'PhrictionHistoryConduitAPIMethod' => 'applications/phriction/conduit/PhrictionHistoryConduitAPIMethod.php',
     'PhrictionHistoryController' => 'applications/phriction/controller/PhrictionHistoryController.php',
     'PhrictionInfoConduitAPIMethod' => 'applications/phriction/conduit/PhrictionInfoConduitAPIMethod.php',
     'PhrictionListController' => 'applications/phriction/controller/PhrictionListController.php',
     'PhrictionMarkupPreviewController' => 'applications/phriction/controller/PhrictionMarkupPreviewController.php',
     'PhrictionMoveController' => 'applications/phriction/controller/PhrictionMoveController.php',
     'PhrictionNewController' => 'applications/phriction/controller/PhrictionNewController.php',
     'PhrictionRemarkupRule' => 'applications/phriction/markup/PhrictionRemarkupRule.php',
     'PhrictionReplyHandler' => 'applications/phriction/mail/PhrictionReplyHandler.php',
     'PhrictionSchemaSpec' => 'applications/phriction/storage/PhrictionSchemaSpec.php',
     'PhrictionSearchEngine' => 'applications/phriction/query/PhrictionSearchEngine.php',
     'PhrictionTransaction' => 'applications/phriction/storage/PhrictionTransaction.php',
     'PhrictionTransactionComment' => 'applications/phriction/storage/PhrictionTransactionComment.php',
     'PhrictionTransactionEditor' => 'applications/phriction/editor/PhrictionTransactionEditor.php',
     'PhrictionTransactionQuery' => 'applications/phriction/query/PhrictionTransactionQuery.php',
     'PolicyLockOptionType' => 'applications/policy/config/PolicyLockOptionType.php',
     'PonderAddAnswerView' => 'applications/ponder/view/PonderAddAnswerView.php',
     'PonderAnswer' => 'applications/ponder/storage/PonderAnswer.php',
     'PonderAnswerCommentController' => 'applications/ponder/controller/PonderAnswerCommentController.php',
     'PonderAnswerEditController' => 'applications/ponder/controller/PonderAnswerEditController.php',
     'PonderAnswerEditor' => 'applications/ponder/editor/PonderAnswerEditor.php',
     'PonderAnswerHistoryController' => 'applications/ponder/controller/PonderAnswerHistoryController.php',
     'PonderAnswerMailReceiver' => 'applications/ponder/mail/PonderAnswerMailReceiver.php',
     'PonderAnswerPHIDType' => 'applications/ponder/phid/PonderAnswerPHIDType.php',
     'PonderAnswerQuery' => 'applications/ponder/query/PonderAnswerQuery.php',
     'PonderAnswerReplyHandler' => 'applications/ponder/mail/PonderAnswerReplyHandler.php',
     'PonderAnswerSaveController' => 'applications/ponder/controller/PonderAnswerSaveController.php',
     'PonderAnswerStatus' => 'applications/ponder/constants/PonderAnswerStatus.php',
     'PonderAnswerTransaction' => 'applications/ponder/storage/PonderAnswerTransaction.php',
     'PonderAnswerTransactionComment' => 'applications/ponder/storage/PonderAnswerTransactionComment.php',
     'PonderAnswerTransactionQuery' => 'applications/ponder/query/PonderAnswerTransactionQuery.php',
     'PonderAnswerView' => 'applications/ponder/view/PonderAnswerView.php',
     'PonderConstants' => 'applications/ponder/constants/PonderConstants.php',
     'PonderController' => 'applications/ponder/controller/PonderController.php',
     'PonderDAO' => 'applications/ponder/storage/PonderDAO.php',
     'PonderDefaultViewCapability' => 'applications/ponder/capability/PonderDefaultViewCapability.php',
     'PonderEditor' => 'applications/ponder/editor/PonderEditor.php',
     'PonderFooterView' => 'applications/ponder/view/PonderFooterView.php',
     'PonderModerateCapability' => 'applications/ponder/capability/PonderModerateCapability.php',
     'PonderQuestion' => 'applications/ponder/storage/PonderQuestion.php',
     'PonderQuestionCommentController' => 'applications/ponder/controller/PonderQuestionCommentController.php',
     'PonderQuestionEditController' => 'applications/ponder/controller/PonderQuestionEditController.php',
     'PonderQuestionEditor' => 'applications/ponder/editor/PonderQuestionEditor.php',
     'PonderQuestionFulltextEngine' => 'applications/ponder/search/PonderQuestionFulltextEngine.php',
     'PonderQuestionHistoryController' => 'applications/ponder/controller/PonderQuestionHistoryController.php',
     'PonderQuestionListController' => 'applications/ponder/controller/PonderQuestionListController.php',
     'PonderQuestionMailReceiver' => 'applications/ponder/mail/PonderQuestionMailReceiver.php',
     'PonderQuestionPHIDType' => 'applications/ponder/phid/PonderQuestionPHIDType.php',
     'PonderQuestionQuery' => 'applications/ponder/query/PonderQuestionQuery.php',
     'PonderQuestionReplyHandler' => 'applications/ponder/mail/PonderQuestionReplyHandler.php',
     'PonderQuestionSearchEngine' => 'applications/ponder/query/PonderQuestionSearchEngine.php',
     'PonderQuestionStatus' => 'applications/ponder/constants/PonderQuestionStatus.php',
     'PonderQuestionStatusController' => 'applications/ponder/controller/PonderQuestionStatusController.php',
     'PonderQuestionTransaction' => 'applications/ponder/storage/PonderQuestionTransaction.php',
     'PonderQuestionTransactionComment' => 'applications/ponder/storage/PonderQuestionTransactionComment.php',
     'PonderQuestionTransactionQuery' => 'applications/ponder/query/PonderQuestionTransactionQuery.php',
     'PonderQuestionViewController' => 'applications/ponder/controller/PonderQuestionViewController.php',
     'PonderRemarkupRule' => 'applications/ponder/remarkup/PonderRemarkupRule.php',
     'PonderSchemaSpec' => 'applications/ponder/storage/PonderSchemaSpec.php',
     'ProjectAddProjectsEmailCommand' => 'applications/project/command/ProjectAddProjectsEmailCommand.php',
     'ProjectBoardTaskCard' => 'applications/project/view/ProjectBoardTaskCard.php',
     'ProjectCanLockProjectsCapability' => 'applications/project/capability/ProjectCanLockProjectsCapability.php',
     'ProjectConduitAPIMethod' => 'applications/project/conduit/ProjectConduitAPIMethod.php',
     'ProjectCreateConduitAPIMethod' => 'applications/project/conduit/ProjectCreateConduitAPIMethod.php',
     'ProjectCreateProjectsCapability' => 'applications/project/capability/ProjectCreateProjectsCapability.php',
     'ProjectDefaultEditCapability' => 'applications/project/capability/ProjectDefaultEditCapability.php',
     'ProjectDefaultJoinCapability' => 'applications/project/capability/ProjectDefaultJoinCapability.php',
     'ProjectDefaultViewCapability' => 'applications/project/capability/ProjectDefaultViewCapability.php',
     'ProjectEditConduitAPIMethod' => 'applications/project/conduit/ProjectEditConduitAPIMethod.php',
     'ProjectQueryConduitAPIMethod' => 'applications/project/conduit/ProjectQueryConduitAPIMethod.php',
     'ProjectRemarkupRule' => 'applications/project/remarkup/ProjectRemarkupRule.php',
     'ProjectRemarkupRuleTestCase' => 'applications/project/remarkup/__tests__/ProjectRemarkupRuleTestCase.php',
     'ProjectReplyHandler' => 'applications/project/mail/ProjectReplyHandler.php',
     'ProjectSearchConduitAPIMethod' => 'applications/project/conduit/ProjectSearchConduitAPIMethod.php',
     'QueryFormattingTestCase' => 'infrastructure/storage/__tests__/QueryFormattingTestCase.php',
     'ReleephAuthorFieldSpecification' => 'applications/releeph/field/specification/ReleephAuthorFieldSpecification.php',
     'ReleephBranch' => 'applications/releeph/storage/ReleephBranch.php',
     'ReleephBranchAccessController' => 'applications/releeph/controller/branch/ReleephBranchAccessController.php',
     'ReleephBranchCommitFieldSpecification' => 'applications/releeph/field/specification/ReleephBranchCommitFieldSpecification.php',
     'ReleephBranchController' => 'applications/releeph/controller/branch/ReleephBranchController.php',
     'ReleephBranchCreateController' => 'applications/releeph/controller/branch/ReleephBranchCreateController.php',
     'ReleephBranchEditController' => 'applications/releeph/controller/branch/ReleephBranchEditController.php',
     'ReleephBranchEditor' => 'applications/releeph/editor/ReleephBranchEditor.php',
     'ReleephBranchHistoryController' => 'applications/releeph/controller/branch/ReleephBranchHistoryController.php',
     'ReleephBranchNamePreviewController' => 'applications/releeph/controller/branch/ReleephBranchNamePreviewController.php',
     'ReleephBranchPHIDType' => 'applications/releeph/phid/ReleephBranchPHIDType.php',
     'ReleephBranchPreviewView' => 'applications/releeph/view/branch/ReleephBranchPreviewView.php',
     'ReleephBranchQuery' => 'applications/releeph/query/ReleephBranchQuery.php',
     'ReleephBranchSearchEngine' => 'applications/releeph/query/ReleephBranchSearchEngine.php',
     'ReleephBranchTemplate' => 'applications/releeph/view/branch/ReleephBranchTemplate.php',
     'ReleephBranchTransaction' => 'applications/releeph/storage/ReleephBranchTransaction.php',
     'ReleephBranchTransactionQuery' => 'applications/releeph/query/ReleephBranchTransactionQuery.php',
     'ReleephBranchViewController' => 'applications/releeph/controller/branch/ReleephBranchViewController.php',
     'ReleephCommitFinder' => 'applications/releeph/commitfinder/ReleephCommitFinder.php',
     'ReleephCommitFinderException' => 'applications/releeph/commitfinder/ReleephCommitFinderException.php',
     'ReleephCommitMessageFieldSpecification' => 'applications/releeph/field/specification/ReleephCommitMessageFieldSpecification.php',
     'ReleephConduitAPIMethod' => 'applications/releeph/conduit/ReleephConduitAPIMethod.php',
     'ReleephController' => 'applications/releeph/controller/ReleephController.php',
     'ReleephDAO' => 'applications/releeph/storage/ReleephDAO.php',
     'ReleephDefaultFieldSelector' => 'applications/releeph/field/selector/ReleephDefaultFieldSelector.php',
     'ReleephDependsOnFieldSpecification' => 'applications/releeph/field/specification/ReleephDependsOnFieldSpecification.php',
     'ReleephDiffChurnFieldSpecification' => 'applications/releeph/field/specification/ReleephDiffChurnFieldSpecification.php',
     'ReleephDiffMessageFieldSpecification' => 'applications/releeph/field/specification/ReleephDiffMessageFieldSpecification.php',
     'ReleephDiffSizeFieldSpecification' => 'applications/releeph/field/specification/ReleephDiffSizeFieldSpecification.php',
     'ReleephFieldParseException' => 'applications/releeph/field/exception/ReleephFieldParseException.php',
     'ReleephFieldSelector' => 'applications/releeph/field/selector/ReleephFieldSelector.php',
     'ReleephFieldSpecification' => 'applications/releeph/field/specification/ReleephFieldSpecification.php',
     'ReleephGetBranchesConduitAPIMethod' => 'applications/releeph/conduit/ReleephGetBranchesConduitAPIMethod.php',
     'ReleephIntentFieldSpecification' => 'applications/releeph/field/specification/ReleephIntentFieldSpecification.php',
     'ReleephLevelFieldSpecification' => 'applications/releeph/field/specification/ReleephLevelFieldSpecification.php',
     'ReleephOriginalCommitFieldSpecification' => 'applications/releeph/field/specification/ReleephOriginalCommitFieldSpecification.php',
     'ReleephProductActionController' => 'applications/releeph/controller/product/ReleephProductActionController.php',
     'ReleephProductController' => 'applications/releeph/controller/product/ReleephProductController.php',
     'ReleephProductCreateController' => 'applications/releeph/controller/product/ReleephProductCreateController.php',
     'ReleephProductEditController' => 'applications/releeph/controller/product/ReleephProductEditController.php',
     'ReleephProductEditor' => 'applications/releeph/editor/ReleephProductEditor.php',
     'ReleephProductHistoryController' => 'applications/releeph/controller/product/ReleephProductHistoryController.php',
     'ReleephProductListController' => 'applications/releeph/controller/product/ReleephProductListController.php',
     'ReleephProductPHIDType' => 'applications/releeph/phid/ReleephProductPHIDType.php',
     'ReleephProductQuery' => 'applications/releeph/query/ReleephProductQuery.php',
     'ReleephProductSearchEngine' => 'applications/releeph/query/ReleephProductSearchEngine.php',
     'ReleephProductTransaction' => 'applications/releeph/storage/ReleephProductTransaction.php',
     'ReleephProductTransactionQuery' => 'applications/releeph/query/ReleephProductTransactionQuery.php',
     'ReleephProductViewController' => 'applications/releeph/controller/product/ReleephProductViewController.php',
     'ReleephProject' => 'applications/releeph/storage/ReleephProject.php',
     'ReleephQueryBranchesConduitAPIMethod' => 'applications/releeph/conduit/ReleephQueryBranchesConduitAPIMethod.php',
     'ReleephQueryProductsConduitAPIMethod' => 'applications/releeph/conduit/ReleephQueryProductsConduitAPIMethod.php',
     'ReleephQueryRequestsConduitAPIMethod' => 'applications/releeph/conduit/ReleephQueryRequestsConduitAPIMethod.php',
     'ReleephReasonFieldSpecification' => 'applications/releeph/field/specification/ReleephReasonFieldSpecification.php',
     'ReleephRequest' => 'applications/releeph/storage/ReleephRequest.php',
     'ReleephRequestActionController' => 'applications/releeph/controller/request/ReleephRequestActionController.php',
     'ReleephRequestCommentController' => 'applications/releeph/controller/request/ReleephRequestCommentController.php',
     'ReleephRequestConduitAPIMethod' => 'applications/releeph/conduit/ReleephRequestConduitAPIMethod.php',
     'ReleephRequestController' => 'applications/releeph/controller/request/ReleephRequestController.php',
     'ReleephRequestDifferentialCreateController' => 'applications/releeph/controller/request/ReleephRequestDifferentialCreateController.php',
     'ReleephRequestEditController' => 'applications/releeph/controller/request/ReleephRequestEditController.php',
     'ReleephRequestMailReceiver' => 'applications/releeph/mail/ReleephRequestMailReceiver.php',
     'ReleephRequestPHIDType' => 'applications/releeph/phid/ReleephRequestPHIDType.php',
     'ReleephRequestQuery' => 'applications/releeph/query/ReleephRequestQuery.php',
     'ReleephRequestReplyHandler' => 'applications/releeph/mail/ReleephRequestReplyHandler.php',
     'ReleephRequestSearchEngine' => 'applications/releeph/query/ReleephRequestSearchEngine.php',
     'ReleephRequestStatus' => 'applications/releeph/constants/ReleephRequestStatus.php',
     'ReleephRequestTransaction' => 'applications/releeph/storage/ReleephRequestTransaction.php',
     'ReleephRequestTransactionComment' => 'applications/releeph/storage/ReleephRequestTransactionComment.php',
     'ReleephRequestTransactionQuery' => 'applications/releeph/query/ReleephRequestTransactionQuery.php',
     'ReleephRequestTransactionalEditor' => 'applications/releeph/editor/ReleephRequestTransactionalEditor.php',
     'ReleephRequestTypeaheadControl' => 'applications/releeph/view/request/ReleephRequestTypeaheadControl.php',
     'ReleephRequestTypeaheadController' => 'applications/releeph/controller/request/ReleephRequestTypeaheadController.php',
     'ReleephRequestView' => 'applications/releeph/view/ReleephRequestView.php',
     'ReleephRequestViewController' => 'applications/releeph/controller/request/ReleephRequestViewController.php',
     'ReleephRequestorFieldSpecification' => 'applications/releeph/field/specification/ReleephRequestorFieldSpecification.php',
     'ReleephRevisionFieldSpecification' => 'applications/releeph/field/specification/ReleephRevisionFieldSpecification.php',
     'ReleephSeverityFieldSpecification' => 'applications/releeph/field/specification/ReleephSeverityFieldSpecification.php',
     'ReleephSummaryFieldSpecification' => 'applications/releeph/field/specification/ReleephSummaryFieldSpecification.php',
     'ReleephWorkCanPushConduitAPIMethod' => 'applications/releeph/conduit/work/ReleephWorkCanPushConduitAPIMethod.php',
     'ReleephWorkGetAuthorInfoConduitAPIMethod' => 'applications/releeph/conduit/work/ReleephWorkGetAuthorInfoConduitAPIMethod.php',
     'ReleephWorkGetBranchCommitMessageConduitAPIMethod' => 'applications/releeph/conduit/work/ReleephWorkGetBranchCommitMessageConduitAPIMethod.php',
     'ReleephWorkGetBranchConduitAPIMethod' => 'applications/releeph/conduit/work/ReleephWorkGetBranchConduitAPIMethod.php',
     'ReleephWorkGetCommitMessageConduitAPIMethod' => 'applications/releeph/conduit/work/ReleephWorkGetCommitMessageConduitAPIMethod.php',
     'ReleephWorkNextRequestConduitAPIMethod' => 'applications/releeph/conduit/work/ReleephWorkNextRequestConduitAPIMethod.php',
     'ReleephWorkRecordConduitAPIMethod' => 'applications/releeph/conduit/work/ReleephWorkRecordConduitAPIMethod.php',
     'ReleephWorkRecordPickStatusConduitAPIMethod' => 'applications/releeph/conduit/work/ReleephWorkRecordPickStatusConduitAPIMethod.php',
     'RemarkupProcessConduitAPIMethod' => 'applications/remarkup/conduit/RemarkupProcessConduitAPIMethod.php',
     'RepositoryConduitAPIMethod' => 'applications/repository/conduit/RepositoryConduitAPIMethod.php',
-    'RepositoryCreateConduitAPIMethod' => 'applications/repository/conduit/RepositoryCreateConduitAPIMethod.php',
     'RepositoryQueryConduitAPIMethod' => 'applications/repository/conduit/RepositoryQueryConduitAPIMethod.php',
     'ShellLogView' => 'applications/harbormaster/view/ShellLogView.php',
     'SlowvoteConduitAPIMethod' => 'applications/slowvote/conduit/SlowvoteConduitAPIMethod.php',
     'SlowvoteEmbedView' => 'applications/slowvote/view/SlowvoteEmbedView.php',
     'SlowvoteInfoConduitAPIMethod' => 'applications/slowvote/conduit/SlowvoteInfoConduitAPIMethod.php',
     'SlowvoteRemarkupRule' => 'applications/slowvote/remarkup/SlowvoteRemarkupRule.php',
     'SubscriptionListDialogBuilder' => 'applications/subscriptions/view/SubscriptionListDialogBuilder.php',
     'SubscriptionListStringBuilder' => 'applications/subscriptions/view/SubscriptionListStringBuilder.php',
     'TokenConduitAPIMethod' => 'applications/tokens/conduit/TokenConduitAPIMethod.php',
     'TokenGiveConduitAPIMethod' => 'applications/tokens/conduit/TokenGiveConduitAPIMethod.php',
     'TokenGivenConduitAPIMethod' => 'applications/tokens/conduit/TokenGivenConduitAPIMethod.php',
     'TokenQueryConduitAPIMethod' => 'applications/tokens/conduit/TokenQueryConduitAPIMethod.php',
     'UserConduitAPIMethod' => 'applications/people/conduit/UserConduitAPIMethod.php',
     'UserDisableConduitAPIMethod' => 'applications/people/conduit/UserDisableConduitAPIMethod.php',
     'UserEnableConduitAPIMethod' => 'applications/people/conduit/UserEnableConduitAPIMethod.php',
     'UserFindConduitAPIMethod' => 'applications/people/conduit/UserFindConduitAPIMethod.php',
     'UserQueryConduitAPIMethod' => 'applications/people/conduit/UserQueryConduitAPIMethod.php',
     'UserWhoAmIConduitAPIMethod' => 'applications/people/conduit/UserWhoAmIConduitAPIMethod.php',
   ),
   'function' => array(
     'celerity_generate_unique_node_id' => 'applications/celerity/api.php',
     'celerity_get_resource_uri' => 'applications/celerity/api.php',
     'javelin_tag' => 'infrastructure/javelin/markup.php',
     'phabricator_date' => 'view/viewutils.php',
     'phabricator_datetime' => 'view/viewutils.php',
     'phabricator_form' => 'infrastructure/javelin/markup.php',
     'phabricator_format_local_time' => 'view/viewutils.php',
     'phabricator_relative_date' => 'view/viewutils.php',
     'phabricator_time' => 'view/viewutils.php',
     'phid_get_subtype' => 'applications/phid/utils.php',
     'phid_get_type' => 'applications/phid/utils.php',
     'phid_group_by_type' => 'applications/phid/utils.php',
     'require_celerity_resource' => 'applications/celerity/api.php',
   ),
   'xmap' => array(
     'AlmanacAddress' => 'Phobject',
     'AlmanacBinding' => array(
       'AlmanacDAO',
       'PhabricatorPolicyInterface',
       'PhabricatorApplicationTransactionInterface',
       'AlmanacPropertyInterface',
       'PhabricatorDestructibleInterface',
       'PhabricatorExtendedPolicyInterface',
     ),
     'AlmanacBindingDisableController' => 'AlmanacServiceController',
     'AlmanacBindingEditController' => 'AlmanacServiceController',
     'AlmanacBindingEditor' => 'AlmanacEditor',
     'AlmanacBindingPHIDType' => 'PhabricatorPHIDType',
     'AlmanacBindingPropertyEditEngine' => 'AlmanacPropertyEditEngine',
     'AlmanacBindingQuery' => 'AlmanacQuery',
     'AlmanacBindingTableView' => 'AphrontView',
     'AlmanacBindingTransaction' => 'AlmanacTransaction',
     'AlmanacBindingTransactionQuery' => 'PhabricatorApplicationTransactionQuery',
     'AlmanacBindingViewController' => 'AlmanacServiceController',
     'AlmanacBindingsSearchEngineAttachment' => 'AlmanacSearchEngineAttachment',
     'AlmanacClusterDatabaseServiceType' => 'AlmanacClusterServiceType',
     'AlmanacClusterRepositoryServiceType' => 'AlmanacClusterServiceType',
     'AlmanacClusterServiceType' => 'AlmanacServiceType',
     'AlmanacConsoleController' => 'AlmanacController',
     'AlmanacController' => 'PhabricatorController',
     'AlmanacCreateDevicesCapability' => 'PhabricatorPolicyCapability',
     'AlmanacCreateNamespacesCapability' => 'PhabricatorPolicyCapability',
     'AlmanacCreateNetworksCapability' => 'PhabricatorPolicyCapability',
     'AlmanacCreateServicesCapability' => 'PhabricatorPolicyCapability',
     'AlmanacCustomServiceType' => 'AlmanacServiceType',
     'AlmanacDAO' => 'PhabricatorLiskDAO',
     'AlmanacDevice' => array(
       'AlmanacDAO',
       'PhabricatorPolicyInterface',
       'PhabricatorApplicationTransactionInterface',
       'PhabricatorProjectInterface',
       'PhabricatorSSHPublicKeyInterface',
       'AlmanacPropertyInterface',
       'PhabricatorDestructibleInterface',
       'PhabricatorNgramsInterface',
       'PhabricatorConduitResultInterface',
       'PhabricatorExtendedPolicyInterface',
     ),
     'AlmanacDeviceController' => 'AlmanacController',
     'AlmanacDeviceEditController' => 'AlmanacDeviceController',
     'AlmanacDeviceEditEngine' => 'PhabricatorEditEngine',
     'AlmanacDeviceEditor' => 'AlmanacEditor',
     'AlmanacDeviceListController' => 'AlmanacDeviceController',
     'AlmanacDeviceNameNgrams' => 'PhabricatorSearchNgrams',
     'AlmanacDevicePHIDType' => 'PhabricatorPHIDType',
     'AlmanacDevicePropertyEditEngine' => 'AlmanacPropertyEditEngine',
     'AlmanacDeviceQuery' => 'AlmanacQuery',
     'AlmanacDeviceSearchConduitAPIMethod' => 'PhabricatorSearchEngineAPIMethod',
     'AlmanacDeviceSearchEngine' => 'PhabricatorApplicationSearchEngine',
     'AlmanacDeviceTransaction' => 'AlmanacTransaction',
     'AlmanacDeviceTransactionQuery' => 'PhabricatorApplicationTransactionQuery',
     'AlmanacDeviceViewController' => 'AlmanacDeviceController',
     'AlmanacDrydockPoolServiceType' => 'AlmanacServiceType',
     'AlmanacEditor' => 'PhabricatorApplicationTransactionEditor',
     'AlmanacInterface' => array(
       'AlmanacDAO',
       'PhabricatorPolicyInterface',
       'PhabricatorDestructibleInterface',
       'PhabricatorExtendedPolicyInterface',
     ),
     'AlmanacInterfaceDatasource' => 'PhabricatorTypeaheadDatasource',
     'AlmanacInterfaceDeleteController' => 'AlmanacDeviceController',
     'AlmanacInterfaceEditController' => 'AlmanacDeviceController',
     'AlmanacInterfacePHIDType' => 'PhabricatorPHIDType',
     'AlmanacInterfaceQuery' => 'AlmanacQuery',
     'AlmanacInterfaceTableView' => 'AphrontView',
     'AlmanacKeys' => 'Phobject',
     'AlmanacManageClusterServicesCapability' => 'PhabricatorPolicyCapability',
     'AlmanacManagementRegisterWorkflow' => 'AlmanacManagementWorkflow',
     'AlmanacManagementTrustKeyWorkflow' => 'AlmanacManagementWorkflow',
     'AlmanacManagementUntrustKeyWorkflow' => 'AlmanacManagementWorkflow',
     'AlmanacManagementWorkflow' => 'PhabricatorManagementWorkflow',
     'AlmanacNames' => 'Phobject',
     'AlmanacNamesTestCase' => 'PhabricatorTestCase',
     'AlmanacNamespace' => array(
       'AlmanacDAO',
       'PhabricatorPolicyInterface',
       'PhabricatorApplicationTransactionInterface',
       'PhabricatorProjectInterface',
       'AlmanacPropertyInterface',
       'PhabricatorDestructibleInterface',
       'PhabricatorNgramsInterface',
     ),
     'AlmanacNamespaceController' => 'AlmanacController',
     'AlmanacNamespaceEditController' => 'AlmanacNamespaceController',
     'AlmanacNamespaceEditEngine' => 'PhabricatorEditEngine',
     'AlmanacNamespaceEditor' => 'PhabricatorApplicationTransactionEditor',
     'AlmanacNamespaceListController' => 'AlmanacNamespaceController',
     'AlmanacNamespaceNameNgrams' => 'PhabricatorSearchNgrams',
     'AlmanacNamespacePHIDType' => 'PhabricatorPHIDType',
     'AlmanacNamespaceQuery' => 'AlmanacQuery',
     'AlmanacNamespaceSearchEngine' => 'PhabricatorApplicationSearchEngine',
     'AlmanacNamespaceTransaction' => 'PhabricatorApplicationTransaction',
     'AlmanacNamespaceTransactionQuery' => 'PhabricatorApplicationTransactionQuery',
     'AlmanacNamespaceViewController' => 'AlmanacNamespaceController',
     'AlmanacNetwork' => array(
       'AlmanacDAO',
       'PhabricatorApplicationTransactionInterface',
       'PhabricatorPolicyInterface',
       'PhabricatorDestructibleInterface',
       'PhabricatorNgramsInterface',
     ),
     'AlmanacNetworkController' => 'AlmanacController',
     'AlmanacNetworkEditController' => 'AlmanacNetworkController',
     'AlmanacNetworkEditEngine' => 'PhabricatorEditEngine',
     'AlmanacNetworkEditor' => 'PhabricatorApplicationTransactionEditor',
     'AlmanacNetworkListController' => 'AlmanacNetworkController',
     'AlmanacNetworkNameNgrams' => 'PhabricatorSearchNgrams',
     'AlmanacNetworkPHIDType' => 'PhabricatorPHIDType',
     'AlmanacNetworkQuery' => 'AlmanacQuery',
     'AlmanacNetworkSearchEngine' => 'PhabricatorApplicationSearchEngine',
     'AlmanacNetworkTransaction' => 'PhabricatorApplicationTransaction',
     'AlmanacNetworkTransactionQuery' => 'PhabricatorApplicationTransactionQuery',
     'AlmanacNetworkViewController' => 'AlmanacNetworkController',
     'AlmanacPropertiesDestructionEngineExtension' => 'PhabricatorDestructionEngineExtension',
     'AlmanacPropertiesSearchEngineAttachment' => 'AlmanacSearchEngineAttachment',
     'AlmanacProperty' => array(
       'AlmanacDAO',
       'PhabricatorPolicyInterface',
     ),
     'AlmanacPropertyController' => 'AlmanacController',
     'AlmanacPropertyDeleteController' => 'AlmanacPropertyController',
     'AlmanacPropertyEditController' => 'AlmanacPropertyController',
     'AlmanacPropertyEditEngine' => 'PhabricatorEditEngine',
     'AlmanacPropertyQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
     'AlmanacQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
     'AlmanacSchemaSpec' => 'PhabricatorConfigSchemaSpec',
     'AlmanacSearchEngineAttachment' => 'PhabricatorSearchEngineAttachment',
     'AlmanacService' => array(
       'AlmanacDAO',
       'PhabricatorPolicyInterface',
       'PhabricatorApplicationTransactionInterface',
       'PhabricatorProjectInterface',
       'AlmanacPropertyInterface',
       'PhabricatorDestructibleInterface',
       'PhabricatorNgramsInterface',
       'PhabricatorConduitResultInterface',
       'PhabricatorExtendedPolicyInterface',
     ),
     'AlmanacServiceController' => 'AlmanacController',
     'AlmanacServiceDatasource' => 'PhabricatorTypeaheadDatasource',
     'AlmanacServiceEditController' => 'AlmanacServiceController',
     'AlmanacServiceEditEngine' => 'PhabricatorEditEngine',
     'AlmanacServiceEditor' => 'AlmanacEditor',
     'AlmanacServiceListController' => 'AlmanacServiceController',
     'AlmanacServiceNameNgrams' => 'PhabricatorSearchNgrams',
     'AlmanacServicePHIDType' => 'PhabricatorPHIDType',
     'AlmanacServicePropertyEditEngine' => 'AlmanacPropertyEditEngine',
     'AlmanacServiceQuery' => 'AlmanacQuery',
     'AlmanacServiceSearchConduitAPIMethod' => 'PhabricatorSearchEngineAPIMethod',
     'AlmanacServiceSearchEngine' => 'PhabricatorApplicationSearchEngine',
     'AlmanacServiceTransaction' => 'AlmanacTransaction',
     'AlmanacServiceTransactionQuery' => 'PhabricatorApplicationTransactionQuery',
     'AlmanacServiceType' => 'Phobject',
     'AlmanacServiceTypeDatasource' => 'PhabricatorTypeaheadDatasource',
     'AlmanacServiceTypeTestCase' => 'PhabricatorTestCase',
     'AlmanacServiceViewController' => 'AlmanacServiceController',
     'AlmanacTransaction' => 'PhabricatorApplicationTransaction',
     'AphlictDropdownDataQuery' => 'Phobject',
     'Aphront304Response' => 'AphrontResponse',
     'Aphront400Response' => 'AphrontResponse',
     'Aphront403Response' => 'AphrontHTMLResponse',
     'Aphront404Response' => 'AphrontHTMLResponse',
     'AphrontAjaxResponse' => 'AphrontResponse',
     'AphrontApplicationConfiguration' => 'Phobject',
     'AphrontBarView' => 'AphrontView',
     'AphrontBoolHTTPParameterType' => 'AphrontHTTPParameterType',
     'AphrontCSRFException' => 'AphrontException',
     'AphrontCalendarEventView' => 'AphrontView',
     'AphrontController' => 'Phobject',
     'AphrontCursorPagerView' => 'AphrontView',
     'AphrontDefaultApplicationConfiguration' => 'AphrontApplicationConfiguration',
     'AphrontDialogResponse' => 'AphrontResponse',
     'AphrontDialogView' => array(
       'AphrontView',
       'AphrontResponseProducerInterface',
     ),
     'AphrontEpochHTTPParameterType' => 'AphrontHTTPParameterType',
     'AphrontException' => 'Exception',
     'AphrontFileResponse' => 'AphrontResponse',
     'AphrontFormCheckboxControl' => 'AphrontFormControl',
     'AphrontFormControl' => 'AphrontView',
     'AphrontFormDateControl' => 'AphrontFormControl',
     'AphrontFormDateControlValue' => 'Phobject',
     'AphrontFormDividerControl' => 'AphrontFormControl',
     'AphrontFormFileControl' => 'AphrontFormControl',
     'AphrontFormHandlesControl' => 'AphrontFormControl',
     'AphrontFormMarkupControl' => 'AphrontFormControl',
     'AphrontFormPasswordControl' => 'AphrontFormControl',
     'AphrontFormPolicyControl' => 'AphrontFormControl',
     'AphrontFormRadioButtonControl' => 'AphrontFormControl',
     'AphrontFormRecaptchaControl' => 'AphrontFormControl',
     'AphrontFormSelectControl' => 'AphrontFormControl',
     'AphrontFormStaticControl' => 'AphrontFormControl',
     'AphrontFormSubmitControl' => 'AphrontFormControl',
     'AphrontFormTextAreaControl' => 'AphrontFormControl',
     'AphrontFormTextControl' => 'AphrontFormControl',
     'AphrontFormTextWithSubmitControl' => 'AphrontFormControl',
     'AphrontFormTokenizerControl' => 'AphrontFormControl',
     'AphrontFormTypeaheadControl' => 'AphrontFormControl',
     'AphrontFormView' => 'AphrontView',
     'AphrontGlyphBarView' => 'AphrontBarView',
     'AphrontHTMLResponse' => 'AphrontResponse',
     'AphrontHTTPParameterType' => 'Phobject',
     'AphrontHTTPProxyResponse' => 'AphrontResponse',
     'AphrontHTTPSink' => 'Phobject',
     'AphrontHTTPSinkTestCase' => 'PhabricatorTestCase',
     'AphrontIntHTTPParameterType' => 'AphrontHTTPParameterType',
     'AphrontIsolatedDatabaseConnectionTestCase' => 'PhabricatorTestCase',
     'AphrontIsolatedHTTPSink' => 'AphrontHTTPSink',
     'AphrontJSONResponse' => 'AphrontResponse',
     'AphrontJavelinView' => 'AphrontView',
     'AphrontKeyboardShortcutsAvailableView' => 'AphrontView',
     'AphrontListFilterView' => 'AphrontView',
     'AphrontListHTTPParameterType' => 'AphrontHTTPParameterType',
     'AphrontMalformedRequestException' => 'AphrontException',
     'AphrontMoreView' => 'AphrontView',
     'AphrontMultiColumnView' => 'AphrontView',
     'AphrontMySQLDatabaseConnectionTestCase' => 'PhabricatorTestCase',
     'AphrontNullView' => 'AphrontView',
     'AphrontPHIDHTTPParameterType' => 'AphrontHTTPParameterType',
     'AphrontPHIDListHTTPParameterType' => 'AphrontListHTTPParameterType',
     'AphrontPHPHTTPSink' => 'AphrontHTTPSink',
     'AphrontPageView' => 'AphrontView',
     'AphrontPlainTextResponse' => 'AphrontResponse',
     'AphrontProgressBarView' => 'AphrontBarView',
     'AphrontProjectListHTTPParameterType' => 'AphrontListHTTPParameterType',
     'AphrontProxyResponse' => array(
       'AphrontResponse',
       'AphrontResponseProducerInterface',
     ),
     'AphrontRedirectResponse' => 'AphrontResponse',
     'AphrontRedirectResponseTestCase' => 'PhabricatorTestCase',
     'AphrontReloadResponse' => 'AphrontRedirectResponse',
     'AphrontRequest' => 'Phobject',
     'AphrontRequestExceptionHandler' => 'Phobject',
     'AphrontRequestTestCase' => 'PhabricatorTestCase',
     'AphrontResponse' => 'Phobject',
     'AphrontRoutingMap' => 'Phobject',
     'AphrontRoutingResult' => 'Phobject',
     'AphrontSelectHTTPParameterType' => 'AphrontHTTPParameterType',
     'AphrontSideNavFilterView' => 'AphrontView',
     'AphrontSite' => 'Phobject',
     'AphrontStackTraceView' => 'AphrontView',
     'AphrontStandaloneHTMLResponse' => 'AphrontHTMLResponse',
     'AphrontStringHTTPParameterType' => 'AphrontHTTPParameterType',
     'AphrontStringListHTTPParameterType' => 'AphrontListHTTPParameterType',
     'AphrontTableView' => 'AphrontView',
     'AphrontTagView' => 'AphrontView',
     'AphrontTokenizerTemplateView' => 'AphrontView',
     'AphrontTypeaheadTemplateView' => 'AphrontView',
     'AphrontUnhandledExceptionResponse' => 'AphrontStandaloneHTMLResponse',
     'AphrontUserListHTTPParameterType' => 'AphrontListHTTPParameterType',
     'AphrontView' => array(
       'Phobject',
       'PhutilSafeHTMLProducerInterface',
     ),
     'AphrontWebpageResponse' => 'AphrontHTMLResponse',
     'ArcanistConduitAPIMethod' => 'ConduitAPIMethod',
     'AuditConduitAPIMethod' => 'ConduitAPIMethod',
     'AuditQueryConduitAPIMethod' => 'AuditConduitAPIMethod',
     'AuthManageProvidersCapability' => 'PhabricatorPolicyCapability',
     'CalendarTimeUtil' => 'Phobject',
     'CalendarTimeUtilTestCase' => 'PhabricatorTestCase',
     'CelerityAPI' => 'Phobject',
     'CelerityDefaultPostprocessor' => 'CelerityPostprocessor',
     'CelerityHighContrastPostprocessor' => 'CelerityPostprocessor',
     'CelerityLargeFontPostprocessor' => 'CelerityPostprocessor',
     'CelerityManagementMapWorkflow' => 'CelerityManagementWorkflow',
+    'CelerityManagementSyntaxWorkflow' => 'CelerityManagementWorkflow',
     'CelerityManagementWorkflow' => 'PhabricatorManagementWorkflow',
     'CelerityPhabricatorResourceController' => 'CelerityResourceController',
     'CelerityPhabricatorResources' => 'CelerityResourcesOnDisk',
     'CelerityPhysicalResources' => 'CelerityResources',
     'CelerityPhysicalResourcesTestCase' => 'PhabricatorTestCase',
     'CelerityPostprocessor' => 'Phobject',
     'CelerityPostprocessorTestCase' => 'PhabricatorTestCase',
     'CelerityResourceController' => 'PhabricatorController',
     'CelerityResourceGraph' => 'AbstractDirectedGraph',
     'CelerityResourceMap' => 'Phobject',
     'CelerityResourceMapGenerator' => 'Phobject',
     'CelerityResourceTransformer' => 'Phobject',
     'CelerityResourceTransformerTestCase' => 'PhabricatorTestCase',
     'CelerityResources' => 'Phobject',
     'CelerityResourcesOnDisk' => 'CelerityPhysicalResources',
     'CeleritySpriteGenerator' => 'Phobject',
     'CelerityStaticResourceResponse' => 'Phobject',
     'ChatLogConduitAPIMethod' => 'ConduitAPIMethod',
     'ChatLogQueryConduitAPIMethod' => 'ChatLogConduitAPIMethod',
     'ChatLogRecordConduitAPIMethod' => 'ChatLogConduitAPIMethod',
     'ConduitAPIMethod' => array(
       'Phobject',
       'PhabricatorPolicyInterface',
     ),
     'ConduitAPIMethodTestCase' => 'PhabricatorTestCase',
     'ConduitAPIRequest' => 'Phobject',
     'ConduitAPIResponse' => 'Phobject',
     'ConduitApplicationNotInstalledException' => 'ConduitMethodNotFoundException',
     'ConduitBoolParameterType' => 'ConduitParameterType',
     'ConduitCall' => 'Phobject',
     'ConduitCallTestCase' => 'PhabricatorTestCase',
     'ConduitColumnsParameterType' => 'ConduitParameterType',
     'ConduitConnectConduitAPIMethod' => 'ConduitAPIMethod',
-    'ConduitEpochParameterType' => 'ConduitListParameterType',
+    'ConduitEpochParameterType' => 'ConduitParameterType',
     'ConduitException' => 'Exception',
     'ConduitGetCapabilitiesConduitAPIMethod' => 'ConduitAPIMethod',
     'ConduitGetCertificateConduitAPIMethod' => 'ConduitAPIMethod',
     'ConduitIntListParameterType' => 'ConduitListParameterType',
     'ConduitIntParameterType' => 'ConduitParameterType',
     'ConduitListParameterType' => 'ConduitParameterType',
     'ConduitLogGarbageCollector' => 'PhabricatorGarbageCollector',
     'ConduitMethodDoesNotExistException' => 'ConduitMethodNotFoundException',
     'ConduitMethodNotFoundException' => 'ConduitException',
     'ConduitPHIDListParameterType' => 'ConduitListParameterType',
     'ConduitPHIDParameterType' => 'ConduitParameterType',
     'ConduitParameterType' => 'Phobject',
     'ConduitPingConduitAPIMethod' => 'ConduitAPIMethod',
     'ConduitPointsParameterType' => 'ConduitParameterType',
     'ConduitProjectListParameterType' => 'ConduitListParameterType',
     'ConduitQueryConduitAPIMethod' => 'ConduitAPIMethod',
     'ConduitResultSearchEngineExtension' => 'PhabricatorSearchEngineExtension',
     'ConduitSSHWorkflow' => 'PhabricatorSSHWorkflow',
     'ConduitStringListParameterType' => 'ConduitListParameterType',
     'ConduitStringParameterType' => 'ConduitParameterType',
     'ConduitTokenGarbageCollector' => 'PhabricatorGarbageCollector',
     'ConduitUserListParameterType' => 'ConduitListParameterType',
     'ConduitUserParameterType' => 'ConduitParameterType',
-    'ConduitWildParameterType' => 'ConduitListParameterType',
+    'ConduitWildParameterType' => 'ConduitParameterType',
     'ConpherenceColumnViewController' => 'ConpherenceController',
     'ConpherenceConduitAPIMethod' => 'ConduitAPIMethod',
     'ConpherenceConfigOptions' => 'PhabricatorApplicationConfigOptions',
     'ConpherenceConstants' => 'Phobject',
     'ConpherenceController' => 'PhabricatorController',
     'ConpherenceCreateThreadConduitAPIMethod' => 'ConpherenceConduitAPIMethod',
     'ConpherenceDAO' => 'PhabricatorLiskDAO',
     'ConpherenceDurableColumnView' => 'AphrontTagView',
     'ConpherenceEditor' => 'PhabricatorApplicationTransactionEditor',
     'ConpherenceFormDragAndDropUploadControl' => 'AphrontFormControl',
     'ConpherenceFulltextQuery' => 'PhabricatorOffsetPagedQuery',
     'ConpherenceImageData' => 'ConpherenceConstants',
     'ConpherenceIndex' => 'ConpherenceDAO',
     'ConpherenceLayoutView' => 'AphrontView',
     'ConpherenceListController' => 'ConpherenceController',
     'ConpherenceMenuItemView' => 'AphrontTagView',
     'ConpherenceNewRoomController' => 'ConpherenceController',
     'ConpherenceNotificationPanelController' => 'ConpherenceController',
     'ConpherenceParticipant' => 'ConpherenceDAO',
     'ConpherenceParticipantCountQuery' => 'PhabricatorOffsetPagedQuery',
     'ConpherenceParticipantQuery' => 'PhabricatorOffsetPagedQuery',
     'ConpherenceParticipationStatus' => 'ConpherenceConstants',
     'ConpherencePeopleWidgetView' => 'ConpherenceWidgetView',
     'ConpherencePicCropControl' => 'AphrontFormControl',
     'ConpherenceQueryThreadConduitAPIMethod' => 'ConpherenceConduitAPIMethod',
     'ConpherenceQueryTransactionConduitAPIMethod' => 'ConpherenceConduitAPIMethod',
     'ConpherenceReplyHandler' => 'PhabricatorMailReplyHandler',
     'ConpherenceRoomListController' => 'ConpherenceController',
     'ConpherenceRoomTestCase' => 'ConpherenceTestCase',
     'ConpherenceSchemaSpec' => 'PhabricatorConfigSchemaSpec',
     'ConpherenceSettings' => 'ConpherenceConstants',
     'ConpherenceTestCase' => 'PhabricatorTestCase',
     'ConpherenceThread' => array(
       'ConpherenceDAO',
       'PhabricatorPolicyInterface',
       'PhabricatorApplicationTransactionInterface',
       'PhabricatorMentionableInterface',
       'PhabricatorDestructibleInterface',
     ),
     'ConpherenceThreadIndexEngineExtension' => 'PhabricatorIndexEngineExtension',
     'ConpherenceThreadListView' => 'AphrontView',
     'ConpherenceThreadMailReceiver' => 'PhabricatorObjectMailReceiver',
     'ConpherenceThreadMembersPolicyRule' => 'PhabricatorPolicyRule',
     'ConpherenceThreadQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
     'ConpherenceThreadRemarkupRule' => 'PhabricatorObjectRemarkupRule',
     'ConpherenceThreadSearchEngine' => 'PhabricatorApplicationSearchEngine',
     'ConpherenceTransaction' => 'PhabricatorApplicationTransaction',
     'ConpherenceTransactionComment' => 'PhabricatorApplicationTransactionComment',
     'ConpherenceTransactionQuery' => 'PhabricatorApplicationTransactionQuery',
     'ConpherenceTransactionRenderer' => 'Phobject',
     'ConpherenceTransactionView' => 'AphrontView',
     'ConpherenceUpdateActions' => 'ConpherenceConstants',
     'ConpherenceUpdateController' => 'ConpherenceController',
     'ConpherenceUpdateThreadConduitAPIMethod' => 'ConpherenceConduitAPIMethod',
     'ConpherenceViewController' => 'ConpherenceController',
     'ConpherenceWidgetConfigConstants' => 'ConpherenceConstants',
     'ConpherenceWidgetController' => 'ConpherenceController',
     'ConpherenceWidgetView' => 'AphrontView',
     'DarkConsoleController' => 'PhabricatorController',
     'DarkConsoleCore' => 'Phobject',
     'DarkConsoleDataController' => 'PhabricatorController',
     'DarkConsoleErrorLogPlugin' => 'DarkConsolePlugin',
     'DarkConsoleErrorLogPluginAPI' => 'Phobject',
     'DarkConsoleEventPlugin' => 'DarkConsolePlugin',
     'DarkConsoleEventPluginAPI' => 'PhabricatorEventListener',
     'DarkConsolePlugin' => 'Phobject',
     'DarkConsoleRequestPlugin' => 'DarkConsolePlugin',
     'DarkConsoleServicesPlugin' => 'DarkConsolePlugin',
     'DarkConsoleStartupPlugin' => 'DarkConsolePlugin',
     'DarkConsoleXHProfPlugin' => 'DarkConsolePlugin',
     'DarkConsoleXHProfPluginAPI' => 'Phobject',
     'DefaultDatabaseConfigurationProvider' => array(
       'Phobject',
       'DatabaseConfigurationProvider',
     ),
     'DifferentialAction' => 'Phobject',
     'DifferentialActionEmailCommand' => 'MetaMTAEmailTransactionCommand',
     'DifferentialActionMenuEventListener' => 'PhabricatorEventListener',
     'DifferentialAddCommentView' => 'AphrontView',
     'DifferentialAdjustmentMapTestCase' => 'PhutilTestCase',
     'DifferentialAffectedPath' => 'DifferentialDAO',
     'DifferentialApplyPatchField' => 'DifferentialCustomField',
     'DifferentialAsanaRepresentationField' => 'DifferentialCustomField',
     'DifferentialAuditorsField' => 'DifferentialStoredCustomField',
     'DifferentialAuthorField' => 'DifferentialCustomField',
     'DifferentialBlameRevisionField' => 'DifferentialStoredCustomField',
     'DifferentialBlockHeraldAction' => 'HeraldAction',
     'DifferentialBranchField' => 'DifferentialCustomField',
+    'DifferentialChangeDetailMailView' => 'DifferentialMailView',
     'DifferentialChangeHeraldFieldGroup' => 'HeraldFieldGroup',
     'DifferentialChangeType' => 'Phobject',
     'DifferentialChangesSinceLastUpdateField' => 'DifferentialCustomField',
     'DifferentialChangeset' => array(
       'DifferentialDAO',
       'PhabricatorPolicyInterface',
     ),
     'DifferentialChangesetDetailView' => 'AphrontView',
     'DifferentialChangesetFileTreeSideNavBuilder' => 'Phobject',
     'DifferentialChangesetHTMLRenderer' => 'DifferentialChangesetRenderer',
     'DifferentialChangesetListView' => 'AphrontView',
+    'DifferentialChangesetOneUpMailRenderer' => 'DifferentialChangesetRenderer',
     'DifferentialChangesetOneUpRenderer' => 'DifferentialChangesetHTMLRenderer',
     'DifferentialChangesetOneUpTestRenderer' => 'DifferentialChangesetTestRenderer',
     'DifferentialChangesetParser' => 'Phobject',
     'DifferentialChangesetParserTestCase' => 'PhabricatorTestCase',
     'DifferentialChangesetQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
     'DifferentialChangesetRenderer' => 'Phobject',
     'DifferentialChangesetTestRenderer' => 'DifferentialChangesetRenderer',
     'DifferentialChangesetTwoUpRenderer' => 'DifferentialChangesetHTMLRenderer',
     'DifferentialChangesetTwoUpTestRenderer' => 'DifferentialChangesetTestRenderer',
     'DifferentialChangesetViewController' => 'DifferentialController',
     'DifferentialCloseConduitAPIMethod' => 'DifferentialConduitAPIMethod',
     'DifferentialCommentPreviewController' => 'DifferentialController',
     'DifferentialCommentSaveController' => 'DifferentialController',
     'DifferentialCommitMessageParser' => 'Phobject',
     'DifferentialCommitMessageParserTestCase' => 'PhabricatorTestCase',
     'DifferentialCommitsField' => 'DifferentialCustomField',
     'DifferentialConduitAPIMethod' => 'ConduitAPIMethod',
     'DifferentialConflictsField' => 'DifferentialCustomField',
     'DifferentialController' => 'PhabricatorController',
     'DifferentialCoreCustomField' => 'DifferentialCustomField',
     'DifferentialCreateCommentConduitAPIMethod' => 'DifferentialConduitAPIMethod',
     'DifferentialCreateDiffConduitAPIMethod' => 'DifferentialConduitAPIMethod',
     'DifferentialCreateInlineConduitAPIMethod' => 'DifferentialConduitAPIMethod',
     'DifferentialCreateMailReceiver' => 'PhabricatorMailReceiver',
     'DifferentialCreateRawDiffConduitAPIMethod' => 'DifferentialConduitAPIMethod',
     'DifferentialCreateRevisionConduitAPIMethod' => 'DifferentialConduitAPIMethod',
     'DifferentialCustomField' => 'PhabricatorCustomField',
     'DifferentialCustomFieldDependsOnParser' => 'PhabricatorCustomFieldMonogramParser',
     'DifferentialCustomFieldDependsOnParserTestCase' => 'PhabricatorTestCase',
     'DifferentialCustomFieldNumericIndex' => 'PhabricatorCustomFieldNumericIndexStorage',
     'DifferentialCustomFieldRevertsParser' => 'PhabricatorCustomFieldMonogramParser',
     'DifferentialCustomFieldRevertsParserTestCase' => 'PhabricatorTestCase',
     'DifferentialCustomFieldStorage' => 'PhabricatorCustomFieldStorage',
     'DifferentialCustomFieldStringIndex' => 'PhabricatorCustomFieldStringIndexStorage',
     'DifferentialDAO' => 'PhabricatorLiskDAO',
     'DifferentialDefaultViewCapability' => 'PhabricatorPolicyCapability',
     'DifferentialDependenciesField' => 'DifferentialCustomField',
     'DifferentialDependsOnField' => 'DifferentialCustomField',
     'DifferentialDiff' => array(
       'DifferentialDAO',
       'PhabricatorPolicyInterface',
       'HarbormasterBuildableInterface',
       'HarbormasterCircleCIBuildableInterface',
       'PhabricatorApplicationTransactionInterface',
       'PhabricatorDestructibleInterface',
     ),
     'DifferentialDiffAffectedFilesHeraldField' => 'DifferentialDiffHeraldField',
     'DifferentialDiffAuthorHeraldField' => 'DifferentialDiffHeraldField',
     'DifferentialDiffAuthorProjectsHeraldField' => 'DifferentialDiffHeraldField',
     'DifferentialDiffContentAddedHeraldField' => 'DifferentialDiffHeraldField',
     'DifferentialDiffContentHeraldField' => 'DifferentialDiffHeraldField',
     'DifferentialDiffContentRemovedHeraldField' => 'DifferentialDiffHeraldField',
     'DifferentialDiffCreateController' => 'DifferentialController',
     'DifferentialDiffEditor' => 'PhabricatorApplicationTransactionEditor',
     'DifferentialDiffExtractionEngine' => 'Phobject',
     'DifferentialDiffHeraldField' => 'HeraldField',
     'DifferentialDiffHeraldFieldGroup' => 'HeraldFieldGroup',
     'DifferentialDiffInlineCommentQuery' => 'PhabricatorDiffInlineCommentQuery',
     'DifferentialDiffPHIDType' => 'PhabricatorPHIDType',
     'DifferentialDiffProperty' => 'DifferentialDAO',
     'DifferentialDiffQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
     'DifferentialDiffRepositoryHeraldField' => 'DifferentialDiffHeraldField',
     'DifferentialDiffRepositoryProjectsHeraldField' => 'DifferentialDiffHeraldField',
     'DifferentialDiffTestCase' => 'PhutilTestCase',
     'DifferentialDiffTransaction' => 'PhabricatorApplicationTransaction',
     'DifferentialDiffTransactionQuery' => 'PhabricatorApplicationTransactionQuery',
     'DifferentialDiffViewController' => 'DifferentialController',
     'DifferentialDoorkeeperRevisionFeedStoryPublisher' => 'DoorkeeperFeedStoryPublisher',
     'DifferentialDraft' => 'DifferentialDAO',
     'DifferentialEditPolicyField' => 'DifferentialCoreCustomField',
     'DifferentialFieldParseException' => 'Exception',
     'DifferentialFieldValidationException' => 'Exception',
     'DifferentialFindConduitAPIMethod' => 'DifferentialConduitAPIMethod',
     'DifferentialGetAllDiffsConduitAPIMethod' => 'DifferentialConduitAPIMethod',
     'DifferentialGetCommitMessageConduitAPIMethod' => 'DifferentialConduitAPIMethod',
     'DifferentialGetCommitPathsConduitAPIMethod' => 'DifferentialConduitAPIMethod',
     'DifferentialGetDiffConduitAPIMethod' => 'DifferentialConduitAPIMethod',
     'DifferentialGetRawDiffConduitAPIMethod' => 'DifferentialConduitAPIMethod',
     'DifferentialGetRevisionCommentsConduitAPIMethod' => 'DifferentialConduitAPIMethod',
     'DifferentialGetRevisionConduitAPIMethod' => 'DifferentialConduitAPIMethod',
     'DifferentialGetWorkingCopy' => 'Phobject',
     'DifferentialGitHubLandingStrategy' => 'DifferentialLandingStrategy',
     'DifferentialGitSVNIDField' => 'DifferentialCustomField',
     'DifferentialHarbormasterField' => 'DifferentialCustomField',
     'DifferentialHiddenComment' => 'DifferentialDAO',
     'DifferentialHostField' => 'DifferentialCustomField',
     'DifferentialHostedGitLandingStrategy' => 'DifferentialLandingStrategy',
     'DifferentialHostedMercurialLandingStrategy' => 'DifferentialLandingStrategy',
     'DifferentialHovercardEngineExtension' => 'PhabricatorHovercardEngineExtension',
     'DifferentialHunk' => array(
       'DifferentialDAO',
       'PhabricatorPolicyInterface',
     ),
     'DifferentialHunkParser' => 'Phobject',
     'DifferentialHunkParserTestCase' => 'PhabricatorTestCase',
     'DifferentialHunkQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
     'DifferentialHunkTestCase' => 'PhutilTestCase',
     'DifferentialInlineComment' => array(
       'Phobject',
       'PhabricatorInlineCommentInterface',
     ),
     'DifferentialInlineCommentEditController' => 'PhabricatorInlineCommentController',
+    'DifferentialInlineCommentMailView' => 'DifferentialMailView',
     'DifferentialInlineCommentPreviewController' => 'PhabricatorInlineCommentPreviewController',
     'DifferentialInlineCommentQuery' => 'PhabricatorOffsetPagedQuery',
     'DifferentialJIRAIssuesField' => 'DifferentialStoredCustomField',
     'DifferentialLandingActionMenuEventListener' => 'PhabricatorEventListener',
     'DifferentialLandingStrategy' => 'Phobject',
     'DifferentialLegacyHunk' => 'DifferentialHunk',
     'DifferentialLineAdjustmentMap' => 'Phobject',
     'DifferentialLintField' => 'DifferentialHarbormasterField',
     'DifferentialLintStatus' => 'Phobject',
     'DifferentialLocalCommitsView' => 'AphrontView',
+    'DifferentialMailView' => 'Phobject',
     'DifferentialManiphestTasksField' => 'DifferentialCoreCustomField',
     'DifferentialModernHunk' => 'DifferentialHunk',
     'DifferentialNextStepField' => 'DifferentialCustomField',
     'DifferentialParseCacheGarbageCollector' => 'PhabricatorGarbageCollector',
     'DifferentialParseCommitMessageConduitAPIMethod' => 'DifferentialConduitAPIMethod',
     'DifferentialParseRenderTestCase' => 'PhabricatorTestCase',
     'DifferentialPathField' => 'DifferentialCustomField',
     'DifferentialProjectReviewersField' => 'DifferentialCustomField',
     'DifferentialProjectsField' => 'DifferentialCoreCustomField',
     'DifferentialQueryConduitAPIMethod' => 'DifferentialConduitAPIMethod',
     'DifferentialQueryDiffsConduitAPIMethod' => 'DifferentialConduitAPIMethod',
     'DifferentialRawDiffRenderer' => 'Phobject',
     'DifferentialReleephRequestFieldSpecification' => 'Phobject',
     'DifferentialRemarkupRule' => 'PhabricatorObjectRemarkupRule',
     'DifferentialReplyHandler' => 'PhabricatorApplicationTransactionReplyHandler',
     'DifferentialRepositoryField' => 'DifferentialCoreCustomField',
     'DifferentialRepositoryLookup' => 'Phobject',
     'DifferentialRequiredSignaturesField' => 'DifferentialCoreCustomField',
     'DifferentialRevertPlanField' => 'DifferentialStoredCustomField',
     'DifferentialReviewedByField' => 'DifferentialCoreCustomField',
     'DifferentialReviewer' => 'Phobject',
     'DifferentialReviewerForRevisionEdgeType' => 'PhabricatorEdgeType',
     'DifferentialReviewerStatus' => 'Phobject',
     'DifferentialReviewersAddBlockingReviewersHeraldAction' => 'DifferentialReviewersHeraldAction',
     'DifferentialReviewersAddBlockingSelfHeraldAction' => 'DifferentialReviewersHeraldAction',
     'DifferentialReviewersAddReviewersHeraldAction' => 'DifferentialReviewersHeraldAction',
     'DifferentialReviewersAddSelfHeraldAction' => 'DifferentialReviewersHeraldAction',
     'DifferentialReviewersField' => 'DifferentialCoreCustomField',
     'DifferentialReviewersHeraldAction' => 'HeraldAction',
     'DifferentialReviewersView' => 'AphrontView',
     'DifferentialRevision' => array(
       'DifferentialDAO',
       'PhabricatorTokenReceiverInterface',
       'PhabricatorPolicyInterface',
       'PhabricatorExtendedPolicyInterface',
       'PhabricatorFlaggableInterface',
       'PhrequentTrackableInterface',
       'HarbormasterBuildableInterface',
       'PhabricatorSubscribableInterface',
       'PhabricatorCustomFieldInterface',
       'PhabricatorApplicationTransactionInterface',
       'PhabricatorMentionableInterface',
       'PhabricatorDestructibleInterface',
       'PhabricatorProjectInterface',
       'PhabricatorFulltextInterface',
     ),
     'DifferentialRevisionAffectedFilesHeraldField' => 'DifferentialRevisionHeraldField',
     'DifferentialRevisionAuthorHeraldField' => 'DifferentialRevisionHeraldField',
     'DifferentialRevisionAuthorProjectsHeraldField' => 'DifferentialRevisionHeraldField',
     'DifferentialRevisionCloseDetailsController' => 'DifferentialController',
     'DifferentialRevisionContentAddedHeraldField' => 'DifferentialRevisionHeraldField',
     'DifferentialRevisionContentHeraldField' => 'DifferentialRevisionHeraldField',
     'DifferentialRevisionContentRemovedHeraldField' => 'DifferentialRevisionHeraldField',
     'DifferentialRevisionControlSystem' => 'Phobject',
     'DifferentialRevisionDependedOnByRevisionEdgeType' => 'PhabricatorEdgeType',
     'DifferentialRevisionDependsOnRevisionEdgeType' => 'PhabricatorEdgeType',
     'DifferentialRevisionEditController' => 'DifferentialController',
     'DifferentialRevisionFulltextEngine' => 'PhabricatorFulltextEngine',
     'DifferentialRevisionHasCommitEdgeType' => 'PhabricatorEdgeType',
     'DifferentialRevisionHasReviewerEdgeType' => 'PhabricatorEdgeType',
     'DifferentialRevisionHasTaskEdgeType' => 'PhabricatorEdgeType',
     'DifferentialRevisionHeraldField' => 'HeraldField',
     'DifferentialRevisionHeraldFieldGroup' => 'HeraldFieldGroup',
     'DifferentialRevisionIDField' => 'DifferentialCustomField',
     'DifferentialRevisionLandController' => 'DifferentialController',
     'DifferentialRevisionListController' => 'DifferentialController',
     'DifferentialRevisionListView' => 'AphrontView',
     'DifferentialRevisionMailReceiver' => 'PhabricatorObjectMailReceiver',
     'DifferentialRevisionOperationController' => 'DifferentialController',
     'DifferentialRevisionPHIDType' => 'PhabricatorPHIDType',
     'DifferentialRevisionPackageHeraldField' => 'DifferentialRevisionHeraldField',
     'DifferentialRevisionPackageOwnerHeraldField' => 'DifferentialRevisionHeraldField',
     'DifferentialRevisionQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
     'DifferentialRevisionRepositoryHeraldField' => 'DifferentialRevisionHeraldField',
     'DifferentialRevisionRepositoryProjectsHeraldField' => 'DifferentialRevisionHeraldField',
     'DifferentialRevisionReviewersHeraldField' => 'DifferentialRevisionHeraldField',
     'DifferentialRevisionSearchEngine' => 'PhabricatorApplicationSearchEngine',
     'DifferentialRevisionStatus' => 'Phobject',
     'DifferentialRevisionSummaryHeraldField' => 'DifferentialRevisionHeraldField',
     'DifferentialRevisionTitleHeraldField' => 'DifferentialRevisionHeraldField',
     'DifferentialRevisionUpdateHistoryView' => 'AphrontView',
     'DifferentialRevisionViewController' => 'DifferentialController',
     'DifferentialSchemaSpec' => 'PhabricatorConfigSchemaSpec',
     'DifferentialSetDiffPropertyConduitAPIMethod' => 'DifferentialConduitAPIMethod',
     'DifferentialStoredCustomField' => 'DifferentialCustomField',
     'DifferentialSubscribersField' => 'DifferentialCoreCustomField',
     'DifferentialSummaryField' => 'DifferentialCoreCustomField',
     'DifferentialTestPlanField' => 'DifferentialCoreCustomField',
     'DifferentialTitleField' => 'DifferentialCoreCustomField',
     'DifferentialTransaction' => 'PhabricatorApplicationTransaction',
     'DifferentialTransactionComment' => 'PhabricatorApplicationTransactionComment',
     'DifferentialTransactionEditor' => 'PhabricatorApplicationTransactionEditor',
     'DifferentialTransactionQuery' => 'PhabricatorApplicationTransactionQuery',
     'DifferentialTransactionView' => 'PhabricatorApplicationTransactionView',
     'DifferentialUnitField' => 'DifferentialCustomField',
     'DifferentialUnitStatus' => 'Phobject',
     'DifferentialUnitTestResult' => 'Phobject',
     'DifferentialUpdateRevisionConduitAPIMethod' => 'DifferentialConduitAPIMethod',
     'DifferentialViewPolicyField' => 'DifferentialCoreCustomField',
     'DiffusionAuditorDatasource' => 'PhabricatorTypeaheadCompositeDatasource',
     'DiffusionAuditorFunctionDatasource' => 'PhabricatorTypeaheadCompositeDatasource',
     'DiffusionAuditorsAddAuditorsHeraldAction' => 'DiffusionAuditorsHeraldAction',
     'DiffusionAuditorsAddSelfHeraldAction' => 'DiffusionAuditorsHeraldAction',
     'DiffusionAuditorsHeraldAction' => 'HeraldAction',
     'DiffusionBlameConduitAPIMethod' => 'DiffusionQueryConduitAPIMethod',
     'DiffusionBlameQuery' => 'DiffusionQuery',
     'DiffusionBlockHeraldAction' => 'HeraldAction',
     'DiffusionBranchQueryConduitAPIMethod' => 'DiffusionQueryConduitAPIMethod',
     'DiffusionBranchTableController' => 'DiffusionController',
     'DiffusionBranchTableView' => 'DiffusionView',
     'DiffusionBrowseController' => 'DiffusionController',
     'DiffusionBrowseQueryConduitAPIMethod' => 'DiffusionQueryConduitAPIMethod',
     'DiffusionBrowseResultSet' => 'Phobject',
     'DiffusionBrowseTableView' => 'DiffusionView',
     'DiffusionCachedResolveRefsQuery' => 'DiffusionLowLevelQuery',
     'DiffusionChangeController' => 'DiffusionController',
     'DiffusionChangeHeraldFieldGroup' => 'HeraldFieldGroup',
+    'DiffusionCloneURIView' => 'AphrontView',
     'DiffusionCommandEngine' => 'Phobject',
     'DiffusionCommandEngineTestCase' => 'PhabricatorTestCase',
     'DiffusionCommitAffectedFilesHeraldField' => 'DiffusionCommitHeraldField',
     'DiffusionCommitAuthorHeraldField' => 'DiffusionCommitHeraldField',
     'DiffusionCommitAutocloseHeraldField' => 'DiffusionCommitHeraldField',
     'DiffusionCommitBranchesController' => 'DiffusionController',
     'DiffusionCommitBranchesHeraldField' => 'DiffusionCommitHeraldField',
     'DiffusionCommitCommitterHeraldField' => 'DiffusionCommitHeraldField',
     'DiffusionCommitController' => 'DiffusionController',
     'DiffusionCommitDiffContentAddedHeraldField' => 'DiffusionCommitHeraldField',
     'DiffusionCommitDiffContentHeraldField' => 'DiffusionCommitHeraldField',
     'DiffusionCommitDiffContentRemovedHeraldField' => 'DiffusionCommitHeraldField',
     'DiffusionCommitDiffEnormousHeraldField' => 'DiffusionCommitHeraldField',
     'DiffusionCommitEditController' => 'DiffusionController',
     'DiffusionCommitFulltextEngine' => 'PhabricatorFulltextEngine',
     'DiffusionCommitHasRevisionEdgeType' => 'PhabricatorEdgeType',
     'DiffusionCommitHasTaskEdgeType' => 'PhabricatorEdgeType',
     'DiffusionCommitHash' => 'Phobject',
     'DiffusionCommitHeraldField' => 'HeraldField',
     'DiffusionCommitHeraldFieldGroup' => 'HeraldFieldGroup',
     'DiffusionCommitHookEngine' => 'Phobject',
     'DiffusionCommitHookRejectException' => 'Exception',
     'DiffusionCommitMergeHeraldField' => 'DiffusionCommitHeraldField',
     'DiffusionCommitMessageHeraldField' => 'DiffusionCommitHeraldField',
     'DiffusionCommitPackageAuditHeraldField' => 'DiffusionCommitHeraldField',
     'DiffusionCommitPackageHeraldField' => 'DiffusionCommitHeraldField',
     'DiffusionCommitPackageOwnerHeraldField' => 'DiffusionCommitHeraldField',
     'DiffusionCommitParentsQueryConduitAPIMethod' => 'DiffusionQueryConduitAPIMethod',
     'DiffusionCommitQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
     'DiffusionCommitRef' => 'Phobject',
     'DiffusionCommitRemarkupRule' => 'PhabricatorObjectRemarkupRule',
     'DiffusionCommitRemarkupRuleTestCase' => 'PhabricatorTestCase',
     'DiffusionCommitRepositoryHeraldField' => 'DiffusionCommitHeraldField',
     'DiffusionCommitRepositoryProjectsHeraldField' => 'DiffusionCommitHeraldField',
     'DiffusionCommitRevertedByCommitEdgeType' => 'PhabricatorEdgeType',
     'DiffusionCommitRevertsCommitEdgeType' => 'PhabricatorEdgeType',
     'DiffusionCommitReviewerHeraldField' => 'DiffusionCommitHeraldField',
     'DiffusionCommitRevisionAcceptedHeraldField' => 'DiffusionCommitHeraldField',
     'DiffusionCommitRevisionHeraldField' => 'DiffusionCommitHeraldField',
     'DiffusionCommitRevisionReviewersHeraldField' => 'DiffusionCommitHeraldField',
     'DiffusionCommitRevisionSubscribersHeraldField' => 'DiffusionCommitHeraldField',
     'DiffusionCommitTagsController' => 'DiffusionController',
     'DiffusionConduitAPIMethod' => 'ConduitAPIMethod',
     'DiffusionController' => 'PhabricatorController',
     'DiffusionCreateCommentConduitAPIMethod' => 'DiffusionConduitAPIMethod',
     'DiffusionCreateRepositoriesCapability' => 'PhabricatorPolicyCapability',
+    'DiffusionDaemonLockException' => 'Exception',
     'DiffusionDefaultEditCapability' => 'PhabricatorPolicyCapability',
     'DiffusionDefaultPushCapability' => 'PhabricatorPolicyCapability',
     'DiffusionDefaultViewCapability' => 'PhabricatorPolicyCapability',
     'DiffusionDiffController' => 'DiffusionController',
     'DiffusionDiffInlineCommentQuery' => 'PhabricatorDiffInlineCommentQuery',
     'DiffusionDiffQueryConduitAPIMethod' => 'DiffusionQueryConduitAPIMethod',
     'DiffusionDoorkeeperCommitFeedStoryPublisher' => 'DoorkeeperFeedStoryPublisher',
     'DiffusionEmptyResultView' => 'DiffusionView',
     'DiffusionExistsQueryConduitAPIMethod' => 'DiffusionQueryConduitAPIMethod',
     'DiffusionExternalController' => 'DiffusionController',
     'DiffusionExternalSymbolQuery' => 'Phobject',
     'DiffusionExternalSymbolsSource' => 'Phobject',
     'DiffusionFileContentQuery' => 'DiffusionQuery',
     'DiffusionFileContentQueryConduitAPIMethod' => 'DiffusionQueryConduitAPIMethod',
     'DiffusionFindSymbolsConduitAPIMethod' => 'DiffusionConduitAPIMethod',
     'DiffusionGetLintMessagesConduitAPIMethod' => 'DiffusionConduitAPIMethod',
     'DiffusionGetRecentCommitsByPathConduitAPIMethod' => 'DiffusionConduitAPIMethod',
     'DiffusionGitBlameQuery' => 'DiffusionBlameQuery',
     'DiffusionGitBranch' => 'Phobject',
     'DiffusionGitBranchTestCase' => 'PhabricatorTestCase',
     'DiffusionGitCommandEngine' => 'DiffusionCommandEngine',
     'DiffusionGitFileContentQuery' => 'DiffusionFileContentQuery',
     'DiffusionGitLFSAuthenticateWorkflow' => 'DiffusionGitSSHWorkflow',
     'DiffusionGitLFSResponse' => 'AphrontResponse',
     'DiffusionGitLFSTemporaryTokenType' => 'PhabricatorAuthTemporaryTokenType',
     'DiffusionGitRawDiffQuery' => 'DiffusionRawDiffQuery',
     'DiffusionGitReceivePackSSHWorkflow' => 'DiffusionGitSSHWorkflow',
     'DiffusionGitRequest' => 'DiffusionRequest',
     'DiffusionGitResponse' => 'AphrontResponse',
     'DiffusionGitSSHWorkflow' => array(
       'DiffusionSSHWorkflow',
       'DiffusionRepositoryClusterEngineLogInterface',
     ),
     'DiffusionGitUploadPackSSHWorkflow' => 'DiffusionGitSSHWorkflow',
     'DiffusionHistoryController' => 'DiffusionController',
     'DiffusionHistoryQueryConduitAPIMethod' => 'DiffusionQueryConduitAPIMethod',
     'DiffusionHistoryTableView' => 'DiffusionView',
     'DiffusionHovercardEngineExtension' => 'PhabricatorHovercardEngineExtension',
     'DiffusionInlineCommentController' => 'PhabricatorInlineCommentController',
     'DiffusionInlineCommentPreviewController' => 'PhabricatorInlineCommentPreviewController',
     'DiffusionInternalGitRawDiffQueryConduitAPIMethod' => 'DiffusionQueryConduitAPIMethod',
     'DiffusionLastModifiedController' => 'DiffusionController',
     'DiffusionLastModifiedQueryConduitAPIMethod' => 'DiffusionQueryConduitAPIMethod',
     'DiffusionLintController' => 'DiffusionController',
     'DiffusionLintCountQuery' => 'PhabricatorQuery',
     'DiffusionLintSaveRunner' => 'Phobject',
+    'DiffusionLocalRepositoryFilter' => 'Phobject',
     'DiffusionLookSoonConduitAPIMethod' => 'DiffusionConduitAPIMethod',
     'DiffusionLowLevelCommitFieldsQuery' => 'DiffusionLowLevelQuery',
     'DiffusionLowLevelCommitQuery' => 'DiffusionLowLevelQuery',
     'DiffusionLowLevelGitRefQuery' => 'DiffusionLowLevelQuery',
     'DiffusionLowLevelMercurialBranchesQuery' => 'DiffusionLowLevelQuery',
     'DiffusionLowLevelMercurialPathsQuery' => 'DiffusionLowLevelQuery',
     'DiffusionLowLevelMercurialPathsQueryTests' => 'PhabricatorTestCase',
     'DiffusionLowLevelParentsQuery' => 'DiffusionLowLevelQuery',
     'DiffusionLowLevelQuery' => 'Phobject',
     'DiffusionLowLevelResolveRefsQuery' => 'DiffusionLowLevelQuery',
     'DiffusionMercurialBlameQuery' => 'DiffusionBlameQuery',
     'DiffusionMercurialCommandEngine' => 'DiffusionCommandEngine',
     'DiffusionMercurialFileContentQuery' => 'DiffusionFileContentQuery',
     'DiffusionMercurialRawDiffQuery' => 'DiffusionRawDiffQuery',
     'DiffusionMercurialRequest' => 'DiffusionRequest',
     'DiffusionMercurialResponse' => 'AphrontResponse',
     'DiffusionMercurialSSHWorkflow' => 'DiffusionSSHWorkflow',
     'DiffusionMercurialServeSSHWorkflow' => 'DiffusionMercurialSSHWorkflow',
     'DiffusionMercurialWireClientSSHProtocolChannel' => 'PhutilProtocolChannel',
     'DiffusionMercurialWireProtocol' => 'Phobject',
     'DiffusionMercurialWireProtocolTests' => 'PhabricatorTestCase',
     'DiffusionMercurialWireSSHTestCase' => 'PhabricatorTestCase',
     'DiffusionMergedCommitsQueryConduitAPIMethod' => 'DiffusionQueryConduitAPIMethod',
-    'DiffusionMirrorDeleteController' => 'DiffusionController',
-    'DiffusionMirrorEditController' => 'DiffusionController',
     'DiffusionPathChange' => 'Phobject',
     'DiffusionPathChangeQuery' => 'Phobject',
     'DiffusionPathCompleteController' => 'DiffusionController',
     'DiffusionPathIDQuery' => 'Phobject',
     'DiffusionPathQuery' => 'Phobject',
     'DiffusionPathQueryTestCase' => 'PhabricatorTestCase',
     'DiffusionPathTreeController' => 'DiffusionController',
     'DiffusionPathValidateController' => 'DiffusionController',
     'DiffusionPhpExternalSymbolsSource' => 'DiffusionExternalSymbolsSource',
     'DiffusionPreCommitContentAffectedFilesHeraldField' => 'DiffusionPreCommitContentHeraldField',
     'DiffusionPreCommitContentAuthorHeraldField' => 'DiffusionPreCommitContentHeraldField',
     'DiffusionPreCommitContentAuthorRawHeraldField' => 'DiffusionPreCommitContentHeraldField',
     'DiffusionPreCommitContentBranchesHeraldField' => 'DiffusionPreCommitContentHeraldField',
     'DiffusionPreCommitContentCommitterHeraldField' => 'DiffusionPreCommitContentHeraldField',
     'DiffusionPreCommitContentCommitterRawHeraldField' => 'DiffusionPreCommitContentHeraldField',
     'DiffusionPreCommitContentDiffContentAddedHeraldField' => 'DiffusionPreCommitContentHeraldField',
     'DiffusionPreCommitContentDiffContentHeraldField' => 'DiffusionPreCommitContentHeraldField',
     'DiffusionPreCommitContentDiffContentRemovedHeraldField' => 'DiffusionPreCommitContentHeraldField',
     'DiffusionPreCommitContentDiffEnormousHeraldField' => 'DiffusionPreCommitContentHeraldField',
     'DiffusionPreCommitContentHeraldField' => 'HeraldField',
     'DiffusionPreCommitContentMergeHeraldField' => 'DiffusionPreCommitContentHeraldField',
     'DiffusionPreCommitContentMessageHeraldField' => 'DiffusionPreCommitContentHeraldField',
     'DiffusionPreCommitContentPusherHeraldField' => 'DiffusionPreCommitContentHeraldField',
     'DiffusionPreCommitContentPusherIsCommitterHeraldField' => 'DiffusionPreCommitContentHeraldField',
     'DiffusionPreCommitContentPusherProjectsHeraldField' => 'DiffusionPreCommitContentHeraldField',
     'DiffusionPreCommitContentRepositoryHeraldField' => 'DiffusionPreCommitContentHeraldField',
     'DiffusionPreCommitContentRepositoryProjectsHeraldField' => 'DiffusionPreCommitContentHeraldField',
     'DiffusionPreCommitContentRevisionAcceptedHeraldField' => 'DiffusionPreCommitContentHeraldField',
     'DiffusionPreCommitContentRevisionHeraldField' => 'DiffusionPreCommitContentHeraldField',
     'DiffusionPreCommitContentRevisionReviewersHeraldField' => 'DiffusionPreCommitContentHeraldField',
     'DiffusionPreCommitContentRevisionSubscribersHeraldField' => 'DiffusionPreCommitContentHeraldField',
     'DiffusionPreCommitRefChangeHeraldField' => 'DiffusionPreCommitRefHeraldField',
     'DiffusionPreCommitRefHeraldField' => 'HeraldField',
     'DiffusionPreCommitRefHeraldFieldGroup' => 'HeraldFieldGroup',
     'DiffusionPreCommitRefNameHeraldField' => 'DiffusionPreCommitRefHeraldField',
     'DiffusionPreCommitRefPusherHeraldField' => 'DiffusionPreCommitRefHeraldField',
     'DiffusionPreCommitRefPusherProjectsHeraldField' => 'DiffusionPreCommitRefHeraldField',
     'DiffusionPreCommitRefRepositoryHeraldField' => 'DiffusionPreCommitRefHeraldField',
     'DiffusionPreCommitRefRepositoryProjectsHeraldField' => 'DiffusionPreCommitRefHeraldField',
     'DiffusionPreCommitRefTypeHeraldField' => 'DiffusionPreCommitRefHeraldField',
     'DiffusionPullEventGarbageCollector' => 'PhabricatorGarbageCollector',
     'DiffusionPushCapability' => 'PhabricatorPolicyCapability',
     'DiffusionPushEventViewController' => 'DiffusionPushLogController',
     'DiffusionPushLogController' => 'DiffusionController',
     'DiffusionPushLogListController' => 'DiffusionPushLogController',
     'DiffusionPushLogListView' => 'AphrontView',
     'DiffusionPythonExternalSymbolsSource' => 'DiffusionExternalSymbolsSource',
     'DiffusionQuery' => 'PhabricatorQuery',
     'DiffusionQueryCommitsConduitAPIMethod' => 'DiffusionConduitAPIMethod',
     'DiffusionQueryConduitAPIMethod' => 'DiffusionConduitAPIMethod',
     'DiffusionQueryPathsConduitAPIMethod' => 'DiffusionQueryConduitAPIMethod',
     'DiffusionRawDiffQuery' => 'DiffusionQuery',
     'DiffusionRawDiffQueryConduitAPIMethod' => 'DiffusionQueryConduitAPIMethod',
     'DiffusionReadmeView' => 'DiffusionView',
     'DiffusionRefDatasource' => 'PhabricatorTypeaheadDatasource',
     'DiffusionRefNotFoundException' => 'Exception',
     'DiffusionRefTableController' => 'DiffusionController',
     'DiffusionRefsQueryConduitAPIMethod' => 'DiffusionQueryConduitAPIMethod',
     'DiffusionRenameHistoryQuery' => 'Phobject',
     'DiffusionRepositoryActionsManagementPanel' => 'DiffusionRepositoryManagementPanel',
     'DiffusionRepositoryAutomationManagementPanel' => 'DiffusionRepositoryManagementPanel',
     'DiffusionRepositoryBasicsManagementPanel' => 'DiffusionRepositoryManagementPanel',
     'DiffusionRepositoryBranchesManagementPanel' => 'DiffusionRepositoryManagementPanel',
     'DiffusionRepositoryByIDRemarkupRule' => 'PhabricatorObjectRemarkupRule',
     'DiffusionRepositoryClusterEngine' => 'Phobject',
     'DiffusionRepositoryController' => 'DiffusionController',
-    'DiffusionRepositoryCreateController' => 'DiffusionRepositoryEditController',
     'DiffusionRepositoryDatasource' => 'PhabricatorTypeaheadDatasource',
     'DiffusionRepositoryDefaultController' => 'DiffusionController',
-    'DiffusionRepositoryEditActionsController' => 'DiffusionRepositoryEditController',
-    'DiffusionRepositoryEditActivateController' => 'DiffusionRepositoryEditController',
-    'DiffusionRepositoryEditAutomationController' => 'DiffusionRepositoryEditController',
-    'DiffusionRepositoryEditBasicController' => 'DiffusionRepositoryEditController',
-    'DiffusionRepositoryEditBranchesController' => 'DiffusionRepositoryEditController',
+    'DiffusionRepositoryDocumentationManagementPanel' => 'DiffusionRepositoryManagementPanel',
+    'DiffusionRepositoryEditActivateController' => 'DiffusionRepositoryManageController',
     'DiffusionRepositoryEditConduitAPIMethod' => 'PhabricatorEditEngineAPIMethod',
-    'DiffusionRepositoryEditController' => 'DiffusionController',
-    'DiffusionRepositoryEditDangerousController' => 'DiffusionRepositoryEditController',
-    'DiffusionRepositoryEditDeleteController' => 'DiffusionRepositoryEditController',
-    'DiffusionRepositoryEditEncodingController' => 'DiffusionRepositoryEditController',
+    'DiffusionRepositoryEditController' => 'DiffusionRepositoryManageController',
+    'DiffusionRepositoryEditDangerousController' => 'DiffusionRepositoryManageController',
+    'DiffusionRepositoryEditDeleteController' => 'DiffusionRepositoryManageController',
     'DiffusionRepositoryEditEngine' => 'PhabricatorEditEngine',
-    'DiffusionRepositoryEditHostingController' => 'DiffusionRepositoryEditController',
-    'DiffusionRepositoryEditMainController' => 'DiffusionRepositoryEditController',
-    'DiffusionRepositoryEditStagingController' => 'DiffusionRepositoryEditController',
-    'DiffusionRepositoryEditStorageController' => 'DiffusionRepositoryEditController',
-    'DiffusionRepositoryEditSubversionController' => 'DiffusionRepositoryEditController',
-    'DiffusionRepositoryEditUpdateController' => 'DiffusionRepositoryEditController',
-    'DiffusionRepositoryEditproController' => 'DiffusionRepositoryEditController',
+    'DiffusionRepositoryEditUpdateController' => 'DiffusionRepositoryManageController',
     'DiffusionRepositoryHistoryManagementPanel' => 'DiffusionRepositoryManagementPanel',
     'DiffusionRepositoryListController' => 'DiffusionController',
     'DiffusionRepositoryManageController' => 'DiffusionController',
+    'DiffusionRepositoryManagePanelsController' => 'DiffusionRepositoryManageController',
     'DiffusionRepositoryManagementPanel' => 'Phobject',
-    'DiffusionRepositoryNewController' => 'DiffusionController',
     'DiffusionRepositoryPath' => 'Phobject',
     'DiffusionRepositoryPoliciesManagementPanel' => 'DiffusionRepositoryManagementPanel',
     'DiffusionRepositoryRef' => 'Phobject',
     'DiffusionRepositoryRemarkupRule' => 'PhabricatorObjectRemarkupRule',
     'DiffusionRepositorySearchConduitAPIMethod' => 'PhabricatorSearchEngineAPIMethod',
     'DiffusionRepositoryStagingManagementPanel' => 'DiffusionRepositoryManagementPanel',
     'DiffusionRepositoryStatusManagementPanel' => 'DiffusionRepositoryManagementPanel',
     'DiffusionRepositoryStorageManagementPanel' => 'DiffusionRepositoryManagementPanel',
-    'DiffusionRepositorySymbolsController' => 'DiffusionRepositoryEditController',
+    'DiffusionRepositorySubversionManagementPanel' => 'DiffusionRepositoryManagementPanel',
     'DiffusionRepositorySymbolsManagementPanel' => 'DiffusionRepositoryManagementPanel',
     'DiffusionRepositoryTag' => 'Phobject',
-    'DiffusionRepositoryTestAutomationController' => 'DiffusionRepositoryEditController',
+    'DiffusionRepositoryTestAutomationController' => 'DiffusionRepositoryManageController',
+    'DiffusionRepositoryURICredentialController' => 'DiffusionController',
     'DiffusionRepositoryURIDisableController' => 'DiffusionController',
     'DiffusionRepositoryURIEditController' => 'DiffusionController',
     'DiffusionRepositoryURIViewController' => 'DiffusionController',
     'DiffusionRepositoryURIsIndexEngineExtension' => 'PhabricatorIndexEngineExtension',
     'DiffusionRepositoryURIsManagementPanel' => 'DiffusionRepositoryManagementPanel',
     'DiffusionRepositoryURIsSearchEngineAttachment' => 'PhabricatorSearchEngineAttachment',
     'DiffusionRequest' => 'Phobject',
     'DiffusionResolveRefsConduitAPIMethod' => 'DiffusionQueryConduitAPIMethod',
     'DiffusionResolveUserQuery' => 'Phobject',
     'DiffusionSSHWorkflow' => 'PhabricatorSSHWorkflow',
     'DiffusionSearchQueryConduitAPIMethod' => 'DiffusionQueryConduitAPIMethod',
     'DiffusionServeController' => 'DiffusionController',
     'DiffusionSetPasswordSettingsPanel' => 'PhabricatorSettingsPanel',
     'DiffusionSetupException' => 'Exception',
     'DiffusionSubversionCommandEngine' => 'DiffusionCommandEngine',
     'DiffusionSubversionSSHWorkflow' => 'DiffusionSSHWorkflow',
     'DiffusionSubversionServeSSHWorkflow' => 'DiffusionSubversionSSHWorkflow',
     'DiffusionSubversionWireProtocol' => 'Phobject',
     'DiffusionSubversionWireProtocolTestCase' => 'PhabricatorTestCase',
     'DiffusionSvnBlameQuery' => 'DiffusionBlameQuery',
     'DiffusionSvnFileContentQuery' => 'DiffusionFileContentQuery',
     'DiffusionSvnRawDiffQuery' => 'DiffusionRawDiffQuery',
     'DiffusionSvnRequest' => 'DiffusionRequest',
     'DiffusionSymbolController' => 'DiffusionController',
     'DiffusionSymbolDatasource' => 'PhabricatorTypeaheadDatasource',
     'DiffusionSymbolQuery' => 'PhabricatorOffsetPagedQuery',
     'DiffusionTagListController' => 'DiffusionController',
     'DiffusionTagListView' => 'DiffusionView',
     'DiffusionTagsQueryConduitAPIMethod' => 'DiffusionQueryConduitAPIMethod',
     'DiffusionURIEditConduitAPIMethod' => 'PhabricatorEditEngineAPIMethod',
     'DiffusionURIEditEngine' => 'PhabricatorEditEngine',
     'DiffusionURIEditor' => 'PhabricatorApplicationTransactionEditor',
     'DiffusionURITestCase' => 'PhutilTestCase',
     'DiffusionUpdateCoverageConduitAPIMethod' => 'DiffusionConduitAPIMethod',
     'DiffusionView' => 'AphrontView',
     'DivinerArticleAtomizer' => 'DivinerAtomizer',
     'DivinerAtom' => 'Phobject',
     'DivinerAtomCache' => 'DivinerDiskCache',
     'DivinerAtomController' => 'DivinerController',
     'DivinerAtomListController' => 'DivinerController',
     'DivinerAtomPHIDType' => 'PhabricatorPHIDType',
     'DivinerAtomQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
     'DivinerAtomRef' => 'Phobject',
     'DivinerAtomSearchEngine' => 'PhabricatorApplicationSearchEngine',
     'DivinerAtomizeWorkflow' => 'DivinerWorkflow',
     'DivinerAtomizer' => 'Phobject',
     'DivinerBookController' => 'DivinerController',
     'DivinerBookDatasource' => 'PhabricatorTypeaheadDatasource',
     'DivinerBookEditController' => 'DivinerController',
     'DivinerBookItemView' => 'AphrontTagView',
     'DivinerBookPHIDType' => 'PhabricatorPHIDType',
     'DivinerBookQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
     'DivinerController' => 'PhabricatorController',
     'DivinerDAO' => 'PhabricatorLiskDAO',
     'DivinerDefaultEditCapability' => 'PhabricatorPolicyCapability',
     'DivinerDefaultRenderer' => 'DivinerRenderer',
     'DivinerDefaultViewCapability' => 'PhabricatorPolicyCapability',
     'DivinerDiskCache' => 'Phobject',
     'DivinerFileAtomizer' => 'DivinerAtomizer',
     'DivinerFindController' => 'DivinerController',
     'DivinerGenerateWorkflow' => 'DivinerWorkflow',
     'DivinerLiveAtom' => 'DivinerDAO',
     'DivinerLiveBook' => array(
       'DivinerDAO',
       'PhabricatorPolicyInterface',
       'PhabricatorProjectInterface',
       'PhabricatorDestructibleInterface',
       'PhabricatorApplicationTransactionInterface',
       'PhabricatorFulltextInterface',
     ),
     'DivinerLiveBookEditor' => 'PhabricatorApplicationTransactionEditor',
     'DivinerLiveBookFulltextEngine' => 'PhabricatorFulltextEngine',
     'DivinerLiveBookTransaction' => 'PhabricatorApplicationTransaction',
     'DivinerLiveBookTransactionQuery' => 'PhabricatorApplicationTransactionQuery',
     'DivinerLivePublisher' => 'DivinerPublisher',
     'DivinerLiveSymbol' => array(
       'DivinerDAO',
       'PhabricatorPolicyInterface',
       'PhabricatorMarkupInterface',
       'PhabricatorDestructibleInterface',
       'PhabricatorFulltextInterface',
     ),
     'DivinerLiveSymbolFulltextEngine' => 'PhabricatorFulltextEngine',
     'DivinerMainController' => 'DivinerController',
     'DivinerPHPAtomizer' => 'DivinerAtomizer',
     'DivinerParameterTableView' => 'AphrontTagView',
     'DivinerPublishCache' => 'DivinerDiskCache',
     'DivinerPublisher' => 'Phobject',
     'DivinerRenderer' => 'Phobject',
     'DivinerReturnTableView' => 'AphrontTagView',
     'DivinerSchemaSpec' => 'PhabricatorConfigSchemaSpec',
     'DivinerSectionView' => 'AphrontTagView',
     'DivinerStaticPublisher' => 'DivinerPublisher',
     'DivinerSymbolRemarkupRule' => 'PhutilRemarkupRule',
     'DivinerWorkflow' => 'PhabricatorManagementWorkflow',
     'DoorkeeperAsanaFeedWorker' => 'DoorkeeperFeedWorker',
     'DoorkeeperAsanaRemarkupRule' => 'DoorkeeperRemarkupRule',
     'DoorkeeperBridge' => 'Phobject',
     'DoorkeeperBridgeAsana' => 'DoorkeeperBridge',
     'DoorkeeperBridgeGitHub' => 'DoorkeeperBridge',
     'DoorkeeperBridgeGitHubIssue' => 'DoorkeeperBridgeGitHub',
     'DoorkeeperBridgeGitHubUser' => 'DoorkeeperBridgeGitHub',
     'DoorkeeperBridgeJIRA' => 'DoorkeeperBridge',
     'DoorkeeperBridgeJIRATestCase' => 'PhabricatorTestCase',
     'DoorkeeperBridgedObjectCurtainExtension' => 'PHUICurtainExtension',
     'DoorkeeperDAO' => 'PhabricatorLiskDAO',
     'DoorkeeperExternalObject' => array(
       'DoorkeeperDAO',
       'PhabricatorPolicyInterface',
     ),
     'DoorkeeperExternalObjectPHIDType' => 'PhabricatorPHIDType',
     'DoorkeeperExternalObjectQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
     'DoorkeeperFeedStoryPublisher' => 'Phobject',
     'DoorkeeperFeedWorker' => 'FeedPushWorker',
     'DoorkeeperImportEngine' => 'Phobject',
     'DoorkeeperJIRAFeedWorker' => 'DoorkeeperFeedWorker',
     'DoorkeeperJIRARemarkupRule' => 'DoorkeeperRemarkupRule',
     'DoorkeeperMissingLinkException' => 'Exception',
     'DoorkeeperObjectRef' => 'Phobject',
     'DoorkeeperRemarkupRule' => 'PhutilRemarkupRule',
     'DoorkeeperSchemaSpec' => 'PhabricatorConfigSchemaSpec',
     'DoorkeeperTagView' => 'AphrontView',
     'DoorkeeperTagsController' => 'PhabricatorController',
     'DrydockAlmanacServiceHostBlueprintImplementation' => 'DrydockBlueprintImplementation',
     'DrydockApacheWebrootInterface' => 'DrydockWebrootInterface',
     'DrydockAuthorization' => array(
       'DrydockDAO',
       'PhabricatorPolicyInterface',
     ),
     'DrydockAuthorizationAuthorizeController' => 'DrydockController',
     'DrydockAuthorizationListController' => 'DrydockController',
     'DrydockAuthorizationListView' => 'AphrontView',
     'DrydockAuthorizationPHIDType' => 'PhabricatorPHIDType',
     'DrydockAuthorizationQuery' => 'DrydockQuery',
     'DrydockAuthorizationSearchEngine' => 'PhabricatorApplicationSearchEngine',
     'DrydockAuthorizationViewController' => 'DrydockController',
     'DrydockBlueprint' => array(
       'DrydockDAO',
       'PhabricatorApplicationTransactionInterface',
       'PhabricatorPolicyInterface',
       'PhabricatorCustomFieldInterface',
       'PhabricatorNgramsInterface',
       'PhabricatorProjectInterface',
     ),
     'DrydockBlueprintController' => 'DrydockController',
     'DrydockBlueprintCoreCustomField' => array(
       'DrydockBlueprintCustomField',
       'PhabricatorStandardCustomFieldInterface',
     ),
     'DrydockBlueprintCustomField' => 'PhabricatorCustomField',
     'DrydockBlueprintDatasource' => 'PhabricatorTypeaheadDatasource',
     'DrydockBlueprintDisableController' => 'DrydockBlueprintController',
     'DrydockBlueprintEditController' => 'DrydockBlueprintController',
     'DrydockBlueprintEditEngine' => 'PhabricatorEditEngine',
     'DrydockBlueprintEditor' => 'PhabricatorApplicationTransactionEditor',
     'DrydockBlueprintImplementation' => 'Phobject',
     'DrydockBlueprintImplementationTestCase' => 'PhabricatorTestCase',
     'DrydockBlueprintListController' => 'DrydockBlueprintController',
     'DrydockBlueprintNameNgrams' => 'PhabricatorSearchNgrams',
     'DrydockBlueprintPHIDType' => 'PhabricatorPHIDType',
     'DrydockBlueprintQuery' => 'DrydockQuery',
     'DrydockBlueprintSearchEngine' => 'PhabricatorApplicationSearchEngine',
     'DrydockBlueprintTransaction' => 'PhabricatorApplicationTransaction',
     'DrydockBlueprintTransactionQuery' => 'PhabricatorApplicationTransactionQuery',
     'DrydockBlueprintViewController' => 'DrydockBlueprintController',
     'DrydockCommand' => array(
       'DrydockDAO',
       'PhabricatorPolicyInterface',
     ),
     'DrydockCommandError' => 'Phobject',
     'DrydockCommandInterface' => 'DrydockInterface',
     'DrydockCommandQuery' => 'DrydockQuery',
     'DrydockConsoleController' => 'DrydockController',
     'DrydockConstants' => 'Phobject',
     'DrydockController' => 'PhabricatorController',
     'DrydockCreateBlueprintsCapability' => 'PhabricatorPolicyCapability',
     'DrydockDAO' => 'PhabricatorLiskDAO',
     'DrydockDefaultEditCapability' => 'PhabricatorPolicyCapability',
     'DrydockDefaultViewCapability' => 'PhabricatorPolicyCapability',
     'DrydockFilesystemInterface' => 'DrydockInterface',
     'DrydockInterface' => 'Phobject',
     'DrydockLandRepositoryOperation' => 'DrydockRepositoryOperationType',
     'DrydockLease' => array(
       'DrydockDAO',
       'PhabricatorPolicyInterface',
     ),
     'DrydockLeaseAcquiredLogType' => 'DrydockLogType',
     'DrydockLeaseActivatedLogType' => 'DrydockLogType',
     'DrydockLeaseActivationFailureLogType' => 'DrydockLogType',
     'DrydockLeaseActivationYieldLogType' => 'DrydockLogType',
     'DrydockLeaseController' => 'DrydockController',
     'DrydockLeaseDatasource' => 'PhabricatorTypeaheadDatasource',
     'DrydockLeaseDestroyedLogType' => 'DrydockLogType',
     'DrydockLeaseListController' => 'DrydockLeaseController',
     'DrydockLeaseListView' => 'AphrontView',
     'DrydockLeaseNoAuthorizationsLogType' => 'DrydockLogType',
     'DrydockLeaseNoBlueprintsLogType' => 'DrydockLogType',
     'DrydockLeasePHIDType' => 'PhabricatorPHIDType',
     'DrydockLeaseQuery' => 'DrydockQuery',
     'DrydockLeaseQueuedLogType' => 'DrydockLogType',
     'DrydockLeaseReclaimLogType' => 'DrydockLogType',
     'DrydockLeaseReleaseController' => 'DrydockLeaseController',
     'DrydockLeaseReleasedLogType' => 'DrydockLogType',
     'DrydockLeaseSearchEngine' => 'PhabricatorApplicationSearchEngine',
     'DrydockLeaseStatus' => 'DrydockConstants',
     'DrydockLeaseUpdateWorker' => 'DrydockWorker',
     'DrydockLeaseViewController' => 'DrydockLeaseController',
     'DrydockLeaseWaitingForResourcesLogType' => 'DrydockLogType',
     'DrydockLog' => array(
       'DrydockDAO',
       'PhabricatorPolicyInterface',
     ),
     'DrydockLogController' => 'DrydockController',
     'DrydockLogGarbageCollector' => 'PhabricatorGarbageCollector',
     'DrydockLogListController' => 'DrydockLogController',
     'DrydockLogListView' => 'AphrontView',
     'DrydockLogQuery' => 'DrydockQuery',
     'DrydockLogSearchEngine' => 'PhabricatorApplicationSearchEngine',
     'DrydockLogType' => 'Phobject',
     'DrydockManagementCommandWorkflow' => 'DrydockManagementWorkflow',
     'DrydockManagementLeaseWorkflow' => 'DrydockManagementWorkflow',
     'DrydockManagementReclaimWorkflow' => 'DrydockManagementWorkflow',
     'DrydockManagementReleaseLeaseWorkflow' => 'DrydockManagementWorkflow',
     'DrydockManagementReleaseResourceWorkflow' => 'DrydockManagementWorkflow',
     'DrydockManagementUpdateLeaseWorkflow' => 'DrydockManagementWorkflow',
     'DrydockManagementUpdateResourceWorkflow' => 'DrydockManagementWorkflow',
     'DrydockManagementWorkflow' => 'PhabricatorManagementWorkflow',
     'DrydockObjectAuthorizationView' => 'AphrontView',
     'DrydockQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
     'DrydockRepositoryOperation' => array(
       'DrydockDAO',
       'PhabricatorPolicyInterface',
     ),
     'DrydockRepositoryOperationController' => 'DrydockController',
     'DrydockRepositoryOperationDismissController' => 'DrydockRepositoryOperationController',
     'DrydockRepositoryOperationListController' => 'DrydockRepositoryOperationController',
     'DrydockRepositoryOperationPHIDType' => 'PhabricatorPHIDType',
     'DrydockRepositoryOperationQuery' => 'DrydockQuery',
     'DrydockRepositoryOperationSearchEngine' => 'PhabricatorApplicationSearchEngine',
     'DrydockRepositoryOperationStatusController' => 'DrydockRepositoryOperationController',
     'DrydockRepositoryOperationStatusView' => 'AphrontView',
     'DrydockRepositoryOperationType' => 'Phobject',
     'DrydockRepositoryOperationUpdateWorker' => 'DrydockWorker',
     'DrydockRepositoryOperationViewController' => 'DrydockRepositoryOperationController',
     'DrydockResource' => array(
       'DrydockDAO',
       'PhabricatorPolicyInterface',
     ),
     'DrydockResourceActivationFailureLogType' => 'DrydockLogType',
     'DrydockResourceActivationYieldLogType' => 'DrydockLogType',
     'DrydockResourceController' => 'DrydockController',
     'DrydockResourceDatasource' => 'PhabricatorTypeaheadDatasource',
     'DrydockResourceListController' => 'DrydockResourceController',
     'DrydockResourceListView' => 'AphrontView',
     'DrydockResourcePHIDType' => 'PhabricatorPHIDType',
     'DrydockResourceQuery' => 'DrydockQuery',
     'DrydockResourceReclaimLogType' => 'DrydockLogType',
     'DrydockResourceReleaseController' => 'DrydockResourceController',
     'DrydockResourceSearchEngine' => 'PhabricatorApplicationSearchEngine',
     'DrydockResourceStatus' => 'DrydockConstants',
     'DrydockResourceUpdateWorker' => 'DrydockWorker',
     'DrydockResourceViewController' => 'DrydockResourceController',
     'DrydockSFTPFilesystemInterface' => 'DrydockFilesystemInterface',
     'DrydockSSHCommandInterface' => 'DrydockCommandInterface',
     'DrydockSchemaSpec' => 'PhabricatorConfigSchemaSpec',
     'DrydockSlotLock' => 'DrydockDAO',
     'DrydockSlotLockException' => 'Exception',
     'DrydockSlotLockFailureLogType' => 'DrydockLogType',
     'DrydockTestRepositoryOperation' => 'DrydockRepositoryOperationType',
     'DrydockWebrootInterface' => 'DrydockInterface',
     'DrydockWorker' => 'PhabricatorWorker',
     'DrydockWorkingCopyBlueprintImplementation' => 'DrydockBlueprintImplementation',
     'FeedConduitAPIMethod' => 'ConduitAPIMethod',
     'FeedPublishConduitAPIMethod' => 'FeedConduitAPIMethod',
     'FeedPublisherHTTPWorker' => 'FeedPushWorker',
     'FeedPublisherWorker' => 'FeedPushWorker',
     'FeedPushWorker' => 'PhabricatorWorker',
     'FeedQueryConduitAPIMethod' => 'FeedConduitAPIMethod',
     'FeedStoryNotificationGarbageCollector' => 'PhabricatorGarbageCollector',
     'FileAllocateConduitAPIMethod' => 'FileConduitAPIMethod',
     'FileConduitAPIMethod' => 'ConduitAPIMethod',
     'FileCreateMailReceiver' => 'PhabricatorMailReceiver',
     'FileDownloadConduitAPIMethod' => 'FileConduitAPIMethod',
     'FileInfoConduitAPIMethod' => 'FileConduitAPIMethod',
     'FileMailReceiver' => 'PhabricatorObjectMailReceiver',
     'FileQueryChunksConduitAPIMethod' => 'FileConduitAPIMethod',
     'FileReplyHandler' => 'PhabricatorApplicationTransactionReplyHandler',
     'FileUploadChunkConduitAPIMethod' => 'FileConduitAPIMethod',
     'FileUploadConduitAPIMethod' => 'FileConduitAPIMethod',
     'FileUploadHashConduitAPIMethod' => 'FileConduitAPIMethod',
     'FilesDefaultViewCapability' => 'PhabricatorPolicyCapability',
     'FlagConduitAPIMethod' => 'ConduitAPIMethod',
     'FlagDeleteConduitAPIMethod' => 'FlagConduitAPIMethod',
     'FlagEditConduitAPIMethod' => 'FlagConduitAPIMethod',
     'FlagQueryConduitAPIMethod' => 'FlagConduitAPIMethod',
     'FundBacker' => array(
       'FundDAO',
       'PhabricatorPolicyInterface',
       'PhabricatorApplicationTransactionInterface',
     ),
     'FundBackerCart' => 'PhortuneCartImplementation',
     'FundBackerEditor' => 'PhabricatorApplicationTransactionEditor',
     'FundBackerListController' => 'FundController',
     'FundBackerPHIDType' => 'PhabricatorPHIDType',
     'FundBackerProduct' => 'PhortuneProductImplementation',
     'FundBackerQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
     'FundBackerSearchEngine' => 'PhabricatorApplicationSearchEngine',
     'FundBackerTransaction' => 'PhabricatorApplicationTransaction',
     'FundBackerTransactionQuery' => 'PhabricatorApplicationTransactionQuery',
     'FundController' => 'PhabricatorController',
     'FundCreateInitiativesCapability' => 'PhabricatorPolicyCapability',
     'FundDAO' => 'PhabricatorLiskDAO',
     'FundDefaultViewCapability' => 'PhabricatorPolicyCapability',
     'FundInitiative' => array(
       'FundDAO',
       'PhabricatorPolicyInterface',
       'PhabricatorProjectInterface',
       'PhabricatorApplicationTransactionInterface',
       'PhabricatorSubscribableInterface',
       'PhabricatorMentionableInterface',
       'PhabricatorFlaggableInterface',
       'PhabricatorTokenReceiverInterface',
       'PhabricatorDestructibleInterface',
       'PhabricatorFulltextInterface',
     ),
     'FundInitiativeBackController' => 'FundController',
     'FundInitiativeCloseController' => 'FundController',
     'FundInitiativeCommentController' => 'FundController',
     'FundInitiativeEditController' => 'FundController',
     'FundInitiativeEditor' => 'PhabricatorApplicationTransactionEditor',
     'FundInitiativeFulltextEngine' => 'PhabricatorFulltextEngine',
     'FundInitiativeListController' => 'FundController',
     'FundInitiativePHIDType' => 'PhabricatorPHIDType',
     'FundInitiativeQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
     'FundInitiativeRemarkupRule' => 'PhabricatorObjectRemarkupRule',
     'FundInitiativeReplyHandler' => 'PhabricatorApplicationTransactionReplyHandler',
     'FundInitiativeSearchEngine' => 'PhabricatorApplicationSearchEngine',
     'FundInitiativeTransaction' => 'PhabricatorApplicationTransaction',
     'FundInitiativeTransactionComment' => 'PhabricatorApplicationTransactionComment',
     'FundInitiativeTransactionQuery' => 'PhabricatorApplicationTransactionQuery',
     'FundInitiativeViewController' => 'FundController',
     'FundSchemaSpec' => 'PhabricatorConfigSchemaSpec',
     'HarbormasterArcLintBuildStepImplementation' => 'HarbormasterBuildStepImplementation',
     'HarbormasterArcUnitBuildStepImplementation' => 'HarbormasterBuildStepImplementation',
     'HarbormasterArtifact' => 'Phobject',
     'HarbormasterAutotargetsTestCase' => 'PhabricatorTestCase',
     'HarbormasterBuild' => array(
       'HarbormasterDAO',
       'PhabricatorApplicationTransactionInterface',
       'PhabricatorPolicyInterface',
     ),
     'HarbormasterBuildAbortedException' => 'Exception',
     'HarbormasterBuildActionController' => 'HarbormasterController',
     'HarbormasterBuildArcanistAutoplan' => 'HarbormasterBuildAutoplan',
     'HarbormasterBuildArtifact' => array(
       'HarbormasterDAO',
       'PhabricatorPolicyInterface',
     ),
     'HarbormasterBuildArtifactPHIDType' => 'PhabricatorPHIDType',
     'HarbormasterBuildArtifactQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
     'HarbormasterBuildAutoplan' => 'Phobject',
     'HarbormasterBuildCommand' => 'HarbormasterDAO',
     'HarbormasterBuildDependencyDatasource' => 'PhabricatorTypeaheadDatasource',
     'HarbormasterBuildEngine' => 'Phobject',
     'HarbormasterBuildFailureException' => 'Exception',
     'HarbormasterBuildGraph' => 'AbstractDirectedGraph',
     'HarbormasterBuildLintMessage' => 'HarbormasterDAO',
     'HarbormasterBuildLog' => array(
       'HarbormasterDAO',
       'PhabricatorPolicyInterface',
     ),
     'HarbormasterBuildLogChunk' => 'HarbormasterDAO',
     'HarbormasterBuildLogChunkIterator' => 'PhutilBufferedIterator',
     'HarbormasterBuildLogPHIDType' => 'PhabricatorPHIDType',
     'HarbormasterBuildLogQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
     'HarbormasterBuildMessage' => array(
       'HarbormasterDAO',
       'PhabricatorPolicyInterface',
     ),
     'HarbormasterBuildMessageQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
     'HarbormasterBuildPHIDType' => 'PhabricatorPHIDType',
     'HarbormasterBuildPlan' => array(
       'HarbormasterDAO',
       'PhabricatorApplicationTransactionInterface',
       'PhabricatorPolicyInterface',
       'PhabricatorSubscribableInterface',
       'PhabricatorNgramsInterface',
       'PhabricatorProjectInterface',
     ),
     'HarbormasterBuildPlanDatasource' => 'PhabricatorTypeaheadDatasource',
     'HarbormasterBuildPlanDefaultEditCapability' => 'PhabricatorPolicyCapability',
     'HarbormasterBuildPlanDefaultViewCapability' => 'PhabricatorPolicyCapability',
     'HarbormasterBuildPlanEditEngine' => 'PhabricatorEditEngine',
     'HarbormasterBuildPlanEditor' => 'PhabricatorApplicationTransactionEditor',
     'HarbormasterBuildPlanNameNgrams' => 'PhabricatorSearchNgrams',
     'HarbormasterBuildPlanPHIDType' => 'PhabricatorPHIDType',
     'HarbormasterBuildPlanQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
     'HarbormasterBuildPlanSearchEngine' => 'PhabricatorApplicationSearchEngine',
     'HarbormasterBuildPlanTransaction' => 'PhabricatorApplicationTransaction',
     'HarbormasterBuildPlanTransactionQuery' => 'PhabricatorApplicationTransactionQuery',
     'HarbormasterBuildQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
     'HarbormasterBuildRequest' => 'Phobject',
     'HarbormasterBuildStep' => array(
       'HarbormasterDAO',
       'PhabricatorApplicationTransactionInterface',
       'PhabricatorPolicyInterface',
       'PhabricatorCustomFieldInterface',
     ),
     'HarbormasterBuildStepCoreCustomField' => array(
       'HarbormasterBuildStepCustomField',
       'PhabricatorStandardCustomFieldInterface',
     ),
     'HarbormasterBuildStepCustomField' => 'PhabricatorCustomField',
     'HarbormasterBuildStepEditor' => 'PhabricatorApplicationTransactionEditor',
     'HarbormasterBuildStepGroup' => 'Phobject',
     'HarbormasterBuildStepImplementation' => 'Phobject',
     'HarbormasterBuildStepImplementationTestCase' => 'PhabricatorTestCase',
     'HarbormasterBuildStepPHIDType' => 'PhabricatorPHIDType',
     'HarbormasterBuildStepQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
     'HarbormasterBuildStepTransaction' => 'PhabricatorApplicationTransaction',
     'HarbormasterBuildStepTransactionQuery' => 'PhabricatorApplicationTransactionQuery',
     'HarbormasterBuildTarget' => array(
       'HarbormasterDAO',
       'PhabricatorPolicyInterface',
     ),
     'HarbormasterBuildTargetPHIDType' => 'PhabricatorPHIDType',
     'HarbormasterBuildTargetQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
     'HarbormasterBuildTransaction' => 'PhabricatorApplicationTransaction',
     'HarbormasterBuildTransactionEditor' => 'PhabricatorApplicationTransactionEditor',
     'HarbormasterBuildTransactionQuery' => 'PhabricatorApplicationTransactionQuery',
     'HarbormasterBuildUnitMessage' => 'HarbormasterDAO',
     'HarbormasterBuildViewController' => 'HarbormasterController',
     'HarbormasterBuildWorker' => 'HarbormasterWorker',
     'HarbormasterBuildable' => array(
       'HarbormasterDAO',
       'PhabricatorApplicationTransactionInterface',
       'PhabricatorPolicyInterface',
       'HarbormasterBuildableInterface',
     ),
     'HarbormasterBuildableActionController' => 'HarbormasterController',
     'HarbormasterBuildableListController' => 'HarbormasterController',
     'HarbormasterBuildablePHIDType' => 'PhabricatorPHIDType',
     'HarbormasterBuildableQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
     'HarbormasterBuildableSearchEngine' => 'PhabricatorApplicationSearchEngine',
     'HarbormasterBuildableTransaction' => 'PhabricatorApplicationTransaction',
     'HarbormasterBuildableTransactionEditor' => 'PhabricatorApplicationTransactionEditor',
     'HarbormasterBuildableTransactionQuery' => 'PhabricatorApplicationTransactionQuery',
     'HarbormasterBuildableViewController' => 'HarbormasterController',
     'HarbormasterBuiltinBuildStepGroup' => 'HarbormasterBuildStepGroup',
     'HarbormasterCircleCIBuildStepImplementation' => 'HarbormasterBuildStepImplementation',
     'HarbormasterCircleCIHookController' => 'HarbormasterController',
     'HarbormasterConduitAPIMethod' => 'ConduitAPIMethod',
     'HarbormasterController' => 'PhabricatorController',
     'HarbormasterCreateArtifactConduitAPIMethod' => 'HarbormasterConduitAPIMethod',
     'HarbormasterCreatePlansCapability' => 'PhabricatorPolicyCapability',
     'HarbormasterDAO' => 'PhabricatorLiskDAO',
     'HarbormasterDrydockBuildStepGroup' => 'HarbormasterBuildStepGroup',
     'HarbormasterDrydockCommandBuildStepImplementation' => 'HarbormasterBuildStepImplementation',
     'HarbormasterDrydockLeaseArtifact' => 'HarbormasterArtifact',
     'HarbormasterExecFuture' => 'Future',
     'HarbormasterExternalBuildStepGroup' => 'HarbormasterBuildStepGroup',
     'HarbormasterFileArtifact' => 'HarbormasterArtifact',
     'HarbormasterHTTPRequestBuildStepImplementation' => 'HarbormasterBuildStepImplementation',
     'HarbormasterHostArtifact' => 'HarbormasterDrydockLeaseArtifact',
     'HarbormasterLeaseWorkingCopyBuildStepImplementation' => 'HarbormasterBuildStepImplementation',
     'HarbormasterLintMessagesController' => 'HarbormasterController',
     'HarbormasterLintPropertyView' => 'AphrontView',
     'HarbormasterManagementArchiveLogsWorkflow' => 'HarbormasterManagementWorkflow',
     'HarbormasterManagementBuildWorkflow' => 'HarbormasterManagementWorkflow',
     'HarbormasterManagementUpdateWorkflow' => 'HarbormasterManagementWorkflow',
     'HarbormasterManagementWorkflow' => 'PhabricatorManagementWorkflow',
     'HarbormasterMessageType' => 'Phobject',
     'HarbormasterObject' => 'HarbormasterDAO',
     'HarbormasterOtherBuildStepGroup' => 'HarbormasterBuildStepGroup',
     'HarbormasterPlanController' => 'HarbormasterController',
     'HarbormasterPlanDisableController' => 'HarbormasterPlanController',
     'HarbormasterPlanEditController' => 'HarbormasterPlanController',
     'HarbormasterPlanListController' => 'HarbormasterPlanController',
     'HarbormasterPlanRunController' => 'HarbormasterPlanController',
     'HarbormasterPlanViewController' => 'HarbormasterPlanController',
     'HarbormasterPrototypeBuildStepGroup' => 'HarbormasterBuildStepGroup',
     'HarbormasterPublishFragmentBuildStepImplementation' => 'HarbormasterBuildStepImplementation',
     'HarbormasterQueryAutotargetsConduitAPIMethod' => 'HarbormasterConduitAPIMethod',
     'HarbormasterQueryBuildablesConduitAPIMethod' => 'HarbormasterConduitAPIMethod',
     'HarbormasterQueryBuildsConduitAPIMethod' => 'HarbormasterConduitAPIMethod',
     'HarbormasterRemarkupRule' => 'PhabricatorObjectRemarkupRule',
     'HarbormasterRunBuildPlansHeraldAction' => 'HeraldAction',
     'HarbormasterSchemaSpec' => 'PhabricatorConfigSchemaSpec',
     'HarbormasterScratchTable' => 'HarbormasterDAO',
     'HarbormasterSendMessageConduitAPIMethod' => 'HarbormasterConduitAPIMethod',
     'HarbormasterSleepBuildStepImplementation' => 'HarbormasterBuildStepImplementation',
     'HarbormasterStepAddController' => 'HarbormasterPlanController',
     'HarbormasterStepDeleteController' => 'HarbormasterPlanController',
     'HarbormasterStepEditController' => 'HarbormasterPlanController',
     'HarbormasterStepViewController' => 'HarbormasterPlanController',
     'HarbormasterTargetEngine' => 'Phobject',
     'HarbormasterTargetWorker' => 'HarbormasterWorker',
     'HarbormasterTestBuildStepGroup' => 'HarbormasterBuildStepGroup',
     'HarbormasterThrowExceptionBuildStep' => 'HarbormasterBuildStepImplementation',
     'HarbormasterUIEventListener' => 'PhabricatorEventListener',
     'HarbormasterURIArtifact' => 'HarbormasterArtifact',
     'HarbormasterUnitMessageListController' => 'HarbormasterController',
     'HarbormasterUnitMessageViewController' => 'HarbormasterController',
     'HarbormasterUnitPropertyView' => 'AphrontView',
     'HarbormasterUnitStatus' => 'Phobject',
     'HarbormasterUnitSummaryView' => 'AphrontView',
     'HarbormasterUploadArtifactBuildStepImplementation' => 'HarbormasterBuildStepImplementation',
     'HarbormasterWaitForPreviousBuildStepImplementation' => 'HarbormasterBuildStepImplementation',
     'HarbormasterWorker' => 'PhabricatorWorker',
     'HarbormasterWorkingCopyArtifact' => 'HarbormasterDrydockLeaseArtifact',
     'HeraldAction' => 'Phobject',
     'HeraldActionGroup' => 'HeraldGroup',
     'HeraldActionRecord' => 'HeraldDAO',
     'HeraldAdapter' => 'Phobject',
     'HeraldAlwaysField' => 'HeraldField',
     'HeraldAnotherRuleField' => 'HeraldField',
     'HeraldApplicationActionGroup' => 'HeraldActionGroup',
     'HeraldApplyTranscript' => 'Phobject',
     'HeraldBasicFieldGroup' => 'HeraldFieldGroup',
     'HeraldCommitAdapter' => array(
       'HeraldAdapter',
       'HarbormasterBuildableAdapterInterface',
     ),
     'HeraldCondition' => 'HeraldDAO',
     'HeraldConditionTranscript' => 'Phobject',
     'HeraldContentSourceField' => 'HeraldField',
     'HeraldController' => 'PhabricatorController',
     'HeraldDAO' => 'PhabricatorLiskDAO',
     'HeraldDifferentialAdapter' => 'HeraldAdapter',
     'HeraldDifferentialDiffAdapter' => 'HeraldDifferentialAdapter',
     'HeraldDifferentialRevisionAdapter' => array(
       'HeraldDifferentialAdapter',
       'HarbormasterBuildableAdapterInterface',
     ),
     'HeraldDisableController' => 'HeraldController',
     'HeraldDoNothingAction' => 'HeraldAction',
     'HeraldEditFieldGroup' => 'HeraldFieldGroup',
     'HeraldEffect' => 'Phobject',
     'HeraldEmptyFieldValue' => 'HeraldFieldValue',
     'HeraldEngine' => 'Phobject',
     'HeraldExactProjectsField' => 'HeraldField',
     'HeraldField' => 'Phobject',
     'HeraldFieldGroup' => 'HeraldGroup',
     'HeraldFieldTestCase' => 'PhutilTestCase',
     'HeraldFieldValue' => 'Phobject',
     'HeraldGroup' => 'Phobject',
     'HeraldInvalidActionException' => 'Exception',
     'HeraldInvalidConditionException' => 'Exception',
     'HeraldManageGlobalRulesCapability' => 'PhabricatorPolicyCapability',
     'HeraldManiphestTaskAdapter' => 'HeraldAdapter',
     'HeraldNewController' => 'HeraldController',
     'HeraldNewObjectField' => 'HeraldField',
     'HeraldNotifyActionGroup' => 'HeraldActionGroup',
     'HeraldObjectTranscript' => 'Phobject',
     'HeraldPhameBlogAdapter' => 'HeraldAdapter',
     'HeraldPhamePostAdapter' => 'HeraldAdapter',
     'HeraldPholioMockAdapter' => 'HeraldAdapter',
     'HeraldPonderQuestionAdapter' => 'HeraldAdapter',
     'HeraldPreCommitAdapter' => 'HeraldAdapter',
     'HeraldPreCommitContentAdapter' => 'HeraldPreCommitAdapter',
     'HeraldPreCommitRefAdapter' => 'HeraldPreCommitAdapter',
     'HeraldPreventActionGroup' => 'HeraldActionGroup',
     'HeraldProjectsField' => 'HeraldField',
     'HeraldRecursiveConditionsException' => 'Exception',
     'HeraldRelatedFieldGroup' => 'HeraldFieldGroup',
     'HeraldRemarkupRule' => 'PhabricatorObjectRemarkupRule',
     'HeraldRepetitionPolicyConfig' => 'Phobject',
     'HeraldRule' => array(
       'HeraldDAO',
       'PhabricatorApplicationTransactionInterface',
       'PhabricatorFlaggableInterface',
       'PhabricatorPolicyInterface',
       'PhabricatorDestructibleInterface',
       'PhabricatorSubscribableInterface',
     ),
     'HeraldRuleController' => 'HeraldController',
     'HeraldRuleEditor' => 'PhabricatorApplicationTransactionEditor',
     'HeraldRuleListController' => 'HeraldController',
     'HeraldRulePHIDType' => 'PhabricatorPHIDType',
     'HeraldRuleQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
     'HeraldRuleSearchEngine' => 'PhabricatorApplicationSearchEngine',
     'HeraldRuleSerializer' => 'Phobject',
     'HeraldRuleTestCase' => 'PhabricatorTestCase',
     'HeraldRuleTransaction' => 'PhabricatorApplicationTransaction',
     'HeraldRuleTransactionComment' => 'PhabricatorApplicationTransactionComment',
     'HeraldRuleTranscript' => 'Phobject',
     'HeraldRuleTypeConfig' => 'Phobject',
     'HeraldRuleViewController' => 'HeraldController',
     'HeraldSchemaSpec' => 'PhabricatorConfigSchemaSpec',
     'HeraldSelectFieldValue' => 'HeraldFieldValue',
     'HeraldSpaceField' => 'HeraldField',
     'HeraldSubscribersField' => 'HeraldField',
     'HeraldSupportActionGroup' => 'HeraldActionGroup',
     'HeraldSupportFieldGroup' => 'HeraldFieldGroup',
     'HeraldTestConsoleController' => 'HeraldController',
     'HeraldTextFieldValue' => 'HeraldFieldValue',
     'HeraldTokenizerFieldValue' => 'HeraldFieldValue',
     'HeraldTransactionQuery' => 'PhabricatorApplicationTransactionQuery',
     'HeraldTranscript' => array(
       'HeraldDAO',
       'PhabricatorPolicyInterface',
       'PhabricatorDestructibleInterface',
     ),
     'HeraldTranscriptController' => 'HeraldController',
     'HeraldTranscriptDestructionEngineExtension' => 'PhabricatorDestructionEngineExtension',
     'HeraldTranscriptGarbageCollector' => 'PhabricatorGarbageCollector',
     'HeraldTranscriptListController' => 'HeraldController',
     'HeraldTranscriptPHIDType' => 'PhabricatorPHIDType',
     'HeraldTranscriptQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
     'HeraldTranscriptSearchEngine' => 'PhabricatorApplicationSearchEngine',
     'HeraldTranscriptTestCase' => 'PhabricatorTestCase',
     'HeraldUtilityActionGroup' => 'HeraldActionGroup',
     'Javelin' => 'Phobject',
     'JavelinReactorUIExample' => 'PhabricatorUIExample',
     'JavelinUIExample' => 'PhabricatorUIExample',
     'JavelinViewExampleServerView' => 'AphrontView',
     'JavelinViewUIExample' => 'PhabricatorUIExample',
     'LegalpadController' => 'PhabricatorController',
     'LegalpadCreateDocumentsCapability' => 'PhabricatorPolicyCapability',
     'LegalpadDAO' => 'PhabricatorLiskDAO',
     'LegalpadDefaultEditCapability' => 'PhabricatorPolicyCapability',
     'LegalpadDefaultViewCapability' => 'PhabricatorPolicyCapability',
     'LegalpadDocument' => array(
       'LegalpadDAO',
       'PhabricatorPolicyInterface',
       'PhabricatorSubscribableInterface',
       'PhabricatorApplicationTransactionInterface',
       'PhabricatorDestructibleInterface',
     ),
     'LegalpadDocumentBody' => array(
       'LegalpadDAO',
       'PhabricatorMarkupInterface',
     ),
     'LegalpadDocumentCommentController' => 'LegalpadController',
     'LegalpadDocumentDatasource' => 'PhabricatorTypeaheadDatasource',
     'LegalpadDocumentDoneController' => 'LegalpadController',
     'LegalpadDocumentEditController' => 'LegalpadController',
     'LegalpadDocumentEditor' => 'PhabricatorApplicationTransactionEditor',
     'LegalpadDocumentListController' => 'LegalpadController',
     'LegalpadDocumentManageController' => 'LegalpadController',
     'LegalpadDocumentQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
     'LegalpadDocumentRemarkupRule' => 'PhabricatorObjectRemarkupRule',
     'LegalpadDocumentSearchEngine' => 'PhabricatorApplicationSearchEngine',
     'LegalpadDocumentSignController' => 'LegalpadController',
     'LegalpadDocumentSignature' => array(
       'LegalpadDAO',
       'PhabricatorPolicyInterface',
     ),
     'LegalpadDocumentSignatureAddController' => 'LegalpadController',
     'LegalpadDocumentSignatureListController' => 'LegalpadController',
     'LegalpadDocumentSignatureQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
     'LegalpadDocumentSignatureSearchEngine' => 'PhabricatorApplicationSearchEngine',
     'LegalpadDocumentSignatureVerificationController' => 'LegalpadController',
     'LegalpadDocumentSignatureViewController' => 'LegalpadController',
     'LegalpadMailReceiver' => 'PhabricatorObjectMailReceiver',
     'LegalpadObjectNeedsSignatureEdgeType' => 'PhabricatorEdgeType',
     'LegalpadReplyHandler' => 'PhabricatorApplicationTransactionReplyHandler',
     'LegalpadRequireSignatureHeraldAction' => 'HeraldAction',
     'LegalpadSchemaSpec' => 'PhabricatorConfigSchemaSpec',
     'LegalpadSignatureNeededByObjectEdgeType' => 'PhabricatorEdgeType',
     'LegalpadTransaction' => 'PhabricatorApplicationTransaction',
     'LegalpadTransactionComment' => 'PhabricatorApplicationTransactionComment',
     'LegalpadTransactionQuery' => 'PhabricatorApplicationTransactionQuery',
     'LegalpadTransactionView' => 'PhabricatorApplicationTransactionView',
     'LiskChunkTestCase' => 'PhabricatorTestCase',
     'LiskDAO' => 'Phobject',
     'LiskDAOSet' => 'Phobject',
     'LiskDAOTestCase' => 'PhabricatorTestCase',
     'LiskEphemeralObjectException' => 'Exception',
     'LiskFixtureTestCase' => 'PhabricatorTestCase',
     'LiskIsolationTestCase' => 'PhabricatorTestCase',
     'LiskIsolationTestDAO' => 'LiskDAO',
     'LiskIsolationTestDAOException' => 'Exception',
     'LiskMigrationIterator' => 'PhutilBufferedIterator',
     'LiskRawMigrationIterator' => 'PhutilBufferedIterator',
     'MacroConduitAPIMethod' => 'ConduitAPIMethod',
     'MacroCreateMemeConduitAPIMethod' => 'MacroConduitAPIMethod',
     'MacroQueryConduitAPIMethod' => 'MacroConduitAPIMethod',
     'ManiphestAssignEmailCommand' => 'ManiphestEmailCommand',
     'ManiphestAssigneeDatasource' => 'PhabricatorTypeaheadCompositeDatasource',
     'ManiphestBatchEditController' => 'ManiphestController',
     'ManiphestBulkEditCapability' => 'PhabricatorPolicyCapability',
     'ManiphestClaimEmailCommand' => 'ManiphestEmailCommand',
     'ManiphestCloseEmailCommand' => 'ManiphestEmailCommand',
     'ManiphestConduitAPIMethod' => 'ConduitAPIMethod',
     'ManiphestConfiguredCustomField' => array(
       'ManiphestCustomField',
       'PhabricatorStandardCustomFieldInterface',
     ),
     'ManiphestConstants' => 'Phobject',
     'ManiphestController' => 'PhabricatorController',
     'ManiphestCreateMailReceiver' => 'PhabricatorMailReceiver',
     'ManiphestCreateTaskConduitAPIMethod' => 'ManiphestConduitAPIMethod',
     'ManiphestCustomField' => 'PhabricatorCustomField',
     'ManiphestCustomFieldNumericIndex' => 'PhabricatorCustomFieldNumericIndexStorage',
     'ManiphestCustomFieldStatusParser' => 'PhabricatorCustomFieldMonogramParser',
     'ManiphestCustomFieldStatusParserTestCase' => 'PhabricatorTestCase',
     'ManiphestCustomFieldStorage' => 'PhabricatorCustomFieldStorage',
     'ManiphestCustomFieldStringIndex' => 'PhabricatorCustomFieldStringIndexStorage',
     'ManiphestDAO' => 'PhabricatorLiskDAO',
     'ManiphestDefaultEditCapability' => 'PhabricatorPolicyCapability',
     'ManiphestDefaultViewCapability' => 'PhabricatorPolicyCapability',
     'ManiphestEditAssignCapability' => 'PhabricatorPolicyCapability',
     'ManiphestEditConduitAPIMethod' => 'PhabricatorEditEngineAPIMethod',
     'ManiphestEditEngine' => 'PhabricatorEditEngine',
     'ManiphestEditPoliciesCapability' => 'PhabricatorPolicyCapability',
     'ManiphestEditPriorityCapability' => 'PhabricatorPolicyCapability',
     'ManiphestEditProjectsCapability' => 'PhabricatorPolicyCapability',
     'ManiphestEditStatusCapability' => 'PhabricatorPolicyCapability',
     'ManiphestEmailCommand' => 'MetaMTAEmailTransactionCommand',
     'ManiphestExcelDefaultFormat' => 'ManiphestExcelFormat',
     'ManiphestExcelFormat' => 'Phobject',
     'ManiphestExcelFormatTestCase' => 'PhabricatorTestCase',
     'ManiphestExportController' => 'ManiphestController',
     'ManiphestGetTaskTransactionsConduitAPIMethod' => 'ManiphestConduitAPIMethod',
     'ManiphestHovercardEngineExtension' => 'PhabricatorHovercardEngineExtension',
     'ManiphestInfoConduitAPIMethod' => 'ManiphestConduitAPIMethod',
     'ManiphestNameIndex' => 'ManiphestDAO',
     'ManiphestPointsConfigOptionType' => 'PhabricatorConfigJSONOptionType',
     'ManiphestPriorityConfigOptionType' => 'PhabricatorConfigJSONOptionType',
     'ManiphestPriorityEmailCommand' => 'ManiphestEmailCommand',
     'ManiphestProjectNameFulltextEngineExtension' => 'PhabricatorFulltextEngineExtension',
     'ManiphestQueryConduitAPIMethod' => 'ManiphestConduitAPIMethod',
     'ManiphestQueryStatusesConduitAPIMethod' => 'ManiphestConduitAPIMethod',
     'ManiphestRemarkupRule' => 'PhabricatorObjectRemarkupRule',
     'ManiphestReplyHandler' => 'PhabricatorApplicationTransactionReplyHandler',
     'ManiphestReportController' => 'ManiphestController',
     'ManiphestSchemaSpec' => 'PhabricatorConfigSchemaSpec',
     'ManiphestSearchConduitAPIMethod' => 'PhabricatorSearchEngineAPIMethod',
     'ManiphestStatusConfigOptionType' => 'PhabricatorConfigJSONOptionType',
     'ManiphestStatusEmailCommand' => 'ManiphestEmailCommand',
     'ManiphestSubpriorityController' => 'ManiphestController',
     'ManiphestTask' => array(
       'ManiphestDAO',
       'PhabricatorSubscribableInterface',
       'PhabricatorMarkupInterface',
       'PhabricatorPolicyInterface',
       'PhabricatorTokenReceiverInterface',
       'PhabricatorFlaggableInterface',
       'PhabricatorMentionableInterface',
       'PhrequentTrackableInterface',
       'PhabricatorCustomFieldInterface',
       'PhabricatorDestructibleInterface',
       'PhabricatorApplicationTransactionInterface',
       'PhabricatorProjectInterface',
       'PhabricatorSpacesInterface',
       'PhabricatorConduitResultInterface',
       'PhabricatorFulltextInterface',
       'DoorkeeperBridgedObjectInterface',
     ),
     'ManiphestTaskAssignHeraldAction' => 'HeraldAction',
     'ManiphestTaskAssignOtherHeraldAction' => 'ManiphestTaskAssignHeraldAction',
     'ManiphestTaskAssignSelfHeraldAction' => 'ManiphestTaskAssignHeraldAction',
     'ManiphestTaskAssigneeHeraldField' => 'ManiphestTaskHeraldField',
     'ManiphestTaskAuthorHeraldField' => 'ManiphestTaskHeraldField',
     'ManiphestTaskAuthorPolicyRule' => 'PhabricatorPolicyRule',
     'ManiphestTaskClosedStatusDatasource' => 'PhabricatorTypeaheadDatasource',
     'ManiphestTaskDependedOnByTaskEdgeType' => 'PhabricatorEdgeType',
     'ManiphestTaskDependsOnTaskEdgeType' => 'PhabricatorEdgeType',
     'ManiphestTaskDescriptionHeraldField' => 'ManiphestTaskHeraldField',
     'ManiphestTaskDetailController' => 'ManiphestController',
     'ManiphestTaskEditBulkJobType' => 'PhabricatorWorkerBulkJobType',
     'ManiphestTaskEditController' => 'ManiphestController',
     'ManiphestTaskFulltextEngine' => 'PhabricatorFulltextEngine',
     'ManiphestTaskHasCommitEdgeType' => 'PhabricatorEdgeType',
     'ManiphestTaskHasMockEdgeType' => 'PhabricatorEdgeType',
     'ManiphestTaskHasRevisionEdgeType' => 'PhabricatorEdgeType',
     'ManiphestTaskHeraldField' => 'HeraldField',
     'ManiphestTaskHeraldFieldGroup' => 'HeraldFieldGroup',
     'ManiphestTaskListController' => 'ManiphestController',
     'ManiphestTaskListHTTPParameterType' => 'AphrontListHTTPParameterType',
     'ManiphestTaskListView' => 'ManiphestView',
     'ManiphestTaskMailReceiver' => 'PhabricatorObjectMailReceiver',
     'ManiphestTaskOpenStatusDatasource' => 'PhabricatorTypeaheadDatasource',
     'ManiphestTaskPHIDResolver' => 'PhabricatorPHIDResolver',
     'ManiphestTaskPHIDType' => 'PhabricatorPHIDType',
     'ManiphestTaskPoints' => 'Phobject',
     'ManiphestTaskPriority' => 'ManiphestConstants',
     'ManiphestTaskPriorityDatasource' => 'PhabricatorTypeaheadDatasource',
     'ManiphestTaskPriorityHeraldAction' => 'HeraldAction',
     'ManiphestTaskPriorityHeraldField' => 'ManiphestTaskHeraldField',
     'ManiphestTaskQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
     'ManiphestTaskResultListView' => 'ManiphestView',
     'ManiphestTaskSearchEngine' => 'PhabricatorApplicationSearchEngine',
     'ManiphestTaskStatus' => 'ManiphestConstants',
     'ManiphestTaskStatusDatasource' => 'PhabricatorTypeaheadDatasource',
     'ManiphestTaskStatusFunctionDatasource' => 'PhabricatorTypeaheadCompositeDatasource',
     'ManiphestTaskStatusHeraldAction' => 'HeraldAction',
     'ManiphestTaskStatusHeraldField' => 'ManiphestTaskHeraldField',
     'ManiphestTaskStatusTestCase' => 'PhabricatorTestCase',
     'ManiphestTaskTestCase' => 'PhabricatorTestCase',
     'ManiphestTaskTitleHeraldField' => 'ManiphestTaskHeraldField',
     'ManiphestTransaction' => 'PhabricatorApplicationTransaction',
     'ManiphestTransactionComment' => 'PhabricatorApplicationTransactionComment',
     'ManiphestTransactionEditor' => 'PhabricatorApplicationTransactionEditor',
     'ManiphestTransactionQuery' => 'PhabricatorApplicationTransactionQuery',
     'ManiphestUpdateConduitAPIMethod' => 'ManiphestConduitAPIMethod',
     'ManiphestView' => 'AphrontView',
     'MetaMTAEmailTransactionCommand' => 'Phobject',
     'MetaMTAEmailTransactionCommandTestCase' => 'PhabricatorTestCase',
     'MetaMTAMailReceivedGarbageCollector' => 'PhabricatorGarbageCollector',
     'MetaMTAMailSentGarbageCollector' => 'PhabricatorGarbageCollector',
     'MetaMTAReceivedMailStatus' => 'Phobject',
     'MultimeterContext' => 'MultimeterDimension',
     'MultimeterControl' => 'Phobject',
     'MultimeterController' => 'PhabricatorController',
     'MultimeterDAO' => 'PhabricatorLiskDAO',
     'MultimeterDimension' => 'MultimeterDAO',
     'MultimeterEvent' => 'MultimeterDAO',
     'MultimeterEventGarbageCollector' => 'PhabricatorGarbageCollector',
     'MultimeterHost' => 'MultimeterDimension',
     'MultimeterLabel' => 'MultimeterDimension',
     'MultimeterSampleController' => 'MultimeterController',
     'MultimeterViewer' => 'MultimeterDimension',
     'NuanceConduitAPIMethod' => 'ConduitAPIMethod',
     'NuanceConsoleController' => 'NuanceController',
     'NuanceContentSource' => 'PhabricatorContentSource',
     'NuanceController' => 'PhabricatorController',
     'NuanceDAO' => 'PhabricatorLiskDAO',
     'NuanceGitHubEventItemType' => 'NuanceItemType',
     'NuanceGitHubImportCursor' => 'NuanceImportCursor',
     'NuanceGitHubIssuesImportCursor' => 'NuanceGitHubImportCursor',
     'NuanceGitHubRawEvent' => 'Phobject',
     'NuanceGitHubRawEventTestCase' => 'PhabricatorTestCase',
     'NuanceGitHubRepositoryImportCursor' => 'NuanceGitHubImportCursor',
     'NuanceGitHubRepositorySourceDefinition' => 'NuanceSourceDefinition',
     'NuanceImportCursor' => 'Phobject',
     'NuanceImportCursorData' => array(
       'NuanceDAO',
       'PhabricatorPolicyInterface',
     ),
     'NuanceImportCursorDataQuery' => 'NuanceQuery',
     'NuanceImportCursorPHIDType' => 'PhabricatorPHIDType',
     'NuanceItem' => array(
       'NuanceDAO',
       'PhabricatorPolicyInterface',
       'PhabricatorApplicationTransactionInterface',
     ),
     'NuanceItemActionController' => 'NuanceController',
     'NuanceItemCommand' => array(
       'NuanceDAO',
       'PhabricatorPolicyInterface',
     ),
     'NuanceItemCommandQuery' => 'NuanceQuery',
     'NuanceItemController' => 'NuanceController',
     'NuanceItemEditor' => 'PhabricatorApplicationTransactionEditor',
     'NuanceItemListController' => 'NuanceItemController',
     'NuanceItemManageController' => 'NuanceController',
     'NuanceItemPHIDType' => 'PhabricatorPHIDType',
     'NuanceItemQuery' => 'NuanceQuery',
     'NuanceItemSearchEngine' => 'PhabricatorApplicationSearchEngine',
     'NuanceItemTransaction' => 'NuanceTransaction',
     'NuanceItemTransactionComment' => 'PhabricatorApplicationTransactionComment',
     'NuanceItemTransactionQuery' => 'PhabricatorApplicationTransactionQuery',
     'NuanceItemType' => 'Phobject',
     'NuanceItemUpdateWorker' => 'NuanceWorker',
     'NuanceItemViewController' => 'NuanceController',
     'NuanceManagementImportWorkflow' => 'NuanceManagementWorkflow',
     'NuanceManagementUpdateWorkflow' => 'NuanceManagementWorkflow',
     'NuanceManagementWorkflow' => 'PhabricatorManagementWorkflow',
     'NuancePhabricatorFormSourceDefinition' => 'NuanceSourceDefinition',
     'NuanceQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
     'NuanceQueue' => array(
       'NuanceDAO',
       'PhabricatorPolicyInterface',
       'PhabricatorApplicationTransactionInterface',
     ),
     'NuanceQueueController' => 'NuanceController',
     'NuanceQueueDatasource' => 'PhabricatorTypeaheadDatasource',
     'NuanceQueueEditController' => 'NuanceQueueController',
     'NuanceQueueEditEngine' => 'PhabricatorEditEngine',
     'NuanceQueueEditor' => 'PhabricatorApplicationTransactionEditor',
     'NuanceQueueListController' => 'NuanceQueueController',
     'NuanceQueuePHIDType' => 'PhabricatorPHIDType',
     'NuanceQueueQuery' => 'NuanceQuery',
     'NuanceQueueSearchEngine' => 'PhabricatorApplicationSearchEngine',
     'NuanceQueueTransaction' => 'NuanceTransaction',
     'NuanceQueueTransactionComment' => 'PhabricatorApplicationTransactionComment',
     'NuanceQueueTransactionQuery' => 'PhabricatorApplicationTransactionQuery',
     'NuanceQueueViewController' => 'NuanceQueueController',
     'NuanceSchemaSpec' => 'PhabricatorConfigSchemaSpec',
     'NuanceSource' => array(
       'NuanceDAO',
       'PhabricatorApplicationTransactionInterface',
       'PhabricatorPolicyInterface',
       'PhabricatorNgramsInterface',
     ),
     'NuanceSourceActionController' => 'NuanceController',
     'NuanceSourceController' => 'NuanceController',
     'NuanceSourceDefaultEditCapability' => 'PhabricatorPolicyCapability',
     'NuanceSourceDefaultViewCapability' => 'PhabricatorPolicyCapability',
     'NuanceSourceDefinition' => 'Phobject',
     'NuanceSourceDefinitionTestCase' => 'PhabricatorTestCase',
     'NuanceSourceEditController' => 'NuanceSourceController',
     'NuanceSourceEditEngine' => 'PhabricatorEditEngine',
     'NuanceSourceEditor' => 'PhabricatorApplicationTransactionEditor',
     'NuanceSourceListController' => 'NuanceSourceController',
     'NuanceSourceManageCapability' => 'PhabricatorPolicyCapability',
     'NuanceSourceNameNgrams' => 'PhabricatorSearchNgrams',
     'NuanceSourcePHIDType' => 'PhabricatorPHIDType',
     'NuanceSourceQuery' => 'NuanceQuery',
     'NuanceSourceSearchEngine' => 'PhabricatorApplicationSearchEngine',
     'NuanceSourceTransaction' => 'NuanceTransaction',
     'NuanceSourceTransactionComment' => 'PhabricatorApplicationTransactionComment',
     'NuanceSourceTransactionQuery' => 'PhabricatorApplicationTransactionQuery',
     'NuanceSourceViewController' => 'NuanceSourceController',
     'NuanceTransaction' => 'PhabricatorApplicationTransaction',
     'NuanceWorker' => 'PhabricatorWorker',
     'OwnersConduitAPIMethod' => 'ConduitAPIMethod',
     'OwnersEditConduitAPIMethod' => 'PhabricatorEditEngineAPIMethod',
     'OwnersPackageReplyHandler' => 'PhabricatorMailReplyHandler',
     'OwnersQueryConduitAPIMethod' => 'OwnersConduitAPIMethod',
     'OwnersSearchConduitAPIMethod' => 'PhabricatorSearchEngineAPIMethod',
     'PHIDConduitAPIMethod' => 'ConduitAPIMethod',
     'PHIDInfoConduitAPIMethod' => 'PHIDConduitAPIMethod',
     'PHIDLookupConduitAPIMethod' => 'PHIDConduitAPIMethod',
     'PHIDQueryConduitAPIMethod' => 'PHIDConduitAPIMethod',
     'PHUI' => 'Phobject',
     'PHUIActionPanelExample' => 'PhabricatorUIExample',
     'PHUIActionPanelView' => 'AphrontTagView',
     'PHUIApplicationMenuView' => 'Phobject',
     'PHUIBadgeBoxView' => 'AphrontTagView',
     'PHUIBadgeExample' => 'PhabricatorUIExample',
     'PHUIBadgeMiniView' => 'AphrontTagView',
     'PHUIBadgeView' => 'AphrontTagView',
     'PHUIBigInfoView' => 'AphrontTagView',
     'PHUIBoxExample' => 'PhabricatorUIExample',
     'PHUIBoxView' => 'AphrontTagView',
     'PHUIButtonBarExample' => 'PhabricatorUIExample',
     'PHUIButtonBarView' => 'AphrontTagView',
     'PHUIButtonExample' => 'PhabricatorUIExample',
     'PHUIButtonView' => 'AphrontTagView',
     'PHUICalendarDayView' => 'AphrontView',
     'PHUICalendarListView' => 'AphrontTagView',
     'PHUICalendarMonthView' => 'AphrontView',
     'PHUICalendarWidgetView' => 'AphrontTagView',
     'PHUIColorPalletteExample' => 'PhabricatorUIExample',
     'PHUICrumbView' => 'AphrontView',
     'PHUICrumbsView' => 'AphrontView',
     'PHUICurtainExtension' => 'Phobject',
     'PHUICurtainPanelView' => 'AphrontTagView',
     'PHUICurtainView' => 'AphrontTagView',
     'PHUIDiffInlineCommentDetailView' => 'PHUIDiffInlineCommentView',
     'PHUIDiffInlineCommentEditView' => 'PHUIDiffInlineCommentView',
     'PHUIDiffInlineCommentRowScaffold' => 'AphrontView',
     'PHUIDiffInlineCommentTableScaffold' => 'AphrontView',
     'PHUIDiffInlineCommentUndoView' => 'PHUIDiffInlineCommentView',
     'PHUIDiffInlineCommentView' => 'AphrontView',
     'PHUIDiffOneUpInlineCommentRowScaffold' => 'PHUIDiffInlineCommentRowScaffold',
     'PHUIDiffRevealIconView' => 'AphrontView',
     'PHUIDiffTableOfContentsItemView' => 'AphrontView',
     'PHUIDiffTableOfContentsListView' => 'AphrontView',
     'PHUIDiffTwoUpInlineCommentRowScaffold' => 'PHUIDiffInlineCommentRowScaffold',
     'PHUIDocumentExample' => 'PhabricatorUIExample',
     'PHUIDocumentSummaryView' => 'AphrontTagView',
     'PHUIDocumentView' => 'AphrontTagView',
     'PHUIDocumentViewPro' => 'AphrontTagView',
     'PHUIFeedStoryExample' => 'PhabricatorUIExample',
     'PHUIFeedStoryView' => 'AphrontView',
     'PHUIFormDividerControl' => 'AphrontFormControl',
     'PHUIFormFreeformDateControl' => 'AphrontFormControl',
     'PHUIFormIconSetControl' => 'AphrontFormControl',
     'PHUIFormInsetView' => 'AphrontView',
     'PHUIFormLayoutView' => 'AphrontView',
     'PHUIFormMultiSubmitControl' => 'AphrontFormControl',
     'PHUIFormPageView' => 'AphrontView',
     'PHUIHandleListView' => 'AphrontTagView',
     'PHUIHandleTagListView' => 'AphrontTagView',
     'PHUIHandleView' => 'AphrontView',
     'PHUIHeadThingView' => 'AphrontTagView',
     'PHUIHeaderView' => 'AphrontTagView',
     'PHUIHovercardUIExample' => 'PhabricatorUIExample',
     'PHUIHovercardView' => 'AphrontTagView',
     'PHUIIconCircleView' => 'AphrontTagView',
     'PHUIIconExample' => 'PhabricatorUIExample',
     'PHUIIconView' => 'AphrontTagView',
     'PHUIImageMaskExample' => 'PhabricatorUIExample',
     'PHUIImageMaskView' => 'AphrontTagView',
     'PHUIInfoExample' => 'PhabricatorUIExample',
     'PHUIInfoPanelExample' => 'PhabricatorUIExample',
     'PHUIInfoPanelView' => 'AphrontView',
     'PHUIInfoView' => 'AphrontView',
     'PHUIListExample' => 'PhabricatorUIExample',
     'PHUIListItemView' => 'AphrontTagView',
     'PHUIListView' => 'AphrontTagView',
     'PHUIListViewTestCase' => 'PhabricatorTestCase',
     'PHUIMainMenuView' => 'AphrontView',
     'PHUIObjectBoxView' => 'AphrontTagView',
     'PHUIObjectItemListExample' => 'PhabricatorUIExample',
     'PHUIObjectItemListView' => 'AphrontTagView',
     'PHUIObjectItemView' => 'AphrontTagView',
     'PHUIPagedFormView' => 'AphrontView',
     'PHUIPagerView' => 'AphrontView',
     'PHUIPinboardItemView' => 'AphrontView',
     'PHUIPinboardView' => 'AphrontView',
     'PHUIPropertyGroupView' => 'AphrontTagView',
     'PHUIPropertyListExample' => 'PhabricatorUIExample',
     'PHUIPropertyListView' => 'AphrontView',
     'PHUIRemarkupPreviewPanel' => 'AphrontTagView',
     'PHUIRemarkupView' => 'AphrontView',
     'PHUISegmentBarSegmentView' => 'AphrontTagView',
     'PHUISegmentBarView' => 'AphrontTagView',
     'PHUISpacesNamespaceContextView' => 'AphrontView',
     'PHUIStatusItemView' => 'AphrontTagView',
     'PHUIStatusListView' => 'AphrontTagView',
     'PHUITagExample' => 'PhabricatorUIExample',
     'PHUITagView' => 'AphrontTagView',
     'PHUITimelineEventView' => 'AphrontView',
     'PHUITimelineExample' => 'PhabricatorUIExample',
     'PHUITimelineView' => 'AphrontView',
     'PHUITwoColumnView' => 'AphrontTagView',
     'PHUITypeaheadExample' => 'PhabricatorUIExample',
     'PHUIWorkboardView' => 'AphrontTagView',
     'PHUIWorkpanelView' => 'AphrontTagView',
     'PassphraseAbstractKey' => 'Phobject',
     'PassphraseConduitAPIMethod' => 'ConduitAPIMethod',
     'PassphraseController' => 'PhabricatorController',
     'PassphraseCredential' => array(
       'PassphraseDAO',
       'PhabricatorApplicationTransactionInterface',
       'PhabricatorPolicyInterface',
       'PhabricatorFlaggableInterface',
       'PhabricatorSubscribableInterface',
       'PhabricatorDestructibleInterface',
       'PhabricatorSpacesInterface',
       'PhabricatorFulltextInterface',
     ),
     'PassphraseCredentialAuthorPolicyRule' => 'PhabricatorPolicyRule',
     'PassphraseCredentialConduitController' => 'PassphraseController',
     'PassphraseCredentialControl' => 'AphrontFormControl',
     'PassphraseCredentialCreateController' => 'PassphraseController',
     'PassphraseCredentialDestroyController' => 'PassphraseController',
     'PassphraseCredentialEditController' => 'PassphraseController',
     'PassphraseCredentialFulltextEngine' => 'PhabricatorFulltextEngine',
     'PassphraseCredentialListController' => 'PassphraseController',
     'PassphraseCredentialLockController' => 'PassphraseController',
     'PassphraseCredentialPHIDType' => 'PhabricatorPHIDType',
     'PassphraseCredentialPublicController' => 'PassphraseController',
     'PassphraseCredentialQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
     'PassphraseCredentialRevealController' => 'PassphraseController',
     'PassphraseCredentialSearchEngine' => 'PhabricatorApplicationSearchEngine',
     'PassphraseCredentialTransaction' => 'PhabricatorApplicationTransaction',
     'PassphraseCredentialTransactionEditor' => 'PhabricatorApplicationTransactionEditor',
     'PassphraseCredentialTransactionQuery' => 'PhabricatorApplicationTransactionQuery',
     'PassphraseCredentialType' => 'Phobject',
     'PassphraseCredentialTypeTestCase' => 'PhabricatorTestCase',
     'PassphraseCredentialViewController' => 'PassphraseController',
     'PassphraseDAO' => 'PhabricatorLiskDAO',
     'PassphraseDefaultEditCapability' => 'PhabricatorPolicyCapability',
     'PassphraseDefaultViewCapability' => 'PhabricatorPolicyCapability',
     'PassphraseNoteCredentialType' => 'PassphraseCredentialType',
     'PassphrasePasswordCredentialType' => 'PassphraseCredentialType',
     'PassphrasePasswordKey' => 'PassphraseAbstractKey',
     'PassphraseQueryConduitAPIMethod' => 'PassphraseConduitAPIMethod',
     'PassphraseRemarkupRule' => 'PhabricatorObjectRemarkupRule',
     'PassphraseSSHGeneratedKeyCredentialType' => 'PassphraseSSHPrivateKeyCredentialType',
     'PassphraseSSHKey' => 'PassphraseAbstractKey',
     'PassphraseSSHPrivateKeyCredentialType' => 'PassphraseCredentialType',
     'PassphraseSSHPrivateKeyFileCredentialType' => 'PassphraseSSHPrivateKeyCredentialType',
     'PassphraseSSHPrivateKeyTextCredentialType' => 'PassphraseSSHPrivateKeyCredentialType',
     'PassphraseSchemaSpec' => 'PhabricatorConfigSchemaSpec',
     'PassphraseSecret' => 'PassphraseDAO',
     'PassphraseTokenCredentialType' => 'PassphraseCredentialType',
     'PasteConduitAPIMethod' => 'ConduitAPIMethod',
     'PasteCreateConduitAPIMethod' => 'PasteConduitAPIMethod',
     'PasteCreateMailReceiver' => 'PhabricatorMailReceiver',
     'PasteDefaultEditCapability' => 'PhabricatorPolicyCapability',
     'PasteDefaultViewCapability' => 'PhabricatorPolicyCapability',
     'PasteEditConduitAPIMethod' => 'PhabricatorEditEngineAPIMethod',
     'PasteEmbedView' => 'AphrontView',
     'PasteInfoConduitAPIMethod' => 'PasteConduitAPIMethod',
     'PasteMailReceiver' => 'PhabricatorObjectMailReceiver',
     'PasteQueryConduitAPIMethod' => 'PasteConduitAPIMethod',
     'PasteReplyHandler' => 'PhabricatorApplicationTransactionReplyHandler',
     'PasteSearchConduitAPIMethod' => 'PhabricatorSearchEngineAPIMethod',
     'PeopleBrowseUserDirectoryCapability' => 'PhabricatorPolicyCapability',
     'PeopleCreateUsersCapability' => 'PhabricatorPolicyCapability',
     'PeopleHovercardEngineExtension' => 'PhabricatorHovercardEngineExtension',
     'PeopleUserLogGarbageCollector' => 'PhabricatorGarbageCollector',
     'Phabricator404Controller' => 'PhabricatorController',
     'PhabricatorAWSConfigOptions' => 'PhabricatorApplicationConfigOptions',
     'PhabricatorAccessControlTestCase' => 'PhabricatorTestCase',
     'PhabricatorAccessLog' => 'Phobject',
     'PhabricatorAccessLogConfigOptions' => 'PhabricatorApplicationConfigOptions',
     'PhabricatorAccountSettingsPanel' => 'PhabricatorSettingsPanel',
     'PhabricatorActionListView' => 'AphrontView',
     'PhabricatorActionView' => 'AphrontView',
     'PhabricatorActivitySettingsPanel' => 'PhabricatorSettingsPanel',
     'PhabricatorAdministratorsPolicyRule' => 'PhabricatorPolicyRule',
     'PhabricatorAjaxRequestExceptionHandler' => 'PhabricatorRequestExceptionHandler',
     'PhabricatorAlmanacApplication' => 'PhabricatorApplication',
     'PhabricatorAmazonAuthProvider' => 'PhabricatorOAuth2AuthProvider',
     'PhabricatorAnchorView' => 'AphrontView',
     'PhabricatorAphlictManagementDebugWorkflow' => 'PhabricatorAphlictManagementWorkflow',
     'PhabricatorAphlictManagementRestartWorkflow' => 'PhabricatorAphlictManagementWorkflow',
     'PhabricatorAphlictManagementStartWorkflow' => 'PhabricatorAphlictManagementWorkflow',
     'PhabricatorAphlictManagementStatusWorkflow' => 'PhabricatorAphlictManagementWorkflow',
     'PhabricatorAphlictManagementStopWorkflow' => 'PhabricatorAphlictManagementWorkflow',
     'PhabricatorAphlictManagementWorkflow' => 'PhabricatorManagementWorkflow',
     'PhabricatorAphlictSetupCheck' => 'PhabricatorSetupCheck',
     'PhabricatorAphrontBarUIExample' => 'PhabricatorUIExample',
     'PhabricatorAphrontViewTestCase' => 'PhabricatorTestCase',
     'PhabricatorAppSearchEngine' => 'PhabricatorApplicationSearchEngine',
     'PhabricatorApplication' => array(
       'Phobject',
       'PhabricatorPolicyInterface',
     ),
     'PhabricatorApplicationApplicationPHIDType' => 'PhabricatorPHIDType',
     'PhabricatorApplicationConfigOptions' => 'Phobject',
     'PhabricatorApplicationConfigurationPanel' => 'Phobject',
     'PhabricatorApplicationConfigurationPanelTestCase' => 'PhabricatorTestCase',
     'PhabricatorApplicationDatasource' => 'PhabricatorTypeaheadDatasource',
     'PhabricatorApplicationDetailViewController' => 'PhabricatorApplicationsController',
     'PhabricatorApplicationEditController' => 'PhabricatorApplicationsController',
     'PhabricatorApplicationEditHTTPParameterHelpView' => 'AphrontView',
     'PhabricatorApplicationEmailCommandsController' => 'PhabricatorApplicationsController',
     'PhabricatorApplicationLaunchView' => 'AphrontTagView',
     'PhabricatorApplicationPanelController' => 'PhabricatorApplicationsController',
     'PhabricatorApplicationQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
     'PhabricatorApplicationSearchController' => 'PhabricatorSearchBaseController',
     'PhabricatorApplicationSearchEngine' => 'Phobject',
     'PhabricatorApplicationSearchEngineTestCase' => 'PhabricatorTestCase',
     'PhabricatorApplicationSearchResultView' => 'Phobject',
     'PhabricatorApplicationStatusView' => 'AphrontView',
     'PhabricatorApplicationTestCase' => 'PhabricatorTestCase',
     'PhabricatorApplicationTransaction' => array(
       'PhabricatorLiskDAO',
       'PhabricatorPolicyInterface',
       'PhabricatorDestructibleInterface',
     ),
     'PhabricatorApplicationTransactionComment' => array(
       'PhabricatorLiskDAO',
       'PhabricatorMarkupInterface',
       'PhabricatorPolicyInterface',
       'PhabricatorDestructibleInterface',
     ),
     'PhabricatorApplicationTransactionCommentEditController' => 'PhabricatorApplicationTransactionController',
     'PhabricatorApplicationTransactionCommentEditor' => 'PhabricatorEditor',
     'PhabricatorApplicationTransactionCommentHistoryController' => 'PhabricatorApplicationTransactionController',
     'PhabricatorApplicationTransactionCommentQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
     'PhabricatorApplicationTransactionCommentQuoteController' => 'PhabricatorApplicationTransactionController',
     'PhabricatorApplicationTransactionCommentRawController' => 'PhabricatorApplicationTransactionController',
     'PhabricatorApplicationTransactionCommentRemoveController' => 'PhabricatorApplicationTransactionController',
     'PhabricatorApplicationTransactionCommentView' => 'AphrontView',
     'PhabricatorApplicationTransactionController' => 'PhabricatorController',
     'PhabricatorApplicationTransactionDetailController' => 'PhabricatorApplicationTransactionController',
     'PhabricatorApplicationTransactionEditor' => 'PhabricatorEditor',
     'PhabricatorApplicationTransactionFeedStory' => 'PhabricatorFeedStory',
     'PhabricatorApplicationTransactionNoEffectException' => 'Exception',
     'PhabricatorApplicationTransactionNoEffectResponse' => 'AphrontProxyResponse',
     'PhabricatorApplicationTransactionPublishWorker' => 'PhabricatorWorker',
     'PhabricatorApplicationTransactionQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
     'PhabricatorApplicationTransactionRemarkupPreviewController' => 'PhabricatorApplicationTransactionController',
     'PhabricatorApplicationTransactionReplyHandler' => 'PhabricatorMailReplyHandler',
     'PhabricatorApplicationTransactionResponse' => 'AphrontProxyResponse',
     'PhabricatorApplicationTransactionShowOlderController' => 'PhabricatorApplicationTransactionController',
     'PhabricatorApplicationTransactionStructureException' => 'Exception',
     'PhabricatorApplicationTransactionTemplatedCommentQuery' => 'PhabricatorApplicationTransactionCommentQuery',
     'PhabricatorApplicationTransactionTextDiffDetailView' => 'AphrontView',
     'PhabricatorApplicationTransactionTransactionPHIDType' => 'PhabricatorPHIDType',
     'PhabricatorApplicationTransactionValidationError' => 'Phobject',
     'PhabricatorApplicationTransactionValidationException' => 'Exception',
     'PhabricatorApplicationTransactionValidationResponse' => 'AphrontProxyResponse',
     'PhabricatorApplicationTransactionValueController' => 'PhabricatorApplicationTransactionController',
     'PhabricatorApplicationTransactionView' => 'AphrontView',
     'PhabricatorApplicationUninstallController' => 'PhabricatorApplicationsController',
     'PhabricatorApplicationsApplication' => 'PhabricatorApplication',
     'PhabricatorApplicationsController' => 'PhabricatorController',
     'PhabricatorApplicationsListController' => 'PhabricatorApplicationsController',
     'PhabricatorAsanaAuthProvider' => 'PhabricatorOAuth2AuthProvider',
     'PhabricatorAsanaConfigOptions' => 'PhabricatorApplicationConfigOptions',
     'PhabricatorAsanaSubtaskHasObjectEdgeType' => 'PhabricatorEdgeType',
     'PhabricatorAsanaTaskHasObjectEdgeType' => 'PhabricatorEdgeType',
     'PhabricatorAuditActionConstants' => 'Phobject',
     'PhabricatorAuditAddCommentController' => 'PhabricatorAuditController',
     'PhabricatorAuditApplication' => 'PhabricatorApplication',
     'PhabricatorAuditCommentEditor' => 'PhabricatorEditor',
     'PhabricatorAuditCommitStatusConstants' => 'Phobject',
     'PhabricatorAuditController' => 'PhabricatorController',
     'PhabricatorAuditEditor' => 'PhabricatorApplicationTransactionEditor',
     'PhabricatorAuditInlineComment' => array(
       'Phobject',
       'PhabricatorInlineCommentInterface',
     ),
     'PhabricatorAuditListController' => 'PhabricatorAuditController',
     'PhabricatorAuditListView' => 'AphrontView',
     'PhabricatorAuditMailReceiver' => 'PhabricatorObjectMailReceiver',
     'PhabricatorAuditManagementDeleteWorkflow' => 'PhabricatorAuditManagementWorkflow',
     'PhabricatorAuditManagementWorkflow' => 'PhabricatorManagementWorkflow',
     'PhabricatorAuditPreviewController' => 'PhabricatorAuditController',
     'PhabricatorAuditReplyHandler' => 'PhabricatorApplicationTransactionReplyHandler',
     'PhabricatorAuditStatusConstants' => 'Phobject',
     'PhabricatorAuditTransaction' => 'PhabricatorApplicationTransaction',
     'PhabricatorAuditTransactionComment' => 'PhabricatorApplicationTransactionComment',
     'PhabricatorAuditTransactionQuery' => 'PhabricatorApplicationTransactionQuery',
     'PhabricatorAuditTransactionView' => 'PhabricatorApplicationTransactionView',
     'PhabricatorAuthAccountView' => 'AphrontView',
     'PhabricatorAuthApplication' => 'PhabricatorApplication',
     'PhabricatorAuthAuthFactorPHIDType' => 'PhabricatorPHIDType',
     'PhabricatorAuthAuthProviderPHIDType' => 'PhabricatorPHIDType',
     'PhabricatorAuthConduitAPIMethod' => 'ConduitAPIMethod',
     'PhabricatorAuthConfirmLinkController' => 'PhabricatorAuthController',
     'PhabricatorAuthController' => 'PhabricatorController',
     'PhabricatorAuthDAO' => 'PhabricatorLiskDAO',
     'PhabricatorAuthDisableController' => 'PhabricatorAuthProviderConfigController',
     'PhabricatorAuthDowngradeSessionController' => 'PhabricatorAuthController',
     'PhabricatorAuthEditController' => 'PhabricatorAuthProviderConfigController',
     'PhabricatorAuthFactor' => 'Phobject',
     'PhabricatorAuthFactorConfig' => 'PhabricatorAuthDAO',
     'PhabricatorAuthFactorTestCase' => 'PhabricatorTestCase',
     'PhabricatorAuthFinishController' => 'PhabricatorAuthController',
     'PhabricatorAuthHighSecurityRequiredException' => 'Exception',
     'PhabricatorAuthHighSecurityToken' => 'Phobject',
     'PhabricatorAuthInvite' => array(
       'PhabricatorUserDAO',
       'PhabricatorPolicyInterface',
     ),
     'PhabricatorAuthInviteAccountException' => 'PhabricatorAuthInviteDialogException',
     'PhabricatorAuthInviteAction' => 'Phobject',
     'PhabricatorAuthInviteActionTableView' => 'AphrontView',
     'PhabricatorAuthInviteController' => 'PhabricatorAuthController',
     'PhabricatorAuthInviteDialogException' => 'PhabricatorAuthInviteException',
     'PhabricatorAuthInviteEngine' => 'Phobject',
     'PhabricatorAuthInviteException' => 'Exception',
     'PhabricatorAuthInviteInvalidException' => 'PhabricatorAuthInviteDialogException',
     'PhabricatorAuthInviteLoginException' => 'PhabricatorAuthInviteDialogException',
     'PhabricatorAuthInvitePHIDType' => 'PhabricatorPHIDType',
     'PhabricatorAuthInviteQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
     'PhabricatorAuthInviteRegisteredException' => 'PhabricatorAuthInviteException',
     'PhabricatorAuthInviteSearchEngine' => 'PhabricatorApplicationSearchEngine',
     'PhabricatorAuthInviteTestCase' => 'PhabricatorTestCase',
     'PhabricatorAuthInviteVerifyException' => 'PhabricatorAuthInviteDialogException',
     'PhabricatorAuthInviteWorker' => 'PhabricatorWorker',
     'PhabricatorAuthLinkController' => 'PhabricatorAuthController',
     'PhabricatorAuthListController' => 'PhabricatorAuthProviderConfigController',
     'PhabricatorAuthLoginController' => 'PhabricatorAuthController',
     'PhabricatorAuthLoginHandler' => 'Phobject',
     'PhabricatorAuthLogoutConduitAPIMethod' => 'PhabricatorAuthConduitAPIMethod',
     'PhabricatorAuthMainMenuBarExtension' => 'PhabricatorMainMenuBarExtension',
     'PhabricatorAuthManagementCachePKCS8Workflow' => 'PhabricatorAuthManagementWorkflow',
     'PhabricatorAuthManagementLDAPWorkflow' => 'PhabricatorAuthManagementWorkflow',
     'PhabricatorAuthManagementListFactorsWorkflow' => 'PhabricatorAuthManagementWorkflow',
     'PhabricatorAuthManagementRecoverWorkflow' => 'PhabricatorAuthManagementWorkflow',
     'PhabricatorAuthManagementRefreshWorkflow' => 'PhabricatorAuthManagementWorkflow',
     'PhabricatorAuthManagementStripWorkflow' => 'PhabricatorAuthManagementWorkflow',
     'PhabricatorAuthManagementTrustOAuthClientWorkflow' => 'PhabricatorAuthManagementWorkflow',
     'PhabricatorAuthManagementUnlimitWorkflow' => 'PhabricatorAuthManagementWorkflow',
     'PhabricatorAuthManagementUntrustOAuthClientWorkflow' => 'PhabricatorAuthManagementWorkflow',
     'PhabricatorAuthManagementVerifyWorkflow' => 'PhabricatorAuthManagementWorkflow',
     'PhabricatorAuthManagementWorkflow' => 'PhabricatorManagementWorkflow',
     'PhabricatorAuthNeedsApprovalController' => 'PhabricatorAuthController',
     'PhabricatorAuthNeedsMultiFactorController' => 'PhabricatorAuthController',
     'PhabricatorAuthNewController' => 'PhabricatorAuthProviderConfigController',
     'PhabricatorAuthOldOAuthRedirectController' => 'PhabricatorAuthController',
     'PhabricatorAuthOneTimeLoginController' => 'PhabricatorAuthController',
     'PhabricatorAuthOneTimeLoginTemporaryTokenType' => 'PhabricatorAuthTemporaryTokenType',
     'PhabricatorAuthPasswordResetTemporaryTokenType' => 'PhabricatorAuthTemporaryTokenType',
     'PhabricatorAuthProvider' => 'Phobject',
     'PhabricatorAuthProviderConfig' => array(
       'PhabricatorAuthDAO',
       'PhabricatorApplicationTransactionInterface',
       'PhabricatorPolicyInterface',
     ),
     'PhabricatorAuthProviderConfigController' => 'PhabricatorAuthController',
     'PhabricatorAuthProviderConfigEditor' => 'PhabricatorApplicationTransactionEditor',
     'PhabricatorAuthProviderConfigQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
     'PhabricatorAuthProviderConfigTransaction' => 'PhabricatorApplicationTransaction',
     'PhabricatorAuthProviderConfigTransactionQuery' => 'PhabricatorApplicationTransactionQuery',
     'PhabricatorAuthQueryPublicKeysConduitAPIMethod' => 'PhabricatorAuthConduitAPIMethod',
     'PhabricatorAuthRegisterController' => 'PhabricatorAuthController',
     'PhabricatorAuthRevokeTokenController' => 'PhabricatorAuthController',
     'PhabricatorAuthSSHKey' => array(
       'PhabricatorAuthDAO',
       'PhabricatorPolicyInterface',
       'PhabricatorDestructibleInterface',
     ),
     'PhabricatorAuthSSHKeyController' => 'PhabricatorAuthController',
     'PhabricatorAuthSSHKeyDeleteController' => 'PhabricatorAuthSSHKeyController',
     'PhabricatorAuthSSHKeyEditController' => 'PhabricatorAuthSSHKeyController',
     'PhabricatorAuthSSHKeyGenerateController' => 'PhabricatorAuthSSHKeyController',
     'PhabricatorAuthSSHKeyPHIDType' => 'PhabricatorPHIDType',
     'PhabricatorAuthSSHKeyQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
     'PhabricatorAuthSSHKeyTableView' => 'AphrontView',
     'PhabricatorAuthSSHPublicKey' => 'Phobject',
     'PhabricatorAuthSession' => array(
       'PhabricatorAuthDAO',
       'PhabricatorPolicyInterface',
     ),
     'PhabricatorAuthSessionEngine' => 'Phobject',
     'PhabricatorAuthSessionEngineExtension' => 'Phobject',
     'PhabricatorAuthSessionEngineExtensionModule' => 'PhabricatorConfigModule',
     'PhabricatorAuthSessionGarbageCollector' => 'PhabricatorGarbageCollector',
     'PhabricatorAuthSessionQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
     'PhabricatorAuthSetupCheck' => 'PhabricatorSetupCheck',
     'PhabricatorAuthStartController' => 'PhabricatorAuthController',
     'PhabricatorAuthTOTPKeyTemporaryTokenType' => 'PhabricatorAuthTemporaryTokenType',
     'PhabricatorAuthTemporaryToken' => array(
       'PhabricatorAuthDAO',
       'PhabricatorPolicyInterface',
     ),
     'PhabricatorAuthTemporaryTokenGarbageCollector' => 'PhabricatorGarbageCollector',
     'PhabricatorAuthTemporaryTokenQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
     'PhabricatorAuthTemporaryTokenType' => 'Phobject',
     'PhabricatorAuthTemporaryTokenTypeModule' => 'PhabricatorConfigModule',
     'PhabricatorAuthTerminateSessionController' => 'PhabricatorAuthController',
     'PhabricatorAuthTryFactorAction' => 'PhabricatorSystemAction',
     'PhabricatorAuthUnlinkController' => 'PhabricatorAuthController',
     'PhabricatorAuthValidateController' => 'PhabricatorAuthController',
     'PhabricatorAuthenticationConfigOptions' => 'PhabricatorApplicationConfigOptions',
     'PhabricatorAutoEventListener' => 'PhabricatorEventListener',
     'PhabricatorBadgeHasRecipientEdgeType' => 'PhabricatorEdgeType',
     'PhabricatorBadgesApplication' => 'PhabricatorApplication',
     'PhabricatorBadgesArchiveController' => 'PhabricatorBadgesController',
     'PhabricatorBadgesAward' => array(
       'PhabricatorBadgesDAO',
       'PhabricatorDestructibleInterface',
       'PhabricatorPolicyInterface',
     ),
     'PhabricatorBadgesAwardController' => 'PhabricatorBadgesController',
     'PhabricatorBadgesAwardQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
     'PhabricatorBadgesBadge' => array(
       'PhabricatorBadgesDAO',
       'PhabricatorPolicyInterface',
       'PhabricatorApplicationTransactionInterface',
       'PhabricatorSubscribableInterface',
       'PhabricatorTokenReceiverInterface',
       'PhabricatorFlaggableInterface',
       'PhabricatorDestructibleInterface',
       'PhabricatorConduitResultInterface',
       'PhabricatorNgramsInterface',
     ),
     'PhabricatorBadgesBadgeNameNgrams' => 'PhabricatorSearchNgrams',
     'PhabricatorBadgesCommentController' => 'PhabricatorBadgesController',
     'PhabricatorBadgesController' => 'PhabricatorController',
     'PhabricatorBadgesCreateCapability' => 'PhabricatorPolicyCapability',
     'PhabricatorBadgesDAO' => 'PhabricatorLiskDAO',
     'PhabricatorBadgesDatasource' => 'PhabricatorTypeaheadDatasource',
     'PhabricatorBadgesDefaultEditCapability' => 'PhabricatorPolicyCapability',
     'PhabricatorBadgesEditConduitAPIMethod' => 'PhabricatorEditEngineAPIMethod',
     'PhabricatorBadgesEditController' => 'PhabricatorBadgesController',
     'PhabricatorBadgesEditEngine' => 'PhabricatorEditEngine',
     'PhabricatorBadgesEditRecipientsController' => 'PhabricatorBadgesController',
     'PhabricatorBadgesEditor' => 'PhabricatorApplicationTransactionEditor',
     'PhabricatorBadgesIconSet' => 'PhabricatorIconSet',
     'PhabricatorBadgesListController' => 'PhabricatorBadgesController',
     'PhabricatorBadgesMailReceiver' => 'PhabricatorObjectMailReceiver',
     'PhabricatorBadgesPHIDType' => 'PhabricatorPHIDType',
     'PhabricatorBadgesQuality' => 'Phobject',
     'PhabricatorBadgesQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
     'PhabricatorBadgesRecipientsListView' => 'AphrontView',
     'PhabricatorBadgesRemoveRecipientsController' => 'PhabricatorBadgesController',
     'PhabricatorBadgesReplyHandler' => 'PhabricatorApplicationTransactionReplyHandler',
     'PhabricatorBadgesSchemaSpec' => 'PhabricatorConfigSchemaSpec',
     'PhabricatorBadgesSearchConduitAPIMethod' => 'PhabricatorSearchEngineAPIMethod',
     'PhabricatorBadgesSearchEngine' => 'PhabricatorApplicationSearchEngine',
     'PhabricatorBadgesTransaction' => 'PhabricatorApplicationTransaction',
     'PhabricatorBadgesTransactionComment' => 'PhabricatorApplicationTransactionComment',
     'PhabricatorBadgesTransactionQuery' => 'PhabricatorApplicationTransactionQuery',
     'PhabricatorBadgesViewController' => 'PhabricatorBadgesController',
     'PhabricatorBarePageUIExample' => 'PhabricatorUIExample',
     'PhabricatorBarePageView' => 'AphrontPageView',
     'PhabricatorBaseURISetupCheck' => 'PhabricatorSetupCheck',
     'PhabricatorBcryptPasswordHasher' => 'PhabricatorPasswordHasher',
     'PhabricatorBinariesSetupCheck' => 'PhabricatorSetupCheck',
     'PhabricatorBitbucketAuthProvider' => 'PhabricatorOAuth1AuthProvider',
     'PhabricatorBoardLayoutEngine' => 'Phobject',
     'PhabricatorBoardRenderingEngine' => 'Phobject',
     'PhabricatorBoardResponseEngine' => 'Phobject',
     'PhabricatorBoolEditField' => 'PhabricatorEditField',
     'PhabricatorBot' => 'PhabricatorDaemon',
     'PhabricatorBotChannel' => 'PhabricatorBotTarget',
     'PhabricatorBotDebugLogHandler' => 'PhabricatorBotHandler',
     'PhabricatorBotFeedNotificationHandler' => 'PhabricatorBotHandler',
     'PhabricatorBotFlowdockProtocolAdapter' => 'PhabricatorStreamingProtocolAdapter',
     'PhabricatorBotHandler' => 'Phobject',
     'PhabricatorBotLogHandler' => 'PhabricatorBotHandler',
     'PhabricatorBotMacroHandler' => 'PhabricatorBotHandler',
     'PhabricatorBotMessage' => 'Phobject',
     'PhabricatorBotObjectNameHandler' => 'PhabricatorBotHandler',
     'PhabricatorBotSymbolHandler' => 'PhabricatorBotHandler',
     'PhabricatorBotTarget' => 'Phobject',
     'PhabricatorBotUser' => 'PhabricatorBotTarget',
     'PhabricatorBotWhatsNewHandler' => 'PhabricatorBotHandler',
     'PhabricatorBritishEnglishTranslation' => 'PhutilTranslation',
     'PhabricatorBuiltinPatchList' => 'PhabricatorSQLPatchList',
     'PhabricatorBulkContentSource' => 'PhabricatorContentSource',
     'PhabricatorBusyUIExample' => 'PhabricatorUIExample',
     'PhabricatorCacheDAO' => 'PhabricatorLiskDAO',
     'PhabricatorCacheGeneralGarbageCollector' => 'PhabricatorGarbageCollector',
     'PhabricatorCacheManagementPurgeWorkflow' => 'PhabricatorCacheManagementWorkflow',
     'PhabricatorCacheManagementWorkflow' => 'PhabricatorManagementWorkflow',
     'PhabricatorCacheMarkupGarbageCollector' => 'PhabricatorGarbageCollector',
     'PhabricatorCacheSchemaSpec' => 'PhabricatorConfigSchemaSpec',
     'PhabricatorCacheSetupCheck' => 'PhabricatorSetupCheck',
     'PhabricatorCacheSpec' => 'Phobject',
     'PhabricatorCacheTTLGarbageCollector' => 'PhabricatorGarbageCollector',
     'PhabricatorCaches' => 'Phobject',
     'PhabricatorCachesTestCase' => 'PhabricatorTestCase',
     'PhabricatorCalendarApplication' => 'PhabricatorApplication',
     'PhabricatorCalendarController' => 'PhabricatorController',
     'PhabricatorCalendarDAO' => 'PhabricatorLiskDAO',
     'PhabricatorCalendarEvent' => array(
       'PhabricatorCalendarDAO',
       'PhabricatorPolicyInterface',
       'PhabricatorProjectInterface',
       'PhabricatorMarkupInterface',
       'PhabricatorApplicationTransactionInterface',
       'PhabricatorSubscribableInterface',
       'PhabricatorTokenReceiverInterface',
       'PhabricatorDestructibleInterface',
       'PhabricatorMentionableInterface',
       'PhabricatorFlaggableInterface',
       'PhabricatorSpacesInterface',
       'PhabricatorFulltextInterface',
     ),
     'PhabricatorCalendarEventCancelController' => 'PhabricatorCalendarController',
     'PhabricatorCalendarEventCommentController' => 'PhabricatorCalendarController',
     'PhabricatorCalendarEventDragController' => 'PhabricatorCalendarController',
     'PhabricatorCalendarEventEditController' => 'PhabricatorCalendarController',
     'PhabricatorCalendarEventEditor' => 'PhabricatorApplicationTransactionEditor',
     'PhabricatorCalendarEventEmailCommand' => 'MetaMTAEmailTransactionCommand',
     'PhabricatorCalendarEventFulltextEngine' => 'PhabricatorFulltextEngine',
     'PhabricatorCalendarEventInvitee' => array(
       'PhabricatorCalendarDAO',
       'PhabricatorPolicyInterface',
     ),
     'PhabricatorCalendarEventInviteeQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
     'PhabricatorCalendarEventJoinController' => 'PhabricatorCalendarController',
     'PhabricatorCalendarEventListController' => 'PhabricatorCalendarController',
     'PhabricatorCalendarEventMailReceiver' => 'PhabricatorObjectMailReceiver',
     'PhabricatorCalendarEventPHIDType' => 'PhabricatorPHIDType',
     'PhabricatorCalendarEventQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
     'PhabricatorCalendarEventRSVPEmailCommand' => 'PhabricatorCalendarEventEmailCommand',
     'PhabricatorCalendarEventSearchEngine' => 'PhabricatorApplicationSearchEngine',
     'PhabricatorCalendarEventTransaction' => 'PhabricatorApplicationTransaction',
     'PhabricatorCalendarEventTransactionComment' => 'PhabricatorApplicationTransactionComment',
     'PhabricatorCalendarEventTransactionQuery' => 'PhabricatorApplicationTransactionQuery',
     'PhabricatorCalendarEventViewController' => 'PhabricatorCalendarController',
     'PhabricatorCalendarHoliday' => 'PhabricatorCalendarDAO',
     'PhabricatorCalendarHolidayTestCase' => 'PhabricatorTestCase',
     'PhabricatorCalendarIconSet' => 'PhabricatorIconSet',
     'PhabricatorCalendarRemarkupRule' => 'PhabricatorObjectRemarkupRule',
     'PhabricatorCalendarReplyHandler' => 'PhabricatorApplicationTransactionReplyHandler',
     'PhabricatorCalendarSchemaSpec' => 'PhabricatorConfigSchemaSpec',
     'PhabricatorCampfireProtocolAdapter' => 'PhabricatorStreamingProtocolAdapter',
     'PhabricatorCelerityApplication' => 'PhabricatorApplication',
     'PhabricatorCelerityTestCase' => 'PhabricatorTestCase',
     'PhabricatorChangeParserTestCase' => 'PhabricatorWorkingCopyTestCase',
     'PhabricatorChangesetResponse' => 'AphrontProxyResponse',
     'PhabricatorChatLogApplication' => 'PhabricatorApplication',
     'PhabricatorChatLogChannel' => array(
       'PhabricatorChatLogDAO',
       'PhabricatorPolicyInterface',
     ),
     'PhabricatorChatLogChannelListController' => 'PhabricatorChatLogController',
     'PhabricatorChatLogChannelLogController' => 'PhabricatorChatLogController',
     'PhabricatorChatLogChannelQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
     'PhabricatorChatLogController' => 'PhabricatorController',
     'PhabricatorChatLogDAO' => 'PhabricatorLiskDAO',
     'PhabricatorChatLogEvent' => array(
       'PhabricatorChatLogDAO',
       'PhabricatorPolicyInterface',
     ),
     'PhabricatorChatLogQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
     'PhabricatorChunkedFileStorageEngine' => 'PhabricatorFileStorageEngine',
     'PhabricatorClusterConfigOptions' => 'PhabricatorApplicationConfigOptions',
     'PhabricatorClusterDatabasesConfigOptionType' => 'PhabricatorConfigJSONOptionType',
     'PhabricatorClusterException' => 'Exception',
     'PhabricatorClusterExceptionHandler' => 'PhabricatorRequestExceptionHandler',
     'PhabricatorClusterImpossibleWriteException' => 'PhabricatorClusterException',
     'PhabricatorClusterImproperWriteException' => 'PhabricatorClusterException',
     'PhabricatorClusterStrandedException' => 'PhabricatorClusterException',
     'PhabricatorColumnsEditField' => 'PhabricatorPHIDListEditField',
     'PhabricatorCommentEditEngineExtension' => 'PhabricatorEditEngineExtension',
     'PhabricatorCommentEditField' => 'PhabricatorEditField',
     'PhabricatorCommentEditType' => 'PhabricatorEditType',
     'PhabricatorCommitBranchesField' => 'PhabricatorCommitCustomField',
     'PhabricatorCommitCustomField' => 'PhabricatorCustomField',
     'PhabricatorCommitMergedCommitsField' => 'PhabricatorCommitCustomField',
     'PhabricatorCommitRepositoryField' => 'PhabricatorCommitCustomField',
     'PhabricatorCommitSearchEngine' => 'PhabricatorApplicationSearchEngine',
     'PhabricatorCommitTagsField' => 'PhabricatorCommitCustomField',
     'PhabricatorCommonPasswords' => 'Phobject',
     'PhabricatorConduitAPIController' => 'PhabricatorConduitController',
     'PhabricatorConduitApplication' => 'PhabricatorApplication',
     'PhabricatorConduitCertificateToken' => 'PhabricatorConduitDAO',
     'PhabricatorConduitConnectionLog' => 'PhabricatorConduitDAO',
     'PhabricatorConduitConsoleController' => 'PhabricatorConduitController',
     'PhabricatorConduitContentSource' => 'PhabricatorContentSource',
     'PhabricatorConduitController' => 'PhabricatorController',
     'PhabricatorConduitDAO' => 'PhabricatorLiskDAO',
     'PhabricatorConduitEditField' => 'PhabricatorEditField',
     'PhabricatorConduitListController' => 'PhabricatorConduitController',
     'PhabricatorConduitLogController' => 'PhabricatorConduitController',
     'PhabricatorConduitLogQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
     'PhabricatorConduitLogSearchEngine' => 'PhabricatorApplicationSearchEngine',
     'PhabricatorConduitMethodCallLog' => array(
       'PhabricatorConduitDAO',
       'PhabricatorPolicyInterface',
     ),
     'PhabricatorConduitMethodQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
     'PhabricatorConduitRequestExceptionHandler' => 'PhabricatorRequestExceptionHandler',
     'PhabricatorConduitResultInterface' => 'PhabricatorPHIDInterface',
     'PhabricatorConduitSearchEngine' => 'PhabricatorApplicationSearchEngine',
     'PhabricatorConduitSearchFieldSpecification' => 'Phobject',
     'PhabricatorConduitTestCase' => 'PhabricatorTestCase',
     'PhabricatorConduitToken' => array(
       'PhabricatorConduitDAO',
       'PhabricatorPolicyInterface',
     ),
     'PhabricatorConduitTokenController' => 'PhabricatorConduitController',
     'PhabricatorConduitTokenEditController' => 'PhabricatorConduitController',
     'PhabricatorConduitTokenHandshakeController' => 'PhabricatorConduitController',
     'PhabricatorConduitTokenQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
     'PhabricatorConduitTokenTerminateController' => 'PhabricatorConduitController',
     'PhabricatorConduitTokensSettingsPanel' => 'PhabricatorSettingsPanel',
     'PhabricatorConfigAllController' => 'PhabricatorConfigController',
     'PhabricatorConfigApplication' => 'PhabricatorApplication',
     'PhabricatorConfigCacheController' => 'PhabricatorConfigController',
     'PhabricatorConfigClusterDatabasesController' => 'PhabricatorConfigController',
     'PhabricatorConfigClusterNotificationsController' => 'PhabricatorConfigController',
     'PhabricatorConfigClusterRepositoriesController' => 'PhabricatorConfigController',
     'PhabricatorConfigCollectorsModule' => 'PhabricatorConfigModule',
     'PhabricatorConfigColumnSchema' => 'PhabricatorConfigStorageSchema',
     'PhabricatorConfigConfigPHIDType' => 'PhabricatorPHIDType',
     'PhabricatorConfigController' => 'PhabricatorController',
     'PhabricatorConfigCoreSchemaSpec' => 'PhabricatorConfigSchemaSpec',
     'PhabricatorConfigDatabaseController' => 'PhabricatorConfigController',
     'PhabricatorConfigDatabaseIssueController' => 'PhabricatorConfigDatabaseController',
     'PhabricatorConfigDatabaseSchema' => 'PhabricatorConfigStorageSchema',
     'PhabricatorConfigDatabaseSource' => 'PhabricatorConfigProxySource',
     'PhabricatorConfigDatabaseStatusController' => 'PhabricatorConfigDatabaseController',
     'PhabricatorConfigDefaultSource' => 'PhabricatorConfigProxySource',
     'PhabricatorConfigDictionarySource' => 'PhabricatorConfigSource',
     'PhabricatorConfigEdgeModule' => 'PhabricatorConfigModule',
     'PhabricatorConfigEditController' => 'PhabricatorConfigController',
     'PhabricatorConfigEditor' => 'PhabricatorApplicationTransactionEditor',
     'PhabricatorConfigEntry' => array(
       'PhabricatorConfigEntryDAO',
       'PhabricatorApplicationTransactionInterface',
       'PhabricatorPolicyInterface',
     ),
     'PhabricatorConfigEntryDAO' => 'PhabricatorLiskDAO',
     'PhabricatorConfigEntryQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
     'PhabricatorConfigFileSource' => 'PhabricatorConfigProxySource',
     'PhabricatorConfigGroupController' => 'PhabricatorConfigController',
     'PhabricatorConfigHTTPParameterTypesModule' => 'PhabricatorConfigModule',
     'PhabricatorConfigHistoryController' => 'PhabricatorConfigController',
     'PhabricatorConfigIgnoreController' => 'PhabricatorConfigController',
     'PhabricatorConfigIssueListController' => 'PhabricatorConfigController',
     'PhabricatorConfigIssueViewController' => 'PhabricatorConfigController',
     'PhabricatorConfigJSON' => 'Phobject',
     'PhabricatorConfigJSONOptionType' => 'PhabricatorConfigOptionType',
     'PhabricatorConfigKeySchema' => 'PhabricatorConfigStorageSchema',
     'PhabricatorConfigListController' => 'PhabricatorConfigController',
     'PhabricatorConfigLocalSource' => 'PhabricatorConfigProxySource',
     'PhabricatorConfigManagementDeleteWorkflow' => 'PhabricatorConfigManagementWorkflow',
     'PhabricatorConfigManagementGetWorkflow' => 'PhabricatorConfigManagementWorkflow',
     'PhabricatorConfigManagementListWorkflow' => 'PhabricatorConfigManagementWorkflow',
     'PhabricatorConfigManagementMigrateWorkflow' => 'PhabricatorConfigManagementWorkflow',
     'PhabricatorConfigManagementSetWorkflow' => 'PhabricatorConfigManagementWorkflow',
     'PhabricatorConfigManagementWorkflow' => 'PhabricatorManagementWorkflow',
     'PhabricatorConfigModule' => 'Phobject',
     'PhabricatorConfigModuleController' => 'PhabricatorConfigController',
     'PhabricatorConfigOption' => array(
       'Phobject',
       'PhabricatorMarkupInterface',
     ),
     'PhabricatorConfigOptionType' => 'Phobject',
     'PhabricatorConfigPHIDModule' => 'PhabricatorConfigModule',
     'PhabricatorConfigProxySource' => 'PhabricatorConfigSource',
     'PhabricatorConfigPurgeCacheController' => 'PhabricatorConfigController',
     'PhabricatorConfigRequestExceptionHandlerModule' => 'PhabricatorConfigModule',
     'PhabricatorConfigResponse' => 'AphrontStandaloneHTMLResponse',
     'PhabricatorConfigSchemaQuery' => 'Phobject',
     'PhabricatorConfigSchemaSpec' => 'Phobject',
     'PhabricatorConfigServerSchema' => 'PhabricatorConfigStorageSchema',
     'PhabricatorConfigSiteModule' => 'PhabricatorConfigModule',
     'PhabricatorConfigSiteSource' => 'PhabricatorConfigProxySource',
     'PhabricatorConfigSource' => 'Phobject',
     'PhabricatorConfigStackSource' => 'PhabricatorConfigSource',
     'PhabricatorConfigStorageSchema' => 'Phobject',
     'PhabricatorConfigTableSchema' => 'PhabricatorConfigStorageSchema',
     'PhabricatorConfigTransaction' => 'PhabricatorApplicationTransaction',
     'PhabricatorConfigTransactionQuery' => 'PhabricatorApplicationTransactionQuery',
     'PhabricatorConfigValidationException' => 'Exception',
     'PhabricatorConfigVersionsModule' => 'PhabricatorConfigModule',
     'PhabricatorConfigWelcomeController' => 'PhabricatorConfigController',
     'PhabricatorConpherenceApplication' => 'PhabricatorApplication',
     'PhabricatorConpherencePreferencesSettingsPanel' => 'PhabricatorSettingsPanel',
     'PhabricatorConpherenceThreadPHIDType' => 'PhabricatorPHIDType',
     'PhabricatorConsoleApplication' => 'PhabricatorApplication',
     'PhabricatorConsoleContentSource' => 'PhabricatorContentSource',
     'PhabricatorContentSource' => 'Phobject',
     'PhabricatorContentSourceModule' => 'PhabricatorConfigModule',
     'PhabricatorContentSourceView' => 'AphrontView',
     'PhabricatorContributedToObjectEdgeType' => 'PhabricatorEdgeType',
     'PhabricatorController' => 'AphrontController',
     'PhabricatorCookies' => 'Phobject',
     'PhabricatorCoreConfigOptions' => 'PhabricatorApplicationConfigOptions',
     'PhabricatorCountdown' => array(
       'PhabricatorCountdownDAO',
       'PhabricatorPolicyInterface',
       'PhabricatorFlaggableInterface',
       'PhabricatorSubscribableInterface',
       'PhabricatorApplicationTransactionInterface',
       'PhabricatorTokenReceiverInterface',
       'PhabricatorSpacesInterface',
       'PhabricatorProjectInterface',
     ),
     'PhabricatorCountdownApplication' => 'PhabricatorApplication',
     'PhabricatorCountdownController' => 'PhabricatorController',
     'PhabricatorCountdownCountdownPHIDType' => 'PhabricatorPHIDType',
     'PhabricatorCountdownDAO' => 'PhabricatorLiskDAO',
     'PhabricatorCountdownDefaultEditCapability' => 'PhabricatorPolicyCapability',
     'PhabricatorCountdownDefaultViewCapability' => 'PhabricatorPolicyCapability',
     'PhabricatorCountdownDeleteController' => 'PhabricatorCountdownController',
     'PhabricatorCountdownEditController' => 'PhabricatorCountdownController',
     'PhabricatorCountdownEditEngine' => 'PhabricatorEditEngine',
     'PhabricatorCountdownEditor' => 'PhabricatorApplicationTransactionEditor',
     'PhabricatorCountdownListController' => 'PhabricatorCountdownController',
     'PhabricatorCountdownMailReceiver' => 'PhabricatorObjectMailReceiver',
     'PhabricatorCountdownQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
     'PhabricatorCountdownRemarkupRule' => 'PhabricatorObjectRemarkupRule',
     'PhabricatorCountdownReplyHandler' => 'PhabricatorApplicationTransactionReplyHandler',
     'PhabricatorCountdownSchemaSpec' => 'PhabricatorConfigSchemaSpec',
     'PhabricatorCountdownSearchEngine' => 'PhabricatorApplicationSearchEngine',
     'PhabricatorCountdownTransaction' => 'PhabricatorApplicationTransaction',
     'PhabricatorCountdownTransactionComment' => 'PhabricatorApplicationTransactionComment',
     'PhabricatorCountdownTransactionQuery' => 'PhabricatorApplicationTransactionQuery',
     'PhabricatorCountdownView' => 'AphrontView',
     'PhabricatorCountdownViewController' => 'PhabricatorCountdownController',
     'PhabricatorCredentialsUsedByObjectEdgeType' => 'PhabricatorEdgeType',
     'PhabricatorCursorPagedPolicyAwareQuery' => 'PhabricatorPolicyAwareQuery',
     'PhabricatorCustomField' => 'Phobject',
     'PhabricatorCustomFieldAttachment' => 'Phobject',
     'PhabricatorCustomFieldConfigOptionType' => 'PhabricatorConfigOptionType',
     'PhabricatorCustomFieldDataNotAvailableException' => 'Exception',
     'PhabricatorCustomFieldEditEngineExtension' => 'PhabricatorEditEngineExtension',
     'PhabricatorCustomFieldEditField' => 'PhabricatorEditField',
     'PhabricatorCustomFieldEditType' => 'PhabricatorEditType',
     'PhabricatorCustomFieldFulltextEngineExtension' => 'PhabricatorFulltextEngineExtension',
     'PhabricatorCustomFieldHeraldField' => 'HeraldField',
     'PhabricatorCustomFieldHeraldFieldGroup' => 'HeraldFieldGroup',
     'PhabricatorCustomFieldImplementationIncompleteException' => 'Exception',
     'PhabricatorCustomFieldIndexStorage' => 'PhabricatorLiskDAO',
     'PhabricatorCustomFieldList' => 'Phobject',
     'PhabricatorCustomFieldMonogramParser' => 'Phobject',
     'PhabricatorCustomFieldNotAttachedException' => 'Exception',
     'PhabricatorCustomFieldNotProxyException' => 'Exception',
     'PhabricatorCustomFieldNumericIndexStorage' => 'PhabricatorCustomFieldIndexStorage',
     'PhabricatorCustomFieldSearchEngineExtension' => 'PhabricatorSearchEngineExtension',
     'PhabricatorCustomFieldStorage' => 'PhabricatorLiskDAO',
     'PhabricatorCustomFieldStringIndexStorage' => 'PhabricatorCustomFieldIndexStorage',
     'PhabricatorCustomHeaderConfigType' => 'PhabricatorConfigOptionType',
     'PhabricatorDaemon' => 'PhutilDaemon',
     'PhabricatorDaemonBulkJobController' => 'PhabricatorDaemonController',
     'PhabricatorDaemonBulkJobListController' => 'PhabricatorDaemonBulkJobController',
     'PhabricatorDaemonBulkJobMonitorController' => 'PhabricatorDaemonBulkJobController',
     'PhabricatorDaemonBulkJobViewController' => 'PhabricatorDaemonBulkJobController',
     'PhabricatorDaemonConsoleController' => 'PhabricatorDaemonController',
     'PhabricatorDaemonContentSource' => 'PhabricatorContentSource',
     'PhabricatorDaemonController' => 'PhabricatorController',
     'PhabricatorDaemonDAO' => 'PhabricatorLiskDAO',
     'PhabricatorDaemonEventListener' => 'PhabricatorEventListener',
     'PhabricatorDaemonLog' => array(
       'PhabricatorDaemonDAO',
       'PhabricatorPolicyInterface',
     ),
     'PhabricatorDaemonLogEvent' => 'PhabricatorDaemonDAO',
     'PhabricatorDaemonLogEventGarbageCollector' => 'PhabricatorGarbageCollector',
     'PhabricatorDaemonLogEventViewController' => 'PhabricatorDaemonController',
     'PhabricatorDaemonLogEventsView' => 'AphrontView',
     'PhabricatorDaemonLogGarbageCollector' => 'PhabricatorGarbageCollector',
     'PhabricatorDaemonLogListController' => 'PhabricatorDaemonController',
     'PhabricatorDaemonLogListView' => 'AphrontView',
     'PhabricatorDaemonLogQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
     'PhabricatorDaemonLogViewController' => 'PhabricatorDaemonController',
     'PhabricatorDaemonManagementDebugWorkflow' => 'PhabricatorDaemonManagementWorkflow',
     'PhabricatorDaemonManagementLaunchWorkflow' => 'PhabricatorDaemonManagementWorkflow',
     'PhabricatorDaemonManagementListWorkflow' => 'PhabricatorDaemonManagementWorkflow',
     'PhabricatorDaemonManagementLogWorkflow' => 'PhabricatorDaemonManagementWorkflow',
     'PhabricatorDaemonManagementReloadWorkflow' => 'PhabricatorDaemonManagementWorkflow',
     'PhabricatorDaemonManagementRestartWorkflow' => 'PhabricatorDaemonManagementWorkflow',
     'PhabricatorDaemonManagementStartWorkflow' => 'PhabricatorDaemonManagementWorkflow',
     'PhabricatorDaemonManagementStatusWorkflow' => 'PhabricatorDaemonManagementWorkflow',
     'PhabricatorDaemonManagementStopWorkflow' => 'PhabricatorDaemonManagementWorkflow',
     'PhabricatorDaemonManagementWorkflow' => 'PhabricatorManagementWorkflow',
     'PhabricatorDaemonOverseerModule' => 'PhutilDaemonOverseerModule',
     'PhabricatorDaemonReference' => 'Phobject',
     'PhabricatorDaemonTaskGarbageCollector' => 'PhabricatorGarbageCollector',
     'PhabricatorDaemonTasksTableView' => 'AphrontView',
     'PhabricatorDaemonsApplication' => 'PhabricatorApplication',
     'PhabricatorDaemonsSetupCheck' => 'PhabricatorSetupCheck',
     'PhabricatorDailyRoutineTriggerClock' => 'PhabricatorTriggerClock',
     'PhabricatorDashboard' => array(
       'PhabricatorDashboardDAO',
       'PhabricatorApplicationTransactionInterface',
       'PhabricatorPolicyInterface',
       'PhabricatorFlaggableInterface',
       'PhabricatorDestructibleInterface',
       'PhabricatorProjectInterface',
     ),
     'PhabricatorDashboardAddPanelController' => 'PhabricatorDashboardController',
     'PhabricatorDashboardApplication' => 'PhabricatorApplication',
     'PhabricatorDashboardArchiveController' => 'PhabricatorDashboardController',
     'PhabricatorDashboardController' => 'PhabricatorController',
     'PhabricatorDashboardCopyController' => 'PhabricatorDashboardController',
     'PhabricatorDashboardDAO' => 'PhabricatorLiskDAO',
     'PhabricatorDashboardDashboardHasPanelEdgeType' => 'PhabricatorEdgeType',
     'PhabricatorDashboardDashboardPHIDType' => 'PhabricatorPHIDType',
     'PhabricatorDashboardEditController' => 'PhabricatorDashboardController',
     'PhabricatorDashboardInstall' => 'PhabricatorDashboardDAO',
     'PhabricatorDashboardInstallController' => 'PhabricatorDashboardController',
     'PhabricatorDashboardLayoutConfig' => 'Phobject',
     'PhabricatorDashboardListController' => 'PhabricatorDashboardController',
     'PhabricatorDashboardManageController' => 'PhabricatorDashboardController',
     'PhabricatorDashboardMovePanelController' => 'PhabricatorDashboardController',
     'PhabricatorDashboardPanel' => array(
       'PhabricatorDashboardDAO',
       'PhabricatorApplicationTransactionInterface',
       'PhabricatorPolicyInterface',
       'PhabricatorCustomFieldInterface',
       'PhabricatorFlaggableInterface',
       'PhabricatorProjectInterface',
       'PhabricatorDestructibleInterface',
     ),
     'PhabricatorDashboardPanelArchiveController' => 'PhabricatorDashboardController',
     'PhabricatorDashboardPanelCoreCustomField' => array(
       'PhabricatorDashboardPanelCustomField',
       'PhabricatorStandardCustomFieldInterface',
     ),
     'PhabricatorDashboardPanelCustomField' => 'PhabricatorCustomField',
     'PhabricatorDashboardPanelEditController' => 'PhabricatorDashboardController',
     'PhabricatorDashboardPanelHasDashboardEdgeType' => 'PhabricatorEdgeType',
     'PhabricatorDashboardPanelListController' => 'PhabricatorDashboardController',
     'PhabricatorDashboardPanelPHIDType' => 'PhabricatorPHIDType',
     'PhabricatorDashboardPanelQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
     'PhabricatorDashboardPanelRenderController' => 'PhabricatorDashboardController',
     'PhabricatorDashboardPanelRenderingEngine' => 'Phobject',
     'PhabricatorDashboardPanelSearchApplicationCustomField' => 'PhabricatorStandardCustomField',
     'PhabricatorDashboardPanelSearchEngine' => 'PhabricatorApplicationSearchEngine',
     'PhabricatorDashboardPanelSearchQueryCustomField' => 'PhabricatorStandardCustomField',
     'PhabricatorDashboardPanelTabsCustomField' => 'PhabricatorStandardCustomField',
     'PhabricatorDashboardPanelTransaction' => 'PhabricatorApplicationTransaction',
     'PhabricatorDashboardPanelTransactionEditor' => 'PhabricatorApplicationTransactionEditor',
     'PhabricatorDashboardPanelTransactionQuery' => 'PhabricatorApplicationTransactionQuery',
     'PhabricatorDashboardPanelType' => 'Phobject',
     'PhabricatorDashboardPanelViewController' => 'PhabricatorDashboardController',
     'PhabricatorDashboardQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
     'PhabricatorDashboardQueryPanelType' => 'PhabricatorDashboardPanelType',
     'PhabricatorDashboardRemarkupRule' => 'PhabricatorObjectRemarkupRule',
     'PhabricatorDashboardRemovePanelController' => 'PhabricatorDashboardController',
     'PhabricatorDashboardRenderingEngine' => 'Phobject',
     'PhabricatorDashboardSchemaSpec' => 'PhabricatorConfigSchemaSpec',
     'PhabricatorDashboardSearchEngine' => 'PhabricatorApplicationSearchEngine',
     'PhabricatorDashboardTabsPanelType' => 'PhabricatorDashboardPanelType',
     'PhabricatorDashboardTextPanelType' => 'PhabricatorDashboardPanelType',
     'PhabricatorDashboardTransaction' => 'PhabricatorApplicationTransaction',
     'PhabricatorDashboardTransactionEditor' => 'PhabricatorApplicationTransactionEditor',
     'PhabricatorDashboardTransactionQuery' => 'PhabricatorApplicationTransactionQuery',
     'PhabricatorDashboardUninstallController' => 'PhabricatorDashboardController',
     'PhabricatorDashboardViewController' => 'PhabricatorDashboardController',
     'PhabricatorDataCacheSpec' => 'PhabricatorCacheSpec',
     'PhabricatorDataNotAttachedException' => 'Exception',
     'PhabricatorDatabaseHealthRecord' => 'Phobject',
     'PhabricatorDatabaseRef' => 'Phobject',
     'PhabricatorDatabaseSetupCheck' => 'PhabricatorSetupCheck',
     'PhabricatorDatasourceEditField' => 'PhabricatorTokenizerEditField',
     'PhabricatorDatasourceEditType' => 'PhabricatorPHIDListEditType',
     'PhabricatorDateTimeSettingsPanel' => 'PhabricatorSettingsPanel',
     'PhabricatorDebugController' => 'PhabricatorController',
     'PhabricatorDefaultRequestExceptionHandler' => 'PhabricatorRequestExceptionHandler',
+    'PhabricatorDefaultSyntaxStyle' => 'PhabricatorSyntaxStyle',
     'PhabricatorDesktopNotificationsSettingsPanel' => 'PhabricatorSettingsPanel',
     'PhabricatorDestructionEngine' => 'Phobject',
     'PhabricatorDestructionEngineExtension' => 'Phobject',
     'PhabricatorDestructionEngineExtensionModule' => 'PhabricatorConfigModule',
     'PhabricatorDeveloperConfigOptions' => 'PhabricatorApplicationConfigOptions',
     'PhabricatorDeveloperPreferencesSettingsPanel' => 'PhabricatorSettingsPanel',
     'PhabricatorDiffInlineCommentQuery' => 'PhabricatorApplicationTransactionCommentQuery',
     'PhabricatorDiffPreferencesSettingsPanel' => 'PhabricatorSettingsPanel',
     'PhabricatorDifferenceEngine' => 'Phobject',
     'PhabricatorDifferentialApplication' => 'PhabricatorApplication',
     'PhabricatorDifferentialAttachCommitWorkflow' => 'PhabricatorDifferentialManagementWorkflow',
     'PhabricatorDifferentialConfigOptions' => 'PhabricatorApplicationConfigOptions',
     'PhabricatorDifferentialExtractWorkflow' => 'PhabricatorDifferentialManagementWorkflow',
     'PhabricatorDifferentialManagementWorkflow' => 'PhabricatorManagementWorkflow',
     'PhabricatorDifferentialRevisionTestDataGenerator' => 'PhabricatorTestDataGenerator',
     'PhabricatorDiffusionApplication' => 'PhabricatorApplication',
     'PhabricatorDiffusionConfigOptions' => 'PhabricatorApplicationConfigOptions',
     'PhabricatorDisabledUserController' => 'PhabricatorAuthController',
     'PhabricatorDisplayPreferencesSettingsPanel' => 'PhabricatorSettingsPanel',
     'PhabricatorDisqusAuthProvider' => 'PhabricatorOAuth2AuthProvider',
     'PhabricatorDividerProfilePanel' => 'PhabricatorProfilePanel',
     'PhabricatorDivinerApplication' => 'PhabricatorApplication',
     'PhabricatorDoorkeeperApplication' => 'PhabricatorApplication',
     'PhabricatorDraft' => 'PhabricatorDraftDAO',
     'PhabricatorDraftDAO' => 'PhabricatorLiskDAO',
     'PhabricatorDrydockApplication' => 'PhabricatorApplication',
     'PhabricatorEdgeConfig' => 'PhabricatorEdgeConstants',
     'PhabricatorEdgeConstants' => 'Phobject',
     'PhabricatorEdgeCycleException' => 'Exception',
     'PhabricatorEdgeEditType' => 'PhabricatorPHIDListEditType',
     'PhabricatorEdgeEditor' => 'Phobject',
     'PhabricatorEdgeGraph' => 'AbstractDirectedGraph',
     'PhabricatorEdgeQuery' => 'PhabricatorQuery',
     'PhabricatorEdgeTestCase' => 'PhabricatorTestCase',
     'PhabricatorEdgeType' => 'Phobject',
     'PhabricatorEdgeTypeTestCase' => 'PhabricatorTestCase',
     'PhabricatorEdgesDestructionEngineExtension' => 'PhabricatorDestructionEngineExtension',
     'PhabricatorEditEngine' => array(
       'Phobject',
       'PhabricatorPolicyInterface',
     ),
     'PhabricatorEditEngineAPIMethod' => 'ConduitAPIMethod',
     'PhabricatorEditEngineColumnsCommentAction' => 'PhabricatorEditEngineCommentAction',
     'PhabricatorEditEngineCommentAction' => 'Phobject',
     'PhabricatorEditEngineConfiguration' => array(
       'PhabricatorSearchDAO',
       'PhabricatorApplicationTransactionInterface',
       'PhabricatorPolicyInterface',
     ),
     'PhabricatorEditEngineConfigurationDefaultCreateController' => 'PhabricatorEditEngineController',
     'PhabricatorEditEngineConfigurationDefaultsController' => 'PhabricatorEditEngineController',
     'PhabricatorEditEngineConfigurationDisableController' => 'PhabricatorEditEngineController',
     'PhabricatorEditEngineConfigurationEditController' => 'PhabricatorEditEngineController',
     'PhabricatorEditEngineConfigurationEditEngine' => 'PhabricatorEditEngine',
     'PhabricatorEditEngineConfigurationEditor' => 'PhabricatorApplicationTransactionEditor',
     'PhabricatorEditEngineConfigurationIsEditController' => 'PhabricatorEditEngineController',
     'PhabricatorEditEngineConfigurationListController' => 'PhabricatorEditEngineController',
     'PhabricatorEditEngineConfigurationLockController' => 'PhabricatorEditEngineController',
     'PhabricatorEditEngineConfigurationPHIDType' => 'PhabricatorPHIDType',
     'PhabricatorEditEngineConfigurationQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
     'PhabricatorEditEngineConfigurationReorderController' => 'PhabricatorEditEngineController',
     'PhabricatorEditEngineConfigurationSaveController' => 'PhabricatorEditEngineController',
     'PhabricatorEditEngineConfigurationSearchEngine' => 'PhabricatorApplicationSearchEngine',
     'PhabricatorEditEngineConfigurationSortController' => 'PhabricatorEditEngineController',
     'PhabricatorEditEngineConfigurationTransaction' => 'PhabricatorApplicationTransaction',
     'PhabricatorEditEngineConfigurationTransactionQuery' => 'PhabricatorApplicationTransactionQuery',
     'PhabricatorEditEngineConfigurationViewController' => 'PhabricatorEditEngineController',
     'PhabricatorEditEngineController' => 'PhabricatorApplicationTransactionController',
     'PhabricatorEditEngineExtension' => 'Phobject',
     'PhabricatorEditEngineExtensionModule' => 'PhabricatorConfigModule',
     'PhabricatorEditEngineListController' => 'PhabricatorEditEngineController',
     'PhabricatorEditEnginePointsCommentAction' => 'PhabricatorEditEngineCommentAction',
     'PhabricatorEditEngineQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
     'PhabricatorEditEngineSearchEngine' => 'PhabricatorApplicationSearchEngine',
     'PhabricatorEditEngineSelectCommentAction' => 'PhabricatorEditEngineCommentAction',
     'PhabricatorEditEngineTokenizerCommentAction' => 'PhabricatorEditEngineCommentAction',
     'PhabricatorEditField' => 'Phobject',
+    'PhabricatorEditPage' => 'Phobject',
     'PhabricatorEditType' => 'Phobject',
     'PhabricatorEditor' => 'Phobject',
     'PhabricatorElasticFulltextStorageEngine' => 'PhabricatorFulltextStorageEngine',
     'PhabricatorElasticSearchSetupCheck' => 'PhabricatorSetupCheck',
     'PhabricatorEmailAddressesSettingsPanel' => 'PhabricatorSettingsPanel',
     'PhabricatorEmailContentSource' => 'PhabricatorContentSource',
     'PhabricatorEmailFormatSettingsPanel' => 'PhabricatorSettingsPanel',
     'PhabricatorEmailLoginController' => 'PhabricatorAuthController',
     'PhabricatorEmailPreferencesSettingsPanel' => 'PhabricatorSettingsPanel',
     'PhabricatorEmailVerificationController' => 'PhabricatorAuthController',
     'PhabricatorEmbedFileRemarkupRule' => 'PhabricatorObjectRemarkupRule',
     'PhabricatorEmojiRemarkupRule' => 'PhutilRemarkupRule',
     'PhabricatorEmptyQueryException' => 'Exception',
     'PhabricatorEnv' => 'Phobject',
     'PhabricatorEnvTestCase' => 'PhabricatorTestCase',
     'PhabricatorEpochEditField' => 'PhabricatorEditField',
     'PhabricatorEvent' => 'PhutilEvent',
     'PhabricatorEventEngine' => 'Phobject',
     'PhabricatorEventListener' => 'PhutilEventListener',
     'PhabricatorEventType' => 'PhutilEventType',
     'PhabricatorExampleEventListener' => 'PhabricatorEventListener',
     'PhabricatorExecFutureFileUploadSource' => 'PhabricatorFileUploadSource',
     'PhabricatorExtendingPhabricatorConfigOptions' => 'PhabricatorApplicationConfigOptions',
     'PhabricatorExtensionsSetupCheck' => 'PhabricatorSetupCheck',
     'PhabricatorExternalAccount' => array(
       'PhabricatorUserDAO',
       'PhabricatorPolicyInterface',
     ),
     'PhabricatorExternalAccountQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
     'PhabricatorExternalAccountsSettingsPanel' => 'PhabricatorSettingsPanel',
     'PhabricatorExtraConfigSetupCheck' => 'PhabricatorSetupCheck',
     'PhabricatorFacebookAuthProvider' => 'PhabricatorOAuth2AuthProvider',
     'PhabricatorFactAggregate' => 'PhabricatorFactDAO',
     'PhabricatorFactApplication' => 'PhabricatorApplication',
     'PhabricatorFactChartController' => 'PhabricatorFactController',
     'PhabricatorFactController' => 'PhabricatorController',
     'PhabricatorFactCountEngine' => 'PhabricatorFactEngine',
     'PhabricatorFactCursor' => 'PhabricatorFactDAO',
     'PhabricatorFactDAO' => 'PhabricatorLiskDAO',
     'PhabricatorFactDaemon' => 'PhabricatorDaemon',
     'PhabricatorFactEngine' => 'Phobject',
     'PhabricatorFactEngineTestCase' => 'PhabricatorTestCase',
     'PhabricatorFactHomeController' => 'PhabricatorFactController',
     'PhabricatorFactLastUpdatedEngine' => 'PhabricatorFactEngine',
     'PhabricatorFactManagementAnalyzeWorkflow' => 'PhabricatorFactManagementWorkflow',
     'PhabricatorFactManagementCursorsWorkflow' => 'PhabricatorFactManagementWorkflow',
     'PhabricatorFactManagementDestroyWorkflow' => 'PhabricatorFactManagementWorkflow',
     'PhabricatorFactManagementListWorkflow' => 'PhabricatorFactManagementWorkflow',
     'PhabricatorFactManagementStatusWorkflow' => 'PhabricatorFactManagementWorkflow',
     'PhabricatorFactManagementWorkflow' => 'PhabricatorManagementWorkflow',
     'PhabricatorFactRaw' => 'PhabricatorFactDAO',
     'PhabricatorFactSimpleSpec' => 'PhabricatorFactSpec',
     'PhabricatorFactSpec' => 'Phobject',
     'PhabricatorFactUpdateIterator' => 'PhutilBufferedIterator',
     'PhabricatorFaxContentSource' => 'PhabricatorContentSource',
     'PhabricatorFeedApplication' => 'PhabricatorApplication',
     'PhabricatorFeedBuilder' => 'Phobject',
     'PhabricatorFeedConfigOptions' => 'PhabricatorApplicationConfigOptions',
     'PhabricatorFeedController' => 'PhabricatorController',
     'PhabricatorFeedDAO' => 'PhabricatorLiskDAO',
     'PhabricatorFeedDetailController' => 'PhabricatorFeedController',
     'PhabricatorFeedListController' => 'PhabricatorFeedController',
     'PhabricatorFeedManagementRepublishWorkflow' => 'PhabricatorFeedManagementWorkflow',
     'PhabricatorFeedManagementWorkflow' => 'PhabricatorManagementWorkflow',
     'PhabricatorFeedQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
     'PhabricatorFeedSearchEngine' => 'PhabricatorApplicationSearchEngine',
     'PhabricatorFeedStory' => array(
       'Phobject',
       'PhabricatorPolicyInterface',
       'PhabricatorMarkupInterface',
     ),
     'PhabricatorFeedStoryData' => 'PhabricatorFeedDAO',
     'PhabricatorFeedStoryNotification' => 'PhabricatorFeedDAO',
     'PhabricatorFeedStoryPublisher' => 'Phobject',
     'PhabricatorFeedStoryReference' => 'PhabricatorFeedDAO',
     'PhabricatorFile' => array(
       'PhabricatorFileDAO',
       'PhabricatorApplicationTransactionInterface',
       'PhabricatorTokenReceiverInterface',
       'PhabricatorSubscribableInterface',
       'PhabricatorFlaggableInterface',
       'PhabricatorPolicyInterface',
       'PhabricatorDestructibleInterface',
     ),
     'PhabricatorFileBundleLoader' => 'Phobject',
     'PhabricatorFileChunk' => array(
       'PhabricatorFileDAO',
       'PhabricatorPolicyInterface',
       'PhabricatorDestructibleInterface',
     ),
     'PhabricatorFileChunkIterator' => array(
       'Phobject',
       'Iterator',
     ),
     'PhabricatorFileChunkQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
     'PhabricatorFileCommentController' => 'PhabricatorFileController',
     'PhabricatorFileComposeController' => 'PhabricatorFileController',
     'PhabricatorFileController' => 'PhabricatorController',
     'PhabricatorFileDAO' => 'PhabricatorLiskDAO',
     'PhabricatorFileDataController' => 'PhabricatorFileController',
     'PhabricatorFileDeleteController' => 'PhabricatorFileController',
     'PhabricatorFileDropUploadController' => 'PhabricatorFileController',
     'PhabricatorFileEditController' => 'PhabricatorFileController',
     'PhabricatorFileEditor' => 'PhabricatorApplicationTransactionEditor',
     'PhabricatorFileFilePHIDType' => 'PhabricatorPHIDType',
     'PhabricatorFileHasObjectEdgeType' => 'PhabricatorEdgeType',
     'PhabricatorFileIconSetSelectController' => 'PhabricatorFileController',
     'PhabricatorFileImageMacro' => array(
       'PhabricatorFileDAO',
       'PhabricatorSubscribableInterface',
       'PhabricatorApplicationTransactionInterface',
       'PhabricatorFlaggableInterface',
       'PhabricatorTokenReceiverInterface',
       'PhabricatorPolicyInterface',
     ),
     'PhabricatorFileImageTransform' => 'PhabricatorFileTransform',
     'PhabricatorFileInfoController' => 'PhabricatorFileController',
     'PhabricatorFileLinkView' => 'AphrontView',
     'PhabricatorFileListController' => 'PhabricatorFileController',
     'PhabricatorFileQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
     'PhabricatorFileSchemaSpec' => 'PhabricatorConfigSchemaSpec',
     'PhabricatorFileSearchEngine' => 'PhabricatorApplicationSearchEngine',
     'PhabricatorFileStorageBlob' => 'PhabricatorFileDAO',
     'PhabricatorFileStorageConfigurationException' => 'Exception',
     'PhabricatorFileStorageEngine' => 'Phobject',
     'PhabricatorFileStorageEngineTestCase' => 'PhabricatorTestCase',
     'PhabricatorFileTemporaryGarbageCollector' => 'PhabricatorGarbageCollector',
     'PhabricatorFileTestCase' => 'PhabricatorTestCase',
     'PhabricatorFileTestDataGenerator' => 'PhabricatorTestDataGenerator',
     'PhabricatorFileThumbnailTransform' => 'PhabricatorFileImageTransform',
     'PhabricatorFileTransaction' => 'PhabricatorApplicationTransaction',
     'PhabricatorFileTransactionComment' => 'PhabricatorApplicationTransactionComment',
     'PhabricatorFileTransactionQuery' => 'PhabricatorApplicationTransactionQuery',
     'PhabricatorFileTransform' => 'Phobject',
     'PhabricatorFileTransformController' => 'PhabricatorFileController',
     'PhabricatorFileTransformListController' => 'PhabricatorFileController',
     'PhabricatorFileTransformTestCase' => 'PhabricatorTestCase',
     'PhabricatorFileUploadController' => 'PhabricatorFileController',
     'PhabricatorFileUploadDialogController' => 'PhabricatorFileController',
     'PhabricatorFileUploadException' => 'Exception',
     'PhabricatorFileUploadSource' => 'Phobject',
     'PhabricatorFileinfoSetupCheck' => 'PhabricatorSetupCheck',
     'PhabricatorFilesApplication' => 'PhabricatorApplication',
     'PhabricatorFilesApplicationStorageEnginePanel' => 'PhabricatorApplicationConfigurationPanel',
     'PhabricatorFilesBuiltinFile' => 'Phobject',
     'PhabricatorFilesComposeIconBuiltinFile' => 'PhabricatorFilesBuiltinFile',
     'PhabricatorFilesConfigOptions' => 'PhabricatorApplicationConfigOptions',
     'PhabricatorFilesManagementCatWorkflow' => 'PhabricatorFilesManagementWorkflow',
     'PhabricatorFilesManagementCompactWorkflow' => 'PhabricatorFilesManagementWorkflow',
     'PhabricatorFilesManagementEnginesWorkflow' => 'PhabricatorFilesManagementWorkflow',
     'PhabricatorFilesManagementMigrateWorkflow' => 'PhabricatorFilesManagementWorkflow',
     'PhabricatorFilesManagementPurgeWorkflow' => 'PhabricatorFilesManagementWorkflow',
     'PhabricatorFilesManagementRebuildWorkflow' => 'PhabricatorFilesManagementWorkflow',
     'PhabricatorFilesManagementWorkflow' => 'PhabricatorManagementWorkflow',
     'PhabricatorFilesOnDiskBuiltinFile' => 'PhabricatorFilesBuiltinFile',
     'PhabricatorFilesOutboundRequestAction' => 'PhabricatorSystemAction',
     'PhabricatorFlag' => array(
       'PhabricatorFlagDAO',
       'PhabricatorPolicyInterface',
     ),
     'PhabricatorFlagAddFlagHeraldAction' => 'HeraldAction',
     'PhabricatorFlagColor' => 'PhabricatorFlagConstants',
     'PhabricatorFlagConstants' => 'Phobject',
     'PhabricatorFlagController' => 'PhabricatorController',
     'PhabricatorFlagDAO' => 'PhabricatorLiskDAO',
     'PhabricatorFlagDeleteController' => 'PhabricatorFlagController',
     'PhabricatorFlagDestructionEngineExtension' => 'PhabricatorDestructionEngineExtension',
     'PhabricatorFlagEditController' => 'PhabricatorFlagController',
     'PhabricatorFlagListController' => 'PhabricatorFlagController',
     'PhabricatorFlagQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
     'PhabricatorFlagSearchEngine' => 'PhabricatorApplicationSearchEngine',
     'PhabricatorFlagSelectControl' => 'AphrontFormControl',
     'PhabricatorFlaggableInterface' => 'PhabricatorPHIDInterface',
     'PhabricatorFlagsApplication' => 'PhabricatorApplication',
     'PhabricatorFlagsUIEventListener' => 'PhabricatorEventListener',
     'PhabricatorFulltextEngine' => 'Phobject',
     'PhabricatorFulltextEngineExtension' => 'Phobject',
     'PhabricatorFulltextEngineExtensionModule' => 'PhabricatorConfigModule',
     'PhabricatorFulltextIndexEngineExtension' => 'PhabricatorIndexEngineExtension',
     'PhabricatorFulltextStorageEngine' => 'Phobject',
     'PhabricatorFundApplication' => 'PhabricatorApplication',
     'PhabricatorGDSetupCheck' => 'PhabricatorSetupCheck',
     'PhabricatorGarbageCollector' => 'Phobject',
     'PhabricatorGarbageCollectorManagementCollectWorkflow' => 'PhabricatorGarbageCollectorManagementWorkflow',
     'PhabricatorGarbageCollectorManagementSetPolicyWorkflow' => 'PhabricatorGarbageCollectorManagementWorkflow',
     'PhabricatorGarbageCollectorManagementWorkflow' => 'PhabricatorManagementWorkflow',
     'PhabricatorGestureUIExample' => 'PhabricatorUIExample',
     'PhabricatorGitGraphStream' => 'PhabricatorRepositoryGraphStream',
     'PhabricatorGitHubAuthProvider' => 'PhabricatorOAuth2AuthProvider',
     'PhabricatorGlobalLock' => 'PhutilLock',
     'PhabricatorGlobalUploadTargetView' => 'AphrontView',
     'PhabricatorGoogleAuthProvider' => 'PhabricatorOAuth2AuthProvider',
     'PhabricatorHTTPParameterTypeTableView' => 'AphrontView',
     'PhabricatorHandleList' => array(
       'Phobject',
       'Iterator',
       'ArrayAccess',
       'Countable',
     ),
     'PhabricatorHandleObjectSelectorDataView' => 'Phobject',
     'PhabricatorHandlePool' => 'Phobject',
     'PhabricatorHandlePoolTestCase' => 'PhabricatorTestCase',
     'PhabricatorHandleQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
     'PhabricatorHandleRemarkupRule' => 'PhutilRemarkupRule',
     'PhabricatorHandlesEditField' => 'PhabricatorPHIDListEditField',
     'PhabricatorHarbormasterApplication' => 'PhabricatorApplication',
     'PhabricatorHarbormasterConfigOptions' => 'PhabricatorApplicationConfigOptions',
     'PhabricatorHash' => 'Phobject',
     'PhabricatorHashTestCase' => 'PhabricatorTestCase',
     'PhabricatorHelpApplication' => 'PhabricatorApplication',
     'PhabricatorHelpController' => 'PhabricatorController',
     'PhabricatorHelpDocumentationController' => 'PhabricatorHelpController',
     'PhabricatorHelpEditorProtocolController' => 'PhabricatorHelpController',
     'PhabricatorHelpKeyboardShortcutController' => 'PhabricatorHelpController',
     'PhabricatorHelpMainMenuBarExtension' => 'PhabricatorMainMenuBarExtension',
     'PhabricatorHeraldApplication' => 'PhabricatorApplication',
     'PhabricatorHeraldContentSource' => 'PhabricatorContentSource',
     'PhabricatorHighSecurityRequestExceptionHandler' => 'PhabricatorRequestExceptionHandler',
     'PhabricatorHomeApplication' => 'PhabricatorApplication',
     'PhabricatorHomeController' => 'PhabricatorController',
     'PhabricatorHomeMainController' => 'PhabricatorHomeController',
     'PhabricatorHomePreferencesSettingsPanel' => 'PhabricatorSettingsPanel',
     'PhabricatorHomeQuickCreateController' => 'PhabricatorHomeController',
     'PhabricatorHovercardEngineExtension' => 'Phobject',
     'PhabricatorHovercardEngineExtensionModule' => 'PhabricatorConfigModule',
     'PhabricatorHunksManagementMigrateWorkflow' => 'PhabricatorHunksManagementWorkflow',
     'PhabricatorHunksManagementWorkflow' => 'PhabricatorManagementWorkflow',
     'PhabricatorIDsSearchEngineExtension' => 'PhabricatorSearchEngineExtension',
     'PhabricatorIDsSearchField' => 'PhabricatorSearchField',
     'PhabricatorIRCProtocolAdapter' => 'PhabricatorProtocolAdapter',
     'PhabricatorIconRemarkupRule' => 'PhutilRemarkupRule',
     'PhabricatorIconSet' => 'Phobject',
     'PhabricatorIconSetEditField' => 'PhabricatorEditField',
     'PhabricatorIconSetIcon' => 'Phobject',
     'PhabricatorImageMacroRemarkupRule' => 'PhutilRemarkupRule',
     'PhabricatorImageTransformer' => 'Phobject',
     'PhabricatorImagemagickSetupCheck' => 'PhabricatorSetupCheck',
     'PhabricatorIndexEngine' => 'Phobject',
     'PhabricatorIndexEngineExtension' => 'Phobject',
     'PhabricatorIndexEngineExtensionModule' => 'PhabricatorConfigModule',
     'PhabricatorInfrastructureTestCase' => 'PhabricatorTestCase',
     'PhabricatorInlineCommentController' => 'PhabricatorController',
     'PhabricatorInlineCommentInterface' => 'PhabricatorMarkupInterface',
     'PhabricatorInlineCommentPreviewController' => 'PhabricatorController',
     'PhabricatorInlineSummaryView' => 'AphrontView',
     'PhabricatorInstructionsEditField' => 'PhabricatorEditField',
     'PhabricatorInternationalizationManagementExtractWorkflow' => 'PhabricatorInternationalizationManagementWorkflow',
     'PhabricatorInternationalizationManagementWorkflow' => 'PhabricatorManagementWorkflow',
     'PhabricatorInvalidConfigSetupCheck' => 'PhabricatorSetupCheck',
     'PhabricatorIteratedMD5PasswordHasher' => 'PhabricatorPasswordHasher',
     'PhabricatorIteratedMD5PasswordHasherTestCase' => 'PhabricatorTestCase',
     'PhabricatorIteratorFileUploadSource' => 'PhabricatorFileUploadSource',
     'PhabricatorJIRAAuthProvider' => 'PhabricatorOAuth1AuthProvider',
     'PhabricatorJavelinLinter' => 'ArcanistLinter',
     'PhabricatorJiraIssueHasObjectEdgeType' => 'PhabricatorEdgeType',
     'PhabricatorJumpNavHandler' => 'Phobject',
     'PhabricatorKeyValueDatabaseCache' => 'PhutilKeyValueCache',
     'PhabricatorLDAPAuthProvider' => 'PhabricatorAuthProvider',
     'PhabricatorLegalpadApplication' => 'PhabricatorApplication',
     'PhabricatorLegalpadConfigOptions' => 'PhabricatorApplicationConfigOptions',
     'PhabricatorLegalpadDocumentPHIDType' => 'PhabricatorPHIDType',
     'PhabricatorLegalpadSignaturePolicyRule' => 'PhabricatorPolicyRule',
     'PhabricatorLibraryTestCase' => 'PhutilLibraryTestCase',
     'PhabricatorLinkProfilePanel' => 'PhabricatorProfilePanel',
     'PhabricatorLipsumArtist' => 'Phobject',
     'PhabricatorLipsumContentSource' => 'PhabricatorContentSource',
     'PhabricatorLipsumGenerateWorkflow' => 'PhabricatorLipsumManagementWorkflow',
     'PhabricatorLipsumManagementWorkflow' => 'PhabricatorManagementWorkflow',
     'PhabricatorLipsumMondrianArtist' => 'PhabricatorLipsumArtist',
     'PhabricatorLiskDAO' => 'LiskDAO',
     'PhabricatorLiskFulltextEngineExtension' => 'PhabricatorFulltextEngineExtension',
     'PhabricatorLiskSearchEngineExtension' => 'PhabricatorSearchEngineExtension',
     'PhabricatorLiskSerializer' => 'Phobject',
     'PhabricatorListFilterUIExample' => 'PhabricatorUIExample',
     'PhabricatorLocalDiskFileStorageEngine' => 'PhabricatorFileStorageEngine',
     'PhabricatorLocalTimeTestCase' => 'PhabricatorTestCase',
     'PhabricatorLocaleScopeGuard' => 'Phobject',
     'PhabricatorLocaleScopeGuardTestCase' => 'PhabricatorTestCase',
     'PhabricatorLogTriggerAction' => 'PhabricatorTriggerAction',
     'PhabricatorLogoutController' => 'PhabricatorAuthController',
     'PhabricatorLunarPhasePolicyRule' => 'PhabricatorPolicyRule',
     'PhabricatorMacroApplication' => 'PhabricatorApplication',
     'PhabricatorMacroAudioController' => 'PhabricatorMacroController',
     'PhabricatorMacroCommentController' => 'PhabricatorMacroController',
     'PhabricatorMacroConfigOptions' => 'PhabricatorApplicationConfigOptions',
     'PhabricatorMacroController' => 'PhabricatorController',
     'PhabricatorMacroDatasource' => 'PhabricatorTypeaheadDatasource',
     'PhabricatorMacroDisableController' => 'PhabricatorMacroController',
     'PhabricatorMacroEditController' => 'PhabricatorMacroController',
     'PhabricatorMacroEditor' => 'PhabricatorApplicationTransactionEditor',
     'PhabricatorMacroListController' => 'PhabricatorMacroController',
     'PhabricatorMacroMacroPHIDType' => 'PhabricatorPHIDType',
     'PhabricatorMacroMailReceiver' => 'PhabricatorObjectMailReceiver',
     'PhabricatorMacroManageCapability' => 'PhabricatorPolicyCapability',
     'PhabricatorMacroMemeController' => 'PhabricatorMacroController',
     'PhabricatorMacroMemeDialogController' => 'PhabricatorMacroController',
     'PhabricatorMacroQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
     'PhabricatorMacroReplyHandler' => 'PhabricatorApplicationTransactionReplyHandler',
     'PhabricatorMacroSearchEngine' => 'PhabricatorApplicationSearchEngine',
     'PhabricatorMacroTransaction' => 'PhabricatorApplicationTransaction',
     'PhabricatorMacroTransactionComment' => 'PhabricatorApplicationTransactionComment',
     'PhabricatorMacroTransactionQuery' => 'PhabricatorApplicationTransactionQuery',
     'PhabricatorMacroViewController' => 'PhabricatorMacroController',
     'PhabricatorMailEmailHeraldField' => 'HeraldField',
     'PhabricatorMailEmailHeraldFieldGroup' => 'HeraldFieldGroup',
     'PhabricatorMailEmailSubjectHeraldField' => 'PhabricatorMailEmailHeraldField',
     'PhabricatorMailImplementationAdapter' => 'Phobject',
     'PhabricatorMailImplementationAmazonSESAdapter' => 'PhabricatorMailImplementationPHPMailerLiteAdapter',
     'PhabricatorMailImplementationMailgunAdapter' => 'PhabricatorMailImplementationAdapter',
     'PhabricatorMailImplementationPHPMailerAdapter' => 'PhabricatorMailImplementationAdapter',
     'PhabricatorMailImplementationPHPMailerLiteAdapter' => 'PhabricatorMailImplementationAdapter',
     'PhabricatorMailImplementationSendGridAdapter' => 'PhabricatorMailImplementationAdapter',
     'PhabricatorMailImplementationTestAdapter' => 'PhabricatorMailImplementationAdapter',
     'PhabricatorMailManagementListInboundWorkflow' => 'PhabricatorMailManagementWorkflow',
     'PhabricatorMailManagementListOutboundWorkflow' => 'PhabricatorMailManagementWorkflow',
     'PhabricatorMailManagementReceiveTestWorkflow' => 'PhabricatorMailManagementWorkflow',
     'PhabricatorMailManagementResendWorkflow' => 'PhabricatorMailManagementWorkflow',
     'PhabricatorMailManagementSendTestWorkflow' => 'PhabricatorMailManagementWorkflow',
     'PhabricatorMailManagementShowInboundWorkflow' => 'PhabricatorMailManagementWorkflow',
     'PhabricatorMailManagementShowOutboundWorkflow' => 'PhabricatorMailManagementWorkflow',
     'PhabricatorMailManagementVolumeWorkflow' => 'PhabricatorMailManagementWorkflow',
     'PhabricatorMailManagementWorkflow' => 'PhabricatorManagementWorkflow',
     'PhabricatorMailOutboundMailHeraldAdapter' => 'HeraldAdapter',
     'PhabricatorMailOutboundRoutingHeraldAction' => 'HeraldAction',
     'PhabricatorMailOutboundRoutingSelfEmailHeraldAction' => 'PhabricatorMailOutboundRoutingHeraldAction',
     'PhabricatorMailOutboundRoutingSelfNotificationHeraldAction' => 'PhabricatorMailOutboundRoutingHeraldAction',
     'PhabricatorMailOutboundStatus' => 'Phobject',
     'PhabricatorMailReceiver' => 'Phobject',
     'PhabricatorMailReceiverTestCase' => 'PhabricatorTestCase',
     'PhabricatorMailReplyHandler' => 'Phobject',
     'PhabricatorMailRoutingRule' => 'Phobject',
     'PhabricatorMailSetupCheck' => 'PhabricatorSetupCheck',
     'PhabricatorMailTarget' => 'Phobject',
     'PhabricatorMailgunConfigOptions' => 'PhabricatorApplicationConfigOptions',
     'PhabricatorMainMenuBarExtension' => 'Phobject',
     'PhabricatorMainMenuSearchView' => 'AphrontView',
     'PhabricatorMainMenuView' => 'AphrontView',
     'PhabricatorManagementWorkflow' => 'PhutilArgumentWorkflow',
     'PhabricatorManiphestApplication' => 'PhabricatorApplication',
     'PhabricatorManiphestConfigOptions' => 'PhabricatorApplicationConfigOptions',
     'PhabricatorManiphestTaskTestDataGenerator' => 'PhabricatorTestDataGenerator',
     'PhabricatorMarkupCache' => 'PhabricatorCacheDAO',
     'PhabricatorMarkupEngine' => 'Phobject',
     'PhabricatorMarkupOneOff' => array(
       'Phobject',
       'PhabricatorMarkupInterface',
     ),
     'PhabricatorMarkupPreviewController' => 'PhabricatorController',
     'PhabricatorMemeRemarkupRule' => 'PhutilRemarkupRule',
     'PhabricatorMentionRemarkupRule' => 'PhutilRemarkupRule',
     'PhabricatorMercurialGraphStream' => 'PhabricatorRepositoryGraphStream',
     'PhabricatorMetaMTAActor' => 'Phobject',
     'PhabricatorMetaMTAActorQuery' => 'PhabricatorQuery',
     'PhabricatorMetaMTAApplication' => 'PhabricatorApplication',
     'PhabricatorMetaMTAApplicationEmail' => array(
       'PhabricatorMetaMTADAO',
       'PhabricatorPolicyInterface',
       'PhabricatorApplicationTransactionInterface',
       'PhabricatorDestructibleInterface',
       'PhabricatorSpacesInterface',
     ),
     'PhabricatorMetaMTAApplicationEmailDatasource' => 'PhabricatorTypeaheadDatasource',
     'PhabricatorMetaMTAApplicationEmailEditor' => 'PhabricatorApplicationTransactionEditor',
     'PhabricatorMetaMTAApplicationEmailHeraldField' => 'HeraldField',
     'PhabricatorMetaMTAApplicationEmailPHIDType' => 'PhabricatorPHIDType',
     'PhabricatorMetaMTAApplicationEmailPanel' => 'PhabricatorApplicationConfigurationPanel',
     'PhabricatorMetaMTAApplicationEmailQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
     'PhabricatorMetaMTAApplicationEmailTransaction' => 'PhabricatorApplicationTransaction',
     'PhabricatorMetaMTAApplicationEmailTransactionQuery' => 'PhabricatorApplicationTransactionQuery',
     'PhabricatorMetaMTAAttachment' => 'Phobject',
     'PhabricatorMetaMTAConfigOptions' => 'PhabricatorApplicationConfigOptions',
     'PhabricatorMetaMTAController' => 'PhabricatorController',
     'PhabricatorMetaMTADAO' => 'PhabricatorLiskDAO',
     'PhabricatorMetaMTAEmailBodyParser' => 'Phobject',
     'PhabricatorMetaMTAEmailBodyParserTestCase' => 'PhabricatorTestCase',
     'PhabricatorMetaMTAEmailHeraldAction' => 'HeraldAction',
     'PhabricatorMetaMTAEmailOthersHeraldAction' => 'PhabricatorMetaMTAEmailHeraldAction',
     'PhabricatorMetaMTAEmailSelfHeraldAction' => 'PhabricatorMetaMTAEmailHeraldAction',
     'PhabricatorMetaMTAErrorMailAction' => 'PhabricatorSystemAction',
     'PhabricatorMetaMTAMail' => array(
       'PhabricatorMetaMTADAO',
       'PhabricatorPolicyInterface',
     ),
     'PhabricatorMetaMTAMailBody' => 'Phobject',
     'PhabricatorMetaMTAMailBodyTestCase' => 'PhabricatorTestCase',
     'PhabricatorMetaMTAMailHasRecipientEdgeType' => 'PhabricatorEdgeType',
     'PhabricatorMetaMTAMailListController' => 'PhabricatorMetaMTAController',
     'PhabricatorMetaMTAMailPHIDType' => 'PhabricatorPHIDType',
     'PhabricatorMetaMTAMailQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
     'PhabricatorMetaMTAMailSearchEngine' => 'PhabricatorApplicationSearchEngine',
     'PhabricatorMetaMTAMailSection' => 'Phobject',
     'PhabricatorMetaMTAMailTestCase' => 'PhabricatorTestCase',
     'PhabricatorMetaMTAMailViewController' => 'PhabricatorMetaMTAController',
     'PhabricatorMetaMTAMailableDatasource' => 'PhabricatorTypeaheadCompositeDatasource',
     'PhabricatorMetaMTAMailableFunctionDatasource' => 'PhabricatorTypeaheadCompositeDatasource',
     'PhabricatorMetaMTAMailgunReceiveController' => 'PhabricatorMetaMTAController',
     'PhabricatorMetaMTAMemberQuery' => 'PhabricatorQuery',
     'PhabricatorMetaMTAPermanentFailureException' => 'Exception',
     'PhabricatorMetaMTAReceivedMail' => 'PhabricatorMetaMTADAO',
     'PhabricatorMetaMTAReceivedMailProcessingException' => 'Exception',
     'PhabricatorMetaMTAReceivedMailTestCase' => 'PhabricatorTestCase',
     'PhabricatorMetaMTASchemaSpec' => 'PhabricatorConfigSchemaSpec',
     'PhabricatorMetaMTASendGridReceiveController' => 'PhabricatorMetaMTAController',
     'PhabricatorMetaMTAWorker' => 'PhabricatorWorker',
     'PhabricatorMetronomicTriggerClock' => 'PhabricatorTriggerClock',
     'PhabricatorMotivatorProfilePanel' => 'PhabricatorProfilePanel',
     'PhabricatorMultiColumnUIExample' => 'PhabricatorUIExample',
     'PhabricatorMultiFactorSettingsPanel' => 'PhabricatorSettingsPanel',
     'PhabricatorMultimeterApplication' => 'PhabricatorApplication',
     'PhabricatorMustVerifyEmailController' => 'PhabricatorAuthController',
     'PhabricatorMySQLConfigOptions' => 'PhabricatorApplicationConfigOptions',
     'PhabricatorMySQLFileStorageEngine' => 'PhabricatorFileStorageEngine',
     'PhabricatorMySQLFulltextStorageEngine' => 'PhabricatorFulltextStorageEngine',
     'PhabricatorMySQLSetupCheck' => 'PhabricatorSetupCheck',
     'PhabricatorNamedQuery' => array(
       'PhabricatorSearchDAO',
       'PhabricatorPolicyInterface',
     ),
     'PhabricatorNamedQueryQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
     'PhabricatorNavigationRemarkupRule' => 'PhutilRemarkupRule',
     'PhabricatorNeverTriggerClock' => 'PhabricatorTriggerClock',
     'PhabricatorNgramsIndexEngineExtension' => 'PhabricatorIndexEngineExtension',
     'PhabricatorNotificationBuilder' => 'Phobject',
     'PhabricatorNotificationClearController' => 'PhabricatorNotificationController',
     'PhabricatorNotificationClient' => 'Phobject',
     'PhabricatorNotificationConfigOptions' => 'PhabricatorApplicationConfigOptions',
     'PhabricatorNotificationController' => 'PhabricatorController',
     'PhabricatorNotificationDestructionEngineExtension' => 'PhabricatorDestructionEngineExtension',
     'PhabricatorNotificationIndividualController' => 'PhabricatorNotificationController',
     'PhabricatorNotificationListController' => 'PhabricatorNotificationController',
     'PhabricatorNotificationPanelController' => 'PhabricatorNotificationController',
     'PhabricatorNotificationQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
     'PhabricatorNotificationSearchEngine' => 'PhabricatorApplicationSearchEngine',
     'PhabricatorNotificationServerRef' => 'Phobject',
     'PhabricatorNotificationServersConfigOptionType' => 'PhabricatorConfigJSONOptionType',
     'PhabricatorNotificationStatusView' => 'AphrontTagView',
     'PhabricatorNotificationTestController' => 'PhabricatorNotificationController',
     'PhabricatorNotificationTestFeedStory' => 'PhabricatorFeedStory',
     'PhabricatorNotificationUIExample' => 'PhabricatorUIExample',
     'PhabricatorNotificationsApplication' => 'PhabricatorApplication',
     'PhabricatorNuanceApplication' => 'PhabricatorApplication',
     'PhabricatorOAuth1AuthProvider' => 'PhabricatorOAuthAuthProvider',
     'PhabricatorOAuth1SecretTemporaryTokenType' => 'PhabricatorAuthTemporaryTokenType',
     'PhabricatorOAuth2AuthProvider' => 'PhabricatorOAuthAuthProvider',
     'PhabricatorOAuthAuthProvider' => 'PhabricatorAuthProvider',
     'PhabricatorOAuthClientAuthorization' => array(
       'PhabricatorOAuthServerDAO',
       'PhabricatorPolicyInterface',
     ),
     'PhabricatorOAuthClientAuthorizationQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
     'PhabricatorOAuthClientController' => 'PhabricatorOAuthServerController',
     'PhabricatorOAuthClientDisableController' => 'PhabricatorOAuthClientController',
     'PhabricatorOAuthClientEditController' => 'PhabricatorOAuthClientController',
     'PhabricatorOAuthClientListController' => 'PhabricatorOAuthClientController',
     'PhabricatorOAuthClientSecretController' => 'PhabricatorOAuthClientController',
     'PhabricatorOAuthClientTestController' => 'PhabricatorOAuthClientController',
     'PhabricatorOAuthClientViewController' => 'PhabricatorOAuthClientController',
     'PhabricatorOAuthResponse' => 'AphrontResponse',
     'PhabricatorOAuthServer' => 'Phobject',
     'PhabricatorOAuthServerAccessToken' => 'PhabricatorOAuthServerDAO',
     'PhabricatorOAuthServerApplication' => 'PhabricatorApplication',
     'PhabricatorOAuthServerAuthController' => 'PhabricatorOAuthServerController',
     'PhabricatorOAuthServerAuthorizationCode' => 'PhabricatorOAuthServerDAO',
     'PhabricatorOAuthServerAuthorizationsSettingsPanel' => 'PhabricatorSettingsPanel',
     'PhabricatorOAuthServerClient' => array(
       'PhabricatorOAuthServerDAO',
       'PhabricatorPolicyInterface',
       'PhabricatorApplicationTransactionInterface',
       'PhabricatorDestructibleInterface',
     ),
     'PhabricatorOAuthServerClientAuthorizationPHIDType' => 'PhabricatorPHIDType',
     'PhabricatorOAuthServerClientPHIDType' => 'PhabricatorPHIDType',
     'PhabricatorOAuthServerClientQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
     'PhabricatorOAuthServerClientSearchEngine' => 'PhabricatorApplicationSearchEngine',
     'PhabricatorOAuthServerController' => 'PhabricatorController',
     'PhabricatorOAuthServerCreateClientsCapability' => 'PhabricatorPolicyCapability',
     'PhabricatorOAuthServerDAO' => 'PhabricatorLiskDAO',
     'PhabricatorOAuthServerEditEngine' => 'PhabricatorEditEngine',
     'PhabricatorOAuthServerEditor' => 'PhabricatorApplicationTransactionEditor',
     'PhabricatorOAuthServerScope' => 'Phobject',
     'PhabricatorOAuthServerTestCase' => 'PhabricatorTestCase',
     'PhabricatorOAuthServerTokenController' => 'PhabricatorOAuthServerController',
     'PhabricatorOAuthServerTransaction' => 'PhabricatorApplicationTransaction',
     'PhabricatorOAuthServerTransactionQuery' => 'PhabricatorApplicationTransactionQuery',
     'PhabricatorObjectHandle' => array(
       'Phobject',
       'PhabricatorPolicyInterface',
     ),
     'PhabricatorObjectHasAsanaSubtaskEdgeType' => 'PhabricatorEdgeType',
     'PhabricatorObjectHasAsanaTaskEdgeType' => 'PhabricatorEdgeType',
     'PhabricatorObjectHasContributorEdgeType' => 'PhabricatorEdgeType',
     'PhabricatorObjectHasFileEdgeType' => 'PhabricatorEdgeType',
     'PhabricatorObjectHasJiraIssueEdgeType' => 'PhabricatorEdgeType',
     'PhabricatorObjectHasSubscriberEdgeType' => 'PhabricatorEdgeType',
     'PhabricatorObjectHasUnsubscriberEdgeType' => 'PhabricatorEdgeType',
     'PhabricatorObjectHasWatcherEdgeType' => 'PhabricatorEdgeType',
     'PhabricatorObjectListQuery' => 'Phobject',
     'PhabricatorObjectListQueryTestCase' => 'PhabricatorTestCase',
     'PhabricatorObjectMailReceiver' => 'PhabricatorMailReceiver',
     'PhabricatorObjectMailReceiverTestCase' => 'PhabricatorTestCase',
     'PhabricatorObjectMentionedByObjectEdgeType' => 'PhabricatorEdgeType',
     'PhabricatorObjectMentionsObjectEdgeType' => 'PhabricatorEdgeType',
     'PhabricatorObjectQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
     'PhabricatorObjectRemarkupRule' => 'PhutilRemarkupRule',
     'PhabricatorObjectSelectorDialog' => 'Phobject',
     'PhabricatorObjectUsesCredentialsEdgeType' => 'PhabricatorEdgeType',
     'PhabricatorOffsetPagedQuery' => 'PhabricatorQuery',
     'PhabricatorOldWorldContentSource' => 'PhabricatorContentSource',
     'PhabricatorOneTimeTriggerClock' => 'PhabricatorTriggerClock',
     'PhabricatorOpcodeCacheSpec' => 'PhabricatorCacheSpec',
     'PhabricatorOwnerPathQuery' => 'Phobject',
     'PhabricatorOwnersApplication' => 'PhabricatorApplication',
     'PhabricatorOwnersArchiveController' => 'PhabricatorOwnersController',
     'PhabricatorOwnersConfigOptions' => 'PhabricatorApplicationConfigOptions',
     'PhabricatorOwnersConfiguredCustomField' => array(
       'PhabricatorOwnersCustomField',
       'PhabricatorStandardCustomFieldInterface',
     ),
     'PhabricatorOwnersController' => 'PhabricatorController',
     'PhabricatorOwnersCustomField' => 'PhabricatorCustomField',
     'PhabricatorOwnersCustomFieldNumericIndex' => 'PhabricatorCustomFieldNumericIndexStorage',
     'PhabricatorOwnersCustomFieldStorage' => 'PhabricatorCustomFieldStorage',
     'PhabricatorOwnersCustomFieldStringIndex' => 'PhabricatorCustomFieldStringIndexStorage',
     'PhabricatorOwnersDAO' => 'PhabricatorLiskDAO',
     'PhabricatorOwnersDefaultEditCapability' => 'PhabricatorPolicyCapability',
     'PhabricatorOwnersDefaultViewCapability' => 'PhabricatorPolicyCapability',
     'PhabricatorOwnersDetailController' => 'PhabricatorOwnersController',
     'PhabricatorOwnersEditController' => 'PhabricatorOwnersController',
     'PhabricatorOwnersListController' => 'PhabricatorOwnersController',
     'PhabricatorOwnersOwner' => 'PhabricatorOwnersDAO',
     'PhabricatorOwnersPackage' => array(
       'PhabricatorOwnersDAO',
       'PhabricatorPolicyInterface',
       'PhabricatorApplicationTransactionInterface',
       'PhabricatorCustomFieldInterface',
       'PhabricatorDestructibleInterface',
       'PhabricatorConduitResultInterface',
       'PhabricatorFulltextInterface',
       'PhabricatorNgramsInterface',
     ),
     'PhabricatorOwnersPackageDatasource' => 'PhabricatorTypeaheadDatasource',
     'PhabricatorOwnersPackageEditEngine' => 'PhabricatorEditEngine',
     'PhabricatorOwnersPackageFulltextEngine' => 'PhabricatorFulltextEngine',
     'PhabricatorOwnersPackageFunctionDatasource' => 'PhabricatorTypeaheadCompositeDatasource',
     'PhabricatorOwnersPackageNameNgrams' => 'PhabricatorSearchNgrams',
     'PhabricatorOwnersPackageOwnerDatasource' => 'PhabricatorTypeaheadCompositeDatasource',
     'PhabricatorOwnersPackagePHIDType' => 'PhabricatorPHIDType',
     'PhabricatorOwnersPackageQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
     'PhabricatorOwnersPackageSearchEngine' => 'PhabricatorApplicationSearchEngine',
     'PhabricatorOwnersPackageTestCase' => 'PhabricatorTestCase',
     'PhabricatorOwnersPackageTransaction' => 'PhabricatorApplicationTransaction',
     'PhabricatorOwnersPackageTransactionEditor' => 'PhabricatorApplicationTransactionEditor',
     'PhabricatorOwnersPackageTransactionQuery' => 'PhabricatorApplicationTransactionQuery',
     'PhabricatorOwnersPath' => 'PhabricatorOwnersDAO',
     'PhabricatorOwnersPathsController' => 'PhabricatorOwnersController',
     'PhabricatorOwnersPathsSearchEngineAttachment' => 'PhabricatorSearchEngineAttachment',
     'PhabricatorOwnersSchemaSpec' => 'PhabricatorConfigSchemaSpec',
     'PhabricatorOwnersSearchField' => 'PhabricatorSearchTokenizerField',
     'PhabricatorPHDConfigOptions' => 'PhabricatorApplicationConfigOptions',
     'PhabricatorPHID' => 'Phobject',
     'PhabricatorPHIDConstants' => 'Phobject',
     'PhabricatorPHIDListEditField' => 'PhabricatorEditField',
     'PhabricatorPHIDListEditType' => 'PhabricatorEditType',
     'PhabricatorPHIDResolver' => 'Phobject',
     'PhabricatorPHIDType' => 'Phobject',
     'PhabricatorPHIDTypeTestCase' => 'PhutilTestCase',
     'PhabricatorPHIDsSearchField' => 'PhabricatorSearchField',
     'PhabricatorPHPASTApplication' => 'PhabricatorApplication',
     'PhabricatorPHPConfigSetupCheck' => 'PhabricatorSetupCheck',
     'PhabricatorPHPMailerConfigOptions' => 'PhabricatorApplicationConfigOptions',
     'PhabricatorPagedFormUIExample' => 'PhabricatorUIExample',
     'PhabricatorPagerUIExample' => 'PhabricatorUIExample',
     'PhabricatorPassphraseApplication' => 'PhabricatorApplication',
     'PhabricatorPasswordAuthProvider' => 'PhabricatorAuthProvider',
     'PhabricatorPasswordHasher' => 'Phobject',
     'PhabricatorPasswordHasherTestCase' => 'PhabricatorTestCase',
     'PhabricatorPasswordHasherUnavailableException' => 'Exception',
     'PhabricatorPasswordSettingsPanel' => 'PhabricatorSettingsPanel',
     'PhabricatorPaste' => array(
       'PhabricatorPasteDAO',
       'PhabricatorSubscribableInterface',
       'PhabricatorTokenReceiverInterface',
       'PhabricatorFlaggableInterface',
       'PhabricatorMentionableInterface',
       'PhabricatorPolicyInterface',
       'PhabricatorProjectInterface',
       'PhabricatorDestructibleInterface',
       'PhabricatorApplicationTransactionInterface',
       'PhabricatorSpacesInterface',
       'PhabricatorConduitResultInterface',
     ),
     'PhabricatorPasteApplication' => 'PhabricatorApplication',
     'PhabricatorPasteArchiveController' => 'PhabricatorPasteController',
     'PhabricatorPasteConfigOptions' => 'PhabricatorApplicationConfigOptions',
     'PhabricatorPasteContentSearchEngineAttachment' => 'PhabricatorSearchEngineAttachment',
     'PhabricatorPasteController' => 'PhabricatorController',
     'PhabricatorPasteDAO' => 'PhabricatorLiskDAO',
     'PhabricatorPasteEditController' => 'PhabricatorPasteController',
     'PhabricatorPasteEditEngine' => 'PhabricatorEditEngine',
     'PhabricatorPasteEditor' => 'PhabricatorApplicationTransactionEditor',
     'PhabricatorPasteFilenameContextFreeGrammar' => 'PhutilContextFreeGrammar',
     'PhabricatorPasteListController' => 'PhabricatorPasteController',
     'PhabricatorPastePastePHIDType' => 'PhabricatorPHIDType',
     'PhabricatorPasteQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
     'PhabricatorPasteRawController' => 'PhabricatorPasteController',
     'PhabricatorPasteRemarkupRule' => 'PhabricatorObjectRemarkupRule',
     'PhabricatorPasteSchemaSpec' => 'PhabricatorConfigSchemaSpec',
     'PhabricatorPasteSearchEngine' => 'PhabricatorApplicationSearchEngine',
     'PhabricatorPasteSnippet' => 'Phobject',
     'PhabricatorPasteTestDataGenerator' => 'PhabricatorTestDataGenerator',
     'PhabricatorPasteTransaction' => 'PhabricatorApplicationTransaction',
     'PhabricatorPasteTransactionComment' => 'PhabricatorApplicationTransactionComment',
     'PhabricatorPasteTransactionQuery' => 'PhabricatorApplicationTransactionQuery',
     'PhabricatorPasteViewController' => 'PhabricatorPasteController',
     'PhabricatorPathSetupCheck' => 'PhabricatorSetupCheck',
     'PhabricatorPeopleAnyOwnerDatasource' => 'PhabricatorTypeaheadDatasource',
     'PhabricatorPeopleApplication' => 'PhabricatorApplication',
     'PhabricatorPeopleApproveController' => 'PhabricatorPeopleController',
     'PhabricatorPeopleCalendarController' => 'PhabricatorPeopleProfileController',
     'PhabricatorPeopleController' => 'PhabricatorController',
     'PhabricatorPeopleCreateController' => 'PhabricatorPeopleController',
     'PhabricatorPeopleDatasource' => 'PhabricatorTypeaheadDatasource',
     'PhabricatorPeopleDeleteController' => 'PhabricatorPeopleController',
     'PhabricatorPeopleDetailsProfilePanel' => 'PhabricatorProfilePanel',
     'PhabricatorPeopleDisableController' => 'PhabricatorPeopleController',
     'PhabricatorPeopleEmpowerController' => 'PhabricatorPeopleController',
     'PhabricatorPeopleExternalPHIDType' => 'PhabricatorPHIDType',
     'PhabricatorPeopleIconSet' => 'PhabricatorIconSet',
     'PhabricatorPeopleInviteController' => 'PhabricatorPeopleController',
     'PhabricatorPeopleInviteListController' => 'PhabricatorPeopleInviteController',
     'PhabricatorPeopleInviteSendController' => 'PhabricatorPeopleInviteController',
     'PhabricatorPeopleLdapController' => 'PhabricatorPeopleController',
     'PhabricatorPeopleListController' => 'PhabricatorPeopleController',
     'PhabricatorPeopleLogQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
     'PhabricatorPeopleLogSearchEngine' => 'PhabricatorApplicationSearchEngine',
     'PhabricatorPeopleLogsController' => 'PhabricatorPeopleController',
     'PhabricatorPeopleMainMenuBarExtension' => 'PhabricatorMainMenuBarExtension',
     'PhabricatorPeopleManageProfilePanel' => 'PhabricatorProfilePanel',
     'PhabricatorPeopleNewController' => 'PhabricatorPeopleController',
     'PhabricatorPeopleNoOwnerDatasource' => 'PhabricatorTypeaheadDatasource',
     'PhabricatorPeopleOwnerDatasource' => 'PhabricatorTypeaheadCompositeDatasource',
     'PhabricatorPeopleProfileController' => 'PhabricatorPeopleController',
     'PhabricatorPeopleProfileEditController' => 'PhabricatorPeopleProfileController',
     'PhabricatorPeopleProfileManageController' => 'PhabricatorPeopleProfileController',
     'PhabricatorPeopleProfilePanelEngine' => 'PhabricatorProfilePanelEngine',
     'PhabricatorPeopleProfilePictureController' => 'PhabricatorPeopleProfileController',
     'PhabricatorPeopleProfileViewController' => 'PhabricatorPeopleProfileController',
     'PhabricatorPeopleQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
     'PhabricatorPeopleRenameController' => 'PhabricatorPeopleController',
     'PhabricatorPeopleSearchEngine' => 'PhabricatorApplicationSearchEngine',
     'PhabricatorPeopleTestDataGenerator' => 'PhabricatorTestDataGenerator',
     'PhabricatorPeopleTransactionQuery' => 'PhabricatorApplicationTransactionQuery',
     'PhabricatorPeopleUserFunctionDatasource' => 'PhabricatorTypeaheadCompositeDatasource',
     'PhabricatorPeopleUserPHIDType' => 'PhabricatorPHIDType',
     'PhabricatorPeopleWelcomeController' => 'PhabricatorPeopleController',
     'PhabricatorPersonaAuthProvider' => 'PhabricatorAuthProvider',
     'PhabricatorPhabricatorAuthProvider' => 'PhabricatorOAuth2AuthProvider',
     'PhabricatorPhameApplication' => 'PhabricatorApplication',
     'PhabricatorPhameBlogPHIDType' => 'PhabricatorPHIDType',
     'PhabricatorPhamePostPHIDType' => 'PhabricatorPHIDType',
     'PhabricatorPhluxApplication' => 'PhabricatorApplication',
     'PhabricatorPholioApplication' => 'PhabricatorApplication',
     'PhabricatorPholioConfigOptions' => 'PhabricatorApplicationConfigOptions',
     'PhabricatorPholioMockTestDataGenerator' => 'PhabricatorTestDataGenerator',
     'PhabricatorPhortuneApplication' => 'PhabricatorApplication',
     'PhabricatorPhortuneContentSource' => 'PhabricatorContentSource',
     'PhabricatorPhortuneManagementInvoiceWorkflow' => 'PhabricatorPhortuneManagementWorkflow',
     'PhabricatorPhortuneManagementWorkflow' => 'PhabricatorManagementWorkflow',
     'PhabricatorPhragmentApplication' => 'PhabricatorApplication',
     'PhabricatorPhrequentApplication' => 'PhabricatorApplication',
     'PhabricatorPhrequentConfigOptions' => 'PhabricatorApplicationConfigOptions',
     'PhabricatorPhrictionApplication' => 'PhabricatorApplication',
     'PhabricatorPhrictionConfigOptions' => 'PhabricatorApplicationConfigOptions',
     'PhabricatorPhurlApplication' => 'PhabricatorApplication',
     'PhabricatorPhurlConfigOptions' => 'PhabricatorApplicationConfigOptions',
     'PhabricatorPhurlController' => 'PhabricatorController',
     'PhabricatorPhurlDAO' => 'PhabricatorLiskDAO',
     'PhabricatorPhurlLinkRemarkupRule' => 'PhutilRemarkupRule',
     'PhabricatorPhurlRemarkupRule' => 'PhabricatorObjectRemarkupRule',
     'PhabricatorPhurlSchemaSpec' => 'PhabricatorConfigSchemaSpec',
     'PhabricatorPhurlShortURLController' => 'PhabricatorPhurlController',
     'PhabricatorPhurlShortURLDefaultController' => 'PhabricatorPhurlController',
     'PhabricatorPhurlURL' => array(
       'PhabricatorPhurlDAO',
       'PhabricatorPolicyInterface',
       'PhabricatorProjectInterface',
       'PhabricatorApplicationTransactionInterface',
       'PhabricatorSubscribableInterface',
       'PhabricatorTokenReceiverInterface',
       'PhabricatorDestructibleInterface',
       'PhabricatorMentionableInterface',
       'PhabricatorFlaggableInterface',
       'PhabricatorSpacesInterface',
     ),
     'PhabricatorPhurlURLAccessController' => 'PhabricatorPhurlController',
     'PhabricatorPhurlURLCommentController' => 'PhabricatorPhurlController',
     'PhabricatorPhurlURLCreateCapability' => 'PhabricatorPolicyCapability',
     'PhabricatorPhurlURLEditController' => 'PhabricatorPhurlController',
     'PhabricatorPhurlURLEditor' => 'PhabricatorApplicationTransactionEditor',
     'PhabricatorPhurlURLListController' => 'PhabricatorPhurlController',
     'PhabricatorPhurlURLMailReceiver' => 'PhabricatorObjectMailReceiver',
     'PhabricatorPhurlURLPHIDType' => 'PhabricatorPHIDType',
     'PhabricatorPhurlURLQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
     'PhabricatorPhurlURLReplyHandler' => 'PhabricatorApplicationTransactionReplyHandler',
     'PhabricatorPhurlURLSearchEngine' => 'PhabricatorApplicationSearchEngine',
     'PhabricatorPhurlURLTransaction' => 'PhabricatorApplicationTransaction',
     'PhabricatorPhurlURLTransactionComment' => 'PhabricatorApplicationTransactionComment',
     'PhabricatorPhurlURLTransactionQuery' => 'PhabricatorApplicationTransactionQuery',
     'PhabricatorPhurlURLViewController' => 'PhabricatorPhurlController',
     'PhabricatorPirateEnglishTranslation' => 'PhutilTranslation',
     'PhabricatorPlatformSite' => 'PhabricatorSite',
     'PhabricatorPointsEditField' => 'PhabricatorEditField',
     'PhabricatorPolicies' => 'PhabricatorPolicyConstants',
     'PhabricatorPolicy' => array(
       'PhabricatorPolicyDAO',
       'PhabricatorPolicyInterface',
       'PhabricatorDestructibleInterface',
     ),
     'PhabricatorPolicyApplication' => 'PhabricatorApplication',
     'PhabricatorPolicyAwareQuery' => 'PhabricatorOffsetPagedQuery',
     'PhabricatorPolicyAwareTestQuery' => 'PhabricatorPolicyAwareQuery',
     'PhabricatorPolicyCanEditCapability' => 'PhabricatorPolicyCapability',
     'PhabricatorPolicyCanJoinCapability' => 'PhabricatorPolicyCapability',
     'PhabricatorPolicyCanViewCapability' => 'PhabricatorPolicyCapability',
     'PhabricatorPolicyCapability' => 'Phobject',
     'PhabricatorPolicyCapabilityTestCase' => 'PhabricatorTestCase',
     'PhabricatorPolicyConfigOptions' => 'PhabricatorApplicationConfigOptions',
     'PhabricatorPolicyConstants' => 'Phobject',
     'PhabricatorPolicyController' => 'PhabricatorController',
     'PhabricatorPolicyDAO' => 'PhabricatorLiskDAO',
     'PhabricatorPolicyDataTestCase' => 'PhabricatorTestCase',
     'PhabricatorPolicyEditController' => 'PhabricatorPolicyController',
     'PhabricatorPolicyEditEngineExtension' => 'PhabricatorEditEngineExtension',
     'PhabricatorPolicyEditField' => 'PhabricatorEditField',
     'PhabricatorPolicyException' => 'Exception',
     'PhabricatorPolicyExplainController' => 'PhabricatorPolicyController',
     'PhabricatorPolicyFilter' => 'Phobject',
     'PhabricatorPolicyInterface' => 'PhabricatorPHIDInterface',
     'PhabricatorPolicyManagementShowWorkflow' => 'PhabricatorPolicyManagementWorkflow',
     'PhabricatorPolicyManagementUnlockWorkflow' => 'PhabricatorPolicyManagementWorkflow',
     'PhabricatorPolicyManagementWorkflow' => 'PhabricatorManagementWorkflow',
     'PhabricatorPolicyPHIDTypePolicy' => 'PhabricatorPHIDType',
     'PhabricatorPolicyQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
     'PhabricatorPolicyRequestExceptionHandler' => 'PhabricatorRequestExceptionHandler',
     'PhabricatorPolicyRule' => 'Phobject',
     'PhabricatorPolicySearchEngineExtension' => 'PhabricatorSearchEngineExtension',
     'PhabricatorPolicyTestCase' => 'PhabricatorTestCase',
     'PhabricatorPolicyTestObject' => array(
       'Phobject',
       'PhabricatorPolicyInterface',
       'PhabricatorExtendedPolicyInterface',
     ),
     'PhabricatorPolicyType' => 'PhabricatorPolicyConstants',
     'PhabricatorPonderApplication' => 'PhabricatorApplication',
     'PhabricatorProfilePanel' => 'Phobject',
     'PhabricatorProfilePanelConfiguration' => array(
       'PhabricatorSearchDAO',
       'PhabricatorPolicyInterface',
       'PhabricatorExtendedPolicyInterface',
       'PhabricatorApplicationTransactionInterface',
     ),
     'PhabricatorProfilePanelConfigurationQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
     'PhabricatorProfilePanelConfigurationTransaction' => 'PhabricatorApplicationTransaction',
     'PhabricatorProfilePanelConfigurationTransactionQuery' => 'PhabricatorApplicationTransactionQuery',
     'PhabricatorProfilePanelEditEngine' => 'PhabricatorEditEngine',
     'PhabricatorProfilePanelEditor' => 'PhabricatorApplicationTransactionEditor',
     'PhabricatorProfilePanelEngine' => 'Phobject',
     'PhabricatorProfilePanelIconSet' => 'PhabricatorIconSet',
     'PhabricatorProfilePanelPHIDType' => 'PhabricatorPHIDType',
     'PhabricatorProject' => array(
       'PhabricatorProjectDAO',
       'PhabricatorApplicationTransactionInterface',
       'PhabricatorFlaggableInterface',
       'PhabricatorPolicyInterface',
       'PhabricatorExtendedPolicyInterface',
       'PhabricatorCustomFieldInterface',
       'PhabricatorDestructibleInterface',
       'PhabricatorFulltextInterface',
       'PhabricatorConduitResultInterface',
       'PhabricatorColumnProxyInterface',
     ),
     'PhabricatorProjectAddHeraldAction' => 'PhabricatorProjectHeraldAction',
     'PhabricatorProjectApplication' => 'PhabricatorApplication',
     'PhabricatorProjectArchiveController' => 'PhabricatorProjectController',
     'PhabricatorProjectBoardBackgroundController' => 'PhabricatorProjectBoardController',
     'PhabricatorProjectBoardController' => 'PhabricatorProjectController',
     'PhabricatorProjectBoardDisableController' => 'PhabricatorProjectBoardController',
     'PhabricatorProjectBoardImportController' => 'PhabricatorProjectBoardController',
     'PhabricatorProjectBoardManageController' => 'PhabricatorProjectBoardController',
     'PhabricatorProjectBoardReorderController' => 'PhabricatorProjectBoardController',
     'PhabricatorProjectBoardViewController' => 'PhabricatorProjectBoardController',
     'PhabricatorProjectCardView' => 'AphrontTagView',
     'PhabricatorProjectColorsConfigOptionType' => 'PhabricatorConfigJSONOptionType',
     'PhabricatorProjectColumn' => array(
       'PhabricatorProjectDAO',
       'PhabricatorApplicationTransactionInterface',
       'PhabricatorPolicyInterface',
       'PhabricatorDestructibleInterface',
       'PhabricatorExtendedPolicyInterface',
     ),
     'PhabricatorProjectColumnDetailController' => 'PhabricatorProjectBoardController',
     'PhabricatorProjectColumnEditController' => 'PhabricatorProjectBoardController',
     'PhabricatorProjectColumnHideController' => 'PhabricatorProjectBoardController',
     'PhabricatorProjectColumnPHIDType' => 'PhabricatorPHIDType',
     'PhabricatorProjectColumnPosition' => array(
       'PhabricatorProjectDAO',
       'PhabricatorPolicyInterface',
     ),
     'PhabricatorProjectColumnPositionQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
     'PhabricatorProjectColumnQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
     'PhabricatorProjectColumnTransaction' => 'PhabricatorApplicationTransaction',
     'PhabricatorProjectColumnTransactionEditor' => 'PhabricatorApplicationTransactionEditor',
     'PhabricatorProjectColumnTransactionQuery' => 'PhabricatorApplicationTransactionQuery',
     'PhabricatorProjectConfigOptions' => 'PhabricatorApplicationConfigOptions',
     'PhabricatorProjectConfiguredCustomField' => array(
       'PhabricatorProjectStandardCustomField',
       'PhabricatorStandardCustomFieldInterface',
     ),
     'PhabricatorProjectController' => 'PhabricatorController',
     'PhabricatorProjectCoreTestCase' => 'PhabricatorTestCase',
     'PhabricatorProjectCoverController' => 'PhabricatorProjectController',
     'PhabricatorProjectCustomField' => 'PhabricatorCustomField',
     'PhabricatorProjectCustomFieldNumericIndex' => 'PhabricatorCustomFieldNumericIndexStorage',
     'PhabricatorProjectCustomFieldStorage' => 'PhabricatorCustomFieldStorage',
     'PhabricatorProjectCustomFieldStringIndex' => 'PhabricatorCustomFieldStringIndexStorage',
     'PhabricatorProjectDAO' => 'PhabricatorLiskDAO',
     'PhabricatorProjectDatasource' => 'PhabricatorTypeaheadDatasource',
     'PhabricatorProjectDefaultController' => 'PhabricatorProjectBoardController',
     'PhabricatorProjectDescriptionField' => 'PhabricatorProjectStandardCustomField',
     'PhabricatorProjectDetailsProfilePanel' => 'PhabricatorProfilePanel',
     'PhabricatorProjectEditController' => 'PhabricatorProjectController',
     'PhabricatorProjectEditEngine' => 'PhabricatorEditEngine',
     'PhabricatorProjectEditPictureController' => 'PhabricatorProjectController',
     'PhabricatorProjectFulltextEngine' => 'PhabricatorFulltextEngine',
     'PhabricatorProjectHeraldAction' => 'HeraldAction',
     'PhabricatorProjectHeraldAdapter' => 'HeraldAdapter',
     'PhabricatorProjectHeraldFieldGroup' => 'HeraldFieldGroup',
     'PhabricatorProjectHovercardEngineExtension' => 'PhabricatorHovercardEngineExtension',
     'PhabricatorProjectIconSet' => 'PhabricatorIconSet',
     'PhabricatorProjectIconsConfigOptionType' => 'PhabricatorConfigJSONOptionType',
     'PhabricatorProjectListController' => 'PhabricatorProjectController',
     'PhabricatorProjectListView' => 'AphrontView',
     'PhabricatorProjectLockController' => 'PhabricatorProjectController',
     'PhabricatorProjectLogicalAncestorDatasource' => 'PhabricatorTypeaheadCompositeDatasource',
     'PhabricatorProjectLogicalDatasource' => 'PhabricatorTypeaheadCompositeDatasource',
     'PhabricatorProjectLogicalOrNotDatasource' => 'PhabricatorTypeaheadCompositeDatasource',
     'PhabricatorProjectLogicalUserDatasource' => 'PhabricatorTypeaheadCompositeDatasource',
     'PhabricatorProjectLogicalViewerDatasource' => 'PhabricatorTypeaheadDatasource',
     'PhabricatorProjectManageController' => 'PhabricatorProjectController',
     'PhabricatorProjectManageProfilePanel' => 'PhabricatorProfilePanel',
     'PhabricatorProjectMaterializedMemberEdgeType' => 'PhabricatorEdgeType',
     'PhabricatorProjectMemberListView' => 'PhabricatorProjectUserListView',
     'PhabricatorProjectMemberOfProjectEdgeType' => 'PhabricatorEdgeType',
     'PhabricatorProjectMembersAddController' => 'PhabricatorProjectController',
     'PhabricatorProjectMembersDatasource' => 'PhabricatorTypeaheadCompositeDatasource',
     'PhabricatorProjectMembersPolicyRule' => 'PhabricatorPolicyRule',
     'PhabricatorProjectMembersProfilePanel' => 'PhabricatorProfilePanel',
     'PhabricatorProjectMembersRemoveController' => 'PhabricatorProjectController',
     'PhabricatorProjectMembersViewController' => 'PhabricatorProjectController',
     'PhabricatorProjectMoveController' => 'PhabricatorProjectController',
     'PhabricatorProjectNameContextFreeGrammar' => 'PhutilContextFreeGrammar',
     'PhabricatorProjectNoProjectsDatasource' => 'PhabricatorTypeaheadDatasource',
     'PhabricatorProjectObjectHasProjectEdgeType' => 'PhabricatorEdgeType',
     'PhabricatorProjectOrUserDatasource' => 'PhabricatorTypeaheadCompositeDatasource',
     'PhabricatorProjectOrUserFunctionDatasource' => 'PhabricatorTypeaheadCompositeDatasource',
     'PhabricatorProjectPHIDResolver' => 'PhabricatorPHIDResolver',
     'PhabricatorProjectPanelController' => 'PhabricatorProjectController',
     'PhabricatorProjectPointsProfilePanel' => 'PhabricatorProfilePanel',
     'PhabricatorProjectProfileController' => 'PhabricatorProjectController',
     'PhabricatorProjectProfilePanelEngine' => 'PhabricatorProfilePanelEngine',
     'PhabricatorProjectProjectHasMemberEdgeType' => 'PhabricatorEdgeType',
     'PhabricatorProjectProjectHasObjectEdgeType' => 'PhabricatorEdgeType',
     'PhabricatorProjectProjectPHIDType' => 'PhabricatorPHIDType',
     'PhabricatorProjectQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
     'PhabricatorProjectRemoveHeraldAction' => 'PhabricatorProjectHeraldAction',
     'PhabricatorProjectSchemaSpec' => 'PhabricatorConfigSchemaSpec',
     'PhabricatorProjectSearchEngine' => 'PhabricatorApplicationSearchEngine',
     'PhabricatorProjectSearchField' => 'PhabricatorSearchTokenizerField',
     'PhabricatorProjectSilenceController' => 'PhabricatorProjectController',
     'PhabricatorProjectSilencedEdgeType' => 'PhabricatorEdgeType',
     'PhabricatorProjectSlug' => 'PhabricatorProjectDAO',
     'PhabricatorProjectStandardCustomField' => array(
       'PhabricatorProjectCustomField',
       'PhabricatorStandardCustomFieldInterface',
     ),
     'PhabricatorProjectStatus' => 'Phobject',
     'PhabricatorProjectSubprojectWarningController' => 'PhabricatorProjectController',
     'PhabricatorProjectSubprojectsController' => 'PhabricatorProjectController',
     'PhabricatorProjectSubprojectsProfilePanel' => 'PhabricatorProfilePanel',
     'PhabricatorProjectTestDataGenerator' => 'PhabricatorTestDataGenerator',
     'PhabricatorProjectTransaction' => 'PhabricatorApplicationTransaction',
     'PhabricatorProjectTransactionEditor' => 'PhabricatorApplicationTransactionEditor',
     'PhabricatorProjectTransactionQuery' => 'PhabricatorApplicationTransactionQuery',
     'PhabricatorProjectUIEventListener' => 'PhabricatorEventListener',
     'PhabricatorProjectUpdateController' => 'PhabricatorProjectController',
     'PhabricatorProjectUserFunctionDatasource' => 'PhabricatorTypeaheadCompositeDatasource',
     'PhabricatorProjectUserListView' => 'AphrontView',
     'PhabricatorProjectViewController' => 'PhabricatorProjectController',
     'PhabricatorProjectWatchController' => 'PhabricatorProjectController',
     'PhabricatorProjectWatcherListView' => 'PhabricatorProjectUserListView',
     'PhabricatorProjectWorkboardBackgroundColor' => 'Phobject',
     'PhabricatorProjectWorkboardProfilePanel' => 'PhabricatorProfilePanel',
     'PhabricatorProjectsCurtainExtension' => 'PHUICurtainExtension',
     'PhabricatorProjectsEditEngineExtension' => 'PhabricatorEditEngineExtension',
     'PhabricatorProjectsEditField' => 'PhabricatorTokenizerEditField',
     'PhabricatorProjectsFulltextEngineExtension' => 'PhabricatorFulltextEngineExtension',
     'PhabricatorProjectsMembersSearchEngineAttachment' => 'PhabricatorSearchEngineAttachment',
     'PhabricatorProjectsMembershipIndexEngineExtension' => 'PhabricatorIndexEngineExtension',
     'PhabricatorProjectsPolicyRule' => 'PhabricatorPolicyRule',
     'PhabricatorProjectsSearchEngineAttachment' => 'PhabricatorSearchEngineAttachment',
     'PhabricatorProjectsSearchEngineExtension' => 'PhabricatorSearchEngineExtension',
     'PhabricatorProjectsWatchersSearchEngineAttachment' => 'PhabricatorSearchEngineAttachment',
     'PhabricatorProtocolAdapter' => 'Phobject',
     'PhabricatorPygmentSetupCheck' => 'PhabricatorSetupCheck',
     'PhabricatorQuery' => 'Phobject',
     'PhabricatorQueryConstraint' => 'Phobject',
     'PhabricatorQueryOrderItem' => 'Phobject',
     'PhabricatorQueryOrderTestCase' => 'PhabricatorTestCase',
     'PhabricatorQueryOrderVector' => array(
       'Phobject',
       'Iterator',
     ),
     'PhabricatorRateLimitRequestExceptionHandler' => 'PhabricatorRequestExceptionHandler',
     'PhabricatorRecaptchaConfigOptions' => 'PhabricatorApplicationConfigOptions',
     'PhabricatorRecipientHasBadgeEdgeType' => 'PhabricatorEdgeType',
     'PhabricatorRedirectController' => 'PhabricatorController',
     'PhabricatorRefreshCSRFController' => 'PhabricatorAuthController',
     'PhabricatorRegistrationProfile' => 'Phobject',
     'PhabricatorReleephApplication' => 'PhabricatorApplication',
     'PhabricatorReleephApplicationConfigOptions' => 'PhabricatorApplicationConfigOptions',
     'PhabricatorRemarkupControl' => 'AphrontFormTextAreaControl',
     'PhabricatorRemarkupCowsayBlockInterpreter' => 'PhutilRemarkupBlockInterpreter',
     'PhabricatorRemarkupCustomBlockRule' => 'PhutilRemarkupBlockRule',
     'PhabricatorRemarkupCustomInlineRule' => 'PhutilRemarkupRule',
     'PhabricatorRemarkupEditField' => 'PhabricatorEditField',
     'PhabricatorRemarkupFigletBlockInterpreter' => 'PhutilRemarkupBlockInterpreter',
     'PhabricatorRemarkupUIExample' => 'PhabricatorUIExample',
     'PhabricatorRepositoriesSetupCheck' => 'PhabricatorSetupCheck',
     'PhabricatorRepository' => array(
       'PhabricatorRepositoryDAO',
       'PhabricatorApplicationTransactionInterface',
       'PhabricatorPolicyInterface',
       'PhabricatorFlaggableInterface',
       'PhabricatorMarkupInterface',
       'PhabricatorDestructibleInterface',
       'PhabricatorProjectInterface',
       'PhabricatorSpacesInterface',
       'PhabricatorConduitResultInterface',
     ),
     'PhabricatorRepositoryAuditRequest' => array(
       'PhabricatorRepositoryDAO',
       'PhabricatorPolicyInterface',
     ),
     'PhabricatorRepositoryBranch' => 'PhabricatorRepositoryDAO',
     'PhabricatorRepositoryCommit' => array(
       'PhabricatorRepositoryDAO',
       'PhabricatorPolicyInterface',
       'PhabricatorFlaggableInterface',
       'PhabricatorProjectInterface',
       'PhabricatorTokenReceiverInterface',
       'PhabricatorSubscribableInterface',
       'PhabricatorMentionableInterface',
       'HarbormasterBuildableInterface',
       'HarbormasterCircleCIBuildableInterface',
       'PhabricatorCustomFieldInterface',
       'PhabricatorApplicationTransactionInterface',
       'PhabricatorFulltextInterface',
     ),
     'PhabricatorRepositoryCommitChangeParserWorker' => 'PhabricatorRepositoryCommitParserWorker',
     'PhabricatorRepositoryCommitData' => 'PhabricatorRepositoryDAO',
     'PhabricatorRepositoryCommitHeraldWorker' => 'PhabricatorRepositoryCommitParserWorker',
     'PhabricatorRepositoryCommitMessageParserWorker' => 'PhabricatorRepositoryCommitParserWorker',
     'PhabricatorRepositoryCommitOwnersWorker' => 'PhabricatorRepositoryCommitParserWorker',
     'PhabricatorRepositoryCommitPHIDType' => 'PhabricatorPHIDType',
     'PhabricatorRepositoryCommitParserWorker' => 'PhabricatorWorker',
     'PhabricatorRepositoryCommitRef' => 'Phobject',
     'PhabricatorRepositoryConfigOptions' => 'PhabricatorApplicationConfigOptions',
     'PhabricatorRepositoryDAO' => 'PhabricatorLiskDAO',
     'PhabricatorRepositoryDiscoveryEngine' => 'PhabricatorRepositoryEngine',
     'PhabricatorRepositoryEditor' => 'PhabricatorApplicationTransactionEditor',
     'PhabricatorRepositoryEngine' => 'Phobject',
     'PhabricatorRepositoryGitCommitChangeParserWorker' => 'PhabricatorRepositoryCommitChangeParserWorker',
     'PhabricatorRepositoryGitCommitMessageParserWorker' => 'PhabricatorRepositoryCommitMessageParserWorker',
     'PhabricatorRepositoryGitLFSRef' => array(
       'PhabricatorRepositoryDAO',
       'PhabricatorPolicyInterface',
       'PhabricatorDestructibleInterface',
     ),
     'PhabricatorRepositoryGitLFSRefQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
     'PhabricatorRepositoryGraphCache' => 'Phobject',
     'PhabricatorRepositoryGraphStream' => 'Phobject',
     'PhabricatorRepositoryManagementCacheWorkflow' => 'PhabricatorRepositoryManagementWorkflow',
     'PhabricatorRepositoryManagementClusterizeWorkflow' => 'PhabricatorRepositoryManagementWorkflow',
     'PhabricatorRepositoryManagementDiscoverWorkflow' => 'PhabricatorRepositoryManagementWorkflow',
-    'PhabricatorRepositoryManagementEditWorkflow' => 'PhabricatorRepositoryManagementWorkflow',
     'PhabricatorRepositoryManagementImportingWorkflow' => 'PhabricatorRepositoryManagementWorkflow',
     'PhabricatorRepositoryManagementListPathsWorkflow' => 'PhabricatorRepositoryManagementWorkflow',
     'PhabricatorRepositoryManagementListWorkflow' => 'PhabricatorRepositoryManagementWorkflow',
     'PhabricatorRepositoryManagementLookupUsersWorkflow' => 'PhabricatorRepositoryManagementWorkflow',
     'PhabricatorRepositoryManagementMarkImportedWorkflow' => 'PhabricatorRepositoryManagementWorkflow',
     'PhabricatorRepositoryManagementMirrorWorkflow' => 'PhabricatorRepositoryManagementWorkflow',
     'PhabricatorRepositoryManagementMovePathsWorkflow' => 'PhabricatorRepositoryManagementWorkflow',
     'PhabricatorRepositoryManagementParentsWorkflow' => 'PhabricatorRepositoryManagementWorkflow',
     'PhabricatorRepositoryManagementPullWorkflow' => 'PhabricatorRepositoryManagementWorkflow',
     'PhabricatorRepositoryManagementRefsWorkflow' => 'PhabricatorRepositoryManagementWorkflow',
     'PhabricatorRepositoryManagementReparseWorkflow' => 'PhabricatorRepositoryManagementWorkflow',
     'PhabricatorRepositoryManagementThawWorkflow' => 'PhabricatorRepositoryManagementWorkflow',
     'PhabricatorRepositoryManagementUpdateWorkflow' => 'PhabricatorRepositoryManagementWorkflow',
     'PhabricatorRepositoryManagementWorkflow' => 'PhabricatorManagementWorkflow',
     'PhabricatorRepositoryMercurialCommitChangeParserWorker' => 'PhabricatorRepositoryCommitChangeParserWorker',
     'PhabricatorRepositoryMercurialCommitMessageParserWorker' => 'PhabricatorRepositoryCommitMessageParserWorker',
-    'PhabricatorRepositoryMirror' => array(
-      'PhabricatorRepositoryDAO',
-      'PhabricatorPolicyInterface',
-    ),
+    'PhabricatorRepositoryMirror' => 'PhabricatorRepositoryDAO',
     'PhabricatorRepositoryMirrorEngine' => 'PhabricatorRepositoryEngine',
-    'PhabricatorRepositoryMirrorPHIDType' => 'PhabricatorPHIDType',
-    'PhabricatorRepositoryMirrorQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
     'PhabricatorRepositoryParsedChange' => 'Phobject',
     'PhabricatorRepositoryPullEngine' => 'PhabricatorRepositoryEngine',
     'PhabricatorRepositoryPullEvent' => array(
       'PhabricatorRepositoryDAO',
       'PhabricatorPolicyInterface',
     ),
     'PhabricatorRepositoryPullEventPHIDType' => 'PhabricatorPHIDType',
     'PhabricatorRepositoryPullEventQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
     'PhabricatorRepositoryPullLocalDaemon' => 'PhabricatorDaemon',
     'PhabricatorRepositoryPushEvent' => array(
       'PhabricatorRepositoryDAO',
       'PhabricatorPolicyInterface',
     ),
     'PhabricatorRepositoryPushEventPHIDType' => 'PhabricatorPHIDType',
     'PhabricatorRepositoryPushEventQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
     'PhabricatorRepositoryPushLog' => array(
       'PhabricatorRepositoryDAO',
       'PhabricatorPolicyInterface',
     ),
     'PhabricatorRepositoryPushLogPHIDType' => 'PhabricatorPHIDType',
     'PhabricatorRepositoryPushLogQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
     'PhabricatorRepositoryPushLogSearchEngine' => 'PhabricatorApplicationSearchEngine',
     'PhabricatorRepositoryPushMailWorker' => 'PhabricatorWorker',
     'PhabricatorRepositoryPushReplyHandler' => 'PhabricatorMailReplyHandler',
     'PhabricatorRepositoryQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
     'PhabricatorRepositoryRefCursor' => array(
       'PhabricatorRepositoryDAO',
       'PhabricatorPolicyInterface',
     ),
     'PhabricatorRepositoryRefCursorPHIDType' => 'PhabricatorPHIDType',
     'PhabricatorRepositoryRefCursorQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
     'PhabricatorRepositoryRefEngine' => 'PhabricatorRepositoryEngine',
     'PhabricatorRepositoryRepositoryPHIDType' => 'PhabricatorPHIDType',
     'PhabricatorRepositorySchemaSpec' => 'PhabricatorConfigSchemaSpec',
     'PhabricatorRepositorySearchEngine' => 'PhabricatorApplicationSearchEngine',
     'PhabricatorRepositoryStatusMessage' => 'PhabricatorRepositoryDAO',
     'PhabricatorRepositorySvnCommitChangeParserWorker' => 'PhabricatorRepositoryCommitChangeParserWorker',
     'PhabricatorRepositorySvnCommitMessageParserWorker' => 'PhabricatorRepositoryCommitMessageParserWorker',
     'PhabricatorRepositorySymbol' => 'PhabricatorRepositoryDAO',
     'PhabricatorRepositoryTestCase' => 'PhabricatorTestCase',
     'PhabricatorRepositoryTransaction' => 'PhabricatorApplicationTransaction',
     'PhabricatorRepositoryTransactionQuery' => 'PhabricatorApplicationTransactionQuery',
     'PhabricatorRepositoryType' => 'Phobject',
     'PhabricatorRepositoryURI' => array(
       'PhabricatorRepositoryDAO',
       'PhabricatorApplicationTransactionInterface',
       'PhabricatorPolicyInterface',
       'PhabricatorExtendedPolicyInterface',
       'PhabricatorConduitResultInterface',
     ),
     'PhabricatorRepositoryURIIndex' => 'PhabricatorRepositoryDAO',
     'PhabricatorRepositoryURINormalizer' => 'Phobject',
     'PhabricatorRepositoryURINormalizerTestCase' => 'PhabricatorTestCase',
     'PhabricatorRepositoryURIPHIDType' => 'PhabricatorPHIDType',
     'PhabricatorRepositoryURIQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
     'PhabricatorRepositoryURITestCase' => 'PhabricatorTestCase',
     'PhabricatorRepositoryURITransaction' => 'PhabricatorApplicationTransaction',
     'PhabricatorRepositoryURITransactionQuery' => 'PhabricatorApplicationTransactionQuery',
     'PhabricatorRepositoryVCSPassword' => 'PhabricatorRepositoryDAO',
     'PhabricatorRepositoryVersion' => 'Phobject',
     'PhabricatorRepositoryWorkingCopyVersion' => 'PhabricatorRepositoryDAO',
     'PhabricatorRequestExceptionHandler' => 'AphrontRequestExceptionHandler',
     'PhabricatorResourceSite' => 'PhabricatorSite',
     'PhabricatorRobotsController' => 'PhabricatorController',
     'PhabricatorS3FileStorageEngine' => 'PhabricatorFileStorageEngine',
     'PhabricatorSMS' => 'PhabricatorSMSDAO',
     'PhabricatorSMSConfigOptions' => 'PhabricatorApplicationConfigOptions',
     'PhabricatorSMSDAO' => 'PhabricatorLiskDAO',
     'PhabricatorSMSDemultiplexWorker' => 'PhabricatorSMSWorker',
     'PhabricatorSMSImplementationAdapter' => 'Phobject',
     'PhabricatorSMSImplementationTestBlackholeAdapter' => 'PhabricatorSMSImplementationAdapter',
     'PhabricatorSMSImplementationTwilioAdapter' => 'PhabricatorSMSImplementationAdapter',
     'PhabricatorSMSManagementListOutboundWorkflow' => 'PhabricatorSMSManagementWorkflow',
     'PhabricatorSMSManagementSendTestWorkflow' => 'PhabricatorSMSManagementWorkflow',
     'PhabricatorSMSManagementShowOutboundWorkflow' => 'PhabricatorSMSManagementWorkflow',
     'PhabricatorSMSManagementWorkflow' => 'PhabricatorManagementWorkflow',
     'PhabricatorSMSSendWorker' => 'PhabricatorSMSWorker',
     'PhabricatorSMSWorker' => 'PhabricatorWorker',
     'PhabricatorSQLPatchList' => 'Phobject',
     'PhabricatorSSHKeyGenerator' => 'Phobject',
     'PhabricatorSSHKeysSettingsPanel' => 'PhabricatorSettingsPanel',
     'PhabricatorSSHLog' => 'Phobject',
     'PhabricatorSSHPassthruCommand' => 'Phobject',
     'PhabricatorSSHWorkflow' => 'PhabricatorManagementWorkflow',
     'PhabricatorSavedQuery' => array(
       'PhabricatorSearchDAO',
       'PhabricatorPolicyInterface',
     ),
     'PhabricatorSavedQueryQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
     'PhabricatorScheduleTaskTriggerAction' => 'PhabricatorTriggerAction',
     'PhabricatorScopedEnv' => 'Phobject',
     'PhabricatorSearchAbstractDocument' => 'Phobject',
     'PhabricatorSearchApplication' => 'PhabricatorApplication',
     'PhabricatorSearchApplicationSearchEngine' => 'PhabricatorApplicationSearchEngine',
     'PhabricatorSearchApplicationStorageEnginePanel' => 'PhabricatorApplicationConfigurationPanel',
     'PhabricatorSearchAttachController' => 'PhabricatorSearchBaseController',
     'PhabricatorSearchBaseController' => 'PhabricatorController',
     'PhabricatorSearchCheckboxesField' => 'PhabricatorSearchField',
     'PhabricatorSearchConfigOptions' => 'PhabricatorApplicationConfigOptions',
     'PhabricatorSearchController' => 'PhabricatorSearchBaseController',
     'PhabricatorSearchCustomFieldProxyField' => 'PhabricatorSearchField',
     'PhabricatorSearchDAO' => 'PhabricatorLiskDAO',
     'PhabricatorSearchDatasource' => 'PhabricatorTypeaheadCompositeDatasource',
     'PhabricatorSearchDatasourceField' => 'PhabricatorSearchTokenizerField',
     'PhabricatorSearchDateControlField' => 'PhabricatorSearchField',
     'PhabricatorSearchDateField' => 'PhabricatorSearchField',
     'PhabricatorSearchDeleteController' => 'PhabricatorSearchBaseController',
     'PhabricatorSearchDocument' => 'PhabricatorSearchDAO',
     'PhabricatorSearchDocumentField' => 'PhabricatorSearchDAO',
     'PhabricatorSearchDocumentFieldType' => 'Phobject',
     'PhabricatorSearchDocumentQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
     'PhabricatorSearchDocumentRelationship' => 'PhabricatorSearchDAO',
     'PhabricatorSearchDocumentTypeDatasource' => 'PhabricatorTypeaheadDatasource',
     'PhabricatorSearchEditController' => 'PhabricatorSearchBaseController',
     'PhabricatorSearchEngineAPIMethod' => 'ConduitAPIMethod',
     'PhabricatorSearchEngineAttachment' => 'Phobject',
     'PhabricatorSearchEngineExtension' => 'Phobject',
     'PhabricatorSearchEngineExtensionModule' => 'PhabricatorConfigModule',
     'PhabricatorSearchEngineTestCase' => 'PhabricatorTestCase',
     'PhabricatorSearchField' => 'Phobject',
     'PhabricatorSearchHovercardController' => 'PhabricatorSearchBaseController',
     'PhabricatorSearchIndexVersion' => 'PhabricatorSearchDAO',
     'PhabricatorSearchIndexVersionDestructionEngineExtension' => 'PhabricatorDestructionEngineExtension',
     'PhabricatorSearchManagementIndexWorkflow' => 'PhabricatorSearchManagementWorkflow',
     'PhabricatorSearchManagementInitWorkflow' => 'PhabricatorSearchManagementWorkflow',
     'PhabricatorSearchManagementWorkflow' => 'PhabricatorManagementWorkflow',
     'PhabricatorSearchNgrams' => 'PhabricatorSearchDAO',
     'PhabricatorSearchNgramsDestructionEngineExtension' => 'PhabricatorDestructionEngineExtension',
     'PhabricatorSearchOrderController' => 'PhabricatorSearchBaseController',
     'PhabricatorSearchOrderField' => 'PhabricatorSearchField',
     'PhabricatorSearchPreferencesSettingsPanel' => 'PhabricatorSettingsPanel',
     'PhabricatorSearchRelationship' => 'Phobject',
     'PhabricatorSearchResultView' => 'AphrontView',
     'PhabricatorSearchSchemaSpec' => 'PhabricatorConfigSchemaSpec',
     'PhabricatorSearchSelectController' => 'PhabricatorSearchBaseController',
     'PhabricatorSearchSelectField' => 'PhabricatorSearchField',
     'PhabricatorSearchStringListField' => 'PhabricatorSearchField',
     'PhabricatorSearchSubscribersField' => 'PhabricatorSearchTokenizerField',
     'PhabricatorSearchTextField' => 'PhabricatorSearchField',
     'PhabricatorSearchThreeStateField' => 'PhabricatorSearchField',
     'PhabricatorSearchTokenizerField' => 'PhabricatorSearchField',
     'PhabricatorSearchWorker' => 'PhabricatorWorker',
     'PhabricatorSecurityConfigOptions' => 'PhabricatorApplicationConfigOptions',
     'PhabricatorSecuritySetupCheck' => 'PhabricatorSetupCheck',
     'PhabricatorSelectEditField' => 'PhabricatorEditField',
     'PhabricatorSendGridConfigOptions' => 'PhabricatorApplicationConfigOptions',
     'PhabricatorSessionsSettingsPanel' => 'PhabricatorSettingsPanel',
     'PhabricatorSettingsAddEmailAction' => 'PhabricatorSystemAction',
     'PhabricatorSettingsAdjustController' => 'PhabricatorController',
     'PhabricatorSettingsApplication' => 'PhabricatorApplication',
     'PhabricatorSettingsMainController' => 'PhabricatorController',
     'PhabricatorSettingsMainMenuBarExtension' => 'PhabricatorMainMenuBarExtension',
     'PhabricatorSettingsPanel' => 'Phobject',
     'PhabricatorSetupCheck' => 'Phobject',
     'PhabricatorSetupCheckTestCase' => 'PhabricatorTestCase',
     'PhabricatorSetupIssue' => 'Phobject',
     'PhabricatorSetupIssueUIExample' => 'PhabricatorUIExample',
     'PhabricatorSetupIssueView' => 'AphrontView',
     'PhabricatorShortSite' => 'PhabricatorSite',
     'PhabricatorSimpleEditType' => 'PhabricatorEditType',
     'PhabricatorSite' => 'AphrontSite',
     'PhabricatorSlowvoteApplication' => 'PhabricatorApplication',
     'PhabricatorSlowvoteChoice' => 'PhabricatorSlowvoteDAO',
     'PhabricatorSlowvoteCloseController' => 'PhabricatorSlowvoteController',
     'PhabricatorSlowvoteCommentController' => 'PhabricatorSlowvoteController',
     'PhabricatorSlowvoteController' => 'PhabricatorController',
     'PhabricatorSlowvoteDAO' => 'PhabricatorLiskDAO',
     'PhabricatorSlowvoteDefaultViewCapability' => 'PhabricatorPolicyCapability',
     'PhabricatorSlowvoteEditController' => 'PhabricatorSlowvoteController',
     'PhabricatorSlowvoteEditor' => 'PhabricatorApplicationTransactionEditor',
     'PhabricatorSlowvoteListController' => 'PhabricatorSlowvoteController',
     'PhabricatorSlowvoteMailReceiver' => 'PhabricatorObjectMailReceiver',
     'PhabricatorSlowvoteOption' => 'PhabricatorSlowvoteDAO',
     'PhabricatorSlowvotePoll' => array(
       'PhabricatorSlowvoteDAO',
       'PhabricatorApplicationTransactionInterface',
       'PhabricatorPolicyInterface',
       'PhabricatorSubscribableInterface',
       'PhabricatorFlaggableInterface',
       'PhabricatorTokenReceiverInterface',
       'PhabricatorProjectInterface',
       'PhabricatorDestructibleInterface',
       'PhabricatorSpacesInterface',
     ),
     'PhabricatorSlowvotePollController' => 'PhabricatorSlowvoteController',
     'PhabricatorSlowvotePollPHIDType' => 'PhabricatorPHIDType',
     'PhabricatorSlowvoteQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
     'PhabricatorSlowvoteReplyHandler' => 'PhabricatorApplicationTransactionReplyHandler',
     'PhabricatorSlowvoteSchemaSpec' => 'PhabricatorConfigSchemaSpec',
     'PhabricatorSlowvoteSearchEngine' => 'PhabricatorApplicationSearchEngine',
     'PhabricatorSlowvoteTransaction' => 'PhabricatorApplicationTransaction',
     'PhabricatorSlowvoteTransactionComment' => 'PhabricatorApplicationTransactionComment',
     'PhabricatorSlowvoteTransactionQuery' => 'PhabricatorApplicationTransactionQuery',
     'PhabricatorSlowvoteVoteController' => 'PhabricatorSlowvoteController',
     'PhabricatorSlug' => 'Phobject',
     'PhabricatorSlugTestCase' => 'PhabricatorTestCase',
     'PhabricatorSortTableUIExample' => 'PhabricatorUIExample',
     'PhabricatorSourceCodeView' => 'AphrontView',
     'PhabricatorSpaceEditField' => 'PhabricatorEditField',
     'PhabricatorSpacesApplication' => 'PhabricatorApplication',
     'PhabricatorSpacesArchiveController' => 'PhabricatorSpacesController',
     'PhabricatorSpacesCapabilityCreateSpaces' => 'PhabricatorPolicyCapability',
     'PhabricatorSpacesCapabilityDefaultEdit' => 'PhabricatorPolicyCapability',
     'PhabricatorSpacesCapabilityDefaultView' => 'PhabricatorPolicyCapability',
     'PhabricatorSpacesController' => 'PhabricatorController',
     'PhabricatorSpacesDAO' => 'PhabricatorLiskDAO',
     'PhabricatorSpacesEditController' => 'PhabricatorSpacesController',
     'PhabricatorSpacesInterface' => 'PhabricatorPHIDInterface',
     'PhabricatorSpacesListController' => 'PhabricatorSpacesController',
     'PhabricatorSpacesNamespace' => array(
       'PhabricatorSpacesDAO',
       'PhabricatorPolicyInterface',
       'PhabricatorApplicationTransactionInterface',
       'PhabricatorDestructibleInterface',
     ),
     'PhabricatorSpacesNamespaceDatasource' => 'PhabricatorTypeaheadDatasource',
     'PhabricatorSpacesNamespaceEditor' => 'PhabricatorApplicationTransactionEditor',
     'PhabricatorSpacesNamespacePHIDType' => 'PhabricatorPHIDType',
     'PhabricatorSpacesNamespaceQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
     'PhabricatorSpacesNamespaceSearchEngine' => 'PhabricatorApplicationSearchEngine',
     'PhabricatorSpacesNamespaceTransaction' => 'PhabricatorApplicationTransaction',
     'PhabricatorSpacesNamespaceTransactionQuery' => 'PhabricatorApplicationTransactionQuery',
     'PhabricatorSpacesNoAccessController' => 'PhabricatorSpacesController',
     'PhabricatorSpacesRemarkupRule' => 'PhabricatorObjectRemarkupRule',
     'PhabricatorSpacesSchemaSpec' => 'PhabricatorConfigSchemaSpec',
     'PhabricatorSpacesSearchEngineExtension' => 'PhabricatorSearchEngineExtension',
     'PhabricatorSpacesSearchField' => 'PhabricatorSearchTokenizerField',
     'PhabricatorSpacesTestCase' => 'PhabricatorTestCase',
     'PhabricatorSpacesViewController' => 'PhabricatorSpacesController',
     'PhabricatorStandardCustomField' => 'PhabricatorCustomField',
     'PhabricatorStandardCustomFieldBlueprints' => 'PhabricatorStandardCustomFieldTokenizer',
     'PhabricatorStandardCustomFieldBool' => 'PhabricatorStandardCustomField',
     'PhabricatorStandardCustomFieldCredential' => 'PhabricatorStandardCustomField',
     'PhabricatorStandardCustomFieldDatasource' => 'PhabricatorStandardCustomFieldTokenizer',
     'PhabricatorStandardCustomFieldDate' => 'PhabricatorStandardCustomField',
     'PhabricatorStandardCustomFieldHeader' => 'PhabricatorStandardCustomField',
     'PhabricatorStandardCustomFieldInt' => 'PhabricatorStandardCustomField',
     'PhabricatorStandardCustomFieldLink' => 'PhabricatorStandardCustomField',
     'PhabricatorStandardCustomFieldPHIDs' => 'PhabricatorStandardCustomField',
     'PhabricatorStandardCustomFieldRemarkup' => 'PhabricatorStandardCustomField',
     'PhabricatorStandardCustomFieldSelect' => 'PhabricatorStandardCustomField',
     'PhabricatorStandardCustomFieldText' => 'PhabricatorStandardCustomField',
     'PhabricatorStandardCustomFieldTokenizer' => 'PhabricatorStandardCustomFieldPHIDs',
     'PhabricatorStandardCustomFieldUsers' => 'PhabricatorStandardCustomFieldTokenizer',
     'PhabricatorStandardPageView' => array(
       'PhabricatorBarePageView',
       'AphrontResponseProducerInterface',
     ),
     'PhabricatorStandardSelectCustomFieldDatasource' => 'PhabricatorTypeaheadDatasource',
     'PhabricatorStaticEditField' => 'PhabricatorEditField',
     'PhabricatorStatusController' => 'PhabricatorController',
     'PhabricatorStatusUIExample' => 'PhabricatorUIExample',
     'PhabricatorStorageFixtureScopeGuard' => 'Phobject',
     'PhabricatorStorageManagementAPI' => 'Phobject',
     'PhabricatorStorageManagementAdjustWorkflow' => 'PhabricatorStorageManagementWorkflow',
     'PhabricatorStorageManagementDatabasesWorkflow' => 'PhabricatorStorageManagementWorkflow',
     'PhabricatorStorageManagementDestroyWorkflow' => 'PhabricatorStorageManagementWorkflow',
     'PhabricatorStorageManagementDumpWorkflow' => 'PhabricatorStorageManagementWorkflow',
     'PhabricatorStorageManagementProbeWorkflow' => 'PhabricatorStorageManagementWorkflow',
     'PhabricatorStorageManagementQuickstartWorkflow' => 'PhabricatorStorageManagementWorkflow',
     'PhabricatorStorageManagementRenamespaceWorkflow' => 'PhabricatorStorageManagementWorkflow',
     'PhabricatorStorageManagementShellWorkflow' => 'PhabricatorStorageManagementWorkflow',
     'PhabricatorStorageManagementStatusWorkflow' => 'PhabricatorStorageManagementWorkflow',
     'PhabricatorStorageManagementUpgradeWorkflow' => 'PhabricatorStorageManagementWorkflow',
     'PhabricatorStorageManagementWorkflow' => 'PhabricatorManagementWorkflow',
     'PhabricatorStoragePatch' => 'Phobject',
     'PhabricatorStorageSchemaSpec' => 'PhabricatorConfigSchemaSpec',
     'PhabricatorStorageSetupCheck' => 'PhabricatorSetupCheck',
     'PhabricatorStreamingProtocolAdapter' => 'PhabricatorProtocolAdapter',
     'PhabricatorStringListEditField' => 'PhabricatorEditField',
     'PhabricatorSubscribedToObjectEdgeType' => 'PhabricatorEdgeType',
     'PhabricatorSubscribersEditField' => 'PhabricatorTokenizerEditField',
     'PhabricatorSubscribersQuery' => 'PhabricatorQuery',
     'PhabricatorSubscriptionTriggerClock' => 'PhabricatorTriggerClock',
     'PhabricatorSubscriptionsAddSelfHeraldAction' => 'PhabricatorSubscriptionsHeraldAction',
     'PhabricatorSubscriptionsAddSubscribersHeraldAction' => 'PhabricatorSubscriptionsHeraldAction',
     'PhabricatorSubscriptionsApplication' => 'PhabricatorApplication',
     'PhabricatorSubscriptionsCurtainExtension' => 'PHUICurtainExtension',
     'PhabricatorSubscriptionsEditController' => 'PhabricatorController',
     'PhabricatorSubscriptionsEditEngineExtension' => 'PhabricatorEditEngineExtension',
     'PhabricatorSubscriptionsEditor' => 'PhabricatorEditor',
     'PhabricatorSubscriptionsFulltextEngineExtension' => 'PhabricatorFulltextEngineExtension',
     'PhabricatorSubscriptionsHeraldAction' => 'HeraldAction',
     'PhabricatorSubscriptionsListController' => 'PhabricatorController',
     'PhabricatorSubscriptionsRemoveSelfHeraldAction' => 'PhabricatorSubscriptionsHeraldAction',
     'PhabricatorSubscriptionsRemoveSubscribersHeraldAction' => 'PhabricatorSubscriptionsHeraldAction',
     'PhabricatorSubscriptionsSearchEngineAttachment' => 'PhabricatorSearchEngineAttachment',
     'PhabricatorSubscriptionsSearchEngineExtension' => 'PhabricatorSearchEngineExtension',
     'PhabricatorSubscriptionsSubscribeEmailCommand' => 'MetaMTAEmailTransactionCommand',
     'PhabricatorSubscriptionsSubscribersPolicyRule' => 'PhabricatorPolicyRule',
     'PhabricatorSubscriptionsTransactionController' => 'PhabricatorController',
     'PhabricatorSubscriptionsUIEventListener' => 'PhabricatorEventListener',
     'PhabricatorSubscriptionsUnsubscribeEmailCommand' => 'MetaMTAEmailTransactionCommand',
     'PhabricatorSupportApplication' => 'PhabricatorApplication',
     'PhabricatorSyntaxHighlighter' => 'Phobject',
     'PhabricatorSyntaxHighlightingConfigOptions' => 'PhabricatorApplicationConfigOptions',
+    'PhabricatorSyntaxStyle' => 'Phobject',
     'PhabricatorSystemAction' => 'Phobject',
     'PhabricatorSystemActionEngine' => 'Phobject',
     'PhabricatorSystemActionGarbageCollector' => 'PhabricatorGarbageCollector',
     'PhabricatorSystemActionLog' => 'PhabricatorSystemDAO',
     'PhabricatorSystemActionRateLimitException' => 'Exception',
     'PhabricatorSystemApplication' => 'PhabricatorApplication',
     'PhabricatorSystemDAO' => 'PhabricatorLiskDAO',
     'PhabricatorSystemDestructionGarbageCollector' => 'PhabricatorGarbageCollector',
     'PhabricatorSystemDestructionLog' => 'PhabricatorSystemDAO',
     'PhabricatorSystemReadOnlyController' => 'PhabricatorController',
     'PhabricatorSystemRemoveDestroyWorkflow' => 'PhabricatorSystemRemoveWorkflow',
     'PhabricatorSystemRemoveLogWorkflow' => 'PhabricatorSystemRemoveWorkflow',
     'PhabricatorSystemRemoveWorkflow' => 'PhabricatorManagementWorkflow',
     'PhabricatorSystemSelectEncodingController' => 'PhabricatorController',
     'PhabricatorSystemSelectHighlightController' => 'PhabricatorController',
     'PhabricatorTOTPAuthFactor' => 'PhabricatorAuthFactor',
     'PhabricatorTOTPAuthFactorTestCase' => 'PhabricatorTestCase',
     'PhabricatorTaskmasterDaemon' => 'PhabricatorDaemon',
     'PhabricatorTestApplication' => 'PhabricatorApplication',
     'PhabricatorTestCase' => 'PhutilTestCase',
     'PhabricatorTestController' => 'PhabricatorController',
     'PhabricatorTestDataGenerator' => 'Phobject',
     'PhabricatorTestNoCycleEdgeType' => 'PhabricatorEdgeType',
     'PhabricatorTestStorageEngine' => 'PhabricatorFileStorageEngine',
     'PhabricatorTestWorker' => 'PhabricatorWorker',
     'PhabricatorTextAreaEditField' => 'PhabricatorEditField',
     'PhabricatorTextEditField' => 'PhabricatorEditField',
     'PhabricatorTime' => 'Phobject',
     'PhabricatorTimeGuard' => 'Phobject',
     'PhabricatorTimeTestCase' => 'PhabricatorTestCase',
     'PhabricatorTimezoneSetupCheck' => 'PhabricatorSetupCheck',
     'PhabricatorToken' => array(
       'PhabricatorTokenDAO',
       'PhabricatorPolicyInterface',
     ),
     'PhabricatorTokenController' => 'PhabricatorController',
     'PhabricatorTokenCount' => 'PhabricatorTokenDAO',
     'PhabricatorTokenCountQuery' => 'PhabricatorOffsetPagedQuery',
     'PhabricatorTokenDAO' => 'PhabricatorLiskDAO',
     'PhabricatorTokenDestructionEngineExtension' => 'PhabricatorDestructionEngineExtension',
     'PhabricatorTokenGiveController' => 'PhabricatorTokenController',
     'PhabricatorTokenGiven' => array(
       'PhabricatorTokenDAO',
       'PhabricatorPolicyInterface',
     ),
     'PhabricatorTokenGivenController' => 'PhabricatorTokenController',
     'PhabricatorTokenGivenEditor' => 'PhabricatorEditor',
     'PhabricatorTokenGivenFeedStory' => 'PhabricatorFeedStory',
     'PhabricatorTokenGivenQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
     'PhabricatorTokenLeaderController' => 'PhabricatorTokenController',
     'PhabricatorTokenQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
     'PhabricatorTokenReceiverQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
     'PhabricatorTokenTokenPHIDType' => 'PhabricatorPHIDType',
     'PhabricatorTokenUIEventListener' => 'PhabricatorEventListener',
     'PhabricatorTokenizerEditField' => 'PhabricatorPHIDListEditField',
     'PhabricatorTokensApplication' => 'PhabricatorApplication',
     'PhabricatorTokensCurtainExtension' => 'PHUICurtainExtension',
     'PhabricatorTokensSettingsPanel' => 'PhabricatorSettingsPanel',
     'PhabricatorTooltipUIExample' => 'PhabricatorUIExample',
     'PhabricatorTransactions' => 'Phobject',
     'PhabricatorTransactionsApplication' => 'PhabricatorApplication',
     'PhabricatorTransactionsDestructionEngineExtension' => 'PhabricatorDestructionEngineExtension',
     'PhabricatorTransactionsFulltextEngineExtension' => 'PhabricatorFulltextEngineExtension',
     'PhabricatorTransformedFile' => 'PhabricatorFileDAO',
     'PhabricatorTranslationsConfigOptions' => 'PhabricatorApplicationConfigOptions',
     'PhabricatorTriggerAction' => 'Phobject',
     'PhabricatorTriggerClock' => 'Phobject',
     'PhabricatorTriggerClockTestCase' => 'PhabricatorTestCase',
     'PhabricatorTriggerDaemon' => 'PhabricatorDaemon',
     'PhabricatorTrivialTestCase' => 'PhabricatorTestCase',
     'PhabricatorTwitchAuthProvider' => 'PhabricatorOAuth2AuthProvider',
     'PhabricatorTwitterAuthProvider' => 'PhabricatorOAuth1AuthProvider',
     'PhabricatorTypeaheadApplication' => 'PhabricatorApplication',
     'PhabricatorTypeaheadCompositeDatasource' => 'PhabricatorTypeaheadDatasource',
     'PhabricatorTypeaheadDatasource' => 'Phobject',
     'PhabricatorTypeaheadDatasourceController' => 'PhabricatorController',
     'PhabricatorTypeaheadFunctionHelpController' => 'PhabricatorTypeaheadDatasourceController',
     'PhabricatorTypeaheadInvalidTokenException' => 'Exception',
     'PhabricatorTypeaheadModularDatasourceController' => 'PhabricatorTypeaheadDatasourceController',
     'PhabricatorTypeaheadMonogramDatasource' => 'PhabricatorTypeaheadDatasource',
     'PhabricatorTypeaheadResult' => 'Phobject',
     'PhabricatorTypeaheadRuntimeCompositeDatasource' => 'PhabricatorTypeaheadCompositeDatasource',
     'PhabricatorTypeaheadTokenView' => 'AphrontTagView',
     'PhabricatorUIConfigOptions' => 'PhabricatorApplicationConfigOptions',
     'PhabricatorUIExample' => 'Phobject',
     'PhabricatorUIExampleRenderController' => 'PhabricatorController',
     'PhabricatorUIExamplesApplication' => 'PhabricatorApplication',
     'PhabricatorUSEnglishTranslation' => 'PhutilTranslation',
     'PhabricatorUnitTestContentSource' => 'PhabricatorContentSource',
     'PhabricatorUnitsTestCase' => 'PhabricatorTestCase',
     'PhabricatorUnknownContentSource' => 'PhabricatorContentSource',
     'PhabricatorUnsubscribedFromObjectEdgeType' => 'PhabricatorEdgeType',
     'PhabricatorUser' => array(
       'PhabricatorUserDAO',
       'PhutilPerson',
       'PhabricatorPolicyInterface',
       'PhabricatorCustomFieldInterface',
       'PhabricatorDestructibleInterface',
       'PhabricatorSSHPublicKeyInterface',
       'PhabricatorFlaggableInterface',
       'PhabricatorApplicationTransactionInterface',
       'PhabricatorFulltextInterface',
     ),
     'PhabricatorUserBlurbField' => 'PhabricatorUserCustomField',
     'PhabricatorUserCardView' => 'AphrontTagView',
     'PhabricatorUserConfigOptions' => 'PhabricatorApplicationConfigOptions',
     'PhabricatorUserConfiguredCustomField' => array(
       'PhabricatorUserCustomField',
       'PhabricatorStandardCustomFieldInterface',
     ),
     'PhabricatorUserConfiguredCustomFieldStorage' => 'PhabricatorCustomFieldStorage',
     'PhabricatorUserCustomField' => 'PhabricatorCustomField',
     'PhabricatorUserCustomFieldNumericIndex' => 'PhabricatorCustomFieldNumericIndexStorage',
     'PhabricatorUserCustomFieldStringIndex' => 'PhabricatorCustomFieldStringIndexStorage',
     'PhabricatorUserDAO' => 'PhabricatorLiskDAO',
     'PhabricatorUserEditor' => 'PhabricatorEditor',
     'PhabricatorUserEditorTestCase' => 'PhabricatorTestCase',
     'PhabricatorUserEmail' => 'PhabricatorUserDAO',
     'PhabricatorUserEmailTestCase' => 'PhabricatorTestCase',
     'PhabricatorUserFulltextEngine' => 'PhabricatorFulltextEngine',
     'PhabricatorUserIconField' => 'PhabricatorUserCustomField',
     'PhabricatorUserLog' => array(
       'PhabricatorUserDAO',
       'PhabricatorPolicyInterface',
     ),
     'PhabricatorUserLogView' => 'AphrontView',
     'PhabricatorUserPHIDResolver' => 'PhabricatorPHIDResolver',
     'PhabricatorUserPreferences' => 'PhabricatorUserDAO',
     'PhabricatorUserProfile' => 'PhabricatorUserDAO',
     'PhabricatorUserProfileEditor' => 'PhabricatorApplicationTransactionEditor',
     'PhabricatorUserRealNameField' => 'PhabricatorUserCustomField',
     'PhabricatorUserRolesField' => 'PhabricatorUserCustomField',
     'PhabricatorUserSchemaSpec' => 'PhabricatorConfigSchemaSpec',
     'PhabricatorUserSinceField' => 'PhabricatorUserCustomField',
     'PhabricatorUserStatusField' => 'PhabricatorUserCustomField',
     'PhabricatorUserTestCase' => 'PhabricatorTestCase',
     'PhabricatorUserTitleField' => 'PhabricatorUserCustomField',
     'PhabricatorUserTransaction' => 'PhabricatorApplicationTransaction',
     'PhabricatorUsersEditField' => 'PhabricatorTokenizerEditField',
     'PhabricatorUsersPolicyRule' => 'PhabricatorPolicyRule',
     'PhabricatorUsersSearchField' => 'PhabricatorSearchTokenizerField',
     'PhabricatorVCSResponse' => 'AphrontResponse',
     'PhabricatorVersionedDraft' => 'PhabricatorDraftDAO',
     'PhabricatorVeryWowEnglishTranslation' => 'PhutilTranslation',
     'PhabricatorViewerDatasource' => 'PhabricatorTypeaheadDatasource',
     'PhabricatorWatcherHasObjectEdgeType' => 'PhabricatorEdgeType',
     'PhabricatorWebContentSource' => 'PhabricatorContentSource',
     'PhabricatorWordPressAuthProvider' => 'PhabricatorOAuth2AuthProvider',
     'PhabricatorWorker' => 'Phobject',
     'PhabricatorWorkerActiveTask' => 'PhabricatorWorkerTask',
     'PhabricatorWorkerArchiveTask' => 'PhabricatorWorkerTask',
     'PhabricatorWorkerArchiveTaskQuery' => 'PhabricatorQuery',
     'PhabricatorWorkerBulkJob' => array(
       'PhabricatorWorkerDAO',
       'PhabricatorPolicyInterface',
       'PhabricatorSubscribableInterface',
       'PhabricatorApplicationTransactionInterface',
       'PhabricatorDestructibleInterface',
     ),
     'PhabricatorWorkerBulkJobCreateWorker' => 'PhabricatorWorkerBulkJobWorker',
     'PhabricatorWorkerBulkJobEditor' => 'PhabricatorApplicationTransactionEditor',
     'PhabricatorWorkerBulkJobPHIDType' => 'PhabricatorPHIDType',
     'PhabricatorWorkerBulkJobQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
     'PhabricatorWorkerBulkJobSearchEngine' => 'PhabricatorApplicationSearchEngine',
     'PhabricatorWorkerBulkJobTaskWorker' => 'PhabricatorWorkerBulkJobWorker',
     'PhabricatorWorkerBulkJobTestCase' => 'PhabricatorTestCase',
     'PhabricatorWorkerBulkJobTransaction' => 'PhabricatorApplicationTransaction',
     'PhabricatorWorkerBulkJobTransactionQuery' => 'PhabricatorApplicationTransactionQuery',
     'PhabricatorWorkerBulkJobType' => 'Phobject',
     'PhabricatorWorkerBulkJobWorker' => 'PhabricatorWorker',
     'PhabricatorWorkerBulkTask' => 'PhabricatorWorkerDAO',
     'PhabricatorWorkerDAO' => 'PhabricatorLiskDAO',
     'PhabricatorWorkerDestructionEngineExtension' => 'PhabricatorDestructionEngineExtension',
     'PhabricatorWorkerLeaseQuery' => 'PhabricatorQuery',
     'PhabricatorWorkerManagementCancelWorkflow' => 'PhabricatorWorkerManagementWorkflow',
     'PhabricatorWorkerManagementExecuteWorkflow' => 'PhabricatorWorkerManagementWorkflow',
     'PhabricatorWorkerManagementFloodWorkflow' => 'PhabricatorWorkerManagementWorkflow',
     'PhabricatorWorkerManagementFreeWorkflow' => 'PhabricatorWorkerManagementWorkflow',
     'PhabricatorWorkerManagementRetryWorkflow' => 'PhabricatorWorkerManagementWorkflow',
     'PhabricatorWorkerManagementWorkflow' => 'PhabricatorManagementWorkflow',
     'PhabricatorWorkerPermanentFailureException' => 'Exception',
     'PhabricatorWorkerSchemaSpec' => 'PhabricatorConfigSchemaSpec',
     'PhabricatorWorkerTask' => 'PhabricatorWorkerDAO',
     'PhabricatorWorkerTaskData' => 'PhabricatorWorkerDAO',
     'PhabricatorWorkerTaskDetailController' => 'PhabricatorDaemonController',
     'PhabricatorWorkerTestCase' => 'PhabricatorTestCase',
     'PhabricatorWorkerTrigger' => array(
       'PhabricatorWorkerDAO',
       'PhabricatorDestructibleInterface',
       'PhabricatorPolicyInterface',
     ),
     'PhabricatorWorkerTriggerEvent' => 'PhabricatorWorkerDAO',
     'PhabricatorWorkerTriggerManagementFireWorkflow' => 'PhabricatorWorkerTriggerManagementWorkflow',
     'PhabricatorWorkerTriggerManagementWorkflow' => 'PhabricatorManagementWorkflow',
     'PhabricatorWorkerTriggerPHIDType' => 'PhabricatorPHIDType',
     'PhabricatorWorkerTriggerQuery' => 'PhabricatorPolicyAwareQuery',
     'PhabricatorWorkerYieldException' => 'Exception',
     'PhabricatorWorkingCopyDiscoveryTestCase' => 'PhabricatorWorkingCopyTestCase',
     'PhabricatorWorkingCopyPullTestCase' => 'PhabricatorWorkingCopyTestCase',
     'PhabricatorWorkingCopyTestCase' => 'PhabricatorTestCase',
     'PhabricatorXHPASTDAO' => 'PhabricatorLiskDAO',
     'PhabricatorXHPASTParseTree' => 'PhabricatorXHPASTDAO',
     'PhabricatorXHPASTViewController' => 'PhabricatorController',
     'PhabricatorXHPASTViewFrameController' => 'PhabricatorXHPASTViewController',
     'PhabricatorXHPASTViewFramesetController' => 'PhabricatorXHPASTViewController',
     'PhabricatorXHPASTViewInputController' => 'PhabricatorXHPASTViewPanelController',
     'PhabricatorXHPASTViewPanelController' => 'PhabricatorXHPASTViewController',
     'PhabricatorXHPASTViewRunController' => 'PhabricatorXHPASTViewController',
     'PhabricatorXHPASTViewStreamController' => 'PhabricatorXHPASTViewPanelController',
     'PhabricatorXHPASTViewTreeController' => 'PhabricatorXHPASTViewPanelController',
     'PhabricatorXHProfApplication' => 'PhabricatorApplication',
     'PhabricatorXHProfController' => 'PhabricatorController',
     'PhabricatorXHProfDAO' => 'PhabricatorLiskDAO',
     'PhabricatorXHProfProfileController' => 'PhabricatorXHProfController',
     'PhabricatorXHProfProfileSymbolView' => 'PhabricatorXHProfProfileView',
     'PhabricatorXHProfProfileTopLevelView' => 'PhabricatorXHProfProfileView',
     'PhabricatorXHProfProfileView' => 'AphrontView',
     'PhabricatorXHProfSample' => 'PhabricatorXHProfDAO',
     'PhabricatorXHProfSampleListController' => 'PhabricatorXHProfController',
     'PhabricatorYoutubeRemarkupRule' => 'PhutilRemarkupRule',
     'PhameBlog' => array(
       'PhameDAO',
       'PhabricatorPolicyInterface',
       'PhabricatorMarkupInterface',
       'PhabricatorSubscribableInterface',
       'PhabricatorFlaggableInterface',
       'PhabricatorProjectInterface',
       'PhabricatorDestructibleInterface',
       'PhabricatorApplicationTransactionInterface',
       'PhabricatorConduitResultInterface',
     ),
     'PhameBlogArchiveController' => 'PhameBlogController',
     'PhameBlogController' => 'PhameController',
     'PhameBlogCreateCapability' => 'PhabricatorPolicyCapability',
     'PhameBlogEditConduitAPIMethod' => 'PhabricatorEditEngineAPIMethod',
     'PhameBlogEditController' => 'PhameBlogController',
     'PhameBlogEditEngine' => 'PhabricatorEditEngine',
     'PhameBlogEditor' => 'PhabricatorApplicationTransactionEditor',
     'PhameBlogFeedController' => 'PhameBlogController',
     'PhameBlogListController' => 'PhameBlogController',
     'PhameBlogListView' => 'AphrontTagView',
     'PhameBlogManageController' => 'PhameBlogController',
     'PhameBlogProfilePictureController' => 'PhameBlogController',
     'PhameBlogQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
     'PhameBlogReplyHandler' => 'PhabricatorApplicationTransactionReplyHandler',
     'PhameBlogSearchConduitAPIMethod' => 'PhabricatorSearchEngineAPIMethod',
     'PhameBlogSearchEngine' => 'PhabricatorApplicationSearchEngine',
     'PhameBlogSite' => 'PhameSite',
     'PhameBlogTransaction' => 'PhabricatorApplicationTransaction',
     'PhameBlogTransactionQuery' => 'PhabricatorApplicationTransactionQuery',
     'PhameBlogViewController' => 'PhameLiveController',
     'PhameConstants' => 'Phobject',
     'PhameController' => 'PhabricatorController',
     'PhameDAO' => 'PhabricatorLiskDAO',
     'PhameDescriptionView' => 'AphrontTagView',
     'PhameDraftListView' => 'AphrontTagView',
     'PhameHomeController' => 'PhamePostController',
     'PhameLiveController' => 'PhameController',
     'PhameNextPostView' => 'AphrontTagView',
     'PhamePost' => array(
       'PhameDAO',
       'PhabricatorPolicyInterface',
       'PhabricatorMarkupInterface',
       'PhabricatorFlaggableInterface',
       'PhabricatorProjectInterface',
       'PhabricatorApplicationTransactionInterface',
       'PhabricatorSubscribableInterface',
       'PhabricatorDestructibleInterface',
       'PhabricatorTokenReceiverInterface',
       'PhabricatorConduitResultInterface',
     ),
     'PhamePostCommentController' => 'PhamePostController',
     'PhamePostController' => 'PhameController',
     'PhamePostEditConduitAPIMethod' => 'PhabricatorEditEngineAPIMethod',
     'PhamePostEditController' => 'PhamePostController',
     'PhamePostEditEngine' => 'PhabricatorEditEngine',
     'PhamePostEditor' => 'PhabricatorApplicationTransactionEditor',
     'PhamePostHistoryController' => 'PhamePostController',
     'PhamePostListController' => 'PhamePostController',
     'PhamePostListView' => 'AphrontTagView',
     'PhamePostMailReceiver' => 'PhabricatorObjectMailReceiver',
     'PhamePostMoveController' => 'PhamePostController',
     'PhamePostPublishController' => 'PhamePostController',
     'PhamePostQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
     'PhamePostReplyHandler' => 'PhabricatorApplicationTransactionReplyHandler',
     'PhamePostSearchConduitAPIMethod' => 'PhabricatorSearchEngineAPIMethod',
     'PhamePostSearchEngine' => 'PhabricatorApplicationSearchEngine',
     'PhamePostTransaction' => 'PhabricatorApplicationTransaction',
     'PhamePostTransactionComment' => 'PhabricatorApplicationTransactionComment',
     'PhamePostTransactionQuery' => 'PhabricatorApplicationTransactionQuery',
     'PhamePostViewController' => 'PhameLiveController',
     'PhameSchemaSpec' => 'PhabricatorConfigSchemaSpec',
     'PhameSite' => 'PhabricatorSite',
     'PhluxController' => 'PhabricatorController',
     'PhluxDAO' => 'PhabricatorLiskDAO',
     'PhluxEditController' => 'PhluxController',
     'PhluxListController' => 'PhluxController',
     'PhluxTransaction' => 'PhabricatorApplicationTransaction',
     'PhluxTransactionQuery' => 'PhabricatorApplicationTransactionQuery',
     'PhluxVariable' => array(
       'PhluxDAO',
       'PhabricatorApplicationTransactionInterface',
       'PhabricatorFlaggableInterface',
       'PhabricatorPolicyInterface',
     ),
     'PhluxVariableEditor' => 'PhabricatorApplicationTransactionEditor',
     'PhluxVariablePHIDType' => 'PhabricatorPHIDType',
     'PhluxVariableQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
     'PhluxViewController' => 'PhluxController',
     'PholioActionMenuEventListener' => 'PhabricatorEventListener',
     'PholioController' => 'PhabricatorController',
     'PholioDAO' => 'PhabricatorLiskDAO',
     'PholioDefaultEditCapability' => 'PhabricatorPolicyCapability',
     'PholioDefaultViewCapability' => 'PhabricatorPolicyCapability',
     'PholioImage' => array(
       'PholioDAO',
       'PhabricatorMarkupInterface',
       'PhabricatorPolicyInterface',
     ),
     'PholioImagePHIDType' => 'PhabricatorPHIDType',
     'PholioImageQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
     'PholioImageUploadController' => 'PholioController',
     'PholioInlineController' => 'PholioController',
     'PholioInlineListController' => 'PholioController',
     'PholioMock' => array(
       'PholioDAO',
       'PhabricatorMarkupInterface',
       'PhabricatorPolicyInterface',
       'PhabricatorSubscribableInterface',
       'PhabricatorTokenReceiverInterface',
       'PhabricatorFlaggableInterface',
       'PhabricatorApplicationTransactionInterface',
       'PhabricatorProjectInterface',
       'PhabricatorDestructibleInterface',
       'PhabricatorSpacesInterface',
       'PhabricatorMentionableInterface',
       'PhabricatorFulltextInterface',
     ),
     'PholioMockArchiveController' => 'PholioController',
     'PholioMockAuthorHeraldField' => 'PholioMockHeraldField',
     'PholioMockCommentController' => 'PholioController',
     'PholioMockDescriptionHeraldField' => 'PholioMockHeraldField',
     'PholioMockEditController' => 'PholioController',
     'PholioMockEditor' => 'PhabricatorApplicationTransactionEditor',
     'PholioMockEmbedView' => 'AphrontView',
     'PholioMockFulltextEngine' => 'PhabricatorFulltextEngine',
     'PholioMockHasTaskEdgeType' => 'PhabricatorEdgeType',
     'PholioMockHeraldField' => 'HeraldField',
     'PholioMockHeraldFieldGroup' => 'HeraldFieldGroup',
     'PholioMockImagesView' => 'AphrontView',
     'PholioMockListController' => 'PholioController',
     'PholioMockMailReceiver' => 'PhabricatorObjectMailReceiver',
     'PholioMockNameHeraldField' => 'PholioMockHeraldField',
     'PholioMockPHIDType' => 'PhabricatorPHIDType',
     'PholioMockQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
     'PholioMockSearchEngine' => 'PhabricatorApplicationSearchEngine',
     'PholioMockThumbGridView' => 'AphrontView',
     'PholioMockViewController' => 'PholioController',
     'PholioRemarkupRule' => 'PhabricatorObjectRemarkupRule',
     'PholioReplyHandler' => 'PhabricatorApplicationTransactionReplyHandler',
     'PholioSchemaSpec' => 'PhabricatorConfigSchemaSpec',
     'PholioTransaction' => 'PhabricatorApplicationTransaction',
     'PholioTransactionComment' => 'PhabricatorApplicationTransactionComment',
     'PholioTransactionQuery' => 'PhabricatorApplicationTransactionQuery',
     'PholioTransactionView' => 'PhabricatorApplicationTransactionView',
     'PholioUploadedImageView' => 'AphrontView',
     'PhortuneAccount' => array(
       'PhortuneDAO',
       'PhabricatorApplicationTransactionInterface',
       'PhabricatorPolicyInterface',
     ),
     'PhortuneAccountEditController' => 'PhortuneController',
     'PhortuneAccountEditor' => 'PhabricatorApplicationTransactionEditor',
     'PhortuneAccountHasMemberEdgeType' => 'PhabricatorEdgeType',
     'PhortuneAccountListController' => 'PhortuneController',
     'PhortuneAccountPHIDType' => 'PhabricatorPHIDType',
     'PhortuneAccountQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
     'PhortuneAccountTransaction' => 'PhabricatorApplicationTransaction',
     'PhortuneAccountTransactionQuery' => 'PhabricatorApplicationTransactionQuery',
     'PhortuneAccountViewController' => 'PhortuneController',
     'PhortuneAdHocCart' => 'PhortuneCartImplementation',
     'PhortuneAdHocProduct' => 'PhortuneProductImplementation',
     'PhortuneCart' => array(
       'PhortuneDAO',
       'PhabricatorApplicationTransactionInterface',
       'PhabricatorPolicyInterface',
     ),
     'PhortuneCartAcceptController' => 'PhortuneCartController',
     'PhortuneCartCancelController' => 'PhortuneCartController',
     'PhortuneCartCheckoutController' => 'PhortuneCartController',
     'PhortuneCartController' => 'PhortuneController',
     'PhortuneCartEditor' => 'PhabricatorApplicationTransactionEditor',
     'PhortuneCartImplementation' => 'Phobject',
     'PhortuneCartListController' => 'PhortuneController',
     'PhortuneCartPHIDType' => 'PhabricatorPHIDType',
     'PhortuneCartQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
     'PhortuneCartReplyHandler' => 'PhabricatorApplicationTransactionReplyHandler',
     'PhortuneCartSearchEngine' => 'PhabricatorApplicationSearchEngine',
     'PhortuneCartTransaction' => 'PhabricatorApplicationTransaction',
     'PhortuneCartTransactionQuery' => 'PhabricatorApplicationTransactionQuery',
     'PhortuneCartUpdateController' => 'PhortuneCartController',
     'PhortuneCartViewController' => 'PhortuneCartController',
     'PhortuneCharge' => array(
       'PhortuneDAO',
       'PhabricatorPolicyInterface',
     ),
     'PhortuneChargeListController' => 'PhortuneController',
     'PhortuneChargePHIDType' => 'PhabricatorPHIDType',
     'PhortuneChargeQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
     'PhortuneChargeSearchEngine' => 'PhabricatorApplicationSearchEngine',
     'PhortuneChargeTableView' => 'AphrontView',
     'PhortuneConstants' => 'Phobject',
     'PhortuneController' => 'PhabricatorController',
     'PhortuneCreditCardForm' => 'Phobject',
     'PhortuneCurrency' => 'Phobject',
     'PhortuneCurrencySerializer' => 'PhabricatorLiskSerializer',
     'PhortuneCurrencyTestCase' => 'PhabricatorTestCase',
     'PhortuneDAO' => 'PhabricatorLiskDAO',
     'PhortuneErrCode' => 'PhortuneConstants',
     'PhortuneLandingController' => 'PhortuneController',
     'PhortuneMemberHasAccountEdgeType' => 'PhabricatorEdgeType',
     'PhortuneMemberHasMerchantEdgeType' => 'PhabricatorEdgeType',
     'PhortuneMerchant' => array(
       'PhortuneDAO',
       'PhabricatorApplicationTransactionInterface',
       'PhabricatorPolicyInterface',
     ),
     'PhortuneMerchantCapability' => 'PhabricatorPolicyCapability',
     'PhortuneMerchantController' => 'PhortuneController',
     'PhortuneMerchantEditController' => 'PhortuneMerchantController',
     'PhortuneMerchantEditor' => 'PhabricatorApplicationTransactionEditor',
     'PhortuneMerchantHasMemberEdgeType' => 'PhabricatorEdgeType',
     'PhortuneMerchantInvoiceCreateController' => 'PhortuneMerchantController',
     'PhortuneMerchantListController' => 'PhortuneMerchantController',
     'PhortuneMerchantPHIDType' => 'PhabricatorPHIDType',
     'PhortuneMerchantQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
     'PhortuneMerchantSearchEngine' => 'PhabricatorApplicationSearchEngine',
     'PhortuneMerchantTransaction' => 'PhabricatorApplicationTransaction',
     'PhortuneMerchantTransactionQuery' => 'PhabricatorApplicationTransactionQuery',
     'PhortuneMerchantViewController' => 'PhortuneMerchantController',
     'PhortuneMonthYearExpiryControl' => 'AphrontFormControl',
     'PhortuneOrderTableView' => 'AphrontView',
     'PhortunePayPalPaymentProvider' => 'PhortunePaymentProvider',
     'PhortunePaymentMethod' => array(
       'PhortuneDAO',
       'PhabricatorPolicyInterface',
     ),
     'PhortunePaymentMethodCreateController' => 'PhortuneController',
     'PhortunePaymentMethodDisableController' => 'PhortuneController',
     'PhortunePaymentMethodEditController' => 'PhortuneController',
     'PhortunePaymentMethodPHIDType' => 'PhabricatorPHIDType',
     'PhortunePaymentMethodQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
     'PhortunePaymentProvider' => 'Phobject',
     'PhortunePaymentProviderConfig' => array(
       'PhortuneDAO',
       'PhabricatorPolicyInterface',
     ),
     'PhortunePaymentProviderConfigEditor' => 'PhabricatorApplicationTransactionEditor',
     'PhortunePaymentProviderConfigQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
     'PhortunePaymentProviderConfigTransaction' => 'PhabricatorApplicationTransaction',
     'PhortunePaymentProviderConfigTransactionQuery' => 'PhabricatorApplicationTransactionQuery',
     'PhortunePaymentProviderPHIDType' => 'PhabricatorPHIDType',
     'PhortunePaymentProviderTestCase' => 'PhabricatorTestCase',
     'PhortuneProduct' => array(
       'PhortuneDAO',
       'PhabricatorPolicyInterface',
     ),
     'PhortuneProductImplementation' => 'Phobject',
     'PhortuneProductListController' => 'PhabricatorController',
     'PhortuneProductPHIDType' => 'PhabricatorPHIDType',
     'PhortuneProductQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
     'PhortuneProductViewController' => 'PhortuneController',
     'PhortuneProviderActionController' => 'PhortuneController',
     'PhortuneProviderDisableController' => 'PhortuneMerchantController',
     'PhortuneProviderEditController' => 'PhortuneMerchantController',
     'PhortunePurchase' => array(
       'PhortuneDAO',
       'PhabricatorPolicyInterface',
     ),
     'PhortunePurchasePHIDType' => 'PhabricatorPHIDType',
     'PhortunePurchaseQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
     'PhortuneSchemaSpec' => 'PhabricatorConfigSchemaSpec',
     'PhortuneStripePaymentProvider' => 'PhortunePaymentProvider',
     'PhortuneSubscription' => array(
       'PhortuneDAO',
       'PhabricatorPolicyInterface',
     ),
     'PhortuneSubscriptionCart' => 'PhortuneCartImplementation',
     'PhortuneSubscriptionEditController' => 'PhortuneController',
     'PhortuneSubscriptionImplementation' => 'Phobject',
     'PhortuneSubscriptionListController' => 'PhortuneController',
     'PhortuneSubscriptionPHIDType' => 'PhabricatorPHIDType',
     'PhortuneSubscriptionProduct' => 'PhortuneProductImplementation',
     'PhortuneSubscriptionQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
     'PhortuneSubscriptionSearchEngine' => 'PhabricatorApplicationSearchEngine',
     'PhortuneSubscriptionTableView' => 'AphrontView',
     'PhortuneSubscriptionViewController' => 'PhortuneController',
     'PhortuneSubscriptionWorker' => 'PhabricatorWorker',
     'PhortuneTestPaymentProvider' => 'PhortunePaymentProvider',
     'PhortuneWePayPaymentProvider' => 'PhortunePaymentProvider',
     'PhragmentBrowseController' => 'PhragmentController',
     'PhragmentCanCreateCapability' => 'PhabricatorPolicyCapability',
     'PhragmentConduitAPIMethod' => 'ConduitAPIMethod',
     'PhragmentController' => 'PhabricatorController',
     'PhragmentCreateController' => 'PhragmentController',
     'PhragmentDAO' => 'PhabricatorLiskDAO',
     'PhragmentFragment' => array(
       'PhragmentDAO',
       'PhabricatorPolicyInterface',
     ),
     'PhragmentFragmentPHIDType' => 'PhabricatorPHIDType',
     'PhragmentFragmentQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
     'PhragmentFragmentVersion' => array(
       'PhragmentDAO',
       'PhabricatorPolicyInterface',
     ),
     'PhragmentFragmentVersionPHIDType' => 'PhabricatorPHIDType',
     'PhragmentFragmentVersionQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
     'PhragmentGetPatchConduitAPIMethod' => 'PhragmentConduitAPIMethod',
     'PhragmentHistoryController' => 'PhragmentController',
     'PhragmentPatchController' => 'PhragmentController',
     'PhragmentPatchUtil' => 'Phobject',
     'PhragmentPolicyController' => 'PhragmentController',
     'PhragmentQueryFragmentsConduitAPIMethod' => 'PhragmentConduitAPIMethod',
     'PhragmentRevertController' => 'PhragmentController',
     'PhragmentSchemaSpec' => 'PhabricatorConfigSchemaSpec',
     'PhragmentSnapshot' => array(
       'PhragmentDAO',
       'PhabricatorPolicyInterface',
     ),
     'PhragmentSnapshotChild' => array(
       'PhragmentDAO',
       'PhabricatorPolicyInterface',
     ),
     'PhragmentSnapshotChildQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
     'PhragmentSnapshotCreateController' => 'PhragmentController',
     'PhragmentSnapshotDeleteController' => 'PhragmentController',
     'PhragmentSnapshotPHIDType' => 'PhabricatorPHIDType',
     'PhragmentSnapshotPromoteController' => 'PhragmentController',
     'PhragmentSnapshotQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
     'PhragmentSnapshotViewController' => 'PhragmentController',
     'PhragmentUpdateController' => 'PhragmentController',
     'PhragmentVersionController' => 'PhragmentController',
     'PhragmentZIPController' => 'PhragmentController',
     'PhrequentConduitAPIMethod' => 'ConduitAPIMethod',
     'PhrequentController' => 'PhabricatorController',
     'PhrequentCurtainExtension' => 'PHUICurtainExtension',
     'PhrequentDAO' => 'PhabricatorLiskDAO',
     'PhrequentListController' => 'PhrequentController',
     'PhrequentPopConduitAPIMethod' => 'PhrequentConduitAPIMethod',
     'PhrequentPushConduitAPIMethod' => 'PhrequentConduitAPIMethod',
     'PhrequentSearchEngine' => 'PhabricatorApplicationSearchEngine',
     'PhrequentTimeBlock' => 'Phobject',
     'PhrequentTimeBlockTestCase' => 'PhabricatorTestCase',
     'PhrequentTimeSlices' => 'Phobject',
     'PhrequentTrackController' => 'PhrequentController',
     'PhrequentTrackingConduitAPIMethod' => 'PhrequentConduitAPIMethod',
     'PhrequentTrackingEditor' => 'PhabricatorEditor',
     'PhrequentUIEventListener' => 'PhabricatorEventListener',
     'PhrequentUserTime' => array(
       'PhrequentDAO',
       'PhabricatorPolicyInterface',
     ),
     'PhrequentUserTimeQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
     'PhrictionChangeType' => 'PhrictionConstants',
     'PhrictionConduitAPIMethod' => 'ConduitAPIMethod',
     'PhrictionConstants' => 'Phobject',
     'PhrictionContent' => array(
       'PhrictionDAO',
       'PhabricatorMarkupInterface',
     ),
     'PhrictionController' => 'PhabricatorController',
     'PhrictionCreateConduitAPIMethod' => 'PhrictionConduitAPIMethod',
     'PhrictionDAO' => 'PhabricatorLiskDAO',
     'PhrictionDeleteController' => 'PhrictionController',
     'PhrictionDiffController' => 'PhrictionController',
     'PhrictionDocument' => array(
       'PhrictionDAO',
       'PhabricatorPolicyInterface',
       'PhabricatorSubscribableInterface',
       'PhabricatorFlaggableInterface',
       'PhabricatorTokenReceiverInterface',
       'PhabricatorDestructibleInterface',
       'PhabricatorApplicationTransactionInterface',
       'PhabricatorFulltextInterface',
     ),
     'PhrictionDocumentAuthorHeraldField' => 'PhrictionDocumentHeraldField',
     'PhrictionDocumentContentHeraldField' => 'PhrictionDocumentHeraldField',
     'PhrictionDocumentController' => 'PhrictionController',
     'PhrictionDocumentFulltextEngine' => 'PhabricatorFulltextEngine',
     'PhrictionDocumentHeraldAdapter' => 'HeraldAdapter',
     'PhrictionDocumentHeraldField' => 'HeraldField',
     'PhrictionDocumentHeraldFieldGroup' => 'HeraldFieldGroup',
     'PhrictionDocumentPHIDType' => 'PhabricatorPHIDType',
     'PhrictionDocumentPathHeraldField' => 'PhrictionDocumentHeraldField',
     'PhrictionDocumentQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
     'PhrictionDocumentStatus' => 'PhrictionConstants',
     'PhrictionDocumentTitleHeraldField' => 'PhrictionDocumentHeraldField',
     'PhrictionEditConduitAPIMethod' => 'PhrictionConduitAPIMethod',
     'PhrictionEditController' => 'PhrictionController',
     'PhrictionHistoryConduitAPIMethod' => 'PhrictionConduitAPIMethod',
     'PhrictionHistoryController' => 'PhrictionController',
     'PhrictionInfoConduitAPIMethod' => 'PhrictionConduitAPIMethod',
     'PhrictionListController' => 'PhrictionController',
     'PhrictionMarkupPreviewController' => 'PhabricatorController',
     'PhrictionMoveController' => 'PhrictionController',
     'PhrictionNewController' => 'PhrictionController',
     'PhrictionRemarkupRule' => 'PhutilRemarkupRule',
     'PhrictionReplyHandler' => 'PhabricatorApplicationTransactionReplyHandler',
     'PhrictionSchemaSpec' => 'PhabricatorConfigSchemaSpec',
     'PhrictionSearchEngine' => 'PhabricatorApplicationSearchEngine',
     'PhrictionTransaction' => 'PhabricatorApplicationTransaction',
     'PhrictionTransactionComment' => 'PhabricatorApplicationTransactionComment',
     'PhrictionTransactionEditor' => 'PhabricatorApplicationTransactionEditor',
     'PhrictionTransactionQuery' => 'PhabricatorApplicationTransactionQuery',
     'PolicyLockOptionType' => 'PhabricatorConfigJSONOptionType',
     'PonderAddAnswerView' => 'AphrontView',
     'PonderAnswer' => array(
       'PonderDAO',
       'PhabricatorApplicationTransactionInterface',
       'PhabricatorMarkupInterface',
       'PhabricatorPolicyInterface',
       'PhabricatorFlaggableInterface',
       'PhabricatorSubscribableInterface',
       'PhabricatorDestructibleInterface',
     ),
     'PonderAnswerCommentController' => 'PonderController',
     'PonderAnswerEditController' => 'PonderController',
     'PonderAnswerEditor' => 'PonderEditor',
     'PonderAnswerHistoryController' => 'PonderController',
     'PonderAnswerMailReceiver' => 'PhabricatorObjectMailReceiver',
     'PonderAnswerPHIDType' => 'PhabricatorPHIDType',
     'PonderAnswerQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
     'PonderAnswerReplyHandler' => 'PhabricatorApplicationTransactionReplyHandler',
     'PonderAnswerSaveController' => 'PonderController',
     'PonderAnswerStatus' => 'PonderConstants',
     'PonderAnswerTransaction' => 'PhabricatorApplicationTransaction',
     'PonderAnswerTransactionComment' => 'PhabricatorApplicationTransactionComment',
     'PonderAnswerTransactionQuery' => 'PhabricatorApplicationTransactionQuery',
     'PonderAnswerView' => 'AphrontTagView',
     'PonderConstants' => 'Phobject',
     'PonderController' => 'PhabricatorController',
     'PonderDAO' => 'PhabricatorLiskDAO',
     'PonderDefaultViewCapability' => 'PhabricatorPolicyCapability',
     'PonderEditor' => 'PhabricatorApplicationTransactionEditor',
     'PonderFooterView' => 'AphrontTagView',
     'PonderModerateCapability' => 'PhabricatorPolicyCapability',
     'PonderQuestion' => array(
       'PonderDAO',
       'PhabricatorApplicationTransactionInterface',
       'PhabricatorMarkupInterface',
       'PhabricatorSubscribableInterface',
       'PhabricatorFlaggableInterface',
       'PhabricatorPolicyInterface',
       'PhabricatorTokenReceiverInterface',
       'PhabricatorProjectInterface',
       'PhabricatorDestructibleInterface',
       'PhabricatorSpacesInterface',
       'PhabricatorFulltextInterface',
     ),
     'PonderQuestionCommentController' => 'PonderController',
     'PonderQuestionEditController' => 'PonderController',
     'PonderQuestionEditor' => 'PonderEditor',
     'PonderQuestionFulltextEngine' => 'PhabricatorFulltextEngine',
     'PonderQuestionHistoryController' => 'PonderController',
     'PonderQuestionListController' => 'PonderController',
     'PonderQuestionMailReceiver' => 'PhabricatorObjectMailReceiver',
     'PonderQuestionPHIDType' => 'PhabricatorPHIDType',
     'PonderQuestionQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
     'PonderQuestionReplyHandler' => 'PhabricatorApplicationTransactionReplyHandler',
     'PonderQuestionSearchEngine' => 'PhabricatorApplicationSearchEngine',
     'PonderQuestionStatus' => 'PonderConstants',
     'PonderQuestionStatusController' => 'PonderController',
     'PonderQuestionTransaction' => 'PhabricatorApplicationTransaction',
     'PonderQuestionTransactionComment' => 'PhabricatorApplicationTransactionComment',
     'PonderQuestionTransactionQuery' => 'PhabricatorApplicationTransactionQuery',
     'PonderQuestionViewController' => 'PonderController',
     'PonderRemarkupRule' => 'PhabricatorObjectRemarkupRule',
     'PonderSchemaSpec' => 'PhabricatorConfigSchemaSpec',
     'ProjectAddProjectsEmailCommand' => 'MetaMTAEmailTransactionCommand',
     'ProjectBoardTaskCard' => 'Phobject',
     'ProjectCanLockProjectsCapability' => 'PhabricatorPolicyCapability',
     'ProjectConduitAPIMethod' => 'ConduitAPIMethod',
     'ProjectCreateConduitAPIMethod' => 'ProjectConduitAPIMethod',
     'ProjectCreateProjectsCapability' => 'PhabricatorPolicyCapability',
     'ProjectDefaultEditCapability' => 'PhabricatorPolicyCapability',
     'ProjectDefaultJoinCapability' => 'PhabricatorPolicyCapability',
     'ProjectDefaultViewCapability' => 'PhabricatorPolicyCapability',
     'ProjectEditConduitAPIMethod' => 'PhabricatorEditEngineAPIMethod',
     'ProjectQueryConduitAPIMethod' => 'ProjectConduitAPIMethod',
     'ProjectRemarkupRule' => 'PhabricatorObjectRemarkupRule',
     'ProjectRemarkupRuleTestCase' => 'PhabricatorTestCase',
     'ProjectReplyHandler' => 'PhabricatorApplicationTransactionReplyHandler',
     'ProjectSearchConduitAPIMethod' => 'PhabricatorSearchEngineAPIMethod',
     'QueryFormattingTestCase' => 'PhabricatorTestCase',
     'ReleephAuthorFieldSpecification' => 'ReleephFieldSpecification',
     'ReleephBranch' => array(
       'ReleephDAO',
       'PhabricatorApplicationTransactionInterface',
       'PhabricatorPolicyInterface',
     ),
     'ReleephBranchAccessController' => 'ReleephBranchController',
     'ReleephBranchCommitFieldSpecification' => 'ReleephFieldSpecification',
     'ReleephBranchController' => 'ReleephController',
     'ReleephBranchCreateController' => 'ReleephProductController',
     'ReleephBranchEditController' => 'ReleephBranchController',
     'ReleephBranchEditor' => 'PhabricatorEditor',
     'ReleephBranchHistoryController' => 'ReleephBranchController',
     'ReleephBranchNamePreviewController' => 'ReleephController',
     'ReleephBranchPHIDType' => 'PhabricatorPHIDType',
     'ReleephBranchPreviewView' => 'AphrontFormControl',
     'ReleephBranchQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
     'ReleephBranchSearchEngine' => 'PhabricatorApplicationSearchEngine',
     'ReleephBranchTemplate' => 'Phobject',
     'ReleephBranchTransaction' => 'PhabricatorApplicationTransaction',
     'ReleephBranchTransactionQuery' => 'PhabricatorApplicationTransactionQuery',
     'ReleephBranchViewController' => 'ReleephBranchController',
     'ReleephCommitFinder' => 'Phobject',
     'ReleephCommitFinderException' => 'Exception',
     'ReleephCommitMessageFieldSpecification' => 'ReleephFieldSpecification',
     'ReleephConduitAPIMethod' => 'ConduitAPIMethod',
     'ReleephController' => 'PhabricatorController',
     'ReleephDAO' => 'PhabricatorLiskDAO',
     'ReleephDefaultFieldSelector' => 'ReleephFieldSelector',
     'ReleephDependsOnFieldSpecification' => 'ReleephFieldSpecification',
     'ReleephDiffChurnFieldSpecification' => 'ReleephFieldSpecification',
     'ReleephDiffMessageFieldSpecification' => 'ReleephFieldSpecification',
     'ReleephDiffSizeFieldSpecification' => 'ReleephFieldSpecification',
     'ReleephFieldParseException' => 'Exception',
     'ReleephFieldSelector' => 'Phobject',
     'ReleephFieldSpecification' => array(
       'PhabricatorCustomField',
       'PhabricatorMarkupInterface',
     ),
     'ReleephGetBranchesConduitAPIMethod' => 'ReleephConduitAPIMethod',
     'ReleephIntentFieldSpecification' => 'ReleephFieldSpecification',
     'ReleephLevelFieldSpecification' => 'ReleephFieldSpecification',
     'ReleephOriginalCommitFieldSpecification' => 'ReleephFieldSpecification',
     'ReleephProductActionController' => 'ReleephProductController',
     'ReleephProductController' => 'ReleephController',
     'ReleephProductCreateController' => 'ReleephProductController',
     'ReleephProductEditController' => 'ReleephProductController',
     'ReleephProductEditor' => 'PhabricatorApplicationTransactionEditor',
     'ReleephProductHistoryController' => 'ReleephProductController',
     'ReleephProductListController' => 'ReleephController',
     'ReleephProductPHIDType' => 'PhabricatorPHIDType',
     'ReleephProductQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
     'ReleephProductSearchEngine' => 'PhabricatorApplicationSearchEngine',
     'ReleephProductTransaction' => 'PhabricatorApplicationTransaction',
     'ReleephProductTransactionQuery' => 'PhabricatorApplicationTransactionQuery',
     'ReleephProductViewController' => 'ReleephProductController',
     'ReleephProject' => array(
       'ReleephDAO',
       'PhabricatorApplicationTransactionInterface',
       'PhabricatorPolicyInterface',
     ),
     'ReleephQueryBranchesConduitAPIMethod' => 'ReleephConduitAPIMethod',
     'ReleephQueryProductsConduitAPIMethod' => 'ReleephConduitAPIMethod',
     'ReleephQueryRequestsConduitAPIMethod' => 'ReleephConduitAPIMethod',
     'ReleephReasonFieldSpecification' => 'ReleephFieldSpecification',
     'ReleephRequest' => array(
       'ReleephDAO',
       'PhabricatorApplicationTransactionInterface',
       'PhabricatorPolicyInterface',
       'PhabricatorCustomFieldInterface',
     ),
     'ReleephRequestActionController' => 'ReleephRequestController',
     'ReleephRequestCommentController' => 'ReleephRequestController',
     'ReleephRequestConduitAPIMethod' => 'ReleephConduitAPIMethod',
     'ReleephRequestController' => 'ReleephController',
     'ReleephRequestDifferentialCreateController' => 'ReleephController',
     'ReleephRequestEditController' => 'ReleephBranchController',
     'ReleephRequestMailReceiver' => 'PhabricatorObjectMailReceiver',
     'ReleephRequestPHIDType' => 'PhabricatorPHIDType',
     'ReleephRequestQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
     'ReleephRequestReplyHandler' => 'PhabricatorApplicationTransactionReplyHandler',
     'ReleephRequestSearchEngine' => 'PhabricatorApplicationSearchEngine',
     'ReleephRequestStatus' => 'Phobject',
     'ReleephRequestTransaction' => 'PhabricatorApplicationTransaction',
     'ReleephRequestTransactionComment' => 'PhabricatorApplicationTransactionComment',
     'ReleephRequestTransactionQuery' => 'PhabricatorApplicationTransactionQuery',
     'ReleephRequestTransactionalEditor' => 'PhabricatorApplicationTransactionEditor',
     'ReleephRequestTypeaheadControl' => 'AphrontFormControl',
     'ReleephRequestTypeaheadController' => 'PhabricatorTypeaheadDatasourceController',
     'ReleephRequestView' => 'AphrontView',
     'ReleephRequestViewController' => 'ReleephBranchController',
     'ReleephRequestorFieldSpecification' => 'ReleephFieldSpecification',
     'ReleephRevisionFieldSpecification' => 'ReleephFieldSpecification',
     'ReleephSeverityFieldSpecification' => 'ReleephLevelFieldSpecification',
     'ReleephSummaryFieldSpecification' => 'ReleephFieldSpecification',
     'ReleephWorkCanPushConduitAPIMethod' => 'ReleephConduitAPIMethod',
     'ReleephWorkGetAuthorInfoConduitAPIMethod' => 'ReleephConduitAPIMethod',
     'ReleephWorkGetBranchCommitMessageConduitAPIMethod' => 'ReleephConduitAPIMethod',
     'ReleephWorkGetBranchConduitAPIMethod' => 'ReleephConduitAPIMethod',
     'ReleephWorkGetCommitMessageConduitAPIMethod' => 'ReleephConduitAPIMethod',
     'ReleephWorkNextRequestConduitAPIMethod' => 'ReleephConduitAPIMethod',
     'ReleephWorkRecordConduitAPIMethod' => 'ReleephConduitAPIMethod',
     'ReleephWorkRecordPickStatusConduitAPIMethod' => 'ReleephConduitAPIMethod',
     'RemarkupProcessConduitAPIMethod' => 'ConduitAPIMethod',
     'RepositoryConduitAPIMethod' => 'ConduitAPIMethod',
-    'RepositoryCreateConduitAPIMethod' => 'RepositoryConduitAPIMethod',
     'RepositoryQueryConduitAPIMethod' => 'RepositoryConduitAPIMethod',
     'ShellLogView' => 'AphrontView',
     'SlowvoteConduitAPIMethod' => 'ConduitAPIMethod',
     'SlowvoteEmbedView' => 'AphrontView',
     'SlowvoteInfoConduitAPIMethod' => 'SlowvoteConduitAPIMethod',
     'SlowvoteRemarkupRule' => 'PhabricatorObjectRemarkupRule',
     'SubscriptionListDialogBuilder' => 'Phobject',
     'SubscriptionListStringBuilder' => 'Phobject',
     'TokenConduitAPIMethod' => 'ConduitAPIMethod',
     'TokenGiveConduitAPIMethod' => 'TokenConduitAPIMethod',
     'TokenGivenConduitAPIMethod' => 'TokenConduitAPIMethod',
     'TokenQueryConduitAPIMethod' => 'TokenConduitAPIMethod',
     'UserConduitAPIMethod' => 'ConduitAPIMethod',
     'UserDisableConduitAPIMethod' => 'UserConduitAPIMethod',
     'UserEnableConduitAPIMethod' => 'UserConduitAPIMethod',
     'UserFindConduitAPIMethod' => 'UserConduitAPIMethod',
     'UserQueryConduitAPIMethod' => 'UserConduitAPIMethod',
     'UserWhoAmIConduitAPIMethod' => 'UserConduitAPIMethod',
   ),
 ));
diff --git a/src/aphront/AphrontRequest.php b/src/aphront/AphrontRequest.php
index 9305d6b48..a49821e10 100644
--- a/src/aphront/AphrontRequest.php
+++ b/src/aphront/AphrontRequest.php
@@ -1,813 +1,813 @@
 <?php
 
 /**
  * @task data     Accessing Request Data
  * @task cookie   Managing Cookies
  * @task cluster  Working With a Phabricator Cluster
  */
 final class AphrontRequest extends Phobject {
 
   // NOTE: These magic request-type parameters are automatically included in
   // certain requests (e.g., by phabricator_form(), JX.Request,
   // JX.Workflow, and ConduitClient) and help us figure out what sort of
   // response the client expects.
 
   const TYPE_AJAX = '__ajax__';
   const TYPE_FORM = '__form__';
   const TYPE_CONDUIT = '__conduit__';
   const TYPE_WORKFLOW = '__wflow__';
   const TYPE_CONTINUE = '__continue__';
   const TYPE_PREVIEW = '__preview__';
   const TYPE_HISEC = '__hisec__';
   const TYPE_QUICKSAND = '__quicksand__';
 
   private $host;
   private $path;
   private $requestData;
   private $user;
   private $applicationConfiguration;
   private $site;
   private $controller;
   private $uriData = array();
   private $cookiePrefix;
 
   public function __construct($host, $path) {
     $this->host = $host;
     $this->path = $path;
   }
 
   public function setURIMap(array $uri_data) {
     $this->uriData = $uri_data;
     return $this;
   }
 
   public function getURIMap() {
     return $this->uriData;
   }
 
   public function getURIData($key, $default = null) {
     return idx($this->uriData, $key, $default);
   }
 
   public function setApplicationConfiguration(
     $application_configuration) {
     $this->applicationConfiguration = $application_configuration;
     return $this;
   }
 
   public function getApplicationConfiguration() {
     return $this->applicationConfiguration;
   }
 
   public function setPath($path) {
     $this->path = $path;
     return $this;
   }
 
   public function getPath() {
     return $this->path;
   }
 
   public function getHost() {
     // The "Host" header may include a port number, or may be a malicious
     // header in the form "realdomain.com:ignored@evil.com". Invoke the full
     // parser to extract the real domain correctly. See here for coverage of
     // a similar issue in Django:
     //
     //  https://www.djangoproject.com/weblog/2012/oct/17/security/
     $uri = new PhutilURI('http://'.$this->host);
     return $uri->getDomain();
   }
 
   public function setSite(AphrontSite $site) {
     $this->site = $site;
     return $this;
   }
 
   public function getSite() {
     return $this->site;
   }
 
   public function setController(AphrontController $controller) {
     $this->controller = $controller;
     return $this;
   }
 
   public function getController() {
     return $this->controller;
   }
 
 
 /* -(  Accessing Request Data  )--------------------------------------------- */
 
 
   /**
    * @task data
    */
   public function setRequestData(array $request_data) {
     $this->requestData = $request_data;
     return $this;
   }
 
 
   /**
    * @task data
    */
   public function getRequestData() {
     return $this->requestData;
   }
 
 
   /**
    * @task data
    */
   public function getInt($name, $default = null) {
     if (isset($this->requestData[$name])) {
       // Converting from array to int is "undefined". Don't rely on whatever
       // PHP decides to do.
       if (is_array($this->requestData[$name])) {
         return $default;
       }
       return (int)$this->requestData[$name];
     } else {
       return $default;
     }
   }
 
 
   /**
    * @task data
    */
   public function getBool($name, $default = null) {
     if (isset($this->requestData[$name])) {
       if ($this->requestData[$name] === 'true') {
         return true;
       } else if ($this->requestData[$name] === 'false') {
         return false;
       } else {
         return (bool)$this->requestData[$name];
       }
     } else {
       return $default;
     }
   }
 
 
   /**
    * @task data
    */
   public function getStr($name, $default = null) {
     if (isset($this->requestData[$name])) {
       $str = (string)$this->requestData[$name];
       // Normalize newline craziness.
       $str = str_replace(
         array("\r\n", "\r"),
         array("\n", "\n"),
         $str);
       return $str;
     } else {
       return $default;
     }
   }
 
 
   /**
    * @task data
    */
   public function getArr($name, $default = array()) {
     if (isset($this->requestData[$name]) &&
         is_array($this->requestData[$name])) {
       return $this->requestData[$name];
     } else {
       return $default;
     }
   }
 
 
   /**
    * @task data
    */
   public function getStrList($name, $default = array()) {
     if (!isset($this->requestData[$name])) {
       return $default;
     }
     $list = $this->getStr($name);
     $list = preg_split('/[\s,]+/', $list, $limit = -1, PREG_SPLIT_NO_EMPTY);
     return $list;
   }
 
 
   /**
    * @task data
    */
   public function getExists($name) {
     return array_key_exists($name, $this->requestData);
   }
 
   public function getFileExists($name) {
     return isset($_FILES[$name]) &&
            (idx($_FILES[$name], 'error') !== UPLOAD_ERR_NO_FILE);
   }
 
   public function isHTTPGet() {
     return ($_SERVER['REQUEST_METHOD'] == 'GET');
   }
 
   public function isHTTPPost() {
     return ($_SERVER['REQUEST_METHOD'] == 'POST');
   }
 
   public function isAjax() {
     return $this->getExists(self::TYPE_AJAX) && !$this->isQuicksand();
   }
 
   public function isWorkflow() {
     return $this->getExists(self::TYPE_WORKFLOW) && !$this->isQuicksand();
   }
 
   public function isQuicksand() {
     return $this->getExists(self::TYPE_QUICKSAND);
   }
 
   public function isConduit() {
     return $this->getExists(self::TYPE_CONDUIT);
   }
 
   public static function getCSRFTokenName() {
     return '__csrf__';
   }
 
   public static function getCSRFHeaderName() {
     return 'X-Phabricator-Csrf';
   }
 
   public static function getViaHeaderName() {
     return 'X-Phabricator-Via';
   }
 
   public function validateCSRF() {
     $token_name = self::getCSRFTokenName();
     $token = $this->getStr($token_name);
 
     // No token in the request, check the HTTP header which is added for Ajax
     // requests.
     if (empty($token)) {
       $token = self::getHTTPHeader(self::getCSRFHeaderName());
     }
 
     $valid = $this->getUser()->validateCSRFToken($token);
     if (!$valid) {
 
       // Add some diagnostic details so we can figure out if some CSRF issues
       // are JS problems or people accessing Ajax URIs directly with their
       // browsers.
       $more_info = array();
 
       if ($this->isAjax()) {
         $more_info[] = pht('This was an Ajax request.');
       } else {
         $more_info[] = pht('This was a Web request.');
       }
 
       if ($token) {
         $more_info[] = pht('This request had an invalid CSRF token.');
       } else {
         $more_info[] = pht('This request had no CSRF token.');
       }
 
       // Give a more detailed explanation of how to avoid the exception
       // in developer mode.
       if (PhabricatorEnv::getEnvConfig('phabricator.developer-mode')) {
         // TODO: Clean this up, see T1921.
         $more_info[] = pht(
           "To avoid this error, use %s to construct forms. If you are already ".
           "using %s, make sure the form 'action' uses a relative URI (i.e., ".
           "begins with a '%s'). Forms using absolute URIs do not include CSRF ".
           "tokens, to prevent leaking tokens to external sites.\n\n".
           "If this page performs writes which do not require CSRF protection ".
           "(usually, filling caches or logging), you can use %s to ".
           "temporarily bypass CSRF protection while writing. You should use ".
           "this only for writes which can not be protected with normal CSRF ".
           "mechanisms.\n\n".
           "Some UI elements (like %s) also have methods which will allow you ".
           "to render links as forms (like %s).",
           'phabricator_form()',
           'phabricator_form()',
           '/',
           'AphrontWriteGuard::beginScopedUnguardedWrites()',
           'PhabricatorActionListView',
           'setRenderAsForm(true)');
       }
 
       // This should only be able to happen if you load a form, pull your
       // internet for 6 hours, and then reconnect and immediately submit,
       // but give the user some indication of what happened since the workflow
       // is incredibly confusing otherwise.
       throw new AphrontCSRFException(
         pht(
           'You are trying to save some data to Phabricator, but the request '.
           'your browser made included an incorrect token. Reload the page '.
           'and try again. You may need to clear your cookies.')."\n\n".
           implode("\n", $more_info));
     }
 
     return true;
   }
 
   public function isFormPost() {
     $post = $this->getExists(self::TYPE_FORM) &&
             !$this->getExists(self::TYPE_HISEC) &&
             $this->isHTTPPost();
 
     if (!$post) {
       return false;
     }
 
     return $this->validateCSRF();
   }
 
   public function isFormOrHisecPost() {
     $post = $this->getExists(self::TYPE_FORM) &&
             $this->isHTTPPost();
 
     if (!$post) {
       return false;
     }
 
     return $this->validateCSRF();
   }
 
 
   public function setCookiePrefix($prefix) {
     $this->cookiePrefix = $prefix;
     return $this;
   }
 
   private function getPrefixedCookieName($name) {
     if (strlen($this->cookiePrefix)) {
       return $this->cookiePrefix.'_'.$name;
     } else {
       return $name;
     }
   }
 
   public function getCookie($name, $default = null) {
     $name = $this->getPrefixedCookieName($name);
     $value = idx($_COOKIE, $name, $default);
 
     // Internally, PHP deletes cookies by setting them to the value 'deleted'
     // with an expiration date in the past.
 
     // At least in Safari, the browser may send this cookie anyway in some
     // circumstances. After logging out, the 302'd GET to /login/ consistently
     // includes deleted cookies on my local install. If a cookie value is
     // literally 'deleted', pretend it does not exist.
 
     if ($value === 'deleted') {
       return null;
     }
 
     return $value;
   }
 
   public function clearCookie($name) {
     $this->setCookieWithExpiration($name, '', time() - (60 * 60 * 24 * 30));
     unset($_COOKIE[$name]);
   }
 
   /**
    * Get the domain which cookies should be set on for this request, or null
    * if the request does not correspond to a valid cookie domain.
    *
    * @return PhutilURI|null   Domain URI, or null if no valid domain exists.
    *
    * @task cookie
    */
   private function getCookieDomainURI() {
     if (PhabricatorEnv::getEnvConfig('security.require-https') &&
         !$this->isHTTPS()) {
       return null;
     }
 
     $host = $this->getHost();
 
     // If there's no base domain configured, just use whatever the request
     // domain is. This makes setup easier, and we'll tell administrators to
     // configure a base domain during the setup process.
     $base_uri = PhabricatorEnv::getEnvConfig('phabricator.base-uri');
     if (!strlen($base_uri)) {
       return new PhutilURI('http://'.$host.'/');
     }
 
     $alternates = PhabricatorEnv::getEnvConfig('phabricator.allowed-uris');
     $allowed_uris = array_merge(
       array($base_uri),
       $alternates);
 
     foreach ($allowed_uris as $allowed_uri) {
       $uri = new PhutilURI($allowed_uri);
       if ($uri->getDomain() == $host) {
         return $uri;
       }
     }
 
     return null;
   }
 
   /**
    * Determine if security policy rules will allow cookies to be set when
    * responding to the request.
    *
    * @return bool True if setCookie() will succeed. If this method returns
    *              false, setCookie() will throw.
    *
    * @task cookie
    */
   public function canSetCookies() {
     return (bool)$this->getCookieDomainURI();
   }
 
 
   /**
    * Set a cookie which does not expire for a long time.
    *
    * To set a temporary cookie, see @{method:setTemporaryCookie}.
    *
    * @param string  Cookie name.
    * @param string  Cookie value.
    * @return this
    * @task cookie
    */
   public function setCookie($name, $value) {
     $far_future = time() + (60 * 60 * 24 * 365 * 5);
     return $this->setCookieWithExpiration($name, $value, $far_future);
   }
 
 
   /**
    * Set a cookie which expires soon.
    *
    * To set a durable cookie, see @{method:setCookie}.
    *
    * @param string  Cookie name.
    * @param string  Cookie value.
    * @return this
    * @task cookie
    */
   public function setTemporaryCookie($name, $value) {
     return $this->setCookieWithExpiration($name, $value, 0);
   }
 
 
   /**
    * Set a cookie with a given expiration policy.
    *
    * @param string  Cookie name.
    * @param string  Cookie value.
    * @param int     Epoch timestamp for cookie expiration.
    * @return this
    * @task cookie
    */
   private function setCookieWithExpiration(
     $name,
     $value,
     $expire) {
 
     $is_secure = false;
 
     $base_domain_uri = $this->getCookieDomainURI();
     if (!$base_domain_uri) {
       $configured_as = PhabricatorEnv::getEnvConfig('phabricator.base-uri');
       $accessed_as = $this->getHost();
 
       throw new Exception(
         pht(
           'This Phabricator install is configured as "%s", but you are '.
           'using the domain name "%s" to access a page which is trying to '.
-          'set a cookie. Acccess Phabricator on the configured primary '.
+          'set a cookie. Access Phabricator on the configured primary '.
           'domain or a configured alternate domain. Phabricator will not '.
           'set cookies on other domains for security reasons.',
           $configured_as,
           $accessed_as));
     }
 
     $base_domain = $base_domain_uri->getDomain();
     $is_secure = ($base_domain_uri->getProtocol() == 'https');
 
     $name = $this->getPrefixedCookieName($name);
 
     if (php_sapi_name() == 'cli') {
       // Do nothing, to avoid triggering "Cannot modify header information"
       // warnings.
 
       // TODO: This is effectively a test for whether we're running in a unit
       // test or not. Move this actual call to HTTPSink?
     } else {
       setcookie(
         $name,
         $value,
         $expire,
         $path = '/',
         $base_domain,
         $is_secure,
         $http_only = true);
     }
 
     $_COOKIE[$name] = $value;
 
     return $this;
   }
 
   public function setUser($user) {
     $this->user = $user;
     return $this;
   }
 
   public function getUser() {
     return $this->user;
   }
 
   public function getViewer() {
     return $this->user;
   }
 
   public function getRequestURI() {
     $get = $_GET;
     unset($get['__path__']);
     $path = phutil_escape_uri($this->getPath());
     return id(new PhutilURI($path))->setQueryParams($get);
   }
 
   public function isDialogFormPost() {
     return $this->isFormPost() && $this->getStr('__dialog__');
   }
 
   public function getRemoteAddress() {
     $address = $_SERVER['REMOTE_ADDR'];
     if (!strlen($address)) {
       return null;
     }
     return substr($address, 0, 64);
   }
 
   public function isHTTPS() {
     if (empty($_SERVER['HTTPS'])) {
       return false;
     }
     if (!strcasecmp($_SERVER['HTTPS'], 'off')) {
       return false;
     }
     return true;
   }
 
   public function isContinueRequest() {
     return $this->isFormPost() && $this->getStr('__continue__');
   }
 
   public function isPreviewRequest() {
     return $this->isFormPost() && $this->getStr('__preview__');
   }
 
   /**
    * Get application request parameters in a flattened form suitable for
    * inclusion in an HTTP request, excluding parameters with special meanings.
    * This is primarily useful if you want to ask the user for more input and
    * then resubmit their request.
    *
    * @return  dict<string, string>  Original request parameters.
    */
   public function getPassthroughRequestParameters($include_quicksand = false) {
     return self::flattenData(
       $this->getPassthroughRequestData($include_quicksand));
   }
 
   /**
    * Get request data other than "magic" parameters.
    *
    * @return dict<string, wild> Request data, with magic filtered out.
    */
   public function getPassthroughRequestData($include_quicksand = false) {
     $data = $this->getRequestData();
 
     // Remove magic parameters like __dialog__ and __ajax__.
     foreach ($data as $key => $value) {
       if ($include_quicksand && $key == self::TYPE_QUICKSAND) {
         continue;
       }
       if (!strncmp($key, '__', 2)) {
         unset($data[$key]);
       }
     }
 
     return $data;
   }
 
 
   /**
    * Flatten an array of key-value pairs (possibly including arrays as values)
    * into a list of key-value pairs suitable for submitting via HTTP request
    * (with arrays flattened).
    *
    * @param   dict<string, wild>    Data to flatten.
    * @return  dict<string, string>  Flat data suitable for inclusion in an HTTP
    *                                request.
    */
   public static function flattenData(array $data) {
     $result = array();
     foreach ($data as $key => $value) {
       if (is_array($value)) {
         foreach (self::flattenData($value) as $fkey => $fvalue) {
           $fkey = '['.preg_replace('/(?=\[)|$/', ']', $fkey, $limit = 1);
           $result[$key.$fkey] = $fvalue;
         }
       } else {
         $result[$key] = (string)$value;
       }
     }
 
     ksort($result);
 
     return $result;
   }
 
 
   /**
    * Read the value of an HTTP header from `$_SERVER`, or a similar datasource.
    *
    * This function accepts a canonical header name, like `"Accept-Encoding"`,
    * and looks up the appropriate value in `$_SERVER` (in this case,
    * `"HTTP_ACCEPT_ENCODING"`).
    *
    * @param   string        Canonical header name, like `"Accept-Encoding"`.
    * @param   wild          Default value to return if header is not present.
    * @param   array?        Read this instead of `$_SERVER`.
    * @return  string|wild   Header value if present, or `$default` if not.
    */
   public static function getHTTPHeader($name, $default = null, $data = null) {
     // PHP mangles HTTP headers by uppercasing them and replacing hyphens with
     // underscores, then prepending 'HTTP_'.
     $php_index = strtoupper($name);
     $php_index = str_replace('-', '_', $php_index);
 
     $try_names = array();
 
     $try_names[] = 'HTTP_'.$php_index;
     if ($php_index == 'CONTENT_TYPE' || $php_index == 'CONTENT_LENGTH') {
       // These headers may be available under alternate names. See
       // http://www.php.net/manual/en/reserved.variables.server.php#110763
       $try_names[] = $php_index;
     }
 
     if ($data === null) {
       $data = $_SERVER;
     }
 
     foreach ($try_names as $try_name) {
       if (array_key_exists($try_name, $data)) {
         return $data[$try_name];
       }
     }
 
     return $default;
   }
 
 
 /* -(  Working With a Phabricator Cluster  )--------------------------------- */
 
 
   /**
    * Is this a proxied request originating from within the Phabricator cluster?
    *
    * IMPORTANT: This means the request is dangerous!
    *
    * These requests are **more dangerous** than normal requests (they can not
    * be safely proxied, because proxying them may cause a loop). Cluster
    * requests are not guaranteed to come from a trusted source, and should
    * never be treated as safer than normal requests. They are strictly less
    * safe.
    */
   public function isProxiedClusterRequest() {
     return (bool)self::getHTTPHeader('X-Phabricator-Cluster');
   }
 
 
   /**
    * Build a new @{class:HTTPSFuture} which proxies this request to another
    * node in the cluster.
    *
    * IMPORTANT: This is very dangerous!
    *
    * The future forwards authentication information present in the request.
    * Proxied requests must only be sent to trusted hosts. (We attempt to
    * enforce this.)
    *
    * This is not a general-purpose proxying method; it is a specialized
    * method with niche applications and severe security implications.
    *
    * @param string URI identifying the host we are proxying the request to.
    * @return HTTPSFuture New proxy future.
    *
    * @phutil-external-symbol class PhabricatorStartup
    */
   public function newClusterProxyFuture($uri) {
     $uri = new PhutilURI($uri);
 
     $domain = $uri->getDomain();
     $ip = gethostbyname($domain);
     if (!$ip) {
       throw new Exception(
         pht(
           'Unable to resolve domain "%s"!',
           $domain));
     }
 
     if (!PhabricatorEnv::isClusterAddress($ip)) {
       throw new Exception(
         pht(
           'Refusing to proxy a request to IP address ("%s") which is not '.
           'in the cluster address block (this address was derived by '.
           'resolving the domain "%s").',
           $ip,
           $domain));
     }
 
     $uri->setPath($this->getPath());
     $uri->setQueryParams(self::flattenData($_GET));
 
     $input = PhabricatorStartup::getRawInput();
 
     $future = id(new HTTPSFuture($uri))
       ->addHeader('Host', self::getHost())
       ->addHeader('X-Phabricator-Cluster', true)
       ->setMethod($_SERVER['REQUEST_METHOD'])
       ->write($input);
 
     if (isset($_SERVER['PHP_AUTH_USER'])) {
       $future->setHTTPBasicAuthCredentials(
         $_SERVER['PHP_AUTH_USER'],
         new PhutilOpaqueEnvelope(idx($_SERVER, 'PHP_AUTH_PW', '')));
     }
 
     $headers = array();
     $seen = array();
 
     // NOTE: apache_request_headers() might provide a nicer way to do this,
     // but isn't available under FCGI until PHP 5.4.0.
     foreach ($_SERVER as $key => $value) {
       if (!preg_match('/^HTTP_/', $key)) {
         continue;
       }
 
       // Unmangle the header as best we can.
       $key = substr($key, strlen('HTTP_'));
       $key = str_replace('_', ' ', $key);
       $key = strtolower($key);
       $key = ucwords($key);
       $key = str_replace(' ', '-', $key);
 
       // By default, do not forward headers.
       $should_forward = false;
 
       // Forward "X-Hgarg-..." headers.
       if (preg_match('/^X-Hgarg-/', $key)) {
         $should_forward = true;
       }
 
       if ($should_forward) {
         $headers[] = array($key, $value);
         $seen[$key] = true;
       }
     }
 
     // In some situations, this may not be mapped into the HTTP_X constants.
     // CONTENT_LENGTH is similarly affected, but we trust cURL to take care
     // of that if it matters, since we're handing off a request body.
     if (empty($seen['Content-Type'])) {
       if (isset($_SERVER['CONTENT_TYPE'])) {
         $headers[] = array('Content-Type', $_SERVER['CONTENT_TYPE']);
       }
     }
 
     foreach ($headers as $header) {
       list($key, $value) = $header;
       switch ($key) {
         case 'Host':
         case 'Authorization':
           // Don't forward these headers, we've already handled them elsewhere.
           unset($headers[$key]);
           break;
         default:
           break;
       }
     }
 
     foreach ($headers as $header) {
       list($key, $value) = $header;
       $future->addHeader($key, $value);
     }
 
     return $future;
   }
 
 
 }
diff --git a/src/applications/almanac/util/AlmanacKeys.php b/src/applications/almanac/util/AlmanacKeys.php
index 3f482416d..7ed9098e9 100644
--- a/src/applications/almanac/util/AlmanacKeys.php
+++ b/src/applications/almanac/util/AlmanacKeys.php
@@ -1,69 +1,77 @@
 <?php
 
 final class AlmanacKeys extends Phobject {
 
   public static function getKeyPath($key_name) {
     $root = dirname(phutil_get_library_root('phabricator'));
     $keys = $root.'/conf/keys/';
 
     return $keys.ltrim($key_name, '/');
   }
 
   public static function getDeviceID() {
+    // While running unit tests, ignore any configured device identity.
+    try {
+      PhabricatorTestCase::assertExecutingUnitTests();
+      return null;
+    } catch (Exception $ex) {
+      // Continue normally.
+    }
+
     $device_id_path = self::getKeyPath('device.id');
 
     if (Filesystem::pathExists($device_id_path)) {
       return trim(Filesystem::readFile($device_id_path));
     }
 
     return null;
   }
 
   public static function getLiveDevice() {
     $device_id = self::getDeviceID();
     if (!$device_id) {
       return null;
     }
 
     $cache = PhabricatorCaches::getRequestCache();
     $cache_key = 'almanac.device.self';
 
     $device = $cache->getKey($cache_key);
     if (!$device) {
       $viewer = PhabricatorUser::getOmnipotentUser();
       $device = id(new AlmanacDeviceQuery())
         ->setViewer($viewer)
         ->withNames(array($device_id))
         ->executeOne();
       if (!$device) {
         throw new Exception(
           pht(
             'This host has device ID "%s", but there is no corresponding '.
             'device record in Almanac.',
             $device_id));
       }
       $cache->setKey($cache_key, $device);
     }
 
     return $device;
   }
 
   public static function getClusterSSHUser() {
     // NOTE: When instancing, we currently use the SSH username to figure out
     // which instance you are connecting to. We can't use the host name because
     // we have no way to tell which host you think you're reaching: the SSH
     // protocol does not have a mechanism like a "Host" header.
     $username = PhabricatorEnv::getEnvConfig('cluster.instance');
     if (strlen($username)) {
       return $username;
     }
 
     $username = PhabricatorEnv::getEnvConfig('diffusion.ssh-user');
     if (strlen($username)) {
       return $username;
     }
 
     return null;
   }
 
 }
diff --git a/src/applications/base/PhabricatorApplication.php b/src/applications/base/PhabricatorApplication.php
index 0bbb38ff8..a667717dd 100644
--- a/src/applications/base/PhabricatorApplication.php
+++ b/src/applications/base/PhabricatorApplication.php
@@ -1,654 +1,654 @@
 <?php
 
 /**
  * @task  info  Application Information
  * @task  ui    UI Integration
  * @task  uri   URI Routing
  * @task  mail  Email integration
  * @task  fact  Fact Integration
  * @task  meta  Application Management
  */
 abstract class PhabricatorApplication
   extends Phobject
   implements PhabricatorPolicyInterface {
 
   const MAX_STATUS_ITEMS      = 100;
 
   const GROUP_CORE            = 'core';
   const GROUP_UTILITIES       = 'util';
   const GROUP_ADMIN           = 'admin';
   const GROUP_DEVELOPER       = 'developer';
 
   final public static function getApplicationGroups() {
     return array(
       self::GROUP_CORE          => pht('Core Applications'),
       self::GROUP_UTILITIES     => pht('Utilities'),
       self::GROUP_ADMIN         => pht('Administration'),
       self::GROUP_DEVELOPER     => pht('Developer Tools'),
     );
   }
 
 
 /* -(  Application Information  )-------------------------------------------- */
 
   abstract public function getName();
 
   public function getShortDescription() {
     return pht('%s Application', $this->getName());
   }
 
   final public function isInstalled() {
     if (!$this->canUninstall()) {
       return true;
     }
 
     $prototypes = PhabricatorEnv::getEnvConfig('phabricator.show-prototypes');
     if (!$prototypes && $this->isPrototype()) {
       return false;
     }
 
     $uninstalled = PhabricatorEnv::getEnvConfig(
       'phabricator.uninstalled-applications');
 
     return empty($uninstalled[get_class($this)]);
   }
 
 
   public function isPrototype() {
     return false;
   }
 
 
   /**
    * Return `true` if this application should never appear in application lists
    * in the UI. Primarily intended for unit test applications or other
    * pseudo-applications.
    *
    * Few applications should be unlisted. For most applications, use
    * @{method:isLaunchable} to hide them from main launch views instead.
    *
    * @return bool True to remove application from UI lists.
    */
   public function isUnlisted() {
     return false;
   }
 
 
   /**
    * Return `true` if this application is a normal application with a base
    * URI and a web interface.
    *
    * Launchable applications can be pinned to the home page, and show up in the
    * "Launcher" view of the Applications application. Making an application
    * unlauncahble prevents pinning and hides it from this view.
    *
    * Usually, an application should be marked unlaunchable if:
    *
    *   - it is available on every page anyway (like search); or
    *   - it does not have a web interface (like subscriptions); or
    *   - it is still pre-release and being intentionally buried.
    *
    * To hide applications more completely, use @{method:isUnlisted}.
    *
    * @return bool True if the application is launchable.
    */
   public function isLaunchable() {
     return true;
   }
 
 
   /**
    * Return `true` if this application should be pinned by default.
    *
    * Users who have not yet set preferences see a default list of applications.
    *
    * @param PhabricatorUser User viewing the pinned application list.
    * @return bool True if this application should be pinned by default.
    */
   public function isPinnedByDefault(PhabricatorUser $viewer) {
     return false;
   }
 
 
   /**
    * Returns true if an application is first-party (developed by Phacility)
    * and false otherwise.
    *
    * @return bool True if this application is developed by Phacility.
    */
   final public function isFirstParty() {
     $where = id(new ReflectionClass($this))->getFileName();
     $root = phutil_get_library_root('phabricator');
 
     if (!Filesystem::isDescendant($where, $root)) {
       return false;
     }
 
     if (Filesystem::isDescendant($where, $root.'/extensions')) {
       return false;
     }
 
     return true;
   }
 
   public function canUninstall() {
     return true;
   }
 
   final public function getPHID() {
     return 'PHID-APPS-'.get_class($this);
   }
 
   public function getTypeaheadURI() {
     return $this->isLaunchable() ? $this->getBaseURI() : null;
   }
 
   public function getBaseURI() {
     return null;
   }
 
   final public function getApplicationURI($path = '') {
     return $this->getBaseURI().ltrim($path, '/');
   }
 
   public function getIconURI() {
     return null;
   }
 
   public function getIcon() {
     return 'fa-puzzle-piece';
   }
 
   public function getApplicationOrder() {
     return PHP_INT_MAX;
   }
 
   public function getApplicationGroup() {
     return self::GROUP_CORE;
   }
 
   public function getTitleGlyph() {
     return null;
   }
 
   final public function getHelpMenuItems(PhabricatorUser $viewer) {
     $items = array();
 
     $articles = $this->getHelpDocumentationArticles($viewer);
     if ($articles) {
       $items[] = id(new PHUIListItemView())
         ->setType(PHUIListItemView::TYPE_LABEL)
         ->setName(pht('%s Documentation', $this->getName()));
       foreach ($articles as $article) {
         $item = id(new PHUIListItemView())
           ->setName($article['name'])
           ->setIcon('fa-book')
           ->setHref($article['href']);
 
         $items[] = $item;
       }
     }
 
     $command_specs = $this->getMailCommandObjects();
     if ($command_specs) {
       $items[] = id(new PHUIListItemView())
         ->setType(PHUIListItemView::TYPE_LABEL)
         ->setName(pht('Email Help'));
       foreach ($command_specs as $key => $spec) {
         $object = $spec['object'];
 
         $class = get_class($this);
         $href = '/applications/mailcommands/'.$class.'/'.$key.'/';
 
         $item = id(new PHUIListItemView())
           ->setName($spec['name'])
           ->setIcon('fa-envelope-o')
           ->setHref($href);
         $items[] = $item;
       }
     }
 
     return $items;
   }
 
   public function getHelpDocumentationArticles(PhabricatorUser $viewer) {
     return array();
   }
 
   public function getOverview() {
     return null;
   }
 
   public function getEventListeners() {
     return array();
   }
 
   public function getRemarkupRules() {
     return array();
   }
 
   public function getQuicksandURIPatternBlacklist() {
     return array();
   }
 
   public function getMailCommandObjects() {
     return array();
   }
 
 
 /* -(  URI Routing  )-------------------------------------------------------- */
 
 
   public function getRoutes() {
     return array();
   }
 
   public function getResourceRoutes() {
     return array();
   }
 
 
 /* -(  Email Integration  )-------------------------------------------------- */
 
 
   public function supportsEmailIntegration() {
     return false;
   }
 
   final protected function getInboundEmailSupportLink() {
     return PhabricatorEnv::getDocLink('Configuring Inbound Email');
   }
 
   public function getAppEmailBlurb() {
     throw new PhutilMethodNotImplementedException();
   }
 
 
 /* -(  Fact Integration  )--------------------------------------------------- */
 
 
   public function getFactObjectsForAnalysis() {
     return array();
   }
 
 
 /* -(  UI Integration  )----------------------------------------------------- */
 
 
   /**
    * Render status elements (like "3 Waiting Reviews") for application list
    * views. These provide a way to alert users to new or pending action items
    * in applications.
    *
    * @param PhabricatorUser Viewing user.
    * @return list<PhabricatorApplicationStatusView> Application status elements.
    * @task ui
    */
   public function loadStatus(PhabricatorUser $user) {
     return array();
   }
 
 
   /**
    * You can provide an optional piece of flavor text for the application. This
    * is currently rendered in application launch views if the application has no
    * status elements.
    *
    * @return string|null Flavor text.
    * @task ui
    */
   public function getFlavorText() {
     return null;
   }
 
 
   /**
    * Build items for the main menu.
    *
    * @param  PhabricatorUser    The viewing user.
    * @param  AphrontController  The current controller. May be null for special
    *                            pages like 404, exception handlers, etc.
    * @return list<PHUIListItemView> List of menu items.
    * @task ui
    */
   public function buildMainMenuItems(
     PhabricatorUser $user,
     PhabricatorController $controller = null) {
     return array();
   }
 
 
   /**
    * Build extra items for the main menu. Generally, this is used to render
    * static dropdowns.
    *
    * @param  PhabricatorUser    The viewing user.
    * @param  AphrontController  The current controller. May be null for special
    *                            pages like 404, exception handlers, etc.
    * @return view               List of menu items.
    * @task ui
    */
   public function buildMainMenuExtraNodes(
     PhabricatorUser $viewer,
     PhabricatorController $controller = null) {
     return array();
   }
 
 
   /**
    * Build items for the "quick create" menu.
    *
    * @param   PhabricatorUser         The viewing user.
    * @return  list<PHUIListItemView>  List of menu items.
    */
   public function getQuickCreateItems(PhabricatorUser $viewer) {
     return array();
   }
 
 
 /* -(  Application Management  )--------------------------------------------- */
 
 
   final public static function getByClass($class_name) {
     $selected = null;
     $applications = self::getAllApplications();
 
     foreach ($applications as $application) {
       if (get_class($application) == $class_name) {
         $selected = $application;
         break;
       }
     }
 
     if (!$selected) {
       throw new Exception(pht("No application '%s'!", $class_name));
     }
 
     return $selected;
   }
 
   final public static function getAllApplications() {
     static $applications;
 
     if ($applications === null) {
       $apps = id(new PhutilClassMapQuery())
         ->setAncestorClass(__CLASS__)
         ->setSortMethod('getApplicationOrder')
         ->execute();
 
       // Reorder the applications into "application order". Notably, this
       // ensures their event handlers register in application order.
       $apps = mgroup($apps, 'getApplicationGroup');
 
       $group_order = array_keys(self::getApplicationGroups());
       $apps = array_select_keys($apps, $group_order) + $apps;
 
       $apps = array_mergev($apps);
 
       $applications = $apps;
     }
 
     return $applications;
   }
 
   final public static function getAllInstalledApplications() {
     $all_applications = self::getAllApplications();
     $apps = array();
     foreach ($all_applications as $app) {
       if (!$app->isInstalled()) {
         continue;
       }
 
       $apps[] = $app;
     }
 
     return $apps;
   }
 
 
   /**
    * Determine if an application is installed, by application class name.
    *
    * To check if an application is installed //and// available to a particular
    * viewer, user @{method:isClassInstalledForViewer}.
    *
    * @param string  Application class name.
    * @return bool   True if the class is installed.
    * @task meta
    */
   final public static function isClassInstalled($class) {
     return self::getByClass($class)->isInstalled();
   }
 
 
   /**
    * Determine if an application is installed and available to a viewer, by
    * application class name.
    *
    * To check if an application is installed at all, use
    * @{method:isClassInstalled}.
    *
    * @param string Application class name.
    * @param PhabricatorUser Viewing user.
    * @return bool True if the class is installed for the viewer.
    * @task meta
    */
   final public static function isClassInstalledForViewer(
     $class,
     PhabricatorUser $viewer) {
 
     if ($viewer->isOmnipotent()) {
       return true;
     }
 
     $cache = PhabricatorCaches::getRequestCache();
     $viewer_phid = $viewer->getPHID();
     $key = 'app.'.$class.'.installed.'.$viewer_phid;
 
     $result = $cache->getKey($key);
     if ($result === null) {
       if (!self::isClassInstalled($class)) {
         $result = false;
       } else {
         $result = PhabricatorPolicyFilter::hasCapability(
           $viewer,
           self::getByClass($class),
           PhabricatorPolicyCapability::CAN_VIEW);
       }
 
       $cache->setKey($key, $result);
     }
 
     return $result;
   }
 
 
 /* -(  PhabricatorPolicyInterface  )----------------------------------------- */
 
 
   public function getCapabilities() {
     return array_merge(
       array(
         PhabricatorPolicyCapability::CAN_VIEW,
         PhabricatorPolicyCapability::CAN_EDIT,
       ),
       array_keys($this->getCustomCapabilities()));
   }
 
   public function getPolicy($capability) {
     $default = $this->getCustomPolicySetting($capability);
     if ($default) {
       return $default;
     }
 
     switch ($capability) {
       case PhabricatorPolicyCapability::CAN_VIEW:
         return PhabricatorPolicies::getMostOpenPolicy();
       case PhabricatorPolicyCapability::CAN_EDIT:
         return PhabricatorPolicies::POLICY_ADMIN;
       default:
         $spec = $this->getCustomCapabilitySpecification($capability);
         return idx($spec, 'default', PhabricatorPolicies::POLICY_USER);
     }
   }
 
   public function hasAutomaticCapability($capability, PhabricatorUser $viewer) {
     return false;
   }
 
   public function describeAutomaticCapability($capability) {
     return null;
   }
 
 
 /* -(  Policies  )----------------------------------------------------------- */
 
   protected function getCustomCapabilities() {
     return array();
   }
 
   final private function getCustomPolicySetting($capability) {
     if (!$this->isCapabilityEditable($capability)) {
       return null;
     }
 
     $policy_locked = PhabricatorEnv::getEnvConfig('policy.locked');
     if (isset($policy_locked[$capability])) {
       return $policy_locked[$capability];
     }
 
     $config = PhabricatorEnv::getEnvConfig('phabricator.application-settings');
 
     $app = idx($config, $this->getPHID());
     if (!$app) {
       return null;
     }
 
     $policy = idx($app, 'policy');
     if (!$policy) {
       return null;
     }
 
     return idx($policy, $capability);
   }
 
 
   final private function getCustomCapabilitySpecification($capability) {
     $custom = $this->getCustomCapabilities();
     if (!isset($custom[$capability])) {
       throw new Exception(pht("Unknown capability '%s'!", $capability));
     }
     return $custom[$capability];
   }
 
   final public function getCapabilityLabel($capability) {
     switch ($capability) {
       case PhabricatorPolicyCapability::CAN_VIEW:
         return pht('Can Use Application');
       case PhabricatorPolicyCapability::CAN_EDIT:
         return pht('Can Configure Application');
     }
 
     $capobj = PhabricatorPolicyCapability::getCapabilityByKey($capability);
     if ($capobj) {
       return $capobj->getCapabilityName();
     }
 
     return null;
   }
 
   final public function isCapabilityEditable($capability) {
     switch ($capability) {
       case PhabricatorPolicyCapability::CAN_VIEW:
         return $this->canUninstall();
       case PhabricatorPolicyCapability::CAN_EDIT:
         return false;
       default:
         $spec = $this->getCustomCapabilitySpecification($capability);
         return idx($spec, 'edit', true);
     }
   }
 
   final public function getCapabilityCaption($capability) {
     switch ($capability) {
       case PhabricatorPolicyCapability::CAN_VIEW:
         if (!$this->canUninstall()) {
           return pht(
             'This application is required for Phabricator to operate, so all '.
             'users must have access to it.');
         } else {
           return null;
         }
       case PhabricatorPolicyCapability::CAN_EDIT:
         return null;
       default:
         $spec = $this->getCustomCapabilitySpecification($capability);
         return idx($spec, 'caption');
     }
   }
 
   final public function getCapabilityTemplatePHIDType($capability) {
     switch ($capability) {
       case PhabricatorPolicyCapability::CAN_VIEW:
       case PhabricatorPolicyCapability::CAN_EDIT:
         return null;
     }
 
     $spec = $this->getCustomCapabilitySpecification($capability);
     return idx($spec, 'template');
   }
 
   final public function getDefaultObjectTypePolicyMap() {
     $map = array();
 
     foreach ($this->getCustomCapabilities() as $capability => $spec) {
       if (empty($spec['template'])) {
         continue;
       }
       if (empty($spec['capability'])) {
         continue;
       }
       $default = $this->getPolicy($capability);
       $map[$spec['template']][$spec['capability']] = $default;
     }
 
     return $map;
   }
 
   public function getApplicationSearchDocumentTypes() {
     return array();
   }
 
   protected function getEditRoutePattern($base = null) {
     return $base.'(?:'.
       '(?P<id>[0-9]\d*)/)?'.
       '(?:'.
         '(?:'.
-          '(?P<editAction>parameters|nodefault|nocreate|nomanage|comment)'.
+          '(?P<editAction>parameters|nodefault|nocreate|nomanage|comment)/'.
           '|'.
-          '(?:form/(?P<formKey>[^/]+))'.
+          '(?:form/(?P<formKey>[^/]+)/)?(?:page/(?P<pageKey>[^/]+)/)?'.
         ')'.
-      '/)?';
+      ')?';
   }
 
   protected function getQueryRoutePattern($base = null) {
     return $base.'(?:query/(?P<queryKey>[^/]+)/)?';
   }
 
   protected function getPanelRouting($controller) {
     $edit_route = $this->getEditRoutePattern();
 
     return array(
       '(?P<panelAction>view)/(?P<panelID>[^/]+)/' => $controller,
       '(?P<panelAction>hide)/(?P<panelID>[^/]+)/' => $controller,
       '(?P<panelAction>default)/(?P<panelID>[^/]+)/' => $controller,
       '(?P<panelAction>configure)/' => $controller,
       '(?P<panelAction>reorder)/' => $controller,
       '(?P<panelAction>edit)/'.$edit_route => $controller,
       '(?P<panelAction>new)/(?<panelKey>[^/]+)/'.$edit_route => $controller,
       '(?P<panelAction>builtin)/(?<panelID>[^/]+)/'.$edit_route
         => $controller,
     );
   }
 
 }
diff --git a/src/applications/celerity/management/CelerityManagementSyntaxWorkflow.php b/src/applications/celerity/management/CelerityManagementSyntaxWorkflow.php
new file mode 100644
index 000000000..d9e4ee8ee
--- /dev/null
+++ b/src/applications/celerity/management/CelerityManagementSyntaxWorkflow.php
@@ -0,0 +1,67 @@
+<?php
+
+final class CelerityManagementSyntaxWorkflow
+  extends CelerityManagementWorkflow {
+
+  protected function didConstruct() {
+    $this
+      ->setName('syntax')
+      ->setExamples('**syntax** [options]')
+      ->setSynopsis(pht('Rebuild syntax highlighting CSS.'))
+      ->setArguments(
+        array());
+  }
+
+  public function execute(PhutilArgumentParser $args) {
+    $styles = PhabricatorSyntaxStyle::getAllStyles();
+
+    $root = dirname(phutil_get_library_root('phabricator'));
+    $root = $root.'/webroot/rsrc/css/syntax/';
+
+    foreach ($styles as $key => $style) {
+      $content = $this->generateCSS($style);
+      $path = $root.'/syntax-'.$key.'.css';
+      Filesystem::writeFile($path, $content);
+
+      echo tsprintf(
+        "%s\n",
+        pht(
+          'Rebuilt "%s" syntax CSS.',
+          basename($path)));
+    }
+
+    return 0;
+  }
+
+  private function generateCSS(PhabricatorSyntaxStyle $style) {
+    $key = $style->getSyntaxStyleKey();
+    $provides = "syntax-{$key}-css";
+    $generated = 'generated';
+
+    $header =
+      "/**\n".
+      " * @provides {$provides}\n".
+      " * @{$generated}\n".
+      " */\n\n";
+
+    $groups = array();
+    $map = $style->getStyleMap();
+    ksort($map);
+    foreach ($map as $key => $value) {
+      $groups[$value][] = $key;
+    }
+
+    $rules = array();
+    foreach ($groups as $body => $classes) {
+      $parts = array();
+      foreach ($classes as $class) {
+        $parts[] = ".remarkup-code .{$class}";
+      }
+      $rules[] = implode(",\n", $parts)." {\n  {$body}\n}";
+    }
+    $rules = implode("\n\n", $rules);
+
+    return $header.$rules."\n";
+  }
+
+}
diff --git a/src/applications/conduit/parametertype/ConduitEpochParameterType.php b/src/applications/conduit/parametertype/ConduitEpochParameterType.php
index 9f906c9c3..1594186e5 100644
--- a/src/applications/conduit/parametertype/ConduitEpochParameterType.php
+++ b/src/applications/conduit/parametertype/ConduitEpochParameterType.php
@@ -1,42 +1,42 @@
 <?php
 
 final class ConduitEpochParameterType
-  extends ConduitListParameterType {
+  extends ConduitParameterType {
 
   protected function getParameterValue(array $request, $key) {
     $value = parent::getParameterValue($request, $key);
 
     if (!is_int($value)) {
       $this->raiseValidationException(
         $request,
         $key,
         pht('Expected epoch timestamp as integer, got something else.'));
     }
 
     if ($value <= 0) {
       $this->raiseValidationException(
         $request,
         $key,
         pht('Epoch timestamp must be larger than 0, got %d.', $value));
     }
 
     return $value;
   }
 
   protected function getParameterTypeName() {
     return 'epoch';
   }
 
   protected function getParameterFormatDescriptions() {
     return array(
       pht('Epoch timestamp, as an integer.'),
     );
   }
 
   protected function getParameterExamples() {
     return array(
       '1450019509',
     );
   }
 
 }
diff --git a/src/applications/conduit/parametertype/ConduitWildParameterType.php b/src/applications/conduit/parametertype/ConduitWildParameterType.php
index c2e43a14e..a89e7a649 100644
--- a/src/applications/conduit/parametertype/ConduitWildParameterType.php
+++ b/src/applications/conduit/parametertype/ConduitWildParameterType.php
@@ -1,22 +1,22 @@
 <?php
 
 final class ConduitWildParameterType
-  extends ConduitListParameterType {
+  extends ConduitParameterType {
 
   protected function getParameterTypeName() {
     return 'wild';
   }
 
   protected function getParameterFormatDescriptions() {
     return array(
       pht('Any mixed or complex value. Check the documentation for details.'),
     );
   }
 
   protected function getParameterExamples() {
     return array(
       pht('(Wildcard)'),
     );
   }
 
 }
diff --git a/src/applications/config/check/PhabricatorExtraConfigSetupCheck.php b/src/applications/config/check/PhabricatorExtraConfigSetupCheck.php
index 5e5b49359..602d756d0 100644
--- a/src/applications/config/check/PhabricatorExtraConfigSetupCheck.php
+++ b/src/applications/config/check/PhabricatorExtraConfigSetupCheck.php
@@ -1,383 +1,387 @@
 <?php
 
 final class PhabricatorExtraConfigSetupCheck extends PhabricatorSetupCheck {
 
   public function getDefaultGroup() {
     return self::GROUP_OTHER;
   }
 
   protected function executeChecks() {
     $ancient_config = self::getAncientConfig();
 
     $all_keys = PhabricatorEnv::getAllConfigKeys();
     $all_keys = array_keys($all_keys);
     sort($all_keys);
 
     $defined_keys = PhabricatorApplicationConfigOptions::loadAllOptions();
 
     foreach ($all_keys as $key) {
       if (isset($defined_keys[$key])) {
         continue;
       }
 
       if (isset($ancient_config[$key])) {
         $summary = pht(
           'This option has been removed. You may delete it at your '.
           'convenience.');
         $message = pht(
           "The configuration option '%s' has been removed. You may delete ".
           "it at your convenience.".
           "\n\n%s",
           $key,
           $ancient_config[$key]);
         $short = pht('Obsolete Config');
         $name = pht('Obsolete Configuration Option "%s"', $key);
       } else {
         $summary = pht('This option is not recognized. It may be misspelled.');
         $message = pht(
           "The configuration option '%s' is not recognized. It may be ".
           "misspelled, or it might have existed in an older version of ".
           "Phabricator. It has no effect, and should be corrected or deleted.",
           $key);
         $short = pht('Unknown Config');
         $name = pht('Unknown Configuration Option "%s"', $key);
       }
 
       $issue = $this->newIssue('config.unknown.'.$key)
         ->setShortName($short)
         ->setName($name)
         ->setSummary($summary);
 
       $stack = PhabricatorEnv::getConfigSourceStack();
       $stack = $stack->getStack();
 
       $found = array();
       $found_local = false;
       $found_database = false;
 
       foreach ($stack as $source_key => $source) {
         $value = $source->getKeys(array($key));
         if ($value) {
           $found[] = $source->getName();
           if ($source instanceof PhabricatorConfigDatabaseSource) {
             $found_database = true;
           }
           if ($source instanceof PhabricatorConfigLocalSource) {
             $found_local = true;
           }
         }
       }
 
       $message = $message."\n\n".pht(
         'This configuration value is defined in these %d '.
         'configuration source(s): %s.',
         count($found),
         implode(', ', $found));
       $issue->setMessage($message);
 
       if ($found_local) {
         $command = csprintf('phabricator/ $ ./bin/config delete %s', $key);
         $issue->addCommand($command);
       }
 
       if ($found_database) {
         $issue->addPhabricatorConfig($key);
       }
     }
 
     $this->executeManiphestFieldChecks();
   }
 
   /**
    * Return a map of deleted config options. Keys are option keys; values are
    * explanations of what happened to the option.
    */
   public static function getAncientConfig() {
     $reason_auth = pht(
       'This option has been migrated to the "Auth" application. Your old '.
       'configuration is still in effect, but now stored in "Auth" instead of '.
       'configuration. Going forward, you can manage authentication from '.
       'the web UI.');
 
     $auth_config = array(
       'controller.oauth-registration',
       'auth.password-auth-enabled',
       'facebook.auth-enabled',
       'facebook.registration-enabled',
       'facebook.auth-permanent',
       'facebook.application-id',
       'facebook.application-secret',
       'facebook.require-https-auth',
       'github.auth-enabled',
       'github.registration-enabled',
       'github.auth-permanent',
       'github.application-id',
       'github.application-secret',
       'google.auth-enabled',
       'google.registration-enabled',
       'google.auth-permanent',
       'google.application-id',
       'google.application-secret',
       'ldap.auth-enabled',
       'ldap.hostname',
       'ldap.port',
       'ldap.base_dn',
       'ldap.search_attribute',
       'ldap.search-first',
       'ldap.username-attribute',
       'ldap.real_name_attributes',
       'ldap.activedirectory_domain',
       'ldap.version',
       'ldap.referrals',
       'ldap.anonymous-user-name',
       'ldap.anonymous-user-password',
       'ldap.start-tls',
       'disqus.auth-enabled',
       'disqus.registration-enabled',
       'disqus.auth-permanent',
       'disqus.application-id',
       'disqus.application-secret',
       'phabricator.oauth-uri',
       'phabricator.auth-enabled',
       'phabricator.registration-enabled',
       'phabricator.auth-permanent',
       'phabricator.application-id',
       'phabricator.application-secret',
     );
 
     $ancient_config = array_fill_keys($auth_config, $reason_auth);
 
     $markup_reason = pht(
       'Custom remarkup rules are now added by subclassing '.
       '%s or %s.',
       'PhabricatorRemarkupCustomInlineRule',
       'PhabricatorRemarkupCustomBlockRule');
 
     $session_reason = pht(
       'Sessions now expire and are garbage collected rather than having an '.
       'arbitrary concurrency limit.');
 
     $differential_field_reason = pht(
       'All Differential fields are now managed through the configuration '.
       'option "%s". Use that option to configure which fields are shown.',
       'differential.fields');
 
     $reply_domain_reason = pht(
       'Individual application reply handler domains have been removed. '.
       'Configure a reply domain with "%s".',
       'metamta.reply-handler-domain');
 
     $reply_handler_reason = pht(
       'Reply handlers can no longer be overridden with configuration.');
 
     $monospace_reason = pht(
       'Phabricator no longer supports global customization of monospaced '.
       'fonts.');
 
     $public_mail_reason = pht(
       'Inbound mail addresses are now configured for each application '.
       'in the Applications tool.');
 
     $gc_reason = pht(
       'Garbage collectors are now configured with "%s".',
       'bin/garbage set-policy');
 
     $aphlict_reason = pht(
       'Configuration of the notification server has changed substantially. '.
       'For discussion, see T10794.');
 
     $ancient_config += array(
       'phid.external-loaders' =>
         pht(
           'External loaders have been replaced. Extend `%s` '.
           'to implement new PHID and handle types.',
           'PhabricatorPHIDType'),
       'maniphest.custom-task-extensions-class' =>
         pht(
           'Maniphest fields are now loaded automatically. '.
           'You can configure them with `%s`.',
           'maniphest.fields'),
       'maniphest.custom-fields' =>
         pht(
           'Maniphest fields are now defined in `%s`. '.
           'Existing definitions have been migrated.',
           'maniphest.custom-field-definitions'),
       'differential.custom-remarkup-rules' => $markup_reason,
       'differential.custom-remarkup-block-rules' => $markup_reason,
       'auth.sshkeys.enabled' => pht(
         'SSH keys are now actually useful, so they are always enabled.'),
       'differential.anonymous-access' => pht(
         'Phabricator now has meaningful global access controls. See `%s`.',
         'policy.allow-public'),
       'celerity.resource-path' => pht(
         'An alternate resource map is no longer supported. Instead, use '.
         'multiple maps. See T4222.'),
       'metamta.send-immediately' => pht(
         'Mail is now always delivered by the daemons.'),
       'auth.sessions.conduit' => $session_reason,
       'auth.sessions.web' => $session_reason,
       'tokenizer.ondemand' => pht(
         'Phabricator now manages typeahead strategies automatically.'),
       'differential.revision-custom-detail-renderer' => pht(
         'Obsolete; use standard rendering events instead.'),
       'differential.show-host-field' => $differential_field_reason,
       'differential.show-test-plan-field' => $differential_field_reason,
       'differential.field-selector' => $differential_field_reason,
       'phabricator.show-beta-applications' => pht(
         'This option has been renamed to `%s` to emphasize the '.
         'unfinished nature of many prototype applications. '.
         'Your existing setting has been migrated.',
         'phabricator.show-prototypes'),
       'notification.user' => pht(
         'The notification server no longer requires root permissions. Start '.
         'the server as the user you want it to run under.'),
       'notification.debug' => pht(
         'Notifications no longer have a dedicated debugging mode.'),
       'translation.provider' => pht(
         'The translation implementation has changed and providers are no '.
         'longer used or supported.'),
       'config.mask' => pht(
         'Use `%s` instead of this option.',
         'config.hide'),
       'phd.start-taskmasters' => pht(
         'Taskmasters now use an autoscaling pool. You can configure the '.
         'pool size with `%s`.',
         'phd.taskmasters'),
       'storage.engine-selector' => pht(
         'Phabricator now automatically discovers available storage engines '.
         'at runtime.'),
       'storage.upload-size-limit' => pht(
         'Phabricator now supports arbitrarily large files. Consult the '.
         'documentation for configuration details.'),
       'security.allow-outbound-http' => pht(
         'This option has been replaced with the more granular option `%s`.',
         'security.outbound-blacklist'),
       'metamta.reply.show-hints' => pht(
         'Phabricator no longer shows reply hints in mail.'),
 
       'metamta.differential.reply-handler-domain' => $reply_domain_reason,
       'metamta.diffusion.reply-handler-domain' => $reply_domain_reason,
       'metamta.macro.reply-handler-domain' => $reply_domain_reason,
       'metamta.maniphest.reply-handler-domain' => $reply_domain_reason,
       'metamta.pholio.reply-handler-domain' => $reply_domain_reason,
 
       'metamta.diffusion.reply-handler' => $reply_handler_reason,
       'metamta.differential.reply-handler' => $reply_handler_reason,
       'metamta.maniphest.reply-handler' => $reply_handler_reason,
       'metamta.package.reply-handler' => $reply_handler_reason,
 
       'metamta.precedence-bulk' => pht(
         'Phabricator now always sends transaction mail with '.
         '"Precedence: bulk" to improve deliverability.'),
 
       'style.monospace' => $monospace_reason,
       'style.monospace.windows' => $monospace_reason,
 
       'search.engine-selector' => pht(
         'Phabricator now automatically discovers available search engines '.
         'at runtime.'),
 
       'metamta.files.public-create-email' => $public_mail_reason,
       'metamta.maniphest.public-create-email' => $public_mail_reason,
       'metamta.maniphest.default-public-author' => $public_mail_reason,
       'metamta.paste.public-create-email' => $public_mail_reason,
 
       'security.allow-conduit-act-as-user' => pht(
         'Impersonating users over the API is no longer supported.'),
 
       'feed.public' => pht('The framable public feed is no longer supported.'),
 
       'auth.login-message' => pht(
         'This configuration option has been replaced with a modular '.
         'handler. See T9346.'),
 
       'gcdaemon.ttl.herald-transcripts' => $gc_reason,
       'gcdaemon.ttl.daemon-logs' => $gc_reason,
       'gcdaemon.ttl.differential-parse-cache' => $gc_reason,
       'gcdaemon.ttl.markup-cache' => $gc_reason,
       'gcdaemon.ttl.task-archive' => $gc_reason,
       'gcdaemon.ttl.general-cache' => $gc_reason,
       'gcdaemon.ttl.conduit-logs' => $gc_reason,
 
       'phd.variant-config' => pht(
         'This configuration is no longer relevant because daemons '.
         'restart automatically on configuration changes.'),
 
       'notification.ssl-cert' => $aphlict_reason,
       'notification.ssl-key' => $aphlict_reason,
       'notification.pidfile' => $aphlict_reason,
       'notification.log' => $aphlict_reason,
       'notification.enabled' => $aphlict_reason,
       'notification.client-uri' => $aphlict_reason,
       'notification.server-uri' => $aphlict_reason,
+
+      'metamta.differential.unified-comment-context' => pht(
+        'Inline comments are now always rendered with a limited amount '.
+        'of context.'),
     );
 
     return $ancient_config;
   }
 
   private function executeManiphestFieldChecks() {
     $maniphest_appclass = 'PhabricatorManiphestApplication';
     if (!PhabricatorApplication::isClassInstalled($maniphest_appclass)) {
       return;
     }
 
     $capabilities = array(
       ManiphestEditAssignCapability::CAPABILITY,
       ManiphestEditPoliciesCapability::CAPABILITY,
       ManiphestEditPriorityCapability::CAPABILITY,
       ManiphestEditProjectsCapability::CAPABILITY,
       ManiphestEditStatusCapability::CAPABILITY,
     );
 
     // Check for any of these capabilities set to anything other than
     // "All Users".
 
     $any_set = false;
     $app = new PhabricatorManiphestApplication();
     foreach ($capabilities as $capability) {
       $setting = $app->getPolicy($capability);
       if ($setting != PhabricatorPolicies::POLICY_USER) {
         $any_set = true;
         break;
       }
     }
 
     if (!$any_set) {
       return;
     }
 
     $issue_summary = pht(
       'Maniphest is currently configured with deprecated policy settings '.
       'which will be removed in a future version of Phabricator.');
 
 
     $message = pht(
       'Some policy settings in Maniphest are now deprecated and will be '.
       'removed in a future version of Phabricator. You are currently using '.
       'at least one of these settings.'.
       "\n\n".
       'The deprecated settings are "Can Assign Tasks", '.
       '"Can Edit Task Policies", "Can Prioritize Tasks", '.
       '"Can Edit Task Projects", and "Can Edit Task Status". You can '.
       'find these settings in Applications, or follow the link below.'.
       "\n\n".
       'You can find discussion of this change (including rationale and '.
       'recommendations on how to configure similar features) in the upstream, '.
       'at the link below.'.
       "\n\n".
       'To resolve this issue, set all of these policies to "All Users" after '.
       'making any necessary form customization changes.');
 
     $more_href = 'https://secure.phabricator.com/T10003';
     $edit_href = '/applications/view/PhabricatorManiphestApplication/';
 
     $issue = $this->newIssue('maniphest.T10003-per-field-policies')
       ->setShortName(pht('Deprecated Policies'))
       ->setName(pht('Deprecated Maniphest Field Policies'))
       ->setSummary($issue_summary)
       ->setMessage($message)
       ->addLink($more_href, pht('Learn More: Upstream Discussion'))
       ->addLink($edit_href, pht('Edit These Settings'));
   }
 
 }
diff --git a/src/applications/differential/config/PhabricatorDifferentialConfigOptions.php b/src/applications/differential/config/PhabricatorDifferentialConfigOptions.php
index bd1aa9935..70f9fe77e 100644
--- a/src/applications/differential/config/PhabricatorDifferentialConfigOptions.php
+++ b/src/applications/differential/config/PhabricatorDifferentialConfigOptions.php
@@ -1,308 +1,293 @@
 <?php
 
 final class PhabricatorDifferentialConfigOptions
   extends PhabricatorApplicationConfigOptions {
 
   public function getName() {
     return pht('Differential');
   }
 
   public function getDescription() {
     return pht('Configure Differential code review.');
   }
 
   public function getIcon() {
     return 'fa-cog';
   }
 
   public function getGroup() {
     return 'apps';
   }
 
   public function getOptions() {
     $caches_href = PhabricatorEnv::getDoclink('Managing Caches');
 
     $custom_field_type = 'custom:PhabricatorCustomFieldConfigOptionType';
 
     $fields = array(
       new DifferentialNextStepField(),
 
       new DifferentialTitleField(),
       new DifferentialSummaryField(),
       new DifferentialTestPlanField(),
       new DifferentialAuthorField(),
       new DifferentialReviewersField(),
       new DifferentialProjectReviewersField(),
       new DifferentialReviewedByField(),
       new DifferentialSubscribersField(),
       new DifferentialRepositoryField(),
       new DifferentialProjectsField(),
       new DifferentialViewPolicyField(),
       new DifferentialEditPolicyField(),
 
       new DifferentialDependsOnField(),
       new DifferentialDependenciesField(),
       new DifferentialManiphestTasksField(),
       new DifferentialCommitsField(),
 
       new DifferentialJIRAIssuesField(),
       new DifferentialAsanaRepresentationField(),
 
       new DifferentialChangesSinceLastUpdateField(),
       new DifferentialBranchField(),
 
       new DifferentialBlameRevisionField(),
       new DifferentialPathField(),
       new DifferentialHostField(),
       new DifferentialLintField(),
       new DifferentialUnitField(),
       new DifferentialRevertPlanField(),
 
       new DifferentialApplyPatchField(),
 
       new DifferentialRevisionIDField(),
     );
 
     $default_fields = array();
     foreach ($fields as $field) {
       $default_fields[$field->getFieldKey()] = array(
         'disabled' => $field->shouldDisableByDefault(),
       );
     }
 
     return array(
       $this->newOption(
         'differential.fields',
         $custom_field_type,
         $default_fields)
         ->setCustomData(
           id(new DifferentialRevision())->getCustomFieldBaseClass())
         ->setDescription(
           pht(
             "Select and reorder revision fields.\n\n".
             "NOTE: This feature is under active development and subject ".
             "to change.")),
       $this->newOption(
         'differential.whitespace-matters',
         'list<regex>',
         array(
           '/\.py$/',
           '/\.l?hs$/',
         ))
         ->setDescription(
           pht(
             "List of file regexps where whitespace is meaningful and should ".
             "not use 'ignore-all' by default")),
       $this->newOption('differential.require-test-plan-field', 'bool', true)
         ->setBoolOptions(
           array(
             pht("Require 'Test Plan' field"),
             pht("Make 'Test Plan' field optional"),
           ))
         ->setSummary(pht('Require "Test Plan" field?'))
         ->setDescription(
           pht(
             "Differential has a required 'Test Plan' field by default. You ".
             "can make it optional by setting this to false. You can also ".
             "completely remove it above, if you prefer.")),
       $this->newOption('differential.enable-email-accept', 'bool', false)
         ->setBoolOptions(
           array(
             pht('Enable Email "!accept" Action'),
             pht('Disable Email "!accept" Action'),
           ))
         ->setSummary(pht('Enable or disable "!accept" action via email.'))
         ->setDescription(
           pht(
             'If inbound email is configured, users can interact with '.
             'revisions by using "!actions" in email replies (for example, '.
             '"!resign" or "!rethink"). However, by default, users may not '.
             '"!accept" revisions via email: email authentication can be '.
             'configured to be very weak, and email "!accept" is kind of '.
             'sketchy and implies the revision may not actually be receiving '.
             'thorough review. You can enable "!accept" by setting this '.
             'option to true.')),
       $this->newOption('differential.generated-paths', 'list<regex>', array())
         ->setSummary(pht('File regexps to treat as automatically generated.'))
         ->setDescription(
           pht(
             'List of file regexps that should be treated as if they are '.
             'generated by an automatic process, and thus be hidden by '.
             'default in Differential.'.
             "\n\n".
             'NOTE: This property is cached, so you will need to purge the '.
             'cache after making changes if you want the new configuration '.
             'to affect existing revisions. For instructions, see '.
             '**[[ %s | Managing Caches ]]** in the documentation.',
             $caches_href))
         ->addExample("/config\.h$/\n#(^|/)autobuilt/#", pht('Valid Setting')),
       $this->newOption('differential.sticky-accept', 'bool', true)
         ->setBoolOptions(
           array(
             pht('Accepts persist across updates'),
             pht('Accepts are reset by updates'),
           ))
         ->setSummary(
           pht('Should "Accepted" revisions remain "Accepted" after updates?'))
         ->setDescription(
           pht(
             'Normally, when revisions that have been "Accepted" are updated, '.
             'they remain "Accepted". This allows reviewers to suggest minor '.
             'alterations when accepting, and encourages authors to update '.
             'if they make minor changes in response to this feedback.'.
             "\n\n".
             'If you want updates to always require re-review, you can disable '.
             'the "stickiness" of the "Accepted" status with this option. '.
             'This may make the process for minor changes much more burdensome '.
             'to both authors and reviewers.')),
       $this->newOption('differential.allow-self-accept', 'bool', false)
         ->setBoolOptions(
           array(
             pht('Allow self-accept'),
             pht('Disallow self-accept'),
           ))
         ->setSummary(pht('Allows users to accept their own revisions.'))
         ->setDescription(
           pht(
             "If you set this to true, users can accept their own revisions. ".
             "This action is disabled by default because it's most likely not ".
             "a behavior you want, but it proves useful if you are working ".
             "alone on a project and want to make use of all of ".
             "differential's features.")),
       $this->newOption('differential.always-allow-close', 'bool', false)
         ->setBoolOptions(
           array(
             pht('Allow any user'),
             pht('Restrict to submitter'),
           ))
         ->setSummary(pht('Allows any user to close accepted revisions.'))
         ->setDescription(
           pht(
             'If you set this to true, any user can close any revision so '.
             'long as it has been accepted. This can be useful depending on '.
             'your development model. For example, github-style pull requests '.
             'where the reviewer is often the actual committer can benefit '.
             'from turning this option to true. If false, only the submitter '.
             'can close a revision.')),
       $this->newOption('differential.always-allow-abandon', 'bool', false)
         ->setBoolOptions(
           array(
             pht('Allow any user'),
             pht('Restrict to submitter'),
           ))
         ->setSummary(pht('Allows any user to abandon revisions.'))
         ->setDescription(
           pht(
             'If you set this to true, any user can abandon any revision. If '.
             'false, only the submitter can abandon a revision.')),
       $this->newOption('differential.allow-reopen', 'bool', false)
         ->setBoolOptions(
           array(
             pht('Enable reopen'),
             pht('Disable reopen'),
           ))
         ->setSummary(pht('Allows any user to reopen a closed revision.'))
         ->setDescription(
           pht(
             'If you set this to true, any user can reopen a revision so '.
             'long as it has been closed. This can be useful if a revision '.
             'is accidentally closed or if a developer changes his or her '.
             'mind after closing a revision. If it is false, reopening '.
             'is not allowed.')),
       $this->newOption('differential.close-on-accept', 'bool', false)
         ->setBoolOptions(
           array(
             pht('Treat Accepted Revisions as "Closed"'),
             pht('Treat Accepted Revisions as "Open"'),
           ))
         ->setSummary(pht('Allows "Accepted" to act as a closed status.'))
         ->setDescription(
           pht(
             'Normally, Differential revisions remain on the dashboard when '.
             'they are "Accepted", and the author then commits the changes '.
             'to "Close" the revision and move it off the dashboard.'.
             "\n\n".
             'If you have an unusual workflow where Differential is used for '.
             'post-commit review (normally called "Audit", elsewhere in '.
             'Phabricator), you can set this flag to treat the "Accepted" '.
             'state as a "Closed" state and end the review workflow early.'.
             "\n\n".
             'This sort of workflow is very unusual. Very few installs should '.
             'need to change this option.')),
       $this->newOption('differential.days-fresh', 'int', 1)
         ->setSummary(
           pht(
             "For how many business days should a revision be considered ".
             "'fresh'?"))
         ->setDescription(
           pht(
             'Revisions newer than this number of days are marked as fresh in '.
             'Action Required and Revisions Waiting on You views. Only work '.
             'days (not weekends and holidays) are included. Set to 0 to '.
             'disable this feature.')),
       $this->newOption('differential.days-stale', 'int', 3)
         ->setSummary(
           pht("After this many days, a revision will be considered 'stale'."))
         ->setDescription(
           pht(
             "Similar to `%s` but marks stale revisions. ".
             "If the revision is even older than it is when marked as 'old'.",
             'differential.days-fresh')),
       $this->newOption(
         'metamta.differential.subject-prefix',
         'string',
         '[Differential]')
         ->setDescription(pht('Subject prefix for Differential mail.')),
       $this->newOption(
         'metamta.differential.attach-patches',
         'bool',
         false)
         ->setBoolOptions(
           array(
             pht('Attach Patches'),
             pht('Do Not Attach Patches'),
           ))
         ->setSummary(pht('Attach patches to email, as text attachments.'))
         ->setDescription(
           pht(
             'If you set this to true, Phabricator will attach patches to '.
             'Differential mail (as text attachments). This will not work if '.
             'you are using SendGrid as your mail adapter.')),
       $this->newOption(
         'metamta.differential.inline-patches',
         'int',
         0)
         ->setSummary(pht('Inline patches in email, as body text.'))
         ->setDescription(
           pht(
             "To include patches inline in email bodies, set this to a ".
             "positive integer. Patches will be inlined if they are at most ".
             "that many lines. For instance, a value of 100 means 'inline ".
             "patches if they are no longer than 100 lines'. By default, ".
             "patches are not inlined.")),
       // TODO: Implement 'enum'? Options are 'unified' or 'git'.
       $this->newOption(
         'metamta.differential.patch-format',
         'string',
         'unified')
         ->setDescription(
           pht("Format for inlined or attached patches: 'git' or 'unified'.")),
-      $this->newOption(
-        'metamta.differential.unified-comment-context',
-        'bool',
-        false)
-        ->setBoolOptions(
-          array(
-            pht('Show context'),
-            pht('Do not show context'),
-          ))
-        ->setSummary(pht('Show diff context around inline comments in email.'))
-        ->setDescription(
-          pht(
-            'Normally, inline comments in emails are shown with a file and '.
-            'line but without any diff context. Enabling this option adds '.
-            'diff context and the comment thread.')),
     );
   }
 
 }
diff --git a/src/applications/differential/editor/DifferentialTransactionEditor.php b/src/applications/differential/editor/DifferentialTransactionEditor.php
index fabd2511b..3855edeac 100644
--- a/src/applications/differential/editor/DifferentialTransactionEditor.php
+++ b/src/applications/differential/editor/DifferentialTransactionEditor.php
@@ -1,1806 +1,1721 @@
 <?php
 
 final class DifferentialTransactionEditor
   extends PhabricatorApplicationTransactionEditor {
 
   private $changedPriorToCommitURI;
   private $isCloseByCommit;
   private $repositoryPHIDOverride = false;
   private $didExpandInlineState = false;
 
   public function getEditorApplicationClass() {
     return 'PhabricatorDifferentialApplication';
   }
 
   public function getEditorObjectsDescription() {
     return pht('Differential Revisions');
   }
 
   public function getDiffUpdateTransaction(array $xactions) {
     $type_update = DifferentialTransaction::TYPE_UPDATE;
 
     foreach ($xactions as $xaction) {
       if ($xaction->getTransactionType() == $type_update) {
         return $xaction;
       }
     }
 
     return null;
   }
 
   public function setIsCloseByCommit($is_close_by_commit) {
     $this->isCloseByCommit = $is_close_by_commit;
     return $this;
   }
 
   public function getIsCloseByCommit() {
     return $this->isCloseByCommit;
   }
 
   public function setChangedPriorToCommitURI($uri) {
     $this->changedPriorToCommitURI = $uri;
     return $this;
   }
 
   public function getChangedPriorToCommitURI() {
     return $this->changedPriorToCommitURI;
   }
 
   public function setRepositoryPHIDOverride($phid_or_null) {
     $this->repositoryPHIDOverride = $phid_or_null;
     return $this;
   }
 
   public function getTransactionTypes() {
     $types = parent::getTransactionTypes();
 
     $types[] = PhabricatorTransactions::TYPE_COMMENT;
     $types[] = PhabricatorTransactions::TYPE_VIEW_POLICY;
     $types[] = PhabricatorTransactions::TYPE_EDIT_POLICY;
 
     $types[] = DifferentialTransaction::TYPE_ACTION;
     $types[] = DifferentialTransaction::TYPE_INLINE;
     $types[] = DifferentialTransaction::TYPE_STATUS;
     $types[] = DifferentialTransaction::TYPE_UPDATE;
 
     return $types;
   }
 
   protected function getCustomTransactionOldValue(
     PhabricatorLiskDAO $object,
     PhabricatorApplicationTransaction $xaction) {
 
     switch ($xaction->getTransactionType()) {
       case DifferentialTransaction::TYPE_ACTION:
         return null;
       case DifferentialTransaction::TYPE_INLINE:
         return null;
       case DifferentialTransaction::TYPE_UPDATE:
         if ($this->getIsNewObject()) {
           return null;
         } else {
           return $object->getActiveDiff()->getPHID();
         }
     }
 
     return parent::getCustomTransactionOldValue($object, $xaction);
   }
 
   protected function getCustomTransactionNewValue(
     PhabricatorLiskDAO $object,
     PhabricatorApplicationTransaction $xaction) {
 
     switch ($xaction->getTransactionType()) {
       case DifferentialTransaction::TYPE_ACTION:
       case DifferentialTransaction::TYPE_UPDATE:
         return $xaction->getNewValue();
       case DifferentialTransaction::TYPE_INLINE:
         return null;
     }
 
     return parent::getCustomTransactionNewValue($object, $xaction);
   }
 
   protected function transactionHasEffect(
     PhabricatorLiskDAO $object,
     PhabricatorApplicationTransaction $xaction) {
 
     $actor_phid = $this->getActingAsPHID();
 
     switch ($xaction->getTransactionType()) {
       case DifferentialTransaction::TYPE_INLINE:
         return $xaction->hasComment();
       case DifferentialTransaction::TYPE_ACTION:
         $status_closed = ArcanistDifferentialRevisionStatus::CLOSED;
         $status_abandoned = ArcanistDifferentialRevisionStatus::ABANDONED;
         $status_review = ArcanistDifferentialRevisionStatus::NEEDS_REVIEW;
         $status_revision = ArcanistDifferentialRevisionStatus::NEEDS_REVISION;
         $status_plan = ArcanistDifferentialRevisionStatus::CHANGES_PLANNED;
 
         $action_type = $xaction->getNewValue();
         switch ($action_type) {
           case DifferentialAction::ACTION_ACCEPT:
           case DifferentialAction::ACTION_REJECT:
             if ($action_type == DifferentialAction::ACTION_ACCEPT) {
               $new_status = DifferentialReviewerStatus::STATUS_ACCEPTED;
             } else {
               $new_status = DifferentialReviewerStatus::STATUS_REJECTED;
             }
 
             $actor = $this->getActor();
 
             // These transactions can cause effects in two ways: by altering the
             // status of an existing reviewer; or by adding the actor as a new
             // reviewer.
 
             $will_add_reviewer = true;
             foreach ($object->getReviewerStatus() as $reviewer) {
               if ($reviewer->hasAuthority($actor)) {
                 if ($reviewer->getStatus() != $new_status) {
                   return true;
                 }
               }
               if ($reviewer->getReviewerPHID() == $actor_phid) {
                 $will_add_reviwer = false;
               }
             }
 
             return $will_add_reviewer;
           case DifferentialAction::ACTION_CLOSE:
             return ($object->getStatus() != $status_closed);
           case DifferentialAction::ACTION_ABANDON:
             return ($object->getStatus() != $status_abandoned);
           case DifferentialAction::ACTION_RECLAIM:
             return ($object->getStatus() == $status_abandoned);
           case DifferentialAction::ACTION_REOPEN:
             return ($object->getStatus() == $status_closed);
           case DifferentialAction::ACTION_RETHINK:
             return ($object->getStatus() != $status_plan);
           case DifferentialAction::ACTION_REQUEST:
             return ($object->getStatus() != $status_review);
           case DifferentialAction::ACTION_RESIGN:
             foreach ($object->getReviewerStatus() as $reviewer) {
               if ($reviewer->getReviewerPHID() == $actor_phid) {
                 return true;
               }
             }
             return false;
           case DifferentialAction::ACTION_CLAIM:
             return ($actor_phid != $object->getAuthorPHID());
         }
     }
 
     return parent::transactionHasEffect($object, $xaction);
   }
 
   protected function applyCustomInternalTransaction(
     PhabricatorLiskDAO $object,
     PhabricatorApplicationTransaction $xaction) {
 
     $status_review = ArcanistDifferentialRevisionStatus::NEEDS_REVIEW;
     $status_revision = ArcanistDifferentialRevisionStatus::NEEDS_REVISION;
     $status_plan = ArcanistDifferentialRevisionStatus::CHANGES_PLANNED;
     $status_abandoned = ArcanistDifferentialRevisionStatus::ABANDONED;
 
     switch ($xaction->getTransactionType()) {
       case DifferentialTransaction::TYPE_INLINE:
         return;
       case DifferentialTransaction::TYPE_UPDATE:
         if (!$this->getIsCloseByCommit()) {
           switch ($object->getStatus()) {
             case $status_revision:
             case $status_plan:
             case $status_abandoned:
               $object->setStatus($status_review);
               break;
           }
         }
 
         $diff = $this->requireDiff($xaction->getNewValue());
 
         $object->setLineCount($diff->getLineCount());
         if ($this->repositoryPHIDOverride !== false) {
           $object->setRepositoryPHID($this->repositoryPHIDOverride);
         } else {
           $object->setRepositoryPHID($diff->getRepositoryPHID());
         }
         $object->attachActiveDiff($diff);
 
         // TODO: Update the `diffPHID` once we add that.
         return;
       case DifferentialTransaction::TYPE_ACTION:
         switch ($xaction->getNewValue()) {
           case DifferentialAction::ACTION_RESIGN:
           case DifferentialAction::ACTION_ACCEPT:
           case DifferentialAction::ACTION_REJECT:
             // These have no direct effects, and affect review status only
             // indirectly by altering reviewers with TYPE_EDGE transactions.
             return;
           case DifferentialAction::ACTION_ABANDON:
             $object->setStatus(ArcanistDifferentialRevisionStatus::ABANDONED);
             return;
           case DifferentialAction::ACTION_RETHINK:
             $object->setStatus($status_plan);
             return;
           case DifferentialAction::ACTION_RECLAIM:
             $object->setStatus($status_review);
             return;
           case DifferentialAction::ACTION_REOPEN:
             $object->setStatus($status_review);
             return;
           case DifferentialAction::ACTION_REQUEST:
             $object->setStatus($status_review);
             return;
           case DifferentialAction::ACTION_CLOSE:
             $object->setStatus(ArcanistDifferentialRevisionStatus::CLOSED);
             return;
           case DifferentialAction::ACTION_CLAIM:
             $object->setAuthorPHID($this->getActingAsPHID());
             return;
           default:
             throw new Exception(
               pht(
                 'Differential action "%s" is not a valid action!',
                 $xaction->getNewValue()));
         }
         break;
     }
 
     return parent::applyCustomInternalTransaction($object, $xaction);
   }
 
   protected function expandTransaction(
     PhabricatorLiskDAO $object,
     PhabricatorApplicationTransaction $xaction) {
 
     $results = parent::expandTransaction($object, $xaction);
 
     $actor = $this->getActor();
     $actor_phid = $this->getActingAsPHID();
     $type_edge = PhabricatorTransactions::TYPE_EDGE;
 
     $status_plan = ArcanistDifferentialRevisionStatus::CHANGES_PLANNED;
 
     $edge_reviewer = DifferentialRevisionHasReviewerEdgeType::EDGECONST;
     $edge_ref_task = DifferentialRevisionHasTaskEdgeType::EDGECONST;
 
     $is_sticky_accept = PhabricatorEnv::getEnvConfig(
       'differential.sticky-accept');
 
     $downgrade_rejects = false;
     $downgrade_accepts = false;
     if ($this->getIsCloseByCommit()) {
       // Never downgrade reviewers when we're closing a revision after a
       // commit.
     } else {
       switch ($xaction->getTransactionType()) {
         case DifferentialTransaction::TYPE_UPDATE:
           $downgrade_rejects = true;
           if (!$is_sticky_accept) {
             // If "sticky accept" is disabled, also downgrade the accepts.
             $downgrade_accepts = true;
           }
           break;
         case DifferentialTransaction::TYPE_ACTION:
           switch ($xaction->getNewValue()) {
             case DifferentialAction::ACTION_REQUEST:
               $downgrade_rejects = true;
               if ((!$is_sticky_accept) ||
                   ($object->getStatus() != $status_plan)) {
                 // If the old state isn't "changes planned", downgrade the
                 // accepts. This exception allows an accepted revision to
                 // go through Plan Changes -> Request Review to return to
                 // "accepted" if the author didn't update the revision.
                 $downgrade_accepts = true;
               }
               break;
           }
           break;
       }
     }
 
     $new_accept = DifferentialReviewerStatus::STATUS_ACCEPTED;
     $new_reject = DifferentialReviewerStatus::STATUS_REJECTED;
     $old_accept = DifferentialReviewerStatus::STATUS_ACCEPTED_OLDER;
     $old_reject = DifferentialReviewerStatus::STATUS_REJECTED_OLDER;
 
     if ($downgrade_rejects || $downgrade_accepts) {
       // When a revision is updated, change all "reject" to "rejected older
       // revision". This means we won't immediately push the update back into
       // "needs review", but outstanding rejects will still block it from
       // moving to "accepted".
 
       // We also do this for "Request Review", even though the diff is not
       // updated directly. Essentially, this acts like an update which doesn't
       // actually change the diff text.
 
       $edits = array();
       foreach ($object->getReviewerStatus() as $reviewer) {
         if ($downgrade_rejects) {
           if ($reviewer->getStatus() == $new_reject) {
             $edits[$reviewer->getReviewerPHID()] = array(
               'data' => array(
                 'status' => $old_reject,
               ),
             );
           }
         }
 
         if ($downgrade_accepts) {
           if ($reviewer->getStatus() == $new_accept) {
             $edits[$reviewer->getReviewerPHID()] = array(
               'data' => array(
                 'status' => $old_accept,
               ),
             );
           }
         }
       }
 
       if ($edits) {
         $results[] = id(new DifferentialTransaction())
           ->setTransactionType($type_edge)
           ->setMetadataValue('edge:type', $edge_reviewer)
           ->setIgnoreOnNoEffect(true)
           ->setNewValue(array('+' => $edits));
       }
     }
 
     switch ($xaction->getTransactionType()) {
       case DifferentialTransaction::TYPE_UPDATE:
         if ($this->getIsCloseByCommit()) {
           // Don't bother with any of this if this update is a side effect of
           // commit detection.
           break;
         }
 
         // When a revision is updated and the diff comes from a branch named
         // "T123" or similar, automatically associate the commit with the
         // task that the branch names.
 
         $maniphest = 'PhabricatorManiphestApplication';
         if (PhabricatorApplication::isClassInstalled($maniphest)) {
           $diff = $this->requireDiff($xaction->getNewValue());
           $branch = $diff->getBranch();
 
           // No "$", to allow for branches like T123_demo.
           $match = null;
           if (preg_match('/^T(\d+)/i', $branch, $match)) {
             $task_id = $match[1];
             $tasks = id(new ManiphestTaskQuery())
               ->setViewer($this->getActor())
               ->withIDs(array($task_id))
               ->execute();
             if ($tasks) {
               $task = head($tasks);
               $task_phid = $task->getPHID();
 
               $results[] = id(new DifferentialTransaction())
                 ->setTransactionType($type_edge)
                 ->setMetadataValue('edge:type', $edge_ref_task)
                 ->setIgnoreOnNoEffect(true)
                 ->setNewValue(array('+' => array($task_phid => $task_phid)));
             }
           }
         }
         break;
 
       case PhabricatorTransactions::TYPE_COMMENT:
         // When a user leaves a comment, upgrade their reviewer status from
         // "added" to "commented" if they're also a reviewer. We may further
         // upgrade this based on other actions in the transaction group.
 
         $status_added = DifferentialReviewerStatus::STATUS_ADDED;
         $status_commented = DifferentialReviewerStatus::STATUS_COMMENTED;
 
         $data = array(
           'status' => $status_commented,
         );
 
         $edits = array();
         foreach ($object->getReviewerStatus() as $reviewer) {
           if ($reviewer->getReviewerPHID() == $actor_phid) {
             if ($reviewer->getStatus() == $status_added) {
               $edits[$actor_phid] = array(
                 'data' => $data,
               );
             }
           }
         }
 
         if ($edits) {
           $results[] = id(new DifferentialTransaction())
             ->setTransactionType($type_edge)
             ->setMetadataValue('edge:type', $edge_reviewer)
             ->setIgnoreOnNoEffect(true)
             ->setNewValue(array('+' => $edits));
         }
         break;
 
       case DifferentialTransaction::TYPE_ACTION:
         $action_type = $xaction->getNewValue();
 
         switch ($action_type) {
           case DifferentialAction::ACTION_ACCEPT:
           case DifferentialAction::ACTION_REJECT:
             if ($action_type == DifferentialAction::ACTION_ACCEPT) {
               $data = array(
                 'status' => DifferentialReviewerStatus::STATUS_ACCEPTED,
               );
             } else {
               $data = array(
                 'status' => DifferentialReviewerStatus::STATUS_REJECTED,
               );
             }
 
             $edits = array();
 
             foreach ($object->getReviewerStatus() as $reviewer) {
               if ($reviewer->hasAuthority($actor)) {
                 $edits[$reviewer->getReviewerPHID()] = array(
                   'data' => $data,
                 );
               }
             }
 
             // Also either update or add the actor themselves as a reviewer.
             $edits[$actor_phid] = array(
               'data' => $data,
             );
 
             $results[] = id(new DifferentialTransaction())
               ->setTransactionType($type_edge)
               ->setMetadataValue('edge:type', $edge_reviewer)
               ->setIgnoreOnNoEffect(true)
               ->setNewValue(array('+' => $edits));
             break;
 
           case DifferentialAction::ACTION_CLAIM:
             // If the user is commandeering, add the previous owner as a
             // reviewer and remove the actor.
 
             $edits = array(
               '-' => array(
                 $actor_phid => $actor_phid,
               ),
             );
 
             $owner_phid = $object->getAuthorPHID();
             if ($owner_phid) {
               $reviewer = new DifferentialReviewer(
                 $owner_phid,
                 array(
                   'status' => DifferentialReviewerStatus::STATUS_ADDED,
                 ));
 
               $edits['+'] = array(
                 $owner_phid => array(
                   'data' => $reviewer->getEdgeData(),
                 ),
               );
             }
 
             // NOTE: We're setting setIsCommandeerSideEffect() on this because
             // normally you can't add a revision's author as a reviewer, but
             // this action swaps them after validation executes.
 
             $results[] = id(new DifferentialTransaction())
               ->setTransactionType($type_edge)
               ->setMetadataValue('edge:type', $edge_reviewer)
               ->setIgnoreOnNoEffect(true)
               ->setIsCommandeerSideEffect(true)
               ->setNewValue($edits);
 
             break;
           case DifferentialAction::ACTION_RESIGN:
             // If the user is resigning, add a separate reviewer edit
             // transaction which removes them as a reviewer.
 
             $results[] = id(new DifferentialTransaction())
               ->setTransactionType($type_edge)
               ->setMetadataValue('edge:type', $edge_reviewer)
               ->setIgnoreOnNoEffect(true)
               ->setNewValue(
                 array(
                   '-' => array(
                     $actor_phid => $actor_phid,
                   ),
                 ));
 
             break;
         }
       break;
     }
 
     if (!$this->didExpandInlineState) {
       switch ($xaction->getTransactionType()) {
         case PhabricatorTransactions::TYPE_COMMENT:
         case DifferentialTransaction::TYPE_ACTION:
         case DifferentialTransaction::TYPE_UPDATE:
         case DifferentialTransaction::TYPE_INLINE:
           $this->didExpandInlineState = true;
 
           $actor_phid = $this->getActingAsPHID();
           $actor_is_author = ($object->getAuthorPHID() == $actor_phid);
           if (!$actor_is_author) {
             break;
           }
 
           $state_map = PhabricatorTransactions::getInlineStateMap();
 
           $inlines = id(new DifferentialDiffInlineCommentQuery())
             ->setViewer($this->getActor())
             ->withRevisionPHIDs(array($object->getPHID()))
             ->withFixedStates(array_keys($state_map))
             ->execute();
 
           if (!$inlines) {
             break;
           }
 
           $old_value = mpull($inlines, 'getFixedState', 'getPHID');
           $new_value = array();
           foreach ($old_value as $key => $state) {
             $new_value[$key] = $state_map[$state];
           }
 
           $results[] = id(new DifferentialTransaction())
             ->setTransactionType(PhabricatorTransactions::TYPE_INLINESTATE)
             ->setIgnoreOnNoEffect(true)
             ->setOldValue($old_value)
             ->setNewValue($new_value);
           break;
       }
     }
 
     return $results;
   }
 
   protected function applyCustomExternalTransaction(
     PhabricatorLiskDAO $object,
     PhabricatorApplicationTransaction $xaction) {
 
     switch ($xaction->getTransactionType()) {
       case DifferentialTransaction::TYPE_ACTION:
         return;
       case DifferentialTransaction::TYPE_INLINE:
         $reply = $xaction->getComment()->getReplyToComment();
         if ($reply && !$reply->getHasReplies()) {
           $reply->setHasReplies(1)->save();
         }
         return;
       case DifferentialTransaction::TYPE_UPDATE:
         // Now that we're inside the transaction, do a final check.
         $diff = $this->requireDiff($xaction->getNewValue());
 
         // TODO: It would be slightly cleaner to just revalidate this
         // transaction somehow using the same validation code, but that's
         // not easy to do at the moment.
 
         $revision_id = $diff->getRevisionID();
         if ($revision_id && ($revision_id != $object->getID())) {
           throw new Exception(
             pht(
               'Diff is already attached to another revision. You lost '.
               'a race?'));
         }
 
         // TODO: This can race with diff updates, particularly those from
         // Harbormaster. See discussion in T8650.
         $diff->setRevisionID($object->getID());
         $diff->save();
 
         // Update Harbormaster to set the containerPHID correctly for any
         // existing buildables. We may otherwise have buildables stuck with
         // the old (`null`) container.
 
         // TODO: This is a bit iffy, maybe we can find a cleaner approach?
         // In particular, this could (rarely) be overwritten by Harbormaster
         // workers.
         $table = new HarbormasterBuildable();
         $conn_w = $table->establishConnection('w');
         queryfx(
           $conn_w,
           'UPDATE %T SET containerPHID = %s WHERE buildablePHID = %s',
           $table->getTableName(),
           $object->getPHID(),
           $diff->getPHID());
 
         return;
     }
 
     return parent::applyCustomExternalTransaction($object, $xaction);
   }
 
   protected function applyBuiltinExternalTransaction(
     PhabricatorLiskDAO $object,
     PhabricatorApplicationTransaction $xaction) {
 
     switch ($xaction->getTransactionType()) {
       case PhabricatorTransactions::TYPE_INLINESTATE:
         $table = new DifferentialTransactionComment();
         $conn_w = $table->establishConnection('w');
         foreach ($xaction->getNewValue() as $phid => $state) {
           queryfx(
             $conn_w,
             'UPDATE %T SET fixedState = %s WHERE phid = %s',
             $table->getTableName(),
             $state,
             $phid);
         }
         break;
     }
 
     return parent::applyBuiltinExternalTransaction($object, $xaction);
   }
 
   protected function mergeEdgeData($type, array $u, array $v) {
     $result = parent::mergeEdgeData($type, $u, $v);
 
     switch ($type) {
       case DifferentialRevisionHasReviewerEdgeType::EDGECONST:
         // When the same reviewer has their status updated by multiple
         // transactions, we want the strongest status to win. An example of
         // this is when a user adds a comment and also accepts a revision which
         // they are a reviewer on. The comment creates a "commented" status,
         // while the accept creates an "accepted" status. Since accept is
         // stronger, it should win and persist.
 
         $u_status = idx($u, 'status');
         $v_status = idx($v, 'status');
         $u_str = DifferentialReviewerStatus::getStatusStrength($u_status);
         $v_str = DifferentialReviewerStatus::getStatusStrength($v_status);
         if ($u_str > $v_str) {
           $result['status'] = $u_status;
         } else {
           $result['status'] = $v_status;
         }
         break;
     }
 
     return $result;
   }
 
   protected function applyFinalEffects(
     PhabricatorLiskDAO $object,
     array $xactions) {
 
     // Load the most up-to-date version of the revision and its reviewers,
     // so we don't need to try to deduce the state of reviewers by examining
     // all the changes made by the transactions. Then, update the reviewers
     // on the object to make sure we're acting on the current reviewer set
     // (and, for example, sending mail to the right people).
 
     $new_revision = id(new DifferentialRevisionQuery())
       ->setViewer($this->getActor())
       ->needReviewerStatus(true)
       ->needActiveDiffs(true)
       ->withIDs(array($object->getID()))
       ->executeOne();
     if (!$new_revision) {
       throw new Exception(
         pht('Failed to load revision from transaction finalization.'));
     }
 
     $object->attachReviewerStatus($new_revision->getReviewerStatus());
     $object->attachActiveDiff($new_revision->getActiveDiff());
     $object->attachRepository($new_revision->getRepository());
 
     foreach ($xactions as $xaction) {
       switch ($xaction->getTransactionType()) {
         case DifferentialTransaction::TYPE_UPDATE:
           $diff = $this->requireDiff($xaction->getNewValue(), true);
 
           // Update these denormalized index tables when we attach a new
           // diff to a revision.
 
           $this->updateRevisionHashTable($object, $diff);
           $this->updateAffectedPathTable($object, $diff);
           break;
       }
     }
 
     $status_accepted = ArcanistDifferentialRevisionStatus::ACCEPTED;
     $status_revision = ArcanistDifferentialRevisionStatus::NEEDS_REVISION;
     $status_review = ArcanistDifferentialRevisionStatus::NEEDS_REVIEW;
 
     $old_status = $object->getStatus();
     switch ($old_status) {
       case $status_accepted:
       case $status_revision:
       case $status_review:
         // Try to move a revision to "accepted". We look for:
         //
         //   - at least one accepting reviewer who is a user; and
         //   - no rejects; and
         //   - no rejects of older diffs; and
         //   - no blocking reviewers.
 
         $has_accepting_user = false;
         $has_rejecting_reviewer = false;
         $has_rejecting_older_reviewer = false;
         $has_blocking_reviewer = false;
         foreach ($object->getReviewerStatus() as $reviewer) {
           $reviewer_status = $reviewer->getStatus();
           switch ($reviewer_status) {
             case DifferentialReviewerStatus::STATUS_REJECTED:
               $has_rejecting_reviewer = true;
               break;
             case DifferentialReviewerStatus::STATUS_REJECTED_OLDER:
               $has_rejecting_older_reviewer = true;
               break;
             case DifferentialReviewerStatus::STATUS_BLOCKING:
               $has_blocking_reviewer = true;
               break;
             case DifferentialReviewerStatus::STATUS_ACCEPTED:
               if ($reviewer->isUser()) {
                 $has_accepting_user = true;
               }
               break;
           }
         }
 
         $new_status = null;
         if ($has_accepting_user &&
             !$has_rejecting_reviewer &&
             !$has_rejecting_older_reviewer &&
             !$has_blocking_reviewer) {
           $new_status = $status_accepted;
         } else if ($has_rejecting_reviewer) {
           // This isn't accepted, and there's at least one rejecting reviewer,
           // so the revision needs changes. This usually happens after a
           // "reject".
           $new_status = $status_revision;
         } else if ($old_status == $status_accepted) {
           // This revision was accepted, but it no longer satisfies the
           // conditions for acceptance. This usually happens after an accepting
           // reviewer resigns or is removed.
           $new_status = $status_review;
         }
 
         if ($new_status !== null && ($new_status != $old_status)) {
           $xaction = id(new DifferentialTransaction())
             ->setTransactionType(DifferentialTransaction::TYPE_STATUS)
             ->setOldValue($old_status)
             ->setNewValue($new_status);
 
           $xaction = $this->populateTransaction($object, $xaction)->save();
 
           $xactions[] = $xaction;
 
           $object->setStatus($new_status)->save();
         }
         break;
       default:
         // Revisions can't transition out of other statuses (like closed or
         // abandoned) as a side effect of reviewer status changes.
         break;
     }
 
     return $xactions;
   }
 
   protected function validateTransaction(
     PhabricatorLiskDAO $object,
     $type,
     array $xactions) {
 
     $errors = parent::validateTransaction($object, $type, $xactions);
 
     $config_self_accept_key = 'differential.allow-self-accept';
     $allow_self_accept = PhabricatorEnv::getEnvConfig($config_self_accept_key);
 
     foreach ($xactions as $xaction) {
       switch ($type) {
         case PhabricatorTransactions::TYPE_EDGE:
           switch ($xaction->getMetadataValue('edge:type')) {
             case DifferentialRevisionHasReviewerEdgeType::EDGECONST:
 
               // Prevent the author from becoming a reviewer.
 
               // NOTE: This is pretty gross, but this restriction is unusual.
               // If we end up with too much more of this, we should try to clean
               // this up -- maybe by moving validation to after transactions
               // are adjusted (so we can just examine the final value) or adding
               // a second phase there?
 
               $author_phid = $object->getAuthorPHID();
               $new = $xaction->getNewValue();
 
               $add = idx($new, '+', array());
               $eq = idx($new, '=', array());
               $phids = array_keys($add + $eq);
 
               foreach ($phids as $phid) {
                 if (($phid == $author_phid) &&
                     !$allow_self_accept &&
                     !$xaction->getIsCommandeerSideEffect()) {
                   $errors[] =
                     new PhabricatorApplicationTransactionValidationError(
                       $type,
                       pht('Invalid'),
                       pht('The author of a revision can not be a reviewer.'),
                       $xaction);
                 }
               }
               break;
           }
           break;
         case DifferentialTransaction::TYPE_UPDATE:
           $diff = $this->loadDiff($xaction->getNewValue());
           if (!$diff) {
             $errors[] = new PhabricatorApplicationTransactionValidationError(
               $type,
               pht('Invalid'),
               pht('The specified diff does not exist.'),
               $xaction);
           } else if (($diff->getRevisionID()) &&
             ($diff->getRevisionID() != $object->getID())) {
             $errors[] = new PhabricatorApplicationTransactionValidationError(
               $type,
               pht('Invalid'),
               pht(
                 'You can not update this revision to the specified diff, '.
                 'because the diff is already attached to another revision.'),
               $xaction);
           }
           break;
         case DifferentialTransaction::TYPE_ACTION:
           $error = $this->validateDifferentialAction(
             $object,
             $type,
             $xaction,
             $xaction->getNewValue());
           if ($error) {
             $errors[] = new PhabricatorApplicationTransactionValidationError(
               $type,
               pht('Invalid'),
               $error,
               $xaction);
           }
           break;
       }
     }
 
     return $errors;
   }
 
   private function validateDifferentialAction(
     DifferentialRevision $revision,
     $type,
     DifferentialTransaction $xaction,
     $action) {
 
     $author_phid = $revision->getAuthorPHID();
     $actor_phid = $this->getActingAsPHID();
     $actor_is_author = ($author_phid == $actor_phid);
 
     $config_abandon_key = 'differential.always-allow-abandon';
     $always_allow_abandon = PhabricatorEnv::getEnvConfig($config_abandon_key);
 
     $config_close_key = 'differential.always-allow-close';
     $always_allow_close = PhabricatorEnv::getEnvConfig($config_close_key);
 
     $config_reopen_key = 'differential.allow-reopen';
     $allow_reopen = PhabricatorEnv::getEnvConfig($config_reopen_key);
 
     $config_self_accept_key = 'differential.allow-self-accept';
     $allow_self_accept = PhabricatorEnv::getEnvConfig($config_self_accept_key);
 
     $revision_status = $revision->getStatus();
 
     $status_accepted = ArcanistDifferentialRevisionStatus::ACCEPTED;
     $status_abandoned = ArcanistDifferentialRevisionStatus::ABANDONED;
     $status_closed = ArcanistDifferentialRevisionStatus::CLOSED;
 
     switch ($action) {
       case DifferentialAction::ACTION_ACCEPT:
         if ($actor_is_author && !$allow_self_accept) {
           return pht(
             'You can not accept this revision because you are the owner.');
         }
 
         if ($revision_status == $status_abandoned) {
           return pht(
             'You can not accept this revision because it has been '.
             'abandoned.');
         }
 
         if ($revision_status == $status_closed) {
           return pht(
             'You can not accept this revision because it has already been '.
             'closed.');
         }
 
         // TODO: It would be nice to make this generic at some point.
         $signatures = DifferentialRequiredSignaturesField::loadForRevision(
           $revision);
         foreach ($signatures as $phid => $signed) {
           if (!$signed) {
             return pht(
               'You can not accept this revision because the author has '.
               'not signed all of the required legal documents.');
           }
         }
 
         break;
 
       case DifferentialAction::ACTION_REJECT:
         if ($actor_is_author) {
           return pht('You can not request changes to your own revision.');
         }
 
         if ($revision_status == $status_abandoned) {
           return pht(
             'You can not request changes to this revision because it has been '.
             'abandoned.');
         }
 
         if ($revision_status == $status_closed) {
           return pht(
             'You can not request changes to this revision because it has '.
             'already been closed.');
         }
         break;
 
       case DifferentialAction::ACTION_RESIGN:
         // You can always resign from a revision if you're a reviewer. If you
         // aren't, this is a no-op rather than invalid.
         break;
 
       case DifferentialAction::ACTION_CLAIM:
         // You can claim a revision if you're not the owner. If you are, this
         // is a no-op rather than invalid.
 
         if ($revision_status == $status_closed) {
           return pht(
             'You can not commandeer this revision because it has already been '.
             'closed.');
         }
         break;
 
       case DifferentialAction::ACTION_ABANDON:
         if (!$actor_is_author && !$always_allow_abandon) {
           return pht(
             'You can not abandon this revision because you do not own it. '.
             'You can only abandon revisions you own.');
         }
 
         if ($revision_status == $status_closed) {
           return pht(
             'You can not abandon this revision because it has already been '.
             'closed.');
         }
 
         // NOTE: Abandons of already-abandoned revisions are treated as no-op
         // instead of invalid. Other abandons are OK.
 
         break;
 
       case DifferentialAction::ACTION_RECLAIM:
         if (!$actor_is_author) {
           return pht(
             'You can not reclaim this revision because you do not own '.
             'it. You can only reclaim revisions you own.');
         }
 
         if ($revision_status == $status_closed) {
           return pht(
             'You can not reclaim this revision because it has already been '.
             'closed.');
         }
 
         // NOTE: Reclaims of other non-abandoned revisions are treated as no-op
         // instead of invalid.
 
         break;
 
       case DifferentialAction::ACTION_REOPEN:
         if (!$allow_reopen) {
           return pht(
             'The reopen action is not enabled on this Phabricator install. '.
             'Adjust your configuration to enable it.');
         }
 
         // NOTE: If the revision is not closed, this is caught as a no-op
         // instead of an invalid transaction.
 
         break;
 
       case DifferentialAction::ACTION_RETHINK:
         if (!$actor_is_author) {
           return pht(
             'You can not plan changes to this revision because you do not '.
             'own it. To plan changes to a revision, you must be its owner.');
         }
 
         switch ($revision_status) {
           case ArcanistDifferentialRevisionStatus::ACCEPTED:
           case ArcanistDifferentialRevisionStatus::NEEDS_REVISION:
           case ArcanistDifferentialRevisionStatus::NEEDS_REVIEW:
             // These are OK.
             break;
           case ArcanistDifferentialRevisionStatus::CHANGES_PLANNED:
             // Let this through, it's a no-op.
             break;
           case ArcanistDifferentialRevisionStatus::ABANDONED:
             return pht(
               'You can not plan changes to this revision because it has '.
               'been abandoned.');
           case ArcanistDifferentialRevisionStatus::CLOSED:
             return pht(
               'You can not plan changes to this revision because it has '.
               'already been closed.');
           default:
             throw new Exception(
               pht(
                 'Encountered unexpected revision status ("%s") when '.
                 'validating "%s" action.',
                 $revision_status,
                 $action));
         }
         break;
 
       case DifferentialAction::ACTION_REQUEST:
         if (!$actor_is_author) {
           return pht(
             'You can not request review of this revision because you do '.
             'not own it. To request review of a revision, you must be its '.
             'owner.');
         }
 
         switch ($revision_status) {
           case ArcanistDifferentialRevisionStatus::ACCEPTED:
           case ArcanistDifferentialRevisionStatus::NEEDS_REVISION:
           case ArcanistDifferentialRevisionStatus::CHANGES_PLANNED:
             // These are OK.
             break;
           case ArcanistDifferentialRevisionStatus::NEEDS_REVIEW:
             // This will be caught as "no effect" later on.
             break;
           case ArcanistDifferentialRevisionStatus::ABANDONED:
             return pht(
               'You can not request review of this revision because it has '.
               'been abandoned. Instead, reclaim it.');
           case ArcanistDifferentialRevisionStatus::CLOSED:
             return pht(
               'You can not request review of this revision because it has '.
               'already been closed.');
           default:
             throw new Exception(
               pht(
                 'Encountered unexpected revision status ("%s") when '.
                 'validating "%s" action.',
                 $revision_status,
                 $action));
         }
         break;
 
       case DifferentialAction::ACTION_CLOSE:
         // We force revisions closed when we discover a corresponding commit.
         // In this case, revisions are allowed to transition to closed from
         // any state. This is an automated action taken by the daemons.
 
         if (!$this->getIsCloseByCommit()) {
           if (!$actor_is_author && !$always_allow_close) {
             return pht(
               'You can not close this revision because you do not own it. To '.
               'close a revision, you must be its owner.');
           }
 
           if ($revision_status != $status_accepted) {
             return pht(
               'You can not close this revision because it has not been '.
               'accepted. You can only close accepted revisions.');
           }
         }
         break;
     }
 
     return null;
   }
 
   protected function sortTransactions(array $xactions) {
     $xactions = parent::sortTransactions($xactions);
 
     $head = array();
     $tail = array();
 
     foreach ($xactions as $xaction) {
       $type = $xaction->getTransactionType();
       if ($type == DifferentialTransaction::TYPE_INLINE) {
         $tail[] = $xaction;
       } else {
         $head[] = $xaction;
       }
     }
 
     return array_values(array_merge($head, $tail));
   }
 
   protected function requireCapabilities(
     PhabricatorLiskDAO $object,
     PhabricatorApplicationTransaction $xaction) {
 
     switch ($xaction->getTransactionType()) {}
 
     return parent::requireCapabilities($object, $xaction);
   }
 
   protected function shouldPublishFeedStory(
     PhabricatorLiskDAO $object,
     array $xactions) {
     return true;
   }
 
   protected function shouldSendMail(
     PhabricatorLiskDAO $object,
     array $xactions) {
     return true;
   }
 
   protected function getMailTo(PhabricatorLiskDAO $object) {
     $phids = array();
     $phids[] = $object->getAuthorPHID();
     foreach ($object->getReviewerStatus() as $reviewer) {
       $phids[] = $reviewer->getReviewerPHID();
     }
     return $phids;
   }
 
   protected function getMailAction(
     PhabricatorLiskDAO $object,
     array $xactions) {
     $action = parent::getMailAction($object, $xactions);
 
     $strongest = $this->getStrongestAction($object, $xactions);
     switch ($strongest->getTransactionType()) {
       case DifferentialTransaction::TYPE_UPDATE:
         $count = new PhutilNumber($object->getLineCount());
         $action = pht('%s, %s line(s)', $action, $count);
         break;
     }
 
     return $action;
   }
 
   protected function getMailSubjectPrefix() {
     return PhabricatorEnv::getEnvConfig('metamta.differential.subject-prefix');
   }
 
   protected function getMailThreadID(PhabricatorLiskDAO $object) {
     // This is nonstandard, but retains threading with older messages.
     $phid = $object->getPHID();
     return "differential-rev-{$phid}-req";
   }
 
   protected function buildReplyHandler(PhabricatorLiskDAO $object) {
     return id(new DifferentialReplyHandler())
       ->setMailReceiver($object);
   }
 
   protected function buildMailTemplate(PhabricatorLiskDAO $object) {
     $id = $object->getID();
     $title = $object->getTitle();
 
     $original_title = $object->getOriginalTitle();
 
     $subject = "D{$id}: {$title}";
     $thread_topic = "D{$id}: {$original_title}";
 
     return id(new PhabricatorMetaMTAMail())
       ->setSubject($subject)
       ->addHeader('Thread-Topic', $thread_topic);
   }
 
   protected function buildMailBody(
     PhabricatorLiskDAO $object,
     array $xactions) {
 
     $body = new PhabricatorMetaMTAMailBody();
     $body->setViewer($this->requireActor());
 
     $this->addHeadersAndCommentsToMailBody($body, $xactions);
 
     $type_inline = DifferentialTransaction::TYPE_INLINE;
 
     $inlines = array();
     foreach ($xactions as $xaction) {
       if ($xaction->getTransactionType() == $type_inline) {
         $inlines[] = $xaction;
       }
     }
 
     if ($inlines) {
-      $body->addTextSection(
-        pht('INLINE COMMENTS'),
-        $this->renderInlineCommentsForMail($object, $inlines));
+      $this->appendInlineCommentsForMail($object, $inlines, $body);
     }
 
     $changed_uri = $this->getChangedPriorToCommitURI();
     if ($changed_uri) {
       $body->addLinkSection(
         pht('CHANGED PRIOR TO COMMIT'),
         $changed_uri);
     }
 
     $this->addCustomFieldsToMailBody($body, $object, $xactions);
 
     $body->addLinkSection(
       pht('REVISION DETAIL'),
       PhabricatorEnv::getProductionURI('/D'.$object->getID()));
 
     $update_xaction = null;
     foreach ($xactions as $xaction) {
       switch ($xaction->getTransactionType()) {
         case DifferentialTransaction::TYPE_UPDATE:
           $update_xaction = $xaction;
           break;
       }
     }
 
     if ($update_xaction) {
       $diff = $this->requireDiff($update_xaction->getNewValue(), true);
 
       $body->addTextSection(
         pht('AFFECTED FILES'),
         $this->renderAffectedFilesForMail($diff));
 
       $config_key_inline = 'metamta.differential.inline-patches';
       $config_inline = PhabricatorEnv::getEnvConfig($config_key_inline);
 
       $config_key_attach = 'metamta.differential.attach-patches';
       $config_attach = PhabricatorEnv::getEnvConfig($config_key_attach);
 
       if ($config_inline || $config_attach) {
-        $patch_section = $this->renderPatchForMail($diff);
-        $lines = count(phutil_split_lines($patch_section->getPlaintext()));
+        $patch = $this->buildPatchForMail($diff);
+        $lines = substr_count($patch, "\n");
 
         if ($config_inline && ($lines <= $config_inline)) {
-          $body->addTextSection(
-            pht('CHANGE DETAILS'),
-            $patch_section);
+          $this->appendChangeDetailsForMail($object, $diff, $patch, $body);
         }
 
         if ($config_attach) {
           $name = pht('D%s.%s.patch', $object->getID(), $diff->getID());
           $mime_type = 'text/x-patch; charset=utf-8';
           $body->addAttachment(
-            new PhabricatorMetaMTAAttachment(
-              $patch_section->getPlaintext(), $name, $mime_type));
+            new PhabricatorMetaMTAAttachment($patch, $name, $mime_type));
         }
       }
     }
 
     return $body;
   }
 
   public function getMailTagsMap() {
     return array(
       DifferentialTransaction::MAILTAG_REVIEW_REQUEST =>
         pht('A revision is created.'),
       DifferentialTransaction::MAILTAG_UPDATED =>
         pht('A revision is updated.'),
       DifferentialTransaction::MAILTAG_COMMENT =>
         pht('Someone comments on a revision.'),
       DifferentialTransaction::MAILTAG_CLOSED =>
         pht('A revision is closed.'),
       DifferentialTransaction::MAILTAG_REVIEWERS =>
         pht("A revision's reviewers change."),
       DifferentialTransaction::MAILTAG_CC =>
         pht("A revision's CCs change."),
       DifferentialTransaction::MAILTAG_OTHER =>
         pht('Other revision activity not listed above occurs.'),
     );
   }
 
   protected function supportsSearch() {
     return true;
   }
 
   protected function expandCustomRemarkupBlockTransactions(
     PhabricatorLiskDAO $object,
     array $xactions,
     $blocks,
     PhutilMarkupEngine $engine) {
 
     $flat_blocks = array_mergev($blocks);
     $huge_block = implode("\n\n", $flat_blocks);
 
     $task_map = array();
     $task_refs = id(new ManiphestCustomFieldStatusParser())
       ->parseCorpus($huge_block);
     foreach ($task_refs as $match) {
       foreach ($match['monograms'] as $monogram) {
         $task_id = (int)trim($monogram, 'tT');
         $task_map[$task_id] = true;
       }
     }
 
     $rev_map = array();
     $rev_refs = id(new DifferentialCustomFieldDependsOnParser())
       ->parseCorpus($huge_block);
     foreach ($rev_refs as $match) {
       foreach ($match['monograms'] as $monogram) {
         $rev_id = (int)trim($monogram, 'dD');
         $rev_map[$rev_id] = true;
       }
     }
 
     $edges = array();
     $task_phids = array();
     $rev_phids = array();
 
     if ($task_map) {
       $tasks = id(new ManiphestTaskQuery())
         ->setViewer($this->getActor())
         ->withIDs(array_keys($task_map))
         ->execute();
 
       if ($tasks) {
         $task_phids = mpull($tasks, 'getPHID', 'getPHID');
         $edge_related = DifferentialRevisionHasTaskEdgeType::EDGECONST;
         $edges[$edge_related] = $task_phids;
       }
     }
 
     if ($rev_map) {
       $revs = id(new DifferentialRevisionQuery())
         ->setViewer($this->getActor())
         ->withIDs(array_keys($rev_map))
         ->execute();
       $rev_phids = mpull($revs, 'getPHID', 'getPHID');
 
       // NOTE: Skip any write attempts if a user cleverly implies a revision
       // depends upon itself.
       unset($rev_phids[$object->getPHID()]);
 
       if ($revs) {
         $depends = DifferentialRevisionDependsOnRevisionEdgeType::EDGECONST;
         $edges[$depends] = $rev_phids;
       }
     }
 
     $this->setUnmentionablePHIDMap(array_merge($task_phids, $rev_phids));
 
     $result = array();
     foreach ($edges as $type => $specs) {
       $result[] = id(new DifferentialTransaction())
         ->setTransactionType(PhabricatorTransactions::TYPE_EDGE)
         ->setMetadataValue('edge:type', $type)
         ->setNewValue(array('+' => $specs));
     }
 
     return $result;
   }
 
-  protected function indentForMail(array $lines) {
-    $indented = array();
-    foreach ($lines as $line) {
-      $indented[] = '> '.$line;
-    }
-    return $indented;
-  }
+  private function appendInlineCommentsForMail(
+    PhabricatorLiskDAO $object,
+    array $inlines,
+    PhabricatorMetaMTAMailBody $body) {
 
-  protected function nestCommentHistory(
-    DifferentialTransactionComment $comment, array $comments_by_line_number,
-    array $users_by_phid) {
+    $section = id(new DifferentialInlineCommentMailView())
+      ->setViewer($this->getActor())
+      ->setInlines($inlines)
+      ->buildMailSection();
 
-    $nested = array();
-    $previous_comments = $comments_by_line_number[$comment->getChangesetID()]
-                                                 [$comment->getLineNumber()];
-    foreach ($previous_comments as $previous_comment) {
-      if ($previous_comment->getID() >= $comment->getID()) {
-        break;
-      }
-      $nested = $this->indentForMail(
-        array_merge(
-          $nested,
-          explode("\n", $previous_comment->getContent())));
-      $user = idx($users_by_phid, $previous_comment->getAuthorPHID(), null);
-      if ($user) {
-        array_unshift($nested, pht('%s wrote:', $user->getUserName()));
-      }
-    }
+    $header = pht('INLINE COMMENTS');
+
+    $section_text = "\n".$section->getPlaintext();
+
+    $style = array(
+      'margin: 6px 0 12px 0;',
+    );
 
-    $nested = array_merge($nested, explode("\n", $comment->getContent()));
-    return implode("\n", $nested);
+    $section_html = phutil_tag(
+      'div',
+      array(
+        'style' => implode(' ', $style),
+      ),
+      $section->getHTML());
+
+    $body->addPlaintextSection($header, $section_text, false);
+    $body->addHTMLSection($header, $section_html);
   }
 
-  private function renderInlineCommentsForMail(
+  private function appendChangeDetailsForMail(
     PhabricatorLiskDAO $object,
-    array $inlines) {
-
-    $context_key = 'metamta.differential.unified-comment-context';
-    $show_context = PhabricatorEnv::getEnvConfig($context_key);
-
-    $changeset_ids = array();
-    $line_numbers_by_changeset = array();
-    foreach ($inlines as $inline) {
-      $id = $inline->getComment()->getChangesetID();
-      $changeset_ids[$id] = $id;
-      $line_numbers_by_changeset[$id][] =
-        $inline->getComment()->getLineNumber();
-    }
+    DifferentialDiff $diff,
+    $patch,
+    PhabricatorMetaMTAMailBody $body) {
 
-    $changesets = id(new DifferentialChangesetQuery())
+    $section = id(new DifferentialChangeDetailMailView())
       ->setViewer($this->getActor())
-      ->withIDs($changeset_ids)
-      ->needHunks(true)
-      ->execute();
-
-    $inline_groups = DifferentialTransactionComment::sortAndGroupInlines(
-      $inlines,
-      $changesets);
-
-    if ($show_context) {
-      $hunk_parser = new DifferentialHunkParser();
-      $table = new DifferentialTransactionComment();
-      $conn_r = $table->establishConnection('r');
-      $queries = array();
-      foreach ($line_numbers_by_changeset as $id => $line_numbers) {
-        $queries[] = qsprintf(
-          $conn_r,
-          '(changesetID = %d AND lineNumber IN (%Ld))',
-          $id, $line_numbers);
-      }
-      $all_comments = id(new DifferentialTransactionComment())->loadAllWhere(
-        'transactionPHID IS NOT NULL AND (%Q)', implode(' OR ', $queries));
-      $comments_by_line_number = array();
-      foreach ($all_comments as $comment) {
-        $comments_by_line_number
-          [$comment->getChangesetID()]
-          [$comment->getLineNumber()]
-          [$comment->getID()] = $comment;
-      }
-      $author_phids = mpull($all_comments, 'getAuthorPHID');
-      $authors = id(new PhabricatorPeopleQuery())
-        ->setViewer($this->getActor())
-        ->withPHIDs($author_phids)
-        ->execute();
-      $authors_by_phid = mpull($authors, null, 'getPHID');
-    }
+      ->setDiff($diff)
+      ->setPatch($patch)
+      ->buildMailSection();
 
-    $section = new PhabricatorMetaMTAMailSection();
-    foreach ($inline_groups as $changeset_id => $group) {
-      $changeset = idx($changesets, $changeset_id);
-      if (!$changeset) {
-        continue;
-      }
+    $header = pht('CHANGE DETAILS');
 
-      foreach ($group as $inline) {
-        $comment = $inline->getComment();
-        $file = $changeset->getFilename();
-        $start = $comment->getLineNumber();
-        $len = $comment->getLineLength();
-        if ($len) {
-          $range = $start.'-'.($start + $len);
-        } else {
-          $range = $start;
-        }
+    $section_text = "\n".$section->getPlaintext();
 
-        $inline_content = $comment->getContent();
+    $style = array(
+      'margin: 6px 0 12px 0;',
+    );
 
-        if (!$show_context) {
-          $section->addFragment("{$file}:{$range} {$inline_content}");
-        } else {
-          $patch = $hunk_parser->makeContextDiff(
-            $changeset->getHunks(),
-            $comment->getIsNewFile(),
-            $comment->getLineNumber(),
-            $comment->getLineLength(),
-            1);
-          $nested_comments = $this->nestCommentHistory(
-            $inline->getComment(), $comments_by_line_number, $authors_by_phid);
-
-          $section
-            ->addFragment('================')
-            ->addFragment(pht('Comment at: %s:%s', $file, $range))
-            ->addPlaintextFragment($patch)
-            ->addHTMLFragment($this->renderPatchHTMLForMail($patch))
-            ->addFragment('----------------')
-            ->addFragment($nested_comments)
-            ->addFragment(null);
-        }
-      }
-    }
+    $section_html = phutil_tag(
+      'div',
+      array(
+        'style' => implode(' ', $style),
+      ),
+      $section->getHTML());
 
-    return $section;
+    $body->addPlaintextSection($header, $section_text, false);
+    $body->addHTMLSection($header, $section_html);
   }
 
   private function loadDiff($phid, $need_changesets = false) {
     $query = id(new DifferentialDiffQuery())
       ->withPHIDs(array($phid))
       ->setViewer($this->getActor());
 
     if ($need_changesets) {
       $query->needChangesets(true);
     }
 
     return $query->executeOne();
   }
 
   private function requireDiff($phid, $need_changesets = false) {
     $diff = $this->loadDiff($phid, $need_changesets);
     if (!$diff) {
       throw new Exception(pht('Diff "%s" does not exist!', $phid));
     }
 
     return $diff;
   }
 
 /* -(  Herald Integration  )------------------------------------------------- */
 
   protected function shouldApplyHeraldRules(
     PhabricatorLiskDAO $object,
     array $xactions) {
 
     if ($this->getIsNewObject()) {
       return true;
     }
 
     foreach ($xactions as $xaction) {
       switch ($xaction->getTransactionType()) {
         case DifferentialTransaction::TYPE_UPDATE:
           if (!$this->getIsCloseByCommit()) {
             return true;
           }
           break;
         case DifferentialTransaction::TYPE_ACTION:
           switch ($xaction->getNewValue()) {
             case DifferentialAction::ACTION_CLAIM:
               // When users commandeer revisions, we may need to trigger
               // signatures or author-based rules.
               return true;
           }
           break;
       }
     }
 
     return parent::shouldApplyHeraldRules($object, $xactions);
   }
 
   protected function buildHeraldAdapter(
     PhabricatorLiskDAO $object,
     array $xactions) {
 
     $revision = id(new DifferentialRevisionQuery())
       ->setViewer($this->getActor())
       ->withPHIDs(array($object->getPHID()))
       ->needActiveDiffs(true)
       ->needReviewerStatus(true)
       ->executeOne();
     if (!$revision) {
       throw new Exception(
         pht('Failed to load revision for Herald adapter construction!'));
     }
 
     $adapter = HeraldDifferentialRevisionAdapter::newLegacyAdapter(
       $revision,
       $revision->getActiveDiff());
 
     return $adapter;
   }
 
   /**
    * Update the table which links Differential revisions to paths they affect,
    * so Diffusion can efficiently find pending revisions for a given file.
    */
   private function updateAffectedPathTable(
     DifferentialRevision $revision,
     DifferentialDiff $diff) {
 
     $repository = $revision->getRepository();
     if (!$repository) {
       // The repository where the code lives is untracked.
       return;
     }
 
     $path_prefix = null;
 
     $local_root = $diff->getSourceControlPath();
     if ($local_root) {
       // We're in a working copy which supports subdirectory checkouts (e.g.,
       // SVN) so we need to figure out what prefix we should add to each path
       // (e.g., trunk/projects/example/) to get the absolute path from the
       // root of the repository. DVCS systems like Git and Mercurial are not
       // affected.
 
       // Normalize both paths and check if the repository root is a prefix of
       // the local root. If so, throw it away. Note that this correctly handles
       // the case where the remote path is "/".
       $local_root = id(new PhutilURI($local_root))->getPath();
       $local_root = rtrim($local_root, '/');
 
       $repo_root = id(new PhutilURI($repository->getRemoteURI()))->getPath();
       $repo_root = rtrim($repo_root, '/');
 
       if (!strncmp($repo_root, $local_root, strlen($repo_root))) {
         $path_prefix = substr($local_root, strlen($repo_root));
       }
     }
 
     $changesets = $diff->getChangesets();
     $paths = array();
     foreach ($changesets as $changeset) {
       $paths[] = $path_prefix.'/'.$changeset->getFilename();
     }
 
     // Mark this as also touching all parent paths, so you can see all pending
     // changes to any file within a directory.
     $all_paths = array();
     foreach ($paths as $local) {
       foreach (DiffusionPathIDQuery::expandPathToRoot($local) as $path) {
         $all_paths[$path] = true;
       }
     }
     $all_paths = array_keys($all_paths);
 
     $path_ids =
       PhabricatorRepositoryCommitChangeParserWorker::lookupOrCreatePaths(
         $all_paths);
 
     $table = new DifferentialAffectedPath();
     $conn_w = $table->establishConnection('w');
 
     $sql = array();
     foreach ($path_ids as $path_id) {
       $sql[] = qsprintf(
         $conn_w,
         '(%d, %d, %d, %d)',
         $repository->getID(),
         $path_id,
         time(),
         $revision->getID());
     }
 
     queryfx(
       $conn_w,
       'DELETE FROM %T WHERE revisionID = %d',
       $table->getTableName(),
       $revision->getID());
     foreach (array_chunk($sql, 256) as $chunk) {
       queryfx(
         $conn_w,
         'INSERT INTO %T (repositoryID, pathID, epoch, revisionID) VALUES %Q',
         $table->getTableName(),
         implode(', ', $chunk));
     }
   }
 
   /**
    * Update the table connecting revisions to DVCS local hashes, so we can
    * identify revisions by commit/tree hashes.
    */
   private function updateRevisionHashTable(
     DifferentialRevision $revision,
     DifferentialDiff $diff) {
 
     $vcs = $diff->getSourceControlSystem();
     if ($vcs == DifferentialRevisionControlSystem::SVN) {
       // Subversion has no local commit or tree hash information, so we don't
       // have to do anything.
       return;
     }
 
     $property = id(new DifferentialDiffProperty())->loadOneWhere(
       'diffID = %d AND name = %s',
       $diff->getID(),
       'local:commits');
     if (!$property) {
       return;
     }
 
     $hashes = array();
 
     $data = $property->getData();
     switch ($vcs) {
       case DifferentialRevisionControlSystem::GIT:
         foreach ($data as $commit) {
           $hashes[] = array(
             ArcanistDifferentialRevisionHash::HASH_GIT_COMMIT,
             $commit['commit'],
           );
           $hashes[] = array(
             ArcanistDifferentialRevisionHash::HASH_GIT_TREE,
             $commit['tree'],
           );
         }
         break;
       case DifferentialRevisionControlSystem::MERCURIAL:
         foreach ($data as $commit) {
           $hashes[] = array(
             ArcanistDifferentialRevisionHash::HASH_MERCURIAL_COMMIT,
             $commit['rev'],
           );
         }
         break;
     }
 
     $conn_w = $revision->establishConnection('w');
 
     $sql = array();
     foreach ($hashes as $info) {
       list($type, $hash) = $info;
       $sql[] = qsprintf(
         $conn_w,
         '(%d, %s, %s)',
         $revision->getID(),
         $type,
         $hash);
     }
 
     queryfx(
       $conn_w,
       'DELETE FROM %T WHERE revisionID = %d',
       ArcanistDifferentialRevisionHash::TABLE_NAME,
       $revision->getID());
 
     if ($sql) {
       queryfx(
         $conn_w,
         'INSERT INTO %T (revisionID, type, hash) VALUES %Q',
         ArcanistDifferentialRevisionHash::TABLE_NAME,
         implode(', ', $sql));
     }
   }
 
   private function renderAffectedFilesForMail(DifferentialDiff $diff) {
     $changesets = $diff->getChangesets();
 
     $filenames = mpull($changesets, 'getDisplayFilename');
     sort($filenames);
 
     $count = count($filenames);
     $max = 250;
     if ($count > $max) {
       $filenames = array_slice($filenames, 0, $max);
       $filenames[] = pht('(%d more files...)', ($count - $max));
     }
 
     return implode("\n", $filenames);
   }
 
   private function renderPatchHTMLForMail($patch) {
     return phutil_tag('pre',
       array('style' => 'font-family: monospace;'), $patch);
   }
 
-  private function renderPatchForMail(DifferentialDiff $diff) {
+  private function buildPatchForMail(DifferentialDiff $diff) {
     $format = PhabricatorEnv::getEnvConfig('metamta.differential.patch-format');
 
-    $patch = id(new DifferentialRawDiffRenderer())
+    return id(new DifferentialRawDiffRenderer())
       ->setViewer($this->getActor())
       ->setFormat($format)
       ->setChangesets($diff->getChangesets())
       ->buildPatch();
-
-    $section = new PhabricatorMetaMTAMailSection();
-    $section->addHTMLFragment($this->renderPatchHTMLForMail($patch));
-    $section->addPlaintextFragment($patch);
-
-    return $section;
   }
 
   protected function willPublish(PhabricatorLiskDAO $object, array $xactions) {
     // Reload to pick up the active diff and reviewer status.
     return id(new DifferentialRevisionQuery())
       ->setViewer($this->getActor())
       ->needReviewerStatus(true)
       ->needActiveDiffs(true)
       ->withIDs(array($object->getID()))
       ->executeOne();
   }
 
   protected function getCustomWorkerState() {
     return array(
       'changedPriorToCommitURI' => $this->changedPriorToCommitURI,
     );
   }
 
   protected function loadCustomWorkerState(array $state) {
     $this->changedPriorToCommitURI = idx($state, 'changedPriorToCommitURI');
     return $this;
   }
 
 }
diff --git a/src/applications/differential/mail/DifferentialChangeDetailMailView.php b/src/applications/differential/mail/DifferentialChangeDetailMailView.php
new file mode 100644
index 000000000..d0f0be71b
--- /dev/null
+++ b/src/applications/differential/mail/DifferentialChangeDetailMailView.php
@@ -0,0 +1,77 @@
+<?php
+
+final class DifferentialChangeDetailMailView
+  extends DifferentialMailView {
+
+  private $viewer;
+  private $diff;
+  private $patch;
+
+  public function setViewer(PhabricatorUser $viewer) {
+    $this->viewer = $viewer;
+    return $this;
+  }
+
+  public function getViewer() {
+    return $this->viewer;
+  }
+
+  public function setDiff(DifferentialDiff $diff) {
+    $this->diff = $diff;
+    return $this;
+  }
+
+  public function getDiff() {
+    return $this->diff;
+  }
+
+  public function setPatch($patch) {
+    $this->patch = $patch;
+    return $this;
+  }
+
+  public function getPatch() {
+    return $this->patch;
+  }
+
+  public function buildMailSection() {
+    $viewer = $this->getViewer();
+
+    $diff = $this->getDiff();
+
+    $engine = new PhabricatorMarkupEngine();
+
+    $out = array();
+    foreach ($diff->getChangesets() as $changeset) {
+      $parser = id(new DifferentialChangesetParser())
+        ->setUser($viewer)
+        ->setChangeset($changeset)
+        ->setLinesOfContext(2)
+        ->setMarkupEngine($engine);
+
+      $parser->setRenderer(new DifferentialChangesetOneUpMailRenderer());
+      $block = $parser->render();
+
+      $filename = $changeset->getFilename();
+      $filename = $this->renderHeaderBold($filename);
+      $header = $this->renderHeaderBlock($filename);
+
+      $out[] = $this->renderContentBox(
+        array(
+          $header,
+          $this->renderCodeBlock($block),
+        ));
+    }
+
+    $out = phutil_implode_html(phutil_tag('br'), $out);
+
+    $patch_html = $out;
+
+    $patch_text = $this->getPatch();
+
+    return id(new PhabricatorMetaMTAMailSection())
+      ->addPlaintextFragment($patch_text)
+      ->addHTMLFragment($patch_html);
+  }
+
+}
diff --git a/src/applications/differential/mail/DifferentialInlineCommentMailView.php b/src/applications/differential/mail/DifferentialInlineCommentMailView.php
new file mode 100644
index 000000000..57958202b
--- /dev/null
+++ b/src/applications/differential/mail/DifferentialInlineCommentMailView.php
@@ -0,0 +1,477 @@
+<?php
+
+final class DifferentialInlineCommentMailView
+  extends DifferentialMailView {
+
+  private $viewer;
+  private $inlines;
+  private $changesets;
+  private $authors;
+
+  public function setViewer(PhabricatorUser $viewer) {
+    $this->viewer = $viewer;
+    return $this;
+  }
+
+  public function getViewer() {
+    return $this->viewer;
+  }
+
+  public function setInlines($inlines) {
+    $this->inlines = $inlines;
+    return $this;
+  }
+
+  public function getInlines() {
+    return $this->inlines;
+  }
+
+  public function buildMailSection() {
+    $inlines = $this->getInlines();
+
+    $comments = mpull($inlines, 'getComment');
+    $comments = mpull($comments, null, 'getPHID');
+    $parents = $this->loadParents($comments);
+    $all_comments = $comments + $parents;
+
+    $this->changesets = $this->loadChangesets($all_comments);
+    $this->authors = $this->loadAuthors($all_comments);
+    $groups = $this->groupInlines($inlines);
+
+    $hunk_parser = new DifferentialHunkParser();
+
+    $spacer_text = null;
+    $spacer_html = phutil_tag('br');
+
+    $section = new PhabricatorMetaMTAMailSection();
+
+    $last_group_key = last_key($groups);
+    foreach ($groups as $changeset_id => $group) {
+      $changeset = $this->getChangeset($changeset_id);
+      if (!$changeset) {
+        continue;
+      }
+
+      $is_last_group = ($changeset_id == $last_group_key);
+
+      $last_inline_key = last_key($group);
+      foreach ($group as $inline_key => $inline) {
+        $comment = $inline->getComment();
+        $parent_phid = $comment->getReplyToCommentPHID();
+
+        $is_last_inline = ($inline_key == $last_inline_key);
+
+        $context_text = null;
+        $context_html = null;
+
+        if ($parent_phid) {
+          $parent = idx($parents, $parent_phid);
+          if ($parent) {
+            $context_text = $this->renderInline($parent, false, true);
+            $context_html = $this->renderInline($parent, true, true);
+          }
+        } else {
+          $patch_text = $this->getPatch($hunk_parser, $comment, false);
+          $context_text = $this->renderPatch($comment, $patch_text, false);
+
+          $patch_html = $this->getPatch($hunk_parser, $comment, true);
+          $context_html = $this->renderPatch($comment, $patch_html, true);
+        }
+
+        $render_text = $this->renderInline($comment, false, false);
+        $render_html = $this->renderInline($comment, true, false);
+
+        $section->addPlaintextFragment($context_text);
+        $section->addPlaintextFragment($spacer_text);
+        $section->addPlaintextFragment($render_text);
+
+        $html_fragment = $this->renderContentBox(
+          array(
+            $context_html,
+            $render_html,
+          ));
+
+        $section->addHTMLFragment($html_fragment);
+
+        if (!$is_last_group || !$is_last_inline) {
+          $section->addPlaintextFragment($spacer_text);
+          $section->addHTMLFragment($spacer_html);
+        }
+      }
+    }
+
+    return $section;
+  }
+
+  private function loadChangesets(array $comments) {
+    if (!$comments) {
+      return array();
+    }
+
+    $ids = array();
+    foreach ($comments as $comment) {
+      $ids[] = $comment->getChangesetID();
+    }
+
+    $changesets = id(new DifferentialChangesetQuery())
+      ->setViewer($this->getViewer())
+      ->withIDs($ids)
+      ->needHunks(true)
+      ->execute();
+
+    return mpull($changesets, null, 'getID');
+  }
+
+  private function loadParents(array $comments) {
+    $viewer = $this->getViewer();
+
+    $phids = array();
+    foreach ($comments as $comment) {
+      $parent_phid = $comment->getReplyToCommentPHID();
+      if (!$parent_phid) {
+        continue;
+      }
+      $phids[] = $parent_phid;
+    }
+
+    if (!$phids) {
+      return array();
+    }
+
+    $parents = id(new DifferentialDiffInlineCommentQuery())
+      ->setViewer($viewer)
+      ->withPHIDs($phids)
+      ->execute();
+
+    return mpull($parents, null, 'getPHID');
+  }
+
+  private function loadAuthors(array $comments) {
+    $viewer = $this->getViewer();
+
+    $phids = array();
+    foreach ($comments as $comment) {
+      $author_phid = $comment->getAuthorPHID();
+      if (!$author_phid) {
+        continue;
+      }
+      $phids[] = $author_phid;
+    }
+
+    if (!$phids) {
+      return array();
+    }
+
+    return $viewer->loadHandles($phids);
+  }
+
+  private function groupInlines(array $inlines) {
+    return DifferentialTransactionComment::sortAndGroupInlines(
+      $inlines,
+      $this->changesets);
+  }
+
+  private function renderInline(
+    DifferentialTransactionComment $comment,
+    $is_html,
+    $is_quote) {
+
+    $changeset = $this->getChangeset($comment->getChangesetID());
+    if (!$changeset) {
+      return null;
+    }
+
+    $content = $comment->getContent();
+    $content = $this->renderRemarkupContent($content, $is_html);
+
+    if ($is_quote) {
+      $header = $this->renderHeader($comment, $is_html, true);
+    } else {
+      $header = null;
+    }
+
+    if ($is_html) {
+      $style = array(
+        'margin: 8px 0;',
+        'padding: 0 12px;',
+      );
+
+      if ($is_quote) {
+        $style[] = 'color: #74777D;';
+      }
+
+      $content = phutil_tag(
+        'div',
+        array(
+          'style' => implode(' ', $style),
+        ),
+        $content);
+    }
+
+    $parts = array(
+      $header,
+      "\n",
+      $content,
+    );
+
+    if (!$is_html) {
+      $parts = implode('', $parts);
+      $parts = trim($parts);
+    }
+
+    if ($is_quote) {
+      if ($is_html) {
+        $parts = $this->quoteHTML($parts);
+      } else {
+        $parts = $this->quoteText($parts);
+      }
+    }
+
+    return $parts;
+  }
+
+  private function renderRemarkupContent($content, $is_html) {
+    $viewer = $this->getViewer();
+    $production_uri = PhabricatorEnv::getProductionURI('/');
+
+    if ($is_html) {
+      $mode = PhutilRemarkupEngine::MODE_HTML_MAIL;
+    } else {
+      $mode = PhutilRemarkupEngine::MODE_TEXT;
+    }
+
+    $attributes = array(
+      'style' => 'padding: 0; margin: 8px;',
+    );
+
+    $engine = PhabricatorMarkupEngine::newMarkupEngine(array())
+      ->setConfig('viewer', $viewer)
+      ->setConfig('uri.base', $production_uri)
+      ->setConfig('default.p.attributes', $attributes)
+      ->setMode($mode);
+
+    try {
+      return $engine->markupText($content);
+    } catch (Exception $ex) {
+      return $content;
+    }
+  }
+
+  private function getChangeset($id) {
+    return idx($this->changesets, $id);
+  }
+
+  private function getAuthor($phid) {
+    if (isset($this->authors[$phid])) {
+      return $this->authors[$phid];
+    }
+    return null;
+  }
+
+  private function quoteText($block) {
+    $block = phutil_split_lines($block);
+    foreach ($block as $key => $line) {
+      $block[$key] = '> '.$line;
+    }
+
+    return implode('', $block);
+  }
+
+  private function quoteHTML($block) {
+    $styles = array(
+      'padding: 0;',
+      'background: #F7F7F7;',
+      'border-color: #e3e4e8;',
+      'border-style: solid;',
+      'border-width: 0 0 1px 0;',
+      'margin: 0;',
+    );
+
+    $styles = implode(' ', $styles);
+
+    return phutil_tag(
+      'div',
+      array(
+        'style' => $styles,
+      ),
+      $block);
+  }
+
+  private function getPatch(
+    DifferentialHunkParser $parser,
+    DifferentialTransactionComment $comment,
+    $is_html) {
+
+    $changeset = $this->getChangeset($comment->getChangesetID());
+    $is_new = $comment->getIsNewFile();
+    $start = $comment->getLineNumber();
+    $length = $comment->getLineLength();
+
+    // By default, show one line of context around the target inline.
+    $context = 1;
+
+    // If the inline is at least 3 lines long, don't show any extra context.
+    if ($length >= 2) {
+      $context = 0;
+    }
+
+    // If the inline is more than 7 lines long, only show the first 7 lines.
+    if ($length >= 6) {
+      $length = 6;
+    }
+
+    if (!$is_html) {
+      $hunks = $changeset->getHunks();
+      $patch = $parser->makeContextDiff(
+        $hunks,
+        $is_new,
+        $start,
+        $length,
+        $context);
+      $patch = phutil_split_lines($patch);
+
+      // Remove the "@@ -x,y +u,v @@" line.
+      array_shift($patch);
+
+      return implode('', $patch);
+    }
+
+    $viewer = $this->getViewer();
+    $engine = new PhabricatorMarkupEngine();
+
+    if ($is_new) {
+      $offset_mode = 'new';
+    } else {
+      $offset_mode = 'old';
+    }
+
+    $parser = id(new DifferentialChangesetParser())
+      ->setUser($viewer)
+      ->setChangeset($changeset)
+      ->setOffsetMode($offset_mode)
+      ->setMarkupEngine($engine);
+
+    $parser->setRenderer(new DifferentialChangesetOneUpMailRenderer());
+
+    return $parser->render(
+      $start - $context,
+      $length + 1 + (2 * $context),
+      array());
+  }
+
+  private function renderPatch(
+    DifferentialTransactionComment $comment,
+    $patch,
+    $is_html) {
+
+    if ($is_html) {
+      $patch = $this->renderCodeBlock($patch);
+    }
+
+    $header = $this->renderHeader($comment, $is_html, false);
+
+    $patch = array(
+      $header,
+      "\n",
+      $patch,
+    );
+
+    if (!$is_html) {
+      $patch = implode('', $patch);
+      $patch = $this->quoteText($patch);
+    } else {
+      $patch = $this->quoteHTML($patch);
+    }
+
+    return $patch;
+  }
+
+  private function renderHeader(
+    DifferentialTransactionComment $comment,
+    $is_html,
+    $with_author) {
+
+    $changeset = $this->getChangeset($comment->getChangesetID());
+    $path = $changeset->getFilename();
+
+    // Only show the filename.
+    $path = basename($path);
+
+    $start = $comment->getLineNumber();
+    $length = $comment->getLineLength();
+    if ($length) {
+      $range = pht('%s-%s', $start, $start + $length);
+    } else {
+      $range = $start;
+    }
+
+    $header = "{$path}:{$range}";
+    if ($is_html) {
+      $header = $this->renderHeaderBold($header);
+    }
+
+    if ($with_author) {
+      $author = $this->getAuthor($comment->getAuthorPHID());
+    } else {
+      $author = null;
+    }
+
+    if ($author) {
+      $byline = $author->getName();
+
+      if ($is_html) {
+        $byline = $this->renderHeaderBold($byline);
+      }
+
+      $header = pht('%s wrote in %s', $byline, $header);
+    }
+
+    if ($is_html) {
+      $link_href = $this->getInlineURI($comment);
+      if ($link_href) {
+        $link_style = array(
+          'float: right;',
+          'text-decoration: none;',
+        );
+
+        $link = phutil_tag(
+          'a',
+          array(
+            'style' => implode(' ', $link_style),
+            'href' => $link_href,
+          ),
+          pht('View Inline'));
+      } else {
+        $link = null;
+      }
+
+      $header = $this->renderHeaderBlock(array($link, $header));
+    }
+
+    return $header;
+  }
+
+  private function getInlineURI(DifferentialTransactionComment $comment) {
+    $changeset = $this->getChangeset($comment->getChangesetID());
+    if (!$changeset) {
+      return null;
+    }
+
+    $diff = $changeset->getDiff();
+    if (!$diff) {
+      return null;
+    }
+
+    $revision = $diff->getRevision();
+    if (!$revision) {
+      return null;
+    }
+
+    $link_href = '/'.$revision->getMonogram().'#inline-'.$comment->getID();
+    $link_href = PhabricatorEnv::getProductionURI($link_href);
+
+    return $link_href;
+  }
+
+
+}
diff --git a/src/applications/differential/mail/DifferentialMailView.php b/src/applications/differential/mail/DifferentialMailView.php
new file mode 100644
index 000000000..81b783676
--- /dev/null
+++ b/src/applications/differential/mail/DifferentialMailView.php
@@ -0,0 +1,62 @@
+<?php
+
+abstract class DifferentialMailView
+  extends Phobject {
+
+  protected function renderCodeBlock($block) {
+    $style = array(
+      'font: 11px/15px "Menlo", "Consolas", "Monaco", monospace;',
+      'white-space: pre-wrap;',
+      'clear: both;',
+      'padding: 4px 0;',
+      'margin: 0;',
+    );
+
+    return phutil_tag(
+      'div',
+      array(
+        'style' => implode(' ', $style),
+      ),
+      $block);
+  }
+
+  protected function renderHeaderBlock($block) {
+    $style = array(
+      'color: #74777d;',
+      'background: #eff2f4;',
+      'padding: 6px 8px;',
+      'overflow: hidden;',
+    );
+
+    return phutil_tag(
+      'div',
+      array(
+        'style' => implode(' ', $style),
+      ),
+      $block);
+  }
+
+  protected function renderHeaderBold($content) {
+    return phutil_tag(
+      'span',
+      array(
+        'style' => 'color: #4b4d51; font-weight: bold;',
+      ),
+      $content);
+  }
+
+  protected function renderContentBox($content) {
+    $style = array(
+      'border: 1px solid #C7CCD9;',
+      'border-radius: 3px;',
+    );
+
+    return phutil_tag(
+      'div',
+      array(
+        'style' => implode(' ', $style),
+      ),
+      $content);
+  }
+
+}
diff --git a/src/applications/differential/parser/DifferentialChangesetParser.php b/src/applications/differential/parser/DifferentialChangesetParser.php
index f6c7309ec..9fc0adf53 100644
--- a/src/applications/differential/parser/DifferentialChangesetParser.php
+++ b/src/applications/differential/parser/DifferentialChangesetParser.php
@@ -1,1636 +1,1692 @@
 <?php
 
 final class DifferentialChangesetParser extends Phobject {
 
   const HIGHLIGHT_BYTE_LIMIT = 262144;
 
   protected $visible      = array();
   protected $new          = array();
   protected $old          = array();
   protected $intra        = array();
   protected $newRender    = null;
   protected $oldRender    = null;
 
   protected $filename     = null;
   protected $hunkStartLines = array();
 
   protected $comments     = array();
   protected $specialAttributes = array();
 
   protected $changeset;
   protected $whitespaceMode = null;
 
   protected $renderCacheKey = null;
 
   private $handles = array();
   private $user;
 
   private $leftSideChangesetID;
   private $leftSideAttachesToNewFile;
 
   private $rightSideChangesetID;
   private $rightSideAttachesToNewFile;
 
   private $originalLeft;
   private $originalRight;
 
   private $renderingReference;
   private $isSubparser;
 
   private $isTopLevel;
 
   private $coverage;
   private $markupEngine;
   private $highlightErrors;
   private $disableCache;
   private $renderer;
   private $characterEncoding;
   private $highlightAs;
   private $highlightingDisabled;
   private $showEditAndReplyLinks = true;
   private $canMarkDone;
   private $objectOwnerPHID;
+  private $offsetMode;
 
   private $rangeStart;
   private $rangeEnd;
   private $mask;
+  private $linesOfContext = 8;
 
   private $highlightEngine;
 
   public function setRange($start, $end) {
     $this->rangeStart = $start;
     $this->rangeEnd = $end;
     return $this;
   }
 
   public function setMask(array $mask) {
     $this->mask = $mask;
     return $this;
   }
 
   public function renderChangeset() {
     return $this->render($this->rangeStart, $this->rangeEnd, $this->mask);
   }
 
   public function setShowEditAndReplyLinks($bool) {
     $this->showEditAndReplyLinks = $bool;
     return $this;
   }
 
   public function getShowEditAndReplyLinks() {
     return $this->showEditAndReplyLinks;
   }
 
   public function setHighlightAs($highlight_as) {
     $this->highlightAs = $highlight_as;
     return $this;
   }
 
   public function getHighlightAs() {
     return $this->highlightAs;
   }
 
   public function setCharacterEncoding($character_encoding) {
     $this->characterEncoding = $character_encoding;
     return $this;
   }
 
   public function getCharacterEncoding() {
     return $this->characterEncoding;
   }
 
   public function setRenderer(DifferentialChangesetRenderer $renderer) {
     $this->renderer = $renderer;
     return $this;
   }
 
   public function getRenderer() {
     if (!$this->renderer) {
       return new DifferentialChangesetTwoUpRenderer();
     }
     return $this->renderer;
   }
 
   public function setDisableCache($disable_cache) {
     $this->disableCache = $disable_cache;
     return $this;
   }
 
   public function getDisableCache() {
     return $this->disableCache;
   }
 
   public function setCanMarkDone($can_mark_done) {
     $this->canMarkDone = $can_mark_done;
     return $this;
   }
 
   public function getCanMarkDone() {
     return $this->canMarkDone;
   }
 
   public function setObjectOwnerPHID($phid) {
     $this->objectOwnerPHID = $phid;
     return $this;
   }
 
   public function getObjectOwnerPHID() {
     return $this->objectOwnerPHID;
   }
 
+  public function setOffsetMode($offset_mode) {
+    $this->offsetMode = $offset_mode;
+    return $this;
+  }
+
+  public function getOffsetMode() {
+    return $this->offsetMode;
+  }
+
   public static function getDefaultRendererForViewer(PhabricatorUser $viewer) {
     $prefs = $viewer->loadPreferences();
     $pref_unified = PhabricatorUserPreferences::PREFERENCE_DIFF_UNIFIED;
     if ($prefs->getPreference($pref_unified) == 'unified') {
       return '1up';
     }
     return null;
   }
 
   public function readParametersFromRequest(AphrontRequest $request) {
     $this->setWhitespaceMode($request->getStr('whitespace'));
     $this->setCharacterEncoding($request->getStr('encoding'));
     $this->setHighlightAs($request->getStr('highlight'));
 
     $renderer = null;
 
     // If the viewer prefers unified diffs, always set the renderer to unified.
     // Otherwise, we leave it unspecified and the client will choose a
     // renderer based on the screen size.
 
     if ($request->getStr('renderer')) {
       $renderer = $request->getStr('renderer');
     } else {
       $renderer = self::getDefaultRendererForViewer($request->getViewer());
     }
 
     switch ($renderer) {
       case '1up':
         $this->setRenderer(new DifferentialChangesetOneUpRenderer());
         break;
       default:
         $this->setRenderer(new DifferentialChangesetTwoUpRenderer());
         break;
     }
 
     return $this;
   }
 
   const CACHE_VERSION = 11;
   const CACHE_MAX_SIZE = 8e6;
 
   const ATTR_GENERATED  = 'attr:generated';
   const ATTR_DELETED    = 'attr:deleted';
   const ATTR_UNCHANGED  = 'attr:unchanged';
   const ATTR_WHITELINES = 'attr:white';
   const ATTR_MOVEAWAY   = 'attr:moveaway';
 
-  const LINES_CONTEXT = 8;
-
   const WHITESPACE_SHOW_ALL         = 'show-all';
   const WHITESPACE_IGNORE_TRAILING  = 'ignore-trailing';
   const WHITESPACE_IGNORE_MOST      = 'ignore-most';
   const WHITESPACE_IGNORE_ALL       = 'ignore-all';
 
   public function setOldLines(array $lines) {
     $this->old = $lines;
     return $this;
   }
 
   public function setNewLines(array $lines) {
     $this->new = $lines;
     return $this;
   }
 
   public function setSpecialAttributes(array $attributes) {
     $this->specialAttributes = $attributes;
     return $this;
   }
 
   public function setIntraLineDiffs(array $diffs) {
     $this->intra = $diffs;
     return $this;
   }
 
   public function setVisibileLinesMask(array $mask) {
     $this->visible = $mask;
     return $this;
   }
 
+  public function setLinesOfContext($lines_of_context) {
+    $this->linesOfContext = $lines_of_context;
+    return $this;
+  }
+
+  public function getLinesOfContext() {
+    return $this->linesOfContext;
+  }
+
+
   /**
    * Configure which Changeset comments added to the right side of the visible
    * diff will be attached to. The ID must be the ID of a real Differential
    * Changeset.
    *
    * The complexity here is that we may show an arbitrary side of an arbitrary
    * changeset as either the left or right part of a diff. This method allows
    * the left and right halves of the displayed diff to be correctly mapped to
    * storage changesets.
    *
    * @param id    The Differential Changeset ID that comments added to the right
    *              side of the visible diff should be attached to.
    * @param bool  If true, attach new comments to the right side of the storage
    *              changeset. Note that this may be false, if the left side of
    *              some storage changeset is being shown as the right side of
    *              a display diff.
    * @return this
    */
   public function setRightSideCommentMapping($id, $is_new) {
     $this->rightSideChangesetID = $id;
     $this->rightSideAttachesToNewFile = $is_new;
     return $this;
   }
 
   /**
    * See setRightSideCommentMapping(), but this sets information for the left
    * side of the display diff.
    */
   public function setLeftSideCommentMapping($id, $is_new) {
     $this->leftSideChangesetID = $id;
     $this->leftSideAttachesToNewFile = $is_new;
     return $this;
   }
 
   public function setOriginals(
     DifferentialChangeset $left,
     DifferentialChangeset $right) {
 
     $this->originalLeft = $left;
     $this->originalRight = $right;
     return $this;
   }
 
   public function diffOriginals() {
     $engine = new PhabricatorDifferenceEngine();
     $changeset = $engine->generateChangesetFromFileContent(
       implode('', mpull($this->originalLeft->getHunks(), 'getChanges')),
       implode('', mpull($this->originalRight->getHunks(), 'getChanges')));
 
     $parser = new DifferentialHunkParser();
 
     return $parser->parseHunksForHighlightMasks(
       $changeset->getHunks(),
       $this->originalLeft->getHunks(),
       $this->originalRight->getHunks());
   }
 
   /**
    * Set a key for identifying this changeset in the render cache. If set, the
    * parser will attempt to use the changeset render cache, which can improve
    * performance for frequently-viewed changesets.
    *
    * By default, there is no render cache key and parsers do not use the cache.
    * This is appropriate for rarely-viewed changesets.
    *
    * NOTE: Currently, this key must be a valid Differential Changeset ID.
    *
    * @param   string  Key for identifying this changeset in the render cache.
    * @return  this
    */
   public function setRenderCacheKey($key) {
     $this->renderCacheKey = $key;
     return $this;
   }
 
   private function getRenderCacheKey() {
     return $this->renderCacheKey;
   }
 
   public function setChangeset(DifferentialChangeset $changeset) {
     $this->changeset = $changeset;
 
     $this->setFilename($changeset->getFilename());
 
     return $this;
   }
 
   public function setWhitespaceMode($whitespace_mode) {
     $this->whitespaceMode = $whitespace_mode;
     return $this;
   }
 
   public function setRenderingReference($ref) {
     $this->renderingReference = $ref;
     return $this;
   }
 
   private function getRenderingReference() {
     return $this->renderingReference;
   }
 
   public function getChangeset() {
     return $this->changeset;
   }
 
   public function setFilename($filename) {
     $this->filename = $filename;
     return $this;
   }
 
   public function setHandles(array $handles) {
     assert_instances_of($handles, 'PhabricatorObjectHandle');
     $this->handles = $handles;
     return $this;
   }
 
   public function setMarkupEngine(PhabricatorMarkupEngine $engine) {
     $this->markupEngine = $engine;
     return $this;
   }
 
   public function setUser(PhabricatorUser $user) {
     $this->user = $user;
     return $this;
   }
 
   public function getUser() {
     return $this->user;
   }
 
   public function setCoverage($coverage) {
     $this->coverage = $coverage;
     return $this;
   }
   private function getCoverage() {
     return $this->coverage;
   }
 
   public function parseInlineComment(
     PhabricatorInlineCommentInterface $comment) {
 
     // Parse only comments which are actually visible.
     if ($this->isCommentVisibleOnRenderedDiff($comment)) {
       $this->comments[] = $comment;
     }
     return $this;
   }
 
   private function loadCache() {
     $render_cache_key = $this->getRenderCacheKey();
     if (!$render_cache_key) {
       return false;
     }
 
     $data = null;
 
     $changeset = new DifferentialChangeset();
     $conn_r = $changeset->establishConnection('r');
     $data = queryfx_one(
       $conn_r,
       'SELECT * FROM %T WHERE id = %d',
       $changeset->getTableName().'_parse_cache',
       $render_cache_key);
 
     if (!$data) {
       return false;
     }
 
     if ($data['cache'][0] == '{') {
       // This is likely an old-style JSON cache which we will not be able to
       // deserialize.
       return false;
     }
 
     $data = unserialize($data['cache']);
     if (!is_array($data) || !$data) {
       return false;
     }
 
     foreach (self::getCacheableProperties() as $cache_key) {
       if (!array_key_exists($cache_key, $data)) {
         // If we're missing a cache key, assume we're looking at an old cache
         // and ignore it.
         return false;
       }
     }
 
     if ($data['cacheVersion'] !== self::CACHE_VERSION) {
       return false;
     }
 
     // Someone displays contents of a partially cached shielded file.
     if (!isset($data['newRender']) && (!$this->isTopLevel || $this->comments)) {
       return false;
     }
 
     unset($data['cacheVersion'], $data['cacheHost']);
     $cache_prop = array_select_keys($data, self::getCacheableProperties());
     foreach ($cache_prop as $cache_key => $v) {
       $this->$cache_key = $v;
     }
 
     return true;
   }
 
   protected static function getCacheableProperties() {
     return array(
       'visible',
       'new',
       'old',
       'intra',
       'newRender',
       'oldRender',
       'specialAttributes',
       'hunkStartLines',
       'cacheVersion',
       'cacheHost',
       'highlightingDisabled',
     );
   }
 
   public function saveCache() {
     if ($this->highlightErrors) {
       return false;
     }
 
     $render_cache_key = $this->getRenderCacheKey();
     if (!$render_cache_key) {
       return false;
     }
 
     $cache = array();
     foreach (self::getCacheableProperties() as $cache_key) {
       switch ($cache_key) {
         case 'cacheVersion':
           $cache[$cache_key] = self::CACHE_VERSION;
           break;
         case 'cacheHost':
           $cache[$cache_key] = php_uname('n');
           break;
         default:
           $cache[$cache_key] = $this->$cache_key;
           break;
       }
     }
     $cache = serialize($cache);
 
     // We don't want to waste too much space by a single changeset.
     if (strlen($cache) > self::CACHE_MAX_SIZE) {
       return;
     }
 
     $changeset = new DifferentialChangeset();
     $conn_w = $changeset->establishConnection('w');
 
     $unguarded = AphrontWriteGuard::beginScopedUnguardedWrites();
       try {
         queryfx(
           $conn_w,
           'INSERT INTO %T (id, cache, dateCreated) VALUES (%d, %B, %d)
             ON DUPLICATE KEY UPDATE cache = VALUES(cache)',
           DifferentialChangeset::TABLE_CACHE,
           $render_cache_key,
           $cache,
           time());
       } catch (AphrontQueryException $ex) {
         // Ignore these exceptions. A common cause is that the cache is
         // larger than 'max_allowed_packet', in which case we're better off
         // not writing it.
 
         // TODO: It would be nice to tailor this more narrowly.
       }
     unset($unguarded);
   }
 
   private function markGenerated($new_corpus_block = '') {
     $generated_guess = (strpos($new_corpus_block, '@'.'generated') !== false);
 
     if (!$generated_guess) {
       $generated_path_regexps = PhabricatorEnv::getEnvConfig(
         'differential.generated-paths');
       foreach ($generated_path_regexps as $regexp) {
         if (preg_match($regexp, $this->changeset->getFilename())) {
           $generated_guess = true;
           break;
         }
       }
     }
 
     $event = new PhabricatorEvent(
       PhabricatorEventType::TYPE_DIFFERENTIAL_WILLMARKGENERATED,
       array(
         'corpus' => $new_corpus_block,
         'is_generated' => $generated_guess,
       )
     );
     PhutilEventEngine::dispatchEvent($event);
 
     $generated = $event->getValue('is_generated');
     $this->specialAttributes[self::ATTR_GENERATED] = $generated;
   }
 
   public function isGenerated() {
     return idx($this->specialAttributes, self::ATTR_GENERATED, false);
   }
 
   public function isDeleted() {
     return idx($this->specialAttributes, self::ATTR_DELETED, false);
   }
 
   public function isUnchanged() {
     return idx($this->specialAttributes, self::ATTR_UNCHANGED, false);
   }
 
   public function isWhitespaceOnly() {
     return idx($this->specialAttributes, self::ATTR_WHITELINES, false);
   }
 
   public function isMoveAway() {
     return idx($this->specialAttributes, self::ATTR_MOVEAWAY, false);
   }
 
   private function applyIntraline(&$render, $intra, $corpus) {
 
     foreach ($render as $key => $text) {
       if (isset($intra[$key])) {
         $render[$key] = ArcanistDiffUtils::applyIntralineDiff(
           $text,
           $intra[$key]);
       }
     }
   }
 
   private function getHighlightFuture($corpus) {
     $language = $this->highlightAs;
 
     if (!$language) {
       $language = $this->highlightEngine->getLanguageFromFilename(
         $this->filename);
 
       if (($language != 'txt') &&
           (strlen($corpus) > self::HIGHLIGHT_BYTE_LIMIT)) {
         $this->highlightingDisabled = true;
         $language = 'txt';
       }
     }
 
     return $this->highlightEngine->getHighlightFuture(
       $language,
       $corpus);
   }
 
   protected function processHighlightedSource($data, $result) {
 
     $result_lines = phutil_split_lines($result);
     foreach ($data as $key => $info) {
       if (!$info) {
         unset($result_lines[$key]);
       }
     }
     return $result_lines;
   }
 
   private function tryCacheStuff() {
     $whitespace_mode = $this->whitespaceMode;
     switch ($whitespace_mode) {
       case self::WHITESPACE_SHOW_ALL:
       case self::WHITESPACE_IGNORE_TRAILING:
       case self::WHITESPACE_IGNORE_ALL:
         break;
       default:
         $whitespace_mode = self::WHITESPACE_IGNORE_MOST;
         break;
     }
 
     $skip_cache = ($whitespace_mode != self::WHITESPACE_IGNORE_MOST);
     if ($this->disableCache) {
       $skip_cache = true;
     }
 
     if ($this->characterEncoding) {
       $skip_cache = true;
     }
 
     if ($this->highlightAs) {
       $skip_cache = true;
     }
 
     $this->whitespaceMode = $whitespace_mode;
 
     $changeset = $this->changeset;
 
     if ($changeset->getFileType() != DifferentialChangeType::FILE_TEXT &&
         $changeset->getFileType() != DifferentialChangeType::FILE_SYMLINK) {
 
       $this->markGenerated();
 
     } else {
       if ($skip_cache || !$this->loadCache()) {
         $this->process();
         if (!$skip_cache) {
           $this->saveCache();
         }
       }
     }
   }
 
   private function process() {
     $whitespace_mode = $this->whitespaceMode;
     $changeset = $this->changeset;
 
     $ignore_all = (($whitespace_mode == self::WHITESPACE_IGNORE_MOST) ||
                   ($whitespace_mode == self::WHITESPACE_IGNORE_ALL));
 
     $force_ignore = ($whitespace_mode == self::WHITESPACE_IGNORE_ALL);
 
     if (!$force_ignore) {
       if ($ignore_all && $changeset->getWhitespaceMatters()) {
         $ignore_all = false;
       }
     }
 
     // The "ignore all whitespace" algorithm depends on rediffing the
     // files, and we currently need complete representations of both
     // files to do anything reasonable. If we only have parts of the files,
     // don't use the "ignore all" algorithm.
     if ($ignore_all) {
       $hunks = $changeset->getHunks();
       if (count($hunks) !== 1) {
         $ignore_all = false;
       } else {
         $first_hunk = reset($hunks);
         if ($first_hunk->getOldOffset() != 1 ||
             $first_hunk->getNewOffset() != 1) {
             $ignore_all = false;
         }
       }
     }
 
     if ($ignore_all) {
       $old_file = $changeset->makeOldFile();
       $new_file = $changeset->makeNewFile();
       if ($old_file == $new_file) {
         // If the old and new files are exactly identical, the synthetic
         // diff below will give us nonsense and whitespace modes are
         // irrelevant anyway. This occurs when you, e.g., copy a file onto
         // itself in Subversion (see T271).
         $ignore_all = false;
       }
     }
 
     $hunk_parser = new DifferentialHunkParser();
     $hunk_parser->setWhitespaceMode($whitespace_mode);
     $hunk_parser->parseHunksForLineData($changeset->getHunks());
 
     // Depending on the whitespace mode, we may need to compute a different
     // set of changes than the set of changes in the hunk data (specificaly,
     // we might want to consider changed lines which have only whitespace
     // changes as unchanged).
     if ($ignore_all) {
       $engine = new PhabricatorDifferenceEngine();
       $engine->setIgnoreWhitespace(true);
       $no_whitespace_changeset = $engine->generateChangesetFromFileContent(
         $old_file,
         $new_file);
 
       $type_parser = new DifferentialHunkParser();
       $type_parser->parseHunksForLineData($no_whitespace_changeset->getHunks());
 
       $hunk_parser->setOldLineTypeMap($type_parser->getOldLineTypeMap());
       $hunk_parser->setNewLineTypeMap($type_parser->getNewLineTypeMap());
     }
 
     $hunk_parser->reparseHunksForSpecialAttributes();
 
     $unchanged = false;
     if (!$hunk_parser->getHasAnyChanges()) {
       $filetype = $this->changeset->getFileType();
       if ($filetype == DifferentialChangeType::FILE_TEXT ||
           $filetype == DifferentialChangeType::FILE_SYMLINK) {
         $unchanged = true;
       }
     }
 
     $moveaway = false;
     $changetype = $this->changeset->getChangeType();
     if ($changetype == DifferentialChangeType::TYPE_MOVE_AWAY) {
       $moveaway = true;
     }
 
     $this->setSpecialAttributes(array(
       self::ATTR_UNCHANGED  => $unchanged,
       self::ATTR_DELETED    => $hunk_parser->getIsDeleted(),
       self::ATTR_WHITELINES => !$hunk_parser->getHasTextChanges(),
       self::ATTR_MOVEAWAY   => $moveaway,
     ));
 
+    $lines_context = $this->getLinesOfContext();
+
     $hunk_parser->generateIntraLineDiffs();
-    $hunk_parser->generateVisibileLinesMask();
+    $hunk_parser->generateVisibileLinesMask($lines_context);
 
     $this->setOldLines($hunk_parser->getOldLines());
     $this->setNewLines($hunk_parser->getNewLines());
     $this->setIntraLineDiffs($hunk_parser->getIntraLineDiffs());
     $this->setVisibileLinesMask($hunk_parser->getVisibleLinesMask());
     $this->hunkStartLines = $hunk_parser->getHunkStartLines(
       $changeset->getHunks());
 
     $new_corpus = $hunk_parser->getNewCorpus();
     $new_corpus_block = implode('', $new_corpus);
     $this->markGenerated($new_corpus_block);
 
     if ($this->isTopLevel &&
         !$this->comments &&
           ($this->isGenerated() ||
            $this->isUnchanged() ||
            $this->isDeleted())) {
       return;
     }
 
     $old_corpus = $hunk_parser->getOldCorpus();
     $old_corpus_block = implode('', $old_corpus);
     $old_future = $this->getHighlightFuture($old_corpus_block);
     $new_future = $this->getHighlightFuture($new_corpus_block);
     $futures = array(
       'old' => $old_future,
       'new' => $new_future,
     );
     $corpus_blocks = array(
       'old' => $old_corpus_block,
       'new' => $new_corpus_block,
     );
 
     $this->highlightErrors = false;
     foreach (new FutureIterator($futures) as $key => $future) {
       try {
         try {
           $highlighted = $future->resolve();
         } catch (PhutilSyntaxHighlighterException $ex) {
           $this->highlightErrors = true;
           $highlighted = id(new PhutilDefaultSyntaxHighlighter())
             ->getHighlightFuture($corpus_blocks[$key])
             ->resolve();
         }
         switch ($key) {
         case 'old':
           $this->oldRender = $this->processHighlightedSource(
             $this->old,
             $highlighted);
           break;
         case 'new':
           $this->newRender = $this->processHighlightedSource(
             $this->new,
             $highlighted);
           break;
         }
       } catch (Exception $ex) {
         phlog($ex);
         throw $ex;
       }
     }
 
     $this->applyIntraline(
       $this->oldRender,
       ipull($this->intra, 0),
       $old_corpus);
     $this->applyIntraline(
       $this->newRender,
       ipull($this->intra, 1),
       $new_corpus);
   }
 
   private function shouldRenderPropertyChangeHeader($changeset) {
     if (!$this->isTopLevel) {
       // We render properties only at top level; otherwise we get multiple
       // copies of them when a user clicks "Show More".
       return false;
     }
 
     return true;
   }
 
   public function render(
     $range_start  = null,
     $range_len    = null,
     $mask_force   = array()) {
 
     // "Top level" renders are initial requests for the whole file, versus
     // requests for a specific range generated by clicking "show more". We
     // generate property changes and "shield" UI elements only for toplevel
     // requests.
     $this->isTopLevel = (($range_start === null) && ($range_len === null));
     $this->highlightEngine = PhabricatorSyntaxHighlighter::newEngine();
 
     $encoding = null;
     if ($this->characterEncoding) {
       // We are forcing this changeset to be interpreted with a specific
       // character encoding, so force all the hunks into that encoding and
       // propagate it to the renderer.
       $encoding = $this->characterEncoding;
       foreach ($this->changeset->getHunks() as $hunk) {
         $hunk->forceEncoding($this->characterEncoding);
       }
     } else {
       // We're just using the default, so tell the renderer what that is
       // (by reading the encoding from the first hunk).
       foreach ($this->changeset->getHunks() as $hunk) {
         $encoding = $hunk->getDataEncoding();
         break;
       }
     }
 
     $this->tryCacheStuff();
+
+    // If we're rendering in an offset mode, treat the range numbers as line
+    // numbers instead of rendering offsets.
+    $offset_mode = $this->getOffsetMode();
+    if ($offset_mode) {
+      if ($offset_mode == 'new') {
+        $offset_map = $this->new;
+      } else {
+        $offset_map = $this->old;
+      }
+
+      $range_end = $this->getOffset($offset_map, $range_start + $range_len);
+      $range_start = $this->getOffset($offset_map, $range_start);
+      $range_len = ($range_end - $range_start);
+    }
+
     $render_pch = $this->shouldRenderPropertyChangeHeader($this->changeset);
 
     $rows = max(
       count($this->old),
       count($this->new));
 
     $renderer = $this->getRenderer()
       ->setUser($this->getUser())
       ->setChangeset($this->changeset)
       ->setRenderPropertyChangeHeader($render_pch)
       ->setIsTopLevel($this->isTopLevel)
       ->setOldRender($this->oldRender)
       ->setNewRender($this->newRender)
       ->setHunkStartLines($this->hunkStartLines)
       ->setOldChangesetID($this->leftSideChangesetID)
       ->setNewChangesetID($this->rightSideChangesetID)
       ->setOldAttachesToNewFile($this->leftSideAttachesToNewFile)
       ->setNewAttachesToNewFile($this->rightSideAttachesToNewFile)
       ->setCodeCoverage($this->getCoverage())
       ->setRenderingReference($this->getRenderingReference())
       ->setMarkupEngine($this->markupEngine)
       ->setHandles($this->handles)
       ->setOldLines($this->old)
       ->setNewLines($this->new)
       ->setOriginalCharacterEncoding($encoding)
       ->setShowEditAndReplyLinks($this->getShowEditAndReplyLinks())
       ->setCanMarkDone($this->getCanMarkDone())
       ->setObjectOwnerPHID($this->getObjectOwnerPHID())
       ->setHighlightingDisabled($this->highlightingDisabled);
 
     $shield = null;
     if ($this->isTopLevel && !$this->comments) {
       if ($this->isGenerated()) {
         $shield = $renderer->renderShield(
           pht(
             'This file contains generated code, which does not normally '.
             'need to be reviewed.'));
       } else if ($this->isMoveAway()) {
         // We put an empty shield on these files. Normally, they do not have
         // any diff content anyway. However, if they come through `arc`, they
         // may have content. We don't want to show it (it's not useful) and
         // we bailed out of fully processing it earlier anyway.
 
         // We could show a message like "this file was moved", but we show
         // that as a change header anyway, so it would be redundant. Instead,
         // just render an empty shield to skip rendering the diff body.
         $shield = '';
       } else if ($this->isUnchanged()) {
         $type = 'text';
         if (!$rows) {
           // NOTE: Normally, diffs which don't change files do not include
           // file content (for example, if you "chmod +x" a file and then
           // run "git show", the file content is not available). Similarly,
           // if you move a file from A to B without changing it, diffs normally
           // do not show the file content. In some cases `arc` is able to
           // synthetically generate content for these diffs, but for raw diffs
           // we'll never have it so we need to be prepared to not render a link.
           $type = 'none';
         }
 
         $type_add = DifferentialChangeType::TYPE_ADD;
         if ($this->changeset->getChangeType() == $type_add) {
           // Although the generic message is sort of accurate in a technical
           // sense, this more-tailored message is less confusing.
           $shield = $renderer->renderShield(
             pht('This is an empty file.'),
             $type);
         } else {
           $shield = $renderer->renderShield(
             pht('The contents of this file were not changed.'),
             $type);
         }
       } else if ($this->isWhitespaceOnly()) {
         $shield = $renderer->renderShield(
           pht('This file was changed only by adding or removing whitespace.'),
           'whitespace');
       } else if ($this->isDeleted()) {
         $shield = $renderer->renderShield(
           pht('This file was completely deleted.'));
       } else if ($this->changeset->getAffectedLineCount() > 2500) {
         $shield = $renderer->renderShield(
           pht(
             'This file has a very large number of changes (%s lines).',
             new PhutilNumber($this->changeset->getAffectedLineCount())));
       }
     }
 
     if ($shield !== null) {
       return $renderer->renderChangesetTable($shield);
     }
 
     // This request should render the "undershield" headers if it's a top-level
     // request which made it this far (indicating the changeset has no shield)
     // or it's a request with no mask information (indicating it's the request
     // that removes the rendering shield). Possibly, this second class of
     // request might need to be made more explicit.
     $is_undershield = (empty($mask_force) || $this->isTopLevel);
     $renderer->setIsUndershield($is_undershield);
 
     $old_comments = array();
     $new_comments = array();
     $old_mask = array();
     $new_mask = array();
     $feedback_mask = array();
+    $lines_context = $this->getLinesOfContext();
 
     if ($this->comments) {
       // If there are any comments which appear in sections of the file which
       // we don't have, we're going to move them backwards to the closest
       // earlier line. Two cases where this may happen are:
       //
       //   - Porting ghost comments forward into a file which was mostly
       //     deleted.
       //   - Porting ghost comments forward from a full-context diff to a
       //     partial-context diff.
 
       list($old_backmap, $new_backmap) = $this->buildLineBackmaps();
 
       foreach ($this->comments as $comment) {
         $new_side = $this->isCommentOnRightSideWhenDisplayed($comment);
 
         $line = $comment->getLineNumber();
         if ($new_side) {
           $back_line = $new_backmap[$line];
         } else {
           $back_line = $old_backmap[$line];
         }
 
         if ($back_line != $line) {
           // TODO: This should probably be cleaner, but just be simple and
           // obvious for now.
           $ghost = $comment->getIsGhost();
           if ($ghost) {
             $moved = pht(
               'This comment originally appeared on line %s, but that line '.
               'does not exist in this version of the diff. It has been '.
               'moved backward to the nearest line.',
               new PhutilNumber($line));
             $ghost['reason'] = $ghost['reason']."\n\n".$moved;
             $comment->setIsGhost($ghost);
           }
 
           $comment->setLineNumber($back_line);
           $comment->setLineLength(0);
 
         }
 
-        $start = max($comment->getLineNumber() - self::LINES_CONTEXT, 0);
+        $start = max($comment->getLineNumber() - $lines_context, 0);
         $end = $comment->getLineNumber() +
           $comment->getLineLength() +
-          self::LINES_CONTEXT;
+          $lines_context;
         for ($ii = $start; $ii <= $end; $ii++) {
           if ($new_side) {
             $new_mask[$ii] = true;
           } else {
             $old_mask[$ii] = true;
           }
         }
       }
 
       foreach ($this->old as $ii => $old) {
         if (isset($old['line']) && isset($old_mask[$old['line']])) {
           $feedback_mask[$ii] = true;
         }
       }
 
       foreach ($this->new as $ii => $new) {
         if (isset($new['line']) && isset($new_mask[$new['line']])) {
           $feedback_mask[$ii] = true;
         }
       }
 
       $this->comments = $this->reorderAndThreadComments($this->comments);
 
       foreach ($this->comments as $comment) {
         $final = $comment->getLineNumber() +
           $comment->getLineLength();
         $final = max(1, $final);
         if ($this->isCommentOnRightSideWhenDisplayed($comment)) {
           $new_comments[$final][] = $comment;
         } else {
           $old_comments[$final][] = $comment;
         }
       }
     }
     $renderer
       ->setOldComments($old_comments)
       ->setNewComments($new_comments);
 
     switch ($this->changeset->getFileType()) {
       case DifferentialChangeType::FILE_IMAGE:
         $old = null;
         $new = null;
         // TODO: Improve the architectural issue as discussed in D955
         // https://secure.phabricator.com/D955
         $reference = $this->getRenderingReference();
         $parts = explode('/', $reference);
         if (count($parts) == 2) {
           list($id, $vs) = $parts;
         } else {
           $id = $parts[0];
           $vs = 0;
         }
         $id = (int)$id;
         $vs = (int)$vs;
 
         if (!$vs) {
           $metadata = $this->changeset->getMetadata();
           $data = idx($metadata, 'attachment-data');
 
           $old_phid = idx($metadata, 'old:binary-phid');
           $new_phid = idx($metadata, 'new:binary-phid');
         } else {
           $vs_changeset = id(new DifferentialChangeset())->load($vs);
           $old_phid = null;
           $new_phid = null;
 
           // TODO: This is spooky, see D6851
           if ($vs_changeset) {
             $vs_metadata = $vs_changeset->getMetadata();
             $old_phid = idx($vs_metadata, 'new:binary-phid');
           }
 
           $changeset = id(new DifferentialChangeset())->load($id);
           if ($changeset) {
             $metadata = $changeset->getMetadata();
             $new_phid = idx($metadata, 'new:binary-phid');
           }
         }
 
         if ($old_phid || $new_phid) {
           // grab the files, (micro) optimization for 1 query not 2
           $file_phids = array();
           if ($old_phid) {
             $file_phids[] = $old_phid;
           }
           if ($new_phid) {
             $file_phids[] = $new_phid;
           }
 
           $files = id(new PhabricatorFileQuery())
             ->setViewer($this->getUser())
             ->withPHIDs($file_phids)
             ->execute();
           foreach ($files as $file) {
             if (empty($file)) {
               continue;
             }
             if ($file->getPHID() == $old_phid) {
               $old = $file;
             } else if ($file->getPHID() == $new_phid) {
               $new = $file;
             }
           }
         }
 
         $renderer->attachOldFile($old);
         $renderer->attachNewFile($new);
 
         return $renderer->renderFileChange($old, $new, $id, $vs);
       case DifferentialChangeType::FILE_DIRECTORY:
       case DifferentialChangeType::FILE_BINARY:
         $output = $renderer->renderChangesetTable(null);
         return $output;
     }
 
     if ($this->originalLeft && $this->originalRight) {
       list($highlight_old, $highlight_new) = $this->diffOriginals();
       $highlight_old = array_flip($highlight_old);
       $highlight_new = array_flip($highlight_new);
       $renderer
         ->setHighlightOld($highlight_old)
         ->setHighlightNew($highlight_new);
     }
     $renderer
       ->setOriginalOld($this->originalLeft)
       ->setOriginalNew($this->originalRight);
 
     if ($range_start === null) {
       $range_start = 0;
     }
     if ($range_len === null) {
       $range_len = $rows;
     }
     $range_len = min($range_len, $rows - $range_start);
 
     list($gaps, $mask, $depths) = $this->calculateGapsMaskAndDepths(
       $mask_force,
       $feedback_mask,
       $range_start,
       $range_len);
 
     $renderer
       ->setGaps($gaps)
       ->setMask($mask)
       ->setDepths($depths);
 
     $html = $renderer->renderTextChange(
       $range_start,
       $range_len,
       $rows);
 
     return $renderer->renderChangesetTable($html);
   }
 
   /**
    * This function calculates a lot of stuff we need to know to display
    * the diff:
    *
    * Gaps - compute gaps in the visible display diff, where we will render
    * "Show more context" spacers. If a gap is smaller than the context size,
    * we just display it. Otherwise, we record it into $gaps and will render a
    * "show more context" element instead of diff text below. A given $gap
    * is a tuple of $gap_line_number_start and $gap_length.
    *
    * Mask - compute the actual lines that need to be shown (because they
    * are near changes lines, near inline comments, or the request has
    * explicitly asked for them, i.e. resulting from the user clicking
    * "show more"). The $mask returned is a sparesely populated dictionary
    * of $visible_line_number => true.
    *
    * Depths - compute how indented any given line is. The $depths returned
    * is a sparesely populated dictionary of $visible_line_number => $depth.
    *
    * This function also has the side effect of modifying member variable
    * new such that tabs are normalized to spaces for each line of the diff.
    *
    * @return array($gaps, $mask, $depths)
    */
   private function calculateGapsMaskAndDepths(
     $mask_force,
     $feedback_mask,
     $range_start,
     $range_len) {
 
+    $lines_context = $this->getLinesOfContext();
+
     // Calculate gaps and mask first
     $gaps = array();
     $gap_start = 0;
     $in_gap = false;
     $base_mask = $this->visible + $mask_force + $feedback_mask;
     $base_mask[$range_start + $range_len] = true;
     for ($ii = $range_start; $ii <= $range_start + $range_len; $ii++) {
       if (isset($base_mask[$ii])) {
         if ($in_gap) {
           $gap_length = $ii - $gap_start;
-          if ($gap_length <= self::LINES_CONTEXT) {
+          if ($gap_length <= $lines_context) {
             for ($jj = $gap_start; $jj <= $gap_start + $gap_length; $jj++) {
               $base_mask[$jj] = true;
             }
           } else {
             $gaps[] = array($gap_start, $gap_length);
           }
           $in_gap = false;
         }
       } else {
         if (!$in_gap) {
           $gap_start = $ii;
           $in_gap = true;
         }
       }
     }
     $gaps = array_reverse($gaps);
     $mask = $base_mask;
 
     // Time to calculate depth.
     // We need to go backwards to properly indent whitespace in this code:
     //
     //   0: class C {
     //   1:
     //   1:   function f() {
     //   2:
     //   2:     return;
     //   1:
     //   1:   }
     //   0:
     //   0: }
     //
     $depths = array();
     $last_depth = 0;
     $range_end = $range_start + $range_len;
     if (!isset($this->new[$range_end])) {
       $range_end--;
     }
     for ($ii = $range_end; $ii >= $range_start; $ii--) {
       // We need to expand tabs to process mixed indenting and to round
       // correctly later.
       $line = str_replace("\t", '  ', $this->new[$ii]['text']);
       $trimmed = ltrim($line);
       if ($trimmed != '') {
         // We round down to flatten "/**" and " *".
         $last_depth = floor((strlen($line) - strlen($trimmed)) / 2);
       }
       $depths[$ii] = $last_depth;
     }
 
     return array($gaps, $mask, $depths);
   }
 
   /**
    * Determine if an inline comment will appear on the rendered diff,
    * taking into consideration which halves of which changesets will actually
    * be shown.
    *
    * @param PhabricatorInlineCommentInterface Comment to test for visibility.
    * @return bool True if the comment is visible on the rendered diff.
    */
   private function isCommentVisibleOnRenderedDiff(
     PhabricatorInlineCommentInterface $comment) {
 
     $changeset_id = $comment->getChangesetID();
     $is_new = $comment->getIsNewFile();
 
     if ($changeset_id == $this->rightSideChangesetID &&
         $is_new == $this->rightSideAttachesToNewFile) {
         return true;
     }
 
     if ($changeset_id == $this->leftSideChangesetID &&
         $is_new == $this->leftSideAttachesToNewFile) {
         return true;
     }
 
     return false;
   }
 
 
   /**
    * Determine if a comment will appear on the right side of the display diff.
    * Note that the comment must appear somewhere on the rendered changeset, as
    * per isCommentVisibleOnRenderedDiff().
    *
    * @param PhabricatorInlineCommentInterface Comment to test for display
    *              location.
    * @return bool True for right, false for left.
    */
   private function isCommentOnRightSideWhenDisplayed(
     PhabricatorInlineCommentInterface $comment) {
 
     if (!$this->isCommentVisibleOnRenderedDiff($comment)) {
       throw new Exception(pht('Comment is not visible on changeset!'));
     }
 
     $changeset_id = $comment->getChangesetID();
     $is_new = $comment->getIsNewFile();
 
     if ($changeset_id == $this->rightSideChangesetID &&
         $is_new == $this->rightSideAttachesToNewFile) {
       return true;
     }
 
     return false;
   }
 
   /**
    * Parse the 'range' specification that this class and the client-side JS
    * emit to indicate that a user clicked "Show more..." on a diff. Generally,
    * use is something like this:
    *
    *   $spec = $request->getStr('range');
    *   $parsed = DifferentialChangesetParser::parseRangeSpecification($spec);
    *   list($start, $end, $mask) = $parsed;
    *   $parser->render($start, $end, $mask);
    *
    * @param string Range specification, indicating the range of the diff that
    *               should be rendered.
    * @return tuple List of <start, end, mask> suitable for passing to
    *               @{method:render}.
    */
   public static function parseRangeSpecification($spec) {
     $range_s = null;
     $range_e = null;
     $mask = array();
 
     if ($spec) {
       $match = null;
       if (preg_match('@^(\d+)-(\d+)(?:/(\d+)-(\d+))?$@', $spec, $match)) {
         $range_s = (int)$match[1];
         $range_e = (int)$match[2];
         if (count($match) > 3) {
           $start = (int)$match[3];
           $len = (int)$match[4];
           for ($ii = $start; $ii < $start + $len; $ii++) {
             $mask[$ii] = true;
           }
         }
       }
     }
 
     return array($range_s, $range_e, $mask);
   }
 
   /**
    * Render "modified coverage" information; test coverage on modified lines.
    * This synthesizes diff information with unit test information into a useful
    * indicator of how well tested a change is.
    */
   public function renderModifiedCoverage() {
     $na = phutil_tag('em', array(), '-');
 
     $coverage = $this->getCoverage();
     if (!$coverage) {
       return $na;
     }
 
     $covered = 0;
     $not_covered = 0;
 
     foreach ($this->new as $k => $new) {
       if (!$new['line']) {
         continue;
       }
 
       if (!$new['type']) {
         continue;
       }
 
       if (empty($coverage[$new['line'] - 1])) {
         continue;
       }
 
       switch ($coverage[$new['line'] - 1]) {
         case 'C':
           $covered++;
           break;
         case 'U':
           $not_covered++;
           break;
       }
     }
 
     if (!$covered && !$not_covered) {
       return $na;
     }
 
     return sprintf('%d%%', 100 * ($covered / ($covered + $not_covered)));
   }
 
   public function detectCopiedCode(
     array $changesets,
     $min_width = 30,
     $min_lines = 3) {
 
     assert_instances_of($changesets, 'DifferentialChangeset');
 
     $map = array();
     $files = array();
     $types = array();
     foreach ($changesets as $changeset) {
       $file = $changeset->getFilename();
       foreach ($changeset->getHunks() as $hunk) {
         $lines = $hunk->getStructuredOldFile();
         foreach ($lines as $line => $info) {
           $type = $info['type'];
           if ($type == '\\') {
             continue;
           }
           $types[$file][$line] = $type;
 
           $text = $info['text'];
           $text = trim($text);
           $files[$file][$line] = $text;
 
           if (strlen($text) >= $min_width) {
             $map[$text][] = array($file, $line);
           }
         }
       }
     }
 
     foreach ($changesets as $changeset) {
       $copies = array();
       foreach ($changeset->getHunks() as $hunk) {
         $added = $hunk->getStructuredNewFile();
         $atype = array();
 
         foreach ($added as $line => $info) {
           $atype[$line] = $info['type'];
           $added[$line] = trim($info['text']);
         }
 
         $skip_lines = 0;
         foreach ($added as $line => $code) {
           if ($skip_lines) {
             // We're skipping lines that we already processed because we
             // extended a block above them downward to include them.
             $skip_lines--;
             continue;
           }
 
           if ($atype[$line] !== '+') {
             // This line hasn't been changed in the new file, so don't try
             // to figure out where it came from.
             continue;
           }
 
           if (empty($map[$code])) {
             // This line was too short to trigger copy/move detection.
             continue;
           }
 
           if (count($map[$code]) > 16) {
             // If there are a large number of identical lines in this diff,
             // don't try to figure out where this block came from: the analysis
             // is O(N^2), since we need to compare every line against every
             // other line. Even if we arrive at a result, it is unlikely to be
             // meaningful. See T5041.
             continue;
           }
 
           $best_length = 0;
 
           // Explore all candidates.
           foreach ($map[$code] as $val) {
             list($file, $orig_line) = $val;
             $length = 1;
 
             // Search backward and forward to find all of the adjacent lines
             // which match.
             foreach (array(-1, 1) as $direction) {
               $offset = $direction;
               while (true) {
                 if (isset($copies[$line + $offset])) {
                   // If we run into a block above us which we've already
                   // attributed to a move or copy from elsewhere, stop
                   // looking.
                   break;
                 }
 
                 if (!isset($added[$line + $offset])) {
                   // If we've run off the beginning or end of the new file,
                   // stop looking.
                   break;
                 }
 
                 if (!isset($files[$file][$orig_line + $offset])) {
                   // If we've run off the beginning or end of the original
                   // file, we also stop looking.
                   break;
                 }
 
                 $old = $files[$file][$orig_line + $offset];
                 $new = $added[$line + $offset];
                 if ($old !== $new) {
                   // If the old line doesn't match the new line, stop
                   // looking.
                   break;
                 }
 
                 $length++;
                 $offset += $direction;
               }
             }
 
             if ($length < $best_length) {
               // If we already know of a better source (more matching lines)
               // for this move/copy, stick with that one. We prefer long
               // copies/moves which match a lot of context over short ones.
               continue;
             }
 
             if ($length == $best_length) {
               if (idx($types[$file], $orig_line) != '-') {
                 // If we already know of an equally good source (same number
                 // of matching lines) and this isn't a move, stick with the
                 // other one. We prefer moves over copies.
                 continue;
               }
             }
 
             $best_length = $length;
             // ($offset - 1) contains number of forward matching lines.
             $best_offset = $offset - 1;
             $best_file = $file;
             $best_line = $orig_line;
           }
 
           $file = ($best_file == $changeset->getFilename() ? '' : $best_file);
           for ($i = $best_length; $i--; ) {
             $type = idx($types[$best_file], $best_line + $best_offset - $i);
             $copies[$line + $best_offset - $i] = ($best_length < $min_lines
               ? array() // Ignore short blocks.
               : array($file, $best_line + $best_offset - $i, $type));
           }
 
           $skip_lines = $best_offset;
         }
       }
 
       $copies = array_filter($copies);
       if ($copies) {
         $metadata = $changeset->getMetadata();
         $metadata['copy:lines'] = $copies;
         $changeset->setMetadata($metadata);
       }
     }
     return $changesets;
   }
 
   /**
    * Build maps from lines comments appear on to actual lines.
    */
   private function buildLineBackmaps() {
     $old_back = array();
     $new_back = array();
     foreach ($this->old as $ii => $old) {
       $old_back[$old['line']] = $old['line'];
     }
     foreach ($this->new as $ii => $new) {
       $new_back[$new['line']] = $new['line'];
     }
 
     $max_old_line = 0;
     $max_new_line = 0;
     foreach ($this->comments as $comment) {
       if ($this->isCommentOnRightSideWhenDisplayed($comment)) {
         $max_new_line = max($max_new_line, $comment->getLineNumber());
       } else {
         $max_old_line = max($max_old_line, $comment->getLineNumber());
       }
     }
 
     $cursor = 1;
     for ($ii = 1; $ii <= $max_old_line; $ii++) {
       if (empty($old_back[$ii])) {
         $old_back[$ii] = $cursor;
       } else {
         $cursor = $old_back[$ii];
       }
     }
 
     $cursor = 1;
     for ($ii = 1; $ii <= $max_new_line; $ii++) {
       if (empty($new_back[$ii])) {
         $new_back[$ii] = $cursor;
       } else {
         $cursor = $new_back[$ii];
       }
     }
 
     return array($old_back, $new_back);
   }
 
   private function reorderAndThreadComments(array $comments) {
     $comments = msort($comments, 'getID');
 
     // Build an empty map of all the comments we actually have. If a comment
     // is a reply but the parent has gone missing, we don't want it to vanish
     // completely.
     $comment_phids = mpull($comments, 'getPHID');
     $replies = array_fill_keys($comment_phids, array());
 
     // Now, remove all comments which are replies, leaving only the top-level
     // comments.
     foreach ($comments as $key => $comment) {
       $reply_phid = $comment->getReplyToCommentPHID();
       if (isset($replies[$reply_phid])) {
         $replies[$reply_phid][] = $comment;
         unset($comments[$key]);
       }
     }
 
     // For each top level comment, add the comment, then add any replies
     // to it. Do this recursively so threads are shown in threaded order.
     $results = array();
     foreach ($comments as $comment) {
       $results[] = $comment;
       $phid = $comment->getPHID();
       $descendants = $this->getInlineReplies($replies, $phid, 1);
       foreach ($descendants as $descendant) {
         $results[] = $descendant;
       }
     }
 
     // If we have anything left, they were cyclic references. Just dump
     // them in a the end. This should be impossible, but users are very
     // creative.
     foreach ($replies as $phid => $comments) {
       foreach ($comments as $comment) {
         $results[] = $comment;
       }
     }
 
     return $results;
   }
 
   private function getInlineReplies(array &$replies, $phid, $depth) {
     $comments = idx($replies, $phid, array());
     unset($replies[$phid]);
 
     $results = array();
     foreach ($comments as $comment) {
       $results[] = $comment;
       $descendants = $this->getInlineReplies(
         $replies,
         $comment->getPHID(),
         $depth + 1);
       foreach ($descendants as $descendant) {
         $results[] = $descendant;
       }
     }
 
     return $results;
   }
 
+  private function getOffset(array $map, $line) {
+    if (!$map) {
+      return null;
+    }
+
+    $line = (int)$line;
+    foreach ($map as $key => $spec) {
+      if ($spec && isset($spec['line'])) {
+        if ((int)$spec['line'] >= $line) {
+          return $key;
+        }
+      }
+    }
+
+    return $key;
+  }
 
 }
diff --git a/src/applications/differential/parser/DifferentialHunkParser.php b/src/applications/differential/parser/DifferentialHunkParser.php
index 22b82e002..5bd98e901 100644
--- a/src/applications/differential/parser/DifferentialHunkParser.php
+++ b/src/applications/differential/parser/DifferentialHunkParser.php
@@ -1,675 +1,674 @@
 <?php
 
 final class DifferentialHunkParser extends Phobject {
 
   private $oldLines;
   private $newLines;
   private $intraLineDiffs;
   private $visibleLinesMask;
   private $whitespaceMode;
 
   /**
    * Get a map of lines on which hunks start, other than line 1. This
    * datastructure is used to determine when to render "Context not available."
    * in diffs with multiple hunks.
    *
    * @return dict<int, bool>  Map of lines where hunks start, other than line 1.
    */
   public function getHunkStartLines(array $hunks) {
     assert_instances_of($hunks, 'DifferentialHunk');
 
     $map = array();
     foreach ($hunks as $hunk) {
       $line = $hunk->getOldOffset();
       if ($line > 1) {
         $map[$line] = true;
       }
     }
 
     return $map;
   }
 
   private function setVisibleLinesMask($mask) {
     $this->visibleLinesMask = $mask;
     return $this;
   }
   public function getVisibleLinesMask() {
     if ($this->visibleLinesMask === null) {
       throw new PhutilInvalidStateException('generateVisibileLinesMask');
     }
     return $this->visibleLinesMask;
   }
 
   private function setIntraLineDiffs($intra_line_diffs) {
     $this->intraLineDiffs = $intra_line_diffs;
     return $this;
   }
   public function getIntraLineDiffs() {
     if ($this->intraLineDiffs === null) {
       throw new PhutilInvalidStateException('generateIntraLineDiffs');
     }
     return $this->intraLineDiffs;
   }
 
   private function setNewLines($new_lines) {
     $this->newLines = $new_lines;
     return $this;
   }
   public function getNewLines() {
     if ($this->newLines === null) {
       throw new PhutilInvalidStateException('parseHunksForLineData');
     }
     return $this->newLines;
   }
 
   private function setOldLines($old_lines) {
     $this->oldLines = $old_lines;
     return $this;
   }
   public function getOldLines() {
     if ($this->oldLines === null) {
       throw new PhutilInvalidStateException('parseHunksForLineData');
     }
     return $this->oldLines;
   }
 
   public function getOldLineTypeMap() {
     $map = array();
     $old = $this->getOldLines();
     foreach ($old as $o) {
       if (!$o) {
         continue;
       }
       $map[$o['line']] = $o['type'];
     }
     return $map;
   }
 
   public function setOldLineTypeMap(array $map) {
     $lines = $this->getOldLines();
     foreach ($lines as $key => $data) {
       $lines[$key]['type'] = idx($map, $data['line']);
     }
     $this->oldLines = $lines;
     return $this;
   }
 
   public function getNewLineTypeMap() {
     $map = array();
     $new = $this->getNewLines();
     foreach ($new as $n) {
       if (!$n) {
         continue;
       }
       $map[$n['line']] = $n['type'];
     }
     return $map;
   }
 
   public function setNewLineTypeMap(array $map) {
     $lines = $this->getNewLines();
     foreach ($lines as $key => $data) {
       $lines[$key]['type'] = idx($map, $data['line']);
     }
     $this->newLines = $lines;
     return $this;
   }
 
 
   public function setWhitespaceMode($white_space_mode) {
     $this->whitespaceMode = $white_space_mode;
     return $this;
   }
 
   private function getWhitespaceMode() {
     if ($this->whitespaceMode === null) {
       throw new Exception(
         pht(
           'You must %s before accessing this data.',
           'setWhitespaceMode'));
     }
     return $this->whitespaceMode;
   }
 
   public function getIsDeleted() {
     foreach ($this->getNewLines() as $line) {
       if ($line) {
         // At least one new line, so the entire file wasn't deleted.
         return false;
       }
     }
 
     foreach ($this->getOldLines() as $line) {
       if ($line) {
         // No new lines, at least one old line; the entire file was deleted.
         return true;
       }
     }
 
     // This is an empty file.
     return false;
   }
 
   /**
    * Returns true if the hunks change any text, not just whitespace.
    */
   public function getHasTextChanges() {
     return $this->getHasChanges('text');
   }
 
   /**
    * Returns true if the hunks change anything, including whitespace.
    */
   public function getHasAnyChanges() {
     return $this->getHasChanges('any');
   }
 
   private function getHasChanges($filter) {
     if ($filter !== 'any' && $filter !== 'text') {
       throw new Exception(pht("Unknown change filter '%s'.", $filter));
     }
 
     $old = $this->getOldLines();
     $new = $this->getNewLines();
 
     $is_any = ($filter === 'any');
 
     foreach ($old as $key => $o) {
       $n = $new[$key];
       if ($o === null || $n === null) {
         // One side is missing, and it's impossible for both sides to be null,
         // so the other side must have something, and thus the two sides are
         // different and the file has been changed under any type of filter.
         return true;
       }
 
       if ($o['type'] !== $n['type']) {
         // The types are different, so either the underlying text is actually
         // different or whatever whitespace rules we're using consider them
         // different.
         return true;
       }
 
       if ($o['text'] !== $n['text']) {
         if ($is_any) {
           // The text is different, so there's a change.
           return true;
         } else if (trim($o['text']) !== trim($n['text'])) {
           return true;
         }
       }
     }
 
     // No changes anywhere in the file.
     return false;
   }
 
 
   /**
    * This function takes advantage of the parsing work done in
    * @{method:parseHunksForLineData} and continues the struggle to hammer this
    * data into something we can display to a user.
    *
    * In particular, this function re-parses the hunks to make them equivalent
    * in length for easy rendering, adding `null` as necessary to pad the
    * length.
    *
    * Anyhoo, this function is not particularly well-named but I try.
    *
    * NOTE: this function must be called after
    * @{method:parseHunksForLineData}.
    */
   public function reparseHunksForSpecialAttributes() {
     $rebuild_old = array();
     $rebuild_new = array();
 
     $old_lines = array_reverse($this->getOldLines());
     $new_lines = array_reverse($this->getNewLines());
 
     while (count($old_lines) || count($new_lines)) {
       $old_line_data = array_pop($old_lines);
       $new_line_data = array_pop($new_lines);
 
       if ($old_line_data) {
         $o_type = $old_line_data['type'];
       } else {
         $o_type = null;
       }
 
       if ($new_line_data) {
         $n_type = $new_line_data['type'];
       } else {
         $n_type = null;
       }
 
       // This line does not exist in the new file.
       if (($o_type != null) && ($n_type == null)) {
         $rebuild_old[] = $old_line_data;
         $rebuild_new[] = null;
         if ($new_line_data) {
           array_push($new_lines, $new_line_data);
         }
         continue;
       }
 
       // This line does not exist in the old file.
       if (($n_type != null) && ($o_type == null)) {
         $rebuild_old[] = null;
         $rebuild_new[] = $new_line_data;
         if ($old_line_data) {
           array_push($old_lines, $old_line_data);
         }
         continue;
       }
 
       $rebuild_old[] = $old_line_data;
       $rebuild_new[] = $new_line_data;
     }
 
     $this->setOldLines($rebuild_old);
     $this->setNewLines($rebuild_new);
 
     $this->updateChangeTypesForWhitespaceMode();
 
     return $this;
   }
 
   private function updateChangeTypesForWhitespaceMode() {
     $mode = $this->getWhitespaceMode();
 
     $mode_show_all = DifferentialChangesetParser::WHITESPACE_SHOW_ALL;
     if ($mode === $mode_show_all) {
       // If we're showing all whitespace, we don't need to perform any updates.
       return;
     }
 
     $mode_trailing = DifferentialChangesetParser::WHITESPACE_IGNORE_TRAILING;
     $is_trailing = ($mode === $mode_trailing);
 
     $new = $this->getNewLines();
     $old = $this->getOldLines();
     foreach ($old as $key => $o) {
       $n = $new[$key];
 
       if (!$o || !$n) {
         continue;
       }
 
       if ($is_trailing) {
         // In "trailing" mode, we need to identify lines which are marked
         // changed but differ only by trailing whitespace. We mark these lines
         // unchanged.
         if ($o['type'] != $n['type']) {
           if (rtrim($o['text']) === rtrim($n['text'])) {
             $old[$key]['type'] = null;
             $new[$key]['type'] = null;
           }
         }
       } else {
         // In "ignore most" and "ignore all" modes, we need to identify lines
         // which are marked unchanged but have internal whitespace changes.
         // We want to ignore leading and trailing whitespace changes only, not
         // internal whitespace changes (`diff` doesn't have a mode for this, so
         // we have to fix it here). If the text is marked unchanged but the
         // old and new text differs by internal space, mark the lines changed.
         if ($o['type'] === null && $n['type'] === null) {
           if ($o['text'] !== $n['text']) {
             if (trim($o['text']) !== trim($n['text'])) {
               $old[$key]['type'] = '-';
               $new[$key]['type'] = '+';
             }
           }
         }
       }
     }
 
     $this->setOldLines($old);
     $this->setNewLines($new);
 
     return $this;
   }
 
   public function generateIntraLineDiffs() {
     $old = $this->getOldLines();
     $new = $this->getNewLines();
 
     $diffs = array();
     foreach ($old as $key => $o) {
       $n = $new[$key];
 
       if (!$o || !$n) {
         continue;
       }
 
       if ($o['type'] != $n['type']) {
         $diffs[$key] = ArcanistDiffUtils::generateIntralineDiff(
           $o['text'],
           $n['text']);
       }
     }
 
     $this->setIntraLineDiffs($diffs);
 
     return $this;
   }
 
-  public function generateVisibileLinesMask() {
-    $lines_context = DifferentialChangesetParser::LINES_CONTEXT;
+  public function generateVisibileLinesMask($lines_context) {
     $old = $this->getOldLines();
     $new = $this->getNewLines();
     $max_length = max(count($old), count($new));
     $visible = false;
     $last = 0;
     $mask = array();
     for ($cursor = -$lines_context; $cursor < $max_length; $cursor++) {
       $offset = $cursor + $lines_context;
       if ((isset($old[$offset]) && $old[$offset]['type']) ||
           (isset($new[$offset]) && $new[$offset]['type'])) {
         $visible = true;
         $last = $offset;
       } else if ($cursor > $last + $lines_context) {
         $visible = false;
       }
       if ($visible && $cursor > 0) {
         $mask[$cursor] = 1;
       }
     }
 
     $this->setVisibleLinesMask($mask);
 
     return $this;
   }
 
   public function getOldCorpus() {
     return $this->getCorpus($this->getOldLines());
   }
 
   public function getNewCorpus() {
     return $this->getCorpus($this->getNewLines());
   }
 
   private function getCorpus(array $lines) {
 
     $corpus = array();
     foreach ($lines as $l) {
       if ($l['type'] != '\\') {
         if ($l['text'] === null) {
           // There's no text on this side of the diff, but insert a placeholder
           // newline so the highlighted line numbers match up.
           $corpus[] = "\n";
         } else {
           $corpus[] = $l['text'];
         }
       }
     }
     return $corpus;
   }
 
   public function parseHunksForLineData(array $hunks) {
     assert_instances_of($hunks, 'DifferentialHunk');
 
     $old_lines = array();
     $new_lines = array();
     foreach ($hunks as $hunk) {
       $lines = $hunk->getSplitLines();
 
       $line_type_map = array();
       $line_text = array();
       foreach ($lines as $line_index => $line) {
         if (isset($line[0])) {
           $char = $line[0];
           switch ($char) {
             case ' ':
               $line_type_map[$line_index] = null;
               $line_text[$line_index] = substr($line, 1);
               break;
             case "\r":
             case "\n":
               // NOTE: Normally, the first character is a space, plus, minus or
               // backslash, but it may be a newline if it used to be a space and
               // trailing whitespace has been stripped via email transmission or
               // some similar mechanism. In these cases, we essentially pretend
               // the missing space is still there.
               $line_type_map[$line_index] = null;
               $line_text[$line_index] = $line;
               break;
             case '+':
             case '-':
             case '\\':
               $line_type_map[$line_index] = $char;
               $line_text[$line_index] = substr($line, 1);
               break;
             default:
               throw new Exception(
                 pht(
                   'Unexpected leading character "%s" at line index %s!',
                   $char,
                   $line_index));
           }
         } else {
           $line_type_map[$line_index] = null;
           $line_text[$line_index] = '';
         }
       }
 
       $old_line = $hunk->getOldOffset();
       $new_line = $hunk->getNewOffset();
 
       $num_lines = count($lines);
       for ($cursor = 0; $cursor < $num_lines; $cursor++) {
         $type = $line_type_map[$cursor];
         $data = array(
           'type'  => $type,
           'text'  => $line_text[$cursor],
           'line'  => $new_line,
         );
         if ($type == '\\') {
           $type = $line_type_map[$cursor - 1];
           $data['text'] = ltrim($data['text']);
         }
         switch ($type) {
           case '+':
             $new_lines[] = $data;
             ++$new_line;
             break;
           case '-':
             $data['line'] = $old_line;
             $old_lines[] = $data;
             ++$old_line;
             break;
           default:
             $new_lines[] = $data;
             $data['line'] = $old_line;
             $old_lines[] = $data;
             ++$new_line;
             ++$old_line;
             break;
         }
       }
     }
 
     $this->setOldLines($old_lines);
     $this->setNewLines($new_lines);
 
     return $this;
   }
 
   public function parseHunksForHighlightMasks(
     array $changeset_hunks,
     array $old_hunks,
     array $new_hunks) {
     assert_instances_of($changeset_hunks, 'DifferentialHunk');
     assert_instances_of($old_hunks,       'DifferentialHunk');
     assert_instances_of($new_hunks,       'DifferentialHunk');
 
     // Put changes side by side.
     $olds = array();
     $news = array();
     $olds_cursor = -1;
     $news_cursor = -1;
     foreach ($changeset_hunks as $hunk) {
       $n_old = $hunk->getOldOffset();
       $n_new = $hunk->getNewOffset();
       $changes = $hunk->getSplitLines();
       foreach ($changes as $line) {
         $diff_type = $line[0]; // Change type in diff of diffs.
         $orig_type = $line[1]; // Change type in the original diff.
         if ($diff_type == ' ') {
           // Use the same key for lines that are next to each other.
           if ($olds_cursor > $news_cursor) {
             $key = $olds_cursor + 1;
           } else {
             $key = $news_cursor + 1;
           }
           $olds[$key] = null;
           $news[$key] = null;
           $olds_cursor = $key;
           $news_cursor = $key;
         } else if ($diff_type == '-') {
           $olds[] = array($n_old, $orig_type);
           $olds_cursor++;
         } else if ($diff_type == '+') {
           $news[] = array($n_new, $orig_type);
           $news_cursor++;
         }
         if (($diff_type == '-' || $diff_type == ' ') && $orig_type != '-') {
           $n_old++;
         }
         if (($diff_type == '+' || $diff_type == ' ') && $orig_type != '-') {
           $n_new++;
         }
       }
     }
 
     $offsets_old = $this->computeOffsets($old_hunks);
     $offsets_new = $this->computeOffsets($new_hunks);
 
     // Highlight lines that were added on each side or removed on the other
     // side.
     $highlight_old = array();
     $highlight_new = array();
     $last = max(last_key($olds), last_key($news));
     for ($i = 0; $i <= $last; $i++) {
       if (isset($olds[$i])) {
         list($n, $type) = $olds[$i];
         if ($type == '+' ||
             ($type == ' ' && isset($news[$i]) && $news[$i][1] != ' ')) {
           $highlight_old[] = $offsets_old[$n];
         }
       }
       if (isset($news[$i])) {
         list($n, $type) = $news[$i];
         if ($type == '+' ||
             ($type == ' ' && isset($olds[$i]) && $olds[$i][1] != ' ')) {
           $highlight_new[] = $offsets_new[$n];
         }
       }
     }
 
     return array($highlight_old, $highlight_new);
   }
 
   public function makeContextDiff(
     array $hunks,
     $is_new,
     $line_number,
     $line_length,
     $add_context) {
 
     assert_instances_of($hunks, 'DifferentialHunk');
 
     $context = array();
 
     if ($is_new) {
       $prefix = '+';
     } else {
       $prefix = '-';
     }
 
     foreach ($hunks as $hunk) {
       if ($is_new) {
         $offset = $hunk->getNewOffset();
         $length = $hunk->getNewLen();
       } else {
         $offset = $hunk->getOldOffset();
         $length = $hunk->getOldLen();
       }
       $start = $line_number - $offset;
       $end = $start + $line_length;
       // We need to go in if $start == $length, because the last line
       // might be a "\No newline at end of file" marker, which we want
       // to show if the additional context is > 0.
       if ($start <= $length && $end >= 0) {
         $start = $start - $add_context;
         $end = $end + $add_context;
         $hunk_content = array();
         $hunk_pos = array('-' => 0, '+' => 0);
         $hunk_offset = array('-' => null, '+' => null);
         $hunk_last = array('-' => null, '+' => null);
         foreach (explode("\n", $hunk->getChanges()) as $line) {
           $in_common = strncmp($line, ' ', 1) === 0;
           $in_old = strncmp($line, '-', 1) === 0 || $in_common;
           $in_new = strncmp($line, '+', 1) === 0 || $in_common;
           $in_selected = strncmp($line, $prefix, 1) === 0;
           $skip = !$in_selected && !$in_common;
           if ($hunk_pos[$prefix] <= $end) {
             if ($start <= $hunk_pos[$prefix]) {
               if (!$skip || ($hunk_pos[$prefix] != $start &&
                 $hunk_pos[$prefix] != $end)) {
                   if ($in_old) {
                     if ($hunk_offset['-'] === null) {
                       $hunk_offset['-'] = $hunk_pos['-'];
                     }
                     $hunk_last['-'] = $hunk_pos['-'];
                   }
                   if ($in_new) {
                     if ($hunk_offset['+'] === null) {
                       $hunk_offset['+'] = $hunk_pos['+'];
                     }
                     $hunk_last['+'] = $hunk_pos['+'];
                   }
 
                   $hunk_content[] = $line;
                 }
             }
             if ($in_old) { ++$hunk_pos['-']; }
             if ($in_new) { ++$hunk_pos['+']; }
           }
         }
         if ($hunk_offset['-'] !== null || $hunk_offset['+'] !== null) {
           $header = '@@';
           if ($hunk_offset['-'] !== null) {
             $header .= ' -'.($hunk->getOldOffset() + $hunk_offset['-']).
               ','.($hunk_last['-'] - $hunk_offset['-'] + 1);
           }
           if ($hunk_offset['+'] !== null) {
             $header .= ' +'.($hunk->getNewOffset() + $hunk_offset['+']).
               ','.($hunk_last['+'] - $hunk_offset['+'] + 1);
           }
           $header .= ' @@';
           $context[] = $header;
           $context[] = implode("\n", $hunk_content);
         }
       }
     }
     return implode("\n", $context);
   }
 
   private function computeOffsets(array $hunks) {
     assert_instances_of($hunks, 'DifferentialHunk');
 
     $offsets = array();
     $n = 1;
     foreach ($hunks as $hunk) {
       $new_length = $hunk->getNewLen();
       $new_offset = $hunk->getNewOffset();
 
       for ($i = 0; $i < $new_length; $i++) {
         $offsets[$n] = $new_offset + $i;
         $n++;
       }
     }
 
     return $offsets;
   }
 }
diff --git a/src/applications/differential/render/DifferentialChangesetOneUpMailRenderer.php b/src/applications/differential/render/DifferentialChangesetOneUpMailRenderer.php
new file mode 100644
index 000000000..e720422ec
--- /dev/null
+++ b/src/applications/differential/render/DifferentialChangesetOneUpMailRenderer.php
@@ -0,0 +1,160 @@
+<?php
+
+final class DifferentialChangesetOneUpMailRenderer
+  extends DifferentialChangesetRenderer {
+
+  public function isOneUpRenderer() {
+    return true;
+  }
+
+  protected function getRendererTableClass() {
+    return 'diff-1up-mail';
+  }
+
+  public function getRendererKey() {
+    return '1up-mail';
+  }
+
+  protected function renderChangeTypeHeader($force) {
+    return null;
+  }
+
+  protected function renderUndershieldHeader() {
+    return null;
+  }
+
+  public function renderShield($message, $force = 'default') {
+    return null;
+  }
+
+  protected function renderPropertyChangeHeader() {
+    return null;
+  }
+
+  public function renderFileChange(
+    $old_file = null,
+    $new_file = null,
+    $id = 0,
+    $vs = 0) {
+    return null;
+  }
+
+  public function renderTextChange(
+    $range_start,
+    $range_len,
+    $rows) {
+
+    $primitives = $this->buildPrimitives($range_start, $range_len);
+    return $this->renderPrimitives($primitives, $rows);
+  }
+
+  protected function renderPrimitives(array $primitives, $rows) {
+    $out = array();
+
+    $context_style = array(
+      'background: #F7F7F7;',
+      'color: #74777D;',
+      'border-style: dashed;',
+      'border-color: #C7CCD9;',
+      'border-width: 1px 0;',
+    );
+
+    $context_style = implode(' ', $context_style);
+
+    foreach ($primitives as $k => $p) {
+      $type = $p['type'];
+      switch ($type) {
+        case 'old':
+        case 'new':
+        case 'old-file':
+        case 'new-file':
+          $is_old = ($type == 'old' || $type == 'old-file');
+
+          if ($is_old) {
+            if ($p['htype']) {
+              $style = 'background: #ffd0d0;';
+            } else {
+              $style = null;
+            }
+          } else {
+            if ($p['htype']) {
+              $style = 'background: #d0ffd0;';
+            } else {
+              $style =  null;
+            }
+          }
+
+          $out[] = array(
+            'style' => $style,
+            'render' => $p['render'],
+            'text' => (string)$p['render'],
+          );
+          break;
+        case 'context':
+          // NOTE: These are being included with no text so they get stripped
+          // in the header and footer.
+          $out[] = array(
+            'style' => $context_style,
+            'render' => '...',
+            'text' => '',
+          );
+          break;
+        default:
+          break;
+      }
+    }
+
+    // Remove all leading and trailing empty lines, since these just look kind
+    // of weird in mail.
+    foreach ($out as $key => $line) {
+      if (!strlen(trim($line['text']))) {
+        unset($out[$key]);
+      } else {
+        break;
+      }
+    }
+
+    $keys = array_reverse(array_keys($out));
+    foreach ($keys as $key) {
+      $line = $out[$key];
+      if (!strlen(trim($line['text']))) {
+        unset($out[$key]);
+      } else {
+        break;
+      }
+    }
+
+    // If the user has commented on an empty line in the middle of a bunch of
+    // other empty lines, emit an explicit marker instead of just rendering
+    // nothing.
+    if (!$out) {
+      $out[] = array(
+        'style' => 'color: #888888;',
+        'render' => pht('(Empty.)'),
+      );
+    }
+
+    $render = array();
+    foreach ($out as $line) {
+      $style = $line['style'];
+      $style = "padding: 0 8px; margin: 0 4px; {$style}";
+
+      $render[] = phutil_tag(
+        'div',
+        array(
+          'style' => $style,
+        ),
+        $line['render']);
+    }
+
+    $style_map = id(new PhabricatorDefaultSyntaxStyle())
+      ->getRemarkupStyleMap();
+
+    $styled_body = id(new PhutilPygmentizeParser())
+      ->setMap($style_map)
+      ->parse((string)hsprintf('%s', $render));
+
+    return phutil_safe_html($styled_body);
+  }
+
+}
diff --git a/src/applications/differential/view/DifferentialReviewersView.php b/src/applications/differential/view/DifferentialReviewersView.php
index ac4e48227..b94e246d7 100644
--- a/src/applications/differential/view/DifferentialReviewersView.php
+++ b/src/applications/differential/view/DifferentialReviewersView.php
@@ -1,130 +1,131 @@
 <?php
 
 final class DifferentialReviewersView extends AphrontView {
 
   private $reviewers;
   private $handles;
   private $diff;
 
   public function setReviewers(array $reviewers) {
     assert_instances_of($reviewers, 'DifferentialReviewer');
     $this->reviewers = $reviewers;
     return $this;
   }
 
   public function setHandles(array $handles) {
     assert_instances_of($handles, 'PhabricatorObjectHandle');
     $this->handles = $handles;
     return $this;
   }
 
   public function setActiveDiff(DifferentialDiff $diff) {
     $this->diff = $diff;
     return $this;
   }
 
   public function render() {
     $viewer = $this->getUser();
 
     $view = new PHUIStatusListView();
     foreach ($this->reviewers as $reviewer) {
       $phid = $reviewer->getReviewerPHID();
       $handle = $this->handles[$phid];
 
       // If we're missing either the diff or action information for the
       // reviewer, render information as current.
       $is_current = (!$this->diff) ||
                     (!$reviewer->getDiffID()) ||
                     ($this->diff->getID() == $reviewer->getDiffID());
 
       $item = new PHUIStatusItemView();
 
       $item->setHighlighted($reviewer->hasAuthority($viewer));
 
       switch ($reviewer->getStatus()) {
         case DifferentialReviewerStatus::STATUS_ADDED:
           $item->setIcon(
             PHUIStatusItemView::ICON_OPEN,
             'bluegrey',
             pht('Review Requested'));
           break;
 
         case DifferentialReviewerStatus::STATUS_ACCEPTED:
           if ($is_current) {
             $item->setIcon(
               PHUIStatusItemView::ICON_ACCEPT,
               'green',
               pht('Accepted'));
           } else {
             $item->setIcon(
               PHUIStatusItemView::ICON_ACCEPT,
-              'dark',
+              'bluegrey',
               pht('Accepted Prior Diff'));
           }
           break;
 
         case DifferentialReviewerStatus::STATUS_ACCEPTED_OLDER:
           $item->setIcon(
-            PHUIStatusItemView::ICON_ACCEPT,
-            'dark',
+            'fa-check-circle-o',
+            'bluegrey',
             pht('Accepted Prior Diff'));
           break;
 
         case DifferentialReviewerStatus::STATUS_REJECTED:
           if ($is_current) {
             $item->setIcon(
               PHUIStatusItemView::ICON_REJECT,
               'red',
               pht('Requested Changes'));
           } else {
             $item->setIcon(
-              PHUIStatusItemView::ICON_REJECT,
-              'dark',
+              'fa-times-circle-o',
+              'bluegrey',
               pht('Requested Changes to Prior Diff'));
           }
           break;
 
         case DifferentialReviewerStatus::STATUS_REJECTED_OLDER:
           $item->setIcon(
-            PHUIStatusItemView::ICON_REJECT,
-            'dark',
+            'fa-times-circle-o',
+            'bluegrey',
             pht('Rejected Prior Diff'));
           break;
 
         case DifferentialReviewerStatus::STATUS_COMMENTED:
           if ($is_current) {
             $item->setIcon(
-              PHUIStatusItemView::ICON_INFO,
+              'fa-question-circle',
               'blue',
               pht('Commented'));
           } else {
             $item->setIcon(
-              'info-dark',
+              'fa-question-circle-o',
+              'bluegrey',
               pht('Commented Previously'));
           }
           break;
 
         case DifferentialReviewerStatus::STATUS_BLOCKING:
           $item->setIcon(
             PHUIStatusItemView::ICON_MINUS,
             'red',
             pht('Blocking Review'));
           break;
 
         default:
           $item->setIcon(
             PHUIStatusItemView::ICON_QUESTION,
             'bluegrey',
             pht('%s?', $reviewer->getStatus()));
           break;
 
       }
 
       $item->setTarget($handle->renderHovercardLink());
       $view->addItem($item);
     }
 
     return $view;
   }
 
 }
diff --git a/src/applications/diffusion/application/PhabricatorDiffusionApplication.php b/src/applications/diffusion/application/PhabricatorDiffusionApplication.php
index ec27124d0..7eff7d7bc 100644
--- a/src/applications/diffusion/application/PhabricatorDiffusionApplication.php
+++ b/src/applications/diffusion/application/PhabricatorDiffusionApplication.php
@@ -1,198 +1,179 @@
 <?php
 
 final class PhabricatorDiffusionApplication extends PhabricatorApplication {
 
   public function getName() {
     return pht('Diffusion');
   }
 
   public function getShortDescription() {
     return pht('Host and Browse Repositories');
   }
 
   public function getBaseURI() {
     return '/diffusion/';
   }
 
   public function getIcon() {
     return 'fa-code';
   }
 
   public function isPinnedByDefault(PhabricatorUser $viewer) {
     return true;
   }
 
   public function getHelpDocumentationArticles(PhabricatorUser $viewer) {
     return array(
       array(
         'name' => pht('Diffusion User Guide'),
         'href' => PhabricatorEnv::getDoclink('Diffusion User Guide'),
       ),
     );
   }
 
   public function getFactObjectsForAnalysis() {
     return array(
       new PhabricatorRepositoryCommit(),
     );
   }
 
   public function getRemarkupRules() {
     return array(
       new DiffusionCommitRemarkupRule(),
       new DiffusionRepositoryRemarkupRule(),
       new DiffusionRepositoryByIDRemarkupRule(),
     );
   }
 
   public function getRoutes() {
     return array(
       '/(?:'.
         'r(?P<repositoryCallsign>[A-Z]+)'.
         '|'.
         'R(?P<repositoryID>[1-9]\d*):'.
       ')(?P<commit>[a-f0-9]+)'
         => 'DiffusionCommitController',
 
       '/diffusion/' => array(
         $this->getQueryRoutePattern()
           => 'DiffusionRepositoryListController',
-        $this->getEditRoutePattern('editpro/') =>
-          'DiffusionRepositoryEditproController',
-        'new/' => 'DiffusionRepositoryNewController',
-        '(?P<edit>create)/' => 'DiffusionRepositoryCreateController',
-        '(?P<edit>import)/' => 'DiffusionRepositoryCreateController',
+        $this->getEditRoutePattern('edit/') =>
+          'DiffusionRepositoryEditController',
         'pushlog/' => array(
           '(?:query/(?P<queryKey>[^/]+)/)?' => 'DiffusionPushLogListController',
           'view/(?P<id>\d+)/' => 'DiffusionPushEventViewController',
         ),
         '(?:'.
           '(?P<repositoryCallsign>[A-Z]+)'.
           '|'.
           '(?P<repositoryID>[1-9]\d*)'.
         ')/' => array(
           '' => 'DiffusionRepositoryController',
 
           'repository/(?P<dblob>.*)'    => 'DiffusionRepositoryController',
           'change/(?P<dblob>.*)'        => 'DiffusionChangeController',
           'history/(?P<dblob>.*)'       => 'DiffusionHistoryController',
           'browse/(?P<dblob>.*)'        => 'DiffusionBrowseController',
           'lastmodified/(?P<dblob>.*)'  => 'DiffusionLastModifiedController',
           'diff/'                       => 'DiffusionDiffController',
           'tags/(?P<dblob>.*)'          => 'DiffusionTagListController',
           'branches/(?P<dblob>.*)'      => 'DiffusionBranchTableController',
           'refs/(?P<dblob>.*)'          => 'DiffusionRefTableController',
           'lint/(?P<dblob>.*)'          => 'DiffusionLintController',
           'commit/(?P<commit>[a-z0-9]+)/branches/'
             => 'DiffusionCommitBranchesController',
           'commit/(?P<commit>[a-z0-9]+)/tags/'
             => 'DiffusionCommitTagsController',
           'commit/(?P<commit>[a-z0-9]+)/edit/'
             => 'DiffusionCommitEditController',
           'manage/(?:(?P<panel>[^/]+)/)?'
-            => 'DiffusionRepositoryManageController',
+            => 'DiffusionRepositoryManagePanelsController',
           'uri/' => array(
             'view/(?P<id>[0-9]\d*)/' => 'DiffusionRepositoryURIViewController',
             'disable/(?P<id>[0-9]\d*)/'
               => 'DiffusionRepositoryURIDisableController',
             $this->getEditRoutePattern('edit/')
               => 'DiffusionRepositoryURIEditController',
+            'credential/(?P<id>[0-9]\d*)/(?P<action>edit|remove)/'
+              => 'DiffusionRepositoryURICredentialController',
           ),
           'edit/' => array(
-            '' => 'DiffusionRepositoryEditMainController',
-            'basic/' => 'DiffusionRepositoryEditBasicController',
-            'encoding/' => 'DiffusionRepositoryEditEncodingController',
             'activate/' => 'DiffusionRepositoryEditActivateController',
             'dangerous/' => 'DiffusionRepositoryEditDangerousController',
-            'branches/' => 'DiffusionRepositoryEditBranchesController',
-            'subversion/' => 'DiffusionRepositoryEditSubversionController',
-            'actions/' => 'DiffusionRepositoryEditActionsController',
-            '(?P<edit>remote)/' => 'DiffusionRepositoryCreateController',
-            '(?P<edit>policy)/' => 'DiffusionRepositoryCreateController',
-            'storage/' => 'DiffusionRepositoryEditStorageController',
             'delete/' => 'DiffusionRepositoryEditDeleteController',
-            'hosting/' => 'DiffusionRepositoryEditHostingController',
-            '(?P<serve>serve)/' => 'DiffusionRepositoryEditHostingController',
             'update/' => 'DiffusionRepositoryEditUpdateController',
-            'symbol/' => 'DiffusionRepositorySymbolsController',
-            'staging/' => 'DiffusionRepositoryEditStagingController',
-            'automation/' => 'DiffusionRepositoryEditAutomationController',
             'testautomation/' => 'DiffusionRepositoryTestAutomationController',
           ),
           'pathtree/(?P<dblob>.*)' => 'DiffusionPathTreeController',
-          'mirror/' => array(
-            'edit/(?:(?P<id>\d+)/)?' => 'DiffusionMirrorEditController',
-            'delete/(?P<id>\d+)/' => 'DiffusionMirrorDeleteController',
-          ),
         ),
 
         // NOTE: This must come after the rule above; it just gives us a
         // catch-all for serving repositories over HTTP. We must accept
         // requests without the trailing "/" because SVN commands don't
         // necessarily include it.
         '(?:(?P<repositoryCallsign>[A-Z]+)|(?P<repositoryID>[1-9]\d*))'.
           '(?:/.*)?'
           => 'DiffusionRepositoryDefaultController',
 
         'inline/' => array(
           'edit/(?P<phid>[^/]+)/' => 'DiffusionInlineCommentController',
           'preview/(?P<phid>[^/]+)/'
             => 'DiffusionInlineCommentPreviewController',
         ),
         'services/' => array(
           'path/' => array(
             'complete/' => 'DiffusionPathCompleteController',
             'validate/' => 'DiffusionPathValidateController',
           ),
         ),
         'symbol/(?P<name>[^/]+)/' => 'DiffusionSymbolController',
         'external/' => 'DiffusionExternalController',
         'lint/' => 'DiffusionLintController',
       ),
     );
   }
 
   public function getApplicationOrder() {
     return 0.120;
   }
 
   protected function getCustomCapabilities() {
     return array(
       DiffusionDefaultViewCapability::CAPABILITY => array(
         'template' => PhabricatorRepositoryRepositoryPHIDType::TYPECONST,
         'capability' => PhabricatorPolicyCapability::CAN_VIEW,
       ),
       DiffusionDefaultEditCapability::CAPABILITY => array(
         'default' => PhabricatorPolicies::POLICY_ADMIN,
         'template' => PhabricatorRepositoryRepositoryPHIDType::TYPECONST,
         'capability' => PhabricatorPolicyCapability::CAN_EDIT,
       ),
       DiffusionDefaultPushCapability::CAPABILITY => array(
         'template' => PhabricatorRepositoryRepositoryPHIDType::TYPECONST,
       ),
       DiffusionCreateRepositoriesCapability::CAPABILITY => array(
         'default' => PhabricatorPolicies::POLICY_ADMIN,
       ),
     );
   }
 
   public function getMailCommandObjects() {
     return array(
       'commit' => array(
         'name' => pht('Email Commands: Commits'),
         'header' => pht('Interacting with Commits'),
         'object' => new PhabricatorRepositoryCommit(),
         'summary' => pht(
           'This page documents the commands you can use to interact with '.
           'commits and audits in Diffusion.'),
       ),
     );
   }
 
   public function getApplicationSearchDocumentTypes() {
     return array(
       PhabricatorRepositoryCommitPHIDType::TYPECONST,
     );
   }
 
 }
diff --git a/src/applications/diffusion/controller/DiffusionMirrorDeleteController.php b/src/applications/diffusion/controller/DiffusionMirrorDeleteController.php
deleted file mode 100644
index a049146cb..000000000
--- a/src/applications/diffusion/controller/DiffusionMirrorDeleteController.php
+++ /dev/null
@@ -1,45 +0,0 @@
-<?php
-
-final class DiffusionMirrorDeleteController
-  extends DiffusionController {
-
-  public function handleRequest(AphrontRequest $request) {
-    $response = $this->loadDiffusionContext();
-    if ($response) {
-      return $response;
-    }
-
-    $viewer = $this->getViewer();
-    $drequest = $this->getDiffusionRequest();
-    $repository = $drequest->getRepository();
-
-    $mirror = id(new PhabricatorRepositoryMirrorQuery())
-      ->setViewer($viewer)
-      ->withIDs(array($request->getURIData('id')))
-      ->requireCapabilities(
-        array(
-          PhabricatorPolicyCapability::CAN_VIEW,
-          PhabricatorPolicyCapability::CAN_EDIT,
-        ))
-      ->executeOne();
-    if (!$mirror) {
-      return new Aphront404Response();
-    }
-
-    $edit_uri = $this->getRepositoryControllerURI($repository, 'edit/#mirrors');
-
-    if ($request->isFormPost()) {
-      $mirror->delete();
-      return id(new AphrontReloadResponse())->setURI($edit_uri);
-    }
-
-    return $this->newDialog()
-      ->setTitle(pht('Really delete mirror?'))
-      ->appendChild(
-        pht('Phabricator will stop pushing updates to this mirror.'))
-      ->addSubmitButton(pht('Delete Mirror'))
-      ->addCancelButton($edit_uri);
-  }
-
-
-}
diff --git a/src/applications/diffusion/controller/DiffusionMirrorEditController.php b/src/applications/diffusion/controller/DiffusionMirrorEditController.php
deleted file mode 100644
index d438a9e6d..000000000
--- a/src/applications/diffusion/controller/DiffusionMirrorEditController.php
+++ /dev/null
@@ -1,130 +0,0 @@
-<?php
-
-final class DiffusionMirrorEditController
-  extends DiffusionController {
-
-  public function handleRequest(AphrontRequest $request) {
-    $response = $this->loadDiffusionContext();
-    if ($response) {
-      return $response;
-    }
-
-    $viewer = $this->getViewer();
-    $drequest = $this->getDiffusionRequest();
-    $repository = $drequest->getRepository();
-
-    PhabricatorPolicyFilter::requireCapability(
-      $viewer,
-      $repository,
-      PhabricatorPolicyCapability::CAN_EDIT);
-
-    if ($request->getURIData('id')) {
-      $mirror = id(new PhabricatorRepositoryMirrorQuery())
-        ->setViewer($viewer)
-        ->withIDs(array($request->getURIData('id')))
-        ->requireCapabilities(
-          array(
-            PhabricatorPolicyCapability::CAN_VIEW,
-            PhabricatorPolicyCapability::CAN_EDIT,
-          ))
-        ->executeOne();
-      if (!$mirror) {
-        return new Aphront404Response();
-      }
-      $is_new = false;
-    } else {
-      $mirror = PhabricatorRepositoryMirror::initializeNewMirror($viewer)
-        ->setRepositoryPHID($repository->getPHID())
-        ->attachRepository($repository);
-      $is_new = true;
-    }
-
-    $edit_uri = $this->getRepositoryControllerURI($repository, 'edit/#mirrors');
-
-    $v_remote = $mirror->getRemoteURI();
-    $e_remote = true;
-
-    $v_credentials = $mirror->getCredentialPHID();
-    $e_credentials = null;
-
-    $credentials = id(new PassphraseCredentialQuery())
-      ->setViewer($viewer)
-      ->withIsDestroyed(false)
-      ->execute();
-
-    $errors = array();
-    if ($request->isFormPost()) {
-      $v_remote = $request->getStr('remoteURI');
-      if (strlen($v_remote)) {
-        try {
-          PhabricatorRepository::assertValidRemoteURI($v_remote);
-          $e_remote = null;
-        } catch (Exception $ex) {
-          $e_remote = pht('Invalid');
-          $errors[] = $ex->getMessage();
-        }
-      } else {
-        $e_remote = pht('Required');
-        $errors[] = pht('You must provide a remote URI.');
-      }
-
-      $v_credentials = $request->getStr('credential');
-      if ($v_credentials) {
-        $phids = mpull($credentials, null, 'getPHID');
-        if (empty($phids[$v_credentials])) {
-          $e_credentials = pht('Invalid');
-          $errors[] = pht(
-            'You do not have permission to use those credentials.');
-        }
-      }
-
-      if (!$errors) {
-        $mirror
-          ->setRemoteURI($v_remote)
-          ->setCredentialPHID($v_credentials)
-          ->save();
-        return id(new AphrontReloadResponse())->setURI($edit_uri);
-      }
-    }
-
-    $form_errors = null;
-    if ($errors) {
-      $form_errors = id(new PHUIInfoView())
-        ->setErrors($errors);
-    }
-
-    if ($is_new) {
-      $title = pht('Create Mirror');
-      $submit = pht('Create Mirror');
-    } else {
-      $title = pht('Edit Mirror');
-      $submit = pht('Save Changes');
-    }
-
-    $form = id(new PHUIFormLayoutView())
-      ->appendChild(
-        id(new AphrontFormTextControl())
-          ->setLabel(pht('Remote URI'))
-          ->setName('remoteURI')
-          ->setValue($v_remote)
-          ->setError($e_remote))
-      ->appendChild(
-        id(new PassphraseCredentialControl())
-          ->setLabel(pht('Credentials'))
-          ->setName('credential')
-          ->setAllowNull(true)
-          ->setValue($v_credentials)
-          ->setError($e_credentials)
-          ->setOptions($credentials));
-
-    return $this->newDialog()
-      ->setTitle($title)
-      ->setWidth(AphrontDialogView::WIDTH_FORM)
-      ->appendChild($form_errors)
-      ->appendChild($form)
-      ->addSubmitButton($submit)
-      ->addCancelButton($edit_uri);
-  }
-
-
-}
diff --git a/src/applications/diffusion/controller/DiffusionRepositoryController.php b/src/applications/diffusion/controller/DiffusionRepositoryController.php
index b9e8fa40c..d38ad7778 100644
--- a/src/applications/diffusion/controller/DiffusionRepositoryController.php
+++ b/src/applications/diffusion/controller/DiffusionRepositoryController.php
@@ -1,780 +1,691 @@
 <?php
 
 final class DiffusionRepositoryController extends DiffusionController {
 
   public function shouldAllowPublic() {
     return true;
   }
 
   public function handleRequest(AphrontRequest $request) {
     $response = $this->loadDiffusionContext();
     if ($response) {
       return $response;
     }
 
     $viewer = $this->getViewer();
     $drequest = $this->getDiffusionRequest();
     $repository = $drequest->getRepository();
 
     $crumbs = $this->buildCrumbs();
     $crumbs->setBorder(true);
 
     $header = $this->buildHeaderView($repository);
     $curtain = $this->buildCurtain($repository);
     $property_table = $this->buildPropertiesTable($repository);
     $description = $this->buildDescriptionView($repository);
     $locate_file = $this->buildLocateFile();
 
     // Before we do any work, make sure we're looking at a some content: we're
     // on a valid branch, and the repository is not empty.
     $page_has_content = false;
     $empty_title = null;
     $empty_message = null;
 
     // If this VCS supports branches, check that the selected branch actually
     // exists.
     if ($drequest->supportsBranches()) {
       // NOTE: Mercurial may have multiple branch heads with the same name.
       $ref_cursors = id(new PhabricatorRepositoryRefCursorQuery())
         ->setViewer($viewer)
         ->withRepositoryPHIDs(array($repository->getPHID()))
         ->withRefTypes(array(PhabricatorRepositoryRefCursor::TYPE_BRANCH))
         ->withRefNames(array($drequest->getBranch()))
         ->execute();
       if ($ref_cursors) {
         // This is a valid branch, so we necessarily have some content.
         $page_has_content = true;
       } else {
         $empty_title = pht('No Such Branch');
         $empty_message = pht(
           'There is no branch named "%s" in this repository.',
           $drequest->getBranch());
       }
     }
 
     // If we didn't find any branches, check if there are any commits at all.
     // This can tailor the message for empty repositories.
     if (!$page_has_content) {
       $any_commit = id(new DiffusionCommitQuery())
         ->setViewer($viewer)
         ->withRepository($repository)
         ->setLimit(1)
         ->execute();
       if ($any_commit) {
         if (!$drequest->supportsBranches()) {
           $page_has_content = true;
         }
       } else {
         $empty_title = pht('Empty Repository');
         $empty_message = pht('This repository does not have any commits yet.');
       }
     }
 
     if ($page_has_content) {
       $content = $this->buildNormalContent($drequest);
     } else {
       $content = id(new PHUIInfoView())
         ->setTitle($empty_title)
         ->setSeverity(PHUIInfoView::SEVERITY_WARNING)
         ->setErrors(array($empty_message));
     }
 
     $view = id(new PHUITwoColumnView())
       ->setHeader($header)
       ->setCurtain($curtain)
       ->setMainColumn(array(
         $property_table,
         $description,
         $locate_file,
       ))
       ->setFooter($content);
 
     return $this->newPage()
       ->setTitle(
         array(
           $repository->getName(),
           $repository->getDisplayName(),
         ))
       ->setCrumbs($crumbs)
       ->appendChild(array(
         $view,
       ));
   }
 
 
   private function buildNormalContent(DiffusionRequest $drequest) {
     $request = $this->getRequest();
     $repository = $drequest->getRepository();
 
     $phids = array();
     $content = array();
 
     try {
       $history_results = $this->callConduitWithDiffusionRequest(
         'diffusion.historyquery',
         array(
           'commit' => $drequest->getCommit(),
           'path' => $drequest->getPath(),
           'offset' => 0,
           'limit' => 15,
         ));
       $history = DiffusionPathChange::newFromConduit(
         $history_results['pathChanges']);
 
       foreach ($history as $item) {
         $data = $item->getCommitData();
         if ($data) {
           if ($data->getCommitDetail('authorPHID')) {
             $phids[$data->getCommitDetail('authorPHID')] = true;
           }
           if ($data->getCommitDetail('committerPHID')) {
             $phids[$data->getCommitDetail('committerPHID')] = true;
           }
         }
       }
       $history_exception = null;
     } catch (Exception $ex) {
       $history_results = null;
       $history = null;
       $history_exception = $ex;
     }
 
     $browse_pager = id(new PHUIPagerView())
       ->readFromRequest($request);
 
     try {
       $browse_results = DiffusionBrowseResultSet::newFromConduit(
         $this->callConduitWithDiffusionRequest(
           'diffusion.browsequery',
           array(
             'path' => $drequest->getPath(),
             'commit' => $drequest->getCommit(),
             'limit' => $browse_pager->getPageSize() + 1,
           )));
       $browse_paths = $browse_results->getPaths();
       $browse_paths = $browse_pager->sliceResults($browse_paths);
 
       foreach ($browse_paths as $item) {
         $data = $item->getLastCommitData();
         if ($data) {
           if ($data->getCommitDetail('authorPHID')) {
             $phids[$data->getCommitDetail('authorPHID')] = true;
           }
           if ($data->getCommitDetail('committerPHID')) {
             $phids[$data->getCommitDetail('committerPHID')] = true;
           }
         }
       }
 
       $browse_exception = null;
     } catch (Exception $ex) {
       $browse_results = null;
       $browse_paths = null;
       $browse_exception = $ex;
     }
 
     $phids = array_keys($phids);
     $handles = $this->loadViewerHandles($phids);
 
     if ($browse_results) {
       $readme = $this->renderDirectoryReadme($browse_results);
     } else {
       $readme = null;
     }
 
     $content[] = $this->buildBrowseTable(
       $browse_results,
       $browse_paths,
       $browse_exception,
       $handles,
       $browse_pager);
 
     $content[] = $this->buildHistoryTable(
       $history_results,
       $history,
       $history_exception);
 
     try {
       $content[] = $this->buildTagListTable($drequest);
     } catch (Exception $ex) {
       if (!$repository->isImporting()) {
         $content[] = $this->renderStatusMessage(
           pht('Unable to Load Tags'),
           $ex->getMessage());
       }
     }
 
     try {
       $content[] = $this->buildBranchListTable($drequest);
     } catch (Exception $ex) {
       if (!$repository->isImporting()) {
         $content[] = $this->renderStatusMessage(
           pht('Unable to Load Branches'),
           $ex->getMessage());
       }
     }
 
     if ($readme) {
       $content[] = $readme;
     }
 
     return $content;
   }
 
   private function buildHeaderView(PhabricatorRepository $repository) {
     $viewer = $this->getViewer();
     $header = id(new PHUIHeaderView())
       ->setHeader($repository->getName())
       ->setUser($viewer)
       ->setPolicyObject($repository)
       ->setHeaderIcon('fa-code');
 
     if (!$repository->isTracked()) {
       $header->setStatus('fa-ban', 'dark', pht('Inactive'));
     } else if ($repository->isImporting()) {
       $ratio = $repository->loadImportProgress();
       $percentage = sprintf('%.2f%%', 100 * $ratio);
       $header->setStatus(
         'fa-clock-o',
         'indigo',
         pht('Importing (%s)...', $percentage));
     } else {
       $header->setStatus('fa-check', 'bluegrey', pht('Active'));
     }
 
     return $header;
   }
 
   private function buildCurtain(PhabricatorRepository $repository) {
     $viewer = $this->getViewer();
 
-    $edit_uri = $repository->getPathURI('edit/');
+    $edit_uri = $repository->getPathURI('manage/');
     $curtain = $this->newCurtainView($repository);
 
-    $can_edit = PhabricatorPolicyFilter::hasCapability(
-      $viewer,
-      $repository,
-      PhabricatorPolicyCapability::CAN_EDIT);
-
     $curtain->addAction(
       id(new PhabricatorActionView())
-        ->setName(pht('Edit Repository'))
-        ->setIcon('fa-pencil')
-        ->setHref($edit_uri)
-        ->setWorkflow(!$can_edit)
-        ->setDisabled(!$can_edit));
+        ->setName(pht('Manage Repository'))
+        ->setIcon('fa-cogs')
+        ->setHref($edit_uri));
 
     if ($repository->isHosted()) {
       $push_uri = $this->getApplicationURI(
         'pushlog/?repositories='.$repository->getMonogram());
 
       $curtain->addAction(
         id(new PhabricatorActionView())
           ->setName(pht('View Push Logs'))
           ->setIcon('fa-list-alt')
           ->setHref($push_uri));
     }
 
     return $curtain;
   }
 
   private function buildDescriptionView(PhabricatorRepository $repository) {
     $viewer = $this->getViewer();
     $view = id(new PHUIPropertyListView())
       ->setUser($viewer);
 
     $description = $repository->getDetail('description');
     if (strlen($description)) {
       $description = new PHUIRemarkupView($viewer, $description);
       $view->addTextContent($description);
       return id(new PHUIObjectBoxView())
         ->setHeaderText(pht('Description'))
         ->setBackground(PHUIObjectBoxView::BLUE_PROPERTY)
         ->appendChild($view);
     }
     return null;
   }
 
   private function buildPropertiesTable(PhabricatorRepository $repository) {
     $viewer = $this->getViewer();
 
     $view = id(new PHUIPropertyListView())
       ->setUser($viewer);
 
-    if ($repository->isHosted()) {
-      $ssh_uri = $repository->getSSHCloneURIObject();
-      if ($ssh_uri) {
-        $clone_uri = $this->renderCloneCommand(
-          $repository,
-          $ssh_uri,
-          $repository->getServeOverSSH(),
-          '/settings/panel/ssh/');
-
-        $view->addProperty(
-          $repository->isSVN()
-            ? pht('Checkout (SSH)')
-            : pht('Clone (SSH)'),
-          $clone_uri);
+    $display_never = PhabricatorRepositoryURI::DISPLAY_NEVER;
+
+    $uris = $repository->getURIs();
+    foreach ($uris as $uri) {
+      if ($uri->getIsDisabled()) {
+        continue;
       }
 
-      $http_uri = $repository->getHTTPCloneURIObject();
-      if ($http_uri) {
-        $clone_uri = $this->renderCloneCommand(
-          $repository,
-          $http_uri,
-          $repository->getServeOverHTTP(),
-          PhabricatorEnv::getEnvConfig('diffusion.allow-http-auth')
-            ? '/settings/panel/vcspassword/'
-            : null);
-
-        $view->addProperty(
-          $repository->isSVN()
-            ? pht('Checkout (HTTP)')
-            : pht('Clone (HTTP)'),
-          $clone_uri);
+      if ($uri->getEffectiveDisplayType() == $display_never) {
+        continue;
       }
-    } else {
-      switch ($repository->getVersionControlSystem()) {
-        case PhabricatorRepositoryType::REPOSITORY_TYPE_GIT:
-        case PhabricatorRepositoryType::REPOSITORY_TYPE_MERCURIAL:
-          $view->addProperty(
-            pht('Clone'),
-            $this->renderCloneCommand(
-              $repository,
-              $repository->getPublicCloneURI()));
-          break;
-        case PhabricatorRepositoryType::REPOSITORY_TYPE_SVN:
-          $view->addProperty(
-            pht('Checkout'),
-            $this->renderCloneCommand(
-              $repository,
-              $repository->getPublicCloneURI()));
-          break;
+
+      if ($repository->isSVN()) {
+        $label = pht('Checkout');
+      } else {
+        $label = pht('Clone');
       }
+
+      $view->addProperty(
+        $label,
+        $this->renderCloneURI($repository, $uri));
     }
 
     $box = id(new PHUIObjectBoxView())
       ->setHeaderText(pht('Details'))
       ->setBackground(PHUIObjectBoxView::BLUE_PROPERTY)
       ->appendChild($view);
 
     $info = null;
     $drequest = $this->getDiffusionRequest();
 
     // Try to load alternatives. This may fail for repositories which have not
     // cloned yet. If it does, just ignore it and continue.
     try {
       $alternatives = $drequest->getRefAlternatives();
     } catch (ConduitClientException $ex) {
       $alternatives = array();
     }
 
     if ($alternatives) {
       $message = array(
         pht(
           'The ref "%s" is ambiguous in this repository.',
           $drequest->getBranch()),
         ' ',
         phutil_tag(
           'a',
           array(
             'href' => $drequest->generateURI(
               array(
                 'action' => 'refs',
               )),
           ),
           pht('View Alternatives')),
       );
 
       $messages = array($message);
 
       $info = id(new PHUIInfoView())
         ->setSeverity(PHUIInfoView::SEVERITY_WARNING)
         ->setErrors(array($message));
 
       $box->setInfoView($info);
     }
 
 
     return $box;
   }
 
   private function buildBranchListTable(DiffusionRequest $drequest) {
     $viewer = $this->getViewer();
 
     if ($drequest->getBranch() === null) {
       return null;
     }
 
     $limit = 15;
 
     $branches = $this->callConduitWithDiffusionRequest(
       'diffusion.branchquery',
       array(
         'closed' => false,
         'limit' => $limit + 1,
       ));
     if (!$branches) {
       return null;
     }
 
     $more_branches = (count($branches) > $limit);
     $branches = array_slice($branches, 0, $limit);
 
     $branches = DiffusionRepositoryRef::loadAllFromDictionaries($branches);
 
     $commits = id(new DiffusionCommitQuery())
       ->setViewer($viewer)
       ->withIdentifiers(mpull($branches, 'getCommitIdentifier'))
       ->withRepository($drequest->getRepository())
       ->execute();
 
     $table = id(new DiffusionBranchTableView())
       ->setUser($viewer)
       ->setDiffusionRequest($drequest)
       ->setBranches($branches)
       ->setCommits($commits);
 
     $panel = id(new PHUIObjectBoxView())
       ->setBackground(PHUIObjectBoxView::BLUE_PROPERTY);
     $header = new PHUIHeaderView();
     $header->setHeader(pht('Branches'));
 
     if ($more_branches) {
       $header->setSubHeader(pht('Showing %d branches.', $limit));
     }
 
     $button = new PHUIButtonView();
     $button->setText(pht('Show All'));
     $button->setTag('a');
     $button->setIcon('fa-code-fork');
     $button->setHref($drequest->generateURI(
       array(
         'action' => 'branches',
       )));
 
     $header->addActionLink($button);
     $panel->setHeader($header);
     $panel->setTable($table);
 
     return $panel;
   }
 
   private function buildTagListTable(DiffusionRequest $drequest) {
     $viewer = $this->getViewer();
     $repository = $drequest->getRepository();
 
     switch ($repository->getVersionControlSystem()) {
       case PhabricatorRepositoryType::REPOSITORY_TYPE_SVN:
         // no tags in SVN
         return null;
     }
     $tag_limit = 15;
     $tags = array();
     $tags = DiffusionRepositoryTag::newFromConduit(
       $this->callConduitWithDiffusionRequest(
         'diffusion.tagsquery',
         array(
           // On the home page, we want to find tags on any branch.
           'commit' => null,
           'limit' => $tag_limit + 1,
         )));
 
     if (!$tags) {
       return null;
     }
 
     $more_tags = (count($tags) > $tag_limit);
     $tags = array_slice($tags, 0, $tag_limit);
 
     $commits = id(new DiffusionCommitQuery())
       ->setViewer($viewer)
       ->withIdentifiers(mpull($tags, 'getCommitIdentifier'))
       ->withRepository($repository)
       ->needCommitData(true)
       ->execute();
 
     $view = id(new DiffusionTagListView())
       ->setUser($viewer)
       ->setDiffusionRequest($drequest)
       ->setTags($tags)
       ->setCommits($commits);
 
     $phids = $view->getRequiredHandlePHIDs();
     $handles = $this->loadViewerHandles($phids);
     $view->setHandles($handles);
 
     $panel = new PHUIObjectBoxView();
     $header = new PHUIHeaderView();
     $header->setHeader(pht('Tags'));
 
     if ($more_tags) {
       $header->setSubHeader(
         pht('Showing the %d most recent tags.', $tag_limit));
     }
 
     $button = new PHUIButtonView();
     $button->setText(pht('Show All Tags'));
     $button->setTag('a');
     $button->setIcon('fa-tag');
     $button->setHref($drequest->generateURI(
       array(
         'action' => 'tags',
       )));
 
     $header->addActionLink($button);
 
     $panel->setHeader($header);
     $panel->setTable($view);
     $panel->setBackground(PHUIObjectBoxView::BLUE_PROPERTY);
 
     return $panel;
   }
 
   private function buildHistoryTable(
     $history_results,
     $history,
     $history_exception) {
 
     $request = $this->getRequest();
     $viewer = $request->getUser();
     $drequest = $this->getDiffusionRequest();
     $repository = $drequest->getRepository();
 
     if ($history_exception) {
       if ($repository->isImporting()) {
         return $this->renderStatusMessage(
           pht('Still Importing...'),
           pht(
             'This repository is still importing. History is not yet '.
             'available.'));
       } else {
         return $this->renderStatusMessage(
           pht('Unable to Retrieve History'),
           $history_exception->getMessage());
       }
     }
 
     $history_table = id(new DiffusionHistoryTableView())
       ->setUser($viewer)
       ->setDiffusionRequest($drequest)
       ->setHistory($history);
 
     // TODO: Super sketchy.
     $history_table->loadRevisions();
 
     if ($history_results) {
       $history_table->setParents($history_results['parents']);
     }
 
     $history_table->setIsHead(true);
 
     $icon = id(new PHUIIconView())
       ->setIcon('fa-list-alt');
 
     $button = id(new PHUIButtonView())
       ->setText(pht('View History'))
       ->setHref($drequest->generateURI(
         array(
           'action' => 'history',
         )))
       ->setTag('a')
       ->setIcon($icon);
 
     $panel = id(new PHUIObjectBoxView())
       ->setBackground(PHUIObjectBoxView::BLUE_PROPERTY);
     $header = id(new PHUIHeaderView())
       ->setHeader(pht('Recent Commits'))
       ->addActionLink($button);
     $panel->setHeader($header);
     $panel->setTable($history_table);
 
     return $panel;
   }
 
   private function buildLocateFile() {
     $request = $this->getRequest();
     $viewer = $request->getUser();
     $drequest = $this->getDiffusionRequest();
     $repository = $drequest->getRepository();
 
     $locate_panel = null;
     if ($repository->canUsePathTree()) {
       Javelin::initBehavior(
         'diffusion-locate-file',
         array(
           'controlID' => 'locate-control',
           'inputID' => 'locate-input',
           'browseBaseURI' => (string)$drequest->generateURI(
             array(
               'action' => 'browse',
             )),
           'uri' => (string)$drequest->generateURI(
             array(
               'action' => 'pathtree',
             )),
         ));
 
       $form = id(new AphrontFormView())
         ->setUser($viewer)
         ->appendChild(
           id(new AphrontFormTypeaheadControl())
             ->setHardpointID('locate-control')
             ->setID('locate-input')
             ->setLabel(pht('Locate File')));
       $form_box = id(new PHUIBoxView())
         ->appendChild($form->buildLayoutView());
       $locate_panel = id(new PHUIObjectBoxView())
         ->setHeaderText(pht('Locate File'))
         ->setBackground(PHUIObjectBoxView::BLUE_PROPERTY)
         ->appendChild($form_box);
     }
     return $locate_panel;
   }
 
   private function buildBrowseTable(
     $browse_results,
     $browse_paths,
     $browse_exception,
     array $handles,
     PHUIPagerView $pager) {
 
     require_celerity_resource('diffusion-icons-css');
 
     $request = $this->getRequest();
     $viewer = $request->getUser();
     $drequest = $this->getDiffusionRequest();
     $repository = $drequest->getRepository();
 
     if ($browse_exception) {
       if ($repository->isImporting()) {
         // The history table renders a useful message.
         return null;
       } else {
         return $this->renderStatusMessage(
           pht('Unable to Retrieve Paths'),
           $browse_exception->getMessage());
       }
     }
 
     $browse_table = id(new DiffusionBrowseTableView())
       ->setUser($viewer)
       ->setDiffusionRequest($drequest)
       ->setHandles($handles);
     if ($browse_paths) {
       $browse_table->setPaths($browse_paths);
     } else {
       $browse_table->setPaths(array());
     }
 
     $browse_uri = $drequest->generateURI(array('action' => 'browse'));
 
     $browse_panel = id(new PHUIObjectBoxView())
       ->setBackground(PHUIObjectBoxView::BLUE_PROPERTY);
     $header = id(new PHUIHeaderView())
       ->setHeader($repository->getName());
 
     $icon = id(new PHUIIconView())
       ->setIcon('fa-folder-open');
 
     $button = new PHUIButtonView();
     $button->setText(pht('Browse Repository'));
     $button->setTag('a');
     $button->setIcon($icon);
     $button->setHref($browse_uri);
 
     $header->addActionLink($button);
     $browse_panel->setHeader($header);
     $browse_panel->setTable($browse_table);
 
     $pager->setURI($browse_uri, 'offset');
 
     if ($pager->willShowPagingControls()) {
       $pager_box = $this->renderTablePagerBox($pager);
     } else {
       $pager_box = null;
     }
 
     return array(
       $browse_panel,
       $pager_box,
     );
   }
 
-  private function renderCloneCommand(
+  private function renderCloneURI(
     PhabricatorRepository $repository,
-    $uri,
-    $serve_mode = null,
-    $manage_uri = null) {
-
-    require_celerity_resource('diffusion-icons-css');
-
-    Javelin::initBehavior('select-on-click');
-
-    switch ($repository->getVersionControlSystem()) {
-      case PhabricatorRepositoryType::REPOSITORY_TYPE_GIT:
-        $command = csprintf(
-          'git clone %R',
-          $uri);
-        break;
-      case PhabricatorRepositoryType::REPOSITORY_TYPE_MERCURIAL:
-        $command = csprintf(
-          'hg clone %R',
-          $uri);
-        break;
-      case PhabricatorRepositoryType::REPOSITORY_TYPE_SVN:
-        if ($repository->isHosted()) {
-          $command = csprintf(
-            'svn checkout %R %R',
-            $uri,
-            $repository->getCloneName());
-        } else {
-          $command = csprintf(
-            'svn checkout %R',
-            $uri);
-        }
-        break;
-    }
+    PhabricatorRepositoryURI $uri) {
 
-    $input = javelin_tag(
-      'input',
-      array(
-        'type' => 'text',
-        'value' => (string)$command,
-        'class' => 'diffusion-clone-uri',
-        'sigil' => 'select-on-click',
-        'readonly' => 'true',
-      ));
-
-    $extras = array();
-    if ($serve_mode) {
-      if ($serve_mode === PhabricatorRepository::SERVE_READONLY) {
-        $extras[] = pht('(Read Only)');
-      }
-    }
-
-    if ($manage_uri) {
-      if ($this->getRequest()->getUser()->isLoggedIn()) {
-        $extras[] = phutil_tag(
-          'a',
-          array(
-            'href' => $manage_uri,
-          ),
-          pht('Manage Credentials'));
-      }
+    if ($repository->isSVN()) {
+      $display = csprintf(
+        'svn checkout %R %R',
+        (string)$uri->getDisplayURI(),
+        $repository->getCloneName());
+    } else {
+      $display = csprintf('%R', (string)$uri->getDisplayURI());
     }
 
-    if ($extras) {
-      $extras = phutil_implode_html(' ', $extras);
-      $extras = phutil_tag(
-        'div',
-        array(
-          'class' => 'diffusion-clone-extras',
-        ),
-        $extras);
-    }
+    $display = (string)$display;
+    $viewer = $this->getViewer();
 
-    return array($input, $extras);
+    return id(new DiffusionCloneURIView())
+      ->setViewer($viewer)
+      ->setRepository($repository)
+      ->setRepositoryURI($uri)
+      ->setDisplayURI($display);
   }
 
 }
diff --git a/src/applications/diffusion/controller/DiffusionRepositoryCreateController.php b/src/applications/diffusion/controller/DiffusionRepositoryCreateController.php
deleted file mode 100644
index 09fdd2356..000000000
--- a/src/applications/diffusion/controller/DiffusionRepositoryCreateController.php
+++ /dev/null
@@ -1,848 +0,0 @@
-<?php
-
-final class DiffusionRepositoryCreateController
-  extends DiffusionRepositoryEditController {
-
-  private $edit;
-  private $repository;
-
-  public function handleRequest(AphrontRequest $request) {
-    $viewer = $request->getUser();
-    $this->edit = $request->getURIData('edit');
-
-    // NOTE: We can end up here via either "Create Repository", or via
-    // "Import Repository", or via "Edit Remote", or via "Edit Policies". In
-    // the latter two cases, we show only a few of the pages.
-
-    $repository = null;
-    $service = null;
-    switch ($this->edit) {
-      case 'remote':
-      case 'policy':
-        $response = $this->loadDiffusionContextForEdit();
-        if ($response) {
-          return $response;
-        }
-
-        $repository = $this->getDiffusionRequest()->getRepository();
-
-        // Make sure we have CAN_EDIT.
-        PhabricatorPolicyFilter::requireCapability(
-          $viewer,
-          $repository,
-          PhabricatorPolicyCapability::CAN_EDIT);
-
-        $this->setRepository($repository);
-
-        $cancel_uri = $this->getRepositoryControllerURI($repository, 'edit/');
-        break;
-      case 'import':
-      case 'create':
-        $this->requireApplicationCapability(
-          DiffusionCreateRepositoriesCapability::CAPABILITY);
-
-        // Pick a random open service to allocate this repository on, if any
-        // exist. If there are no services, we aren't in cluster mode and
-        // will allocate locally. If there are services but none permit
-        // allocations, we fail.
-        $services = id(new AlmanacServiceQuery())
-          ->setViewer(PhabricatorUser::getOmnipotentUser())
-          ->withServiceTypes(
-            array(
-              AlmanacClusterRepositoryServiceType::SERVICETYPE,
-            ))
-          ->needProperties(true)
-          ->execute();
-        if ($services) {
-          // Filter out services which do not permit new allocations.
-          foreach ($services as $key => $possible_service) {
-            if ($possible_service->getAlmanacPropertyValue('closed')) {
-              unset($services[$key]);
-            }
-          }
-
-          if (!$services) {
-            throw new Exception(
-              pht(
-                'This install is configured in cluster mode, but all '.
-                'available repository cluster services are closed to new '.
-                'allocations. At least one service must be open to allow '.
-                'new allocations to take place.'));
-          }
-
-          shuffle($services);
-          $service = head($services);
-        }
-
-        $cancel_uri = $this->getApplicationURI('new/');
-        break;
-      default:
-        throw new Exception(pht('Invalid edit operation!'));
-    }
-
-    $form = id(new PHUIPagedFormView())
-      ->setUser($viewer)
-      ->setCancelURI($cancel_uri);
-
-    switch ($this->edit) {
-      case 'remote':
-        $title = pht('Edit Remote');
-        $form
-          ->addPage('remote-uri', $this->buildRemoteURIPage())
-          ->addPage('auth', $this->buildAuthPage());
-        break;
-      case 'policy':
-        $title = pht('Edit Policies');
-        $form
-          ->addPage('policy', $this->buildPolicyPage());
-        break;
-      case 'create':
-        $title = pht('Create Repository');
-        $form
-          ->addPage('vcs', $this->buildVCSPage())
-          ->addPage('name', $this->buildNamePage())
-          ->addPage('policy', $this->buildPolicyPage())
-          ->addPage('done', $this->buildDonePage());
-        break;
-      case 'import':
-        $title = pht('Import Repository');
-        $form
-          ->addPage('vcs', $this->buildVCSPage())
-          ->addPage('name', $this->buildNamePage())
-          ->addPage('remote-uri', $this->buildRemoteURIPage())
-          ->addPage('auth', $this->buildAuthPage())
-          ->addPage('policy', $this->buildPolicyPage())
-          ->addPage('done', $this->buildDonePage());
-        break;
-    }
-
-    if ($request->isFormPost()) {
-      $form->readFromRequest($request);
-      if ($form->isComplete()) {
-
-        $is_create = ($this->edit === 'import' || $this->edit === 'create');
-        $is_auth = ($this->edit == 'import' || $this->edit == 'remote');
-        $is_policy = ($this->edit != 'remote');
-        $is_init = ($this->edit == 'create');
-
-        if ($is_create) {
-          $repository = PhabricatorRepository::initializeNewRepository(
-            $viewer);
-        }
-
-        $template = id(new PhabricatorRepositoryTransaction());
-
-        $type_name = PhabricatorRepositoryTransaction::TYPE_NAME;
-        $type_vcs = PhabricatorRepositoryTransaction::TYPE_VCS;
-        $type_activate = PhabricatorRepositoryTransaction::TYPE_ACTIVATE;
-        $type_remote_uri = PhabricatorRepositoryTransaction::TYPE_REMOTE_URI;
-        $type_hosting = PhabricatorRepositoryTransaction::TYPE_HOSTING;
-        $type_http = PhabricatorRepositoryTransaction::TYPE_PROTOCOL_HTTP;
-        $type_ssh = PhabricatorRepositoryTransaction::TYPE_PROTOCOL_SSH;
-        $type_credential = PhabricatorRepositoryTransaction::TYPE_CREDENTIAL;
-        $type_view = PhabricatorTransactions::TYPE_VIEW_POLICY;
-        $type_edit = PhabricatorTransactions::TYPE_EDIT_POLICY;
-        $type_space = PhabricatorTransactions::TYPE_SPACE;
-        $type_push = PhabricatorRepositoryTransaction::TYPE_PUSH_POLICY;
-        $type_service = PhabricatorRepositoryTransaction::TYPE_SERVICE;
-
-        $xactions = array();
-
-        // If we're creating a new repository, set all this core stuff.
-        if ($is_create) {
-          $xactions[] = id(clone $template)
-            ->setTransactionType($type_name)
-            ->setNewValue(
-              $form->getPage('name')->getControl('name')->getValue());
-
-          $xactions[] = id(clone $template)
-            ->setTransactionType($type_vcs)
-            ->setNewValue(
-              $form->getPage('vcs')->getControl('vcs')->getValue());
-
-          $activate = $form->getPage('done')
-            ->getControl('activate')->getValue();
-          if ($activate == 'start') {
-            $initial_status = PhabricatorRepository::STATUS_ACTIVE;
-          } else {
-            $initial_status = PhabricatorRepository::STATUS_INACTIVE;
-          }
-
-          $xactions[] = id(clone $template)
-            ->setTransactionType($type_activate)
-            ->setNewValue($initial_status);
-
-          if ($service) {
-            $xactions[] = id(clone $template)
-              ->setTransactionType($type_service)
-              ->setNewValue($service->getPHID());
-          }
-        }
-
-        if ($is_init) {
-          $xactions[] = id(clone $template)
-            ->setTransactionType($type_hosting)
-            ->setNewValue(true);
-          $vcs = $form->getPage('vcs')->getControl('vcs')->getValue();
-          if ($vcs != PhabricatorRepositoryType::REPOSITORY_TYPE_SVN) {
-            if (PhabricatorEnv::getEnvConfig('diffusion.allow-http-auth')) {
-              $v_http_mode = PhabricatorRepository::SERVE_READWRITE;
-            } else {
-              $v_http_mode = PhabricatorRepository::SERVE_OFF;
-            }
-            $xactions[] = id(clone $template)
-              ->setTransactionType($type_http)
-              ->setNewValue($v_http_mode);
-          }
-
-          if (PhabricatorEnv::getEnvConfig('diffusion.ssh-user')) {
-            $v_ssh_mode = PhabricatorRepository::SERVE_READWRITE;
-          } else {
-            $v_ssh_mode = PhabricatorRepository::SERVE_OFF;
-          }
-          $xactions[] = id(clone $template)
-            ->setTransactionType($type_ssh)
-            ->setNewValue($v_ssh_mode);
-        }
-
-        if ($is_auth) {
-          $xactions[] = id(clone $template)
-            ->setTransactionType($type_remote_uri)
-            ->setNewValue(
-              $form->getPage('remote-uri')->getControl('remoteURI')
-                ->getValue());
-
-          $xactions[] = id(clone $template)
-            ->setTransactionType($type_credential)
-            ->setNewValue(
-              $form->getPage('auth')->getControl('credential')->getValue());
-        }
-
-        if ($is_policy) {
-          $policy_page = $form->getPage('policy');
-
-          $xactions[] = id(clone $template)
-            ->setTransactionType($type_view)
-            ->setNewValue($policy_page->getControl('viewPolicy')->getValue());
-
-          $xactions[] = id(clone $template)
-            ->setTransactionType($type_edit)
-            ->setNewValue($policy_page->getControl('editPolicy')->getValue());
-
-          if ($is_init || $repository->isHosted()) {
-            $xactions[] = id(clone $template)
-              ->setTransactionType($type_push)
-              ->setNewValue($policy_page->getControl('pushPolicy')->getValue());
-          }
-
-          $xactions[] = id(clone $template)
-            ->setTransactionType($type_space)
-            ->setNewValue(
-              $policy_page->getControl('viewPolicy')->getSpacePHID());
-        }
-
-        id(new PhabricatorRepositoryEditor())
-          ->setContinueOnNoEffect(true)
-          ->setContentSourceFromRequest($request)
-          ->setActor($viewer)
-          ->applyTransactions($repository, $xactions);
-
-        $repo_uri = $this->getRepositoryControllerURI($repository, 'edit/');
-        return id(new AphrontRedirectResponse())->setURI($repo_uri);
-      }
-    } else {
-      $dict = array();
-      if ($repository) {
-        $dict = array(
-          'remoteURI' => $repository->getRemoteURI(),
-          'credential' => $repository->getCredentialPHID(),
-          'viewPolicy' => $repository->getViewPolicy(),
-          'editPolicy' => $repository->getEditPolicy(),
-          'pushPolicy' => $repository->getPushPolicy(),
-          'spacePHID' => $repository->getSpacePHID(),
-        );
-      }
-      $form->readFromObject($dict);
-    }
-
-    $crumbs = $this->buildApplicationCrumbs();
-    $crumbs->addTextCrumb($title);
-
-    $header = id(new PHUIHeaderView())
-      ->setHeader($title)
-      ->setHeaderIcon('fa-pencil');
-
-    $form_box = id(new PHUIObjectBoxView())
-      ->setHeaderText($title)
-      ->setBackground(PHUIObjectBoxView::BLUE_PROPERTY)
-      ->setForm($form);
-
-    $view = id(new PHUITwoColumnView())
-      ->setHeader($header)
-      ->setFooter(array(
-        $form,
-      ));
-
-    return $this->newPage()
-      ->setTitle($title)
-      ->setCrumbs($crumbs)
-      ->appendChild($view);
-
-  }
-
-
-/* -(  Page: VCS Type  )----------------------------------------------------- */
-
-
-  private function buildVCSPage() {
-
-    $is_import = ($this->edit == 'import');
-
-    if ($is_import) {
-      $git_str = pht(
-        'Import a Git repository (for example, a repository hosted '.
-        'on GitHub).');
-      $hg_str = pht(
-        'Import a Mercurial repository (for example, a repository '.
-        'hosted on Bitbucket).');
-      $svn_str = pht('Import a Subversion repository.');
-    } else {
-      $git_str = pht('Create a new, empty Git repository.');
-      $hg_str = pht('Create a new, empty Mercurial repository.');
-      $svn_str = pht('Create a new, empty Subversion repository.');
-    }
-
-    $control = id(new AphrontFormRadioButtonControl())
-      ->setName('vcs')
-      ->setLabel(pht('Type'))
-      ->addButton(
-        PhabricatorRepositoryType::REPOSITORY_TYPE_GIT,
-        pht('Git'),
-        $git_str)
-      ->addButton(
-        PhabricatorRepositoryType::REPOSITORY_TYPE_MERCURIAL,
-        pht('Mercurial'),
-        $hg_str)
-      ->addButton(
-        PhabricatorRepositoryType::REPOSITORY_TYPE_SVN,
-        pht('Subversion'),
-        $svn_str);
-
-    return id(new PHUIFormPageView())
-      ->setPageName(pht('Repository Type'))
-      ->setUser($this->getRequest()->getUser())
-      ->setValidateFormPageCallback(array($this, 'validateVCSPage'))
-      ->addControl($control);
-  }
-
-  public function validateVCSPage(PHUIFormPageView $page) {
-    $valid = array(
-      PhabricatorRepositoryType::REPOSITORY_TYPE_GIT => true,
-      PhabricatorRepositoryType::REPOSITORY_TYPE_MERCURIAL => true,
-      PhabricatorRepositoryType::REPOSITORY_TYPE_SVN => true,
-    );
-
-    $c_vcs = $page->getControl('vcs');
-    $v_vcs = $c_vcs->getValue();
-    if (!$v_vcs) {
-      $c_vcs->setError(pht('Required'));
-      $page->addPageError(
-        pht('You must select a version control system.'));
-    } else if (empty($valid[$v_vcs])) {
-      $c_vcs->setError(pht('Invalid'));
-      $page->addPageError(
-        pht('You must select a valid version control system.'));
-    }
-
-    return $c_vcs->isValid();
-  }
-
-
-/* -(  Page: Name  )--------------------------------------------------------- */
-
-
-  private function buildNamePage() {
-    return id(new PHUIFormPageView())
-      ->setUser($this->getRequest()->getUser())
-      ->setPageName(pht('Repository Name and Location'))
-      ->setValidateFormPageCallback(array($this, 'validateNamePage'))
-      ->addRemarkupInstructions(
-        pht(
-          '**Choose a human-readable name for this repository**, like '.
-          '"CompanyName Mobile App" or "CompanyName Backend Server". You '.
-          'can change this later.'))
-      ->addControl(
-        id(new AphrontFormTextControl())
-          ->setName('name')
-          ->setLabel(pht('Name')));
-  }
-
-  public function validateNamePage(PHUIFormPageView $page) {
-    $c_name = $page->getControl('name');
-    $v_name = $c_name->getValue();
-    if (!strlen($v_name)) {
-      $c_name->setError(pht('Required'));
-      $page->addPageError(
-        pht('You must choose a name for this repository.'));
-    }
-
-    return $c_name->isValid();
-  }
-
-
-/* -(  Page: Remote URI  )--------------------------------------------------- */
-
-
-  private function buildRemoteURIPage() {
-    return id(new PHUIFormPageView())
-      ->setUser($this->getRequest()->getUser())
-      ->setPageName(pht('Repository Remote URI'))
-      ->setValidateFormPageCallback(array($this, 'validateRemoteURIPage'))
-      ->setAdjustFormPageCallback(array($this, 'adjustRemoteURIPage'))
-      ->addControl(
-        id(new AphrontFormTextControl())
-          ->setName('remoteURI'));
-  }
-
-  public function adjustRemoteURIPage(PHUIFormPageView $page) {
-    $form = $page->getForm();
-
-    $is_git = false;
-    $is_svn = false;
-    $is_mercurial = false;
-
-    if ($this->getRepository()) {
-      $vcs = $this->getRepository()->getVersionControlSystem();
-    } else {
-      $vcs = $form->getPage('vcs')->getControl('vcs')->getValue();
-    }
-
-    switch ($vcs) {
-      case PhabricatorRepositoryType::REPOSITORY_TYPE_GIT:
-        $is_git = true;
-        break;
-      case PhabricatorRepositoryType::REPOSITORY_TYPE_SVN:
-        $is_svn = true;
-        break;
-      case PhabricatorRepositoryType::REPOSITORY_TYPE_MERCURIAL:
-        $is_mercurial = true;
-        break;
-      default:
-        throw new Exception(pht('Unsupported VCS!'));
-    }
-
-    $has_local = ($is_git || $is_mercurial);
-    if ($is_git) {
-      $uri_label = pht('Remote URI');
-      $instructions = pht(
-        'Enter the URI to clone this Git repository from. It should usually '.
-        'look like one of these examples:'.
-        "\n\n".
-        "| Example Git Remote URIs |\n".
-        "| ----------------------- |\n".
-        "| `git@github.com:example/example.git` |\n".
-        "| `ssh://user@host.com/git/example.git` |\n".
-        "| `https://example.com/repository.git` |\n");
-    } else if ($is_mercurial) {
-      $uri_label = pht('Remote URI');
-      $instructions = pht(
-        'Enter the URI to clone this Mercurial repository from. It should '.
-        'usually look like one of these examples:'.
-        "\n\n".
-        "| Example Mercurial Remote URIs |\n".
-        "| ----------------------- |\n".
-        "| `ssh://hg@bitbucket.org/example/repository` |\n".
-        "| `https://bitbucket.org/example/repository` |\n");
-    } else if ($is_svn) {
-      $uri_label = pht('Repository Root');
-      $instructions = pht(
-        'Enter the **Repository Root** for this Subversion repository. '.
-        'You can figure this out by running `svn info` in a working copy '.
-        'and looking at the value in the `Repository Root` field. It '.
-        'should be a URI and will usually look like these:'.
-        "\n\n".
-        "| Example Subversion Repository Root URIs |\n".
-        "| ------------------------------ |\n".
-        "| `http://svn.example.org/svnroot/` |\n".
-        "| `svn+ssh://svn.example.com/svnroot/` |\n".
-        "| `svn://svn.example.net/svnroot/` |\n".
-        "\n\n".
-        "You **MUST** specify the root of the repository, not a ".
-        "subdirectory. (If you want to import only part of a Subversion ".
-        "repository, use the //Import Only// option at the end of this ".
-        "workflow.)");
-    } else {
-      throw new Exception(pht('Unsupported VCS!'));
-    }
-
-    $page->addRemarkupInstructions($instructions, 'remoteURI');
-    $page->getControl('remoteURI')->setLabel($uri_label);
-  }
-
-  public function validateRemoteURIPage(PHUIFormPageView $page) {
-    $c_remote = $page->getControl('remoteURI');
-    $v_remote = $c_remote->getValue();
-
-    if (!strlen($v_remote)) {
-      $c_remote->setError(pht('Required'));
-      $page->addPageError(
-        pht('You must specify a URI.'));
-    } else {
-      try {
-        PhabricatorRepository::assertValidRemoteURI($v_remote);
-      } catch (Exception $ex) {
-        $c_remote->setError(pht('Invalid'));
-        $page->addPageError($ex->getMessage());
-      }
-    }
-
-    return $c_remote->isValid();
-  }
-
-
-/* -(  Page: Authentication  )----------------------------------------------- */
-
-
-  public function buildAuthPage() {
-    return id(new PHUIFormPageView())
-      ->setPageName(pht('Authentication'))
-      ->setUser($this->getRequest()->getUser())
-      ->setAdjustFormPageCallback(array($this, 'adjustAuthPage'))
-      ->addControl(
-        id(new PassphraseCredentialControl())
-          ->setName('credential'));
-  }
-
-
-  public function adjustAuthPage($page) {
-    $form = $page->getForm();
-
-    if ($this->getRepository()) {
-      $vcs = $this->getRepository()->getVersionControlSystem();
-    } else {
-      $vcs = $form->getPage('vcs')->getControl('vcs')->getValue();
-    }
-
-    $remote_uri = $form->getPage('remote-uri')
-      ->getControl('remoteURI')
-      ->getValue();
-
-    $proto = PhabricatorRepository::getRemoteURIProtocol($remote_uri);
-    $remote_user = $this->getRemoteURIUser($remote_uri);
-
-    $c_credential = $page->getControl('credential');
-    $c_credential->setDefaultUsername($remote_user);
-
-    if ($this->isSSHProtocol($proto)) {
-      $c_credential->setLabel(pht('SSH Key'));
-      $c_credential->setCredentialType(
-        PassphraseSSHPrivateKeyTextCredentialType::CREDENTIAL_TYPE);
-      $provides_type = PassphraseSSHPrivateKeyCredentialType::PROVIDES_TYPE;
-
-      $page->addRemarkupInstructions(
-        pht(
-          'Choose or add the SSH credentials to use to connect to the '.
-          'repository hosted at:'.
-          "\n\n".
-          "  lang=text\n".
-          "  %s",
-          $remote_uri),
-        'credential');
-    } else if ($this->isUsernamePasswordProtocol($proto)) {
-      $c_credential->setLabel(pht('Password'));
-      $c_credential->setAllowNull(true);
-      $c_credential->setCredentialType(
-        PassphrasePasswordCredentialType::CREDENTIAL_TYPE);
-      $provides_type = PassphrasePasswordCredentialType::PROVIDES_TYPE;
-
-      $page->addRemarkupInstructions(
-        pht(
-          'Choose the username and password used to connect to the '.
-          'repository hosted at:'.
-          "\n\n".
-          "  lang=text\n".
-          "  %s".
-          "\n\n".
-          "If this repository does not require a username or password, ".
-          "you can continue to the next step.",
-          $remote_uri),
-        'credential');
-    } else {
-      throw new Exception(pht('Unknown URI protocol!'));
-    }
-
-    if ($provides_type) {
-      $viewer = $this->getRequest()->getUser();
-
-      $options = id(new PassphraseCredentialQuery())
-        ->setViewer($viewer)
-        ->withIsDestroyed(false)
-        ->withProvidesTypes(array($provides_type))
-        ->execute();
-
-      $c_credential->setOptions($options);
-    }
-
-  }
-
-  public function validateAuthPage(PHUIFormPageView $page) {
-    $form = $page->getForm();
-    $remote_uri = $form->getPage('remote')->getControl('remoteURI')->getValue();
-    $proto = $this->getRemoteURIProtocol($remote_uri);
-
-    $c_credential = $page->getControl('credential');
-    $v_credential = $c_credential->getValue();
-
-    // NOTE: We're using the omnipotent user here because the viewer might be
-    // editing a repository they're allowed to edit which uses a credential they
-    // are not allowed to see. This is fine, as long as they don't change it.
-    $credential = id(new PassphraseCredentialQuery())
-      ->setViewer(PhabricatorUser::getOmnipotentUser())
-      ->withPHIDs(array($v_credential))
-      ->executeOne();
-
-    if ($this->isSSHProtocol($proto)) {
-      if (!$credential) {
-        $c_credential->setError(pht('Required'));
-        $page->addPageError(
-          pht('You must choose an SSH credential to connect over SSH.'));
-      }
-
-      $ssh_type = PassphraseSSHPrivateKeyCredentialType::PROVIDES_TYPE;
-      if ($credential->getProvidesType() !== $ssh_type) {
-        $c_credential->setError(pht('Invalid'));
-        $page->addPageError(
-          pht(
-            'You must choose an SSH credential, not some other type '.
-            'of credential.'));
-      }
-
-    } else if ($this->isUsernamePasswordProtocol($proto)) {
-      if ($credential) {
-        $password_type = PassphrasePasswordCredentialType::PROVIDES_TYPE;
-        if ($credential->getProvidesType() !== $password_type) {
-        $c_credential->setError(pht('Invalid'));
-        $page->addPageError(
-          pht(
-            'You must choose a username/password credential, not some other '.
-            'type of credential.'));
-        }
-      }
-
-      return $c_credential->isValid();
-    } else {
-      return true;
-    }
-  }
-
-
-/* -(  Page: Policy  )------------------------------------------------------- */
-
-
-  private function buildPolicyPage() {
-    $viewer = $this->getRequest()->getUser();
-    if ($this->getRepository()) {
-      $repository = $this->getRepository();
-    } else {
-      $repository = PhabricatorRepository::initializeNewRepository($viewer);
-    }
-
-    $policies = id(new PhabricatorPolicyQuery())
-      ->setViewer($viewer)
-      ->setObject($repository)
-      ->execute();
-
-    $view_policy = id(new AphrontFormPolicyControl())
-      ->setUser($viewer)
-      ->setCapability(PhabricatorPolicyCapability::CAN_VIEW)
-      ->setPolicyObject($repository)
-      ->setPolicies($policies)
-      ->setName('viewPolicy');
-
-    $edit_policy = id(new AphrontFormPolicyControl())
-      ->setUser($viewer)
-      ->setCapability(PhabricatorPolicyCapability::CAN_EDIT)
-      ->setPolicyObject($repository)
-      ->setPolicies($policies)
-      ->setName('editPolicy');
-
-    $push_policy = id(new AphrontFormPolicyControl())
-      ->setUser($viewer)
-      ->setCapability(DiffusionPushCapability::CAPABILITY)
-      ->setPolicyObject($repository)
-      ->setPolicies($policies)
-      ->setName('pushPolicy');
-
-    return id(new PHUIFormPageView())
-      ->setPageName(pht('Policies'))
-      ->setValidateFormPageCallback(array($this, 'validatePolicyPage'))
-      ->setAdjustFormPageCallback(array($this, 'adjustPolicyPage'))
-      ->setUser($viewer)
-      ->addRemarkupInstructions(
-        pht('Select access policies for this repository.'))
-      ->addControl($view_policy)
-      ->addControl($edit_policy)
-      ->addControl($push_policy);
-  }
-
-  public function adjustPolicyPage(PHUIFormPageView $page) {
-    if ($this->getRepository()) {
-      $repository = $this->getRepository();
-      $show_push = $repository->isHosted();
-    } else {
-      $show_push = ($this->edit == 'create');
-    }
-
-    if (!$show_push) {
-      $c_push = $page->getControl('pushPolicy');
-      $c_push->setHidden(true);
-    }
-  }
-
-  public function validatePolicyPage(PHUIFormPageView $page) {
-    $form = $page->getForm();
-    $viewer = $this->getRequest()->getUser();
-
-    $c_view = $page->getControl('viewPolicy');
-    $c_edit = $page->getControl('editPolicy');
-    $c_push = $page->getControl('pushPolicy');
-    $v_view = $c_view->getValue();
-    $v_edit = $c_edit->getValue();
-    $v_push = $c_push->getValue();
-
-    if ($this->getRepository()) {
-      $repository = $this->getRepository();
-    } else {
-      $repository = PhabricatorRepository::initializeNewRepository($viewer);
-    }
-
-    $proxy = clone $repository;
-    $proxy->setViewPolicy($v_view);
-    $proxy->setEditPolicy($v_edit);
-
-    $can_view = PhabricatorPolicyFilter::hasCapability(
-      $viewer,
-      $proxy,
-      PhabricatorPolicyCapability::CAN_VIEW);
-
-    $can_edit = PhabricatorPolicyFilter::hasCapability(
-      $viewer,
-      $proxy,
-      PhabricatorPolicyCapability::CAN_EDIT);
-
-    if (!$can_view) {
-      $c_view->setError(pht('Invalid'));
-      $page->addPageError(
-        pht(
-          'You can not use the selected policy, because you would be unable '.
-          'to see the repository.'));
-    }
-
-    if (!$can_edit) {
-      $c_edit->setError(pht('Invalid'));
-      $page->addPageError(
-        pht(
-          'You can not use the selected edit policy, because you would be '.
-          'unable to edit the repository.'));
-    }
-
-    return $c_view->isValid() &&
-           $c_edit->isValid();
-  }
-
-/* -(  Page: Done  )--------------------------------------------------------- */
-
-
-  private function buildDonePage() {
-
-    $is_create = ($this->edit == 'create');
-    if ($is_create) {
-      $now_label = pht('Create Repository Now');
-      $now_caption = pht(
-        'Create the repository right away. This will create the repository '.
-        'using default settings.');
-
-      $wait_label = pht('Configure More Options First');
-      $wait_caption = pht(
-        'Configure more options before creating the repository. '.
-        'This will let you fine-tune settings. You can create the repository '.
-        'whenever you are ready.');
-    } else {
-      $now_label = pht('Start Import Now');
-      $now_caption = pht(
-        'Start importing the repository right away. This will import '.
-        'the entire repository using default settings.');
-
-      $wait_label = pht('Configure More Options First');
-      $wait_caption = pht(
-        'Configure more options before beginning the repository '.
-        'import. This will let you fine-tune settings. You can '.
-        'start the import whenever you are ready.');
-    }
-
-    return id(new PHUIFormPageView())
-      ->setPageName(pht('Repository Ready!'))
-      ->setValidateFormPageCallback(array($this, 'validateDonePage'))
-      ->setUser($this->getRequest()->getUser())
-      ->addControl(
-        id(new AphrontFormRadioButtonControl())
-          ->setName('activate')
-          ->setLabel(pht('Start Now'))
-          ->addButton(
-            'start',
-            $now_label,
-            $now_caption)
-          ->addButton(
-            'wait',
-            $wait_label,
-            $wait_caption));
-  }
-
-  public function validateDonePage(PHUIFormPageView $page) {
-    $c_activate = $page->getControl('activate');
-    $v_activate = $c_activate->getValue();
-
-    if ($v_activate != 'start' && $v_activate != 'wait') {
-      $c_activate->setError(pht('Required'));
-      $page->addPageError(
-        pht('Make a choice about repository activation.'));
-    }
-
-    return $c_activate->isValid();
-  }
-
-
-/* -(  Internal  )----------------------------------------------------------- */
-
-  private function getRemoteURIUser($raw_uri) {
-    $uri = new PhutilURI($raw_uri);
-    if ($uri->getUser()) {
-      return $uri->getUser();
-    }
-
-    $git_uri = new PhutilGitURI($raw_uri);
-    if (strlen($git_uri->getDomain()) && strlen($git_uri->getPath())) {
-      return $git_uri->getUser();
-    }
-
-    return null;
-  }
-
-  private function isSSHProtocol($proto) {
-    return ($proto == 'git' || $proto == 'ssh' || $proto == 'svn+ssh');
-  }
-
-  private function isUsernamePasswordProtocol($proto) {
-    return ($proto == 'http' || $proto == 'https' || $proto == 'svn');
-  }
-
-  private function setRepository(PhabricatorRepository $repository) {
-    $this->repository = $repository;
-    return $this;
-  }
-
-  private function getRepository() {
-    return $this->repository;
-  }
-
-}
diff --git a/src/applications/diffusion/controller/DiffusionRepositoryEditActionsController.php b/src/applications/diffusion/controller/DiffusionRepositoryEditActionsController.php
deleted file mode 100644
index b8f95f36f..000000000
--- a/src/applications/diffusion/controller/DiffusionRepositoryEditActionsController.php
+++ /dev/null
@@ -1,121 +0,0 @@
-<?php
-
-final class DiffusionRepositoryEditActionsController
-  extends DiffusionRepositoryEditController {
-
-  public function handleRequest(AphrontRequest $request) {
-    $response = $this->loadDiffusionContextForEdit();
-    if ($response) {
-      return $response;
-    }
-
-    $viewer = $this->getViewer();
-    $drequest = $this->getDiffusionRequest();
-    $repository = $drequest->getRepository();
-
-    $edit_uri = $this->getRepositoryControllerURI($repository, 'edit/');
-
-    // NOTE: We're inverting these here, because the storage is silly.
-    $v_notify = !$repository->getHumanReadableDetail('herald-disabled');
-    $v_autoclose = !$repository->getHumanReadableDetail('disable-autoclose');
-
-    if ($request->isFormPost()) {
-      $v_notify = $request->getBool('notify');
-      $v_autoclose = $request->getBool('autoclose');
-
-      $xactions = array();
-      $template = id(new PhabricatorRepositoryTransaction());
-
-      $type_notify = PhabricatorRepositoryTransaction::TYPE_NOTIFY;
-      $type_autoclose = PhabricatorRepositoryTransaction::TYPE_AUTOCLOSE;
-
-      $xactions[] = id(clone $template)
-        ->setTransactionType($type_notify)
-        ->setNewValue($v_notify);
-
-      $xactions[] = id(clone $template)
-        ->setTransactionType($type_autoclose)
-        ->setNewValue($v_autoclose);
-
-      id(new PhabricatorRepositoryEditor())
-        ->setContinueOnNoEffect(true)
-        ->setContentSourceFromRequest($request)
-        ->setActor($viewer)
-        ->applyTransactions($repository, $xactions);
-
-      return id(new AphrontRedirectResponse())->setURI($edit_uri);
-    }
-
-    $content = array();
-
-    $crumbs = $this->buildApplicationCrumbs();
-    $crumbs->addTextCrumb(pht('Edit Actions'));
-
-    $title = pht('Edit Actions (%s)', $repository->getName());
-
-    $header = id(new PHUIHeaderView())
-      ->setHeader($title)
-      ->setHeaderIcon('fa-pencil');
-
-    $policies = id(new PhabricatorPolicyQuery())
-      ->setViewer($viewer)
-      ->setObject($repository)
-      ->execute();
-
-    $form = id(new AphrontFormView())
-      ->setUser($viewer)
-      ->appendRemarkupInstructions(
-        pht(
-          "Normally, Phabricator publishes notifications when it discovers ".
-          "new commits. You can disable publishing for this repository by ".
-          "turning off **Notify/Publish**. This will disable notifications, ".
-          "feed, and Herald (including audits and build plans) for this ".
-          "repository.\n\n".
-          "When Phabricator discovers a new commit, it can automatically ".
-          "close associated revisions and tasks. If you don't want ".
-          "Phabricator to close objects when it discovers new commits in ".
-          "this repository, you can disable **Autoclose**."))
-      ->appendChild(
-        id(new AphrontFormSelectControl())
-          ->setName('notify')
-          ->setLabel(pht('Notify/Publish'))
-          ->setValue((int)$v_notify)
-          ->setOptions(
-            array(
-              1 => pht('Enable Notifications, Feed and Herald'),
-              0 => pht('Disable Notifications, Feed and Herald'),
-            )))
-      ->appendChild(
-        id(new AphrontFormSelectControl())
-          ->setName('autoclose')
-          ->setLabel(pht('Autoclose'))
-          ->setValue((int)$v_autoclose)
-          ->setOptions(
-            array(
-              1 => pht('Enable Autoclose'),
-              0 => pht('Disable Autoclose'),
-            )))
-      ->appendChild(
-        id(new AphrontFormSubmitControl())
-          ->setValue(pht('Save Actions'))
-          ->addCancelButton($edit_uri));
-
-    $form_box = id(new PHUIObjectBoxView())
-      ->setHeaderText(pht('Actions'))
-      ->setBackground(PHUIObjectBoxView::BLUE_PROPERTY)
-      ->setForm($form);
-
-    $view = id(new PHUITwoColumnView())
-      ->setHeader($header)
-      ->setFooter(array(
-        $form_box,
-      ));
-
-    return $this->newPage()
-      ->setTitle($title)
-      ->setCrumbs($crumbs)
-      ->appendChild($view);
-
-  }
-
-}
diff --git a/src/applications/diffusion/controller/DiffusionRepositoryEditActivateController.php b/src/applications/diffusion/controller/DiffusionRepositoryEditActivateController.php
index 55caaa59b..b383333fb 100644
--- a/src/applications/diffusion/controller/DiffusionRepositoryEditActivateController.php
+++ b/src/applications/diffusion/controller/DiffusionRepositoryEditActivateController.php
@@ -1,56 +1,78 @@
 <?php
 
 final class DiffusionRepositoryEditActivateController
-  extends DiffusionRepositoryEditController {
+  extends DiffusionRepositoryManageController {
 
   public function handleRequest(AphrontRequest $request) {
     $response = $this->loadDiffusionContextForEdit();
     if ($response) {
       return $response;
     }
 
     $viewer = $this->getViewer();
     $drequest = $this->getDiffusionRequest();
     $repository = $drequest->getRepository();
 
-    $edit_uri = $this->getRepositoryControllerURI($repository, 'edit/');
+    $panel_uri = id(new DiffusionRepositoryBasicsManagementPanel())
+      ->setRepository($repository)
+      ->getPanelURI();
 
     if ($request->isFormPost()) {
       if (!$repository->isTracked()) {
         $new_status = PhabricatorRepository::STATUS_ACTIVE;
       } else {
         $new_status = PhabricatorRepository::STATUS_INACTIVE;
       }
 
       $xaction = id(new PhabricatorRepositoryTransaction())
         ->setTransactionType(PhabricatorRepositoryTransaction::TYPE_ACTIVATE)
         ->setNewValue($new_status);
 
       $editor = id(new PhabricatorRepositoryEditor())
         ->setContinueOnNoEffect(true)
         ->setContinueOnMissingFields(true)
         ->setContentSourceFromRequest($request)
         ->setActor($viewer)
         ->applyTransactions($repository, array($xaction));
 
-      return id(new AphrontReloadResponse())->setURI($edit_uri);
+      return id(new AphrontReloadResponse())->setURI($panel_uri);
     }
 
     if ($repository->isTracked()) {
-      return $this->newDialog()
-        ->setTitle(pht('Deactivate Repository?'))
-        ->appendChild(
-          pht('Deactivate this repository?'))
-        ->addSubmitButton(pht('Deactivate Repository'))
-        ->addCancelButton($edit_uri);
+      $title = pht('Deactivate Repository');
+      $body = pht(
+        'If you deactivate this repository, it will no longer be updated. '.
+        'Observation and mirroring will cease, and pushing and pulling will '.
+        'be disabled. You can reactivate the repository later.');
+      $submit = pht('Deactivate Repository');
     } else {
-      return $this->newDialog()
-        ->setTitle(pht('Activate Repository?'))
-        ->appendChild(
-          pht('Activate this repository?'))
-        ->addSubmitButton(pht('Activate Repository'))
-        ->addCancelButton($edit_uri);
+      $title = pht('Activate Repository');
+
+      $is_new = $repository->isNewlyInitialized();
+      if ($is_new) {
+        if ($repository->isHosted()) {
+          $body = pht(
+            'This repository will become a new hosted repository. '.
+            'It will begin serving read and write traffic.');
+        } else {
+          $body = pht(
+            'This repository will observe an existing remote repository. '.
+            'It will begin fetching changes from the remote.');
+        }
+      } else {
+        $body = pht(
+          'This repository will resume updates, observation, mirroring, '.
+          'and serving any configured read and write traffic.');
+      }
+
+      $submit = pht('Activate Repository');
     }
+
+    return $this->newDialog()
+      ->setTitle($title)
+      ->appendChild($body)
+      ->addSubmitButton($submit)
+      ->addCancelButton($panel_uri);
   }
 
 }
diff --git a/src/applications/diffusion/controller/DiffusionRepositoryEditAutomationController.php b/src/applications/diffusion/controller/DiffusionRepositoryEditAutomationController.php
deleted file mode 100644
index 038cc0f0c..000000000
--- a/src/applications/diffusion/controller/DiffusionRepositoryEditAutomationController.php
+++ /dev/null
@@ -1,93 +0,0 @@
-<?php
-
-final class DiffusionRepositoryEditAutomationController
-  extends DiffusionRepositoryEditController {
-
-  public function handleRequest(AphrontRequest $request) {
-    $response = $this->loadDiffusionContextForEdit();
-    if ($response) {
-      return $response;
-    }
-
-    $viewer = $this->getViewer();
-    $drequest = $this->getDiffusionRequest();
-    $repository = $drequest->getRepository();
-
-    if (!$repository->supportsAutomation()) {
-      return new Aphront404Response();
-    }
-
-    $edit_uri = $this->getRepositoryControllerURI($repository, 'edit/');
-
-    $v_blueprints = $repository->getHumanReadableDetail(
-      'automation.blueprintPHIDs');
-
-    if ($request->isFormPost()) {
-      $v_blueprints = $request->getArr('blueprintPHIDs');
-
-      $xactions = array();
-      $template = id(new PhabricatorRepositoryTransaction());
-
-      $type_blueprints =
-        PhabricatorRepositoryTransaction::TYPE_AUTOMATION_BLUEPRINTS;
-
-      $xactions[] = id(clone $template)
-        ->setTransactionType($type_blueprints)
-        ->setNewValue($v_blueprints);
-
-      id(new PhabricatorRepositoryEditor())
-        ->setContinueOnNoEffect(true)
-        ->setContentSourceFromRequest($request)
-        ->setActor($viewer)
-        ->applyTransactions($repository, $xactions);
-
-      return id(new AphrontRedirectResponse())->setURI($edit_uri);
-    }
-
-    $crumbs = $this->buildApplicationCrumbs();
-    $crumbs->addTextCrumb(pht('Edit Automation'));
-
-    $title = pht('Edit %s', $repository->getName());
-
-    $header = id(new PHUIHeaderView())
-      ->setHeader($title)
-      ->setHeaderIcon('fa-pencil');
-
-    $form = id(new AphrontFormView())
-      ->setUser($viewer)
-      ->appendRemarkupInstructions(
-        pht(
-          "Configure **Repository Automation** to allow Phabricator to ".
-          "write to this repository.".
-          "\n\n".
-          "IMPORTANT: This feature is new, experimental, and not supported. ".
-          "Use it at your own risk."))
-      ->appendControl(
-        id(new AphrontFormTokenizerControl())
-          ->setLabel(pht('Use Blueprints'))
-          ->setName('blueprintPHIDs')
-          ->setValue($v_blueprints)
-          ->setDatasource(new DrydockBlueprintDatasource()))
-      ->appendChild(
-        id(new AphrontFormSubmitControl())
-          ->setValue(pht('Save'))
-          ->addCancelButton($edit_uri));
-
-    $form_box = id(new PHUIObjectBoxView())
-      ->setHeaderText(pht('Automation'))
-      ->setBackground(PHUIObjectBoxView::BLUE_PROPERTY)
-      ->setForm($form);
-
-    $view = id(new PHUITwoColumnView())
-      ->setHeader($header)
-      ->setFooter(array(
-        $form_box,
-      ));
-
-    return $this->newPage()
-      ->setTitle($title)
-      ->setCrumbs($crumbs)
-      ->appendChild($view);
-  }
-
-}
diff --git a/src/applications/diffusion/controller/DiffusionRepositoryEditBasicController.php b/src/applications/diffusion/controller/DiffusionRepositoryEditBasicController.php
deleted file mode 100644
index 10f4b13ef..000000000
--- a/src/applications/diffusion/controller/DiffusionRepositoryEditBasicController.php
+++ /dev/null
@@ -1,193 +0,0 @@
-<?php
-
-final class DiffusionRepositoryEditBasicController
-  extends DiffusionRepositoryEditController {
-
-  public function handleRequest(AphrontRequest $request) {
-    $response = $this->loadDiffusionContextForEdit();
-    if ($response) {
-      return $response;
-    }
-
-    $viewer = $request->getUser();
-    $drequest = $this->getDiffusionRequest();
-    $repository = $drequest->getRepository();
-
-    $edit_uri = $this->getRepositoryControllerURI($repository, 'edit/');
-
-    $v_name = $repository->getName();
-    $v_desc = $repository->getDetail('description');
-    $v_slug = $repository->getRepositorySlug();
-    $v_callsign = $repository->getCallsign();
-    $v_projects = PhabricatorEdgeQuery::loadDestinationPHIDs(
-      $repository->getPHID(),
-      PhabricatorProjectObjectHasProjectEdgeType::EDGECONST);
-    $e_name = true;
-    $e_slug = null;
-    $e_callsign = null;
-    $errors = array();
-
-    $validation_exception = null;
-    if ($request->isFormPost()) {
-      $v_name = $request->getStr('name');
-      $v_desc = $request->getStr('description');
-      $v_projects = $request->getArr('projectPHIDs');
-      $v_slug = $request->getStr('slug');
-      $v_callsign = $request->getStr('callsign');
-
-      if (!strlen($v_name)) {
-        $e_name = pht('Required');
-        $errors[] = pht('Repository name is required.');
-      } else {
-        $e_name = null;
-      }
-
-      if (!$errors) {
-        $xactions = array();
-        $template = id(new PhabricatorRepositoryTransaction());
-
-        $type_name = PhabricatorRepositoryTransaction::TYPE_NAME;
-        $type_desc = PhabricatorRepositoryTransaction::TYPE_DESCRIPTION;
-        $type_edge = PhabricatorTransactions::TYPE_EDGE;
-        $type_slug = PhabricatorRepositoryTransaction::TYPE_SLUG;
-        $type_callsign = PhabricatorRepositoryTransaction::TYPE_CALLSIGN;
-
-        $xactions[] = id(clone $template)
-          ->setTransactionType($type_name)
-          ->setNewValue($v_name);
-
-        $xactions[] = id(clone $template)
-          ->setTransactionType($type_desc)
-          ->setNewValue($v_desc);
-
-        $xactions[] = id(clone $template)
-          ->setTransactionType($type_slug)
-          ->setNewValue($v_slug);
-
-        $xactions[] = id(clone $template)
-          ->setTransactionType($type_callsign)
-          ->setNewValue($v_callsign);
-
-        $xactions[] = id(clone $template)
-          ->setTransactionType($type_edge)
-          ->setMetadataValue(
-            'edge:type',
-            PhabricatorProjectObjectHasProjectEdgeType::EDGECONST)
-          ->setNewValue(
-            array(
-              '=' => array_fuse($v_projects),
-            ));
-
-        $editor = id(new PhabricatorRepositoryEditor())
-          ->setContinueOnNoEffect(true)
-          ->setContentSourceFromRequest($request)
-          ->setActor($viewer);
-
-        try {
-          $editor->applyTransactions($repository, $xactions);
-
-          // The preferred edit URI may have changed if the callsign or slug
-          // were adjusted, so grab a fresh copy.
-          $edit_uri = $this->getRepositoryControllerURI($repository, 'edit/');
-
-          return id(new AphrontRedirectResponse())->setURI($edit_uri);
-        } catch (PhabricatorApplicationTransactionValidationException $ex) {
-          $validation_exception = $ex;
-
-          $e_slug = $ex->getShortMessage($type_slug);
-          $e_callsign = $ex->getShortMessage($type_callsign);
-        }
-      }
-    }
-
-    $crumbs = $this->buildApplicationCrumbs();
-    $crumbs->addTextCrumb(pht('Edit Basics'));
-
-    $title = pht('Edit %s', $repository->getName());
-
-    $header = id(new PHUIHeaderView())
-      ->setHeader($title)
-      ->setHeaderIcon('fa-pencil');
-
-    $form = id(new AphrontFormView())
-      ->setUser($viewer)
-      ->appendChild(
-        id(new AphrontFormTextControl())
-          ->setName('name')
-          ->setLabel(pht('Name'))
-          ->setValue($v_name)
-          ->setError($e_name))
-      ->appendChild(
-        id(new AphrontFormTextControl())
-          ->setName('slug')
-          ->setLabel(pht('Short Name'))
-          ->setValue($v_slug)
-          ->setError($e_slug))
-      ->appendChild(
-        id(new AphrontFormTextControl())
-          ->setName('callsign')
-          ->setLabel(pht('Callsign'))
-          ->setValue($v_callsign)
-          ->setError($e_callsign))
-      ->appendChild(
-        id(new PhabricatorRemarkupControl())
-          ->setUser($viewer)
-          ->setName('description')
-          ->setLabel(pht('Description'))
-          ->setValue($v_desc))
-      ->appendControl(
-        id(new AphrontFormTokenizerControl())
-          ->setDatasource(new PhabricatorProjectDatasource())
-          ->setName('projectPHIDs')
-          ->setLabel(pht('Projects'))
-          ->setValue($v_projects))
-      ->appendChild(
-        id(new AphrontFormSubmitControl())
-          ->setValue(pht('Save'))
-          ->addCancelButton($edit_uri))
-      ->appendChild(id(new PHUIFormDividerControl()))
-      ->appendRemarkupInstructions($this->getReadmeInstructions());
-
-    $form_box = id(new PHUIObjectBoxView())
-      ->setHeaderText(pht('Basic Information'))
-      ->setValidationException($validation_exception)
-      ->setBackground(PHUIObjectBoxView::BLUE_PROPERTY)
-      ->setForm($form)
-      ->setFormErrors($errors);
-
-    $view = id(new PHUITwoColumnView())
-      ->setHeader($header)
-      ->setFooter(array(
-        $form_box,
-      ));
-
-    return $this->newPage()
-      ->setTitle($title)
-      ->setCrumbs($crumbs)
-      ->appendChild($view);
-  }
-
-  private function getReadmeInstructions() {
-    return pht(<<<EOTEXT
-You can also create a `%s` file at the repository root (or in any
-subdirectory) to provide information about the repository. These formats are
-supported:
-
-| File Name | Rendered As...  |
-|-----------|-----------------|
-| `%s`      | Plain Text      |
-| `%s`      | Plain Text      |
-| `%s`      | Remarkup        |
-| `%s`      | Remarkup        |
-| `%s`      | \xC2\xA1Fiesta! |
-EOTEXT
-  ,
-  'README',
-  'README',
-  'README.txt',
-  'README.remarkup',
-  'README.md',
-  'README.rainbow');
-  }
-
-}
diff --git a/src/applications/diffusion/controller/DiffusionRepositoryEditBranchesController.php b/src/applications/diffusion/controller/DiffusionRepositoryEditBranchesController.php
deleted file mode 100644
index 927af295d..000000000
--- a/src/applications/diffusion/controller/DiffusionRepositoryEditBranchesController.php
+++ /dev/null
@@ -1,248 +0,0 @@
-<?php
-
-final class DiffusionRepositoryEditBranchesController
-  extends DiffusionRepositoryEditController {
-
-  public function handleRequest(AphrontRequest $request) {
-    $response = $this->loadDiffusionContextForEdit();
-    if ($response) {
-      return $response;
-    }
-
-    $viewer = $this->getViewer();
-    $drequest = $this->getDiffusionRequest();
-    $repository = $drequest->getRepository();
-
-    $is_git = false;
-    $is_hg = false;
-
-    switch ($repository->getVersionControlSystem()) {
-      case PhabricatorRepositoryType::REPOSITORY_TYPE_GIT:
-        $is_git = true;
-        break;
-      case PhabricatorRepositoryType::REPOSITORY_TYPE_MERCURIAL:
-        $is_hg = true;
-        break;
-      case PhabricatorRepositoryType::REPOSITORY_TYPE_SVN:
-        throw new Exception(
-          pht('Subversion does not support branches!'));
-      default:
-        throw new Exception(
-          pht('Repository has unknown version control system!'));
-    }
-
-    $edit_uri = $this->getRepositoryControllerURI($repository, 'edit/');
-
-    $v_default = $repository->getHumanReadableDetail('default-branch');
-    $v_track = $repository->getDetail(
-      'branch-filter',
-      array());
-    $v_track = array_keys($v_track);
-    $v_autoclose = $repository->getDetail(
-      'close-commits-filter',
-      array());
-    $v_autoclose = array_keys($v_autoclose);
-
-    $e_track = null;
-    $e_autoclose = null;
-
-    $validation_exception = null;
-    if ($request->isFormPost()) {
-      $v_default = $request->getStr('default');
-
-      $v_track = $this->processBranches($request->getStr('track'));
-      if (!$is_hg) {
-        $v_autoclose = $this->processBranches($request->getStr('autoclose'));
-      }
-
-      $xactions = array();
-      $template = id(new PhabricatorRepositoryTransaction());
-
-      $type_default = PhabricatorRepositoryTransaction::TYPE_DEFAULT_BRANCH;
-      $type_track = PhabricatorRepositoryTransaction::TYPE_TRACK_ONLY;
-      $type_autoclose = PhabricatorRepositoryTransaction::TYPE_AUTOCLOSE_ONLY;
-
-      $xactions[] = id(clone $template)
-        ->setTransactionType($type_default)
-        ->setNewValue($v_default);
-
-      $xactions[] = id(clone $template)
-        ->setTransactionType($type_track)
-        ->setNewValue($v_track);
-
-      if (!$is_hg) {
-        $xactions[] = id(clone $template)
-          ->setTransactionType($type_autoclose)
-          ->setNewValue($v_autoclose);
-      }
-
-      $editor = id(new PhabricatorRepositoryEditor())
-        ->setContinueOnNoEffect(true)
-        ->setContentSourceFromRequest($request)
-        ->setActor($viewer);
-
-      try {
-        $editor->applyTransactions($repository, $xactions);
-        return id(new AphrontRedirectResponse())->setURI($edit_uri);
-      } catch (PhabricatorApplicationTransactionValidationException $ex) {
-        $validation_exception = $ex;
-
-        $e_track = $validation_exception->getShortMessage($type_track);
-        $e_autoclose = $validation_exception->getShortMessage($type_autoclose);
-      }
-    }
-
-    $content = array();
-
-    $crumbs = $this->buildApplicationCrumbs();
-    $crumbs->addTextCrumb(pht('Edit Branches'));
-
-    $title = pht('Edit Branches (%s)', $repository->getName());
-    $header = id(new PHUIHeaderView())
-      ->setHeader($title)
-      ->setHeaderIcon('fa-pencil');
-
-    $policies = id(new PhabricatorPolicyQuery())
-      ->setViewer($viewer)
-      ->setObject($repository)
-      ->execute();
-
-    $rows = array();
-    $rows[] = array(
-      array(
-        'master',
-      ),
-      pht('Select only master.'),
-    );
-    $rows[] = array(
-      array(
-        'master',
-        'develop',
-        'release',
-      ),
-      pht('Select %s, %s, and %s.', 'master', 'develop', 'release'),
-    );
-    $rows[] = array(
-      array(
-        'master',
-        'regexp(/^release-/)',
-      ),
-      pht('Select master, and all branches which start with "%s".', 'release-'),
-    );
-    $rows[] = array(
-      array(
-        'regexp(/^(?!temp-)/)',
-      ),
-      pht('Select all branches which do not start with "%s".', 'temp-'),
-    );
-
-    foreach ($rows as $k => $row) {
-      $rows[$k][0] = phutil_tag(
-        'pre',
-        array(),
-        implode("\n", $row[0]));
-    }
-
-    $example_table = id(new AphrontTableView($rows))
-      ->setHeaders(
-        array(
-          pht('Example'),
-          pht('Effect'),
-        ))
-      ->setColumnClasses(
-        array(
-          '',
-          'wide',
-        ));
-
-    $v_track = implode("\n", $v_track);
-    $v_autoclose = implode("\n", $v_autoclose);
-
-    $form = id(new AphrontFormView())
-      ->setUser($viewer)
-      ->appendRemarkupInstructions(
-        pht('You can choose a **Default Branch** for viewing this repository.'))
-      ->appendChild(
-        id(new AphrontFormTextControl())
-          ->setName('default')
-          ->setLabel(pht('Default Branch'))
-          ->setValue($v_default))
-      ->appendRemarkupInstructions(
-        pht(
-          'If you want to import only some branches into Diffusion, you can '.
-          'list them in **Track Only**. Other branches will be ignored. If '.
-          'you do not specify any branches, all branches are tracked.'));
-
-    if (!$is_hg) {
-      $form->appendRemarkupInstructions(
-        pht(
-          'If you have **Autoclose** enabled for this repository, Phabricator '.
-          'can close tasks and revisions when corresponding commits are '.
-          'pushed to the repository. If you want to autoclose objects only '.
-          'when commits appear on specific branches, you can list those '.
-          'branches in **Autoclose Only**. By default, all tracked branches '.
-          'will autoclose objects.'));
-    }
-
-    $form
-      ->appendRemarkupInstructions(
-        pht(
-          'When specifying branches, you should enter one branch name per '.
-          'line. You can use regular expressions to match branches by '.
-          'wrapping an expression in `%s`. For example:',
-          'regexp(...)'))
-      ->appendChild(
-        id(new AphrontFormMarkupControl())
-          ->setValue($example_table))
-      ->appendChild(
-        id(new AphrontFormTextAreaControl())
-          ->setName('track')
-          ->setLabel(pht('Track Only'))
-          ->setError($e_track)
-          ->setValue($v_track));
-
-    if (!$is_hg) {
-      $form->appendChild(
-        id(new AphrontFormTextAreaControl())
-          ->setName('autoclose')
-          ->setLabel(pht('Autoclose Only'))
-          ->setError($e_autoclose)
-          ->setValue($v_autoclose));
-    }
-
-    $form->appendChild(
-      id(new AphrontFormSubmitControl())
-        ->setValue(pht('Save Branches'))
-        ->addCancelButton($edit_uri));
-
-    $form_box = id(new PHUIObjectBoxView())
-      ->setHeaderText(pht('Branches'))
-      ->setValidationException($validation_exception)
-      ->setBackground(PHUIObjectBoxView::BLUE_PROPERTY)
-      ->setForm($form);
-
-    $view = id(new PHUITwoColumnView())
-      ->setHeader($header)
-      ->setFooter(array(
-        $form_box,
-      ));
-
-    return $this->newPage()
-      ->setTitle($title)
-      ->setCrumbs($crumbs)
-      ->appendChild($view);
-  }
-
-  private function processBranches($string) {
-    $lines = phutil_split_lines($string, $retain_endings = false);
-    foreach ($lines as $key => $line) {
-      $lines[$key] = trim($line);
-      if (!strlen($lines[$key])) {
-        unset($lines[$key]);
-      }
-    }
-
-    return array_values($lines);
-  }
-
-}
diff --git a/src/applications/diffusion/controller/DiffusionRepositoryEditController.php b/src/applications/diffusion/controller/DiffusionRepositoryEditController.php
index 714568f18..b8892a94e 100644
--- a/src/applications/diffusion/controller/DiffusionRepositoryEditController.php
+++ b/src/applications/diffusion/controller/DiffusionRepositoryEditController.php
@@ -1,28 +1,69 @@
 <?php
 
-abstract class DiffusionRepositoryEditController
-  extends DiffusionController {
+final class DiffusionRepositoryEditController
+  extends DiffusionRepositoryManageController {
 
-  protected function buildApplicationCrumbs($is_main = false) {
-    $crumbs = parent::buildApplicationCrumbs();
+  public function handleRequest(AphrontRequest $request) {
+    $engine = id(new DiffusionRepositoryEditEngine())
+      ->setController($this);
 
-    if ($this->hasDiffusionRequest()) {
-      $drequest = $this->getDiffusionRequest();
-      $repository = $drequest->getRepository();
-      $repo_uri = $repository->getURI();
-      $edit_uri = $this->getRepositoryControllerURI($repository, 'edit/');
+    $id = $request->getURIData('id');
+    if (!$id) {
+      $this->requireApplicationCapability(
+        DiffusionCreateRepositoriesCapability::CAPABILITY);
 
-      $crumbs->addTextCrumb($repository->getDisplayname(), $repo_uri);
-
-      if ($is_main) {
-        $crumbs->addTextCrumb(pht('Edit Repository'));
-      } else {
-        $crumbs->addTextCrumb(pht('Edit'), $edit_uri);
+      $vcs = $request->getStr('vcs');
+      $vcs_types = PhabricatorRepositoryType::getRepositoryTypeMap();
+      if (empty($vcs_types[$vcs])) {
+        return $this->buildVCSTypeResponse();
       }
+
+      $engine
+        ->addContextParameter('vcs', $vcs)
+        ->setVersionControlSystem($vcs);
     }
+
+    return $engine->buildResponse();
+  }
+
+  private function buildVCSTypeResponse() {
+    $vcs_types = PhabricatorRepositoryType::getRepositoryTypeMap();
+
+    $request = $this->getRequest();
+    $viewer = $this->getViewer();
+
+    $crumbs = $this->buildApplicationCrumbs();
+    $crumbs->addTextCrumb(pht('Create Repository'));
     $crumbs->setBorder(true);
 
-    return $crumbs;
+    $title = pht('Choose Repository Type');
+    $header = id(new PHUIHeaderView())
+      ->setHeader(pht('Create Repository'))
+      ->setHeaderIcon('fa-plus-square');
+
+    $layout = id(new AphrontMultiColumnView())
+      ->setFluidLayout(true);
+
+    $create_uri = $request->getRequestURI();
+
+    foreach ($vcs_types as $vcs_key => $vcs_type) {
+      $action = id(new PHUIActionPanelView())
+        ->setIcon(idx($vcs_type, 'icon'))
+        ->setHeader(idx($vcs_type, 'create.header'))
+        ->setHref($create_uri->alter('vcs', $vcs_key))
+        ->setSubheader(idx($vcs_type, 'create.subheader'));
+
+      $layout->addColumn($action);
+    }
+
+    $view = id(new PHUITwoColumnView())
+      ->setHeader($header)
+      ->setFooter($layout);
+
+    return $this->newPage()
+      ->setTitle($title)
+      ->setCrumbs($crumbs)
+      ->appendChild($view);
   }
 
 }
diff --git a/src/applications/diffusion/controller/DiffusionRepositoryEditDangerousController.php b/src/applications/diffusion/controller/DiffusionRepositoryEditDangerousController.php
index 26d8b57f3..d0dd2cc2e 100644
--- a/src/applications/diffusion/controller/DiffusionRepositoryEditDangerousController.php
+++ b/src/applications/diffusion/controller/DiffusionRepositoryEditDangerousController.php
@@ -1,62 +1,82 @@
 <?php
 
 final class DiffusionRepositoryEditDangerousController
-  extends DiffusionRepositoryEditController {
+  extends DiffusionRepositoryManageController {
 
   public function handleRequest(AphrontRequest $request) {
     $response = $this->loadDiffusionContextForEdit();
     if ($response) {
       return $response;
     }
 
     $viewer = $this->getViewer();
     $drequest = $this->getDiffusionRequest();
     $repository = $drequest->getRepository();
 
+    $panel_uri = id(new DiffusionRepositoryBasicsManagementPanel())
+      ->setRepository($repository)
+      ->getPanelURI();
+
     if (!$repository->canAllowDangerousChanges()) {
-      return new Aphront400Response();
+      if ($repository->isSVN()) {
+        return $this->newDialog()
+          ->setTitle(pht('Not in Danger'))
+          ->appendParagraph(
+            pht(
+              'It is not possible for users to push any dangerous changes '.
+              'to a Subversion repository. Pushes to a Subversion repository '.
+              'can always be reverted and never destroy data.'))
+          ->addCancelButton($panel_uri);
+      } else {
+        return $this->newDialog()
+          ->setTitle(pht('Unprotectable Repository'))
+          ->appendParagraph(
+            pht(
+              'This repository can not be protected from dangerous changes '.
+              'because Phabricator does not control what users are allowed '.
+              'to push to it.'))
+          ->addCancelButton($panel_uri);
+      }
     }
 
-    $edit_uri = $this->getRepositoryControllerURI($repository, 'edit/');
-
     if ($request->isFormPost()) {
       $xaction = id(new PhabricatorRepositoryTransaction())
         ->setTransactionType(PhabricatorRepositoryTransaction::TYPE_DANGEROUS)
         ->setNewValue(!$repository->shouldAllowDangerousChanges());
 
       $editor = id(new PhabricatorRepositoryEditor())
         ->setContinueOnNoEffect(true)
         ->setContentSourceFromRequest($request)
         ->setActor($viewer)
         ->applyTransactions($repository, array($xaction));
 
-      return id(new AphrontReloadResponse())->setURI($edit_uri);
+      return id(new AphrontReloadResponse())->setURI($panel_uri);
     }
 
     $force = phutil_tag('tt', array(), '--force');
 
     if ($repository->shouldAllowDangerousChanges()) {
-      return $this->newDialog()
-        ->setTitle(pht('Prevent Dangerous changes?'))
-        ->appendChild(
-          pht(
-            'It will no longer be possible to delete branches from this '.
-            'repository, or %s push to this repository.',
-            $force))
-        ->addSubmitButton(pht('Prevent Dangerous Changes'))
-        ->addCancelButton($edit_uri);
+      $title = pht('Prevent Dangerous Changes');
+      $body = pht(
+        'It will no longer be possible to delete branches from this '.
+        'repository, or %s push to this repository.',
+        $force);
+      $submit = pht('Prevent Dangerous Changes');
     } else {
-      return $this->newDialog()
-        ->setTitle(pht('Allow Dangerous Changes?'))
-        ->appendChild(
-          pht(
-            'If you allow dangerous changes, it will be possible to delete '.
-            'branches and %s push this repository. These operations can '.
-            'alter a repository in a way that is difficult to recover from.',
-            $force))
-        ->addSubmitButton(pht('Allow Dangerous Changes'))
-        ->addCancelButton($edit_uri);
+      $title = pht('Allow Dangerous Changes');
+      $body = pht(
+        'If you allow dangerous changes, it will be possible to delete '.
+        'branches and %s push this repository. These operations can '.
+        'alter a repository in a way that is difficult to recover from.',
+        $force);
+      $submit = pht('Allow Dangerous Changes');
     }
+
+    return $this->newDialog()
+      ->setTitle($title)
+      ->appendParagraph($body)
+      ->addSubmitButton($submit)
+      ->addCancelButton($panel_uri);
   }
 
 }
diff --git a/src/applications/diffusion/controller/DiffusionRepositoryEditDeleteController.php b/src/applications/diffusion/controller/DiffusionRepositoryEditDeleteController.php
index 074e79445..0380b99f0 100644
--- a/src/applications/diffusion/controller/DiffusionRepositoryEditDeleteController.php
+++ b/src/applications/diffusion/controller/DiffusionRepositoryEditDeleteController.php
@@ -1,46 +1,48 @@
 <?php
 
 final class DiffusionRepositoryEditDeleteController
-  extends DiffusionRepositoryEditController {
+  extends DiffusionRepositoryManageController {
 
   public function handleRequest(AphrontRequest $request) {
     $response = $this->loadDiffusionContextForEdit();
     if ($response) {
       return $response;
     }
 
     $viewer = $this->getViewer();
     $drequest = $this->getDiffusionRequest();
     $repository = $drequest->getRepository();
 
-    $edit_uri = $this->getRepositoryControllerURI($repository, 'edit/');
+    $panel_uri = id(new DiffusionRepositoryBasicsManagementPanel())
+      ->setRepository($repository)
+      ->getPanelURI();
 
     $dialog = new AphrontDialogView();
     $text_1 = pht(
       'If you really want to delete the repository, run this command from '.
       'the command line:');
     $command = csprintf(
       'phabricator/ $ ./bin/remove destroy %R',
       $repository->getMonogram());
     $text_2 = pht(
       'Repositories touch many objects and as such deletes are '.
       'prohibitively expensive to run from the web UI.');
     $body = phutil_tag(
       'div',
       array(
         'class' => 'phabricator-remarkup',
       ),
       array(
         phutil_tag('p', array(), $text_1),
         phutil_tag('p', array(),
           phutil_tag('tt', array(), $command)),
         phutil_tag('p', array(), $text_2),
       ));
 
     return $this->newDialog()
       ->setTitle(pht('Really want to delete the repository?'))
       ->appendChild($body)
-      ->addCancelButton($edit_uri, pht('Okay'));
+      ->addCancelButton($panel_uri, pht('Okay'));
   }
 
 }
diff --git a/src/applications/diffusion/controller/DiffusionRepositoryEditEncodingController.php b/src/applications/diffusion/controller/DiffusionRepositoryEditEncodingController.php
deleted file mode 100644
index 256e69ac5..000000000
--- a/src/applications/diffusion/controller/DiffusionRepositoryEditEncodingController.php
+++ /dev/null
@@ -1,107 +0,0 @@
-<?php
-
-final class DiffusionRepositoryEditEncodingController
-  extends DiffusionRepositoryEditController {
-
-  public function handleRequest(AphrontRequest $request) {
-    $response = $this->loadDiffusionContextForEdit();
-    if ($response) {
-      return $response;
-    }
-
-    $user = $this->getViewer();
-    $drequest = $this->getDiffusionRequest();
-    $repository = $drequest->getRepository();
-
-    $edit_uri = $this->getRepositoryControllerURI($repository, 'edit/');
-
-    $v_encoding = $repository->getDetail('encoding');
-    $e_encoding = null;
-    $errors = array();
-
-    if ($request->isFormPost()) {
-      $v_encoding = $request->getStr('encoding');
-
-      if (!$errors) {
-        $xactions = array();
-        $template = id(new PhabricatorRepositoryTransaction());
-
-        $type_encoding = PhabricatorRepositoryTransaction::TYPE_ENCODING;
-
-        $xactions[] = id(clone $template)
-          ->setTransactionType($type_encoding)
-          ->setNewValue($v_encoding);
-
-        try {
-          id(new PhabricatorRepositoryEditor())
-            ->setContinueOnNoEffect(true)
-            ->setContentSourceFromRequest($request)
-            ->setActor($user)
-            ->applyTransactions($repository, $xactions);
-
-          return id(new AphrontRedirectResponse())->setURI($edit_uri);
-        } catch (Exception $ex) {
-          $errors[] = $ex->getMessage();
-        }
-      }
-    }
-
-    $crumbs = $this->buildApplicationCrumbs();
-    $crumbs->addTextCrumb(pht('Edit Encoding'));
-
-    $title = pht('Edit %s', $repository->getName());
-    $header = id(new PHUIHeaderView())
-      ->setHeader($title)
-      ->setHeaderIcon('fa-pencil');
-
-    $form = id(new AphrontFormView())
-      ->setUser($user)
-      ->appendRemarkupInstructions($this->getEncodingInstructions())
-      ->appendChild(
-        id(new AphrontFormTextControl())
-          ->setName('encoding')
-          ->setLabel(pht('Text Encoding'))
-          ->setValue($v_encoding)
-          ->setError($e_encoding))
-      ->appendChild(
-        id(new AphrontFormSubmitControl())
-          ->setValue(pht('Save Encoding'))
-          ->addCancelButton($edit_uri));
-
-    $form_box = id(new PHUIObjectBoxView())
-      ->setHeaderText(pht('Encoding'))
-      ->setBackground(PHUIObjectBoxView::BLUE_PROPERTY)
-      ->setForm($form)
-      ->setFormErrors($errors);
-
-    $view = id(new PHUITwoColumnView())
-      ->setHeader($header)
-      ->setFooter(array(
-        $form_box,
-      ));
-
-    return $this->newPage()
-      ->setTitle($title)
-      ->setCrumbs($crumbs)
-      ->appendChild($view);
-  }
-
-  private function getEncodingInstructions() {
-    return pht(<<<EOT
-If source code in this repository uses a character encoding other than UTF-8
-(for example, `ISO-8859-1`), specify it here.
-
-**Normally, you can leave this field blank.** If your source code is written in
-ASCII or UTF-8, everything will work correctly.
-
-Source files will be translated from the specified encoding to UTF-8 when they
-are read from the repository, before they are displayed in Diffusion.
-
-See [[%s | UTF-8 and Character Encoding]] for more information on how
-Phabricator handles text encodings.
-EOT
-    ,
-    PhabricatorEnv::getDoclink('User Guide: UTF-8 and Character Encoding'));
-  }
-
-}
diff --git a/src/applications/diffusion/controller/DiffusionRepositoryEditHostingController.php b/src/applications/diffusion/controller/DiffusionRepositoryEditHostingController.php
deleted file mode 100644
index 0c759eba5..000000000
--- a/src/applications/diffusion/controller/DiffusionRepositoryEditHostingController.php
+++ /dev/null
@@ -1,288 +0,0 @@
-<?php
-
-final class DiffusionRepositoryEditHostingController
-  extends DiffusionRepositoryEditController {
-
-  private $serve;
-
-  public function handleRequest(AphrontRequest $request) {
-    $response = $this->loadDiffusionContextForEdit();
-    if ($response) {
-      return $response;
-    }
-
-    $drequest = $this->getDiffusionRequest();
-    $repository = $drequest->getRepository();
-
-    $this->serve = $request->getURIData('serve');
-
-    if (!$this->serve) {
-      return $this->handleHosting($repository);
-    } else {
-      return $this->handleProtocols($repository);
-    }
-  }
-
-  public function handleHosting(PhabricatorRepository $repository) {
-    $request = $this->getRequest();
-    $user = $request->getUser();
-
-    $v_hosting = $repository->isHosted();
-
-    $edit_uri = $this->getRepositoryControllerURI($repository, 'edit/');
-    $next_uri = $this->getRepositoryControllerURI($repository, 'edit/serve/');
-
-    if ($request->isFormPost()) {
-      $v_hosting = $request->getBool('hosting');
-
-      $xactions = array();
-      $template = id(new PhabricatorRepositoryTransaction());
-
-      $type_hosting = PhabricatorRepositoryTransaction::TYPE_HOSTING;
-
-      $xactions[] = id(clone $template)
-        ->setTransactionType($type_hosting)
-        ->setNewValue($v_hosting);
-
-      id(new PhabricatorRepositoryEditor())
-        ->setContinueOnNoEffect(true)
-        ->setContentSourceFromRequest($request)
-        ->setActor($user)
-        ->applyTransactions($repository, $xactions);
-
-      return id(new AphrontRedirectResponse())->setURI($next_uri);
-    }
-
-    $crumbs = $this->buildApplicationCrumbs();
-    $crumbs->addTextCrumb(pht('Edit Hosting'));
-
-    $title = pht('Edit Hosting (%s)', $repository->getName());
-    $header = id(new PHUIHeaderView())
-      ->setHeader($title)
-      ->setHeaderIcon('fa-pencil');
-
-    $hosted_control = id(new AphrontFormRadioButtonControl())
-        ->setName('hosting')
-        ->setLabel(pht('Hosting'))
-        ->addButton(
-          true,
-          pht('Host Repository on Phabricator'),
-          pht(
-            'Phabricator will host this repository. Users will be able to '.
-            'push commits to Phabricator. Phabricator will not pull '.
-            'changes from elsewhere.'))
-        ->addButton(
-          false,
-          pht('Host Repository Elsewhere'),
-          pht(
-            'Phabricator will pull updates to this repository from a master '.
-            'repository elsewhere (for example, on GitHub or Bitbucket). '.
-            'Users will not be able to push commits to this repository.'))
-        ->setValue($v_hosting);
-
-    $doc_href = PhabricatorEnv::getDoclink(
-      'Diffusion User Guide: Repository Hosting');
-
-    $form = id(new AphrontFormView())
-      ->setUser($user)
-      ->appendRemarkupInstructions(
-        pht(
-          'Phabricator can host repositories, or it can track repositories '.
-          'hosted elsewhere (like on GitHub or Bitbucket). For information '.
-          'on configuring hosting, see [[ %s | Diffusion User Guide: '.
-          'Repository Hosting]]',
-          $doc_href))
-      ->appendChild($hosted_control)
-      ->appendChild(
-        id(new AphrontFormSubmitControl())
-          ->setValue(pht('Save and Continue'))
-          ->addCancelButton($edit_uri));
-
-    $form_box = id(new PHUIObjectBoxView())
-      ->setHeaderText(pht('Hosting'))
-      ->setBackground(PHUIObjectBoxView::BLUE_PROPERTY)
-      ->setForm($form);
-
-    $view = id(new PHUITwoColumnView())
-      ->setHeader($header)
-      ->setFooter(array(
-        $form_box,
-      ));
-
-    return $this->newPage()
-      ->setTitle($title)
-      ->setCrumbs($crumbs)
-      ->appendChild($view);
-  }
-
-  public function handleProtocols(PhabricatorRepository $repository) {
-    $request = $this->getRequest();
-    $user = $request->getUser();
-
-    $type = $repository->getVersionControlSystem();
-    $is_svn = ($type == PhabricatorRepositoryType::REPOSITORY_TYPE_SVN);
-
-    $v_http_mode = $repository->getDetail(
-      'serve-over-http',
-      PhabricatorRepository::SERVE_OFF);
-    $v_ssh_mode = $repository->getDetail(
-      'serve-over-ssh',
-      PhabricatorRepository::SERVE_OFF);
-
-    $edit_uri = $this->getRepositoryControllerURI($repository, 'edit/');
-    $prev_uri = $this->getRepositoryControllerURI($repository, 'edit/hosting/');
-
-    if ($request->isFormPost()) {
-      $v_http_mode = $request->getStr('http');
-      $v_ssh_mode = $request->getStr('ssh');
-
-      $xactions = array();
-      $template = id(new PhabricatorRepositoryTransaction());
-
-      $type_http = PhabricatorRepositoryTransaction::TYPE_PROTOCOL_HTTP;
-      $type_ssh = PhabricatorRepositoryTransaction::TYPE_PROTOCOL_SSH;
-
-      if (!$is_svn) {
-        $xactions[] = id(clone $template)
-          ->setTransactionType($type_http)
-          ->setNewValue($v_http_mode);
-      }
-
-      $xactions[] = id(clone $template)
-        ->setTransactionType($type_ssh)
-        ->setNewValue($v_ssh_mode);
-
-      id(new PhabricatorRepositoryEditor())
-        ->setContinueOnNoEffect(true)
-        ->setContentSourceFromRequest($request)
-        ->setActor($user)
-        ->applyTransactions($repository, $xactions);
-
-      return id(new AphrontRedirectResponse())->setURI($edit_uri);
-    }
-
-    $crumbs = $this->buildApplicationCrumbs();
-    $crumbs->addTextCrumb(pht('Edit Protocols'));
-
-    $title = pht('Edit Protocols (%s)', $repository->getName());
-    $header = id(new PHUIHeaderView())
-      ->setHeader($title)
-      ->setHeaderIcon('fa-pencil');
-
-    $rw_message = pht(
-      'Phabricator will serve a read-write copy of this repository.');
-
-    if (!$repository->isHosted()) {
-      $rw_message = array(
-        $rw_message,
-        phutil_tag('br'),
-        phutil_tag('br'),
-        pht(
-          '%s: This repository is hosted elsewhere, so Phabricator can not '.
-          'perform writes. This mode will act like "Read Only" for '.
-          'repositories hosted elsewhere.',
-          phutil_tag('strong', array(), pht('WARNING'))),
-      );
-    }
-
-    $ssh_control =
-      id(new AphrontFormRadioButtonControl())
-        ->setName('ssh')
-        ->setLabel(pht('SSH'))
-        ->setValue($v_ssh_mode)
-        ->addButton(
-          PhabricatorRepository::SERVE_OFF,
-          PhabricatorRepository::getProtocolAvailabilityName(
-            PhabricatorRepository::SERVE_OFF),
-          pht('Phabricator will not serve this repository over SSH.'))
-        ->addButton(
-          PhabricatorRepository::SERVE_READONLY,
-          PhabricatorRepository::getProtocolAvailabilityName(
-            PhabricatorRepository::SERVE_READONLY),
-          pht(
-            'Phabricator will serve a read-only copy of this repository '.
-            'over SSH.'))
-        ->addButton(
-          PhabricatorRepository::SERVE_READWRITE,
-          PhabricatorRepository::getProtocolAvailabilityName(
-            PhabricatorRepository::SERVE_READWRITE),
-          $rw_message);
-
-    $http_control =
-      id(new AphrontFormRadioButtonControl())
-        ->setName('http')
-        ->setLabel(pht('HTTP'))
-        ->setValue($v_http_mode)
-        ->addButton(
-          PhabricatorRepository::SERVE_OFF,
-          PhabricatorRepository::getProtocolAvailabilityName(
-            PhabricatorRepository::SERVE_OFF),
-          pht('Phabricator will not serve this repository over HTTP.'))
-        ->addButton(
-          PhabricatorRepository::SERVE_READONLY,
-          PhabricatorRepository::getProtocolAvailabilityName(
-            PhabricatorRepository::SERVE_READONLY),
-          pht(
-            'Phabricator will serve a read-only copy of this repository '.
-            'over HTTP.'))
-        ->addButton(
-          PhabricatorRepository::SERVE_READWRITE,
-          PhabricatorRepository::getProtocolAvailabilityName(
-            PhabricatorRepository::SERVE_READWRITE),
-          $rw_message);
-
-    if ($is_svn) {
-      $http_control = id(new AphrontFormMarkupControl())
-        ->setLabel(pht('HTTP'))
-        ->setValue(
-          phutil_tag(
-            'em',
-            array(),
-            pht(
-              'Phabricator does not currently support HTTP access to '.
-              'Subversion repositories.')));
-    }
-
-    $form = id(new AphrontFormView())
-      ->setUser($user)
-      ->appendRemarkupInstructions(
-        pht(
-          'Phabricator can serve repositories over various protocols. You can '.
-          'configure server protocols here.'))
-      ->appendChild($ssh_control);
-
-    if (!PhabricatorEnv::getEnvConfig('diffusion.allow-http-auth')) {
-      $form->appendRemarkupInstructions(
-        pht(
-          'NOTE: The configuration setting [[ %s | %s ]] is currently '.
-          'disabled. You must enable it to activate authenticated access '.
-          'to repositories over HTTP.',
-          '/config/edit/diffusion.allow-http-auth/',
-          'diffusion.allow-http-auth'));
-    }
-
-    $form
-      ->appendChild($http_control)
-      ->appendChild(
-        id(new AphrontFormSubmitControl())
-          ->setValue(pht('Save Changes'))
-          ->addCancelButton($prev_uri, pht('Back')));
-
-    $form_box = id(new PHUIObjectBoxView())
-      ->setHeaderText(pht('Protocols'))
-      ->setBackground(PHUIObjectBoxView::BLUE_PROPERTY)
-      ->setForm($form);
-
-    $view = id(new PHUITwoColumnView())
-      ->setHeader($header)
-      ->setFooter(array(
-        $form_box,
-      ));
-
-    return $this->newPage()
-      ->setTitle($title)
-      ->setCrumbs($crumbs)
-      ->appendChild($view);
-  }
-
-}
diff --git a/src/applications/diffusion/controller/DiffusionRepositoryEditMainController.php b/src/applications/diffusion/controller/DiffusionRepositoryEditMainController.php
deleted file mode 100644
index d8a4cf40b..000000000
--- a/src/applications/diffusion/controller/DiffusionRepositoryEditMainController.php
+++ /dev/null
@@ -1,1338 +0,0 @@
-<?php
-
-final class DiffusionRepositoryEditMainController
-  extends DiffusionRepositoryEditController {
-
-  public function handleRequest(AphrontRequest $request) {
-    $response = $this->loadDiffusionContextForEdit();
-    if ($response) {
-      return $response;
-    }
-
-    $viewer = $this->getViewer();
-    $drequest = $this->getDiffusionRequest();
-    $repository = $drequest->getRepository();
-
-    $is_svn = false;
-    $is_git = false;
-    $is_hg = false;
-    switch ($repository->getVersionControlSystem()) {
-      case PhabricatorRepositoryType::REPOSITORY_TYPE_GIT:
-        $is_git = true;
-        break;
-      case PhabricatorRepositoryType::REPOSITORY_TYPE_SVN:
-        $is_svn = true;
-        break;
-      case PhabricatorRepositoryType::REPOSITORY_TYPE_MERCURIAL:
-        $is_hg = true;
-        break;
-    }
-
-    $has_branches = ($is_git || $is_hg);
-    $has_local = $repository->usesLocalWorkingCopy();
-    $supports_staging = $repository->supportsStaging();
-    $supports_automation = $repository->supportsAutomation();
-
-    $crumbs = $this->buildApplicationCrumbs($is_main = true);
-
-    $title = pht('Edit %s', $repository->getName());
-
-    $header = id(new PHUIHeaderView())
-      ->setHeader($title)
-      ->setHeaderIcon('fa-pencil');
-    if ($repository->isTracked()) {
-      $header->setStatus('fa-check', 'bluegrey', pht('Active'));
-    } else {
-      $header->setStatus('fa-ban', 'dark', pht('Inactive'));
-    }
-
-    $curtain = $this->buildCurtain($repository);
-    $basic_properties = $this->buildBasicProperties($repository);
-
-    $policy_actions = $this->buildPolicyActions($repository);
-    $policy_properties =
-      $this->buildPolicyProperties($repository, $policy_actions);
-
-    $remote_properties = null;
-    if (!$repository->isHosted()) {
-      $remote_properties = $this->buildRemoteProperties(
-        $repository,
-        $this->buildRemoteActions($repository));
-    }
-
-    $encoding_actions = $this->buildEncodingActions($repository);
-    $encoding_properties =
-      $this->buildEncodingProperties($repository, $encoding_actions);
-
-    $symbols_actions = $this->buildSymbolsActions($repository);
-    $symbols_properties =
-      $this->buildSymbolsProperties($repository, $symbols_actions);
-
-    $hosting_properties = $this->buildHostingProperties(
-      $repository,
-      $this->buildHostingActions($repository));
-
-
-    $branches_properties = null;
-    if ($has_branches) {
-      $branches_properties = $this->buildBranchesProperties(
-        $repository,
-        $this->buildBranchesActions($repository));
-    }
-
-    $subversion_properties = null;
-    if ($is_svn) {
-      $subversion_properties = $this->buildSubversionProperties(
-        $repository,
-        $this->buildSubversionActions($repository));
-    }
-
-    $storage_properties = null;
-    if ($has_local) {
-      $storage_properties = $this->buildStorageProperties(
-        $repository,
-        $this->buildStorageActions($repository));
-    }
-
-    $staging_properties = null;
-    if ($supports_staging) {
-      $staging_properties = $this->buildStagingProperties(
-        $repository,
-        $this->buildStagingActions($repository));
-    }
-
-    $automation_properties = null;
-    if ($supports_automation) {
-      $automation_properties = $this->buildAutomationProperties(
-        $repository,
-        $this->buildAutomationActions($repository));
-    }
-
-    $actions_properties = $this->buildActionsProperties(
-      $repository,
-      $this->buildActionsActions($repository));
-
-    $timeline = $this->buildTransactionTimeline(
-      $repository,
-      new PhabricatorRepositoryTransactionQuery());
-    $timeline->setShouldTerminate(true);
-
-    $boxes = array();
-
-    $boxes[] = id(new PHUIObjectBoxView())
-      ->setHeaderText(pht('Policies'))
-      ->setBackground(PHUIObjectBoxView::BLUE_PROPERTY)
-      ->addPropertyList($policy_properties);
-
-    $boxes[] = id(new PHUIObjectBoxView())
-      ->setHeaderText(pht('Hosting'))
-      ->setBackground(PHUIObjectBoxView::BLUE_PROPERTY)
-      ->addPropertyList($hosting_properties);
-
-    if ($repository->canMirror()) {
-      $mirror_actions = $this->buildMirrorActions($repository);
-      $mirror_properties = $this->buildMirrorProperties(
-        $repository,
-        $mirror_actions);
-
-      $mirrors = id(new PhabricatorRepositoryMirrorQuery())
-        ->setViewer($viewer)
-        ->withRepositoryPHIDs(array($repository->getPHID()))
-        ->execute();
-
-      $mirror_list = $this->buildMirrorList($repository, $mirrors);
-
-      $boxes[] = id(new PhabricatorAnchorView())->setAnchorName('mirrors');
-
-      $mirror_info = array();
-      if (PhabricatorEnv::getEnvConfig('phabricator.silent')) {
-        $mirror_info[] = pht(
-          'Phabricator is running in silent mode, so changes will not '.
-          'be pushed to mirrors.');
-      }
-
-      $boxes[] = id(new PHUIObjectBoxView())
-        ->setFormErrors($mirror_info)
-        ->setHeaderText(pht('Mirrors'))
-        ->setBackground(PHUIObjectBoxView::BLUE_PROPERTY)
-        ->addPropertyList($mirror_properties);
-
-      $boxes[] = $mirror_list;
-    }
-
-    if ($remote_properties) {
-      $boxes[] = id(new PHUIObjectBoxView())
-        ->setHeaderText(pht('Remote'))
-        ->setBackground(PHUIObjectBoxView::BLUE_PROPERTY)
-        ->addPropertyList($remote_properties);
-    }
-
-    if ($storage_properties) {
-      $boxes[] = id(new PHUIObjectBoxView())
-        ->setHeaderText(pht('Storage'))
-        ->setBackground(PHUIObjectBoxView::BLUE_PROPERTY)
-        ->addPropertyList($storage_properties);
-    }
-
-    if ($staging_properties) {
-      $boxes[] = id(new PHUIObjectBoxView())
-        ->setHeaderText(pht('Staging'))
-        ->setBackground(PHUIObjectBoxView::BLUE_PROPERTY)
-        ->addPropertyList($staging_properties);
-    }
-
-    if ($automation_properties) {
-      $boxes[] = id(new PHUIObjectBoxView())
-        ->setHeaderText(pht('Automation'))
-        ->setBackground(PHUIObjectBoxView::BLUE_PROPERTY)
-        ->addPropertyList($automation_properties);
-    }
-
-    $boxes[] = id(new PHUIObjectBoxView())
-      ->setHeaderText(pht('Text Encoding'))
-      ->setBackground(PHUIObjectBoxView::BLUE_PROPERTY)
-      ->addPropertyList($encoding_properties);
-
-    $boxes[] = id(new PHUIObjectBoxView())
-      ->setHeaderText(pht('Symbols'))
-      ->setBackground(PHUIObjectBoxView::BLUE_PROPERTY)
-      ->addPropertyList($symbols_properties);
-
-    if ($branches_properties) {
-      $boxes[] = id(new PHUIObjectBoxView())
-        ->setHeaderText(pht('Branches'))
-        ->setBackground(PHUIObjectBoxView::BLUE_PROPERTY)
-        ->addPropertyList($branches_properties);
-    }
-
-    if ($subversion_properties) {
-      $boxes[] = id(new PHUIObjectBoxView())
-        ->setHeaderText(pht('Subversion'))
-        ->setBackground(PHUIObjectBoxView::BLUE_PROPERTY)
-        ->addPropertyList($subversion_properties);
-    }
-
-    $boxes[] = id(new PHUIObjectBoxView())
-      ->setHeaderText(pht('Actions'))
-      ->setBackground(PHUIObjectBoxView::BLUE_PROPERTY)
-      ->addPropertyList($actions_properties);
-
-    $crumbs->setBorder(true);
-
-    $view = id(new PHUITwoColumnView())
-      ->setHeader($header)
-      ->setCurtain($curtain)
-      ->addPropertySection(pht('Properties'), $basic_properties)
-      ->setMainColumn(array(
-        $boxes,
-        $timeline,
-      ));
-
-    return $this->newPage()
-      ->setTitle($title)
-      ->setCrumbs($crumbs)
-      ->appendChild($view);
-
-  }
-
-  private function buildCurtain(PhabricatorRepository $repository) {
-    $viewer = $this->getViewer();
-
-    $curtain = $this->newCurtainView($repository);
-
-    $edit = id(new PhabricatorActionView())
-      ->setIcon('fa-pencil')
-      ->setName(pht('Edit Basic Information'))
-      ->setHref($this->getRepositoryControllerURI($repository, 'edit/basic/'));
-    $curtain->addAction($edit);
-
-    $edit = id(new PhabricatorActionView())
-      ->setIcon('fa-refresh')
-      ->setName(pht('Update Now'))
-      ->setWorkflow(true)
-      ->setHref(
-        $this->getRepositoryControllerURI($repository, 'edit/update/'));
-    $curtain->addAction($edit);
-
-    $activate = id(new PhabricatorActionView())
-      ->setHref(
-        $this->getRepositoryControllerURI($repository, 'edit/activate/'))
-      ->setWorkflow(true);
-
-    if ($repository->isTracked()) {
-      $activate
-        ->setIcon('fa-pause')
-        ->setName(pht('Deactivate Repository'));
-    } else {
-      $activate
-        ->setIcon('fa-play')
-        ->setName(pht('Activate Repository'));
-    }
-
-    $curtain->addAction($activate);
-
-    $curtain->addAction(
-      id(new PhabricatorActionView())
-        ->setName(pht('Delete Repository'))
-        ->setIcon('fa-times')
-        ->setHref(
-          $this->getRepositoryControllerURI($repository, 'edit/delete/'))
-        ->setDisabled(true)
-        ->setWorkflow(true));
-
-    return $curtain;
-  }
-
-  private function buildBasicProperties(
-    PhabricatorRepository $repository) {
-
-    $viewer = $this->getViewer();
-
-    $view = id(new PHUIPropertyListView())
-      ->setUser($viewer);
-
-    $type = PhabricatorRepositoryType::getNameForRepositoryType(
-      $repository->getVersionControlSystem());
-
-    $view->addProperty(pht('Type'), $type);
-
-    $callsign = $repository->getCallsign();
-    if (!strlen($callsign)) {
-      $callsign = phutil_tag('em', array(), pht('No Callsign'));
-    }
-    $view->addProperty(pht('Callsign'), $callsign);
-
-    $short_name = $repository->getRepositorySlug();
-    if ($short_name === null) {
-      $short_name = $repository->getCloneName();
-      $short_name = phutil_tag('em', array(), $short_name);
-    }
-    $view->addProperty(pht('Short Name'), $short_name);
-
-    $view->invokeWillRenderEvent();
-
-    $view->addProperty(
-      pht('Status'),
-      $this->buildRepositoryStatus($repository));
-
-    $view->addProperty(
-      pht('Update Frequency'),
-      $this->buildRepositoryUpdateInterval($repository));
-
-    $description = $repository->getDetail('description');
-    $view->addSectionHeader(
-      pht('Description'), PHUIPropertyListView::ICON_SUMMARY);
-    if (!strlen($description)) {
-      $description = phutil_tag('em', array(), pht('No description provided.'));
-    } else {
-      $description = new PHUIRemarkupView($viewer, $description);
-    }
-    $view->addTextContent($description);
-
-    return $view;
-  }
-
-  private function buildEncodingActions(PhabricatorRepository $repository) {
-    $viewer = $this->getViewer();
-
-    $view = id(new PhabricatorActionListView())
-      ->setUser($viewer);
-
-    $edit = id(new PhabricatorActionView())
-      ->setIcon('fa-pencil')
-      ->setName(pht('Edit Text Encoding'))
-      ->setHref(
-        $this->getRepositoryControllerURI($repository, 'edit/encoding/'));
-    $view->addAction($edit);
-
-    return $view;
-  }
-
-  private function buildEncodingProperties(
-    PhabricatorRepository $repository,
-    PhabricatorActionListView $actions) {
-
-    $viewer = $this->getViewer();
-
-    $view = id(new PHUIPropertyListView())
-      ->setUser($viewer)
-      ->setActionList($actions);
-
-    $encoding = $repository->getDetail('encoding');
-    if (!$encoding) {
-      $encoding = phutil_tag('em', array(), pht('Use Default (UTF-8)'));
-    }
-
-    $view->addProperty(pht('Encoding'), $encoding);
-
-    return $view;
-  }
-
-  private function buildPolicyActions(PhabricatorRepository $repository) {
-    $viewer = $this->getViewer();
-
-    $view = id(new PhabricatorActionListView())
-      ->setUser($viewer);
-
-    $edit = id(new PhabricatorActionView())
-      ->setIcon('fa-pencil')
-      ->setName(pht('Edit Policies'))
-      ->setHref(
-        $this->getRepositoryControllerURI($repository, 'edit/policy/'));
-    $view->addAction($edit);
-
-    return $view;
-  }
-
-  private function buildPolicyProperties(
-    PhabricatorRepository $repository,
-    PhabricatorActionListView $actions) {
-
-    $viewer = $this->getViewer();
-
-    $view = id(new PHUIPropertyListView())
-      ->setUser($viewer)
-      ->setActionList($actions);
-
-    $descriptions = PhabricatorPolicyQuery::renderPolicyDescriptions(
-      $viewer,
-      $repository);
-
-    $view_parts = array();
-    if (PhabricatorSpacesNamespaceQuery::getViewerSpacesExist($viewer)) {
-      $space_phid = PhabricatorSpacesNamespaceQuery::getObjectSpacePHID(
-        $repository);
-      $view_parts[] = $viewer->renderHandle($space_phid);
-    }
-    $view_parts[] = $descriptions[PhabricatorPolicyCapability::CAN_VIEW];
-
-    $view->addProperty(
-      pht('Visible To'),
-      phutil_implode_html(" \xC2\xB7 ", $view_parts));
-
-    $view->addProperty(
-      pht('Editable By'),
-      $descriptions[PhabricatorPolicyCapability::CAN_EDIT]);
-
-    $pushable = $repository->isHosted()
-      ? $descriptions[DiffusionPushCapability::CAPABILITY]
-      : phutil_tag('em', array(), pht('Not a Hosted Repository'));
-    $view->addProperty(pht('Pushable By'), $pushable);
-
-    return $view;
-  }
-
-  private function buildBranchesActions(PhabricatorRepository $repository) {
-    $viewer = $this->getViewer();
-
-    $view = id(new PhabricatorActionListView())
-      ->setUser($viewer);
-
-    $edit = id(new PhabricatorActionView())
-      ->setIcon('fa-pencil')
-      ->setName(pht('Edit Branches'))
-      ->setHref(
-        $this->getRepositoryControllerURI($repository, 'edit/branches/'));
-    $view->addAction($edit);
-
-    return $view;
-  }
-
-  private function buildBranchesProperties(
-    PhabricatorRepository $repository,
-    PhabricatorActionListView $actions) {
-
-    $viewer = $this->getViewer();
-
-    $view = id(new PHUIPropertyListView())
-      ->setUser($viewer)
-      ->setActionList($actions);
-
-    $default_branch = nonempty(
-      $repository->getHumanReadableDetail('default-branch'),
-      phutil_tag('em', array(), $repository->getDefaultBranch()));
-    $view->addProperty(pht('Default Branch'), $default_branch);
-
-    $track_only = nonempty(
-      $repository->getHumanReadableDetail('branch-filter', array()),
-      phutil_tag('em', array(), pht('Track All Branches')));
-    $view->addProperty(pht('Track Only'), $track_only);
-
-    $autoclose_only = nonempty(
-      $repository->getHumanReadableDetail('close-commits-filter', array()),
-      phutil_tag('em', array(), pht('Autoclose On All Branches')));
-
-    if ($repository->getDetail('disable-autoclose')) {
-      $autoclose_only = phutil_tag('em', array(), pht('Disabled'));
-    }
-
-    $view->addProperty(pht('Autoclose Only'), $autoclose_only);
-
-    return $view;
-  }
-
-  private function buildSubversionActions(PhabricatorRepository $repository) {
-    $viewer = $this->getViewer();
-
-    $view = id(new PhabricatorActionListView())
-      ->setUser($viewer);
-
-    $edit = id(new PhabricatorActionView())
-      ->setIcon('fa-pencil')
-      ->setName(pht('Edit Subversion Info'))
-      ->setHref(
-        $this->getRepositoryControllerURI($repository, 'edit/subversion/'));
-    $view->addAction($edit);
-
-    return $view;
-  }
-
-  private function buildSubversionProperties(
-    PhabricatorRepository $repository,
-    PhabricatorActionListView $actions) {
-
-    $viewer = $this->getViewer();
-
-    $view = id(new PHUIPropertyListView())
-      ->setUser($viewer)
-      ->setActionList($actions);
-
-    $svn_uuid = nonempty(
-      $repository->getUUID(),
-      phutil_tag('em', array(), pht('Not Configured')));
-    $view->addProperty(pht('Subversion UUID'), $svn_uuid);
-
-    $svn_subpath = nonempty(
-      $repository->getHumanReadableDetail('svn-subpath'),
-      phutil_tag('em', array(), pht('Import Entire Repository')));
-    $view->addProperty(pht('Import Only'), $svn_subpath);
-
-    return $view;
-  }
-
-  private function buildActionsActions(PhabricatorRepository $repository) {
-    $viewer = $this->getViewer();
-
-    $view = id(new PhabricatorActionListView())
-      ->setUser($viewer);
-
-    $edit = id(new PhabricatorActionView())
-      ->setIcon('fa-pencil')
-      ->setName(pht('Edit Actions'))
-      ->setHref(
-        $this->getRepositoryControllerURI($repository, 'edit/actions/'));
-    $view->addAction($edit);
-
-    return $view;
-  }
-
-  private function buildActionsProperties(
-    PhabricatorRepository $repository,
-    PhabricatorActionListView $actions) {
-
-    $viewer = $this->getViewer();
-
-    $view = id(new PHUIPropertyListView())
-      ->setUser($viewer)
-      ->setActionList($actions);
-
-    $notify = $repository->getDetail('herald-disabled')
-      ? pht('Off')
-      : pht('On');
-    $notify = phutil_tag('em', array(), $notify);
-    $view->addProperty(pht('Publish/Notify'), $notify);
-
-    $autoclose = $repository->getDetail('disable-autoclose')
-      ? pht('Off')
-      : pht('On');
-    $autoclose = phutil_tag('em', array(), $autoclose);
-    $view->addProperty(pht('Autoclose'), $autoclose);
-
-    return $view;
-  }
-
-  private function buildRemoteActions(PhabricatorRepository $repository) {
-    $viewer = $this->getViewer();
-
-    $view = id(new PhabricatorActionListView())
-      ->setUser($viewer);
-
-    $edit = id(new PhabricatorActionView())
-      ->setIcon('fa-pencil')
-      ->setName(pht('Edit Remote'))
-      ->setHref(
-        $this->getRepositoryControllerURI($repository, 'edit/remote/'));
-    $view->addAction($edit);
-
-    return $view;
-  }
-
-  private function buildRemoteProperties(
-    PhabricatorRepository $repository,
-    PhabricatorActionListView $actions) {
-
-    $viewer = $this->getViewer();
-
-    $view = id(new PHUIPropertyListView())
-      ->setUser($viewer)
-      ->setActionList($actions);
-
-    $view->addProperty(
-      pht('Remote URI'),
-      $repository->getHumanReadableDetail('remote-uri'));
-
-    $credential_phid = $repository->getCredentialPHID();
-    if ($credential_phid) {
-      $view->addProperty(
-        pht('Credential'),
-        $viewer->renderHandle($credential_phid));
-    }
-
-    return $view;
-  }
-
-  private function buildStorageActions(PhabricatorRepository $repository) {
-    $viewer = $this->getViewer();
-
-    $view = id(new PhabricatorActionListView())
-      ->setUser($viewer);
-
-    $edit = id(new PhabricatorActionView())
-      ->setIcon('fa-pencil')
-      ->setName(pht('Edit Storage'))
-      ->setHref(
-        $this->getRepositoryControllerURI($repository, 'edit/storage/'));
-    $view->addAction($edit);
-
-    return $view;
-  }
-
-  private function buildStorageProperties(
-    PhabricatorRepository $repository,
-    PhabricatorActionListView $actions) {
-
-    $viewer = $this->getViewer();
-
-    $view = id(new PHUIPropertyListView())
-      ->setUser($viewer)
-      ->setActionList($actions);
-
-    $service_phid = $repository->getAlmanacServicePHID();
-    if ($service_phid) {
-      $v_service = $viewer->renderHandle($service_phid);
-    } else {
-      $v_service = phutil_tag(
-        'em',
-        array(),
-        pht('Local'));
-    }
-
-    $view->addProperty(
-      pht('Storage Service'),
-      $v_service);
-
-    $view->addProperty(
-      pht('Storage Path'),
-      $repository->getHumanReadableDetail('local-path'));
-
-    return $view;
-  }
-
-  private function buildStagingActions(PhabricatorRepository $repository) {
-    $viewer = $this->getViewer();
-
-    $view = id(new PhabricatorActionListView())
-      ->setUser($viewer);
-
-    $edit = id(new PhabricatorActionView())
-      ->setIcon('fa-pencil')
-      ->setName(pht('Edit Staging'))
-      ->setHref(
-        $this->getRepositoryControllerURI($repository, 'edit/staging/'));
-    $view->addAction($edit);
-
-    return $view;
-  }
-
-  private function buildStagingProperties(
-    PhabricatorRepository $repository,
-    PhabricatorActionListView $actions) {
-    $viewer = $this->getViewer();
-
-    $view = id(new PHUIPropertyListView())
-      ->setUser($viewer)
-      ->setActionList($actions);
-
-    $staging_uri = $repository->getStagingURI();
-    if (!$staging_uri) {
-      $staging_uri = phutil_tag('em', array(), pht('No Staging Area'));
-    }
-
-    $view->addProperty(
-      pht('Staging Area'),
-      $staging_uri);
-
-    return $view;
-  }
-
-  private function buildAutomationActions(PhabricatorRepository $repository) {
-    $viewer = $this->getViewer();
-
-    $view = id(new PhabricatorActionListView())
-      ->setUser($viewer);
-
-    $edit = id(new PhabricatorActionView())
-      ->setIcon('fa-pencil')
-      ->setName(pht('Edit Automation'))
-      ->setHref(
-        $this->getRepositoryControllerURI($repository, 'edit/automation/'));
-    $view->addAction($edit);
-
-    $can_test = $repository->canPerformAutomation();
-
-    $test = id(new PhabricatorActionView())
-      ->setIcon('fa-gamepad')
-      ->setName(pht('Test Configuration'))
-      ->setWorkflow(true)
-      ->setDisabled(!$can_test)
-      ->setHref(
-        $this->getRepositoryControllerURI(
-          $repository,
-          'edit/testautomation/'));
-    $view->addAction($test);
-
-    return $view;
-  }
-
-  private function buildAutomationProperties(
-    PhabricatorRepository $repository,
-    PhabricatorActionListView $actions) {
-    $viewer = $this->getViewer();
-
-    $view = id(new PHUIPropertyListView())
-      ->setUser($viewer)
-      ->setActionList($actions);
-
-    $blueprint_phids = $repository->getAutomationBlueprintPHIDs();
-    if (!$blueprint_phids) {
-      $blueprint_view = phutil_tag('em', array(), pht('Not Configured'));
-    } else {
-      $blueprint_view = id(new DrydockObjectAuthorizationView())
-        ->setUser($viewer)
-        ->setObjectPHID($repository->getPHID())
-        ->setBlueprintPHIDs($blueprint_phids);
-    }
-
-    $view->addProperty(pht('Automation'), $blueprint_view);
-
-    return $view;
-  }
-
-  private function buildHostingActions(PhabricatorRepository $repository) {
-    $user = $this->getRequest()->getUser();
-
-    $view = id(new PhabricatorActionListView())
-      ->setUser($user);
-
-    $edit = id(new PhabricatorActionView())
-      ->setIcon('fa-pencil')
-      ->setName(pht('Edit Hosting'))
-      ->setHref(
-        $this->getRepositoryControllerURI($repository, 'edit/hosting/'));
-    $view->addAction($edit);
-
-    if ($repository->canAllowDangerousChanges()) {
-      if ($repository->shouldAllowDangerousChanges()) {
-        $changes = id(new PhabricatorActionView())
-          ->setIcon('fa-shield')
-          ->setName(pht('Prevent Dangerous Changes'))
-          ->setHref(
-            $this->getRepositoryControllerURI($repository, 'edit/dangerous/'))
-          ->setWorkflow(true);
-      } else {
-        $changes = id(new PhabricatorActionView())
-          ->setIcon('fa-bullseye')
-          ->setName(pht('Allow Dangerous Changes'))
-          ->setHref(
-            $this->getRepositoryControllerURI($repository, 'edit/dangerous/'))
-          ->setWorkflow(true);
-      }
-      $view->addAction($changes);
-    }
-
-    return $view;
-  }
-
-  private function buildHostingProperties(
-    PhabricatorRepository $repository,
-    PhabricatorActionListView $actions) {
-
-    $user = $this->getRequest()->getUser();
-
-    $view = id(new PHUIPropertyListView())
-      ->setUser($user)
-      ->setActionList($actions);
-
-    $hosting = $repository->isHosted()
-      ? pht('Hosted on Phabricator')
-      : pht('Hosted Elsewhere');
-    $view->addProperty(pht('Hosting'), phutil_tag('em', array(), $hosting));
-
-    $view->addProperty(
-      pht('Serve over HTTP'),
-      phutil_tag(
-        'em',
-        array(),
-        PhabricatorRepository::getProtocolAvailabilityName(
-          $repository->getServeOverHTTP())));
-
-    $view->addProperty(
-      pht('Serve over SSH'),
-      phutil_tag(
-        'em',
-        array(),
-        PhabricatorRepository::getProtocolAvailabilityName(
-          $repository->getServeOverSSH())));
-
-    if ($repository->canAllowDangerousChanges()) {
-      if ($repository->shouldAllowDangerousChanges()) {
-        $description = pht('Allowed');
-      } else {
-        $description = pht('Not Allowed');
-      }
-
-      $view->addProperty(
-        pht('Dangerous Changes'),
-        $description);
-    }
-
-    return $view;
-  }
-
-  private function buildRepositoryStatus(
-    PhabricatorRepository $repository) {
-
-    $viewer = $this->getViewer();
-    $is_cluster = $repository->getAlmanacServicePHID();
-
-    $view = new PHUIStatusListView();
-
-    $messages = id(new PhabricatorRepositoryStatusMessage())
-      ->loadAllWhere('repositoryID = %d', $repository->getID());
-    $messages = mpull($messages, null, 'getStatusType');
-
-    if ($repository->isTracked()) {
-      $view->addItem(
-        id(new PHUIStatusItemView())
-          ->setIcon(PHUIStatusItemView::ICON_ACCEPT, 'green')
-          ->setTarget(pht('Repository Active')));
-    } else {
-      $view->addItem(
-        id(new PHUIStatusItemView())
-          ->setIcon(PHUIStatusItemView::ICON_WARNING, 'bluegrey')
-          ->setTarget(pht('Repository Inactive'))
-          ->setNote(
-            pht('Activate this repository to begin or resume import.')));
-      return $view;
-    }
-
-    $binaries = array();
-    $svnlook_check = false;
-    switch ($repository->getVersionControlSystem()) {
-      case PhabricatorRepositoryType::REPOSITORY_TYPE_GIT:
-        $binaries[] = 'git';
-        break;
-      case PhabricatorRepositoryType::REPOSITORY_TYPE_SVN:
-        $binaries[] = 'svn';
-        break;
-      case PhabricatorRepositoryType::REPOSITORY_TYPE_MERCURIAL:
-        $binaries[] = 'hg';
-        break;
-    }
-
-    if ($repository->isHosted()) {
-      if ($repository->getServeOverHTTP() != PhabricatorRepository::SERVE_OFF) {
-        switch ($repository->getVersionControlSystem()) {
-          case PhabricatorRepositoryType::REPOSITORY_TYPE_GIT:
-            $binaries[] = 'git-http-backend';
-            break;
-          case PhabricatorRepositoryType::REPOSITORY_TYPE_SVN:
-            $binaries[] = 'svnserve';
-            $binaries[] = 'svnadmin';
-            $binaries[] = 'svnlook';
-            $svnlook_check = true;
-            break;
-          case PhabricatorRepositoryType::REPOSITORY_TYPE_MERCURIAL:
-            $binaries[] = 'hg';
-            break;
-        }
-      }
-      if ($repository->getServeOverSSH() != PhabricatorRepository::SERVE_OFF) {
-        switch ($repository->getVersionControlSystem()) {
-          case PhabricatorRepositoryType::REPOSITORY_TYPE_GIT:
-            $binaries[] = 'git-receive-pack';
-            $binaries[] = 'git-upload-pack';
-            break;
-          case PhabricatorRepositoryType::REPOSITORY_TYPE_SVN:
-            $binaries[] = 'svnserve';
-            $binaries[] = 'svnadmin';
-            $binaries[] = 'svnlook';
-            $svnlook_check = true;
-            break;
-          case PhabricatorRepositoryType::REPOSITORY_TYPE_MERCURIAL:
-            $binaries[] = 'hg';
-            break;
-        }
-      }
-    }
-
-    $binaries = array_unique($binaries);
-    if (!$is_cluster) {
-      // We're only checking for binaries if we aren't running with a cluster
-      // configuration. In theory, we could check for binaries on the
-      // repository host machine, but we'd need to make this more complicated
-      // to do that.
-
-      foreach ($binaries as $binary) {
-        $where = Filesystem::resolveBinary($binary);
-        if (!$where) {
-          $view->addItem(
-            id(new PHUIStatusItemView())
-              ->setIcon(PHUIStatusItemView::ICON_WARNING, 'red')
-              ->setTarget(
-                pht('Missing Binary %s', phutil_tag('tt', array(), $binary)))
-              ->setNote(pht(
-                "Unable to find this binary in the webserver's PATH. You may ".
-                "need to configure %s.",
-                $this->getEnvConfigLink())));
-        } else {
-          $view->addItem(
-            id(new PHUIStatusItemView())
-              ->setIcon(PHUIStatusItemView::ICON_ACCEPT, 'green')
-              ->setTarget(
-                pht('Found Binary %s', phutil_tag('tt', array(), $binary)))
-              ->setNote(phutil_tag('tt', array(), $where)));
-        }
-      }
-
-      // This gets checked generically above. However, for svn commit hooks, we
-      // need this to be in environment.append-paths because subversion strips
-      // PATH.
-      if ($svnlook_check) {
-        $where = Filesystem::resolveBinary('svnlook');
-        if ($where) {
-          $path = substr($where, 0, strlen($where) - strlen('svnlook'));
-          $dirs = PhabricatorEnv::getEnvConfig('environment.append-paths');
-          $in_path = false;
-          foreach ($dirs as $dir) {
-            if (Filesystem::isDescendant($path, $dir)) {
-              $in_path = true;
-              break;
-            }
-          }
-          if (!$in_path) {
-            $view->addItem(
-              id(new PHUIStatusItemView())
-              ->setIcon(PHUIStatusItemView::ICON_WARNING, 'red')
-              ->setTarget(
-                pht('Missing Binary %s', phutil_tag('tt', array(), $binary)))
-              ->setNote(pht(
-                  'Unable to find this binary in `%s`. '.
-                  'You need to configure %s and include %s.',
-                  'environment.append-paths',
-                  $this->getEnvConfigLink(),
-                  $path)));
-          }
-        }
-      }
-    }
-
-    $doc_href = PhabricatorEnv::getDocLink('Managing Daemons with phd');
-
-    $daemon_instructions = pht(
-      'Use %s to start daemons. See %s.',
-      phutil_tag('tt', array(), 'bin/phd start'),
-      phutil_tag(
-        'a',
-        array(
-          'href' => $doc_href,
-        ),
-        pht('Managing Daemons with phd')));
-
-
-    $pull_daemon = id(new PhabricatorDaemonLogQuery())
-      ->setViewer(PhabricatorUser::getOmnipotentUser())
-      ->withStatus(PhabricatorDaemonLogQuery::STATUS_ALIVE)
-      ->withDaemonClasses(array('PhabricatorRepositoryPullLocalDaemon'))
-      ->setLimit(1)
-      ->execute();
-
-    if ($pull_daemon) {
-
-      // TODO: In a cluster environment, we need a daemon on this repository's
-      // host, specifically, and we aren't checking for that right now. This
-      // is a reasonable proxy for things being more-or-less correctly set up,
-      // though.
-
-      $view->addItem(
-        id(new PHUIStatusItemView())
-          ->setIcon(PHUIStatusItemView::ICON_ACCEPT, 'green')
-          ->setTarget(pht('Pull Daemon Running')));
-    } else {
-      $view->addItem(
-        id(new PHUIStatusItemView())
-          ->setIcon(PHUIStatusItemView::ICON_WARNING, 'red')
-          ->setTarget(pht('Pull Daemon Not Running'))
-          ->setNote($daemon_instructions));
-    }
-
-
-    $task_daemon = id(new PhabricatorDaemonLogQuery())
-      ->setViewer(PhabricatorUser::getOmnipotentUser())
-      ->withStatus(PhabricatorDaemonLogQuery::STATUS_ALIVE)
-      ->withDaemonClasses(array('PhabricatorTaskmasterDaemon'))
-      ->setLimit(1)
-      ->execute();
-    if ($task_daemon) {
-      $view->addItem(
-        id(new PHUIStatusItemView())
-          ->setIcon(PHUIStatusItemView::ICON_ACCEPT, 'green')
-          ->setTarget(pht('Task Daemon Running')));
-    } else {
-      $view->addItem(
-        id(new PHUIStatusItemView())
-          ->setIcon(PHUIStatusItemView::ICON_WARNING, 'red')
-          ->setTarget(pht('Task Daemon Not Running'))
-          ->setNote($daemon_instructions));
-    }
-
-
-    if ($is_cluster) {
-      // Just omit this status check for now in cluster environments. We
-      // could make a service call and pull it from the repository host
-      // eventually.
-    } else if ($repository->usesLocalWorkingCopy()) {
-      $local_parent = dirname($repository->getLocalPath());
-      if (Filesystem::pathExists($local_parent)) {
-        $view->addItem(
-          id(new PHUIStatusItemView())
-            ->setIcon(PHUIStatusItemView::ICON_ACCEPT, 'green')
-            ->setTarget(pht('Storage Directory OK'))
-            ->setNote(phutil_tag('tt', array(), $local_parent)));
-      } else {
-        $view->addItem(
-          id(new PHUIStatusItemView())
-            ->setIcon(PHUIStatusItemView::ICON_WARNING, 'red')
-            ->setTarget(pht('No Storage Directory'))
-            ->setNote(
-              pht(
-                'Storage directory %s does not exist, or is not readable by '.
-                'the webserver. Create this directory or make it readable.',
-                phutil_tag('tt', array(), $local_parent))));
-        return $view;
-      }
-
-      $local_path = $repository->getLocalPath();
-      $message = idx($messages, PhabricatorRepositoryStatusMessage::TYPE_INIT);
-      if ($message) {
-        switch ($message->getStatusCode()) {
-          case PhabricatorRepositoryStatusMessage::CODE_ERROR:
-            $view->addItem(
-              id(new PHUIStatusItemView())
-              ->setIcon(PHUIStatusItemView::ICON_WARNING, 'red')
-                ->setTarget(pht('Initialization Error'))
-                ->setNote($message->getParameter('message')));
-            return $view;
-          case PhabricatorRepositoryStatusMessage::CODE_OKAY:
-              if (Filesystem::pathExists($local_path)) {
-                $view->addItem(
-                  id(new PHUIStatusItemView())
-                    ->setIcon(PHUIStatusItemView::ICON_ACCEPT, 'green')
-                    ->setTarget(pht('Working Copy OK'))
-                    ->setNote(phutil_tag('tt', array(), $local_path)));
-              } else {
-                $view->addItem(
-                  id(new PHUIStatusItemView())
-                    ->setIcon(PHUIStatusItemView::ICON_WARNING, 'red')
-                    ->setTarget(pht('Working Copy Error'))
-                    ->setNote(
-                      pht(
-                        'Working copy %s has been deleted, or is not '.
-                        'readable by the webserver. Make this directory '.
-                        'readable. If it has been deleted, the daemons should '.
-                        'restore it automatically.',
-                        phutil_tag('tt', array(), $local_path))));
-                return $view;
-              }
-            break;
-          case PhabricatorRepositoryStatusMessage::CODE_WORKING:
-            $view->addItem(
-              id(new PHUIStatusItemView())
-                ->setIcon(PHUIStatusItemView::ICON_CLOCK, 'green')
-                ->setTarget(pht('Initializing Working Copy'))
-                ->setNote(pht('Daemons are initializing the working copy.')));
-            return $view;
-          default:
-            $view->addItem(
-              id(new PHUIStatusItemView())
-                ->setIcon(PHUIStatusItemView::ICON_WARNING, 'red')
-                ->setTarget(pht('Unknown Init Status'))
-                ->setNote($message->getStatusCode()));
-            return $view;
-        }
-      } else {
-        $view->addItem(
-          id(new PHUIStatusItemView())
-            ->setIcon(PHUIStatusItemView::ICON_CLOCK, 'orange')
-            ->setTarget(pht('No Working Copy Yet'))
-            ->setNote(
-              pht('Waiting for daemons to build a working copy.')));
-        return $view;
-      }
-    }
-
-    $message = idx($messages, PhabricatorRepositoryStatusMessage::TYPE_FETCH);
-    if ($message) {
-      switch ($message->getStatusCode()) {
-        case PhabricatorRepositoryStatusMessage::CODE_ERROR:
-          $message = $message->getParameter('message');
-
-          $suggestion = null;
-          if (preg_match('/Permission denied \(publickey\)./', $message)) {
-            $suggestion = pht(
-              'Public Key Error: This error usually indicates that the '.
-              'keypair you have configured does not have permission to '.
-              'access the repository.');
-          }
-
-          $message = phutil_escape_html_newlines($message);
-
-          if ($suggestion !== null) {
-            $message = array(
-              phutil_tag('strong', array(), $suggestion),
-              phutil_tag('br'),
-              phutil_tag('br'),
-              phutil_tag('em', array(), pht('Raw Error')),
-              phutil_tag('br'),
-              $message,
-            );
-          }
-
-          $view->addItem(
-            id(new PHUIStatusItemView())
-              ->setIcon(PHUIStatusItemView::ICON_WARNING, 'red')
-              ->setTarget(pht('Update Error'))
-              ->setNote($message));
-          return $view;
-        case PhabricatorRepositoryStatusMessage::CODE_OKAY:
-          $ago = (PhabricatorTime::getNow() - $message->getEpoch());
-          $view->addItem(
-            id(new PHUIStatusItemView())
-              ->setIcon(PHUIStatusItemView::ICON_ACCEPT, 'green')
-              ->setTarget(pht('Updates OK'))
-              ->setNote(
-                pht(
-                  'Last updated %s (%s ago).',
-                  phabricator_datetime($message->getEpoch(), $viewer),
-                  phutil_format_relative_time_detailed($ago))));
-          break;
-      }
-    } else {
-      $view->addItem(
-        id(new PHUIStatusItemView())
-          ->setIcon(PHUIStatusItemView::ICON_CLOCK, 'orange')
-          ->setTarget(pht('Waiting For Update'))
-          ->setNote(
-            pht('Waiting for daemons to read updates.')));
-    }
-
-    if ($repository->isImporting()) {
-      $ratio = $repository->loadImportProgress();
-      $percentage = sprintf('%.2f%%', 100 * $ratio);
-
-      $view->addItem(
-        id(new PHUIStatusItemView())
-          ->setIcon(PHUIStatusItemView::ICON_CLOCK, 'green')
-          ->setTarget(pht('Importing'))
-          ->setNote(
-            pht('%s Complete', $percentage)));
-    } else {
-      $view->addItem(
-        id(new PHUIStatusItemView())
-          ->setIcon(PHUIStatusItemView::ICON_ACCEPT, 'green')
-          ->setTarget(pht('Fully Imported')));
-    }
-
-    if (idx($messages, PhabricatorRepositoryStatusMessage::TYPE_NEEDS_UPDATE)) {
-      $view->addItem(
-        id(new PHUIStatusItemView())
-          ->setIcon(PHUIStatusItemView::ICON_UP, 'indigo')
-          ->setTarget(pht('Prioritized'))
-          ->setNote(pht('This repository will be updated soon!')));
-    }
-
-    return $view;
-  }
-
-  private function buildRepositoryUpdateInterval(
-    PhabricatorRepository $repository) {
-
-    $smart_wait = $repository->loadUpdateInterval();
-
-    $doc_href = PhabricatorEnv::getDoclink(
-      'Diffusion User Guide: Repository Updates');
-
-    return array(
-      phutil_format_relative_time_detailed($smart_wait),
-      " \xC2\xB7 ",
-      phutil_tag(
-        'a',
-        array(
-          'href' => $doc_href,
-          'target' => '_blank',
-        ),
-        pht('Learn More')),
-    );
-  }
-
-
-  private function buildMirrorActions(
-    PhabricatorRepository $repository) {
-
-    $viewer = $this->getViewer();
-
-    $mirror_actions = id(new PhabricatorActionListView())
-      ->setUser($viewer);
-
-    $new_mirror_uri = $this->getRepositoryControllerURI(
-      $repository,
-      'mirror/edit/');
-
-    $mirror_actions->addAction(
-      id(new PhabricatorActionView())
-        ->setName(pht('Add Mirror'))
-        ->setIcon('fa-plus')
-        ->setHref($new_mirror_uri)
-        ->setWorkflow(true));
-
-    return $mirror_actions;
-  }
-
-  private function buildMirrorProperties(
-    PhabricatorRepository $repository,
-    PhabricatorActionListView $actions) {
-
-    $viewer = $this->getViewer();
-
-    $mirror_properties = id(new PHUIPropertyListView())
-      ->setUser($viewer)
-      ->setActionList($actions);
-
-    $mirror_properties->addProperty(
-      '',
-      phutil_tag(
-        'em',
-        array(),
-        pht('Automatically push changes into other remotes.')));
-
-    return $mirror_properties;
-  }
-
-  private function buildMirrorList(
-    PhabricatorRepository $repository,
-    array $mirrors) {
-    assert_instances_of($mirrors, 'PhabricatorRepositoryMirror');
-
-    $mirror_list = id(new PHUIObjectItemListView())
-      ->setNoDataString(pht('This repository has no configured mirrors.'));
-
-    foreach ($mirrors as $mirror) {
-      $item = id(new PHUIObjectItemView())
-        ->setHeader($mirror->getRemoteURI());
-
-      $edit_uri = $this->getRepositoryControllerURI(
-        $repository,
-        'mirror/edit/'.$mirror->getID().'/');
-
-      $delete_uri = $this->getRepositoryControllerURI(
-        $repository,
-        'mirror/delete/'.$mirror->getID().'/');
-
-      $item->addAction(
-        id(new PHUIListItemView())
-          ->setIcon('fa-pencil')
-          ->setHref($edit_uri)
-          ->setWorkflow(true));
-
-      $item->addAction(
-        id(new PHUIListItemView())
-          ->setIcon('fa-times')
-          ->setHref($delete_uri)
-          ->setWorkflow(true));
-
-      $mirror_list->addItem($item);
-    }
-
-    return id(new PHUIObjectBoxView())
-      ->setHeaderText(pht('Configured Mirrors'))
-      ->setBackground(PHUIObjectBoxView::BLUE_PROPERTY)
-      ->setObjectList($mirror_list);
-  }
-
-  private function buildSymbolsActions(PhabricatorRepository $repository) {
-    $viewer = $this->getViewer();
-
-    $view = id(new PhabricatorActionListView())
-      ->setUser($viewer);
-
-    $edit = id(new PhabricatorActionView())
-      ->setIcon('fa-pencil')
-      ->setName(pht('Edit Symbols'))
-      ->setHref(
-        $this->getRepositoryControllerURI($repository, 'edit/symbol/'));
-    $view->addAction($edit);
-
-    return $view;
-  }
-
-  private function buildSymbolsProperties(
-    PhabricatorRepository $repository,
-    PhabricatorActionListView $actions) {
-
-    $viewer = $this->getViewer();
-
-    $view = id(new PHUIPropertyListView())
-      ->setUser($viewer)
-      ->setActionList($actions);
-
-    $languages = $repository->getSymbolLanguages();
-
-    if ($languages) {
-      $languages = implode(', ', $languages);
-    } else {
-      $languages = phutil_tag('em', array(), pht('Any'));
-    }
-    $view->addProperty(pht('Languages'), $languages);
-
-    $sources = $repository->getSymbolSources();
-    if ($sources) {
-      $handles = $viewer->loadHandles($sources);
-      $sources = $handles->renderList();
-    } else {
-      $sources = phutil_tag('em', array(), pht('This Repository Only'));
-    }
-    $view->addProperty(pht('Use Symbols From'), $sources);
-    return $view;
-  }
-
-  private function getEnvConfigLink() {
-    $config_href = '/config/edit/environment.append-paths/';
-    return phutil_tag(
-      'a',
-      array(
-        'href' => $config_href,
-      ),
-      'environment.append-paths');
-  }
-
-}
diff --git a/src/applications/diffusion/controller/DiffusionRepositoryEditStagingController.php b/src/applications/diffusion/controller/DiffusionRepositoryEditStagingController.php
deleted file mode 100644
index 2d158e61e..000000000
--- a/src/applications/diffusion/controller/DiffusionRepositoryEditStagingController.php
+++ /dev/null
@@ -1,91 +0,0 @@
-<?php
-
-final class DiffusionRepositoryEditStagingController
-  extends DiffusionRepositoryEditController {
-
-  public function handleRequest(AphrontRequest $request) {
-    $response = $this->loadDiffusionContextForEdit();
-    if ($response) {
-      return $response;
-    }
-
-    $viewer = $this->getViewer();
-    $drequest = $this->getDiffusionRequest();
-    $repository = $drequest->getRepository();
-
-
-    if (!$repository->supportsStaging()) {
-      return new Aphront404Response();
-    }
-
-    $edit_uri = $this->getRepositoryControllerURI($repository, 'edit/');
-
-    $v_area = $repository->getHumanReadableDetail('staging-uri');
-    if ($request->isFormPost()) {
-      $v_area = $request->getStr('area');
-
-      $xactions = array();
-      $template = id(new PhabricatorRepositoryTransaction());
-
-      $type_encoding = PhabricatorRepositoryTransaction::TYPE_STAGING_URI;
-
-      $xactions[] = id(clone $template)
-        ->setTransactionType($type_encoding)
-        ->setNewValue($v_area);
-
-      id(new PhabricatorRepositoryEditor())
-        ->setContinueOnNoEffect(true)
-        ->setContentSourceFromRequest($request)
-        ->setActor($viewer)
-        ->applyTransactions($repository, $xactions);
-
-      return id(new AphrontRedirectResponse())->setURI($edit_uri);
-    }
-
-    $crumbs = $this->buildApplicationCrumbs();
-    $crumbs->addTextCrumb(pht('Edit Staging'));
-
-    $title = pht('Edit Staging (%s)', $repository->getName());
-    $header = id(new PHUIHeaderView())
-      ->setHeader($title)
-      ->setHeaderIcon('fa-pencil');
-
-    $form = id(new AphrontFormView())
-      ->setUser($viewer)
-      ->appendRemarkupInstructions(
-        pht(
-          "To make it easier to run integration tests and builds on code ".
-          "under review, you can configure a **Staging Area**. When `arc` ".
-          "creates a diff, it will push a copy of the changes to the ".
-          "configured staging area with a corresponding tag.".
-          "\n\n".
-          "IMPORTANT: This feature is new, experimental, and not supported. ".
-          "Use it at your own risk."))
-      ->appendChild(
-        id(new AphrontFormTextControl())
-          ->setLabel(pht('Staging Area URI'))
-          ->setName('area')
-          ->setValue($v_area))
-      ->appendChild(
-        id(new AphrontFormSubmitControl())
-          ->setValue(pht('Save'))
-          ->addCancelButton($edit_uri));
-
-    $form_box = id(new PHUIObjectBoxView())
-      ->setHeaderText(pht('Staging'))
-      ->setBackground(PHUIObjectBoxView::BLUE_PROPERTY)
-      ->setForm($form);
-
-    $view = id(new PHUITwoColumnView())
-      ->setHeader($header)
-      ->setFooter(array(
-        $form_box,
-      ));
-
-    return $this->newPage()
-      ->setTitle($title)
-      ->setCrumbs($crumbs)
-      ->appendChild($view);
-  }
-
-}
diff --git a/src/applications/diffusion/controller/DiffusionRepositoryEditStorageController.php b/src/applications/diffusion/controller/DiffusionRepositoryEditStorageController.php
deleted file mode 100644
index f3a492e3b..000000000
--- a/src/applications/diffusion/controller/DiffusionRepositoryEditStorageController.php
+++ /dev/null
@@ -1,80 +0,0 @@
-<?php
-
-final class DiffusionRepositoryEditStorageController
-  extends DiffusionRepositoryEditController {
-
-  public function handleRequest(AphrontRequest $request) {
-    $response = $this->loadDiffusionContextForEdit();
-    if ($response) {
-      return $response;
-    }
-
-    $viewer = $this->getViewer();
-    $drequest = $this->getDiffusionRequest();
-    $repository = $drequest->getRepository();
-
-    $edit_uri = $this->getRepositoryControllerURI($repository, 'edit/');
-
-    $v_local = $repository->getHumanReadableDetail('local-path');
-    $errors = array();
-
-    $crumbs = $this->buildApplicationCrumbs();
-    $crumbs->addTextCrumb(pht('Edit Storage'));
-
-    $title = pht('Edit %s', $repository->getName());
-    $header = id(new PHUIHeaderView())
-      ->setHeader($title)
-      ->setHeaderIcon('fa-pencil');
-
-    $service_phid = $repository->getAlmanacServicePHID();
-    if ($service_phid) {
-      $handles = $this->loadViewerHandles(array($service_phid));
-      $v_service = $handles[$service_phid]->renderLink();
-    } else {
-      $v_service = phutil_tag(
-        'em',
-        array(),
-        pht('Local'));
-    }
-
-    $form = id(new AphrontFormView())
-      ->setUser($viewer)
-      ->appendChild(
-        id(new AphrontFormMarkupControl())
-          ->setLabel(pht('Storage Service'))
-          ->setValue($v_service))
-      ->appendChild(
-        id(new AphrontFormMarkupControl())
-          ->setName('local')
-          ->setLabel(pht('Storage Path'))
-          ->setValue($v_local))
-      ->appendRemarkupInstructions(
-        pht(
-          "You can not adjust the local path for this repository from the ".
-          "web interface. To edit it, run this command:\n\n  %s",
-          sprintf(
-            'phabricator/ $ ./bin/repository edit %s --as %s --local-path ...',
-            $repository->getMonogram(),
-            $viewer->getUsername())))
-      ->appendChild(
-        id(new AphrontFormSubmitControl())
-          ->addCancelButton($edit_uri, pht('Done')));
-
-    $form_box = id(new PHUIObjectBoxView())
-      ->setHeaderText(pht('Storage'))
-      ->setBackground(PHUIObjectBoxView::BLUE_PROPERTY)
-      ->setForm($form);
-
-    $view = id(new PHUITwoColumnView())
-      ->setHeader($header)
-      ->setFooter(array(
-        $form_box,
-      ));
-
-    return $this->newPage()
-      ->setTitle($title)
-      ->setCrumbs($crumbs)
-      ->appendChild($view);
-  }
-
-}
diff --git a/src/applications/diffusion/controller/DiffusionRepositoryEditSubversionController.php b/src/applications/diffusion/controller/DiffusionRepositoryEditSubversionController.php
deleted file mode 100644
index 6fcc31613..000000000
--- a/src/applications/diffusion/controller/DiffusionRepositoryEditSubversionController.php
+++ /dev/null
@@ -1,118 +0,0 @@
-<?php
-
-final class DiffusionRepositoryEditSubversionController
-  extends DiffusionRepositoryEditController {
-
-  public function handleRequest(AphrontRequest $request) {
-    $response = $this->loadDiffusionContextForEdit();
-    if ($response) {
-      return $response;
-    }
-
-    $viewer = $this->getViewer();
-    $drequest = $this->getDiffusionRequest();
-    $repository = $drequest->getRepository();
-
-    switch ($repository->getVersionControlSystem()) {
-      case PhabricatorRepositoryType::REPOSITORY_TYPE_GIT:
-      case PhabricatorRepositoryType::REPOSITORY_TYPE_MERCURIAL:
-        throw new Exception(
-          pht('Git and Mercurial do not support editing SVN properties!'));
-      case PhabricatorRepositoryType::REPOSITORY_TYPE_SVN:
-        break;
-      default:
-        throw new Exception(
-          pht('Repository has unknown version control system!'));
-    }
-
-    $edit_uri = $this->getRepositoryControllerURI($repository, 'edit/');
-
-    $v_subpath = $repository->getHumanReadableDetail('svn-subpath');
-    $v_uuid = $repository->getUUID();
-
-    if ($request->isFormPost()) {
-      $v_subpath = $request->getStr('subpath');
-      $v_uuid = $request->getStr('uuid');
-
-      $xactions = array();
-      $template = id(new PhabricatorRepositoryTransaction());
-
-      $type_subpath = PhabricatorRepositoryTransaction::TYPE_SVN_SUBPATH;
-      $type_uuid = PhabricatorRepositoryTransaction::TYPE_UUID;
-
-      $xactions[] = id(clone $template)
-        ->setTransactionType($type_subpath)
-        ->setNewValue($v_subpath);
-
-      $xactions[] = id(clone $template)
-        ->setTransactionType($type_uuid)
-        ->setNewValue($v_uuid);
-
-      id(new PhabricatorRepositoryEditor())
-        ->setContinueOnNoEffect(true)
-        ->setContentSourceFromRequest($request)
-        ->setActor($viewer)
-        ->applyTransactions($repository, $xactions);
-
-      return id(new AphrontRedirectResponse())->setURI($edit_uri);
-    }
-
-    $content = array();
-
-    $crumbs = $this->buildApplicationCrumbs();
-    $crumbs->addTextCrumb(pht('Edit Subversion Info'));
-
-    $title = pht('Edit Subversion Info (%s)', $repository->getName());
-    $header = id(new PHUIHeaderView())
-      ->setHeader($title)
-      ->setHeaderIcon('fa-pencil');
-
-    $policies = id(new PhabricatorPolicyQuery())
-      ->setViewer($viewer)
-      ->setObject($repository)
-      ->execute();
-
-    $form = id(new AphrontFormView())
-      ->setUser($viewer)
-      ->appendRemarkupInstructions(
-        pht(
-          "You can set the **Repository UUID**, which will help Phabriactor ".
-          "provide better context in some cases. You can find the UUID of a ".
-          "repository by running `%s`.\n\n".
-          "If you want to import only part of a repository, like `trunk/`, ".
-          "you can set a path in **Import Only**. Phabricator will ignore ".
-          "commits which do not affect this path.",
-          'svn info'))
-      ->appendChild(
-        id(new AphrontFormTextControl())
-          ->setName('uuid')
-          ->setLabel(pht('Repository UUID'))
-          ->setValue($v_uuid))
-      ->appendChild(
-        id(new AphrontFormTextControl())
-          ->setName('subpath')
-          ->setLabel(pht('Import Only'))
-          ->setValue($v_subpath))
-      ->appendChild(
-        id(new AphrontFormSubmitControl())
-          ->setValue(pht('Save Subversion Info'))
-          ->addCancelButton($edit_uri));
-
-    $form_box = id(new PHUIObjectBoxView())
-      ->setHeaderText(pht('Subversion'))
-      ->setBackground(PHUIObjectBoxView::BLUE_PROPERTY)
-      ->setForm($form);
-
-    $view = id(new PHUITwoColumnView())
-      ->setHeader($header)
-      ->setFooter(array(
-        $form_box,
-      ));
-
-    return $this->newPage()
-      ->setTitle($title)
-      ->setCrumbs($crumbs)
-      ->appendChild($view);
-  }
-
-}
diff --git a/src/applications/diffusion/controller/DiffusionRepositoryEditUpdateController.php b/src/applications/diffusion/controller/DiffusionRepositoryEditUpdateController.php
index 303959d09..4cb6ffda6 100644
--- a/src/applications/diffusion/controller/DiffusionRepositoryEditUpdateController.php
+++ b/src/applications/diffusion/controller/DiffusionRepositoryEditUpdateController.php
@@ -1,66 +1,68 @@
 <?php
 
 final class DiffusionRepositoryEditUpdateController
-  extends DiffusionRepositoryEditController {
+  extends DiffusionRepositoryManageController {
 
   public function handleRequest(AphrontRequest $request) {
     $response = $this->loadDiffusionContextForEdit();
     if ($response) {
       return $response;
     }
 
     $viewer = $this->getViewer();
     $drequest = $this->getDiffusionRequest();
     $repository = $drequest->getRepository();
 
-    $edit_uri = $this->getRepositoryControllerURI($repository, 'edit/');
+    $panel_uri = id(new DiffusionRepositoryStatusManagementPanel())
+      ->setRepository($repository)
+      ->getPanelURI();
 
     if ($request->isFormPost()) {
       $params = array(
         'repositories' => array(
           $repository->getPHID(),
         ),
       );
 
       id(new ConduitCall('diffusion.looksoon', $params))
         ->setUser($viewer)
         ->execute();
 
-      return id(new AphrontRedirectResponse())->setURI($edit_uri);
+      return id(new AphrontRedirectResponse())->setURI($panel_uri);
     }
 
     $doc_name = 'Diffusion User Guide: Repository Updates';
     $doc_href = PhabricatorEnv::getDoclink($doc_name);
     $doc_link = phutil_tag(
       'a',
       array(
         'href' => $doc_href,
         'target' => '_blank',
       ),
       $doc_name);
 
     return $this->newDialog()
       ->setTitle(pht('Update Repository Now'))
       ->appendParagraph(
         pht(
           'Normally, Phabricator automatically updates repositories '.
           'based on how much time has elapsed since the last commit. '.
           'This helps reduce load if you have a large number of mostly '.
           'inactive repositories, which is common.'))
       ->appendParagraph(
         pht(
           'You can manually schedule an update for this repository. The '.
           'daemons will perform the update as soon as possible. This may '.
           'be helpful if you have just made a commit to a rarely used '.
           'repository.'))
       ->appendParagraph(
         pht(
           'To learn more about how Phabricator updates repositories, '.
           'read %s in the documentation.',
           $doc_link))
-      ->addCancelButton($edit_uri)
+      ->addCancelButton($panel_uri)
       ->addSubmitButton(pht('Schedule Update'));
   }
 
 
 }
diff --git a/src/applications/diffusion/controller/DiffusionRepositoryEditproController.php b/src/applications/diffusion/controller/DiffusionRepositoryEditproController.php
deleted file mode 100644
index 79febc2cb..000000000
--- a/src/applications/diffusion/controller/DiffusionRepositoryEditproController.php
+++ /dev/null
@@ -1,69 +0,0 @@
-<?php
-
-final class DiffusionRepositoryEditproController
-  extends DiffusionRepositoryEditController {
-
-  public function handleRequest(AphrontRequest $request) {
-    $engine = id(new DiffusionRepositoryEditEngine())
-      ->setController($this);
-
-    $id = $request->getURIData('id');
-    if (!$id) {
-      $this->requireApplicationCapability(
-        DiffusionCreateRepositoriesCapability::CAPABILITY);
-
-      $vcs = $request->getStr('vcs');
-      $vcs_types = PhabricatorRepositoryType::getRepositoryTypeMap();
-      if (empty($vcs_types[$vcs])) {
-        return $this->buildVCSTypeResponse();
-      }
-
-      $engine
-        ->addContextParameter('vcs', $vcs)
-        ->setVersionControlSystem($vcs);
-    }
-
-    return $engine->buildResponse();
-  }
-
-  private function buildVCSTypeResponse() {
-    $vcs_types = PhabricatorRepositoryType::getRepositoryTypeMap();
-
-    $request = $this->getRequest();
-    $viewer = $this->getViewer();
-
-    $crumbs = $this->buildApplicationCrumbs();
-    $crumbs->addTextCrumb(pht('Create Repository'));
-    $crumbs->setBorder(true);
-
-    $title = pht('Choose Repository Type');
-    $header = id(new PHUIHeaderView())
-      ->setHeader(pht('Create Repository'))
-      ->setHeaderIcon('fa-plus-square');
-
-    $layout = id(new AphrontMultiColumnView())
-      ->setFluidLayout(true);
-
-    $create_uri = $request->getRequestURI();
-
-    foreach ($vcs_types as $vcs_key => $vcs_type) {
-      $action = id(new PHUIActionPanelView())
-        ->setIcon(idx($vcs_type, 'icon'))
-        ->setHeader(idx($vcs_type, 'create.header'))
-        ->setHref($create_uri->alter('vcs', $vcs_key))
-        ->setSubheader(idx($vcs_type, 'create.subheader'));
-
-      $layout->addColumn($action);
-    }
-
-    $view = id(new PHUITwoColumnView())
-      ->setHeader($header)
-      ->setFooter($layout);
-
-    return $this->newPage()
-      ->setTitle($title)
-      ->setCrumbs($crumbs)
-      ->appendChild($view);
-  }
-
-}
diff --git a/src/applications/diffusion/controller/DiffusionRepositoryListController.php b/src/applications/diffusion/controller/DiffusionRepositoryListController.php
index 5897d2f16..eebe0bf91 100644
--- a/src/applications/diffusion/controller/DiffusionRepositoryListController.php
+++ b/src/applications/diffusion/controller/DiffusionRepositoryListController.php
@@ -1,32 +1,25 @@
 <?php
 
 final class DiffusionRepositoryListController extends DiffusionController {
 
   public function shouldAllowPublic() {
     return true;
   }
 
   public function handleRequest(AphrontRequest $request) {
     return id(new PhabricatorRepositorySearchEngine())
       ->setController($this)
       ->buildResponse();
   }
 
   protected function buildApplicationCrumbs() {
     $crumbs = parent::buildApplicationCrumbs();
 
-    $can_create = $this->hasApplicationCapability(
-      DiffusionCreateRepositoriesCapability::CAPABILITY);
-
-    $crumbs->addAction(
-      id(new PHUIListItemView())
-        ->setName(pht('New Repository'))
-        ->setHref($this->getApplicationURI('new/'))
-        ->setDisabled(!$can_create)
-        ->setWorkflow(!$can_create)
-        ->setIcon('fa-plus-square'));
+    id(new DiffusionRepositoryEditEngine())
+      ->setViewer($this->getViewer())
+      ->addActionToCrumbs($crumbs);
 
     return $crumbs;
   }
 
 }
diff --git a/src/applications/diffusion/controller/DiffusionRepositoryManageController.php b/src/applications/diffusion/controller/DiffusionRepositoryManageController.php
index aa34f8f90..8b0740c54 100644
--- a/src/applications/diffusion/controller/DiffusionRepositoryManageController.php
+++ b/src/applications/diffusion/controller/DiffusionRepositoryManageController.php
@@ -1,129 +1,25 @@
 <?php
 
-final class DiffusionRepositoryManageController
+abstract class DiffusionRepositoryManageController
   extends DiffusionController {
 
-  private $navigation;
+  protected function buildApplicationCrumbs() {
+    $crumbs = parent::buildApplicationCrumbs();
 
-  public function buildApplicationMenu() {
-    // TODO: This is messy for now; the mobile menu should be set automatically
-    // when the body content is a two-column view with navigation.
-    if ($this->navigation) {
-      return $this->navigation->getMenu();
-    }
-    return parent::buildApplicationMenu();
-  }
+    if ($this->hasDiffusionRequest()) {
+      $drequest = $this->getDiffusionRequest();
+      $repository = $drequest->getRepository();
 
+      $crumbs->addTextCrumb(
+        $repository->getDisplayName(),
+        $repository->getURI());
 
-  public function handleRequest(AphrontRequest $request) {
-    $response = $this->loadDiffusionContext();
-    if ($response) {
-      return $response;
+      $crumbs->addTextCrumb(
+        pht('Manage'),
+        $repository->getPathURI('manage/'));
     }
 
-    $viewer = $this->getViewer();
-    $drequest = $this->getDiffusionRequest();
-    $repository = $drequest->getRepository();
-
-    $panels = DiffusionRepositoryManagementPanel::getAllPanels();
-
-    foreach ($panels as $panel) {
-      $panel
-        ->setViewer($viewer)
-        ->setRepository($repository)
-        ->setController($this);
-    }
-
-    $selected = $request->getURIData('panel');
-    if (!strlen($selected)) {
-      $selected = head_key($panels);
-    }
-
-    if (empty($panels[$selected])) {
-      return new Aphront404Response();
-    }
-
-    $nav = $this->renderSideNav($repository, $panels, $selected);
-    $this->navigation = $nav;
-
-    $panel = $panels[$selected];
-
-    $content = $panel->buildManagementPanelContent();
-
-    $title = array(
-      $panel->getManagementPanelLabel(),
-      $repository->getDisplayName(),
-    );
-
-    $crumbs = $this->buildApplicationCrumbs();
-    $crumbs->addTextCrumb(
-      $repository->getDisplayName(),
-      $repository->getURI());
-    $crumbs->addTextCrumb(
-      pht('Manage'),
-      $repository->getPathURI('manage/'));
-    $crumbs->addTextCrumb($panel->getManagementPanelLabel());
-
-    $header_text = pht(
-      '%s: %s',
-      $repository->getDisplayName(),
-      $panel->getManagementPanelLabel());
-
-    $header = id(new PHUIHeaderView())
-      ->setHeader($header_text)
-      ->setHeaderIcon('fa-pencil');
-    if ($repository->isTracked()) {
-      $header->setStatus('fa-check', 'bluegrey', pht('Active'));
-    } else {
-      $header->setStatus('fa-ban', 'dark', pht('Inactive'));
-    }
-
-    $view = id(new PHUITwoColumnView())
-      ->setHeader($header)
-      ->setNavigation($nav)
-      ->setMainColumn($content);
-
-    $curtain = $panel->buildManagementPanelCurtain();
-    if ($curtain) {
-      $view->setCurtain($curtain);
-    }
-
-    return $this->newPage()
-      ->setTitle($title)
-      ->setCrumbs($crumbs)
-      ->appendChild($view);
-  }
-
-  private function renderSideNav(
-    PhabricatorRepository $repository,
-    array $panels,
-    $selected) {
-
-    $base_uri = $repository->getPathURI('manage/');
-    $base_uri = new PhutilURI($base_uri);
-
-    $nav = id(new AphrontSideNavFilterView())
-      ->setBaseURI($base_uri);
-
-    foreach ($panels as $panel) {
-      $nav->addFilter(
-        $panel->getManagementPanelKey(),
-        $panel->getManagementPanelLabel());
-    }
-
-    $nav->selectFilter($selected);
-
-    return $nav;
+    return $crumbs;
   }
 
-  public function newTimeline(PhabricatorRepository $repository) {
-    $timeline = $this->buildTransactionTimeline(
-      $repository,
-      new PhabricatorRepositoryTransactionQuery());
-    $timeline->setShouldTerminate(true);
-
-    return $timeline;
-  }
-
-
 }
diff --git a/src/applications/diffusion/controller/DiffusionRepositoryManageController.php b/src/applications/diffusion/controller/DiffusionRepositoryManagePanelsController.php
similarity index 75%
copy from src/applications/diffusion/controller/DiffusionRepositoryManageController.php
copy to src/applications/diffusion/controller/DiffusionRepositoryManagePanelsController.php
index aa34f8f90..22c03145e 100644
--- a/src/applications/diffusion/controller/DiffusionRepositoryManageController.php
+++ b/src/applications/diffusion/controller/DiffusionRepositoryManagePanelsController.php
@@ -1,129 +1,146 @@
 <?php
 
-final class DiffusionRepositoryManageController
-  extends DiffusionController {
+final class DiffusionRepositoryManagePanelsController
+  extends DiffusionRepositoryManageController {
 
   private $navigation;
 
   public function buildApplicationMenu() {
     // TODO: This is messy for now; the mobile menu should be set automatically
     // when the body content is a two-column view with navigation.
     if ($this->navigation) {
       return $this->navigation->getMenu();
     }
     return parent::buildApplicationMenu();
   }
 
 
   public function handleRequest(AphrontRequest $request) {
     $response = $this->loadDiffusionContext();
     if ($response) {
       return $response;
     }
 
     $viewer = $this->getViewer();
     $drequest = $this->getDiffusionRequest();
     $repository = $drequest->getRepository();
 
     $panels = DiffusionRepositoryManagementPanel::getAllPanels();
 
-    foreach ($panels as $panel) {
+    foreach ($panels as $key => $panel) {
       $panel
         ->setViewer($viewer)
         ->setRepository($repository)
         ->setController($this);
+
+      if (!$panel->shouldEnableForRepository($repository)) {
+        unset($panels[$key]);
+        continue;
+      }
     }
 
     $selected = $request->getURIData('panel');
     if (!strlen($selected)) {
       $selected = head_key($panels);
     }
 
     if (empty($panels[$selected])) {
       return new Aphront404Response();
     }
 
     $nav = $this->renderSideNav($repository, $panels, $selected);
     $this->navigation = $nav;
 
     $panel = $panels[$selected];
 
     $content = $panel->buildManagementPanelContent();
 
     $title = array(
       $panel->getManagementPanelLabel(),
       $repository->getDisplayName(),
     );
 
     $crumbs = $this->buildApplicationCrumbs();
-    $crumbs->addTextCrumb(
-      $repository->getDisplayName(),
-      $repository->getURI());
-    $crumbs->addTextCrumb(
-      pht('Manage'),
-      $repository->getPathURI('manage/'));
     $crumbs->addTextCrumb($panel->getManagementPanelLabel());
+    $crumbs->setBorder(true);
 
     $header_text = pht(
       '%s: %s',
       $repository->getDisplayName(),
       $panel->getManagementPanelLabel());
 
     $header = id(new PHUIHeaderView())
       ->setHeader($header_text)
       ->setHeaderIcon('fa-pencil');
     if ($repository->isTracked()) {
       $header->setStatus('fa-check', 'bluegrey', pht('Active'));
     } else {
       $header->setStatus('fa-ban', 'dark', pht('Inactive'));
     }
 
+    $header->addActionLink(
+      id(new PHUIButtonView())
+        ->setTag('a')
+        ->setText(pht('View Repository'))
+        ->setHref($repository->getURI())
+        ->setIcon('fa-code'));
+
     $view = id(new PHUITwoColumnView())
       ->setHeader($header)
       ->setNavigation($nav)
       ->setMainColumn($content);
 
     $curtain = $panel->buildManagementPanelCurtain();
     if ($curtain) {
       $view->setCurtain($curtain);
     }
 
     return $this->newPage()
       ->setTitle($title)
       ->setCrumbs($crumbs)
       ->appendChild($view);
   }
 
   private function renderSideNav(
     PhabricatorRepository $repository,
     array $panels,
     $selected) {
 
     $base_uri = $repository->getPathURI('manage/');
     $base_uri = new PhutilURI($base_uri);
 
     $nav = id(new AphrontSideNavFilterView())
       ->setBaseURI($base_uri);
 
     foreach ($panels as $panel) {
-      $nav->addFilter(
-        $panel->getManagementPanelKey(),
-        $panel->getManagementPanelLabel());
+      $key = $panel->getManagementPanelKey();
+      $label = $panel->getManagementPanelLabel();
+      $icon = $panel->getManagementPanelIcon();
+      $href = $panel->getPanelNavigationURI();
+
+      $item = id(new PHUIListItemView())
+        ->setKey($key)
+        ->setName($label)
+        ->setType(PHUIListItemView::TYPE_LINK)
+        ->setHref($href)
+        ->setIcon($icon);
+
+      $nav->addMenuItem($item);
     }
 
     $nav->selectFilter($selected);
 
     return $nav;
   }
 
   public function newTimeline(PhabricatorRepository $repository) {
     $timeline = $this->buildTransactionTimeline(
       $repository,
       new PhabricatorRepositoryTransactionQuery());
     $timeline->setShouldTerminate(true);
 
     return $timeline;
   }
 
 
 }
diff --git a/src/applications/diffusion/controller/DiffusionRepositoryNewController.php b/src/applications/diffusion/controller/DiffusionRepositoryNewController.php
deleted file mode 100644
index 560d896af..000000000
--- a/src/applications/diffusion/controller/DiffusionRepositoryNewController.php
+++ /dev/null
@@ -1,79 +0,0 @@
-<?php
-
-final class DiffusionRepositoryNewController extends DiffusionController {
-
-  public function handleRequest(AphrontRequest $request) {
-    $viewer = $this->getViewer();
-
-    $this->requireApplicationCapability(
-      DiffusionCreateRepositoriesCapability::CAPABILITY);
-
-    if ($request->isFormPost()) {
-      if ($request->getStr('type')) {
-        switch ($request->getStr('type')) {
-          case 'create':
-            $uri = $this->getApplicationURI('create/');
-            break;
-          case 'import':
-          default:
-            $uri = $this->getApplicationURI('import/');
-            break;
-        }
-
-        return id(new AphrontRedirectResponse())->setURI($uri);
-      }
-    }
-
-    $doc_href = PhabricatorEnv::getDoclink(
-      'Diffusion User Guide: Repository Hosting');
-
-    $doc_link = phutil_tag(
-      'a',
-      array(
-        'href' => $doc_href,
-        'target' => '_blank',
-      ),
-      pht('Diffusion User Guide: Repository Hosting'));
-
-    $form = id(new AphrontFormView())
-      ->setUser($viewer)
-      ->appendChild(
-        id(new AphrontFormRadioButtonControl())
-          ->setName('type')
-          ->addButton(
-            'create',
-            pht('Create a New Hosted Repository'),
-            array(
-              pht(
-                'Create a new, empty repository which Phabricator will host. '.
-                'For instructions on configuring repository hosting, see %s.',
-                $doc_link),
-            ))
-          ->addButton(
-            'import',
-            pht('Import an Existing External Repository'),
-            pht(
-              "Import a repository hosted somewhere else, like GitHub, ".
-              "Bitbucket, or your organization's existing servers. ".
-              "Phabricator will read changes from the repository but will ".
-              "not host or manage it. The authoritative master version of ".
-              "the repository will stay where it is now.")))
-      ->appendChild(
-        id(new AphrontFormSubmitControl())
-          ->setValue(pht('Continue'))
-          ->addCancelButton($this->getApplicationURI()));
-
-    $crumbs = $this->buildApplicationCrumbs();
-    $crumbs->addTextCrumb(pht('New Repository'));
-
-    $form_box = id(new PHUIObjectBoxView())
-      ->setHeaderText(pht('Create or Import Repository'))
-      ->setForm($form);
-
-    return $this->newPage()
-      ->setTitle(pht('New Repository'))
-      ->setCrumbs($crumbs)
-      ->appendChild($form_box);
-  }
-
-}
diff --git a/src/applications/diffusion/controller/DiffusionRepositorySymbolsController.php b/src/applications/diffusion/controller/DiffusionRepositorySymbolsController.php
deleted file mode 100644
index 06ce23032..000000000
--- a/src/applications/diffusion/controller/DiffusionRepositorySymbolsController.php
+++ /dev/null
@@ -1,120 +0,0 @@
-<?php
-
-final class DiffusionRepositorySymbolsController
-  extends DiffusionRepositoryEditController {
-
-  public function handleRequest(AphrontRequest $request) {
-    $response = $this->loadDiffusionContextForEdit();
-    if ($response) {
-      return $response;
-    }
-
-    $viewer = $this->getViewer();
-    $drequest = $this->getDiffusionRequest();
-    $repository = $drequest->getRepository();
-
-    $edit_uri = $this->getRepositoryControllerURI($repository, 'edit/');
-
-    $v_sources = $repository->getSymbolSources();
-    $v_languages = $repository->getSymbolLanguages();
-    if ($v_languages) {
-      $v_languages = implode(', ', $v_languages);
-    }
-    $errors = array();
-
-    if ($request->isFormPost()) {
-      $v_sources = $request->getArr('sources');
-      $v_languages = $request->getStrList('languages');
-      $v_languages = array_map('phutil_utf8_strtolower', $v_languages);
-
-      if (!$errors) {
-        $xactions = array();
-        $template = id(new PhabricatorRepositoryTransaction());
-
-        $type_sources = PhabricatorRepositoryTransaction::TYPE_SYMBOLS_SOURCES;
-        $type_lang = PhabricatorRepositoryTransaction::TYPE_SYMBOLS_LANGUAGE;
-
-        $xactions[] = id(clone $template)
-          ->setTransactionType($type_sources)
-          ->setNewValue($v_sources);
-
-        $xactions[] = id(clone $template)
-          ->setTransactionType($type_lang)
-          ->setNewValue($v_languages);
-
-        try {
-          id(new PhabricatorRepositoryEditor())
-            ->setContinueOnNoEffect(true)
-            ->setContentSourceFromRequest($request)
-            ->setActor($viewer)
-            ->applyTransactions($repository, $xactions);
-
-          return id(new AphrontRedirectResponse())->setURI($edit_uri);
-        } catch (Exception $ex) {
-          $errors[] = $ex->getMessage();
-        }
-      }
-    }
-
-    $crumbs = $this->buildApplicationCrumbs();
-    $crumbs->addTextCrumb(pht('Edit Symbols'));
-
-    $title = pht('Edit Symbols (%s)', $repository->getName());
-    $header = id(new PHUIHeaderView())
-      ->setHeader($title)
-      ->setHeaderIcon('fa-pencil');
-
-    $form = id(new AphrontFormView())
-      ->setUser($viewer)
-      ->appendRemarkupInstructions($this->getInstructions())
-      ->appendChild(
-        id(new AphrontFormTextControl())
-          ->setName('languages')
-          ->setLabel(pht('Indexed Languages'))
-          ->setCaption(pht(
-            'File extensions, separate with commas, for example: php, py. '.
-            'Leave blank for "any".'))
-          ->setValue($v_languages))
-
-      ->appendControl(
-        id(new AphrontFormTokenizerControl())
-          ->setName('sources')
-          ->setLabel(pht('Uses Symbols From'))
-          ->setDatasource(new DiffusionRepositoryDatasource())
-          ->setValue($v_sources))
-
-      ->appendChild(
-        id(new AphrontFormSubmitControl())
-          ->setValue(pht('Save'))
-          ->addCancelButton($edit_uri));
-
-    $form_box = id(new PHUIObjectBoxView())
-      ->setHeaderText(pht('Symbols'))
-      ->setBackground(PHUIObjectBoxView::BLUE_PROPERTY)
-      ->setForm($form)
-      ->setFormErrors($errors);
-
-    $view = id(new PHUITwoColumnView())
-      ->setHeader($header)
-      ->setFooter(array(
-        $form_box,
-      ));
-
-    return $this->newPage()
-      ->setTitle($title)
-      ->setCrumbs($crumbs)
-      ->appendChild($view);
-  }
-
-  private function getInstructions() {
-    return pht(<<<EOT
-Configure Symbols for this repository.
-
-See [[%s | Symbol Indexes]] for more information on using symbols.
-EOT
-    ,
-    PhabricatorEnv::getDoclink(
-      'Diffusion User Guide: Symbol Indexes'));
-  }
-
-}
diff --git a/src/applications/diffusion/controller/DiffusionRepositoryTestAutomationController.php b/src/applications/diffusion/controller/DiffusionRepositoryTestAutomationController.php
index c52b0a14e..bc1c470ca 100644
--- a/src/applications/diffusion/controller/DiffusionRepositoryTestAutomationController.php
+++ b/src/applications/diffusion/controller/DiffusionRepositoryTestAutomationController.php
@@ -1,70 +1,72 @@
 <?php
 
 final class DiffusionRepositoryTestAutomationController
-  extends DiffusionRepositoryEditController {
+  extends DiffusionRepositoryManageController {
 
   public function handleRequest(AphrontRequest $request) {
     $response = $this->loadDiffusionContextForEdit();
     if ($response) {
       return $response;
     }
 
     $viewer = $this->getViewer();
     $drequest = $this->getDiffusionRequest();
     $repository = $drequest->getRepository();
 
-    $edit_uri = $this->getRepositoryControllerURI($repository, 'edit/');
+    $panel_uri = id(new DiffusionRepositoryAutomationManagementPanel())
+      ->setRepository($repository)
+      ->getPanelURI();
 
     if (!$repository->canPerformAutomation()) {
       return $this->newDialog()
         ->setTitle(pht('Automation Not Configured'))
         ->appendParagraph(
           pht(
             'You can not run a configuration test for this repository '.
             'because you have not configured repository automation yet. '.
             'Configure it first, then test the configuration.'))
-        ->addCancelButton($edit_uri);
+        ->addCancelButton($panel_uri);
     }
 
     if ($request->isFormPost()) {
       $op = new DrydockTestRepositoryOperation();
 
       $operation = DrydockRepositoryOperation::initializeNewOperation($op)
         ->setAuthorPHID($viewer->getPHID())
         ->setObjectPHID($repository->getPHID())
         ->setRepositoryPHID($repository->getPHID())
         ->setRepositoryTarget('none:')
         ->save();
 
       $operation->scheduleUpdate();
 
       $operation_id = $operation->getID();
       $operation_uri = "/drydock/operation/{$operation_id}/";
 
       return id(new AphrontRedirectResponse())
         ->setURI($operation_uri);
     }
 
     return $this->newDialog()
       ->setTitle(pht('Test Automation Configuration'))
       ->appendParagraph(
         pht(
           'This configuration test will build a working copy of the '.
           'repository and perform some basic validation. If it works, '.
           'your configuration is substantially correct.'))
       ->appendParagraph(
         pht(
           'The test will not perform any writes against the repository, so '.
           'write operations may still fail even if the test passes. This '.
           'test covers building and reading working copies, but not writing '.
           'to them.'))
       ->appendParagraph(
         pht(
           'If you run into write failures despite passing this test, '.
           'it suggests that your setup is nearly correct but authentication '.
           'is probably not fully configured.'))
-      ->addCancelButton($edit_uri)
+      ->addCancelButton($panel_uri)
       ->addSubmitButton(pht('Start Test'));
   }
 
 }
diff --git a/src/applications/diffusion/controller/DiffusionRepositoryURICredentialController.php b/src/applications/diffusion/controller/DiffusionRepositoryURICredentialController.php
new file mode 100644
index 000000000..96b97673c
--- /dev/null
+++ b/src/applications/diffusion/controller/DiffusionRepositoryURICredentialController.php
@@ -0,0 +1,159 @@
+<?php
+
+final class DiffusionRepositoryURICredentialController
+  extends DiffusionController {
+
+  public function handleRequest(AphrontRequest $request) {
+    $response = $this->loadDiffusionContextForEdit();
+    if ($response) {
+      return $response;
+    }
+
+    $viewer = $this->getViewer();
+    $drequest = $this->getDiffusionRequest();
+    $repository = $drequest->getRepository();
+
+    $id = $request->getURIData('id');
+    $uri = id(new PhabricatorRepositoryURIQuery())
+      ->setViewer($viewer)
+      ->withIDs(array($id))
+      ->withRepositories(array($repository))
+      ->requireCapabilities(
+        array(
+          PhabricatorPolicyCapability::CAN_VIEW,
+          PhabricatorPolicyCapability::CAN_EDIT,
+        ))
+      ->executeOne();
+    if (!$uri) {
+      return new Aphront404Response();
+    }
+
+    $is_builtin = $uri->isBuiltin();
+    $has_credential = (bool)$uri->getCredentialPHID();
+    $view_uri = $uri->getViewURI();
+    $is_remove = ($request->getURIData('action') == 'remove');
+
+    if ($is_builtin) {
+      return $this->newDialog()
+        ->setTitle(pht('Builtin URIs Do Not Use Credentials'))
+        ->appendParagraph(
+          pht(
+            'You can not set a credential for builtin URIs which Phabricator '.
+            'hosts and serves. Phabricator does not fetch from these URIs or '.
+            'push to these URIs, and does not need credentials to '.
+            'authenticate any activity against them.'))
+        ->addCancelButton($view_uri);
+    }
+
+    if ($request->isFormPost()) {
+      $xactions = array();
+
+      if ($is_remove) {
+        $new_phid = null;
+      } else {
+        $new_phid = $request->getStr('credentialPHID');
+      }
+
+      $type_credential = PhabricatorRepositoryURITransaction::TYPE_CREDENTIAL;
+
+      $xactions[] = id(new PhabricatorRepositoryURITransaction())
+        ->setTransactionType($type_credential)
+        ->setNewValue($new_phid);
+
+      $editor = id(new DiffusionURIEditor())
+        ->setActor($viewer)
+        ->setContinueOnNoEffect(true)
+        ->setContinueOnMissingFields(true)
+        ->setContentSourceFromRequest($request)
+        ->applyTransactions($uri, $xactions);
+
+      return id(new AphrontRedirectResponse())->setURI($view_uri);
+    }
+
+    $command_engine = $uri->newCommandEngine();
+    $is_supported = $command_engine->isCredentialSupported();
+
+    $body = null;
+    $form = null;
+    $width = AphrontDialogView::WIDTH_DEFAULT;
+    if ($is_remove) {
+      if ($has_credential) {
+        $title = pht('Remove Credential');
+        $body = pht(
+          'This credential will no longer be used to authenticate activity '.
+          'against this URI.');
+        $button = pht('Remove Credential');
+      } else {
+        $title = pht('No Credential');
+        $body = pht(
+          'This URI does not have an associated credential.');
+        $button = null;
+      }
+    } else if (!$is_supported) {
+      $title = pht('Unauthenticated Protocol');
+      $body = pht(
+        'The protocol for this URI ("%s") does not use authentication, so '.
+        'you can not provide a credential.',
+        $command_engine->getDisplayProtocol());
+      $button = null;
+    } else {
+      $effective_uri = $uri->getEffectiveURI();
+
+      $label = $command_engine->getPassphraseCredentialLabel();
+      $credential_type = $command_engine->getPassphraseDefaultCredentialType();
+
+      $provides_type = $command_engine->getPassphraseProvidesCredentialType();
+      $options = id(new PassphraseCredentialQuery())
+        ->setViewer($viewer)
+        ->withIsDestroyed(false)
+        ->withProvidesTypes(array($provides_type))
+        ->execute();
+
+      $control = id(new PassphraseCredentialControl())
+        ->setName('credentialPHID')
+        ->setLabel($label)
+        ->setValue($uri->getCredentialPHID())
+        ->setCredentialType($credential_type)
+        ->setOptions($options);
+
+      $default_user = $effective_uri->getUser();
+      if (strlen($default_user)) {
+        $control->setDefaultUsername($default_user);
+      }
+
+      $form = id(new AphrontFormView())
+        ->setViewer($viewer)
+        ->appendControl($control);
+
+      if ($has_credential) {
+        $title = pht('Update Credential');
+        $button = pht('Update Credential');
+      } else {
+        $title = pht('Set Credential');
+        $button = pht('Set Credential');
+      }
+
+      $width = AphrontDialogView::WIDTH_FORM;
+    }
+
+    $dialog = $this->newDialog()
+      ->setWidth($width)
+      ->setTitle($title)
+      ->addCancelButton($view_uri);
+
+    if ($body) {
+      $dialog->appendParagraph($body);
+    }
+
+    if ($form) {
+      $dialog->appendForm($form);
+    }
+
+    if ($button) {
+      $dialog->addSubmitButton($button);
+    }
+
+    return $dialog;
+  }
+
+}
diff --git a/src/applications/diffusion/controller/DiffusionRepositoryURIDisableController.php b/src/applications/diffusion/controller/DiffusionRepositoryURIDisableController.php
index b846b3f4e..466a0fd38 100644
--- a/src/applications/diffusion/controller/DiffusionRepositoryURIDisableController.php
+++ b/src/applications/diffusion/controller/DiffusionRepositoryURIDisableController.php
@@ -1,72 +1,82 @@
 <?php
 
 final class DiffusionRepositoryURIDisableController
   extends DiffusionController {
 
   public function handleRequest(AphrontRequest $request) {
     $response = $this->loadDiffusionContextForEdit();
     if ($response) {
       return $response;
     }
 
     $viewer = $this->getViewer();
     $drequest = $this->getDiffusionRequest();
     $repository = $drequest->getRepository();
 
     $id = $request->getURIData('id');
     $uri = id(new PhabricatorRepositoryURIQuery())
       ->setViewer($viewer)
       ->withIDs(array($id))
       ->withRepositories(array($repository))
       ->requireCapabilities(
         array(
           PhabricatorPolicyCapability::CAN_VIEW,
           PhabricatorPolicyCapability::CAN_EDIT,
         ))
       ->executeOne();
     if (!$uri) {
       return new Aphront404Response();
     }
 
     $is_disabled = $uri->getIsDisabled();
     $view_uri = $uri->getViewURI();
 
+    if ($uri->isBuiltin()) {
+      return $this->newDialog()
+        ->setTitle(pht('Builtin URI'))
+        ->appendParagraph(
+          pht(
+            'You can not manually disable builtin URIs. To hide a builtin '.
+            'URI, configure its "Display" behavior instead.'))
+        ->addCancelButton($view_uri);
+    }
+
     if ($request->isFormPost()) {
       $xactions = array();
 
       $xactions[] = id(new PhabricatorRepositoryURITransaction())
         ->setTransactionType(PhabricatorRepositoryURITransaction::TYPE_DISABLE)
         ->setNewValue(!$is_disabled);
 
       $editor = id(new DiffusionURIEditor())
         ->setActor($viewer)
         ->setContinueOnNoEffect(true)
         ->setContinueOnMissingFields(true)
         ->setContentSourceFromRequest($request)
         ->applyTransactions($uri, $xactions);
 
       return id(new AphrontRedirectResponse())->setURI($view_uri);
     }
 
     if ($is_disabled) {
       $title = pht('Enable URI');
       $body = pht(
         'Enable this URI? Any configured behaviors will begin working '.
         'again.');
       $button = pht('Enable URI');
     } else {
       $title = pht('Disable URI');
       $body = pht(
         'Disable this URI? It will no longer be observed, fetched, mirrored, '.
         'served or shown to users.');
       $button = pht('Disable URI');
     }
 
     return $this->newDialog()
       ->setTitle($title)
       ->appendParagraph($body)
       ->addCancelButton($view_uri)
       ->addSubmitButton($button);
   }
 
 }
diff --git a/src/applications/diffusion/controller/DiffusionRepositoryURIViewController.php b/src/applications/diffusion/controller/DiffusionRepositoryURIViewController.php
index 8cd143c5a..61335d20d 100644
--- a/src/applications/diffusion/controller/DiffusionRepositoryURIViewController.php
+++ b/src/applications/diffusion/controller/DiffusionRepositoryURIViewController.php
@@ -1,182 +1,298 @@
 <?php
 
 final class DiffusionRepositoryURIViewController
   extends DiffusionController {
 
   public function handleRequest(AphrontRequest $request) {
     $response = $this->loadDiffusionContext();
     if ($response) {
       return $response;
     }
 
     $viewer = $this->getViewer();
     $drequest = $this->getDiffusionRequest();
     $repository = $drequest->getRepository();
     $id = $request->getURIData('id');
 
     $uri = id(new PhabricatorRepositoryURIQuery())
       ->setViewer($viewer)
       ->withIDs(array($id))
       ->withRepositories(array($repository))
       ->executeOne();
     if (!$uri) {
       return new Aphront404Response();
     }
 
     $title = array(
       pht('URI'),
       $repository->getDisplayName(),
     );
 
     $crumbs = $this->buildApplicationCrumbs();
     $crumbs->addTextCrumb(
       $repository->getDisplayName(),
       $repository->getURI());
     $crumbs->addTextCrumb(
       pht('Manage'),
       $repository->getPathURI('manage/'));
 
     $panel_label = id(new DiffusionRepositoryURIsManagementPanel())
       ->getManagementPanelLabel();
     $panel_uri = $repository->getPathURI('manage/uris/');
     $crumbs->addTextCrumb($panel_label, $panel_uri);
 
     $crumbs->addTextCrumb(pht('URI %d', $uri->getID()));
 
     $header_text = pht(
       '%s: URI %d',
       $repository->getDisplayName(),
       $uri->getID());
 
     $header = id(new PHUIHeaderView())
       ->setHeader($header_text)
       ->setHeaderIcon('fa-pencil');
     if ($uri->getIsDisabled()) {
       $header->setStatus('fa-ban', 'dark', pht('Disabled'));
     } else {
       $header->setStatus('fa-check', 'bluegrey', pht('Active'));
     }
 
     $curtain = $this->buildCurtain($uri);
     $details = $this->buildPropertySection($uri);
 
     $timeline = $this->buildTransactionTimeline(
       $uri,
       new PhabricatorRepositoryURITransactionQuery());
     $timeline->setShouldTerminate(true);
 
     $view = id(new PHUITwoColumnView())
       ->setHeader($header)
       ->setMainColumn(
         array(
           $details,
           $timeline,
         ))
       ->setCurtain($curtain);
 
     return $this->newPage()
       ->setTitle($title)
       ->setCrumbs($crumbs)
       ->appendChild($view);
   }
 
   private function buildCurtain(PhabricatorRepositoryURI $uri) {
     $viewer = $this->getViewer();
+    $repository = $uri->getRepository();
     $id = $uri->getID();
 
     $can_edit = PhabricatorPolicyFilter::hasCapability(
       $viewer,
       $uri,
       PhabricatorPolicyCapability::CAN_EDIT);
 
-
     $curtain = $this->newCurtainView($uri);
 
     $edit_uri = $uri->getEditURI();
 
     $curtain->addAction(
       id(new PhabricatorActionView())
         ->setIcon('fa-pencil')
         ->setName(pht('Edit URI'))
         ->setHref($edit_uri)
         ->setWorkflow(!$can_edit)
         ->setDisabled(!$can_edit));
 
+    $credential_uri = $repository->getPathURI("uri/credential/{$id}/edit/");
+    $remove_uri = $repository->getPathURI("uri/credential/{$id}/remove/");
+    $has_credential = (bool)$uri->getCredentialPHID();
+
+    if ($uri->isBuiltin()) {
+      $can_credential = false;
+    } else if (!$uri->newCommandEngine()->isCredentialSupported()) {
+      $can_credential = false;
+    } else {
+      $can_credential = true;
+    }
+
+    $can_update = ($can_edit && $can_credential);
+    $can_remove = ($can_edit && $has_credential);
+
+    if ($has_credential) {
+      $credential_name = pht('Update Credential');
+    } else {
+      $credential_name = pht('Set Credential');
+    }
+
+    $curtain->addAction(
+      id(new PhabricatorActionView())
+        ->setIcon('fa-key')
+        ->setName($credential_name)
+        ->setHref($credential_uri)
+        ->setWorkflow(true)
+        ->setDisabled(!$can_edit));
+
+    $curtain->addAction(
+      id(new PhabricatorActionView())
+        ->setIcon('fa-times')
+        ->setName(pht('Remove Credential'))
+        ->setHref($remove_uri)
+        ->setWorkflow(true)
+        ->setDisabled(!$can_remove));
+
     if ($uri->getIsDisabled()) {
       $disable_name = pht('Enable URI');
       $disable_icon = 'fa-check';
     } else {
       $disable_name = pht('Disable URI');
       $disable_icon = 'fa-ban';
     }
 
-    $disable_uri = $uri->getRepository()->getPathURI("uri/disable/{$id}/");
+    $can_disable = ($can_edit && !$uri->isBuiltin());
+
+    $disable_uri = $repository->getPathURI("uri/disable/{$id}/");
 
     $curtain->addAction(
       id(new PhabricatorActionView())
         ->setIcon($disable_icon)
         ->setName($disable_name)
         ->setHref($disable_uri)
         ->setWorkflow(true)
-        ->setDisabled(!$can_edit));
+        ->setDisabled(!$can_disable));
 
     return $curtain;
   }
 
   private function buildPropertySection(PhabricatorRepositoryURI $uri) {
     $viewer = $this->getViewer();
 
     $properties = id(new PHUIPropertyListView())
       ->setUser($viewer);
 
     $properties->addProperty(pht('URI'), $uri->getDisplayURI());
-    $properties->addProperty(pht('Credential'), 'TODO');
+
+    $credential_phid = $uri->getCredentialPHID();
+    $command_engine = $uri->newCommandEngine();
+    $is_optional = $command_engine->isCredentialOptional();
+    $is_supported = $command_engine->isCredentialSupported();
+    $is_builtin = $uri->isBuiltin();
+
+    if ($is_builtin) {
+      $credential_icon = 'fa-circle-o';
+      $credential_color = 'grey';
+      $credential_label = pht('Builtin');
+      $credential_note = pht('Builtin URIs do not use credentials.');
+    } else if (!$is_supported) {
+      $credential_icon = 'fa-circle-o';
+      $credential_color = 'grey';
+      $credential_label = pht('Not Supported');
+      $credential_note = pht('This protocol does not support authentication.');
+    } else if (!$credential_phid) {
+      if ($is_optional) {
+        $credential_icon = 'fa-circle-o';
+        $credential_color = 'green';
+        $credential_label = pht('No Credential');
+        $credential_note = pht('Configured for anonymous access.');
+      } else {
+        $credential_icon = 'fa-times';
+        $credential_color = 'red';
+        $credential_label = pht('Required');
+        $credential_note = pht('Credential required but not configured.');
+      }
+    } else {
+      // Don't raise a policy exception if we can't see the credential.
+      $credentials = id(new PassphraseCredentialQuery())
+        ->setViewer($viewer)
+        ->withPHIDs(array($credential_phid))
+        ->execute();
+      $credential = head($credentials);
+
+      if (!$credential) {
+        $handles = $viewer->loadHandles(array($credential_phid));
+        $handle = $handles[$credential_phid];
+        if ($handle->getPolicyFiltered()) {
+          $credential_icon = 'fa-lock';
+          $credential_color = 'grey';
+          $credential_label = pht('Restricted');
+          $credential_note = pht(
+            'You do not have permission to view the configured '.
+            'credential.');
+        } else {
+          $credential_icon = 'fa-times';
+          $credential_color = 'red';
+          $credential_label = pht('Invalid');
+          $credential_note = pht('Configured credential is invalid.');
+        }
+      } else {
+        $provides = $credential->getProvidesType();
+        $needs = $command_engine->getPassphraseProvidesCredentialType();
+        if ($provides != $needs) {
+          $credential_icon = 'fa-times';
+          $credential_color = 'red';
+          $credential_label = pht('Wrong Type');
+        } else {
+          $credential_icon = 'fa-check';
+          $credential_color = 'green';
+          $credential_label = $command_engine->getPassphraseCredentialLabel();
+        }
+        $credential_note = $viewer->renderHandle($credential_phid);
+      }
+    }
+
+    $credential_item = id(new PHUIStatusItemView())
+      ->setIcon($credential_icon, $credential_color)
+      ->setTarget(phutil_tag('strong', array(), $credential_label))
+      ->setNote($credential_note);
+
+    $credential_view = id(new PHUIStatusListView())
+      ->addItem($credential_item);
+
+    $properties->addProperty(pht('Credential'), $credential_view);
 
 
     $io_type = $uri->getEffectiveIOType();
     $io_map = PhabricatorRepositoryURI::getIOTypeMap();
     $io_spec = idx($io_map, $io_type, array());
 
     $io_icon = idx($io_spec, 'icon');
     $io_color = idx($io_spec, 'color');
     $io_label = idx($io_spec, 'label', $io_type);
     $io_note = idx($io_spec, 'note');
 
     $io_item = id(new PHUIStatusItemView())
       ->setIcon($io_icon, $io_color)
       ->setTarget(phutil_tag('strong', array(), $io_label))
       ->setNote($io_note);
 
     $io_view = id(new PHUIStatusListView())
       ->addItem($io_item);
 
     $properties->addProperty(pht('I/O'), $io_view);
 
 
     $display_type = $uri->getEffectiveDisplayType();
     $display_map = PhabricatorRepositoryURI::getDisplayTypeMap();
     $display_spec = idx($display_map, $display_type, array());
 
     $display_icon = idx($display_spec, 'icon');
     $display_color = idx($display_spec, 'color');
     $display_label = idx($display_spec, 'label', $display_type);
     $display_note = idx($display_spec, 'note');
 
     $display_item = id(new PHUIStatusItemView())
       ->setIcon($display_icon, $display_color)
       ->setTarget(phutil_tag('strong', array(), $display_label))
       ->setNote($display_note);
 
     $display_view = id(new PHUIStatusListView())
       ->addItem($display_item);
 
     $properties->addProperty(pht('Display'), $display_view);
 
 
     return id(new PHUIObjectBoxView())
       ->setHeaderText(pht('Details'))
       ->setBackground(PHUIObjectBoxView::BLUE_PROPERTY)
       ->appendChild($properties);
   }
 
 }
diff --git a/src/applications/diffusion/controller/DiffusionServeController.php b/src/applications/diffusion/controller/DiffusionServeController.php
index f1703655f..69b8c64b1 100644
--- a/src/applications/diffusion/controller/DiffusionServeController.php
+++ b/src/applications/diffusion/controller/DiffusionServeController.php
@@ -1,1220 +1,1231 @@
 <?php
 
 final class DiffusionServeController extends DiffusionController {
 
   private $serviceViewer;
   private $serviceRepository;
 
   private $isGitLFSRequest;
   private $gitLFSToken;
   private $gitLFSInput;
 
   public function setServiceViewer(PhabricatorUser $viewer) {
     $this->getRequest()->setUser($viewer);
 
     $this->serviceViewer = $viewer;
     return $this;
   }
 
   public function getServiceViewer() {
     return $this->serviceViewer;
   }
 
   public function setServiceRepository(PhabricatorRepository $repository) {
     $this->serviceRepository = $repository;
     return $this;
   }
 
   public function getServiceRepository() {
     return $this->serviceRepository;
   }
 
   public function getIsGitLFSRequest() {
     return $this->isGitLFSRequest;
   }
 
   public function getGitLFSToken() {
     return $this->gitLFSToken;
   }
 
   public function isVCSRequest(AphrontRequest $request) {
     $identifier = $this->getRepositoryIdentifierFromRequest($request);
     if ($identifier === null) {
       return null;
     }
 
     $content_type = $request->getHTTPHeader('Content-Type');
     $user_agent = idx($_SERVER, 'HTTP_USER_AGENT');
     $request_type = $request->getHTTPHeader('X-Phabricator-Request-Type');
 
     // This may have a "charset" suffix, so only match the prefix.
     $lfs_pattern = '(^application/vnd\\.git-lfs\\+json(;|\z))';
 
     $vcs = null;
     if ($request->getExists('service')) {
       $service = $request->getStr('service');
       // We get this initially for `info/refs`.
       // Git also gives us a User-Agent like "git/1.8.2.3".
       $vcs = PhabricatorRepositoryType::REPOSITORY_TYPE_GIT;
     } else if (strncmp($user_agent, 'git/', 4) === 0) {
       $vcs = PhabricatorRepositoryType::REPOSITORY_TYPE_GIT;
     } else if ($content_type == 'application/x-git-upload-pack-request') {
       // We get this for `git-upload-pack`.
       $vcs = PhabricatorRepositoryType::REPOSITORY_TYPE_GIT;
     } else if ($content_type == 'application/x-git-receive-pack-request') {
       // We get this for `git-receive-pack`.
       $vcs = PhabricatorRepositoryType::REPOSITORY_TYPE_GIT;
     } else if (preg_match($lfs_pattern, $content_type)) {
       // This is a Git LFS HTTP API request.
       $vcs = PhabricatorRepositoryType::REPOSITORY_TYPE_GIT;
       $this->isGitLFSRequest = true;
     } else if ($request_type == 'git-lfs') {
       // This is a Git LFS object content request.
       $vcs = PhabricatorRepositoryType::REPOSITORY_TYPE_GIT;
       $this->isGitLFSRequest = true;
     } else if ($request->getExists('cmd')) {
       // Mercurial also sends an Accept header like
       // "application/mercurial-0.1", and a User-Agent like
       // "mercurial/proto-1.0".
       $vcs = PhabricatorRepositoryType::REPOSITORY_TYPE_MERCURIAL;
     } else {
       // Subversion also sends an initial OPTIONS request (vs GET/POST), and
       // has a User-Agent like "SVN/1.8.3 (x86_64-apple-darwin11.4.2)
       // serf/1.3.2".
       $dav = $request->getHTTPHeader('DAV');
       $dav = new PhutilURI($dav);
       if ($dav->getDomain() === 'subversion.tigris.org') {
         $vcs = PhabricatorRepositoryType::REPOSITORY_TYPE_SVN;
       }
     }
 
     return $vcs;
   }
 
   public function handleRequest(AphrontRequest $request) {
     $service_exception = null;
     $response = null;
 
     try {
       $response = $this->serveRequest($request);
     } catch (Exception $ex) {
       $service_exception = $ex;
     }
 
     try {
       $remote_addr = $request->getRemoteAddress();
 
       $pull_event = id(new PhabricatorRepositoryPullEvent())
         ->setEpoch(PhabricatorTime::getNow())
         ->setRemoteAddress($remote_addr)
         ->setRemoteProtocol('http');
 
       if ($response) {
         $pull_event
           ->setResultType('wild')
           ->setResultCode($response->getHTTPResponseCode());
 
         if ($response instanceof PhabricatorVCSResponse) {
           $pull_event->setProperties(
             array(
               'response.message' => $response->getMessage(),
             ));
         }
       } else {
         $pull_event
           ->setResultType('exception')
           ->setResultCode(500)
           ->setProperties(
             array(
               'exception.class' => get_class($ex),
               'exception.message' => $ex->getMessage(),
             ));
       }
 
       $viewer = $this->getServiceViewer();
       if ($viewer) {
         $pull_event->setPullerPHID($viewer->getPHID());
       }
 
       $repository = $this->getServiceRepository();
       if ($repository) {
         $pull_event->setRepositoryPHID($repository->getPHID());
       }
 
       $unguarded = AphrontWriteGuard::beginScopedUnguardedWrites();
         $pull_event->save();
       unset($unguarded);
 
     } catch (Exception $ex) {
       if ($service_exception) {
         throw $service_exception;
       }
       throw $ex;
     }
 
     if ($service_exception) {
       throw $service_exception;
     }
 
     return $response;
   }
 
   private function serveRequest(AphrontRequest $request) {
     $identifier = $this->getRepositoryIdentifierFromRequest($request);
 
     // If authentication credentials have been provided, try to find a user
     // that actually matches those credentials.
 
     // We require both the username and password to be nonempty, because Git
     // won't prompt users who provide a username but no password otherwise.
     // See T10797 for discussion.
 
     $have_user = strlen(idx($_SERVER, 'PHP_AUTH_USER'));
     $have_pass = strlen(idx($_SERVER, 'PHP_AUTH_PW'));
     if ($have_user && $have_pass) {
       $username = $_SERVER['PHP_AUTH_USER'];
       $password = new PhutilOpaqueEnvelope($_SERVER['PHP_AUTH_PW']);
 
       // Try Git LFS auth first since we can usually reject it without doing
       // any queries, since the username won't match the one we expect or the
       // request won't be LFS.
       $viewer = $this->authenticateGitLFSUser($username, $password);
 
       // If that failed, try normal auth. Note that we can use normal auth on
       // LFS requests, so this isn't strictly an alternative to LFS auth.
       if (!$viewer) {
         $viewer = $this->authenticateHTTPRepositoryUser($username, $password);
       }
 
       if (!$viewer) {
         return new PhabricatorVCSResponse(
           403,
           pht('Invalid credentials.'));
       }
     } else {
       // User hasn't provided credentials, which means we count them as
       // being "not logged in".
       $viewer = new PhabricatorUser();
     }
 
     $this->setServiceViewer($viewer);
 
     $allow_public = PhabricatorEnv::getEnvConfig('policy.allow-public');
     $allow_auth = PhabricatorEnv::getEnvConfig('diffusion.allow-http-auth');
     if (!$allow_public) {
       if (!$viewer->isLoggedIn()) {
         if ($allow_auth) {
           return new PhabricatorVCSResponse(
             401,
             pht('You must log in to access repositories.'));
         } else {
           return new PhabricatorVCSResponse(
             403,
             pht('Public and authenticated HTTP access are both forbidden.'));
         }
       }
     }
 
     try {
       $repository = id(new PhabricatorRepositoryQuery())
         ->setViewer($viewer)
         ->withIdentifiers(array($identifier))
+        ->needURIs(true)
         ->executeOne();
       if (!$repository) {
         return new PhabricatorVCSResponse(
           404,
           pht('No such repository exists.'));
       }
     } catch (PhabricatorPolicyException $ex) {
       if ($viewer->isLoggedIn()) {
         return new PhabricatorVCSResponse(
           403,
           pht('You do not have permission to access this repository.'));
       } else {
         if ($allow_auth) {
           return new PhabricatorVCSResponse(
             401,
             pht('You must log in to access this repository.'));
         } else {
           return new PhabricatorVCSResponse(
             403,
             pht(
               'This repository requires authentication, which is forbidden '.
               'over HTTP.'));
         }
       }
     }
 
     $response = $this->validateGitLFSRequest($repository, $viewer);
     if ($response) {
       return $response;
     }
 
     $this->setServiceRepository($repository);
 
     if (!$repository->isTracked()) {
       return new PhabricatorVCSResponse(
         403,
         pht('This repository is inactive.'));
     }
 
     $is_push = !$this->isReadOnlyRequest($repository);
 
     if ($this->getIsGitLFSRequest() && $this->getGitLFSToken()) {
       // We allow git LFS requests over HTTP even if the repository does not
       // otherwise support HTTP reads or writes, as long as the user is using a
       // token from SSH. If they're using HTTP username + password auth, they
       // have to obey the normal HTTP rules.
     } else {
-      switch ($repository->getServeOverHTTP()) {
-        case PhabricatorRepository::SERVE_READONLY:
-          if ($is_push) {
-            return new PhabricatorVCSResponse(
-              403,
-              pht('This repository is read-only over HTTP.'));
-          }
-          break;
-        case PhabricatorRepository::SERVE_READWRITE:
-          // We'll check for push capability below.
-          break;
-        case PhabricatorRepository::SERVE_OFF:
-        default:
+      // For now, we don't distinguish between HTTP and HTTPS-originated
+      // requests that are proxied within the cluster, so the user can connect
+      // with HTTPS but we may be on HTTP by the time we reach this part of
+      // the code. Allow things to move forward as long as either protocol
+      // can be served.
+      $proto_https = PhabricatorRepositoryURI::BUILTIN_PROTOCOL_HTTPS;
+      $proto_http = PhabricatorRepositoryURI::BUILTIN_PROTOCOL_HTTP;
+
+      $can_read =
+        $repository->canServeProtocol($proto_https, false) ||
+        $repository->canServeProtocol($proto_http, false);
+      if (!$can_read) {
+        return new PhabricatorVCSResponse(
+          403,
+          pht('This repository is not available over HTTP.'));
+      }
+
+      if ($is_push) {
+        $can_write =
+          $repository->canServeProtocol($proto_https, true) ||
+          $repository->canServeProtocol($proto_http, true);
+        if (!$can_write) {
           return new PhabricatorVCSResponse(
             403,
-            pht('This repository is not available over HTTP.'));
+            pht('This repository is read-only over HTTP.'));
+        }
       }
     }
 
     if ($is_push) {
       $can_push = PhabricatorPolicyFilter::hasCapability(
         $viewer,
         $repository,
         DiffusionPushCapability::CAPABILITY);
       if (!$can_push) {
         if ($viewer->isLoggedIn()) {
           $error_code = 403;
           $error_message = pht(
             'You do not have permission to push to this repository ("%s").',
             $repository->getDisplayName());
 
           if ($this->getIsGitLFSRequest()) {
             return DiffusionGitLFSResponse::newErrorResponse(
               $error_code,
               $error_message);
           } else {
             return new PhabricatorVCSResponse(
               $error_code,
               $error_message);
           }
         } else {
           if ($allow_auth) {
             return new PhabricatorVCSResponse(
               401,
               pht('You must log in to push to this repository.'));
           } else {
             return new PhabricatorVCSResponse(
               403,
               pht(
                 'Pushing to this repository requires authentication, '.
                 'which is forbidden over HTTP.'));
           }
         }
       }
     }
 
     $vcs_type = $repository->getVersionControlSystem();
     $req_type = $this->isVCSRequest($request);
 
     if ($vcs_type != $req_type) {
       switch ($req_type) {
         case PhabricatorRepositoryType::REPOSITORY_TYPE_GIT:
           $result = new PhabricatorVCSResponse(
             500,
             pht(
               'This repository ("%s") is not a Git repository.',
               $repository->getDisplayName()));
           break;
         case PhabricatorRepositoryType::REPOSITORY_TYPE_MERCURIAL:
           $result = new PhabricatorVCSResponse(
             500,
             pht(
               'This repository ("%s") is not a Mercurial repository.',
               $repository->getDisplayName()));
           break;
         case PhabricatorRepositoryType::REPOSITORY_TYPE_SVN:
           $result = new PhabricatorVCSResponse(
             500,
             pht(
               'This repository ("%s") is not a Subversion repository.',
               $repository->getDisplayName()));
           break;
         default:
           $result = new PhabricatorVCSResponse(
             500,
             pht('Unknown request type.'));
           break;
       }
     } else {
       switch ($vcs_type) {
         case PhabricatorRepositoryType::REPOSITORY_TYPE_GIT:
         case PhabricatorRepositoryType::REPOSITORY_TYPE_MERCURIAL:
           $result = $this->serveVCSRequest($repository, $viewer);
           break;
         case PhabricatorRepositoryType::REPOSITORY_TYPE_SVN:
           $result = new PhabricatorVCSResponse(
             500,
             pht(
               'Phabricator does not support HTTP access to Subversion '.
               'repositories.'));
           break;
         default:
           $result = new PhabricatorVCSResponse(
             500,
             pht('Unknown version control system.'));
           break;
       }
     }
 
     $code = $result->getHTTPResponseCode();
 
     if ($is_push && ($code == 200)) {
       $unguarded = AphrontWriteGuard::beginScopedUnguardedWrites();
         $repository->writeStatusMessage(
           PhabricatorRepositoryStatusMessage::TYPE_NEEDS_UPDATE,
           PhabricatorRepositoryStatusMessage::CODE_OKAY);
       unset($unguarded);
     }
 
     return $result;
   }
 
   private function serveVCSRequest(
     PhabricatorRepository $repository,
     PhabricatorUser $viewer) {
 
     // We can serve Git LFS requests first, since we don't need to proxy them.
     // It's also important that LFS requests never fall through to standard
     // service pathways, because that would let you use LFS tokens to read
     // normal repository data.
     if ($this->getIsGitLFSRequest()) {
       return $this->serveGitLFSRequest($repository, $viewer);
     }
 
     // If this repository is hosted on a service, we need to proxy the request
     // to a host which can serve it.
     $is_cluster_request = $this->getRequest()->isProxiedClusterRequest();
 
     $uri = $repository->getAlmanacServiceURI(
       $viewer,
       $is_cluster_request,
       array(
         'http',
         'https',
       ));
     if ($uri) {
       $future = $this->getRequest()->newClusterProxyFuture($uri);
       return id(new AphrontHTTPProxyResponse())
         ->setHTTPFuture($future);
     }
 
     // Otherwise, we're going to handle the request locally.
 
     $vcs_type = $repository->getVersionControlSystem();
     switch ($vcs_type) {
       case PhabricatorRepositoryType::REPOSITORY_TYPE_GIT:
         $result = $this->serveGitRequest($repository, $viewer);
         break;
       case PhabricatorRepositoryType::REPOSITORY_TYPE_MERCURIAL:
         $result = $this->serveMercurialRequest($repository, $viewer);
         break;
     }
 
     return $result;
   }
 
   private function isReadOnlyRequest(
     PhabricatorRepository $repository) {
     $request = $this->getRequest();
     $method = $_SERVER['REQUEST_METHOD'];
 
     // TODO: This implementation is safe by default, but very incomplete.
 
     if ($this->getIsGitLFSRequest()) {
       return $this->isGitLFSReadOnlyRequest($repository);
     }
 
     switch ($repository->getVersionControlSystem()) {
       case PhabricatorRepositoryType::REPOSITORY_TYPE_GIT:
         $service = $request->getStr('service');
         $path = $this->getRequestDirectoryPath($repository);
         // NOTE: Service names are the reverse of what you might expect, as they
         // are from the point of view of the server. The main read service is
         // "git-upload-pack", and the main write service is "git-receive-pack".
 
         if ($method == 'GET' &&
             $path == '/info/refs' &&
             $service == 'git-upload-pack') {
           return true;
         }
 
         if ($path == '/git-upload-pack') {
           return true;
         }
 
         break;
       case PhabricatorRepositoryType::REPOSITORY_TYPE_MERCURIAL:
         $cmd = $request->getStr('cmd');
         if ($cmd == 'batch') {
           $cmds = idx($this->getMercurialArguments(), 'cmds');
           return DiffusionMercurialWireProtocol::isReadOnlyBatchCommand($cmds);
         }
         return DiffusionMercurialWireProtocol::isReadOnlyCommand($cmd);
       case PhabricatorRepositoryType::REPOSITORY_TYPE_SVN:
         break;
     }
 
     return false;
   }
 
   /**
    * @phutil-external-symbol class PhabricatorStartup
    */
   private function serveGitRequest(
     PhabricatorRepository $repository,
     PhabricatorUser $viewer) {
     $request = $this->getRequest();
 
     $request_path = $this->getRequestDirectoryPath($repository);
     $repository_root = $repository->getLocalPath();
 
     // Rebuild the query string to strip `__magic__` parameters and prevent
     // issues where we might interpret inputs like "service=read&service=write"
     // differently than the server does and pass it an unsafe command.
 
     // NOTE: This does not use getPassthroughRequestParameters() because
     // that code is HTTP-method agnostic and will encode POST data.
 
     $query_data = $_GET;
     foreach ($query_data as $key => $value) {
       if (!strncmp($key, '__', 2)) {
         unset($query_data[$key]);
       }
     }
     $query_string = http_build_query($query_data, '', '&');
 
     // We're about to wipe out PATH with the rest of the environment, so
     // resolve the binary first.
     $bin = Filesystem::resolveBinary('git-http-backend');
     if (!$bin) {
       throw new Exception(
         pht(
           'Unable to find `%s` in %s!',
           'git-http-backend',
           '$PATH'));
     }
 
     // NOTE: We do not set HTTP_CONTENT_ENCODING here, because we already
     // decompressed the request when we read the request body, so the body is
     // just plain data with no encoding.
 
     $env = array(
       'REQUEST_METHOD' => $_SERVER['REQUEST_METHOD'],
       'QUERY_STRING' => $query_string,
       'CONTENT_TYPE' => $request->getHTTPHeader('Content-Type'),
       'REMOTE_ADDR' => $_SERVER['REMOTE_ADDR'],
       'GIT_PROJECT_ROOT' => $repository_root,
       'GIT_HTTP_EXPORT_ALL' => '1',
       'PATH_INFO' => $request_path,
 
       'REMOTE_USER' => $viewer->getUsername(),
 
       // TODO: Set these correctly.
       // GIT_COMMITTER_NAME
       // GIT_COMMITTER_EMAIL
     ) + $this->getCommonEnvironment($viewer);
 
     $input = PhabricatorStartup::getRawInput();
 
     $command = csprintf('%s', $bin);
     $command = PhabricatorDaemon::sudoCommandAsDaemonUser($command);
 
     $unguarded = AphrontWriteGuard::beginScopedUnguardedWrites();
 
     $cluster_engine = id(new DiffusionRepositoryClusterEngine())
       ->setViewer($viewer)
       ->setRepository($repository);
 
     $did_write_lock = false;
     if ($this->isReadOnlyRequest($repository)) {
       $cluster_engine->synchronizeWorkingCopyBeforeRead();
     } else {
       $did_write_lock = true;
       $cluster_engine->synchronizeWorkingCopyBeforeWrite();
     }
 
     $caught = null;
     try {
       list($err, $stdout, $stderr) = id(new ExecFuture('%C', $command))
         ->setEnv($env, true)
         ->write($input)
         ->resolve();
     } catch (Exception $ex) {
       $caught = $ex;
     }
 
     if ($did_write_lock) {
       $cluster_engine->synchronizeWorkingCopyAfterWrite();
     }
 
     unset($unguarded);
 
     if ($caught) {
       throw $caught;
     }
 
     if ($err) {
       if ($this->isValidGitShallowCloneResponse($stdout, $stderr)) {
         // Ignore the error if the response passes this special check for
         // validity.
         $err = 0;
       }
     }
 
     if ($err) {
       return new PhabricatorVCSResponse(
         500,
         pht(
           'Error %d: %s',
           $err,
           phutil_utf8ize($stderr)));
     }
 
     return id(new DiffusionGitResponse())->setGitData($stdout);
   }
 
   private function getRequestDirectoryPath(PhabricatorRepository $repository) {
     $request = $this->getRequest();
     $request_path = $request->getRequestURI()->getPath();
 
     $info = PhabricatorRepository::parseRepositoryServicePath($request_path);
     $base_path = $info['path'];
 
     // For Git repositories, strip an optional directory component if it
     // isn't the name of a known Git resource. This allows users to clone
     // repositories as "/diffusion/X/anything.git", for example.
     if ($repository->isGit()) {
       $known = array(
         'info',
         'git-upload-pack',
         'git-receive-pack',
       );
 
       foreach ($known as $key => $path) {
         $known[$key] = preg_quote($path, '@');
       }
 
       $known = implode('|', $known);
 
       if (preg_match('@^/([^/]+)/('.$known.')(/|$)@', $base_path)) {
         $base_path = preg_replace('@^/([^/]+)@', '', $base_path);
       }
     }
 
     return $base_path;
   }
 
   private function authenticateGitLFSUser(
     $username,
     PhutilOpaqueEnvelope $password) {
 
     // Never accept these credentials for requests which aren't LFS requests.
     if (!$this->getIsGitLFSRequest()) {
       return null;
     }
 
     // If we have the wrong username, don't bother checking if the token
     // is right.
     if ($username !== DiffusionGitLFSTemporaryTokenType::HTTP_USERNAME) {
       return null;
     }
 
     $lfs_pass = $password->openEnvelope();
     $lfs_hash = PhabricatorHash::digest($lfs_pass);
 
     $token = id(new PhabricatorAuthTemporaryTokenQuery())
       ->setViewer(PhabricatorUser::getOmnipotentUser())
       ->withTokenTypes(array(DiffusionGitLFSTemporaryTokenType::TOKENTYPE))
       ->withTokenCodes(array($lfs_hash))
       ->withExpired(false)
       ->executeOne();
     if (!$token) {
       return null;
     }
 
     $user = id(new PhabricatorPeopleQuery())
       ->setViewer(PhabricatorUser::getOmnipotentUser())
       ->withPHIDs(array($token->getUserPHID()))
       ->executeOne();
 
     if (!$user) {
       return null;
     }
 
     if (!$user->isUserActivated()) {
       return null;
     }
 
     $this->gitLFSToken = $token;
 
     return $user;
   }
 
   private function authenticateHTTPRepositoryUser(
     $username,
     PhutilOpaqueEnvelope $password) {
 
     if (!PhabricatorEnv::getEnvConfig('diffusion.allow-http-auth')) {
       // No HTTP auth permitted.
       return null;
     }
 
     if (!strlen($username)) {
       // No username.
       return null;
     }
 
     if (!strlen($password->openEnvelope())) {
       // No password.
       return null;
     }
 
     $user = id(new PhabricatorPeopleQuery())
       ->setViewer(PhabricatorUser::getOmnipotentUser())
       ->withUsernames(array($username))
       ->executeOne();
     if (!$user) {
       // Username doesn't match anything.
       return null;
     }
 
     if (!$user->isUserActivated()) {
       // User is not activated.
       return null;
     }
 
     $password_entry = id(new PhabricatorRepositoryVCSPassword())
       ->loadOneWhere('userPHID = %s', $user->getPHID());
     if (!$password_entry) {
       // User doesn't have a password set.
       return null;
     }
 
     if (!$password_entry->comparePassword($password, $user)) {
       // Password doesn't match.
       return null;
     }
 
     // If the user's password is stored using a less-than-optimal hash, upgrade
     // them to the strongest available hash.
 
     $hash_envelope = new PhutilOpaqueEnvelope(
       $password_entry->getPasswordHash());
     if (PhabricatorPasswordHasher::canUpgradeHash($hash_envelope)) {
       $password_entry->setPassword($password, $user);
       $unguarded = AphrontWriteGuard::beginScopedUnguardedWrites();
         $password_entry->save();
       unset($unguarded);
     }
 
     return $user;
   }
 
   private function serveMercurialRequest(
     PhabricatorRepository $repository,
     PhabricatorUser $viewer) {
     $request = $this->getRequest();
 
     $bin = Filesystem::resolveBinary('hg');
     if (!$bin) {
       throw new Exception(
         pht(
           'Unable to find `%s` in %s!',
           'hg',
           '$PATH'));
     }
 
     $env = $this->getCommonEnvironment($viewer);
     $input = PhabricatorStartup::getRawInput();
 
     $cmd = $request->getStr('cmd');
 
     $args = $this->getMercurialArguments();
     $args = $this->formatMercurialArguments($cmd, $args);
 
     if (strlen($input)) {
       $input = strlen($input)."\n".$input."0\n";
     }
 
     $command = csprintf('%s serve --stdio', $bin);
     $command = PhabricatorDaemon::sudoCommandAsDaemonUser($command);
 
     list($err, $stdout, $stderr) = id(new ExecFuture('%C', $command))
       ->setEnv($env, true)
       ->setCWD($repository->getLocalPath())
       ->write("{$cmd}\n{$args}{$input}")
       ->resolve();
 
     if ($err) {
       return new PhabricatorVCSResponse(
         500,
         pht('Error %d: %s', $err, $stderr));
     }
 
     if ($cmd == 'getbundle' ||
         $cmd == 'changegroup' ||
         $cmd == 'changegroupsubset') {
       // We're not completely sure that "changegroup" and "changegroupsubset"
       // actually work, they're for very old Mercurial.
       $body = gzcompress($stdout);
     } else if ($cmd == 'unbundle') {
       // This includes diagnostic information and anything echoed by commit
       // hooks. We ignore `stdout` since it just has protocol garbage, and
       // substitute `stderr`.
       $body = strlen($stderr)."\n".$stderr;
     } else {
       list($length, $body) = explode("\n", $stdout, 2);
       if ($cmd == 'capabilities') {
         $body = DiffusionMercurialWireProtocol::filterBundle2Capability($body);
       }
     }
 
     return id(new DiffusionMercurialResponse())->setContent($body);
   }
 
   private function getMercurialArguments() {
     // Mercurial sends arguments in HTTP headers. "Why?", you might wonder,
     // "Why would you do this?".
 
     $args_raw = array();
     for ($ii = 1;; $ii++) {
       $header = 'HTTP_X_HGARG_'.$ii;
       if (!array_key_exists($header, $_SERVER)) {
         break;
       }
       $args_raw[] = $_SERVER[$header];
     }
     $args_raw = implode('', $args_raw);
 
     return id(new PhutilQueryStringParser())
       ->parseQueryString($args_raw);
   }
 
   private function formatMercurialArguments($command, array $arguments) {
     $spec = DiffusionMercurialWireProtocol::getCommandArgs($command);
 
     $out = array();
 
     // Mercurial takes normal arguments like this:
     //
     //   name <length(value)>
     //   value
 
     $has_star = false;
     foreach ($spec as $arg_key) {
       if ($arg_key == '*') {
         $has_star = true;
         continue;
       }
       if (isset($arguments[$arg_key])) {
         $value = $arguments[$arg_key];
         $size = strlen($value);
         $out[] = "{$arg_key} {$size}\n{$value}";
         unset($arguments[$arg_key]);
       }
     }
 
     if ($has_star) {
 
       // Mercurial takes arguments for variable argument lists roughly like
       // this:
       //
       //   * <count(args)>
       //   argname1 <length(argvalue1)>
       //   argvalue1
       //   argname2 <length(argvalue2)>
       //   argvalue2
 
       $count = count($arguments);
 
       $out[] = "* {$count}\n";
 
       foreach ($arguments as $key => $value) {
         if (in_array($key, $spec)) {
           // We already added this argument above, so skip it.
           continue;
         }
         $size = strlen($value);
         $out[] = "{$key} {$size}\n{$value}";
       }
     }
 
     return implode('', $out);
   }
 
   private function isValidGitShallowCloneResponse($stdout, $stderr) {
     // If you execute `git clone --depth N ...`, git sends a request which
     // `git-http-backend` responds to by emitting valid output and then exiting
     // with a failure code and an error message. If we ignore this error,
     // everything works.
 
     // This is a pretty funky fix: it would be nice to more precisely detect
     // that a request is a `--depth N` clone request, but we don't have any code
     // to decode protocol frames yet. Instead, look for reasonable evidence
     // in the error and output that we're looking at a `--depth` clone.
 
     // For evidence this isn't completely crazy, see:
     // https://github.com/schacon/grack/pull/7
 
     $stdout_regexp = '(^Content-Type: application/x-git-upload-pack-result)m';
     $stderr_regexp = '(The remote end hung up unexpectedly)';
 
     $has_pack = preg_match($stdout_regexp, $stdout);
     $is_hangup = preg_match($stderr_regexp, $stderr);
 
     return $has_pack && $is_hangup;
   }
 
   private function getCommonEnvironment(PhabricatorUser $viewer) {
     $remote_address = $this->getRequest()->getRemoteAddress();
 
     return array(
       DiffusionCommitHookEngine::ENV_USER => $viewer->getUsername(),
       DiffusionCommitHookEngine::ENV_REMOTE_ADDRESS => $remote_address,
       DiffusionCommitHookEngine::ENV_REMOTE_PROTOCOL => 'http',
     );
   }
 
   private function validateGitLFSRequest(
     PhabricatorRepository $repository,
     PhabricatorUser $viewer) {
     if (!$this->getIsGitLFSRequest()) {
       return null;
     }
 
     if (!$repository->canUseGitLFS()) {
       return new PhabricatorVCSResponse(
         403,
         pht(
           'The requested repository ("%s") does not support Git LFS.',
           $repository->getDisplayName()));
     }
 
     // If this is using an LFS token, sanity check that we're using it on the
     // correct repository. This shouldn't really matter since the user could
     // just request a proper token anyway, but it suspicious and should not
     // be permitted.
 
     $token = $this->getGitLFSToken();
     if ($token) {
       $resource = $token->getTokenResource();
       if ($resource !== $repository->getPHID()) {
         return new PhabricatorVCSResponse(
           403,
           pht(
             'The authentication token provided in the request is bound to '.
             'a different repository than the requested repository ("%s").',
             $repository->getDisplayName()));
       }
     }
 
     return null;
   }
 
   private function serveGitLFSRequest(
     PhabricatorRepository $repository,
     PhabricatorUser $viewer) {
 
     if (!$this->getIsGitLFSRequest()) {
       throw new Exception(pht('This is not a Git LFS request!'));
     }
 
     $path = $this->getGitLFSRequestPath($repository);
     $matches = null;
 
     if (preg_match('(^upload/(.*)\z)', $path, $matches)) {
       $oid = $matches[1];
       return $this->serveGitLFSUploadRequest($repository, $viewer, $oid);
     } else if ($path == 'objects/batch') {
       return $this->serveGitLFSBatchRequest($repository, $viewer);
     } else {
       return DiffusionGitLFSResponse::newErrorResponse(
         404,
         pht(
           'Git LFS operation "%s" is not supported by this server.',
           $path));
     }
   }
 
   private function serveGitLFSBatchRequest(
     PhabricatorRepository $repository,
     PhabricatorUser $viewer) {
 
     $input = $this->getGitLFSInput();
 
     $operation = idx($input, 'operation');
     switch ($operation) {
       case 'upload':
         $want_upload = true;
         break;
       case 'download':
         $want_upload = false;
         break;
       default:
         return DiffusionGitLFSResponse::newErrorResponse(
           404,
           pht(
             'Git LFS batch operation "%s" is not supported by this server.',
             $operation));
     }
 
     $objects = idx($input, 'objects', array());
 
     $hashes = array();
     foreach ($objects as $object) {
       $hashes[] = idx($object, 'oid');
     }
 
     if ($hashes) {
       $refs = id(new PhabricatorRepositoryGitLFSRefQuery())
         ->setViewer($viewer)
         ->withRepositoryPHIDs(array($repository->getPHID()))
         ->withObjectHashes($hashes)
         ->execute();
       $refs = mpull($refs, null, 'getObjectHash');
     } else {
       $refs = array();
     }
 
     $file_phids = mpull($refs, 'getFilePHID');
     if ($file_phids) {
       $files = id(new PhabricatorFileQuery())
         ->setViewer($viewer)
         ->withPHIDs($file_phids)
         ->execute();
       $files = mpull($files, null, 'getPHID');
     } else {
       $files = array();
     }
 
     $authorization = null;
     $output = array();
     foreach ($objects as $object) {
       $oid = idx($object, 'oid');
       $size = idx($object, 'size');
       $ref = idx($refs, $oid);
       $error = null;
 
       // NOTE: If we already have a ref for this object, we only emit a
       // "download" action. The client should not upload the file again.
 
       $actions = array();
       if ($ref) {
         $file = idx($files, $ref->getFilePHID());
         if ($file) {
           // Git LFS may prompt users for authentication if the action does
           // not provide an "Authorization" header and does not have a query
           // parameter named "token". See here for discussion:
           // <https://github.com/github/git-lfs/issues/1088>
           $no_authorization = 'Basic '.base64_encode('none');
 
           $get_uri = $file->getCDNURI();
           $actions['download'] = array(
             'href' => $get_uri,
             'header' => array(
               'Authorization' => $no_authorization,
               'X-Phabricator-Request-Type' => 'git-lfs',
             ),
           );
         } else {
           $error = array(
             'code' => 404,
             'message' => pht(
               'Object "%s" was previously uploaded, but no longer exists '.
               'on this server.',
               $oid),
           );
         }
       } else if ($want_upload) {
         if (!$authorization) {
           // Here, we could reuse the existing authorization if we have one,
           // but it's a little simpler to just generate a new one
           // unconditionally.
           $authorization = $this->newGitLFSHTTPAuthorization(
             $repository,
             $viewer,
             $operation);
         }
 
         $put_uri = $repository->getGitLFSURI("info/lfs/upload/{$oid}");
 
         $actions['upload'] = array(
           'href' => $put_uri,
           'header' => array(
             'Authorization' => $authorization,
             'X-Phabricator-Request-Type' => 'git-lfs',
           ),
         );
       }
 
       $object = array(
         'oid' => $oid,
         'size' => $size,
       );
 
       if ($actions) {
         $object['actions'] = $actions;
       }
 
       if ($error) {
         $object['error'] = $error;
       }
 
       $output[] = $object;
     }
 
     $output = array(
       'objects' => $output,
     );
 
     return id(new DiffusionGitLFSResponse())
       ->setContent($output);
   }
 
   private function serveGitLFSUploadRequest(
     PhabricatorRepository $repository,
     PhabricatorUser $viewer,
     $oid) {
 
     $ref = id(new PhabricatorRepositoryGitLFSRefQuery())
       ->setViewer($viewer)
       ->withRepositoryPHIDs(array($repository->getPHID()))
       ->withObjectHashes(array($oid))
       ->executeOne();
     if ($ref) {
       return DiffusionGitLFSResponse::newErrorResponse(
         405,
         pht(
           'Content for object "%s" is already known to this server. It can '.
           'not be uploaded again.',
           $oid));
     }
 
     // Remove the execution time limit because uploading large files may take
     // a while.
     set_time_limit(0);
 
     $request_stream = new AphrontRequestStream();
     $request_iterator = $request_stream->getIterator();
     $hashing_iterator = id(new PhutilHashingIterator($request_iterator))
       ->setAlgorithm('sha256');
 
     $source = id(new PhabricatorIteratorFileUploadSource())
       ->setName('lfs-'.$oid)
       ->setViewPolicy(PhabricatorPolicies::POLICY_NOONE)
       ->setIterator($hashing_iterator);
 
     $unguarded = AphrontWriteGuard::beginScopedUnguardedWrites();
       $file = $source->uploadFile();
     unset($unguarded);
 
     $hash = $hashing_iterator->getHash();
     if ($hash !== $oid) {
       return DiffusionGitLFSResponse::newErrorResponse(
         400,
         pht(
           'Uploaded data is corrupt or invalid. Expected hash "%s", actual '.
           'hash "%s".',
           $oid,
           $hash));
     }
 
     $ref = id(new PhabricatorRepositoryGitLFSRef())
       ->setRepositoryPHID($repository->getPHID())
       ->setObjectHash($hash)
       ->setByteSize($file->getByteSize())
       ->setAuthorPHID($viewer->getPHID())
       ->setFilePHID($file->getPHID());
 
     $unguarded = AphrontWriteGuard::beginScopedUnguardedWrites();
       // Attach the file to the repository to give users permission
       // to access it.
       $file->attachToObject($repository->getPHID());
       $ref->save();
     unset($unguarded);
 
     // This is just a plain HTTP 200 with no content, which is what `git lfs`
     // expects.
     return new DiffusionGitLFSResponse();
   }
 
   private function newGitLFSHTTPAuthorization(
     PhabricatorRepository $repository,
     PhabricatorUser $viewer,
     $operation) {
 
     $unguarded = AphrontWriteGuard::beginScopedUnguardedWrites();
 
     $authorization = DiffusionGitLFSTemporaryTokenType::newHTTPAuthorization(
       $repository,
       $viewer,
       $operation);
 
     unset($unguarded);
 
     return $authorization;
   }
 
   private function getGitLFSRequestPath(PhabricatorRepository $repository) {
     $request_path = $this->getRequestDirectoryPath($repository);
 
     $matches = null;
     if (preg_match('(^/info/lfs(?:\z|/)(.*))', $request_path, $matches)) {
       return $matches[1];
     }
 
     return null;
   }
 
   private function getGitLFSInput() {
     if (!$this->gitLFSInput) {
       $input = PhabricatorStartup::getRawInput();
       $input = phutil_json_decode($input);
       $this->gitLFSInput = $input;
     }
 
     return $this->gitLFSInput;
   }
 
   private function isGitLFSReadOnlyRequest(PhabricatorRepository $repository) {
     if (!$this->getIsGitLFSRequest()) {
       return false;
     }
 
     $path = $this->getGitLFSRequestPath($repository);
 
     if ($path === 'objects/batch') {
       $input = $this->getGitLFSInput();
       $operation = idx($input, 'operation');
       switch ($operation) {
         case 'download':
           return true;
         default:
           return false;
       }
     }
 
     return false;
   }
 
 
 }
diff --git a/src/applications/diffusion/data/DiffusionLocalRepositoryFilter.php b/src/applications/diffusion/data/DiffusionLocalRepositoryFilter.php
new file mode 100644
index 000000000..b44335220
--- /dev/null
+++ b/src/applications/diffusion/data/DiffusionLocalRepositoryFilter.php
@@ -0,0 +1,184 @@
+<?php
+
+/**
+ * Filter a list of repositories, removing repositories not local to the
+ * current device.
+ */
+final class DiffusionLocalRepositoryFilter extends Phobject {
+
+  private $viewer;
+  private $device;
+  private $repositories;
+  private $rejectionReasons;
+
+  public function setViewer(PhabricatorUser $viewer) {
+    $this->viewer = $viewer;
+    return $this;
+  }
+
+  public function getViewer() {
+    return $this->viewer;
+  }
+
+  public function setDevice(AlmanacDevice $device = null) {
+    $this->device = $device;
+    return $this;
+  }
+
+  public function getDevice() {
+    return $this->device;
+  }
+
+  public function setRepositories(array $repositories) {
+    $this->repositories = $repositories;
+    return $this;
+  }
+
+  public function getRepositories() {
+    return $this->repositories;
+  }
+
+  public function setRejectionReasons($rejection_reasons) {
+    $this->rejectionReasons = $rejection_reasons;
+    return $this;
+  }
+
+  public function getRejectionReasons() {
+    return $this->rejectionReasons;
+  }
+
+  public function execute() {
+    $repositories = $this->getRepositories();
+    $device = $this->getDevice();
+    $viewer = $this->getViewer();
+
+    $reasons = array();
+
+    $service_phids = array();
+    foreach ($repositories as $key => $repository) {
+      $service_phid = $repository->getAlmanacServicePHID();
+
+      // If the repository is bound to a service but this host is not a
+      // recognized device, or vice versa, don't pull the repository unless
+      // we're sure it's safe because the repository has no local working copy
+      // or the working copy already exists on disk.
+      $is_cluster_repo = (bool)$service_phid;
+      $is_cluster_device = (bool)$device;
+      if ($is_cluster_repo != $is_cluster_device) {
+        $has_working_copy = $repository->hasLocalWorkingCopy();
+        if ($is_cluster_device) {
+          if (!$has_working_copy) {
+            $reasons[$key] = pht(
+              'Repository "%s" is not a cluster repository, but the current '.
+              'host is a cluster device ("%s") and updating this repository '.
+              'would create a new local working copy. This is dangerous, so '.
+              'the repository will not be updated on this host.',
+              $repository->getDisplayName(),
+              $device->getName());
+            unset($repositories[$key]);
+            continue;
+          }
+        } else {
+          $reasons[$key] = pht(
+            'Repository "%s" is a cluster repository, but the current host '.
+            'is not a cluster device (it has no device ID), so the '.
+            'repository will not be updated on this host.',
+            $repository->getDisplayName());
+          unset($repositories[$key]);
+          continue;
+        }
+      }
+
+      if ($service_phid) {
+        $service_phids[] = $service_phid;
+      }
+    }
+
+    if (!$device) {
+      $this->rejectionReasons = $reasons;
+      return $repositories;
+    }
+
+    $device_phid = $device->getPHID();
+
+    if ($service_phids) {
+      // We could include `withDevicePHIDs()` here to pull a smaller result
+      // set, but we can provide more helpful diagnostic messages below if
+      // we fetch a little more data.
+      $services = id(new AlmanacServiceQuery())
+        ->setViewer($viewer)
+        ->withPHIDs($service_phids)
+        ->withServiceTypes(
+          array(
+            AlmanacClusterRepositoryServiceType::SERVICETYPE,
+          ))
+        ->needBindings(true)
+        ->execute();
+      $services = mpull($services, null, 'getPHID');
+    } else {
+      $services = array();
+    }
+
+    foreach ($repositories as $key => $repository) {
+      $service_phid = $repository->getAlmanacServicePHID();
+
+      if (!$service_phid) {
+        continue;
+      }
+
+      $service = idx($services, $service_phid);
+      if (!$service) {
+        $reasons[$key] = pht(
+          'Repository "%s" is on cluster service "%s", but that service '.
+          'could not be loaded, so the repository will not be updated on '.
+          'this host.',
+          $repository->getDisplayName(),
+          $service_phid);
+        unset($repositories[$key]);
+        continue;
+      }
+
+      $bindings = $service->getBindings();
+      $bindings = mgroup($bindings, 'getDevicePHID');
+      $bindings = idx($bindings, $device_phid);
+      if (!$bindings) {
+        $reasons[$key] = pht(
+          'Repository "%s" is on cluster service "%s", but that service is '.
+          'not bound to this device ("%s"), so the repository will not be '.
+          'updated on this host.',
+          $repository->getDisplayName(),
+          $service->getName(),
+          $device->getName());
+        unset($repositories[$key]);
+        continue;
+      }
+
+      $all_disabled = true;
+      foreach ($bindings as $binding) {
+        if (!$binding->getIsDisabled()) {
+          $all_disabled = false;
+          break;
+        }
+      }
+
+      if ($all_disabled) {
+        $reasons[$key] = pht(
+          'Repository "%s" is on cluster service "%s", but the binding '.
+          'between that service and this device ("%s") is disabled, so it '.
+          'can not be updated on this host.',
+          $repository->getDisplayName(),
+          $service->getName(),
+          $device->getName());
+        unset($repositories[$key]);
+        continue;
+      }
+
+      // We have a valid service that is actively bound to the current host
+      // device, so we're good to go.
+    }
+
+    $this->rejectionReasons = $reasons;
+    return $repositories;
+  }
+
+}
diff --git a/src/applications/diffusion/editor/DiffusionRepositoryEditEngine.php b/src/applications/diffusion/editor/DiffusionRepositoryEditEngine.php
index 1ebbabe2e..933cdbb59 100644
--- a/src/applications/diffusion/editor/DiffusionRepositoryEditEngine.php
+++ b/src/applications/diffusion/editor/DiffusionRepositoryEditEngine.php
@@ -1,302 +1,447 @@
 <?php
 
 final class DiffusionRepositoryEditEngine
   extends PhabricatorEditEngine {
 
   const ENGINECONST = 'diffusion.repository';
 
   private $versionControlSystem;
 
   public function setVersionControlSystem($version_control_system) {
     $this->versionControlSystem = $version_control_system;
     return $this;
   }
 
   public function getVersionControlSystem() {
     return $this->versionControlSystem;
   }
 
   public function isEngineConfigurable() {
     return false;
   }
 
   public function getEngineName() {
     return pht('Repositories');
   }
 
   public function getSummaryHeader() {
     return pht('Edit Repositories');
   }
 
   public function getSummaryText() {
     return pht('Creates and edits repositories.');
   }
 
   public function getEngineApplicationClass() {
     return 'PhabricatorDiffusionApplication';
   }
 
   protected function newEditableObject() {
     $viewer = $this->getViewer();
     $repository = PhabricatorRepository::initializeNewRepository($viewer);
 
+    $repository->setDetail('newly-initialized', true);
+
     $vcs = $this->getVersionControlSystem();
     if ($vcs) {
       $repository->setVersionControlSystem($vcs);
     }
 
+    // Pick a random open service to allocate this repository on, if any exist.
+    // If there are no services, we aren't in cluster mode and will allocate
+    // locally. If there are services but none permit allocations, we fail.
+
+    // Eventually we can make this more flexible, but this rule is a reasonable
+    // starting point as we begin to deploy cluster services.
+
+    $services = id(new AlmanacServiceQuery())
+      ->setViewer(PhabricatorUser::getOmnipotentUser())
+      ->withServiceTypes(
+        array(
+          AlmanacClusterRepositoryServiceType::SERVICETYPE,
+        ))
+      ->needProperties(true)
+      ->execute();
+    if ($services) {
+      // Filter out services which do not permit new allocations.
+      foreach ($services as $key => $possible_service) {
+        if ($possible_service->getAlmanacPropertyValue('closed')) {
+          unset($services[$key]);
+        }
+      }
+
+      if (!$services) {
+        throw new Exception(
+          pht(
+            'This install is configured in cluster mode, but all available '.
+            'repository cluster services are closed to new allocations. '.
+            'At least one service must be open to allow new allocations to '.
+            'take place.'));
+      }
+
+      shuffle($services);
+      $service = head($services);
+
+      $repository->setAlmanacServicePHID($service->getPHID());
+    }
+
     return $repository;
   }
 
   protected function newObjectQuery() {
     return new PhabricatorRepositoryQuery();
   }
 
   protected function getObjectCreateTitleText($object) {
     return pht('Create Repository');
   }
 
   protected function getObjectCreateButtonText($object) {
     return pht('Create Repository');
   }
 
   protected function getObjectEditTitleText($object) {
     return pht('Edit Repository: %s', $object->getName());
   }
 
   protected function getObjectEditShortText($object) {
     return $object->getDisplayName();
   }
 
   protected function getObjectCreateShortText() {
     return pht('Create Repository');
   }
 
   protected function getObjectName() {
     return pht('Repository');
   }
 
   protected function getObjectViewURI($object) {
     return $object->getPathURI('manage/');
   }
 
   protected function getCreateNewObjectPolicy() {
     return $this->getApplication()->getPolicy(
       DiffusionCreateRepositoriesCapability::CAPABILITY);
   }
 
+  protected function newPages($object) {
+    $panels = DiffusionRepositoryManagementPanel::getAllPanels();
+
+    $pages = array();
+    $uris = array();
+    foreach ($panels as $panel_key => $panel) {
+      $panel->setRepository($object);
+
+      $uris[$panel_key] = $panel->getPanelURI();
+
+      $page = $panel->newEditEnginePage();
+      if (!$page) {
+        continue;
+      }
+      $pages[] = $page;
+    }
+
+    $basics_key = DiffusionRepositoryBasicsManagementPanel::PANELKEY;
+    $basics_uri = $uris[$basics_key];
+
+    $more_pages = array(
+      id(new PhabricatorEditPage())
+        ->setKey('encoding')
+        ->setLabel(pht('Text Encoding'))
+        ->setViewURI($basics_uri)
+        ->setFieldKeys(
+          array(
+            'encoding',
+          )),
+      id(new PhabricatorEditPage())
+        ->setKey('extensions')
+        ->setLabel(pht('Extensions'))
+        ->setIsDefault(true),
+    );
+
+    foreach ($more_pages as $page) {
+      $pages[] = $page;
+    }
+
+    return $pages;
+  }
+
+  protected function willConfigureFields($object, array $fields) {
+    // Change the default field order so related fields are adjacent.
+    $after = array(
+      'policy.edit' => array('policy.push'),
+    );
+
+    $result = array();
+    foreach ($fields as $key => $value) {
+      $result[$key] = $value;
+
+      if (!isset($after[$key])) {
+        continue;
+      }
+
+      foreach ($after[$key] as $next_key) {
+        if (!isset($fields[$next_key])) {
+          continue;
+        }
+
+        unset($result[$next_key]);
+        $result[$next_key] = $fields[$next_key];
+        unset($fields[$next_key]);
+      }
+    }
+
+    return $result;
+  }
+
+
   protected function buildCustomEditFields($object) {
     $viewer = $this->getViewer();
 
     $policies = id(new PhabricatorPolicyQuery())
       ->setViewer($viewer)
       ->setObject($object)
       ->execute();
 
     $track_value = $object->getDetail('branch-filter', array());
     $track_value = array_keys($track_value);
 
     $autoclose_value = $object->getDetail('close-commits-filter', array());
     $autoclose_value = array_keys($autoclose_value);
 
+    $automation_instructions = pht(
+      "Configure **Repository Automation** to allow Phabricator to ".
+      "write to this repository.".
+      "\n\n".
+      "IMPORTANT: This feature is new, experimental, and not supported. ".
+      "Use it at your own risk.");
+
+    $staging_instructions = pht(
+      "To make it easier to run integration tests and builds on code ".
+      "under review, you can configure a **Staging Area**. When `arc` ".
+      "creates a diff, it will push a copy of the changes to the ".
+      "configured staging area with a corresponding tag.".
+      "\n\n".
+      "IMPORTANT: This feature is new, experimental, and not supported. ".
+      "Use it at your own risk.");
+
+    $subpath_instructions = pht(
+      'If you want to import only part of a repository, like `trunk/`, '.
+      'you can set a path in **Import Only**. Phabricator will ignore '.
+      'commits which do not affect this path.');
+
     return array(
       id(new PhabricatorSelectEditField())
         ->setKey('vcs')
         ->setLabel(pht('Version Control System'))
         ->setTransactionType(PhabricatorRepositoryTransaction::TYPE_VCS)
         ->setIsConduitOnly(true)
         ->setIsCopyable(true)
         ->setOptions(PhabricatorRepositoryType::getAllRepositoryTypes())
         ->setDescription(pht('Underlying repository version control system.'))
         ->setConduitDescription(
           pht(
             'Choose which version control system to use when creating a '.
             'repository.'))
         ->setConduitTypeDescription(pht('Version control system selection.'))
         ->setValue($object->getVersionControlSystem()),
       id(new PhabricatorTextEditField())
         ->setKey('name')
         ->setLabel(pht('Name'))
         ->setIsRequired(true)
         ->setTransactionType(PhabricatorRepositoryTransaction::TYPE_NAME)
         ->setDescription(pht('The repository name.'))
         ->setConduitDescription(pht('Rename the repository.'))
         ->setConduitTypeDescription(pht('New repository name.'))
         ->setValue($object->getName()),
       id(new PhabricatorTextEditField())
         ->setKey('callsign')
         ->setLabel(pht('Callsign'))
         ->setTransactionType(PhabricatorRepositoryTransaction::TYPE_CALLSIGN)
         ->setDescription(pht('The repository callsign.'))
         ->setConduitDescription(pht('Change the repository callsign.'))
         ->setConduitTypeDescription(pht('New repository callsign.'))
         ->setValue($object->getCallsign()),
       id(new PhabricatorTextEditField())
         ->setKey('shortName')
         ->setLabel(pht('Short Name'))
         ->setTransactionType(PhabricatorRepositoryTransaction::TYPE_SLUG)
         ->setDescription(pht('Short, unique repository name.'))
         ->setConduitDescription(pht('Change the repository short name.'))
         ->setConduitTypeDescription(pht('New short name for the repository.'))
         ->setValue($object->getRepositorySlug()),
       id(new PhabricatorRemarkupEditField())
         ->setKey('description')
         ->setLabel(pht('Description'))
         ->setTransactionType(PhabricatorRepositoryTransaction::TYPE_DESCRIPTION)
         ->setDescription(pht('Repository description.'))
         ->setConduitDescription(pht('Change the repository description.'))
         ->setConduitTypeDescription(pht('New repository description.'))
         ->setValue($object->getDetail('description')),
       id(new PhabricatorTextEditField())
         ->setKey('encoding')
         ->setLabel(pht('Text Encoding'))
         ->setIsCopyable(true)
         ->setTransactionType(PhabricatorRepositoryTransaction::TYPE_ENCODING)
         ->setDescription(pht('Default text encoding.'))
         ->setConduitDescription(pht('Change the default text encoding.'))
         ->setConduitTypeDescription(pht('New text encoding.'))
         ->setValue($object->getDetail('encoding')),
       id(new PhabricatorBoolEditField())
         ->setKey('allowDangerousChanges')
         ->setLabel(pht('Allow Dangerous Changes'))
         ->setIsCopyable(true)
         ->setIsConduitOnly(true)
         ->setOptions(
           pht('Prevent Dangerous Changes'),
           pht('Allow Dangerous Changes'))
         ->setTransactionType(PhabricatorRepositoryTransaction::TYPE_DANGEROUS)
         ->setDescription(pht('Permit dangerous changes to be made.'))
         ->setConduitDescription(pht('Allow or prevent dangerous changes.'))
         ->setConduitTypeDescription(pht('New protection setting.'))
         ->setValue($object->shouldAllowDangerousChanges()),
       id(new PhabricatorSelectEditField())
         ->setKey('status')
         ->setLabel(pht('Status'))
         ->setTransactionType(PhabricatorRepositoryTransaction::TYPE_ACTIVATE)
         ->setIsConduitOnly(true)
         ->setOptions(PhabricatorRepository::getStatusNameMap())
         ->setDescription(pht('Active or inactive status.'))
         ->setConduitDescription(pht('Active or deactivate the repository.'))
         ->setConduitTypeDescription(pht('New repository status.'))
         ->setValue($object->getStatus()),
       id(new PhabricatorTextEditField())
         ->setKey('defaultBranch')
         ->setLabel(pht('Default Branch'))
         ->setTransactionType(
           PhabricatorRepositoryTransaction::TYPE_DEFAULT_BRANCH)
         ->setIsCopyable(true)
         ->setDescription(pht('Default branch name.'))
         ->setConduitDescription(pht('Set the default branch name.'))
         ->setConduitTypeDescription(pht('New default branch name.'))
         ->setValue($object->getDetail('default-branch')),
       id(new PhabricatorTextAreaEditField())
         ->setIsStringList(true)
         ->setKey('trackOnly')
         ->setLabel(pht('Track Only'))
         ->setTransactionType(
           PhabricatorRepositoryTransaction::TYPE_TRACK_ONLY)
         ->setIsCopyable(true)
         ->setDescription(pht('Track only these branches.'))
         ->setConduitDescription(pht('Set the tracked branches.'))
         ->setConduitTypeDescription(pht('New tracked branchs.'))
         ->setValue($track_value),
       id(new PhabricatorTextAreaEditField())
         ->setIsStringList(true)
         ->setKey('autocloseOnly')
         ->setLabel(pht('Autoclose Only'))
         ->setTransactionType(
           PhabricatorRepositoryTransaction::TYPE_AUTOCLOSE_ONLY)
         ->setIsCopyable(true)
         ->setDescription(pht('Autoclose commits on only these branches.'))
         ->setConduitDescription(pht('Set the autoclose branches.'))
         ->setConduitTypeDescription(pht('New default tracked branchs.'))
         ->setValue($autoclose_value),
+      id(new PhabricatorTextEditField())
+        ->setKey('importOnly')
+        ->setLabel(pht('Import Only'))
+        ->setTransactionType(
+          PhabricatorRepositoryTransaction::TYPE_SVN_SUBPATH)
+        ->setIsCopyable(true)
+        ->setDescription(pht('Subpath to selectively import.'))
+        ->setConduitDescription(pht('Set the subpath to import.'))
+        ->setConduitTypeDescription(pht('New subpath to import.'))
+        ->setValue($object->getDetail('svn-subpath'))
+        ->setControlInstructions($subpath_instructions),
       id(new PhabricatorTextEditField())
         ->setKey('stagingAreaURI')
         ->setLabel(pht('Staging Area URI'))
         ->setTransactionType(
           PhabricatorRepositoryTransaction::TYPE_STAGING_URI)
         ->setIsCopyable(true)
         ->setDescription(pht('Staging area URI.'))
         ->setConduitDescription(pht('Set the staging area URI.'))
         ->setConduitTypeDescription(pht('New staging area URI.'))
-        ->setValue($object->getStagingURI()),
+        ->setValue($object->getStagingURI())
+        ->setControlInstructions($staging_instructions),
       id(new PhabricatorDatasourceEditField())
         ->setKey('automationBlueprintPHIDs')
         ->setLabel(pht('Use Blueprints'))
         ->setTransactionType(
           PhabricatorRepositoryTransaction::TYPE_AUTOMATION_BLUEPRINTS)
         ->setIsCopyable(true)
         ->setDatasource(new DrydockBlueprintDatasource())
         ->setDescription(pht('Automation blueprints.'))
         ->setConduitDescription(pht('Change automation blueprints.'))
         ->setConduitTypeDescription(pht('New blueprint PHIDs.'))
-        ->setValue($object->getAutomationBlueprintPHIDs()),
+        ->setValue($object->getAutomationBlueprintPHIDs())
+        ->setControlInstructions($automation_instructions),
       id(new PhabricatorStringListEditField())
         ->setKey('symbolLanguages')
         ->setLabel(pht('Languages'))
         ->setTransactionType(
           PhabricatorRepositoryTransaction::TYPE_SYMBOLS_LANGUAGE)
         ->setIsCopyable(true)
         ->setDescription(
           pht('Languages which define symbols in this repository.'))
         ->setConduitDescription(
           pht('Change symbol languages for this repository.'))
         ->setConduitTypeDescription(
           pht('New symbol langauges.'))
         ->setValue($object->getSymbolLanguages()),
       id(new PhabricatorDatasourceEditField())
         ->setKey('symbolRepositoryPHIDs')
         ->setLabel(pht('Uses Symbols From'))
         ->setTransactionType(
           PhabricatorRepositoryTransaction::TYPE_SYMBOLS_SOURCES)
         ->setIsCopyable(true)
         ->setDatasource(new DiffusionRepositoryDatasource())
         ->setDescription(pht('Repositories to link symbols from.'))
         ->setConduitDescription(pht('Change symbol source repositories.'))
         ->setConduitTypeDescription(pht('New symbol repositories.'))
         ->setValue($object->getSymbolSources()),
       id(new PhabricatorBoolEditField())
         ->setKey('publish')
         ->setLabel(pht('Publish/Notify'))
         ->setTransactionType(
           PhabricatorRepositoryTransaction::TYPE_NOTIFY)
         ->setIsCopyable(true)
         ->setOptions(
           pht('Disable Notifications, Feed, and Herald'),
           pht('Enable Notifications, Feed, and Herald'))
         ->setDescription(pht('Configure how changes are published.'))
         ->setConduitDescription(pht('Change publishing options.'))
         ->setConduitTypeDescription(pht('New notification setting.'))
         ->setValue(!$object->getDetail('herald-disabled')),
       id(new PhabricatorBoolEditField())
         ->setKey('autoclose')
         ->setLabel(pht('Autoclose'))
         ->setTransactionType(
           PhabricatorRepositoryTransaction::TYPE_AUTOCLOSE)
         ->setIsCopyable(true)
         ->setOptions(
           pht('Disable Autoclose'),
           pht('Enable Autoclose'))
         ->setDescription(pht('Stop or resume autoclosing in this repository.'))
         ->setConduitDescription(pht('Change autoclose setting.'))
         ->setConduitTypeDescription(pht('New autoclose setting.'))
         ->setValue(!$object->getDetail('disable-autoclose')),
       id(new PhabricatorPolicyEditField())
         ->setKey('policy.push')
         ->setLabel(pht('Push Policy'))
         ->setAliases(array('push'))
         ->setIsCopyable(true)
         ->setCapability(DiffusionPushCapability::CAPABILITY)
         ->setPolicies($policies)
         ->setTransactionType(PhabricatorRepositoryTransaction::TYPE_PUSH_POLICY)
         ->setDescription(
           pht('Controls who can push changes to the repository.'))
         ->setConduitDescription(
           pht('Change the push policy of the repository.'))
         ->setConduitTypeDescription(pht('New policy PHID or constant.'))
         ->setValue($object->getPolicy(DiffusionPushCapability::CAPABILITY)),
     );
   }
 
 }
diff --git a/src/applications/diffusion/editor/DiffusionURIEditEngine.php b/src/applications/diffusion/editor/DiffusionURIEditEngine.php
index 6d351219d..fdc91cff5 100644
--- a/src/applications/diffusion/editor/DiffusionURIEditEngine.php
+++ b/src/applications/diffusion/editor/DiffusionURIEditEngine.php
@@ -1,156 +1,219 @@
 <?php
 
 final class DiffusionURIEditEngine
   extends PhabricatorEditEngine {
 
   const ENGINECONST = 'diffusion.uri';
 
   private $repository;
 
   public function setRepository(PhabricatorRepository $repository) {
     $this->repository = $repository;
     return $this;
   }
 
   public function getRepository() {
     return $this->repository;
   }
 
   public function isEngineConfigurable() {
     return false;
   }
 
   public function getEngineName() {
     return pht('Repository URIs');
   }
 
   public function getSummaryHeader() {
     return pht('Edit Repository URI');
   }
 
   public function getSummaryText() {
     return pht('Creates and edits repository URIs.');
   }
 
   public function getEngineApplicationClass() {
     return 'PhabricatorDiffusionApplication';
   }
 
   protected function newEditableObject() {
     $uri = PhabricatorRepositoryURI::initializeNewURI();
 
     $repository = $this->getRepository();
     if ($repository) {
       $uri->setRepositoryPHID($repository->getPHID());
       $uri->attachRepository($repository);
     }
 
     return $uri;
   }
 
   protected function newObjectQuery() {
     return new PhabricatorRepositoryURIQuery();
   }
 
   protected function getObjectCreateTitleText($object) {
     return pht('Create Repository URI');
   }
 
   protected function getObjectCreateButtonText($object) {
     return pht('Create Repository URI');
   }
 
   protected function getObjectEditTitleText($object) {
     return pht('Edit Repository URI %d', $object->getID());
   }
 
   protected function getObjectEditShortText($object) {
     return pht('URI %d', $object->getID());
   }
 
   protected function getObjectCreateShortText() {
     return pht('Create Repository URI');
   }
 
   protected function getObjectName() {
     return pht('Repository URI');
   }
 
   protected function getObjectViewURI($object) {
     return $object->getViewURI();
   }
 
   protected function buildCustomEditFields($object) {
     $viewer = $this->getViewer();
 
+    $uri_instructions = null;
+    if ($object->isBuiltin()) {
+      $is_builtin = true;
+      $uri_value = (string)$object->getDisplayURI();
+
+      switch ($object->getBuiltinProtocol()) {
+        case PhabricatorRepositoryURI::BUILTIN_PROTOCOL_SSH:
+          $uri_instructions = pht(
+            "  - Configure [[ %s | %s ]] to change the SSH username.\n".
+            "  - Configure [[ %s | %s ]] to change the SSH host.\n".
+            "  - Configure [[ %s | %s ]] to change the SSH port.",
+            '/config/edit/diffusion.ssh-user/',
+            'diffusion.ssh-user',
+            '/config/edit/diffusion.ssh-host/',
+            'diffusion.ssh-host',
+            '/config/edit/diffusion.ssh-port/',
+            'diffusion.ssh-port');
+          break;
+      }
+    } else {
+      $is_builtin = false;
+      $uri_value = $object->getURI();
+
+      if ($object->getRepositoryPHID()) {
+        $repository = $object->getRepository();
+        if ($repository->isGit()) {
+          $uri_instructions = pht(
+            "Provide the URI of a Git repository. It should usually look ".
+            "like one of these examples:\n".
+            "\n".
+            "| Example Git URIs\n".
+            "| -----------------------\n".
+            "| `git@github.com:example/example.git`\n".
+            "| `ssh://user@host.com/git/example.git`\n".
+            "| `https://example.com/repository.git`");
+        } else if ($repository->isHg()) {
+          $uri_instructions = pht(
+            "Provide the URI of a Mercurial repository. It should usually ".
+            "look like one of these examples:\n".
+            "\n".
+            "| Example Mercurial URIs\n".
+            "|-----------------------\n".
+            "| `ssh://hg@bitbucket.org/example/repository`\n".
+            "| `https://bitbucket.org/example/repository`");
+        } else if ($repository->isSVN()) {
+          $uri_instructions = pht(
+            "Provide the **Repository Root** of a Subversion repository. ".
+            "You can identify this by running `svn info` in a working ".
+            "copy. It should usually look like one of these examples:\n".
+            "\n".
+            "| Example Subversion URIs\n".
+            "|-----------------------\n".
+            "| `http://svn.example.org/svnroot/`\n".
+            "| `svn+ssh://svn.example.com/svnroot/`\n".
+            "| `svn://svn.example.net/svnroot/`\n\n".
+            "You **MUST** specify the root of the repository, not a ".
+            "subdirectory.");
+        }
+      }
+    }
+
     return array(
       id(new PhabricatorHandlesEditField())
         ->setKey('repository')
         ->setAliases(array('repositoryPHID'))
         ->setLabel(pht('Repository'))
         ->setIsRequired(true)
         ->setIsConduitOnly(true)
         ->setTransactionType(
           PhabricatorRepositoryURITransaction::TYPE_REPOSITORY)
         ->setDescription(pht('The repository this URI is associated with.'))
         ->setConduitDescription(
           pht(
             'Create a URI in a given repository. This transaction type '.
             'must be present when creating a new URI and must not be '.
             'present when editing an existing URI.'))
         ->setConduitTypeDescription(
           pht('Repository PHID to create a new URI for.'))
         ->setSingleValue($object->getRepositoryPHID()),
       id(new PhabricatorTextEditField())
         ->setKey('uri')
         ->setLabel(pht('URI'))
-        ->setIsRequired(true)
         ->setTransactionType(PhabricatorRepositoryURITransaction::TYPE_URI)
         ->setDescription(pht('The repository URI.'))
         ->setConduitDescription(pht('Change the repository URI.'))
         ->setConduitTypeDescription(pht('New repository URI.'))
-        ->setValue($object->getURI()),
+        ->setIsRequired(!$is_builtin)
+        ->setIsLocked($is_builtin)
+        ->setValue($uri_value)
+        ->setControlInstructions($uri_instructions),
       id(new PhabricatorSelectEditField())
         ->setKey('io')
         ->setLabel(pht('I/O Type'))
         ->setTransactionType(PhabricatorRepositoryURITransaction::TYPE_IO)
         ->setDescription(pht('URI I/O behavior.'))
         ->setConduitDescription(pht('Adjust I/O behavior.'))
         ->setConduitTypeDescription(pht('New I/O behavior.'))
         ->setValue($object->getIOType())
         ->setOptions($object->getAvailableIOTypeOptions()),
       id(new PhabricatorSelectEditField())
         ->setKey('display')
         ->setLabel(pht('Display Type'))
         ->setTransactionType(PhabricatorRepositoryURITransaction::TYPE_DISPLAY)
         ->setDescription(pht('URI display behavior.'))
         ->setConduitDescription(pht('Change display behavior.'))
         ->setConduitTypeDescription(pht('New display behavior.'))
         ->setValue($object->getDisplayType())
         ->setOptions($object->getAvailableDisplayTypeOptions()),
       id(new PhabricatorHandlesEditField())
         ->setKey('credential')
         ->setAliases(array('credentialPHID'))
         ->setLabel(pht('Credential'))
         ->setIsConduitOnly(true)
         ->setTransactionType(
           PhabricatorRepositoryURITransaction::TYPE_CREDENTIAL)
         ->setDescription(
           pht('The credential to use when interacting with this URI.'))
         ->setConduitDescription(pht('Change the credential for this URI.'))
         ->setConduitTypeDescription(pht('New credential PHID, or null.'))
         ->setSingleValue($object->getCredentialPHID()),
       id(new PhabricatorBoolEditField())
         ->setKey('disable')
         ->setLabel(pht('Disabled'))
         ->setIsConduitOnly(true)
         ->setTransactionType(PhabricatorRepositoryURITransaction::TYPE_DISABLE)
         ->setDescription(pht('Active status of the URI.'))
         ->setConduitDescription(pht('Disable or activate the URI.'))
         ->setConduitTypeDescription(pht('True to disable the URI.'))
         ->setOptions(pht('Enable'), pht('Disable'))
         ->setValue($object->getIsDisabled()),
     );
   }
 
 }
diff --git a/src/applications/diffusion/editor/DiffusionURIEditor.php b/src/applications/diffusion/editor/DiffusionURIEditor.php
index 3d2164ad0..020275a36 100644
--- a/src/applications/diffusion/editor/DiffusionURIEditor.php
+++ b/src/applications/diffusion/editor/DiffusionURIEditor.php
@@ -1,373 +1,483 @@
 <?php
 
 final class DiffusionURIEditor
   extends PhabricatorApplicationTransactionEditor {
 
+  private $repository;
+  private $repositoryPHID;
+
   public function getEditorApplicationClass() {
     return 'PhabricatorDiffusionApplication';
   }
 
   public function getEditorObjectsDescription() {
     return pht('Diffusion URIs');
   }
 
   public function getTransactionTypes() {
     $types = parent::getTransactionTypes();
 
     $types[] = PhabricatorRepositoryURITransaction::TYPE_REPOSITORY;
     $types[] = PhabricatorRepositoryURITransaction::TYPE_URI;
     $types[] = PhabricatorRepositoryURITransaction::TYPE_IO;
     $types[] = PhabricatorRepositoryURITransaction::TYPE_DISPLAY;
     $types[] = PhabricatorRepositoryURITransaction::TYPE_CREDENTIAL;
     $types[] = PhabricatorRepositoryURITransaction::TYPE_DISABLE;
 
     return $types;
   }
 
   protected function getCustomTransactionOldValue(
     PhabricatorLiskDAO $object,
     PhabricatorApplicationTransaction $xaction) {
 
     switch ($xaction->getTransactionType()) {
       case PhabricatorRepositoryURITransaction::TYPE_URI:
         return $object->getURI();
       case PhabricatorRepositoryURITransaction::TYPE_IO:
         return $object->getIOType();
       case PhabricatorRepositoryURITransaction::TYPE_DISPLAY:
         return $object->getDisplayType();
       case PhabricatorRepositoryURITransaction::TYPE_REPOSITORY:
         return $object->getRepositoryPHID();
       case PhabricatorRepositoryURITransaction::TYPE_CREDENTIAL:
         return $object->getCredentialPHID();
       case PhabricatorRepositoryURITransaction::TYPE_DISABLE:
         return (int)$object->getIsDisabled();
     }
 
     return parent::getCustomTransactionOldValue($object, $xaction);
   }
 
   protected function getCustomTransactionNewValue(
     PhabricatorLiskDAO $object,
     PhabricatorApplicationTransaction $xaction) {
 
     switch ($xaction->getTransactionType()) {
       case PhabricatorRepositoryURITransaction::TYPE_URI:
       case PhabricatorRepositoryURITransaction::TYPE_IO:
       case PhabricatorRepositoryURITransaction::TYPE_DISPLAY:
       case PhabricatorRepositoryURITransaction::TYPE_REPOSITORY:
       case PhabricatorRepositoryURITransaction::TYPE_CREDENTIAL:
         return $xaction->getNewValue();
       case PhabricatorRepositoryURITransaction::TYPE_DISABLE:
         return (int)$xaction->getNewValue();
     }
 
     return parent::getCustomTransactionNewValue($object, $xaction);
   }
 
   protected function applyCustomInternalTransaction(
     PhabricatorLiskDAO $object,
     PhabricatorApplicationTransaction $xaction) {
 
     switch ($xaction->getTransactionType()) {
       case PhabricatorRepositoryURITransaction::TYPE_URI:
+        if (!$this->getIsNewObject()) {
+          $old_uri = $object->getEffectiveURI();
+        } else {
+          $old_uri = null;
+        }
+
         $object->setURI($xaction->getNewValue());
+
+        // If we've changed the domain or protocol of the URI, remove the
+        // current credential. This improves behavior in several cases:
+
+        // If a user switches between protocols with different credential
+        // types, like HTTP and SSH, the old credential won't be valid anyway.
+        // It's cleaner to remove it than leave a bad credential in place.
+
+        // If a user switches hosts, the old credential is probably not
+        // correct (and potentially confusing/misleading). Removing it forces
+        // users to double check that they have the correct credentials.
+
+        // If an attacker can't see a symmetric credential like a username and
+        // password, they could still potentially capture it by changing the
+        // host for a URI that uses it to `evil.com`, a server they control,
+        // then observing the requests. Removing the credential prevents this
+        // kind of escalation.
+
+        // Since port and path changes are less likely to fall among these
+        // cases, they don't trigger a credential wipe.
+
+        $new_uri = $object->getEffectiveURI();
+        if ($old_uri) {
+          $new_proto = ($old_uri->getProtocol() != $new_uri->getProtocol());
+          $new_domain = ($old_uri->getDomain() != $new_uri->getDomain());
+          if ($new_proto || $new_domain) {
+            $object->setCredentialPHID(null);
+          }
+        }
         break;
       case PhabricatorRepositoryURITransaction::TYPE_IO:
         $object->setIOType($xaction->getNewValue());
         break;
       case PhabricatorRepositoryURITransaction::TYPE_DISPLAY:
         $object->setDisplayType($xaction->getNewValue());
         break;
       case PhabricatorRepositoryURITransaction::TYPE_REPOSITORY:
         $object->setRepositoryPHID($xaction->getNewValue());
+        $object->attachRepository($this->repository);
         break;
       case PhabricatorRepositoryURITransaction::TYPE_CREDENTIAL:
         $object->setCredentialPHID($xaction->getNewValue());
         break;
       case PhabricatorRepositoryURITransaction::TYPE_DISABLE:
         $object->setIsDisabled($xaction->getNewValue());
         break;
     }
   }
 
   protected function applyCustomExternalTransaction(
     PhabricatorLiskDAO $object,
     PhabricatorApplicationTransaction $xaction) {
 
     switch ($xaction->getTransactionType()) {
       case PhabricatorRepositoryURITransaction::TYPE_URI:
       case PhabricatorRepositoryURITransaction::TYPE_IO:
       case PhabricatorRepositoryURITransaction::TYPE_DISPLAY:
       case PhabricatorRepositoryURITransaction::TYPE_REPOSITORY:
       case PhabricatorRepositoryURITransaction::TYPE_CREDENTIAL:
       case PhabricatorRepositoryURITransaction::TYPE_DISABLE:
         return;
     }
 
     return parent::applyCustomExternalTransaction($object, $xaction);
   }
 
   protected function validateTransaction(
     PhabricatorLiskDAO $object,
     $type,
     array $xactions) {
 
     $errors = parent::validateTransaction($object, $type, $xactions);
 
     switch ($type) {
       case PhabricatorRepositoryURITransaction::TYPE_REPOSITORY:
+        // Save this, since we need it to validate TYPE_IO transactions.
+        $this->repositoryPHID = $object->getRepositoryPHID();
+
         $missing = $this->validateIsEmptyTextField(
           $object->getRepositoryPHID(),
           $xactions);
         if ($missing) {
           // NOTE: This isn't being marked as a missing field error because
           // it's a fundamental, required property of the URI.
           $errors[] = new PhabricatorApplicationTransactionValidationError(
             $type,
             pht('Required'),
             pht(
               'When creating a repository URI, you must specify which '.
               'repository the URI will belong to.'),
             nonempty(last($xactions), null));
           break;
         }
 
         $viewer = $this->getActor();
 
         foreach ($xactions as $xaction) {
           $repository_phid = $xaction->getNewValue();
 
           // If this isn't changing anything, let it through as-is.
           if ($repository_phid == $object->getRepositoryPHID()) {
             continue;
           }
 
           if (!$this->getIsNewObject()) {
             $errors[] = new PhabricatorApplicationTransactionValidationError(
               $type,
               pht('Invalid'),
               pht(
                 'The repository a URI is associated with is immutable, and '.
                 'can not be changed after the URI is created.'),
               $xaction);
             continue;
           }
 
           $repository = id(new PhabricatorRepositoryQuery())
             ->setViewer($viewer)
             ->withPHIDs(array($repository_phid))
             ->requireCapabilities(
               array(
                 PhabricatorPolicyCapability::CAN_VIEW,
                 PhabricatorPolicyCapability::CAN_EDIT,
               ))
             ->executeOne();
           if (!$repository) {
             $errors[] = new PhabricatorApplicationTransactionValidationError(
               $type,
               pht('Invalid'),
               pht(
                 'To create a URI for a repository ("%s"), it must exist and '.
                 'you must have permission to edit it.',
                 $repository_phid),
               $xaction);
             continue;
           }
+
+          $this->repository = $repository;
+          $this->repositoryPHID = $repository_phid;
         }
         break;
       case PhabricatorRepositoryURITransaction::TYPE_CREDENTIAL:
         $viewer = $this->getActor();
         foreach ($xactions as $xaction) {
           $credential_phid = $xaction->getNewValue();
 
           if ($credential_phid == $object->getCredentialPHID()) {
             continue;
           }
 
+          // Anyone who can edit a URI can remove the credential.
+          if ($credential_phid === null) {
+            continue;
+          }
+
           $credential = id(new PassphraseCredentialQuery())
             ->setViewer($viewer)
             ->withPHIDs(array($credential_phid))
             ->executeOne();
           if (!$credential) {
             $errors[] = new PhabricatorApplicationTransactionValidationError(
               $type,
               pht('Invalid'),
               pht(
                 'You can only associate a credential ("%s") with a repository '.
                 'URI if it exists and you have permission to see it.',
                 $credential_phid),
               $xaction);
             continue;
           }
         }
         break;
       case PhabricatorRepositoryURITransaction::TYPE_URI:
         $missing = $this->validateIsEmptyTextField(
           $object->getURI(),
           $xactions);
 
         if ($missing) {
           $error = new PhabricatorApplicationTransactionValidationError(
             $type,
             pht('Required'),
             pht('A repository URI must have a nonempty URI.'),
             nonempty(last($xactions), null));
 
           $error->setIsMissingFieldError(true);
           $errors[] = $error;
           break;
         }
 
         foreach ($xactions as $xaction) {
           $new_uri = $xaction->getNewValue();
           if ($new_uri == $object->getURI()) {
             continue;
           }
 
           try {
             PhabricatorRepository::assertValidRemoteURI($new_uri);
           } catch (Exception $ex) {
             $errors[] = new PhabricatorApplicationTransactionValidationError(
               $type,
               pht('Invalid'),
               $ex->getMessage(),
               $xaction);
             continue;
           }
         }
 
         break;
       case PhabricatorRepositoryURITransaction::TYPE_IO:
         $available = $object->getAvailableIOTypeOptions();
         foreach ($xactions as $xaction) {
           $new = $xaction->getNewValue();
 
           if (empty($available[$new])) {
             $errors[] = new PhabricatorApplicationTransactionValidationError(
               $type,
               pht('Invalid'),
               pht(
                 'Value "%s" is not a valid display setting for this URI. '.
                 'Available types for this URI are: %s.',
                 implode(', ', array_keys($available))),
               $xaction);
             continue;
           }
 
           // If we are setting this URI to use "Observe", we must have no
           // other "Observe" URIs and must also have no "Read/Write" URIs.
 
           // If we are setting this URI to "Read/Write", we must have no
           // other "Observe" URIs. It's OK to have other "Read/Write" URIs.
 
           $no_observers = false;
           $no_readwrite = false;
           switch ($new) {
             case PhabricatorRepositoryURI::IO_OBSERVE:
               $no_readwrite = true;
               $no_observers = true;
               break;
             case PhabricatorRepositoryURI::IO_READWRITE:
               $no_observers = true;
               break;
           }
 
           if ($no_observers || $no_readwrite) {
             $repository = id(new PhabricatorRepositoryQuery())
               ->setViewer(PhabricatorUser::getOmnipotentUser())
-              ->withPHIDs(array($object->getRepositoryPHID()))
+              ->withPHIDs(array($this->repositoryPHID))
               ->needURIs(true)
               ->executeOne();
             $uris = $repository->getURIs();
 
             $observe_conflict = null;
             $readwrite_conflict = null;
             foreach ($uris as $uri) {
               // If this is the URI being edited, it can not conflict with
               // itself.
               if ($uri->getID() == $object->getID()) {
                 continue;
               }
 
               $io_type = $uri->getIoType();
 
               if ($io_type == PhabricatorRepositoryURI::IO_READWRITE) {
                 if ($no_readwrite) {
                   $readwite_conflict = $uri;
                   break;
                 }
               }
 
               if ($io_type == PhabricatorRepositoryURI::IO_OBSERVE) {
                 if ($no_observers) {
                   $observe_conflict = $uri;
                   break;
                 }
               }
             }
 
             if ($observe_conflict) {
               if ($new == PhabricatorRepositoryURI::IO_OBSERVE) {
                 $message = pht(
                   'You can not set this URI to use Observe IO because '.
                   'another URI for this repository is already configured '.
                   'in Observe IO mode. A repository can not observe two '.
                   'different remotes simultaneously. Turn off IO for the '.
                   'other URI first.');
               } else {
                 $message = pht(
                   'You can not set this URI to use Read/Write IO because '.
                   'another URI for this repository is already configured '.
                   'in Observe IO mode. An observed repository can not be '.
                   'made writable. Turn off IO for the other URI first.');
               }
 
               $errors[] = new PhabricatorApplicationTransactionValidationError(
                 $type,
                 pht('Invalid'),
                 $message,
                 $xaction);
               continue;
             }
 
             if ($readwrite_conflict) {
               $message = pht(
                 'You can not set this URI to use Observe IO because '.
                 'another URI for this repository is already configured '.
                 'in Read/Write IO mode. A repository can not simultaneously '.
                 'be writable and observe a remote. Turn off IO for the '.
                 'other URI first.');
 
               $errors[] = new PhabricatorApplicationTransactionValidationError(
                 $type,
                 pht('Invalid'),
                 $message,
                 $xaction);
               continue;
             }
           }
         }
 
         break;
       case PhabricatorRepositoryURITransaction::TYPE_DISPLAY:
         $available = $object->getAvailableDisplayTypeOptions();
         foreach ($xactions as $xaction) {
           $new = $xaction->getNewValue();
 
           if (empty($available[$new])) {
             $errors[] = new PhabricatorApplicationTransactionValidationError(
               $type,
               pht('Invalid'),
               pht(
                 'Value "%s" is not a valid display setting for this URI. '.
                 'Available types for this URI are: %s.',
                 implode(', ', array_keys($available))));
           }
         }
         break;
+
+      case PhabricatorRepositoryURITransaction::TYPE_DISABLE:
+        $old = $object->getIsDisabled();
+        foreach ($xactions as $xaction) {
+          $new = $xaction->getNewValue();
+
+          if ($old == $new) {
+            continue;
+          }
+
+          if (!$object->isBuiltin()) {
+            continue;
+          }
+
+          $errors[] = new PhabricatorApplicationTransactionValidationError(
+            $type,
+            pht('Invalid'),
+            pht('You can not manually disable builtin URIs.'));
+        }
+        break;
     }
 
     return $errors;
   }
 
+  protected function applyFinalEffects(
+    PhabricatorLiskDAO $object,
+    array $xactions) {
+
+    // Synchronize the repository state based on the presence of an "Observe"
+    // URI.
+    $repository = $object->getRepository();
+
+    $uris = id(new PhabricatorRepositoryURIQuery())
+      ->setViewer(PhabricatorUser::getOmnipotentUser())
+      ->withRepositories(array($repository))
+      ->execute();
+
+    $observe_uri = null;
+    foreach ($uris as $uri) {
+      if ($uri->getIoType() != PhabricatorRepositoryURI::IO_OBSERVE) {
+        continue;
+      }
+
+      $observe_uri = $uri;
+      break;
+    }
+
+    if ($observe_uri) {
+      $repository
+        ->setHosted(false)
+        ->setDetail('remote-uri', (string)$observe_uri->getEffectiveURI())
+        ->setCredentialPHID($observe_uri->getCredentialPHID());
+    } else {
+      $repository
+        ->setHosted(true)
+        ->setDetail('remote-uri', null)
+        ->setCredentialPHID(null);
+    }
+
+    $repository->save();
+
+    return $xactions;
+  }
+
 }
diff --git a/src/applications/diffusion/engine/DiffusionCommitHookEngine.php b/src/applications/diffusion/engine/DiffusionCommitHookEngine.php
index 54fa32a6a..50fc82836 100644
--- a/src/applications/diffusion/engine/DiffusionCommitHookEngine.php
+++ b/src/applications/diffusion/engine/DiffusionCommitHookEngine.php
@@ -1,1267 +1,1260 @@
 <?php
 
 /**
  * @task config   Configuring the Hook Engine
  * @task hook     Hook Execution
  * @task git      Git Hooks
  * @task hg       Mercurial Hooks
  * @task svn      Subversion Hooks
  * @task internal Internals
  */
 final class DiffusionCommitHookEngine extends Phobject {
 
   const ENV_REPOSITORY = 'PHABRICATOR_REPOSITORY';
   const ENV_USER = 'PHABRICATOR_USER';
   const ENV_REMOTE_ADDRESS = 'PHABRICATOR_REMOTE_ADDRESS';
   const ENV_REMOTE_PROTOCOL = 'PHABRICATOR_REMOTE_PROTOCOL';
 
   const EMPTY_HASH = '0000000000000000000000000000000000000000';
 
   private $viewer;
   private $repository;
   private $stdin;
   private $originalArgv;
   private $subversionTransaction;
   private $subversionRepository;
   private $remoteAddress;
   private $remoteProtocol;
   private $transactionKey;
   private $mercurialHook;
   private $mercurialCommits = array();
   private $gitCommits = array();
 
   private $heraldViewerProjects;
   private $rejectCode = PhabricatorRepositoryPushLog::REJECT_BROKEN;
   private $rejectDetails;
   private $emailPHIDs = array();
 
 
 /* -(  Config  )------------------------------------------------------------- */
 
 
   public function setRemoteProtocol($remote_protocol) {
     $this->remoteProtocol = $remote_protocol;
     return $this;
   }
 
   public function getRemoteProtocol() {
     return $this->remoteProtocol;
   }
 
   public function setRemoteAddress($remote_address) {
     $this->remoteAddress = $remote_address;
     return $this;
   }
 
   public function getRemoteAddress() {
     return $this->remoteAddress;
   }
 
   public function setSubversionTransactionInfo($transaction, $repository) {
     $this->subversionTransaction = $transaction;
     $this->subversionRepository = $repository;
     return $this;
   }
 
   public function setStdin($stdin) {
     $this->stdin = $stdin;
     return $this;
   }
 
   public function getStdin() {
     return $this->stdin;
   }
 
   public function setOriginalArgv(array $original_argv) {
     $this->originalArgv = $original_argv;
     return $this;
   }
 
   public function getOriginalArgv() {
     return $this->originalArgv;
   }
 
   public function setRepository(PhabricatorRepository $repository) {
     $this->repository = $repository;
     return $this;
   }
 
   public function getRepository() {
     return $this->repository;
   }
 
   public function setViewer(PhabricatorUser $viewer) {
     $this->viewer = $viewer;
     return $this;
   }
 
   public function getViewer() {
     return $this->viewer;
   }
 
   public function setMercurialHook($mercurial_hook) {
     $this->mercurialHook = $mercurial_hook;
     return $this;
   }
 
   public function getMercurialHook() {
     return $this->mercurialHook;
   }
 
 
 /* -(  Hook Execution  )----------------------------------------------------- */
 
 
   public function execute() {
     $ref_updates = $this->findRefUpdates();
     $all_updates = $ref_updates;
 
     $caught = null;
     try {
 
       try {
         $this->rejectDangerousChanges($ref_updates);
       } catch (DiffusionCommitHookRejectException $ex) {
         // If we're rejecting dangerous changes, flag everything that we've
         // seen as rejected so it's clear that none of it was accepted.
         $this->rejectCode = PhabricatorRepositoryPushLog::REJECT_DANGEROUS;
         throw $ex;
       }
 
       $this->applyHeraldRefRules($ref_updates, $all_updates);
 
       $content_updates = $this->findContentUpdates($ref_updates);
       $all_updates = array_merge($all_updates, $content_updates);
 
       $this->applyHeraldContentRules($content_updates, $all_updates);
 
       // Run custom scripts in `hook.d/` directories.
       $this->applyCustomHooks($all_updates);
 
       // If we make it this far, we're accepting these changes. Mark all the
       // logs as accepted.
       $this->rejectCode = PhabricatorRepositoryPushLog::REJECT_ACCEPT;
     } catch (Exception $ex) {
       // We'll throw this again in a minute, but we want to save all the logs
       // first.
       $caught = $ex;
     }
 
     // Save all the logs no matter what the outcome was.
     $event = $this->newPushEvent();
 
     $event->setRejectCode($this->rejectCode);
     $event->setRejectDetails($this->rejectDetails);
 
     $event->openTransaction();
       $event->save();
       foreach ($all_updates as $update) {
         $update->setPushEventPHID($event->getPHID());
         $update->save();
       }
     $event->saveTransaction();
 
     if ($caught) {
       throw $caught;
     }
 
     // If this went through cleanly, detect pushes which are actually imports
     // of an existing repository rather than an addition of new commits. If
     // this push is importing a bunch of stuff, set the importing flag on
     // the repository. It will be cleared once we fully process everything.
 
     if ($this->isInitialImport($all_updates)) {
       $repository = $this->getRepository();
-
-      $repository->openTransaction();
-        $repository->beginReadLocking();
-          $repository = $repository->reload();
-          $repository->setDetail('importing', true);
-          $repository->save();
-        $repository->endReadLocking();
-      $repository->saveTransaction();
+      $repository->markImporting();
     }
 
     if ($this->emailPHIDs) {
       // If Herald rules triggered email to users, queue a worker to send the
       // mail. We do this out-of-process so that we block pushes as briefly
       // as possible.
 
       // (We do need to pull some commit info here because the commit objects
       // may not exist yet when this worker runs, which could be immediately.)
 
       PhabricatorWorker::scheduleTask(
         'PhabricatorRepositoryPushMailWorker',
         array(
           'eventPHID' => $event->getPHID(),
           'emailPHIDs' => array_values($this->emailPHIDs),
           'info' => $this->loadCommitInfoForWorker($all_updates),
         ),
         array(
           'priority' => PhabricatorWorker::PRIORITY_ALERTS,
         ));
     }
 
     return 0;
   }
 
   private function findRefUpdates() {
     $type = $this->getRepository()->getVersionControlSystem();
     switch ($type) {
       case PhabricatorRepositoryType::REPOSITORY_TYPE_GIT:
         return $this->findGitRefUpdates();
       case PhabricatorRepositoryType::REPOSITORY_TYPE_MERCURIAL:
         return $this->findMercurialRefUpdates();
       case PhabricatorRepositoryType::REPOSITORY_TYPE_SVN:
         return $this->findSubversionRefUpdates();
       default:
         throw new Exception(pht('Unsupported repository type "%s"!', $type));
     }
   }
 
   private function rejectDangerousChanges(array $ref_updates) {
     assert_instances_of($ref_updates, 'PhabricatorRepositoryPushLog');
 
     $repository = $this->getRepository();
     if ($repository->shouldAllowDangerousChanges()) {
       return;
     }
 
     $flag_dangerous = PhabricatorRepositoryPushLog::CHANGEFLAG_DANGEROUS;
 
     foreach ($ref_updates as $ref_update) {
       if (!$ref_update->hasChangeFlags($flag_dangerous)) {
         // This is not a dangerous change.
         continue;
       }
 
       // We either have a branch deletion or a non fast-forward branch update.
       // Format a message and reject the push.
 
       $message = pht(
         "DANGEROUS CHANGE: %s\n".
         "Dangerous change protection is enabled for this repository.\n".
         "Edit the repository configuration before making dangerous changes.",
         $ref_update->getDangerousChangeDescription());
 
       throw new DiffusionCommitHookRejectException($message);
     }
   }
 
   private function findContentUpdates(array $ref_updates) {
     assert_instances_of($ref_updates, 'PhabricatorRepositoryPushLog');
 
     $type = $this->getRepository()->getVersionControlSystem();
     switch ($type) {
       case PhabricatorRepositoryType::REPOSITORY_TYPE_GIT:
         return $this->findGitContentUpdates($ref_updates);
       case PhabricatorRepositoryType::REPOSITORY_TYPE_MERCURIAL:
         return $this->findMercurialContentUpdates($ref_updates);
       case PhabricatorRepositoryType::REPOSITORY_TYPE_SVN:
         return $this->findSubversionContentUpdates($ref_updates);
       default:
         throw new Exception(pht('Unsupported repository type "%s"!', $type));
     }
   }
 
 
 /* -(  Herald  )------------------------------------------------------------- */
 
   private function applyHeraldRefRules(
     array $ref_updates,
     array $all_updates) {
     $this->applyHeraldRules(
       $ref_updates,
       new HeraldPreCommitRefAdapter(),
       $all_updates);
   }
 
   private function applyHeraldContentRules(
     array $content_updates,
     array $all_updates) {
     $this->applyHeraldRules(
       $content_updates,
       new HeraldPreCommitContentAdapter(),
       $all_updates);
   }
 
   private function applyHeraldRules(
     array $updates,
     HeraldAdapter $adapter_template,
     array $all_updates) {
 
     if (!$updates) {
       return;
     }
 
     $adapter_template->setHookEngine($this);
 
     $engine = new HeraldEngine();
     $rules = null;
     $blocking_effect = null;
     $blocked_update = null;
     $blocking_xscript = null;
     foreach ($updates as $update) {
       $adapter = id(clone $adapter_template)
         ->setPushLog($update);
 
       if ($rules === null) {
         $rules = $engine->loadRulesForAdapter($adapter);
       }
 
       $effects = $engine->applyRules($rules, $adapter);
       $engine->applyEffects($effects, $adapter, $rules);
       $xscript = $engine->getTranscript();
 
       // Store any PHIDs we want to send email to for later.
       foreach ($adapter->getEmailPHIDs() as $email_phid) {
         $this->emailPHIDs[$email_phid] = $email_phid;
       }
 
       $block_action = DiffusionBlockHeraldAction::ACTIONCONST;
 
       if ($blocking_effect === null) {
         foreach ($effects as $effect) {
           if ($effect->getAction() == $block_action) {
             $blocking_effect = $effect;
             $blocked_update = $update;
             $blocking_xscript = $xscript;
             break;
           }
         }
       }
     }
 
     if ($blocking_effect) {
       $rule = $blocking_effect->getRule();
 
       $this->rejectCode = PhabricatorRepositoryPushLog::REJECT_HERALD;
       $this->rejectDetails = $rule->getPHID();
 
       $message = $blocking_effect->getTarget();
       if (!strlen($message)) {
         $message = pht('(None.)');
       }
 
       $blocked_ref_name = coalesce(
         $blocked_update->getRefName(),
         $blocked_update->getRefNewShort());
       $blocked_name = $blocked_update->getRefType().'/'.$blocked_ref_name;
 
       throw new DiffusionCommitHookRejectException(
         pht(
           "This push was rejected by Herald push rule %s.\n".
           "    Change: %s\n".
           "      Rule: %s\n".
           "    Reason: %s\n".
           "Transcript: %s",
           $rule->getMonogram(),
           $blocked_name,
           $rule->getName(),
           $message,
           PhabricatorEnv::getProductionURI(
             '/herald/transcript/'.$blocking_xscript->getID().'/')));
     }
   }
 
   public function loadViewerProjectPHIDsForHerald() {
     // This just caches the viewer's projects so we don't need to load them
     // over and over again when applying Herald rules.
     if ($this->heraldViewerProjects === null) {
       $this->heraldViewerProjects = id(new PhabricatorProjectQuery())
         ->setViewer($this->getViewer())
         ->withMemberPHIDs(array($this->getViewer()->getPHID()))
         ->execute();
     }
 
     return mpull($this->heraldViewerProjects, 'getPHID');
   }
 
 
 /* -(  Git  )---------------------------------------------------------------- */
 
 
   private function findGitRefUpdates() {
     $ref_updates = array();
 
     // First, parse stdin, which lists all the ref changes. The input looks
     // like this:
     //
     //   <old hash> <new hash> <ref>
 
     $stdin = $this->getStdin();
     $lines = phutil_split_lines($stdin, $retain_endings = false);
     foreach ($lines as $line) {
       $parts = explode(' ', $line, 3);
       if (count($parts) != 3) {
         throw new Exception(pht('Expected "old new ref", got "%s".', $line));
       }
 
       $ref_old = $parts[0];
       $ref_new = $parts[1];
       $ref_raw = $parts[2];
 
       if (preg_match('(^refs/heads/)', $ref_raw)) {
         $ref_type = PhabricatorRepositoryPushLog::REFTYPE_BRANCH;
         $ref_raw = substr($ref_raw, strlen('refs/heads/'));
       } else if (preg_match('(^refs/tags/)', $ref_raw)) {
         $ref_type = PhabricatorRepositoryPushLog::REFTYPE_TAG;
         $ref_raw = substr($ref_raw, strlen('refs/tags/'));
       } else {
         throw new Exception(
           pht(
             "Unable to identify the reftype of '%s'. Rejecting push.",
             $ref_raw));
       }
 
       $ref_update = $this->newPushLog()
         ->setRefType($ref_type)
         ->setRefName($ref_raw)
         ->setRefOld($ref_old)
         ->setRefNew($ref_new);
 
       $ref_updates[] = $ref_update;
     }
 
     $this->findGitMergeBases($ref_updates);
     $this->findGitChangeFlags($ref_updates);
 
     return $ref_updates;
   }
 
 
   private function findGitMergeBases(array $ref_updates) {
     assert_instances_of($ref_updates, 'PhabricatorRepositoryPushLog');
 
     $futures = array();
     foreach ($ref_updates as $key => $ref_update) {
       // If the old hash is "00000...", the ref is being created (either a new
       // branch, or a new tag). If the new hash is "00000...", the ref is being
       // deleted. If both are nonempty, the ref is being updated. For updates,
       // we'll figure out the `merge-base` of the old and new objects here. This
       // lets us reject non-FF changes cheaply; later, we'll figure out exactly
       // which commits are new.
       $ref_old = $ref_update->getRefOld();
       $ref_new = $ref_update->getRefNew();
 
       if (($ref_old === self::EMPTY_HASH) ||
           ($ref_new === self::EMPTY_HASH)) {
         continue;
       }
 
       $futures[$key] = $this->getRepository()->getLocalCommandFuture(
         'merge-base %s %s',
         $ref_old,
         $ref_new);
     }
 
     $futures = id(new FutureIterator($futures))
       ->limit(8);
     foreach ($futures as $key => $future) {
 
       // If 'old' and 'new' have no common ancestors (for example, a force push
       // which completely rewrites a ref), `git merge-base` will exit with
       // an error and no output. It would be nice to find a positive test
       // for this instead, but I couldn't immediately come up with one. See
       // T4224. Assume this means there are no ancestors.
 
       list($err, $stdout) = $future->resolve();
 
       if ($err) {
         $merge_base = null;
       } else {
         $merge_base = rtrim($stdout, "\n");
       }
 
       $ref_update = $ref_updates[$key];
       $ref_update->setMergeBase($merge_base);
     }
 
     return $ref_updates;
   }
 
 
   private function findGitChangeFlags(array $ref_updates) {
     assert_instances_of($ref_updates, 'PhabricatorRepositoryPushLog');
 
     foreach ($ref_updates as $key => $ref_update) {
       $ref_old = $ref_update->getRefOld();
       $ref_new = $ref_update->getRefNew();
       $ref_type = $ref_update->getRefType();
 
       $ref_flags = 0;
       $dangerous = null;
 
       if (($ref_old === self::EMPTY_HASH) && ($ref_new === self::EMPTY_HASH)) {
         // This happens if you try to delete a tag or branch which does not
         // exist by pushing directly to the ref. Git will warn about it but
         // allow it. Just call it a delete, without flagging it as dangerous.
         $ref_flags |= PhabricatorRepositoryPushLog::CHANGEFLAG_DELETE;
       } else if ($ref_old === self::EMPTY_HASH) {
         $ref_flags |= PhabricatorRepositoryPushLog::CHANGEFLAG_ADD;
       } else if ($ref_new === self::EMPTY_HASH) {
         $ref_flags |= PhabricatorRepositoryPushLog::CHANGEFLAG_DELETE;
         if ($ref_type == PhabricatorRepositoryPushLog::REFTYPE_BRANCH) {
           $ref_flags |= PhabricatorRepositoryPushLog::CHANGEFLAG_DANGEROUS;
           $dangerous = pht(
             "The change you're attempting to push deletes the branch '%s'.",
             $ref_update->getRefName());
         }
       } else {
         $merge_base = $ref_update->getMergeBase();
         if ($merge_base == $ref_old) {
           // This is a fast-forward update to an existing branch.
           // These are safe.
           $ref_flags |= PhabricatorRepositoryPushLog::CHANGEFLAG_APPEND;
         } else {
           $ref_flags |= PhabricatorRepositoryPushLog::CHANGEFLAG_REWRITE;
 
           // For now, we don't consider deleting or moving tags to be a
           // "dangerous" update. It's way harder to get wrong and should be easy
           // to recover from once we have better logging. Only add the dangerous
           // flag if this ref is a branch.
 
           if ($ref_type == PhabricatorRepositoryPushLog::REFTYPE_BRANCH) {
             $ref_flags |= PhabricatorRepositoryPushLog::CHANGEFLAG_DANGEROUS;
 
             $dangerous = pht(
               "The change you're attempting to push updates the branch '%s' ".
               "from '%s' to '%s', but this is not a fast-forward. Pushes ".
               "which rewrite published branch history are dangerous.",
               $ref_update->getRefName(),
               $ref_update->getRefOldShort(),
               $ref_update->getRefNewShort());
           }
         }
       }
 
       $ref_update->setChangeFlags($ref_flags);
       if ($dangerous !== null) {
         $ref_update->attachDangerousChangeDescription($dangerous);
       }
     }
 
     return $ref_updates;
   }
 
 
   private function findGitContentUpdates(array $ref_updates) {
     $flag_delete = PhabricatorRepositoryPushLog::CHANGEFLAG_DELETE;
 
     $futures = array();
     foreach ($ref_updates as $key => $ref_update) {
       if ($ref_update->hasChangeFlags($flag_delete)) {
         // Deleting a branch or tag can never create any new commits.
         continue;
       }
 
       // NOTE: This piece of magic finds all new commits, by walking backward
       // from the new value to the value of *any* existing ref in the
       // repository. Particularly, this will cover the cases of a new branch, a
       // completely moved tag, etc.
       $futures[$key] = $this->getRepository()->getLocalCommandFuture(
         'log --format=%s %s --not --all',
         '%H',
         $ref_update->getRefNew());
     }
 
     $content_updates = array();
     $futures = id(new FutureIterator($futures))
       ->limit(8);
     foreach ($futures as $key => $future) {
       list($stdout) = $future->resolvex();
 
       if (!strlen(trim($stdout))) {
         // This change doesn't have any new commits. One common case of this
         // is creating a new tag which points at an existing commit.
         continue;
       }
 
       $commits = phutil_split_lines($stdout, $retain_newlines = false);
 
       // If we're looking at a branch, mark all of the new commits as on that
       // branch. It's only possible for these commits to be on updated branches,
       // since any other branch heads are necessarily behind them.
       $branch_name = null;
       $ref_update = $ref_updates[$key];
       $type_branch = PhabricatorRepositoryPushLog::REFTYPE_BRANCH;
       if ($ref_update->getRefType() == $type_branch) {
         $branch_name = $ref_update->getRefName();
       }
 
       foreach ($commits as $commit) {
         if ($branch_name) {
           $this->gitCommits[$commit][] = $branch_name;
         }
         $content_updates[$commit] = $this->newPushLog()
           ->setRefType(PhabricatorRepositoryPushLog::REFTYPE_COMMIT)
           ->setRefNew($commit)
           ->setChangeFlags(PhabricatorRepositoryPushLog::CHANGEFLAG_ADD);
       }
     }
 
     return $content_updates;
   }
 
 /* -(  Custom  )------------------------------------------------------------- */
 
   private function applyCustomHooks(array $updates) {
     $args = $this->getOriginalArgv();
     $stdin = $this->getStdin();
     $console = PhutilConsole::getConsole();
 
     $env = array(
       self::ENV_REPOSITORY => $this->getRepository()->getPHID(),
       self::ENV_USER => $this->getViewer()->getUsername(),
       self::ENV_REMOTE_PROTOCOL => $this->getRemoteProtocol(),
       self::ENV_REMOTE_ADDRESS => $this->getRemoteAddress(),
     );
 
     $directories = $this->getRepository()->getHookDirectories();
     foreach ($directories as $directory) {
       $hooks = $this->getExecutablesInDirectory($directory);
       sort($hooks);
       foreach ($hooks as $hook) {
         // NOTE: We're explicitly running the hooks in sequential order to
         // make this more predictable.
         $future = id(new ExecFuture('%s %Ls', $hook, $args))
           ->setEnv($env, $wipe_process_env = false)
           ->write($stdin);
 
         list($err, $stdout, $stderr) = $future->resolve();
         if (!$err) {
           // This hook ran OK, but echo its output in case there was something
           // informative.
           $console->writeOut('%s', $stdout);
           $console->writeErr('%s', $stderr);
           continue;
         }
 
         $this->rejectCode = PhabricatorRepositoryPushLog::REJECT_EXTERNAL;
         $this->rejectDetails = basename($hook);
 
         throw new DiffusionCommitHookRejectException(
           pht(
             "This push was rejected by custom hook script '%s':\n\n%s%s",
             basename($hook),
             $stdout,
             $stderr));
       }
     }
   }
 
   private function getExecutablesInDirectory($directory) {
     $executables = array();
 
     if (!Filesystem::pathExists($directory)) {
       return $executables;
     }
 
     foreach (Filesystem::listDirectory($directory) as $path) {
       $full_path = $directory.DIRECTORY_SEPARATOR.$path;
       if (!is_executable($full_path)) {
         // Don't include non-executable files.
         continue;
       }
 
       if (basename($full_path) == 'README') {
         // Don't include README, even if it is marked as executable. It almost
         // certainly got caught in the crossfire of a sweeping `chmod`, since
         // users do this with some frequency.
         continue;
       }
 
       $executables[] = $full_path;
     }
 
     return $executables;
   }
 
 
 /* -(  Mercurial  )---------------------------------------------------------- */
 
 
   private function findMercurialRefUpdates() {
     $hook = $this->getMercurialHook();
     switch ($hook) {
       case 'pretxnchangegroup':
         return $this->findMercurialChangegroupRefUpdates();
       case 'prepushkey':
         return $this->findMercurialPushKeyRefUpdates();
       default:
         throw new Exception(pht('Unrecognized hook "%s"!', $hook));
     }
   }
 
   private function findMercurialChangegroupRefUpdates() {
     $hg_node = getenv('HG_NODE');
     if (!$hg_node) {
       throw new Exception(
         pht(
           'Expected %s in environment!',
           'HG_NODE'));
     }
 
     // NOTE: We need to make sure this is passed to subprocesses, or they won't
     // be able to see new commits. Mercurial uses this as a marker to determine
     // whether the pending changes are visible or not.
     $_ENV['HG_PENDING'] = getenv('HG_PENDING');
     $repository = $this->getRepository();
 
     $futures = array();
 
     foreach (array('old', 'new') as $key) {
       $futures[$key] = $repository->getLocalCommandFuture(
         'heads --template %s',
         '{node}\1{branch}\2');
     }
     // Wipe HG_PENDING out of the old environment so we see the pre-commit
     // state of the repository.
     $futures['old']->updateEnv('HG_PENDING', null);
 
     $futures['commits'] = $repository->getLocalCommandFuture(
       'log --rev %s --template %s',
       hgsprintf('%s:%s', $hg_node, 'tip'),
       '{node}\1{branch}\2');
 
     // Resolve all of the futures now. We don't need the 'commits' future yet,
     // but it simplifies the logic to just get it out of the way.
     foreach (new FutureIterator($futures) as $future) {
       $future->resolve();
     }
 
     list($commit_raw) = $futures['commits']->resolvex();
     $commit_map = $this->parseMercurialCommits($commit_raw);
     $this->mercurialCommits = $commit_map;
 
     // NOTE: `hg heads` exits with an error code and no output if the repository
     // has no heads. Most commonly this happens on a new repository. We know
     // we can run `hg` successfully since the `hg log` above didn't error, so
     // just ignore the error code.
 
     list($err, $old_raw) = $futures['old']->resolve();
     $old_refs = $this->parseMercurialHeads($old_raw);
 
     list($err, $new_raw) = $futures['new']->resolve();
     $new_refs = $this->parseMercurialHeads($new_raw);
 
     $all_refs = array_keys($old_refs + $new_refs);
 
     $ref_updates = array();
     foreach ($all_refs as $ref) {
       $old_heads = idx($old_refs, $ref, array());
       $new_heads = idx($new_refs, $ref, array());
 
       sort($old_heads);
       sort($new_heads);
 
       if (!$old_heads && !$new_heads) {
         // This should never be possible, as it makes no sense. Explode.
         throw new Exception(
           pht(
             'Mercurial repository has no new or old heads for branch "%s" '.
             'after push. This makes no sense; rejecting change.',
             $ref));
       }
 
       if ($old_heads === $new_heads) {
         // No changes to this branch, so skip it.
         continue;
       }
 
       $stray_heads = array();
       $head_map = array();
 
       if ($old_heads && !$new_heads) {
         // This is a branch deletion with "--close-branch".
         foreach ($old_heads as $old_head) {
           $head_map[$old_head] = array(self::EMPTY_HASH);
         }
       } else if (count($old_heads) > 1) {
         // HORRIBLE: In Mercurial, branches can have multiple heads. If the
         // old branch had multiple heads, we need to figure out which new
         // heads descend from which old heads, so we can tell whether you're
         // actively creating new heads (dangerous) or just working in a
         // repository that's already full of garbage (strongly discouraged but
         // not as inherently dangerous). These cases should be very uncommon.
 
         // NOTE: We're only looking for heads on the same branch. The old
         // tip of the branch may be the branchpoint for other branches, but that
         // is OK.
 
         $dfutures = array();
         foreach ($old_heads as $old_head) {
           $dfutures[$old_head] = $repository->getLocalCommandFuture(
             'log --branch %s --rev %s --template %s',
             $ref,
             hgsprintf('(descendants(%s) and head())', $old_head),
             '{node}\1');
         }
 
         foreach (new FutureIterator($dfutures) as $future_head => $dfuture) {
           list($stdout) = $dfuture->resolvex();
           $descendant_heads = array_filter(explode("\1", $stdout));
           if ($descendant_heads) {
             // This old head has at least one descendant in the push.
             $head_map[$future_head] = $descendant_heads;
           } else {
             // This old head has no descendants, so it is being deleted.
             $head_map[$future_head] = array(self::EMPTY_HASH);
           }
         }
 
         // Now, find all the new stray heads this push creates, if any. These
         // are new heads which do not descend from the old heads.
         $seen = array_fuse(array_mergev($head_map));
         foreach ($new_heads as $new_head) {
           if ($new_head === self::EMPTY_HASH) {
             // If a branch head is being deleted, don't insert it as an add.
             continue;
           }
           if (empty($seen[$new_head])) {
             $head_map[self::EMPTY_HASH][] = $new_head;
           }
         }
       } else if ($old_heads) {
         $head_map[head($old_heads)] = $new_heads;
       } else {
         $head_map[self::EMPTY_HASH] = $new_heads;
       }
 
       foreach ($head_map as $old_head => $child_heads) {
         foreach ($child_heads as $new_head) {
           if ($new_head === $old_head) {
             continue;
           }
 
           $ref_flags = 0;
           $dangerous = null;
           if ($old_head == self::EMPTY_HASH) {
             $ref_flags |= PhabricatorRepositoryPushLog::CHANGEFLAG_ADD;
           } else {
             $ref_flags |= PhabricatorRepositoryPushLog::CHANGEFLAG_APPEND;
           }
 
 
           $deletes_existing_head = ($new_head == self::EMPTY_HASH);
           $splits_existing_head = (count($child_heads) > 1);
           $creates_duplicate_head = ($old_head == self::EMPTY_HASH) &&
                                     (count($head_map) > 1);
 
           if ($splits_existing_head || $creates_duplicate_head) {
             $readable_child_heads = array();
             foreach ($child_heads as $child_head) {
               $readable_child_heads[] = substr($child_head, 0, 12);
             }
 
             $ref_flags |= PhabricatorRepositoryPushLog::CHANGEFLAG_DANGEROUS;
 
             if ($splits_existing_head) {
               // We're splitting an existing head into two or more heads.
               // This is dangerous, and a super bad idea. Note that we're only
               // raising this if you're actively splitting a branch head. If a
               // head split in the past, we don't consider appends to it
               // to be dangerous.
               $dangerous = pht(
                 "The change you're attempting to push splits the head of ".
                 "branch '%s' into multiple heads: %s. This is inadvisable ".
                 "and dangerous.",
                 $ref,
                 implode(', ', $readable_child_heads));
             } else {
               // We're adding a second (or more) head to a branch. The new
               // head is not a descendant of any old head.
               $dangerous = pht(
                 "The change you're attempting to push creates new, divergent ".
                 "heads for the branch '%s': %s. This is inadvisable and ".
                 "dangerous.",
                 $ref,
                 implode(', ', $readable_child_heads));
             }
           }
 
           if ($deletes_existing_head) {
             // TODO: Somewhere in here we should be setting CHANGEFLAG_REWRITE
             // if we are also creating at least one other head to replace
             // this one.
 
             // NOTE: In Git, this is a dangerous change, but it is not dangerous
             // in Mercurial. Mercurial branches are version controlled, and
             // Mercurial does not prompt you for any special flags when pushing
             // a `--close-branch` commit by default.
 
             $ref_flags |= PhabricatorRepositoryPushLog::CHANGEFLAG_DELETE;
           }
 
           $ref_update = $this->newPushLog()
             ->setRefType(PhabricatorRepositoryPushLog::REFTYPE_BRANCH)
             ->setRefName($ref)
             ->setRefOld($old_head)
             ->setRefNew($new_head)
             ->setChangeFlags($ref_flags);
 
           if ($dangerous !== null) {
             $ref_update->attachDangerousChangeDescription($dangerous);
           }
 
           $ref_updates[] = $ref_update;
         }
       }
     }
 
     return $ref_updates;
   }
 
   private function findMercurialPushKeyRefUpdates() {
     $key_namespace = getenv('HG_NAMESPACE');
 
     if ($key_namespace === 'phases') {
       // Mercurial changes commit phases as part of normal push operations. We
       // just ignore these, as they don't seem to represent anything
       // interesting.
       return array();
     }
 
     $key_name = getenv('HG_KEY');
 
     $key_old = getenv('HG_OLD');
     if (!strlen($key_old)) {
       $key_old = null;
     }
 
     $key_new = getenv('HG_NEW');
     if (!strlen($key_new)) {
       $key_new = null;
     }
 
     if ($key_namespace !== 'bookmarks') {
       throw new Exception(
         pht(
           "Unknown Mercurial key namespace '%s', with key '%s' (%s -> %s). ".
           "Rejecting push.",
           $key_namespace,
           $key_name,
           coalesce($key_old, pht('null')),
           coalesce($key_new, pht('null'))));
     }
 
     if ($key_old === $key_new) {
       // We get a callback when the bookmark doesn't change. Just ignore this,
       // as it's a no-op.
       return array();
     }
 
     $ref_flags = 0;
     $merge_base = null;
     if ($key_old === null) {
       $ref_flags |= PhabricatorRepositoryPushLog::CHANGEFLAG_ADD;
     } else if ($key_new === null) {
       $ref_flags |= PhabricatorRepositoryPushLog::CHANGEFLAG_DELETE;
     } else {
       list($merge_base_raw) = $this->getRepository()->execxLocalCommand(
         'log --template %s --rev %s',
         '{node}',
         hgsprintf('ancestor(%s, %s)', $key_old, $key_new));
 
       if (strlen(trim($merge_base_raw))) {
         $merge_base = trim($merge_base_raw);
       }
 
       if ($merge_base && ($merge_base === $key_old)) {
         $ref_flags |= PhabricatorRepositoryPushLog::CHANGEFLAG_APPEND;
       } else {
         $ref_flags |= PhabricatorRepositoryPushLog::CHANGEFLAG_REWRITE;
       }
     }
 
     $ref_update = $this->newPushLog()
       ->setRefType(PhabricatorRepositoryPushLog::REFTYPE_BOOKMARK)
       ->setRefName($key_name)
       ->setRefOld(coalesce($key_old, self::EMPTY_HASH))
       ->setRefNew(coalesce($key_new, self::EMPTY_HASH))
       ->setChangeFlags($ref_flags);
 
     return array($ref_update);
   }
 
   private function findMercurialContentUpdates(array $ref_updates) {
     $content_updates = array();
 
     foreach ($this->mercurialCommits as $commit => $branches) {
       $content_updates[$commit] = $this->newPushLog()
         ->setRefType(PhabricatorRepositoryPushLog::REFTYPE_COMMIT)
         ->setRefNew($commit)
         ->setChangeFlags(PhabricatorRepositoryPushLog::CHANGEFLAG_ADD);
     }
 
     return $content_updates;
   }
 
   private function parseMercurialCommits($raw) {
     $commits_lines = explode("\2", $raw);
     $commits_lines = array_filter($commits_lines);
     $commit_map = array();
     foreach ($commits_lines as $commit_line) {
       list($node, $branch) = explode("\1", $commit_line);
       $commit_map[$node] = array($branch);
     }
 
     return $commit_map;
   }
 
   private function parseMercurialHeads($raw) {
     $heads_map = $this->parseMercurialCommits($raw);
 
     $heads = array();
     foreach ($heads_map as $commit => $branches) {
       foreach ($branches as $branch) {
         $heads[$branch][] = $commit;
       }
     }
 
     return $heads;
   }
 
 
 /* -(  Subversion  )--------------------------------------------------------- */
 
 
   private function findSubversionRefUpdates() {
     // Subversion doesn't have any kind of mutable ref metadata.
     return array();
   }
 
   private function findSubversionContentUpdates(array $ref_updates) {
     list($youngest) = execx(
       'svnlook youngest %s',
       $this->subversionRepository);
     $ref_new = (int)$youngest + 1;
 
     $ref_flags = 0;
     $ref_flags |= PhabricatorRepositoryPushLog::CHANGEFLAG_ADD;
     $ref_flags |= PhabricatorRepositoryPushLog::CHANGEFLAG_APPEND;
 
     $ref_content = $this->newPushLog()
       ->setRefType(PhabricatorRepositoryPushLog::REFTYPE_COMMIT)
       ->setRefNew($ref_new)
       ->setChangeFlags($ref_flags);
 
     return array($ref_content);
   }
 
 
 /* -(  Internals  )---------------------------------------------------------- */
 
 
   private function newPushLog() {
     // NOTE: We generate PHIDs up front so the Herald transcripts can pick them
     // up.
     $phid = id(new PhabricatorRepositoryPushLog())->generatePHID();
 
     $device = AlmanacKeys::getLiveDevice();
     if ($device) {
       $device_phid = $device->getPHID();
     } else {
       $device_phid = null;
     }
 
     return PhabricatorRepositoryPushLog::initializeNewLog($this->getViewer())
       ->setPHID($phid)
       ->setDevicePHID($device_phid)
       ->setRepositoryPHID($this->getRepository()->getPHID())
       ->attachRepository($this->getRepository())
       ->setEpoch(time());
   }
 
   private function newPushEvent() {
     $viewer = $this->getViewer();
     return PhabricatorRepositoryPushEvent::initializeNewEvent($viewer)
       ->setRepositoryPHID($this->getRepository()->getPHID())
       ->setRemoteAddress($this->getRemoteAddress())
       ->setRemoteProtocol($this->getRemoteProtocol())
       ->setEpoch(time());
   }
 
   public function loadChangesetsForCommit($identifier) {
     $byte_limit = HeraldCommitAdapter::getEnormousByteLimit();
     $time_limit = HeraldCommitAdapter::getEnormousTimeLimit();
 
     $vcs = $this->getRepository()->getVersionControlSystem();
     switch ($vcs) {
       case PhabricatorRepositoryType::REPOSITORY_TYPE_GIT:
       case PhabricatorRepositoryType::REPOSITORY_TYPE_MERCURIAL:
         // For git and hg, we can use normal commands.
         $drequest = DiffusionRequest::newFromDictionary(
           array(
             'repository' => $this->getRepository(),
             'user' => $this->getViewer(),
             'commit' => $identifier,
           ));
 
         $raw_diff = DiffusionRawDiffQuery::newFromDiffusionRequest($drequest)
           ->setTimeout($time_limit)
           ->setByteLimit($byte_limit)
           ->setLinesOfContext(0)
           ->loadRawDiff();
         break;
       case PhabricatorRepositoryType::REPOSITORY_TYPE_SVN:
         // TODO: This diff has 3 lines of context, which produces slightly
         // incorrect "added file content" and "removed file content" results.
         // This may also choke on binaries, but "svnlook diff" does not support
         // the "--diff-cmd" flag.
 
         // For subversion, we need to use `svnlook`.
         $future = new ExecFuture(
           'svnlook diff -t %s %s',
           $this->subversionTransaction,
           $this->subversionRepository);
 
         $future->setTimeout($time_limit);
         $future->setStdoutSizeLimit($byte_limit);
         $future->setStderrSizeLimit($byte_limit);
 
         list($raw_diff) = $future->resolvex();
         break;
       default:
         throw new Exception(pht("Unknown VCS '%s!'", $vcs));
     }
 
     if (strlen($raw_diff) >= $byte_limit) {
       throw new Exception(
         pht(
           'The raw text of this change is enormous (larger than %d '.
           'bytes). Herald can not process it.',
           $byte_limit));
     }
 
     if (!strlen($raw_diff)) {
       // If the commit is actually empty, just return no changesets.
       return array();
     }
 
     $parser = new ArcanistDiffParser();
     $changes = $parser->parseDiff($raw_diff);
     $diff = DifferentialDiff::newEphemeralFromRawChanges(
       $changes);
     return $diff->getChangesets();
   }
 
   public function loadCommitRefForCommit($identifier) {
     $repository = $this->getRepository();
     $vcs = $repository->getVersionControlSystem();
     switch ($vcs) {
       case PhabricatorRepositoryType::REPOSITORY_TYPE_GIT:
       case PhabricatorRepositoryType::REPOSITORY_TYPE_MERCURIAL:
         return id(new DiffusionLowLevelCommitQuery())
           ->setRepository($repository)
           ->withIdentifier($identifier)
           ->execute();
       case PhabricatorRepositoryType::REPOSITORY_TYPE_SVN:
         // For subversion, we need to use `svnlook`.
         list($message) = execx(
           'svnlook log -t %s %s',
           $this->subversionTransaction,
           $this->subversionRepository);
 
         return id(new DiffusionCommitRef())
           ->setMessage($message);
         break;
       default:
         throw new Exception(pht("Unknown VCS '%s!'", $vcs));
     }
   }
 
   public function loadBranches($identifier) {
     $repository = $this->getRepository();
     $vcs = $repository->getVersionControlSystem();
     switch ($vcs) {
       case PhabricatorRepositoryType::REPOSITORY_TYPE_GIT:
         return idx($this->gitCommits, $identifier, array());
       case PhabricatorRepositoryType::REPOSITORY_TYPE_MERCURIAL:
         // NOTE: This will be "the branch the commit was made to", not
         // "a list of all branch heads which descend from the commit".
         // This is consistent with Mercurial, but possibly confusing.
         return idx($this->mercurialCommits, $identifier, array());
       case PhabricatorRepositoryType::REPOSITORY_TYPE_SVN:
         // Subversion doesn't have branches.
         return array();
     }
   }
 
   private function loadCommitInfoForWorker(array $all_updates) {
     $type_commit = PhabricatorRepositoryPushLog::REFTYPE_COMMIT;
 
     $map = array();
     foreach ($all_updates as $update) {
       if ($update->getRefType() != $type_commit) {
         continue;
       }
       $map[$update->getRefNew()] = array();
     }
 
     foreach ($map as $identifier => $info) {
       $ref = $this->loadCommitRefForCommit($identifier);
       $map[$identifier] += array(
         'summary'  => $ref->getSummary(),
         'branches' => $this->loadBranches($identifier),
       );
     }
 
     return $map;
   }
 
   private function isInitialImport(array $all_updates) {
     $repository = $this->getRepository();
 
     $vcs = $repository->getVersionControlSystem();
     switch ($vcs) {
       case PhabricatorRepositoryType::REPOSITORY_TYPE_SVN:
         // There is no meaningful way to import history into Subversion by
         // pushing.
         return false;
       default:
         break;
     }
 
     // Now, apply a heuristic to guess whether this is a normal commit or
     // an initial import. We guess something is an initial import if:
     //
     //   - the repository is currently empty; and
     //   - it pushes more than 7 commits at once.
     //
     // The number "7" is chosen arbitrarily as seeming reasonable. We could
     // also look at author data (do the commits come from multiple different
     // authors?) and commit date data (is the oldest commit more than 48 hours
     // old), but we don't have immediate access to those and this simple
     // heruistic might be good enough.
 
     $commit_count = 0;
     $type_commit = PhabricatorRepositoryPushLog::REFTYPE_COMMIT;
     foreach ($all_updates as $update) {
       if ($update->getRefType() != $type_commit) {
         continue;
       }
       $commit_count++;
     }
 
-    if ($commit_count <= 7) {
+    if ($commit_count <= PhabricatorRepository::IMPORT_THRESHOLD) {
       // If this pushes a very small number of commits, assume it's an
       // initial commit or stack of a few initial commits.
       return false;
     }
 
     $any_commits = id(new DiffusionCommitQuery())
       ->setViewer($this->getViewer())
       ->withRepository($repository)
       ->setLimit(1)
       ->execute();
 
     if ($any_commits) {
       // If the repository already has commits, this isn't an import.
       return false;
     }
 
     return true;
   }
 
 }
diff --git a/src/applications/diffusion/engineextension/DiffusionRepositoryURIsIndexEngineExtension.php b/src/applications/diffusion/engineextension/DiffusionRepositoryURIsIndexEngineExtension.php
index 2c1e49b3d..7fb12b6b6 100644
--- a/src/applications/diffusion/engineextension/DiffusionRepositoryURIsIndexEngineExtension.php
+++ b/src/applications/diffusion/engineextension/DiffusionRepositoryURIsIndexEngineExtension.php
@@ -1,22 +1,34 @@
 <?php
 
 final class DiffusionRepositoryURIsIndexEngineExtension
   extends PhabricatorIndexEngineExtension {
 
   const EXTENSIONKEY = 'diffusion.repositories.uri';
 
   public function getExtensionName() {
     return pht('Repository URIs');
   }
 
   public function shouldIndexObject($object) {
     return ($object instanceof PhabricatorRepository);
   }
 
   public function indexObject(
     PhabricatorIndexEngine $engine,
     $object) {
+
+    // Reload the repository to pick up URIs, which we need in order to update
+    // the URI index.
+    $object = id(new PhabricatorRepositoryQuery())
+      ->setViewer(PhabricatorUser::getOmnipotentUser())
+      ->withPHIDs(array($object->getPHID()))
+      ->needURIs(true)
+      ->executeOne();
+    if (!$object) {
+      return;
+    }
+
     $object->updateURIIndex();
   }
 
 }
diff --git a/src/applications/diffusion/exception/DiffusionDaemonLockException.php b/src/applications/diffusion/exception/DiffusionDaemonLockException.php
new file mode 100644
index 000000000..f2f5be001
--- /dev/null
+++ b/src/applications/diffusion/exception/DiffusionDaemonLockException.php
@@ -0,0 +1,3 @@
+<?php
+
+final class DiffusionDaemonLockException extends Exception {}
diff --git a/src/applications/diffusion/management/DiffusionRepositoryActionsManagementPanel.php b/src/applications/diffusion/management/DiffusionRepositoryActionsManagementPanel.php
index 4b9fa5b52..3a82879b2 100644
--- a/src/applications/diffusion/management/DiffusionRepositoryActionsManagementPanel.php
+++ b/src/applications/diffusion/management/DiffusionRepositoryActionsManagementPanel.php
@@ -1,60 +1,84 @@
 <?php
 
 final class DiffusionRepositoryActionsManagementPanel
   extends DiffusionRepositoryManagementPanel {
 
   const PANELKEY = 'actions';
 
   public function getManagementPanelLabel() {
     return pht('Actions');
   }
 
   public function getManagementPanelOrder() {
     return 1100;
   }
 
+  public function getManagementPanelIcon() {
+    $repository = $this->getRepository();
+
+    $has_any =
+      $repository->getDetail('herald-disabled') ||
+      $repository->getDetail('disable-autoclose');
+
+    // NOTE: Any value here really means something is disabled, so try to
+    // hint that a little bit with the icon.
+
+    if ($has_any) {
+      return 'fa-comment-o';
+    } else {
+      return 'fa-commenting grey';
+    }
+  }
+
+  protected function getEditEngineFieldKeys() {
+    return array(
+      'publish',
+      'autoclose',
+    );
+  }
+
   protected function buildManagementPanelActions() {
     $repository = $this->getRepository();
     $viewer = $this->getViewer();
 
     $can_edit = PhabricatorPolicyFilter::hasCapability(
       $viewer,
       $repository,
       PhabricatorPolicyCapability::CAN_EDIT);
 
-    $actions_uri = $repository->getPathURI('edit/actions/');
+    $actions_uri = $this->getEditPageURI();
 
     return array(
       id(new PhabricatorActionView())
         ->setIcon('fa-pencil')
         ->setName(pht('Edit Actions'))
         ->setHref($actions_uri)
         ->setDisabled(!$can_edit)
         ->setWorkflow(!$can_edit),
     );
   }
 
   public function buildManagementPanelContent() {
     $repository = $this->getRepository();
     $viewer = $this->getViewer();
 
     $view = id(new PHUIPropertyListView())
       ->setViewer($viewer)
       ->setActionList($this->newActions());
 
     $notify = $repository->getDetail('herald-disabled')
       ? pht('Off')
       : pht('On');
     $notify = phutil_tag('em', array(), $notify);
     $view->addProperty(pht('Publish/Notify'), $notify);
 
     $autoclose = $repository->getDetail('disable-autoclose')
       ? pht('Off')
       : pht('On');
     $autoclose = phutil_tag('em', array(), $autoclose);
     $view->addProperty(pht('Autoclose'), $autoclose);
 
     return $this->newBox(pht('Branches'), $view);
   }
 
 }
diff --git a/src/applications/diffusion/management/DiffusionRepositoryAutomationManagementPanel.php b/src/applications/diffusion/management/DiffusionRepositoryAutomationManagementPanel.php
index beed039c4..14182a044 100644
--- a/src/applications/diffusion/management/DiffusionRepositoryAutomationManagementPanel.php
+++ b/src/applications/diffusion/management/DiffusionRepositoryAutomationManagementPanel.php
@@ -1,69 +1,102 @@
 <?php
 
 final class DiffusionRepositoryAutomationManagementPanel
   extends DiffusionRepositoryManagementPanel {
 
   const PANELKEY = 'automation';
 
   public function getManagementPanelLabel() {
     return pht('Automation');
   }
 
   public function getManagementPanelOrder() {
     return 800;
   }
 
+  public function shouldEnableForRepository(
+    PhabricatorRepository $repository) {
+    return $repository->isGit();
+  }
+
+  protected function getEditEngineFieldKeys() {
+    return array(
+      'automationBlueprintPHIDs',
+    );
+  }
+
+  public function getManagementPanelIcon() {
+    $repository = $this->getRepository();
+
+    if (!$repository->canPerformAutomation()) {
+      return 'fa-truck grey';
+    }
+
+    $blueprint_phids = $repository->getAutomationBlueprintPHIDs();
+    if (!$blueprint_phids) {
+      return 'fa-truck grey';
+    }
+
+    $is_authorized = DrydockAuthorizationQuery::isFullyAuthorized(
+      $repository->getPHID(),
+      $blueprint_phids);
+    if (!$is_authorized) {
+      return 'fa-exclamation-triangle yellow';
+    }
+
+    return 'fa-truck';
+  }
+
   protected function buildManagementPanelActions() {
     $repository = $this->getRepository();
     $viewer = $this->getViewer();
 
     $can_edit = PhabricatorPolicyFilter::hasCapability(
       $viewer,
       $repository,
       PhabricatorPolicyCapability::CAN_EDIT);
 
     $can_test = $can_edit && $repository->canPerformAutomation();
 
-    $automation_uri = $repository->getPathURI('edit/automation/');
+    $automation_uri = $this->getEditPageURI();
     $test_uri = $repository->getPathURI('edit/testautomation/');
 
     return array(
       id(new PhabricatorActionView())
         ->setIcon('fa-pencil')
         ->setName(pht('Edit Automation'))
         ->setHref($automation_uri)
         ->setDisabled(!$can_edit)
         ->setWorkflow(!$can_edit),
       id(new PhabricatorActionView())
         ->setIcon('fa-gamepad')
         ->setName(pht('Test Configuration'))
         ->setWorkflow(true)
         ->setDisabled(!$can_test)
         ->setHref($test_uri),
     );
   }
 
   public function buildManagementPanelContent() {
     $repository = $this->getRepository();
     $viewer = $this->getViewer();
 
     $view = id(new PHUIPropertyListView())
       ->setViewer($viewer)
       ->setActionList($this->newActions());
 
     $blueprint_phids = $repository->getAutomationBlueprintPHIDs();
     if (!$blueprint_phids) {
       $blueprint_view = phutil_tag('em', array(), pht('Not Configured'));
     } else {
       $blueprint_view = id(new DrydockObjectAuthorizationView())
         ->setUser($viewer)
         ->setObjectPHID($repository->getPHID())
         ->setBlueprintPHIDs($blueprint_phids);
     }
 
     $view->addProperty(pht('Automation'), $blueprint_view);
 
     return $this->newBox(pht('Automation'), $view);
   }
 
 }
diff --git a/src/applications/diffusion/management/DiffusionRepositoryBasicsManagementPanel.php b/src/applications/diffusion/management/DiffusionRepositoryBasicsManagementPanel.php
index 7ddb38d34..6a2f70d69 100644
--- a/src/applications/diffusion/management/DiffusionRepositoryBasicsManagementPanel.php
+++ b/src/applications/diffusion/management/DiffusionRepositoryBasicsManagementPanel.php
@@ -1,167 +1,218 @@
 <?php
 
 final class DiffusionRepositoryBasicsManagementPanel
   extends DiffusionRepositoryManagementPanel {
 
   const PANELKEY = 'basics';
 
   public function getManagementPanelLabel() {
     return pht('Basics');
   }
 
   public function getManagementPanelOrder() {
     return 100;
   }
 
+  public function getManagementPanelIcon() {
+    $repository = $this->getRepository();
+
+    if (!$repository->isTracked()) {
+      return 'fa-ban indigo';
+    } else {
+      return 'fa-code';
+    }
+  }
+
+  protected function getEditEngineFieldKeys() {
+    return array(
+      'name',
+      'callsign',
+      'shortName',
+      'description',
+      'projectPHIDs',
+    );
+  }
+
   protected function buildManagementPanelActions() {
     $repository = $this->getRepository();
     $viewer = $this->getViewer();
 
     $can_edit = PhabricatorPolicyFilter::hasCapability(
       $viewer,
       $repository,
       PhabricatorPolicyCapability::CAN_EDIT);
 
-    $edit_uri = $repository->getPathURI('manage/');
+    $edit_uri = $this->getEditPageURI();
     $activate_uri = $repository->getPathURI('edit/activate/');
     $delete_uri = $repository->getPathURI('edit/delete/');
-    $encoding_uri = $repository->getPathURI('edit/encoding/');
+    $encoding_uri = $this->getEditPageURI('encoding');
     $dangerous_uri = $repository->getPathURI('edit/dangerous/');
 
     if ($repository->isTracked()) {
       $activate_icon = 'fa-pause';
       $activate_label = pht('Deactivate Repository');
     } else {
       $activate_icon = 'fa-play';
       $activate_label = pht('Activate Repository');
     }
 
     $should_dangerous = $repository->shouldAllowDangerousChanges();
     if ($should_dangerous) {
       $dangerous_icon = 'fa-shield';
       $dangerous_name = pht('Prevent Dangerous Changes');
       $can_dangerous = $can_edit;
     } else {
       $dangerous_icon = 'fa-bullseye';
       $dangerous_name = pht('Allow Dangerous Changes');
       $can_dangerous = ($can_edit && $repository->canAllowDangerousChanges());
     }
 
     return array(
       id(new PhabricatorActionView())
         ->setIcon('fa-pencil')
         ->setName(pht('Edit Basic Information'))
         ->setHref($edit_uri)
         ->setDisabled(!$can_edit)
         ->setWorkflow(!$can_edit),
       id(new PhabricatorActionView())
         ->setIcon('fa-text-width')
         ->setName(pht('Edit Text Encoding'))
         ->setHref($encoding_uri)
         ->setDisabled(!$can_edit)
         ->setWorkflow(!$can_edit),
       id(new PhabricatorActionView())
         ->setIcon($dangerous_icon)
         ->setName($dangerous_name)
         ->setHref($dangerous_uri)
         ->setDisabled(!$can_dangerous)
         ->setWorkflow(true),
       id(new PhabricatorActionView())
         ->setHref($activate_uri)
         ->setIcon($activate_icon)
         ->setName($activate_label)
         ->setDisabled(!$can_edit)
         ->setWorkflow(true),
       id(new PhabricatorActionView())
         ->setName(pht('Delete Repository'))
         ->setIcon('fa-times')
         ->setHref($delete_uri)
         ->setDisabled(true)
         ->setWorkflow(true),
     );
   }
 
   public function buildManagementPanelContent() {
     $result = array();
 
-    $result[] = $this->newBox(pht('Repository Basics'), $this->buildBasics());
+    $basics = $this->newBox(pht('Repository Basics'), $this->buildBasics());
+
+    $repository = $this->getRepository();
+    $is_new = $repository->isNewlyInitialized();
+    if ($is_new) {
+      $messages = array();
+
+      $messages[] = pht(
+        'This newly created repository is not active yet. Configure policies, '.
+        'options, and URIs. When ready, %s the repository.',
+        phutil_tag('strong', array(), pht('Activate')));
+
+      if ($repository->isHosted()) {
+        $messages[] = pht(
+          'If activated now, this repository will become a new hosted '.
+          'repository. To observe an existing repository instead, configure '.
+          'it in the %s panel.',
+          phutil_tag('strong', array(), pht('URIs')));
+      } else {
+        $messages[] = pht(
+          'If activated now, this repository will observe an existing remote '.
+          'repository and begin importing changes.');
+      }
+
+      $info_view = id(new PHUIInfoView())
+        ->setSeverity(PHUIInfoView::SEVERITY_NOTICE)
+        ->setErrors($messages);
+
+      $basics->setInfoView($info_view);
+    }
+
+    $result[] = $basics;
 
     $description = $this->buildDescription();
     if ($description) {
       $result[] = $this->newBox(pht('Description'), $description);
     }
 
     return $result;
   }
 
   private function buildBasics() {
     $repository = $this->getRepository();
     $viewer = $this->getViewer();
 
     $view = id(new PHUIPropertyListView())
       ->setViewer($viewer)
       ->setActionList($this->newActions());
 
     $name = $repository->getName();
     $view->addProperty(pht('Name'), $name);
 
     $type = PhabricatorRepositoryType::getNameForRepositoryType(
       $repository->getVersionControlSystem());
     $view->addProperty(pht('Type'), $type);
 
     $callsign = $repository->getCallsign();
     if (!strlen($callsign)) {
       $callsign = phutil_tag('em', array(), pht('No Callsign'));
     }
     $view->addProperty(pht('Callsign'), $callsign);
 
     $short_name = $repository->getRepositorySlug();
     if ($short_name === null) {
       $short_name = $repository->getCloneName();
       $short_name = phutil_tag('em', array(), $short_name);
     }
     $view->addProperty(pht('Short Name'), $short_name);
 
     $encoding = $repository->getDetail('encoding');
     if (!$encoding) {
       $encoding = phutil_tag('em', array(), pht('Use Default (UTF-8)'));
     }
     $view->addProperty(pht('Encoding'), $encoding);
 
     $can_dangerous = $repository->canAllowDangerousChanges();
     if (!$can_dangerous) {
       $dangerous = phutil_tag('em', array(), pht('Not Preventable'));
     } else {
       $should_dangerous = $repository->shouldAllowDangerousChanges();
       if ($should_dangerous) {
         $dangerous = pht('Allowed');
       } else {
         $dangerous = pht('Not Allowed');
       }
     }
 
     $view->addProperty(pht('Dangerous Changes'), $dangerous);
 
     return $view;
   }
 
 
   private function buildDescription() {
     $repository = $this->getRepository();
     $viewer = $this->getViewer();
 
     $description = $repository->getDetail('description');
 
     $view = id(new PHUIPropertyListView())
       ->setViewer($viewer);
     if (!strlen($description)) {
       $description = phutil_tag('em', array(), pht('No description provided.'));
     } else {
       $description = new PHUIRemarkupView($viewer, $description);
     }
     $view->addTextContent($description);
 
     return $view;
   }
 
 }
diff --git a/src/applications/diffusion/management/DiffusionRepositoryBranchesManagementPanel.php b/src/applications/diffusion/management/DiffusionRepositoryBranchesManagementPanel.php
index 54184cd8b..b3e7926cf 100644
--- a/src/applications/diffusion/management/DiffusionRepositoryBranchesManagementPanel.php
+++ b/src/applications/diffusion/management/DiffusionRepositoryBranchesManagementPanel.php
@@ -1,68 +1,96 @@
 <?php
 
 final class DiffusionRepositoryBranchesManagementPanel
   extends DiffusionRepositoryManagementPanel {
 
   const PANELKEY = 'branches';
 
   public function getManagementPanelLabel() {
     return pht('Branches');
   }
 
   public function getManagementPanelOrder() {
     return 1000;
   }
 
+  public function shouldEnableForRepository(
+    PhabricatorRepository $repository) {
+    return ($repository->isGit() || $repository->isHg());
+  }
+
+  public function getManagementPanelIcon() {
+    $repository = $this->getRepository();
+
+    $has_any =
+      $repository->getDetail('default-branch') ||
+      $repository->getDetail('branch-filter') ||
+      $repository->getDetail('close-commits-filter');
+
+    if ($has_any) {
+      return 'fa-code-fork';
+    } else {
+      return 'fa-code-fork grey';
+    }
+  }
+
+  protected function getEditEngineFieldKeys() {
+    return array(
+      'defaultBranch',
+      'trackOnly',
+      'autocloseOnly',
+    );
+  }
+
   protected function buildManagementPanelActions() {
     $repository = $this->getRepository();
     $viewer = $this->getViewer();
 
     $can_edit = PhabricatorPolicyFilter::hasCapability(
       $viewer,
       $repository,
       PhabricatorPolicyCapability::CAN_EDIT);
 
-    $branches_uri = $repository->getPathURI('edit/branches/');
+    $branches_uri = $this->getEditPageURI();
 
     return array(
       id(new PhabricatorActionView())
         ->setIcon('fa-pencil')
         ->setName(pht('Edit Branches'))
         ->setHref($branches_uri)
         ->setDisabled(!$can_edit)
         ->setWorkflow(!$can_edit),
     );
   }
 
   public function buildManagementPanelContent() {
     $repository = $this->getRepository();
     $viewer = $this->getViewer();
 
     $view = id(new PHUIPropertyListView())
       ->setViewer($viewer)
       ->setActionList($this->newActions());
 
     $default_branch = nonempty(
       $repository->getHumanReadableDetail('default-branch'),
       phutil_tag('em', array(), $repository->getDefaultBranch()));
     $view->addProperty(pht('Default Branch'), $default_branch);
 
     $track_only = nonempty(
       $repository->getHumanReadableDetail('branch-filter', array()),
       phutil_tag('em', array(), pht('Track All Branches')));
     $view->addProperty(pht('Track Only'), $track_only);
 
     $autoclose_only = nonempty(
       $repository->getHumanReadableDetail('close-commits-filter', array()),
       phutil_tag('em', array(), pht('Autoclose On All Branches')));
 
     if ($repository->getDetail('disable-autoclose')) {
       $autoclose_only = phutil_tag('em', array(), pht('Disabled'));
     }
 
     $view->addProperty(pht('Autoclose Only'), $autoclose_only);
 
     return $this->newBox(pht('Branches'), $view);
   }
 
 }
diff --git a/src/applications/diffusion/management/DiffusionRepositoryDocumentationManagementPanel.php b/src/applications/diffusion/management/DiffusionRepositoryDocumentationManagementPanel.php
new file mode 100644
index 000000000..ca2c577b3
--- /dev/null
+++ b/src/applications/diffusion/management/DiffusionRepositoryDocumentationManagementPanel.php
@@ -0,0 +1,29 @@
+<?php
+
+final class DiffusionRepositoryDocumentationManagementPanel
+  extends DiffusionRepositoryManagementPanel {
+
+  const PANELKEY = 'documentation';
+
+  public function getManagementPanelLabel() {
+    return pht('Documentation');
+  }
+
+  public function getManagementPanelOrder() {
+    return 3000;
+  }
+
+  public function getManagementPanelIcon() {
+    return 'fa-book';
+  }
+
+  public function buildManagementPanelContent() {
+    return null;
+  }
+
+  public function getPanelNavigationURI() {
+    return PhabricatorEnv::getDoclink(
+      'Diffusion User Guide: Managing Repositories');
+  }
+
+}
diff --git a/src/applications/diffusion/management/DiffusionRepositoryHistoryManagementPanel.php b/src/applications/diffusion/management/DiffusionRepositoryHistoryManagementPanel.php
index e574030bf..c1a812dca 100644
--- a/src/applications/diffusion/management/DiffusionRepositoryHistoryManagementPanel.php
+++ b/src/applications/diffusion/management/DiffusionRepositoryHistoryManagementPanel.php
@@ -1,21 +1,25 @@
 <?php
 
 final class DiffusionRepositoryHistoryManagementPanel
   extends DiffusionRepositoryManagementPanel {
 
   const PANELKEY = 'history';
 
   public function getManagementPanelLabel() {
     return pht('History');
   }
 
   public function getManagementPanelOrder() {
     return 2000;
   }
 
+  public function getManagementPanelIcon() {
+    return 'fa-list-ul';
+  }
+
   public function buildManagementPanelContent() {
     return $this->newTimeline();
   }
 
 
 }
diff --git a/src/applications/diffusion/management/DiffusionRepositoryManagementPanel.php b/src/applications/diffusion/management/DiffusionRepositoryManagementPanel.php
index f278c3323..34047cdb8 100644
--- a/src/applications/diffusion/management/DiffusionRepositoryManagementPanel.php
+++ b/src/applications/diffusion/management/DiffusionRepositoryManagementPanel.php
@@ -1,101 +1,151 @@
 <?php
 
 abstract class DiffusionRepositoryManagementPanel
   extends Phobject {
 
   private $viewer;
   private $repository;
   private $controller;
 
   final public function setViewer(PhabricatorUser $viewer) {
     $this->viewer = $viewer;
     return $this;
   }
 
   final public function getViewer() {
     return $this->viewer;
   }
 
   final public function setRepository(PhabricatorRepository $repository) {
     $this->repository = $repository;
     return $this;
   }
 
   final public function getRepository() {
     return $this->repository;
   }
 
   final public function setController(PhabricatorController $controller) {
     $this->controller = $controller;
     return $this;
   }
 
   final public function getManagementPanelKey() {
     return $this->getPhobjectClassConstant('PANELKEY');
   }
 
   abstract public function getManagementPanelLabel();
   abstract public function getManagementPanelOrder();
   abstract public function buildManagementPanelContent();
 
+  public function getManagementPanelIcon() {
+    return 'fa-pencil';
+  }
+
   protected function buildManagementPanelActions() {
     return array();
   }
 
+  public function shouldEnableForRepository(
+    PhabricatorRepository $repository) {
+    return true;
+  }
+
   final protected function newActions() {
     $actions = $this->buildManagementPanelActions();
     if (!$actions) {
       return null;
     }
 
     $viewer = $this->getViewer();
 
     $action_list = id(new PhabricatorActionListView())
       ->setViewer($viewer);
 
     foreach ($actions as $action) {
       $action_list->addAction($action);
     }
 
     return $action_list;
   }
 
   public function buildManagementPanelCurtain() {
     // TODO: Delete or fix this, curtains always render in the left gutter
     // at the moment.
     return null;
 
     $actions = $this->newActions();
     if (!$actions) {
       return null;
     }
 
     $viewer = $this->getViewer();
 
     $curtain = id(new PHUICurtainView())
       ->setViewer($viewer)
       ->setActionList($actions);
 
     return $curtain;
   }
 
   public static function getAllPanels() {
     return id(new PhutilClassMapQuery())
       ->setAncestorClass(__CLASS__)
       ->setUniqueMethod('getManagementPanelKey')
       ->setSortMethod('getManagementPanelOrder')
       ->execute();
   }
 
   final protected function newBox($header_text, $body) {
     return id(new PHUIObjectBoxView())
       ->setHeaderText($header_text)
       ->setBackground(PHUIObjectBoxView::BLUE_PROPERTY)
       ->appendChild($body);
   }
 
   final protected function newTimeline() {
     return $this->controller->newTimeline($this->getRepository());
   }
 
+  final public function getPanelURI() {
+    $repository = $this->getRepository();
+    $key = $this->getManagementPanelKey();
+    return $repository->getPathURI("manage/{$key}/");
+  }
+
+  final public function newEditEnginePage() {
+    $field_keys = $this->getEditEngineFieldKeys();
+    if (!$field_keys) {
+      return null;
+    }
+
+    $key = $this->getManagementPanelKey();
+    $label = $this->getManagementPanelLabel();
+    $panel_uri = $this->getPanelURI();
+
+    return id(new PhabricatorEditPage())
+      ->setKey($key)
+      ->setLabel($label)
+      ->setViewURI($panel_uri)
+      ->setFieldKeys($field_keys);
+  }
+
+  protected function getEditEngineFieldKeys() {
+    return array();
+  }
+
+  protected function getEditPageURI($page = null) {
+    if ($page === null) {
+      $page = $this->getManagementPanelKey();
+    }
+
+    $repository = $this->getRepository();
+    $id = $repository->getID();
+    return "/diffusion/edit/{$id}/page/{$page}/";
+  }
+
+  public function getPanelNavigationURI() {
+    return $this->getPanelURI();
+  }
+
 }
diff --git a/src/applications/diffusion/management/DiffusionRepositoryPoliciesManagementPanel.php b/src/applications/diffusion/management/DiffusionRepositoryPoliciesManagementPanel.php
index 9bfe57207..081869fba 100644
--- a/src/applications/diffusion/management/DiffusionRepositoryPoliciesManagementPanel.php
+++ b/src/applications/diffusion/management/DiffusionRepositoryPoliciesManagementPanel.php
@@ -1,73 +1,114 @@
 <?php
 
 final class DiffusionRepositoryPoliciesManagementPanel
   extends DiffusionRepositoryManagementPanel {
 
   const PANELKEY = 'policies';
 
   public function getManagementPanelLabel() {
     return pht('Policies');
   }
 
   public function getManagementPanelOrder() {
     return 300;
   }
 
+  public function getManagementPanelIcon() {
+    $viewer = $this->getViewer();
+    $repository = $this->getRepository();
+
+    $can_view = PhabricatorPolicyCapability::CAN_VIEW;
+    $can_edit = PhabricatorPolicyCapability::CAN_EDIT;
+    $can_push = DiffusionPushCapability::CAPABILITY;
+
+    $actual_values = array(
+      'spacePHID' => $repository->getSpacePHID(),
+      'view' => $repository->getPolicy($can_view),
+      'edit' => $repository->getPolicy($can_edit),
+      'push' => $repository->getPolicy($can_push),
+    );
+
+    $default = PhabricatorRepository::initializeNewRepository(
+      $viewer);
+
+    $default_values = array(
+      'spacePHID' => $default->getSpacePHID(),
+      'view' => $default->getPolicy($can_view),
+      'edit' => $default->getPolicy($can_edit),
+      'push' => $default->getPolicy($can_push),
+    );
+
+    if ($actual_values === $default_values) {
+      return 'fa-lock grey';
+    } else {
+      return 'fa-lock';
+    }
+  }
+
+  protected function getEditEngineFieldKeys() {
+    return array(
+      'policy.view',
+      'policy.edit',
+      'spacePHID',
+      'policy.push',
+    );
+  }
+
   protected function buildManagementPanelActions() {
     $repository = $this->getRepository();
     $viewer = $this->getViewer();
 
     $can_edit = PhabricatorPolicyFilter::hasCapability(
       $viewer,
       $repository,
       PhabricatorPolicyCapability::CAN_EDIT);
 
-    $edit_uri = $repository->getPathURI('manage/');
+    $edit_uri = $this->getEditPageURI();
 
     return array(
       id(new PhabricatorActionView())
         ->setIcon('fa-pencil')
         ->setName(pht('Edit Policies'))
         ->setHref($edit_uri)
         ->setDisabled(!$can_edit)
         ->setWorkflow(!$can_edit),
     );
   }
 
   public function buildManagementPanelContent() {
     $repository = $this->getRepository();
     $viewer = $this->getViewer();
 
     $view = id(new PHUIPropertyListView())
       ->setViewer($viewer)
       ->setActionList($this->newActions());
 
     $descriptions = PhabricatorPolicyQuery::renderPolicyDescriptions(
       $viewer,
       $repository);
 
     $view_parts = array();
     if (PhabricatorSpacesNamespaceQuery::getViewerSpacesExist($viewer)) {
       $space_phid = PhabricatorSpacesNamespaceQuery::getObjectSpacePHID(
         $repository);
       $view_parts[] = $viewer->renderHandle($space_phid);
     }
     $view_parts[] = $descriptions[PhabricatorPolicyCapability::CAN_VIEW];
 
     $view->addProperty(
       pht('Visible To'),
       phutil_implode_html(" \xC2\xB7 ", $view_parts));
 
     $view->addProperty(
       pht('Editable By'),
       $descriptions[PhabricatorPolicyCapability::CAN_EDIT]);
 
     $pushable = $repository->isHosted()
       ? $descriptions[DiffusionPushCapability::CAPABILITY]
       : phutil_tag('em', array(), pht('Not a Hosted Repository'));
     $view->addProperty(pht('Pushable By'), $pushable);
 
     return $this->newBox(pht('Policies'), $view);
   }
 
 }
diff --git a/src/applications/diffusion/management/DiffusionRepositoryStagingManagementPanel.php b/src/applications/diffusion/management/DiffusionRepositoryStagingManagementPanel.php
index bff042019..79b716e82 100644
--- a/src/applications/diffusion/management/DiffusionRepositoryStagingManagementPanel.php
+++ b/src/applications/diffusion/management/DiffusionRepositoryStagingManagementPanel.php
@@ -1,55 +1,79 @@
 <?php
 
 final class DiffusionRepositoryStagingManagementPanel
   extends DiffusionRepositoryManagementPanel {
 
   const PANELKEY = 'staging';
 
   public function getManagementPanelLabel() {
     return pht('Staging Area');
   }
 
   public function getManagementPanelOrder() {
     return 700;
   }
 
+  public function shouldEnableForRepository(
+    PhabricatorRepository $repository) {
+    return $repository->isGit();
+  }
+
+
+  public function getManagementPanelIcon() {
+    $repository = $this->getRepository();
+
+    $staging_uri = $repository->getStagingURI();
+
+    if ($staging_uri) {
+      return 'fa-upload';
+    } else {
+      return 'fa-upload grey';
+    }
+  }
+
+  protected function getEditEngineFieldKeys() {
+    return array(
+      'stagingAreaURI',
+    );
+  }
+
   protected function buildManagementPanelActions() {
     $repository = $this->getRepository();
     $viewer = $this->getViewer();
 
     $can_edit = PhabricatorPolicyFilter::hasCapability(
       $viewer,
       $repository,
       PhabricatorPolicyCapability::CAN_EDIT);
 
-    $staging_uri = $repository->getPathURI('edit/staging/');
+    $staging_uri = $this->getEditPageURI();
 
     return array(
       id(new PhabricatorActionView())
         ->setIcon('fa-pencil')
         ->setName(pht('Edit Staging'))
         ->setHref($staging_uri)
         ->setDisabled(!$can_edit)
         ->setWorkflow(!$can_edit),
     );
   }
 
   public function buildManagementPanelContent() {
     $repository = $this->getRepository();
     $viewer = $this->getViewer();
 
     $view = id(new PHUIPropertyListView())
       ->setViewer($viewer)
       ->setActionList($this->newActions());
 
     $staging_uri = $repository->getStagingURI();
     if (!$staging_uri) {
       $staging_uri = phutil_tag('em', array(), pht('No Staging Area'));
     }
 
     $view->addProperty(pht('Staging Area URI'), $staging_uri);
 
     return $this->newBox(pht('Staging Area'), $view);
   }
 
 }
diff --git a/src/applications/diffusion/management/DiffusionRepositoryStatusManagementPanel.php b/src/applications/diffusion/management/DiffusionRepositoryStatusManagementPanel.php
index 166674998..573162b92 100644
--- a/src/applications/diffusion/management/DiffusionRepositoryStatusManagementPanel.php
+++ b/src/applications/diffusion/management/DiffusionRepositoryStatusManagementPanel.php
@@ -1,473 +1,513 @@
 <?php
 
 final class DiffusionRepositoryStatusManagementPanel
   extends DiffusionRepositoryManagementPanel {
 
   const PANELKEY = 'status';
 
   public function getManagementPanelLabel() {
     return pht('Status');
   }
 
   public function getManagementPanelOrder() {
     return 200;
   }
 
+  public function getManagementPanelIcon() {
+    $repository = $this->getRepository();
+
+    // TODO: We could try to show a warning icon in more cases, but just
+    // raise in the most serious cases for now.
+    $messages = $this->loadStatusMessages($repository);
+
+    $raw_error = $this->buildRepositoryRawError($repository, $messages);
+    if ($raw_error) {
+      return 'fa-exclamation-triangle red';
+    }
+
+    return 'fa-check grey';
+  }
+
   protected function buildManagementPanelActions() {
     $repository = $this->getRepository();
     $viewer = $this->getViewer();
 
     $can_edit = PhabricatorPolicyFilter::hasCapability(
       $viewer,
       $repository,
       PhabricatorPolicyCapability::CAN_EDIT);
 
     $update_uri = $repository->getPathURI('edit/update/');
 
     return array(
       id(new PhabricatorActionView())
         ->setIcon('fa-refresh')
         ->setName(pht('Update Now'))
         ->setWorkflow(true)
         ->setDisabled(!$can_edit)
         ->setHref($update_uri),
     );
   }
 
   public function buildManagementPanelContent() {
     $repository = $this->getRepository();
     $viewer = $this->getViewer();
 
     $view = id(new PHUIPropertyListView())
       ->setViewer($viewer)
       ->setActionList($this->newActions());
 
     $view->addProperty(
       pht('Update Frequency'),
       $this->buildRepositoryUpdateInterval($repository));
 
-    $messages = id(new PhabricatorRepositoryStatusMessage())
-      ->loadAllWhere('repositoryID = %d', $repository->getID());
-    $messages = mpull($messages, null, 'getStatusType');
+    $messages = $this->loadStatusMessages($repository);
 
     $status = $this->buildRepositoryStatus($repository, $messages);
     $raw_error = $this->buildRepositoryRawError($repository, $messages);
 
     $view->addProperty(pht('Status'), $status);
     if ($raw_error) {
       $view->addSectionHeader(pht('Raw Error'));
       $view->addTextContent($raw_error);
     }
 
     return $this->newBox(pht('Status'), $view);
   }
 
   private function buildRepositoryUpdateInterval(
     PhabricatorRepository $repository) {
 
     $smart_wait = $repository->loadUpdateInterval();
 
     $doc_href = PhabricatorEnv::getDoclink(
       'Diffusion User Guide: Repository Updates');
 
     return array(
       phutil_format_relative_time_detailed($smart_wait),
       " \xC2\xB7 ",
       phutil_tag(
         'a',
         array(
           'href' => $doc_href,
           'target' => '_blank',
         ),
         pht('Learn More')),
     );
   }
 
   private function buildRepositoryStatus(
     PhabricatorRepository $repository,
     array $messages) {
 
     $viewer = $this->getViewer();
     $is_cluster = $repository->getAlmanacServicePHID();
 
     $view = new PHUIStatusListView();
 
     if ($repository->isTracked()) {
       $view->addItem(
         id(new PHUIStatusItemView())
           ->setIcon(PHUIStatusItemView::ICON_ACCEPT, 'green')
           ->setTarget(pht('Repository Active')));
     } else {
       $view->addItem(
         id(new PHUIStatusItemView())
           ->setIcon(PHUIStatusItemView::ICON_WARNING, 'bluegrey')
           ->setTarget(pht('Repository Inactive'))
           ->setNote(
             pht('Activate this repository to begin or resume import.')));
       return $view;
     }
 
     $binaries = array();
     $svnlook_check = false;
     switch ($repository->getVersionControlSystem()) {
       case PhabricatorRepositoryType::REPOSITORY_TYPE_GIT:
         $binaries[] = 'git';
         break;
       case PhabricatorRepositoryType::REPOSITORY_TYPE_SVN:
         $binaries[] = 'svn';
         break;
       case PhabricatorRepositoryType::REPOSITORY_TYPE_MERCURIAL:
         $binaries[] = 'hg';
         break;
     }
 
     if ($repository->isHosted()) {
-      if ($repository->getServeOverHTTP() != PhabricatorRepository::SERVE_OFF) {
+      $proto_https = PhabricatorRepositoryURI::BUILTIN_PROTOCOL_HTTPS;
+      $proto_http = PhabricatorRepositoryURI::BUILTIN_PROTOCOL_HTTP;
+      $can_http = $repository->canServeProtocol($proto_http, false) ||
+                  $repository->canServeProtocol($proto_https, false);
+
+      if ($can_http) {
         switch ($repository->getVersionControlSystem()) {
           case PhabricatorRepositoryType::REPOSITORY_TYPE_GIT:
             $binaries[] = 'git-http-backend';
             break;
           case PhabricatorRepositoryType::REPOSITORY_TYPE_SVN:
             $binaries[] = 'svnserve';
             $binaries[] = 'svnadmin';
             $binaries[] = 'svnlook';
             $svnlook_check = true;
             break;
           case PhabricatorRepositoryType::REPOSITORY_TYPE_MERCURIAL:
             $binaries[] = 'hg';
             break;
         }
       }
-      if ($repository->getServeOverSSH() != PhabricatorRepository::SERVE_OFF) {
+
+
+      $proto_ssh = PhabricatorRepositoryURI::BUILTIN_PROTOCOL_SSH;
+      $can_ssh = $repository->canServeProtocol($proto_ssh, false);
+
+      if ($can_ssh) {
         switch ($repository->getVersionControlSystem()) {
           case PhabricatorRepositoryType::REPOSITORY_TYPE_GIT:
             $binaries[] = 'git-receive-pack';
             $binaries[] = 'git-upload-pack';
             break;
           case PhabricatorRepositoryType::REPOSITORY_TYPE_SVN:
             $binaries[] = 'svnserve';
             $binaries[] = 'svnadmin';
             $binaries[] = 'svnlook';
             $svnlook_check = true;
             break;
           case PhabricatorRepositoryType::REPOSITORY_TYPE_MERCURIAL:
             $binaries[] = 'hg';
             break;
         }
       }
     }
 
     $binaries = array_unique($binaries);
     if (!$is_cluster) {
       // We're only checking for binaries if we aren't running with a cluster
       // configuration. In theory, we could check for binaries on the
       // repository host machine, but we'd need to make this more complicated
       // to do that.
 
       foreach ($binaries as $binary) {
         $where = Filesystem::resolveBinary($binary);
         if (!$where) {
           $view->addItem(
             id(new PHUIStatusItemView())
               ->setIcon(PHUIStatusItemView::ICON_WARNING, 'red')
               ->setTarget(
                 pht('Missing Binary %s', phutil_tag('tt', array(), $binary)))
               ->setNote(pht(
                 "Unable to find this binary in the webserver's PATH. You may ".
                 "need to configure %s.",
                 $this->getEnvConfigLink())));
         } else {
           $view->addItem(
             id(new PHUIStatusItemView())
               ->setIcon(PHUIStatusItemView::ICON_ACCEPT, 'green')
               ->setTarget(
                 pht('Found Binary %s', phutil_tag('tt', array(), $binary)))
               ->setNote(phutil_tag('tt', array(), $where)));
         }
       }
 
       // This gets checked generically above. However, for svn commit hooks, we
       // need this to be in environment.append-paths because subversion strips
       // PATH.
       if ($svnlook_check) {
         $where = Filesystem::resolveBinary('svnlook');
         if ($where) {
           $path = substr($where, 0, strlen($where) - strlen('svnlook'));
           $dirs = PhabricatorEnv::getEnvConfig('environment.append-paths');
           $in_path = false;
           foreach ($dirs as $dir) {
             if (Filesystem::isDescendant($path, $dir)) {
               $in_path = true;
               break;
             }
           }
           if (!$in_path) {
             $view->addItem(
               id(new PHUIStatusItemView())
               ->setIcon(PHUIStatusItemView::ICON_WARNING, 'red')
               ->setTarget(
                 pht('Missing Binary %s', phutil_tag('tt', array(), $binary)))
               ->setNote(pht(
                   'Unable to find this binary in `%s`. '.
                   'You need to configure %s and include %s.',
                   'environment.append-paths',
                   $this->getEnvConfigLink(),
                   $path)));
           }
         }
       }
     }
 
     $doc_href = PhabricatorEnv::getDocLink('Managing Daemons with phd');
 
     $daemon_instructions = pht(
       'Use %s to start daemons. See %s.',
       phutil_tag('tt', array(), 'bin/phd start'),
       phutil_tag(
         'a',
         array(
           'href' => $doc_href,
         ),
         pht('Managing Daemons with phd')));
 
 
     $pull_daemon = id(new PhabricatorDaemonLogQuery())
       ->setViewer(PhabricatorUser::getOmnipotentUser())
       ->withStatus(PhabricatorDaemonLogQuery::STATUS_ALIVE)
       ->withDaemonClasses(array('PhabricatorRepositoryPullLocalDaemon'))
       ->setLimit(1)
       ->execute();
 
     if ($pull_daemon) {
 
       // TODO: In a cluster environment, we need a daemon on this repository's
       // host, specifically, and we aren't checking for that right now. This
       // is a reasonable proxy for things being more-or-less correctly set up,
       // though.
 
       $view->addItem(
         id(new PHUIStatusItemView())
           ->setIcon(PHUIStatusItemView::ICON_ACCEPT, 'green')
           ->setTarget(pht('Pull Daemon Running')));
     } else {
       $view->addItem(
         id(new PHUIStatusItemView())
           ->setIcon(PHUIStatusItemView::ICON_WARNING, 'red')
           ->setTarget(pht('Pull Daemon Not Running'))
           ->setNote($daemon_instructions));
     }
 
 
     $task_daemon = id(new PhabricatorDaemonLogQuery())
       ->setViewer(PhabricatorUser::getOmnipotentUser())
       ->withStatus(PhabricatorDaemonLogQuery::STATUS_ALIVE)
       ->withDaemonClasses(array('PhabricatorTaskmasterDaemon'))
       ->setLimit(1)
       ->execute();
     if ($task_daemon) {
       $view->addItem(
         id(new PHUIStatusItemView())
           ->setIcon(PHUIStatusItemView::ICON_ACCEPT, 'green')
           ->setTarget(pht('Task Daemon Running')));
     } else {
       $view->addItem(
         id(new PHUIStatusItemView())
           ->setIcon(PHUIStatusItemView::ICON_WARNING, 'red')
           ->setTarget(pht('Task Daemon Not Running'))
           ->setNote($daemon_instructions));
     }
 
 
     if ($is_cluster) {
       // Just omit this status check for now in cluster environments. We
       // could make a service call and pull it from the repository host
       // eventually.
     } else if ($repository->usesLocalWorkingCopy()) {
       $local_parent = dirname($repository->getLocalPath());
       if (Filesystem::pathExists($local_parent)) {
         $view->addItem(
           id(new PHUIStatusItemView())
             ->setIcon(PHUIStatusItemView::ICON_ACCEPT, 'green')
             ->setTarget(pht('Storage Directory OK'))
             ->setNote(phutil_tag('tt', array(), $local_parent)));
       } else {
         $view->addItem(
           id(new PHUIStatusItemView())
             ->setIcon(PHUIStatusItemView::ICON_WARNING, 'red')
             ->setTarget(pht('No Storage Directory'))
             ->setNote(
               pht(
                 'Storage directory %s does not exist, or is not readable by '.
                 'the webserver. Create this directory or make it readable.',
                 phutil_tag('tt', array(), $local_parent))));
         return $view;
       }
 
       $local_path = $repository->getLocalPath();
       $message = idx($messages, PhabricatorRepositoryStatusMessage::TYPE_INIT);
       if ($message) {
         switch ($message->getStatusCode()) {
           case PhabricatorRepositoryStatusMessage::CODE_ERROR:
             $view->addItem(
               id(new PHUIStatusItemView())
               ->setIcon(PHUIStatusItemView::ICON_WARNING, 'red')
                 ->setTarget(pht('Initialization Error'))
                 ->setNote($message->getParameter('message')));
             return $view;
           case PhabricatorRepositoryStatusMessage::CODE_OKAY:
               if (Filesystem::pathExists($local_path)) {
                 $view->addItem(
                   id(new PHUIStatusItemView())
                     ->setIcon(PHUIStatusItemView::ICON_ACCEPT, 'green')
                     ->setTarget(pht('Working Copy OK'))
                     ->setNote(phutil_tag('tt', array(), $local_path)));
               } else {
                 $view->addItem(
                   id(new PHUIStatusItemView())
                     ->setIcon(PHUIStatusItemView::ICON_WARNING, 'red')
                     ->setTarget(pht('Working Copy Error'))
                     ->setNote(
                       pht(
                         'Working copy %s has been deleted, or is not '.
                         'readable by the webserver. Make this directory '.
                         'readable. If it has been deleted, the daemons should '.
                         'restore it automatically.',
                         phutil_tag('tt', array(), $local_path))));
                 return $view;
               }
             break;
           case PhabricatorRepositoryStatusMessage::CODE_WORKING:
             $view->addItem(
               id(new PHUIStatusItemView())
                 ->setIcon(PHUIStatusItemView::ICON_CLOCK, 'green')
                 ->setTarget(pht('Initializing Working Copy'))
                 ->setNote(pht('Daemons are initializing the working copy.')));
             return $view;
           default:
             $view->addItem(
               id(new PHUIStatusItemView())
                 ->setIcon(PHUIStatusItemView::ICON_WARNING, 'red')
                 ->setTarget(pht('Unknown Init Status'))
                 ->setNote($message->getStatusCode()));
             return $view;
         }
       } else {
         $view->addItem(
           id(new PHUIStatusItemView())
             ->setIcon(PHUIStatusItemView::ICON_CLOCK, 'orange')
             ->setTarget(pht('No Working Copy Yet'))
             ->setNote(
               pht('Waiting for daemons to build a working copy.')));
         return $view;
       }
     }
 
     $message = idx($messages, PhabricatorRepositoryStatusMessage::TYPE_FETCH);
     if ($message) {
       switch ($message->getStatusCode()) {
         case PhabricatorRepositoryStatusMessage::CODE_ERROR:
           $message = $message->getParameter('message');
 
           $suggestion = null;
           if (preg_match('/Permission denied \(publickey\)./', $message)) {
             $suggestion = pht(
               'Public Key Error: This error usually indicates that the '.
               'keypair you have configured does not have permission to '.
               'access the repository.');
           }
 
           $view->addItem(
             id(new PHUIStatusItemView())
               ->setIcon(PHUIStatusItemView::ICON_WARNING, 'red')
               ->setTarget(pht('Update Error'))
               ->setNote($suggestion));
           return $view;
         case PhabricatorRepositoryStatusMessage::CODE_OKAY:
           $ago = (PhabricatorTime::getNow() - $message->getEpoch());
           $view->addItem(
             id(new PHUIStatusItemView())
               ->setIcon(PHUIStatusItemView::ICON_ACCEPT, 'green')
               ->setTarget(pht('Updates OK'))
               ->setNote(
                 pht(
                   'Last updated %s (%s ago).',
                   phabricator_datetime($message->getEpoch(), $viewer),
                   phutil_format_relative_time_detailed($ago))));
           break;
       }
     } else {
       $view->addItem(
         id(new PHUIStatusItemView())
           ->setIcon(PHUIStatusItemView::ICON_CLOCK, 'orange')
           ->setTarget(pht('Waiting For Update'))
           ->setNote(
             pht('Waiting for daemons to read updates.')));
     }
 
     if ($repository->isImporting()) {
       $ratio = $repository->loadImportProgress();
       $percentage = sprintf('%.2f%%', 100 * $ratio);
 
       $view->addItem(
         id(new PHUIStatusItemView())
           ->setIcon(PHUIStatusItemView::ICON_CLOCK, 'green')
           ->setTarget(pht('Importing'))
           ->setNote(
             pht('%s Complete', $percentage)));
     } else {
       $view->addItem(
         id(new PHUIStatusItemView())
           ->setIcon(PHUIStatusItemView::ICON_ACCEPT, 'green')
           ->setTarget(pht('Fully Imported')));
     }
 
     if (idx($messages, PhabricatorRepositoryStatusMessage::TYPE_NEEDS_UPDATE)) {
       $view->addItem(
         id(new PHUIStatusItemView())
           ->setIcon(PHUIStatusItemView::ICON_UP, 'indigo')
           ->setTarget(pht('Prioritized'))
           ->setNote(pht('This repository will be updated soon!')));
     }
 
     return $view;
   }
 
   private function buildRepositoryRawError(
     PhabricatorRepository $repository,
     array $messages) {
     $viewer = $this->getViewer();
 
     $can_edit = PhabricatorPolicyFilter::hasCapability(
       $viewer,
       $repository,
       PhabricatorPolicyCapability::CAN_EDIT);
 
     $raw_error = null;
 
     $message = idx($messages, PhabricatorRepositoryStatusMessage::TYPE_FETCH);
     if ($message) {
       switch ($message->getStatusCode()) {
         case PhabricatorRepositoryStatusMessage::CODE_ERROR:
           $raw_error = $message->getParameter('message');
           break;
       }
     }
 
     if ($raw_error !== null) {
       if (!$can_edit) {
         $raw_message = pht(
           'You must be able to edit a repository to see raw error messages '.
           'because they sometimes disclose sensitive information.');
         $raw_message = phutil_tag('em', array(), $raw_message);
       } else {
         $raw_message = phutil_escape_html_newlines($raw_error);
       }
     } else {
       $raw_message = null;
     }
 
     return $raw_message;
   }
 
+  private function loadStatusMessages(PhabricatorRepository $repository) {
+    $messages = id(new PhabricatorRepositoryStatusMessage())
+      ->loadAllWhere('repositoryID = %d', $repository->getID());
+    $messages = mpull($messages, null, 'getStatusType');
+
+    return $messages;
+  }
+
+  private function getEnvConfigLink() {
+    $config_href = '/config/edit/environment.append-paths/';
+    return phutil_tag(
+      'a',
+      array(
+        'href' => $config_href,
+      ),
+      'environment.append-paths');
+  }
 
 }
diff --git a/src/applications/diffusion/management/DiffusionRepositoryStorageManagementPanel.php b/src/applications/diffusion/management/DiffusionRepositoryStorageManagementPanel.php
index 54c446b9f..0c723bd9e 100644
--- a/src/applications/diffusion/management/DiffusionRepositoryStorageManagementPanel.php
+++ b/src/applications/diffusion/management/DiffusionRepositoryStorageManagementPanel.php
@@ -1,223 +1,235 @@
 <?php
 
 final class DiffusionRepositoryStorageManagementPanel
   extends DiffusionRepositoryManagementPanel {
 
   const PANELKEY = 'storage';
 
   public function getManagementPanelLabel() {
     return pht('Storage');
   }
 
   public function getManagementPanelOrder() {
     return 600;
   }
 
+  public function getManagementPanelIcon() {
+    $repository = $this->getRepository();
+
+    if ($repository->getAlmanacServicePHID()) {
+      return 'fa-sitemap';
+    } else if ($repository->isHosted()) {
+      return 'fa-folder';
+    } else {
+      return 'fa-download';
+    }
+  }
+
   public function buildManagementPanelContent() {
     return array(
       $this->buildStorageStatusPanel(),
       $this->buildClusterStatusPanel(),
     );
   }
 
   private function buildStorageStatusPanel() {
     $repository = $this->getRepository();
     $viewer = $this->getViewer();
 
     $view = id(new PHUIPropertyListView())
       ->setViewer($viewer);
 
     if ($repository->usesLocalWorkingCopy()) {
-      $storage_path = $repository->getHumanReadableDetail('local-path');
+      $storage_path = $repository->getLocalPath();
     } else {
       $storage_path = phutil_tag('em', array(), pht('No Local Working Copy'));
     }
 
     $service_phid = $repository->getAlmanacServicePHID();
     if ($service_phid) {
       $storage_service = $viewer->renderHandle($service_phid);
     } else {
       $storage_service = phutil_tag('em', array(), pht('Local'));
     }
 
     $view->addProperty(pht('Storage Path'), $storage_path);
     $view->addProperty(pht('Storage Cluster'), $storage_service);
 
     $header = id(new PHUIHeaderView())
       ->setHeader(pht('Storage'));
 
     return id(new PHUIObjectBoxView())
       ->setHeader($header)
       ->setBackground(PHUIObjectBoxView::BLUE_PROPERTY)
       ->addPropertyList($view);
   }
 
   private function buildClusterStatusPanel() {
     $repository = $this->getRepository();
     $viewer = $this->getViewer();
 
     $service_phid = $repository->getAlmanacServicePHID();
     if ($service_phid) {
       $service = id(new AlmanacServiceQuery())
         ->setViewer($viewer)
         ->withServiceTypes(
           array(
             AlmanacClusterRepositoryServiceType::SERVICETYPE,
           ))
         ->withPHIDs(array($service_phid))
         ->needBindings(true)
         ->executeOne();
       if (!$service) {
         // TODO: Viewer may not have permission to see the service, or it may
         // be invalid? Raise some more useful error here?
         throw new Exception(pht('Unable to load cluster service.'));
       }
     } else {
       $service = null;
     }
 
     Javelin::initBehavior('phabricator-tooltips');
 
     $rows = array();
     if ($service) {
       $bindings = $service->getBindings();
       $bindings = mgroup($bindings, 'getDevicePHID');
 
       // This is an unusual read which always comes from the master.
       if (PhabricatorEnv::isReadOnly()) {
         $versions = array();
       } else {
         $versions = PhabricatorRepositoryWorkingCopyVersion::loadVersions(
           $repository->getPHID());
       }
 
       $versions = mpull($versions, null, 'getDevicePHID');
 
       foreach ($bindings as $binding_group) {
         $all_disabled = true;
         foreach ($binding_group as $binding) {
           if (!$binding->getIsDisabled()) {
             $all_disabled = false;
             break;
           }
         }
 
         $any_binding = head($binding_group);
 
         if ($all_disabled) {
           $binding_icon = 'fa-times grey';
           $binding_tip = pht('Disabled');
         } else {
           $binding_icon = 'fa-folder-open green';
           $binding_tip = pht('Active');
         }
 
         $binding_icon = id(new PHUIIconView())
           ->setIcon($binding_icon)
           ->addSigil('has-tooltip')
           ->setMetadata(
             array(
               'tip' => $binding_tip,
             ));
 
         $device = $any_binding->getDevice();
 
         $version = idx($versions, $device->getPHID());
         if ($version) {
           $version_number = $version->getRepositoryVersion();
           $version_number = phutil_tag(
             'a',
             array(
               'href' => "/diffusion/pushlog/view/{$version_number}/",
             ),
             $version_number);
         } else {
           $version_number = '-';
         }
 
         if ($version && $version->getIsWriting()) {
           $is_writing = id(new PHUIIconView())
             ->setIcon('fa-pencil green');
         } else {
           $is_writing = id(new PHUIIconView())
             ->setIcon('fa-pencil grey');
         }
 
         $write_properties = null;
         if ($version) {
           $write_properties = $version->getWriteProperties();
           if ($write_properties) {
             try {
               $write_properties = phutil_json_decode($write_properties);
             } catch (Exception $ex) {
               $write_properties = null;
             }
           }
         }
 
         if ($write_properties) {
           $writer_phid = idx($write_properties, 'userPHID');
           $last_writer = $viewer->renderHandle($writer_phid);
 
           $writer_epoch = idx($write_properties, 'epoch');
           $writer_epoch = phabricator_datetime($writer_epoch, $viewer);
         } else {
           $last_writer = null;
           $writer_epoch = null;
         }
 
         $rows[] = array(
           $binding_icon,
           phutil_tag(
             'a',
             array(
               'href' => $device->getURI(),
             ),
             $device->getName()),
           $version_number,
           $is_writing,
           $last_writer,
           $writer_epoch,
         );
       }
     }
 
     $table = id(new AphrontTableView($rows))
       ->setNoDataString(pht('This is not a cluster repository.'))
       ->setHeaders(
         array(
           null,
           pht('Device'),
           pht('Version'),
           pht('Writing'),
           pht('Last Writer'),
           pht('Last Write At'),
         ))
       ->setColumnClasses(
         array(
           null,
           null,
           null,
           'right wide',
           null,
           'date',
         ));
 
     $doc_href = PhabricatorEnv::getDoclink('Cluster: Repositories');
 
     $header = id(new PHUIHeaderView())
       ->setHeader(pht('Cluster Status'))
       ->addActionLink(
         id(new PHUIButtonView())
           ->setIcon('fa-book')
           ->setHref($doc_href)
           ->setTag('a')
           ->setText(pht('Documentation')));
 
     return id(new PHUIObjectBoxView())
       ->setHeader($header)
       ->setBackground(PHUIObjectBoxView::BLUE_PROPERTY)
       ->setTable($table);
   }
 
 }
diff --git a/src/applications/diffusion/management/DiffusionRepositorySubversionManagementPanel.php b/src/applications/diffusion/management/DiffusionRepositorySubversionManagementPanel.php
new file mode 100644
index 000000000..c6b236d5b
--- /dev/null
+++ b/src/applications/diffusion/management/DiffusionRepositorySubversionManagementPanel.php
@@ -0,0 +1,77 @@
+<?php
+
+final class DiffusionRepositorySubversionManagementPanel
+  extends DiffusionRepositoryManagementPanel {
+
+  const PANELKEY = 'subversion';
+
+  public function getManagementPanelLabel() {
+    return pht('Subversion');
+  }
+
+  public function getManagementPanelOrder() {
+    return 1000;
+  }
+
+  public function shouldEnableForRepository(
+    PhabricatorRepository $repository) {
+    return $repository->isSVN();
+  }
+
+  public function getManagementPanelIcon() {
+    $repository = $this->getRepository();
+
+    $has_any = (bool)$repository->getDetail('svn-subpath');
+
+    if ($has_any) {
+      return 'fa-database';
+    } else {
+      return 'fa-database grey';
+    }
+  }
+
+  protected function getEditEngineFieldKeys() {
+    return array(
+      'importOnly',
+    );
+  }
+
+  protected function buildManagementPanelActions() {
+    $repository = $this->getRepository();
+    $viewer = $this->getViewer();
+
+    $can_edit = PhabricatorPolicyFilter::hasCapability(
+      $viewer,
+      $repository,
+      PhabricatorPolicyCapability::CAN_EDIT);
+
+    $subversion_uri = $this->getEditPageURI();
+
+    return array(
+      id(new PhabricatorActionView())
+        ->setIcon('fa-pencil')
+        ->setName(pht('Edit Properties'))
+        ->setHref($subversion_uri)
+        ->setDisabled(!$can_edit)
+        ->setWorkflow(!$can_edit),
+    );
+  }
+
+  public function buildManagementPanelContent() {
+    $repository = $this->getRepository();
+    $viewer = $this->getViewer();
+
+    $view = id(new PHUIPropertyListView())
+      ->setViewer($viewer)
+      ->setActionList($this->newActions());
+
+    $default_branch = nonempty(
+      $repository->getHumanReadableDetail('svn-subpath'),
+      phutil_tag('em', array(), pht('Import Entire Repository')));
+    $view->addProperty(pht('Import Only'), $default_branch);
+
+
+    return $this->newBox(pht('Subversion'), $view);
+  }
+
+}
diff --git a/src/applications/diffusion/management/DiffusionRepositorySymbolsManagementPanel.php b/src/applications/diffusion/management/DiffusionRepositorySymbolsManagementPanel.php
index 4e934c67a..996473f4f 100644
--- a/src/applications/diffusion/management/DiffusionRepositorySymbolsManagementPanel.php
+++ b/src/applications/diffusion/management/DiffusionRepositorySymbolsManagementPanel.php
@@ -1,64 +1,85 @@
 <?php
 
 final class DiffusionRepositorySymbolsManagementPanel
   extends DiffusionRepositoryManagementPanel {
 
   const PANELKEY = 'symbols';
 
   public function getManagementPanelLabel() {
     return pht('Symbols');
   }
 
   public function getManagementPanelOrder() {
     return 900;
   }
 
+  public function getManagementPanelIcon() {
+    $repository = $this->getRepository();
+
+    $has_any =
+      $repository->getSymbolLanguages() ||
+      $repository->getSymbolSources();
+
+    if ($has_any) {
+      return 'fa-link';
+    } else {
+      return 'fa-link grey';
+    }
+  }
+
+  protected function getEditEngineFieldKeys() {
+    return array(
+      'symbolLanguages',
+      'symbolRepositoryPHIDs',
+    );
+  }
+
   protected function buildManagementPanelActions() {
     $repository = $this->getRepository();
     $viewer = $this->getViewer();
 
     $can_edit = PhabricatorPolicyFilter::hasCapability(
       $viewer,
       $repository,
       PhabricatorPolicyCapability::CAN_EDIT);
 
-    $symbols_uri = $repository->getPathURI('edit/symbols/');
+    $symbols_uri = $this->getEditPageURI();
 
     return array(
       id(new PhabricatorActionView())
         ->setIcon('fa-pencil')
         ->setName(pht('Edit Symbols'))
         ->setHref($symbols_uri)
         ->setDisabled(!$can_edit)
         ->setWorkflow(!$can_edit),
     );
   }
 
   public function buildManagementPanelContent() {
     $repository = $this->getRepository();
     $viewer = $this->getViewer();
 
     $view = id(new PHUIPropertyListView())
       ->setViewer($viewer)
       ->setActionList($this->newActions());
 
     $languages = $repository->getSymbolLanguages();
     if ($languages) {
       $languages = implode(', ', $languages);
     } else {
       $languages = phutil_tag('em', array(), pht('Any'));
     }
     $view->addProperty(pht('Languages'), $languages);
 
     $sources = $repository->getSymbolSources();
     if ($sources) {
       $sources = $viewer->renderHandleList($sources);
     } else {
       $sources = phutil_tag('em', array(), pht('This Repository Only'));
     }
     $view->addProperty(pht('Uses Symbols From'), $sources);
 
     return $this->newBox(pht('Symbols'), $view);
   }
 
 }
diff --git a/src/applications/diffusion/management/DiffusionRepositoryURIsManagementPanel.php b/src/applications/diffusion/management/DiffusionRepositoryURIsManagementPanel.php
index 634535fef..333c2f03c 100644
--- a/src/applications/diffusion/management/DiffusionRepositoryURIsManagementPanel.php
+++ b/src/applications/diffusion/management/DiffusionRepositoryURIsManagementPanel.php
@@ -1,118 +1,158 @@
 <?php
 
 final class DiffusionRepositoryURIsManagementPanel
   extends DiffusionRepositoryManagementPanel {
 
   const PANELKEY = 'uris';
 
   public function getManagementPanelLabel() {
-    return pht('Clone / Fetch / Mirror');
+    return pht('URIs');
+  }
+
+  public function getManagementPanelIcon() {
+    return 'fa-cogs';
   }
 
   public function getManagementPanelOrder() {
-    return 300;
+    return 400;
   }
 
   public function buildManagementPanelContent() {
     $repository = $this->getRepository();
     $viewer = $this->getViewer();
     $uris = $repository->getURIs();
 
     Javelin::initBehavior('phabricator-tooltips');
     $rows = array();
     foreach ($uris as $uri) {
 
       $uri_name = $uri->getDisplayURI();
       $uri_name = phutil_tag(
         'a',
         array(
           'href' => $uri->getViewURI(),
         ),
         $uri_name);
 
       if ($uri->getIsDisabled()) {
         $status_icon = 'fa-times grey';
       } else {
         $status_icon = 'fa-check green';
       }
 
       $uri_status = id(new PHUIIconView())->setIcon($status_icon);
 
       $io_type = $uri->getEffectiveIOType();
       $io_map = PhabricatorRepositoryURI::getIOTypeMap();
       $io_spec = idx($io_map, $io_type, array());
 
       $io_icon = idx($io_spec, 'icon');
       $io_color = idx($io_spec, 'color');
       $io_label = idx($io_spec, 'label', $io_type);
 
       $uri_io = array(
         id(new PHUIIconView())->setIcon("{$io_icon} {$io_color}"),
         ' ',
         $io_label,
       );
 
       $display_type = $uri->getEffectiveDisplayType();
       $display_map = PhabricatorRepositoryURI::getDisplayTypeMap();
       $display_spec = idx($display_map, $display_type, array());
 
       $display_icon = idx($display_spec, 'icon');
       $display_color = idx($display_spec, 'color');
       $display_label = idx($display_spec, 'label', $display_type);
 
       $uri_display = array(
         id(new PHUIIconView())->setIcon("{$display_icon} {$display_color}"),
         ' ',
         $display_label,
       );
 
       $rows[] = array(
         $uri_status,
         $uri_name,
         $uri_io,
         $uri_display,
       );
     }
 
     $table = id(new AphrontTableView($rows))
       ->setNoDataString(pht('This repository has no URIs.'))
       ->setHeaders(
         array(
           null,
           pht('URI'),
           pht('I/O'),
           pht('Display'),
         ))
       ->setColumnClasses(
         array(
           null,
           'pri wide',
           null,
           null,
         ));
 
     $doc_href = PhabricatorEnv::getDoclink('Diffusion User Guide: URIs');
     $add_href = $repository->getPathURI('uri/edit/');
 
     $header = id(new PHUIHeaderView())
       ->setHeader(pht('Repository URIs'))
       ->addActionLink(
         id(new PHUIButtonView())
           ->setIcon('fa-plus')
           ->setHref($add_href)
           ->setTag('a')
           ->setText(pht('Add New URI')))
       ->addActionLink(
         id(new PHUIButtonView())
           ->setIcon('fa-book')
           ->setHref($doc_href)
           ->setTag('a')
           ->setText(pht('Documentation')));
 
+    $is_new = $repository->isNewlyInitialized();
+
+    $messages = array();
+    if ($repository->isHosted()) {
+      if ($is_new) {
+        $host_message = pht('Phabricator will host this repository.');
+      } else {
+        $host_message = pht('Phabricator is hosting this repository.');
+      }
+
+      $messages[] = array(
+        id(new PHUIIconView())->setIcon('fa-folder'),
+        ' ',
+        $host_message,
+      );
+    } else {
+      if ($is_new) {
+        $observe_message = pht(
+          'Phabricator will observe a remote repository.');
+      } else {
+        $observe_message = pht(
+          'This repository is hosted remotely. Phabricator is observing it.');
+      }
+
+      $messages[] = array(
+        id(new PHUIIconView())->setIcon('fa-download'),
+        ' ',
+        $observe_message,
+      );
+    }
+
+    $info_view = id(new PHUIInfoView())
+      ->setSeverity(PHUIInfoView::SEVERITY_NOTICE)
+      ->setErrors($messages);
+
     return id(new PHUIObjectBoxView())
       ->setHeader($header)
+      ->setInfoView($info_view)
       ->setBackground(PHUIObjectBoxView::BLUE_PROPERTY)
       ->setTable($table);
   }
 
 }
diff --git a/src/applications/diffusion/protocol/DiffusionCommandEngine.php b/src/applications/diffusion/protocol/DiffusionCommandEngine.php
index 552ca813a..5a1859504 100644
--- a/src/applications/diffusion/protocol/DiffusionCommandEngine.php
+++ b/src/applications/diffusion/protocol/DiffusionCommandEngine.php
@@ -1,233 +1,285 @@
 <?php
 
 abstract class DiffusionCommandEngine extends Phobject {
 
   private $repository;
   private $protocol;
   private $credentialPHID;
   private $argv;
   private $passthru;
   private $connectAsDevice;
   private $sudoAsDaemon;
 
   public static function newCommandEngine(PhabricatorRepository $repository) {
     $engines = self::newCommandEngines();
 
     foreach ($engines as $engine) {
       if ($engine->canBuildForRepository($repository)) {
         return id(clone $engine)
           ->setRepository($repository);
       }
     }
 
     throw new Exception(
       pht(
         'No registered command engine can build commands for this '.
         'repository ("%s").',
         $repository->getDisplayName()));
   }
 
   private static function newCommandEngines() {
     return id(new PhutilClassMapQuery())
       ->setAncestorClass(__CLASS__)
       ->execute();
   }
 
   abstract protected function canBuildForRepository(
     PhabricatorRepository $repository);
 
   abstract protected function newFormattedCommand($pattern, array $argv);
   abstract protected function newCustomEnvironment();
 
   public function setRepository(PhabricatorRepository $repository) {
     $this->repository = $repository;
     return $this;
   }
 
   public function getRepository() {
     return $this->repository;
   }
 
   public function setProtocol($protocol) {
     $this->protocol = $protocol;
     return $this;
   }
 
   public function getProtocol() {
     return $this->protocol;
   }
 
+  public function getDisplayProtocol() {
+    return $this->getProtocol().'://';
+  }
+
   public function setCredentialPHID($credential_phid) {
     $this->credentialPHID = $credential_phid;
     return $this;
   }
 
   public function getCredentialPHID() {
     return $this->credentialPHID;
   }
 
   public function setArgv(array $argv) {
     $this->argv = $argv;
     return $this;
   }
 
   public function getArgv() {
     return $this->argv;
   }
 
   public function setPassthru($passthru) {
     $this->passthru = $passthru;
     return $this;
   }
 
   public function getPassthru() {
     return $this->passthru;
   }
 
   public function setConnectAsDevice($connect_as_device) {
     $this->connectAsDevice = $connect_as_device;
     return $this;
   }
 
   public function getConnectAsDevice() {
     return $this->connectAsDevice;
   }
 
   public function setSudoAsDaemon($sudo_as_daemon) {
     $this->sudoAsDaemon = $sudo_as_daemon;
     return $this;
   }
 
   public function getSudoAsDaemon() {
     return $this->sudoAsDaemon;
   }
 
   public function newFuture() {
     $argv = $this->newCommandArgv();
     $env = $this->newCommandEnvironment();
 
     if ($this->getSudoAsDaemon()) {
       $command = call_user_func_array('csprintf', $argv);
       $command = PhabricatorDaemon::sudoCommandAsDaemonUser($command);
       $argv = array('%C', $command);
     }
 
     if ($this->getPassthru()) {
       $future = newv('PhutilExecPassthru', $argv);
     } else {
       $future = newv('ExecFuture', $argv);
     }
 
     $future->setEnv($env);
 
     return $future;
   }
 
   private function newCommandArgv() {
     $argv = $this->argv;
     $pattern = $argv[0];
     $argv = array_slice($argv, 1);
 
     list($pattern, $argv) = $this->newFormattedCommand($pattern, $argv);
 
     return array_merge(array($pattern), $argv);
   }
 
   private function newCommandEnvironment() {
     $env = $this->newCommonEnvironment() + $this->newCustomEnvironment();
     foreach ($env as $key => $value) {
       if ($value === null) {
         unset($env[$key]);
       }
     }
     return $env;
   }
 
   private function newCommonEnvironment() {
     $repository = $this->getRepository();
 
     $env = array();
       // NOTE: Force the language to "en_US.UTF-8", which overrides locale
       // settings. This makes stuff print in English instead of, e.g., French,
       // so we can parse the output of some commands, error messages, etc.
     $env['LANG'] = 'en_US.UTF-8';
 
       // Propagate PHABRICATOR_ENV explicitly. For discussion, see T4155.
     $env['PHABRICATOR_ENV'] = PhabricatorEnv::getSelectedEnvironmentName();
 
     $as_device = $this->getConnectAsDevice();
     $credential_phid = $this->getCredentialPHID();
 
     if ($as_device) {
       $device = AlmanacKeys::getLiveDevice();
       if (!$device) {
         throw new Exception(
           pht(
             'Attempting to build a reposiory command (for repository "%s") '.
             'as device, but this host ("%s") is not configured as a cluster '.
             'device.',
             $repository->getDisplayName(),
             php_uname('n')));
       }
 
       if ($credential_phid) {
         throw new Exception(
           pht(
             'Attempting to build a repository command (for repository "%s"), '.
             'but the CommandEngine is configured to connect as both the '.
             'current cluster device ("%s") and with a specific credential '.
             '("%s"). These options are mutually exclusive. Connections must '.
             'authenticate as one or the other, not both.',
             $repository->getDisplayName(),
             $device->getName(),
             $credential_phid));
       }
     }
 
 
     if ($this->isAnySSHProtocol()) {
       if ($credential_phid) {
         $env['PHABRICATOR_CREDENTIAL'] = $credential_phid;
       }
       if ($as_device) {
         $env['PHABRICATOR_AS_DEVICE'] = 1;
       }
     }
 
     return $env;
   }
 
-  protected function isSSHProtocol() {
+  public function isSSHProtocol() {
     return ($this->getProtocol() == 'ssh');
   }
 
-  protected function isSVNProtocol() {
+  public function isSVNProtocol() {
     return ($this->getProtocol() == 'svn');
   }
 
-  protected function isSVNSSHProtocol() {
+  public function isSVNSSHProtocol() {
     return ($this->getProtocol() == 'svn+ssh');
   }
 
-  protected function isHTTPProtocol() {
+  public function isHTTPProtocol() {
     return ($this->getProtocol() == 'http');
   }
 
-  protected function isHTTPSProtocol() {
+  public function isHTTPSProtocol() {
     return ($this->getProtocol() == 'https');
   }
 
-  protected function isAnyHTTPProtocol() {
+  public function isAnyHTTPProtocol() {
     return ($this->isHTTPProtocol() || $this->isHTTPSProtocol());
   }
 
-  protected function isAnySSHProtocol() {
+  public function isAnySSHProtocol() {
     return ($this->isSSHProtocol() || $this->isSVNSSHProtocol());
   }
 
+  public function isCredentialSupported() {
+    return ($this->getPassphraseProvidesCredentialType() !== null);
+  }
+
+  public function isCredentialOptional() {
+    if ($this->isAnySSHProtocol()) {
+      return false;
+    }
+
+    return true;
+  }
+
+  public function getPassphraseCredentialLabel() {
+    if ($this->isAnySSHProtocol()) {
+      return pht('SSH Key');
+    }
+
+    if ($this->isAnyHTTPProtocol() || $this->isSVNProtocol()) {
+      return pht('Password');
+    }
+
+    return null;
+  }
+
+  public function getPassphraseDefaultCredentialType() {
+    if ($this->isAnySSHProtocol()) {
+      return PassphraseSSHPrivateKeyTextCredentialType::CREDENTIAL_TYPE;
+    }
+
+    if ($this->isAnyHTTPProtocol() || $this->isSVNProtocol()) {
+      return PassphrasePasswordCredentialType::CREDENTIAL_TYPE;
+    }
+
+    return null;
+  }
+
+  public function getPassphraseProvidesCredentialType() {
+    if ($this->isAnySSHProtocol()) {
+      return PassphraseSSHPrivateKeyCredentialType::PROVIDES_TYPE;
+    }
+
+    if ($this->isAnyHTTPProtocol() || $this->isSVNProtocol()) {
+      return PassphrasePasswordCredentialType::PROVIDES_TYPE;
+    }
+
+    return null;
+  }
+
   protected function getSSHWrapper() {
     $root = dirname(phutil_get_library_root('phabricator'));
     return $root.'/bin/ssh-connect';
   }
 
 }
diff --git a/src/applications/diffusion/protocol/DiffusionGitCommandEngine.php b/src/applications/diffusion/protocol/DiffusionGitCommandEngine.php
index 3458d27ed..5eae19e03 100644
--- a/src/applications/diffusion/protocol/DiffusionGitCommandEngine.php
+++ b/src/applications/diffusion/protocol/DiffusionGitCommandEngine.php
@@ -1,37 +1,36 @@
 <?php
 
 final class DiffusionGitCommandEngine
   extends DiffusionCommandEngine {
 
   protected function canBuildForRepository(
     PhabricatorRepository $repository) {
     return $repository->isGit();
   }
 
   protected function newFormattedCommand($pattern, array $argv) {
     $pattern = "git {$pattern}";
     return array($pattern, $argv);
   }
 
   protected function newCustomEnvironment() {
     $env = array();
 
     // NOTE: See T2965. Some time after Git 1.7.5.4, Git started fataling if
     // it can not read $HOME. For many users, $HOME points at /root (this
     // seems to be a default result of Apache setup). Instead, explicitly
     // point $HOME at a readable, empty directory so that Git looks for the
     // config file it's after, fails to locate it, and moves on. This is
     // really silly, but seems like the least damaging approach to
     // mitigating the issue.
 
-    $root = dirname(phutil_get_library_root('phabricator'));
-    $env['HOME'] = $root.'/support/empty/';
+    $env['HOME'] = PhabricatorEnv::getEmptyCWD();
 
     if ($this->isAnySSHProtocol()) {
       $env['GIT_SSH'] = $this->getSSHWrapper();
     }
 
     return $env;
   }
 
 }
diff --git a/src/applications/diffusion/query/lowlevel/DiffusionLowLevelResolveRefsQuery.php b/src/applications/diffusion/query/lowlevel/DiffusionLowLevelResolveRefsQuery.php
index 53dc6b30d..4e9ed246d 100644
--- a/src/applications/diffusion/query/lowlevel/DiffusionLowLevelResolveRefsQuery.php
+++ b/src/applications/diffusion/query/lowlevel/DiffusionLowLevelResolveRefsQuery.php
@@ -1,298 +1,303 @@
 <?php
 
 /**
  * Resolves references (like short commit names, branch names, tag names, etc.)
  * into canonical, stable commit identifiers. This query works for all
  * repository types.
  *
  * This query will always resolve refs which can be resolved, but may need to
  * perform VCS operations. A faster (but less complete) counterpart query is
  * available in @{class:DiffusionCachedResolveRefsQuery}; that query can
  * resolve most refs without VCS operations.
  */
 final class DiffusionLowLevelResolveRefsQuery
   extends DiffusionLowLevelQuery {
 
   private $refs;
   private $types;
 
   public function withRefs(array $refs) {
     $this->refs = $refs;
     return $this;
   }
 
   public function withTypes(array $types) {
     $this->types = $types;
     return $this;
   }
 
   protected function executeQuery() {
     if (!$this->refs) {
       return array();
     }
 
+    $repository = $this->getRepository();
+    if (!$repository->hasLocalWorkingCopy()) {
+      return array();
+    }
+
     switch ($this->getRepository()->getVersionControlSystem()) {
       case PhabricatorRepositoryType::REPOSITORY_TYPE_GIT:
         $result = $this->resolveGitRefs();
         break;
       case PhabricatorRepositoryType::REPOSITORY_TYPE_MERCURIAL:
         $result = $this->resolveMercurialRefs();
         break;
       case PhabricatorRepositoryType::REPOSITORY_TYPE_SVN:
         $result = $this->resolveSubversionRefs();
         break;
       default:
         throw new Exception(pht('Unsupported repository type!'));
     }
 
     if ($this->types !== null) {
       $result = $this->filterRefsByType($result, $this->types);
     }
 
     return $result;
   }
 
   private function resolveGitRefs() {
     $repository = $this->getRepository();
 
     $unresolved = array_fuse($this->refs);
     $results = array();
 
     // First, resolve branches and tags.
     $ref_map = id(new DiffusionLowLevelGitRefQuery())
       ->setRepository($repository)
       ->withIsTag(true)
       ->withIsOriginBranch(true)
       ->execute();
     $ref_map = mgroup($ref_map, 'getShortName');
 
     $tag_prefix = 'refs/tags/';
     foreach ($unresolved as $ref) {
       if (empty($ref_map[$ref])) {
         continue;
       }
 
       foreach ($ref_map[$ref] as $result) {
         $fields = $result->getRawFields();
         $objectname = idx($fields, 'refname');
         if (!strncmp($objectname, $tag_prefix, strlen($tag_prefix))) {
           $type = 'tag';
         } else {
           $type = 'branch';
         }
 
         $info = array(
           'type' => $type,
           'identifier' => $result->getCommitIdentifier(),
         );
 
         if ($type == 'tag') {
           $alternate = idx($fields, 'objectname');
           if ($alternate) {
             $info['alternate'] = $alternate;
           }
         }
 
         $results[$ref][] = $info;
       }
 
       unset($unresolved[$ref]);
     }
 
     // If we resolved everything, we're done.
     if (!$unresolved) {
       return $results;
     }
 
     // Try to resolve anything else. This stuff either doesn't exist or is
     // some ref like "HEAD^^^".
     $future = $repository->getLocalCommandFuture('cat-file --batch-check');
     $future->write(implode("\n", $unresolved));
     list($stdout) = $future->resolvex();
 
     $lines = explode("\n", rtrim($stdout, "\n"));
     if (count($lines) !== count($unresolved)) {
       throw new Exception(
         pht(
           'Unexpected line count from `%s`!',
           'git cat-file'));
     }
 
     $hits = array();
     $tags = array();
 
     $lines = array_combine($unresolved, $lines);
     foreach ($lines as $ref => $line) {
       $parts = explode(' ', $line);
       if (count($parts) < 2) {
         throw new Exception(
           pht(
             'Failed to parse `%s` output: %s',
             'git cat-file',
             $line));
       }
       list($identifier, $type) = $parts;
 
       if ($type == 'missing') {
         // This is either an ambiguous reference which resolves to several
         // objects, or an invalid reference. For now, always treat it as
         // invalid. It would be nice to resolve all possibilities for
         // ambiguous references at some point, although the strategy for doing
         // so isn't clear to me.
         continue;
       }
 
       switch ($type) {
         case 'commit':
           break;
         case 'tag':
           $tags[] = $identifier;
           break;
         default:
           throw new Exception(
             pht(
               'Unexpected object type from `%s`: %s',
               'git cat-file',
               $line));
       }
 
       $hits[] = array(
         'ref' => $ref,
         'type' => $type,
         'identifier' => $identifier,
       );
     }
 
     $tag_map = array();
     if ($tags) {
       // If some of the refs were tags, just load every tag in order to figure
       // out which commits they map to. This might be somewhat inefficient in
       // repositories with a huge number of tags.
       $tag_refs = id(new DiffusionLowLevelGitRefQuery())
         ->setRepository($repository)
         ->withIsTag(true)
         ->executeQuery();
       foreach ($tag_refs as $tag_ref) {
         $tag_map[$tag_ref->getShortName()] = $tag_ref->getCommitIdentifier();
       }
     }
 
     $results = array();
     foreach ($hits as $hit) {
       $type = $hit['type'];
       $ref = $hit['ref'];
 
       $alternate = null;
       if ($type == 'tag') {
         $alternate = $identifier;
         $identifier = idx($tag_map, $ref);
         if (!$identifier) {
           throw new Exception(
             pht(
               "Failed to look up tag '%s'!",
               $ref));
         }
       }
 
       $result = array(
         'type' => $type,
         'identifier' => $identifier,
       );
 
       if ($alternate !== null) {
         $result['alternate'] = $alternate;
       }
 
       $results[$ref][] = $result;
     }
 
     return $results;
   }
 
   private function resolveMercurialRefs() {
     $repository = $this->getRepository();
 
     // First, pull all of the branch heads in the repository. Doing this in
     // bulk is much faster than querying each individual head if we're
     // checking even a small number of refs.
     $branches = id(new DiffusionLowLevelMercurialBranchesQuery())
       ->setRepository($repository)
       ->executeQuery();
 
     $branches = mgroup($branches, 'getShortName');
 
     $results = array();
     $unresolved = $this->refs;
     foreach ($unresolved as $key => $ref) {
       if (empty($branches[$ref])) {
         continue;
       }
 
       foreach ($branches[$ref] as $branch) {
         $fields = $branch->getRawFields();
 
         $results[$ref][] = array(
           'type' => 'branch',
           'identifier' => $branch->getCommitIdentifier(),
           'closed' => idx($fields, 'closed', false),
         );
       }
 
       unset($unresolved[$key]);
     }
 
     if (!$unresolved) {
       return $results;
     }
 
     // If we still have unresolved refs (which might be things like "tip"),
     // try to resolve them individually.
 
     $futures = array();
     foreach ($unresolved as $ref) {
       $futures[$ref] = $repository->getLocalCommandFuture(
         'log --template=%s --rev %s',
         '{node}',
         hgsprintf('%s', $ref));
     }
 
     foreach (new FutureIterator($futures) as $ref => $future) {
       try {
         list($stdout) = $future->resolvex();
       } catch (CommandException $ex) {
         if (preg_match('/ambiguous identifier/', $ex->getStdErr())) {
           // This indicates that the ref ambiguously matched several things.
           // Eventually, it would be nice to return all of them, but it is
           // unclear how to best do that. For now, treat it as a miss instead.
           continue;
         }
         if (preg_match('/unknown revision/', $ex->getStdErr())) {
           // No matches for this ref.
           continue;
         }
         throw $ex;
       }
 
       // It doesn't look like we can figure out the type (commit/branch/rev)
       // from this output very easily. For now, just call everything a commit.
       $type = 'commit';
 
       $results[$ref][] = array(
         'type' => $type,
         'identifier' => trim($stdout),
       );
     }
 
     return $results;
   }
 
   private function resolveSubversionRefs() {
     // We don't have any VCS logic for Subversion, so just use the cached
     // query.
     return id(new DiffusionCachedResolveRefsQuery())
       ->setRepository($this->getRepository())
       ->withRefs($this->refs)
       ->execute();
   }
 
 }
diff --git a/src/applications/diffusion/ssh/DiffusionGitReceivePackSSHWorkflow.php b/src/applications/diffusion/ssh/DiffusionGitReceivePackSSHWorkflow.php
index 17d3badaa..76f8d3c83 100644
--- a/src/applications/diffusion/ssh/DiffusionGitReceivePackSSHWorkflow.php
+++ b/src/applications/diffusion/ssh/DiffusionGitReceivePackSSHWorkflow.php
@@ -1,92 +1,92 @@
 <?php
 
 final class DiffusionGitReceivePackSSHWorkflow extends DiffusionGitSSHWorkflow {
 
   protected function didConstruct() {
     $this->setName('git-receive-pack');
     $this->setArguments(
       array(
         array(
           'name'      => 'dir',
           'wildcard'  => true,
         ),
       ));
   }
 
   protected function executeRepositoryOperations() {
     $repository = $this->getRepository();
-    $viewer = $this->getViewer();
+    $viewer = $this->getUser();
     $device = AlmanacKeys::getLiveDevice();
 
     // This is a write, and must have write access.
     $this->requireWriteAccess();
 
     $cluster_engine = id(new DiffusionRepositoryClusterEngine())
       ->setViewer($viewer)
       ->setRepository($repository)
       ->setLog($this);
 
     if ($this->shouldProxy()) {
       $command = $this->getProxyCommand();
       $did_synchronize = false;
 
       if ($device) {
         $this->writeClusterEngineLogMessage(
           pht(
             "# Push received by \"%s\", forwarding to cluster host.\n",
             $device->getName()));
       }
     } else {
       $command = csprintf('git-receive-pack %s', $repository->getLocalPath());
       $did_synchronize = true;
       $cluster_engine->synchronizeWorkingCopyBeforeWrite();
 
       if ($device) {
         $this->writeClusterEngineLogMessage(
           pht(
             "# Ready to receive on cluster host \"%s\".\n",
             $device->getName()));
       }
     }
 
     $caught = null;
     try {
       $err = $this->executeRepositoryCommand($command);
     } catch (Exception $ex) {
       $caught = $ex;
     }
 
     // We've committed the write (or rejected it), so we can release the lock
     // without waiting for the client to receive the acknowledgement.
     if ($did_synchronize) {
       $cluster_engine->synchronizeWorkingCopyAfterWrite();
     }
 
     if ($caught) {
       throw $caught;
     }
 
     if (!$err) {
       $repository->writeStatusMessage(
         PhabricatorRepositoryStatusMessage::TYPE_NEEDS_UPDATE,
         PhabricatorRepositoryStatusMessage::CODE_OKAY);
       $this->waitForGitClient();
     }
 
     return $err;
   }
 
   private function executeRepositoryCommand($command) {
     $repository = $this->getRepository();
     $command = PhabricatorDaemon::sudoCommandAsDaemonUser($command);
 
     $future = id(new ExecFuture('%C', $command))
       ->setEnv($this->getEnvironment());
 
     return $this->newPassthruCommand()
       ->setIOChannel($this->getIOChannel())
       ->setCommandChannelFromExecFuture($future)
       ->execute();
   }
 
 }
diff --git a/src/applications/diffusion/ssh/DiffusionSSHWorkflow.php b/src/applications/diffusion/ssh/DiffusionSSHWorkflow.php
index 60b929f7c..78252fb8d 100644
--- a/src/applications/diffusion/ssh/DiffusionSSHWorkflow.php
+++ b/src/applications/diffusion/ssh/DiffusionSSHWorkflow.php
@@ -1,276 +1,263 @@
 <?php
 
 abstract class DiffusionSSHWorkflow extends PhabricatorSSHWorkflow {
 
   private $args;
   private $repository;
   private $hasWriteAccess;
   private $proxyURI;
   private $baseRequestPath;
 
   public function getRepository() {
     if (!$this->repository) {
       throw new Exception(pht('Repository is not available yet!'));
     }
     return $this->repository;
   }
 
   private function setRepository(PhabricatorRepository $repository) {
     $this->repository = $repository;
     return $this;
   }
 
   public function getArgs() {
     return $this->args;
   }
 
   public function getEnvironment() {
     $env = array(
       DiffusionCommitHookEngine::ENV_USER => $this->getUser()->getUsername(),
       DiffusionCommitHookEngine::ENV_REMOTE_PROTOCOL => 'ssh',
     );
 
     $ssh_client = getenv('SSH_CLIENT');
     if ($ssh_client) {
       // This has the format "<ip> <remote-port> <local-port>". Grab the IP.
       $remote_address = head(explode(' ', $ssh_client));
       $env[DiffusionCommitHookEngine::ENV_REMOTE_ADDRESS] = $remote_address;
     }
 
     return $env;
   }
 
   /**
    * Identify and load the affected repository.
    */
   abstract protected function identifyRepository();
   abstract protected function executeRepositoryOperations();
 
   protected function getBaseRequestPath() {
     return $this->baseRequestPath;
   }
 
   protected function writeError($message) {
     $this->getErrorChannel()->write($message);
     return $this;
   }
 
   protected function getCurrentDeviceName() {
     $device = AlmanacKeys::getLiveDevice();
     if ($device) {
       return $device->getName();
     }
 
     return php_uname('n');
   }
 
   protected function getTargetDeviceName() {
     // TODO: This should use the correct device identity.
     $uri = new PhutilURI($this->proxyURI);
     return $uri->getDomain();
   }
 
   protected function shouldProxy() {
     return (bool)$this->proxyURI;
   }
 
   protected function getProxyCommand() {
     $uri = new PhutilURI($this->proxyURI);
 
     $username = AlmanacKeys::getClusterSSHUser();
     if ($username === null) {
       throw new Exception(
         pht(
           'Unable to determine the username to connect with when trying '.
           'to proxy an SSH request within the Phabricator cluster.'));
     }
 
     $port = $uri->getPort();
     $host = $uri->getDomain();
     $key_path = AlmanacKeys::getKeyPath('device.key');
     if (!Filesystem::pathExists($key_path)) {
       throw new Exception(
         pht(
           'Unable to proxy this SSH request within the cluster: this device '.
           'is not registered and has a missing device key (expected to '.
           'find key at "%s").',
           $key_path));
     }
 
     $options = array();
     $options[] = '-o';
     $options[] = 'StrictHostKeyChecking=no';
     $options[] = '-o';
     $options[] = 'UserKnownHostsFile=/dev/null';
 
     // This is suppressing "added <address> to the list of known hosts"
     // messages, which are confusing and irrelevant when they arise from
     // proxied requests. It might also be suppressing lots of useful errors,
     // of course. Ideally, we would enforce host keys eventually.
     $options[] = '-o';
     $options[] = 'LogLevel=quiet';
 
     // NOTE: We prefix the command with "@username", which the far end of the
     // connection will parse in order to act as the specified user. This
     // behavior is only available to cluster requests signed by a trusted
     // device key.
 
     return csprintf(
       'ssh %Ls -l %s -i %s -p %s %s -- %s %Ls',
       $options,
       $username,
       $key_path,
       $port,
       $host,
       '@'.$this->getUser()->getUsername(),
       $this->getOriginalArguments());
   }
 
   final public function execute(PhutilArgumentParser $args) {
     $this->args = $args;
 
     $viewer = $this->getUser();
     $have_diffusion = PhabricatorApplication::isClassInstalledForViewer(
       'PhabricatorDiffusionApplication',
       $viewer);
     if (!$have_diffusion) {
       throw new Exception(
         pht(
           'You do not have permission to access the Diffusion application, '.
           'so you can not interact with repositories over SSH.'));
     }
 
     $repository = $this->identifyRepository();
     $this->setRepository($repository);
 
     $is_cluster_request = $this->getIsClusterRequest();
     $uri = $repository->getAlmanacServiceURI(
       $viewer,
       $is_cluster_request,
       array(
         'ssh',
       ));
 
     if ($uri) {
       $this->proxyURI = $uri;
     }
 
     try {
       return $this->executeRepositoryOperations();
     } catch (Exception $ex) {
       $this->writeError(get_class($ex).': '.$ex->getMessage());
       return 1;
     }
   }
 
   protected function loadRepositoryWithPath($path) {
     $viewer = $this->getUser();
 
     $info = PhabricatorRepository::parseRepositoryServicePath($path);
     if ($info === null) {
       throw new Exception(
         pht(
           'Unrecognized repository path "%s". Expected a path like "%s" '.
           'or "%s".',
           $path,
           '/diffusion/X/',
           '/diffusion/123/'));
     }
 
     $identifier = $info['identifier'];
     $base = $info['base'];
 
     $this->baseRequestPath = $base;
 
     $repository = id(new PhabricatorRepositoryQuery())
       ->setViewer($viewer)
       ->withIdentifiers(array($identifier))
+      ->needURIs(true)
       ->executeOne();
     if (!$repository) {
       throw new Exception(
         pht('No repository "%s" exists!', $identifier));
     }
 
-    switch ($repository->getServeOverSSH()) {
-      case PhabricatorRepository::SERVE_READONLY:
-      case PhabricatorRepository::SERVE_READWRITE:
-        // If we have read or read/write access, proceed for now. We will
-        // check write access when the user actually issues a write command.
-        break;
-      case PhabricatorRepository::SERVE_OFF:
-      default:
-        throw new Exception(
-          pht(
-            'This repository ("%s") is not available over SSH.',
-            $repository->getDisplayName()));
+    $protocol = PhabricatorRepositoryURI::BUILTIN_PROTOCOL_SSH;
+    if (!$repository->canServeProtocol($protocol, false)) {
+      throw new Exception(
+        pht(
+          'This repository ("%s") is not available over SSH.',
+          $repository->getDisplayName()));
     }
 
     return $repository;
   }
 
   protected function requireWriteAccess($protocol_command = null) {
     if ($this->hasWriteAccess === true) {
       return;
     }
 
     $repository = $this->getRepository();
     $viewer = $this->getUser();
 
     if ($viewer->isOmnipotent()) {
       throw new Exception(
         pht(
           'This request is authenticated as a cluster device, but is '.
           'performing a write. Writes must be performed with a real '.
           'user account.'));
     }
 
-    switch ($repository->getServeOverSSH()) {
-      case PhabricatorRepository::SERVE_READONLY:
-        if ($protocol_command !== null) {
-          throw new Exception(
-            pht(
-              'This repository is read-only over SSH (tried to execute '.
-              'protocol command "%s").',
-              $protocol_command));
-        } else {
-          throw new Exception(
-            pht('This repository is read-only over SSH.'));
-        }
-        break;
-      case PhabricatorRepository::SERVE_READWRITE:
-        $can_push = PhabricatorPolicyFilter::hasCapability(
-          $viewer,
-          $repository,
-          DiffusionPushCapability::CAPABILITY);
-        if (!$can_push) {
-          throw new Exception(
-            pht('You do not have permission to push to this repository.'));
-        }
-        break;
-      case PhabricatorRepository::SERVE_OFF:
-      default:
-        // This shouldn't be reachable because we don't get this far if the
-        // repository isn't enabled, but kick them out anyway.
+    $protocol = PhabricatorRepositoryURI::BUILTIN_PROTOCOL_SSH;
+    if ($repository->canServeProtocol($protocol, true)) {
+      $can_push = PhabricatorPolicyFilter::hasCapability(
+        $viewer,
+        $repository,
+        DiffusionPushCapability::CAPABILITY);
+      if (!$can_push) {
+        throw new Exception(
+          pht('You do not have permission to push to this repository.'));
+      }
+    } else {
+      if ($protocol_command !== null) {
+        throw new Exception(
+          pht(
+            'This repository is read-only over SSH (tried to execute '.
+            'protocol command "%s").',
+            $protocol_command));
+      } else {
         throw new Exception(
-          pht('This repository is not available over SSH.'));
+          pht('This repository is read-only over SSH.'));
+      }
     }
 
     $this->hasWriteAccess = true;
     return $this->hasWriteAccess;
   }
 
   protected function shouldSkipReadSynchronization() {
     $viewer = $this->getUser();
 
     // Currently, the only case where devices interact over SSH without
     // assuming user credentials is when synchronizing before a read. These
     // synchronizing reads do not themselves need to be synchronized.
     if ($viewer->isOmnipotent()) {
       return true;
     }
 
     return false;
   }
 
 
 }
diff --git a/src/applications/diffusion/ssh/DiffusionSubversionServeSSHWorkflow.php b/src/applications/diffusion/ssh/DiffusionSubversionServeSSHWorkflow.php
index 820a38085..57a44d470 100644
--- a/src/applications/diffusion/ssh/DiffusionSubversionServeSSHWorkflow.php
+++ b/src/applications/diffusion/ssh/DiffusionSubversionServeSSHWorkflow.php
@@ -1,440 +1,450 @@
 <?php
 
 /**
  * This protocol has a good spec here:
  *
  *   http://svn.apache.org/repos/asf/subversion/trunk/subversion/libsvn_ra_svn/protocol
  */
 final class DiffusionSubversionServeSSHWorkflow
   extends DiffusionSubversionSSHWorkflow {
 
   private $didSeeWrite;
 
   private $inProtocol;
   private $outProtocol;
 
   private $inSeenGreeting;
 
   private $outPhaseCount = 0;
 
   private $internalBaseURI;
   private $externalBaseURI;
   private $peekBuffer;
   private $command;
   private $isProxying;
 
   private function getCommand() {
     return $this->command;
   }
 
   protected function didConstruct() {
     $this->setName('svnserve');
     $this->setArguments(
       array(
         array(
           'name' => 'tunnel',
           'short' => 't',
         ),
       ));
   }
 
   protected function identifyRepository() {
     // NOTE: In SVN, we need to read the first few protocol frames before we
     // can determine which repository the user is trying to access. We're
     // going to peek at the data on the wire to identify the repository.
 
     $io_channel = $this->getIOChannel();
 
     // Before the client will send us the first protocol frame, we need to send
     // it a connection frame with server capabilities. To figure out the
     // correct frame we're going to start `svnserve`, read the frame from it,
     // send it to the client, then kill the subprocess.
 
     // TODO: This is pretty inelegant and the protocol frame will change very
     // rarely. We could cache it if we can find a reasonable way to dirty the
     // cache.
 
     $command = csprintf('svnserve -t');
     $command = PhabricatorDaemon::sudoCommandAsDaemonUser($command);
     $future = new ExecFuture('%C', $command);
     $exec_channel = new PhutilExecChannel($future);
     $exec_protocol = new DiffusionSubversionWireProtocol();
 
     while (true) {
       PhutilChannel::waitForAny(array($exec_channel));
       $exec_channel->update();
 
       $exec_message = $exec_channel->read();
       if ($exec_message !== null) {
         $messages = $exec_protocol->writeData($exec_message);
         if ($messages) {
           $message = head($messages);
           $raw = $message['raw'];
 
           // Write the greeting frame to the client.
           $io_channel->write($raw);
 
           // Kill the subprocess.
           $future->resolveKill();
           break;
         }
       }
 
       if (!$exec_channel->isOpenForReading()) {
         throw new Exception(
           pht(
             '%s subprocess exited before emitting a protocol frame.',
             'svnserve'));
       }
     }
 
     $io_protocol = new DiffusionSubversionWireProtocol();
     while (true) {
       PhutilChannel::waitForAny(array($io_channel));
       $io_channel->update();
 
       $in_message = $io_channel->read();
       if ($in_message !== null) {
         $this->peekBuffer .= $in_message;
         if (strlen($this->peekBuffer) > (1024 * 1024)) {
           throw new Exception(
             pht(
               'Client transmitted more than 1MB of data without transmitting '.
               'a recognizable protocol frame.'));
         }
 
         $messages = $io_protocol->writeData($in_message);
         if ($messages) {
           $message = head($messages);
           $struct = $message['structure'];
 
           // This is the:
           //
           //   ( version ( cap1 ... ) url ... )
           //
           // The `url` allows us to identify the repository.
 
           $uri = $struct[2]['value'];
           $path = $this->getPathFromSubversionURI($uri);
 
           return $this->loadRepositoryWithPath($path);
         }
       }
 
       if (!$io_channel->isOpenForReading()) {
         throw new Exception(
           pht(
             'Client closed connection before sending a complete protocol '.
             'frame.'));
       }
 
       // If the client has disconnected, kill the subprocess and bail.
       if (!$io_channel->isOpenForWriting()) {
         throw new Exception(
           pht(
             'Client closed connection before receiving response.'));
       }
     }
   }
 
   protected function executeRepositoryOperations() {
     $repository = $this->getRepository();
 
     $args = $this->getArgs();
     if (!$args->getArg('tunnel')) {
       throw new Exception(pht('Expected `%s`!', 'svnserve -t'));
     }
 
     if ($this->shouldProxy()) {
       $command = $this->getProxyCommand();
       $this->isProxying = true;
+      $cwd = null;
     } else {
       $command = csprintf(
         'svnserve -t --tunnel-user=%s',
         $this->getUser()->getUsername());
+      $cwd = PhabricatorEnv::getEmptyCWD();
     }
 
     $command = PhabricatorDaemon::sudoCommandAsDaemonUser($command);
     $future = new ExecFuture('%C', $command);
 
+    // If we're receiving a commit, svnserve will fail to execute the commit
+    // hook with an unhelpful error if the CWD isn't readable by the user we
+    // are sudoing to. Switch to a readable, empty CWD before running
+    // svnserve. See T10941.
+    if ($cwd !== null) {
+      $future->setCWD($cwd);
+    }
+
     $this->inProtocol = new DiffusionSubversionWireProtocol();
     $this->outProtocol = new DiffusionSubversionWireProtocol();
 
     $this->command = id($this->newPassthruCommand())
       ->setIOChannel($this->getIOChannel())
       ->setCommandChannelFromExecFuture($future)
       ->setWillWriteCallback(array($this, 'willWriteMessageCallback'))
       ->setWillReadCallback(array($this, 'willReadMessageCallback'));
 
     $this->command->setPauseIOReads(true);
 
     $err = $this->command->execute();
 
     if (!$err && $this->didSeeWrite) {
       $this->getRepository()->writeStatusMessage(
         PhabricatorRepositoryStatusMessage::TYPE_NEEDS_UPDATE,
         PhabricatorRepositoryStatusMessage::CODE_OKAY);
     }
 
     return $err;
   }
 
   public function willWriteMessageCallback(
     PhabricatorSSHPassthruCommand $command,
     $message) {
 
     $proto = $this->inProtocol;
     $messages = $proto->writeData($message);
 
     $result = array();
     foreach ($messages as $message) {
       $message_raw = $message['raw'];
       $struct = $message['structure'];
 
       if (!$this->inSeenGreeting) {
         $this->inSeenGreeting = true;
 
         // The first message the client sends looks like:
         //
         //   ( version ( cap1 ... ) url ... )
         //
         // We want to grab the URL, load the repository, make sure it exists and
         // is accessible, and then replace it with the location of the
         // repository on disk.
 
         $uri = $struct[2]['value'];
         $struct[2]['value'] = $this->makeInternalURI($uri);
 
         $message_raw = $proto->serializeStruct($struct);
       } else if (isset($struct[0]) && $struct[0]['type'] == 'word') {
 
         if (!$proto->isReadOnlyCommand($struct)) {
           $this->didSeeWrite = true;
           $this->requireWriteAccess($struct[0]['value']);
         }
 
         // Several other commands also pass in URLs. We need to translate
         // all of these into the internal representation; this also makes sure
         // they're valid and accessible.
 
         switch ($struct[0]['value']) {
           case 'reparent':
             // ( reparent ( url ) )
             $struct[1]['value'][0]['value'] = $this->makeInternalURI(
               $struct[1]['value'][0]['value']);
             $message_raw = $proto->serializeStruct($struct);
             break;
           case 'switch':
             // ( switch ( ( rev ) target recurse url ... ) )
             $struct[1]['value'][3]['value'] = $this->makeInternalURI(
               $struct[1]['value'][3]['value']);
             $message_raw = $proto->serializeStruct($struct);
             break;
           case 'diff':
             // ( diff ( ( rev ) target recurse ignore-ancestry url ... ) )
             $struct[1]['value'][4]['value'] = $this->makeInternalURI(
               $struct[1]['value'][4]['value']);
             $message_raw = $proto->serializeStruct($struct);
             break;
           case 'add-file':
           case 'add-dir':
             // ( add-file ( path dir-token file-token [ copy-path copy-rev ] ) )
             // ( add-dir ( path parent child [ copy-path copy-rev ] ) )
             if (isset($struct[1]['value'][3]['value'][0]['value'])) {
               $copy_from = $struct[1]['value'][3]['value'][0]['value'];
               $copy_from = $this->makeInternalURI($copy_from);
               $struct[1]['value'][3]['value'][0]['value'] = $copy_from;
             }
             $message_raw = $proto->serializeStruct($struct);
             break;
         }
       }
 
       $result[] = $message_raw;
     }
 
     if (!$result) {
       return null;
     }
 
     return implode('', $result);
   }
 
   public function willReadMessageCallback(
     PhabricatorSSHPassthruCommand $command,
     $message) {
 
     $proto = $this->outProtocol;
     $messages = $proto->writeData($message);
 
     $result = array();
     foreach ($messages as $message) {
       $message_raw = $message['raw'];
       $struct = $message['structure'];
 
       if (isset($struct[0]) && ($struct[0]['type'] == 'word')) {
 
         if ($struct[0]['value'] == 'success') {
           switch ($this->outPhaseCount) {
             case 0:
               // This is the "greeting", which announces capabilities.
 
               // We already sent this when we were figuring out which
               // repository this request is for, so we aren't going to send
               // it again.
 
               // Instead, we're going to replay the client's response (which
               // we also already read).
 
               $command = $this->getCommand();
               $command->writeIORead($this->peekBuffer);
               $command->setPauseIOReads(false);
 
               $message_raw = null;
               break;
             case 1:
               // This responds to the client greeting, and announces auth.
               break;
             case 2:
               // This responds to auth, which should be trivial over SSH.
               break;
             case 3:
               // This contains the URI of the repository. We need to edit it;
               // if it does not match what the client requested it will reject
               // the response.
               $struct[1]['value'][1]['value'] = $this->makeExternalURI(
                 $struct[1]['value'][1]['value']);
               $message_raw = $proto->serializeStruct($struct);
               break;
             default:
               // We don't care about other protocol frames.
               break;
           }
 
           $this->outPhaseCount++;
         } else if ($struct[0]['value'] == 'failure') {
           // Find any error messages which include the internal URI, and
           // replace the text with the external URI.
           foreach ($struct[1]['value'] as $key => $error) {
             $code = $error['value'][0]['value'];
             $message = $error['value'][1]['value'];
 
             $message = str_replace(
               $this->internalBaseURI,
               $this->externalBaseURI,
               $message);
 
             // Derp derp derp derp derp. The structure looks like this:
             //   ( failure ( ( code message ... ) ... ) )
             $struct[1]['value'][$key]['value'][1]['value'] = $message;
           }
           $message_raw = $proto->serializeStruct($struct);
         }
 
       }
 
       if ($message_raw !== null) {
         $result[] = $message_raw;
       }
     }
 
     if (!$result) {
       return null;
     }
 
     return implode('', $result);
   }
 
   private function getPathFromSubversionURI($uri_string) {
     $uri = new PhutilURI($uri_string);
 
     $proto = $uri->getProtocol();
     if ($proto !== 'svn+ssh') {
       throw new Exception(
         pht(
           'Protocol for URI "%s" MUST be "%s".',
           $uri_string,
           'svn+ssh'));
     }
     $path = $uri->getPath();
 
     // Subversion presumably deals with this, but make sure there's nothing
     // sketchy going on with the URI.
     if (preg_match('(/\\.\\./)', $path)) {
       throw new Exception(
         pht(
           'String "%s" is invalid in path specification "%s".',
           '/../',
           $uri_string));
     }
 
     $path = $this->normalizeSVNPath($path);
 
     return $path;
   }
 
   private function makeInternalURI($uri_string) {
     if ($this->isProxying) {
       return $uri_string;
     }
 
     $uri = new PhutilURI($uri_string);
 
     $repository = $this->getRepository();
 
     $path = $this->getPathFromSubversionURI($uri_string);
     $external_base = $this->getBaseRequestPath();
 
     // Replace "/diffusion/X" in the request with the repository local path,
     // so "/diffusion/X/master/" becomes "/path/to/repository/X/master/".
     $local_path = rtrim($repository->getLocalPath(), '/');
     $path = $local_path.substr($path, strlen($external_base));
 
     // NOTE: We are intentionally NOT removing username information from the
     // URI. Subversion retains it over the course of the request and considers
     // two repositories with different username identifiers to be distinct and
     // incompatible.
 
     $uri->setPath($path);
 
     // If this is happening during the handshake, these are the base URIs for
     // the request.
     if ($this->externalBaseURI === null) {
       $pre = (string)id(clone $uri)->setPath('');
 
       $external_path = $external_base;
       $external_path = $this->normalizeSVNPath($external_path);
       $this->externalBaseURI = $pre.$external_path;
 
       $internal_path = rtrim($repository->getLocalPath(), '/');
       $internal_path = $this->normalizeSVNPath($internal_path);
       $this->internalBaseURI = $pre.$internal_path;
     }
 
     return (string)$uri;
   }
 
   private function makeExternalURI($uri) {
     if ($this->isProxying) {
       return $uri;
     }
 
     $internal = $this->internalBaseURI;
     $external = $this->externalBaseURI;
 
     if (strncmp($uri, $internal, strlen($internal)) === 0) {
       $uri = $external.substr($uri, strlen($internal));
     }
 
     return $uri;
   }
 
   private function normalizeSVNPath($path) {
     // Subversion normalizes redundant slashes internally, so normalize them
     // here as well to make sure things match up.
     $path = preg_replace('(/+)', '/', $path);
 
     return $path;
   }
 
 }
diff --git a/src/applications/diffusion/view/DiffusionCloneURIView.php b/src/applications/diffusion/view/DiffusionCloneURIView.php
new file mode 100644
index 000000000..fc861ed18
--- /dev/null
+++ b/src/applications/diffusion/view/DiffusionCloneURIView.php
@@ -0,0 +1,131 @@
+<?php
+
+
+final class DiffusionCloneURIView extends AphrontView {
+
+  private $repository;
+  private $repositoryURI;
+  private $displayURI;
+
+  public function setRepository(PhabricatorRepository $repository) {
+    $this->repository = $repository;
+    return $this;
+  }
+
+  public function getRepository() {
+    return $this->repository;
+  }
+
+  public function setRepositoryURI(PhabricatorRepositoryURI $repository_uri) {
+    $this->repositoryURI = $repository_uri;
+    return $this;
+  }
+
+  public function getRepositoryURI() {
+    return $this->repositoryURI;
+  }
+
+  public function setDisplayURI($display_uri) {
+    $this->displayURI = $display_uri;
+    return $this;
+  }
+
+  public function getDisplayURI() {
+    return $this->displayURI;
+  }
+
+  public function render() {
+    require_celerity_resource('diffusion-icons-css');
+
+    Javelin::initBehavior('select-content');
+
+    $uri_id = celerity_generate_unique_node_id();
+
+    $display = $this->getDisplayURI();
+
+    $input = javelin_tag(
+      'input',
+      array(
+        'id' => $uri_id,
+        'type' => 'text',
+        'value' => $display,
+        'class' => 'diffusion-clone-uri',
+        'readonly' => 'true',
+      ));
+
+    $uri = $this->getRepositoryURI();
+    switch ($uri->getEffectiveIOType()) {
+      case PhabricatorRepositoryURI::IO_READ:
+        $io_icon = 'fa-eye';
+        $io_tip = pht('Read-Only');
+        break;
+      case PhabricatorRepositoryURI::IO_READWRITE:
+        $io_icon = 'fa-download';
+        $io_tip = pht('Read / Write');
+        break;
+      default:
+        $io_icon = 'fa-cloud';
+        $io_tip = pht('External');
+        break;
+    }
+
+    $io = id(new PHUIButtonView())
+      ->setTag('a')
+      ->setColor(PHUIButtonView::GREY)
+      ->setIcon($io_icon)
+      ->setHref('#')
+      ->addSigil('select-content')
+      ->addSigil('has-tooltip')
+      ->setMetadata(
+        array(
+          'tip' => $io_tip,
+          'selectID' => $uri_id,
+        ));
+
+    switch ($uri->getEffectiveIOType()) {
+      case PhabricatorRepositoryURI::IO_READ:
+      case PhabricatorRepositoryURI::IO_READWRITE:
+        switch ($uri->getBuiltinProtocol()) {
+          case PhabricatorRepositoryURI::BUILTIN_PROTOCOL_SSH:
+            $auth_uri = '/settings/panel/ssh/';
+            $auth_tip = pht('Manage SSH Keys');
+            $auth_disabled = false;
+            break;
+          default:
+            $auth_uri = '/settings/panel/vcspassword';
+            $auth_tip = pht('Manage Password');
+            $auth_disabled = false;
+            break;
+        }
+        break;
+      default:
+        $auth_disabled = true;
+        $auth_tip = pht('External');
+        $auth_uri = '#';
+        break;
+    }
+
+    $credentials = id(new PHUIButtonView())
+      ->setTag('a')
+      ->setColor(PHUIButtonView::GREY)
+      ->setIcon('fa-key')
+      ->setTooltip($auth_tip)
+      ->setHref($auth_uri)
+      ->setDisabled($auth_disabled);
+
+    $cells = array();
+    $cells[] = phutil_tag('td', array(), $input);
+    $cells[] = phutil_tag('th', array(), $io);
+    $cells[] = phutil_tag('th', array(), $credentials);
+
+    $row = phutil_tag('tr', array(), $cells);
+
+    return phutil_tag(
+      'table',
+      array(
+        'class' => 'diffusion-clone-uri-table',
+      ),
+      $row);
+  }
+
+}
diff --git a/src/applications/drydock/query/DrydockAuthorizationQuery.php b/src/applications/drydock/query/DrydockAuthorizationQuery.php
index 6d2cddcf8..d6436fc93 100644
--- a/src/applications/drydock/query/DrydockAuthorizationQuery.php
+++ b/src/applications/drydock/query/DrydockAuthorizationQuery.php
@@ -1,146 +1,175 @@
 <?php
 
 final class DrydockAuthorizationQuery extends DrydockQuery {
 
   private $ids;
   private $phids;
   private $blueprintPHIDs;
   private $objectPHIDs;
   private $blueprintStates;
   private $objectStates;
 
+  public static function isFullyAuthorized(
+    $object_phid,
+    array $blueprint_phids) {
+
+    if (!$blueprint_phids) {
+      return true;
+    }
+
+    $authorizations = id(new self())
+      ->setViewer(PhabricatorUser::getOmnipotentUser())
+      ->withObjectPHIDs(array($object_phid))
+      ->withBlueprintPHIDs($blueprint_phids)
+      ->execute();
+    $authorizations = mpull($authorizations, null, 'getBlueprintPHID');
+
+    foreach ($blueprint_phids as $phid) {
+      $authorization = idx($authorizations, $phid);
+      if (!$authorization) {
+        return false;
+      }
+
+      if (!$authorization->isAuthorized()) {
+        return false;
+      }
+    }
+
+    return true;
+  }
+
   public function withIDs(array $ids) {
     $this->ids = $ids;
     return $this;
   }
 
   public function withPHIDs(array $phids) {
     $this->phids = $phids;
     return $this;
   }
 
   public function withBlueprintPHIDs(array $phids) {
     $this->blueprintPHIDs = $phids;
     return $this;
   }
 
   public function withObjectPHIDs(array $phids) {
     $this->objectPHIDs = $phids;
     return $this;
   }
 
   public function withBlueprintStates(array $states) {
     $this->blueprintStates = $states;
     return $this;
   }
 
   public function withObjectStates(array $states) {
     $this->objectStates = $states;
     return $this;
   }
 
   public function newResultObject() {
     return new DrydockAuthorization();
   }
 
   protected function loadPage() {
     return $this->loadStandardPage($this->newResultObject());
   }
 
   protected function willFilterPage(array $authorizations) {
     $blueprint_phids = mpull($authorizations, 'getBlueprintPHID');
     if ($blueprint_phids) {
       $blueprints = id(new DrydockBlueprintQuery())
         ->setViewer($this->getViewer())
         ->setParentQuery($this)
         ->withPHIDs($blueprint_phids)
         ->execute();
       $blueprints = mpull($blueprints, null, 'getPHID');
     } else {
       $blueprints = array();
     }
 
     foreach ($authorizations as $key => $authorization) {
       $blueprint = idx($blueprints, $authorization->getBlueprintPHID());
       if (!$blueprint) {
         $this->didRejectResult($authorization);
         unset($authorizations[$key]);
         continue;
       }
       $authorization->attachBlueprint($blueprint);
     }
 
     $object_phids = mpull($authorizations, 'getObjectPHID');
     if ($object_phids) {
       $objects = id(new PhabricatorObjectQuery())
         ->setViewer($this->getViewer())
         ->setParentQuery($this)
         ->withPHIDs($object_phids)
         ->execute();
       $objects = mpull($objects, null, 'getPHID');
     } else {
       $objects = array();
     }
 
     foreach ($authorizations as $key => $authorization) {
       $object = idx($objects, $authorization->getObjectPHID());
       if (!$object) {
         $this->didRejectResult($authorization);
         unset($authorizations[$key]);
         continue;
       }
       $authorization->attachObject($object);
     }
 
     return $authorizations;
   }
 
   protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) {
     $where = parent::buildWhereClauseParts($conn);
 
     if ($this->ids !== null) {
       $where[] = qsprintf(
         $conn,
         'id IN (%Ld)',
         $this->ids);
     }
 
     if ($this->phids !== null) {
       $where[] = qsprintf(
         $conn,
         'phid IN (%Ls)',
         $this->phids);
     }
 
     if ($this->blueprintPHIDs !== null) {
       $where[] = qsprintf(
         $conn,
         'blueprintPHID IN (%Ls)',
         $this->blueprintPHIDs);
     }
 
     if ($this->objectPHIDs !== null) {
       $where[] = qsprintf(
         $conn,
         'objectPHID IN (%Ls)',
         $this->objectPHIDs);
     }
 
     if ($this->blueprintStates !== null) {
       $where[] = qsprintf(
         $conn,
         'blueprintAuthorizationState IN (%Ls)',
         $this->blueprintStates);
     }
 
     if ($this->objectStates !== null) {
       $where[] = qsprintf(
         $conn,
         'objectAuthorizationState IN (%Ls)',
         $this->objectStates);
     }
 
     return $where;
   }
 
 }
diff --git a/src/applications/drydock/storage/DrydockAuthorization.php b/src/applications/drydock/storage/DrydockAuthorization.php
index ea1160b23..9503a0376 100644
--- a/src/applications/drydock/storage/DrydockAuthorization.php
+++ b/src/applications/drydock/storage/DrydockAuthorization.php
@@ -1,202 +1,207 @@
 <?php
 
 final class DrydockAuthorization extends DrydockDAO
   implements
     PhabricatorPolicyInterface {
 
   const OBJECTAUTH_ACTIVE = 'active';
   const OBJECTAUTH_INACTIVE = 'inactive';
 
   const BLUEPRINTAUTH_REQUESTED = 'requested';
   const BLUEPRINTAUTH_AUTHORIZED = 'authorized';
   const BLUEPRINTAUTH_DECLINED = 'declined';
 
   protected $blueprintPHID;
   protected $blueprintAuthorizationState;
   protected $objectPHID;
   protected $objectAuthorizationState;
 
   private $blueprint = self::ATTACHABLE;
   private $object = self::ATTACHABLE;
 
   protected function getConfiguration() {
     return array(
       self::CONFIG_AUX_PHID => true,
       self::CONFIG_COLUMN_SCHEMA => array(
         'blueprintAuthorizationState' => 'text32',
         'objectAuthorizationState' => 'text32',
       ),
       self::CONFIG_KEY_SCHEMA => array(
         'key_unique' => array(
           'columns' => array('objectPHID', 'blueprintPHID'),
           'unique' => true,
         ),
         'key_blueprint' => array(
           'columns' => array('blueprintPHID', 'blueprintAuthorizationState'),
         ),
         'key_object' => array(
           'columns' => array('objectPHID', 'objectAuthorizationState'),
         ),
       ),
     ) + parent::getConfiguration();
   }
 
   public function generatePHID() {
     return PhabricatorPHID::generateNewPHID(
       DrydockAuthorizationPHIDType::TYPECONST);
   }
 
   public function attachBlueprint(DrydockBlueprint $blueprint) {
     $this->blueprint = $blueprint;
     return $this;
   }
 
   public function getBlueprint() {
     return $this->assertAttached($this->blueprint);
   }
 
   public function attachObject($object) {
     $this->object = $object;
     return $this;
   }
 
   public function getObject() {
     return $this->assertAttached($this->object);
   }
 
   public static function getBlueprintStateIcon($state) {
     $map = array(
       self::BLUEPRINTAUTH_REQUESTED => 'fa-exclamation-circle pink',
       self::BLUEPRINTAUTH_AUTHORIZED => 'fa-check-circle green',
       self::BLUEPRINTAUTH_DECLINED => 'fa-times red',
     );
 
     return idx($map, $state, null);
   }
 
   public static function getBlueprintStateName($state) {
     $map = array(
       self::BLUEPRINTAUTH_REQUESTED => pht('Requested'),
       self::BLUEPRINTAUTH_AUTHORIZED => pht('Authorized'),
       self::BLUEPRINTAUTH_DECLINED => pht('Declined'),
     );
 
     return idx($map, $state, pht('<Unknown: %s>', $state));
   }
 
   public static function getObjectStateName($state) {
     $map = array(
       self::OBJECTAUTH_ACTIVE => pht('Active'),
       self::OBJECTAUTH_INACTIVE => pht('Inactive'),
     );
 
     return idx($map, $state, pht('<Unknown: %s>', $state));
   }
 
+  public function isAuthorized() {
+    $state = $this->getBlueprintAuthorizationState();
+    return ($state == self::BLUEPRINTAUTH_AUTHORIZED);
+  }
+
   /**
    * Apply external authorization effects after a user chagnes the value of a
    * blueprint selector control an object.
    *
    * @param PhabricatorUser User applying the change.
    * @param phid Object PHID change is being applied to.
    * @param list<phid> Old blueprint PHIDs.
    * @param list<phid> New blueprint PHIDs.
    * @return void
    */
   public static function applyAuthorizationChanges(
     PhabricatorUser $viewer,
     $object_phid,
     array $old,
     array $new) {
 
     $old_phids = array_fuse($old);
     $new_phids = array_fuse($new);
 
     $rem_phids = array_diff_key($old_phids, $new_phids);
     $add_phids = array_diff_key($new_phids, $old_phids);
 
     $altered_phids = $rem_phids + $add_phids;
 
     if (!$altered_phids) {
       return;
     }
 
     $authorizations = id(new DrydockAuthorizationQuery())
       ->setViewer(PhabricatorUser::getOmnipotentUser())
       ->withObjectPHIDs(array($object_phid))
       ->withBlueprintPHIDs($altered_phids)
       ->execute();
     $authorizations = mpull($authorizations, null, 'getBlueprintPHID');
 
     $state_active = self::OBJECTAUTH_ACTIVE;
     $state_inactive = self::OBJECTAUTH_INACTIVE;
 
     $state_requested = self::BLUEPRINTAUTH_REQUESTED;
 
     // Disable the object side of the authorization for any existing
     // authorizations.
     foreach ($rem_phids as $rem_phid) {
       $authorization = idx($authorizations, $rem_phid);
       if (!$authorization) {
         continue;
       }
 
       $authorization
         ->setObjectAuthorizationState($state_inactive)
         ->save();
     }
 
     // For new authorizations, either add them or reactivate them depending
     // on the current state.
     foreach ($add_phids as $add_phid) {
       $needs_update = false;
 
       $authorization = idx($authorizations, $add_phid);
       if (!$authorization) {
         $authorization = id(new DrydockAuthorization())
           ->setObjectPHID($object_phid)
           ->setObjectAuthorizationState($state_active)
           ->setBlueprintPHID($add_phid)
           ->setBlueprintAuthorizationState($state_requested);
 
         $needs_update = true;
       } else {
         $current_state = $authorization->getObjectAuthorizationState();
         if ($current_state != $state_active) {
           $authorization->setObjectAuthorizationState($state_active);
           $needs_update = true;
         }
       }
 
       if ($needs_update) {
         $authorization->save();
       }
     }
   }
 
 /* -(  PhabricatorPolicyInterface  )----------------------------------------- */
 
 
   public function getCapabilities() {
     return array(
       PhabricatorPolicyCapability::CAN_VIEW,
       PhabricatorPolicyCapability::CAN_EDIT,
     );
   }
 
   public function getPolicy($capability) {
     return $this->getBlueprint()->getPolicy($capability);
   }
 
   public function hasAutomaticCapability($capability, PhabricatorUser $viewer) {
     return $this->getBlueprint()->hasAutomaticCapability($capability, $viewer);
   }
 
   public function describeAutomaticCapability($capability) {
     return pht(
       'An authorization inherits the policies of the blueprint it '.
       'authorizes access to.');
   }
 
 
 }
diff --git a/src/applications/files/markup/PhabricatorEmbedFileRemarkupRule.php b/src/applications/files/markup/PhabricatorEmbedFileRemarkupRule.php
index ecb0e4fd0..22459a34a 100644
--- a/src/applications/files/markup/PhabricatorEmbedFileRemarkupRule.php
+++ b/src/applications/files/markup/PhabricatorEmbedFileRemarkupRule.php
@@ -1,244 +1,252 @@
 <?php
 
 final class PhabricatorEmbedFileRemarkupRule
   extends PhabricatorObjectRemarkupRule {
 
   const KEY_EMBED_FILE_PHIDS = 'phabricator.embedded-file-phids';
 
   protected function getObjectNamePrefix() {
     return 'F';
   }
 
   protected function loadObjects(array $ids) {
     $engine = $this->getEngine();
 
     $viewer = $engine->getConfig('viewer');
     $objects = id(new PhabricatorFileQuery())
       ->setViewer($viewer)
       ->withIDs($ids)
       ->needTransforms(
         array(
           PhabricatorFileThumbnailTransform::TRANSFORM_PREVIEW,
         ))
       ->execute();
 
     $phids_key = self::KEY_EMBED_FILE_PHIDS;
     $phids = $engine->getTextMetadata($phids_key, array());
     foreach (mpull($objects, 'getPHID') as $phid) {
       $phids[] = $phid;
     }
     $engine->setTextMetadata($phids_key, $phids);
 
     return $objects;
   }
 
   protected function renderObjectEmbed(
     $object,
     PhabricatorObjectHandle $handle,
     $options) {
 
     $options = $this->getFileOptions($options) + array(
       'name' => $object->getName(),
     );
 
     $is_viewable_image = $object->isViewableImage();
     $is_audio = $object->isAudio();
     $force_link = ($options['layout'] == 'link');
 
     $options['viewable'] = ($is_viewable_image || $is_audio);
 
     if ($is_viewable_image && !$force_link) {
       return $this->renderImageFile($object, $handle, $options);
     } else if ($is_audio && !$force_link) {
       return $this->renderAudioFile($object, $handle, $options);
     } else {
       return $this->renderFileLink($object, $handle, $options);
     }
   }
 
   private function getFileOptions($option_string) {
     $options = array(
       'size'    => null,
       'layout'  => 'left',
       'float'   => false,
       'width'   => null,
       'height'  => null,
       'alt' => null,
     );
 
     if ($option_string) {
       $option_string = trim($option_string, ', ');
       $parser = new PhutilSimpleOptions();
       $options = $parser->parse($option_string) + $options;
     }
 
     return $options;
   }
 
   private function renderImageFile(
     PhabricatorFile $file,
     PhabricatorObjectHandle $handle,
     array $options) {
 
     require_celerity_resource('lightbox-attachment-css');
 
     $attrs = array();
     $image_class = 'phabricator-remarkup-embed-image';
 
     $use_size = true;
     if (!$options['size']) {
       $width = $this->parseDimension($options['width']);
       $height = $this->parseDimension($options['height']);
       if ($width || $height) {
         $use_size = false;
         $attrs += array(
           'src' => $file->getBestURI(),
           'width' => $width,
           'height' => $height,
         );
       }
     }
 
     if ($use_size) {
       switch ((string)$options['size']) {
         case 'full':
           $attrs += array(
             'src' => $file->getBestURI(),
             'height' => $file->getImageHeight(),
             'width' => $file->getImageWidth(),
           );
           $image_class = 'phabricator-remarkup-embed-image-full';
           break;
+        // Displays "full" in normal Remarkup, "wide" in Documents
+        case 'wide':
+          $attrs += array(
+            'src' => $file->getBestURI(),
+            'width' => $file->getImageWidth(),
+          );
+          $image_class = 'phabricator-remarkup-embed-image-wide';
+          break;
         case 'thumb':
         default:
           $preview_key = PhabricatorFileThumbnailTransform::TRANSFORM_PREVIEW;
           $xform = PhabricatorFileTransform::getTransformByKey($preview_key);
 
           $existing_xform = $file->getTransform($preview_key);
           if ($existing_xform) {
             $xform_uri = $existing_xform->getCDNURI();
           } else {
             $xform_uri = $file->getURIForTransform($xform);
           }
 
           $attrs['src'] = $xform_uri;
 
           $dimensions = $xform->getTransformedDimensions($file);
           if ($dimensions) {
             list($x, $y) = $dimensions;
             $attrs['width'] = $x;
             $attrs['height'] = $y;
           }
           break;
       }
     }
 
     if (isset($options['alt'])) {
       $attrs['alt'] = $options['alt'];
     }
 
     $img = phutil_tag('img', $attrs);
 
     $embed = javelin_tag(
       'a',
       array(
         'href'        => $file->getBestURI(),
         'class'       => $image_class,
         'sigil'       => 'lightboxable',
         'meta'        => array(
           'phid'     => $file->getPHID(),
           'uri'      => $file->getBestURI(),
           'dUri'     => $file->getDownloadURI(),
           'viewable' => true,
         ),
       ),
       $img);
 
     switch ($options['layout']) {
       case 'right':
       case 'center':
       case 'inline':
       case 'left':
         $layout_class = 'phabricator-remarkup-embed-layout-'.$options['layout'];
         break;
       default:
         $layout_class = 'phabricator-remarkup-embed-layout-left';
         break;
     }
 
     if ($options['float']) {
       switch ($options['layout']) {
         case 'center':
         case 'inline':
           break;
         case 'right':
           $layout_class .= ' phabricator-remarkup-embed-float-right';
           break;
         case 'left':
         default:
           $layout_class .= ' phabricator-remarkup-embed-float-left';
           break;
       }
     }
 
     return phutil_tag(
       ($options['layout'] == 'inline' ? 'span' : 'div'),
       array(
         'class' => $layout_class,
       ),
       $embed);
   }
 
   private function renderAudioFile(
     PhabricatorFile $file,
     PhabricatorObjectHandle $handle,
     array $options) {
 
     if (idx($options, 'autoplay')) {
       $preload = 'auto';
       $autoplay = 'autoplay';
     } else {
       $preload = 'none';
       $autoplay = null;
     }
 
     return $this->newTag(
       'audio',
       array(
         'controls' => 'controls',
         'preload' => $preload,
         'autoplay' => $autoplay,
         'loop' => idx($options, 'loop') ? 'loop' : null,
       ),
       $this->newTag(
         'source',
         array(
           'src' => $file->getBestURI(),
           'type' => $file->getMimeType(),
         )));
   }
 
   private function renderFileLink(
     PhabricatorFile $file,
     PhabricatorObjectHandle $handle,
     array $options) {
 
     return id(new PhabricatorFileLinkView())
       ->setFilePHID($file->getPHID())
       ->setFileName($this->assertFlatText($options['name']))
       ->setFileDownloadURI($file->getDownloadURI())
       ->setFileViewURI($file->getBestURI())
       ->setFileViewable((bool)$options['viewable']);
   }
 
   private function parseDimension($string) {
     $string = trim($string);
 
     if (preg_match('/^(?:\d*\\.)?\d+%?$/', $string)) {
       return $string;
     }
 
     return null;
   }
 
 }
diff --git a/src/applications/maniphest/editor/ManiphestTransactionEditor.php b/src/applications/maniphest/editor/ManiphestTransactionEditor.php
index 6c9a7d008..da0d12db1 100644
--- a/src/applications/maniphest/editor/ManiphestTransactionEditor.php
+++ b/src/applications/maniphest/editor/ManiphestTransactionEditor.php
@@ -1,1197 +1,1266 @@
 <?php
 
 final class ManiphestTransactionEditor
   extends PhabricatorApplicationTransactionEditor {
 
   private $moreValidationErrors = array();
 
   public function getEditorApplicationClass() {
     return 'PhabricatorManiphestApplication';
   }
 
   public function getEditorObjectsDescription() {
     return pht('Maniphest Tasks');
   }
 
   public function getTransactionTypes() {
     $types = parent::getTransactionTypes();
 
     $types[] = PhabricatorTransactions::TYPE_COMMENT;
     $types[] = PhabricatorTransactions::TYPE_EDGE;
     $types[] = ManiphestTransaction::TYPE_PRIORITY;
     $types[] = ManiphestTransaction::TYPE_STATUS;
     $types[] = ManiphestTransaction::TYPE_TITLE;
     $types[] = ManiphestTransaction::TYPE_DESCRIPTION;
     $types[] = ManiphestTransaction::TYPE_OWNER;
     $types[] = ManiphestTransaction::TYPE_SUBPRIORITY;
     $types[] = ManiphestTransaction::TYPE_MERGED_INTO;
     $types[] = ManiphestTransaction::TYPE_MERGED_FROM;
     $types[] = ManiphestTransaction::TYPE_UNBLOCK;
     $types[] = ManiphestTransaction::TYPE_PARENT;
     $types[] = ManiphestTransaction::TYPE_COVER_IMAGE;
     $types[] = ManiphestTransaction::TYPE_POINTS;
     $types[] = PhabricatorTransactions::TYPE_COLUMNS;
     $types[] = PhabricatorTransactions::TYPE_VIEW_POLICY;
     $types[] = PhabricatorTransactions::TYPE_EDIT_POLICY;
 
     return $types;
   }
 
   protected function getCustomTransactionOldValue(
     PhabricatorLiskDAO $object,
     PhabricatorApplicationTransaction $xaction) {
 
     switch ($xaction->getTransactionType()) {
       case ManiphestTransaction::TYPE_PRIORITY:
         if ($this->getIsNewObject()) {
           return null;
         }
         return (int)$object->getPriority();
       case ManiphestTransaction::TYPE_STATUS:
         if ($this->getIsNewObject()) {
           return null;
         }
         return $object->getStatus();
       case ManiphestTransaction::TYPE_TITLE:
         if ($this->getIsNewObject()) {
           return null;
         }
         return $object->getTitle();
       case ManiphestTransaction::TYPE_DESCRIPTION:
         if ($this->getIsNewObject()) {
           return null;
         }
         return $object->getDescription();
       case ManiphestTransaction::TYPE_OWNER:
         return nonempty($object->getOwnerPHID(), null);
       case ManiphestTransaction::TYPE_SUBPRIORITY:
         return $object->getSubpriority();
       case ManiphestTransaction::TYPE_COVER_IMAGE:
         return $object->getCoverImageFilePHID();
       case ManiphestTransaction::TYPE_POINTS:
         $points = $object->getPoints();
         if ($points !== null) {
           $points = (double)$points;
         }
         return $points;
       case ManiphestTransaction::TYPE_MERGED_INTO:
       case ManiphestTransaction::TYPE_MERGED_FROM:
         return null;
       case ManiphestTransaction::TYPE_PARENT:
       case PhabricatorTransactions::TYPE_COLUMNS:
         return null;
     }
   }
 
   protected function getCustomTransactionNewValue(
     PhabricatorLiskDAO $object,
     PhabricatorApplicationTransaction $xaction) {
 
     switch ($xaction->getTransactionType()) {
       case ManiphestTransaction::TYPE_PRIORITY:
         return (int)$xaction->getNewValue();
       case ManiphestTransaction::TYPE_OWNER:
         return nonempty($xaction->getNewValue(), null);
       case ManiphestTransaction::TYPE_STATUS:
       case ManiphestTransaction::TYPE_TITLE:
       case ManiphestTransaction::TYPE_DESCRIPTION:
       case ManiphestTransaction::TYPE_SUBPRIORITY:
       case ManiphestTransaction::TYPE_MERGED_INTO:
       case ManiphestTransaction::TYPE_MERGED_FROM:
       case ManiphestTransaction::TYPE_UNBLOCK:
       case ManiphestTransaction::TYPE_COVER_IMAGE:
         return $xaction->getNewValue();
       case ManiphestTransaction::TYPE_PARENT:
       case PhabricatorTransactions::TYPE_COLUMNS:
         return $xaction->getNewValue();
       case ManiphestTransaction::TYPE_POINTS:
         $value = $xaction->getNewValue();
         if (!strlen($value)) {
           $value = null;
         }
         if ($value !== null) {
           $value = (double)$value;
         }
         return $value;
     }
   }
 
   protected function transactionHasEffect(
     PhabricatorLiskDAO $object,
     PhabricatorApplicationTransaction $xaction) {
 
     $old = $xaction->getOldValue();
     $new = $xaction->getNewValue();
 
     switch ($xaction->getTransactionType()) {
       case PhabricatorTransactions::TYPE_COLUMNS:
         return (bool)$new;
     }
 
     return parent::transactionHasEffect($object, $xaction);
   }
 
   protected function applyCustomInternalTransaction(
     PhabricatorLiskDAO $object,
     PhabricatorApplicationTransaction $xaction) {
 
     switch ($xaction->getTransactionType()) {
       case ManiphestTransaction::TYPE_PRIORITY:
         return $object->setPriority($xaction->getNewValue());
       case ManiphestTransaction::TYPE_STATUS:
         return $object->setStatus($xaction->getNewValue());
       case ManiphestTransaction::TYPE_TITLE:
         return $object->setTitle($xaction->getNewValue());
       case ManiphestTransaction::TYPE_DESCRIPTION:
         return $object->setDescription($xaction->getNewValue());
       case ManiphestTransaction::TYPE_OWNER:
         $phid = $xaction->getNewValue();
 
         // Update the "ownerOrdering" column to contain the full name of the
         // owner, if the task is assigned.
 
         $handle = null;
         if ($phid) {
           $handle = id(new PhabricatorHandleQuery())
             ->setViewer($this->getActor())
             ->withPHIDs(array($phid))
             ->executeOne();
         }
 
         if ($handle) {
           $object->setOwnerOrdering($handle->getName());
         } else {
           $object->setOwnerOrdering(null);
         }
 
         return $object->setOwnerPHID($phid);
       case ManiphestTransaction::TYPE_SUBPRIORITY:
         $object->setSubpriority($xaction->getNewValue());
         return;
       case ManiphestTransaction::TYPE_MERGED_INTO:
         $object->setStatus(ManiphestTaskStatus::getDuplicateStatus());
         return;
       case ManiphestTransaction::TYPE_COVER_IMAGE:
         $file_phid = $xaction->getNewValue();
 
         if ($file_phid) {
           $file = id(new PhabricatorFileQuery())
             ->setViewer($this->getActor())
             ->withPHIDs(array($file_phid))
             ->executeOne();
         } else {
           $file = null;
         }
 
         if (!$file || !$file->isTransformableImage()) {
           $object->setProperty('cover.filePHID', null);
           $object->setProperty('cover.thumbnailPHID', null);
           return;
         }
 
         $xform_key = PhabricatorFileThumbnailTransform::TRANSFORM_WORKCARD;
 
         $xform = PhabricatorFileTransform::getTransformByKey($xform_key)
           ->executeTransform($file);
 
         $object->setProperty('cover.filePHID', $file->getPHID());
         $object->setProperty('cover.thumbnailPHID', $xform->getPHID());
         return;
       case ManiphestTransaction::TYPE_POINTS:
         $object->setPoints($xaction->getNewValue());
         return;
       case ManiphestTransaction::TYPE_MERGED_FROM:
       case ManiphestTransaction::TYPE_PARENT:
       case PhabricatorTransactions::TYPE_COLUMNS:
         return;
     }
   }
 
   protected function applyCustomExternalTransaction(
     PhabricatorLiskDAO $object,
     PhabricatorApplicationTransaction $xaction) {
 
     switch ($xaction->getTransactionType()) {
       case ManiphestTransaction::TYPE_PARENT:
         $parent_phid = $xaction->getNewValue();
         $parent_type = ManiphestTaskDependsOnTaskEdgeType::EDGECONST;
         $task_phid = $object->getPHID();
 
         id(new PhabricatorEdgeEditor())
           ->addEdge($parent_phid, $parent_type, $task_phid)
           ->save();
         break;
       case PhabricatorTransactions::TYPE_COLUMNS:
         foreach ($xaction->getNewValue() as $move) {
           $this->applyBoardMove($object, $move);
         }
         break;
       default:
         break;
     }
   }
 
   protected function applyFinalEffects(
     PhabricatorLiskDAO $object,
     array $xactions) {
 
     // When we change the status of a task, update tasks this tasks blocks
     // with a message to the effect of "alincoln resolved blocking task Txxx."
     $unblock_xaction = null;
     foreach ($xactions as $xaction) {
       switch ($xaction->getTransactionType()) {
         case ManiphestTransaction::TYPE_STATUS:
           $unblock_xaction = $xaction;
           break;
       }
     }
 
     if ($unblock_xaction !== null) {
       $blocked_phids = PhabricatorEdgeQuery::loadDestinationPHIDs(
         $object->getPHID(),
         ManiphestTaskDependedOnByTaskEdgeType::EDGECONST);
       if ($blocked_phids) {
         // In theory we could apply these through policies, but that seems a
         // little bit surprising. For now, use the actor's vision.
         $blocked_tasks = id(new ManiphestTaskQuery())
           ->setViewer($this->getActor())
           ->withPHIDs($blocked_phids)
           ->needSubscriberPHIDs(true)
           ->needProjectPHIDs(true)
           ->execute();
 
         $old = $unblock_xaction->getOldValue();
         $new = $unblock_xaction->getNewValue();
 
         foreach ($blocked_tasks as $blocked_task) {
           $parent_xaction = id(new ManiphestTransaction())
             ->setTransactionType(ManiphestTransaction::TYPE_UNBLOCK)
             ->setOldValue(array($object->getPHID() => $old))
             ->setNewValue(array($object->getPHID() => $new));
 
           if ($this->getIsNewObject()) {
             $parent_xaction->setMetadataValue('blocker.new', true);
           }
 
           id(new ManiphestTransactionEditor())
             ->setActor($this->getActor())
             ->setActingAsPHID($this->getActingAsPHID())
             ->setContentSource($this->getContentSource())
             ->setContinueOnNoEffect(true)
             ->setContinueOnMissingFields(true)
             ->applyTransactions($blocked_task, array($parent_xaction));
         }
       }
     }
 
     return $xactions;
   }
 
   protected function shouldSendMail(
     PhabricatorLiskDAO $object,
     array $xactions) {
     return true;
   }
 
   protected function getMailSubjectPrefix() {
     return PhabricatorEnv::getEnvConfig('metamta.maniphest.subject-prefix');
   }
 
   protected function getMailThreadID(PhabricatorLiskDAO $object) {
     return 'maniphest-task-'.$object->getPHID();
   }
 
   protected function getMailTo(PhabricatorLiskDAO $object) {
     $phids = array();
 
     if ($object->getOwnerPHID()) {
       $phids[] = $object->getOwnerPHID();
     }
     $phids[] = $this->getActingAsPHID();
 
     return $phids;
   }
 
   public function getMailTagsMap() {
     return array(
       ManiphestTransaction::MAILTAG_STATUS =>
         pht("A task's status changes."),
       ManiphestTransaction::MAILTAG_OWNER =>
         pht("A task's owner changes."),
       ManiphestTransaction::MAILTAG_PRIORITY =>
         pht("A task's priority changes."),
       ManiphestTransaction::MAILTAG_CC =>
         pht("A task's subscribers change."),
       ManiphestTransaction::MAILTAG_PROJECTS =>
         pht("A task's associated projects change."),
       ManiphestTransaction::MAILTAG_UNBLOCK =>
         pht('One of the tasks a task is blocked by changes status.'),
       ManiphestTransaction::MAILTAG_COLUMN =>
         pht('A task is moved between columns on a workboard.'),
       ManiphestTransaction::MAILTAG_COMMENT =>
         pht('Someone comments on a task.'),
       ManiphestTransaction::MAILTAG_OTHER =>
         pht('Other task activity not listed above occurs.'),
     );
   }
 
   protected function buildReplyHandler(PhabricatorLiskDAO $object) {
     return id(new ManiphestReplyHandler())
       ->setMailReceiver($object);
   }
 
   protected function buildMailTemplate(PhabricatorLiskDAO $object) {
     $id = $object->getID();
     $title = $object->getTitle();
 
     return id(new PhabricatorMetaMTAMail())
       ->setSubject("T{$id}: {$title}")
       ->addHeader('Thread-Topic', "T{$id}: ".$object->getOriginalTitle());
   }
 
   protected function buildMailBody(
     PhabricatorLiskDAO $object,
     array $xactions) {
 
     $body = parent::buildMailBody($object, $xactions);
 
     if ($this->getIsNewObject()) {
       $body->addRemarkupSection(
         pht('TASK DESCRIPTION'),
         $object->getDescription());
     }
 
     $body->addLinkSection(
       pht('TASK DETAIL'),
       PhabricatorEnv::getProductionURI('/T'.$object->getID()));
 
 
     $board_phids = array();
     $type_columns = PhabricatorTransactions::TYPE_COLUMNS;
     foreach ($xactions as $xaction) {
       if ($xaction->getTransactionType() == $type_columns) {
         $moves = $xaction->getNewValue();
         foreach ($moves as $move) {
           $board_phids[] = $move['boardPHID'];
         }
       }
     }
 
     if ($board_phids) {
       $projects = id(new PhabricatorProjectQuery())
         ->setViewer($this->requireActor())
         ->withPHIDs($board_phids)
         ->execute();
 
       foreach ($projects as $project) {
         $body->addLinkSection(
           pht('WORKBOARD'),
           PhabricatorEnv::getProductionURI(
             '/project/board/'.$project->getID().'/'));
       }
     }
 
 
     return $body;
   }
 
   protected function shouldPublishFeedStory(
     PhabricatorLiskDAO $object,
     array $xactions) {
     return $this->shouldSendMail($object, $xactions);
   }
 
   protected function supportsSearch() {
     return true;
   }
 
   protected function shouldApplyHeraldRules(
     PhabricatorLiskDAO $object,
     array $xactions) {
     return true;
   }
 
   protected function buildHeraldAdapter(
     PhabricatorLiskDAO $object,
     array $xactions) {
 
     return id(new HeraldManiphestTaskAdapter())
       ->setTask($object);
   }
 
   protected function requireCapabilities(
     PhabricatorLiskDAO $object,
     PhabricatorApplicationTransaction $xaction) {
 
     parent::requireCapabilities($object, $xaction);
 
     $app_capability_map = array(
       ManiphestTransaction::TYPE_PRIORITY =>
         ManiphestEditPriorityCapability::CAPABILITY,
       ManiphestTransaction::TYPE_STATUS =>
         ManiphestEditStatusCapability::CAPABILITY,
       ManiphestTransaction::TYPE_OWNER =>
         ManiphestEditAssignCapability::CAPABILITY,
       PhabricatorTransactions::TYPE_EDIT_POLICY =>
         ManiphestEditPoliciesCapability::CAPABILITY,
       PhabricatorTransactions::TYPE_VIEW_POLICY =>
         ManiphestEditPoliciesCapability::CAPABILITY,
     );
 
 
     $transaction_type = $xaction->getTransactionType();
 
     $app_capability = null;
     if ($transaction_type == PhabricatorTransactions::TYPE_EDGE) {
       switch ($xaction->getMetadataValue('edge:type')) {
         case PhabricatorProjectObjectHasProjectEdgeType::EDGECONST:
           $app_capability = ManiphestEditProjectsCapability::CAPABILITY;
           break;
       }
     } else {
       $app_capability = idx($app_capability_map, $transaction_type);
     }
 
     if ($app_capability) {
       $app = id(new PhabricatorApplicationQuery())
         ->setViewer($this->getActor())
         ->withClasses(array('PhabricatorManiphestApplication'))
         ->executeOne();
       PhabricatorPolicyFilter::requireCapability(
         $this->getActor(),
         $app,
         $app_capability);
     }
   }
 
   protected function adjustObjectForPolicyChecks(
     PhabricatorLiskDAO $object,
     array $xactions) {
 
     $copy = parent::adjustObjectForPolicyChecks($object, $xactions);
     foreach ($xactions as $xaction) {
       switch ($xaction->getTransactionType()) {
         case ManiphestTransaction::TYPE_OWNER:
           $copy->setOwnerPHID($xaction->getNewValue());
           break;
         default:
           continue;
       }
     }
 
     return $copy;
   }
 
   /**
    * Get priorities for moving a task to a new priority.
    */
   public static function getEdgeSubpriority(
     $priority,
     $is_end) {
 
     $query = id(new ManiphestTaskQuery())
       ->setViewer(PhabricatorUser::getOmnipotentUser())
       ->withPriorities(array($priority))
       ->setLimit(1);
 
     if ($is_end) {
       $query->setOrderVector(array('-priority', '-subpriority', '-id'));
     } else {
       $query->setOrderVector(array('priority', 'subpriority', 'id'));
     }
 
     $result = $query->executeOne();
     $step = (double)(2 << 32);
 
     if ($result) {
       $base = $result->getSubpriority();
       if ($is_end) {
         $sub = ($base - $step);
       } else {
         $sub = ($base + $step);
       }
     } else {
       $sub = 0;
     }
 
     return array($priority, $sub);
   }
 
 
   /**
    * Get priorities for moving a task before or after another task.
    */
   public static function getAdjacentSubpriority(
     ManiphestTask $dst,
     $is_after,
     $allow_recursion = true) {
 
     $query = id(new ManiphestTaskQuery())
       ->setViewer(PhabricatorUser::getOmnipotentUser())
       ->setOrder(ManiphestTaskQuery::ORDER_PRIORITY)
       ->withPriorities(array($dst->getPriority()))
       ->setLimit(1);
 
     if ($is_after) {
       $query->setAfterID($dst->getID());
     } else {
       $query->setBeforeID($dst->getID());
     }
 
     $adjacent = $query->executeOne();
 
     $base = $dst->getSubpriority();
     $step = (double)(2 << 32);
 
     // If we find an adjacent task, we average the two subpriorities and
     // return the result.
     if ($adjacent) {
       $epsilon = 0.01;
 
       // If the adjacent task has a subpriority that is identical or very
       // close to the task we're looking at, we're going to move it and all
       // tasks with the same subpriority a little farther down the subpriority
       // scale.
       if ($allow_recursion &&
           (abs($adjacent->getSubpriority() - $base) < $epsilon)) {
         $conn_w = $adjacent->establishConnection('w');
 
         $min = ($adjacent->getSubpriority() - ($epsilon));
         $max = ($adjacent->getSubpriority() + ($epsilon));
 
         // Get all of the tasks with the similar subpriorities to the adjacent
         // task, including the adjacent task itself.
         $query = id(new ManiphestTaskQuery())
           ->setViewer(PhabricatorUser::getOmnipotentUser())
           ->withPriorities(array($adjacent->getPriority()))
           ->withSubpriorityBetween($min, $max);
 
         if (!$is_after) {
           $query->setOrderVector(array('-priority', '-subpriority', '-id'));
         } else {
           $query->setOrderVector(array('priority', 'subpriority', 'id'));
         }
 
         $shift_all = $query->execute();
         $shift_last = last($shift_all);
 
         // Select the most extreme subpriority in the result set as the
         // base value.
         $shift_base = head($shift_all)->getSubpriority();
 
         // Find the subpriority before or after the task at the end of the
         // block.
         list($shift_pri, $shift_sub) = self::getAdjacentSubpriority(
           $shift_last,
           $is_after,
           $allow_recursion = false);
 
         $delta = ($shift_sub - $shift_base);
         $count = count($shift_all);
 
         $shift = array();
         $cursor = 1;
         foreach ($shift_all as $shift_task) {
           $shift_target = $shift_base + (($cursor / $count) * $delta);
           $cursor++;
 
           queryfx(
             $conn_w,
             'UPDATE %T SET subpriority = %f WHERE id = %d',
             $adjacent->getTableName(),
             $shift_target,
             $shift_task->getID());
 
           // If we're shifting the adjacent task, update it.
           if ($shift_task->getID() == $adjacent->getID()) {
             $adjacent->setSubpriority($shift_target);
           }
 
           // If we're shifting the original target task, update the base
           // subpriority.
           if ($shift_task->getID() == $dst->getID()) {
             $base = $shift_target;
           }
         }
       }
 
       $sub = ($adjacent->getSubpriority() + $base) / 2;
     } else {
       // Otherwise, we take a step away from the target's subpriority and
       // use that.
       if ($is_after) {
         $sub = ($base - $step);
       } else {
         $sub = ($base + $step);
       }
     }
 
     return array($dst->getPriority(), $sub);
   }
 
   protected function validateTransaction(
     PhabricatorLiskDAO $object,
     $type,
     array $xactions) {
 
     $errors = parent::validateTransaction($object, $type, $xactions);
 
     switch ($type) {
       case ManiphestTransaction::TYPE_TITLE:
         $missing = $this->validateIsEmptyTextField(
           $object->getTitle(),
           $xactions);
 
         if ($missing) {
           $error = new PhabricatorApplicationTransactionValidationError(
             $type,
             pht('Required'),
             pht('Task title is required.'),
             nonempty(last($xactions), null));
 
           $error->setIsMissingFieldError(true);
           $errors[] = $error;
         }
         break;
       case ManiphestTransaction::TYPE_PARENT:
         $with_effect = array();
         foreach ($xactions as $xaction) {
           $task_phid = $xaction->getNewValue();
           if (!$task_phid) {
             continue;
           }
 
           $with_effect[] = $xaction;
 
           $task = id(new ManiphestTaskQuery())
             ->setViewer($this->getActor())
             ->withPHIDs(array($task_phid))
             ->executeOne();
           if (!$task) {
             $errors[] = new PhabricatorApplicationTransactionValidationError(
               $type,
               pht('Invalid'),
               pht(
                 'Parent task identifier "%s" does not identify a visible '.
                 'task.',
                 $task_phid),
               $xaction);
           }
         }
 
         if ($with_effect && !$this->getIsNewObject()) {
           $errors[] = new PhabricatorApplicationTransactionValidationError(
             $type,
             pht('Invalid'),
             pht(
               'You can only select a parent task when creating a '.
               'transaction for the first time.'),
             last($with_effect));
         }
         break;
       case ManiphestTransaction::TYPE_OWNER:
         foreach ($xactions as $xaction) {
           $old = $xaction->getOldValue();
           $new = $xaction->getNewValue();
           if (!strlen($new)) {
             continue;
           }
 
           if ($new === $old) {
             continue;
           }
 
           $assignee_list = id(new PhabricatorPeopleQuery())
             ->setViewer($this->getActor())
             ->withPHIDs(array($new))
             ->execute();
           if (!$assignee_list) {
             $errors[] = new PhabricatorApplicationTransactionValidationError(
               $type,
               pht('Invalid'),
               pht(
                 'User "%s" is not a valid user.',
                 $new),
               $xaction);
           }
         }
         break;
       case ManiphestTransaction::TYPE_COVER_IMAGE:
         foreach ($xactions as $xaction) {
           $old = $xaction->getOldValue();
           $new = $xaction->getNewValue();
           if (!$new) {
             continue;
           }
 
           if ($new === $old) {
             continue;
           }
 
           $file = id(new PhabricatorFileQuery())
             ->setViewer($this->getActor())
             ->withPHIDs(array($new))
             ->executeOne();
           if (!$file) {
             $errors[] = new PhabricatorApplicationTransactionValidationError(
               $type,
               pht('Invalid'),
               pht('File "%s" is not valid.', $new),
               $xaction);
             continue;
           }
 
           if (!$file->isTransformableImage()) {
             $errors[] = new PhabricatorApplicationTransactionValidationError(
               $type,
               pht('Invalid'),
               pht('File "%s" is not a valid image file.', $new),
               $xaction);
             continue;
           }
         }
         break;
 
       case ManiphestTransaction::TYPE_POINTS:
         foreach ($xactions as $xaction) {
           $new = $xaction->getNewValue();
           if (strlen($new) && !is_numeric($new)) {
             $errors[] = new PhabricatorApplicationTransactionValidationError(
               $type,
               pht('Invalid'),
               pht('Points value must be numeric or empty.'),
               $xaction);
             continue;
           }
 
           if ((double)$new < 0) {
             $errors[] = new PhabricatorApplicationTransactionValidationError(
               $type,
               pht('Invalid'),
               pht('Points value must be nonnegative.'),
               $xaction);
             continue;
           }
         }
         break;
 
     }
 
     return $errors;
   }
 
   protected function validateAllTransactions(
     PhabricatorLiskDAO $object,
     array $xactions) {
 
     $errors = parent::validateAllTransactions($object, $xactions);
 
     if ($this->moreValidationErrors) {
       $errors = array_merge($errors, $this->moreValidationErrors);
     }
 
     return $errors;
   }
 
   protected function expandTransactions(
     PhabricatorLiskDAO $object,
     array $xactions) {
 
     $actor = $this->getActor();
     $actor_phid = $actor->getPHID();
 
     $results = parent::expandTransactions($object, $xactions);
 
     $is_unassigned = ($object->getOwnerPHID() === null);
 
     $any_assign = false;
     foreach ($xactions as $xaction) {
       if ($xaction->getTransactionType() == ManiphestTransaction::TYPE_OWNER) {
         $any_assign = true;
         break;
       }
     }
 
     $is_open = !$object->isClosed();
 
     $new_status = null;
     foreach ($xactions as $xaction) {
       switch ($xaction->getTransactionType()) {
         case ManiphestTransaction::TYPE_STATUS:
           $new_status = $xaction->getNewValue();
           break;
       }
     }
 
     if ($new_status === null) {
       $is_closing = false;
     } else {
       $is_closing = ManiphestTaskStatus::isClosedStatus($new_status);
     }
 
     // If the task is not assigned, not being assigned, currently open, and
     // being closed, try to assign the actor as the owner.
     if ($is_unassigned && !$any_assign && $is_open && $is_closing) {
       $is_claim = ManiphestTaskStatus::isClaimStatus($new_status);
 
       // Don't assign the actor if they aren't a real user.
       // Don't claim the task if the status is configured to not claim.
       if ($actor_phid && $is_claim) {
         $results[] = id(new ManiphestTransaction())
           ->setTransactionType(ManiphestTransaction::TYPE_OWNER)
           ->setNewValue($actor_phid);
       }
     }
 
     // Automatically subscribe the author when they create a task.
     if ($this->getIsNewObject()) {
       if ($actor_phid) {
         $results[] = id(new ManiphestTransaction())
           ->setTransactionType(PhabricatorTransactions::TYPE_SUBSCRIBERS)
           ->setNewValue(
             array(
               '+' => array($actor_phid => $actor_phid),
             ));
       }
     }
 
     return $results;
   }
 
   protected function expandTransaction(
     PhabricatorLiskDAO $object,
     PhabricatorApplicationTransaction $xaction) {
 
     $results = parent::expandTransaction($object, $xaction);
 
     $type = $xaction->getTransactionType();
     switch ($type) {
       case PhabricatorTransactions::TYPE_COLUMNS:
         try {
-          $this->buildMoveTransaction($object, $xaction);
-
-          // Implicilty add the task to any boards that we're moving it
-          // on, since moves on a board the task isn't part of are not
-          // meaningful.
-          $board_phids = ipull($xaction->getNewValue(), 'boardPHID');
-          if ($board_phids) {
-            $results[] = id(new ManiphestTransaction())
-              ->setTransactionType(PhabricatorTransactions::TYPE_EDGE)
-              ->setMetadataValue(
-                'edge:type',
-                PhabricatorProjectObjectHasProjectEdgeType::EDGECONST)
-              ->setIgnoreOnNoEffect(true)
-              ->setNewValue(
-                array(
-                  '+' => array_fuse($board_phids),
-                ));
+          $more_xactions = $this->buildMoveTransaction($object, $xaction);
+          foreach ($more_xactions as $more_xaction) {
+            $results[] = $more_xaction;
           }
         } catch (Exception $ex) {
           $error = new PhabricatorApplicationTransactionValidationError(
             $type,
             pht('Invalid'),
             $ex->getMessage(),
             $xaction);
           $this->moreValidationErrors[] = $error;
         }
         break;
       case ManiphestTransaction::TYPE_OWNER:
         // If this is a no-op update, don't expand it.
         $old_value = $object->getOwnerPHID();
         $new_value = $xaction->getNewValue();
         if ($old_value === $new_value) {
           continue;
         }
 
         // When a task is reassigned, move the old owner to the subscriber
         // list so they're still in the loop.
         if ($old_value) {
           $results[] = id(new ManiphestTransaction())
             ->setTransactionType(PhabricatorTransactions::TYPE_SUBSCRIBERS)
             ->setIgnoreOnNoEffect(true)
             ->setNewValue(
               array(
                 '+' => array($old_value => $old_value),
               ));
         }
         break;
     }
 
     return $results;
   }
 
   protected function extractFilePHIDsFromCustomTransaction(
     PhabricatorLiskDAO $object,
     PhabricatorApplicationTransaction $xaction) {
     $phids = parent::extractFilePHIDsFromCustomTransaction($object, $xaction);
 
     switch ($xaction->getTransactionType()) {
       case ManiphestTransaction::TYPE_COVER_IMAGE:
         $phids[] = $xaction->getNewValue();
         break;
     }
 
     return $phids;
   }
 
   private function buildMoveTransaction(
     PhabricatorLiskDAO $object,
     PhabricatorApplicationTransaction $xaction) {
 
     $new = $xaction->getNewValue();
     if (!is_array($new)) {
       $this->validateColumnPHID($new);
       $new = array($new);
     }
 
     $nearby_phids = array();
     foreach ($new as $key => $value) {
       if (!is_array($value)) {
         $this->validateColumnPHID($value);
         $value = array(
           'columnPHID' => $value,
         );
       }
 
       PhutilTypeSpec::checkMap(
         $value,
         array(
           'columnPHID' => 'string',
           'beforePHID' => 'optional string',
           'afterPHID' => 'optional string',
         ));
 
       $new[$key] = $value;
 
       if (!empty($value['beforePHID'])) {
         $nearby_phids[] = $value['beforePHID'];
       }
 
       if (!empty($value['afterPHID'])) {
         $nearby_phids[] = $value['afterPHID'];
       }
     }
 
     if ($nearby_phids) {
       $nearby_objects = id(new PhabricatorObjectQuery())
         ->setViewer($this->getActor())
         ->withPHIDs($nearby_phids)
         ->execute();
       $nearby_objects = mpull($nearby_objects, null, 'getPHID');
     } else {
       $nearby_objects = array();
     }
 
     $column_phids = ipull($new, 'columnPHID');
     if ($column_phids) {
       $columns = id(new PhabricatorProjectColumnQuery())
         ->setViewer($this->getActor())
         ->withPHIDs($column_phids)
         ->execute();
       $columns = mpull($columns, null, 'getPHID');
     } else {
       $columns = array();
     }
 
     $board_phids = mpull($columns, 'getProjectPHID');
     $object_phid = $object->getPHID();
 
     $object_phids = $nearby_phids;
 
     // Note that we may not have an object PHID if we're creating a new
     // object.
     if ($object_phid) {
       $object_phids[] = $object_phid;
     }
 
     if ($object_phids) {
       $layout_engine = id(new PhabricatorBoardLayoutEngine())
         ->setViewer($this->getActor())
         ->setBoardPHIDs($board_phids)
         ->setObjectPHIDs($object_phids)
         ->setFetchAllBoards(true)
         ->executeLayout();
     }
 
     foreach ($new as $key => $spec) {
       $column_phid = $spec['columnPHID'];
       $column = idx($columns, $column_phid);
       if (!$column) {
         throw new Exception(
           pht(
             'Column move transaction specifies column PHID "%s", but there '.
             'is no corresponding column with this PHID.',
             $column_phid));
       }
 
       $board_phid = $column->getProjectPHID();
 
       $nearby = array();
 
       if (!empty($spec['beforePHID'])) {
         $nearby['beforePHID'] = $spec['beforePHID'];
       }
 
       if (!empty($spec['afterPHID'])) {
         $nearby['afterPHID'] = $spec['afterPHID'];
       }
 
       if (count($nearby) > 1) {
         throw new Exception(
           pht(
             'Column move transaction moves object to multiple positions. '.
             'Specify only "beforePHID" or "afterPHID", not both.'));
       }
 
       foreach ($nearby as $where => $nearby_phid) {
         if (empty($nearby_objects[$nearby_phid])) {
           throw new Exception(
             pht(
               'Column move transaction specifies object "%s" as "%s", but '.
               'there is no corresponding object with this PHID.',
               $object_phid,
               $where));
         }
 
         $nearby_columns = $layout_engine->getObjectColumns(
           $board_phid,
           $nearby_phid);
         $nearby_columns = mpull($nearby_columns, null, 'getPHID');
 
         if (empty($nearby_columns[$column_phid])) {
           throw new Exception(
             pht(
               'Column move transaction specifies object "%s" as "%s" in '.
               'column "%s", but this object is not in that column!',
               $nearby_phid,
               $where,
               $column_phid));
         }
       }
 
       if ($object_phid) {
         $old_columns = $layout_engine->getObjectColumns(
           $board_phid,
           $object_phid);
         $old_column_phids = mpull($old_columns, 'getPHID');
       } else {
         $old_column_phids = array();
       }
 
       $spec += array(
         'boardPHID' => $board_phid,
         'fromColumnPHIDs' => $old_column_phids,
       );
 
       // Check if the object is already in this column, and isn't being moved.
       // We can just drop this column change if it has no effect.
       $from_map = array_fuse($spec['fromColumnPHIDs']);
       $already_here = isset($from_map[$column_phid]);
       $is_reordering = (bool)$nearby;
 
       if ($already_here && !$is_reordering) {
         unset($new[$key]);
       } else {
         $new[$key] = $spec;
       }
     }
 
     $new = array_values($new);
     $xaction->setNewValue($new);
+
+
+    $more = array();
+
+    // If we're moving the object into a column and it does not already belong
+    // in the column, add the appropriate board. For normal columns, this
+    // is the board PHID. For proxy columns, it is the proxy PHID, unless the
+    // object is already a member of some descendant of the proxy PHID.
+
+    // The major case where this can happen is moves via the API, but it also
+    // happens when a user drags a task from the "Backlog" to a milestone
+    // column.
+
+    if ($object_phid) {
+      $current_phids = PhabricatorEdgeQuery::loadDestinationPHIDs(
+        $object_phid,
+        PhabricatorProjectObjectHasProjectEdgeType::EDGECONST);
+      $current_phids = array_fuse($current_phids);
+    } else {
+      $current_phids = array();
+    }
+
+    $add_boards = array();
+    foreach ($new as $move) {
+      $column_phid = $move['columnPHID'];
+      $board_phid = $move['boardPHID'];
+      $column = $columns[$column_phid];
+      $proxy_phid = $column->getProxyPHID();
+
+      // If this is a normal column, add the board if the object isn't already
+      // associated.
+      if (!$proxy_phid) {
+        if (!isset($current_phids[$board_phid])) {
+          $add_boards[] = $board_phid;
+        }
+        continue;
+      }
+
+      // If this is a proxy column but the object is already associated with
+      // the proxy board, we don't need to do anything.
+      if (isset($current_phids[$proxy_phid])) {
+        continue;
+      }
+
+      // If this a proxy column and the object is already associated with some
+      // descendant of the proxy board, we also don't need to do anything.
+      $descendants = id(new PhabricatorProjectQuery())
+        ->setViewer(PhabricatorUser::getOmnipotentUser())
+        ->withAncestorProjectPHIDs(array($proxy_phid))
+        ->execute();
+
+      $found_descendant = false;
+      foreach ($descendants as $descendant) {
+        if (isset($current_phids[$descendant->getPHID()])) {
+          $found_descendant = true;
+          break;
+        }
+      }
+
+      if ($found_descendant) {
+        continue;
+      }
+
+      // Otherwise, we're moving the object to a proxy column which it is not
+      // a member of yet, so add an association to the column's proxy board.
+
+      $add_boards[] = $proxy_phid;
+    }
+
+    if ($add_boards) {
+      $more[] = id(new ManiphestTransaction())
+        ->setTransactionType(PhabricatorTransactions::TYPE_EDGE)
+        ->setMetadataValue(
+          'edge:type',
+          PhabricatorProjectObjectHasProjectEdgeType::EDGECONST)
+        ->setIgnoreOnNoEffect(true)
+        ->setNewValue(
+          array(
+            '+' => array_fuse($add_boards),
+          ));
+    }
+
+    return $more;
   }
 
   private function applyBoardMove($object, array $move) {
     $board_phid = $move['boardPHID'];
     $column_phid = $move['columnPHID'];
     $before_phid = idx($move, 'beforePHID');
     $after_phid = idx($move, 'afterPHID');
 
     $object_phid = $object->getPHID();
 
     // We're doing layout with the ominpotent viewer to make sure we don't
     // remove positions in columns that exist, but which the actual actor
     // can't see.
     $omnipotent_viewer = PhabricatorUser::getOmnipotentUser();
 
     $select_phids = array($board_phid);
 
     $descendants = id(new PhabricatorProjectQuery())
       ->setViewer($omnipotent_viewer)
       ->withAncestorProjectPHIDs($select_phids)
       ->execute();
     foreach ($descendants as $descendant) {
       $select_phids[] = $descendant->getPHID();
     }
 
     $board_tasks = id(new ManiphestTaskQuery())
       ->setViewer($omnipotent_viewer)
       ->withEdgeLogicPHIDs(
         PhabricatorProjectObjectHasProjectEdgeType::EDGECONST,
         PhabricatorQueryConstraint::OPERATOR_ANCESTOR,
         array($select_phids))
       ->execute();
 
     $board_tasks = mpull($board_tasks, null, 'getPHID');
     $board_tasks[$object_phid] = $object;
 
     // Make sure tasks are sorted by ID, so we lay out new positions in
     // a consistent way.
     $board_tasks = msort($board_tasks, 'getID');
 
     $object_phids = array_keys($board_tasks);
 
     $engine = id(new PhabricatorBoardLayoutEngine())
       ->setViewer($omnipotent_viewer)
       ->setBoardPHIDs(array($board_phid))
       ->setObjectPHIDs($object_phids)
       ->executeLayout();
 
     // TODO: This logic needs to be revised when we legitimately support
     // multiple column positions.
     $columns = $engine->getObjectColumns($board_phid, $object_phid);
     foreach ($columns as $column) {
       $engine->queueRemovePosition(
         $board_phid,
         $column->getPHID(),
         $object_phid);
     }
 
     if ($before_phid) {
       $engine->queueAddPositionBefore(
         $board_phid,
         $column_phid,
         $object_phid,
         $before_phid);
     } else if ($after_phid) {
       $engine->queueAddPositionAfter(
         $board_phid,
         $column_phid,
         $object_phid,
         $after_phid);
     } else {
       $engine->queueAddPosition(
         $board_phid,
         $column_phid,
         $object_phid);
     }
 
     $engine->applyPositionUpdates();
   }
 
 
   private function validateColumnPHID($value) {
     if (phid_get_type($value) == PhabricatorProjectColumnPHIDType::TYPECONST) {
       return;
     }
 
     throw new Exception(
       pht(
         'When moving objects between columns on a board, columns must '.
         'be identified by PHIDs. This transaction uses "%s" to identify '.
         'a column, but that is not a valid column PHID.',
         $value));
   }
 
 
 
 }
diff --git a/src/applications/metamta/view/PhabricatorMetaMTAMailBody.php b/src/applications/metamta/view/PhabricatorMetaMTAMailBody.php
index 743082f47..8a21a75cf 100644
--- a/src/applications/metamta/view/PhabricatorMetaMTAMailBody.php
+++ b/src/applications/metamta/view/PhabricatorMetaMTAMailBody.php
@@ -1,215 +1,218 @@
 <?php
 
 /**
  * Render the body of an application email by building it up section-by-section.
  *
  * @task compose  Composition
  * @task render   Rendering
  */
 final class PhabricatorMetaMTAMailBody extends Phobject {
 
   private $sections = array();
   private $htmlSections = array();
   private $attachments = array();
 
   private $viewer;
 
   public function getViewer() {
     return $this->viewer;
   }
 
   public function setViewer($viewer) {
     $this->viewer = $viewer;
     return $this;
   }
 
 /* -(  Composition  )-------------------------------------------------------- */
 
 
   /**
    * Add a raw block of text to the email. This will be rendered as-is.
    *
    * @param string Block of text.
    * @return this
    * @task compose
    */
   public function addRawSection($text) {
     if (strlen($text)) {
       $text = rtrim($text);
       $this->sections[] = $text;
       $this->htmlSections[] = phutil_escape_html_newlines(
         phutil_tag('div', array(), $text));
     }
     return $this;
   }
 
   public function addRemarkupSection($header, $text) {
     try {
       $engine = PhabricatorMarkupEngine::newMarkupEngine(array());
       $engine->setConfig('viewer', $this->getViewer());
       $engine->setMode(PhutilRemarkupEngine::MODE_TEXT);
       $styled_text = $engine->markupText($text);
       $this->addPlaintextSection($header, $styled_text);
     } catch (Exception $ex) {
       phlog($ex);
       $this->addTextSection($header, $text);
     }
 
     try {
       $mail_engine = PhabricatorMarkupEngine::newMarkupEngine(array());
       $mail_engine->setConfig('viewer', $this->getViewer());
       $mail_engine->setMode(PhutilRemarkupEngine::MODE_HTML_MAIL);
       $mail_engine->setConfig(
         'uri.base',
         PhabricatorEnv::getProductionURI('/'));
       $html = $mail_engine->markupText($text);
       $this->addHTMLSection($header, $html);
     } catch (Exception $ex) {
       phlog($ex);
       $this->addHTMLSection($header, $text);
     }
 
     return $this;
   }
 
   public function addRawPlaintextSection($text) {
     if (strlen($text)) {
       $text = rtrim($text);
       $this->sections[] = $text;
     }
     return $this;
   }
 
   public function addRawHTMLSection($html) {
     $this->htmlSections[] = phutil_safe_html($html);
     return $this;
   }
 
 
   /**
    * Add a block of text with a section header. This is rendered like this:
    *
    *    HEADER
    *      Text is indented.
    *
    * @param string Header text.
    * @param string Section text.
    * @return this
    * @task compose
    */
   public function addTextSection($header, $section) {
     if ($section instanceof PhabricatorMetaMTAMailSection) {
       $plaintext = $section->getPlaintext();
       $html = $section->getHTML();
     } else {
       $plaintext = $section;
       $html = phutil_escape_html_newlines(phutil_tag('div', array(), $section));
     }
 
     $this->addPlaintextSection($header, $plaintext);
     $this->addHTMLSection($header, $html);
     return $this;
   }
 
-  public function addPlaintextSection($header, $text) {
-    $this->sections[] = $header."\n".$this->indent($text);
+  public function addPlaintextSection($header, $text, $indent = true) {
+    if ($indent) {
+      $text = $this->indent($text);
+    }
+    $this->sections[] = $header."\n".$text;
     return $this;
   }
 
   public function addHTMLSection($header, $html_fragment) {
     if ($header !== null) {
       $header = phutil_tag('strong', array(), $header);
     }
 
     $this->htmlSections[] = array(
       phutil_tag(
         'div',
         array(),
         array(
           $header,
           phutil_tag('div', array(), $html_fragment),
         )),
     );
     return $this;
   }
 
   public function addLinkSection($header, $link) {
     $html = phutil_tag('a', array('href' => $link), $link);
     $this->addPlaintextSection($header, $link);
     $this->addHTMLSection($header, $html);
     return $this;
   }
 
   /**
    * Add a Herald section with a rule management URI and a transcript URI.
    *
    * @param string URI to rule transcripts.
    * @return this
    * @task compose
    */
   public function addHeraldSection($xscript_uri) {
     if (!PhabricatorEnv::getEnvConfig('metamta.herald.show-hints')) {
       return $this;
     }
 
     $this->addLinkSection(
       pht('WHY DID I GET THIS EMAIL?'),
       PhabricatorEnv::getProductionURI($xscript_uri));
 
     return $this;
   }
 
   /**
    * Add an attachment.
    *
    * @param PhabricatorMetaMTAAttachment Attachment.
    * @return this
    * @task compose
    */
   public function addAttachment(PhabricatorMetaMTAAttachment $attachment) {
     $this->attachments[] = $attachment;
     return $this;
   }
 
 
 /* -(  Rendering  )---------------------------------------------------------- */
 
 
   /**
    * Render the email body.
    *
    * @return string Rendered body.
    * @task render
    */
   public function render() {
     return implode("\n\n", $this->sections)."\n";
   }
 
   public function renderHTML() {
     $br = phutil_tag('br');
     $body = phutil_implode_html($br, $this->htmlSections);
     return (string)hsprintf('%s', array($body, $br));
   }
 
   /**
    * Retrieve attachments.
    *
    * @return list<PhabricatorMetaMTAAttachment> Attachments.
    * @task render
    */
   public function getAttachments() {
     return $this->attachments;
   }
 
 
   /**
    * Indent a block of text for rendering under a section heading.
    *
    * @param string Text to indent.
    * @return string Indented text.
    * @task render
    */
   private function indent($text) {
     return rtrim("  ".str_replace("\n", "\n  ", $text));
   }
 }
diff --git a/src/applications/owners/controller/PhabricatorOwnersPathsController.php b/src/applications/owners/controller/PhabricatorOwnersPathsController.php
index ad7db5cc6..55aeb11b6 100644
--- a/src/applications/owners/controller/PhabricatorOwnersPathsController.php
+++ b/src/applications/owners/controller/PhabricatorOwnersPathsController.php
@@ -1,171 +1,171 @@
 <?php
 
 final class PhabricatorOwnersPathsController
   extends PhabricatorOwnersController {
 
   public function handleRequest(AphrontRequest $request) {
     $viewer = $request->getUser();
 
     $package = id(new PhabricatorOwnersPackageQuery())
       ->setViewer($viewer)
       ->withIDs(array($request->getURIData('id')))
       ->requireCapabilities(
         array(
           PhabricatorPolicyCapability::CAN_VIEW,
           PhabricatorPolicyCapability::CAN_EDIT,
         ))
       ->needPaths(true)
       ->executeOne();
     if (!$package) {
       return new Aphront404Response();
     }
 
     if ($request->isFormPost()) {
       $paths = $request->getArr('path');
       $repos = $request->getArr('repo');
       $excludes = $request->getArr('exclude');
 
       $path_refs = array();
       foreach ($paths as $key => $path) {
         if (!isset($repos[$key])) {
           throw new Exception(
             pht(
               'No repository PHID for path "%s"!',
               $key));
         }
 
         if (!isset($excludes[$key])) {
           throw new Exception(
             pht(
               'No exclusion value for path "%s"!',
               $key));
         }
 
         $path_refs[] = array(
           'repositoryPHID' => $repos[$key],
           'path' => $path,
           'excluded' => (int)$excludes[$key],
         );
       }
 
       $type_paths = PhabricatorOwnersPackageTransaction::TYPE_PATHS;
 
       $xactions = array();
       $xactions[] = id(new PhabricatorOwnersPackageTransaction())
         ->setTransactionType($type_paths)
         ->setNewValue($path_refs);
 
       $editor = id(new PhabricatorOwnersPackageTransactionEditor())
         ->setActor($viewer)
         ->setContentSourceFromRequest($request)
         ->setContinueOnNoEffect(true)
         ->setContinueOnMissingFields(true);
 
       $editor->applyTransactions($package, $xactions);
 
       return id(new AphrontRedirectResponse())
         ->setURI('/owners/package/'.$package->getID().'/');
     } else {
       $paths = $package->getPaths();
       $path_refs = mpull($paths, 'getRef');
     }
 
     $repos = id(new PhabricatorRepositoryQuery())
       ->setViewer($viewer)
       ->execute();
 
     $default_paths = array();
     foreach ($repos as $repo) {
       $default_path = $repo->getDetail('default-owners-path');
       if ($default_path) {
         $default_paths[$repo->getPHID()] = $default_path;
       }
     }
 
-    $repos = mpull($repos, 'getMonogram', 'getPHID');
+    $repos = mpull($repos, 'getDisplayName', 'getPHID');
     asort($repos);
 
     $template = new AphrontTypeaheadTemplateView();
     $template = $template->render();
 
     Javelin::initBehavior(
       'owners-path-editor',
       array(
         'root'                => 'path-editor',
         'table'               => 'paths',
         'add_button'          => 'addpath',
         'repositories'        => $repos,
         'input_template'      => $template,
         'pathRefs'            => $path_refs,
 
         'completeURI'         => '/diffusion/services/path/complete/',
         'validateURI'         => '/diffusion/services/path/validate/',
 
         'repositoryDefaultPaths' => $default_paths,
       ));
 
     require_celerity_resource('owners-path-editor-css');
 
     $cancel_uri = '/owners/package/'.$package->getID().'/';
 
     $form = id(new AphrontFormView())
       ->setUser($viewer)
       ->appendChild(
         id(new PHUIFormInsetView())
           ->setTitle(pht('Paths'))
           ->addDivAttributes(array('id' => 'path-editor'))
           ->setRightButton(javelin_tag(
               'a',
               array(
                 'href' => '#',
                 'class' => 'button green',
                 'sigil' => 'addpath',
                 'mustcapture' => true,
               ),
               pht('Add New Path')))
           ->setDescription(
             pht(
               'Specify the files and directories which comprise '.
               'this package.'))
           ->setContent(javelin_tag(
               'table',
               array(
                 'class' => 'owners-path-editor-table',
                 'sigil' => 'paths',
               ),
               '')))
       ->appendChild(
         id(new AphrontFormSubmitControl())
           ->addCancelButton($cancel_uri)
           ->setValue(pht('Save Paths')));
 
     $box = id(new PHUIObjectBoxView())
       ->setHeaderText(pht('Paths'))
       ->setBackground(PHUIObjectBoxView::BLUE_PROPERTY)
       ->setForm($form);
 
     $crumbs = $this->buildApplicationCrumbs();
     $crumbs->addTextCrumb(
       $package->getName(),
       $this->getApplicationURI('package/'.$package->getID().'/'));
     $crumbs->addTextCrumb(pht('Edit Paths'));
     $crumbs->setBorder(true);
 
     $header = id(new PHUIHeaderView())
       ->setHeader(pht('Edit Paths: %s', $package->getName()))
       ->setHeaderIcon('fa-pencil');
 
     $view = id(new PHUITwoColumnView())
       ->setHeader($header)
       ->setFooter($box);
 
     $title = array($package->getName(), pht('Edit Paths'));
 
     return $this->newPage()
       ->setTitle($title)
       ->setCrumbs($crumbs)
       ->appendChild($view);
 
       }
 
 }
diff --git a/src/applications/passphrase/view/PassphraseCredentialControl.php b/src/applications/passphrase/view/PassphraseCredentialControl.php
index 4e1493d45..015dce1f4 100644
--- a/src/applications/passphrase/view/PassphraseCredentialControl.php
+++ b/src/applications/passphrase/view/PassphraseCredentialControl.php
@@ -1,160 +1,200 @@
 <?php
 
 final class PassphraseCredentialControl extends AphrontFormControl {
 
   private $options = array();
   private $credentialType;
   private $defaultUsername;
   private $allowNull;
 
   public function setAllowNull($allow_null) {
     $this->allowNull = $allow_null;
     return $this;
   }
 
   public function setDefaultUsername($default_username) {
     $this->defaultUsername = $default_username;
     return $this;
   }
 
   public function setCredentialType($credential_type) {
     $this->credentialType = $credential_type;
     return $this;
   }
 
   public function getCredentialType() {
     return $this->credentialType;
   }
 
   public function setOptions(array $options) {
     assert_instances_of($options, 'PassphraseCredential');
     $this->options = $options;
     return $this;
   }
 
   protected function getCustomControlClass() {
     return 'passphrase-credential-control';
   }
 
   protected function renderInput() {
 
     $options_map = array();
     foreach ($this->options as $option) {
       $options_map[$option->getPHID()] = pht(
         '%s %s',
-        'K'.$option->getID(),
+        $option->getMonogram(),
         $option->getName());
     }
 
+    // The user editing the form may not have permission to see the current
+    // credential. Populate it into the menu to allow them to save the form
+    // without making any changes.
+    $current_phid = $this->getValue();
+    if (strlen($current_phid) && empty($options_map[$current_phid])) {
+      $viewer = $this->getViewer();
+
+      $user_credential = id(new PassphraseCredentialQuery())
+        ->setViewer($viewer)
+        ->withPHIDs(array($current_phid))
+        ->executeOne();
+      if (!$user_credential) {
+        // Pull the credential with the ominipotent viewer so we can look up
+        // the ID and tell if it's restricted or invalid.
+        $omnipotent_credential = id(new PassphraseCredentialQuery())
+          ->setViewer(PhabricatorUser::getOmnipotentUser())
+          ->withPHIDs(array($current_phid))
+          ->executeOne();
+        if ($omnipotent_credential) {
+          $current_name = pht(
+            '%s (Restricted Credential)',
+            $omnipotent_credential->getMonogram());
+        } else {
+          $current_name = pht(
+            'Invalid Credential ("%s")',
+            $current_phid);
+        }
+      } else {
+        $current_name = pht(
+          '%s %s',
+          $user_credential->getMonogram(),
+          $user_credential->getName());
+      }
+
+      $options_map = array(
+        $current_phid => $current_name,
+      ) + $options_map;
+    }
+
+
     $disabled = $this->getDisabled();
     if ($this->allowNull) {
       $options_map = array('' => pht('(No Credentials)')) + $options_map;
     } else {
       if (!$options_map) {
         $options_map[''] = pht('(No Existing Credentials)');
         $disabled = true;
       }
     }
 
     Javelin::initBehavior('passphrase-credential-control');
 
     $options = AphrontFormSelectControl::renderSelectTag(
       $this->getValue(),
       $options_map,
       array(
         'id' => $this->getControlID(),
         'name' => $this->getName(),
         'disabled' => $disabled ? 'disabled' : null,
         'sigil' => 'passphrase-credential-select',
       ));
 
     if ($this->credentialType) {
       $button = javelin_tag(
         'a',
         array(
           'href' => '#',
           'class' => 'button grey',
           'sigil' => 'passphrase-credential-add',
           'mustcapture' => true,
         ),
         pht('Add Credential'));
     } else {
       $button = null;
     }
 
     return javelin_tag(
       'div',
       array(
         'sigil' => 'passphrase-credential-control',
         'meta' => array(
           'type' => $this->getCredentialType(),
           'username' => $this->defaultUsername,
           'allowNull' => $this->allowNull,
         ),
       ),
       array(
         $options,
         $button,
       ));
   }
 
   /**
    * Verify that a given actor has permission to use all of the credentials
    * in a list of credential transactions.
    *
    * In general, the rule here is:
    *
    *   - If you're editing an object and it uses a credential you can't use,
    *     that's fine as long as you don't change the credential.
    *   - If you do change the credential, the new credential must be one you
    *     can use.
    *
    * @param PhabricatorUser The acting user.
    * @param list<PhabricatorApplicationTransaction> List of credential altering
    *        transactions.
    * @return bool True if the transactions are valid.
    */
   public static function validateTransactions(
     PhabricatorUser $actor,
     array $xactions) {
 
     $new_phids = array();
     foreach ($xactions as $xaction) {
       $new = $xaction->getNewValue();
       if (!$new) {
         // Removing a credential, so this is OK.
         continue;
       }
 
       $old = $xaction->getOldValue();
       if ($old == $new) {
         // This is a no-op transaction, so this is also OK.
         continue;
       }
 
       // Otherwise, we need to check this credential.
       $new_phids[] = $new;
     }
 
     if (!$new_phids) {
       // No new credentials being set, so this is fine.
       return true;
     }
 
     $usable_credentials = id(new PassphraseCredentialQuery())
       ->setViewer($actor)
       ->withPHIDs($new_phids)
       ->execute();
     $usable_credentials = mpull($usable_credentials, null, 'getPHID');
 
     foreach ($new_phids as $phid) {
       if (empty($usable_credentials[$phid])) {
         return false;
       }
     }
 
     return true;
   }
 
 
 }
diff --git a/src/applications/project/__tests__/PhabricatorProjectCoreTestCase.php b/src/applications/project/__tests__/PhabricatorProjectCoreTestCase.php
index 5ead89603..0c3f51ef6 100644
--- a/src/applications/project/__tests__/PhabricatorProjectCoreTestCase.php
+++ b/src/applications/project/__tests__/PhabricatorProjectCoreTestCase.php
@@ -1,1468 +1,1501 @@
 <?php
 
 final class PhabricatorProjectCoreTestCase extends PhabricatorTestCase {
 
   protected function getPhabricatorTestCaseConfiguration() {
     return array(
       self::PHABRICATOR_TESTCONFIG_BUILD_STORAGE_FIXTURES => true,
     );
   }
 
   public function testViewProject() {
     $user = $this->createUser();
     $user->save();
 
     $user2 = $this->createUser();
     $user2->save();
 
     $proj = $this->createProject($user);
 
     $proj = $this->refreshProject($proj, $user, true);
 
     $this->joinProject($proj, $user);
     $proj->setViewPolicy(PhabricatorPolicies::POLICY_USER);
     $proj->save();
 
     $can_view = PhabricatorPolicyCapability::CAN_VIEW;
 
     // When the view policy is set to "users", any user can see the project.
     $this->assertTrue((bool)$this->refreshProject($proj, $user));
     $this->assertTrue((bool)$this->refreshProject($proj, $user2));
 
 
     // When the view policy is set to "no one", members can still see the
     // project.
     $proj->setViewPolicy(PhabricatorPolicies::POLICY_NOONE);
     $proj->save();
 
     $this->assertTrue((bool)$this->refreshProject($proj, $user));
     $this->assertFalse((bool)$this->refreshProject($proj, $user2));
   }
 
   public function testIsViewerMemberOrWatcher() {
     $user1 = $this->createUser()
       ->save();
 
     $user2 = $this->createUser()
       ->save();
 
     $user3 = $this->createUser()
       ->save();
 
     $proj1 = $this->createProject($user1);
     $proj1 = $this->refreshProject($proj1, $user1);
 
     $this->joinProject($proj1, $user1);
     $this->joinProject($proj1, $user3);
     $this->watchProject($proj1, $user3);
 
     $proj1 = $this->refreshProject($proj1, $user1);
 
     $this->assertTrue($proj1->isUserMember($user1->getPHID()));
 
     $proj1 = $this->refreshProject($proj1, $user1, false, true);
 
     $this->assertTrue($proj1->isUserMember($user1->getPHID()));
     $this->assertFalse($proj1->isUserWatcher($user1->getPHID()));
 
     $proj1 = $this->refreshProject($proj1, $user1, true, false);
 
     $this->assertTrue($proj1->isUserMember($user1->getPHID()));
     $this->assertFalse($proj1->isUserMember($user2->getPHID()));
     $this->assertTrue($proj1->isUserMember($user3->getPHID()));
 
     $proj1 = $this->refreshProject($proj1, $user1, true, true);
 
     $this->assertTrue($proj1->isUserMember($user1->getPHID()));
     $this->assertFalse($proj1->isUserMember($user2->getPHID()));
     $this->assertTrue($proj1->isUserMember($user3->getPHID()));
 
     $this->assertFalse($proj1->isUserWatcher($user1->getPHID()));
     $this->assertFalse($proj1->isUserWatcher($user2->getPHID()));
     $this->assertTrue($proj1->isUserWatcher($user3->getPHID()));
   }
 
   public function testEditProject() {
     $user = $this->createUser();
     $user->save();
 
     $user2 = $this->createUser();
     $user2->save();
 
     $proj = $this->createProject($user);
 
 
     // When edit and view policies are set to "user", anyone can edit.
     $proj->setViewPolicy(PhabricatorPolicies::POLICY_USER);
     $proj->setEditPolicy(PhabricatorPolicies::POLICY_USER);
     $proj->save();
 
     $this->assertTrue($this->attemptProjectEdit($proj, $user));
 
 
     // When edit policy is set to "no one", no one can edit.
     $proj->setEditPolicy(PhabricatorPolicies::POLICY_NOONE);
     $proj->save();
 
     $caught = null;
     try {
       $this->attemptProjectEdit($proj, $user);
     } catch (Exception $ex) {
       $caught = $ex;
     }
     $this->assertTrue($caught instanceof Exception);
   }
 
   public function testAncestorMembers() {
     $user1 = $this->createUser();
     $user1->save();
 
     $user2 = $this->createUser();
     $user2->save();
 
     $parent = $this->createProject($user1);
     $child = $this->createProject($user1, $parent);
 
     $this->joinProject($child, $user1);
     $this->joinProject($child, $user2);
 
     $project = id(new PhabricatorProjectQuery())
       ->setViewer($user1)
       ->withPHIDs(array($child->getPHID()))
       ->needAncestorMembers(true)
       ->executeOne();
 
     $members = array_fuse($project->getParentProject()->getMemberPHIDs());
     ksort($members);
 
     $expect = array_fuse(
       array(
         $user1->getPHID(),
         $user2->getPHID(),
       ));
     ksort($expect);
 
     $this->assertEqual($expect, $members);
   }
 
   public function testAncestryQueries() {
     $user = $this->createUser();
     $user->save();
 
     $ancestor = $this->createProject($user);
     $parent = $this->createProject($user, $ancestor);
     $child = $this->createProject($user, $parent);
 
     $projects = id(new PhabricatorProjectQuery())
       ->setViewer($user)
       ->withAncestorProjectPHIDs(array($ancestor->getPHID()))
       ->execute();
     $this->assertEqual(2, count($projects));
 
     $projects = id(new PhabricatorProjectQuery())
       ->setViewer($user)
       ->withParentProjectPHIDs(array($ancestor->getPHID()))
       ->execute();
     $this->assertEqual(1, count($projects));
     $this->assertEqual(
       $parent->getPHID(),
       head($projects)->getPHID());
 
     $projects = id(new PhabricatorProjectQuery())
       ->setViewer($user)
       ->withAncestorProjectPHIDs(array($ancestor->getPHID()))
       ->withDepthBetween(2, null)
       ->execute();
     $this->assertEqual(1, count($projects));
     $this->assertEqual(
       $child->getPHID(),
       head($projects)->getPHID());
 
     $parent2 = $this->createProject($user, $ancestor);
     $child2 = $this->createProject($user, $parent2);
     $grandchild2 = $this->createProject($user, $child2);
 
     $projects = id(new PhabricatorProjectQuery())
       ->setViewer($user)
       ->withAncestorProjectPHIDs(array($ancestor->getPHID()))
       ->execute();
     $this->assertEqual(5, count($projects));
 
     $projects = id(new PhabricatorProjectQuery())
       ->setViewer($user)
       ->withParentProjectPHIDs(array($ancestor->getPHID()))
       ->execute();
     $this->assertEqual(2, count($projects));
 
     $projects = id(new PhabricatorProjectQuery())
       ->setViewer($user)
       ->withAncestorProjectPHIDs(array($ancestor->getPHID()))
       ->withDepthBetween(2, null)
       ->execute();
     $this->assertEqual(3, count($projects));
 
     $projects = id(new PhabricatorProjectQuery())
       ->setViewer($user)
       ->withAncestorProjectPHIDs(array($ancestor->getPHID()))
       ->withDepthBetween(3, null)
       ->execute();
     $this->assertEqual(1, count($projects));
 
     $projects = id(new PhabricatorProjectQuery())
       ->setViewer($user)
       ->withPHIDs(
         array(
           $child->getPHID(),
           $grandchild2->getPHID(),
         ))
       ->execute();
     $this->assertEqual(2, count($projects));
   }
 
   public function testMemberMaterialization() {
     $material_type = PhabricatorProjectMaterializedMemberEdgeType::EDGECONST;
 
     $user = $this->createUser();
     $user->save();
 
     $parent = $this->createProject($user);
     $child = $this->createProject($user, $parent);
 
     $this->joinProject($child, $user);
 
     $parent_material = PhabricatorEdgeQuery::loadDestinationPHIDs(
       $parent->getPHID(),
       $material_type);
 
     $this->assertEqual(
       array($user->getPHID()),
       $parent_material);
   }
 
   public function testMilestones() {
     $user = $this->createUser();
     $user->save();
 
     $parent = $this->createProject($user);
 
     $m1 = $this->createProject($user, $parent, true);
     $m2 = $this->createProject($user, $parent, true);
     $m3 = $this->createProject($user, $parent, true);
 
     $this->assertEqual(1, $m1->getMilestoneNumber());
     $this->assertEqual(2, $m2->getMilestoneNumber());
     $this->assertEqual(3, $m3->getMilestoneNumber());
   }
 
   public function testMilestoneMembership() {
     $user = $this->createUser();
     $user->save();
 
     $parent = $this->createProject($user);
     $milestone = $this->createProject($user, $parent, true);
 
     $this->joinProject($parent, $user);
 
     $milestone = id(new PhabricatorProjectQuery())
       ->setViewer($user)
       ->withPHIDs(array($milestone->getPHID()))
       ->executeOne();
 
     $this->assertTrue($milestone->isUserMember($user->getPHID()));
 
     $milestone = id(new PhabricatorProjectQuery())
       ->setViewer($user)
       ->withPHIDs(array($milestone->getPHID()))
       ->needMembers(true)
       ->executeOne();
 
     $this->assertEqual(
       array($user->getPHID()),
       $milestone->getMemberPHIDs());
   }
 
   public function testSameSlugAsName() {
     // It should be OK to type the primary hashtag into "additional hashtags",
     // even if the primary hashtag doesn't exist yet because you're creating
     // or renaming the project.
 
     $user = $this->createUser();
     $user->save();
 
     $project = $this->createProject($user);
 
     // In this first case, set the name and slugs at the same time.
     $name = 'slugproject';
 
     $xactions = array();
     $xactions[] = id(new PhabricatorProjectTransaction())
       ->setTransactionType(PhabricatorProjectTransaction::TYPE_NAME)
       ->setNewValue($name);
     $this->applyTransactions($project, $user, $xactions);
 
     $xactions = array();
     $xactions[] = id(new PhabricatorProjectTransaction())
       ->setTransactionType(PhabricatorProjectTransaction::TYPE_SLUGS)
       ->setNewValue(array($name));
     $this->applyTransactions($project, $user, $xactions);
 
     $project = id(new PhabricatorProjectQuery())
       ->setViewer($user)
       ->withPHIDs(array($project->getPHID()))
       ->needSlugs(true)
       ->executeOne();
 
     $slugs = $project->getSlugs();
     $slugs = mpull($slugs, 'getSlug');
 
     $this->assertTrue(in_array($name, $slugs));
 
     // In this second case, set the name first and then the slugs separately.
     $name2 = 'slugproject2';
     $xactions = array();
 
     $xactions[] = id(new PhabricatorProjectTransaction())
       ->setTransactionType(PhabricatorProjectTransaction::TYPE_NAME)
       ->setNewValue($name2);
 
     $xactions[] = id(new PhabricatorProjectTransaction())
       ->setTransactionType(PhabricatorProjectTransaction::TYPE_SLUGS)
       ->setNewValue(array($name2));
 
     $this->applyTransactions($project, $user, $xactions);
 
     $project = id(new PhabricatorProjectQuery())
       ->setViewer($user)
       ->withPHIDs(array($project->getPHID()))
       ->needSlugs(true)
       ->executeOne();
 
     $slugs = $project->getSlugs();
     $slugs = mpull($slugs, 'getSlug');
 
     $this->assertTrue(in_array($name2, $slugs));
   }
 
   public function testDuplicateSlugs() {
     // Creating a project with multiple duplicate slugs should succeed.
 
     $user = $this->createUser();
     $user->save();
 
     $project = $this->createProject($user);
 
     $input = 'duplicate';
 
     $xactions = array();
 
     $xactions[] = id(new PhabricatorProjectTransaction())
       ->setTransactionType(PhabricatorProjectTransaction::TYPE_SLUGS)
       ->setNewValue(array($input, $input));
 
     $this->applyTransactions($project, $user, $xactions);
 
     $project = id(new PhabricatorProjectQuery())
       ->setViewer($user)
       ->withPHIDs(array($project->getPHID()))
       ->needSlugs(true)
       ->executeOne();
 
     $slugs = $project->getSlugs();
     $slugs = mpull($slugs, 'getSlug');
 
     $this->assertTrue(in_array($input, $slugs));
   }
 
   public function testNormalizeSlugs() {
     // When a user creates a project with slug "XxX360n0sc0perXxX", normalize
     // it before writing it.
 
     $user = $this->createUser();
     $user->save();
 
     $project = $this->createProject($user);
 
     $input = 'NoRmAlIzE';
     $expect = 'normalize';
 
     $xactions = array();
 
     $xactions[] = id(new PhabricatorProjectTransaction())
       ->setTransactionType(PhabricatorProjectTransaction::TYPE_SLUGS)
       ->setNewValue(array($input));
 
     $this->applyTransactions($project, $user, $xactions);
 
     $project = id(new PhabricatorProjectQuery())
       ->setViewer($user)
       ->withPHIDs(array($project->getPHID()))
       ->needSlugs(true)
       ->executeOne();
 
     $slugs = $project->getSlugs();
     $slugs = mpull($slugs, 'getSlug');
 
     $this->assertTrue(in_array($expect, $slugs));
 
 
     // If another user tries to add the same slug in denormalized form, it
     // should be caught and fail, even though the database version of the slug
     // is normalized.
 
     $project2 = $this->createProject($user);
 
     $xactions = array();
 
     $xactions[] = id(new PhabricatorProjectTransaction())
       ->setTransactionType(PhabricatorProjectTransaction::TYPE_SLUGS)
       ->setNewValue(array($input));
 
     $caught = null;
     try {
       $this->applyTransactions($project2, $user, $xactions);
     } catch (PhabricatorApplicationTransactionValidationException $ex) {
       $caught = $ex;
     }
 
     $this->assertTrue((bool)$caught);
   }
 
   public function testProjectMembersVisibility() {
     // This is primarily testing that you can create a project and set the
     // visibility or edit policy to "Project Members" immediately.
 
     $user1 = $this->createUser();
     $user1->save();
 
     $user2 = $this->createUser();
     $user2->save();
 
     $project = PhabricatorProject::initializeNewProject($user1);
     $name = pht('Test Project %d', mt_rand());
 
     $xactions = array();
 
     $xactions[] = id(new PhabricatorProjectTransaction())
       ->setTransactionType(PhabricatorProjectTransaction::TYPE_NAME)
       ->setNewValue($name);
 
     $xactions[] = id(new PhabricatorProjectTransaction())
       ->setTransactionType(PhabricatorTransactions::TYPE_VIEW_POLICY)
       ->setNewValue(
         id(new PhabricatorProjectMembersPolicyRule())
           ->getObjectPolicyFullKey());
 
     $edge_type = PhabricatorProjectProjectHasMemberEdgeType::EDGECONST;
 
     $xactions[] = id(new PhabricatorProjectTransaction())
       ->setTransactionType(PhabricatorTransactions::TYPE_EDGE)
       ->setMetadataValue('edge:type', $edge_type)
       ->setNewValue(
         array(
           '=' => array($user1->getPHID() => $user1->getPHID()),
         ));
 
     $this->applyTransactions($project, $user1, $xactions);
 
     $this->assertTrue((bool)$this->refreshProject($project, $user1));
     $this->assertFalse((bool)$this->refreshProject($project, $user2));
 
     $this->leaveProject($project, $user1);
 
     $this->assertFalse((bool)$this->refreshProject($project, $user1));
   }
 
   public function testParentProject() {
     $user = $this->createUser();
     $user->save();
 
     $parent = $this->createProject($user);
     $child = $this->createProject($user, $parent);
 
     $this->assertTrue(true);
 
     $child = $this->refreshProject($child, $user);
 
     $this->assertEqual(
       $parent->getPHID(),
       $child->getParentProject()->getPHID());
 
     $this->assertEqual(1, (int)$child->getProjectDepth());
 
     $this->assertFalse(
       $child->isUserMember($user->getPHID()));
 
     $this->assertFalse(
       $child->getParentProject()->isUserMember($user->getPHID()));
 
     $this->joinProject($child, $user);
 
     $child = $this->refreshProject($child, $user);
 
     $this->assertTrue(
       $child->isUserMember($user->getPHID()));
 
     $this->assertTrue(
       $child->getParentProject()->isUserMember($user->getPHID()));
 
 
     // Test that hiding a parent hides the child.
 
     $user2 = $this->createUser();
     $user2->save();
 
     // Second user can see the project for now.
     $this->assertTrue((bool)$this->refreshProject($child, $user2));
 
     // Hide the parent.
     $this->setViewPolicy($parent, $user, $user->getPHID());
 
     // First user (who can see the parent because they are a member of
     // the child) can see the project.
     $this->assertTrue((bool)$this->refreshProject($child, $user));
 
     // Second user can not, because they can't see the parent.
     $this->assertFalse((bool)$this->refreshProject($child, $user2));
   }
 
   public function testSlugMaps() {
     // When querying by slugs, slugs should be normalized and the mapping
     // should be reported correctly.
     $user = $this->createUser();
     $user->save();
 
     $name = 'queryslugproject';
     $name2 = 'QUERYslugPROJECT';
     $slug = 'queryslugextra';
     $slug2 = 'QuErYSlUgExTrA';
 
     $project = PhabricatorProject::initializeNewProject($user);
 
     $xactions = array();
 
     $xactions[] = id(new PhabricatorProjectTransaction())
       ->setTransactionType(PhabricatorProjectTransaction::TYPE_NAME)
       ->setNewValue($name);
 
     $xactions[] = id(new PhabricatorProjectTransaction())
       ->setTransactionType(PhabricatorProjectTransaction::TYPE_SLUGS)
       ->setNewValue(array($slug));
 
     $this->applyTransactions($project, $user, $xactions);
 
     $project_query = id(new PhabricatorProjectQuery())
       ->setViewer($user)
       ->withSlugs(array($name));
     $project_query->execute();
     $map = $project_query->getSlugMap();
 
     $this->assertEqual(
       array(
         $name => $project->getPHID(),
       ),
       ipull($map, 'projectPHID'));
 
     $project_query = id(new PhabricatorProjectQuery())
       ->setViewer($user)
       ->withSlugs(array($slug));
     $project_query->execute();
     $map = $project_query->getSlugMap();
 
     $this->assertEqual(
       array(
         $slug => $project->getPHID(),
       ),
       ipull($map, 'projectPHID'));
 
     $project_query = id(new PhabricatorProjectQuery())
       ->setViewer($user)
       ->withSlugs(array($name, $slug, $name2, $slug2));
     $project_query->execute();
     $map = $project_query->getSlugMap();
 
     $expect = array(
       $name => $project->getPHID(),
       $slug => $project->getPHID(),
       $name2 => $project->getPHID(),
       $slug2 => $project->getPHID(),
     );
 
     $actual = ipull($map, 'projectPHID');
 
     ksort($expect);
     ksort($actual);
 
     $this->assertEqual($expect, $actual);
 
     $expect = array(
       $name => $name,
       $slug => $slug,
       $name2 => $name,
       $slug2 => $slug,
     );
 
     $actual = ipull($map, 'slug');
 
     ksort($expect);
     ksort($actual);
 
     $this->assertEqual($expect, $actual);
   }
 
   public function testJoinLeaveProject() {
     $user = $this->createUser();
     $user->save();
 
     $proj = $this->createProjectWithNewAuthor();
 
     $proj = $this->refreshProject($proj, $user, true);
     $this->assertTrue(
       (bool)$proj,
       pht(
         'Assumption that projects are default visible '.
         'to any user when created.'));
 
     $this->assertFalse(
       $proj->isUserMember($user->getPHID()),
       pht('Arbitrary user not member of project.'));
 
     // Join the project.
     $this->joinProject($proj, $user);
 
     $proj = $this->refreshProject($proj, $user, true);
     $this->assertTrue((bool)$proj);
 
     $this->assertTrue(
       $proj->isUserMember($user->getPHID()),
       pht('Join works.'));
 
 
     // Join the project again.
     $this->joinProject($proj, $user);
 
     $proj = $this->refreshProject($proj, $user, true);
     $this->assertTrue((bool)$proj);
 
     $this->assertTrue(
       $proj->isUserMember($user->getPHID()),
       pht('Joining an already-joined project is a no-op.'));
 
 
     // Leave the project.
     $this->leaveProject($proj, $user);
 
     $proj = $this->refreshProject($proj, $user, true);
     $this->assertTrue((bool)$proj);
 
     $this->assertFalse(
       $proj->isUserMember($user->getPHID()),
       pht('Leave works.'));
 
 
     // Leave the project again.
     $this->leaveProject($proj, $user);
 
     $proj = $this->refreshProject($proj, $user, true);
     $this->assertTrue((bool)$proj);
 
     $this->assertFalse(
       $proj->isUserMember($user->getPHID()),
       pht('Leaving an already-left project is a no-op.'));
 
 
     // If a user can't edit or join a project, joining fails.
     $proj->setEditPolicy(PhabricatorPolicies::POLICY_NOONE);
     $proj->setJoinPolicy(PhabricatorPolicies::POLICY_NOONE);
     $proj->save();
 
     $proj = $this->refreshProject($proj, $user, true);
     $caught = null;
     try {
       $this->joinProject($proj, $user);
     } catch (Exception $ex) {
       $caught = $ex;
     }
     $this->assertTrue($ex instanceof Exception);
 
 
     // If a user can edit a project, they can join.
     $proj->setEditPolicy(PhabricatorPolicies::POLICY_USER);
     $proj->setJoinPolicy(PhabricatorPolicies::POLICY_NOONE);
     $proj->save();
 
     $proj = $this->refreshProject($proj, $user, true);
     $this->joinProject($proj, $user);
     $proj = $this->refreshProject($proj, $user, true);
     $this->assertTrue(
       $proj->isUserMember($user->getPHID()),
       pht('Join allowed with edit permission.'));
     $this->leaveProject($proj, $user);
 
 
     // If a user can join a project, they can join, even if they can't edit.
     $proj->setEditPolicy(PhabricatorPolicies::POLICY_NOONE);
     $proj->setJoinPolicy(PhabricatorPolicies::POLICY_USER);
     $proj->save();
 
     $proj = $this->refreshProject($proj, $user, true);
     $this->joinProject($proj, $user);
     $proj = $this->refreshProject($proj, $user, true);
     $this->assertTrue(
       $proj->isUserMember($user->getPHID()),
       pht('Join allowed with join permission.'));
 
 
     // A user can leave a project even if they can't edit it or join.
     $proj->setEditPolicy(PhabricatorPolicies::POLICY_NOONE);
     $proj->setJoinPolicy(PhabricatorPolicies::POLICY_NOONE);
     $proj->save();
 
     $proj = $this->refreshProject($proj, $user, true);
     $this->leaveProject($proj, $user);
     $proj = $this->refreshProject($proj, $user, true);
     $this->assertFalse(
       $proj->isUserMember($user->getPHID()),
       pht('Leave allowed without any permission.'));
   }
 
 
   public function testComplexConstraints() {
     $user = $this->createUser();
     $user->save();
 
     $engineering = $this->createProject($user);
     $engineering_scan = $this->createProject($user, $engineering);
     $engineering_warp = $this->createProject($user, $engineering);
 
     $exploration = $this->createProject($user);
     $exploration_diplomacy = $this->createProject($user, $exploration);
 
     $task_engineering = $this->newTask(
       $user,
       array($engineering),
       pht('Engineering Only'));
 
     $task_exploration = $this->newTask(
       $user,
       array($exploration),
       pht('Exploration Only'));
 
     $task_warp_explore = $this->newTask(
       $user,
       array($engineering_warp, $exploration),
       pht('Warp to New Planet'));
 
     $task_diplomacy_scan = $this->newTask(
       $user,
       array($engineering_scan, $exploration_diplomacy),
       pht('Scan Diplomat'));
 
     $task_diplomacy = $this->newTask(
       $user,
       array($exploration_diplomacy),
       pht('Diplomatic Meeting'));
 
     $task_warp_scan = $this->newTask(
       $user,
       array($engineering_scan, $engineering_warp),
       pht('Scan Warp Drives'));
 
     $this->assertQueryByProjects(
       $user,
       array(
         $task_engineering,
         $task_warp_explore,
         $task_diplomacy_scan,
         $task_warp_scan,
       ),
       array($engineering),
       pht('All Engineering'));
 
     $this->assertQueryByProjects(
       $user,
       array(
         $task_diplomacy_scan,
         $task_warp_scan,
       ),
       array($engineering_scan),
       pht('All Scan'));
 
     $this->assertQueryByProjects(
       $user,
       array(
         $task_warp_explore,
         $task_diplomacy_scan,
       ),
       array($engineering, $exploration),
       pht('Engineering + Exploration'));
 
     // This is testing that a query for "Parent" and "Parent > Child" works
     // properly.
     $this->assertQueryByProjects(
       $user,
       array(
         $task_diplomacy_scan,
         $task_warp_scan,
       ),
       array($engineering, $engineering_scan),
       pht('Engineering + Scan'));
   }
 
   public function testTagAncestryConflicts() {
     $user = $this->createUser();
     $user->save();
 
     $stonework = $this->createProject($user);
     $stonework_masonry = $this->createProject($user, $stonework);
     $stonework_sculpting = $this->createProject($user, $stonework);
 
     $task = $this->newTask($user, array());
     $this->assertEqual(array(), $this->getTaskProjects($task));
 
     $this->addProjectTags($user, $task, array($stonework->getPHID()));
     $this->assertEqual(
       array(
         $stonework->getPHID(),
       ),
       $this->getTaskProjects($task));
 
     // Adding a descendant should remove the parent.
     $this->addProjectTags($user, $task, array($stonework_masonry->getPHID()));
     $this->assertEqual(
       array(
         $stonework_masonry->getPHID(),
       ),
       $this->getTaskProjects($task));
 
     // Adding an ancestor should remove the descendant.
     $this->addProjectTags($user, $task, array($stonework->getPHID()));
     $this->assertEqual(
       array(
         $stonework->getPHID(),
       ),
       $this->getTaskProjects($task));
 
     // Adding two tags in the same hierarchy which are not mutual ancestors
     // should remove the ancestor but otherwise work fine.
     $this->addProjectTags(
       $user,
       $task,
       array(
         $stonework_masonry->getPHID(),
         $stonework_sculpting->getPHID(),
       ));
 
     $expect = array(
       $stonework_masonry->getPHID(),
       $stonework_sculpting->getPHID(),
     );
     sort($expect);
 
     $this->assertEqual($expect,  $this->getTaskProjects($task));
   }
 
   public function testTagMilestoneConflicts() {
     $user = $this->createUser();
     $user->save();
 
     $stonework = $this->createProject($user);
     $stonework_1 = $this->createProject($user, $stonework, true);
     $stonework_2 = $this->createProject($user, $stonework, true);
 
     $task = $this->newTask($user, array());
     $this->assertEqual(array(), $this->getTaskProjects($task));
 
     $this->addProjectTags($user, $task, array($stonework->getPHID()));
     $this->assertEqual(
       array(
         $stonework->getPHID(),
       ),
       $this->getTaskProjects($task));
 
     // Adding a milesone should remove the parent.
     $this->addProjectTags($user, $task, array($stonework_1->getPHID()));
     $this->assertEqual(
       array(
         $stonework_1->getPHID(),
       ),
       $this->getTaskProjects($task));
 
     // Adding the parent should remove the milestone.
     $this->addProjectTags($user, $task, array($stonework->getPHID()));
     $this->assertEqual(
       array(
         $stonework->getPHID(),
       ),
       $this->getTaskProjects($task));
 
     // First, add one milestone.
     $this->addProjectTags($user, $task, array($stonework_1->getPHID()));
     // Now, adding a second milestone should remove the first milestone.
     $this->addProjectTags($user, $task, array($stonework_2->getPHID()));
     $this->assertEqual(
       array(
         $stonework_2->getPHID(),
       ),
       $this->getTaskProjects($task));
   }
 
   public function testBoardMoves() {
     $user = $this->createUser();
     $user->save();
 
     $board = $this->createProject($user);
 
     $backlog = $this->addColumn($user, $board, 0);
     $column = $this->addColumn($user, $board, 1);
 
     // New tasks should appear in the backlog.
     $task1 = $this->newTask($user, array($board));
     $expect = array(
       $backlog->getPHID(),
     );
     $this->assertColumns($expect, $user, $board, $task1);
 
     // Moving a task should move it to the destination column.
     $this->moveToColumn($user, $board, $task1, $backlog, $column);
     $expect = array(
       $column->getPHID(),
     );
     $this->assertColumns($expect, $user, $board, $task1);
 
     // Same thing again, with a new task.
     $task2 = $this->newTask($user, array($board));
     $expect = array(
       $backlog->getPHID(),
     );
     $this->assertColumns($expect, $user, $board, $task2);
 
     // Move it, too.
     $this->moveToColumn($user, $board, $task2, $backlog, $column);
     $expect = array(
       $column->getPHID(),
     );
     $this->assertColumns($expect, $user, $board, $task2);
 
     // Now the stuff should be in the column, in order, with the more recently
     // moved task on top.
     $expect = array(
       $task2->getPHID(),
       $task1->getPHID(),
     );
     $this->assertTasksInColumn($expect, $user, $board, $column);
 
     // Move the second task after the first task.
     $options = array(
       'afterPHID' => $task1->getPHID(),
     );
     $this->moveToColumn($user, $board, $task2, $column, $column, $options);
     $expect = array(
       $task1->getPHID(),
       $task2->getPHID(),
     );
     $this->assertTasksInColumn($expect, $user, $board, $column);
 
     // Move the second task before the first task.
     $options = array(
       'beforePHID' => $task1->getPHID(),
     );
     $this->moveToColumn($user, $board, $task2, $column, $column, $options);
     $expect = array(
       $task2->getPHID(),
       $task1->getPHID(),
     );
     $this->assertTasksInColumn($expect, $user, $board, $column);
   }
 
   public function testMilestoneMoves() {
     $user = $this->createUser();
     $user->save();
 
     $board = $this->createProject($user);
 
     $backlog = $this->addColumn($user, $board, 0);
 
     // Create a task into the backlog.
     $task = $this->newTask($user, array($board));
     $expect = array(
       $backlog->getPHID(),
     );
     $this->assertColumns($expect, $user, $board, $task);
 
     $milestone = $this->createProject($user, $board, true);
 
     $this->addProjectTags($user, $task, array($milestone->getPHID()));
 
     // We just want the side effect of looking at the board: creation of the
     // milestone column.
     $this->loadColumns($user, $board, $task);
 
     $column = id(new PhabricatorProjectColumnQuery())
       ->setViewer($user)
       ->withProjectPHIDs(array($board->getPHID()))
       ->withProxyPHIDs(array($milestone->getPHID()))
       ->executeOne();
 
     $this->assertTrue((bool)$column);
 
     // Moving the task to the milestone should have moved it to the milestone
     // column.
     $expect = array(
       $column->getPHID(),
     );
     $this->assertColumns($expect, $user, $board, $task);
+
+
+    // Move the task within the "Milestone" column. This should not affect
+    // the projects the task is tagged with. See T10912.
+    $task_a = $task;
+
+    $task_b = $this->newTask($user, array($backlog));
+    $this->moveToColumn($user, $board, $task_b, $backlog, $column);
+
+    $a_options = array(
+      'beforePHID' => $task_b->getPHID(),
+    );
+
+    $b_options = array(
+      'beforePHID' => $task_a->getPHID(),
+    );
+
+    $old_projects = $this->getTaskProjects($task);
+
+    // Move the target task to the top.
+    $this->moveToColumn($user, $board, $task_a, $column, $column, $a_options);
+    $new_projects = $this->getTaskProjects($task_a);
+    $this->assertEqual($old_projects, $new_projects);
+
+    // Move the other task.
+    $this->moveToColumn($user, $board, $task_b, $column, $column, $b_options);
+    $new_projects = $this->getTaskProjects($task_a);
+    $this->assertEqual($old_projects, $new_projects);
+
+    // Move the target task again.
+    $this->moveToColumn($user, $board, $task_a, $column, $column, $a_options);
+    $new_projects = $this->getTaskProjects($task_a);
+    $this->assertEqual($old_projects, $new_projects);
   }
 
   public function testColumnExtendedPolicies() {
     $user = $this->createUser();
     $user->save();
 
     $board = $this->createProject($user);
     $column = $this->addColumn($user, $board, 0);
 
     // At first, the user should be able to view and edit the column.
     $column = $this->refreshColumn($user, $column);
     $this->assertTrue((bool)$column);
 
     $can_edit = PhabricatorPolicyFilter::hasCapability(
       $user,
       $column,
       PhabricatorPolicyCapability::CAN_EDIT);
     $this->assertTrue($can_edit);
 
     // Now, set the project edit policy to "Members of Project". This should
     // disable editing.
     $members_policy = id(new PhabricatorProjectMembersPolicyRule())
       ->getObjectPolicyFullKey();
     $board->setEditPolicy($members_policy)->save();
 
     $column = $this->refreshColumn($user, $column);
     $this->assertTrue((bool)$column);
 
     $can_edit = PhabricatorPolicyFilter::hasCapability(
       $user,
       $column,
       PhabricatorPolicyCapability::CAN_EDIT);
     $this->assertFalse($can_edit);
 
     // Now, join the project. This should make the column editable again.
     $this->joinProject($board, $user);
 
     $column = $this->refreshColumn($user, $column);
     $this->assertTrue((bool)$column);
 
     $can_edit = PhabricatorPolicyFilter::hasCapability(
       $user,
       $column,
       PhabricatorPolicyCapability::CAN_EDIT);
     $this->assertTrue($can_edit);
   }
 
   private function moveToColumn(
     PhabricatorUser $viewer,
     PhabricatorProject $board,
     ManiphestTask $task,
     PhabricatorProjectColumn $src,
     PhabricatorProjectColumn $dst,
     $options = null) {
 
     $xactions = array();
 
     if (!$options) {
       $options = array();
     }
 
     $value = array(
       'columnPHID' => $dst->getPHID(),
     ) + $options;
 
     $xactions[] = id(new ManiphestTransaction())
       ->setTransactionType(PhabricatorTransactions::TYPE_COLUMNS)
       ->setNewValue(array($value));
 
     $editor = id(new ManiphestTransactionEditor())
       ->setActor($viewer)
       ->setContentSource($this->newContentSource())
       ->setContinueOnNoEffect(true)
       ->applyTransactions($task, $xactions);
   }
 
   private function assertColumns(
     array $expect,
     PhabricatorUser $viewer,
     PhabricatorProject $board,
     ManiphestTask $task) {
     $column_phids = $this->loadColumns($viewer, $board, $task);
     $this->assertEqual($expect, $column_phids);
   }
 
   private function loadColumns(
     PhabricatorUser $viewer,
     PhabricatorProject $board,
     ManiphestTask $task) {
     $engine = id(new PhabricatorBoardLayoutEngine())
       ->setViewer($viewer)
       ->setBoardPHIDs(array($board->getPHID()))
       ->setObjectPHIDs(
         array(
           $task->getPHID(),
         ))
       ->executeLayout();
 
     $columns = $engine->getObjectColumns($board->getPHID(), $task->getPHID());
     $column_phids = mpull($columns, 'getPHID');
     $column_phids = array_values($column_phids);
 
     return $column_phids;
   }
 
   private function assertTasksInColumn(
     array $expect,
     PhabricatorUser $viewer,
     PhabricatorProject $board,
     PhabricatorProjectColumn $column) {
 
     $engine = id(new PhabricatorBoardLayoutEngine())
       ->setViewer($viewer)
       ->setBoardPHIDs(array($board->getPHID()))
       ->setObjectPHIDs($expect)
       ->executeLayout();
 
     $object_phids = $engine->getColumnObjectPHIDs(
       $board->getPHID(),
       $column->getPHID());
     $object_phids = array_values($object_phids);
 
     $this->assertEqual($expect, $object_phids);
   }
 
   private function addColumn(
     PhabricatorUser $viewer,
     PhabricatorProject $project,
     $sequence) {
 
     $project->setHasWorkboard(1)->save();
 
     return PhabricatorProjectColumn::initializeNewColumn($viewer)
       ->setSequence(0)
       ->setProperty('isDefault', ($sequence == 0))
       ->setProjectPHID($project->getPHID())
       ->save();
   }
 
   private function getTaskProjects(ManiphestTask $task) {
     $project_phids = PhabricatorEdgeQuery::loadDestinationPHIDs(
       $task->getPHID(),
       PhabricatorProjectObjectHasProjectEdgeType::EDGECONST);
 
     sort($project_phids);
 
     return $project_phids;
   }
 
   private function attemptProjectEdit(
     PhabricatorProject $proj,
     PhabricatorUser $user,
     $skip_refresh = false) {
 
     $proj = $this->refreshProject($proj, $user, true);
 
     $new_name = $proj->getName().' '.mt_rand();
 
     $xaction = new PhabricatorProjectTransaction();
     $xaction->setTransactionType(PhabricatorProjectTransaction::TYPE_NAME);
     $xaction->setNewValue($new_name);
 
     $this->applyTransactions($proj, $user, array($xaction));
 
     return true;
   }
 
 
   private function addProjectTags(
     PhabricatorUser $viewer,
     ManiphestTask $task,
     array $phids) {
 
     $xactions = array();
 
     $xactions[] = id(new ManiphestTransaction())
       ->setTransactionType(PhabricatorTransactions::TYPE_EDGE)
       ->setMetadataValue(
         'edge:type',
         PhabricatorProjectObjectHasProjectEdgeType::EDGECONST)
       ->setNewValue(
         array(
           '+' => array_fuse($phids),
         ));
 
     $editor = id(new ManiphestTransactionEditor())
       ->setActor($viewer)
       ->setContentSource($this->newContentSource())
       ->setContinueOnNoEffect(true)
       ->applyTransactions($task, $xactions);
   }
 
   private function newTask(
     PhabricatorUser $viewer,
     array $projects,
     $name = null) {
 
     $task = ManiphestTask::initializeNewTask($viewer);
 
     if (!strlen($name)) {
       $name = pht('Test Task');
     }
 
     $xactions = array();
 
     $xactions[] = id(new ManiphestTransaction())
       ->setTransactionType(ManiphestTransaction::TYPE_TITLE)
       ->setNewValue($name);
 
     if ($projects) {
       $xactions[] = id(new ManiphestTransaction())
         ->setTransactionType(PhabricatorTransactions::TYPE_EDGE)
         ->setMetadataValue(
           'edge:type',
           PhabricatorProjectObjectHasProjectEdgeType::EDGECONST)
         ->setNewValue(
           array(
             '=' => array_fuse(mpull($projects, 'getPHID')),
           ));
     }
 
     $editor = id(new ManiphestTransactionEditor())
       ->setActor($viewer)
       ->setContentSource($this->newContentSource())
       ->setContinueOnNoEffect(true)
       ->applyTransactions($task, $xactions);
 
     return $task;
   }
 
   private function assertQueryByProjects(
     PhabricatorUser $viewer,
     array $expect,
     array $projects,
     $label = null) {
 
     $datasource = id(new PhabricatorProjectLogicalDatasource())
       ->setViewer($viewer);
 
     $project_phids = mpull($projects, 'getPHID');
     $constraints = $datasource->evaluateTokens($project_phids);
 
     $query = id(new ManiphestTaskQuery())
       ->setViewer($viewer);
 
     $query->withEdgeLogicConstraints(
       PhabricatorProjectObjectHasProjectEdgeType::EDGECONST,
       $constraints);
 
     $tasks = $query->execute();
 
     $expect_phids = mpull($expect, 'getTitle', 'getPHID');
     ksort($expect_phids);
 
     $actual_phids = mpull($tasks, 'getTitle', 'getPHID');
     ksort($actual_phids);
 
     $this->assertEqual($expect_phids, $actual_phids, $label);
   }
 
   private function refreshProject(
     PhabricatorProject $project,
     PhabricatorUser $viewer,
     $need_members = false,
     $need_watchers = false) {
 
     $results = id(new PhabricatorProjectQuery())
       ->setViewer($viewer)
       ->needMembers($need_members)
       ->needWatchers($need_watchers)
       ->withIDs(array($project->getID()))
       ->execute();
 
     if ($results) {
       return head($results);
     } else {
       return null;
     }
   }
 
   private function refreshColumn(
     PhabricatorUser $viewer,
     PhabricatorProjectColumn $column) {
 
     $results = id(new PhabricatorProjectColumnQuery())
       ->setViewer($viewer)
       ->withIDs(array($column->getID()))
       ->execute();
 
     if ($results) {
       return head($results);
     } else {
       return null;
     }
   }
 
   private function createProject(
     PhabricatorUser $user,
     PhabricatorProject $parent = null,
     $is_milestone = false) {
 
     $project = PhabricatorProject::initializeNewProject($user);
 
 
     $name = pht('Test Project %d', mt_rand());
 
     $xactions = array();
 
     $xactions[] = id(new PhabricatorProjectTransaction())
       ->setTransactionType(PhabricatorProjectTransaction::TYPE_NAME)
       ->setNewValue($name);
 
     if ($parent) {
       if ($is_milestone) {
         $xactions[] = id(new PhabricatorProjectTransaction())
           ->setTransactionType(PhabricatorProjectTransaction::TYPE_MILESTONE)
           ->setNewValue($parent->getPHID());
       } else {
         $xactions[] = id(new PhabricatorProjectTransaction())
           ->setTransactionType(PhabricatorProjectTransaction::TYPE_PARENT)
           ->setNewValue($parent->getPHID());
       }
     }
 
     $this->applyTransactions($project, $user, $xactions);
 
     // Force these values immediately; they are normally updated by the
     // index engine.
     if ($parent) {
       if ($is_milestone) {
         $parent->setHasMilestones(1)->save();
       } else {
         $parent->setHasSubprojects(1)->save();
       }
     }
 
     return $project;
   }
 
   private function setViewPolicy(
     PhabricatorProject $project,
     PhabricatorUser $user,
     $policy) {
 
     $xactions = array();
 
     $xactions[] = id(new PhabricatorProjectTransaction())
       ->setTransactionType(PhabricatorTransactions::TYPE_VIEW_POLICY)
       ->setNewValue($policy);
 
     $this->applyTransactions($project, $user, $xactions);
 
     return $project;
   }
 
   private function createProjectWithNewAuthor() {
     $author = $this->createUser();
     $author->save();
 
     $project = $this->createProject($author);
 
     return $project;
   }
 
   private function createUser() {
     $rand = mt_rand();
 
     $user = new PhabricatorUser();
     $user->setUsername('unittestuser'.$rand);
     $user->setRealName(pht('Unit Test User %d', $rand));
 
     return $user;
   }
 
   private function joinProject(
     PhabricatorProject $project,
     PhabricatorUser $user) {
     return $this->joinOrLeaveProject($project, $user, '+');
   }
 
   private function leaveProject(
     PhabricatorProject $project,
     PhabricatorUser $user) {
     return $this->joinOrLeaveProject($project, $user, '-');
   }
 
   private function watchProject(
     PhabricatorProject $project,
     PhabricatorUser $user) {
     return $this->watchOrUnwatchProject($project, $user, '+');
   }
 
   private function unwatchProject(
     PhabricatorProject $project,
     PhabricatorUser $user) {
     return $this->watchOrUnwatchProject($project, $user, '-');
   }
 
   private function joinOrLeaveProject(
     PhabricatorProject $project,
     PhabricatorUser $user,
     $operation) {
     return $this->applyProjectEdgeTransaction(
       $project,
       $user,
       $operation,
       PhabricatorProjectProjectHasMemberEdgeType::EDGECONST);
   }
 
   private function watchOrUnwatchProject(
     PhabricatorProject $project,
     PhabricatorUser $user,
     $operation) {
     return $this->applyProjectEdgeTransaction(
       $project,
       $user,
       $operation,
       PhabricatorObjectHasWatcherEdgeType::EDGECONST);
   }
 
   private function applyProjectEdgeTransaction(
     PhabricatorProject $project,
     PhabricatorUser $user,
     $operation,
     $edge_type) {
 
     $spec = array(
       $operation => array($user->getPHID() => $user->getPHID()),
     );
 
     $xactions = array();
     $xactions[] = id(new PhabricatorProjectTransaction())
       ->setTransactionType(PhabricatorTransactions::TYPE_EDGE)
       ->setMetadataValue('edge:type', $edge_type)
       ->setNewValue($spec);
 
     $this->applyTransactions($project, $user, $xactions);
 
     return $project;
   }
 
   private function applyTransactions(
     PhabricatorProject $project,
     PhabricatorUser $user,
     array $xactions) {
 
     $editor = id(new PhabricatorProjectTransactionEditor())
       ->setActor($user)
       ->setContentSource($this->newContentSource())
       ->setContinueOnNoEffect(true)
       ->applyTransactions($project, $xactions);
   }
 
 
 }
diff --git a/src/applications/project/controller/PhabricatorProjectMoveController.php b/src/applications/project/controller/PhabricatorProjectMoveController.php
index e529fab61..c92c84320 100644
--- a/src/applications/project/controller/PhabricatorProjectMoveController.php
+++ b/src/applications/project/controller/PhabricatorProjectMoveController.php
@@ -1,229 +1,203 @@
 <?php
 
 final class PhabricatorProjectMoveController
   extends PhabricatorProjectController {
 
   public function handleRequest(AphrontRequest $request) {
     $viewer = $request->getViewer();
     $id = $request->getURIData('id');
 
     $request->validateCSRF();
 
     $column_phid = $request->getStr('columnPHID');
     $object_phid = $request->getStr('objectPHID');
     $after_phid = $request->getStr('afterPHID');
     $before_phid = $request->getStr('beforePHID');
     $order = $request->getStr('order', PhabricatorProjectColumn::DEFAULT_ORDER);
 
     $project = id(new PhabricatorProjectQuery())
       ->setViewer($viewer)
       ->requireCapabilities(
         array(
           PhabricatorPolicyCapability::CAN_VIEW,
         ))
       ->withIDs(array($id))
       ->executeOne();
     if (!$project) {
       return new Aphront404Response();
     }
 
     $board_phid = $project->getPHID();
 
     $object = id(new ManiphestTaskQuery())
       ->setViewer($viewer)
       ->withPHIDs(array($object_phid))
       ->needProjectPHIDs(true)
       ->requireCapabilities(
         array(
           PhabricatorPolicyCapability::CAN_VIEW,
           PhabricatorPolicyCapability::CAN_EDIT,
         ))
       ->executeOne();
 
     if (!$object) {
       return new Aphront404Response();
     }
 
     $columns = id(new PhabricatorProjectColumnQuery())
       ->setViewer($viewer)
       ->withProjectPHIDs(array($project->getPHID()))
       ->execute();
 
     $columns = mpull($columns, null, 'getPHID');
     $column = idx($columns, $column_phid);
     if (!$column) {
       // User is trying to drop this object into a nonexistent column, just kick
       // them out.
       return new Aphront404Response();
     }
 
     $engine = id(new PhabricatorBoardLayoutEngine())
       ->setViewer($viewer)
       ->setBoardPHIDs(array($board_phid))
       ->setObjectPHIDs(array($object_phid))
       ->executeLayout();
 
     $columns = $engine->getObjectColumns($board_phid, $object_phid);
     $old_column_phids = mpull($columns, 'getPHID');
 
     $xactions = array();
 
     $order_params = array();
     if ($order == PhabricatorProjectColumn::ORDER_NATURAL) {
       if ($after_phid) {
         $order_params['afterPHID'] = $after_phid;
       } else if ($before_phid) {
         $order_params['beforePHID'] = $before_phid;
       }
     }
 
     $xactions[] = id(new ManiphestTransaction())
       ->setTransactionType(PhabricatorTransactions::TYPE_COLUMNS)
       ->setNewValue(
         array(
           array(
             'columnPHID' => $column->getPHID(),
           ) + $order_params,
         ));
 
     if ($order == PhabricatorProjectColumn::ORDER_PRIORITY) {
       $priority_xactions = $this->getPriorityTransactions(
         $object,
         $after_phid,
         $before_phid);
       foreach ($priority_xactions as $xaction) {
         $xactions[] = $xaction;
       }
     }
 
-    $proxy = $column->getProxy();
-    if ($proxy) {
-      // We're moving the task into a subproject or milestone column, so add
-      // the subproject or milestone.
-      $add_projects = array($proxy->getPHID());
-    } else if ($project->getHasSubprojects() || $project->getHasMilestones()) {
-      // We're moving the task into the "Backlog" column on the parent project,
-      // so add the parent explicitly. This gets rid of any subproject or
-      // milestone tags.
-      $add_projects = array($project->getPHID());
-    } else {
-      $add_projects = array();
-    }
-
-    if ($add_projects) {
-      $project_type = PhabricatorProjectObjectHasProjectEdgeType::EDGECONST;
-
-      $xactions[] = id(new ManiphestTransaction())
-        ->setTransactionType(PhabricatorTransactions::TYPE_EDGE)
-        ->setMetadataValue('edge:type', $project_type)
-        ->setNewValue(
-          array(
-            '+' => array_fuse($add_projects),
-          ));
-    }
-
     $editor = id(new ManiphestTransactionEditor())
       ->setActor($viewer)
       ->setContinueOnMissingFields(true)
       ->setContinueOnNoEffect(true)
       ->setContentSourceFromRequest($request);
 
     $editor->applyTransactions($object, $xactions);
 
     return $this->newCardResponse($board_phid, $object_phid);
   }
 
   private function getPriorityTransactions(
     ManiphestTask $task,
     $after_phid,
     $before_phid) {
 
     list($after_task, $before_task) = $this->loadPriorityTasks(
       $after_phid,
       $before_phid);
 
     $must_move = false;
     if ($after_task && !$task->isLowerPriorityThan($after_task)) {
       $must_move = true;
     }
 
     if ($before_task && !$task->isHigherPriorityThan($before_task)) {
       $must_move = true;
     }
 
     // The move doesn't require a priority change to be valid, so don't
     // change the priority since we are not being forced to.
     if (!$must_move) {
       return array();
     }
 
     $try = array(
       array($after_task, true),
       array($before_task, false),
     );
 
     $pri = null;
     $sub = null;
     foreach ($try as $spec) {
       list($task, $is_after) = $spec;
 
       if (!$task) {
         continue;
       }
 
       list($pri, $sub) = ManiphestTransactionEditor::getAdjacentSubpriority(
         $task,
         $is_after);
     }
 
     $xactions = array();
     if ($pri !== null) {
       $xactions[] = id(new ManiphestTransaction())
         ->setTransactionType(ManiphestTransaction::TYPE_PRIORITY)
         ->setNewValue($pri);
       $xactions[] = id(new ManiphestTransaction())
         ->setTransactionType(ManiphestTransaction::TYPE_SUBPRIORITY)
         ->setNewValue($sub);
     }
 
     return $xactions;
   }
 
   private function loadPriorityTasks($after_phid, $before_phid) {
     $viewer = $this->getViewer();
 
     $task_phids = array();
 
     if ($after_phid) {
       $task_phids[] = $after_phid;
     }
     if ($before_phid) {
       $task_phids[] = $before_phid;
     }
 
     if (!$task_phids) {
       return array(null, null);
     }
 
     $tasks = id(new ManiphestTaskQuery())
       ->setViewer($viewer)
       ->withPHIDs($task_phids)
       ->execute();
     $tasks = mpull($tasks, null, 'getPHID');
 
     if ($after_phid) {
       $after_task = idx($tasks, $after_phid);
     } else {
       $after_task = null;
     }
 
     if ($before_phid) {
       $before_task = idx($tasks, $before_phid);
     } else {
       $before_task = null;
     }
 
     return array($after_task, $before_task);
   }
 
 }
diff --git a/src/applications/repository/conduit/RepositoryCreateConduitAPIMethod.php b/src/applications/repository/conduit/RepositoryCreateConduitAPIMethod.php
deleted file mode 100644
index ad4b62104..000000000
--- a/src/applications/repository/conduit/RepositoryCreateConduitAPIMethod.php
+++ /dev/null
@@ -1,140 +0,0 @@
-<?php
-
-final class RepositoryCreateConduitAPIMethod
-  extends RepositoryConduitAPIMethod {
-
-  public function getAPIMethodName() {
-    return 'repository.create';
-  }
-
-  public function getMethodStatus() {
-    return self::METHOD_STATUS_UNSTABLE;
-  }
-
-  public function getMethodStatusDescription() {
-    return pht('Repository methods are new and subject to change.');
-  }
-
-  public function getMethodDescription() {
-    return pht('Create a new repository.');
-  }
-
-  protected function defineParamTypes() {
-    $vcs_const = $this->formatStringConstants(array('git', 'hg', 'svn'));
-
-    return array(
-      'name'                => 'required string',
-      'vcs'                 => 'required '.$vcs_const,
-      'callsign'            => 'required string',
-      'description'         => 'optional string',
-      'encoding'            => 'optional string',
-      'tracking'            => 'optional bool',
-      'uri'                 => 'required string',
-      'credentialPHID'      => 'optional string',
-      'svnSubpath'          => 'optional string',
-      'branchFilter'        => 'optional list<string>',
-      'closeCommitsFilter'  => 'optional list<string>',
-      'pullFrequency'       => 'optional int',
-      'defaultBranch'       => 'optional string',
-      'heraldEnabled'       => 'optional bool, default = true',
-      'autocloseEnabled'    => 'optional bool, default = true',
-      'svnUUID'             => 'optional string',
-    );
-  }
-
-  protected function defineReturnType() {
-    return 'nonempty dict';
-  }
-
-  protected function defineErrorTypes() {
-    return array(
-      'ERR-DUPLICATE' => pht('Duplicate repository callsign.'),
-      'ERR-BAD-CALLSIGN' => pht(
-        'Callsign is required and must be ALL UPPERCASE LETTERS.'),
-      'ERR-UNKNOWN-REPOSITORY-VCS' => pht('Unknown repository VCS type.'),
-    );
-  }
-
-  protected function execute(ConduitAPIRequest $request) {
-    $application = id(new PhabricatorApplicationQuery())
-      ->setViewer($request->getUser())
-      ->withClasses(array('PhabricatorDiffusionApplication'))
-      ->executeOne();
-
-    PhabricatorPolicyFilter::requireCapability(
-      $request->getUser(),
-      $application,
-      DiffusionCreateRepositoriesCapability::CAPABILITY);
-
-    // TODO: This has some duplication with (and lacks some of the validation
-    // of) the web workflow; refactor things so they can share more code as this
-    // stabilizes. Specifically, this should move to transactions since they
-    // work properly now.
-
-    $repository = PhabricatorRepository::initializeNewRepository(
-      $request->getUser());
-
-    $repository->setName($request->getValue('name'));
-
-    $callsign = $request->getValue('callsign');
-    if (!preg_match('/^[A-Z]+\z/', $callsign)) {
-      throw new ConduitException('ERR-BAD-CALLSIGN');
-    }
-    $repository->setCallsign($callsign);
-
-    $local_path = PhabricatorEnv::getEnvConfig(
-      'repository.default-local-path');
-
-    $local_path = rtrim($local_path, '/');
-    $local_path = $local_path.'/'.$callsign.'/';
-
-    $vcs = $request->getValue('vcs');
-
-    $map = array(
-      'git' => PhabricatorRepositoryType::REPOSITORY_TYPE_GIT,
-      'hg'  => PhabricatorRepositoryType::REPOSITORY_TYPE_MERCURIAL,
-      'svn' => PhabricatorRepositoryType::REPOSITORY_TYPE_SVN,
-    );
-    if (empty($map[$vcs])) {
-      throw new ConduitException('ERR-UNKNOWN-REPOSITORY-VCS');
-    }
-    $repository->setVersionControlSystem($map[$vcs]);
-
-    $repository->setCredentialPHID($request->getValue('credentialPHID'));
-
-    $remote_uri = $request->getValue('uri');
-    PhabricatorRepository::assertValidRemoteURI($remote_uri);
-
-    $details = array(
-      'encoding'          => $request->getValue('encoding'),
-      'description'       => $request->getValue('description'),
-      'tracking-enabled'  => (bool)$request->getValue('tracking', true),
-      'remote-uri'        => $remote_uri,
-      'local-path'        => $local_path,
-      'branch-filter'     => array_fill_keys(
-        $request->getValue('branchFilter', array()),
-        true),
-      'close-commits-filter' => array_fill_keys(
-        $request->getValue('closeCommitsFilter', array()),
-        true),
-      'pull-frequency'    => $request->getValue('pullFrequency'),
-      'default-branch'    => $request->getValue('defaultBranch'),
-      'herald-disabled'   => !$request->getValue('heraldEnabled', true),
-      'svn-subpath'       => $request->getValue('svnSubpath'),
-      'disable-autoclose' => !$request->getValue('autocloseEnabled', true),
-    );
-
-    foreach ($details as $key => $value) {
-      $repository->setDetail($key, $value);
-    }
-
-    try {
-      $repository->save();
-    } catch (AphrontDuplicateKeyQueryException $ex) {
-      throw new ConduitException('ERR-DUPLICATE');
-    }
-
-    return $repository->toDictionary();
-  }
-
-}
diff --git a/src/applications/repository/daemon/PhabricatorRepositoryPullLocalDaemon.php b/src/applications/repository/daemon/PhabricatorRepositoryPullLocalDaemon.php
index da8d9336d..bbd73d2d9 100644
--- a/src/applications/repository/daemon/PhabricatorRepositoryPullLocalDaemon.php
+++ b/src/applications/repository/daemon/PhabricatorRepositoryPullLocalDaemon.php
@@ -1,564 +1,464 @@
 <?php
 
 /**
  * Run pull commands on local working copies to keep them up to date. This
  * daemon handles all repository types.
  *
  * By default, the daemon pulls **every** repository. If you want it to be
  * responsible for only some repositories, you can launch it with a list of
  * repositories:
  *
  *   ./phd launch repositorypulllocal -- X Q Z
  *
  * You can also launch a daemon which is responsible for all //but// one or
  * more repositories:
  *
  *   ./phd launch repositorypulllocal -- --not A --not B
  *
  * If you have a very large number of repositories and some aren't being pulled
  * as frequently as you'd like, you can either change the pull frequency of
  * the less-important repositories to a larger number (so the daemon will skip
  * them more often) or launch one daemon for all the less-important repositories
  * and one for the more important repositories (or one for each more important
  * repository).
  *
  * @task pull   Pulling Repositories
  */
 final class PhabricatorRepositoryPullLocalDaemon
   extends PhabricatorDaemon {
 
   private $statusMessageCursor = 0;
 
 /* -(  Pulling Repositories  )----------------------------------------------- */
 
 
   /**
    * @task pull
    */
   protected function run() {
     $argv = $this->getArgv();
     array_unshift($argv, __CLASS__);
     $args = new PhutilArgumentParser($argv);
     $args->parse(
       array(
         array(
           'name'      => 'no-discovery',
           'help'      => pht('Pull only, without discovering commits.'),
         ),
         array(
           'name'      => 'not',
           'param'     => 'repository',
           'repeat'    => true,
           'help'      => pht('Do not pull __repository__.'),
         ),
         array(
           'name'      => 'repositories',
           'wildcard'  => true,
           'help'      => pht('Pull specific __repositories__ instead of all.'),
         ),
       ));
 
     $no_discovery   = $args->getArg('no-discovery');
     $include = $args->getArg('repositories');
     $exclude = $args->getArg('not');
 
     // Each repository has an individual pull frequency; after we pull it,
     // wait that long to pull it again. When we start up, try to pull everything
     // serially.
     $retry_after = array();
 
     $min_sleep = 15;
     $max_futures = 4;
     $futures = array();
     $queue = array();
 
     while (!$this->shouldExit()) {
       PhabricatorCaches::destroyRequestCache();
       $device = AlmanacKeys::getLiveDevice();
 
       $pullable = $this->loadPullableRepositories($include, $exclude, $device);
 
       // If any repositories have the NEEDS_UPDATE flag set, pull them
       // as soon as possible.
       $need_update_messages = $this->loadRepositoryUpdateMessages(true);
       foreach ($need_update_messages as $message) {
         $repo = idx($pullable, $message->getRepositoryID());
         if (!$repo) {
           continue;
         }
 
         $this->log(
           pht(
             'Got an update message for repository "%s"!',
             $repo->getMonogram()));
 
         $retry_after[$message->getRepositoryID()] = time();
       }
 
       // If any repositories were deleted, remove them from the retry timer map
       // so we don't end up with a retry timer that never gets updated and
       // causes us to sleep for the minimum amount of time.
       $retry_after = array_select_keys(
         $retry_after,
         array_keys($pullable));
 
 
       // Figure out which repositories we need to queue for an update.
       foreach ($pullable as $id => $repository) {
         $monogram = $repository->getMonogram();
 
         if (isset($futures[$id])) {
           $this->log(pht('Repository "%s" is currently updating.', $monogram));
           continue;
         }
 
         if (isset($queue[$id])) {
           $this->log(pht('Repository "%s" is already queued.', $monogram));
           continue;
         }
 
         $after = idx($retry_after, $id, 0);
         if ($after > time()) {
           $this->log(
             pht(
               'Repository "%s" is not due for an update for %s second(s).',
               $monogram,
               new PhutilNumber($after - time())));
           continue;
         }
 
         if (!$after) {
           $this->log(
             pht(
               'Scheduling repository "%s" for an initial update.',
               $monogram));
         } else {
           $this->log(
             pht(
               'Scheduling repository "%s" for an update (%s seconds overdue).',
               $monogram,
               new PhutilNumber(time() - $after)));
         }
 
         $queue[$id] = $after;
       }
 
       // Process repositories in the order they became candidates for updates.
       asort($queue);
 
       // Dequeue repositories until we hit maximum parallelism.
       while ($queue && (count($futures) < $max_futures)) {
         foreach ($queue as $id => $time) {
           $repository = idx($pullable, $id);
           if (!$repository) {
             $this->log(
               pht('Repository %s is no longer pullable; skipping.', $id));
             unset($queue[$id]);
             continue;
           }
 
           $monogram = $repository->getMonogram();
           $this->log(pht('Starting update for repository "%s".', $monogram));
 
           unset($queue[$id]);
           $futures[$id] = $this->buildUpdateFuture(
             $repository,
             $no_discovery);
 
           break;
         }
       }
 
       if ($queue) {
         $this->log(
           pht(
             'Not enough process slots to schedule the other %s '.
             'repository(s) for updates yet.',
             phutil_count($queue)));
       }
 
       if ($futures) {
         $iterator = id(new FutureIterator($futures))
           ->setUpdateInterval($min_sleep);
 
         foreach ($iterator as $id => $future) {
           $this->stillWorking();
 
           if ($future === null) {
             $this->log(pht('Waiting for updates to complete...'));
             $this->stillWorking();
 
             if ($this->loadRepositoryUpdateMessages()) {
               $this->log(pht('Interrupted by pending updates!'));
               break;
             }
 
             continue;
           }
 
           unset($futures[$id]);
           $retry_after[$id] = $this->resolveUpdateFuture(
             $pullable[$id],
             $future,
             $min_sleep);
 
           // We have a free slot now, so go try to fill it.
           break;
         }
 
         // Jump back into prioritization if we had any futures to deal with.
         continue;
       }
 
       $this->waitForUpdates($min_sleep, $retry_after);
     }
 
   }
 
 
   /**
    * @task pull
    */
   private function buildUpdateFuture(
     PhabricatorRepository $repository,
     $no_discovery) {
 
     $bin = dirname(phutil_get_library_root('phabricator')).'/bin/repository';
 
     $flags = array();
     if ($no_discovery) {
       $flags[] = '--no-discovery';
     }
 
     $monogram = $repository->getMonogram();
     $future = new ExecFuture('%s update %Ls -- %s', $bin, $flags, $monogram);
 
     // Sometimes, the underlying VCS commands will hang indefinitely. We've
     // observed this occasionally with GitHub, and other users have observed
     // it with other VCS servers.
 
     // To limit the damage this can cause, kill the update out after a
     // reasonable amount of time, under the assumption that it has hung.
 
     // Since it's hard to know what a "reasonable" amount of time is given that
     // users may be downloading a repository full of pirated movies over a
     // potato, these limits are fairly generous. Repositories exceeding these
     // limits can be manually pulled with `bin/repository update X`, which can
     // just run for as long as it wants.
 
     if ($repository->isImporting()) {
       $timeout = phutil_units('4 hours in seconds');
     } else {
       $timeout = phutil_units('15 minutes in seconds');
     }
 
     $future->setTimeout($timeout);
 
     return $future;
   }
 
 
   /**
    * Check for repositories that should be updated immediately.
    *
    * With the `$consume` flag, an internal cursor will also be incremented so
    * that these messages are not returned by subsequent calls.
    *
    * @param bool Pass `true` to consume these messages, so the process will
    *   not see them again.
    * @return list<wild> Pending update messages.
    *
    * @task pull
    */
   private function loadRepositoryUpdateMessages($consume = false) {
     $type_need_update = PhabricatorRepositoryStatusMessage::TYPE_NEEDS_UPDATE;
     $messages = id(new PhabricatorRepositoryStatusMessage())->loadAllWhere(
       'statusType = %s AND id > %d',
       $type_need_update,
       $this->statusMessageCursor);
 
     // Keep track of messages we've seen so that we don't load them again.
     // If we reload messages, we can get stuck a loop if we have a failing
     // repository: we update immediately in response to the message, but do
     // not clear the message because the update does not succeed. We then
     // immediately retry. Instead, messages are only permitted to trigger
     // an immediate update once.
 
     if ($consume) {
       foreach ($messages as $message) {
         $this->statusMessageCursor = max(
           $this->statusMessageCursor,
           $message->getID());
       }
     }
 
     return $messages;
   }
 
 
   /**
    * @task pull
    */
   private function loadPullableRepositories(
     array $include,
     array $exclude,
     AlmanacDevice $device = null) {
 
     $query = id(new PhabricatorRepositoryQuery())
       ->setViewer($this->getViewer());
 
     if ($include) {
       $query->withIdentifiers($include);
     }
 
     $repositories = $query->execute();
     $repositories = mpull($repositories, null, 'getPHID');
 
     if ($include) {
       $map = $query->getIdentifierMap();
       foreach ($include as $identifier) {
         if (empty($map[$identifier])) {
           throw new Exception(
             pht(
               'No repository "%s" exists!',
               $identifier));
         }
       }
     }
 
     if ($exclude) {
       $xquery = id(new PhabricatorRepositoryQuery())
         ->setViewer($this->getViewer())
         ->withIdentifiers($exclude);
 
       $excluded_repos = $xquery->execute();
       $xmap = $xquery->getIdentifierMap();
 
       foreach ($exclude as $identifier) {
         if (empty($xmap[$identifier])) {
           throw new Exception(
             pht(
               'No repository "%s" exists!',
               $identifier));
         }
       }
 
       foreach ($excluded_repos as $excluded_repo) {
         unset($repositories[$excluded_repo->getPHID()]);
       }
     }
 
     foreach ($repositories as $key => $repository) {
       if (!$repository->isTracked()) {
         unset($repositories[$key]);
       }
     }
 
-    $service_phids = array();
-    foreach ($repositories as $key => $repository) {
-      $service_phid = $repository->getAlmanacServicePHID();
-
-      // If the repository is bound to a service but this host is not a
-      // recognized device, or vice versa, don't pull the repository.
-      $is_cluster_repo = (bool)$service_phid;
-      $is_cluster_device = (bool)$device;
-      if ($is_cluster_repo != $is_cluster_device) {
-        if ($is_cluster_device) {
-          $this->log(
-            pht(
-              'Repository "%s" is not a cluster repository, but the current '.
-              'host is a cluster device ("%s"), so the repository will not '.
-              'be updated on this host.',
-              $repository->getDisplayName(),
-              $device->getName()));
-        } else {
-          $this->log(
-            pht(
-              'Repository "%s" is a cluster repository, but the current '.
-              'host is not a cluster device (it has no device ID), so the '.
-              'repository will not be updated on this host.',
-              $repository->getDisplayName()));
-        }
-        unset($repositories[$key]);
-        continue;
-      }
-
-      if ($service_phid) {
-        $service_phids[] = $service_phid;
-      }
-    }
+    $viewer = $this->getViewer();
 
-    if ($device) {
-      $device_phid = $device->getPHID();
-
-      if ($service_phids) {
-        // We could include `withDevicePHIDs()` here to pull a smaller result
-        // set, but we can provide more helpful diagnostic messages below if
-        // we fetch a little more data.
-        $services = id(new AlmanacServiceQuery())
-          ->setViewer($this->getViewer())
-          ->withPHIDs($service_phids)
-          ->withServiceTypes(
-            array(
-              AlmanacClusterRepositoryServiceType::SERVICETYPE,
-            ))
-          ->needBindings(true)
-          ->execute();
-        $services = mpull($services, null, 'getPHID');
-      } else {
-        $services = array();
-      }
+    $filter = id(new DiffusionLocalRepositoryFilter())
+      ->setViewer($viewer)
+      ->setDevice($device)
+      ->setRepositories($repositories);
 
-      foreach ($repositories as $key => $repository) {
-        $service_phid = $repository->getAlmanacServicePHID();
+    $repositories = $filter->execute();
 
-        $service = idx($services, $service_phid);
-        if (!$service) {
-          $this->log(
-            pht(
-              'Repository "%s" is on cluster service "%s", but that service '.
-              'could not be loaded, so the repository will not be updated '.
-              'on this host.',
-              $repository->getDisplayName(),
-              $service_phid));
-          unset($repositories[$key]);
-          continue;
-        }
-
-        $bindings = $service->getBindings();
-        $bindings = mgroup($bindings, 'getDevicePHID');
-        $bindings = idx($bindings, $device_phid);
-        if (!$bindings) {
-          $this->log(
-            pht(
-              'Repository "%s" is on cluster service "%s", but that service '.
-              'is not bound to this device ("%s"), so the repository will '.
-              'not be updated on this host.',
-              $repository->getDisplayName(),
-              $service->getName(),
-              $device->getName()));
-          unset($repositories[$key]);
-          continue;
-        }
-
-        $all_disabled = true;
-        foreach ($bindings as $binding) {
-          if (!$binding->getIsDisabled()) {
-            $all_disabled = false;
-            break;
-          }
-        }
-
-        if ($all_disabled) {
-          $this->log(
-            pht(
-              'Repository "%s" is on cluster service "%s", but the binding '.
-              'between that service and this device ("%s") is disabled, so '.
-              'the not be updated on this host.',
-              $repository->getDisplayName(),
-              $service->getName(),
-              $device->getName()));
-          unset($repositories[$key]);
-          continue;
-        }
-
-        // We have a valid service that is actively bound to the current host
-        // device, so we're good to go.
-      }
+    foreach ($filter->getRejectionReasons() as $reason) {
+      $this->log($reason);
     }
 
     // Shuffle the repositories, then re-key the array since shuffle()
     // discards keys. This is mostly for startup, we'll use soft priorities
     // later.
     shuffle($repositories);
     $repositories = mpull($repositories, null, 'getID');
 
     return $repositories;
   }
 
 
   /**
    * @task pull
    */
   private function resolveUpdateFuture(
     PhabricatorRepository $repository,
     ExecFuture $future,
     $min_sleep) {
 
     $monogram = $repository->getMonogram();
 
     $this->log(pht('Resolving update for "%s".', $monogram));
 
     try {
       list($stdout, $stderr) = $future->resolvex();
     } catch (Exception $ex) {
       $proxy = new PhutilProxyException(
         pht(
           'Error while updating the "%s" repository.',
           $repository->getMonogram()),
         $ex);
       phlog($proxy);
 
       return time() + $min_sleep;
     }
 
     if (strlen($stderr)) {
       $stderr_msg = pht(
         'Unexpected output while updating repository "%s": %s',
         $monogram,
         $stderr);
       phlog($stderr_msg);
     }
 
     $smart_wait = $repository->loadUpdateInterval($min_sleep);
 
     $this->log(
       pht(
         'Based on activity in repository "%s", considering a wait of %s '.
         'seconds before update.',
         $repository->getMonogram(),
         new PhutilNumber($smart_wait)));
 
     return time() + $smart_wait;
   }
 
 
 
   /**
    * Sleep for a short period of time, waiting for update messages from the
    *
    *
    * @task pull
    */
   private function waitForUpdates($min_sleep, array $retry_after) {
     $this->log(
       pht('No repositories need updates right now, sleeping...'));
 
     $sleep_until = time() + $min_sleep;
     if ($retry_after) {
       $sleep_until = min($sleep_until, min($retry_after));
     }
 
     while (($sleep_until - time()) > 0) {
       $sleep_duration = ($sleep_until - time());
 
       $this->log(
         pht(
           'Sleeping for %s more second(s)...',
           new PhutilNumber($sleep_duration)));
 
       $this->sleep(1);
 
       if ($this->shouldExit()) {
         $this->log(pht('Awakened from sleep by graceful shutdown!'));
         return;
       }
 
       if ($this->loadRepositoryUpdateMessages()) {
         $this->log(pht('Awakened from sleep by pending updates!'));
         break;
       }
     }
   }
 
 }
diff --git a/src/applications/repository/data/PhabricatorRepositoryURINormalizer.php b/src/applications/repository/data/PhabricatorRepositoryURINormalizer.php
index 3a9508e19..42224c955 100644
--- a/src/applications/repository/data/PhabricatorRepositoryURINormalizer.php
+++ b/src/applications/repository/data/PhabricatorRepositoryURINormalizer.php
@@ -1,123 +1,156 @@
 <?php
 
 /**
  * Normalize repository URIs. For example, these URIs are generally equivalent
  * and all point at the same repository:
  *
  *   ssh://user@host/repo
  *   ssh://user@host/repo/
  *   ssh://user@host:22/repo
  *   user@host:/repo
  *   ssh://user@host/repo.git
  *
  * This class can be used to normalize URIs like this, in order to detect
  * alternate spellings of the same repository URI. In particular, the
  * @{method:getNormalizedPath} method will return:
  *
  *   repo
  *
  * ...for all of these URIs. Generally, usage looks like this:
  *
  *   $norm_a = new PhabricatorRepositoryURINormalizer($type, $uri_a);
  *   $norm_b = new PhabricatorRepositoryURINormalizer($type, $uri_b);
  *
  *   if ($norm_a->getNormalizedPath() == $norm_b->getNormalizedPath()) {
  *     // URIs appear to point at the same repository.
  *   } else {
  *     // URIs are very unlikely to be the same repository.
  *   }
  *
  * Because a repository can be hosted at arbitrarly many arbitrary URIs, there
  * is no way to completely prevent false negatives by only examining URIs
  * (that is, repositories with totally different URIs could really be the same).
  * However, normalization is relatively agressive and false negatives should
  * be rare: if normalization says two URIs are different repositories, they
  * probably are.
  *
  * @task normal Normalizing URIs
  */
 final class PhabricatorRepositoryURINormalizer extends Phobject {
 
   const TYPE_GIT = 'git';
   const TYPE_SVN = 'svn';
   const TYPE_MERCURIAL = 'hg';
 
   private $type;
   private $uri;
 
   public function __construct($type, $uri) {
     switch ($type) {
       case self::TYPE_GIT:
       case self::TYPE_SVN:
       case self::TYPE_MERCURIAL:
         break;
       default:
         throw new Exception(pht('Unknown URI type "%s"!', $type));
     }
 
     $this->type = $type;
     $this->uri = $uri;
   }
 
+  public static function getAllURITypes() {
+    return array(
+      self::TYPE_GIT,
+      self::TYPE_SVN,
+      self::TYPE_MERCURIAL,
+    );
+  }
+
 
 /* -(  Normalizing URIs  )--------------------------------------------------- */
 
 
   /**
    * @task normal
    */
   public function getPath() {
     switch ($this->type) {
       case self::TYPE_GIT:
         $uri = new PhutilURI($this->uri);
         if ($uri->getProtocol()) {
           return $uri->getPath();
         }
 
         $uri = new PhutilGitURI($this->uri);
         if ($uri->getDomain()) {
           return $uri->getPath();
         }
 
         return $this->uri;
       case self::TYPE_SVN:
       case self::TYPE_MERCURIAL:
         $uri = new PhutilURI($this->uri);
         if ($uri->getProtocol()) {
           return $uri->getPath();
         }
 
         return $this->uri;
     }
   }
 
+  public function getNormalizedURI() {
+    return $this->getNormalizedDomain().'/'.$this->getNormalizedPath();
+  }
+
 
   /**
    * @task normal
    */
   public function getNormalizedPath() {
     $path = $this->getPath();
     $path = trim($path, '/');
 
     switch ($this->type) {
       case self::TYPE_GIT:
         $path = preg_replace('/\.git$/', '', $path);
         break;
       case self::TYPE_SVN:
       case self::TYPE_MERCURIAL:
         break;
     }
 
     // If this is a Phabricator URI, strip it down to the callsign. We mutably
     // allow you to clone repositories as "/diffusion/X/anything.git", for
     // example.
 
     $matches = null;
-    if (preg_match('@^(diffusion/[A-Z]+)@', $path, $matches)) {
+    if (preg_match('@^(diffusion/(?:[A-Z]+|\d+))@', $path, $matches)) {
       $path = $matches[1];
     }
 
     return $path;
   }
 
+  public function getNormalizedDomain() {
+    $domain = null;
+
+    $uri = new PhutilURI($this->uri);
+    if ($uri->getProtocol()) {
+      $domain = $uri->getDomain();
+    }
+
+    if (!strlen($domain)) {
+      $uri = new PhutilGitURI($this->uri);
+      $domain = $uri->getDomain();
+    }
+
+    if (!strlen($domain)) {
+      $domain = '<void>';
+    }
+
+    return phutil_utf8_strtolower($domain);
+  }
+
+
 }
diff --git a/src/applications/repository/editor/PhabricatorRepositoryEditor.php b/src/applications/repository/editor/PhabricatorRepositoryEditor.php
index 3dc842d94..43c95f6b4 100644
--- a/src/applications/repository/editor/PhabricatorRepositoryEditor.php
+++ b/src/applications/repository/editor/PhabricatorRepositoryEditor.php
@@ -1,733 +1,640 @@
 <?php
 
 final class PhabricatorRepositoryEditor
   extends PhabricatorApplicationTransactionEditor {
 
   public function getEditorApplicationClass() {
     return 'PhabricatorDiffusionApplication';
   }
 
   public function getEditorObjectsDescription() {
     return pht('Repositories');
   }
 
   public function getTransactionTypes() {
     $types = parent::getTransactionTypes();
 
     $types[] = PhabricatorRepositoryTransaction::TYPE_VCS;
     $types[] = PhabricatorRepositoryTransaction::TYPE_ACTIVATE;
     $types[] = PhabricatorRepositoryTransaction::TYPE_NAME;
     $types[] = PhabricatorRepositoryTransaction::TYPE_DESCRIPTION;
     $types[] = PhabricatorRepositoryTransaction::TYPE_ENCODING;
     $types[] = PhabricatorRepositoryTransaction::TYPE_DEFAULT_BRANCH;
     $types[] = PhabricatorRepositoryTransaction::TYPE_TRACK_ONLY;
     $types[] = PhabricatorRepositoryTransaction::TYPE_AUTOCLOSE_ONLY;
     $types[] = PhabricatorRepositoryTransaction::TYPE_UUID;
     $types[] = PhabricatorRepositoryTransaction::TYPE_SVN_SUBPATH;
     $types[] = PhabricatorRepositoryTransaction::TYPE_NOTIFY;
     $types[] = PhabricatorRepositoryTransaction::TYPE_AUTOCLOSE;
-    $types[] = PhabricatorRepositoryTransaction::TYPE_REMOTE_URI;
-    $types[] = PhabricatorRepositoryTransaction::TYPE_SSH_LOGIN;
-    $types[] = PhabricatorRepositoryTransaction::TYPE_SSH_KEY;
-    $types[] = PhabricatorRepositoryTransaction::TYPE_SSH_KEYFILE;
-    $types[] = PhabricatorRepositoryTransaction::TYPE_HTTP_LOGIN;
-    $types[] = PhabricatorRepositoryTransaction::TYPE_HTTP_PASS;
-    $types[] = PhabricatorRepositoryTransaction::TYPE_LOCAL_PATH;
-    $types[] = PhabricatorRepositoryTransaction::TYPE_HOSTING;
-    $types[] = PhabricatorRepositoryTransaction::TYPE_PROTOCOL_HTTP;
-    $types[] = PhabricatorRepositoryTransaction::TYPE_PROTOCOL_SSH;
     $types[] = PhabricatorRepositoryTransaction::TYPE_PUSH_POLICY;
-    $types[] = PhabricatorRepositoryTransaction::TYPE_CREDENTIAL;
     $types[] = PhabricatorRepositoryTransaction::TYPE_DANGEROUS;
     $types[] = PhabricatorRepositoryTransaction::TYPE_SLUG;
     $types[] = PhabricatorRepositoryTransaction::TYPE_SERVICE;
     $types[] = PhabricatorRepositoryTransaction::TYPE_SYMBOLS_LANGUAGE;
     $types[] = PhabricatorRepositoryTransaction::TYPE_SYMBOLS_SOURCES;
     $types[] = PhabricatorRepositoryTransaction::TYPE_STAGING_URI;
     $types[] = PhabricatorRepositoryTransaction::TYPE_AUTOMATION_BLUEPRINTS;
     $types[] = PhabricatorRepositoryTransaction::TYPE_CALLSIGN;
 
     $types[] = PhabricatorTransactions::TYPE_EDGE;
     $types[] = PhabricatorTransactions::TYPE_VIEW_POLICY;
     $types[] = PhabricatorTransactions::TYPE_EDIT_POLICY;
 
     return $types;
   }
 
   protected function getCustomTransactionOldValue(
     PhabricatorLiskDAO $object,
     PhabricatorApplicationTransaction $xaction) {
 
     switch ($xaction->getTransactionType()) {
       case PhabricatorRepositoryTransaction::TYPE_VCS:
         return $object->getVersionControlSystem();
       case PhabricatorRepositoryTransaction::TYPE_ACTIVATE:
         return $object->isTracked();
       case PhabricatorRepositoryTransaction::TYPE_NAME:
         return $object->getName();
       case PhabricatorRepositoryTransaction::TYPE_DESCRIPTION:
         return $object->getDetail('description');
       case PhabricatorRepositoryTransaction::TYPE_ENCODING:
         return $object->getDetail('encoding');
       case PhabricatorRepositoryTransaction::TYPE_DEFAULT_BRANCH:
         return $object->getDetail('default-branch');
       case PhabricatorRepositoryTransaction::TYPE_TRACK_ONLY:
         return array_keys($object->getDetail('branch-filter', array()));
       case PhabricatorRepositoryTransaction::TYPE_AUTOCLOSE_ONLY:
         return array_keys($object->getDetail('close-commits-filter', array()));
       case PhabricatorRepositoryTransaction::TYPE_UUID:
         return $object->getUUID();
       case PhabricatorRepositoryTransaction::TYPE_SVN_SUBPATH:
         return $object->getDetail('svn-subpath');
       case PhabricatorRepositoryTransaction::TYPE_NOTIFY:
         return (int)!$object->getDetail('herald-disabled');
       case PhabricatorRepositoryTransaction::TYPE_AUTOCLOSE:
         return (int)!$object->getDetail('disable-autoclose');
-      case PhabricatorRepositoryTransaction::TYPE_REMOTE_URI:
-        return $object->getDetail('remote-uri');
-      case PhabricatorRepositoryTransaction::TYPE_LOCAL_PATH:
-        return $object->getDetail('local-path');
-      case PhabricatorRepositoryTransaction::TYPE_HOSTING:
-        return $object->isHosted();
-      case PhabricatorRepositoryTransaction::TYPE_PROTOCOL_HTTP:
-        return $object->getServeOverHTTP();
-      case PhabricatorRepositoryTransaction::TYPE_PROTOCOL_SSH:
-        return $object->getServeOverSSH();
       case PhabricatorRepositoryTransaction::TYPE_PUSH_POLICY:
         return $object->getPushPolicy();
-      case PhabricatorRepositoryTransaction::TYPE_CREDENTIAL:
-        return $object->getCredentialPHID();
       case PhabricatorRepositoryTransaction::TYPE_DANGEROUS:
         return $object->shouldAllowDangerousChanges();
       case PhabricatorRepositoryTransaction::TYPE_SLUG:
         return $object->getRepositorySlug();
       case PhabricatorRepositoryTransaction::TYPE_SERVICE:
         return $object->getAlmanacServicePHID();
       case PhabricatorRepositoryTransaction::TYPE_SYMBOLS_LANGUAGE:
         return $object->getSymbolLanguages();
       case PhabricatorRepositoryTransaction::TYPE_SYMBOLS_SOURCES:
         return $object->getSymbolSources();
       case PhabricatorRepositoryTransaction::TYPE_STAGING_URI:
         return $object->getDetail('staging-uri');
       case PhabricatorRepositoryTransaction::TYPE_AUTOMATION_BLUEPRINTS:
         return $object->getDetail('automation.blueprintPHIDs', array());
       case PhabricatorRepositoryTransaction::TYPE_CALLSIGN:
         return $object->getCallsign();
     }
   }
 
   protected function getCustomTransactionNewValue(
     PhabricatorLiskDAO $object,
     PhabricatorApplicationTransaction $xaction) {
 
     switch ($xaction->getTransactionType()) {
       case PhabricatorRepositoryTransaction::TYPE_ACTIVATE:
       case PhabricatorRepositoryTransaction::TYPE_NAME:
       case PhabricatorRepositoryTransaction::TYPE_DESCRIPTION:
       case PhabricatorRepositoryTransaction::TYPE_ENCODING:
       case PhabricatorRepositoryTransaction::TYPE_DEFAULT_BRANCH:
       case PhabricatorRepositoryTransaction::TYPE_TRACK_ONLY:
       case PhabricatorRepositoryTransaction::TYPE_AUTOCLOSE_ONLY:
       case PhabricatorRepositoryTransaction::TYPE_UUID:
       case PhabricatorRepositoryTransaction::TYPE_SVN_SUBPATH:
-      case PhabricatorRepositoryTransaction::TYPE_REMOTE_URI:
-      case PhabricatorRepositoryTransaction::TYPE_SSH_LOGIN:
-      case PhabricatorRepositoryTransaction::TYPE_SSH_KEY:
-      case PhabricatorRepositoryTransaction::TYPE_SSH_KEYFILE:
-      case PhabricatorRepositoryTransaction::TYPE_HTTP_LOGIN:
-      case PhabricatorRepositoryTransaction::TYPE_HTTP_PASS:
-      case PhabricatorRepositoryTransaction::TYPE_LOCAL_PATH:
       case PhabricatorRepositoryTransaction::TYPE_VCS:
-      case PhabricatorRepositoryTransaction::TYPE_HOSTING:
-      case PhabricatorRepositoryTransaction::TYPE_PROTOCOL_HTTP:
-      case PhabricatorRepositoryTransaction::TYPE_PROTOCOL_SSH:
       case PhabricatorRepositoryTransaction::TYPE_PUSH_POLICY:
-      case PhabricatorRepositoryTransaction::TYPE_CREDENTIAL:
       case PhabricatorRepositoryTransaction::TYPE_DANGEROUS:
       case PhabricatorRepositoryTransaction::TYPE_SERVICE:
       case PhabricatorRepositoryTransaction::TYPE_SYMBOLS_LANGUAGE:
       case PhabricatorRepositoryTransaction::TYPE_SYMBOLS_SOURCES:
       case PhabricatorRepositoryTransaction::TYPE_STAGING_URI:
       case PhabricatorRepositoryTransaction::TYPE_AUTOMATION_BLUEPRINTS:
         return $xaction->getNewValue();
       case PhabricatorRepositoryTransaction::TYPE_SLUG:
       case PhabricatorRepositoryTransaction::TYPE_CALLSIGN:
         $name = $xaction->getNewValue();
         if (strlen($name)) {
           return $name;
         }
         return null;
       case PhabricatorRepositoryTransaction::TYPE_NOTIFY:
       case PhabricatorRepositoryTransaction::TYPE_AUTOCLOSE:
         return (int)$xaction->getNewValue();
     }
   }
 
   protected function applyCustomInternalTransaction(
     PhabricatorLiskDAO $object,
     PhabricatorApplicationTransaction $xaction) {
 
     switch ($xaction->getTransactionType()) {
       case PhabricatorRepositoryTransaction::TYPE_VCS:
         $object->setVersionControlSystem($xaction->getNewValue());
         break;
       case PhabricatorRepositoryTransaction::TYPE_ACTIVATE:
-        $object->setDetail('tracking-enabled', $xaction->getNewValue());
+        $active = $xaction->getNewValue();
+
+        // The first time a repository is activated, clear the "new repository"
+        // flag so we stop showing setup hints.
+        if ($active) {
+          $object->setDetail('newly-initialized', false);
+        }
+
+        $object->setDetail('tracking-enabled', $active);
         break;
       case PhabricatorRepositoryTransaction::TYPE_NAME:
         $object->setName($xaction->getNewValue());
         break;
       case PhabricatorRepositoryTransaction::TYPE_DESCRIPTION:
         $object->setDetail('description', $xaction->getNewValue());
         break;
       case PhabricatorRepositoryTransaction::TYPE_DEFAULT_BRANCH:
         $object->setDetail('default-branch', $xaction->getNewValue());
         break;
       case PhabricatorRepositoryTransaction::TYPE_TRACK_ONLY:
         $object->setDetail(
           'branch-filter',
           array_fill_keys($xaction->getNewValue(), true));
         break;
       case PhabricatorRepositoryTransaction::TYPE_AUTOCLOSE_ONLY:
         $object->setDetail(
           'close-commits-filter',
           array_fill_keys($xaction->getNewValue(), true));
         break;
       case PhabricatorRepositoryTransaction::TYPE_UUID:
         $object->setUUID($xaction->getNewValue());
         break;
       case PhabricatorRepositoryTransaction::TYPE_SVN_SUBPATH:
         $object->setDetail('svn-subpath', $xaction->getNewValue());
         break;
       case PhabricatorRepositoryTransaction::TYPE_NOTIFY:
         $object->setDetail('herald-disabled', (int)!$xaction->getNewValue());
         break;
       case PhabricatorRepositoryTransaction::TYPE_AUTOCLOSE:
         $object->setDetail('disable-autoclose', (int)!$xaction->getNewValue());
         break;
-      case PhabricatorRepositoryTransaction::TYPE_REMOTE_URI:
-        $object->setDetail('remote-uri', $xaction->getNewValue());
-        break;
-      case PhabricatorRepositoryTransaction::TYPE_LOCAL_PATH:
-        $object->setDetail('local-path', $xaction->getNewValue());
-        break;
-      case PhabricatorRepositoryTransaction::TYPE_HOSTING:
-        return $object->setHosted($xaction->getNewValue());
-      case PhabricatorRepositoryTransaction::TYPE_PROTOCOL_HTTP:
-        return $object->setServeOverHTTP($xaction->getNewValue());
-      case PhabricatorRepositoryTransaction::TYPE_PROTOCOL_SSH:
-        return $object->setServeOverSSH($xaction->getNewValue());
       case PhabricatorRepositoryTransaction::TYPE_PUSH_POLICY:
         return $object->setPushPolicy($xaction->getNewValue());
-      case PhabricatorRepositoryTransaction::TYPE_CREDENTIAL:
-        return $object->setCredentialPHID($xaction->getNewValue());
       case PhabricatorRepositoryTransaction::TYPE_DANGEROUS:
         $object->setDetail('allow-dangerous-changes', $xaction->getNewValue());
         return;
       case PhabricatorRepositoryTransaction::TYPE_SLUG:
         $object->setRepositorySlug($xaction->getNewValue());
         return;
       case PhabricatorRepositoryTransaction::TYPE_SERVICE:
         $object->setAlmanacServicePHID($xaction->getNewValue());
         return;
       case PhabricatorRepositoryTransaction::TYPE_SYMBOLS_LANGUAGE:
         $object->setDetail('symbol-languages', $xaction->getNewValue());
         return;
       case PhabricatorRepositoryTransaction::TYPE_SYMBOLS_SOURCES:
         $object->setDetail('symbol-sources', $xaction->getNewValue());
         return;
       case PhabricatorRepositoryTransaction::TYPE_STAGING_URI:
         $object->setDetail('staging-uri', $xaction->getNewValue());
         return;
       case PhabricatorRepositoryTransaction::TYPE_AUTOMATION_BLUEPRINTS:
         $object->setDetail(
           'automation.blueprintPHIDs',
           $xaction->getNewValue());
         return;
       case PhabricatorRepositoryTransaction::TYPE_CALLSIGN:
         $object->setCallsign($xaction->getNewValue());
         return;
       case PhabricatorRepositoryTransaction::TYPE_ENCODING:
         $object->setDetail('encoding', $xaction->getNewValue());
         break;
     }
   }
 
   protected function applyCustomExternalTransaction(
     PhabricatorLiskDAO $object,
     PhabricatorApplicationTransaction $xaction) {
 
     switch ($xaction->getTransactionType()) {
-      case PhabricatorRepositoryTransaction::TYPE_CREDENTIAL:
-        // Adjust the object <-> credential edge for this repository.
-
-        $old_phid = $xaction->getOldValue();
-        $new_phid = $xaction->getNewValue();
-
-        $editor = new PhabricatorEdgeEditor();
-
-        $edge_type = PhabricatorObjectUsesCredentialsEdgeType::EDGECONST;
-        $src_phid = $object->getPHID();
-
-        if ($old_phid) {
-          $editor->removeEdge($src_phid, $edge_type, $old_phid);
-        }
-
-        if ($new_phid) {
-          $editor->addEdge($src_phid, $edge_type, $new_phid);
-        }
-
-        $editor->save();
-        break;
       case PhabricatorRepositoryTransaction::TYPE_AUTOMATION_BLUEPRINTS:
         DrydockAuthorization::applyAuthorizationChanges(
           $this->getActor(),
           $object->getPHID(),
           $xaction->getOldValue(),
           $xaction->getNewValue());
         break;
     }
 
   }
 
   protected function requireCapabilities(
     PhabricatorLiskDAO $object,
     PhabricatorApplicationTransaction $xaction) {
 
     switch ($xaction->getTransactionType()) {
       case PhabricatorRepositoryTransaction::TYPE_ACTIVATE:
       case PhabricatorRepositoryTransaction::TYPE_NAME:
       case PhabricatorRepositoryTransaction::TYPE_DESCRIPTION:
       case PhabricatorRepositoryTransaction::TYPE_ENCODING:
       case PhabricatorRepositoryTransaction::TYPE_DEFAULT_BRANCH:
       case PhabricatorRepositoryTransaction::TYPE_TRACK_ONLY:
       case PhabricatorRepositoryTransaction::TYPE_AUTOCLOSE_ONLY:
       case PhabricatorRepositoryTransaction::TYPE_UUID:
       case PhabricatorRepositoryTransaction::TYPE_SVN_SUBPATH:
-      case PhabricatorRepositoryTransaction::TYPE_REMOTE_URI:
-      case PhabricatorRepositoryTransaction::TYPE_SSH_LOGIN:
-      case PhabricatorRepositoryTransaction::TYPE_SSH_KEY:
-      case PhabricatorRepositoryTransaction::TYPE_SSH_KEYFILE:
-      case PhabricatorRepositoryTransaction::TYPE_HTTP_LOGIN:
-      case PhabricatorRepositoryTransaction::TYPE_HTTP_PASS:
-      case PhabricatorRepositoryTransaction::TYPE_LOCAL_PATH:
       case PhabricatorRepositoryTransaction::TYPE_VCS:
       case PhabricatorRepositoryTransaction::TYPE_NOTIFY:
       case PhabricatorRepositoryTransaction::TYPE_AUTOCLOSE:
-      case PhabricatorRepositoryTransaction::TYPE_HOSTING:
-      case PhabricatorRepositoryTransaction::TYPE_PROTOCOL_HTTP:
-      case PhabricatorRepositoryTransaction::TYPE_PROTOCOL_SSH:
       case PhabricatorRepositoryTransaction::TYPE_PUSH_POLICY:
-      case PhabricatorRepositoryTransaction::TYPE_CREDENTIAL:
       case PhabricatorRepositoryTransaction::TYPE_DANGEROUS:
       case PhabricatorRepositoryTransaction::TYPE_SLUG:
       case PhabricatorRepositoryTransaction::TYPE_SERVICE:
       case PhabricatorRepositoryTransaction::TYPE_SYMBOLS_SOURCES:
       case PhabricatorRepositoryTransaction::TYPE_SYMBOLS_LANGUAGE:
       case PhabricatorRepositoryTransaction::TYPE_STAGING_URI:
       case PhabricatorRepositoryTransaction::TYPE_AUTOMATION_BLUEPRINTS:
         PhabricatorPolicyFilter::requireCapability(
           $this->requireActor(),
           $object,
           PhabricatorPolicyCapability::CAN_EDIT);
         break;
     }
   }
 
   protected function validateTransaction(
     PhabricatorLiskDAO $object,
     $type,
     array $xactions) {
 
     $errors = parent::validateTransaction($object, $type, $xactions);
 
     switch ($type) {
       case PhabricatorRepositoryTransaction::TYPE_AUTOCLOSE_ONLY:
       case PhabricatorRepositoryTransaction::TYPE_TRACK_ONLY:
         foreach ($xactions as $xaction) {
           foreach ($xaction->getNewValue() as $pattern) {
             // Check for invalid regular expressions.
             $regexp = PhabricatorRepository::extractBranchRegexp($pattern);
             if ($regexp !== null) {
               $ok = @preg_match($regexp, '');
               if ($ok === false) {
                 $error = new PhabricatorApplicationTransactionValidationError(
                   $type,
                   pht('Invalid'),
                   pht(
                     'Expression "%s" is not a valid regular expression. Note '.
                     'that you must include delimiters.',
                     $regexp),
                   $xaction);
                 $errors[] = $error;
                 continue;
               }
             }
 
             // Check for formatting mistakes like `regex(...)` instead of
             // `regexp(...)`.
             $matches = null;
             if (preg_match('/^([^(]+)\\(.*\\)\z/', $pattern, $matches)) {
               switch ($matches[1]) {
                 case 'regexp':
                   break;
                 default:
                   $error = new PhabricatorApplicationTransactionValidationError(
                     $type,
                     pht('Invalid'),
                     pht(
                       'Matching function "%s(...)" is not recognized. Valid '.
                       'functions are: regexp(...).',
                       $matches[1]),
                     $xaction);
                   $errors[] = $error;
                   break;
               }
             }
           }
         }
         break;
 
-      case PhabricatorRepositoryTransaction::TYPE_REMOTE_URI:
-        foreach ($xactions as $xaction) {
-          $new_uri = $xaction->getNewValue();
-          try {
-            PhabricatorRepository::assertValidRemoteURI($new_uri);
-          } catch (Exception $ex) {
-            $errors[] = new PhabricatorApplicationTransactionValidationError(
-              $type,
-              pht('Invalid'),
-              $ex->getMessage(),
-              $xaction);
-          }
-        }
-        break;
-
-      case PhabricatorRepositoryTransaction::TYPE_CREDENTIAL:
-        $ok = PassphraseCredentialControl::validateTransactions(
-          $this->getActor(),
-          $xactions);
-        if (!$ok) {
-          foreach ($xactions as $xaction) {
-            $errors[] = new PhabricatorApplicationTransactionValidationError(
-              $type,
-              pht('Invalid'),
-              pht(
-                'The selected credential does not exist, or you do not have '.
-                'permission to use it.'),
-              $xaction);
-          }
-        }
-        break;
-
       case PhabricatorRepositoryTransaction::TYPE_AUTOMATION_BLUEPRINTS:
         foreach ($xactions as $xaction) {
           $old = nonempty($xaction->getOldValue(), array());
           $new = nonempty($xaction->getNewValue(), array());
 
           $add = array_diff($new, $old);
 
           $invalid = PhabricatorObjectQuery::loadInvalidPHIDsForViewer(
             $this->getActor(),
             $add);
           if ($invalid) {
             $errors[] = new PhabricatorApplicationTransactionValidationError(
               $type,
               pht('Invalid'),
               pht(
                 'Some of the selected automation blueprints are invalid '.
                 'or restricted: %s.',
                 implode(', ', $invalid)),
               $xaction);
           }
         }
         break;
 
       case PhabricatorRepositoryTransaction::TYPE_VCS:
         $vcs_map = PhabricatorRepositoryType::getAllRepositoryTypes();
         $current_vcs = $object->getVersionControlSystem();
 
         if (!$this->getIsNewObject()) {
           foreach ($xactions as $xaction) {
             if ($xaction->getNewValue() == $current_vcs) {
               continue;
             }
 
             $errors[] = new PhabricatorApplicationTransactionValidationError(
               $type,
               pht('Immutable'),
               pht(
                 'You can not change the version control system an existing '.
                 'repository uses. It can only be set when a repository is '.
                 'first created.'),
               $xaction);
           }
         } else {
           $value = $object->getVersionControlSystem();
           foreach ($xactions as $xaction) {
             $value = $xaction->getNewValue();
 
             if (empty($vcs_map[$value])) {
               $errors[] = new PhabricatorApplicationTransactionValidationError(
                 $type,
                 pht('Invalid'),
                 pht(
                   'Specified version control system must be a VCS '.
                   'recognized by Phabricator: %s.',
                   implode(', ', array_keys($vcs_map))),
                 $xaction);
             }
           }
 
           if (!strlen($value)) {
             $error = new PhabricatorApplicationTransactionValidationError(
               $type,
               pht('Required'),
               pht(
                 'When creating a repository, you must specify a valid '.
                 'underlying version control system: %s.',
                 implode(', ', array_keys($vcs_map))),
               nonempty(last($xactions), null));
             $error->setIsMissingFieldError(true);
             $errors[] = $error;
           }
         }
         break;
 
       case PhabricatorRepositoryTransaction::TYPE_NAME:
         $missing = $this->validateIsEmptyTextField(
           $object->getName(),
           $xactions);
 
         if ($missing) {
           $error = new PhabricatorApplicationTransactionValidationError(
             $type,
             pht('Required'),
             pht('Repository name is required.'),
             nonempty(last($xactions), null));
 
           $error->setIsMissingFieldError(true);
           $errors[] = $error;
         }
         break;
 
       case PhabricatorRepositoryTransaction::TYPE_ACTIVATE:
         $status_map = PhabricatorRepository::getStatusMap();
         foreach ($xactions as $xaction) {
           $status = $xaction->getNewValue();
           if (empty($status_map[$status])) {
             $errors[] = new PhabricatorApplicationTransactionValidationError(
               $type,
               pht('Invalid'),
               pht(
                 'Repository status "%s" is not valid.',
                 $status),
               $xaction);
           }
         }
         break;
 
       case PhabricatorRepositoryTransaction::TYPE_ENCODING:
         foreach ($xactions as $xaction) {
           // Make sure the encoding is valid by converting to UTF-8. This tests
           // that the user has mbstring installed, and also that they didn't
           // type a garbage encoding name. Note that we're converting from
           // UTF-8 to the target encoding, because mbstring is fine with
           // converting from a nonsense encoding.
           $encoding = $xaction->getNewValue();
           if (!strlen($encoding)) {
             continue;
           }
 
           try {
             phutil_utf8_convert('.', $encoding, 'UTF-8');
           } catch (Exception $ex) {
             $errors[] = new PhabricatorApplicationTransactionValidationError(
               $type,
               pht('Invalid'),
               pht(
                 'Repository encoding "%s" is not valid: %s',
                 $encoding,
                 $ex->getMessage()),
               $xaction);
           }
         }
         break;
 
       case PhabricatorRepositoryTransaction::TYPE_SLUG:
         foreach ($xactions as $xaction) {
           $old = $xaction->getOldValue();
           $new = $xaction->getNewValue();
 
           if (!strlen($new)) {
             continue;
           }
 
           if ($new === $old) {
             continue;
           }
 
           try {
             PhabricatorRepository::assertValidRepositorySlug($new);
           } catch (Exception $ex) {
             $errors[] = new PhabricatorApplicationTransactionValidationError(
               $type,
               pht('Invalid'),
               $ex->getMessage(),
               $xaction);
             continue;
           }
 
           $other = id(new PhabricatorRepositoryQuery())
             ->setViewer(PhabricatorUser::getOmnipotentUser())
             ->withSlugs(array($new))
             ->executeOne();
           if ($other && ($other->getID() !== $object->getID())) {
             $errors[] = new PhabricatorApplicationTransactionValidationError(
               $type,
               pht('Duplicate'),
               pht(
                 'The selected repository short name is already in use by '.
                 'another repository. Choose a unique short name.'),
               $xaction);
             continue;
           }
         }
         break;
 
       case PhabricatorRepositoryTransaction::TYPE_CALLSIGN:
         foreach ($xactions as $xaction) {
           $old = $xaction->getOldValue();
           $new = $xaction->getNewValue();
 
           if (!strlen($new)) {
             continue;
           }
 
           if ($new === $old) {
             continue;
           }
 
           try {
             PhabricatorRepository::assertValidCallsign($new);
           } catch (Exception $ex) {
             $errors[] = new PhabricatorApplicationTransactionValidationError(
               $type,
               pht('Invalid'),
               $ex->getMessage(),
               $xaction);
             continue;
           }
 
           $other = id(new PhabricatorRepositoryQuery())
             ->setViewer(PhabricatorUser::getOmnipotentUser())
             ->withCallsigns(array($new))
             ->executeOne();
           if ($other && ($other->getID() !== $object->getID())) {
             $errors[] = new PhabricatorApplicationTransactionValidationError(
               $type,
               pht('Duplicate'),
               pht(
                 'The selected callsign ("%s") is already in use by another '.
                 'repository. Choose a unique callsign.',
                 $new),
               $xaction);
             continue;
           }
         }
         break;
 
       case PhabricatorRepositoryTransaction::TYPE_SYMBOLS_SOURCES:
         foreach ($xactions as $xaction) {
           $old = $object->getSymbolSources();
           $new = $xaction->getNewValue();
 
           // If the viewer is adding new repositories, make sure they are
           // valid and visible.
           $add = array_diff($new, $old);
           if (!$add) {
             continue;
           }
 
           $repositories = id(new PhabricatorRepositoryQuery())
             ->setViewer($this->getActor())
             ->withPHIDs($add)
             ->execute();
           $repositories = mpull($repositories, null, 'getPHID');
 
           foreach ($add as $phid) {
             if (isset($repositories[$phid])) {
               continue;
             }
 
             $errors[] = new PhabricatorApplicationTransactionValidationError(
               $type,
               pht('Invalid'),
               pht(
                 'Repository ("%s") does not exist, or you do not have '.
                 'permission to see it.',
                 $phid),
               $xaction);
             break;
           }
         }
         break;
 
     }
 
     return $errors;
   }
 
   protected function didCatchDuplicateKeyException(
     PhabricatorLiskDAO $object,
     array $xactions,
     Exception $ex) {
 
     $errors = array();
 
     $errors[] = new PhabricatorApplicationTransactionValidationError(
       null,
       pht('Invalid'),
       pht(
         'The chosen callsign or repository short name is already in '.
         'use by another repository.'),
       null);
 
     throw new PhabricatorApplicationTransactionValidationException($errors);
   }
 
   protected function supportsSearch() {
     return true;
   }
 
   protected function applyFinalEffects(
     PhabricatorLiskDAO $object,
     array $xactions) {
 
     // If the repository does not have a local path yet, assign it one based
     // on its ID. We can't do this earlier because we won't have an ID yet.
-    $local_path = $object->getDetail('local-path');
+    $local_path = $object->getLocalPath();
     if (!strlen($local_path)) {
       $local_key = 'repository.default-local-path';
 
       $local_root = PhabricatorEnv::getEnvConfig($local_key);
       $local_root = rtrim($local_root, '/');
 
       $id = $object->getID();
       $local_path = "{$local_root}/{$id}/";
 
-      $object->setDetail('local-path', $local_path);
+      $object->setLocalPath($local_path);
       $object->save();
     }
 
     if ($this->getIsNewObject()) {
+      // The default state of repositories is to be hosted, if they are
+      // enabled without configuring any "Observe" URIs.
+      $object->setHosted(true);
+      $object->save();
+
+      // Create this repository's builtin URIs.
+      $builtin_uris = $object->newBuiltinURIs();
+      foreach ($builtin_uris as $uri) {
+        $uri->save();
+      }
+
       id(new DiffusionRepositoryClusterEngine())
         ->setViewer($this->getActor())
         ->setRepository($object)
         ->synchronizeWorkingCopyAfterCreation();
     }
 
     return $xactions;
   }
 
 }
diff --git a/src/applications/repository/engine/PhabricatorRepositoryDiscoveryEngine.php b/src/applications/repository/engine/PhabricatorRepositoryDiscoveryEngine.php
index 3c3a0526b..78a40986e 100644
--- a/src/applications/repository/engine/PhabricatorRepositoryDiscoveryEngine.php
+++ b/src/applications/repository/engine/PhabricatorRepositoryDiscoveryEngine.php
@@ -1,582 +1,645 @@
 <?php
 
 /**
  * @task discover   Discovering Repositories
  * @task svn        Discovering Subversion Repositories
  * @task git        Discovering Git Repositories
  * @task hg         Discovering Mercurial Repositories
  * @task internal   Internals
  */
 final class PhabricatorRepositoryDiscoveryEngine
   extends PhabricatorRepositoryEngine {
 
   private $repairMode;
   private $commitCache = array();
   private $workingSet = array();
 
   const MAX_COMMIT_CACHE_SIZE = 2048;
 
 
 /* -(  Discovering Repositories  )------------------------------------------- */
 
 
   public function setRepairMode($repair_mode) {
     $this->repairMode = $repair_mode;
     return $this;
   }
 
 
   public function getRepairMode() {
     return $this->repairMode;
   }
 
 
   /**
    * @task discovery
    */
   public function discoverCommits() {
     $repository = $this->getRepository();
 
+    $lock = $this->newRepositoryLock($repository, 'repo.look', false);
+
+    try {
+      $lock->lock();
+    } catch (PhutilLockException $ex) {
+      throw new DiffusionDaemonLockException(
+        pht(
+          'Another process is currently discovering repository "%s", '.
+          'skipping discovery.',
+          $repository->getDisplayName()));
+    }
+
+    try {
+      $result = $this->discoverCommitsWithLock();
+    } catch (Exception $ex) {
+      $lock->unlock();
+      throw $ex;
+    }
+
+    $lock->unlock();
+
+    return $result;
+  }
+
+  private function discoverCommitsWithLock() {
+    $repository = $this->getRepository();
+
     $vcs = $repository->getVersionControlSystem();
     switch ($vcs) {
       case PhabricatorRepositoryType::REPOSITORY_TYPE_SVN:
         $refs = $this->discoverSubversionCommits();
         break;
       case PhabricatorRepositoryType::REPOSITORY_TYPE_MERCURIAL:
         $refs = $this->discoverMercurialCommits();
         break;
       case PhabricatorRepositoryType::REPOSITORY_TYPE_GIT:
         $refs = $this->discoverGitCommits();
         break;
       default:
         throw new Exception(pht("Unknown VCS '%s'!", $vcs));
     }
 
+    if ($this->isInitialImport($refs)) {
+      $this->log(
+        pht(
+          'Discovered more than %s commit(s) in an empty repository, '.
+          'marking repository as importing.',
+          new PhutilNumber(PhabricatorRepository::IMPORT_THRESHOLD)));
+
+      $repository->markImporting();
+    }
+
     // Clear the working set cache.
     $this->workingSet = array();
 
     // Record discovered commits and mark them in the cache.
     foreach ($refs as $ref) {
       $this->recordCommit(
         $repository,
         $ref->getIdentifier(),
         $ref->getEpoch(),
         $ref->getCanCloseImmediately(),
         $ref->getParents());
 
       $this->commitCache[$ref->getIdentifier()] = true;
     }
 
     return $refs;
   }
 
 
 /* -(  Discovering Git Repositories  )--------------------------------------- */
 
 
   /**
    * @task git
    */
   private function discoverGitCommits() {
     $repository = $this->getRepository();
 
     if (!$repository->isHosted()) {
       $this->verifyGitOrigin($repository);
     }
 
     $branches = id(new DiffusionLowLevelGitRefQuery())
       ->setRepository($repository)
       ->withIsOriginBranch(true)
       ->execute();
 
     if (!$branches) {
       // This repository has no branches at all, so we don't need to do
       // anything. Generally, this means the repository is empty.
       return array();
     }
 
     $branches = $this->sortBranches($branches);
     $branches = mpull($branches, 'getCommitIdentifier', 'getShortName');
 
     $this->log(
       pht(
         'Discovering commits in repository "%s".',
         $repository->getDisplayName()));
 
     $this->fillCommitCache(array_values($branches));
 
     $refs = array();
     foreach ($branches as $name => $commit) {
       $this->log(pht('Examining branch "%s", at "%s".', $name, $commit));
 
       if (!$repository->shouldTrackBranch($name)) {
         $this->log(pht('Skipping, branch is untracked.'));
         continue;
       }
 
       if ($this->isKnownCommit($commit)) {
         $this->log(pht('Skipping, HEAD is known.'));
         continue;
       }
 
       $this->log(pht('Looking for new commits.'));
 
       $branch_refs = $this->discoverStreamAncestry(
         new PhabricatorGitGraphStream($repository, $commit),
         $commit,
         $repository->shouldAutocloseBranch($name));
 
       $this->didDiscoverRefs($branch_refs);
 
       $refs[] = $branch_refs;
     }
 
     return array_mergev($refs);
   }
 
 
 /* -(  Discovering Subversion Repositories  )-------------------------------- */
 
 
   /**
    * @task svn
    */
   private function discoverSubversionCommits() {
     $repository = $this->getRepository();
 
     if (!$repository->isHosted()) {
       $this->verifySubversionRoot($repository);
     }
 
     $upper_bound = null;
     $limit = 1;
     $refs = array();
     do {
       // Find all the unknown commits on this path. Note that we permit
       // importing an SVN subdirectory rather than the entire repository, so
       // commits may be nonsequential.
 
       if ($upper_bound === null) {
         $at_rev = 'HEAD';
       } else {
         $at_rev = ($upper_bound - 1);
       }
 
       try {
         list($xml, $stderr) = $repository->execxRemoteCommand(
           'log --xml --quiet --limit %d %s',
           $limit,
           $repository->getSubversionBaseURI($at_rev));
       } catch (CommandException $ex) {
         $stderr = $ex->getStdErr();
         if (preg_match('/(path|File) not found/', $stderr)) {
           // We've gone all the way back through history and this path was not
           // affected by earlier commits.
           break;
         }
         throw $ex;
       }
 
       $xml = phutil_utf8ize($xml);
       $log = new SimpleXMLElement($xml);
       foreach ($log->logentry as $entry) {
         $identifier = (int)$entry['revision'];
         $epoch = (int)strtotime((string)$entry->date[0]);
         $refs[$identifier] = id(new PhabricatorRepositoryCommitRef())
           ->setIdentifier($identifier)
           ->setEpoch($epoch)
           ->setCanCloseImmediately(true);
 
         if ($upper_bound === null) {
           $upper_bound = $identifier;
         } else {
           $upper_bound = min($upper_bound, $identifier);
         }
       }
 
       // Discover 2, 4, 8, ... 256 logs at a time. This allows us to initially
       // import large repositories fairly quickly, while pulling only as much
       // data as we need in the common case (when we've already imported the
       // repository and are just grabbing one commit at a time).
       $limit = min($limit * 2, 256);
 
     } while ($upper_bound > 1 && !$this->isKnownCommit($upper_bound));
 
     krsort($refs);
     while ($refs && $this->isKnownCommit(last($refs)->getIdentifier())) {
       array_pop($refs);
     }
     $refs = array_reverse($refs);
 
     $this->didDiscoverRefs($refs);
 
     return $refs;
   }
 
 
   private function verifySubversionRoot(PhabricatorRepository $repository) {
     list($xml) = $repository->execxRemoteCommand(
       'info --xml %s',
       $repository->getSubversionPathURI());
 
     $xml = phutil_utf8ize($xml);
     $xml = new SimpleXMLElement($xml);
 
     $remote_root = (string)($xml->entry[0]->repository[0]->root[0]);
     $expect_root = $repository->getSubversionPathURI();
 
     $normal_type_svn = PhabricatorRepositoryURINormalizer::TYPE_SVN;
 
     $remote_normal = id(new PhabricatorRepositoryURINormalizer(
       $normal_type_svn,
       $remote_root))->getNormalizedPath();
 
     $expect_normal = id(new PhabricatorRepositoryURINormalizer(
       $normal_type_svn,
       $expect_root))->getNormalizedPath();
 
     if ($remote_normal != $expect_normal) {
       throw new Exception(
         pht(
           'Repository "%s" does not have a correctly configured remote URI. '.
           'The remote URI for a Subversion repository MUST point at the '.
           'repository root. The root for this repository is "%s", but the '.
           'configured URI is "%s". To resolve this error, set the remote URI '.
           'to point at the repository root. If you want to import only part '.
           'of a Subversion repository, use the "Import Only" option.',
           $repository->getDisplayName(),
           $remote_root,
           $expect_root));
     }
   }
 
 
 /* -(  Discovering Mercurial Repositories  )--------------------------------- */
 
 
   /**
    * @task hg
    */
   private function discoverMercurialCommits() {
     $repository = $this->getRepository();
 
     $branches = id(new DiffusionLowLevelMercurialBranchesQuery())
       ->setRepository($repository)
       ->execute();
 
     $this->fillCommitCache(mpull($branches, 'getCommitIdentifier'));
 
     $refs = array();
     foreach ($branches as $branch) {
       // NOTE: Mercurial branches may have multiple heads, so the names may
       // not be unique.
       $name = $branch->getShortName();
       $commit = $branch->getCommitIdentifier();
 
       $this->log(pht('Examining branch "%s" head "%s".', $name, $commit));
       if (!$repository->shouldTrackBranch($name)) {
         $this->log(pht('Skipping, branch is untracked.'));
         continue;
       }
 
       if ($this->isKnownCommit($commit)) {
         $this->log(pht('Skipping, this head is a known commit.'));
         continue;
       }
 
       $this->log(pht('Looking for new commits.'));
 
       $branch_refs = $this->discoverStreamAncestry(
         new PhabricatorMercurialGraphStream($repository, $commit),
         $commit,
         $close_immediately = true);
 
       $this->didDiscoverRefs($branch_refs);
 
       $refs[] = $branch_refs;
     }
 
     return array_mergev($refs);
   }
 
 
 /* -(  Internals  )---------------------------------------------------------- */
 
 
   private function discoverStreamAncestry(
     PhabricatorRepositoryGraphStream $stream,
     $commit,
     $close_immediately) {
 
     $discover = array($commit);
     $graph = array();
     $seen = array();
 
     // Find all the reachable, undiscovered commits. Build a graph of the
     // edges.
     while ($discover) {
       $target = array_pop($discover);
 
       if (empty($graph[$target])) {
         $graph[$target] = array();
       }
 
       $parents = $stream->getParents($target);
       foreach ($parents as $parent) {
         if ($this->isKnownCommit($parent)) {
           continue;
         }
 
         $graph[$target][$parent] = true;
 
         if (empty($seen[$parent])) {
           $seen[$parent] = true;
           $discover[] = $parent;
         }
       }
     }
 
     // Now, sort them topographically.
     $commits = $this->reduceGraph($graph);
 
     $refs = array();
     foreach ($commits as $commit) {
       $refs[] = id(new PhabricatorRepositoryCommitRef())
         ->setIdentifier($commit)
         ->setEpoch($stream->getCommitDate($commit))
         ->setCanCloseImmediately($close_immediately)
         ->setParents($stream->getParents($commit));
     }
 
     return $refs;
   }
 
 
   private function reduceGraph(array $edges) {
     foreach ($edges as $commit => $parents) {
       $edges[$commit] = array_keys($parents);
     }
 
     $graph = new PhutilDirectedScalarGraph();
     $graph->addNodes($edges);
 
     $commits = $graph->getTopographicallySortedNodes();
 
     // NOTE: We want the most ancestral nodes first, so we need to reverse the
     // list we get out of AbstractDirectedGraph.
     $commits = array_reverse($commits);
 
     return $commits;
   }
 
 
   private function isKnownCommit($identifier) {
     if (isset($this->commitCache[$identifier])) {
       return true;
     }
 
     if (isset($this->workingSet[$identifier])) {
       return true;
     }
 
     if ($this->repairMode) {
       // In repair mode, rediscover the entire repository, ignoring the
       // database state. We can hit the local cache above, but if we miss it
       // stop the script from going to the database cache.
       return false;
     }
 
     $this->fillCommitCache(array($identifier));
 
     return isset($this->commitCache[$identifier]);
   }
 
   private function fillCommitCache(array $identifiers) {
     if (!$identifiers) {
       return;
     }
 
     $commits = id(new PhabricatorRepositoryCommit())->loadAllWhere(
       'repositoryID = %d AND commitIdentifier IN (%Ls)',
       $this->getRepository()->getID(),
       $identifiers);
 
     foreach ($commits as $commit) {
       $this->commitCache[$commit->getCommitIdentifier()] = true;
     }
 
     while (count($this->commitCache) > self::MAX_COMMIT_CACHE_SIZE) {
       array_shift($this->commitCache);
     }
   }
 
   /**
    * Sort branches so we process closeable branches first. This makes the
    * whole import process a little cheaper, since we can close these commits
    * the first time through rather than catching them in the refs step.
    *
    * @task internal
    *
    * @param   list<DiffusionRepositoryRef> List of branch heads.
    * @return  list<DiffusionRepositoryRef> Sorted list of branch heads.
    */
   private function sortBranches(array $branches) {
     $repository = $this->getRepository();
 
     $head_branches = array();
     $tail_branches = array();
     foreach ($branches as $branch) {
       $name = $branch->getShortName();
 
       if ($repository->shouldAutocloseBranch($name)) {
         $head_branches[] = $branch;
       } else {
         $tail_branches[] = $branch;
       }
     }
 
     return array_merge($head_branches, $tail_branches);
   }
 
 
   private function recordCommit(
     PhabricatorRepository $repository,
     $commit_identifier,
     $epoch,
     $close_immediately,
     array $parents) {
 
     $commit = new PhabricatorRepositoryCommit();
     $commit->setRepositoryID($repository->getID());
     $commit->setCommitIdentifier($commit_identifier);
     $commit->setEpoch($epoch);
     if ($close_immediately) {
       $commit->setImportStatus(PhabricatorRepositoryCommit::IMPORTED_CLOSEABLE);
     }
 
     $data = new PhabricatorRepositoryCommitData();
 
     $conn_w = $repository->establishConnection('w');
 
     try {
 
       // If this commit has parents, look up their IDs. The parent commits
       // should always exist already.
 
       $parent_ids = array();
       if ($parents) {
         $parent_rows = queryfx_all(
           $conn_w,
           'SELECT id, commitIdentifier FROM %T
             WHERE commitIdentifier IN (%Ls) AND repositoryID = %d',
           $commit->getTableName(),
           $parents,
           $repository->getID());
 
         $parent_map = ipull($parent_rows, 'id', 'commitIdentifier');
 
         foreach ($parents as $parent) {
           if (empty($parent_map[$parent])) {
             throw new Exception(
               pht('Unable to identify parent "%s"!', $parent));
           }
           $parent_ids[] = $parent_map[$parent];
         }
       } else {
         // Write an explicit 0 so we can distinguish between "really no
         // parents" and "data not available".
         if (!$repository->isSVN()) {
           $parent_ids = array(0);
         }
       }
 
       $commit->openTransaction();
         $commit->save();
 
         $data->setCommitID($commit->getID());
         $data->save();
 
         foreach ($parent_ids as $parent_id) {
           queryfx(
             $conn_w,
             'INSERT IGNORE INTO %T (childCommitID, parentCommitID)
               VALUES (%d, %d)',
             PhabricatorRepository::TABLE_PARENTS,
             $commit->getID(),
             $parent_id);
         }
       $commit->saveTransaction();
 
       $this->insertTask($repository, $commit);
 
       queryfx(
         $conn_w,
         'INSERT INTO %T (repositoryID, size, lastCommitID, epoch)
           VALUES (%d, 1, %d, %d)
           ON DUPLICATE KEY UPDATE
             size = size + 1,
             lastCommitID =
               IF(VALUES(epoch) > epoch, VALUES(lastCommitID), lastCommitID),
             epoch = IF(VALUES(epoch) > epoch, VALUES(epoch), epoch)',
         PhabricatorRepository::TABLE_SUMMARY,
         $repository->getID(),
         $commit->getID(),
         $epoch);
 
       if ($this->repairMode) {
         // Normally, the query should throw a duplicate key exception. If we
         // reach this in repair mode, we've actually performed a repair.
         $this->log(pht('Repaired commit "%s".', $commit_identifier));
       }
 
       PhutilEventEngine::dispatchEvent(
         new PhabricatorEvent(
           PhabricatorEventType::TYPE_DIFFUSION_DIDDISCOVERCOMMIT,
           array(
             'repository'  => $repository,
             'commit'      => $commit,
           )));
 
 
 
     } catch (AphrontDuplicateKeyQueryException $ex) {
       $commit->killTransaction();
       // Ignore. This can happen because we discover the same new commit
       // more than once when looking at history, or because of races or
       // data inconsistency or cosmic radiation; in any case, we're still
       // in a good state if we ignore the failure.
     }
   }
 
   private function didDiscoverRefs(array $refs) {
     foreach ($refs as $ref) {
       $this->workingSet[$ref->getIdentifier()] = true;
     }
   }
 
   private function insertTask(
     PhabricatorRepository $repository,
     PhabricatorRepositoryCommit $commit,
     $data = array()) {
 
     $vcs = $repository->getVersionControlSystem();
     switch ($vcs) {
       case PhabricatorRepositoryType::REPOSITORY_TYPE_GIT:
         $class = 'PhabricatorRepositoryGitCommitMessageParserWorker';
         break;
       case PhabricatorRepositoryType::REPOSITORY_TYPE_SVN:
         $class = 'PhabricatorRepositorySvnCommitMessageParserWorker';
         break;
       case PhabricatorRepositoryType::REPOSITORY_TYPE_MERCURIAL:
         $class = 'PhabricatorRepositoryMercurialCommitMessageParserWorker';
         break;
       default:
         throw new Exception(pht("Unknown repository type '%s'!", $vcs));
     }
 
     $data['commitID'] = $commit->getID();
 
     PhabricatorWorker::scheduleTask($class, $data);
   }
 
+  private function isInitialImport(array $refs) {
+    $commit_count = count($refs);
+
+    if ($commit_count <= PhabricatorRepository::IMPORT_THRESHOLD) {
+      // If we fetched a small number of commits, assume it's an initial
+      // commit or a stack of a few initial commits.
+      return false;
+    }
+
+    $viewer = $this->getViewer();
+    $repository = $this->getRepository();
+
+    $any_commits = id(new DiffusionCommitQuery())
+      ->setViewer($viewer)
+      ->withRepository($repository)
+      ->setLimit(1)
+      ->execute();
+
+    if ($any_commits) {
+      // If the repository already has commits, this isn't an import.
+      return false;
+    }
+
+    return true;
+  }
+
 }
diff --git a/src/applications/repository/engine/PhabricatorRepositoryEngine.php b/src/applications/repository/engine/PhabricatorRepositoryEngine.php
index 42ad650a3..445357e78 100644
--- a/src/applications/repository/engine/PhabricatorRepositoryEngine.php
+++ b/src/applications/repository/engine/PhabricatorRepositoryEngine.php
@@ -1,178 +1,199 @@
 <?php
 
 /**
  * @task config     Configuring Repository Engines
  * @task internal   Internals
  */
 abstract class PhabricatorRepositoryEngine extends Phobject {
 
   private $repository;
   private $verbose;
 
   /**
    * @task config
    */
   public function setRepository(PhabricatorRepository $repository) {
     $this->repository = $repository;
     return $this;
   }
 
 
   /**
    * @task config
    */
   protected function getRepository() {
     if ($this->repository === null) {
       throw new PhutilInvalidStateException('setRepository');
     }
 
     return $this->repository;
   }
 
 
   /**
    * @task config
    */
   public function setVerbose($verbose) {
     $this->verbose = $verbose;
     return $this;
   }
 
 
   /**
    * @task config
    */
   public function getVerbose() {
     return $this->verbose;
   }
 
 
   public function getViewer() {
     return PhabricatorUser::getOmnipotentUser();
   }
 
+  protected function newRepositoryLock(
+    PhabricatorRepository $repository,
+    $lock_key,
+    $lock_device_only) {
+
+    $lock_parts = array();
+    $lock_parts[] = $lock_key;
+    $lock_parts[] = $repository->getID();
+
+    if ($lock_device_only) {
+      $device = AlmanacKeys::getLiveDevice();
+      if ($device) {
+        $lock_parts[] = $device->getID();
+      }
+    }
+
+    $lock_name = implode(':', $lock_parts);
+    return PhabricatorGlobalLock::newLock($lock_name);
+  }
+
+
   /**
    * Verify that the "origin" remote exists, and points at the correct URI.
    *
    * This catches or corrects some types of misconfiguration, and also repairs
    * an issue where Git 1.7.1 does not create an "origin" for `--bare` clones.
    * See T4041.
    *
    * @param   PhabricatorRepository Repository to verify.
    * @return  void
    */
   protected function verifyGitOrigin(PhabricatorRepository $repository) {
     try {
       list($remotes) = $repository->execxLocalCommand(
         'remote show -n origin');
     } catch (CommandException $ex) {
       throw new PhutilProxyException(
         pht(
           'Expected to find a Git working copy at path "%s", but the '.
           'path exists and is not a valid working copy. If you remove '.
           'this directory, the daemons will automatically recreate it '.
           'correctly. Phabricator will not destroy the directory for you '.
           'because it can not be sure that it does not contain important '.
           'data.',
           $repository->getLocalPath()),
         $ex);
     }
 
     $matches = null;
     if (!preg_match('/^\s*Fetch URL:\s*(.*?)\s*$/m', $remotes, $matches)) {
       throw new Exception(
         pht(
           "Expected '%s' in '%s'.",
           'Fetch URL',
           'git remote show -n origin'));
     }
 
     $remote_uri = $matches[1];
     $expect_remote = $repository->getRemoteURI();
 
     if ($remote_uri == 'origin') {
       // If a remote does not exist, git pretends it does and prints out a
       // made up remote where the URI is the same as the remote name. This is
       // definitely not correct.
 
       // Possibly, we should use `git remote --verbose` instead, which does not
       // suffer from this problem (but is a little more complicated to parse).
       $valid = false;
       $exists = false;
     } else {
       $normal_type_git = PhabricatorRepositoryURINormalizer::TYPE_GIT;
 
       $remote_normal = id(new PhabricatorRepositoryURINormalizer(
         $normal_type_git,
         $remote_uri))->getNormalizedPath();
 
       $expect_normal = id(new PhabricatorRepositoryURINormalizer(
         $normal_type_git,
         $expect_remote))->getNormalizedPath();
 
       $valid = ($remote_normal == $expect_normal);
       $exists = true;
     }
 
     if (!$valid) {
       if (!$exists) {
         // If there's no "origin" remote, just create it regardless of how
         // strongly we own the working copy. There is almost no conceivable
         // scenario in which this could do damage.
         $this->log(
           pht(
             'Remote "origin" does not exist. Creating "origin", with '.
             'URI "%s".',
             $expect_remote));
         $repository->execxLocalCommand(
           'remote add origin %P',
           $repository->getRemoteURIEnvelope());
 
         // NOTE: This doesn't fetch the origin (it just creates it), so we won't
         // know about origin branches until the next "pull" happens. That's fine
         // for our purposes, but might impact things in the future.
       } else {
         if ($repository->canDestroyWorkingCopy()) {
           // Bad remote, but we can try to repair it.
           $this->log(
             pht(
               'Remote "origin" exists, but is pointed at the wrong URI, "%s". '.
               'Resetting origin URI to "%s.',
               $remote_uri,
               $expect_remote));
           $repository->execxLocalCommand(
             'remote set-url origin %P',
             $repository->getRemoteURIEnvelope());
         } else {
           // Bad remote and we aren't comfortable repairing it.
           $message = pht(
             'Working copy at "%s" has a mismatched origin URI, "%s". '.
             'The expected origin URI is "%s". Fix your configuration, or '.
             'set the remote URI correctly. To avoid breaking anything, '.
             'Phabricator will not automatically fix this.',
             $repository->getLocalPath(),
             $remote_uri,
             $expect_remote);
           throw new Exception($message);
         }
       }
     }
   }
 
 
 
 
   /**
    * @task internal
    */
   protected function log($pattern /* ... */) {
     if ($this->getVerbose()) {
       $console = PhutilConsole::getConsole();
       $argv = func_get_args();
       array_unshift($argv, "%s\n");
       call_user_func_array(array($console, 'writeOut'), $argv);
     }
     return $this;
   }
 
 }
diff --git a/src/applications/repository/engine/PhabricatorRepositoryMirrorEngine.php b/src/applications/repository/engine/PhabricatorRepositoryMirrorEngine.php
index e4e65aab6..f07c301bf 100644
--- a/src/applications/repository/engine/PhabricatorRepositoryMirrorEngine.php
+++ b/src/applications/repository/engine/PhabricatorRepositoryMirrorEngine.php
@@ -1,105 +1,119 @@
 <?php
 
 /**
  * Pushes a repository to its mirrors.
  */
 final class PhabricatorRepositoryMirrorEngine
   extends PhabricatorRepositoryEngine {
 
   public function pushToMirrors() {
+    $viewer = $this->getViewer();
     $repository = $this->getRepository();
 
     if (!$repository->canMirror()) {
       return;
     }
 
     if (PhabricatorEnv::getEnvConfig('phabricator.silent')) {
       $this->log(
         pht('Phabricator is running in silent mode; declining to mirror.'));
       return;
     }
 
-    $mirrors = id(new PhabricatorRepositoryMirrorQuery())
-      ->setViewer($this->getViewer())
-      ->withRepositoryPHIDs(array($repository->getPHID()))
+    $uris = id(new PhabricatorRepositoryURIQuery())
+      ->setViewer($viewer)
+      ->withRepositories(array($repository))
       ->execute();
 
+    $io_mirror = PhabricatorRepositoryURI::IO_MIRROR;
+
     $exceptions = array();
-    foreach ($mirrors as $mirror) {
+    foreach ($uris as $mirror) {
+      if ($mirror->getIsDisabled()) {
+        continue;
+      }
+
+      $io_type = $mirror->getEffectiveIOType();
+      if ($io_type != $io_mirror) {
+        continue;
+      }
+
       try {
         $this->pushRepositoryToMirror($repository, $mirror);
       } catch (Exception $ex) {
         $exceptions[] = $ex;
       }
     }
 
     if ($exceptions) {
       throw new PhutilAggregateException(
         pht(
           'Exceptions occurred while mirroring the "%s" repository.',
           $repository->getDisplayName()),
         $exceptions);
     }
   }
 
   private function pushRepositoryToMirror(
     PhabricatorRepository $repository,
-    PhabricatorRepositoryMirror $mirror) {
-
-    // TODO: This is a little bit janky, but we don't have first-class
-    // infrastructure for running remote commands against an arbitrary remote
-    // right now. Just make an emphemeral copy of the repository and muck with
-    // it a little bit. In the medium term, we should pull this command stuff
-    // out and use it here and for "Land to ...".
+    PhabricatorRepositoryURI $mirror_uri) {
 
-    $proxy = clone $repository;
-    $proxy->makeEphemeral();
+    $this->log(
+      pht(
+        'Pushing to remote "%s"...',
+        $mirror_uri->getEffectiveURI()));
 
-    $proxy->setDetail('hosting-enabled', false);
-    $proxy->setDetail('remote-uri', $mirror->getRemoteURI());
-    $proxy->setCredentialPHID($mirror->getCredentialPHID());
-
-    $this->log(pht('Pushing to remote "%s"...', $mirror->getRemoteURI()));
-
-    if ($proxy->isGit()) {
-      $this->pushToGitRepository($proxy);
-    } else if ($proxy->isHg()) {
-      $this->pushToHgRepository($proxy);
+    if ($repository->isGit()) {
+      $this->pushToGitRepository($repository, $mirror_uri);
+    } else if ($repository->isHg()) {
+      $this->pushToHgRepository($repository, $mirror_uri);
     } else {
       throw new Exception(pht('Unsupported VCS!'));
     }
   }
 
   private function pushToGitRepository(
-    PhabricatorRepository $proxy) {
+    PhabricatorRepository $repository,
+    PhabricatorRepositoryURI $mirror_uri) {
 
-    $future = $proxy->getRemoteCommandFuture(
+    $argv = array(
       'push --verbose --mirror -- %P',
-      $proxy->getRemoteURIEnvelope());
+      $mirror_uri->getURIEnvelope(),
+    );
+
+    $future = $mirror_uri->newCommandEngine()
+      ->setArgv($argv)
+      ->newFuture();
 
     $future
-      ->setCWD($proxy->getLocalPath())
+      ->setCWD($repository->getLocalPath())
       ->resolvex();
   }
 
   private function pushToHgRepository(
-    PhabricatorRepository $proxy) {
+    PhabricatorRepository $repository,
+    PhabricatorRepositoryURI $mirror_uri) {
 
-    $future = $proxy->getRemoteCommandFuture(
+    $argv = array(
       'push --verbose --rev tip -- %P',
-      $proxy->getRemoteURIEnvelope());
+      $mirror_uri->getURIEnvelope(),
+    );
+
+    $future = $mirror_uri->newCommandEngine()
+      ->setArgv($argv)
+      ->newFuture();
 
     try {
       $future
-        ->setCWD($proxy->getLocalPath())
+        ->setCWD($repository->getLocalPath())
         ->resolvex();
     } catch (CommandException $ex) {
       if (preg_match('/no changes found/', $ex->getStdOut())) {
         // mercurial says nothing changed, but that's good
       } else {
         throw $ex;
       }
     }
   }
 
 }
diff --git a/src/applications/repository/engine/PhabricatorRepositoryPullEngine.php b/src/applications/repository/engine/PhabricatorRepositoryPullEngine.php
index 621c71857..9e151ecd8 100644
--- a/src/applications/repository/engine/PhabricatorRepositoryPullEngine.php
+++ b/src/applications/repository/engine/PhabricatorRepositoryPullEngine.php
@@ -1,566 +1,593 @@
 <?php
 
 /**
  * Manages execution of `git pull` and `hg pull` commands for
  * @{class:PhabricatorRepository} objects. Used by
  * @{class:PhabricatorRepositoryPullLocalDaemon}.
  *
  * This class also covers initial working copy setup through `git clone`,
  * `git init`, `hg clone`, `hg init`, or `svnadmin create`.
  *
  * @task pull     Pulling Working Copies
  * @task git      Pulling Git Working Copies
  * @task hg       Pulling Mercurial Working Copies
  * @task svn      Pulling Subversion Working Copies
  * @task internal Internals
  */
 final class PhabricatorRepositoryPullEngine
   extends PhabricatorRepositoryEngine {
 
 
 /* -(  Pulling Working Copies  )--------------------------------------------- */
 
 
   public function pullRepository() {
     $repository = $this->getRepository();
+
+    $lock = $this->newRepositoryLock($repository, 'repo.pull', true);
+
+    try {
+      $lock->lock();
+    } catch (PhutilLockException $ex) {
+      throw new DiffusionDaemonLockException(
+        pht(
+          'Another process is currently updating repository "%s", '.
+          'skipping pull.',
+          $repository->getDisplayName()));
+    }
+
+    try {
+      $result = $this->pullRepositoryWithLock();
+    } catch (Exception $ex) {
+      $lock->unlock();
+      throw $ex;
+    }
+
+    $lock->unlock();
+
+    return $result;
+  }
+
+  private function pullRepositoryWithLock() {
+    $repository = $this->getRepository();
     $viewer = PhabricatorUser::getOmnipotentUser();
 
     $is_hg = false;
     $is_git = false;
     $is_svn = false;
 
     $vcs = $repository->getVersionControlSystem();
 
     switch ($vcs) {
       case PhabricatorRepositoryType::REPOSITORY_TYPE_SVN:
         // We never pull a local copy of non-hosted Subversion repositories.
         if (!$repository->isHosted()) {
           $this->skipPull(
             pht(
               'Repository "%s" is a non-hosted Subversion repository, which '.
               'does not require a local working copy to be pulled.',
               $repository->getDisplayName()));
           return;
         }
         $is_svn = true;
         break;
       case PhabricatorRepositoryType::REPOSITORY_TYPE_GIT:
         $is_git = true;
         break;
       case PhabricatorRepositoryType::REPOSITORY_TYPE_MERCURIAL:
         $is_hg = true;
         break;
       default:
         $this->abortPull(pht('Unknown VCS "%s"!', $vcs));
         break;
     }
 
     $local_path = $repository->getLocalPath();
     if ($local_path === null) {
       $this->abortPull(
         pht(
           'No local path is configured for repository "%s".',
           $repository->getDisplayName()));
     }
 
     try {
       $dirname = dirname($local_path);
       if (!Filesystem::pathExists($dirname)) {
         Filesystem::createDirectory($dirname, 0755, $recursive = true);
       }
 
       if (!Filesystem::pathExists($local_path)) {
         $this->logPull(
           pht(
             'Creating a new working copy for repository "%s".',
             $repository->getDisplayName()));
         if ($is_git) {
           $this->executeGitCreate();
         } else if ($is_hg) {
           $this->executeMercurialCreate();
         } else {
           $this->executeSubversionCreate();
         }
       } else {
         if (!$repository->isHosted()) {
           $this->logPull(
             pht(
               'Updating the working copy for repository "%s".',
               $repository->getDisplayName()));
           if ($is_git) {
             $this->verifyGitOrigin($repository);
             $this->executeGitUpdate();
           } else if ($is_hg) {
             $this->executeMercurialUpdate();
           }
         }
       }
 
       if ($repository->isHosted()) {
         id(new DiffusionRepositoryClusterEngine())
           ->setViewer($viewer)
           ->setRepository($repository)
           ->synchronizeWorkingCopyBeforeRead();
 
         if ($is_git) {
           $this->installGitHook();
         } else if ($is_svn) {
           $this->installSubversionHook();
         } else if ($is_hg) {
           $this->installMercurialHook();
         }
 
         foreach ($repository->getHookDirectories() as $directory) {
           $this->installHookDirectory($directory);
         }
       }
 
     } catch (Exception $ex) {
       $this->abortPull(
         pht(
           "Pull of '%s' failed: %s",
           $repository->getDisplayName(),
           $ex->getMessage()),
         $ex);
     }
 
     $this->donePull();
 
     return $this;
   }
 
   private function skipPull($message) {
     $this->log('%s', $message);
     $this->donePull();
   }
 
   private function abortPull($message, Exception $ex = null) {
     $code_error = PhabricatorRepositoryStatusMessage::CODE_ERROR;
     $this->updateRepositoryInitStatus($code_error, $message);
     if ($ex) {
       throw $ex;
     } else {
       throw new Exception($message);
     }
   }
 
   private function logPull($message) {
     $code_working = PhabricatorRepositoryStatusMessage::CODE_WORKING;
     $this->updateRepositoryInitStatus($code_working, $message);
     $this->log('%s', $message);
   }
 
   private function donePull() {
     $code_okay = PhabricatorRepositoryStatusMessage::CODE_OKAY;
     $this->updateRepositoryInitStatus($code_okay);
   }
 
   private function updateRepositoryInitStatus($code, $message = null) {
     $this->getRepository()->writeStatusMessage(
       PhabricatorRepositoryStatusMessage::TYPE_INIT,
       $code,
       array(
         'message' => $message,
       ));
   }
 
   private function installHook($path) {
     $this->log('%s', pht('Installing commit hook to "%s"...', $path));
 
     $repository = $this->getRepository();
     $identifier = $this->getHookContextIdentifier($repository);
 
     $root = dirname(phutil_get_library_root('phabricator'));
     $bin = $root.'/bin/commit-hook';
 
     $full_php_path = Filesystem::resolveBinary('php');
     $cmd = csprintf(
       'exec %s -f %s -- %s "$@"',
       $full_php_path,
       $bin,
       $identifier);
 
     $hook = "#!/bin/sh\nexport TERM=dumb\n{$cmd}\n";
 
     Filesystem::writeFile($path, $hook);
     Filesystem::changePermissions($path, 0755);
   }
 
   private function installHookDirectory($path) {
     $readme = pht(
       "To add custom hook scripts to this repository, add them to this ".
       "directory.\n\nPhabricator will run any executables in this directory ".
       "after running its own checks, as though they were normal hook ".
       "scripts.");
 
     Filesystem::createDirectory($path, 0755);
     Filesystem::writeFile($path.'/README', $readme);
   }
 
   private function getHookContextIdentifier(PhabricatorRepository $repository) {
     $identifier = $repository->getPHID();
 
     $instance = PhabricatorEnv::getEnvConfig('cluster.instance');
     if (strlen($instance)) {
       $identifier = "{$identifier}:{$instance}";
     }
 
     return $identifier;
   }
 
 
 /* -(  Pulling Git Working Copies  )----------------------------------------- */
 
 
   /**
    * @task git
    */
   private function executeGitCreate() {
     $repository = $this->getRepository();
 
     $path = rtrim($repository->getLocalPath(), '/');
 
     if ($repository->isHosted()) {
       $repository->execxRemoteCommand(
         'init --bare -- %s',
         $path);
     } else {
       $repository->execxRemoteCommand(
         'clone --bare -- %P %s',
         $repository->getRemoteURIEnvelope(),
         $path);
     }
   }
 
 
   /**
    * @task git
    */
   private function executeGitUpdate() {
     $repository = $this->getRepository();
 
     list($err, $stdout) = $repository->execLocalCommand(
       'rev-parse --show-toplevel');
 
     $message = null;
     $path = $repository->getLocalPath();
     if ($err) {
       // Try to raise a more tailored error message in the more common case
       // of the user creating an empty directory. (We could try to remove it,
       // but might not be able to, and it's much simpler to raise a good
       // message than try to navigate those waters.)
       if (is_dir($path)) {
         $files = Filesystem::listDirectory($path, $include_hidden = true);
         if (!$files) {
           $message = pht(
             "Expected to find a git repository at '%s', but there ".
             "is an empty directory there. Remove the directory: the daemon ".
             "will run '%s' for you.",
             $path,
             'git clone');
         } else {
           $message = pht(
             "Expected to find a git repository at '%s', but there is ".
             "a non-repository directory (with other stuff in it) there. Move ".
             "or remove this directory (or reconfigure the repository to use a ".
             "different directory), and then either clone a repository ".
             "yourself or let the daemon do it.",
             $path);
         }
       } else if (is_file($path)) {
         $message = pht(
           "Expected to find a git repository at '%s', but there is a ".
           "file there instead. Remove it and let the daemon clone a ".
           "repository for you.",
           $path);
       } else {
         $message = pht(
           "Expected to find a git repository at '%s', but did not.",
           $path);
       }
     } else {
       $repo_path = rtrim($stdout, "\n");
 
       if (empty($repo_path)) {
         // This can mean one of two things: we're in a bare repository, or
         // we're inside a git repository inside another git repository. Since
         // the first is dramatically more likely now that we perform bare
         // clones and I don't have a great way to test for the latter, assume
         // we're OK.
       } else if (!Filesystem::pathsAreEquivalent($repo_path, $path)) {
         $err = true;
         $message = pht(
           "Expected to find repo at '%s', but the actual git repository root ".
           "for this directory is '%s'. Something is misconfigured. ".
           "The repository's 'Local Path' should be set to some place where ".
           "the daemon can check out a working copy, ".
           "and should not be inside another git repository.",
           $path,
           $repo_path);
       }
     }
 
     if ($err && $repository->canDestroyWorkingCopy()) {
       phlog(
         pht(
           "Repository working copy at '%s' failed sanity check; ".
           "destroying and re-cloning. %s",
           $path,
           $message));
       Filesystem::remove($path);
       $this->executeGitCreate();
     } else if ($err) {
       throw new Exception($message);
     }
 
     $retry = false;
     do {
       // This is a local command, but needs credentials.
       if ($repository->isWorkingCopyBare()) {
         // For bare working copies, we need this magic incantation.
         $future = $repository->getRemoteCommandFuture(
           'fetch origin %s --prune',
           '+refs/heads/*:refs/heads/*');
       } else {
         $future = $repository->getRemoteCommandFuture(
           'fetch --all --prune');
       }
 
       $future->setCWD($path);
       list($err, $stdout, $stderr) = $future->resolve();
 
       if ($err && !$retry && $repository->canDestroyWorkingCopy()) {
         $retry = true;
         // Fix remote origin url if it doesn't match our configuration
         $origin_url = $repository->execLocalCommand(
           'config --get remote.origin.url');
         $remote_uri = $repository->getRemoteURIEnvelope();
         if ($origin_url != $remote_uri->openEnvelope()) {
           $repository->execLocalCommand(
             'remote set-url origin %P',
             $remote_uri);
         }
       } else if ($err) {
         throw new CommandException(
           pht('Failed to fetch changes!'),
           $future->getCommand(),
           $err,
           $stdout,
           $stderr);
       } else {
         $retry = false;
       }
     } while ($retry);
   }
 
 
   /**
    * @task git
    */
   private function installGitHook() {
     $repository = $this->getRepository();
     $root = $repository->getLocalPath();
 
     if ($repository->isWorkingCopyBare()) {
       $path = '/hooks/pre-receive';
     } else {
       $path = '/.git/hooks/pre-receive';
     }
 
     $this->installHook($root.$path);
   }
 
 
 /* -(  Pulling Mercurial Working Copies  )----------------------------------- */
 
 
   /**
    * @task hg
    */
   private function executeMercurialCreate() {
     $repository = $this->getRepository();
 
     $path = rtrim($repository->getLocalPath(), '/');
 
     if ($repository->isHosted()) {
       $repository->execxRemoteCommand(
         'init -- %s',
         $path);
     } else {
       $remote = $repository->getRemoteURIEnvelope();
 
       // NOTE: Mercurial prior to 3.2.4 has an severe command injection
       // vulnerability. See: <http://bit.ly/19B58E9>
 
       // On vulnerable versions of Mercurial, we refuse to clone remotes which
       // contain characters which may be interpreted by the shell.
       $hg_version = PhabricatorRepositoryVersion::getMercurialVersion();
       $is_vulnerable = version_compare($hg_version, '3.2.4', '<');
       if ($is_vulnerable) {
         $cleartext = $remote->openEnvelope();
         // The use of "%R" here is an attempt to limit collateral damage
         // for normal URIs because it isn't clear how long this vulnerability
         // has been around for.
 
         $escaped = csprintf('%R', $cleartext);
         if ((string)$escaped !== (string)$cleartext) {
           throw new Exception(
             pht(
               'You have an old version of Mercurial (%s) which has a severe '.
               'command injection security vulnerability. The remote URI for '.
               'this repository (%s) is potentially unsafe. Upgrade Mercurial '.
               'to at least 3.2.4 to clone it.',
               $hg_version,
               $repository->getMonogram()));
         }
       }
 
       try {
         $repository->execxRemoteCommand(
           'clone --noupdate -- %P %s',
           $remote,
           $path);
       } catch (Exception $ex) {
         $message = $ex->getMessage();
         $message = $this->censorMercurialErrorMessage($message);
         throw new Exception($message);
       }
     }
   }
 
 
   /**
    * @task hg
    */
   private function executeMercurialUpdate() {
     $repository = $this->getRepository();
     $path = $repository->getLocalPath();
 
     // This is a local command, but needs credentials.
     $remote = $repository->getRemoteURIEnvelope();
     $future = $repository->getRemoteCommandFuture('pull -u -- %P', $remote);
     $future->setCWD($path);
 
     try {
       $future->resolvex();
     } catch (CommandException $ex) {
       $err = $ex->getError();
       $stdout = $ex->getStdOut();
 
       // NOTE: Between versions 2.1 and 2.1.1, Mercurial changed the behavior
       // of "hg pull" to return 1 in case of a successful pull with no changes.
       // This behavior has been reverted, but users who updated between Feb 1,
       // 2012 and Mar 1, 2012 will have the erroring version. Do a dumb test
       // against stdout to check for this possibility.
       // See: https://github.com/phacility/phabricator/issues/101/
 
       // NOTE: Mercurial has translated versions, which translate this error
       // string. In a translated version, the string will be something else,
       // like "aucun changement trouve". There didn't seem to be an easy way
       // to handle this (there are hard ways but this is not a common problem
       // and only creates log spam, not application failures). Assume English.
 
       // TODO: Remove this once we're far enough in the future that deployment
       // of 2.1 is exceedingly rare?
       if ($err == 1 && preg_match('/no changes found/', $stdout)) {
         return;
       } else {
         $message = $ex->getMessage();
         $message = $this->censorMercurialErrorMessage($message);
         throw new Exception($message);
       }
     }
   }
 
 
   /**
    * Censor response bodies from Mercurial error messages.
    *
    * When Mercurial attempts to clone an HTTP repository but does not
    * receive a response it expects, it emits the response body in the
    * command output.
    *
    * This represents a potential SSRF issue, because an attacker with
    * permission to create repositories can create one which points at the
    * remote URI for some local service, then read the response from the
    * error message. To prevent this, censor response bodies out of error
    * messages.
    *
    * @param string Uncensored Mercurial command output.
    * @return string Censored Mercurial command output.
    */
   private function censorMercurialErrorMessage($message) {
     return preg_replace(
       '/^---%<---.*/sm',
       pht('<Response body omitted from Mercurial error message.>')."\n",
       $message);
   }
 
 
   /**
    * @task hg
    */
   private function installMercurialHook() {
     $repository = $this->getRepository();
     $path = $repository->getLocalPath().'/.hg/hgrc';
 
     $identifier = $this->getHookContextIdentifier($repository);
 
     $root = dirname(phutil_get_library_root('phabricator'));
     $bin = $root.'/bin/commit-hook';
 
     $data = array();
     $data[] = '[hooks]';
 
     // This hook handles normal pushes.
     $data[] = csprintf(
       'pretxnchangegroup.phabricator = TERM=dumb %s %s %s',
       $bin,
       $identifier,
       'pretxnchangegroup');
 
     // This one handles creating bookmarks.
     $data[] = csprintf(
       'prepushkey.phabricator = TERM=dumb %s %s %s',
       $bin,
       $identifier,
       'prepushkey');
 
     $data[] = null;
 
     $data = implode("\n", $data);
 
     $this->log('%s', pht('Installing commit hook config to "%s"...', $path));
 
     Filesystem::writeFile($path, $data);
   }
 
 
 /* -(  Pulling Subversion Working Copies  )---------------------------------- */
 
 
   /**
    * @task svn
    */
   private function executeSubversionCreate() {
     $repository = $this->getRepository();
 
     $path = rtrim($repository->getLocalPath(), '/');
     execx('svnadmin create -- %s', $path);
   }
 
 
   /**
    * @task svn
    */
   private function installSubversionHook() {
     $repository = $this->getRepository();
     $root = $repository->getLocalPath();
 
     $path = '/hooks/pre-commit';
 
     $this->installHook($root.$path);
   }
 
 
 }
diff --git a/src/applications/repository/engine/__tests__/PhabricatorWorkingCopyTestCase.php b/src/applications/repository/engine/__tests__/PhabricatorWorkingCopyTestCase.php
index 5a9be9921..c489d6942 100644
--- a/src/applications/repository/engine/__tests__/PhabricatorWorkingCopyTestCase.php
+++ b/src/applications/repository/engine/__tests__/PhabricatorWorkingCopyTestCase.php
@@ -1,108 +1,107 @@
 <?php
 
 abstract class PhabricatorWorkingCopyTestCase extends PhabricatorTestCase {
 
   private $dirs = array();
 
   protected function getPhabricatorTestCaseConfiguration() {
     return array(
       self::PHABRICATOR_TESTCONFIG_BUILD_STORAGE_FIXTURES => true,
     );
   }
 
   protected function buildBareRepository($callsign) {
     $existing_repository = id(new PhabricatorRepositoryQuery())
       ->withCallsigns(array($callsign))
       ->setViewer(PhabricatorUser::getOmnipotentUser())
       ->executeOne();
     if ($existing_repository) {
       $existing_repository->delete();
     }
 
     $data_dir = dirname(__FILE__).'/data/';
 
     $types = array(
       'svn'   => PhabricatorRepositoryType::REPOSITORY_TYPE_SVN,
       'hg'    => PhabricatorRepositoryType::REPOSITORY_TYPE_MERCURIAL,
       'git'   => PhabricatorRepositoryType::REPOSITORY_TYPE_GIT,
     );
 
     $hits = array();
     foreach ($types as $type => $const) {
       $path = $data_dir.$callsign.'.'.$type.'.tgz';
       if (Filesystem::pathExists($path)) {
         $hits[$const] = $path;
       }
     }
 
     if (!$hits) {
       throw new Exception(
         pht(
           "No test data for callsign '%s'. Expected an archive ".
           "like '%s' in '%s'.",
           $callsign,
           "{$callsign}.git.tgz",
           $data_dir));
     }
 
     if (count($hits) > 1) {
       throw new Exception(
         pht(
           "Expected exactly one archive matching callsign '%s', ".
           "found too many: %s",
           $callsign,
           implode(', ', $hits)));
     }
 
     $path = head($hits);
     $vcs_type = head_key($hits);
 
     $dir = PhutilDirectoryFixture::newFromArchive($path);
     $local = new TempFile('.ignore');
 
     $user = $this->generateNewTestUser();
     $repo = PhabricatorRepository::initializeNewRepository($user)
       ->setCallsign($callsign)
       ->setName(pht('Test Repo "%s"', $callsign))
       ->setVersionControlSystem($vcs_type)
-      ->setDetail('local-path', dirname($local).'/'.$callsign)
+      ->setLocalPath(dirname($local).'/'.$callsign)
       ->setDetail('remote-uri', 'file://'.$dir->getPath().'/');
 
     $this->didConstructRepository($repo);
 
     $repo->save();
-    $repo->makeEphemeral();
 
     // Keep the disk resources around until we exit.
     $this->dirs[] = $dir;
     $this->dirs[] = $local;
 
     return $repo;
   }
 
   protected function didConstructRepository(PhabricatorRepository $repository) {
     return;
   }
 
   protected function buildPulledRepository($callsign) {
     $repository = $this->buildBareRepository($callsign);
 
     id(new PhabricatorRepositoryPullEngine())
       ->setRepository($repository)
       ->pullRepository();
 
     return $repository;
   }
 
   protected function buildDiscoveredRepository($callsign) {
     $repository = $this->buildPulledRepository($callsign);
 
     id(new PhabricatorRepositoryDiscoveryEngine())
       ->setRepository($repository)
       ->discoverCommits();
 
     return $repository;
   }
 
 
 }
diff --git a/src/applications/repository/management/PhabricatorRepositoryManagementDiscoverWorkflow.php b/src/applications/repository/management/PhabricatorRepositoryManagementDiscoverWorkflow.php
index 03c474584..c534edf90 100644
--- a/src/applications/repository/management/PhabricatorRepositoryManagementDiscoverWorkflow.php
+++ b/src/applications/repository/management/PhabricatorRepositoryManagementDiscoverWorkflow.php
@@ -1,57 +1,57 @@
 <?php
 
 final class PhabricatorRepositoryManagementDiscoverWorkflow
   extends PhabricatorRepositoryManagementWorkflow {
 
   protected function didConstruct() {
     $this
       ->setName('discover')
       ->setExamples('**discover** [__options__] __repository__ ...')
       ->setSynopsis(pht('Discover __repository__.'))
       ->setArguments(
         array(
           array(
             'name'        => 'verbose',
             'help'        => pht('Show additional debugging information.'),
           ),
           array(
             'name'        => 'repair',
             'help'        => pht(
               'Repair a repository with gaps in commit history.'),
           ),
           array(
             'name'        => 'repos',
             'wildcard'    => true,
           ),
         ));
   }
 
   public function execute(PhutilArgumentParser $args) {
-    $repos = $this->loadRepositories($args, 'repos');
+    $repos = $this->loadLocalRepositories($args, 'repos');
 
     if (!$repos) {
       throw new PhutilArgumentUsageException(
         pht('Specify one or more repositories to discover.'));
     }
 
     $console = PhutilConsole::getConsole();
     foreach ($repos as $repo) {
       $console->writeOut(
         "%s\n",
         pht(
           'Discovering "%s"...',
           $repo->getDisplayName()));
 
       id(new PhabricatorRepositoryDiscoveryEngine())
         ->setRepository($repo)
         ->setVerbose($args->getArg('verbose'))
         ->setRepairMode($args->getArg('repair'))
         ->discoverCommits();
     }
 
     $console->writeOut("%s\n", pht('Done.'));
 
     return 0;
   }
 
 }
diff --git a/src/applications/repository/management/PhabricatorRepositoryManagementEditWorkflow.php b/src/applications/repository/management/PhabricatorRepositoryManagementEditWorkflow.php
deleted file mode 100644
index cc33f1021..000000000
--- a/src/applications/repository/management/PhabricatorRepositoryManagementEditWorkflow.php
+++ /dev/null
@@ -1,135 +0,0 @@
-<?php
-
-final class PhabricatorRepositoryManagementEditWorkflow
-  extends PhabricatorRepositoryManagementWorkflow {
-
-  protected function didConstruct() {
-    $this
-      ->setName('edit')
-      ->setExamples('**edit** --as __username__ __repository__ ...')
-      ->setSynopsis(
-        pht(
-          'Edit __repository__ (will eventually be deprecated by Conduit).'))
-      ->setArguments(
-        array(
-          array(
-            'name'        => 'repos',
-            'wildcard'    => true,
-          ),
-          array(
-            'name' => 'as',
-            'param' => 'user',
-            'help' => pht('Edit as user.'),
-          ),
-          array(
-            'name' => 'local-path',
-            'param' => 'path',
-            'help' => pht('Edit the local path.'),
-          ),
-          array(
-            'name' => 'serve-http',
-            'param' => 'string',
-            'help' => pht('Edit the http serving policy.'),
-          ),
-          array(
-            'name' => 'serve-ssh',
-            'param' => 'string',
-            'help' => pht('Edit the ssh serving policy.'),
-          ),
-        ));
-  }
-
-  public function execute(PhutilArgumentParser $args) {
-    $repos = $this->loadRepositories($args, 'repos');
-
-    if (!$repos) {
-      throw new PhutilArgumentUsageException(
-        pht('Specify one or more repositories to edit.'));
-    }
-
-    $console = PhutilConsole::getConsole();
-
-    // TODO: It would be nice to just take this action as "Administrator" or
-    // similar, since that would make it easier to use this script, harder to
-    // impersonate users, and more clear to viewers what happened. However,
-    // the omnipotent user doesn't have a PHID right now, can't be loaded,
-    // doesn't have a handle, etc. Adding all of that is fairly involved, and
-    // I want to wait for stronger use cases first.
-
-    $username = $args->getArg('as');
-    if (!$username) {
-      throw new PhutilArgumentUsageException(
-        pht(
-          'Specify a user to edit as with %s.',
-          '--as <username>'));
-    }
-
-    $actor = id(new PhabricatorPeopleQuery())
-      ->setViewer($this->getViewer())
-      ->withUsernames(array($username))
-      ->executeOne();
-
-    if (!$actor) {
-      throw new PhutilArgumentUsageException(
-        pht("No such user '%s'!", $username));
-    }
-
-    foreach ($repos as $repo) {
-      $console->writeOut(
-        "%s\n",
-        pht(
-          'Editing "%s"...',
-          $repo->getDisplayName()));
-
-      $xactions = array();
-
-      $type_local_path = PhabricatorRepositoryTransaction::TYPE_LOCAL_PATH;
-      $type_protocol_http =
-        PhabricatorRepositoryTransaction::TYPE_PROTOCOL_HTTP;
-      $type_protocol_ssh = PhabricatorRepositoryTransaction::TYPE_PROTOCOL_SSH;
-      $allowed_serve_modes = array(
-        PhabricatorRepository::SERVE_OFF,
-        PhabricatorRepository::SERVE_READONLY,
-        PhabricatorRepository::SERVE_READWRITE,
-      );
-
-      if ($args->getArg('local-path')) {
-        $xactions[] = id(new PhabricatorRepositoryTransaction())
-          ->setTransactionType($type_local_path)
-          ->setNewValue($args->getArg('local-path'));
-      }
-      $serve_http = $args->getArg('serve-http');
-      if ($serve_http && in_array($serve_http, $allowed_serve_modes)) {
-        $xactions[] = id(new PhabricatorRepositoryTransaction())
-          ->setTransactionType($type_protocol_http)
-          ->setNewValue($serve_http);
-      }
-      $serve_ssh = $args->getArg('serve-ssh');
-      if ($serve_ssh && in_array($serve_ssh, $allowed_serve_modes)) {
-        $xactions[] = id(new PhabricatorRepositoryTransaction())
-          ->setTransactionType($type_protocol_ssh)
-          ->setNewValue($serve_ssh);
-      }
-
-
-      if (!$xactions) {
-        throw new PhutilArgumentUsageException(
-          pht('Specify one or more fields to edit!'));
-      }
-
-      $content_source = $this->newContentSource();
-
-      $editor = id(new PhabricatorRepositoryEditor())
-        ->setActor($actor)
-        ->setContentSource($content_source)
-        ->setContinueOnNoEffect(true)
-        ->setContinueOnMissingFields(true)
-        ->applyTransactions($repo, $xactions);
-    }
-
-    $console->writeOut("%s\n", pht('Done.'));
-
-    return 0;
-  }
-
-}
diff --git a/src/applications/repository/management/PhabricatorRepositoryManagementMirrorWorkflow.php b/src/applications/repository/management/PhabricatorRepositoryManagementMirrorWorkflow.php
index 99a2b08c8..caa757442 100644
--- a/src/applications/repository/management/PhabricatorRepositoryManagementMirrorWorkflow.php
+++ b/src/applications/repository/management/PhabricatorRepositoryManagementMirrorWorkflow.php
@@ -1,53 +1,54 @@
 <?php
 
 final class PhabricatorRepositoryManagementMirrorWorkflow
   extends PhabricatorRepositoryManagementWorkflow {
 
   protected function didConstruct() {
     $this
       ->setName('mirror')
       ->setExamples('**mirror** [__options__] __repository__ ...')
       ->setSynopsis(
         pht('Push __repository__ to mirrors.'))
       ->setArguments(
         array(
           array(
-            'name'        => 'verbose',
-            'help'        => pht('Show additional debugging information.'),
+            'name' => 'verbose',
+            'help' => pht('Show additional debugging information.'),
           ),
           array(
-            'name'        => 'repos',
-            'wildcard'    => true,
+            'name' => 'repos',
+            'wildcard' => true,
           ),
         ));
   }
 
   public function execute(PhutilArgumentParser $args) {
-    $repos = $this->loadRepositories($args, 'repos');
+    $repos = $this->loadLocalRepositories($args, 'repos');
 
     if (!$repos) {
       throw new PhutilArgumentUsageException(
         pht(
           'Specify one or more repositories to push to mirrors.'));
     }
 
-    $console = PhutilConsole::getConsole();
     foreach ($repos as $repo) {
-      $console->writeOut(
+      echo tsprintf(
         "%s\n",
         pht(
-          "Pushing '%s' to mirrors...",
+          'Pushing "%s" to mirrors...',
           $repo->getDisplayName()));
 
       $engine = id(new PhabricatorRepositoryMirrorEngine())
         ->setRepository($repo)
         ->setVerbose($args->getArg('verbose'))
         ->pushToMirrors();
     }
 
-    $console->writeOut("%s\n", pht('Done.'));
+    echo tsprintf(
+      "%s\n",
+      pht('Done.'));
 
     return 0;
   }
 
 }
diff --git a/src/applications/repository/management/PhabricatorRepositoryManagementMovePathsWorkflow.php b/src/applications/repository/management/PhabricatorRepositoryManagementMovePathsWorkflow.php
index f0146847a..baee43788 100644
--- a/src/applications/repository/management/PhabricatorRepositoryManagementMovePathsWorkflow.php
+++ b/src/applications/repository/management/PhabricatorRepositoryManagementMovePathsWorkflow.php
@@ -1,146 +1,144 @@
 <?php
 
 final class PhabricatorRepositoryManagementMovePathsWorkflow
   extends PhabricatorRepositoryManagementWorkflow {
 
   protected function didConstruct() {
     $this
       ->setName('move-paths')
       ->setSynopsis(pht('Move repository local paths.'))
       ->setArguments(
         array(
           array(
             'name' => 'from',
             'param' => 'prefix',
             'help' => pht('Move paths with this prefix.'),
           ),
           array(
             'name' => 'to',
             'param' => 'prefix',
             'help' => pht('Replace matching prefixes with this string.'),
           ),
         ));
   }
 
   public function execute(PhutilArgumentParser $args) {
     $console = PhutilConsole::getConsole();
 
     $repos = id(new PhabricatorRepositoryQuery())
       ->setViewer($this->getViewer())
       ->execute();
     if (!$repos) {
       $console->writeErr("%s\n", pht('There are no repositories.'));
       return 0;
     }
 
     $from = $args->getArg('from');
     if (!strlen($from)) {
       throw new Exception(
         pht(
           'You must specify a path prefix to move from with --from.'));
     }
 
     $to = $args->getArg('to');
     if (!strlen($to)) {
       throw new Exception(
         pht(
           'You must specify a path prefix to move to with --to.'));
     }
 
     $rows = array();
 
     $any_changes = false;
     foreach ($repos as $repo) {
       $src = $repo->getLocalPath();
 
       $row = array(
         'repository' => $repo,
         'move' => false,
         'monogram' => $repo->getMonogram(),
         'src' => $src,
         'dst' => '',
       );
 
       if (strncmp($src, $from, strlen($from))) {
         $row['action'] = pht('Ignore');
       } else {
         $dst = $to.substr($src, strlen($from));
 
         $row['action'] = tsprintf('**%s**', pht('Move'));
         $row['dst'] = $dst;
         $row['move'] = true;
         $any_changes = true;
       }
 
       $rows[] = $row;
     }
 
     $table = id(new PhutilConsoleTable())
       ->addColumn(
         'action',
         array(
           'title' => pht('Action'),
         ))
       ->addColumn(
         'monogram',
         array(
           'title' => pht('Repository'),
         ))
       ->addColumn(
         'src',
         array(
           'title' => pht('Src'),
         ))
       ->addColumn(
         'dst',
         array(
           'title' => pht('Dst'),
         ))
       ->setBorders(true);
 
     foreach ($rows as $row) {
       $display = array_select_keys(
         $row,
         array(
           'action',
           'monogram',
           'src',
           'dst',
         ));
       $table->addRow($display);
     }
 
     $table->draw();
 
     if (!$any_changes) {
       $console->writeOut(pht('No matching repositories.')."\n");
       return 0;
     }
 
     $prompt = pht('Apply these changes?');
     if (!phutil_console_confirm($prompt)) {
       throw new Exception(pht('Declining to apply changes.'));
     }
 
     foreach ($rows as $row) {
       if (empty($row['move'])) {
         continue;
       }
 
       $repo = $row['repository'];
-      $details = $repo->getDetails();
-      $details['local-path'] = $row['dst'];
 
       queryfx(
         $repo->establishConnection('w'),
-        'UPDATE %T SET details = %s WHERE id = %d',
+        'UPDATE %T SET localPath = %s WHERE id = %d',
         $repo->getTableName(),
-        phutil_json_encode($details),
+        $row['dst'],
         $repo->getID());
     }
 
     $console->writeOut(pht('Applied changes.')."\n");
     return 0;
   }
 
 }
diff --git a/src/applications/repository/management/PhabricatorRepositoryManagementPullWorkflow.php b/src/applications/repository/management/PhabricatorRepositoryManagementPullWorkflow.php
index f41f8b95a..808986f9a 100644
--- a/src/applications/repository/management/PhabricatorRepositoryManagementPullWorkflow.php
+++ b/src/applications/repository/management/PhabricatorRepositoryManagementPullWorkflow.php
@@ -1,51 +1,51 @@
 <?php
 
 final class PhabricatorRepositoryManagementPullWorkflow
   extends PhabricatorRepositoryManagementWorkflow {
 
   protected function didConstruct() {
     $this
       ->setName('pull')
       ->setExamples('**pull** __repository__ ...')
       ->setSynopsis(pht('Pull __repository__.'))
       ->setArguments(
         array(
           array(
             'name'      => 'verbose',
             'help'      => pht('Show additional debugging information.'),
           ),
           array(
             'name'      => 'repos',
             'wildcard'  => true,
           ),
         ));
   }
 
   public function execute(PhutilArgumentParser $args) {
-    $repos = $this->loadRepositories($args, 'repos');
+    $repos = $this->loadLocalRepositories($args, 'repos');
 
     if (!$repos) {
       throw new PhutilArgumentUsageException(
         pht('Specify one or more repositories to pull.'));
     }
 
     $console = PhutilConsole::getConsole();
     foreach ($repos as $repo) {
       $console->writeOut(
         "%s\n",
         pht(
           'Pulling "%s"...',
           $repo->getDisplayName()));
 
       id(new PhabricatorRepositoryPullEngine())
         ->setRepository($repo)
         ->setVerbose($args->getArg('verbose'))
         ->pullRepository();
     }
 
     $console->writeOut("%s\n", pht('Done.'));
 
     return 0;
   }
 
 }
diff --git a/src/applications/repository/management/PhabricatorRepositoryManagementRefsWorkflow.php b/src/applications/repository/management/PhabricatorRepositoryManagementRefsWorkflow.php
index ad9bbbd0d..8c806edac 100644
--- a/src/applications/repository/management/PhabricatorRepositoryManagementRefsWorkflow.php
+++ b/src/applications/repository/management/PhabricatorRepositoryManagementRefsWorkflow.php
@@ -1,52 +1,52 @@
 <?php
 
 final class PhabricatorRepositoryManagementRefsWorkflow
   extends PhabricatorRepositoryManagementWorkflow {
 
   protected function didConstruct() {
     $this
       ->setName('refs')
       ->setExamples('**refs** [__options__] __repository__ ...')
       ->setSynopsis(pht('Update refs in __repository__.'))
       ->setArguments(
         array(
           array(
             'name'        => 'verbose',
             'help'        => pht('Show additional debugging information.'),
           ),
           array(
             'name'        => 'repos',
             'wildcard'    => true,
           ),
         ));
   }
 
   public function execute(PhutilArgumentParser $args) {
-    $repos = $this->loadRepositories($args, 'repos');
+    $repos = $this->loadLocalRepositories($args, 'repos');
 
     if (!$repos) {
       throw new PhutilArgumentUsageException(
         pht(
           'Specify one or more repositories to update refs for.'));
     }
 
     $console = PhutilConsole::getConsole();
     foreach ($repos as $repo) {
       $console->writeOut(
         "%s\n",
         pht(
           'Updating refs in "%s"...',
           $repo->getDisplayName()));
 
       $engine = id(new PhabricatorRepositoryRefEngine())
         ->setRepository($repo)
         ->setVerbose($args->getArg('verbose'))
         ->updateRefs();
     }
 
     $console->writeOut("%s\n", pht('Done.'));
 
     return 0;
   }
 
 }
diff --git a/src/applications/repository/management/PhabricatorRepositoryManagementUpdateWorkflow.php b/src/applications/repository/management/PhabricatorRepositoryManagementUpdateWorkflow.php
index 9d987636b..f435861c6 100644
--- a/src/applications/repository/management/PhabricatorRepositoryManagementUpdateWorkflow.php
+++ b/src/applications/repository/management/PhabricatorRepositoryManagementUpdateWorkflow.php
@@ -1,200 +1,181 @@
 <?php
 
 final class PhabricatorRepositoryManagementUpdateWorkflow
   extends PhabricatorRepositoryManagementWorkflow {
 
   private $verbose;
 
   public function setVerbose($verbose) {
     $this->verbose = $verbose;
     return $this;
   }
 
   public function getVerbose() {
     return $this->verbose;
   }
 
   protected function didConstruct() {
     $this
       ->setName('update')
       ->setExamples('**update** [options] __repository__')
       ->setSynopsis(
         pht(
           'Update __repository__. This performs the __pull__, __discover__, '.
           '__refs__ and __mirror__ operations and is primarily an internal '.
           'workflow.'))
       ->setArguments(
         array(
           array(
             'name'        => 'verbose',
             'help'        => pht('Show additional debugging information.'),
           ),
           array(
             'name'        => 'no-discovery',
             'help'        => pht('Do not perform discovery.'),
           ),
           array(
             'name'        => 'repos',
             'wildcard'    => true,
           ),
         ));
   }
 
   public function execute(PhutilArgumentParser $args) {
     $this->setVerbose($args->getArg('verbose'));
     $console = PhutilConsole::getConsole();
 
-    $repos = $this->loadRepositories($args, 'repos');
+    $repos = $this->loadLocalRepositories($args, 'repos');
     if (count($repos) !== 1) {
       throw new PhutilArgumentUsageException(
         pht('Specify exactly one repository to update.'));
     }
 
     $repository = head($repos);
 
     try {
-      $lock_name = 'repository.update:'.$repository->getID();
-      $lock = PhabricatorGlobalLock::newLock($lock_name);
-
-      try {
-        $lock->lock();
-      } catch (PhutilLockException $ex) {
-        throw new PhutilProxyException(
-          pht(
-            'Another process is currently holding the update lock for '.
-            'repository "%s". Repositories may only be updated by one '.
-            'process at a time. This can happen if you are running multiple '.
-            'copies of the daemons. This can also happen if you manually '.
-            'update a repository while the daemons are also updating it '.
-            '(in this case, just try again in a few moments).',
-            $repository->getMonogram()),
-          $ex);
-      }
-
-      try {
-        $no_discovery = $args->getArg('no-discovery');
-
-        id(new PhabricatorRepositoryPullEngine())
-          ->setRepository($repository)
-          ->setVerbose($this->getVerbose())
-          ->pullRepository();
+      id(new PhabricatorRepositoryPullEngine())
+        ->setRepository($repository)
+        ->setVerbose($this->getVerbose())
+        ->pullRepository();
 
-        if ($no_discovery) {
-          $lock->unlock();
-          return;
-        }
+      $no_discovery = $args->getArg('no-discovery');
+      if ($no_discovery) {
+        return 0;
+      }
 
-        // TODO: It would be nice to discover only if we pulled something, but
-        // this isn't totally trivial. It's slightly more complicated with
-        // hosted repositories, too.
+      // TODO: It would be nice to discover only if we pulled something, but
+      // this isn't totally trivial. It's slightly more complicated with
+      // hosted repositories, too.
 
-        $repository->writeStatusMessage(
-          PhabricatorRepositoryStatusMessage::TYPE_NEEDS_UPDATE,
-          null);
+      $repository->writeStatusMessage(
+        PhabricatorRepositoryStatusMessage::TYPE_NEEDS_UPDATE,
+        null);
 
-        $this->discoverRepository($repository);
+      $this->discoverRepository($repository);
 
-        $this->checkIfRepositoryIsFullyImported($repository);
+      $this->checkIfRepositoryIsFullyImported($repository);
 
-        $this->updateRepositoryRefs($repository);
+      $this->updateRepositoryRefs($repository);
 
-        $this->mirrorRepository($repository);
+      $this->mirrorRepository($repository);
 
-        $repository->writeStatusMessage(
-          PhabricatorRepositoryStatusMessage::TYPE_FETCH,
-          PhabricatorRepositoryStatusMessage::CODE_OKAY);
-      } catch (Exception $ex) {
-        $lock->unlock();
-        throw $ex;
-      }
+      $repository->writeStatusMessage(
+        PhabricatorRepositoryStatusMessage::TYPE_FETCH,
+        PhabricatorRepositoryStatusMessage::CODE_OKAY);
+    } catch (DiffusionDaemonLockException $ex) {
+      // If we miss a pull or discover because some other process is already
+      // doing the work, just bail out.
+      echo tsprintf(
+        "%s\n",
+        $ex->getMessage());
+      return 0;
     } catch (Exception $ex) {
       $repository->writeStatusMessage(
         PhabricatorRepositoryStatusMessage::TYPE_FETCH,
         PhabricatorRepositoryStatusMessage::CODE_ERROR,
         array(
           'message' => pht(
             'Error updating working copy: %s', $ex->getMessage()),
         ));
       throw $ex;
     }
 
-    $lock->unlock();
-
-    $console->writeOut(
+    echo tsprintf(
+      "%s\n",
       pht(
-        'Updated repository **%s**.',
-        $repository->getMonogram())."\n");
+        'Updated repository "%s".',
+        $repository->getDisplayName()));
 
     return 0;
   }
 
   private function discoverRepository(PhabricatorRepository $repository) {
     $refs = id(new PhabricatorRepositoryDiscoveryEngine())
       ->setRepository($repository)
       ->setVerbose($this->getVerbose())
       ->discoverCommits();
 
     return (bool)count($refs);
   }
 
   private function mirrorRepository(PhabricatorRepository $repository) {
     try {
       id(new PhabricatorRepositoryMirrorEngine())
         ->setRepository($repository)
         ->pushToMirrors();
     } catch (Exception $ex) {
       // TODO: We should report these into the UI properly, but for now just
       // complain. These errors are much less severe than pull errors.
       $proxy = new PhutilProxyException(
         pht(
           'Error while pushing "%s" repository to mirrors.',
           $repository->getMonogram()),
         $ex);
       phlog($proxy);
     }
   }
 
   private function updateRepositoryRefs(PhabricatorRepository $repository) {
     id(new PhabricatorRepositoryRefEngine())
       ->setRepository($repository)
       ->updateRefs();
   }
 
   private function checkIfRepositoryIsFullyImported(
     PhabricatorRepository $repository) {
 
     // Check if the repository has the "Importing" flag set. We want to clear
     // the flag if we can.
     $importing = $repository->getDetail('importing');
     if (!$importing) {
       // This repository isn't marked as "Importing", so we're done.
       return;
     }
 
     // Look for any commit which hasn't imported.
     $unparsed_commit = queryfx_one(
       $repository->establishConnection('r'),
       'SELECT * FROM %T WHERE repositoryID = %d AND (importStatus & %d) != %d
         LIMIT 1',
       id(new PhabricatorRepositoryCommit())->getTableName(),
       $repository->getID(),
       PhabricatorRepositoryCommit::IMPORTED_ALL,
       PhabricatorRepositoryCommit::IMPORTED_ALL);
     if ($unparsed_commit) {
       // We found a commit which still needs to import, so we can't clear the
       // flag.
       return;
     }
 
     // Clear the "importing" flag.
     $repository->openTransaction();
       $repository->beginReadLocking();
         $repository = $repository->reload();
         $repository->setDetail('importing', false);
         $repository->save();
       $repository->endReadLocking();
     $repository->saveTransaction();
   }
 
 
 }
diff --git a/src/applications/repository/management/PhabricatorRepositoryManagementWorkflow.php b/src/applications/repository/management/PhabricatorRepositoryManagementWorkflow.php
index 300f8ec50..654a974e6 100644
--- a/src/applications/repository/management/PhabricatorRepositoryManagementWorkflow.php
+++ b/src/applications/repository/management/PhabricatorRepositoryManagementWorkflow.php
@@ -1,68 +1,95 @@
 <?php
 
 abstract class PhabricatorRepositoryManagementWorkflow
   extends PhabricatorManagementWorkflow {
 
   protected function loadRepositories(PhutilArgumentParser $args, $param) {
     $identifiers = $args->getArg($param);
 
     if (!$identifiers) {
       return null;
     }
 
     $query = id(new PhabricatorRepositoryQuery())
       ->setViewer($this->getViewer())
+      ->needURIs(true)
       ->withIdentifiers($identifiers);
 
     $query->execute();
 
     $map = $query->getIdentifierMap();
     foreach ($identifiers as $identifier) {
       if (empty($map[$identifier])) {
         throw new PhutilArgumentUsageException(
           pht(
             'Repository "%s" does not exist!',
             $identifier));
       }
     }
 
     // Reorder repositories according to argument order.
     $repositories = array_select_keys($map, $identifiers);
 
     return array_values($repositories);
   }
 
+  protected function loadLocalRepositories(
+    PhutilArgumentParser $args,
+    $param) {
+
+    $repositories = $this->loadRepositories($args, $param);
+    if (!$repositories) {
+      return $repositories;
+    }
+
+    $device = AlmanacKeys::getLiveDevice();
+    $viewer = $this->getViewer();
+
+    $filter = id(new DiffusionLocalRepositoryFilter())
+      ->setViewer($viewer)
+      ->setDevice($device)
+      ->setRepositories($repositories);
+
+    $repositories = $filter->execute();
+
+    foreach ($filter->getRejectionReasons() as $reason) {
+      throw new PhutilArgumentUsageException($reason);
+    }
+
+    return $repositories;
+  }
+
   protected function loadCommits(PhutilArgumentParser $args, $param) {
     $names = $args->getArg($param);
     if (!$names) {
       return null;
     }
 
     return $this->loadNamedCommits($names);
   }
 
   protected function loadNamedCommit($name) {
     $map = $this->loadNamedCommits(array($name));
     return $map[$name];
   }
 
   protected function loadNamedCommits(array $names) {
     $query = id(new DiffusionCommitQuery())
       ->setViewer($this->getViewer())
       ->withIdentifiers($names);
 
     $query->execute();
     $map = $query->getIdentifierMap();
 
     foreach ($names as $name) {
       if (empty($map[$name])) {
         throw new PhutilArgumentUsageException(
           pht('Commit "%s" does not exist or is ambiguous.', $name));
       }
     }
 
     return $map;
   }
 
 
 }
diff --git a/src/applications/repository/phid/PhabricatorRepositoryMirrorPHIDType.php b/src/applications/repository/phid/PhabricatorRepositoryMirrorPHIDType.php
deleted file mode 100644
index 42737c728..000000000
--- a/src/applications/repository/phid/PhabricatorRepositoryMirrorPHIDType.php
+++ /dev/null
@@ -1,41 +0,0 @@
-<?php
-
-final class PhabricatorRepositoryMirrorPHIDType extends PhabricatorPHIDType {
-
-  const TYPECONST = 'RMIR';
-
-  public function getTypeName() {
-    return pht('Repository Mirror');
-  }
-
-  public function newObject() {
-    return new PhabricatorRepositoryMirror();
-  }
-
-  public function getPHIDTypeApplicationClass() {
-    return 'PhabricatorDiffusionApplication';
-  }
-
-  protected function buildQueryForObjects(
-    PhabricatorObjectQuery $query,
-    array $phids) {
-
-    return id(new PhabricatorRepositoryMirrorQuery())
-      ->withPHIDs($phids);
-  }
-
-  public function loadHandles(
-    PhabricatorHandleQuery $query,
-    array $handles,
-    array $objects) {
-
-    foreach ($handles as $phid => $handle) {
-      $mirror = $objects[$phid];
-
-      $handle->setName(
-        pht('Mirror %d %s', $mirror->getID(), $mirror->getRemoteURI()));
-      $handle->setURI('/diffusion/mirror/'.$mirror->getID().'/');
-    }
-  }
-
-}
diff --git a/src/applications/repository/query/PhabricatorRepositoryMirrorQuery.php b/src/applications/repository/query/PhabricatorRepositoryMirrorQuery.php
deleted file mode 100644
index 84c5ef74c..000000000
--- a/src/applications/repository/query/PhabricatorRepositoryMirrorQuery.php
+++ /dev/null
@@ -1,99 +0,0 @@
-<?php
-
-final class PhabricatorRepositoryMirrorQuery
-  extends PhabricatorCursorPagedPolicyAwareQuery {
-
-  private $ids;
-  private $phids;
-  private $repositoryPHIDs;
-
-  public function withIDs(array $ids) {
-    $this->ids = $ids;
-    return $this;
-  }
-
-  public function withPHIDs(array $phids) {
-    $this->phids = $phids;
-    return $this;
-  }
-
-  public function withRepositoryPHIDs(array $repository_phids) {
-    $this->repositoryPHIDs = $repository_phids;
-    return $this;
-  }
-
-  protected function loadPage() {
-    $table = new PhabricatorRepositoryMirror();
-    $conn_r = $table->establishConnection('r');
-
-    $data = queryfx_all(
-      $conn_r,
-      'SELECT * FROM %T %Q %Q %Q',
-      $table->getTableName(),
-      $this->buildWhereClause($conn_r),
-      $this->buildOrderClause($conn_r),
-      $this->buildLimitClause($conn_r));
-
-    return $table->loadAllFromArray($data);
-  }
-
-  protected function willFilterPage(array $mirrors) {
-    assert_instances_of($mirrors, 'PhabricatorRepositoryMirror');
-
-    $repository_phids = mpull($mirrors, 'getRepositoryPHID');
-    if ($repository_phids) {
-      $repositories = id(new PhabricatorRepositoryQuery())
-        ->setViewer($this->getViewer())
-        ->withPHIDs($repository_phids)
-        ->execute();
-      $repositories = mpull($repositories, null, 'getPHID');
-    } else {
-      $repositories = array();
-    }
-
-    foreach ($mirrors as $key => $mirror) {
-      $phid = $mirror->getRepositoryPHID();
-      if (empty($repositories[$phid])) {
-        unset($mirrors[$key]);
-        continue;
-      }
-      $mirror->attachRepository($repositories[$phid]);
-    }
-
-    return $mirrors;
-  }
-
-  protected function buildWhereClause(AphrontDatabaseConnection $conn_r) {
-    $where = array();
-
-    if ($this->ids) {
-      $where[] = qsprintf(
-        $conn_r,
-        'id IN (%Ld)',
-        $this->ids);
-    }
-
-    if ($this->phids) {
-      $where[] = qsprintf(
-        $conn_r,
-        'phid IN (%Ls)',
-        $this->phids);
-    }
-
-    if ($this->repositoryPHIDs) {
-      $where[] = qsprintf(
-        $conn_r,
-        'repositoryPHID IN (%Ls)',
-        $this->repositoryPHIDs);
-    }
-
-    $where[] = $this->buildPagingClause($conn_r);
-
-    return $this->formatWhereClause($where);
-  }
-
-  public function getQueryApplicationClass() {
-    return 'PhabricatorDiffusionApplication';
-  }
-
-}
diff --git a/src/applications/repository/query/PhabricatorRepositoryQuery.php b/src/applications/repository/query/PhabricatorRepositoryQuery.php
index 78693d54d..b67b3c561 100644
--- a/src/applications/repository/query/PhabricatorRepositoryQuery.php
+++ b/src/applications/repository/query/PhabricatorRepositoryQuery.php
@@ -1,693 +1,689 @@
 <?php
 
 final class PhabricatorRepositoryQuery
   extends PhabricatorCursorPagedPolicyAwareQuery {
 
   private $ids;
   private $phids;
   private $callsigns;
   private $types;
   private $uuids;
   private $nameContains;
   private $uris;
   private $datasourceQuery;
   private $slugs;
 
   private $numericIdentifiers;
   private $callsignIdentifiers;
   private $phidIdentifiers;
   private $monogramIdentifiers;
   private $slugIdentifiers;
 
   private $identifierMap;
 
   const STATUS_OPEN = 'status-open';
   const STATUS_CLOSED = 'status-closed';
   const STATUS_ALL = 'status-all';
   private $status = self::STATUS_ALL;
 
   const HOSTED_PHABRICATOR = 'hosted-phab';
   const HOSTED_REMOTE = 'hosted-remote';
   const HOSTED_ALL = 'hosted-all';
   private $hosted = self::HOSTED_ALL;
 
   private $needMostRecentCommits;
   private $needCommitCounts;
   private $needProjectPHIDs;
   private $needURIs;
 
   public function withIDs(array $ids) {
     $this->ids = $ids;
     return $this;
   }
 
   public function withPHIDs(array $phids) {
     $this->phids = $phids;
     return $this;
   }
 
   public function withCallsigns(array $callsigns) {
     $this->callsigns = $callsigns;
     return $this;
   }
 
   public function withIdentifiers(array $identifiers) {
     $identifiers = array_fuse($identifiers);
 
     $ids = array();
     $callsigns = array();
     $phids = array();
     $monograms = array();
     $slugs = array();
 
     foreach ($identifiers as $identifier) {
       if (ctype_digit((string)$identifier)) {
         $ids[$identifier] = $identifier;
         continue;
       }
 
       if (preg_match('/^(r[A-Z]+|R[1-9]\d*)\z/', $identifier)) {
         $monograms[$identifier] = $identifier;
         continue;
       }
 
       $repository_type = PhabricatorRepositoryRepositoryPHIDType::TYPECONST;
       if (phid_get_type($identifier) === $repository_type) {
         $phids[$identifier] = $identifier;
         continue;
       }
 
       if (preg_match('/^[A-Z]+\z/', $identifier)) {
         $callsigns[$identifier] = $identifier;
         continue;
       }
 
       $slugs[$identifier] = $identifier;
     }
 
     $this->numericIdentifiers = $ids;
     $this->callsignIdentifiers = $callsigns;
     $this->phidIdentifiers = $phids;
     $this->monogramIdentifiers = $monograms;
     $this->slugIdentifiers = $slugs;
 
     return $this;
   }
 
   public function withStatus($status) {
     $this->status = $status;
     return $this;
   }
 
   public function withHosted($hosted) {
     $this->hosted = $hosted;
     return $this;
   }
 
   public function withTypes(array $types) {
     $this->types = $types;
     return $this;
   }
 
   public function withUUIDs(array $uuids) {
     $this->uuids = $uuids;
     return $this;
   }
 
   public function withNameContains($contains) {
     $this->nameContains = $contains;
     return $this;
   }
 
   public function withURIs(array $uris) {
     $this->uris = $uris;
     return $this;
   }
 
   public function withDatasourceQuery($query) {
     $this->datasourceQuery = $query;
     return $this;
   }
 
   public function withSlugs(array $slugs) {
     $this->slugs = $slugs;
     return $this;
   }
 
   public function needCommitCounts($need_counts) {
     $this->needCommitCounts = $need_counts;
     return $this;
   }
 
   public function needMostRecentCommits($need_commits) {
     $this->needMostRecentCommits = $need_commits;
     return $this;
   }
 
   public function needProjectPHIDs($need_phids) {
     $this->needProjectPHIDs = $need_phids;
     return $this;
   }
 
   public function needURIs($need_uris) {
     $this->needURIs = $need_uris;
     return $this;
   }
 
   public function getBuiltinOrders() {
     return array(
       'committed' => array(
         'vector' => array('committed', 'id'),
         'name' => pht('Most Recent Commit'),
       ),
       'name' => array(
         'vector' => array('name', 'id'),
         'name' => pht('Name'),
       ),
       'callsign' => array(
         'vector' => array('callsign'),
         'name' => pht('Callsign'),
       ),
       'size' => array(
         'vector' => array('size', 'id'),
         'name' => pht('Size'),
       ),
     ) + parent::getBuiltinOrders();
   }
 
   public function getIdentifierMap() {
     if ($this->identifierMap === null) {
       throw new PhutilInvalidStateException('execute');
     }
     return $this->identifierMap;
   }
 
   protected function willExecute() {
     $this->identifierMap = array();
   }
 
   public function newResultObject() {
     return new PhabricatorRepository();
   }
 
   protected function loadPage() {
     $table = $this->newResultObject();
     $data = $this->loadStandardPageRows($table);
     $repositories = $table->loadAllFromArray($data);
 
     if ($this->needCommitCounts) {
       $sizes = ipull($data, 'size', 'id');
       foreach ($repositories as $id => $repository) {
         $repository->attachCommitCount(nonempty($sizes[$id], 0));
       }
     }
 
     if ($this->needMostRecentCommits) {
       $commit_ids = ipull($data, 'lastCommitID', 'id');
       $commit_ids = array_filter($commit_ids);
       if ($commit_ids) {
         $commits = id(new DiffusionCommitQuery())
           ->setViewer($this->getViewer())
           ->withIDs($commit_ids)
           ->execute();
       } else {
         $commits = array();
       }
       foreach ($repositories as $id => $repository) {
         $commit = null;
         if (idx($commit_ids, $id)) {
           $commit = idx($commits, $commit_ids[$id]);
         }
         $repository->attachMostRecentCommit($commit);
       }
     }
 
     return $repositories;
   }
 
   protected function willFilterPage(array $repositories) {
     assert_instances_of($repositories, 'PhabricatorRepository');
 
     // TODO: Denormalize repository status into the PhabricatorRepository
     // table so we can do this filtering in the database.
     foreach ($repositories as $key => $repo) {
       $status = $this->status;
       switch ($status) {
         case self::STATUS_OPEN:
           if (!$repo->isTracked()) {
             unset($repositories[$key]);
           }
           break;
         case self::STATUS_CLOSED:
           if ($repo->isTracked()) {
             unset($repositories[$key]);
           }
           break;
         case self::STATUS_ALL:
           break;
         default:
           throw new Exception("Unknown status '{$status}'!");
       }
 
       // TODO: This should also be denormalized.
       $hosted = $this->hosted;
       switch ($hosted) {
         case self::HOSTED_PHABRICATOR:
           if (!$repo->isHosted()) {
             unset($repositories[$key]);
           }
           break;
         case self::HOSTED_REMOTE:
           if ($repo->isHosted()) {
             unset($repositories[$key]);
           }
           break;
         case self::HOSTED_ALL:
           break;
         default:
           throw new Exception(pht("Unknown hosted failed '%s'!", $hosted));
       }
     }
 
     // Build the identifierMap
     if ($this->numericIdentifiers) {
       foreach ($this->numericIdentifiers as $id) {
         if (isset($repositories[$id])) {
           $this->identifierMap[$id] = $repositories[$id];
         }
       }
     }
 
     if ($this->callsignIdentifiers) {
       $repository_callsigns = mpull($repositories, null, 'getCallsign');
 
       foreach ($this->callsignIdentifiers as $callsign) {
         if (isset($repository_callsigns[$callsign])) {
           $this->identifierMap[$callsign] = $repository_callsigns[$callsign];
         }
       }
     }
 
     if ($this->phidIdentifiers) {
       $repository_phids = mpull($repositories, null, 'getPHID');
 
       foreach ($this->phidIdentifiers as $phid) {
         if (isset($repository_phids[$phid])) {
           $this->identifierMap[$phid] = $repository_phids[$phid];
         }
       }
     }
 
     if ($this->monogramIdentifiers) {
       $monogram_map = array();
       foreach ($repositories as $repository) {
         foreach ($repository->getAllMonograms() as $monogram) {
           $monogram_map[$monogram] = $repository;
         }
       }
 
       foreach ($this->monogramIdentifiers as $monogram) {
         if (isset($monogram_map[$monogram])) {
           $this->identifierMap[$monogram] = $monogram_map[$monogram];
         }
       }
     }
 
     if ($this->slugIdentifiers) {
       $slug_map = array();
       foreach ($repositories as $repository) {
         $slug = $repository->getRepositorySlug();
         if ($slug === null) {
           continue;
         }
 
         $normal = phutil_utf8_strtolower($slug);
         $slug_map[$normal] = $repository;
       }
 
       foreach ($this->slugIdentifiers as $slug) {
         $normal = phutil_utf8_strtolower($slug);
         if (isset($slug_map[$normal])) {
           $this->identifierMap[$slug] = $slug_map[$normal];
         }
       }
     }
 
     return $repositories;
   }
 
   protected function didFilterPage(array $repositories) {
     if ($this->needProjectPHIDs) {
       $type_project = PhabricatorProjectObjectHasProjectEdgeType::EDGECONST;
 
       $edge_query = id(new PhabricatorEdgeQuery())
         ->withSourcePHIDs(mpull($repositories, 'getPHID'))
         ->withEdgeTypes(array($type_project));
       $edge_query->execute();
 
       foreach ($repositories as $repository) {
         $project_phids = $edge_query->getDestinationPHIDs(
           array(
             $repository->getPHID(),
           ));
         $repository->attachProjectPHIDs($project_phids);
       }
     }
 
     $viewer = $this->getViewer();
 
     if ($this->needURIs) {
       $uris = id(new PhabricatorRepositoryURIQuery())
         ->setViewer($viewer)
         ->withRepositories($repositories)
         ->execute();
       $uri_groups = mgroup($uris, 'getRepositoryPHID');
       foreach ($repositories as $repository) {
         $repository_uris = idx($uri_groups, $repository->getPHID(), array());
         $repository->attachURIs($repository_uris);
       }
     }
 
     return $repositories;
   }
 
   protected function getPrimaryTableAlias() {
     return 'r';
   }
 
   public function getOrderableColumns() {
     return parent::getOrderableColumns() + array(
       'committed' => array(
         'table' => 's',
         'column' => 'epoch',
         'type' => 'int',
         'null' => 'tail',
       ),
       'callsign' => array(
         'table' => 'r',
         'column' => 'callsign',
         'type' => 'string',
         'unique' => true,
         'reverse' => true,
       ),
       'name' => array(
         'table' => 'r',
         'column' => 'name',
         'type' => 'string',
         'reverse' => true,
       ),
       'size' => array(
         'table' => 's',
         'column' => 'size',
         'type' => 'int',
         'null' => 'tail',
       ),
     );
   }
 
   protected function willExecuteCursorQuery(
     PhabricatorCursorPagedPolicyAwareQuery $query) {
     $vector = $this->getOrderVector();
 
     if ($vector->containsKey('committed')) {
       $query->needMostRecentCommits(true);
     }
 
     if ($vector->containsKey('size')) {
       $query->needCommitCounts(true);
     }
   }
 
   protected function getPagingValueMap($cursor, array $keys) {
     $repository = $this->loadCursorObject($cursor);
 
     $map = array(
       'id' => $repository->getID(),
       'callsign' => $repository->getCallsign(),
       'name' => $repository->getName(),
     );
 
     foreach ($keys as $key) {
       switch ($key) {
         case 'committed':
           $commit = $repository->getMostRecentCommit();
           if ($commit) {
             $map[$key] = $commit->getEpoch();
           } else {
             $map[$key] = null;
           }
           break;
         case 'size':
           $count = $repository->getCommitCount();
           if ($count) {
             $map[$key] = $count;
           } else {
             $map[$key] = null;
           }
           break;
       }
     }
 
     return $map;
   }
 
   protected function buildSelectClauseParts(AphrontDatabaseConnection $conn) {
     $parts = parent::buildSelectClauseParts($conn);
 
     $parts[] = 'r.*';
 
     if ($this->shouldJoinSummaryTable()) {
       $parts[] = 's.*';
     }
 
     return $parts;
   }
 
   protected function buildJoinClauseParts(AphrontDatabaseConnection $conn) {
     $joins = parent::buildJoinClauseParts($conn);
 
     if ($this->shouldJoinSummaryTable()) {
       $joins[] = qsprintf(
         $conn,
         'LEFT JOIN %T s ON r.id = s.repositoryID',
         PhabricatorRepository::TABLE_SUMMARY);
     }
 
     if ($this->shouldJoinURITable()) {
       $joins[] = qsprintf(
         $conn,
         'LEFT JOIN %T uri ON r.phid = uri.repositoryPHID',
         id(new PhabricatorRepositoryURIIndex())->getTableName());
     }
 
     return $joins;
   }
 
   protected function shouldGroupQueryResultRows() {
     if ($this->shouldJoinURITable()) {
       return true;
     }
 
     return parent::shouldGroupQueryResultRows();
   }
 
   private function shouldJoinURITable() {
     return ($this->uris !== null);
   }
 
   private function shouldJoinSummaryTable() {
     if ($this->needCommitCounts) {
       return true;
     }
 
     if ($this->needMostRecentCommits) {
       return true;
     }
 
     $vector = $this->getOrderVector();
     if ($vector->containsKey('committed')) {
       return true;
     }
 
     if ($vector->containsKey('size')) {
       return true;
     }
 
     return false;
   }
 
   protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) {
     $where = parent::buildWhereClauseParts($conn);
 
     if ($this->ids !== null) {
       $where[] = qsprintf(
         $conn,
         'r.id IN (%Ld)',
         $this->ids);
     }
 
     if ($this->phids !== null) {
       $where[] = qsprintf(
         $conn,
         'r.phid IN (%Ls)',
         $this->phids);
     }
 
     if ($this->callsigns !== null) {
       $where[] = qsprintf(
         $conn,
         'r.callsign IN (%Ls)',
         $this->callsigns);
     }
 
     if ($this->numericIdentifiers ||
       $this->callsignIdentifiers ||
       $this->phidIdentifiers ||
       $this->monogramIdentifiers ||
       $this->slugIdentifiers) {
       $identifier_clause = array();
 
       if ($this->numericIdentifiers) {
         $identifier_clause[] = qsprintf(
           $conn,
           'r.id IN (%Ld)',
           $this->numericIdentifiers);
       }
 
       if ($this->callsignIdentifiers) {
         $identifier_clause[] = qsprintf(
           $conn,
           'r.callsign IN (%Ls)',
           $this->callsignIdentifiers);
       }
 
       if ($this->phidIdentifiers) {
         $identifier_clause[] = qsprintf(
           $conn,
           'r.phid IN (%Ls)',
           $this->phidIdentifiers);
       }
 
       if ($this->monogramIdentifiers) {
         $monogram_callsigns = array();
         $monogram_ids = array();
 
         foreach ($this->monogramIdentifiers as $identifier) {
           if ($identifier[0] == 'r') {
             $monogram_callsigns[] = substr($identifier, 1);
           } else {
             $monogram_ids[] = substr($identifier, 1);
           }
         }
 
         if ($monogram_ids) {
           $identifier_clause[] = qsprintf(
             $conn,
             'r.id IN (%Ld)',
             $monogram_ids);
         }
 
         if ($monogram_callsigns) {
           $identifier_clause[] = qsprintf(
             $conn,
             'r.callsign IN (%Ls)',
             $monogram_callsigns);
         }
       }
 
       if ($this->slugIdentifiers) {
         $identifier_clause[] = qsprintf(
           $conn,
           'r.repositorySlug IN (%Ls)',
           $this->slugIdentifiers);
       }
 
       $where = array('('.implode(' OR ', $identifier_clause).')');
     }
 
     if ($this->types) {
       $where[] = qsprintf(
         $conn,
         'r.versionControlSystem IN (%Ls)',
         $this->types);
     }
 
     if ($this->uuids) {
       $where[] = qsprintf(
         $conn,
         'r.uuid IN (%Ls)',
         $this->uuids);
     }
 
     if (strlen($this->nameContains)) {
       $where[] = qsprintf(
         $conn,
         'r.name LIKE %~',
         $this->nameContains);
     }
 
     if (strlen($this->datasourceQuery)) {
       // This handles having "rP" match callsigns starting with "P...".
       $query = trim($this->datasourceQuery);
       if (preg_match('/^r/', $query)) {
         $callsign = substr($query, 1);
       } else {
         $callsign = $query;
       }
       $where[] = qsprintf(
         $conn,
         'r.name LIKE %> OR r.callsign LIKE %> OR r.repositorySlug LIKE %>',
         $query,
         $callsign,
         $query);
     }
 
     if ($this->slugs !== null) {
       $where[] = qsprintf(
         $conn,
         'r.repositorySlug IN (%Ls)',
         $this->slugs);
     }
 
     if ($this->uris !== null) {
-      $try_uris = $this->getNormalizedPaths();
+      $try_uris = $this->getNormalizedURIs();
       $try_uris = array_fuse($try_uris);
 
       $where[] = qsprintf(
         $conn,
         'uri.repositoryURI IN (%Ls)',
         $try_uris);
     }
 
     return $where;
   }
 
   public function getQueryApplicationClass() {
     return 'PhabricatorDiffusionApplication';
   }
 
-  private function getNormalizedPaths() {
+  private function getNormalizedURIs() {
     $normalized_uris = array();
 
     // Since we don't know which type of repository this URI is in the general
     // case, just generate all the normalizations. We could refine this in some
     // cases: if the query specifies VCS types, or the URI is a git-style URI
     // or an `svn+ssh` URI, we could deduce how to normalize it. However, this
     // would be more complicated and it's not clear if it matters in practice.
 
+    $types = PhabricatorRepositoryURINormalizer::getAllURITypes();
     foreach ($this->uris as $uri) {
-      $normalized_uris[] = new PhabricatorRepositoryURINormalizer(
-        PhabricatorRepositoryURINormalizer::TYPE_GIT,
-        $uri);
-      $normalized_uris[] = new PhabricatorRepositoryURINormalizer(
-        PhabricatorRepositoryURINormalizer::TYPE_SVN,
-        $uri);
-      $normalized_uris[] = new PhabricatorRepositoryURINormalizer(
-        PhabricatorRepositoryURINormalizer::TYPE_MERCURIAL,
-        $uri);
-    }
-
-    return array_unique(mpull($normalized_uris, 'getNormalizedPath'));
+      foreach ($types as $type) {
+        $normalized_uri = new PhabricatorRepositoryURINormalizer($type, $uri);
+        $normalized_uris[] = $normalized_uri->getNormalizedURI();
+      }
+    }
+
+    return array_unique($normalized_uris);
   }
 
 }
diff --git a/src/applications/repository/query/PhabricatorRepositorySearchEngine.php b/src/applications/repository/query/PhabricatorRepositorySearchEngine.php
index 059ae9a76..78f29fad1 100644
--- a/src/applications/repository/query/PhabricatorRepositorySearchEngine.php
+++ b/src/applications/repository/query/PhabricatorRepositorySearchEngine.php
@@ -1,270 +1,272 @@
 <?php
 
 final class PhabricatorRepositorySearchEngine
   extends PhabricatorApplicationSearchEngine {
 
   public function getResultTypeDescription() {
     return pht('Repositories');
   }
 
   public function getApplicationClassName() {
     return 'PhabricatorDiffusionApplication';
   }
 
   public function newQuery() {
     return id(new PhabricatorRepositoryQuery())
       ->needProjectPHIDs(true)
       ->needCommitCounts(true)
       ->needMostRecentCommits(true);
   }
 
   protected function buildCustomSearchFields() {
     return array(
       id(new PhabricatorSearchStringListField())
         ->setLabel(pht('Callsigns'))
         ->setKey('callsigns'),
       id(new PhabricatorSearchTextField())
         ->setLabel(pht('Name Contains'))
         ->setKey('name'),
       id(new PhabricatorSearchSelectField())
         ->setLabel(pht('Status'))
         ->setKey('status')
         ->setOptions($this->getStatusOptions()),
       id(new PhabricatorSearchSelectField())
         ->setLabel(pht('Hosted'))
         ->setKey('hosted')
         ->setOptions($this->getHostedOptions()),
       id(new PhabricatorSearchCheckboxesField())
         ->setLabel(pht('Types'))
         ->setKey('types')
         ->setOptions(PhabricatorRepositoryType::getAllRepositoryTypes()),
+      id(new PhabricatorSearchStringListField())
+        ->setLabel(pht('URIs'))
+        ->setKey('uris')
+        ->setDescription(
+          pht('Search for repositories by clone/checkout URI.')),
     );
   }
 
   protected function buildQueryFromParameters(array $map) {
     $query = $this->newQuery();
 
     if ($map['callsigns']) {
       $query->withCallsigns($map['callsigns']);
     }
 
     if ($map['status']) {
       $status = idx($this->getStatusValues(), $map['status']);
       if ($status) {
         $query->withStatus($status);
       }
     }
 
     if ($map['hosted']) {
       $hosted = idx($this->getHostedValues(), $map['hosted']);
       if ($hosted) {
         $query->withHosted($hosted);
       }
     }
 
     if ($map['types']) {
       $query->withTypes($map['types']);
     }
 
     if (strlen($map['name'])) {
       $query->withNameContains($map['name']);
     }
 
+    if ($map['uris']) {
+      $query->withURIs($map['uris']);
+    }
+
     return $query;
   }
 
   protected function getURI($path) {
     return '/diffusion/'.$path;
   }
 
   protected function getBuiltinQueryNames() {
     $names = array(
       'active' => pht('Active Repositories'),
       'all' => pht('All Repositories'),
     );
 
     return $names;
   }
 
   public function buildSavedQueryFromBuiltin($query_key) {
 
     $query = $this->newSavedQuery();
     $query->setQueryKey($query_key);
 
     switch ($query_key) {
       case 'active':
         return $query->setParameter('status', 'open');
       case 'all':
         return $query;
     }
 
     return parent::buildSavedQueryFromBuiltin($query_key);
   }
 
   private function getStatusOptions() {
     return array(
       '' => pht('Active and Inactive Repositories'),
       'open' => pht('Active Repositories'),
       'closed' => pht('Inactive Repositories'),
     );
   }
 
   private function getStatusValues() {
     return array(
       '' => PhabricatorRepositoryQuery::STATUS_ALL,
       'open' => PhabricatorRepositoryQuery::STATUS_OPEN,
       'closed' => PhabricatorRepositoryQuery::STATUS_CLOSED,
     );
   }
 
   private function getHostedOptions() {
     return array(
       '' => pht('Hosted and Remote Repositories'),
       'phabricator' => pht('Hosted Repositories'),
       'remote' => pht('Remote Repositories'),
     );
   }
 
   private function getHostedValues() {
     return array(
       '' => PhabricatorRepositoryQuery::HOSTED_ALL,
       'phabricator' => PhabricatorRepositoryQuery::HOSTED_PHABRICATOR,
       'remote' => PhabricatorRepositoryQuery::HOSTED_REMOTE,
     );
   }
 
   protected function getRequiredHandlePHIDsForResultList(
     array $repositories,
     PhabricatorSavedQuery $query) {
     return array_mergev(mpull($repositories, 'getProjectPHIDs'));
   }
 
   protected function renderResultList(
     array $repositories,
     PhabricatorSavedQuery $query,
     array $handles) {
     assert_instances_of($repositories, 'PhabricatorRepository');
 
     $viewer = $this->requireViewer();
 
     $list = new PHUIObjectItemListView();
     foreach ($repositories as $repository) {
       $id = $repository->getID();
 
       $item = id(new PHUIObjectItemView())
         ->setUser($viewer)
         ->setObject($repository)
         ->setHeader($repository->getName())
         ->setObjectName($repository->getMonogram())
         ->setHref($repository->getURI());
 
       $commit = $repository->getMostRecentCommit();
       if ($commit) {
         $commit_link = phutil_tag(
           'a',
           array(
             'href' => $commit->getURI(),
           ),
           pht(
             '%s: %s',
             $commit->getLocalName(),
             $commit->getSummary()));
 
         $item->setSubhead($commit_link);
         $item->setEpoch($commit->getEpoch());
       }
 
       $item->addIcon(
         'none',
         PhabricatorRepositoryType::getNameForRepositoryType(
           $repository->getVersionControlSystem()));
 
       $size = $repository->getCommitCount();
       if ($size) {
         $history_uri = $repository->generateURI(
           array(
             'action' => 'history',
           ));
 
         $item->addAttribute(
           phutil_tag(
             'a',
             array(
               'href' => $history_uri,
             ),
             pht('%s Commit(s)', new PhutilNumber($size))));
       } else {
         $item->addAttribute(pht('No Commits'));
       }
 
       $project_handles = array_select_keys(
         $handles,
         $repository->getProjectPHIDs());
       if ($project_handles) {
         $item->addAttribute(
           id(new PHUIHandleTagListView())
             ->setSlim(true)
             ->setHandles($project_handles));
       }
 
       if (!$repository->isTracked()) {
         $item->setDisabled(true);
         $item->addIcon('disable-grey', pht('Inactive'));
       } else if ($repository->isImporting()) {
         $item->addIcon('fa-clock-o indigo', pht('Importing...'));
       }
 
       $list->addItem($item);
     }
 
     $result = new PhabricatorApplicationSearchResultView();
     $result->setObjectList($list);
     $result->setNoDataString(pht('No repositories found for this query.'));
 
     return $result;
   }
 
   protected function willUseSavedQuery(PhabricatorSavedQuery $saved) {
     $project_phids = $saved->getParameter('projectPHIDs', array());
 
     $old = $saved->getParameter('projects', array());
     foreach ($old as $phid) {
       $project_phids[] = $phid;
     }
 
     $any = $saved->getParameter('anyProjectPHIDs', array());
     foreach ($any as $project) {
       $project_phids[] = 'any('.$project.')';
     }
 
     $saved->setParameter('projectPHIDs', $project_phids);
   }
 
   protected function getNewUserBody() {
 
-    $import_button = id(new PHUIButtonView())
-      ->setTag('a')
-      ->setText(pht('Import Repository'))
-      ->setHref('/diffusion/import/')
-      ->setColor(PHUIButtonView::GREEN);
-
-    $create_button = id(new PHUIButtonView())
+    $new_button = id(new PHUIButtonView())
       ->setTag('a')
-      ->setText(pht('Create Repository'))
-      ->setHref('/diffusion/create/')
+      ->setText(pht('New Repository'))
+      ->setHref('/diffusion/edit/')
       ->setColor(PHUIButtonView::GREEN);
 
     $icon = $this->getApplication()->getIcon();
     $app_name =  $this->getApplication()->getName();
     $view = id(new PHUIBigInfoView())
       ->setIcon($icon)
       ->setTitle(pht('Welcome to %s', $app_name))
       ->setDescription(
         pht('Import, create, or just browse repositories in Diffusion.'))
-      ->addAction($import_button)
-      ->addAction($create_button);
+      ->addAction($new_button);
 
       return $view;
   }
 
 }
diff --git a/src/applications/repository/storage/PhabricatorRepository.php b/src/applications/repository/storage/PhabricatorRepository.php
index be77852be..bc786e46a 100644
--- a/src/applications/repository/storage/PhabricatorRepository.php
+++ b/src/applications/repository/storage/PhabricatorRepository.php
@@ -1,2481 +1,2423 @@
 <?php
 
 /**
  * @task uri        Repository URI Management
  * @task autoclose  Autoclose
  * @task sync       Cluster Synchronization
  */
 final class PhabricatorRepository extends PhabricatorRepositoryDAO
   implements
     PhabricatorApplicationTransactionInterface,
     PhabricatorPolicyInterface,
     PhabricatorFlaggableInterface,
     PhabricatorMarkupInterface,
     PhabricatorDestructibleInterface,
     PhabricatorProjectInterface,
     PhabricatorSpacesInterface,
     PhabricatorConduitResultInterface {
 
   /**
    * Shortest hash we'll recognize in raw "a829f32" form.
    */
   const MINIMUM_UNQUALIFIED_HASH = 7;
 
   /**
    * Shortest hash we'll recognize in qualified "rXab7ef2f8" form.
    */
   const MINIMUM_QUALIFIED_HASH = 5;
 
+  /**
+   * Minimum number of commits to an empty repository to trigger "import" mode.
+   */
+  const IMPORT_THRESHOLD = 7;
+
   const TABLE_PATH = 'repository_path';
   const TABLE_PATHCHANGE = 'repository_pathchange';
   const TABLE_FILESYSTEM = 'repository_filesystem';
   const TABLE_SUMMARY = 'repository_summary';
   const TABLE_BADCOMMIT = 'repository_badcommit';
   const TABLE_LINTMESSAGE = 'repository_lintmessage';
   const TABLE_PARENTS = 'repository_parents';
   const TABLE_COVERAGE = 'repository_coverage';
 
-  const SERVE_OFF = 'off';
-  const SERVE_READONLY = 'readonly';
-  const SERVE_READWRITE = 'readwrite';
-
   const BECAUSE_REPOSITORY_IMPORTING = 'auto/importing';
   const BECAUSE_AUTOCLOSE_DISABLED = 'auto/disabled';
   const BECAUSE_NOT_ON_AUTOCLOSE_BRANCH = 'auto/nobranch';
   const BECAUSE_BRANCH_UNTRACKED = 'auto/notrack';
   const BECAUSE_BRANCH_NOT_AUTOCLOSE = 'auto/noclose';
   const BECAUSE_AUTOCLOSE_FORCED = 'auto/forced';
 
   const STATUS_ACTIVE = 'active';
   const STATUS_INACTIVE = 'inactive';
 
   protected $name;
   protected $callsign;
   protected $repositorySlug;
   protected $uuid;
   protected $viewPolicy;
   protected $editPolicy;
   protected $pushPolicy;
 
   protected $versionControlSystem;
   protected $details = array();
   protected $credentialPHID;
   protected $almanacServicePHID;
   protected $spacePHID;
+  protected $localPath;
 
   private $commitCount = self::ATTACHABLE;
   private $mostRecentCommit = self::ATTACHABLE;
   private $projectPHIDs = self::ATTACHABLE;
   private $uris = self::ATTACHABLE;
 
 
   public static function initializeNewRepository(PhabricatorUser $actor) {
     $app = id(new PhabricatorApplicationQuery())
       ->setViewer($actor)
       ->withClasses(array('PhabricatorDiffusionApplication'))
       ->executeOne();
 
     $view_policy = $app->getPolicy(DiffusionDefaultViewCapability::CAPABILITY);
     $edit_policy = $app->getPolicy(DiffusionDefaultEditCapability::CAPABILITY);
     $push_policy = $app->getPolicy(DiffusionDefaultPushCapability::CAPABILITY);
 
     $repository = id(new PhabricatorRepository())
       ->setViewPolicy($view_policy)
       ->setEditPolicy($edit_policy)
       ->setPushPolicy($push_policy)
       ->setSpacePHID($actor->getDefaultSpacePHID());
 
     // Put the repository in "Importing" mode until we finish
     // parsing it.
     $repository->setDetail('importing', true);
 
     return $repository;
   }
 
   protected function getConfiguration() {
     return array(
       self::CONFIG_AUX_PHID => true,
       self::CONFIG_SERIALIZATION => array(
         'details' => self::SERIALIZATION_JSON,
       ),
       self::CONFIG_COLUMN_SCHEMA => array(
         'name' => 'sort255',
         'callsign' => 'sort32?',
         'repositorySlug' => 'sort64?',
         'versionControlSystem' => 'text32',
         'uuid' => 'text64?',
         'pushPolicy' => 'policy',
         'credentialPHID' => 'phid?',
         'almanacServicePHID' => 'phid?',
+        'localPath' => 'text128?',
       ),
       self::CONFIG_KEY_SCHEMA => array(
         'callsign' => array(
           'columns' => array('callsign'),
           'unique' => true,
         ),
         'key_name' => array(
           'columns' => array('name(128)'),
         ),
         'key_vcs' => array(
           'columns' => array('versionControlSystem'),
         ),
         'key_slug' => array(
           'columns' => array('repositorySlug'),
           'unique' => true,
         ),
+        'key_local' => array(
+          'columns' => array('localPath'),
+          'unique' => true,
+        ),
       ),
     ) + parent::getConfiguration();
   }
 
   public function generatePHID() {
     return PhabricatorPHID::generateNewPHID(
       PhabricatorRepositoryRepositoryPHIDType::TYPECONST);
   }
 
   public static function getStatusMap() {
     return array(
       self::STATUS_ACTIVE => array(
         'name' => pht('Active'),
         'isTracked' => 1,
       ),
       self::STATUS_INACTIVE => array(
         'name' => pht('Inactive'),
         'isTracked' => 0,
       ),
     );
   }
 
   public static function getStatusNameMap() {
     return ipull(self::getStatusMap(), 'name');
   }
 
   public function getStatus() {
     if ($this->isTracked()) {
       return self::STATUS_ACTIVE;
     } else {
       return self::STATUS_INACTIVE;
     }
   }
 
   public function toDictionary() {
     return array(
       'id'          => $this->getID(),
       'name'        => $this->getName(),
       'phid'        => $this->getPHID(),
       'callsign'    => $this->getCallsign(),
       'monogram'    => $this->getMonogram(),
       'vcs'         => $this->getVersionControlSystem(),
       'uri'         => PhabricatorEnv::getProductionURI($this->getURI()),
       'remoteURI'   => (string)$this->getRemoteURI(),
       'description' => $this->getDetail('description'),
       'isActive'    => $this->isTracked(),
       'isHosted'    => $this->isHosted(),
       'isImporting' => $this->isImporting(),
       'encoding'    => $this->getDefaultTextEncoding(),
       'staging' => array(
         'supported' => $this->supportsStaging(),
         'prefix' => 'phabricator',
         'uri' => $this->getStagingURI(),
       ),
     );
   }
 
   public function getDefaultTextEncoding() {
     return $this->getDetail('encoding', 'UTF-8');
   }
 
   public function getMonogram() {
     $callsign = $this->getCallsign();
     if (strlen($callsign)) {
       return "r{$callsign}";
     }
 
     $id = $this->getID();
     return "R{$id}";
   }
 
   public function getDisplayName() {
     $slug = $this->getRepositorySlug();
     if (strlen($slug)) {
       return $slug;
     }
 
     return $this->getMonogram();
   }
 
   public function getAllMonograms() {
     $monograms = array();
 
     $monograms[] = 'R'.$this->getID();
 
     $callsign = $this->getCallsign();
     if (strlen($callsign)) {
       $monograms[] = 'r'.$callsign;
     }
 
     return $monograms;
   }
 
+  public function setLocalPath($path) {
+    // Convert any extra slashes ("//") in the path to a single slash ("/").
+    $path = preg_replace('(//+)', '/', $path);
+
+    return parent::setLocalPath($path);
+  }
+
   public function getDetail($key, $default = null) {
     return idx($this->details, $key, $default);
   }
 
   public function getHumanReadableDetail($key, $default = null) {
     $value = $this->getDetail($key, $default);
 
     switch ($key) {
       case 'branch-filter':
       case 'close-commits-filter':
         $value = array_keys($value);
         $value = implode(', ', $value);
         break;
     }
 
     return $value;
   }
 
   public function setDetail($key, $value) {
     $this->details[$key] = $value;
     return $this;
   }
 
   public function attachCommitCount($count) {
     $this->commitCount = $count;
     return $this;
   }
 
   public function getCommitCount() {
     return $this->assertAttached($this->commitCount);
   }
 
   public function attachMostRecentCommit(
     PhabricatorRepositoryCommit $commit = null) {
     $this->mostRecentCommit = $commit;
     return $this;
   }
 
   public function getMostRecentCommit() {
     return $this->assertAttached($this->mostRecentCommit);
   }
 
   public function getDiffusionBrowseURIForPath(
     PhabricatorUser $user,
     $path,
     $line = null,
     $branch = null) {
 
     $drequest = DiffusionRequest::newFromDictionary(
       array(
         'user' => $user,
         'repository' => $this,
         'path' => $path,
         'branch' => $branch,
       ));
 
     return $drequest->generateURI(
       array(
         'action' => 'browse',
         'line'   => $line,
       ));
   }
 
-  public function getLocalPath() {
-    return $this->getDetail('local-path');
-  }
-
   public function getSubversionBaseURI($commit = null) {
     $subpath = $this->getDetail('svn-subpath');
     if (!strlen($subpath)) {
       $subpath = null;
     }
     return $this->getSubversionPathURI($subpath, $commit);
   }
 
   public function getSubversionPathURI($path = null, $commit = null) {
     $vcs = $this->getVersionControlSystem();
     if ($vcs != PhabricatorRepositoryType::REPOSITORY_TYPE_SVN) {
       throw new Exception(pht('Not a subversion repository!'));
     }
 
     if ($this->isHosted()) {
       $uri = 'file://'.$this->getLocalPath();
     } else {
       $uri = $this->getDetail('remote-uri');
     }
 
     $uri = rtrim($uri, '/');
 
     if (strlen($path)) {
       $path = rawurlencode($path);
       $path = str_replace('%2F', '/', $path);
       $uri = $uri.'/'.ltrim($path, '/');
     }
 
     if ($path !== null || $commit !== null) {
       $uri .= '@';
     }
 
     if ($commit !== null) {
       $uri .= $commit;
     }
 
     return $uri;
   }
 
   public function attachProjectPHIDs(array $project_phids) {
     $this->projectPHIDs = $project_phids;
     return $this;
   }
 
   public function getProjectPHIDs() {
     return $this->assertAttached($this->projectPHIDs);
   }
 
 
   /**
    * Get the name of the directory this repository should clone or checkout
    * into. For example, if the repository name is "Example Repository", a
    * reasonable name might be "example-repository". This is used to help users
    * get reasonable results when cloning repositories, since they generally do
    * not want to clone into directories called "X/" or "Example Repository/".
    *
    * @return string
    */
   public function getCloneName() {
     $name = $this->getRepositorySlug();
 
     // Make some reasonable effort to produce reasonable default directory
     // names from repository names.
     if (!strlen($name)) {
       $name = $this->getName();
       $name = phutil_utf8_strtolower($name);
-      $name = preg_replace('@[/ -:]+@', '-', $name);
+      $name = preg_replace('@[/ -:<>]+@', '-', $name);
       $name = trim($name, '-');
       if (!strlen($name)) {
         $name = $this->getCallsign();
       }
     }
 
     return $name;
   }
 
   public static function isValidRepositorySlug($slug) {
     try {
       self::assertValidRepositorySlug($slug);
       return true;
     } catch (Exception $ex) {
       return false;
     }
   }
 
   public static function assertValidRepositorySlug($slug) {
     if (!strlen($slug)) {
       throw new Exception(
         pht(
           'The empty string is not a valid repository short name. '.
           'Repository short names must be at least one character long.'));
     }
 
     if (strlen($slug) > 64) {
       throw new Exception(
         pht(
           'The name "%s" is not a valid repository short name. Repository '.
           'short names must not be longer than 64 characters.',
           $slug));
     }
 
     if (preg_match('/[^a-zA-Z0-9._-]/', $slug)) {
       throw new Exception(
         pht(
           'The name "%s" is not a valid repository short name. Repository '.
           'short names may only contain letters, numbers, periods, hyphens '.
           'and underscores.',
           $slug));
     }
 
     if (!preg_match('/^[a-zA-Z0-9]/', $slug)) {
       throw new Exception(
         pht(
           'The name "%s" is not a valid repository short name. Repository '.
           'short names must begin with a letter or number.',
           $slug));
     }
 
     if (!preg_match('/[a-zA-Z0-9]\z/', $slug)) {
       throw new Exception(
         pht(
           'The name "%s" is not a valid repository short name. Repository '.
           'short names must end with a letter or number.',
           $slug));
     }
 
     if (preg_match('/__|--|\\.\\./', $slug)) {
       throw new Exception(
         pht(
           'The name "%s" is not a valid repository short name. Repository '.
           'short names must not contain multiple consecutive underscores, '.
           'hyphens, or periods.',
           $slug));
     }
 
     if (preg_match('/^[A-Z]+\z/', $slug)) {
       throw new Exception(
         pht(
           'The name "%s" is not a valid repository short name. Repository '.
           'short names may not contain only uppercase letters.',
           $slug));
     }
 
     if (preg_match('/^\d+\z/', $slug)) {
       throw new Exception(
         pht(
           'The name "%s" is not a valid repository short name. Repository '.
           'short names may not contain only numbers.',
           $slug));
     }
   }
 
   public static function assertValidCallsign($callsign) {
     if (!strlen($callsign)) {
       throw new Exception(
         pht(
           'A repository callsign must be at least one character long.'));
     }
 
     if (strlen($callsign) > 32) {
       throw new Exception(
         pht(
           'The callsign "%s" is not a valid repository callsign. Callsigns '.
           'must be no more than 32 bytes long.',
           $callsign));
     }
 
     if (!preg_match('/^[A-Z]+\z/', $callsign)) {
       throw new Exception(
         pht(
           'The callsign "%s" is not a valid repository callsign. Callsigns '.
           'may only contain UPPERCASE letters.',
           $callsign));
     }
   }
 
 
 /* -(  Remote Command Execution  )------------------------------------------- */
 
 
   public function execRemoteCommand($pattern /* , $arg, ... */) {
     $args = func_get_args();
     return $this->newRemoteCommandFuture($args)->resolve();
   }
 
   public function execxRemoteCommand($pattern /* , $arg, ... */) {
     $args = func_get_args();
     return $this->newRemoteCommandFuture($args)->resolvex();
   }
 
   public function getRemoteCommandFuture($pattern /* , $arg, ... */) {
     $args = func_get_args();
     return $this->newRemoteCommandFuture($args);
   }
 
   public function passthruRemoteCommand($pattern /* , $arg, ... */) {
     $args = func_get_args();
     return $this->newRemoteCommandPassthru($args)->execute();
   }
 
   private function newRemoteCommandFuture(array $argv) {
     return $this->newRemoteCommandEngine($argv)
       ->newFuture();
   }
 
   private function newRemoteCommandPassthru(array $argv) {
     return $this->newRemoteCommandEngine($argv)
       ->setPassthru(true)
       ->newFuture();
   }
 
   private function newRemoteCommandEngine(array $argv) {
     return DiffusionCommandEngine::newCommandEngine($this)
       ->setArgv($argv)
       ->setCredentialPHID($this->getCredentialPHID())
       ->setProtocol($this->getRemoteProtocol());
   }
 
 /* -(  Local Command Execution  )-------------------------------------------- */
 
 
   public function execLocalCommand($pattern /* , $arg, ... */) {
     $args = func_get_args();
     return $this->newLocalCommandFuture($args)->resolve();
   }
 
   public function execxLocalCommand($pattern /* , $arg, ... */) {
     $args = func_get_args();
     return $this->newLocalCommandFuture($args)->resolvex();
   }
 
   public function getLocalCommandFuture($pattern /* , $arg, ... */) {
     $args = func_get_args();
     return $this->newLocalCommandFuture($args);
   }
 
   public function passthruLocalCommand($pattern /* , $arg, ... */) {
     $args = func_get_args();
     return $this->newLocalCommandPassthru($args)->execute();
   }
 
   private function newLocalCommandFuture(array $argv) {
     $this->assertLocalExists();
 
     $future = DiffusionCommandEngine::newCommandEngine($this)
       ->setArgv($argv)
       ->newFuture();
 
     if ($this->usesLocalWorkingCopy()) {
       $future->setCWD($this->getLocalPath());
     }
 
     return $future;
   }
 
   private function newLocalCommandPassthru(array $argv) {
     $this->assertLocalExists();
 
     $future = DiffusionCommandEngine::newCommandEngine($this)
       ->setArgv($argv)
       ->setPassthru(true)
       ->newFuture();
 
     if ($this->usesLocalWorkingCopy()) {
       $future->setCWD($this->getLocalPath());
     }
 
     return $future;
   }
 
   public function getURI() {
     $callsign = $this->getCallsign();
     if (strlen($callsign)) {
       return "/diffusion/{$callsign}/";
     }
 
     $id = $this->getID();
     return "/diffusion/{$id}/";
   }
 
   public function getPathURI($path) {
     return $this->getURI().$path;
   }
 
   public function getCommitURI($identifier) {
     $callsign = $this->getCallsign();
     if (strlen($callsign)) {
       return "/r{$callsign}{$identifier}";
     }
 
     $id = $this->getID();
     return "/R{$id}:{$identifier}";
   }
 
   public static function parseRepositoryServicePath($request_path) {
     // NOTE: In Mercurial over SSH, the path will begin without a leading "/",
     // so we're matching it optionally.
 
     $patterns = array(
       '(^'.
         '(?P<base>/?diffusion/(?P<identifier>[A-Z]+|[0-9]\d*))'.
         '(?P<path>(?:/.*)?)'.
       '\z)',
     );
 
     $identifier = null;
     foreach ($patterns as $pattern) {
       $matches = null;
       if (!preg_match($pattern, $request_path, $matches)) {
         continue;
       }
 
       $identifier = $matches['identifier'];
       $base = $matches['base'];
       $path = $matches['path'];
       break;
     }
 
     if ($identifier === null) {
       return null;
     }
 
     return array(
       'identifier' => $identifier,
       'base' => $base,
       'path' => $path,
     );
   }
 
   public function getCanonicalPath($request_path) {
     $standard_pattern =
       '(^'.
         '(?P<prefix>/diffusion/)'.
         '(?P<identifier>[^/]+)'.
         '(?P<suffix>(?:/.*)?)'.
       '\z)';
 
     $matches = null;
     if (preg_match($standard_pattern, $request_path, $matches)) {
       $prefix = $matches['prefix'];
 
       $callsign = $this->getCallsign();
       if ($callsign) {
         $identifier = $callsign;
       } else {
         $identifier = $this->getID();
       }
 
       $suffix = $matches['suffix'];
       if (!strlen($suffix)) {
         $suffix = '/';
       }
 
       return $prefix.$identifier.$suffix;
     }
 
     $commit_pattern =
       '(^'.
         '(?P<prefix>/)'.
         '(?P<monogram>'.
           '(?:'.
             'r(?P<repositoryCallsign>[A-Z]+)'.
             '|'.
             'R(?P<repositoryID>[1-9]\d*):'.
           ')'.
           '(?P<commit>[a-f0-9]+)'.
         ')'.
       '\z)';
 
     $matches = null;
     if (preg_match($commit_pattern, $request_path, $matches)) {
       $commit = $matches['commit'];
       return $this->getCommitURI($commit);
     }
 
     return null;
   }
 
   public function generateURI(array $params) {
     $req_branch = false;
     $req_commit = false;
 
     $action = idx($params, 'action');
     switch ($action) {
       case 'history':
       case 'browse':
       case 'change':
       case 'lastmodified':
       case 'tags':
       case 'branches':
       case 'lint':
       case 'pathtree':
       case 'refs':
         break;
       case 'branch':
         // NOTE: This does not actually require a branch, and won't have one
         // in Subversion. Possibly this should be more clear.
         break;
       case 'commit':
       case 'rendering-ref':
         $req_commit = true;
         break;
       default:
         throw new Exception(
           pht(
             'Action "%s" is not a valid repository URI action.',
             $action));
     }
 
     $path     = idx($params, 'path');
     $branch   = idx($params, 'branch');
     $commit   = idx($params, 'commit');
     $line     = idx($params, 'line');
 
     if ($req_commit && !strlen($commit)) {
       throw new Exception(
         pht(
           'Diffusion URI action "%s" requires commit!',
           $action));
     }
 
     if ($req_branch && !strlen($branch)) {
       throw new Exception(
         pht(
           'Diffusion URI action "%s" requires branch!',
           $action));
     }
 
     if ($action === 'commit') {
       return $this->getCommitURI($commit);
     }
 
 
     $identifier = $this->getID();
 
     $callsign = $this->getCallsign();
     if ($callsign !== null) {
       $identifier = $callsign;
     }
 
     if (strlen($identifier)) {
       $identifier = phutil_escape_uri_path_component($identifier);
     }
 
     if (strlen($path)) {
       $path = ltrim($path, '/');
       $path = str_replace(array(';', '$'), array(';;', '$$'), $path);
       $path = phutil_escape_uri($path);
     }
 
     if (strlen($branch)) {
       $branch = phutil_escape_uri_path_component($branch);
       $path = "{$branch}/{$path}";
     }
 
     if (strlen($commit)) {
       $commit = str_replace('$', '$$', $commit);
       $commit = ';'.phutil_escape_uri($commit);
     }
 
     if (strlen($line)) {
       $line = '$'.phutil_escape_uri($line);
     }
 
     switch ($action) {
       case 'change':
       case 'history':
       case 'browse':
       case 'lastmodified':
       case 'tags':
       case 'branches':
       case 'lint':
       case 'pathtree':
       case 'refs':
         $uri = "/diffusion/{$identifier}/{$action}/{$path}{$commit}{$line}";
         break;
       case 'branch':
         if (strlen($path)) {
           $uri = "/diffusion/{$identifier}/repository/{$path}";
         } else {
           $uri = "/diffusion/{$identifier}/";
         }
         break;
       case 'external':
         $commit = ltrim($commit, ';');
         $uri = "/diffusion/external/{$commit}/";
         break;
       case 'rendering-ref':
         // This isn't a real URI per se, it's passed as a query parameter to
         // the ajax changeset stuff but then we parse it back out as though
         // it came from a URI.
         $uri = rawurldecode("{$path}{$commit}");
         break;
     }
 
     if ($action == 'rendering-ref') {
       return $uri;
     }
 
     $uri = new PhutilURI($uri);
 
     if (isset($params['lint'])) {
       $params['params'] = idx($params, 'params', array()) + array(
         'lint' => $params['lint'],
       );
     }
 
     if (idx($params, 'params')) {
       $uri->setQueryParams($params['params']);
     }
 
     return $uri;
   }
 
   public function updateURIIndex() {
-    $uris = array(
-      (string)$this->getCloneURIObject(),
-    );
+    $indexes = array();
 
-    foreach ($uris as $key => $uri) {
-      $uris[$key] = $this->getNormalizedURI($uri)
-        ->getNormalizedPath();
+    $uris = $this->getURIs();
+    foreach ($uris as $uri) {
+      if ($uri->getIsDisabled()) {
+        continue;
+      }
+
+      $indexes[] = $uri->getNormalizedURI();
     }
 
     PhabricatorRepositoryURIIndex::updateRepositoryURIs(
       $this->getPHID(),
-      $uris);
+      $indexes);
 
     return $this;
   }
 
-  private function getNormalizedURI($uri) {
-    switch ($this->getVersionControlSystem()) {
-      case PhabricatorRepositoryType::REPOSITORY_TYPE_GIT:
-        return new PhabricatorRepositoryURINormalizer(
-          PhabricatorRepositoryURINormalizer::TYPE_GIT,
-          $uri);
-      case PhabricatorRepositoryType::REPOSITORY_TYPE_SVN:
-        return new PhabricatorRepositoryURINormalizer(
-          PhabricatorRepositoryURINormalizer::TYPE_SVN,
-          $uri);
-      case PhabricatorRepositoryType::REPOSITORY_TYPE_MERCURIAL:
-        return new PhabricatorRepositoryURINormalizer(
-          PhabricatorRepositoryURINormalizer::TYPE_MERCURIAL,
-          $uri);
-      default:
-        throw new Exception(pht('Unrecognized version control system.'));
-    }
-  }
-
   public function isTracked() {
     $status = $this->getDetail('tracking-enabled');
     $map = self::getStatusMap();
     $spec = idx($map, $status);
 
     if (!$spec) {
       if ($status) {
         $status = self::STATUS_ACTIVE;
       } else {
         $status = self::STATUS_INACTIVE;
       }
       $spec = idx($map, $status);
     }
 
     return (bool)idx($spec, 'isTracked', false);
   }
 
   public function getDefaultBranch() {
     $default = $this->getDetail('default-branch');
     if (strlen($default)) {
       return $default;
     }
 
     $default_branches = array(
       PhabricatorRepositoryType::REPOSITORY_TYPE_GIT        => 'master',
       PhabricatorRepositoryType::REPOSITORY_TYPE_MERCURIAL  => 'default',
     );
 
     return idx($default_branches, $this->getVersionControlSystem());
   }
 
   public function getDefaultArcanistBranch() {
     return coalesce($this->getDefaultBranch(), 'svn');
   }
 
   private function isBranchInFilter($branch, $filter_key) {
     $vcs = $this->getVersionControlSystem();
 
     $is_git = ($vcs == PhabricatorRepositoryType::REPOSITORY_TYPE_GIT);
 
     $use_filter = ($is_git);
     if (!$use_filter) {
       // If this VCS doesn't use filters, pass everything through.
       return true;
     }
 
 
     $filter = $this->getDetail($filter_key, array());
 
     // If there's no filter set, let everything through.
     if (!$filter) {
       return true;
     }
 
     // If this branch isn't literally named `regexp(...)`, and it's in the
     // filter list, let it through.
     if (isset($filter[$branch])) {
       if (self::extractBranchRegexp($branch) === null) {
         return true;
       }
     }
 
     // If the branch matches a regexp, let it through.
     foreach ($filter as $pattern => $ignored) {
       $regexp = self::extractBranchRegexp($pattern);
       if ($regexp !== null) {
         if (preg_match($regexp, $branch)) {
           return true;
         }
       }
     }
 
     // Nothing matched, so filter this branch out.
     return false;
   }
 
   public static function extractBranchRegexp($pattern) {
     $matches = null;
     if (preg_match('/^regexp\\((.*)\\)\z/', $pattern, $matches)) {
       return $matches[1];
     }
     return null;
   }
 
   public function shouldTrackBranch($branch) {
     return $this->isBranchInFilter($branch, 'branch-filter');
   }
 
   public function formatCommitName($commit_identifier, $local = false) {
     $vcs = $this->getVersionControlSystem();
 
     $type_git = PhabricatorRepositoryType::REPOSITORY_TYPE_GIT;
     $type_hg = PhabricatorRepositoryType::REPOSITORY_TYPE_MERCURIAL;
 
     $is_git = ($vcs == $type_git);
     $is_hg = ($vcs == $type_hg);
     if ($is_git || $is_hg) {
       $name = substr($commit_identifier, 0, 12);
       $need_scope = false;
     } else {
       $name = $commit_identifier;
       $need_scope = true;
     }
 
     if (!$local) {
       $need_scope = true;
     }
 
     if ($need_scope) {
       $callsign = $this->getCallsign();
       if ($callsign) {
         $scope = "r{$callsign}";
       } else {
         $id = $this->getID();
         $scope = "R{$id}:";
       }
       $name = $scope.$name;
     }
 
     return $name;
   }
 
   public function isImporting() {
     return (bool)$this->getDetail('importing', false);
   }
 
+  public function isNewlyInitialized() {
+    return (bool)$this->getDetail('newly-initialized', false);
+  }
+
   public function loadImportProgress() {
     $progress = queryfx_all(
       $this->establishConnection('r'),
       'SELECT importStatus, count(*) N FROM %T WHERE repositoryID = %d
         GROUP BY importStatus',
       id(new PhabricatorRepositoryCommit())->getTableName(),
       $this->getID());
 
     $done = 0;
     $total = 0;
     foreach ($progress as $row) {
       $total += $row['N'] * 4;
       $status = $row['importStatus'];
       if ($status & PhabricatorRepositoryCommit::IMPORTED_MESSAGE) {
         $done += $row['N'];
       }
       if ($status & PhabricatorRepositoryCommit::IMPORTED_CHANGE) {
         $done += $row['N'];
       }
       if ($status & PhabricatorRepositoryCommit::IMPORTED_OWNERS) {
         $done += $row['N'];
       }
       if ($status & PhabricatorRepositoryCommit::IMPORTED_HERALD) {
         $done += $row['N'];
       }
     }
 
     if ($total) {
       $ratio = ($done / $total);
     } else {
       $ratio = 0;
     }
 
     // Cap this at "99.99%", because it's confusing to users when the actual
     // fraction is "99.996%" and it rounds up to "100.00%".
     if ($ratio > 0.9999) {
       $ratio = 0.9999;
     }
 
     return $ratio;
   }
 
   /**
    * Should this repository publish feed, notifications, audits, and email?
    *
    * We do not publish information about repositories during initial import,
    * or if the repository has been set not to publish.
    */
   public function shouldPublish() {
     if ($this->isImporting()) {
       return false;
     }
 
     if ($this->getDetail('herald-disabled')) {
       return false;
     }
 
     return true;
   }
 
 
 /* -(  Autoclose  )---------------------------------------------------------- */
 
 
   /**
    * Determine if autoclose is active for a branch.
    *
    * For more details about why, use @{method:shouldSkipAutocloseBranch}.
    *
    * @param string Branch name to check.
    * @return bool True if autoclose is active for the branch.
    * @task autoclose
    */
   public function shouldAutocloseBranch($branch) {
     return ($this->shouldSkipAutocloseBranch($branch) === null);
   }
 
   /**
    * Determine if autoclose is active for a commit.
    *
    * For more details about why, use @{method:shouldSkipAutocloseCommit}.
    *
    * @param PhabricatorRepositoryCommit Commit to check.
    * @return bool True if autoclose is active for the commit.
    * @task autoclose
    */
   public function shouldAutocloseCommit(PhabricatorRepositoryCommit $commit) {
     return ($this->shouldSkipAutocloseCommit($commit) === null);
   }
 
 
   /**
    * Determine why autoclose should be skipped for a branch.
    *
    * This method gives a detailed reason why autoclose will be skipped. To
    * perform a simple test, use @{method:shouldAutocloseBranch}.
    *
    * @param string Branch name to check.
    * @return const|null Constant identifying reason to skip this branch, or null
    *   if autoclose is active.
    * @task autoclose
    */
   public function shouldSkipAutocloseBranch($branch) {
     $all_reason = $this->shouldSkipAllAutoclose();
     if ($all_reason) {
       return $all_reason;
     }
 
     if (!$this->shouldTrackBranch($branch)) {
       return self::BECAUSE_BRANCH_UNTRACKED;
     }
 
     if (!$this->isBranchInFilter($branch, 'close-commits-filter')) {
       return self::BECAUSE_BRANCH_NOT_AUTOCLOSE;
     }
 
     return null;
   }
 
 
   /**
    * Determine why autoclose should be skipped for a commit.
    *
    * This method gives a detailed reason why autoclose will be skipped. To
    * perform a simple test, use @{method:shouldAutocloseCommit}.
    *
    * @param PhabricatorRepositoryCommit Commit to check.
    * @return const|null Constant identifying reason to skip this commit, or null
    *   if autoclose is active.
    * @task autoclose
    */
   public function shouldSkipAutocloseCommit(
     PhabricatorRepositoryCommit $commit) {
 
     $all_reason = $this->shouldSkipAllAutoclose();
     if ($all_reason) {
       return $all_reason;
     }
 
     switch ($this->getVersionControlSystem()) {
       case PhabricatorRepositoryType::REPOSITORY_TYPE_SVN:
       case PhabricatorRepositoryType::REPOSITORY_TYPE_MERCURIAL:
         return null;
       case PhabricatorRepositoryType::REPOSITORY_TYPE_GIT:
         break;
       default:
         throw new Exception(pht('Unrecognized version control system.'));
     }
 
     $closeable_flag = PhabricatorRepositoryCommit::IMPORTED_CLOSEABLE;
     if (!$commit->isPartiallyImported($closeable_flag)) {
       return self::BECAUSE_NOT_ON_AUTOCLOSE_BRANCH;
     }
 
     return null;
   }
 
 
   /**
    * Determine why all autoclose operations should be skipped for this
    * repository.
    *
    * @return const|null Constant identifying reason to skip all autoclose
    *   operations, or null if autoclose operations are not blocked at the
    *   repository level.
    * @task autoclose
    */
   private function shouldSkipAllAutoclose() {
     if ($this->isImporting()) {
       return self::BECAUSE_REPOSITORY_IMPORTING;
     }
 
     if ($this->getDetail('disable-autoclose', false)) {
       return self::BECAUSE_AUTOCLOSE_DISABLED;
     }
 
     return null;
   }
 
 
 /* -(  Repository URI Management  )------------------------------------------ */
 
 
   /**
    * Get the remote URI for this repository.
    *
    * @return string
    * @task uri
    */
   public function getRemoteURI() {
     return (string)$this->getRemoteURIObject();
   }
 
 
   /**
    * Get the remote URI for this repository, including credentials if they're
    * used by this repository.
    *
    * @return PhutilOpaqueEnvelope URI, possibly including credentials.
    * @task uri
    */
   public function getRemoteURIEnvelope() {
     $uri = $this->getRemoteURIObject();
 
     $remote_protocol = $this->getRemoteProtocol();
     if ($remote_protocol == 'http' || $remote_protocol == 'https') {
       // For SVN, we use `--username` and `--password` flags separately, so
       // don't add any credentials here.
       if (!$this->isSVN()) {
         $credential_phid = $this->getCredentialPHID();
         if ($credential_phid) {
           $key = PassphrasePasswordKey::loadFromPHID(
             $credential_phid,
             PhabricatorUser::getOmnipotentUser());
 
           $uri->setUser($key->getUsernameEnvelope()->openEnvelope());
           $uri->setPass($key->getPasswordEnvelope()->openEnvelope());
         }
       }
     }
 
     return new PhutilOpaqueEnvelope((string)$uri);
   }
 
 
   /**
    * Get the clone (or checkout) URI for this repository, without authentication
    * information.
    *
    * @return string Repository URI.
    * @task uri
    */
   public function getPublicCloneURI() {
-    $uri = $this->getCloneURIObject();
-
-    // Make sure we don't leak anything if this repo is using HTTP Basic Auth
-    // with the credentials in the URI or something zany like that.
-
-    // If repository is not accessed over SSH we remove both username and
-    // password.
-    if (!$this->isHosted()) {
-      if (!$this->shouldUseSSH()) {
-        $uri->setUser(null);
-
-        // This might be a Git URI or a normal URI. If it's Git, there's no
-        // password support.
-        if ($uri instanceof PhutilURI) {
-          $uri->setPass(null);
-        }
-      }
-    }
-
-    return (string)$uri;
+    return (string)$this->getCloneURIObject();
   }
 
 
   /**
    * Get the protocol for the repository's remote.
    *
    * @return string Protocol, like "ssh" or "git".
    * @task uri
    */
   public function getRemoteProtocol() {
     $uri = $this->getRemoteURIObject();
 
     if ($uri instanceof PhutilGitURI) {
       return 'ssh';
     } else {
       return $uri->getProtocol();
     }
   }
 
 
   /**
    * Get a parsed object representation of the repository's remote URI. This
    * may be a normal URI (returned as a @{class@libphutil:PhutilURI}) or a git
    * URI (returned as a @{class@libphutil:PhutilGitURI}).
    *
    * @return wild A @{class@libphutil:PhutilURI} or
    *              @{class@libphutil:PhutilGitURI}.
    * @task uri
    */
   public function getRemoteURIObject() {
     $raw_uri = $this->getDetail('remote-uri');
     if (!$raw_uri) {
       return new PhutilURI('');
     }
 
     if (!strncmp($raw_uri, '/', 1)) {
       return new PhutilURI('file://'.$raw_uri);
     }
 
     $uri = new PhutilURI($raw_uri);
     if ($uri->getProtocol()) {
       return $uri;
     }
 
     $uri = new PhutilGitURI($raw_uri);
     if ($uri->getDomain()) {
       return $uri;
     }
 
     throw new Exception(pht("Remote URI '%s' could not be parsed!", $raw_uri));
   }
 
 
   /**
    * Get the "best" clone/checkout URI for this repository, on any protocol.
    */
   public function getCloneURIObject() {
     if (!$this->isHosted()) {
       if ($this->isSVN()) {
         // Make sure we pick up the "Import Only" path for Subversion, so
         // the user clones the repository starting at the correct path, not
         // from the root.
         $base_uri = $this->getSubversionBaseURI();
         $base_uri = new PhutilURI($base_uri);
         $path = $base_uri->getPath();
         if (!$path) {
           $path = '/';
         }
 
         // If the trailing "@" is not required to escape the URI, strip it for
         // readability.
         if (!preg_match('/@.*@/', $path)) {
           $path = rtrim($path, '@');
         }
 
         $base_uri->setPath($path);
         return $base_uri;
       } else {
         return $this->getRemoteURIObject();
       }
     }
 
-    // Choose the best URI: pick a read/write URI over a URI which is not
-    // read/write, and SSH over HTTP.
-
-    $serve_ssh = $this->getServeOverSSH();
-    $serve_http = $this->getServeOverHTTP();
-
-    if ($serve_ssh === self::SERVE_READWRITE) {
-      return $this->getSSHCloneURIObject();
-    } else if ($serve_http === self::SERVE_READWRITE) {
-      return $this->getHTTPCloneURIObject();
-    } else if ($serve_ssh !== self::SERVE_OFF) {
-      return $this->getSSHCloneURIObject();
-    } else if ($serve_http !== self::SERVE_OFF) {
-      return $this->getHTTPCloneURIObject();
-    } else {
-      return null;
-    }
-  }
-
-
-  /**
-   * Get the repository's SSH clone/checkout URI, if one exists.
-   */
-  public function getSSHCloneURIObject() {
-    if (!$this->isHosted()) {
-      if ($this->shouldUseSSH()) {
-        return $this->getRemoteURIObject();
-      } else {
-        return null;
-      }
-    }
-
-    $serve_ssh = $this->getServeOverSSH();
-    if ($serve_ssh === self::SERVE_OFF) {
-      return null;
-    }
-
-    $uri = new PhutilURI(PhabricatorEnv::getProductionURI($this->getURI()));
-
-    if ($this->isSVN()) {
-      $uri->setProtocol('svn+ssh');
-    } else {
-      $uri->setProtocol('ssh');
-    }
-
-    if ($this->isGit()) {
-      $uri->setPath($uri->getPath().$this->getCloneName().'.git');
-    } else if ($this->isHg()) {
-      $uri->setPath($uri->getPath().$this->getCloneName().'/');
-    }
-
-    $ssh_user = AlmanacKeys::getClusterSSHUser();
-    if (strlen($ssh_user)) {
-      $uri->setUser($ssh_user);
-    }
-
-    $ssh_host = PhabricatorEnv::getEnvConfig('diffusion.ssh-host');
-    if (strlen($ssh_host)) {
-      $uri->setDomain($ssh_host);
-    }
-
-    $uri->setPort(PhabricatorEnv::getEnvConfig('diffusion.ssh-port'));
-
-    return $uri;
-  }
-
-
-  /**
-   * Get the repository's HTTP clone/checkout URI, if one exists.
-   */
-  public function getHTTPCloneURIObject() {
-    if (!$this->isHosted()) {
-      if ($this->shouldUseHTTP()) {
-        return $this->getRemoteURIObject();
-      } else {
-        return null;
-      }
-    }
+    // TODO: This should be cleaned up to deal with all the new URI handling.
+    $another_copy = id(new PhabricatorRepositoryQuery())
+      ->setViewer(PhabricatorUser::getOmnipotentUser())
+      ->withPHIDs(array($this->getPHID()))
+      ->needURIs(true)
+      ->executeOne();
 
-    $serve_http = $this->getServeOverHTTP();
-    if ($serve_http === self::SERVE_OFF) {
+    $clone_uris = $another_copy->getCloneURIs();
+    if (!$clone_uris) {
       return null;
     }
 
-    return $this->getRawHTTPCloneURIObject();
+    return head($clone_uris)->getEffectiveURI();
   }
 
   private function getRawHTTPCloneURIObject() {
     $uri = PhabricatorEnv::getProductionURI($this->getURI());
     $uri = new PhutilURI($uri);
 
     if ($this->isGit()) {
       $uri->setPath($uri->getPath().$this->getCloneName().'.git');
     } else if ($this->isHg()) {
       $uri->setPath($uri->getPath().$this->getCloneName().'/');
     }
 
     return $uri;
   }
 
 
   /**
    * Determine if we should connect to the remote using SSH flags and
    * credentials.
    *
    * @return bool True to use the SSH protocol.
    * @task uri
    */
   private function shouldUseSSH() {
     if ($this->isHosted()) {
       return false;
     }
 
     $protocol = $this->getRemoteProtocol();
     if ($this->isSSHProtocol($protocol)) {
       return true;
     }
 
     return false;
   }
 
 
   /**
    * Determine if we should connect to the remote using HTTP flags and
    * credentials.
    *
    * @return bool True to use the HTTP protocol.
    * @task uri
    */
   private function shouldUseHTTP() {
     if ($this->isHosted()) {
       return false;
     }
 
     $protocol = $this->getRemoteProtocol();
     return ($protocol == 'http' || $protocol == 'https');
   }
 
 
   /**
    * Determine if we should connect to the remote using SVN flags and
    * credentials.
    *
    * @return bool True to use the SVN protocol.
    * @task uri
    */
   private function shouldUseSVNProtocol() {
     if ($this->isHosted()) {
       return false;
     }
 
     $protocol = $this->getRemoteProtocol();
     return ($protocol == 'svn');
   }
 
 
   /**
    * Determine if a protocol is SSH or SSH-like.
    *
    * @param string A protocol string, like "http" or "ssh".
    * @return bool True if the protocol is SSH-like.
    * @task uri
    */
   private function isSSHProtocol($protocol) {
     return ($protocol == 'ssh' || $protocol == 'svn+ssh');
   }
 
   public function delete() {
     $this->openTransaction();
 
       $paths = id(new PhabricatorOwnersPath())
         ->loadAllWhere('repositoryPHID = %s', $this->getPHID());
       foreach ($paths as $path) {
         $path->delete();
       }
 
       queryfx(
         $this->establishConnection('w'),
         'DELETE FROM %T WHERE repositoryPHID = %s',
         id(new PhabricatorRepositorySymbol())->getTableName(),
         $this->getPHID());
 
       $commits = id(new PhabricatorRepositoryCommit())
         ->loadAllWhere('repositoryID = %d', $this->getID());
       foreach ($commits as $commit) {
         // note PhabricatorRepositoryAuditRequests and
         // PhabricatorRepositoryCommitData are deleted here too.
         $commit->delete();
       }
 
-      $mirrors = id(new PhabricatorRepositoryMirror())
+      $uris = id(new PhabricatorRepositoryURI())
         ->loadAllWhere('repositoryPHID = %s', $this->getPHID());
-      foreach ($mirrors as $mirror) {
-        $mirror->delete();
+      foreach ($uris as $uri) {
+        $uri->delete();
       }
 
       $ref_cursors = id(new PhabricatorRepositoryRefCursor())
         ->loadAllWhere('repositoryPHID = %s', $this->getPHID());
       foreach ($ref_cursors as $cursor) {
         $cursor->delete();
       }
 
       $conn_w = $this->establishConnection('w');
 
       queryfx(
         $conn_w,
         'DELETE FROM %T WHERE repositoryID = %d',
         self::TABLE_FILESYSTEM,
         $this->getID());
 
       queryfx(
         $conn_w,
         'DELETE FROM %T WHERE repositoryID = %d',
         self::TABLE_PATHCHANGE,
         $this->getID());
 
       queryfx(
         $conn_w,
         'DELETE FROM %T WHERE repositoryID = %d',
         self::TABLE_SUMMARY,
         $this->getID());
 
       $result = parent::delete();
 
     $this->saveTransaction();
     return $result;
   }
 
   public function isGit() {
     $vcs = $this->getVersionControlSystem();
     return ($vcs == PhabricatorRepositoryType::REPOSITORY_TYPE_GIT);
   }
 
   public function isSVN() {
     $vcs = $this->getVersionControlSystem();
     return ($vcs == PhabricatorRepositoryType::REPOSITORY_TYPE_SVN);
   }
 
   public function isHg() {
     $vcs = $this->getVersionControlSystem();
     return ($vcs == PhabricatorRepositoryType::REPOSITORY_TYPE_MERCURIAL);
   }
 
   public function isHosted() {
     return (bool)$this->getDetail('hosting-enabled', false);
   }
 
   public function setHosted($enabled) {
     return $this->setDetail('hosting-enabled', $enabled);
   }
 
-  public function getServeOverHTTP() {
-    if ($this->isSVN()) {
-      return self::SERVE_OFF;
+  public function canServeProtocol($protocol, $write) {
+    if (!$this->isTracked()) {
+      return false;
     }
-    $serve = $this->getDetail('serve-over-http', self::SERVE_OFF);
-    return $this->normalizeServeConfigSetting($serve);
-  }
-
-  public function setServeOverHTTP($mode) {
-    return $this->setDetail('serve-over-http', $mode);
-  }
 
-  public function getServeOverSSH() {
-    $serve = $this->getDetail('serve-over-ssh', self::SERVE_OFF);
-    return $this->normalizeServeConfigSetting($serve);
-  }
+    $clone_uris = $this->getCloneURIs();
+    foreach ($clone_uris as $uri) {
+      if ($uri->getBuiltinProtocol() !== $protocol) {
+        continue;
+      }
 
-  public function setServeOverSSH($mode) {
-    return $this->setDetail('serve-over-ssh', $mode);
-  }
+      $io_type = $uri->getEffectiveIoType();
+      if ($io_type == PhabricatorRepositoryURI::IO_READWRITE) {
+        return true;
+      }
 
-  public static function getProtocolAvailabilityName($constant) {
-    switch ($constant) {
-      case self::SERVE_OFF:
-        return pht('Off');
-      case self::SERVE_READONLY:
-        return pht('Read Only');
-      case self::SERVE_READWRITE:
-        return pht('Read/Write');
-      default:
-        return pht('Unknown');
+      if (!$write) {
+        if ($io_type == PhabricatorRepositoryURI::IO_READ) {
+          return true;
+        }
+      }
     }
+
+    return false;
   }
 
-  private function normalizeServeConfigSetting($value) {
-    switch ($value) {
-      case self::SERVE_OFF:
-      case self::SERVE_READONLY:
-        return $value;
-      case self::SERVE_READWRITE:
-        if ($this->isHosted()) {
-          return self::SERVE_READWRITE;
-        } else {
-          return self::SERVE_READONLY;
-        }
-      default:
-        return self::SERVE_OFF;
+  public function hasLocalWorkingCopy() {
+    try {
+      self::assertLocalExists();
+      return true;
+    } catch (Exception $ex) {
+      return false;
     }
   }
 
-
   /**
    * Raise more useful errors when there are basic filesystem problems.
    */
   private function assertLocalExists() {
     if (!$this->usesLocalWorkingCopy()) {
       return;
     }
 
     $local = $this->getLocalPath();
     Filesystem::assertExists($local);
     Filesystem::assertIsDirectory($local);
     Filesystem::assertReadable($local);
   }
 
   /**
    * Determine if the working copy is bare or not. In Git, this corresponds
    * to `--bare`. In Mercurial, `--noupdate`.
    */
   public function isWorkingCopyBare() {
     switch ($this->getVersionControlSystem()) {
       case PhabricatorRepositoryType::REPOSITORY_TYPE_SVN:
       case PhabricatorRepositoryType::REPOSITORY_TYPE_MERCURIAL:
         return false;
       case PhabricatorRepositoryType::REPOSITORY_TYPE_GIT:
         $local = $this->getLocalPath();
         if (Filesystem::pathExists($local.'/.git')) {
           return false;
         } else {
           return true;
         }
     }
   }
 
   public function usesLocalWorkingCopy() {
     switch ($this->getVersionControlSystem()) {
       case PhabricatorRepositoryType::REPOSITORY_TYPE_SVN:
         return $this->isHosted();
       case PhabricatorRepositoryType::REPOSITORY_TYPE_GIT:
       case PhabricatorRepositoryType::REPOSITORY_TYPE_MERCURIAL:
         return true;
     }
   }
 
   public function getHookDirectories() {
     $directories = array();
     if (!$this->isHosted()) {
       return $directories;
     }
 
     $root = $this->getLocalPath();
 
     switch ($this->getVersionControlSystem()) {
       case PhabricatorRepositoryType::REPOSITORY_TYPE_GIT:
         if ($this->isWorkingCopyBare()) {
           $directories[] = $root.'/hooks/pre-receive-phabricator.d/';
         } else {
           $directories[] = $root.'/.git/hooks/pre-receive-phabricator.d/';
         }
         break;
       case PhabricatorRepositoryType::REPOSITORY_TYPE_SVN:
         $directories[] = $root.'/hooks/pre-commit-phabricator.d/';
         break;
       case PhabricatorRepositoryType::REPOSITORY_TYPE_MERCURIAL:
         // NOTE: We don't support custom Mercurial hooks for now because they're
         // messy and we can't easily just drop a `hooks.d/` directory next to
         // the hooks.
         break;
     }
 
     return $directories;
   }
 
   public function canDestroyWorkingCopy() {
     if ($this->isHosted()) {
       // Never destroy hosted working copies.
       return false;
     }
 
     $default_path = PhabricatorEnv::getEnvConfig(
       'repository.default-local-path');
     return Filesystem::isDescendant($this->getLocalPath(), $default_path);
   }
 
   public function canUsePathTree() {
     return !$this->isSVN();
   }
 
   public function canUseGitLFS() {
     if (!$this->isGit()) {
       return false;
     }
 
     if (!$this->isHosted()) {
       return false;
     }
 
     // TODO: Unprototype this feature.
     if (!PhabricatorEnv::getEnvConfig('phabricator.show-prototypes')) {
       return false;
     }
 
     return true;
   }
 
   public function getGitLFSURI($path = null) {
     if (!$this->canUseGitLFS()) {
       throw new Exception(
         pht(
           'This repository does not support Git LFS, so Git LFS URIs can '.
           'not be generated for it.'));
     }
 
     $uri = $this->getRawHTTPCloneURIObject();
     $uri = (string)$uri;
     $uri = $uri.'/'.$path;
 
     return $uri;
   }
 
   public function canMirror() {
     if ($this->isGit() || $this->isHg()) {
       return true;
     }
 
     return false;
   }
 
   public function canAllowDangerousChanges() {
     if (!$this->isHosted()) {
       return false;
     }
 
     if ($this->isGit() || $this->isHg()) {
       return true;
     }
 
     return false;
   }
 
   public function shouldAllowDangerousChanges() {
     return (bool)$this->getDetail('allow-dangerous-changes');
   }
 
   public function writeStatusMessage(
     $status_type,
     $status_code,
     array $parameters = array()) {
 
     $table = new PhabricatorRepositoryStatusMessage();
     $conn_w = $table->establishConnection('w');
     $table_name = $table->getTableName();
 
     if ($status_code === null) {
       queryfx(
         $conn_w,
         'DELETE FROM %T WHERE repositoryID = %d AND statusType = %s',
         $table_name,
         $this->getID(),
         $status_type);
     } else {
       queryfx(
         $conn_w,
         'INSERT INTO %T
           (repositoryID, statusType, statusCode, parameters, epoch)
           VALUES (%d, %s, %s, %s, %d)
           ON DUPLICATE KEY UPDATE
             statusCode = VALUES(statusCode),
             parameters = VALUES(parameters),
             epoch = VALUES(epoch)',
         $table_name,
         $this->getID(),
         $status_type,
         $status_code,
         json_encode($parameters),
         time());
     }
 
     return $this;
   }
 
   public static function getRemoteURIProtocol($raw_uri) {
     $uri = new PhutilURI($raw_uri);
     if ($uri->getProtocol()) {
       return strtolower($uri->getProtocol());
     }
 
     $git_uri = new PhutilGitURI($raw_uri);
     if (strlen($git_uri->getDomain()) && strlen($git_uri->getPath())) {
       return 'ssh';
     }
 
     return null;
   }
 
   public static function assertValidRemoteURI($uri) {
     if (trim($uri) != $uri) {
       throw new Exception(
         pht('The remote URI has leading or trailing whitespace.'));
     }
 
     $protocol = self::getRemoteURIProtocol($uri);
 
     // Catch confusion between Git/SCP-style URIs and normal URIs. See T3619
     // for discussion. This is usually a user adding "ssh://" to an implicit
     // SSH Git URI.
     if ($protocol == 'ssh') {
       if (preg_match('(^[^:@]+://[^/:]+:[^\d])', $uri)) {
         throw new Exception(
           pht(
             "The remote URI is not formatted correctly. Remote URIs ".
             "with an explicit protocol should be in the form ".
             "'%s', not '%s'. The '%s' syntax is only valid in SCP-style URIs.",
             'proto://domain/path',
             'proto://domain:/path',
             ':/path'));
       }
     }
 
     switch ($protocol) {
       case 'ssh':
       case 'http':
       case 'https':
       case 'git':
       case 'svn':
       case 'svn+ssh':
         break;
       default:
         // NOTE: We're explicitly rejecting 'file://' because it can be
         // used to clone from the working copy of another repository on disk
         // that you don't normally have permission to access.
 
         throw new Exception(
           pht(
             'The URI protocol is unrecognized. It should begin with '.
             '"%s", "%s", "%s", "%s", "%s", "%s", or be in the form "%s".',
             'ssh://',
             'http://',
             'https://',
             'git://',
             'svn://',
             'svn+ssh://',
             'git@domain.com:path'));
     }
 
     return true;
   }
 
 
   /**
    * Load the pull frequency for this repository, based on the time since the
    * last activity.
    *
    * We pull rarely used repositories less frequently. This finds the most
    * recent commit which is older than the current time (which prevents us from
    * spinning on repositories with a silly commit post-dated to some time in
    * 2037). We adjust the pull frequency based on when the most recent commit
    * occurred.
    *
    * @param   int   The minimum update interval to use, in seconds.
    * @return  int   Repository update interval, in seconds.
    */
   public function loadUpdateInterval($minimum = 15) {
     // If a repository is still importing, always pull it as frequently as
     // possible. This prevents us from hanging for a long time at 99.9% when
     // importing an inactive repository.
     if ($this->isImporting()) {
       return $minimum;
     }
 
     $window_start = (PhabricatorTime::getNow() + $minimum);
 
     $table = id(new PhabricatorRepositoryCommit());
     $last_commit = queryfx_one(
       $table->establishConnection('r'),
       'SELECT epoch FROM %T
         WHERE repositoryID = %d AND epoch <= %d
         ORDER BY epoch DESC LIMIT 1',
       $table->getTableName(),
       $this->getID(),
       $window_start);
     if ($last_commit) {
       $time_since_commit = ($window_start - $last_commit['epoch']);
 
       $last_few_days = phutil_units('3 days in seconds');
 
       if ($time_since_commit <= $last_few_days) {
         // For repositories with activity in the recent past, we wait one
         // extra second for every 10 minutes since the last commit. This
         // shorter backoff is intended to handle weekends and other short
         // breaks from development.
         $smart_wait = ($time_since_commit / 600);
       } else {
         // For repositories without recent activity, we wait one extra second
         // for every 4 minutes since the last commit. This longer backoff
         // handles rarely used repositories, up to the maximum.
         $smart_wait = ($time_since_commit / 240);
       }
 
       // We'll never wait more than 6 hours to pull a repository.
       $longest_wait = phutil_units('6 hours in seconds');
       $smart_wait = min($smart_wait, $longest_wait);
 
       $smart_wait = max($minimum, $smart_wait);
     } else {
       $smart_wait = $minimum;
     }
 
     return $smart_wait;
   }
 
 
   /**
    * Retrieve the sevice URI for the device hosting this repository.
    *
    * See @{method:newConduitClient} for a general discussion of interacting
    * with repository services. This method provides lower-level resolution of
    * services, returning raw URIs.
    *
    * @param PhabricatorUser Viewing user.
    * @param bool `true` to throw if a remote URI would be returned.
    * @param list<string> List of allowable protocols.
    * @return string|null URI, or `null` for local repositories.
    */
   public function getAlmanacServiceURI(
     PhabricatorUser $viewer,
     $never_proxy,
     array $protocols) {
 
     $service = $this->loadAlmanacService();
     if (!$service) {
       return null;
     }
 
     $bindings = $service->getActiveBindings();
     if (!$bindings) {
       throw new Exception(
         pht(
           'The Almanac service for this repository is not bound to any '.
           'interfaces.'));
     }
 
     $local_device = AlmanacKeys::getDeviceID();
 
     if ($never_proxy && !$local_device) {
       throw new Exception(
         pht(
           'Unable to handle proxied service request. This device is not '.
           'registered, so it can not identify local services. Register '.
           'this device before sending requests here.'));
     }
 
     $protocol_map = array_fuse($protocols);
 
     $uris = array();
     foreach ($bindings as $binding) {
       $iface = $binding->getInterface();
 
       // If we're never proxying this and it's locally satisfiable, return
       // `null` to tell the caller to handle it locally. If we're allowed to
       // proxy, we skip this check and may proxy the request to ourselves.
       // (That proxied request will end up here with proxying forbidden,
       // return `null`, and then the request will actually run.)
 
       if ($local_device && $never_proxy) {
         if ($iface->getDevice()->getName() == $local_device) {
           return null;
         }
       }
 
       $uri = $this->getClusterRepositoryURIFromBinding($binding);
 
       $protocol = $uri->getProtocol();
       if (empty($protocol_map[$protocol])) {
         continue;
       }
 
       $uris[] = $uri;
     }
 
     if (!$uris) {
       throw new Exception(
         pht(
           'The Almanac service for this repository is not bound to any '.
           'interfaces which support the required protocols (%s).',
           implode(', ', $protocols)));
     }
 
     if ($never_proxy) {
       throw new Exception(
         pht(
           'Refusing to proxy a repository request from a cluster host. '.
           'Cluster hosts must correctly route their intracluster requests.'));
     }
 
     shuffle($uris);
     return head($uris);
   }
 
 
   /**
    * Build a new Conduit client in order to make a service call to this
    * repository.
    *
    * If the repository is hosted locally, this method may return `null`. The
    * caller should use `ConduitCall` or other local logic to complete the
    * request.
    *
    * By default, we will return a @{class:ConduitClient} for any repository with
    * a service, even if that service is on the current device.
    *
    * We do this because this configuration does not make very much sense in a
    * production context, but is very common in a test/development context
    * (where the developer's machine is both the web host and the repository
    * service). By proxying in development, we get more consistent behavior
    * between development and production, and don't have a major untested
    * codepath.
    *
    * The `$never_proxy` parameter can be used to prevent this local proxying.
    * If the flag is passed:
    *
    *   - The method will return `null` (implying a local service call)
    *     if the repository service is hosted on the current device.
    *   - The method will throw if it would need to return a client.
    *
    * This is used to prevent loops in Conduit: the first request will proxy,
    * even in development, but the second request will be identified as a
    * cluster request and forced not to proxy.
    *
    * For lower-level service resolution, see @{method:getAlmanacServiceURI}.
    *
    * @param PhabricatorUser Viewing user.
    * @param bool `true` to throw if a client would be returned.
    * @return ConduitClient|null Client, or `null` for local repositories.
    */
   public function newConduitClient(
     PhabricatorUser $viewer,
     $never_proxy = false) {
 
     $uri = $this->getAlmanacServiceURI(
       $viewer,
       $never_proxy,
       array(
         'http',
         'https',
       ));
     if ($uri === null) {
       return null;
     }
 
     $domain = id(new PhutilURI(PhabricatorEnv::getURI('/')))->getDomain();
 
     $client = id(new ConduitClient($uri))
       ->setHost($domain);
 
     if ($viewer->isOmnipotent()) {
       // If the caller is the omnipotent user (normally, a daemon), we will
       // sign the request with this host's asymmetric keypair.
 
       $public_path = AlmanacKeys::getKeyPath('device.pub');
       try {
         $public_key = Filesystem::readFile($public_path);
       } catch (Exception $ex) {
         throw new PhutilAggregateException(
           pht(
             'Unable to read device public key while attempting to make '.
             'authenticated method call within the Phabricator cluster. '.
             'Use `%s` to register keys for this device. Exception: %s',
             'bin/almanac register',
             $ex->getMessage()),
           array($ex));
       }
 
       $private_path = AlmanacKeys::getKeyPath('device.key');
       try {
         $private_key = Filesystem::readFile($private_path);
         $private_key = new PhutilOpaqueEnvelope($private_key);
       } catch (Exception $ex) {
         throw new PhutilAggregateException(
           pht(
             'Unable to read device private key while attempting to make '.
             'authenticated method call within the Phabricator cluster. '.
             'Use `%s` to register keys for this device. Exception: %s',
             'bin/almanac register',
             $ex->getMessage()),
           array($ex));
       }
 
       $client->setSigningKeys($public_key, $private_key);
     } else {
       // If the caller is a normal user, we generate or retrieve a cluster
       // API token.
 
       $token = PhabricatorConduitToken::loadClusterTokenForUser($viewer);
       if ($token) {
         $client->setConduitToken($token->getToken());
       }
     }
 
     return $client;
   }
 
 /* -(  Repository URIs  )---------------------------------------------------- */
 
 
   public function attachURIs(array $uris) {
     $custom_map = array();
     foreach ($uris as $key => $uri) {
       $builtin_key = $uri->getRepositoryURIBuiltinKey();
       if ($builtin_key !== null) {
         $custom_map[$builtin_key] = $key;
       }
     }
 
     $builtin_uris = $this->newBuiltinURIs();
     $seen_builtins = array();
     foreach ($builtin_uris as $builtin_uri) {
       $builtin_key = $builtin_uri->getRepositoryURIBuiltinKey();
       $seen_builtins[$builtin_key] = true;
 
       // If this builtin URI is disabled, don't attach it and remove the
       // persisted version if it exists.
       if ($builtin_uri->getIsDisabled()) {
         if (isset($custom_map[$builtin_key])) {
           unset($uris[$custom_map[$builtin_key]]);
         }
         continue;
       }
 
-      // If we don't have a persisted version of the URI, add the builtin
-      // version.
-      if (empty($custom_map[$builtin_key])) {
-        $uris[] = $builtin_uri;
+      // If the URI exists, make sure it's marked as not being disabled.
+      if (isset($custom_map[$builtin_key])) {
+        $uris[$custom_map[$builtin_key]]->setIsDisabled(false);
       }
     }
 
     // Remove any builtins which no longer exist.
     foreach ($custom_map as $builtin_key => $key) {
       if (empty($seen_builtins[$builtin_key])) {
         unset($uris[$key]);
       }
     }
 
     $this->uris = $uris;
 
     return $this;
   }
 
   public function getURIs() {
     return $this->assertAttached($this->uris);
   }
 
-  protected function newBuiltinURIs() {
+  public function getCloneURIs() {
+    $uris = $this->getURIs();
+
+    $clone = array();
+    foreach ($uris as $uri) {
+      if (!$uri->isBuiltin()) {
+        continue;
+      }
+
+      if ($uri->getIsDisabled()) {
+        continue;
+      }
+
+      $io_type = $uri->getEffectiveIoType();
+      $is_clone =
+        ($io_type == PhabricatorRepositoryURI::IO_READ) ||
+        ($io_type == PhabricatorRepositoryURI::IO_READWRITE);
+
+      if (!$is_clone) {
+        continue;
+      }
+
+      $clone[] = $uri;
+    }
+
+    return msort($clone, 'getURIScore');
+  }
+
+
+  public function newBuiltinURIs() {
     $has_callsign = ($this->getCallsign() !== null);
     $has_shortname = ($this->getRepositorySlug() !== null);
 
+    // TODO: For now, never enable these because they don't work yet.
+    $has_shortname = false;
+
     $identifier_map = array(
       PhabricatorRepositoryURI::BUILTIN_IDENTIFIER_CALLSIGN => $has_callsign,
       PhabricatorRepositoryURI::BUILTIN_IDENTIFIER_SHORTNAME => $has_shortname,
       PhabricatorRepositoryURI::BUILTIN_IDENTIFIER_ID => true,
     );
 
     $allow_http = PhabricatorEnv::getEnvConfig('diffusion.allow-http-auth');
 
     $base_uri = PhabricatorEnv::getURI('/');
     $base_uri = new PhutilURI($base_uri);
     $has_https = ($base_uri->getProtocol() == 'https');
     $has_https = ($has_https && $allow_http);
 
     $has_http = !PhabricatorEnv::getEnvConfig('security.require-https');
     $has_http = ($has_http && $allow_http);
 
-    // TODO: Maybe allow users to disable this by default somehow?
-    $has_ssh = true;
+    // HTTP is not supported for Subversion.
+    if ($this->isSVN()) {
+      $has_http = false;
+      $has_https = false;
+    }
+
+    $has_ssh = (bool)strlen(PhabricatorEnv::getEnvConfig('phd.user'));
 
     $protocol_map = array(
       PhabricatorRepositoryURI::BUILTIN_PROTOCOL_SSH => $has_ssh,
       PhabricatorRepositoryURI::BUILTIN_PROTOCOL_HTTPS => $has_https,
       PhabricatorRepositoryURI::BUILTIN_PROTOCOL_HTTP => $has_http,
     );
 
     $uris = array();
     foreach ($protocol_map as $protocol => $proto_supported) {
       foreach ($identifier_map as $identifier => $id_supported) {
+        // This is just a dummy value because it can't be empty; we'll force
+        // it to a proper value when using it in the UI.
+        $builtin_uri = "{$protocol}://{$identifier}";
         $uris[] = PhabricatorRepositoryURI::initializeNewURI()
           ->setRepositoryPHID($this->getPHID())
           ->attachRepository($this)
           ->setBuiltinProtocol($protocol)
           ->setBuiltinIdentifier($identifier)
-          ->setIsDisabled(!$proto_supported || !$id_supported);
+          ->setURI($builtin_uri)
+          ->setIsDisabled((int)(!$proto_supported || !$id_supported));
       }
     }
 
     return $uris;
   }
 
 
   public function getClusterRepositoryURIFromBinding(
     AlmanacBinding $binding) {
     $protocol = $binding->getAlmanacPropertyValue('protocol');
     if ($protocol === null) {
       $protocol = 'https';
     }
 
     $iface = $binding->getInterface();
     $address = $iface->renderDisplayAddress();
 
     $path = $this->getURI();
 
     return id(new PhutilURI("{$protocol}://{$address}"))
       ->setPath($path);
   }
 
   public function loadAlmanacService() {
     $service_phid = $this->getAlmanacServicePHID();
     if (!$service_phid) {
       // No service, so this is a local repository.
       return null;
     }
 
     $service = id(new AlmanacServiceQuery())
       ->setViewer(PhabricatorUser::getOmnipotentUser())
       ->withPHIDs(array($service_phid))
       ->needBindings(true)
       ->needProperties(true)
       ->executeOne();
     if (!$service) {
       throw new Exception(
         pht(
           'The Almanac service for this repository is invalid or could not '.
           'be loaded.'));
     }
 
     $service_type = $service->getServiceImplementation();
     if (!($service_type instanceof AlmanacClusterRepositoryServiceType)) {
       throw new Exception(
         pht(
           'The Almanac service for this repository does not have the correct '.
           'service type.'));
     }
 
     return $service;
   }
 
+  public function markImporting() {
+    $this->openTransaction();
+      $this->beginReadLocking();
+        $repository = $this->reload();
+        $repository->setDetail('importing', true);
+        $repository->save();
+      $this->endReadLocking();
+    $this->saveTransaction();
+
+    return $repository;
+  }
+
 
 /* -(  Symbols  )-------------------------------------------------------------*/
 
   public function getSymbolSources() {
     return $this->getDetail('symbol-sources', array());
   }
 
   public function getSymbolLanguages() {
     return $this->getDetail('symbol-languages', array());
   }
 
 
 /* -(  Staging  )------------------------------------------------------------ */
 
 
   public function supportsStaging() {
     return $this->isGit();
   }
 
 
   public function getStagingURI() {
     if (!$this->supportsStaging()) {
       return null;
     }
     return $this->getDetail('staging-uri', null);
   }
 
 
 /* -(  Automation  )--------------------------------------------------------- */
 
 
   public function supportsAutomation() {
     return $this->isGit();
   }
 
   public function canPerformAutomation() {
     if (!$this->supportsAutomation()) {
       return false;
     }
 
     if (!$this->getAutomationBlueprintPHIDs()) {
       return false;
     }
 
     return true;
   }
 
   public function getAutomationBlueprintPHIDs() {
     if (!$this->supportsAutomation()) {
       return array();
     }
     return $this->getDetail('automation.blueprintPHIDs', array());
   }
 
 
 /* -(  PhabricatorApplicationTransactionInterface  )------------------------- */
 
 
   public function getApplicationTransactionEditor() {
     return new PhabricatorRepositoryEditor();
   }
 
   public function getApplicationTransactionObject() {
     return $this;
   }
 
   public function getApplicationTransactionTemplate() {
     return new PhabricatorRepositoryTransaction();
   }
 
   public function willRenderTimeline(
     PhabricatorApplicationTransactionView $timeline,
     AphrontRequest $request) {
 
     return $timeline;
   }
 
 
 /* -(  PhabricatorPolicyInterface  )----------------------------------------- */
 
 
   public function getCapabilities() {
     return array(
       PhabricatorPolicyCapability::CAN_VIEW,
       PhabricatorPolicyCapability::CAN_EDIT,
       DiffusionPushCapability::CAPABILITY,
     );
   }
 
   public function getPolicy($capability) {
     switch ($capability) {
       case PhabricatorPolicyCapability::CAN_VIEW:
         return $this->getViewPolicy();
       case PhabricatorPolicyCapability::CAN_EDIT:
         return $this->getEditPolicy();
       case DiffusionPushCapability::CAPABILITY:
         return $this->getPushPolicy();
     }
   }
 
   public function hasAutomaticCapability($capability, PhabricatorUser $user) {
     return false;
   }
 
   public function describeAutomaticCapability($capability) {
     return null;
   }
 
 
 
 /* -(  PhabricatorMarkupInterface  )----------------------------------------- */
 
 
   public function getMarkupFieldKey($field) {
     $hash = PhabricatorHash::digestForIndex($this->getMarkupText($field));
     return "repo:{$hash}";
   }
 
   public function newMarkupEngine($field) {
     return PhabricatorMarkupEngine::newMarkupEngine(array());
   }
 
   public function getMarkupText($field) {
     return $this->getDetail('description');
   }
 
   public function didMarkupText(
     $field,
     $output,
     PhutilMarkupEngine $engine) {
     require_celerity_resource('phabricator-remarkup-css');
     return phutil_tag(
       'div',
       array(
         'class' => 'phabricator-remarkup',
       ),
       $output);
   }
 
   public function shouldUseMarkupCache($field) {
     return true;
   }
 
 
 /* -(  PhabricatorDestructibleInterface  )----------------------------------- */
 
 
   public function destroyObjectPermanently(
     PhabricatorDestructionEngine $engine) {
 
     $phid = $this->getPHID();
 
     $this->openTransaction();
 
       $this->delete();
 
       PhabricatorRepositoryURIIndex::updateRepositoryURIs($phid, array());
 
       $books = id(new DivinerBookQuery())
         ->setViewer($engine->getViewer())
         ->withRepositoryPHIDs(array($phid))
         ->execute();
       foreach ($books as $book) {
         $engine->destroyObject($book);
       }
 
       $atoms = id(new DivinerAtomQuery())
         ->setViewer($engine->getViewer())
         ->withRepositoryPHIDs(array($phid))
         ->execute();
       foreach ($atoms as $atom) {
         $engine->destroyObject($atom);
       }
 
       $lfs_refs = id(new PhabricatorRepositoryGitLFSRefQuery())
         ->setViewer($engine->getViewer())
         ->withRepositoryPHIDs(array($phid))
         ->execute();
       foreach ($lfs_refs as $ref) {
         $engine->destroyObject($ref);
       }
 
     $this->saveTransaction();
   }
 
 
 /* -(  PhabricatorSpacesInterface  )----------------------------------------- */
 
 
   public function getSpacePHID() {
     return $this->spacePHID;
   }
 
 /* -(  PhabricatorConduitResultInterface  )---------------------------------- */
 
 
   public function getFieldSpecificationsForConduit() {
     return array(
       id(new PhabricatorConduitSearchFieldSpecification())
         ->setKey('name')
         ->setType('string')
         ->setDescription(pht('The repository name.')),
       id(new PhabricatorConduitSearchFieldSpecification())
         ->setKey('vcs')
         ->setType('string')
         ->setDescription(
           pht('The VCS this repository uses ("git", "hg" or "svn").')),
       id(new PhabricatorConduitSearchFieldSpecification())
         ->setKey('callsign')
         ->setType('string')
         ->setDescription(pht('The repository callsign, if it has one.')),
       id(new PhabricatorConduitSearchFieldSpecification())
         ->setKey('shortName')
         ->setType('string')
         ->setDescription(pht('Unique short name, if the repository has one.')),
       id(new PhabricatorConduitSearchFieldSpecification())
         ->setKey('status')
         ->setType('string')
         ->setDescription(pht('Active or inactive status.')),
     );
   }
 
   public function getFieldValuesForConduit() {
     return array(
       'name' => $this->getName(),
       'vcs' => $this->getVersionControlSystem(),
       'callsign' => $this->getCallsign(),
       'shortName' => $this->getRepositorySlug(),
       'status' => $this->getStatus(),
     );
   }
 
   public function getConduitSearchAttachments() {
     return array(
       id(new DiffusionRepositoryURIsSearchEngineAttachment())
         ->setAttachmentKey('uris'),
     );
   }
 
 }
diff --git a/src/applications/repository/storage/PhabricatorRepositoryMirror.php b/src/applications/repository/storage/PhabricatorRepositoryMirror.php
index a5058ab34..d124297bc 100644
--- a/src/applications/repository/storage/PhabricatorRepositoryMirror.php
+++ b/src/applications/repository/storage/PhabricatorRepositoryMirror.php
@@ -1,69 +1,30 @@
 <?php
 
-final class PhabricatorRepositoryMirror extends PhabricatorRepositoryDAO
-  implements PhabricatorPolicyInterface {
+/**
+ * TODO: Remove this class and drop the underlying table after some time has
+ * passed. It currently exists only so that "bin/storage adjust" does not
+ * complain about the table.
+ */
+final class PhabricatorRepositoryMirror
+  extends PhabricatorRepositoryDAO {
 
   protected $repositoryPHID;
   protected $remoteURI;
   protected $credentialPHID;
 
-  private $repository = self::ATTACHABLE;
-
-  public static function initializeNewMirror(PhabricatorUser $actor) {
-    return id(new PhabricatorRepositoryMirror())
-      ->setRemoteURI('');
-  }
-
   protected function getConfiguration() {
     return array(
       self::CONFIG_AUX_PHID => true,
       self::CONFIG_COLUMN_SCHEMA => array(
         'remoteURI' => 'text255',
         'credentialPHID' => 'phid?',
       ),
       self::CONFIG_KEY_SCHEMA => array(
         'key_repository' => array(
           'columns' => array('repositoryPHID'),
         ),
       ),
     ) + parent::getConfiguration();
   }
 
-  public function generatePHID() {
-    return PhabricatorPHID::generateNewPHID(
-      PhabricatorRepositoryMirrorPHIDType::TYPECONST);
-  }
-
-  public function attachRepository(PhabricatorRepository $repository) {
-    $this->repository = $repository;
-    return $this;
-  }
-
-  public function getRepository() {
-    return $this->assertAttached($this->repository);
-  }
-
-
-/* -(  PhabricatorPolicyInterface  )----------------------------------------- */
-
-
-  public function getCapabilities() {
-    return array(
-      PhabricatorPolicyCapability::CAN_VIEW,
-      PhabricatorPolicyCapability::CAN_EDIT,
-    );
-  }
-
-  public function getPolicy($capability) {
-    return $this->getRepository()->getPolicy($capability);
-  }
-
-  public function hasAutomaticCapability($capability, PhabricatorUser $viewer) {
-    return $this->getRepository()->hasAutomaticCapability($capability, $viewer);
-  }
-
-  public function describeAutomaticCapability($capability) {
-    return null;
-  }
-
 }
diff --git a/src/applications/repository/storage/PhabricatorRepositoryTransaction.php b/src/applications/repository/storage/PhabricatorRepositoryTransaction.php
index 76e75a70b..e02b670f7 100644
--- a/src/applications/repository/storage/PhabricatorRepositoryTransaction.php
+++ b/src/applications/repository/storage/PhabricatorRepositoryTransaction.php
@@ -1,516 +1,516 @@
 <?php
 
 final class PhabricatorRepositoryTransaction
   extends PhabricatorApplicationTransaction {
 
   const TYPE_VCS = 'repo:vcs';
   const TYPE_ACTIVATE = 'repo:activate';
   const TYPE_NAME = 'repo:name';
   const TYPE_DESCRIPTION = 'repo:description';
   const TYPE_ENCODING = 'repo:encoding';
   const TYPE_DEFAULT_BRANCH = 'repo:default-branch';
   const TYPE_TRACK_ONLY = 'repo:track-only';
   const TYPE_AUTOCLOSE_ONLY = 'repo:autoclose-only';
   const TYPE_SVN_SUBPATH = 'repo:svn-subpath';
   const TYPE_NOTIFY = 'repo:notify';
   const TYPE_AUTOCLOSE = 'repo:autoclose';
   const TYPE_PUSH_POLICY = 'repo:push-policy';
   const TYPE_DANGEROUS = 'repo:dangerous';
   const TYPE_SLUG = 'repo:slug';
   const TYPE_SERVICE = 'repo:service';
   const TYPE_SYMBOLS_SOURCES = 'repo:symbol-source';
   const TYPE_SYMBOLS_LANGUAGE = 'repo:symbol-language';
   const TYPE_STAGING_URI = 'repo:staging-uri';
   const TYPE_AUTOMATION_BLUEPRINTS = 'repo:automation-blueprints';
   const TYPE_CALLSIGN = 'repo:callsign';
 
   // TODO: Clean up these legacy transaction types.
   const TYPE_SSH_LOGIN = 'repo:ssh-login';
   const TYPE_SSH_KEY = 'repo:ssh-key';
   const TYPE_SSH_KEYFILE = 'repo:ssh-keyfile';
   const TYPE_HTTP_LOGIN = 'repo:http-login';
   const TYPE_HTTP_PASS = 'repo:http-pass';
   const TYPE_CREDENTIAL = 'repo:credential';
   const TYPE_PROTOCOL_HTTP = 'repo:serve-http';
   const TYPE_PROTOCOL_SSH = 'repo:serve-ssh';
   const TYPE_HOSTING = 'repo:hosting';
   const TYPE_LOCAL_PATH = 'repo:local-path';
   const TYPE_REMOTE_URI = 'repo:remote-uri';
   const TYPE_UUID = 'repo:uuid';
 
   public function getApplicationName() {
     return 'repository';
   }
 
   public function getApplicationTransactionType() {
     return PhabricatorRepositoryRepositoryPHIDType::TYPECONST;
   }
 
   public function getApplicationTransactionCommentObject() {
     return null;
   }
 
   public function getRequiredHandlePHIDs() {
     $phids = parent::getRequiredHandlePHIDs();
 
     $old = $this->getOldValue();
     $new = $this->getNewValue();
 
     switch ($this->getTransactionType()) {
       case self::TYPE_PUSH_POLICY:
       case self::TYPE_SERVICE:
         if ($old) {
           $phids[] = $old;
         }
         if ($new) {
           $phids[] = $new;
         }
         break;
       case self::TYPE_SYMBOLS_SOURCES:
       case self::TYPE_AUTOMATION_BLUEPRINTS:
         if ($old) {
           $phids = array_merge($phids, $old);
         }
         if ($new) {
           $phids = array_merge($phids, $new);
         }
         break;
     }
 
     return $phids;
   }
 
   public function shouldHide() {
     $old = $this->getOldValue();
     $new = $this->getNewValue();
 
     switch ($this->getTransactionType()) {
       case self::TYPE_REMOTE_URI:
       case self::TYPE_SSH_LOGIN:
       case self::TYPE_SSH_KEY:
       case self::TYPE_SSH_KEYFILE:
       case self::TYPE_HTTP_LOGIN:
       case self::TYPE_HTTP_PASS:
         // Hide null vs empty string changes.
         return (!strlen($old) && !strlen($new));
       case self::TYPE_LOCAL_PATH:
       case self::TYPE_NAME:
         // Hide these on create, they aren't interesting and we have an
         // explicit "create" transaction.
         if (!strlen($old)) {
           return true;
         }
         break;
     }
 
     return parent::shouldHide();
   }
 
   public function getIcon() {
     switch ($this->getTransactionType()) {
       case self::TYPE_VCS:
         return 'fa-plus';
     }
     return parent::getIcon();
   }
 
   public function getColor() {
     switch ($this->getTransactionType()) {
       case self::TYPE_VCS:
         return 'green';
     }
     return parent::getIcon();
   }
 
   public function getTitle() {
     $author_phid = $this->getAuthorPHID();
 
     $old = $this->getOldValue();
     $new = $this->getNewValue();
 
     switch ($this->getTransactionType()) {
       case self::TYPE_VCS:
         return pht(
           '%s created this repository.',
           $this->renderHandleLink($author_phid));
       case self::TYPE_ACTIVATE:
         // TODO: Old versions of this transaction use a boolean value, but
         // should be migrated.
         $is_deactivate =
           (!$new) ||
           ($new == PhabricatorRepository::STATUS_INACTIVE);
 
         if (!$is_deactivate) {
           return pht(
             '%s activated this repository.',
             $this->renderHandleLink($author_phid));
         } else {
           return pht(
             '%s deactivated this repository.',
             $this->renderHandleLink($author_phid));
         }
       case self::TYPE_NAME:
         return pht(
           '%s renamed this repository from "%s" to "%s".',
           $this->renderHandleLink($author_phid),
           $old,
           $new);
       case self::TYPE_DESCRIPTION:
         return pht(
           '%s updated the description of this repository.',
           $this->renderHandleLink($author_phid));
       case self::TYPE_ENCODING:
         if (strlen($old) && !strlen($new)) {
           return pht(
             '%s removed the "%s" encoding configured for this repository.',
             $this->renderHandleLink($author_phid),
             $old);
         } else if (strlen($new) && !strlen($old)) {
           return pht(
             '%s set the encoding for this repository to "%s".',
             $this->renderHandleLink($author_phid),
             $new);
         } else {
           return pht(
             '%s changed the repository encoding from "%s" to "%s".',
             $this->renderHandleLink($author_phid),
             $old,
             $new);
         }
       case self::TYPE_DEFAULT_BRANCH:
         if (!strlen($new)) {
           return pht(
             '%s removed "%s" as the default branch.',
             $this->renderHandleLink($author_phid),
             $old);
         } else if (!strlen($old)) {
           return pht(
             '%s set the default branch to "%s".',
             $this->renderHandleLink($author_phid),
             $new);
         } else {
           return pht(
             '%s changed the default branch from "%s" to "%s".',
             $this->renderHandleLink($author_phid),
             $old,
             $new);
         }
         break;
       case self::TYPE_TRACK_ONLY:
         if (!$new) {
           return pht(
             '%s set this repository to track all branches.',
             $this->renderHandleLink($author_phid));
         } else if (!$old) {
           return pht(
             '%s set this repository to track branches: %s.',
             $this->renderHandleLink($author_phid),
             implode(', ', $new));
         } else {
           return pht(
             '%s changed track branches from "%s" to "%s".',
             $this->renderHandleLink($author_phid),
             implode(', ', $old),
             implode(', ', $new));
         }
         break;
       case self::TYPE_AUTOCLOSE_ONLY:
         if (!$new) {
           return pht(
             '%s set this repository to autoclose on all branches.',
             $this->renderHandleLink($author_phid));
         } else if (!$old) {
           return pht(
             '%s set this repository to autoclose on branches: %s.',
             $this->renderHandleLink($author_phid),
             implode(', ', $new));
         } else {
           return pht(
             '%s changed autoclose branches from "%s" to "%s".',
             $this->renderHandleLink($author_phid),
             implode(', ', $old),
             implode(', ', $new));
         }
         break;
       case self::TYPE_UUID:
         if (!strlen($new)) {
           return pht(
             '%s removed "%s" as the repository UUID.',
             $this->renderHandleLink($author_phid),
             $old);
         } else if (!strlen($old)) {
           return pht(
             '%s set the repository UUID to "%s".',
             $this->renderHandleLink($author_phid),
             $new);
         } else {
           return pht(
             '%s changed the repository UUID from "%s" to "%s".',
             $this->renderHandleLink($author_phid),
             $old,
             $new);
         }
         break;
       case self::TYPE_SVN_SUBPATH:
         if (!strlen($new)) {
           return pht(
             '%s removed "%s" as the Import Only path.',
             $this->renderHandleLink($author_phid),
             $old);
         } else if (!strlen($old)) {
           return pht(
             '%s set the repository to import only "%s".',
             $this->renderHandleLink($author_phid),
             $new);
         } else {
           return pht(
             '%s changed the import path from "%s" to "%s".',
             $this->renderHandleLink($author_phid),
             $old,
             $new);
         }
         break;
       case self::TYPE_NOTIFY:
         if ($new) {
           return pht(
             '%s enabled notifications and publishing for this repository.',
             $this->renderHandleLink($author_phid));
         } else {
           return pht(
             '%s disabled notifications and publishing for this repository.',
             $this->renderHandleLink($author_phid));
         }
         break;
       case self::TYPE_AUTOCLOSE:
         if ($new) {
           return pht(
             '%s enabled autoclose for this repository.',
             $this->renderHandleLink($author_phid));
         } else {
           return pht(
             '%s disabled autoclose for this repository.',
             $this->renderHandleLink($author_phid));
         }
         break;
       case self::TYPE_REMOTE_URI:
         if (!strlen($old)) {
           return pht(
             '%s set the remote URI for this repository to "%s".',
             $this->renderHandleLink($author_phid),
             $new);
         } else if (!strlen($new)) {
           return pht(
             '%s removed the remote URI for this repository.',
             $this->renderHandleLink($author_phid));
         } else {
           return pht(
             '%s changed the remote URI for this repository from "%s" to "%s".',
             $this->renderHandleLink($author_phid),
             $old,
             $new);
         }
         break;
       case self::TYPE_SSH_LOGIN:
         return pht(
           '%s updated the SSH login for this repository.',
           $this->renderHandleLink($author_phid));
       case self::TYPE_SSH_KEY:
         return pht(
           '%s updated the SSH key for this repository.',
           $this->renderHandleLink($author_phid));
       case self::TYPE_SSH_KEYFILE:
         return pht(
           '%s updated the SSH keyfile for this repository.',
           $this->renderHandleLink($author_phid));
       case self::TYPE_HTTP_LOGIN:
         return pht(
           '%s updated the HTTP login for this repository.',
           $this->renderHandleLink($author_phid));
       case self::TYPE_HTTP_PASS:
         return pht(
           '%s updated the HTTP password for this repository.',
           $this->renderHandleLink($author_phid));
       case self::TYPE_LOCAL_PATH:
         return pht(
           '%s changed the local path from "%s" to "%s".',
           $this->renderHandleLink($author_phid),
           $old,
           $new);
       case self::TYPE_HOSTING:
         if ($new) {
           return pht(
             '%s changed this repository to be hosted on Phabricator.',
             $this->renderHandleLink($author_phid));
         } else {
           return pht(
             '%s changed this repository to track a remote elsewhere.',
             $this->renderHandleLink($author_phid));
         }
       case self::TYPE_PROTOCOL_HTTP:
         return pht(
           '%s changed the availability of this repository over HTTP from '.
           '"%s" to "%s".',
           $this->renderHandleLink($author_phid),
-          PhabricatorRepository::getProtocolAvailabilityName($old),
-          PhabricatorRepository::getProtocolAvailabilityName($new));
+          $old,
+          $new);
       case self::TYPE_PROTOCOL_SSH:
         return pht(
           '%s changed the availability of this repository over SSH from '.
           '"%s" to "%s".',
           $this->renderHandleLink($author_phid),
-          PhabricatorRepository::getProtocolAvailabilityName($old),
-          PhabricatorRepository::getProtocolAvailabilityName($new));
+          $old,
+          $new);
       case self::TYPE_PUSH_POLICY:
         return pht(
           '%s changed the push policy of this repository from "%s" to "%s".',
           $this->renderHandleLink($author_phid),
           $this->renderPolicyName($old, 'old'),
           $this->renderPolicyName($new, 'new'));
       case self::TYPE_DANGEROUS:
         if ($new) {
           return pht(
             '%s disabled protection against dangerous changes.',
             $this->renderHandleLink($author_phid));
         } else {
           return pht(
             '%s enabled protection against dangerous changes.',
             $this->renderHandleLink($author_phid));
         }
       case self::TYPE_SLUG:
         if (strlen($old) && !strlen($new)) {
           return pht(
             '%s removed the short name of this repository.',
             $this->renderHandleLink($author_phid));
         } else if (strlen($new) && !strlen($old)) {
           return pht(
             '%s set the short name of this repository to "%s".',
             $this->renderHandleLink($author_phid),
             $new);
         } else {
           return pht(
             '%s changed the short name of this repository from "%s" to "%s".',
             $this->renderHandleLink($author_phid),
             $old,
             $new);
         }
       case self::TYPE_SERVICE:
         if (strlen($old) && !strlen($new)) {
           return pht(
             '%s moved storage for this repository from %s to local.',
             $this->renderHandleLink($author_phid),
             $this->renderHandleLink($old));
         } else if (!strlen($old) && strlen($new)) {
           // TODO: Possibly, we should distinguish between automatic assignment
           // on creation vs explicit adjustment.
           return pht(
             '%s set storage for this repository to %s.',
             $this->renderHandleLink($author_phid),
             $this->renderHandleLink($new));
         } else {
           return pht(
             '%s moved storage for this repository from %s to %s.',
             $this->renderHandleLink($author_phid),
             $this->renderHandleLink($old),
             $this->renderHandleLink($new));
         }
       case self::TYPE_SYMBOLS_SOURCES:
         return pht(
           '%s changed symbol sources from %s to %s.',
           $this->renderHandleLink($author_phid),
           empty($old) ? pht('None') : $this->renderHandleList($old),
           empty($new) ? pht('None') : $this->renderHandleList($new));
 
       case self::TYPE_SYMBOLS_LANGUAGE:
         return pht('%s changed indexed languages from %s to %s.',
           $this->renderHandleLink($author_phid),
           $old ? implode(', ', $old) : pht('Any'),
           $new ? implode(', ', $new) : pht('Any'));
 
       case self::TYPE_STAGING_URI:
         if (!$old) {
           return pht(
             '%s set "%s" as the staging area for this repository.',
             $this->renderHandleLink($author_phid),
             $new);
         } else if (!$new) {
           return pht(
             '%s removed "%s" as the staging area for this repository.',
             $this->renderHandleLink($author_phid),
             $old);
         } else {
           return pht(
             '%s changed the staging area for this repository from '.
             '"%s" to "%s".',
             $this->renderHandleLink($author_phid),
             $old,
             $new);
         }
 
       case self::TYPE_AUTOMATION_BLUEPRINTS:
         $add = array_diff($new, $old);
         $rem = array_diff($old, $new);
 
         if ($add && $rem) {
           return pht(
             '%s changed %s automation blueprint(s), '.
             'added %s: %s; removed %s: %s.',
             $this->renderHandleLink($author_phid),
             new PhutilNumber(count($add) + count($rem)),
             new PhutilNumber(count($add)),
             $this->renderHandleList($add),
             new PhutilNumber(count($rem)),
             $this->renderHandleList($rem));
         } else if ($add) {
           return pht(
             '%s added %s automation blueprint(s): %s.',
             $this->renderHandleLink($author_phid),
             new PhutilNumber(count($add)),
             $this->renderHandleList($add));
         } else {
           return pht(
             '%s removed %s automation blueprint(s): %s.',
             $this->renderHandleLink($author_phid),
             new PhutilNumber(count($rem)),
             $this->renderHandleList($rem));
         }
 
       case self::TYPE_CALLSIGN:
         if ($old === null) {
           return pht(
             '%s set the callsign for this repository to "%s".',
             $this->renderHandleLink($author_phid),
             $new);
         } else if ($new === null) {
           return pht(
             '%s removed the callsign ("%s") for this repository.',
             $this->renderHandleLink($author_phid),
             $old);
         } else {
           return pht(
             '%s changed the callsign for this repository from "%s" to "%s".',
             $this->renderHandleLink($author_phid),
             $old,
             $new);
         }
 
     }
 
     return parent::getTitle();
   }
 
   public function hasChangeDetails() {
     switch ($this->getTransactionType()) {
       case self::TYPE_DESCRIPTION:
         return true;
     }
     return parent::hasChangeDetails();
   }
 
   public function renderChangeDetails(PhabricatorUser $viewer) {
     return $this->renderTextCorpusChangeDetails(
       $viewer,
       $this->getOldValue(),
       $this->getNewValue());
   }
 
 }
diff --git a/src/applications/repository/storage/PhabricatorRepositoryURI.php b/src/applications/repository/storage/PhabricatorRepositoryURI.php
index 7e7741e37..53b904045 100644
--- a/src/applications/repository/storage/PhabricatorRepositoryURI.php
+++ b/src/applications/repository/storage/PhabricatorRepositoryURI.php
@@ -1,599 +1,743 @@
 <?php
 
 final class PhabricatorRepositoryURI
   extends PhabricatorRepositoryDAO
   implements
     PhabricatorApplicationTransactionInterface,
     PhabricatorPolicyInterface,
     PhabricatorExtendedPolicyInterface,
     PhabricatorConduitResultInterface {
 
   protected $repositoryPHID;
   protected $uri;
   protected $builtinProtocol;
   protected $builtinIdentifier;
   protected $credentialPHID;
   protected $ioType;
   protected $displayType;
   protected $isDisabled;
 
   private $repository = self::ATTACHABLE;
 
   const BUILTIN_PROTOCOL_SSH = 'ssh';
   const BUILTIN_PROTOCOL_HTTP = 'http';
   const BUILTIN_PROTOCOL_HTTPS = 'https';
 
   const BUILTIN_IDENTIFIER_ID = 'id';
   const BUILTIN_IDENTIFIER_SHORTNAME = 'shortname';
   const BUILTIN_IDENTIFIER_CALLSIGN = 'callsign';
 
   const DISPLAY_DEFAULT = 'default';
   const DISPLAY_NEVER = 'never';
   const DISPLAY_ALWAYS = 'always';
 
   const IO_DEFAULT = 'default';
   const IO_OBSERVE = 'observe';
   const IO_MIRROR = 'mirror';
   const IO_NONE = 'none';
   const IO_READ = 'read';
   const IO_READWRITE = 'readwrite';
 
   protected function getConfiguration() {
     return array(
       self::CONFIG_AUX_PHID => true,
       self::CONFIG_COLUMN_SCHEMA => array(
         'uri' => 'text255',
         'builtinProtocol' => 'text32?',
         'builtinIdentifier' => 'text32?',
         'credentialPHID' => 'phid?',
         'ioType' => 'text32',
         'displayType' => 'text32',
         'isDisabled' => 'bool',
       ),
       self::CONFIG_KEY_SCHEMA => array(
         'key_builtin' => array(
           'columns' => array(
             'repositoryPHID',
             'builtinProtocol',
             'builtinIdentifier',
           ),
           'unique' => true,
         ),
       ),
     ) + parent::getConfiguration();
   }
 
   public static function initializeNewURI() {
     return id(new self())
       ->setIoType(self::IO_DEFAULT)
       ->setDisplayType(self::DISPLAY_DEFAULT)
       ->setIsDisabled(0);
   }
 
   public function generatePHID() {
     return PhabricatorPHID::generateNewPHID(
       PhabricatorRepositoryURIPHIDType::TYPECONST);
   }
 
   public function attachRepository(PhabricatorRepository $repository) {
     $this->repository = $repository;
     return $this;
   }
 
   public function getRepository() {
     return $this->assertAttached($this->repository);
   }
 
   public function getRepositoryURIBuiltinKey() {
     if (!$this->getBuiltinProtocol()) {
       return null;
     }
 
     $parts = array(
       $this->getBuiltinProtocol(),
       $this->getBuiltinIdentifier(),
     );
 
     return implode('.', $parts);
   }
 
   public function isBuiltin() {
     return (bool)$this->getBuiltinProtocol();
   }
 
   public function getEffectiveDisplayType() {
     $display = $this->getDisplayType();
 
     if ($display != self::DISPLAY_DEFAULT) {
       return $display;
     }
 
     return $this->getDefaultDisplayType();
   }
 
   public function getDefaultDisplayType() {
     switch ($this->getEffectiveIOType()) {
       case self::IO_MIRROR:
       case self::IO_OBSERVE:
       case self::IO_NONE:
         return self::DISPLAY_NEVER;
       case self::IO_READ:
       case self::IO_READWRITE:
         // By default, only show the "best" version of the builtin URI, not the
         // other redundant versions.
         $repository = $this->getRepository();
         $other_uris = $repository->getURIs();
 
         $identifier_value = array(
           self::BUILTIN_IDENTIFIER_CALLSIGN => 3,
           self::BUILTIN_IDENTIFIER_SHORTNAME => 2,
           self::BUILTIN_IDENTIFIER_ID => 1,
         );
 
         $have_identifiers = array();
         foreach ($other_uris as $other_uri) {
           if ($other_uri->getIsDisabled()) {
             continue;
           }
 
           $identifier = $other_uri->getBuiltinIdentifier();
           if (!$identifier) {
             continue;
           }
 
           $have_identifiers[$identifier] = $identifier_value[$identifier];
         }
 
         $best_identifier = max($have_identifiers);
         $this_identifier = $identifier_value[$this->getBuiltinIdentifier()];
 
         if ($this_identifier < $best_identifier) {
           return self::DISPLAY_NEVER;
         }
 
         return self::DISPLAY_ALWAYS;
     }
 
     return self::DISPLAY_NEVER;
   }
 
 
   public function getEffectiveIOType() {
     $io = $this->getIoType();
 
     if ($io != self::IO_DEFAULT) {
       return $io;
     }
 
     return $this->getDefaultIOType();
   }
 
   public function getDefaultIOType() {
     if ($this->isBuiltin()) {
       $repository = $this->getRepository();
       $other_uris = $repository->getURIs();
 
       $any_observe = false;
       foreach ($other_uris as $other_uri) {
         if ($other_uri->getIoType() == self::IO_OBSERVE) {
           $any_observe = true;
           break;
         }
       }
 
       if ($any_observe) {
         return self::IO_READ;
       } else {
         return self::IO_READWRITE;
       }
     }
 
     return self::IO_NONE;
   }
 
 
   public function getDisplayURI() {
-    $uri = new PhutilURI($this->getURI());
+    return $this->getURIObject(false);
+  }
+
+  public function getNormalizedURI() {
+    $vcs = $this->getRepository()->getVersionControlSystem();
+
+    $map = array(
+      PhabricatorRepositoryType::REPOSITORY_TYPE_GIT =>
+        PhabricatorRepositoryURINormalizer::TYPE_GIT,
+      PhabricatorRepositoryType::REPOSITORY_TYPE_SVN =>
+        PhabricatorRepositoryURINormalizer::TYPE_SVN,
+      PhabricatorRepositoryType::REPOSITORY_TYPE_MERCURIAL =>
+        PhabricatorRepositoryURINormalizer::TYPE_MERCURIAL,
+    );
+
+    $type = $map[$vcs];
+    $display = (string)$this->getDisplayURI();
+
+    $normal_uri = new PhabricatorRepositoryURINormalizer($type, $display);
+
+    return $normal_uri->getNormalizedURI();
+  }
+
+  public function getEffectiveURI() {
+    return $this->getURIObject(true);
+  }
+
+  public function getURIEnvelope() {
+    $uri = $this->getEffectiveURI();
+
+    $command_engine = $this->newCommandEngine();
+
+    $is_http = $command_engine->isAnyHTTPProtocol();
+    // For SVN, we use `--username` and `--password` flags separately in the
+    // CommandEngine, so we don't need to add any credentials here.
+    $is_svn = $this->getRepository()->isSVN();
+    $credential_phid = $this->getCredentialPHID();
+
+    if ($is_http && !$is_svn && $credential_phid) {
+      $key = PassphrasePasswordKey::loadFromPHID(
+        $credential_phid,
+        PhabricatorUser::getOmnipotentUser());
+
+      $uri->setUser($key->getUsernameEnvelope()->openEnvelope());
+      $uri->setPass($key->getPasswordEnvelope()->openEnvelope());
+    }
+
+    return new PhutilOpaqueEnvelope((string)$uri);
+  }
+
+  private function getURIObject($normalize) {
+    // Users can provide Git/SCP-style URIs in the form "user@host:path".
+    // These are equivalent to "ssh://user@host/path". We use the more standard
+    // form internally, and convert to it if we need to specify a port number,
+    // but try to preserve what the user typed when displaying the URI.
+
+    if ($this->isBuiltin()) {
+      $builtin_protocol = $this->getForcedProtocol();
+      $builtin_domain = $this->getForcedHost();
+      $raw_uri = "{$builtin_protocol}://{$builtin_domain}";
+    } else {
+      $raw_uri = $this->getURI();
+    }
+
+    $port = $this->getForcedPort();
+
+    $default_ports = array(
+      'ssh' => 22,
+      'http' => 80,
+      'https' => 443,
+    );
+
+    $uri = new PhutilURI($raw_uri);
+
+    // Make sure to remove any password from the URI before we do anything
+    // with it; this should always be provided by the associated credential.
+    $uri->setPass(null);
+
+    if (!$uri->getProtocol()) {
+      $git_uri = new PhutilGitURI($raw_uri);
+
+      // We must normalize this Git-style URI into a normal URI
+      $must_normalize = ($port && ($port != $default_ports['ssh']));
+      if ($must_normalize || $normalize) {
+        $domain = $git_uri->getDomain();
 
-    $protocol = $this->getForcedProtocol();
-    if ($protocol) {
-      $uri->setProtocol($protocol);
+
+        $uri = id(new PhutilURI("ssh://{$domain}"))
+          ->setUser($git_uri->getUser())
+          ->setPath($git_uri->getPath());
+      } else {
+        $uri = $git_uri;
+      }
+    }
+
+    $is_normal = ($uri instanceof PhutilURI);
+
+    if ($is_normal) {
+      $protocol = $this->getForcedProtocol();
+      if ($protocol) {
+        $uri->setProtocol($protocol);
+      }
+
+      if ($port) {
+        $uri->setPort($port);
+      }
+
+      // Remove any explicitly set default ports.
+      $uri_port = $uri->getPort();
+      $uri_protocol = $uri->getProtocol();
+
+      $uri_default = idx($default_ports, $uri_protocol);
+      if ($uri_default && ($uri_default == $uri_port)) {
+        $uri->setPort(null);
+      }
     }
 
     $user = $this->getForcedUser();
     if ($user) {
       $uri->setUser($user);
     }
 
     $host = $this->getForcedHost();
     if ($host) {
       $uri->setDomain($host);
     }
 
-    $port = $this->getForcedPort();
-    if ($port) {
-      $uri->setPort($port);
-    }
-
     $path = $this->getForcedPath();
     if ($path) {
       $uri->setPath($path);
     }
 
     return $uri;
   }
 
+
   private function getForcedProtocol() {
+    $repository = $this->getRepository();
+
     switch ($this->getBuiltinProtocol()) {
       case self::BUILTIN_PROTOCOL_SSH:
-        return 'ssh';
+        if ($repository->isSVN()) {
+          return 'svn+ssh';
+        } else {
+          return 'ssh';
+        }
       case self::BUILTIN_PROTOCOL_HTTP:
         return 'http';
       case self::BUILTIN_PROTOCOL_HTTPS:
         return 'https';
       default:
         return null;
     }
   }
 
   private function getForcedUser() {
     switch ($this->getBuiltinProtocol()) {
       case self::BUILTIN_PROTOCOL_SSH:
         return AlmanacKeys::getClusterSSHUser();
       default:
         return null;
     }
   }
 
   private function getForcedHost() {
     $phabricator_uri = PhabricatorEnv::getURI('/');
     $phabricator_uri = new PhutilURI($phabricator_uri);
 
     $phabricator_host = $phabricator_uri->getDomain();
 
     switch ($this->getBuiltinProtocol()) {
       case self::BUILTIN_PROTOCOL_SSH:
         $ssh_host = PhabricatorEnv::getEnvConfig('diffusion.ssh-host');
         if ($ssh_host !== null) {
           return $ssh_host;
         }
         return $phabricator_host;
       case self::BUILTIN_PROTOCOL_HTTP:
       case self::BUILTIN_PROTOCOL_HTTPS:
         return $phabricator_host;
       default:
         return null;
     }
   }
 
   private function getForcedPort() {
     switch ($this->getBuiltinProtocol()) {
       case self::BUILTIN_PROTOCOL_SSH:
         return PhabricatorEnv::getEnvConfig('diffusion.ssh-port');
       case self::BUILTIN_PROTOCOL_HTTP:
       case self::BUILTIN_PROTOCOL_HTTPS:
       default:
         return null;
     }
   }
 
   private function getForcedPath() {
     if (!$this->isBuiltin()) {
       return null;
     }
 
     $repository = $this->getRepository();
 
     $id = $repository->getID();
     $callsign = $repository->getCallsign();
     $short_name = $repository->getRepositorySlug();
 
     $clone_name = $repository->getCloneName();
 
     if ($repository->isGit()) {
       $suffix = '.git';
     } else if ($repository->isHg()) {
       $suffix = '/';
     } else {
       $suffix = '';
+      $clone_name = '';
     }
 
     switch ($this->getBuiltinIdentifier()) {
       case self::BUILTIN_IDENTIFIER_ID:
         return "/diffusion/{$id}/{$clone_name}{$suffix}";
       case self::BUILTIN_IDENTIFIER_SHORTNAME:
         return "/source/{$short_name}{$suffix}";
       case self::BUILTIN_IDENTIFIER_CALLSIGN:
         return "/diffusion/{$callsign}/{$clone_name}{$suffix}";
       default:
         return null;
     }
   }
 
   public function getViewURI() {
     $id = $this->getID();
     return $this->getRepository()->getPathURI("uri/view/{$id}/");
   }
 
   public function getEditURI() {
     $id = $this->getID();
     return $this->getRepository()->getPathURI("uri/edit/{$id}/");
   }
 
   public function getAvailableIOTypeOptions() {
     $options = array(
       self::IO_DEFAULT,
       self::IO_NONE,
     );
 
     if ($this->isBuiltin()) {
       $options[] = self::IO_READ;
-      $options[] = self::IO_WRITE;
+      $options[] = self::IO_READWRITE;
     } else {
       $options[] = self::IO_OBSERVE;
       $options[] = self::IO_MIRROR;
     }
 
     $map = array();
     $io_map = self::getIOTypeMap();
     foreach ($options as $option) {
       $spec = idx($io_map, $option, array());
 
       $label = idx($spec, 'label', $option);
       $short = idx($spec, 'short');
 
       $name = pht('%s: %s', $label, $short);
       $map[$option] = $name;
     }
 
     return $map;
   }
 
   public function getAvailableDisplayTypeOptions() {
     $options = array(
       self::DISPLAY_DEFAULT,
       self::DISPLAY_ALWAYS,
       self::DISPLAY_NEVER,
     );
 
     $map = array();
     $display_map = self::getDisplayTypeMap();
     foreach ($options as $option) {
       $spec = idx($display_map, $option, array());
 
       $label = idx($spec, 'label', $option);
       $short = idx($spec, 'short');
 
       $name = pht('%s: %s', $label, $short);
       $map[$option] = $name;
     }
 
     return $map;
   }
 
   public static function getIOTypeMap() {
     return array(
       self::IO_DEFAULT => array(
         'label' => pht('Default'),
         'short' => pht('Use default behavior.'),
       ),
       self::IO_OBSERVE => array(
         'icon' => 'fa-download',
         'color' => 'green',
         'label' => pht('Observe'),
         'note' => pht(
           'Phabricator will observe changes to this URI and copy them.'),
         'short' => pht('Copy from a remote.'),
       ),
       self::IO_MIRROR => array(
         'icon' => 'fa-upload',
         'color' => 'green',
         'label' => pht('Mirror'),
         'note' => pht(
           'Phabricator will push a copy of any changes to this URI.'),
         'short' => pht('Push a copy to a remote.'),
       ),
       self::IO_NONE => array(
         'icon' => 'fa-times',
         'color' => 'grey',
         'label' => pht('No I/O'),
         'note' => pht(
           'Phabricator will not push or pull any changes to this URI.'),
         'short' => pht('Do not perform any I/O.'),
       ),
       self::IO_READ => array(
         'icon' => 'fa-folder',
         'color' => 'blue',
         'label' => pht('Read Only'),
         'note' => pht(
           'Phabricator will serve a read-only copy of the repository from '.
           'this URI.'),
         'short' => pht('Serve repository in read-only mode.'),
       ),
       self::IO_READWRITE => array(
         'icon' => 'fa-folder-open',
         'color' => 'blue',
         'label' => pht('Read/Write'),
         'note' => pht(
           'Phabricator will serve a read/write copy of the repository from '.
           'this URI.'),
         'short' => pht('Serve repository in read/write mode.'),
       ),
     );
   }
 
   public static function getDisplayTypeMap() {
     return array(
       self::DISPLAY_DEFAULT => array(
         'label' => pht('Default'),
         'short' => pht('Use default behavior.'),
       ),
       self::DISPLAY_ALWAYS => array(
         'icon' => 'fa-eye',
         'color' => 'green',
         'label' => pht('Visible'),
         'note' => pht('This URI will be shown to users as a clone URI.'),
         'short' => pht('Show as a clone URI.'),
       ),
       self::DISPLAY_NEVER => array(
         'icon' => 'fa-eye-slash',
         'color' => 'grey',
         'label' => pht('Hidden'),
         'note' => pht(
           'This URI will be hidden from users.'),
         'short' => pht('Do not show as a clone URI.'),
       ),
     );
   }
 
+  public function newCommandEngine() {
+    $repository = $this->getRepository();
+    $protocol = $this->getEffectiveURI()->getProtocol();
+
+    return DiffusionCommandEngine::newCommandEngine($repository)
+      ->setCredentialPHID($this->getCredentialPHID())
+      ->setProtocol($protocol);
+  }
+
+  public function getURIScore() {
+    $score = 0;
+
+    $io_points = array(
+      self::IO_READWRITE => 20,
+      self::IO_READ => 10,
+    );
+    $score += idx($io_points, $this->getEffectiveIoType(), 0);
+
+    $protocol_points = array(
+      self::BUILTIN_PROTOCOL_SSH => 3,
+      self::BUILTIN_PROTOCOL_HTTPS => 2,
+      self::BUILTIN_PROTOCOL_HTTP => 1,
+    );
+    $score += idx($protocol_points, $this->getBuiltinProtocol(), 0);
+
+    return $score;
+  }
+
+
 
 /* -(  PhabricatorApplicationTransactionInterface  )------------------------- */
 
 
   public function getApplicationTransactionEditor() {
     return new DiffusionURIEditor();
   }
 
   public function getApplicationTransactionObject() {
     return $this;
   }
 
   public function getApplicationTransactionTemplate() {
     return new PhabricatorRepositoryURITransaction();
   }
 
   public function willRenderTimeline(
     PhabricatorApplicationTransactionView $timeline,
     AphrontRequest $request) {
     return $timeline;
   }
 
 
 /* -(  PhabricatorPolicyInterface  )----------------------------------------- */
 
 
   public function getCapabilities() {
     return array(
       PhabricatorPolicyCapability::CAN_VIEW,
       PhabricatorPolicyCapability::CAN_EDIT,
     );
   }
 
   public function getPolicy($capability) {
     switch ($capability) {
       case PhabricatorPolicyCapability::CAN_VIEW:
       case PhabricatorPolicyCapability::CAN_EDIT:
         return PhabricatorPolicies::getMostOpenPolicy();
     }
   }
 
   public function hasAutomaticCapability($capability, PhabricatorUser $viewer) {
     return false;
   }
 
   public function describeAutomaticCapability($capability) {
     return null;
   }
 
 
 /* -(  PhabricatorExtendedPolicyInterface  )--------------------------------- */
 
 
   public function getExtendedPolicy($capability, PhabricatorUser $viewer) {
     $extended = array();
 
     switch ($capability) {
       case PhabricatorPolicyCapability::CAN_EDIT:
         // To edit a repository URI, you must be able to edit the
         // corresponding repository.
         $extended[] = array($this->getRepository(), $capability);
         break;
     }
 
     return $extended;
   }
 
 
 /* -(  PhabricatorConduitResultInterface  )---------------------------------- */
 
 
   public function getFieldSpecificationsForConduit() {
     return array(
       id(new PhabricatorConduitSearchFieldSpecification())
         ->setKey('repositoryPHID')
         ->setType('phid')
         ->setDescription(pht('The associated repository PHID.')),
       id(new PhabricatorConduitSearchFieldSpecification())
         ->setKey('uri')
         ->setType('map<string, string>')
         ->setDescription(pht('The raw and effective URI.')),
       id(new PhabricatorConduitSearchFieldSpecification())
         ->setKey('io')
         ->setType('map<string, const>')
         ->setDescription(
           pht('The raw, default, and effective I/O Type settings.')),
       id(new PhabricatorConduitSearchFieldSpecification())
         ->setKey('display')
         ->setType('map<string, const>')
         ->setDescription(
           pht('The raw, default, and effective Display Type settings.')),
       id(new PhabricatorConduitSearchFieldSpecification())
         ->setKey('credentialPHID')
         ->setType('phid?')
         ->setDescription(
           pht('The associated credential PHID, if one exists.')),
       id(new PhabricatorConduitSearchFieldSpecification())
         ->setKey('disabled')
         ->setType('bool')
         ->setDescription(pht('True if the URI is disabled.')),
       id(new PhabricatorConduitSearchFieldSpecification())
         ->setKey('builtin')
         ->setType('map<string, string>')
         ->setDescription(
           pht('Information about builtin URIs.')),
       id(new PhabricatorConduitSearchFieldSpecification())
         ->setKey('dateCreated')
         ->setType('int')
         ->setDescription(
           pht('Epoch timestamp when the object was created.')),
       id(new PhabricatorConduitSearchFieldSpecification())
         ->setKey('dateModified')
         ->setType('int')
         ->setDescription(
           pht('Epoch timestamp when the object was last updated.')),
     );
   }
 
   public function getFieldValuesForConduit() {
     return array(
       'repositoryPHID' => $this->getRepositoryPHID(),
       'uri' => array(
         'raw' => $this->getURI(),
-        'effective' => (string)$this->getDisplayURI(),
+        'display' => (string)$this->getDisplayURI(),
+        'effective' => (string)$this->getEffectiveURI(),
+        'normalized' => (string)$this->getNormalizedURI(),
       ),
       'io' => array(
         'raw' => $this->getIOType(),
         'default' => $this->getDefaultIOType(),
         'effective' => $this->getEffectiveIOType(),
       ),
       'display' => array(
         'raw' => $this->getDisplayType(),
         'default' => $this->getDefaultDisplayType(),
         'effective' => $this->getEffectiveDisplayType(),
       ),
       'credentialPHID' => $this->getCredentialPHID(),
       'disabled' => (bool)$this->getIsDisabled(),
       'builtin' => array(
         'protocol' => $this->getBuiltinProtocol(),
         'identifier' => $this->getBuiltinIdentifier(),
       ),
       'dateCreated' => $this->getDateCreated(),
       'dateModified' => $this->getDateModified(),
     );
   }
 
   public function getConduitSearchAttachments() {
     return array();
   }
 
 }
diff --git a/src/applications/repository/storage/PhabricatorRepositoryURITransaction.php b/src/applications/repository/storage/PhabricatorRepositoryURITransaction.php
index 034aa48d7..241a95dad 100644
--- a/src/applications/repository/storage/PhabricatorRepositoryURITransaction.php
+++ b/src/applications/repository/storage/PhabricatorRepositoryURITransaction.php
@@ -1,70 +1,108 @@
 <?php
 
 final class PhabricatorRepositoryURITransaction
   extends PhabricatorApplicationTransaction {
 
   const TYPE_REPOSITORY = 'diffusion.uri.repository';
   const TYPE_URI = 'diffusion.uri.uri';
   const TYPE_IO = 'diffusion.uri.io';
   const TYPE_DISPLAY = 'diffusion.uri.display';
   const TYPE_CREDENTIAL = 'diffusion.uri.credential';
   const TYPE_DISABLE = 'diffusion.uri.disable';
 
   public function getApplicationName() {
     return 'repository';
   }
 
   public function getApplicationTransactionType() {
     return PhabricatorRepositoryURIPHIDType::TYPECONST;
   }
 
+  public function getRequiredHandlePHIDs() {
+    $phids = parent::getRequiredHandlePHIDs();
+
+    $old = $this->getOldValue();
+    $new = $this->getNewValue();
+
+    switch ($this->getTransactionType()) {
+      case self::TYPE_CREDENTIAL:
+        if ($old) {
+          $phids[] = $old;
+        }
+        if ($new) {
+          $phids[] = $new;
+        }
+        break;
+    }
+
+    return $phids;
+  }
+
   public function getTitle() {
     $author_phid = $this->getAuthorPHID();
 
     $old = $this->getOldValue();
     $new = $this->getNewValue();
 
     switch ($this->getTransactionType()) {
       case self::TYPE_URI:
         return pht(
           '%s changed this URI from "%s" to "%s".',
           $this->renderHandleLink($author_phid),
           $old,
           $new);
       case self::TYPE_IO:
         $map = PhabricatorRepositoryURI::getIOTypeMap();
         $old_label = idx(idx($map, $old, array()), 'label', $old);
         $new_label = idx(idx($map, $new, array()), 'label', $new);
 
         return pht(
           '%s changed the display type for this URI from "%s" to "%s".',
           $this->renderHandleLink($author_phid),
           $old_label,
           $new_label);
       case self::TYPE_DISPLAY:
         $map = PhabricatorRepositoryURI::getDisplayTypeMap();
         $old_label = idx(idx($map, $old, array()), 'label', $old);
         $new_label = idx(idx($map, $new, array()), 'label', $new);
 
         return pht(
           '%s changed the display type for this URI from "%s" to "%s".',
           $this->renderHandleLink($author_phid),
           $old_label,
           $new_label);
       case self::TYPE_DISABLE:
         if ($new) {
           return pht(
             '%s disabled this URI.',
             $this->renderHandleLink($author_phid));
         } else {
           return pht(
             '%s enabled this URI.',
             $this->renderHandleLink($author_phid));
         }
+      case self::TYPE_CREDENTIAL:
+        if ($old && $new) {
+          return pht(
+            '%s changed the credential for this URI from %s to %s.',
+            $this->renderHandleLink($author_phid),
+            $this->renderHandleLink($old),
+            $this->renderHandleLink($new));
+        } else if ($old) {
+          return pht(
+            '%s removed %s as the credential for this URI.',
+            $this->renderHandleLink($author_phid),
+            $this->renderHandleLink($old));
+        } else if ($new) {
+          return pht(
+            '%s set the credential for this URI to %s.',
+            $this->renderHandleLink($author_phid),
+            $this->renderHandleLink($new));
+        }
 
     }
 
     return parent::getTitle();
   }
 
 }
diff --git a/src/applications/repository/storage/__tests__/PhabricatorRepositoryTestCase.php b/src/applications/repository/storage/__tests__/PhabricatorRepositoryTestCase.php
index c6d227826..63bae03d8 100644
--- a/src/applications/repository/storage/__tests__/PhabricatorRepositoryTestCase.php
+++ b/src/applications/repository/storage/__tests__/PhabricatorRepositoryTestCase.php
@@ -1,220 +1,220 @@
 <?php
 
 final class PhabricatorRepositoryTestCase
   extends PhabricatorTestCase {
 
   public function testRepositoryURIProtocols() {
     $tests = array(
       '/path/to/repo'               => 'file',
       'file:///path/to/repo'        => 'file',
       'ssh://user@domain.com/path'  => 'ssh',
       'git@example.com:path'        => 'ssh',
       'git://git@example.com/path'  => 'git',
       'svn+ssh://example.com/path'  => 'svn+ssh',
       'https://example.com/repo/'   => 'https',
       'http://example.com/'         => 'http',
       'https://user@example.com/'   => 'https',
     );
 
     foreach ($tests as $uri => $expect) {
       $repository = new PhabricatorRepository();
       $repository->setDetail('remote-uri', $uri);
 
       $this->assertEqual(
         $expect,
         $repository->getRemoteProtocol(),
         pht("Protocol for '%s'.", $uri));
     }
   }
 
   public function testBranchFilter() {
     $git = PhabricatorRepositoryType::REPOSITORY_TYPE_GIT;
 
     $repo = new PhabricatorRepository();
     $repo->setVersionControlSystem($git);
 
     $this->assertTrue(
       $repo->shouldTrackBranch('imaginary'),
       pht('Track all branches by default.'));
 
     $repo->setDetail(
       'branch-filter',
       array(
         'master' => true,
       ));
 
     $this->assertTrue(
       $repo->shouldTrackBranch('master'),
       pht('Track listed branches.'));
 
     $this->assertFalse(
       $repo->shouldTrackBranch('imaginary'),
       pht('Do not track unlisted branches.'));
   }
 
   public function testSubversionPathInfo() {
     $svn = PhabricatorRepositoryType::REPOSITORY_TYPE_SVN;
 
     $repo = new PhabricatorRepository();
     $repo->setVersionControlSystem($svn);
 
     $repo->setDetail('remote-uri', 'http://svn.example.com/repo');
     $this->assertEqual(
       'http://svn.example.com/repo',
       $repo->getSubversionPathURI());
 
     $repo->setDetail('remote-uri', 'http://svn.example.com/repo/');
     $this->assertEqual(
       'http://svn.example.com/repo',
       $repo->getSubversionPathURI());
 
     $repo->setDetail('hosting-enabled', true);
 
-    $repo->setDetail('local-path', '/var/repo/SVN');
+    $repo->setLocalPath('/var/repo/SVN');
     $this->assertEqual(
       'file:///var/repo/SVN',
       $repo->getSubversionPathURI());
 
-    $repo->setDetail('local-path', '/var/repo/SVN/');
+    $repo->setLocalPath('/var/repo/SVN/');
     $this->assertEqual(
       'file:///var/repo/SVN',
       $repo->getSubversionPathURI());
 
     $this->assertEqual(
       'file:///var/repo/SVN/a@',
       $repo->getSubversionPathURI('a'));
 
     $this->assertEqual(
       'file:///var/repo/SVN/a@1',
       $repo->getSubversionPathURI('a', 1));
 
     $this->assertEqual(
       'file:///var/repo/SVN/%3F@22',
       $repo->getSubversionPathURI('?', 22));
 
     $repo->setDetail('svn-subpath', 'quack/trunk/');
 
     $this->assertEqual(
       'file:///var/repo/SVN/quack/trunk/@',
       $repo->getSubversionBaseURI());
 
     $this->assertEqual(
       'file:///var/repo/SVN/quack/trunk/@HEAD',
       $repo->getSubversionBaseURI('HEAD'));
 
   }
 
   public function testFilterMercurialDebugOutput() {
     $map = array(
       '' => '',
 
       "quack\n" => "quack\n",
 
       "ignoring untrusted configuration option x.y = z\nquack\n" =>
         "quack\n",
 
       "ignoring untrusted configuration option x.y = z\n".
       "ignoring untrusted configuration option x.y = z\n".
       "quack\n" =>
         "quack\n",
 
       "ignoring untrusted configuration option x.y = z\n".
       "ignoring untrusted configuration option x.y = z\n".
       "ignoring untrusted configuration option x.y = z\n".
       "quack\n" =>
         "quack\n",
 
       "quack\n".
       "ignoring untrusted configuration option x.y = z\n".
       "ignoring untrusted configuration option x.y = z\n".
       "ignoring untrusted configuration option x.y = z\n" =>
         "quack\n",
 
       "ignoring untrusted configuration option x.y = z\n".
       "ignoring untrusted configuration option x.y = z\n".
       "duck\n".
       "ignoring untrusted configuration option x.y = z\n".
       "ignoring untrusted configuration option x.y = z\n".
       "bread\n".
       "ignoring untrusted configuration option x.y = z\n".
       "quack\n" =>
         "duck\nbread\nquack\n",
 
       "ignoring untrusted configuration option x.y = z\n".
       "duckignoring untrusted configuration option x.y = z\n".
       "quack" =>
         'duckquack',
     );
 
     foreach ($map as $input => $expect) {
       $actual = DiffusionMercurialCommandEngine::filterMercurialDebugOutput(
         $input);
       $this->assertEqual($expect, $actual, $input);
     }
   }
 
   public function testRepositoryShortNameValidation() {
     $good = array(
       'sensible-repository',
       'AReasonableName',
       'ACRONYM-project',
       'sol-123',
       '46-helixes',
       'node.io',
       'internet.com',
       'www.internet-site.com.repository',
       'with_under-scores',
 
       // Can't win them all.
       'A-_._-_._-_._-_._-_._-_._-1',
 
       // 64-character names are fine.
       str_repeat('a', 64),
     );
 
     $poor = array(
       '',
       '1',
       '.',
       '-_-',
       'AAAA',
       '..',
       'a/b',
       '../../etc/passwd',
       '/',
       '!',
       '@',
       'ca$hmoney',
       'repo with spaces',
       'hyphen-',
       '-ated',
       '_underscores_',
       'yes!',
 
       // 65-character names are no good.
       str_repeat('a', 65),
     );
 
     foreach ($good as $nice_name) {
       $actual = PhabricatorRepository::isValidRepositorySlug($nice_name);
       $this->assertEqual(
         true,
         $actual,
         pht(
           'Expected "%s" to be a valid repository short name.',
           $nice_name));
     }
 
     foreach ($poor as $poor_name) {
       $actual = PhabricatorRepository::isValidRepositorySlug($poor_name);
       $this->assertEqual(
         false,
         $actual,
         pht(
           'Expected "%s" to be rejected as an invalid repository '.
           'short name.',
           $poor_name));
     }
   }
 
 }
diff --git a/src/applications/repository/worker/PhabricatorRepositoryCommitOwnersWorker.php b/src/applications/repository/worker/PhabricatorRepositoryCommitOwnersWorker.php
index 5756c4e88..61a615950 100644
--- a/src/applications/repository/worker/PhabricatorRepositoryCommitOwnersWorker.php
+++ b/src/applications/repository/worker/PhabricatorRepositoryCommitOwnersWorker.php
@@ -1,139 +1,144 @@
 <?php
 
 final class PhabricatorRepositoryCommitOwnersWorker
   extends PhabricatorRepositoryCommitParserWorker {
 
   protected function parseCommit(
     PhabricatorRepository $repository,
     PhabricatorRepositoryCommit $commit) {
 
     $this->triggerOwnerAudits($repository, $commit);
 
     $commit->writeImportStatusFlag(
       PhabricatorRepositoryCommit::IMPORTED_OWNERS);
 
     if ($this->shouldQueueFollowupTasks()) {
       $this->queueTask(
         'PhabricatorRepositoryCommitHeraldWorker',
         array(
           'commitID' => $commit->getID(),
         ));
     }
   }
 
   private function triggerOwnerAudits(
     PhabricatorRepository $repository,
     PhabricatorRepositoryCommit $commit) {
 
     if (!$repository->shouldPublish()) {
       return;
     }
 
     $affected_paths = PhabricatorOwnerPathQuery::loadAffectedPaths(
       $repository,
       $commit,
       PhabricatorUser::getOmnipotentUser());
     $affected_packages = PhabricatorOwnersPackage::loadAffectedPackages(
       $repository,
       $affected_paths);
 
     if ($affected_packages) {
       $requests = id(new PhabricatorRepositoryAuditRequest())
         ->loadAllWhere(
           'commitPHID = %s',
           $commit->getPHID());
       $requests = mpull($requests, null, 'getAuditorPHID');
 
       foreach ($affected_packages as $package) {
         $request = idx($requests, $package->getPHID());
         if ($request) {
           // Don't update request if it exists already.
           continue;
         }
 
+        if ($package->isArchived()) {
+          // Don't trigger audits if the package is archived.
+          continue;
+        }
+
         if ($package->getAuditingEnabled()) {
           $reasons = $this->checkAuditReasons($commit, $package);
           if ($reasons) {
             $audit_status =
               PhabricatorAuditStatusConstants::AUDIT_REQUIRED;
           } else {
             $audit_status =
               PhabricatorAuditStatusConstants::AUDIT_NOT_REQUIRED;
           }
         } else {
           $reasons = array();
           $audit_status = PhabricatorAuditStatusConstants::NONE;
         }
 
         $relationship = new PhabricatorRepositoryAuditRequest();
         $relationship->setAuditorPHID($package->getPHID());
         $relationship->setCommitPHID($commit->getPHID());
         $relationship->setAuditReasons($reasons);
         $relationship->setAuditStatus($audit_status);
 
         $relationship->save();
 
         $requests[$package->getPHID()] = $relationship;
       }
 
       $commit->updateAuditStatus($requests);
       $commit->save();
     }
   }
 
   private function checkAuditReasons(
     PhabricatorRepositoryCommit $commit,
     PhabricatorOwnersPackage $package) {
 
     $data = id(new PhabricatorRepositoryCommitData())->loadOneWhere(
       'commitID = %d',
       $commit->getID());
 
     $reasons = array();
 
     if ($data->getCommitDetail('vsDiff')) {
       $reasons[] = pht('Changed After Revision Was Accepted');
     }
 
     $commit_author_phid = $data->getCommitDetail('authorPHID');
     if (!$commit_author_phid) {
       $reasons[] = pht('Commit Author Not Recognized');
     }
 
     $revision_id = $data->getCommitDetail('differential.revisionID');
 
     $revision_author_phid = null;
     $commit_reviewedby_phid = null;
 
     if ($revision_id) {
       $revision = id(new DifferentialRevisionQuery())
         ->setViewer(PhabricatorUser::getOmnipotentUser())
         ->withIDs(array($revision_id))
         ->executeOne();
       if ($revision) {
         $revision_author_phid = $revision->getAuthorPHID();
         $commit_reviewedby_phid = $data->getCommitDetail('reviewerPHID');
         if ($revision_author_phid !== $commit_author_phid) {
           $reasons[] = pht('Author Not Matching with Revision');
         }
       } else {
         $reasons[] = pht('Revision Not Found');
       }
 
     } else {
       $reasons[] = pht('No Revision Specified');
     }
 
     $owners_phids = PhabricatorOwnersOwner::loadAffiliatedUserPHIDs(
       array($package->getID()));
 
     if (!($commit_author_phid && in_array($commit_author_phid, $owners_phids) ||
         $commit_reviewedby_phid && in_array($commit_reviewedby_phid,
           $owners_phids))) {
       $reasons[] = pht('Owners Not Involved');
     }
 
     return $reasons;
   }
 
 }
diff --git a/src/applications/settings/storage/PhabricatorUserPreferences.php b/src/applications/settings/storage/PhabricatorUserPreferences.php
index 271fd1afb..d8c4982cc 100644
--- a/src/applications/settings/storage/PhabricatorUserPreferences.php
+++ b/src/applications/settings/storage/PhabricatorUserPreferences.php
@@ -1,119 +1,119 @@
 <?php
 
 final class PhabricatorUserPreferences extends PhabricatorUserDAO {
 
   const PREFERENCE_MONOSPACED           = 'monospaced';
   const PREFERENCE_DARK_CONSOLE         = 'dark_console';
   const PREFERENCE_EDITOR               = 'editor';
   const PREFERENCE_MULTIEDIT            = 'multiedit';
   const PREFERENCE_TITLES               = 'titles';
   const PREFERENCE_MONOSPACED_TEXTAREAS = 'monospaced-textareas';
   const PREFERENCE_DATE_FORMAT          = 'date-format';
   const PREFERENCE_TIME_FORMAT          = 'time-format';
   const PREFERENCE_WEEK_START_DAY       = 'week-start-day';
 
   const PREFERENCE_RE_PREFIX            = 're-prefix';
   const PREFERENCE_NO_SELF_MAIL         = 'self-mail';
   const PREFERENCE_NO_MAIL              = 'no-mail';
   const PREFERENCE_MAILTAGS             = 'mailtags';
   const PREFERENCE_VARY_SUBJECT         = 'vary-subject';
   const PREFERENCE_HTML_EMAILS          = 'html-emails';
 
   const PREFERENCE_SEARCHBAR_JUMP       = 'searchbar-jump';
   const PREFERENCE_SEARCH_SHORTCUT      = 'search-shortcut';
   const PREFERENCE_SEARCH_SCOPE         = 'search-scope';
 
   const PREFERENCE_DIFFUSION_BLAME      = 'diffusion-blame';
   const PREFERENCE_DIFFUSION_COLOR      = 'diffusion-color';
 
   const PREFERENCE_NAV_COLLAPSED        = 'nav-collapsed';
   const PREFERENCE_NAV_WIDTH            = 'nav-width';
   const PREFERENCE_APP_TILES            = 'app-tiles';
   const PREFERENCE_APP_PINNED           = 'app-pinned';
 
   const PREFERENCE_DIFF_UNIFIED         = 'diff-unified';
   const PREFERENCE_DIFF_FILETREE        = 'diff-filetree';
   const PREFERENCE_DIFF_GHOSTS          = 'diff-ghosts';
 
   const PREFERENCE_CONPH_NOTIFICATIONS = 'conph-notifications';
   const PREFERENCE_CONPHERENCE_COLUMN  = 'conpherence-column';
 
   const PREFERENCE_RESOURCE_POSTPROCESSOR = 'resource-postprocessor';
   const PREFERENCE_DESKTOP_NOTIFICATIONS = 'desktop-notifications';
 
   const PREFERENCE_PROFILE_MENU_COLLAPSED = 'profile-menu.collapsed';
   const PREFERENCE_FAVORITE_POLICIES = 'policy.favorites';
 
   // These are in an unusual order for historic reasons.
   const MAILTAG_PREFERENCE_NOTIFY       = 0;
   const MAILTAG_PREFERENCE_EMAIL        = 1;
   const MAILTAG_PREFERENCE_IGNORE       = 2;
 
   protected $userPHID;
   protected $preferences = array();
 
   protected function getConfiguration() {
     return array(
       self::CONFIG_SERIALIZATION => array(
         'preferences' => self::SERIALIZATION_JSON,
       ),
       self::CONFIG_TIMESTAMPS => false,
       self::CONFIG_KEY_SCHEMA => array(
         'userPHID' => array(
           'columns' => array('userPHID'),
           'unique' => true,
         ),
       ),
     ) + parent::getConfiguration();
   }
 
   public function getPreference($key, $default = null) {
     return idx($this->preferences, $key, $default);
   }
 
   public function setPreference($key, $value) {
     $this->preferences[$key] = $value;
     return $this;
   }
 
   public function unsetPreference($key) {
     unset($this->preferences[$key]);
     return $this;
   }
 
   public function getPinnedApplications(array $apps, PhabricatorUser $viewer) {
     $pref_pinned = self::PREFERENCE_APP_PINNED;
     $pinned = $this->getPreference($pref_pinned);
 
     if ($pinned) {
       return $pinned;
     }
 
     $pref_tiles = self::PREFERENCE_APP_TILES;
     $tiles = $this->getPreference($pref_tiles, array());
     $full_tile = 'full';
 
     $large = array();
     foreach ($apps as $app) {
       $show = $app->isPinnedByDefault($viewer);
 
       // TODO: This is legacy stuff, clean it up eventually. This approximately
       // retains the old "tiles" preference.
       if (isset($tiles[get_class($app)])) {
         $show = ($tiles[get_class($app)] == $full_tile);
       }
 
       if ($show) {
         $large[] = get_class($app);
       }
     }
 
     return $large;
   }
 
   public static function filterMonospacedCSSRule($monospaced) {
     // Prevent the user from doing dangerous things.
-    return preg_replace('/[^a-z0-9 ,".]+/i', '', $monospaced);
+    return preg_replace('([^a-z0-9 ,"./]+)i', '', $monospaced);
   }
 
 }
diff --git a/src/applications/transactions/editengine/PhabricatorEditEngine.php b/src/applications/transactions/editengine/PhabricatorEditEngine.php
index 7c9c0fbed..1717b2cc7 100644
--- a/src/applications/transactions/editengine/PhabricatorEditEngine.php
+++ b/src/applications/transactions/editengine/PhabricatorEditEngine.php
@@ -1,1989 +1,2117 @@
 <?php
 
 
 /**
  * @task fields Managing Fields
  * @task text Display Text
  * @task config Edit Engine Configuration
  * @task uri Managing URIs
  * @task load Creating and Loading Objects
  * @task web Responding to Web Requests
  * @task edit Responding to Edit Requests
  * @task http Responding to HTTP Parameter Requests
  * @task conduit Responding to Conduit Requests
  */
 abstract class PhabricatorEditEngine
   extends Phobject
   implements PhabricatorPolicyInterface {
 
   const EDITENGINECONFIG_DEFAULT = 'default';
 
   private $viewer;
   private $controller;
   private $isCreate;
   private $editEngineConfiguration;
   private $contextParameters = array();
   private $targetObject;
+  private $page;
+  private $pages;
 
   final public function setViewer(PhabricatorUser $viewer) {
     $this->viewer = $viewer;
     return $this;
   }
 
   final public function getViewer() {
     return $this->viewer;
   }
 
   final public function setController(PhabricatorController $controller) {
     $this->controller = $controller;
     $this->setViewer($controller->getViewer());
     return $this;
   }
 
   final public function getController() {
     return $this->controller;
   }
 
   final public function getEngineKey() {
     return $this->getPhobjectClassConstant('ENGINECONST', 64);
   }
 
   final public function getApplication() {
     $app_class = $this->getEngineApplicationClass();
     return PhabricatorApplication::getByClass($app_class);
   }
 
   final public function addContextParameter($key) {
     $this->contextParameters[] = $key;
     return $this;
   }
 
   public function isEngineConfigurable() {
     return true;
   }
 
   public function isEngineExtensible() {
     return true;
   }
 
   /**
    * Force the engine to edit a particular object.
    */
   public function setTargetObject($target_object) {
     $this->targetObject = $target_object;
     return $this;
   }
 
   public function getTargetObject() {
     return $this->targetObject;
   }
 
 
 /* -(  Managing Fields  )---------------------------------------------------- */
 
 
   abstract public function getEngineApplicationClass();
   abstract protected function buildCustomEditFields($object);
 
   public function getFieldsForConfig(
     PhabricatorEditEngineConfiguration $config) {
 
     $object = $this->newEditableObject();
 
     $this->editEngineConfiguration = $config;
 
     // This is mostly making sure that we fill in default values.
     $this->setIsCreate(true);
 
     return $this->buildEditFields($object);
   }
 
   final protected function buildEditFields($object) {
     $viewer = $this->getViewer();
 
     $fields = $this->buildCustomEditFields($object);
 
     foreach ($fields as $field) {
       $field
         ->setViewer($viewer)
         ->setObject($object);
     }
 
     $fields = mpull($fields, null, 'getKey');
 
     if ($this->isEngineExtensible()) {
       $extensions = PhabricatorEditEngineExtension::getAllEnabledExtensions();
     } else {
       $extensions = array();
     }
 
     foreach ($extensions as $extension) {
       $extension->setViewer($viewer);
 
       if (!$extension->supportsObject($this, $object)) {
         continue;
       }
 
       $extension_fields = $extension->buildCustomEditFields($this, $object);
 
       // TODO: Validate this in more detail with a more tailored error.
       assert_instances_of($extension_fields, 'PhabricatorEditField');
 
       foreach ($extension_fields as $field) {
         $field
           ->setViewer($viewer)
           ->setObject($object);
       }
 
       $extension_fields = mpull($extension_fields, null, 'getKey');
 
       foreach ($extension_fields as $key => $field) {
         $fields[$key] = $field;
       }
     }
 
     $config = $this->getEditEngineConfiguration();
     $fields = $this->willConfigureFields($object, $fields);
     $fields = $config->applyConfigurationToFields($this, $object, $fields);
 
+    $fields = $this->applyPageToFields($object, $fields);
+
     return $fields;
   }
 
   protected function willConfigureFields($object, array $fields) {
     return $fields;
   }
 
 
 /* -(  Display Text  )------------------------------------------------------- */
 
 
   /**
    * @task text
    */
   abstract public function getEngineName();
 
 
   /**
    * @task text
    */
   abstract protected function getObjectCreateTitleText($object);
 
   /**
    * @task text
    */
   protected function getFormHeaderText($object) {
     $config = $this->getEditEngineConfiguration();
     return $config->getName();
   }
 
   /**
    * @task text
    */
   abstract protected function getObjectEditTitleText($object);
 
 
   /**
    * @task text
    */
   abstract protected function getObjectCreateShortText();
 
 
   /**
    * @task text
    */
   abstract protected function getObjectName();
 
 
   /**
    * @task text
    */
   abstract protected function getObjectEditShortText($object);
 
 
   /**
    * @task text
    */
   protected function getObjectCreateButtonText($object) {
     return $this->getObjectCreateTitleText($object);
   }
 
 
   /**
    * @task text
    */
   protected function getObjectEditButtonText($object) {
     return pht('Save Changes');
   }
 
 
   /**
    * @task text
    */
   protected function getCommentViewSeriousHeaderText($object) {
     return pht('Take Action');
   }
 
 
   /**
    * @task text
    */
   protected function getCommentViewSeriousButtonText($object) {
     return pht('Submit');
   }
 
 
   /**
    * @task text
    */
   protected function getCommentViewHeaderText($object) {
     return $this->getCommentViewSeriousHeaderText($object);
   }
 
 
   /**
    * @task text
    */
   protected function getCommentViewButtonText($object) {
     return $this->getCommentViewSeriousButtonText($object);
   }
 
 
   /**
    * @task text
    */
   protected function getQuickCreateMenuHeaderText() {
     return $this->getObjectCreateShortText();
   }
 
 
   /**
    * Return a human-readable header describing what this engine is used to do,
    * like "Configure Maniphest Task Forms".
    *
    * @return string Human-readable description of the engine.
    * @task text
    */
   abstract public function getSummaryHeader();
 
 
   /**
    * Return a human-readable summary of what this engine is used to do.
    *
    * @return string Human-readable description of the engine.
    * @task text
    */
   abstract public function getSummaryText();
 
 
 
 
 /* -(  Edit Engine Configuration  )------------------------------------------ */
 
 
   protected function supportsEditEngineConfiguration() {
     return true;
   }
 
   final protected function getEditEngineConfiguration() {
     return $this->editEngineConfiguration;
   }
 
   private function newConfigurationQuery() {
     return id(new PhabricatorEditEngineConfigurationQuery())
       ->setViewer($this->getViewer())
       ->withEngineKeys(array($this->getEngineKey()));
   }
 
   private function loadEditEngineConfigurationWithQuery(
     PhabricatorEditEngineConfigurationQuery $query,
     $sort_method) {
 
     if ($sort_method) {
       $results = $query->execute();
       $results = msort($results, $sort_method);
       $result = head($results);
     } else {
       $result = $query->executeOne();
     }
 
     if (!$result) {
       return null;
     }
 
     $this->editEngineConfiguration = $result;
     return $result;
   }
 
   private function loadEditEngineConfigurationWithIdentifier($identifier) {
     $query = $this->newConfigurationQuery()
       ->withIdentifiers(array($identifier));
 
     return $this->loadEditEngineConfigurationWithQuery($query, null);
   }
 
   private function loadDefaultConfiguration() {
     $query = $this->newConfigurationQuery()
       ->withIdentifiers(
         array(
           self::EDITENGINECONFIG_DEFAULT,
         ))
       ->withIgnoreDatabaseConfigurations(true);
 
     return $this->loadEditEngineConfigurationWithQuery($query, null);
   }
 
   private function loadDefaultCreateConfiguration() {
     $query = $this->newConfigurationQuery()
       ->withIsDefault(true)
       ->withIsDisabled(false);
 
     return $this->loadEditEngineConfigurationWithQuery(
       $query,
       'getCreateSortKey');
   }
 
   public function loadDefaultEditConfiguration() {
     $query = $this->newConfigurationQuery()
       ->withIsEdit(true)
       ->withIsDisabled(false);
 
     return $this->loadEditEngineConfigurationWithQuery(
       $query,
       'getEditSortKey');
   }
 
   final public function getBuiltinEngineConfigurations() {
     $configurations = $this->newBuiltinEngineConfigurations();
 
     if (!$configurations) {
       throw new Exception(
         pht(
           'EditEngine ("%s") returned no builtin engine configurations, but '.
           'an edit engine must have at least one configuration.',
           get_class($this)));
     }
 
     assert_instances_of($configurations, 'PhabricatorEditEngineConfiguration');
 
     $has_default = false;
     foreach ($configurations as $config) {
       if ($config->getBuiltinKey() == self::EDITENGINECONFIG_DEFAULT) {
         $has_default = true;
       }
     }
 
     if (!$has_default) {
       $first = head($configurations);
       if (!$first->getBuiltinKey()) {
         $first
           ->setBuiltinKey(self::EDITENGINECONFIG_DEFAULT)
           ->setIsDefault(true)
           ->setIsEdit(true);
 
         if (!strlen($first->getName())) {
           $first->setName($this->getObjectCreateShortText());
         }
     } else {
         throw new Exception(
           pht(
             'EditEngine ("%s") returned builtin engine configurations, '.
             'but none are marked as default and the first configuration has '.
             'a different builtin key already. Mark a builtin as default or '.
             'omit the key from the first configuration',
             get_class($this)));
       }
     }
 
     $builtins = array();
     foreach ($configurations as $key => $config) {
       $builtin_key = $config->getBuiltinKey();
 
       if ($builtin_key === null) {
         throw new Exception(
           pht(
             'EditEngine ("%s") returned builtin engine configurations, '.
             'but one (with key "%s") is missing a builtin key. Provide a '.
             'builtin key for each configuration (you can omit it from the '.
             'first configuration in the list to automatically assign the '.
             'default key).',
             get_class($this),
             $key));
       }
 
       if (isset($builtins[$builtin_key])) {
         throw new Exception(
           pht(
             'EditEngine ("%s") returned builtin engine configurations, '.
             'but at least two specify the same builtin key ("%s"). Engines '.
             'must have unique builtin keys.',
             get_class($this),
             $builtin_key));
       }
 
       $builtins[$builtin_key] = $config;
     }
 
 
     return $builtins;
   }
 
   protected function newBuiltinEngineConfigurations() {
     return array(
       $this->newConfiguration(),
     );
   }
 
   final protected function newConfiguration() {
     return PhabricatorEditEngineConfiguration::initializeNewConfiguration(
       $this->getViewer(),
       $this);
   }
 
 
 /* -(  Managing URIs  )------------------------------------------------------ */
 
 
   /**
    * @task uri
    */
   abstract protected function getObjectViewURI($object);
 
 
   /**
    * @task uri
    */
   protected function getObjectCreateCancelURI($object) {
     return $this->getApplication()->getApplicationURI();
   }
 
 
   /**
    * @task uri
    */
   protected function getEditorURI() {
     return $this->getApplication()->getApplicationURI('edit/');
   }
 
 
   /**
    * @task uri
    */
   protected function getObjectEditCancelURI($object) {
     return $this->getObjectViewURI($object);
   }
 
 
   /**
    * @task uri
    */
   public function getEditURI($object = null, $path = null) {
     $parts = array();
 
     $parts[] = $this->getEditorURI();
 
     if ($object && $object->getID()) {
       $parts[] = $object->getID().'/';
     }
 
     if ($path !== null) {
       $parts[] = $path;
     }
 
     return implode('', $parts);
   }
 
+  public function getEffectiveObjectViewURI($object) {
+    if ($this->getIsCreate()) {
+      return $this->getObjectViewURI($object);
+    }
+
+    $page = $this->getSelectedPage();
+    if ($page) {
+      $view_uri = $page->getViewURI();
+      if ($view_uri !== null) {
+        return $view_uri;
+      }
+    }
+
+    return $this->getObjectViewURI($object);
+  }
+
+  public function getEffectiveObjectEditCancelURI($object) {
+    $page = $this->getSelectedPage();
+    if ($page) {
+      $view_uri = $page->getViewURI();
+      if ($view_uri !== null) {
+        return $view_uri;
+      }
+    }
+
+    return $this->getObjectEditCancelURI($object);
+  }
+
 
 /* -(  Creating and Loading Objects  )--------------------------------------- */
 
 
   /**
    * Initialize a new object for creation.
    *
    * @return object Newly initialized object.
    * @task load
    */
   abstract protected function newEditableObject();
 
 
   /**
    * Build an empty query for objects.
    *
    * @return PhabricatorPolicyAwareQuery Query.
    * @task load
    */
   abstract protected function newObjectQuery();
 
 
   /**
    * Test if this workflow is creating a new object or editing an existing one.
    *
    * @return bool True if a new object is being created.
    * @task load
    */
   final public function getIsCreate() {
     return $this->isCreate;
   }
 
 
   /**
    * Flag this workflow as a create or edit.
    *
    * @param bool True if this is a create workflow.
    * @return this
    * @task load
    */
   private function setIsCreate($is_create) {
     $this->isCreate = $is_create;
     return $this;
   }
 
 
   /**
    * Try to load an object by ID, PHID, or monogram. This is done primarily
    * to make Conduit a little easier to use.
    *
    * @param wild ID, PHID, or monogram.
    * @param list<const> List of required capability constants, or omit for
    *   defaults.
    * @return object Corresponding editable object.
    * @task load
    */
   private function newObjectFromIdentifier(
     $identifier,
     array $capabilities = array()) {
     if (is_int($identifier) || ctype_digit($identifier)) {
       $object = $this->newObjectFromID($identifier, $capabilities);
 
       if (!$object) {
         throw new Exception(
           pht(
             'No object exists with ID "%s".',
             $identifier));
       }
 
       return $object;
     }
 
     $type_unknown = PhabricatorPHIDConstants::PHID_TYPE_UNKNOWN;
     if (phid_get_type($identifier) != $type_unknown) {
       $object = $this->newObjectFromPHID($identifier, $capabilities);
 
       if (!$object) {
         throw new Exception(
           pht(
             'No object exists with PHID "%s".',
             $identifier));
       }
 
       return $object;
     }
 
     $target = id(new PhabricatorObjectQuery())
       ->setViewer($this->getViewer())
       ->withNames(array($identifier))
       ->executeOne();
     if (!$target) {
       throw new Exception(
         pht(
           'Monogram "%s" does not identify a valid object.',
           $identifier));
     }
 
     $expect = $this->newEditableObject();
     $expect_class = get_class($expect);
     $target_class = get_class($target);
     if ($expect_class !== $target_class) {
       throw new Exception(
         pht(
           'Monogram "%s" identifies an object of the wrong type. Loaded '.
           'object has class "%s", but this editor operates on objects of '.
           'type "%s".',
           $identifier,
           $target_class,
           $expect_class));
     }
 
     // Load the object by PHID using this engine's standard query. This makes
     // sure it's really valid, goes through standard policy check logic, and
     // picks up any `need...()` clauses we want it to load with.
 
     $object = $this->newObjectFromPHID($target->getPHID(), $capabilities);
     if (!$object) {
       throw new Exception(
         pht(
           'Failed to reload object identified by monogram "%s" when '.
           'querying by PHID.',
           $identifier));
     }
 
     return $object;
   }
 
   /**
    * Load an object by ID.
    *
    * @param int Object ID.
    * @param list<const> List of required capability constants, or omit for
    *   defaults.
    * @return object|null Object, or null if no such object exists.
    * @task load
    */
   private function newObjectFromID($id, array $capabilities = array()) {
     $query = $this->newObjectQuery()
       ->withIDs(array($id));
 
     return $this->newObjectFromQuery($query, $capabilities);
   }
 
 
   /**
    * Load an object by PHID.
    *
    * @param phid Object PHID.
    * @param list<const> List of required capability constants, or omit for
    *   defaults.
    * @return object|null Object, or null if no such object exists.
    * @task load
    */
   private function newObjectFromPHID($phid, array $capabilities = array()) {
     $query = $this->newObjectQuery()
       ->withPHIDs(array($phid));
 
     return $this->newObjectFromQuery($query, $capabilities);
   }
 
 
   /**
    * Load an object given a configured query.
    *
    * @param PhabricatorPolicyAwareQuery Configured query.
    * @param list<const> List of required capabilitiy constants, or omit for
    *  defaults.
    * @return object|null Object, or null if no such object exists.
    * @task load
    */
   private function newObjectFromQuery(
     PhabricatorPolicyAwareQuery $query,
     array $capabilities = array()) {
 
     $viewer = $this->getViewer();
 
     if (!$capabilities) {
       $capabilities = array(
         PhabricatorPolicyCapability::CAN_VIEW,
         PhabricatorPolicyCapability::CAN_EDIT,
       );
     }
 
     $object = $query
       ->setViewer($viewer)
       ->requireCapabilities($capabilities)
       ->executeOne();
     if (!$object) {
       return null;
     }
 
     return $object;
   }
 
 
   /**
    * Verify that an object is appropriate for editing.
    *
    * @param wild Loaded value.
    * @return void
    * @task load
    */
   private function validateObject($object) {
     if (!$object || !is_object($object)) {
       throw new Exception(
         pht(
           'EditEngine "%s" created or loaded an invalid object: object must '.
           'actually be an object, but is of some other type ("%s").',
           get_class($this),
           gettype($object)));
     }
 
     if (!($object instanceof PhabricatorApplicationTransactionInterface)) {
       throw new Exception(
         pht(
           'EditEngine "%s" created or loaded an invalid object: object (of '.
           'class "%s") must implement "%s", but does not.',
           get_class($this),
           get_class($object),
           'PhabricatorApplicationTransactionInterface'));
     }
   }
 
 
 /* -(  Responding to Web Requests  )----------------------------------------- */
 
 
   final public function buildResponse() {
     $viewer = $this->getViewer();
     $controller = $this->getController();
     $request = $controller->getRequest();
 
     $action = $request->getURIData('editAction');
 
     $capabilities = array();
     $use_default = false;
     $require_create = true;
     switch ($action) {
       case 'comment':
         $capabilities = array(
           PhabricatorPolicyCapability::CAN_VIEW,
         );
         $use_default = true;
         break;
       case 'parameters':
         $use_default = true;
         break;
       case 'nodefault':
       case 'nocreate':
       case 'nomanage':
         $require_create = false;
         break;
       default:
         break;
     }
 
     $object = $this->getTargetObject();
     if (!$object) {
       $id = $request->getURIData('id');
 
       if ($id) {
         $this->setIsCreate(false);
         $object = $this->newObjectFromID($id, $capabilities);
         if (!$object) {
           return new Aphront404Response();
         }
       } else {
         // Make sure the viewer has permission to create new objects of
         // this type if we're going to create a new object.
         if ($require_create) {
           $this->requireCreateCapability();
         }
 
         $this->setIsCreate(true);
         $object = $this->newEditableObject();
       }
     } else {
       $id = $object->getID();
     }
 
     $this->validateObject($object);
 
     if ($use_default) {
       $config = $this->loadDefaultConfiguration();
       if (!$config) {
         return new Aphront404Response();
       }
     } else {
       $form_key = $request->getURIData('formKey');
       if (strlen($form_key)) {
         $config = $this->loadEditEngineConfigurationWithIdentifier($form_key);
 
         if (!$config) {
           return new Aphront404Response();
         }
 
         if ($id && !$config->getIsEdit()) {
           return $this->buildNotEditFormRespose($object, $config);
         }
       } else {
         if ($id) {
           $config = $this->loadDefaultEditConfiguration();
           if (!$config) {
             return $this->buildNoEditResponse($object);
           }
         } else {
           $config = $this->loadDefaultCreateConfiguration();
           if (!$config) {
             return $this->buildNoCreateResponse($object);
           }
         }
       }
     }
 
     if ($config->getIsDisabled()) {
       return $this->buildDisabledFormResponse($object, $config);
     }
 
+    $page_key = $request->getURIData('pageKey');
+    if (!strlen($page_key)) {
+      $pages = $this->getPages($object);
+      if ($pages) {
+        $page_key = head_key($pages);
+      }
+    }
+
+    if (strlen($page_key)) {
+      $page = $this->selectPage($object, $page_key);
+      if (!$page) {
+        return new Aphront404Response();
+      }
+    }
+
     switch ($action) {
       case 'parameters':
         return $this->buildParametersResponse($object);
       case 'nodefault':
         return $this->buildNoDefaultResponse($object);
       case 'nocreate':
         return $this->buildNoCreateResponse($object);
       case 'nomanage':
         return $this->buildNoManageResponse($object);
       case 'comment':
         return $this->buildCommentResponse($object);
       default:
         return $this->buildEditResponse($object);
     }
   }
 
   private function buildCrumbs($object, $final = false) {
     $controller = $this->getController();
 
     $crumbs = $controller->buildApplicationCrumbsForEditEngine();
     if ($this->getIsCreate()) {
       $create_text = $this->getObjectCreateShortText();
       if ($final) {
         $crumbs->addTextCrumb($create_text);
       } else {
         $edit_uri = $this->getEditURI($object);
         $crumbs->addTextCrumb($create_text, $edit_uri);
       }
     } else {
       $crumbs->addTextCrumb(
         $this->getObjectEditShortText($object),
-        $this->getObjectViewURI($object));
+        $this->getEffectiveObjectViewURI($object));
 
       $edit_text = pht('Edit');
       if ($final) {
         $crumbs->addTextCrumb($edit_text);
       } else {
         $edit_uri = $this->getEditURI($object);
         $crumbs->addTextCrumb($edit_text, $edit_uri);
       }
     }
 
     return $crumbs;
   }
 
   private function buildEditResponse($object) {
     $viewer = $this->getViewer();
     $controller = $this->getController();
     $request = $controller->getRequest();
 
     $fields = $this->buildEditFields($object);
     $template = $object->getApplicationTransactionTemplate();
 
     $validation_exception = null;
     if ($request->isFormPost() && $request->getBool('editEngine')) {
       $submit_fields = $fields;
 
       foreach ($submit_fields as $key => $field) {
         if (!$field->shouldGenerateTransactionsFromSubmit()) {
           unset($submit_fields[$key]);
           continue;
         }
       }
 
       // Before we read the submitted values, store a copy of what we would
       // use if the form was empty so we can figure out which transactions are
       // just setting things to their default values for the current form.
       $defaults = array();
       foreach ($submit_fields as $key => $field) {
         $defaults[$key] = $field->getValueForTransaction();
       }
 
       foreach ($submit_fields as $key => $field) {
         $field->setIsSubmittedForm(true);
 
         if (!$field->shouldReadValueFromSubmit()) {
           continue;
         }
 
         $field->readValueFromSubmit($request);
       }
 
       $xactions = array();
 
       if ($this->getIsCreate()) {
         $xactions[] = id(clone $template)
           ->setTransactionType(PhabricatorTransactions::TYPE_CREATE);
       }
 
       foreach ($submit_fields as $key => $field) {
         $field_value = $field->getValueForTransaction();
 
         $type_xactions = $field->generateTransactions(
           clone $template,
           array(
             'value' => $field_value,
           ));
 
         foreach ($type_xactions as $type_xaction) {
           $default = $defaults[$key];
 
           if ($default === $field->getValueForTransaction()) {
             $type_xaction->setIsDefaultTransaction(true);
           }
 
           $xactions[] = $type_xaction;
         }
       }
 
       $editor = $object->getApplicationTransactionEditor()
         ->setActor($viewer)
         ->setContentSourceFromRequest($request)
         ->setContinueOnNoEffect(true);
 
       try {
 
         $editor->applyTransactions($object, $xactions);
 
         return $this->newEditResponse($request, $object, $xactions);
       } catch (PhabricatorApplicationTransactionValidationException $ex) {
         $validation_exception = $ex;
 
         foreach ($fields as $field) {
           $xaction_type = $field->getTransactionType();
           if ($xaction_type === null) {
             continue;
           }
 
           $message = $ex->getShortMessage($xaction_type);
           if ($message === null) {
             continue;
           }
 
           $field->setControlError($message);
         }
       }
     } else {
       if ($this->getIsCreate()) {
         $template = $request->getStr('template');
 
         if (strlen($template)) {
           $template_object = $this->newObjectFromIdentifier(
             $template,
             array(
               PhabricatorPolicyCapability::CAN_VIEW,
             ));
           if (!$template_object) {
             return new Aphront404Response();
           }
         } else {
           $template_object = null;
         }
 
         if ($template_object) {
           $copy_fields = $this->buildEditFields($template_object);
           $copy_fields = mpull($copy_fields, null, 'getKey');
           foreach ($copy_fields as $copy_key => $copy_field) {
             if (!$copy_field->getIsCopyable()) {
               unset($copy_fields[$copy_key]);
             }
           }
         } else {
           $copy_fields = array();
         }
 
         foreach ($fields as $field) {
           if (!$field->shouldReadValueFromRequest()) {
             continue;
           }
 
           $field_key = $field->getKey();
           if (isset($copy_fields[$field_key])) {
             $field->readValueFromField($copy_fields[$field_key]);
           }
 
           $field->readValueFromRequest($request);
         }
       }
     }
 
     $action_button = $this->buildEditFormActionButton($object);
 
     if ($this->getIsCreate()) {
       $header_text = $this->getFormHeaderText($object);
       $header_icon = 'fa-plus-square';
     } else {
       $header_text = $this->getObjectEditTitleText($object);
       $header_icon = 'fa-pencil';
     }
 
     $show_preview = !$request->isAjax();
 
     if ($show_preview) {
       $previews = array();
       foreach ($fields as $field) {
         $preview = $field->getPreviewPanel();
         if (!$preview) {
           continue;
         }
 
         $control_id = $field->getControlID();
 
         $preview
           ->setControlID($control_id)
           ->setPreviewURI('/transactions/remarkuppreview/');
 
         $previews[] = $preview;
       }
     } else {
       $previews = array();
     }
 
     $form = $this->buildEditForm($object, $fields);
 
     if ($request->isAjax()) {
       if ($this->getIsCreate()) {
         $cancel_uri = $this->getObjectCreateCancelURI($object);
         $submit_button = $this->getObjectCreateButtonText($object);
       } else {
-        $cancel_uri = $this->getObjectEditCancelURI($object);
+        $cancel_uri = $this->getEffectiveObjectEditCancelURI($object);
         $submit_button = $this->getObjectEditButtonText($object);
       }
 
       return $this->getController()
         ->newDialog()
         ->setWidth(AphrontDialogView::WIDTH_FULL)
         ->setTitle($header_text)
         ->setValidationException($validation_exception)
         ->appendForm($form)
         ->addCancelButton($cancel_uri)
         ->addSubmitButton($submit_button);
     }
 
     $header = id(new PHUIHeaderView())
       ->setHeader($header_text)
       ->setHeaderIcon($header_icon);
 
     if ($action_button) {
       $header->addActionLink($action_button);
     }
 
     $crumbs = $this->buildCrumbs($object, $final = true);
     $crumbs->setBorder(true);
 
     $box = id(new PHUIObjectBoxView())
       ->setUser($viewer)
       ->setHeaderText($this->getObjectName())
       ->setValidationException($validation_exception)
       ->setBackground(PHUIObjectBoxView::BLUE_PROPERTY)
       ->appendChild($form);
 
     $view = id(new PHUITwoColumnView())
       ->setHeader($header)
       ->setFooter(array(
         $box,
         $previews,
       ));
 
     return $controller->newPage()
       ->setTitle($header_text)
       ->setCrumbs($crumbs)
       ->appendChild($view);
   }
 
   protected function newEditResponse(
     AphrontRequest $request,
     $object,
     array $xactions) {
     return id(new AphrontRedirectResponse())
-      ->setURI($this->getObjectViewURI($object));
+      ->setURI($this->getEffectiveObjectViewURI($object));
   }
 
   private function buildEditForm($object, array $fields) {
     $viewer = $this->getViewer();
     $controller = $this->getController();
     $request = $controller->getRequest();
 
     $form = id(new AphrontFormView())
       ->setUser($viewer)
       ->addHiddenInput('editEngine', 'true');
 
     foreach ($this->contextParameters as $param) {
       $form->addHiddenInput($param, $request->getStr($param));
     }
 
     foreach ($fields as $field) {
       $field->appendToForm($form);
     }
 
     if ($this->getIsCreate()) {
       $cancel_uri = $this->getObjectCreateCancelURI($object);
       $submit_button = $this->getObjectCreateButtonText($object);
     } else {
-      $cancel_uri = $this->getObjectEditCancelURI($object);
+      $cancel_uri = $this->getEffectiveObjectEditCancelURI($object);
       $submit_button = $this->getObjectEditButtonText($object);
     }
 
     if (!$request->isAjax()) {
       $form->appendControl(
         id(new AphrontFormSubmitControl())
           ->addCancelButton($cancel_uri)
           ->setValue($submit_button));
     }
 
     return $form;
   }
 
   private function buildEditFormActionButton($object) {
     if (!$this->isEngineConfigurable()) {
       return null;
     }
 
     $viewer = $this->getViewer();
 
     $action_view = id(new PhabricatorActionListView())
       ->setUser($viewer);
 
     foreach ($this->buildEditFormActions($object) as $action) {
       $action_view->addAction($action);
     }
 
     $action_button = id(new PHUIButtonView())
       ->setTag('a')
       ->setText(pht('Configure Form'))
       ->setHref('#')
       ->setIcon('fa-gear')
       ->setDropdownMenu($action_view);
 
     return $action_button;
   }
 
   private function buildEditFormActions($object) {
     $actions = array();
 
     if ($this->supportsEditEngineConfiguration()) {
       $engine_key = $this->getEngineKey();
       $config = $this->getEditEngineConfiguration();
 
       $can_manage = PhabricatorPolicyFilter::hasCapability(
         $this->getViewer(),
         $config,
         PhabricatorPolicyCapability::CAN_EDIT);
 
       if ($can_manage) {
         $manage_uri = $config->getURI();
       } else {
         $manage_uri = $this->getEditURI(null, 'nomanage/');
       }
 
       $view_uri = "/transactions/editengine/{$engine_key}/";
 
       $actions[] = id(new PhabricatorActionView())
         ->setLabel(true)
         ->setName(pht('Configuration'));
 
       $actions[] = id(new PhabricatorActionView())
         ->setName(pht('View Form Configurations'))
         ->setIcon('fa-list-ul')
         ->setHref($view_uri);
 
       $actions[] = id(new PhabricatorActionView())
         ->setName(pht('Edit Form Configuration'))
         ->setIcon('fa-pencil')
         ->setHref($manage_uri)
         ->setDisabled(!$can_manage)
         ->setWorkflow(!$can_manage);
     }
 
     $actions[] = id(new PhabricatorActionView())
       ->setLabel(true)
       ->setName(pht('Documentation'));
 
     $actions[] = id(new PhabricatorActionView())
       ->setName(pht('Using HTTP Parameters'))
       ->setIcon('fa-book')
       ->setHref($this->getEditURI($object, 'parameters/'));
 
     $doc_href = PhabricatorEnv::getDoclink('User Guide: Customizing Forms');
     $actions[] = id(new PhabricatorActionView())
       ->setName(pht('User Guide: Customizing Forms'))
       ->setIcon('fa-book')
       ->setHref($doc_href);
 
     return $actions;
   }
 
 
   /**
    * Test if the viewer could apply a certain type of change by using the
    * normal "Edit" form.
    *
    * This method returns `true` if the user has access to an edit form and
    * that edit form has a field which applied the specified transaction type,
    * and that field is visible and editable for the user.
    *
    * For example, you can use it to test if a user is able to reassign tasks
    * or not, prior to rendering dedicated UI for task reassingment.
    *
    * Note that this method does NOT test if the user can actually edit the
    * current object, just if they have access to the related field.
    *
    * @param const Transaction type to test for.
    * @return bool True if the user could "Edit" to apply the transaction type.
    */
   final public function hasEditAccessToTransaction($xaction_type) {
     $viewer = $this->getViewer();
 
     $config = $this->loadDefaultEditConfiguration();
     if (!$config) {
       return false;
     }
 
     $object = $this->getTargetObject();
     if (!$object) {
       $object = $this->newEditableObject();
     }
 
     $fields = $this->buildEditFields($object);
 
     $field = null;
     foreach ($fields as $form_field) {
       $field_xaction_type = $form_field->getTransactionType();
       if ($field_xaction_type === $xaction_type) {
         $field = $form_field;
         break;
       }
     }
 
     if (!$field) {
       return false;
     }
 
     if (!$field->shouldReadValueFromSubmit()) {
       return false;
     }
 
     return true;
   }
 
 
   final public function addActionToCrumbs(PHUICrumbsView $crumbs) {
     $viewer = $this->getViewer();
 
     $can_create = $this->hasCreateCapability();
     if ($can_create) {
       $configs = $this->loadUsableConfigurationsForCreate();
     } else {
       $configs = array();
     }
 
     $dropdown = null;
     $disabled = false;
     $workflow = false;
 
     $menu_icon = 'fa-plus-square';
 
     if (!$configs) {
       if ($viewer->isLoggedIn()) {
         $disabled = true;
       } else {
         // If the viewer isn't logged in, assume they'll get hit with a login
         // dialog and are likely able to create objects after they log in.
         $disabled = false;
       }
       $workflow = true;
 
       if ($can_create) {
         $create_uri = $this->getEditURI(null, 'nodefault/');
       } else {
         $create_uri = $this->getEditURI(null, 'nocreate/');
       }
     } else {
       $config = head($configs);
       $form_key = $config->getIdentifier();
       $create_uri = $this->getEditURI(null, "form/{$form_key}/");
 
       if (count($configs) > 1) {
         $menu_icon = 'fa-caret-square-o-down';
 
         $dropdown = id(new PhabricatorActionListView())
           ->setUser($viewer);
 
         foreach ($configs as $config) {
           $form_key = $config->getIdentifier();
           $config_uri = $this->getEditURI(null, "form/{$form_key}/");
 
           $item_icon = 'fa-plus';
 
           $dropdown->addAction(
             id(new PhabricatorActionView())
               ->setName($config->getDisplayName())
               ->setIcon($item_icon)
               ->setHref($config_uri));
         }
       }
     }
 
     $action = id(new PHUIListItemView())
       ->setName($this->getObjectCreateShortText())
       ->setHref($create_uri)
       ->setIcon($menu_icon)
       ->setWorkflow($workflow)
       ->setDisabled($disabled);
 
     if ($dropdown) {
       $action->setDropdownMenu($dropdown);
     }
 
     $crumbs->addAction($action);
   }
 
   final public function buildEditEngineCommentView($object) {
     $config = $this->loadDefaultEditConfiguration();
 
     if (!$config) {
       // TODO: This just nukes the entire comment form if you don't have access
       // to any edit forms. We might want to tailor this UX a bit.
       return id(new PhabricatorApplicationTransactionCommentView())
         ->setNoPermission(true);
     }
 
     $viewer = $this->getViewer();
     $object_phid = $object->getPHID();
 
     $is_serious = PhabricatorEnv::getEnvConfig('phabricator.serious-business');
 
     if ($is_serious) {
       $header_text = $this->getCommentViewSeriousHeaderText($object);
       $button_text = $this->getCommentViewSeriousButtonText($object);
     } else {
       $header_text = $this->getCommentViewHeaderText($object);
       $button_text = $this->getCommentViewButtonText($object);
     }
 
     $comment_uri = $this->getEditURI($object, 'comment/');
 
     $view = id(new PhabricatorApplicationTransactionCommentView())
       ->setUser($viewer)
       ->setObjectPHID($object_phid)
       ->setHeaderText($header_text)
       ->setAction($comment_uri)
       ->setSubmitButtonName($button_text);
 
     $draft = PhabricatorVersionedDraft::loadDraft(
       $object_phid,
       $viewer->getPHID());
     if ($draft) {
       $view->setVersionedDraft($draft);
     }
 
     $view->setCurrentVersion($this->loadDraftVersion($object));
 
     $fields = $this->buildEditFields($object);
 
     $comment_actions = array();
     foreach ($fields as $field) {
       if (!$field->shouldGenerateTransactionsFromComment()) {
         continue;
       }
 
       $comment_action = $field->getCommentAction();
       if (!$comment_action) {
         continue;
       }
 
       $key = $comment_action->getKey();
 
       // TODO: Validate these better.
 
       $comment_actions[$key] = $comment_action;
     }
 
     $comment_actions = msortv($comment_actions, 'getSortVector');
 
     $view->setCommentActions($comment_actions);
 
     return $view;
   }
 
   protected function loadDraftVersion($object) {
     $viewer = $this->getViewer();
 
     if (!$viewer->isLoggedIn()) {
       return null;
     }
 
     $template = $object->getApplicationTransactionTemplate();
     $conn_r = $template->establishConnection('r');
 
     // Find the most recent transaction the user has written. We'll use this
     // as a version number to make sure that out-of-date drafts get discarded.
     $result = queryfx_one(
       $conn_r,
       'SELECT id AS version FROM %T
         WHERE objectPHID = %s AND authorPHID = %s
         ORDER BY id DESC LIMIT 1',
       $template->getTableName(),
       $object->getPHID(),
       $viewer->getPHID());
 
     if ($result) {
       return (int)$result['version'];
     } else {
       return null;
     }
   }
 
 
 /* -(  Responding to HTTP Parameter Requests  )------------------------------ */
 
 
   /**
    * Respond to a request for documentation on HTTP parameters.
    *
    * @param object Editable object.
    * @return AphrontResponse Response object.
    * @task http
    */
   private function buildParametersResponse($object) {
     $controller = $this->getController();
     $viewer = $this->getViewer();
     $request = $controller->getRequest();
     $fields = $this->buildEditFields($object);
 
     $crumbs = $this->buildCrumbs($object);
     $crumbs->addTextCrumb(pht('HTTP Parameters'));
     $crumbs->setBorder(true);
 
     $header_text = pht(
       'HTTP Parameters: %s',
       $this->getObjectCreateShortText());
 
     $header = id(new PHUIHeaderView())
       ->setHeader($header_text);
 
     $help_view = id(new PhabricatorApplicationEditHTTPParameterHelpView())
       ->setUser($viewer)
       ->setFields($fields);
 
     $document = id(new PHUIDocumentViewPro())
       ->setUser($viewer)
       ->setHeader($header)
       ->appendChild($help_view);
 
     return $controller->newPage()
       ->setTitle(pht('HTTP Parameters'))
       ->setCrumbs($crumbs)
       ->appendChild($document);
   }
 
 
   private function buildError($object, $title, $body) {
     $cancel_uri = $this->getObjectCreateCancelURI($object);
 
     return $this->getController()
       ->newDialog()
       ->setTitle($title)
       ->appendParagraph($body)
       ->addCancelButton($cancel_uri);
   }
 
 
   private function buildNoDefaultResponse($object) {
     return $this->buildError(
       $object,
       pht('No Default Create Forms'),
       pht(
         'This application is not configured with any forms for creating '.
         'objects that are visible to you and enabled.'));
   }
 
   private function buildNoCreateResponse($object) {
     return $this->buildError(
       $object,
       pht('No Create Permission'),
       pht('You do not have permission to create these objects.'));
   }
 
   private function buildNoManageResponse($object) {
     return $this->buildError(
       $object,
       pht('No Manage Permission'),
       pht(
         'You do not have permission to configure forms for this '.
         'application.'));
   }
 
   private function buildNoEditResponse($object) {
     return $this->buildError(
       $object,
       pht('No Edit Forms'),
       pht(
         'You do not have access to any forms which are enabled and marked '.
         'as edit forms.'));
   }
 
   private function buildNotEditFormRespose($object, $config) {
     return $this->buildError(
       $object,
       pht('Not an Edit Form'),
       pht(
         'This form ("%s") is not marked as an edit form, so '.
         'it can not be used to edit objects.',
         $config->getName()));
   }
 
   private function buildDisabledFormResponse($object, $config) {
     return $this->buildError(
       $object,
       pht('Form Disabled'),
       pht(
         'This form ("%s") has been disabled, so it can not be used.',
         $config->getName()));
   }
 
   private function buildCommentResponse($object) {
     $viewer = $this->getViewer();
 
     if ($this->getIsCreate()) {
       return new Aphront404Response();
     }
 
     $controller = $this->getController();
     $request = $controller->getRequest();
 
     if (!$request->isFormPost()) {
       return new Aphront400Response();
     }
 
     $config = $this->loadDefaultEditConfiguration();
     if (!$config) {
       return new Aphront404Response();
     }
 
     $fields = $this->buildEditFields($object);
 
     $is_preview = $request->isPreviewRequest();
-    $view_uri = $this->getObjectViewURI($object);
+    $view_uri = $this->getEffectiveObjectViewURI($object);
 
     $template = $object->getApplicationTransactionTemplate();
     $comment_template = $template->getApplicationTransactionCommentObject();
 
     $comment_text = $request->getStr('comment');
 
     $actions = $request->getStr('editengine.actions');
     if ($actions) {
       $actions = phutil_json_decode($actions);
     }
 
     if ($is_preview) {
       $version_key = PhabricatorVersionedDraft::KEY_VERSION;
       $request_version = $request->getInt($version_key);
       $current_version = $this->loadDraftVersion($object);
       if ($request_version >= $current_version) {
         $draft = PhabricatorVersionedDraft::loadOrCreateDraft(
           $object->getPHID(),
           $viewer->getPHID(),
           $current_version);
 
         $draft
           ->setProperty('comment', $comment_text)
           ->setProperty('actions', $actions)
           ->save();
       }
     }
 
     $xactions = array();
 
     if ($actions) {
       $action_map = array();
       foreach ($actions as $action) {
         $type = idx($action, 'type');
         if (!$type) {
           continue;
         }
 
         if (empty($fields[$type])) {
           continue;
         }
 
         $action_map[$type] = $action;
       }
 
       foreach ($action_map as $type => $action) {
         $field = $fields[$type];
 
         if (!$field->shouldGenerateTransactionsFromComment()) {
           continue;
         }
 
         if (array_key_exists('initialValue', $action)) {
           $field->setInitialValue($action['initialValue']);
         }
 
         $field->readValueFromComment(idx($action, 'value'));
 
         $type_xactions = $field->generateTransactions(
           clone $template,
           array(
             'value' => $field->getValueForTransaction(),
           ));
         foreach ($type_xactions as $type_xaction) {
           $xactions[] = $type_xaction;
         }
       }
     }
 
     if (strlen($comment_text) || !$xactions) {
       $xactions[] = id(clone $template)
         ->setTransactionType(PhabricatorTransactions::TYPE_COMMENT)
         ->attachComment(
           id(clone $comment_template)
             ->setContent($comment_text));
     }
 
     $editor = $object->getApplicationTransactionEditor()
       ->setActor($viewer)
       ->setContinueOnNoEffect($request->isContinueRequest())
       ->setContinueOnMissingFields(true)
       ->setContentSourceFromRequest($request)
       ->setIsPreview($is_preview);
 
     try {
       $xactions = $editor->applyTransactions($object, $xactions);
     } catch (PhabricatorApplicationTransactionNoEffectException $ex) {
       return id(new PhabricatorApplicationTransactionNoEffectResponse())
         ->setCancelURI($view_uri)
         ->setException($ex);
     }
 
     if (!$is_preview) {
       PhabricatorVersionedDraft::purgeDrafts(
         $object->getPHID(),
         $viewer->getPHID(),
         $this->loadDraftVersion($object));
     }
 
     if ($request->isAjax() && $is_preview) {
       return id(new PhabricatorApplicationTransactionResponse())
         ->setViewer($viewer)
         ->setTransactions($xactions)
         ->setIsPreview($is_preview);
     } else {
       return id(new AphrontRedirectResponse())
         ->setURI($view_uri);
     }
   }
 
 
 /* -(  Conduit  )------------------------------------------------------------ */
 
 
   /**
    * Respond to a Conduit edit request.
    *
    * This method accepts a list of transactions to apply to an object, and
    * either edits an existing object or creates a new one.
    *
    * @task conduit
    */
   final public function buildConduitResponse(ConduitAPIRequest $request) {
     $viewer = $this->getViewer();
 
     $config = $this->loadDefaultConfiguration();
     if (!$config) {
       throw new Exception(
         pht(
           'Unable to load configuration for this EditEngine ("%s").',
           get_class($this)));
     }
 
     $identifier = $request->getValue('objectIdentifier');
     if ($identifier) {
       $this->setIsCreate(false);
       $object = $this->newObjectFromIdentifier($identifier);
     } else {
       $this->requireCreateCapability();
 
       $this->setIsCreate(true);
       $object = $this->newEditableObject();
     }
 
     $this->validateObject($object);
 
     $fields = $this->buildEditFields($object);
 
     $types = $this->getConduitEditTypesFromFields($fields);
     $template = $object->getApplicationTransactionTemplate();
 
     $xactions = $this->getConduitTransactions($request, $types, $template);
 
     $editor = $object->getApplicationTransactionEditor()
       ->setActor($viewer)
       ->setContentSource($request->newContentSource())
       ->setContinueOnNoEffect(true);
 
     if (!$this->getIsCreate()) {
       $editor->setContinueOnMissingFields(true);
     }
 
     $xactions = $editor->applyTransactions($object, $xactions);
 
     $xactions_struct = array();
     foreach ($xactions as $xaction) {
       $xactions_struct[] = array(
         'phid' => $xaction->getPHID(),
       );
     }
 
     return array(
       'object' => array(
         'id' => $object->getID(),
         'phid' => $object->getPHID(),
       ),
       'transactions' => $xactions_struct,
     );
   }
 
 
   /**
    * Generate transactions which can be applied from edit actions in a Conduit
    * request.
    *
    * @param ConduitAPIRequest The request.
    * @param list<PhabricatorEditType> Supported edit types.
    * @param PhabricatorApplicationTransaction Template transaction.
    * @return list<PhabricatorApplicationTransaction> Generated transactions.
    * @task conduit
    */
   private function getConduitTransactions(
     ConduitAPIRequest $request,
     array $types,
     PhabricatorApplicationTransaction $template) {
 
     $viewer = $request->getUser();
     $transactions_key = 'transactions';
 
     $xactions = $request->getValue($transactions_key);
     if (!is_array($xactions)) {
       throw new Exception(
         pht(
           'Parameter "%s" is not a list of transactions.',
           $transactions_key));
     }
 
     foreach ($xactions as $key => $xaction) {
       if (!is_array($xaction)) {
         throw new Exception(
           pht(
             'Parameter "%s" must contain a list of transaction descriptions, '.
             'but item with key "%s" is not a dictionary.',
             $transactions_key,
             $key));
       }
 
       if (!array_key_exists('type', $xaction)) {
         throw new Exception(
           pht(
             'Parameter "%s" must contain a list of transaction descriptions, '.
             'but item with key "%s" is missing a "type" field. Each '.
             'transaction must have a type field.',
             $transactions_key,
             $key));
       }
 
       $type = $xaction['type'];
       if (empty($types[$type])) {
         throw new Exception(
           pht(
             'Transaction with key "%s" has invalid type "%s". This type is '.
             'not recognized. Valid types are: %s.',
             $key,
             $type,
             implode(', ', array_keys($types))));
       }
     }
 
     $results = array();
 
     if ($this->getIsCreate()) {
       $results[] = id(clone $template)
         ->setTransactionType(PhabricatorTransactions::TYPE_CREATE);
     }
 
     foreach ($xactions as $xaction) {
       $type = $types[$xaction['type']];
 
       // Let the parameter type interpret the value. This allows you to
       // use usernames in list<user> fields, for example.
       $parameter_type = $type->getConduitParameterType();
 
       $parameter_type->setViewer($viewer);
 
       try {
         $xaction['value'] = $parameter_type->getValue($xaction, 'value');
       } catch (Exception $ex) {
         throw new PhutilProxyException(
           pht(
             'Exception when processing transaction of type "%s": %s',
             $xaction['type'],
             $ex->getMessage()),
           $ex);
       }
 
       $type_xactions = $type->generateTransactions(
         clone $template,
         $xaction);
 
       foreach ($type_xactions as $type_xaction) {
         $results[] = $type_xaction;
       }
     }
 
     return $results;
   }
 
 
   /**
    * @return map<string, PhabricatorEditType>
    * @task conduit
    */
   private function getConduitEditTypesFromFields(array $fields) {
     $types = array();
     foreach ($fields as $field) {
       $field_types = $field->getConduitEditTypes();
 
       if ($field_types === null) {
         continue;
       }
 
       foreach ($field_types as $field_type) {
         $field_type->setField($field);
         $types[$field_type->getEditType()] = $field_type;
       }
     }
     return $types;
   }
 
   public function getConduitEditTypes() {
     $config = $this->loadDefaultConfiguration();
     if (!$config) {
       return array();
     }
 
     $object = $this->newEditableObject();
     $fields = $this->buildEditFields($object);
     return $this->getConduitEditTypesFromFields($fields);
   }
 
   final public static function getAllEditEngines() {
     return id(new PhutilClassMapQuery())
       ->setAncestorClass(__CLASS__)
       ->setUniqueMethod('getEngineKey')
       ->execute();
   }
 
   final public static function getByKey(PhabricatorUser $viewer, $key) {
     return id(new PhabricatorEditEngineQuery())
       ->setViewer($viewer)
       ->withEngineKeys(array($key))
       ->executeOne();
   }
 
   public function getIcon() {
     $application = $this->getApplication();
     return $application->getIcon();
   }
 
   public function loadQuickCreateItems() {
     $items = array();
 
     if (!$this->hasCreateCapability()) {
       return $items;
     }
 
     $configs = $this->loadUsableConfigurationsForCreate();
 
     if (!$configs) {
       // No items to add.
     } else if (count($configs) == 1) {
       $config = head($configs);
       $items[] = $this->newQuickCreateItem($config);
     } else {
       $group_name = $this->getQuickCreateMenuHeaderText();
 
       $items[] = id(new PHUIListItemView())
         ->setType(PHUIListItemView::TYPE_LABEL)
         ->setName($group_name);
 
       foreach ($configs as $config) {
         $items[] = $this->newQuickCreateItem($config)
           ->setIndented(true);
       }
     }
 
     return $items;
   }
 
   private function loadUsableConfigurationsForCreate() {
     $viewer = $this->getViewer();
 
     $configs = id(new PhabricatorEditEngineConfigurationQuery())
       ->setViewer($viewer)
       ->withEngineKeys(array($this->getEngineKey()))
       ->withIsDefault(true)
       ->withIsDisabled(false)
       ->execute();
 
     $configs = msort($configs, 'getCreateSortKey');
 
     return $configs;
   }
 
   private function newQuickCreateItem(
     PhabricatorEditEngineConfiguration $config) {
 
     $item_name = $config->getName();
     $item_icon = $config->getIcon();
     $form_key = $config->getIdentifier();
     $item_uri = $this->getEditURI(null, "form/{$form_key}/");
 
     return id(new PHUIListItemView())
       ->setName($item_name)
       ->setIcon($item_icon)
       ->setHref($item_uri);
   }
 
   protected function getCreateNewObjectPolicy() {
     return PhabricatorPolicies::POLICY_USER;
   }
 
   private function requireCreateCapability() {
     PhabricatorPolicyFilter::requireCapability(
       $this->getViewer(),
       $this,
       PhabricatorPolicyCapability::CAN_EDIT);
   }
 
   private function hasCreateCapability() {
     return PhabricatorPolicyFilter::hasCapability(
       $this->getViewer(),
       $this,
       PhabricatorPolicyCapability::CAN_EDIT);
   }
 
+/* -(  Form Pages  )--------------------------------------------------------- */
+
+
+  public function getSelectedPage() {
+    return $this->page;
+  }
+
+
+  private function selectPage($object, $page_key) {
+    $pages = $this->getPages($object);
+
+    if (empty($pages[$page_key])) {
+      return null;
+    }
+
+    $this->page = $pages[$page_key];
+    return $this->page;
+  }
+
+
+  protected function newPages($object) {
+    return array();
+  }
+
+
+  protected function getPages($object) {
+    if ($this->pages === null) {
+      $pages = $this->newPages($object);
+
+      assert_instances_of($pages, 'PhabricatorEditPage');
+      $pages = mpull($pages, null, 'getKey');
+
+      $this->pages = $pages;
+    }
+
+    return $this->pages;
+  }
+
+  private function applyPageToFields($object, array $fields) {
+    $pages = $this->getPages($object);
+    if (!$pages) {
+      return $fields;
+    }
+
+    if (!$this->getSelectedPage()) {
+      return $fields;
+    }
+
+    $page_picks = array();
+    $default_key = head($pages)->getKey();
+    foreach ($pages as $page_key => $page) {
+      foreach ($page->getFieldKeys() as $field_key) {
+        $page_picks[$field_key] = $page_key;
+      }
+      if ($page->getIsDefault()) {
+        $default_key = $page_key;
+      }
+    }
+
+    $page_map = array_fill_keys(array_keys($pages), array());
+    foreach ($fields as $field_key => $field) {
+      if (isset($page_picks[$field_key])) {
+        $page_map[$page_picks[$field_key]][$field_key] = $field;
+        continue;
+      }
+
+      // TODO: Maybe let the field pick a page to associate itself with so
+      // extensions can force themselves onto a particular page?
+
+      $page_map[$default_key][$field_key] = $field;
+    }
+
+    $page = $this->getSelectedPage();
+    if (!$page) {
+      $page = head($pages);
+    }
+
+    $selected_key = $page->getKey();
+    return $page_map[$selected_key];
+  }
+
 
 /* -(  PhabricatorPolicyInterface  )----------------------------------------- */
 
 
   public function getPHID() {
     return get_class($this);
   }
 
   public function getCapabilities() {
     return array(
       PhabricatorPolicyCapability::CAN_VIEW,
       PhabricatorPolicyCapability::CAN_EDIT,
     );
   }
 
   public function getPolicy($capability) {
     switch ($capability) {
       case PhabricatorPolicyCapability::CAN_VIEW:
         return PhabricatorPolicies::getMostOpenPolicy();
       case PhabricatorPolicyCapability::CAN_EDIT:
         return $this->getCreateNewObjectPolicy();
     }
   }
 
   public function hasAutomaticCapability($capability, PhabricatorUser $viewer) {
     return false;
   }
 
   public function describeAutomaticCapability($capability) {
     return null;
   }
 }
diff --git a/src/applications/transactions/editengine/PhabricatorEditPage.php b/src/applications/transactions/editengine/PhabricatorEditPage.php
new file mode 100644
index 000000000..3669ef449
--- /dev/null
+++ b/src/applications/transactions/editengine/PhabricatorEditPage.php
@@ -0,0 +1,58 @@
+<?php
+
+
+final class PhabricatorEditPage
+  extends Phobject {
+
+  private $key;
+  private $label;
+  private $fieldKeys = array();
+  private $viewURI;
+  private $isDefault;
+
+  public function setKey($key) {
+    $this->key = $key;
+    return $this;
+  }
+
+  public function getKey() {
+    return $this->key;
+  }
+
+  public function setLabel($label) {
+    $this->label = $label;
+    return $this;
+  }
+
+  public function getLabel() {
+    return $this->label;
+  }
+
+  public function setFieldKeys(array $field_keys) {
+    $this->fieldKeys = $field_keys;
+    return $this;
+  }
+
+  public function getFieldKeys() {
+    return $this->fieldKeys;
+  }
+
+  public function setIsDefault($is_default) {
+    $this->isDefault = $is_default;
+    return $this;
+  }
+
+  public function getIsDefault() {
+    return $this->isDefault;
+  }
+
+  public function setViewURI($view_uri) {
+    $this->viewURI = $view_uri;
+    return $this;
+  }
+
+  public function getViewURI() {
+    return $this->viewURI;
+  }
+
+}
diff --git a/src/applications/transactions/editfield/PhabricatorEditField.php b/src/applications/transactions/editfield/PhabricatorEditField.php
index 3b4c4e264..6ff84da87 100644
--- a/src/applications/transactions/editfield/PhabricatorEditField.php
+++ b/src/applications/transactions/editfield/PhabricatorEditField.php
@@ -1,791 +1,806 @@
 <?php
 
 abstract class PhabricatorEditField extends Phobject {
 
   private $key;
   private $viewer;
   private $label;
   private $aliases = array();
   private $value;
   private $initialValue;
   private $hasValue = false;
   private $object;
   private $transactionType;
   private $metadata = array();
   private $editTypeKey;
   private $isRequired;
   private $previewPanel;
   private $controlID;
+  private $controlInstructions;
 
   private $description;
   private $conduitDescription;
   private $conduitDocumentation;
   private $conduitTypeDescription;
 
   private $commentActionLabel;
   private $commentActionValue;
   private $commentActionOrder = 1000;
   private $hasCommentActionValue;
 
   private $isLocked;
   private $isHidden;
 
   private $isPreview;
   private $isEditDefaults;
   private $isSubmittedForm;
   private $controlError;
 
   private $isReorderable = true;
   private $isDefaultable = true;
   private $isLockable = true;
   private $isCopyable = false;
   private $isConduitOnly = false;
 
   private $conduitEditTypes;
 
   public function setKey($key) {
     $this->key = $key;
     return $this;
   }
 
   public function getKey() {
     return $this->key;
   }
 
   public function setLabel($label) {
     $this->label = $label;
     return $this;
   }
 
   public function getLabel() {
     return $this->label;
   }
 
   public function setViewer(PhabricatorUser $viewer) {
     $this->viewer = $viewer;
     return $this;
   }
 
   public function getViewer() {
     return $this->viewer;
   }
 
   public function setAliases(array $aliases) {
     $this->aliases = $aliases;
     return $this;
   }
 
   public function getAliases() {
     return $this->aliases;
   }
 
   public function setObject($object) {
     $this->object = $object;
     return $this;
   }
 
   public function getObject() {
     return $this->object;
   }
 
   public function setIsLocked($is_locked) {
     $this->isLocked = $is_locked;
     return $this;
   }
 
   public function getIsLocked() {
     return $this->isLocked;
   }
 
   public function setIsPreview($preview) {
     $this->isPreview = $preview;
     return $this;
   }
 
   public function getIsPreview() {
     return $this->isPreview;
   }
 
   public function setIsReorderable($is_reorderable) {
     $this->isReorderable = $is_reorderable;
     return $this;
   }
 
   public function getIsReorderable() {
     return $this->isReorderable;
   }
 
   public function setIsConduitOnly($is_conduit_only) {
     $this->isConduitOnly = $is_conduit_only;
     return $this;
   }
 
   public function getIsConduitOnly() {
     return $this->isConduitOnly;
   }
 
   public function setDescription($description) {
     $this->description = $description;
     return $this;
   }
 
   public function getDescription() {
     return $this->description;
   }
 
   public function setConduitDescription($conduit_description) {
     $this->conduitDescription = $conduit_description;
     return $this;
   }
 
   public function getConduitDescription() {
     if ($this->conduitDescription === null) {
       return $this->getDescription();
     }
     return $this->conduitDescription;
   }
 
   public function setConduitDocumentation($conduit_documentation) {
     $this->conduitDocumentation = $conduit_documentation;
     return $this;
   }
 
   public function getConduitDocumentation() {
     return $this->conduitDocumentation;
   }
 
   public function setConduitTypeDescription($conduit_type_description) {
     $this->conduitTypeDescription = $conduit_type_description;
     return $this;
   }
 
   public function getConduitTypeDescription() {
     return $this->conduitTypeDescription;
   }
 
   public function setIsEditDefaults($is_edit_defaults) {
     $this->isEditDefaults = $is_edit_defaults;
     return $this;
   }
 
   public function getIsEditDefaults() {
     return $this->isEditDefaults;
   }
 
   public function setIsDefaultable($is_defaultable) {
     $this->isDefaultable = $is_defaultable;
     return $this;
   }
 
   public function getIsDefaultable() {
     return $this->isDefaultable;
   }
 
   public function setIsLockable($is_lockable) {
     $this->isLockable = $is_lockable;
     return $this;
   }
 
   public function getIsLockable() {
     return $this->isLockable;
   }
 
   public function setIsHidden($is_hidden) {
     $this->isHidden = $is_hidden;
     return $this;
   }
 
   public function getIsHidden() {
     return $this->isHidden;
   }
 
   public function setIsCopyable($is_copyable) {
     $this->isCopyable = $is_copyable;
     return $this;
   }
 
   public function getIsCopyable() {
     return $this->isCopyable;
   }
 
   public function setIsSubmittedForm($is_submitted) {
     $this->isSubmittedForm = $is_submitted;
     return $this;
   }
 
   public function getIsSubmittedForm() {
     return $this->isSubmittedForm;
   }
 
   public function setIsRequired($is_required) {
     $this->isRequired = $is_required;
     return $this;
   }
 
   public function getIsRequired() {
     return $this->isRequired;
   }
 
   public function setControlError($control_error) {
     $this->controlError = $control_error;
     return $this;
   }
 
   public function getControlError() {
     return $this->controlError;
   }
 
   public function setCommentActionLabel($label) {
     $this->commentActionLabel = $label;
     return $this;
   }
 
   public function getCommentActionLabel() {
     return $this->commentActionLabel;
   }
 
   public function setCommentActionOrder($order) {
     $this->commentActionOrder = $order;
     return $this;
   }
 
   public function getCommentActionOrder() {
     return $this->commentActionOrder;
   }
 
   public function setCommentActionValue($comment_action_value) {
     $this->hasCommentActionValue = true;
     $this->commentActionValue = $comment_action_value;
     return $this;
   }
 
   public function getCommentActionValue() {
     return $this->commentActionValue;
   }
 
   public function setPreviewPanel(PHUIRemarkupPreviewPanel $preview_panel) {
     $this->previewPanel = $preview_panel;
     return $this;
   }
 
   public function getPreviewPanel() {
     return $this->previewPanel;
   }
 
+  public function setControlInstructions($control_instructions) {
+    $this->controlInstructions = $control_instructions;
+    return $this;
+  }
+
+  public function getControlInstructions() {
+    return $this->controlInstructions;
+  }
+
   protected function newControl() {
     throw new PhutilMethodNotImplementedException();
   }
 
   protected function buildControl() {
     if ($this->getIsConduitOnly()) {
       return null;
     }
 
     $control = $this->newControl();
     if ($control === null) {
       return null;
     }
 
     $control
       ->setValue($this->getValueForControl())
       ->setName($this->getKey());
 
     if (!$control->getLabel()) {
       $control->setLabel($this->getLabel());
     }
 
     if ($this->getIsSubmittedForm()) {
       $error = $this->getControlError();
       if ($error !== null) {
         $control->setError($error);
       }
     } else if ($this->getIsRequired()) {
       $control->setError(true);
     }
 
     return $control;
   }
 
   public function getControlID() {
     if (!$this->controlID) {
       $this->controlID = celerity_generate_unique_node_id();
     }
     return $this->controlID;
   }
 
   protected function renderControl() {
     $control = $this->buildControl();
     if ($control === null) {
       return null;
     }
 
     if ($this->getIsPreview()) {
       $disabled = true;
       $hidden = false;
     } else if ($this->getIsEditDefaults()) {
       $disabled = false;
       $hidden = false;
     } else {
       $disabled = $this->getIsLocked();
       $hidden = $this->getIsHidden();
     }
 
     if ($hidden) {
       return null;
     }
 
     $control->setDisabled($disabled);
 
     if ($this->controlID) {
       $control->setID($this->controlID);
     }
 
     return $control;
   }
 
   public function appendToForm(AphrontFormView $form) {
     $control = $this->renderControl();
     if ($control !== null) {
 
       if ($this->getIsPreview()) {
         if ($this->getIsHidden()) {
           $control
             ->addClass('aphront-form-preview-hidden')
             ->setError(pht('Hidden'));
         } else if ($this->getIsLocked()) {
           $control
             ->setError(pht('Locked'));
         }
       }
 
+      $instructions = $this->getControlInstructions();
+      if (strlen($instructions)) {
+        $form->appendRemarkupInstructions($instructions);
+      }
+
       $form->appendControl($control);
     }
     return $this;
   }
 
   protected function getValueForControl() {
     return $this->getValue();
   }
 
   public function getValueForDefaults() {
     $value = $this->getValue();
 
     // By default, just treat the empty string like `null` since they're
     // equivalent for almost all fields and this reduces the number of
     // meaningless transactions we generate when adjusting defaults.
     if ($value === '') {
       return null;
     }
 
     return $value;
   }
 
   protected function getValue() {
     return $this->value;
   }
 
   public function setValue($value) {
     $this->hasValue = true;
     $this->initialValue = $value;
     $this->value = $value;
     return $this;
   }
 
   public function setMetadataValue($key, $value) {
     $this->metadata[$key] = $value;
     return $this;
   }
 
   public function getMetadata() {
     return $this->metadata;
   }
 
   public function getValueForTransaction() {
     return $this->getValue();
   }
 
   public function getTransactionType() {
     return $this->transactionType;
   }
 
   public function setTransactionType($type) {
     $this->transactionType = $type;
     return $this;
   }
 
   public function readValueFromRequest(AphrontRequest $request) {
     $check = $this->getAllReadValueFromRequestKeys();
     foreach ($check as $key) {
       if (!$this->getValueExistsInRequest($request, $key)) {
         continue;
       }
 
       $this->value = $this->getValueFromRequest($request, $key);
       break;
     }
     return $this;
   }
 
   public function readValueFromComment($value) {
     $this->value = $this->getValueFromComment($value);
     return $this;
   }
 
   protected function getValueFromComment($value) {
     return $value;
   }
 
   public function getAllReadValueFromRequestKeys() {
     $keys = array();
 
     $keys[] = $this->getKey();
     foreach ($this->getAliases() as $alias) {
       $keys[] = $alias;
     }
 
     return $keys;
   }
 
   public function readDefaultValueFromConfiguration($value) {
     $this->value = $this->getDefaultValueFromConfiguration($value);
     return $this;
   }
 
   protected function getDefaultValueFromConfiguration($value) {
     return $value;
   }
 
   protected function getValueFromObject($object) {
     if ($this->hasValue) {
       return $this->value;
     } else {
       return $this->getDefaultValue();
     }
   }
 
   protected function getValueExistsInRequest(AphrontRequest $request, $key) {
     return $this->getHTTPParameterValueExists($request, $key);
   }
 
   protected function getValueFromRequest(AphrontRequest $request, $key) {
     return $this->getHTTPParameterValue($request, $key);
   }
 
   public function readValueFromField(PhabricatorEditField $other) {
     $this->value = $this->getValueFromField($other);
     return $this;
   }
 
   protected function getValueFromField(PhabricatorEditField $other) {
     return $other->getValue();
   }
 
 
   /**
    * Read and return the value the object had when the user first loaded the
    * form.
    *
    * This is the initial value from the user's point of view when they started
    * the edit process, and used primarily to prevent race conditions for fields
    * like "Projects" and "Subscribers" that use tokenizers and support edge
    * transactions.
    *
    * Most fields do not need to store these values or deal with initial value
    * handling.
    *
    * @param AphrontRequest Request to read from.
    * @param string Key to read.
    * @return wild Value read from request.
    */
   protected function getInitialValueFromSubmit(AphrontRequest $request, $key) {
     return null;
   }
 
   public function getInitialValue() {
     return $this->initialValue;
   }
 
   public function setInitialValue($initial_value) {
     $this->initialValue = $initial_value;
     return $this;
   }
 
   public function readValueFromSubmit(AphrontRequest $request) {
     $key = $this->getKey();
     if ($this->getValueExistsInSubmit($request, $key)) {
       $value = $this->getValueFromSubmit($request, $key);
     } else {
       $value = $this->getDefaultValue();
     }
     $this->value = $value;
 
     $initial_value = $this->getInitialValueFromSubmit($request, $key);
     $this->initialValue = $initial_value;
 
     return $this;
   }
 
   protected function getValueExistsInSubmit(AphrontRequest $request, $key) {
     return $this->getHTTPParameterValueExists($request, $key);
   }
 
   protected function getValueFromSubmit(AphrontRequest $request, $key) {
     return $this->getHTTPParameterValue($request, $key);
   }
 
   protected function getHTTPParameterValueExists(
     AphrontRequest $request,
     $key) {
     $type = $this->getHTTPParameterType();
 
     if ($type) {
       return $type->getExists($request, $key);
     }
 
     return false;
   }
 
   protected function getHTTPParameterValue($request, $key) {
     $type = $this->getHTTPParameterType();
 
     if ($type) {
       return $type->getValue($request, $key);
     }
 
     return null;
   }
 
   protected function getDefaultValue() {
     $type = $this->getHTTPParameterType();
 
     if ($type) {
       return $type->getDefaultValue();
     }
 
     return null;
   }
 
   final public function getHTTPParameterType() {
     if ($this->getIsConduitOnly()) {
       return null;
     }
 
     $type = $this->newHTTPParameterType();
 
     if ($type) {
       $type->setViewer($this->getViewer());
     }
 
     return $type;
   }
 
   protected function newHTTPParameterType() {
     return new AphrontStringHTTPParameterType();
   }
 
   public function getConduitParameterType() {
     $type = $this->newConduitParameterType();
 
     if (!$type) {
       return null;
     }
 
     $type->setViewer($this->getViewer());
 
     return $type;
   }
 
   abstract protected function newConduitParameterType();
 
   public function setEditTypeKey($edit_type_key) {
     $this->editTypeKey = $edit_type_key;
     return $this;
   }
 
   public function getEditTypeKey() {
     if ($this->editTypeKey === null) {
       return $this->getKey();
     }
     return $this->editTypeKey;
   }
 
   protected function newEditType() {
     $parameter_type = $this->getConduitParameterType();
     if (!$parameter_type) {
       return null;
     }
 
     return id(new PhabricatorSimpleEditType())
       ->setConduitParameterType($parameter_type);
   }
 
   protected function getEditType() {
     $transaction_type = $this->getTransactionType();
 
     if ($transaction_type === null) {
       return null;
     }
 
     $type_key = $this->getEditTypeKey();
     $edit_type = $this->newEditType();
     if (!$edit_type) {
       return null;
     }
 
     return $edit_type
       ->setEditType($type_key)
       ->setTransactionType($transaction_type)
       ->setMetadata($this->getMetadata());
   }
 
   final public function getConduitEditTypes() {
     if ($this->conduitEditTypes === null) {
       $edit_types = $this->newConduitEditTypes();
       $edit_types = mpull($edit_types, null, 'getEditType');
 
       foreach ($edit_types as $edit_type) {
         $edit_type->setEditField($this);
       }
 
       $this->conduitEditTypes = $edit_types;
     }
 
     return $this->conduitEditTypes;
   }
 
   final public function getConduitEditType($key) {
     $edit_types = $this->getConduitEditTypes();
 
     if (empty($edit_types[$key])) {
       throw new Exception(
         pht(
           'This EditField does not provide a Conduit EditType with key "%s".',
           $key));
     }
 
     return $edit_types[$key];
   }
 
   protected function newConduitEditTypes() {
     $edit_type = $this->getEditType();
 
     if (!$edit_type) {
       return array();
     }
 
     return array($edit_type);
   }
 
   public function getCommentAction() {
     $label = $this->getCommentActionLabel();
     if ($label === null) {
       return null;
     }
 
     $action = $this->newCommentAction();
     if ($action === null) {
       return null;
     }
 
     if ($this->hasCommentActionValue) {
       $value = $this->getCommentActionValue();
     } else {
       $value = $this->getValue();
     }
 
     $action
       ->setKey($this->getKey())
       ->setLabel($label)
       ->setValue($this->getValueForCommentAction($value))
       ->setOrder($this->getCommentActionOrder());
 
     return $action;
   }
 
   protected function newCommentAction() {
     return null;
   }
 
   protected function getValueForCommentAction($value) {
     return $value;
   }
 
   public function shouldGenerateTransactionsFromSubmit() {
     if ($this->getIsConduitOnly()) {
       return false;
     }
 
     $edit_type = $this->getEditType();
     if (!$edit_type) {
       return false;
     }
 
     return true;
   }
 
   public function shouldReadValueFromRequest() {
     if ($this->getIsConduitOnly()) {
       return false;
     }
 
     if ($this->getIsLocked()) {
       return false;
     }
 
     if ($this->getIsHidden()) {
       return false;
     }
 
     return true;
   }
 
   public function shouldReadValueFromSubmit() {
     if ($this->getIsConduitOnly()) {
       return false;
     }
 
     if ($this->getIsLocked()) {
       return false;
     }
 
     if ($this->getIsHidden()) {
       return false;
     }
 
     return true;
   }
 
   public function shouldGenerateTransactionsFromComment() {
     if ($this->getIsConduitOnly()) {
       return false;
     }
 
     if ($this->getIsLocked()) {
       return false;
     }
 
     if ($this->getIsHidden()) {
       return false;
     }
 
     return true;
   }
 
   public function generateTransactions(
     PhabricatorApplicationTransaction $template,
     array $spec) {
 
     $edit_type = $this->getEditType();
     if (!$edit_type) {
       throw new Exception(
         pht(
           'EditField (with key "%s", of class "%s") is generating '.
           'transactions, but has no EditType.',
           $this->getKey(),
           get_class($this)));
     }
 
     return $edit_type->generateTransactions($template, $spec);
   }
 
 }
diff --git a/src/applications/transactions/editfield/PhabricatorTextAreaEditField.php b/src/applications/transactions/editfield/PhabricatorTextAreaEditField.php
index 4c07b31e5..4d4e3a75b 100644
--- a/src/applications/transactions/editfield/PhabricatorTextAreaEditField.php
+++ b/src/applications/transactions/editfield/PhabricatorTextAreaEditField.php
@@ -1,73 +1,77 @@
 <?php
 
 final class PhabricatorTextAreaEditField
   extends PhabricatorEditField {
 
   private $monospaced;
   private $height;
   private $isStringList;
 
   public function setMonospaced($monospaced) {
     $this->monospaced = $monospaced;
     return $this;
   }
 
   public function getMonospaced() {
     return $this->monospaced;
   }
 
   public function setHeight($height) {
     $this->height = $height;
     return $this;
   }
 
   public function getHeight() {
     return $this->height;
   }
 
   public function setIsStringList($is_string_list) {
     $this->isStringList = $is_string_list;
     return $this;
   }
 
   public function getIsStringList() {
     return $this->isStringList;
   }
 
   protected function newControl() {
     $control = new AphrontFormTextAreaControl();
 
     if ($this->getMonospaced()) {
       $control->setCustomClass('PhabricatorMonospaced');
     }
 
     $height = $this->getHeight();
     if ($height) {
       $control->setHeight($height);
     }
 
     return $control;
   }
 
   protected function getValueForControl() {
     $value = $this->getValue();
-    return implode("\n", $value);
+    if ($this->getIsStringList()) {
+      return implode("\n", $value);
+    } else {
+      return $value;
+    }
   }
 
   protected function newConduitParameterType() {
     if ($this->getIsStringList()) {
       return new ConduitStringListParameterType();
     } else {
       return new ConduitStringParameterType();
     }
   }
 
   protected function newHTTPParameterType() {
     if ($this->getIsStringList()) {
       return new AphrontStringListHTTPParameterType();
     } else {
       return new AphrontStringHTTPParameterType();
     }
   }
 
 }
diff --git a/src/docs/contributor/describing_problems.diviner b/src/docs/contributor/describing_problems.diviner
index d6ba41450..57bb849b0 100644
--- a/src/docs/contributor/describing_problems.diviner
+++ b/src/docs/contributor/describing_problems.diviner
@@ -1,159 +1,159 @@
 @title Describing Root Problems
 @group detail
 
 Explains how to describe a root problem effectively.
 
 Overview
 ========
 
 We receive many feature requests with poor problem descriptions. You may have
 filed such a request if you've been sent here. This document explains what we
 want, and how to give us the information to help you.
 
 We will **never** implement a feature request without first understanding the
 root problem.
 
 Good problem descriptions let us answer your questions quickly and correctly,
 and suggest workarounds or alternate ways to accomplish what you want.
 
 Poor problem descriptions require us to ask multiple clarifying questions and
 do not give us enough information to suggest alternate solutions or
 workarounds. We need to keep going back and forth to understand the problem
 you're really facing, which means it will take a long time to get the answer
 you want.
 
 
 What We Want
 ============
 
 We want a description of your overarching goal. The problem you started trying
 to solve first, long before you decided what feature you needed.
 
 This doesn't need to be very detailed, we just need to know what you are
 ultimately hoping to accomplish.
 
 Problem descriptions should include context and explain why you're encountering
 a problem and why it's important for you to resolve it.
 
 Here are some examples of good ways to start a problem description:
 
 > My company does contracting work for government agencies. Because of the
 > nature of our customers, deadlines are critical and it's very important
 > for us to keep track of where we are on a timeline. We're using Maniphest
 > to track tasks...
 
 > I have poor eyesight, and use a screenreader to help me use software like
 > Phabricator in my job as a developer. I'm having difficulty...
 
 > We work on a large server program which has very long compile times.
 > Switching branches is a huge pain (you have to rebuild the binary after
 > every switch, which takes about 8 minutes), but we've recently begun using
 > `git worktree` to help, which has made life a lot better. However, ...
 
 > I triage manual test failures from our offshore QA team. Here's how our
 > workflow works...
 
 All of these descriptions are helpful: the provide context about what goals
 you're trying to accomplish and why.
 
 Here are some examples of ways to start a problem description that probably
 are not very good:
 
 > {icon times color=red} Add custom keyboard shortcuts.
 
 > {icon times color=red} I have a problem: there is no way to download
 > .tar archives of repositories.
 
 > {icon times color=red} I want an RSS feed of my tokens. My root problem is
 > that I do not have an RSS feed of my tokens.
 
 > {icon times color=red} There is no way to see other users' email addresses.
 > That is a problem.
 
 > {icon times color=red} I've used some other software that has a cool
 > feature. Phabricator should have that feature too.
 
 These problem descriptions are not helpful. They do not describe goals or
 provide context.
 
 
 "5 Whys" Technique
 ================
 
 If you're having trouble understanding what we're asking for, one technique
 which may help is ask yourself "Why?" repeatedly. Each answer will usually
 get you closer to describing the root problem.
 
 For example:
 
 > I want custom keyboard shortcuts.
 
 This is a very poor feature request which does not describe the root problem.
 It limits us to only one possible solution. Try asking "Why?" to get closer
 to the root problem.
 
 > **Why?**
 > I want to add a shortcut to create a new task.
 
 This is still very poor, but we can now think about solutions involving making
 this whole flow easier, or adding a shortcut for exactly this to the upstream,
 which might be a lot easier than adding custom keyboard shortcuts.
 
 It's common to stop here and report this as your root problem. This is **not**
 a root problem. This problem is only //slightly// more general than the one
 we started with. Let's ask "Why?" again to get closer to the root problem.
 
 > **Why?**
 > I create a lot of very similar tasks every day.
 
 This is still quite poor, but we can now think about solutions like a bulk task
 creation flow, or maybe point you at task creation templating or prefilling or
 the Conduit API or email integration or Doorkeeper.
 
 > **Why?**
 > The other developers email me issues and I copy/paste them into Maniphest.
 
 This is getting closer, but still doesn't tell us what your goal is.
 
 > **Why?**
 > We set up email integration before, but each task needs to have specific
 > projects so that didn't work and now I'm stuck doing the entry by hand.
 
 This is in the realm of reasonable, and likely easy to solve with custom
 inbound addresses and Herald rules, or with a small extension to Herald. We
 might try to improve the documentation to make the feature easier to discover
 or understand.
 
 You could (and should) go even further than this and explain why tasks need to
 be tagged with specific projects. It's very easy to provide more context and
 can only improve the speed and quality of our response.
 
 Note that this solution (Herald rules on inbound email) has nothing to do with
 the narrow feature request (keyboard shortcuts) that you otherwise arrived at,
 but there's no possible way we can suggest a solution involving email
 integration or Herald if your report doesn't even mention that part of the
 context.
 
 
 Additional Resources
 ====================
 
 Poor problem descriptions are a common issue in software development and
 extensively documented elsewhere. Here are some additional resources describing
 how to describe problems and ask questions effectivey:
 
   - [[ http://www.catb.org/esr/faqs/smart-questions.html | How To Ask
     Questions The Smart Way ]], by Eric S. Raymond
   - [[ http://xyproblem.info | XY Problem ]]
   - [[ https://en.wikipedia.org/wiki/5_Whys | 5 Whys Technique ]]
 
 Asking good questions and describing problems clearly is an important,
 fundamental communication skill that software professionals should cultivate.
 
 
 Next Steps
 ==========
 
 Continue by:
 
-  - returning to @{article:Contribuing Feature Requests}.
+  - returning to @{article:Contributing Feature Requests}.
diff --git a/src/docs/user/cluster/cluster.diviner b/src/docs/user/cluster/cluster.diviner
index 0180c7ff4..d78c445c4 100644
--- a/src/docs/user/cluster/cluster.diviner
+++ b/src/docs/user/cluster/cluster.diviner
@@ -1,289 +1,326 @@
 @title Clustering Introduction
 @group cluster
 
 Guide to configuring Phabricator across multiple hosts for availability and
 performance.
 
+
 Overview
 ========
 
-WARNING: This feature is a very early prototype; the features this document
-describes are mostly speculative fantasy.
+WARNING: This feature is a prototype. Installs should expect a challening
+adventure when deploying clusters. In the best of times, configuring a
+cluster is complex and requires significant operations experience.
 
 Phabricator can be configured to run on multiple hosts with redundant services
 to improve its availability and scalability, and make disaster recovery much
 easier.
 
 Clustering is more complex to setup and maintain than running everything on a
 single host, but greatly reduces the cost of recovering from hardware and
 network failures.
 
 Each Phabricator service has an array of clustering options that can be
-configured independently. Configuring a cluster is inherently complex, and this
-is an advanced feature aimed at installs with large userbases and experienced
-operations personnel who need this high degree of flexibility.
+configured somewhat independently. Configuring a cluster is inherently complex,
+and this is an advanced feature aimed at installs with large userbases and
+experienced operations personnel who need this high degree of flexibility.
 
 The remainder of this document summarizes how to add redundancy to each
 service and where your efforts are likely to have the greatest impact.
 
 For additional guidance on setting up a cluster, see "Overlaying Services"
 and "Cluster Recipes" at the bottom of this document.
 
 
+Clusterable Services
+====================
+
+This table provides an overview of clusterable services, their setup
+complexity, and the rough impact that converting them to run on multiple hosts
+will have on availability, resistance to data loss, and scalability.
+
+| Service | Setup | Availability | Loss Resistance | Scalability
+|---------|-------|--------------|-----------|------------
+| **Databases** | Moderate | **High** | **High** | Low
+| **Repositories** | Complex | Moderate | **High** | Moderate
+| **Daemons** | Minimal | Low | No Risk | Low
+| **SSH Servers** | Minimal | Low | No Risk | Low
+| **Web Servers** | Minimal | **High** | No Risk | Moderate
+| **Notifications** | Minimal | Low | No Risk | Low
+
+See below for a walkthrough of these services in greater detail.
+
+
 Preparing for Clustering
 ========================
 
 To begin deploying Phabricator in cluster mode, set up `cluster.addresses`
 in your configuration.
 
 This option should contain a list of network address blocks which are considered
 to be part of the cluster. Hosts in this list are allowed to bend (or even
 break) some of the security and policy rules when they make requests to other
 hosts in the cluster, so this list should be as small as possible. See "Cluster
 Whitelist Security" below for discussion.
 
 If you are deploying hardware in EC2, a reasonable approach is to launch a
 dedicated Phabricator VPC, whitelist the whole VPC as a Phabricator cluster,
 and then deploy only Phabricator services into that VPC.
 
 If you have additional auxiliary hosts which run builds and tests via Drydock,
 you should //not// include them in the cluster address definition. For more
 detailed discussion of the Drydock security model, see @{Drydock User Guide:
 Security}.
 
 Most other clustering features will not work until you define a cluster by
 configuring `cluster.addresses`.
 
 
 Cluster Whitelist Security
 ========================
 
 When you configure `cluster.addresses`, you should keep the list of trusted
 cluster hosts as small as possible. Hosts on this list gain additional
 capabilities, including these:
 
 **Trusted HTTP Headers**: Normally, Phabricator distrusts the load balancer
 HTTP headers `X-Forwarded-For` and `X-Forwarded-Proto` because they may be
 client-controlled and can be set to arbitrary values by an attacker if no load
 balancer is deployed. In particular, clients can set `X-Forwarded-For` to any
 value and spoof traffic from arbitrary remotes.
 
 These headers are trusted when they are received from a host on the cluster
 address whitelist. This allows requests from cluster loadbalancers to be
 interpreted correctly by default without requiring additional custom code or
 configuration.
 
 **Intracluster HTTP**: Requests from cluster hosts are not required to use
 HTTPS, even if `security.require-https` is enabled, because it is common to
 terminate HTTPS on load balancers and use plain HTTP for requests within a
 cluster.
 
 **Special Authentication Mechanisms**: Cluster hosts are allowed to connect to
 other cluster hosts with "root credentials", and to impersonate any user
 account.
 
 The use of root credentials is required because the daemons must be able to
 bypass policies in order to function properly: they need to send mail about
 private conversations and import commits in private repositories.
 
 The ability to impersonate users is required because SSH nodes must receive,
 interpret, modify, and forward SSH traffic. They can not use the original
 credentials to do this because SSH authentication is asymmetric and they do not
 have the user's private key. Instead, they use root credentials and impersonate
 the user within the cluster.
 
 These mechanisms are still authenticated (and use asymmetric keys, like SSH
 does), so access to a host in the cluster address block does not mean that an
 attacker can immediately compromise the cluster. However, an over-broad cluster
 address whitelist may give an attacker who gains some access additional tools
 to escalate access.
 
 Note that if an attacker gains access to an actual cluster host, these extra
 powers are largely moot. Most cluster hosts must be able to connect to the
 master database to function properly, so the attacker will just do that and
 freely read or modify whatever data they want.
 
 
 Cluster: Databases
 =================
 
 Configuring multiple database hosts is moderately complex, but normally has the
 highest impact on availability and resistance to data loss. This is usually the
 most important service to make redundant if your focus is on availability and
 disaster recovery.
 
 Configuring replicas allows Phabricator to run in read-only mode if you lose
 the master and to quickly promote the replica as a replacement.
 
 For details, see @{article:Cluster: Databases}.
 
 
 Cluster: Repositories
 =====================
 
 Configuring multiple repository hosts is complex, but is required before you
 can add multiple daemon or web hosts.
 
 Repository replicas are important for availability if you host repositories
 on Phabricator, but less important if you host repositories elsewhere
 (instead, you should focus on making that service more available).
 
 The distributed nature of Git and Mercurial tend to mean that they are
 naturally somewhat resistant to data loss: every clone of a repository includes
 the entire history.
 
 Repositories may become a scalability bottleneck, although this is rare unless
 your install has an unusually heavy repository read volume. Slow clones/fetches
 may hint at a repository capacity problem. Adding more repository hosts will
 provide an approximately linear increase in capacity.
 
 For details, see @{article:Cluster: Repositories}.
 
 
 Cluster: Daemons
 ================
 
 Configuring multiple daemon hosts is straightforward, but you must configure
 repositories first.
 
-With daemons running on multiple hosts, you can transparently survive the loss
+With daemons running on multiple hosts you can transparently survive the loss
 of any subset of hosts without an interruption to daemon services, as long as
 at least one host remains alive. Daemons are stateless, so spreading daemons
 across multiple hosts provides no resistance to data loss.
 
 Daemons can become a bottleneck, particularly if your install sees a large
 volume of write traffic to repositories. If the daemon task queue has a
 backlog, that hints at a capacity problem. If existing hosts have unused
 resources, increase `phd.taskmasters` until they are fully utilized. From
 there, adding more daemon hosts will provide an approximately linear increase
 in capacity.
 
 For details, see @{article:Cluster: Daemons}.
 
 
+Cluster: SSH Servers
+====================
+
+Configuring multiple SSH hosts is straightforward, but you must configure
+repositories first.
+
+With multiple SSH hosts you can transparently survive the loss of any subset
+of hosts without interruption to repository services, as long as at last one
+host remains alive. SSH services are stateless, so putting multiple hosts in
+service provides no resistance to data loss because no data is at risk.
+
+SSH hosts are very rarely a scalability bottleneck.
+
+For details, see @{article:Cluster: SSH Servers}.
+
+
 Cluster: Web Servers
 ====================
 
 Configuring multiple web hosts is straightforward, but you must configure
 repositories first.
 
-With multiple web hosts, you can transparently survive the loss of any subset
-of hosts as long as at least one host remains alive. Web hosts are stateless,
+With multiple web hosts you can transparently survive the loss of any subset
+of hosts as long as at least one host remains alive. Web services are stateless,
 so putting multiple hosts in service provides no resistance to data loss
 because no data is at risk.
 
 Web hosts can become a bottleneck, particularly if you have a workload that is
 heavily focused on reads from the web UI (like a public install with many
 anonymous users). Slow responses to web requests may hint at a web capacity
 problem. Adding more hosts will provide an approximately linear increase in
 capacity.
 
 For details, see @{article:Cluster: Web Servers}.
 
 
 Cluster: Notifications
 ======================
 
 Configuring multiple notification hosts is simple and has no pre-requisites.
 
 With multiple notification hosts, you can survive the loss of any subset of
 hosts as long as at least one host remains alive. Service may be breifly
 disrupted directly after the incident which destroys the other hosts.
 
 Notifications are noncritical, so this normally has little practical impact
 on service availability. Notifications are also stateless, so clustering this
 service provides no resistance to data loss because no data is at risk.
 
 Notification delivery normally requires very few resources, so adding more
 hosts is unlikely to have much impact on scalability.
 
 For details, see @{article:Cluster: Notifications}.
 
 
 Overlaying Services
 ===================
 
 Although hosts can run a single dedicated service type, certain groups of
 services work well together. Phabricator clusters usually do not need to be
 very large, so deploying a small number of hosts with multiple services is a
 good place to start.
 
 In planning a cluster, consider these blended host types:
 
 **Everything**: Run HTTP, SSH, MySQL, notifications, repositories and daemons
 on a single host. This is the starting point for single-node setups, and
 usually also the best configuration when adding the second node.
 
 **Everything Except Databases**: Run HTTP, SSH, notifications, repositories and
 daemons on one host, and MySQL on a different host. MySQL uses many of the same
 resources that other services use. It's also simpler to separate than other
 services, and tends to benefit the most from dedicated hardware.
 
 **Repositories and Daemons**: Run repositories and daemons on the same host.
 Repository hosts //must// run daemons, and it normally makes sense to
 completely overlay repositories and daemons. These services tend to use
 different resources (repositories are heavier on I/O and lighter on CPU/RAM;
 daemons are heavier on CPU/RAM and lighter on I/O).
 
 Repositories and daemons are also both less latency sensitive than other
 service types, so there's a wider margin of error for under provisioning them
 before performance is noticeably affected.
 
 These nodes tend to use system resources in a balanced way. Individual nodes
 in this class do not need to be particularly powerful.
 
 **Frontend Servers**: Run HTTP and SSH on the same host. These are easy to set
 up, stateless, and you can scale the pool up or down easily to meet demand.
 Routing both types of ingress traffic through the same initial tier can
 simplify load balancing.
 
 These nodes tend to need relatively little RAM.
 
 
 Cluster Recipes
 ===============
 
 This section provides some guidance on reasonable ways to scale up a cluster.
 
 The smallest possible cluster is **two hosts**. Run everything (web, ssh,
 database, notifications, repositories, and daemons) on each host. One host will
 serve as the master; the other will serve as a replica.
 
 Ideally, you should physically separate these hosts to reduce the chance that a
 natural disaster or infrastructure disruption could disable or destroy both
 hosts at the same time.
 
 From here, you can choose how you expand the cluster.
 
 To improve **scalability and performance**, separate loaded services onto
 dedicated hosts and then add more hosts of that type to increase capacity. If
 you have a two-node cluster, the best way to improve scalability by adding one
 host is likely to separate the master database onto its own host.
 
 Note that increasing scale may //decrease// availability by leaving you with
 too little capacity after a failure. If you have three hosts handling traffic
 and one datacenter fails, too much traffic may be sent to the single remaining
 host in the surviving datacenter. You can hedge against this by mirroring new
 hosts in other datacenters (for example, also separate the replica database
 onto its own host).
 
 After separating databases, separating repository + daemon nodes is likely
 the next step to consider.
 
 To improve **availability**, add another copy of everything you run in one
 datacenter to a new datacenter. For example, if you have a two-node cluster,
 the best way to improve availability is to run everything on a third host in a
 third datacenter. If you have a 6-node cluster with a web node, a database node
 and a repo + daemon node in two datacenters, add 3 more nodes to create a copy
 of each node in a third datacenter.
 
 You can continue adding hosts until you run out of hosts.
 
 
 Next Steps
 ==========
 
 Continue by:
 
   - learning how Phacility configures and operates a large, multi-tenant
     production cluster in ((cluster)).
diff --git a/src/docs/user/cluster/cluster_daemons.diviner b/src/docs/user/cluster/cluster_daemons.diviner
index 61cedd464..8cde3e7b7 100644
--- a/src/docs/user/cluster/cluster_daemons.diviner
+++ b/src/docs/user/cluster/cluster_daemons.diviner
@@ -1,59 +1,57 @@
 @title Cluster: Daemons
 @group cluster
 
 Configuring Phabricator to use multiple daemon hosts.
 
 Overview
 ========
 
-WARNING: This feature is a very early prototype; the features this document
-describes are mostly speculative fantasy.
-
 You can run daemons on multiple hosts. The advantages of doing this are:
 
   - you can completely survive the loss of multiple daemon hosts; and
   - worker queue throughput may improve.
 
 This configuration is simple, but you must configure repositories first. For
 details, see @{article:Cluster: Repositories}.
 
 Since repository hosts must run daemons anyway, you usually do not need to do
-any additional work and can skip this entirely.
+any additional work and can skip this entirely if you have already configured
+multiple repository hosts.
 
 
 Adding Daemon Hosts
 ===================
 
 After configuring repositories for clustering, launch daemons on every
 repository host according to the documentation in
 @{article:Cluster: Repositories}. These daemons are necessary: repositories
 will not fetch, update, or synchronize properly without them.
 
 If your repository clustering is redundant (you have at least two repository
 hosts), these daemons are also likely to be sufficient in most cases. If you
 want to launch additional hosts anyway (for example, to increase queue capacity
 for unusual workloads), see "Dedicated Daemon Hosts" below.
 
 
 Dedicated Daemon Hosts
 ======================
 
 You can launch additional daemon hosts without any special configuration.
 Daemon hosts must be able to reach other hosts on the network, but do not need
 to run any services (like HTTP or SSH). Simply deploy the Phabricator software
 and configuration and start the daemons.
 
 Normally, there is little reason to deploy dedicated daemon hosts. They can
 improve queue capacity, but generally do not improve availability or increase
 resistance to data loss on their own. Instead, consider deploying more
 repository hosts: repository hosts run daemons, so this will increase queue
 capacity but also improve repository availability and cluster resistance.
 
 
 Next Steps
 ==========
 
 Continue by:
 
   - returning to @{article:Clustering Introduction}; or
   - configuring repositories first with @{article:Cluster: Repositories}.
diff --git a/src/docs/user/cluster/cluster_devices.diviner b/src/docs/user/cluster/cluster_devices.diviner
index 197620e46..57729fdb8 100644
--- a/src/docs/user/cluster/cluster_devices.diviner
+++ b/src/docs/user/cluster/cluster_devices.diviner
@@ -1,247 +1,247 @@
 @title Cluster: Devices
 @group cluster
 
 Guide to configuring hosts to act as cluster devices.
 
 Cluster Context
 ===============
 
 This document describes a step in configuring Phabricator to run on
 multiple hosts in a cluster configuration. This is an advanced feature. For
 more information on clustering, see @{article:Clustering Introduction}.
 
 In this context, device configuration is mostly relevant to configuring
 repository services in a cluster. You can find more details about this in
 @{article:Cluster: Repositories}.
 
 
 Overview
 ========
 
 Some cluster services need to be able to authenticate themselves and interact
 with other services. For example, two repository hosts holding copies of the
 same repository must be able to fetch changes from one another, even if the
 repository is private.
 
 Within a cluster, devices authenticate using SSH keys. Some operations happen
 over SSH (using keys in a normal way, as you would when running `ssh` from the
 command line), while others happen over HTTP (using SSH keys to sign requests).
 
 Before hosts can authenticate to one another, you need to configure the
 credentials so other devices know the keys can be trusted. Beyond establishing
 trust, this configuration will establish //device identity//, so each host
 knows which device it is explicitly.
 
 Today, this is primarily necessary when configuring repository clusters.
 
 
 Using Almanac
 =============
 
 The tool Phabricator uses to manage cluster devices is the **Almanac**
 application, and most configuration will occur through the application's web
 UI. If you are not familiar with it, see @{article:Almanac User Guide} first.
 This document assumes you are familiar with Almanac concepts.
 
 
 What Lies Ahead
 ===============
 
 Here's a brief overview of the steps required to register cluster devices. The
 remainder of this document walks through these points in more detail.
 
   - Create an Almanac device record for each device.
   - Generate, add, and trust SSH keys if necessary.
   - Install Phabricator on the host.
   - Use `bin/almanac register` from the host to register it as a device.
 
 See below for guidance on each of these steps.
 
 
 Individual vs Shared Keys
 =========================
 
 Before getting started, you should choose how you plan to manage device SSH
 keys. Trust and device identity are handled separately, and there are two ways
 to set up SSH keys so that devices can authenticate with one another:
 
   - you can generate a unique SSH key for each device; or
   - you can generate one SSH key and share it across multiple devices.
 
 Using **unique keys** allows the tools to do some more sanity/safety checks and
 makes it a bit more difficult to misconfigure things, but you'll have to do
 more work managing the actual keys. This may be a better choice if you are
 setting up a small cluster (2-3 devices) for the first time.
 
 Using **shared keys** makes key management easier but safety checks won't be
 able to catch a few kinds of mistakes. This may be a better choice if you are
 setting up a larger cluster, plan to expand the cluster later, or have
 experience with Phabricator clustering.
 
 Because all cluster keys are all-powerful, there is no material difference
 between these methods from a security or trust viewpoint. Unique keys are just
 potentially easier to administrate at small scales, while shared keys are
 easier at larger scales.
 
 
 Create Almanac Device Records
 =============================
 
 For each host you plan to make part of a Phabricator cluster, go to the
 {nav Almanac} application and create a **device** record. For guidance on this
 application, see @{article:Almanac User Guide}.
 
 Add **interfaces** to each device record so Phabricator can tell how to
 connect to these hosts. Normally, you'll add one HTTP interface (usually on
 port 80) and one SSH interface (by default, on port 2222) to each device:
 
 For example, if you are building a two-host repository cluster, you may end
 up with records that look like these:
 
   - Device: `repo001.mycompany.net`
     - Interface: `123.0.0.1:2222`
     - Interface: `123.0.0.1:80`
-  - Device: `repo002.mycopmany.net`
+  - Device: `repo002.mycompany.net`
     - Interface: `123.0.0.2:2222`
     - Interface: `123.0.0.2:80`
 
 Note that these hosts will normally run two `sshd` ports: the standard `sshd`
 which you connect to to operate and administrate the host, and the special
 Phabricator `sshd` that you connect to to clone and push repositories.
 
 You should specify the Phabricator `sshd` port, **not** the standard `sshd`
 port.
 
 If you're using **unique** SSH keys for each device, continue to the next step.
 
 If you're using **shared** SSH keys, create a third device with no interfaces,
 like `keywarden.mycompany.net`. This device will just be used as a container to
 hold the trusted SSH key and is not a real device.
 
 NOTE: Do **not** create a **service** record yet. Today, service records become
 active immediately once they are created, and you haven't set things up yet.
 
 
 Generate and Trust SSH Keys
 ===========================
 
 Next, you need to generate or upload SSH keys and mark them as trusted. Marking
 a key as trusted gives it tremendous power.
 
 If you're using **unique** SSH keys, upload or generate a key for each
 individual device from the device detail screen in the Almanac web UI. Save the
 private keys for the next step.
 
 If you're using a **shared** SSH key, upload or generate a single key for
 the keywarden device from the device detail screen in the Almanac web UI.
 Save the private key for the next step.
 
 Regardless of how many keys you generated, take the key IDs from the tables
 in the web UI and run this command from the command line for each key, to mark
 each key as trusted:
 
 ```
 phabricator/ $ ./bin/almanac trust-key --id <key-id-1>
 phabricator/ $ ./bin/almanac trust-key --id <key-id-2>
 ...
 ```
 
 The warnings this command emits are serious. The private keys are now trusted,
 and allow any user or device possessing them to sign requests that bypass
 policy checks without requiring additional credentials. Guard them carefully!
 
 If you need to revoke trust for a key later, use `untrust-key`:
 
 ```
 phabricator/ $ ./bin/almanac untrust-key --id <key-id>
 ```
 
 Once the keys are trusted, continue to the next step.
 
 
 Install Phabricator
 ===================
 
 If you haven't already, install Phabricator on each device you plan to enroll
 in the cluster. Cluster repository devices must provide services over both HTTP
 and SSH, so you need to install and configure both a webserver and a
 Phabricator `sshd` on these hosts.
 
 Generally, you will follow whatever process you otherwise use when installing
 Phabricator.
 
 NOTE: Do not start the daemons on the new devices yet. They won't work properly
 until you've finished configuring things.
 
 Once Phabricator is installed, you can enroll the devices in the cluster by
 registering them.
 
 
 Register Devices
 ================
 
 To register a host as an Almanac device, use `bin/almanac register`.
 
 If you are using **unique** keys, run it like this:
 
 ```
 $ ./bin/almanac register \
   --device <device> \
   --private-key <key>
 ```
 
 For example, you might run this command on `repo001` when using unique keys:
 
 ```
 $ ./bin/almanac register \
   --device repo001.mycompany.net \
   --private-key /path/to/private.key
 ```
 
 If you are using a **shared** key, this will be a little more complicated
 because you need to override some checks that are intended to prevent mistakes.
 Use the `--identify-as` flag to choose a device identity:
 
 ```
 $ ./bin/almanac register \
   --device <keywarden-device> \
   --private-key <key> \
   --identify-as <actual-device>
 ```
 
 For example, you might run this command on `repo001` when using a shared key:
 
 ```
 $ ./bin/almanac register
   --device keywarden.mycompany.net \
   --private-key /path/to/private-key \
   --identify-as repo001.mycompany.net
 ```
 
 In particular, note that `--device` is always the **trusted** device associated
 with the trusted key. The `--identify-as` flag allows several different hosts
 to share the same key but still identify as different devices.
 
 The overall effect of the `bin/almanac` command is to copy identity and key
 files into `phabricator/conf/keys/`. You can inspect the results by examining
 that directory. The helper script just catches potential mistakes and makes
 sure the process is completed correctly.
 
 Note that a copy of the active private key is stored in the `conf/keys/`
 directory permanently.
 
 When converting a host into a cluster host, you may need to revisit
 @{article:Diffusion User Guide: Repository Hosting} and double check the `sudo`
 permission for the host. In particular, cluster hosts need to be able to run
 `ssh` via `sudo` so they can read the device private key.
 
 
 Next Steps
 ==========
 
 Now that devices are registered, you can build cluster services from them.
 Return to the relevant cluster service documentation to continue:
 
   - build repository clusters with @{article:Cluster: Repositories};
   - return to @{article:Clustering Introduction}; or
   - review the Almanac application with @{article:Almanac User Guide}.
diff --git a/src/docs/user/cluster/cluster_notifications.diviner b/src/docs/user/cluster/cluster_notifications.diviner
index 3dd4e9d90..3cdeec3c3 100644
--- a/src/docs/user/cluster/cluster_notifications.diviner
+++ b/src/docs/user/cluster/cluster_notifications.diviner
@@ -1,174 +1,171 @@
 @title Cluster: Notifications
 @group cluster
 
 Configuring Phabricator to use multiple notification servers.
 
 Overview
 ========
 
-WARNING: This feature is a very early prototype; the features this document
-describes are mostly speculative fantasy.
-
 You can run multiple notification servers. The advantages of doing this
 are:
 
   - you can completely survive the loss of any subset so long as one
     remains standing; and
   - performance and capacity may improve.
 
 This configuration is relatively simple, but has a small impact on availability
 and does nothing to increase resitance to data loss.
 
 
 Clustering Design Goals
 =======================
 
 Notification clustering aims to restore service automatically after the loss
 of some nodes. It does **not** attempt to guarantee that every message is
 delivered.
 
 Notification messages provide timely information about events, but they are
 never authoritative and never the only way for users to learn about events.
 For example, if a notification about a task update is not delivered, the next
 page you load will still show the notification in your notification menu.
 
 Generally, Phabricator works fine without notifications configured at all, so
 clustering assumes that losing some messages during a disruption is acceptable.
 
 
 How Clustering Works
 ====================
 
 Notification clustering is very simple: notification servers relay every
 message they receive to a list of peers.
 
 When you configure clustering, you'll run multiple servers and tell them that
 the other servers exist. When any server receives a message, it retransmits it
 to all the severs it knows about.
 
 When a server is lost, clients will automatically reconnect after a brief
 delay. They may lose some notifications while their client is reconnecting,
 but normally this should only last for a few seconds.
 
 
 Configuring Aphlict
 ===================
 
 To configure clustering on the server side, add a `cluster` key to your
 Aphlict configuration file. For more details about configuring Aphlict,
 see @{article:Notifications User Guide: Setup and Configuration}.
 
 The `cluster` key should contain a list of `"admin"` server locations. Every
 message the server receives will be retransmitted to all nodes in the list.
 
 The server is smart enough to avoid sending messages in a cycle, and to avoid
 sending messages to itself. You can safely list every server you run in the
 configuration file, including the current server.
 
 You do not need to configure servers in an acyclic graph or only list //other//
 servers: just list everything on every server and Aphlict will figure things
 out from there.
 
 A simple example with two servers might look like this:
 
 ```lang=json, name="aphlict.json (Cluster)"
 {
   ...
   "cluster": [
     {
       "host": "notify001.mycompany.com",
       "port": 22281,
       "protocol": "http"
     },
     {
       "host": "notify002.mycompany.com",
       "port": 22281,
       "protocol": "http"
     }
   ]
   ...
 }
 ```
 
 
 Configuring Phabricator
 =======================
 
 To configure clustering on the client side, add every service you run to
 `notification.servers`. Generally, this will be twice as many entries as
 you run actual servers, since each server runs a `"client"` service and an
 `"admin"` service.
 
 A simple example with the two servers above (providing four total services)
 might look like this:
 
 ```lang=json, name="notification.servers (Cluster)"
 [
   {
     "type": "client",
     "host": "notify001.mycompany.com",
     "port": 22280,
     "protocol": "https"
   },
   {
     "type": "client",
     "host": "notify002.mycompany.com",
     "port": 22280,
     "protocol": "https"
   },
   {
     "type": "admin",
     "host": "notify001.mycompany.com",
     "port": 22281,
     "protocol": "http"
   },
   {
     "type": "admin",
     "host": "notify002.mycompany.com",
     "port": 22281,
     "protocol": "http"
   }
 ]
 ```
 
 If you put all of the `"client"` servers behind a load balancer, you would
 just list the load balancer and let it handle pulling nodes in and out of
 service.
 
 ```lang=json, name="notification.servers (Cluster + Load Balancer)"
 [
   {
     "type": "client",
     "host": "notify-lb.mycompany.com",
     "port": 22280,
     "protocol": "https"
   },
   {
     "type": "admin",
     "host": "notify001.mycompany.com",
     "port": 22281,
     "protocol": "http"
   },
   {
     "type": "admin",
     "host": "notify002.mycompany.com",
     "port": 22281,
     "protocol": "http"
   }
 ]
 ```
 
 Notification hosts do not need to run any additional services, although they
 are free to do so. The notification server generally consumes few resources
 and is resistant to most other loads on the machine, so it's reasonable to
 overlay these on top of other services wherever it is convenient.
 
 
 Next Steps
 ==========
 
 Continue by:
 
   - reviewing notification configuration with
     @{article:Notifications User Guide: Setup and Configuration}; or
   - returning to @{article:Clustering Introduction}.
diff --git a/src/docs/user/cluster/cluster_repositories.diviner b/src/docs/user/cluster/cluster_repositories.diviner
index 35508b081..07203f03e 100644
--- a/src/docs/user/cluster/cluster_repositories.diviner
+++ b/src/docs/user/cluster/cluster_repositories.diviner
@@ -1,492 +1,487 @@
 @title Cluster: Repositories
 @group cluster
 
 Configuring Phabricator to use multiple repository hosts.
 
 Overview
 ========
 
-WARNING: This feature is a very early prototype; the features this document
-describes are mostly speculative fantasy.
-
 If you use Git, you can deploy Phabricator with multiple repository hosts,
 configured so that each host is readable and writable. The advantages of doing
 this are:
 
   - you can completely survive the loss of repository hosts;
   - reads and writes can scale across multiple machines; and
   - read and write performance across multiple geographic regions may improve.
 
 This configuration is complex, and many installs do not need to pursue it.
 
 This configuration is not currently supported with Subversion or Mercurial.
 
 
 How Reads and Writes Work
 =========================
 
 Phabricator repository replicas are multi-master: every node is readable and
 writable, and a cluster of nodes can (almost always) survive the loss of any
 arbitrary subset of nodes so long as at least one node is still alive.
 
 Phabricator maintains an internal version for each repository, and increments
 it when the repository is mutated.
 
 Before responding to a read, replicas make sure their version of the repository
 is up to date (no node in the cluster has a newer version of the repository).
 If it isn't, they block the read until they can complete a fetch.
 
 Before responding to a write, replicas obtain a global lock, perform the same
 version check and fetch if necessary, then allow the write to continue.
 
 Additionally, repositories passively check other nodes for updates and
 replicate changes in the background. After you push a change to a repositroy,
 it will usually spread passively to all other repository nodes within a few
 minutes.
 
 Even if passive replication is slow, the active replication makes acknowledged
 changes sequential to all observers: after a write is acknowledged, all
 subsequent reads are guaranteed to see it. The system does not permit stale
 reads, and you do not need to wait for a replication delay to see a consistent
 view of the repository no matter which node you ask.
 
 
 HTTP vs HTTPS
 =============
 
 Intracluster requests (from the daemons to repository servers, or from
 webservers to repository servers) are permitted to use HTTP, even if you have
 set `security.require-https` in your configuration.
 
 It is common to terminate SSL at a load balancer and use plain HTTP beyond
 that, and the `security.require-https` feature is primarily focused on making
 client browser behavior more convenient for users, so it does not apply to
 intracluster traffic.
 
 Using HTTP within the cluster leaves you vulnerable to attackers who can
 observe traffic within a datacenter, or observe traffic between datacenters.
 This is normally very difficult, but within reach for state-level adversaries
 like the NSA.
 
 If you are concerned about these attackers, you can terminate HTTPS on
 repository hosts and bind to them with the "https" protocol. Just be aware that
 the `security.require-https` setting won't prevent you from making
 configuration mistakes, as it doesn't cover intracluster traffic.
 
 Other mitigations are possible, but securing a network against the NSA and
 similar agents of other rogue nations is beyond the scope of this document.
 
 
 Repository Hosts
 ================
 
 Repository hosts must run a complete, fully configured copy of Phabricator,
 including a webserver. They must also run a properly configured `sshd`.
 
 If you are converting existing hosts into cluster hosts, you may need to
 revisit @{article:Diffusion User Guide: Repository Hosting} and make sure
 the system user accounts have all the necessary `sudo` permissions. In
 particular, cluster devices need `sudo` access to `ssh` so they can read
 device keys.
 
 Generally, these hosts will run the same set of services and configuration that
 web hosts run. If you prefer, you can overlay these services and put web and
 repository services on the same hosts. See @{article:Clustering Introduction}
 for some guidance on overlaying services.
 
 When a user requests information about a repository that can only be satisfied
 by examining a repository working copy, the webserver receiving the request
 will make an HTTP service call to a repository server which hosts the
 repository to retrieve the data it needs. It will use the result of this query
 to respond to the user.
 
 
 Setting up a Cluster Services
 =============================
 
 To set up clustering, first register the devices that you want to use as part
 of the cluster with Almanac. For details, see @{article:Cluster: Devices}.
 
 NOTE: Once you create a service, new repositories will immediately allocate
 on it. You may want to disable repository creation during initial setup.
 
 Once the hosts are registered as devices, you can create a new service in
 Almanac:
 
   - First, register at least one device according to the device clustering
     instructions.
   - Create a new service of type **Phabricator Cluster: Repository** in
     Almanac.
   - Bind this service to all the interfaces on the device or devices.
   - For each binding, add a `protocol` key with one of these values:
     `ssh`, `http`, `https`.
 
 For example, a service might look like this:
 
   - Service: `repos001.mycompany.net`
     - Binding: `repo001.mycompany.net:80`, `protocol=http`
     - Binding: `repo001.mycompany.net:2222`, `protocol=ssh`
 
 The service itself has a `closed` property. You can set this to `true` to
 disable new repository allocations on this service (for example, if it is
 reaching capacity).
 
 
 Migrating to Clustered Services
 ===============================
 
 To convert existing repositories on an install into cluster repositories, you
 will generally perform these steps:
 
   - Register the existing host as a cluster device.
   - Configure a single host repository service using //only// that host.
 
 This puts you in a transitional state where repositories on the host can work
 as either on-host repositories or cluster repositories. You can move forward
 from here slowly and make sure services still work, with a quick path back to
 safety if you run into trouble.
 
 To move forward, migrate one repository to the service and make sure things
 work correctly. If you run into issues, you can back out by migrating the
 repository off the service.
 
 To migrate a repository onto a cluster service, use this command:
 
 ```
 $ ./bin/repository clusterize <repository> --service <service>
 ```
 
 To migrate a repository back off a service, use this command:
 
 ```
 $ ./bin/repoistory clusterize <repository> --remove-service
 ```
 
 This command only changes how Phabricator connects to the repository; it does
 not move any data or make any complex structural changes.
 
 When Phabricator needs information about a non-clustered repository, it just
 runs a command like `git log` directly on disk. When Phabricator needs
 information about a clustered repository, it instead makes a service call to
 another server, asking that server to run `git log` instead.
 
 In a single-host cluster the server will make this service call to itself, so
 nothing will really change. But this //is// an effective test for most
 possible configuration mistakes.
 
 If your canary repository works well, you can migrate the rest of your
 repositories when ready (you can use `bin/repository list` to quickly get a
 list of all repository monograms).
 
 Once all repositories are migrated, you've reached a stable state and can
 remain here as long as you want. This state is sufficient to convert daemons,
 SSH, and web services into clustered versions and spread them across multiple
 machines if those goals are more interesting.
 
 Obviously, your single-device "cluster" will not be able to survive the loss of
 the single repository host, but you can take as long as you want to expand the
 cluster and add redundancy.
 
 After creating a service, you do not need to `clusterize` new repositories:
 they will automatically allocate onto an open service.
 
 When you're ready to expand the cluster, continue below.
 
 
 Expanding a Cluster
 ===================
 
 To expand an existing cluster, follow these general steps:
 
   - Register new devices in Almanac.
   - Add bindings to the new devices to the repository service, also in Almanac.
   - Start the daemons on the new devices.
 
 For instructions on configuring and registering devices, see
 @{article:Cluster: Devices}.
 
 As soon as you add active bindings to a service, Phabricator will begin
 synchronizing repositories and sending traffic to the new device. You do not
 need to copy any repository data to the device: Phabricator will automatically
 synchronize it.
 
 If you have a large amount of repository data, you may want to help this
 process along by copying the repository directory from an existing cluster
 device before bringing the new host online. This is optional, but can reduce
 the amount of time required to fully synchronize the cluster.
 
 You do not need to synchronize the most up-to-date data or stop writes during
 this process. For example, loading the most recent backup snapshot onto the new
 device will substantially reduce the amount of data that needs to be
 synchronized.
 
 
 Contracting a Cluster
 =====================
 
 To reduce the size of an existing cluster, follow these general steps:
 
   - Disable the bindings from the service to the dead device in Almanac.
 
 If you are removing a device because it failed abruptly (or removing several
 devices at once) it is possible that some repositories will have lost all their
 leaders. See "Loss of Leaders" below to understand and resolve this.
 
 
 Monitoring Services
 ===================
 
 You can get an overview of repository cluster status from the
 {nav Config > Repository Servers} screen. This table shows a high-level
 overview of all active repository services.
 
 **Repos**: The number of repositories hosted on this service.
 
 **Sync**: Synchronization status of repositories on this service. This is an
 at-a-glance view of service health, and can show these values:
 
   - **Synchronized**: All nodes are fully synchronized and have the latest
     version of all repositories.
   - **Partial**: All repositories either have at least two leaders, or have
     a very recent write which is not expected to have propagated yet.
   - **Unsynchronized**: At least one repository has changes which are
     only available on one node and were not pushed very recently. Data may
     be at risk.
   - **No Repositories**: This service has no repositories.
   - **Ambiguous Leader**: At least one repository has an ambiguous leader.
 
 If this screen identifies problems, you can drill down into repository details
 to get more information about them. See the next section for details.
 
 
 Monitoring Repositories
 =======================
 
 You can get a more detailed view the current status of a specific repository on
 cluster devices in {nav Diffusion > (Repository) > Manage Repository > Cluster
 Configuration}.
 
 This screen shows all the configured devices which are hosting the repository
 and the available version on that device.
 
 **Version**: When a repository is mutated by a push, Phabricator increases
 an internal version number for the repository. This column shows which version
 is on disk on the corresponding device.
 
 After a change is pushed, the device which received the change will have a
 larger version number than the other devices. The change should be passively
 replicated to the remaining devices after a brief period of time, although this
 can take a while if the change was large or the network connection between
 devices is slow or unreliable.
 
 You can click the version number to see the corresponding push logs for that
 change. The logs contain details about what was changed, and can help you
 identify if replication is slow because a change is large or for some other
 reason.
 
 **Writing**: This shows that the device is currently holding a write lock. This
 normally means that it is actively receiving a push, but can also mean that
 there was a write interruption. See "Write Interruptions" below for details.
 
 **Last Writer**: This column identifies the user who most recently pushed a
 change to this device. If the write lock is currently held, this user is
 the user whose change is holding the lock.
 
 **Last Write At**: When the most recent write started. If the write lock is
 currently held, this shows when the lock was acquired.
 
 
-
-
 Cluster Failure Modes
 =====================
 
 There are three major cluster failure modes:
 
   - **Write Interruptions**: A write started but did not complete, leaving
     the disk state and cluster state out of sync.
   - **Loss of Leaders**: None of the devices with the most up-to-date data
     are reachable.
   - **Ambiguous Leaders**: The internal state of the repository is unclear.
 
 Phabricator can detect these issues, and responds by freezing the repository
 (usually preventing all reads and writes) until the issue is resolved. These
 conditions are normally rare and very little data is at risk, but Phabricator
 errs on the side of caution and requires decisions which may result in data
 loss to be confirmed by a human.
 
 The next sections cover these failure modes and appropriate responses in
 more detail. In general, you will respond to these issues by assessing the
 situation and then possibly choosing to discard some data.
 
 
 Write Interruptions
 ===================
 
 A repository cluster can be put into an inconsistent state by an interruption
 in a brief window during and immediately after a write. This looks like this:
 
   - A change is pushed to a server.
   - The server acquires a write lock and begins writing the change.
   - During or immediately after the write, lightning strikes the server
     and destroys it.
 
 Phabricator can not commit changes to a working copy (stored on disk) and to
 the global state (stored in a database) atomically, so there is necessarily a
 narrow window between committing these two different states when some tragedy
 can befall a server, leaving the global and local views of the repository state
 possibly divergent.
 
 In these cases, Phabricator fails into a frozen state where further writes
 are not permitted until the failure is investigated and resolved. When a
 repository is frozen in this way it remains readable.
 
 You can use the monitoring console to review the state of a frozen repository
 with a held write lock. The **Writing** column will show which device is
 holding the lock, and whoever is named in the **Last Writer** column may be
 able to help you figure out what happened by providing more information about
 what they were doing and what they observed.
 
 Because the push was not acknowledged, it is normally safe to resolve this
 issue by demoting the device. Demoting the device will undo any changes
 committed by the push, and they will be lost forever.
 
 However, the user should have received an error anyway, and should not expect
 their push to have worked. Still, data is technically at risk and you may want
 to investigate further and try to understand the issue in more detail before
 continuing.
 
 There is no way to explicitly keep the write, but if it was committed to disk
 you can recover it manually from the working copy on the device (for example,
 by using `git format-patch`) and then push it again after recovering.
 
 If you demote the device, the in-process write will be thrown away, even if it
 was complete on disk. To demote the device and release the write lock, run this
 command:
 
 ```
 phabricator/ $ ./bin/repository thaw <repository> --demote <device>
 ```
 
 {icon exclamation-triangle, color="yellow"} Any committed but unacknowledged
 data on the device will be lost.
 
 
 Loss of Leaders
 ===============
 
 A more straightforward failure condition is the loss of all servers in a
 cluster which have the most up-to-date copy of a repository. This looks like
 this:
 
   - There is a cluster setup with two devices, X and Y.
   - A new change is pushed to server X.
   - Before the change can propagate to server Y, lightning strikes server X
     and destroys it.
 
 Here, all of the "leader" devices with the most up-to-date copy of the
 repository have been lost. Phabricator will freeze the repository refuse to
 serve requests because it can not serve it consistently, and can not accept new
 writes without data loss.
 
 The most straightforward way to resolve this issue is to restore any leader to
 service. The change will be able to replicate to other devices once a leader
 comes back online.
 
 If you are unable to restore a leader or unsure that you can restore one
 quickly, you can use the monitoring console to review which changes are
 present on the leaders but not present on the followers by examining the
 push logs.
 
 If you are comfortable discarding these changes, you can instruct Phabricator
 that it can forget about the leaders in two ways: disable the service bindings
 to all of the leader devices so they are no longer part of the cluster, or use
 `bin/repository thaw` to `--demote` the leaders explicitly.
 
 If you do this, **you will lose data**. Either action will discard any changes
 on the affected leaders which have not replicated to other devices in the
 cluster.
 
 To remove a device from the cluster, disable all of the bindings to it
 in Almanac, using the web UI.
 
 {icon exclamation-triangle, color="red"} Any data which is only present on
 the disabled device will be lost.
 
 To demote a device without removing it from the cluster, run this command:
 
 ```
 phabricator/ $ ./bin/repository thaw rXYZ --demote repo002.corp.net
 ```
 
 {icon exclamation-triangle, color="red"} Any data which is only present on
 **this** device will be lost.
 
 
 Ambiguous Leaders
 =================
 
 Repository clusters can also freeze if the leader devices are ambiguous. This
 can happen if you replace an entire cluster with new devices suddenly, or
 make a mistake with the `--demote` flag. This generally arises from some kind
 of operator error, like this:
 
   - Someone accidentally uses `bin/repository thaw ... --demote` to demote
     every device in a cluster.
   - Someone accidentally deletes all the version information for a repository
     from the database by making a mistake with a `DELETE` or `UPDATE` query.
   - Someone accidentally disable all of the devices in a cluster, then add
     entirely new ones before repositories can propagate.
 
 When Phabricator can not tell which device in a cluster is a leader, it freezes
 the cluster because it is possible that some devices have less data and others
 have more, and if it choses a leader arbitrarily it may destroy some data
 which you would prefer to retain.
 
 To resolve this, you need to tell Phabricator which device has the most
 up-to-date data and promote that device to become a leader. If you know all
 devices have the same data, you are free to promote any device.
 
 If you promote a device, **you may lose data** if you promote the wrong device
 and some other device really had more up-to-date data. If you want to double
 check, you can examine the working copies on disk before promoting by
 connecting to the machines and using commands like `git log` to inspect state.
 
 Once you have identified a device which has data you're happy with, use
 `bin/repository thaw` to `--promote` the device. The data on the chosen
 device will become authoritative:
 
 ```
 phabricator/ $ ./bin/repository thaw rXYZ --promote repo002.corp.net
 ```
 
 {icon exclamation-triangle, color="red"} Any data which is only present on
 **other** devices will be lost.
 
 
 Backups
 ======
 
 Even if you configure clustering, you should still consider retaining separate
 backup snapshots. Replicas protect you from data loss if you lose a host, but
 they do not let you rewind time to recover from data mutation mistakes.
 
 If something issues a `--force` push that destroys branch heads, the mutation
 will propagate to the replicas.
 
 You may be able to manually restore the branches by using tools like the
 Phabricator push log or the Git reflog so it is less important to retain
 repository snapshots than database snapshots, but it is still possible for
 data to be lost permanently, especially if you don't notice the problem for
 some time.
 
 Retaining separate backup snapshots will improve your ability to recover more
 data more easily in a wider range of disaster situations.
 
 
 Next Steps
 ==========
 
 Continue by:
 
   - returning to @{article:Clustering Introduction}.
diff --git a/src/docs/user/cluster/cluster_ssh.diviner b/src/docs/user/cluster/cluster_ssh.diviner
new file mode 100644
index 000000000..940ef98b0
--- /dev/null
+++ b/src/docs/user/cluster/cluster_ssh.diviner
@@ -0,0 +1,47 @@
+@title Cluster: SSH Servers
+@group cluster
+
+Configuring Phabricator to use multiple SSH servers.
+
+Overview
+========
+
+You can run Phabricator on multiple SSH servers. The advantages of doing this
+are:
+
+  - you can completely survive the loss of multiple SSH hosts.
+
+This configuration is simple, but you must configure repositories first. For
+details, see @{article:Cluster: Repositories}.
+
+SSH servers accept SSH requests from commands like `git clone` and relay them
+to hosts that can serve the requests.
+
+
+Adding SSH Hosts
+================
+
+After configuring repositories in cluster mode, you can add more web hosts
+at any time.
+
+First, deploy the Phabricator software and configuration to a host, then
+register the host as a cluster device if it is not already registered (for
+help, see @{article:Cluster: Devices}.
+
+Once the host is registered, start the SSH server, and then add the host to the
+SSH load balancer pool.
+
+Phabricator SSH servers are stateless, so you can pull them in and out of
+production freely.
+
+You may also want to run web services on these hosts, since the service is very
+similar to SSH, also stateless, and it may be simpler to load balance the
+services together. For details, see @{cluster: Web Servers}.
+
+
+Next Steps
+==========
+
+Continue by:
+
+  - returning to @{article:Clustering Introduction}.
diff --git a/src/docs/user/cluster/cluster_webservers.diviner b/src/docs/user/cluster/cluster_webservers.diviner
index 95699a829..744696af6 100644
--- a/src/docs/user/cluster/cluster_webservers.diviner
+++ b/src/docs/user/cluster/cluster_webservers.diviner
@@ -1,42 +1,45 @@
 @title Cluster: Web Servers
 @group cluster
 
 Configuring Phabricator to use multiple web servers.
 
 Overview
 ========
 
-WARNING: This feature is a very early prototype; the features this document
-describes are mostly speculative fantasy.
-
 You can run Phabricator on multiple web servers. The advantages of doing this
 are:
 
   - you can completely survive the loss of multiple web hosts; and
   - performance and capacity may improve.
 
 This configuration is simple, but you must configure repositories first. For
 details, see @{article:Cluster: Repositories}.
 
 
 Adding Web Hosts
 ================
 
 After configuring repositories in cluster mode, you can add more web hosts
-at any time: simply deploy the Phabricator software and configuration to a
-host, start the web server, and then add the host to the load balancer pool.
+at any time.
+
+First, deploy the Phabricator software and configuration to a host, then
+register the host as a cluster device if it is not already registered (for
+help, see @{article:Cluster: Devices}.
+
+Once the host is registered, start the web server, and then add the host to the
+load balancer pool.
 
 Phabricator web servers are stateless, so you can pull them in and out of
 production freely.
 
 You may also want to run SSH services on these hosts, since the service is very
 similar to HTTP, also stateless, and it may be simpler to load balance the
-services together.
+services together. For details, see @{cluster:SSH Servers}.
 
 
 Next Steps
 ==========
 
 Continue by:
 
   - returning to @{article:Clustering Introduction}.
diff --git a/src/docs/user/userguide/diffusion.diviner b/src/docs/user/userguide/diffusion.diviner
index f3fa53da3..7c48d7a63 100644
--- a/src/docs/user/userguide/diffusion.diviner
+++ b/src/docs/user/userguide/diffusion.diviner
@@ -1,116 +1,94 @@
 @title Diffusion User Guide
 @group userguide
 
-Guide to Diffusion, the Phabricator repository browser.
+Guide to Diffusion, the Phabricator application for hosting and browsing
+repositories.
 
-= Overview =
+Overview
+========
 
-Diffusion is a repository browser which allows you to explore source code in a
-Subversion, Git, or Mercurial repository. It is somewhat similar to software
-like Trac and GitWeb.
+Diffusion allows you to create repositories so that you can browse them from
+the web and interact with them from other applications.
 
-Diffusion can either import a read-only copy of repositories hosted somewhere
-else (for example, from GitHub, Bitbucket or existing hosting) or host
-repositories within Phabricator. Hosted repositories support a variety of
-triggers and access controls.
+Diffusion can host repositories locally, or observe existing remote
+repositories which are hosted elsewhere (for example, on GitHub, Bitbucket, or
+other existing hosting). Both types of repositories can be browsed and
+interacted with, but hosted repositories support some additional triggers
+and access controls which are not available for observed repositories.
 
 Diffusion is integrated with the other tools in the Phabricator suite. For
 instance:
 
   - when you commit Differential revisions to a tracked repository, they are
     automatically updated and linked to the corresponding commits;
   - you can add Herald rules to notify you about commits that match certain
     rules;
   - for hosted repositories, Herald can enforce granular access control rules;
   - in all the tools, commit names are automatically linked.
 
-= Adding Repositories =
+The remainder of this document walks through creating, configuring, and
+managing repositories.
+
+
+Adding Repositories
+===================
 
 Repository administration is accomplished through Diffusion. You can use the
-web interface in Diffusion to import an external repository, or create a new
+web interface in Diffusion to observe an external repository or create a new
 hosted repository.
 
   - For hosted repositories, make sure you go through the setup instructions
     in @{article:Diffusion User Guide: Repository Hosting} first.
   - For all repositories, you'll need to be running the daemons. If you have
     not set them up yet, see @{article:Managing Daemons with phd}.
 
-By default, you must be an administrator to create a new repository.
-
-= Repository Callsigns and Commit Names =
+By default, you must be an administrator to create a new repository. You can
+change this in the application settings.
 
-Each repository is identified by a "callsign", which is a short uppercase string
-like "P" (for Phabricator) or "ARC" (for Arcanist).
 
-Each repository must have a unique callsign. Callsigns must be unique within
-an install but do not need to be globally unique, so you are free to use the
-single-letter callsigns for brevity. For example, Facebook uses "E" for the
-Engineering repository, "O" for the Ops repository, "Y" for a Yum package
-repository, and so on, while Phabricator uses "P", "ARC", "PHU" for libphutil,
-and "J" for Javelin. Keeping callsigns brief will make them easier to use, and
-the use of one-character callsigns is recommended if they are reasonably
-evocative and you have no more than 26 tracked repositories.
+Managing Repositories
+=====================
 
-The primary goal of callsigns is to namespace commits to SVN repositories: if
-you use multiple SVN repositories, each repository has a revision 1, revision 2,
-etc., so referring to them by number alone is ambiguous. However, even for Git
-they impart additional information to human readers and allow parsers to detect
-that something is a commit name with high probability (and allow distinguishing
-between multiple copies of a repository).
+Diffusion repositories have an array of configurable options and behaviors. For
+details on the available options and guidance on managing and administrating
+repositories, see @{article:Diffusion User Guide: Managing Repositories}.
 
-Diffusion uses this callsign and information about the commit itself to generate
-a commit name, like "rE12345" or "rP28146171ce1278f2375e3646a1e1ea3fd56fc5a3".
-The "r" stands for "revision". It is followed by the repository callsign, and
-then a VCS-specific commit identifier (for SVN, the commit number; for Git and
-Mercurial, the commit hash). When writing the name of a Git commit you may
-abbreviate the hash, but note that hash collisions are probable for short prefix
-lengths. See this post on the LKML for a historical explanation of Git's
-occasional internal use of 7-character hashes:
+Repositories can also be managed via the API. For an overview on using the
+API to create and edit repositories, see
+@{article:Diffusion User Guide: Repositories API}.
 
-https://lkml.org/lkml/2010/10/28/287
 
-Because 7-character hashes are likely to collide for even moderately large
-repositories, Diffusion generally uses either a 16-character prefix (which makes
-collisions very unlikely) or the full 40-character hash (which makes collisions
-astronomically unlikely).
+Repository Clustering
+=====================
 
-= Running Diffusion Daemons =
+Phabricator repository hosts can be set up in a cluster configuration so you
+can lose hosts with minimal downtime and data loss. This is an advanced feature
+which most installs do not need to pursue.
 
-In most cases, it is sufficient to run:
+To get started with clustering, see @{article:Clustering Introduction}. For
+details on repository clustering, see @{article:Cluster: Repositories}.
 
-  phabricator/bin/ $ ./phd start
-
-...to start the daemons. For a more in-depth explanation of `phd` and daemons,
-see @{article:Managing Daemons with phd}.
-
-NOTE: If you have an unusually large install with multiple web frontends, see
-notes in @{article:Managing Daemons with phd}.
-
-You can use the repository detail screen and the Daemon Console to monitor the
-daemons and their progress importing the repository. Small repositories should
-import quickly, while larger repositories may take some time. Commits should
-begin appearing in Diffusion within a few minutes for all but the largest
-repositories.
 
-= Next Steps =
+Next Steps
+==========
 
 Continue by:
 
   - learning how to creating a symbol index at
     @{article:Diffusion User Guide: Symbol Indexes}; or
   - setting up repository hosting with
     @{article:Diffusion User Guide: Repository Hosting}; or
   - managing repository hooks with
     @{article:Diffusion User Guide: Commit Hooks}; or
   - understanding daemons in more detail with
     @{article:Managing Daemons with phd}.
 
 If you're having trouble getting things working, these topic guides may be
 helpful:
 
   - get details about automatically closing tasks and revisions in response
     to commits in @{article:Diffusion User Guide: Autoclose}; or
   - understand how Phabricator updates repositories with
     @{article:Diffusion User Guide: Repository Updates}; or
   - fix issues with repository imports with
     @{article:Troubleshooting Repository Imports}.
diff --git a/src/docs/user/userguide/diffusion_api.diviner b/src/docs/user/userguide/diffusion_api.diviner
new file mode 100644
index 000000000..8ddd6d4bf
--- /dev/null
+++ b/src/docs/user/userguide/diffusion_api.diviner
@@ -0,0 +1,182 @@
+@title Diffusion User Guide: Repositories API
+@group userguide
+
+Managing repositories with the API.
+
+Overview
+========
+
+You can create and update Diffusion repositories using the Conduit API. This
+may be useful if you have a large number of existing repositories you want
+to import or apply bulk actions to.
+
+For an introduction to Conduit, see @{article:Conduit API Overview}.
+
+In general, you'll use these API methods:
+
+  - `diffusion.repository.edit`: Create and edit repositorie.
+  - `diffusion.uri.edit`: Create and edit repository URIs to configure
+    observation, mirroring, and cloning.
+
+To create a repository, you'll generally do this:
+
+  - Call `diffusion.repository.edit` to create a new object and configure
+    basic information.
+  - Optionally, call `diffusion.uri.edit` to add URIs to observe or mirror.
+  - Call `diffusion.repository.edit` to activate the repository.
+
+This workflow mirrors the workflow from the web UI. The remainder of this
+document walks through this workflow in greater detail.
+
+
+Create a Repository
+===================
+
+To create a repository, call `diffusion.repository.edit`, providing any
+properties you want to set. For simplicity these examples will use the
+builtin `arc call-conduit` client, but you can use whatever Conduit client
+you prefer.
+
+When creating a repository, you must provide a `vcs` transaction to choose
+a repository type, one of: `git`, `hg` or `svn`.
+
+You must also provide a `name`.
+
+Other properties are optional. Review the Conduit method documentation from the
+web UI for an exhaustive list.
+
+```
+$ echo '{
+  "transactions": [
+    {
+      "type": "vcs",
+      "value": "git"
+    },
+    {
+      "type": "name",
+      "value": "Poetry"
+    }
+  ]
+}' | arc call-conduit diffusion.repository.edit
+```
+
+If things work, you should get a result that looks something like this:
+
+```lang=json
+{
+  ...
+  "response": {
+    "object": {
+      "id": 1,
+      "phid": "PHID-REPO-7vm42oayez2rxcmpwhuv"
+    },
+    ...
+  }
+  ...
+}
+```
+
+If so, your new repository has been created. It hasn't been activated yet so
+it will not show up in the default repository list, but you can find it in the
+web UI by browsing to {nav Diffusion > All Repositories}.
+
+Continue to the next step to configure URIs.
+
+
+Configure URIs
+==============
+
+Now that the repository exists, you can add URIs to it. This is optional,
+and if you're creating a //hosted// repository you may be able to skip this
+step.
+
+However, if you want Phabricator to observe an existing remote, you'll
+configure it here by adding a URI in "Observe" mode. Use the PHID from the
+previous step to identify the repository you want to add a URI to, and call
+`diffusion.uri.edit` to create a new URI in Observe mode for the repository.
+
+You need to provide a `repository` to add the URI to, and the `uri` itself.
+
+To add the URI in Observe mode, provide an `io` transaction selecting
+`observe` mode.
+
+You may also want to provide a `credential`.
+
+```
+$ echo '{
+  "transactions": [
+    {
+      "type": "repository",
+      "value": "PHID-REPO-7vm42oayez2rxcmpwhuv"
+    },
+    {
+      "type": "uri",
+      "value": "https://github.com/epriestley/poems.git"
+    },
+    {
+      "type": "io",
+      "value": "observe"
+    }
+  ]
+}' | arc call-conduit diffusion.uri.edit
+```
+
+You should get a response that looks something like this:
+
+```lang=json
+{
+  ...
+  "response": {
+    "object": {
+      "id": 1,
+      "phid": "PHID-RURI-zwtho5o7h3m6rjzgsgrh"
+    },
+    ...
+  }
+  ...
+}
+```
+
+If so, your URI has been created. You can review it in the web UI, under
+{nav Manage Repository > URIs}.
+
+When satisfied, continue to the next step to activate the repository.
+
+
+Activate the Repository
+=======================
+
+Now that any URIs have been configured, activate the repository with another
+call to `diffusion.repository.edit`. This time, modify the existing repostitory
+instead of creating a new one:
+
+```
+$ echo '{
+  "objectIdentifier": "PHID-REPO-7vm42oayez2rxcmpwhuv",
+  "transactions": [
+    {
+      "type": "status",
+      "value": "active"
+    }
+  ]
+}' | arc call-conduit diffusion.repository.edit
+```
+
+If that goes through cleanly, you should be all set. You can review the
+repository from the web UI.
+
+
+Editing Repositories
+====================
+
+To edit an existing repository, apply changes normally with
+`diffusion.repository.edit`. For more details on using edit endpoints, see
+@{article:Conduit API: Using Edit Endpoints}.
+
+
+Next Steps
+==========
+
+Continue by:
+
+  - returning to the @{article:Diffusion User Guide}.
diff --git a/src/docs/user/userguide/diffusion_managing.diviner b/src/docs/user/userguide/diffusion_managing.diviner
new file mode 100644
index 000000000..f6ab0997c
--- /dev/null
+++ b/src/docs/user/userguide/diffusion_managing.diviner
@@ -0,0 +1,385 @@
+@title Diffusion User Guide: Managing Repositories
+@group userguide
+
+Guide to configuring and managing repositories in Diffusion.
+
+Overview
+========
+
+After you create a new repository in Diffusion or select **Manage Repository**
+from the main screen if an existing repository, you'll be taken to the
+repository management interface for that repository.
+
+On this interface, you'll find many options which allow you to configure the
+behavior of a repository. This document walks through the options.
+
+Basics
+======
+
+The **Basics** section of the management interface allows you to configure
+the repository name, description, and identifiers. You can also activate or
+deactivate the repository here, and configure a few other miscellaneous
+settings.
+
+Basics: Name
+============
+
+The repository name is a human-readable primary name for the repository. It
+does not need to be unique
+
+Because the name is not unique and does not have any meaningful restrictions,
+it's fairly ambiguous and isn't very useful as an identifier. The other basic
+information (primarily callsigns and short names) gives you control over
+repository identifiers.
+
+
+Basics: Callsigns
+=================
+
+Each repository can optionally be identified by a "callsign", which is a short
+uppercase string like "P" (for Phabricator) or "ARC" (for Arcanist).
+
+The primary goal of callsigns is to namespace commits to SVN repositories: if
+you use multiple SVN repositories, each repository has a revision 1, revision 2,
+etc., so referring to them by number alone is ambiguous.
+
+However, even for Git and Mercurial they impart additional information to human
+readers and allow parsers to detect that something is a commit name with high
+probability (and allow distinguishing between multiple copies of a repository).
+
+Configuring a callsign can make interacting with a commonly-used repository
+easier, but you may not want to bother assigning one to every repository if you
+have some similar, templated, or rarely-used repositories.
+
+If you choose to assign a callsign to a repository, it must be unique within an
+install but do not need to be globally unique, so you are free to use the
+single-letter callsigns for brevity. For example, Facebook uses "E" for the
+Engineering repository, "O" for the Ops repository, "Y" for a Yum package
+repository, and so on, while Phabricator uses "P", "ARC", "PHU" for libphutil,
+and "J" for Javelin. Keeping callsigns brief will make them easier to use, and
+the use of one-character callsigns is encouraged if they are reasonably
+evocative.
+
+If you configure a callsign like `XYZ`, Phabricator will activate callsign URIs
+and activate the callsign identifier (like `rXYZ`) for the repository. These
+more human-readable identifiers can make things a little easier to interact
+with.
+
+
+Basics: Short Name
+==================
+
+Each repository can optionally have a unique short name. Short names must be
+unique and have some minor restrictions to make sure they are unambiguous and
+appropriate for use as directory names and in URIs.
+
+
+Basics: Description
+===================
+
+You may optionally provide a brief (or, at your discretion, excruciatingly
+long) human-readable description of the repository. This description will be
+shown on the main repository page.
+
+You can also create a `README` file at the repository root (or in any
+subdirectory) to provide information about the repository. These formats are
+supported:
+
+| File Name         | Rendered As...
+|-------------------|---------------
+| `README`          | Plain Text
+| `README.txt`      | Plain Text
+| `README.remarkup` | Remarkup
+| `README.md`       | Remarkup
+| `README.rainbow`  | Rainbow
+
+
+Basics: Encoding
+================
+
+Before content from the repository can be shown in the web UI or embedded in
+other contexts like email, it must be converted to UTF-8.
+
+Most source code is written in UTF-8 or a subset of UTF-8 (like plain ASCII)
+already, so everything will work fine. The majority of repositories do not need
+to adjust this setting.
+
+If your repository is primarily written in some other encoding, specify it here
+so Phabricator can convert from it properly when reading content to embed in
+a webpage or email.
+
+
+Basics: Dangerous Changes
+=========================
+
+By default, repositories are protected against dangerous changes. Dangerous
+changes are operations which rewrite or destroy repository history (for
+example, by deleting or rewriting branches). Normally, these take the form
+of `git push --force` or similar.
+
+It is normally a good idea to leave this protection enabled because most
+scalable workflows rarely rewrite repository history and it's easy to make
+mistakes which are expensive to correct if this protection is disabled.
+
+If you do occasionally need to rewite published history, you can treat this
+option like a safety: disable it, perform required rewrites, then enable it
+again.
+
+If you fully disable this at the repository level, you can still use Herald to
+selectively protect certain branches or grant this power to a limited set of
+users.
+
+This option is only available in Git and Mercurial, because it is impossible
+to make dangerous changes in Subversion.
+
+This option has no effect if a repository is not hosted because Phabricator
+can not prevent dangerous changes in a remote repository it is merely
+observing.
+
+
+Basics: Deactivate Repository
+=============================
+
+Repositories can be deactivated. Deactivating a repository has these effects:
+
+  - the repository will no longer be updated;
+  - users will no longer be able to clone/fetch/checkout the repository;
+  - users will no longer be able to push to the repository; and
+  - the repository will be hidden from view in default queries.
+
+When repositories are created for the first time, they are deactivated. This
+gives you an opportuinty to customize settings, like adjusting policies or
+configuring a URI to observe. You must activate a repository before it will
+start working normally.
+
+
+Basics: Delete Repository
+=========================
+
+Repositories can not be deleted from the web UI, so this option is always
+disabled. Clicking it gives you information about how to delete a repository.
+
+Repositories can only be deleted from the command line, with `bin/remove`:
+
+```
+$ ./bin/remove destroy <repository>
+```
+
+WARNING: This command will issue you a dire warning about the severity of the
+action you are taking. Heed this warning. You are **strongly discouraged** from
+destroying repositories. Instead, deactivate them.
+
+
+Policies
+========
+
+The **Policies** section of the management interface allows you to review and
+manage repository access policies.
+
+You can configure granular access policies for each repository to control who
+can view, clone, administate, and push to the repository.
+
+
+Policies: View
+==============
+
+The view policy for a repository controls who can view the repository from
+the web UI and clone, fetch, or check it out from Phabricator.
+
+Users who can view a repository can also access the "Manage" interface to
+review information about the repository and examine the edit history, but can
+not make any changes.
+
+
+Policies: Edit
+==============
+
+The edit policy for a repository controls who can change repository settings
+using the "Manage" interface. In essence, this is permission to administrate
+the repository.
+
+You must be able to view a repository to edit it.
+
+You do not need this permission to push changes to a repository.
+
+
+Policies: Push
+==============
+
+The push policy for a repository controls who can push changes to the
+repository.
+
+This policy has no effect if Phabricator is not hosting the repository, because
+it can not control who is allowed to make changes to a remote repository it is
+merely observing.
+
+You must also be able to view a repository to push to it.
+
+You do not need to be able to edit a repository to push to it.
+
+Further restrictions on who can push (and what they can push) can be configured
+for hosted repositories with Herald, which allows you to write more
+sophisticated rules that evaluate when Phabricator receives a push. To get
+started with Herald, see @{article:Herald User Guide}.
+
+Additionally, Git and Mercurial repositories have a setting which allows
+you to **Prevent Dangerous Changes**. This setting is enabled by default and
+will prevent any users from pushing changes which rewrite or destroy history.
+
+
+URIs
+====
+
+The **URIs** panel allows you to add and manage URIs which Phabricator will
+fetch from, serve from, and push to.
+
+These options are covered in detail in @{article:Diffusion User Guide: URIs}.
+
+
+Staging Area
+============
+
+The **Staging Area** panel configures staging areas, used to make proposed
+changes available to build and continuous integration systems.
+
+For more details, see @{article:Harbormaster User Guide}.
+
+
+Automation
+==========
+
+The **Automation** panel configures support for allowing Phabricator to make
+writes directly to the repository, so that it can perform operations like
+automatically landing revisions from the web UI.
+
+For details on repository automation, see
+@{article:Drydock User Guide: Repository Automation}.
+
+
+Symbols
+======
+
+The **Symbols** panel allows you to customize how symbols (like class and
+function names) are linked when viewing code in the repository, and when
+viewing revisions which propose code changes to the repository.
+
+To take advantage of this feature, you need to do additional work to build
+symbol indexes. For details on configuring and populating symbol indexes, see
+@{article:User Guide: Symbol Indexes}.
+
+
+Branches
+========
+
+The **Branches** panel allows you to configure how Phabricator interacts with
+branches.
+
+This panel is not available for Subversion repositories, because Subversion
+does not have formal branches.
+
+You can configure **Default Branch**. This controls which branch is shown by
+default in the UI. If no branch is provided, Phabricator will use `master` in
+Git and `default` in Mercurial.
+
+If you want Diffusion to ignore some branches in the repository, you can
+configure **Track Only**. Other branches will be ignored. If you do not specify
+any branches, all branches are tracked.
+
+When specifying branches, you should enter one branch name per line. You can
+use regular expressions to match branches by wrapping an expression in
+`regexp(...)`. For example:
+
+| Example | Effect |
+|---------|--------|
+| `master` | Track only `master`.
+| `regexp(/^release-/)` | Track all branches which start with `release-`.
+| `regexp(/^(?!temp-)/)` | Do not track branches which start with `temp-`.
+
+
+Actions
+======
+
+The **Actions** panel can configure notifications and publishing behavior.
+
+Normally, Phabricator publishes notifications when it discovers new commits.
+You can disable publishing for a repository by turning off **Publish/Noitfy**.
+This will disable notifications, feed, and Herald (including audits and build
+plans) for this repository.
+
+When Phabricator discovers a new commit, it can automatically close associated
+revisions and tasks. If you don't want Phabricator to close objects when it
+discovers new commits, disable **Autoclose** for the repository.
+
+
+Repository Identifiers and Names
+================================
+
+Repositories have several short identifiers which you can use to refer to the
+repository. For example, if you use command-line administrative tools to
+interact with a repository, you'll provide one of these identifiers:
+
+```
+$ ./bin/repository update <identifier>
+```
+
+The identifiers available for a repository depend on which options are
+configured. Each repository may have several identifiers:
+
+  - An **ID** identifier, like `R123`. This is available for all repositories.
+  - A **callsign** identifier, like `rXY`. This is available for repositories
+    with a callsign.
+  - A **short name** identifier, like `xylophone`. This is available for
+    repositories with a short name.
+
+All three identifiers can be used to refer to the repository in cases where
+the intent is unambiguous, but only the first two forms work in ambiguous
+contexts.
+
+For example, if you type `R123` or `rXY` into a comment, Phabricator will
+recognize them as references to the repository. If you type `xylophone`, it
+assumes you mean the word "xylophone".
+
+Only the `R123` identifier is immutable: the others can be changed later by
+adjusting the callsign or short name for the repository.
+
+
+Commit Identifiers
+==================
+
+Diffusion uses repository identifiers and information about the commit itself
+to generate globally unique identifers for each commit, like `rE12345`.
+
+Each commit may have several identifiers:
+
+  - A repository **ID** identifier, like `R123:abcdef123...`.
+  - A repository **callsign** identifier, like `rXYZabcdef123...`. This only
+    works if a repository has a callsign.
+  - Any unique prefix of the commit hash.
+
+Git and Mercurial use commit hashes to identify commits, and Phabricator will
+recognize a commit if the hash prefix is unique and sufficiently long. Commit
+hashes qualified with a repository identifier must be at least 5 characters
+long; unqualified commit hashes must be at least 7 characters long.
+
+In Subversion, commit identifiers are sequential integers and prefixes can not
+be used to identify them.
+
+When rendering the name of a Git or Mercurial commit hash, Phabricator tends to
+shorten it to 12 characters. This "short length" is relatively long compared to
+Git itself (which often uses 7 characters). See this post on the LKML for a
+historical explanation of Git's occasional internal use of 7-character hashes:
+
+https://lkml.org/lkml/2010/10/28/287
+
+Because 7-character hashes are likely to collide for even moderately large
+repositories, Diffusion generally uses either a 12-character prefix (which makes
+collisions very unlikely) or the full 40-character hash (which makes collisions
+astronomically unlikely).
+
+
+Next Steps
+==========
+
+Continue by:
+
+  - returning to the @{article:Diffusion User Guide}.
diff --git a/src/docs/user/userguide/diffusion_updates.diviner b/src/docs/user/userguide/diffusion_updates.diviner
index eb01b8c9b..7012e6228 100644
--- a/src/docs/user/userguide/diffusion_updates.diviner
+++ b/src/docs/user/userguide/diffusion_updates.diviner
@@ -1,123 +1,123 @@
 @title Diffusion User Guide: Repository Updates
 @group userguide
 
 Explains how Diffusion updates repositories to discover new changes.
 
 Overview
 ========
 
 When Phabricator is configured to import repositories which are hosted
 elsewhere, it needs to poll those repositories for changes. If it polls too
 frequently, it can create too much load locally and on remote services. If it
 polls too rarely, it may take a long time for commits to show up in the web
 interface.
 
 This document describes the rules around polling and how to understand and
 adjust the behavior. In general:
 
   - Phabricator chooses a default poll interval based on repository
     activity. These intervals range from every 15 seconds (for active
     repositories) to every 6 hours (for repositories with no commits in two
     months).
   - If you use `arc` to push commits, or you host repositories on Phabricator,
     repositories automatically update after changes are pushed.
   - If you don't use `arc` and your repository is hosted elsewhere, this
     document describes ways you can make polling more responsive.
 
 
 Default Behavior
 ================
 
 By default, Phabricator determines how frequently to poll repositories by
 examining how long it has been since the last commit. In most cases this is
 fairly accurate and produces good behavior. In particular, it automatically
 reduces the polling frequency for rarely-used repositories. This dramatically
 reduces load for installs with a large number of inactive repositories, which
 is common.
 
 For repositories with activity in the last 3 days, we wait 1 second for every
 10 minutes without activity. The table below has some examples.
 
 | Time Since Commit | Poll Interval    |
 |-------------------|------------------|
 | //Minimum//       | 15 seconds       |
 | 6h                | about 30 seconds |
 | 12h               | about 1 minute   |
 | 1 day             | about 2 minutes  |
 | 2 days            | about 5 minutes  |
 | 3 days            | about 7 minutes  |
 
 This means that you may need to wait about 2 minutes for the first commit to
 be imported in the morning, and about 5 minutes after a long weekend, but other
 commits to active repositories should usually be recognized in 30 seconds or
 less.
 
 For repositories with no activity in the last 3 days, we wait longer between
 updates (1 second for every 4 minutes without activity). The table below has
 some examples.
 
 | Time Since Commit | Poll Interval    |
 |-------------------|------------------|
 | 4 days            | about 30 minutes |
 | 7 days            | about 45 minutes |
 | 10 days           | about 1 hour     |
 | 20 days           | about 2 hours    |
 | 30 days           | about 3 hours    |
 | //Maximum//       | 6 hours          |
 
 You can find the exact default poll frequency of a repository in
 Diffusion > (Choose a Repository) > Edit Repository, under "Update Frequency".
 You can also see the time when the repository was last updated in this
 interface.
 
 Repositories that are currently importing are always updated at the minimum
 update frequency so the import finishes as quickly as possible.
 
 
 Triggering Repository Updates
 =============================
 
 If you want Phabricator to update a repository more quickly than the default
 update frequency (for example, because you just pushed a commit to it), you can
 tell Phabricator that it should schedule an update as soon as possible.
 
 There are several ways to do this:
 
   - If you push changes with `arc land` or `arc commit`, this will be done
     for you automatically. These commits should normally be recognized within
     a few seconds.
   - If your repository is hosted on Phabricator, this will also be done for you
     automatically.
   - You can schedule an update from the web interface, in Diffusion >
-    (Choose a Repository) > Edit Repository > Update Now.
+    (Choose a Repository) > Manage Repository > Status > Update Now.
   - You can make a call to the Conduit API method `diffusion.looksoon`. This
     hints to Phabricator that it should poll a repository as soon as it can.
     All of the other mechanisms do this under the hood.
 
 In particular, you may be able to add a commit hook to your external repository
 which calls `diffusion.looksoon`. This should make an external repository about
 as responsive as a hosted repository.
 
 If a repository has an update scheduled, the Diffusion > (Choose a
 Repository) > Edit Repository interface will show that the repository is
 prioritized and will be updated soon.
 
 
 Troubleshooting Updates
 =======================
 
 You can manually run a repository update from the command line to troubleshoot
 issues, using the `--trace` flag to get full details:
 
-  phabricator/ $ ./bin/repository update --trace <callsign>
+  phabricator/ $ ./bin/repository update --trace <repository>
 
 To catch potential issues with permissions, run this command as the same user
 that the daemons run as.
 
 Next Steps
 ==========
 
 Continue by:
 
   - troubleshooting in greater depth with
     @{article:Troubleshooting Repository Imports}.
diff --git a/src/docs/user/userguide/diffusion_uris.diviner b/src/docs/user/userguide/diffusion_uris.diviner
index b560f4ab3..7377250f2 100644
--- a/src/docs/user/userguide/diffusion_uris.diviner
+++ b/src/docs/user/userguide/diffusion_uris.diviner
@@ -1,275 +1,305 @@
 @title Diffusion User Guide: URIs
 @group userguide
 
 Guide to configuring repository URIs for fetching, cloning and mirroring.
 
 Overview
 ========
 
-WARNING: This document describes a feature which is still under development,
-and is not necessarily accurate or complete.
-
 Phabricator can create, host, observe, mirror, proxy, and import repositories.
 For example, you can:
 
 **Host Repositories**: Phabricator can host repositories locally. Phabricator
 maintains the writable master version of the repository, and you can push and
 pull the repository. This is the most straightforward kind of repository
 configuration, and similar to repositories on other services like GitHub or
 Bitbucket.
 
 **Observe Repositories**: Phabricator can create a copy of an repository which
 is hosted elsewhere (like GitHub or Bitbucket) and track updates to the remote
 repository. This will create a read-only copy of the repository in Phabricator.
 
 **Mirror Repositories**: Phabricator can publish any repository to mirrors,
-updating the mirrors as changes are made to the repository. This works with
-both local hosted repositories and remote repositories that Phabricator is
-observing.
+overwiting them with an exact copy of the repository that stays up to date as
+the source changes. This works with both local repositories that Phabricator is
+hosting and remote repositories that Phabricator is observing.
 
 **Proxy Repositories**: If you are observing a repository, you can allow users
 to read Phabricator's copy of the repository. Phabricator supports granular
 read permissions, so this can let you open a private repository up a little
 bit in a flexible way.
 
 **Import Repositories**: If you have a repository elsewhere that you want to
 host on Phabricator, you can observe the remote repository first, then turn
 the tracking off once the repository fully synchronizes. This allows you to
 copy an existing repository and begin hosting it in Phabricator.
 
 You can also import repositories by creating an empty hosted repository and
 then pushing everything to the repository directly.
 
 You configure the behavior of a Phabricator repository by adding and
 configuring URIs and marking them to be fetched from, mirrored to, clonable,
 and so on. By configuring all the URIs that a repository should interact with
 and expose to users, you configure the read, write, and mirroring behavior
 of the repository.
 
 The remainder of this document walks through this configuration in greater
 detail.
 
 
 Host a Repository
 =================
 
 You can create new repositories that Phabricator will host, like you would
 create repositories on services like GitHub or Bitbucket. Phabricator will
 serve a read-write copy of the repository and you can clone it from Phabricator
 and push changes to Phabricator.
 
 If you haven't already, you may need to configure Phabricator for hosting
 before you can create your first hosted repository. For a detailed guide,
 see @{article:Diffusion User Guide: Repository Hosting}.
 
 This is the default mode for new repositories. To host a repository:
 
   - Create a new repository.
   - Activate it.
 
 Phabricator will create an empty repository and allow you to fetch from it and
 push to it.
 
 
 Observe a Repository
 ====================
 
 If you have an existing repository hosted on another service (like GitHub,
 Bitbucket, or a private server) that you want to work with in Phabricator,
 you can configure Phabricator to observe it.
 
 When observing a repository, Phabricator will keep track of changes in the
 remote repository and allow you to browse and interact with the repository from
 the web UI in Diffusion and other applications, but you can continue hosting it
 elsewhere.
 
 To observe a repository:
 
   - Create a new repository, but don't activate it yet.
   - Add the remote URI you want to observe as a repository URI.
   - Set the **I/O Type** for the URI to **Observe**.
   - If necessary, configure a credential.
   - Activate the repository.
 
 Phabricator will perform an initial import of the repository, creating a local
 read-only copy. Once this process completes, it will continue keeping track of
 changes in the remote, fetching them, and reflecting them in the UI.
 
 
 Mirror a Repository
 ===================
 
 NOTE: Mirroring is not supported in Subversion.
 
 You can create a read-only mirror of an existing repository. Phabricator will
-push all changes made to the repository to the mirror.
+continuously publish the state of the source repository to the mirror, creating
+an exact copy.
 
 For example, if you have a repository hosted in Phabricator that you want to
 mirror to GitHub, you can configure Phabricator to automatically maintain the
 mirror. This is how the upstream repositories are set up.
 
+The mirror copy must be read-only for users because any writes made to the
+mirror will be undone when Phabricator updates it. The mirroring process copies
+the entire repository state exactly, so the remote state will be completely
+replaced with an exact copy of the source repository. This may remove or
+destroy information. Normally, you should only mirror to an empty repository.
+
 You can mirror any repository, even if Phabricator is only observing it and not
 hosting it directly.
 
 To begin mirroring a repository:
 
   - Create a hosted or observed repository by following the relevant
     instructions above.
   - Add the remote URI you want to mirror to as a repository URI.
   - Set the **I/O Type** for the URI to **Mirror**.
   - If necessary, configure a credential.
 
 To stop mirroring:
 
   - Disable the mirror URI; or
   - Change the **I/O Type** for the URI to **None**.
 
 
 Import a Repository
 ===================
 
 If you have an existing repository that you want to move so it is hosted on
 Phabricator, there are three ways to do it:
 
 **Push Everything**: //(Git, Mercurial)// Create a new empty hosted repository
 according to the instructions above. Once the empty repository initializes,
 push your entire existing repository to it.
 
 **Observe First**: //(Git, Mercurial)// Observe the existing repository first,
 according to the instructions above. Once Phabricator's copy of the repository
 is fully synchronized, change the **I/O Type** for the **Observe** URI to
 **None** to stop fetching changes from the remote.
 
 By default, this will automatically make Phabricator's copy of the repository
 writable, and you can begin pushing to it. If you've adjusted URI
 configuration away from the defaults, you may need to set at least one URI
 to **Read/Write** mode so you can push to it.
 
 **Copy on Disk**: //(Git, Mercurial, Subversion)// Create a new empty hosted
 repository according to the instructions above, but do not activate it yet.
 
 Using the **Storage** tab, find the location of the repository's working copy
 on disk, and place a working copy of the repository you wish to import there.
 
 For Git and Mercurial, use a bare working copy for best results.
 
 This is the only way to import a Subversion repository because only the master
 copy of the repository has history.
 
 Once you've put a working copy in the right place on disk, activate the
 repository.
 
 
+Builtin Clone URIs
+==================
+
+By default, Phabricator automatically exposes and activates HTTP, HTTPS and
+SSH clone URIs by examining configuration.
+
+**HTTP**: The `http://` clone URI will be available if these conditions are
+satisfied:
+
+  - `diffusion.allow-http-auth` must be enabled.
+  - The repository must be a Git or Mercurial repository.
+  - `security.require-https` must be disabled.
+
+**HTTPS**: The `https://` clone URI will be available if these conditions are
+satisfied:
+
+  - `diffusion.allow-http-auth` must be enabled.
+  - The repository must be a Git or Mercurial repository.
+  - The `phabricator.base-uri` protocol must be `https://`.
+
+**SSH**: The `ssh://` or `svn+ssh://` clone URI will be available if these
+conditions are satisfied:
+
+  - `phd.user` must be configured.
+
+
 Customizing Displayed Clone URIs
 ================================
 
 If you have an unusual configuration and want the UI to offers users specific
 clone URIs other than the URIs that Phabricator serves or interacts with, you
 can add those URIs with the **I/O Type** set to **None** and then set their
 **Display Type** to **Always**.
 
 Likewise, you can set the **Display Type** of any URIs you do //not// want
 to be visible to **Never**.
 
 This allows you to precisely configure which clone URIs are shown to users for
 a repository.
 
 
 Reference: I/O Types
 ====================
 
 This section details the available **I/O Type** options for URIs.
 
 Each repository has some **builtin** URIs. These are URIs hosted by Phabricator
 itself. The modes available for each URI depend primarily on whether it is a
 builtin URI or not.
 
 **Default**: This setting has Phabricator guess the correct option for the
 URI.
 
 For **builtin** URIs, the default behavior is //Read/Write// if the repository
 is hosted, and //Read-Only// if the repository is observed.
 
 For custom URIs, the default type is //None// because we can not automatically
 guess if you want to ignore, observe, or mirror a URI and //None// is the
 safest default.
 
 **Observe**: Phabricator will observe this repository and regularly fetch any
 changes made to it to a local read-only copy.
 
 You can not observe builtin URIs because reading a repository from itself
 does not make sense.
 
 You can not add a URI in Observe mode if an existing builtin URI is in
 //Read/Write// mode, because this would mean the repository had two different
 authorities: the observed remote copy and the hosted local copy. Take the
 other URI out of //Read/Write// mode first.
 
 **Mirror**: Phabricator will push any changes made to this repository to the
 remote URI, keeping a read-only mirror hosted at that URI up to date.
 
 This works for both observed and hosted repositories.
 
 This option is not available for builtin URIs because it does not make sense
 to mirror a repository to itself.
 
 It is possible to mirror a repository to another repository that is also
 hosted by Phabricator by adding that other repository's URI, although this is
 silly and probably very rarely of any use.
 
 **None**: Phabricator will not fetch changes from or push changes to this URI.
 For builtin URIs, it will not let users fetch changes from or push changes to
 this URI.
 
 You can use this mode to turn off an Observe URI after an import, stop a Mirror
 URI from updating, or to add URIs that you're only using to customize which
 clone URIs are displayed to the user but don't want Phabricator to interact
 with directly.
 
 **Read Only**: Phabricator will serve the repository from this URI in read-only
 mode. Users will be able to fetch from it but will not be able to push to it.
 
 Because Phabricator must be able to serve the repository from URIs configured
 in this mode, this option is only available for builtin URIs.
 
 **Read/Write**: Phabricator will serve the repository from this URI in
 read/write mode. Users will be able to fetch from it and push to it.
 
 URIs can not be set into this mode if another URI is set to //Observe// mode,
 because that would mean the repository had two different authorities: the
 observed remote copy and the hosted local copy. Take the other URI out of
 //Observe// mode first.
 
 Because Phabricator must be able to serve the repository from URIs configured
 in this mode, this option is only available for builtin URIs.
 
 
 Reference: Display Types
 ========================
 
 This section details the available **Display Type** options for URIs.
 
 **Default**: Phabricator will guess the correct option for the URI. It
 guesses based on the configured **I/O Type** and whether the URI is
 **builtin** or not.
 
 For //Observe//, //Mirror// and //None// URIs, the default is //Never//.
 
 For builtin URIs in //Read Only// or //Read/Write// mode, the most
 human-readable URI defaults to //Always// and the others default to //Never//.
 
 **Always**: This URI will be shown to users as a clone/checkout URI. You can
 add URIs in this mode to cutomize exactly what users are shown.
 
 **Never**: This URI will not be shown to users. You can hide less-preferred
 URIs to guide users to the URIs they should be using to interact with the
 repository.
 
 
 Next Steps
 ==========
 
 Continue by:
 
   - configuring Phabricator to host repositories with
     @{article:Diffusion User Guide: Repository Hosting}.
diff --git a/src/infrastructure/customfield/standard/PhabricatorStandardCustomFieldCredential.php b/src/infrastructure/customfield/standard/PhabricatorStandardCustomFieldCredential.php
index c1b6c84de..c2f958f22 100644
--- a/src/infrastructure/customfield/standard/PhabricatorStandardCustomFieldCredential.php
+++ b/src/infrastructure/customfield/standard/PhabricatorStandardCustomFieldCredential.php
@@ -1,150 +1,151 @@
 <?php
 
 final class PhabricatorStandardCustomFieldCredential
   extends PhabricatorStandardCustomField {
 
   public function getFieldType() {
     return 'credential';
   }
 
   public function buildFieldIndexes() {
     $indexes = array();
 
     $value = $this->getFieldValue();
     if (strlen($value)) {
       $indexes[] = $this->newStringIndex($value);
     }
 
     return $indexes;
   }
 
   public function renderEditControl(array $handles) {
     $provides_type = $this->getFieldConfigValue('credential.provides');
     $credential_type = $this->getFieldConfigValue('credential.type');
 
     $all_types = PassphraseCredentialType::getAllProvidesTypes();
     if (!in_array($provides_type, $all_types)) {
       $provides_type = PassphrasePasswordCredentialType::PROVIDES_TYPE;
     }
 
     $credentials = id(new PassphraseCredentialQuery())
       ->setViewer($this->getViewer())
       ->withIsDestroyed(false)
       ->withProvidesTypes(array($provides_type))
       ->execute();
 
     return id(new PassphraseCredentialControl())
+      ->setViewer($this->getViewer())
       ->setLabel($this->getFieldName())
       ->setName($this->getFieldKey())
       ->setCaption($this->getCaption())
       ->setAllowNull(!$this->getRequired())
       ->setCredentialType($credential_type)
       ->setValue($this->getFieldValue())
       ->setError($this->getFieldError())
       ->setOptions($credentials);
   }
 
   public function getRequiredHandlePHIDsForPropertyView() {
     $value = $this->getFieldValue();
     if ($value) {
       return array($value);
     }
     return array();
   }
 
   public function renderPropertyViewValue(array $handles) {
     $value = $this->getFieldValue();
     if ($value) {
       return $handles[$value]->renderLink();
     }
     return null;
   }
 
   public function validateApplicationTransactions(
     PhabricatorApplicationTransactionEditor $editor,
     $type,
     array $xactions) {
 
     $errors = parent::validateApplicationTransactions(
       $editor,
       $type,
       $xactions);
 
     $ok = PassphraseCredentialControl::validateTransactions(
       $this->getViewer(),
       $xactions);
 
     if (!$ok) {
       foreach ($xactions as $xaction) {
         $errors[] = new PhabricatorApplicationTransactionValidationError(
           $type,
           pht('Invalid'),
           pht(
             'The selected credential does not exist, or you do not have '.
             'permission to use it.'),
           $xaction);
         $this->setFieldError(pht('Invalid'));
       }
     }
 
     return $errors;
   }
 
   public function getApplicationTransactionRequiredHandlePHIDs(
     PhabricatorApplicationTransaction $xaction) {
     $phids = array();
     $old = $xaction->getOldValue();
     $new = $xaction->getNewValue();
     if ($old) {
       $phids[] = $old;
     }
     if ($new) {
       $phids[] = $new;
     }
     return $phids;
   }
 
 
   public function getApplicationTransactionTitle(
     PhabricatorApplicationTransaction $xaction) {
     $author_phid = $xaction->getAuthorPHID();
 
     $old = $xaction->getOldValue();
     $new = $xaction->getNewValue();
 
     if ($old && !$new) {
       return pht(
         '%s removed %s as %s.',
         $xaction->renderHandleLink($author_phid),
         $xaction->renderHandleLink($old),
         $this->getFieldName());
     } else if ($new && !$old) {
       return pht(
         '%s set %s to %s.',
         $xaction->renderHandleLink($author_phid),
         $this->getFieldName(),
         $xaction->renderHandleLink($new));
     } else {
       return pht(
         '%s changed %s from %s to %s.',
         $xaction->renderHandleLink($author_phid),
         $this->getFieldName(),
         $xaction->renderHandleLink($old),
         $xaction->renderHandleLink($new));
     }
   }
 
 
   protected function getHTTPParameterType() {
     return new AphrontPHIDHTTPParameterType();
   }
 
   protected function newConduitSearchParameterType() {
     return new ConduitPHIDParameterType();
   }
 
   protected function newConduitEditParameterType() {
     return new ConduitPHIDParameterType();
   }
 
 }
diff --git a/src/infrastructure/env/PhabricatorEnv.php b/src/infrastructure/env/PhabricatorEnv.php
index 60bd9e459..dd8c11e62 100644
--- a/src/infrastructure/env/PhabricatorEnv.php
+++ b/src/infrastructure/env/PhabricatorEnv.php
@@ -1,880 +1,897 @@
 <?php
 
 /**
  * Manages the execution environment configuration, exposing APIs to read
  * configuration settings and other similar values that are derived directly
  * from configuration settings.
  *
  *
  * = Reading Configuration =
  *
  * The primary role of this class is to provide an API for reading
  * Phabricator configuration, @{method:getEnvConfig}:
  *
  *   $value = PhabricatorEnv::getEnvConfig('some.key', $default);
  *
  * The class also handles some URI construction based on configuration, via
  * the methods @{method:getURI}, @{method:getProductionURI},
  * @{method:getCDNURI}, and @{method:getDoclink}.
  *
  * For configuration which allows you to choose a class to be responsible for
  * some functionality (e.g., which mail adapter to use to deliver email),
  * @{method:newObjectFromConfig} provides a simple interface that validates
  * the configured value.
  *
  *
  * = Unit Test Support =
  *
  * In unit tests, you can use @{method:beginScopedEnv} to create a temporary,
  * mutable environment. The method returns a scope guard object which restores
  * the environment when it is destroyed. For example:
  *
  *   public function testExample() {
  *     $env = PhabricatorEnv::beginScopedEnv();
  *     $env->overrideEnv('some.key', 'new-value-for-this-test');
  *
  *     // Some test which depends on the value of 'some.key'.
  *
  *   }
  *
  * Your changes will persist until the `$env` object leaves scope or is
  * destroyed.
  *
  * You should //not// use this in normal code.
  *
  *
  * @task read     Reading Configuration
  * @task uri      URI Validation
  * @task test     Unit Test Support
  * @task internal Internals
  */
 final class PhabricatorEnv extends Phobject {
 
   private static $sourceStack;
   private static $repairSource;
   private static $overrideSource;
   private static $requestBaseURI;
   private static $cache;
   private static $localeCode;
   private static $readOnly;
   private static $readOnlyReason;
 
   const READONLY_CONFIG = 'config';
   const READONLY_UNREACHABLE = 'unreachable';
   const READONLY_SEVERED = 'severed';
   const READONLY_MASTERLESS = 'masterless';
 
   /**
    * @phutil-external-symbol class PhabricatorStartup
    */
   public static function initializeWebEnvironment() {
     self::initializeCommonEnvironment();
   }
 
   public static function initializeScriptEnvironment() {
     self::initializeCommonEnvironment();
 
     // NOTE: This is dangerous in general, but we know we're in a script context
     // and are not vulnerable to CSRF.
     AphrontWriteGuard::allowDangerousUnguardedWrites(true);
 
     // There are several places where we log information (about errors, events,
     // service calls, etc.) for analysis via DarkConsole or similar. These are
     // useful for web requests, but grow unboundedly in long-running scripts and
     // daemons. Discard data as it arrives in these cases.
     PhutilServiceProfiler::getInstance()->enableDiscardMode();
     DarkConsoleErrorLogPluginAPI::enableDiscardMode();
     DarkConsoleEventPluginAPI::enableDiscardMode();
   }
 
 
   private static function initializeCommonEnvironment() {
     PhutilErrorHandler::initialize();
 
     self::resetUmask();
     self::buildConfigurationSourceStack();
 
     // Force a valid timezone. If both PHP and Phabricator configuration are
     // invalid, use UTC.
     $tz = self::getEnvConfig('phabricator.timezone');
     if ($tz) {
       @date_default_timezone_set($tz);
     }
     $ok = @date_default_timezone_set(date_default_timezone_get());
     if (!$ok) {
       date_default_timezone_set('UTC');
     }
 
     // Prepend '/support/bin' and append any paths to $PATH if we need to.
     $env_path = getenv('PATH');
     $phabricator_path = dirname(phutil_get_library_root('phabricator'));
     $support_path = $phabricator_path.'/support/bin';
     $env_path = $support_path.PATH_SEPARATOR.$env_path;
     $append_dirs = self::getEnvConfig('environment.append-paths');
     if (!empty($append_dirs)) {
       $append_path = implode(PATH_SEPARATOR, $append_dirs);
       $env_path = $env_path.PATH_SEPARATOR.$append_path;
     }
     putenv('PATH='.$env_path);
 
     // Write this back into $_ENV, too, so ExecFuture picks it up when creating
     // subprocess environments.
     $_ENV['PATH'] = $env_path;
 
 
     // If an instance identifier is defined, write it into the environment so
     // it's available to subprocesses.
     $instance = self::getEnvConfig('cluster.instance');
     if (strlen($instance)) {
       putenv('PHABRICATOR_INSTANCE='.$instance);
       $_ENV['PHABRICATOR_INSTANCE'] = $instance;
     }
 
     PhabricatorEventEngine::initialize();
 
     // TODO: Add a "locale.default" config option once we have some reasonable
     // defaults which aren't silly nonsense.
     self::setLocaleCode('en_US');
   }
 
   public static function beginScopedLocale($locale_code) {
     return new PhabricatorLocaleScopeGuard($locale_code);
   }
 
   public static function getLocaleCode() {
     return self::$localeCode;
   }
 
   public static function setLocaleCode($locale_code) {
     if (!$locale_code) {
       return;
     }
 
     if ($locale_code == self::$localeCode) {
       return;
     }
 
     try {
       $locale = PhutilLocale::loadLocale($locale_code);
       $translations = PhutilTranslation::getTranslationMapForLocale(
         $locale_code);
 
       $override = self::getEnvConfig('translation.override');
       if (!is_array($override)) {
         $override = array();
       }
 
       PhutilTranslator::getInstance()
         ->setLocale($locale)
         ->setTranslations($override + $translations);
 
       self::$localeCode = $locale_code;
     } catch (Exception $ex) {
       // Just ignore this; the user likely has an out-of-date locale code.
     }
   }
 
   private static function buildConfigurationSourceStack() {
     self::dropConfigCache();
 
     $stack = new PhabricatorConfigStackSource();
     self::$sourceStack = $stack;
 
     $default_source = id(new PhabricatorConfigDefaultSource())
       ->setName(pht('Global Default'));
     $stack->pushSource($default_source);
 
     $env = self::getSelectedEnvironmentName();
     if ($env) {
       $stack->pushSource(
         id(new PhabricatorConfigFileSource($env))
           ->setName(pht("File '%s'", $env)));
     }
 
     $stack->pushSource(
       id(new PhabricatorConfigLocalSource())
         ->setName(pht('Local Config')));
 
     // If the install overrides the database adapter, we might need to load
     // the database adapter class before we can push on the database config.
     // This config is locked and can't be edited from the web UI anyway.
     foreach (self::getEnvConfig('load-libraries') as $library) {
       phutil_load_library($library);
     }
 
     // If custom libraries specify config options, they won't get default
     // values as the Default source has already been loaded, so we get it to
     // pull in all options from non-phabricator libraries now they are loaded.
     $default_source->loadExternalOptions();
 
     // If this install has site config sources, load them now.
     $site_sources = id(new PhutilClassMapQuery())
       ->setAncestorClass('PhabricatorConfigSiteSource')
       ->setSortMethod('getPriority')
       ->execute();
 
     foreach ($site_sources as $site_source) {
       $stack->pushSource($site_source);
     }
 
     $master = PhabricatorDatabaseRef::getMasterDatabaseRef();
     if (!$master) {
       self::setReadOnly(true, self::READONLY_MASTERLESS);
     } else if ($master->isSevered()) {
       $master->checkHealth();
       if ($master->isSevered()) {
         self::setReadOnly(true, self::READONLY_SEVERED);
       }
     }
 
     try {
       $stack->pushSource(
         id(new PhabricatorConfigDatabaseSource('default'))
           ->setName(pht('Database')));
     } catch (AphrontQueryException $exception) {
       // If the database is not available, just skip this configuration
       // source. This happens during `bin/storage upgrade`, `bin/conf` before
       // schema setup, etc.
     }
   }
 
   public static function repairConfig($key, $value) {
     if (!self::$repairSource) {
       self::$repairSource = id(new PhabricatorConfigDictionarySource(array()))
         ->setName(pht('Repaired Config'));
       self::$sourceStack->pushSource(self::$repairSource);
     }
     self::$repairSource->setKeys(array($key => $value));
     self::dropConfigCache();
   }
 
   public static function overrideConfig($key, $value) {
     if (!self::$overrideSource) {
       self::$overrideSource = id(new PhabricatorConfigDictionarySource(array()))
         ->setName(pht('Overridden Config'));
       self::$sourceStack->pushSource(self::$overrideSource);
     }
     self::$overrideSource->setKeys(array($key => $value));
     self::dropConfigCache();
   }
 
   public static function getUnrepairedEnvConfig($key, $default = null) {
     foreach (self::$sourceStack->getStack() as $source) {
       if ($source === self::$repairSource) {
         continue;
       }
       $result = $source->getKeys(array($key));
       if ($result) {
         return $result[$key];
       }
     }
     return $default;
   }
 
   public static function getSelectedEnvironmentName() {
     $env_var = 'PHABRICATOR_ENV';
 
     $env = idx($_SERVER, $env_var);
 
     if (!$env) {
       $env = getenv($env_var);
     }
 
     if (!$env) {
       $env = idx($_ENV, $env_var);
     }
 
     if (!$env) {
       $root = dirname(phutil_get_library_root('phabricator'));
       $path = $root.'/conf/local/ENVIRONMENT';
       if (Filesystem::pathExists($path)) {
         $env = trim(Filesystem::readFile($path));
       }
     }
 
     return $env;
   }
 
 
 /* -(  Reading Configuration  )---------------------------------------------- */
 
 
   /**
    * Get the current configuration setting for a given key.
    *
    * If the key is not found, then throw an Exception.
    *
    * @task read
    */
   public static function getEnvConfig($key) {
     if (isset(self::$cache[$key])) {
       return self::$cache[$key];
     }
 
     if (array_key_exists($key, self::$cache)) {
       return self::$cache[$key];
     }
 
     $result = self::$sourceStack->getKeys(array($key));
     if (array_key_exists($key, $result)) {
       self::$cache[$key] = $result[$key];
       return $result[$key];
     } else {
       throw new Exception(
         pht(
           "No config value specified for key '%s'.",
           $key));
     }
   }
 
 
   /**
    * Get the current configuration setting for a given key. If the key
    * does not exist, return a default value instead of throwing. This is
    * primarily useful for migrations involving keys which are slated for
    * removal.
    *
    * @task read
    */
   public static function getEnvConfigIfExists($key, $default = null) {
     try {
       return self::getEnvConfig($key);
     } catch (Exception $ex) {
       return $default;
     }
   }
 
 
   /**
    * Get the fully-qualified URI for a path.
    *
    * @task read
    */
   public static function getURI($path) {
     return rtrim(self::getAnyBaseURI(), '/').$path;
   }
 
 
   /**
    * Get the fully-qualified production URI for a path.
    *
    * @task read
    */
   public static function getProductionURI($path) {
     // If we're passed a URI which already has a domain, simply return it
     // unmodified. In particular, files may have URIs which point to a CDN
     // domain.
     $uri = new PhutilURI($path);
     if ($uri->getDomain()) {
       return $path;
     }
 
     $production_domain = self::getEnvConfig('phabricator.production-uri');
     if (!$production_domain) {
       $production_domain = self::getAnyBaseURI();
     }
     return rtrim($production_domain, '/').$path;
   }
 
   public static function getAllowedURIs($path) {
     $uri = new PhutilURI($path);
     if ($uri->getDomain()) {
       return $path;
     }
 
     $allowed_uris = self::getEnvConfig('phabricator.allowed-uris');
     $return = array();
     foreach ($allowed_uris as $allowed_uri) {
       $return[] = rtrim($allowed_uri, '/').$path;
     }
 
     return $return;
   }
 
 
   /**
    * Get the fully-qualified production URI for a static resource path.
    *
    * @task read
    */
   public static function getCDNURI($path) {
     $alt = self::getEnvConfig('security.alternate-file-domain');
     if (!$alt) {
       $alt = self::getAnyBaseURI();
     }
     $uri = new PhutilURI($alt);
     $uri->setPath($path);
     return (string)$uri;
   }
 
 
   /**
    * Get the fully-qualified production URI for a documentation resource.
    *
    * @task read
    */
   public static function getDoclink($resource, $type = 'article') {
     $uri = new PhutilURI('https://secure.phabricator.com/diviner/find/');
     $uri->setQueryParam('name', $resource);
     $uri->setQueryParam('type', $type);
     $uri->setQueryParam('jump', true);
     return (string)$uri;
   }
 
 
   /**
    * Build a concrete object from a configuration key.
    *
    * @task read
    */
   public static function newObjectFromConfig($key, $args = array()) {
     $class = self::getEnvConfig($key);
     return newv($class, $args);
   }
 
   public static function getAnyBaseURI() {
     $base_uri = self::getEnvConfig('phabricator.base-uri');
 
     if (!$base_uri) {
       $base_uri = self::getRequestBaseURI();
     }
 
     if (!$base_uri) {
       throw new Exception(
         pht(
           "Define '%s' in your configuration to continue.",
           'phabricator.base-uri'));
     }
 
     return $base_uri;
   }
 
   public static function getRequestBaseURI() {
     return self::$requestBaseURI;
   }
 
   public static function setRequestBaseURI($uri) {
     self::$requestBaseURI = $uri;
   }
 
   public static function isReadOnly() {
     if (self::$readOnly !== null) {
       return self::$readOnly;
     }
     return self::getEnvConfig('cluster.read-only');
   }
 
   public static function setReadOnly($read_only, $reason) {
     self::$readOnly = $read_only;
     self::$readOnlyReason = $reason;
   }
 
   public static function getReadOnlyMessage() {
     $reason = self::getReadOnlyReason();
     switch ($reason) {
       case self::READONLY_MASTERLESS:
         return pht(
           'Phabricator is in read-only mode (no writable database '.
           'is configured).');
       case self::READONLY_UNREACHABLE:
         return pht(
           'Phabricator is in read-only mode (unreachable master).');
       case self::READONLY_SEVERED:
         return pht(
           'Phabricator is in read-only mode (major interruption).');
     }
 
     return pht('Phabricator is in read-only mode.');
   }
 
   public static function getReadOnlyURI() {
     return urisprintf(
       '/readonly/%s/',
       self::getReadOnlyReason());
   }
 
   public static function getReadOnlyReason() {
     if (!self::isReadOnly()) {
       return null;
     }
 
     if (self::$readOnlyReason !== null) {
       return self::$readOnlyReason;
     }
 
     return self::READONLY_CONFIG;
   }
 
 
 /* -(  Unit Test Support  )-------------------------------------------------- */
 
 
   /**
    * @task test
    */
   public static function beginScopedEnv() {
     return new PhabricatorScopedEnv(self::pushTestEnvironment());
   }
 
 
   /**
    * @task test
    */
   private static function pushTestEnvironment() {
     self::dropConfigCache();
     $source = new PhabricatorConfigDictionarySource(array());
     self::$sourceStack->pushSource($source);
     return spl_object_hash($source);
   }
 
 
   /**
    * @task test
    */
   public static function popTestEnvironment($key) {
     self::dropConfigCache();
     $source = self::$sourceStack->popSource();
     $stack_key = spl_object_hash($source);
     if ($stack_key !== $key) {
       self::$sourceStack->pushSource($source);
       throw new Exception(
         pht(
           'Scoped environments were destroyed in a different order than they '.
           'were initialized.'));
     }
   }
 
 
 /* -(  URI Validation  )----------------------------------------------------- */
 
 
   /**
    * Detect if a URI satisfies either @{method:isValidLocalURIForLink} or
    * @{method:isValidRemoteURIForLink}, i.e. is a page on this server or the
    * URI of some other resource which has a valid protocol. This rejects
    * garbage URIs and URIs with protocols which do not appear in the
    * `uri.allowed-protocols` configuration, notably 'javascript:' URIs.
    *
    * NOTE: This method is generally intended to reject URIs which it may be
    * unsafe to put in an "href" link attribute.
    *
    * @param string URI to test.
    * @return bool True if the URI identifies a web resource.
    * @task uri
    */
   public static function isValidURIForLink($uri) {
     return self::isValidLocalURIForLink($uri) ||
            self::isValidRemoteURIForLink($uri);
   }
 
 
   /**
    * Detect if a URI identifies some page on this server.
    *
    * NOTE: This method is generally intended to reject URIs which it may be
    * unsafe to issue a "Location:" redirect to.
    *
    * @param string URI to test.
    * @return bool True if the URI identifies a local page.
    * @task uri
    */
   public static function isValidLocalURIForLink($uri) {
     $uri = (string)$uri;
 
     if (!strlen($uri)) {
       return false;
     }
 
     if (preg_match('/\s/', $uri)) {
       // PHP hasn't been vulnerable to header injection attacks for a bunch of
       // years, but we can safely reject these anyway since they're never valid.
       return false;
     }
 
     // Chrome (at a minimum) interprets backslashes in Location headers and the
     // URL bar as forward slashes. This is probably intended to reduce user
     // error caused by confusion over which key is "forward slash" vs "back
     // slash".
     //
     // However, it means a URI like "/\evil.com" is interpreted like
     // "//evil.com", which is a protocol relative remote URI.
     //
     // Since we currently never generate URIs with backslashes in them, reject
     // these unconditionally rather than trying to figure out how browsers will
     // interpret them.
     if (preg_match('/\\\\/', $uri)) {
       return false;
     }
 
     // Valid URIs must begin with '/', followed by the end of the string or some
     // other non-'/' character. This rejects protocol-relative URIs like
     // "//evil.com/evil_stuff/".
     return (bool)preg_match('@^/([^/]|$)@', $uri);
   }
 
 
   /**
    * Detect if a URI identifies some valid linkable remote resource.
    *
    * @param string URI to test.
    * @return bool True if a URI idenfies a remote resource with an allowed
    *              protocol.
    * @task uri
    */
   public static function isValidRemoteURIForLink($uri) {
     try {
       self::requireValidRemoteURIForLink($uri);
       return true;
     } catch (Exception $ex) {
       return false;
     }
   }
 
 
   /**
    * Detect if a URI identifies a valid linkable remote resource, throwing a
    * detailed message if it does not.
    *
    * A valid linkable remote resource can be safely linked or redirected to.
    * This is primarily a protocol whitelist check.
    *
    * @param string URI to test.
    * @return void
    * @task uri
    */
   public static function requireValidRemoteURIForLink($raw_uri) {
     $uri = new PhutilURI($raw_uri);
 
     $proto = $uri->getProtocol();
     if (!strlen($proto)) {
       throw new Exception(
         pht(
           'URI "%s" is not a valid linkable resource. A valid linkable '.
           'resource URI must specify a protocol.',
           $raw_uri));
     }
 
     $protocols = self::getEnvConfig('uri.allowed-protocols');
     if (!isset($protocols[$proto])) {
       throw new Exception(
         pht(
           'URI "%s" is not a valid linkable resource. A valid linkable '.
           'resource URI must use one of these protocols: %s.',
           $raw_uri,
           implode(', ', array_keys($protocols))));
     }
 
     $domain = $uri->getDomain();
     if (!strlen($domain)) {
       throw new Exception(
         pht(
           'URI "%s" is not a valid linkable resource. A valid linkable '.
           'resource URI must specify a domain.',
           $raw_uri));
     }
   }
 
 
   /**
    * Detect if a URI identifies a valid fetchable remote resource.
    *
    * @param string URI to test.
    * @param list<string> Allowed protocols.
    * @return bool True if the URI is a valid fetchable remote resource.
    * @task uri
    */
   public static function isValidRemoteURIForFetch($uri, array $protocols) {
     try {
       self::requireValidRemoteURIForFetch($uri, $protocols);
       return true;
     } catch (Exception $ex) {
       return false;
     }
   }
 
 
   /**
    * Detect if a URI identifies a valid fetchable remote resource, throwing
    * a detailed message if it does not.
    *
    * A valid fetchable remote resource can be safely fetched using a request
    * originating on this server. This is a primarily an address check against
    * the outbound address blacklist.
    *
    * @param string URI to test.
    * @param list<string> Allowed protocols.
    * @return pair<string, string> Pre-resolved URI and domain.
    * @task uri
    */
   public static function requireValidRemoteURIForFetch(
     $uri,
     array $protocols) {
 
     $uri = new PhutilURI($uri);
 
     $proto = $uri->getProtocol();
     if (!strlen($proto)) {
       throw new Exception(
         pht(
           'URI "%s" is not a valid fetchable resource. A valid fetchable '.
           'resource URI must specify a protocol.',
           $uri));
     }
 
     $protocols = array_fuse($protocols);
     if (!isset($protocols[$proto])) {
       throw new Exception(
         pht(
           'URI "%s" is not a valid fetchable resource. A valid fetchable '.
           'resource URI must use one of these protocols: %s.',
           $uri,
           implode(', ', array_keys($protocols))));
     }
 
     $domain = $uri->getDomain();
     if (!strlen($domain)) {
       throw new Exception(
         pht(
           'URI "%s" is not a valid fetchable resource. A valid fetchable '.
           'resource URI must specify a domain.',
           $uri));
     }
 
     $addresses = gethostbynamel($domain);
     if (!$addresses) {
       throw new Exception(
         pht(
           'URI "%s" is not a valid fetchable resource. The domain "%s" could '.
           'not be resolved.',
           $uri,
           $domain));
     }
 
     foreach ($addresses as $address) {
       if (self::isBlacklistedOutboundAddress($address)) {
         throw new Exception(
           pht(
             'URI "%s" is not a valid fetchable resource. The domain "%s" '.
             'resolves to the address "%s", which is blacklisted for '.
             'outbound requests.',
             $uri,
             $domain,
             $address));
       }
     }
 
     $resolved_uri = clone $uri;
     $resolved_uri->setDomain(head($addresses));
 
     return array($resolved_uri, $domain);
   }
 
 
   /**
    * Determine if an IP address is in the outbound address blacklist.
    *
    * @param string IP address.
    * @return bool True if the address is blacklisted.
    */
   public static function isBlacklistedOutboundAddress($address) {
     $blacklist = self::getEnvConfig('security.outbound-blacklist');
 
     return PhutilCIDRList::newList($blacklist)->containsAddress($address);
   }
 
   public static function isClusterRemoteAddress() {
     $cluster_addresses = self::getEnvConfig('cluster.addresses');
     if (!$cluster_addresses) {
       return false;
     }
 
     $address = idx($_SERVER, 'REMOTE_ADDR');
     if (!$address) {
       throw new Exception(
         pht(
           'Unable to test remote address against cluster whitelist: '.
           'REMOTE_ADDR is not defined.'));
     }
 
     return self::isClusterAddress($address);
   }
 
   public static function isClusterAddress($address) {
     $cluster_addresses = self::getEnvConfig('cluster.addresses');
     if (!$cluster_addresses) {
       throw new Exception(
         pht(
           'Phabricator is not configured to serve cluster requests. '.
           'Set `cluster.addresses` in the configuration to whitelist '.
           'cluster hosts before sending requests that use a cluster '.
           'authentication mechanism.'));
     }
 
     return PhutilCIDRList::newList($cluster_addresses)
       ->containsAddress($address);
   }
 
 /* -(  Internals  )---------------------------------------------------------- */
 
 
   /**
    * @task internal
    */
   public static function envConfigExists($key) {
     return array_key_exists($key, self::$sourceStack->getKeys(array($key)));
   }
 
 
   /**
    * @task internal
    */
   public static function getAllConfigKeys() {
     return self::$sourceStack->getAllKeys();
   }
 
   public static function getConfigSourceStack() {
     return self::$sourceStack;
   }
 
   /**
    * @task internal
    */
   public static function overrideTestEnvConfig($stack_key, $key, $value) {
     $tmp = array();
 
     // If we don't have the right key, we'll throw when popping the last
     // source off the stack.
     do {
       $source = self::$sourceStack->popSource();
       array_unshift($tmp, $source);
       if (spl_object_hash($source) == $stack_key) {
         $source->setKeys(array($key => $value));
         break;
       }
     } while (true);
 
     foreach ($tmp as $source) {
       self::$sourceStack->pushSource($source);
     }
 
     self::dropConfigCache();
   }
 
   private static function dropConfigCache() {
     self::$cache = array();
   }
 
   private static function resetUmask() {
     // Reset the umask to the common standard umask. The umask controls default
     // permissions when files are created and propagates to subprocesses.
 
     // "022" is the most common umask, but sometimes it is set to something
     // unusual by the calling environment.
 
     // Since various things rely on this umask to work properly and we are
     // not aware of any legitimate reasons to adjust it, unconditionally
     // normalize it until such reasons arise. See T7475 for discussion.
     umask(022);
   }
 
+
+  /**
+   * Get the path to an empty directory which is readable by all of the system
+   * user accounts that Phabricator acts as.
+   *
+   * In some cases, a binary needs some valid HOME or CWD to continue, but not
+   * all user accounts have valid home directories and even if they do they
+   * may not be readable after a `sudo` operation.
+   *
+   * @return string Path to an empty directory suitable for use as a CWD.
+   */
+  public static function getEmptyCWD() {
+    $root = dirname(phutil_get_library_root('phabricator'));
+    return $root.'/support/empty/';
+  }
+
+
 }
diff --git a/src/infrastructure/markup/PhabricatorMarkupEngine.php b/src/infrastructure/markup/PhabricatorMarkupEngine.php
index f570ee7e5..92e027f86 100644
--- a/src/infrastructure/markup/PhabricatorMarkupEngine.php
+++ b/src/infrastructure/markup/PhabricatorMarkupEngine.php
@@ -1,654 +1,658 @@
 <?php
 
 /**
  * Manages markup engine selection, configuration, application, caching and
  * pipelining.
  *
  * @{class:PhabricatorMarkupEngine} can be used to render objects which
  * implement @{interface:PhabricatorMarkupInterface} in a batched, cache-aware
  * way. For example, if you have a list of comments written in remarkup (and
  * the objects implement the correct interface) you can render them by first
  * building an engine and adding the fields with @{method:addObject}.
  *
  *   $field  = 'field:body'; // Field you want to render. Each object exposes
  *                           // one or more fields of markup.
  *
  *   $engine = new PhabricatorMarkupEngine();
  *   foreach ($comments as $comment) {
  *     $engine->addObject($comment, $field);
  *   }
  *
  * Now, call @{method:process} to perform the actual cache/rendering
  * step. This is a heavyweight call which does batched data access and
  * transforms the markup into output.
  *
  *   $engine->process();
  *
  * Finally, do something with the results:
  *
  *   $results = array();
  *   foreach ($comments as $comment) {
  *     $results[] = $engine->getOutput($comment, $field);
  *   }
  *
  * If you have a single object to render, you can use the convenience method
  * @{method:renderOneObject}.
  *
  * @task markup Markup Pipeline
  * @task engine Engine Construction
  */
 final class PhabricatorMarkupEngine extends Phobject {
 
   private $objects = array();
   private $viewer;
   private $contextObject;
   private $version = 15;
   private $engineCaches = array();
   private $auxiliaryConfig = array();
 
 
 /* -(  Markup Pipeline  )---------------------------------------------------- */
 
 
   /**
    * Convenience method for pushing a single object through the markup
    * pipeline.
    *
    * @param PhabricatorMarkupInterface  The object to render.
    * @param string                      The field to render.
    * @param PhabricatorUser             User viewing the markup.
    * @param object                      A context object for policy checks
    * @return string                     Marked up output.
    * @task markup
    */
   public static function renderOneObject(
     PhabricatorMarkupInterface $object,
     $field,
     PhabricatorUser $viewer,
     $context_object = null) {
     return id(new PhabricatorMarkupEngine())
       ->setViewer($viewer)
       ->setContextObject($context_object)
       ->addObject($object, $field)
       ->process()
       ->getOutput($object, $field);
   }
 
 
   /**
    * Queue an object for markup generation when @{method:process} is
    * called. You can retrieve the output later with @{method:getOutput}.
    *
    * @param PhabricatorMarkupInterface  The object to render.
    * @param string                      The field to render.
    * @return this
    * @task markup
    */
   public function addObject(PhabricatorMarkupInterface $object, $field) {
     $key = $this->getMarkupFieldKey($object, $field);
     $this->objects[$key] = array(
       'object' => $object,
       'field'  => $field,
     );
 
     return $this;
   }
 
 
   /**
    * Process objects queued with @{method:addObject}. You can then retrieve
    * the output with @{method:getOutput}.
    *
    * @return this
    * @task markup
    */
   public function process() {
     $keys = array();
     foreach ($this->objects as $key => $info) {
       if (!isset($info['markup'])) {
         $keys[] = $key;
       }
     }
 
     if (!$keys) {
       return;
     }
 
     $objects = array_select_keys($this->objects, $keys);
 
     // Build all the markup engines. We need an engine for each field whether
     // we have a cache or not, since we still need to postprocess the cache.
     $engines = array();
     foreach ($objects as $key => $info) {
       $engines[$key] = $info['object']->newMarkupEngine($info['field']);
       $engines[$key]->setConfig('viewer', $this->viewer);
       $engines[$key]->setConfig('contextObject', $this->contextObject);
 
       foreach ($this->auxiliaryConfig as $aux_key => $aux_value) {
         $engines[$key]->setConfig($aux_key, $aux_value);
       }
     }
 
     // Load or build the preprocessor caches.
     $blocks = $this->loadPreprocessorCaches($engines, $objects);
     $blocks = mpull($blocks, 'getCacheData');
 
     $this->engineCaches = $blocks;
 
     // Finalize the output.
     foreach ($objects as $key => $info) {
       $engine = $engines[$key];
       $field = $info['field'];
       $object = $info['object'];
 
       $output = $engine->postprocessText($blocks[$key]);
       $output = $object->didMarkupText($field, $output, $engine);
       $this->objects[$key]['output'] = $output;
     }
 
     return $this;
   }
 
 
   /**
    * Get the output of markup processing for a field queued with
    * @{method:addObject}. Before you can call this method, you must call
    * @{method:process}.
    *
    * @param PhabricatorMarkupInterface  The object to retrieve.
    * @param string                      The field to retrieve.
    * @return string                     Processed output.
    * @task markup
    */
   public function getOutput(PhabricatorMarkupInterface $object, $field) {
     $key = $this->getMarkupFieldKey($object, $field);
     $this->requireKeyProcessed($key);
 
     return $this->objects[$key]['output'];
   }
 
 
   /**
    * Retrieve engine metadata for a given field.
    *
    * @param PhabricatorMarkupInterface  The object to retrieve.
    * @param string                      The field to retrieve.
    * @param string                      The engine metadata field to retrieve.
    * @param wild                        Optional default value.
    * @task markup
    */
   public function getEngineMetadata(
     PhabricatorMarkupInterface $object,
     $field,
     $metadata_key,
     $default = null) {
 
     $key = $this->getMarkupFieldKey($object, $field);
     $this->requireKeyProcessed($key);
 
     return idx($this->engineCaches[$key]['metadata'], $metadata_key, $default);
   }
 
 
   /**
    * @task markup
    */
   private function requireKeyProcessed($key) {
     if (empty($this->objects[$key])) {
       throw new Exception(
         pht(
           "Call %s before using results (key = '%s').",
           'addObject()',
           $key));
     }
 
     if (!isset($this->objects[$key]['output'])) {
       throw new PhutilInvalidStateException('process');
     }
   }
 
 
   /**
    * @task markup
    */
   private function getMarkupFieldKey(
     PhabricatorMarkupInterface $object,
     $field) {
 
     static $custom;
     if ($custom === null) {
       $custom = array_merge(
         self::loadCustomInlineRules(),
         self::loadCustomBlockRules());
 
       $custom = mpull($custom, 'getRuleVersion', null);
       ksort($custom);
       $custom = PhabricatorHash::digestForIndex(serialize($custom));
     }
 
     return $object->getMarkupFieldKey($field).'@'.$this->version.'@'.$custom;
   }
 
 
   /**
    * @task markup
    */
   private function loadPreprocessorCaches(array $engines, array $objects) {
     $blocks = array();
 
     $use_cache = array();
     foreach ($objects as $key => $info) {
       if ($info['object']->shouldUseMarkupCache($info['field'])) {
         $use_cache[$key] = true;
       }
     }
 
     if ($use_cache) {
       try {
         $blocks = id(new PhabricatorMarkupCache())->loadAllWhere(
           'cacheKey IN (%Ls)',
           array_keys($use_cache));
         $blocks = mpull($blocks, null, 'getCacheKey');
       } catch (Exception $ex) {
         phlog($ex);
       }
     }
 
     $is_readonly = PhabricatorEnv::isReadOnly();
 
     foreach ($objects as $key => $info) {
       // False check in case MySQL doesn't support unicode characters
       // in the string (T1191), resulting in unserialize returning false.
       if (isset($blocks[$key]) && $blocks[$key]->getCacheData() !== false) {
         // If we already have a preprocessing cache, we don't need to rebuild
         // it.
         continue;
       }
 
       $text = $info['object']->getMarkupText($info['field']);
       $data = $engines[$key]->preprocessText($text);
 
       // NOTE: This is just debugging information to help sort out cache issues.
       // If one machine is misconfigured and poisoning caches you can use this
       // field to hunt it down.
 
       $metadata = array(
         'host' => php_uname('n'),
       );
 
       $blocks[$key] = id(new PhabricatorMarkupCache())
         ->setCacheKey($key)
         ->setCacheData($data)
         ->setMetadata($metadata);
 
       if (isset($use_cache[$key]) && !$is_readonly) {
         // This is just filling a cache and always safe, even on a read pathway.
         $unguarded = AphrontWriteGuard::beginScopedUnguardedWrites();
           $blocks[$key]->replace();
         unset($unguarded);
       }
     }
 
     return $blocks;
   }
 
 
   /**
    * Set the viewing user. Used to implement object permissions.
    *
    * @param PhabricatorUser The viewing user.
    * @return this
    * @task markup
    */
   public function setViewer(PhabricatorUser $viewer) {
     $this->viewer = $viewer;
     return $this;
   }
 
   /**
    * Set the context object. Used to implement object permissions.
    *
    * @param The object in which context this remarkup is used.
    * @return this
    * @task markup
    */
   public function setContextObject($object) {
     $this->contextObject = $object;
     return $this;
   }
 
   public function setAuxiliaryConfig($key, $value) {
     // TODO: This is gross and should be removed. Avoid use.
     $this->auxiliaryConfig[$key] = $value;
     return $this;
   }
 
 
 /* -(  Engine Construction  )------------------------------------------------ */
 
 
 
   /**
    * @task engine
    */
   public static function newManiphestMarkupEngine() {
     return self::newMarkupEngine(array(
     ));
   }
 
 
   /**
    * @task engine
    */
   public static function newPhrictionMarkupEngine() {
     return self::newMarkupEngine(array(
       'header.generate-toc' => true,
     ));
   }
 
 
   /**
    * @task engine
    */
   public static function newPhameMarkupEngine() {
     return self::newMarkupEngine(array(
       'macros' => false,
       'uri.full' => true,
     ));
   }
 
 
   /**
    * @task engine
    */
   public static function newFeedMarkupEngine() {
     return self::newMarkupEngine(
       array(
         'macros'      => false,
         'youtube'     => false,
       ));
   }
 
   /**
    * @task engine
    */
   public static function newCalendarMarkupEngine() {
     return self::newMarkupEngine(array(
     ));
   }
 
 
   /**
    * @task engine
    */
   public static function newDifferentialMarkupEngine(array $options = array()) {
     return self::newMarkupEngine(array(
       'differential.diff' => idx($options, 'differential.diff'),
     ));
   }
 
 
   /**
    * @task engine
    */
   public static function newDiffusionMarkupEngine(array $options = array()) {
     return self::newMarkupEngine(array(
       'header.generate-toc' => true,
     ));
   }
 
   /**
    * @task engine
    */
   public static function getEngine($ruleset = 'default') {
     static $engines = array();
     if (isset($engines[$ruleset])) {
       return $engines[$ruleset];
     }
 
     $engine = null;
     switch ($ruleset) {
       case 'default':
         $engine = self::newMarkupEngine(array());
         break;
       case 'nolinebreaks':
         $engine = self::newMarkupEngine(array());
         $engine->setConfig('preserve-linebreaks', false);
         break;
       case 'diffusion-readme':
         $engine = self::newMarkupEngine(array());
         $engine->setConfig('preserve-linebreaks', false);
         $engine->setConfig('header.generate-toc', true);
         break;
       case 'diviner':
         $engine = self::newMarkupEngine(array());
         $engine->setConfig('preserve-linebreaks', false);
   //    $engine->setConfig('diviner.renderer', new DivinerDefaultRenderer());
         $engine->setConfig('header.generate-toc', true);
         break;
       case 'extract':
         // Engine used for reference/edge extraction. Turn off anything which
         // is slow and doesn't change reference extraction.
         $engine = self::newMarkupEngine(array());
         $engine->setConfig('pygments.enabled', false);
         break;
       default:
         throw new Exception(pht('Unknown engine ruleset: %s!', $ruleset));
     }
 
     $engines[$ruleset] = $engine;
     return $engine;
   }
 
   /**
    * @task engine
    */
   private static function getMarkupEngineDefaultConfiguration() {
     return array(
       'pygments'      => PhabricatorEnv::getEnvConfig('pygments.enabled'),
       'youtube'       => PhabricatorEnv::getEnvConfig(
         'remarkup.enable-embedded-youtube'),
       'differential.diff' => null,
       'header.generate-toc' => false,
       'macros'        => true,
       'uri.allowed-protocols' => PhabricatorEnv::getEnvConfig(
         'uri.allowed-protocols'),
       'uri.full' => false,
       'syntax-highlighter.engine' => PhabricatorEnv::getEnvConfig(
         'syntax-highlighter.engine'),
       'preserve-linebreaks' => true,
     );
   }
 
 
   /**
    * @task engine
    */
   public static function newMarkupEngine(array $options) {
     $options += self::getMarkupEngineDefaultConfiguration();
 
     $engine = new PhutilRemarkupEngine();
 
     $engine->setConfig('preserve-linebreaks', $options['preserve-linebreaks']);
     $engine->setConfig('pygments.enabled', $options['pygments']);
     $engine->setConfig(
       'uri.allowed-protocols',
       $options['uri.allowed-protocols']);
     $engine->setConfig('differential.diff', $options['differential.diff']);
     $engine->setConfig('header.generate-toc', $options['header.generate-toc']);
     $engine->setConfig(
       'syntax-highlighter.engine',
       $options['syntax-highlighter.engine']);
 
+    $style_map = id(new PhabricatorDefaultSyntaxStyle())
+      ->getRemarkupStyleMap();
+    $engine->setConfig('phutil.codeblock.style-map', $style_map);
+
     $engine->setConfig('uri.full', $options['uri.full']);
 
     $rules = array();
     $rules[] = new PhutilRemarkupEscapeRemarkupRule();
     $rules[] = new PhutilRemarkupMonospaceRule();
 
 
     $rules[] = new PhutilRemarkupDocumentLinkRule();
     $rules[] = new PhabricatorNavigationRemarkupRule();
 
     if ($options['youtube']) {
       $rules[] = new PhabricatorYoutubeRemarkupRule();
     }
 
     $rules[] = new PhabricatorIconRemarkupRule();
     $rules[] = new PhabricatorEmojiRemarkupRule();
     $rules[] = new PhabricatorHandleRemarkupRule();
 
     $applications = PhabricatorApplication::getAllInstalledApplications();
     foreach ($applications as $application) {
       foreach ($application->getRemarkupRules() as $rule) {
         $rules[] = $rule;
       }
     }
 
     $rules[] = new PhutilRemarkupHyperlinkRule();
 
     if ($options['macros']) {
       $rules[] = new PhabricatorImageMacroRemarkupRule();
       $rules[] = new PhabricatorMemeRemarkupRule();
     }
 
     $rules[] = new PhutilRemarkupBoldRule();
     $rules[] = new PhutilRemarkupItalicRule();
     $rules[] = new PhutilRemarkupDelRule();
     $rules[] = new PhutilRemarkupUnderlineRule();
     $rules[] = new PhutilRemarkupHighlightRule();
 
     foreach (self::loadCustomInlineRules() as $rule) {
       $rules[] = clone $rule;
     }
 
     $blocks = array();
     $blocks[] = new PhutilRemarkupQuotesBlockRule();
     $blocks[] = new PhutilRemarkupReplyBlockRule();
     $blocks[] = new PhutilRemarkupLiteralBlockRule();
     $blocks[] = new PhutilRemarkupHeaderBlockRule();
     $blocks[] = new PhutilRemarkupHorizontalRuleBlockRule();
     $blocks[] = new PhutilRemarkupListBlockRule();
     $blocks[] = new PhutilRemarkupCodeBlockRule();
     $blocks[] = new PhutilRemarkupNoteBlockRule();
     $blocks[] = new PhutilRemarkupTableBlockRule();
     $blocks[] = new PhutilRemarkupSimpleTableBlockRule();
     $blocks[] = new PhutilRemarkupInterpreterBlockRule();
     $blocks[] = new PhutilRemarkupDefaultBlockRule();
 
     foreach (self::loadCustomBlockRules() as $rule) {
       $blocks[] = $rule;
     }
 
     foreach ($blocks as $block) {
       $block->setMarkupRules($rules);
     }
 
     $engine->setBlockRules($blocks);
 
     return $engine;
   }
 
   public static function extractPHIDsFromMentions(
     PhabricatorUser $viewer,
     array $content_blocks) {
 
     $mentions = array();
 
     $engine = self::newDifferentialMarkupEngine();
     $engine->setConfig('viewer', $viewer);
 
     foreach ($content_blocks as $content_block) {
       $engine->markupText($content_block);
       $phids = $engine->getTextMetadata(
         PhabricatorMentionRemarkupRule::KEY_MENTIONED,
         array());
       $mentions += $phids;
     }
 
     return $mentions;
   }
 
   public static function extractFilePHIDsFromEmbeddedFiles(
     PhabricatorUser $viewer,
     array $content_blocks) {
     $files = array();
 
     $engine = self::newDifferentialMarkupEngine();
     $engine->setConfig('viewer', $viewer);
 
     foreach ($content_blocks as $content_block) {
       $engine->markupText($content_block);
       $phids = $engine->getTextMetadata(
         PhabricatorEmbedFileRemarkupRule::KEY_EMBED_FILE_PHIDS,
         array());
       foreach ($phids as $phid) {
         $files[$phid] = $phid;
       }
     }
 
     return array_values($files);
   }
 
   /**
    * Produce a corpus summary, in a way that shortens the underlying text
    * without truncating it somewhere awkward.
    *
    * TODO: We could do a better job of this.
    *
    * @param string  Remarkup corpus to summarize.
    * @return string Summarized corpus.
    */
   public static function summarize($corpus) {
 
     // Major goals here are:
     //  - Don't split in the middle of a character (utf-8).
     //  - Don't split in the middle of, e.g., **bold** text, since
     //    we end up with hanging '**' in the summary.
     //  - Try not to pick an image macro, header, embedded file, etc.
     //  - Hopefully don't return too much text. We don't explicitly limit
     //    this right now.
 
     $blocks = preg_split("/\n *\n\s*/", $corpus);
 
     $best = null;
     foreach ($blocks as $block) {
       // This is a test for normal spaces in the block, i.e. a heuristic to
       // distinguish standard paragraphs from things like image macros. It may
       // not work well for non-latin text. We prefer to summarize with a
       // paragraph of normal words over an image macro, if possible.
       $has_space = preg_match('/\w\s\w/', $block);
 
       // This is a test to find embedded images and headers. We prefer to
       // summarize with a normal paragraph over a header or an embedded object,
       // if possible.
       $has_embed = preg_match('/^[{=]/', $block);
 
       if ($has_space && !$has_embed) {
         // This seems like a good summary, so return it.
         return $block;
       }
 
       if (!$best) {
         // This is the first block we found; if everything is garbage just
         // use the first block.
         $best = $block;
       }
     }
 
     return $best;
   }
 
   private static function loadCustomInlineRules() {
     return id(new PhutilClassMapQuery())
       ->setAncestorClass('PhabricatorRemarkupCustomInlineRule')
       ->execute();
   }
 
   private static function loadCustomBlockRules() {
     return id(new PhutilClassMapQuery())
       ->setAncestorClass('PhabricatorRemarkupCustomBlockRule')
       ->execute();
   }
 
 }
diff --git a/src/infrastructure/syntax/PhabricatorDefaultSyntaxStyle.php b/src/infrastructure/syntax/PhabricatorDefaultSyntaxStyle.php
new file mode 100644
index 000000000..72b6dfb4b
--- /dev/null
+++ b/src/infrastructure/syntax/PhabricatorDefaultSyntaxStyle.php
@@ -0,0 +1,76 @@
+<?php
+
+final class PhabricatorDefaultSyntaxStyle
+  extends PhabricatorSyntaxStyle {
+
+  const STYLEKEY = 'default';
+
+  public function getStyleName() {
+    return pht('Default');
+  }
+
+  public function getStyleMap() {
+    return array(
+      'hll' => 'color: #ffffcc',
+      'c' => 'color: #74777d',
+      'cm' => 'color: #74777d',
+      'c1' => 'color: #74777d',
+      'cs' => 'color: #74777d',
+      'sd' => 'color: #000000',
+      'sh' => 'color: #000000',
+      's' => 'color: #766510',
+      'sb' => 'color: #766510',
+      'sc' => 'color: #766510',
+      's2' => 'color: #766510',
+      's1' => 'color: #766510',
+      'sx' => 'color: #766510',
+      'sr' => 'color: #bb6688',
+      'nv' => 'color: #001294',
+      'vi' => 'color: #001294',
+      'vg' => 'color: #001294',
+      'na' => 'color: #354bb3',
+      'kc' => 'color: #000a65',
+      'no' => 'color: #000a65',
+      'k' => 'color: #aa4000',
+      'kd' => 'color: #aa4000',
+      'kn' => 'color: #aa4000',
+      'kt' => 'color: #aa4000',
+      'cp' => 'color: #304a96',
+      'kp' => 'color: #304a96',
+      'kr' => 'color: #304a96',
+      'nb' => 'color: #304a96',
+      'bp' => 'color: #304a96',
+      'nc' => 'color: #00702a',
+      'nt' => 'color: #00702a',
+      'vc' => 'color: #00702a',
+      'nf' => 'color: #004012',
+      'nx' => 'color: #004012',
+      'o' => 'color: #aa2211',
+      'ss' => 'color: #aa2211',
+      'm' => 'color: #601200',
+      'mf' => 'color: #601200',
+      'mh' => 'color: #601200',
+      'mi' => 'color: #601200',
+      'mo' => 'color: #601200',
+      'il' => 'color: #601200',
+      'gd' => 'color: #a00000',
+      'gr' => 'color: #ff0000',
+      'gh' => 'color: #000080',
+      'gi' => 'color: #00a000',
+      'go' => 'color: #808080',
+      'gp' => 'color: #000080',
+      'gu' => 'color: #800080',
+      'gt' => 'color: #0040d0',
+      'nd' => 'color: #aa22ff',
+      'ni' => 'color: #92969d',
+      'ne' => 'color: #d2413a',
+      'nl' => 'color: #a0a000',
+      'nn' => 'color: #0000ff',
+      'ow' => 'color: #aa22ff',
+      'w' => 'color: #bbbbbb',
+      'se' => 'color: #bb6622',
+      'si' => 'color: #bb66bb',
+    );
+  }
+
+}
diff --git a/src/infrastructure/syntax/PhabricatorSyntaxStyle.php b/src/infrastructure/syntax/PhabricatorSyntaxStyle.php
new file mode 100644
index 000000000..3f75f5c63
--- /dev/null
+++ b/src/infrastructure/syntax/PhabricatorSyntaxStyle.php
@@ -0,0 +1,44 @@
+<?php
+
+abstract class PhabricatorSyntaxStyle extends Phobject {
+
+  abstract public function getStyleName();
+  abstract public function getStyleMap();
+
+  final public function getStyleOrder() {
+    return (string)id(new PhutilSortVector())
+      ->addInt($this->isDefaultStyle() ? 0 : 1)
+      ->addString($this->getStyleName());
+  }
+
+  final public function getSyntaxStyleKey() {
+    return $this->getPhobjectClassConstant('STYLEKEY');
+  }
+
+  final public function isDefaultStyle() {
+    return ($this->getSyntaxStyleKey() == 'default');
+  }
+
+  public static function getAllStyles() {
+    return id(new PhutilClassMapQuery())
+      ->setAncestorClass(__CLASS__)
+      ->setUniqueMethod('getSyntaxStyleKey')
+      ->setSortMethod('getStyleName')
+      ->execute();
+  }
+
+  final public function getRemarkupStyleMap() {
+    $map = array(
+      'rbw_r' => 'color: red',
+      'rbw_o' => 'color: orange',
+      'rbw_y' => 'color: yellow',
+      'rbw_g' => 'color: green',
+      'rbw_b' => 'color: blue',
+      'rbw_i' => 'color: indigo',
+      'rbw_v' => 'color: violet',
+    );
+
+    return $map + $this->getStyleMap();
+  }
+
+}
diff --git a/webroot/rsrc/css/application/countdown/timer.css b/webroot/rsrc/css/application/countdown/timer.css
index bbb2fdd68..832379aec 100644
--- a/webroot/rsrc/css/application/countdown/timer.css
+++ b/webroot/rsrc/css/application/countdown/timer.css
@@ -1,65 +1,71 @@
 /**
  * @provides phabricator-countdown-css
  */
 
 .phabricator-timer {
   margin: 16px 16px 0 16px;
 }
 
 .device .phabricator-timer {
-  margin-left: 8px;
-  margin-right: 8px;
+  margin-left: 12px;
+  margin-right: 12px;
 }
 
 .phabricator-remarkup .phabricator-timer-view {
   max-width: 460px;
   margin: 0 0 12px 0;
 }
 
-.phabricator-timer .countdown-description {
+body .phabricator-timer .countdown-description.phabricator-remarkup {
   border-bottom: 1px solid {$thinblueborder};
-  padding-bottom: 16px;
+  padding: 0 0 16px;
+  margin: 0;
+}
+
+.phabricator-timer .countdown-description.phabricator-remarkup
+  .phabricator-remarkup {
+    padding: 0;
 }
 
 .device-phone .phabricator-remarkup .phabricator-timer {
   width: auto;
 }
 
 .phabricator-timer-table {
   width: 100%;
   margin: 8px 0 0 0;
 }
 
 .phabricator-timer-table th {
   text-align: center;
   font-size: {$biggerfontsize};
   color: {$bluetext};
   width: 10%;
   padding: 4px;
 }
 
 .phabricator-timer-table td {
   padding: 4px;
   text-align: center;
   font-size: 36px;
   font-weight: bold;
   overflow: hidden;
   line-height: auto;
   height: auto;
   line-height: 44px;
 }
 
 .phabricator-timer-table td.phabricator-timer-foot {
   font-size: {$biggerfontsize};
   line-height: 16px;
   border-top: 1px solid {$thinblueborder};
   color: {$bluetext};
   padding: 12px 0;
   border-bottom-right-radius: 3px;
   border-bottom-left-radius: 3px;
   text-align: center;
 }
 
 .phabricator-timer-table small {
   color: {$lightgreytext};
 }
diff --git a/webroot/rsrc/css/application/diffusion/diffusion-icons.css b/webroot/rsrc/css/application/diffusion/diffusion-icons.css
index c735e3d84..0e00f6e46 100644
--- a/webroot/rsrc/css/application/diffusion/diffusion-icons.css
+++ b/webroot/rsrc/css/application/diffusion/diffusion-icons.css
@@ -1,30 +1,49 @@
 /**
  * @provides diffusion-icons-css
  */
 
 input.diffusion-clone-uri {
   display: block;
   width: 100%;
 }
 
 .diffusion-clone-extras {
   font-size: 11px;
   text-align: right;
   color: {$lightgreytext};
 }
 
 .diffusion-browse-name {
   margin-left: 8px;
 }
 
 .diffusion-link-icon + .diffusion-link-icon {
   margin-left: 6px;
 }
 
 .diffusion-search-boxen {
   padding: 16px;
 }
 
 .diffusion-search-boxen .phui-form-view {
   padding: 0;
 }
+
+.diffusion-clone-uri-table {
+  width: 100%;
+}
+
+.diffusion-clone-uri-table th {
+  width: 24px;
+  padding: 0 0 0 4px;
+}
+
+.diffusion-clone-uri-table th a.button {
+  width: 12px;
+  height: 19px;
+}
+
+.diffusion-clone-uri-table th a.button .phui-icon-view {
+  left: 12px;
+  top: 7px;
+}
diff --git a/webroot/rsrc/css/core/remarkup.css b/webroot/rsrc/css/core/remarkup.css
index 8f48d7228..b4fa18ac8 100644
--- a/webroot/rsrc/css/core/remarkup.css
+++ b/webroot/rsrc/css/core/remarkup.css
@@ -1,649 +1,651 @@
 /**
  * @provides phabricator-remarkup-css
  */
 
 .phabricator-remarkup {
   line-height: 1.51em;
   word-break: break-word;
 }
 
 .phabricator-remarkup p {
   margin: 0 0 12px;
 }
 
 .PhabricatorMonospaced,
 .phabricator-remarkup .remarkup-code-block .remarkup-code {
   font: 11px/15px "Menlo", "Consolas", "Monaco", monospace;
 }
 
 .platform-windows .PhabricatorMonospaced,
 .platform-windows .phabricator-remarkup .remarkup-code-block .remarkup-code {
   font: 12px/15px "Menlo", "Consolas", "Monaco", monospace;
 }
 
 .phabricator-remarkup .remarkup-code-block {
   margin: 12px 0;
   white-space: pre;
 }
 
 .phabricator-remarkup .remarkup-code-header {
   padding: 6px 12px;
   font-size: 13px;
   font-weight: bold;
   background: rgba({$alphablue},0.08);
   display: inline-block;
   border-top-left-radius: 3px;
   border-top-right-radius: 3px;
 }
 
 .phabricator-remarkup .code-block-counterexample .remarkup-code-header {
   background-color: {$sh-redbackground};
 }
 
 .phabricator-remarkup .remarkup-code-block .remarkup-code-header + pre {
   border-top-left-radius: 0;
 }
 
 .phabricator-remarkup .remarkup-code-block pre {
   background: rgba({$alphablue},0.08);
   display: block;
   color: #000;
   overflow: auto;
   padding: 12px;
   border-radius: 3px;
 }
 
 .phabricator-remarkup pre.remarkup-counterexample {
   background-color: {$sh-redbackground};
 }
 
 .phabricator-remarkup tt.remarkup-monospaced {
   color: #000;
   background: rgba({$alphablue},0.1);
   padding: 1px 4px;
   border-radius: 3px;
   white-space: pre-wrap;
 }
 
 /* NOTE: You can currently produce this with [[link | `name`]]. Restore the
    link color. */
 .phabricator-remarkup a tt.remarkup-monospaced {
   color: {$anchor};
 }
 
 .phabricator-remarkup .remarkup-header tt.remarkup-monospaced {
   font-weight: normal;
 }
 
 .phabricator-remarkup ul.remarkup-list {
   list-style: disc;
   margin: 12px 0 12px 30px;
 }
 
 .phabricator-remarkup ol.remarkup-list {
   list-style: decimal;
   margin: 12px 0 12px 30px;
 }
 
 .phabricator-remarkup ol ol.remarkup-list {
   list-style: upper-alpha;
 }
 
 .phabricator-remarkup ol ol ol.remarkup-list {
   list-style: lower-alpha;
 }
 
 .phabricator-remarkup ol ol ol ol.remarkup-list {
   list-style: lower-roman;
 }
 
 .phabricator-remarkup .remarkup-list-with-checkmarks .remarkup-checked-item,
 .phabricator-remarkup .remarkup-list-with-checkmarks .remarkup-unchecked-item {
   list-style: none;
   margin-left: -18px;
 }
 
 .phabricator-remarkup .remarkup-list-with-checkmarks input {
   margin-right: 2px;
   opacity: 1;
 }
 
 .phabricator-remarkup .remarkup-list-with-checkmarks .remarkup-checked-item {
   text-decoration: line-through;
 }
 
 .phabricator-remarkup ul.remarkup-list ol.remarkup-list,
 .phabricator-remarkup ul.remarkup-list ul.remarkup-list,
 .phabricator-remarkup ol.remarkup-list ol.remarkup-list,
 .phabricator-remarkup ol.remarkup-list ul.remarkup-list {
   margin: 4px 0 4px 24px;
 }
 
 .phabricator-remarkup .remarkup-list-item {
   line-height: 1.7em;
 }
 
 .phabricator-remarkup li.phantom-item,
 .phabricator-remarkup li.phantom-item {
   list-style-type: none;
 }
 
 .phabricator-remarkup h1.remarkup-header {
   font-size: 24px;
   line-height: 1.625em;
   margin: 24px 0 4px;
 }
 
 .phabricator-remarkup h2.remarkup-header {
   font-size: 20px;
   line-height: 1.5em;
   margin: 20px 0 4px;
 }
 
 .phabricator-remarkup h3.remarkup-header {
   font-size: 18px;
   line-height: 1.375em;
   margin: 20px 0 4px;
 }
 
 .phabricator-remarkup h4.remarkup-header {
   font-size: 16px;
   line-height: 1.25em;
   margin: 12px 0 4px;
 }
 
 .phabricator-remarkup h5.remarkup-header {
   font-size: 15px;
   line-height: 1.125em;
   margin: 8px 0 4px;
 }
 
 .phabricator-remarkup h6.remarkup-header {
   font-size: 14px;
   line-height: 1em;
   margin: 4px 0;
 }
 
 .phabricator-remarkup blockquote {
   border-left: 3px solid {$sh-blueborder};
   color: {$darkbluetext};
   font-style:  italic;
   margin: 4px 0 12px 0;
   padding: 8px 12px;
   background-color: {$lightbluebackground};
 }
 
 .phabricator-remarkup blockquote *:last-child {
   margin-bottom: 0;
 }
 
 .phabricator-remarkup blockquote blockquote {
   background-color: rgba(175,175,175, .1);
 }
 
 .phabricator-remarkup blockquote em {
   /* In blockquote bodies, default text is italic so emphasized text should
      be normal. */
   font-style: normal;
 }
 
 .phabricator-remarkup blockquote div.remarkup-reply-head {
   font-style: normal;
   padding-bottom: 4px;
 }
 
 .phabricator-remarkup blockquote div.remarkup-reply-head em {
   /* In blockquote headers, default text is normal so emphasized text should
      be italic. See T10686. */
   font-style: italic;
 }
 
 .phabricator-remarkup blockquote div.remarkup-reply-head
   .phui-tag-core {
   background-color: transparent;
   border: none;
   padding: 0;
   color: {$darkbluetext};
 }
 
 .phabricator-remarkup img.remarkup-proxy-image {
   max-width: 640px;
   max-height: 640px;
 }
 
 .phabricator-remarkup audio {
   display: block;
   margin: 16px auto;
   min-width: 240px;
   width: 50%;
 }
 
 .phabricator-remarkup-mention-exists {
   font-weight: bold;
   background: #e6f3ff;
 }
 
 .phabricator-remarkup-mention-disabled {
   font-weight: bold;
   background: #dddddd;
 }
 
 .phui-remarkup-preview .phabricator-remarkup-mention-unknown,
 .aphront-panel-preview .phabricator-remarkup-mention-unknown {
   font-weight: bold;
   background: #ffaaaa;
 }
 
 .phabricator-remarkup .phriction-link {
   font-weight: bold;
 }
 
 .phabricator-remarkup .phriction-link-missing {
   color: {$red};
 }
 
 .phabricator-remarkup .phriction-link-lock {
   color: {$greytext};
 }
 
 .phabricator-remarkup-mention-nopermission .phui-tag-core {
   background: {$lightgreybackground};
   color: {$lightgreytext};
 }
 
 .phabricator-remarkup .remarkup-note {
   margin: 16px 0;
   padding: 12px;
   border-left: 3px solid {$blue};
   background: {$lightblue};
 }
 
 .phabricator-remarkup .remarkup-warning {
   margin: 16px 0;
   padding: 12px;
   border-left: 3px solid {$yellow};
   background: {$lightyellow};
 }
 
 .phabricator-remarkup .remarkup-important {
   margin: 16px 0;
   padding: 12px;
   border-left: 3px solid {$red};
   background: {$lightred};
 }
 
 .phabricator-remarkup .remarkup-note .remarkup-monospaced,
 .phabricator-remarkup .remarkup-important .remarkup-monospaced,
 .phabricator-remarkup .remarkup-warning .remarkup-monospaced {
   background-color: rgba(150,150,150,.2);
 }
 
 .phabricator-remarkup .remarkup-note-word {
   font-weight: bold;
   color: {$darkbluetext};
 }
 
 .phabricator-remarkup-toc {
   float: right;
   border-left: 1px solid {$lightblueborder};
   background: #fff;
   width: 160px;
   padding-left: 8px;
   margin: 0 0 4px 8px;
 }
 
 .phabricator-remarkup-toc-header {
   font-size: 13px;
   line-height: 13px;
   color: {$darkbluetext};
   font-weight: bold;
   margin-bottom: 4px;
 }
 
 .phabricator-remarkup-toc ul {
   padding: 0;
   margin: 0;
   list-style: none;
   overflow: hidden;
 }
 
 .phabricator-remarkup-toc ul ul {
   margin: 0 0 0 8px;
 }
 
 .phabricator-remarkup-toc ul li {
   padding: 0;
   margin: 0;
   font-size: 12px;
   overflow: hidden;
   text-overflow: ellipsis;
   white-space: nowrap;
 }
 
 .phabricator-remarkup-embed-layout-right {
   text-align: right;
 }
 
 .phabricator-remarkup-embed-layout-center {
   text-align: center;
 }
 
 .phabricator-remarkup-embed-layout-inline {
   display: inline;
 }
 
 .phabricator-remarkup-embed-float-right {
   float: right;
   margin: .5em 1em 0;
 }
 
 .phabricator-remarkup-embed-layout-link {
   padding-left: 20px;
   background: url(/rsrc/image/icon/fatcow/page_white_put.png) 0 0 no-repeat;
 }
 
 .phabricator-remarkup-embed-float-left {
   float: left;
   margin: .5em 1em 0;
 }
 
 .phabricator-remarkup-embed-image {
   display: inline-block;
   border: 3px solid white;
   box-shadow: 1px 1px 2px rgba({$alphablack}, 0.20);
 }
 
-.phabricator-remarkup-embed-image-full {
+.phabricator-remarkup-embed-image-full,
+.phabricator-remarkup-embed-image-wide {
   display: inline-block;
   max-width: 100%;
 }
 
-.phabricator-remarkup-embed-image-full img {
+.phabricator-remarkup-embed-image-full img,
+.phabricator-remarkup-embed-image-wide img {
   height: auto;
   max-width: 100%;
 }
 
 .phabricator-remarkup .remarkup-table-wrap {
   overflow-x: auto;
 }
 
 .phabricator-remarkup table.remarkup-table {
   border-collapse: separate;
   border-spacing: 1px;
   background: {$lightblueborder};
   margin: 12px 0;
   word-break: normal;
 }
 
 .phabricator-remarkup table.remarkup-table th {
   font-weight: bold;
   padding: 4px 6px;
   background: {$lightbluebackground};
 }
 
 .phabricator-remarkup table.remarkup-table td {
   background: #ffffff;
   padding: 3px 6px;
 }
 
 body div.phabricator-remarkup.remarkup-has-toc
   .phabricator-remarkup-toc + .remarkup-header {
   margin-top: 0;
   padding-top: 0;
 }
 
 body .phabricator-standard-page div.phabricator-remarkup *:first-child,
 body .phabricator-standard-page div.phabricator-remarkup .remarkup-header + * {
   margin-top: 0;
 }
 
 body div.phabricator-remarkup > *:last-child {
   margin-bottom: 0;
 }
 
 .remarkup-assist-textarea {
   border-left-color: {$blueborder};
   border-right-color: {$blueborder};
   border-bottom-color: {$blueborder};
   border-top-color: {$thinblueborder};
   border-radius: 0;
 
   box-shadow: none;
   -webkit-box-shadow: none;
 
   /* Set line height explicitly so the metrics <var /> and the real textarea
      are forced to the same value. */
   line-height: 1.25em;
 
   /* Prevent Safari and Chrome users from dragging the textarea any wider,
      because the top bar won't resize along with it. */
   resize: vertical;
 }
 
 var.remarkup-assist-textarea {
   /* This is an invisible element used to measure the size of text in the
      textarea so we can float typeaheads over the cursor position. */
   display: block;
   border-color: orange;
   box-sizing: border-box;
   padding: 4px 6px;
   white-space: pre-wrap;
   visibility: hidden;
 }
 
 .remarkup-assist-textarea:focus {
   border: 1px solid rgba(82, 168, 236, 0.8);
 }
 
 .remarkup-assist-bar {
   height: 26px;
   border-width: 1px 1px 0;
   border-style: solid;
   border-top-color: {$blueborder};
   border-left-color: {$blueborder};
   border-right-color: {$blueborder};
 
   background: {$lightbluebackground};
   overflow: hidden;
 }
 
 .remarkup-assist-button {
   display: block;
   padding: 4px 5px;
   float: left;
 }
 
 .remarkup-assist-button:hover {
   background-color: rgba(100,100,100,.15);
 }
 
 .remarkup-assist-button:hover .phui-icon-view.phui-font-fa {
   color: {$darkbluetext};
 }
 
 .remarkup-assist-button:active {
   outline: none;
 }
 
 .remarkup-assist-button:focus {
   outline: none;
 }
 
 .remarkup-assist-separator {
   display: block;
   float: left;
 
   margin: 7px 4px;
   height: 14px;
 
   width: 0px;
   border-right: 1px solid #cccccc;
 }
 
 .remarkup-interpreter-error {
   padding: 8px;
   border: 1px solid {$sh-redborder};
   background-color: {$sh-redbackground};
 }
 
 .remarkup-cowsay {
   white-space: pre-wrap;
 }
 
 .remarkup-figlet {
   white-space: pre-wrap;
 }
 
 .remarkup-assist {
   width: 14px;
   height: 14px;
   overflow: hidden;
   text-align: center;
   vertical-align: middle;
 }
 
 .remarkup-assist-right {
   float: right;
 }
 
 .jx-order-mask {
   background: white;
   opacity: 1.0;
 }
 
 .remarkup-control-fullscreen-mode {
   position: fixed;
   top: -1px;
   bottom: -1px;
   left: -1px;
   right: -1px;
 }
 
 .remarkup-control-fullscreen-mode textarea.remarkup-assist-textarea {
   position: absolute;
   top: 27px;
   left: 0;
   right: 0;
   bottom: 0;
   /* NOTE: This doesn't work in Firefox, there's a JS behavior to correct it. */
   height: auto;
   border-width: 1px 0 0 0;
   outline: none;
   resize: none;
 }
 
 .phabricator-image-macro-hero {
   margin: auto;
   max-width: 95%;
 }
 
 .phabricator-remarkup-macro {
   height: auto;
   max-width: 100%;
 }
 
 .remarkup-nav-sequence-arrow {
   color: {$lightgreytext};
 }
 
 .phabricator-remarkup hr {
   background: {$thinblueborder};
   margin: 24px 0;
   clear: both;
 }
 
 .phabricator-remarkup .remarkup-highlight {
   background-color: {$lightviolet};
   padding: 0 4px;
 }
 
 .remarkup-inline-preview {
   display: block;
   position: relative;
   background: #fff;
   overflow-y: auto;
   box-sizing: border-box;
   width: 100%;
   border: 1px solid {$sky};
   resize: vertical;
   padding: 4px 6px;
 }
 
 .remarkup-control-fullscreen-mode .remarkup-inline-preview {
   resize: none;
 }
 
 .remarkup-inline-preview * {
   resize: none;
 }
 
 .remarkup-assist-button.preview-active {
   background: {$sky};
 }
 
 .remarkup-assist-button.preview-active .phui-icon-view {
   color: #ffffff;
 }
 
 .remarkup-assist-button.preview-active:hover .phui-icon-view {
   color: {$lightsky};
 }
 
 .device .remarkup-assist-nodevice {
   display: none;
 }
 
 .phuix-autocomplete {
   position: absolute;
   width: 300px;
   box-shadow: {$dropshadow};
   background: #ffffff;
   border: 1px solid {$lightgreyborder};
   border-radius: 3px;
 }
 
 .phuix-autocomplete-head {
   white-space: nowrap;
   overflow: hidden;
   text-overflow: ellipsis;
   padding: 6px 8px;
   background: {$lightgreybackground};
   color: {$darkgreytext};
   border-radius: 3px;
 }
 
 .phuix-autocomplete-head .phui-icon-view {
   margin-right: 4px;
   color: {$lightgreytext};
 }
 
 .phuix-autocomplete-echo {
   margin-left: 4px;
   color: {$lightgreytext};
 }
 
 .phuix-autocomplete-list a.jx-result {
   display: block;
   padding: 5px 8px;
   font-size: {$normalfontsize};
   border-top: 1px solid {$thinblueborder};
   font-weight: bold;
   color: {$darkgreytext};
 }
 
 .phuix-autocomplete-list a.jx-result .phui-icon-view {
   margin-right: 4px;
   color: {$lightbluetext};
 }
 
 .phuix-autocomplete-list a.jx-result:hover {
   text-decoration: none;
   background: {$sh-bluebackground};
   color: #000;
 }
 
 .phuix-autocomplete-list a.jx-result.focused,
 .phuix-autocomplete-list a.jx-result.focused:hover {
   background: {$sh-bluebackground};
   color: #000;
 }
diff --git a/webroot/rsrc/css/core/syntax.css b/webroot/rsrc/css/core/syntax.css
index 9cf3fc9a2..35e3d299d 100644
--- a/webroot/rsrc/css/core/syntax.css
+++ b/webroot/rsrc/css/core/syntax.css
@@ -1,145 +1,42 @@
 /**
  * @provides syntax-highlighting-css
+ * @requires syntax-default-css
  */
 .remarkup-code .uu { /* Forbidden Unicode */
   color: #aa0066;
 }
 
 .remarkup-code .over-the-line {
   color:   #aa0066;
   margin-right: 1px;
 }
 
 .remarkup-code td > span,
 .remarkup-code td > span > span {
   padding: 1px 0 3px;
 }
 
 .remarkup-code span.bright {
   border-bottom: 1px solid transparent;
 }
 
-.remarkup-code .hll {
-  background-color: #ffffcc;
-}
-
-.remarkup-code .c, /* Comment */
-.remarkup-code .cm, /* Comment.Multiline */
-.remarkup-code .c1, /* Comment.Single */
-.remarkup-code .cs { /* Comment.Special */
-  color: {$greytext};
-}
-
-.remarkup-code .sd, /* Literal.String.Doc */
-.remarkup-code .sh { /* Literal.String.Heredoc */
-  color: #000000;
-}
-
-.remarkup-code .s, /* Literal.String */
-.remarkup-code .sb, /* Literal.String.Backtick */
-.remarkup-code .sc, /* Literal.String.Char */
-.remarkup-code .s2, /* Literal.String.Double */
-.remarkup-code .s1, /* Literal.String.Single */
-.remarkup-code .sx { /* Literal.String.Other */
-  color: #766510;
-}
-
-.remarkup-code .sr { /* Literal.String.Regex */
-  color: #BB6688;
-}
-
-.remarkup-code .nv, /* Name.Variable */
-.remarkup-code .vi, /* Name.Variable.Instance */
-.remarkup-code .vg { /* Name.Variable.Global */
-  color: #001294;
-}
-
-.remarkup-code .na { /* Name.Attribute */
-  color: #354BB3;
-}
-
-.remarkup-code .kc, /* Keyword.Constant */
-.remarkup-code .no { /* Name.Constant */
-  color: #000A65;
-}
-
-.remarkup-code .k, /* Keyword */
-.remarkup-code .kd, /* Keyword.Declaration */
-.remarkup-code .kn, /* Keyword.Namespace */
-.remarkup-code .kt { /* Keyword.Type */
-  color: #AA4000;
-}
-
-.remarkup-code .cp, /* Comment.Preproc */
-.remarkup-code .kp, /* Keyword.Pseudo */
-.remarkup-code .kr, /* Keyword.Reserved */
-.remarkup-code .nb, /* Name.Builtin */
-.remarkup-code .bp { /* Name.Builtin.Pseudo */
-  color: #304A96;
-}
-
-.remarkup-code .nc, /* Name.Class */
-.remarkup-code .nt, /* Name.Tag */
-.remarkup-code .vc { /* Name.Variable.Class */
-  color: #00702A;
-}
-
-.remarkup-code .nf, /* Name.Function */
-.remarkup-code .nx { /* Name.Other */
-  color: #004012;
-}
-
-.remarkup-code .o, /* Operator */
-.remarkup-code .ss { /* Literal.String.Symbol */
-  color: #AA2211;
-}
-
-.remarkup-code .m, /* Literal.Number */
-.remarkup-code .mf, /* Literal.Number.Float */
-.remarkup-code .mh, /* Literal.Number.Hex */
-.remarkup-code .mi, /* Literal.Number.Integer */
-.remarkup-code .mo, /* Literal.Number.Oct */
-.remarkup-code .il { /* Literal.Number.Integer.Long */
-  color: #601200;
-}
-
-.remarkup-code .gd { color: #A00000 } /* Generic.Deleted */
-.remarkup-code .ge { } /* Generic.Emph */
-.remarkup-code .gr { color: #FF0000 } /* Generic.Error */
-.remarkup-code .gh { color: #000080; } /* Generic.Heading */
-.remarkup-code .gi { color: #00A000 } /* Generic.Inserted */
-.remarkup-code .go { color: #808080 } /* Generic.Output */
-.remarkup-code .gp { color: #000080 } /* Generic.Prompt */
-.remarkup-code .gs { } /* Generic.Strong */
-.remarkup-code .gu { color: #800080 } /* Generic.Subheading */
-.remarkup-code .gt { color: #0040D0 } /* Generic.Traceback */
-.remarkup-code .nd { color: #AA22FF } /* Name.Decorator */
-.remarkup-code .ni { color: {$lightgreytext} } /* Name.Entity */
-.remarkup-code .ne { color: #D2413A } /* Name.Exception */
-.remarkup-code .nl { color: #A0A000 } /* Name.Label */
-.remarkup-code .nn { color: #0000FF } /* Name.Namespace */
-.remarkup-code .ow { color: #AA22FF } /* Operator.Word */
-.remarkup-code .w { color: #bbbbbb } /* Text.Whitespace */
-.remarkup-code .se { color: #BB6622 } /* Literal.String.Escape */
-.remarkup-code .si { color: #BB6688 } /* Literal.String.Interpol */
-
 .remarkup-code .rbw_r { color: red; }
 .remarkup-code .rbw_o { color: orange; }
 .remarkup-code .rbw_y { color: yellow; }
 .remarkup-code .rbw_g { color: green; }
 .remarkup-code .rbw_b { color: blue; }
 .remarkup-code .rbw_i { color: indigo; }
 .remarkup-code .rbw_v { color: violet; }
 
 .repository-crossreference .remarkup-code .crossreference-item {
   background: lightyellow;
   border-bottom: 1px dotted #bbddbb;
 }
 .crossreference-cursor {
   cursor: help;
 }
 
 .remarkup-code .invisible {
   color: #222222;
   background: #dddddd;
 }
diff --git a/webroot/rsrc/css/layout/phabricator-side-menu-view.css b/webroot/rsrc/css/layout/phabricator-side-menu-view.css
index 77c0b7cf3..b64fe5fbb 100644
--- a/webroot/rsrc/css/layout/phabricator-side-menu-view.css
+++ b/webroot/rsrc/css/layout/phabricator-side-menu-view.css
@@ -1,53 +1,59 @@
 /**
  * @provides phabricator-side-menu-view-css
  */
 
 .phabricator-basic-nav .phabricator-side-menu .phui-list-item-view {
   display: block;
   white-space: nowrap;
   text-decoration: none;
   font-size: 13px;
   -webkit-font-smoothing: antialiased;
 }
 
 .phabricator-basic-nav .phabricator-side-menu .phui-list-item-href {
   display: block;
   padding: 6px 8px 6px 24px;
   color: {$darkbluetext};
   border-top-right-radius: 3px;
   border-bottom-right-radius: 3px;
 }
 
+.phabricator-basic-nav .phabricator-side-menu .phui-list-item-icon {
+  margin-left: -12px;
+  text-align: center;
+  width: 24px;
+}
+
 .phabricator-basic-nav .phabricator-side-menu .phui-list-item-selected {
   background-color: rgba({$alphablack},.05);
   border-left: 4px solid {$sky};
   border-top-right-radius: 3px;
   border-bottom-right-radius: 3px;
   font-weight: bold;
 }
 
 .device-desktop .phabricator-basic-nav .phabricator-side-menu
   .phui-list-item-selected
   a.phui-list-item-href:hover {
   background-color: rgba({$alphablack},.05);
 }
 
 .phabricator-basic-nav .phabricator-side-menu .phui-list-item-selected
   .phui-list-item-href {
   padding-left: 20px;
 }
 
 .phabricator-basic-nav .phabricator-side-menu .phui-list-item-type-label {
   padding: 6px 8px 4px 12px;
   color: {$darkbluetext};
   text-transform: uppercase;
   font-size: 12px;
   font-weight: bold;
   border-style: solid;
 }
 
 .device-desktop .phabricator-basic-nav .phabricator-side-menu
   a.phui-list-item-href:hover {
   text-decoration: none;
   background-color: rgba({$alphablack},.07);
 }
diff --git a/webroot/rsrc/css/phui/phui-box.css b/webroot/rsrc/css/phui/phui-box.css
index 7fed4a535..8d2acd006 100644
--- a/webroot/rsrc/css/phui/phui-box.css
+++ b/webroot/rsrc/css/phui/phui-box.css
@@ -1,115 +1,116 @@
 /**
  * @provides phui-box-css
  */
 
 .phui-box-border {
   border: 1px solid {$lightblueborder};
   background-color: #fff;
   border-radius: 3px;
 }
 
 .phui-box.focus {
   box-shadow: 0 0 5px 5px rgba(255, 255, 0, 0.90);
 }
 
 .phui-box-grey {
   background-color: #F7F7F9;
   border-radius: 3px;
   border-color: rgba({$alphagrey},.2);
 }
 
 .phui-box-blue {
   background-color: {$bluebackground};
   border-radius: 3px;
   border-color: {$thinblueborder};
 }
 
 .phui-box-blue .phui-object-item,
 .phui-box-grey .phui-object-item {
   background: transparent;
 }
 
 .phui-box-blue .phui-object-item-link,
 .phui-box-grey .phui-object-item-link {
   color: {$darkbluetext};
 }
 
 .phui-box-blue .phui-object-item-list-view,
 .phui-box-grey .phui-object-item-list-view {
   background-color: #fff;
 }
 
 .phui-box-blue .phui-header-shell {
   border-color: {$thinblueborder};
 }
 
 .phui-box-grey .phui-header-shell {
   border-color: rgba({$alphagrey},.1);
 }
 
 .phui-object-box.phui-box-blue div.phui-info-severity-nodata,
 .phui-object-box.phui-box-grey div.phui-info-severity-nodata {
   background: transparent;
   padding: 20px 4px 24px;
   text-align: center;
   border: none;
   color: {$greytext};
 }
 
 
 /* Property Boxes */
 
 .phui-box.phui-box-blue-property {
   border-radius: 3px;
   border-color: {$lightblueborder};
   margin: 0;
   padding: 0;
 }
 
 .phui-box.phui-box-blue-property .phui-header-header .phui-header-icon {
   margin-right: 6px;
 }
 
 .phui-box.phui-box-blue-property .phui-header-action-link {
   margin-top: 0;
   margin-bottom: 0;
 }
 
 .device .phui-box.phui-box-blue-property {
   padding: 0;
 }
 
 .phui-box.phui-object-box.phui-box-blue-property .phui-header-shell {
   background-color: #eff3fc;
   border-top-right-radius: 3px;
   border-top-left-radius: 3px;
   padding: 6px 16px;
 }
 
-.device .phui-box.phui-box-blue-property .phui-header-shell,
-.device .phui-box-blue-property.phui-object-box.phui-object-box-collapsed
+body.device .phui-box.phui-box-blue-property .phui-header-shell,
+body.device .phui-box-blue-property.phui-object-box.phui-object-box-collapsed
   .phui-header-shell {
     padding: 6px 12px;
+    margin-top: 0;
 }
 
 .phui-box.phui-box-blue-property .phui-header-header {
   font-size: {$biggerfontsize};
   color: {$bluetext};
 }
 
 .phui-box-blue-property .phui-object-item-list-view {
   padding: 2px 8px;
 }
 
 body .phui-box-blue-property.phui-object-box.phui-object-box-collapsed {
   padding: 0;
 }
 
 body .phui-box-blue-property .phui-header-shell + .phui-object-box {
   margin-bottom: 0;
 }
 
 .phui-box-blue-property .phui-header-shell + .phui-object-box
   .phui-header-shell {
     background: #fff;
 }
diff --git a/webroot/rsrc/css/phui/phui-document-pro.css b/webroot/rsrc/css/phui/phui-document-pro.css
index 85f815001..0df18815d 100644
--- a/webroot/rsrc/css/phui/phui-document-pro.css
+++ b/webroot/rsrc/css/phui/phui-document-pro.css
@@ -1,203 +1,222 @@
 /**
  * @provides phui-document-view-pro-css
  */
 
 .phui-document-view.phui-document-view-pro {
   max-width: 800px;
   padding: 16px 16px 64px 16px;
   margin: 0 auto;
 }
 
 .phui-document-container {
   background-color: #fff;
   position: relative;
   border-bottom: 1px solid #dedee1;
 }
 
 .phui-document-view-pro-box,
 .phui-document-properties {
   max-width: 800px;
   margin: 0 auto;
 }
 
 .device .phui-document-view-pro-box {
   margin: 0 8px;
 }
 
 .phui-document-view-pro-box .phui-property-list-section {
   margin: 16px auto;
 }
 
 .device .phui-document-view-pro-box .phui-property-list-section {
   margin: 0 8px 16px;
 }
 
 .device .phui-document-view-pro-box .phui-property-list-container {
   padding: 24px 0 0 0;
 }
 
 .device-phone .phui-document-view.phui-document-view-pro {
   padding: 0 8px;
   margin: 0 auto;
 }
 
 .phui-document-view-pro .phui-document-toc {
   position: absolute;
   top: 34px;
   left: -36px;
 }
 
 a.button.phui-document-toc {
   display: inline-block;
   height: 16px;
   width: 16px;
   padding: 3px 8px 4px 8px;
 }
 
 .phui-document-view-pro .phui-document-toc-list {
   margin: 8px;
   border: 1px solid {$blueborder};
   border-radius: 3px;
   box-shadow: {$dropshadow};
   width: 200px;
   position: absolute;
   z-index: 30;
   background-color: #fff;
   top: 52px;
   left: -44px;
 }
 
 .device .phui-document-view-pro .phui-document-toc {
   display: none;
 }
 
 .phui-document-toc-list {
   display: none;
 }
 
 .phui-document-toc-open .phui-document-toc-list {
   display: block;
 }
 
 .phui-document-toc-open .phui-document-toc {
   background-color: {$blue};
 }
 
 .phui-document-toc-open .phui-document-toc .phui-icon-view {
   color: #fff;
 }
 
 .phui-document-view-pro .phui-document-toc-content {
   margin: 4px 12px;
 }
 
 .phui-document-view-pro .phui-document-toc-header {
   font-weight: bold;
   color: {$bluetext};
   margin-bottom: 8px;
 }
 
 .phui-document-view-pro .phui-document-toc-content li {
   margin: 4px 8px;
 }
 
 .phui-document-view-pro .phui-document-content .phabricator-remarkup {
   padding: 16px 0;
   line-height: 1.7em;
 }
 
 .device-desktop .phui-document-view.phui-document-view-pro {
   border: 0;
 }
 
 .phui-document-view.phui-document-view-pro .phui-header-shell {
   background: transparent;
   border-bottom: 1px solid {$thinblueborder};
 }
 
 .phui-document-view.phui-document-view-pro .phui-header-shell {
   margin: 0;
   padding: 16px 0 32px;
 }
 
 .device-phone .phui-document-view.phui-document-view-pro .phui-header-shell {
   margin: 8px 0 0 0;
   padding: 8px 0 20px;
 }
 
 .phui-document-view.phui-document-view-pro .phui-header-tall
   .phui-header-header {
   font-size: 24px;
   line-height: 30px;
   color: #000;
 }
 
 .device-phone .phui-document-view-pro .phui-header-subheader {
   display: block;
   padding: 8px 0 0 0;
 }
 
 .phui-document-view-pro .phui-info-view {
   margin: 16px 0 0 0;
 }
 
+.phui-document-view-pro .phabricator-remarkup-embed-image-wide {
+  margin-left: -200px;
+  margin-right: -200px;
+  width: auto;
+}
+
+.phui-document-view-pro .phabricator-remarkup-embed-image-wide img {
+  max-width: 1200px;
+}
 
+@media (max-width: 1200px) {
+  .phui-document-view-pro .phabricator-remarkup-embed-image-wide {
+    margin-left: 0;
+    margin-right: 0;
+    width: auto;
+  }
 
+  .phui-document-view-pro .phabricator-remarkup-embed-image-wide img {
+    max-width: inherit;
+  }
+}
 
 .phui-document-view-pro-box .phui-timeline-view {
   padding: 16px 0 0 0;
   background: none;
   border-top: 1px solid rgba({$alphablue}, 0.20);
 }
 
 .phui-document-view-pro-box .phui-timeline-image {
   border-radius: 25px;
 }
 
 .phui-document-view-pro-box .phui-timeline-wedge {
   display: none;
 }
 
 .phui-document-view-pro-box .phui-timeline-major-event .phui-timeline-group {
   border: none;
 }
 
 .phui-document-view-pro-box .phui-timeline-major-event .phui-timeline-content {
   border: none;
 }
 
 .device-desktop .phui-document-view-pro-box
   .phui-timeline-event-view.phui-timeline-minor-event {
     margin-left: 62px;
 }
 
 .phui-document-view-pro-box .phui-timeline-title {
   border-top-right-radius: 3px;
   border-top-left-radius: 3px;
   background-color: #fff;
   border-bottom: 1px solid #F1F1F4;
 }
 
 .phui-document-view-pro-box .phui-timeline-title-with-icon {
   padding-left: 12px;
 }
 
 .phui-document-view-pro-box .phui-timeline-icon-fill {
   display: none;
 }
 
 .phui-document-view-pro-box .phui-object-box {
   margin: 0;
 }
 
 .phui-document-view-pro-box .phui-object-box .remarkup-assist-textarea {
   height: 9em;
 }
 
 .document-has-foot .phui-document-view-pro {
   padding-bottom: 0;
 }
 
 .phui-document-foot-content {
   margin: 64px 0 32px;
 }
diff --git a/webroot/rsrc/css/phui/phui-document.css b/webroot/rsrc/css/phui/phui-document.css
index fd1f057b8..2b75227c4 100644
--- a/webroot/rsrc/css/phui/phui-document.css
+++ b/webroot/rsrc/css/phui/phui-document.css
@@ -1,161 +1,160 @@
 /**
  * @provides phui-document-view-css
  */
 
 .phui-document-view {
   margin-bottom: 16px;
   border-radius: 3px;
   position: relative;
 }
 
 .phui-document-view .phui-header-shell {
   padding: 16px;
   background-color: {$bluebackground};
 }
 
 .phui-document-content {
   border-radius: 3px;
 }
 
 .device-desktop .phui-document-view {
   border: 1px solid {$lightblueborder};
   max-width: 960px;
   margin: 16px auto;
 }
 
 .device-desktop .phui-document-box {
   max-width: 996px;
   margin: 24px auto;
 }
 
 .device-desktop .phui-document-fluid .phui-document-view {
   max-width: none;
   margin: 16px;
 }
 
 .phui-crumbs-view + .phui-document-fluid .phui-document-view {
   margin-top: 0;
 }
 
 .device-desktop .phui-document-view.phui-offset-view {
   max-width: 800px;
 }
 
 /* Fix so that Phriction Document preview is the same width as the document */
 .device-desktop .phui-remarkup-preview .phui-document-view {
   width: 800px;
 }
 
 .phui-document-sidenav {
   position:absolute;
   width: 200px;
   text-overflow: ellipsis;
   top: 0;
   right: 0;
 }
 
 .device-phone .phui-document-sidenav {
   position: static;
   width: auto;
   border-top: 1px solid {$thinblueborder};
   border-bottom: 1px solid {$thinblueborder};
 }
 
 .device-phone .phui-sidenav-view .phui-document-inner {
   margin: 0;
 }
 
 .phui-sidenav-view .phui-document-inner {
   margin-right: 200px;
   border-right: 1px solid {$thinblueborder};
   background: #fff;
 }
 
 .phui-document-content .phui-header-shell {
   border-top: none;
   border-bottom: 1px solid {$lightblueborder};
 }
 
 .phui-document-content
   .phui-header-shell.phui-header-no-backgound {
   border-bottom: 1px solid {$thinblueborder};
   margin: 0 0 16px 0;
 }
 
 .phui-document-content
   .phui-header-shell.phui-header-no-backgound
   .phui-header-view {
   padding: 8px 0 4px;
 }
 
 .phui-document-content .phui-property-list-container {
   border-bottom: 1px solid {$thinblueborder};
   background-color: {$lightgreybackground};
 }
 
 .legalpad .phui-document-content .phui-property-list-view {
   border: none;
   box-shadow: none;
   border-radius: 3px;
   margin: 16px 0 0  0;
   background-color: {$bluebackground};
 }
 
 .phui-document-content {
   background: #fff;
-  overflow: hidden;
 }
 
 .phui-document-content .phabricator-remarkup {
   padding: 16px;
   font-size: 14px;
 }
 
 .phui-document-view .phui-header-action-links .phui-mobile-menu {
   display: block;
 }
 
 .device-phone .phui-document-content .phabricator-remarkup {
   padding: 8px;
 }
 
 .device-desktop .phui-document-content .phabricator-action-list-view {
   display: none;
 }
 
 .phui-document-content .phabricator-remarkup .remarkup-code-block {
   clear: both;
   margin: 16px 0;
 }
 
 .device-desktop .phui-document-offset {
   padding-right: 120px;
 }
 
 .phui-document-view .phui-info-severity-nodata {
   background-color: {$lightgreybackground};
 }
 
 body .phui-document-view .phui-header-shell.phui-bleed-header {
   padding: 0;
 }
 
 .phui-document-view .phui-property-list-section-header {
   padding: 20px 24px 0px;
   border-top: none;
 }
 
 .phui-document-view .phui-property-list-text-content {
   padding: 0 24px 4px;
 }
 
 .phui-document-view .PhabricatorMonospaced,
 .phui-document-view .phabricator-remarkup .remarkup-code-block .remarkup-code {
   font: 12px/18px "Menlo", "Consolas", "Monaco", monospace;
 }
 
 .platform-windows .phui-document-view .PhabricatorMonospaced,
 .platform-windows .phui-document-view .phabricator-remarkup .remarkup-code-block
   .remarkup-code {
   font: 13px/18px "Menlo", "Consolas", "Monaco", monospace;
 }
diff --git a/webroot/rsrc/css/phui/phui-feed-story.css b/webroot/rsrc/css/phui/phui-feed-story.css
index 0d4b98a46..d971202eb 100644
--- a/webroot/rsrc/css/phui/phui-feed-story.css
+++ b/webroot/rsrc/css/phui/phui-feed-story.css
@@ -1,109 +1,108 @@
 /**
  * @provides phui-feed-story-css
  */
 
 .phui-object-box .phui-box.phui-feed-story {
   border-bottom: 1px solid {$thinblueborder};
 }
 
 .phui-object-box .phui-box.phui-feed-story:last-child {
   border: none;
 }
 
 .phui-feed-story-head .phui-feed-story-actor {
   width: 35px;
   height: 35px;
   float: left;
   margin-right: 8px;
   border-radius: 3px;
   box-shadow: {$borderinset};
 }
 
 .phui-feed-story-head .phui-feed-story-actor-image {
   background-size: 35px;
 }
 
 .phui-feed-story-head .phui-feed-story-actor-icon {
   text-align: center;
   vertical-align: middle;
   font-size: 24px;
-  line-height: 35px;
-  color: {$lightgreytext};
-  background: {$greybackground};
+  line-height: 32px;
+  color: #fff;
+  background-color: #c4cde0;
 }
 
-
 .phui-feed-story-head {
   padding: 12px 4px;
   overflow: hidden;
   color: {$greytext};
   word-break: break-word;
 }
 
 .phui-feed-story-head .phui-link-person {
   color: {$darkgreytext};
   font-weight: bold;
 }
 
 .phui-feed-story-body {
   margin: 4px 4px 8px;
   padding-bottom: 8px;
   color: {$darkgreytext};
   word-break: break-word;
   max-height: 300px;
   overflow: hidden;
 }
 
 .phui-feed-story-foot {
   font-size: {$smallerfontsize};
   padding: 0 4px 12px;
 }
 
 .phui-feed-story-foot,
 .phui-feed-story-foot a {
   color: {$greytext};
 }
 
 .phui-feed-story-foot .phui-icon-view {
   margin-right: 5px;
 }
 
 .phui-feed-story-bigtext-post {
   line-height: 18px;
   color: {$darkgreytext};
 }
 
 .phui-feed-story-bigtext-post h3 {
   font-size: {$biggestfontsize};
   line-height: 18px;
   color: {$darkgreytext};
   margin: 0 0 5px 0;
 }
 
 .phui-feed-token-bar {
   margin-top: 8px;
   border-top: 1px solid #e7e7e7;
   padding-top: 8px;
 }
 
 .phui-feed-token-bar .phui-icon-view {
   margin-right: 2px;
   display: inline-block;
 }
 
 .phui-feed-story-action-list {
   float: right;
   padding-top: 4px;
 }
 
 .phui-feed-story-action-item {
   float: right;
   padding-left: 2px;
   height: 18px;
   width: 18px;
   font-size: 18px;
 }
 
 .phui-feed-story-action-list .phui-icon-view {
   display: block;
 }
diff --git a/webroot/rsrc/css/phui/phui-status.css b/webroot/rsrc/css/phui/phui-status.css
index d8f46c002..dd22e6f33 100644
--- a/webroot/rsrc/css/phui/phui-status.css
+++ b/webroot/rsrc/css/phui/phui-status.css
@@ -1,35 +1,38 @@
 /**
  * @provides phui-status-list-view-css
  */
 
 .phui-status-list-view {
   width: 100%;
 }
 
 .phui-status-list-view .phui-icon-view {
   width: 14px;
   height: 14px;
   margin: 2px 4px 2px 0;
 }
 
 .phui-status-item-target {
-  padding: 0 12px 0 0;
-  line-height: 20px;
+  padding: 1px 8px;
   white-space: nowrap;
 }
 
 .phui-status-item-note {
   width: 100%;
   color: {$greytext};
   line-height: 14px;
-  padding: 3px 0;
+  padding: 3px 4px;
 }
 
 .phui-status-item-highlighted td {
-  background-color: {$lightyellow};
+  background-color: {$sh-yellowbackground};
+  border-radius: 3px;
+}
+
+.phui-status-list-view td a {
+ color: {$darkbluetext};
 }
 
 .phui-status-item-highlighted td.phui-status-item-note {
   background-color: transparent;
-  padding-left: 4px;
 }
diff --git a/webroot/rsrc/css/syntax/syntax-default.css b/webroot/rsrc/css/syntax/syntax-default.css
new file mode 100644
index 000000000..687698b1e
--- /dev/null
+++ b/webroot/rsrc/css/syntax/syntax-default.css
@@ -0,0 +1,150 @@
+/**
+ * @provides syntax-default-css
+ * @generated
+ */
+
+.remarkup-code .bp,
+.remarkup-code .cp,
+.remarkup-code .kp,
+.remarkup-code .kr,
+.remarkup-code .nb {
+  color: #304a96
+}
+
+.remarkup-code .c,
+.remarkup-code .c1,
+.remarkup-code .cm,
+.remarkup-code .cs {
+  color: #74777d
+}
+
+.remarkup-code .gd {
+  color: #a00000
+}
+
+.remarkup-code .gh,
+.remarkup-code .gp {
+  color: #000080
+}
+
+.remarkup-code .gi {
+  color: #00a000
+}
+
+.remarkup-code .go {
+  color: #808080
+}
+
+.remarkup-code .gr {
+  color: #ff0000
+}
+
+.remarkup-code .gt {
+  color: #0040d0
+}
+
+.remarkup-code .gu {
+  color: #800080
+}
+
+.remarkup-code .hll {
+  color: #ffffcc
+}
+
+.remarkup-code .il,
+.remarkup-code .m,
+.remarkup-code .mf,
+.remarkup-code .mh,
+.remarkup-code .mi,
+.remarkup-code .mo {
+  color: #601200
+}
+
+.remarkup-code .k,
+.remarkup-code .kd,
+.remarkup-code .kn,
+.remarkup-code .kt {
+  color: #aa4000
+}
+
+.remarkup-code .kc,
+.remarkup-code .no {
+  color: #000a65
+}
+
+.remarkup-code .na {
+  color: #354bb3
+}
+
+.remarkup-code .nc,
+.remarkup-code .nt,
+.remarkup-code .vc {
+  color: #00702a
+}
+
+.remarkup-code .nd,
+.remarkup-code .ow {
+  color: #aa22ff
+}
+
+.remarkup-code .ne {
+  color: #d2413a
+}
+
+.remarkup-code .nf,
+.remarkup-code .nx {
+  color: #004012
+}
+
+.remarkup-code .ni {
+  color: #92969d
+}
+
+.remarkup-code .nl {
+  color: #a0a000
+}
+
+.remarkup-code .nn {
+  color: #0000ff
+}
+
+.remarkup-code .nv,
+.remarkup-code .vg,
+.remarkup-code .vi {
+  color: #001294
+}
+
+.remarkup-code .o,
+.remarkup-code .ss {
+  color: #aa2211
+}
+
+.remarkup-code .s,
+.remarkup-code .s1,
+.remarkup-code .s2,
+.remarkup-code .sb,
+.remarkup-code .sc,
+.remarkup-code .sx {
+  color: #766510
+}
+
+.remarkup-code .sd,
+.remarkup-code .sh {
+  color: #000000
+}
+
+.remarkup-code .se {
+  color: #bb6622
+}
+
+.remarkup-code .si {
+  color: #bb66bb
+}
+
+.remarkup-code .sr {
+  color: #bb6688
+}
+
+.remarkup-code .w {
+  color: #bbbbbb
+}
diff --git a/webroot/rsrc/js/core/behavior-select-content.js b/webroot/rsrc/js/core/behavior-select-content.js
new file mode 100644
index 000000000..b54b6308c
--- /dev/null
+++ b/webroot/rsrc/js/core/behavior-select-content.js
@@ -0,0 +1,23 @@
+/**
+ * @provides javelin-behavior-select-content
+ * @requires javelin-behavior
+ *           javelin-stratcom
+ *           javelin-dom
+ * @javelin
+ */
+
+JX.behavior('select-content', function() {
+  JX.Stratcom.listen(
+    'click',
+    'select-content',
+    function(e) {
+      e.kill();
+
+      var node = e.getNode('select-content');
+      var data = JX.Stratcom.getData(node);
+
+      var target = JX.$(data.selectID);
+      JX.DOM.focus(target);
+      target.select();
+    });
+});