diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php
index 61100f848f..d40b8c6805 100644
--- a/src/__phutil_library_map__.php
+++ b/src/__phutil_library_map__.php
@@ -1,1214 +1,1224 @@
 <?php
 
 /**
  * This file is automatically generated. Use 'phutil_mapper.php' to rebuild it.
  * @generated
  */
 
 phutil_register_library_map(array(
   'class' =>
   array(
     'Aphront304Response' => 'aphront/response/304',
     'Aphront400Response' => 'aphront/response/400',
     'Aphront404Response' => 'aphront/response/404',
     'AphrontAjaxResponse' => 'aphront/response/ajax',
     'AphrontApplicationConfiguration' => 'aphront/applicationconfiguration',
     'AphrontAttachedFileView' => 'view/control/attachedfile',
     'AphrontCSRFException' => 'aphront/exception/csrf',
     'AphrontCalendarMonthView' => 'applications/calendar/view/month',
     'AphrontContextBarView' => 'view/layout/contextbar',
     'AphrontController' => 'aphront/controller',
     'AphrontCrumbsView' => 'view/layout/crumbs',
     'AphrontDatabaseConnection' => 'storage/connection/base',
     'AphrontDefaultApplicationConfiguration' => 'aphront/default/configuration',
     'AphrontDefaultApplicationController' => 'aphront/default/controller',
     'AphrontDialogResponse' => 'aphront/response/dialog',
     'AphrontDialogView' => 'view/dialog',
     'AphrontErrorView' => 'view/form/error',
     'AphrontException' => 'aphront/exception/base',
     'AphrontFilePreviewView' => 'view/layout/filepreview',
     'AphrontFileResponse' => 'aphront/response/file',
     'AphrontFormCheckboxControl' => 'view/form/control/checkbox',
     'AphrontFormControl' => 'view/form/control/base',
     'AphrontFormDividerControl' => 'view/form/control/divider',
     'AphrontFormDragAndDropUploadControl' => 'view/form/control/draganddropupload',
     'AphrontFormFileControl' => 'view/form/control/file',
     'AphrontFormLayoutView' => 'view/form/layout',
     'AphrontFormMarkupControl' => 'view/form/control/markup',
     'AphrontFormPasswordControl' => 'view/form/control/password',
     'AphrontFormRecaptchaControl' => 'view/form/control/recaptcha',
     'AphrontFormSelectControl' => 'view/form/control/select',
     'AphrontFormStaticControl' => 'view/form/control/static',
     'AphrontFormSubmitControl' => 'view/form/control/submit',
     'AphrontFormTextAreaControl' => 'view/form/control/textarea',
     'AphrontFormTextControl' => 'view/form/control/text',
     'AphrontFormToggleButtonsControl' => 'view/form/control/togglebuttons',
     'AphrontFormTokenizerControl' => 'view/form/control/tokenizer',
     'AphrontFormView' => 'view/form/base',
     'AphrontHeadsupActionListView' => 'view/layout/headsup/actionlist',
     'AphrontHeadsupActionView' => 'view/layout/headsup/action',
     'AphrontIsolatedDatabaseConnection' => 'storage/connection/isolated',
     'AphrontIsolatedDatabaseConnectionTestCase' => 'storage/connection/isolated/__tests__',
     'AphrontKeyboardShortcutsAvailableView' => 'view/widget/keyboardshortcuts',
     'AphrontListFilterView' => 'view/layout/listfilter',
     'AphrontMySQLDatabaseConnection' => 'storage/connection/mysql',
     'AphrontNullView' => 'view/null',
     'AphrontPageView' => 'view/page/base',
     'AphrontPagerView' => 'view/control/pager',
     'AphrontPanelView' => 'view/layout/panel',
     'AphrontQueryAccessDeniedException' => 'storage/exception/accessdenied',
     'AphrontQueryConnectionException' => 'storage/exception/connection',
     'AphrontQueryConnectionLostException' => 'storage/exception/connectionlost',
     'AphrontQueryCountException' => 'storage/exception/count',
     'AphrontQueryDuplicateKeyException' => 'storage/exception/duplicatekey',
     'AphrontQueryException' => 'storage/exception/base',
     'AphrontQueryObjectMissingException' => 'storage/exception/objectmissing',
     'AphrontQueryParameterException' => 'storage/exception/parameter',
     'AphrontQueryRecoverableException' => 'storage/exception/recoverable',
     'AphrontRedirectException' => 'aphront/exception/redirect',
     'AphrontRedirectResponse' => 'aphront/response/redirect',
     'AphrontReloadResponse' => 'aphront/response/reload',
     'AphrontRequest' => 'aphront/request',
     'AphrontRequestFailureView' => 'view/page/failure',
     'AphrontResponse' => 'aphront/response/base',
     'AphrontSideNavView' => 'view/layout/sidenav',
     'AphrontTableView' => 'view/control/table',
     'AphrontTokenizerTemplateView' => 'view/control/tokenizer',
     'AphrontTypeaheadTemplateView' => 'view/control/typeahead',
     'AphrontURIMapper' => 'aphront/mapper',
     'AphrontView' => 'view/base',
     'AphrontWebpageResponse' => 'aphront/response/webpage',
     'CelerityAPI' => 'infrastructure/celerity/api',
     'CelerityResourceController' => 'infrastructure/celerity/controller',
     'CelerityResourceMap' => 'infrastructure/celerity/map',
     'CelerityStaticResourceResponse' => 'infrastructure/celerity/response',
     'ConduitAPIMethod' => 'applications/conduit/method/base',
     'ConduitAPIRequest' => 'applications/conduit/protocol/request',
     'ConduitAPI_conduit_connect_Method' => 'applications/conduit/method/conduit/connect',
     'ConduitAPI_conduit_getcertificate_Method' => 'applications/conduit/method/conduit/getcertificate',
     'ConduitAPI_conduit_ping_Method' => 'applications/conduit/method/conduit/ping',
     'ConduitAPI_daemon_launched_Method' => 'applications/conduit/method/daemon/launched',
     'ConduitAPI_daemon_log_Method' => 'applications/conduit/method/daemon/log',
     'ConduitAPI_differential_creatediff_Method' => 'applications/conduit/method/differential/creatediff',
     'ConduitAPI_differential_createrevision_Method' => 'applications/conduit/method/differential/createrevision',
     'ConduitAPI_differential_find_Method' => 'applications/conduit/method/differential/find',
     'ConduitAPI_differential_getalldiffs_Method' => 'applications/conduit/method/differential/getalldiffs',
     'ConduitAPI_differential_getcommitmessage_Method' => 'applications/conduit/method/differential/getcommitmessage',
     'ConduitAPI_differential_getcommitpaths_Method' => 'applications/conduit/method/differential/getcommitpaths',
     'ConduitAPI_differential_getdiff_Method' => 'applications/conduit/method/differential/getdiff',
     'ConduitAPI_differential_getrevision_Method' => 'applications/conduit/method/differential/getrevision',
     'ConduitAPI_differential_getrevisionfeedback_Method' => 'applications/conduit/method/differential/getrevisionfeedback',
     'ConduitAPI_differential_markcommitted_Method' => 'applications/conduit/method/differential/markcommitted',
     'ConduitAPI_differential_parsecommitmessage_Method' => 'applications/conduit/method/differential/parsecommitmessage',
     'ConduitAPI_differential_setdiffproperty_Method' => 'applications/conduit/method/differential/setdiffproperty',
     'ConduitAPI_differential_updaterevision_Method' => 'applications/conduit/method/differential/updaterevision',
     'ConduitAPI_differential_updatetaskrevisionassoc_Method' => 'applications/conduit/method/differential/updatetaskrevisionassoc',
     'ConduitAPI_differential_updateunitresults_Method' => 'applications/conduit/method/differential/updateunitresults',
     'ConduitAPI_diffusion_getcommits_Method' => 'applications/conduit/method/diffusion/getcommits',
     'ConduitAPI_diffusion_getrecentcommitsbypath_Method' => 'applications/conduit/method/diffusion/getrecentcommitsbypath',
     'ConduitAPI_feed_publish_Method' => 'applications/conduit/method/feed/publish',
     'ConduitAPI_file_download_Method' => 'applications/conduit/method/file/download',
     'ConduitAPI_file_info_Method' => 'applications/conduit/method/file/info',
     'ConduitAPI_file_upload_Method' => 'applications/conduit/method/file/upload',
     'ConduitAPI_maniphest_info_Method' => 'applications/conduit/method/maniphest/info',
     'ConduitAPI_paste_Method' => 'applications/conduit/method/paste/base',
     'ConduitAPI_paste_create_Method' => 'applications/conduit/method/paste/create',
     'ConduitAPI_paste_info_Method' => 'applications/conduit/method/paste/info',
     'ConduitAPI_path_getowners_Method' => 'applications/conduit/method/path/getowners',
     'ConduitAPI_slowvote_info_Method' => 'applications/conduit/method/slowvote/info',
     'ConduitAPI_user_find_Method' => 'applications/conduit/method/user/find',
     'ConduitAPI_user_whoami_Method' => 'applications/conduit/method/user/whoami',
     'ConduitException' => 'applications/conduit/protocol/exception',
     'DarkConsole' => 'aphront/console/api',
     'DarkConsoleConfigPlugin' => 'aphront/console/plugin/config',
     'DarkConsoleController' => 'aphront/console/controller',
     'DarkConsoleCore' => 'aphront/console/core',
     'DarkConsoleErrorLogPlugin' => 'aphront/console/plugin/errorlog',
     'DarkConsoleErrorLogPluginAPI' => 'aphront/console/plugin/errorlog/api',
     'DarkConsolePlugin' => 'aphront/console/plugin/base',
     'DarkConsoleRequestPlugin' => 'aphront/console/plugin/request',
     'DarkConsoleServicesPlugin' => 'aphront/console/plugin/services',
     'DarkConsoleXHProfPlugin' => 'aphront/console/plugin/xhprof',
     'DarkConsoleXHProfPluginAPI' => 'aphront/console/plugin/xhprof/api',
     'DatabaseConfigurationProvider' => 'applications/base/storage/configuration',
     'DifferentialAction' => 'applications/differential/constants/action',
     'DifferentialAddCommentView' => 'applications/differential/view/addcomment',
     'DifferentialApplyPatchFieldSpecification' => 'applications/differential/field/specification/applypatch',
     'DifferentialArcanistProjectFieldSpecification' => 'applications/differential/field/specification/arcanistproject',
     'DifferentialAuthorFieldSpecification' => 'applications/differential/field/specification/author',
     'DifferentialAuxiliaryField' => 'applications/differential/storage/auxiliaryfield',
     'DifferentialBlameRevisionFieldSpecification' => 'applications/differential/field/specification/blamerev',
     'DifferentialCCWelcomeMail' => 'applications/differential/mail/ccwelcome',
+    'DifferentialCCsFieldSpecification' => 'applications/differential/field/specification/ccs',
     'DifferentialChangeType' => 'applications/differential/constants/changetype',
     'DifferentialChangeset' => 'applications/differential/storage/changeset',
     'DifferentialChangesetDetailView' => 'applications/differential/view/changesetdetailview',
     'DifferentialChangesetListView' => 'applications/differential/view/changesetlistview',
     'DifferentialChangesetParser' => 'applications/differential/parser/changeset',
     'DifferentialChangesetViewController' => 'applications/differential/controller/changesetview',
     'DifferentialComment' => 'applications/differential/storage/comment',
     'DifferentialCommentEditor' => 'applications/differential/editor/comment',
     'DifferentialCommentMail' => 'applications/differential/mail/comment',
     'DifferentialCommentPreviewController' => 'applications/differential/controller/commentpreview',
     'DifferentialCommentSaveController' => 'applications/differential/controller/commentsave',
     'DifferentialCommitMessage' => 'applications/differential/parser/commitmessage',
     'DifferentialCommitMessageData' => 'applications/differential/data/commitmessage',
     'DifferentialCommitMessageField' => 'applications/differential/data/commitmessage',
     'DifferentialCommitMessageModifier' => 'applications/differential/data/commitmessage',
     'DifferentialCommitMessageParserException' => 'applications/differential/parser/commitmessage/exception',
     'DifferentialCommitsFieldSpecification' => 'applications/differential/field/specification/commits',
     'DifferentialController' => 'applications/differential/controller/base',
     'DifferentialDAO' => 'applications/differential/storage/base',
     'DifferentialDefaultFieldSelector' => 'applications/differential/field/selector/default',
     'DifferentialDependenciesFieldSpecification' => 'applications/differential/field/specification/dependencies',
     'DifferentialDiff' => 'applications/differential/storage/diff',
     'DifferentialDiffContentMail' => 'applications/differential/mail/diffcontent',
     'DifferentialDiffCreateController' => 'applications/differential/controller/diffcreate',
     'DifferentialDiffProperty' => 'applications/differential/storage/diffproperty',
     'DifferentialDiffTableOfContentsView' => 'applications/differential/view/difftableofcontents',
     'DifferentialDiffViewController' => 'applications/differential/controller/diffview',
     'DifferentialExceptionMail' => 'applications/differential/mail/exception',
     'DifferentialExportPatchFieldSpecification' => 'applications/differential/field/specification/exportpatch',
     'DifferentialFieldDataNotAvailableException' => 'applications/differential/field/exception/notavailable',
     'DifferentialFieldSelector' => 'applications/differential/field/selector/base',
     'DifferentialFieldSpecification' => 'applications/differential/field/specification/base',
     'DifferentialFieldSpecificationIncompleteException' => 'applications/differential/field/exception/incomplete',
     'DifferentialFieldValidationException' => 'applications/differential/field/exception/validation',
     'DifferentialHostFieldSpecification' => 'applications/differential/field/specification/host',
     'DifferentialHunk' => 'applications/differential/storage/hunk',
     'DifferentialInlineComment' => 'applications/differential/storage/inlinecomment',
     'DifferentialInlineCommentEditController' => 'applications/differential/controller/inlinecommentedit',
     'DifferentialInlineCommentPreviewController' => 'applications/differential/controller/inlinecommentpreview',
     'DifferentialInlineCommentView' => 'applications/differential/view/inlinecomment',
     'DifferentialLinesFieldSpecification' => 'applications/differential/field/specification/lines',
+    'DifferentialLintFieldSpecification' => 'applications/differential/field/specification/lint',
     'DifferentialLintStatus' => 'applications/differential/constants/lintstatus',
     'DifferentialMail' => 'applications/differential/mail/base',
     'DifferentialManiphestTasksFieldSpecification' => 'applications/differential/field/specification/maniphesttasks',
     'DifferentialNewDiffMail' => 'applications/differential/mail/newdiff',
     'DifferentialPathFieldSpecification' => 'applications/differential/field/specification/path',
     'DifferentialPrimaryPaneView' => 'applications/differential/view/primarypane',
     'DifferentialReplyHandler' => 'applications/differential/replyhandler',
     'DifferentialRevertPlanFieldSpecification' => 'applications/differential/field/specification/revertplan',
     'DifferentialReviewRequestMail' => 'applications/differential/mail/reviewrequest',
+    'DifferentialReviewersFieldSpecification' => 'applications/differential/field/specification/reviewers',
     'DifferentialRevision' => 'applications/differential/storage/revision',
     'DifferentialRevisionCommentListView' => 'applications/differential/view/revisioncommentlist',
     'DifferentialRevisionCommentView' => 'applications/differential/view/revisioncomment',
     'DifferentialRevisionControlSystem' => 'applications/differential/constants/revisioncontrolsystem',
     'DifferentialRevisionDetailRenderer' => 'applications/differential/controller/customrenderer',
     'DifferentialRevisionDetailView' => 'applications/differential/view/revisiondetail',
     'DifferentialRevisionEditController' => 'applications/differential/controller/revisionedit',
     'DifferentialRevisionEditor' => 'applications/differential/editor/revision',
     'DifferentialRevisionListController' => 'applications/differential/controller/revisionlist',
     'DifferentialRevisionListData' => 'applications/differential/data/revisionlist',
     'DifferentialRevisionStatus' => 'applications/differential/constants/revisionstatus',
+    'DifferentialRevisionStatusFieldSpecification' => 'applications/differential/field/specification/revisionstatus',
     'DifferentialRevisionUpdateHistoryView' => 'applications/differential/view/revisionupdatehistory',
     'DifferentialRevisionViewController' => 'applications/differential/controller/revisionview',
     'DifferentialSubscribeController' => 'applications/differential/controller/subscribe',
     'DifferentialTasksAttacher' => 'applications/differential/tasks',
+    'DifferentialUnitFieldSpecification' => 'applications/differential/field/specification/unit',
     'DifferentialUnitStatus' => 'applications/differential/constants/unitstatus',
     'DifferentialUnitTestResult' => 'applications/differential/constants/unittestresult',
     'DifferentialViewTime' => 'applications/differential/storage/viewtime',
     'DiffusionBranchInformation' => 'applications/diffusion/data/branch',
     'DiffusionBranchQuery' => 'applications/diffusion/query/branch/base',
     'DiffusionBranchTableView' => 'applications/diffusion/view/branchtable',
     'DiffusionBrowseController' => 'applications/diffusion/controller/browse',
     'DiffusionBrowseFileController' => 'applications/diffusion/controller/file',
     'DiffusionBrowseQuery' => 'applications/diffusion/query/browse/base',
     'DiffusionBrowseTableView' => 'applications/diffusion/view/browsetable',
     'DiffusionChangeController' => 'applications/diffusion/controller/change',
     'DiffusionCommitChangeTableView' => 'applications/diffusion/view/commitchangetable',
     'DiffusionCommitController' => 'applications/diffusion/controller/commit',
     'DiffusionCommitListController' => 'applications/diffusion/controller/commitlist',
     'DiffusionController' => 'applications/diffusion/controller/base',
     'DiffusionDiffController' => 'applications/diffusion/controller/diff',
     'DiffusionDiffQuery' => 'applications/diffusion/query/diff/base',
     'DiffusionFileContent' => 'applications/diffusion/data/filecontent',
     'DiffusionFileContentQuery' => 'applications/diffusion/query/filecontent/base',
     'DiffusionGitBranchQuery' => 'applications/diffusion/query/branch/git',
     'DiffusionGitBrowseQuery' => 'applications/diffusion/query/browse/git',
     'DiffusionGitDiffQuery' => 'applications/diffusion/query/diff/git',
     'DiffusionGitFileContentQuery' => 'applications/diffusion/query/filecontent/git',
     'DiffusionGitHistoryQuery' => 'applications/diffusion/query/history/git',
     'DiffusionGitLastModifiedQuery' => 'applications/diffusion/query/lastmodified/git',
     'DiffusionGitPathIDQuery' => 'applications/diffusion/query/pathid/base',
     'DiffusionGitRequest' => 'applications/diffusion/request/git',
     'DiffusionHistoryController' => 'applications/diffusion/controller/history',
     'DiffusionHistoryQuery' => 'applications/diffusion/query/history/base',
     'DiffusionHistoryTableView' => 'applications/diffusion/view/historytable',
     'DiffusionHomeController' => 'applications/diffusion/controller/home',
     'DiffusionLastModifiedController' => 'applications/diffusion/controller/lastmodified',
     'DiffusionLastModifiedQuery' => 'applications/diffusion/query/lastmodified/base',
     'DiffusionPathChange' => 'applications/diffusion/data/pathchange',
     'DiffusionPathChangeQuery' => 'applications/diffusion/query/pathchange/base',
     'DiffusionPathCompleteController' => 'applications/diffusion/controller/pathcomplete',
     'DiffusionPathValidateController' => 'applications/diffusion/controller/pathvalidate',
     'DiffusionRepositoryController' => 'applications/diffusion/controller/repository',
     'DiffusionRepositoryPath' => 'applications/diffusion/data/repositorypath',
     'DiffusionRequest' => 'applications/diffusion/request/base',
     'DiffusionSvnBrowseQuery' => 'applications/diffusion/query/browse/svn',
     'DiffusionSvnDiffQuery' => 'applications/diffusion/query/diff/svn',
     'DiffusionSvnFileContentQuery' => 'applications/diffusion/query/filecontent/svn',
     'DiffusionSvnHistoryQuery' => 'applications/diffusion/query/history/svn',
     'DiffusionSvnLastModifiedQuery' => 'applications/diffusion/query/lastmodified/svn',
     'DiffusionSvnRequest' => 'applications/diffusion/request/svn',
     'DiffusionView' => 'applications/diffusion/view/base',
     'HeraldAction' => 'applications/herald/storage/action',
     'HeraldActionConfig' => 'applications/herald/config/action',
     'HeraldApplyTranscript' => 'applications/herald/storage/transcript/apply',
     'HeraldCommitAdapter' => 'applications/herald/adapter/commit',
     'HeraldCondition' => 'applications/herald/storage/condition',
     'HeraldConditionConfig' => 'applications/herald/config/condition',
     'HeraldConditionTranscript' => 'applications/herald/storage/transcript/condition',
     'HeraldContentTypeConfig' => 'applications/herald/config/contenttype',
     'HeraldController' => 'applications/herald/controller/base',
     'HeraldDAO' => 'applications/herald/storage/base',
     'HeraldDeleteController' => 'applications/herald/controller/delete',
     'HeraldDifferentialRevisionAdapter' => 'applications/herald/adapter/differential',
     'HeraldDryRunAdapter' => 'applications/herald/adapter/dryrun',
     'HeraldEffect' => 'applications/herald/engine/effect',
     'HeraldEngine' => 'applications/herald/engine/engine',
     'HeraldFieldConfig' => 'applications/herald/config/field',
     'HeraldHomeController' => 'applications/herald/controller/home',
     'HeraldInvalidConditionException' => 'applications/herald/engine/engine/exception',
     'HeraldInvalidFieldException' => 'applications/herald/engine/engine/exception',
     'HeraldNewController' => 'applications/herald/controller/new',
     'HeraldObjectAdapter' => 'applications/herald/adapter/base',
     'HeraldObjectTranscript' => 'applications/herald/storage/transcript/object',
     'HeraldRecursiveConditionsException' => 'applications/herald/engine/engine/exception',
     'HeraldRepetitionPolicyConfig' => 'applications/herald/config/repetitionpolicy',
     'HeraldRule' => 'applications/herald/storage/rule',
     'HeraldRuleController' => 'applications/herald/controller/rule',
     'HeraldRuleTranscript' => 'applications/herald/storage/transcript/rule',
     'HeraldTestConsoleController' => 'applications/herald/controller/test',
     'HeraldTranscript' => 'applications/herald/storage/transcript/base',
     'HeraldTranscriptController' => 'applications/herald/controller/transcript',
     'HeraldTranscriptListController' => 'applications/herald/controller/transcriptlist',
     'HeraldValueTypeConfig' => 'applications/herald/config/valuetype',
     'Javelin' => 'infrastructure/javelin/api',
     'LiskDAO' => 'storage/lisk/dao',
     'LiskIsolationTestCase' => 'storage/lisk/dao/__tests__',
     'LiskIsolationTestDAO' => 'storage/lisk/dao/__tests__',
     'LiskIsolationTestDAOException' => 'storage/lisk/dao/__tests__',
     'ManiphestAuxiliaryFieldDefaultSpecification' => 'applications/maniphest/auxiliaryfield/default',
     'ManiphestAuxiliaryFieldSpecification' => 'applications/maniphest/auxiliaryfield/base',
     'ManiphestAuxiliaryFieldTypeException' => 'applications/maniphest/auxiliaryfield/typeexception',
     'ManiphestAuxiliaryFieldValidationException' => 'applications/maniphest/auxiliaryfield/validationexception',
     'ManiphestConstants' => 'applications/maniphest/constants/base',
     'ManiphestController' => 'applications/maniphest/controller/base',
     'ManiphestDAO' => 'applications/maniphest/storage/base',
     'ManiphestDefaultTaskExtensions' => 'applications/maniphest/extensions/task',
     'ManiphestReplyHandler' => 'applications/maniphest/replyhandler',
     'ManiphestTask' => 'applications/maniphest/storage/task',
     'ManiphestTaskAuxiliaryStorage' => 'applications/maniphest/storage/auxiliary',
     'ManiphestTaskDescriptionChangeController' => 'applications/maniphest/controller/descriptionchange',
     'ManiphestTaskDetailController' => 'applications/maniphest/controller/taskdetail',
     'ManiphestTaskEditController' => 'applications/maniphest/controller/taskedit',
     'ManiphestTaskExtensions' => 'applications/maniphest/extensions/base',
     'ManiphestTaskListController' => 'applications/maniphest/controller/tasklist',
     'ManiphestTaskListView' => 'applications/maniphest/view/tasklist',
     'ManiphestTaskOwner' => 'applications/maniphest/constants/owner',
     'ManiphestTaskPriority' => 'applications/maniphest/constants/priority',
     'ManiphestTaskProject' => 'applications/maniphest/storage/taskproject',
     'ManiphestTaskQuery' => 'applications/maniphest/query',
     'ManiphestTaskStatus' => 'applications/maniphest/constants/status',
     'ManiphestTaskSubscriber' => 'applications/maniphest/storage/subscriber',
     'ManiphestTaskSummaryView' => 'applications/maniphest/view/tasksummary',
     'ManiphestTransaction' => 'applications/maniphest/storage/transaction',
     'ManiphestTransactionDetailView' => 'applications/maniphest/view/transactiondetail',
     'ManiphestTransactionEditor' => 'applications/maniphest/editor/transaction',
     'ManiphestTransactionListView' => 'applications/maniphest/view/transactionlist',
     'ManiphestTransactionPreviewController' => 'applications/maniphest/controller/transactionpreview',
     'ManiphestTransactionSaveController' => 'applications/maniphest/controller/transactionsave',
     'ManiphestTransactionType' => 'applications/maniphest/constants/transactiontype',
     'ManiphestView' => 'applications/maniphest/view/base',
     'Phabricator404Controller' => 'applications/base/controller/404',
     'PhabricatorAuthController' => 'applications/auth/controller/base',
     'PhabricatorCalendarBrowseController' => 'applications/calendar/controller/browse',
     'PhabricatorCalendarController' => 'applications/calendar/controller/base',
     'PhabricatorConduitAPIController' => 'applications/conduit/controller/api',
     'PhabricatorConduitCertificateToken' => 'applications/conduit/storage/token',
     'PhabricatorConduitConnectionLog' => 'applications/conduit/storage/connectionlog',
     'PhabricatorConduitConsoleController' => 'applications/conduit/controller/console',
     'PhabricatorConduitController' => 'applications/conduit/controller/base',
     'PhabricatorConduitDAO' => 'applications/conduit/storage/base',
     'PhabricatorConduitLogController' => 'applications/conduit/controller/log',
     'PhabricatorConduitMethodCallLog' => 'applications/conduit/storage/methodcalllog',
     'PhabricatorConduitTokenController' => 'applications/conduit/controller/token',
     'PhabricatorController' => 'applications/base/controller/base',
     'PhabricatorCountdownController' => 'applications/countdown/controller/base',
     'PhabricatorCountdownDAO' => 'applications/countdown/storage/base',
     'PhabricatorCountdownDeleteController' => 'applications/countdown/controller/delete',
     'PhabricatorCountdownEditController' => 'applications/countdown/controller/edit',
     'PhabricatorCountdownListController' => 'applications/countdown/controller/list',
     'PhabricatorCountdownViewController' => 'applications/countdown/controller/view',
     'PhabricatorDaemon' => 'infrastructure/daemon/base',
     'PhabricatorDaemonCombinedLogController' => 'applications/daemon/controller/combined',
     'PhabricatorDaemonConsoleController' => 'applications/daemon/controller/console',
     'PhabricatorDaemonControl' => 'infrastructure/daemon/control',
     'PhabricatorDaemonController' => 'applications/daemon/controller/base',
     'PhabricatorDaemonDAO' => 'infrastructure/daemon/storage/base',
     'PhabricatorDaemonLog' => 'infrastructure/daemon/storage/log',
     'PhabricatorDaemonLogEvent' => 'infrastructure/daemon/storage/event',
     'PhabricatorDaemonLogEventsView' => 'applications/daemon/view/daemonlogevents',
     'PhabricatorDaemonLogListController' => 'applications/daemon/controller/loglist',
     'PhabricatorDaemonLogListView' => 'applications/daemon/view/daemonloglist',
     'PhabricatorDaemonLogViewController' => 'applications/daemon/controller/logview',
     'PhabricatorDaemonReference' => 'infrastructure/daemon/control/reference',
     'PhabricatorDaemonTimelineConsoleController' => 'applications/daemon/controller/timeline',
     'PhabricatorDaemonTimelineEventController' => 'applications/daemon/controller/timelineevent',
     'PhabricatorDefaultFileStorageEngineSelector' => 'applications/files/engineselector/default',
     'PhabricatorDefaultSearchEngineSelector' => 'applications/search/selector/default',
     'PhabricatorDifferenceEngine' => 'infrastructure/diff/engine',
     'PhabricatorDirectoryCategory' => 'applications/directory/storage/category',
     'PhabricatorDirectoryCategoryDeleteController' => 'applications/directory/controller/categorydelete',
     'PhabricatorDirectoryCategoryEditController' => 'applications/directory/controller/categoryedit',
     'PhabricatorDirectoryCategoryListController' => 'applications/directory/controller/categorylist',
     'PhabricatorDirectoryController' => 'applications/directory/controller/base',
     'PhabricatorDirectoryDAO' => 'applications/directory/storage/base',
     'PhabricatorDirectoryItem' => 'applications/directory/storage/item',
     'PhabricatorDirectoryItemDeleteController' => 'applications/directory/controller/itemdelete',
     'PhabricatorDirectoryItemEditController' => 'applications/directory/controller/itemedit',
     'PhabricatorDirectoryItemListController' => 'applications/directory/controller/itemlist',
     'PhabricatorDirectoryMainController' => 'applications/directory/controller/main',
     'PhabricatorDisabledUserController' => 'applications/auth/controller/disabled',
     'PhabricatorDraft' => 'applications/draft/storage/draft',
     'PhabricatorDraftDAO' => 'applications/draft/storage/base',
     'PhabricatorEmailLoginController' => 'applications/auth/controller/email',
     'PhabricatorEmailTokenController' => 'applications/auth/controller/emailtoken',
     'PhabricatorEnv' => 'infrastructure/env',
     'PhabricatorFeedConstants' => 'applications/feed/constants/base',
     'PhabricatorFeedController' => 'applications/feed/controller/base',
     'PhabricatorFeedDAO' => 'applications/feed/storage/base',
     'PhabricatorFeedPublicStreamController' => 'applications/feed/controller/publicstream',
     'PhabricatorFeedQuery' => 'applications/feed/query',
     'PhabricatorFeedStory' => 'applications/feed/story/base',
     'PhabricatorFeedStoryData' => 'applications/feed/storage/story',
     'PhabricatorFeedStoryDifferential' => 'applications/feed/story/differential',
     'PhabricatorFeedStoryPhriction' => 'applications/feed/story/phriction',
     'PhabricatorFeedStoryPublisher' => 'applications/feed/publisher',
     'PhabricatorFeedStoryReference' => 'applications/feed/storage/storyreference',
     'PhabricatorFeedStoryStatus' => 'applications/feed/story/status',
     'PhabricatorFeedStoryTypeConstants' => 'applications/feed/constants/story',
     'PhabricatorFeedStoryUnknown' => 'applications/feed/story/unknown',
     'PhabricatorFeedStoryView' => 'applications/feed/view/story',
     'PhabricatorFeedStreamController' => 'applications/feed/controller/stream',
     'PhabricatorFeedView' => 'applications/feed/view/base',
     'PhabricatorFile' => 'applications/files/storage/file',
     'PhabricatorFileController' => 'applications/files/controller/base',
     'PhabricatorFileDAO' => 'applications/files/storage/base',
     'PhabricatorFileDropUploadController' => 'applications/files/controller/dropupload',
     'PhabricatorFileImageMacro' => 'applications/files/storage/imagemacro',
     'PhabricatorFileListController' => 'applications/files/controller/list',
     'PhabricatorFileMacroDeleteController' => 'applications/files/controller/macrodelete',
     'PhabricatorFileMacroEditController' => 'applications/files/controller/macroedit',
     'PhabricatorFileMacroListController' => 'applications/files/controller/macrolist',
     'PhabricatorFileProxyController' => 'applications/files/controller/proxy',
     'PhabricatorFileProxyImage' => 'applications/files/storage/proxyimage',
     'PhabricatorFileStorageBlob' => 'applications/files/storage/storageblob',
     'PhabricatorFileStorageEngine' => 'applications/files/engine/base',
     'PhabricatorFileStorageEngineSelector' => 'applications/files/engineselector/base',
     'PhabricatorFileTransformController' => 'applications/files/controller/transform',
     'PhabricatorFileURI' => 'applications/files/uri',
     'PhabricatorFileUploadController' => 'applications/files/controller/upload',
     'PhabricatorFileViewController' => 'applications/files/controller/view',
     'PhabricatorGarbageCollectorDaemon' => 'infrastructure/daemon/garbagecollector',
     'PhabricatorGoodForNothingWorker' => 'infrastructure/daemon/workers/worker/goodfornothing',
     'PhabricatorHandleObjectSelectorDataView' => 'applications/phid/handle/view/selector',
     'PhabricatorHelpController' => 'applications/help/controller/base',
     'PhabricatorHelpKeyboardShortcutController' => 'applications/help/controller/keyboardshortcut',
     'PhabricatorIRCBot' => 'infrastructure/daemon/irc/bot',
     'PhabricatorIRCHandler' => 'infrastructure/daemon/irc/handler/base',
     'PhabricatorIRCMessage' => 'infrastructure/daemon/irc/message',
     'PhabricatorIRCObjectNameHandler' => 'infrastructure/daemon/irc/handler/objectname',
     'PhabricatorIRCProtocolHandler' => 'infrastructure/daemon/irc/handler/protocol',
     'PhabricatorImageTransformer' => 'applications/files/transform',
     'PhabricatorJavelinLinter' => 'infrastructure/lint/linter/javelin',
     'PhabricatorLintEngine' => 'infrastructure/lint/engine',
     'PhabricatorLiskDAO' => 'applications/base/storage/lisk',
     'PhabricatorLocalDiskFileStorageEngine' => 'applications/files/engine/localdisk',
     'PhabricatorLoginController' => 'applications/auth/controller/login',
     'PhabricatorLogoutController' => 'applications/auth/controller/logout',
     'PhabricatorMailImplementationAdapter' => 'applications/metamta/adapter/base',
     'PhabricatorMailImplementationAmazonSESAdapter' => 'applications/metamta/adapter/amazonses',
     'PhabricatorMailImplementationPHPMailerLiteAdapter' => 'applications/metamta/adapter/phpmailerlite',
     'PhabricatorMailImplementationSendGridAdapter' => 'applications/metamta/adapter/sendgrid',
     'PhabricatorMailImplementationTestAdapter' => 'applications/metamta/adapter/test',
     'PhabricatorMailReplyHandler' => 'applications/metamta/replyhandler/base',
     'PhabricatorMarkupEngine' => 'applications/markup/engine',
     'PhabricatorMetaMTAController' => 'applications/metamta/controller/base',
     'PhabricatorMetaMTADAO' => 'applications/metamta/storage/base',
     'PhabricatorMetaMTADaemon' => 'applications/metamta/daemon/mta',
     'PhabricatorMetaMTAEmailBodyParser' => 'applications/metamta/parser',
     'PhabricatorMetaMTAEmailBodyParserTestCase' => 'applications/metamta/parser/__tests__',
     'PhabricatorMetaMTAListController' => 'applications/metamta/controller/list',
     'PhabricatorMetaMTAMail' => 'applications/metamta/storage/mail',
     'PhabricatorMetaMTAMailTestCase' => 'applications/metamta/storage/mail/__tests__',
     'PhabricatorMetaMTAMailingList' => 'applications/metamta/storage/mailinglist',
     'PhabricatorMetaMTAMailingListEditController' => 'applications/metamta/controller/mailinglistedit',
     'PhabricatorMetaMTAMailingListsController' => 'applications/metamta/controller/mailinglists',
     'PhabricatorMetaMTAReceiveController' => 'applications/metamta/controller/receive',
     'PhabricatorMetaMTAReceivedListController' => 'applications/metamta/controller/receivedlist',
     'PhabricatorMetaMTAReceivedMail' => 'applications/metamta/storage/receivedmail',
     'PhabricatorMetaMTASendController' => 'applications/metamta/controller/send',
     'PhabricatorMetaMTASendGridReceiveController' => 'applications/metamta/controller/sendgridreceive',
     'PhabricatorMetaMTAViewController' => 'applications/metamta/controller/view',
     'PhabricatorMySQLFileStorageEngine' => 'applications/files/engine/mysql',
     'PhabricatorOAuthDefaultRegistrationController' => 'applications/auth/controller/oauthregistration/default',
     'PhabricatorOAuthDiagnosticsController' => 'applications/auth/controller/oauthdiagnostics',
     'PhabricatorOAuthFailureView' => 'applications/auth/view/oauthfailure',
     'PhabricatorOAuthLoginController' => 'applications/auth/controller/oauth',
     'PhabricatorOAuthProvider' => 'applications/auth/oauth/provider/base',
     'PhabricatorOAuthProviderFacebook' => 'applications/auth/oauth/provider/facebook',
     'PhabricatorOAuthProviderGithub' => 'applications/auth/oauth/provider/github',
     'PhabricatorOAuthRegistrationController' => 'applications/auth/controller/oauthregistration/base',
     'PhabricatorOAuthUnlinkController' => 'applications/auth/controller/unlink',
     'PhabricatorObjectGraph' => 'applications/phid/graph',
     'PhabricatorObjectHandle' => 'applications/phid/handle',
     'PhabricatorObjectHandleConstants' => 'applications/phid/handle/const/base',
     'PhabricatorObjectHandleData' => 'applications/phid/handle/data',
     'PhabricatorObjectHandleStatus' => 'applications/phid/handle/const/status',
     'PhabricatorObjectSelectorDialog' => 'view/control/objectselector',
     'PhabricatorOwnersController' => 'applications/owners/controller/base',
     'PhabricatorOwnersDAO' => 'applications/owners/storage/base',
     'PhabricatorOwnersDeleteController' => 'applications/owners/controller/delete',
     'PhabricatorOwnersDetailController' => 'applications/owners/controller/detail',
     'PhabricatorOwnersEditController' => 'applications/owners/controller/edit',
     'PhabricatorOwnersListController' => 'applications/owners/controller/list',
     'PhabricatorOwnersOwner' => 'applications/owners/storage/owner',
     'PhabricatorOwnersPackage' => 'applications/owners/storage/package',
     'PhabricatorOwnersPath' => 'applications/owners/storage/path',
     'PhabricatorPHID' => 'applications/phid/storage/phid',
     'PhabricatorPHIDConstants' => 'applications/phid/constants',
     'PhabricatorPHIDController' => 'applications/phid/controller/base',
     'PhabricatorPHIDDAO' => 'applications/phid/storage/base',
     'PhabricatorPHIDListController' => 'applications/phid/controller/list',
     'PhabricatorPHIDLookupController' => 'applications/phid/controller/lookup',
     'PhabricatorPaste' => 'applications/paste/storage/paste',
     'PhabricatorPasteController' => 'applications/paste/controller/base',
     'PhabricatorPasteCreateController' => 'applications/paste/controller/create',
     'PhabricatorPasteDAO' => 'applications/paste/storage/base',
     'PhabricatorPasteListController' => 'applications/paste/controller/list',
     'PhabricatorPasteViewController' => 'applications/paste/controller/view',
     'PhabricatorPeopleController' => 'applications/people/controller/base',
     'PhabricatorPeopleEditController' => 'applications/people/controller/edit',
     'PhabricatorPeopleListController' => 'applications/people/controller/list',
     'PhabricatorPeopleLogsController' => 'applications/people/controller/logs',
     'PhabricatorPeopleProfileController' => 'applications/people/controller/profile',
     'PhabricatorProfileView' => 'view/layout/profile',
     'PhabricatorProject' => 'applications/project/storage/project',
     'PhabricatorProjectAffiliation' => 'applications/project/storage/affiliation',
     'PhabricatorProjectAffiliationEditController' => 'applications/project/controller/editaffiliation',
     'PhabricatorProjectController' => 'applications/project/controller/base',
     'PhabricatorProjectCreateController' => 'applications/project/controller/create',
     'PhabricatorProjectDAO' => 'applications/project/storage/base',
     'PhabricatorProjectListController' => 'applications/project/controller/list',
     'PhabricatorProjectProfile' => 'applications/project/storage/profile',
     'PhabricatorProjectProfileController' => 'applications/project/controller/profile',
     'PhabricatorProjectProfileEditController' => 'applications/project/controller/profileedit',
     'PhabricatorProjectStatus' => 'applications/project/constants/status',
     'PhabricatorProjectSubproject' => 'applications/project/storage/subproject',
     'PhabricatorRedirectController' => 'applications/base/controller/redirect',
     'PhabricatorRefreshCSRFController' => 'applications/auth/controller/refresh',
     'PhabricatorRemarkupRuleDifferential' => 'infrastructure/markup/remarkup/markuprule/differential',
     'PhabricatorRemarkupRuleDifferentialHandle' => 'infrastructure/markup/remarkup/markuprule/handle/differential',
     'PhabricatorRemarkupRuleDiffusion' => 'infrastructure/markup/remarkup/markuprule/diffusion',
     'PhabricatorRemarkupRuleEmbedFile' => 'infrastructure/markup/remarkup/markuprule/embedobject',
     'PhabricatorRemarkupRuleImageMacro' => 'infrastructure/markup/remarkup/markuprule/imagemacro',
     'PhabricatorRemarkupRuleManiphest' => 'infrastructure/markup/remarkup/markuprule/maniphest',
     'PhabricatorRemarkupRuleManiphestHandle' => 'infrastructure/markup/remarkup/markuprule/handle/maniphest',
     'PhabricatorRemarkupRuleMention' => 'infrastructure/markup/remarkup/markuprule/mention',
     'PhabricatorRemarkupRuleObjectHandle' => 'infrastructure/markup/remarkup/markuprule/handle',
     'PhabricatorRemarkupRuleObjectName' => 'infrastructure/markup/remarkup/markuprule/objectname',
     'PhabricatorRemarkupRulePaste' => 'infrastructure/markup/remarkup/markuprule/paste',
     'PhabricatorRemarkupRulePhriction' => 'infrastructure/markup/remarkup/markuprule/phriction',
     'PhabricatorRemarkupRuleProxyImage' => 'infrastructure/markup/remarkup/markuprule/proxyimage',
     'PhabricatorRemarkupRuleYoutube' => 'infrastructure/markup/remarkup/markuprule/youtube',
     'PhabricatorRepository' => 'applications/repository/storage/repository',
     'PhabricatorRepositoryArcanistProject' => 'applications/repository/storage/arcanistproject',
     'PhabricatorRepositoryArcanistProjectEditController' => 'applications/repository/controller/arcansistprojectedit',
     'PhabricatorRepositoryCommit' => 'applications/repository/storage/commit',
     'PhabricatorRepositoryCommitChangeParserWorker' => 'applications/repository/worker/commitchangeparser/base',
     'PhabricatorRepositoryCommitData' => 'applications/repository/storage/commitdata',
     'PhabricatorRepositoryCommitDiscoveryDaemon' => 'applications/repository/daemon/commitdiscovery/base',
     'PhabricatorRepositoryCommitHeraldWorker' => 'applications/repository/worker/herald',
     'PhabricatorRepositoryCommitMessageDetailParser' => 'applications/repository/parser/base',
     'PhabricatorRepositoryCommitMessageParserWorker' => 'applications/repository/worker/commitmessageparser/base',
     'PhabricatorRepositoryCommitParserWorker' => 'applications/repository/worker/base',
     'PhabricatorRepositoryCommitTaskDaemon' => 'applications/repository/daemon/committask',
     'PhabricatorRepositoryController' => 'applications/repository/controller/base',
     'PhabricatorRepositoryCreateController' => 'applications/repository/controller/create',
     'PhabricatorRepositoryDAO' => 'applications/repository/storage/base',
     'PhabricatorRepositoryDaemon' => 'applications/repository/daemon/base',
     'PhabricatorRepositoryDefaultCommitMessageDetailParser' => 'applications/repository/parser/default',
     'PhabricatorRepositoryDeleteController' => 'applications/repository/controller/delete',
     'PhabricatorRepositoryEditController' => 'applications/repository/controller/edit',
     'PhabricatorRepositoryGitCommitChangeParserWorker' => 'applications/repository/worker/commitchangeparser/git',
     'PhabricatorRepositoryGitCommitDiscoveryDaemon' => 'applications/repository/daemon/commitdiscovery/git',
     'PhabricatorRepositoryGitCommitMessageParserWorker' => 'applications/repository/worker/commitmessageparser/git',
     'PhabricatorRepositoryGitFetchDaemon' => 'applications/repository/daemon/gitfetch',
     'PhabricatorRepositoryGitHubNotification' => 'applications/repository/storage/githubnotification',
     'PhabricatorRepositoryGitHubPostReceiveController' => 'applications/repository/controller/github-post-receive',
     'PhabricatorRepositoryListController' => 'applications/repository/controller/list',
     'PhabricatorRepositoryShortcut' => 'applications/repository/storage/shortcut',
     'PhabricatorRepositorySvnCommitChangeParserWorker' => 'applications/repository/worker/commitchangeparser/svn',
     'PhabricatorRepositorySvnCommitDiscoveryDaemon' => 'applications/repository/daemon/commitdiscovery/svn',
     'PhabricatorRepositorySvnCommitMessageParserWorker' => 'applications/repository/worker/commitmessageparser/svn',
     'PhabricatorRepositoryType' => 'applications/repository/constants/repositorytype',
     'PhabricatorS3FileStorageEngine' => 'applications/files/engine/s3',
     'PhabricatorSQLPatchList' => 'infrastructure/setup/sql',
     'PhabricatorSearchAbstractDocument' => 'applications/search/index/abstractdocument',
     'PhabricatorSearchAttachController' => 'applications/search/controller/attach',
     'PhabricatorSearchBaseController' => 'applications/search/controller/base',
     'PhabricatorSearchCommitIndexer' => 'applications/search/index/indexer/repository',
     'PhabricatorSearchController' => 'applications/search/controller/search',
     'PhabricatorSearchDAO' => 'applications/search/storage/base',
     'PhabricatorSearchDifferentialIndexer' => 'applications/search/index/indexer/differential',
     'PhabricatorSearchDocument' => 'applications/search/storage/document/document',
     'PhabricatorSearchDocumentField' => 'applications/search/storage/document/field',
     'PhabricatorSearchDocumentIndexer' => 'applications/search/index/indexer/base',
     'PhabricatorSearchDocumentRelationship' => 'applications/search/storage/document/relationship',
     'PhabricatorSearchEngine' => 'applications/search/engine/base',
     'PhabricatorSearchEngineMySQL' => 'applications/search/engine/mysql',
     'PhabricatorSearchEngineSelector' => 'applications/search/selector/base',
     'PhabricatorSearchField' => 'applications/search/constants/field',
     'PhabricatorSearchIndexController' => 'applications/search/controller/index',
     'PhabricatorSearchManiphestIndexer' => 'applications/search/index/indexer/maniphest',
     'PhabricatorSearchPhrictionIndexer' => 'applications/search/index/indexer/phriction',
     'PhabricatorSearchQuery' => 'applications/search/storage/query',
     'PhabricatorSearchRelationship' => 'applications/search/constants/relationship',
     'PhabricatorSearchResultView' => 'applications/search/view/searchresult',
     'PhabricatorSearchSelectController' => 'applications/search/controller/select',
     'PhabricatorSearchUserIndexer' => 'applications/search/index/indexer/user',
     'PhabricatorSetup' => 'infrastructure/setup',
     'PhabricatorSlowvoteChoice' => 'applications/slowvote/storage/choice',
     'PhabricatorSlowvoteComment' => 'applications/slowvote/storage/comment',
     'PhabricatorSlowvoteController' => 'applications/slowvote/controller/base',
     'PhabricatorSlowvoteCreateController' => 'applications/slowvote/controller/create',
     'PhabricatorSlowvoteDAO' => 'applications/slowvote/storage/base',
     'PhabricatorSlowvoteListController' => 'applications/slowvote/controller/list',
     'PhabricatorSlowvoteOption' => 'applications/slowvote/storage/option',
     'PhabricatorSlowvotePoll' => 'applications/slowvote/storage/poll',
     'PhabricatorSlowvotePollController' => 'applications/slowvote/controller/poll',
     'PhabricatorStandardPageView' => 'view/page/standard',
     'PhabricatorStatusController' => 'applications/status/base',
     'PhabricatorSyntaxHighlighter' => 'applications/markup/syntax',
     'PhabricatorTaskmasterDaemon' => 'infrastructure/daemon/workers/taskmaster',
     'PhabricatorTestCase' => 'infrastructure/testing/testcase',
     'PhabricatorTimelineCursor' => 'infrastructure/daemon/timeline/storage/cursor',
     'PhabricatorTimelineDAO' => 'infrastructure/daemon/timeline/storage/base',
     'PhabricatorTimelineEvent' => 'infrastructure/daemon/timeline/storage/event',
     'PhabricatorTimelineEventData' => 'infrastructure/daemon/timeline/storage/eventdata',
     'PhabricatorTimelineIterator' => 'infrastructure/daemon/timeline/cursor/iterator',
     'PhabricatorTimer' => 'applications/countdown/storage/timer',
     'PhabricatorTransformedFile' => 'applications/files/storage/transformed',
     'PhabricatorTrivialTestCase' => 'infrastructure/testing/testcase/__tests__',
     'PhabricatorTypeaheadCommonDatasourceController' => 'applications/typeahead/controller/common',
     'PhabricatorTypeaheadDatasourceController' => 'applications/typeahead/controller/base',
     'PhabricatorUIExample' => 'applications/uiexample/examples/base',
     'PhabricatorUIExampleController' => 'applications/uiexample/controller/base',
     'PhabricatorUIExampleRenderController' => 'applications/uiexample/controller/render',
     'PhabricatorUIListFilterExample' => 'applications/uiexample/examples/listfilter',
     'PhabricatorUIPagerExample' => 'applications/uiexample/examples/pager',
     'PhabricatorUser' => 'applications/people/storage/user',
     'PhabricatorUserAccountSettingsPanelController' => 'applications/people/controller/settings/panels/account',
     'PhabricatorUserConduitSettingsPanelController' => 'applications/people/controller/settings/panels/conduit',
     'PhabricatorUserDAO' => 'applications/people/storage/base',
     'PhabricatorUserEmailSettingsPanelController' => 'applications/people/controller/settings/panels/email',
     'PhabricatorUserLog' => 'applications/people/storage/log',
     'PhabricatorUserOAuthInfo' => 'applications/people/storage/useroauthinfo',
     'PhabricatorUserOAuthSettingsPanelController' => 'applications/people/controller/settings/panels/oauth',
     'PhabricatorUserPreferenceSettingsPanelController' => 'applications/people/controller/settings/panels/preferences',
     'PhabricatorUserPreferences' => 'applications/people/storage/preferences',
     'PhabricatorUserProfile' => 'applications/people/storage/profile',
     'PhabricatorUserProfileSettingsPanelController' => 'applications/people/controller/settings/panels/profile',
     'PhabricatorUserSSHKey' => 'applications/people/storage/usersshkey',
     'PhabricatorUserSSHKeysSettingsPanelController' => 'applications/people/controller/settings/panels/sshkeys',
     'PhabricatorUserSettingsController' => 'applications/people/controller/settings',
     'PhabricatorUserSettingsPanelController' => 'applications/people/controller/settings/panels/base',
     'PhabricatorWorker' => 'infrastructure/daemon/workers/worker',
     'PhabricatorWorkerDAO' => 'infrastructure/daemon/workers/storage/base',
     'PhabricatorWorkerTask' => 'infrastructure/daemon/workers/storage/task',
     'PhabricatorWorkerTaskData' => 'infrastructure/daemon/workers/storage/taskdata',
     'PhabricatorWorkerTaskDetailController' => 'applications/daemon/controller/workertaskdetail',
     'PhabricatorXHPASTViewController' => 'applications/xhpastview/controller/base',
     'PhabricatorXHPASTViewDAO' => 'applications/xhpastview/storage/base',
     'PhabricatorXHPASTViewFrameController' => 'applications/xhpastview/controller/viewframe',
     'PhabricatorXHPASTViewFramesetController' => 'applications/xhpastview/controller/viewframeset',
     'PhabricatorXHPASTViewInputController' => 'applications/xhpastview/controller/viewinput',
     'PhabricatorXHPASTViewPanelController' => 'applications/xhpastview/controller/viewpanel',
     'PhabricatorXHPASTViewParseTree' => 'applications/xhpastview/storage/parsetree',
     'PhabricatorXHPASTViewRunController' => 'applications/xhpastview/controller/run',
     'PhabricatorXHPASTViewStreamController' => 'applications/xhpastview/controller/viewstream',
     'PhabricatorXHPASTViewTreeController' => 'applications/xhpastview/controller/viewtree',
     'PhabricatorXHProfController' => 'applications/xhprof/controller/base',
     'PhabricatorXHProfProfileController' => 'applications/xhprof/controller/profile',
     'PhabricatorXHProfProfileSymbolView' => 'applications/xhprof/view/symbol',
     'PhabricatorXHProfProfileTopLevelView' => 'applications/xhprof/view/toplevel',
     'PhrictionActionConstants' => 'applications/phriction/constants/action',
     'PhrictionConstants' => 'applications/phriction/constants/base',
     'PhrictionContent' => 'applications/phriction/storage/content',
     'PhrictionController' => 'applications/phriction/controller/base',
     'PhrictionDAO' => 'applications/phriction/storage/base',
     'PhrictionDiffController' => 'applications/phriction/controller/diff',
     'PhrictionDocument' => 'applications/phriction/storage/document',
     'PhrictionDocumentController' => 'applications/phriction/controller/document',
     'PhrictionDocumentPreviewController' => 'applications/phriction/controller/documentpreview',
     'PhrictionDocumentTestCase' => 'applications/phriction/storage/document/__tests__',
     'PhrictionEditController' => 'applications/phriction/controller/edit',
     'PhrictionHistoryController' => 'applications/phriction/controller/history',
     'PhrictionListController' => 'applications/phriction/controller/list',
   ),
   'function' =>
   array(
     '_qsprintf_check_scalar_type' => 'storage/qsprintf',
     '_qsprintf_check_type' => 'storage/qsprintf',
     'celerity_generate_unique_node_id' => 'infrastructure/celerity/api',
     'celerity_register_resource_map' => 'infrastructure/celerity/map',
     'javelin_render_tag' => 'infrastructure/javelin/markup',
     'phabricator_date' => 'view/utils',
     'phabricator_datetime' => 'view/utils',
     'phabricator_format_relative_time' => 'view/utils',
     'phabricator_format_timestamp' => 'view/utils',
     'phabricator_format_units_generic' => 'view/utils',
     'phabricator_render_form' => 'infrastructure/javelin/markup',
     'phabricator_time' => 'view/utils',
     'qsprintf' => 'storage/qsprintf',
     'queryfx' => 'storage/queryfx',
     'queryfx_all' => 'storage/queryfx',
     'queryfx_one' => 'storage/queryfx',
     'require_celerity_resource' => 'infrastructure/celerity/api',
     'vqsprintf' => 'storage/qsprintf',
     'vqueryfx' => 'storage/queryfx',
     'vqueryfx_all' => 'storage/queryfx',
     'xsprintf_query' => 'storage/qsprintf',
   ),
   'requires_class' =>
   array(
     'Aphront304Response' => 'AphrontResponse',
     'Aphront400Response' => 'AphrontResponse',
     'Aphront404Response' => 'AphrontResponse',
     'AphrontAjaxResponse' => 'AphrontResponse',
     'AphrontAttachedFileView' => 'AphrontView',
     'AphrontCSRFException' => 'AphrontException',
     'AphrontCalendarMonthView' => 'AphrontView',
     'AphrontContextBarView' => 'AphrontView',
     'AphrontCrumbsView' => 'AphrontView',
     'AphrontDefaultApplicationConfiguration' => 'AphrontApplicationConfiguration',
     'AphrontDefaultApplicationController' => 'AphrontController',
     'AphrontDialogResponse' => 'AphrontResponse',
     'AphrontDialogView' => 'AphrontView',
     'AphrontErrorView' => 'AphrontView',
     'AphrontFilePreviewView' => 'AphrontView',
     'AphrontFileResponse' => 'AphrontResponse',
     'AphrontFormCheckboxControl' => 'AphrontFormControl',
     'AphrontFormControl' => 'AphrontView',
     'AphrontFormDividerControl' => 'AphrontFormControl',
     'AphrontFormDragAndDropUploadControl' => 'AphrontFormControl',
     'AphrontFormFileControl' => 'AphrontFormControl',
     'AphrontFormLayoutView' => 'AphrontView',
     'AphrontFormMarkupControl' => 'AphrontFormControl',
     'AphrontFormPasswordControl' => 'AphrontFormControl',
     'AphrontFormRecaptchaControl' => 'AphrontFormControl',
     'AphrontFormSelectControl' => 'AphrontFormControl',
     'AphrontFormStaticControl' => 'AphrontFormControl',
     'AphrontFormSubmitControl' => 'AphrontFormControl',
     'AphrontFormTextAreaControl' => 'AphrontFormControl',
     'AphrontFormTextControl' => 'AphrontFormControl',
     'AphrontFormToggleButtonsControl' => 'AphrontFormControl',
     'AphrontFormTokenizerControl' => 'AphrontFormControl',
     'AphrontFormView' => 'AphrontView',
     'AphrontHeadsupActionListView' => 'AphrontView',
     'AphrontHeadsupActionView' => 'AphrontView',
     'AphrontIsolatedDatabaseConnection' => 'AphrontDatabaseConnection',
     'AphrontIsolatedDatabaseConnectionTestCase' => 'PhabricatorTestCase',
     'AphrontKeyboardShortcutsAvailableView' => 'AphrontView',
     'AphrontListFilterView' => 'AphrontView',
     'AphrontMySQLDatabaseConnection' => 'AphrontDatabaseConnection',
     'AphrontNullView' => 'AphrontView',
     'AphrontPageView' => 'AphrontView',
     'AphrontPagerView' => 'AphrontView',
     'AphrontPanelView' => 'AphrontView',
     'AphrontQueryAccessDeniedException' => 'AphrontQueryRecoverableException',
     'AphrontQueryConnectionException' => 'AphrontQueryException',
     'AphrontQueryConnectionLostException' => 'AphrontQueryRecoverableException',
     'AphrontQueryCountException' => 'AphrontQueryException',
     'AphrontQueryDuplicateKeyException' => 'AphrontQueryException',
     'AphrontQueryObjectMissingException' => 'AphrontQueryException',
     'AphrontQueryParameterException' => 'AphrontQueryException',
     'AphrontQueryRecoverableException' => 'AphrontQueryException',
     'AphrontRedirectException' => 'AphrontException',
     'AphrontRedirectResponse' => 'AphrontResponse',
     'AphrontReloadResponse' => 'AphrontRedirectResponse',
     'AphrontRequestFailureView' => 'AphrontView',
     'AphrontSideNavView' => 'AphrontView',
     'AphrontTableView' => 'AphrontView',
     'AphrontTokenizerTemplateView' => 'AphrontView',
     'AphrontTypeaheadTemplateView' => 'AphrontView',
     'AphrontWebpageResponse' => 'AphrontResponse',
     'CelerityResourceController' => 'AphrontController',
     'ConduitAPI_conduit_connect_Method' => 'ConduitAPIMethod',
     'ConduitAPI_conduit_getcertificate_Method' => 'ConduitAPIMethod',
     'ConduitAPI_conduit_ping_Method' => 'ConduitAPIMethod',
     'ConduitAPI_daemon_launched_Method' => 'ConduitAPIMethod',
     'ConduitAPI_daemon_log_Method' => 'ConduitAPIMethod',
     'ConduitAPI_differential_creatediff_Method' => 'ConduitAPIMethod',
     'ConduitAPI_differential_createrevision_Method' => 'ConduitAPIMethod',
     'ConduitAPI_differential_find_Method' => 'ConduitAPIMethod',
     'ConduitAPI_differential_getalldiffs_Method' => 'ConduitAPIMethod',
     'ConduitAPI_differential_getcommitmessage_Method' => 'ConduitAPIMethod',
     'ConduitAPI_differential_getcommitpaths_Method' => 'ConduitAPIMethod',
     'ConduitAPI_differential_getdiff_Method' => 'ConduitAPIMethod',
     'ConduitAPI_differential_getrevision_Method' => 'ConduitAPIMethod',
     'ConduitAPI_differential_getrevisionfeedback_Method' => 'ConduitAPIMethod',
     'ConduitAPI_differential_markcommitted_Method' => 'ConduitAPIMethod',
     'ConduitAPI_differential_parsecommitmessage_Method' => 'ConduitAPIMethod',
     'ConduitAPI_differential_setdiffproperty_Method' => 'ConduitAPIMethod',
     'ConduitAPI_differential_updaterevision_Method' => 'ConduitAPIMethod',
     'ConduitAPI_differential_updatetaskrevisionassoc_Method' => 'ConduitAPIMethod',
     'ConduitAPI_differential_updateunitresults_Method' => 'ConduitAPIMethod',
     'ConduitAPI_diffusion_getcommits_Method' => 'ConduitAPIMethod',
     'ConduitAPI_diffusion_getrecentcommitsbypath_Method' => 'ConduitAPIMethod',
     'ConduitAPI_feed_publish_Method' => 'ConduitAPIMethod',
     'ConduitAPI_file_download_Method' => 'ConduitAPIMethod',
     'ConduitAPI_file_info_Method' => 'ConduitAPIMethod',
     'ConduitAPI_file_upload_Method' => 'ConduitAPIMethod',
     'ConduitAPI_maniphest_info_Method' => 'ConduitAPIMethod',
     'ConduitAPI_paste_Method' => 'ConduitAPIMethod',
     'ConduitAPI_paste_create_Method' => 'ConduitAPI_paste_Method',
     'ConduitAPI_paste_info_Method' => 'ConduitAPI_paste_Method',
     'ConduitAPI_path_getowners_Method' => 'ConduitAPIMethod',
     'ConduitAPI_slowvote_info_Method' => 'ConduitAPIMethod',
     'ConduitAPI_user_find_Method' => 'ConduitAPIMethod',
     'ConduitAPI_user_whoami_Method' => 'ConduitAPIMethod',
     'DarkConsoleConfigPlugin' => 'DarkConsolePlugin',
     'DarkConsoleController' => 'PhabricatorController',
     'DarkConsoleErrorLogPlugin' => 'DarkConsolePlugin',
     'DarkConsoleRequestPlugin' => 'DarkConsolePlugin',
     'DarkConsoleServicesPlugin' => 'DarkConsolePlugin',
     'DarkConsoleXHProfPlugin' => 'DarkConsolePlugin',
     'DifferentialAddCommentView' => 'AphrontView',
     'DifferentialApplyPatchFieldSpecification' => 'DifferentialFieldSpecification',
     'DifferentialArcanistProjectFieldSpecification' => 'DifferentialFieldSpecification',
     'DifferentialAuthorFieldSpecification' => 'DifferentialFieldSpecification',
     'DifferentialAuxiliaryField' => 'DifferentialDAO',
     'DifferentialBlameRevisionFieldSpecification' => 'DifferentialFieldSpecification',
     'DifferentialCCWelcomeMail' => 'DifferentialReviewRequestMail',
+    'DifferentialCCsFieldSpecification' => 'DifferentialFieldSpecification',
     'DifferentialChangeset' => 'DifferentialDAO',
     'DifferentialChangesetDetailView' => 'AphrontView',
     'DifferentialChangesetListView' => 'AphrontView',
     'DifferentialChangesetViewController' => 'DifferentialController',
     'DifferentialComment' => 'DifferentialDAO',
     'DifferentialCommentMail' => 'DifferentialMail',
     'DifferentialCommentPreviewController' => 'DifferentialController',
     'DifferentialCommentSaveController' => 'DifferentialController',
     'DifferentialCommitsFieldSpecification' => 'DifferentialFieldSpecification',
     'DifferentialController' => 'PhabricatorController',
     'DifferentialDAO' => 'PhabricatorLiskDAO',
     'DifferentialDefaultFieldSelector' => 'DifferentialFieldSelector',
     'DifferentialDependenciesFieldSpecification' => 'DifferentialFieldSpecification',
     'DifferentialDiff' => 'DifferentialDAO',
     'DifferentialDiffContentMail' => 'DifferentialMail',
     'DifferentialDiffCreateController' => 'DifferentialController',
     'DifferentialDiffProperty' => 'DifferentialDAO',
     'DifferentialDiffTableOfContentsView' => 'AphrontView',
     'DifferentialDiffViewController' => 'DifferentialController',
     'DifferentialExceptionMail' => 'DifferentialMail',
     'DifferentialExportPatchFieldSpecification' => 'DifferentialFieldSpecification',
     'DifferentialHostFieldSpecification' => 'DifferentialFieldSpecification',
     'DifferentialHunk' => 'DifferentialDAO',
     'DifferentialInlineComment' => 'DifferentialDAO',
     'DifferentialInlineCommentEditController' => 'DifferentialController',
     'DifferentialInlineCommentPreviewController' => 'DifferentialController',
     'DifferentialInlineCommentView' => 'AphrontView',
     'DifferentialLinesFieldSpecification' => 'DifferentialFieldSpecification',
+    'DifferentialLintFieldSpecification' => 'DifferentialFieldSpecification',
     'DifferentialManiphestTasksFieldSpecification' => 'DifferentialFieldSpecification',
     'DifferentialNewDiffMail' => 'DifferentialReviewRequestMail',
     'DifferentialPathFieldSpecification' => 'DifferentialFieldSpecification',
     'DifferentialPrimaryPaneView' => 'AphrontView',
     'DifferentialReplyHandler' => 'PhabricatorMailReplyHandler',
     'DifferentialRevertPlanFieldSpecification' => 'DifferentialFieldSpecification',
     'DifferentialReviewRequestMail' => 'DifferentialMail',
+    'DifferentialReviewersFieldSpecification' => 'DifferentialFieldSpecification',
     'DifferentialRevision' => 'DifferentialDAO',
     'DifferentialRevisionCommentListView' => 'AphrontView',
     'DifferentialRevisionCommentView' => 'AphrontView',
     'DifferentialRevisionDetailView' => 'AphrontView',
     'DifferentialRevisionEditController' => 'DifferentialController',
     'DifferentialRevisionListController' => 'DifferentialController',
+    'DifferentialRevisionStatusFieldSpecification' => 'DifferentialFieldSpecification',
     'DifferentialRevisionUpdateHistoryView' => 'AphrontView',
     'DifferentialRevisionViewController' => 'DifferentialController',
     'DifferentialSubscribeController' => 'DifferentialController',
+    'DifferentialUnitFieldSpecification' => 'DifferentialFieldSpecification',
     'DifferentialViewTime' => 'DifferentialDAO',
     'DiffusionBranchTableView' => 'DiffusionView',
     'DiffusionBrowseController' => 'DiffusionController',
     'DiffusionBrowseFileController' => 'DiffusionController',
     'DiffusionBrowseTableView' => 'DiffusionView',
     'DiffusionChangeController' => 'DiffusionController',
     'DiffusionCommitChangeTableView' => 'DiffusionView',
     'DiffusionCommitController' => 'DiffusionController',
     'DiffusionCommitListController' => 'DiffusionController',
     'DiffusionController' => 'PhabricatorController',
     'DiffusionDiffController' => 'DiffusionController',
     'DiffusionGitBranchQuery' => 'DiffusionBranchQuery',
     'DiffusionGitBrowseQuery' => 'DiffusionBrowseQuery',
     'DiffusionGitDiffQuery' => 'DiffusionDiffQuery',
     'DiffusionGitFileContentQuery' => 'DiffusionFileContentQuery',
     'DiffusionGitHistoryQuery' => 'DiffusionHistoryQuery',
     'DiffusionGitLastModifiedQuery' => 'DiffusionLastModifiedQuery',
     'DiffusionGitRequest' => 'DiffusionRequest',
     'DiffusionHistoryController' => 'DiffusionController',
     'DiffusionHistoryTableView' => 'DiffusionView',
     'DiffusionHomeController' => 'DiffusionController',
     'DiffusionLastModifiedController' => 'DiffusionController',
     'DiffusionPathCompleteController' => 'DiffusionController',
     'DiffusionPathValidateController' => 'DiffusionController',
     'DiffusionRepositoryController' => 'DiffusionController',
     'DiffusionSvnBrowseQuery' => 'DiffusionBrowseQuery',
     'DiffusionSvnDiffQuery' => 'DiffusionDiffQuery',
     'DiffusionSvnFileContentQuery' => 'DiffusionFileContentQuery',
     'DiffusionSvnHistoryQuery' => 'DiffusionHistoryQuery',
     'DiffusionSvnLastModifiedQuery' => 'DiffusionLastModifiedQuery',
     'DiffusionSvnRequest' => 'DiffusionRequest',
     'DiffusionView' => 'AphrontView',
     'HeraldAction' => 'HeraldDAO',
     'HeraldApplyTranscript' => 'HeraldDAO',
     'HeraldCommitAdapter' => 'HeraldObjectAdapter',
     'HeraldCondition' => 'HeraldDAO',
     'HeraldController' => 'PhabricatorController',
     'HeraldDAO' => 'PhabricatorLiskDAO',
     'HeraldDeleteController' => 'HeraldController',
     'HeraldDifferentialRevisionAdapter' => 'HeraldObjectAdapter',
     'HeraldDryRunAdapter' => 'HeraldObjectAdapter',
     'HeraldHomeController' => 'HeraldController',
     'HeraldNewController' => 'HeraldController',
     'HeraldRule' => 'HeraldDAO',
     'HeraldRuleController' => 'HeraldController',
     'HeraldTestConsoleController' => 'HeraldController',
     'HeraldTranscript' => 'HeraldDAO',
     'HeraldTranscriptController' => 'HeraldController',
     'HeraldTranscriptListController' => 'HeraldController',
     'LiskIsolationTestCase' => 'PhabricatorTestCase',
     'LiskIsolationTestDAO' => 'LiskDAO',
     'ManiphestAuxiliaryFieldDefaultSpecification' => 'ManiphestAuxiliaryFieldSpecification',
     'ManiphestController' => 'PhabricatorController',
     'ManiphestDAO' => 'PhabricatorLiskDAO',
     'ManiphestDefaultTaskExtensions' => 'ManiphestTaskExtensions',
     'ManiphestReplyHandler' => 'PhabricatorMailReplyHandler',
     'ManiphestTask' => 'ManiphestDAO',
     'ManiphestTaskAuxiliaryStorage' => 'ManiphestDAO',
     'ManiphestTaskDescriptionChangeController' => 'ManiphestController',
     'ManiphestTaskDetailController' => 'ManiphestController',
     'ManiphestTaskEditController' => 'ManiphestController',
     'ManiphestTaskListController' => 'ManiphestController',
     'ManiphestTaskListView' => 'ManiphestView',
     'ManiphestTaskOwner' => 'ManiphestConstants',
     'ManiphestTaskPriority' => 'ManiphestConstants',
     'ManiphestTaskProject' => 'ManiphestDAO',
     'ManiphestTaskStatus' => 'ManiphestConstants',
     'ManiphestTaskSubscriber' => 'ManiphestDAO',
     'ManiphestTaskSummaryView' => 'ManiphestView',
     'ManiphestTransaction' => 'ManiphestDAO',
     'ManiphestTransactionDetailView' => 'ManiphestView',
     'ManiphestTransactionListView' => 'ManiphestView',
     'ManiphestTransactionPreviewController' => 'ManiphestController',
     'ManiphestTransactionSaveController' => 'ManiphestController',
     'ManiphestTransactionType' => 'ManiphestConstants',
     'ManiphestView' => 'AphrontView',
     'Phabricator404Controller' => 'PhabricatorController',
     'PhabricatorAuthController' => 'PhabricatorController',
     'PhabricatorCalendarBrowseController' => 'PhabricatorCalendarController',
     'PhabricatorCalendarController' => 'PhabricatorController',
     'PhabricatorConduitAPIController' => 'PhabricatorConduitController',
     'PhabricatorConduitCertificateToken' => 'PhabricatorConduitDAO',
     'PhabricatorConduitConnectionLog' => 'PhabricatorConduitDAO',
     'PhabricatorConduitConsoleController' => 'PhabricatorConduitController',
     'PhabricatorConduitController' => 'PhabricatorController',
     'PhabricatorConduitDAO' => 'PhabricatorLiskDAO',
     'PhabricatorConduitLogController' => 'PhabricatorConduitController',
     'PhabricatorConduitMethodCallLog' => 'PhabricatorConduitDAO',
     'PhabricatorConduitTokenController' => 'PhabricatorConduitController',
     'PhabricatorController' => 'AphrontController',
     'PhabricatorCountdownController' => 'PhabricatorController',
     'PhabricatorCountdownDAO' => 'PhabricatorLiskDAO',
     'PhabricatorCountdownDeleteController' => 'PhabricatorCountdownController',
     'PhabricatorCountdownEditController' => 'PhabricatorCountdownController',
     'PhabricatorCountdownListController' => 'PhabricatorCountdownController',
     'PhabricatorCountdownViewController' => 'PhabricatorCountdownController',
     'PhabricatorDaemon' => 'PhutilDaemon',
     'PhabricatorDaemonCombinedLogController' => 'PhabricatorDaemonController',
     'PhabricatorDaemonConsoleController' => 'PhabricatorDaemonController',
     'PhabricatorDaemonController' => 'PhabricatorController',
     'PhabricatorDaemonDAO' => 'PhabricatorLiskDAO',
     'PhabricatorDaemonLog' => 'PhabricatorDaemonDAO',
     'PhabricatorDaemonLogEvent' => 'PhabricatorDaemonDAO',
     'PhabricatorDaemonLogEventsView' => 'AphrontView',
     'PhabricatorDaemonLogListController' => 'PhabricatorDaemonController',
     'PhabricatorDaemonLogListView' => 'AphrontView',
     'PhabricatorDaemonLogViewController' => 'PhabricatorDaemonController',
     'PhabricatorDaemonTimelineConsoleController' => 'PhabricatorDaemonController',
     'PhabricatorDaemonTimelineEventController' => 'PhabricatorDaemonController',
     'PhabricatorDefaultFileStorageEngineSelector' => 'PhabricatorFileStorageEngineSelector',
     'PhabricatorDefaultSearchEngineSelector' => 'PhabricatorSearchEngineSelector',
     'PhabricatorDirectoryCategory' => 'PhabricatorDirectoryDAO',
     'PhabricatorDirectoryCategoryDeleteController' => 'PhabricatorDirectoryController',
     'PhabricatorDirectoryCategoryEditController' => 'PhabricatorDirectoryController',
     'PhabricatorDirectoryCategoryListController' => 'PhabricatorDirectoryController',
     'PhabricatorDirectoryController' => 'PhabricatorController',
     'PhabricatorDirectoryDAO' => 'PhabricatorLiskDAO',
     'PhabricatorDirectoryItem' => 'PhabricatorDirectoryDAO',
     'PhabricatorDirectoryItemDeleteController' => 'PhabricatorDirectoryController',
     'PhabricatorDirectoryItemEditController' => 'PhabricatorDirectoryController',
     'PhabricatorDirectoryItemListController' => 'PhabricatorDirectoryController',
     'PhabricatorDirectoryMainController' => 'PhabricatorDirectoryController',
     'PhabricatorDisabledUserController' => 'PhabricatorAuthController',
     'PhabricatorDraft' => 'PhabricatorDraftDAO',
     'PhabricatorDraftDAO' => 'PhabricatorLiskDAO',
     'PhabricatorEmailLoginController' => 'PhabricatorAuthController',
     'PhabricatorEmailTokenController' => 'PhabricatorAuthController',
     'PhabricatorFeedController' => 'PhabricatorController',
     'PhabricatorFeedDAO' => 'PhabricatorLiskDAO',
     'PhabricatorFeedPublicStreamController' => 'PhabricatorFeedController',
     'PhabricatorFeedStoryData' => 'PhabricatorFeedDAO',
     'PhabricatorFeedStoryDifferential' => 'PhabricatorFeedStory',
     'PhabricatorFeedStoryPhriction' => 'PhabricatorFeedStory',
     'PhabricatorFeedStoryReference' => 'PhabricatorFeedDAO',
     'PhabricatorFeedStoryStatus' => 'PhabricatorFeedStory',
     'PhabricatorFeedStoryTypeConstants' => 'PhabricatorFeedConstants',
     'PhabricatorFeedStoryUnknown' => 'PhabricatorFeedStory',
     'PhabricatorFeedStoryView' => 'PhabricatorFeedView',
     'PhabricatorFeedStreamController' => 'PhabricatorFeedController',
     'PhabricatorFeedView' => 'AphrontView',
     'PhabricatorFile' => 'PhabricatorFileDAO',
     'PhabricatorFileController' => 'PhabricatorController',
     'PhabricatorFileDAO' => 'PhabricatorLiskDAO',
     'PhabricatorFileDropUploadController' => 'PhabricatorFileController',
     'PhabricatorFileImageMacro' => 'PhabricatorFileDAO',
     'PhabricatorFileListController' => 'PhabricatorFileController',
     'PhabricatorFileMacroDeleteController' => 'PhabricatorFileController',
     'PhabricatorFileMacroEditController' => 'PhabricatorFileController',
     'PhabricatorFileMacroListController' => 'PhabricatorFileController',
     'PhabricatorFileProxyController' => 'PhabricatorFileController',
     'PhabricatorFileProxyImage' => 'PhabricatorFileDAO',
     'PhabricatorFileStorageBlob' => 'PhabricatorFileDAO',
     'PhabricatorFileTransformController' => 'PhabricatorFileController',
     'PhabricatorFileUploadController' => 'PhabricatorFileController',
     'PhabricatorFileViewController' => 'PhabricatorFileController',
     'PhabricatorGarbageCollectorDaemon' => 'PhabricatorDaemon',
     'PhabricatorGoodForNothingWorker' => 'PhabricatorWorker',
     'PhabricatorHelpController' => 'PhabricatorController',
     'PhabricatorHelpKeyboardShortcutController' => 'PhabricatorHelpController',
     'PhabricatorIRCBot' => 'PhabricatorDaemon',
     'PhabricatorIRCObjectNameHandler' => 'PhabricatorIRCHandler',
     'PhabricatorIRCProtocolHandler' => 'PhabricatorIRCHandler',
     'PhabricatorJavelinLinter' => 'ArcanistLinter',
     'PhabricatorLintEngine' => 'PhutilLintEngine',
     'PhabricatorLiskDAO' => 'LiskDAO',
     'PhabricatorLocalDiskFileStorageEngine' => 'PhabricatorFileStorageEngine',
     'PhabricatorLoginController' => 'PhabricatorAuthController',
     'PhabricatorLogoutController' => 'PhabricatorAuthController',
     'PhabricatorMailImplementationAmazonSESAdapter' => 'PhabricatorMailImplementationPHPMailerLiteAdapter',
     'PhabricatorMailImplementationPHPMailerLiteAdapter' => 'PhabricatorMailImplementationAdapter',
     'PhabricatorMailImplementationSendGridAdapter' => 'PhabricatorMailImplementationAdapter',
     'PhabricatorMailImplementationTestAdapter' => 'PhabricatorMailImplementationAdapter',
     'PhabricatorMetaMTAController' => 'PhabricatorController',
     'PhabricatorMetaMTADAO' => 'PhabricatorLiskDAO',
     'PhabricatorMetaMTADaemon' => 'PhabricatorDaemon',
     'PhabricatorMetaMTAEmailBodyParserTestCase' => 'PhabricatorTestCase',
     'PhabricatorMetaMTAListController' => 'PhabricatorMetaMTAController',
     'PhabricatorMetaMTAMail' => 'PhabricatorMetaMTADAO',
     'PhabricatorMetaMTAMailTestCase' => 'PhabricatorTestCase',
     'PhabricatorMetaMTAMailingList' => 'PhabricatorMetaMTADAO',
     'PhabricatorMetaMTAMailingListEditController' => 'PhabricatorMetaMTAController',
     'PhabricatorMetaMTAMailingListsController' => 'PhabricatorMetaMTAController',
     'PhabricatorMetaMTAReceiveController' => 'PhabricatorMetaMTAController',
     'PhabricatorMetaMTAReceivedListController' => 'PhabricatorMetaMTAController',
     'PhabricatorMetaMTAReceivedMail' => 'PhabricatorMetaMTADAO',
     'PhabricatorMetaMTASendController' => 'PhabricatorMetaMTAController',
     'PhabricatorMetaMTASendGridReceiveController' => 'PhabricatorMetaMTAController',
     'PhabricatorMetaMTAViewController' => 'PhabricatorMetaMTAController',
     'PhabricatorMySQLFileStorageEngine' => 'PhabricatorFileStorageEngine',
     'PhabricatorOAuthDefaultRegistrationController' => 'PhabricatorOAuthRegistrationController',
     'PhabricatorOAuthDiagnosticsController' => 'PhabricatorAuthController',
     'PhabricatorOAuthFailureView' => 'AphrontView',
     'PhabricatorOAuthLoginController' => 'PhabricatorAuthController',
     'PhabricatorOAuthProviderFacebook' => 'PhabricatorOAuthProvider',
     'PhabricatorOAuthProviderGithub' => 'PhabricatorOAuthProvider',
     'PhabricatorOAuthRegistrationController' => 'PhabricatorAuthController',
     'PhabricatorOAuthUnlinkController' => 'PhabricatorAuthController',
     'PhabricatorObjectGraph' => 'AbstractDirectedGraph',
     'PhabricatorObjectHandleStatus' => 'PhabricatorObjectHandleConstants',
     'PhabricatorOwnersController' => 'PhabricatorController',
     'PhabricatorOwnersDAO' => 'PhabricatorLiskDAO',
     'PhabricatorOwnersDeleteController' => 'PhabricatorOwnersController',
     'PhabricatorOwnersDetailController' => 'PhabricatorOwnersController',
     'PhabricatorOwnersEditController' => 'PhabricatorOwnersController',
     'PhabricatorOwnersListController' => 'PhabricatorOwnersController',
     'PhabricatorOwnersOwner' => 'PhabricatorOwnersDAO',
     'PhabricatorOwnersPackage' => 'PhabricatorOwnersDAO',
     'PhabricatorOwnersPath' => 'PhabricatorOwnersDAO',
     'PhabricatorPHID' => 'PhabricatorPHIDDAO',
     'PhabricatorPHIDController' => 'PhabricatorController',
     'PhabricatorPHIDDAO' => 'PhabricatorLiskDAO',
     'PhabricatorPHIDListController' => 'PhabricatorPHIDController',
     'PhabricatorPHIDLookupController' => 'PhabricatorPHIDController',
     'PhabricatorPaste' => 'PhabricatorPasteDAO',
     'PhabricatorPasteController' => 'PhabricatorController',
     'PhabricatorPasteCreateController' => 'PhabricatorPasteController',
     'PhabricatorPasteDAO' => 'PhabricatorLiskDAO',
     'PhabricatorPasteListController' => 'PhabricatorPasteController',
     'PhabricatorPasteViewController' => 'PhabricatorPasteController',
     'PhabricatorPeopleController' => 'PhabricatorController',
     'PhabricatorPeopleEditController' => 'PhabricatorPeopleController',
     'PhabricatorPeopleListController' => 'PhabricatorPeopleController',
     'PhabricatorPeopleLogsController' => 'PhabricatorPeopleController',
     'PhabricatorPeopleProfileController' => 'PhabricatorPeopleController',
     'PhabricatorProfileView' => 'AphrontView',
     'PhabricatorProject' => 'PhabricatorProjectDAO',
     'PhabricatorProjectAffiliation' => 'PhabricatorProjectDAO',
     'PhabricatorProjectAffiliationEditController' => 'PhabricatorProjectController',
     'PhabricatorProjectController' => 'PhabricatorController',
     'PhabricatorProjectCreateController' => 'PhabricatorProjectController',
     'PhabricatorProjectDAO' => 'PhabricatorLiskDAO',
     'PhabricatorProjectListController' => 'PhabricatorProjectController',
     'PhabricatorProjectProfile' => 'PhabricatorProjectDAO',
     'PhabricatorProjectProfileController' => 'PhabricatorProjectController',
     'PhabricatorProjectProfileEditController' => 'PhabricatorProjectController',
     'PhabricatorProjectSubproject' => 'PhabricatorProjectDAO',
     'PhabricatorRedirectController' => 'PhabricatorController',
     'PhabricatorRefreshCSRFController' => 'PhabricatorAuthController',
     'PhabricatorRemarkupRuleDifferential' => 'PhabricatorRemarkupRuleObjectName',
     'PhabricatorRemarkupRuleDifferentialHandle' => 'PhabricatorRemarkupRuleObjectHandle',
     'PhabricatorRemarkupRuleDiffusion' => 'PhutilRemarkupRule',
     'PhabricatorRemarkupRuleEmbedFile' => 'PhutilRemarkupRule',
     'PhabricatorRemarkupRuleImageMacro' => 'PhutilRemarkupRule',
     'PhabricatorRemarkupRuleManiphest' => 'PhabricatorRemarkupRuleObjectName',
     'PhabricatorRemarkupRuleManiphestHandle' => 'PhabricatorRemarkupRuleObjectHandle',
     'PhabricatorRemarkupRuleMention' => 'PhutilRemarkupRule',
     'PhabricatorRemarkupRuleObjectHandle' => 'PhutilRemarkupRule',
     'PhabricatorRemarkupRuleObjectName' => 'PhutilRemarkupRule',
     'PhabricatorRemarkupRulePaste' => 'PhabricatorRemarkupRuleObjectName',
     'PhabricatorRemarkupRulePhriction' => 'PhutilRemarkupRule',
     'PhabricatorRemarkupRuleProxyImage' => 'PhutilRemarkupRule',
     'PhabricatorRemarkupRuleYoutube' => 'PhutilRemarkupRule',
     'PhabricatorRepository' => 'PhabricatorRepositoryDAO',
     'PhabricatorRepositoryArcanistProject' => 'PhabricatorRepositoryDAO',
     'PhabricatorRepositoryArcanistProjectEditController' => 'PhabricatorRepositoryController',
     'PhabricatorRepositoryCommit' => 'PhabricatorRepositoryDAO',
     'PhabricatorRepositoryCommitChangeParserWorker' => 'PhabricatorRepositoryCommitParserWorker',
     'PhabricatorRepositoryCommitData' => 'PhabricatorRepositoryDAO',
     'PhabricatorRepositoryCommitDiscoveryDaemon' => 'PhabricatorRepositoryDaemon',
     'PhabricatorRepositoryCommitHeraldWorker' => 'PhabricatorRepositoryCommitParserWorker',
     'PhabricatorRepositoryCommitMessageParserWorker' => 'PhabricatorRepositoryCommitParserWorker',
     'PhabricatorRepositoryCommitParserWorker' => 'PhabricatorWorker',
     'PhabricatorRepositoryCommitTaskDaemon' => 'PhabricatorRepositoryDaemon',
     'PhabricatorRepositoryController' => 'PhabricatorController',
     'PhabricatorRepositoryCreateController' => 'PhabricatorRepositoryController',
     'PhabricatorRepositoryDAO' => 'PhabricatorLiskDAO',
     'PhabricatorRepositoryDaemon' => 'PhabricatorDaemon',
     'PhabricatorRepositoryDefaultCommitMessageDetailParser' => 'PhabricatorRepositoryCommitMessageDetailParser',
     'PhabricatorRepositoryDeleteController' => 'PhabricatorRepositoryController',
     'PhabricatorRepositoryEditController' => 'PhabricatorRepositoryController',
     'PhabricatorRepositoryGitCommitChangeParserWorker' => 'PhabricatorRepositoryCommitChangeParserWorker',
     'PhabricatorRepositoryGitCommitDiscoveryDaemon' => 'PhabricatorRepositoryCommitDiscoveryDaemon',
     'PhabricatorRepositoryGitCommitMessageParserWorker' => 'PhabricatorRepositoryCommitMessageParserWorker',
     'PhabricatorRepositoryGitFetchDaemon' => 'PhabricatorRepositoryDaemon',
     'PhabricatorRepositoryGitHubNotification' => 'PhabricatorRepositoryDAO',
     'PhabricatorRepositoryGitHubPostReceiveController' => 'PhabricatorRepositoryController',
     'PhabricatorRepositoryListController' => 'PhabricatorRepositoryController',
     'PhabricatorRepositoryShortcut' => 'PhabricatorRepositoryDAO',
     'PhabricatorRepositorySvnCommitChangeParserWorker' => 'PhabricatorRepositoryCommitChangeParserWorker',
     'PhabricatorRepositorySvnCommitDiscoveryDaemon' => 'PhabricatorRepositoryCommitDiscoveryDaemon',
     'PhabricatorRepositorySvnCommitMessageParserWorker' => 'PhabricatorRepositoryCommitMessageParserWorker',
     'PhabricatorS3FileStorageEngine' => 'PhabricatorFileStorageEngine',
     'PhabricatorSearchAttachController' => 'PhabricatorSearchController',
     'PhabricatorSearchBaseController' => 'PhabricatorController',
     'PhabricatorSearchCommitIndexer' => 'PhabricatorSearchDocumentIndexer',
     'PhabricatorSearchController' => 'PhabricatorSearchBaseController',
     'PhabricatorSearchDAO' => 'PhabricatorLiskDAO',
     'PhabricatorSearchDifferentialIndexer' => 'PhabricatorSearchDocumentIndexer',
     'PhabricatorSearchDocument' => 'PhabricatorSearchDAO',
     'PhabricatorSearchDocumentField' => 'PhabricatorSearchDAO',
     'PhabricatorSearchDocumentRelationship' => 'PhabricatorSearchDAO',
     'PhabricatorSearchEngineMySQL' => 'PhabricatorSearchEngine',
     'PhabricatorSearchIndexController' => 'PhabricatorSearchBaseController',
     'PhabricatorSearchManiphestIndexer' => 'PhabricatorSearchDocumentIndexer',
     'PhabricatorSearchPhrictionIndexer' => 'PhabricatorSearchDocumentIndexer',
     'PhabricatorSearchQuery' => 'PhabricatorSearchDAO',
     'PhabricatorSearchResultView' => 'AphrontView',
     'PhabricatorSearchSelectController' => 'PhabricatorSearchController',
     'PhabricatorSearchUserIndexer' => 'PhabricatorSearchDocumentIndexer',
     'PhabricatorSlowvoteChoice' => 'PhabricatorSlowvoteDAO',
     'PhabricatorSlowvoteComment' => 'PhabricatorSlowvoteDAO',
     'PhabricatorSlowvoteController' => 'PhabricatorController',
     'PhabricatorSlowvoteCreateController' => 'PhabricatorSlowvoteController',
     'PhabricatorSlowvoteDAO' => 'PhabricatorLiskDAO',
     'PhabricatorSlowvoteListController' => 'PhabricatorSlowvoteController',
     'PhabricatorSlowvoteOption' => 'PhabricatorSlowvoteDAO',
     'PhabricatorSlowvotePoll' => 'PhabricatorSlowvoteDAO',
     'PhabricatorSlowvotePollController' => 'PhabricatorSlowvoteController',
     'PhabricatorStandardPageView' => 'AphrontPageView',
     'PhabricatorStatusController' => 'PhabricatorController',
     'PhabricatorTaskmasterDaemon' => 'PhabricatorDaemon',
     'PhabricatorTestCase' => 'ArcanistPhutilTestCase',
     'PhabricatorTimelineCursor' => 'PhabricatorTimelineDAO',
     'PhabricatorTimelineDAO' => 'PhabricatorLiskDAO',
     'PhabricatorTimelineEvent' => 'PhabricatorTimelineDAO',
     'PhabricatorTimelineEventData' => 'PhabricatorTimelineDAO',
     'PhabricatorTimer' => 'PhabricatorCountdownDAO',
     'PhabricatorTransformedFile' => 'PhabricatorFileDAO',
     'PhabricatorTrivialTestCase' => 'PhabricatorTestCase',
     'PhabricatorTypeaheadCommonDatasourceController' => 'PhabricatorTypeaheadDatasourceController',
     'PhabricatorTypeaheadDatasourceController' => 'PhabricatorController',
     'PhabricatorUIExampleController' => 'PhabricatorController',
     'PhabricatorUIExampleRenderController' => 'PhabricatorUIExampleController',
     'PhabricatorUIListFilterExample' => 'PhabricatorUIExample',
     'PhabricatorUIPagerExample' => 'PhabricatorUIExample',
     'PhabricatorUser' => 'PhabricatorUserDAO',
     'PhabricatorUserAccountSettingsPanelController' => 'PhabricatorUserSettingsPanelController',
     'PhabricatorUserConduitSettingsPanelController' => 'PhabricatorUserSettingsPanelController',
     'PhabricatorUserDAO' => 'PhabricatorLiskDAO',
     'PhabricatorUserEmailSettingsPanelController' => 'PhabricatorUserSettingsPanelController',
     'PhabricatorUserLog' => 'PhabricatorUserDAO',
     'PhabricatorUserOAuthInfo' => 'PhabricatorUserDAO',
     'PhabricatorUserOAuthSettingsPanelController' => 'PhabricatorUserSettingsPanelController',
     'PhabricatorUserPreferenceSettingsPanelController' => 'PhabricatorUserSettingsPanelController',
     'PhabricatorUserPreferences' => 'PhabricatorUserDAO',
     'PhabricatorUserProfile' => 'PhabricatorUserDAO',
     'PhabricatorUserProfileSettingsPanelController' => 'PhabricatorUserSettingsPanelController',
     'PhabricatorUserSSHKey' => 'PhabricatorUserDAO',
     'PhabricatorUserSSHKeysSettingsPanelController' => 'PhabricatorUserSettingsPanelController',
     'PhabricatorUserSettingsController' => 'PhabricatorPeopleController',
     'PhabricatorUserSettingsPanelController' => 'PhabricatorPeopleController',
     'PhabricatorWorkerDAO' => 'PhabricatorLiskDAO',
     'PhabricatorWorkerTask' => 'PhabricatorWorkerDAO',
     'PhabricatorWorkerTaskData' => 'PhabricatorWorkerDAO',
     'PhabricatorWorkerTaskDetailController' => 'PhabricatorDaemonController',
     'PhabricatorXHPASTViewController' => 'PhabricatorController',
     'PhabricatorXHPASTViewDAO' => 'PhabricatorLiskDAO',
     'PhabricatorXHPASTViewFrameController' => 'PhabricatorXHPASTViewController',
     'PhabricatorXHPASTViewFramesetController' => 'PhabricatorXHPASTViewController',
     'PhabricatorXHPASTViewInputController' => 'PhabricatorXHPASTViewPanelController',
     'PhabricatorXHPASTViewPanelController' => 'PhabricatorXHPASTViewController',
     'PhabricatorXHPASTViewParseTree' => 'PhabricatorXHPASTViewDAO',
     'PhabricatorXHPASTViewRunController' => 'PhabricatorXHPASTViewController',
     'PhabricatorXHPASTViewStreamController' => 'PhabricatorXHPASTViewPanelController',
     'PhabricatorXHPASTViewTreeController' => 'PhabricatorXHPASTViewPanelController',
     'PhabricatorXHProfController' => 'PhabricatorController',
     'PhabricatorXHProfProfileController' => 'PhabricatorXHProfController',
     'PhabricatorXHProfProfileSymbolView' => 'AphrontView',
     'PhabricatorXHProfProfileTopLevelView' => 'AphrontView',
     'PhrictionActionConstants' => 'PhrictionConstants',
     'PhrictionContent' => 'PhrictionDAO',
     'PhrictionController' => 'PhabricatorController',
     'PhrictionDAO' => 'PhabricatorLiskDAO',
     'PhrictionDiffController' => 'PhrictionController',
     'PhrictionDocument' => 'PhrictionDAO',
     'PhrictionDocumentController' => 'PhrictionController',
     'PhrictionDocumentPreviewController' => 'PhrictionController',
     'PhrictionDocumentTestCase' => 'PhabricatorTestCase',
     'PhrictionEditController' => 'PhrictionController',
     'PhrictionHistoryController' => 'PhrictionController',
     'PhrictionListController' => 'PhrictionController',
   ),
   'requires_interface' =>
   array(
   ),
 ));
diff --git a/src/applications/differential/controller/revisionview/DifferentialRevisionViewController.php b/src/applications/differential/controller/revisionview/DifferentialRevisionViewController.php
index accf29754a..a9dc09b4a7 100644
--- a/src/applications/differential/controller/revisionview/DifferentialRevisionViewController.php
+++ b/src/applications/differential/controller/revisionview/DifferentialRevisionViewController.php
@@ -1,691 +1,562 @@
 <?php
 
 /*
  * Copyright 2011 Facebook, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
  * You may obtain a copy of the License at
  *
  *   http://www.apache.org/licenses/LICENSE-2.0
  *
  * Unless required by applicable law or agreed to in writing, software
  * distributed under the License is distributed on an "AS IS" BASIS,
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
 
 class DifferentialRevisionViewController extends DifferentialController {
 
   private $revisionID;
 
   public function willProcessRequest(array $data) {
     $this->revisionID = $data['id'];
   }
 
   public function processRequest() {
 
     $request = $this->getRequest();
     $user = $request->getUser();
 
     $revision = id(new DifferentialRevision())->load($this->revisionID);
     if (!$revision) {
       return new Aphront404Response();
     }
 
     $revision->loadRelationships();
 
     $diffs = $revision->loadDiffs();
 
     if (!$diffs) {
       throw new Exception(
         "This revision has no diffs. Something has gone quite wrong.");
     }
 
     $diff_vs = $request->getInt('vs');
 
     $target = end($diffs);
     $target_id = $request->getInt('id');
     if ($target_id) {
       if (isset($diffs[$target_id])) {
         $target = $diffs[$target_id];
       }
     }
 
     $diffs = mpull($diffs, null, 'getID');
     if (empty($diffs[$diff_vs])) {
       $diff_vs = null;
     }
 
-    $aux_fields = $this->loadAuxiliaryFields($revision);
-    foreach ($aux_fields as $aux_field) {
-      $aux_field->setDiff($target);
-    }
+    $aux_fields = $this->loadAuxiliaryFields($revision, $target);
 
     list($changesets, $vs_map, $rendering_references) =
       $this->loadChangesetsAndVsMap($diffs, $diff_vs, $target);
 
     $comments = $revision->loadComments();
     $comments = array_merge(
       $this->getImplicitComments($revision),
       $comments);
 
     $all_changesets = $changesets;
     $inlines = $this->loadInlineComments($comments, $all_changesets);
 
     $object_phids = array_merge(
       $revision->getReviewers(),
       $revision->getCCPHIDs(),
       $revision->loadCommitPHIDs(),
       array(
         $revision->getAuthorPHID(),
         $user->getPHID(),
       ),
       mpull($comments, 'getAuthorPHID'));
 
     foreach ($comments as $comment) {
       $metadata = $comment->getMetadata();
       $added_reviewers = idx(
         $metadata,
         DifferentialComment::METADATA_ADDED_REVIEWERS);
       if ($added_reviewers) {
         foreach ($added_reviewers as $phid) {
           $object_phids[] = $phid;
         }
       }
       $added_ccs = idx(
         $metadata,
         DifferentialComment::METADATA_ADDED_CCS);
       if ($added_ccs) {
         foreach ($added_ccs as $phid) {
           $object_phids[] = $phid;
         }
       }
     }
 
     foreach ($revision->getAttached() as $type => $phids) {
       foreach ($phids as $phid => $info) {
         $object_phids[] = $phid;
       }
     }
 
     $aux_phids = array();
     foreach ($aux_fields as $key => $aux_field) {
       $aux_phids[$key] = $aux_field->getRequiredHandlePHIDsForRevisionView();
     }
     $object_phids = array_merge($object_phids, array_mergev($aux_phids));
     $object_phids = array_unique($object_phids);
 
     $handles = id(new PhabricatorObjectHandleData($object_phids))
       ->loadHandles();
 
     foreach ($aux_fields as $key => $aux_field) {
       // Make sure each field only has access to handles it specifically
       // requested, not all handles. Otherwise you can get a field which works
       // only in the presence of other fields.
       $aux_field->setHandles(array_select_keys($handles, $aux_phids[$key]));
     }
 
     $request_uri = $request->getRequestURI();
 
     $limit = 100;
     $large = $request->getStr('large');
     if (count($changesets) > $limit && !$large) {
       $count = number_format(count($changesets));
       $warning = new AphrontErrorView();
       $warning->setTitle('Very Large Diff');
       $warning->setSeverity(AphrontErrorView::SEVERITY_WARNING);
       $warning->setWidth(AphrontErrorView::WIDTH_WIDE);
       $warning->appendChild(
         "<p>This diff is very large and affects {$count} files. Use ".
         "Table of Contents to open files in a standalone view. ".
         "<strong>".
           phutil_render_tag(
             'a',
             array(
               'href' => $request_uri->alter('large', 'true'),
             ),
             'Show All Files Inline').
         "</strong>");
       $warning = $warning->render();
 
       $visible_changesets = array();
     } else {
       $warning = null;
       $visible_changesets = $changesets;
     }
 
-    $diff_properties = id(new DifferentialDiffProperty())->loadAllWhere(
-      'diffID = %d AND name IN (%Ls)',
-      $target->getID(),
-      array(
-        'arc:lint',
-        'arc:unit',
-      ));
-    $diff_properties = mpull($diff_properties, 'getData', 'getName');
-
     $revision_detail = new DifferentialRevisionDetailView();
     $revision_detail->setRevision($revision);
     $revision_detail->setAuxiliaryFields($aux_fields);
 
+    $actions = $this->getRevisionActions($revision);
+
     $custom_renderer_class = PhabricatorEnv::getEnvConfig(
       'differential.revision-custom-detail-renderer');
     if ($custom_renderer_class) {
+
+      // TODO: Either deprecate generateProperties() or build a better version
+      // of the action links and deprecate the whole class. Custom fields
+      // now provide a much more powerful version of generateProperties().
+
       PhutilSymbolLoader::loadClass($custom_renderer_class);
       $custom_renderer =
         newv($custom_renderer_class, array());
-    } else {
-      $custom_renderer = null;
-    }
-
-    $properties = $this->getRevisionProperties(
-      $revision,
-      $target,
-      $handles,
-      $diff_properties);
-    if ($custom_renderer) {
-      $properties = array_merge(
-        $properties,
-        $custom_renderer->generateProperties($revision, $target));
-    }
-
-    $revision_detail->setProperties($properties);
+      $properties = $custom_renderer->generateProperties($revision, $target);
+      $revision_detail->setProperties($properties);
 
-    $actions = $this->getRevisionActions($revision);
-    if ($custom_renderer) {
       $actions = array_merge(
         $actions,
         $custom_renderer->generateActionLinks($revision, $target));
+    } else {
+      $revision_detail->setProperties(array());
     }
 
     $whitespace = $request->getStr(
       'whitespace',
       DifferentialChangesetParser::WHITESPACE_IGNORE_ALL
     );
 
     $revision_detail->setActions($actions);
 
     $revision_detail->setUser($user);
 
     $comment_view = new DifferentialRevisionCommentListView();
     $comment_view->setComments($comments);
     $comment_view->setHandles($handles);
     $comment_view->setInlineComments($inlines);
     $comment_view->setChangesets($all_changesets);
     $comment_view->setUser($user);
     $comment_view->setTargetDiff($target);
 
     $changeset_view = new DifferentialChangesetListView();
     $changeset_view->setChangesets($visible_changesets);
     $changeset_view->setEditable(true);
     $changeset_view->setStandaloneViews(true);
     $changeset_view->setRevision($revision);
     $changeset_view->setRenderingReferences($rendering_references);
     $changeset_view->setWhitespace($whitespace);
 
     $diff_history = new DifferentialRevisionUpdateHistoryView();
     $diff_history->setDiffs($diffs);
     $diff_history->setSelectedVersusDiffID($diff_vs);
     $diff_history->setSelectedDiffID($target->getID());
     $diff_history->setSelectedWhitespace($whitespace);
 
     $toc_view = new DifferentialDiffTableOfContentsView();
     $toc_view->setChangesets($changesets);
     $toc_view->setStandaloneViewLink(empty($visible_changesets));
     $toc_view->setVsMap($vs_map);
     $toc_view->setRevisionID($revision->getID());
     $toc_view->setWhitespace($whitespace);
 
 
     $draft = id(new PhabricatorDraft())->loadOneWhere(
       'authorPHID = %s AND draftKey = %s',
       $user->getPHID(),
       'differential-comment-'.$revision->getID());
     if ($draft) {
       $draft = $draft->getDraft();
     } else {
       $draft = null;
     }
 
     $comment_form = new DifferentialAddCommentView();
     $comment_form->setRevision($revision);
     $comment_form->setActions($this->getRevisionCommentActions($revision));
     $comment_form->setActionURI('/differential/comment/save/');
     $comment_form->setUser($user);
     $comment_form->setDraft($draft);
 
     $this->updateViewTime($user->getPHID(), $revision->getPHID());
 
     $pane_id = celerity_generate_unique_node_id();
     Javelin::initBehavior(
       'differential-keyboard-navigation',
       array(
         'haunt' => $pane_id,
       ));
 
     return $this->buildStandardPageResponse(
       id(new DifferentialPrimaryPaneView())
         ->setLineWidthFromChangesets($changesets)
         ->setID($pane_id)
         ->appendChild(
           $revision_detail->render().
           $comment_view->render().
           $diff_history->render().
           $warning.
           $toc_view->render().
           $changeset_view->render().
           $comment_form->render()),
       array(
         'title' => 'D'.$revision->getID().' '.$revision->getTitle(),
       ));
   }
 
   private function getImplicitComments(DifferentialRevision $revision) {
 
     $template = new DifferentialComment();
     $template->setAuthorPHID($revision->getAuthorPHID());
     $template->setRevisionID($revision->getID());
     $template->setDateCreated($revision->getDateCreated());
 
     $comments = array();
 
     if (strlen($revision->getSummary())) {
       $summary_comment = clone $template;
       $summary_comment->setContent($revision->getSummary());
       $summary_comment->setAction(DifferentialAction::ACTION_SUMMARIZE);
       $comments[] = $summary_comment;
     }
 
     if (strlen($revision->getTestPlan())) {
       $testplan_comment = clone $template;
       $testplan_comment->setContent($revision->getTestPlan());
       $testplan_comment->setAction(DifferentialAction::ACTION_TESTPLAN);
       $comments[] = $testplan_comment;
     }
 
     return $comments;
   }
 
-  private function getRevisionProperties(
-    DifferentialRevision $revision,
-    DifferentialDiff $diff,
-    array $handles,
-    array $diff_properties) {
-
-    $properties = array();
-
-    $status = $revision->getStatus();
-    $next_step = null;
-    if ($status == DifferentialRevisionStatus::ACCEPTED) {
-      switch ($diff->getSourceControlSystem()) {
-        case PhabricatorRepositoryType::REPOSITORY_TYPE_GIT:
-          $next_step = 'arc amend --revision '.$revision->getID();
-          break;
-        case PhabricatorRepositoryType::REPOSITORY_TYPE_SVN:
-          $next_step = 'arc commit --revision '.$revision->getID();
-          break;
-      }
-      if ($next_step) {
-        $next_step =
-          ' &middot; '.
-          'Next step: <tt>'.phutil_escape_html($next_step).'</tt>';
-      }
-    }
-    $status = DifferentialRevisionStatus::getNameForRevisionStatus($status);
-    $properties['Revision Status'] = '<strong>'.$status.'</strong>'.$next_step;
-
-    $properties['Reviewers'] = $this->renderHandleLinkList(
-      array_select_keys(
-        $handles,
-        $revision->getReviewers()));
-
-    $properties['CCs'] = $this->renderHandleLinkList(
-      array_select_keys(
-        $handles,
-        $revision->getCCPHIDs()));
-
-    $lstar = DifferentialRevisionUpdateHistoryView::renderDiffLintStar($diff);
-    $lmsg = DifferentialRevisionUpdateHistoryView::getDiffLintMessage($diff);
-    $ldata = idx($diff_properties, 'arc:lint');
-    $ltail = null;
-    if ($ldata) {
-      $ldata = igroup($ldata, 'path');
-      $lint_messages = array();
-      foreach ($ldata as $path => $messages) {
-        $message_markup = array();
-        foreach ($messages as $message) {
-          $path = idx($message, 'path');
-          $line = idx($message, 'line');
-
-          $code = idx($message, 'code');
-          $severity = idx($message, 'severity');
-
-          $name = idx($message, 'name');
-          $description = idx($message, 'description');
-
-          $message_markup[] =
-            '<li>'.
-              '<span class="lint-severity-'.phutil_escape_html($severity).'">'.
-                phutil_escape_html(ucwords($severity)).
-              '</span>'.
-              ' '.
-              '('.phutil_escape_html($code).') '.
-              phutil_escape_html($name).
-              ' at line '.phutil_escape_html($line).
-              '<p>'.phutil_escape_html($description).'</p>'.
-            '</li>';
-        }
-        $lint_messages[] =
-          '<li class="lint-file-block">'.
-            'Lint for <strong>'.phutil_escape_html($path).'</strong>'.
-            '<ul>'.implode("\n", $message_markup).'</ul>'.
-          '</li>';
-      }
-      $ltail =
-        '<div class="differential-lint-block">'.
-          '<ul>'.
-            implode("\n", $lint_messages).
-          '</ul>'.
-        '</div>';
-    }
-
-    $properties['Lint'] = $lstar.' '.$lmsg.$ltail;
-
-    $ustar = DifferentialRevisionUpdateHistoryView::renderDiffUnitStar($diff);
-    $umsg = DifferentialRevisionUpdateHistoryView::getDiffUnitMessage($diff);
-
-    $postponed_count = 0;
-    $udata = idx($diff_properties, 'arc:unit');
-    $utail = null;
-    if ($udata) {
-      $unit_messages = array();
-      foreach ($udata as $test) {
-        $name = phutil_escape_html(idx($test, 'name'));
-        $result = phutil_escape_html(idx($test, 'result'));
-
-        if ($result != DifferentialUnitTestResult::RESULT_POSTPONED &&
-            $result != DifferentialUnitTestResult::RESULT_PASS) {
-          $userdata = phutil_escape_html(idx($test, 'userdata'));
-          if (strlen($userdata) > 256) {
-            $userdata = substr($userdata, 0, 256).'...';
-          }
-          $userdata = str_replace("\n", '<br />', $userdata);
-          $unit_messages[] =
-            '<tr>'.
-            '<th>'.$name.'</th>'.
-            '<th class="unit-test-result">'.
-            '<div class="result-'.$result.'">'.
-            strtoupper($result).
-            '</div>'.
-            '</th>'.
-            '<td>'.$userdata.'</td>'.
-            '</tr>';
-
-          $utail =
-            '<div class="differential-unit-block">'.
-            '<table class="differential-unit-table">'.
-            implode("\n", $unit_messages).
-            '</table>'.
-            '</div>';
-        } else if ($result == DifferentialUnitTestResult::RESULT_POSTPONED) {
-          $postponed_count++;
-        }
-      }
-    }
-
-    if ($postponed_count > 0 &&
-        $diff->getUnitStatus() == DifferentialUnitStatus::UNIT_POSTPONED) {
-      $umsg = $postponed_count.' '.$umsg;
-    }
-
-    $properties['Unit'] = $ustar.' '.$umsg.$utail;
-
-    return $properties;
-  }
-
   private function getRevisionActions(DifferentialRevision $revision) {
     $viewer_phid = $this->getRequest()->getUser()->getPHID();
     $viewer_is_owner = ($revision->getAuthorPHID() == $viewer_phid);
     $viewer_is_reviewer = in_array($viewer_phid, $revision->getReviewers());
     $viewer_is_cc = in_array($viewer_phid, $revision->getCCPHIDs());
     $status = $revision->getStatus();
     $revision_id = $revision->getID();
     $revision_phid = $revision->getPHID();
 
     $links = array();
 
     if ($viewer_is_owner) {
       $links[] = array(
         'class' => 'revision-edit',
         'href'  => "/differential/revision/edit/{$revision_id}/",
         'name'  => 'Edit Revision',
       );
     }
 
     if (!$viewer_is_owner && !$viewer_is_reviewer) {
       $action = $viewer_is_cc ? 'rem' : 'add';
       $links[] = array(
         'class'   => $viewer_is_cc ? 'subscribe-rem' : 'subscribe-add',
         'href'    => "/differential/subscribe/{$action}/{$revision_id}/",
         'name'    => $viewer_is_cc ? 'Unsubscribe' : 'Subscribe',
         'instant' => true,
       );
     } else {
       $links[] = array(
         'class' => 'subscribe-rem unavailable',
         'name'  => 'Automatically Subscribed',
       );
     }
 
     require_celerity_resource('phabricator-object-selector-css');
     require_celerity_resource('javelin-behavior-phabricator-object-selector');
 
     $links[] = array(
       'class' => 'action-dependencies',
       'name'  => 'Edit Dependencies',
       'href'  => "/search/attach/{$revision_phid}/DREV/dependencies/",
       'sigil' => 'workflow',
     );
 
     if (PhabricatorEnv::getEnvConfig('maniphest.enabled')) {
       $links[] = array(
         'class' => 'attach-maniphest',
         'name'  => 'Edit Maniphest Tasks',
         'href'  => "/search/attach/{$revision_phid}/TASK/",
         'sigil' => 'workflow',
       );
     }
 
     $links[] = array(
       'class' => 'transcripts-metamta',
       'name'  => 'MetaMTA Transcripts',
       'href'  => "/mail/?phid={$revision_phid}",
     );
 
     $links[] = array(
       'class' => 'transcripts-herald',
       'name'  => 'Herald Transcripts',
       'href'  => "/herald/transcript/?phid={$revision_phid}",
     );
 
     return $links;
   }
 
-
-  private function renderHandleLinkList(array $list) {
-    if (empty($list)) {
-      return '<em>None</em>';
-    }
-    return implode(', ', mpull($list, 'renderLink'));
-  }
-
   private function getRevisionCommentActions(DifferentialRevision $revision) {
 
     $actions = array(
       DifferentialAction::ACTION_COMMENT => true,
     );
 
     $viewer_phid = $this->getRequest()->getUser()->getPHID();
     $viewer_is_owner = ($viewer_phid == $revision->getAuthorPHID());
     $viewer_is_reviewer = in_array($viewer_phid, $revision->getReviewers());
 
     if ($viewer_is_owner) {
       switch ($revision->getStatus()) {
         case DifferentialRevisionStatus::NEEDS_REVIEW:
           $actions[DifferentialAction::ACTION_ABANDON] = true;
           $actions[DifferentialAction::ACTION_RETHINK] = true;
           break;
         case DifferentialRevisionStatus::NEEDS_REVISION:
           $actions[DifferentialAction::ACTION_ABANDON] = true;
           $actions[DifferentialAction::ACTION_REQUEST] = true;
           break;
         case DifferentialRevisionStatus::ACCEPTED:
           $actions[DifferentialAction::ACTION_ABANDON] = true;
           $actions[DifferentialAction::ACTION_REQUEST] = true;
           $actions[DifferentialAction::ACTION_RETHINK] = true;
           break;
         case DifferentialRevisionStatus::COMMITTED:
           break;
         case DifferentialRevisionStatus::ABANDONED:
           $actions[DifferentialAction::ACTION_RECLAIM] = true;
           break;
       }
     } else {
       switch ($revision->getStatus()) {
         case DifferentialRevisionStatus::NEEDS_REVIEW:
           $actions[DifferentialAction::ACTION_ACCEPT] = true;
           $actions[DifferentialAction::ACTION_REJECT] = true;
           $actions[DifferentialAction::ACTION_RESIGN] = $viewer_is_reviewer;
           break;
         case DifferentialRevisionStatus::NEEDS_REVISION:
           $actions[DifferentialAction::ACTION_ACCEPT] = true;
           $actions[DifferentialAction::ACTION_RESIGN] = $viewer_is_reviewer;
           break;
         case DifferentialRevisionStatus::ACCEPTED:
           $actions[DifferentialAction::ACTION_REJECT] = true;
           break;
         case DifferentialRevisionStatus::COMMITTED:
         case DifferentialRevisionStatus::ABANDONED:
           break;
       }
     }
 
     $actions[DifferentialAction::ACTION_ADDREVIEWERS] = true;
     $actions[DifferentialAction::ACTION_ADDCCS] = true;
 
     return array_keys(array_filter($actions));
   }
 
   private function loadInlineComments(array $comments, array &$changesets) {
 
     $inline_comments = array();
 
     $comment_ids = array_filter(mpull($comments, 'getID'));
     if (!$comment_ids) {
       return $inline_comments;
     }
 
     $inline_comments = id(new DifferentialInlineComment())
       ->loadAllWhere(
         'commentID in (%Ld)',
         $comment_ids);
 
     $load_changesets = array();
     foreach ($inline_comments as $inline) {
       $changeset_id = $inline->getChangesetID();
       if (isset($changesets[$changeset_id])) {
         continue;
       }
       $load_changesets[$changeset_id] = true;
     }
 
     $more_changesets = array();
     if ($load_changesets) {
       $changeset_ids = array_keys($load_changesets);
       $more_changesets += id(new DifferentialChangeset())
         ->loadAllWhere(
           'id IN (%Ld)',
           $changeset_ids);
     }
 
     if ($more_changesets) {
       $changesets += $more_changesets;
       $changesets = msort($changesets, 'getSortKey');
     }
 
     return $inline_comments;
   }
 
   private function loadChangesetsAndVsMap(array $diffs, $diff_vs, $target) {
     $load_ids = array();
     if ($diff_vs) {
       $load_ids[] = $diff_vs;
     }
     $load_ids[] = $target->getID();
 
     $raw_changesets = id(new DifferentialChangeset())
       ->loadAllWhere(
         'diffID IN (%Ld)',
         $load_ids);
     $changeset_groups = mgroup($raw_changesets, 'getDiffID');
 
     $changesets = idx($changeset_groups, $target->getID(), array());
     $changesets = mpull($changesets, null, 'getID');
 
     $refs = array();
     foreach ($changesets as $changeset) {
       $refs[$changeset->getID()] = $changeset->getID();
     }
 
     $vs_map = array();
     if ($diff_vs) {
       $vs_changesets = idx($changeset_groups, $diff_vs, array());
       $vs_changesets = mpull($vs_changesets, null, 'getFilename');
       foreach ($changesets as $key => $changeset) {
         $file = $changeset->getFilename();
         if (isset($vs_changesets[$file])) {
           $vs_map[$changeset->getID()] = $vs_changesets[$file]->getID();
           $refs[$changeset->getID()] =
             $changeset->getID().'/'.$vs_changesets[$file]->getID();
           unset($vs_changesets[$file]);
         } else {
           $refs[$changeset->getID()] = $changeset->getID();
         }
       }
       foreach ($vs_changesets as $changeset) {
         $changesets[$changeset->getID()] = $changeset;
         $vs_map[$changeset->getID()] = -1;
         $refs[$changeset->getID()] = $changeset->getID().'/-1';
       }
     }
 
     $changesets = msort($changesets, 'getSortKey');
 
     return array($changesets, $vs_map, $refs);
   }
 
   private function updateViewTime($user_phid, $revision_phid) {
     $view_time =
       id(new DifferentialViewTime())
         ->setViewerPHID($user_phid)
         ->setObjectPHID($revision_phid)
         ->setViewTime(time())
         ->replace();
   }
 
-  private function loadAuxiliaryFields(DifferentialRevision $revision) {
+  private function loadAuxiliaryFields(
+    DifferentialRevision $revision,
+    DifferentialDiff $diff) {
     $aux_fields = DifferentialFieldSelector::newSelector()
       ->getFieldSpecifications();
     foreach ($aux_fields as $key => $aux_field) {
       if (!$aux_field->shouldAppearOnRevisionView()) {
         unset($aux_fields[$key]);
       }
     }
 
-    return DifferentialAuxiliaryField::loadFromStorage(
+    $aux_fields = DifferentialAuxiliaryField::loadFromStorage(
       $revision,
       $aux_fields);
+
+    $aux_props = array();
+    foreach ($aux_fields as $key => $aux_field) {
+      $aux_field->setDiff($diff);
+      $aux_props[$key] = $aux_field->getRequiredDiffProperties();
+    }
+
+    $required_properties = array_mergev($aux_props);
+    $property_map = array();
+    if ($required_properties) {
+      $properties = id(new DifferentialDiffProperty())->loadAllWhere(
+        'diffID = %d AND name IN (%Ls)',
+        $diff->getID(),
+        $required_properties);
+      $property_map = mpull($properties, 'getData', 'getName');
+    }
+
+    foreach ($aux_fields as $key => $aux_field) {
+      // Give each field only the properties it specifically required, and
+      // set 'null' for each requested key which we didn't actually load a
+      // value for (otherwise, getDiffProperty() will throw).
+      if ($aux_props[$key]) {
+        $props = array_select_keys($property_map, $aux_props[$key]) +
+                 array_fill_keys($aux_props[$key], null);
+      } else {
+        $props = array();
+      }
+
+      $aux_field->setDiffProperties($props);
+    }
+
+    return $aux_fields;
   }
 
+
 }
diff --git a/src/applications/differential/controller/revisionview/__init__.php b/src/applications/differential/controller/revisionview/__init__.php
index 042008c08e..a7809f0195 100644
--- a/src/applications/differential/controller/revisionview/__init__.php
+++ b/src/applications/differential/controller/revisionview/__init__.php
@@ -1,44 +1,41 @@
 <?php
 /**
  * This file is automatically generated. Lint this module to rebuild it.
  * @generated
  */
 
 
 
 phutil_require_module('phabricator', 'aphront/response/404');
 phutil_require_module('phabricator', 'applications/differential/constants/action');
 phutil_require_module('phabricator', 'applications/differential/constants/revisionstatus');
-phutil_require_module('phabricator', 'applications/differential/constants/unitstatus');
-phutil_require_module('phabricator', 'applications/differential/constants/unittestresult');
 phutil_require_module('phabricator', 'applications/differential/controller/base');
 phutil_require_module('phabricator', 'applications/differential/field/selector/base');
 phutil_require_module('phabricator', 'applications/differential/parser/changeset');
 phutil_require_module('phabricator', 'applications/differential/storage/auxiliaryfield');
 phutil_require_module('phabricator', 'applications/differential/storage/changeset');
 phutil_require_module('phabricator', 'applications/differential/storage/comment');
 phutil_require_module('phabricator', 'applications/differential/storage/diffproperty');
 phutil_require_module('phabricator', 'applications/differential/storage/inlinecomment');
 phutil_require_module('phabricator', 'applications/differential/storage/revision');
 phutil_require_module('phabricator', 'applications/differential/storage/viewtime');
 phutil_require_module('phabricator', 'applications/differential/view/addcomment');
 phutil_require_module('phabricator', 'applications/differential/view/changesetlistview');
 phutil_require_module('phabricator', 'applications/differential/view/difftableofcontents');
 phutil_require_module('phabricator', 'applications/differential/view/primarypane');
 phutil_require_module('phabricator', 'applications/differential/view/revisioncommentlist');
 phutil_require_module('phabricator', 'applications/differential/view/revisiondetail');
 phutil_require_module('phabricator', 'applications/differential/view/revisionupdatehistory');
 phutil_require_module('phabricator', 'applications/draft/storage/draft');
 phutil_require_module('phabricator', 'applications/phid/handle/data');
-phutil_require_module('phabricator', 'applications/repository/constants/repositorytype');
 phutil_require_module('phabricator', 'infrastructure/celerity/api');
 phutil_require_module('phabricator', 'infrastructure/env');
 phutil_require_module('phabricator', 'infrastructure/javelin/api');
 phutil_require_module('phabricator', 'view/form/error');
 
 phutil_require_module('phutil', 'markup');
 phutil_require_module('phutil', 'symbols');
 phutil_require_module('phutil', 'utils');
 
 
 phutil_require_source('DifferentialRevisionViewController.php');
diff --git a/src/applications/differential/editor/revision/DifferentialRevisionEditor.php b/src/applications/differential/editor/revision/DifferentialRevisionEditor.php
index 3f56616304..6611492e51 100644
--- a/src/applications/differential/editor/revision/DifferentialRevisionEditor.php
+++ b/src/applications/differential/editor/revision/DifferentialRevisionEditor.php
@@ -1,694 +1,720 @@
 <?php
 
 /*
  * Copyright 2011 Facebook, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
  * You may obtain a copy of the License at
  *
  *   http://www.apache.org/licenses/LICENSE-2.0
  *
  * Unless required by applicable law or agreed to in writing, software
  * distributed under the License is distributed on an "AS IS" BASIS,
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
 
 /**
  * Handle major edit operations to DifferentialRevision -- adding and removing
  * reviewers, diffs, and CCs. Unlike simple edits, these changes trigger
  * complicated email workflows.
  */
 class DifferentialRevisionEditor {
 
   protected $revision;
   protected $actorPHID;
 
   protected $cc         = null;
   protected $reviewers  = null;
   protected $diff;
   protected $comments;
   protected $silentUpdate;
   protected $tasks = null;
 
   private $auxiliaryFields = array();
 
   public function __construct(DifferentialRevision $revision, $actor_phid) {
     $this->revision = $revision;
     $this->actorPHID = $actor_phid;
   }
 
   public static function newRevisionFromConduitWithDiff(
     array $fields,
     DifferentialDiff $diff,
     $user_phid) {
 
     $revision = new DifferentialRevision();
     $revision->setPHID($revision->generatePHID());
 
     $revision->setAuthorPHID($user_phid);
     $revision->setStatus(DifferentialRevisionStatus::NEEDS_REVIEW);
 
     $editor = new DifferentialRevisionEditor($revision, $user_phid);
 
     $editor->copyFieldsFromConduit($fields);
 
     $editor->addDiff($diff, null);
     $editor->save();
 
     // Tasks can only be updated after revision has been saved to the
     // database. Currently tasks are updated only when a revision is created.
     // UI must be used to modify tasks after creating one.
     $editor->updateTasks();
 
     return $revision;
   }
 
   public function copyFieldsFromConduit(array $fields) {
 
     $revision = $this->revision;
 
     $revision->setTitle((string)$fields['title']);
     $revision->setSummary((string)$fields['summary']);
     $revision->setTestPlan((string)$fields['testPlan']);
     $revision->setBlameRevision((string)$fields['blameRevision']);
     $revision->setRevertPlan((string)$fields['revertPlan']);
 
     $this->setReviewers($fields['reviewerPHIDs']);
     $this->setCCPHIDs($fields['ccPHIDs']);
     $this->setTasks($fields['tasks']);
   }
 
   public function setAuxiliaryFields(array $auxiliary_fields) {
     $this->auxiliaryFields = $auxiliary_fields;
     return $this;
   }
 
   public function getRevision() {
     return $this->revision;
   }
 
   public function setReviewers(array $reviewers) {
     $this->reviewers = $reviewers;
     return $this;
   }
 
   public function setCCPHIDs(array $cc) {
     $this->cc = $cc;
     return $this;
   }
 
   public function setTasks(array $tasks) {
     $this->tasks = $tasks;
   }
 
   public function addDiff(DifferentialDiff $diff, $comments) {
     if ($diff->getRevisionID() &&
         $diff->getRevisionID() != $this->getRevision()->getID()) {
       $diff_id = (int)$diff->getID();
       $targ_id = (int)$this->getRevision()->getID();
       $real_id = (int)$diff->getRevisionID();
       throw new Exception(
         "Can not attach diff #{$diff_id} to Revision D{$targ_id}, it is ".
         "already attached to D{$real_id}.");
     }
     $this->diff = $diff;
     $this->comments = $comments;
     return $this;
   }
 
   protected function getDiff() {
     return $this->diff;
   }
 
   protected function getComments() {
     return $this->comments;
   }
 
   protected function getActorPHID() {
     return $this->actorPHID;
   }
 
   public function isNewRevision() {
     return !$this->getRevision()->getID();
   }
 
   /**
    * A silent update does not trigger Herald rules or send emails. This is used
    * for auto-amends at commit time.
    */
   public function setSilentUpdate($silent) {
     $this->silentUpdate = $silent;
     return $this;
   }
 
   public function save() {
     $revision = $this->getRevision();
 
 // TODO
 //    $revision->openTransaction();
 
     $is_new = $this->isNewRevision();
     if ($is_new) {
       // These fields aren't nullable; set them to sensible defaults if they
       // haven't been configured. We're just doing this so we can generate an
       // ID for the revision if we don't have one already.
       $revision->setLineCount(0);
       if ($revision->getStatus() === null) {
         $revision->setStatus(DifferentialRevisionStatus::NEEDS_REVIEW);
       }
       if ($revision->getTitle() === null) {
         $revision->setTitle('Untitled Revision');
       }
       if ($revision->getAuthorPHID() === null) {
         $revision->setAuthorPHID($this->getActorPHID());
       }
-
+      if ($revision->getRevertPlan() === null) {
+        $revision->setRevertPlan('');
+      }
+      if ($revision->getBlameRevision() === null) {
+        $revision->setBlameRevision('');
+      }
+      if ($revision->getSummary() === null) {
+        $revision->setSummary('');
+      }
+      if ($revision->getTestPlan() === null) {
+        $revision->setTestPlan('');
+      }
       $revision->save();
     }
 
     $revision->loadRelationships();
 
+    $this->willWriteRevision();
+
     if ($this->reviewers === null) {
       $this->reviewers = $revision->getReviewers();
     }
 
     if ($this->cc === null) {
       $this->cc = $revision->getCCPHIDs();
     }
 
     // We're going to build up three dictionaries: $add, $rem, and $stable. The
     // $add dictionary has added reviewers/CCs. The $rem dictionary has
     // reviewers/CCs who have been removed, and the $stable array is
     // reviewers/CCs who haven't changed. We're going to send new reviewers/CCs
     // a different ("welcome") email than we send stable reviewers/CCs.
 
     $old = array(
       'rev' => array_fill_keys($revision->getReviewers(), true),
       'ccs' => array_fill_keys($revision->getCCPHIDs(), true),
     );
 
     $diff = $this->getDiff();
 
     $xscript_header = null;
     $xscript_uri = null;
 
     $new = array(
       'rev' => array_fill_keys($this->reviewers, true),
       'ccs' => array_fill_keys($this->cc, true),
     );
 
 
     $rem_ccs = array();
     if ($diff) {
       $diff->setRevisionID($revision->getID());
       $revision->setLineCount($diff->getLineCount());
 
       $adapter = new HeraldDifferentialRevisionAdapter(
         $revision,
         $diff);
       $adapter->setExplicitCCs($new['ccs']);
       $adapter->setExplicitReviewers($new['rev']);
       $adapter->setForbiddenCCs($revision->getUnsubscribedPHIDs());
 
       $xscript = HeraldEngine::loadAndApplyRules($adapter);
       $xscript_uri = PhabricatorEnv::getProductionURI(
         '/herald/transcript/'.$xscript->getID().'/');
       $xscript_phid = $xscript->getPHID();
       $xscript_header = $xscript->getXHeraldRulesHeader();
 
       HeraldTranscript::saveXHeraldRulesHeader(
         $revision->getPHID(),
         $xscript_header);
 
       $sub = array(
         'rev' => array(),
         'ccs' => $adapter->getCCsAddedByHerald(),
       );
       $rem_ccs = $adapter->getCCsRemovedByHerald();
     } else {
       $sub = array(
         'rev' => array(),
         'ccs' => array(),
       );
     }
 
     // Remove any CCs which are prevented by Herald rules.
     $sub['ccs'] = array_diff_key($sub['ccs'], $rem_ccs);
     $new['ccs'] = array_diff_key($new['ccs'], $rem_ccs);
 
     $add = array();
     $rem = array();
     $stable = array();
     foreach (array('rev', 'ccs') as $key) {
       $add[$key] = array();
       if ($new[$key] !== null) {
         $add[$key] += array_diff_key($new[$key], $old[$key]);
       }
       $add[$key] += array_diff_key($sub[$key], $old[$key]);
 
       $combined = $sub[$key];
       if ($new[$key] !== null) {
         $combined += $new[$key];
       }
       $rem[$key] = array_diff_key($old[$key], $combined);
 
       $stable[$key] = array_diff_key($old[$key], $add[$key] + $rem[$key]);
     }
 
     self::alterReviewers(
       $revision,
       $this->reviewers,
       array_keys($rem['rev']),
       array_keys($add['rev']),
       $this->actorPHID);
 
 /*
 
     // TODO: When Herald is brought over, run through this stuff to figure
     // out which adds are Herald's fault.
 
     // TODO: Still need to do this.
 
     if ($add['ccs'] || $rem['ccs']) {
       foreach (array_keys($add['ccs']) as $id) {
         if (empty($new['ccs'][$id])) {
           $reason_phid = 'TODO';//$xscript_phid;
         } else {
           $reason_phid = $this->getActorPHID();
         }
       }
       foreach (array_keys($rem['ccs']) as $id) {
         if (empty($new['ccs'][$id])) {
           $reason_phid = $this->getActorPHID();
         } else {
           $reason_phid = 'TODO';//$xscript_phid;
         }
       }
     }
 */
     self::alterCCs(
       $revision,
       $this->cc,
       array_keys($rem['ccs']),
       array_keys($add['ccs']),
       $this->actorPHID);
 
     $this->updateAuxiliaryFields();
 
     // Add the author and users included from Herald rules to the relevant set
     // of users so they get a copy of the email.
     if (!$this->silentUpdate) {
       if ($is_new) {
         $add['rev'][$this->getActorPHID()] = true;
         if ($diff) {
           $add['rev'] += $adapter->getEmailPHIDsAddedByHerald();
         }
       } else {
         $stable['rev'][$this->getActorPHID()] = true;
         if ($diff) {
           $stable['rev'] += $adapter->getEmailPHIDsAddedByHerald();
         }
       }
     }
 
     $mail = array();
 
     $phids = array($this->getActorPHID());
 
     $handles = id(new PhabricatorObjectHandleData($phids))
       ->loadHandles();
     $actor_handle = $handles[$this->getActorPHID()];
 
     $changesets = null;
     $comment = null;
     if ($diff) {
       $changesets = $diff->loadChangesets();
       // TODO: This should probably be in DifferentialFeedbackEditor?
       if (!$is_new) {
         $comment = $this->createComment();
       }
       if ($comment) {
         $mail[] = id(new DifferentialNewDiffMail(
             $revision,
             $actor_handle,
             $changesets))
           ->setIsFirstMailAboutRevision($is_new)
           ->setIsFirstMailToRecipients($is_new)
           ->setComments($this->getComments())
           ->setToPHIDs(array_keys($stable['rev']))
           ->setCCPHIDs(array_keys($stable['ccs']));
       }
 
       // Save the changes we made above.
 
       $diff->setDescription(substr($this->getComments(), 0, 80));
       $diff->save();
 
       // An updated diff should require review, as long as it's not committed
       // or accepted. The "accepted" status is "sticky" to encourage courtesy
       // re-diffs after someone accepts with minor changes/suggestions.
 
       $status = $revision->getStatus();
       if ($status != DifferentialRevisionStatus::COMMITTED &&
           $status != DifferentialRevisionStatus::ACCEPTED) {
         $revision->setStatus(DifferentialRevisionStatus::NEEDS_REVIEW);
       }
 
     } else {
       $diff = $revision->loadActiveDiff();
       if ($diff) {
         $changesets = $diff->loadChangesets();
       } else {
         $changesets = array();
       }
     }
 
     $revision->save();
 
+    $this->didWriteRevision();
+
     $event_data = array(
       'revision_id'          => $revision->getID(),
       'revision_phid'        => $revision->getPHID(),
       'revision_name'        => $revision->getTitle(),
       'revision_author_phid' => $revision->getAuthorPHID(),
       'action'               => $is_new
         ? DifferentialAction::ACTION_CREATE
         : DifferentialAction::ACTION_UPDATE,
       'feedback_content'     => $is_new
         ? phutil_utf8_shorten($revision->getSummary(), 140)
         : $this->getComments(),
       'actor_phid'           => $revision->getAuthorPHID(),
     );
     id(new PhabricatorTimelineEvent('difx', $event_data))
       ->recordEvent();
 
     id(new PhabricatorFeedStoryPublisher())
       ->setStoryType(PhabricatorFeedStoryTypeConstants::STORY_DIFFERENTIAL)
       ->setStoryData($event_data)
       ->setStoryTime(time())
       ->setStoryAuthorPHID($revision->getAuthorPHID())
       ->setRelatedPHIDs(
         array(
           $revision->getPHID(),
           $revision->getAuthorPHID(),
         ))
       ->publish();
 
 // TODO
 //    $revision->saveTransaction();
 
 //  TODO: Move this into a worker task thing.
     PhabricatorSearchDifferentialIndexer::indexRevision($revision);
 
     if ($this->silentUpdate) {
       return;
     }
 
     $revision->loadRelationships();
 
     if ($add['rev']) {
       $message = id(new DifferentialNewDiffMail(
           $revision,
           $actor_handle,
           $changesets))
         ->setIsFirstMailAboutRevision($is_new)
         ->setIsFirstMailToRecipients(true)
         ->setToPHIDs(array_keys($add['rev']));
 
       if ($is_new) {
         // The first time we send an email about a revision, put the CCs in
         // the "CC:" field of the same "Review Requested" email that reviewers
         // get, so you don't get two initial emails if you're on a list that
         // is CC'd.
         $message->setCCPHIDs(array_keys($add['ccs']));
       }
 
       $mail[] = $message;
     }
 
     // If you were added as a reviewer and a CC, just give you the reviewer
     // email. We could go to greater lengths to prevent this, but there's
     // bunch of stuff with list subscriptions anyway. You can still get two
     // emails, but only if a revision is updated and you are added as a reviewer
     // at the same time a list you are on is added as a CC, which is rare and
     // reasonable.
     $add['ccs'] = array_diff_key($add['ccs'], $add['rev']);
 
     if (!$is_new && $add['ccs']) {
       $mail[] = id(new DifferentialCCWelcomeMail(
           $revision,
           $actor_handle,
           $changesets))
         ->setIsFirstMailToRecipients(true)
         ->setToPHIDs(array_keys($add['ccs']));
     }
 
     foreach ($mail as $message) {
       $message->setHeraldTranscriptURI($xscript_uri);
       $message->setXHeraldRulesHeader($xscript_header);
       $message->send();
     }
   }
 
   public static function addCCAndUpdateRevision(
     $revision,
     $phid,
     $reason) {
 
     self::addCC($revision, $phid, $reason);
 
     $unsubscribed = $revision->getUnsubscribed();
     if (isset($unsubscribed[$phid])) {
       unset($unsubscribed[$phid]);
       $revision->setUnsubscribed($unsubscribed);
       $revision->save();
     }
   }
 
   public static function removeCCAndUpdateRevision(
     $revision,
     $phid,
     $reason) {
 
     self::removeCC($revision, $phid, $reason);
 
     $unsubscribed = $revision->getUnsubscribed();
     if (empty($unsubscribed[$phid])) {
       $unsubscribed[$phid] = true;
       $revision->setUnsubscribed($unsubscribed);
       $revision->save();
     }
   }
 
   public static function addCC(
     DifferentialRevision $revision,
     $phid,
     $reason) {
     return self::alterCCs(
       $revision,
       $revision->getCCPHIDs(),
       $rem = array(),
       $add = array($phid),
       $reason);
   }
 
   public static function removeCC(
     DifferentialRevision $revision,
     $phid,
     $reason) {
     return self::alterCCs(
       $revision,
       $revision->getCCPHIDs(),
       $rem = array($phid),
       $add = array(),
       $reason);
   }
 
   protected static function alterCCs(
     DifferentialRevision $revision,
     array $stable_phids,
     array $rem_phids,
     array $add_phids,
     $reason_phid) {
 
     return self::alterRelationships(
       $revision,
       $stable_phids,
       $rem_phids,
       $add_phids,
       $reason_phid,
       DifferentialRevision::RELATION_SUBSCRIBED);
   }
 
 
   public static function alterReviewers(
     DifferentialRevision $revision,
     array $stable_phids,
     array $rem_phids,
     array $add_phids,
     $reason_phid) {
 
     return self::alterRelationships(
       $revision,
       $stable_phids,
       $rem_phids,
       $add_phids,
       $reason_phid,
       DifferentialRevision::RELATION_REVIEWER);
   }
 
   private static function alterRelationships(
     DifferentialRevision $revision,
     array $stable_phids,
     array $rem_phids,
     array $add_phids,
     $reason_phid,
     $relation_type) {
 
     $rem_map = array_fill_keys($rem_phids, true);
     $add_map = array_fill_keys($add_phids, true);
 
     $seq_map = array_values($stable_phids);
     $seq_map = array_flip($seq_map);
     foreach ($rem_map as $phid => $ignored) {
       if (!isset($seq_map[$phid])) {
         $seq_map[$phid] = count($seq_map);
       }
     }
     foreach ($add_map as $phid => $ignored) {
       if (!isset($seq_map[$phid])) {
         $seq_map[$phid] = count($seq_map);
       }
     }
 
     $raw = $revision->getRawRelations($relation_type);
     $raw = ipull($raw, null, 'objectPHID');
 
     $sequence = count($seq_map);
     foreach ($raw as $phid => $ignored) {
       if (isset($seq_map[$phid])) {
         $raw[$phid]['sequence'] = $seq_map[$phid];
       } else {
         $raw[$phid]['sequence'] = $sequence++;
       }
     }
     $raw = isort($raw, 'sequence');
 
     foreach ($raw as $phid => $ignored) {
       if (isset($rem_map[$phid])) {
         unset($raw[$phid]);
       }
     }
 
     foreach ($add_phids as $add) {
       $raw[$add] = array(
         'objectPHID'  => $add,
         'sequence'    => idx($seq_map, $add, $sequence++),
         'reasonPHID'  => $reason_phid,
       );
     }
 
     $conn_w = $revision->establishConnection('w');
 
     $sql = array();
     foreach ($raw as $relation) {
       $sql[] = qsprintf(
         $conn_w,
         '(%d, %s, %s, %d, %s)',
         $revision->getID(),
         $relation_type,
         $relation['objectPHID'],
         $relation['sequence'],
         $relation['reasonPHID']);
     }
 
     $conn_w->openTransaction();
       queryfx(
         $conn_w,
         'DELETE FROM %T WHERE revisionID = %d AND relation = %s',
         DifferentialRevision::RELATIONSHIP_TABLE,
         $revision->getID(),
         $relation_type);
       if ($sql) {
         queryfx(
           $conn_w,
           'INSERT INTO %T
             (revisionID, relation, objectPHID, sequence, reasonPHID)
           VALUES %Q',
           DifferentialRevision::RELATIONSHIP_TABLE,
           implode(', ', $sql));
       }
     $conn_w->saveTransaction();
 
     $revision->loadRelationships();
   }
 
 
   private function createComment() {
     $revision_id = $this->revision->getID();
     $comment = id(new DifferentialComment())
       ->setAuthorPHID($this->getActorPHID())
       ->setRevisionID($revision_id)
       ->setContent($this->getComments())
       ->setAction('update');
     $comment->save();
 
     return $comment;
   }
 
   private function updateTasks() {
     if ($this->tasks) {
       $task_class = PhabricatorEnv::getEnvConfig(
         'differential.attach-task-class');
       if ($task_class) {
         PhutilSymbolLoader::loadClass($task_class);
         $task_attacher = newv($task_class, array());
         $ret = $task_attacher->attachTasksToRevision(
           $this->actorPHID,
           $this->revision,
           $this->tasks);
       }
     }
   }
 
   private function updateAuxiliaryFields() {
     $aux_map = array();
     foreach ($this->auxiliaryFields as $aux_field) {
       $key = $aux_field->getStorageKey();
       $val = $aux_field->getValueForStorage();
 
       $aux_map[$key] = $val;
     }
 
     if (!$aux_map) {
       return;
     }
 
     $revision = $this->revision;
 
     $fields = id(new DifferentialAuxiliaryField())->loadAllWhere(
       'revisionPHID = %s AND name IN (%Ls)',
       $revision->getPHID(),
       array_keys($aux_map));
     $fields = mpull($fields, null, 'getName');
 
     foreach ($aux_map as $key => $val) {
       $obj = idx($fields, $key);
       if (!$obj) {
         $obj = new DifferentialAuxiliaryField();
         $obj->setRevisionPHID($revision->getPHID());
         $obj->setName($key);
       }
 
       if ($obj->getValue() !== $val) {
         $obj->setValue($val);
         $obj->save();
       }
     }
   }
 
+  private function willWriteRevision() {
+    foreach ($this->auxiliaryFields as $aux_field) {
+      $aux_field->willWriteRevision($this);
+    }
+  }
+
+  private function didWriteRevision() {
+    foreach ($this->auxiliaryFields as $aux_field) {
+      $aux_field->didWriteRevision($this);
+    }
+  }
 
 }
 
diff --git a/src/applications/differential/field/selector/default/DifferentialDefaultFieldSelector.php b/src/applications/differential/field/selector/default/DifferentialDefaultFieldSelector.php
index 5c31d0fb02..0c9380c8ac 100644
--- a/src/applications/differential/field/selector/default/DifferentialDefaultFieldSelector.php
+++ b/src/applications/differential/field/selector/default/DifferentialDefaultFieldSelector.php
@@ -1,37 +1,42 @@
 <?php
 
 /*
  * Copyright 2011 Facebook, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
  * You may obtain a copy of the License at
  *
  *   http://www.apache.org/licenses/LICENSE-2.0
  *
  * Unless required by applicable law or agreed to in writing, software
  * distributed under the License is distributed on an "AS IS" BASIS,
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
 
 final class DifferentialDefaultFieldSelector
   extends DifferentialFieldSelector {
 
   public function getFieldSpecifications() {
     return array(
+      new DifferentialRevisionStatusFieldSpecification(),
       new DifferentialAuthorFieldSpecification(),
+      new DifferentialReviewersFieldSpecification(),
+      new DifferentialCCsFieldSpecification(),
+      new DifferentialUnitFieldSpecification(),
+      new DifferentialLintFieldSpecification(),
+      new DifferentialCommitsFieldSpecification(),
       new DifferentialDependenciesFieldSpecification(),
       new DifferentialManiphestTasksFieldSpecification(),
-      new DifferentialCommitsFieldSpecification(),
       new DifferentialHostFieldSpecification(),
       new DifferentialPathFieldSpecification(),
       new DifferentialLinesFieldSpecification(),
       new DifferentialArcanistProjectFieldSpecification(),
       new DifferentialApplyPatchFieldSpecification(),
       new DifferentialExportPatchFieldSpecification(),
     );
   }
 
 }
diff --git a/src/applications/differential/field/selector/default/__init__.php b/src/applications/differential/field/selector/default/__init__.php
index b8cd112fb7..85864635b0 100644
--- a/src/applications/differential/field/selector/default/__init__.php
+++ b/src/applications/differential/field/selector/default/__init__.php
@@ -1,22 +1,27 @@
 <?php
 /**
  * This file is automatically generated. Lint this module to rebuild it.
  * @generated
  */
 
 
 
 phutil_require_module('phabricator', 'applications/differential/field/selector/base');
 phutil_require_module('phabricator', 'applications/differential/field/specification/applypatch');
 phutil_require_module('phabricator', 'applications/differential/field/specification/arcanistproject');
 phutil_require_module('phabricator', 'applications/differential/field/specification/author');
+phutil_require_module('phabricator', 'applications/differential/field/specification/ccs');
 phutil_require_module('phabricator', 'applications/differential/field/specification/commits');
 phutil_require_module('phabricator', 'applications/differential/field/specification/dependencies');
 phutil_require_module('phabricator', 'applications/differential/field/specification/exportpatch');
 phutil_require_module('phabricator', 'applications/differential/field/specification/host');
 phutil_require_module('phabricator', 'applications/differential/field/specification/lines');
+phutil_require_module('phabricator', 'applications/differential/field/specification/lint');
 phutil_require_module('phabricator', 'applications/differential/field/specification/maniphesttasks');
 phutil_require_module('phabricator', 'applications/differential/field/specification/path');
+phutil_require_module('phabricator', 'applications/differential/field/specification/reviewers');
+phutil_require_module('phabricator', 'applications/differential/field/specification/revisionstatus');
+phutil_require_module('phabricator', 'applications/differential/field/specification/unit');
 
 
 phutil_require_source('DifferentialDefaultFieldSelector.php');
diff --git a/src/applications/differential/field/specification/base/DifferentialFieldSpecification.php b/src/applications/differential/field/specification/base/DifferentialFieldSpecification.php
index 4e87173a1e..ae036cfeb4 100644
--- a/src/applications/differential/field/specification/base/DifferentialFieldSpecification.php
+++ b/src/applications/differential/field/specification/base/DifferentialFieldSpecification.php
@@ -1,354 +1,443 @@
 <?php
 
 /*
  * Copyright 2011 Facebook, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
  * You may obtain a copy of the License at
  *
  *   http://www.apache.org/licenses/LICENSE-2.0
  *
  * Unless required by applicable law or agreed to in writing, software
  * distributed under the License is distributed on an "AS IS" BASIS,
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
 
 /**
  * Describes and implements the behavior for a custom field on Differential
  * revisions. Along with other configuration, you can extend this class to add
  * custom fields to Differential revisions and commit messages.
  *
  * Generally, you should implement all methods from the storage task and then
  * the methods from one or more interface tasks.
  *
  * @task storage Field Storage
  * @task edit Extending the Revision Edit Interface
  * @task view Extending the Revision View Interface
  * @task conduit Extending the Conduit View Interface
- * @task handles Loading Handles
+ * @task load Loading Additional Data
  * @task context Contextual Data
  */
 abstract class DifferentialFieldSpecification {
 
   private $revision;
   private $diff;
   private $handles;
+  private $diffProperties;
 
 
 /* -(  Storage  )------------------------------------------------------------ */
 
 
   /**
    * Return a unique string used to key storage of this field's value, like
    * "mycompany.fieldname" or similar. You can return null (the default) to
    * indicate that this field does not use any storage. This is appropriate for
    * display fields, like @{class:DifferentialLinesFieldSpecification}. If you
    * implement this, you must also implement @{method:getValueForStorage} and
    * @{method:setValueFromStorage}.
    *
    * @return string|null  Unique key which identifies this field in auxiliary
    *                      field storage. Maximum length is 32. Alternatively,
    *                      null (default) to indicate that this field does not
    *                      use auxiliary field storage.
    * @task storage
    */
   public function getStorageKey() {
     return null;
   }
 
 
   /**
    * Return a serialized representation of the field value, appropriate for
    * storing in auxiliary field storage. You must implement this method if
    * you implement @{method:getStorageKey}.
    *
    * @return string Serialized field value.
    * @task storage
    */
   public function getValueForStorage() {
     throw new DifferentialFieldSpecificationIncompleteException($this);
   }
 
 
   /**
    * Set the field's value given a serialized storage value. This is called
    * when the field is loaded; if no data is available, the value will be
    * null. You must implement this method if you implement
    * @{method:getStorageKey}.
    *
    * @param string|null Serialized field representation (from
    *                    @{method:getValueForStorage}) or null if no value has
    *                    ever been stored.
    * @return this
    * @task storage
    */
   public function setValueFromStorage($value) {
     throw new DifferentialFieldSpecificationIncompleteException($this);
   }
 
 
 /* -(  Extending the Revision Edit Interface  )------------------------------ */
 
 
   /**
    * Determine if this field should appear on the "Edit Revision" interface. If
    * you return true from this method, you must implement
    * @{method:setValueFromRequest}, @{method:renderEditControl} and
    * @{method:validateField}.
    *
    * For a concrete example of a field which implements an edit interface, see
    * @{class:DifferentialRevertPlanFieldSpecification}.
    *
    * @return bool True to indicate that this field implements an edit interface.
    * @task edit
    */
   public function shouldAppearOnEdit() {
     return false;
   }
 
 
   /**
    * Set the field's value from an HTTP request. Generally, you should read
    * the value of some field name you emitted in @{method:renderEditControl}
    * and save it into the object, e.g.:
    *
    *   $this->value = $request->getStr('my-custom-field');
    *
    * If you have some particularly complicated field, you may need to read
    * more data; this is why you have access to the entire request.
    *
    * You must implement this if you implement @{method:shouldAppearOnEdit}.
    *
    * You should not perform field validation here; instead, you should implement
    * @{method:validateField}.
    *
    * @param AphrontRequest HTTP request representing a user submitting a form
    *                       with this field in it.
    * @return this
    * @task edit
    */
   public function setValueFromRequest(AphrontRequest $request) {
     throw new DifferentialFieldSpecificationIncompleteException($this);
   }
 
 
   /**
    * Build a renderable object (generally, some @{class:AphrontFormControl})
    * which can be appended to a @{class:AphrontFormView} and represents the
    * interface the user sees on the "Edit Revision" screen when interacting
    * with this field.
    *
    * For example:
    *
    *   return id(new AphrontFormTextControl())
    *     ->setLabel('Custom Field')
    *     ->setName('my-custom-key')
    *     ->setValue($this->value);
    *
    * You must implement this if you implement @{method:shouldAppearOnEdit}.
    *
    * @return AphrontView|string Something renderable.
    * @task edit
    */
   public function renderEditControl() {
     throw new DifferentialFieldSpecificationIncompleteException($this);
   }
 
 
   /**
    * This method will be called after @{method:setValueFromRequest} but before
    * the field is saved. It gives you an opportunity to inspect the field value
    * and throw a @{class:DifferentialFieldValidationException} if there is a
    * problem with the value the user has provided (for example, the value the
    * user entered is not correctly formatted).
    *
    * By default, fields are not validated.
    *
    * @return void
    * @task edit
    */
   public function validateField() {
     return;
   }
 
+  /**
+   * @task edit
+   */
+  public function willWriteRevision(DifferentialRevisionEditor $editor) {
+    return;
+  }
+
+  /**
+   * @task edit
+   */
+  public function didWriteRevision(DifferentialRevisionEditor $editor) {
+    return;
+  }
+
 
 /* -(  Extending the Revision View Interface  )------------------------------ */
 
 
   /**
+   * Determine if this field should appear on the revision detail view
+   * interface. One use of this interface is to add purely informational
+   * fields to the revision view, without any sort of backing storage.
+   *
+   * If you return true from this method, you must implement the methods
+   * @{method:renderLabelForRevisionView} and
+   * @{method:renderValueForRevisionView}.
+   *
+   * @return bool True if this field should appear when viewing a revision.
    * @task view
    */
   public function shouldAppearOnRevisionView() {
     return false;
   }
 
 
   /**
+   * Return a string field label which will appear in the revision detail
+   * table.
+   *
+   * You must implement this method if you return true from
+   * @{method:shouldAppearOnRevisionView}.
+   *
+   * @return string Label for field in revision detail view.
    * @task view
    */
   public function renderLabelForRevisionView() {
     throw new DifferentialFieldSpecificationIncompleteException($this);
   }
 
 
   /**
+   * Return a markup block representing the field for the revision detail
+   * view. Note that you can return null to suppress display (for instance,
+   * if the field shows related objects of some type and the revision doesn't
+   * have any related objects).
+   *
+   * You must implement this method if you return true from
+   * @{method:shouldAppearOnRevisionView}.
+   *
+   * @return string|null Display markup for field value, or null to suppress
+   *                     field rendering.
    * @task view
    */
   public function renderValueForRevisionView() {
     throw new DifferentialFieldSpecificationIncompleteException($this);
   }
 
 
 /* -(  Extending the Conduit Interface  )------------------------------------ */
 
 
   /**
    * @task conduit
    */
   public function shouldAppearOnConduitView() {
     return false;
   }
 
   /**
    * @task conduit
    */
   public function getValueForConduit() {
     throw new DifferentialFieldSpecificationIncompleteException($this);
   }
 
   /**
    * @task conduit
    */
   public function getKeyForConduit() {
     $key = $this->getStorageKey();
     if ($key === null) {
       throw new DifferentialFieldSpecificationIncompleteException($this);
     }
     return $key;
   }
 
 
-/* -(  Loading Handles  )---------------------------------------------------- */
+/* -(  Loading Additional Data  )-------------------------------------------- */
 
 
   /**
    * Specify which @{class:PhabricatorObjectHandles} need to be loaded for your
    * field to render correctly.
    *
    * This is a convenience method which makes the handles available on all
    * interfaces where the field appears. If your field needs handles on only
    * some interfaces (or needs different handles on different interfaces) you
    * can overload the more specific methods to customize which interfaces you
    * retrieve handles for. Requesting only the handles you need will improve
    * the performance of your field.
    *
    * You can later retrieve these handles by calling @{method:getHandle}.
    *
    * @return list List of PHIDs to load handles for.
-   * @task handles
+   * @task load
    */
   protected function getRequiredHandlePHIDs() {
     return array();
   }
 
   /**
    * Specify which @{class:PhabricatorObjectHandles} need to be loaded for your
    * field to render correctly on the view interface.
    *
    * This is a more specific version of @{method:getRequiredHandlePHIDs} which
    * can be overridden to improve field performance by loading only data you
    * need.
    *
    * @return list List of PHIDs to load handles for.
-   * @task handles
+   * @task load
    */
   public function getRequiredHandlePHIDsForRevisionView() {
     return $this->getRequiredHandlePHIDs();
   }
 
   /**
    * Specify which @{class:PhabricatorObjectHandles} need to be loaded for your
    * field to render correctly on the edit interface.
    *
    * This is a more specific version of @{method:getRequiredHandlePHIDs} which
    * can be overridden to improve field performance by loading only data you
    * need.
    *
    * @return list List of PHIDs to load handles for.
-   * @task handles
+   * @task load
    */
   public function getRequiredHandlePHIDsForEdit() {
     return $this->getRequiredHandlePHIDs();
   }
 
 
+  /**
+   * Specify which diff properties this field needs to load.
+   *
+   * @return list List of diff property keys this field requires.
+   * @task load
+   */
+  public function getRequiredDiffProperties() {
+    return array();
+  }
+
+
 /* -(  Contextual Data  )---------------------------------------------------- */
 
 
   /**
    * @task context
    */
   final public function setRevision(DifferentialRevision $revision) {
     $this->revision = $revision;
     return $this;
   }
 
   /**
    * @task context
    */
   final public function setDiff(DifferentialDiff $diff) {
     $this->diff = $diff;
     return $this;
   }
 
   /**
    * @task context
    */
   final public function setHandles(array $handles) {
     $this->handles = $handles;
     return $this;
   }
 
+  /**
+   * @task context
+   */
+  final public function setDiffProperties(array $diff_properties) {
+    $this->diffProperties = $diff_properties;
+    return $this;
+  }
+
   /**
    * @task context
    */
   final protected function getRevision() {
     if (empty($this->revision)) {
       throw new DifferentialFieldDataNotAvailableException($this);
     }
     return $this->revision;
   }
 
   /**
    * @task context
    */
   final protected function getDiff() {
     if (empty($this->diff)) {
       throw new DifferentialFieldDataNotAvailableException($this);
     }
     return $this->diff;
   }
 
   /**
    * Get the handle for an object PHID. You must overload
    * @{method:getRequiredHandlePHIDs} (or a more specific version thereof)
    * and include the PHID you want in the list for it to be available here.
    *
    * @return PhabricatorObjectHandle Handle to the object.
    * @task context
    */
   final protected function getHandle($phid) {
+    if ($this->handles === null) {
+      throw new DifferentialFieldDataNotAvailableException($this);
+    }
     if (empty($this->handles[$phid])) {
       $class = get_class($this);
       throw new Exception(
         "A differential field (of class '{$class}') is attempting to retrieve ".
         "a handle ('{$phid}') which it did not request. Return all handle ".
         "PHIDs you need from getRequiredHandlePHIDs().");
     }
     return $this->handles[$phid];
   }
 
+  /**
+   * Get a diff property which this field previously requested by returning
+   * the key from @{method:getRequiredDiffProperties}.
+   *
+   * @param  string      Diff property key.
+   * @return string|null Diff property, or null if the property does not have
+   *                     a value.
+   * @task context
+   */
+  final public function getDiffProperty($key) {
+    if ($this->diffProperties === null) {
+      // This will be set to some (possibly empty) array if we've loaded
+      // properties, so null means diff properties aren't available in this
+      // context.
+      throw new DifferentialFieldDataNotAvailableException($this);
+    }
+    if (!array_key_exists($key, $this->diffProperties)) {
+      $class = get_class($this);
+      throw new Exception(
+        "A differential field (of class '{$class}') is attempting to retrieve ".
+        "a diff property ('{$key}') which it did not request. Return all ".
+        "diff property keys you need from getRequiredDiffProperties().");
+    }
+    return $this->diffProperties[$key];
+  }
+
 }
diff --git a/src/applications/differential/field/specification/ccs/DifferentialCCsFieldSpecification.php b/src/applications/differential/field/specification/ccs/DifferentialCCsFieldSpecification.php
new file mode 100644
index 0000000000..ed57ab83fc
--- /dev/null
+++ b/src/applications/differential/field/specification/ccs/DifferentialCCsFieldSpecification.php
@@ -0,0 +1,53 @@
+<?php
+
+/*
+ * Copyright 2011 Facebook, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+final class DifferentialCCsFieldSpecification
+  extends DifferentialFieldSpecification {
+
+  public function shouldAppearOnRevisionView() {
+    return true;
+  }
+
+  public function getRequiredHandlePHIDsForRevisionView() {
+    return $this->getCCPHIDs();
+  }
+
+  public function renderLabelForRevisionView() {
+    return 'CCs:';
+  }
+
+  public function renderValueForRevisionView() {
+    $cc_phids = $this->getCCPHIDs();
+    if (!$cc_phids) {
+      return '<em>None</em>';
+    }
+
+    $links = array();
+    foreach ($cc_phids as $cc_phid) {
+      $links[] = $this->getHandle($cc_phid)->renderLink();
+    }
+
+    return implode(', ', $links);
+  }
+
+  private function getCCPHIDs() {
+    $revision = $this->getRevision();
+    return $revision->getCCPHIDs();
+  }
+
+}
diff --git a/src/applications/differential/field/specification/ccs/__init__.php b/src/applications/differential/field/specification/ccs/__init__.php
new file mode 100644
index 0000000000..f7e1685565
--- /dev/null
+++ b/src/applications/differential/field/specification/ccs/__init__.php
@@ -0,0 +1,12 @@
+<?php
+/**
+ * This file is automatically generated. Lint this module to rebuild it.
+ * @generated
+ */
+
+
+
+phutil_require_module('phabricator', 'applications/differential/field/specification/base');
+
+
+phutil_require_source('DifferentialCCsFieldSpecification.php');
diff --git a/src/applications/differential/field/specification/lint/DifferentialLintFieldSpecification.php b/src/applications/differential/field/specification/lint/DifferentialLintFieldSpecification.php
new file mode 100644
index 0000000000..759ef4436a
--- /dev/null
+++ b/src/applications/differential/field/specification/lint/DifferentialLintFieldSpecification.php
@@ -0,0 +1,84 @@
+<?php
+
+/*
+ * Copyright 2011 Facebook, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+final class DifferentialLintFieldSpecification
+  extends DifferentialFieldSpecification {
+
+  public function shouldAppearOnRevisionView() {
+    return true;
+  }
+
+  public function renderLabelForRevisionView() {
+    return 'Lint:';
+  }
+
+  public function getRequiredDiffProperties() {
+    return array('arc:lint');
+  }
+
+  public function renderValueForRevisionView() {
+    $diff = $this->getDiff();
+
+    $lstar = DifferentialRevisionUpdateHistoryView::renderDiffLintStar($diff);
+    $lmsg = DifferentialRevisionUpdateHistoryView::getDiffLintMessage($diff);
+    $ldata = $this->getDiffProperty('arc:lint');
+    $ltail = null;
+    if ($ldata) {
+      $ldata = igroup($ldata, 'path');
+      $lint_messages = array();
+      foreach ($ldata as $path => $messages) {
+        $message_markup = array();
+        foreach ($messages as $message) {
+          $path = idx($message, 'path');
+          $line = idx($message, 'line');
+
+          $code = idx($message, 'code');
+          $severity = idx($message, 'severity');
+
+          $name = idx($message, 'name');
+          $description = idx($message, 'description');
+
+          $message_markup[] =
+            '<li>'.
+              '<span class="lint-severity-'.phutil_escape_html($severity).'">'.
+                phutil_escape_html(ucwords($severity)).
+              '</span>'.
+              ' '.
+              '('.phutil_escape_html($code).') '.
+              phutil_escape_html($name).
+              ' at line '.phutil_escape_html($line).
+              '<p>'.phutil_escape_html($description).'</p>'.
+            '</li>';
+        }
+        $lint_messages[] =
+          '<li class="lint-file-block">'.
+            'Lint for <strong>'.phutil_escape_html($path).'</strong>'.
+            '<ul>'.implode("\n", $message_markup).'</ul>'.
+          '</li>';
+      }
+      $ltail =
+        '<div class="differential-lint-block">'.
+          '<ul>'.
+            implode("\n", $lint_messages).
+          '</ul>'.
+        '</div>';
+    }
+
+    return $lstar.' '.$lmsg.$ltail;
+  }
+}
diff --git a/src/applications/differential/field/specification/lint/__init__.php b/src/applications/differential/field/specification/lint/__init__.php
new file mode 100644
index 0000000000..0041dfe5f7
--- /dev/null
+++ b/src/applications/differential/field/specification/lint/__init__.php
@@ -0,0 +1,16 @@
+<?php
+/**
+ * This file is automatically generated. Lint this module to rebuild it.
+ * @generated
+ */
+
+
+
+phutil_require_module('phabricator', 'applications/differential/field/specification/base');
+phutil_require_module('phabricator', 'applications/differential/view/revisionupdatehistory');
+
+phutil_require_module('phutil', 'markup');
+phutil_require_module('phutil', 'utils');
+
+
+phutil_require_source('DifferentialLintFieldSpecification.php');
diff --git a/src/applications/differential/field/specification/reviewers/DifferentialReviewersFieldSpecification.php b/src/applications/differential/field/specification/reviewers/DifferentialReviewersFieldSpecification.php
new file mode 100644
index 0000000000..909087db72
--- /dev/null
+++ b/src/applications/differential/field/specification/reviewers/DifferentialReviewersFieldSpecification.php
@@ -0,0 +1,53 @@
+<?php
+
+/*
+ * Copyright 2011 Facebook, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+final class DifferentialReviewersFieldSpecification
+  extends DifferentialFieldSpecification {
+
+  public function shouldAppearOnRevisionView() {
+    return true;
+  }
+
+  public function getRequiredHandlePHIDsForRevisionView() {
+    return $this->getReviewerPHIDs();
+  }
+
+  public function renderLabelForRevisionView() {
+    return 'Reviewers:';
+  }
+
+  public function renderValueForRevisionView() {
+    $reviewer_phids = $this->getReviewerPHIDs();
+    if (!$reviewer_phids) {
+      return '<em>None</em>';
+    }
+
+    $links = array();
+    foreach ($reviewer_phids as $reviewer_phid) {
+      $links[] = $this->getHandle($reviewer_phid)->renderLink();
+    }
+
+    return implode(', ', $links);
+  }
+
+  private function getReviewerPHIDs() {
+    $revision = $this->getRevision();
+    return $revision->getReviewers();
+  }
+
+}
diff --git a/src/applications/differential/field/specification/reviewers/__init__.php b/src/applications/differential/field/specification/reviewers/__init__.php
new file mode 100644
index 0000000000..fb1adf0a61
--- /dev/null
+++ b/src/applications/differential/field/specification/reviewers/__init__.php
@@ -0,0 +1,12 @@
+<?php
+/**
+ * This file is automatically generated. Lint this module to rebuild it.
+ * @generated
+ */
+
+
+
+phutil_require_module('phabricator', 'applications/differential/field/specification/base');
+
+
+phutil_require_source('DifferentialReviewersFieldSpecification.php');
diff --git a/src/applications/differential/field/specification/revisionstatus/DifferentialRevisionStatusFieldSpecification.php b/src/applications/differential/field/specification/revisionstatus/DifferentialRevisionStatusFieldSpecification.php
new file mode 100644
index 0000000000..f25e25ed62
--- /dev/null
+++ b/src/applications/differential/field/specification/revisionstatus/DifferentialRevisionStatusFieldSpecification.php
@@ -0,0 +1,55 @@
+<?php
+
+/*
+ * Copyright 2011 Facebook, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+final class DifferentialRevisionStatusFieldSpecification
+  extends DifferentialFieldSpecification {
+
+  public function shouldAppearOnRevisionView() {
+    return true;
+  }
+
+  public function renderLabelForRevisionView() {
+    return 'Revision Status:';
+  }
+
+  public function renderValueForRevisionView() {
+    $revision = $this->getRevision();
+    $diff = $this->getDiff();
+
+    $status = $revision->getStatus();
+    $next_step = null;
+    if ($status == DifferentialRevisionStatus::ACCEPTED) {
+      switch ($diff->getSourceControlSystem()) {
+        case PhabricatorRepositoryType::REPOSITORY_TYPE_GIT:
+          $next_step = 'arc amend --revision '.$revision->getID();
+          break;
+        case PhabricatorRepositoryType::REPOSITORY_TYPE_SVN:
+          $next_step = 'arc commit --revision '.$revision->getID();
+          break;
+      }
+      if ($next_step) {
+        $next_step =
+          ' &middot; '.
+          'Next step: <tt>'.phutil_escape_html($next_step).'</tt>';
+      }
+    }
+    $status = DifferentialRevisionStatus::getNameForRevisionStatus($status);
+    return '<strong>'.$status.'</strong>'.$next_step;
+  }
+
+}
diff --git a/src/applications/differential/field/specification/revisionstatus/__init__.php b/src/applications/differential/field/specification/revisionstatus/__init__.php
new file mode 100644
index 0000000000..c8649c3340
--- /dev/null
+++ b/src/applications/differential/field/specification/revisionstatus/__init__.php
@@ -0,0 +1,16 @@
+<?php
+/**
+ * This file is automatically generated. Lint this module to rebuild it.
+ * @generated
+ */
+
+
+
+phutil_require_module('phabricator', 'applications/differential/constants/revisionstatus');
+phutil_require_module('phabricator', 'applications/differential/field/specification/base');
+phutil_require_module('phabricator', 'applications/repository/constants/repositorytype');
+
+phutil_require_module('phutil', 'markup');
+
+
+phutil_require_source('DifferentialRevisionStatusFieldSpecification.php');
diff --git a/src/applications/differential/field/specification/unit/DifferentialUnitFieldSpecification.php b/src/applications/differential/field/specification/unit/DifferentialUnitFieldSpecification.php
new file mode 100644
index 0000000000..ce243ef0e2
--- /dev/null
+++ b/src/applications/differential/field/specification/unit/DifferentialUnitFieldSpecification.php
@@ -0,0 +1,88 @@
+<?php
+
+/*
+ * Copyright 2011 Facebook, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+final class DifferentialUnitFieldSpecification
+  extends DifferentialFieldSpecification {
+
+  public function shouldAppearOnRevisionView() {
+    return true;
+  }
+
+  public function renderLabelForRevisionView() {
+    return 'Unit:';
+  }
+
+  public function getRequiredDiffProperties() {
+    return array('arc:unit');
+  }
+
+  public function renderValueForRevisionView() {
+    $diff = $this->getDiff();
+
+    $ustar = DifferentialRevisionUpdateHistoryView::renderDiffUnitStar($diff);
+    $umsg = DifferentialRevisionUpdateHistoryView::getDiffUnitMessage($diff);
+
+    $postponed_count = 0;
+    $udata = $this->getDiffProperty('arc:unit');
+    $utail = null;
+
+    if ($udata) {
+      $unit_messages = array();
+      foreach ($udata as $test) {
+        $name = phutil_escape_html(idx($test, 'name'));
+        $result = phutil_escape_html(idx($test, 'result'));
+
+        if ($result != DifferentialUnitTestResult::RESULT_POSTPONED &&
+            $result != DifferentialUnitTestResult::RESULT_PASS) {
+          $userdata = phutil_escape_html(idx($test, 'userdata'));
+          if (strlen($userdata) > 256) {
+            $userdata = substr($userdata, 0, 256).'...';
+          }
+          $userdata = str_replace("\n", '<br />', $userdata);
+          $unit_messages[] =
+            '<tr>'.
+            '<th>'.$name.'</th>'.
+            '<th class="unit-test-result">'.
+            '<div class="result-'.$result.'">'.
+            strtoupper($result).
+            '</div>'.
+            '</th>'.
+            '<td>'.$userdata.'</td>'.
+            '</tr>';
+
+          $utail =
+            '<div class="differential-unit-block">'.
+            '<table class="differential-unit-table">'.
+            implode("\n", $unit_messages).
+            '</table>'.
+            '</div>';
+        } else if ($result == DifferentialUnitTestResult::RESULT_POSTPONED) {
+          $postponed_count++;
+        }
+      }
+    }
+
+    if ($postponed_count > 0 &&
+        $diff->getUnitStatus() == DifferentialUnitStatus::UNIT_POSTPONED) {
+      $umsg = $postponed_count.' '.$umsg;
+    }
+
+    return $ustar.' '.$umsg.$utail;
+  }
+
+}
diff --git a/src/applications/differential/field/specification/unit/__init__.php b/src/applications/differential/field/specification/unit/__init__.php
new file mode 100644
index 0000000000..73b0a4571c
--- /dev/null
+++ b/src/applications/differential/field/specification/unit/__init__.php
@@ -0,0 +1,18 @@
+<?php
+/**
+ * This file is automatically generated. Lint this module to rebuild it.
+ * @generated
+ */
+
+
+
+phutil_require_module('phabricator', 'applications/differential/constants/unitstatus');
+phutil_require_module('phabricator', 'applications/differential/constants/unittestresult');
+phutil_require_module('phabricator', 'applications/differential/field/specification/base');
+phutil_require_module('phabricator', 'applications/differential/view/revisionupdatehistory');
+
+phutil_require_module('phutil', 'markup');
+phutil_require_module('phutil', 'utils');
+
+
+phutil_require_source('DifferentialUnitFieldSpecification.php');